dsh-mnemon 0.5.6 → 0.5.8
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 +2 -0
- package/README.zh-CN.md +2 -0
- package/bin/repair-legacy-session.mjs +220 -0
- package/cordis.patch.yml +5 -0
- package/lib/client.js +23 -3
- package/lib/index.js +469 -280
- package/lib/types/src/client/locales.d.ts +6 -0
- package/package.json +53 -32
package/README.md
CHANGED
|
@@ -52,6 +52,8 @@ dsh plugin --profile web add dsh-mnemon
|
|
|
52
52
|
dsh web
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
The current development baseline is DSH `0.1.5-rc.1` (npm `latest`). Existing Sessions with `source summary requires notice form` need the explicit `dsh-mnemon-repair-session --input FILE --output NEW_FILE` copy repair; see [legacy Session recovery](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/en/guides/operations.md#dsh-015-compatibility-and-legacy-session-recovery) before replacing any artifact.
|
|
56
|
+
|
|
55
57
|
For Headless, add the same package to that profile with `dsh plugin --profile headless add dsh-mnemon`.
|
|
56
58
|
|
|
57
59
|
Open **Memory System → Status**, then add a Runtime memory. Select a DSH workspace before creating Documents, even with global storage. To retain long-term facts, create a Memory Space with an explicitly selected Provider. Sidebar is the default; optional Builtin placement uses the same pages.
|
package/README.zh-CN.md
CHANGED
|
@@ -58,6 +58,8 @@ Headless 使用同一个包:`dsh plugin --profile headless add dsh-mnemon`。
|
|
|
58
58
|
|
|
59
59
|
从 v0.4 升级保留熟悉的配置、数据与工作流。三个可选增强仅在**设置 → 记忆系统**中透出,不增加 View 页或通用记忆插件管理器。[升级清单](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/zh-CN/reference/compatibility.md)。
|
|
60
60
|
|
|
61
|
+
当前开发基线为 npm `latest` 发布的 DSH `0.1.5-rc.1`。现有会话若报 `source summary requires notice form`,需要显式执行 `dsh-mnemon-repair-session --input FILE --output NEW_FILE` 生成修复副本;替换任何文件前请阅读[旧会话恢复流程](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/zh-CN/guides/operations.md#dsh-015-兼容与旧会话恢复)。
|
|
62
|
+
|
|
61
63
|
## Source + Strategy → View
|
|
62
64
|
|
|
63
65
|
[](https://github.com/omdsh-dev/dsh-mnemon/blob/main/docs/zh-CN/development/architecture.md)
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
import { createReadStream, realpathSync } from 'node:fs'
|
|
4
|
+
import { link, mkdtemp, open, rm } from 'node:fs/promises'
|
|
5
|
+
import { dirname, join, resolve } from 'node:path'
|
|
6
|
+
import { fileURLToPath } from 'node:url'
|
|
7
|
+
import * as zlib from 'node:zlib'
|
|
8
|
+
|
|
9
|
+
const maximumBytes = 128 * 1024 * 1024
|
|
10
|
+
const summaries = {
|
|
11
|
+
recall: 'Memory View snapshot',
|
|
12
|
+
instructions: 'Optional memory recall and remember reminder',
|
|
13
|
+
}
|
|
14
|
+
const sha256 = value => createHash('sha256').update(value).digest('hex')
|
|
15
|
+
|
|
16
|
+
// Locate JSON object members without reserializing message content or numbers.
|
|
17
|
+
// JSON.parse validates each complete row before this scanner is used.
|
|
18
|
+
function objectMembers(text, start) {
|
|
19
|
+
let cursor = start
|
|
20
|
+
const space = () => { while (/\s/.test(text[cursor] ?? '') && cursor < text.length) cursor++ }
|
|
21
|
+
const stringEnd = () => {
|
|
22
|
+
cursor++
|
|
23
|
+
while (cursor < text.length) {
|
|
24
|
+
if (text[cursor++] === '"') return
|
|
25
|
+
if (text[cursor - 1] === '\\') cursor++
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const valueEnd = () => {
|
|
29
|
+
if (text[cursor] === '"') return stringEnd()
|
|
30
|
+
if (text[cursor] === '{' || text[cursor] === '[') {
|
|
31
|
+
let depth = 0
|
|
32
|
+
do {
|
|
33
|
+
if (text[cursor] === '"') stringEnd()
|
|
34
|
+
else {
|
|
35
|
+
if ('{['.includes(text[cursor])) depth++
|
|
36
|
+
if ('}]'.includes(text[cursor])) depth--
|
|
37
|
+
cursor++
|
|
38
|
+
}
|
|
39
|
+
} while (depth > 0)
|
|
40
|
+
} else {
|
|
41
|
+
while (cursor < text.length && !/[\s,}\]]/.test(text[cursor])) cursor++
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
space()
|
|
45
|
+
if (text[cursor++] !== '{') throw new Error('Expected a JSON object in the legacy message path.')
|
|
46
|
+
const members = []
|
|
47
|
+
const names = new Set()
|
|
48
|
+
space()
|
|
49
|
+
while (text[cursor] !== '}') {
|
|
50
|
+
const start = cursor
|
|
51
|
+
stringEnd()
|
|
52
|
+
const name = JSON.parse(text.slice(start, cursor))
|
|
53
|
+
if (names.has(name)) throw new Error('Duplicate JSON property in the legacy message path; refusing ambiguous repair.')
|
|
54
|
+
names.add(name)
|
|
55
|
+
space()
|
|
56
|
+
cursor++ // colon, already validated by JSON.parse
|
|
57
|
+
space()
|
|
58
|
+
const valueStart = cursor
|
|
59
|
+
valueEnd()
|
|
60
|
+
members.push({ name, start, end: cursor, valueStart })
|
|
61
|
+
space()
|
|
62
|
+
if (text[cursor] === '}') break
|
|
63
|
+
cursor++ // comma
|
|
64
|
+
space()
|
|
65
|
+
}
|
|
66
|
+
return members
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function removeSummary(line) {
|
|
70
|
+
const data = objectMembers(line, 0).find(member => member.name === 'data')
|
|
71
|
+
const source = objectMembers(line, data.valueStart).find(member => member.name === 'source')
|
|
72
|
+
const members = objectMembers(line, source.valueStart)
|
|
73
|
+
const index = members.findIndex(member => member.name === 'summary')
|
|
74
|
+
const member = members[index]
|
|
75
|
+
const start = index === members.length - 1 && index > 0 ? members[index - 1].end : member.start
|
|
76
|
+
const end = index < members.length - 1 ? members[index + 1].start : member.end
|
|
77
|
+
return line.slice(0, start) + line.slice(end)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function readBounded(path) {
|
|
81
|
+
const chunks = []
|
|
82
|
+
let length = 0
|
|
83
|
+
for await (const chunk of createReadStream(path)) {
|
|
84
|
+
length += chunk.length
|
|
85
|
+
if (length > maximumBytes) throw new Error('Session exceeds the 128 MiB repair limit.')
|
|
86
|
+
chunks.push(chunk)
|
|
87
|
+
}
|
|
88
|
+
return Buffer.concat(chunks)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// RFC 8878 framing: reject torn headers, blocks and checksums before Node's
|
|
92
|
+
// decoder (which can otherwise accept an unfinished frame) sees the bytes.
|
|
93
|
+
function frameLength(input, start) {
|
|
94
|
+
const requireBytes = end => { if (end > input.length) throw new Error('Truncated Zstandard frame; no output written.') }
|
|
95
|
+
requireBytes(start + 5)
|
|
96
|
+
if (input.readUInt32LE(start) !== 0xfd2fb528) throw new Error('Unsupported Zstandard frame; only ordinary DSH frames are accepted.')
|
|
97
|
+
const descriptor = input[start + 4]
|
|
98
|
+
if (descriptor & 0x18) throw new Error('Reserved Zstandard frame header bits are set.')
|
|
99
|
+
const single = (descriptor & 0x20) !== 0
|
|
100
|
+
const sizeFlag = descriptor >>> 6
|
|
101
|
+
const contentSizeBytes = sizeFlag === 0 ? (single ? 1 : 0) : [0, 2, 4, 8][sizeFlag]
|
|
102
|
+
let offset = start + 5 + (single ? 0 : 1) + [0, 1, 2, 4][descriptor & 3] + contentSizeBytes
|
|
103
|
+
requireBytes(offset)
|
|
104
|
+
let last = false
|
|
105
|
+
while (!last) {
|
|
106
|
+
requireBytes(offset + 3)
|
|
107
|
+
const block = input.readUIntLE(offset, 3)
|
|
108
|
+
last = (block & 1) !== 0
|
|
109
|
+
const type = (block >>> 1) & 3
|
|
110
|
+
if (type === 3) throw new Error('Reserved Zstandard block type.')
|
|
111
|
+
offset += 3 + (type === 1 ? 1 : block >>> 3)
|
|
112
|
+
requireBytes(offset)
|
|
113
|
+
}
|
|
114
|
+
offset += descriptor & 4 ? 4 : 0
|
|
115
|
+
requireBytes(offset)
|
|
116
|
+
return offset - start
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function decodeFrames(input) {
|
|
120
|
+
const chunks = []
|
|
121
|
+
let offset = 0
|
|
122
|
+
let length = 0
|
|
123
|
+
while (offset < input.length) {
|
|
124
|
+
const lengthOnDisk = frameLength(input, offset)
|
|
125
|
+
const { buffer, engine } = zlib.zstdDecompressSync(input.subarray(offset, offset + lengthOnDisk), {
|
|
126
|
+
info: true, maxOutputLength: maximumBytes - length,
|
|
127
|
+
})
|
|
128
|
+
if (engine.bytesWritten !== lengthOnDisk) throw new Error('Invalid Zstandard frame length.')
|
|
129
|
+
offset += engine.bytesWritten
|
|
130
|
+
length += buffer.length
|
|
131
|
+
chunks.push(buffer)
|
|
132
|
+
}
|
|
133
|
+
return Buffer.concat(chunks)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function encodeFrames(text) {
|
|
137
|
+
// DSH requires the first frame to contain exactly its header line.
|
|
138
|
+
const end = text.indexOf('\n') + 1
|
|
139
|
+
const options = { params: { [zlib.constants.ZSTD_c_checksumFlag]: 1 } }
|
|
140
|
+
return Buffer.concat([text.slice(0, end), text.slice(end)].filter(Boolean).map(part => zlib.zstdCompressSync(part, options)))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function repairLegacySession(input, output) {
|
|
144
|
+
const original = await readBounded(input)
|
|
145
|
+
const compressed = original.subarray(0, 4).equals(Buffer.from([0x28, 0xb5, 0x2f, 0xfd]))
|
|
146
|
+
if (compressed && (typeof zlib.zstdDecompressSync !== 'function' || typeof zlib.zstdCompressSync !== 'function')) {
|
|
147
|
+
throw new Error('Zstandard sessions require Node 22.19 or newer (or Node 24+).')
|
|
148
|
+
}
|
|
149
|
+
const decoded = compressed ? decodeFrames(original) : original
|
|
150
|
+
const text = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(decoded)
|
|
151
|
+
if (!text.endsWith('\n')) throw new Error('Session must end with a complete newline-terminated JSON record.')
|
|
152
|
+
const lines = text.match(/[^\n]*\n/g) ?? []
|
|
153
|
+
let changes = 0
|
|
154
|
+
const repaired = lines.map((line, index) => {
|
|
155
|
+
let row
|
|
156
|
+
try { row = JSON.parse(line) } catch { throw new Error(`Invalid JSON at line ${index + 1}; no output written.`) }
|
|
157
|
+
if (index === 0) {
|
|
158
|
+
objectMembers(line, 0)
|
|
159
|
+
if (row.type !== 'session' || row.version !== 0 || typeof row.id !== 'string' || !row.id ||
|
|
160
|
+
!Number.isSafeInteger(row.createdAt) || row.createdAt < 0 ||
|
|
161
|
+
!Number.isSafeInteger(row.delegationDepth) || row.delegationDepth < 0) {
|
|
162
|
+
throw new Error('Only a legacy DSH format v0 Session header is supported.')
|
|
163
|
+
}
|
|
164
|
+
return line
|
|
165
|
+
}
|
|
166
|
+
const source = row?.data?.source
|
|
167
|
+
if (row?.type !== 'user/message' || source?.kind !== 'plugin' || source.plugin !== 'dsh-mnemon' ||
|
|
168
|
+
!Object.hasOwn(summaries, source.form) || source.summary !== summaries[source.form]) return line
|
|
169
|
+
changes++
|
|
170
|
+
return removeSummary(line)
|
|
171
|
+
}).join('')
|
|
172
|
+
const plain = Buffer.from(repaired)
|
|
173
|
+
// A clean artifact stays byte-identical, including its existing frame layout.
|
|
174
|
+
const result = changes === 0 ? original : compressed ? encodeFrames(repaired) : plain
|
|
175
|
+
if (output !== undefined) {
|
|
176
|
+
if (resolve(input) === resolve(output)) throw new Error('Output must be a new copy; the input cannot be overwritten.')
|
|
177
|
+
const temporary = await mkdtemp(join(dirname(resolve(output)), '.mnemon-repair-'))
|
|
178
|
+
try {
|
|
179
|
+
const path = join(temporary, 'session')
|
|
180
|
+
const file = await open(path, 'wx', 0o600)
|
|
181
|
+
try { await file.writeFile(result); await file.sync() } finally { await file.close() }
|
|
182
|
+
// Publish exclusively, including when output is a symlink or hard link.
|
|
183
|
+
await link(path, output)
|
|
184
|
+
} finally { await rm(temporary, { recursive: true, force: true }) }
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
mode: output === undefined ? 'preview' : 'copy',
|
|
188
|
+
format: 0,
|
|
189
|
+
encoding: compressed ? 'zstd' : 'jsonl',
|
|
190
|
+
repairedMessages: changes,
|
|
191
|
+
inputSha256: sha256(original),
|
|
192
|
+
outputSha256: sha256(result),
|
|
193
|
+
originalPreserved: true,
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async function main(args) {
|
|
198
|
+
const usage = 'Usage: dsh-mnemon-repair-session --input FILE [--output NEW_FILE]\nWithout --output, only preview the two known Mnemon v0 metadata repairs. Stop DSH and work on a backup copy. The input and existing output files are never overwritten.'
|
|
199
|
+
if (args.length === 1 && args[0] === '--help') { console.log(usage); return }
|
|
200
|
+
const options = new Map()
|
|
201
|
+
for (let index = 0; index < args.length; index += 2) {
|
|
202
|
+
const name = args[index]
|
|
203
|
+
const value = args[index + 1]
|
|
204
|
+
if (!['--input', '--output'].includes(name) || !value || value.startsWith('--') || options.has(name)) throw new Error(usage)
|
|
205
|
+
options.set(name, value)
|
|
206
|
+
}
|
|
207
|
+
if (!options.has('--input')) throw new Error(usage)
|
|
208
|
+
console.log(JSON.stringify(await repairLegacySession(options.get('--input'), options.get('--output')), null, 2))
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// npm global installs expose this executable through a symlink. Resolve both
|
|
212
|
+
// sides while keeping imports from stdin or another entry point side-effect free.
|
|
213
|
+
function isEntrypoint() {
|
|
214
|
+
try { return process.argv[1] !== undefined && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url)) }
|
|
215
|
+
catch { return false }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (isEntrypoint()) {
|
|
219
|
+
main(process.argv.slice(2)).catch(error => { console.error(error.message); process.exitCode = 1 })
|
|
220
|
+
}
|
package/cordis.patch.yml
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
# Mount the Mnemon bridge into a DSH profile. A profile's final patch may
|
|
2
2
|
# replace this config row as a whole; use user settings or environment
|
|
3
3
|
# variables for one-field overrides such as cliPath.
|
|
4
|
+
# Connection's generic RPC registry uses its owning Context to mount routes.
|
|
5
|
+
# DSH 0.1.5's Web profile omits this dependency from that service's scope.
|
|
6
|
+
- id: connection
|
|
7
|
+
inject: [webRuntime, webServer]
|
|
8
|
+
|
|
4
9
|
- insert:
|
|
5
10
|
# Keep the v0.4 `mnemon` Entry as the public configuration target while
|
|
6
11
|
# making every v0.5 component inherit its enabled state. The expression
|
package/lib/client.js
CHANGED
|
@@ -543,7 +543,7 @@ window.__ModuleLoader__.load({
|
|
|
543
543
|
"documents.size": "文件大小",
|
|
544
544
|
"documents.archiveReceipt": "Mnemon 冷索引回执",
|
|
545
545
|
"documents.archiveTitle": "迁入归档",
|
|
546
|
-
"documents.archiveDescription": "
|
|
546
|
+
"documents.archiveDescription": "独立任务 Agent 先拟定摘要和目标空间;Host 校验后写入冷索引,再移动原文。需要已启用且支持精确写入与删除的记忆空间。",
|
|
547
547
|
"documents.archive": "归档",
|
|
548
548
|
"documents.archiveConfirm": "确认建立 Mnemon 索引并迁移这份档案?",
|
|
549
549
|
"documents.archiving": "索引并迁移中…",
|
|
@@ -605,7 +605,7 @@ window.__ModuleLoader__.load({
|
|
|
605
605
|
"documents.size": "File size",
|
|
606
606
|
"documents.archiveReceipt": "Mnemon cold-index receipt",
|
|
607
607
|
"documents.archiveTitle": "Move to archive",
|
|
608
|
-
"documents.archiveDescription": "
|
|
608
|
+
"documents.archiveDescription": "An independent task Agent proposes a summary and destination. The Host validates and writes the cold index before moving the original. An active Memory Space with exact writes and safe deletion is required.",
|
|
609
609
|
"documents.archive": "Archive",
|
|
610
610
|
"documents.archiveConfirm": "Create the Mnemon index and archive this document?",
|
|
611
611
|
"documents.archiving": "Indexing and moving…",
|
|
@@ -1460,6 +1460,9 @@ window.__ModuleLoader__.load({
|
|
|
1460
1460
|
"status.title": "系统状态",
|
|
1461
1461
|
"status.description": "聚焦 dsh-mnemon、各记忆 Provider、三层存储和当前读写目录;连接配置由 DSH 部署统一管理。",
|
|
1462
1462
|
"status.nominal": "系统正常",
|
|
1463
|
+
"status.reviewFailed": "后台审查失败",
|
|
1464
|
+
"status.reviewFailedDetail": "本轮记忆检查未完成;候选内容会保留到后续符合条件的审查。",
|
|
1465
|
+
"status.reviewContextWindow": "请在 Mnemon 设置中选择上下文窗口足以覆盖父会话的任务模型。后台审查需要继承父会话。",
|
|
1463
1466
|
"status.checkRequired": "需要检查",
|
|
1464
1467
|
"status.rechecking": "检查中…",
|
|
1465
1468
|
"status.recheck": "重新检查",
|
|
@@ -1982,6 +1985,9 @@ window.__ModuleLoader__.load({
|
|
|
1982
1985
|
"status.title": "System Status",
|
|
1983
1986
|
"status.description": "dsh-mnemon, memory providers, three-tier storage, and the current read/write root. DSH deployment owns connection configuration.",
|
|
1984
1987
|
"status.nominal": "System nominal",
|
|
1988
|
+
"status.reviewFailed": "Background review failed",
|
|
1989
|
+
"status.reviewFailedDetail": "This memory checkpoint did not complete. Candidates remain pending for a later eligible review.",
|
|
1990
|
+
"status.reviewContextWindow": "Choose a task model in Mnemon settings whose context window covers the parent conversation. Background review inherits the parent conversation.",
|
|
1985
1991
|
"status.checkRequired": "Check required",
|
|
1986
1992
|
"status.rechecking": "Checking…",
|
|
1987
1993
|
"status.recheck": "Check again",
|
|
@@ -6765,6 +6771,7 @@ window.__ModuleLoader__.load({
|
|
|
6765
6771
|
const t = useT();
|
|
6766
6772
|
const [versionsOpen, setVersionsOpen] = (0, react.useState)(false);
|
|
6767
6773
|
const status = props.status;
|
|
6774
|
+
const reviewError = status?.lifecycle?.current?.lastError;
|
|
6768
6775
|
const documents = status?.documents;
|
|
6769
6776
|
const catalogKnown = status?.memoryBodies !== void 0;
|
|
6770
6777
|
const memorySpaces = (0, react.useMemo)(() => status?.memoryBodies ?? [], [status]);
|
|
@@ -6781,7 +6788,7 @@ window.__ModuleLoader__.load({
|
|
|
6781
6788
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PageHeader, {
|
|
6782
6789
|
title: t("status.title"),
|
|
6783
6790
|
description: t("status.description"),
|
|
6784
|
-
meta: status === null && props.loading ? t("common.loading") : status === null ? t("status.checkRequired") : t("status.nominal"),
|
|
6791
|
+
meta: status === null && props.loading ? t("common.loading") : status === null || reviewError !== void 0 ? t("status.checkRequired") : t("status.nominal"),
|
|
6785
6792
|
...props.loading ? { loadingLabel: t("status.rechecking") } : {},
|
|
6786
6793
|
action: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6787
6794
|
className: MnemonView_module_css_default.statusHeaderActions,
|
|
@@ -7435,6 +7442,17 @@ window.__ModuleLoader__.load({
|
|
|
7435
7442
|
role: "alert",
|
|
7436
7443
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("header.notReady") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: statusError ?? status?.error })]
|
|
7437
7444
|
}),
|
|
7445
|
+
status?.lifecycle?.current?.lastError !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7446
|
+
className: MnemonView_module_css_default.alert,
|
|
7447
|
+
role: "alert",
|
|
7448
|
+
"aria-label": t("status.reviewFailed"),
|
|
7449
|
+
children: [
|
|
7450
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: t("status.reviewFailed") }),
|
|
7451
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: status.lifecycle.current.lastError }),
|
|
7452
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("status.reviewFailedDetail") }),
|
|
7453
|
+
/CONTEXT_WINDOW_EXCEEDED|exceed(?:s|ed)? (?:the )?(?:available )?context (?:size|window)/iu.test(status.lifecycle.current.lastError) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("status.reviewContextWindow") })
|
|
7454
|
+
]
|
|
7455
|
+
}),
|
|
7438
7456
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7439
7457
|
className: MnemonView_module_css_default.workspace,
|
|
7440
7458
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WorkspaceNavigation, {
|
|
@@ -7522,6 +7540,8 @@ window.__ModuleLoader__.load({
|
|
|
7522
7540
|
const entry = document.createElement("button");
|
|
7523
7541
|
entry.type = "button";
|
|
7524
7542
|
entry.dataset.dshMnemonEntry = "";
|
|
7543
|
+
entry.dataset.dshPlugin = "dsh-mnemon";
|
|
7544
|
+
entry.dataset.dshPart = "sidebar-entry";
|
|
7525
7545
|
entry.className = MnemonWorkspace_module_css_default.entry ?? "";
|
|
7526
7546
|
const icon = document.createElement("span");
|
|
7527
7547
|
icon.className = MnemonWorkspace_module_css_default.entryIcon ?? "";
|