@wisdoverse/dsh-inline-media-viewer 1.0.3 → 1.0.6
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 +16 -0
- package/client/client.js +33 -66
- package/index.js +3 -4
- package/lib.js +7 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.6
|
|
4
|
+
|
|
5
|
+
- Resolve workspace media from persisted session metadata when a historical
|
|
6
|
+
conversation is no longer attached to a live host session.
|
|
7
|
+
|
|
8
|
+
## 1.0.5
|
|
9
|
+
|
|
10
|
+
- Register the browser bundle under the scoped package name expected by the
|
|
11
|
+
DSH module loader.
|
|
12
|
+
|
|
13
|
+
## 1.0.4
|
|
14
|
+
|
|
15
|
+
- Read media candidates from the standard assistant-step projection so the
|
|
16
|
+
first inline preview renders reliably.
|
|
17
|
+
- Avoid extracting duplicate paths from Markdown links, code spans, and URLs.
|
|
18
|
+
|
|
3
19
|
## 1.0.3
|
|
4
20
|
|
|
5
21
|
- Clarify and enforce the transport split: workspace files use the DSH host,
|
package/client/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
window.__ModuleLoader__.load({
|
|
2
|
-
id: "dsh-inline-media-viewer",
|
|
2
|
+
id: "@wisdoverse/dsh-inline-media-viewer",
|
|
3
3
|
factory: (require) => {
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
@@ -9,7 +9,6 @@ window.__ModuleLoader__.load({
|
|
|
9
9
|
const { createElement: h, useEffect, useMemo, useState } = React;
|
|
10
10
|
const CHANNEL = "/inline-media";
|
|
11
11
|
const ENDPOINT = "read";
|
|
12
|
-
const DATA_KEY = "inlineMedia";
|
|
13
12
|
const DISPLAY_CAP = 12;
|
|
14
13
|
const SETTINGS_NAMESPACE = "inline-media";
|
|
15
14
|
// Empty config = the host-side built-in default
|
|
@@ -88,79 +87,48 @@ window.__ModuleLoader__.load({
|
|
|
88
87
|
found.push({ source, kind });
|
|
89
88
|
};
|
|
90
89
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
let plain = text.replace(/!?\[[^\]]*\]\(([^)]+)\)/g, (match, source) => {
|
|
91
|
+
add(source);
|
|
92
|
+
return " ".repeat(match.length);
|
|
93
|
+
});
|
|
94
|
+
plain = plain.replace(/`([^`\n]+)`/g, (match, source) => {
|
|
95
|
+
add(source);
|
|
96
|
+
return " ".repeat(match.length);
|
|
97
|
+
});
|
|
98
|
+
plain = plain.replace(/https?:\/\/[^\s<>"'`]+/gi, (source) => {
|
|
99
|
+
add(source);
|
|
100
|
+
return " ".repeat(source.length);
|
|
101
|
+
});
|
|
94
102
|
|
|
95
103
|
const ext = Array.from(MEDIA_EXTENSIONS).join("|");
|
|
96
104
|
const pathPattern = new RegExp(
|
|
97
105
|
String.raw`(?:\/|\.\.?\/|[\w.-]+\/)[^\s<>"'\x60()\[\]{}]+?\.(?:${ext})(?:\?[^\s<>"'\x60]*)?`,
|
|
98
106
|
"gi",
|
|
99
107
|
);
|
|
100
|
-
for (const match of
|
|
108
|
+
for (const match of plain.matchAll(pathPattern)) add(match[0]);
|
|
101
109
|
return found;
|
|
102
110
|
}
|
|
103
111
|
|
|
104
|
-
function assistantText(event) {
|
|
105
|
-
if (event.type !== "assistant/message") return "";
|
|
106
|
-
const content = event.data && event.data.message && event.data.message.content;
|
|
107
|
-
if (!Array.isArray(content)) return "";
|
|
108
|
-
return content
|
|
109
|
-
.filter((block) => block && block.type === "text" && typeof block.text === "string")
|
|
110
|
-
.map((block) => block.text)
|
|
111
|
-
.join("\n");
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const mediaDefinition = {
|
|
115
|
-
kind: DATA_KEY,
|
|
116
|
-
match(event) {
|
|
117
|
-
if (event.type === "turn/start") {
|
|
118
|
-
return { id: String(event.data.turn), role: "start" };
|
|
119
|
-
}
|
|
120
|
-
if (event.type === "assistant/message" && Number.isInteger(event.data && event.data.turn)) {
|
|
121
|
-
return { id: String(event.data.turn), role: "update" };
|
|
122
|
-
}
|
|
123
|
-
return null;
|
|
124
|
-
},
|
|
125
|
-
start(_context, match) {
|
|
126
|
-
return { turn: match.event.data.turn, candidates: [] };
|
|
127
|
-
},
|
|
128
|
-
update(context, match) {
|
|
129
|
-
const additions = extractCandidates(assistantText(match.event)).map((candidate) => ({
|
|
130
|
-
...candidate,
|
|
131
|
-
seq: match.event.seq,
|
|
132
|
-
}));
|
|
133
|
-
if (additions.length === 0) return context.state;
|
|
134
|
-
const seen = new Set(context.state.candidates.map((candidate) => candidate.source));
|
|
135
|
-
const fresh = additions.filter((candidate) => {
|
|
136
|
-
if (seen.has(candidate.source)) return false;
|
|
137
|
-
seen.add(candidate.source);
|
|
138
|
-
return true;
|
|
139
|
-
});
|
|
140
|
-
if (fresh.length === 0) return context.state;
|
|
141
|
-
return {
|
|
142
|
-
...context.state,
|
|
143
|
-
candidates: [...context.state.candidates, ...fresh].slice(0, DISPLAY_CAP),
|
|
144
|
-
};
|
|
145
|
-
},
|
|
146
|
-
buildLocationData(context, scope) {
|
|
147
|
-
if (scope !== "turn" || context.state === undefined) return null;
|
|
148
|
-
return {
|
|
149
|
-
kind: "turn",
|
|
150
|
-
turn: context.state.turn,
|
|
151
|
-
key: DATA_KEY,
|
|
152
|
-
value: { candidates: context.state.candidates },
|
|
153
|
-
};
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
|
|
157
112
|
function selectMedia(owner) {
|
|
158
113
|
const settings = readSettings();
|
|
159
114
|
if (!settings.autoRender) return null;
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
|
|
115
|
+
const candidates = [];
|
|
116
|
+
const seen = new Set();
|
|
117
|
+
for (const step of owner.turn.steps) {
|
|
118
|
+
const assistant = step.data.get("assistant-step");
|
|
119
|
+
if (!assistant || !assistant.finalNode || assistant.finalNode.seq > owner.seq || !Array.isArray(assistant.blocks)) continue;
|
|
120
|
+
const text = assistant.blocks
|
|
121
|
+
.filter((block) => block && block.kind === "text" && typeof block.text === "string")
|
|
122
|
+
.map((block) => block.text)
|
|
123
|
+
.join("\n");
|
|
124
|
+
for (const candidate of extractCandidates(text)) {
|
|
125
|
+
if (seen.has(candidate.source)) continue;
|
|
126
|
+
seen.add(candidate.source);
|
|
127
|
+
candidates.push(candidate);
|
|
128
|
+
if (candidates.length === settings.displayCap) return candidates;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return candidates.length === 0 ? null : candidates;
|
|
164
132
|
}
|
|
165
133
|
|
|
166
134
|
function normalizeComfyOrigin(input) {
|
|
@@ -528,7 +496,7 @@ window.__ModuleLoader__.load({
|
|
|
528
496
|
}
|
|
529
497
|
|
|
530
498
|
const name = "dsh-inline-media-viewer";
|
|
531
|
-
const inject = ["slots", "
|
|
499
|
+
const inject = ["slots", "connection", "settingsScope", "locale"];
|
|
532
500
|
|
|
533
501
|
function apply(ctx) {
|
|
534
502
|
const connection = ctx.get("connection");
|
|
@@ -536,7 +504,6 @@ window.__ModuleLoader__.load({
|
|
|
536
504
|
const t = ctx.locale.bind(SETTINGS_NS);
|
|
537
505
|
const scope = ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE });
|
|
538
506
|
settingsScope = scope;
|
|
539
|
-
ctx.conversationEvents.register(mediaDefinition);
|
|
540
507
|
ctx.slots.inject("conversation.chat.turnTail", () => ctx.slots.register({
|
|
541
508
|
name: "conversation.chat.turnTail",
|
|
542
509
|
select: selectMedia,
|
|
@@ -555,7 +522,7 @@ window.__ModuleLoader__.load({
|
|
|
555
522
|
exports.apply = apply;
|
|
556
523
|
exports.inject = inject;
|
|
557
524
|
exports.name = name;
|
|
558
|
-
exports.testing = Object.freeze({ mediaTransport });
|
|
525
|
+
exports.testing = Object.freeze({ extractCandidates, mediaTransport, selectMedia });
|
|
559
526
|
return module.exports;
|
|
560
527
|
},
|
|
561
528
|
});
|
package/index.js
CHANGED
|
@@ -22,12 +22,12 @@ import { isAbsolute, resolve } from "node:path";
|
|
|
22
22
|
import z from "@deepseek-ai/schemastery";
|
|
23
23
|
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
24
24
|
|
|
25
|
-
import { COMFY_DEFAULT_ORIGIN, MAX_BYTES, comfyUrl, isInside, mimeOf, normalizeComfyOrigin, testing } from "./lib.js";
|
|
25
|
+
import { COMFY_DEFAULT_ORIGIN, MAX_BYTES, comfyUrl, isInside, mimeOf, normalizeComfyOrigin, resolveSessionCwd, testing } from "./lib.js";
|
|
26
26
|
|
|
27
27
|
export { testing } from "./lib.js";
|
|
28
28
|
|
|
29
29
|
export const name = "dsh-inline-media-viewer";
|
|
30
|
-
export const inject = ["connection", "sessions", "settings"];
|
|
30
|
+
export const inject = ["connection", "sessions", "sessionQuery", "settings"];
|
|
31
31
|
|
|
32
32
|
const CHANNEL = "/inline-media";
|
|
33
33
|
const ENDPOINT = "read";
|
|
@@ -119,8 +119,7 @@ async function readRemote(source, signal, settingsValue) {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
async function readLocal(ctx, source, sessionId) {
|
|
122
|
-
const
|
|
123
|
-
const cwd = session?.header?.cwd;
|
|
122
|
+
const cwd = await resolveSessionCwd(ctx.sessions, ctx.sessionQuery, sessionId);
|
|
124
123
|
if (!cwd) throw new Error("session working directory is unavailable");
|
|
125
124
|
const mime = mimeOf(source);
|
|
126
125
|
if (!mime) throw new Error("unsupported media extension");
|
package/lib.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* dsh-inline-media-viewer —
|
|
2
|
+
* dsh-inline-media-viewer — dependency-free helpers.
|
|
3
3
|
*
|
|
4
4
|
* This module has NO runtime dependencies: unit tests and reviewers can import
|
|
5
5
|
* it anywhere. Integration concerns (RPC channel, settings registration) live
|
|
@@ -79,6 +79,11 @@ export function isInside(root, target) {
|
|
|
79
79
|
return rel === "" || (!isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${sep}`));
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
export async function resolveSessionCwd(sessions, sessionQuery, sessionId) {
|
|
83
|
+
const live = sessions.get(sessionId);
|
|
84
|
+
return live ? live.header.cwd : (await sessionQuery.readSession(sessionId)).session.cwd;
|
|
85
|
+
}
|
|
86
|
+
|
|
82
87
|
/**
|
|
83
88
|
* Parse a user-configured ComfyUI address into a canonical origin URL.
|
|
84
89
|
*
|
|
@@ -175,4 +180,5 @@ export const testing = Object.freeze({
|
|
|
175
180
|
isInside,
|
|
176
181
|
mimeOf,
|
|
177
182
|
normalizeComfyOrigin,
|
|
183
|
+
resolveSessionCwd,
|
|
178
184
|
});
|
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.6",
|
|
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",
|