dsh-plugin-lookatstudy 0.12.2 → 0.12.4
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/lib/client.js +35 -17
- package/lib/client.js.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -298,10 +298,10 @@ window.__ModuleLoader__.load({
|
|
|
298
298
|
* ic_ds_* glyphs vendored from @deepseek-ai/dsh-client-ui-primitives
|
|
299
299
|
* (src/icons/index.tsx, same figma source as the host's icon set) so the
|
|
300
300
|
* plugin's client bundle stays self-contained per the dsh client-bundle
|
|
301
|
-
* contract — only react
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
301
|
+
* contract — only react is runtime-external; importing the host package
|
|
302
|
+
* root would drag its CSS modules into the inlined bundle. Re-sync these
|
|
303
|
+
* three paths if the host icon set redesigns them; scripts/verify.mjs
|
|
304
|
+
* pins the path data.
|
|
305
305
|
*
|
|
306
306
|
* 2026-09-01 critique round: seven more ic_ds glyphs vendored (play/plus/
|
|
307
307
|
* trash/refresh/globe/goal/warning) plus plugin-original learning-domain
|
|
@@ -2045,16 +2045,19 @@ window.__ModuleLoader__.load({
|
|
|
2045
2045
|
return runs.at(-1) ?? [];
|
|
2046
2046
|
}
|
|
2047
2047
|
function textOf(blocks) {
|
|
2048
|
-
return blocks.filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n\n").trim();
|
|
2048
|
+
return (blocks ?? []).filter((b) => b.type === "text").map((b) => b.text ?? "").join("\n\n").trim();
|
|
2049
2049
|
}
|
|
2050
2050
|
function assistantText(blocks) {
|
|
2051
|
-
return blocks.filter((b) => b.kind === "text").map((b) => b.text).join("\n\n").trim();
|
|
2051
|
+
return (blocks ?? []).filter((b) => b.kind === "text").map((b) => b.text ?? "").join("\n\n").trim();
|
|
2052
2052
|
}
|
|
2053
2053
|
/**
|
|
2054
2054
|
* Fold conversation nodes (plus the streaming partial) into render rows.
|
|
2055
2055
|
* Tool results are skipped: the host conversation renders the rich
|
|
2056
2056
|
* `tool.call.toolview` cards, and the tutor column stays a pure persona
|
|
2057
2057
|
* surface (a transcript mirror there grew a second, diverging record).
|
|
2058
|
+
* Both inputs tolerate undefined: newer hosts (DSH v0.1.2-alpha.4) mount
|
|
2059
|
+
* the conversation view before session data exists, and the fold must
|
|
2060
|
+
* degrade to an empty transcript instead of blanking the whole tab.
|
|
2058
2061
|
* @param nodes - finalized conversation nodes from the snapshot.
|
|
2059
2062
|
* @param partial - the in-flight assistant partial, or null.
|
|
2060
2063
|
* @param t - translator (defaults to the active one, per call).
|
|
@@ -2062,7 +2065,7 @@ window.__ModuleLoader__.load({
|
|
|
2062
2065
|
*/
|
|
2063
2066
|
function transcriptRows(nodes, partial, t = tr) {
|
|
2064
2067
|
const rows = [];
|
|
2065
|
-
for (const node of nodes) switch (node.kind) {
|
|
2068
|
+
for (const node of nodes ?? []) switch (node.kind) {
|
|
2066
2069
|
case "user":
|
|
2067
2070
|
case "steering": {
|
|
2068
2071
|
const text = textOf(node.content);
|
|
@@ -2085,10 +2088,10 @@ window.__ModuleLoader__.load({
|
|
|
2085
2088
|
case "turn-error": rows.push({
|
|
2086
2089
|
key: `e${node.seq}`,
|
|
2087
2090
|
role: "error",
|
|
2088
|
-
text: node.message
|
|
2091
|
+
text: node.message ?? ""
|
|
2089
2092
|
});
|
|
2090
2093
|
}
|
|
2091
|
-
if (partial
|
|
2094
|
+
if (partial != null) {
|
|
2092
2095
|
const text = assistantText(partial.blocks);
|
|
2093
2096
|
if (text !== "") rows.push({
|
|
2094
2097
|
key: "streaming",
|
|
@@ -2108,6 +2111,17 @@ window.__ModuleLoader__.load({
|
|
|
2108
2111
|
if (error === null) return null;
|
|
2109
2112
|
return (0, react.createElement)("div", { className: "lks-propcard-err" }, error);
|
|
2110
2113
|
}
|
|
2114
|
+
/**
|
|
2115
|
+
* Resolve the transcript across host generations. 0.1.3+ hosts expose the
|
|
2116
|
+
* node array as the Chat target's `legacy` slice; pre-0.1.3 hosts hand the
|
|
2117
|
+
* whole conversation snapshot (nodes + partial) through `useSession`, whose
|
|
2118
|
+
* 0.1.3+ successor returns the session MACHINE (no node array). The array
|
|
2119
|
+
* check tells those two `useSession` shapes apart. Pure.
|
|
2120
|
+
*/
|
|
2121
|
+
function pickTranscript(chatLegacy, sessionSnapshot) {
|
|
2122
|
+
if (chatLegacy !== void 0) return chatLegacy;
|
|
2123
|
+
return sessionSnapshot !== void 0 && Array.isArray(sessionSnapshot.nodes) ? sessionSnapshot : void 0;
|
|
2124
|
+
}
|
|
2111
2125
|
/** sessionStorage key keeping the narrow-mode pane across view remounts (tab switches) and reloads. */
|
|
2112
2126
|
const PANE_KEY = "dsh-plugin-lookatstudy:pane";
|
|
2113
2127
|
/** Narrow the stored value back to a pane id; anything else (or nothing stored) falls back to 导师. Pure, unit-testable. */
|
|
@@ -2134,9 +2148,12 @@ window.__ModuleLoader__.load({
|
|
|
2134
2148
|
};
|
|
2135
2149
|
}
|
|
2136
2150
|
/** Tab body: the factory-bound ctx carries workspaces/sessions for the per-lesson session jumps. */
|
|
2137
|
-
function StudyTab({
|
|
2151
|
+
function StudyTab({ inputActions, ctx, ...standard }) {
|
|
2138
2152
|
const { data, activate, setMode, setFocus, searchLessons, deleteCourse, bindLessonSession } = useStudy();
|
|
2139
|
-
const
|
|
2153
|
+
const chatLegacy = standard.useChat?.((s) => s.legacy);
|
|
2154
|
+
const sessionSnapshot = standard.useSession?.((s) => s);
|
|
2155
|
+
const snapshot = pickTranscript(chatLegacy, sessionSnapshot);
|
|
2156
|
+
const currentSessionId = standard.sessionId ?? snapshot?.sessionId ?? "";
|
|
2140
2157
|
const [pane, setPane] = (0, react.useState)(storedPane);
|
|
2141
2158
|
const rootRef = (0, react.useRef)(null);
|
|
2142
2159
|
(0, react.useEffect)(() => {
|
|
@@ -2234,7 +2251,7 @@ window.__ModuleLoader__.load({
|
|
|
2234
2251
|
bindLessonSession,
|
|
2235
2252
|
send,
|
|
2236
2253
|
ctx,
|
|
2237
|
-
currentSessionId
|
|
2254
|
+
currentSessionId
|
|
2238
2255
|
}), (0, react.createElement)(TutorColumn, {
|
|
2239
2256
|
data,
|
|
2240
2257
|
setMode,
|
|
@@ -2265,7 +2282,7 @@ window.__ModuleLoader__.load({
|
|
|
2265
2282
|
if (!area.ok) throw new Error(`study area unavailable (HTTP ${area.status})`);
|
|
2266
2283
|
const { path } = await area.json();
|
|
2267
2284
|
const workspace = await ctx.workspaces.create({ path });
|
|
2268
|
-
const sessionId = await ctx.workspaces.connectWorkspace(workspace.workspaceId);
|
|
2285
|
+
const sessionId = ctx.workspaces.connectWorkspace !== void 0 ? await ctx.workspaces.connectWorkspace(workspace.workspaceId) : await ctx.sessions.create({ workspaceId: workspace.workspaceId });
|
|
2269
2286
|
const actx = ctx.sessions.scope(sessionId);
|
|
2270
2287
|
const face = actx === void 0 ? void 0 : ctx.sessions.sessionOf(actx);
|
|
2271
2288
|
if (face === void 0) throw new Error("lesson session is not addressable yet");
|
|
@@ -2517,7 +2534,7 @@ window.__ModuleLoader__.load({
|
|
|
2517
2534
|
/** Middle column: the tutor — transcript, proposal banner, pills, starters. Typing happens in the native composer below the tab. */
|
|
2518
2535
|
function TutorColumn({ data, setMode, send, snapshot }) {
|
|
2519
2536
|
const [error, setError] = (0, react.useState)(null);
|
|
2520
|
-
const rows = transcriptRows(snapshot
|
|
2537
|
+
const rows = transcriptRows(snapshot?.nodes, snapshot?.partial ?? null);
|
|
2521
2538
|
const lastAssistant = rows.reduce((acc, row, i) => row.role === "assistant" ? i : acc, -1);
|
|
2522
2539
|
const scrollRef = (0, react.useRef)(null);
|
|
2523
2540
|
(0, react.useEffect)(() => {
|
|
@@ -2666,8 +2683,9 @@ window.__ModuleLoader__.load({
|
|
|
2666
2683
|
* @returns the component for `conversation.input.left`.
|
|
2667
2684
|
*/
|
|
2668
2685
|
function studyStartButton(ctx) {
|
|
2669
|
-
return function StudyStartButton(
|
|
2670
|
-
|
|
2686
|
+
return function StudyStartButton(props) {
|
|
2687
|
+
const machine = props.useSession?.((s) => s);
|
|
2688
|
+
if ((props.session?.blank ?? machine?.blank) !== true) return null;
|
|
2671
2689
|
return (0, react.createElement)(Inner, {
|
|
2672
2690
|
key: "inner",
|
|
2673
2691
|
ctx
|
|
@@ -2690,7 +2708,7 @@ window.__ModuleLoader__.load({
|
|
|
2690
2708
|
if (!area.ok) throw new Error(`study area unavailable (HTTP ${area.status})`);
|
|
2691
2709
|
const { path } = await area.json();
|
|
2692
2710
|
const workspace = await ctx.workspaces.create({ path });
|
|
2693
|
-
const sessionId = await ctx.workspaces.connectWorkspace(workspace.workspaceId);
|
|
2711
|
+
const sessionId = ctx.workspaces.connectWorkspace !== void 0 ? await ctx.workspaces.connectWorkspace(workspace.workspaceId) : await ctx.sessions.create({ workspaceId: workspace.workspaceId });
|
|
2694
2712
|
const actx = ctx.sessions.scope(sessionId);
|
|
2695
2713
|
const face = actx === void 0 ? void 0 : ctx.sessions.sessionOf(actx);
|
|
2696
2714
|
if (face === void 0) throw new Error("study session is not addressable yet");
|