dsh-tiddlywiki 0.16.12 → 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/README.md +1 -0
- package/lib/client.bundle.js +1 -1
- package/lib/client.js +1 -1
- package/lib/index.js +15 -1
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/endpoints.ts +1 -0
- package/src/client/session-summary.ts +28 -0
- package/src/host/session-summary.ts +18 -1
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.13",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"dsh",
|
package/src/client/endpoints.ts
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import * as React from 'react'
|
|
20
20
|
import { attachThemeSync } from './theme-sync.ts'
|
|
21
|
+
import { GET_ENDPOINT } from './endpoints.ts'
|
|
21
22
|
|
|
22
23
|
export const SESSION_SUMMARY_VIEW_ID = 'dsh-tiddlywiki-summary'
|
|
23
24
|
const SUMMARY_ENDPOINT = '/dsh-tiddlywiki/session/summary'
|
|
@@ -49,6 +50,7 @@ function SessionSummaryView(props: SessionSummaryViewProps): React.ReactElement
|
|
|
49
50
|
const { sessionId, viewRequest, completeViewRequest } = props
|
|
50
51
|
const [phase, setPhase] = React.useState<'loading' | 'ready' | 'error'>('loading')
|
|
51
52
|
const [twUrl, setTwUrl] = React.useState<string | null>(null)
|
|
53
|
+
const [summaryTitle, setSummaryTitle] = React.useState<string | null>(null)
|
|
52
54
|
const [error, setError] = React.useState('')
|
|
53
55
|
const [frameNonce, setFrameNonce] = React.useState(0)
|
|
54
56
|
const genRef = React.useRef(0)
|
|
@@ -77,6 +79,7 @@ function SessionSummaryView(props: SessionSummaryViewProps): React.ReactElement
|
|
|
77
79
|
return
|
|
78
80
|
}
|
|
79
81
|
if (typeof data.title === 'string' && data.title.length > 0) {
|
|
82
|
+
setSummaryTitle(data.title)
|
|
80
83
|
setTwUrl(new URL(TW_PROXY_BASE, window.location.origin).href + '#' + encodeURIComponent(data.title))
|
|
81
84
|
// 新 nonce 强制 iframe 重挂载 → 用最新汇总重新加载(含刷新场景)。
|
|
82
85
|
setFrameNonce((n) => n + 1)
|
|
@@ -94,6 +97,31 @@ function SessionSummaryView(props: SessionSummaryViewProps): React.ReactElement
|
|
|
94
97
|
void generate()
|
|
95
98
|
}, [generate])
|
|
96
99
|
|
|
100
|
+
// 自愈:汇总条目是 volatile 的 `$:/temp`,TW 重启即消失;若 iframe 已就绪但条目
|
|
101
|
+
// 被清掉(/get 404),自动重新生成,避免停在 TW 的「佚失条目」页。连续 3 次仍
|
|
102
|
+
// 缺失(例如服务端写不进去)就停止自动重试,交还手动「🔄 刷新」。
|
|
103
|
+
React.useEffect(() => {
|
|
104
|
+
if (phase !== 'ready' || summaryTitle === null) return
|
|
105
|
+
let misses = 0
|
|
106
|
+
const timer = window.setInterval(() => {
|
|
107
|
+
void (async () => {
|
|
108
|
+
try {
|
|
109
|
+
const res = await fetch(`${GET_ENDPOINT}?title=${encodeURIComponent(summaryTitle)}`, { signal: AbortSignal.timeout(8_000) })
|
|
110
|
+
const data = (await res.json().catch(() => null)) as { notFound?: boolean } | null
|
|
111
|
+
if (res.status === 404 || data?.notFound === true) {
|
|
112
|
+
misses++
|
|
113
|
+
if (misses < 3) void generate()
|
|
114
|
+
} else {
|
|
115
|
+
misses = 0
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
/* 检查失败保持现状,下个周期再试 */
|
|
119
|
+
}
|
|
120
|
+
})()
|
|
121
|
+
}, 30_000)
|
|
122
|
+
return () => window.clearInterval(timer)
|
|
123
|
+
}, [phase, summaryTitle, generate])
|
|
124
|
+
|
|
97
125
|
// 一次性 focus 请求直接确认(本视图无可聚焦子目标,避免 shell 挂起)。
|
|
98
126
|
React.useEffect(() => {
|
|
99
127
|
if (viewRequest !== null && viewRequest !== undefined) completeViewRequest?.()
|
|
@@ -105,9 +105,24 @@ function collectDescendantIds(nodes: SessionLineageNode[] | undefined, out: stri
|
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* 汇总条目会直接写进 `[[标题]]` wikitext——标题既要能安全渲染、又不能制造死链。
|
|
110
|
+
* 会话日志里常混入模型的示例/残缺写法(`#…`、`#标题`、带反引号的截断链接、
|
|
111
|
+
* 控制字符等),这类收进来会被 TW 渲染成「佚失条目」;一律过滤。
|
|
112
|
+
*/
|
|
113
|
+
function isPlausibleTitle(title: string): boolean {
|
|
114
|
+
if (title.length === 0 || title.length > 300) return false
|
|
115
|
+
if (title.trim().length === 0) return false
|
|
116
|
+
if (/[\u0000-\u001f\u007f]/.test(title)) return false // 控制字符 / 换行
|
|
117
|
+
if (/[\]|`#]/.test(title)) return false // 会破坏 [[…]] 链接语法
|
|
118
|
+
if (title.startsWith('…') || title === '标题') return false // 占位 / 示例写法
|
|
119
|
+
return true
|
|
120
|
+
}
|
|
121
|
+
|
|
108
122
|
/** 记录一篇笔记的触达;同标题合并(时间取最新、detail 取最新、subagent 取 AND)。 */
|
|
109
123
|
function record(map: Map<string, NoteEntry>, entry: NoteEntry): void {
|
|
110
124
|
if (entry.title.startsWith('$:/')) return
|
|
125
|
+
if (!isPlausibleTitle(entry.title)) return
|
|
111
126
|
const prev = map.get(entry.title)
|
|
112
127
|
if (prev !== undefined) {
|
|
113
128
|
prev.time = Math.max(prev.time, entry.time)
|
|
@@ -120,7 +135,9 @@ function record(map: Map<string, NoteEntry>, entry: NoteEntry): void {
|
|
|
120
135
|
|
|
121
136
|
/** 扫助手回复文本里的 `/dsh-tiddlywiki/tw/#标题` 引用链接 → 读取。 */
|
|
122
137
|
function scanRefs(text: string, time: number, subagent: boolean, collected: Collected): void {
|
|
123
|
-
|
|
138
|
+
// 只认到下一个 `)` / 空白 / `]` / `#` 为止——`#` 会截断后续碎片(模型示例常写成
|
|
139
|
+
// `#…` 或 `#标题`,这些由 isPlausibleTitle 在 record 时过滤)。
|
|
140
|
+
const re = /\/dsh-tiddlywiki\/tw\/#([^#)\s\]]+)/g
|
|
124
141
|
let m: RegExpExecArray | null
|
|
125
142
|
while ((m = re.exec(text)) !== null) {
|
|
126
143
|
const raw = m[1] ?? ''
|