dsh-plugin-lookatstudy 0.14.0 → 0.14.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/lib/client.js +84 -13
- package/lib/client.js.map +1 -1
- package/lib/index.mjs +1 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -2210,6 +2210,7 @@ html[data-dsh-lookatstudy-active] [class*='centerCol'] > :not([data-dsh-lookatst
|
|
|
2210
2210
|
if (event === void 0) continue;
|
|
2211
2211
|
if (entry.type === "event") {
|
|
2212
2212
|
if (event.type === "user/message") {
|
|
2213
|
+
if (event.data?.source?.kind !== "user") continue;
|
|
2213
2214
|
const text = userText(event);
|
|
2214
2215
|
if (text !== "") rows.push({
|
|
2215
2216
|
key: `u${event.seq}`,
|
|
@@ -2394,7 +2395,13 @@ html[data-dsh-lookatstudy-active] [class*='centerCol'] > :not([data-dsh-lookatst
|
|
|
2394
2395
|
run = [];
|
|
2395
2396
|
};
|
|
2396
2397
|
for (const line of text.split("\n")) {
|
|
2397
|
-
const
|
|
2398
|
+
const plain = /^([A-D])[.、:)]\s*(.+)$/.exec(line.trim());
|
|
2399
|
+
const table = /^\|\s*\*{0,2}([A-D])\*{0,2}\s*\|\s*(.+?)\s*\|$/.exec(line.trim());
|
|
2400
|
+
const match = plain ?? (table !== null ? [
|
|
2401
|
+
table[0],
|
|
2402
|
+
table[1],
|
|
2403
|
+
table[2].replaceAll("**", "").replaceAll("`", "")
|
|
2404
|
+
] : null);
|
|
2398
2405
|
if (match === null) {
|
|
2399
2406
|
flush();
|
|
2400
2407
|
continue;
|
|
@@ -2475,6 +2482,17 @@ html[data-dsh-lookatstudy-active] [class*='centerCol'] > :not([data-dsh-lookatst
|
|
|
2475
2482
|
function titleMatches(title, query) {
|
|
2476
2483
|
return query.trim().toLowerCase().split(/\s+/).filter(Boolean).every((k) => title.toLowerCase().includes(k));
|
|
2477
2484
|
}
|
|
2485
|
+
/**
|
|
2486
|
+
* Whether the host session list actually knows a bound thread id. A restart
|
|
2487
|
+
* can drop young sessions from persistence; an empty byId means the list has
|
|
2488
|
+
* not hydrated yet (assume known — the send's own open surfaces a real error).
|
|
2489
|
+
* Pure over the injected list snapshot.
|
|
2490
|
+
*/
|
|
2491
|
+
function sessionKnown(ctx, id) {
|
|
2492
|
+
const byId = (ctx.sessions.list?.getSnapshot())?.byId;
|
|
2493
|
+
if (byId === void 0) return true;
|
|
2494
|
+
return Object.keys(byId).length === 0 ? true : id in byId;
|
|
2495
|
+
}
|
|
2478
2496
|
/** One rendered chat row (shared shape with the read-aloud era's renderer). */
|
|
2479
2497
|
function chatRow(row, interactive) {
|
|
2480
2498
|
if (row.role === "user") return (0, react.createElement)("div", {
|
|
@@ -2520,6 +2538,7 @@ html[data-dsh-lookatstudy-active] [class*='centerCol'] > :not([data-dsh-lookatst
|
|
|
2520
2538
|
const [sendError, setSendError] = (0, react.useState)(null);
|
|
2521
2539
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
2522
2540
|
const [rows, setRows] = (0, react.useState)([]);
|
|
2541
|
+
const [feedAttached, setFeedAttached] = (0, react.useState)(false);
|
|
2523
2542
|
const [draft, setDraft] = (0, react.useState)("");
|
|
2524
2543
|
const lesson = data?.lesson ?? null;
|
|
2525
2544
|
const localBound = (0, react.useRef)(null);
|
|
@@ -2528,18 +2547,66 @@ html[data-dsh-lookatstudy-active] [class*='centerCol'] > :not([data-dsh-lookatst
|
|
|
2528
2547
|
(0, react.useEffect)(() => {
|
|
2529
2548
|
if (boundId === null) {
|
|
2530
2549
|
setRows([]);
|
|
2550
|
+
setFeedAttached(false);
|
|
2531
2551
|
return;
|
|
2532
2552
|
}
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2553
|
+
setFeedAttached(false);
|
|
2554
|
+
let disposed = false;
|
|
2555
|
+
let offSource;
|
|
2556
|
+
let offList;
|
|
2557
|
+
const attach = () => {
|
|
2558
|
+
const source = ctx.sessions.binding(boundId)?.eventSource;
|
|
2559
|
+
if (source === void 0) return false;
|
|
2560
|
+
const update = () => {
|
|
2561
|
+
setRows(feedRows(source.getSnapshot()));
|
|
2562
|
+
};
|
|
2563
|
+
update();
|
|
2564
|
+
setFeedAttached(true);
|
|
2565
|
+
offSource = source.subscribe(update);
|
|
2566
|
+
return true;
|
|
2567
|
+
};
|
|
2568
|
+
const stage = () => {
|
|
2569
|
+
if (!sessionKnown(ctx, boundId)) return;
|
|
2570
|
+
try {
|
|
2571
|
+
const shell = PANEL_SHELL.current;
|
|
2572
|
+
if (shell !== null) shell.suppressHandBack(() => {
|
|
2573
|
+
ctx.sessions.open(boundId);
|
|
2574
|
+
});
|
|
2575
|
+
else ctx.sessions.open(boundId);
|
|
2576
|
+
} catch {}
|
|
2577
|
+
};
|
|
2578
|
+
const reconcile = () => {
|
|
2579
|
+
if (disposed || offSource !== void 0) return true;
|
|
2580
|
+
if (ctx.sessions.list?.getSnapshot().current !== boundId) {
|
|
2581
|
+
stage();
|
|
2582
|
+
return false;
|
|
2583
|
+
}
|
|
2584
|
+
return attach();
|
|
2585
|
+
};
|
|
2586
|
+
if (!reconcile()) {
|
|
2587
|
+
let tries = 0;
|
|
2588
|
+
const timer = setInterval(() => {
|
|
2589
|
+
if (disposed || offSource !== void 0 || reconcile() || ++tries >= 40) clearInterval(timer);
|
|
2590
|
+
}, 150);
|
|
2591
|
+
const list = ctx.sessions.list;
|
|
2592
|
+
if (list !== void 0) offList = list.subscribe(() => {
|
|
2593
|
+
if (disposed || reconcile()) {
|
|
2594
|
+
offList?.();
|
|
2595
|
+
offList = void 0;
|
|
2596
|
+
}
|
|
2597
|
+
});
|
|
2598
|
+
return () => {
|
|
2599
|
+
disposed = true;
|
|
2600
|
+
clearInterval(timer);
|
|
2601
|
+
offSource?.();
|
|
2602
|
+
offList?.();
|
|
2603
|
+
};
|
|
2537
2604
|
}
|
|
2538
|
-
|
|
2539
|
-
|
|
2605
|
+
return () => {
|
|
2606
|
+
disposed = true;
|
|
2607
|
+
offSource?.();
|
|
2608
|
+
offList?.();
|
|
2540
2609
|
};
|
|
2541
|
-
update();
|
|
2542
|
-
return source.subscribe(update);
|
|
2543
2610
|
}, [boundId, ctx]);
|
|
2544
2611
|
(0, react.useEffect)(() => {
|
|
2545
2612
|
const el = streamEl.current;
|
|
@@ -2553,8 +2620,9 @@ html[data-dsh-lookatstudy-active] [class*='centerCol'] > :not([data-dsh-lookatst
|
|
|
2553
2620
|
setSendError(null);
|
|
2554
2621
|
try {
|
|
2555
2622
|
if (data?.active !== true) await activate(true);
|
|
2556
|
-
let sessionId = localBound.current ?? data?.lessonSessions[lesson.lessonId];
|
|
2557
|
-
if (sessionId
|
|
2623
|
+
let sessionId = localBound.current ?? data?.lessonSessions[lesson.lessonId] ?? null;
|
|
2624
|
+
if (sessionId !== null && !sessionKnown(ctx, sessionId)) sessionId = null;
|
|
2625
|
+
if (sessionId === null) {
|
|
2558
2626
|
const area = await fetch("/lookatstudy/api/study-workspace");
|
|
2559
2627
|
if (!area.ok) throw new Error(`study area unavailable (HTTP ${String(area.status)})`);
|
|
2560
2628
|
const { path } = await area.json();
|
|
@@ -2595,6 +2663,8 @@ html[data-dsh-lookatstudy-active] [class*='centerCol'] > :not([data-dsh-lookatst
|
|
|
2595
2663
|
data,
|
|
2596
2664
|
lesson,
|
|
2597
2665
|
rows,
|
|
2666
|
+
feedAttached,
|
|
2667
|
+
bound: boundId !== null,
|
|
2598
2668
|
busy,
|
|
2599
2669
|
sendError,
|
|
2600
2670
|
draft,
|
|
@@ -2780,7 +2850,7 @@ html[data-dsh-lookatstudy-active] [class*='centerCol'] > :not([data-dsh-lookatst
|
|
|
2780
2850
|
}, tr("rail.empty.button"))), (0, react.createElement)("div", { className: "lks14-hint" }, tr("rail.empty.hint"), (0, react.createElement)("br"), tr("rail.empty.hint2")));
|
|
2781
2851
|
}
|
|
2782
2852
|
/** 中栏:the tutor chat stream with its own composer (upstream ChatStream + ChatComposer). */
|
|
2783
|
-
function ChatPane({ data, lesson, rows, busy, sendError, draft, setDraft, send, setMode }) {
|
|
2853
|
+
function ChatPane({ data, lesson, rows, feedAttached, bound, busy, sendError, draft, setDraft, send, setMode }) {
|
|
2784
2854
|
const streamEl = (0, react.useRef)(null);
|
|
2785
2855
|
(0, react.useEffect)(() => {
|
|
2786
2856
|
const el = streamEl.current;
|
|
@@ -2814,7 +2884,8 @@ html[data-dsh-lookatstudy-active] [class*='centerCol'] > :not([data-dsh-lookatst
|
|
|
2814
2884
|
}
|
|
2815
2885
|
}, tr(mode.labelKey))))), (0, react.createElement)("div", {
|
|
2816
2886
|
className: "lks14-stream",
|
|
2817
|
-
ref: streamEl
|
|
2887
|
+
ref: streamEl,
|
|
2888
|
+
"data-lks-feed": rows.length > 0 ? `rows:${String(rows.length)}` : bound ? feedAttached ? "attached-empty" : "waiting" : "no-thread"
|
|
2818
2889
|
}, rows.length === 0 ? (0, react.createElement)("div", { className: "lks14-empty" }, dormant ? tr("tutor.dormant") : tr("tutor.empty"), (0, react.createElement)("br"), tr("tutor.empty.hint")) : rows.map((row, i) => chatRow(row, !dormant && i === lastAssistant ? { send } : void 0))), (0, react.createElement)("div", { className: "lks14-starters" }, ...starters.map((s) => (0, react.createElement)("button", {
|
|
2819
2890
|
key: s.label,
|
|
2820
2891
|
className: "lks14-starter",
|