dsh-plugin-lookatstudy 0.11.1 → 0.12.0
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/README.md +0 -1
- package/lib/client.js +29 -98
- package/lib/client.js.map +1 -1
- package/lib/epub-parser-oH96guBW.mjs +306 -0
- package/lib/{html-article-C6nhqJbX.mjs → html-article-Da8ksU0i.mjs} +35 -1
- package/lib/index.mjs +56 -26
- package/lib/{pptx-parser-CD5pR2cj.mjs → pptx-parser-B9IDkiGM.mjs} +50 -2
- package/package.json +1 -1
- package/lib/epub-parser-DvlKap-d.mjs +0 -112
package/README.md
CHANGED
|
@@ -8,7 +8,6 @@ Turn any markdown document, local folder, or GitHub learning repository into a g
|
|
|
8
8
|
|---|---|
|
|
9
9
|
|  | **The 学习 tab** — course rail (progress, mastery bars, due box), the live tutor conversation, and the focus lesson's blackboard, all inside one dsh conversation view. |
|
|
10
10
|
|  | **讲解** — server-sanitized markdown rendered rich on demand: KaTeX formulas, syntax-highlighted code, mermaid diagrams (CDN loaders, silent degrade offline). |
|
|
11
|
-
|  | **🧠 脑图** — the lesson as an interactive markmap mindmap. |
|
|
12
11
|
|  | **🕸 概念图** — the lesson's knowledge components laid out as a draw.io-style concept graph (bundled ELK layout, no external service). |
|
|
13
12
|
|  | **课程栏** — sections, gating, mastery bars, ⚡ weak spots, exam stars, XP & streak. |
|
|
14
13
|
|
package/lib/client.js
CHANGED
|
@@ -195,7 +195,7 @@ window.__ModuleLoader__.load({
|
|
|
195
195
|
.lks-prose ul,.lks-prose ol{padding-left:22px;margin:6px 0}
|
|
196
196
|
.lks-prose img{max-width:100%}
|
|
197
197
|
/* width guards for CDN-rendered artifacts (both teach and diagram panes are
|
|
198
|
-
.lks-prose): mermaid/
|
|
198
|
+
.lks-prose): mermaid/elk SVGs carry fixed width attributes; KaTeX
|
|
199
199
|
display math can exceed the column; both must degrade to fit/scroll
|
|
200
200
|
internally instead of stretching the blackboard. */
|
|
201
201
|
.lks-prose svg{max-width:100%;height:auto}
|
|
@@ -651,7 +651,7 @@ window.__ModuleLoader__.load({
|
|
|
651
651
|
docker: "dockerfile"
|
|
652
652
|
};
|
|
653
653
|
let deps = {};
|
|
654
|
-
const CDN
|
|
654
|
+
const CDN = {
|
|
655
655
|
katexJs: "https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js",
|
|
656
656
|
katexCss: "https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css",
|
|
657
657
|
shiki: "https://esm.sh/shiki@1.29.2",
|
|
@@ -677,11 +677,11 @@ window.__ModuleLoader__.load({
|
|
|
677
677
|
if (!document.querySelector(`link[data-lks-katex]`)) {
|
|
678
678
|
const link = document.createElement("link");
|
|
679
679
|
link.rel = "stylesheet";
|
|
680
|
-
link.href = CDN
|
|
680
|
+
link.href = CDN.katexCss;
|
|
681
681
|
link.setAttribute("data-lks-katex", "1");
|
|
682
682
|
document.head.append(link);
|
|
683
683
|
}
|
|
684
|
-
if (window.katex === void 0) await loadScript(CDN
|
|
684
|
+
if (window.katex === void 0) await loadScript(CDN.katexJs);
|
|
685
685
|
const katex = window.katex;
|
|
686
686
|
if (katex === void 0) throw new Error("katex missing after load");
|
|
687
687
|
return katex;
|
|
@@ -692,7 +692,7 @@ window.__ModuleLoader__.load({
|
|
|
692
692
|
function defaultLoadShiki() {
|
|
693
693
|
shikiPromise ??= import(
|
|
694
694
|
/* @vite-ignore */
|
|
695
|
-
CDN
|
|
695
|
+
CDN.shiki
|
|
696
696
|
).then((mod) => {
|
|
697
697
|
const create = mod.createHighlighter;
|
|
698
698
|
if (create === void 0) throw new Error("shiki createHighlighter missing");
|
|
@@ -723,12 +723,12 @@ window.__ModuleLoader__.load({
|
|
|
723
723
|
mermaidPromise ??= (async () => {
|
|
724
724
|
const mermaid = (await import(
|
|
725
725
|
/* @vite-ignore */
|
|
726
|
-
CDN
|
|
726
|
+
CDN.mermaid
|
|
727
727
|
)).default;
|
|
728
728
|
try {
|
|
729
729
|
const elk = await import(
|
|
730
730
|
/* @vite-ignore */
|
|
731
|
-
CDN
|
|
731
|
+
CDN.mermaidElk
|
|
732
732
|
);
|
|
733
733
|
mermaid.registerLayoutLoaders(elk.default);
|
|
734
734
|
} catch {}
|
|
@@ -868,36 +868,6 @@ window.__ModuleLoader__.load({
|
|
|
868
868
|
} catch {}
|
|
869
869
|
}
|
|
870
870
|
//#endregion
|
|
871
|
-
//#region src/vendor/mindmap-markdown.ts
|
|
872
|
-
/**
|
|
873
|
-
* mindmap-markdown —— 讲解 markdown → 思维导图源文本的纯函数预处理(v0.21)。
|
|
874
|
-
*
|
|
875
|
-
* 思维导图是**结构概览**:代码块整段进脑图节点只是噪声(markmap-lib 还会为它
|
|
876
|
-
* 内嵌 hljs 高亮 HTML),图片也无处安放。喂给 markmap 前剥离:
|
|
877
|
-
* - 围栏代码块(``` 或 ~~~,含缩进围栏)→ 单行「代码块」占位(保住结构位置)
|
|
878
|
-
* - 图片  → alt 文字
|
|
879
|
-
* - HTML 注释整段剥除
|
|
880
|
-
* 其余(标题层级/列表/粗体/链接文字)原样保留 —— 层级就是脑图骨架。
|
|
881
|
-
*/
|
|
882
|
-
function mindmapMarkdown(md) {
|
|
883
|
-
const out = [];
|
|
884
|
-
let fence = null;
|
|
885
|
-
let fenceLen = 0;
|
|
886
|
-
for (const line of md.split("\n")) {
|
|
887
|
-
const open = /^(\s*)(`{3,}|~{3,})/.exec(line);
|
|
888
|
-
if (fence == null) {
|
|
889
|
-
if (open) {
|
|
890
|
-
fence = (open[2] ?? "")[0] ?? "`";
|
|
891
|
-
fenceLen = (open[2] ?? "").length;
|
|
892
|
-
out.push(`${open[1] ?? ""}- 代码块`);
|
|
893
|
-
continue;
|
|
894
|
-
}
|
|
895
|
-
out.push(line);
|
|
896
|
-
} else if (new RegExp(`^\\s*\\${fence === "~" ? "~" : "`"}{${fenceLen},}\\s*$`).test(line)) fence = null;
|
|
897
|
-
}
|
|
898
|
-
return out.join("\n").replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/<!--[\s\S]*?-->/g, "");
|
|
899
|
-
}
|
|
900
|
-
//#endregion
|
|
901
871
|
//#region src/vendor/cmap-elk-layout.ts
|
|
902
872
|
/** 字符宽度估算:CJK 全宽,ASCII 约 0.58em,其余(BBOP…)同 ASCII。 */
|
|
903
873
|
function estTextWidth(text, fontSize = 13) {
|
|
@@ -1263,52 +1233,17 @@ window.__ModuleLoader__.load({
|
|
|
1263
1233
|
//#endregion
|
|
1264
1234
|
//#region src/client/diagrams.ts
|
|
1265
1235
|
/**
|
|
1266
|
-
* Blackboard diagram views (SPEC Phase 2): the lesson
|
|
1267
|
-
*
|
|
1268
|
-
*
|
|
1269
|
-
*
|
|
1270
|
-
*
|
|
1236
|
+
* Blackboard diagram views (SPEC Phase 2): the lesson concept map (ELK layered
|
|
1237
|
+
* layout via the vendored cmap-elk-layout + CDN elkjs, draw.io-flavored SVG
|
|
1238
|
+
* skin rendered plugin-side). Degrades honestly: any CDN/ELK failure leaves a
|
|
1239
|
+
* one-line notice instead of a broken pane.
|
|
1240
|
+
*
|
|
1241
|
+
* The lesson mind map view was RETIRED with upstream v0.26.0 (2026-09-01
|
|
1242
|
+
* port): body text that has headings already shows its structure, text without
|
|
1243
|
+
* headings yields a first-line-truncated node graph nobody can read — both
|
|
1244
|
+
* lose — and this concept map already covers the semantic-structure need at
|
|
1245
|
+
* higher quality. Its three CDN imports went with it.
|
|
1271
1246
|
*/
|
|
1272
|
-
const CDN = {
|
|
1273
|
-
markmapLib: "https://esm.sh/markmap-lib@0.18.12",
|
|
1274
|
-
markmapView: "https://esm.sh/markmap-view@0.18.10"
|
|
1275
|
-
};
|
|
1276
|
-
let markmapPromise = null;
|
|
1277
|
-
async function loadMarkmap() {
|
|
1278
|
-
markmapPromise ??= (async () => {
|
|
1279
|
-
const lib = await import(
|
|
1280
|
-
/* @vite-ignore */
|
|
1281
|
-
CDN.markmapLib
|
|
1282
|
-
);
|
|
1283
|
-
const view = await import(
|
|
1284
|
-
/* @vite-ignore */
|
|
1285
|
-
CDN.markmapView
|
|
1286
|
-
);
|
|
1287
|
-
const Transformer = lib.Transformer ?? {};
|
|
1288
|
-
const d3 = await import(
|
|
1289
|
-
/* @vite-ignore */
|
|
1290
|
-
"https://esm.sh/d3@7"
|
|
1291
|
-
);
|
|
1292
|
-
return {
|
|
1293
|
-
transformer: new Transformer(),
|
|
1294
|
-
Markmap: view.Markmap,
|
|
1295
|
-
d3
|
|
1296
|
-
};
|
|
1297
|
-
})();
|
|
1298
|
-
return markmapPromise;
|
|
1299
|
-
}
|
|
1300
|
-
/** Render the lesson markdown as a mind map into an empty container. */
|
|
1301
|
-
async function renderMindmap(container, markdown) {
|
|
1302
|
-
const { transformer, Markmap, d3 } = await loadMarkmap();
|
|
1303
|
-
const { root } = transformer.transform(mindmapMarkdown(markdown));
|
|
1304
|
-
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
1305
|
-
svg.setAttribute("style", "width:100%;min-height:420px");
|
|
1306
|
-
container.append(svg);
|
|
1307
|
-
window["markmap"] = { d3 };
|
|
1308
|
-
const mm = Markmap.create(svg, { autoFit: true });
|
|
1309
|
-
mm.setData(root);
|
|
1310
|
-
mm.fit();
|
|
1311
|
-
}
|
|
1312
1247
|
/** Lesson concept map: the lesson node + its concepts (weak ones accented),
|
|
1313
1248
|
* laid out by the vendored ELK pipeline, skinned draw.io-style (plugin-original,
|
|
1314
1249
|
* upstream's skin is coupled to its token pipeline). Mastery shades the nodes. */
|
|
@@ -1378,9 +1313,7 @@ window.__ModuleLoader__.load({
|
|
|
1378
1313
|
"pane.tutor": "导师",
|
|
1379
1314
|
"pane.bb": "黑板",
|
|
1380
1315
|
"viewtab.teach": "讲解",
|
|
1381
|
-
"viewtab.mind": "🧠 脑图",
|
|
1382
1316
|
"viewtab.cmap": "🕸 概念图",
|
|
1383
|
-
"viewtab.mind.title": "本课结构一图流(markmap)",
|
|
1384
1317
|
"viewtab.cmap.title": "本课概念关系(ELK 布局)",
|
|
1385
1318
|
"loading": "加载中…",
|
|
1386
1319
|
"start.title": "一键准备学习:建立学习工作区、开启会话并让导师就位",
|
|
@@ -1447,7 +1380,6 @@ window.__ModuleLoader__.load({
|
|
|
1447
1380
|
"bb.empty": "黑板还空着",
|
|
1448
1381
|
"bb.empty.hint": "在左侧课程树选择一课",
|
|
1449
1382
|
"bb.mastery": "掌握度 {pct}%",
|
|
1450
|
-
"bb.fallback.mind": "脑图渲染不可用(网络受限) — 已回退讲解视图",
|
|
1451
1383
|
"bb.fallback.cmap.empty": "概念图需要先由导师定义本课概念",
|
|
1452
1384
|
"bb.fallback.cmap": "概念图渲染不可用(布局引擎加载失败) — 已回退讲解视图",
|
|
1453
1385
|
"bb.notes": "笔记",
|
|
@@ -1471,6 +1403,8 @@ window.__ModuleLoader__.load({
|
|
|
1471
1403
|
"settings.stats.xp": "XP {xp} · Lv{level}({pct}%)",
|
|
1472
1404
|
"settings.stats.today": "今日 {xp}/{goal}",
|
|
1473
1405
|
"settings.stats.streak": "连续 {days} 天(最长 {best},剩冻结 {freeze})",
|
|
1406
|
+
"settings.about": "关于",
|
|
1407
|
+
"settings.about.hint": "当前运行的构建版本,点击查看该版更新内容。",
|
|
1474
1408
|
"settings.stateFile": "状态文件",
|
|
1475
1409
|
"settings.stateFile.hint": "学习进度存放于本机 JSON,换机可迁移",
|
|
1476
1410
|
"dock.title": "学习状态:待复习 {due} · 连续 {streak} 天 · Lv{level} — 点上方「学习」页签进入",
|
|
@@ -1494,9 +1428,7 @@ window.__ModuleLoader__.load({
|
|
|
1494
1428
|
"pane.tutor": "Tutor",
|
|
1495
1429
|
"pane.bb": "Board",
|
|
1496
1430
|
"viewtab.teach": "Teach",
|
|
1497
|
-
"viewtab.mind": "🧠 Mind map",
|
|
1498
1431
|
"viewtab.cmap": "🕸 Concepts",
|
|
1499
|
-
"viewtab.mind.title": "The lesson as one markmap graph",
|
|
1500
1432
|
"viewtab.cmap.title": "The lesson's concept relations (ELK layout)",
|
|
1501
1433
|
"loading": "Loading…",
|
|
1502
1434
|
"start.title": "One click to study: workspace + session + tutor, all set up",
|
|
@@ -1563,7 +1495,6 @@ window.__ModuleLoader__.load({
|
|
|
1563
1495
|
"bb.empty": "The blackboard is empty",
|
|
1564
1496
|
"bb.empty.hint": "Pick a lesson in the course tree on the left",
|
|
1565
1497
|
"bb.mastery": "mastery {pct}%",
|
|
1566
|
-
"bb.fallback.mind": "Mind map unavailable (network-restricted) — fell back to the teach view",
|
|
1567
1498
|
"bb.fallback.cmap.empty": "The concept map needs the tutor to define this lesson's concepts first",
|
|
1568
1499
|
"bb.fallback.cmap": "Concept map unavailable (layout engine failed to load) — fell back to the teach view",
|
|
1569
1500
|
"bb.notes": "Notes",
|
|
@@ -1587,6 +1518,8 @@ window.__ModuleLoader__.load({
|
|
|
1587
1518
|
"settings.stats.xp": "XP {xp} · Lv{level} ({pct}%)",
|
|
1588
1519
|
"settings.stats.today": "Today {xp}/{goal}",
|
|
1589
1520
|
"settings.stats.streak": "Streak {days}d (best {best}, {freeze} freeze left)",
|
|
1521
|
+
"settings.about": "About",
|
|
1522
|
+
"settings.about.hint": "The version of the running build — click to see its release notes.",
|
|
1590
1523
|
"settings.stateFile": "State file",
|
|
1591
1524
|
"settings.stateFile.hint": "Your progress lives in a local JSON file — copy it to migrate machines",
|
|
1592
1525
|
"dock.title": "Study status: {due} due · {streak}d streak · Lv{level} — open the Study tab above to continue",
|
|
@@ -2376,15 +2309,15 @@ window.__ModuleLoader__.load({
|
|
|
2376
2309
|
enhanceRendered(el).catch(() => {});
|
|
2377
2310
|
}, [pane, lesson?.html]);
|
|
2378
2311
|
(0, react.useEffect)(() => {
|
|
2379
|
-
if (pane !== "
|
|
2312
|
+
if (pane !== "cmap" || diagRef.current === null || lesson === null) return;
|
|
2380
2313
|
const el = diagRef.current;
|
|
2381
2314
|
el.textContent = "";
|
|
2382
|
-
(
|
|
2315
|
+
(lesson.concepts.length === 0 ? Promise.reject(/* @__PURE__ */ new Error("no concepts")) : renderLessonConceptMap(el, lesson.title, lesson.concepts.map((c) => ({
|
|
2383
2316
|
title: c.title,
|
|
2384
2317
|
masteryPct: c.masteryPct
|
|
2385
2318
|
})))).catch((err) => {
|
|
2386
2319
|
console.error("lks diagram pane failed:", pane, err);
|
|
2387
|
-
el.textContent =
|
|
2320
|
+
el.textContent = lesson.concepts.length === 0 ? tr("bb.fallback.cmap.empty") : tr("bb.fallback.cmap");
|
|
2388
2321
|
});
|
|
2389
2322
|
}, [
|
|
2390
2323
|
pane,
|
|
@@ -2400,12 +2333,6 @@ window.__ModuleLoader__.load({
|
|
|
2400
2333
|
setPane("teach");
|
|
2401
2334
|
}
|
|
2402
2335
|
}, tr("viewtab.teach")), (0, react.createElement)("button", {
|
|
2403
|
-
className: `lks-viewtab${pane === "mind" ? " on" : ""}`,
|
|
2404
|
-
title: tr("viewtab.mind.title"),
|
|
2405
|
-
onClick: () => {
|
|
2406
|
-
setPane("mind");
|
|
2407
|
-
}
|
|
2408
|
-
}, tr("viewtab.mind")), (0, react.createElement)("button", {
|
|
2409
2336
|
className: `lks-viewtab${pane === "cmap" ? " on" : ""}`,
|
|
2410
2337
|
title: tr("viewtab.cmap.title"),
|
|
2411
2338
|
onClick: () => {
|
|
@@ -2572,7 +2499,11 @@ window.__ModuleLoader__.load({
|
|
|
2572
2499
|
days: progress.streak,
|
|
2573
2500
|
best: progress.longestStreak,
|
|
2574
2501
|
freeze: progress.freezeCount
|
|
2575
|
-
})))), (0, react.createElement)("section", { className: "lks-set-row" }, (0, react.createElement)("h3", null, tr("settings.
|
|
2502
|
+
})))), (0, react.createElement)("section", { className: "lks-set-row" }, (0, react.createElement)("h3", null, tr("settings.about")), (0, react.createElement)("p", { className: "lks-set-hint" }, tr("settings.about.hint")), data?.version ? (0, react.createElement)("a", {
|
|
2503
|
+
href: `https://github.com/Kaiji-Z/dsh-plugin-lookatstudy/releases/tag/v${data.version}`,
|
|
2504
|
+
target: "_blank",
|
|
2505
|
+
rel: "noreferrer"
|
|
2506
|
+
}, `v${data.version}`) : null), (0, react.createElement)("section", { className: "lks-set-row" }, (0, react.createElement)("h3", null, tr("settings.stateFile")), (0, react.createElement)("p", { className: "lks-set-hint" }, tr("settings.stateFile.hint")), data === null ? null : (0, react.createElement)("code", { className: "lks-set-path" }, data.statePath)), (0, react.createElement)(ActionError, { error }));
|
|
2576
2507
|
}
|
|
2577
2508
|
//#endregion
|
|
2578
2509
|
//#region src/client/dock.tsx
|