dsh-tiddlywiki 0.2.0 → 0.2.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-tiddlywiki",
3
3
  "description": "TiddlyWiki 5 as the DSH persistent knowledge base: tiddlywiki_* agent tools (search/get/put/delete + git sync), a full TiddlyWiki editor embedded in the GUI center column, a floating quick-note widget in the chat area, and git-based sync with auto-commit. Mounts via the official dsh plugin system — no DSH source changes.",
4
- "version": "0.2.0",
4
+ "version": "0.2.1",
5
5
  "type": "module",
6
6
  "keywords": [
7
7
  "dsh",
@@ -79,6 +79,7 @@ interface StatusPayload {
79
79
  wikiPath?: string
80
80
  error?: string
81
81
  note?: { tag?: string }
82
+ ui?: { showPanelStatus?: boolean }
82
83
  }
83
84
 
84
85
  function conversationColumn(): HTMLElement | undefined {
@@ -115,6 +116,8 @@ export function mountPanel(state: PanelState): () => void {
115
116
  let frameArea: HTMLDivElement | undefined
116
117
  let errorArea: HTMLDivElement | undefined
117
118
  let chip: HTMLSpanElement | undefined
119
+ let statusDot: HTMLSpanElement | undefined
120
+ let statusFloater: HTMLDivElement | undefined
118
121
  let refreshTimer: number | undefined
119
122
  let refreshAttempts = 0
120
123
 
@@ -123,27 +126,6 @@ export function mountPanel(state: PanelState): () => void {
123
126
  view.dataset.dshTwView = ''
124
127
  view.className = 'dsh-tw-view'
125
128
 
126
- const bar = document.createElement('div')
127
- bar.className = 'dsh-tw-panel-bar'
128
- const title = document.createElement('span')
129
- title.className = 'dsh-tw-panel-title'
130
- title.textContent = 'TiddlyWiki 知识库'
131
- chip = document.createElement('span')
132
- chip.className = 'dsh-tw-status-chip'
133
- chip.dataset.state = 'unknown'
134
- chip.textContent = '—'
135
- const reload = document.createElement('button')
136
- reload.type = 'button'
137
- reload.textContent = '重载'
138
- reload.addEventListener('click', () => {
139
- if (iframe !== undefined && !iframe.hidden) iframe.src = iframe.src
140
- })
141
- const refresh = document.createElement('button')
142
- refresh.type = 'button'
143
- refresh.textContent = '状态'
144
- refresh.addEventListener('click', () => { void doRefresh() })
145
- bar.append(title, chip, refresh, reload)
146
-
147
129
  frameArea = document.createElement('div')
148
130
  frameArea.className = 'dsh-tw-panel-frame-wrap'
149
131
  frameArea.style.cssText = 'flex:1;min-height:0;display:flex;flex-direction:column'
@@ -157,14 +139,50 @@ export function mountPanel(state: PanelState): () => void {
157
139
  errorArea.className = 'dsh-tw-panel-error'
158
140
  errorArea.hidden = true
159
141
 
160
- view.append(bar, frameArea, errorArea)
142
+ view.append(frameArea, errorArea)
161
143
  return view
162
144
  }
163
145
 
146
+ /**
147
+ * Bottom-right floating status/reload buttons (same visual language as the
148
+ * quick-note toggle) — the top panel bar is gone, so TW's menubar reaches
149
+ * the very top of the panel and clears the host's top-right buttons. Mount
150
+ * is gated by `ui.showPanelStatus` (settings page toggle).
151
+ */
152
+ const buildStatusFloater = (): HTMLDivElement => {
153
+ const wrap = document.createElement('div')
154
+ wrap.className = 'dsh-tw-panel-status'
155
+
156
+ const statusBtn = document.createElement('button')
157
+ statusBtn.type = 'button'
158
+ statusBtn.className = 'dsh-tw-panel-status-btn'
159
+ statusBtn.title = '刷新状态'
160
+ statusDot = document.createElement('span')
161
+ statusDot.className = 'dsh-tw-status-dot'
162
+ statusDot.dataset.state = 'unknown'
163
+ chip = document.createElement('span')
164
+ chip.className = 'dsh-tw-panel-status-text'
165
+ chip.textContent = '—'
166
+ statusBtn.append(statusDot, chip)
167
+ statusBtn.addEventListener('click', () => { void doRefresh() })
168
+
169
+ const reloadBtn = document.createElement('button')
170
+ reloadBtn.type = 'button'
171
+ reloadBtn.className = 'dsh-tw-panel-status-btn'
172
+ reloadBtn.textContent = '重载'
173
+ reloadBtn.title = '重载 TiddlyWiki 面板'
174
+ reloadBtn.addEventListener('click', () => {
175
+ if (iframe !== undefined && !iframe.hidden) iframe.src = iframe.src
176
+ })
177
+
178
+ wrap.append(statusBtn, reloadBtn)
179
+ document.body.append(wrap)
180
+ return wrap
181
+ }
182
+
164
183
  const setChip = (stateName: string, text: string): void => {
165
- if (chip === undefined) return
166
- chip.dataset.state = stateName
167
- chip.textContent = text
184
+ if (statusDot !== undefined) statusDot.dataset.state = stateName
185
+ if (chip !== undefined) chip.textContent = text
168
186
  }
169
187
 
170
188
  /** Pin the overlay to the center column's current viewport rect. */
@@ -329,6 +347,13 @@ export function mountPanel(state: PanelState): () => void {
329
347
  ensure()
330
348
  applyActive()
331
349
 
350
+ // Bottom-right status/reload buttons, gated by ui.showPanelStatus.
351
+ void (async () => {
352
+ const payload = await fetchStatus()
353
+ if (payload?.ui?.showPanelStatus === false) return
354
+ statusFloater = buildStatusFloater()
355
+ })()
356
+
332
357
  return () => {
333
358
  if (refreshTimer !== undefined) window.clearTimeout(refreshTimer)
334
359
  window.clearInterval(syncInterval)
@@ -341,5 +366,6 @@ export function mountPanel(state: PanelState): () => void {
341
366
  unsubscribe()
342
367
  document.documentElement.removeAttribute(ACTIVE_ATTR)
343
368
  container?.remove()
369
+ statusFloater?.remove()
344
370
  }
345
371
  }
@@ -182,7 +182,7 @@ function renderConfigSection(body: HTMLElement, config: Record<string, unknown>,
182
182
  textField('git.remote', 'git 远端(空=仅本地)', typeof git.remote === 'string' ? git.remote : '')
183
183
  textField('git.branch', 'git 分支', typeof git.branch === 'string' ? git.branch : 'main')
184
184
  checkField('ui.showQuickNote', '显示「快速笔记」悬浮按钮', ui.showQuickNote !== false)
185
- checkField('ui.sidebarLeftCss', '侧边栏移左 CSS(随启动补丁还原被删除的样式)', ui.sidebarLeftCss !== false)
185
+ checkField('ui.showPanelStatus', '显示 TW 面板右下角「状态/重载」悬浮按钮', ui.showPanelStatus !== false)
186
186
  // 界面语言在下方「语言管理」区块设置(config 的 uiLanguage 仅供启动时自动应用)。
187
187
 
188
188
  const save = make('button', 'dsh-tw-settings-btn dsh-tw-settings-primary', '保存配置')
@@ -51,31 +51,31 @@ html[data-dsh-tw-active] [data-pane="conversation"] > :not([data-dsh-tw-view]),
51
51
  html[data-dsh-tw-active] [class*="centerCol"] > :not([data-dsh-tw-view]),
52
52
  html[data-dsh-tw-active] .dshDesktopConversationSurface > :not([data-dsh-tw-view]) { display: none !important; }
53
53
 
54
- .dsh-tw-panel-bar {
55
- display: flex; align-items: center; gap: 8px; padding: 6px 10px;
56
- border-bottom: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.18));
57
- background: var(--dsw-alias-bg-layer-1, transparent);
58
- font-size: 12px; color: var(--dsw-alias-label-secondary, inherit);
59
- flex: none;
54
+ /* ── bottom-right status / reload floating buttons ───────────────────
55
+ The top panel bar (title + status + reload row) is gone so TW's menubar
56
+ reaches the panel top; status/reload now live here as floating buttons,
57
+ same visual language as the quick-note toggle. The ui.showPanelStatus
58
+ setting controls whether they mount at all. */
59
+ .dsh-tw-panel-status {
60
+ /* 状态/重载悬浮按钮:右对齐到与快速笔记按钮同一条竖线(right:24),
61
+ 垂直排在快速笔记按钮(bottom:88,高约 38px)上方;
62
+ 两个按钮竖直依次排列(状态在上、重载在下),与其他插件按钮列对齐、不堆叠。 */
63
+ position: fixed; right: 24px; bottom: 140px; z-index: 950;
64
+ display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
60
65
  }
61
- .dsh-tw-panel-bar button {
62
- border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.25));
63
- background: transparent; color: inherit; font: inherit; font-size: 12px;
64
- padding: 2px 8px; border-radius: 6px; cursor: pointer;
66
+ .dsh-tw-panel-status-btn {
67
+ border: 1px solid var(--dsw-alias-border-l2, rgba(0,0,0,.18));
68
+ background: var(--dsw-alias-bg-layer-2, #fff); color: var(--dsw-alias-label-primary, #222);
69
+ font: inherit; font-size: 12px; padding: 7px 12px; border-radius: 999px;
70
+ box-shadow: var(--dsw-shadow-lv3, 0 4px 16px rgba(0,0,0,.16)); cursor: pointer;
71
+ display: inline-flex; align-items: center; gap: 6px;
72
+ transition: background-color 120ms ease;
65
73
  }
66
- .dsh-tw-panel-bar button:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); }
67
- .dsh-tw-panel-title { font-weight: 600; margin-right: auto; }
68
- /* dsh-better-sidebar coexistence: the floating toggle cluster (two 28px
69
- buttons at the viewport's top-right) lives above the panel (z-index is
70
- handled in JS — the panel stays below the host layer), so reserve the
71
- cluster's width at the panel bar's right end — mirroring how
72
- dsh-better-sidebar pads the DSH session header — and the panel's own
73
- 状态/重载 buttons never sit underneath it. */
74
- .dsh-tw-view[data-sidebar-host] .dsh-tw-panel-bar { padding-right: 80px; }
75
- .dsh-tw-status-chip { font-variant-numeric: tabular-nums; }
76
- .dsh-tw-status-chip[data-state="running"] { color: var(--dsw-alias-state-success-primary, #3eaa5f); }
77
- .dsh-tw-status-chip[data-state="starting"] { color: var(--dsw-alias-state-warning-primary, #d9822b); }
78
- .dsh-tw-status-chip[data-state="failed"], .dsh-tw-status-chip[data-state="stopped"] { color: var(--dsw-alias-state-error-primary, #d13b3b); }
74
+ .dsh-tw-panel-status-btn:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.1)); }
75
+ .dsh-tw-status-dot { width: 8px; height: 8px; border-radius: 50%; background: #999; flex: none; }
76
+ .dsh-tw-status-dot[data-state="running"] { background: var(--dsw-alias-state-success-primary, #3eaa5f); }
77
+ .dsh-tw-status-dot[data-state="starting"] { background: var(--dsw-alias-state-warning-primary, #d9822b); }
78
+ .dsh-tw-status-dot[data-state="failed"], .dsh-tw-status-dot[data-state="stopped"] { background: var(--dsw-alias-state-error-primary, #d13b3b); }
79
79
 
80
80
  .dsh-tw-panel-frame { flex: 1; min-height: 0; border: 0; width: 100%; display: block; background: #fff; }
81
81
  /* [hidden] must beat the author display rules above (UA hidden is overridden). */
@@ -23,8 +23,8 @@ export interface PluginConfigShape {
23
23
  ui?: {
24
24
  /** 是否在界面右下角显示「快速笔记」悬浮按钮(默认 true)。 */
25
25
  showQuickNote?: boolean
26
- /** 是否启用「侧边移到左侧.css」默认 CSS;被删除后随启动补丁还原(默认 true)。 */
27
- sidebarLeftCss?: boolean
26
+ /** 是否显示 TW 面板右下角的「状态/重载」悬浮按钮(默认 true)。 */
27
+ showPanelStatus?: boolean
28
28
  }
29
29
  uiLanguage?: string
30
30
  [key: string]: unknown
@@ -39,7 +39,7 @@ export interface RouteDeps {
39
39
  git: GitFace
40
40
  autoCommit: () => void
41
41
  noteDefaults: () => { tag: string }
42
- uiDefaults: () => { showQuickNote: boolean }
42
+ uiDefaults: () => { showQuickNote: boolean; showPanelStatus: boolean }
43
43
  getWikiPath: () => string
44
44
  }
45
45
 
@@ -1,122 +1,79 @@
1
- /**
2
- * Built-in doc note + optional UI-patch seeds for the plugin (design doc §14).
3
- *
4
- * Two seeds, deliberately different lifetimes:
5
- *
6
- * 1. DOC NOTE (`dsh-tiddlywiki 插件说明`) — ONE-SHOT: seeded only the first
7
- * time this plugin runs on a wiki (guarded by a `seed-doc-note` marker
8
- * tiddler). After that the note is the user's own content: deleting it and
9
- * restarting dsh web does NOT recreate it.
10
- * 2. SIDEBAR-LEFT CSS (`侧边移到左侧.css`) — PATCH-REPAIR: written whenever
11
- * the wiki lacks it while the `ui.sidebarLeftCss` option is enabled
12
- * (default on). The user may delete it; a restart with the option still on
13
- * re-seeds it (the "补丁选项"). Turning the option off stops re-seeding.
14
- *
15
- * @module dsh-tiddlywiki/host/seed-notes
16
- */
17
- import type { TiddlyWebClient } from './tw-api.ts'
18
-
19
- /** Note tiddler title (a normal, searchable note — not a system tiddler). */
20
- export const DOC_NOTE_TITLE = 'dsh-tiddlywiki 插件说明'
21
-
22
- /** Tag that makes the note easy to find via `tiddlywiki_search tag=docs`. */
23
- export const DOC_NOTE_TAG = 'docs'
24
-
25
- /** One-time marker: its presence means "the note was offered once — hands off". */
26
- export const SEED_MARKER_TITLE = '$:/plugins/dsh-tiddlywiki/seed-doc-note'
27
-
28
- /** The note body, TiddlyWiki wiki-text. */
29
- export const DOC_NOTE_TEXT = `! dsh-tiddlywiki 插件说明
30
-
31
- 本插件把 **TiddlyWiki 5** 作为 DSH 的持久知识库(wiki 文件夹本身就是一个 git 仓库,随内容自动提交/同步)。
32
-
33
- !! 它能做什么
34
-
35
- * **5 个 agent 工具**:\`tiddlywiki_search\`(检索)/ \`tiddlywiki_get\`(读)/ \`tiddlywiki_put\`(写)/ \`tiddlywiki_delete\`(删)/ \`tiddlywiki_git_sync\`(git 同步)。
36
- * **TW 编辑器面板**:侧边栏「TiddlyWiki」按钮 → 在界面中央打开完整版 TW 编辑器。
37
- * **快速笔记**:右下角悬浮「📝 快速笔记」写随手记(可多选/自动补全 tag),\`Ctrl+Enter\` 保存;点「✏️ 在 TW 中编辑」会弹出独立小窗用 TW 原生编辑器编辑。
38
- * **git 同步**:写入自动防抖 commit(默认 60 秒);手动 \`tiddlywiki_git_sync action=sync\` 做 pull → commit → push。
39
- * **设置页**:DSH 设置 → 「TiddlyWiki 知识库」管理插件/主题/语言与运行配置(含「快速笔记」显示开关、侧边栏移左 CSS 补丁开关)。
40
-
41
- !! 知识库纪律(三条)
42
-
43
- 1. 开工先 \`tiddlywiki_git_sync action=pull\`(rebase + autostash,真冲突会自动 abort 并报文件)。
44
- 2. 收工 \`tiddlywiki_git_sync action=sync\`。
45
- 3. 插件自动 commit 兜底,手动 sync 用于需要主动推送的场合。
46
-
47
- !! 主题与语言
48
-
49
- * **主题**分两层:每行一个「☑ 加载」(多选 = TW 里可用的主题,依赖链自动带上)和「◉ 活动」(单选 = 当前视觉主题)。应用后自动重启 TW。
50
- * **语言**:设置页勾选 \`zh-Hans\`(简体)并应用,TW 界面即切换为中文。
51
-
52
- !! 说明
53
-
54
- * 本笔记由插件在**首次启动**时自动写入(一次性:只写一次)。删除后重启 dsh web **不会自动恢复**——它从此归你所有。
55
- * 「\`侧边移到左侧.css\`」是插件默认安装的界面补丁(把 TW 右侧栏移到左边);可在设置页关闭「侧边栏移左 CSS」开关来停用补丁还原。
56
- * 更多细节见插件仓库 README。`
57
-
58
- /**
59
- * Seed the doc note exactly once per wiki. A marker tiddler records that the
60
- * note has been offered; from then on the note is user-owned and is never
61
- * re-created (deleting it survives restarts). Returns whether a note was
62
- * written this call. Never throws.
63
- */
64
- export async function seedDocNote(client: TiddlyWebClient): Promise<boolean> {
65
- const marker = await client.get(SEED_MARKER_TITLE).catch(() => undefined)
66
- if (marker !== undefined) return false
67
- const existing = await client.get(DOC_NOTE_TITLE).catch(() => undefined)
68
- let wrote = false
69
- if (existing === undefined) {
70
- await client.put({
71
- title: DOC_NOTE_TITLE,
72
- text: DOC_NOTE_TEXT,
73
- type: 'text/vnd.tiddlywiki',
74
- tags: [DOC_NOTE_TAG],
75
- })
76
- wrote = true
77
- }
78
- // Record the offer regardless, so an existing note (upgrade from an older
79
- // create-if-missing version) also becomes user-owned from here on.
80
- await client
81
- .put({ title: SEED_MARKER_TITLE, text: 'seeded-once', type: 'text/plain', tags: [] })
82
- .catch(() => undefined)
83
- return wrote
84
- }
85
-
86
- /** Sidebar-left CSS tiddler: moves TW's sidebar to the LEFT (mirror of the
87
- * `centralised` default), so dsh-better-sidebar's top-right toggle cluster
88
- * no longer sits over TW's own right sidebar. A normal tiddler (tag
89
- * `$:/tags/Stylesheet`, type text/css) — same pattern as hand-made
90
- * `编辑界面微调:小标题·大编辑器.css`, easy to find and delete. */
91
- export const SIDEBAR_LEFT_CSS_TITLE = '侧边移到左侧.css'
92
-
93
- /** The stylesheet body. Hardcoded 960px breakpoint (vanilla default
94
- * sidebarbreakpoint) — plain text/css tiddlers don't process `{{...}}`. */
95
- export const SIDEBAR_LEFT_CSS = `/* 由 dsh-tiddlywiki 插件默认安装:把 TW 右侧栏镜像到左侧(与 centralised 主题默认对称)。
96
- 删除后,只要设置页「侧边栏移左 CSS」补丁开关开着,插件会在下次启动时重建本 tiddler。 */
97
- @media (min-width: 960px) {
98
- html .tc-sidebar-scrollable {
99
- left: 0 !important;
100
- right: 50% !important;
101
- margin-left: 0 !important;
102
- margin-right: 343px !important;
103
- }
104
- }`
105
-
106
- /**
107
- * Patch-repair the sidebar-left CSS while the option is enabled: write it
108
- * whenever it is missing (fresh installs and deleted-by-user both re-seed).
109
- * Returns whether it was written. Never throws.
110
- */
111
- export async function seedSidebarLeftCss(client: TiddlyWebClient, enabled: boolean): Promise<boolean> {
112
- if (!enabled) return false
113
- const existing = await client.get(SIDEBAR_LEFT_CSS_TITLE).catch(() => undefined)
114
- if (existing !== undefined) return false
115
- await client.put({
116
- title: SIDEBAR_LEFT_CSS_TITLE,
117
- text: SIDEBAR_LEFT_CSS,
118
- type: 'text/css',
119
- tags: ['$:/tags/Stylesheet'],
120
- })
121
- return true
122
- }
1
+ /**
2
+ * Built-in doc note for the plugin (design doc §14): a short user-facing
3
+ * "how to use dsh-tiddlywiki" note seeded into the wiki the first time the
4
+ * plugin runs on a wiki.
5
+ *
6
+ * ONE-SHOT seed: a marker tiddler (`seed-doc-note`) records that the note has
7
+ * been offered once; after that the note is the user's own content — deleting
8
+ * it and restarting dsh web does NOT recreate it, and edits are never
9
+ * overwritten.
10
+ *
11
+ * @module dsh-tiddlywiki/host/seed-notes
12
+ */
13
+ import type { TiddlyWebClient } from './tw-api.ts'
14
+
15
+ /** Note tiddler title (a normal, searchable note — not a system tiddler). */
16
+ export const DOC_NOTE_TITLE = 'dsh-tiddlywiki 插件说明'
17
+
18
+ /** Tag that makes the note easy to find via `tiddlywiki_search tag=docs`. */
19
+ export const DOC_NOTE_TAG = 'docs'
20
+
21
+ /** One-time marker: its presence means "the note was offered once — hands off". */
22
+ export const SEED_MARKER_TITLE = '$:/plugins/dsh-tiddlywiki/seed-doc-note'
23
+
24
+ /** The note body, TiddlyWiki wiki-text. */
25
+ export const DOC_NOTE_TEXT = `! dsh-tiddlywiki 插件说明
26
+
27
+ 本插件把 **TiddlyWiki 5** 作为 DSH 的持久知识库(wiki 文件夹本身就是一个 git 仓库,随内容自动提交/同步)。
28
+
29
+ !! 它能做什么
30
+
31
+ * **5 个 agent 工具**:\`tiddlywiki_search\`(检索)/ \`tiddlywiki_get\`(读)/ \`tiddlywiki_put\`(写)/ \`tiddlywiki_delete\`(删)/ \`tiddlywiki_git_sync\`(git 同步)。
32
+ * **TW 编辑器面板**:侧边栏「TiddlyWiki」按钮 → 在界面中央打开完整版 TW 编辑器。
33
+ * **快速笔记**:右下角悬浮「📝 快速笔记」写随手记(可多选/自动补全 tag),\`Ctrl+Enter\` 保存;点「✏️ 在 TW 中编辑」会弹出独立小窗用 TW 原生编辑器编辑。
34
+ * **git 同步**:写入自动防抖 commit(默认 60 秒);手动 \`tiddlywiki_git_sync action=sync\` 做 pull → commit → push。
35
+ * **设置页**:DSH 设置 → 「TiddlyWiki 知识库」管理插件/主题/语言与运行配置(含「快速笔记」显示开关、面板「状态/重载」悬浮按钮开关)。
36
+
37
+ !! 知识库纪律(三条)
38
+
39
+ 1. 开工先 \`tiddlywiki_git_sync action=pull\`(rebase + autostash,真冲突会自动 abort 并报文件)。
40
+ 2. 收工 \`tiddlywiki_git_sync action=sync\`。
41
+ 3. 插件自动 commit 兜底,手动 sync 用于需要主动推送的场合。
42
+
43
+ !! 主题与语言
44
+
45
+ * **主题**分两层:每行一个「☑ 加载」(多选 = TW 里可用的主题,依赖链自动带上)和「◉ 活动」(单选 = 当前视觉主题)。应用后自动重启 TW。
46
+ * **语言**:设置页勾选 \`zh-Hans\`(简体)并应用,TW 界面即切换为中文。
47
+
48
+ !! 说明
49
+
50
+ * 本笔记由插件在**首次启动**时自动写入(一次性:只写一次)。删除后重启 dsh web **不会自动恢复**——它从此归你所有。
51
+ * 更多细节见插件仓库 README。`
52
+
53
+ /**
54
+ * Seed the doc note exactly once per wiki. A marker tiddler records that the
55
+ * note has been offered; from then on the note is user-owned and is never
56
+ * re-created (deleting it survives restarts). Returns whether a note was
57
+ * written this call. Never throws.
58
+ */
59
+ export async function seedDocNote(client: TiddlyWebClient): Promise<boolean> {
60
+ const marker = await client.get(SEED_MARKER_TITLE).catch(() => undefined)
61
+ if (marker !== undefined) return false
62
+ const existing = await client.get(DOC_NOTE_TITLE).catch(() => undefined)
63
+ let wrote = false
64
+ if (existing === undefined) {
65
+ await client.put({
66
+ title: DOC_NOTE_TITLE,
67
+ text: DOC_NOTE_TEXT,
68
+ type: 'text/vnd.tiddlywiki',
69
+ tags: [DOC_NOTE_TAG],
70
+ })
71
+ wrote = true
72
+ }
73
+ // Record the offer regardless, so an existing note (upgrade from an older
74
+ // create-if-missing version) also becomes user-owned from here on.
75
+ await client
76
+ .put({ title: SEED_MARKER_TITLE, text: 'seeded-once', type: 'text/plain', tags: [] })
77
+ .catch(() => undefined)
78
+ return wrote
79
+ }
package/src/index.ts CHANGED
@@ -25,7 +25,7 @@ import { AutoCommitter, GitFace } from './host/git.ts'
25
25
  import { registerRoutes, type WebServerFace } from './host/routes.ts'
26
26
  import { ConfigStore, deepMerge, type PluginConfigShape } from './host/config.ts'
27
27
  import { registerAdminRoutes, ensureLanguage, resolveTwRoot, type AdminDeps } from './host/admin.ts'
28
- import { seedDocNote, seedSidebarLeftCss, DOC_NOTE_TITLE } from './host/seed-notes.ts'
28
+ import { seedDocNote, DOC_NOTE_TITLE } from './host/seed-notes.ts'
29
29
  import { TiddlyWebClient } from './host/tw-api.ts'
30
30
  import { registerTiddlywikiTools, type ToolsDeps } from './host/tools.ts'
31
31
  import { PATH_PREFIX, WikiServer, type WikiServerOptions } from './host/wiki.ts'
@@ -42,7 +42,7 @@ export { AutoCommitter, GitFace, PATH_PREFIX, TiddlyWebClient, WikiServer, dshHo
42
42
  export { ConfigStore, deepMerge } from './host/config.ts'
43
43
  export { openInTwEditor } from './host/routes.ts'
44
44
  export { registerAdminRoutes, resolveTwRoot, readWikiInfo, writeWikiInfo, bundledCatalog, ensureLanguage, normalizeThemes } from './host/admin.ts'
45
- export { seedDocNote, seedSidebarLeftCss, DOC_NOTE_TITLE, DOC_NOTE_TAG, DOC_NOTE_TEXT, SIDEBAR_LEFT_CSS_TITLE, SIDEBAR_LEFT_CSS } from './host/seed-notes.ts'
45
+ export { seedDocNote, DOC_NOTE_TITLE, DOC_NOTE_TAG, DOC_NOTE_TEXT } from './host/seed-notes.ts'
46
46
  export type { PluginConfigShape } from './host/config.ts'
47
47
  export type { GitStatusView } from './host/git.ts'
48
48
  export type { Tiddler } from './host/tw-api.ts'
@@ -55,7 +55,7 @@ export interface TiddlywikiConfig {
55
55
  port?: number
56
56
  git?: { autoCommit?: boolean; debounceMs?: number; remote?: string; branch?: string }
57
57
  note?: { tag?: string }
58
- ui?: { showQuickNote?: boolean; sidebarLeftCss?: boolean }
58
+ ui?: { showQuickNote?: boolean; showPanelStatus?: boolean }
59
59
  auth?: { username?: string; password?: string }
60
60
  }
61
61
 
@@ -76,7 +76,7 @@ interface ResolvedConfig {
76
76
  port: number
77
77
  git: { autoCommit: boolean; debounceMs: number; remote: string; branch: string }
78
78
  note: { tag: string }
79
- ui: { showQuickNote: boolean; sidebarLeftCss: boolean }
79
+ ui: { showQuickNote: boolean; showPanelStatus: boolean }
80
80
  auth: { username?: string; password?: string }
81
81
  }
82
82
 
@@ -86,7 +86,7 @@ const DEFAULTS: ResolvedConfig = {
86
86
  port: 0,
87
87
  git: { autoCommit: true, debounceMs: 60_000, remote: '', branch: 'main' },
88
88
  note: { tag: 'inbox' },
89
- ui: { showQuickNote: true, sidebarLeftCss: true },
89
+ ui: { showQuickNote: true, showPanelStatus: true },
90
90
  auth: { username: '', password: '' },
91
91
  }
92
92
 
@@ -181,9 +181,9 @@ export function apply(ctx: HostCtx, rawConfig: TiddlywikiConfig = {}): void {
181
181
  const tag = eff().note?.tag
182
182
  return typeof tag === 'string' && tag.trim().length > 0 ? tag : config.note.tag
183
183
  }
184
- const effectiveUi = (): { showQuickNote: boolean; sidebarLeftCss: boolean } => ({
184
+ const effectiveUi = (): { showQuickNote: boolean; showPanelStatus: boolean } => ({
185
185
  showQuickNote: eff().ui?.showQuickNote !== false,
186
- sidebarLeftCss: eff().ui?.sidebarLeftCss !== false,
186
+ showPanelStatus: eff().ui?.showPanelStatus !== false,
187
187
  })
188
188
 
189
189
  const disposers: Array<() => void> = []
@@ -276,16 +276,13 @@ export function apply(ctx: HostCtx, rawConfig: TiddlywikiConfig = {}): void {
276
276
  // Seeds (run after the effective config is loaded):
277
277
  // - doc note: ONE-SHOT — a fresh wiki gets the plugin guide; deleting
278
278
  // it afterwards survives restarts (see seedDocNote).
279
- // - sidebar-left CSS: PATCH-REPAIR while ui.sidebarLeftCss is on —
280
- // re-seeds when missing (fresh install OR user deleted it).
281
279
  try {
282
280
  const seedClient = client()
283
281
  if (seedClient !== undefined) {
284
282
  await seedDocNote(seedClient)
285
- await seedSidebarLeftCss(seedClient, effectiveUi().sidebarLeftCss)
286
283
  }
287
284
  } catch (err) {
288
- console.warn('[dsh-tiddlywiki] seeding doc note / css:', err)
285
+ console.warn('[dsh-tiddlywiki] seeding doc note:', err)
289
286
  }
290
287
  // Apply the configured UI language (e.g. "zh-Hans"): enable the bundled
291
288
  // language plugin in tiddlywiki.info.languages + restart once so TW loads