From a414b60cb06c97d697afcb237b5b52517fe1698f Mon Sep 17 00:00:00 2001 From: Devon22 Date: Wed, 9 Apr 2025 00:22:38 +0800 Subject: [PATCH] 1.9.9 --- manifest.json | 2 +- package-lock.json | 4 +- package.json | 2 +- src/FileWatcher.ts | 11 +++ src/FolderRenameModal.ts | 84 +++++++++++++++++++++++ src/FolderSelectionModal.ts | 2 +- src/GridView.ts | 132 ++++++++++++++++++++++++++++++++++-- src/translations.ts | 16 +++++ styles.css | 16 +++++ versions.json | 2 +- 10 files changed, 258 insertions(+), 13 deletions(-) create mode 100644 src/FolderRenameModal.ts diff --git a/manifest.json b/manifest.json index a82fdee..7cf31db 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "gridexplorer", "name": "GridExplorer", - "version": "1.9.8", + "version": "1.9.9", "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 08dece7..84ae71b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gridexplorer", - "version": "1.9.8", + "version": "1.9.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gridexplorer", - "version": "1.9.8", + "version": "1.9.9", "license": "MIT", "devDependencies": { "@types/node": "^16.11.6", diff --git a/package.json b/package.json index cc5fa1d..142cedf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gridexplorer", - "version": "1.9.8", + "version": "1.9.9", "description": "Browse note files in a grid view.", "main": "main.js", "scripts": { diff --git a/src/FileWatcher.ts b/src/FileWatcher.ts index 6a19df6..7727c2a 100644 --- a/src/FileWatcher.ts +++ b/src/FileWatcher.ts @@ -86,6 +86,17 @@ export class FileWatcher { } }) ); + + // 監聽當前開啟的檔案變更,讀取反向連結 + this.plugin.registerEvent( + this.app.workspace.on('file-open', (file) => { + if (file instanceof TFile) { + if (this.gridView.sourceMode === 'backlinks' && this.gridView.searchQuery === '') { + this.gridView.render(); + } + } + }) + ); } } diff --git a/src/FolderRenameModal.ts b/src/FolderRenameModal.ts new file mode 100644 index 0000000..a22e925 --- /dev/null +++ b/src/FolderRenameModal.ts @@ -0,0 +1,84 @@ +import { App, Modal, Setting, TFolder } from 'obsidian'; +import { t } from './translations'; +import GridExplorerPlugin from '../main'; +import { GridView } from './GridView'; + +export function showFolderRenameModal(app: App, plugin: GridExplorerPlugin, folder: TFolder, gridView: GridView) { + new FolderRenameModal(app, plugin, folder, gridView).open(); +} + +export class FolderRenameModal extends Modal { + plugin: GridExplorerPlugin; + folder: TFolder; + gridView: GridView; + newName: string; + + constructor(app: App, plugin: GridExplorerPlugin, folder: TFolder, gridView: GridView) { + super(app); + this.plugin = plugin; + this.folder = folder; + this.gridView = gridView; + this.newName = folder.name; + } + + onOpen() { + const { contentEl } = this; + contentEl.empty(); + + // 如果有 GridView 實例,禁用其鍵盤導航 + if (this.gridView) { + this.gridView.disableKeyboardNavigation(); + } + + new Setting(contentEl) + .setName(t('rename_folder')) + .setDesc(t('enter_new_folder_name')) + .addText(text => { + text + .setValue(this.folder.name) + .onChange(value => { + this.newName = value; + }); + }); + + new Setting(contentEl) + .addButton(button => { + button + .setButtonText(t('confirm')) + .setCta() + .onClick(() => { + this.renameFolder(); + this.close(); + }); + }) + .addButton(button => { + button + .setButtonText(t('cancel')) + .onClick(() => { + this.close(); + }); + }); + } + + async renameFolder() { + try { + await this.app.fileManager.renameFile(this.folder, this.newName); + // 重新渲染視圖 + setTimeout(() => { + this.gridView.render(); + }, 100); + } catch (error) { + console.error('Failed to rename folder', error); + } + } + + onClose() { + const { contentEl } = this; + contentEl.empty(); + + // 如果有 GridView 實例,重新啟用其鍵盤導航 + if (this.gridView) { + this.gridView.enableKeyboardNavigation(); + } + } +} \ No newline at end of file diff --git a/src/FolderSelectionModal.ts b/src/FolderSelectionModal.ts index 8b4f856..b1ebc93 100644 --- a/src/FolderSelectionModal.ts +++ b/src/FolderSelectionModal.ts @@ -1,4 +1,4 @@ -import { App, Modal, Setting, Platform } from 'obsidian'; +import { App, Modal, Platform } from 'obsidian'; import { GridView } from './GridView'; import { t } from './translations'; import GridExplorerPlugin from '../main'; diff --git a/src/GridView.ts b/src/GridView.ts index 12383d8..8eb67aa 100644 --- a/src/GridView.ts +++ b/src/GridView.ts @@ -5,6 +5,7 @@ import { findFirstImageInNote } from './mediaUtils'; import { MediaModal } from './MediaModal'; import { showFolderNoteSettingsModal } from './FolderNoteSettingsModal'; import { showNoteColorSettingsModal } from './NoteColorSettingsModal'; +import { showFolderRenameModal } from './FolderRenameModal'; import { showSearchModal } from './SearchModal'; import { FileWatcher } from './FileWatcher'; import { t } from './translations'; @@ -102,10 +103,10 @@ export class GridView extends ItemView { } } - async setSource(mode: string, path = '', resetScroll = false) { + async setSource(mode: string, path = '', resetScroll = false, recordHistory = true) { // 記錄之前的狀態到歷史記錄中(如果有) - if (this.sourceMode) { + if (this.sourceMode && recordHistory) { const previousState = JSON.stringify({ mode: this.sourceMode, path: this.sourcePath }); this.recentSources.unshift(previousState); // 限制歷史記錄數量為10個 @@ -170,6 +171,11 @@ export class GridView extends ItemView { } return []; } else if (this.sourceMode === 'backlinks') { + + if(this.searchQuery !== '') { + return []; + } + // 反向連結模式:找出所有引用當前筆記的檔案 const activeFile = this.app.workspace.getActiveFile(); if (!activeFile) { @@ -188,7 +194,7 @@ export class GridView extends ItemView { } } - return this.sortFiles(this.ignoredFiles(Array.from(backlinks) as TFile[])); + return this.sortFiles(Array.from(backlinks) as TFile[]); } else if(this.sourceMode === 'bookmarks') { // 書籤模式 const bookmarksPlugin = (this.app as any).internalPlugins.plugins.bookmarks; @@ -499,6 +505,64 @@ export class GridView extends ItemView { }); setIcon(newNoteButton, 'square-pen'); + newNoteButton.addEventListener('contextmenu', (event) => { + event.preventDefault(); + const menu = new Menu(); + // 新增筆記 + menu.addItem((item) => { + item + .setTitle(t('new_note')) + .setIcon('square-pen') + .onClick(async () => { + let newFileName = `${t('untitled')}.md`; + let newFilePath = !this.sourcePath || this.sourcePath === '/' ? newFileName : `${this.sourcePath}/${newFileName}`; + + // 檢查檔案是否已存在,如果存在則遞增編號 + let counter = 1; + while (this.app.vault.getAbstractFileByPath(newFilePath)) { + newFileName = `${t('untitled')} ${counter}.md`; + newFilePath = !this.sourcePath || this.sourcePath === '/' ? newFileName : `${this.sourcePath}/${newFileName}`; + counter++; + } + + try { + // 建立新筆記 + const newFile = await this.app.vault.create(newFilePath, ''); + // 開啟新筆記 + await this.app.workspace.getLeaf().openFile(newFile); + } catch (error) { + console.error('An error occurred while creating a new note:', error); + } + }); + }); + // 新增資料夾 + menu.addItem((item) => { + item.setTitle(t('new_folder')) + .setIcon('folder') + .onClick(async () => { + let newFolderName = `${t('untitled')}`; + let newFolderPath = !this.sourcePath || this.sourcePath === '/' ? newFolderName : `${this.sourcePath}/${newFolderName}`; + + // 檢查資料夾是否已存在,如果存在則遞增編號 + let counter = 1; + while (this.app.vault.getAbstractFileByPath(newFolderPath)) { + newFolderName = `${t('untitled')} ${counter}`; + newFolderPath = !this.sourcePath || this.sourcePath === '/' ? newFolderName : `${this.sourcePath}/${newFolderName}`; + counter++; + } + + try { + // 建立新資料夾 + await this.app.vault.createFolder(newFolderPath); + this.render(false); + } catch (error) { + console.error('An error occurred while creating a new folder:', error); + } + }); + }); + menu.showAtMouseEvent(event); + }); + // 添加回上層按鈕(僅在資料夾模式且不在根目錄時顯示) if (this.sourceMode === 'folder' && this.sourcePath !== '/' && this.searchQuery === '') { const upButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('go_up') } }); @@ -655,7 +719,18 @@ export class GridView extends ItemView { .setTitle(`${displayText}`) .setIcon(`${icon}`) .onClick(() => { - this.setSource(mode, path, true); + // 找出當前點擊的紀錄索引 + const clickedIndex = this.recentSources.findIndex(source => { + const parsed = JSON.parse(source); + return parsed.mode === mode && parsed.path === path; + }); + + // 如果找到點擊的紀錄,清除它之上的紀錄 + if (clickedIndex !== -1) { + this.recentSources = this.recentSources.slice(clickedIndex + 1); + } + + this.setSource(mode, path, true, false); }); }); } catch (error) { @@ -792,6 +867,9 @@ export class GridView extends ItemView { }); } + // 創建資料夾夾名稱區域 + // headerButtonsDiv.createDiv('ge-foldername-content'); + // 創建內容區域 const contentEl = this.containerEl.createDiv('view-content'); @@ -851,6 +929,21 @@ export class GridView extends ItemView { return; } + // 如果是資料夾模式且沒有搜尋結果,顯示目前資料夾名稱 + // if (this.sourceMode === 'folder' && this.searchQuery === '' && this.sourcePath !== '/') { + // const folderName = this.sourcePath.split('/').pop(); + // const folderNameContainer = this.containerEl.querySelector('.ge-foldername-content') as HTMLElement; + // if (folderNameContainer) { + // folderNameContainer.createEl('span', { text: `📁 ${folderName}` }); + // } + // } else { + // const folderNameContainer = this.containerEl.querySelector('.ge-foldername-content') as HTMLElement; + // if (folderNameContainer) { + // folderNameContainer.empty(); + // folderNameContainer.style.display = 'none'; + // } + // } + // 如果啟用了顯示"回上層資料夾"選項 if (this.sourceMode === 'folder' && this.searchQuery === '' && this.plugin.settings.showParentFolderItem && this.sourcePath !== '/') { @@ -992,10 +1085,9 @@ export class GridView extends ItemView { }); //刪除資料夾筆記 menu.addItem((item) => { - (item as any).setWarning(true); item .setTitle(t('delete_folder_note')) - .setIcon('trash') + .setIcon('folder-x') .onClick(() => { this.app.fileManager.trashFile(noteFile as TFile); // 重新渲染視圖 @@ -1025,7 +1117,33 @@ export class GridView extends ItemView { .onClick(() => { this.plugin.settings.ignoredFolders.push(folder.path); this.plugin.saveSettings(); - this.render(); + }); + }); + // 重新命名資料夾 + menu.addItem((item) => { + item + .setTitle(t('rename_folder')) + .setIcon('file-cog') + .onClick(() => { + if (folder instanceof TFolder) { + showFolderRenameModal(this.app, this.plugin, folder, this); + } + }); + }); + //刪除資料夾 + menu.addItem((item) => { + (item as any).setWarning(true); + item + .setTitle(t('delete_folder')) + .setIcon('trash') + .onClick(async () => { + if (folder instanceof TFolder) { + await this.app.fileManager.trashFile(folder); + // 重新渲染視圖 + setTimeout(() => { + this.render(); + }, 100); + } }); }); menu.showAtMouseEvent(event); diff --git a/src/translations.ts b/src/translations.ts index c46f6cd..15ad8af 100644 --- a/src/translations.ts +++ b/src/translations.ts @@ -34,6 +34,8 @@ export const TRANSLATIONS: Translations = { 'search_current_location_only': '僅搜尋目前位置', 'cancel': '取消', 'new_note': '新增筆記', + 'new_folder': '新增資料夾', + 'delete_folder': '刪除資料夾', 'untitled': '未命名', 'files': '個檔案', 'add': '新增', @@ -168,6 +170,8 @@ export const TRANSLATIONS: Translations = { 'note_color': '筆記顏色', 'note_color_desc': '設定此筆記的顯示顏色', 'set_note_color': '設定筆記顏色', + 'rename_folder': '重新命名資料夾', + 'enter_new_folder_name': '輸入新資料夾名稱', }, 'en': { // Notifications @@ -184,6 +188,8 @@ export const TRANSLATIONS: Translations = { 'search_current_location_only': 'Search current location only', 'cancel': 'Cancel', 'new_note': 'New note', + 'new_folder': 'New folder', + 'delete_folder': 'Delete folder', 'untitled': 'Untitled', 'files': 'files', 'add': 'Add', @@ -318,6 +324,8 @@ export const TRANSLATIONS: Translations = { 'note_color': 'Note color', 'note_color_desc': 'Set the display color for this note', 'set_note_color': 'Set note color', + 'rename_folder': 'Rename folder', + 'enter_new_folder_name': 'Enter new folder name', }, 'zh': { // 通知信息 @@ -334,6 +342,8 @@ export const TRANSLATIONS: Translations = { 'search_current_location_only': '仅搜索当前位置', 'cancel': '取消', 'new_note': '新建笔记', + 'new_folder': '新建文件夹', + 'delete_folder': '删除文件夹', 'untitled': '未命名', 'files': '个文件', 'add': '添加', @@ -468,6 +478,8 @@ export const TRANSLATIONS: Translations = { 'note_color': '笔记颜色', 'note_color_desc': '设置此笔记的显示颜色', 'set_note_color': '设置笔记颜色', + 'rename_folder': '重新命名文件夹', + 'enter_new_folder_name': '输入新文件夹名称', }, 'ja': { // 通知メッジ @@ -484,6 +496,8 @@ export const TRANSLATIONS: Translations = { 'search_current_location_only': '現在の場所のみ検索', 'cancel': 'キャンセル', 'new_note': '新規ノート', + 'new_folder': '新規フォルダ', + 'delete_folder': '削除フォルダ', 'untitled': '無題', 'files': 'ファイル', 'add': '追加', @@ -618,5 +632,7 @@ export const TRANSLATIONS: Translations = { 'note_color': 'ノート色', 'note_color_desc': 'このノートの表示色を設定', 'set_note_color': 'ノート色を設定', + 'rename_folder': 'フォルダを再命名', + 'enter_new_folder_name': '新しいフォルダ名を入力', } } diff --git a/styles.css b/styles.css index 07bf84d..7e2d46a 100644 --- a/styles.css +++ b/styles.css @@ -257,6 +257,22 @@ align-items: center; } +/* .ge-foldername-content { + margin-top: 5px; + flex-basis: 100%; + order: 1; + display: flex; + justify-content: center; + background-color: var(--background-secondary); + border-radius: var(--button-radius); +} + +.ge-foldername-content span { + display: inline-flex; + align-items: center; + justify-content: center; +} */ + /* 搜尋對話框樣式 */ .ge-search-container { margin-bottom: 8px; diff --git a/versions.json b/versions.json index 14df53d..96762cd 100644 --- a/versions.json +++ b/versions.json @@ -1,3 +1,3 @@ { - "1.9.8": "1.1.0" + "1.9.9": "1.1.0" } \ No newline at end of file