mirror of
https://github.com/devon22/obsidian-gridexplorer.git
synced 2026-07-22 05:38:16 +00:00
2.8.4
This commit is contained in:
parent
11be87984d
commit
8e513a17e9
10 changed files with 370 additions and 38 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "gridexplorer",
|
||||
"name": "GridExplorer",
|
||||
"version": "2.8.3",
|
||||
"version": "2.8.4",
|
||||
"minAppVersion": "1.1.0",
|
||||
"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": "2.8.3",
|
||||
"version": "2.8.4",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "gridexplorer",
|
||||
"version": "2.8.3",
|
||||
"version": "2.8.4",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.6",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "gridexplorer",
|
||||
"version": "2.8.3",
|
||||
"version": "2.8.4",
|
||||
"description": "Browse note files in a grid view.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ export class GridView extends ItemView {
|
|||
hasKeyboardFocus: boolean = false; // 是否有鍵盤焦點
|
||||
fileWatcher: FileWatcher; // 檔案監聽器
|
||||
recentSources: string[] = []; // 歷史記錄
|
||||
futureSources: string[] = []; // 未來紀錄(前進堆疊)
|
||||
minMode: boolean = false; // 最小模式
|
||||
showIgnoredFolders: boolean = false; // 顯示忽略資料夾
|
||||
showDateDividers: boolean = false; // 顯示日期分隔器
|
||||
|
|
@ -161,10 +162,9 @@ export class GridView extends ItemView {
|
|||
return (this.leaf as any)?.pinned ?? false;
|
||||
}
|
||||
|
||||
// 將來源加入歷史記錄(LRU 去重)
|
||||
// 1. 若已有相同紀錄先移除,確保唯一
|
||||
// 2. 插入到陣列開頭,代表最新使用
|
||||
// 3. 超過上限時裁切
|
||||
// 將來源加入歷史記錄
|
||||
// 1. 插入到陣列開頭,代表最新使用
|
||||
// 2. 超過上限時裁切
|
||||
public pushHistory(
|
||||
mode: string,
|
||||
path: string | null,
|
||||
|
|
@ -182,12 +182,11 @@ export class GridView extends ItemView {
|
|||
searchFilesNameOnly,
|
||||
searchMediaFiles,
|
||||
});
|
||||
const existingIndex = this.recentSources.indexOf(key);
|
||||
if (existingIndex !== -1) {
|
||||
this.recentSources.splice(existingIndex, 1);
|
||||
}
|
||||
|
||||
this.recentSources.unshift(key);
|
||||
const limit = 15;
|
||||
// 一旦有新的歷史被推入,清空 futureSources
|
||||
this.futureSources = [];
|
||||
const limit = 10;
|
||||
if (this.recentSources.length > limit) {
|
||||
this.recentSources.length = limit;
|
||||
}
|
||||
|
|
@ -1615,7 +1614,10 @@ export class GridView extends ItemView {
|
|||
this.clearSelection();
|
||||
return true;
|
||||
} else if (redirectType === 'search') {
|
||||
this.setSource('', '', true, redirectPath);
|
||||
const searchLocationFiles = fileCache?.frontmatter?.searchLocationFiles || false;
|
||||
const searchFilesNameOnly = fileCache?.frontmatter?.searchFilesNameOnly || false;
|
||||
const searchMediaFiles = fileCache?.frontmatter?.searchMediaFiles || false;
|
||||
this.setSource('', '', true, redirectPath, !searchLocationFiles, searchFilesNameOnly, searchMediaFiles);
|
||||
this.clearSelection();
|
||||
return true;
|
||||
} else if (redirectType === 'uri') {
|
||||
|
|
@ -1804,6 +1806,7 @@ export class GridView extends ItemView {
|
|||
showDateDividers: this.showDateDividers,
|
||||
showNoteTags: this.showNoteTags,
|
||||
recentSources: this.recentSources,
|
||||
futureSources: this.futureSources,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1827,6 +1830,7 @@ export class GridView extends ItemView {
|
|||
this.showDateDividers = state.state.showDateDividers ?? this.plugin.settings.dateDividerMode !== 'none';
|
||||
this.showNoteTags = state.state.showNoteTags ?? this.plugin.settings.showNoteTags;
|
||||
this.recentSources = state.state.recentSources ?? [];
|
||||
this.futureSources = state.state.futureSources ?? [];
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import { App, TFile } from 'obsidian';
|
|||
// 尋找筆記中的第一張圖片
|
||||
export async function findFirstImageInNote(app: App, content: string) {
|
||||
try {
|
||||
const internalStyle = /!?\[\[(.*?\.(?:jpg|jpeg|png|gif|webp))(?:\|.*?)?\]\]/;
|
||||
const markdownStyle = /!\[(.*?)\]\(\s*(\S+?(?:\.(?:jpg|jpeg|png|gif|webp)|format=(?:jpg|jpeg|png|gif|webp))[^\s)]*)\s*(?:\s+["'][^"']*["'])?\s*\)/;
|
||||
const frontmatterUrl = /^[\w\-_]+:\s*(https?:\/\/\S+?(?:\.(?:jpg|jpeg|png|gif|webp)|format=(?:jpg|jpeg|png|gif|webp))[^\s]*)\s*$/;
|
||||
const internalStyle = /!?\[\[(.*?\.(?:jpg|jpeg|png|gif|webp|avif))(?:\|.*?)?\]\]/;
|
||||
const markdownStyle = /!\[(.*?)\]\(\s*(\S+?(?:\.(?:jpg|jpeg|png|gif|webp|avif)|format=(?:jpg|jpeg|png|gif|webp))[^\s)]*)\s*(?:\s+["'][^"']*["'])?\s*\)/;
|
||||
const frontmatterUrl = /^[\w\-_]+:\s*(https?:\/\/\S+?(?:\.(?:jpg|jpeg|png|gif|webp|avif)|format=(?:jpg|jpeg|png|gif|webp))[^\s]*)\s*$/;
|
||||
|
||||
const internalMatch = content.match(new RegExp(`(?:${internalStyle.source}|${markdownStyle.source}|${frontmatterUrl.source})`, 'im'));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,19 @@
|
|||
import { App, Modal } from 'obsidian';
|
||||
import { t } from '../translations';
|
||||
|
||||
export interface SearchOptions {
|
||||
searchLocationFiles: boolean;
|
||||
searchFilesNameOnly: boolean;
|
||||
searchMediaFiles: boolean;
|
||||
}
|
||||
|
||||
interface InputModalOptions {
|
||||
title: string;
|
||||
placeholder: string;
|
||||
defaultValue?: string;
|
||||
inputType?: 'text' | 'url';
|
||||
onSubmit: (value: string) => void;
|
||||
onSubmit: (value: string, searchOptions?: SearchOptions) => void;
|
||||
showSearchOptions?: boolean;
|
||||
}
|
||||
|
||||
export class InputModal extends Modal {
|
||||
|
|
@ -35,6 +42,73 @@ export class InputModal extends Modal {
|
|||
cls: 'ge-input-field'
|
||||
});
|
||||
|
||||
// 如果是搜尋文字,添加搜尋選項
|
||||
let searchOptions: SearchOptions = {
|
||||
searchLocationFiles: false,
|
||||
searchFilesNameOnly: false,
|
||||
searchMediaFiles: false
|
||||
};
|
||||
|
||||
if (this.options.showSearchOptions || this.options.title === t('search_text')) {
|
||||
const searchOptionsContainer = contentEl.createDiv('ge-search-options');
|
||||
|
||||
// 僅搜尋目前位置檔案選項
|
||||
const searchScopeContainer = searchOptionsContainer.createDiv('ge-search-option');
|
||||
const searchScopeCheckbox = searchScopeContainer.createEl('input', {
|
||||
type: 'checkbox',
|
||||
attr: {
|
||||
id: 'searchLocationFiles'
|
||||
}
|
||||
}) as HTMLInputElement;
|
||||
if (searchOptions.searchLocationFiles) {
|
||||
searchScopeCheckbox.checked = true;
|
||||
}
|
||||
const searchScopeLabel = searchScopeContainer.createEl('label', { text: t('search_current_location_only') });
|
||||
searchScopeLabel.setAttribute('for', 'searchLocationFiles');
|
||||
|
||||
// 只搜尋檔名選項
|
||||
const searchNameContainer = searchOptionsContainer.createDiv('ge-search-option');
|
||||
const searchNameCheckbox = searchNameContainer.createEl('input', {
|
||||
type: 'checkbox',
|
||||
attr: {
|
||||
id: 'searchFilesNameOnly'
|
||||
}
|
||||
}) as HTMLInputElement;
|
||||
if (searchOptions.searchFilesNameOnly) {
|
||||
searchNameCheckbox.checked = true;
|
||||
}
|
||||
const searchNameLabel = searchNameContainer.createEl('label', { text: t('search_files_name_only') });
|
||||
searchNameLabel.setAttribute('for', 'searchFilesNameOnly');
|
||||
|
||||
// 搜尋媒體檔案選項
|
||||
const searchMediaFilesContainer = searchOptionsContainer.createDiv('ge-search-option');
|
||||
const searchMediaFilesCheckbox = searchMediaFilesContainer.createEl('input', {
|
||||
type: 'checkbox',
|
||||
attr: {
|
||||
id: 'searchMediaFiles'
|
||||
}
|
||||
}) as HTMLInputElement;
|
||||
if (searchOptions.searchMediaFiles) {
|
||||
searchMediaFilesCheckbox.checked = true;
|
||||
}
|
||||
const searchMediaFilesLabel = searchMediaFilesContainer.createEl('label', { text: t('search_media_files') });
|
||||
searchMediaFilesLabel.setAttribute('for', 'searchMediaFiles');
|
||||
|
||||
// 更新搜尋選項
|
||||
const updateSearchOptions = () => {
|
||||
searchOptions = {
|
||||
searchLocationFiles: searchScopeCheckbox.checked,
|
||||
searchFilesNameOnly: searchNameCheckbox.checked,
|
||||
searchMediaFiles: searchMediaFilesCheckbox.checked
|
||||
};
|
||||
};
|
||||
|
||||
// 添加事件監聽器
|
||||
searchScopeCheckbox.addEventListener('change', updateSearchOptions);
|
||||
searchNameCheckbox.addEventListener('change', updateSearchOptions);
|
||||
searchMediaFilesCheckbox.addEventListener('change', updateSearchOptions);
|
||||
}
|
||||
|
||||
// 創建按鈕容器
|
||||
const buttonContainer = contentEl.createDiv('ge-button-container');
|
||||
|
||||
|
|
@ -53,7 +127,11 @@ export class InputModal extends Modal {
|
|||
const performSubmit = () => {
|
||||
const value = input.value.trim();
|
||||
if (value) {
|
||||
this.options.onSubmit(value);
|
||||
if (this.options.showSearchOptions || this.options.title === t('search_text')) {
|
||||
this.options.onSubmit(value, searchOptions);
|
||||
} else {
|
||||
this.options.onSubmit(value);
|
||||
}
|
||||
this.close();
|
||||
}
|
||||
};
|
||||
|
|
@ -83,11 +161,12 @@ export class InputModal extends Modal {
|
|||
}
|
||||
|
||||
// 便利函數:顯示搜尋輸入 modal
|
||||
export function showSearchInputModal(app: App, onSubmit: (searchText: string) => void) {
|
||||
export function showSearchInputModal(app: App, onSubmit: (searchText: string, searchOptions?: SearchOptions) => void) {
|
||||
new InputModal(app, {
|
||||
title: t('search_text'),
|
||||
placeholder: t('enter_search_text'),
|
||||
onSubmit
|
||||
onSubmit,
|
||||
showSearchOptions: true
|
||||
}).open();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { App, Modal, TFolder, TFile, FuzzySuggestModal } from 'obsidian';
|
||||
import GridExplorerPlugin from '../main';
|
||||
import { t } from '../translations';
|
||||
import { showSearchInputModal, showUriInputModal } from './inputModal';
|
||||
import { showSearchInputModal, showUriInputModal, SearchOptions } from './inputModal';
|
||||
|
||||
interface ShortcutOption {
|
||||
type: 'mode' | 'folder' | 'file' | 'search' | 'uri';
|
||||
value: string;
|
||||
display: string;
|
||||
searchOptions?: SearchOptions;
|
||||
}
|
||||
|
||||
export class ShortcutSelectionModal extends Modal {
|
||||
|
|
@ -66,11 +67,19 @@ export class ShortcutSelectionModal extends Modal {
|
|||
|
||||
// 點擊搜尋按鈕時打開搜尋輸入模態框
|
||||
searchButton.addEventListener('click', () => {
|
||||
showSearchInputModal(this.app, (searchText) => {
|
||||
showSearchInputModal(this.app, (searchText, searchOptions) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
if (searchOptions) {
|
||||
searchParams.set('allFiles', searchOptions.searchLocationFiles.toString());
|
||||
searchParams.set('nameOnly', searchOptions.searchFilesNameOnly.toString());
|
||||
searchParams.set('mediaOnly', searchOptions.searchMediaFiles.toString());
|
||||
}
|
||||
|
||||
this.onSubmit({
|
||||
type: 'search',
|
||||
value: searchText,
|
||||
display: `🔎 ${searchText}`
|
||||
display: `🔎 ${searchText}`,
|
||||
searchOptions: searchOptions
|
||||
});
|
||||
this.close();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
}
|
||||
});
|
||||
|
||||
// 添加回上一步按鈕
|
||||
// 添加返回按鈕
|
||||
const backButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('back') } });
|
||||
setIcon(backButton, 'arrow-left');
|
||||
backButton.addEventListener('click', async (event) => {
|
||||
|
|
@ -35,6 +35,20 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
|
||||
// 如果有歷史記錄
|
||||
if (gridView.recentSources.length > 0) {
|
||||
// 將當前狀態推入 futureSources 以便前進
|
||||
const currentKey = JSON.stringify({
|
||||
mode: gridView.sourceMode,
|
||||
path: gridView.sourcePath,
|
||||
searchQuery: gridView.searchQuery,
|
||||
searchAllFiles: gridView.searchAllFiles,
|
||||
searchFilesNameOnly: gridView.searchFilesNameOnly,
|
||||
searchMediaFiles: gridView.searchMediaFiles,
|
||||
});
|
||||
gridView.futureSources.unshift(currentKey);
|
||||
if (gridView.futureSources.length > 10) {
|
||||
gridView.futureSources.length = 10;
|
||||
}
|
||||
|
||||
// 取得最近一筆歷史記錄
|
||||
const lastSource = JSON.parse(gridView.recentSources[0]);
|
||||
gridView.recentSources.shift(); // 從歷史記錄中移除
|
||||
|
|
@ -49,6 +63,52 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
lastSource.searchFilesNameOnly ?? false,
|
||||
lastSource.searchMediaFiles ?? false
|
||||
);
|
||||
|
||||
// 更新按鈕狀態
|
||||
updateNavButtons();
|
||||
}
|
||||
});
|
||||
|
||||
// 添加前進按鈕
|
||||
const forwardButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('forward') } });
|
||||
setIcon(forwardButton, 'arrow-right');
|
||||
forwardButton.addEventListener('click', async (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// 如果有未來紀錄
|
||||
if (gridView.futureSources.length > 0) {
|
||||
// 將當前狀態推入 recentSources 以便返回
|
||||
const currentKey = JSON.stringify({
|
||||
mode: gridView.sourceMode,
|
||||
path: gridView.sourcePath,
|
||||
searchQuery: gridView.searchQuery,
|
||||
searchAllFiles: gridView.searchAllFiles,
|
||||
searchFilesNameOnly: gridView.searchFilesNameOnly,
|
||||
searchMediaFiles: gridView.searchMediaFiles,
|
||||
});
|
||||
gridView.recentSources.unshift(currentKey);
|
||||
if (gridView.recentSources.length > 10) {
|
||||
gridView.recentSources.length = 10;
|
||||
}
|
||||
|
||||
// 取得下一筆未來紀錄
|
||||
const nextSource = JSON.parse(gridView.futureSources[0]);
|
||||
gridView.futureSources.shift(); // 從未來紀錄中移除
|
||||
|
||||
// 設定來源及搜尋狀態(不記錄到歷史)
|
||||
await gridView.setSource(
|
||||
nextSource.mode,
|
||||
nextSource.path || '',
|
||||
false, // 不記錄到歷史
|
||||
nextSource.searchQuery || '',
|
||||
nextSource.searchAllFiles ?? true,
|
||||
nextSource.searchFilesNameOnly ?? false,
|
||||
nextSource.searchMediaFiles ?? false
|
||||
);
|
||||
|
||||
// 更新按鈕狀態
|
||||
updateNavButtons();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -59,7 +119,7 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
event.preventDefault();
|
||||
|
||||
const menu = new Menu();
|
||||
|
||||
|
||||
// 添加歷史記錄
|
||||
gridView.recentSources.forEach((sourceInfoStr, index) => {
|
||||
try {
|
||||
|
|
@ -135,14 +195,26 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
.setTitle(`${displayText}`)
|
||||
.setIcon(`${icon}`)
|
||||
.onClick(async () => {
|
||||
// 找出當前點擊的紀錄索引
|
||||
const clickedIndex = gridView.recentSources.findIndex(source => {
|
||||
const parsed = JSON.parse(source);
|
||||
return parsed.mode === mode && parsed.path === path;
|
||||
// 將目前狀態與較新的歷史推入 futureSources
|
||||
const currentKey = JSON.stringify({
|
||||
mode: gridView.sourceMode,
|
||||
path: gridView.sourcePath,
|
||||
searchQuery: gridView.searchQuery,
|
||||
searchAllFiles: gridView.searchAllFiles,
|
||||
searchFilesNameOnly: gridView.searchFilesNameOnly,
|
||||
searchMediaFiles: gridView.searchMediaFiles,
|
||||
});
|
||||
|
||||
// 如果找到點擊的紀錄,清除它之上的紀錄
|
||||
|
||||
// 直接使用當前迴圈索引(允許重複項)
|
||||
const clickedIndex = index;
|
||||
|
||||
if (clickedIndex !== -1) {
|
||||
const newerHistory = gridView.recentSources.slice(0, clickedIndex);
|
||||
const forwardStack = [...newerHistory].reverse();
|
||||
gridView.futureSources = [...forwardStack, currentKey, ...gridView.futureSources];
|
||||
if (gridView.futureSources.length > 10) {
|
||||
gridView.futureSources.length = 10;
|
||||
}
|
||||
gridView.recentSources = gridView.recentSources.slice(clickedIndex + 1);
|
||||
}
|
||||
|
||||
|
|
@ -156,18 +228,154 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
sourceInfo.searchFilesNameOnly ?? false,
|
||||
sourceInfo.searchMediaFiles ?? false
|
||||
);
|
||||
|
||||
// 更新按鈕狀態
|
||||
updateNavButtons();
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to parse source info:', error);
|
||||
}
|
||||
});
|
||||
menu.showAtMouseEvent(event);
|
||||
}
|
||||
});
|
||||
|
||||
// 添加右鍵選單支援
|
||||
forwardButton.addEventListener('contextmenu', (event) => {
|
||||
// 只有在有歷史記錄或未來記錄時才顯示右鍵選單
|
||||
if (gridView.futureSources.length > 0) {
|
||||
event.preventDefault();
|
||||
|
||||
const menu = new Menu();
|
||||
|
||||
// 添加未來記錄
|
||||
gridView.futureSources.forEach((sourceInfoStr, index) => {
|
||||
try {
|
||||
const sourceInfo = JSON.parse(sourceInfoStr);
|
||||
const { mode, path } = sourceInfo;
|
||||
|
||||
// 根據模式顯示圖示和文字
|
||||
let displayText = '';
|
||||
let icon = '';
|
||||
switch (mode) {
|
||||
case 'folder':
|
||||
displayText = path || '/';
|
||||
icon = 'folder';
|
||||
break;
|
||||
case 'bookmarks':
|
||||
displayText = t('bookmarks_mode');
|
||||
icon = 'bookmark';
|
||||
break;
|
||||
case 'search':
|
||||
displayText = t('search_results');
|
||||
icon = 'search';
|
||||
break;
|
||||
case 'backlinks':
|
||||
displayText = t('backlinks_mode');
|
||||
icon = 'links-coming-in';
|
||||
break;
|
||||
case 'outgoinglinks':
|
||||
displayText = t('outgoinglinks_mode');
|
||||
icon = 'links-going-out';
|
||||
break;
|
||||
case 'all-files':
|
||||
displayText = t('all_files_mode');
|
||||
icon = 'book-text';
|
||||
break;
|
||||
case 'recent-files':
|
||||
displayText = t('recent_files_mode');
|
||||
icon = 'calendar-days';
|
||||
break;
|
||||
case 'random-note':
|
||||
displayText = t('random_note_mode');
|
||||
icon = 'dice';
|
||||
break;
|
||||
case 'tasks':
|
||||
displayText = t('tasks_mode');
|
||||
icon = 'square-check-big';
|
||||
break;
|
||||
default:
|
||||
if (mode.startsWith('custom-')) {
|
||||
const customMode = gridView.plugin.settings.customModes.find(m => m.internalName === mode);
|
||||
displayText = customMode ? customMode.displayName : t('custom_mode');
|
||||
icon = 'puzzle';
|
||||
} else {
|
||||
displayText = mode;
|
||||
icon = 'grid';
|
||||
}
|
||||
}
|
||||
|
||||
// 處理搜尋顯示文字
|
||||
if (sourceInfo.searchQuery) {
|
||||
if (sourceInfo.searchAllFiles) {
|
||||
displayText = '"' + (sourceInfo.searchQuery || t('search_results')) + '"';
|
||||
} else {
|
||||
displayText += `: \"${sourceInfo.searchQuery}\"`;
|
||||
}
|
||||
}
|
||||
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(`${displayText}`)
|
||||
.setIcon(`${icon}`)
|
||||
.onClick(async () => {
|
||||
// 將目前狀態與較舊的 future 項目移至 recentSources
|
||||
const currentKey = JSON.stringify({
|
||||
mode: gridView.sourceMode,
|
||||
path: gridView.sourcePath,
|
||||
searchQuery: gridView.searchQuery,
|
||||
searchAllFiles: gridView.searchAllFiles,
|
||||
searchFilesNameOnly: gridView.searchFilesNameOnly,
|
||||
searchMediaFiles: gridView.searchMediaFiles,
|
||||
});
|
||||
|
||||
const clickedIndex = index;
|
||||
|
||||
let olderFuture: string[] = [];
|
||||
if (clickedIndex !== -1) {
|
||||
olderFuture = gridView.futureSources.slice(0, clickedIndex);
|
||||
gridView.futureSources = gridView.futureSources.slice(clickedIndex + 1);
|
||||
}
|
||||
const backStack = [...olderFuture].reverse();
|
||||
gridView.recentSources = [...backStack, currentKey, ...gridView.recentSources];
|
||||
if (gridView.recentSources.length > 10) {
|
||||
gridView.recentSources.length = 10;
|
||||
}
|
||||
|
||||
await gridView.setSource(
|
||||
mode,
|
||||
path,
|
||||
false,
|
||||
sourceInfo.searchQuery || '',
|
||||
sourceInfo.searchAllFiles ?? true,
|
||||
sourceInfo.searchFilesNameOnly ?? false,
|
||||
sourceInfo.searchMediaFiles ?? false
|
||||
);
|
||||
|
||||
// 更新按鈕狀態
|
||||
updateNavButtons();
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to parse source info:', error);
|
||||
}
|
||||
});
|
||||
|
||||
// 顯示歷史選單
|
||||
menu.showAtMouseEvent(event);
|
||||
}
|
||||
});
|
||||
|
||||
// 更新返回/前進按鈕啟用狀態
|
||||
const updateNavButtons = () => {
|
||||
backButton.disabled = gridView.recentSources.length === 0;
|
||||
forwardButton.disabled = gridView.futureSources.length === 0;
|
||||
};
|
||||
|
||||
// 初始狀態
|
||||
updateNavButtons();
|
||||
|
||||
// 添加新增筆記按鈕
|
||||
const newNoteButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('new_note') } });
|
||||
setIcon(newNoteButton, 'square-pen');
|
||||
|
|
@ -310,11 +518,25 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
showSearchModal(gridView.app, gridView, gridView.searchQuery, searchText);
|
||||
});
|
||||
|
||||
// 先保存開啟 Modal 時的原始狀態
|
||||
const originalSearchQuery = gridView.searchQuery;
|
||||
const originalSearchAllFiles = gridView.searchAllFiles;
|
||||
const originalSearchFilesNameOnly = gridView.searchFilesNameOnly;
|
||||
const originalSearchMediaFiles = gridView.searchMediaFiles;
|
||||
|
||||
// 創建取消按鈕
|
||||
const clearButton = searchTextContainer.createDiv('ge-clear-button');
|
||||
setIcon(clearButton, 'x');
|
||||
clearButton.addEventListener('click', (e) => {
|
||||
e.stopPropagation(); // 防止觸發搜尋文字的點擊事件
|
||||
gridView.pushHistory(
|
||||
gridView.sourceMode,
|
||||
gridView.sourcePath,
|
||||
originalSearchQuery,
|
||||
originalSearchAllFiles,
|
||||
originalSearchFilesNameOnly,
|
||||
originalSearchMediaFiles,
|
||||
);
|
||||
gridView.searchQuery = '';
|
||||
gridView.clearSelection();
|
||||
gridView.app.workspace.requestSaveLayout();
|
||||
|
|
@ -538,9 +760,16 @@ function generateFilenameFromUri(uri: string): string {
|
|||
// 創建捷徑檔案
|
||||
async function createShortcut(
|
||||
gridView: GridView,
|
||||
option: { type: 'mode' | 'folder' | 'file' | 'search' | 'uri';
|
||||
value: string;
|
||||
display: string; }) {
|
||||
option: {
|
||||
type: 'mode' | 'folder' | 'file' | 'search' | 'uri';
|
||||
value: string;
|
||||
display: string;
|
||||
searchOptions?: {
|
||||
searchLocationFiles: boolean;
|
||||
searchFilesNameOnly: boolean;
|
||||
searchMediaFiles: boolean;
|
||||
};
|
||||
}) {
|
||||
try {
|
||||
// 生成不重複的檔案名稱
|
||||
let counter = 0;
|
||||
|
|
@ -582,6 +811,12 @@ async function createShortcut(
|
|||
} else if (option.type === 'search') {
|
||||
frontmatter.type = 'search';
|
||||
frontmatter.redirect = option.value;
|
||||
// 添加搜尋選項到 frontmatter
|
||||
if (option.searchOptions) {
|
||||
frontmatter.searchLocationFiles = option.searchOptions.searchLocationFiles;
|
||||
frontmatter.searchFilesNameOnly = option.searchOptions.searchFilesNameOnly;
|
||||
frontmatter.searchMediaFiles = option.searchOptions.searchMediaFiles;
|
||||
}
|
||||
} else if (option.type === 'uri') {
|
||||
frontmatter.type = 'uri';
|
||||
frontmatter.redirect = option.value;
|
||||
|
|
|
|||
|
|
@ -546,7 +546,7 @@
|
|||
/* 頂部按鈕區域樣式 Header Buttons */
|
||||
.ge-header-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
gap: 7px;
|
||||
padding: 9px;
|
||||
background: var(--background-primary);
|
||||
border-bottom: 1px solid var(--background-modifier-border);
|
||||
|
|
@ -558,7 +558,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 6px 12px;
|
||||
padding: 6px 11px;
|
||||
background-color: var(--interactive-normal);
|
||||
border-radius: var(--button-radius);
|
||||
color: var(--text-normal);
|
||||
|
|
@ -567,6 +567,11 @@
|
|||
transition: background-color 0.2s, transform 0.1s;
|
||||
}
|
||||
|
||||
.ge-header-buttons button:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.is-tablet .ge-header-buttons button:not(.clickable-icon) {
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"2.8.3": "1.1.0"
|
||||
"2.8.4": "1.1.0"
|
||||
}
|
||||
Loading…
Reference in a new issue