mirror of
https://github.com/devon22/obsidian-gridexplorer.git
synced 2026-07-22 05:38:16 +00:00
2.9.12
This commit is contained in:
parent
6415d57234
commit
eb78e913ad
11 changed files with 553 additions and 256 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "gridexplorer",
|
||||
"name": "GridExplorer",
|
||||
"version": "2.9.11",
|
||||
"version": "2.9.12",
|
||||
"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.9.11",
|
||||
"version": "2.9.12",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "gridexplorer",
|
||||
"version": "2.9.11",
|
||||
"version": "2.9.12",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.6",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "gridexplorer",
|
||||
"version": "2.9.11",
|
||||
"version": "2.9.12",
|
||||
"description": "Browse note files in a grid view.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { TFile, TFolder, WorkspaceLeaf, Menu, setIcon, Platform, normalizePath, ItemView, EventRef, FuzzySuggestModal, parseLinktext } from 'obsidian';
|
||||
import { TFile, TFolder, WorkspaceLeaf, Menu, setIcon, Platform, normalizePath, ItemView, EventRef, FuzzySuggestModal, parseLinktext, Notice } from 'obsidian';
|
||||
import GridExplorerPlugin from './main';
|
||||
import { GridView } from './GridView';
|
||||
import { isFolderIgnored, isImageFile, isVideoFile, isAudioFile, isMediaFile } from './fileUtils';
|
||||
|
|
@ -9,6 +9,8 @@ import { showFolderRenameModal } from './modal/folderRenameModal';
|
|||
import { showFolderMoveModal } from './modal/folderMoveModal';
|
||||
import { FloatingAudioPlayer } from './floatingAudioPlayer';
|
||||
import { MediaModal } from './modal/mediaModal';
|
||||
import { ShortcutSelectionModal } from './modal/shortcutSelectionModal';
|
||||
import { createNewNote, createNewFolder, createNewCanvas, createNewBase, createShortcut } from './createItemUtils';
|
||||
import { t } from './translations';
|
||||
|
||||
// 探索器視圖類型常數
|
||||
|
|
@ -847,12 +849,15 @@ export class ExplorerView extends ItemView {
|
|||
if (!foldersExpanded) foldersChildren.addClass('is-collapsed');
|
||||
|
||||
foldersHeader.addEventListener('click', async (evt) => {
|
||||
// 判斷點擊的是切換箭頭、資料夾名稱還是空白區域
|
||||
if ((evt.target as HTMLElement).closest('.ge-explorer-folder-toggle')) {
|
||||
// 點擊箭頭:只展開/收合
|
||||
const newExpanded = !this.isExpanded(foldersGroupKey);
|
||||
this.setExpanded(foldersGroupKey, newExpanded);
|
||||
setIcon(foldersToggle, newExpanded ? 'chevron-down' : 'chevron-right');
|
||||
foldersChildren.toggleClass('is-collapsed', !newExpanded);
|
||||
} else {
|
||||
} else if ((evt.target as HTMLElement).closest('.ge-explorer-folder-name')) {
|
||||
// 點擊資料夾名稱:可能展開/收合,也可能開啟 GridView
|
||||
// Ctrl/Meta + 點擊:在新的 GridView 分頁中開啟
|
||||
if (evt.ctrlKey || evt.metaKey) {
|
||||
this.openFolderInNewView(rootPath);
|
||||
|
|
@ -877,6 +882,12 @@ export class ExplorerView extends ItemView {
|
|||
const root = this.app.vault.getRoot();
|
||||
const view = await this.plugin.activateView();
|
||||
if (view instanceof GridView) await view.setSource('folder', (root as any).path ?? '/');
|
||||
} else {
|
||||
// 點擊空白區域:展開/收合
|
||||
const newExpanded = !this.isExpanded(foldersGroupKey);
|
||||
this.setExpanded(foldersGroupKey, newExpanded);
|
||||
setIcon(foldersToggle, newExpanded ? 'chevron-down' : 'chevron-right');
|
||||
foldersChildren.toggleClass('is-collapsed', !newExpanded);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -965,6 +976,11 @@ export class ExplorerView extends ItemView {
|
|||
this.highlightActiveFolder(folder, header);
|
||||
this.attachDropTarget(header, folder.path);
|
||||
|
||||
// 如果資料夾沒有可見的子目錄,添加 ge-no-children 類別
|
||||
if (!this.hasVisibleChildren(folder)) {
|
||||
header.addClass('ge-no-children');
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
|
|
@ -1050,7 +1066,7 @@ export class ExplorerView extends ItemView {
|
|||
|
||||
/**
|
||||
* 處理資料夾的點擊事件
|
||||
* 區分點擊切換箭頭和點擊資料夾名稱的不同行為
|
||||
* 區分點擊切換箭頭、資料夾名稱和空白區域的不同行為
|
||||
* @param evt 點擊事件
|
||||
* @param folder 被點擊的資料夾
|
||||
* @param header 資料夾標頭元素
|
||||
|
|
@ -1059,13 +1075,24 @@ export class ExplorerView extends ItemView {
|
|||
private handleFolderClick(evt: Event, folder: TFolder, header: HTMLElement, childrenContainer: HTMLElement) {
|
||||
const toggle = header.querySelector('.ge-explorer-folder-toggle') as HTMLElement;
|
||||
|
||||
// 判斷點擊的是切換箭頭還是資料夾名稱
|
||||
// 判斷點擊的是切換箭頭、資料夾名稱還是空白區域
|
||||
if ((evt.target as HTMLElement).closest('.ge-explorer-folder-toggle')) {
|
||||
// 點擊箭頭:只展開/收合,不開啟 GridView
|
||||
this.handleToggleClick(folder, toggle, childrenContainer);
|
||||
} else {
|
||||
} else if ((evt.target as HTMLElement).closest('.ge-explorer-folder-name')) {
|
||||
// 點擊資料夾名稱:可能展開/收合,也可能開啟 GridView
|
||||
this.handleFolderNameClick(evt as MouseEvent, folder, header, toggle, childrenContainer);
|
||||
} else {
|
||||
// 點擊空白區域:如果沒有子資料夾且未選取則開啟 GridView,已選取則無作用;有子資料夾則展開/收合
|
||||
const isActive = header.hasClass('is-active');
|
||||
if (!this.hasVisibleChildren(folder)) {
|
||||
if (!isActive) {
|
||||
this.openFolderInGrid(folder.path);
|
||||
}
|
||||
// 如果已選取且沒有子資料夾,則不做任何動作
|
||||
} else {
|
||||
this.handleToggleClick(folder, toggle, childrenContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1782,11 +1809,73 @@ export class ExplorerView extends ItemView {
|
|||
});
|
||||
menu.addSeparator();
|
||||
|
||||
// 添加新增筆記相關選項
|
||||
this.addNewItemMenuItems(menu, folder);
|
||||
menu.addSeparator();
|
||||
|
||||
this.addFolderNoteMenuItems(menu, folder);
|
||||
menu.addSeparator();
|
||||
this.addFolderManagementMenuItems(menu, folder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加新增項目的右鍵選單項目
|
||||
* @param menu 右鍵選單
|
||||
* @param folder 資料夾
|
||||
*/
|
||||
private addNewItemMenuItems(menu: Menu, folder: TFolder) {
|
||||
// 新增筆記
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('new_note'))
|
||||
.setIcon('square-pen')
|
||||
.onClick(async () => {
|
||||
await createNewNote(this.app, folder.path);
|
||||
});
|
||||
});
|
||||
|
||||
// 新增資料夾
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('new_folder'))
|
||||
.setIcon('folder')
|
||||
.onClick(async () => {
|
||||
await createNewFolder(this.app, folder.path, () => {
|
||||
this.scheduleRender();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 新增畫布
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('new_canvas'))
|
||||
.setIcon('layout-dashboard')
|
||||
.onClick(async () => {
|
||||
await createNewCanvas(this.app, folder.path);
|
||||
});
|
||||
});
|
||||
|
||||
// 新增 base
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('new_base'))
|
||||
.setIcon('layout-dashboard')
|
||||
.onClick(async () => {
|
||||
await createNewBase(this.app, folder.path);
|
||||
});
|
||||
});
|
||||
|
||||
// 新增捷徑
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('new_shortcut'))
|
||||
.setIcon('shuffle')
|
||||
.onClick(async () => {
|
||||
const modal = new ShortcutSelectionModal(this.app, this.plugin, async (option) => {
|
||||
await createShortcut(this.app, folder.path, option);
|
||||
});
|
||||
modal.open();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加資料夾筆記的右鍵選單項目
|
||||
* @param menu 右鍵選單
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ export class GridView extends ItemView {
|
|||
renderToken: number = 0; // 用於取消尚未完成之批次排程的遞增令牌
|
||||
isShowingNote: boolean = false; // 是否正在顯示筆記
|
||||
noteViewContainer: HTMLElement | null = null; // 筆記檢視容器
|
||||
eventCleanupFunctions: (() => void)[] = []; // 存儲事件清理函數
|
||||
|
||||
constructor(leaf: WorkspaceLeaf, plugin: GridExplorerPlugin) {
|
||||
super(leaf);
|
||||
|
|
@ -305,6 +306,23 @@ export class GridView extends ItemView {
|
|||
await this.render();
|
||||
}
|
||||
|
||||
// 清理事件監聽器
|
||||
onunload() {
|
||||
// 清理所有事件監聽器
|
||||
this.eventCleanupFunctions.forEach(cleanup => {
|
||||
try {
|
||||
cleanup();
|
||||
} catch (error) {
|
||||
console.warn('GridExplorer: Error during event cleanup:', error);
|
||||
}
|
||||
});
|
||||
this.eventCleanupFunctions = [];
|
||||
|
||||
// 清理檔案監聽器(FileWatcher 的事件監聽器會在插件卸載時自動清理)
|
||||
|
||||
super.onunload();
|
||||
}
|
||||
|
||||
// 渲染網格
|
||||
async render() {
|
||||
|
||||
|
|
@ -315,6 +333,16 @@ export class GridView extends ItemView {
|
|||
selectedFilePath = selectedItem.dataset.filePath || null;
|
||||
}
|
||||
|
||||
// 清理之前的事件監聽器
|
||||
this.eventCleanupFunctions.forEach(cleanup => {
|
||||
try {
|
||||
cleanup();
|
||||
} catch (error) {
|
||||
console.warn('GridExplorer: Error during event cleanup:', error);
|
||||
}
|
||||
});
|
||||
this.eventCleanupFunctions = [];
|
||||
|
||||
// 清空整個容器
|
||||
this.containerEl.empty();
|
||||
|
||||
|
|
@ -1207,9 +1235,11 @@ export class GridView extends ItemView {
|
|||
if (Platform.isDesktop && file.extension === 'md' && !this.plugin.settings.showNoteInGrid) {
|
||||
let triggeredInHover = false;
|
||||
let isHovering = false;
|
||||
let isMouseDown = false; // 追蹤滑鼠按下狀態
|
||||
let keydownListener: ((e: KeyboardEvent) => void) | null = null;
|
||||
|
||||
const trigger = () => {
|
||||
if (triggeredInHover) return;
|
||||
if (triggeredInHover || isMouseDown) return; // 如果滑鼠按下則不觸發
|
||||
triggeredInHover = true;
|
||||
if (!this.openShortcutFile(file)) {
|
||||
this.showNoteInGrid(file);
|
||||
|
|
@ -1218,25 +1248,66 @@ export class GridView extends ItemView {
|
|||
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
// 只有在滑鼠確實懸停在此項目上且按下 Ctrl 時才觸發
|
||||
if (isHovering && e.ctrlKey) {
|
||||
trigger();
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// 且滑鼠沒有按下(避免干擾 Ctrl+click)
|
||||
if (isHovering && e.ctrlKey && !isMouseDown) {
|
||||
// 短暫延遲以確保不是 Ctrl+click 操作
|
||||
setTimeout(() => {
|
||||
if (isHovering && !triggeredInHover && !isMouseDown) {
|
||||
trigger();
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseDown = () => {
|
||||
isMouseDown = true;
|
||||
triggeredInHover = true; // 防止在點擊過程中觸發 hover 功能
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
isMouseDown = false;
|
||||
// 重置觸發狀態,但稍微延遲以避免立即重新觸發
|
||||
setTimeout(() => {
|
||||
if (isHovering) {
|
||||
triggeredInHover = false;
|
||||
}
|
||||
}, 50);
|
||||
};
|
||||
|
||||
const onMouseEnter = () => {
|
||||
triggeredInHover = false;
|
||||
isHovering = true;
|
||||
document.addEventListener('keydown', onKeyDown, { capture: true });
|
||||
if (!keydownListener) {
|
||||
keydownListener = onKeyDown;
|
||||
document.addEventListener('keydown', keydownListener, { capture: true });
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseLeave = () => {
|
||||
isHovering = false;
|
||||
document.removeEventListener('keydown', onKeyDown, { capture: true } as any);
|
||||
isMouseDown = false;
|
||||
if (keydownListener) {
|
||||
document.removeEventListener('keydown', keydownListener, { capture: true });
|
||||
keydownListener = null;
|
||||
}
|
||||
triggeredInHover = false;
|
||||
};
|
||||
|
||||
fileEl.addEventListener('mouseenter', onMouseEnter);
|
||||
fileEl.addEventListener('mouseleave', onMouseLeave);
|
||||
fileEl.addEventListener('mousedown', onMouseDown);
|
||||
fileEl.addEventListener('mouseup', onMouseUp);
|
||||
|
||||
// 添加清理函數到數組中
|
||||
this.eventCleanupFunctions.push(() => {
|
||||
if (keydownListener) {
|
||||
document.removeEventListener('keydown', keydownListener, { capture: true });
|
||||
}
|
||||
fileEl.removeEventListener('mouseenter', onMouseEnter);
|
||||
fileEl.removeEventListener('mouseleave', onMouseLeave);
|
||||
fileEl.removeEventListener('mousedown', onMouseDown);
|
||||
fileEl.removeEventListener('mouseup', onMouseUp);
|
||||
});
|
||||
}
|
||||
|
||||
// 點擊時開啟檔案
|
||||
|
|
|
|||
286
src/createItemUtils.ts
Normal file
286
src/createItemUtils.ts
Normal file
|
|
@ -0,0 +1,286 @@
|
|||
import { App, TFile, Notice } from 'obsidian';
|
||||
import { t } from './translations';
|
||||
|
||||
/**
|
||||
* 創建新筆記的共用函數
|
||||
* @param app Obsidian App 實例
|
||||
* @param folderPath 目標資料夾路徑
|
||||
*/
|
||||
export async function createNewNote(app: App, folderPath: string) {
|
||||
let newFileName = `${t('untitled')}.md`;
|
||||
let newFilePath = !folderPath || folderPath === '/' ? newFileName : `${folderPath}/${newFileName}`;
|
||||
|
||||
// 檢查檔案是否已存在,如果存在則遞增編號
|
||||
let counter = 1;
|
||||
while (app.vault.getAbstractFileByPath(newFilePath)) {
|
||||
newFileName = `${t('untitled')} ${counter}.md`;
|
||||
newFilePath = !folderPath || folderPath === '/' ? newFileName : `${folderPath}/${newFileName}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
try {
|
||||
// 建立新筆記
|
||||
const newFile = await app.vault.create(newFilePath, '');
|
||||
// 開啟新筆記
|
||||
await app.workspace.getLeaf().openFile(newFile);
|
||||
} catch (error) {
|
||||
console.error('An error occurred while creating a new note:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 創建新資料夾的共用函數
|
||||
* @param app Obsidian App 實例
|
||||
* @param folderPath 目標資料夾路徑
|
||||
* @param onSuccess 成功後的回調函數
|
||||
*/
|
||||
export async function createNewFolder(app: App, folderPath: string, onSuccess?: () => void) {
|
||||
let newFolderName = `${t('untitled')}`;
|
||||
let newFolderPath = !folderPath || folderPath === '/' ? newFolderName : `${folderPath}/${newFolderName}`;
|
||||
|
||||
// 檢查資料夾是否已存在,如果存在則遞增編號
|
||||
let counter = 1;
|
||||
while (app.vault.getAbstractFileByPath(newFolderPath)) {
|
||||
newFolderName = `${t('untitled')} ${counter}`;
|
||||
newFolderPath = !folderPath || folderPath === '/' ? newFolderName : `${folderPath}/${newFolderName}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
try {
|
||||
// 建立新資料夾
|
||||
await app.vault.createFolder(newFolderPath);
|
||||
// 執行成功回調
|
||||
if (onSuccess) {
|
||||
onSuccess();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('An error occurred while creating a new folder:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 創建新畫布的共用函數
|
||||
* @param app Obsidian App 實例
|
||||
* @param folderPath 目標資料夾路徑
|
||||
*/
|
||||
export async function createNewCanvas(app: App, folderPath: string) {
|
||||
let newFileName = `${t('untitled')}.canvas`;
|
||||
let newFilePath = !folderPath || folderPath === '/' ? newFileName : `${folderPath}/${newFileName}`;
|
||||
|
||||
// 檢查檔案是否已存在,如果存在則遞增編號
|
||||
let counter = 1;
|
||||
while (app.vault.getAbstractFileByPath(newFilePath)) {
|
||||
newFileName = `${t('untitled')} ${counter}.canvas`;
|
||||
newFilePath = !folderPath || folderPath === '/' ? newFileName : `${folderPath}/${newFileName}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
try {
|
||||
// 建立新畫布
|
||||
const newFile = await app.vault.create(newFilePath, '');
|
||||
// 開啟新畫布
|
||||
await app.workspace.getLeaf().openFile(newFile);
|
||||
} catch (error) {
|
||||
console.error('An error occurred while creating a new canvas:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 創建新 base 檔案的共用函數
|
||||
* @param app Obsidian App 實例
|
||||
* @param folderPath 目標資料夾路徑
|
||||
*/
|
||||
export async function createNewBase(app: App, folderPath: string) {
|
||||
let newFileName = `${t('untitled')}.base`;
|
||||
let newFilePath = !folderPath || folderPath === '/' ? newFileName : `${folderPath}/${newFileName}`;
|
||||
|
||||
// 檢查檔案是否已存在,如果存在則遞增編號
|
||||
let counter = 1;
|
||||
while (app.vault.getAbstractFileByPath(newFilePath)) {
|
||||
newFileName = `${t('untitled')} ${counter}.base`;
|
||||
newFilePath = !folderPath || folderPath === '/' ? newFileName : `${folderPath}/${newFileName}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
try {
|
||||
// 建立新 base 檔案
|
||||
const newFile = await app.vault.create(newFilePath, '');
|
||||
// 開啟新檔案
|
||||
await app.workspace.getLeaf().openFile(newFile);
|
||||
} catch (error) {
|
||||
console.error('An error occurred while creating a new base:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 創建捷徑檔案的共用函數
|
||||
* @param app Obsidian App 實例
|
||||
* @param folderPath 目標資料夾路徑
|
||||
* @param option 捷徑選項
|
||||
*/
|
||||
export async function createShortcut(
|
||||
app: App,
|
||||
folderPath: string,
|
||||
option: {
|
||||
type: 'mode' | 'folder' | 'file' | 'search' | 'uri';
|
||||
value: string;
|
||||
display: string;
|
||||
searchOptions?: {
|
||||
searchCurrentLocationOnly: boolean;
|
||||
searchFilesNameOnly: boolean;
|
||||
searchMediaFiles: boolean;
|
||||
};
|
||||
}
|
||||
) {
|
||||
try {
|
||||
// 生成不重複的檔案名稱
|
||||
let counter = 0;
|
||||
let shortcutName: string;
|
||||
|
||||
// 對於 URI 類型,使用特殊的檔名生成邏輯
|
||||
if (option.type === 'uri') {
|
||||
shortcutName = generateFilenameFromUri(option.value);
|
||||
} else {
|
||||
shortcutName = `${option.display}`;
|
||||
}
|
||||
|
||||
let newName = `${shortcutName}.md`;
|
||||
let newPath = !folderPath || folderPath === '/' ? newName : `${folderPath}/${newName}`;
|
||||
while (app.vault.getAbstractFileByPath(newPath)) {
|
||||
counter++;
|
||||
const baseName = option.type === 'uri' ? generateFilenameFromUri(option.value) : option.display;
|
||||
shortcutName = `${baseName} ${counter}`;
|
||||
newName = `${shortcutName}.md`;
|
||||
newPath = !folderPath || folderPath === '/' ? newName : `${folderPath}/${newName}`;
|
||||
}
|
||||
|
||||
// 創建新檔案
|
||||
const newFile = await app.vault.create(newPath, '');
|
||||
|
||||
// 使用 processFrontMatter 來更新 frontmatter
|
||||
await app.fileManager.processFrontMatter(newFile, (frontmatter: any) => {
|
||||
if (option.type === 'mode') {
|
||||
frontmatter.type = 'mode';
|
||||
frontmatter.redirect = option.value;
|
||||
} else if (option.type === 'folder') {
|
||||
frontmatter.type = 'folder';
|
||||
frontmatter.redirect = option.value;
|
||||
} else if (option.type === 'file') {
|
||||
const link = app.fileManager.generateMarkdownLink(
|
||||
app.vault.getAbstractFileByPath(option.value) as TFile,
|
||||
""
|
||||
);
|
||||
frontmatter.type = "file";
|
||||
frontmatter.redirect = link;
|
||||
} else if (option.type === 'search') {
|
||||
frontmatter.type = 'search';
|
||||
frontmatter.redirect = option.value;
|
||||
// 添加搜尋選項到 frontmatter
|
||||
if (option.searchOptions) {
|
||||
frontmatter.searchCurrentLocationOnly = option.searchOptions.searchCurrentLocationOnly;
|
||||
frontmatter.searchFilesNameOnly = option.searchOptions.searchFilesNameOnly;
|
||||
frontmatter.searchMediaFiles = option.searchOptions.searchMediaFiles;
|
||||
}
|
||||
} else if (option.type === 'uri') {
|
||||
frontmatter.type = 'uri';
|
||||
frontmatter.redirect = option.value;
|
||||
}
|
||||
});
|
||||
|
||||
new Notice(`${t('shortcut_created')}: ${shortcutName}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Create shortcut error', error);
|
||||
new Notice(t('failed_to_create_shortcut'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 從 URI 生成檔案名稱
|
||||
* @param uri URI 字串
|
||||
* @returns 檔案名稱
|
||||
*/
|
||||
function generateFilenameFromUri(uri: string): string {
|
||||
try {
|
||||
// 處理 obsidian:// 協議
|
||||
if (uri.startsWith('obsidian://')) {
|
||||
const match = uri.match(/obsidian:\/\/([^?]+)/);
|
||||
let vaultName = '';
|
||||
|
||||
// 嘗試提取 vault 參數
|
||||
const vaultMatch = uri.match(/[?&]vault=([^&]+)/);
|
||||
if (vaultMatch) {
|
||||
vaultName = decodeURIComponent(vaultMatch[1]);
|
||||
// 清理 vault 名稱,移除不適合檔名的字符
|
||||
vaultName = vaultName.replace(/[<>:"/\\|?*]/g, '_');
|
||||
}
|
||||
|
||||
if (match) {
|
||||
const action = match[1];
|
||||
const vaultSuffix = vaultName ? ` (${vaultName})` : '';
|
||||
|
||||
// 根據不同的 obsidian 動作生成檔名
|
||||
switch (action) {
|
||||
case 'open':
|
||||
return `🌐 Obsidian Open${vaultSuffix}`;
|
||||
case 'new':
|
||||
return `🌐 Obsidian New${vaultSuffix}`;
|
||||
case 'search':
|
||||
return `🌐 Obsidian Search${vaultSuffix}`;
|
||||
case 'hook-get-address':
|
||||
return `🌐 Obsidian Hook${vaultSuffix}`;
|
||||
default:
|
||||
return `🌐 Obsidian ${action}${vaultSuffix}`;
|
||||
}
|
||||
}
|
||||
return vaultName ? `🌐 Obsidian Link (${vaultName})` : '🌐 Obsidian Link';
|
||||
}
|
||||
|
||||
// 處理 file:// 協議
|
||||
if (uri.startsWith('file://')) {
|
||||
const filename = uri.split('/').pop() || 'Local File';
|
||||
return `🌐 ${filename}`;
|
||||
}
|
||||
|
||||
// 處理 http/https 協議
|
||||
if (uri.startsWith('http://') || uri.startsWith('https://')) {
|
||||
const url = new URL(uri);
|
||||
let domain = url.hostname;
|
||||
|
||||
// 移除 www. 前綴
|
||||
if (domain.startsWith('www.')) {
|
||||
domain = domain.substring(4);
|
||||
}
|
||||
|
||||
// 如果有路徑,嘗試提取有意義的部分
|
||||
if (url.pathname && url.pathname !== '/') {
|
||||
const pathParts = url.pathname.split('/').filter(part => part.length > 0);
|
||||
if (pathParts.length > 0) {
|
||||
const lastPart = pathParts[pathParts.length - 1];
|
||||
// 如果最後一部分看起來像檔名或有意義的標識符
|
||||
if (lastPart.length < 50 && !lastPart.includes('?')) {
|
||||
return `🌐 ${domain} - ${lastPart}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return `🌐 ${domain}`;
|
||||
}
|
||||
|
||||
// 其他協議的處理
|
||||
const protocolMatch = uri.match(/^([^:]+):/);
|
||||
if (protocolMatch) {
|
||||
const protocol = protocolMatch[1].toUpperCase();
|
||||
return `🌐 ${protocol} Link`;
|
||||
}
|
||||
|
||||
// 如果不是標準 URI,直接使用前 30 個字符
|
||||
const cleanUri = uri.replace(/[<>:"/\\|?*]/g, '_').substring(0, 30);
|
||||
return `🌐 ${cleanUri}`;
|
||||
|
||||
} catch (error) {
|
||||
// 如果解析失敗,使用安全的預設名稱
|
||||
const cleanUri = uri.replace(/[<>:"/\\|?*]/g, '_').substring(0, 30);
|
||||
return `🌐 ${cleanUri}`;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,9 +3,11 @@ import { GridView } from './GridView';
|
|||
import { isFolderIgnored } from './fileUtils';
|
||||
import { extractObsidianPathsFromDT } from './dragUtils';
|
||||
import { showFolderNoteSettingsModal } from './modal/folderNoteSettingsModal';
|
||||
import { showFolderRenameModal } from './modal/folderRenameModal';
|
||||
import { showFolderMoveModal } from './modal/folderMoveModal';
|
||||
import { showFolderRenameModal } from './modal/folderRenameModal';
|
||||
import { t } from './translations';
|
||||
import { createNewNote, createNewFolder, createNewCanvas, createNewBase, createShortcut } from './createItemUtils';
|
||||
import { ShortcutSelectionModal } from './modal/shortcutSelectionModal';
|
||||
|
||||
export async function renderFolder(gridView: GridView, container: HTMLElement) {
|
||||
|
||||
|
|
@ -243,6 +245,50 @@ export async function renderFolder(gridView: GridView, container: HTMLElement) {
|
|||
});
|
||||
menu.addSeparator();
|
||||
|
||||
// 新增筆記相關選項
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('new_note'))
|
||||
.setIcon('square-pen')
|
||||
.onClick(async () => {
|
||||
await createNewNote(gridView.app, folder.path);
|
||||
});
|
||||
});
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('new_folder'))
|
||||
.setIcon('folder-plus')
|
||||
.onClick(async () => {
|
||||
await createNewFolder(gridView.app, folder.path);
|
||||
// 重新渲染視圖
|
||||
requestAnimationFrame(() => {
|
||||
gridView.render();
|
||||
});
|
||||
});
|
||||
});
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('new_canvas'))
|
||||
.setIcon('layout-dashboard')
|
||||
.onClick(async () => {
|
||||
await createNewCanvas(gridView.app, folder.path);
|
||||
});
|
||||
});
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('new_base'))
|
||||
.setIcon('database')
|
||||
.onClick(async () => {
|
||||
await createNewBase(gridView.app, folder.path);
|
||||
});
|
||||
});
|
||||
menu.addItem((item) => {
|
||||
item.setTitle(t('new_shortcut'))
|
||||
.setIcon('link')
|
||||
.onClick(async () => {
|
||||
new ShortcutSelectionModal(gridView.app, gridView.plugin, async (option) => {
|
||||
await createShortcut(gridView.app, folder.path, option);
|
||||
}).open();
|
||||
});
|
||||
});
|
||||
menu.addSeparator();
|
||||
|
||||
// 檢查同名筆記是否存在
|
||||
const notePath = `${folder.path}/${folder.name}.md`;
|
||||
let noteFile = gridView.app.vault.getAbstractFileByPath(notePath);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { EXPLORER_VIEW_TYPE } from './ExplorerView';
|
|||
import { showFolderSelectionModal } from './modal/folderSelectionModal';
|
||||
import { showSearchModal } from './modal/searchModal';
|
||||
import { ShortcutSelectionModal } from './modal/shortcutSelectionModal';
|
||||
import { createNewNote, createNewFolder, createNewCanvas, createNewBase, createShortcut as createShortcutUtil } from './createItemUtils';
|
||||
import { t } from './translations';
|
||||
|
||||
export function renderHeaderButton(gridView: GridView) {
|
||||
|
|
@ -389,25 +390,7 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
.setTitle(t('new_note'))
|
||||
.setIcon('square-pen')
|
||||
.onClick(async () => {
|
||||
let newFileName = `${t('untitled')}.md`;
|
||||
let newFilePath = !gridView.sourcePath || gridView.sourcePath === '/' ? newFileName : `${gridView.sourcePath}/${newFileName}`;
|
||||
|
||||
// 檢查檔案是否已存在,如果存在則遞增編號
|
||||
let counter = 1;
|
||||
while (gridView.app.vault.getAbstractFileByPath(newFilePath)) {
|
||||
newFileName = `${t('untitled')} ${counter}.md`;
|
||||
newFilePath = !gridView.sourcePath || gridView.sourcePath === '/' ? newFileName : `${gridView.sourcePath}/${newFileName}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
try {
|
||||
// 建立新筆記
|
||||
const newFile = await gridView.app.vault.create(newFilePath, '');
|
||||
// 開啟新筆記
|
||||
await gridView.app.workspace.getLeaf().openFile(newFile);
|
||||
} catch (error) {
|
||||
console.error('An error occurred while creating a new note:', error);
|
||||
}
|
||||
await createNewNote(gridView.app, gridView.sourcePath);
|
||||
});
|
||||
});
|
||||
// 新增資料夾
|
||||
|
|
@ -415,27 +398,11 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
item.setTitle(t('new_folder'))
|
||||
.setIcon('folder')
|
||||
.onClick(async () => {
|
||||
let newFolderName = `${t('untitled')}`;
|
||||
let newFolderPath = !gridView.sourcePath || gridView.sourcePath === '/' ? newFolderName : `${gridView.sourcePath}/${newFolderName}`;
|
||||
|
||||
// 檢查資料夾是否已存在,如果存在則遞增編號
|
||||
let counter = 1;
|
||||
while (gridView.app.vault.getAbstractFileByPath(newFolderPath)) {
|
||||
newFolderName = `${t('untitled')} ${counter}`;
|
||||
newFolderPath = !gridView.sourcePath || gridView.sourcePath === '/' ? newFolderName : `${gridView.sourcePath}/${newFolderName}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
try {
|
||||
// 建立新資料夾
|
||||
await gridView.app.vault.createFolder(newFolderPath);
|
||||
// 重新渲染視圖
|
||||
await createNewFolder(gridView.app, gridView.sourcePath, () => {
|
||||
requestAnimationFrame(() => {
|
||||
gridView.render();
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('An error occurred while creating a new folder:', error);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
// 新增畫布
|
||||
|
|
@ -443,25 +410,7 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
item.setTitle(t('new_canvas'))
|
||||
.setIcon('layout-dashboard')
|
||||
.onClick(async () => {
|
||||
let newFileName = `${t('untitled')}.canvas`;
|
||||
let newFilePath = !gridView.sourcePath || gridView.sourcePath === '/' ? newFileName : `${gridView.sourcePath}/${newFileName}`;
|
||||
|
||||
// 檢查檔案是否已存在,如果存在則遞增編號
|
||||
let counter = 1;
|
||||
while (gridView.app.vault.getAbstractFileByPath(newFilePath)) {
|
||||
newFileName = `${t('untitled')} ${counter}.canvas`;
|
||||
newFilePath = !gridView.sourcePath || gridView.sourcePath === '/' ? newFileName : `${gridView.sourcePath}/${newFileName}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
try {
|
||||
// 建立新筆記
|
||||
const newFile = await gridView.app.vault.create(newFilePath, '');
|
||||
// 開啟新筆記
|
||||
await gridView.app.workspace.getLeaf().openFile(newFile);
|
||||
} catch (error) {
|
||||
console.error('An error occurred while creating a new canvas:', error);
|
||||
}
|
||||
await createNewCanvas(gridView.app, gridView.sourcePath);
|
||||
});
|
||||
});
|
||||
// 新增 base
|
||||
|
|
@ -469,25 +418,7 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
item.setTitle(t('new_base'))
|
||||
.setIcon('layout-dashboard')
|
||||
.onClick(async () => {
|
||||
let newFileName = `${t('untitled')}.base`;
|
||||
let newFilePath = !gridView.sourcePath || gridView.sourcePath === '/' ? newFileName : `${gridView.sourcePath}/${newFileName}`;
|
||||
|
||||
// 檢查檔案是否已存在,如果存在則遞增編號
|
||||
let counter = 1;
|
||||
while (gridView.app.vault.getAbstractFileByPath(newFilePath)) {
|
||||
newFileName = `${t('untitled')} ${counter}.base`;
|
||||
newFilePath = !gridView.sourcePath || gridView.sourcePath === '/' ? newFileName : `${gridView.sourcePath}/${newFileName}`;
|
||||
counter++;
|
||||
}
|
||||
|
||||
try {
|
||||
// 建立新筆記
|
||||
const newFile = await gridView.app.vault.create(newFilePath, '');
|
||||
// 開啟新筆記
|
||||
await gridView.app.workspace.getLeaf().openFile(newFile);
|
||||
} catch (error) {
|
||||
console.error('An error occurred while creating a new base:', error);
|
||||
}
|
||||
await createNewBase(gridView.app, gridView.sourcePath);
|
||||
});
|
||||
});
|
||||
// 新增捷徑
|
||||
|
|
@ -496,7 +427,7 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
.setIcon('shuffle')
|
||||
.onClick(async () => {
|
||||
const modal = new ShortcutSelectionModal(gridView.app, gridView.plugin, async (option) => {
|
||||
await createShortcut(gridView, option);
|
||||
await createShortcutUtil(gridView.app, gridView.sourcePath, option);
|
||||
});
|
||||
modal.open();
|
||||
});
|
||||
|
|
@ -720,165 +651,3 @@ export function renderHeaderButton(gridView: GridView) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 將 URI 轉換為合適的檔名
|
||||
function generateFilenameFromUri(uri: string): string {
|
||||
try {
|
||||
// 處理 obsidian:// 協議
|
||||
if (uri.startsWith('obsidian://')) {
|
||||
const match = uri.match(/obsidian:\/\/([^?]+)/);
|
||||
let vaultName = '';
|
||||
|
||||
// 嘗試提取 vault 參數
|
||||
const vaultMatch = uri.match(/[?&]vault=([^&]+)/);
|
||||
if (vaultMatch) {
|
||||
vaultName = decodeURIComponent(vaultMatch[1]);
|
||||
// 清理 vault 名稱,移除不適合檔名的字符
|
||||
vaultName = vaultName.replace(/[<>:"/\\|?*]/g, '_');
|
||||
}
|
||||
|
||||
if (match) {
|
||||
const action = match[1];
|
||||
const vaultSuffix = vaultName ? ` (${vaultName})` : '';
|
||||
|
||||
// 根據不同的 obsidian 動作生成檔名
|
||||
switch (action) {
|
||||
case 'open':
|
||||
return `🌐 Obsidian Open${vaultSuffix}`;
|
||||
case 'new':
|
||||
return `🌐 Obsidian New${vaultSuffix}`;
|
||||
case 'search':
|
||||
return `🌐 Obsidian Search${vaultSuffix}`;
|
||||
case 'hook-get-address':
|
||||
return `🌐 Obsidian Hook${vaultSuffix}`;
|
||||
default:
|
||||
return `🌐 Obsidian ${action}${vaultSuffix}`;
|
||||
}
|
||||
}
|
||||
return vaultName ? `🌐 Obsidian Link (${vaultName})` : '🌐 Obsidian Link';
|
||||
}
|
||||
|
||||
// 處理 file:// 協議
|
||||
if (uri.startsWith('file://')) {
|
||||
const filename = uri.split('/').pop() || 'Local File';
|
||||
return `🌐 ${filename}`;
|
||||
}
|
||||
|
||||
// 處理 http/https 協議
|
||||
if (uri.startsWith('http://') || uri.startsWith('https://')) {
|
||||
const url = new URL(uri);
|
||||
let domain = url.hostname;
|
||||
|
||||
// 移除 www. 前綴
|
||||
if (domain.startsWith('www.')) {
|
||||
domain = domain.substring(4);
|
||||
}
|
||||
|
||||
// 如果有路徑,嘗試提取有意義的部分
|
||||
if (url.pathname && url.pathname !== '/') {
|
||||
const pathParts = url.pathname.split('/').filter(part => part.length > 0);
|
||||
if (pathParts.length > 0) {
|
||||
const lastPart = pathParts[pathParts.length - 1];
|
||||
// 如果最後一部分看起來像檔名或有意義的標識符
|
||||
if (lastPart.length < 50 && !lastPart.includes('?')) {
|
||||
return `🌐 ${domain} - ${lastPart}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return `🌐 ${domain}`;
|
||||
}
|
||||
|
||||
// 其他協議的處理
|
||||
const protocolMatch = uri.match(/^([^:]+):/);
|
||||
if (protocolMatch) {
|
||||
const protocol = protocolMatch[1].toUpperCase();
|
||||
return `🌐 ${protocol} Link`;
|
||||
}
|
||||
|
||||
// 如果不是標準 URI,直接使用前 30 個字符
|
||||
const cleanUri = uri.replace(/[<>:"/\\|?*]/g, '_').substring(0, 30);
|
||||
return `🌐 ${cleanUri}`;
|
||||
|
||||
} catch (error) {
|
||||
// 如果解析失敗,使用安全的預設名稱
|
||||
const cleanUri = uri.replace(/[<>:"/\\|?*]/g, '_').substring(0, 30);
|
||||
return `🌐 ${cleanUri}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 創建捷徑檔案
|
||||
async function createShortcut(
|
||||
gridView: GridView,
|
||||
option: {
|
||||
type: 'mode' | 'folder' | 'file' | 'search' | 'uri';
|
||||
value: string;
|
||||
display: string;
|
||||
searchOptions?: {
|
||||
searchCurrentLocationOnly: boolean;
|
||||
searchFilesNameOnly: boolean;
|
||||
searchMediaFiles: boolean;
|
||||
};
|
||||
}) {
|
||||
try {
|
||||
// 生成不重複的檔案名稱
|
||||
let counter = 0;
|
||||
let shortcutName: string;
|
||||
|
||||
// 對於 URI 類型,使用特殊的檔名生成邏輯
|
||||
if (option.type === 'uri') {
|
||||
shortcutName = generateFilenameFromUri(option.value);
|
||||
} else {
|
||||
shortcutName = `${option.display}`;
|
||||
}
|
||||
|
||||
let newName = `${shortcutName}.md`;
|
||||
let newPath = !gridView.sourcePath || gridView.sourcePath === '/' ? newName : `${gridView.sourcePath}/${newName}`;
|
||||
while (gridView.app.vault.getAbstractFileByPath(newPath)) {
|
||||
counter++;
|
||||
const baseName = option.type === 'uri' ? generateFilenameFromUri(option.value) : option.display;
|
||||
shortcutName = `${baseName} ${counter}`;
|
||||
newName = `${shortcutName}.md`;
|
||||
newPath = !gridView.sourcePath || gridView.sourcePath === '/' ? newName : `${gridView.sourcePath}/${newName}`;
|
||||
}
|
||||
|
||||
// 創建新檔案
|
||||
const newFile = await gridView.app.vault.create(newPath, '');
|
||||
|
||||
// 使用 processFrontMatter 來更新 frontmatter
|
||||
await gridView.app.fileManager.processFrontMatter(newFile, (frontmatter: any) => {
|
||||
if (option.type === 'mode') {
|
||||
frontmatter.type = 'mode';
|
||||
frontmatter.redirect = option.value;
|
||||
} else if (option.type === 'folder') {
|
||||
frontmatter.type = 'folder';
|
||||
frontmatter.redirect = option.value;
|
||||
} else if (option.type === 'file') {
|
||||
const link = gridView.app.fileManager.generateMarkdownLink(
|
||||
gridView.app.vault.getAbstractFileByPath(option.value) as TFile,
|
||||
""
|
||||
);
|
||||
frontmatter.type = "file";
|
||||
frontmatter.redirect = link;
|
||||
} else if (option.type === 'search') {
|
||||
frontmatter.type = 'search';
|
||||
frontmatter.redirect = option.value;
|
||||
// 添加搜尋選項到 frontmatter
|
||||
if (option.searchOptions) {
|
||||
frontmatter.searchCurrentLocationOnly = option.searchOptions.searchCurrentLocationOnly;
|
||||
frontmatter.searchFilesNameOnly = option.searchOptions.searchFilesNameOnly;
|
||||
frontmatter.searchMediaFiles = option.searchOptions.searchMediaFiles;
|
||||
}
|
||||
} else if (option.type === 'uri') {
|
||||
frontmatter.type = 'uri';
|
||||
frontmatter.redirect = option.value;
|
||||
}
|
||||
});
|
||||
|
||||
new Notice(`${t('shortcut_created')}: ${shortcutName}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Create shortcut error', error);
|
||||
new Notice(t('failed_to_create_shortcut'));
|
||||
}
|
||||
}
|
||||
|
|
@ -647,6 +647,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'new_base': '新建 base',
|
||||
'new_shortcut': '新建快捷方式',
|
||||
'delete_folder': '删除文件夹',
|
||||
'move_folder': '移动文件夹',
|
||||
'untitled': '未命名',
|
||||
'files': '个文件',
|
||||
'add': '添加',
|
||||
|
|
@ -947,6 +948,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'new_base': '新規 base',
|
||||
'new_shortcut': '新規ショートカット',
|
||||
'delete_folder': '削除フォルダ',
|
||||
'move_folder': '移動フォルダ',
|
||||
'untitled': '無題',
|
||||
'files': 'ファイル',
|
||||
'add': '追加',
|
||||
|
|
@ -1247,6 +1249,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'new_base': 'Новый base',
|
||||
'new_shortcut': 'Новый ярлык',
|
||||
'delete_folder': 'Удалить папку',
|
||||
'move_folder': 'Переместить папку',
|
||||
'untitled': 'Без названия',
|
||||
'files': 'файлы',
|
||||
'add': 'Добавить',
|
||||
|
|
@ -1548,6 +1551,7 @@ export const TRANSLATIONS: Translations = {
|
|||
'new_base': 'Новий base',
|
||||
'new_shortcut': 'Новий ярлик',
|
||||
'delete_folder': 'Видалити папку',
|
||||
'move_folder': 'Перемістити папку',
|
||||
'untitled': 'Без назви',
|
||||
'files': 'файли',
|
||||
'add': 'Додати',
|
||||
|
|
|
|||
32
styles.css
32
styles.css
|
|
@ -2202,6 +2202,38 @@ a.ge-current-folder:hover {
|
|||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
padding: 2px 4px 2px 0;
|
||||
border-radius: var(--radius-s);
|
||||
transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
/* 只有有子目錄的資料夾名稱才顯示懸停效果 */
|
||||
.ge-explorer-folder-header:not(.ge-no-children) .ge-explorer-folder-name:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
color: var(--text-accent);
|
||||
}
|
||||
|
||||
/* 沒有子目錄的資料夾名稱不顯示特殊游標和懸停效果 */
|
||||
.ge-explorer-folder-header.ge-no-children .ge-explorer-folder-name {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ge-explorer-folder-header.ge-no-children .ge-explorer-folder-name:hover {
|
||||
background-color: transparent !important;
|
||||
color: var(--text-normal) !important;
|
||||
}
|
||||
|
||||
/* 模式項目和暫存區項目的名稱也不顯示特殊懸停效果 */
|
||||
.ge-explorer-mode-item .ge-explorer-folder-name,
|
||||
.ge-explorer-stash-item .ge-explorer-folder-name {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ge-explorer-mode-item .ge-explorer-folder-name:hover,
|
||||
.ge-explorer-stash-item .ge-explorer-folder-name:hover {
|
||||
background-color: transparent !important;
|
||||
color: var(--text-normal) !important;
|
||||
}
|
||||
|
||||
.ge-explorer-folder-children {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"2.9.11": "1.1.0"
|
||||
"2.9.12": "1.1.0"
|
||||
}
|
||||
Loading…
Reference in a new issue