mirror of
https://github.com/devon22/obsidian-gridexplorer.git
synced 2026-07-22 05:38:16 +00:00
3.4.0
This commit is contained in:
parent
7cfda20845
commit
9bf25cabaa
22 changed files with 180 additions and 649 deletions
|
|
@ -1,51 +0,0 @@
|
|||
---
|
||||
date: 2025-06-26
|
||||
---
|
||||
1. My Favorites
|
||||
```
|
||||
return dv.pages("#Favorites").sort(page => page.modified_date ? dv.date(page.modified_date) : page.file.mtime, 'desc');
|
||||
```
|
||||
|
||||
2. On This Day in History
|
||||
```
|
||||
const today = new Date();
|
||||
return dv.pages().where(p => {
|
||||
const pageDate = p.modified_date ? new Date(p.modified_date): new Date(p.file.mtime);
|
||||
return pageDate.getYear() !== today.getYear() &&
|
||||
pageDate.getMonth() === today.getMonth() &&
|
||||
pageDate.getDate() === today.getDate();
|
||||
});
|
||||
```
|
||||
|
||||
3. Backlinks
|
||||
```
|
||||
const currentPage = dv.current();
|
||||
if (!currentPage) return [];
|
||||
return dv.pages().where(p => p.file.outlinks.includes(currentPage.file.link) && p.file.path.split('/').length > 1).sort(page => page.modified_date ? dv.date(page.modified_date) : page.file.mtime, 'desc');
|
||||
```
|
||||
|
||||
4. Random Images
|
||||
```
|
||||
const files = app.vault.getFiles();
|
||||
const imageFiles = files.filter(file =>
|
||||
file.path.startsWith("resources/") &&
|
||||
/\.(png|jpg|jpeg|gif|bmp|svg|webp)$/i.test(file.name)
|
||||
);
|
||||
return imageFiles
|
||||
.sort(() => 0.5 - Math.random())
|
||||
.slice(0, 10)
|
||||
.map(f => ({ file: f }));
|
||||
```
|
||||
|
||||
5. Random Videos
|
||||
```
|
||||
const files = app.vault.getFiles();
|
||||
const videoFiles = files.filter(file =>
|
||||
file.path.startsWith("resources/") &&
|
||||
/\.(mp4|webm|mov|avi|mkv|ogv)$/i.test(file.name)
|
||||
);
|
||||
return videoFiles
|
||||
.sort(() => 0.5 - Math.random())
|
||||
.slice(0, 10)
|
||||
.map(f => ({ file: f }));
|
||||
```
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
# Grid Explorer Custom Mode Fields Guide
|
||||
|
||||
## Overview
|
||||
|
||||
Grid Explorer supports setting field aliases and performing real-time computations in custom modes. This allows for more flexible display and calculation of note metadata.
|
||||
|
||||
## Basic Syntax
|
||||
|
||||
### Field Aliases
|
||||
|
||||
Use the `|` symbol to set a display name for a field:
|
||||
|
||||
```
|
||||
original_field|Display Name
|
||||
```
|
||||
|
||||
**Examples:**
|
||||
- `birthday|Birthday` - Displays the `birthday` field as "Birthday"
|
||||
- `status|Status` - Displays the `status` field as "Status"
|
||||
|
||||
### Computation Expressions
|
||||
|
||||
Use `{{ ... }}` to wrap JavaScript expressions for computation:
|
||||
|
||||
```
|
||||
field_name {{ expression }}
|
||||
```
|
||||
|
||||
**Available Variables:**
|
||||
|
||||
| Variable | Description | Example |
|
||||
|----------|-------------|---------|
|
||||
| `value` | Current field's raw value | `value` or `this` |
|
||||
| `metadata` | The complete frontmatter metadata object | `metadata.tags` |
|
||||
| `app` | Obsidian app instance | `app.vault.getMarkdownFiles()` |
|
||||
| `dv` | Dataview API instance (requires Dataview plugin) | `dv.pages('#tag')` |
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Combining Aliases and Computations
|
||||
|
||||
```
|
||||
field_name|Display Name {{ expression }}
|
||||
```
|
||||
|
||||
**Basic Example:**
|
||||
```
|
||||
age|Age {{ Math.floor((Date.now() - new Date(value).getTime()) / (1000 * 60 * 60 * 24 * 365.25)) }}
|
||||
```
|
||||
|
||||
**With Dataview Example:**
|
||||
```
|
||||
tags|Related Notes {{ dv.pages(value).length }}
|
||||
```
|
||||
|
||||
### Multi-field Combinations
|
||||
|
||||
```
|
||||
full_name|Full Name {{ (metadata.first_name || '') + ' ' + (metadata.last_name || '') }}
|
||||
```
|
||||
|
||||
## Practical Examples
|
||||
|
||||
### 1. Basic Computations
|
||||
|
||||
#### Calculate Age
|
||||
```
|
||||
birthday|Age {{ Math.floor((Date.now() - new Date(value).getTime()) / (1000 * 60 * 60 * 24 * 365.25)) }}
|
||||
```
|
||||
|
||||
#### Format Date
|
||||
```
|
||||
date|Date {{ new Date(value).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) }}
|
||||
```
|
||||
|
||||
### 2. Advanced Dataview Usage
|
||||
|
||||
#### Count Related Notes
|
||||
```
|
||||
tags|Related Notes {{ dv.pages(value).length }}
|
||||
```
|
||||
|
||||
#### Show Incomplete Tasks
|
||||
```
|
||||
file.folder|To-dos {{ dv.pages(`"${value}"`).file.tasks.where(t => !t.completed).length }}
|
||||
```
|
||||
|
||||
#### Calculate Tag Frequency
|
||||
```
|
||||
tags|Tag Stats {{ value.map(tag => `${tag}(${dv.pages(tag).length})`).join(', ') }}
|
||||
```
|
||||
|
||||
#### Show Last Modified Time
|
||||
```
|
||||
file.mtime|Last Modified {{ dv.date(value).toFormat('yyyy-MM-dd HH:mm') }}
|
||||
```
|
||||
|
||||
### 3. Conditional Display
|
||||
|
||||
```
|
||||
status|Status {{ value === 'active' ? 'Active' : 'Inactive' }}
|
||||
```
|
||||
|
||||
### 4. Progress Calculation
|
||||
|
||||
```
|
||||
progress|Progress {{ Math.round((metadata.completed / metadata.total) * 100) + '%' }}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
1. JavaScript code in computation expressions is executed dynamically when displayed
|
||||
2. If a computation fails, it falls back to the original value
|
||||
3. For security, avoid using potentially harmful code in expressions
|
||||
4. Keep expressions simple; use Dataview plugin for complex calculations
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If computations don't work as expected:
|
||||
|
||||
1. Check expression syntax
|
||||
2. Use `console.log(value, metadata)` for debugging
|
||||
3. Verify field names and case sensitivity
|
||||
4. Check for extra spaces or special characters
|
||||
|
||||
## Advanced Tips
|
||||
|
||||
- Use ternary operators for conditional rendering
|
||||
- Combine multiple fields in computations
|
||||
- Use JavaScript built-in functions (like `toLocaleString()`, `toFixed()`) for formatting
|
||||
- Access Obsidian API using the `app` variable
|
||||
---
|
||||
|
||||
This feature allows for flexible display and computation of note data without writing complex plugin code.
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
# Grid Explorer カスタムモード フィールドガイド
|
||||
|
||||
## 概要
|
||||
|
||||
Grid Explorer は、カスタムモードでフィールドのエイリアスを設定し、リアルタイムで計算を実行する機能をサポートしています。これにより、メタデータを柔軟に表示・計算できます。
|
||||
|
||||
## 基本構文
|
||||
|
||||
### フィールドエイリアス
|
||||
|
||||
`|` 記号を使用して、フィールドに表示名を設定します:
|
||||
|
||||
```
|
||||
元のフィールド名|表示名
|
||||
```
|
||||
|
||||
**例:**
|
||||
- `birthday|誕生日` - `birthday` フィールドを「誕生日」として表示
|
||||
- `status|状態` - `status` フィールドを「状態」として表示
|
||||
|
||||
### 計算式
|
||||
|
||||
`{{ ... }}` で JavaScript の式を囲んで計算を実行します:
|
||||
|
||||
```
|
||||
フィールド名 {{ 式 }}
|
||||
```
|
||||
|
||||
**使用可能な変数:**
|
||||
|
||||
| 変数 | 説明 | 例 |
|
||||
|------|------|-----|
|
||||
| `value` | 現在のフィールドの値 | `value` または `this` |
|
||||
| `metadata` | フロントマターのメタデータオブジェクト | `metadata.tags` |
|
||||
| `app` | Obsidian アプリケーションインスタンス | `app.vault.getMarkdownFiles()` |
|
||||
| `dv` | Dataview API インスタンス(Dataview プラグインが必要) | `dv.pages('#tag')` |
|
||||
|
||||
## 高度な使い方
|
||||
|
||||
### エイリアスと計算の組み合わせ
|
||||
|
||||
```
|
||||
フィールド名|表示名 {{ 式 }}
|
||||
```
|
||||
|
||||
**基本例:**
|
||||
```
|
||||
birthday|年齢 {{ Math.floor((Date.now() - new Date(value).getTime()) / (1000 * 60 * 60 * 24 * 365.25)) }}
|
||||
```
|
||||
|
||||
**Dataview 使用例:**
|
||||
```
|
||||
tags|関連ノート {{ dv.pages(value).length }}
|
||||
```
|
||||
|
||||
### 複数フィールドの組み合わせ
|
||||
|
||||
```
|
||||
full_name|フルネーム {{ (metadata.first_name || '') + ' ' + (metadata.last_name || '') }}
|
||||
```
|
||||
|
||||
## 実践的な例
|
||||
|
||||
### 1. 基本的な計算
|
||||
|
||||
#### 年齢の計算
|
||||
```
|
||||
birthday|年齢 {{ Math.floor((Date.now() - new Date(value).getTime()) / (1000 * 60 * 60 * 24 * 365.25)) }}
|
||||
```
|
||||
|
||||
#### 日付のフォーマット
|
||||
```
|
||||
date|日付 {{ new Date(value).toLocaleDateString('ja-JP', { year: 'numeric', month: 'long', day: 'numeric' }) }}
|
||||
```
|
||||
|
||||
### 2. Dataview の高度な機能
|
||||
|
||||
#### 関連ノートのカウント
|
||||
```
|
||||
tags|関連ノート {{ dv.pages(value).length }}
|
||||
```
|
||||
|
||||
#### 未完了タスクの表示
|
||||
```
|
||||
file.folder|ToDo {{ dv.pages(`"${value}"`).file.tasks.where(t => !t.completed).length }}
|
||||
```
|
||||
|
||||
#### タグの使用頻度
|
||||
```
|
||||
tags|タグ統計 {{ value.map(tag => `${tag}(${dv.pages(tag).length})`).join(', ') }}
|
||||
```
|
||||
|
||||
#### 最終更新日時の表示
|
||||
```
|
||||
file.mtime|最終更新 {{ dv.date(value).toFormat('yyyy-MM-dd HH:mm') }}
|
||||
```
|
||||
|
||||
### 3. 条件付き表示
|
||||
|
||||
```
|
||||
status|状態 {{ value === 'active' ? '有効' : '無効' }}
|
||||
```
|
||||
|
||||
### 4. 進捗率の計算
|
||||
|
||||
```
|
||||
progress|進捗 {{ Math.round((metadata.completed / metadata.total) * 100) + '%' }}
|
||||
```
|
||||
|
||||
## 注意事項
|
||||
|
||||
1. 計算式内の JavaScript コードは表示時に動的に実行されます
|
||||
2. 計算に失敗した場合は元の値が表示されます
|
||||
3. セキュリティのため、危険なコードは使用しないでください
|
||||
4. 複雑な計算には Dataview プラグインの使用を推奨します
|
||||
|
||||
## トラブルシューティング
|
||||
|
||||
計算が期待通りに動作しない場合:
|
||||
|
||||
1. 式の構文を確認してください
|
||||
2. `console.log(value, metadata)` でデバッグできます
|
||||
3. フィールド名と大文字小文字を確認してください
|
||||
4. 余分なスペースや特殊文字を確認してください
|
||||
|
||||
## 高度なヒント
|
||||
|
||||
- 条件付き表示には三項演算子を使用します
|
||||
- 複数のフィールドを組み合わせて計算します
|
||||
- 表示のフォーマットには `toLocaleString()` や `toFixed()` などの JavaScript 組み込み関数を使用します
|
||||
- `app` 変数で Obsidian API にアクセスできます
|
||||
---
|
||||
|
||||
この機能を使用すると、複雑なプラグインコードを書かずに、ノートデータを柔軟に表示・計算できます。
|
||||
|
|
@ -1,140 +0,0 @@
|
|||
# Grid Explorer 自訂模式欄位說明
|
||||
|
||||
## 功能概述
|
||||
|
||||
Grid Explorer 現在支援在自訂模式中為欄位設定別名,並在顯示時進行即時計算。這讓您能夠以更靈活的方式顯示和計算筆記中的元數據。
|
||||
|
||||
## 基本語法
|
||||
|
||||
### 欄位別名
|
||||
|
||||
使用 `|` 符號為欄位設定顯示名稱:
|
||||
|
||||
```
|
||||
原始欄位名稱|顯示名稱
|
||||
```
|
||||
|
||||
**範例:**
|
||||
- `birthday|生日` - 將 `birthday` 欄位顯示為「生日」
|
||||
- `status|狀態` - 將 `status` 欄位顯示為「狀態」
|
||||
|
||||
### 計算表達式
|
||||
|
||||
使用 `{{ ... }}` 包裝 JavaScript 表達式來進行計算:
|
||||
|
||||
```
|
||||
欄位名稱 {{ 表達式 }}
|
||||
```
|
||||
|
||||
**可用變數:**
|
||||
|
||||
在計算表達式中,您可以使用以下變數:
|
||||
|
||||
| 變數 | 說明 | 範例 |
|
||||
|------|------|------|
|
||||
| `value` | 當前欄位的原始值 | `value` 或 `this` |
|
||||
| `metadata` | 整個 frontmatter 元數據對象 | `metadata.tags` |
|
||||
| `app` | Obsidian 應用程式實例 | `app.vault.getMarkdownFiles()` |
|
||||
| `dv` | Dataview API 實例(需安裝 Dataview 插件) | `dv.pages('#tag')` |
|
||||
|
||||
## 進階用法
|
||||
|
||||
### 結合別名與計算
|
||||
|
||||
您可以同時使用別名和計算表達式:
|
||||
|
||||
```
|
||||
欄位名稱|顯示名稱 {{ 表達式 }}
|
||||
```
|
||||
|
||||
**基本範例:**
|
||||
```
|
||||
birthday|年齡 {{ Math.floor((Date.now() - new Date(value).getTime()) / (1000 * 60 * 60 * 24 * 365.25)) }}
|
||||
```
|
||||
|
||||
**結合 Dataview 範例:**
|
||||
```
|
||||
tags|相關筆記 {{ dv.pages(value).length }}
|
||||
```
|
||||
|
||||
### 多欄位組合
|
||||
|
||||
您可以在計算表達式中引用其他欄位:
|
||||
|
||||
```
|
||||
full_name|全名 {{ (metadata.first_name || '') + ' ' + (metadata.last_name || '') }}
|
||||
```
|
||||
|
||||
## 實際應用範例
|
||||
|
||||
### 1. 基本計算
|
||||
|
||||
#### 計算年齡
|
||||
```
|
||||
birthday|年齡 {{ Math.floor((Date.now() - new Date(value).getTime()) / (1000 * 60 * 60 * 24 * 365.25)) }}
|
||||
```
|
||||
|
||||
#### 格式化日期
|
||||
```
|
||||
date|日期 {{ new Date(value).toLocaleDateString('zh-TW', { year: 'numeric', month: 'long', day: 'numeric' }) }}
|
||||
```
|
||||
|
||||
### 2. 使用 Dataview 的進階功能
|
||||
|
||||
#### 計算相關筆記數量
|
||||
```
|
||||
tags|相關筆記 {{ dv.pages(value).length }}
|
||||
```
|
||||
|
||||
#### 顯示未完成任務數
|
||||
```
|
||||
file.folder|待辦事項 {{ dv.pages(`"${value}"`).file.tasks.where(t => !t.completed).length }}
|
||||
```
|
||||
|
||||
#### 計算標籤使用頻率
|
||||
```
|
||||
tags|標籤統計 {{ value.map(tag => `${tag}(${dv.pages(tag).length})`).join(', ') }}
|
||||
```
|
||||
|
||||
#### 顯示最近修改時間
|
||||
```
|
||||
file.mtime|最後修改 {{ dv.date(value).toFormat('yyyy-MM-dd HH:mm') }}
|
||||
```
|
||||
|
||||
### 3. 條件顯示
|
||||
|
||||
```
|
||||
status|狀態 {{ value === 'active' ? '啟用' : '停用' }}
|
||||
```
|
||||
|
||||
### 4. 計算進度
|
||||
|
||||
```
|
||||
progress|進度 {{ Math.round((metadata.completed / metadata.total) * 100) + '%' }}
|
||||
```
|
||||
|
||||
## 注意事項
|
||||
|
||||
1. 計算表達式中的 JavaScript 代碼會在顯示時動態執行
|
||||
2. 如果計算表達式出錯,會自動回退顯示原始值
|
||||
3. 為確保安全性,不建議在表達式中使用可能有害的代碼
|
||||
4. 表達式應保持簡潔,複雜的計算建議使用 Dataview 插件
|
||||
|
||||
## 疑難排解
|
||||
|
||||
如果計算結果不符合預期:
|
||||
|
||||
1. 檢查表達式語法是否正確
|
||||
2. 使用 `console.log(value, metadata)` 調試表達式
|
||||
3. 確認欄位名稱和大小寫是否正確
|
||||
4. 檢查是否有額外的空格或特殊字符
|
||||
|
||||
## 進階提示
|
||||
|
||||
- 使用三元運算符進行條件渲染
|
||||
- 結合多個欄位進行計算
|
||||
- 使用 JavaScript 的內建函數(如 `toLocaleString()`、`toFixed()` 等)格式化輸出
|
||||
- 在表達式中使用 `app` 變數訪問 Obsidian API
|
||||
---
|
||||
|
||||
這個功能讓您能夠更靈活地呈現和計算筆記中的數據,無需編寫複雜的插件代碼即可實現強大的顯示效果。
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "gridexplorer",
|
||||
"name": "GridExplorer",
|
||||
"version": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"minAppVersion": "1.8.7",
|
||||
"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": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "gridexplorer",
|
||||
"version": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jszip": "^3.10.1"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "gridexplorer",
|
||||
"version": "3.3.1",
|
||||
"version": "3.4.0",
|
||||
"description": "Browse note files in a grid view.",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -153,11 +153,15 @@ export class FileWatcher {
|
|||
return;
|
||||
}
|
||||
|
||||
// 處理自訂模式,僅當腳本包含 dv.current 時才觸發
|
||||
// 處理自訂模式,僅當查詢包含 this.file 時才觸發
|
||||
if (sourceMode.startsWith('custom-')) {
|
||||
const mode = this.plugin.settings.customModes.find(m => m.internalName === sourceMode);
|
||||
if (mode && mode.dataviewCode.includes('dv.current')) {
|
||||
this.scheduleRender();
|
||||
if (mode) {
|
||||
const hasThisFile = mode.dataviewQuery.includes('this.file') ||
|
||||
(mode.options && mode.options.some(opt => opt.dataviewQuery.includes('this.file')));
|
||||
if (hasThisFile) {
|
||||
this.scheduleRender();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,16 +72,6 @@ interface AppWithCommands {
|
|||
commands?: CommandManager;
|
||||
}
|
||||
|
||||
interface AppWithDataviewPlugin {
|
||||
plugins?: {
|
||||
plugins?: {
|
||||
dataview?: {
|
||||
api?: unknown;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface MenuItemWithWarning {
|
||||
setWarning: (warning: boolean) => void;
|
||||
}
|
||||
|
|
@ -913,59 +903,13 @@ export class GridView extends ItemView {
|
|||
|
||||
// 如果 fields 不為空,則使用它來顯示摘要
|
||||
if (fields) {
|
||||
// 以逗號拆分,但忽略 {{ ... }} 內的逗號
|
||||
const fieldList: string[] = (() => {
|
||||
const parts: string[] = [];
|
||||
let buf = '';
|
||||
let depth = 0; // 在 {{...}} 內時 depth > 0
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
const ch = fields[i];
|
||||
const next = fields[i + 1];
|
||||
// 偵測 '{{'
|
||||
if (ch === '{' && next === '{') {
|
||||
depth++;
|
||||
buf += '{{';
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// 偵測 '}}'
|
||||
if (ch === '}' && next === '}') {
|
||||
if (depth > 0) depth--;
|
||||
buf += '}}';
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
// 只有在不在 {{...}} 內時,逗號才作為分隔符
|
||||
if (ch === ',' && depth === 0) {
|
||||
if (buf.trim()) parts.push(buf.trim());
|
||||
buf = '';
|
||||
continue;
|
||||
}
|
||||
buf += ch;
|
||||
}
|
||||
if (buf.trim()) parts.push(buf.trim());
|
||||
return parts;
|
||||
})();
|
||||
const fieldList = fields.split(',').map(f => f.trim()).filter(Boolean);
|
||||
const fieldValues: string[] = [];
|
||||
|
||||
// 收集所有欄位值,並處理別名("原始欄位|別名")
|
||||
fieldList.forEach(fieldEntry => {
|
||||
// 解析欄位 (fieldKey)、別名 (labelName)、運算式 (calcExpr)
|
||||
// 格式示例:
|
||||
// birthday|年齡 {{ Math.floor(...) }}
|
||||
// birthday {{ Math.floor(...) }}
|
||||
// birthday|年齡
|
||||
// birthday
|
||||
|
||||
let raw = fieldEntry.trim();
|
||||
let calcExpr: string | null = null;
|
||||
// 先取出運算區塊 {{ ... }}
|
||||
const calcMatch = raw.match(/\{\{(.*?)\}\}/);
|
||||
if (calcMatch) {
|
||||
calcExpr = calcMatch[1];
|
||||
raw = raw.substring(0, calcMatch.index).trim(); // 去掉運算部分
|
||||
}
|
||||
// 再處理 alias
|
||||
const raw = fieldEntry.trim();
|
||||
// 處理 alias
|
||||
const aliasIdx = raw.lastIndexOf('|');
|
||||
let fieldKey: string;
|
||||
let labelName: string;
|
||||
|
|
@ -979,32 +923,14 @@ export class GridView extends ItemView {
|
|||
|
||||
const rawValue = getFrontmatterValue(metadata, fieldKey);
|
||||
if (rawValue !== undefined && rawValue !== '' && rawValue !== null) {
|
||||
|
||||
const value = formatFrontmatterValue(rawValue);
|
||||
|
||||
// 如果是數字,則加入千位分隔符號
|
||||
// 如果是陣列,則轉換為字串
|
||||
|
||||
let outputValue: unknown = value;
|
||||
if (calcExpr) {
|
||||
try {
|
||||
const fn = new Function('value', 'metadata', 'app', 'dv', `return (${calcExpr});`) as (value: string, metadata: FrontMatterCache | undefined, app: typeof this.app, dv: unknown) => unknown;
|
||||
|
||||
// 獲取 Dataview API
|
||||
const dvApi = (this.app as AppWithDataviewPlugin).plugins?.plugins?.dataview?.api;
|
||||
|
||||
outputValue = fn(value, metadata, this.app, dvApi);
|
||||
} catch (error) {
|
||||
console.error('GridExplorer: evaluate displayName error', error);
|
||||
}
|
||||
}
|
||||
fieldValues.push(`${labelName}: ${formatFrontmatterValue(outputValue)}`);
|
||||
fieldValues.push(`${labelName}: ${value}`);
|
||||
}
|
||||
});
|
||||
|
||||
// 如果有找到任何欄位值,則組合起來
|
||||
if (fieldValues.length > 0) {
|
||||
summaryValue = fieldValues.join('\n'); // 使用 | 分隔不同欄位
|
||||
summaryValue = fieldValues.join('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
22
src/main.ts
22
src/main.ts
|
|
@ -795,6 +795,7 @@ export default class GridExplorerPlugin extends Plugin {
|
|||
async loadSettings() {
|
||||
const loadedSettings = await this.loadData() as Partial<GallerySettings> | null;
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, loadedSettings ?? {});
|
||||
migrateDataviewCodeToQuery(this.settings);
|
||||
updateCustomDocumentExtensions(this.settings);
|
||||
}
|
||||
|
||||
|
|
@ -821,3 +822,24 @@ export default class GridExplorerPlugin extends Plugin {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
function migrateDataviewCodeToQuery(settings: GallerySettings): void {
|
||||
if (!settings.customModes) return;
|
||||
for (const mode of settings.customModes) {
|
||||
if ('dataviewCode' in mode && !('dataviewQuery' in mode)) {
|
||||
const oldCode = (mode as unknown as { dataviewCode: string }).dataviewCode;
|
||||
(mode as Record<string, unknown>).dataviewQuery = oldCode;
|
||||
// 刪除舊欄位
|
||||
delete (mode as unknown as Record<string, unknown>).dataviewCode;
|
||||
}
|
||||
if (mode.options) {
|
||||
for (const opt of mode.options) {
|
||||
if ('dataviewCode' in opt && !('dataviewQuery' in opt)) {
|
||||
const oldCode = (opt as unknown as { dataviewCode: string }).dataviewCode;
|
||||
(opt as Record<string, unknown>).dataviewQuery = oldCode;
|
||||
delete (opt as unknown as Record<string, unknown>).dataviewCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ export class CustomModeModal extends Modal {
|
|||
let name = this.mode?.name || t('default');
|
||||
// 支援多個子選項,每個選項包含名稱與 Dataview 程式碼
|
||||
const options: CustomModeOption[] = this.mode?.options ? this.mode.options.map(opt => ({ ...opt })) : []; // 其他選項(不含 Default)
|
||||
// 向下相容:使用第一個選項作為主要 dataviewCode
|
||||
let dataviewCode = this.mode ? this.mode.dataviewCode : '';
|
||||
// 向下相容:使用第一個選項作為主要 dataviewQuery
|
||||
let dataviewQuery = this.mode ? this.mode.dataviewQuery : '';
|
||||
let enabled = this.mode ? (this.mode.enabled ?? true) : true;
|
||||
let fields = this.mode ? this.mode.fields : '';
|
||||
|
||||
|
|
@ -63,14 +63,17 @@ export class CustomModeModal extends Modal {
|
|||
.onChange(v => name = v);
|
||||
});
|
||||
|
||||
// 示範查詢
|
||||
const dataviewExampleQuery = 'LIST FROM #tag\nTABLE status FROM "folder" SORT file.mtime DESC'
|
||||
|
||||
dvSetting.addTextArea(text => {
|
||||
text.setValue(dataviewCode)
|
||||
text.setValue(dataviewQuery)
|
||||
.onChange(value => {
|
||||
dataviewCode = value;
|
||||
dataviewQuery = value;
|
||||
})
|
||||
.setPlaceholder('Dataview js code');
|
||||
.setPlaceholder(dataviewExampleQuery);
|
||||
// 給TextArea有足夠的垂直空間和完整的寬度
|
||||
text.inputEl.setAttr('rows', 6);
|
||||
text.inputEl.setAttr('rows', 4);
|
||||
text.inputEl.addClass('ge-custommode-code-input');
|
||||
});
|
||||
|
||||
|
|
@ -195,12 +198,12 @@ export class CustomModeModal extends Modal {
|
|||
});
|
||||
})
|
||||
.addTextArea(text => {
|
||||
text.setPlaceholder('Dataview js code')
|
||||
.setValue(opt.dataviewCode)
|
||||
text.setPlaceholder(dataviewExampleQuery)
|
||||
.setValue(opt.dataviewQuery)
|
||||
.onChange(val => {
|
||||
opt.dataviewCode = val;
|
||||
opt.dataviewQuery = val;
|
||||
});
|
||||
text.inputEl.setAttr('rows', 6);
|
||||
text.inputEl.setAttr('rows', 4);
|
||||
text.inputEl.addClass('ge-custommode-code-input');
|
||||
})
|
||||
.addText(text => {
|
||||
|
|
@ -314,7 +317,7 @@ export class CustomModeModal extends Modal {
|
|||
.addButton(btn => {
|
||||
btn.setButtonText(t('add_option'))
|
||||
.onClick(() => {
|
||||
options.push({ name: `${t('option')} ${options.length + 1}`, dataviewCode: '' });
|
||||
options.push({ name: `${t('option')} ${options.length + 1}`, dataviewQuery: '' });
|
||||
renderOptions();
|
||||
});
|
||||
});
|
||||
|
|
@ -335,7 +338,7 @@ export class CustomModeModal extends Modal {
|
|||
icon,
|
||||
displayName,
|
||||
name,
|
||||
dataviewCode,
|
||||
dataviewQuery,
|
||||
options: options,
|
||||
enabled,
|
||||
fields
|
||||
|
|
|
|||
|
|
@ -177,12 +177,14 @@ export async function renderFolder(gridView: GridView, container: HTMLElement) {
|
|||
|
||||
if (visibleSubfolders.length > 0) {
|
||||
const isCompact = folderStyle === 'compact';
|
||||
const foldersContainer = isCompact ? container.createDiv('ge-folders-container') : null;
|
||||
const foldersContainer = isCompact
|
||||
? container.createDiv('ge-folders-container')
|
||||
: container.createDiv('ge-folders-grid-container');
|
||||
|
||||
for (const folder of visibleSubfolders) {
|
||||
const folderEl = isCompact && foldersContainer
|
||||
? foldersContainer.createDiv('ge-folder-button')
|
||||
: container.createDiv('ge-grid-item ge-folder-item');
|
||||
const folderEl = foldersContainer.createDiv(
|
||||
isCompact ? 'ge-folder-button' : 'ge-grid-item ge-folder-item'
|
||||
);
|
||||
|
||||
if (gridView.showIgnoredItems && isFolderActuallyIgnored(folder)) {
|
||||
folderEl.addClass('ge-folder-ignored');
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { t } from './translations';
|
|||
|
||||
export interface CustomModeOption {
|
||||
name: string;
|
||||
dataviewCode: string;
|
||||
dataviewQuery: string;
|
||||
fields?: string;
|
||||
}
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ export interface CustomMode {
|
|||
icon: string;
|
||||
displayName: string;
|
||||
name: string;
|
||||
dataviewCode: string;
|
||||
dataviewQuery: string;
|
||||
options?: CustomModeOption[];
|
||||
enabled?: boolean; // 是否顯示此自訂模式,預設為 true
|
||||
fields?: string;
|
||||
|
|
@ -31,9 +31,9 @@ function isCustomMode(value: unknown): value is CustomMode {
|
|||
'displayName' in value &&
|
||||
typeof value.displayName === 'string' &&
|
||||
value.displayName.length > 0 &&
|
||||
'dataviewCode' in value &&
|
||||
typeof value.dataviewCode === 'string' &&
|
||||
value.dataviewCode.length > 0
|
||||
'dataviewQuery' in value &&
|
||||
typeof value.dataviewQuery === 'string' &&
|
||||
value.dataviewQuery.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ export const DEFAULT_SETTINGS: GallerySettings = {
|
|||
folderDisplayStyle: 'show', // 預設直接顯示資料夾
|
||||
showMediaFiles: true, // 預設顯示圖片和影片
|
||||
showVideoThumbnails: true, // 預設顯示影片縮圖
|
||||
defaultOpenLocation: 'left', // 預設開啟位置:左側邊欄
|
||||
defaultOpenLocation: Platform.isMobile ? 'tab' : 'left', // 行動裝置預設開啟位置:新分頁,非行動裝置預設開啟位置:左側邊欄
|
||||
reuseExistingLeaf: true, // 預設重用現有的網格視圖
|
||||
showBookmarksMode: true, // 預設顯示書籤模式
|
||||
showSearchMode: true, // 預設顯示搜尋結果模式
|
||||
|
|
@ -149,7 +149,7 @@ export const DEFAULT_SETTINGS: GallerySettings = {
|
|||
icon: '🧩',
|
||||
displayName: 'My Books (Sample)',
|
||||
name: 'Default',
|
||||
dataviewCode: 'return dv.pages("#Book");',
|
||||
dataviewQuery: 'LIST FROM #Book',
|
||||
}
|
||||
], // 自訂模式
|
||||
quickAccessCommandPath: '', // Path used by "Open quick access folder" command
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ export default {
|
|||
'custom_mode_display_name': 'Display Name',
|
||||
'custom_mode_display_name_desc': 'The name displayed in the mode menu',
|
||||
'custom_mode_dataview_code': 'Dataview Query',
|
||||
'custom_mode_dataview_code_desc': 'Enter a Dataview query to get the list of files',
|
||||
'custom_mode_dataview_code_desc': 'Enter a Dataview query to get the list of files, e.g., LIST FROM #tag',
|
||||
'custom_mode_sub_options': 'Custom Mode Sub Options',
|
||||
'custom_mode_fields_placeholder': 'Enter frontmatter field names, separated by commas (e.g. date,category,status) (optional)',
|
||||
'dataview_plugin_not_enabled': 'Dataview plugin is not enabled',
|
||||
|
|
|
|||
|
|
@ -159,8 +159,8 @@ export default {
|
|||
'custom_mode': 'カスタムモード',
|
||||
'custom_mode_display_name': '表示名',
|
||||
'custom_mode_display_name_desc': 'モードメニューで表示する名前',
|
||||
'custom_mode_dataview_code': 'Dataviewjs コード',
|
||||
'custom_mode_dataview_code_desc': 'ファイルリストを取得する Dataviewjs コードを入力',
|
||||
'custom_mode_dataview_code': 'Dataview クエリ',
|
||||
'custom_mode_dataview_code_desc': 'ファイルリストを取得するための Dataview クエリを入力します(例:LIST FROM #tag)',
|
||||
'custom_mode_sub_options': 'カスタムモード子選択肢',
|
||||
'custom_mode_fields_placeholder': 'frontmatterのフィールド名をカンマ区切りで入力 (例: date,category,status) (任意)',
|
||||
'dataview_plugin_not_enabled': 'Dataview プラグインが有効になっていません',
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ export default {
|
|||
'custom_mode_display_name': '표시 이름',
|
||||
'custom_mode_display_name_desc': '모드 메뉴에 표시되는 이름',
|
||||
'custom_mode_dataview_code': 'Dataview 쿼리',
|
||||
'custom_mode_dataview_code_desc': 'Dataview 쿼리를 입력하여 파일 목록 가져오기',
|
||||
'custom_mode_dataview_code_desc': '파일 목록을 가져올 Dataview 쿼리를 입력하세요 (예: LIST FROM #tag)',
|
||||
'custom_mode_sub_options': '사용자 정의 모드 하위 옵션',
|
||||
'custom_mode_fields_placeholder': '프론트매터 필드 이름을 쉼표로 구분하여 입력 (예: date,category,status) (선택사항)',
|
||||
'dataview_plugin_not_enabled': 'Dataview 플러그인이 활성화되지 않았습니다',
|
||||
|
|
|
|||
|
|
@ -159,8 +159,8 @@ export default {
|
|||
'custom_mode': 'Режим отображения',
|
||||
'custom_mode_display_name': 'Отображаемое имя',
|
||||
'custom_mode_display_name_desc': 'Отображаемое имя в меню режимов',
|
||||
'custom_mode_dataview_code': 'Код Dataview',
|
||||
'custom_mode_dataview_code_desc': 'Введите код Dataview для получения списка файлов',
|
||||
'custom_mode_dataview_code': 'Запрос Dataview',
|
||||
'custom_mode_dataview_code_desc': 'Введите запрос Dataview для получения списка файлов (например, LIST FROM #tag)',
|
||||
'custom_mode_sub_options': 'Подопции пользовательского режима',
|
||||
'custom_mode_fields_placeholder': 'Введите названия полей frontmatter через запятую (напр.: date,category,status) (необязательно)',
|
||||
'dataview_plugin_not_enabled': 'Dataview плагин не включен',
|
||||
|
|
|
|||
|
|
@ -159,8 +159,8 @@ export default {
|
|||
'custom_mode': 'Користувацький режим',
|
||||
'custom_mode_display_name': 'Відображувана назва',
|
||||
'custom_mode_display_name_desc': 'Назва, що відображається в меню режимів',
|
||||
'custom_mode_dataview_code': 'Код Dataview',
|
||||
'custom_mode_dataview_code_desc': 'Введіть запит Dataview для отримання списку файлів',
|
||||
'custom_mode_dataview_code': 'Запит Dataview',
|
||||
'custom_mode_dataview_code_desc': 'Введіть запит Dataview для отримання списку файлів (наприклад, LIST FROM #tag)',
|
||||
'custom_mode_sub_options': 'Опції користувацького режиму',
|
||||
'custom_mode_fields_placeholder': 'Введіть назви полів frontmatter, розділені комами (напр. date,category,status) (необов\'язково)',
|
||||
'dataview_plugin_not_enabled': 'Плагін Dataview не увімкнено',
|
||||
|
|
|
|||
|
|
@ -160,8 +160,8 @@ export default {
|
|||
'custom_mode': '自訂模式',
|
||||
'custom_mode_display_name': '顯示名稱',
|
||||
'custom_mode_display_name_desc': '在模式選單中顯示的名稱',
|
||||
'custom_mode_dataview_code': 'Dataviewjs 代碼',
|
||||
'custom_mode_dataview_code_desc': '輸入 Dataviewjs 代碼以取得檔案列表',
|
||||
'custom_mode_dataview_code': 'Dataview 查詢',
|
||||
'custom_mode_dataview_code_desc': '輸入 Dataview 查詢語法以取得檔案列表,例如:LIST FROM #tag',
|
||||
'custom_mode_sub_options': '自訂模式子選項',
|
||||
'custom_mode_fields_placeholder': '輸入 frontmatter 欄位名稱,用逗號分隔 (如: date,category,status) (可選)',
|
||||
'dataview_plugin_not_enabled': 'Dataview 外掛未啟用',
|
||||
|
|
|
|||
|
|
@ -159,8 +159,8 @@ export default {
|
|||
'custom_mode': '自定义模式',
|
||||
'custom_mode_display_name': '显示名称',
|
||||
'custom_mode_display_name_desc': '在模式菜单中显示的名称',
|
||||
'custom_mode_dataview_code': 'Dataviewjs 代码',
|
||||
'custom_mode_dataview_code_desc': '输入 Dataviewjs 代码以获取文件列表',
|
||||
'custom_mode_dataview_code': 'Dataview 查询',
|
||||
'custom_mode_dataview_code_desc': '输入 Dataview 查询语法以获取文件列表,例如:LIST FROM #tag',
|
||||
'custom_mode_sub_options': '自定义模式子选项',
|
||||
'custom_mode_fields_placeholder': '输入 frontmatter 栏位名称,用逗号分隔 (如: date,category,status) (可选)',
|
||||
'dataview_plugin_not_enabled': 'Dataview 外掛未啟用',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { TFile, TFolder, getFrontMatterInfo, Notice, View, EventRef } from 'obsidian';
|
||||
import { App, TFile, TFolder, getFrontMatterInfo, Notice, View, EventRef } from 'obsidian';
|
||||
import { GridView } from '../GridView';
|
||||
import { type GallerySettings } from '../settings';
|
||||
import { t } from '../translations';
|
||||
|
|
@ -21,6 +21,14 @@ interface DataviewApi {
|
|||
};
|
||||
page: (path: string) => Record<string, unknown>;
|
||||
current?: () => Record<string, unknown>;
|
||||
query: (query: string, originFile?: string) => Promise<{
|
||||
successful: boolean;
|
||||
value: {
|
||||
type: string;
|
||||
values: unknown[];
|
||||
};
|
||||
error?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface TasksPlugin {
|
||||
|
|
@ -615,43 +623,44 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
|
|||
return [];
|
||||
}
|
||||
|
||||
// 依據 GridView 目前選擇的選項決定要使用哪段 Dataview 程式碼
|
||||
let dvCode: string | undefined = mode?.dataviewCode;
|
||||
let dvQuery: string | undefined = mode?.dataviewQuery;
|
||||
if (mode && mode.options && mode.options.length > 0) {
|
||||
const idx = gridView.customOptionIndex ?? -1;
|
||||
if (idx >= 0 && idx < mode.options.length) {
|
||||
dvCode = mode.options[idx].dataviewCode;
|
||||
dvQuery = mode.options[idx].dataviewQuery;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dvQuery) return [];
|
||||
|
||||
try {
|
||||
const activeFile = app.workspace.getActiveFile();
|
||||
if (activeFile) {
|
||||
// 暫時添加 .current 到 dvApi 物件內
|
||||
dvApi.current = () => dvApi.page(activeFile.path);
|
||||
}
|
||||
const originPath = activeFile?.path ?? '';
|
||||
|
||||
const func = new Function('app', 'dv', dvCode);
|
||||
const dvPagesResult = func(app, dvApi) as unknown;
|
||||
const dvPages = Array.isArray(dvPagesResult)
|
||||
? (dvPagesResult as unknown[])
|
||||
: Array.from((dvPagesResult as Iterable<unknown> | null | undefined) || []);
|
||||
const result = await dvApi.query(dvQuery, originPath);
|
||||
|
||||
if (!dvPages || dvPages.length === 0) {
|
||||
if (!result.successful) {
|
||||
console.error('Grid Explorer: Dataview query failed.', result.error);
|
||||
return [];
|
||||
}
|
||||
|
||||
const files = new Set<TFile>();
|
||||
const queryResult = result.value;
|
||||
|
||||
for (const page of dvPages as Array<{ file?: { path?: string } } | null | undefined>) {
|
||||
// Add null checks for page and page.file
|
||||
if (page?.file?.path) {
|
||||
const file = app.vault.getAbstractFileByPath(page.file.path);
|
||||
if (file instanceof TFile) {
|
||||
files.add(file);
|
||||
}
|
||||
if (queryResult.type === 'list') {
|
||||
// LIST 查詢:values 是 Literal[],每個元素是 Link
|
||||
for (const item of queryResult.values) {
|
||||
const path = extractPath(item);
|
||||
if (path) addFileByPath(app, files, path);
|
||||
}
|
||||
} else if (queryResult.type === 'table') {
|
||||
// TABLE 查詢:values 是 Literal[][],第一欄是 file link
|
||||
for (const row of queryResult.values) {
|
||||
const path = extractPath((row as unknown[])[0]);
|
||||
if (path) addFileByPath(app, files, path);
|
||||
}
|
||||
}
|
||||
|
||||
return sortFiles(Array.from(files), gridView);
|
||||
} catch (error) {
|
||||
console.error('Grid Explorer: Error executing Dataview query.', error);
|
||||
|
|
@ -661,3 +670,17 @@ export async function getFiles(gridView: GridView, includeMediaFiles: boolean):
|
|||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function extractPath(literal: unknown): string | null {
|
||||
// Dataview 的 Link 物件有 .path 屬性
|
||||
if (literal && typeof literal === 'object' && 'path' in literal) {
|
||||
const path = (literal as Record<string, unknown>).path;
|
||||
if (typeof path === 'string') return path;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function addFileByPath(app: App, files: Set<TFile>, path: string): void {
|
||||
const file = app.vault.getAbstractFileByPath(path);
|
||||
if (file instanceof TFile) files.add(file);
|
||||
}
|
||||
|
|
|
|||
106
styles.css
106
styles.css
|
|
@ -180,8 +180,8 @@
|
|||
height: 14px;
|
||||
}
|
||||
|
||||
.ge-multiline-title {
|
||||
white-space: normal !important;
|
||||
.ge-title.ge-multiline-title {
|
||||
white-space: normal;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
|
|
@ -208,12 +208,11 @@
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
.ge-content-area-p-field,
|
||||
.ge-content-area .ge-content-area-p-field {
|
||||
.ge-content-area p.ge-content-area-p-field {
|
||||
display: block;
|
||||
-webkit-line-clamp: unset !important;
|
||||
line-clamp: unset !important;
|
||||
-webkit-box-orient: unset !important;
|
||||
-webkit-line-clamp: unset;
|
||||
line-clamp: unset;
|
||||
-webkit-box-orient: unset;
|
||||
white-space: pre-wrap;
|
||||
overflow: visible;
|
||||
height: auto;
|
||||
|
|
@ -2171,11 +2170,10 @@ a.ge-current-folder:hover {
|
|||
|
||||
/* --------------------------------------------------------------------------- */
|
||||
|
||||
/* Popup Modal Styles */
|
||||
.ge-popup-modal {
|
||||
background: transparent !important;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15) !important;
|
||||
border: none !important;
|
||||
.modal-container .modal.ge-popup-modal {
|
||||
background: transparent;
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||
border: none;
|
||||
}
|
||||
|
||||
.ge-popup-modal-reset {
|
||||
|
|
@ -2333,7 +2331,7 @@ a.ge-current-folder:hover {
|
|||
.is-tablet .ge-note-close-button:not(.clickable-icon),
|
||||
.is-tablet .ge-note-edit-button:not(.clickable-icon),
|
||||
.is-tablet .ge-note-info-button:not(.clickable-icon) {
|
||||
padding: 6px 12px !important;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.ge-note-close-button:hover,
|
||||
|
|
@ -2353,8 +2351,8 @@ a.ge-current-folder:hover {
|
|||
.is-mobile .ge-note-info-button:focus,
|
||||
.is-mobile .ge-note-info-button:focus-visible {
|
||||
outline: none;
|
||||
background-color: var(--interactive-normal) !important;
|
||||
transform: none !important;
|
||||
background-color: var(--interactive-normal);
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.ge-note-content-container {
|
||||
|
|
@ -2593,36 +2591,36 @@ a.ge-current-folder:hover {
|
|||
/* --------------------------------------------------------------------------- */
|
||||
|
||||
/* 通用輸入框樣式 Universal Input Field Styles */
|
||||
.ge-input-field {
|
||||
width: 100% !important;
|
||||
padding: 8px 12px !important;
|
||||
border: 1px solid var(--background-modifier-border) !important;
|
||||
border-radius: var(--input-radius) !important;
|
||||
background-color: var(--background-primary) !important;
|
||||
color: var(--text-normal) !important;
|
||||
font-size: 14px !important;
|
||||
font-family: var(--font-interface) !important;
|
||||
transition: border-color 0.2s ease !important;
|
||||
box-sizing: border-box !important;
|
||||
.modal-container .modal input.ge-input-field {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--input-radius);
|
||||
background-color: var(--background-primary);
|
||||
color: var(--text-normal);
|
||||
font-size: 14px;
|
||||
font-family: var(--font-interface);
|
||||
transition: border-color 0.2s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.ge-input-field:focus {
|
||||
outline: none !important;
|
||||
.modal-container .modal input.ge-input-field:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.ge-input-field::placeholder {
|
||||
color: var(--text-muted) !important;
|
||||
opacity: 1 !important;
|
||||
.modal-container .modal input.ge-input-field::placeholder {
|
||||
color: var(--text-muted);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* 確保不同 input type 有一致的外觀 */
|
||||
.ge-input-field[type="text"],
|
||||
.ge-input-field[type="url"],
|
||||
.ge-input-field[type="search"],
|
||||
.ge-input-field[type="email"] {
|
||||
-webkit-appearance: none !important;
|
||||
-moz-appearance: none !important;
|
||||
appearance: none !important;
|
||||
.modal-container .modal input.ge-input-field[type="text"],
|
||||
.modal-container .modal input.ge-input-field[type="url"],
|
||||
.modal-container .modal input.ge-input-field[type="search"],
|
||||
.modal-container .modal input.ge-input-field[type="email"] {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
/* 輸入框容器樣式 */
|
||||
|
|
@ -2725,7 +2723,6 @@ a.ge-current-folder:hover {
|
|||
/* 只有有子目錄的資料夾名稱才顯示懸停效果 */
|
||||
.ge-explorer-folder-header:not(.ge-no-children) .ge-explorer-folder-name:hover {
|
||||
background-color: var(--background-modifier-hover);
|
||||
/* color: var(--text-accent); */
|
||||
}
|
||||
|
||||
/* 有子目錄的資料夾整個 header 懸停時不顯示效果 */
|
||||
|
|
@ -3076,9 +3073,9 @@ a.ge-current-folder:hover {
|
|||
/* =========================================================================== */
|
||||
|
||||
/* 移除 view-content 的內距,使 zip viewer 滿版 */
|
||||
.workspace-leaf .zip-viewer-container {
|
||||
padding: 0 !important;
|
||||
overflow: hidden !important;
|
||||
.workspace-leaf .view-content.zip-viewer-container {
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.zip-viewer-container {
|
||||
|
|
@ -3266,11 +3263,11 @@ a.ge-current-folder:hover {
|
|||
|
||||
/* 確保網格滾動最底部時不被底部 Home Indicator 遮擋 */
|
||||
.is-phone .zip-viewer-grid-container {
|
||||
padding-bottom: calc(var(--view-bottom-spacing) + var(--size-4-3)) !important;
|
||||
padding-bottom: calc(var(--view-bottom-spacing) + var(--size-4-3));
|
||||
}
|
||||
|
||||
.is-tablet .zip-viewer-grid-container {
|
||||
padding-bottom: calc(var(--safe-area-inset-bottom) + var(--size-4-3)) !important;
|
||||
padding-bottom: calc(var(--safe-area-inset-bottom) + var(--size-4-3));
|
||||
}
|
||||
|
||||
/* =========================================================================== */
|
||||
|
|
@ -3366,11 +3363,11 @@ a.ge-current-folder:hover {
|
|||
}
|
||||
|
||||
.is-phone .ge-zip-content-container {
|
||||
padding-bottom: var(--view-bottom-spacing) !important;
|
||||
padding-bottom: var(--view-bottom-spacing);
|
||||
}
|
||||
|
||||
.is-tablet .ge-zip-content-container {
|
||||
padding-bottom: var(--safe-area-inset-bottom) !important;
|
||||
padding-bottom: var(--safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.ge-zip-sidebar-content-container {
|
||||
|
|
@ -3386,8 +3383,8 @@ a.ge-current-folder:hover {
|
|||
}
|
||||
|
||||
.is-mobile .ge-zip-view-container::-webkit-scrollbar {
|
||||
display: none !important;
|
||||
width: 0 !important;
|
||||
display: none;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
/* =========================================================================== */
|
||||
|
|
@ -3401,6 +3398,19 @@ a.ge-current-folder:hover {
|
|||
gap: 10px;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
margin-top: 3px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.ge-folders-grid-container {
|
||||
grid-column: 1 / -1;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(var(--grid-item-width, 300px), 1fr));
|
||||
gap: 10px;
|
||||
background: transparent;
|
||||
width: 100%;
|
||||
margin-top: 3px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.ge-folder-button {
|
||||
|
|
|
|||
Loading…
Reference in a new issue