mirror of
https://github.com/devon22/obsidian-gridexplorer.git
synced 2026-07-22 05:38:16 +00:00
2.7.0
This commit is contained in:
parent
ccd430421e
commit
ffb5d74752
13 changed files with 648 additions and 193 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "gridexplorer",
|
||||
"name": "GridExplorer",
|
||||
"version": "2.6.6",
|
||||
"version": "2.7.0",
|
||||
"minAppVersion": "1.1.0",
|
||||
"description": "Browse note files in a grid view.",
|
||||
"author": "Devon22",
|
||||
|
|
|
|||
4
package-lock.json
generated
4
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "gridexplorer",
|
||||
"version": "2.6.6",
|
||||
"version": "2.7.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "gridexplorer",
|
||||
"version": "2.6.6",
|
||||
"version": "2.7.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.6",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "gridexplorer",
|
||||
"version": "2.6.6",
|
||||
"version": "2.7.0",
|
||||
"description": "Browse note files in a grid view.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
387
src/GridView.ts
387
src/GridView.ts
|
|
@ -9,7 +9,9 @@ import { MediaModal } from './modal/mediaModal';
|
|||
import { showFolderNoteSettingsModal } from './modal/folderNoteSettingsModal';
|
||||
import { showNoteSettingsModal } from './modal/noteSettingsModal';
|
||||
import { showFolderRenameModal } from './modal/folderRenameModal';
|
||||
import { moveFolderSuggestModal } from './modal/moveFolderSuggestModal';
|
||||
import { showSearchModal } from './modal/searchModal';
|
||||
import { CustomModeModal } from './modal/customModeModal';
|
||||
import { FloatingAudioPlayer } from './floatingAudioPlayer';
|
||||
import { t } from './translations';
|
||||
|
||||
|
|
@ -46,6 +48,8 @@ export class GridView extends ItemView {
|
|||
recentSources: string[] = []; // 歷史記錄
|
||||
minMode: boolean = false; // 最小模式
|
||||
showIgnoredFolders: boolean = false; // 顯示忽略資料夾
|
||||
showDateDividers: boolean = false; // 顯示日期分隔器
|
||||
showNoteTags: boolean = false; // 顯示筆記標籤
|
||||
pinnedList: string[] = []; // 置頂清單
|
||||
taskFilter: string = 'uncompleted'; // 任務分類
|
||||
hideHeaderElements: boolean = false; // 是否隱藏標題列元素(模式名稱和按鈕)
|
||||
|
|
@ -64,6 +68,8 @@ export class GridView extends ItemView {
|
|||
this.sortType = this.plugin.settings.defaultSortType; // 使用設定中的預設排序模式
|
||||
this.baseCardLayout = this.plugin.settings.cardLayout;
|
||||
this.cardLayout = this.baseCardLayout;
|
||||
this.showDateDividers = this.plugin.settings.dateDividerMode !== 'none';
|
||||
this.showNoteTags = this.plugin.settings.showNoteTags;
|
||||
|
||||
// 根據設定決定是否註冊檔案變更監聽器
|
||||
if (this.plugin.settings.enableFileWatcher) {
|
||||
|
|
@ -467,7 +473,7 @@ export class GridView extends ItemView {
|
|||
// 添加重新選擇資料夾按鈕
|
||||
const reselectButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('reselect') } });
|
||||
reselectButton.addEventListener('click', () => {
|
||||
showFolderSelectionModal(this.app, this.plugin, this);
|
||||
showFolderSelectionModal(this.app, this.plugin, this, reselectButton);
|
||||
});
|
||||
setIcon(reselectButton, "grid");
|
||||
|
||||
|
|
@ -481,8 +487,6 @@ export class GridView extends ItemView {
|
|||
});
|
||||
setIcon(refreshButton, 'refresh-ccw');
|
||||
|
||||
// 排序按鈕已移動到模式名稱區域
|
||||
|
||||
// 添加搜尋按鈕
|
||||
const searchButtonContainer = headerButtonsDiv.createDiv('ge-search-button-container');
|
||||
const searchButton = searchButtonContainer.createEl('button', {
|
||||
|
|
@ -491,7 +495,7 @@ export class GridView extends ItemView {
|
|||
});
|
||||
setIcon(searchButton, 'search');
|
||||
searchButton.addEventListener('click', () => {
|
||||
showSearchModal(this.app, this, '');
|
||||
showSearchModal(this.app, this, '', searchButton);
|
||||
});
|
||||
|
||||
// 如果有搜尋關鍵字,顯示搜尋文字和取消按鈕
|
||||
|
|
@ -504,7 +508,7 @@ export class GridView extends ItemView {
|
|||
// 讓搜尋文字可點選
|
||||
searchText.style.cursor = 'pointer';
|
||||
searchText.addEventListener('click', () => {
|
||||
showSearchModal(this.app, this, this.searchQuery);
|
||||
showSearchModal(this.app, this, this.searchQuery, searchText);
|
||||
});
|
||||
|
||||
// 創建取消按鈕
|
||||
|
|
@ -521,123 +525,154 @@ export class GridView extends ItemView {
|
|||
}
|
||||
|
||||
// 添加更多選項按鈕
|
||||
|
||||
const menu = new Menu();
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(t('open_new_grid_view'))
|
||||
.setIcon('grid')
|
||||
.onClick(() => {
|
||||
const { workspace } = this.app;
|
||||
let leaf = null;
|
||||
workspace.getLeavesOfType('grid-view');
|
||||
switch (this.plugin.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,則回退到新分頁
|
||||
leaf = workspace.getLeaf('tab');
|
||||
}
|
||||
leaf.setViewState({ type: 'grid-view', active: true });
|
||||
// 設定資料來源
|
||||
if (leaf.view instanceof GridView) {
|
||||
leaf.view.setSource('folder', '/');
|
||||
}
|
||||
// 確保視圖是活躍的
|
||||
workspace.revealLeaf(leaf);
|
||||
});
|
||||
});
|
||||
menu.addSeparator();
|
||||
|
||||
// 建立隨機筆記、最近筆記、全部筆記是否包含圖片和影片的設定按鈕
|
||||
if ((this.sourceMode === 'all-files' || this.sourceMode === 'recent-files' || this.sourceMode === 'random-note') &&
|
||||
this.plugin.settings.showMediaFiles && this.searchQuery === '') {
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('random_note_notes_only'))
|
||||
.setIcon('file-text')
|
||||
.setChecked(!this.randomNoteIncludeMedia)
|
||||
.onClick(() => {
|
||||
this.randomNoteIncludeMedia = false;
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('random_note_include_media_files'))
|
||||
.setIcon('file-image')
|
||||
.setChecked(this.randomNoteIncludeMedia)
|
||||
.onClick(() => {
|
||||
this.randomNoteIncludeMedia = true;
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
menu.addSeparator();
|
||||
}
|
||||
// 直向卡片切換
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('vertical_card'))
|
||||
.setIcon('layout')
|
||||
.setChecked(this.baseCardLayout === 'vertical')
|
||||
.onClick(() => {
|
||||
this.baseCardLayout = this.baseCardLayout === 'vertical' ? 'horizontal' : 'vertical';
|
||||
this.cardLayout = this.baseCardLayout;
|
||||
this.render();
|
||||
this.app.workspace.requestSaveLayout();
|
||||
});
|
||||
});
|
||||
// 最小化模式選項
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(t('min_mode'))
|
||||
.setIcon('minimize-2')
|
||||
.setChecked(this.minMode)
|
||||
.onClick(() => {
|
||||
this.minMode = !this.minMode;
|
||||
this.app.workspace.requestSaveLayout();
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
// 顯示日期分隔器
|
||||
if (this.plugin.settings.dateDividerMode !== 'none') {
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(t('show_date_dividers'))
|
||||
.setIcon('calendar')
|
||||
.setChecked(this.showDateDividers)
|
||||
.onClick(() => {
|
||||
this.showDateDividers = !this.showDateDividers;
|
||||
this.app.workspace.requestSaveLayout();
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
}
|
||||
// 顯示筆記標籤
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(t('show_note_tags'))
|
||||
.setIcon('tag')
|
||||
.setChecked(this.showNoteTags)
|
||||
.onClick(() => {
|
||||
this.showNoteTags = !this.showNoteTags;
|
||||
this.app.workspace.requestSaveLayout();
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
// 顯示忽略資料夾選項
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(t('show_ignored_folders'))
|
||||
.setIcon('folder-open-dot')
|
||||
.setChecked(this.showIgnoredFolders)
|
||||
.onClick(() => {
|
||||
this.showIgnoredFolders = !this.showIgnoredFolders;
|
||||
this.app.workspace.requestSaveLayout();
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
menu.addSeparator();
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
if (this.searchQuery === '') {
|
||||
const moreOptionsButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('more_options') } });
|
||||
setIcon(moreOptionsButton, 'ellipsis-vertical');
|
||||
const menu = new Menu();
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(t('open_new_grid_view'))
|
||||
.setIcon('grid')
|
||||
.onClick(() => {
|
||||
const { workspace } = this.app;
|
||||
let leaf = null;
|
||||
workspace.getLeavesOfType('grid-view');
|
||||
switch (this.plugin.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,則回退到新分頁
|
||||
leaf = workspace.getLeaf('tab');
|
||||
}
|
||||
leaf.setViewState({ type: 'grid-view', active: true });
|
||||
// 設定資料來源
|
||||
if (leaf.view instanceof GridView) {
|
||||
leaf.view.setSource('folder', '/');
|
||||
}
|
||||
// 確保視圖是活躍的
|
||||
workspace.revealLeaf(leaf);
|
||||
});
|
||||
});
|
||||
menu.addSeparator();
|
||||
|
||||
|
||||
// 建立隨機筆記、最近筆記、全部筆記是否包含圖片和影片的設定按鈕
|
||||
if ((this.sourceMode === 'all-files' || this.sourceMode === 'recent-files' || this.sourceMode === 'random-note') &&
|
||||
this.plugin.settings.showMediaFiles && this.searchQuery === '') {
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('random_note_notes_only'))
|
||||
.setIcon('file-text')
|
||||
.setChecked(!this.randomNoteIncludeMedia)
|
||||
.onClick(() => {
|
||||
this.randomNoteIncludeMedia = false;
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('random_note_include_media_files'))
|
||||
.setIcon('file-image')
|
||||
.setChecked(this.randomNoteIncludeMedia)
|
||||
.onClick(() => {
|
||||
this.randomNoteIncludeMedia = true;
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
menu.addSeparator();
|
||||
}
|
||||
|
||||
// 直向卡片切換
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('vertical_card'))
|
||||
.setIcon('layout')
|
||||
.setChecked(this.baseCardLayout === 'vertical')
|
||||
.onClick(() => {
|
||||
this.baseCardLayout = this.baseCardLayout === 'vertical' ? 'horizontal' : 'vertical';
|
||||
this.cardLayout = this.baseCardLayout;
|
||||
this.render();
|
||||
this.app.workspace.requestSaveLayout();
|
||||
});
|
||||
});
|
||||
|
||||
// 最小化模式選項
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(t('min_mode'))
|
||||
.setIcon('minimize-2')
|
||||
.setChecked(this.minMode)
|
||||
.onClick(() => {
|
||||
this.minMode = !this.minMode;
|
||||
this.app.workspace.requestSaveLayout();
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
// 顯示忽略資料夾選項
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(t('show_ignored_folders'))
|
||||
.setIcon('folder-open-dot')
|
||||
.setChecked(this.showIgnoredFolders)
|
||||
.onClick(() => {
|
||||
this.showIgnoredFolders = !this.showIgnoredFolders;
|
||||
this.app.workspace.requestSaveLayout();
|
||||
this.render();
|
||||
});
|
||||
});
|
||||
menu.addSeparator();
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
moreOptionsButton.addEventListener('click', (event) => {
|
||||
menu.showAtMouseEvent(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
headerButtonsDiv.addEventListener('contextmenu', (event) => {
|
||||
if (event.target === headerButtonsDiv) {
|
||||
event.preventDefault();
|
||||
menu.showAtMouseEvent(event);
|
||||
}
|
||||
});
|
||||
|
||||
// 創建模式名稱和排序按鈕的容器
|
||||
const modeHeaderContainer = this.containerEl.createDiv('ge-mode-header-container');
|
||||
|
|
@ -1143,46 +1178,80 @@ export class GridView extends ItemView {
|
|||
|
||||
// 取得當前自訂模式
|
||||
const mode = this.plugin.settings.customModes.find(m => m.internalName === this.sourceMode);
|
||||
if (mode && mode.options && mode.options.length > 0) {
|
||||
if (this.customOptionIndex >= mode.options.length || this.customOptionIndex < -1) {
|
||||
this.customOptionIndex = -1;
|
||||
}
|
||||
if (mode) {
|
||||
const hasOptions = mode.options && mode.options.length > 0;
|
||||
|
||||
if (hasOptions && mode.options) {
|
||||
if (this.customOptionIndex >= mode.options.length || this.customOptionIndex < -1) {
|
||||
this.customOptionIndex = -1;
|
||||
}
|
||||
|
||||
let subName: string | undefined;
|
||||
if (this.customOptionIndex === -1) {
|
||||
subName = (mode as any).name?.trim() || t('default');
|
||||
} else if (this.customOptionIndex >= 0 && this.customOptionIndex < mode.options.length) {
|
||||
const opt = mode.options![this.customOptionIndex];
|
||||
subName = opt.name?.trim() || `${t('option')} ${this.customOptionIndex + 1}`;
|
||||
}
|
||||
let subName: string | undefined;
|
||||
if (this.customOptionIndex === -1) {
|
||||
subName = (mode as any).name?.trim() || t('default');
|
||||
} else if (this.customOptionIndex >= 0 && this.customOptionIndex < mode.options.length) {
|
||||
const opt = mode.options[this.customOptionIndex];
|
||||
subName = opt.name?.trim() || `${t('option')} ${this.customOptionIndex + 1}`;
|
||||
}
|
||||
|
||||
const subSpan = modenameContainer.createEl('a', { text: subName ?? '-', cls: 'ge-sub-option' });
|
||||
subSpan.addEventListener('click', (evt) => {
|
||||
const menu = new Menu();
|
||||
// 預設選項
|
||||
const defaultName = (mode as any).name?.trim() || t('default');
|
||||
menu.addItem(item => {
|
||||
item.setTitle(defaultName)
|
||||
.setIcon('puzzle')
|
||||
.setChecked(this.customOptionIndex === -1)
|
||||
.onClick(() => {
|
||||
this.customOptionIndex = -1;
|
||||
this.render(true);
|
||||
});
|
||||
});
|
||||
mode.options!.forEach((opt, idx) => {
|
||||
const subSpan = modenameContainer.createEl('a', { text: subName ?? '-', cls: 'ge-sub-option' });
|
||||
subSpan.addEventListener('click', (evt) => {
|
||||
const menu = new Menu();
|
||||
// 預設選項
|
||||
const defaultName = (mode as any).name?.trim() || t('default');
|
||||
menu.addItem(item => {
|
||||
item.setTitle(opt.name?.trim() || t('option') + ' ' + (idx + 1))
|
||||
item.setTitle(defaultName)
|
||||
.setIcon('puzzle')
|
||||
.setChecked(idx === this.customOptionIndex)
|
||||
.setChecked(this.customOptionIndex === -1)
|
||||
.onClick(() => {
|
||||
this.customOptionIndex = idx;
|
||||
this.customOptionIndex = -1;
|
||||
this.render(true);
|
||||
});
|
||||
});
|
||||
mode.options!.forEach((opt, idx) => {
|
||||
menu.addItem(item => {
|
||||
item.setTitle(opt.name?.trim() || t('option') + ' ' + (idx + 1))
|
||||
.setIcon('puzzle')
|
||||
.setChecked(idx === this.customOptionIndex)
|
||||
.onClick(() => {
|
||||
this.customOptionIndex = idx;
|
||||
this.render(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 設定選項
|
||||
menu.addSeparator();
|
||||
menu.addItem(item => {
|
||||
item.setTitle(t('edit'))
|
||||
.setIcon('settings')
|
||||
.onClick(() => {
|
||||
const modeIndex = this.plugin.settings.customModes.findIndex(m => m.internalName === mode.internalName);
|
||||
if (modeIndex === -1) return;
|
||||
new CustomModeModal(this.app, this.plugin, this.plugin.settings.customModes[modeIndex], (result) => {
|
||||
this.plugin.settings.customModes[modeIndex] = result;
|
||||
this.plugin.saveSettings();
|
||||
this.render(true);
|
||||
}).open();
|
||||
});
|
||||
});
|
||||
|
||||
menu.showAtMouseEvent(evt);
|
||||
});
|
||||
menu.showAtMouseEvent(evt);
|
||||
});
|
||||
} else {
|
||||
// 只有預設選項時,只顯示齒輪圖示
|
||||
const gearIcon = modenameContainer.createEl('a', { cls: 'ge-sub-option' });
|
||||
setIcon(gearIcon, 'settings');
|
||||
gearIcon.addEventListener('click', () => {
|
||||
const modeIndex = this.plugin.settings.customModes.findIndex(m => m.internalName === mode.internalName);
|
||||
if (modeIndex === -1) return;
|
||||
new CustomModeModal(this.app, this.plugin, this.plugin.settings.customModes[modeIndex], (result) => {
|
||||
this.plugin.settings.customModes[modeIndex] = result;
|
||||
this.plugin.saveSettings();
|
||||
this.render(true);
|
||||
}).open();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -1608,6 +1677,17 @@ export class GridView extends ItemView {
|
|||
});
|
||||
});
|
||||
}
|
||||
// 搬移資料夾
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
.setTitle(t('move_folder'))
|
||||
.setIcon('folder-cog')
|
||||
.onClick(() => {
|
||||
if (folder instanceof TFolder) {
|
||||
new moveFolderSuggestModal(this.plugin, folder, this).open();
|
||||
}
|
||||
});
|
||||
});
|
||||
// 重新命名資料夾
|
||||
menu.addItem((item) => {
|
||||
item
|
||||
|
|
@ -1619,7 +1699,7 @@ export class GridView extends ItemView {
|
|||
}
|
||||
});
|
||||
});
|
||||
//刪除資料夾
|
||||
// 刪除資料夾
|
||||
menu.addItem((item) => {
|
||||
(item as any).setWarning(true);
|
||||
item
|
||||
|
|
@ -2147,11 +2227,11 @@ export class GridView extends ItemView {
|
|||
contentArea.createEl('p', { text: file.extension.toUpperCase() });
|
||||
}
|
||||
|
||||
setTooltip(fileEl as HTMLElement, `${file.name}`)
|
||||
setTooltip(fileEl as HTMLElement, `${file.name}`,{ delay:2000 })
|
||||
}
|
||||
|
||||
// 顯示標籤(僅限 Markdown 檔案)
|
||||
if (file.extension === 'md' && this.plugin.settings.showNoteTags && !this.minMode) {
|
||||
if (file.extension === 'md' && this.showNoteTags && !this.minMode) {
|
||||
const fileCache = this.app.metadataCache.getFileCache(file);
|
||||
const displaySetting = fileCache?.frontmatter?.display;
|
||||
|
||||
|
|
@ -2322,7 +2402,8 @@ export class GridView extends ItemView {
|
|||
const shouldShowDateDividers = dateDividerMode !== 'none' &&
|
||||
(sortType.startsWith('mtime-') || sortType.startsWith('ctime-')) &&
|
||||
this.sourceMode !== 'random-note' &&
|
||||
this.sourceMode !== 'bookmarks';
|
||||
this.sourceMode !== 'bookmarks' &&
|
||||
this.showDateDividers;
|
||||
|
||||
let lastDateString = '';
|
||||
let pinDividerAdded = false;
|
||||
|
|
@ -2965,6 +3046,8 @@ export class GridView extends ItemView {
|
|||
baseCardLayout: this.baseCardLayout,
|
||||
cardLayout: this.cardLayout,
|
||||
hideHeaderElements: this.hideHeaderElements,
|
||||
showDateDividers: this.showDateDividers,
|
||||
showNoteTags: this.showNoteTags,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -2985,6 +3068,8 @@ export class GridView extends ItemView {
|
|||
this.baseCardLayout = state.state.baseCardLayout ?? 'horizontal';
|
||||
this.cardLayout = state.state.cardLayout ?? this.baseCardLayout; // 同步 baseCardLayout 的卡片樣式,以便 render() 使用正確的 cardLayout
|
||||
this.hideHeaderElements = state.state.hideHeaderElements ?? false;
|
||||
this.showDateDividers = state.state.showDateDividers ?? this.plugin.settings.dateDividerMode !== 'none';
|
||||
this.showNoteTags = state.state.showNoteTags ?? this.plugin.settings.showNoteTags;
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -513,7 +513,6 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
|
|||
|
||||
const mode = settings.customModes.find(m => m.internalName === sourceMode);
|
||||
if (!mode) {
|
||||
new Notice(`Custom mode ${sourceMode} not found.`);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -128,16 +128,13 @@ export class CustomModeModal extends Modal {
|
|||
});
|
||||
});
|
||||
|
||||
// 移除按鈕(至少保留一個)
|
||||
// 移除按鈕
|
||||
if (options.length > 0) {
|
||||
optSetting.addExtraButton(btn => {
|
||||
btn.setIcon('trash')
|
||||
.setTooltip(t('remove'))
|
||||
.onClick(() => {
|
||||
options.splice(idx, 1);
|
||||
if (idx === 0 && options.length > 0) {
|
||||
dataviewCode = options[0].dataviewCode;
|
||||
}
|
||||
renderOptions();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { GridView } from '../GridView';
|
|||
import { t } from '../translations';
|
||||
|
||||
// 顯示資料夾選擇 modal
|
||||
export function showFolderSelectionModal(app: App, plugin: GridExplorerPlugin, activeView?: GridView) {
|
||||
new FolderSelectionModal(app, plugin, activeView).open();
|
||||
export function showFolderSelectionModal(app: App, plugin: GridExplorerPlugin, activeView?: GridView, buttonElement?: HTMLElement) {
|
||||
new FolderSelectionModal(app, plugin, activeView, buttonElement).open();
|
||||
}
|
||||
|
||||
export class FolderSelectionModal extends Modal {
|
||||
|
|
@ -15,17 +15,25 @@ export class FolderSelectionModal extends Modal {
|
|||
folderOptions: HTMLElement[] = [];
|
||||
selectedIndex: number = -1; // 當前選中的選項索引
|
||||
searchInput: HTMLInputElement;
|
||||
buttonElement: HTMLElement | undefined;
|
||||
|
||||
constructor(app: App, plugin: GridExplorerPlugin, activeView?: GridView) {
|
||||
constructor(app: App, plugin: GridExplorerPlugin, activeView?: GridView, buttonElement?: HTMLElement) {
|
||||
super(app);
|
||||
this.plugin = plugin;
|
||||
this.activeView = activeView;
|
||||
this.buttonElement = buttonElement;
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
|
||||
// 如果有按鈕元素,設置為 popup 樣式
|
||||
if (this.buttonElement) {
|
||||
this.modalEl.addClass('ge-popup-modal');
|
||||
this.positionAsPopup();
|
||||
}
|
||||
|
||||
// 添加搜尋輸入框
|
||||
const searchContainer = contentEl.createEl('div', {
|
||||
cls: 'ge-folder-search-container'
|
||||
|
|
@ -240,9 +248,10 @@ export class FolderSelectionModal extends Modal {
|
|||
}
|
||||
|
||||
// 建立根目錄選項
|
||||
const customFolderIcon = this.plugin.settings.customFolderIcon;
|
||||
const rootFolderOption = this.folderOptionsContainer.createEl('div', {
|
||||
cls: 'ge-grid-view-folder-option',
|
||||
text: `📁 /`
|
||||
text: `${customFolderIcon} /`
|
||||
});
|
||||
|
||||
rootFolderOption.addEventListener('click', () => {
|
||||
|
|
@ -287,7 +296,7 @@ export class FolderSelectionModal extends Modal {
|
|||
|
||||
// 資料夾圖示與名稱
|
||||
const icon = document.createElement('span');
|
||||
icon.textContent = '📁 ';
|
||||
icon.textContent = `${customFolderIcon} `;
|
||||
folderOption.appendChild(icon);
|
||||
|
||||
const nameSpan = document.createElement('span');
|
||||
|
|
@ -423,6 +432,48 @@ export class FolderSelectionModal extends Modal {
|
|||
}
|
||||
}
|
||||
|
||||
// 將 modal 定位到按鈕下方,類似 Chrome popup 樣式
|
||||
positionAsPopup() {
|
||||
if (!this.buttonElement) return;
|
||||
|
||||
const buttonRect = this.buttonElement.getBoundingClientRect();
|
||||
const modalEl = this.modalEl;
|
||||
const contentEl = this.contentEl;
|
||||
|
||||
// 設置 modal 的基本樣式
|
||||
modalEl.addClass('ge-popup-modal-reset');
|
||||
|
||||
// 添加 popup 內容樣式類別
|
||||
contentEl.addClass('ge-popup-content');
|
||||
|
||||
// 計算位置
|
||||
const viewportWidth = window.innerWidth;
|
||||
const viewportHeight = window.innerHeight;
|
||||
|
||||
// 預設位置:按鈕下方中心對齊
|
||||
let left = buttonRect.left + (buttonRect.width / 2) - 150; // 150 是 modal 寬度的一半
|
||||
let top = buttonRect.bottom + 8; // 8px 間距
|
||||
|
||||
// 檢查右側邊界
|
||||
if (left + 300 > viewportWidth) {
|
||||
left = viewportWidth - 300 - 16;
|
||||
}
|
||||
|
||||
// 檢查左側邊界
|
||||
if (left < 16) {
|
||||
left = 16;
|
||||
}
|
||||
|
||||
// 檢查下方空間,如果不夠則顯示在上方
|
||||
if (top + 400 > viewportHeight && buttonRect.top - 400 > 0) {
|
||||
top = buttonRect.top - 8;
|
||||
}
|
||||
|
||||
// 應用位置
|
||||
modalEl.style.left = `${left}px`;
|
||||
modalEl.style.top = `${top}px`;
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
|
|
|
|||
48
src/modal/moveFolderSuggestModal.ts
Normal file
48
src/modal/moveFolderSuggestModal.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import { SuggestModal, TFolder, normalizePath } from 'obsidian';
|
||||
import GridExplorerPlugin from '../main';
|
||||
import { GridView } from '../GridView';
|
||||
|
||||
export class moveFolderSuggestModal extends SuggestModal<string> {
|
||||
private readonly allPaths: string[];
|
||||
|
||||
constructor(
|
||||
private readonly plugin: GridExplorerPlugin,
|
||||
private readonly folder: TFolder,
|
||||
private readonly view: GridView
|
||||
) {
|
||||
super(plugin.app);
|
||||
// 預先快取所有資料夾路徑以加速篩選
|
||||
this.allPaths = this.app.vault.getAllFolders().map(f => f.path).sort((a, b) => a.localeCompare(b));
|
||||
this.setPlaceholder('/');
|
||||
// 自動聚焦輸入框,讓使用者可以直接打字
|
||||
this.inputEl.focus();
|
||||
}
|
||||
|
||||
getSuggestions(query: string): string[] {
|
||||
const lower = query.toLowerCase();
|
||||
const filtered = this.allPaths.filter(p => p.toLowerCase().includes(lower));
|
||||
if ('/'.includes(lower) && !filtered.includes('/')) {
|
||||
// 始終允許根目錄
|
||||
filtered.unshift('/');
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
|
||||
renderSuggestion(value: string, el: HTMLElement): void {
|
||||
el.setText(value);
|
||||
}
|
||||
|
||||
async onChooseSuggestion(value: string): Promise<void> {
|
||||
try {
|
||||
const dest = value === '/' ? '' : value.replace(/\/$/, '');
|
||||
const newPath = normalizePath(dest ? `${dest}/${this.folder.name}` : this.folder.name);
|
||||
if (newPath === this.folder.path) return;
|
||||
|
||||
await this.app.fileManager.renameFile(this.folder, newPath);
|
||||
// 給檔案系統一點時間處理,然後重新整理視圖
|
||||
setTimeout(() => this.view.render(), 100);
|
||||
} catch (err) {
|
||||
console.error('Failed to move folder', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,16 +5,23 @@ import { t } from '../translations';
|
|||
export class SearchModal extends Modal {
|
||||
gridView: GridView;
|
||||
defaultQuery: string;
|
||||
constructor(app: App, gridView: GridView, defaultQuery: string) {
|
||||
buttonElement: HTMLElement | undefined;
|
||||
constructor(app: App, gridView: GridView, defaultQuery: string, buttonElement?: HTMLElement) {
|
||||
super(app);
|
||||
this.gridView = gridView;
|
||||
this.defaultQuery = defaultQuery;
|
||||
this.buttonElement = buttonElement;
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
new Setting(contentEl).setName(t('search')).setHeading();
|
||||
|
||||
// 如果有提供按鈕元素,則設置為 popup 樣式
|
||||
if (this.buttonElement) {
|
||||
this.modalEl.addClass('ge-popup-modal');
|
||||
this.positionAsPopup();
|
||||
}
|
||||
|
||||
// 創建搜尋輸入框容器
|
||||
const searchContainer = contentEl.createDiv('ge-search-container');
|
||||
|
|
@ -326,9 +333,53 @@ export class SearchModal extends Modal {
|
|||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
|
||||
private positionAsPopup() {
|
||||
if (!this.buttonElement) return;
|
||||
|
||||
const modalEl = this.modalEl;
|
||||
const contentEl = this.contentEl;
|
||||
|
||||
// 設置 modal 的基本樣式
|
||||
modalEl.addClass('ge-popup-modal-reset');
|
||||
|
||||
// 添加 popup 內容樣式類別
|
||||
contentEl.addClass('ge-popup-content');
|
||||
|
||||
// 計算按鈕位置
|
||||
const buttonRect = this.buttonElement.getBoundingClientRect();
|
||||
const viewportWidth = window.innerWidth;
|
||||
const viewportHeight = window.innerHeight;
|
||||
|
||||
// 設置初始位置(按鈕下方)
|
||||
let top = buttonRect.bottom + 4;
|
||||
let left = buttonRect.left + (buttonRect.width / 2) - 150; // 300px 寬度的一半
|
||||
|
||||
// 檢查右側邊界
|
||||
if (left + 300 > viewportWidth) {
|
||||
left = viewportWidth - 300 - 10;
|
||||
}
|
||||
|
||||
// 檢查左側邊界
|
||||
if (left < 10) {
|
||||
left = 10;
|
||||
}
|
||||
|
||||
// 檢查下方空間,如果不夠則顯示在上方
|
||||
const estimatedHeight = 400; // 預估高度
|
||||
if (top + estimatedHeight > viewportHeight && buttonRect.top - estimatedHeight > 0) {
|
||||
top = buttonRect.top - estimatedHeight - 4;
|
||||
}
|
||||
|
||||
// 應用位置
|
||||
modalEl.style.position = 'fixed';
|
||||
modalEl.style.top = `${top}px`;
|
||||
modalEl.style.left = `${left}px`;
|
||||
modalEl.style.transform = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// 顯示搜尋 modal
|
||||
export function showSearchModal(app:App, gridView: GridView, defaultQuery = '') {
|
||||
new SearchModal(app, gridView, defaultQuery).open();
|
||||
export function showSearchModal(app:App, gridView: GridView, defaultQuery = '', buttonElement?: HTMLElement) {
|
||||
new SearchModal(app, gridView, defaultQuery, buttonElement).open();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1126,20 +1126,75 @@ export class GridExplorerSettingTab extends PluginSettingTab {
|
|||
|
||||
containerEl.appendChild(ignoredFolderPatternsContainer);
|
||||
|
||||
containerEl.createEl('h3', { text: t('reset_to_default'), attr: { style: 'margin-top: 40px;' } });
|
||||
// 設定檔管理 (Reset / Export / Import)
|
||||
containerEl.createEl('h3', { text: t('config_management'), attr: { style: 'margin-top: 40px;' } });
|
||||
|
||||
// 回復預設值按鈕
|
||||
new Setting(containerEl)
|
||||
.setName(t('reset_to_default'))
|
||||
.setDesc(t('reset_to_default_desc'))
|
||||
.setName(t('config_management'))
|
||||
.setDesc(t('config_management_desc'))
|
||||
// Reset to default
|
||||
.addButton(button => button
|
||||
.setButtonText(t('reset'))
|
||||
.setButtonText(t('reset_to_default'))
|
||||
.setWarning()
|
||||
.onClick(async () => {
|
||||
this.plugin.settings = { ...DEFAULT_SETTINGS };
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
new Notice(t('settings_reset_notice'));
|
||||
}))
|
||||
// Export settings to JSON file
|
||||
.addButton(button => button
|
||||
.setButtonText(t('export'))
|
||||
.setTooltip(t('export'))
|
||||
.onClick(() => {
|
||||
const data = JSON.stringify(this.plugin.settings, null, 2);
|
||||
const blob = new Blob([data], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'grid-explorer-settings.json';
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}))
|
||||
// Import settings from JSON file
|
||||
.addButton(button => button
|
||||
.setButtonText(t('import'))
|
||||
.setTooltip(t('import'))
|
||||
.onClick(() => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.json';
|
||||
input.onchange = async (e) => {
|
||||
const files = (e.target as HTMLInputElement).files;
|
||||
if (!files || files.length === 0) {
|
||||
return;
|
||||
}
|
||||
const file = files[0];
|
||||
const reader = new FileReader();
|
||||
reader.onload = async (evt) => {
|
||||
if (!evt.target || typeof evt.target.result !== 'string') {
|
||||
new Notice(t('import_failed'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const content = evt.target.result;
|
||||
const importedSettings = JSON.parse(content);
|
||||
if (importedSettings && typeof importedSettings === 'object') {
|
||||
this.plugin.settings = { ...DEFAULT_SETTINGS, ...importedSettings } as typeof DEFAULT_SETTINGS;
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
new Notice(t('import_success'));
|
||||
} else {
|
||||
new Notice(t('import_failed'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Grid Explorer: Error importing settings', error);
|
||||
new Notice(t('import_failed'));
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
};
|
||||
input.click();
|
||||
}));
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ export const TRANSLATIONS: Translations = {
|
|||
'new_folder': '新增資料夾',
|
||||
'new_canvas': '新增畫布',
|
||||
'delete_folder': '刪除資料夾',
|
||||
'move_folder': '搬移資料夾',
|
||||
'select_destination_folder': '選擇目標資料夾',
|
||||
'untitled': '未命名',
|
||||
'files': '個檔案',
|
||||
'add': '新增',
|
||||
|
|
@ -136,8 +138,9 @@ export const TRANSLATIONS: Translations = {
|
|||
'intercept_breadcrumb_clicks': '攔截筆記導航列點擊事件',
|
||||
'intercept_breadcrumb_clicks_desc': '啟用後會攔截筆記導航列點擊事件,並在網格視圖中打開路徑',
|
||||
'reset_to_default' : '重置為預設值',
|
||||
'reset_to_default_desc': '將所有設定重置為預設值',
|
||||
'settings_reset_notice': '設定值已重置為預設值',
|
||||
'config_management': '設定檔管理',
|
||||
'config_management_desc': '管理設定檔的重置、匯出和匯入',
|
||||
'ignored_folders_settings': '忽略資料夾設定',
|
||||
'display_mode_settings': '顯示模式設定',
|
||||
'custom_mode_settings': '自訂模式設定',
|
||||
|
|
@ -145,7 +148,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'export': '匯出',
|
||||
'import': '匯入',
|
||||
'no_custom_modes_to_export': '沒有可匯出的自訂模式',
|
||||
'import_success': '自訂模式匯入成功',
|
||||
'import_success': '匯入成功',
|
||||
'import_error': '匯入失敗:無效的檔案格式',
|
||||
'edit_custom_mode': '編輯自訂模式',
|
||||
'custom_mode': '自訂模式',
|
||||
|
|
@ -302,7 +305,9 @@ export const TRANSLATIONS: Translations = {
|
|||
'new_note': 'New note',
|
||||
'new_folder': 'New folder',
|
||||
'new_canvas': 'New canvas',
|
||||
'delete_folder': 'Delete folder',
|
||||
'delete_folder': 'Delete Folder',
|
||||
'move_folder': 'Move Folder',
|
||||
'select_destination_folder': 'Select destination folder',
|
||||
'untitled': 'Untitled',
|
||||
'files': 'files',
|
||||
'add': 'Add',
|
||||
|
|
@ -400,8 +405,9 @@ export const TRANSLATIONS: Translations = {
|
|||
'intercept_breadcrumb_clicks': 'Intercept breadcrumb clicks',
|
||||
'intercept_breadcrumb_clicks_desc': 'When enabled, breadcrumb clicks will be intercepted and the path will be opened in the grid view',
|
||||
'reset_to_default': 'Reset to default',
|
||||
'reset_to_default_desc': 'Reset all settings to default values',
|
||||
'settings_reset_notice': 'Settings have been reset to default values',
|
||||
'config_management': 'Config management',
|
||||
'config_management_desc': 'Manage config reset, export and import',
|
||||
'ignored_folders_settings': 'Ignore folders settings',
|
||||
'display_mode_settings': 'Display mode settings',
|
||||
'custom_mode_settings': 'Custom Mode Settings',
|
||||
|
|
@ -409,7 +415,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'export': 'Export',
|
||||
'import': 'Import',
|
||||
'no_custom_modes_to_export': 'No custom modes to export',
|
||||
'import_success': 'Custom modes imported successfully',
|
||||
'import_success': 'Import success',
|
||||
'import_error': 'Import failed: Invalid file format',
|
||||
'edit_custom_mode': 'Edit Custom Mode',
|
||||
'custom_mode': 'Custom Mode',
|
||||
|
|
@ -664,8 +670,9 @@ export const TRANSLATIONS: Translations = {
|
|||
'intercept_breadcrumb_clicks': '拦截笔记导航栏点击事件',
|
||||
'intercept_breadcrumb_clicks_desc': '启用后会拦截笔记导航栏点击事件,并在网格视图中打开路径',
|
||||
'reset_to_default': '重置为默认值',
|
||||
'reset_to_default_desc': '将所有设置重置为默认值',
|
||||
'settings_reset_notice': '设置值已重置为默认值',
|
||||
'config_management': '配置管理',
|
||||
'config_management_desc': '管理配置的重置、导出和导入',
|
||||
'ignored_folders_settings': '忽略文件夹设置',
|
||||
'display_mode_settings': '显示模式设置',
|
||||
'custom_mode_settings': '自定义模式设置',
|
||||
|
|
@ -673,7 +680,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'export': '导出',
|
||||
'import': '导入',
|
||||
'no_custom_modes_to_export': '没有可导出的自定义模式',
|
||||
'import_success': '自定义模式导入成功',
|
||||
'import_success': '导入成功',
|
||||
'import_error': '导入失败:无效的文件格式',
|
||||
'edit_custom_mode': '编辑自定义模式',
|
||||
'custom_mode': '自定义模式',
|
||||
|
|
@ -928,8 +935,9 @@ export const TRANSLATIONS: Translations = {
|
|||
'intercept_breadcrumb_clicks': 'パンくずリストのクリックをインターセプト',
|
||||
'intercept_breadcrumb_clicks_desc': '有効にすると、パンくずリストのクリックがインターセプトされ、パスがグリッドビューで開かれます',
|
||||
'reset_to_default': 'デフォルトに戻す',
|
||||
'reset_to_default_desc': 'すべての設定をデフォルト値に戻',
|
||||
'settings_reset_notice': '設定値がデフォルト値にリセットされました',
|
||||
'config_management': '設定管理',
|
||||
'config_management_desc': '管理設定のリセット、エクスポート、インポート',
|
||||
'ignored_folders_settings': '無視するフォルダ設定',
|
||||
'display_mode_settings': '表示モード設定',
|
||||
'custom_mode_settings': 'カスタムモード設定',
|
||||
|
|
@ -937,7 +945,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'export': 'エクスポート',
|
||||
'import': 'インポート',
|
||||
'no_custom_modes_to_export': 'エクスポートするカスタムモードがありません',
|
||||
'import_success': 'カスタムモードをインポートしました',
|
||||
'import_success': 'インポート成功',
|
||||
'import_error': 'インポート失敗:無効なファイル形式',
|
||||
'edit_custom_mode': 'カスタムモードを編集',
|
||||
'custom_mode': 'カスタムモード',
|
||||
|
|
@ -1193,8 +1201,9 @@ export const TRANSLATIONS: Translations = {
|
|||
'intercept_breadcrumb_clicks': 'Перехватывать клики по навигационной цепочке',
|
||||
'intercept_breadcrumb_clicks_desc': 'При включении клики по навигационной цепочке будут перехватываться, и путь будет открываться в виде сетки',
|
||||
'reset_to_default': 'Сбросить на значения по умолчанию',
|
||||
'reset_to_default_desc': 'Сбросить все настройки на значения по умолчанию',
|
||||
'settings_reset_notice': 'Настройки сброшены на значения по умолчанию',
|
||||
'config_management': 'Конфигурация управления',
|
||||
'config_management_desc': 'Управление конфигурацией (сброс, экспорт, импорт)',
|
||||
'ignored_folders_settings': 'Настройки игнорируемых папок',
|
||||
'display_mode_settings': 'Настройки режима отображения',
|
||||
'custom_mode_settings': 'Настройки режима отображения',
|
||||
|
|
@ -1202,7 +1211,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'export': 'Экспорт',
|
||||
'import': 'Импорт',
|
||||
'no_custom_modes_to_export': 'Нет режимов отображения для экспорта',
|
||||
'import_success': 'Режимы отображения успешно импортированы',
|
||||
'import_success': 'Импорт успешно',
|
||||
'import_error': 'Ошибка импорта: недопустимый формат файла',
|
||||
'edit_custom_mode': 'Редактировать режим отображения',
|
||||
'custom_mode': 'Режим отображения',
|
||||
|
|
@ -1457,8 +1466,9 @@ export const TRANSLATIONS: Translations = {
|
|||
'intercept_breadcrumb_clicks': 'Перехоплювати кліки за навігаційним ланцюжком',
|
||||
'intercept_breadcrumb_clicks_desc': 'При ввімкненні кліки за навігаційним ланцюжком будуть перехоплюватися, і шлях буде відкриватися у вигляді сітки',
|
||||
'reset_to_default': 'Скинути до стандартних значень',
|
||||
'reset_to_default_desc': 'Скинути всі налаштування до стандартних значень',
|
||||
'settings_reset_notice': 'Налаштування скинуто до стандартних значень',
|
||||
'config_management': 'Конфігурація управління',
|
||||
'config_management_desc': 'Управління конфігурацією (скидання, експорт, імпорт)',
|
||||
'ignored_folders_settings': 'Налаштування ігнорованих папок',
|
||||
'display_mode_settings': 'Налаштування режиму відображення',
|
||||
'custom_mode_settings': 'Налаштування режиму відображення',
|
||||
|
|
@ -1466,7 +1476,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'export': 'Експорт',
|
||||
'import': 'Імпорт',
|
||||
'no_custom_modes_to_export': 'Немає режимів відображення для експорту',
|
||||
'import_success': 'Режими відображення успішно імпортовані',
|
||||
'import_success': 'Імпорт успішний',
|
||||
'import_error': 'Помилка імпорту: недопустимий формат файлу',
|
||||
'edit_custom_mode': 'Редагувати режим відображення',
|
||||
'custom_mode': 'Режим відображення',
|
||||
|
|
|
|||
169
styles.css
169
styles.css
|
|
@ -823,6 +823,7 @@ a.ge-current-folder:hover {
|
|||
padding-right: 24px;
|
||||
height: 24px;
|
||||
border: none;
|
||||
font-size: var(--font-ui-small);
|
||||
}
|
||||
|
||||
.ge-search-tag-delete-button {
|
||||
|
|
@ -863,6 +864,7 @@ a.ge-current-folder:hover {
|
|||
overflow-y: auto;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
font-size: var(--font-ui-small);
|
||||
}
|
||||
|
||||
.ge-search-tag-suggestion-item {
|
||||
|
|
@ -884,7 +886,7 @@ a.ge-current-folder:hover {
|
|||
border-radius: 4px;
|
||||
background-color: var(--background-primary);
|
||||
color: var(--text-normal);
|
||||
font-size: 14px;
|
||||
font-size: var(--font-ui-medium);
|
||||
outline: none;
|
||||
transition: border-color 0.2s;
|
||||
padding-right: 25px;
|
||||
|
|
@ -901,8 +903,9 @@ a.ge-current-folder:hover {
|
|||
/* 搜尋選項容器 Search Options Container */
|
||||
.ge-search-options-container {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
margin: 4px;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
/* 搜尋範圍設定樣式 Search Scope Container */
|
||||
|
|
@ -910,7 +913,6 @@ a.ge-current-folder:hover {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.ge-search-scope-checkbox {
|
||||
|
|
@ -921,6 +923,7 @@ a.ge-current-folder:hover {
|
|||
cursor: pointer;
|
||||
user-select: none;
|
||||
color: var(--text-normal);
|
||||
font-size: var(--font-ui-small);
|
||||
}
|
||||
|
||||
/* 搜尋媒體檔案設定樣式 Search Media Files Container */
|
||||
|
|
@ -928,7 +931,6 @@ a.ge-current-folder:hover {
|
|||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.ge-search-media-files-checkbox {
|
||||
|
|
@ -939,6 +941,7 @@ a.ge-current-folder:hover {
|
|||
cursor: pointer;
|
||||
user-select: none;
|
||||
color: var(--text-normal);
|
||||
font-size: var(--font-ui-small);
|
||||
}
|
||||
|
||||
.ge-button-container {
|
||||
|
|
@ -1446,4 +1449,160 @@ a.ge-current-folder:hover {
|
|||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------- */
|
||||
|
||||
/* Popup Modal Styles */
|
||||
.ge-popup-modal {
|
||||
background: transparent !important;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15) !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.ge-popup-modal-reset {
|
||||
position: fixed;
|
||||
top: auto;
|
||||
left: auto;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
transform: none;
|
||||
max-width: 300px;
|
||||
width: auto;
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ge-popup-modal-reset .modal-close-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ge-popup-modal-reset .modal-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.is-phone .ge-popup-modal-reset.modal {
|
||||
max-width: fit-content;
|
||||
max-height: fit-content;
|
||||
}
|
||||
|
||||
.ge-popup-content {
|
||||
background-color: var(--background-primary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||
padding: 10px 8px;
|
||||
position: relative;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
min-width: 250px;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
/* Popup modal 中的資料夾選項樣式 */
|
||||
.ge-popup-content .ge-grid-view-folder-option {
|
||||
margin: 2px 0;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
font-size: var(--font-ui-medium);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.ge-popup-content .ge-grid-view-folder-option:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
}
|
||||
|
||||
.ge-popup-content .ge-grid-view-folder-option.selected {
|
||||
background-color: var(--background-modifier-active);
|
||||
}
|
||||
|
||||
/* Popup modal 中的搜尋框樣式 */
|
||||
.ge-popup-content .ge-folder-search-container {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.ge-popup-content .ge-folder-search-input {
|
||||
width: 100%;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: 4px;
|
||||
background-color: var(--background-primary);
|
||||
color: var(--text-normal);
|
||||
font-size: var(--font-ui-medium);
|
||||
}
|
||||
|
||||
.ge-popup-content .ge-folder-search-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--interactive-accent);
|
||||
box-shadow: 0 0 0 2px rgba(var(--interactive-accent-rgb), 0.2);
|
||||
}
|
||||
|
||||
.ge-popup-content .ge-folder-options-container {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* 搜尋 modal popup 樣式 */
|
||||
.ge-popup-content .ge-search-container {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.ge-popup-content .ge-search-input-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ge-popup-content .ge-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-medium);
|
||||
}
|
||||
|
||||
.ge-popup-content .ge-search-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--interactive-accent);
|
||||
box-shadow: 0 0 0 2px rgba(var(--interactive-accent-rgb), 0.2);
|
||||
}
|
||||
|
||||
.ge-popup-content .ge-search-clear-button {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 2px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.ge-popup-content .ge-search-clear-button:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
color: var(--text-normal);
|
||||
}
|
||||
|
||||
.ge-popup-content .ge-search-tag-suggestions {
|
||||
margin-top: 8px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.ge-popup-content .setting-item {
|
||||
border: none;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.ge-popup-content .setting-item-name {
|
||||
font-size: var(--font-ui-medium);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"2.6.6": "1.1.0"
|
||||
"2.7.0": "1.1.0"
|
||||
}
|
||||
Loading…
Reference in a new issue