@wisdoverse/dsh-inline-media-viewer 1.0.7 → 1.0.9
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 +13 -1
- package/client/client.js +59 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 1.0.
|
|
3
|
+
## 1.0.9
|
|
4
|
+
|
|
5
|
+
- Collect media paths from tool-call arguments as well as the assistant's final
|
|
6
|
+
text, so every image read during a turn remains available in its preview.
|
|
7
|
+
|
|
8
|
+
## 1.0.8 (deprecated)
|
|
9
|
+
|
|
10
|
+
- Preserve output-directory context across assistant steps in one turn, so a
|
|
11
|
+
later final answer containing only media filenames still renders previews.
|
|
12
|
+
- Superseded by 1.0.9 because tool-call-only media references were not retained.
|
|
13
|
+
|
|
14
|
+
## 1.0.7 (deprecated)
|
|
4
15
|
|
|
5
16
|
- Resolve backticked media filenames against a preceding backticked directory,
|
|
6
17
|
matching the common "output directory plus file list" response format.
|
|
18
|
+
- Superseded by 1.0.9 because it only inspected assistant text.
|
|
7
19
|
|
|
8
20
|
## 1.0.6
|
|
9
21
|
|
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,25 +124,56 @@ 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
|
-
const
|
|
119
|
-
const
|
|
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
|
+
}
|
|
120
169
|
for (const step of owner.turn.steps) {
|
|
121
170
|
const assistant = step.data.get("assistant-step");
|
|
122
171
|
if (!assistant || !assistant.finalNode || assistant.finalNode.seq > owner.seq || !Array.isArray(assistant.blocks)) continue;
|
|
123
|
-
|
|
172
|
+
text.push(...assistant.blocks
|
|
124
173
|
.filter((block) => block && block.kind === "text" && typeof block.text === "string")
|
|
125
|
-
.map((block) => block.text)
|
|
126
|
-
.join("\n");
|
|
127
|
-
for (const candidate of extractCandidates(text)) {
|
|
128
|
-
if (seen.has(candidate.source)) continue;
|
|
129
|
-
seen.add(candidate.source);
|
|
130
|
-
candidates.push(candidate);
|
|
131
|
-
if (candidates.length === settings.displayCap) return candidates;
|
|
132
|
-
}
|
|
174
|
+
.map((block) => block.text));
|
|
133
175
|
}
|
|
176
|
+
const candidates = extractCandidates(text.join("\n")).slice(0, settings.displayCap);
|
|
134
177
|
return candidates.length === 0 ? null : candidates;
|
|
135
178
|
}
|
|
136
179
|
|
|
@@ -499,10 +542,11 @@ window.__ModuleLoader__.load({
|
|
|
499
542
|
}
|
|
500
543
|
|
|
501
544
|
const name = "dsh-inline-media-viewer";
|
|
502
|
-
const inject = ["slots", "connection", "settingsScope", "locale"];
|
|
545
|
+
const inject = ["slots", "connection", "settingsScope", "locale", "conversationEvents"];
|
|
503
546
|
|
|
504
547
|
function apply(ctx) {
|
|
505
548
|
const connection = ctx.get("connection");
|
|
549
|
+
ctx.conversationEvents.register(mediaMentionsDefinition);
|
|
506
550
|
ctx.effect(() => ctx.locale.register(SETTINGS_NS, SETTINGS_DICT), "inline-media: dictionaries");
|
|
507
551
|
const t = ctx.locale.bind(SETTINGS_NS);
|
|
508
552
|
const scope = ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE });
|
|
@@ -525,7 +569,7 @@ window.__ModuleLoader__.load({
|
|
|
525
569
|
exports.apply = apply;
|
|
526
570
|
exports.inject = inject;
|
|
527
571
|
exports.name = name;
|
|
528
|
-
exports.testing = Object.freeze({ extractCandidates, mediaTransport, selectMedia });
|
|
572
|
+
exports.testing = Object.freeze({ extractCandidates, mediaMentionsDefinition, mediaTransport, selectMedia, toolArgumentText });
|
|
529
573
|
return module.exports;
|
|
530
574
|
},
|
|
531
575
|
});
|
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.9",
|
|
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",
|