This commit is contained in:
Devon22 2026-02-14 00:06:54 +08:00
parent 1377a052c7
commit 5003d6d7a8
15 changed files with 232 additions and 150 deletions

View file

@ -1,7 +1,7 @@
{
"id": "gridexplorer",
"name": "GridExplorer",
"version": "3.0.9",
"version": "3.1.0",
"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": "3.0.9",
"version": "3.1.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "gridexplorer",
"version": "3.0.9",
"version": "3.1.0",
"license": "MIT",
"dependencies": {
"@esbuild/linux-x64": "0.17.3",

View file

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

View file

@ -59,6 +59,7 @@ export class GridView extends ItemView {
pinnedList: string[] = []; // 置頂清單
taskFilter: string = 'uncompleted'; // 任務分類
hideHeaderElements: boolean = false; // 是否隱藏標題列元素(模式名稱和按鈕)
bookmarkGroupId: string = 'all'; // 書籤群組 ID
customOptionIndex: number = -1; // 自訂模式選項索引
baseCardLayout: 'horizontal' | 'vertical' = 'horizontal'; // 使用者在設定或 UI 中選擇的基礎卡片樣式(不受資料夾臨時覆蓋影響)
cardLayout: 'horizontal' | 'vertical' = 'horizontal'; // 目前實際使用的卡片樣式(可能被資料夾 metadata 臨時覆蓋)
@ -252,7 +253,8 @@ export class GridView extends ItemView {
searchQuery?: string,
searchCurrentLocationOnly?: boolean,
searchFilesNameOnly?: boolean,
searchMediaFiles?: boolean
searchMediaFiles?: boolean,
bookmarkGroupId?: string
) {
// 如果新的狀態與當前狀態相同,則不進行任何操作
@ -261,7 +263,8 @@ export class GridView extends ItemView {
this.searchQuery === searchQuery &&
this.searchCurrentLocationOnly === searchCurrentLocationOnly &&
this.searchFilesNameOnly === searchFilesNameOnly &&
this.searchMediaFiles === searchMediaFiles) {
this.searchMediaFiles === searchMediaFiles &&
(bookmarkGroupId === undefined || this.bookmarkGroupId === bookmarkGroupId)) {
return;
}
@ -337,6 +340,9 @@ export class GridView extends ItemView {
if (searchMediaFiles !== undefined) {
this.searchMediaFiles = searchMediaFiles;
}
if (bookmarkGroupId !== undefined) {
this.bookmarkGroupId = bookmarkGroupId;
}
// 通知 Obsidian 保存視圖狀態
this.app.workspace.requestSaveLayout();
@ -1991,19 +1997,6 @@ export class GridView extends ItemView {
// 在網格視圖中直接顯示筆記
async showNoteInGrid(file: TFile) {
// 隱藏移動端導航欄
if (Platform.isPhone) {
const mobileNavbar = document.querySelector('.mobile-navbar') as HTMLElement;
if (mobileNavbar) {
if (!document.body.classList.contains('is-floating-nav')) {
mobileNavbar.style.transform = 'translateY(100%)';
} else {
mobileNavbar.style.transform = 'translateY(200%)';
}
mobileNavbar.style.transition = 'transform 0.3s ease-out';
}
}
// 關閉之前的筆記顯示
if (this.isShowingNote) {
this.hideNoteInGrid();
@ -2062,6 +2055,59 @@ export class GridView extends ItemView {
noteContent.style.padding = '15px';
}
// 在移動端添加滾動監聽,根據滾動方向控制導航欄顯示/隱藏
if (Platform.isPhone) {
let lastScrollTop = 0;
const handleScroll = () => {
const mobileNavbar = document.querySelector('.mobile-navbar') as HTMLElement;
if (!mobileNavbar) return;
const currentScrollTop = scrollContainer.scrollTop;
// 往上捲(滾動位置增加)時隱藏導航欄
if (currentScrollTop > lastScrollTop && currentScrollTop > 50) {
if (!document.body.classList.contains('is-floating-nav')) {
mobileNavbar.style.transform = 'translateY(100%)';
} else {
mobileNavbar.style.transform = 'translateY(200%)';
}
mobileNavbar.style.transition = 'transform 0.3s ease-out';
}
// 往下捲(滾動位置減少)時顯示導航欄
else if (currentScrollTop < lastScrollTop) {
mobileNavbar.style.transform = 'translateY(0)';
mobileNavbar.style.transition = 'transform 0.3s ease-in';
}
lastScrollTop = currentScrollTop;
};
scrollContainer.addEventListener('scroll', handleScroll);
// 監聽分頁切換事件,當離開當前視圖時恢復導航欄
const handleActiveLeafChange = () => {
const activeView = this.app.workspace.getActiveViewOfType(GridView);
// 如果當前活動視圖不是這個 GridView 實例,或者不在顯示筆記狀態
if (activeView !== this || !this.isShowingNote) {
const navbar = document.querySelector('.mobile-navbar') as HTMLElement;
if (navbar) {
navbar.style.transform = 'translateY(0)';
navbar.style.transition = 'transform 0.3s ease-in';
}
}
};
// 註冊事件監聽器
this.registerEvent(
this.app.workspace.on('active-leaf-change', handleActiveLeafChange)
);
// 儲存滾動事件清理函數
this.eventCleanupFunctions.push(() => {
scrollContainer.removeEventListener('scroll', handleScroll);
});
}
// 取得 Metadata (Frontmatter)
const fileCache = this.app.metadataCache.getFileCache(file);
const frontmatter = fileCache?.frontmatter;
@ -2304,6 +2350,7 @@ export class GridView extends ItemView {
showNoteTags: this.showNoteTags,
recentSources: this.recentSources,
futureSources: this.futureSources,
bookmarkGroupId: this.bookmarkGroupId,
}
};
}
@ -2329,6 +2376,7 @@ export class GridView extends ItemView {
this.showNoteTags = state.state.showNoteTags ?? this.plugin.settings.showNoteTags;
this.recentSources = state.state.recentSources ?? [];
this.futureSources = state.state.futureSources ?? [];
this.bookmarkGroupId = state.state.bookmarkGroupId ?? 'all';
await this.render();
}
}

View file

@ -34,7 +34,7 @@ export function renderHeaderButton(gridView: GridView) {
backButton.addEventListener('click', async (event) => {
event.preventDefault();
event.stopPropagation();
// 如果有歷史記錄
if (gridView.recentSources.length > 0) {
// 將當前狀態推入 futureSources 以便前進
@ -54,7 +54,7 @@ export function renderHeaderButton(gridView: GridView) {
// 取得最近一筆歷史記錄
const lastSource = JSON.parse(gridView.recentSources[0]);
gridView.recentSources.shift(); // 從歷史記錄中移除
// 設定來源及搜尋狀態(不記錄到歷史)
await gridView.setSource(
lastSource.mode,
@ -119,7 +119,7 @@ export function renderHeaderButton(gridView: GridView) {
// 只有在有歷史記錄時才顯示右鍵選單
if (gridView.recentSources.length > 0) {
event.preventDefault();
const menu = new Menu();
// 添加歷史記錄
@ -127,11 +127,11 @@ export function renderHeaderButton(gridView: GridView) {
try {
const sourceInfo = JSON.parse(sourceInfoStr);
const { mode, path } = sourceInfo;
// 根據模式顯示圖示和文字
let displayText = '';
let icon = '';
switch (mode) {
case 'folder':
displayText = path || '/';
@ -190,7 +190,7 @@ export function renderHeaderButton(gridView: GridView) {
displayText += `: "${sourceInfo.searchQuery}"`;
}
}
// 添加歷史記錄到選單
menu.addItem((item) => {
item
@ -213,7 +213,7 @@ export function renderHeaderButton(gridView: GridView) {
if (clickedIndex !== -1) {
const newerHistory = gridView.recentSources.slice(0, clickedIndex);
const forwardStack = [...newerHistory].reverse();
gridView.futureSources = [...forwardStack, currentKey, ...gridView.futureSources];
gridView.futureSources = [...forwardStack, currentKey, ...gridView.futureSources];
if (gridView.futureSources.length > 10) {
gridView.futureSources.length = 10;
}
@ -242,7 +242,7 @@ export function renderHeaderButton(gridView: GridView) {
menu.showAtMouseEvent(event);
}
});
// 添加右鍵選單支援
forwardButton.addEventListener('contextmenu', (event) => {
// 只有在有歷史記錄或未來記錄時才顯示右鍵選單
@ -250,7 +250,7 @@ export function renderHeaderButton(gridView: GridView) {
event.preventDefault();
const menu = new Menu();
// 添加未來記錄
gridView.futureSources.forEach((sourceInfoStr, index) => {
try {
@ -381,7 +381,7 @@ export function renderHeaderButton(gridView: GridView) {
// 添加新增筆記按鈕
const newNoteButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('new_note') } });
setIcon(newNoteButton, 'square-pen');
newNoteButton.addEventListener('click', (event) => {
newNoteButton.addEventListener('click', (event) => {
event.preventDefault();
const menu = new Menu();
// 新增筆記
@ -396,54 +396,54 @@ export function renderHeaderButton(gridView: GridView) {
// 新增資料夾
menu.addItem((item) => {
item.setTitle(t('new_folder'))
.setIcon('folder')
.onClick(async () => {
await createNewFolder(gridView.app, gridView.sourcePath, () => {
requestAnimationFrame(() => {
gridView.render();
.setIcon('folder')
.onClick(async () => {
await createNewFolder(gridView.app, gridView.sourcePath, () => {
requestAnimationFrame(() => {
gridView.render();
});
});
});
});
});
// 新增畫布
menu.addItem((item) => {
item.setTitle(t('new_canvas'))
.setIcon('layout-dashboard')
.onClick(async () => {
await createNewCanvas(gridView.app, gridView.sourcePath);
});
.setIcon('layout-dashboard')
.onClick(async () => {
await createNewCanvas(gridView.app, gridView.sourcePath);
});
});
// 新增 base
menu.addItem((item) => {
item.setTitle(t('new_base'))
.setIcon('layout-dashboard')
.onClick(async () => {
await createNewBase(gridView.app, gridView.sourcePath);
});
.setIcon('layout-dashboard')
.onClick(async () => {
await createNewBase(gridView.app, gridView.sourcePath);
});
});
// 新增捷徑
menu.addItem((item) => {
item.setTitle(t('new_shortcut'))
.setIcon('shuffle')
.onClick(async () => {
const modal = new ShortcutSelectionModal(gridView.app, gridView.plugin, async (option) => {
await createShortcutUtil(gridView.app, gridView.sourcePath, option);
.setIcon('shuffle')
.onClick(async () => {
const modal = new ShortcutSelectionModal(gridView.app, gridView.plugin, async (option) => {
await createShortcutUtil(gridView.app, gridView.sourcePath, option);
});
modal.open();
});
modal.open();
});
});
menu.showAtMouseEvent(event);
});
// 添加重新選擇資料夾按鈕
const reselectButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('reselect') } });
const reselectButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('reselect') } });
reselectButton.addEventListener('click', () => {
showFolderSelectionModal(gridView.app, gridView.plugin, gridView, reselectButton);
});
setIcon(reselectButton, "grid");
// 添加重新整理按鈕
const refreshButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('refresh') } });
const refreshButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('refresh') } });
refreshButton.addEventListener('click', () => {
if (gridView.sortType === 'random') {
gridView.clearSelection();
@ -462,51 +462,10 @@ export function renderHeaderButton(gridView: GridView) {
searchButton.addEventListener('click', () => {
showSearchModal(gridView.app, gridView, gridView.searchQuery, searchButton);
});
// 如果有搜尋關鍵字,顯示搜尋文字和取消按鈕
// if (gridView.searchQuery) {
// searchButton.style.display = 'none';
// const searchTextContainer = searchButtonContainer.createDiv('ge-search-text-container');
// searchTextContainer.setAttribute('aria-label', gridView.searchQuery);
// // 創建搜尋文字
// const searchText = searchTextContainer.createEl('span', { cls: 'ge-search-text', text: gridView.searchQuery });
// // 讓搜尋文字可點選
// searchText.style.cursor = 'pointer';
// searchText.addEventListener('click', () => {
// showSearchModal(gridView.app, gridView, gridView.searchQuery, searchText);
// });
// // 先保存開啟 Modal 時的原始狀態
// const originalSearchQuery = gridView.searchQuery;
// const originalsearchCurrentLocationOnly = gridView.searchCurrentLocationOnly;
// const originalSearchFilesNameOnly = gridView.searchFilesNameOnly;
// const originalSearchMediaFiles = gridView.searchMediaFiles;
// // 創建取消按鈕
// const clearButton = searchTextContainer.createDiv('ge-clear-button');
// setIcon(clearButton, 'x');
// clearButton.addEventListener('click', (e) => {
// e.stopPropagation(); // 防止觸發搜尋文字的點擊事件
// gridView.pushHistory(
// gridView.sourceMode,
// gridView.sourcePath,
// originalSearchQuery,
// originalsearchCurrentLocationOnly,
// originalSearchFilesNameOnly,
// originalSearchMediaFiles,
// );
// gridView.searchQuery = '';
// gridView.clearSelection();
// gridView.app.workspace.requestSaveLayout();
// gridView.render();
// });
// }
// 更多選項按鈕
const menu = new Menu();
menu.addItem((item) => {
const moreMenu = new Menu();
moreMenu.addItem((item) => {
item
.setTitle(t('open_new_grid_view'))
.setIcon('grid')
@ -539,10 +498,10 @@ export function renderHeaderButton(gridView: GridView) {
workspace.revealLeaf(leaf);
});
});
menu.addSeparator();
moreMenu.addSeparator();
// 直向卡片切換
menu.addItem((item) => {
moreMenu.addItem((item) => {
item.setTitle(t('vertical_card'))
.setIcon('layout')
.setChecked(gridView.baseCardLayout === 'vertical')
@ -554,7 +513,7 @@ export function renderHeaderButton(gridView: GridView) {
});
});
// 最小化模式選項
menu.addItem((item) => {
moreMenu.addItem((item) => {
item
.setTitle(t('min_mode'))
.setIcon('minimize-2')
@ -567,7 +526,7 @@ export function renderHeaderButton(gridView: GridView) {
});
// 顯示日期分隔器
if (gridView.plugin.settings.dateDividerMode !== 'none') {
menu.addItem((item) => {
moreMenu.addItem((item) => {
item
.setTitle(t('show_date_dividers'))
.setIcon('calendar')
@ -580,7 +539,7 @@ export function renderHeaderButton(gridView: GridView) {
});
}
// 顯示筆記標籤
menu.addItem((item) => {
moreMenu.addItem((item) => {
item
.setTitle(t('show_note_tags'))
.setIcon('tag')
@ -592,7 +551,7 @@ export function renderHeaderButton(gridView: GridView) {
});
});
// 顯示忽略的資料夾及檔案選項
menu.addItem((item) => {
moreMenu.addItem((item) => {
item
.setTitle(t('show_ignored_items'))
.setIcon('folder-open-dot')
@ -603,11 +562,11 @@ export function renderHeaderButton(gridView: GridView) {
gridView.render();
});
});
menu.addSeparator();
moreMenu.addSeparator();
// 開啟瀏覽器
menu.addItem((item) => {
moreMenu.addItem((item) => {
item
.setTitle(t('open_explorer'))
.setIcon('folder-tree')
@ -626,7 +585,7 @@ export function renderHeaderButton(gridView: GridView) {
});
// 顯示設定選項
menu.addItem((item) => {
moreMenu.addItem((item) => {
item
.setTitle(t('open_settings'))
.setIcon('settings')
@ -636,17 +595,17 @@ export function renderHeaderButton(gridView: GridView) {
(gridView.app as any).setting.openTabById(gridView.plugin.manifest.id);
});
});
const moreOptionsButton = headerButtonsDiv.createEl('button', { attr: { 'aria-label': t('more_options') } });
setIcon(moreOptionsButton, 'ellipsis-vertical');
moreOptionsButton.addEventListener('click', (event) => {
menu.showAtMouseEvent(event);
moreMenu.showAtMouseEvent(event);
});
headerButtonsDiv.addEventListener('contextmenu', (event) => {
if (event.target === headerButtonsDiv) {
event.preventDefault();
menu.showAtMouseEvent(event);
moreMenu.showAtMouseEvent(event);
}
});
}

View file

@ -13,20 +13,20 @@ export function renderModePath(gridView: GridView) {
// 創建模式名稱和排序按鈕的容器
const modeHeaderContainer = gridView.containerEl.createDiv('ge-mode-header-container');
// 左側:模式名稱
const modenameContainer = modeHeaderContainer.createDiv('ge-modename-content');
// 右側:排序按鈕
const rightActions = modeHeaderContainer.createDiv('ge-right-actions');
// 添加排序按鈕
if (gridView.sourceMode !== 'bookmarks' &&
gridView.sourceMode !== 'recent-files' &&
if (gridView.sourceMode !== 'bookmarks' &&
gridView.sourceMode !== 'recent-files' &&
gridView.sourceMode !== 'random-note') {
const sortButton = rightActions.createEl('a', {
const sortButton = rightActions.createEl('a', {
cls: 'ge-sort-button',
attr: {
attr: {
'aria-label': t('sorting'),
'href': '#'
}
@ -648,6 +648,53 @@ export function renderModePath(gridView: GridView) {
}
switch (gridView.sourceMode) {
case 'bookmarks':
const bookmarkGroupName = gridView.bookmarkGroupId === 'all' ? t('all_bookmarks') : gridView.bookmarkGroupId;
const bookmarkGroupSpan = modenameContainer.createEl('a', { text: bookmarkGroupName, cls: 'ge-sub-option' });
bookmarkGroupSpan.addEventListener('click', (evt) => {
const menu = new Menu();
// "全部" 選項
menu.addItem((item) => {
item.setTitle(t('all_bookmarks'))
.setIcon('layers')
.setChecked(gridView.bookmarkGroupId === 'all')
.onClick(() => {
gridView.setSource('bookmarks', '', false, undefined, undefined, undefined, undefined, 'all');
});
});
menu.addSeparator();
// 取得所有書籤群組
const bookmarksPlugin = (gridView.app as any).internalPlugins.plugins.bookmarks;
if (bookmarksPlugin?.enabled) {
const bookmarks = bookmarksPlugin.instance.items;
const groups: string[] = [];
const findGroups = (items: any[]) => {
items.forEach(item => {
if (item.type === 'group') {
groups.push(item.title);
if (item.items) findGroups(item.items);
}
});
};
findGroups(bookmarks);
groups.forEach(groupTitle => {
menu.addItem((item) => {
item.setTitle(groupTitle)
.setIcon('folder')
.setChecked(gridView.bookmarkGroupId === groupTitle)
.onClick(() => {
gridView.setSource('bookmarks', '', false, undefined, undefined, undefined, undefined, groupTitle);
});
});
});
}
menu.showAtMouseEvent(evt);
});
break;
case 'random-note':
case 'recent-files':
case 'all-files':

View file

@ -178,6 +178,7 @@ export default {
'all': 'All',
'default': 'Default',
'hidden': 'Hidden',
'all_bookmarks': 'All Bookmarks',
// Hide header elements setting
'hide_header_elements': 'Hide header elements',

View file

@ -177,6 +177,7 @@ export default {
'all': 'すべて',
'default': 'デフォルト',
'hidden': '隠す',
'bookmark_groups': 'ブックマーク群組',
// 隠すヘッダー要素
'hide_header_elements': 'ヘッダー要素を隠す',
@ -301,7 +302,7 @@ export default {
'use_quick_access_mode': 'クイックアクセスモードを使用する',
'open_quick_access_folder': 'クイックアクセス フォルダーを開く',
'open_quick_access_mode': 'クイックアクセスモードを開く',
// Explorer Stash
'stash': '一時保存',
'drop_files_here': 'ここにドラッグして一時保存',

View file

@ -178,6 +178,7 @@ export default {
'all': '전체',
'default': '기본값',
'hidden': '숨김',
'bookmark_groups': '북마크群組',
// Hide header elements setting
'hide_header_elements': '헤더 요소 숨기기',

View file

@ -177,6 +177,7 @@ export default {
'all': 'Все',
'default': 'По умолчанию',
'hidden': 'Скрытые',
'bookmark_groups': 'Букмарк группы',
// Hide header elements setting
'hide_header_elements': 'Скрыть элементы заголовка',
@ -301,7 +302,7 @@ export default {
'use_quick_access_mode': 'Использовать режим быстрого доступа',
'open_quick_access_folder': 'Открыть папку быстрого доступа',
'open_quick_access_mode': 'Открыть быстрый доступ по режиму',
// Explorer Stash
'stash': 'Хранилище',
'drop_files_here': 'Перетащите файлы сюда, чтобы сохранить',

View file

@ -177,6 +177,7 @@ export default {
'all': 'Усі',
'default': 'За замовчуванням',
'hidden': 'Приховані',
'bookmark_groups': 'Групи закладок',
// Hide header elements setting
'hide_header_elements': 'Приховати елементи заголовка',

View file

@ -178,6 +178,7 @@ export default {
'all': '全部',
'default': '預設',
'hidden': '隱藏',
'all_bookmarks': '全部書籤',
// 隱藏頂部元素
'hide_header_elements': '隱藏頂部元素',

View file

@ -177,6 +177,7 @@ export default {
'all': '全部',
'default': '默认',
'hidden': '隐藏',
'all_bookmarks': '全部书签',
// 隐藏頂部元素
'hide_header_elements': '隱藏頂部元素',

View file

@ -73,7 +73,7 @@ export function sortFiles(files: TFile[], gridView: GridView, overrideSortType?:
const hasModifiedField = !!settings.modifiedDateField;
const hasCreatedField = !!settings.createdDateField;
const hasAnyDateField = hasModifiedField || hasCreatedField;
// 符合以下任一條件就使用簡單排序:
// 1. 非日期排序類型 (name-asc, name-desc, random)
// 2. 沒有設定任何日期欄位
@ -106,7 +106,7 @@ export function sortFiles(files: TFile[], gridView: GridView, overrideSortType?:
// 只對 .md 檔案讀取 metadata
const shouldReadMetadata = file.extension === 'md';
const metadata = shouldReadMetadata ? app.metadataCache.getFileCache(file) : null;
return {
file,
mDate: (() => {
@ -167,12 +167,12 @@ export function isFolderIgnored(folder: TFolder, ignoredFolders: string[], ignor
if (showIgnoredFolders) return false;
// 檢查資料夾是否在忽略清單中
const isInIgnoredFolders = ignoredFolders.some(ignored =>
const isInIgnoredFolders = ignoredFolders.some(ignored =>
folder.path === ignored || folder.path.startsWith(ignored + '/')
);
if (isInIgnoredFolders) return true;
// 檢查資料夾是否符合忽略的模式
if (ignoredFolderPatterns && ignoredFolderPatterns.length > 0) {
const matchesIgnoredPattern = ignoredFolderPatterns.some(pattern => {
@ -180,7 +180,7 @@ export function isFolderIgnored(folder: TFolder, ignoredFolders: string[], ignor
// 嘗試將模式作為正則表達式處理
// 如果模式包含特殊字符,使用正則表達式處理
if (/[\^\$\*\+\?\(\)\[\]\{\}\|\\]/.test(pattern)) {
const regex = new RegExp(pattern);
const regex = new RegExp(pattern);
return regex.test(folder.path);
} else {
// 檢查資料夾名稱是否包含模式字串(不區分大小寫)
@ -191,7 +191,7 @@ export function isFolderIgnored(folder: TFolder, ignoredFolders: string[], ignor
return folder.name.toLowerCase().includes(pattern.toLowerCase());
}
});
if (matchesIgnoredPattern) return true;
}
@ -201,15 +201,15 @@ export function isFolderIgnored(folder: TFolder, ignoredFolders: string[], ignor
// 檢查檔案是否應該被忽略
export function ignoredFiles(files: TFile[], gridView: GridView): TFile[] {
const settings = gridView.plugin.settings;
// 如果開啟顯示忽略項目模式,則顯示所有檔案
if (gridView.showIgnoredItems) {
return files;
}
// 建立一個快取來存儲已經檢查過的資料夾路徑
const folderCache: {[path: string]: boolean} = {};
const folderCache: { [path: string]: boolean } = {};
return files.filter(file => {
// 檢查檔案的 metadata 是否有 hidden: true
if (file.extension === 'md') {
@ -222,13 +222,13 @@ export function ignoredFiles(files: TFile[], gridView: GridView): TFile[] {
// 獲取檔案所在的資料夾路徑
const folderPath = file.parent?.path || '/';
// 如果快取中沒有這個資料夾的檢查結果,則進行檢查
if (folderCache[folderPath] === undefined) {
// 獲取資料夾對象
const folder = gridView.app.vault.getAbstractFileByPath(folderPath);
if (folder instanceof TFolder) {
// 使用 isFolderIgnored 檢查資料夾是否應該被忽略
folderCache[folderPath] = isFolderIgnored(
@ -242,7 +242,7 @@ export function ignoredFiles(files: TFile[], gridView: GridView): TFile[] {
folderCache[folderPath] = false;
}
}
// 如果資料夾被忽略,則過濾掉該檔案
return !folderCache[folderPath];
});
@ -262,11 +262,11 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
// 只取得當前資料夾中的支援檔案,不包含子資料夾
const files = folder.children.filter((file): file is TFile => {
if (!(file instanceof TFile)) return false;
// 當存在搜尋關鍵字時,是否包含媒體檔案取決於 includeMediaFiles
// 不存在搜尋關鍵字時,是否包含媒體檔案取決於 settings.showMediaFiles
const allowMedia = settings.showMediaFiles && (!gridView.searchQuery || includeMediaFiles);
// 如果是文件檔案,直接包含;若為媒體檔案則依 allowMedia 判斷
if (isDocumentFile(file) || (allowMedia && isMediaFile(file))) {
return true;
@ -285,13 +285,13 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
const resultDomLookup = searchLeaf.view.dom.resultDomLookup;
if (resultDomLookup) {
const files = Array.from(resultDomLookup.keys())
.filter((file): file is TFile => file instanceof TFile);
.filter((file): file is TFile => file instanceof TFile);
return sortFiles(files, gridView);
}
}
}
return [];
} else if (sourceMode === 'backlinks') {
} else if (sourceMode === 'backlinks') {
// 反向連結模式:找出所有引用當前筆記的檔案
const activeFile = app.workspace.getActiveFile();
if (!activeFile) {
@ -305,10 +305,10 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
if (Object.keys(links).includes(activeFile.path)) {
const sourceFile = app.vault.getAbstractFileByPath(sourcePath) as TFile;
if (sourceFile) {
backlinks.add(sourceFile);
}
backlinks.add(sourceFile);
}
}
}
return sortFiles(Array.from(backlinks) as TFile[], gridView);
} else if (sourceMode === 'outgoinglinks') {
@ -322,11 +322,11 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
// 使用 resolvedLinks 來找出外部連結
const resolvedLinks = app.metadataCache.resolvedLinks;
const fileLinks = resolvedLinks[activeFile.path];
if (fileLinks) {
for (const targetPath of Object.keys(fileLinks)) {
const targetFile = app.vault.getAbstractFileByPath(targetPath) as TFile;
if (targetFile && (isDocumentFile(targetFile) ||
if (targetFile && (isDocumentFile(targetFile) ||
(settings.showMediaFiles && isMediaFile(targetFile)))) {
outgoingLinks.add(targetFile);
}
@ -362,7 +362,7 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
}
return sortFiles(Array.from(outgoingLinks), gridView);
} else if(sourceMode === 'bookmarks') {
} else if (sourceMode === 'bookmarks') {
// 書籤模式
const bookmarksPlugin = (app as any).internalPlugins.plugins.bookmarks;
if (!bookmarksPlugin?.enabled) {
@ -371,7 +371,7 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
const bookmarks = bookmarksPlugin.instance.items;
const bookmarkedFiles = new Set();
const processBookmarkItem = (item: any) => {
if (item.type === 'file') {
const file = app.vault.getAbstractFileByPath(item.path);
@ -386,8 +386,29 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
item.items.forEach(processBookmarkItem);
}
};
bookmarks.forEach(processBookmarkItem);
const bookmarkGroupId = gridView.bookmarkGroupId;
if (!bookmarkGroupId || bookmarkGroupId === 'all') {
bookmarks.forEach(processBookmarkItem);
} else {
// 遞迴尋找指定的群組
const findAndProcessGroup = (items: any[]) => {
for (const item of items) {
if (item.type === 'group') {
// 使用 title 作為 ID
if (item.title === bookmarkGroupId) {
item.items.forEach(processBookmarkItem);
return true;
}
if (item.items && findAndProcessGroup(item.items)) {
return true;
}
}
}
return false;
};
findAndProcessGroup(bookmarks);
}
return Array.from(bookmarkedFiles) as TFile[];
} else if (sourceMode === 'tasks') {
// 任務模式
@ -404,7 +425,7 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
for (const task of allTasks) {
const file = app.vault.getAbstractFileByPath(task.path);
if (!(file instanceof TFile)) continue;
// 根據過濾條件檢查任務
if (gridView.taskFilter === 'uncompleted' && !task.isDone) {
filesWithTasks.add(file);
@ -423,7 +444,7 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
try {
const tasks = dv.pages().file.tasks;
let filteredTasks;
if (gridView.taskFilter === 'uncompleted') {
filteredTasks = tasks.where((t: { completed: boolean }) => !t.completed);
} else if (gridView.taskFilter === 'completed') {
@ -431,7 +452,7 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
} else { // 'all'
filteredTasks = tasks;
}
for (const task of filteredTasks.array()) {
const file = app.vault.getAbstractFileByPath(task.path);
if (file instanceof TFile) {
@ -442,7 +463,7 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
console.error('Error getting tasks from Dataview:', error);
return [];
}
} else {
// 使用原生方法
const markdownFiles = app.vault.getMarkdownFiles();
@ -465,7 +486,7 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
const hasCompleted = /^[\s]*[-*]\s*\[x\](?![^\[]*\[\s*[^\s\]]+\]).*$/m.test(content);
shouldAdd = hasIncomplete || hasCompleted;
}
if (shouldAdd) {
filesWithTasks.add(file);
}
@ -531,7 +552,7 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
dvCode = mode.options[idx].dataviewCode;
}
}
try {
const activeFile = app.workspace.getActiveFile();
if (activeFile) {

View file

@ -1,3 +1,3 @@
{
"3.0.9": "1.1.0"
"3.1.0": "1.1.0"
}