@wisdoverse/dsh-inline-media-viewer 1.0.8 → 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 CHANGED
@@ -1,14 +1,21 @@
1
1
  # Changelog
2
2
 
3
- ## 1.0.8
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)
4
9
 
5
10
  - Preserve output-directory context across assistant steps in one turn, so a
6
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.
7
13
 
8
- ## 1.0.7
14
+ ## 1.0.7 (deprecated)
9
15
 
10
16
  - Resolve backticked media filenames against a preceding backticked directory,
11
17
  matching the common "output directory plus file list" response format.
18
+ - Superseded by 1.0.9 because it only inspected assistant text.
12
19
 
13
20
  ## 1.0.6
14
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 add(directory && !/[/\\]/.test(value) ? `${directory}${value}` : value);
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
- const ext = Array.from(MEDIA_EXTENSIONS).join("|");
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", "conversationEvents"];
496
546
 
497
547
  function apply(ctx) {
498
548
  const connection = ctx.get("connection");
549
+ ctx.conversationEvents.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wisdoverse/dsh-inline-media-viewer",
3
- "version": "1.0.8",
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",