This commit is contained in:
Devon22 2025-03-07 00:45:12 +08:00
parent 933848439f
commit 7903d6f1de
14 changed files with 896 additions and 245 deletions

5
.gitignore vendored
View file

@ -19,4 +19,7 @@ main.js
data.json
# Exclude macOS Finder (System Explorer) View States
.DS_Store
.DS_Store
*.bat
Changelog.md

View file

@ -90,11 +90,6 @@ export default class GridExplorerPlugin extends Plugin {
const leaves = this.app.workspace.getLeavesOfType('grid-view');
leaves.forEach(leaf => {
if (leaf.view instanceof GridView) {
// 如果檔案監控設定已變更,需要重新設定檔案監控
if (this.settings.enableFileWatcher) {
leaf.view.registerFileWatcher();
}
// 重新渲染視圖以套用新設定
leaf.view.render();
}

View file

@ -1,7 +1,7 @@
{
"id": "gridexplorer",
"name": "GridExplorer",
"version": "1.8.1",
"version": "1.8.2",
"minAppVersion": "1.1.0",
"description": "Browse note files in a grid view.",
"author": "Devon22",

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "gridexplorer",
"version": "1.8.1",
"version": "1.8.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "gridexplorer",
"version": "1.8.1",
"version": "1.8.2",
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.6",

View file

@ -1,6 +1,6 @@
{
"name": "gridexplorer",
"version": "1.8.1",
"version": "1.8.2",
"description": "Browse note files in a grid view.",
"main": "main.js",
"scripts": {

80
src/FileWatcher.ts Normal file
View file

@ -0,0 +1,80 @@
import { TFile, App } from 'obsidian';
import GridExplorerPlugin from '../main';
import { GridView } from './GridView';
//檔案監聽器
export class FileWatcher {
private plugin: GridExplorerPlugin;
private gridView: GridView;
private app: App;
constructor(plugin: GridExplorerPlugin, gridView: 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 TFile) {
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 TFile) {
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 TFile) {
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 as any).internalPlugins.plugins.bookmarks.instance.on('changed', () => {
if (this.gridView.sourceMode === 'bookmarks') {
this.gridView.render();
}
})
);
}
}

View file

@ -1,7 +1,7 @@
import { App, Modal, Setting, Platform } from 'obsidian';
import GridExplorerPlugin from '../main';
import { GridView } from './GridView';
import { t } from './translations';
import GridExplorerPlugin from '../main';
// 顯示資料夾選擇 modal
export function showFolderSelectionModal(app: App, plugin: GridExplorerPlugin, activeView?: GridView) {

View file

@ -1,10 +1,12 @@
import { App, WorkspaceLeaf, ItemView, Modal, TFolder, TFile, Menu, Notice, Setting} from 'obsidian';
import { setIcon, getFrontMatterInfo } from 'obsidian';
import GridExplorerPlugin from '../main';
import { showFolderSelectionModal } from './FolderSelectionModal';
import { findFirstImageInNote } from './mediaUtils';
import { MediaModal } from './MediaModal';
import { showSearchModal } from './SearchModal';
import { FileWatcher } from './FileWatcher';
import { t } from './translations';
import GridExplorerPlugin from '../main';
// 定義網格視圖
export class GridView extends ItemView {
@ -17,6 +19,7 @@ export class GridView extends ItemView {
gridItems: HTMLElement[] = []; // 存儲所有網格項目的引用
hasKeyboardFocus: boolean = false; // 是否有鍵盤焦點
keyboardNavigationEnabled: boolean = true; // 是否啟用鍵盤導航
fileWatcher: FileWatcher;
constructor(leaf: WorkspaceLeaf, plugin: GridExplorerPlugin) {
super(leaf);
@ -29,7 +32,8 @@ export class GridView extends ItemView {
// 根據設定決定是否註冊檔案變更監聽器
if (this.plugin.settings.enableFileWatcher) {
this.registerFileWatcher();
this.fileWatcher = new FileWatcher(plugin, this);
this.fileWatcher.registerFileWatcher();
}
// 註冊鍵盤事件處理
@ -207,11 +211,41 @@ export class GridView extends ItemView {
//忽略檔案
ignoredFiles(files: TFile[]) {
return files.filter(file =>
!this.plugin.settings.ignoredFolders.some((folder =>
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;
});
}
// 判斷是否為媒體檔案
@ -238,6 +272,23 @@ export class GridView extends ItemView {
// 創建頂部按鈕區域
const headerButtonsDiv = this.containerEl.createDiv('ge-header-buttons');
// 為頂部按鈕區域添加右鍵選單事件
headerButtonsDiv.addEventListener('contextmenu', (event: MouseEvent) => {
event.preventDefault();
const menu = new Menu();
menu.addItem((item) => {
item
.setTitle(t('open_settings'))
.setIcon('settings')
.onClick(() => {
// 打開插件設定頁面
(this.app as any).setting.open();
(this.app as any).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') } });
@ -408,7 +459,7 @@ export class GridView extends ItemView {
this.app.workspace.requestSaveLayout();
});
const searchText = searchTextContainer.createEl('span', { text: this.searchQuery, cls: 'ge-search-text' });
const searchText = searchTextContainer.createEl('span', { cls: 'ge-search-text', text: this.searchQuery });
// 讓搜尋文字可點選
searchText.style.cursor = 'pointer';
searchText.addEventListener('click', () => {
@ -464,17 +515,45 @@ export class GridView extends ItemView {
if (this.sourceMode === 'folder' && this.searchQuery === '') {
const currentFolder = this.app.vault.getAbstractFileByPath(this.sourcePath || '/');
if (currentFolder instanceof TFolder) {
// 只取得當前資料夾中的 Markdown 檔案,不包含子資料夾
const subfolders = currentFolder.children
.filter(child => {
if (!(child instanceof TFolder)) return false;
// 檢查資料夾是否在忽略清單中
return !this.plugin.settings.ignoredFolders.some(
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); // 添加到網格項目數組
@ -851,11 +930,28 @@ export class GridView extends ItemView {
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) {
// 如果按下 Alt + 左鍵,且是資料夾模式且不是根目錄
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();
@ -866,6 +962,17 @@ export class GridView extends ItemView {
event.preventDefault();
break;
case 'ArrowUp':
if (event.altKey) {
// 如果按下 Alt + 左鍵,且是資料夾模式且不是根目錄
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();
@ -963,103 +1070,7 @@ export class GridView extends ItemView {
// 顯示搜尋 modal
showSearchModal(defaultQuery = '') {
class SearchModal extends Modal {
gridView: GridView;
defaultQuery: string;
constructor(app: App, gridView: GridView, defaultQuery: string) {
super(app);
this.gridView = gridView;
this.defaultQuery = defaultQuery;
}
onOpen() {
const { contentEl } = this;
contentEl.empty();
new Setting(contentEl).setName(t('search')).setHeading();
// 如果有 GridView 實例,禁用其鍵盤導航
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'); //這裡不是用 ge-clear-button
clearButton.style.display = this.defaultQuery ? 'flex' : 'none';
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 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.clearSelection();
this.gridView.render();
// 通知 Obsidian 保存視圖狀態
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();
// 如果有 GridView 實例,重新啟用其鍵盤導航
if (this.gridView) {
this.gridView.enableKeyboardNavigation();
}
}
}
new SearchModal(this.app, this, defaultQuery).open();
showSearchModal(this.app,this, defaultQuery);
}
// 保存視圖狀態
@ -1086,70 +1097,6 @@ export class GridView extends ItemView {
}
}
// 註冊檔案監聽器
registerFileWatcher() {
// 只有在設定啟用時才註冊檔案監聽器
if (!this.plugin.settings.enableFileWatcher) {
return;
}
this.registerEvent(
this.app.vault.on('create', (file) => {
if (file instanceof TFile) {
if (this.sourceMode === 'folder' && this.sourcePath && this.searchQuery === '') {
const fileDirPath = file.path.split('/').slice(0, -1).join('/') || '/';
if (fileDirPath === this.sourcePath) {
this.render();
}
} else {
this.render();
}
}
})
);
this.registerEvent(
this.app.vault.on('delete', (file) => {
if (file instanceof TFile) {
if (this.sourceMode === 'folder' && this.sourcePath && this.searchQuery === '') {
const fileDirPath = file.path.split('/').slice(0, -1).join('/') || '/';
if (fileDirPath === this.sourcePath) {
this.render();
}
} else {
this.render();
}
}
})
);
//更名及檔案移動
this.registerEvent(
this.app.vault.on('rename', (file, oldPath) => {
if (file instanceof TFile) {
if (this.sourceMode === 'folder' && this.sourcePath && this.searchQuery === '') {
const fileDirPath = file.path.split('/').slice(0, -1).join('/') || '/';
const oldDirPath = oldPath.split('/').slice(0, -1).join('/') || '/';
if (fileDirPath === this.sourcePath || oldDirPath === this.sourcePath) {
this.render();
}
} else {
this.render();
}
}
})
);
// 處理書籤變更
this.registerEvent(
(this.app as any).internalPlugins.plugins.bookmarks.instance.on('changed', () => {
if (this.sourceMode === 'bookmarks') {
this.render();
}
})
);
}
// 禁用鍵盤導航
disableKeyboardNavigation() {
this.keyboardNavigationEnabled = false;

311
src/MediaModal.ts Normal file
View file

@ -0,0 +1,311 @@
import { App, Modal, TFile, setIcon } from 'obsidian';
export class MediaModal extends Modal {
private file: TFile;
private mediaFiles: TFile[];
private currentIndex: number;
private currentMediaElement: HTMLElement | null = null;
private isZoomed = false;
private handleWheel: ((event: WheelEvent) => void) | null = null;
private gridView: any; // 儲存 GridView 實例的引用
constructor(app: App, file: TFile, mediaFiles: TFile[], gridView?: any) {
super(app);
this.file = file;
this.mediaFiles = mediaFiles;
this.currentIndex = this.mediaFiles.findIndex(f => f.path === file.path);
this.gridView = gridView; // 保存 GridView 實例
// 設置 modal 樣式
this.modalEl.addClass('ge-media-modal');
}
onOpen() {
const { contentEl } = this;
// 如果有 GridView 實例,禁用其鍵盤導航
if (this.gridView) {
this.gridView.disableKeyboardNavigation();
}
// 設置 modal 樣式為全螢幕
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');
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;
}
// 如果有 GridView 實例,重新啟用其鍵盤導航並跳轉到當前選中的項目
if (this.gridView) {
this.gridView.enableKeyboardNavigation();
// 找到當前媒體檔案在 GridView 中的索引
const currentFile = this.mediaFiles[this.currentIndex];
const gridItemIndex = this.gridView.gridItems.findIndex((item: HTMLElement) =>
item.dataset.filePath === currentFile.path
);
// 如果找到了對應的項目,選中它並設置鍵盤焦點
if (gridItemIndex >= 0) {
this.gridView.hasKeyboardFocus = true;
this.gridView.selectItem(gridItemIndex);
}
}
}
// 顯示指定索引的媒體檔案
showMediaAtIndex(index: number) {
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.currentMediaElement) {
this.currentMediaElement.remove();
this.currentMediaElement = null;
}
// 如果存在之前的滾輪事件處理程序,先移除它
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];
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
const videoExtensions = ['mp4', 'webm', 'mov', 'avi', 'mkv'];
if (imageExtensions.includes(mediaFile.extension.toLowerCase())) {
// 創建圖片元素
const img = document.createElement('img');
img.className = 'ge-fullscreen-image';
img.src = this.app.vault.getResourcePath(mediaFile);
mediaContainer.appendChild(img);
this.currentMediaElement = img;
// 設置圖片樣式,預設滿屏顯示
this.resetImageStyles(img);
// 圖片點擊事件(放大/縮小)
img.addEventListener('click', (event) => {
event.stopPropagation();
this.toggleImageZoom(img);
});
} else if (videoExtensions.includes(mediaFile.extension.toLowerCase())) {
// 創建影片元素
const video = document.createElement('video');
video.className = 'ge-fullscreen-video';
video.controls = true;
video.autoplay = true;
const source = document.createElement('source');
source.src = this.app.vault.getResourcePath(mediaFile);
source.type = `video/${mediaFile.extension === 'mov' ? 'quicktime' : mediaFile.extension}`;
video.appendChild(source);
mediaContainer.appendChild(video);
this.currentMediaElement = video;
}
}
// 顯示下一個媒體檔案
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: HTMLImageElement) {
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 as HTMLElement).style.overflowX = 'hidden';
(mediaView as HTMLElement).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: HTMLImageElement) {
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 as HTMLElement).style.overflowX = 'hidden';
(mediaView as HTMLElement).style.overflowY = 'scroll';
} else {
img.style.width = 'auto';
img.style.height = '100vh';
(mediaView as HTMLElement).style.overflowX = 'scroll';
(mediaView as HTMLElement).style.overflowY = 'hidden';
// 將事件處理程序存儲在變數中
this.handleWheel = (event) => {
event.preventDefault();
(mediaView as HTMLElement).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;
}
}
}

105
src/SearchModal.ts Normal file
View file

@ -0,0 +1,105 @@
import { App, Modal, Setting } from 'obsidian';
import { setIcon } from 'obsidian';
import { t } from './translations';
import { GridView } from './GridView';
export class SearchModal extends Modal {
gridView: GridView;
defaultQuery: string;
constructor(app: App, gridView: GridView, defaultQuery: string) {
super(app);
this.gridView = gridView;
this.defaultQuery = defaultQuery;
}
onOpen() {
const { contentEl } = this;
contentEl.empty();
new Setting(contentEl).setName(t('search')).setHeading();
// 如果有 GridView 實例,禁用其鍵盤導航
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'); //這裡不是用 ge-clear-button
clearButton.style.display = this.defaultQuery ? 'flex' : 'none';
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 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.clearSelection();
this.gridView.render();
// 通知 Obsidian 保存視圖狀態
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();
// 如果有 GridView 實例,重新啟用其鍵盤導航
if (this.gridView) {
this.gridView.enableKeyboardNavigation();
}
}
}
// 顯示搜尋 modal
export function showSearchModal(app:App, gridView: GridView, defaultQuery = '') {
new SearchModal(app, gridView, defaultQuery).open();
}

View file

@ -1,24 +1,32 @@
import { App, PluginSettingTab, Setting } from 'obsidian';
import GridExplorerPlugin from '../main';
import { t } from './translations';
import GridExplorerPlugin from '../main';
export interface GallerySettings {
ignoredFolders: string[];
ignoredFolderPatterns: string[];
defaultSortType: string;
gridItemWidth: number;
imageAreaWidth: number;
imageAreaHeight: number;
enableFileWatcher: boolean;
showMediaFiles: boolean;
searchMediaFiles: boolean;
showVideoThumbnails: boolean;
}
// 預設設定
export const DEFAULT_SETTINGS: GallerySettings = {
ignoredFolders: [],
ignoredFolderPatterns: [], // 預設以字串忽略的資料夾模式
defaultSortType: 'mtime-desc', // 預設排序模式:修改時間倒序
gridItemWidth: 300, // 網格項目寬度,預設 300
imageAreaWidth: 100, // 圖片區域寬度,預設 100
imageAreaHeight: 100, // 圖片區域高度,預設 100
enableFileWatcher: true // 預設啟用檔案監控
enableFileWatcher: true, // 預設啟用檔案監控
showMediaFiles: false, // 預設顯示圖片和影片
searchMediaFiles: false, // 預設搜尋時也包含圖片和影片
showVideoThumbnails: false // 預設不顯示影片縮圖
};
// 設定頁面類別
@ -34,6 +42,58 @@ export class GridExplorerSettingTab extends PluginSettingTab {
const { containerEl } = this;
containerEl.empty();
// 媒體檔案設定區域
containerEl.createEl('h3', { text: t('media_files_settings') });
// 顯示圖片和影片設定
new 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;
// 重新載入設定頁面以更新 UI
this.display();
}
await this.plugin.saveSettings();
});
});
// 搜尋圖片和影片設定
new 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 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 Setting(containerEl)
.setName(t('default_sort_type'))
@ -161,6 +221,57 @@ export class GridExplorerSettingTab extends PluginSettingTab {
this.renderIgnoredFoldersList(ignoredFoldersList);
containerEl.appendChild(ignoredFoldersContainer);
// 以字串忽略資料夾(可用正則表達式)設定
const ignoredFolderPatternsContainer = containerEl.createDiv('ignored-folder-patterns-container');
new Setting(containerEl)
.setName(t('ignored_folder_patterns'))
.setDesc(t('ignored_folder_patterns_desc'))
.setHeading();
// 新增字串模式輸入框
const patternSetting = new 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') as HTMLInputElement;
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);
}
// 渲染已忽略的資料夾列表
@ -196,4 +307,37 @@ export class GridExplorerSettingTab extends PluginSettingTab {
});
});
}
// 渲染已忽略的資料夾模式列表
renderIgnoredFolderPatternsList(containerEl: HTMLElement) {
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);
});
});
}
}

View file

@ -34,7 +34,8 @@ export const TRANSLATIONS: Translations = {
'cancel': '取消',
'new_note': '新增筆記',
'untitled': '未命名',
'notes': '個筆記',
'files': '個檔案',
'add': '新增',
// 視圖標題
'grid_view_title': '網格視圖',
@ -42,7 +43,7 @@ export const TRANSLATIONS: Translations = {
'folder_mode': '資料夾',
'search_results': '搜尋結果',
'backlinks_mode': '反向連結',
'all_notes_mode': '所有筆記',
'all_notes_mode': '所有檔案',
// 排序選項
'sort_name_asc': '名稱 (A → Z)',
@ -55,10 +56,22 @@ export const TRANSLATIONS: Translations = {
// 設定
'grid_view_settings': '網格視圖設定',
'media_files_settings': '媒體檔案設定',
'show_media_files': '顯示圖片和影片',
'show_media_files_desc': '在網格視圖中顯示圖片和影片檔案',
'search_media_files': '搜尋圖片和影片',
'search_media_files_desc': '在搜尋結果中包含圖片和影片檔案(僅在啟用顯示圖片和影片時有效)',
'show_video_thumbnails': '顯示影片縮圖',
'show_video_thumbnails_desc': '在網格視圖中顯示影片的縮圖,關閉時將顯示播放圖示',
'ignored_folders': '忽略的資料夾',
'ignored_folders_desc': '在這裡設定要忽略的資料夾',
'add_ignored_folder': '新增忽略資料夾',
'no_ignored_folders': '沒有忽略的資料夾。',
'ignored_folder_patterns': '以字串忽略資料夾和檔案',
'ignored_folder_patterns_desc': '使用字串模式忽略資料夾和檔案(支援正則表達式)',
'add_ignored_folder_pattern': '新增忽略資料夾模式',
'ignored_folder_pattern_placeholder': '輸入資料夾名稱或正則表達式',
'no_ignored_folder_patterns': '沒有忽略的資料夾模式。',
'remove': '移除',
'default_sort_type': '預設排序模式',
'default_sort_type_desc': '設定開啟網格視圖時的預設排序模式',
@ -75,10 +88,11 @@ export const TRANSLATIONS: Translations = {
'select_folders': '選擇資料夾',
'open_grid_view': '開啟網格視圖',
'open_in_grid_view': '在網格視圖中開啟',
'delete_note': '刪除筆記',
'open_settings': '開啟設定',
'delete_note': '刪除檔案',
'open_in_new_tab': '在新分頁開啟',
'searching': '搜尋中...',
'no_files': '沒有找到任何筆記',
'no_files': '沒有找到任何檔案',
'filter_folders': '篩選資料夾...',
},
'en': {
@ -96,7 +110,8 @@ export const TRANSLATIONS: Translations = {
'cancel': 'Cancel',
'new_note': 'New note',
'untitled': 'Untitled',
'notes': 'Notes',
'files': 'files',
'add': 'Add',
// View Titles
'grid_view_title': 'Grid view',
@ -104,23 +119,35 @@ export const TRANSLATIONS: Translations = {
'folder_mode': 'Folder',
'search_results': 'Search results',
'backlinks_mode': 'Backlinks',
'all_notes_mode': 'All notes',
'all_notes_mode': 'All files',
// Sort Options
'sort_name_asc': 'Name (A → Z)',
'sort_name_desc': 'Name (Z → A)',
'sort_mtime_desc': 'Modified time (New → Old)',
'sort_mtime_asc': 'Modified time (Old → New)',
'sort_ctime_desc': 'Created time (New → Old)',
'sort_ctime_asc': 'Created time (Old → New)',
'sort_mtime_desc': 'Modified (New → Old)',
'sort_mtime_asc': 'Modified (Old → New)',
'sort_ctime_desc': 'Created (New → Old)',
'sort_ctime_asc': 'Created (Old → 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 be ignored.',
'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',
@ -133,14 +160,15 @@ export const TRANSLATIONS: Translations = {
'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',
// Folder Selection Dialog
// Select Folder Dialog
'select_folders': 'Select folder',
'open_grid_view': 'Open grid view',
'open_in_grid_view': 'Open in grid view',
'delete_note': 'Delete note',
'open_settings': 'Open settings',
'delete_note': 'Delete file',
'open_in_new_tab': 'Open in new tab',
'searching': 'Searching...',
'no_files': 'No notes found',
'no_files': 'No files found',
'filter_folders': 'Filter folders...',
},
'zh': {
@ -151,14 +179,15 @@ export const TRANSLATIONS: Translations = {
'sorting': '排序方式',
'refresh': '刷新',
'reselect_folder': '重新选择位置',
'go_up': '回上层文件夹',
'go_up': '回上层文件夹',
'no_backlinks': '没有反向链接',
'search': '搜索',
'search_placeholder': '搜索关键',
'search_placeholder': '搜索关键',
'cancel': '取消',
'new_note': '新笔记',
'new_note': '新笔记',
'untitled': '未命名',
'notes': '個笔记',
'files': '个文件',
'add': '添加',
// 视图标题
'grid_view_title': '网格视图',
@ -166,43 +195,56 @@ export const TRANSLATIONS: Translations = {
'folder_mode': '文件夹',
'search_results': '搜索结果',
'backlinks_mode': '反向链接',
'all_notes_mode': '所有笔记',
'all_notes_mode': '所有文件',
// 排序选项
'sort_name_asc': '名称 (A → Z)',
'sort_name_desc': '名称 (Z → A)',
'sort_mtime_desc': '修改时间 (新 → 旧)',
'sort_mtime_asc': '修改时间 (旧 → 新)',
'sort_ctime_desc': '时间 (新 → 旧)',
'sort_ctime_asc': '时间 (旧 → 新)',
'sort_ctime_desc': '建时间 (新 → 旧)',
'sort_ctime_asc': '建时间 (旧 → 新)',
'sort_random': '随机排序',
// 设置
'grid_view_settings': '网格视图设置',
'media_files_settings': '媒体文件设置',
'show_media_files': '显示图片和视频',
'show_media_files_desc': '在网格视图中显示图片和视频文件',
'search_media_files': '搜索图片和视频',
'search_media_files_desc': '在搜索结果中包含图片和视频文件(仅在启用显示图片和视频时有效)',
'show_video_thumbnails': '显示视频缩图',
'show_video_thumbnails_desc': '在网格视图中显示视频的缩图,关闭时将显示播放图标',
'ignored_folders': '忽略的文件夹',
'ignored_folders_desc': '在这里设置要忽略的文件夹',
'add_ignored_folder': '新增忽略資料夾',
'no_ignored_folders': '沒有忽略的資料夾。',
'add_ignored_folder': '添加忽略文件夹',
'no_ignored_folders': '没有忽略的文件夹。',
'ignored_folder_patterns': '以字符串忽略文件夹和文件',
'ignored_folder_patterns_desc': '使用字符串模式忽略文件夹和文件(支持正则表达式)',
'add_ignored_folder_pattern': '添加忽略文件夹模式',
'ignored_folder_pattern_placeholder': '输入文件夹名称或正则表达式',
'no_ignored_folder_patterns': '没有忽略的文件夹模式。',
'remove': '移除',
'default_sort_type': '預設排序模式',
'default_sort_type_desc': '设置开启网格视图时的預设排序模式',
'default_sort_type': '默认排序模式',
'default_sort_type_desc': '设置打开网格视图时的默认排序模式',
'grid_item_width': '网格项目宽度',
'grid_item_width_desc': '设置网格项目的宽度',
'image_area_width': '圖片區域寬度',
'image_area_width_desc': '设置圖片預覽區域的寬度',
'image_area_height': '圖片區域高度',
'image_area_height_desc': '设置圖片預覽區域的高度',
'image_area_width': '图片区域宽度',
'image_area_width_desc': '设置图片预览区域的宽度',
'image_area_height': '图片区域高度',
'image_area_height_desc': '设置图片预览区域的高度',
'enable_file_watcher': '启用文件监控',
'enable_file_watcher_desc': '启用后会自动检测文件变更并更新视图,关闭后需手动点击刷新按钮',
// 选择资料夹对话框
// 选择文件夹对话框
'select_folders': '选择文件夹',
'open_grid_view': '开启网格视图',
'open_in_grid_view': '在网格视图中开启',
'delete_note': '删除笔记',
'open_settings': '开启设置',
'delete_note': '删除文件',
'open_in_new_tab': '在新标签页打开',
'searching': '搜索中...',
'no_files': '没有找到任何笔记',
'no_files': '没有找到任何文件',
'filter_folders': '筛选文件夹...',
},
'ja': {
@ -210,17 +252,18 @@ export const TRANSLATIONS: Translations = {
'bookmarks_plugin_disabled': 'ブックマークプラグインを有効にしてください',
// ボタンとラベル
'sorting': 'ソート',
'refresh': 'リフレッシュ',
'reselect_folder': 'フォルダを再選択',
'go_up': '上へ',
'no_backlinks': 'バックリンクはありません',
'sorting': '並び替え',
'refresh': '更新',
'reselect_folder': '場所を再選択',
'go_up': '上のフォルダへ',
'no_backlinks': 'バックリンクなし',
'search': '検索',
'search_placeholder': '検索キーワード',
'search_placeholder': 'キーワード検索',
'cancel': 'キャンセル',
'new_note': '新規ノート',
'untitled': '無題のファイル',
'notes': 'ファイル',
'untitled': '無題',
'files': 'ファイル',
'add': '追加',
// ビュータイトル
'grid_view_title': 'グリッドビュー',
@ -228,43 +271,56 @@ export const TRANSLATIONS: Translations = {
'folder_mode': 'フォルダ',
'search_results': '検索結果',
'backlinks_mode': 'バックリンク',
'all_notes_mode': 'すべてのノート',
'all_notes_mode': 'すべてのファイル',
// ソートオプション
// 並べ替えオプション
'sort_name_asc': '名前 (A → Z)',
'sort_name_desc': '名前 (Z → A)',
'sort_mtime_desc': '更時 (新 → 旧)',
'sort_mtime_asc': '更時 (旧 → 新)',
'sort_ctime_desc': '作成 (新 → 旧)',
'sort_ctime_asc': '作成 (旧 → 新)',
'sort_mtime_desc': '新日時 (新 → 旧)',
'sort_mtime_asc': '新日時 (旧 → 新)',
'sort_ctime_desc': '作成時 (新 → 旧)',
'sort_ctime_asc': '作成時 (旧 → 新)',
'sort_random': 'ランダム',
// 設定
'grid_view_settings': 'グリッドビューセッティング',
'grid_view_settings': 'グリッドビュー設定',
'media_files_settings': 'メディアファイル設定',
'show_media_files': '画像と動画を表示',
'show_media_files_desc': 'グリッドビューに画像と動画ファイルを表示する',
'search_media_files': '画像と動画を検索',
'search_media_files_desc': '検索結果に画像と動画ファイルを含める(画像と動画を表示が有効な場合のみ有効)',
'show_video_thumbnails': '動画サムネイルを表示',
'show_video_thumbnails_desc': 'グリッドビューに動画のサムネイルを表示する、無効にすると再生アイコンが表示される',
'ignored_folders': '無視するフォルダ',
'ignored_folders_desc': '無視するフォルダを設定します',
'ignored_folders_desc': '無視するフォルダをここで設定します',
'add_ignored_folder': '無視するフォルダを追加',
'no_ignored_folders': '無視するフォルダはありません',
'no_ignored_folders': '無視するフォルダはありません。',
'ignored_folder_patterns': 'パターンでフォルダとファイルを無視',
'ignored_folder_patterns_desc': '文字列パターンを使用してフォルダとファイルを無視します(正規表現をサポート)',
'add_ignored_folder_pattern': 'フォルダパターンを追加',
'ignored_folder_pattern_placeholder': 'フォルダ名または正規表現を入力',
'no_ignored_folder_patterns': '無視するフォルダパターンはありません。',
'remove': '削除',
'default_sort_type': 'デフォルトのソートタイプ',
'default_sort_type_desc': 'グリッドビューを開くときのデフォルトのソート方法を設定します',
'default_sort_type': 'デフォルトの並び替え',
'default_sort_type_desc': 'グリッドビューを開いたときのデフォルトの並び替えを設定',
'grid_item_width': 'グリッドアイテムの幅',
'grid_item_width_desc': 'グリッドアイテムの幅を設定します',
'image_area_width': '画像エリア幅',
'image_area_width_desc': '画像プレビュー領域の幅を設定します',
'image_area_height': '画像エリア高さ',
'image_area_height_desc': '画像プレビュー領域の高さを設定します',
'enable_file_watcher': 'ファイルウォッチャーを有効にする',
'enable_file_watcher_desc': '有効にすると、ファイルの変更を自動的に検知してビューを更新します。無効にすると、手動でリフレッシュボタンをクリックする必要があります',
'grid_item_width_desc': 'グリッドアイテムの幅を設定',
'image_area_width': '画像エリア幅',
'image_area_width_desc': '画像プレビューエリアの幅を設定',
'image_area_height': '画像エリア高さ',
'image_area_height_desc': '画像プレビューエリアの高さを設定',
'enable_file_watcher': 'ファイル監視を有効にする',
'enable_file_watcher_desc': '有効にすると、ファイルの変更を自動的に検出してビューを更新します。無効にすると、手動で更新ボタンをクリックする必要があります',
// フォルダ選択ダイアログ
'select_folders': 'フォルダを選択',
'open_grid_view': 'グリッドビューを開く',
'open_in_grid_view': 'グリッドビューで開く',
'delete_note': 'ノートを削除',
'open_settings': '設定を開く',
'delete_note': 'ファイルを削除',
'open_in_new_tab': '新しいタブで開く',
'searching': '検索中...',
'no_files': 'ノートが見つかりません',
'no_files': 'ファイルが見つかりません',
'filter_folders': 'フォルダをフィルタリング...',
},
};

View file

@ -479,6 +479,16 @@
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%;

View file

@ -1,3 +1,3 @@
{
"1.8.1": "1.1.0"
"1.8.2": "1.1.0"
}