dsh-tiddlywiki 0.16.19 → 0.16.21

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 with filters/recent/tags/batch/rename + git sync & conflict resolve), a full TiddlyWiki editor embedded in the GUI center column (same-origin proxy, works over Tailscale/LAN/domain), a quick-note card with draft auto-save & recent-notes picker behind a single knowledge FAB, and git-based sync with auto-commit. Mounts via the official dsh plugin system — no DSH source changes.",
4
- "version": "0.16.19",
4
+ "version": "0.16.21",
5
5
  "type": "module",
6
6
  "keywords": [
7
7
  "dsh",
@@ -1,10 +1,10 @@
1
- /**
2
- * Same-origin DSH endpoints the client calls. The authoritative route table
3
- * lives in src/host/routes.ts (see AGENTS.md §1「DSH 路由」) — this is just the
4
- * client-side mirror, so the path string exists once per endpoint instead of
5
- * being re-typed in every widget.
6
- *
7
- * @module dsh-tiddlywiki/client/endpoints
8
- */
9
- export const STATUS_ENDPOINT = '/dsh-tiddlywiki/status'
10
- export const GET_ENDPOINT = '/dsh-tiddlywiki/get'
1
+ /**
2
+ * Same-origin DSH endpoints the client calls. The authoritative route table
3
+ * lives in src/host/routes.ts (see AGENTS.md §1「DSH 路由」) — this is just the
4
+ * client-side mirror, so the path string exists once per endpoint instead of
5
+ * being re-typed in every widget.
6
+ *
7
+ * @module dsh-tiddlywiki/client/endpoints
8
+ */
9
+ export const STATUS_ENDPOINT = '/dsh-tiddlywiki/status'
10
+ export const GET_ENDPOINT = '/dsh-tiddlywiki/get'
@@ -24,6 +24,7 @@ import { mountSessionSummaryView } from './session-summary.ts'
24
24
  import { disposeEditorPopup } from './editor-popup.ts'
25
25
  import { SettingsSection } from './settings-page.ts'
26
26
  import { registerToolViews, installWikiLinkInterceptor } from './tool-views.ts'
27
+ import { mountRightbarTab, type RightbarSlotsFace } from './rightbar-tab.ts'
27
28
 
28
29
  /** Client plugin name. */
29
30
  export const name = 'dsh-tiddlywiki/client'
@@ -40,9 +41,16 @@ interface ClientContextFace {
40
41
  component: unknown,
41
42
  ): () => void
42
43
  }
44
+ /** Optional service read without the inject requirement (cordis ctx.get). */
45
+ get?: (name: string) => unknown
43
46
  effect?(fn: () => unknown, label?: string): void
44
47
  }
45
48
 
49
+ /** Structural face over the rightbar tab registry (dsh-client-ui-sidebar-right). */
50
+ interface SidebarRightTabsFace {
51
+ register(definition: unknown): () => void
52
+ }
53
+
46
54
  /**
47
55
  * Client entry: installs styles and mounts the DOM seats + settings page.
48
56
  * @param ctx - the cordis client context.
@@ -91,6 +99,34 @@ export function apply(ctx: ClientContextFace): void {
91
99
  // DOM failures degrade the plugin, never the GUI.
92
100
  console.error('[dsh-tiddlywiki] mount failed:', error)
93
101
  }
102
+ try {
103
+ // 右侧边栏(DSH new rightbar)集成:可选挂载——仅当
104
+ // dsh-client-ui-sidebar-right 提供了 sidebarRightTabs 服务时才启用
105
+ // (老版本 DSH / 无右侧栏时静默跳过,插件其余功能不受影响)。注册 TW
106
+ // tab 类型 + guide 首页入口盒;由 ui.showRightbarTab 控制(默认开)。
107
+ let retryTimer: number | undefined
108
+ const tryMountRightbar = (attempt: number): void => {
109
+ const tabs = ctx.get?.('sidebarRightTabs') as SidebarRightTabsFace | undefined
110
+ if (tabs === undefined) {
111
+ // rightbar 插件可能在本次 apply 之后才就绪:有限重试几次即可。
112
+ if (attempt < 6 && !clientDisposed) {
113
+ retryTimer = window.setTimeout(() => tryMountRightbar(attempt + 1), 500 * (attempt + 1))
114
+ }
115
+ return
116
+ }
117
+ void fetchUiConfig().then((cfg) => {
118
+ if (clientDisposed) return
119
+ if (!cfg.showRightbarTab) return
120
+ const removeRightbar = mountRightbarTab(tabs, ctx.slots as unknown as RightbarSlotsFace)
121
+ if (removeRightbar !== undefined) disposers.push(removeRightbar)
122
+ })
123
+ }
124
+ tryMountRightbar(0)
125
+ disposers.push(() => { if (retryTimer !== undefined) window.clearTimeout(retryTimer) })
126
+ } catch (error) {
127
+ // 右侧栏集成失败只影响该功能本身,绝不让整个插件挂掉。
128
+ console.error('[dsh-tiddlywiki] rightbar mount failed:', error)
129
+ }
94
130
  try {
95
131
  // Reply-stream native tool cards: keyed `tool.call.toolview` slots for
96
132
  // every tiddlywiki_* tool (additive — our keys are unclaimed).
@@ -1,128 +1,128 @@
1
- /**
2
- * CodeMirror 6 Markdown editor for the quick-note widget (replaces the
3
- * hand-rolled regex highlighter in the old markdown.ts — real Lezer syntax
4
- * tree, proper GFM coverage, undo/redo, and a native editing surface).
5
- *
6
- * The widget stays a plain-DOM product: CodeMirror is framework-agnostic, so
7
- * no React is pulled in. The editor is built into a wrapper `<div>`
8
- * (`.dsh-tw-note-editor`) that the widget can append, drag-drop onto, and
9
- * re-read via `getValue()`/`setValue()`.
10
- *
11
- * Theming follows the DSH design tokens (`--dsw-alias-*`) through a
12
- * `HighlightStyle` that maps Markdown tags onto the same palette the old
13
- * `.md-*` rules used, so light/dark both look right.
14
- *
15
- * @module dsh-tiddlywiki/client/markdown-editor
16
- */
17
- import { EditorView, keymap, placeholder, drawSelection } from '@codemirror/view'
18
- import { EditorState } from '@codemirror/state'
19
- import { markdown, markdownLanguage } from '@codemirror/lang-markdown'
20
- import { syntaxHighlighting, HighlightStyle } from '@codemirror/language'
21
- import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'
22
- import { tags as t } from '@lezer/highlight'
23
-
24
- /** Design-token aliases (same values as styles.ts / the old .md-* rules). */
25
- const BRAND = 'var(--dsw-alias-brand-primary, #3e63dd)'
26
- const BRAND_DIM = 'color-mix(in srgb, var(--dsw-alias-brand-primary, #3e63dd) 55%, transparent)'
27
- const SECONDARY = 'var(--dsw-alias-label-secondary, #888)'
28
- const DIMMED = 'var(--dsw-alias-label-dimmed, #999)'
29
- const MONO = 'ui-monospace, "Cascadia Mono", Consolas, "SF Mono", Menlo, monospace'
30
- const CODE_BG = 'color-mix(in srgb, var(--dsw-alias-label-secondary, #888) 12%, transparent)'
31
- const CODE_BLOCK_BG = 'color-mix(in srgb, var(--dsw-alias-label-secondary, #888) 8%, transparent)'
32
-
33
- /** Markdown token → design-token style map (mirrors the old .md-* palette). */
34
- const mdHighlight = HighlightStyle.define([
35
- // Headings (# … ######) — brand + bold.
36
- { tag: [t.heading1, t.heading2, t.heading3, t.heading4, t.heading5, t.heading6], color: BRAND, fontWeight: '700' },
37
- // Markers: #, >, -, *, `, [ ] etc.
38
- { tag: t.processingInstruction, color: BRAND_DIM },
39
- // Inline code + fenced code text — monospace, tinted background.
40
- { tag: t.monospace, fontFamily: MONO, backgroundColor: CODE_BG, borderRadius: '4px', padding: '0 3px' },
41
- // Code block content (inside a fence) gets a slightly wider tint.
42
- { tag: t.content, fontFamily: MONO, backgroundColor: CODE_BLOCK_BG },
43
- { tag: t.strong, fontWeight: '700' },
44
- { tag: t.emphasis, fontStyle: 'italic' },
45
- { tag: t.strikethrough, textDecoration: 'line-through', opacity: '.75' },
46
- { tag: t.link, color: BRAND, textDecoration: 'underline' },
47
- { tag: t.url, color: BRAND_DIM, textDecoration: 'underline dotted' },
48
- { tag: t.quote, fontStyle: 'italic', color: SECONDARY },
49
- { tag: t.contentSeparator, color: DIMMED, textDecoration: 'line-through' },
50
- { tag: t.comment, color: DIMMED },
51
- ])
52
-
53
- /** Public editor surface the quick-note widget consumes. */
54
- export interface MarkdownEditor {
55
- /** Wrapper element (append/drag-target). */
56
- el: HTMLDivElement
57
- /** The CodeMirror EditorView (advanced use / future extensions). */
58
- view: EditorView
59
- getValue(): string
60
- setValue(value: string): void
61
- /** Insert a Markdown line at the current caret (used by file upload). */
62
- insertAtCaret(markdown: string): void
63
- focus(): void
64
- }
65
-
66
- export interface MarkdownEditorOptions {
67
- /** Placeholder text shown when the doc is empty. */
68
- placeholder?: string
69
- /** Called on Ctrl/Cmd+Enter (wired after save is defined by the widget). */
70
- onSave?: () => void
71
- /** Called after every doc change (used for draft auto-save). */
72
- onChange?: () => void
73
- }
74
-
75
- /**
76
- * Build a CodeMirror 6 Markdown editor inside `.dsh-tw-note-editor`.
77
- * GFM base language (strikethrough, tables, task lists, autolinks) — strictly
78
- * more coverage than the old regex highlighter.
79
- */
80
- export function buildMarkdownEditor(opts: MarkdownEditorOptions = {}): MarkdownEditor {
81
- const wrap = document.createElement('div')
82
- wrap.className = 'dsh-tw-note-editor'
83
-
84
- const view = new EditorView({
85
- parent: wrap,
86
- state: EditorState.create({
87
- doc: '',
88
- extensions: [
89
- EditorView.lineWrapping,
90
- history(),
91
- markdown({ base: markdownLanguage }),
92
- syntaxHighlighting(mdHighlight),
93
- drawSelection(),
94
- placeholder(opts.placeholder ?? ''),
95
- keymap.of([
96
- ...defaultKeymap,
97
- ...historyKeymap,
98
- { key: 'Mod-Enter', run: () => { opts.onSave?.(); return true } },
99
- ]),
100
- EditorView.updateListener.of((update) => {
101
- if (update.docChanged) opts.onChange?.()
102
- }),
103
- ],
104
- }),
105
- })
106
-
107
- return {
108
- el: wrap,
109
- view,
110
- getValue: () => view.state.doc.toString(),
111
- setValue(value: string) {
112
- view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: value } })
113
- },
114
- insertAtCaret(markdownLine: string) {
115
- const { from } = view.state.selection.main
116
- const line = view.state.doc.lineAt(from)
117
- const atLineStart = from === line.from
118
- const insert = `${atLineStart ? '' : '\n'}${markdownLine}\n`
119
- view.dispatch({
120
- changes: { from, insert },
121
- selection: { anchor: from + insert.length },
122
- scrollIntoView: true,
123
- })
124
- view.focus()
125
- },
126
- focus: () => view.focus(),
127
- }
128
- }
1
+ /**
2
+ * CodeMirror 6 Markdown editor for the quick-note widget (replaces the
3
+ * hand-rolled regex highlighter in the old markdown.ts — real Lezer syntax
4
+ * tree, proper GFM coverage, undo/redo, and a native editing surface).
5
+ *
6
+ * The widget stays a plain-DOM product: CodeMirror is framework-agnostic, so
7
+ * no React is pulled in. The editor is built into a wrapper `<div>`
8
+ * (`.dsh-tw-note-editor`) that the widget can append, drag-drop onto, and
9
+ * re-read via `getValue()`/`setValue()`.
10
+ *
11
+ * Theming follows the DSH design tokens (`--dsw-alias-*`) through a
12
+ * `HighlightStyle` that maps Markdown tags onto the same palette the old
13
+ * `.md-*` rules used, so light/dark both look right.
14
+ *
15
+ * @module dsh-tiddlywiki/client/markdown-editor
16
+ */
17
+ import { EditorView, keymap, placeholder, drawSelection } from '@codemirror/view'
18
+ import { EditorState } from '@codemirror/state'
19
+ import { markdown, markdownLanguage } from '@codemirror/lang-markdown'
20
+ import { syntaxHighlighting, HighlightStyle } from '@codemirror/language'
21
+ import { defaultKeymap, history, historyKeymap } from '@codemirror/commands'
22
+ import { tags as t } from '@lezer/highlight'
23
+
24
+ /** Design-token aliases (same values as styles.ts / the old .md-* rules). */
25
+ const BRAND = 'var(--dsw-alias-brand-primary, #3e63dd)'
26
+ const BRAND_DIM = 'color-mix(in srgb, var(--dsw-alias-brand-primary, #3e63dd) 55%, transparent)'
27
+ const SECONDARY = 'var(--dsw-alias-label-secondary, #888)'
28
+ const DIMMED = 'var(--dsw-alias-label-dimmed, #999)'
29
+ const MONO = 'ui-monospace, "Cascadia Mono", Consolas, "SF Mono", Menlo, monospace'
30
+ const CODE_BG = 'color-mix(in srgb, var(--dsw-alias-label-secondary, #888) 12%, transparent)'
31
+ const CODE_BLOCK_BG = 'color-mix(in srgb, var(--dsw-alias-label-secondary, #888) 8%, transparent)'
32
+
33
+ /** Markdown token → design-token style map (mirrors the old .md-* palette). */
34
+ const mdHighlight = HighlightStyle.define([
35
+ // Headings (# … ######) — brand + bold.
36
+ { tag: [t.heading1, t.heading2, t.heading3, t.heading4, t.heading5, t.heading6], color: BRAND, fontWeight: '700' },
37
+ // Markers: #, >, -, *, `, [ ] etc.
38
+ { tag: t.processingInstruction, color: BRAND_DIM },
39
+ // Inline code + fenced code text — monospace, tinted background.
40
+ { tag: t.monospace, fontFamily: MONO, backgroundColor: CODE_BG, borderRadius: '4px', padding: '0 3px' },
41
+ // Code block content (inside a fence) gets a slightly wider tint.
42
+ { tag: t.content, fontFamily: MONO, backgroundColor: CODE_BLOCK_BG },
43
+ { tag: t.strong, fontWeight: '700' },
44
+ { tag: t.emphasis, fontStyle: 'italic' },
45
+ { tag: t.strikethrough, textDecoration: 'line-through', opacity: '.75' },
46
+ { tag: t.link, color: BRAND, textDecoration: 'underline' },
47
+ { tag: t.url, color: BRAND_DIM, textDecoration: 'underline dotted' },
48
+ { tag: t.quote, fontStyle: 'italic', color: SECONDARY },
49
+ { tag: t.contentSeparator, color: DIMMED, textDecoration: 'line-through' },
50
+ { tag: t.comment, color: DIMMED },
51
+ ])
52
+
53
+ /** Public editor surface the quick-note widget consumes. */
54
+ export interface MarkdownEditor {
55
+ /** Wrapper element (append/drag-target). */
56
+ el: HTMLDivElement
57
+ /** The CodeMirror EditorView (advanced use / future extensions). */
58
+ view: EditorView
59
+ getValue(): string
60
+ setValue(value: string): void
61
+ /** Insert a Markdown line at the current caret (used by file upload). */
62
+ insertAtCaret(markdown: string): void
63
+ focus(): void
64
+ }
65
+
66
+ export interface MarkdownEditorOptions {
67
+ /** Placeholder text shown when the doc is empty. */
68
+ placeholder?: string
69
+ /** Called on Ctrl/Cmd+Enter (wired after save is defined by the widget). */
70
+ onSave?: () => void
71
+ /** Called after every doc change (used for draft auto-save). */
72
+ onChange?: () => void
73
+ }
74
+
75
+ /**
76
+ * Build a CodeMirror 6 Markdown editor inside `.dsh-tw-note-editor`.
77
+ * GFM base language (strikethrough, tables, task lists, autolinks) — strictly
78
+ * more coverage than the old regex highlighter.
79
+ */
80
+ export function buildMarkdownEditor(opts: MarkdownEditorOptions = {}): MarkdownEditor {
81
+ const wrap = document.createElement('div')
82
+ wrap.className = 'dsh-tw-note-editor'
83
+
84
+ const view = new EditorView({
85
+ parent: wrap,
86
+ state: EditorState.create({
87
+ doc: '',
88
+ extensions: [
89
+ EditorView.lineWrapping,
90
+ history(),
91
+ markdown({ base: markdownLanguage }),
92
+ syntaxHighlighting(mdHighlight),
93
+ drawSelection(),
94
+ placeholder(opts.placeholder ?? ''),
95
+ keymap.of([
96
+ ...defaultKeymap,
97
+ ...historyKeymap,
98
+ { key: 'Mod-Enter', run: () => { opts.onSave?.(); return true } },
99
+ ]),
100
+ EditorView.updateListener.of((update) => {
101
+ if (update.docChanged) opts.onChange?.()
102
+ }),
103
+ ],
104
+ }),
105
+ })
106
+
107
+ return {
108
+ el: wrap,
109
+ view,
110
+ getValue: () => view.state.doc.toString(),
111
+ setValue(value: string) {
112
+ view.dispatch({ changes: { from: 0, to: view.state.doc.length, insert: value } })
113
+ },
114
+ insertAtCaret(markdownLine: string) {
115
+ const { from } = view.state.selection.main
116
+ const line = view.state.doc.lineAt(from)
117
+ const atLineStart = from === line.from
118
+ const insert = `${atLineStart ? '' : '\n'}${markdownLine}\n`
119
+ view.dispatch({
120
+ changes: { from, insert },
121
+ selection: { anchor: from + insert.length },
122
+ scrollIntoView: true,
123
+ })
124
+ view.focus()
125
+ },
126
+ focus: () => view.focus(),
127
+ }
128
+ }
@@ -23,6 +23,7 @@
23
23
  import type { PanelState } from './state.ts'
24
24
  import { ENTRY_SELECTOR } from './sidebar-entry.ts'
25
25
  import { attachThemeSync, setThemeSyncConfig } from './theme-sync.ts'
26
+ import { openTiddlerInRightbar } from './rightbar-tab.ts'
26
27
 
27
28
  export const PANEL_RELOAD_EVENT = 'dsh-tw-panel-reload'
28
29
 
@@ -316,6 +317,9 @@ export function mountPanel(state: PanelState): () => void {
316
317
  const detail = (event as CustomEvent).detail as { title?: unknown } | undefined
317
318
  const title = typeof detail?.title === 'string' && detail.title.length > 0 ? detail.title : ''
318
319
  if (title.length === 0) return
320
+ // 右侧栏的 TW tab 可见时,链接直接在那里打开(与聊天并排);否则退回
321
+ // 中央面板。互斥由 rightbar-tab 的 dsh-panel-activate 协议保证。
322
+ if (openTiddlerInRightbar(title)) return
319
323
  pendingHash = `#${encodeURIComponent(title)}`
320
324
  state.openPanel()
321
325
  applyPendingHash()