diff --git a/manifest.json b/manifest.json index bb6094c..c34bb91 100644 --- a/manifest.json +++ b/manifest.json @@ -1,9 +1,9 @@ { "id": "cubox-sync", "name": "Cubox", - "version": "1.0.3", + "version": "1.0.4", "minAppVersion": "0.15.0", - "description": "Sync your Cubox articles & annotation", + "description": "Sync your Cubox articles & annotations", "author": "delphi-2015", "isDesktopOnly": false } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6fc2302..3978274 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-cubox", - "version": "1.0.3", + "version": "1.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "obsidian-cubox", - "version": "1.0.3", + "version": "1.0.4", "license": "MIT", "dependencies": { "luxon": "^3.1.1", diff --git a/package.json b/package.json index acea808..dc17a6a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-cubox", - "version": "1.0.3", + "version": "1.0.4", "description": "This is a cubox plugin for Obsidian (https://obsidian.md)", "main": "main.js", "scripts": { diff --git a/src/cuboxApi.ts b/src/cuboxApi.ts index cb99a20..74e9894 100644 --- a/src/cuboxApi.ts +++ b/src/cuboxApi.ts @@ -1,4 +1,4 @@ -import { Notice } from 'obsidian'; +import { Notice, requestUrl } from 'obsidian'; import { ALL_FOLDERS_ID } from './modal/folderSelectModal'; import { ALL_ITEMS } from './modal/tagSelectModal'; import { ALL_STATUS_ID } from './modal/statusSelectModal'; @@ -87,24 +87,31 @@ export class CuboxApi { private async request(path: string, options: RequestInit = {}) { const url = `${this.endpoint}${path}`; - const headers = { + const headers: Record = { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', }; - const response = await fetch(url, { - ...options, - headers: { - ...headers, - ...options.headers, - }, - }); - - if (!response.ok) { - throw new Error(`API request failed: ${response.statusText}`); + if (options.headers) { + Object.entries(options.headers).forEach(([key, value]) => { + if (typeof value === 'string') { + headers[key] = value; + } + }); } - return response.json(); + const response = await requestUrl({ + url: url, + method: options.method || 'GET', + body: options.body as string | ArrayBuffer | undefined, + headers: headers, + }); + + if (response.status >= 400) { + throw new Error(`API request failed: ${response.status}`); + } + + return JSON.parse(response.text); } /** diff --git a/src/cuboxSetting.ts b/src/cuboxSetting.ts index 4b05686..8b89546 100644 --- a/src/cuboxSetting.ts +++ b/src/cuboxSetting.ts @@ -6,6 +6,7 @@ import { filenameTemplateInstructions, metadataVariablesInstructions, contentTem import { ALL_CONTENT_TYPES, TypeSelectModal } from './modal/typeSelectModal'; import { ALL_STATUS_ID, StatusSelectModal } from './modal/statusSelectModal'; import { ALL_ITEMS, TagSelectModal } from './modal/tagSelectModal'; +import { normalizePath } from 'obsidian'; export const DEFAULT_CONTENT_TEMPLATE = `# {{{title}}} @@ -56,13 +57,13 @@ export interface CuboxSyncSettings { export const DEFAULT_SETTINGS: CuboxSyncSettings = { domain: '', apiKey: '', - folderFilter: [ALL_FOLDERS_ID], - typeFilter: ALL_CONTENT_TYPES, - statusFilter: [ALL_STATUS_ID], + folderFilter: [], + typeFilter: [], + statusFilter: [], isRead: true, isStarred: true, isAnnotated: true, - tagsFilter: [ALL_ITEMS], + tagsFilter: [], syncFrequency: 30, // 分钟 targetFolder: 'Cubox', filenameTemplate: '{{{title}}}-{{{create_time}}}', @@ -89,21 +90,16 @@ export class CuboxSyncSettingTab extends PluginSettingTab { const {containerEl} = this; containerEl.empty(); - - // 添加 Cubox 标题和版本信息 - new Setting(containerEl) - .setName('Cubox') - .setDesc(`Created by Cubox, version ${this.plugin.manifest.version}`) // 连接设置部分 - containerEl.createEl('h3', {text: 'Connect Obsidian to Your Cubox'}); + new Setting(containerEl).setName('Connect obsidian to your Cubox').setHeading(); // 修改域名选择下拉框 new Setting(containerEl) - .setName('Cubox Server Domain') + .setName('Cubox server domain') .setDesc('Select the correct domain name of the Cubox you are using.') .addDropdown(dropdown => dropdown - .addOption('', 'Choose Region') + .addOption('', 'Choose region') .addOption('cubox.cc', 'cubox.cc (international)') .addOption('cubox.pro', 'cubox.pro') .setValue(this.plugin.settings.domain) @@ -123,7 +119,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab { // 添加 API Key 设置 this.apiKeySetting = new Setting(containerEl) - .setName('Your Cubox API Key') + .setName('Your Cubox API key') .setDesc('Please select a region first') .addText(text => { text.setPlaceholder('Enter your API key') @@ -172,10 +168,10 @@ export class CuboxSyncSettingTab extends PluginSettingTab { this.updateApiKeySetting(); // 过滤器设置部分 - containerEl.createEl('h3', {text: 'Filter'}); + new Setting(containerEl).setName('Filter').setHeading(); new Setting(containerEl) - .setName('Folder Filter') + .setName('Folder filter') .setDesc('Manage Cubox folders to be synced') .addButton(button => button .setButtonText(this.getFolderFilterButtonText()) @@ -217,7 +213,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab { })); new Setting(containerEl) - .setName('Tag Filter') + .setName('Tag filter') .setDesc('Manage Cubox tags to be synced') .addButton(button => button .setButtonText(this.getTagFilterButtonText()) @@ -259,7 +255,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab { })); new Setting(containerEl) - .setName('Type Filter') + .setName('Type filter') .setDesc('Manage Cubox content types to be synced') .addButton(button => button .setButtonText(this.getTypeFilterButtonText()) @@ -285,7 +281,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab { })); new Setting(containerEl) - .setName('Status Filter') + .setName('Status filter') .setDesc('Manage Cubox content status to be synced') .addButton(button => button .setButtonText(this.getStatusFilterButtonText()) @@ -323,10 +319,10 @@ export class CuboxSyncSettingTab extends PluginSettingTab { })); // 同步设置部分 - containerEl.createEl('h3', {text: 'Sync'}); + new Setting(containerEl).setName('Sync').setHeading(); new Setting(containerEl) - .setName('Sync Interval') + .setName('Sync interval') .setDesc('Auto sync interval (in minutes). 0 means manual sync. Each item syncs only once. Subsequent updates won\'t be synced, and modifications in Obsidian won\'t affect Cubox. We recommend avoiding frequent updates.') .addText(text => { const textField = text @@ -357,7 +353,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab { .setValue(this.plugin.settings.targetFolder) .onChange(async (value) => { this.plugin.settings.lastSyncCardId = null; - this.plugin.settings.targetFolder = value; + this.plugin.settings.targetFolder = normalizePath(value); await this.plugin.saveSettings(); })); @@ -365,7 +361,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab { const filenameInstructionsFragment = document.createRange().createContextualFragment(filenameTemplateInstructions); new Setting(containerEl) - .setName('File Name Template') + .setName('File name template') .setDesc(filenameInstructionsFragment) .addText(text => text .setPlaceholder('Enter file name template') @@ -391,7 +387,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab { const metadataInstructionsFragment = document.createRange().createContextualFragment(metadataVariablesInstructions); new Setting(containerEl) - .setName('Metadata Variables') + .setName('Metadata variables') .setDesc(metadataInstructionsFragment) .addTextArea(text => text .setPlaceholder('Enter front matter variables') @@ -431,7 +427,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab { const contentInstructionsFragment = document.createRange().createContextualFragment(contentTemplateInstructions); new Setting(containerEl) - .setName('Content Template') + .setName('Content template') .setDesc(contentInstructionsFragment) .addTextArea(text => text .setPlaceholder('Enter content template') @@ -464,7 +460,7 @@ export class CuboxSyncSettingTab extends PluginSettingTab { const dateFormatInstructionsFragment = document.createRange().createContextualFragment(cuboxDateFormat); new Setting(containerEl) - .setName('Date Format') + .setName('Date format') .setDesc(dateFormatInstructionsFragment) .addText(text => text .setPlaceholder('Enter date format') @@ -476,8 +472,8 @@ export class CuboxSyncSettingTab extends PluginSettingTab { })); // 状态部分 - containerEl.createEl('h3', {text: 'Status'}); - containerEl.createEl('p', {text: `Last sync: ${this.plugin.formatLastSyncTime()}`}); + new Setting(containerEl).setName('Status').setHeading(); + new Setting(containerEl).setDesc(`Last sync: ${this.plugin.formatLastSyncTime()}`); // 初始化帮助链接 setTimeout(() => { @@ -573,9 +569,9 @@ export class CuboxSyncSettingTab extends PluginSettingTab { private getFolderFilterButtonText(): string { const folderFilters = this.plugin.settings.folderFilter; if (!folderFilters || folderFilters.length === 0) { - return 'Manage'; + return 'Select...'; } else if (folderFilters.includes(ALL_FOLDERS_ID)) { - return 'All Folders'; + return 'All folders'; } else { return `${folderFilters.length} selected`; } @@ -584,9 +580,9 @@ export class CuboxSyncSettingTab extends PluginSettingTab { private getTypeFilterButtonText(): string { const typeFilters = this.plugin.settings.typeFilter; if (!typeFilters || typeFilters.length === 0) { - return 'Manage'; + return 'Select...'; } else if (typeFilters.length === ALL_CONTENT_TYPES.length) { - return 'All Types'; + return 'All types'; } else { return `${typeFilters.length} selected`; } @@ -595,9 +591,9 @@ export class CuboxSyncSettingTab extends PluginSettingTab { private getStatusFilterButtonText(): string { const statusFilters = this.plugin.settings.statusFilter; if (!statusFilters || statusFilters.length === 0) { - return 'Manage'; + return 'Select...'; } else if (statusFilters.includes('all')) { - return 'All Items'; + return 'All items'; } else { return `${statusFilters.length} selected`; } @@ -606,11 +602,11 @@ export class CuboxSyncSettingTab extends PluginSettingTab { private getTagFilterButtonText(): string { const tagFilters = this.plugin.settings.tagsFilter; if (!tagFilters || tagFilters.length === 0) { - return 'Manage'; + return 'Select...'; } else if (tagFilters.includes(ALL_ITEMS)) { - return 'All Items'; + return 'All items'; } else if (tagFilters.includes('')) { - return 'No Tags'; + return 'No tags'; } else { return `${tagFilters.length} selected`; } diff --git a/src/main.ts b/src/main.ts index d8af2cf..d7d3b27 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import { addIcon, Notice, Plugin, TFile } from 'obsidian'; +import { addIcon, Notice, Plugin, TFile, TFolder } from 'obsidian'; import { CuboxApi } from './cuboxApi'; import { TemplateProcessor } from './templateProcessor'; import { formatDateTime } from './utils'; @@ -50,10 +50,6 @@ export default class CuboxSyncPlugin extends Plugin { if (this.syncIntervalId) { window.clearInterval(this.syncIntervalId); } - - // 移除样式 - const styleEl = document.getElementById('cubox-sync-styles'); - if (styleEl) styleEl.remove(); } async loadSettings() { @@ -86,6 +82,12 @@ export default class CuboxSyncPlugin extends Plugin { return; } + // Check if filters are configured + if (this.settings.folderFilter.length === 0 || this.settings.typeFilter.length === 0 || this.settings.statusFilter.length === 0 || this.settings.tagsFilter.length === 0) { + new Notice('Please select the filter first'); + return; + } + try { // 设置同步状态为进行中 this.settings.syncing = true; @@ -149,11 +151,11 @@ export default class CuboxSyncPlugin extends Plugin { const file = this.app.vault.getAbstractFileByPath(filePath); if (file instanceof TFile) { let foundMatchingId = false - await this.app.fileManager.processFrontMatter(file, (frontmatter) => { - if (frontmatter.id && frontmatter.id === article.id) { - foundMatchingId = true; - } - }); + const itemId = this.app.metadataCache.getFileCache(file)?.frontmatter?.id + if (itemId && itemId === article.id) { + foundMatchingId = true; + console.log('found matching id', itemId, article.id); + } if (foundMatchingId) { skipCount++; @@ -178,7 +180,7 @@ export default class CuboxSyncPlugin extends Plugin { } finalContent += contentTemplate; - await this.app.vault.adapter.write(filePath, finalContent); + await this.app.vault.create(filePath, finalContent); syncCount++; @@ -216,15 +218,16 @@ export default class CuboxSyncPlugin extends Plugin { } async ensureTargetFolder() { - const folderPath = this.settings.targetFolder; - if (!(await this.app.vault.adapter.exists(folderPath))) { - await this.app.vault.createFolder(folderPath); + const folderName = this.settings.targetFolder; + const cuboxFolder = this.app.vault.getAbstractFileByPath(folderName) + if (!(cuboxFolder instanceof TFolder)) { + await this.app.vault.createFolder(folderName) } } formatLastSyncTime(): string { if (!this.settings.lastSyncTime) { - return 'Never'; + return 'never'; } return formatDateTime(new Date(this.settings.lastSyncTime).toISOString(), 'yyyy-MM-dd HH:mm'); diff --git a/src/modal/folderSelectModal.ts b/src/modal/folderSelectModal.ts index 67ad974..db99b46 100644 --- a/src/modal/folderSelectModal.ts +++ b/src/modal/folderSelectModal.ts @@ -1,6 +1,5 @@ import { App, Modal, Setting, Notice } from 'obsidian'; import { CuboxFolder } from '../cuboxApi'; -import { ModalStyleManager } from './modalStyles'; export const ALL_FOLDERS_ID = 'all_folders'; @@ -25,9 +24,6 @@ export class FolderSelectModal extends Modal { initialSelectedFolders.forEach(id => { if (id) this.selectedFolders.add(id); }); - } else { - // 如果没有初始选择,默认选中"All Items" - this.selectedFolders.add(ALL_FOLDERS_ID); } this.onConfirm = onConfirm; @@ -37,9 +33,9 @@ export class FolderSelectModal extends Modal { const { contentEl } = this; contentEl.createEl('h2', { text: 'Manage Cubox folders to be synced' }); - contentEl.addClass('cubox-folder-select-modal'); + contentEl.addClass('cubox-modal'); - this.listEl = contentEl.createDiv({ cls: 'folder-list-container' }); + this.listEl = contentEl.createDiv({ cls: 'folder-list-container cubox-list-container' }); this.footerEl = contentEl.createDiv({ cls: 'modal-footer' }); // 创建文件夹列表 @@ -67,25 +63,13 @@ export class FolderSelectModal extends Modal { this.onConfirm(resultFolders); this.close(); }); - - // 添加样式 - this.addStyles(); - } - - private addStyles() { - // 使用通用样式管理器添加样式 - ModalStyleManager.addModalStyles( - 'cubox-folder-modal-styles', - 'cubox-folder-select-modal', - 'folder-list-container' - ); } private createFolderList() { this.listEl.empty(); const allItemsSetting = new Setting(this.listEl) - .setName('All Items'); + .setName('All items'); if (this.selectedFolders.has(ALL_FOLDERS_ID)) { allItemsSetting.settingEl.addClass('is-selected'); @@ -153,7 +137,5 @@ export class FolderSelectModal extends Modal { onClose() { const { contentEl } = this; contentEl.empty(); - - ModalStyleManager.removeModalStyles('cubox-folder-modal-styles'); } } \ No newline at end of file diff --git a/src/modal/modalStyles.ts b/src/modal/modalStyles.ts deleted file mode 100644 index 07f9eb9..0000000 --- a/src/modal/modalStyles.ts +++ /dev/null @@ -1,171 +0,0 @@ - -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; - } - - /* 调整设置项的布局,为左侧的 checkbox 腾出空间 */ - .${listContainerClass} .setting-item { - position: relative; - padding-left: 36px; - border-radius: 4px; - transition: background-color 0.1s ease; - display: flex; - align-items: center; - min-height: 40px; - box-sizing: border-box; - } - .${listContainerClass} .setting-item:hover { - background-color: var(--background-modifier-hover); - } - /* 确保所有设置项的名称垂直居中 */ - .${listContainerClass} .setting-item-name { - display: flex; - align-items: center; - padding: 0; - margin: 0; - } - /* 添加自定义 checkbox 样式 - 通用样式 */ - .${listContainerClass} .setting-item::before { - content: ""; - position: absolute; - width: 16px; - height: 16px; - border: 1px solid var(--checkbox-border-color, var(--background-modifier-border)); - border-radius: 3px; - background-color: var(--checkbox-color, var(--background-primary)); - box-sizing: border-box; - } - - /* 为第一行和其他行分别设置不同的定位 */ - .${listContainerClass} .setting-item:first-child::before { - left: 10px; - top: 12px; /* 为第一行特别调整 */ - } - - .${listContainerClass} .setting-item:not(:first-child)::before { - left: 10px; - top: 50%; - transform: translateY(-50%); - } - - /* 选中状态的 checkbox */ - .${listContainerClass} .setting-item.is-selected::before { - background-color: var(--checkbox-color-checked, var(--interactive-accent)); - border-color: var(--checkbox-border-color-checked, var(--interactive-accent)); - } - - /* 添加选中状态的勾选标记 - 通用样式 */ - .${listContainerClass} .setting-item.is-selected::after { - content: ""; - position: absolute; - width: 5px; - height: 9px; - border-right: 2px solid var(--text-on-accent); - border-bottom: 2px solid var(--text-on-accent); - } - - /* 为第一行和其他行分别设置不同的定位 */ - .${listContainerClass} .setting-item:first-child.is-selected::after { - left: 14px; - top: 13px; /* 为第一行特别调整 */ - transform: rotate(45deg); - } - - .${listContainerClass} .setting-item:not(:first-child).is-selected::after { - left: 14px; - top: 50%; - transform: translateY(-60%) rotate(45deg); - } - - /* 确保第一行和其他行的对齐一致 */ - .${listContainerClass} .setting-item:first-child { - margin-top: 0; - padding-top: 0; - } - - /* 确保第一行文本与复选框对齐 */ - .${listContainerClass} .setting-item:first-child .setting-item-name { - margin-top: 0; - padding-top: 0; - position: relative; - top: 6px; /* 微调第一行文本位置 */ - font-weight: bold; /* 为第一行文字添加加粗样式 */ - } - - /* 禁用状态的样式 */ - .${listContainerClass} .setting-item.is-disabled { - opacity: 0.3; - pointer-events: none; - } - - /* 添加横线样式用于表示禁用状态的复选框 */ - .${listContainerClass} .setting-item.is-disabled::before { - background-color: var(--background-primary); - border-color: var(--background-modifier-border); - } - - .${listContainerClass} .setting-item.is-disabled::after { - content: ""; - position: absolute; - width: 8px; - height: 2px; - background-color: var(--text-muted); - left: 14px; - top: 50%; - transform: translateY(-50%); - border: none; - } - - /* 确保所有行的高度一致 */ - .${listContainerClass} .setting-item-heading, - .${listContainerClass} .setting-item-name { - line-height: normal; - margin: auto 0; - } - `; - 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(); - } -} \ No newline at end of file diff --git a/src/modal/statusSelectModal.ts b/src/modal/statusSelectModal.ts index 434801f..bbd0167 100644 --- a/src/modal/statusSelectModal.ts +++ b/src/modal/statusSelectModal.ts @@ -1,5 +1,4 @@ import { App, Modal, Setting, Notice } from 'obsidian'; -import { ModalStyleManager } from './modalStyles'; export interface ContentStatus { id: string; @@ -11,7 +10,7 @@ export const ALL_STATUS_ID = 'all'; export class StatusSelectModal extends Modal { private statuses: ContentStatus[] = [ - { id: 'all', name: 'All Items' }, + { id: 'all', name: 'All items' }, { id: 'read', name: 'Already read items only', value: true }, { id: 'starred', name: 'Starred items only', value: true }, { id: 'annotated', name: 'Annotated items only', value: true } @@ -41,8 +40,6 @@ export class StatusSelectModal extends Modal { initialSelected.forEach(id => { if (id) this.selectedStatuses.add(id); }); - } else { - this.selectedStatuses.add(ALL_STATUS_ID); } } @@ -50,9 +47,9 @@ export class StatusSelectModal extends Modal { const { contentEl } = this; contentEl.createEl('h2', { text: 'Manage Cubox content status to be synced' }); - contentEl.addClass('cubox-status-select-modal'); + contentEl.addClass('cubox-modal'); - this.listEl = contentEl.createDiv({ cls: 'status-list-container' }); + this.listEl = contentEl.createDiv({ cls: 'status-list-container cubox-list-container' }); this.footerEl = contentEl.createDiv({ cls: 'modal-footer' }); // 创建状态列表 @@ -81,16 +78,6 @@ export class StatusSelectModal extends Modal { this.onSave(selectedIds, statusValues); this.close(); }); - - this.addStyles(); - } - - private addStyles() { - ModalStyleManager.addModalStyles( - 'cubox-status-modal-styles', - 'cubox-status-select-modal', - 'status-list-container' - ); } private createStatusList() { @@ -139,10 +126,10 @@ export class StatusSelectModal extends Modal { this.selectedStatuses.clear(); this.selectedStatuses.add(ALL_STATUS_ID); - // 当选择 All 时,所有状态值设为 true + // 当选择 All 时,所有状态值设为 false this.statuses.forEach(status => { if (status.id !== 'all') { - this.statusValues.set(status.id, true); + this.statusValues.set(status.id, false); } }); } else { @@ -154,16 +141,10 @@ export class StatusSelectModal extends Modal { // 如果选择了其他选项,移除"All Items" this.selectedStatuses.delete(ALL_STATUS_ID); this.selectedStatuses.add(statusId); - - this.statuses.forEach(status => { - if (status.id !== 'all') { - this.statusValues.set(status.id, status.id === statusId); - } - }); } else { this.selectedStatuses.delete(statusId); - this.statusValues.set(statusId, false); } + this.statusValues.set(statusId, isSelected); } } @@ -174,7 +155,5 @@ export class StatusSelectModal extends Modal { onClose() { const { contentEl } = this; contentEl.empty(); - - ModalStyleManager.removeModalStyles('cubox-status-modal-styles'); } } \ No newline at end of file diff --git a/src/modal/tagSelectModal.ts b/src/modal/tagSelectModal.ts index 983d8de..9acd79c 100644 --- a/src/modal/tagSelectModal.ts +++ b/src/modal/tagSelectModal.ts @@ -1,6 +1,5 @@ import { App, Modal, Setting, Notice } from 'obsidian'; import { CuboxTag } from '../cuboxApi'; -import { ModalStyleManager } from './modalStyles'; export const ALL_ITEMS = 'all_items'; export const NO_TAGS_ID = ''; @@ -26,9 +25,6 @@ export class TagSelectModal extends Modal { initialSelectedTags.forEach(id => { this.selectedTags.add(id); }); - } else { - // 如果没有初始选择,默认选中"All Items" - this.selectedTags.add(ALL_ITEMS); } this.onConfirm = onConfirm; @@ -38,9 +34,9 @@ export class TagSelectModal extends Modal { const { contentEl } = this; contentEl.createEl('h2', { text: 'Manage Cubox tags to be synced' }); - contentEl.addClass('cubox-tag-select-modal'); + contentEl.addClass('cubox-modal'); - this.listEl = contentEl.createDiv({ cls: 'tag-list-container' }); + this.listEl = contentEl.createDiv({ cls: 'tag-list-container cubox-list-container' }); this.footerEl = contentEl.createDiv({ cls: 'modal-footer' }); // 创建标签列表 @@ -65,16 +61,6 @@ export class TagSelectModal extends Modal { this.onConfirm(resultTags); this.close(); }); - - this.addStyles(); - } - - private addStyles() { - ModalStyleManager.addModalStyles( - 'cubox-tag-modal-styles', - 'cubox-tag-select-modal', - 'tag-list-container' - ); } private createTagList() { @@ -82,7 +68,7 @@ export class TagSelectModal extends Modal { // 添加"All Items"选项 const allItemsSetting = new Setting(this.listEl) - .setName('All Items'); + .setName('All items'); if (this.selectedTags.has(ALL_ITEMS)) { allItemsSetting.settingEl.addClass('is-selected'); @@ -97,7 +83,7 @@ export class TagSelectModal extends Modal { // 添加"No Tags"选项 const noTagsSetting = new Setting(this.listEl) - .setName('No Tags'); + .setName('No tags'); // 如果"All Items"被选中,禁用其他选项 if (this.selectedTags.has(ALL_ITEMS)) { @@ -169,7 +155,5 @@ export class TagSelectModal extends Modal { onClose() { const { contentEl } = this; contentEl.empty(); - - ModalStyleManager.removeModalStyles('cubox-tag-modal-styles'); } } \ No newline at end of file diff --git a/src/modal/typeSelectModal.ts b/src/modal/typeSelectModal.ts index db2991d..8192519 100644 --- a/src/modal/typeSelectModal.ts +++ b/src/modal/typeSelectModal.ts @@ -1,5 +1,4 @@ import { App, Modal, Setting, Notice } from 'obsidian'; -import { ModalStyleManager } from './modalStyles'; export const ALL_CONTENT_TYPES = [ 'Article', @@ -27,11 +26,6 @@ export class TypeSelectModal extends Modal { initialSelected.forEach(id => { if (id) this.selectedTypes.add(id); }); - } else { - // 如果没有初始选择,默认选中所有类型 - ALL_CONTENT_TYPES.forEach(type => { - this.selectedTypes.add(type); - }); } } @@ -40,9 +34,9 @@ export class TypeSelectModal extends Modal { contentEl.empty(); contentEl.createEl('h2', { text: 'Manage Cubox content types to be synced' }); - contentEl.addClass('cubox-type-select-modal'); + contentEl.addClass('cubox-modal'); - this.listEl = contentEl.createDiv({ cls: 'type-list-container' }); + this.listEl = contentEl.createDiv({ cls: 'type-list-container cubox-list-container' }); this.footerEl = contentEl.createDiv({ cls: 'modal-footer' }); // 创建类型列表 @@ -65,27 +59,6 @@ export class TypeSelectModal extends Modal { this.onSave(selectedTypes); this.close(); }); - - this.addStyles(); - } - - private addStyles() { - ModalStyleManager.addModalStyles( - 'cubox-modal-styles', - 'cubox-type-select-modal', - 'type-list-container' - ); - - // 添加额外的样式,覆盖第一行字体加粗的样式 - const styleEl = document.createElement('style'); - styleEl.id = 'cubox-type-modal-additional-styles'; - styleEl.textContent = ` - /* 覆盖第一行字体加粗的样式 */ - .type-list-container .setting-item:first-child .setting-item-name { - font-weight: normal; - } - `; - document.head.appendChild(styleEl); } private createTypeList() { @@ -125,10 +98,5 @@ export class TypeSelectModal extends Modal { onClose() { const { contentEl } = this; contentEl.empty(); - - ModalStyleManager.removeModalStyles('cubox-modal-styles'); - - const additionalStyleEl = document.getElementById('cubox-type-modal-additional-styles'); - if (additionalStyleEl) additionalStyleEl.remove(); } } \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index d834d91..71b6ffa 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,5 @@ import { DateTime } from 'luxon'; +import { normalizePath } from 'obsidian'; // 文件名非法字符正则表达式 export const ILLEGAL_CHAR_REGEX_FILE = /[<>:"/\\|?*\u0000-\u001F]/g; @@ -30,7 +31,7 @@ export const replaceIllegalCharsFolder = (str: string): string => { */ export const generateSafeFileArticleName = (title: string): string => { const safeTitle = replaceIllegalCharsFile(title).trim(); - return safeTitle; + return normalizePath(safeTitle); }; /** diff --git a/styles.css b/styles.css index 315e48d..f727d1c 100644 --- a/styles.css +++ b/styles.css @@ -7,29 +7,15 @@ If your plugin does not need CSS, delete this file. */ -/* 文件夹选择模态框样式 */ -.folder-list { - max-height: 300px; - overflow-y: auto; - margin: 10px 0; - border: 1px solid var(--background-modifier-border); - border-radius: 4px; - padding: 5px; -} - .modal-footer { display: flex; justify-content: flex-end; - margin-top: 20px; - gap: 10px; + padding: 16px; + border-top: 1px solid var(--background-modifier-border); } .modal-footer button { - padding: 6px 12px; - border-radius: 4px; - background-color: var(--interactive-normal); - color: var(--text-normal); - cursor: pointer; + margin-left: 8px; } .modal-footer button.mod-cta { @@ -78,4 +64,150 @@ If your plugin does not need CSS, delete this file. .reference-link:hover { text-decoration: underline; +} + +/* Modal Dialog Styles */ +.cubox-modal { + display: flex; + flex-direction: column; + height: 100%; +} + +.cubox-list-container { + flex-grow: 1; + overflow-y: auto; + padding-top: 20px; + padding-right: 10px; + overflow-anchor: none; + scroll-behavior: auto; + height: 100%; + max-height: 65vh; +} + +/* Setting item styles for modal dialogs */ +.cubox-list-container .setting-item { + position: relative; + padding-left: 36px; + border-radius: 4px; + transition: background-color 0.1s ease; + display: flex; + align-items: center; + min-height: 40px; + box-sizing: border-box; +} + +.cubox-list-container .setting-item:hover { + background-color: var(--background-modifier-hover); +} + +/* Setting item name styles */ +.cubox-list-container .setting-item-name { + display: flex; + align-items: center; + padding: 0; + margin: 0; +} + +/* Custom checkbox styles */ +.cubox-list-container .setting-item::before { + content: ""; + position: absolute; + width: 16px; + height: 16px; + border: 1px solid var(--checkbox-border-color, var(--background-modifier-border)); + border-radius: 3px; + background-color: var(--checkbox-color, var(--background-primary)); + box-sizing: border-box; +} + +/* First child checkbox positioning */ +.cubox-list-container .setting-item:first-child::before { + left: 10px; + top: 12px; +} + +/* Other items checkbox positioning */ +.cubox-list-container .setting-item:not(:first-child)::before { + left: 10px; + top: 50%; + transform: translateY(-50%); +} + +/* Selected checkbox state */ +.cubox-list-container .setting-item.is-selected::before { + background-color: var(--checkbox-color-checked, var(--interactive-accent)); + border-color: var(--checkbox-border-color-checked, var(--interactive-accent)); +} + +/* Checkmark for selected items */ +.cubox-list-container .setting-item.is-selected::after { + content: ""; + position: absolute; + width: 5px; + height: 9px; + border-right: 2px solid var(--text-on-accent); + border-bottom: 2px solid var(--text-on-accent); +} + +/* First child checkmark positioning */ +.cubox-list-container .setting-item:first-child.is-selected::after { + left: 14px; + top: 13px; + transform: rotate(45deg); +} + +/* Other items checkmark positioning */ +.cubox-list-container .setting-item:not(:first-child).is-selected::after { + left: 14px; + top: 50%; + transform: translateY(-60%) rotate(45deg); +} + +/* First item special styling */ +.cubox-list-container .setting-item:first-child { + margin-top: 0; + padding-top: 0; +} + +.cubox-list-container .setting-item:first-child .setting-item-name { + margin-top: 0; + padding-top: 0; + position: relative; + top: 6px; + font-weight: bold; +} + +/* Disabled state styling */ +.cubox-list-container .setting-item.is-disabled { + opacity: 0.3; + pointer-events: none; +} + +.cubox-list-container .setting-item.is-disabled::before { + background-color: var(--background-primary); + border-color: var(--background-modifier-border); +} + +.cubox-list-container .setting-item.is-disabled::after { + content: ""; + position: absolute; + width: 8px; + height: 2px; + background-color: var(--text-muted); + left: 14px; + top: 50%; + transform: translateY(-50%); + border: none; +} + +/* Consistent line height for all items */ +.cubox-list-container .setting-item-heading, +.cubox-list-container .setting-item-name { + line-height: normal; + margin: auto 0; +} + +/* Override first row bold font for type modal */ +.type-list-container .setting-item:first-child .setting-item-name { + font-weight: normal; } \ No newline at end of file diff --git a/versions.json b/versions.json index 1da423a..1d7eb49 100644 --- a/versions.json +++ b/versions.json @@ -2,5 +2,6 @@ "1.0.0": "0.15.0", "1.0.1": "0.15.0", "1.0.2": "0.15.0", - "1.0.3": "0.15.0" + "1.0.3": "0.15.0", + "1.0.4": "0.15.0" } \ No newline at end of file