dsh-obsidian-notes 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -45
- package/README.zh.md +16 -28
- package/lib/client.js +114 -61
- package/package.json +1 -1
- package/src/client.js +114 -61
package/README.md
CHANGED
|
@@ -14,28 +14,28 @@ English | [中文](https://github.com/revive123456/dsh-obsidian-notes/blob/main/
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
17
|
+
## What it does
|
|
18
|
+
|
|
19
|
+
A local-first Markdown notes panel that sits in the DSH sidebar and hands notes
|
|
20
|
+
to the chat without copy-paste:
|
|
21
|
+
|
|
22
|
+
1. **Your notes stay beside the conversation.** Point it at a folder of Markdown
|
|
23
|
+
files and you get the vault tree, a line-numbered Obsidian-flavoured preview,
|
|
24
|
+
edit/save, and search across titles and bodies — no import, no sync, no cloud.
|
|
25
|
+
2. **The handoff is the feature.** Cite a whole note as an `@path` mention, or
|
|
26
|
+
select a passage and press **Ask** — the prompt in the composer carries the
|
|
27
|
+
absolute path, the line range and the quoted text, so the model can refer to
|
|
28
|
+
exactly what you read.
|
|
29
|
+
3. **Screenshots become part of the vault.** Paste an image while editing and it
|
|
30
|
+
is saved into the attachment folder as an Obsidian-native
|
|
31
|
+
`![[Pasted image …]]` embed; moving that folder later rewrites the embeds.
|
|
32
|
+
4. **Two ways to see the shape of a vault.** Turn a note into a **mind map**
|
|
33
|
+
built from its headings and list items, or open the **relation graph**
|
|
34
|
+
computed from `[[links]]` and `#tags` — vault-wide, or locally around the open
|
|
35
|
+
note with 1–5 hops.
|
|
36
|
+
|
|
37
|
+
Everything runs on your machine: the plugin reads and writes only the folder you
|
|
38
|
+
choose, and the client talks to the host over its own loopback routes.
|
|
39
39
|
|
|
40
40
|
## Screenshots
|
|
41
41
|
|
|
@@ -79,7 +79,7 @@ host-side route.
|
|
|
79
79
|
2. Search for **`dsh-obsidian-notes`** and install it.
|
|
80
80
|
3. Restart DeepSeek Harness.
|
|
81
81
|
|
|
82
|
-
> This path requires the package to be published on npm
|
|
82
|
+
> This path requires the package to be published on npm.
|
|
83
83
|
> If search shows nothing, use one of the two paths below.
|
|
84
84
|
|
|
85
85
|
### 2. CLI (after the npm release)
|
|
@@ -125,6 +125,18 @@ configured, choose one:
|
|
|
125
125
|
The default vault is `$DSH_WORKSPACE/obsidian_vault`, falling back to
|
|
126
126
|
`./obsidian_vault` under the directory you started `dsh web` from.
|
|
127
127
|
|
|
128
|
+
### Typical workflow
|
|
129
|
+
|
|
130
|
+
1. Open **📚 Notes** and pick a vault (above).
|
|
131
|
+
2. Click a note to read the line-numbered preview; **Edit** to change it, then
|
|
132
|
+
**Save** or <kbd>Ctrl/Cmd+S</kbd>.
|
|
133
|
+
3. To bring a note into the chat, press **⤴ Cite to chat** for the whole note, or
|
|
134
|
+
select text and press **Ask** to attach a question to that passage.
|
|
135
|
+
4. Paste a screenshot while editing to store it in the vault.
|
|
136
|
+
5. Switch the dock to **🧠 Mind map** to explore the note's structure, or open
|
|
137
|
+
**🕸 Graph view** above the tree to see the vault as a graph (**Local** = around
|
|
138
|
+
the open note, **Depth 1–5**).
|
|
139
|
+
|
|
128
140
|
## Features
|
|
129
141
|
|
|
130
142
|
- **Vault sidebar** — lazy directory tree; open a note with one click, create,
|
|
@@ -182,28 +194,6 @@ The default vault is `$DSH_WORKSPACE/obsidian_vault`, falling back to
|
|
|
182
194
|
The UI is bilingual (English / Chinese) and uses DSH theme tokens, so it follows
|
|
183
195
|
the app's light and dark themes.
|
|
184
196
|
|
|
185
|
-
## Comparison
|
|
186
|
-
|
|
187
|
-
| | This plugin | [DedsecLemon/dsh-obsidian](https://github.com/DedsecLemon/dsh-obsidian) |
|
|
188
|
-
| --- | --- | --- |
|
|
189
|
-
| Vault tree, search | ✅ tree, title + body search | ✅ tree, whole-vault matching-line search |
|
|
190
|
-
| Obsidian callouts | ✅ 13 types + 15 aliases, line-numbered spine | ✅ rendered, fold state kept |
|
|
191
|
-
| Tables, fenced code, setext headings | ✅ | ✅ |
|
|
192
|
-
| Frontmatter | ✅ preserved on save | ✅ shown as properties |
|
|
193
|
-
| Paste screenshots + attachment folder moves | ✅ | — |
|
|
194
|
-
| Cite a note / ask about a selection | ✅ | — |
|
|
195
|
-
| Session & workspace housekeeping | ✅ | — |
|
|
196
|
-
| Backlinks | ❌ | ✅ |
|
|
197
|
-
| Outline pane | ❌ | ✅ |
|
|
198
|
-
| Mind map view (headings + lists) | ✅ | — |
|
|
199
|
-
| Graph view (vault-wide links + tags) | ✅ | — |
|
|
200
|
-
| `==highlight==` | ✅ | ✅ |
|
|
201
|
-
|
|
202
|
-
The point is not "more features" — it is a different trade-off. If you need
|
|
203
|
-
outline and backlinks, use the other plugin (or Obsidian itself); if you want a
|
|
204
|
-
faithful Obsidian reading/editing experience with a clean hand-off into the chat,
|
|
205
|
-
this one is built for that.
|
|
206
|
-
|
|
207
197
|
## Configuration
|
|
208
198
|
|
|
209
199
|
Everything lives inside the notes pane; there is no YAML to edit.
|
package/README.zh.md
CHANGED
|
@@ -14,17 +14,16 @@
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## 它能做什么
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
([DedsecLemon/dsh-obsidian](https://github.com/DedsecLemon/dsh-obsidian)),
|
|
21
|
-
它适合需要大纲和反链的人。本插件押的是另外三件事 —— 如果你习惯「一边写笔记、一边和模型讨论」,这三件更重要:
|
|
19
|
+
一个本地优先的 Markdown 笔记面板:在 DSH 侧边栏里读笔记,并把笔记顺手递进对话,不用复制粘贴。
|
|
22
20
|
|
|
23
|
-
1.
|
|
24
|
-
2.
|
|
25
|
-
3.
|
|
21
|
+
1. **笔记就在对话旁边。** 指向一个 Markdown 文件夹,就有目录树、带行号的 Obsidian 风格预览、编辑保存,以及标题和正文搜索——不导入、不同步、不上云。
|
|
22
|
+
2. **「递进对话」本身就是功能。** 整篇引用为 `@绝对路径`;选中一段点 **Ask**,输入框里的 prompt 带着绝对路径、行号区间和原文,模型能准确引用你正在看的这一段。
|
|
23
|
+
3. **截图直接进笔记库。** 编辑时粘贴图片会存进附件目录,并写成 Obsidian 原生的 `![[Pasted image …]]`;以后迁移附件目录,全库引用会一起改写。
|
|
24
|
+
4. **两种看结构的方式。** 把笔记切换成由标题和列表生成的**思维导图**;或打开由 `[[双链]]` 和 `#标签` 现算的**关系图谱**——全库视角,或聚焦当前笔记的 1–5 跳局部视角。
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
全部在本机运行:插件只读写你选择的文件夹,客户端只走宿主自己的 loopback 路由。
|
|
28
27
|
|
|
29
28
|
## 截图
|
|
30
29
|
|
|
@@ -58,7 +57,7 @@ DSH 生态里已经有一个 Obsidian 方向的插件
|
|
|
58
57
|
2. 搜索 **`dsh-obsidian-notes`** 并安装;
|
|
59
58
|
3. 重启 DeepSeek Harness。
|
|
60
59
|
|
|
61
|
-
> 这条路径要求 npm
|
|
60
|
+
> 这条路径要求 npm 上已有该包。搜不到时用下面两种方式。
|
|
62
61
|
|
|
63
62
|
### 2. CLI(npm 发布后)
|
|
64
63
|
|
|
@@ -99,6 +98,14 @@ dsh plugin --profile web add "$(pwd)" # PowerShell 用 "$PWD"
|
|
|
99
98
|
默认 Vault 为 `$DSH_WORKSPACE/obsidian_vault`,缺省回落到启动 `dsh web` 目录下的
|
|
100
99
|
`./obsidian_vault`。
|
|
101
100
|
|
|
101
|
+
### 典型用法
|
|
102
|
+
|
|
103
|
+
1. 打开 **📚 笔记**,先选一个 Vault(见上)。
|
|
104
|
+
2. 点击笔记读带行号的预览;点 **编辑** 修改,再点 **保存** 或 <kbd>Ctrl/Cmd+S</kbd>。
|
|
105
|
+
3. 把笔记带进对话:整篇点 **⤴ 引用到对话**;选中一段点 **Ask**,把问题附着在那段文字上。
|
|
106
|
+
4. 编辑时粘贴截图,会存进 Vault 的附件目录。
|
|
107
|
+
5. 把 Dock 切成 **🧠 思维导图** 看笔记结构;或点笔记树上方的 **🕸 关系图谱** 看全库图(**局部** = 当前笔记周围,**跳数 1–5**)。
|
|
108
|
+
|
|
102
109
|
## 功能
|
|
103
110
|
|
|
104
111
|
- **Vault 侧边栏**:按需加载的目录树;单击打开笔记,新建 / 重命名 / 删除笔记与文件夹,按标题**和正文**搜索,显示笔记数徽标。
|
|
@@ -115,25 +122,6 @@ dsh plugin --profile web add "$(pwd)" # PowerShell 用 "$PWD"
|
|
|
115
122
|
|
|
116
123
|
界面中英双语,使用 DSH 主题变量,跟随应用的浅色 / 深色主题。
|
|
117
124
|
|
|
118
|
-
## 对比
|
|
119
|
-
|
|
120
|
-
| | 本插件 | [DedsecLemon/dsh-obsidian](https://github.com/DedsecLemon/dsh-obsidian) |
|
|
121
|
-
| --- | --- | --- |
|
|
122
|
-
| Vault 目录树、搜索 | ✅ 目录树 + 标题/正文搜索 | ✅ 目录树 + 全库命中行搜索 |
|
|
123
|
-
| Obsidian callout | ✅ 13 种类型 + 15 别名,逐行渲染 | ✅ 渲染,保留折叠状态 |
|
|
124
|
-
| 表格、围栏代码、setext 标题 | ✅ | ✅ |
|
|
125
|
-
| frontmatter | ✅ 保存时原样拼回 | ✅ 以属性区展示 |
|
|
126
|
-
| 粘贴截图 + 附件目录迁移 | ✅ | — |
|
|
127
|
-
| 整篇引用 / 选中片段提问 | ✅ | — |
|
|
128
|
-
| 会话与工作区整理 | ✅ | — |
|
|
129
|
-
| 反链 | ❌ | ✅ |
|
|
130
|
-
| 大纲面板 | ❌ | ✅ |
|
|
131
|
-
| 思维导图视图(标题 + 列表) | ✅ | — |
|
|
132
|
-
| 关系图谱(全库双链 + 标签) | ✅ | — |
|
|
133
|
-
| `==高亮==` | ✅ | ✅ |
|
|
134
|
-
|
|
135
|
-
比的不是「功能多少」,而是取舍:需要大纲和反链,用另一个插件(或真 Obsidian);想要忠实的 Obsidian 阅读/编辑体验、并顺手把笔记递进对话,这个插件就是为此写的。
|
|
136
|
-
|
|
137
125
|
## 配置
|
|
138
126
|
|
|
139
127
|
所有配置都在笔记面板里完成,不用手改 YAML。
|
package/lib/client.js
CHANGED
|
@@ -1920,11 +1920,10 @@ window.__ModuleLoader__.load({
|
|
|
1920
1920
|
wikilinkNotFound: "未找到笔记",
|
|
1921
1921
|
error: "出错了",
|
|
1922
1922
|
imageInserted: "已插入图片",
|
|
1923
|
-
attachDir: "
|
|
1923
|
+
attachDir: "附件文件夹",
|
|
1924
1924
|
attachDirPick: "选择附件目录(Vault 内)",
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
attachDirPlaceholder: "Vault 相对路径,如 docs/images",
|
|
1925
|
+
attachDirPlaceholder: "Vault 相对路径,如 attachments",
|
|
1926
|
+
attachDirHint: "留空 = Vault 根目录,回车保存",
|
|
1928
1927
|
imageRemoved: "已同步删除未引用的图片",
|
|
1929
1928
|
attachDirConfirm: "更改截图落盘目录:{from} → {to}。会**自动把原目录下的截图移动到新目录**、删除原目录(若空),并同步更新笔记里的引用路径。确认更改?",
|
|
1930
1929
|
attachSettings: "附件设置(位置 / 显示 / 清理)",
|
|
@@ -1933,7 +1932,9 @@ window.__ModuleLoader__.load({
|
|
|
1933
1932
|
attachMode_fixed: "固定目录",
|
|
1934
1933
|
attachMode_note: "与笔记同目录",
|
|
1935
1934
|
attachMode_sub: "笔记目录下子目录",
|
|
1936
|
-
attachSubfolder: "
|
|
1935
|
+
attachSubfolder: "子目录名称",
|
|
1936
|
+
attachSubfolderPlaceholder: "如 attachments",
|
|
1937
|
+
attachSubHint: "回车保存",
|
|
1937
1938
|
attachHideDir: "隐藏图片目录(只含图片、没有笔记的目录)",
|
|
1938
1939
|
attachDeleteOrphans: "保存笔记时自动删除无用附件(默认关)",
|
|
1939
1940
|
migrated: "迁移 {n} 张截图,更新 {m} 篇笔记",
|
|
@@ -2095,11 +2096,10 @@ window.__ModuleLoader__.load({
|
|
|
2095
2096
|
wikilinkNotFound: "Note not found",
|
|
2096
2097
|
error: "Error",
|
|
2097
2098
|
imageInserted: "Image inserted",
|
|
2098
|
-
attachDir: "Attachment
|
|
2099
|
+
attachDir: "Attachment folder",
|
|
2099
2100
|
attachDirPick: "Choose attachment dir (inside vault)",
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
attachDirPlaceholder: "Vault-relative path, e.g. docs/images",
|
|
2101
|
+
attachDirPlaceholder: "Vault-relative path, e.g. attachments",
|
|
2102
|
+
attachDirHint: "Empty = vault root; press Enter to save",
|
|
2103
2103
|
imageRemoved: "Deleted unreferenced image(s)",
|
|
2104
2104
|
attachDirConfirm: "Change screenshot dir: {from} → {to}. This will automatically move existing screenshots to the new dir, remove the old dir (if empty), and update note references. Continue?",
|
|
2105
2105
|
attachSettings: "Attachment settings (location / visibility / cleanup)",
|
|
@@ -2108,7 +2108,9 @@ window.__ModuleLoader__.load({
|
|
|
2108
2108
|
attachMode_fixed: "Fixed folder",
|
|
2109
2109
|
attachMode_note: "Same folder as note",
|
|
2110
2110
|
attachMode_sub: "Subfolder under note",
|
|
2111
|
-
attachSubfolder: "Subfolder name
|
|
2111
|
+
attachSubfolder: "Subfolder name",
|
|
2112
|
+
attachSubfolderPlaceholder: "e.g. attachments",
|
|
2113
|
+
attachSubHint: "Press Enter to save",
|
|
2112
2114
|
attachHideDir: "Hide image folders (folders without any notes)",
|
|
2113
2115
|
attachDeleteOrphans: "Auto-delete orphaned attachments on save (off by default)",
|
|
2114
2116
|
migrated: "moved {n} image(s), updated {m} note(s)",
|
|
@@ -2484,6 +2486,7 @@ window.__ModuleLoader__.load({
|
|
|
2484
2486
|
`.dsh-obs-ws-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;font-weight:500}`,
|
|
2485
2487
|
`.dsh-obs-mini{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:14px;line-height:1;flex:none}`,
|
|
2486
2488
|
`.dsh-obs-mini:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}`,
|
|
2489
|
+
`.dsh-obs-mini.dsh-obs-on{background:var(--dsw-alias-interactive-bg-hover-accent);color:var(--dsw-alias-brand-primary)}`,
|
|
2487
2490
|
// 行尾管理动作(工作区/会话 的 重命名 / 删除,常显)
|
|
2488
2491
|
`.dsh-obs-actions{display:flex;align-items:center;gap:2px;flex:none}`,
|
|
2489
2492
|
`.dsh-obs-actions button{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:12px;line-height:1}`,
|
|
@@ -2532,9 +2535,21 @@ window.__ModuleLoader__.load({
|
|
|
2532
2535
|
`.dsh-obs-tree-head button{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:26px;padding:0 6px;border:0;border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;font-size:14px;line-height:1}`,
|
|
2533
2536
|
`.dsh-obs-tree-head button:hover{background:var(--dsw-alias-interactive-bg-hover)}`,
|
|
2534
2537
|
`.dsh-obs-tree-head button.dsh-obs-on{background:var(--dsw-alias-interactive-bg-hover-accent);color:var(--dsw-alias-brand-primary)}`,
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
`.dsh-obs-
|
|
2538
|
+
// 附件设置面板:窄侧边栏里的表单卡片。位置模式用下拉框,四个中文标签都能整行放下;
|
|
2539
|
+
// 旧版把四个标签塞进 24×24 的图标按钮,汉字被挤成一列,盖住下面的输入框和开关。
|
|
2540
|
+
`.dsh-obs-attachcfg{display:flex;flex-direction:column;gap:8px;margin:2px 4px 8px;padding:8px 8px 10px;border:1px solid var(--dsw-alias-border-l1);border-radius:9px;background:var(--dsw-alias-interactive-bg-hover)}`,
|
|
2541
|
+
`.dsh-obs-field{display:flex;flex-direction:column;gap:4px;min-width:0}`,
|
|
2542
|
+
`.dsh-obs-field-label{font-size:11px;line-height:15px;color:var(--dsw-alias-label-tertiary)}`,
|
|
2543
|
+
`.dsh-obs-select{width:100%;height:28px;padding:0 28px 0 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background-color:transparent;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 8px center;background-size:12px 12px;color:var(--dsw-alias-label-primary);font-size:12px;line-height:26px;appearance:none;outline:none;cursor:pointer}`,
|
|
2544
|
+
`.dsh-obs-select:focus{border-color:var(--dsw-alias-brand-primary)}`,
|
|
2545
|
+
`.dsh-obs-inputrow{display:flex;align-items:center;gap:4px;min-width:0}`,
|
|
2546
|
+
`.dsh-obs-inputrow input{flex:1;min-width:0;height:28px;padding:0 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background:transparent;color:var(--dsw-alias-label-primary);font-size:12px;outline:none}`,
|
|
2547
|
+
`.dsh-obs-inputrow input:focus{border-color:var(--dsw-alias-brand-primary)}`,
|
|
2548
|
+
`.dsh-obs-inputrow .dsh-obs-mini{width:26px;height:26px;border:1px solid var(--dsw-alias-border-l2)}`,
|
|
2549
|
+
`.dsh-obs-hint{font-size:11px;line-height:15px;color:var(--dsw-alias-label-tertiary)}`,
|
|
2550
|
+
`.dsh-obs-checks{display:flex;flex-direction:column;gap:6px;padding-top:2px}`,
|
|
2551
|
+
`.dsh-obs-cfgline{display:flex;align-items:flex-start;gap:6px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary);cursor:pointer;user-select:none}`,
|
|
2552
|
+
`.dsh-obs-cfgline input[type=checkbox]{flex:none;margin:2px 0 0;cursor:pointer}`,
|
|
2538
2553
|
`.dsh-obs-root-row{display:flex;align-items:center;gap:6px;padding:4px 10px;font-size:11px;color:var(--dsw-alias-label-tertiary)}`,
|
|
2539
2554
|
`.dsh-obs-root-row .dsh-obs-root-path{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}`,
|
|
2540
2555
|
`.dsh-obs-root-row .dsh-obs-mini,.dsh-obs-row .dsh-obs-mini{flex:none;width:22px;height:22px}`,
|
|
@@ -2798,7 +2813,14 @@ window.__ModuleLoader__.load({
|
|
|
2798
2813
|
|
|
2799
2814
|
function injectCss() {
|
|
2800
2815
|
if (typeof document === "undefined") return;
|
|
2801
|
-
|
|
2816
|
+
// 已注入过就只更新内容:插件被 HMR 重新装载时 apply() 会再跑一遍,旧实现直接
|
|
2817
|
+
// return,页面会继续用上一次的 CSS —— 新加的类名没有样式,界面半新半旧,只能
|
|
2818
|
+
// 整页刷新才正常。
|
|
2819
|
+
const existing = document.querySelector('style[data-plugin-css="dsh-obsidian-vault/core.css"]');
|
|
2820
|
+
if (existing !== null) {
|
|
2821
|
+
if (existing.textContent !== CSS) existing.textContent = CSS;
|
|
2822
|
+
return;
|
|
2823
|
+
}
|
|
2802
2824
|
const tag = document.createElement("style");
|
|
2803
2825
|
tag.dataset.plugin = "dsh-obsidian-vault";
|
|
2804
2826
|
tag.dataset.pluginCss = "dsh-obsidian-vault/core.css";
|
|
@@ -3052,6 +3074,7 @@ window.__ModuleLoader__.load({
|
|
|
3052
3074
|
const [attachDir, setAttachDirState] = useState(null);
|
|
3053
3075
|
const [attachOpen, setAttachOpen] = useState(false);
|
|
3054
3076
|
const [attachInput, setAttachInput] = useState("");
|
|
3077
|
+
const [attachSubInput, setAttachSubInput] = useState("");
|
|
3055
3078
|
// 附件位置模式 / 子目录 / 两个开关(对齐 Obsidian 的附件位置设置)
|
|
3056
3079
|
const [attachCfg, setAttachCfg] = useState(null);
|
|
3057
3080
|
|
|
@@ -3101,6 +3124,14 @@ window.__ModuleLoader__.load({
|
|
|
3101
3124
|
void loadRoot();
|
|
3102
3125
|
}, [loadRoot, vaultRev]);
|
|
3103
3126
|
|
|
3127
|
+
// 面板每次打开都把输入框填成当前设置:旧版只有铅笔按钮会带值,点齿轮打开时
|
|
3128
|
+
// 输入框是空的,看起来像「设置丢了」。
|
|
3129
|
+
useEffect(() => {
|
|
3130
|
+
if (!attachOpen) return;
|
|
3131
|
+
setAttachInput(typeof attachDir === "string" ? attachDir : "");
|
|
3132
|
+
setAttachSubInput(attachCfg !== null ? attachCfg.subfolder : "attachments");
|
|
3133
|
+
}, [attachOpen]);
|
|
3134
|
+
|
|
3104
3135
|
const toggleDir = useCallback(async (rel) => {
|
|
3105
3136
|
const willOpen = !expanded.has(rel);
|
|
3106
3137
|
setExpanded((prev) => {
|
|
@@ -3402,63 +3433,85 @@ window.__ModuleLoader__.load({
|
|
|
3402
3433
|
onClick: (e) => { e.stopPropagation(); patchShared({ graphOpen: true }); },
|
|
3403
3434
|
}, "↗"),
|
|
3404
3435
|
),
|
|
3405
|
-
rootInfo && rootInfo.exists && attachDir !== null && createElement("div", {
|
|
3436
|
+
rootInfo && rootInfo.exists && attachDir !== null && createElement("div", {
|
|
3437
|
+
className: "dsh-obs-root-row",
|
|
3438
|
+
title: `${t("attachLocation")}: ${attachLocationLabel(attachCfg, attachDir, t)}`,
|
|
3439
|
+
},
|
|
3406
3440
|
createElement("span", { className: "dsh-obs-root-path" },
|
|
3407
3441
|
`📎 ${t("attachLocation")}: ${attachLocationLabel(attachCfg, attachDir, t)}`),
|
|
3408
|
-
attachCfg !== null && attachCfg.mode === "fixed" && createElement("button", {
|
|
3409
|
-
className: "dsh-obs-mini",
|
|
3410
|
-
title: t("attachDirPick"),
|
|
3411
|
-
onClick: (e) => { e.stopPropagation(); void pickAttachDir(); },
|
|
3412
|
-
}, "📂"),
|
|
3413
|
-
attachCfg !== null && attachCfg.mode === "fixed" && createElement("button", {
|
|
3414
|
-
className: "dsh-obs-mini",
|
|
3415
|
-
title: t("attachDirEdit"),
|
|
3416
|
-
onClick: (e) => { e.stopPropagation(); setAttachInput(attachDir); setAttachOpen((v) => !v); },
|
|
3417
|
-
}, "✎"),
|
|
3418
3442
|
createElement("button", {
|
|
3419
|
-
className:
|
|
3443
|
+
className: `dsh-obs-mini${attachOpen ? " dsh-obs-on" : ""}`,
|
|
3420
3444
|
title: t("attachSettings"),
|
|
3445
|
+
"aria-expanded": attachOpen === true,
|
|
3421
3446
|
onClick: (e) => { e.stopPropagation(); setAttachOpen((v) => !v); },
|
|
3422
3447
|
}, "⚙"),
|
|
3423
3448
|
),
|
|
3424
|
-
//
|
|
3449
|
+
// 附件设置面板:窄侧边栏里当表单排(下拉框 + 输入行 + 开关),长标签整行放得下
|
|
3425
3450
|
attachOpen && rootInfo && rootInfo.exists && attachCfg !== null && createElement("div", { className: "dsh-obs-attachcfg" },
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
attachCfg.mode === "fixed" && createElement("input", {
|
|
3433
|
-
autoFocus: true,
|
|
3434
|
-
placeholder: t("attachDirPlaceholder"),
|
|
3435
|
-
value: attachInput,
|
|
3436
|
-
onChange: (e) => setAttachInput(e.target.value),
|
|
3437
|
-
onKeyDown: (e) => {
|
|
3438
|
-
if (e.key === "Enter") void applyAttachDir(attachInput);
|
|
3439
|
-
if (e.key === "Escape") setAttachOpen(false);
|
|
3451
|
+
createElement("label", { className: "dsh-obs-field" },
|
|
3452
|
+
createElement("span", { className: "dsh-obs-field-label" }, t("attachLocation")),
|
|
3453
|
+
createElement("select", {
|
|
3454
|
+
className: "dsh-obs-select",
|
|
3455
|
+
value: attachCfg.mode,
|
|
3456
|
+
onChange: (e) => { void applyAttachSettings({ mode: e.target.value }); },
|
|
3440
3457
|
},
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
placeholder: t("attachSubfolder"),
|
|
3444
|
-
defaultValue: attachCfg.subfolder,
|
|
3445
|
-
onKeyDown: (e) => { if (e.key === "Enter") void applyAttachSettings({ subfolder: e.target.value }); },
|
|
3446
|
-
}),
|
|
3447
|
-
createElement("label", { className: "dsh-obs-cfgline" },
|
|
3448
|
-
createElement("input", {
|
|
3449
|
-
type: "checkbox",
|
|
3450
|
-
checked: attachCfg.hideDir,
|
|
3451
|
-
onChange: (e) => { e.stopPropagation(); void applyAttachSettings({ hideAttachmentDir: e.target.checked === true }); },
|
|
3452
|
-
}),
|
|
3453
|
-
t("attachHideDir"),
|
|
3458
|
+
["root", "fixed", "note", "sub"].map((m) => createElement("option", { key: m, value: m }, t(`attachMode_${m}`))),
|
|
3459
|
+
),
|
|
3454
3460
|
),
|
|
3455
|
-
createElement("
|
|
3456
|
-
createElement("
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3461
|
+
attachCfg.mode === "fixed" && createElement("div", { className: "dsh-obs-field" },
|
|
3462
|
+
createElement("span", { className: "dsh-obs-field-label" }, t("attachDir")),
|
|
3463
|
+
createElement("div", { className: "dsh-obs-inputrow" },
|
|
3464
|
+
createElement("input", {
|
|
3465
|
+
"aria-label": t("attachDir"),
|
|
3466
|
+
placeholder: t("attachDirPlaceholder"),
|
|
3467
|
+
value: attachInput,
|
|
3468
|
+
onChange: (e) => setAttachInput(e.target.value),
|
|
3469
|
+
onKeyDown: (e) => {
|
|
3470
|
+
if (e.key === "Enter") void applyAttachDir(attachInput);
|
|
3471
|
+
if (e.key === "Escape") setAttachOpen(false);
|
|
3472
|
+
},
|
|
3473
|
+
}),
|
|
3474
|
+
createElement("button", {
|
|
3475
|
+
className: "dsh-obs-mini",
|
|
3476
|
+
title: t("attachDirPick"),
|
|
3477
|
+
onClick: (e) => { e.stopPropagation(); void pickAttachDir(); },
|
|
3478
|
+
}, "📂"),
|
|
3479
|
+
),
|
|
3480
|
+
createElement("span", { className: "dsh-obs-hint" }, t("attachDirHint")),
|
|
3481
|
+
),
|
|
3482
|
+
attachCfg.mode === "sub" && createElement("div", { className: "dsh-obs-field" },
|
|
3483
|
+
createElement("span", { className: "dsh-obs-field-label" }, t("attachSubfolder")),
|
|
3484
|
+
createElement("div", { className: "dsh-obs-inputrow" },
|
|
3485
|
+
createElement("input", {
|
|
3486
|
+
"aria-label": t("attachSubfolder"),
|
|
3487
|
+
placeholder: t("attachSubfolderPlaceholder"),
|
|
3488
|
+
value: attachSubInput,
|
|
3489
|
+
onChange: (e) => setAttachSubInput(e.target.value),
|
|
3490
|
+
onKeyDown: (e) => {
|
|
3491
|
+
if (e.key === "Enter") void applyAttachSettings({ subfolder: attachSubInput });
|
|
3492
|
+
if (e.key === "Escape") setAttachOpen(false);
|
|
3493
|
+
},
|
|
3494
|
+
}),
|
|
3495
|
+
),
|
|
3496
|
+
createElement("span", { className: "dsh-obs-hint" }, t("attachSubHint")),
|
|
3497
|
+
),
|
|
3498
|
+
createElement("div", { className: "dsh-obs-checks" },
|
|
3499
|
+
createElement("label", { className: "dsh-obs-cfgline" },
|
|
3500
|
+
createElement("input", {
|
|
3501
|
+
type: "checkbox",
|
|
3502
|
+
checked: attachCfg.hideDir,
|
|
3503
|
+
onChange: (e) => { e.stopPropagation(); void applyAttachSettings({ hideAttachmentDir: e.target.checked === true }); },
|
|
3504
|
+
}),
|
|
3505
|
+
createElement("span", null, t("attachHideDir")),
|
|
3506
|
+
),
|
|
3507
|
+
createElement("label", { className: "dsh-obs-cfgline" },
|
|
3508
|
+
createElement("input", {
|
|
3509
|
+
type: "checkbox",
|
|
3510
|
+
checked: attachCfg.deleteOrphans,
|
|
3511
|
+
onChange: (e) => { e.stopPropagation(); void applyAttachSettings({ deleteOrphanAttachments: e.target.checked === true }); },
|
|
3512
|
+
}),
|
|
3513
|
+
createElement("span", null, t("attachDeleteOrphans")),
|
|
3514
|
+
),
|
|
3462
3515
|
),
|
|
3463
3516
|
),
|
|
3464
3517
|
body,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-obsidian-notes",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Obsidian-style notes panel for DeepSeek Harness Web: vault directory tree, Markdown preview/edit/save, cite-to-chat, batch archive/delete of sessions and workspaces. DSH 内嵌 Obsidian 风格笔记面板:目录树 + Markdown 预览/编辑/保存 + 引用到对话 + 会话/工作区批量操作。",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": "^22.19 || >=24"
|
package/src/client.js
CHANGED
|
@@ -178,11 +178,10 @@
|
|
|
178
178
|
wikilinkNotFound: "未找到笔记",
|
|
179
179
|
error: "出错了",
|
|
180
180
|
imageInserted: "已插入图片",
|
|
181
|
-
attachDir: "
|
|
181
|
+
attachDir: "附件文件夹",
|
|
182
182
|
attachDirPick: "选择附件目录(Vault 内)",
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
attachDirPlaceholder: "Vault 相对路径,如 docs/images",
|
|
183
|
+
attachDirPlaceholder: "Vault 相对路径,如 attachments",
|
|
184
|
+
attachDirHint: "留空 = Vault 根目录,回车保存",
|
|
186
185
|
imageRemoved: "已同步删除未引用的图片",
|
|
187
186
|
attachDirConfirm: "更改截图落盘目录:{from} → {to}。会**自动把原目录下的截图移动到新目录**、删除原目录(若空),并同步更新笔记里的引用路径。确认更改?",
|
|
188
187
|
attachSettings: "附件设置(位置 / 显示 / 清理)",
|
|
@@ -191,7 +190,9 @@
|
|
|
191
190
|
attachMode_fixed: "固定目录",
|
|
192
191
|
attachMode_note: "与笔记同目录",
|
|
193
192
|
attachMode_sub: "笔记目录下子目录",
|
|
194
|
-
attachSubfolder: "
|
|
193
|
+
attachSubfolder: "子目录名称",
|
|
194
|
+
attachSubfolderPlaceholder: "如 attachments",
|
|
195
|
+
attachSubHint: "回车保存",
|
|
195
196
|
attachHideDir: "隐藏图片目录(只含图片、没有笔记的目录)",
|
|
196
197
|
attachDeleteOrphans: "保存笔记时自动删除无用附件(默认关)",
|
|
197
198
|
migrated: "迁移 {n} 张截图,更新 {m} 篇笔记",
|
|
@@ -353,11 +354,10 @@
|
|
|
353
354
|
wikilinkNotFound: "Note not found",
|
|
354
355
|
error: "Error",
|
|
355
356
|
imageInserted: "Image inserted",
|
|
356
|
-
attachDir: "Attachment
|
|
357
|
+
attachDir: "Attachment folder",
|
|
357
358
|
attachDirPick: "Choose attachment dir (inside vault)",
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
attachDirPlaceholder: "Vault-relative path, e.g. docs/images",
|
|
359
|
+
attachDirPlaceholder: "Vault-relative path, e.g. attachments",
|
|
360
|
+
attachDirHint: "Empty = vault root; press Enter to save",
|
|
361
361
|
imageRemoved: "Deleted unreferenced image(s)",
|
|
362
362
|
attachDirConfirm: "Change screenshot dir: {from} → {to}. This will automatically move existing screenshots to the new dir, remove the old dir (if empty), and update note references. Continue?",
|
|
363
363
|
attachSettings: "Attachment settings (location / visibility / cleanup)",
|
|
@@ -366,7 +366,9 @@
|
|
|
366
366
|
attachMode_fixed: "Fixed folder",
|
|
367
367
|
attachMode_note: "Same folder as note",
|
|
368
368
|
attachMode_sub: "Subfolder under note",
|
|
369
|
-
attachSubfolder: "Subfolder name
|
|
369
|
+
attachSubfolder: "Subfolder name",
|
|
370
|
+
attachSubfolderPlaceholder: "e.g. attachments",
|
|
371
|
+
attachSubHint: "Press Enter to save",
|
|
370
372
|
attachHideDir: "Hide image folders (folders without any notes)",
|
|
371
373
|
attachDeleteOrphans: "Auto-delete orphaned attachments on save (off by default)",
|
|
372
374
|
migrated: "moved {n} image(s), updated {m} note(s)",
|
|
@@ -742,6 +744,7 @@
|
|
|
742
744
|
`.dsh-obs-ws-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;font-weight:500}`,
|
|
743
745
|
`.dsh-obs-mini{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:14px;line-height:1;flex:none}`,
|
|
744
746
|
`.dsh-obs-mini:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}`,
|
|
747
|
+
`.dsh-obs-mini.dsh-obs-on{background:var(--dsw-alias-interactive-bg-hover-accent);color:var(--dsw-alias-brand-primary)}`,
|
|
745
748
|
// 行尾管理动作(工作区/会话 的 重命名 / 删除,常显)
|
|
746
749
|
`.dsh-obs-actions{display:flex;align-items:center;gap:2px;flex:none}`,
|
|
747
750
|
`.dsh-obs-actions button{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border:0;border-radius:6px;background:transparent;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:12px;line-height:1}`,
|
|
@@ -790,9 +793,21 @@
|
|
|
790
793
|
`.dsh-obs-tree-head button{display:inline-flex;align-items:center;justify-content:center;min-width:26px;height:26px;padding:0 6px;border:0;border-radius:7px;background:transparent;color:var(--dsw-alias-label-secondary);cursor:pointer;font-size:14px;line-height:1}`,
|
|
791
794
|
`.dsh-obs-tree-head button:hover{background:var(--dsw-alias-interactive-bg-hover)}`,
|
|
792
795
|
`.dsh-obs-tree-head button.dsh-obs-on{background:var(--dsw-alias-interactive-bg-hover-accent);color:var(--dsw-alias-brand-primary)}`,
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
`.dsh-obs-
|
|
796
|
+
// 附件设置面板:窄侧边栏里的表单卡片。位置模式用下拉框,四个中文标签都能整行放下;
|
|
797
|
+
// 旧版把四个标签塞进 24×24 的图标按钮,汉字被挤成一列,盖住下面的输入框和开关。
|
|
798
|
+
`.dsh-obs-attachcfg{display:flex;flex-direction:column;gap:8px;margin:2px 4px 8px;padding:8px 8px 10px;border:1px solid var(--dsw-alias-border-l1);border-radius:9px;background:var(--dsw-alias-interactive-bg-hover)}`,
|
|
799
|
+
`.dsh-obs-field{display:flex;flex-direction:column;gap:4px;min-width:0}`,
|
|
800
|
+
`.dsh-obs-field-label{font-size:11px;line-height:15px;color:var(--dsw-alias-label-tertiary)}`,
|
|
801
|
+
`.dsh-obs-select{width:100%;height:28px;padding:0 28px 0 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background-color:transparent;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 8px center;background-size:12px 12px;color:var(--dsw-alias-label-primary);font-size:12px;line-height:26px;appearance:none;outline:none;cursor:pointer}`,
|
|
802
|
+
`.dsh-obs-select:focus{border-color:var(--dsw-alias-brand-primary)}`,
|
|
803
|
+
`.dsh-obs-inputrow{display:flex;align-items:center;gap:4px;min-width:0}`,
|
|
804
|
+
`.dsh-obs-inputrow input{flex:1;min-width:0;height:28px;padding:0 8px;border:1px solid var(--dsw-alias-border-l2);border-radius:7px;background:transparent;color:var(--dsw-alias-label-primary);font-size:12px;outline:none}`,
|
|
805
|
+
`.dsh-obs-inputrow input:focus{border-color:var(--dsw-alias-brand-primary)}`,
|
|
806
|
+
`.dsh-obs-inputrow .dsh-obs-mini{width:26px;height:26px;border:1px solid var(--dsw-alias-border-l2)}`,
|
|
807
|
+
`.dsh-obs-hint{font-size:11px;line-height:15px;color:var(--dsw-alias-label-tertiary)}`,
|
|
808
|
+
`.dsh-obs-checks{display:flex;flex-direction:column;gap:6px;padding-top:2px}`,
|
|
809
|
+
`.dsh-obs-cfgline{display:flex;align-items:flex-start;gap:6px;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary);cursor:pointer;user-select:none}`,
|
|
810
|
+
`.dsh-obs-cfgline input[type=checkbox]{flex:none;margin:2px 0 0;cursor:pointer}`,
|
|
796
811
|
`.dsh-obs-root-row{display:flex;align-items:center;gap:6px;padding:4px 10px;font-size:11px;color:var(--dsw-alias-label-tertiary)}`,
|
|
797
812
|
`.dsh-obs-root-row .dsh-obs-root-path{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}`,
|
|
798
813
|
`.dsh-obs-root-row .dsh-obs-mini,.dsh-obs-row .dsh-obs-mini{flex:none;width:22px;height:22px}`,
|
|
@@ -1056,7 +1071,14 @@
|
|
|
1056
1071
|
|
|
1057
1072
|
function injectCss() {
|
|
1058
1073
|
if (typeof document === "undefined") return;
|
|
1059
|
-
|
|
1074
|
+
// 已注入过就只更新内容:插件被 HMR 重新装载时 apply() 会再跑一遍,旧实现直接
|
|
1075
|
+
// return,页面会继续用上一次的 CSS —— 新加的类名没有样式,界面半新半旧,只能
|
|
1076
|
+
// 整页刷新才正常。
|
|
1077
|
+
const existing = document.querySelector('style[data-plugin-css="dsh-obsidian-vault/core.css"]');
|
|
1078
|
+
if (existing !== null) {
|
|
1079
|
+
if (existing.textContent !== CSS) existing.textContent = CSS;
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1060
1082
|
const tag = document.createElement("style");
|
|
1061
1083
|
tag.dataset.plugin = "dsh-obsidian-vault";
|
|
1062
1084
|
tag.dataset.pluginCss = "dsh-obsidian-vault/core.css";
|
|
@@ -1310,6 +1332,7 @@
|
|
|
1310
1332
|
const [attachDir, setAttachDirState] = useState(null);
|
|
1311
1333
|
const [attachOpen, setAttachOpen] = useState(false);
|
|
1312
1334
|
const [attachInput, setAttachInput] = useState("");
|
|
1335
|
+
const [attachSubInput, setAttachSubInput] = useState("");
|
|
1313
1336
|
// 附件位置模式 / 子目录 / 两个开关(对齐 Obsidian 的附件位置设置)
|
|
1314
1337
|
const [attachCfg, setAttachCfg] = useState(null);
|
|
1315
1338
|
|
|
@@ -1359,6 +1382,14 @@
|
|
|
1359
1382
|
void loadRoot();
|
|
1360
1383
|
}, [loadRoot, vaultRev]);
|
|
1361
1384
|
|
|
1385
|
+
// 面板每次打开都把输入框填成当前设置:旧版只有铅笔按钮会带值,点齿轮打开时
|
|
1386
|
+
// 输入框是空的,看起来像「设置丢了」。
|
|
1387
|
+
useEffect(() => {
|
|
1388
|
+
if (!attachOpen) return;
|
|
1389
|
+
setAttachInput(typeof attachDir === "string" ? attachDir : "");
|
|
1390
|
+
setAttachSubInput(attachCfg !== null ? attachCfg.subfolder : "attachments");
|
|
1391
|
+
}, [attachOpen]);
|
|
1392
|
+
|
|
1362
1393
|
const toggleDir = useCallback(async (rel) => {
|
|
1363
1394
|
const willOpen = !expanded.has(rel);
|
|
1364
1395
|
setExpanded((prev) => {
|
|
@@ -1660,63 +1691,85 @@
|
|
|
1660
1691
|
onClick: (e) => { e.stopPropagation(); patchShared({ graphOpen: true }); },
|
|
1661
1692
|
}, "↗"),
|
|
1662
1693
|
),
|
|
1663
|
-
rootInfo && rootInfo.exists && attachDir !== null && createElement("div", {
|
|
1694
|
+
rootInfo && rootInfo.exists && attachDir !== null && createElement("div", {
|
|
1695
|
+
className: "dsh-obs-root-row",
|
|
1696
|
+
title: `${t("attachLocation")}: ${attachLocationLabel(attachCfg, attachDir, t)}`,
|
|
1697
|
+
},
|
|
1664
1698
|
createElement("span", { className: "dsh-obs-root-path" },
|
|
1665
1699
|
`📎 ${t("attachLocation")}: ${attachLocationLabel(attachCfg, attachDir, t)}`),
|
|
1666
|
-
attachCfg !== null && attachCfg.mode === "fixed" && createElement("button", {
|
|
1667
|
-
className: "dsh-obs-mini",
|
|
1668
|
-
title: t("attachDirPick"),
|
|
1669
|
-
onClick: (e) => { e.stopPropagation(); void pickAttachDir(); },
|
|
1670
|
-
}, "📂"),
|
|
1671
|
-
attachCfg !== null && attachCfg.mode === "fixed" && createElement("button", {
|
|
1672
|
-
className: "dsh-obs-mini",
|
|
1673
|
-
title: t("attachDirEdit"),
|
|
1674
|
-
onClick: (e) => { e.stopPropagation(); setAttachInput(attachDir); setAttachOpen((v) => !v); },
|
|
1675
|
-
}, "✎"),
|
|
1676
1700
|
createElement("button", {
|
|
1677
|
-
className:
|
|
1701
|
+
className: `dsh-obs-mini${attachOpen ? " dsh-obs-on" : ""}`,
|
|
1678
1702
|
title: t("attachSettings"),
|
|
1703
|
+
"aria-expanded": attachOpen === true,
|
|
1679
1704
|
onClick: (e) => { e.stopPropagation(); setAttachOpen((v) => !v); },
|
|
1680
1705
|
}, "⚙"),
|
|
1681
1706
|
),
|
|
1682
|
-
//
|
|
1707
|
+
// 附件设置面板:窄侧边栏里当表单排(下拉框 + 输入行 + 开关),长标签整行放得下
|
|
1683
1708
|
attachOpen && rootInfo && rootInfo.exists && attachCfg !== null && createElement("div", { className: "dsh-obs-attachcfg" },
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
attachCfg.mode === "fixed" && createElement("input", {
|
|
1691
|
-
autoFocus: true,
|
|
1692
|
-
placeholder: t("attachDirPlaceholder"),
|
|
1693
|
-
value: attachInput,
|
|
1694
|
-
onChange: (e) => setAttachInput(e.target.value),
|
|
1695
|
-
onKeyDown: (e) => {
|
|
1696
|
-
if (e.key === "Enter") void applyAttachDir(attachInput);
|
|
1697
|
-
if (e.key === "Escape") setAttachOpen(false);
|
|
1709
|
+
createElement("label", { className: "dsh-obs-field" },
|
|
1710
|
+
createElement("span", { className: "dsh-obs-field-label" }, t("attachLocation")),
|
|
1711
|
+
createElement("select", {
|
|
1712
|
+
className: "dsh-obs-select",
|
|
1713
|
+
value: attachCfg.mode,
|
|
1714
|
+
onChange: (e) => { void applyAttachSettings({ mode: e.target.value }); },
|
|
1698
1715
|
},
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
placeholder: t("attachSubfolder"),
|
|
1702
|
-
defaultValue: attachCfg.subfolder,
|
|
1703
|
-
onKeyDown: (e) => { if (e.key === "Enter") void applyAttachSettings({ subfolder: e.target.value }); },
|
|
1704
|
-
}),
|
|
1705
|
-
createElement("label", { className: "dsh-obs-cfgline" },
|
|
1706
|
-
createElement("input", {
|
|
1707
|
-
type: "checkbox",
|
|
1708
|
-
checked: attachCfg.hideDir,
|
|
1709
|
-
onChange: (e) => { e.stopPropagation(); void applyAttachSettings({ hideAttachmentDir: e.target.checked === true }); },
|
|
1710
|
-
}),
|
|
1711
|
-
t("attachHideDir"),
|
|
1716
|
+
["root", "fixed", "note", "sub"].map((m) => createElement("option", { key: m, value: m }, t(`attachMode_${m}`))),
|
|
1717
|
+
),
|
|
1712
1718
|
),
|
|
1713
|
-
createElement("
|
|
1714
|
-
createElement("
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1719
|
+
attachCfg.mode === "fixed" && createElement("div", { className: "dsh-obs-field" },
|
|
1720
|
+
createElement("span", { className: "dsh-obs-field-label" }, t("attachDir")),
|
|
1721
|
+
createElement("div", { className: "dsh-obs-inputrow" },
|
|
1722
|
+
createElement("input", {
|
|
1723
|
+
"aria-label": t("attachDir"),
|
|
1724
|
+
placeholder: t("attachDirPlaceholder"),
|
|
1725
|
+
value: attachInput,
|
|
1726
|
+
onChange: (e) => setAttachInput(e.target.value),
|
|
1727
|
+
onKeyDown: (e) => {
|
|
1728
|
+
if (e.key === "Enter") void applyAttachDir(attachInput);
|
|
1729
|
+
if (e.key === "Escape") setAttachOpen(false);
|
|
1730
|
+
},
|
|
1731
|
+
}),
|
|
1732
|
+
createElement("button", {
|
|
1733
|
+
className: "dsh-obs-mini",
|
|
1734
|
+
title: t("attachDirPick"),
|
|
1735
|
+
onClick: (e) => { e.stopPropagation(); void pickAttachDir(); },
|
|
1736
|
+
}, "📂"),
|
|
1737
|
+
),
|
|
1738
|
+
createElement("span", { className: "dsh-obs-hint" }, t("attachDirHint")),
|
|
1739
|
+
),
|
|
1740
|
+
attachCfg.mode === "sub" && createElement("div", { className: "dsh-obs-field" },
|
|
1741
|
+
createElement("span", { className: "dsh-obs-field-label" }, t("attachSubfolder")),
|
|
1742
|
+
createElement("div", { className: "dsh-obs-inputrow" },
|
|
1743
|
+
createElement("input", {
|
|
1744
|
+
"aria-label": t("attachSubfolder"),
|
|
1745
|
+
placeholder: t("attachSubfolderPlaceholder"),
|
|
1746
|
+
value: attachSubInput,
|
|
1747
|
+
onChange: (e) => setAttachSubInput(e.target.value),
|
|
1748
|
+
onKeyDown: (e) => {
|
|
1749
|
+
if (e.key === "Enter") void applyAttachSettings({ subfolder: attachSubInput });
|
|
1750
|
+
if (e.key === "Escape") setAttachOpen(false);
|
|
1751
|
+
},
|
|
1752
|
+
}),
|
|
1753
|
+
),
|
|
1754
|
+
createElement("span", { className: "dsh-obs-hint" }, t("attachSubHint")),
|
|
1755
|
+
),
|
|
1756
|
+
createElement("div", { className: "dsh-obs-checks" },
|
|
1757
|
+
createElement("label", { className: "dsh-obs-cfgline" },
|
|
1758
|
+
createElement("input", {
|
|
1759
|
+
type: "checkbox",
|
|
1760
|
+
checked: attachCfg.hideDir,
|
|
1761
|
+
onChange: (e) => { e.stopPropagation(); void applyAttachSettings({ hideAttachmentDir: e.target.checked === true }); },
|
|
1762
|
+
}),
|
|
1763
|
+
createElement("span", null, t("attachHideDir")),
|
|
1764
|
+
),
|
|
1765
|
+
createElement("label", { className: "dsh-obs-cfgline" },
|
|
1766
|
+
createElement("input", {
|
|
1767
|
+
type: "checkbox",
|
|
1768
|
+
checked: attachCfg.deleteOrphans,
|
|
1769
|
+
onChange: (e) => { e.stopPropagation(); void applyAttachSettings({ deleteOrphanAttachments: e.target.checked === true }); },
|
|
1770
|
+
}),
|
|
1771
|
+
createElement("span", null, t("attachDeleteOrphans")),
|
|
1772
|
+
),
|
|
1720
1773
|
),
|
|
1721
1774
|
),
|
|
1722
1775
|
body,
|