dsh-plugin-lookatstudy 0.24.0 → 0.25.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 +85 -20
- package/lib/client.js.map +1 -1
- package/lib/index.mjs +68 -26
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -362,8 +362,8 @@ window.__ModuleLoader__.load({
|
|
|
362
362
|
.lks-ui .lks14-railbody{position:absolute;inset:0;overflow:hidden;z-index:10}
|
|
363
363
|
.lks-ui .lks14-railtrack{display:flex;height:100%;width:200%;transition:transform .3s var(--ease-out-expo)}
|
|
364
364
|
.lks-ui .lks14-railpane{width:50%;height:100%;position:relative}
|
|
365
|
-
.lks-ui .lks14-railscroll{height:100%;overflow-y:auto;overflow-x:hidden;padding:112px 8px 16px}
|
|
366
|
-
.lks-ui .lks14-railpane-import{height:100%;overflow-y:auto;padding:64px 12px 12px}
|
|
365
|
+
.lks-ui .lks14-railscroll{height:100%;overflow-y:auto;overflow-x:hidden;padding:112px 8px 16px;box-sizing:border-box}
|
|
366
|
+
.lks-ui .lks14-railpane-import{height:100%;overflow-y:auto;padding:64px 12px 12px;box-sizing:border-box}
|
|
367
367
|
.lks-ui .lks14-raillist{display:flex;flex-direction:column;gap:8px;margin-bottom:10px}
|
|
368
368
|
.lks-ui .lks14-railcourse{text-align:left;padding:10px 12px;border-radius:12px;background:rgb(255 255 255/0.05);display:flex;flex-direction:column;gap:2px}
|
|
369
369
|
.lks-ui .lks14-railcourse:hover{background:rgb(255 255 255/0.1)}
|
|
@@ -2258,6 +2258,7 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
2258
2258
|
"toast.close": "关闭",
|
|
2259
2259
|
"composer.busy": "导师正在回复…",
|
|
2260
2260
|
"rail.empty.demo": "导入示例课程",
|
|
2261
|
+
"import.session.title": "课程导入",
|
|
2261
2262
|
"import.cta": "导入新课程",
|
|
2262
2263
|
"import.tab.url": "URL",
|
|
2263
2264
|
"import.tab.md": "MD",
|
|
@@ -2360,7 +2361,6 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
2360
2361
|
"chat.reasoning": "思考过程({n} 字)",
|
|
2361
2362
|
"chat.think": "思考",
|
|
2362
2363
|
"langview.original": "原文",
|
|
2363
|
-
"langview.bilingual": "对照",
|
|
2364
2364
|
"langview.translation": "译文",
|
|
2365
2365
|
"msg.speak": "朗读",
|
|
2366
2366
|
"note.pin": "置顶笔记",
|
|
@@ -2629,6 +2629,7 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
2629
2629
|
"toast.close": "Close",
|
|
2630
2630
|
"composer.busy": "The tutor is replying…",
|
|
2631
2631
|
"rail.empty.demo": "Import the demo course",
|
|
2632
|
+
"import.session.title": "Course import",
|
|
2632
2633
|
"import.cta": "Import a new course",
|
|
2633
2634
|
"import.tab.url": "URL",
|
|
2634
2635
|
"import.tab.md": "MD",
|
|
@@ -2731,7 +2732,6 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
2731
2732
|
"chat.reasoning": "Thinking ({n} chars)",
|
|
2732
2733
|
"chat.think": "Think",
|
|
2733
2734
|
"langview.original": "Original",
|
|
2734
|
-
"langview.bilingual": "Both",
|
|
2735
2735
|
"langview.translation": "Translation",
|
|
2736
2736
|
"msg.speak": "Speak",
|
|
2737
2737
|
"note.pin": "Pin note",
|
|
@@ -3747,6 +3747,17 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
3747
3747
|
})()).replaceAll("**", "");
|
|
3748
3748
|
}
|
|
3749
3749
|
/**
|
|
3750
|
+
* The dedicated import session's host-list title (0.24.1): imports mint a
|
|
3751
|
+
* course-less one-shot session that never rides a lesson thread — a uniform
|
|
3752
|
+
* label keeps the host session list legible instead of auto-title noise.
|
|
3753
|
+
* Label slugified to one line, whole title capped. Pure.
|
|
3754
|
+
*/
|
|
3755
|
+
function importSessionTitle(label, now, t = tr) {
|
|
3756
|
+
const flat = label.replace(/[\u0000-\u001f\u007f]+/g, " ").replace(/\s+/g, " ").trim().slice(0, 24);
|
|
3757
|
+
const date = `${String(now.getFullYear()).slice(2)}.${String(now.getMonth() + 1).padStart(2, "0")}.${String(now.getDate()).padStart(2, "0")}`;
|
|
3758
|
+
return `${flat === "" ? t("import.session.title") : flat} · ${date}`;
|
|
3759
|
+
}
|
|
3760
|
+
/**
|
|
3750
3761
|
* Tooltip text for one course-tree status glyph. Pure (the translator is an
|
|
3751
3762
|
* optional parameter defaulting to the active one, evaluated per call).
|
|
3752
3763
|
*/
|
|
@@ -7719,11 +7730,19 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
7719
7730
|
const { data, activate, setMode, setFocus, searchLessons, deleteCourse, deleteNote, bindLessonSession, lessonThreadOp, setCourseScope, uploadAttachment, setInterfaceLang } = useStudy();
|
|
7720
7731
|
(0, react.useEffect)(() => {
|
|
7721
7732
|
const read = () => {
|
|
7722
|
-
|
|
7733
|
+
let lang = document.documentElement.lang;
|
|
7734
|
+
if (!/^[a-z]{2,3}(-[A-Za-z0-9]+)*$/.test(lang)) lang = navigator.language;
|
|
7723
7735
|
if (/^[a-z]{2,3}(-[A-Za-z0-9]+)*$/.test(lang)) setInterfaceLang(lang).catch(() => {});
|
|
7724
7736
|
};
|
|
7725
7737
|
read();
|
|
7726
|
-
|
|
7738
|
+
const t1 = setTimeout(read, 600);
|
|
7739
|
+
const t2 = setTimeout(read, 2400);
|
|
7740
|
+
const off = ctx.locale?.subscribe(read);
|
|
7741
|
+
return () => {
|
|
7742
|
+
clearTimeout(t1);
|
|
7743
|
+
clearTimeout(t2);
|
|
7744
|
+
off?.();
|
|
7745
|
+
};
|
|
7727
7746
|
}, []);
|
|
7728
7747
|
(0, react.useEffect)(() => {
|
|
7729
7748
|
wireCodeBlockCopy();
|
|
@@ -7925,7 +7944,43 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
7925
7944
|
} else celebrate(ev.kind);
|
|
7926
7945
|
prevCelebrationRef.current = snapshot;
|
|
7927
7946
|
}, [data]);
|
|
7928
|
-
const
|
|
7947
|
+
const [importSessionId, setImportSessionId] = (0, react.useState)(null);
|
|
7948
|
+
const sendImport = async (prompt, label) => {
|
|
7949
|
+
if (data?.active !== true) await activate(true);
|
|
7950
|
+
let sessionId = importSessionId;
|
|
7951
|
+
if (sessionId === null || !sessionKnown(ctx, sessionId)) {
|
|
7952
|
+
const area = await fetch("/lookatstudy/api/study-workspace");
|
|
7953
|
+
if (!area.ok) throw new Error(`study area unavailable (HTTP ${String(area.status)})`);
|
|
7954
|
+
const { path } = await area.json();
|
|
7955
|
+
const workspace = await ctx.workspaces.create({ path });
|
|
7956
|
+
sessionId = await ctx.sessions.create({ workspaceId: workspace.workspaceId });
|
|
7957
|
+
setImportSessionId(sessionId);
|
|
7958
|
+
}
|
|
7959
|
+
const shell = PANEL_SHELL.current;
|
|
7960
|
+
if (shell !== null) shell.suppressHandBack(() => {
|
|
7961
|
+
ctx.sessions.open(sessionId);
|
|
7962
|
+
});
|
|
7963
|
+
else ctx.sessions.open(sessionId);
|
|
7964
|
+
const actx = ctx.sessions.scope(sessionId);
|
|
7965
|
+
const face = actx === void 0 ? void 0 : ctx.sessions.sessionOf(actx);
|
|
7966
|
+
if (face === void 0) throw new Error("import session is not addressable yet");
|
|
7967
|
+
activeFace.current = face;
|
|
7968
|
+
try {
|
|
7969
|
+
await face.rename?.(importSessionTitle(label, /* @__PURE__ */ new Date()));
|
|
7970
|
+
} catch {}
|
|
7971
|
+
const result = await face.prompt([{
|
|
7972
|
+
type: "text",
|
|
7973
|
+
text: prompt
|
|
7974
|
+
}], "queue");
|
|
7975
|
+
if (!result.ok) throw new Error(`prompt rejected: ${result.error.code}: ${result.error.message}`);
|
|
7976
|
+
};
|
|
7977
|
+
/** Forget the import session — called from every funnel exit. Unpinning
|
|
7978
|
+
* re-runs the binding effect, which restages the focused lesson's thread. */
|
|
7979
|
+
const endImportSession = (0, react.useCallback)(() => {
|
|
7980
|
+
setImportSessionId(null);
|
|
7981
|
+
activeFace.current = null;
|
|
7982
|
+
}, []);
|
|
7983
|
+
const boundId = importSessionId ?? boundSessionOf(lesson !== null ? lesson.lessonId : null) ?? threadGroupOf(lesson !== null ? lesson.lessonId : null)?.active ?? (lesson !== null ? data?.lessonSessions[lesson.lessonId] ?? null : null);
|
|
7929
7984
|
const nudged = (0, react.useRef)(false);
|
|
7930
7985
|
(0, react.useEffect)(() => {
|
|
7931
7986
|
if (nudged.current || (data?.dueCount ?? 0) === 0) return;
|
|
@@ -8167,6 +8222,7 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
8167
8222
|
}, [send]);
|
|
8168
8223
|
const switchLessonThread = (sessionId) => {
|
|
8169
8224
|
if (lesson === null) return;
|
|
8225
|
+
if (importSessionId !== null) return;
|
|
8170
8226
|
if (localBound.current?.lessonId === lesson.lessonId && localBound.current.sessionId === sessionId) return;
|
|
8171
8227
|
localBound.current = {
|
|
8172
8228
|
lessonId: lesson.lessonId,
|
|
@@ -8181,6 +8237,7 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
8181
8237
|
};
|
|
8182
8238
|
const startNewThread = () => {
|
|
8183
8239
|
if (lesson === null) return;
|
|
8240
|
+
if (importSessionId !== null) return;
|
|
8184
8241
|
localBound.current = null;
|
|
8185
8242
|
bindLessonSession(lesson.lessonId, null);
|
|
8186
8243
|
setRows([]);
|
|
@@ -8243,6 +8300,8 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
8243
8300
|
turnActive: feedGen,
|
|
8244
8301
|
importProgress,
|
|
8245
8302
|
stop,
|
|
8303
|
+
sendImport,
|
|
8304
|
+
endImportSession,
|
|
8246
8305
|
paletteBus
|
|
8247
8306
|
}), (0, react.createElement)(PaneResizeHandle, {
|
|
8248
8307
|
side: "rail",
|
|
@@ -8654,7 +8713,7 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
8654
8713
|
PANEL_SHELL.current = shell;
|
|
8655
8714
|
}
|
|
8656
8715
|
/** 左栏:course picker, tree, review box, import. */
|
|
8657
|
-
function CourseRail({ data, activate, setFocus, searchLessons, deleteCourse, send, onJumped, streamingLessonId, turnActive, importProgress, stop, paletteBus }) {
|
|
8716
|
+
function CourseRail({ data, activate, setFocus, searchLessons, deleteCourse, send, onJumped, streamingLessonId, turnActive, importProgress, stop, paletteBus, sendImport, endImportSession }) {
|
|
8658
8717
|
const [selectedCourse, setSelectedCourse] = (0, react.useState)("");
|
|
8659
8718
|
const [query, setQuery] = (0, react.useState)("");
|
|
8660
8719
|
const [searchRows, setSearchRows] = (0, react.useState)([]);
|
|
@@ -8694,13 +8753,21 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
8694
8753
|
startedAt: Date.now(),
|
|
8695
8754
|
baselineIds: new Set((data?.courses ?? []).map((c) => c.courseId))
|
|
8696
8755
|
});
|
|
8697
|
-
|
|
8756
|
+
sendImport(prompt, label).catch((err) => {
|
|
8757
|
+
setImportJob(null);
|
|
8758
|
+
endImportSession();
|
|
8759
|
+
setImportResult({
|
|
8760
|
+
ok: false,
|
|
8761
|
+
msg: err instanceof Error ? err.message : String(err)
|
|
8762
|
+
});
|
|
8763
|
+
});
|
|
8698
8764
|
};
|
|
8699
8765
|
(0, react.useEffect)(() => {
|
|
8700
8766
|
if (importJob === null || data === null) return;
|
|
8701
8767
|
const fresh = data.courses.find((c) => !importJob.baselineIds.has(c.courseId));
|
|
8702
8768
|
if (fresh !== void 0) {
|
|
8703
8769
|
setImportJob(null);
|
|
8770
|
+
endImportSession();
|
|
8704
8771
|
setImportResult({
|
|
8705
8772
|
ok: true,
|
|
8706
8773
|
msg: tr("import.success")
|
|
@@ -8711,6 +8778,7 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
8711
8778
|
}
|
|
8712
8779
|
if (importJobDead(importJob, turnActive, Date.now())) {
|
|
8713
8780
|
setImportJob(null);
|
|
8781
|
+
endImportSession();
|
|
8714
8782
|
setImportResult({
|
|
8715
8783
|
ok: false,
|
|
8716
8784
|
msg: tr("import.error.turn")
|
|
@@ -8908,7 +8976,7 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
8908
8976
|
}))))) : null, data.courses.length === 0 ? (0, react.createElement)("div", { className: "lks14-empty" }, tr("rail.empty.title")) : null, (0, react.createElement)("button", {
|
|
8909
8977
|
className: "lks-btn ghost lks14-raildemo",
|
|
8910
8978
|
onClick: () => {
|
|
8911
|
-
|
|
8979
|
+
startImport(tr("import.tab.url"), tr("prompt.import", { url: "https://github.com/microsoft/AI-For-Beginners" }));
|
|
8912
8980
|
}
|
|
8913
8981
|
}, (0, react.createElement)(IconDownloadOutline16, null), tr("rail.empty.demo")), (0, react.createElement)(ImportPanel, {
|
|
8914
8982
|
job: importJob,
|
|
@@ -8919,6 +8987,7 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
8919
8987
|
onCancel: () => {
|
|
8920
8988
|
stop();
|
|
8921
8989
|
setImportJob(null);
|
|
8990
|
+
endImportSession();
|
|
8922
8991
|
setImportResult({
|
|
8923
8992
|
ok: false,
|
|
8924
8993
|
msg: tr("import.cancelled")
|
|
@@ -9946,17 +10015,17 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
9946
10015
|
const [read, setRead] = (0, react.useState)(null);
|
|
9947
10016
|
const readCtl = (0, react.useRef)(null);
|
|
9948
10017
|
const [readError, setReadError] = (0, react.useState)(null);
|
|
9949
|
-
const [langView, setLangView] = (0, react.useState)("
|
|
10018
|
+
const [langView, setLangView] = (0, react.useState)("original");
|
|
9950
10019
|
(0, react.useEffect)(() => {
|
|
9951
10020
|
if (lesson === null) return;
|
|
9952
|
-
let next = "
|
|
10021
|
+
let next = "original";
|
|
9953
10022
|
try {
|
|
9954
10023
|
const stored = localStorage.getItem(`lks-langview:${lesson.lessonId}`);
|
|
9955
|
-
if (stored === "original" || stored === "
|
|
10024
|
+
if (stored === "original" || stored === "translation") next = stored;
|
|
9956
10025
|
} catch {}
|
|
9957
10026
|
setLangView(next);
|
|
9958
10027
|
}, [lesson?.lessonId]);
|
|
9959
|
-
const teachHtml = lesson === null ? "" : langView === "translation" && lesson.translationHtml !== void 0 ? lesson.translationHtml :
|
|
10028
|
+
const teachHtml = lesson === null ? "" : langView === "translation" && lesson.translationHtml !== void 0 ? lesson.translationHtml : lesson.html;
|
|
9960
10029
|
const { tts, addUserNote, recordReview, editNote, pinNote } = useStudy();
|
|
9961
10030
|
const proseRef = (0, react.useRef)(null);
|
|
9962
10031
|
const diagRef = (0, react.useRef)(null);
|
|
@@ -10221,14 +10290,10 @@ mark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border
|
|
|
10221
10290
|
setRateBusy(false);
|
|
10222
10291
|
});
|
|
10223
10292
|
}
|
|
10224
|
-
}, tr(key))))) : null, (0, react.createElement)("div", { className: "lks-readbar" }, lesson.
|
|
10293
|
+
}, tr(key))))) : null, (0, react.createElement)("div", { className: "lks-readbar" }, lesson.translationHtml !== void 0 ? (0, react.createElement)("div", {
|
|
10225
10294
|
className: "lks-langview",
|
|
10226
10295
|
"data-testid": "langview"
|
|
10227
|
-
}, [
|
|
10228
|
-
"original",
|
|
10229
|
-
"bilingual",
|
|
10230
|
-
"translation"
|
|
10231
|
-
].map((v) => (0, react.createElement)("button", {
|
|
10296
|
+
}, ["original", "translation"].map((v) => (0, react.createElement)("button", {
|
|
10232
10297
|
key: v,
|
|
10233
10298
|
className: langView === v ? "on" : "",
|
|
10234
10299
|
"data-testid": `langview-${v}`,
|