dsh-tiddlywiki 0.1.0 → 0.1.2
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 +203 -172
- package/cordis.patch.yml +13 -13
- package/lib/client.bundle.js +46 -1
- package/lib/client.js +46 -1
- package/lib/index.js +2152 -2152
- package/package.json +79 -63
- package/src/client/editor-popup.ts +121 -121
- package/src/client/index.ts +76 -76
- package/src/client/note-widget.ts +210 -210
- package/src/client/panel.ts +345 -304
- package/src/client/settings-page.ts +397 -397
- package/src/client/sidebar-entry.ts +148 -148
- package/src/client/state.ts +39 -39
- package/src/client/styles.ts +242 -235
- package/src/client/toast.ts +22 -22
- package/src/host/admin.ts +408 -408
- package/src/host/config.ts +86 -86
- package/src/host/git.ts +218 -218
- package/src/host/routes.ts +233 -233
- package/src/host/seed-notes.ts +62 -62
- package/src/host/tools.ts +254 -254
- package/src/host/tw-api.ts +157 -157
- package/src/host/wiki.ts +287 -287
- package/src/index.ts +331 -331
- package/src/sdk.ts +198 -198
package/package.json
CHANGED
|
@@ -1,63 +1,79 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dsh-tiddlywiki",
|
|
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.1.
|
|
5
|
-
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
"lib
|
|
25
|
-
"lib
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-tiddlywiki",
|
|
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.1.2",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"dsh",
|
|
8
|
+
"dsh-plugin",
|
|
9
|
+
"tiddlywiki",
|
|
10
|
+
"knowledge-base",
|
|
11
|
+
"note-taking",
|
|
12
|
+
"notes",
|
|
13
|
+
"wiki",
|
|
14
|
+
"git-sync",
|
|
15
|
+
"agent-tools",
|
|
16
|
+
"plugin"
|
|
17
|
+
],
|
|
18
|
+
"author": {
|
|
19
|
+
"name": "bbqisbbq",
|
|
20
|
+
"url": "https://github.com/bbqisbbq"
|
|
21
|
+
},
|
|
22
|
+
"main": "lib/index.js",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": "./lib/index.js",
|
|
25
|
+
"./client": "./lib/client.js",
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"dsh": {
|
|
29
|
+
"bundle": {
|
|
30
|
+
"patch": "./cordis.patch.yml"
|
|
31
|
+
},
|
|
32
|
+
"client": {
|
|
33
|
+
"inject": [
|
|
34
|
+
"slots"
|
|
35
|
+
],
|
|
36
|
+
"platform": "web"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"lib/**/*.js",
|
|
41
|
+
"lib/**/*.js.map",
|
|
42
|
+
"src",
|
|
43
|
+
"cordis.patch.yml",
|
|
44
|
+
"LICENSE",
|
|
45
|
+
"README.md"
|
|
46
|
+
],
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"homepage": "https://github.com/bbqisbbq/dsh-tiddlywiki#readme",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "https://github.com/bbqisbbq/dsh-tiddlywiki.git"
|
|
52
|
+
},
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/bbqisbbq/dsh-tiddlywiki/issues"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=22"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"tiddlywiki": "^5.4.1"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "^22.20.1",
|
|
64
|
+
"@types/react": "^19.1.0",
|
|
65
|
+
"react": "^19.1.0",
|
|
66
|
+
"tsdown": "0.22.2",
|
|
67
|
+
"tsx": "^4.19.2",
|
|
68
|
+
"typescript": "~5.7.2",
|
|
69
|
+
"unrun": "*"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"build": "node scripts/clean-lib.mjs && npm run build:host && npm run build:client",
|
|
73
|
+
"build:host": "tsdown -c tsdown.host.config.ts",
|
|
74
|
+
"build:client": "tsdown -c tsdown.client.config.ts && node scripts/wrap-client.mjs",
|
|
75
|
+
"watch": "tsdown -c tsdown.host.config.ts --watch",
|
|
76
|
+
"typecheck": "tsc --noEmit",
|
|
77
|
+
"selftest": "node scripts/selftest.mjs"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -1,121 +1,121 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Floating popup iframe that loads TiddlyWiki's NATIVE editor for a draft
|
|
3
|
-
* (quick-note "✏️ 在 TW 中编辑"). A small draggable + resizable overlay with its
|
|
4
|
-
* own iframe pointed at `twUrl#<draftTitle>`: the fragment-only navigation
|
|
5
|
-
* triggers TW's hashchange, which opens the draft in the story, and because the
|
|
6
|
-
* draft tiddler carries `draft.of` the story renders the native EditTemplate.
|
|
7
|
-
*
|
|
8
|
-
* Independent of the center panel — a separate floating window so the user can
|
|
9
|
-
* edit a note without leaving the chat context.
|
|
10
|
-
*
|
|
11
|
-
* @module dsh-tiddlywiki/client/editor-popup
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
let root: HTMLDivElement | undefined
|
|
15
|
-
let frame: HTMLIFrameElement | undefined
|
|
16
|
-
let titleEl: HTMLSpanElement | undefined
|
|
17
|
-
|
|
18
|
-
/** Open (create on first use) the popup and load `url` (twUrl#draftTitle). */
|
|
19
|
-
export function openEditorPopup(url: string, label: string): void {
|
|
20
|
-
ensurePopup()
|
|
21
|
-
if (root === undefined || frame === undefined) return
|
|
22
|
-
if (titleEl !== undefined) titleEl.textContent = `TiddlyWiki 编辑器 · ${label}`
|
|
23
|
-
root.style.display = ''
|
|
24
|
-
// Same-base fragment navigation reloads nothing; a changed base (restart on a
|
|
25
|
-
// new port) reloads the app and TW still opens the draft from the hash.
|
|
26
|
-
frame.src = url
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Remove the popup DOM entirely (plugin dispose). */
|
|
30
|
-
export function disposeEditorPopup(): void {
|
|
31
|
-
root?.remove()
|
|
32
|
-
root = undefined
|
|
33
|
-
frame = undefined
|
|
34
|
-
titleEl = undefined
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function ensurePopup(): void {
|
|
38
|
-
if (root !== undefined && frame !== undefined) return
|
|
39
|
-
|
|
40
|
-
root = document.createElement('div')
|
|
41
|
-
root.className = 'dsh-tw-editor-popup'
|
|
42
|
-
root.style.display = 'none'
|
|
43
|
-
|
|
44
|
-
const bar = document.createElement('div')
|
|
45
|
-
bar.className = 'dsh-tw-editor-bar'
|
|
46
|
-
titleEl = document.createElement('span')
|
|
47
|
-
titleEl.className = 'dsh-tw-editor-title'
|
|
48
|
-
titleEl.textContent = 'TiddlyWiki 编辑器'
|
|
49
|
-
const close = document.createElement('button')
|
|
50
|
-
close.type = 'button'
|
|
51
|
-
close.className = 'dsh-tw-editor-close'
|
|
52
|
-
close.textContent = '✕'
|
|
53
|
-
close.title = '关闭'
|
|
54
|
-
bar.append(titleEl, close)
|
|
55
|
-
|
|
56
|
-
frame = document.createElement('iframe')
|
|
57
|
-
frame.className = 'dsh-tw-editor-frame'
|
|
58
|
-
frame.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups')
|
|
59
|
-
frame.title = 'TiddlyWiki 编辑器'
|
|
60
|
-
|
|
61
|
-
const resize = document.createElement('div')
|
|
62
|
-
resize.className = 'dsh-tw-editor-resize'
|
|
63
|
-
resize.title = '拖拽调整大小'
|
|
64
|
-
|
|
65
|
-
root.append(bar, frame, resize)
|
|
66
|
-
document.body.append(root)
|
|
67
|
-
|
|
68
|
-
close.addEventListener('click', () => {
|
|
69
|
-
if (root !== undefined) root.style.display = 'none'
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
// Drag by the title bar (un-center by setting explicit left/top + margin 0).
|
|
73
|
-
bar.addEventListener('mousedown', (event) => {
|
|
74
|
-
if (event.button !== 0 || root === undefined) return
|
|
75
|
-
event.preventDefault()
|
|
76
|
-
const rect = root.getBoundingClientRect()
|
|
77
|
-
const startX = event.clientX
|
|
78
|
-
const startY = event.clientY
|
|
79
|
-
const baseLeft = rect.left
|
|
80
|
-
const baseTop = rect.top
|
|
81
|
-
const onMove = (ev: MouseEvent): void => {
|
|
82
|
-
if (root === undefined) return
|
|
83
|
-
root.style.left = `${baseLeft + ev.clientX - startX}px`
|
|
84
|
-
root.style.top = `${baseTop + ev.clientY - startY}px`
|
|
85
|
-
root.style.margin = '0'
|
|
86
|
-
root.style.right = 'auto'
|
|
87
|
-
root.style.bottom = 'auto'
|
|
88
|
-
}
|
|
89
|
-
const onUp = (): void => {
|
|
90
|
-
window.removeEventListener('mousemove', onMove)
|
|
91
|
-
window.removeEventListener('mouseup', onUp)
|
|
92
|
-
}
|
|
93
|
-
window.addEventListener('mousemove', onMove)
|
|
94
|
-
window.addEventListener('mouseup', onUp)
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
// Resize from the bottom-right corner.
|
|
98
|
-
resize.addEventListener('mousedown', (event) => {
|
|
99
|
-
if (event.button !== 0 || root === undefined) return
|
|
100
|
-
event.preventDefault()
|
|
101
|
-
event.stopPropagation()
|
|
102
|
-
const rect = root.getBoundingClientRect()
|
|
103
|
-
const startX = event.clientX
|
|
104
|
-
const startY = event.clientY
|
|
105
|
-
const baseW = rect.width
|
|
106
|
-
const baseH = rect.height
|
|
107
|
-
const onMove = (ev: MouseEvent): void => {
|
|
108
|
-
if (root === undefined) return
|
|
109
|
-
root.style.width = `${Math.max(360, baseW + ev.clientX - startX)}px`
|
|
110
|
-
root.style.height = `${Math.max(260, baseH + ev.clientY - startY)}px`
|
|
111
|
-
root.style.right = 'auto'
|
|
112
|
-
root.style.bottom = 'auto'
|
|
113
|
-
}
|
|
114
|
-
const onUp = (): void => {
|
|
115
|
-
window.removeEventListener('mousemove', onMove)
|
|
116
|
-
window.removeEventListener('mouseup', onUp)
|
|
117
|
-
}
|
|
118
|
-
window.addEventListener('mousemove', onMove)
|
|
119
|
-
window.addEventListener('mouseup', onUp)
|
|
120
|
-
})
|
|
121
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Floating popup iframe that loads TiddlyWiki's NATIVE editor for a draft
|
|
3
|
+
* (quick-note "✏️ 在 TW 中编辑"). A small draggable + resizable overlay with its
|
|
4
|
+
* own iframe pointed at `twUrl#<draftTitle>`: the fragment-only navigation
|
|
5
|
+
* triggers TW's hashchange, which opens the draft in the story, and because the
|
|
6
|
+
* draft tiddler carries `draft.of` the story renders the native EditTemplate.
|
|
7
|
+
*
|
|
8
|
+
* Independent of the center panel — a separate floating window so the user can
|
|
9
|
+
* edit a note without leaving the chat context.
|
|
10
|
+
*
|
|
11
|
+
* @module dsh-tiddlywiki/client/editor-popup
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
let root: HTMLDivElement | undefined
|
|
15
|
+
let frame: HTMLIFrameElement | undefined
|
|
16
|
+
let titleEl: HTMLSpanElement | undefined
|
|
17
|
+
|
|
18
|
+
/** Open (create on first use) the popup and load `url` (twUrl#draftTitle). */
|
|
19
|
+
export function openEditorPopup(url: string, label: string): void {
|
|
20
|
+
ensurePopup()
|
|
21
|
+
if (root === undefined || frame === undefined) return
|
|
22
|
+
if (titleEl !== undefined) titleEl.textContent = `TiddlyWiki 编辑器 · ${label}`
|
|
23
|
+
root.style.display = ''
|
|
24
|
+
// Same-base fragment navigation reloads nothing; a changed base (restart on a
|
|
25
|
+
// new port) reloads the app and TW still opens the draft from the hash.
|
|
26
|
+
frame.src = url
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Remove the popup DOM entirely (plugin dispose). */
|
|
30
|
+
export function disposeEditorPopup(): void {
|
|
31
|
+
root?.remove()
|
|
32
|
+
root = undefined
|
|
33
|
+
frame = undefined
|
|
34
|
+
titleEl = undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function ensurePopup(): void {
|
|
38
|
+
if (root !== undefined && frame !== undefined) return
|
|
39
|
+
|
|
40
|
+
root = document.createElement('div')
|
|
41
|
+
root.className = 'dsh-tw-editor-popup'
|
|
42
|
+
root.style.display = 'none'
|
|
43
|
+
|
|
44
|
+
const bar = document.createElement('div')
|
|
45
|
+
bar.className = 'dsh-tw-editor-bar'
|
|
46
|
+
titleEl = document.createElement('span')
|
|
47
|
+
titleEl.className = 'dsh-tw-editor-title'
|
|
48
|
+
titleEl.textContent = 'TiddlyWiki 编辑器'
|
|
49
|
+
const close = document.createElement('button')
|
|
50
|
+
close.type = 'button'
|
|
51
|
+
close.className = 'dsh-tw-editor-close'
|
|
52
|
+
close.textContent = '✕'
|
|
53
|
+
close.title = '关闭'
|
|
54
|
+
bar.append(titleEl, close)
|
|
55
|
+
|
|
56
|
+
frame = document.createElement('iframe')
|
|
57
|
+
frame.className = 'dsh-tw-editor-frame'
|
|
58
|
+
frame.setAttribute('sandbox', 'allow-scripts allow-same-origin allow-forms allow-popups')
|
|
59
|
+
frame.title = 'TiddlyWiki 编辑器'
|
|
60
|
+
|
|
61
|
+
const resize = document.createElement('div')
|
|
62
|
+
resize.className = 'dsh-tw-editor-resize'
|
|
63
|
+
resize.title = '拖拽调整大小'
|
|
64
|
+
|
|
65
|
+
root.append(bar, frame, resize)
|
|
66
|
+
document.body.append(root)
|
|
67
|
+
|
|
68
|
+
close.addEventListener('click', () => {
|
|
69
|
+
if (root !== undefined) root.style.display = 'none'
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Drag by the title bar (un-center by setting explicit left/top + margin 0).
|
|
73
|
+
bar.addEventListener('mousedown', (event) => {
|
|
74
|
+
if (event.button !== 0 || root === undefined) return
|
|
75
|
+
event.preventDefault()
|
|
76
|
+
const rect = root.getBoundingClientRect()
|
|
77
|
+
const startX = event.clientX
|
|
78
|
+
const startY = event.clientY
|
|
79
|
+
const baseLeft = rect.left
|
|
80
|
+
const baseTop = rect.top
|
|
81
|
+
const onMove = (ev: MouseEvent): void => {
|
|
82
|
+
if (root === undefined) return
|
|
83
|
+
root.style.left = `${baseLeft + ev.clientX - startX}px`
|
|
84
|
+
root.style.top = `${baseTop + ev.clientY - startY}px`
|
|
85
|
+
root.style.margin = '0'
|
|
86
|
+
root.style.right = 'auto'
|
|
87
|
+
root.style.bottom = 'auto'
|
|
88
|
+
}
|
|
89
|
+
const onUp = (): void => {
|
|
90
|
+
window.removeEventListener('mousemove', onMove)
|
|
91
|
+
window.removeEventListener('mouseup', onUp)
|
|
92
|
+
}
|
|
93
|
+
window.addEventListener('mousemove', onMove)
|
|
94
|
+
window.addEventListener('mouseup', onUp)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
// Resize from the bottom-right corner.
|
|
98
|
+
resize.addEventListener('mousedown', (event) => {
|
|
99
|
+
if (event.button !== 0 || root === undefined) return
|
|
100
|
+
event.preventDefault()
|
|
101
|
+
event.stopPropagation()
|
|
102
|
+
const rect = root.getBoundingClientRect()
|
|
103
|
+
const startX = event.clientX
|
|
104
|
+
const startY = event.clientY
|
|
105
|
+
const baseW = rect.width
|
|
106
|
+
const baseH = rect.height
|
|
107
|
+
const onMove = (ev: MouseEvent): void => {
|
|
108
|
+
if (root === undefined) return
|
|
109
|
+
root.style.width = `${Math.max(360, baseW + ev.clientX - startX)}px`
|
|
110
|
+
root.style.height = `${Math.max(260, baseH + ev.clientY - startY)}px`
|
|
111
|
+
root.style.right = 'auto'
|
|
112
|
+
root.style.bottom = 'auto'
|
|
113
|
+
}
|
|
114
|
+
const onUp = (): void => {
|
|
115
|
+
window.removeEventListener('mousemove', onMove)
|
|
116
|
+
window.removeEventListener('mouseup', onUp)
|
|
117
|
+
}
|
|
118
|
+
window.addEventListener('mousemove', onMove)
|
|
119
|
+
window.addEventListener('mouseup', onUp)
|
|
120
|
+
})
|
|
121
|
+
}
|
package/src/client/index.ts
CHANGED
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Browser half entry for dsh-tiddlywiki (design doc §12, D5): injects the
|
|
3
|
-
* stylesheet, mounts the sidebar entry, the center-column TiddlyWiki panel,
|
|
4
|
-
* the floating quick-note widget, and registers the plugin's settings page
|
|
5
|
-
* (config panel, §13) into the shell's Settings.
|
|
6
|
-
*
|
|
7
|
-
* Failure policy: DOM mounting problems are logged, never thrown — the web
|
|
8
|
-
* shell fails the whole boot when a plugin apply throws.
|
|
9
|
-
*
|
|
10
|
-
* Export shape: `name` / `inject` / `apply`, no default.
|
|
11
|
-
*
|
|
12
|
-
* @module dsh-tiddlywiki/client
|
|
13
|
-
*/
|
|
14
|
-
import { injectStyles } from './styles.ts'
|
|
15
|
-
import { PanelState } from './state.ts'
|
|
16
|
-
import { mountSidebarEntry } from './sidebar-entry.ts'
|
|
17
|
-
import { mountPanel } from './panel.ts'
|
|
18
|
-
import { mountNoteWidget } from './note-widget.ts'
|
|
19
|
-
import { disposeEditorPopup } from './editor-popup.ts'
|
|
20
|
-
import { SettingsSection } from './settings-page.ts'
|
|
21
|
-
|
|
22
|
-
/** Client plugin name. */
|
|
23
|
-
export const name = 'dsh-tiddlywiki/client'
|
|
24
|
-
|
|
25
|
-
/** Required client services: the slots registry (settings.section seat). */
|
|
26
|
-
export const inject: string[] = ['slots']
|
|
27
|
-
|
|
28
|
-
/** Effect-hook face the runner provides on the client context. */
|
|
29
|
-
interface ClientContextFace {
|
|
30
|
-
slots?: {
|
|
31
|
-
inject(name: string, register: () => unknown): (() => void) | undefined
|
|
32
|
-
register(
|
|
33
|
-
opts: { name: string; id: string; order?: number; label?: string | (() => string) },
|
|
34
|
-
component: unknown,
|
|
35
|
-
): () => void
|
|
36
|
-
}
|
|
37
|
-
effect?(fn: () => unknown, label?: string): void
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Client entry: installs styles and mounts the DOM seats + settings page.
|
|
42
|
-
* @param ctx - the cordis client context.
|
|
43
|
-
*/
|
|
44
|
-
export function apply(ctx: ClientContextFace): void {
|
|
45
|
-
try {
|
|
46
|
-
injectStyles()
|
|
47
|
-
const state = new PanelState()
|
|
48
|
-
const disposers: Array<() => void> = []
|
|
49
|
-
try {
|
|
50
|
-
disposers.push(mountSidebarEntry(state))
|
|
51
|
-
disposers.push(mountPanel(state))
|
|
52
|
-
disposers.push(mountNoteWidget())
|
|
53
|
-
disposers.push(disposeEditorPopup)
|
|
54
|
-
} catch (error) {
|
|
55
|
-
// DOM failures degrade the plugin, never the GUI.
|
|
56
|
-
console.error('[dsh-tiddlywiki] mount failed:', error)
|
|
57
|
-
}
|
|
58
|
-
try {
|
|
59
|
-
// Settings page → Settings → 「TiddlyWiki 知识库」(config panel §13).
|
|
60
|
-
const removeSettings = ctx.slots?.inject('settings.section', () =>
|
|
61
|
-
ctx.slots?.register(
|
|
62
|
-
{ name: 'settings.section', id: 'dsh-tiddlywiki', order: 50, label: 'TiddlyWiki 知识库' },
|
|
63
|
-
SettingsSection,
|
|
64
|
-
),
|
|
65
|
-
)
|
|
66
|
-
if (removeSettings !== undefined) disposers.push(removeSettings)
|
|
67
|
-
} catch (error) {
|
|
68
|
-
console.error('[dsh-tiddlywiki] settings section failed:', error)
|
|
69
|
-
}
|
|
70
|
-
ctx.effect?.(() => () => {
|
|
71
|
-
for (const dispose of disposers.splice(0)) dispose()
|
|
72
|
-
}, 'dsh-tiddlywiki: client mount')
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.error('[dsh-tiddlywiki] client half failed to start:', error)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Browser half entry for dsh-tiddlywiki (design doc §12, D5): injects the
|
|
3
|
+
* stylesheet, mounts the sidebar entry, the center-column TiddlyWiki panel,
|
|
4
|
+
* the floating quick-note widget, and registers the plugin's settings page
|
|
5
|
+
* (config panel, §13) into the shell's Settings.
|
|
6
|
+
*
|
|
7
|
+
* Failure policy: DOM mounting problems are logged, never thrown — the web
|
|
8
|
+
* shell fails the whole boot when a plugin apply throws.
|
|
9
|
+
*
|
|
10
|
+
* Export shape: `name` / `inject` / `apply`, no default.
|
|
11
|
+
*
|
|
12
|
+
* @module dsh-tiddlywiki/client
|
|
13
|
+
*/
|
|
14
|
+
import { injectStyles } from './styles.ts'
|
|
15
|
+
import { PanelState } from './state.ts'
|
|
16
|
+
import { mountSidebarEntry } from './sidebar-entry.ts'
|
|
17
|
+
import { mountPanel } from './panel.ts'
|
|
18
|
+
import { mountNoteWidget } from './note-widget.ts'
|
|
19
|
+
import { disposeEditorPopup } from './editor-popup.ts'
|
|
20
|
+
import { SettingsSection } from './settings-page.ts'
|
|
21
|
+
|
|
22
|
+
/** Client plugin name. */
|
|
23
|
+
export const name = 'dsh-tiddlywiki/client'
|
|
24
|
+
|
|
25
|
+
/** Required client services: the slots registry (settings.section seat). */
|
|
26
|
+
export const inject: string[] = ['slots']
|
|
27
|
+
|
|
28
|
+
/** Effect-hook face the runner provides on the client context. */
|
|
29
|
+
interface ClientContextFace {
|
|
30
|
+
slots?: {
|
|
31
|
+
inject(name: string, register: () => unknown): (() => void) | undefined
|
|
32
|
+
register(
|
|
33
|
+
opts: { name: string; id: string; order?: number; label?: string | (() => string) },
|
|
34
|
+
component: unknown,
|
|
35
|
+
): () => void
|
|
36
|
+
}
|
|
37
|
+
effect?(fn: () => unknown, label?: string): void
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Client entry: installs styles and mounts the DOM seats + settings page.
|
|
42
|
+
* @param ctx - the cordis client context.
|
|
43
|
+
*/
|
|
44
|
+
export function apply(ctx: ClientContextFace): void {
|
|
45
|
+
try {
|
|
46
|
+
injectStyles()
|
|
47
|
+
const state = new PanelState()
|
|
48
|
+
const disposers: Array<() => void> = []
|
|
49
|
+
try {
|
|
50
|
+
disposers.push(mountSidebarEntry(state))
|
|
51
|
+
disposers.push(mountPanel(state))
|
|
52
|
+
disposers.push(mountNoteWidget())
|
|
53
|
+
disposers.push(disposeEditorPopup)
|
|
54
|
+
} catch (error) {
|
|
55
|
+
// DOM failures degrade the plugin, never the GUI.
|
|
56
|
+
console.error('[dsh-tiddlywiki] mount failed:', error)
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
// Settings page → Settings → 「TiddlyWiki 知识库」(config panel §13).
|
|
60
|
+
const removeSettings = ctx.slots?.inject('settings.section', () =>
|
|
61
|
+
ctx.slots?.register(
|
|
62
|
+
{ name: 'settings.section', id: 'dsh-tiddlywiki', order: 50, label: 'TiddlyWiki 知识库' },
|
|
63
|
+
SettingsSection,
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
if (removeSettings !== undefined) disposers.push(removeSettings)
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error('[dsh-tiddlywiki] settings section failed:', error)
|
|
69
|
+
}
|
|
70
|
+
ctx.effect?.(() => () => {
|
|
71
|
+
for (const dispose of disposers.splice(0)) dispose()
|
|
72
|
+
}, 'dsh-tiddlywiki: client mount')
|
|
73
|
+
} catch (error) {
|
|
74
|
+
console.error('[dsh-tiddlywiki] client half failed to start:', error)
|
|
75
|
+
}
|
|
76
|
+
}
|