filter logic

This commit is contained in:
delphi 2025-02-27 17:00:04 +08:00
parent 9e37aa7bfb
commit a1568cc50f
9 changed files with 713 additions and 218 deletions

View file

@ -37,6 +37,7 @@ export interface CuboxApiOptions {
export interface CuboxFolder {
id: string;
name: string;
nested_name: string;
}
interface ListResponse {
@ -110,74 +111,65 @@ export class CuboxApi {
/**
*
* @param folders
* @param type
* @param status 'all' | 'read' | 'starred' | 'annotated'
* @param folderFilter
* @param typeFilter
* @param statusFilter 'all' | 'read' | 'starred' | 'annotated'
* @param lastCardId ID
*/
async getArticles(
folders: string[] = [],
type?: string,
status?: string,
lastCardId: string | null = null,
pageSize: number = 50
): Promise<{
articles: CuboxArticle[],
hasMore: boolean,
lastCardId: string | null
}> {
folderFilter: string[],
typeFilter: string[],
statusFilter: string[],
lastCardId: string | null = null
): Promise<{ articles: any[], hasMore: boolean, lastCardId: string | null }> {
try {
const searchParams = new URLSearchParams();
// 构建请求参数
const params: any = {
limit: 20,
};
if (lastCardId !== null && lastCardId.length > 0) {
searchParams.append('lastCardId', lastCardId);
}
searchParams.append('pageSize', pageSize.toString());
// 如果有选择文件夹,添加文件夹过滤参数
if (folders && folders.length > 0) {
searchParams.append('folders', folders.join(','));
// 添加文件夹过滤
if (folderFilter && folderFilter.length > 0) {
params.folder_id = folderFilter;
}
// 添加状态过滤参数
if (status && status !== 'all') {
switch (status) {
case 'read':
searchParams.append('isRead', 'true');
break;
case 'starred':
searchParams.append('hasStar', 'true');
break;
case 'annotated':
searchParams.append('hasAnnotation', 'true');
break;
// 添加类型过滤
if (typeFilter && typeFilter.length > 0) {
params.type = typeFilter;
}
// 添加状态过滤 - 修改为支持多选
if (statusFilter && statusFilter.length > 0) {
// 如果包含 'all',则不添加状态过滤
if (!statusFilter.includes('all')) {
// 处理多个状态过滤
if (statusFilter.includes('read')) params.is_read = true;
if (statusFilter.includes('starred')) params.is_starred = true;
if (statusFilter.includes('archived')) params.is_archived = true;
if (statusFilter.includes('annotated')) params.is_annotated = true;
}
}
const path = `/c/api/third-party/card/list?${searchParams.toString()}`;
const response = await this.request(path) as ListResponse;
const articles = response.data.list.map(article => {
// 添加兼容字段,并格式化日期
// 添加分页参数
if (lastCardId) {
params.last_id = lastCardId;
}
// 发送请求
const response = await this.request('/v2/article/list', params);
// 处理响应
if (response && response.data) {
return {
...article,
id: article.cardId,
createDate: article.createTime ? formatISODateTime(article.createTime) : '',
// 其他可能需要的兼容字段
articles: response.data.data || [],
hasMore: response.data.has_more || false,
lastCardId: response.data.last_id || null
};
});
}
const hasMore = articles.length >= pageSize;
const newLastCardId = articles.length > 0 ? articles[articles.length - 1].cardId : null;
return {
articles,
hasMore,
lastCardId: newLastCardId
};
return { articles: [], hasMore: false, lastCardId: null };
} catch (error) {
console.error('获取 Cubox 文章失败:', error);
new Notice('获取 Cubox 文章失败');
console.error('获取文章列表失败:', error);
throw error;
}
}
@ -224,7 +216,10 @@ export class CuboxApi {
const path = '/c/api/third-party/folder/list';
const response = await this.request(path) as FoldersResponse;
return response.data.list || [];
return response.data.list.map(folder => ({
...folder,
nested_name: folder.name
}));
} catch (error) {
console.error('获取 Cubox 文件夹列表失败:', error);
new Notice('获取 Cubox 文件夹列表失败');

View file

@ -1,127 +0,0 @@
import { App, Modal, Setting } from 'obsidian';
import { CuboxApi } from './cuboxApi';
export interface CuboxFolder {
id: string;
name: string;
}
export class FolderSelectModal extends Modal {
private folders: CuboxFolder[] = [];
private selectedFolders: Set<string> = new Set();
private onSave: (selectedFolders: string[]) => void;
private cuboxApi: CuboxApi;
constructor(app: App, cuboxApi: CuboxApi, initialSelected: string[], onSave: (selectedFolders: string[]) => void) {
super(app);
this.cuboxApi = cuboxApi;
this.onSave = onSave;
// 初始化已选择的文件夹
initialSelected.forEach(id => {
if (id) this.selectedFolders.add(id);
});
}
async onOpen() {
const { contentEl } = this;
// 设置标题
contentEl.createEl('h2', { text: 'Cubox: 管理要同步的文件夹' });
try {
// 显示加载中提示
const loadingEl = contentEl.createEl('p', { text: '加载文件夹列表...' });
// 获取文件夹列表
this.folders = await this.cuboxApi.getFolders();
// 移除加载提示
loadingEl.remove();
// 如果没有文件夹,显示提示
if (this.folders.length === 0) {
contentEl.createEl('p', { text: '没有找到文件夹' });
return;
}
// 添加"全选"选项
new Setting(contentEl)
.addToggle(toggle => toggle
.setValue(this.isAllSelected())
.onChange(value => {
if (value) {
// 全选
this.folders.forEach(folder => this.selectedFolders.add(folder.id));
} else {
// 全不选
this.selectedFolders.clear();
}
// 刷新界面
this.redraw();
}))
.setName('全部文件夹');
// 创建文件夹列表
this.createFolderList();
// 添加底部按钮
const footerEl = contentEl.createDiv('modal-footer');
// 取消按钮
const cancelButton = footerEl.createEl('button', { text: '取消' });
cancelButton.addEventListener('click', () => {
this.close();
});
// 保存按钮
const saveButton = footerEl.createEl('button', { text: '保存', cls: 'mod-cta' });
saveButton.addEventListener('click', () => {
this.onSave(Array.from(this.selectedFolders));
this.close();
});
} catch (error) {
console.error('加载文件夹失败:', error);
contentEl.createEl('p', { text: '加载文件夹失败,请检查网络连接和 API 密钥' });
}
}
private createFolderList() {
const { contentEl } = this;
// 清除现有列表
const existingList = contentEl.querySelector('.folder-list');
if (existingList) existingList.remove();
// 创建新列表容器
const listEl = contentEl.createDiv({ cls: 'folder-list' });
// 添加每个文件夹的选项
this.folders.forEach(folder => {
new Setting(listEl)
.addToggle(toggle => toggle
.setValue(this.selectedFolders.has(folder.id))
.onChange(value => {
if (value) {
this.selectedFolders.add(folder.id);
} else {
this.selectedFolders.delete(folder.id);
}
}))
.setName(folder.name);
});
}
private isAllSelected(): boolean {
return this.folders.length > 0 && this.folders.every(folder => this.selectedFolders.has(folder.id));
}
private redraw() {
this.createFolderList();
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
}

View file

@ -2,15 +2,17 @@ import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Set
import { CuboxApi, CuboxApiOptions } from './cuboxApi';
import { TemplateProcessor } from './templateProcessor';
import { formatISODateTime, getCurrentFormattedTime } from './utils';
import { FolderSelectModal } from './folderSelectModal';
import { FolderSelectModal } from './modal/folderSelectModal';
import { filenameTemplateInstructions, metadataTemplateInstructions, contentTemplateInstructions } from './templateInstructions';
import { TypeSelectModal } from './modal/typeSelectModal';
import { StatusSelectModal } from './modal/statusSelectModal';
interface CuboxSyncSettings {
domain: string; // 可以是 'cubox.cc' | 'cubox.pro' | ''
apiKey: string;
folderFilter: string[];
typeFilter: string;
statusFilter: 'all' | 'read' | 'starred' | 'annotated';
typeFilter: string[];
statusFilter: string[];
syncFrequency: number;
targetFolder: string;
filenameTemplate: string;
@ -20,14 +22,15 @@ interface CuboxSyncSettings {
dateFormat: string;
lastSyncTime: number;
lastSyncCardId: string | null;
syncing: boolean; // 添加同步状态标志
}
const DEFAULT_SETTINGS: CuboxSyncSettings = {
domain: '', // 默认为空,表示未选择
apiKey: '',
folderFilter: [],
typeFilter: '',
statusFilter: 'all',
typeFilter: [],
statusFilter: ['all'],
syncFrequency: 30, // 分钟
targetFolder: 'Cubox',
filenameTemplate: '{{cardTitle}}-{{createDate}}',
@ -36,7 +39,8 @@ const DEFAULT_SETTINGS: CuboxSyncSettings = {
highlightInContent: true,
dateFormat: 'YYYY-MM-dd',
lastSyncTime: 0,
lastSyncCardId: null
lastSyncCardId: null,
syncing: false // 默认为非同步状态
}
export default class CuboxSyncPlugin extends Plugin {
@ -131,10 +135,27 @@ export default class CuboxSyncPlugin extends Plugin {
}
async syncCubox(continueLastSync: boolean = false) {
// 如果已经在同步中,则跳过
if (this.settings.syncing) {
new Notice('同步已在进行中,请等待当前同步完成');
return;
}
try {
// 设置同步状态为 true
this.settings.syncing = true;
await this.saveSettings();
// 更新状态栏显示
const statusBarItemEl = this.addStatusBarItem();
statusBarItemEl.setText(`正在同步 Cubox...`);
// 检查 API 连接
const isConnected = await this.cuboxApi.testConnection();
if (!isConnected) {
this.settings.syncing = false;
await this.saveSettings();
statusBarItemEl.setText(`上次同步: ${this.formatLastSyncTime()}`);
return;
}
@ -206,6 +227,9 @@ export default class CuboxSyncPlugin extends Plugin {
await this.app.vault.adapter.write(filePath, finalContent);
syncCount++;
// 更新状态栏显示同步进度
statusBarItemEl.setText(`正在同步 Cubox: ${syncCount} 篇文章已同步`);
}
// 更新分页参数
@ -225,12 +249,27 @@ export default class CuboxSyncPlugin extends Plugin {
// 同步完成后,清除 lastSyncCardId表示完整同步已完成
this.settings.lastSyncCardId = null;
this.settings.lastSyncTime = Date.now();
// 设置同步状态为 false
this.settings.syncing = false;
await this.saveSettings();
// 更新状态栏
statusBarItemEl.setText(`上次同步: ${this.formatLastSyncTime()}`);
new Notice(`成功同步 ${syncCount} 篇 Cubox 文章`);
} catch (error) {
console.error('同步 Cubox 数据失败:', error);
new Notice('同步 Cubox 数据失败,请检查设置和网络连接');
// 出错时也要重置同步状态
this.settings.syncing = false;
await this.saveSettings();
// 更新状态栏
const statusBarItemEl = this.addStatusBarItem();
statusBarItemEl.setText(`上次同步: ${this.formatLastSyncTime()} (失败)`);
}
}
@ -325,6 +364,7 @@ class CuboxSyncSettingTab extends PluginSettingTab {
.setDesc('Manage Cubox folders to be synced')
.addButton(button => button
.setButtonText(this.getFolderFilterButtonText())
.setCta()
.onClick(async () => {
// 确保 API 已初始化
if (!this.plugin.settings.apiKey) {
@ -332,6 +372,9 @@ class CuboxSyncSettingTab extends PluginSettingTab {
return;
}
// 显示加载状态
button.setButtonText('Loading folders...');
// 打开文件夹选择模态框
const modal = new FolderSelectModal(
this.app,
@ -349,27 +392,56 @@ class CuboxSyncSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName('Type Filter')
.setDesc('Enter the xxxxx, 0 means manual sync')
.addDropdown(dropdown => dropdown
.addOption('', 'Sync all the items')
.setValue(this.plugin.settings.typeFilter)
.onChange(async (value) => {
this.plugin.settings.typeFilter = value;
await this.plugin.saveSettings();
.setDesc('Manage Cubox content types to be synced')
.addButton(button => button
.setButtonText(this.getTypeFilterButtonText())
.setCta()
.onClick(async () => {
// 确保 API 已初始化
if (!this.plugin.settings.apiKey) {
new Notice('请先设置 API Key');
return;
}
// 打开类型选择模态框
const modal = new TypeSelectModal(
this.app,
this.plugin.settings.typeFilter,
async (selectedTypes) => {
this.plugin.settings.typeFilter = selectedTypes;
await this.plugin.saveSettings();
// 更新按钮文本
button.setButtonText(this.getTypeFilterButtonText());
}
);
modal.open();
}));
new Setting(containerEl)
.setName('Status Filter')
.setDesc('Only the items that match the status will be synced.')
.addDropdown(dropdown => dropdown
.addOption('all', 'Sync all the items')
.addOption('read', 'Sync only read items')
.addOption('starred', 'Sync only starred items')
.addOption('annotated', 'Sync only annotated items')
.setValue(this.plugin.settings.statusFilter || 'all')
.onChange(async (value) => {
this.plugin.settings.statusFilter = value as 'all' | 'read' | 'starred' | 'annotated';
await this.plugin.saveSettings();
.setDesc('Manage Cubox content status to be synced')
.addButton(button => button
.setButtonText(this.getStatusFilterButtonText())
.setCta()
.onClick(async () => {
// 确保 API 已初始化
if (!this.plugin.settings.apiKey) {
new Notice('请先设置 API Key');
return;
}
// 打开状态选择模态框
const modal = new StatusSelectModal(
this.app,
this.plugin.settings.statusFilter,
async (selectedStatuses) => {
this.plugin.settings.statusFilter = selectedStatuses;
await this.plugin.saveSettings();
// 更新按钮文本
button.setButtonText(this.getStatusFilterButtonText());
}
);
modal.open();
}));
// 同步设置部分
@ -443,8 +515,8 @@ class CuboxSyncSettingTab extends PluginSettingTab {
})
// 设置文本区域的大小
.then(textArea => {
textArea.inputEl.rows = 6;
textArea.inputEl.cols = 50;
textArea.inputEl.rows = 20;
textArea.inputEl.cols = 30;
}))
.addButton(button => button
.setIcon('reset')
@ -470,8 +542,8 @@ class CuboxSyncSettingTab extends PluginSettingTab {
})
// 设置文本区域的大小
.then(textArea => {
textArea.inputEl.rows = 8;
textArea.inputEl.cols = 50;
textArea.inputEl.rows = 20;
textArea.inputEl.cols = 30;
}))
.addButton(button => button
.setIcon('reset')
@ -544,9 +616,46 @@ class CuboxSyncSettingTab extends PluginSettingTab {
private getFolderFilterButtonText(): string {
const count = this.plugin.settings.folderFilter.length;
if (count === 0) {
return 'Sync all folders';
return 'Manage';
} else {
return `已选择 ${count} 个文件夹`;
}
}
// 修改获取类型过滤器按钮文本的方法
private getTypeFilterButtonText(): string {
const typeFilters = this.plugin.settings.typeFilter;
if (!typeFilters || typeFilters.length === 0) {
return 'Manage';
} else {
// 将类型ID转换为可读名称
const typeMap: {[key: string]: string} = {
'article': 'Article',
'snippet': 'Snippet',
'memo': 'Memo',
'image': 'Image',
'audio': 'Audio',
'video': 'Video',
'file': 'File'
};
// 如果选择了所有类型,显示"All Types"
if (typeFilters.length === Object.keys(typeMap).length) {
return 'All Types';
}
// 否则显示选择的类型数量
return `已选择 ${typeFilters.length} 个类型`;
}
}
private getStatusFilterButtonText(): string {
const statusFilters = this.plugin.settings.statusFilter;
if (!statusFilters || statusFilters.length === 0 ||
(statusFilters.length === 1 && statusFilters[0] === 'all')) {
return 'All Items';
} else {
return `已选择 ${statusFilters.length} 个状态`;
}
}
}

View file

@ -0,0 +1,130 @@
import { App, Modal, Setting, Notice } from 'obsidian';
import { CuboxApi, CuboxFolder } from '../cuboxApi';
export class FolderSelectModal extends Modal {
private cuboxApi: CuboxApi;
private selectedFolders: string[];
private onConfirm: (selectedFolders: string[]) => void;
private folders: CuboxFolder[] = [];
private loading: boolean = true;
constructor(
app: App,
cuboxApi: CuboxApi,
initialSelectedFolders: string[],
onConfirm: (selectedFolders: string[]) => void
) {
super(app);
this.cuboxApi = cuboxApi;
this.selectedFolders = [...initialSelectedFolders];
this.onConfirm = onConfirm;
}
async onOpen() {
const { contentEl } = this;
// 设置标题
contentEl.createEl('h2', { text: '选择要同步的文件夹' });
// 添加说明
contentEl.createEl('p', {
text: '选择要从 Cubox 同步的文件夹。不选择任何文件夹将同步所有内容。'
});
// 添加加载指示器
const loadingEl = contentEl.createEl('div', { text: '正在加载文件夹...' });
try {
// 获取文件夹列表
this.folders = await this.cuboxApi.getFolders();
this.loading = false;
// 移除加载指示器
loadingEl.remove();
// 创建文件夹列表
const folderListEl = contentEl.createEl('div', { cls: 'folder-list' });
// 添加"全部"选项
new Setting(folderListEl)
.setName('全部文件夹')
.setDesc('同步所有文件夹的内容')
.addToggle(toggle => toggle
.setValue(this.selectedFolders.length === 0)
.onChange(value => {
if (value) {
// 如果选择"全部",清空已选文件夹
this.selectedFolders = [];
// 更新所有其他切换按钮
folderListEl.querySelectorAll('.folder-toggle').forEach((el: HTMLElement) => {
if (el.hasClass('is-enabled')) {
// @ts-ignore
el.click();
}
});
}
})
);
// 添加每个文件夹的选项
this.folders.forEach(folder => {
new Setting(folderListEl)
.setName(folder.nested_name)
.addToggle(toggle => {
toggle
.setValue(this.selectedFolders.includes(folder.id))
.onChange(value => {
if (value) {
// 添加到选中列表
this.selectedFolders.push(folder.id);
// 如果有选择具体文件夹,取消"全部"选项
const allToggle = folderListEl.querySelector('.setting:first-child .toggle');
if (allToggle && allToggle.hasClass('is-enabled')) {
// @ts-ignore
allToggle.click();
}
} else {
// 从选中列表移除
this.selectedFolders = this.selectedFolders.filter(id => id !== folder.id);
}
});
// 添加类名以便于选择
toggle.toggleEl.addClass('folder-toggle');
return toggle;
});
});
// 添加确认和取消按钮
const buttonContainer = contentEl.createEl('div', { cls: 'button-container' });
buttonContainer.createEl('button', { text: '取消' })
.addEventListener('click', () => {
this.close();
});
buttonContainer.createEl('button', { text: '确认', cls: 'mod-cta' })
.addEventListener('click', () => {
this.onConfirm(this.selectedFolders);
this.close();
});
} catch (error) {
console.error('获取文件夹列表失败:', error);
loadingEl.setText('获取文件夹列表失败,请检查网络连接');
// 添加重试按钮
contentEl.createEl('button', { text: '重试' })
.addEventListener('click', () => {
this.close();
new FolderSelectModal(this.app, this.cuboxApi, this.selectedFolders, this.onConfirm).open();
});
}
}
onClose() {
const { contentEl } = this;
contentEl.empty();
}
}

View file

@ -0,0 +1,139 @@
import { App, Modal, Setting, Notice } from 'obsidian';
import { ModalStyleManager } from '../utils/modalStyles';
export interface ContentStatus {
id: string;
name: string;
}
export class StatusSelectModal extends Modal {
private statuses: ContentStatus[] = [
{ id: 'all', name: 'All Items' },
{ id: 'read', name: 'Already Read Items' },
{ id: 'starred', name: 'Starred Items' },
{ id: 'archived', name: 'Archived Items' },
{ id: 'annotated', name: 'Annotated Items' }
];
private selectedStatuses: Set<string> = new Set();
private onSave: (selectedStatuses: string[]) => void;
private allItemsId: string = 'all';
private listEl: HTMLElement;
private footerEl: HTMLElement;
constructor(app: App, initialSelected: string[], onSave: (selectedStatuses: string[]) => void) {
super(app);
this.onSave = onSave;
// 初始化已选择的状态
if (initialSelected && initialSelected.length > 0) {
initialSelected.forEach(id => {
if (id) this.selectedStatuses.add(id);
});
} else {
// 如果没有初始选择,默认选中"All Items"
this.selectedStatuses.add(this.allItemsId);
}
}
async onOpen() {
const { contentEl } = this;
// 设置标题和样式
contentEl.createEl('h2', { text: 'Manage Cubox content status to be synced' });
contentEl.addClass('cubox-status-select-modal');
// 创建固定的容器结构
this.listEl = contentEl.createDiv({ cls: 'status-list-container' });
this.footerEl = contentEl.createDiv({ cls: 'modal-footer' });
// 创建状态列表
this.createStatusList();
// 添加底部按钮
// 取消按钮
const cancelButton = this.footerEl.createEl('button', { text: '取消' });
cancelButton.addEventListener('click', () => {
this.close();
});
// 保存按钮
const saveButton = this.footerEl.createEl('button', { text: '保存', cls: 'mod-cta' });
saveButton.addEventListener('click', () => {
// 返回选中的状态
const selectedIds = Array.from(this.selectedStatuses);
this.onSave(selectedIds);
this.close();
});
// 添加样式以固定底部按钮
this.addStyles();
}
private addStyles() {
// 使用通用样式管理器添加样式
ModalStyleManager.addModalStyles(
'cubox-status-modal-styles',
'cubox-status-select-modal',
'status-list-container'
);
}
private createStatusList() {
// 清除现有列表
this.listEl.empty();
// 添加每个状态的选项
this.statuses.forEach(status => {
new Setting(this.listEl)
.addToggle(toggle => toggle
.setValue(this.selectedStatuses.has(status.id))
.onChange(value => {
this.handleStatusToggle(status.id, value);
this.redraw();
}))
.setName(status.name);
});
}
private handleStatusToggle(statusId: string, isSelected: boolean) {
if (statusId === this.allItemsId) {
if (isSelected) {
// 如果选择了"All Items",清除其他所有选择
this.selectedStatuses.clear();
this.selectedStatuses.add(this.allItemsId);
} else {
// 如果取消了"All Items"且没有其他选择,重新选中"All Items"
if (this.selectedStatuses.size <= 1) {
this.selectedStatuses.clear();
this.selectedStatuses.add(this.allItemsId);
}
}
} else {
if (isSelected) {
// 如果选择了其他选项,移除"All Items"
this.selectedStatuses.delete(this.allItemsId);
// 添加新选择的状态
this.selectedStatuses.add(statusId);
} else {
// 移除取消选择的状态
this.selectedStatuses.delete(statusId);
// 如果没有任何选择,默认选中"All Items"
if (this.selectedStatuses.size === 0) {
this.selectedStatuses.add(this.allItemsId);
}
}
}
}
private redraw() {
this.createStatusList();
}
onClose() {
const { contentEl } = this;
contentEl.empty();
// 使用通用样式管理器移除样式
ModalStyleManager.removeModalStyles('cubox-status-modal-styles');
}
}

View file

@ -0,0 +1,172 @@
import { App, Modal, Setting, Notice } from 'obsidian';
import { ModalStyleManager } from '../utils/modalStyles';
export interface ContentType {
id: string;
name: string;
}
export class TypeSelectModal extends Modal {
private onSave: (selectedTypes: string[]) => void;
private selectedTypes: Set<string> = new Set();
private listEl: HTMLElement;
private allItemsId: string = 'all';
// 定义可用的类型
private types = [
{ id: 'article', name: 'Article' },
{ id: 'snippet', name: 'Snippet' },
{ id: 'memo', name: 'Memo' },
{ id: 'image', name: 'Image' },
{ id: 'audio', name: 'Audio' },
{ id: 'video', name: 'Video' },
{ id: 'file', name: 'File' }
];
constructor(app: App, initialSelected: string[] = [], onSave: (selectedTypes: string[]) => void) {
super(app);
this.onSave = onSave;
console.log('TypeSelectModal constructor called with initialSelected:', initialSelected);
// 初始化已选择的类型
if (initialSelected && initialSelected.length > 0) {
// 添加初始选择的类型
initialSelected.forEach(id => {
if (id) this.selectedTypes.add(id);
});
// 检查是否所有类型都被选中,如果是则也选中"All Items"
const allTypesSelected = this.types.every(type =>
initialSelected.includes(type.id)
);
if (allTypesSelected) {
this.selectedTypes.add(this.allItemsId);
}
} else {
// 如果没有初始选择,默认选中"All Items"和所有类型
this.selectedTypes.add(this.allItemsId);
this.types.forEach(type => {
this.selectedTypes.add(type.id);
});
}
}
onOpen() {
const { contentEl } = this;
contentEl.empty();
// 添加标题
contentEl.createEl('h2', { text: 'Select Content Types' });
contentEl.addClass('cubox-type-select-modal');
// 创建类型列表容器
this.listEl = contentEl.createDiv({ cls: 'type-list-container' });
// 创建类型列表
this.createTypeList();
// 添加底部按钮容器 - 使用与 StatusSelectModal 相同的类名
const footerEl = contentEl.createDiv({ cls: 'modal-footer' });
// 添加取消按钮
const cancelButton = footerEl.createEl('button', { text: 'Cancel' });
cancelButton.addEventListener('click', () => {
this.close();
});
// 添加保存按钮
const saveButton = footerEl.createEl('button', { text: 'Save', cls: 'mod-cta' });
saveButton.addEventListener('click', () => {
// 过滤掉"All Items",只保留实际类型
const selectedTypes = Array.from(this.selectedTypes)
.filter(id => id !== this.allItemsId);
this.onSave(selectedTypes);
this.close();
});
// 添加样式
this.addStyles();
}
private addStyles() {
// 使用通用样式管理器添加样式
ModalStyleManager.addModalStyles(
'cubox-modal-styles',
'cubox-type-select-modal',
'type-list-container'
);
}
private createTypeList() {
// 清除现有列表
this.listEl.empty();
// 添加"All Items"选项
new Setting(this.listEl)
.addToggle(toggle => toggle
.setValue(this.selectedTypes.has(this.allItemsId))
.onChange(value => {
if (value) {
// 选中"All Items"时,选中所有类型
this.selectedTypes.add(this.allItemsId);
this.types.forEach(type => {
this.selectedTypes.add(type.id);
});
} else {
// 取消选中"All Items"时,取消选中所有类型
this.selectedTypes.delete(this.allItemsId);
this.types.forEach(type => {
this.selectedTypes.delete(type.id);
});
}
this.redraw();
}))
.setName('All Items');
// 添加每个类型的选项
this.types.forEach(type => {
new Setting(this.listEl)
.addToggle(toggle => toggle
.setValue(this.selectedTypes.has(type.id))
.onChange(value => {
this.handleTypeToggle(type.id, value);
this.redraw();
}))
.setName(type.name);
});
}
private handleTypeToggle(typeId: string, isSelected: boolean) {
if (isSelected) {
this.selectedTypes.add(typeId);
// 检查是否所有类型都被选中
const allTypesSelected = this.types.every(type =>
this.selectedTypes.has(type.id)
);
// 不再自动选中"All Items",即使所有类型都被选中
// 只有当用户手动选择"All Items"时才选中它
} else {
this.selectedTypes.delete(typeId);
// 如果有任何类型未被选中,取消选中"All Items"
this.selectedTypes.delete(this.allItemsId);
}
}
private redraw() {
this.createTypeList();
}
onClose() {
const { contentEl } = this;
contentEl.empty();
// 使用通用样式管理器移除样式
ModalStyleManager.removeModalStyles('cubox-modal-styles');
}
}

View file

@ -24,11 +24,35 @@ export class TemplateProcessor {
return article.title || 'Untitled';
}
// 准备用于 Mustache 的数据
const data = this.prepareTemplateData(article);
// 使用简单的字符串替换而不是 Mustache
let filename = template;
// 使用 Mustache 渲染模板
const filename = Mustache.render(template, data);
// 替换所有支持的模板字段
if (article.title) {
filename = filename.replace(/{{{card_title}}}/g, article.title);
filename = filename.replace(/{{{article_title}}}/g, article.title);
}
const createDate = article.createTime ? formatISODateTime(article.createTime, this.dateFormat) : '';
if (createDate) {
filename = filename.replace(/{{{date_saved}}}/g, createDate);
}
const updateDate = article.updateTime ? formatISODateTime(article.updateTime, this.dateFormat) : '';
if (updateDate) {
filename = filename.replace(/{{{date_updated}}}/g, updateDate);
}
if (article.domain) {
filename = filename.replace(/{{{site_domain}}}/g, article.domain);
}
if (article.type) {
filename = filename.replace(/{{{type}}}/g, article.type);
}
// 移除任何未被替换的模板标记 (如果某些字段不存在)
filename = filename.replace(/{{{[^}]+}}}/g, '');
// 处理文件名安全性
return generateSafeFileArticleName(filename);

53
src/utils/modalStyles.ts Normal file
View file

@ -0,0 +1,53 @@
/**
*
*/
export class ModalStyleManager {
/**
*
* @param styleId ID
* @param modalClass
* @param listContainerClass
* @returns
*/
public static addModalStyles(
styleId: string,
modalClass: string,
listContainerClass: string
): HTMLStyleElement {
const styleEl = document.createElement('style');
styleEl.id = styleId;
styleEl.textContent = `
.${modalClass} {
display: flex;
flex-direction: column;
height: 100%;
}
.${listContainerClass} {
flex-grow: 1;
overflow-y: auto;
padding-top: 20px;
padding-right: 10px;
}
.modal-footer {
display: flex;
justify-content: flex-end;
padding: 16px;
border-top: 1px solid var(--background-modifier-border);
}
.modal-footer button {
margin-left: 8px;
}
`;
document.head.appendChild(styleEl);
return styleEl;
}
/**
* ID的样式元素
* @param styleId ID
*/
public static removeModalStyles(styleId: string): void {
const styleEl = document.getElementById(styleId);
if (styleEl) styleEl.remove();
}
}

View file

@ -51,19 +51,19 @@ If your plugin does not need CSS, delete this file.
.cubox-variables-list {
margin: 0;
padding-left: 20px;
padding-left: 15px;
list-style-type: disc;
}
.cubox-variables-list li {
margin-bottom: 4px;
line-height: 1.5;
line-height: 1.2;
}
.highlight-sublist {
margin-top: 4px;
padding-left: 20px;
list-style-type: circle;
padding-left: 15px;
list-style-type: disc;
}
.cubox-reference {