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/LICENSE +21 -21
- package/README.md +8 -1
- package/cordis.patch.yml +13 -13
- package/lib/client.bundle.js +53 -26
- package/lib/client.js +53 -26
- package/lib/index.js +136 -25
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/endpoints.ts +10 -10
- package/src/client/index.ts +36 -0
- package/src/client/markdown-editor.ts +128 -128
- package/src/client/panel.ts +4 -0
- package/src/client/rightbar-tab.ts +440 -0
- package/src/client/session-summary.ts +279 -279
- package/src/client/settings-page.ts +1 -0
- package/src/client/state.ts +39 -39
- package/src/client/styles.ts +27 -0
- package/src/client/theme-sync.ts +312 -312
- package/src/client/toast.ts +22 -22
- package/src/client/tool-views.ts +604 -604
- package/src/client/ui-config.ts +64 -61
- package/src/host/config.ts +7 -0
- package/src/host/http.ts +57 -57
- package/src/host/routes.ts +2 -0
- package/src/host/seed-home.ts +1 -1
- package/src/host/seed-render.ts +66 -66
- package/src/host/seed-send-to-agent.ts +63 -63
- package/src/host/tools.ts +40 -5
- package/src/host/tw-api.ts +108 -16
- package/src/host/wiki.ts +299 -299
- package/src/index.ts +9 -8
- package/src/sdk.ts +204 -204
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.
|
|
4
|
+
"version": "0.16.21",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"dsh",
|
package/src/client/endpoints.ts
CHANGED
|
@@ -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'
|
package/src/client/index.ts
CHANGED
|
@@ -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
|
+
}
|
package/src/client/panel.ts
CHANGED
|
@@ -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()
|