commit c7e13c0168d86efc24de04289b2826d63140ac42 Author: Marek Lenczewski Date: Wed Apr 15 21:14:00 2026 +0200 init diff --git a/data.json b/data.json new file mode 100644 index 0000000..42fca03 --- /dev/null +++ b/data.json @@ -0,0 +1,24 @@ +{ + "ignoredFolders": [], + "ignoredFolderPatterns": [], + "defaultSortType": "mtime-desc", + "gridItemWidth": 150, + "gridItemHeight": 0, + "imageAreaWidth": 0, + "imageAreaHeight": 0, + "titleFontSize": 0.7, + "summaryLength": 0, + "enableFileWatcher": true, + "showMediaFiles": false, + "searchMediaFiles": false, + "showVideoThumbnails": false, + "defaultOpenLocation": "tab", + "showParentFolderItem": true, + "reuseExistingLeaf": true, + "showBookmarksMode": true, + "showSearchMode": true, + "showBacklinksMode": true, + "showAllFilesMode": false, + "showRandomNoteMode": false, + "customDocumentExtensions": "" +} \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..98d0871 --- /dev/null +++ b/main.js @@ -0,0 +1,3329 @@ +/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if ((from && typeof from === "object") || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { + get: () => from[key], + enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable, + }); + } + return to; +}; +var __toCommonJS = (mod) => + __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// main.ts +var main_exports = {}; +__export(main_exports, { + default: () => GridExplorerPlugin, +}); +module.exports = __toCommonJS(main_exports); +var import_obsidian10 = require("obsidian"); + +// src/GridView.ts +var import_obsidian7 = require("obsidian"); +var import_obsidian8 = require("obsidian"); + +// src/FolderSelectionModal.ts +var import_obsidian = require("obsidian"); + +// src/translations.ts +function t(key) { + const lang = window.localStorage.getItem("language"); + const translations = TRANSLATIONS[lang] || TRANSLATIONS["en"]; + return translations[key] || key; +} +var TRANSLATIONS = { + "zh-TW": { + // 通知訊息 + bookmarks_plugin_disabled: + "\u8ACB\u5148\u555F\u7528\u66F8\u7C64\u5916\u639B", + // 按鈕和標籤 + sorting: "\u6392\u5E8F\u65B9\u5F0F", + refresh: "\u91CD\u65B0\u6574\u7406", + reselect_folder: "\u91CD\u65B0\u9078\u64C7\u4F4D\u7F6E", + go_up: "\u8FD4\u56DE\u4E0A\u5C64\u8CC7\u6599\u593E", + no_backlinks: "\u6C92\u6709\u53CD\u5411\u9023\u7D50", + search: "\u641C\u5C0B", + search_placeholder: "\u641C\u5C0B\u95DC\u9375\u5B57", + search_current_location_only: "\u50C5\u641C\u5C0B\u76EE\u524D\u4F4D\u7F6E", + cancel: "\u53D6\u6D88", + new_note: "\u65B0\u589E\u7B46\u8A18", + untitled: "\u672A\u547D\u540D", + files: "\u500B\u6A94\u6848", + add: "\u65B0\u589E", + // 視圖標題 + grid_view_title: "\u7DB2\u683C\u8996\u5716", + bookmarks_mode: "\u66F8\u7C64", + folder_mode: "\u8CC7\u6599\u593E", + search_results: "\u641C\u5C0B\u7D50\u679C", + backlinks_mode: "\u53CD\u5411\u9023\u7D50", + all_files_mode: "\u6240\u6709\u6A94\u6848", + random_note_mode: "\u96A8\u6A5F\u7B46\u8A18", + // 排序選項 + sort_name_asc: "\u540D\u7A31 (A \u2192 Z)", + sort_name_desc: "\u540D\u7A31 (Z \u2192 A)", + sort_mtime_desc: "\u4FEE\u6539\u6642\u9593 (\u65B0 \u2192 \u820A)", + sort_mtime_asc: "\u4FEE\u6539\u6642\u9593 (\u820A \u2192 \u65B0)", + sort_ctime_desc: "\u5EFA\u7ACB\u6642\u9593 (\u65B0 \u2192 \u820A)", + sort_ctime_asc: "\u5EFA\u7ACB\u6642\u9593 (\u820A \u2192 \u65B0)", + sort_random: "\u96A8\u6A5F\u6392\u5E8F", + // 設定 + grid_view_settings: "\u7DB2\u683C\u8996\u5716\u8A2D\u5B9A", + media_files_settings: "\u5A92\u9AD4\u6A94\u6848\u8A2D\u5B9A", + show_media_files: "\u986F\u793A\u5716\u7247\u548C\u5F71\u7247", + show_media_files_desc: + "\u5728\u7DB2\u683C\u8996\u5716\u4E2D\u986F\u793A\u5716\u7247\u548C\u5F71\u7247\u6A94\u6848", + search_media_files: "\u641C\u5C0B\u5716\u7247\u548C\u5F71\u7247", + search_media_files_desc: + "\u5728\u641C\u5C0B\u7D50\u679C\u4E2D\u5305\u542B\u5716\u7247\u548C\u5F71\u7247\u6A94\u6848\uFF08\u50C5\u5728\u555F\u7528\u986F\u793A\u5716\u7247\u548C\u5F71\u7247\u6642\u6709\u6548\uFF09", + show_video_thumbnails: "\u986F\u793A\u5F71\u7247\u7E2E\u5716", + show_video_thumbnails_desc: + "\u5728\u7DB2\u683C\u8996\u5716\u4E2D\u986F\u793A\u5F71\u7247\u7684\u7E2E\u5716\uFF0C\u95DC\u9589\u6642\u5C07\u986F\u793A\u64AD\u653E\u5716\u793A", + ignored_folders: "\u5FFD\u7565\u7684\u8CC7\u6599\u593E", + ignored_folders_desc: + "\u5728\u9019\u88E1\u8A2D\u5B9A\u8981\u5FFD\u7565\u7684\u8CC7\u6599\u593E", + add_ignored_folder: "\u65B0\u589E\u5FFD\u7565\u8CC7\u6599\u593E", + no_ignored_folders: + "\u6C92\u6709\u5FFD\u7565\u7684\u8CC7\u6599\u593E\u3002", + ignored_folder_patterns: + "\u4EE5\u5B57\u4E32\u5FFD\u7565\u8CC7\u6599\u593E\u548C\u6A94\u6848", + ignored_folder_patterns_desc: + "\u4F7F\u7528\u5B57\u4E32\u6A21\u5F0F\u5FFD\u7565\u8CC7\u6599\u593E\u548C\u6A94\u6848\uFF08\u652F\u63F4\u6B63\u5247\u8868\u9054\u5F0F\uFF09", + add_ignored_folder_pattern: + "\u65B0\u589E\u5FFD\u7565\u8CC7\u6599\u593E\u6A21\u5F0F", + ignored_folder_pattern_placeholder: + "\u8F38\u5165\u8CC7\u6599\u593E\u540D\u7A31\u6216\u6B63\u5247\u8868\u9054\u5F0F", + no_ignored_folder_patterns: + "\u6C92\u6709\u5FFD\u7565\u7684\u8CC7\u6599\u593E\u6A21\u5F0F\u3002", + remove: "\u79FB\u9664", + default_sort_type: "\u9810\u8A2D\u6392\u5E8F\u6A21\u5F0F", + default_sort_type_desc: + "\u8A2D\u5B9A\u958B\u555F\u7DB2\u683C\u8996\u5716\u6642\u7684\u9810\u8A2D\u6392\u5E8F\u6A21\u5F0F", + grid_item_width: "\u7DB2\u683C\u9805\u76EE\u5BEC\u5EA6", + grid_item_width_desc: + "\u8A2D\u5B9A\u7DB2\u683C\u9805\u76EE\u7684\u5BEC\u5EA6", + grid_item_height: "\u7DB2\u683C\u9805\u76EE\u9AD8\u5EA6", + grid_item_height_desc: + "\u8A2D\u5B9A\u7DB2\u683C\u9805\u76EE\u7684\u9AD8\u5EA6 (\u8A2D\u70BA0\u6642\u70BA\u81EA\u52D5\u8ABF\u6574)", + image_area_width: "\u5716\u7247\u5340\u57DF\u5BEC\u5EA6", + image_area_width_desc: + "\u8A2D\u5B9A\u5716\u7247\u9810\u89BD\u5340\u57DF\u7684\u5BEC\u5EA6", + image_area_height: "\u5716\u7247\u5340\u57DF\u9AD8\u5EA6", + image_area_height_desc: + "\u8A2D\u5B9A\u5716\u7247\u9810\u89BD\u5340\u57DF\u7684\u9AD8\u5EA6", + title_font_size: "\u6A19\u984C\u5B57\u9AD4\u5927\u5C0F", + title_font_size_desc: + "\u8A2D\u5B9A\u6A19\u984C\u5B57\u9AD4\u7684\u5927\u5C0F", + summary_length: "\u6458\u8981\u9577\u5EA6", + summary_length_desc: "\u8A2D\u5B9A\u6458\u8981\u7684\u9577\u5EA6", + enable_file_watcher: "\u555F\u7528\u6A94\u6848\u76E3\u63A7", + enable_file_watcher_desc: + "\u555F\u7528\u5F8C\u6703\u81EA\u52D5\u5075\u6E2C\u6A94\u6848\u8B8A\u66F4\u4E26\u66F4\u65B0\u8996\u5716\uFF0C\u95DC\u9589\u5F8C\u9700\u624B\u52D5\u9EDE\u64CA\u91CD\u65B0\u6574\u7406\u6309\u9215", + reset_to_default: "\u91CD\u7F6E\u70BA\u9810\u8A2D\u503C", + reset_to_default_desc: + "\u5C07\u6240\u6709\u8A2D\u5B9A\u91CD\u7F6E\u70BA\u9810\u8A2D\u503C", + settings_reset_notice: + "\u8A2D\u5B9A\u503C\u5DF2\u91CD\u7F6E\u70BA\u9810\u8A2D\u503C", + ignored_folders_settings: "\u5FFD\u7565\u8CC7\u6599\u593E\u8A2D\u5B9A", + display_mode_settings: "\u986F\u793A\u6A21\u5F0F\u8A2D\u5B9A", + show_bookmarks_mode: "\u986F\u793A\u66F8\u7C64\u6A21\u5F0F", + show_search_mode: "\u986F\u793A\u641C\u5C0B\u7D50\u679C\u6A21\u5F0F", + show_backlinks_mode: "\u986F\u793A\u53CD\u5411\u9023\u7D50\u6A21\u5F0F", + show_all_files_mode: "\u986F\u793A\u6240\u6709\u6A94\u6848\u6A21\u5F0F", + show_random_note_mode: "\u986F\u793A\u96A8\u6A5F\u7B46\u8A18\u6A21\u5F0F", + random_note_notes_only: "\u50C5\u7B46\u8A18", + random_note_include_media_files: "\u5305\u542B\u5716\u7247\u5F71\u7247", + // 顯示"返回上層資料夾"選項設定 + show_parent_folder_item: + "\u986F\u793A\u300C\u8FD4\u56DE\u4E0A\u5C64\u8CC7\u6599\u593E\u300D", + show_parent_folder_item_desc: + "\u5728\u7DB2\u683C\u7684\u7B2C\u4E00\u9805\u986F\u793A\u300C\u8FD4\u56DE\u4E0A\u5C64\u8CC7\u6599\u593E\u300D\u9078\u9805", + parent_folder: "\u4E0A\u5C64\u8CC7\u6599\u593E", + // 預設開啟位置設定 + default_open_location: "\u9810\u8A2D\u958B\u555F\u4F4D\u7F6E", + default_open_location_desc: + "\u8A2D\u5B9A\u7DB2\u683C\u8996\u5716\u9810\u8A2D\u958B\u555F\u7684\u4F4D\u7F6E", + open_in_left_sidebar: "\u958B\u5728\u5DE6\u5074\u908A\u6B04", + open_in_right_sidebar: "\u958B\u5728\u53F3\u5074\u908A\u6B04", + open_in_new_tab: "\u5728\u65B0\u5206\u9801\u958B\u555F", + reuse_existing_leaf: + "\u91CD\u8907\u4F7F\u7528\u5DF2\u958B\u555F\u7684\u8996\u5716", + reuse_existing_leaf_desc: + "\u958B\u555F\u7DB2\u683C\u8996\u5716\u6642\uFF0C\u512A\u5148\u4F7F\u7528\u5DF2\u958B\u555F\u7684\u8996\u5716\u800C\u975E\u5EFA\u7ACB\u65B0\u8996\u5716", + custom_document_extensions: + "\u81EA\u8A02\u6587\u4EF6\u6A94\u6848\u526F\u6A94\u540D", + custom_document_extensions_desc: + "\u984D\u5916\u7684\u6587\u4EF6\u526F\u6A94\u540D\uFF08\u7528\u9017\u865F\u5206\u9694\uFF0C\u4E0D\u542B\u9EDE\u865F\uFF09", + custom_document_extensions_placeholder: "\u4F8B\u5982\uFF1Atxt,doc,docx", + // 選擇資料夾對話框 + select_folders: "\u9078\u64C7\u8CC7\u6599\u593E", + open_grid_view: "\u958B\u555F\u7DB2\u683C\u8996\u5716", + open_note_in_grid_view: + "\u5728\u7DB2\u683C\u8996\u5716\u4E2D\u958B\u555F\u7576\u524D\u7B46\u8A18", + open_backlinks_in_grid_view: + "\u5728\u7DB2\u683C\u8996\u5716\u4E2D\u958B\u555F\u53CD\u5411\u9023\u7D50", + open_settings: "\u958B\u555F\u8A2D\u5B9A", + delete_note: "\u522A\u9664\u6A94\u6848", + open_folder_note: "\u958B\u555F\u8CC7\u6599\u593E\u7B46\u8A18", + create_folder_note: "\u5EFA\u7ACB\u8CC7\u6599\u593E\u7B46\u8A18", + ignore_folder: "\u5FFD\u7565\u6B64\u8CC7\u6599\u593E", + searching: "\u641C\u5C0B\u4E2D...", + no_files: "\u6C92\u6709\u627E\u5230\u4EFB\u4F55\u6A94\u6848", + filter_folders: "\u7BE9\u9078\u8CC7\u6599\u593E...", + }, + en: { + // Notifications + bookmarks_plugin_disabled: "Please enable the Bookmarks plugin first", + // Buttons and Labels + sorting: "Sort by", + refresh: "Refresh", + reselect_folder: "Reselect folder", + go_up: "Go Up", + no_backlinks: "No backlinks", + search: "Search", + search_placeholder: "Search keyword", + search_current_location_only: "Search current location only", + cancel: "Cancel", + new_note: "New note", + untitled: "Untitled", + files: "files", + add: "Add", + // View Titles + grid_view_title: "Grid view", + bookmarks_mode: "Bookmarks", + folder_mode: "Folder", + search_results: "Search results", + backlinks_mode: "Backlinks", + all_files_mode: "All files", + random_note_mode: "Random note", + // Sort Options + sort_name_asc: "Name (A \u2192 Z)", + sort_name_desc: "Name (Z \u2192 A)", + sort_mtime_desc: "Modified (New \u2192 Old)", + sort_mtime_asc: "Modified (Old \u2192 New)", + sort_ctime_desc: "Created (New \u2192 Old)", + sort_ctime_asc: "Created (Old \u2192 New)", + sort_random: "Random", + // Settings + grid_view_settings: "Grid view settings", + media_files_settings: "Media files settings", + show_media_files: "Show images and videos", + show_media_files_desc: "Display image and video files in the grid view", + search_media_files: "Search images and videos", + search_media_files_desc: + "Include image and video files in search results (only effective when show images and videos is enabled)", + show_video_thumbnails: "Show video thumbnails", + show_video_thumbnails_desc: + "Display thumbnails for videos in the grid view, shows a play icon when disabled", + ignored_folders: "Ignored folders", + ignored_folders_desc: "Set folders to ignore here", + add_ignored_folder: "Add ignored folder", + no_ignored_folders: "No ignored folders.", + ignored_folder_patterns: "Ignore folders and files by pattern", + ignored_folder_patterns_desc: + "Use string patterns to ignore folders and files (supports regular expressions)", + add_ignored_folder_pattern: "Add folder pattern", + ignored_folder_pattern_placeholder: "Enter folder name or regex pattern", + no_ignored_folder_patterns: "No ignored folder patterns.", + remove: "Remove", + default_sort_type: "Default sort type", + default_sort_type_desc: + "Set the default sorting method when opening Grid View", + grid_item_width: "Grid item width", + grid_item_width_desc: "Set the width of grid items", + grid_item_height: "Grid item height", + grid_item_height_desc: + "Set the height of grid items (set to 0 to automatically adjust)", + image_area_width: "Image area width", + image_area_width_desc: "Set the width of the image preview area", + image_area_height: "Image area height", + image_area_height_desc: "Set the height of the image preview area", + title_font_size: "Title font size", + title_font_size_desc: "Set the size of the title font", + summary_length: "Summary length", + summary_length_desc: "Set the length of 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", + 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", + ignored_folders_settings: "Ignore folders settings", + display_mode_settings: "Display mode settings", + show_bookmarks_mode: "Show bookmarks mode", + show_search_mode: "Show search results mode", + show_backlinks_mode: "Show backlinks mode", + show_all_files_mode: "Show all files mode", + show_random_note_mode: "Show random note mode", + random_note_notes_only: "Notes Only", + random_note_include_media_files: "Include Media Files", + // Show "Parent Folder" option setting + show_parent_folder_item: 'Show "Parent Folder" item', + show_parent_folder_item_desc: + 'Show a "Parent Folder" item as the first item in the grid', + parent_folder: "Parent Folder", + // Default open location setting + default_open_location: "Default open location", + default_open_location_desc: + "Set the default location to open the grid view", + open_in_left_sidebar: "Open in left sidebar", + open_in_right_sidebar: "Open in right sidebar", + open_in_new_tab: "Open in new tab", + reuse_existing_leaf: "Reuse existing view", + reuse_existing_leaf_desc: + "When opening Grid View, prioritize using an existing view instead of creating a new one", + custom_document_extensions: "Custom Document Extensions", + custom_document_extensions_desc: + "Additional document extensions (comma-separated, without dots)", + custom_document_extensions_placeholder: "e.g., txt,doc,docx", + // Select Folder Dialog + select_folders: "Select folder", + open_grid_view: "Open grid view", + open_note_in_grid_view: "Open note in grid view", + open_backlinks_in_grid_view: "Open backlinks in grid view", + open_settings: "Open settings", + delete_note: "Delete file", + open_folder_note: "Open folder note", + create_folder_note: "Create folder note", + ignore_folder: "Ignore this folder", + searching: "Searching...", + no_files: "No files found", + filter_folders: "Filter folders...", + }, + zh: { + // 通知信息 + bookmarks_plugin_disabled: + "\u8BF7\u5148\u542F\u7528\u4E66\u7B7E\u63D2\u4EF6", + // 按钮和标签 + sorting: "\u6392\u5E8F\u65B9\u5F0F", + refresh: "\u5237\u65B0", + reselect_folder: "\u91CD\u65B0\u9009\u62E9\u4F4D\u7F6E", + go_up: "\u8FD4\u56DE\u4E0A\u7EA7\u6587\u4EF6\u5939", + no_backlinks: "\u6CA1\u6709\u53CD\u5411\u94FE\u63A5", + search: "\u641C\u7D22", + search_placeholder: "\u641C\u7D22\u5173\u952E\u5B57", + search_current_location_only: "\u4EC5\u641C\u7D22\u5F53\u524D\u4F4D\u7F6E", + cancel: "\u53D6\u6D88", + new_note: "\u65B0\u5EFA\u7B14\u8BB0", + untitled: "\u672A\u547D\u540D", + files: "\u4E2A\u6587\u4EF6", + add: "\u6DFB\u52A0", + // 视图标题 + grid_view_title: "\u7F51\u683C\u89C6\u56FE", + bookmarks_mode: "\u4E66\u7B7E", + folder_mode: "\u6587\u4EF6\u5939", + search_results: "\u641C\u7D22\u7ED3\u679C", + backlinks_mode: "\u53CD\u5411\u94FE\u63A5", + all_files_mode: "\u6240\u6709\u6587\u4EF6", + random_note_mode: "\u968F\u673A\u7B14\u8BB0", + // 排序选项 + sort_name_asc: "\u540D\u79F0 (A \u2192 Z)", + sort_name_desc: "\u540D\u79F0 (Z \u2192 A)", + sort_mtime_desc: "\u4FEE\u6539\u65F6\u95F4 (\u65B0 \u2192 \u65E7)", + sort_mtime_asc: "\u4FEE\u6539\u65F6\u95F4 (\u65E7 \u2192 \u65B0)", + sort_ctime_desc: "\u521B\u5EFA\u65F6\u95F4 (\u65B0 \u2192 \u65E7)", + sort_ctime_asc: "\u521B\u5EFA\u65F6\u95F4 (\u65E7 \u2192 \u65B0)", + sort_random: "\u968F\u673A\u6392\u5E8F", + // 设置 + grid_view_settings: "\u7F51\u683C\u89C6\u56FE\u8BBE\u7F6E", + media_files_settings: "\u5A92\u4F53\u6587\u4EF6\u8BBE\u7F6E", + show_media_files: "\u663E\u793A\u56FE\u7247\u548C\u89C6\u9891", + show_media_files_desc: + "\u5728\u7F51\u683C\u89C6\u56FE\u4E2D\u663E\u793A\u56FE\u7247\u548C\u89C6\u9891\u6587\u4EF6", + search_media_files: "\u641C\u7D22\u56FE\u7247\u548C\u89C6\u9891", + search_media_files_desc: + "\u5728\u641C\u7D22\u7ED3\u679C\u4E2D\u5305\u542B\u56FE\u7247\u548C\u89C6\u9891\u6587\u4EF6\uFF08\u4EC5\u5728\u542F\u7528\u663E\u793A\u56FE\u7247\u548C\u89C6\u9891\u65F6\u6709\u6548\uFF09", + show_video_thumbnails: "\u663E\u793A\u89C6\u9891\u7F29\u7565\u56FE", + show_video_thumbnails_desc: + "\u5728\u7F51\u683C\u89C6\u56FE\u4E2D\u663E\u793A\u89C6\u9891\u7684\u7F29\u7565\u56FE\uFF0C\u5173\u95ED\u65F6\u5C06\u663E\u793A\u64AD\u653E\u56FE\u6807", + ignored_folders: "\u5FFD\u7565\u7684\u6587\u4EF6\u5939", + ignored_folders_desc: + "\u5728\u8FD9\u91CC\u8BBE\u7F6E\u8981\u5FFD\u7565\u7684\u6587\u4EF6\u5939", + add_ignored_folder: "\u6DFB\u52A0\u5FFD\u7565\u6587\u4EF6\u5939", + no_ignored_folders: + "\u6CA1\u6709\u5FFD\u7565\u7684\u6587\u4EF6\u5939\u3002", + ignored_folder_patterns: + "\u4EE5\u5B57\u7B26\u4E32\u5FFD\u7565\u6587\u4EF6\u5939\u548C\u6587\u4EF6", + ignored_folder_patterns_desc: + "\u4F7F\u7528\u5B57\u7B26\u4E32\u6A21\u5F0F\u5FFD\u7565\u6587\u4EF6\u5939\u548C\u6587\u4EF6\uFF08\u652F\u6301\u6B63\u5219\u8868\u8FBE\u5F0F\uFF09", + add_ignored_folder_pattern: + "\u6DFB\u52A0\u5FFD\u7565\u6587\u4EF6\u5939\u6A21\u5F0F", + ignored_folder_pattern_placeholder: + "\u8F93\u5165\u6587\u4EF6\u5939\u540D\u79F0\u6216\u6B63\u5219\u8868\u8FBE\u5F0F", + no_ignored_folder_patterns: + "\u6CA1\u6709\u5FFD\u7565\u7684\u6587\u4EF6\u5939\u6A21\u5F0F\u3002", + remove: "\u79FB\u9664", + default_sort_type: "\u9ED8\u8BA4\u6392\u5E8F\u6A21\u5F0F", + default_sort_type_desc: + "\u8BBE\u7F6E\u6253\u5F00\u7F51\u683C\u89C6\u56FE\u65F6\u7684\u9ED8\u8BA4\u6392\u5E8F\u6A21\u5F0F", + grid_item_width: "\u7F51\u683C\u9879\u76EE\u5BBD\u5EA6", + grid_item_width_desc: + "\u8BBE\u7F6E\u7F51\u683C\u9879\u76EE\u7684\u5BBD\u5EA6", + grid_item_height: "\u7F51\u683C\u9879\u76EE\u9AD8\u5EA6", + grid_item_height_desc: + "\u8BBE\u7F6E\u7F51\u683C\u9879\u76EE\u7684\u9AD8\u5EA6 (\u8BBE\u4E3A0\u65F6\u4E3A\u81EA\u52A8\u8C03\u6574)", + image_area_width: "\u56FE\u7247\u533A\u57DF\u5BBD\u5EA6", + image_area_width_desc: + "\u8BBE\u7F6E\u56FE\u7247\u9884\u89C8\u533A\u57DF\u7684\u5BBD\u5EA6", + image_area_height: "\u56FE\u7247\u533A\u57DF\u9AD8\u5EA6", + image_area_height_desc: + "\u8BBE\u7F6E\u56FE\u7247\u9884\u89C8\u533A\u57DF\u7684\u9AD8\u5EA6", + title_font_size: "\u6807\u9898\u5B57\u4F53\u5927\u5C0F", + title_font_size_desc: + "\u8BBE\u7F6E\u6807\u9898\u5B57\u4F53\u7684\u5927\u5C0F", + summary_length: "\u6458\u8981\u957F\u5EA6", + summary_length_desc: "\u8BBE\u7F6E\u6458\u8981\u7684\u957F\u5EA6", + enable_file_watcher: "\u542F\u7528\u6587\u4EF6\u76D1\u63A7", + enable_file_watcher_desc: + "\u542F\u7528\u540E\u4F1A\u81EA\u52A8\u68C0\u6D4B\u6587\u4EF6\u53D8\u66F4\u5E76\u66F4\u65B0\u89C6\u56FE\uFF0C\u5173\u95ED\u540E\u9700\u624B\u52A8\u70B9\u51FB\u5237\u65B0\u6309\u94AE", + reset_to_default: "\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C", + reset_to_default_desc: + "\u5C06\u6240\u6709\u8BBE\u7F6E\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C", + settings_reset_notice: + "\u8BBE\u7F6E\u503C\u5DF2\u91CD\u7F6E\u4E3A\u9ED8\u8BA4\u503C", + ignored_folders_settings: "\u5FFD\u7565\u6587\u4EF6\u5939\u8BBE\u7F6E", + display_mode_settings: "\u663E\u793A\u6A21\u5F0F\u8BBE\u7F6E", + show_bookmarks_mode: "\u663E\u793A\u4E66\u7B7E\u6A21\u5F0F", + show_search_mode: "\u663E\u793A\u641C\u7D22\u7ED3\u679C\u6A21\u5F0F", + show_backlinks_mode: "\u663E\u793A\u53CD\u5411\u94FE\u63A5\u6A21\u5F0F", + show_all_files_mode: "\u663E\u793A\u6240\u6709\u6587\u4EF6\u6A21\u5F0F", + show_random_note_mode: "\u663E\u793A\u968F\u673A\u7B14\u8BB0\u6A21\u5F0F", + random_note_notes_only: "\u4EC5\u7B14\u8BB0", + random_note_include_media_files: "\u5305\u542B\u56FE\u7247\u89C6\u9891", + // 显示"返回上级文件夹"选项设置 + show_parent_folder_item: + "\u663E\u793A\u300C\u8FD4\u56DE\u4E0A\u7EA7\u6587\u4EF6\u5939\u300D", + show_parent_folder_item_desc: + "\u5728\u7F51\u683C\u7684\u7B2C\u4E00\u9879\u663E\u793A\u300C\u8FD4\u56DE\u4E0A\u7EA7\u6587\u4EF6\u5939\u300D\u9009\u9879", + parent_folder: "\u4E0A\u7EA7\u6587\u4EF6\u5939", + // 默认打开位置设置 + default_open_location: "\u9ED8\u8BA4\u6253\u5F00\u4F4D\u7F6E", + default_open_location_desc: + "\u8BBE\u7F6E\u7F51\u683C\u89C6\u56FE\u9ED8\u8BA4\u6253\u5F00\u7684\u4F4D\u7F6E", + open_in_left_sidebar: "\u5728\u5DE6\u4FA7\u8FB9\u680F\u6253\u5F00", + open_in_right_sidebar: "\u5728\u53F3\u4FA7\u8FB9\u680F\u6253\u5F00", + open_in_new_tab: "\u5728\u65B0\u6807\u7B7E\u9875\u6253\u5F00", + reuse_existing_leaf: + "\u91CD\u590D\u4F7F\u7528\u5DF2\u6253\u5F00\u7684\u89C6\u56FE", + reuse_existing_leaf_desc: + "\u6253\u5F00\u7F51\u683C\u89C6\u56FE\u65F6\uFF0C\u4F18\u5148\u4F7F\u7528\u5DF2\u6253\u5F00\u7684\u89C6\u56FE\u800C\u975E\u521B\u5EFA\u65B0\u89C6\u56FE", + custom_document_extensions: + "\u81EA\u5B9A\u4E49\u6587\u4EF6\u6269\u5C55\u540D", + custom_document_extensions_desc: + "\u989D\u5916\u7684\u6587\u4EF6\u6269\u5C55\u540D\uFF08\u7528\u9017\u53F7\u5206\u9694\uFF0C\u4E0D\u542B\u70B9\u53F7\uFF09", + custom_document_extensions_placeholder: "\u4F8B\u5982\uFF1Atxt,doc,docx", + // 选择文件夹对话框 + select_folders: "\u9009\u62E9\u6587\u4EF6\u5939", + open_grid_view: "\u6253\u5F00\u7F51\u683C\u89C6\u56FE", + open_note_in_grid_view: + "\u5728\u7F51\u683C\u89C6\u56FE\u4E2D\u6253\u5F00\u5F53\u524D\u7B14\u8BB0", + open_backlinks_in_grid_view: + "\u5728\u7F51\u683C\u89C6\u56FE\u4E2D\u6253\u5F00\u53CD\u5411\u94FE\u63A5", + open_settings: "\u6253\u5F00\u8BBE\u7F6E", + delete_note: "\u5220\u9664\u6587\u4EF6", + open_folder_note: "\u6253\u5F00\u6587\u4EF6\u5939\u7B14\u8BB0", + create_folder_note: "\u521B\u5EFA\u6587\u4EF6\u5939\u7B14\u8BB0", + ignore_folder: "\u5FFD\u7565\u6B64\u6587\u4EF6\u5939", + searching: "\u641C\u7D22\u4E2D...", + no_files: "\u6CA1\u6709\u627E\u5230\u4EFB\u4F55\u6587\u4EF6", + filter_folders: "\u7B5B\u9009\u6587\u4EF6\u5939...", + }, + ja: { + // 通知メッジ + bookmarks_plugin_disabled: + "\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u30D7\u30E9\u30B0\u30A4\u30F3\u3092\u6709\u52B9\u306B\u3057\u3066\u304F\u3060\u3055\u3044", + // ボタンとラベル + sorting: "\u4E26\u3073\u66FF\u3048", + refresh: "\u66F4\u65B0", + reselect_folder: "\u30D5\u30A9\u30EB\u30C0\u3092\u518D\u9078\u629E", + go_up: "\u4E0A\u306E\u968E\u5C64\u3078", + no_backlinks: "\u30D0\u30C3\u30AF\u30EA\u30F3\u30AF\u306A\u3057", + search: "\u691C\u7D22", + search_placeholder: "\u30AD\u30FC\u30EF\u30FC\u30C9\u691C\u7D22", + search_current_location_only: + "\u73FE\u5728\u306E\u5834\u6240\u306E\u307F\u691C\u7D22", + cancel: "\u30AD\u30E3\u30F3\u30BB\u30EB", + new_note: "\u65B0\u898F\u30CE\u30FC\u30C8", + untitled: "\u7121\u984C", + files: "\u30D5\u30A1\u30A4\u30EB", + add: "\u8FFD\u52A0", + // ビュータイトル + grid_view_title: "\u30B0\u30EA\u30C3\u30C9\u30D3\u30E5\u30FC", + bookmarks_mode: "\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF", + folder_mode: "\u30D5\u30A9\u30EB\u30C0", + search_results: "\u691C\u7D22\u7D50\u679C", + backlinks_mode: "\u30D0\u30C3\u30AF\u30EA\u30F3\u30AF", + all_files_mode: "\u3059\u3079\u3066\u306E\u30D5\u30A1\u30A4\u30EB", + random_note_mode: "\u30E9\u30F3\u30C0\u30E0\u30CE\u30FC\u30C8", + // 並べ替えオプション + sort_name_asc: "\u540D\u524D (A \u2192 Z)", + sort_name_desc: "\u540D\u524D (Z \u2192 A)", + sort_mtime_desc: "\u66F4\u65B0\u65E5\u6642 (\u65B0 \u2192 \u53E4)", + sort_mtime_asc: "\u66F4\u65B0\u65E5\u6642 (\u53E4 \u2192 \u65B0)", + sort_ctime_desc: "\u4F5C\u6210\u65E5\u6642 (\u65B0 \u2192 \u53E4)", + sort_ctime_asc: "\u4F5C\u6210\u65E5\u6642 (\u53E4 \u2192 \u65B0)", + sort_random: "\u30E9\u30F3\u30C0\u30E0", + // 設定 + grid_view_settings: + "\u30B0\u30EA\u30C3\u30C9\u30D3\u30E5\u30FC\u8A2D\u5B9A", + media_files_settings: + "\u30E1\u30C7\u30A3\u30A2\u30D5\u30A1\u30A4\u30EB\u8A2D\u5B9A", + show_media_files: "\u753B\u50CF\u3068\u52D5\u753B\u3092\u8868\u793A", + show_media_files_desc: + "\u30B0\u30EA\u30C3\u30C9\u30D3\u30E5\u30FC\u3067\u753B\u50CF\u3068\u52D5\u753B\u30D5\u30A1\u30A4\u30EB\u3092\u8868\u793A\u3059\u308B", + search_media_files: "\u753B\u50CF\u3068\u52D5\u753B\u3092\u691C\u7D22", + search_media_files_desc: + "\u691C\u7D22\u7D50\u679C\u306B\u753B\u50CF\u3068\u52D5\u753B\u30D5\u30A1\u30A4\u30EB\u3092\u542B\u3081\u308B\uFF08\u753B\u50CF\u3068\u52D5\u753B\u306E\u8868\u793A\u304C\u6709\u52B9\u306A\u5834\u5408\u306E\u307F\uFF09", + show_video_thumbnails: + "\u52D5\u753B\u306E\u30B5\u30E0\u30CD\u30A4\u30EB\u3092\u8868\u793A", + show_video_thumbnails_desc: + "\u30B0\u30EA\u30C3\u30C9\u30D3\u30E5\u30FC\u3067\u52D5\u753B\u306E\u30B5\u30E0\u30CD\u30A4\u30EB\u3092\u8868\u793A\u3059\u308B\u3001\u7121\u52B9\u306E\u5834\u5408\u306F\u518D\u751F\u30A2\u30A4\u30B3\u30F3\u3092\u8868\u793A", + ignored_folders: "\u7121\u8996\u3059\u308B\u30D5\u30A9\u30EB\u30C0", + ignored_folders_desc: + "\u7121\u8996\u3059\u308B\u30D5\u30A9\u30EB\u30C0\u3092\u8A2D\u5B9A\u3059\u308B", + add_ignored_folder: + "\u7121\u8996\u3059\u308B\u30D5\u30A9\u30EB\u30C0\u3092\u8FFD\u52A0", + no_ignored_folders: + "\u7121\u8996\u3059\u308B\u30D5\u30A9\u30EB\u30C0\u306F\u3042\u308A\u307E\u305B\u3093\u3002", + ignored_folder_patterns: + "\u30D1\u30BF\u30FC\u30F3\u3067\u30D5\u30A9\u30EB\u30C0\u3068\u30D5\u30A1\u30A4\u30EB\u3092\u7121\u8996", + ignored_folder_patterns_desc: + "\u6587\u5B57\u5217\u30D1\u30BF\u30FC\u30F3\u3092\u4F7F\u7528\u3057\u3066\u30D5\u30A9\u30EB\u30C0\u3068\u30D5\u30A1\u30A4\u30EB\u3092\u7121\u8996\u3059\u308B\uFF08\u6B63\u898F\u8868\u73FE\u3092\u30B5\u30DD\u30FC\u30C8\uFF09", + add_ignored_folder_pattern: + "\u30D5\u30A9\u30EB\u30C0\u30D1\u30BF\u30FC\u30F3\u3092\u8FFD\u52A0", + ignored_folder_pattern_placeholder: + "\u30D5\u30A9\u30EB\u30C0\u540D\u307E\u305F\u306F\u6B63\u898F\u8868\u73FE\u30D1\u30BF\u30FC\u30F3\u3092\u5165\u529B", + no_ignored_folder_patterns: + "\u7121\u8996\u3059\u308B\u30D5\u30A9\u30EB\u30C0\u30D1\u30BF\u30FC\u30F3\u306F\u3042\u308A\u307E\u305B\u3093\u3002", + remove: "\u524A\u9664", + default_sort_type: + "\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u4E26\u3073\u66FF\u3048", + default_sort_type_desc: + "\u30B0\u30EA\u30C3\u30C9\u30D3\u30E5\u30FC\u3092\u958B\u3044\u305F\u3068\u304D\u306E\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u4E26\u3073\u66FF\u3048\u65B9\u6CD5\u3092\u8A2D\u5B9A", + grid_item_width: + "\u30B0\u30EA\u30C3\u30C9\u30A2\u30A4\u30C6\u30E0\u306E\u5E45", + grid_item_width_desc: + "\u30B0\u30EA\u30C3\u30C9\u30A2\u30A4\u30C6\u30E0\u306E\u5E45\u3092\u8A2D\u5B9A", + grid_item_height: + "\u30B0\u30EA\u30C3\u30C9\u30A2\u30A4\u30C6\u30E0\u306E\u9AD8\u3055", + grid_item_height_desc: + "\u30B0\u30EA\u30C3\u30C9\u30A2\u30A4\u30C6\u30E0\u306E\u9AD8\u3055\u3092\u8A2D\u5B9A\uFF080\u306B\u8A2D\u5B9A\u3059\u308B\u3068\u81EA\u52D5\u8ABF\u6574\uFF09", + image_area_width: "\u753B\u50CF\u30A8\u30EA\u30A2\u306E\u5E45", + image_area_width_desc: + "\u753B\u50CF\u30D7\u30EC\u30D3\u30E5\u30FC\u30A8\u30EA\u30A2\u306E\u5E45\u3092\u8A2D\u5B9A", + image_area_height: "\u753B\u50CF\u30A8\u30EA\u30A2\u306E\u9AD8\u3055", + image_area_height_desc: + "\u753B\u50CF\u30D7\u30EC\u30D3\u30E5\u30FC\u30A8\u30EA\u30A2\u306E\u9AD8\u3055\u3092\u8A2D\u5B9A", + title_font_size: + "\u30BF\u30A4\u30C8\u30EB\u306E\u30D5\u30A9\u30F3\u30C8\u30B5\u30A4\u30BA", + title_font_size_desc: + "\u30BF\u30A4\u30C8\u30EB\u306E\u30D5\u30A9\u30F3\u30C8\u30B5\u30A4\u30BA\u3092\u8A2D\u5B9A", + summary_length: "\u8981\u7D04\u306E\u9577\u3055", + summary_length_desc: "\u8981\u7D04\u306E\u9577\u3055\u3092\u8A2D\u5B9A", + enable_file_watcher: + "\u30D5\u30A1\u30A4\u30EB\u76E3\u8996\u3092\u6709\u52B9\u306B\u3059\u308B", + enable_file_watcher_desc: + "\u6709\u52B9\u306B\u3059\u308B\u3068\u3001\u30D5\u30A1\u30A4\u30EB\u306E\u5909\u66F4\u3092\u81EA\u52D5\u7684\u306B\u691C\u51FA\u3057\u3066\u30D3\u30E5\u30FC\u3092\u66F4\u65B0\u3057\u307E\u3059\u3002\u7121\u52B9\u306E\u5834\u5408\u306F\u3001\u66F4\u65B0\u30DC\u30BF\u30F3\u3092\u624B\u52D5\u3067\u30AF\u30EA\u30C3\u30AF\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059", + reset_to_default: "\u30C7\u30D5\u30A9\u30EB\u30C8\u306B\u623B\u3059", + reset_to_default_desc: + "\u3059\u3079\u3066\u306E\u8A2D\u5B9A\u3092\u30C7\u30D5\u30A9\u30EB\u30C8\u5024\u306B\u623B", + settings_reset_notice: + "\u8A2D\u5B9A\u5024\u304C\u30C7\u30D5\u30A9\u30EB\u30C8\u5024\u306B\u30EA\u30BB\u30C3\u30C8\u3055\u308C\u307E\u3057\u305F", + ignored_folders_settings: + "\u7121\u8996\u3059\u308B\u30D5\u30A9\u30EB\u30C0\u8A2D\u5B9A", + display_mode_settings: "\u8868\u793A\u30E2\u30FC\u30C9\u8A2D\u5B9A", + show_bookmarks_mode: + "\u30D6\u30C3\u30AF\u30DE\u30FC\u30AF\u30E2\u30FC\u30C9\u3092\u8868\u793A", + show_search_mode: + "\u691C\u7D22\u7D50\u679C\u30E2\u30FC\u30C9\u3092\u8868\u793A", + show_backlinks_mode: + "\u30D0\u30C3\u30AF\u30EA\u30F3\u30AF\u30E2\u30FC\u30C9\u3092\u8868\u793A", + show_all_files_mode: + "\u5168\u30D5\u30A1\u30A4\u30EB\u30E2\u30FC\u30C9\u3092\u8868\u793A", + show_random_note_mode: + "\u30E9\u30F3\u30C0\u30E0\u30CE\u30FC\u30C8\u30E2\u30FC\u30C9\u3092\u8868\u793A", + random_note_notes_only: "\u30CE\u30FC\u30C8\u306E\u307F", + random_note_include_media_files: + "\u30E1\u30C7\u30A3\u30A2\u3092\u542B\u3080", + // 「親フォルダ」オプション設定を表示 + show_parent_folder_item: + "\u300C\u89AA\u30D5\u30A9\u30EB\u30C0\u300D\u9805\u76EE\u3092\u8868\u793A", + show_parent_folder_item_desc: + "\u30B0\u30EA\u30C3\u30C9\u306E\u6700\u521D\u306E\u9805\u76EE\u3068\u3057\u3066\u300C\u89AA\u30D5\u30A9\u30EB\u30C0\u300D\u9805\u76EE\u3092\u8868\u793A\u3057\u307E\u3059", + parent_folder: "\u89AA\u30D5\u30A9\u30EB\u30C0", + // 預設開啟位置設定 + default_open_location: + "\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u958B\u304F\u5834\u6240", + default_open_location_desc: + "\u30B0\u30EA\u30C3\u30C9\u30D3\u30E5\u30FC\u3092\u958B\u304F\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u5834\u6240\u3092\u8A2D\u5B9A", + open_in_left_sidebar: + "\u5DE6\u30B5\u30A4\u30C9\u30D0\u30FC\u3067\u958B\u304F", + open_in_right_sidebar: + "\u53F3\u30B5\u30A4\u30C9\u30D0\u30FC\u3067\u958B\u304F", + open_in_new_tab: "\u65B0\u3057\u3044\u30BF\u30D6\u3067\u958B\u304F", + reuse_existing_leaf: + "\u65E2\u5B58\u306E\u30D3\u30E5\u30FC\u3092\u518D\u5229\u7528", + reuse_existing_leaf_desc: + "\u30B0\u30EA\u30C3\u30C9\u30D3\u30E5\u30FC\u3092\u958B\u304F\u3068\u304D\u3001\u65B0\u3057\u3044\u30D3\u30E5\u30FC\u3092\u4F5C\u6210\u305B\u305A\u306B\u65E2\u5B58\u306E\u30D3\u30E5\u30FC\u3092\u512A\u5148\u4F7F\u7528", + custom_document_extensions: + "\u30AB\u30B9\u30BF\u30E0\u6587\u66F8\u62E1\u5F35\u5B50", + custom_document_extensions_desc: + "\u8FFD\u52A0\u306E\u6587\u66F8\u62E1\u5F35\u5B50\uFF08\u30AB\u30F3\u30DE\u533A\u5207\u308A\u3001\u30C9\u30C3\u30C8\u7121\u3057\uFF09", + custom_document_extensions_placeholder: "\u4F8B\uFF1Atxt,doc,docx", + // フォルダ選択ダイアログ + select_folders: "\u30D5\u30A9\u30EB\u30C0\u3092\u9078\u629E", + open_grid_view: + "\u30B0\u30EA\u30C3\u30C9\u30D3\u30E5\u30FC\u3092\u958B\u304F", + open_note_in_grid_view: + "\u30B0\u30EA\u30C3\u30C9\u30D3\u30E5\u30FC\u3067\u73FE\u5728\u306E\u30CE\u30FC\u30C8\u3092\u958B\u304F", + open_backlinks_in_grid_view: + "\u30B0\u30EA\u30C3\u30C9\u30D3\u30E5\u30FC\u3067\u30D0\u30C3\u30AF\u30EA\u30F3\u30AF\u3092\u958B\u304F", + open_settings: "\u8A2D\u5B9A\u3092\u958B\u304F", + delete_note: "\u30D5\u30A1\u30A4\u30EB\u3092\u524A\u9664", + open_folder_note: + "\u30D5\u30A9\u30EB\u30C0\u30CE\u30FC\u30C8\u3092\u958B\u304F", + create_folder_note: + "\u30D5\u30A9\u30EB\u30C0\u30CE\u30FC\u30C8\u3092\u4F5C\u6210", + ignore_folder: "\u3053\u306E\u30D5\u30A9\u30EB\u30C0\u3092\u7121\u8996", + searching: "\u691C\u7D22\u4E2D...", + no_files: + "\u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093", + filter_folders: + "\u30D5\u30A9\u30EB\u30C0\u3092\u30D5\u30A3\u30EB\u30BF\u30EA\u30F3\u30B0...", + }, +}; + +// src/FolderSelectionModal.ts +function showFolderSelectionModal(app, plugin, activeView) { + new FolderSelectionModal(app, plugin, activeView).open(); +} +var FolderSelectionModal = class extends import_obsidian.Modal { + constructor(app, plugin, activeView) { + super(app); + this.folderOptions = []; + this.selectedIndex = -1; + this.plugin = plugin; + this.activeView = activeView; + } + onOpen() { + const { contentEl } = this; + contentEl.empty(); + new import_obsidian.Setting(contentEl) + .setName(t("select_folders")) + .setHeading(); + const searchContainer = contentEl.createEl("div", { + cls: "ge-folder-search-container", + }); + this.searchInput = searchContainer.createEl("input", { + cls: "ge-folder-search-input", + attr: { + type: "text", + placeholder: t("filter_folders"), + ...(import_obsidian.Platform.isMobile && { tabindex: "1" }), + }, + }); + this.folderOptionsContainer = contentEl.createEl("div", { + cls: "ge-folder-options-container", + attr: import_obsidian.Platform.isMobile ? { tabindex: "0" } : {}, + }); + this.searchInput.addEventListener("input", () => { + const searchTerm = this.searchInput.value.toLowerCase(); + this.filterFolderOptions(searchTerm); + }); + this.searchInput.addEventListener("keydown", this.handleKeyDown.bind(this)); + if (this.plugin.settings.showBookmarksMode) { + const bookmarksPlugin = this.app.internalPlugins.plugins.bookmarks; + if (bookmarksPlugin == null ? void 0 : bookmarksPlugin.enabled) { + const bookmarkOption = this.folderOptionsContainer.createEl("div", { + cls: "ge-grid-view-folder-option ge-special-option", + text: `\u{1F4D1} ${t("bookmarks_mode")}`, + }); + bookmarkOption.addEventListener("click", () => { + if (this.activeView) { + this.activeView.setSource("bookmarks"); + } else { + this.plugin.activateView("bookmarks"); + } + this.close(); + }); + this.folderOptions.push(bookmarkOption); + } + } + if (this.plugin.settings.showSearchMode) { + const searchLeaf = this.app.workspace.getLeavesOfType("search")[0]; + if (searchLeaf) { + const searchView = searchLeaf.view; + const searchInputEl = searchView.searchComponent + ? searchView.searchComponent.inputEl + : null; + if (searchInputEl) { + if (searchInputEl.value.trim().length > 0) { + const searchOption = this.folderOptionsContainer.createEl("div", { + cls: "ge-grid-view-folder-option ge-special-option", + text: `\u{1F50D} ${t("search_results")}: ${searchInputEl.value}`, + }); + searchOption.addEventListener("click", () => { + if (this.activeView) { + this.activeView.setSource("search"); + } else { + this.plugin.activateView("search"); + } + this.close(); + }); + this.folderOptions.push(searchOption); + } + } + } + } + if (this.plugin.settings.showBacklinksMode) { + const activeFile = this.app.workspace.getActiveFile(); + if (activeFile) { + const activeFileName = activeFile ? `: ${activeFile.basename}` : ""; + const backlinksOption = this.folderOptionsContainer.createEl("div", { + cls: "ge-grid-view-folder-option ge-special-option", + text: `\u{1F517} ${t("backlinks_mode")}${activeFileName}`, + }); + backlinksOption.addEventListener("click", () => { + if (this.activeView) { + this.activeView.setSource("backlinks"); + } else { + this.plugin.activateView("backlinks"); + } + this.close(); + }); + this.folderOptions.push(backlinksOption); + } + } + if (this.plugin.settings.showAllFilesMode) { + const allFilesOption = this.folderOptionsContainer.createEl("div", { + cls: "ge-grid-view-folder-option ge-special-option", + text: `\u{1F4D4} ${t("all_files_mode")}`, + }); + allFilesOption.addEventListener("click", () => { + if (this.activeView) { + this.activeView.setSource("all-files"); + } else { + this.plugin.activateView("all-files"); + } + this.close(); + }); + this.folderOptions.push(allFilesOption); + } + if (this.plugin.settings.showRandomNoteMode) { + const randomNoteOption = this.folderOptionsContainer.createEl("div", { + cls: "ge-grid-view-folder-option ge-special-option", + text: `\u{1F3B2} ${t("random_note_mode")}`, + }); + randomNoteOption.addEventListener("click", () => { + if (this.activeView) { + this.activeView.setSource("random-note"); + } else { + this.plugin.activateView("random-note"); + } + this.close(); + }); + this.folderOptions.push(randomNoteOption); + } + const rootFolderOption = this.folderOptionsContainer.createEl("div", { + cls: "ge-grid-view-folder-option", + text: `\u{1F4C1} /`, + }); + rootFolderOption.addEventListener("click", () => { + if (this.activeView) { + this.activeView.setSource("folder", "/"); + } else { + this.plugin.activateView("folder", "/"); + } + this.close(); + }); + this.folderOptions.push(rootFolderOption); + const folders = this.app.vault + .getAllFolders() + .filter((folder) => { + return !this.plugin.settings.ignoredFolders.some( + (ignoredPath) => + folder.path === ignoredPath || + folder.path.startsWith(ignoredPath + "/") + ); + }) + .sort((a, b) => a.path.localeCompare(b.path)); + folders.forEach((folder) => { + const folderOption = this.folderOptionsContainer.createEl("div", { + cls: "ge-grid-view-folder-option", + text: `\u{1F4C1} ${folder.path || "/"}`, + }); + folderOption.addEventListener("click", () => { + if (this.activeView) { + this.activeView.setSource("folder", folder.path); + } else { + this.plugin.activateView("folder", folder.path); + } + this.close(); + }); + this.folderOptions.push(folderOption); + }); + this.folderOptions.forEach((option, index) => { + option.addEventListener("mouseenter", () => { + this.updateSelection(index); + }); + }); + } + // 處理鍵盤事件 + handleKeyDown(event) { + const visibleOptions = this.getVisibleOptions(); + if (visibleOptions.length === 0) return; + switch (event.key) { + case "ArrowDown": + event.preventDefault(); + this.moveSelection(1, visibleOptions); + break; + case "ArrowUp": + event.preventDefault(); + this.moveSelection(-1, visibleOptions); + break; + case "Enter": + event.preventDefault(); + if (this.selectedIndex >= 0) { + const selectedOption = this.folderOptions[this.selectedIndex]; + if (selectedOption && selectedOption.style.display !== "none") { + selectedOption.click(); + } + } + break; + case "Escape": + this.close(); + break; + } + } + // 移動選擇 + moveSelection(direction, visibleOptions) { + let currentVisibleIndex = -1; + if (this.selectedIndex >= 0) { + const selectedOption = this.folderOptions[this.selectedIndex]; + currentVisibleIndex = visibleOptions.indexOf(selectedOption); + } + let newVisibleIndex = currentVisibleIndex + direction; + if (newVisibleIndex < 0) { + newVisibleIndex = visibleOptions.length - 1; + } else if (newVisibleIndex >= visibleOptions.length) { + newVisibleIndex = 0; + } + if (newVisibleIndex >= 0 && newVisibleIndex < visibleOptions.length) { + const newSelectedOption = visibleOptions[newVisibleIndex]; + const newIndex = this.folderOptions.indexOf(newSelectedOption); + this.updateSelection(newIndex); + newSelectedOption.scrollIntoView({ block: "nearest" }); + } + } + // 更新選擇 + updateSelection(index) { + if ( + this.selectedIndex >= 0 && + this.selectedIndex < this.folderOptions.length + ) { + this.folderOptions[this.selectedIndex].removeClass("ge-selected-option"); + } + this.selectedIndex = index; + if ( + this.selectedIndex >= 0 && + this.selectedIndex < this.folderOptions.length + ) { + this.folderOptions[this.selectedIndex].addClass("ge-selected-option"); + } + } + // 獲取當前可見的選項 + getVisibleOptions() { + return this.folderOptions.filter( + (option) => option.style.display !== "none" + ); + } + // 篩選資料夾選項 + filterFolderOptions(searchTerm) { + let hasVisibleOptions = false; + this.folderOptions.forEach((option) => { + var _a; + const text = + ((_a = option.textContent) == null ? void 0 : _a.toLowerCase()) || ""; + if (searchTerm === "" || text.includes(searchTerm)) { + option.style.display = "block"; + hasVisibleOptions = true; + } else { + option.style.display = "none"; + } + }); + this.updateSelection(-1); + if (hasVisibleOptions) { + const visibleOptions = this.getVisibleOptions(); + if (visibleOptions.length > 0) { + const firstVisibleIndex = this.folderOptions.indexOf(visibleOptions[0]); + this.updateSelection(firstVisibleIndex); + } + } + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/mediaUtils.ts +var import_obsidian2 = require("obsidian"); +async function findFirstImageInNote(app, content) { + try { + const internalMatch = content.match( + /(?:!?\[\[(.*?\.(?:jpg|jpeg|png|gif|webp))(?:\|.*?)?\]\]|!\[(.*?)\]\(\s*(\S+?(?:\.(?:jpg|jpeg|png|gif|webp)|format=(?:jpg|jpeg|png|gif|webp))[^\s)]*)\s*(?:\s+["'][^"']*["'])?\s*\))/i + ); + if (internalMatch) { + return processMediaLink(app, internalMatch[0]); + } else { + return null; + } + } catch (error) { + console.error("Error finding image in note:", error); + return null; + } +} +function processMediaLink(app, linkText) { + const internalMatch = linkText.match(/!?\[\[(.*?)\]\]/); + if (internalMatch) { + const file = app.metadataCache.getFirstLinkpathDest(internalMatch[1], ""); + if (file) { + return app.vault.getResourcePath(file); + } + } + const markdownMatch = linkText.match(/!?\[(.*?)\]\((.*?)\)/); + if (markdownMatch) { + const url = markdownMatch[2].split(' "')[0]; + if (url.startsWith("http")) { + return url; + } else { + const file = app.metadataCache.getFirstLinkpathDest(url, ""); + if (!file) { + const fileByPath = app.vault.getAbstractFileByPath(url); + if (fileByPath instanceof import_obsidian2.TFile) { + return app.vault.getResourcePath(fileByPath); + } + } else { + return app.vault.getResourcePath(file); + } + } + } + return null; +} + +// src/MediaModal.ts +var import_obsidian3 = require("obsidian"); + +// src/fileUtils.ts +var customDocumentExtensions = []; +function updateCustomDocumentExtensions(settings) { + if (settings.customDocumentExtensions) { + customDocumentExtensions = settings.customDocumentExtensions + .split(",") + .map((ext) => ext.trim().toLowerCase()) + .filter((ext) => ext.length > 0); + } else { + customDocumentExtensions = []; + } +} +function isDocumentFile(file) { + const defaultDocumentExtensions = ["md", "pdf", "canvas"]; + const extension = file.extension.toLowerCase(); + return ( + defaultDocumentExtensions.includes(extension) || + customDocumentExtensions.includes(extension) + ); +} +function isImageFile(file) { + const imageExtensions = [ + "jpg", + "jpeg", + "png", + "gif", + "webp", + "avif", + "bmp", + "svg", + ]; + return imageExtensions.includes(file.extension.toLowerCase()); +} +function isVideoFile(file) { + const videoExtensions = ["mp4", "webm", "mov", "avi", "mkv", "ogv"]; + return videoExtensions.includes(file.extension.toLowerCase()); +} +function isAudioFile(file) { + const audioExtensions = ["flac", "m4a", "mp3", "ogg", "wav", "webm", "3gp"]; + return audioExtensions.includes(file.extension.toLowerCase()); +} +function isMediaFile(file) { + return isImageFile(file) || isVideoFile(file) || isAudioFile(file); +} + +// src/MediaModal.ts +var MediaModal = class extends import_obsidian3.Modal { + // 儲存 GridView 實例的引用 + constructor(app, file, mediaFiles, gridView) { + super(app); + this.currentMediaElement = null; + this.isZoomed = false; + this.handleWheel = null; + this.file = file; + this.mediaFiles = mediaFiles; + this.currentIndex = this.mediaFiles.findIndex((f) => f.path === file.path); + this.gridView = gridView; + this.modalEl.addClass("ge-media-modal"); + } + onOpen() { + const { contentEl } = this; + if (this.gridView) { + this.gridView.disableKeyboardNavigation(); + } + contentEl.empty(); + contentEl.style.width = "100%"; + contentEl.style.height = "100%"; + contentEl.addClass("ge-media-modal-content"); + const mediaView = contentEl.createDiv("ge-media-view"); + const closeButton = contentEl.createDiv("ge-media-close-button"); + (0, import_obsidian3.setIcon)(closeButton, "x"); + closeButton.addEventListener("click", (e) => { + e.stopPropagation(); + this.close(); + }); + const prevArea = contentEl.createDiv("ge-media-prev-area"); + const nextArea = contentEl.createDiv("ge-media-next-area"); + const mediaContainer = mediaView.createDiv("ge-media-container"); + mediaContainer.addEventListener("click", (e) => { + if (e.target === mediaContainer) { + this.close(); + } + }); + prevArea.addEventListener("click", (e) => { + e.stopPropagation(); + this.showPrevMedia(); + }); + nextArea.addEventListener("click", (e) => { + e.stopPropagation(); + this.showNextMedia(); + }); + contentEl.addEventListener("wheel", (e) => { + if (!this.isZoomed) { + e.preventDefault(); + if (e.deltaY > 0) { + this.showNextMedia(); + } else { + this.showPrevMedia(); + } + } + }); + this.scope.register(null, "ArrowLeft", () => { + this.showPrevMedia(); + return false; + }); + this.scope.register(null, "ArrowRight", () => { + this.showNextMedia(); + return false; + }); + this.showMediaAtIndex(this.currentIndex); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + if (this.handleWheel) { + const mediaView = contentEl.querySelector(".ge-media-view"); + if (mediaView) { + mediaView.removeEventListener("wheel", this.handleWheel); + } + this.handleWheel = null; + } + if (this.gridView) { + this.gridView.enableKeyboardNavigation(); + const currentFile = this.mediaFiles[this.currentIndex]; + const gridItemIndex = this.gridView.gridItems.findIndex( + (item) => item.dataset.filePath === currentFile.path + ); + if (gridItemIndex >= 0) { + this.gridView.hasKeyboardFocus = true; + this.gridView.selectItem(gridItemIndex); + } + } + } + // 顯示指定索引的媒體檔案 + showMediaAtIndex(index) { + if (index < 0 || index >= this.mediaFiles.length) return; + const { contentEl } = this; + const mediaContainer = contentEl.querySelector(".ge-media-container"); + if (!mediaContainer) return; + this.currentIndex = index; + if (this.handleWheel) { + const mediaView = contentEl.querySelector(".ge-media-view"); + if (mediaView) { + mediaView.removeEventListener("wheel", this.handleWheel); + } + this.handleWheel = null; + } + this.isZoomed = false; + const mediaFile = this.mediaFiles[index]; + if (isImageFile(mediaFile)) { + const img = document.createElement("img"); + img.className = "ge-fullscreen-image"; + img.style.display = "none"; + img.src = this.app.vault.getResourcePath(mediaFile); + img.onload = () => { + if (this.currentMediaElement) { + this.currentMediaElement.remove(); + } + this.currentMediaElement = img; + this.resetImageStyles(img); + img.style.display = ""; + }; + mediaContainer.appendChild(img); + img.addEventListener("click", (event) => { + event.stopPropagation(); + this.toggleImageZoom(img); + }); + } else if (isVideoFile(mediaFile) || isAudioFile(mediaFile)) { + if (this.currentMediaElement) { + this.currentMediaElement.remove(); + } + const video = document.createElement("video"); + video.className = "ge-fullscreen-video"; + video.controls = true; + video.autoplay = true; + video.src = this.app.vault.getResourcePath(mediaFile); + mediaContainer.appendChild(video); + this.currentMediaElement = video; + } + const oldFileNameElement = mediaContainer.querySelector( + ".ge-fullscreen-file-name" + ); + if (oldFileNameElement) { + oldFileNameElement.remove(); + } + if (isAudioFile(mediaFile)) { + const fileName = mediaFile.name; + const fileNameElement = document.createElement("div"); + fileNameElement.className = "ge-fullscreen-file-name"; + fileNameElement.textContent = fileName; + mediaContainer.appendChild(fileNameElement); + } + } + // 顯示下一個媒體檔案 + showNextMedia() { + const nextIndex = (this.currentIndex + 1) % this.mediaFiles.length; + this.showMediaAtIndex(nextIndex); + } + // 顯示上一個媒體檔案 + showPrevMedia() { + const prevIndex = + (this.currentIndex - 1 + this.mediaFiles.length) % this.mediaFiles.length; + this.showMediaAtIndex(prevIndex); + } + // 重設圖片樣式 + resetImageStyles(img) { + const mediaView = this.contentEl.querySelector(".ge-media-view"); + if (!mediaView) return; + img.style.width = "auto"; + img.style.height = "auto"; + img.style.maxWidth = "100vw"; + img.style.maxHeight = "100vh"; + img.style.position = "absolute"; + img.style.left = "50%"; + img.style.top = "50%"; + img.style.transform = "translate(-50%, -50%)"; + img.style.cursor = "zoom-in"; + mediaView.style.overflowX = "hidden"; + mediaView.style.overflowY = "hidden"; + img.onload = () => { + if (mediaView.clientWidth > mediaView.clientHeight) { + if (img.naturalHeight < mediaView.clientHeight) { + img.style.height = "100%"; + } + } else { + if (img.naturalWidth < mediaView.clientWidth) { + img.style.width = "100%"; + } + } + }; + if (img.complete) { + if (mediaView.clientWidth > mediaView.clientHeight) { + if (img.naturalHeight < mediaView.clientHeight) { + img.style.height = "100%"; + } + } else { + if (img.naturalWidth < mediaView.clientWidth) { + img.style.width = "100%"; + } + } + } + } + // 切換圖片縮放 + toggleImageZoom(img) { + const mediaView = this.contentEl.querySelector(".ge-media-view"); + if (!mediaView) return; + if (!this.isZoomed) { + if (mediaView.clientWidth > mediaView.clientHeight) { + if (img.naturalHeight < mediaView.clientHeight) { + img.style.maxWidth = "none"; + } + } else { + if (img.naturalWidth < mediaView.clientWidth) { + img.style.maxHeight = "none"; + } + } + if (img.offsetWidth < mediaView.clientWidth) { + img.style.width = "100vw"; + img.style.height = "auto"; + mediaView.style.overflowX = "hidden"; + mediaView.style.overflowY = "scroll"; + } else { + img.style.width = "auto"; + img.style.height = "100vh"; + mediaView.style.overflowX = "scroll"; + mediaView.style.overflowY = "hidden"; + this.handleWheel = (event) => { + event.preventDefault(); + mediaView.scrollLeft += event.deltaY; + }; + mediaView.addEventListener("wheel", this.handleWheel); + } + img.style.maxWidth = "none"; + img.style.maxHeight = "none"; + img.style.position = "relative"; + img.style.left = "0"; + img.style.top = "0"; + img.style.margin = "auto"; + img.style.transform = "none"; + img.style.cursor = "zoom-out"; + this.isZoomed = true; + } else { + if (this.handleWheel) { + mediaView.removeEventListener("wheel", this.handleWheel); + this.handleWheel = null; + } + this.resetImageStyles(img); + this.isZoomed = false; + } + } +}; + +// src/SearchModal.ts +var import_obsidian4 = require("obsidian"); +var import_obsidian5 = require("obsidian"); +var SearchModal = class extends import_obsidian4.Modal { + constructor(app, gridView, defaultQuery) { + super(app); + this.gridView = gridView; + this.defaultQuery = defaultQuery; + } + onOpen() { + const { contentEl } = this; + contentEl.empty(); + new import_obsidian4.Setting(contentEl).setName(t("search")).setHeading(); + if (this.gridView) { + this.gridView.disableKeyboardNavigation(); + } + const searchContainer = contentEl.createDiv("ge-search-container"); + const searchInput = searchContainer.createEl("input", { + type: "text", + value: this.defaultQuery, + placeholder: t("search_placeholder"), + }); + const clearButton = searchContainer.createDiv("ge-search-clear-button"); + clearButton.style.display = this.defaultQuery ? "flex" : "none"; + (0, import_obsidian5.setIcon)(clearButton, "x"); + searchInput.addEventListener("input", () => { + clearButton.style.display = searchInput.value ? "flex" : "none"; + }); + clearButton.addEventListener("click", () => { + searchInput.value = ""; + clearButton.style.display = "none"; + searchInput.focus(); + }); + const searchScopeContainer = contentEl.createDiv( + "ge-search-scope-container" + ); + const searchScopeCheckbox = searchScopeContainer.createEl("input", { + type: "checkbox", + cls: "ge-search-scope-checkbox", + }); + searchScopeCheckbox.checked = !this.gridView.searchAllFiles; + const searchScopeLabel = searchScopeContainer.createEl("span", { + text: t("search_current_location_only"), + cls: "ge-search-scope-label", + }); + searchScopeContainer.addEventListener("click", (e) => { + if (e.target !== searchScopeCheckbox) { + searchScopeCheckbox.checked = !searchScopeCheckbox.checked; + this.gridView.searchAllFiles = !searchScopeCheckbox.checked; + } + }); + searchScopeCheckbox.addEventListener("change", () => { + this.gridView.searchAllFiles = !searchScopeCheckbox.checked; + }); + const buttonContainer = contentEl.createDiv("ge-button-container"); + const searchButton = buttonContainer.createEl("button", { + text: t("search"), + }); + const cancelButton = buttonContainer.createEl("button", { + text: t("cancel"), + }); + const performSearch = () => { + this.gridView.searchQuery = searchInput.value; + this.gridView.searchAllFiles = !searchScopeCheckbox.checked; + this.gridView.clearSelection(); + this.gridView.render(); + this.gridView.app.workspace.requestSaveLayout(); + this.close(); + }; + searchButton.addEventListener("click", performSearch); + searchInput.addEventListener("keypress", (e) => { + if (e.key === "Enter") { + performSearch(); + } + }); + cancelButton.addEventListener("click", () => { + this.close(); + }); + searchInput.focus(); + searchInput.setSelectionRange( + searchInput.value.length, + searchInput.value.length + ); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + if (this.gridView) { + this.gridView.enableKeyboardNavigation(); + } + } +}; +function showSearchModal(app, gridView, defaultQuery = "") { + new SearchModal(app, gridView, defaultQuery).open(); +} + +// src/FileWatcher.ts +var import_obsidian6 = require("obsidian"); +var FileWatcher = class { + constructor(plugin, gridView) { + this.plugin = plugin; + this.gridView = gridView; + this.app = plugin.app; + } + registerFileWatcher() { + if (!this.plugin.settings.enableFileWatcher) { + return; + } + this.plugin.registerEvent( + this.app.vault.on("create", (file) => { + if (file instanceof import_obsidian6.TFile) { + if (this.gridView.sourceMode === "random-note") { + return; + } + if ( + this.gridView.sourceMode === "folder" && + this.gridView.sourcePath && + this.gridView.searchQuery === "" + ) { + const fileDirPath = + file.path.split("/").slice(0, -1).join("/") || "/"; + if (fileDirPath === this.gridView.sourcePath) { + this.gridView.render(); + } + } else { + this.gridView.render(); + } + } + }) + ); + this.plugin.registerEvent( + this.app.vault.on("delete", (file) => { + if (file instanceof import_obsidian6.TFile) { + if (this.gridView.sourceMode === "random-note") { + return; + } + if ( + this.gridView.sourceMode === "folder" && + this.gridView.sourcePath && + this.gridView.searchQuery === "" + ) { + const fileDirPath = + file.path.split("/").slice(0, -1).join("/") || "/"; + if (fileDirPath === this.gridView.sourcePath) { + this.gridView.render(); + } + } else { + this.gridView.render(); + } + } + }) + ); + this.plugin.registerEvent( + this.app.vault.on("rename", (file, oldPath) => { + if (file instanceof import_obsidian6.TFile) { + if (this.gridView.sourceMode === "random-note") { + return; + } + if ( + this.gridView.sourceMode === "folder" && + this.gridView.sourcePath && + this.gridView.searchQuery === "" + ) { + const fileDirPath = + file.path.split("/").slice(0, -1).join("/") || "/"; + const oldDirPath = oldPath.split("/").slice(0, -1).join("/") || "/"; + if ( + fileDirPath === this.gridView.sourcePath || + oldDirPath === this.gridView.sourcePath + ) { + this.gridView.render(); + } + } else { + this.gridView.render(); + } + } + }) + ); + this.plugin.registerEvent( + this.app.internalPlugins.plugins.bookmarks.instance.on("changed", () => { + if (this.gridView.sourceMode === "bookmarks") { + this.gridView.render(); + } + }) + ); + } +}; + +// src/GridView.ts +var GridView = class extends import_obsidian7.ItemView { + // 隨機筆記是否包含圖片和影片 + constructor(leaf, plugin) { + super(leaf); + this.selectedItemIndex = -1; + // 當前選中的項目索引 + this.gridItems = []; + // 存儲所有網格項目的引用 + this.hasKeyboardFocus = false; + // 是否有鍵盤焦點 + this.keyboardNavigationEnabled = true; + this.randomNoteIncludeMedia = false; + this.plugin = plugin; + this.containerEl.addClass("ge-grid-view-container"); + this.sourceMode = ""; + this.sourcePath = ""; + this.sortType = this.plugin.settings.defaultSortType; + this.searchQuery = ""; + this.searchAllFiles = true; + this.randomNoteIncludeMedia = this.plugin.settings.showMediaFiles; + if (this.plugin.settings.enableFileWatcher) { + this.fileWatcher = new FileWatcher(plugin, this); + this.fileWatcher.registerFileWatcher(); + } + this.registerDomEvent(document, "keydown", (event) => { + if (this.app.workspace.getActiveViewOfType(GridView) === this) { + this.handleKeyDown(event); + } + }); + } + getViewType() { + return "grid-view"; + } + getIcon() { + return "grid"; + } + getDisplayText() { + if (this.sourceMode === "") { + return t("grid_view_title"); + } else if (this.sourceMode === "bookmarks") { + return t("bookmarks_mode"); + } else if (this.sourceMode === "folder") { + return this.sourcePath; + } else if (this.sourceMode === "search") { + return t("search_results"); + } else if (this.sourceMode === "backlinks") { + return t("backlinks_mode"); + } else if (this.sourceMode === "all-files") { + return t("all_files_mode"); + } else if (this.sourceMode === "random-note") { + return t("random_note_mode"); + } else { + return ""; + } + } + setSource(mode, path = "") { + this.sourceMode = mode; + this.sourcePath = path; + this.render(); + this.app.workspace.requestSaveLayout(); + } + async getFiles() { + if (this.sourceMode === "folder" && this.sourcePath) { + const folder = this.app.vault.getAbstractFileByPath(this.sourcePath); + if (folder instanceof import_obsidian7.TFolder) { + const files = folder.children.filter((file) => { + if (!(file instanceof import_obsidian7.TFile)) return false; + if (isDocumentFile(file)) return true; + if (this.plugin.settings.showMediaFiles && isMediaFile(file)) { + return true; + } + return false; + }); + return this.sortFiles(files); + } + return []; + } else if (this.sourceMode === "search") { + const globalSearchPlugin = + this.app.internalPlugins.getPluginById("global-search"); + if (globalSearchPlugin == null ? void 0 : globalSearchPlugin.instance) { + const searchLeaf = this.app.workspace.getLeavesOfType("search")[0]; + if (searchLeaf && searchLeaf.view && searchLeaf.view.dom) { + const resultDomLookup = searchLeaf.view.dom.resultDomLookup; + if (resultDomLookup) { + const files = Array.from(resultDomLookup.keys()).filter( + (file) => file instanceof import_obsidian7.TFile + ); + return this.sortFiles(files); + } + } + } + return []; + } else if (this.sourceMode === "backlinks") { + const activeFile = this.app.workspace.getActiveFile(); + if (!activeFile) { + return []; + } + const backlinks = /* @__PURE__ */ new Set(); + const resolvedLinks = this.app.metadataCache.resolvedLinks; + for (const [sourcePath, links] of Object.entries(resolvedLinks)) { + if (Object.keys(links).includes(activeFile.path)) { + const sourceFile = this.app.vault.getAbstractFileByPath(sourcePath); + if (sourceFile) { + backlinks.add(sourceFile); + } + } + } + return this.sortFiles(this.ignoredFiles(Array.from(backlinks))); + } else if (this.sourceMode === "bookmarks") { + const bookmarksPlugin = this.app.internalPlugins.plugins.bookmarks; + if (!(bookmarksPlugin == null ? void 0 : bookmarksPlugin.enabled)) { + return []; + } + const bookmarks = bookmarksPlugin.instance.items; + const bookmarkedFiles = /* @__PURE__ */ new Set(); + const processBookmarkItem = (item) => { + if (item.type === "file") { + const file = this.app.vault.getAbstractFileByPath(item.path); + if (file instanceof import_obsidian7.TFile) { + if ( + isDocumentFile(file) || + (this.plugin.settings.showMediaFiles && isMediaFile(file)) + ) { + bookmarkedFiles.add(file); + } + } + } else if (item.type === "group" && item.items) { + item.items.forEach(processBookmarkItem); + } + }; + bookmarks.forEach(processBookmarkItem); + return Array.from(bookmarkedFiles); + } else if (this.sourceMode === "all-files") { + const allFiles = this.app.vault.getFiles(); + const filteredFiles = allFiles.filter((file) => { + if (isDocumentFile(file)) return true; + if (this.plugin.settings.showMediaFiles && isMediaFile(file)) { + return true; + } + return false; + }); + return this.sortFiles(filteredFiles); + } else if (this.sourceMode === "random-note") { + const allFiles = this.app.vault.getFiles(); + const filteredFiles = allFiles.filter((file) => { + if (isDocumentFile(file)) return true; + if (this.randomNoteIncludeMedia && isMediaFile(file)) { + return true; + } + return false; + }); + return filteredFiles.sort(() => Math.random() - 0.5); + } else { + return []; + } + } + //排序檔案 + sortFiles(files) { + if (this.sortType === "name-asc") { + return files.sort((a, b) => a.basename.localeCompare(b.basename)); + } else if (this.sortType === "name-desc") { + return files.sort((a, b) => b.basename.localeCompare(a.basename)); + } else if (this.sortType === "mtime-desc") { + return files.sort((a, b) => b.stat.mtime - a.stat.mtime); + } else if (this.sortType === "mtime-asc") { + return files.sort((a, b) => a.stat.mtime - b.stat.mtime); + } else if (this.sortType === "ctime-desc") { + return files.sort((a, b) => b.stat.ctime - a.stat.ctime); + } else if (this.sortType === "ctime-asc") { + return files.sort((a, b) => a.stat.ctime - b.stat.ctime); + } else if (this.sortType === "random") { + return files.sort(() => Math.random() - 0.5); + } else { + return files; + } + } + //忽略檔案 + ignoredFiles(files) { + return files.filter((file) => { + const isInIgnoredFolder = this.plugin.settings.ignoredFolders.some( + (folder) => file.path.startsWith(`${folder}/`) + ); + if (isInIgnoredFolder) { + return false; + } + if ( + this.plugin.settings.ignoredFolderPatterns && + this.plugin.settings.ignoredFolderPatterns.length > 0 + ) { + const matchesIgnoredPattern = + this.plugin.settings.ignoredFolderPatterns.some((pattern) => { + try { + if (/[\^\$\*\+\?\(\)\[\]\{\}\|\\]/.test(pattern)) { + const regex = new RegExp(pattern); + return regex.test(file.path); + } else { + return file.path.toLowerCase().includes(pattern.toLowerCase()); + } + } catch (error) { + return file.path.toLowerCase().includes(pattern.toLowerCase()); + } + }); + return !matchesIgnoredPattern; + } + return true; + }); + } + async render() { + const scrollContainer = this.containerEl.children[1]; + const scrollTop = scrollContainer ? scrollContainer.scrollTop : 0; + let selectedFilePath = null; + if ( + this.selectedItemIndex >= 0 && + this.selectedItemIndex < this.gridItems.length + ) { + const selectedItem = this.gridItems[this.selectedItemIndex]; + selectedFilePath = selectedItem.dataset.filePath || null; + } + this.containerEl.empty(); + const headerButtonsDiv = this.containerEl.createDiv("ge-header-buttons"); + headerButtonsDiv.addEventListener("click", (event) => { + if (event.target === headerButtonsDiv) { + event.preventDefault(); + const gridContainer = + this.containerEl.querySelector(".ge-grid-container"); + if (gridContainer) { + gridContainer.scrollTo({ + top: 0, + behavior: "smooth", + }); + } + } + }); + headerButtonsDiv.addEventListener("contextmenu", (event) => { + event.preventDefault(); + const menu = new import_obsidian7.Menu(); + menu.addItem((item) => { + item + .setTitle(t("open_settings")) + .setIcon("settings") + .onClick(() => { + this.app.setting.open(); + this.app.setting.openTabById(this.plugin.manifest.id); + }); + }); + menu.showAtMouseEvent(event); + }); + if (this.sourceMode === "folder" && this.searchQuery === "") { + const newNoteButton = headerButtonsDiv.createEl("button", { + attr: { "aria-label": t("new_note") }, + }); + newNoteButton.addEventListener("click", async () => { + let newFileName = `${t("untitled")}.md`; + let newFilePath = + this.sourcePath === "/" + ? newFileName + : `${this.sourcePath}/${newFileName}`; + let counter = 1; + while (this.app.vault.getAbstractFileByPath(newFilePath)) { + newFileName = `${t("untitled")} ${counter}.md`; + newFilePath = + 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); + } + }); + (0, import_obsidian8.setIcon)(newNoteButton, "square-pen"); + } + if ( + this.sourceMode === "folder" && + this.sourcePath !== "/" && + this.searchQuery === "" + ) { + const upButton = headerButtonsDiv.createEl("button", { + attr: { "aria-label": t("go_up") }, + }); + upButton.addEventListener("click", () => { + const parentPath = + this.sourcePath.split("/").slice(0, -1).join("/") || "/"; + this.setSource("folder", parentPath); + this.clearSelection(); + }); + (0, import_obsidian8.setIcon)(upButton, "arrow-up"); + if (import_obsidian7.Platform.isDesktop) { + upButton.addEventListener("dragover", (event) => { + event.preventDefault(); + event.dataTransfer.dropEffect = "move"; + upButton.addClass("ge-dragover"); + }); + upButton.addEventListener("dragleave", () => { + upButton.removeClass("ge-dragover"); + }); + upButton.addEventListener("drop", async (event) => { + var _a; + event.preventDefault(); + upButton.removeClass("ge-dragover"); + const filePath = + (_a = event.dataTransfer) == null + ? void 0 + : _a.getData("text/plain"); + if (!filePath) return; + const cleanedFilePath = filePath.replace(/!?\[\[(.*?)\]\]/, "$1"); + const parentPath = + this.sourcePath.split("/").slice(0, -1).join("/") || "/"; + if (!parentPath) return; + const file = this.app.vault.getAbstractFileByPath(cleanedFilePath); + const folder = this.app.vault.getAbstractFileByPath(parentPath); + if ( + file instanceof import_obsidian7.TFile && + folder instanceof import_obsidian7.TFolder + ) { + try { + const newPath = `${parentPath}/${file.name}`; + await this.app.fileManager.renameFile(file, newPath); + this.render(); + } catch (error) { + console.error( + "An error occurred while moving the file to parent folder:", + error + ); + } + } + }); + } + } + const reselectButton = headerButtonsDiv.createEl("button", { + attr: { "aria-label": t("reselect_folder") }, + }); + reselectButton.addEventListener("click", () => { + showFolderSelectionModal(this.app, this.plugin, this); + }); + (0, import_obsidian8.setIcon)(reselectButton, "folder"); + const refreshButton = headerButtonsDiv.createEl("button", { + attr: { "aria-label": t("refresh") }, + }); + refreshButton.addEventListener("click", () => { + if (this.sortType === "random") { + this.clearSelection(); + } + this.render(); + }); + (0, import_obsidian8.setIcon)(refreshButton, "refresh-ccw"); + if (this.sourceMode !== "bookmarks" && this.sourceMode !== "random-note") { + const sortButton = headerButtonsDiv.createEl("button", { + attr: { "aria-label": t("sorting") }, + }); + sortButton.addEventListener("click", (evt) => { + const menu = new import_obsidian7.Menu(); + const sortOptions = [ + { value: "name-asc", label: t("sort_name_asc"), icon: "a-arrow-up" }, + { + value: "name-desc", + label: t("sort_name_desc"), + icon: "a-arrow-down", + }, + { value: "mtime-desc", label: t("sort_mtime_desc"), icon: "clock" }, + { value: "mtime-asc", label: t("sort_mtime_asc"), icon: "clock" }, + { + value: "ctime-desc", + label: t("sort_ctime_desc"), + icon: "calendar", + }, + { value: "ctime-asc", label: t("sort_ctime_asc"), icon: "calendar" }, + { value: "random", label: t("sort_random"), icon: "dice" }, + ]; + sortOptions.forEach((option) => { + menu.addItem((item) => { + item + .setTitle(option.label) + .setIcon(option.icon) + .setChecked(this.sortType === option.value) + .onClick(() => { + this.sortType = option.value; + this.render(); + this.app.workspace.requestSaveLayout(); + }); + }); + }); + menu.showAtMouseEvent(evt); + }); + (0, import_obsidian8.setIcon)(sortButton, "arrow-up-narrow-wide"); + } + if (this.sourceMode !== "random-note") { + const searchButtonContainer = headerButtonsDiv.createDiv( + "ge-search-button-container" + ); + const searchButton = searchButtonContainer.createEl("button", { + cls: "search-button", + attr: { "aria-label": t("search") }, + }); + (0, import_obsidian8.setIcon)(searchButton, "search"); + searchButton.addEventListener("click", () => { + this.showSearchModal(); + }); + if (this.searchQuery) { + searchButton.style.display = "none"; + const searchTextContainer = searchButtonContainer.createDiv( + "ge-search-text-container" + ); + const searchText = searchTextContainer.createEl("span", { + cls: "ge-search-text", + text: this.searchQuery, + }); + searchText.style.cursor = "pointer"; + searchText.addEventListener("click", () => { + this.showSearchModal(this.searchQuery); + }); + const clearButton = searchTextContainer.createDiv("ge-clear-button"); + (0, import_obsidian8.setIcon)(clearButton, "x"); + clearButton.addEventListener("click", (e) => { + e.stopPropagation(); + this.searchQuery = ""; + this.searchAllFiles = true; + this.clearSelection(); + this.render(); + this.app.workspace.requestSaveLayout(); + }); + } + } + if ( + this.sourceMode === "random-note" && + this.plugin.settings.showMediaFiles + ) { + const randomNoteSettingsButton = headerButtonsDiv.createEl("button", { + text: this.randomNoteIncludeMedia + ? t("random_note_include_media_files") + : t("random_note_notes_only"), + }); + const menu = new import_obsidian7.Menu(); + menu.addItem((item) => { + item + .setTitle(t("random_note_notes_only")) + .setIcon(this.randomNoteIncludeMedia ? "" : "checkmark") + .onClick(async () => { + this.randomNoteIncludeMedia = false; + randomNoteSettingsButton.textContent = t("random_note_notes_only"); + this.render(); + }); + }); + menu.addItem((item) => { + item + .setTitle(t("random_note_include_media_files")) + .setIcon(this.randomNoteIncludeMedia ? "checkmark" : "") + .onClick(async () => { + this.randomNoteIncludeMedia = true; + randomNoteSettingsButton.textContent = t( + "random_note_include_media_files" + ); + this.render(); + }); + }); + randomNoteSettingsButton.addEventListener("click", (event) => { + menu.showAtMouseEvent(event); + }); + } + const contentEl = this.containerEl.createDiv("view-content"); + await this.grid_render(); + this.leaf.updateHeader(); + if (scrollContainer) { + contentEl.scrollTop = scrollTop; + } + if (selectedFilePath && this.hasKeyboardFocus) { + const newIndex = this.gridItems.findIndex( + (item) => item.dataset.filePath === selectedFilePath + ); + if (newIndex >= 0) { + this.selectItem(newIndex); + } + } + } + async grid_render() { + var _a; + const container = this.containerEl.children[1]; + container.empty(); + container.addClass("ge-grid-container"); + container.style.setProperty( + "--grid-item-width", + this.plugin.settings.gridItemWidth + "px" + ); + if (this.plugin.settings.gridItemHeight === 0) { + container.style.setProperty("--grid-item-height", "100%"); + } else { + container.style.setProperty( + "--grid-item-height", + this.plugin.settings.gridItemHeight + "px" + ); + } + container.style.setProperty( + "--image-area-width", + this.plugin.settings.imageAreaWidth + "px" + ); + container.style.setProperty( + "--image-area-height", + this.plugin.settings.imageAreaHeight + "px" + ); + container.style.setProperty( + "--title-font-size", + this.plugin.settings.titleFontSize + "em" + ); + this.gridItems = []; + if ( + this.sourceMode === "bookmarks" && + !((_a = this.app.internalPlugins.plugins.bookmarks) == null + ? void 0 + : _a.enabled) + ) { + new import_obsidian7.Notice(t("bookmarks_plugin_disabled")); + return; + } + if ( + this.sourceMode === "backlinks" && + !this.app.workspace.getActiveFile() + ) { + new import_obsidian7.Notice(t("no_backlinks")); + return; + } + if ( + this.sourceMode === "folder" && + this.searchQuery === "" && + this.plugin.settings.showParentFolderItem && + this.sourcePath !== "/" + ) { + const parentFolderEl = container.createDiv("ge-grid-item ge-folder-item"); + this.gridItems.push(parentFolderEl); + const parentPath = + this.sourcePath.split("/").slice(0, -1).join("/") || "/"; + parentFolderEl.dataset.folderPath = parentPath; + const contentArea = parentFolderEl.createDiv("ge-content-area"); + const titleContainer = contentArea.createDiv("ge-title-container"); + titleContainer.createEl("span", { + cls: "ge-title", + text: `\u{1F4C1} ..`, + }); + parentFolderEl.addEventListener("click", () => { + this.setSource("folder", parentPath); + this.clearSelection(); + }); + if (import_obsidian7.Platform.isDesktop) { + parentFolderEl.addEventListener("dragover", (event) => { + event.preventDefault(); + event.dataTransfer.dropEffect = "move"; + parentFolderEl.addClass("ge-dragover"); + }); + parentFolderEl.addEventListener("dragleave", () => { + parentFolderEl.removeClass("ge-dragover"); + }); + parentFolderEl.addEventListener("drop", async (event) => { + var _a2; + event.preventDefault(); + parentFolderEl.removeClass("ge-dragover"); + const filePath = + (_a2 = event.dataTransfer) == null + ? void 0 + : _a2.getData("text/plain"); + if (!filePath) return; + const cleanedFilePath = filePath.replace(/!?\[\[(.*?)\]\]/, "$1"); + const file = this.app.vault.getAbstractFileByPath(cleanedFilePath); + const folder = this.app.vault.getAbstractFileByPath(parentPath); + if ( + file instanceof import_obsidian7.TFile && + folder instanceof import_obsidian7.TFolder + ) { + try { + const newPath = `${parentPath}/${file.name}`; + await this.app.fileManager.renameFile(file, newPath); + this.render(); + } catch (error) { + console.error( + "An error occurred while moving the file to parent folder:", + error + ); + } + } + }); + } + } + if (this.sourceMode === "folder" && this.searchQuery === "") { + const currentFolder = this.app.vault.getAbstractFileByPath( + this.sourcePath || "/" + ); + if (currentFolder instanceof import_obsidian7.TFolder) { + const subfolders = currentFolder.children + .filter((child) => { + if (!(child instanceof import_obsidian7.TFolder)) return false; + const isInIgnoredFolders = this.plugin.settings.ignoredFolders.some( + (ignoredPath) => + child.path === ignoredPath || + child.path.startsWith(ignoredPath + "/") + ); + if (isInIgnoredFolders) { + return false; + } + if ( + this.plugin.settings.ignoredFolderPatterns && + this.plugin.settings.ignoredFolderPatterns.length > 0 + ) { + const matchesIgnoredPattern = + this.plugin.settings.ignoredFolderPatterns.some((pattern) => { + try { + if (/[\^\$\*\+\?\(\)\[\]\{\}\|\\]/.test(pattern)) { + const regex = new RegExp(pattern); + return regex.test(child.path); + } else { + return child.name + .toLowerCase() + .includes(pattern.toLowerCase()); + } + } catch (error) { + return child.name + .toLowerCase() + .includes(pattern.toLowerCase()); + } + }); + if (matchesIgnoredPattern) { + return false; + } + } + return true; + }) + .sort((a, b) => a.name.localeCompare(b.name)); + for (const folder of subfolders) { + const folderEl = container.createDiv("ge-grid-item ge-folder-item"); + this.gridItems.push(folderEl); + folderEl.dataset.folderPath = folder.path; + const contentArea = folderEl.createDiv("ge-content-area"); + const titleContainer = contentArea.createDiv("ge-title-container"); + titleContainer.createEl("span", { + cls: "ge-title", + text: `\u{1F4C1} ${folder.name}`, + }); + const notePath = `${folder.path}/${folder.name}.md`; + const noteFile = this.app.vault.getAbstractFileByPath(notePath); + if (noteFile instanceof import_obsidian7.TFile) { + const noteIcon = titleContainer.createEl("span", { + cls: "ge-note-button", + }); + (0, import_obsidian8.setIcon)(noteIcon, "panel-left-open"); + noteIcon.addEventListener("click", (e) => { + e.stopPropagation(); + this.app.workspace.getLeaf().openFile(noteFile); + }); + } + folderEl.addEventListener("click", () => { + this.setSource("folder", folder.path); + this.clearSelection(); + }); + folderEl.addEventListener("contextmenu", (event) => { + event.preventDefault(); + const menu = new import_obsidian7.Menu(); + const notePath2 = `${folder.path}/${folder.name}.md`; + let noteFile2 = this.app.vault.getAbstractFileByPath(notePath2); + if (noteFile2 instanceof import_obsidian7.TFile) { + menu.addItem((item) => { + item + .setTitle(t("open_folder_note")) + .setIcon("panel-left-open") + .onClick(() => { + this.app.workspace.getLeaf(false).openFile(noteFile2); + }); + }); + } else { + menu.addItem((item) => { + item + .setTitle(t("create_folder_note")) + .setIcon("file-cog") + .onClick(() => { + this.app.vault.create(notePath2, ""); + setTimeout(() => { + this.render(); + noteFile2 = + this.app.vault.getAbstractFileByPath(notePath2); + this.app.workspace.getLeaf(false).openFile(noteFile2); + }, 100); + }); + }); + } + menu.addItem((item) => { + item + .setTitle(t("ignore_folder")) + .setIcon("x") + .onClick(() => { + this.plugin.settings.ignoredFolders.push(folder.path); + this.plugin.saveSettings(); + this.render(); + }); + }); + menu.showAtMouseEvent(event); + }); + } + } + } + let files = []; + if (this.searchQuery && this.sourceMode !== "random-note") { + const loadingDiv = container.createDiv("ge-loading-indicator"); + loadingDiv.setText(t("searching")); + let allFiles = []; + if (this.searchAllFiles) { + allFiles = this.app.vault.getFiles(); + } else { + allFiles = await this.getFiles(); + } + const filteredFiles = allFiles.filter((file) => { + if (isDocumentFile(file)) { + return true; + } + if (isMediaFile(file)) { + return this.plugin.settings.searchMediaFiles; + } + return false; + }); + const lowerCaseSearchQuery = this.searchQuery.toLowerCase(); + await Promise.all( + filteredFiles.map(async (file) => { + const fileName = file.name.toLowerCase(); + if (fileName.includes(lowerCaseSearchQuery)) { + files.push(file); + } else if (file.extension === "md") { + const content = ( + await this.app.vault.cachedRead(file) + ).toLowerCase(); + if (content.includes(lowerCaseSearchQuery)) { + files.push(file); + } + } + }) + ); + files = this.sortFiles(files); + loadingDiv.remove(); + } else { + files = await this.getFiles(); + } + files = this.ignoredFiles(files); + if (this.sourceMode === "random-note") { + files = files.slice(0, 10); + } + if (files.length === 0) { + const noFilesDiv = container.createDiv("ge-no-files"); + noFilesDiv.setText(t("no_files")); + if (this.plugin.statusBarItem) { + this.plugin.statusBarItem.setText(""); + } + return; + } + const observer = new IntersectionObserver( + (entries, observer2) => { + entries.forEach(async (entry) => { + if (entry.isIntersecting) { + const fileEl = entry.target; + const filePath = fileEl.dataset.filePath; + if (!filePath) return; + const file = this.app.vault.getAbstractFileByPath(filePath); + if (!(file instanceof import_obsidian7.TFile)) return; + let imageUrl = ""; + const contentArea = fileEl.querySelector(".ge-content-area"); + if (!contentArea.hasAttribute("data-loaded")) { + if (file.extension === "md") { + let summaryLength = this.plugin.settings.summaryLength; + const content = await this.app.vault.cachedRead(file); + const frontMatterInfo = (0, + import_obsidian8.getFrontMatterInfo)(content); + let contentWithoutFrontmatter = ""; + if (summaryLength < 500) { + contentWithoutFrontmatter = content + .substring(frontMatterInfo.contentStart) + .slice(0, 500); + } else { + contentWithoutFrontmatter = content + .substring(frontMatterInfo.contentStart) + .slice(0, summaryLength + summaryLength); + } + let contentWithoutMediaLinks = + contentWithoutFrontmatter.replace( + /```[\s\S]*?```\n||!?(?:\[[^\]]*\]\([^)]+\)|\[\[[^\]]+\]\])/g, + "" + ); + contentWithoutMediaLinks = contentWithoutMediaLinks + .replace(/```[\s\S]*$/, "") + .trim(); + if ( + contentWithoutMediaLinks.startsWith("# ") || + contentWithoutMediaLinks.startsWith("## ") || + contentWithoutMediaLinks.startsWith("### ") + ) { + contentWithoutMediaLinks = contentWithoutMediaLinks + .split("\n") + .slice(1) + .join("\n"); + } + const preview = + contentWithoutMediaLinks.slice(0, summaryLength) + + (contentWithoutMediaLinks.length > summaryLength ? "" : ""); + contentArea.createEl("p", { text: preview.trim() }); + imageUrl = await findFirstImageInNote(this.app, content); + } else { + contentArea.createEl("p", { + text: file.extension.toUpperCase(), + }); + } + contentArea.setAttribute("data-loaded", "true"); + } + const imageArea = fileEl.querySelector(".ge-image-area"); + if (imageArea && !imageArea.hasAttribute("data-loaded")) { + if (isImageFile(file)) { + const img = imageArea.createEl("img"); + img.src = this.app.vault.getResourcePath(file); + imageArea.setAttribute("data-loaded", "true"); + } else if (isVideoFile(file)) { + if (this.plugin.settings.showVideoThumbnails) { + const video = imageArea.createEl("video"); + video.src = this.app.vault.getResourcePath(file); + } else { + const videoThumb = imageArea.createDiv("ge-video-thumbnail"); + (0, import_obsidian8.setIcon)(videoThumb, "play-circle"); + } + imageArea.setAttribute("data-loaded", "true"); + } else if (file.extension === "md") { + if (imageUrl) { + const img = imageArea.createEl("img"); + img.src = imageUrl; + imageArea.setAttribute("data-loaded", "true"); + } else { + imageArea.remove(); + } + } else { + imageArea.remove(); + } + } + observer2.unobserve(fileEl); + } + }); + }, + { + root: container, + rootMargin: "50px", + // 預先載入視窗外 50px 的內容 + threshold: 0.1, + } + ); + for (const file of files) { + const fileEl = container.createDiv("ge-grid-item"); + this.gridItems.push(fileEl); + fileEl.dataset.filePath = file.path; + const contentArea = fileEl.createDiv("ge-content-area"); + const titleContainer = contentArea.createDiv("ge-title-container"); + const extension = file.extension.toLowerCase(); + if (isImageFile(file)) { + const iconContainer = titleContainer.createDiv( + "ge-icon-container ge-img" + ); + (0, import_obsidian8.setIcon)(iconContainer, "image"); + } else if (isVideoFile(file)) { + const iconContainer = titleContainer.createDiv( + "ge-icon-container ge-video" + ); + (0, import_obsidian8.setIcon)(iconContainer, "play-circle"); + } else if (isAudioFile(file)) { + const iconContainer = titleContainer.createDiv( + "ge-icon-container ge-audio" + ); + (0, import_obsidian8.setIcon)(iconContainer, "music"); + } else if (extension === "pdf") { + const iconContainer = titleContainer.createDiv( + "ge-icon-container ge-pdf" + ); + (0, import_obsidian8.setIcon)(iconContainer, "paperclip"); + } else if (extension === "canvas") { + const iconContainer = titleContainer.createDiv( + "ge-icon-container ge-canvas" + ); + (0, import_obsidian8.setIcon)(iconContainer, "layout-dashboard"); + } else if (extension === "md" || extension === "txt") { + const iconContainer = titleContainer.createDiv("ge-icon-container"); + (0, import_obsidian8.setIcon)(iconContainer, "file-text"); + } else { + const iconContainer = titleContainer.createDiv("ge-icon-container"); + (0, import_obsidian8.setIcon)(iconContainer, "file"); + } + const titleEl = titleContainer.createEl("span", { + cls: "ge-title", + text: file.basename, + }); + titleEl.setAttribute("title", file.basename); + fileEl.createDiv("ge-image-area"); + observer.observe(fileEl); + fileEl.addEventListener("click", (event) => { + const index = this.gridItems.indexOf(fileEl); + if (index >= 0) { + this.selectItem(index); + this.hasKeyboardFocus = true; + } + if (isMediaFile(file)) { + if (isAudioFile(file)) { + this.openAudioFile(file); + } else { + this.openMediaFile(file, files); + } + } else { + if (event.ctrlKey) { + this.app.workspace.getLeaf(true).openFile(file); + } else { + this.app.workspace.getLeaf().openFile(file); + } + } + }); + fileEl.addEventListener("mousedown", (event) => { + if (event.button === 1) { + event.preventDefault(); + } + }); + fileEl.addEventListener("mouseup", (event) => { + if (event.button === 1) { + event.preventDefault(); + if (!isMediaFile(file)) { + this.app.workspace.getLeaf(true).openFile(file); + } + } + }); + if (import_obsidian7.Platform.isDesktop) { + fileEl.setAttribute("draggable", "true"); + fileEl.addEventListener("dragstart", (event) => { + var _a2; + const isMedia = isMediaFile(file); + const mdLink = isMedia ? `![[${file.path}]]` : `[[${file.path}]]`; + (_a2 = event.dataTransfer) == null + ? void 0 + : _a2.setData("text/plain", mdLink); + event.dataTransfer.effectAllowed = "all"; + fileEl.addClass("ge-dragging"); + }); + fileEl.addEventListener("dragend", () => { + fileEl.removeClass("ge-dragging"); + }); + } + fileEl.addEventListener("contextmenu", (event) => { + event.preventDefault(); + const menu = new import_obsidian7.Menu(); + this.app.workspace.trigger("file-menu", menu, file); + menu.addItem((item) => { + item + .setTitle(t("open_in_new_tab")) + .setIcon("external-link") + .onClick(() => { + this.app.workspace.getLeaf(true).openFile(file); + }); + }); + menu.addItem((item) => { + item + .setTitle(t("delete_note")) + .setIcon("trash") + .onClick(async () => { + await this.app.fileManager.trashFile(file); + }); + }); + menu.showAtMouseEvent(event); + }); + } + if (import_obsidian7.Platform.isDesktop) { + const folderItems = this.containerEl.querySelectorAll(".ge-folder-item"); + folderItems.forEach((folderItem) => { + folderItem.addEventListener("dragover", (event) => { + event.preventDefault(); + event.dataTransfer.dropEffect = "move"; + folderItem.addClass("ge-dragover"); + }); + folderItem.addEventListener("dragleave", () => { + folderItem.removeClass("ge-dragover"); + }); + folderItem.addEventListener("drop", async (event) => { + var _a2; + event.preventDefault(); + folderItem.removeClass("ge-dragover"); + const filePath = + (_a2 = event.dataTransfer) == null + ? void 0 + : _a2.getData("text/plain"); + if (!filePath) return; + const cleanedFilePath = filePath.replace(/!?\[\[(.*?)\]\]/, "$1"); + const folderPath = folderItem.dataset.folderPath; + if (!folderPath) return; + const file = this.app.vault.getAbstractFileByPath(cleanedFilePath); + const folder = this.app.vault.getAbstractFileByPath(folderPath); + if ( + file instanceof import_obsidian7.TFile && + folder instanceof import_obsidian7.TFolder + ) { + try { + const newPath = `${folderPath}/${file.name}`; + await this.app.fileManager.renameFile(file, newPath); + this.render(); + } catch (error) { + console.error("An error occurred while moving the file:", error); + } + } + }); + }); + } + if ( + this.selectedItemIndex >= 0 && + this.selectedItemIndex < this.gridItems.length && + this.hasKeyboardFocus + ) { + this.selectItem(this.selectedItemIndex); + } else if (this.gridItems.length > 0) { + this.selectItem(-1); + } + if (this.plugin.statusBarItem) { + this.plugin.statusBarItem.setText(`${files.length} ${t("files")}`); + } + } + // 處理鍵盤導航 + handleKeyDown(event) { + if (!this.keyboardNavigationEnabled || this.gridItems.length === 0) return; + const container = this.containerEl.children[1]; + const containerWidth = container.clientWidth; + const itemWidth = this.plugin.settings.gridItemWidth + 20; + const itemsPerRow = Math.max(1, Math.floor(containerWidth / itemWidth)); + let newIndex = this.selectedItemIndex; + if ( + this.selectedItemIndex === -1 && + [ + "ArrowRight", + "ArrowLeft", + "ArrowDown", + "ArrowUp", + "Home", + "End", + ].includes(event.key) + ) { + this.hasKeyboardFocus = true; + this.selectItem(0); + event.preventDefault(); + return; + } + switch (event.key) { + case "ArrowRight": + if (event.altKey) { + if ( + this.selectedItemIndex >= 0 && + this.selectedItemIndex < this.gridItems.length + ) { + this.gridItems[this.selectedItemIndex].click(); + } + } + newIndex = Math.min( + this.gridItems.length - 1, + this.selectedItemIndex + 1 + ); + this.hasKeyboardFocus = true; + event.preventDefault(); + break; + case "ArrowLeft": + if (event.altKey) { + if ( + this.sourceMode === "folder" && + this.sourcePath && + this.sourcePath !== "/" + ) { + const parentPath = + this.sourcePath.split("/").slice(0, -1).join("/") || "/"; + this.setSource("folder", parentPath); + this.clearSelection(); + event.preventDefault(); + } + break; + } + newIndex = Math.max(0, this.selectedItemIndex - 1); + this.hasKeyboardFocus = true; + event.preventDefault(); + break; + case "ArrowDown": + newIndex = Math.min( + this.gridItems.length - 1, + this.selectedItemIndex + itemsPerRow + ); + this.hasKeyboardFocus = true; + event.preventDefault(); + break; + case "ArrowUp": + if (event.altKey) { + if ( + this.sourceMode === "folder" && + this.sourcePath && + this.sourcePath !== "/" + ) { + const parentPath = + this.sourcePath.split("/").slice(0, -1).join("/") || "/"; + this.setSource("folder", parentPath); + this.clearSelection(); + event.preventDefault(); + } + break; + } + newIndex = Math.max(0, this.selectedItemIndex - itemsPerRow); + this.hasKeyboardFocus = true; + event.preventDefault(); + break; + case "Home": + newIndex = 0; + this.hasKeyboardFocus = true; + event.preventDefault(); + break; + case "End": + newIndex = this.gridItems.length - 1; + this.hasKeyboardFocus = true; + event.preventDefault(); + break; + case "Enter": + if ( + this.selectedItemIndex >= 0 && + this.selectedItemIndex < this.gridItems.length + ) { + this.gridItems[this.selectedItemIndex].click(); + } + this.clearSelection(); + event.preventDefault(); + break; + case "Backspace": + if ( + this.sourceMode === "folder" && + this.sourcePath && + this.sourcePath !== "/" + ) { + const parentPath = + this.sourcePath.split("/").slice(0, -1).join("/") || "/"; + this.setSource("folder", parentPath); + this.clearSelection(); + event.preventDefault(); + } + break; + case "Escape": + if (this.selectedItemIndex >= 0) { + this.hasKeyboardFocus = false; + this.clearSelection(); + event.preventDefault(); + } + break; + } + if (newIndex !== this.selectedItemIndex) { + this.selectItem(newIndex); + } + } + // 清除選中狀態 + clearSelection() { + this.gridItems.forEach((item) => { + item.removeClass("ge-selected-item"); + }); + this.selectedItemIndex = -1; + } + // 選中指定索引的項目 + selectItem(index) { + this.gridItems.forEach((item) => { + item.removeClass("ge-selected-item"); + }); + if (index >= 0 && index < this.gridItems.length) { + this.selectedItemIndex = index; + const selectedItem = this.gridItems[index]; + selectedItem.addClass("ge-selected-item"); + selectedItem.scrollIntoView({ + behavior: "smooth", + block: "nearest", + }); + } + } + // 開啟媒體檔案 + openMediaFile(file, mediaFiles) { + const getMediaFilesPromise = mediaFiles + ? Promise.resolve(mediaFiles.filter((f) => isMediaFile(f))) + : this.getFiles().then((allFiles) => + allFiles.filter((f) => isMediaFile(f)) + ); + getMediaFilesPromise.then((filteredMediaFiles) => { + const currentIndex = filteredMediaFiles.findIndex( + (f) => f.path === file.path + ); + if (currentIndex === -1) return; + const mediaModal = new MediaModal( + this.app, + file, + filteredMediaFiles, + this + ); + mediaModal.open(); + }); + } + // 開啟音樂檔案 + openAudioFile(file) { + const existingPlayers = document.querySelectorAll( + ".ge-floating-audio-player" + ); + existingPlayers.forEach((player) => player.remove()); + const audioPlayerContainer = document.createElement("div"); + audioPlayerContainer.className = "ge-floating-audio-player"; + const audio = document.createElement("audio"); + audio.controls = true; + audio.src = this.app.vault.getResourcePath(file); + const titleElement = document.createElement("div"); + titleElement.className = "ge-audio-title"; + titleElement.textContent = file.basename; + const closeButton = document.createElement("div"); + closeButton.className = "ge-audio-close-button"; + (0, import_obsidian8.setIcon)(closeButton, "x"); + closeButton.addEventListener("click", () => { + audioPlayerContainer.remove(); + }); + const handleElement = document.createElement("div"); + handleElement.className = "ge-audio-handle"; + audioPlayerContainer.appendChild(handleElement); + audioPlayerContainer.appendChild(titleElement); + audioPlayerContainer.appendChild(audio); + audioPlayerContainer.appendChild(closeButton); + let isDragging = false; + let offsetX = 0; + let offsetY = 0; + let isTouchEvent = false; + handleElement.addEventListener("mousedown", (e) => { + if (isTouchEvent) return; + isDragging = true; + offsetX = e.clientX - audioPlayerContainer.getBoundingClientRect().left; + offsetY = e.clientY - audioPlayerContainer.getBoundingClientRect().top; + audioPlayerContainer.classList.add("ge-audio-dragging"); + }); + handleElement.addEventListener( + "touchstart", + (e) => { + isTouchEvent = true; + isDragging = true; + const touch = e.touches[0]; + offsetX = + touch.clientX - audioPlayerContainer.getBoundingClientRect().left; + offsetY = + touch.clientY - audioPlayerContainer.getBoundingClientRect().top; + audioPlayerContainer.classList.add("ge-audio-dragging"); + }, + { passive: true } + ); + document.addEventListener("mousemove", (e) => { + if (!isDragging || isTouchEvent) return; + const x = e.clientX - offsetX; + const y = e.clientY - offsetY; + audioPlayerContainer.style.left = `${x}px`; + audioPlayerContainer.style.top = `${y}px`; + }); + document.addEventListener( + "touchmove", + (e) => { + if (!isDragging) return; + const touch = e.touches[0]; + const x = touch.clientX - offsetX; + const y = touch.clientY - offsetY; + audioPlayerContainer.style.left = `${x}px`; + audioPlayerContainer.style.top = `${y}px`; + e.preventDefault(); + }, + { passive: false } + ); + document.addEventListener("mouseup", () => { + if (isTouchEvent) return; + isDragging = false; + audioPlayerContainer.classList.remove("ge-audio-dragging"); + }); + document.addEventListener("touchend", () => { + isDragging = false; + isTouchEvent = false; + audioPlayerContainer.classList.remove("ge-audio-dragging"); + }); + document.body.appendChild(audioPlayerContainer); + const rect = audioPlayerContainer.getBoundingClientRect(); + audioPlayerContainer.style.left = `${ + window.innerWidth - rect.width - 20 + }px`; + audioPlayerContainer.style.top = `${ + window.innerHeight - rect.height - 20 + }px`; + audio.play(); + } + // 顯示搜尋 modal + showSearchModal(defaultQuery = "") { + showSearchModal(this.app, this, defaultQuery); + } + // 保存視圖狀態 + getState() { + return { + type: "grid-view", + state: { + sourceMode: this.sourceMode, + sourcePath: this.sourcePath, + sortType: this.sortType, + searchQuery: this.searchQuery, + searchAllFiles: this.searchAllFiles, + randomNoteIncludeMedia: this.randomNoteIncludeMedia, + }, + }; + } + // 讀取視圖狀態 + async setState(state) { + var _a, _b; + if (state.state) { + this.sourceMode = state.state.sourceMode || ""; + this.sourcePath = state.state.sourcePath || null; + this.sortType = state.state.sortType || "mtime-desc"; + this.searchQuery = state.state.searchQuery || ""; + this.searchAllFiles = + (_a = state.state.searchAllFiles) != null ? _a : true; + this.randomNoteIncludeMedia = + (_b = state.state.randomNoteIncludeMedia) != null + ? _b + : this.plugin.settings.showMediaFiles; + this.render(); + } + } + // 禁用鍵盤導航 + disableKeyboardNavigation() { + this.keyboardNavigationEnabled = false; + } + // 啟用鍵盤導航 + enableKeyboardNavigation() { + this.keyboardNavigationEnabled = true; + } +}; + +// src/settings.ts +var import_obsidian9 = require("obsidian"); +var DEFAULT_SETTINGS = { + ignoredFolders: [], + ignoredFolderPatterns: [], + // 預設以字串忽略的資料夾模式 + defaultSortType: "mtime-desc", + // 預設排序模式:修改時間倒序 + gridItemWidth: 300, + // 網格項目寬度,預設 300 + gridItemHeight: 0, + // 網格項目高度,預設 0 + imageAreaWidth: 100, + // 圖片區域寬度,預設 100 + imageAreaHeight: 100, + // 圖片區域高度,預設 100 + titleFontSize: 1, + // 筆記標題的字型大小,預設 1.0 + summaryLength: 100, + // 筆記摘要的字數,預設 100 + enableFileWatcher: true, + // 預設啟用檔案監控 + showMediaFiles: true, + // 預設顯示圖片和影片 + searchMediaFiles: false, + // 預設搜尋時也包含圖片和影片 + showVideoThumbnails: false, + // 預設不顯示影片縮圖 + defaultOpenLocation: "tab", + // 預設開啟位置:新分頁 + showParentFolderItem: false, + // 預設不顯示"返回上级文件夹"選項 + reuseExistingLeaf: false, + // 預設不重用現有的網格視圖 + showBookmarksMode: true, + // 預設顯示書籤模式 + showSearchMode: true, + // 預設顯示搜尋結果模式 + showBacklinksMode: true, + // 預設顯示反向連結模式 + showAllFilesMode: false, + // 預設不顯示所有檔案模式 + showRandomNoteMode: false, + // 預設不顯示隨機筆記模式 + customDocumentExtensions: "", + // 自訂文件副檔名(用逗號分隔) +}; +var GridExplorerSettingTab = class extends import_obsidian9.PluginSettingTab { + constructor(app, plugin) { + super(app, plugin); + this.plugin = plugin; + } + display() { + const { containerEl } = this; + containerEl.empty(); + new import_obsidian9.Setting(containerEl) + .setName(t("reset_to_default")) + .setDesc(t("reset_to_default_desc")) + .addButton((button) => + button.setButtonText(t("reset")).onClick(async () => { + this.plugin.settings = { ...DEFAULT_SETTINGS }; + await this.plugin.saveSettings(); + this.display(); + new import_obsidian9.Notice(t("settings_reset_notice")); + }) + ); + containerEl.createEl("h3", { text: t("display_mode_settings") }); + new import_obsidian9.Setting(containerEl) + .setName(t("show_bookmarks_mode")) + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.showBookmarksMode) + .onChange(async (value) => { + this.plugin.settings.showBookmarksMode = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("show_search_mode")) + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.showSearchMode) + .onChange(async (value) => { + this.plugin.settings.showSearchMode = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("show_backlinks_mode")) + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.showBacklinksMode) + .onChange(async (value) => { + this.plugin.settings.showBacklinksMode = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("show_all_files_mode")) + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.showAllFilesMode) + .onChange(async (value) => { + this.plugin.settings.showAllFilesMode = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("show_random_note_mode")) + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.showRandomNoteMode) + .onChange(async (value) => { + this.plugin.settings.showRandomNoteMode = value; + await this.plugin.saveSettings(); + }); + }); + containerEl.createEl("h3", { text: t("media_files_settings") }); + new import_obsidian9.Setting(containerEl) + .setName(t("show_media_files")) + .setDesc(t("show_media_files_desc")) + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.showMediaFiles) + .onChange(async (value) => { + this.plugin.settings.showMediaFiles = value; + if (!value) { + this.plugin.settings.searchMediaFiles = false; + this.display(); + } + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("search_media_files")) + .setDesc(t("search_media_files_desc")) + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.searchMediaFiles) + .onChange(async (value) => { + this.plugin.settings.searchMediaFiles = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("show_video_thumbnails")) + .setDesc(t("show_video_thumbnails_desc")) + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.showVideoThumbnails) + .onChange(async (value) => { + this.plugin.settings.showVideoThumbnails = value; + await this.plugin.saveSettings(); + }); + }); + containerEl.createEl("h3", { text: t("grid_view_settings") }); + new import_obsidian9.Setting(containerEl) + .setName(t("reuse_existing_leaf")) + .setDesc(t("reuse_existing_leaf_desc")) + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.reuseExistingLeaf) + .onChange(async (value) => { + this.plugin.settings.reuseExistingLeaf = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("default_open_location")) + .setDesc(t("default_open_location_desc")) + .addDropdown((dropdown) => { + dropdown + .addOption("tab", t("open_in_new_tab")) + .addOption("left", t("open_in_left_sidebar")) + .addOption("right", t("open_in_right_sidebar")) + .setValue(this.plugin.settings.defaultOpenLocation) + .onChange(async (value) => { + this.plugin.settings.defaultOpenLocation = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("default_sort_type")) + .setDesc(t("default_sort_type_desc")) + .addDropdown((dropdown) => { + dropdown + .addOption("name-asc", t("sort_name_asc")) + .addOption("name-desc", t("sort_name_desc")) + .addOption("mtime-desc", t("sort_mtime_desc")) + .addOption("mtime-asc", t("sort_mtime_asc")) + .addOption("ctime-desc", t("sort_ctime_desc")) + .addOption("ctime-asc", t("sort_ctime_asc")) + .addOption("random", t("sort_random")) + .setValue(this.plugin.settings.defaultSortType) + .onChange(async (value) => { + this.plugin.settings.defaultSortType = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("enable_file_watcher")) + .setDesc(t("enable_file_watcher_desc")) + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.enableFileWatcher) + .onChange(async (value) => { + this.plugin.settings.enableFileWatcher = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("custom_document_extensions")) + .setDesc(t("custom_document_extensions_desc")) + .addText((text) => { + text + .setPlaceholder(t("custom_document_extensions_placeholder")) + .setValue(this.plugin.settings.customDocumentExtensions) + .onChange(async (value) => { + this.plugin.settings.customDocumentExtensions = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("show_parent_folder_item")) + .setDesc(t("show_parent_folder_item_desc")) + .addToggle((toggle) => { + toggle + .setValue(this.plugin.settings.showParentFolderItem) + .onChange(async (value) => { + this.plugin.settings.showParentFolderItem = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("grid_item_width")) + .setDesc(t("grid_item_width_desc")) + .addSlider((slider) => { + slider + .setLimits(0, 600, 10) + .setValue(this.plugin.settings.gridItemWidth) + .setDynamicTooltip() + .onChange(async (value) => { + this.plugin.settings.gridItemWidth = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("grid_item_height")) + .setDesc(t("grid_item_height_desc")) + .addSlider((slider) => { + slider + .setLimits(0, 600, 10) + .setValue(this.plugin.settings.gridItemHeight) + .setDynamicTooltip() + .onChange(async (value) => { + this.plugin.settings.gridItemHeight = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("image_area_width")) + .setDesc(t("image_area_width_desc")) + .addSlider((slider) => { + slider + .setLimits(0, 300, 10) + .setValue(this.plugin.settings.imageAreaWidth) + .setDynamicTooltip() + .onChange(async (value) => { + this.plugin.settings.imageAreaWidth = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("image_area_height")) + .setDesc(t("image_area_height_desc")) + .addSlider((slider) => { + slider + .setLimits(0, 300, 10) + .setValue(this.plugin.settings.imageAreaHeight) + .setDynamicTooltip() + .onChange(async (value) => { + this.plugin.settings.imageAreaHeight = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("title_font_size")) + .setDesc(t("title_font_size_desc")) + .addSlider((slider) => { + slider + .setLimits(0.1, 1.5, 0.05) + .setValue(this.plugin.settings.titleFontSize) + .setDynamicTooltip() + .onChange(async (value) => { + this.plugin.settings.titleFontSize = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian9.Setting(containerEl) + .setName(t("summary_length")) + .setDesc(t("summary_length_desc")) + .addSlider((slider) => { + slider + .setLimits(0, 600, 25) + .setValue(this.plugin.settings.summaryLength) + .setDynamicTooltip() + .onChange(async (value) => { + this.plugin.settings.summaryLength = value; + await this.plugin.saveSettings(); + }); + }); + containerEl.createEl("h3", { text: t("ignored_folders_settings") }); + const ignoredFoldersContainer = containerEl.createDiv( + "ignored-folders-container" + ); + new import_obsidian9.Setting(containerEl) + .setName(t("ignored_folders")) + .setDesc(t("ignored_folders_desc")) + .setHeading(); + new import_obsidian9.Setting(ignoredFoldersContainer) + .setName(t("add_ignored_folder")) + .addDropdown((dropdown) => { + const folders = this.app.vault + .getAllFolders() + .filter((folder) => folder.path !== "/") + .sort((a, b) => a.path.localeCompare(b.path)); + dropdown.addOption("", t("select_folders")); + folders.forEach((folder) => { + const isIgnored = this.plugin.settings.ignoredFolders.some( + (ignoredPath) => + folder.path === ignoredPath || + folder.path.startsWith(ignoredPath + "/") + ); + if (!isIgnored) { + dropdown.addOption(folder.path, folder.path); + } + }); + dropdown.onChange(async (value) => { + if (value) { + this.plugin.settings.ignoredFolders.push(value); + await this.plugin.saveSettings(); + this.renderIgnoredFoldersList(ignoredFoldersList); + dropdown.setValue(""); + this.display(); + } + }); + }); + const ignoredFoldersList = ignoredFoldersContainer.createDiv( + "ge-ignored-folders-list" + ); + this.renderIgnoredFoldersList(ignoredFoldersList); + containerEl.appendChild(ignoredFoldersContainer); + const ignoredFolderPatternsContainer = containerEl.createDiv( + "ignored-folder-patterns-container" + ); + new import_obsidian9.Setting(containerEl) + .setName(t("ignored_folder_patterns")) + .setDesc(t("ignored_folder_patterns_desc")) + .setHeading(); + const patternSetting = new import_obsidian9.Setting( + ignoredFolderPatternsContainer + ) + .setName(t("add_ignored_folder_pattern")) + .addText((text) => { + text + .setPlaceholder(t("ignored_folder_pattern_placeholder")) + .onChange(() => {}); + return text; + }); + patternSetting.addButton((button) => { + button + .setButtonText(t("add")) + .setCta() + .onClick(async () => { + const inputEl = patternSetting.controlEl.querySelector("input"); + const pattern = inputEl.value.trim(); + if ( + pattern && + !this.plugin.settings.ignoredFolderPatterns.includes(pattern) + ) { + this.plugin.settings.ignoredFolderPatterns.push(pattern); + await this.plugin.saveSettings(); + this.renderIgnoredFolderPatternsList(ignoredFolderPatternsList); + inputEl.value = ""; + } + }); + }); + const ignoredFolderPatternsList = ignoredFolderPatternsContainer.createDiv( + "ge-ignored-folder-patterns-list" + ); + this.renderIgnoredFolderPatternsList(ignoredFolderPatternsList); + containerEl.appendChild(ignoredFolderPatternsContainer); + } + // 渲染已忽略的資料夾列表 + renderIgnoredFoldersList(containerEl) { + containerEl.empty(); + if (this.plugin.settings.ignoredFolders.length === 0) { + containerEl.createEl("p", { text: t("no_ignored_folders") }); + return; + } + const list = containerEl.createEl("ul", { cls: "ge-ignored-folders-list" }); + this.plugin.settings.ignoredFolders.forEach((folder) => { + const item = list.createEl("li", { cls: "ge-ignored-folder-item" }); + item.createSpan({ text: folder, cls: "ge-ignored-folder-path" }); + const removeButton = item.createEl("button", { + cls: "ge-ignored-folder-remove", + text: t("remove"), + }); + removeButton.addEventListener("click", async () => { + this.plugin.settings.ignoredFolders = + this.plugin.settings.ignoredFolders.filter((f) => f !== folder); + await this.plugin.saveSettings(); + this.renderIgnoredFoldersList(containerEl); + this.display(); + }); + }); + } + // 渲染已忽略的資料夾模式列表 + renderIgnoredFolderPatternsList(containerEl) { + containerEl.empty(); + if (this.plugin.settings.ignoredFolderPatterns.length === 0) { + containerEl.createEl("p", { text: t("no_ignored_folder_patterns") }); + return; + } + const list = containerEl.createEl("ul", { cls: "ge-ignored-folders-list" }); + this.plugin.settings.ignoredFolderPatterns.forEach((pattern) => { + const item = list.createEl("li", { cls: "ge-ignored-folder-item" }); + item.createSpan({ text: pattern, cls: "ge-ignored-folder-path" }); + const removeButton = item.createEl("button", { + cls: "ge-ignored-folder-remove", + text: t("remove"), + }); + removeButton.addEventListener("click", async () => { + this.plugin.settings.ignoredFolderPatterns = + this.plugin.settings.ignoredFolderPatterns.filter( + (p) => p !== pattern + ); + await this.plugin.saveSettings(); + this.renderIgnoredFolderPatternsList(containerEl); + }); + }); + } +}; + +// main.ts +var GridExplorerPlugin = class extends import_obsidian10.Plugin { + async onload() { + await this.loadSettings(); + this.registerView("grid-view", (leaf) => new GridView(leaf, this)); + this.addSettingTab(new GridExplorerSettingTab(this.app, this)); + this.addCommand({ + id: "open-grid-view", + name: t("open_grid_view"), + callback: () => { + showFolderSelectionModal(this.app, this); + }, + }); + this.addCommand({ + id: "view-current-note-in-grid-view", + name: t("open_note_in_grid_view"), + callback: () => { + const activeFile = this.app.workspace.getActiveFile(); + if (activeFile) { + this.openInGridView(activeFile); + } else { + this.openInGridView(this.app.vault.getRoot()); + } + }, + }); + this.addCommand({ + id: "view-backlinks-in-grid-view", + name: t("open_backlinks_in_grid_view"), + callback: () => { + const activeFile = this.app.workspace.getActiveFile(); + if (activeFile) { + this.activateView("backlinks"); + } else { + this.openInGridView(this.app.vault.getRoot()); + } + }, + }); + this.addRibbonIcon("grid", t("open_grid_view"), () => { + showFolderSelectionModal(this.app, this); + }); + this.statusBarItem = this.addStatusBarItem(); + this.statusBarItem.onClickEvent(() => { + this.activateView(); + }); + this.registerEvent( + this.app.workspace.on("file-menu", (menu, file) => { + if ( + file instanceof import_obsidian10.TFolder || + file instanceof import_obsidian10.TFile + ) { + menu.addItem((item) => { + item + .setTitle(t("open_note_in_grid_view")) + .setIcon("grid") + .onClick(() => { + this.openInGridView(file); + }); + }); + if (this.settings.showBacklinksMode) { + menu.addItem((item) => { + item + .setTitle(t("open_backlinks_in_grid_view")) + .setIcon("grid") + .onClick(() => { + this.activateView("backlinks"); + }); + }); + } + } + }) + ); + } + async openInGridView(file = this.app.vault.getRoot()) { + var _a; + const folderPath = file + ? file instanceof import_obsidian10.TFile + ? (_a = file.parent) == null + ? void 0 + : _a.path + : file.path + : "/"; + const view = await this.activateView("folder", folderPath); + if (file instanceof import_obsidian10.TFile) { + setTimeout(() => { + const gridContainer = + view.containerEl.querySelector(".ge-grid-container"); + if (!gridContainer) return; + const gridItem = Array.from( + gridContainer.querySelectorAll(".ge-grid-item") + ).find((item) => item.dataset.filePath === file.path); + if (gridItem) { + gridItem.scrollIntoView({ behavior: "smooth", block: "center" }); + const itemIndex = view.gridItems.indexOf(gridItem); + if (itemIndex >= 0) { + view.selectItem(itemIndex); + } + } + }, 100); + } + } + async activateView(mode = "bookmarks", path = "") { + const { workspace } = this.app; + let leaf = null; + const leaves = workspace.getLeavesOfType("grid-view"); + if (this.settings.reuseExistingLeaf && leaves.length > 0) { + leaf = leaves[0]; + } else { + switch (this.settings.defaultOpenLocation) { + case "left": + leaf = workspace.getLeftLeaf(false); + break; + case "right": + leaf = workspace.getRightLeaf(false); + break; + case "tab": + default: + leaf = workspace.getLeaf("tab"); + break; + } + } + if (!leaf) { + leaf = workspace.getLeaf("tab"); + } + await leaf.setViewState({ type: "grid-view", active: true }); + if (leaf.view instanceof GridView) { + leaf.view.setSource(mode, path); + } + workspace.revealLeaf(leaf); + return leaf.view; + } + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + updateCustomDocumentExtensions(this.settings); + } + async saveSettings() { + await this.saveData(this.settings); + updateCustomDocumentExtensions(this.settings); + const leaves = this.app.workspace.getLeavesOfType("grid-view"); + leaves.forEach((leaf) => { + if (leaf.view instanceof GridView) { + leaf.view.render(); + } + }); + } +}; + +/* nosourcemap */ diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..c105806 --- /dev/null +++ b/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "gridexplorer", + "name": "GridExplorer", + "version": "1.9.4", + "minAppVersion": "1.1.0", + "description": "Browse note files in a grid view.", + "author": "Devon22", + "authorUrl": "https://github.com/Devon22", + "isDesktopOnly": false +} \ No newline at end of file diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..72ac057 --- /dev/null +++ b/styles.css @@ -0,0 +1,800 @@ +.ge-grid-view-container { + padding: 0px; + height: 100%; +} + +/* 選擇資料夾的樣式 */ +.ge-grid-view-folder-option { + cursor: pointer; + padding: 8px; + margin-bottom: 8px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; +} + +.ge-grid-view-folder-option:hover { + background-color: var(--background-modifier-hover); +} + +/* Grid 樣式 */ +.ge-grid-container { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(var(--grid-item-width, 300px), 1fr)); + gap: 12px; + padding: 12px !important; + align-items: start; + align-content: start; + background: var(--background-secondary) !important; + flex: 1; + overflow-y: auto; +} + +.is-mobile .ge-grid-container::-webkit-scrollbar { + display: none !important; + width: 0 !important; +} + +.ge-grid-item { + background-color: var(--background-primary); + border: 1px solid var(--background-modifier-border); + border-radius: var(--button-radius); + padding: 12px; + cursor: pointer; + transition: transform 0.2s, box-shadow 0.2s; + display: flex; + gap: 14px; + height: var(--grid-item-height); +} + +.ge-grid-item:hover { + transform: translateY(-2px); + background-color: var(--text-selection); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.ge-content-area { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +.ge-title-container { + display: flex; + align-items: center; + gap: 0px; + width: 100%; +} + +.ge-icon-container { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: 18px; + height: 18px; + color: var(--text-muted); +} + +.ge-icon-container.ge-img { + color: var(--color-blue); +} + +.ge-icon-container.ge-video { + color: var(--color-red); +} + +.ge-icon-container.ge-audio { + color: var(--color-purple); +} + +.ge-icon-container.ge-pdf { + color: var(--color-orange); +} + +.ge-icon-container.ge-canvas { + color: var(--color-green); +} + +.ge-title { + margin: 0 0 0 3px; + font-size: var(--title-font-size); + color: var(--text-normal); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + display: block; + width: 100%; +} + +.ge-note-button { + color: var(--text-muted); + cursor: pointer; + margin-left: 8px; + opacity: 0.7; + transition: opacity 0.2s ease; + background: none; + border: none; + padding: 0; + display: flex; + align-items: center; +} + +.ge-note-button:hover { + opacity: 1; + color: var(--text-accent); +} + +.ge-note-button svg { + width: 18px; + height: 18px; +} + +.ge-content-area p { + margin: 8px 0 0 0; + color: var(--text-faint); + font-size: 0.85em; + line-height: 1.4; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + height: 100%; +} + +.ge-content-area p:empty { + margin: 0; +} + +.ge-image-area { + width: var(--image-area-width); + height: var(--image-area-height); + flex-shrink: 0; +} + +.ge-image-area img { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 4px; +} + +.ge-image-area video { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 4px; +} + +/* 資料夾項目的特殊樣式 */ +.ge-grid-item.ge-folder-item { + background-color: var(--background-primary-alt); + border: 2px solid var(--background-modifier-border); + padding: 7px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + transition: all 0.2s ease; + height: 100%; +} + +.ge-grid-item.ge-folder-item:hover { + background-color: var(--background-modifier-hover); + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.ge-grid-item.ge-folder-item .ge-icon-container { + color: var(--interactive-accent); +} + +.ge-grid-item.ge-folder-item .ge-title { + color: var(--text-normal); +} + +/* 上層資料夾的特殊樣式 */ +.ge-grid-item.parent-folder { + background-color: var(--background-secondary-alt); +} + +.ge-grid-item.parent-folder:hover { + background-color: var(--background-modifier-hover); +} + +/* 調整資料夾項目的高度 */ +.ge-grid-item.ge-folder-item .ge-content-area { + min-height: 0px; + justify-content: center; +} + +.ge-grid-item.ge-folder-item .ge-title-container { + align-items: center; +} + +/* 頂部按鈕區域樣式 */ +.ge-header-buttons { + display: flex; + gap: 8px; + padding: 12px 16px; + background: var(--background-primary); + border-bottom: 1px solid var(--background-modifier-border); + flex-shrink: 0; + justify-content: center; +} + +.ge-header-buttons button { + display: flex; + align-items: center; + gap: 4px; + padding: 6px 12px; + background-color: var(--interactive-normal); + border-radius: var(--button-radius); + color: var(--text-normal); + font-size: 14px; + cursor: pointer; + transition: background-color 0.2s, transform 0.1s; + min-width: 38px; +} + +.is-tablet .ge-header-buttons button:not(.clickable-icon) { + padding: 6px 12px; +} + +.ge-header-buttons button:hover { + background-color: var(--interactive-hover); + transform: translateY(-1px); +} + +.ge-header-buttons button:active { + transform: translateY(0); +} + +/* 特定按鈕樣式 */ +.ge-header-buttons .sort-button, +.ge-header-buttons .reselect-button, +.ge-header-buttons .refresh-button, +.ge-header-buttons .up-button { + display: inline-flex; + align-items: center; +} + +/* 搜尋對話框樣式 */ +.ge-search-container { + margin-bottom: 8px; + position: relative; + display: flex; + align-items: center; +} + +.ge-search-container input { + width: 100%; + padding: 8px 12px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + background-color: var(--background-primary); + color: var(--text-normal); + font-size: 14px; + outline: none; + transition: border-color 0.2s; + padding-right: 25px; +} + +.ge-search-container input:focus { + border-color: var(--interactive-accent); +} + +.ge-search-container input::placeholder { + color: var(--text-muted); +} + +/* 搜尋範圍設定樣式 */ +.ge-search-scope-container { + display: flex; + align-items: center; + margin: 4px 0; + cursor: pointer; + padding: 4px; +} + +.ge-search-scope-checkbox { + cursor: pointer; +} + +.ge-search-scope-label { + cursor: pointer; + user-select: none; + color: var(--text-normal); +} + +.ge-button-container { + display: flex; + gap: 8px; + justify-content: flex-end; +} + +.ge-button-container button { + padding: 6px 12px; + border-radius: 4px; + font-size: 14px; + cursor: pointer; + transition: background-color 0.2s, transform 0.1s; +} + +.ge-button-container button:first-child { + background-color: var(--interactive-accent); + color: var(--text-on-accent); +} + +.ge-button-container button:last-child { + background-color: var(--interactive-normal); + color: var(--text-normal); +} + +.ge-button-container button:hover { + transform: translateY(-1px); +} + +.ge-button-container button:first-child:hover { + background-color: var(--interactive-accent-hover); +} + +.ge-button-container button:last-child:hover { + background-color: var(--interactive-hover); +} + +.ge-button-container button:active { + transform: translateY(0); +} + +/* 搜尋按鈕容器 */ +.ge-search-button-container { + display: flex; + align-items: center; + gap: 8px; + background-color: var(--background-primary); + border-radius: 4px; + padding: 0; +} + +/* 搜尋按鈕啟用狀態 */ +.ge-search-button-container button.active { + background-color: var(--interactive-accent); + color: var(--text-on-accent); +} + +/* 搜尋文字容器 */ +.ge-search-text-container { + position: relative; + display: flex; + align-items: center; + justify-content: flex-start; + max-width: 150px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + background-color: var(--background-modifier-active-hover); + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + transition: border-color 0.15s ease; +} + +.ge-search-text-container:hover { + border-color: var(--interactive-accent); +} + +/* 搜尋文字 */ +.ge-search-text { + font-size: 14px; + color: var(--text-normal); + flex-grow: 1; /* 讓文字填滿剩餘空間 */ + min-width: 0; /* 防止内容溢出 */ + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-left: 8px; +} + +/* 取消按鈕 */ +.ge-clear-button { + width: 22px; + height: 22px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + cursor: pointer; + color: var(--text-muted); + font-size: 14px; + margin-right:4px; /* 添加右边距 */ + flex-shrink: 0; /* 防止被压缩*/ +} + +.ge-clear-button:hover { + color: #FFF; + background-color: var(--background-modifier-error-hover); +} + +.ge-clear-button svg { + width: 18px; + height: 18px; +} + +.ge-search-clear-button { + color: var(--text-muted); + border-radius: 50%; + width: 18px; + height: 18px; + position: absolute; + right: 5px; + cursor: pointer; + align-items: center; + justify-content: center; + padding: 3px; +} + +.ge-search-clear-button:hover { + color: var(--text-normal); + background-color: var(--background-modifier-hover); +} + +.ge-loading-indicator { + display: flex; + justify-content: center; + align-items: center; + height: 100px; + font-size: 1.2em; + color: var(--text-muted); +} + +.ge-no-files { + text-align: center; + padding: 2em; + color: var(--text-muted); + font-size: 1.2em; +} + +/* 資料夾搜尋輸入框樣式 */ +.ge-folder-search-container { + margin-bottom: 16px; + padding: 0 4px; +} + +.ge-folder-search-input { + width: 100%; + padding: 8px 12px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + background-color: var(--background-primary); + color: var(--text-normal); + font-size: var(--font-ui-small); +} + +.ge-folder-search-input:focus { + border-color: var(--interactive-accent); + box-shadow: 0 0 0 2px rgba(var(--interactive-accent-rgb), 0.2); + outline: none; +} + +.ge-folder-options-container { + max-height: 70vh; + overflow-y: auto; + padding-right: 4px; +} + +/* 鍵盤導航選中項的樣式 */ +.ge-selected-option { + background-color: var(--background-modifier-hover); + border-left: 3px solid var(--interactive-accent) !important; + padding-left: 5px !important; +} + +/* 忽略資料夾設定 */ +.ge-ignored-folders-container { + margin-bottom: 16px; +} + +.ge-ignored-folders-list { + list-style: none; + padding: 0; +} + +.ge-ignored-folder-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 8px; + margin-bottom: 4px; + border: 1px solid var(--background-modifier-border); + border-radius: 4px; + background-color: var(--background-primary); +} + +.ge-ignored-folder-path { + flex-grow: 1; + margin-right: 8px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.ge-ignored-folder-remove { + background-color: var(--interactive-normal); + color: var(--text-normal); + border: none; + padding: 4px 8px; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.2s; +} + +.ge-ignored-folder-remove:hover { + background-color: var(--interactive-hover); +} + +/* 忽略資料夾樣式 */ +.ignored-folder-patterns-container { + margin-bottom: 16px; +} + +.ge-ignored-folder-patterns-list { + list-style: none; + padding: 0; +} + +/* 影片縮圖樣式 */ +.ge-video-thumbnail { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + background-color: var(--background-secondary); + border-radius: 4px; +} + +.ge-video-thumbnail svg { + width: 40px; + height: 40px; + color: var(--text-accent); +} + +/* 媒體檔案全螢幕顯示樣式 */ +.ge-media-fullscreen-container { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(0, 0, 0, 0.9); + z-index: 1000; + display: flex; + justify-content: center; + align-items: center; +} + +.ge-media-view { + position: relative; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.ge-media-container { + position: relative; + width: 100%; + height: 100%; + display: flex; + align-items: center; +} + +.ge-fullscreen-image { + max-width: 100vw; + max-height: 100vh; + object-fit: contain; +} + +.ge-fullscreen-video { + max-width: 100vw; + max-height: 100vh; +} + +.ge-media-container video { + margin: 0 auto; +} + +.ge-fullscreen-file-name { + position: fixed; + top: 15px; + left: 50%; + transform: translateX(-50%); + background-color: rgba(0, 0, 0, 0.6); + color: white; + padding: 8px 16px; + border-radius: 4px; + font-size: 14px; + z-index: 1007; + white-space: nowrap; +} + +.ge-media-close-button { + position: fixed; + top: 15px; + right: 15px; + width: 40px; + height: 40px; + background-color: rgba(0, 0, 0, 0.5); + color: white; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + z-index: 1010; +} + +.ge-media-close-button:hover { + background-color: rgba(255, 0, 0, 0.7); +} + +/* 左右切換區域 */ +.ge-media-prev-area, +.ge-media-next-area { + position: absolute; + top: 15%; + height: 70%; + width: 10%; + display: flex; + align-items: center; + justify-content: center; + z-index: 1005; + cursor: pointer; +} + +.ge-media-prev-area { + left: 0; +} + +.ge-media-next-area { + right: 0; +} + +/* MediaModal 樣式 */ +.ge-media-modal { + padding: 0 !important; + background-color: rgba(0, 0, 0, 0.9) !important; + border: none !important; + border-radius: 0 !important; + box-shadow: none !important; + max-width: 100% !important; + max-height: 100% !important; + width: 100% !important; + height: 100% !important; + margin: 0 !important; +} + +.ge-media-modal .modal-header { + margin: 0 !important; +} + +.ge-media-modal .modal-close-button { + display: none !important; +} + +.ge-media-modal-content { + background-color: transparent !important; + padding: 0 !important; + margin: 0 !important; + display: flex; + justify-content: center; + align-items: center; + position: relative; + width: 100%; + height: 100%; +} + +/* 媒體 Modal 樣式 */ +.ge-media-modal { + padding: 0 !important; + background-color: rgba(0, 0, 0, 0.9) !important; + border: none !important; + border-radius: 0 !important; + box-shadow: none !important; + max-width: 100% !important; + max-height: 100% !important; +} + +/* 選中項目的樣式 */ +.ge-selected-item { + outline: 1px solid var(--vault-profile-color-hover) !important; + outline-offset: -1px; + position: relative; + z-index: 1; +} + +/* 拖曳相關樣式 */ +.ge-grid-item.ge-dragging { + opacity: 0.5; + transform: scale(0.95); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); +} + +.ge-folder-item.ge-dragover { + background-color: var(--interactive-accent-hover); + border: 2px dashed var(--interactive-accent); + transform: scale(1.05); +} + +/* 音樂播放器 */ +.ge-floating-audio-player { + position: fixed; + width: 300px; + background-color: var(--background-primary); + border: 1px solid var(--background-modifier-border); + border-radius: 8px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); + z-index: 1100; + padding: 10px; + display: flex; + flex-direction: column; + gap: 8px; + transition: box-shadow 0.3s ease; +} + +.ge-floating-audio-player:hover { + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3); +} + +.ge-audio-title { + font-size: 14px; + font-weight: 500; + color: var(--text-normal); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding-right: 24px; + cursor: default; + margin: 2px; +} + +.ge-floating-audio-player audio { + width: 100%; + outline: none; +} + +.ge-audio-close-button { + position: absolute; + top: 8px; + right: 8px; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); + cursor: pointer; + border-radius: 50%; + transition: background-color 0.2s ease, color 0.2s ease; + z-index: 1102; +} + +.ge-audio-close-button:hover { + background-color: var(--background-modifier-error-hover); + color: white; +} + +.ge-audio-handle { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 30px; + cursor: move; + border-radius: 8px 8px 0 0; + z-index: 1101; +} + +.ge-audio-dragging { + opacity: 0.9; + user-select: none; +}