From f01a2cbf83405ee9cb31b19cdfc6814e04eabfef Mon Sep 17 00:00:00 2001 From: Devon22 Date: Sat, 27 Jun 2026 23:47:05 +0800 Subject: [PATCH] 3.4.10 --- manifest.json | 2 +- package-lock.json | 4 +- package.json | 2 +- src/GridPreviewManager.ts | 32 ++++++++- src/GridView.ts | 145 +++++++++++++++++++++++++++++++++----- src/handleKeyDown.ts | 19 ++--- src/renderHeaderButton.ts | 78 ++------------------ styles.css | 2 + 8 files changed, 175 insertions(+), 109 deletions(-) diff --git a/manifest.json b/manifest.json index 50494da..ed22f17 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "gridexplorer", "name": "GridExplorer", - "version": "3.4.9", + "version": "3.4.10", "minAppVersion": "1.8.7", "description": "Browse note files in a grid view.", "author": "Devon22", diff --git a/package-lock.json b/package-lock.json index 1d9faa0..dfe78d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gridexplorer", - "version": "3.4.9", + "version": "3.4.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gridexplorer", - "version": "3.4.9", + "version": "3.4.10", "license": "MIT", "dependencies": { "jszip": "^3.10.1" diff --git a/package.json b/package.json index 260bffe..e282281 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gridexplorer", - "version": "3.4.9", + "version": "3.4.10", "description": "Browse note files in a grid view.", "main": "main.js", "scripts": { diff --git a/src/GridPreviewManager.ts b/src/GridPreviewManager.ts index 54688d4..dc10a02 100644 --- a/src/GridPreviewManager.ts +++ b/src/GridPreviewManager.ts @@ -44,6 +44,30 @@ export class GridPreviewManager { ); } + // 預覽歷史記錄後退 + async navigatePreviewBack() { + if (this.previewHistoryIndex > 0) { + this.previewHistoryIndex--; + const prevFile = this.previewHistory[this.previewHistoryIndex]; + await this.showNoteInGrid(prevFile, true); + } else { + if (this.view.isShowingZip) { + this.hideZipInGrid(); + } else { + this.hideNoteInGrid(); + } + } + } + + // 預覽歷史記錄前進 + async navigatePreviewForward() { + if (this.previewHistoryIndex < this.previewHistory.length - 1) { + this.previewHistoryIndex++; + const nextFile = this.previewHistory[this.previewHistoryIndex]; + await this.showNoteInGrid(nextFile, true); + } + } + // 在網格視圖中直接顯示筆記 @@ -87,12 +111,14 @@ export class GridPreviewManager { // 返回與前進按鈕 const backButton = leftBar.createEl('button', { cls: 'clickable-icon ge-note-nav-button ge-note-back-button' }); setIcon(backButton, 'arrow-left'); - backButton.disabled = this.previewHistoryIndex <= 0; + backButton.disabled = false; backButton.addEventListener('click', () => { if (this.previewHistoryIndex > 0) { this.previewHistoryIndex--; const prevFile = this.previewHistory[this.previewHistoryIndex]; void this.showNoteInGrid(prevFile, true); + } else { + this.hideNoteInGrid(); } }); @@ -607,12 +633,14 @@ export class GridPreviewManager { // 返回與前進按鈕 const backButton = leftBar.createEl('button', { cls: 'clickable-icon ge-note-nav-button ge-note-back-button' }); setIcon(backButton, 'arrow-left'); - backButton.disabled = this.previewHistoryIndex <= 0; + backButton.disabled = false; backButton.addEventListener('click', () => { if (this.previewHistoryIndex > 0) { this.previewHistoryIndex--; const prevFile = this.previewHistory[this.previewHistoryIndex]; void this.showNoteInGrid(prevFile, true); + } else { + this.hideZipInGrid(); } }); diff --git a/src/GridView.ts b/src/GridView.ts index aaaa665..351a403 100644 --- a/src/GridView.ts +++ b/src/GridView.ts @@ -85,6 +85,15 @@ interface WorkspaceSplitWithChildren { children?: unknown[]; } +interface SourceInfo { + mode: string; + path?: string; + searchQuery?: string; + searchCurrentLocationOnly?: boolean; + searchFilesNameOnly?: boolean; + searchMediaFiles?: boolean; +} + interface GridViewStateData { sourceMode?: string; sourcePath?: string; @@ -218,36 +227,66 @@ export class GridView extends ItemView { this.fileWatcher.registerFileWatcher(); } - // 在 window 層級以捕獲階段攔截 Alt+ArrowLeft,優先阻擋 Obsidian 內建快捷鍵 + // 在 window 層級以捕獲階段攔截快捷鍵,優先阻擋 Obsidian 內建快捷鍵並處理歷史前後退與關閉預覽 this.registerDomEvent(window, 'keydown', (event: KeyboardEvent) => { + // 如果焦點在輸入框或可編輯區域,不處理 + const target = event.target as HTMLElement | null; + if (target && ( + target.isContentEditable || + target.closest('input, textarea, select, [contenteditable="true"]') + )) { + return; + } + if (event.key === 'ArrowLeft' && event.altKey) { // 僅在本視圖為活動視圖時才處理 if (this.app.workspace.getActiveViewOfType(GridView) !== this) return; - // 與一般鍵盤邏輯相同的防護:顯示筆記中或有 modal 時不處理 - if (this.isShowingNote) return; + // 有 modal 時不處理 if (activeDocument.querySelector('.modal-container')) return; - // 僅在資料夾模式且不是根目錄時才後退 - if (this.sourceMode === 'folder' && this.sourcePath && this.sourcePath !== '/') { - // 阻止內建快捷鍵與其他監聽器 - event.preventDefault(); - // 停止後續所有監聽器(包含 Obsidian 內建 hotkey) - event.stopImmediatePropagation(); - const parentPath = this.sourcePath.split('/').slice(0, -1).join('/') || '/'; - void this.setSource('folder', parentPath); + + // 阻止內建快捷鍵與其他監聽器 + event.preventDefault(); + // 停止後續所有監聽器(包含 Obsidian 內建 hotkey) + event.stopImmediatePropagation(); + + if (this.isShowingNote || this.isShowingZip) { + void this.previewManager.navigatePreviewBack(); + } else { + void this.navigateBack(); this.clearSelection(); } } else if (event.key === 'ArrowRight' && event.altKey) { - // Alt + 右鍵:若有選中項目則模擬點擊(例如開啟/預覽) // 僅在本視圖為活動視圖時才處理 if (this.app.workspace.getActiveViewOfType(GridView) !== this) return; - // 與一般鍵盤邏輯相同的防護:顯示筆記中或有 modal 時不處理 - if (this.isShowingNote) return; + // 有 modal 時不處理 if (activeDocument.querySelector('.modal-container')) return; - if (this.selectedItemIndex >= 0 && this.selectedItemIndex < this.gridItems.length) { - // 阻止可能的其他快捷鍵或後續處理,避免重複觸發 + + // 阻止內建快捷鍵與其他監聽器 + event.preventDefault(); + // 停止後續所有監聽器(包含 Obsidian 內建 hotkey) + event.stopImmediatePropagation(); + + if (this.isShowingNote || this.isShowingZip) { + void this.previewManager.navigatePreviewForward(); + } else { + void this.navigateForward(); + this.clearSelection(); + } + } else if ((event.key === 'ArrowUp' && event.altKey) || event.key === 'Backspace') { + // 僅在本視圖為活動視圖時才處理 + if (this.app.workspace.getActiveViewOfType(GridView) !== this) return; + // 有 modal 時不處理 + if (activeDocument.querySelector('.modal-container')) return; + + // 若正在顯示預覽,則關閉預覽 + if (this.isShowingNote || this.isShowingZip) { event.preventDefault(); event.stopImmediatePropagation(); - this.gridItems[this.selectedItemIndex].click(); + if (this.isShowingZip) { + this.previewManager.hideZipInGrid(); + } else { + this.previewManager.hideNoteInGrid(); + } } } }, true); @@ -385,6 +424,78 @@ export class GridView extends ItemView { } } + private parseSourceInfo(sourceInfoStr: string): SourceInfo | null { + try { + return JSON.parse(sourceInfoStr) as SourceInfo; + } catch { + return null; + } + } + + // 歷史記錄後退 + async navigateBack() { + if (this.recentSources.length > 0) { + const currentKey = JSON.stringify({ + mode: this.sourceMode, + path: this.sourcePath, + searchQuery: this.searchQuery, + searchCurrentLocationOnly: this.searchCurrentLocationOnly, + searchFilesNameOnly: this.searchFilesNameOnly, + searchMediaFiles: this.searchMediaFiles, + }); + this.futureSources.unshift(currentKey); + if (this.futureSources.length > 10) { + this.futureSources.length = 10; + } + + const lastSource = this.parseSourceInfo(this.recentSources[0]); + if (!lastSource) return; + this.recentSources.shift(); // 從歷史記錄中移除 + + await this.setSource( + lastSource.mode, + lastSource.path || '', + false, // 不記錄到歷史 + lastSource.searchQuery || '', + lastSource.searchCurrentLocationOnly ?? false, + lastSource.searchFilesNameOnly ?? false, + lastSource.searchMediaFiles ?? false + ); + } + } + + // 歷史記錄前進 + async navigateForward() { + if (this.futureSources.length > 0) { + const currentKey = JSON.stringify({ + mode: this.sourceMode, + path: this.sourcePath, + searchQuery: this.searchQuery, + searchCurrentLocationOnly: this.searchCurrentLocationOnly, + searchFilesNameOnly: this.searchFilesNameOnly, + searchMediaFiles: this.searchMediaFiles, + }); + this.recentSources.unshift(currentKey); + if (this.recentSources.length > 10) { + this.recentSources.length = 10; + } + + const nextSource = this.parseSourceInfo(this.futureSources[0]); + if (!nextSource) return; + this.futureSources.shift(); // 從未來紀錄中移除 + + await this.setSource( + nextSource.mode, + nextSource.path || '', + false, // 不記錄到歷史 + nextSource.searchQuery || '', + nextSource.searchCurrentLocationOnly ?? false, + nextSource.searchFilesNameOnly ?? false, + nextSource.searchMediaFiles ?? false + ); + } + } + // 設定來源模式 async setSource( mode: string, diff --git a/src/handleKeyDown.ts b/src/handleKeyDown.ts index 116b3db..ee1af66 100644 --- a/src/handleKeyDown.ts +++ b/src/handleKeyDown.ts @@ -29,10 +29,10 @@ export function handleKeyDown(gridView: GridView, event: KeyboardEvent) { switch (event.key) { case 'ArrowRight': if (event.altKey) { - // 如果有選中的項目,模擬點擊 - if (gridView.selectedItemIndex >= 0 && gridView.selectedItemIndex < gridView.gridItems.length) { - gridView.gridItems[gridView.selectedItemIndex].click(); - } + void gridView.navigateForward(); + gridView.clearSelection(); + event.preventDefault(); + break; } newIndex = Math.min(gridView.gridItems.length - 1, gridView.selectedItemIndex + 1); gridView.hasKeyboardFocus = true; @@ -40,14 +40,9 @@ export function handleKeyDown(gridView: GridView, event: KeyboardEvent) { break; case 'ArrowLeft': if (event.altKey) { - // 如果按下 Alt + 左鍵,且是資料夾模式且不是根目錄 - if (gridView.sourceMode === 'folder' && gridView.sourcePath && gridView.sourcePath !== '/') { - // 獲取上一層資料夾路徑 - const parentPath = gridView.sourcePath.split('/').slice(0, -1).join('/') || '/'; - void gridView.setSource('folder', parentPath); - gridView.clearSelection(); - event.preventDefault(); - } + void gridView.navigateBack(); + gridView.clearSelection(); + event.preventDefault(); break; } newIndex = Math.max(0, gridView.selectedItemIndex - 1); diff --git a/src/renderHeaderButton.ts b/src/renderHeaderButton.ts index aa556d0..1d76e06 100644 --- a/src/renderHeaderButton.ts +++ b/src/renderHeaderButton.ts @@ -70,44 +70,8 @@ export function renderHeaderButton(gridView: GridView) { void (async () => { event.preventDefault(); event.stopPropagation(); - - // 如果有歷史記錄 - if (gridView.recentSources.length > 0) { - // 將當前狀態推入 futureSources 以便前進 - const currentKey = JSON.stringify({ - mode: gridView.sourceMode, - path: gridView.sourcePath, - searchQuery: gridView.searchQuery, - searchCurrentLocationOnly: gridView.searchCurrentLocationOnly, - searchFilesNameOnly: gridView.searchFilesNameOnly, - searchMediaFiles: gridView.searchMediaFiles, - }); - gridView.futureSources.unshift(currentKey); - if (gridView.futureSources.length > 10) { - gridView.futureSources.length = 10; - } - - // 取得最近一筆歷史記錄 - const lastSource = parseSourceInfo(gridView.recentSources[0]); - if (!lastSource) return; - gridView.recentSources.shift(); // 從歷史記錄中移除 - - // 設定來源及搜尋狀態(不記錄到歷史) - await gridView.setSource( - lastSource.mode, - lastSource.path || '', - false, // 不記錄到歷史 - lastSource.searchQuery || '', - lastSource.searchCurrentLocationOnly ?? false, - lastSource.searchFilesNameOnly ?? false, - lastSource.searchMediaFiles ?? false - ); - - // 更新按鈕狀態 - updateNavButtons(); - } else { - // 如果沒有歷史記錄,點擊按鈕無效 - } + await gridView.navigateBack(); + updateNavButtons(); })(); }); @@ -118,42 +82,8 @@ export function renderHeaderButton(gridView: GridView) { void (async () => { event.preventDefault(); event.stopPropagation(); - - // 如果有未來紀錄 - if (gridView.futureSources.length > 0) { - // 將當前狀態推入 recentSources 以便返回 - const currentKey = JSON.stringify({ - mode: gridView.sourceMode, - path: gridView.sourcePath, - searchQuery: gridView.searchQuery, - searchCurrentLocationOnly: gridView.searchCurrentLocationOnly, - searchFilesNameOnly: gridView.searchFilesNameOnly, - searchMediaFiles: gridView.searchMediaFiles, - }); - gridView.recentSources.unshift(currentKey); - if (gridView.recentSources.length > 10) { - gridView.recentSources.length = 10; - } - - // 取得下一筆未來紀錄 - const nextSource = parseSourceInfo(gridView.futureSources[0]); - if (!nextSource) return; - gridView.futureSources.shift(); // 從未來紀錄中移除 - - // 設定來源及搜尋狀態(不記錄到歷史) - await gridView.setSource( - nextSource.mode, - nextSource.path || '', - false, // 不記錄到歷史 - nextSource.searchQuery || '', - nextSource.searchCurrentLocationOnly ?? false, - nextSource.searchFilesNameOnly ?? false, - nextSource.searchMediaFiles ?? false - ); - - // 更新按鈕狀態 - updateNavButtons(); - } + await gridView.navigateForward(); + updateNavButtons(); })(); }); diff --git a/styles.css b/styles.css index c8b6fe7..938f2e3 100644 --- a/styles.css +++ b/styles.css @@ -53,6 +53,7 @@ transform: translateY(-2px); background-color: var(--text-selection); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + border-color: var(--interactive-accent) !important; } .ge-grid-item:hover p { @@ -3431,6 +3432,7 @@ a.ge-current-folder:hover { background-color: var(--text-selection); transform: translateY(-1px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + border-color: var(--interactive-accent) !important; } }