dsh-session-toolbox 0.1.7 → 0.1.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/CHANGELOG.md +5 -0
- package/cordis.patch.yml +2 -2
- package/lib/{chunk-4UQB3MPY.js → chunk-QXW2NIKH.js} +3 -1
- package/lib/{chunk-4UQB3MPY.js.map → chunk-QXW2NIKH.js.map} +2 -2
- package/lib/cli.js +1 -1
- package/lib/client.js +63 -11
- package/lib/client.js.map +2 -2
- package/lib/{host-v0.1.7.js → host-v0.1.8.js} +25 -2
- package/lib/host.js +25 -2
- package/lib/index.js +25 -2
- package/lib/index.js.map +3 -3
- package/package.json +1 -1
package/lib/client.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/client.jsx", "../package.json", "../src/draft-vault.js"],
|
|
4
|
-
"sourcesContent": ["/**\n * dsh-session-toolbox browser plugin: management page (\u00A74 FR5/FR6, \u00A710 MR6, \u00A711\n * TB2), session context-menu entries, the input-dock vault badge, and the\n * draft vault itself (capture + IndexedDB + synthetic-paste restore; route A).\n * Every official surface is probed (`ctx.get?.`, guarded registrations); no\n * path here may take the native composer down with it (NFR3).\n */\nimport { useEffect, useRef, useState } from 'react';\nimport pkg from '../package.json' with { type: 'json' };\nconst pluginVersion = pkg.version;\nimport { createVault } from './draft-vault.js';\n\nexport const name = 'dsh-session-toolbox-client';\n// `locale` is a real service, so it must be injected before use: cordis throws\n// on property access for a service that was never injected (it does not return\n// undefined), which is what surfaced as \"cannot get property \\\"locale\\\" without\n// inject\" on the plugin page.\nexport const inject = ['slots', 'locale'];\nexport const NS = 'dsh-session-toolbox';\nconst ENDPOINT = `/.well-known/${NS}`;\n\nlet vault; // browser-side singleton (IndexedDB + host manifest)\nlet directoryPicker; // ctx.get('directoryPicker') probed at apply time\nconst store = { state: null, error: '', listeners: new Set() };\n\nasync function readResponse(res) {\n\tlet next;\n\ttry { next = JSON.parse(await res.text()); }\n\tcatch { throw new Error(`\u63D2\u4EF6\u540E\u53F0\u672A\u5C31\u7EEA\uFF08HTTP ${res.status}\uFF09\uFF1B\u5B89\u88C5\u6216\u91CD\u542F\u671F\u95F4\u4F1A\u81EA\u52A8\u91CD\u8BD5`); }\n\tif (next === null || typeof next !== 'object' || Array.isArray(next)) throw new Error('\u63D2\u4EF6\u540E\u53F0\u8FD4\u56DE\u4E86\u65E0\u6548\u6570\u636E');\n\tif (!res.ok) throw new Error(next.error ?? `\u64CD\u4F5C\u5931\u8D25\uFF08HTTP ${res.status}\uFF09`);\n\treturn next;\n}\n\n/** GET the shared snapshot; also (re)binds the write key the host rotates. */\nexport let sessionKey = '';\nexport async function loadState() {\n\ttry {\n\t\tconst next = await readResponse(await fetch(ENDPOINT, { cache: 'no-store' }));\n\t\tstore.state = next; store.error = ''; sessionKey = next.key ?? sessionKey;\n\t} catch (error) { store.error = error.message; }\n\tfor (const listener of store.listeners) listener();\n\treturn store.state;\n}\nexport async function callHost(input) {\n\tif (!sessionKey) await loadState();\n\tconst res = await readResponse(await fetch(ENDPOINT, {\n\t\tmethod: 'POST', cache: 'no-store',\n\t\theaders: { 'content-type': 'application/json', 'x-session-toolbox-key': sessionKey },\n\t\tbody: JSON.stringify(input),\n\t}));\n\tsessionKey = res.key ?? sessionKey;\n\tfor (const listener of store.listeners) listener();\n\treturn res;\n}\n\nexport function apply(ctx) {\n\tdirectoryPicker = ctx.get?.('directoryPicker');\n\tconst vaultInstance = vault ??= createVault({\n\t\tendpoint: ENDPOINT,\n\t\tlog: (level, message) => { try { (level === 'error' ? console.warn : console.log)(`[${NS}]`, message); } catch {} },\n\t});\n\tvoid loadState();\n\tconst timer = setInterval(() => void loadState(), 30000);\n\tctx.effect?.(() => () => clearInterval(timer));\n\tvaultInstance.on('change', () => void loadState());\n\tvoid vaultInstance.start();\n\t// `locale` is injected above, so the service is guaranteed here; registering\n\t// inside ctx.effect keeps the dictionaries disposable across hot reloads\n\t// (same shape as the official client plugins).\n\tctx.effect(() => ctx.locale.register(NS, {\n\t\tzh: { 'menu.move': '\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE\u2026', 'menu.trash': '\u79FB\u5165\u56DE\u6536\u7AD9' },\n\t\ten: { 'menu.move': 'Move to another project\u2026', 'menu.trash': 'Move to trash' },\n\t}), 'session-toolbox: dictionaries');\n\n\t// Keyed slot (one occupant = package name): the plugin settings page.\n\tctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({ name: 'plugins.bundle.config', key: NS }, KitPage));\n\t// Session context menu: move + trash entries beside the official four.\n\tctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register({ name: 'sidebar.workspaces.session.menu.item', id: 'session-toolbox-move', order: 500, locale: NS }, MoveSessionMenuItem));\n\tctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register({ name: 'sidebar.workspaces.session.menu.item', id: 'session-toolbox-trash', order: 600, locale: NS }, TrashSessionMenuItem));\n\t// Input dock badge (\u00A74 FR5): how many image drafts are backed up here.\n\tctx.slots.inject('conversation.input.dock', () => ctx.slots.register({ name: 'conversation.input.dock', id: 'session-toolbox-vault-badge', order: 5, locale: NS }, VaultBadge));\n}\n\nfunction useKit() {\n\tconst [, bump] = useState(0);\n\tuseEffect(() => {\n\t\tconst listener = () => bump(n => n + 1);\n\t\tstore.listeners.add(listener);\n\t\tvoid loadState();\n\t\treturn () => store.listeners.delete(listener);\n\t}, []);\n\treturn store;\n}\n\nconst styles = `\n.sk{--sk-accent:var(--dsw-alias-state-active-primary,#3a71db);--sk-border:var(--dsw-alias-border-l2,#444);--sk-bg:var(--dsw-alias-bg-layer-1,#202020);--sk-card:var(--dsw-alias-bg-module-platform,#282828);--sk-text:var(--dsw-alias-label-primary,#f4f4f4);--sk-muted:var(--dsw-alias-label-secondary,#b0b0b0);color:var(--sk-text);font:inherit;font-size:14px;line-height:1.6;max-width:1120px}\n.sk *{box-sizing:border-box}.sk h2,.sk h3,.sk p{margin:0}.sk h2{font-size:22px}.sk h3{font-size:16px}.sk .muted,.sk small{color:var(--sk-muted)}\n.sk .box{border:1px solid var(--sk-border);border-radius:12px;padding:18px 20px;background:var(--sk-card);display:flex;flex-direction:column;gap:14px}\n.sk .row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.sk .stack{display:flex;flex-direction:column;gap:16px}\n.sk button,.sk input,.sk select{font:inherit;color:inherit;border:1px solid var(--sk-border);border-radius:8px;background:var(--sk-bg);min-height:36px;padding:7px 12px}\n.sk button{cursor:pointer}.sk button:hover{background:var(--sk-card)}.sk button:disabled{opacity:.45;cursor:default}\n.sk button.primary{background:var(--sk-accent);color:#fff;border-color:transparent}\n.sk table{width:100%;border-collapse:collapse;font-size:13px}.sk th,.sk td{border-bottom:1px solid var(--sk-border);padding:8px;text-align:left}.sk th{color:var(--sk-muted);font-weight:500}\n.sk .error{color:var(--dsw-alias-state-error-primary,#ef9393)}.sk .good{color:var(--dsw-alias-state-success-primary,#77ca9d)}\n.sk .pill{border:1px solid var(--sk-border);border-radius:100px;padding:2px 10px;font-size:12px;background:var(--sk-bg);display:inline-block}\n.sk .mono{font:12px ui-monospace,monospace;overflow-wrap:anywhere}\n.sk dialog{color:var(--sk-text);background:var(--sk-bg);border:1px solid var(--sk-border);border-radius:14px;width:min(560px,calc(100vw - 32px));padding:0}\n.sk dialog::backdrop{background:#0007}.sk .modal-head{padding:16px 20px;border-bottom:1px solid var(--sk-border);display:flex;justify-content:space-between;align-items:center}\n.sk .modal-body{padding:20px;display:flex;flex-direction:column;gap:14px}\n.sk input.grow{flex:1;min-width:0}\n`;\n\nexport function KitPage() {\n\tconst { state, error } = useKit();\n\tconst [busy, setBusy] = useState(false);\n\tconst [feedback, setFeedback] = useState('');\n\tconst say = message => { setFeedback(message); setTimeout(() => setFeedback(''), 8000); };\n\tconst act = async (input, ok) => {\n\t\tsetBusy(true);\n\t\ttry { const result = await callHost(input); if (ok) say(ok); await loadState(); setBusy(false); return result; }\n\t\tcatch (err) { say(`\u5931\u8D25\uFF1A${err.message}`); setBusy(false); throw err; }\n\t};\n\tconst settings = state?.settings;\n\treturn (\n\t\t<div className=\"sk\">\n\t\t\t<style>{styles}</style>\n\t\t\t<h2>\u4F1A\u8BDD\u5DE5\u5177\u7BB1</h2>\n\t\t\t<p className=\"muted\">\u8349\u7A3F\u4FDD\u9669\u7BB1 \u00B7 \u4F1A\u8BDD\u5207\u6362\u9879\u76EE \u00B7 \u56DE\u6536\u7AD9 \u2014 v{pluginVersion}\u3002\u6570\u636E\u4EC5\u5B58\u672C\u673A\uFF08\u6D4F\u89C8\u5668 IndexedDB + ~/.dsh/session-toolbox/\uFF09\u3002</p>\n\t\t\t{error ? <p className=\"error\">\u540E\u53F0\u8FDE\u63A5\u5931\u8D25\uFF1A{error}</p> : null}\n\t\t\t{feedback ? <p className=\"good\">{feedback}</p> : null}\n\t\t\t<section className=\"box\">\n\t\t\t\t<h3>\u8349\u7A3F\u4FDD\u9669\u7BB1\uFF08\u56FE\u7247\u8349\u7A3F\u6301\u4E45\u5316\uFF09</h3>\n\t\t\t\t{settings ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"row\">\n\t\t\t\t\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={settings.draftVault?.enabled !== false} disabled={busy}\n\t\t\t\t\t\t\t\tonChange={e => void act({ type: 'settings.update', patch: { draftVault: { ...settings.draftVault, enabled: e.target.checked } } }, e.target.checked ? '\u5DF2\u542F\u7528\u8349\u7A3F\u4FDD\u9669\u7BB1' : '\u5DF2\u505C\u7528\u8349\u7A3F\u4FDD\u9669\u7BB1\uFF08\u6D4F\u89C8\u5668\u4FA7\u5373\u65F6\u751F\u6548\uFF09').catch(() => {})} /> \u542F\u7528</label>\n\t\t\t\t\t\t\t<span className=\"pill\">\u5BB9\u91CF {Math.round((settings.draftVault?.maxSizeBytes ?? 0) / 1048576)} MB</span>\n\t\t\t\t\t\t\t<span className=\"pill\">\u4FDD\u7559 {settings.draftVault?.ttlDays ?? 7} \u5929</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"row\">\n\t\t\t\t\t\t\t<span className=\"muted\">\u6E05\u5355\uFF1A\u5DF2\u5907\u4EFD {state?.vault?.records ?? 0} \u9879 \u00B7 \u7EA6 {fmtBytes(state?.vault?.bytes ?? 0)}\uFF08\u56FE\u7247\u5B57\u8282\u5728 IndexedDB\uFF0C\u8D85\u51FA\u4E0A\u9650\u6309\u6700\u65E7\u6E05\u7406\uFF0C\u4E8B\u4EF6\u4F1A\u663E\u793A\u5728\u8FD9\u91CC\uFF09</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{state?.vault?.notice ? <p className=\"muted\">{state.vault.notice}</p> : null}\n\t\t\t\t\t\t<div className=\"row\"><button disabled={busy} onClick={() => void act({ type: 'vault.sweep' }, '\u5DF2\u6309 TTL/\u5BB9\u91CF\u6E05\u7406').catch(() => {})}>\u7ACB\u5373\u6574\u7406</button></div>\n\t\t\t\t\t</>\n\t\t\t\t) : <p className=\"muted\">\u7B49\u5F85\u540E\u53F0\u6570\u636E\u2026</p>}\n\t\t\t</section>\n\t\t\t<MoveSection act={act} busy={busy} say={say} state={state} />\n\t\t\t<TrashSection act={act} busy={busy} say={say} state={state} />\n\t\t</div>\n\t);\n}\n\nfunction MoveSection({ act, busy, say, state }) {\n\tconst [sessionId, setSessionId] = useState('');\n\tconst [toCwd, setToCwd] = useState('');\n\tconst [createTarget, setCreateTarget] = useState(false);\n\tconst [plan, setPlan] = useState(null);\n\tconst journal = state?.journal;\n\tconst pick = async () => {\n\t\ttry {\n\t\t\tconst result = await directoryPicker?.pick?.();\n\t\t\tconst value = result?.ok ? result.value : undefined;\n\t\t\tif (typeof value === 'string' && value) setToCwd(value);\n\t\t\telse say('\u5F53\u524D DSH \u672A\u63D0\u4F9B\u76EE\u5F55\u9009\u62E9\u5668\uFF08\u6216\u5DF2\u53D6\u6D88\uFF09\uFF0C\u8BF7\u624B\u52A8\u8F93\u5165\u7EDD\u5BF9\u8DEF\u5F84');\n\t\t} catch (error) { say(`\u76EE\u5F55\u9009\u62E9\u5931\u8D25\uFF1A${error.message}`); }\n\t};\n\treturn (\n\t\t<section className=\"box\">\n\t\t\t<h3>\u4F1A\u8BDD\u5207\u6362\u9879\u76EE</h3>\n\t\t\t{journal && <p className=\"error\">\u5B58\u5728\u672A\u5B8C\u6210\u7684\u8FC1\u79FB\uFF1A{journal.sessionId} \u2192 {journal.toCwd}\uFF08\u5DF2\u5B8C\u6210 {journal.done?.join('/') || '\u65E0'}\uFF09\u3002\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'move.complete' }, '\u8FC1\u79FB\u5DF2\u524D\u6EDA\u5B8C\u6210').catch(() => {})}>\u5B8C\u6210</button>\n\t\t\t\t{' '}<button disabled={busy} onClick={() => void act({ type: 'move.rollback' }, '\u8FC1\u79FB\u5DF2\u56DE\u6EDA').catch(() => {})}>\u56DE\u6EDA</button></p>}\n\t\t\t<div className=\"row\">\n\t\t\t\t<input className=\"grow mono\" placeholder=\"session-\u2026\uFF08\u6216\u4ECE\u4F1A\u8BDD\u53F3\u952E\u83DC\u5355\u53D1\u8D77\uFF09\" value={sessionId} onChange={e => { setSessionId(e.target.value); setPlan(null); }} />\n\t\t\t\t<input className=\"grow mono\" placeholder=\"\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84\" value={toCwd} onChange={e => { setToCwd(e.target.value); setPlan(null); }} />\n\t\t\t\t<button onClick={() => void pick()} disabled={busy}>\u6D4F\u89C8\u2026</button>\n\t\t\t</div>\n\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={createTarget} onChange={e => setCreateTarget(e.target.checked)} /> \u76EE\u6807\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA</label>\n\t\t\t<div className=\"row\">\n\t\t\t\t<button disabled={busy || !sessionId || !toCwd} onClick={() => { setBusy(true); setPlan(null); void callHost({ type: 'preflight', sessionId, toCwd, createTarget })\n\t\t\t\t\t.then(result => setPlan(result)).catch(error => say(`\u4F53\u68C0\u5931\u8D25\uFF1A${error.message}`)).finally(() => setBusy(false)); }}>\u4F53\u68C0</button>\n\t\t\t\t<button className=\"primary\" disabled={busy || plan?.toProject === undefined} onClick={() => void act({ type: 'move', sessionId, toCwd, createTarget }, '\u8FC1\u79FB\u5B8C\u6210\uFF1B\u5176\u5B83\u7A97\u53E3\u8BF7\u91CD\u5F00\u8BE5\u4F1A\u8BDD').catch(() => {})}>\u8FC1\u79FB</button>\n\t\t\t</div>\n\t\t\t{plan?.toProject && (\n\t\t\t\t<pre className=\"mono muted\" style={{ whiteSpace: 'pre-wrap' }}>{JSON.stringify({ \u4ECE: plan.fromCwd, \u5230: plan.toCwd, \u539F\u76EE\u5F55: plan.fromProject, \u65B0\u76EE\u5F55: plan.toProject, \u65E5\u5FD7\u6587\u4EF6: plan.logName }, null, 2)}</pre>\n\t\t\t)}\n\t\t\t<p className=\"muted\">\u53EA\u6539 DSH \u5185\u90E8\u5F52\u5C5E\u4E0E\u4F1A\u8BDD\u5934\uFF1B\u5386\u53F2\u4E0D\u53D8\uFF0C\u65E7\u6D88\u606F\u4E2D\u7684\u7EDD\u5BF9\u8DEF\u5F84\u4ECD\u6307\u5411\u539F\u9879\u76EE\uFF08\u5DF2\u77E5\u8BED\u4E49\uFF09\u3002\u8FC1\u79FB\u524D\u8BF7\u5173\u95ED\u8BE5\u4F1A\u8BDD\u3002</p>\n\t\t</section>\n\t);\n}\n\nfunction TrashSection({ act, busy, say, state }) {\n\tconst items = state?.trash ?? [];\n\treturn (\n\t\t<section className=\"box\">\n\t\t\t<h3>\u56DE\u6536\u7AD9</h3>\n\t\t\t<p className=\"muted\">\u5220\u9664\u4F1A\u8BDD = \u5B98\u65B9\u5F52\u6863 + \u5165\u7AD9\u8BB0\u5F55\uFF1B\u5220\u9664\u9879\u76EE\u4FDD\u7559\u5168\u90E8\u4F1A\u8BDD\u65E5\u5FD7\u3002\u5F7B\u5E95\u5220\u9664\u4F1A\u5148\u79FB\u5230\u6682\u5B58\u76EE\u5F55\u518D\u6E05\u7406\uFF0C\u4E2D\u65AD\u53EF\u7528 CLI \u68C0\u51FA\u3002</p>\n\t\t\t{items.length === 0 ? <p className=\"muted\">\uFF08\u7A7A\uFF09</p> : (\n\t\t\t\t<table>\n\t\t\t\t\t<thead><tr><th>\u7C7B\u578B</th><th>\u5BF9\u8C61</th><th>\u6807\u9898 / \u8DEF\u5F84</th><th>\u5165\u7AD9\u65F6\u95F4</th><th /></tr></thead>\n\t\t\t\t\t<tbody>{items.map(item => (\n\t\t\t\t\t\t<tr key={item.itemId}>\n\t\t\t\t\t\t\t<td>{item.kind === 'session' ? '\u4F1A\u8BDD' : '\u9879\u76EE'}</td>\n\t\t\t\t\t\t\t<td className=\"mono\">{shortId(item.targetId)}</td>\n\t\t\t\t\t\t\t<td className=\"mono\">{item.kind === 'session' ? item.snapshot.title || '\uFF08\u65E0\u6807\u9898\uFF09' : item.snapshot.path}</td>\n\t\t\t\t\t\t\t<td>{new Date(item.trashedAt).toLocaleString('zh-CN', { hour12: false })}</td>\n\t\t\t\t\t\t\t<td className=\"row\">\n\t\t\t\t\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.restore', itemId: item.itemId }, '\u5DF2\u8FD8\u539F\uFF08\u82E5\u5904\u4E8E\u5F52\u6863\u6001\u8BF7\u5148\u5728\u5217\u8868\u91CC\u8FD8\u539F\u5F52\u6863\uFF09').catch(() => {})}>\u8FD8\u539F</button>\n\t\t\t\t\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.purge', itemId: item.itemId }, '\u5DF2\u5F7B\u5E95\u5220\u9664').catch(() => {})}>\u5F7B\u5E95\u5220\u9664</button>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>))}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t)}\n\t\t\t<div className=\"row\">\n\t\t\t\t<span className=\"muted\">\u4FDD\u7559 {state?.settings?.trash?.ttlDays ?? 30} \u5929\uFF1B\u5230\u671F\u81EA\u52A8\u6E05\u7406{state?.settings?.trash?.autoPurge ? '\uFF08\u5DF2\u5F00\uFF09' : '\uFF08\u672A\u5F00\uFF0C\u4EC5\u63D0\u9192\uFF09'}\u3002</span>\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'settings.update', patch: { trash: { ...state?.settings?.trash, autoPurge: !(state?.settings?.trash?.autoPurge) } } }, '\u7B56\u7565\u5DF2\u66F4\u65B0').catch(() => {})}>\u5207\u6362\u81EA\u52A8\u6E05\u7406</button>\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.sweep' }, '\u5DF2\u6309 TTL \u5DE1\u68C0').catch(() => {})}>\u5DE1\u68C0\u5230\u671F\u9879</button>\n\t\t\t</div>\n\t\t</section>\n\t);\n}\n\nconst shortId = id => String(id).length > 18 ? `${String(id).slice(0, 8)}\u2026${String(id).slice(-6)}` : String(id);\nconst fmtBytes = n => n >= 1048576 ? `${(n / 1048576).toFixed(1)} MB` : n >= 1024 ? `${(n / 1024).toFixed(1)} KB` : `${n} B`;\n\n/** Menu entry: move this session to another project (\u00A710 MR6). */\nexport function MoveSessionMenuItem({ sessionId, t }) {\n\tconst [open, setOpen] = useState(false);\n\treturn (\n\t\t<>\n\t\t\t<button role=\"menuitem\" onClick={() => setOpen(true)} style={menuItemStyle}>{t ? t('menu.move') : '\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE\u2026'}</button>\n\t\t\t{open ? <MoveDialog sessionId={sessionId} close={() => setOpen(false)} /> : null}\n\t\t</>\n\t);\n}\n\nexport function TrashSessionMenuItem({ sessionId, t }) {\n\tconst [confirm, setConfirm] = useState(false);\n\tconst [busy, setBusy] = useState(false);\n\tif (confirm) {\n\t\treturn <button role=\"menuitem\" disabled={busy} style={menuItemStyle} onClick={async () => {\n\t\t\tsetBusy(true);\n\t\t\ttry { await callHost({ type: 'trash.session', sessionId }); setConfirm(false); }\n\t\t\tcatch { sayRow(`\u56DE\u6536\u5931\u8D25`); setConfirm(false); }\n\t\t\tsetBusy(false);\n\t\t}}>\u786E\u8BA4\u79FB\u5165\u56DE\u6536\u7AD9\uFF1F\uFF08\u5148\u5F52\u6863\uFF09</button>;\n\t}\n\treturn <button role=\"menuitem\" style={menuItemStyle} onClick={() => setConfirm(true)}>{t ? t('menu.trash') : '\u79FB\u5165\u56DE\u6536\u7AD9'}</button>;\n}\nfunction sayRow(message) { store.error = message; for (const listener of store.listeners) listener(); }\n\nconst menuItemStyle = { background: 'transparent', border: 'none', color: 'inherit', font: 'inherit', padding: '6px 10px', textAlign: 'left', cursor: 'pointer', width: '100%' };\n\nexport function MoveDialog({ sessionId, close }) {\n\tconst ref = useRef(null);\n\tconst [toCwd, setToCwd] = useState('');\n\tconst [createTarget, setCreateTarget] = useState(false);\n\tconst [error, setError] = useState('');\n\tconst [busy, setBusy] = useState(false);\n\tuseEffect(() => { const dialog = ref.current; dialog?.showModal(); return () => { try { dialog?.close(); } catch {} }; }, []);\n\tconst submit = async () => {\n\t\tsetBusy(true); setError('');\n\t\ttry { await callHost({ type: 'move', sessionId, toCwd, createTarget }); close(); }\n\t\tcatch (err) { setError(err.message); setBusy(false); }\n\t};\n\tconst valid = /^\\/|^[A-Za-z]:[\\\\/]/.test(toCwd);\n\treturn (\n\t\t<div className=\"sk\"><style>{styles}</style>\n\t\t\t<dialog ref={ref} onCancel={e => { e.preventDefault(); if (!busy) close(); }}>\n\t\t\t\t<div className=\"modal-head\"><strong>\u4F1A\u8BDD\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE</strong><button onClick={close} disabled={busy}>\u5173\u95ED</button></div>\n\t\t\t\t<div className=\"modal-body\">\n\t\t\t\t\t<p className=\"muted mono\">{sessionId}</p>\n\t\t\t\t\t<input autoFocus className=\"grow mono\" placeholder=\"\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84\" value={toCwd} onChange={e => setToCwd(e.target.value)} />\n\t\t\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={createTarget} onChange={e => setCreateTarget(e.target.checked)} /> \u76EE\u5F55\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA</label>\n\t\t\t\t\t{error ? <p className=\"error\">{error}</p> : null}\n\t\t\t\t\t<div className=\"row\"><button className=\"primary\" disabled={busy || !valid} onClick={() => void submit()}>\u4F53\u68C0\u5E76\u8FC1\u79FB</button>\n\t\t\t\t\t\t<span className=\"muted\">\u4F1A\u8BDD\u9700\u7A7A\u95F2\uFF1B\u8FC1\u79FB\u5728\u540E\u53F0\u79BB\u7EBF\u5B8C\u6210\u3002</span></div>\n\t\t\t\t</div>\n\t\t\t</dialog>\n\t\t</div>\n\t);\n}\n\n/** Input-dock badge (\u00A74 FR5): how many image drafts are backed up for this session. */\nexport function VaultBadge({ sessionId }) {\n\tconst [count, setCount] = useState(0);\n\tconst [notice, setNotice] = useState('');\n\tuseEffect(() => {\n\t\tif (vault === undefined) return;\n\t\tlet live = true;\n\t\tconst sync = async () => {\n\t\t\ttry { const value = await vault.countFor(sessionId ?? vault.currentSession()); if (live) setCount(value); }\n\t\t\tcatch { if (live) setCount(0); }\n\t\t\tif (live) setNotice(vault.lastNotice ?? '');\n\t\t};\n\t\tvoid sync();\n\t\tconst listener = () => void sync();\n\t\tvault.on('change', listener);\n\t\twindow.addEventListener(`${NS}-notice`, listener);\n\t\tconst timer = setInterval(listener, 20000);\n\t\treturn () => { live = false; vault.off('change', listener); window.removeEventListener(`${NS}-notice`, listener); clearInterval(timer); };\n\t}, [sessionId]);\n\tif (count === 0) return null; // the notice channel serves the panel; the badge anchors per-session counts\n\treturn <span className=\"sk\"><span className=\"pill\" title={`\u672C\u4F1A\u8BDD\u6709 ${count} \u5F20\u56FE\u7247\u8349\u7A3F\u5DF2\u5907\u4EFD\uFF08\u5207\u6362\u4F1A\u8BDD/\u5237\u65B0/\u91CD\u542F\u540E\u53EF\u6062\u590D\uFF09${notice ? `\uFF5C\u6700\u8FD1\uFF1A${notice}` : ''}`}>\uD83D\uDEDF \u8349\u7A3F\u5907\u4EFD {count}</span></span>;\n}\n", "{\n \"name\": \"dsh-session-toolbox\",\n \"version\": \"0.1.7\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AndroidSix/dsh-session-toolbox.git\"\n },\n \"description\": \"Draft vault, session-to-project move and a trash bin for DeepSeek Harness\",\n \"type\": \"module\",\n \"main\": \"lib/index.js\",\n \"bin\": {\n \"dsh-session-toolbox\": \"lib/cli.js\"\n },\n \"exports\": {\n \".\": \"./lib/index.js\",\n \"./host\": \"./lib/host.js\",\n \"./client\": \"./lib/client.js\",\n \"./cordis.patch.yml\": \"./cordis.patch.yml\",\n \"./package.json\": \"./package.json\"\n },\n \"engines\": {\n \"node\": \"^22.19.0 || >=24.0.0\",\n \"dsh\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\"\n },\n \"files\": [\n \"lib\",\n \"cordis.patch.yml\",\n \"README.md\",\n \"LICENSE\",\n \"THIRD_PARTY_NOTICES.md\",\n \"session-toolbox.example.json\",\n \"CHANGELOG.md\"\n ],\n \"dsh\": {\n \"bundle\": {\n \"patch\": \"./cordis.patch.yml\"\n },\n \"client\": {\n \"inject\": [\n \"@deepseek-ai/dsh-client-ui-renderer\"\n ],\n \"platform\": \"web\"\n }\n },\n \"scripts\": {\n \"build\": \"node scripts/build.mjs\",\n \"test\": \"node --test tests/*.test.mjs\",\n \"check\": \"npm run build && npm test && node scripts/check-package.mjs\",\n \"check:tarball\": \"npm run build && node scripts/check-package.mjs --tarball\",\n \"pack:release\": \"npm run build && npm pack --ignore-scripts\"\n },\n \"peerDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"react\": \"^18.2.0\"\n },\n \"devDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"esbuild\": \"^0.25.0\",\n \"react\": \"^18.2.0\",\n \"react-dom\": \"^18.2.0\"\n },\n \"license\": \"MIT\",\n \"keywords\": [\n \"dsh-plugin\",\n \"deepseek-harness\",\n \"draft-vault\",\n \"session-move\",\n \"trash-bin\"\n ]\n}\n", "/**\n * Draft vault (\u00A74-\u00A79): mirrors image drafts into IndexedDB and replays them\n * through the official paste pipeline. No official internals are touched:\n * capture listens at document-level `paste`/`drop`/file-input `change` (never\n * preventing or stopping the native flow), persistence is browser IndexedDB,\n * and restore re-dispatches a ClipboardEvent carrying a synthetic\n * `clipboardData` (Chromium ignores the ClipboardEventInit clipboardData and\n * exposes it only as a prototype accessor, so the property is shadowed with a\n * defined own property \u2014 the official handler only reads `items`/`getData`,\n * verified against the shipped build).\n *\n * Idempotency is DOM-based and safe by construction:\n * - seeding only happens into an EMPTY attachment rail;\n * - a seed is confirmed by the official handler calling preventDefault() (its\n * documented contract for accepted files);\n * - rail dropping to zero while the session view is mounted means \"sent or\n * user-deleted\" (\u00A7 FR3: both delete the backups); the view unmounting keeps\n * them \u2014 that is exactly the loss this vault repairs.\n */\n\nexport const DB_NAME = 'dsh-session-toolbox-vault';\nexport const STORE = 'blobs';\nexport const IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/webp', 'image/gif'];\n/** Marks our own synthetic paste so it is not re-captured as a new draft. */\nconst SEED_MARKER = Symbol.for('dsh-session-toolbox.seed');\n\nexport const MAX_FILE_BYTES = 10 * 1024 * 1024; // the official per-image admission cap\nexport const DEFAULT_LIMITS = Object.freeze({ enabled: true, maxSizeBytes: 200 * 1024 * 1024, ttlDays: 7 });\n\nexport function isImageFile(file) {\n\treturn file !== undefined && IMAGE_TYPES.includes(file.type) && file.size > 0 && file.size <= MAX_FILE_BYTES;\n}\n\n/** sha256 hex of bytes; crypto.subtle is absent on non-secure origins \u2192 size/name fallback hash. */\nexport async function contentHash(file) {\n\tif (globalThis.crypto?.subtle !== undefined) {\n\t\ttry {\n\t\t\tconst digest = await crypto.subtle.digest('SHA-256', await file.arrayBuffer());\n\t\t\treturn [...new Uint8Array(digest)].map(byte => byte.toString(16).padStart(2, '0')).join('');\n\t\t} catch { /* fall through */ }\n\t}\n\treturn `weak:${file.size}:${file.name}:${file.lastModified}`;\n}\n\nfunction openDb() {\n\tif (typeof indexedDB === 'undefined') throw new Error('IndexedDB unavailable');\n\treturn new Promise((resolve, reject) => {\n\t\tconst request = indexedDB.open(DB_NAME, 1);\n\t\trequest.onupgradeneeded = () => {\n\t\t\tconst database = request.result;\n\t\t\tif (!database.objectStoreNames.contains(STORE)) database.createObjectStore(STORE, { keyPath: 'key' });\n\t\t};\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('IndexedDB open failed'));\n\t});\n}\n\n/** Resolve with the request result; reject with its error. */\nfunction done(request) {\n\treturn new Promise((resolve, reject) => {\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('vault storage request failed'));\n\t});\n}\n\nfunction storeOp(database, mode, run) {\n\treturn new Promise((resolve, reject) => {\n\t\tlet outcome;\n\t\tconst transaction = database.transaction(STORE, mode);\n\t\ttransaction.onabort = () => reject(transaction.error ?? new Error('vault transaction aborted'));\n\t\ttransaction.onerror = () => reject(transaction.error ?? new Error('vault transaction failed'));\n\t\ttry { outcome = run(transaction.objectStore(STORE)); } catch (error) { reject(error); return; }\n\t\tPromise.resolve(outcome).then(resolve, reject);\n\t});\n}\n\nfunction emitter() {\n\tconst listeners = new Map();\n\treturn {\n\t\ton(event, fn) { if (!listeners.has(event)) listeners.set(event, new Set()); listeners.get(event).add(fn); },\n\t\toff(event, fn) { listeners.get(event)?.delete(fn); },\n\t\temit(event, detail) { for (const fn of [...(listeners.get(event) ?? [])]) { try { fn(detail); } catch {} } },\n\t};\n}\n\nfunction fakeClipboardData(files) {\n\tconst items = files.map(f => ({ kind: 'file', type: f.type, getAsFile: () => f }));\n\treturn { items, files, length: files.length, types: ['Files'], getData: () => '', setData: () => false, clearData: () => {} };\n}\n\nasync function blobToFile(record) {\n\tif (record.blob instanceof File) return record.blob;\n\tconst blob = record.blob instanceof Blob ? record.blob : new Blob([record.blob ?? new Uint8Array()], { type: record.type });\n\treturn new File([blob], record.name ?? '', { type: record.type, lastModified: record.createdAt });\n}\n\n/**\n * @param options - {endpoint, log, now, fetchImpl}\n */\nexport function createVault(options = {}) {\n\tconst log = options.log ?? (() => {});\n\tconst now = options.now ?? (() => Date.now());\n\tconst fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));\n\tconst bus = emitter();\n\tlet dbPromise = null;\n\tlet limits = { ...DEFAULT_LIMITS };\n\tlet lastNotice = '';\n\tlet started = false;\n\tlet teardown = null;\n\tlet hostKey = '';\n\n\tconst db = () => dbPromise ??= openDb();\n\tfunction notice(message) {\n\t\tlastNotice = message;\n\t\tbus.emit('notice', message);\n\t\ttry { globalThis.window?.dispatchEvent?.(new CustomEvent('dsh-session-toolbox-notice', { detail: message })); } catch { /* test envs */ }\n\t}\n\tfunction changed() { bus.emit('change'); }\n\n\t// ---- IndexedDB CRUD -----------------------------------------------------\n\tconst allRecords = async () => {\n\t\tconst database = await db();\n\t\treturn await storeOp(database, 'readonly', store => done(store.getAll()));\n\t};\n\tconst putRecord = async record => {\n\t\tconst database = await db();\n\t\tawait storeOp(database, 'readwrite', store => done(store.put(record)));\n\t};\n\tconst dropRecord = async key => {\n\t\tconst database = await db();\n\t\tawait storeOp(database, 'readwrite', store => done(store.delete(key)));\n\t};\n\n\t/** Best-effort host manifest mirror (status display only; failures invisible). */\n\tasync function syncHost() {\n\t\tconst endpoint = options.endpoint ?? '/.well-known/dsh-session-toolbox';\n\t\ttry {\n\t\t\tif (hostKey === '') {\n\t\t\t\tconst probe = await fetchImpl(endpoint, { cache: 'no-store' });\n\t\t\t\thostKey = (await probe.json()).key ?? '';\n\t\t\t}\n\t\t\tconst records = (await allRecords()).slice(-500).map(({ key, sessionId, attachmentId, name, size, hash, seeded, createdAt }) =>\n\t\t\t\t({ key, sessionId, attachmentId, name, size, hash, seeded, createdAt }));\n\t\t\tawait fetchImpl(endpoint, { method: 'POST', cache: 'no-store', headers: { 'content-type': 'application/json', 'x-session-toolbox-key': hostKey },\n\t\t\t\tbody: JSON.stringify({ type: 'vault.sync', records }) });\n\t\t} catch { /* the manifest is cosmetic; the vault itself is IndexedDB */ }\n\t}\n\n\t// ---- FR4 capacity + TTL --------------------------------------------------\n\tasync function sweep() {\n\t\tconst records = await allRecords();\n\t\tconst ttlMs = limits.ttlDays * 24 * 60 * 60 * 1000;\n\t\tconst keep = [];\n\t\tconst remove = [];\n\t\tfor (const record of records) (now() - (record.createdAt ?? 0) > ttlMs ? remove : keep).push(record);\n\t\tkeep.sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0));\n\t\tlet bytes = keep.reduce((total, record) => total + (record.size ?? 0), 0);\n\t\twhile (bytes > limits.maxSizeBytes && keep.length > 0) {\n\t\t\tconst oldest = keep.shift();\n\t\t\tbytes -= oldest.size ?? 0;\n\t\t\tremove.push(oldest);\n\t\t}\n\t\tfor (const record of remove) await dropRecord(record.key);\n\t\tif (remove.length > 0) notice(`\u8349\u7A3F\u4FDD\u9669\u7BB1\u5DF2\u6E05\u7406 ${remove.length} \u9879\uFF08\u8D85\u671F/\u8D85\u5BB9\u91CF\uFF09`);\n\t\tchanged();\n\t\treturn { removed: remove.length, kept: keep.length, bytes };\n\t}\n\n\t// ---- FR1 capture ---------------------------------------------------------\n\tasync function intake(sessionId, files) {\n\t\tif (!limits.enabled || sessionId === undefined || sessionId === '') return;\n\t\tconst existing = (await allRecords()).filter(record => record.sessionId === sessionId);\n\t\tconst seen = new Set(existing.map(record => record.hash));\n\t\tlet added = 0;\n\t\tfor (const item of files) {\n\t\t\tif (!isImageFile(item)) continue;\n\t\t\tconst hash = await contentHash(item);\n\t\t\tif (seen.has(hash)) continue; // same image already held for this session\n\t\t\tseen.add(hash);\n\t\t\tconst id = (globalThis.crypto?.randomUUID?.() ?? `id-${now()}-${Math.random().toString(36).slice(2)}`);\n\t\t\tconst record = { key: `${sessionId}__${id}`, sessionId, attachmentId: id,\n\t\t\t\tname: item.name || '', type: item.type, size: item.size, hash, createdAt: now(), seeded: false };\n\t\t\trecord.blob = item;\n\t\t\tawait putRecord(record);\n\t\t\tadded += 1;\n\t\t}\n\t\tif (added > 0) notice(`\u5DF2\u5907\u4EFD ${added} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);\n\t\tvoid syncHost();\n\t}\n\n\tconst readImages = event => [...(event.clipboardData?.files ?? event.dataTransfer?.files ?? [])].filter(isImageFile);\n\tconst fileInputImages = event => {\n\t\tconst target = event.target;\n\t\tif (!(target instanceof HTMLInputElement) || target.type !== 'file' || target.files === null) return [];\n\t\treturn [...target.files].filter(isImageFile);\n\t};\n\n\t// ---- DOM discovery (official composer markers) ----------------------------\n\tfunction currentSession() {\n\t\treturn document.querySelector?.('[data-conversation-session]')?.getAttribute?.('data-conversation-session') ?? '';\n\t}\n\tfunction composerRoot() {\n\t\treturn document.querySelector?.('[data-conversation-session] [contenteditable=\"true\"]') ?? undefined;\n\t}\n\tfunction attachmentRail(editor) {\n\t\t// Official structure: attachment cards are imgs with blob: srcs sitting\n\t\t// above the contenteditable root inside the composer bar subtree.\n\t\tconst bar = editor?.closest?.('[data-composer-bar], [data-conversation-region=\"composer\"]') ?? editor?.parentElement?.parentElement?.parentElement;\n\t\tif (bar?.querySelectorAll === undefined) return undefined;\n\t\treturn [...bar.querySelectorAll('img')].filter(img => img.getAttribute?.('src')?.startsWith('blob:'));\n\t}\n\n\t// ---- FR2 restore -----------------------------------------------------------\n\tasync function seedSession(sessionId = currentSession()) {\n\t\tif (sessionId === undefined || sessionId === '' || !limits.enabled) return false;\n\t\tconst editor = composerRoot();\n\t\tif (editor === undefined) return false;\n\t\tconst rail = attachmentRail(editor);\n\t\tif (rail === undefined) return false;\n\t\tif (rail.length > 0) return true; // non-empty: official still holds them (or was seeded before)\n\t\tconst pending = (await allRecords()).filter(record => record.sessionId === sessionId);\n\t\tif (pending.length === 0) return true;\n\t\tconst files = [];\n\t\tfor (const record of pending) {\n\t\t\ttry { files.push(await blobToFile(record)); }\n\t\t\tcatch { await dropRecord(record.key); } // unreadable backup \u2014 drop and move on\n\t\t}\n\t\tif (files.length === 0) { changed(); return false; }\n\t\tconst event = new ClipboardEvent('paste', { bubbles: true, cancelable: true });\n\t\tObject.defineProperty(event, 'clipboardData', { value: fakeClipboardData(files) });\n\t\t// Mark as a restore so our own intake (which observes the same event)\n\t\t// never stores the replayed images as a second fresh draft.\n\t\ttry { Object.defineProperty(event, SEED_MARKER, { value: true }); } catch { /* ignore */ }\n\t\ttry { editor.focus?.(); } catch { /* ignore */ }\n\t\teditor.dispatchEvent(event);\n\t\tconst accepted = event.defaultPrevented === true;\n\t\tif (accepted) notice(`\u5DF2\u6062\u590D ${files.length} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);\n\t\telse log('warn', 'synthetic paste was not accepted; vault stays idle for this session');\n\t\tchanged();\n\t\treturn accepted;\n\t}\n\n\t// ---- FR3 lifecycle ---------------------------------------------------------\n\tfunction watchSession(sessionId, editor) {\n\t\tlet rail = attachmentRail(editor)?.length ?? 0;\n\t\tconst scope = editor.closest?.('[data-conversation-content]') ?? editor;\n\t\tconst check = async () => {\n\t\t\tconst next = attachmentRail(editor)?.length ?? rail;\n\t\t\tif (next === rail) return;\n\t\t\tif (rail > 0 && next === 0 && scope?.isConnected === true) {\n\t\t\t\t// Sent or manually removed while mounted \u2192 backups retire (\u00A7 FR3).\n\t\t\t\tfor (const record of await allRecords()) if (record.sessionId === sessionId) await dropRecord(record.key);\n\t\t\t\tnotice('\u672C\u4F1A\u8BDD\u8349\u7A3F\u56FE\u7247\u5DF2\u6E05\u7A7A\uFF0C\u5907\u4EFD\u540C\u6B65\u79FB\u9664');\n\t\t\t\tvoid syncHost();\n\t\t\t}\n\t\t\trail = next;\n\t\t\tchanged();\n\t\t};\n\t\tif (typeof MutationObserver !== 'function') return () => {};\n\t\tconst observer = new MutationObserver(() => void check());\n\t\tobserver.observe(scope, { subtree: true, childList: true });\n\t\treturn () => observer.disconnect();\n\t}\n\n\tfunction watchMounts() {\n\t\tlet boundSession = '', dispose = () => {};\n\t\tconst sync = async () => {\n\t\t\tconst sessionId = currentSession();\n\t\t\tconst editor = composerRoot();\n\t\t\tif (sessionId === '' || editor === undefined) return;\n\t\t\tif (sessionId === boundSession && editor.isConnected !== false) return;\n\t\t\tboundSession = sessionId;\n\t\t\tdispose();\n\t\t\tdispose = watchSession(sessionId, editor);\n\t\t\tsetTimeout(() => void seedSession(sessionId).catch(error => log('warn', `vault seed failed: ${error.message}`)), 120);\n\t\t\tvoid sweep().catch(() => {});\n\t\t};\n\t\tconst interval = setInterval(() => void sync(), 5000);\n\t\tif (typeof MutationObserver !== 'function') return () => clearInterval(interval);\n\t\tconst observer = new MutationObserver(() => void sync());\n\t\tobserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-conversation-session'] });\n\t\treturn () => { observer.disconnect(); clearInterval(interval); dispose(); };\n\t}\n\n\treturn {\n\t\ton: bus.on, off: bus.off,\n\t\tget lastNotice() { return lastNotice; },\n\t\tcurrentSession,\n\t\trecords: allRecords,\n\t\tcountFor: async sessionId => {\n\t\t\tconst records = await allRecords();\n\t\t\treturn sessionId === undefined || sessionId === '' ? records.length : records.filter(record => record.sessionId === sessionId).length;\n\t\t},\n\t\tstart: async ({ limits: override } = {}) => {\n\t\t\tif (started || typeof document === 'undefined') return false;\n\t\t\tstarted = true;\n\t\t\tif (override !== undefined) limits = { ...limits, ...override };\n\t\t\telse {\n\t\t\t\ttry {\n\t\t\t\t\tconst res = await fetchImpl(`${options.endpoint ?? '/.well-known/dsh-session-toolbox'}`, { cache: 'no-store' });\n\t\t\t\t\tconst next = await res.json();\n\t\t\t\t\tlimits = { ...limits, ...(next.settings?.draftVault ?? {}) };\n\t\t\t\t} catch { /* defaults hold */ }\n\t\t\t}\n\t\t\tconst capture = event => {\n\t\t\t\tif (event[SEED_MARKER] === true) return;\n\t\t\t\tvoid intake(currentSession(), readImages(event)).catch(() => {});\n\t\t\t};\n\t\t\tdocument.addEventListener('paste', capture, true);\n\t\t\tdocument.addEventListener('drop', capture, true);\n\t\t\tdocument.addEventListener('change', event => void intake(currentSession(), fileInputImages(event)).catch(() => {}), true);\n\t\t\tteardown = watchMounts();\n\t\t\tvoid sweep().catch(() => {});\n\t\t\tlog('info', 'draft vault started');\n\t\t\treturn true;\n\t\t},\n\t\tstop: () => { if (started) { started = false; teardown?.(); teardown = null; } },\n\t\tsetLimits: next => {\n\t\t\tlimits = { ...limits, ...next };\n\t\t\tif (limits.enabled === false) void (async () => { for (const record of await allRecords()) await dropRecord(record.key); notice('\u8349\u7A3F\u4FDD\u9669\u7BB1\u5DF2\u505C\u7528\uFF0C\u5B58\u91CF\u5DF2\u6E05\u7A7A'); })();\n\t\t},\n\t\tsweep,\n\t\tseedSession,\n\t\tintake,\n\t\twatchMounts,\n\t};\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * dsh-session-toolbox browser plugin: management page (\u00A74 FR5/FR6, \u00A710 MR6, \u00A711\n * TB2), session context-menu entries, the input-dock vault badge, and the\n * draft vault itself (capture + IndexedDB + synthetic-paste restore; route A).\n * Every official surface is probed (`ctx.get?.`, guarded registrations); no\n * path here may take the native composer down with it (NFR3).\n */\nimport { useEffect, useRef, useState } from 'react';\nimport pkg from '../package.json' with { type: 'json' };\nconst pluginVersion = pkg.version;\nimport { createVault } from './draft-vault.js';\n\nexport const name = 'dsh-session-toolbox-client';\n// `locale` is a real service, so it must be injected before use: cordis throws\n// on property access for a service that was never injected (it does not return\n// undefined), which is what surfaced as \"cannot get property \\\"locale\\\" without\n// inject\" on the plugin page.\nexport const inject = ['slots', 'locale'];\nexport const NS = 'dsh-session-toolbox';\nconst ENDPOINT = `/.well-known/${NS}`;\n\nlet vault; // browser-side singleton (IndexedDB + host manifest)\nlet directoryPicker; // ctx.get('directoryPicker') probed at apply time\nconst store = { state: null, error: '', listeners: new Set() };\n\nasync function readResponse(res) {\n\tlet next;\n\ttry { next = JSON.parse(await res.text()); }\n\tcatch { throw new Error(`\u63D2\u4EF6\u540E\u53F0\u672A\u5C31\u7EEA\uFF08HTTP ${res.status}\uFF09\uFF1B\u5B89\u88C5\u6216\u91CD\u542F\u671F\u95F4\u4F1A\u81EA\u52A8\u91CD\u8BD5`); }\n\tif (next === null || typeof next !== 'object' || Array.isArray(next)) throw new Error('\u63D2\u4EF6\u540E\u53F0\u8FD4\u56DE\u4E86\u65E0\u6548\u6570\u636E');\n\tif (!res.ok) throw new Error(next.error ?? `\u64CD\u4F5C\u5931\u8D25\uFF08HTTP ${res.status}\uFF09`);\n\treturn next;\n}\n\n/** GET the shared snapshot; also (re)binds the write key the host rotates. */\nexport let sessionKey = '';\nexport async function loadState() {\n\ttry {\n\t\tconst next = await readResponse(await fetch(ENDPOINT, { cache: 'no-store' }));\n\t\tstore.state = next; store.error = ''; sessionKey = next.key ?? sessionKey;\n\t} catch (error) { store.error = error.message; }\n\tfor (const listener of store.listeners) listener();\n\treturn store.state;\n}\nexport async function callHost(input) {\n\tif (!sessionKey) await loadState();\n\tconst res = await readResponse(await fetch(ENDPOINT, {\n\t\tmethod: 'POST', cache: 'no-store',\n\t\theaders: { 'content-type': 'application/json', 'x-session-toolbox-key': sessionKey },\n\t\tbody: JSON.stringify(input),\n\t}));\n\tsessionKey = res.key ?? sessionKey;\n\tfor (const listener of store.listeners) listener();\n\treturn res;\n}\n\nexport function apply(ctx) {\n\tdirectoryPicker = ctx.get?.('directoryPicker');\n\tconst vaultInstance = vault ??= createVault({\n\t\tendpoint: ENDPOINT,\n\t\tlog: (level, message) => { try { (level === 'error' ? console.warn : console.log)(`[${NS}]`, message); } catch {} },\n\t});\n\tvoid loadState();\n\tconst timer = setInterval(() => void loadState(), 30000);\n\tctx.effect?.(() => () => clearInterval(timer));\n\tvaultInstance.on('change', () => void loadState());\n\tvoid vaultInstance.start();\n\t// `locale` is injected above, so the service is guaranteed here; registering\n\t// inside ctx.effect keeps the dictionaries disposable across hot reloads\n\t// (same shape as the official client plugins).\n\tctx.effect(() => ctx.locale.register(NS, {\n\t\tzh: { 'menu.move': '\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE\u2026', 'menu.trash': '\u5F52\u6863\u5230\u5F52\u6863\u6C60' },\n\t\ten: { 'menu.move': 'Move to another project\u2026', 'menu.trash': 'Archive' },\n\t}), 'session-toolbox: dictionaries');\n\n\t// Keyed slot (one occupant = package name): the plugin settings page.\n\tctx.slots.inject('plugins.bundle.config', () => ctx.slots.register({ name: 'plugins.bundle.config', key: NS }, KitPage));\n\t// Session context menu: move + trash entries beside the official four.\n\tctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register({ name: 'sidebar.workspaces.session.menu.item', id: 'session-toolbox-move', order: 500, locale: NS }, MoveSessionMenuItem));\n\tctx.slots.inject('sidebar.workspaces.session.menu.item', () => ctx.slots.register({ name: 'sidebar.workspaces.session.menu.item', id: 'session-toolbox-trash', order: 600, locale: NS }, TrashSessionMenuItem));\n\t// Input dock badge (\u00A74 FR5): how many image drafts are backed up here.\n\tctx.slots.inject('conversation.input.dock', () => ctx.slots.register({ name: 'conversation.input.dock', id: 'session-toolbox-vault-badge', order: 5, locale: NS }, VaultBadge));\n}\n\nfunction useKit() {\n\tconst [, bump] = useState(0);\n\tuseEffect(() => {\n\t\tconst listener = () => bump(n => n + 1);\n\t\tstore.listeners.add(listener);\n\t\tvoid loadState();\n\t\treturn () => store.listeners.delete(listener);\n\t}, []);\n\treturn store;\n}\n\nconst styles = `\n.sk{--sk-accent:var(--dsw-alias-state-active-primary,#3a71db);--sk-border:var(--dsw-alias-border-l2,#444);--sk-bg:var(--dsw-alias-bg-layer-1,#202020);--sk-card:var(--dsw-alias-bg-module-platform,#282828);--sk-text:var(--dsw-alias-label-primary,#f4f4f4);--sk-muted:var(--dsw-alias-label-secondary,#b0b0b0);color:var(--sk-text);font:inherit;font-size:14px;line-height:1.6;max-width:1120px}\n.sk *{box-sizing:border-box}.sk h2,.sk h3,.sk p{margin:0}.sk h2{font-size:22px}.sk h3{font-size:16px}.sk .muted,.sk small{color:var(--sk-muted)}\n.sk .box{border:1px solid var(--sk-border);border-radius:12px;padding:18px 20px;background:var(--sk-card);display:flex;flex-direction:column;gap:14px}\n.sk .row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}.sk .stack{display:flex;flex-direction:column;gap:16px}\n.sk button,.sk input,.sk select{font:inherit;color:inherit;border:1px solid var(--sk-border);border-radius:8px;background:var(--sk-bg);min-height:36px;padding:7px 12px}\n.sk button{cursor:pointer}.sk button:hover{background:var(--sk-card)}.sk button:disabled{opacity:.45;cursor:default}\n.sk button.primary{background:var(--sk-accent);color:#fff;border-color:transparent}\n.sk table{width:100%;border-collapse:collapse;font-size:13px}.sk th,.sk td{border-bottom:1px solid var(--sk-border);padding:8px;text-align:left}.sk th{color:var(--sk-muted);font-weight:500}\n.sk .error{color:var(--dsw-alias-state-error-primary,#ef9393)}.sk .good{color:var(--dsw-alias-state-success-primary,#77ca9d)}\n.sk .pill{border:1px solid var(--sk-border);border-radius:100px;padding:2px 10px;font-size:12px;background:var(--sk-bg);display:inline-block}\n.sk .mono{font:12px ui-monospace,monospace;overflow-wrap:anywhere}\n.sk dialog{color:var(--sk-text);background:var(--sk-bg);border:1px solid var(--sk-border);border-radius:14px;width:min(560px,calc(100vw - 32px));padding:0}\n.sk dialog::backdrop{background:#0007}.sk .modal-head{padding:16px 20px;border-bottom:1px solid var(--sk-border);display:flex;justify-content:space-between;align-items:center}\n.sk .modal-body{padding:20px;display:flex;flex-direction:column;gap:14px}\n.sk input.grow{flex:1;min-width:0}\n`;\n\nexport function KitPage() {\n\tconst { state, error } = useKit();\n\tconst [busy, setBusy] = useState(false);\n\tconst [feedback, setFeedback] = useState('');\n\tconst say = message => { setFeedback(message); setTimeout(() => setFeedback(''), 8000); };\n\tconst act = async (input, ok) => {\n\t\tsetBusy(true);\n\t\ttry { const result = await callHost(input); if (ok) say(ok); await loadState(); setBusy(false); return result; }\n\t\tcatch (err) { say(`\u5931\u8D25\uFF1A${err.message}`); setBusy(false); throw err; }\n\t};\n\tconst settings = state?.settings;\n\treturn (\n\t\t<div className=\"sk\">\n\t\t\t<style>{styles}</style>\n\t\t\t<h2>\u4F1A\u8BDD\u5DE5\u5177\u7BB1</h2>\n\t\t\t<p className=\"muted\">\u8349\u7A3F\u4FDD\u9669\u7BB1 \u00B7 \u4F1A\u8BDD\u5207\u6362\u9879\u76EE \u00B7 \u5F52\u6863\u6C60 \u2014 v{pluginVersion}\u3002\u6570\u636E\u4EC5\u5B58\u672C\u673A\uFF08\u6D4F\u89C8\u5668 IndexedDB + ~/.dsh/session-toolbox/\uFF09\u3002</p>\n\t\t\t{error ? <p className=\"error\">\u540E\u53F0\u8FDE\u63A5\u5931\u8D25\uFF1A{error}</p> : null}\n\t\t\t{feedback ? <p className=\"good\">{feedback}</p> : null}\n\t\t\t<section className=\"box\">\n\t\t\t\t<h3>\u8349\u7A3F\u4FDD\u9669\u7BB1\uFF08\u56FE\u7247\u8349\u7A3F\u6301\u4E45\u5316\uFF09</h3>\n\t\t\t\t{settings ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"row\">\n\t\t\t\t\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={settings.draftVault?.enabled !== false} disabled={busy}\n\t\t\t\t\t\t\t\tonChange={e => void act({ type: 'settings.update', patch: { draftVault: { ...settings.draftVault, enabled: e.target.checked } } }, e.target.checked ? '\u5DF2\u542F\u7528\u8349\u7A3F\u4FDD\u9669\u7BB1' : '\u5DF2\u505C\u7528\u8349\u7A3F\u4FDD\u9669\u7BB1\uFF08\u6D4F\u89C8\u5668\u4FA7\u5373\u65F6\u751F\u6548\uFF09').catch(() => {})} /> \u542F\u7528</label>\n\t\t\t\t\t\t\t<span className=\"pill\">\u5BB9\u91CF {Math.round((settings.draftVault?.maxSizeBytes ?? 0) / 1048576)} MB</span>\n\t\t\t\t\t\t\t<span className=\"pill\">\u4FDD\u7559 {settings.draftVault?.ttlDays ?? 7} \u5929</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"row\">\n\t\t\t\t\t\t\t<span className=\"muted\">\u6E05\u5355\uFF1A\u5DF2\u5907\u4EFD {state?.vault?.records ?? 0} \u9879 \u00B7 \u7EA6 {fmtBytes(state?.vault?.bytes ?? 0)}\uFF08\u56FE\u7247\u5B57\u8282\u5728 IndexedDB\uFF0C\u8D85\u51FA\u4E0A\u9650\u6309\u6700\u65E7\u6E05\u7406\uFF0C\u4E8B\u4EF6\u4F1A\u663E\u793A\u5728\u8FD9\u91CC\uFF09</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{state?.vault?.notice ? <p className=\"muted\">{state.vault.notice}</p> : null}\n\t\t\t\t\t\t<div className=\"row\"><button disabled={busy} onClick={() => void act({ type: 'vault.sweep' }, '\u5DF2\u6309 TTL/\u5BB9\u91CF\u6E05\u7406').catch(() => {})}>\u7ACB\u5373\u6574\u7406</button></div>\n\t\t\t\t\t</>\n\t\t\t\t) : <p className=\"muted\">\u7B49\u5F85\u540E\u53F0\u6570\u636E\u2026</p>}\n\t\t\t</section>\n\t\t\t<MoveSection act={act} busy={busy} say={say} state={state} />\n\t\t\t<TrashSection act={act} busy={busy} say={say} state={state} />\n\t\t</div>\n\t);\n}\n\nfunction MoveSection({ act, busy, say, state }) {\n\tconst [sessionId, setSessionId] = useState('');\n\tconst [toCwd, setToCwd] = useState('');\n\tconst [createTarget, setCreateTarget] = useState(false);\n\tconst [plan, setPlan] = useState(null);\n\tconst journal = state?.journal;\n\t// Known projects, so the target can be picked by name instead of typed as a path.\n\tconst projects = state?.projects ?? [];\n\t// Which project the typed session currently belongs to (computed host-side).\n\tconst [fromProject, setFromProject] = useState(null);\n\tuseEffect(() => {\n\t\tif (!sessionId) { setFromProject(null); return; }\n\t\tlet alive = true;\n\t\tvoid callHost({ type: 'project.of', sessionId })\n\t\t\t.then(result => { if (alive) setFromProject(result?.project ?? null); })\n\t\t\t.catch(() => { if (alive) setFromProject(null); });\n\t\treturn () => { alive = false; };\n\t}, [sessionId]);\n\tconst pick = async () => {\n\t\ttry {\n\t\t\tconst result = await directoryPicker?.pick?.();\n\t\t\tconst value = result?.ok ? result.value : undefined;\n\t\t\tif (typeof value === 'string' && value) setToCwd(value);\n\t\t\telse say('\u5F53\u524D DSH \u672A\u63D0\u4F9B\u76EE\u5F55\u9009\u62E9\u5668\uFF08\u6216\u5DF2\u53D6\u6D88\uFF09\uFF0C\u8BF7\u624B\u52A8\u8F93\u5165\u7EDD\u5BF9\u8DEF\u5F84');\n\t\t} catch (error) { say(`\u76EE\u5F55\u9009\u62E9\u5931\u8D25\uFF1A${error.message}`); }\n\t};\n\treturn (\n\t\t<section className=\"box\">\n\t\t\t<h3>\u4F1A\u8BDD\u5207\u6362\u9879\u76EE</h3>\n\t\t\t{journal && <p className=\"error\">\u5B58\u5728\u672A\u5B8C\u6210\u7684\u8FC1\u79FB\uFF1A{journal.sessionId} \u2192 {journal.toCwd}\uFF08\u5DF2\u5B8C\u6210 {journal.done?.join('/') || '\u65E0'}\uFF09\u3002\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'move.complete' }, '\u8FC1\u79FB\u5DF2\u524D\u6EDA\u5B8C\u6210').catch(() => {})}>\u5B8C\u6210</button>\n\t\t\t\t{' '}<button disabled={busy} onClick={() => void act({ type: 'move.rollback' }, '\u8FC1\u79FB\u5DF2\u56DE\u6EDA').catch(() => {})}>\u56DE\u6EDA</button></p>}\n\t\t\t<div className=\"row\">\n\t\t\t\t<input className=\"grow mono\" placeholder=\"session-\u2026\uFF08\u6216\u4ECE\u4F1A\u8BDD\u53F3\u952E\u83DC\u5355\u53D1\u8D77\uFF09\" value={sessionId} onChange={e => { setSessionId(e.target.value); setPlan(null); }} />\n\t\t\t\t{projects.length > 0 && (\n\t\t\t\t\t<select className=\"grow\" value={projects.some(p => p.path === toCwd) ? toCwd : ''}\n\t\t\t\t\t\tonChange={e => { setToCwd(e.target.value); setPlan(null); }}>\n\t\t\t\t\t\t<option value=\"\">\u9009\u62E9\u76EE\u6807\u9879\u76EE\u2026</option>\n\t\t\t\t\t\t{projects.map(project => (\n\t\t\t\t\t\t\t<option key={project.id} value={project.path} disabled={fromProject?.path === project.path}>\n\t\t\t\t\t\t\t\t{project.title}\uFF08{project.sessions} \u4E2A\u4F1A\u8BDD\uFF09{fromProject?.path === project.path ? ' \u2014 \u5F53\u524D\u9879\u76EE' : ''}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</select>\n\t\t\t\t)}\n\t\t\t\t<input className=\"grow mono\" placeholder=\"\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84\" value={toCwd} onChange={e => { setToCwd(e.target.value); setPlan(null); }} />\n\t\t\t\t<button onClick={() => void pick()} disabled={busy}>\u6D4F\u89C8\u2026</button>\n\t\t\t</div>\n\t\t\t{sessionId && (\n\t\t\t\t<p className=\"muted\">\n\t\t\t\t\t\u5F53\u524D\u9879\u76EE\uFF1A{fromProject === null ? '\uFF08\u672A\u627E\u5230\u8BE5\u4F1A\u8BDD\uFF0C\u6216\u5B83\u4E0D\u5C5E\u4E8E\u4EFB\u4F55\u9879\u76EE\uFF09'\n\t\t\t\t\t\t: <><strong>{fromProject.title}</strong>{' '}<span className=\"mono\">{fromProject.path}</span></>}\n\t\t\t\t</p>\n\t\t\t)}\n\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={createTarget} onChange={e => setCreateTarget(e.target.checked)} /> \u76EE\u6807\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA</label>\n\t\t\t<div className=\"row\">\n\t\t\t\t<button disabled={busy || !sessionId || !toCwd} onClick={() => { setBusy(true); setPlan(null); void callHost({ type: 'preflight', sessionId, toCwd, createTarget })\n\t\t\t\t\t.then(result => setPlan(result)).catch(error => say(`\u4F53\u68C0\u5931\u8D25\uFF1A${error.message}`)).finally(() => setBusy(false)); }}>\u4F53\u68C0</button>\n\t\t\t\t<button className=\"primary\" disabled={busy || plan?.toProject === undefined} onClick={() => void act({ type: 'move', sessionId, toCwd, createTarget }, '\u8FC1\u79FB\u5B8C\u6210\uFF1B\u5176\u5B83\u7A97\u53E3\u8BF7\u91CD\u5F00\u8BE5\u4F1A\u8BDD').catch(() => {})}>\u8FC1\u79FB</button>\n\t\t\t</div>\n\t\t\t{plan?.toProject && (\n\t\t\t\t<pre className=\"mono muted\" style={{ whiteSpace: 'pre-wrap' }}>{JSON.stringify({ \u4ECE: plan.fromCwd, \u5230: plan.toCwd, \u539F\u76EE\u5F55: plan.fromProject, \u65B0\u76EE\u5F55: plan.toProject, \u65E5\u5FD7\u6587\u4EF6: plan.logName }, null, 2)}</pre>\n\t\t\t)}\n\t\t\t<p className=\"muted\">\u53EA\u6539 DSH \u5185\u90E8\u5F52\u5C5E\u4E0E\u4F1A\u8BDD\u5934\uFF1B\u5386\u53F2\u4E0D\u53D8\uFF0C\u65E7\u6D88\u606F\u4E2D\u7684\u7EDD\u5BF9\u8DEF\u5F84\u4ECD\u6307\u5411\u539F\u9879\u76EE\uFF08\u5DF2\u77E5\u8BED\u4E49\uFF09\u3002\u8FC1\u79FB\u524D\u8BF7\u5173\u95ED\u8BE5\u4F1A\u8BDD\u3002</p>\n\t\t</section>\n\t);\n}\n\nfunction TrashSection({ act, busy, say, state }) {\n\tconst items = state?.trash ?? [];\n\treturn (\n\t\t<section className=\"box\">\n\t\t\t<h3>\u5F52\u6863\u6C60</h3>\n\t\t\t<p className=\"muted\">\n\t\t\t\tDSH \u6CA1\u6709\u72EC\u7ACB\u7684\u300C\u5220\u9664\u4F1A\u8BDD\u300D\u2014\u2014\u4F1A\u8BDD\u53EA\u80FD<b>\u5F52\u6863</b>\u3002\u8FD9\u91CC\u5C31\u662F\u5B98\u65B9\u7684\u5F52\u6863\u6C60\uFF1A\u5165\u6C60\u7684\u4F1A\u8BDD\u4ECD\u7136\u662F\u5B98\u65B9\u5F52\u6863\u72B6\u6001\uFF08\u4FA7\u680F\u7684\u5F52\u6863\u89C6\u56FE\u91CC\u540C\u6837\u80FD\u770B\u5230\uFF09\uFF0C\u672C\u63D2\u4EF6\u989D\u5916\u8BB0\u5F55\u4E86\u5B83\u5165\u6C60\u65F6\u7684<b>\u9879\u76EE\u5F52\u5C5E\u3001\u5217\u8868\u4F4D\u7F6E\u4E0E\u7F6E\u9876\u72B6\u6001</b>\uFF0C\u56E0\u6B64\u53EF\u4EE5\u4E00\u952E\u8FD8\u539F\u5230\u539F\u4F4D\u3002\n\t\t\t\t\u5F52\u6863\u9879\u76EE\u5219\u4FDD\u7559\u5168\u90E8\u4F1A\u8BDD\u65E5\u5FD7\uFF0C\u53EA\u79FB\u9664\u9879\u76EE\u6CE8\u518C\u8868\u9879\u3002\u5F7B\u5E95\u5220\u9664\u4F1A\u5148\u79FB\u5165\u6682\u5B58\u76EE\u5F55\u518D\u6E05\u7406\uFF0C\u4E2D\u65AD\u53EF\u7528 CLI \u68C0\u51FA\u3002\n\t\t\t</p>\n\t\t\t{items.length === 0 ? <p className=\"muted\">\uFF08\u7A7A\uFF09</p> : (\n\t\t\t\t<table>\n\t\t\t\t\t<thead><tr><th>\u7C7B\u578B</th><th>\u5BF9\u8C61</th><th>\u6807\u9898 / \u8DEF\u5F84</th><th>\u5165\u6C60\u65F6\u95F4</th><th /></tr></thead>\n\t\t\t\t\t<tbody>{items.map(item => (\n\t\t\t\t\t\t<tr key={item.itemId}>\n\t\t\t\t\t\t\t<td>{item.kind === 'session' ? '\u4F1A\u8BDD' : '\u9879\u76EE'}</td>\n\t\t\t\t\t\t\t<td className=\"mono\">{shortId(item.targetId)}</td>\n\t\t\t\t\t\t\t<td className=\"mono\">{item.kind === 'session' ? item.snapshot.title || '\uFF08\u65E0\u6807\u9898\uFF09' : item.snapshot.path}</td>\n\t\t\t\t\t\t\t<td>{new Date(item.trashedAt).toLocaleString('zh-CN', { hour12: false })}</td>\n\t\t\t\t\t\t\t<td className=\"row\">\n\t\t\t\t\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.restore', itemId: item.itemId }, '\u5DF2\u8FD8\u539F\u5230\u539F\u4F4D').catch(() => {})}>\u8FD8\u539F</button>\n\t\t\t\t\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.purge', itemId: item.itemId }, '\u5DF2\u5F7B\u5E95\u5220\u9664').catch(() => {})}>\u5F7B\u5E95\u5220\u9664</button>\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>))}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t)}\n\t\t\t<div className=\"row\">\n\t\t\t\t<span className=\"muted\">\u4FDD\u7559 {state?.settings?.trash?.ttlDays ?? 30} \u5929\uFF1B\u5230\u671F\u81EA\u52A8\u79FB\u51FA{state?.settings?.trash?.autoPurge ? '\uFF08\u5DF2\u5F00\uFF09' : '\uFF08\u672A\u5F00\uFF0C\u4EC5\u63D0\u9192\uFF09'}\u3002</span>\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'settings.update', patch: { trash: { ...state?.settings?.trash, autoPurge: !(state?.settings?.trash?.autoPurge) } } }, '\u7B56\u7565\u5DF2\u66F4\u65B0').catch(() => {})}>\u5207\u6362\u81EA\u52A8\u6E05\u7406</button>\n\t\t\t\t<button disabled={busy} onClick={() => void act({ type: 'trash.sweep' }, '\u5DF2\u6309 TTL \u5DE1\u68C0').catch(() => {})}>\u5DE1\u68C0\u5230\u671F\u9879</button>\n\t\t\t</div>\n\t\t</section>\n\t);\n}\n\nconst shortId = id => String(id).length > 18 ? `${String(id).slice(0, 8)}\u2026${String(id).slice(-6)}` : String(id);\nconst fmtBytes = n => n >= 1048576 ? `${(n / 1048576).toFixed(1)} MB` : n >= 1024 ? `${(n / 1024).toFixed(1)} KB` : `${n} B`;\n\n/** Menu entry: move this session to another project (\u00A710 MR6). */\nexport function MoveSessionMenuItem({ sessionId, t }) {\n\tconst [open, setOpen] = useState(false);\n\treturn (\n\t\t<>\n\t\t\t<button role=\"menuitem\" onClick={() => setOpen(true)} style={menuItemStyle}>{t ? t('menu.move') : '\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE\u2026'}</button>\n\t\t\t{open ? <MoveDialog sessionId={sessionId} close={() => setOpen(false)} /> : null}\n\t\t</>\n\t);\n}\n\nexport function TrashSessionMenuItem({ sessionId, t }) {\n\tconst [confirm, setConfirm] = useState(false);\n\tconst [busy, setBusy] = useState(false);\n\tif (confirm) {\n\t\treturn <button role=\"menuitem\" disabled={busy} style={menuItemStyle} onClick={async () => {\n\t\t\tsetBusy(true);\n\t\t\ttry { await callHost({ type: 'trash.session', sessionId }); setConfirm(false); }\n\t\t\tcatch { sayRow(`\u56DE\u6536\u5931\u8D25`); setConfirm(false); }\n\t\t\tsetBusy(false);\n\t\t}}>\u786E\u8BA4\u5F52\u6863\uFF1F\uFF08\u4F1A\u8BDD\u5C06\u8FDB\u5165\u5F52\u6863\u6C60\uFF09</button>;\n\t}\n\treturn <button role=\"menuitem\" style={menuItemStyle} onClick={() => setConfirm(true)}>{t ? t('menu.trash') : '\u5F52\u6863\u5230\u5F52\u6863\u6C60'}</button>;\n}\nfunction sayRow(message) { store.error = message; for (const listener of store.listeners) listener(); }\n\nconst menuItemStyle = { background: 'transparent', border: 'none', color: 'inherit', font: 'inherit', padding: '6px 10px', textAlign: 'left', cursor: 'pointer', width: '100%' };\n\nexport function MoveDialog({ sessionId, close }) {\n\tconst ref = useRef(null);\n\tconst [toCwd, setToCwd] = useState('');\n\tconst [createTarget, setCreateTarget] = useState(false);\n\tconst [error, setError] = useState('');\n\tconst [busy, setBusy] = useState(false);\n\tuseEffect(() => { const dialog = ref.current; dialog?.showModal(); return () => { try { dialog?.close(); } catch {} }; }, []);\n\tconst submit = async () => {\n\t\tsetBusy(true); setError('');\n\t\ttry { await callHost({ type: 'move', sessionId, toCwd, createTarget }); close(); }\n\t\tcatch (err) { setError(err.message); setBusy(false); }\n\t};\n\tconst valid = /^\\/|^[A-Za-z]:[\\\\/]/.test(toCwd);\n\treturn (\n\t\t<div className=\"sk\"><style>{styles}</style>\n\t\t\t<dialog ref={ref} onCancel={e => { e.preventDefault(); if (!busy) close(); }}>\n\t\t\t\t<div className=\"modal-head\"><strong>\u4F1A\u8BDD\u5207\u6362\u5230\u5176\u5B83\u9879\u76EE</strong><button onClick={close} disabled={busy}>\u5173\u95ED</button></div>\n\t\t\t\t<div className=\"modal-body\">\n\t\t\t\t\t<p className=\"muted mono\">{sessionId}</p>\n\t\t\t\t\t<input autoFocus className=\"grow mono\" placeholder=\"\u76EE\u6807\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84\" value={toCwd} onChange={e => setToCwd(e.target.value)} />\n\t\t\t\t\t<label className=\"row\"><input type=\"checkbox\" checked={createTarget} onChange={e => setCreateTarget(e.target.checked)} /> \u76EE\u5F55\u4E0D\u5B58\u5728\u65F6\u521B\u5EFA</label>\n\t\t\t\t\t{error ? <p className=\"error\">{error}</p> : null}\n\t\t\t\t\t<div className=\"row\"><button className=\"primary\" disabled={busy || !valid} onClick={() => void submit()}>\u4F53\u68C0\u5E76\u8FC1\u79FB</button>\n\t\t\t\t\t\t<span className=\"muted\">\u4F1A\u8BDD\u9700\u7A7A\u95F2\uFF1B\u8FC1\u79FB\u5728\u540E\u53F0\u79BB\u7EBF\u5B8C\u6210\u3002</span></div>\n\t\t\t\t</div>\n\t\t\t</dialog>\n\t\t</div>\n\t);\n}\n\n/** Input-dock badge (\u00A74 FR5): how many image drafts are backed up for this session. */\nexport function VaultBadge({ sessionId }) {\n\tconst [count, setCount] = useState(0);\n\tconst [notice, setNotice] = useState('');\n\tuseEffect(() => {\n\t\tif (vault === undefined) return;\n\t\tlet live = true;\n\t\tconst sync = async () => {\n\t\t\ttry { const value = await vault.countFor(sessionId ?? vault.currentSession()); if (live) setCount(value); }\n\t\t\tcatch { if (live) setCount(0); }\n\t\t\tif (live) setNotice(vault.lastNotice ?? '');\n\t\t};\n\t\tvoid sync();\n\t\tconst listener = () => void sync();\n\t\tvault.on('change', listener);\n\t\twindow.addEventListener(`${NS}-notice`, listener);\n\t\tconst timer = setInterval(listener, 20000);\n\t\treturn () => { live = false; vault.off('change', listener); window.removeEventListener(`${NS}-notice`, listener); clearInterval(timer); };\n\t}, [sessionId]);\n\tif (count === 0) return null; // the notice channel serves the panel; the badge anchors per-session counts\n\treturn <span className=\"sk\"><span className=\"pill\" title={`\u672C\u4F1A\u8BDD\u6709 ${count} \u5F20\u56FE\u7247\u8349\u7A3F\u5DF2\u5907\u4EFD\uFF08\u5207\u6362\u4F1A\u8BDD/\u5237\u65B0/\u91CD\u542F\u540E\u53EF\u6062\u590D\uFF09${notice ? `\uFF5C\u6700\u8FD1\uFF1A${notice}` : ''}`}>\uD83D\uDEDF \u8349\u7A3F\u5907\u4EFD {count}</span></span>;\n}\n", "{\n \"name\": \"dsh-session-toolbox\",\n \"version\": \"0.1.8\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/AndroidSix/dsh-session-toolbox.git\"\n },\n \"description\": \"Draft vault, session-to-project move and a trash bin for DeepSeek Harness\",\n \"type\": \"module\",\n \"main\": \"lib/index.js\",\n \"bin\": {\n \"dsh-session-toolbox\": \"lib/cli.js\"\n },\n \"exports\": {\n \".\": \"./lib/index.js\",\n \"./host\": \"./lib/host.js\",\n \"./client\": \"./lib/client.js\",\n \"./cordis.patch.yml\": \"./cordis.patch.yml\",\n \"./package.json\": \"./package.json\"\n },\n \"engines\": {\n \"node\": \"^22.19.0 || >=24.0.0\",\n \"dsh\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\"\n },\n \"files\": [\n \"lib\",\n \"cordis.patch.yml\",\n \"README.md\",\n \"LICENSE\",\n \"THIRD_PARTY_NOTICES.md\",\n \"session-toolbox.example.json\",\n \"CHANGELOG.md\"\n ],\n \"dsh\": {\n \"bundle\": {\n \"patch\": \"./cordis.patch.yml\"\n },\n \"client\": {\n \"inject\": [\n \"@deepseek-ai/dsh-client-ui-renderer\"\n ],\n \"platform\": \"web\"\n }\n },\n \"scripts\": {\n \"build\": \"node scripts/build.mjs\",\n \"test\": \"node --test tests/*.test.mjs\",\n \"check\": \"npm run build && npm test && node scripts/check-package.mjs\",\n \"check:tarball\": \"npm run build && node scripts/check-package.mjs --tarball\",\n \"pack:release\": \"npm run build && npm pack --ignore-scripts\"\n },\n \"peerDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"react\": \"^18.2.0\"\n },\n \"devDependencies\": {\n \"@deepseek-ai/cordis\": \"^4.0.4\",\n \"@deepseek-ai/dsh-atomic-write\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/dsh-home-paths\": \"^0.2.0-rc.2 || ^0.2.1-rc.2\",\n \"@deepseek-ai/schemastery\": \"^3.18.4\",\n \"esbuild\": \"^0.25.0\",\n \"react\": \"^18.2.0\",\n \"react-dom\": \"^18.2.0\"\n },\n \"license\": \"MIT\",\n \"keywords\": [\n \"dsh-plugin\",\n \"deepseek-harness\",\n \"draft-vault\",\n \"session-move\",\n \"trash-bin\"\n ]\n}\n", "/**\n * Draft vault (\u00A74-\u00A79): mirrors image drafts into IndexedDB and replays them\n * through the official paste pipeline. No official internals are touched:\n * capture listens at document-level `paste`/`drop`/file-input `change` (never\n * preventing or stopping the native flow), persistence is browser IndexedDB,\n * and restore re-dispatches a ClipboardEvent carrying a synthetic\n * `clipboardData` (Chromium ignores the ClipboardEventInit clipboardData and\n * exposes it only as a prototype accessor, so the property is shadowed with a\n * defined own property \u2014 the official handler only reads `items`/`getData`,\n * verified against the shipped build).\n *\n * Idempotency is DOM-based and safe by construction:\n * - seeding only happens into an EMPTY attachment rail;\n * - a seed is confirmed by the official handler calling preventDefault() (its\n * documented contract for accepted files);\n * - rail dropping to zero while the session view is mounted means \"sent or\n * user-deleted\" (\u00A7 FR3: both delete the backups); the view unmounting keeps\n * them \u2014 that is exactly the loss this vault repairs.\n */\n\nexport const DB_NAME = 'dsh-session-toolbox-vault';\nexport const STORE = 'blobs';\nexport const IMAGE_TYPES = ['image/png', 'image/jpeg', 'image/webp', 'image/gif'];\n/** Marks our own synthetic paste so it is not re-captured as a new draft. */\nconst SEED_MARKER = Symbol.for('dsh-session-toolbox.seed');\n\nexport const MAX_FILE_BYTES = 10 * 1024 * 1024; // the official per-image admission cap\nexport const DEFAULT_LIMITS = Object.freeze({ enabled: true, maxSizeBytes: 200 * 1024 * 1024, ttlDays: 7 });\n\nexport function isImageFile(file) {\n\treturn file !== undefined && IMAGE_TYPES.includes(file.type) && file.size > 0 && file.size <= MAX_FILE_BYTES;\n}\n\n/** sha256 hex of bytes; crypto.subtle is absent on non-secure origins \u2192 size/name fallback hash. */\nexport async function contentHash(file) {\n\tif (globalThis.crypto?.subtle !== undefined) {\n\t\ttry {\n\t\t\tconst digest = await crypto.subtle.digest('SHA-256', await file.arrayBuffer());\n\t\t\treturn [...new Uint8Array(digest)].map(byte => byte.toString(16).padStart(2, '0')).join('');\n\t\t} catch { /* fall through */ }\n\t}\n\treturn `weak:${file.size}:${file.name}:${file.lastModified}`;\n}\n\nfunction openDb() {\n\tif (typeof indexedDB === 'undefined') throw new Error('IndexedDB unavailable');\n\treturn new Promise((resolve, reject) => {\n\t\tconst request = indexedDB.open(DB_NAME, 1);\n\t\trequest.onupgradeneeded = () => {\n\t\t\tconst database = request.result;\n\t\t\tif (!database.objectStoreNames.contains(STORE)) database.createObjectStore(STORE, { keyPath: 'key' });\n\t\t};\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('IndexedDB open failed'));\n\t});\n}\n\n/** Resolve with the request result; reject with its error. */\nfunction done(request) {\n\treturn new Promise((resolve, reject) => {\n\t\trequest.onsuccess = () => resolve(request.result);\n\t\trequest.onerror = () => reject(request.error ?? new Error('vault storage request failed'));\n\t});\n}\n\nfunction storeOp(database, mode, run) {\n\treturn new Promise((resolve, reject) => {\n\t\tlet outcome;\n\t\tconst transaction = database.transaction(STORE, mode);\n\t\ttransaction.onabort = () => reject(transaction.error ?? new Error('vault transaction aborted'));\n\t\ttransaction.onerror = () => reject(transaction.error ?? new Error('vault transaction failed'));\n\t\ttry { outcome = run(transaction.objectStore(STORE)); } catch (error) { reject(error); return; }\n\t\tPromise.resolve(outcome).then(resolve, reject);\n\t});\n}\n\nfunction emitter() {\n\tconst listeners = new Map();\n\treturn {\n\t\ton(event, fn) { if (!listeners.has(event)) listeners.set(event, new Set()); listeners.get(event).add(fn); },\n\t\toff(event, fn) { listeners.get(event)?.delete(fn); },\n\t\temit(event, detail) { for (const fn of [...(listeners.get(event) ?? [])]) { try { fn(detail); } catch {} } },\n\t};\n}\n\nfunction fakeClipboardData(files) {\n\tconst items = files.map(f => ({ kind: 'file', type: f.type, getAsFile: () => f }));\n\treturn { items, files, length: files.length, types: ['Files'], getData: () => '', setData: () => false, clearData: () => {} };\n}\n\nasync function blobToFile(record) {\n\tif (record.blob instanceof File) return record.blob;\n\tconst blob = record.blob instanceof Blob ? record.blob : new Blob([record.blob ?? new Uint8Array()], { type: record.type });\n\treturn new File([blob], record.name ?? '', { type: record.type, lastModified: record.createdAt });\n}\n\n/**\n * @param options - {endpoint, log, now, fetchImpl}\n */\nexport function createVault(options = {}) {\n\tconst log = options.log ?? (() => {});\n\tconst now = options.now ?? (() => Date.now());\n\tconst fetchImpl = options.fetchImpl ?? ((input, init) => fetch(input, init));\n\tconst bus = emitter();\n\tlet dbPromise = null;\n\tlet limits = { ...DEFAULT_LIMITS };\n\tlet lastNotice = '';\n\tlet started = false;\n\tlet teardown = null;\n\tlet hostKey = '';\n\n\tconst db = () => dbPromise ??= openDb();\n\tfunction notice(message) {\n\t\tlastNotice = message;\n\t\tbus.emit('notice', message);\n\t\ttry { globalThis.window?.dispatchEvent?.(new CustomEvent('dsh-session-toolbox-notice', { detail: message })); } catch { /* test envs */ }\n\t}\n\tfunction changed() { bus.emit('change'); }\n\n\t// ---- IndexedDB CRUD -----------------------------------------------------\n\tconst allRecords = async () => {\n\t\tconst database = await db();\n\t\treturn await storeOp(database, 'readonly', store => done(store.getAll()));\n\t};\n\tconst putRecord = async record => {\n\t\tconst database = await db();\n\t\tawait storeOp(database, 'readwrite', store => done(store.put(record)));\n\t};\n\tconst dropRecord = async key => {\n\t\tconst database = await db();\n\t\tawait storeOp(database, 'readwrite', store => done(store.delete(key)));\n\t};\n\n\t/** Best-effort host manifest mirror (status display only; failures invisible). */\n\tasync function syncHost() {\n\t\tconst endpoint = options.endpoint ?? '/.well-known/dsh-session-toolbox';\n\t\ttry {\n\t\t\tif (hostKey === '') {\n\t\t\t\tconst probe = await fetchImpl(endpoint, { cache: 'no-store' });\n\t\t\t\thostKey = (await probe.json()).key ?? '';\n\t\t\t}\n\t\t\tconst records = (await allRecords()).slice(-500).map(({ key, sessionId, attachmentId, name, size, hash, seeded, createdAt }) =>\n\t\t\t\t({ key, sessionId, attachmentId, name, size, hash, seeded, createdAt }));\n\t\t\tawait fetchImpl(endpoint, { method: 'POST', cache: 'no-store', headers: { 'content-type': 'application/json', 'x-session-toolbox-key': hostKey },\n\t\t\t\tbody: JSON.stringify({ type: 'vault.sync', records }) });\n\t\t} catch { /* the manifest is cosmetic; the vault itself is IndexedDB */ }\n\t}\n\n\t// ---- FR4 capacity + TTL --------------------------------------------------\n\tasync function sweep() {\n\t\tconst records = await allRecords();\n\t\tconst ttlMs = limits.ttlDays * 24 * 60 * 60 * 1000;\n\t\tconst keep = [];\n\t\tconst remove = [];\n\t\tfor (const record of records) (now() - (record.createdAt ?? 0) > ttlMs ? remove : keep).push(record);\n\t\tkeep.sort((a, b) => (a.createdAt ?? 0) - (b.createdAt ?? 0));\n\t\tlet bytes = keep.reduce((total, record) => total + (record.size ?? 0), 0);\n\t\twhile (bytes > limits.maxSizeBytes && keep.length > 0) {\n\t\t\tconst oldest = keep.shift();\n\t\t\tbytes -= oldest.size ?? 0;\n\t\t\tremove.push(oldest);\n\t\t}\n\t\tfor (const record of remove) await dropRecord(record.key);\n\t\tif (remove.length > 0) notice(`\u8349\u7A3F\u4FDD\u9669\u7BB1\u5DF2\u6E05\u7406 ${remove.length} \u9879\uFF08\u8D85\u671F/\u8D85\u5BB9\u91CF\uFF09`);\n\t\tchanged();\n\t\treturn { removed: remove.length, kept: keep.length, bytes };\n\t}\n\n\t// ---- FR1 capture ---------------------------------------------------------\n\tasync function intake(sessionId, files) {\n\t\tif (!limits.enabled || sessionId === undefined || sessionId === '') return;\n\t\tconst existing = (await allRecords()).filter(record => record.sessionId === sessionId);\n\t\tconst seen = new Set(existing.map(record => record.hash));\n\t\tlet added = 0;\n\t\tfor (const item of files) {\n\t\t\tif (!isImageFile(item)) continue;\n\t\t\tconst hash = await contentHash(item);\n\t\t\tif (seen.has(hash)) continue; // same image already held for this session\n\t\t\tseen.add(hash);\n\t\t\tconst id = (globalThis.crypto?.randomUUID?.() ?? `id-${now()}-${Math.random().toString(36).slice(2)}`);\n\t\t\tconst record = { key: `${sessionId}__${id}`, sessionId, attachmentId: id,\n\t\t\t\tname: item.name || '', type: item.type, size: item.size, hash, createdAt: now(), seeded: false };\n\t\t\trecord.blob = item;\n\t\t\tawait putRecord(record);\n\t\t\tadded += 1;\n\t\t}\n\t\tif (added > 0) notice(`\u5DF2\u5907\u4EFD ${added} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);\n\t\tvoid syncHost();\n\t}\n\n\tconst readImages = event => [...(event.clipboardData?.files ?? event.dataTransfer?.files ?? [])].filter(isImageFile);\n\tconst fileInputImages = event => {\n\t\tconst target = event.target;\n\t\tif (!(target instanceof HTMLInputElement) || target.type !== 'file' || target.files === null) return [];\n\t\treturn [...target.files].filter(isImageFile);\n\t};\n\n\t// ---- DOM discovery (official composer markers) ----------------------------\n\tfunction currentSession() {\n\t\treturn document.querySelector?.('[data-conversation-session]')?.getAttribute?.('data-conversation-session') ?? '';\n\t}\n\tfunction composerRoot() {\n\t\treturn document.querySelector?.('[data-conversation-session] [contenteditable=\"true\"]') ?? undefined;\n\t}\n\tfunction attachmentRail(editor) {\n\t\t// Official structure: attachment cards are imgs with blob: srcs sitting\n\t\t// above the contenteditable root inside the composer bar subtree.\n\t\tconst bar = editor?.closest?.('[data-composer-bar], [data-conversation-region=\"composer\"]') ?? editor?.parentElement?.parentElement?.parentElement;\n\t\tif (bar?.querySelectorAll === undefined) return undefined;\n\t\treturn [...bar.querySelectorAll('img')].filter(img => img.getAttribute?.('src')?.startsWith('blob:'));\n\t}\n\n\t// ---- FR2 restore -----------------------------------------------------------\n\tasync function seedSession(sessionId = currentSession()) {\n\t\tif (sessionId === undefined || sessionId === '' || !limits.enabled) return false;\n\t\tconst editor = composerRoot();\n\t\tif (editor === undefined) return false;\n\t\tconst rail = attachmentRail(editor);\n\t\tif (rail === undefined) return false;\n\t\tif (rail.length > 0) return true; // non-empty: official still holds them (or was seeded before)\n\t\tconst pending = (await allRecords()).filter(record => record.sessionId === sessionId);\n\t\tif (pending.length === 0) return true;\n\t\tconst files = [];\n\t\tfor (const record of pending) {\n\t\t\ttry { files.push(await blobToFile(record)); }\n\t\t\tcatch { await dropRecord(record.key); } // unreadable backup \u2014 drop and move on\n\t\t}\n\t\tif (files.length === 0) { changed(); return false; }\n\t\tconst event = new ClipboardEvent('paste', { bubbles: true, cancelable: true });\n\t\tObject.defineProperty(event, 'clipboardData', { value: fakeClipboardData(files) });\n\t\t// Mark as a restore so our own intake (which observes the same event)\n\t\t// never stores the replayed images as a second fresh draft.\n\t\ttry { Object.defineProperty(event, SEED_MARKER, { value: true }); } catch { /* ignore */ }\n\t\ttry { editor.focus?.(); } catch { /* ignore */ }\n\t\teditor.dispatchEvent(event);\n\t\tconst accepted = event.defaultPrevented === true;\n\t\tif (accepted) notice(`\u5DF2\u6062\u590D ${files.length} \u5F20\u8349\u7A3F\u56FE\u7247\uFF08\u4F1A\u8BDD ${sessionId.slice(-6)}\uFF09`);\n\t\telse log('warn', 'synthetic paste was not accepted; vault stays idle for this session');\n\t\tchanged();\n\t\treturn accepted;\n\t}\n\n\t// ---- FR3 lifecycle ---------------------------------------------------------\n\tfunction watchSession(sessionId, editor) {\n\t\tlet rail = attachmentRail(editor)?.length ?? 0;\n\t\tconst scope = editor.closest?.('[data-conversation-content]') ?? editor;\n\t\tconst check = async () => {\n\t\t\tconst next = attachmentRail(editor)?.length ?? rail;\n\t\t\tif (next === rail) return;\n\t\t\tif (rail > 0 && next === 0 && scope?.isConnected === true) {\n\t\t\t\t// Sent or manually removed while mounted \u2192 backups retire (\u00A7 FR3).\n\t\t\t\tfor (const record of await allRecords()) if (record.sessionId === sessionId) await dropRecord(record.key);\n\t\t\t\tnotice('\u672C\u4F1A\u8BDD\u8349\u7A3F\u56FE\u7247\u5DF2\u6E05\u7A7A\uFF0C\u5907\u4EFD\u540C\u6B65\u79FB\u9664');\n\t\t\t\tvoid syncHost();\n\t\t\t}\n\t\t\trail = next;\n\t\t\tchanged();\n\t\t};\n\t\tif (typeof MutationObserver !== 'function') return () => {};\n\t\tconst observer = new MutationObserver(() => void check());\n\t\tobserver.observe(scope, { subtree: true, childList: true });\n\t\treturn () => observer.disconnect();\n\t}\n\n\tfunction watchMounts() {\n\t\tlet boundSession = '', dispose = () => {};\n\t\tconst sync = async () => {\n\t\t\tconst sessionId = currentSession();\n\t\t\tconst editor = composerRoot();\n\t\t\tif (sessionId === '' || editor === undefined) return;\n\t\t\tif (sessionId === boundSession && editor.isConnected !== false) return;\n\t\t\tboundSession = sessionId;\n\t\t\tdispose();\n\t\t\tdispose = watchSession(sessionId, editor);\n\t\t\tsetTimeout(() => void seedSession(sessionId).catch(error => log('warn', `vault seed failed: ${error.message}`)), 120);\n\t\t\tvoid sweep().catch(() => {});\n\t\t};\n\t\tconst interval = setInterval(() => void sync(), 5000);\n\t\tif (typeof MutationObserver !== 'function') return () => clearInterval(interval);\n\t\tconst observer = new MutationObserver(() => void sync());\n\t\tobserver.observe(document.documentElement, { subtree: true, childList: true, attributes: true, attributeFilter: ['data-conversation-session'] });\n\t\treturn () => { observer.disconnect(); clearInterval(interval); dispose(); };\n\t}\n\n\treturn {\n\t\ton: bus.on, off: bus.off,\n\t\tget lastNotice() { return lastNotice; },\n\t\tcurrentSession,\n\t\trecords: allRecords,\n\t\tcountFor: async sessionId => {\n\t\t\tconst records = await allRecords();\n\t\t\treturn sessionId === undefined || sessionId === '' ? records.length : records.filter(record => record.sessionId === sessionId).length;\n\t\t},\n\t\tstart: async ({ limits: override } = {}) => {\n\t\t\tif (started || typeof document === 'undefined') return false;\n\t\t\tstarted = true;\n\t\t\tif (override !== undefined) limits = { ...limits, ...override };\n\t\t\telse {\n\t\t\t\ttry {\n\t\t\t\t\tconst res = await fetchImpl(`${options.endpoint ?? '/.well-known/dsh-session-toolbox'}`, { cache: 'no-store' });\n\t\t\t\t\tconst next = await res.json();\n\t\t\t\t\tlimits = { ...limits, ...(next.settings?.draftVault ?? {}) };\n\t\t\t\t} catch { /* defaults hold */ }\n\t\t\t}\n\t\t\tconst capture = event => {\n\t\t\t\tif (event[SEED_MARKER] === true) return;\n\t\t\t\tvoid intake(currentSession(), readImages(event)).catch(() => {});\n\t\t\t};\n\t\t\tdocument.addEventListener('paste', capture, true);\n\t\t\tdocument.addEventListener('drop', capture, true);\n\t\t\tdocument.addEventListener('change', event => void intake(currentSession(), fileInputImages(event)).catch(() => {}), true);\n\t\t\tteardown = watchMounts();\n\t\t\tvoid sweep().catch(() => {});\n\t\t\tlog('info', 'draft vault started');\n\t\t\treturn true;\n\t\t},\n\t\tstop: () => { if (started) { started = false; teardown?.(); teardown = null; } },\n\t\tsetLimits: next => {\n\t\t\tlimits = { ...limits, ...next };\n\t\t\tif (limits.enabled === false) void (async () => { for (const record of await allRecords()) await dropRecord(record.key); notice('\u8349\u7A3F\u4FDD\u9669\u7BB1\u5DF2\u505C\u7528\uFF0C\u5B58\u91CF\u5DF2\u6E05\u7A7A'); })();\n\t\t},\n\t\tsweep,\n\t\tseedSession,\n\t\tintake,\n\t\twatchMounts,\n\t};\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["store", "name", "setBusy"]
|
|
7
7
|
}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
guardLedger,
|
|
9
9
|
guardManifest,
|
|
10
10
|
guardSettings
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-QXW2NIKH.js";
|
|
12
12
|
|
|
13
13
|
// src/index.mjs
|
|
14
14
|
import { mkdir, realpath, stat } from "node:fs/promises";
|
|
@@ -20,7 +20,7 @@ import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
|
|
|
20
20
|
// package.json
|
|
21
21
|
var package_default = {
|
|
22
22
|
name: "dsh-session-toolbox",
|
|
23
|
-
version: "0.1.
|
|
23
|
+
version: "0.1.8",
|
|
24
24
|
repository: {
|
|
25
25
|
type: "git",
|
|
26
26
|
url: "https://github.com/AndroidSix/dsh-session-toolbox.git"
|
|
@@ -175,6 +175,16 @@ async function applyInner(ctx, options = {}) {
|
|
|
175
175
|
journal: () => journalCache,
|
|
176
176
|
settings: () => settingsCache.trash ?? {}
|
|
177
177
|
});
|
|
178
|
+
const listProjects = (ctx2) => {
|
|
179
|
+
const registry = ctx2.get("workspaceRegistry");
|
|
180
|
+
if (registry === void 0) return [];
|
|
181
|
+
return registry.list().map((row) => ({
|
|
182
|
+
id: row.id,
|
|
183
|
+
path: row.path,
|
|
184
|
+
title: row.title ?? row.path,
|
|
185
|
+
sessions: (row.sessionIds ?? []).length
|
|
186
|
+
})).sort((a, b) => a.title.localeCompare(b.title));
|
|
187
|
+
};
|
|
178
188
|
const kit = {
|
|
179
189
|
settings: settingsDoc,
|
|
180
190
|
manifest: manifestDoc,
|
|
@@ -211,12 +221,25 @@ async function applyInner(ctx, options = {}) {
|
|
|
211
221
|
capabilities,
|
|
212
222
|
settings,
|
|
213
223
|
journal,
|
|
224
|
+
projects: listProjects(ctx),
|
|
214
225
|
trash: await trash.items(),
|
|
215
226
|
// Bytes live in browser IndexedDB; the manifest mirrors their metadata only.
|
|
216
227
|
vault: { records: manifest.records.length, bytes: manifest.records.reduce((total, record) => total + (record.size ?? 0), 0) },
|
|
217
228
|
recovery: journal === null ? null : `\u672A\u5B8C\u6210\u8FC1\u79FB\uFF1A${journal.sessionId} \u2192 ${journal.toCwd}\uFF08\u5DF2\u5B8C\u6210 ${journal.done.join("/") || "\u65E0"}\uFF09`
|
|
218
229
|
};
|
|
219
230
|
},
|
|
231
|
+
/** The project a session currently belongs to (null when unknown). */
|
|
232
|
+
projectOf: async (sessionId) => {
|
|
233
|
+
if (sessionId === "") return null;
|
|
234
|
+
const registry = ctx.get("workspaceRegistry");
|
|
235
|
+
if (registry === void 0) return null;
|
|
236
|
+
for (const row of registry.list()) {
|
|
237
|
+
if ((row.sessionIds ?? []).includes(sessionId)) {
|
|
238
|
+
return { id: row.id, path: row.path, title: row.title ?? row.path };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
},
|
|
220
243
|
action: (input) => dispatch(kit, input)
|
|
221
244
|
};
|
|
222
245
|
ctx.inject?.(["webServer"], (webCtx) => {
|
package/lib/host.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
guardLedger,
|
|
9
9
|
guardManifest,
|
|
10
10
|
guardSettings
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-QXW2NIKH.js";
|
|
12
12
|
|
|
13
13
|
// src/index.mjs
|
|
14
14
|
import { mkdir, realpath, stat } from "node:fs/promises";
|
|
@@ -20,7 +20,7 @@ import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
|
|
|
20
20
|
// package.json
|
|
21
21
|
var package_default = {
|
|
22
22
|
name: "dsh-session-toolbox",
|
|
23
|
-
version: "0.1.
|
|
23
|
+
version: "0.1.8",
|
|
24
24
|
repository: {
|
|
25
25
|
type: "git",
|
|
26
26
|
url: "https://github.com/AndroidSix/dsh-session-toolbox.git"
|
|
@@ -175,6 +175,16 @@ async function applyInner(ctx, options = {}) {
|
|
|
175
175
|
journal: () => journalCache,
|
|
176
176
|
settings: () => settingsCache.trash ?? {}
|
|
177
177
|
});
|
|
178
|
+
const listProjects = (ctx2) => {
|
|
179
|
+
const registry = ctx2.get("workspaceRegistry");
|
|
180
|
+
if (registry === void 0) return [];
|
|
181
|
+
return registry.list().map((row) => ({
|
|
182
|
+
id: row.id,
|
|
183
|
+
path: row.path,
|
|
184
|
+
title: row.title ?? row.path,
|
|
185
|
+
sessions: (row.sessionIds ?? []).length
|
|
186
|
+
})).sort((a, b) => a.title.localeCompare(b.title));
|
|
187
|
+
};
|
|
178
188
|
const kit = {
|
|
179
189
|
settings: settingsDoc,
|
|
180
190
|
manifest: manifestDoc,
|
|
@@ -211,12 +221,25 @@ async function applyInner(ctx, options = {}) {
|
|
|
211
221
|
capabilities,
|
|
212
222
|
settings,
|
|
213
223
|
journal,
|
|
224
|
+
projects: listProjects(ctx),
|
|
214
225
|
trash: await trash.items(),
|
|
215
226
|
// Bytes live in browser IndexedDB; the manifest mirrors their metadata only.
|
|
216
227
|
vault: { records: manifest.records.length, bytes: manifest.records.reduce((total, record) => total + (record.size ?? 0), 0) },
|
|
217
228
|
recovery: journal === null ? null : `\u672A\u5B8C\u6210\u8FC1\u79FB\uFF1A${journal.sessionId} \u2192 ${journal.toCwd}\uFF08\u5DF2\u5B8C\u6210 ${journal.done.join("/") || "\u65E0"}\uFF09`
|
|
218
229
|
};
|
|
219
230
|
},
|
|
231
|
+
/** The project a session currently belongs to (null when unknown). */
|
|
232
|
+
projectOf: async (sessionId) => {
|
|
233
|
+
if (sessionId === "") return null;
|
|
234
|
+
const registry = ctx.get("workspaceRegistry");
|
|
235
|
+
if (registry === void 0) return null;
|
|
236
|
+
for (const row of registry.list()) {
|
|
237
|
+
if ((row.sessionIds ?? []).includes(sessionId)) {
|
|
238
|
+
return { id: row.id, path: row.path, title: row.title ?? row.path };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
},
|
|
220
243
|
action: (input) => dispatch(kit, input)
|
|
221
244
|
};
|
|
222
245
|
ctx.inject?.(["webServer"], (webCtx) => {
|
package/lib/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
guardLedger,
|
|
9
9
|
guardManifest,
|
|
10
10
|
guardSettings
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-QXW2NIKH.js";
|
|
12
12
|
|
|
13
13
|
// src/index.mjs
|
|
14
14
|
import { mkdir, realpath, stat } from "node:fs/promises";
|
|
@@ -20,7 +20,7 @@ import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
|
|
|
20
20
|
// package.json
|
|
21
21
|
var package_default = {
|
|
22
22
|
name: "dsh-session-toolbox",
|
|
23
|
-
version: "0.1.
|
|
23
|
+
version: "0.1.8",
|
|
24
24
|
repository: {
|
|
25
25
|
type: "git",
|
|
26
26
|
url: "https://github.com/AndroidSix/dsh-session-toolbox.git"
|
|
@@ -175,6 +175,16 @@ async function applyInner(ctx, options = {}) {
|
|
|
175
175
|
journal: () => journalCache,
|
|
176
176
|
settings: () => settingsCache.trash ?? {}
|
|
177
177
|
});
|
|
178
|
+
const listProjects = (ctx2) => {
|
|
179
|
+
const registry = ctx2.get("workspaceRegistry");
|
|
180
|
+
if (registry === void 0) return [];
|
|
181
|
+
return registry.list().map((row) => ({
|
|
182
|
+
id: row.id,
|
|
183
|
+
path: row.path,
|
|
184
|
+
title: row.title ?? row.path,
|
|
185
|
+
sessions: (row.sessionIds ?? []).length
|
|
186
|
+
})).sort((a, b) => a.title.localeCompare(b.title));
|
|
187
|
+
};
|
|
178
188
|
const kit = {
|
|
179
189
|
settings: settingsDoc,
|
|
180
190
|
manifest: manifestDoc,
|
|
@@ -211,12 +221,25 @@ async function applyInner(ctx, options = {}) {
|
|
|
211
221
|
capabilities,
|
|
212
222
|
settings,
|
|
213
223
|
journal,
|
|
224
|
+
projects: listProjects(ctx),
|
|
214
225
|
trash: await trash.items(),
|
|
215
226
|
// Bytes live in browser IndexedDB; the manifest mirrors their metadata only.
|
|
216
227
|
vault: { records: manifest.records.length, bytes: manifest.records.reduce((total, record) => total + (record.size ?? 0), 0) },
|
|
217
228
|
recovery: journal === null ? null : `\u672A\u5B8C\u6210\u8FC1\u79FB\uFF1A${journal.sessionId} \u2192 ${journal.toCwd}\uFF08\u5DF2\u5B8C\u6210 ${journal.done.join("/") || "\u65E0"}\uFF09`
|
|
218
229
|
};
|
|
219
230
|
},
|
|
231
|
+
/** The project a session currently belongs to (null when unknown). */
|
|
232
|
+
projectOf: async (sessionId) => {
|
|
233
|
+
if (sessionId === "") return null;
|
|
234
|
+
const registry = ctx.get("workspaceRegistry");
|
|
235
|
+
if (registry === void 0) return null;
|
|
236
|
+
for (const row of registry.list()) {
|
|
237
|
+
if ((row.sessionIds ?? []).includes(sessionId)) {
|
|
238
|
+
return { id: row.id, path: row.path, title: row.title ?? row.path };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
},
|
|
220
243
|
action: (input) => dispatch(kit, input)
|
|
221
244
|
};
|
|
222
245
|
ctx.inject?.(["webServer"], (webCtx) => {
|