@wisdoverse/dsh-inline-media-viewer 1.0.8 → 1.0.10-alpha.1
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 +17 -2
- package/README.md +3 -1
- package/README.zh-CN.md +3 -1
- package/client/client.js +55 -4
- package/index.js +5 -15
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 1.0.
|
|
3
|
+
## 1.0.10-alpha.1
|
|
4
|
+
|
|
5
|
+
- Adapt to DSH v0.1.3-alpha.1: register host settings through `settings.register`,
|
|
6
|
+
register client projections through `uiConversation.events`, and use the
|
|
7
|
+
authenticated two-argument RPC registration contract.
|
|
8
|
+
- Remove the unused settings package peer dependency; consume the host-provided
|
|
9
|
+
settings service and add a real-dependency host registration check.
|
|
10
|
+
|
|
11
|
+
## 1.0.9
|
|
12
|
+
|
|
13
|
+
- Collect media paths from tool-call arguments as well as the assistant's final
|
|
14
|
+
text, so every image read during a turn remains available in its preview.
|
|
15
|
+
|
|
16
|
+
## 1.0.8 (deprecated)
|
|
4
17
|
|
|
5
18
|
- Preserve output-directory context across assistant steps in one turn, so a
|
|
6
19
|
later final answer containing only media filenames still renders previews.
|
|
20
|
+
- Superseded by 1.0.9 because tool-call-only media references were not retained.
|
|
7
21
|
|
|
8
|
-
## 1.0.7
|
|
22
|
+
## 1.0.7 (deprecated)
|
|
9
23
|
|
|
10
24
|
- Resolve backticked media filenames against a preceding backticked directory,
|
|
11
25
|
matching the common "output directory plus file list" response format.
|
|
26
|
+
- Superseded by 1.0.9 because it only inspected assistant text.
|
|
12
27
|
|
|
13
28
|
## 1.0.6
|
|
14
29
|
|
package/README.md
CHANGED
|
@@ -59,12 +59,14 @@ audio/sound.mp3 → <audio controls>
|
|
|
59
59
|
|
|
60
60
|
## Installation
|
|
61
61
|
|
|
62
|
+
Targets DSH **v0.1.3-alpha.1**. For older DSH versions, use plugin **1.0.9**.
|
|
63
|
+
|
|
62
64
|
### From npm
|
|
63
65
|
|
|
64
66
|
Install and activate the bundle in your Web profile:
|
|
65
67
|
|
|
66
68
|
```bash
|
|
67
|
-
dsh plugin --profile web add @wisdoverse/dsh-inline-media-viewer
|
|
69
|
+
dsh plugin --profile web add @wisdoverse/dsh-inline-media-viewer@1.0.10-alpha.1
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
Restart the Web profile after installation.
|
package/README.zh-CN.md
CHANGED
|
@@ -57,12 +57,14 @@ audio/sound.mp3 → <audio controls>
|
|
|
57
57
|
|
|
58
58
|
## 安装
|
|
59
59
|
|
|
60
|
+
适配 DSH **v0.1.3-alpha.1**;旧版 DSH 请使用插件 **1.0.9**。
|
|
61
|
+
|
|
60
62
|
### 通过 npm 安装
|
|
61
63
|
|
|
62
64
|
在 Web profile 中安装并启用 Bundle:
|
|
63
65
|
|
|
64
66
|
```bash
|
|
65
|
-
dsh plugin --profile web add @wisdoverse/dsh-inline-media-viewer
|
|
67
|
+
dsh plugin --profile web add @wisdoverse/dsh-inline-media-viewer@1.0.10-alpha.1
|
|
66
68
|
```
|
|
67
69
|
|
|
68
70
|
安装后重启 Web profile。
|
package/client/client.js
CHANGED
|
@@ -80,6 +80,11 @@ window.__ModuleLoader__.load({
|
|
|
80
80
|
const found = [];
|
|
81
81
|
const seen = new Set();
|
|
82
82
|
let directory = "";
|
|
83
|
+
const ext = Array.from(MEDIA_EXTENSIONS).join("|");
|
|
84
|
+
const directories = new Set(Array.from(text.matchAll(
|
|
85
|
+
/[`'"]((?:\/|\.\.?\/|[\w.-]+\/)[^`'"\n]*[\/\\])[`'"]/g,
|
|
86
|
+
), (match) => cleanCandidate(match[1])));
|
|
87
|
+
const soleDirectory = directories.size === 1 ? directories.values().next().value : "";
|
|
83
88
|
const add = (raw) => {
|
|
84
89
|
const source = cleanCandidate(raw);
|
|
85
90
|
const kind = mediaKind(source);
|
|
@@ -95,7 +100,10 @@ window.__ModuleLoader__.load({
|
|
|
95
100
|
plain = plain.replace(/`([^`\n]+)`/g, (match, source) => {
|
|
96
101
|
const value = cleanCandidate(source);
|
|
97
102
|
if (/[/\\]$/.test(value)) directory = value;
|
|
98
|
-
else
|
|
103
|
+
else {
|
|
104
|
+
const base = directory || soleDirectory;
|
|
105
|
+
add(base && !/[/\\]/.test(value) ? `${base}${value}` : value);
|
|
106
|
+
}
|
|
99
107
|
return " ".repeat(match.length);
|
|
100
108
|
});
|
|
101
109
|
plain = plain.replace(/https?:\/\/[^\s<>"'`]+/gi, (source) => {
|
|
@@ -103,7 +111,11 @@ window.__ModuleLoader__.load({
|
|
|
103
111
|
return " ".repeat(source.length);
|
|
104
112
|
});
|
|
105
113
|
|
|
106
|
-
|
|
114
|
+
if (soleDirectory) {
|
|
115
|
+
const filenamePattern = new RegExp(String.raw`[\x60'"]([^\x60'"/\\\n]+\.(?:${ext})(?:\?[^\x60'"\s]*)?)[\x60'"]`, "gi");
|
|
116
|
+
for (const match of text.matchAll(filenamePattern)) add(`${soleDirectory}${match[1]}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
107
119
|
const pathPattern = new RegExp(
|
|
108
120
|
String.raw`(?:\/|\.\.?\/|[\w.-]+\/)[^\s<>"'\x60()\[\]{}]+?\.(?:${ext})(?:\?[^\s<>"'\x60]*)?`,
|
|
109
121
|
"gi",
|
|
@@ -112,10 +124,48 @@ window.__ModuleLoader__.load({
|
|
|
112
124
|
return found;
|
|
113
125
|
}
|
|
114
126
|
|
|
127
|
+
function toolArgumentText(raw) {
|
|
128
|
+
try {
|
|
129
|
+
const text = [];
|
|
130
|
+
const visit = (value) => {
|
|
131
|
+
if (typeof value === "string") text.push(value);
|
|
132
|
+
else if (value && typeof value === "object") Object.values(value).forEach(visit);
|
|
133
|
+
};
|
|
134
|
+
visit(JSON.parse(raw));
|
|
135
|
+
return text.join("\n");
|
|
136
|
+
} catch (_error) {
|
|
137
|
+
return raw;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const mediaMentionsDefinition = {
|
|
142
|
+
kind: "inline-media-mentions",
|
|
143
|
+
match: (event) => {
|
|
144
|
+
if (event.type === "turn/start") return { id: String(event.data.turn), role: "start" };
|
|
145
|
+
if (event.type === "tool/call") return { id: String(event.data.turn), role: "update" };
|
|
146
|
+
return null;
|
|
147
|
+
},
|
|
148
|
+
start: (_context, match) => ({ turn: match.event.data.turn, texts: [] }),
|
|
149
|
+
update: (context, match) => typeof match.event.data.arguments !== "string" ? context.state : {
|
|
150
|
+
...context.state,
|
|
151
|
+
texts: [...context.state.texts, { seq: match.event.seq, text: toolArgumentText(match.event.data.arguments) }],
|
|
152
|
+
},
|
|
153
|
+
buildLocationData: (context, scope) => scope !== "turn" || context.state === undefined ? null : {
|
|
154
|
+
kind: "turn",
|
|
155
|
+
turn: context.state.turn,
|
|
156
|
+
key: "inline-media-mentions",
|
|
157
|
+
value: { texts: context.state.texts },
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
115
161
|
function selectMedia(owner) {
|
|
116
162
|
const settings = readSettings();
|
|
117
163
|
if (!settings.autoRender) return null;
|
|
118
164
|
const text = [];
|
|
165
|
+
const mentions = owner.turn.data && owner.turn.data.get("inline-media-mentions");
|
|
166
|
+
if (mentions) {
|
|
167
|
+
text.push(...mentions.texts.filter((entry) => entry.seq <= owner.seq).map((entry) => entry.text));
|
|
168
|
+
}
|
|
119
169
|
for (const step of owner.turn.steps) {
|
|
120
170
|
const assistant = step.data.get("assistant-step");
|
|
121
171
|
if (!assistant || !assistant.finalNode || assistant.finalNode.seq > owner.seq || !Array.isArray(assistant.blocks)) continue;
|
|
@@ -492,10 +542,11 @@ window.__ModuleLoader__.load({
|
|
|
492
542
|
}
|
|
493
543
|
|
|
494
544
|
const name = "dsh-inline-media-viewer";
|
|
495
|
-
const inject = ["slots", "connection", "settingsScope", "locale"];
|
|
545
|
+
const inject = ["slots", "connection", "settingsScope", "locale", "uiConversation"];
|
|
496
546
|
|
|
497
547
|
function apply(ctx) {
|
|
498
548
|
const connection = ctx.get("connection");
|
|
549
|
+
ctx.uiConversation.events.register(mediaMentionsDefinition);
|
|
499
550
|
ctx.effect(() => ctx.locale.register(SETTINGS_NS, SETTINGS_DICT), "inline-media: dictionaries");
|
|
500
551
|
const t = ctx.locale.bind(SETTINGS_NS);
|
|
501
552
|
const scope = ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE });
|
|
@@ -518,7 +569,7 @@ window.__ModuleLoader__.load({
|
|
|
518
569
|
exports.apply = apply;
|
|
519
570
|
exports.inject = inject;
|
|
520
571
|
exports.name = name;
|
|
521
|
-
exports.testing = Object.freeze({ extractCandidates, mediaTransport, selectMedia });
|
|
572
|
+
exports.testing = Object.freeze({ extractCandidates, mediaMentionsDefinition, mediaTransport, selectMedia, toolArgumentText });
|
|
522
573
|
return module.exports;
|
|
523
574
|
},
|
|
524
575
|
});
|
package/index.js
CHANGED
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
import { readFile, realpath, stat } from "node:fs/promises";
|
|
21
21
|
import { isAbsolute, resolve } from "node:path";
|
|
22
22
|
import z from "@deepseek-ai/schemastery";
|
|
23
|
-
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
24
23
|
|
|
25
24
|
import { COMFY_DEFAULT_ORIGIN, MAX_BYTES, comfyUrl, isInside, mimeOf, normalizeComfyOrigin, resolveSessionCwd, testing } from "./lib.js";
|
|
26
25
|
|
|
@@ -33,7 +32,7 @@ const CHANNEL = "/inline-media";
|
|
|
33
32
|
const ENDPOINT = "read";
|
|
34
33
|
|
|
35
34
|
/** Persistent user settings namespace for this plugin. */
|
|
36
|
-
export const MEDIA_SETTINGS_NAMESPACE =
|
|
35
|
+
export const MEDIA_SETTINGS_NAMESPACE = "inline-media";
|
|
37
36
|
|
|
38
37
|
/** Schema of the user settings section. */
|
|
39
38
|
export const MEDIA_SETTINGS_SCHEMA = z.object({
|
|
@@ -157,28 +156,19 @@ async function handleRead(ctx, endpoint, payload, signal, resolveSettings) {
|
|
|
157
156
|
}
|
|
158
157
|
|
|
159
158
|
export function apply(ctx) {
|
|
160
|
-
|
|
159
|
+
const settings = ctx.settings.register(MEDIA_SETTINGS_NAMESPACE, MEDIA_SETTINGS_SCHEMA, {
|
|
160
|
+
base: MEDIA_SETTINGS_DEFAULTS,
|
|
161
|
+
});
|
|
162
|
+
const resolveSettings = () => settings.get();
|
|
161
163
|
ctx.effect(() => {
|
|
162
164
|
const dispose = ctx.connection.rpc.handle(
|
|
163
165
|
CHANNEL,
|
|
164
166
|
(endpoint, payload, signal) => handleRead(ctx, endpoint, payload, signal, resolveSettings),
|
|
165
|
-
{ authority: "trusted-host" },
|
|
166
167
|
);
|
|
167
168
|
return () => {
|
|
168
169
|
void dispose();
|
|
169
170
|
};
|
|
170
171
|
}, "inline-media: rpc");
|
|
171
|
-
|
|
172
|
-
// Register the user settings namespace (defaults + schema). The client reads
|
|
173
|
-
// and writes this section through the settings mirror. autoRender /
|
|
174
|
-
// displayCap / imageMaxPx are consumed client-side; comfyUrl is read here,
|
|
175
|
-
// per request, to decide which remote URLs to proxy and where to fetch them.
|
|
176
|
-
installSettingsSection(ctx, MEDIA_SETTINGS_NAMESPACE, MEDIA_SETTINGS_SCHEMA, MEDIA_SETTINGS_DEFAULTS, {
|
|
177
|
-
setSource: (source) => {
|
|
178
|
-
resolveSettings = source;
|
|
179
|
-
},
|
|
180
|
-
onChange: () => {},
|
|
181
|
-
});
|
|
182
172
|
}
|
|
183
173
|
|
|
184
174
|
export { MIME } from "./lib.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wisdoverse/dsh-inline-media-viewer",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10-alpha.1",
|
|
4
4
|
"description": "Persistent inline image, video, and audio previews for DeepSeek Harness Web conversations, with workspace-confined local reads and an optional ComfyUI proxy.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
],
|
|
50
50
|
"scripts": {
|
|
51
51
|
"test": "node test.mjs",
|
|
52
|
-
"
|
|
52
|
+
"test:host": "node test.mjs --host",
|
|
53
|
+
"lint": "node --check index.js && node --check lib.js && node --check client/client.js && node --check test.mjs"
|
|
53
54
|
},
|
|
54
55
|
"peerDependencies": {
|
|
55
56
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
56
|
-
"@deepseek-ai/dsh-settings": "^0.1.1-rc.2",
|
|
57
57
|
"@deepseek-ai/schemastery": "^3.18.1"
|
|
58
58
|
},
|
|
59
59
|
"dsh": {
|