From 1718504eab509c5c635ff01135f4d803763bdaf0 Mon Sep 17 00:00:00 2001 From: Devon22 Date: Wed, 18 Jun 2025 00:24:14 +0800 Subject: [PATCH] 2.3.0 --- manifest.json | 2 +- package-lock.json | 4 +- package.json | 2 +- src/FileWatcher.ts | 44 +++++++---- src/FolderSelectionModal.ts | 18 +++++ src/GridView.ts | 154 ++++++++++++++++++++++++++---------- src/fileUtils.ts | 33 ++++++++ src/settings.ts | 132 ++++++++++++++++++++----------- src/translations.ts | 42 ++++++++++ styles.css | 42 +++++++++- versions.json | 2 +- 11 files changed, 361 insertions(+), 114 deletions(-) diff --git a/manifest.json b/manifest.json index fd60b30..bdd2078 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "gridexplorer", "name": "GridExplorer", - "version": "2.2.2", + "version": "2.3.0", "minAppVersion": "1.1.0", "description": "Browse note files in a grid view.", "author": "Devon22", diff --git a/package-lock.json b/package-lock.json index c51929b..c6358da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gridexplorer", - "version": "2.2.2", + "version": "2.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gridexplorer", - "version": "2.2.2", + "version": "2.3.0", "license": "MIT", "devDependencies": { "@types/node": "^16.11.6", diff --git a/package.json b/package.json index 04c895d..fce1c1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gridexplorer", - "version": "2.2.2", + "version": "2.3.0", "description": "Browse note files in a grid view.", "main": "main.js", "scripts": { diff --git a/src/FileWatcher.ts b/src/FileWatcher.ts index 8ea9e5c..89e9a68 100644 --- a/src/FileWatcher.ts +++ b/src/FileWatcher.ts @@ -7,6 +7,7 @@ export class FileWatcher { private plugin: GridExplorerPlugin; private gridView: GridView; private app: App; + private renderTimer: number | null = null; // 用於去抖動 render() constructor(plugin: GridExplorerPlugin, gridView: GridView) { this.plugin = plugin; @@ -24,27 +25,25 @@ export class FileWatcher { this.plugin.registerEvent( this.app.vault.on('create', (file) => { if (file instanceof TFile) { - if(this.gridView.sourceMode === 'random-note' || - this.gridView.sourceMode === 'bookmarks' || - this.gridView.sourceMode === 'outgoinglinks') { + if(this.gridView.sourceMode === 'random-note') { return; } else if (this.gridView.sourceMode === 'recent-files') { if(isDocumentFile(file) || (isMediaFile(file) && this.gridView.randomNoteIncludeMedia)) { - this.gridView.render(); + this.scheduleRender(); } } else if (this.gridView.sourceMode === 'folder') { if (this.gridView.sourcePath && this.gridView.searchQuery === '') { const fileDirPath = file.path.split('/').slice(0, -1).join('/') || '/'; if (fileDirPath === this.gridView.sourcePath) { - this.gridView.render(); + this.scheduleRender(); } } } else if (this.gridView.sourceMode === 'backlinks') { if(isDocumentFile(file)) { - this.gridView.render(); + this.scheduleRender(); } } else { - this.gridView.render(); + this.scheduleRender(); } } }) @@ -54,21 +53,21 @@ export class FileWatcher { this.plugin.registerEvent( this.app.vault.on('delete', (file) => { if (file instanceof TFile) { - if(this.gridView.sourceMode === 'random-note' || this.gridView.sourceMode === 'bookmarks') { + if(this.gridView.sourceMode === 'random-note') { return; } else if (this.gridView.sourceMode === 'recent-files') { if(isDocumentFile(file) || (isMediaFile(file) && this.gridView.randomNoteIncludeMedia)) { - this.gridView.render(); + this.scheduleRender(); } } else if (this.gridView.sourceMode === 'folder') { if (this.gridView.sourcePath && this.gridView.searchQuery === '') { const fileDirPath = file.path.split('/').slice(0, -1).join('/') || '/'; if (fileDirPath === this.gridView.sourcePath) { - this.gridView.render(); + this.scheduleRender(); } } } else { - this.gridView.render(); + this.scheduleRender(); } } }) @@ -85,11 +84,11 @@ export class FileWatcher { const fileDirPath = file.path.split('/').slice(0, -1).join('/') || '/'; const oldDirPath = oldPath.split('/').slice(0, -1).join('/') || '/'; if (fileDirPath === this.gridView.sourcePath || oldDirPath === this.gridView.sourcePath) { - this.gridView.render(); + this.scheduleRender(); } } } else { - this.gridView.render(); + this.scheduleRender(); } } }) @@ -99,7 +98,7 @@ export class FileWatcher { this.plugin.registerEvent( (this.app as any).internalPlugins.plugins.bookmarks.instance.on('changed', () => { if (this.gridView.sourceMode === 'bookmarks') { - this.gridView.render(); + this.scheduleRender(); } }) ); @@ -110,11 +109,24 @@ export class FileWatcher { if (file instanceof TFile) { if ((this.gridView.sourceMode === 'backlinks' || this.gridView.sourceMode === 'outgoinglinks') && this.gridView.searchQuery === '') { - this.gridView.render(); + this.scheduleRender(); } } }) ); } - + + // 以 200ms 去抖動的方式排程 render,避免短時間內大量重繪 + private scheduleRender = (): void => { + if (this.renderTimer !== null) { + clearTimeout(this.renderTimer); + } + // 200ms 延遲,可視需求調整 + this.renderTimer = window.setTimeout((): void => { + console.log('123'); + this.gridView.render(); + this.renderTimer = null; + }, 200); + } + } diff --git a/src/FolderSelectionModal.ts b/src/FolderSelectionModal.ts index ca65e39..efcf068 100644 --- a/src/FolderSelectionModal.ts +++ b/src/FolderSelectionModal.ts @@ -200,6 +200,24 @@ export class FolderSelectionModal extends Modal { this.folderOptions.push(randomNoteOption); } + // 建立任務選項 + if (this.plugin.settings.showTasksMode) { + const tasksOption = this.folderOptionsContainer.createEl('div', { + cls: 'ge-grid-view-folder-option ge-special-option', + text: `☑️ ${t('tasks_mode')}` + }); + + tasksOption.addEventListener('click', () => { + if (this.activeView) { + this.activeView.setSource('tasks', '', true); + } else { + this.plugin.activateView('tasks'); + } + this.close(); + }); + this.folderOptions.push(tasksOption); + } + // 建立根目錄選項 const rootFolderOption = this.folderOptionsContainer.createEl('div', { cls: 'ge-grid-view-folder-option', diff --git a/src/GridView.ts b/src/GridView.ts index f3f0e8c..f788c80 100644 --- a/src/GridView.ts +++ b/src/GridView.ts @@ -32,6 +32,7 @@ export class GridView extends ItemView { minMode: boolean = false; // 最小模式 showIgnoredFolders: boolean = false; // 顯示忽略資料夾 pinnedList: string[] = []; // 置頂清單 + taskFilter: string = 'uncompleted'; // 任務分類 constructor(leaf: WorkspaceLeaf, plugin: GridExplorerPlugin) { super(leaf); @@ -69,6 +70,8 @@ export class GridView extends ItemView { return 'links-going-out'; } else if (this.sourceMode === 'random-note') { return 'dice'; + } else if (this.sourceMode === 'tasks') { + return 'square-check-big'; } else if (this.sourceMode === 'recent-files') { return 'calendar-days'; } else if (this.sourceMode === 'all-files') { @@ -93,6 +96,8 @@ export class GridView extends ItemView { return t('outgoinglinks_mode'); } else if (this.sourceMode === 'random-note') { return t('random_note_mode'); + } else if (this.sourceMode === 'tasks') { + return t('tasks_mode'); } else if (this.sourceMode === 'recent-files') { return t('recent_files_mode'); } else if (this.sourceMode === 'all-files') { @@ -171,7 +176,7 @@ export class GridView extends ItemView { // 只有當點擊的是頂部按鈕區域本身(而不是其中的按鈕)時才觸發捲動 if (event.target === headerButtonsDiv) { event.preventDefault(); - // 取得網格容器(應該是在 grid_render 後建立的第二個子元素) + // 取得網格容器 const gridContainer = this.containerEl.querySelector('.ge-grid-container'); if (gridContainer) { gridContainer.scrollTo({ @@ -663,6 +668,60 @@ export class GridView extends ItemView { }); } + if (this.sourceMode === 'tasks' && this.searchQuery === '') { + // 建立任務分類按鈕,區分未完成、已完成、全部 + const taskFilterButton = headerButtonsDiv.createEl('button', { + attr: { 'aria-label': t('task_filter') } + }); + if (this.taskFilter === 'uncompleted') { + setIcon(taskFilterButton, 'square'); + } else if (this.taskFilter === 'completed') { + setIcon(taskFilterButton, 'square-check-big'); + } else { + setIcon(taskFilterButton, 'square-asterisk'); + } + // 建立下拉選單,uncompleted、completed、all + const menu = new Menu(); + menu.addItem((item) => { + item.setTitle(t('uncompleted')) + .setChecked(this.taskFilter === 'uncompleted') + .setIcon('square') + .onClick(() => { + this.taskFilter = 'uncompleted'; + taskFilterButton.textContent = t('uncompleted'); + setIcon(taskFilterButton, 'square'); + this.render(); + }); + }); + menu.addItem((item) => { + item.setTitle(t('completed')) + .setChecked(this.taskFilter === 'completed') + .setIcon('square-check-big') + .onClick(() => { + this.taskFilter = 'completed'; + taskFilterButton.textContent = t('completed'); + setIcon(taskFilterButton, 'square-check-big'); + this.render(); + }); + }); + menu.addItem((item) => { + item.setTitle(t('all')) + .setChecked(this.taskFilter === 'all') + .setIcon('square-asterisk') + .onClick(() => { + this.taskFilter = 'all'; + taskFilterButton.textContent = t('all'); + setIcon(taskFilterButton, 'square-asterisk'); + this.render(); + }); + }); + + // 點擊按鈕時顯示下拉選單 + taskFilterButton.addEventListener('click', (event) => { + menu.showAtMouseEvent(event); + }); + } + // 創建內容區域 const contentEl = this.containerEl.createDiv('view-content'); @@ -747,35 +806,42 @@ export class GridView extends ItemView { } // 如果是資料夾模式且沒有搜尋結果,顯示目前資料夾名稱 - // if (this.sourceMode === 'folder' && this.searchQuery === '' && this.sourcePath !== '/') { - // const folderName = this.sourcePath.split('/').pop(); - // const folderNameContainer = container.createDiv('ge-foldername-content'); - // if (folderNameContainer) { - // folderNameContainer.createEl('span', { text: `/ ${folderName}` }); - // } - // } - - // 如果啟用了顯示"回上層資料夾"選項 if (this.sourceMode === 'folder' && this.searchQuery === '' && this.plugin.settings.showParentFolderItem && this.sourcePath !== '/') { - // 創建"回上層資料夾" - const parentFolderEl = container.createDiv('ge-grid-item ge-folder-item'); - this.gridItems.push(parentFolderEl); // 添加到網格項目數組 - // 獲取父資料夾路徑 - const parentPath = this.sourcePath.split('/').slice(0, -1).join('/') || '/'; - // 設置資料夾路徑屬性 - parentFolderEl.dataset.folderPath = parentPath; - - const contentArea = parentFolderEl.createDiv('ge-content-area'); - const titleContainer = contentArea.createDiv('ge-title-container'); - const customFolderIcon = this.plugin.settings.customFolderIcon; - titleContainer.createEl('span', { cls: 'ge-title', text: `${customFolderIcon} ..`.trim() }); - - // 回上層資料夾事件 - parentFolderEl.addEventListener('click', () => { - this.setSource('folder', parentPath, true); - this.clearSelection(); - }); + const pathParts = this.sourcePath.split('/'); + const parentPath = pathParts.slice(0, -1).join('/') || '/'; + let parentFolderName = pathParts.slice(-2, -1)[0] || '/'; + const currentFolderName = pathParts.pop() || t('root'); + + // 若為根目錄,以 'root' 顯示 + if (parentPath === '/' || parentFolderName === '/' || parentFolderName === '') { + parentFolderName = t('root'); + } + + const folderNameContainer = container.createDiv('ge-foldername-content'); + + if (folderNameContainer) { + // 建立可點擊的上層資料夾名稱 + const customFolderIcon = this.plugin.settings.customFolderIcon; + const parentFolderLink = folderNameContainer.createEl('a', { + text: `${customFolderIcon} ${parentFolderName}`.trim(), + cls: 'ge-parent-folder-link' + }); + + // 點擊後跳轉到上層資料夾 + parentFolderLink.addEventListener('click', (event) => { + event.preventDefault(); + event.stopPropagation(); + this.setSource('folder', parentPath, true); + this.clearSelection(); + }); + + // 分隔符號 + folderNameContainer.createEl('span', { text: ' > ' }); + + // 目前資料夾名稱 + folderNameContainer.createEl('span', { text: currentFolderName }); + } } // 如果是資料夾模式,先顯示所有子資料夾 @@ -1022,20 +1088,22 @@ export class GridView extends ItemView { } // 資料夾渲染完插入 break(僅當有資料夾時或有顯示回上一層資料夾項目時) - if (subfolders.length > 0 || (subfolders.length === 0 && this.plugin.settings.showParentFolderItem)) { + if (subfolders.length > 0 || (subfolders.length === 0 && this.plugin.settings.dateDividerMode !== 'none' && this.plugin.settings.showParentFolderItem)) { container.createDiv('ge-break'); } } } + let loadingDiv: HTMLElement | null = null; + if (this.searchQuery || this.sourceMode === 'tasks') { + // 顯示搜尋中的提示 + loadingDiv = container.createDiv({ text: t('searching'), cls: 'ge-loading-indicator' }); + } + let files: TFile[] = []; // 使用 Map 來記錄原始順序 let fileIndexMap = new Map(); if (this.searchQuery) { - // 顯示搜尋中的提示 - const loadingDiv = container.createDiv('ge-loading-indicator'); - loadingDiv.setText(t('searching')); - // 取得 vault 中所有支援的檔案 let allFiles: TFile[] = []; if (this.searchAllFiles) { @@ -1112,9 +1180,6 @@ export class GridView extends ItemView { // 忽略檔案 files = ignoredFiles(files, this); - - // 移除搜尋中的提示 - loadingDiv.remove(); } else { // 無搜尋關鍵字的情況 files = await getFiles(this, this.randomNoteIncludeMedia); @@ -1133,6 +1198,10 @@ export class GridView extends ItemView { } } + if (loadingDiv) { + loadingDiv.remove(); + } + // 如果沒有檔案,顯示提示訊息 if (files.length === 0) { const noFilesDiv = container.createDiv('ge-no-files'); @@ -1246,7 +1315,7 @@ export class GridView extends ItemView { //將預覽文字設定到標題的 title 屬性中 const titleEl = fileEl.querySelector('.ge-title'); if (titleEl && pEl) { - titleEl.setAttribute('title', `<${titleEl.textContent}>\n${pEl.textContent}` || ''); + titleEl.setAttribute('title', `${titleEl.textContent}\n${pEl.textContent}` || ''); } if (frontMatterInfo.exists) { @@ -1282,6 +1351,7 @@ export class GridView extends ItemView { if (imageAreaEl) { imageAreaEl.remove(); } + fileEl.style.height = '100%'; } } @@ -1826,10 +1896,10 @@ export class GridView extends ItemView { const file = this.app.vault.getAbstractFileByPath(path); if (file instanceof TFile) { try { - // 計算新的檔案路徑 + // 計算新的檔案路徑 const newPath = `${folderPath}/${file.name}`; - // 移動檔案 - await this.app.fileManager.renameFile(file, newPath); + // 移動檔案 + await this.app.fileManager.renameFile(file, newPath); } catch (error) { console.error(`An error occurred while moving the file ${file.path}:`, error); } @@ -1837,7 +1907,7 @@ export class GridView extends ItemView { } // 重新渲染視圖 - this.render(); + // this.render(); return; } catch (error) { console.error('Error parsing dragged files data:', error); @@ -1865,7 +1935,7 @@ export class GridView extends ItemView { // 移動檔案 await this.app.fileManager.renameFile(file, newPath); // 重新渲染視圖 - this.render(); + // this.render(); } catch (error) { console.error('An error occurred while moving the file:', error); } diff --git a/src/fileUtils.ts b/src/fileUtils.ts index 760d4c2..69d1813 100644 --- a/src/fileUtils.ts +++ b/src/fileUtils.ts @@ -324,6 +324,39 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean): bookmarks.forEach(processBookmarkItem); return Array.from(bookmarkedFiles) as TFile[]; + } else if (sourceMode === 'tasks') { + // 任務模式 + const filesWithTasks = new Set(); + const markdownFiles = app.vault.getMarkdownFiles(); + + for (const file of markdownFiles) { + try { + const content = await app.vault.cachedRead(file); + let shouldAdd = false; + // 用 gridView.taskFilter 匹配 uncompleted、completed、all 任務 + if (gridView.taskFilter === 'uncompleted') { + // 只匹配未完成的任務: - [ ] 或 * [ ] + shouldAdd = /^[\s]*[-*]\s*\[\s*\](?![^\[]*\[\s*[^\s\]]+\]).*$/m.test(content); + } else if (gridView.taskFilter === 'completed') { + // 只匹配所有任務均已完成的檔案(至少有一個已完成且沒有未完成) + const hasCompleted = /^[\s]*[-*]\s*\[x\](?![^\[]*\[\s*[^\s\]]+\]).*$/m.test(content); + const hasIncomplete = /^[\s]*[-*]\s*\[\s*\](?![^\[]*\[\s*[^\s\]]+\]).*$/m.test(content); + shouldAdd = hasCompleted && !hasIncomplete; + } else if (gridView.taskFilter === 'all') { + // 匹配任何任務(已完成或未完成皆可) + const hasIncomplete = /^[\s]*[-*]\s*\[\s*\](?![^\[]*\[\s*[^\s\]]+\]).*$/m.test(content); + const hasCompleted = /^[\s]*[-*]\s*\[x\](?![^\[]*\[\s*[^\s\]]+\]).*$/m.test(content); + shouldAdd = hasIncomplete || hasCompleted; + } + + if (shouldAdd) { + filesWithTasks.add(file); + } + } catch (error) { + console.error(`Error reading file ${file.path}:`, error); + } + } + return sortFiles(Array.from(filesWithTasks), gridView); } else if (sourceMode === 'all-files') { // 所有筆記模式 const allVaultFiles = app.vault.getFiles().filter(file => { diff --git a/src/settings.ts b/src/settings.ts index b5c9a31..998c457 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -25,6 +25,7 @@ export interface GallerySettings { showAllFilesMode: boolean; // 是否顯示所有檔案模式 showRandomNoteMode: boolean; // 是否顯示隨機筆記模式 showRecentFilesMode: boolean; // 是否顯示最近筆記模式 + showTasksMode: boolean; // 是否顯示任務模式 customFolderIcon: string; // 自訂資料夾圖示 customDocumentExtensions: string; // 自訂文件副檔名(用逗號分隔) recentSources: string[]; // 最近的瀏覽記錄 @@ -52,9 +53,9 @@ export const DEFAULT_SETTINGS: GallerySettings = { summaryLength: 100, // 筆記摘要的字數,預設 100 enableFileWatcher: true, // 預設啟用檔案監控 showMediaFiles: true, // 預設顯示圖片和影片 - showVideoThumbnails: false, // 預設不顯示影片縮圖 + showVideoThumbnails: true, // 預設顯示影片縮圖 defaultOpenLocation: 'tab', // 預設開啟位置:新分頁 - showParentFolderItem: false, // 預設不顯示"返回上级文件夹"選項 + showParentFolderItem: false, // 預設不顯示"返回上層資料夾"選項 reuseExistingLeaf: false, // 預設不重用現有的網格視圖 showBookmarksMode: true, // 預設顯示書籤模式 showSearchMode: true, // 預設顯示搜尋結果模式 @@ -63,6 +64,7 @@ export const DEFAULT_SETTINGS: GallerySettings = { showAllFilesMode: false, // 預設不顯示所有檔案模式 showRandomNoteMode: false, // 預設不顯示隨機筆記模式 showRecentFilesMode: true, // 預設顯示最近筆記模式 + showTasksMode: false, // 預設不顯示任務模式 recentFilesCount: 30, // 預設最近筆記模式顯示的筆數 randomNoteCount: 10, // 預設隨機筆記模式顯示的筆數 customFolderIcon: '📁', // 自訂資料夾圖示 @@ -108,7 +110,7 @@ export class GridExplorerSettingTab extends PluginSettingTab { // 設定是否顯示書籤模式 new Setting(containerEl) - .setName(t('show_bookmarks_mode')) + .setName(`📑 ${t('show_bookmarks_mode')}`) .addToggle(toggle => { toggle .setValue(this.plugin.settings.showBookmarksMode) @@ -120,7 +122,7 @@ export class GridExplorerSettingTab extends PluginSettingTab { // 設定是否顯示搜尋結果模式 new Setting(containerEl) - .setName(t('show_search_mode')) + .setName(`🔍 ${t('show_search_mode')}`) .addToggle(toggle => { toggle .setValue(this.plugin.settings.showSearchMode) @@ -132,7 +134,7 @@ export class GridExplorerSettingTab extends PluginSettingTab { // 設定是否顯示反向連結模式 new Setting(containerEl) - .setName(t('show_backlinks_mode')) + .setName(`🔗 ${t('show_backlinks_mode')}`) .addToggle(toggle => { toggle .setValue(this.plugin.settings.showBacklinksMode) @@ -144,7 +146,7 @@ export class GridExplorerSettingTab extends PluginSettingTab { // 設定是否顯示外部連結模式 new Setting(containerEl) - .setName(t('show_outgoinglinks_mode')) + .setName(`🔗 ${t('show_outgoinglinks_mode')}`) .addToggle(toggle => { toggle .setValue(this.plugin.settings.showOutgoinglinksMode) @@ -156,7 +158,7 @@ export class GridExplorerSettingTab extends PluginSettingTab { // 設定是否顯示所有檔案模式 new Setting(containerEl) - .setName(t('show_all_files_mode')) + .setName(`📔 ${t('show_all_files_mode')}`) .addToggle(toggle => { toggle .setValue(this.plugin.settings.showAllFilesMode) @@ -166,54 +168,90 @@ export class GridExplorerSettingTab extends PluginSettingTab { }); }); - // 設定是否顯示最近檔案模式 + // 最近檔案模式設定 + const recentFilesSetting = new Setting(containerEl) + .setName(`📅 ${t('show_recent_files_mode')}`); + + // 添加切換按鈕 + recentFilesSetting.addToggle(toggle => { + toggle + .setValue(this.plugin.settings.showRecentFilesMode) + .onChange(async (value) => { + this.plugin.settings.showRecentFilesMode = value; + await this.plugin.saveSettings(); + }); + }); + + // 在設定描述區域添加數字輸入框 + const recentDescEl = recentFilesSetting.descEl.createEl('div', { cls: 'ge-setting-desc' }); + + recentDescEl.createEl('span', { text: t('recent_files_count') }); + + const recentInput = recentDescEl.createEl('input', { + type: 'number', + value: this.plugin.settings.recentFilesCount.toString(), + cls: 'ge-setting-number-input' + }); + + recentInput.addEventListener('change', async (e) => { + const target = e.target as HTMLInputElement; + const value = parseInt(target.value); + if (!isNaN(value) && value > 0) { + this.plugin.settings.recentFilesCount = value; + await this.plugin.saveSettings(false); + } else { + target.value = this.plugin.settings.recentFilesCount.toString(); + } + }); + + // 隨機筆記模式設定 + const randomNoteSetting = new Setting(containerEl) + .setName(`🎲 ${t('show_random_note_mode')}`); + + // 添加切換按鈕 + randomNoteSetting.addToggle(toggle => { + toggle + .setValue(this.plugin.settings.showRandomNoteMode) + .onChange(async (value) => { + this.plugin.settings.showRandomNoteMode = value; + await this.plugin.saveSettings(); + }); + }); + + // 在設定描述區域添加數字輸入框 + const descEl = randomNoteSetting.descEl.createEl('div', { cls: 'ge-setting-desc' }); + + descEl.createEl('span', { text: t('random_note_count') }); + + const input = descEl.createEl('input', { + type: 'number', + value: this.plugin.settings.randomNoteCount.toString(), + cls: 'ge-setting-number-input' + }); + + input.addEventListener('change', async (e) => { + const target = e.target as HTMLInputElement; + const value = parseInt(target.value); + if (!isNaN(value) && value > 0) { + this.plugin.settings.randomNoteCount = value; + await this.plugin.saveSettings(false); + } else { + target.value = this.plugin.settings.randomNoteCount.toString(); + } + }); + + // 顯示任務模式 new Setting(containerEl) - .setName(t('show_recent_files_mode')) + .setName(`☑️ ${t('show_tasks_mode')}`) .addToggle(toggle => { toggle - .setValue(this.plugin.settings.showRecentFilesMode) + .setValue(this.plugin.settings.showTasksMode) .onChange(async (value) => { - this.plugin.settings.showRecentFilesMode = value; + this.plugin.settings.showTasksMode = value; await this.plugin.saveSettings(); }); }); - // 最近檔案模式的顯示資料筆數 - new Setting(containerEl) - .setName(t('recent_files_count')) - .addText(text => { - text - .setValue(this.plugin.settings.recentFilesCount.toString()) - .onChange(async (value) => { - this.plugin.settings.recentFilesCount = parseInt(value); - await this.plugin.saveSettings(false); - }); - }); - - // 設定是否顯示隨機筆記模式 - new Setting(containerEl) - .setName(t('show_random_note_mode')) - .addToggle(toggle => { - toggle - .setValue(this.plugin.settings.showRandomNoteMode) - .onChange(async (value) => { - this.plugin.settings.showRandomNoteMode = value; - await this.plugin.saveSettings(); - }); - }); - - // 隨機筆記模式的顯示資料筆數 - new Setting(containerEl) - .setName(t('random_note_count')) - .addText(text => { - text - .setValue(this.plugin.settings.randomNoteCount.toString()) - .onChange(async (value) => { - this.plugin.settings.randomNoteCount = parseInt(value); - await this.plugin.saveSettings(false); - }); - }); - // 媒體檔案設定區域 containerEl.createEl('h3', { text: t('media_files_settings') }); diff --git a/src/translations.ts b/src/translations.ts index 5005628..412fda9 100644 --- a/src/translations.ts +++ b/src/translations.ts @@ -42,6 +42,7 @@ export const TRANSLATIONS: Translations = { 'untitled': '未命名', 'files': '個檔案', 'add': '新增', + 'root': '根目錄', // 視圖標題 'grid_view_title': '網格視圖', @@ -53,6 +54,7 @@ export const TRANSLATIONS: Translations = { 'all_files_mode': '所有檔案', 'recent_files_mode': '最近檔案', 'random_note_mode': '隨機筆記', + 'tasks_mode': '任務', // 排序選項 'sort_name_asc': '名稱 (A → Z)', @@ -124,6 +126,11 @@ export const TRANSLATIONS: Translations = { 'random_note_count': '隨機筆記模式顯示筆數', 'random_note_notes_only': '僅筆記', 'random_note_include_media_files': '包含媒體檔案', + 'show_tasks_mode': '顯示任務模式', + 'task_filter': '任務分類', + 'uncompleted': '未完成', + 'completed': '已完成', + 'all': '全部', // 顯示"返回上層資料夾"選項設定 'show_parent_folder_item': '顯示「返回上層資料夾」', @@ -235,6 +242,7 @@ export const TRANSLATIONS: Translations = { 'untitled': 'Untitled', 'files': 'files', 'add': 'Add', + 'root': 'Root', // View Titles 'grid_view_title': 'Grid view', @@ -246,6 +254,7 @@ export const TRANSLATIONS: Translations = { 'all_files_mode': 'All files', 'recent_files_mode': 'Recent files', 'random_note_mode': 'Random note', + 'tasks_mode': 'Tasks', // Sort Options 'sort_name_asc': 'Name (A → Z)', @@ -317,6 +326,11 @@ export const TRANSLATIONS: Translations = { 'random_note_count': 'Random note count', 'random_note_notes_only': 'Notes Only', 'random_note_include_media_files': 'Include Media Files', + 'show_tasks_mode': 'Show tasks mode', + 'task_filter': 'Task filter', + 'uncompleted': 'Uncompleted', + 'completed': 'Completed', + 'all': 'All', // Show "Parent Folder" option setting 'show_parent_folder_item': 'Show "Parent Folder" item', @@ -428,6 +442,7 @@ export const TRANSLATIONS: Translations = { 'untitled': '未命名', 'files': '个文件', 'add': '添加', + 'root': '根目录', // 视图标题 'grid_view_title': '网格视图', @@ -439,6 +454,7 @@ export const TRANSLATIONS: Translations = { 'all_files_mode': '所有文件', 'recent_files_mode': '最近文件', 'random_note_mode': '随机笔记', + 'tasks_mode': '任务', // 排序选项 'sort_name_asc': '名称 (A → Z)', @@ -510,6 +526,11 @@ export const TRANSLATIONS: Translations = { 'random_note_count': '随机笔记模式显示笔数', 'random_note_notes_only': '仅笔记', 'random_note_include_media_files': '包含媒体文件', + 'show_tasks_mode': '显示任务模式', + 'task_filter': '任务分类', + 'uncompleted': '未完成', + 'completed': '已完成', + 'all': '全部', // 显示"返回上级文件夹"选项设置 'show_parent_folder_item': '显示「返回上级文件夹」', @@ -621,6 +642,7 @@ export const TRANSLATIONS: Translations = { 'untitled': '無題', 'files': 'ファイル', 'add': '追加', + 'root': 'ルート', // ビュータイトル 'grid_view_title': 'グリッドビュー', @@ -632,6 +654,7 @@ export const TRANSLATIONS: Translations = { 'all_files_mode': 'すべてのファイル', 'recent_files_mode': '最近のファイル', 'random_note_mode': 'ランダムノート', + 'tasks_mode': 'タスク', // 並べ替えオプション 'sort_name_asc': '名前 (A → Z)', @@ -703,6 +726,11 @@ export const TRANSLATIONS: Translations = { 'random_note_count': 'ランダムノートモード表示筆数', 'random_note_notes_only': 'ノートのみ', 'random_note_include_media_files': 'メディアファイルを含む', + 'show_tasks_mode': 'タスクモードを表示', + 'task_filter': 'タスクフィルタ', + 'uncompleted': '未完了', + 'completed': '完了', + 'all': 'すべて', // "親フォルダ"オプション設定を表示 'show_parent_folder_item': '「親フォルダ」項目を表示', @@ -814,6 +842,7 @@ export const TRANSLATIONS: Translations = { 'untitled': 'Без названия', 'files': 'файлы', 'add': 'Добавить', + 'root': 'Корень', // View Titles 'grid_view_title': 'Сеточный вид', @@ -825,6 +854,7 @@ export const TRANSLATIONS: Translations = { 'all_files_mode': 'Все файлы', 'recent_files_mode': 'Недавние файлы', 'random_note_mode': 'Случайная заметка', + 'tasks_mode': 'Задачи', // Sort Options 'sort_name_asc': 'Имя (А → Я)', @@ -896,6 +926,11 @@ export const TRANSLATIONS: Translations = { 'random_note_count': 'Количество случайных заметок', 'random_note_notes_only': 'Только заметки', 'random_note_include_media_files': 'Включить медиафайлы', + 'show_tasks_mode': 'Показывать режим задач', + 'task_filter': 'Фильтр задач', + 'uncompleted': 'Незавершенные', + 'completed': 'Завершенные', + 'all': 'Все', // Show "Parent Folder" option setting 'show_parent_folder_item': 'Показывать элемент "Родительская папка"', @@ -1007,6 +1042,7 @@ export const TRANSLATIONS: Translations = { 'untitled': 'Без назви', 'files': 'файли', 'add': 'Додати', + 'root': 'Корень', // View Titles 'grid_view_title': 'Сітковий вигляд', @@ -1018,6 +1054,7 @@ export const TRANSLATIONS: Translations = { 'all_files_mode': 'Усі файли', 'recent_files_mode': 'Останні файли', 'random_note_mode': 'Випадкова нотатка', + 'tasks_mode': 'Задачі', // Sort Options 'sort_name_asc': 'Назва (А → Я)', @@ -1089,6 +1126,11 @@ export const TRANSLATIONS: Translations = { 'random_note_count': 'Кількість випадкових нотаток', 'random_note_notes_only': 'Тільки нотатки', 'random_note_include_media_files': 'Включити медіафайли', + 'show_tasks_mode': 'Показувати режим завдань', + 'task_filter': 'Фільтр завдань', + 'uncompleted': 'Незавершені', + 'completed': 'Завершені', + 'all': 'Всі', // Show "Parent Folder" option setting 'show_parent_folder_item': 'Показувати елемент "Батьківська папка"', diff --git a/styles.css b/styles.css index 756db94..bc8957c 100644 --- a/styles.css +++ b/styles.css @@ -326,17 +326,38 @@ align-items: center; } -/* .ge-foldername-content { +.ge-foldername-content { grid-column: 1 / -1; - margin-top: 5px; + margin-top: -2px; + margin-bottom: -3px; + margin-left: 7px; justify-content: center; } .ge-foldername-content span { display: inline-flex; align-items: center; - font-size: 14px; -} */ +} + +/* 為資料夾名稱、分隔符等元素添加間距 */ +.ge-foldername-content > *:nth-child(1) { + margin-right: 3px; +} + +.ge-foldername-content > *:nth-child(2) { + margin-right: 6px; +} + +.ge-parent-folder-link { + text-decoration: none; + padding: 5px; +} + +.ge-parent-folder-link:hover { + border-radius: 4px; + background-color: var(--background-modifier-hover); + text-decoration: none; +} /* 搜尋對話框樣式 */ .ge-search-container { @@ -542,6 +563,7 @@ } .ge-loading-indicator { + grid-column: 1 / -1; display: flex; justify-content: center; align-items: center; @@ -551,6 +573,7 @@ } .ge-no-files { + grid-column: 1 / -1; text-align: center; padding: 2em; color: var(--text-muted); @@ -902,3 +925,14 @@ opacity: 0.9; user-select: none; } + +/* 設定描述區域樣式 */ +.ge-setting-desc { + display: flex; + align-items: center; + gap: 10px; +} + +.ge-setting-number-input { + width: 60px; +} \ No newline at end of file diff --git a/versions.json b/versions.json index e46fd63..14ee751 100644 --- a/versions.json +++ b/versions.json @@ -1,3 +1,3 @@ { - "2.2.2": "1.1.0" + "2.3.0": "1.1.0" } \ No newline at end of file