diff --git a/manifest.json b/manifest.json index 7dcb52e..e260c11 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "gridexplorer", "name": "GridExplorer", - "version": "3.0.7", + "version": "3.0.8", "minAppVersion": "1.1.0", "description": "Browse note files in a grid view.", "author": "Devon22", diff --git a/package-lock.json b/package-lock.json index a043bc9..2060ab5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gridexplorer", - "version": "3.0.7", + "version": "3.0.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gridexplorer", - "version": "3.0.7", + "version": "3.0.8", "license": "MIT", "dependencies": { "@esbuild/linux-x64": "0.17.3", @@ -2315,4 +2315,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 82167d3..e66e4f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gridexplorer", - "version": "3.0.7", + "version": "3.0.8", "description": "Browse note files in a grid view.", "main": "main.js", "scripts": { @@ -25,4 +25,4 @@ "@esbuild/linux-x64": "^0.17.3", "@esbuild/win32-x64": "^0.17.3" } -} +} \ No newline at end of file diff --git a/src/GridView.ts b/src/GridView.ts index 9f43ffc..e777e7f 100644 --- a/src/GridView.ts +++ b/src/GridView.ts @@ -2016,7 +2016,9 @@ export class GridView extends ItemView { // 筆記標題 const noteTitle = leftBar.createDiv('ge-note-title'); noteTitle.textContent = file.basename; - setTooltip(noteTitle, file.basename); + if (Platform.isDesktop) { + setTooltip(noteTitle, file.basename); + } // 編輯按鈕 const editButton = rightBar.createEl('button', { cls: 'ge-note-edit-button' }); @@ -2025,6 +2027,10 @@ export class GridView extends ItemView { this.getLeafByMode(file).openFile(file); }); + // Metadata 切換按鈕 + const infoButton = rightBar.createEl('button', { cls: 'ge-note-info-button' }); + setIcon(infoButton, 'info') + // 關閉按鈕 const closeButton = rightBar.createEl('button', { cls: 'ge-note-close-button' }); setIcon(closeButton, 'x'); @@ -2049,6 +2055,134 @@ export class GridView extends ItemView { noteContent.style.padding = '15px'; } + // 取得 Metadata (Frontmatter) + const fileCache = this.app.metadataCache.getFileCache(file); + const frontmatter = fileCache?.frontmatter; + + if (frontmatter) { + // 檢查是否除了 position 以外還有其他屬性 + const keys = Object.keys(frontmatter).filter(k => k !== 'position'); + if (keys.length > 0) { + const metadataContainer = noteContent.createDiv('ge-note-metadata-container'); + + // 綁定切換事件 + infoButton.addEventListener('click', () => { + metadataContainer.classList.toggle('is-visible'); + scrollContainer.scrollTo(0, 0); + }); + + const metadataContent = metadataContainer.createDiv('ge-note-metadata-content'); + for (const key of keys) { + const item = metadataContent.createDiv('ge-note-metadata-item'); + item.createSpan({ cls: 'ge-note-metadata-key', text: `${key}: ` }); + const value = frontmatter[key]; + const valueSpan = item.createSpan({ cls: 'ge-note-metadata-value' }); + + const values = Array.isArray(value) ? value : [value]; + values.forEach((val, index) => { + const valStr = String(val); + // 處理內部連結 [[link]] 或 [[link|alias]] + const wikilinkRegex = /\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g; + // 處理 URL + const urlRegex = /(https?:\/\/[^\s]+)/g; + // 處理 Tag + const tagRegex = /#([^\s#]+)/g; + + if (wikilinkRegex.test(valStr)) { + wikilinkRegex.lastIndex = 0; + let lastIndex = 0; + let match; + while ((match = wikilinkRegex.exec(valStr)) !== null) { + // 插入匹配前的文字 + if (match.index > lastIndex) { + valueSpan.createSpan({ text: valStr.substring(lastIndex, match.index) }); + } + const linkPath = match[1]; + const linkAlias = match[2] || linkPath; + const linkEl = valueSpan.createEl('a', { + cls: 'internal-link', + text: linkAlias, + attr: { 'data-href': linkPath } + }); + linkEl.addEventListener('click', (e) => { + e.preventDefault(); + const linkedFile = this.app.metadataCache.getFirstLinkpathDest(linkPath, file.path); + if (linkedFile) { + this.getLeafByMode(linkedFile).openFile(linkedFile); + } + }); + lastIndex = wikilinkRegex.lastIndex; + } + if (lastIndex < valStr.length) { + valueSpan.createSpan({ text: valStr.substring(lastIndex) }); + } + } else if (urlRegex.test(valStr)) { + urlRegex.lastIndex = 0; + let lastIndex = 0; + let match; + while ((match = urlRegex.exec(valStr)) !== null) { + if (match.index > lastIndex) { + valueSpan.createSpan({ text: valStr.substring(lastIndex, match.index) }); + } + const url = match[1]; + valueSpan.createEl('a', { + cls: 'external-link', + text: url, + attr: { 'href': url, 'target': '_blank', 'rel': 'noopener' } + }); + lastIndex = urlRegex.lastIndex; + } + if (lastIndex < valStr.length) { + valueSpan.createSpan({ text: valStr.substring(lastIndex) }); + } + } else if (key.toLowerCase() === 'tags' || key.toLowerCase() === 'tag' || tagRegex.test(valStr)) { + if ((key.toLowerCase() === 'tags' || key.toLowerCase() === 'tag') && !valStr.startsWith('#')) { + const tagEl = valueSpan.createEl('a', { + cls: 'tag', + text: '#' + valStr, + attr: { 'href': '#' + valStr } + }); + tagEl.addEventListener('click', (e) => { + e.preventDefault(); + (this.app as any).internalPlugins?.getPluginById('global-search')?.instance?.openGlobalSearch('tag:#' + valStr); + }); + } else { + tagRegex.lastIndex = 0; + let lastIndex = 0; + let match; + while ((match = tagRegex.exec(valStr)) !== null) { + if (match.index > lastIndex) { + valueSpan.createSpan({ text: valStr.substring(lastIndex, match.index) }); + } + const tagName = match[1]; + const tagEl = valueSpan.createEl('a', { + cls: 'tag', + text: '#' + tagName, + attr: { 'href': '#' + tagName } + }); + tagEl.addEventListener('click', (e) => { + e.preventDefault(); + (this.app as any).internalPlugins?.getPluginById('global-search')?.instance?.openGlobalSearch('tag:#' + tagName); + }); + lastIndex = tagRegex.lastIndex; + } + if (lastIndex < valStr.length) { + valueSpan.createSpan({ text: valStr.substring(lastIndex) }); + } + } + } else { + valueSpan.createSpan({ text: valStr }); + } + + if (index < values.length - 1) { + const isTag = key.toLowerCase() === 'tags' || key.toLowerCase() === 'tag'; + valueSpan.createSpan({ text: isTag ? ' ' : ', ' }); + } + }); + } + } + } + // 創建筆記內容區域 const noteContentArea = noteContent.createDiv('ge-note-content'); diff --git a/styles.css b/styles.css index 958ee9c..ae29764 100644 --- a/styles.css +++ b/styles.css @@ -2070,6 +2070,17 @@ a.ge-current-folder:hover { text-overflow: ellipsis; } +.is-mobile .ge-note-title { + overflow-x: auto; + text-overflow: clip; + /* 或者 unset */ +} + +/* 隱藏行動裝置上的捲軸,保持簡潔 */ +.is-mobile .ge-note-title::-webkit-scrollbar { + display: none; +} + .ge-note-content { line-height: 1.6; color: var(--text-normal); @@ -2092,6 +2103,61 @@ a.ge-current-folder:hover { /* --------------------------------------------------------------------------- */ +/* 筆記 Metadata 區塊 */ +.ge-note-metadata-container { + margin-bottom: 20px; + border: 1px solid var(--background-modifier-border); + border-radius: 6px; + background-color: var(--background-secondary-alt); + overflow: hidden; + display: none; + /* 預設隱藏 */ +} + +.ge-note-metadata-container.is-visible { + display: block; +} + +.ge-note-info-button { + display: flex; + align-items: center; + gap: 4px; + padding: 6px 12px; + background-color: var(--interactive-normal); + border-radius: var(--button-radius); + color: var(--text-normal); + font-size: 14px; + cursor: pointer; + transition: background-color 0.2s, transform 0.1s; +} + +.ge-note-info-button:hover { + transform: translateY(-1px); +} + +.ge-note-metadata-content { + padding: 10px 15px; +} + +.ge-note-metadata-item { + margin-bottom: 4px; + font-size: 0.8em; + display: flex; + flex-wrap: wrap; + gap: 4px; +} + +.ge-note-metadata-key { + color: var(--text-faint); + font-weight: 500; +} + +.ge-note-metadata-value { + color: var(--text-normal); +} + +/* --------------------------------------------------------------------------- */ + .ge-note-content>p:nth-child(2) { margin-block-start: 0; } diff --git a/versions.json b/versions.json index e0c6d52..cd1f735 100644 --- a/versions.json +++ b/versions.json @@ -1,3 +1,3 @@ { - "3.0.7": "1.1.0" + "3.0.8": "1.1.0" } \ No newline at end of file