From 9409cafcc51fe6705dbe3ad0b25985b70c5865de Mon Sep 17 00:00:00 2001 From: Devon22 Date: Tue, 24 Jun 2025 00:11:29 +0800 Subject: [PATCH] 2.4.6 --- main.ts | 79 ++++++++++++++++++++++++++++++++++++++------- manifest.json | 2 +- package-lock.json | 4 +-- package.json | 2 +- src/FileWatcher.ts | 1 + src/GridView.ts | 59 +++++++++++++++++++++------------ src/settings.ts | 17 +++++++++- src/translations.ts | 36 +++++++++++++++++---- versions.json | 2 +- 9 files changed, 158 insertions(+), 44 deletions(-) diff --git a/main.ts b/main.ts index ceb0348..b155e29 100644 --- a/main.ts +++ b/main.ts @@ -99,9 +99,9 @@ export default class GridExplorerPlugin extends Plugin { // 註冊狀態列項目 this.statusBarItem = this.addStatusBarItem(); - this.statusBarItem.onClickEvent(() => { - this.activateView(); - }); + // this.statusBarItem.onClickEvent(() => { + // this.activateView(); + // }); // 註冊資料夾的右鍵選單 this.registerEvent( @@ -197,13 +197,10 @@ export default class GridExplorerPlugin extends Plugin { .onClick(async () => { const selectedText = editor.getSelection(); // 取得或啟用 GridView - const view = await this.activateView('folder','/'); + const view = await this.activateView('',''); if (view instanceof GridView) { // 設定搜尋模式和關鍵字 view.searchQuery = selectedText; - // 設定搜尋範圍 (預設搜尋所有檔案,不含媒體) - view.searchAllFiles = true; - view.searchMediaFiles = false; // 重新渲染視圖 view.render(true); // resetScroll = true } @@ -227,13 +224,10 @@ export default class GridExplorerPlugin extends Plugin { .setSection?.("view") .onClick(async () => { // 取得或啟用 GridView - const view = await this.activateView('folder','/'); + const view = await this.activateView('',''); if (view instanceof GridView) { // 設定搜尋模式和關鍵字 view.searchQuery = `#${tagName}`; - // 設定搜尋範圍 (預設搜尋所有檔案,不含媒體) - view.searchAllFiles = true; - view.searchMediaFiles = false; // 重新渲染視圖 view.render(true); // resetScroll = true } @@ -241,6 +235,67 @@ export default class GridExplorerPlugin extends Plugin { }); }) ); + + // 攔截所有tag點擊事件 + this.registerDomEvent(document, 'click', async (evt: MouseEvent) => { + // 如果未啟用攔截所有tag點擊事件,則跳過 + if (!this.settings.interceptAllTagClicks) return; + // 只處理左鍵 + if (evt.button !== 0) return; + + // 從觸發點往上找,看是否碰到 tag 連結 + const el = (evt.target as HTMLElement).closest( + 'a.tag, /* 預覽模式中的 #tag */\n' + + '.tag-pane-tag, /* 標籤面板中的 tag */\n' + + 'span.cm-hashtag, /* 編輯器中的 tag */\n' + + '.metadata-property[data-property-key="tags"] .multi-select-pill /* 屬性面板中的 tag */' + ) as HTMLElement | null; + if (!el) return; // 不是 tag 點擊就跳過 + + // 取出 tag 名稱(去掉前導 #) + let tagName = ''; + if (el.matches('span.cm-hashtag')) { + // 編輯器模式:可能被拆成多個 span,需組合 + const collect = [] as string[]; + let curr: HTMLElement | null = el; + // 向左收集 + while (curr && curr.matches('span.cm-hashtag') && !curr.classList.contains('cm-formatting')) { + collect.unshift(curr.textContent ?? ''); + curr = curr.previousElementSibling as HTMLElement | null; + if (curr && (!curr.matches('span.cm-hashtag') || curr.classList.contains('cm-formatting'))) break; + } + // 向右收集 + curr = el.nextElementSibling as HTMLElement | null; + while (curr && curr.matches('span.cm-hashtag') && !curr.classList.contains('cm-formatting')) { + collect.push(curr.textContent ?? ''); + curr = curr.nextElementSibling as HTMLElement | null; + } + tagName = collect.join('').trim(); + } else if (el.classList.contains('tag-pane-tag')) { + // Tag Pane 中的元素,需避開計數器 + const inner = el.querySelector('.tag-pane-tag-text, .tree-item-inner-text') as HTMLElement | null; + tagName = inner?.textContent?.trim() ?? ''; + } else if (el.matches('.metadata-property[data-property-key="tags"] .multi-select-pill')) { + // Frontmatter/Properties view + tagName = el.textContent?.trim() ?? ''; + } else { + // 預覽模式中的連結 a.tag + tagName = (el.getAttribute('data-tag') ?? el.textContent ?? '').trim(); + } + tagName = tagName.replace(/^#/, ''); + if (!tagName) return; + + // 阻止 Obsidian 自己的處理(開搜尋窗) + evt.preventDefault(); + evt.stopPropagation(); + + // 叫出 GridView,並把搜尋字串設成這個 tag + const view = await this.activateView('', ''); + if (view instanceof GridView) { + view.searchQuery = `#${tagName}`; + view.render(true); // resetScroll + } + }, true); // 用 capture,可在其他 listener 前先吃到 } async openNoteInRecentFiles(file: TFile) { @@ -300,7 +355,7 @@ export default class GridExplorerPlugin extends Plugin { } } - async activateView(mode = 'bookmarks', path = '') { + async activateView(mode = 'folder', path = '') { const { workspace } = this.app; let leaf = null; diff --git a/manifest.json b/manifest.json index 928c49a..116fe70 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "gridexplorer", "name": "GridExplorer", - "version": "2.4.5", + "version": "2.4.6", "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 ae71f66..34e839a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gridexplorer", - "version": "2.4.5", + "version": "2.4.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gridexplorer", - "version": "2.4.5", + "version": "2.4.6", "license": "MIT", "devDependencies": { "@types/node": "^16.11.6", diff --git a/package.json b/package.json index 288b741..aed2f91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gridexplorer", - "version": "2.4.5", + "version": "2.4.6", "description": "Browse note files in a grid view.", "main": "main.js", "scripts": { diff --git a/src/FileWatcher.ts b/src/FileWatcher.ts index bc7d597..754be1d 100644 --- a/src/FileWatcher.ts +++ b/src/FileWatcher.ts @@ -149,6 +149,7 @@ export class FileWatcher { }, 200); } + // 清理日期分隔線 private cleanupDateDividers(container: HTMLElement | null): void { if (!container) return; diff --git a/src/GridView.ts b/src/GridView.ts index bc61f9b..64727f0 100644 --- a/src/GridView.ts +++ b/src/GridView.ts @@ -150,8 +150,11 @@ export class GridView extends ItemView { } } - this.sourceMode = mode; - this.sourcePath = path; + if(mode !== '') this.sourceMode = mode; + if(path !== '') this.sourcePath = path; + if(this.sourceMode === '') this.sourceMode = 'folder'; + if(this.sourcePath === '') this.sourcePath = '/'; + this.render(resetScroll); // 通知 Obsidian 保存視圖狀態 this.app.workspace.requestSaveLayout(); @@ -880,6 +883,13 @@ export class GridView extends ItemView { default: break; } + } else if (this.searchQuery !== '' && this.searchAllFiles) { + // 顯示全域搜尋名稱 + const folderNameContainer = this.containerEl.createDiv('ge-foldername-content'); + folderNameContainer.createEl('span', { + text: `🔍 ${t('global_search')}`, + cls: 'ge-mode-title' + }); } // 創建內容區域 @@ -1657,21 +1667,32 @@ export class GridView extends ItemView { tagEl.addEventListener('contextmenu', (e) => { e.preventDefault(); e.stopPropagation(); + const tagText = tag.startsWith('#') ? tag : `#${tag}`; const menu = new Menu(); - menu.addItem(item => item - .setTitle(t('add_to_search')) - .onClick(() => { - const tagText = tag.startsWith('#') ? tag : `#${tag}`; - if (this.searchQuery.includes(tagText)) { - return; - } - this.searchQuery += ` ${tagText}`; - this.searchAllFiles = true; - this.searchMediaFiles = false; - this.render(true); - return false; - }) - ); + //添加加入搜尋選項 + if (!this.searchQuery.includes(tagText)) { + menu.addItem(item => item + .setTitle(t('add_tag_to_search')) + .setIcon('circle-plus') + .onClick(() => { + this.searchQuery += ` ${tagText}`; + this.render(true); + return false; + }) + ); + } + // 添加刪除標籤選項 + if (this.searchQuery.includes(tagText)) { + menu.addItem(item => item + .setTitle(t('remove_tag_from_search')) + .setIcon('circle-minus') + .onClick(() => { + this.searchQuery = this.searchQuery.replace(tagText, ''); + this.render(true); + return false; + }) + ); + } menu.showAtPosition({ x: e.clientX, y: e.clientY @@ -1687,8 +1708,6 @@ export class GridView extends ItemView { return; } this.searchQuery = tagText; - this.searchAllFiles = true; - this.searchMediaFiles = false; this.render(true); return false; }); @@ -2104,12 +2123,12 @@ export class GridView extends ItemView { for (const f of selectedFiles) { await this.app.fileManager.trashFile(f); } - // 清除選中狀態 - this.clearSelection(); } else { // 刪除單個檔案 await this.app.fileManager.trashFile(file); } + // 清除選中狀態 + this.clearSelection(); }); }); menu.showAtMouseEvent(event); diff --git a/src/settings.ts b/src/settings.ts index b9f8b98..32f7ad6 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -38,6 +38,7 @@ export interface GallerySettings { dateDividerMode: string; // 日期分隔器模式:none, year, month, day showCodeBlocksInSummary: boolean; // 是否在摘要中顯示程式碼區塊 folderNoteDisplaySettings: string; // 資料夾筆記設定 + interceptAllTagClicks: boolean; // 攔截所有tag點擊事件 } // 預設設定 @@ -61,7 +62,7 @@ export const DEFAULT_SETTINGS: GallerySettings = { showBacklinksMode: true, // 預設顯示反向連結模式 showOutgoinglinksMode: false, // 預設不顯示外部連結模式 showAllFilesMode: false, // 預設不顯示所有檔案模式 - showRandomNoteMode: false, // 預設不顯示隨機筆記模式 + showRandomNoteMode: true, // 預設顯示隨機筆記模式 showRecentFilesMode: true, // 預設顯示最近筆記模式 showTasksMode: false, // 預設不顯示任務模式 recentFilesCount: 30, // 預設最近筆記模式顯示的筆數 @@ -77,6 +78,7 @@ export const DEFAULT_SETTINGS: GallerySettings = { dateDividerMode: 'none', // 預設不使用日期分隔器 showCodeBlocksInSummary: false, // 預設不在摘要中顯示程式碼區塊 folderNoteDisplaySettings: 'default', // 預設不處理資料夾筆記 + interceptAllTagClicks: false, // 預設不攔截所有tag點擊事件 }; // 設定頁面類別 @@ -410,6 +412,19 @@ export class GridExplorerSettingTab extends PluginSettingTab { }); }); + // 攔截所有tag點擊事件 + new Setting(containerEl) + .setName(t('intercept_all_tag_clicks')) + .setDesc(t('intercept_all_tag_clicks_desc')) + .addToggle(toggle => { + toggle + .setValue(this.plugin.settings.interceptAllTagClicks) + .onChange(async (value) => { + this.plugin.settings.interceptAllTagClicks = value; + await this.plugin.saveSettings(); + }); + }); + // 自訂文件副檔名設定 new Setting(containerEl) .setName(t('custom_document_extensions')) diff --git a/src/translations.ts b/src/translations.ts index 80fe15c..c9c2445 100644 --- a/src/translations.ts +++ b/src/translations.ts @@ -44,7 +44,9 @@ export const TRANSLATIONS: Translations = { 'add': '新增', 'root': '根目錄', 'more_options': '更多選項', - 'add_to_search': '加入搜尋', + 'add_tag_to_search': '加入搜尋', + 'remove_tag_from_search': '從搜尋中移除', + 'global_search': '全域搜尋', // 視圖標題 'grid_view_title': '網格視圖', @@ -112,6 +114,8 @@ export const TRANSLATIONS: Translations = { 'show_code_block_in_summary_desc': '設定是否在摘要中顯示CodeBlock的內容', 'enable_file_watcher': '啟用檔案監控', 'enable_file_watcher_desc': '啟用後會自動偵測檔案變更並更新視圖,關閉後需手動點擊重新整理按鈕', + 'intercept_all_tag_clicks': '攔截所有標籤點擊事件', + 'intercept_all_tag_clicks_desc': '啟用後會攔截所有標籤點擊事件,並在網格視圖中打開標籤', 'reset_to_default' : '重置為預設值', 'reset_to_default_desc': '將所有設定重置為預設值', 'settings_reset_notice': '設定值已重置為預設值', @@ -252,7 +256,9 @@ export const TRANSLATIONS: Translations = { 'add': 'Add', 'root': 'Root', 'more_options': 'More options', - 'add_to_search': 'Add to search', + 'add_tag_to_search': 'Add to search', + 'remove_tag_from_search': 'Remove from search', + 'global_search': 'Global search', // View Titles 'grid_view_title': 'Grid view', @@ -320,6 +326,8 @@ export const TRANSLATIONS: Translations = { 'show_code_block_in_summary_desc': 'Set whether to show code block in the summary', 'enable_file_watcher': 'Enable file watcher', 'enable_file_watcher_desc': 'When enabled, the view will automatically update when files change. If disabled, you need to click the refresh button manually', + 'intercept_all_tag_clicks': 'Intercept all tag clicks', + 'intercept_all_tag_clicks_desc': 'When enabled, all tag clicks will be intercepted and opened in the grid view', 'reset_to_default': 'Reset to default', 'reset_to_default_desc': 'Reset all settings to default values', 'settings_reset_notice': 'Settings have been reset to default values', @@ -460,7 +468,9 @@ export const TRANSLATIONS: Translations = { 'add': '添加', 'root': '根目录', 'more_options': '更多选项', - 'add_to_search': '加入搜索', + 'add_tag_to_search': '加入搜索', + 'remove_tag_from_search': '从搜索中移除', + 'global_search': '全域搜索', // 视图标题 'grid_view_title': '网格视图', @@ -528,6 +538,8 @@ export const TRANSLATIONS: Translations = { 'show_code_block_in_summary_desc': '设置是否在摘要中显示CodeBlock的內容', 'enable_file_watcher': '启用文件监控', 'enable_file_watcher_desc': '启用后会自动检测文件变更并更新视图,关闭后需手动点击刷新按钮', + 'intercept_all_tag_clicks': '攔截所有標籤點擊事件', + 'intercept_all_tag_clicks_desc': '啟用後會攔截所有標籤點擊事件,並在網格視圖中打開標籤', 'reset_to_default': '重置为默认值', 'reset_to_default_desc': '将所有设置重置为默认值', 'settings_reset_notice': '设置值已重置为默认值', @@ -668,7 +680,9 @@ export const TRANSLATIONS: Translations = { 'add': '追加', 'root': 'ルート', 'more_options': 'もっと選択肢', - 'add_to_search': '検索に追加', + 'add_tag_to_search': '検索に追加', + 'remove_tag_from_search': '検索から削除', + 'global_search': 'グローバル検索', // ビュータイトル 'grid_view_title': 'グリッドビュー', @@ -736,6 +750,8 @@ export const TRANSLATIONS: Translations = { 'show_code_block_in_summary_desc': '要約にCodeBlockを表示するかどうかを設定', 'enable_file_watcher': 'ファイル監視を有効にする', 'enable_file_watcher_desc': '有効にすると、ファイルの変更を自動的に検出してビューを更新します。無効の場合は、更新ボタンを手動でクリックする必要があります', + 'intercept_all_tag_clicks': 'すべてのタグクリックを攔截する', + 'intercept_all_tag_clicks_desc': '有効にすると、すべてのタグクリックを攔截し、グリッドビューでタグを開きます', 'reset_to_default': 'デフォルトに戻す', 'reset_to_default_desc': 'すべての設定をデフォルト値に戻', 'settings_reset_notice': '設定値がデフォルト値にリセットされました', @@ -876,7 +892,9 @@ export const TRANSLATIONS: Translations = { 'add': 'Добавить', 'root': 'Корень', 'more_options': 'Больше опций', - 'add_to_search': 'Добавить в поиск', + 'add_tag_to_search': 'Добавить в поиск', + 'remove_tag_from_search': 'Удалить из поиска', + 'global_search': 'Глобальный поиск', // View Titles 'grid_view_title': 'Сеточный вид', @@ -944,6 +962,8 @@ export const TRANSLATIONS: Translations = { 'show_code_block_in_summary_desc': 'Установите, чтобы показывать CodeBlock в кратком описании', 'enable_file_watcher': 'Включить наблюдение за файлами', 'enable_file_watcher_desc': 'При включении вид будет автоматически обновляться при изменении файлов. Если отключено, нужно вручную нажимать кнопку обновления', + 'intercept_all_tag_clicks': 'Поймать все клики по тегу', + 'intercept_all_tag_clicks_desc': 'При включении все клики по тегу будут пойматься и открыться в сетке', 'reset_to_default': 'Сбросить на значения по умолчанию', 'reset_to_default_desc': 'Сбросить все настройки на значения по умолчанию', 'settings_reset_notice': 'Настройки сброшены на значения по умолчанию', @@ -1084,7 +1104,9 @@ export const TRANSLATIONS: Translations = { 'add': 'Додати', 'root': 'Корень', 'more_options': 'Більше опцій', - 'add_to_search': 'Додати до пошуку', + 'add_tag_to_search': 'Додати до пошуку', + 'remove_tag_from_search': 'Видалити з пошуку', + 'global_search': 'Глобальний пошук', // View Titles 'grid_view_title': 'Сітковий вигляд', @@ -1152,6 +1174,8 @@ export const TRANSLATIONS: Translations = { 'show_code_block_in_summary_desc': 'Встановіть, щоб показувати CodeBlock в короткому описі', 'enable_file_watcher': 'Увімкнути спостереження за файлами', 'enable_file_watcher_desc': 'При увімкненні вигляд автоматично оновлюватиметься при зміні файлів. Якщо вимкнено, потрібно вручну натискати кнопку оновлення', + 'intercept_all_tag_clicks': 'Поймати всі кліки по тегу', + 'intercept_all_tag_clicks_desc': 'При увімкненні всі кліки по тегу будуть поймані та відображатися в сетці', 'reset_to_default': 'Скинути до стандартних значень', 'reset_to_default_desc': 'Скинути всі налаштування до стандартних значень', 'settings_reset_notice': 'Налаштування скинуто до стандартних значень', diff --git a/versions.json b/versions.json index f0fa3b5..f020afa 100644 --- a/versions.json +++ b/versions.json @@ -1,3 +1,3 @@ { - "2.4.5": "1.1.0" + "2.4.6": "1.1.0" } \ No newline at end of file