dsh-tiddlywiki 0.16.11 → 0.16.13

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/src/host/tools.ts CHANGED
@@ -115,6 +115,14 @@ export function registerTiddlywikiTools(ctx: ToolsCtx, deps: ToolsDeps): Array<(
115
115
  const disposers: Array<() => void> = []
116
116
  const register = (tool: unknown): void => { disposers.push(ctx.tools.register(tool)) }
117
117
 
118
+ /** Every read/write tool needs a live TW client — shared guard for the 8
119
+ * wiki-facing tools (git tools operate on the repo path instead). */
120
+ const requireWiki = (): TiddlyWebClient => {
121
+ const wiki = deps.wiki()
122
+ if (wiki === undefined) throw new Error('TiddlyWiki 服务未运行(tiddlywiki_status 可查)')
123
+ return wiki
124
+ }
125
+
118
126
  // ── tiddlywiki_search ────────────────────────────────────────────────────
119
127
  register(defineTool({
120
128
  name: 'tiddlywiki_search',
@@ -148,8 +156,7 @@ export function registerTiddlywikiTools(ctx: ToolsCtx, deps: ToolsDeps): Array<(
148
156
  },
149
157
  },
150
158
  execute: async (args: { query: string; tags?: string[]; tag?: string; since?: string; type?: string; limit?: number }): Promise<SearchResult> => {
151
- const wiki = deps.wiki()
152
- if (wiki === undefined) throw new Error('TiddlyWiki 服务未运行(tiddlywiki_status 可查)')
159
+ const wiki = requireWiki()
153
160
  const { items, total } = await wiki.search(args.query, {
154
161
  tags: args.tags,
155
162
  tag: args.tag,
@@ -190,8 +197,7 @@ export function registerTiddlywikiTools(ctx: ToolsCtx, deps: ToolsDeps): Array<(
190
197
  },
191
198
  },
192
199
  execute: async (args: { limit?: number; since?: string }): Promise<RecentResult> => {
193
- const wiki = deps.wiki()
194
- if (wiki === undefined) throw new Error('TiddlyWiki 服务未运行(tiddlywiki_status 可查)')
200
+ const wiki = requireWiki()
195
201
  const items = await wiki.recent(args.limit ?? 15, args.since)
196
202
  return {
197
203
  since: args.since ?? null,
@@ -215,8 +221,7 @@ export function registerTiddlywikiTools(ctx: ToolsCtx, deps: ToolsDeps): Array<(
215
221
  },
216
222
  },
217
223
  execute: async (): Promise<TagListResult> => {
218
- const wiki = deps.wiki()
219
- if (wiki === undefined) throw new Error('TiddlyWiki 服务未运行(tiddlywiki_status 可查)')
224
+ const wiki = requireWiki()
220
225
  const tags = await wiki.listTags()
221
226
  return { count: tags.length, tags }
222
227
  },
@@ -244,8 +249,7 @@ export function registerTiddlywikiTools(ctx: ToolsCtx, deps: ToolsDeps): Array<(
244
249
  },
245
250
  },
246
251
  execute: async (args: { title: string }): Promise<GetResult> => {
247
- const wiki = deps.wiki()
248
- if (wiki === undefined) throw new Error('TiddlyWiki 服务未运行(tiddlywiki_status 可查)')
252
+ const wiki = requireWiki()
249
253
  const t = await wiki.get(args.title)
250
254
  if (t === undefined) return { notFound: true, title: args.title, text: '', tags: [], fields: {}, modified: null }
251
255
  return { notFound: false, title: t.title, text: t.text ?? '', tags: t.tags ?? [], fields: pickFields(t), modified: typeof t.modified === 'string' ? t.modified : null }
@@ -275,8 +279,7 @@ export function registerTiddlywikiTools(ctx: ToolsCtx, deps: ToolsDeps): Array<(
275
279
  },
276
280
  },
277
281
  execute: async (args: { title: string; text: string; tags?: string[]; fields?: Record<string, unknown> }): Promise<PutResult> => {
278
- const wiki = deps.wiki()
279
- if (wiki === undefined) throw new Error('TiddlyWiki 服务未运行(tiddlywiki_status 可查)')
282
+ const wiki = requireWiki()
280
283
  const existing = await wiki.get(args.title).catch(() => undefined)
281
284
  const tags = finalTagsForWrite(args.title, existing, Array.isArray(args.tags) ? args.tags.filter((t) => typeof t === 'string' && t.trim().length > 0) : [])
282
285
  const tiddler: Tiddler = { title: args.title, text: args.text }
@@ -320,8 +323,7 @@ export function registerTiddlywikiTools(ctx: ToolsCtx, deps: ToolsDeps): Array<(
320
323
  },
321
324
  },
322
325
  execute: async (args: { items: Array<{ title: string; text: string; tags?: string[]; fields?: Record<string, unknown> }>; overwrite?: boolean }): Promise<BatchResult> => {
323
- const wiki = deps.wiki()
324
- if (wiki === undefined) throw new Error('TiddlyWiki 服务未运行(tiddlywiki_status 可查)')
326
+ const wiki = requireWiki()
325
327
  const list = Array.isArray(args.items) ? args.items : []
326
328
  if (list.length === 0) return { ok: true, written: 0, skipped: 0, items: [] }
327
329
  const overwrite = args.overwrite !== false
@@ -369,8 +371,7 @@ export function registerTiddlywikiTools(ctx: ToolsCtx, deps: ToolsDeps): Array<(
369
371
  },
370
372
  },
371
373
  execute: async (args: { oldTitle: string; newTitle: string; updateRefs?: boolean }): Promise<RenameResult> => {
372
- const wiki = deps.wiki()
373
- if (wiki === undefined) throw new Error('TiddlyWiki 服务未运行(tiddlywiki_status 可查)')
374
+ const wiki = requireWiki()
374
375
  const { oldTitle, newTitle } = args
375
376
  if (oldTitle === newTitle) return { ok: true, from: oldTitle, to: newTitle, refsUpdated: 0, refsTiddlers: 0 }
376
377
  const existing = await wiki.get(oldTitle)
@@ -417,8 +418,7 @@ export function registerTiddlywikiTools(ctx: ToolsCtx, deps: ToolsDeps): Array<(
417
418
  render: (_args, value: DeleteResult) => [{ type: 'text', text: `已删除 tiddler「${value.title}」。` }],
418
419
  },
419
420
  execute: async (args: { title: string }): Promise<DeleteResult> => {
420
- const wiki = deps.wiki()
421
- if (wiki === undefined) throw new Error('TiddlyWiki 服务未运行(tiddlywiki_status 可查)')
421
+ const wiki = requireWiki()
422
422
  await wiki.delete(args.title)
423
423
  deps.autoCommit()
424
424
  return { ok: true, title: args.title }
@@ -497,15 +497,7 @@ export function registerTiddlywikiTools(ctx: ToolsCtx, deps: ToolsDeps): Array<(
497
497
  lines.push(` ${value.message}`)
498
498
  if (value.files !== undefined && value.files.length > 0) lines.push(`涉及文件: ${value.files.join(', ')}`)
499
499
  if (value.commit !== undefined) lines.push(`本地 commit: ${value.commit}`)
500
- if (value.status !== undefined) {
501
- const s = value.status
502
- const bits = [`分支 ${s.branch}`]
503
- if (s.ahead !== undefined) bits.push(`领先 ${s.ahead}`)
504
- if (s.behind !== undefined) bits.push(`落后 ${s.behind}`)
505
- if (s.dirty) bits.push(`工作区有 ${s.dirtyFiles.length} 个未提交改动`)
506
- if (s.lastCommit !== undefined) bits.push(`最近提交 ${s.lastCommit}`)
507
- lines.push(`状态: ${bits.join(' · ')}`)
508
- }
500
+ if (value.status !== undefined) lines.push(`状态: ${gitStatusBits(value.status)}`)
509
501
  return [{ type: 'text', text: lines.join('\n') }]
510
502
  },
511
503
  },
@@ -576,7 +568,7 @@ interface SyncResult {
576
568
  changed?: boolean
577
569
  restarted?: boolean
578
570
  restartError?: string
579
- status?: { branch: string; dirty: boolean; dirtyFiles: string[]; remote: string; lastCommit?: string; ahead?: number; behind?: number }
571
+ status?: GitStatusView
580
572
  }
581
573
  interface ResolveResult {
582
574
  ok: boolean
@@ -585,7 +577,28 @@ interface ResolveResult {
585
577
  files?: string[]
586
578
  commit?: string
587
579
  hint?: string
588
- status?: { branch: string; dirty: boolean; dirtyFiles: string[]; remote: string; lastCommit?: string; ahead?: number; behind?: number }
580
+ status?: GitStatusView
581
+ }
582
+
583
+ /** Shape of deps.git.status() as surfaced to the model (shared by renders). */
584
+ interface GitStatusView {
585
+ branch: string
586
+ dirty: boolean
587
+ dirtyFiles: string[]
588
+ remote: string
589
+ lastCommit?: string
590
+ ahead?: number
591
+ behind?: number
592
+ }
593
+
594
+ /** One-line 状态 summary: 分支 … 领先 … 落后 … 工作区未提交 … 最近提交. */
595
+ function gitStatusBits(s: GitStatusView): string {
596
+ const bits = [`分支 ${s.branch}`]
597
+ if (s.ahead !== undefined) bits.push(`领先 ${s.ahead}`)
598
+ if (s.behind !== undefined) bits.push(`落后 ${s.behind}`)
599
+ if (s.dirty) bits.push(`工作区有 ${s.dirtyFiles.length} 个未提交改动`)
600
+ if (s.lastCommit !== undefined) bits.push(`最近提交 ${s.lastCommit}`)
601
+ return bits.join(' · ')
589
602
  }
590
603
 
591
604
  function renderSync(value: SyncResult): Array<{ type: 'text'; text: string }> {
@@ -605,13 +618,8 @@ function renderSync(value: SyncResult): Array<{ type: 'text'; text: string }> {
605
618
  }
606
619
  if (value.restartError !== undefined) lines.push(`TW 重启失败: ${value.restartError}`)
607
620
  if (value.status !== undefined) {
621
+ lines.push(`状态: ${gitStatusBits(value.status)}`)
608
622
  const s = value.status
609
- const bits = [`分支 ${s.branch}`]
610
- if (s.ahead !== undefined) bits.push(`领先 ${s.ahead}`)
611
- if (s.behind !== undefined) bits.push(`落后 ${s.behind}`)
612
- if (s.dirty) bits.push(`工作区有 ${s.dirtyFiles.length} 个未提交改动`)
613
- if (s.lastCommit !== undefined) bits.push(`最近提交 ${s.lastCommit}`)
614
- lines.push(`状态: ${bits.join(' · ')}`)
615
623
  if (s.dirty && s.dirtyFiles.length > 0) lines.push(` 未提交: ${s.dirtyFiles.join(', ')}`)
616
624
  }
617
625
  return [{ type: 'text', text: lines.join('\n') }]
package/src/index.ts CHANGED
@@ -57,14 +57,18 @@ export type { GitStatusView } from './host/git.ts'
57
57
  export type { Tiddler } from './host/tw-api.ts'
58
58
  export type { WikiServerOptions, WikiStatusView } from './host/wiki.ts'
59
59
 
60
- /** Plugin config (design doc §13). Defaults are applied in apply(). */
60
+ /** Plugin config (design doc §13). Defaults are applied in apply().
61
+ * Mirrors PluginConfigShape (src/host/config.ts) — the cordis `config:` block;
62
+ * keep the two shapes in lockstep when adding a config field. */
61
63
  export interface TiddlywikiConfig {
62
64
  wikiRoot?: string
63
65
  wiki?: string
64
66
  port?: number
65
67
  git?: { autoCommit?: boolean; debounceMs?: number; remote?: string; branch?: string }
66
68
  note?: { tag?: string }
67
- ui?: { showQuickNote?: boolean; showQuickNoteDock?: boolean; sidebarLabel?: string; showPanelStatus?: boolean; showSyncButton?: boolean; followDshTheme?: boolean; darkPalette?: string; tabLabel?: string; showSessionTab?: boolean }
69
+ ui?: { showQuickNote?: boolean; showQuickNoteDock?: boolean; sidebarLabel?: string; showPanelStatus?: boolean; showSyncButton?: boolean; followDshTheme?: boolean; darkPalette?: string; tabLabel?: string; showSessionTab?: boolean; sendToAgent?: { enabled?: boolean; endpoint?: string; token?: string }; allArticles?: { pageSize?: number } }
70
+ /** 启动时自动启用的 TW 语言代码(如 "zh-Hans"),也受配置 tiddler 覆盖。 */
71
+ uiLanguage?: string
68
72
  auth?: { username?: string; password?: string }
69
73
  }
70
74
 
@@ -85,7 +89,8 @@ interface ResolvedConfig {
85
89
  port: number
86
90
  git: { autoCommit: boolean; debounceMs: number; remote: string; branch: string }
87
91
  note: { tag: string }
88
- ui: { showQuickNote: boolean; showQuickNoteDock: boolean; sidebarLabel: string; showPanelStatus: boolean; showSyncButton: boolean; followDshTheme: boolean; darkPalette: string; tabLabel: string; showSessionTab: boolean; sendToAgent: { enabled: boolean } }
92
+ ui: { showQuickNote: boolean; showQuickNoteDock: boolean; sidebarLabel: string; showPanelStatus: boolean; showSyncButton: boolean; followDshTheme: boolean; darkPalette: string; tabLabel: string; showSessionTab: boolean; sendToAgent: { enabled: boolean; endpoint?: string; token?: string }; allArticles: { pageSize: number } }
93
+ uiLanguage: string
89
94
  auth: { username?: string; password?: string }
90
95
  }
91
96
 
@@ -95,7 +100,8 @@ const DEFAULTS: ResolvedConfig = {
95
100
  port: 0,
96
101
  git: { autoCommit: true, debounceMs: 60_000, remote: '', branch: 'main' },
97
102
  note: { tag: 'inbox' },
98
- ui: { showQuickNote: true, showQuickNoteDock: true, sidebarLabel: 'TiddlyWiki', showPanelStatus: true, showSyncButton: true, followDshTheme: true, darkPalette: DARK_PALETTE_DEFAULT, tabLabel: '知识库', showSessionTab: true, sendToAgent: { enabled: true } },
103
+ ui: { showQuickNote: true, showQuickNoteDock: true, sidebarLabel: 'TiddlyWiki', showPanelStatus: true, showSyncButton: true, followDshTheme: true, darkPalette: DARK_PALETTE_DEFAULT, tabLabel: '知识库', showSessionTab: true, sendToAgent: { enabled: true }, allArticles: { pageSize: 10 } },
104
+ uiLanguage: '',
99
105
  auth: { username: '', password: '' },
100
106
  }
101
107
 
@@ -228,7 +234,15 @@ export function apply(ctx: HostCtx, rawConfig: TiddlywikiConfig = {}): void {
228
234
  port: rawConfig.port ?? DEFAULTS.port,
229
235
  git: { ...DEFAULTS.git, ...(rawConfig.git ?? {}) },
230
236
  note: { ...DEFAULTS.note, ...(rawConfig.note ?? {}) },
231
- ui: { ...DEFAULTS.ui, ...(rawConfig.ui ?? {}) },
237
+ ui: {
238
+ ...DEFAULTS.ui,
239
+ ...(rawConfig.ui ?? {}),
240
+ // Merge nested ui.* groups explicitly so defaults stay required (a plain
241
+ // spread of the lax cordis shape would widen them to optional).
242
+ sendToAgent: { ...DEFAULTS.ui.sendToAgent, ...(rawConfig.ui?.sendToAgent ?? {}) },
243
+ allArticles: { ...DEFAULTS.ui.allArticles, ...(rawConfig.ui?.allArticles ?? {}) },
244
+ },
245
+ uiLanguage: typeof rawConfig.uiLanguage === 'string' ? rawConfig.uiLanguage.trim() : DEFAULTS.uiLanguage,
232
246
  auth: { ...DEFAULTS.auth, ...(rawConfig.auth ?? {}) },
233
247
  }
234
248
  const wikiPath = join(config.wikiRoot, config.wiki)
@@ -237,7 +251,7 @@ export function apply(ctx: HostCtx, rawConfig: TiddlywikiConfig = {}): void {
237
251
  // Runtime-editable config (settings page): the cordis `config:` block is the
238
252
  // BASE; a config tiddler ($:/plugins/dsh-tiddlywiki/config) written by the
239
253
  // settings page overlays it. Effective values come from configStore.get().
240
- const configStore = new ConfigStore({ note: config.note, git: config.git, ui: config.ui } satisfies PluginConfigShape)
254
+ const configStore = new ConfigStore({ note: config.note, git: config.git, ui: config.ui, uiLanguage: config.uiLanguage } satisfies PluginConfigShape)
241
255
  const eff = (): PluginConfigShape => configStore.get()
242
256
  const effectiveNoteTag = (): string => {
243
257
  const tag = eff().note?.tag