mirror of
https://github.com/devon22/obsidian-gridexplorer.git
synced 2026-07-22 05:38:16 +00:00
3.4.10
This commit is contained in:
parent
87549960d7
commit
f01a2cbf83
8 changed files with 175 additions and 109 deletions
|
|
@ -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",
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
145
src/GridView.ts
145
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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
})();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue