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 CHANGED
@@ -8,7 +8,6 @@ Turn any markdown document, local folder, or GitHub learning repository into a g
8
8
  |---|---|
9
9
  | ![Three-column study tab: course rail, tutor, blackboard](docs/media/overview.png) | **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
  | ![Lesson page with rendered math, code and diagrams](docs/media/blackboard-lesson.png) | **讲解** — server-sanitized markdown rendered rich on demand: KaTeX formulas, syntax-highlighted code, mermaid diagrams (CDN loaders, silent degrade offline). |
11
- | ![Mindmap view of the lesson](docs/media/mindmap.png) | **🧠 脑图** — the lesson as an interactive markmap mindmap. |
12
11
  | ![Concept map view](docs/media/concept-map.png) | **🕸 概念图** — the lesson's knowledge components laid out as a draw.io-style concept graph (bundled ELK layout, no external service). |
13
12
  | ![Course rail with lesson tree](docs/media/course-rail.png) | **课程栏** — 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/markmap/elk SVGs carry fixed width attributes; KaTeX
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$1 = {
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$1.katexCss;
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$1.katexJs);
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$1.shiki
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$1.mermaid
726
+ CDN.mermaid
727
727
  )).default;
728
728
  try {
729
729
  const elk = await import(
730
730
  /* @vite-ignore */
731
- CDN$1.mermaidElk
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](url) → 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 mind map (markmap via
1267
- * CDN, markdown preprocessed by the vendored mindmap-markdown) and the lesson
1268
- * concept map (ELK layered layout via the vendored cmap-elk-layout + CDN elkjs,
1269
- * draw.io-flavored SVG skin rendered plugin-side). Both degrade honestly: any
1270
- * CDN/ELK failure leaves a one-line notice instead of a broken pane.
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 !== "mind" && pane !== "cmap" || diagRef.current === null || lesson === null) return;
2312
+ if (pane !== "cmap" || diagRef.current === null || lesson === null) return;
2380
2313
  const el = diagRef.current;
2381
2314
  el.textContent = "";
2382
- (pane === "mind" ? renderMindmap(el, lesson.markdown) : lesson.concepts.length === 0 ? Promise.reject(/* @__PURE__ */ new Error("no concepts")) : renderLessonConceptMap(el, lesson.title, lesson.concepts.map((c) => ({
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 = pane === "mind" ? tr("bb.fallback.mind") : lesson.concepts.length === 0 ? tr("bb.fallback.cmap.empty") : tr("bb.fallback.cmap");
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.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 }));
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