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.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["createElement","useSyncExternalStore","createElement","MODES","createElement","useState","useRef","createRoot","createElement","useState","createElement","createElement","createElement"],"sources":["../src/client/styles.ts","../src/client/icons.tsx","../src/client/data.ts","../src/markdown.ts","../src/vendor/mermaid-elk-rewrite.ts","../src/client/enhance.ts","../src/vendor/cmap-elk-layout.ts","../src/client/locale.ts","../src/client/diagrams.ts","../src/vendor/speech-text.ts","../src/vendor/math-speech.ts","../src/client/session-feed.ts","../src/client/readaloud.ts","../src/client/views.tsx","../src/client/panel.tsx","../src/client/shell-entry.ts","../src/client/settings.tsx","../src/client/dock.tsx","../src/client/toolviews.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * Style injection for the study surfaces: one `<style>` element carrying the\n * `.lks14-*` panel classes (the sidebar-entry center-takeover, arranged like\n * upstream LookatStudy) plus the shared `.lks-*` classes the host-native\n * surfaces still use (toolview cards, dock pill, settings section, note\n * cards, read-aloud bar). Authored against the dsh `--dsw-*` design tokens so\n * everything follows the app's theme instead of carrying its own palette.\n * CSS Modules are unavailable to patch-layer bundles, so the stylesheet is\n * injected once at client-plugin apply; the `data-` attribute keeps the\n * injection idempotent.\n * @module dsh-plugin-lookatstudy/client/styles\n */\n\n/** Marker attribute proving the stylesheet is already in the document. */\nconst STYLE_ID = 'data-dsh-plugin-lookatstudy'\n\n/**\n * The study stylesheet. The center-takeover visibility follows the stardeck\n * panel doctrine: the shell view is display:none until the `<html>` active\n * attribute flips, and the host conversation column's own children are hidden\n * behind `!important` so React never fights the takeover.\n */\nexport const STUDY_CSS = `\n/* State inks: small colored text must clear WCAG AA (4.5:1), but the host's\n raw state tokens (success/warn primaries) sit at 2.3–2.6:1 on light\n backgrounds, and success/error *label* aliases are not published by this\n host at all (the tv-chip used to reference them and silently inherit).\n Mixing the state color toward label-primary keeps the hue while pulling\n luminance to the readable pole — and works in BOTH themes, because\n label-primary is always the current theme's high-contrast ink. Declared on\n every surface family that consumes the inks: .lks-root (dock pill,\n settings) and .lks-tv (toolview cards, which render in the host\n conversation with no .lks-root ancestor). */\n.lks-root,.lks-tv{--lks-warn-ink:color-mix(in srgb,var(--dsw-alias-state-warn-label) 45%,var(--dsw-alias-label-primary));--lks-ok-ink:color-mix(in srgb,var(--dsw-alias-state-success-primary) 40%,var(--dsw-alias-label-primary));--lks-err-ink:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,var(--dsw-alias-label-primary))}\n.lks-root{font-family:var(--dsw-font-family)}\n.lks-root :where(button){font-family:var(--dsw-font-family);cursor:pointer;border:none;background:none;padding:0}\n\n/* ── sidebar entry row (DOM-injected next to the session family) ── */\n.lks14-sidebar-row{display:flex;align-items:center;gap:9px;width:100%;padding:7px 10px;border:0;background:transparent;color:var(--dsw-alias-label-secondary);font-size:13px;font-family:var(--dsw-font-family);cursor:pointer;border-radius:8px;text-align:left}\n.lks14-sidebar-row:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.lks14-sidebar-row[data-active='true']{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-weight:600}\n.lks14-sidebar-row:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-sidebar-icon{display:inline-flex;align-items:center;flex:0 0 auto;color:currentColor}\n.lks14-sidebar-label{white-space:nowrap}\n/* Host sidebar collapsed to the icon rail: hide the label, center the icon. */\n[class*='_collapsed'] .lks14-sidebar-row{justify-content:center;padding:7px 0;gap:0}\n[class*='_collapsed'] .lks14-sidebar-label{display:none}\n\n/* ── center-column takeover (the study panel) ── */\n.lks14-shell-view{display:none}\nhtml[data-dsh-lookatstudy-active] .lks14-shell-view{display:flex;flex-direction:column;height:100%;min-height:0}\nhtml[data-dsh-lookatstudy-active] [data-pane='conversation'] > :not([data-dsh-lookatstudy-view]),\nhtml[data-dsh-lookatstudy-active] [class*='centerCol'] > :not([data-dsh-lookatstudy-view]){display:none !important}\n[data-dsh-lookatstudy-view]{container-type:inline-size}\n.lks14{flex:1;min-height:0;display:flex;flex-direction:column;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);overflow:hidden;font-family:var(--dsw-font-family)}\n.lks14 button{font-family:var(--dsw-font-family);cursor:pointer}\n.lks14-body{display:flex;flex:1;min-width:0;min-height:0}\n.lks14-col{display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden}\n.lks14-colhead{flex:none;display:flex;align-items:center;gap:8px;padding:10px 12px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1)}\n\n/* 左 rail: course picker, tree, review, import */\n.lks14-rail{flex:0 0 240px;border-right:1px solid var(--dsw-alias-border-l1);padding:0 10px 10px;overflow-y:auto}\n.lks14-railhead{display:flex;align-items:center;gap:6px;margin-top:10px}\n.lks14-railtitle{font-weight:600;font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}\n.lks14-railsub{font-size:12px;color:var(--dsw-alias-label-secondary);margin:6px 0}\n.lks14-masterybar{height:5px;border-radius:3px;background:var(--dsw-alias-bg-layer-2);overflow:hidden;margin-bottom:8px}\n.lks14-masterybar i{display:block;height:100%;background:var(--dsw-alias-business-primary);transform-origin:left;transition:transform .3s}\n.lks14-masterybar.gold i{background:var(--dsw-alias-state-warn-primary)}\n.lks14-search{width:100%;box-sizing:border-box;font:inherit;font-size:12.5px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:5px 8px;margin:4px 0}\n.lks14-search:focus{outline:none;border-color:var(--dsw-alias-state-business-primary)}\n.lks14-duebox{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:8px 10px;margin:8px 0;font-size:12.5px}\n.lks14-dueitem{display:flex;justify-content:space-between;gap:6px;padding:2px 0;color:var(--dsw-alias-label-secondary)}\n.lks14-over{color:var(--dsw-alias-state-error-primary)}\n.lks14-sechead{display:flex;align-items:center;gap:6px;width:100%;background:none;border:none;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12.5px;font-weight:600;padding:6px 2px;cursor:pointer;text-align:left}\n.lks14-sechead:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-secnum{flex:none;width:18px;height:18px;border-radius:5px;background:var(--dsw-alias-bg-layer-2);display:inline-flex;align-items:center;justify-content:center;font-size:11px}\n.lks14-secheadt{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.lks14-node{display:flex;align-items:center;gap:6px;width:100%;background:none;border:none;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;padding:5px 6px;border-radius:8px;cursor:pointer;text-align:left}\n.lks14-node:hover{background:var(--dsw-alias-bg-layer-1)}\n.lks14-node:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-node.focus{background:var(--dsw-alias-bg-layer-2)}\n.lks14-node[aria-disabled='true']{color:var(--dsw-alias-label-tertiary);cursor:default}\n.lks14-g{flex:none;display:inline-flex}\n.lks14-t{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.lks14-tag{flex:none;display:inline-flex;align-items:center;gap:2px;font-size:10.5px;border-radius:6px;padding:1px 4px}\n.lks14-tag.weak{color:var(--dsw-alias-state-warn-primary)}\n.lks14-bar{flex:none;width:26px;height:4px;border-radius:2px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}\n.lks14-bar i{display:block;height:100%;background:var(--dsw-alias-business-primary);transform-origin:left}\n.lks14-pct{flex:none;font-size:10.5px;color:var(--dsw-alias-label-tertiary)}\n.lks14-empty{color:var(--dsw-alias-label-tertiary);font-size:13px;padding:18px 6px;line-height:1.7}\n.lks14-import{margin:8px 0}\n.lks14-inputrow{display:flex;gap:6px}\n.lks14-hint{font-size:11.5px;color:var(--dsw-alias-label-tertiary);margin-top:6px;line-height:1.6}\n\n/* 中 chat: the tutor stream + its own composer (upstream ChatStream/ChatComposer) */\n.lks14-chat{flex:1 1 46%;min-width:340px;border-right:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-0, transparent)}\n.lks14-chatlesson{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:400;color:var(--dsw-alias-label-tertiary)}\n.lks14-pills{display:inline-flex;gap:2px}\n.lks14-pill{border:none;background:none;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:12px;padding:2px 8px;border-radius:7px;cursor:pointer}\n.lks14-pill:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-pill.on{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.lks14-stream{flex:1;overflow-y:auto;padding:12px 14px;display:flex;flex-direction:column;gap:10px}\n.lks14-msg{max-width:92%;line-height:1.65;font-size:13.5px}\n.lks14-msg-user{align-self:flex-end;background:var(--dsw-alias-bg-layer-2);border-radius:12px 12px 3px 12px;padding:7px 11px;white-space:pre-wrap}\n.lks14-msg-assistant{align-self:flex-start;background:var(--dsw-alias-bg-layer-1);border-radius:12px 12px 12px 3px;padding:8px 12px}\n.lks14-msg.streaming{opacity:.7}\n.lks14-msg-assistant p{margin:4px 0}\n.lks14-msg-assistant pre{overflow-x:auto;font-size:12px}\n.lks14-msg-assistant table{border-collapse:collapse;margin:6px 0;display:block;max-width:100%;overflow-x:auto}\n.lks14-msg-assistant th,.lks14-msg-assistant td{border:1px solid var(--dsw-alias-border-l2);padding:4px 8px;font-size:12px}\n.lks14-turn{max-width:92%;align-self:flex-start;display:flex;flex-direction:column;gap:6px}\n.lks14-quiz{display:flex;flex-direction:column;gap:4px}\n.lks14-opt{display:flex;gap:6px;align-items:baseline;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:9px;padding:6px 10px;font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left}\n.lks14-opt:hover{background:var(--dsw-alias-bg-layer-2)}\n.lks14-opt:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-optletter{flex:none;font-weight:700;color:var(--dsw-alias-business-primary)}\n.lks14-starters{flex:none;display:flex;flex-wrap:wrap;gap:4px;padding:6px 12px}\n.lks14-starter{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;border-radius:14px;padding:3px 10px;cursor:pointer}\n.lks14-starter:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}\n.lks14-starter:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-starter[aria-disabled='true']{opacity:.5;cursor:default}\n.lks14-composer{flex:none;display:flex;gap:6px;align-items:flex-end;padding:8px 12px 10px;border-top:1px solid var(--dsw-alias-border-l1)}\n.lks14-composertext{flex:1;font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:7px 10px;resize:none;box-sizing:border-box}\n.lks14-composertext:focus{outline:none;border-color:var(--dsw-alias-state-business-primary)}\n\n/* 右 notebook: 讲解/概念图/笔记 (upstream NotebookPanel) */\n.lks14-note{flex:0 0 clamp(320px, 30vw, 460px);padding:0 12px 12px;overflow-y:auto}\n.lks14-lessonhead{margin:10px 0 6px}\n.lks14-lessonhead h2{margin:0 0 4px;font-size:16px}\n.lks14-meta{font-size:12px;color:var(--dsw-alias-label-secondary);margin:2px 0}\n.lks14-viewtabs{display:flex;gap:2px;margin:8px 0}\n.lks14-viewtab{border:none;background:none;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:12.5px;padding:4px 10px;border-radius:8px;cursor:pointer;display:inline-flex;align-items:center;gap:4px}\n.lks14-viewtab:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-viewtab.on{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.lks14-prose{font-size:13.5px;line-height:1.7;color:var(--dsw-alias-label-primary);min-height:200px}\n.lks14-prose h1,.lks14-prose h2,.lks14-prose h3{font-size:15px;margin:12px 0 4px}\n.lks14-prose p{margin:6px 0}\n.lks14-prose pre{overflow-x:auto;font-size:12px;background:var(--dsw-alias-bg-layer-1);border-radius:8px;padding:8px}\n.lks14-prose code{font-family:var(--dsw-font-markdown-code);background:var(--dsw-alias-bg-layer-1);border-radius:4px;padding:1px 4px;font-size:.95em}\n.lks14-prose table{border-collapse:collapse;margin:6px 0;display:block;max-width:100%;overflow-x:auto}\n.lks14-prose th,.lks14-prose td{border:1px solid var(--dsw-alias-border-l2);padding:4px 8px;font-size:12.5px}\n.lks14-prose svg{max-width:100%;height:auto}\n.lks14-prose .katex-display{overflow-x:auto}\n.lks14-zones{min-height:200px}\n.lks14-zone{margin-bottom:12px}\n.lks14-zoneh{font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-secondary);margin-bottom:6px}\n\n/* note cards (shared: panel notebook + settings-era shapes) */\n.lks-note{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:10px 14px;margin-bottom:8px}\n.lks-note .lks-note-src{float:right;font-size:11.5px;color:var(--dsw-alias-label-secondary)}\n.lks-note-del{float:right;clear:right;border:none;background:none;color:var(--dsw-alias-label-tertiary);cursor:pointer;padding:2px;border-radius:5px;line-height:0}\n.lks-note-del:hover{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}\n.lks-note-del.armed{color:#fff;background:var(--dsw-alias-state-error-primary)}\n.lks-note .lks-note-title{font-weight:600;font-size:13px}\n.lks-note .lks-note-text{margin-top:4px;font-size:13px;color:var(--dsw-alias-label-secondary);line-height:1.65}\n.lks-note .lks-note-text p{margin:4px 0}\n.lks-note .lks-note-text table{border-collapse:collapse;margin:6px 0;display:block;max-width:100%;overflow-x:auto}\n.lks-note .lks-note-text th,.lks-note .lks-note-text td{border:1px solid var(--dsw-alias-border-l2);padding:4px 8px;font-size:12.5px}\n.lks-note .lks-note-text code{font-family:var(--dsw-font-markdown-code);background:var(--dsw-alias-bg-layer-3);border-radius:4px;padding:1px 4px;font-size:.95em}\n.lks-note .lks-note-q{margin-top:6px;color:var(--dsw-alias-label-tertiary);font-size:12px;border-left:2px solid var(--dsw-alias-state-warn-primary);padding-left:8px}\n\n/* shared buttons + inline error text */\n.lks-btn{display:inline-flex;align-items:center;gap:6px;border-radius:8px;padding:6px 14px;font-size:13.5px;font-weight:600;flex:none;font-family:var(--dsw-font-family);cursor:pointer}\n.lks-btn:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks-btn.primary{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 86%,#000);color:#fff}\n.lks-btn.primary:hover{filter:brightness(1.1)}\n.lks-btn.primary:disabled{opacity:.5;cursor:default;filter:none}\n.lks-btn.ghost{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l2)}\n.lks-btn.ghost:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}\n.lks-propcard-err{color:var(--dsw-alias-state-error-primary);font-size:12px;margin-top:6px;flex:none}\n\n/* read-aloud bar (notebook teach pane) + busy spinner */\n@keyframes lks-spin{to{transform:rotate(360deg)}}\n.lks-spin{animation:lks-spin .8s linear infinite}\n.lks-readbar{display:flex;align-items:center;gap:6px;margin:0 0 8px;min-height:26px}\n.lks-readbar .lks-btn{display:inline-flex;align-items:center;gap:4px}\n.lks-readbar-cur{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;color:var(--dsw-alias-label-secondary);border-left:2px solid var(--dsw-alias-state-info-primary,var(--dsw-alias-business-primary));padding-left:8px}\n.lks-readbar-notice{flex:none;font-size:11.5px;color:var(--dsw-alias-label-tertiary)}\n\n/* concept map legend (amber = weak) */\n.lks-cmap-legend{display:inline-flex;align-items:center;gap:5px;font-size:12px;color:var(--dsw-alias-label-secondary);margin-top:4px}\n.lks-cmap-legend i{width:10px;height:10px;border-radius:3px;background:var(--dsw-alias-state-warn-tertiary,#fef5e7);border:1px solid var(--dsw-alias-state-warn-primary,#dd8629)}\n\n/* settings section (settings.section entry inside the host settings shell) */\n.lks-settings{display:flex;flex-direction:column;gap:18px;font-family:var(--dsw-font-family);color:var(--dsw-alias-label-primary)}\n.lks-set-row h3{margin:0 0 4px;font-size:15px;font-weight:600}\n.lks-set-hint{margin:0 0 10px;font-size:13px;color:var(--dsw-alias-label-secondary)}\n.lks-set-state{margin-left:10px;font-size:13px;color:var(--dsw-alias-label-secondary)}\n.lks-set-select{font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:5px 8px;max-width:100%}\n.lks-set-stats{margin:0;padding-left:18px;font-size:14px;line-height:1.9}\n.lks-set-path{font-family:var(--dsw-font-markdown-code);font-size:12.5px;background:var(--dsw-alias-bg-layer-3);border-radius:6px;padding:3px 8px;word-break:break-all}\n\n/* soul pills (settings reuse) */\n.lks-pills{display:inline-flex;align-items:center;height:28px}\n.lks-pill{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border:none;border-radius:24px;background:transparent;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px;font-weight:500;transition:background .12s ease,color .12s ease;font-family:var(--dsw-font-family);cursor:pointer}\n.lks-pill:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}\n.lks-pill:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks-pill.on{background:var(--dsw-alias-state-business-tertiary);color:var(--dsw-alias-label-primary-bluish)}\n\n/* composer dock status pill (conversation.composer.dock entry) */\n.lks-dockpill{display:inline-flex;gap:10px;align-items:center;font-size:12px;color:var(--dsw-alias-label-secondary)}\n.lks-dockseg{white-space:nowrap;display:inline-flex;align-items:center;gap:3px}\n.lks-dock-due{color:var(--lks-warn-ink)}\n.lks-dock-streak{color:var(--dsw-alias-state-business-primary)}\n.lks-dockseg.lks-muted{color:var(--dsw-alias-label-secondary)}\n\n/* keyed tool.call.toolview cards (conversation tab tool rows) */\n.lks-tv{display:flex;flex-direction:column;gap:4px;padding:6px 10px;border-radius:10px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);font-size:13px}\n.lks-tv.err{border-color:var(--dsw-alias-state-error-primary)}\n.lks-tv-head{font-weight:600;color:var(--dsw-alias-label-secondary);font-size:12px}\n.lks-tv-chip{align-self:flex-start;border-radius:999px;padding:2px 10px;font-size:12.5px;font-weight:600}\n.lks-tv-chip.ok{background:var(--dsw-alias-state-success-tertiary);color:var(--lks-ok-ink)}\n.lks-tv-chip.bad{background:var(--dsw-alias-state-error-tertiary);color:var(--lks-err-ink)}\n.lks-tv-lines{display:flex;flex-direction:column;gap:4px}\n.lks-tv-line{color:var(--dsw-alias-label-secondary);line-height:1.6;white-space:pre-wrap}\n/* exam star card + the failure moment */\n.lks-tv-stars{display:inline-flex;align-items:center;gap:5px;font-size:13px;font-weight:600;color:var(--lks-warn-ink)}\n.lks-tv-stars .dim{opacity:.22}\n.lks-tv-stars-label{font-weight:400;color:var(--dsw-alias-label-secondary)}\n.lks-tv-failnote{margin-top:4px;font-size:12.5px;color:var(--dsw-alias-label-secondary);line-height:1.6}\n`\n\n/**\n * Inject the study stylesheet into `document.head` (idempotent).\n * @returns the stylesheet element (existing one when already injected).\n */\nexport function ensureStudyStyles(): HTMLStyleElement {\n const existing = document.head.querySelector(`style[${STYLE_ID}]`)\n if (existing !== null) return existing as HTMLStyleElement\n const style = document.createElement('style')\n style.setAttribute(STYLE_ID, '')\n style.textContent = STUDY_CSS\n document.head.appendChild(style)\n return style\n}\n","/**\n * ic_ds_* glyphs vendored from @deepseek-ai/dsh-client-ui-primitives\n * (src/icons/index.tsx, same figma source as the host's icon set) so the\n * plugin's client bundle stays self-contained per the dsh client-bundle\n * contract — only react is runtime-external; importing the host package\n * root would drag its CSS modules into the inlined bundle. Re-sync these\n * three paths if the host icon set redesigns them; scripts/verify.mjs\n * pins the path data.\n *\n * 2026-09-01 critique round: seven more ic_ds glyphs vendored (play/plus/\n * trash/refresh/globe/goal/warning) plus plugin-original learning-domain\n * companions (lock/star/book/crown/bolt/power/pin/flame) drawn in the same\n * language — those are NOT ic_ds, see their section comment below.\n * @module dsh-plugin-lookatstudy/client/icons\n */\n\nimport { createElement } from 'react'\n\n/** Shared props for every vendored glyph (mirrors the host's IconProps). */\ninterface IconProps {\n /** Square edge in px; defaults to the glyph's own drawn size. */\n size?: number\n /** Extra class for layout placement; color rides currentColor. */\n className?: string\n}\n\n/** ic_ds_think_outline_16 */\nexport function IconThinkOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', {\n d: 'M8.00192 6.64454C8.75026 6.64454 9.35732 7.25169 9.35739 8.00001C9.35739 8.74838 8.7503 9.35548 8.00192 9.35548C7.25367 9.35533 6.64743 8.74829 6.64743 8.00001C6.6475 7.25178 7.25371 6.64468 8.00192 6.64454Z',\n fill: 'currentColor',\n }),\n createElement('path', {\n 'fill-rule': 'evenodd',\n 'clip-rule': 'evenodd',\n d: 'M9.97165 1.29981C11.5853 0.718916 13.271 0.642197 14.3144 1.68555C15.3577 2.72902 15.2811 4.41466 14.7002 6.02833C14.4707 6.66561 14.1504 7.32937 13.75 8.00001C14.1504 8.67062 14.4707 9.33444 14.7002 9.97169C15.2811 11.5854 15.3578 13.271 14.3144 14.3145C13.271 15.3579 11.5854 15.2811 9.97165 14.7002C9.3344 14.4708 8.67059 14.1505 7.99997 13.75C7.32933 14.1505 6.66558 14.4708 6.02829 14.7002C4.41461 15.2811 2.72899 15.3578 1.68552 14.3145C0.642155 13.271 0.71887 11.5854 1.29977 9.97169C1.52915 9.33454 1.84865 8.67049 2.24899 8.00001C1.84866 7.32953 1.52915 6.66544 1.29977 6.02833C0.718852 4.41459 0.64207 2.729 1.68552 1.68555C2.72897 0.642112 4.41456 0.718887 6.02829 1.29981C6.66541 1.52918 7.32949 1.8487 7.99997 2.24903C8.67045 1.84869 9.33451 1.52919 9.97165 1.29981ZM12.9404 9.2129C12.4391 9.893 11.8616 10.5681 11.2148 11.2149C10.568 11.8616 9.89296 12.4391 9.21286 12.9404C9.62532 13.1579 10.0271 13.338 10.4121 13.4766C11.9146 14.0174 12.9172 13.8738 13.3955 13.3955C13.8737 12.9173 14.0174 11.9146 13.4765 10.4121C13.3379 10.0271 13.1578 9.62535 12.9404 9.2129ZM3.05856 9.2129C2.84121 9.62523 2.66197 10.0272 2.52341 10.4121C1.98252 11.9146 2.12627 12.9172 2.60446 13.3955C3.08278 13.8737 4.08544 14.0174 5.58786 13.4766C5.97264 13.338 6.37389 13.1577 6.7861 12.9404C6.10624 12.4393 5.43168 11.8614 4.78513 11.2149C4.13823 10.5679 3.55992 9.89313 3.05856 9.2129ZM7.99899 3.792C7.23179 4.31419 6.45306 4.95512 5.70407 5.70411C4.95509 6.45309 4.31415 7.23184 3.79196 7.99903C4.3143 8.76666 4.95471 9.54653 5.70407 10.2959C6.45309 11.0449 7.23271 11.6848 7.99997 12.207C8.76725 11.6848 9.54683 11.0449 10.2959 10.2959C11.0449 9.54686 11.6848 8.76729 12.207 8.00001C11.6848 7.23275 11.0449 6.45312 10.2959 5.70411C9.5465 4.95475 8.76662 4.31434 7.99899 3.792ZM5.58786 2.52344C4.08533 1.98255 3.08272 2.12625 2.60446 2.6045C2.12621 3.08275 1.98252 4.08536 2.52341 5.5879C2.66189 5.97253 2.8414 6.37409 3.05856 6.78614C3.55983 6.10611 4.1384 5.43189 4.78513 4.78516C5.43186 4.13843 6.10606 3.55987 6.7861 3.0586C6.37405 2.84144 5.97249 2.66192 5.58786 2.52344ZM13.3955 2.6045C12.9172 2.12631 11.9146 1.98257 10.4121 2.52344C10.0272 2.66201 9.62519 2.84125 9.21286 3.0586C9.8931 3.55996 10.5679 4.13827 11.2148 4.78516C11.8614 5.43172 12.4392 6.10627 12.9404 6.78614C13.1577 6.37393 13.338 5.97267 13.4765 5.5879C14.0174 4.08549 13.8736 3.08281 13.3955 2.6045Z',\n fill: 'currentColor',\n }))\n}\n\n/** ic_ds_loading_outline_16 */\nexport function IconLoadingOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', {\n d: 'M2.871 13.1286C0.0387669 10.2962 0.0387669 5.70383 2.871 2.87141C5.70341 0.0390029 10.2957 0.0391154 13.1282 2.87141L12.1387 3.86094C9.85292 1.57538 6.1469 1.57596 3.86123 3.86163C1.57573 6.14732 1.57573 9.85269 3.86123 12.1384C6.1469 14.424 9.85292 14.4246 12.1387 12.1391L13.1282 13.1286C10.2957 15.9609 5.70341 15.961 2.871 13.1286Z',\n fill: 'currentColor',\n }))\n}\n\n/** ic_ds_download_outline_16 */\nexport function IconDownloadOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', {\n d: 'M15.3695 11.411L15.1234 12.8866C14.8869 14.3042 13.6603 15.3436 12.223 15.3436H3.77673C2.33958 15.3434 1.1128 14.3042 0.876343 12.8866L0.630249 11.411L2.05408 11.1747L2.29919 12.6493C2.41973 13.3713 3.04475 13.9001 3.77673 13.9003H12.223C12.9551 13.9002 13.58 13.3713 13.7006 12.6493L13.9457 11.1747L15.3695 11.411ZM8.72205 8.994C8.77717 8.93934 8.83792 8.88106 8.90271 8.81627L12.4828 5.23424L13.5043 6.25572L9.92224 9.8358C9.6395 10.1185 9.38763 10.3732 9.15857 10.5575C8.91892 10.7503 8.63953 10.9224 8.2865 10.9784C8.09711 11.0083 7.90363 11.0083 7.71423 10.9784C7.36106 10.9224 7.0809 10.7503 6.84119 10.5575C6.61215 10.3732 6.36022 10.1185 6.07751 9.8358L2.49646 6.25572L3.51697 5.23424L7.09705 8.81627C7.16219 8.88142 7.22331 8.94006 7.27869 8.99498V1.3065H8.72205V8.994Z',\n fill: 'currentColor',\n }))\n}\n\n/** Vendored 2026-09-01 for the critique icon round (same figma source as the three above). */\n/** IconPlayOutline16 */\nexport function IconPlayOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M14.1446 8C14.1446 4.6062 11.3938 1.85539 8 1.85539C4.6062 1.85539 1.85539 4.6062 1.85539 8C1.85539 11.3938 4.6062 14.1446 8 14.1446C11.3938 14.1446 14.1446 11.3938 14.1446 8ZM15.511 8C15.511 12.148 12.148 15.511 8 15.511C3.85202 15.511 0.489014 12.148 0.489014 8C0.489014 3.85202 3.85202 0.489014 8 0.489014C12.148 0.489014 15.511 3.85202 15.511 8Z', fill: 'currentColor' }),\n createElement('path', { d: 'M10.5617 8.42578C10.852 8.21614 10.852 7.78386 10.5617 7.57422L7.25708 5.18751C6.90974 4.93666 6.42436 5.18484 6.42436 5.61329V10.3867C6.42436 10.8152 6.90974 11.0633 7.25708 10.8125L10.5617 8.42578Z', fill: 'currentColor' }))\n}\n\n/** IconPlusOutline16 */\nexport function IconPlusOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M8.64453 1.5V7.34961H14.5V8.65039H8.64453V14.5H7.34473V8.65039H1.5V7.34961H7.34473V1.5H8.64453Z', fill: 'currentColor' }))\n}\n\n/** IconTrashOutline16 */\nexport function IconTrashOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M14.4782 4.84067L14.2138 10.1152C14.1102 12.1872 14.067 13.0115 13.3866 13.9607C13.1044 14.3546 12.7498 14.6912 12.3424 14.9535C11.8239 15.2872 11.2415 15.4316 10.5585 15.4998C9.88727 15.5668 9.04946 15.5656 7.99998 15.5656C6.95051 15.5656 6.1127 15.5668 5.44142 15.4998C4.75851 15.4316 4.17602 15.2872 3.65753 14.9535C3.25012 14.6912 2.89559 14.3546 2.61332 13.9607C1.93296 13.0115 1.88979 12.1872 1.78619 10.1152L1.52179 4.84067L2.89006 4.77277L3.15343 10.0463C3.26221 12.2218 3.32452 12.6015 3.72646 13.1624C3.90825 13.4161 4.13686 13.6334 4.39927 13.8023C4.66204 13.9714 5.00263 14.0792 5.57825 14.1367C6.16562 14.1953 6.92298 14.1963 7.99998 14.1963C9.07699 14.1963 9.83434 14.1953 10.4217 14.1367C10.9973 14.0792 11.3379 13.9714 11.6007 13.8023C11.8631 13.6334 12.0917 13.4161 12.2735 13.1624C12.6755 12.6015 12.7378 12.2218 12.8465 10.0463L13.1099 4.77277L14.4782 4.84067ZM5.43011 6.22849H6.7994V11.3909H5.43011V6.22849ZM9.20056 6.22849H10.5699V11.3909H9.20056V6.22849ZM8.53597 0.434431C9.17976 0.434431 9.6522 0.426926 10.0966 0.571258C10.2357 0.616451 10.3717 0.672554 10.502 0.738948C10.9182 0.951107 11.2464 1.29099 11.7015 1.74612L12.4978 2.54136H15.3742V3.91169H0.625732V2.54136H3.50218L4.29845 1.74612C4.75358 1.29099 5.08174 0.951107 5.49801 0.738948C5.62831 0.672554 5.76425 0.616451 5.90334 0.571258C6.34776 0.426926 6.82021 0.434431 7.46399 0.434431H8.53597ZM7.46399 1.80476C6.73208 1.80476 6.51641 1.81187 6.32617 1.87369C6.25545 1.89667 6.18668 1.92533 6.12041 1.95907C5.96398 2.03878 5.82348 2.16253 5.44142 2.54136H10.5585C10.1765 2.16253 10.036 2.03878 9.87955 1.95907C9.81329 1.92533 9.74452 1.89667 9.6738 1.87369C9.48356 1.81187 9.26789 1.80476 8.53597 1.80476H7.46399Z', fill: 'currentColor' }))\n}\n\n/** IconRefreshOutline16 */\nexport function IconRefreshOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M7.92136 0.349152C10.3744 0.349234 12.5564 1.5052 13.9557 3.29894L15.1281 2.12759C15.3303 1.92546 15.6767 2.06943 15.6767 2.35538V5.53923C15.6766 5.71626 15.5329 5.85976 15.3559 5.86002H12.171C11.8854 5.8597 11.7426 5.51465 11.9443 5.31249L12.9641 4.29056C11.8237 2.74305 9.98908 1.74106 7.92136 1.74097C4.46436 1.74097 1.66233 4.543 1.66233 8C1.66233 11.457 4.46436 14.259 7.92136 14.259C11.3782 14.2589 14.1804 11.4569 14.1804 8H15.5722C15.5722 12.2251 12.1465 15.6507 7.92136 15.6508C3.69614 15.6508 0.270508 12.2252 0.270508 8C0.270508 3.77478 3.69614 0.349152 7.92136 0.349152Z', fill: 'currentColor' }))\n}\n\n/** IconGlobeOutline14 */\nexport function IconGlobeOutline14({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M7.00018 0.353516C10.6708 0.353535 13.6468 3.32958 13.6469 7.00018C13.6468 10.6708 10.6708 13.6468 7.00018 13.6469C3.32957 13.6468 0.353535 10.6708 0.353516 7.00018C0.353535 3.32957 3.32957 0.353531 7.00018 0.353516ZM5.44643 7.59661C5.49463 8.97506 5.70762 10.191 6.02136 11.0793C6.20141 11.5891 6.40328 11.9585 6.59898 12.1889C6.79501 12.4196 6.93213 12.454 7.00018 12.454C7.06822 12.454 7.20533 12.4197 7.40138 12.1889C7.59708 11.9585 7.79895 11.589 7.979 11.0793C8.29274 10.191 8.50574 8.97506 8.55394 7.59661H5.44643ZM1.57861 7.59661C1.80785 9.70467 3.2386 11.4509 5.1715 12.1388C5.07135 11.9317 4.97972 11.7098 4.89746 11.477C4.53084 10.4391 4.30224 9.0828 4.25357 7.59661H1.57861ZM9.74679 7.59661C9.69813 9.0828 9.46952 10.4391 9.1029 11.477C9.0206 11.7099 8.92818 11.9316 8.82797 12.1388C10.7613 11.4511 12.1925 9.70496 12.4218 7.59661H9.74679ZM5.1706 1.8616C3.23814 2.54963 1.80876 4.29604 1.5795 6.40376H4.25357C4.30224 4.91756 4.53083 3.56129 4.89746 2.5234C4.97968 2.29066 5.07051 2.0686 5.1706 1.8616ZM7.00018 1.54637C6.93213 1.54638 6.79503 1.5807 6.59898 1.81145C6.40332 2.04177 6.20139 2.41058 6.02136 2.92012C5.70754 3.80851 5.49461 5.02499 5.44643 6.40376H8.55394C8.50575 5.025 8.29282 3.80851 7.979 2.92012C7.79898 2.41059 7.59705 2.04177 7.40138 1.81145C7.20531 1.58067 7.06823 1.54637 7.00018 1.54637ZM8.82887 1.8616C8.92902 2.0687 9.02064 2.29053 9.1029 2.5234C9.46953 3.56129 9.69812 4.91756 9.74679 6.40376H12.4209C12.1916 4.29575 10.7618 2.54943 8.82887 1.8616Z', fill: 'currentColor' }))\n}\n\n/** IconGoalOutline16 */\nexport function IconGoalOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M8 0C8.31451 0 8.62464 0.019379 8.92969 0.0546875C8.48228 0.403371 8.0952 0.825758 7.78809 1.30469C4.18586 1.41664 1.2998 4.37061 1.2998 8C1.2998 11.7003 4.29969 14.7002 8 14.7002C11.6297 14.7002 14.5829 11.8136 14.6943 8.21094C15.1734 7.90377 15.5956 7.51688 15.9443 7.06934C15.9797 7.37473 16 7.68512 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0ZM7.0166 3.6084C7.00658 3.73765 7 3.86817 7 4C7 4.31845 7.03098 4.62973 7.08789 4.93164C5.76489 5.32438 4.7998 6.54958 4.7998 8C4.7998 9.76731 6.23269 11.2002 8 11.2002C9.45065 11.2002 10.6749 10.2345 11.0674 8.91113C11.3696 8.96818 11.6812 9 12 9C12.1315 9 12.2617 8.99239 12.3906 8.98242C11.9423 10.995 10.1477 12.5 8 12.5C5.51472 12.5 3.5 10.4853 3.5 8C3.5 5.85255 5.00435 4.05702 7.0166 3.6084Z', fill: 'currentColor' }),\n createElement('path', { d: 'M7.5 8.62109L9.12109 7', fill: 'currentColor' }),\n createElement('path', { d: 'M9.08245 3.35798L11.8651 0.575334C11.895 0.545384 11.9463 0.56391 11.9502 0.606086L12.2362 3.69859C12.2384 3.72259 12.2574 3.74159 12.2814 3.74378L15.3697 4.02583C15.4119 4.02968 15.4305 4.08101 15.4005 4.11098L12.618 6.89351C12.6086 6.90289 12.5959 6.90816 12.5826 6.90816L9.11781 6.90815C9.09019 6.90816 9.06781 6.88577 9.06781 6.85816L9.06781 3.39333C9.06781 3.38007 9.07308 3.36735 9.08245 3.35798Z', fill: 'currentColor' }))\n}\n\n/** IconWarningOutline16 */\nexport function IconWarningOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M6.3002 3.32843L7.69986 3.32843L7.69986 7.79657H6.3002L6.3002 3.32843Z', fill: 'currentColor' }),\n createElement('path', { d: 'M6.3002 9.01935H7.69986V10.6711H6.3002V9.01935Z', fill: 'currentColor' }),\n createElement('path', { d: 'M12.6328 6.99976C12.6328 3.88874 10.111 1.36694 7 1.36694C3.88899 1.36695 1.3672 3.88875 1.36719 6.99976C1.36719 10.1108 3.88899 12.6326 7 12.6326C10.111 12.6326 12.6328 10.1108 12.6328 6.99976ZM13.8582 6.99976C13.8582 10.7873 10.7876 13.8579 7 13.8579C3.21244 13.8579 0.141846 10.7873 0.141846 6.99976C0.141857 3.2122 3.21245 0.141612 7 0.141602C10.7876 0.141602 13.8581 3.21219 13.8582 6.99976Z', fill: 'currentColor' }))\n}\n\n\n/**\n * Plugin-original companions for learning-domain glyphs the host set does not\n * carry (lock/star/book/crown/bolt/power/pin/flame) — authored here in the\n * same 16px currentColor filled language as the ic_ds set, but they are NOT\n * ic_ds glyphs and must be re-drawn (not re-synced) if the style drifts.\n */\n/** IconLockFill16 */\nexport function IconLockFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { 'fill-rule': 'evenodd', 'clip-rule': 'evenodd', d: 'M5 6.5V5a3 3 0 0 1 6 0v1.5h.6c.77 0 1.4.63 1.4 1.4v5.7c0 .77-.63 1.4-1.4 1.4H4.4A1.4 1.4 0 0 1 3 13.6V7.9c0-.77.63-1.4 1.4-1.4H5Zm1.444 0h3.112V5a1.556 1.556 0 0 0-3.112 0v1.5Z', fill: 'currentColor' }))\n}\n\n/** IconStarFill16 */\nexport function IconStarFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M8 1.6l1.86 3.77 4.16.6-3.01 2.94.71 4.14L8 11.42l-3.72 1.96.71-4.15-3.01-2.93 4.16-.6L8 1.6Z', fill: 'currentColor' }))\n}\n\n/** IconBookFill16 */\nexport function IconBookFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { 'fill-rule': 'evenodd', 'clip-rule': 'evenodd', d: 'M8 3.1C6.6 2.15 4.65 1.75 2.5 1.75c-.5 0-1 .03-1.5.1v10.7c.5-.07 1-.1 1.5-.1 2.15 0 4.1.4 5.5 1.35 1.4-.95 3.35-1.35 5.5-1.35.5 0 1 .03 1.5.1V1.85c-.5-.07-1-.1-1.5-.1-2.15 0-4.1.4-5.5 1.35ZM7.2 4.9v7.3C5.9 11.55 4.3 11.3 2.6 11.3V3.45c1.7 0 3.3.25 4.6 1.45Zm1.6 0c1.3-1.2 2.9-1.45 4.6-1.45v7.85c-1.7 0-3.3.25-4.6.9V4.9Z', fill: 'currentColor' }))\n}\n\n/** IconCrownFill16 */\nexport function IconCrownFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M1.7 11.2 1 4.9l3.6 2.2L8 2.6l3.4 4.5L15 4.9l-.7 6.3H1.7Zm.5 1.4h11.6v1.5H2.1v-1.5Z', fill: 'currentColor' }))\n}\n\n/** IconBoltFill16 */\nexport function IconBoltFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M9.6 1 3.4 9.3h3.4L6.3 15l6.3-8.5H9.2L9.6 1Z', fill: 'currentColor' }))\n}\n\n/** IconPowerFill16 */\nexport function IconPowerFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M7.2 1h1.6v5.6H7.2V1Z', fill: 'currentColor' }),\n createElement('path', { 'fill-rule': 'evenodd', 'clip-rule': 'evenodd', d: 'M4.55 3.15A6 6 0 1 0 11.45 3.15L12.5 4.2a7.5 7.5 0 1 1-9 0l1.05-1.05Z', fill: 'currentColor' }))\n}\n\n/** IconPinFill16 */\nexport function IconPinFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { 'fill-rule': 'evenodd', 'clip-rule': 'evenodd', d: 'M8 1.4a4.8 4.8 0 0 1 4.8 4.8c0 3.4-4.8 8.4-4.8 8.4S3.2 9.6 3.2 6.2A4.8 4.8 0 0 1 8 1.4Zm0 2.7a2.1 2.1 0 1 0 0 4.2 2.1 2.1 0 0 0 0-4.2Z', fill: 'currentColor' }))\n}\n\n/** IconFlameFill16 */\nexport function IconFlameFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M8.9 1.2c.4 2.5-.9 3.9-2.1 5.2C5.6 7.7 4.4 9 4.4 10.9a3.9 3.9 0 0 0 7.8 0c0-1.6-.7-2.9-1.5-4-.3.8-.8 1.4-1.6 1.7.5-2.4-.2-5.4-1.2-7.4Z', fill: 'currentColor' }))\n}\n\n","/**\n * Shared client-side study store: one module-level poller over the host\n * plugin's `/lookatstudy/api/state` endpoint, plus the three write actions\n * (focus, mode, reverse message). Every seat component binds through\n * {@link useStudy}; subscribers share the single 3 s poll cycle and the last\n * snapshot, so mounting more controls costs no extra requests.\n * @module dsh-plugin-lookatstudy/client/data\n */\n\nimport { useSyncExternalStore } from 'react'\n\n/** Wire shape of `GET /lookatstudy/api/state` (structural mirror of the host's WorkbenchState). */\nexport interface StudyState {\n readonly active: boolean\n readonly mode: 'direct' | 'guide' | 'practice'\n readonly courses: ReadonlyArray<{\n readonly courseId: string\n readonly title: string\n readonly mastered: number\n readonly total: number\n readonly avgMasteryPct: number | null\n readonly sections: ReadonlyArray<{\n readonly title: string\n readonly index: number\n readonly lessons: ReadonlyArray<{\n readonly id: string\n readonly title: string\n readonly kind: 'study' | 'practice' | 'exam'\n readonly status: string\n readonly masteryPct: number | null\n readonly weakConcepts: number\n readonly frictionCount: number\n readonly due: boolean\n readonly focus: boolean\n }>\n }>\n }>\n readonly focusLessonId: string | null\n readonly lesson: {\n readonly lessonId: string\n readonly courseTitle: string\n readonly sectionTitle: string\n readonly title: string\n readonly status: string\n readonly masteryPct: number | null\n readonly strategy: string\n readonly concepts: ReadonlyArray<{ title: string; masteryPct: number; weak: boolean }>\n readonly starters: ReadonlyArray<{ label: string; message: string }>\n readonly notes: ReadonlyArray<{ id: string; zone: string; title: string; text: string; source: string; quote: string | null }>\n readonly html: string\n readonly markdown: string\n readonly speechText: string\n } | null\n readonly dueCount: number\n readonly due: ReadonlyArray<{ lessonId: string; lessonTitle: string; courseTitle: string; overdueDays: number }>\n readonly pendingProposals: ReadonlyArray<{ id: string; lessonTitle: string; rationale: string }>\n readonly memory: { global: string | null; lesson: string | null; pattern: string | null }\n readonly lessonSessions: Readonly<Record<string, string>>\n /** XP + streak block (mirrors study_courses; feeds the dock pill and the settings page). */\n readonly progress: {\n readonly totalXp: number\n readonly level: number\n readonly levelPct: number\n readonly todayXp: number\n readonly dailyGoal: number\n readonly streak: number\n readonly longestStreak: number\n readonly freezeCount: number\n }\n /** Absolute state-file path (read-only display). */\n readonly statePath: string\n /** Plugin version from the running build's package.json (settings About row). */\n readonly version: string\n}\n\n/** Poll cadence for the shared store; one cycle serves every mounted seat. */\nconst POLL_MS = 3_000\n\ntype Listener = () => void\n\nasync function fetchJson(url: string, init?: RequestInit): Promise<unknown> {\n const res = await fetch(url, init)\n const body: unknown = await res.json().catch(() => null)\n if (!res.ok) {\n const error = body !== null && typeof body === 'object' && 'error' in body && typeof body.error === 'string'\n ? body.error\n : `HTTP ${res.status}`\n throw new Error(error)\n }\n return body\n}\n\nclass StudyStore {\n private snapshot: StudyState | null = null\n private readonly listeners = new Set<Listener>()\n private timer: ReturnType<typeof setInterval> | undefined\n private inflight = false\n\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener)\n if (this.listeners.size === 1) {\n void this.tick()\n this.timer = setInterval(() => { void this.tick() }, POLL_MS)\n }\n return () => {\n this.listeners.delete(listener)\n if (this.listeners.size === 0 && this.timer !== undefined) {\n clearInterval(this.timer)\n this.timer = undefined\n }\n }\n }\n\n getSnapshot = (): StudyState | null => this.snapshot\n\n private emit(): void {\n for (const listener of this.listeners) listener()\n }\n\n private async tick(): Promise<void> {\n if (this.inflight) return\n this.inflight = true\n try {\n const next = await fetchJson('/lookatstudy/api/state') as StudyState\n this.snapshot = next\n this.emit()\n } catch {\n // Host route absent (headless composition) or transient failure: keep\n // the last snapshot; seats render their loading/empty states.\n } finally {\n this.inflight = false\n }\n }\n\n /** One immediate re-read (after a write action). */\n refresh(): void {\n void this.tick()\n }\n\n /** Switch the tutor's soul mode directly (host persists and applies it on the next request). */\n async setMode(mode: StudyState['mode']): Promise<void> {\n await fetchJson('/lookatstudy/api/mode', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ mode }),\n })\n this.refresh()\n }\n\n /**\n * Flip learning mode. The host syncs its tool registry before responding,\n * so awaiting this guarantees the `study_*` tools exist for the next prompt.\n */\n async activate(active: boolean): Promise<void> {\n await fetchJson('/lookatstudy/api/active', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ active }),\n })\n this.refresh()\n }\n\n /** Full-text lesson search (rail fallback when local title matching misses). */\n async searchLessons(query: string): Promise<Array<{ lessonId: string; lessonTitle: string; snippet: string }>> {\n const body = await fetchJson(`/lookatstudy/api/search?q=${encodeURIComponent(query)}`) as { matches?: Array<{ lessonId: string; lessonTitle: string; snippet: string }> }\n return body.matches ?? []\n }\n\n /** Point the tutor's focus at one lesson. */\n async setFocus(lessonId: string): Promise<void> {\n await fetchJson('/lookatstudy/api/focus', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ lessonId }),\n })\n this.refresh()\n }\n\n /** Record the dsh session backing one lesson (the simplified thread system). */\n async bindLessonSession(lessonId: string, sessionId: string): Promise<void> {\n await fetchJson('/lookatstudy/api/lesson-session', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ lessonId, sessionId }),\n })\n this.refresh()\n }\n\n /** Delete one course (and its proposals) from the host state. */\n async deleteCourse(courseId: string): Promise<void> {\n await fetchJson('/lookatstudy/api/course/delete', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ courseId }),\n })\n this.refresh()\n }\n\n /** Delete one notebook entry from a lesson's Cornell zones. */\n async deleteNote(lessonId: string, noteId: string): Promise<void> {\n await fetchJson('/lookatstudy/api/note/delete', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ lessonId, noteId }),\n })\n this.refresh()\n }\n\n /** Synthesize one speakable chunk to MP3 (host-side Edge TTS, cache-first). */\n async tts(text: string, voice?: string): Promise<ArrayBuffer> {\n const body = await fetchJson('/lookatstudy/api/tts', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ text, voice }),\n }) as { ok?: boolean; dataBase64?: string }\n const raw = body.dataBase64\n if (body.ok !== true || typeof raw !== 'string') throw new Error('tts response missing audio payload')\n // atob → Uint8Array (binary string decode), Blob-ready for <audio>.\n const bin = atob(raw)\n const bytes = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)\n return bytes.buffer\n }\n}\n\n/** The voices the dashboard route accepts (same allowlist server-side). */\nexport const TTS_VOICES: ReadonlyArray<{ id: string; labelKey: string }> = [\n { id: 'zh-CN-XiaoxiaoNeural', labelKey: 'voice.xiaoxiao' },\n { id: 'zh-CN-YunxiNeural', labelKey: 'voice.yunxi' },\n { id: 'zh-CN-YunyangNeural', labelKey: 'voice.yunyang' },\n { id: 'zh-CN-XiaoyiNeural', labelKey: 'voice.xiaoyi' },\n { id: 'en-US-AriaNeural', labelKey: 'voice.aria' },\n { id: 'en-US-GuyNeural', labelKey: 'voice.guy' },\n]\n\nconst TTS_VOICE_KEY = 'dsh-plugin-lookatstudy:tts-voice'\n\n/** Narrow a stored voice id to the allowlist; anything else falls back to 晓晓. Pure. */\nexport function normalizeStoredVoice(raw: string | null | undefined): string {\n return TTS_VOICES.some(v => v.id === raw) ? raw! : TTS_VOICES[0]!.id\n}\n\n/** The selected read-aloud voice (browser-local preference; host state untouched). */\nexport function storedTtsVoice(): string {\n try {\n return normalizeStoredVoice(typeof localStorage === 'undefined' ? null : localStorage.getItem(TTS_VOICE_KEY))\n } catch {\n return TTS_VOICES[0]!.id\n }\n}\n\nexport function storeTtsVoice(id: string): void {\n try {\n localStorage.setItem(TTS_VOICE_KEY, normalizeStoredVoice(id))\n } catch {\n // Privacy modes forbid storage writes; the picker still reflects this page's choice.\n }\n}\n\n/** The one shared store instance backing every study seat component. */\nexport const studyStore = new StudyStore()\n\n/** React binding: live snapshot plus the write actions. */\nexport function useStudy(): {\n data: StudyState | null\n activate: (active: boolean) => Promise<void>\n setMode: (mode: StudyState['mode']) => Promise<void>\n setFocus: (lessonId: string) => Promise<void>\n searchLessons: (query: string) => Promise<Array<{ lessonId: string; lessonTitle: string; snippet: string }>>\n deleteCourse: (courseId: string) => Promise<void>\n deleteNote: (lessonId: string, noteId: string) => Promise<void>\n bindLessonSession: (lessonId: string, sessionId: string) => Promise<void>\n} {\n const data = useSyncExternalStore(studyStore.subscribe, studyStore.getSnapshot, studyStore.getSnapshot)\n return {\n data,\n activate: studyStore.activate.bind(studyStore),\n setMode: studyStore.setMode.bind(studyStore),\n setFocus: studyStore.setFocus.bind(studyStore),\n searchLessons: studyStore.searchLessons.bind(studyStore),\n deleteCourse: studyStore.deleteCourse.bind(studyStore),\n deleteNote: studyStore.deleteNote.bind(studyStore),\n bindLessonSession: studyStore.bindLessonSession.bind(studyStore),\n }\n}\n","/**\n * Server-side markdown → HTML for the study workbench's 讲解 view. Escapes\n * every HTML character first, then renders a pragmatic GFM subset (headings,\n * fenced code, inline code, bold/italic, links, lists, blockquotes, tables,\n * hr, paragraphs). Lesson bodies are imported teaching material, so raw HTML\n * never passes through.\n * @module dsh-plugin-lookatstudy/markdown\n */\n\n/** Escape all HTML-significant characters. */\nfunction escapeHtml(text: string): string {\n return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"')\n}\n\n/** Render inline markup (code, bold, italic, links, images) over escaped text.\n * Image src allowlist: https?:// or data:image/ — folder imports inline local\n * images as data URLs (capped), GitHub imports reference jsDelivr; anything\n * else stays literal text (no broken relative images, no exotic schemes). */\nfunction inline(escaped: string): string {\n return escaped\n .replace(/`([^`]+)`/g, '<code>$1</code>')\n .replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n .replace(/\\*([^*]+)\\*/g, '<em>$1</em>')\n .replace(/!\\[([^\\]]*)\\]\\((https?:\\/\\/[^)\\s]+|data:image\\/[^)\\s]+)\\)/g, (_m, alt: string, src: string) => `<img src=\"${src}\" alt=\"${alt}\" loading=\"lazy\">`)\n .replace(/\\[([^\\]]+)\\]\\((https?:\\/\\/[^)\\s]+)\\)/g, '<a href=\"$2\" target=\"_blank\" rel=\"noreferrer\">$1</a>')\n}\n\n/** Interleave one body with its translation, paragraph by paragraph (upstream\n * translation-layout \"microsoft\" semantics: original paragraph then its\n * translation as a quote, tight pairing for bilingual reading; length\n * mismatches append the remainder in order). Pure. */\nexport function renderBilingual(bodyMd: string, translationMd: string): string {\n const par = (md: string): string[] => md.split(/\\n\\s*\\n/).map(p => p.trim()).filter(p => p !== '')\n const a = par(bodyMd)\n const b = par(translationMd)\n const out: string[] = []\n for (let i = 0; i < Math.max(a.length, b.length); i++) {\n if (a[i] !== undefined) out.push(a[i]!)\n if (b[i] !== undefined) out.push(`> ${b[i]!.split('\\n').join('\\n> ')}`)\n }\n return out.join('\\n\\n')\n}\n\n/** True when the line opens a GFM table row (pipes with a delimiter row next). */\nfunction isTableRow(line: string): boolean {\n return line.trim().startsWith('|') && line.trim().endsWith('|') && line.includes('|', 1)\n}\n\nfunction isDelimiterRow(line: string): boolean {\n const trimmed = line.trim()\n if (!trimmed.startsWith('|') || !trimmed.endsWith('|')) return false\n const cells = trimmed.replace(/^\\|/, '').replace(/\\|$/, '').split('|').map(c => c.trim())\n return cells.length > 0 && cells.every(c => /^:?-+:?$/.test(c))\n}\n\n/** Split a table row into trimmed cells (leading/trailing pipes removed). */\nfunction rowCells(line: string): string[] {\n return line.trim().replace(/^\\|/, '').replace(/\\|$/, '').split('|').map(c => c.trim())\n}\n\n/**\n * Render markdown text to sanitized HTML.\n * @param md - markdown source (lesson body).\n * @returns HTML string safe to inject into the page.\n */\nexport function renderMarkdown(md: string): string {\n const lines = escapeHtml(md).split(/\\r?\\n/)\n const out: string[] = []\n let i = 0\n\n const flushParagraph = (buffer: string[]): void => {\n if (buffer.length > 0) out.push(`<p>${inline(buffer.join(' '))}</p>`)\n }\n\n let paragraph: string[] = []\n while (i < lines.length) {\n const line = lines[i]!\n\n // Fenced code block\n const fence = /^```(\\w*)\\s*$/.exec(line.trim())\n if (fence) {\n flushParagraph(paragraph)\n paragraph = []\n const lang = fence[1] ?? ''\n const code: string[] = []\n i++\n while (i < lines.length && lines[i]!.trim() !== '```') {\n code.push(lines[i]!)\n i++\n }\n i++ // closing fence\n out.push(`<pre><code${lang === '' ? '' : ` class=\"lang-${lang}\"`}>${code.join('\\n')}</code></pre>`)\n continue\n }\n\n // Heading\n const heading = /^(#{1,6})\\s+(.*)$/.exec(line)\n if (heading) {\n flushParagraph(paragraph)\n paragraph = []\n const level = heading[1]!.length\n out.push(`<h${level}>${inline(heading[2]!)}</h${level}>`)\n i++\n continue\n }\n\n // Horizontal rule\n if (/^\\s*(-{3,}|\\*{3,}|_{3,})\\s*$/.test(line)) {\n flushParagraph(paragraph)\n paragraph = []\n out.push('<hr>')\n i++\n continue\n }\n\n // Blockquote\n if (/^\\s*>\\s?/.test(line)) {\n flushParagraph(paragraph)\n paragraph = []\n const quote: string[] = []\n while (i < lines.length && /^\\s*>\\s?/.test(lines[i]!)) {\n quote.push(lines[i]!.replace(/^\\s*>\\s?/, ''))\n i++\n }\n out.push(`<blockquote><p>${inline(quote.join(' '))}</p></blockquote>`)\n continue\n }\n\n // Unordered list\n if (/^\\s*[-*+]\\s+/.test(line)) {\n flushParagraph(paragraph)\n paragraph = []\n const items: string[] = []\n while (i < lines.length && /^\\s*[-*+]\\s+/.test(lines[i]!)) {\n items.push(`<li>${inline(lines[i]!.replace(/^\\s*[-*+]\\s+/, ''))}</li>`)\n i++\n }\n out.push(`<ul>${items.join('')}</ul>`)\n continue\n }\n\n // Ordered list\n if (/^\\s*\\d+\\.\\s+/.test(line)) {\n flushParagraph(paragraph)\n paragraph = []\n const items: string[] = []\n while (i < lines.length && /^\\s*\\d+\\.\\s+/.test(lines[i]!)) {\n items.push(`<li>${inline(lines[i]!.replace(/^\\s*\\d+\\.\\s+/, ''))}</li>`)\n i++\n }\n out.push(`<ol>${items.join('')}</ol>`)\n continue\n }\n\n // GFM table\n if (isTableRow(line) && i + 1 < lines.length && isDelimiterRow(lines[i + 1]!)) {\n flushParagraph(paragraph)\n paragraph = []\n const headers = rowCells(line)\n i += 2\n const rows: string[] = []\n while (i < lines.length && isTableRow(lines[i]!)) {\n const cells = rowCells(lines[i]!)\n rows.push(`<tr>${cells.map(c => `<td>${inline(c)}</td>`).join('')}</tr>`)\n i++\n }\n out.push(`<table><thead><tr>${headers.map(h => `<th>${inline(h)}</th>`).join('')}</tr></thead><tbody>${rows.join('')}</tbody></table>`)\n continue\n }\n\n // Blank line ends the paragraph\n if (line.trim() === '') {\n flushParagraph(paragraph)\n paragraph = []\n i++\n continue\n }\n\n paragraph.push(line.trim())\n i++\n }\n flushParagraph(paragraph)\n return out.join('\\n')\n}\n","// Vendored from LookatStudy src/renderer/lib/mermaid-elk-rewrite.ts (MIT License, https://github.com/Kaiji-Z/LookatStudy).\n// Unmodified except this provenance header (elkjs dynamic import target adapted to CDN).\n/**\n * mermaid-elk-rewrite —— flowchart → flowchart-elk 前缀改写(v0.21 纯函数)。\n *\n * 只在渲染层改写:LLM 出题提示词(draw_diagram)与语法修复回路\n * (diagram-repair-service)都继续产出/接收**原版 flowchart 语法**,零改动;\n * renderMermaid 喂给 mermaid 前把 flowchart/graph 声明行改成 flowchart-elk ——\n * 同一套语法换 ELK 布局引擎(正交路由,复杂流程图边交叉显著少于 dagre)。\n *\n * mermaid 机制:flowchart-elk 前缀 → detector 置 config.layout=\"elk\" → 查\n * registerLayoutLoaders 注册的 \"elk\" 算法;未注册则静默退 dagre 并 console.warn\n * (ui-test 靠该 warn 断言 ELK 真实生效)。\n *\n * 范围:只改首个内容行的 flowchart 家族声明;sequenceDiagram/stateDiagram-v2/\n * classDiagram 等其余图类型原样返回(ELK 布局只对 flowchart 家族注册)。\n */\n\n/** 首个内容行(跳过空行与 %% 注释/`%%{init}%%` 指令行)的 flowchart 声明改为\n * flowchart-elk;已是 flowchart-elk 则幂等返回;其他图类型不动。 */\nexport function rewriteFlowchartToElk(code: string): string {\n const lines = code.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? \"\";\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"%%\")) continue;\n if (/^flowchart-elk\\b/i.test(trimmed)) return code;\n if (/^(flowchart|graph)\\b/i.test(trimmed)) {\n lines[i] = line.replace(/^(\\s*)(flowchart|graph)\\b/i, \"$1flowchart-elk\");\n return lines.join(\"\\n\");\n }\n // 首个内容行不是 flowchart 家族 → 其他图类型,不改写\n return code;\n }\n return code;\n}\n","/**\n * Post-render blackboard enhancement (SPEC Phase 2): math (KaTeX), code\n * highlighting (shiki), and mermaid diagrams — every dependency loaded from a\n * CDN at runtime (zero-dependency mandate: nothing enters the bundle), every\n * failure degrading silently to the existing plain rendering. Upstream parity:\n * math-normalize runs host-side before renderMarkdown; the CDN + lazy-singleton\n * pattern follows upstream lazy-shiki/lazy-mermaid, pointed at esm.sh/cdnjs.\n *\n * Test seams: every loader is injectable via `setEnhanceDeps` so node:test\n * proves the enhance/degrade paths offline (the structural-test discipline:\n * prove a gate fails first, then trust its green).\n */\n\n/** Map a lang-xxx class to shiki's grammar id (upstream's curated set, aliases included). */\nconst LANG_ALIAS: Record<string, string> = {\n ts: 'typescript', tsx: 'tsx', js: 'javascript', jsx: 'jsx', mjs: 'javascript', cjs: 'javascript',\n py: 'python', rb: 'ruby', rs: 'rust', 'c++': 'cpp', cs: 'csharp', kt: 'kotlin', sh: 'bash', shell: 'bash', zsh: 'bash',\n yml: 'yaml', md: 'markdown', tex: 'latex', docker: 'dockerfile',\n}\n\nimport { rewriteFlowchartToElk } from '../vendor/mermaid-elk-rewrite.ts'\n\ntype KatexLike = { renderToString(tex: string, opts: Record<string, unknown>): string }\ntype ShikiLike = { codeToHtml(code: string, opts: Record<string, unknown>): Promise<string> }\ntype MermaidLike = { registerLayoutLoaders(l: unknown): void; initialize(cfg: Record<string, unknown>): void; render(id: string, code: string): Promise<{ svg: string }> }\n\nexport interface EnhanceDeps {\n loadKatex?: () => Promise<KatexLike>\n loadShiki?: () => Promise<ShikiLike>\n loadMermaid?: () => Promise<MermaidLike>\n}\nlet deps: EnhanceDeps = {}\nexport function setEnhanceDeps(d: EnhanceDeps | null): void { deps = d ?? {} }\n\n/* ---------------- CDN loaders (production defaults) ---------------- */\n\nconst CDN = {\n katexJs: 'https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js',\n katexCss: 'https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css',\n shiki: 'https://esm.sh/shiki@1.29.2',\n mermaid: 'https://esm.sh/mermaid@11.4.1',\n mermaidElk: 'https://esm.sh/@mermaid-js/layout-elk@0.1.7',\n}\n\nfunction loadScript(src: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const el = document.createElement('script')\n el.src = src\n el.onload = () => { resolve() }\n el.onerror = () => { reject(new Error(`script load failed: ${src}`)) }\n document.head.append(el)\n })\n}\n\nlet katexPromise: Promise<KatexLike> | null = null\nfunction defaultLoadKatex(): Promise<KatexLike> {\n katexPromise ??= (async () => {\n if (!document.querySelector(`link[data-lks-katex]`)) {\n const link = document.createElement('link')\n link.rel = 'stylesheet'\n link.href = CDN.katexCss\n link.setAttribute('data-lks-katex', '1')\n document.head.append(link)\n }\n if ((window as { katex?: KatexLike }).katex === undefined) await loadScript(CDN.katexJs)\n const katex = (window as { katex?: KatexLike }).katex\n if (katex === undefined) throw new Error('katex missing after load')\n return katex\n })()\n return katexPromise\n}\n\nlet shikiPromise: Promise<ShikiLike> | null = null\nfunction defaultLoadShiki(): Promise<ShikiLike> {\n shikiPromise ??= import(/* @vite-ignore */ CDN.shiki).then((mod: Record<string, unknown>) => {\n const create = mod.createHighlighter as ((opts: Record<string, unknown>) => Promise<ShikiLike>) | undefined\n if (create === undefined) throw new Error('shiki createHighlighter missing')\n return create({ themes: ['github-dark', 'github-light'], langs: ['typescript', 'javascript', 'python', 'bash', 'json', 'yaml', 'css', 'html', 'markdown', 'cpp', 'go', 'rust', 'java', 'sql'] })\n }) as Promise<ShikiLike>\n return shikiPromise\n}\n\nlet mermaidPromise: Promise<MermaidLike> | null = null\nfunction defaultLoadMermaid(): Promise<MermaidLike> {\n mermaidPromise ??= (async () => {\n const mod = await import(/* @vite-ignore */ CDN.mermaid) as Record<string, unknown>\n const mermaid = mod.default as MermaidLike\n try {\n const elk = await import(/* @vite-ignore */ CDN.mermaidElk) as Record<string, unknown>\n mermaid.registerLayoutLoaders(elk.default)\n } catch { /* ELK unavailable → mermaid falls back to dagre silently */ }\n mermaid.initialize({ startOnLoad: false, theme: 'base', securityLevel: 'loose' })\n return mermaid\n })()\n return mermaidPromise\n}\n\n/* ---------------- enhancers ---------------- */\n\n/** Split one text node's data around the FIRST math span; returns null when none. */\nfunction findMathSpan(text: string): { before: string; tex: string; after: string; display: boolean } | null {\n const display = /\\$\\$([^$]+)\\$\\$/.exec(text)\n const inline = /\\$([^$\\n]+)\\$/.exec(text)\n if (display === null && inline === null) return null\n const m = display !== null && (inline === null || display.index <= inline.index) ? { ...display, display: true } : inline === null ? null : { ...inline, display: false }\n if (m === null) return null\n return { before: text.slice(0, m.index), tex: m[1]!, after: text.slice(m.index + m[0].length), display: m.display }\n}\n\n/** KaTeX every $..$ / $$..$$ in the container; degrades to plain text on any failure. */\nexport async function enhanceMath(container: HTMLElement): Promise<number> {\n let katex: KatexLike\n try { katex = await (deps.loadKatex ?? defaultLoadKatex)() } catch { return 0 }\n let replaced = 0\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT)\n const targets: Text[] = []\n let node: Node | null\n while ((node = walker.nextNode()) !== null) {\n if (findMathSpan((node as Text).data) !== null && (node as Text).parentElement?.tagName !== 'CODE') targets.push(node as Text)\n }\n for (const t of targets) {\n const span = findMathSpan(t.data)\n if (span === null) continue\n try {\n const html = katex.renderToString(span.tex, { displayMode: span.display, throwOnError: false })\n const holder = document.createElement('span')\n holder.innerHTML = html\n if (span.before !== '') t.before(document.createTextNode(span.before))\n t.before(holder)\n t.data = span.after\n replaced++\n } catch { /* leave the text as-is */ }\n }\n return replaced\n}\n\n/** shiki-highlight every fenced code block; unknown langs and failures keep the plain <pre>. */\nexport async function enhanceCode(container: HTMLElement): Promise<number> {\n let shiki: ShikiLike\n try { shiki = await (deps.loadShiki ?? defaultLoadShiki)() } catch { return 0 }\n let replaced = 0\n const blocks = [...container.querySelectorAll<HTMLElement>('pre > code')]\n for (const code of blocks) {\n if (code.dataset.lksEnhanced === '1') continue\n const langClass = [...code.classList].find(c => c.startsWith('lang-'))\n if (langClass === undefined) continue\n const lang = LANG_ALIAS[langClass.slice(5)] ?? langClass.slice(5)\n try {\n const html = await shiki.codeToHtml(code.textContent ?? '', {\n lang,\n themes: { light: 'github-light', dark: 'github-dark' },\n defaultColor: false,\n cssVariables: { '--shiki-dark': 'var(--shiki-dark, #e6edf3)' },\n })\n code.dataset.lksEnhanced = '1'\n const pre = code.parentElement!\n const wrap = document.createElement('div')\n wrap.className = 'lks-shiki'\n wrap.innerHTML = html\n pre.replaceWith(wrap)\n replaced++\n } catch { code.dataset.lksEnhanced = '1' /* don't retry a failing lang */ }\n }\n return replaced\n}\n\n/** Render mermaid fence blocks to SVG (ELK layout via the registered loader);\n * the flowchart prefix rewrite is upstream's mermaid-elk-rewrite, vendored. */\nexport async function enhanceDiagrams(container: HTMLElement): Promise<number> {\n let mermaid: MermaidLike\n try { mermaid = await (deps.loadMermaid ?? defaultLoadMermaid)() } catch { return 0 }\n let replaced = 0\n const blocks = [...container.querySelectorAll<HTMLElement>('pre > code.lang-mermaid')]\n for (const code of blocks) {\n if (code.dataset.lksEnhanced === '1') continue\n code.dataset.lksEnhanced = '1'\n try {\n const { svg } = await mermaid.render(`lks-mermaid-${replaced}`, rewriteFlowchartToElk(code.textContent ?? ''))\n const wrap = document.createElement('div')\n wrap.className = 'lks-mermaid'\n wrap.innerHTML = svg\n code.parentElement!.replaceWith(wrap)\n replaced++\n } catch { /* keep the code block as-is */ }\n }\n return replaced\n}\n\n/** The full pass over one freshly-rendered container. Never throws. */\nexport async function enhanceRendered(container: HTMLElement): Promise<void> {\n try { await enhanceDiagrams(container) } catch { /* degrade */ }\n try { await enhanceCode(container) } catch { /* degrade */ }\n try { await enhanceMath(container) } catch { /* degrade */ }\n}\n\n","// Vendored from LookatStudy src/renderer/lib/cmap-elk-layout.ts (MIT License, https://github.com/Kaiji-Z/LookatStudy).\n// Unmodified except this provenance header (elkjs dynamic import target adapted to CDN).\n/**\n * cmap-elk-layout —— 概念图 v2 布局(ELK layered,复合分组 + 正交路由)。\n *\n * v0.12 径向布局的视觉评审结论:空白与拥挤并存/边交叉/无层级,且交叉消解要靠\n * 自研启发式。v0.21 换 elkjs(draw.io 新版同款 Eclipse Layout Kernel):\n * - 分组 = ELK 复合图(compound children):组容器盒 + 标题栏留白\n * - 连线 = elk.edgeRouting ORTHOGONAL:直角折线绕开盒子,结构性消灭\"线穿节点\"\n * - 分组来源:LLM show_concept_map 的可选 groups 字段;缺省/无效时客户端按\n * 邻接聚类兜底(clusterByAdjacency,确定性多源 BFS),不回头折腾 prompt\n *\n * 结构分层(全部可单测):\n * resolveGroups / clusterByAdjacency / cmNodeBox / buildElkGraph —— 纯函数\n * flattenElkResult —— 把 ELK 输出递归拍平成绝对坐标(复合嵌套只有一层)\n * layoutConceptMap —— 异步管线,elkjs 走动态 import(lazy chunk,同 mermaid)\n *\n * CJK 估宽/两行包裹沿用 v0.12 已验证的实现(estTextWidth/wrapLabel 原样迁移)。\n * 全链确定性:输入序驱动 + ELK 无随机源,verify-artifact-cmap-v2 断言两次运行\n * 逐坐标全等。\n */\n\n// ============================================================\n// 数据类型(与 artifact-harness ConceptMapData 对齐,groups 为 v2 增列)\n// ============================================================\n\nexport interface CmNode {\n id: string;\n label: string;\n}\nexport interface CmEdge {\n from: string;\n to: string;\n label?: string;\n}\nexport interface CmGroup {\n id: string;\n label: string;\n nodeIds: string[];\n}\n\n// ============================================================\n// 文本度量(v0.12 迁移,CJK 感知)\n// ============================================================\n\n/** 字符宽度估算:CJK 全宽,ASCII 约 0.58em,其余(BBOP…)同 ASCII。 */\nexport function estTextWidth(text: string, fontSize = 13): number {\n let w = 0;\n for (const ch of text) w += /[\\u2e80-\\u9fff\\uf900-\\ufaff\\uff00-\\uffef\\u3000-\\u303f]/.test(ch) ? fontSize : fontSize * 0.58;\n return w;\n}\n\nconst LINE_MAX_PX = 118;\n\n/** 标签包裹:≤2 行;优先自然断点(空格///-),CJK 连续按宽度一半硬拆;仍超宽截断。 */\nexport function wrapLabel(label: string): string[] {\n if (estTextWidth(label) <= LINE_MAX_PX) return [label];\n const breaks: number[] = [];\n for (let i = 0; i < label.length; i++) {\n if (/[\\s/\\-—·]/.test(label[i] ?? \"\")) breaks.push(i);\n }\n const mid = label.length / 2;\n let split = -1;\n let best = Infinity;\n for (const b of breaks) {\n const d = Math.abs(b + 1 - mid);\n if (d < best) {\n best = d;\n split = b;\n }\n }\n let l1: string;\n let l2: string;\n if (split >= 0) {\n l1 = label.slice(0, split + 1).trim();\n l2 = label.slice(split + 1).trim();\n } else {\n let acc = 0;\n let idx = 0;\n for (const ch of label) {\n acc += estTextWidth(ch);\n idx++;\n if (acc >= estTextWidth(label) / 2) break;\n }\n l1 = label.slice(0, idx);\n l2 = label.slice(idx);\n }\n if (estTextWidth(l1) <= LINE_MAX_PX && estTextWidth(l2) <= LINE_MAX_PX) return [l1, l2];\n const fit = (s: string) => {\n let out = \"\";\n for (const ch of s) {\n if (estTextWidth(out + ch + \"…\") > LINE_MAX_PX) break;\n out += ch;\n }\n return out + \"…\";\n };\n return [estTextWidth(l1) > LINE_MAX_PX ? fit(l1) : l1, estTextWidth(l2) > LINE_MAX_PX ? fit(l2) : l2];\n}\n\n/** 节点盒:hub(度数最大或 ≥4)字号/内边距更大;宽高由包裹后行宽决定。 */\nexport function cmNodeBox(label: string, hub: boolean): { width: number; height: number; lines: string[] } {\n const lines = wrapLabel(label);\n const fs = hub ? 14 : 13;\n const textW = Math.max(...lines.map((l) => estTextWidth(l, fs)));\n const padX = hub ? 30 : 24;\n const lh = 17;\n const padY = hub ? 18 : 14;\n return {\n width: Math.min(hub ? 200 : 180, Math.max(hub ? 92 : 76, textW + padX)),\n height: padY + lines.length * lh,\n lines,\n };\n}\n\n// ============================================================\n// 分组解析:LLM groups 优先,邻接聚类兜底\n// ============================================================\n\nexport interface ResolvedGroups {\n groups: CmGroup[];\n /** true = 来自客户端聚类(LLM 未给/无效) */\n fallback: boolean;\n}\n\n/**\n * 净化 LLM groups:丢弃指向不存在节点的 nodeIds、去重(先到先得)、\n * 剩余成员 < 2 的组丢弃;唯一覆盖全部节点的单组视为无效(一个巨容器没有信息量)。\n * 净化后为空 → clusterByAdjacency 兜底。\n */\nexport function resolveGroups(nodes: CmNode[], edges: CmEdge[], input?: CmGroup[] | null): ResolvedGroups {\n const ids = new Set(nodes.map((n) => n.id));\n if (Array.isArray(input) && input.length > 0) {\n const assigned = new Set<string>();\n const groups: CmGroup[] = [];\n for (const g of input) {\n if (!g || typeof g.id !== \"string\" || !g.id) continue;\n const nodeIds = Array.isArray(g.nodeIds)\n ? [...new Set(g.nodeIds.filter((x) => typeof x === \"string\" && ids.has(x) && !assigned.has(x)))]\n : [];\n if (nodeIds.length < 2) continue;\n nodeIds.forEach((x) => assigned.add(x));\n groups.push({ id: g.id, label: typeof g.label === \"string\" && g.label ? g.label : \"分组\", nodeIds });\n }\n if (groups.length > 0 && !(groups.length === 1 && assigned.size === nodes.length && nodes.length > 2)) {\n return { groups, fallback: false };\n }\n }\n return { groups: clusterByAdjacency(nodes, edges), fallback: true };\n}\n\n/**\n * 邻接聚类兜底(确定性):度数 top-k 节点做种子(k = clamp(ceil(n/4),1,3)),\n * 多源 BFS 就近归属(平手先出现的种子赢);孤立节点不归组(根层漂浮,不造假组)。\n * 组名 = 种子标签(≤5 字加\"相关\")。\n */\nexport function clusterByAdjacency(nodes: CmNode[], edges: CmEdge[]): CmGroup[] {\n if (nodes.length < 4) return []; // 太小不分组\n const index = new Map(nodes.map((n, i) => [n.id, i] as const));\n const adj = new Map<string, string[]>();\n for (const n of nodes) adj.set(n.id, []);\n const seenPair = new Set<string>();\n for (const e of edges) {\n if (!index.has(e.from) || !index.has(e.to) || e.from === e.to) continue;\n const key = [e.from, e.to].sort().join(\"→\");\n if (seenPair.has(key)) continue;\n seenPair.add(key);\n adj.get(e.from)!.push(e.to);\n adj.get(e.to)!.push(e.from);\n }\n const ranked = [...nodes].sort((a, b) => {\n const da = adj.get(a.id)!.length;\n const db = adj.get(b.id)!.length;\n return da !== db ? db - da : (index.get(a.id) ?? 0) - (index.get(b.id) ?? 0);\n });\n const k = Math.min(3, Math.max(1, Math.ceil(nodes.length / 4)));\n const seeds = ranked.slice(0, k).filter((s) => (adj.get(s.id)?.length ?? 0) > 0);\n if (seeds.length === 0) return [];\n const owner = new Map<string, number>(); // nodeId → seed 下标\n seeds.forEach((s, si) => owner.set(s.id, si));\n const queue = seeds.map((s) => s.id);\n while (queue.length) {\n const cur = queue.shift()!;\n const si = owner.get(cur)!;\n for (const nb of adj.get(cur) ?? []) {\n if (owner.has(nb)) continue;\n owner.set(nb, si);\n queue.push(nb);\n }\n }\n const groups: CmGroup[] = [];\n seeds.forEach((s, si) => {\n const nodeIds = nodes.filter((n) => owner.get(n.id) === si).map((n) => n.id);\n if (nodeIds.length < 2) return;\n groups.push({\n id: `grp-${s.id}`,\n label: s.label.length <= 5 ? `${s.label}相关` : s.label,\n nodeIds,\n });\n });\n return groups;\n}\n\n// ============================================================\n// ELK 图构建(纯函数,不触 elkjs —— 结构可脱离引擎直测)\n// ============================================================\n\n/** ELK JSON 的最小结构(只声明我们用到的字段;elkjs 原样吃任意附加字段)。 */\nexport interface ElkGraphJSON {\n id: string;\n layoutOptions?: Record<string, string>;\n width?: number;\n height?: number;\n x?: number;\n y?: number;\n children?: ElkGraphJSON[];\n edges?: {\n id: string;\n sources: string[];\n targets: string[];\n /** elkjs 在 hierarchyHandling=INCLUDE_CHILDREN 下把全部边挂到根,坐标属于 container 指向的容器 */\n container?: string;\n sections?: {\n startPoint: { x: number; y: number };\n endPoint: { x: number; y: number };\n bendPoints?: { x: number; y: number }[];\n }[];\n }[];\n}\n\n/** 组容器标题栏高度:ELK 组盒顶部 padding 要为标题留白。 */\nexport const GROUP_TITLE_PX = 34;\n\nexport function buildElkGraph(nodes: CmNode[], edges: CmEdge[], groups: CmGroup[]): ElkGraphJSON {\n const ids = new Set(nodes.map((n) => n.id));\n const degree = new Map<string, number>();\n for (const n of nodes) degree.set(n.id, 0);\n const seenPair = new Set<string>();\n const drawEdges: CmEdge[] = [];\n for (const e of edges) {\n if (!ids.has(e.from) || !ids.has(e.to) || e.from === e.to) continue;\n const key = [e.from, e.to].sort().join(\"→\");\n if (seenPair.has(key)) continue;\n seenPair.add(key);\n drawEdges.push(e);\n degree.set(e.from, degree.get(e.from)! + 1);\n degree.set(e.to, degree.get(e.to)! + 1);\n }\n let hubId = nodes[0]?.id ?? \"\";\n for (const n of nodes) if ((degree.get(n.id) ?? 0) > (degree.get(hubId) ?? 0)) hubId = n.id;\n\n const inGroup = new Map<string, string>(); // nodeId → groupId\n for (const g of groups) for (const nid of g.nodeIds) if (ids.has(nid) && !inGroup.has(nid)) inGroup.set(nid, g.id);\n\n const nodeChild = (n: CmNode): ElkGraphJSON => {\n const box = cmNodeBox(n.label, n.id === hubId || (degree.get(n.id) ?? 0) >= 4);\n return { id: n.id, width: Math.ceil(box.width), height: Math.ceil(box.height) };\n };\n\n const children: ElkGraphJSON[] = [];\n for (const g of groups) {\n const members = g.nodeIds.filter((nid) => inGroup.get(nid) === g.id && ids.has(nid));\n if (members.length === 0) continue;\n children.push({\n id: `g:${g.id}`,\n layoutOptions: { \"elk.padding\": `[top=${GROUP_TITLE_PX},left=14,bottom=12,right=14]` },\n children: members.map((nid) => nodeChild(nodes.find((n) => n.id === nid)!)),\n });\n }\n for (const n of nodes) {\n if (!inGroup.has(n.id)) children.push(nodeChild(n));\n }\n\n return {\n id: \"root\",\n layoutOptions: {\n \"elk.algorithm\": \"layered\",\n \"elk.direction\": \"DOWN\",\n \"elk.edgeRouting\": \"ORTHOGONAL\",\n // 复合图关键开关:layered 默认不处理嵌套(跨组边不路由、无 sections),\n // INCLUDE_CHILDREN 让整个复合图一次分层,跨组边才有正交折线\n \"elk.hierarchyHandling\": \"INCLUDE_CHILDREN\",\n \"elk.spacing.nodeNode\": \"34\",\n \"elk.spacing.edgeNode\": \"26\",\n \"elk.spacing.componentComponent\": \"48\",\n \"elk.layered.spacing.nodeNodeBetweenLayers\": \"56\",\n },\n children,\n edges: drawEdges.map((e, i) => ({ id: `e${i}`, sources: [e.from], targets: [e.to] })),\n };\n}\n\n// ============================================================\n// ELK 输出拍平(复合嵌套一层:root → 组盒 → 节点盒)\n// ============================================================\n\nexport interface CmapNodeGeo {\n id: string;\n x: number; // 左上角(绝对)\n y: number;\n w: number;\n h: number;\n lines: string[];\n hub: boolean;\n groupId: string | null;\n /** 颜色档 = 所属组序号(0-4 循环);无组 = -1(中性) */\n colorIdx: number;\n}\nexport interface CmapGroupGeo {\n id: string;\n label: string;\n x: number;\n y: number;\n w: number;\n h: number;\n colorIdx: number;\n}\nexport interface CmapEdgeGeo {\n from: string;\n to: string;\n label?: string;\n pts: { x: number; y: number }[];\n /** 边标签锚点(折线弧长中点);无标签为 null */\n labelPt: { x: number; y: number } | null;\n}\nexport interface CmapLayout {\n width: number;\n height: number;\n nodes: CmapNodeGeo[];\n groups: CmapGroupGeo[];\n edges: CmapEdgeGeo[];\n hubId: string;\n /** true = 分组来自客户端兜底(调试/未来 UI 提示用) */\n groupsFallback: boolean;\n}\n\n/** 折线弧长中点(标签锚点)。 */\nfunction polylineMidpoint(pts: { x: number; y: number }[]): { x: number; y: number } {\n if (pts.length === 0) return { x: 0, y: 0 };\n if (pts.length === 1) return pts[0];\n const segLens = [];\n let total = 0;\n for (let i = 1; i < pts.length; i++) {\n const l = Math.hypot(pts[i].x - pts[i - 1].x, pts[i].y - pts[i - 1].y);\n segLens.push(l);\n total += l;\n }\n let remain = total / 2;\n for (let i = 0; i < segLens.length; i++) {\n if (remain <= segLens[i] || i === segLens.length - 1) {\n const t = segLens[i] === 0 ? 0 : remain / segLens[i];\n return {\n x: pts[i].x + (pts[i + 1].x - pts[i].x) * t,\n y: pts[i].y + (pts[i + 1].y - pts[i].y) * t,\n };\n }\n remain -= segLens[i];\n }\n return pts[pts.length - 1];\n}\n\nexport function flattenElkResult(\n elkOut: ElkGraphJSON,\n nodes: CmNode[],\n edges: CmEdge[],\n groups: CmGroup[],\n groupsFallback: boolean,\n hubId: string,\n): CmapLayout {\n const nodesById = new Map(nodes.map((n) => [n.id, n] as const));\n const groupColor = new Map<string, number>();\n groups.forEach((g, i) => groupColor.set(g.id, i % 5));\n const groupById = new Map(groups.map((g) => [g.id, g] as const));\n const memberGroup = new Map<string, string>();\n for (const g of groups) for (const nid of g.nodeIds) if (!memberGroup.has(nid)) memberGroup.set(nid, g.id);\n\n const outNodes: CmapNodeGeo[] = [];\n const outGroups: CmapGroupGeo[] = [];\n const outEdges: CmapEdgeGeo[] = [];\n\n // elkjs 把边统一挂到根数组,坐标属于 e.container 指向的容器(INCLUDE_CHILDREN 下)——\n // 必须先收集\"容器 id → 绝对偏移\"表,再按 container 加偏移;按树位置加偏移会让组内边整体错位。\n const containerOff = new Map<string, { x: number; y: number }>([[\"root\", { x: 0, y: 0 }]]);\n const rawEdges: { e: NonNullable<ElkGraphJSON[\"edges\"]>[number]; fallback: { x: number; y: number } }[] = [];\n\n const walk = (elk: ElkGraphJSON, offX: number, offY: number, containerId: string | null) => {\n for (const child of elk.children ?? []) {\n const cx = offX + (child.x ?? 0);\n const cy = offY + (child.y ?? 0);\n if (child.children?.length) {\n const gid = child.id.startsWith(\"g:\") ? child.id.slice(2) : child.id;\n const meta = groupById.get(gid);\n outGroups.push({\n id: gid,\n label: meta?.label ?? \"分组\",\n x: cx,\n y: cy,\n w: child.width ?? 0,\n h: child.height ?? 0,\n colorIdx: groupColor.get(gid) ?? 0,\n });\n containerOff.set(child.id, { x: cx, y: cy });\n walk(child, cx, cy, gid);\n } else {\n const n = nodesById.get(child.id);\n if (!n) continue;\n const box = cmNodeBox(n.label, n.id === hubId);\n const gid = memberGroup.get(n.id) ?? null;\n outNodes.push({\n id: n.id,\n x: cx,\n y: cy,\n w: child.width ?? Math.ceil(box.width),\n h: child.height ?? Math.ceil(box.height),\n lines: box.lines,\n hub: n.id === hubId,\n groupId: containerId,\n colorIdx: gid != null ? groupColor.get(gid) ?? 0 : -1,\n });\n }\n }\n for (const e of elk.edges ?? []) {\n // 记录现场偏移兜底:理论上边也可挂在 LCA 自己的 edges 里(无 container 字段)\n rawEdges.push({ e, fallback: { x: offX, y: offY } });\n }\n };\n walk(elkOut, 0, 0, null);\n\n for (const { e, fallback } of rawEdges) {\n const sec = e.sections?.[0];\n if (!sec) continue;\n const off = (e.container != null ? containerOff.get(e.container) : undefined) ?? fallback;\n const pts = [sec.startPoint, ...(sec.bendPoints ?? []), sec.endPoint].map((p) => ({\n x: p.x + off.x,\n y: p.y + off.y,\n }));\n const idx = Number(e.id.slice(1));\n const src = edges[idx];\n if (!src) continue;\n outEdges.push({ from: src.from, to: src.to, label: src.label, pts, labelPt: src.label ? polylineMidpoint(pts) : null });\n }\n\n return {\n width: Math.ceil(elkOut.width ?? 0),\n height: Math.ceil(elkOut.height ?? 0),\n nodes: outNodes,\n groups: outGroups,\n edges: outEdges,\n hubId,\n groupsFallback,\n };\n}\n\n// ============================================================\n// 异步管线:elkjs 动态 import(独立 lazy chunk)\n// ============================================================\n\ntype ElkInstance = { layout: (graph: ElkGraphJSON) => Promise<ElkGraphJSON> };\nlet elkPromise: Promise<ElkInstance> | null = null;\n\n/** elkjs 单例:elk.bundled.js 主线程版(无 Worker,打包器友好,React Flow 生态同款)。 */\nfunction loadElk(): Promise<ElkInstance> {\n elkPromise ??= import(/* @vite-ignore */ \"https://esm.sh/elkjs@0.12/lib/elk.bundled.js\").then((mod: { default: unknown }) => {\n // interop 双环境:CJS(Node/tsx)default 在 mod.default;打包后 ESM 可能整包即构造器\n const m = mod as unknown as Record<string, unknown>;\n const Ctor = (m.default ?? m) as new () => ElkInstance;\n return new Ctor();\n });\n return elkPromise;\n}\n\n/** 概念图 → 分组解析 → ELK 布局 → 绝对坐标几何。输入相同则输出逐字段全等。 */\nexport async function layoutConceptMap(\n nodes: CmNode[],\n edges: CmEdge[],\n groups?: CmGroup[] | null,\n): Promise<CmapLayout> {\n const resolved = resolveGroups(nodes, edges, groups);\n const graph = buildElkGraph(nodes, edges, resolved.groups);\n const hubId = computeHubId(nodes, edges);\n const elk = await loadElk();\n const out = await elk.layout(graph);\n return flattenElkResult(out, nodes, edges, resolved.groups, resolved.fallback, hubId);\n}\n\nfunction computeHubId(nodes: CmNode[], edges: CmEdge[]): string {\n const ids = new Set(nodes.map((n) => n.id));\n const degree = new Map<string, number>();\n for (const n of nodes) degree.set(n.id, 0);\n const seen = new Set<string>();\n for (const e of edges) {\n if (!ids.has(e.from) || !ids.has(e.to) || e.from === e.to) continue;\n const key = [e.from, e.to].sort().join(\"→\");\n if (seen.has(key)) continue;\n seen.add(key);\n degree.set(e.from, degree.get(e.from)! + 1);\n degree.set(e.to, degree.get(e.to)! + 1);\n }\n let hubId = nodes[0]?.id ?? \"\";\n for (const n of nodes) if ((degree.get(n.id) ?? 0) > (degree.get(hubId) ?? 0)) hubId = n.id;\n return hubId;\n}\n","/**\n * The study tab's locale surface: one `lookatstudy` namespace registered with\n * the framework locale service (zh + en, key sets identical — the service\n * enforces balance at register time and this module's tests re-assert it).\n * `makeT` is the pure lookup (own chain: locale → zh → key, `{name}` template\n * interpolation — the same semantics as the service's translate) so unit\n * tests and non-slot code can translate without a live context;\n * `bindStudyT` prefers the service's stable `bind()` when present.\n * @module dsh-plugin-lookatstudy/client/locale\n */\n\n/** Flat key → template string, `{name}` placeholders (LocaleDict shape). */\nexport type StudyDict = Record<string, string>\n\n/** Simplified Chinese dictionary (also the fallback locale). */\nexport const ZH: StudyDict = {\n 'tab.label': '学习',\n 'col.rail': '课程',\n 'col.tutor': '导师',\n 'col.bb': '黑板',\n 'pane.rail': '课程',\n 'pane.tutor': '导师',\n 'pane.bb': '黑板',\n 'viewtab.teach': '讲解',\n 'viewtab.cmap': '概念图',\n 'viewtab.cmap.title': '本课概念之间的关系图',\n 'loading': '加载中…',\n 'start.title': '一键准备学习:建立学习工作区、开启会话并让导师就位',\n 'start.go': '开始学习',\n 'start.enter': '进入学习',\n 'start.busy': '正在准备学习区…',\n 'soul.direct': '直讲',\n 'soul.direct.hint': '精讲:先讲清楚,再确认懂没懂',\n 'soul.guide': '引导',\n 'soul.guide.hint': '引导:让你自己往前推一步,导师递台阶',\n 'soul.practice': '实战',\n 'soul.practice.hint': '实战:在真实世界的乱问题里学',\n 'zone.understand': '🧠 理解区 — 知识结构',\n 'zone.record': '📝 记录区 — 我的话',\n 'zone.practice': '✍️ 练习区 — 答题日志',\n 'rail.empty.title': '暂无课程',\n 'rail.empty.hint': '粘贴 markdown → 说「导入为课程」',\n 'rail.empty.hint2': '本地文件夹 → 说「导入 D:/path/to/folder」',\n 'rail.empty.placeholder': 'GitHub 仓库链接,如 microsoft/AI-For-Beginners',\n 'rail.empty.button': '导入',\n 'composer.send': '发送',\n 'composer.busy': '导师正在回复…',\n 'rail.empty.demo': '导入示例课程',\n 'rail.mastered': '毕业 {mastered}/{total} 课',\n 'rail.avg': '平均掌握度 {pct}%',\n 'rail.avg.none': '尚无掌握度数据',\n 'rail.search': '搜索课时…(多关键词空格分隔)',\n 'rail.locate.title': '在课程树中定位当前焦点课时(自动展开所在章节)',\n 'rail.locate': '回到当前课时',\n 'rail.due': '待复习 {count}',\n 'rail.due.over': '超{days}天',\n 'rail.due.start': '开始复习',\n 'rail.due.tag': '这课时的复习今天到期(SM-2)',\n 'rail.delete': '删除本课程',\n 'rail.delete.confirm': '确认删除?',\n 'rail.delete.title.confirm': '再点一次确认删除(含全部进度与笔记)。反悔前可先在设置页备份状态文件',\n 'note.delete': '删除本条笔记',\n 'note.delete.confirm': '确认删除?',\n 'read.play': '朗读本课',\n 'read.stop': '停止朗读',\n 'read.pause': '暂停朗读',\n 'read.resume': '继续朗读',\n 'read.engine.system': '网络合成不可用,已切换为系统语音',\n 'read.unavailable': '朗读不可用',\n 'rail.section.collapse': '折叠本章节',\n 'rail.section.expand': '展开本章节({count} 课时)',\n 'rail.section.count': '{count} 课',\n 'rail.lesson.opening': '正在打开课时会话…',\n 'rail.lesson.openHint': '(点击将打开本课专属会话)',\n 'rail.import.toggle': '导入课程',\n 'rail.import.close': '收起导入',\n 'tag.weak': '{count} 个薄弱知识点,测验会优先考察',\n 'tag.friction': '{count} 次卡点记录(你说\"不懂\"时导师记下的)',\n 'tag.mastery': '课时掌握度 {pct}%(取最薄弱知识点)',\n 'tag.mastery.short': '课时掌握度 = 最薄弱知识点的掌握度',\n 'status.exam': '章节测验:本节全部课时掌握度 ≥50% 后开放',\n 'status.mastered': '已毕业:掌握度 ≥90%(或你接受了掌握提案)',\n 'status.in_progress': '学习中:已开课,掌握度从 50% 起步',\n 'status.available': '可开始:已解锁,尚未学习',\n 'status.locked': '未解锁:先完成前面的课时',\n 'chip.unattributed': '未归因',\n 'chip.correct': '✓ 答对 · {concept}',\n 'chip.wrong': '✗ 答错 · {concept}',\n 'row.thinking': '导师思考中…',\n 'row.thinking.title': '导师正在推理,回复马上就来',\n 'quiz.title': '点击选项作答,也可以直接打字回答',\n 'quiz.answer': '选 {letter}:{text}',\n 'tutor.dormant': '学习模式已关闭 — 点右上「▶ 开始学习」开启导师,按钮恢复可用。',\n 'tutor.empty': '对话会出现在这里',\n 'tutor.empty.hint': '在下方输入框和导师说话',\n 'proposal.text': '导师提议你已掌握「{lesson}」:{rationale}',\n 'proposal.accept': '接受',\n 'proposal.decline': '再练练',\n 'bb.empty': '黑板还空着',\n 'bb.empty.hint': '在左侧课程树选择一课',\n 'bb.mastery': '掌握度 {pct}%',\n 'cmap.legend': '琥珀色 = 薄弱知识点(掌握度 <70%)',\n 'cmap.node.pct': '掌握度 {pct}%',\n 'bb.strategy': '本课学法',\n 'bb.fallback.cmap.empty': '概念图需要先由导师定义本课概念',\n 'bb.fallback.cmap': '概念图渲染不可用(布局引擎加载失败) — 已回退讲解视图',\n 'bb.notes': '笔记',\n 'bb.notes.empty': '这一课还没有笔记',\n 'tv.exam.stars': '本次 {n}★(历史最佳 {best}★)',\n 'tv.exam.fail': '这轮没拿到星——错题就是下一课的地图,从每题的解析看起。',\n 'active.off.title': '注册 study 工具并载入导师人格,之后普通对话和现在一样',\n 'active.on.title': '注销 study 工具与导师人格;学习进度保留,可随时重新开启',\n 'active.off': '▶ 开始学习',\n 'active.on': '⏻ 退出学习模式',\n 'prompt.kickoff': '开始学习:查看我的学习状态并打开当前焦点课时。如果我还没有课程,推荐我导入示例课程(AI-For-Beginners)并说明怎么开始。回复最后请提醒我点上方「学习」页签进入学习界面。',\n 'settings.nav': '学习',\n 'settings.mode': '教学风格',\n 'settings.mode.hint': '导师的讲解风格;也可在学习页随时切换',\n 'settings.studyMode': '学习模式',\n 'settings.voice': '朗读语音',\n 'settings.voice.hint': '「朗读本课」使用的神经网络声音(浏览器本地偏好)',\n 'voice.xiaoxiao': '晓晓 · 中文女声',\n 'voice.yunxi': '云希 · 中文男声',\n 'voice.yunyang': '云扬 · 中文男声(新闻)',\n 'voice.xiaoyi': '晓伊 · 中文女声(轻快)',\n 'voice.aria': 'Aria · English female',\n 'voice.guy': 'Guy · English male',\n 'settings.studyMode.hint': '开启后注册 study_* 工具并载入导师人格;进度与笔记始终保留',\n 'settings.on': '状态:已开启',\n 'settings.off': '状态:已关闭',\n 'settings.turnOn': '开启',\n 'settings.turnOff': '关闭',\n 'settings.stats': '学习统计',\n 'settings.stats.courses': '课程 {count} 个',\n 'settings.stats.xp': 'XP {xp} · Lv{level}({pct}%)',\n 'settings.stats.today': '今日 {xp}/{goal}',\n 'settings.stats.streak': '连续 {days} 天(最长 {best},剩冻结 {freeze})',\n 'settings.about': '关于',\n 'settings.about.hint': '当前运行的构建版本,点击查看该版更新内容。',\n 'settings.stateFile': '状态文件',\n 'settings.stateFile.hint': '学习进度存放于本机 JSON,换机可迁移',\n 'dock.title': '学习状态:待复习 {due} · 连续 {streak} 天 · Lv{level} — 点上方「学习」页签进入',\n 'dock.due': '{count}',\n 'dock.streak': '{days}天',\n 'dock.lv': 'Lv{level}',\n 'prompt.import': '导入课程:用 study_import_github 抓取 {url}',\n 'prompt.lesson': '学习「{title}」:用 study_lesson 打开这一课开始学习。',\n 'prompt.exam': '开始「{section}」的章节测验:按本节课时出题,答完逐题判分',\n 'prompt.review': '开始今天的复习,从最到期的课时开始',\n 'prompt.proposal.accept': '接受提案 {id} —— 确认标记这课为已掌握',\n 'prompt.proposal.decline': '拒绝提案 {id} —— 我想再练练',\n}\n\n/** English dictionary — key set must equal {@link ZH}'s (test-asserted). */\nexport const EN: StudyDict = {\n 'tab.label': 'Study',\n 'col.rail': 'Courses',\n 'col.tutor': 'Tutor',\n 'col.bb': 'Blackboard',\n 'pane.rail': 'Courses',\n 'pane.tutor': 'Tutor',\n 'pane.bb': 'Board',\n 'viewtab.teach': 'Teach',\n 'viewtab.cmap': 'Concepts',\n 'viewtab.cmap.title': 'The lesson\\'s concepts relate',\n 'loading': 'Loading…',\n 'start.title': 'One click to study: workspace + session + tutor, all set up',\n 'start.go': 'Start learning',\n 'start.enter': 'Enter study',\n 'start.busy': 'Preparing the study area…',\n 'soul.direct': 'Direct',\n 'soul.direct.hint': 'Explain first, then check understanding',\n 'soul.guide': 'Guide',\n 'soul.guide.hint': 'You push one step forward; the tutor hands you the stairs',\n 'soul.practice': 'Practice',\n 'soul.practice.hint': 'Learn inside real, messy problems',\n 'zone.understand': '🧠 Understand — knowledge structure',\n 'zone.record': '📝 Record — in my own words',\n 'zone.practice': '✍️ Practice — answer log',\n 'rail.empty.title': 'No courses yet',\n 'rail.empty.hint': 'Paste markdown → say \"import as a course\"',\n 'rail.empty.hint2': 'Local folder → say \"import D:/path/to/folder\"',\n 'rail.empty.placeholder': 'GitHub repo link, e.g. microsoft/AI-For-Beginners',\n 'rail.empty.button': 'Import',\n 'composer.send': 'Send',\n 'composer.busy': 'The tutor is replying…',\n 'rail.empty.demo': 'Import the demo course',\n 'rail.mastered': '{mastered}/{total} lessons graduated',\n 'rail.avg': 'avg mastery {pct}%',\n 'rail.avg.none': 'no mastery data yet',\n 'rail.search': 'Search lessons… (space-separated keywords)',\n 'rail.locate.title': 'Locate the current focus lesson in the tree (auto-expands its section)',\n 'rail.locate': 'Back to current lesson',\n 'rail.due': '{count} due',\n 'rail.due.over': '{days}d overdue',\n 'rail.due.start': 'Start reviewing',\n 'rail.due.tag': 'this lesson\\'s review is due today (SM-2)',\n 'rail.delete': 'Delete this course',\n 'rail.delete.confirm': 'Delete?',\n 'rail.delete.title.confirm': 'Click again to confirm (all progress and notes included). Back up via the state file in Settings first',\n 'note.delete': 'Delete this note',\n 'note.delete.confirm': 'Delete?',\n 'read.play': 'Read aloud',\n 'read.stop': 'Stop reading',\n 'read.pause': 'Pause reading',\n 'read.resume': 'Resume reading',\n 'read.engine.system': 'Network synthesis unavailable — switched to the system voice',\n 'read.unavailable': 'Read-aloud unavailable',\n 'rail.section.collapse': 'Collapse this section',\n 'rail.section.expand': 'Expand this section ({count} lessons)',\n 'rail.section.count': '{count} lessons',\n 'rail.lesson.opening': 'Opening the lesson session…',\n 'rail.lesson.openHint': ' (opens this lesson\\'s own session)',\n 'rail.import.toggle': 'Import a course',\n 'rail.import.close': 'Collapse import',\n 'tag.weak': '{count} weak concepts — quizzes target them first',\n 'tag.friction': '{count} friction marks (logged when you said you were stuck)',\n 'tag.mastery': 'lesson mastery {pct}% (the weakest concept)',\n 'tag.mastery.short': 'lesson mastery = the weakest concept\\'s mastery',\n 'status.exam': 'Section exam: opens when every lesson in the section reaches ≥50% mastery',\n 'status.mastered': 'Graduated: mastery ≥90% (or you accepted a mastery proposal)',\n 'status.in_progress': 'Learning: opened, mastery starts at 50%',\n 'status.available': 'Ready: unlocked, not started',\n 'status.locked': 'Locked: finish the preceding lessons first',\n 'chip.unattributed': 'unattributed',\n 'chip.correct': '✓ correct · {concept}',\n 'chip.wrong': '✗ wrong · {concept}',\n 'row.thinking': 'tutor is thinking…',\n 'row.thinking.title': 'The tutor is reasoning; the reply is coming',\n 'quiz.title': 'Click an option to answer, or just type',\n 'quiz.answer': 'Choose {letter}: {text}',\n 'tutor.dormant': 'Study mode is off — click \"▶ Start learning\" above to wake the tutor and re-enable these controls.',\n 'tutor.empty': 'The conversation will appear here',\n 'tutor.empty.hint': 'Talk to the tutor in the input below',\n 'proposal.text': 'The tutor proposes you have mastered \"{lesson}\": {rationale}',\n 'proposal.accept': 'Accept',\n 'proposal.decline': 'Keep practicing',\n 'bb.empty': 'The blackboard is empty',\n 'bb.empty.hint': 'Pick a lesson in the course tree on the left',\n 'bb.mastery': 'mastery {pct}%',\n 'cmap.legend': 'Amber = weak concept (mastery < 70%)',\n 'cmap.node.pct': 'mastery {pct}%',\n 'bb.strategy': 'Approach',\n 'bb.fallback.cmap.empty': 'The concept map needs the tutor to define this lesson\\'s concepts first',\n 'bb.fallback.cmap': 'Concept map unavailable (layout engine failed to load) — fell back to the teach view',\n 'bb.notes': 'Notes',\n 'bb.notes.empty': 'No notes for this lesson yet',\n 'tv.exam.stars': 'This attempt {n}★ (best {best}★)',\n 'tv.exam.fail': 'No stars this round — the wrong answers are the map for the next one; start with their explanations.',\n 'active.off.title': 'Register the study tools and load the tutor persona; plain chats stay as they are',\n 'active.on.title': 'Retire the study tools and tutor persona; progress is kept and you can re-enable anytime',\n 'active.off': '▶ Start learning',\n 'active.on': '⏻ Exit study mode',\n 'prompt.kickoff': 'Start learning: check my study state and open the current focus lesson. If I have no course yet, suggest importing the demo course (AI-For-Beginners) and explain how to begin. Remind me at the end to open the \"Study\" tab above to enter the study UI.',\n 'settings.nav': 'Study',\n 'settings.mode': 'Teaching style',\n 'settings.mode.hint': 'The tutor\\'s explanation style; switchable anytime in the study tab too',\n 'settings.studyMode': 'Study mode',\n 'settings.voice': 'Read-aloud voice',\n 'settings.voice.hint': 'The neural voice used by 朗读本课 (a browser-local preference)',\n 'voice.xiaoxiao': 'Xiaoxiao · Chinese female',\n 'voice.yunxi': 'Yunxi · Chinese male',\n 'voice.yunyang': 'Yunyang · Chinese male (news)',\n 'voice.xiaoyi': 'Xiaoyi · Chinese female (lively)',\n 'voice.aria': 'Aria · English female',\n 'voice.guy': 'Guy · English male',\n 'settings.studyMode.hint': 'On registers the study_* tools and the tutor persona; progress and notes are always kept',\n 'settings.on': 'Status: on',\n 'settings.off': 'Status: off',\n 'settings.turnOn': 'Turn on',\n 'settings.turnOff': 'Turn off',\n 'settings.stats': 'Study stats',\n 'settings.stats.courses': '{count} courses',\n 'settings.stats.xp': 'XP {xp} · Lv{level} ({pct}%)',\n 'settings.stats.today': 'Today {xp}/{goal}',\n 'settings.stats.streak': 'Streak {days}d (best {best}, {freeze} freeze left)',\n 'settings.about': 'About',\n 'settings.about.hint': 'The version of the running build — click to see its release notes.',\n 'settings.stateFile': 'State file',\n 'settings.stateFile.hint': 'Your progress lives in a local JSON file — copy it to migrate machines',\n 'dock.title': 'Study status: {due} due · {streak}d streak · Lv{level} — open the Study tab above to continue',\n 'dock.due': '{count}',\n 'dock.streak': '{days}d',\n 'dock.lv': 'Lv{level}',\n 'prompt.import': 'Import a course: fetch {url} with study_import_github',\n 'prompt.lesson': 'Study \"{title}\": open this lesson with study_lesson and start teaching.',\n 'prompt.exam': 'Start the section exam for \"{section}\": quiz me on this section\\'s lessons and grade each answer',\n 'prompt.review': 'Start today\\'s review, from the most overdue lesson first',\n 'prompt.proposal.accept': 'Accept proposal {id} — confirm marking this lesson as mastered',\n 'prompt.proposal.decline': 'Reject proposal {id} — I want to practice more',\n}\n\n/** Both dictionaries under their locale ids (the service's register shape). */\nexport const STUDY_DICTS = { zh: ZH, en: EN }\n\n/** The namespace id this plugin registers with the framework locale service. */\nexport const STUDY_NS = 'lookatstudy'\n\n/** Translate function for the study namespace. */\nexport type StudyT = (key: string, params?: Record<string, unknown>) => string\n\n/**\n * Pure translator with the service's semantics: own-locale dict → zh\n * fallback → the key itself, `{name}` interpolation left untouched when the\n * param is missing.\n * @param locale - active locale id ('zh'/'en'; unknown ids fall back to zh).\n * @returns a stable-shaped translate function.\n */\nexport function makeT(locale: string): StudyT {\n const dict = locale === 'en' ? EN : ZH\n return (key, params) => {\n const template = dict[key] ?? ZH[key] ?? key\n if (params === undefined) return template\n return template.replace(/\\{(\\w+)\\}/g, (match, name: string) =>\n name in params ? String(params[name]) : match)\n }\n}\n\n/** Minimal shape of the framework locale service this plugin consumes. */\nexport interface LocaleServiceFace {\n register(ns: string, dicts: Record<string, StudyDict>): () => void\n bind(ns: string): StudyT\n subscribe(fn: () => void): () => void\n}\n\n/**\n * Register the namespace with the framework locale service.\n * @param locale - the client locale service when present.\n * @returns the registration disposer (a no-op when the service is absent or\n * a hostile re-registration was refused).\n */\nexport function registerStudyLocale(locale?: LocaleServiceFace): () => void {\n if (locale === undefined) return () => {}\n try {\n return locale.register(STUDY_NS, STUDY_DICTS)\n } catch {\n // A namespace/locale already claimed by someone else must not break the\n // tab — fall back to the pure translator everywhere.\n return () => {}\n }\n}\n\n/**\n * The live translator: the service's stable `bind` when present (reads the\n * active locale at call time, so a locale-change re-render picks new\n * strings), the pure zh translator otherwise.\n * @param locale - the client locale service when present.\n */\nexport function studyTranslator(locale?: LocaleServiceFace): StudyT {\n return locale === undefined ? makeT('zh') : locale.bind(STUDY_NS)\n}\n\n/** The module's active translator — zh until the view factory installs the live bind. */\nlet activeT: StudyT = makeT('zh')\n\n/**\n * Install the client-wide translator (called once at client apply, before\n * any component renders).\n */\nexport function setStudyTranslator(t: StudyT): void {\n activeT = t\n}\n\n/**\n * Translate through the active translator — the import every client module\n * uses for user-visible copy. Pure helpers take an optional explicit `t`\n * defaulting to the current active translator, evaluated per call.\n */\nexport function tr(key: string, params?: Record<string, unknown>): string {\n return activeT(key, params)\n}\n","/**\n * Blackboard diagram views (SPEC Phase 2): the lesson concept map (ELK layered\n * layout via the vendored cmap-elk-layout + CDN elkjs, draw.io-flavored SVG\n * skin rendered plugin-side). Degrades honestly: any CDN/ELK failure leaves a\n * one-line notice instead of a broken pane.\n *\n * The lesson mind map view was RETIRED with upstream v0.26.0 (2026-09-01\n * port): body text that has headings already shows its structure, text without\n * headings yields a first-line-truncated node graph nobody can read — both\n * lose — and this concept map already covers the semantic-structure need at\n * higher quality. Its three CDN imports went with it.\n */\n\nimport { layoutConceptMap, type CmNode, type CmEdge } from '../vendor/cmap-elk-layout.ts'\nimport { tr } from './locale.ts'\n\n\n/** Lesson concept map: the lesson node + its concepts (weak ones accented),\n * laid out by the vendored ELK pipeline, skinned draw.io-style (plugin-original,\n * upstream's skin is coupled to its token pipeline). Mastery shades the nodes. */\nexport async function renderLessonConceptMap(\n container: HTMLElement,\n lessonTitle: string,\n concepts: ReadonlyArray<{ title: string; masteryPct: number }>,\n): Promise<void> {\n const nodes: CmNode[] = [{ id: 'lesson', label: lessonTitle }, ...concepts.map(c => ({ id: `c-${c.title}`, label: c.title }))]\n const edges: CmEdge[] = concepts.map(c => ({ from: 'lesson', to: `c-${c.title}` }))\n const layout = await layoutConceptMap(nodes, edges)\n const masteryBy = new Map(concepts.map(c => [`c-${c.title}`, c.masteryPct]))\n const ns = 'http://www.w3.org/2000/svg'\n const svg = document.createElementNS(ns, 'svg')\n svg.setAttribute('width', String(Math.max(320, layout.width)))\n svg.setAttribute('height', String(Math.max(200, layout.height)))\n svg.setAttribute('viewBox', `0 0 ${layout.width} ${layout.height}`)\n svg.setAttribute('style', 'max-width:100%;background:var(--dsw-alias-bg-layer-1, #fff);border:1px solid var(--dsw-alias-border-l2, #d0d7de);border-radius:8px')\n for (const e of layout.edges) {\n const path = document.createElementNS(ns, 'path')\n path.setAttribute('d', e.pts.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`).join(' '))\n path.setAttribute('fill', 'none')\n path.setAttribute('stroke', 'var(--dsw-alias-border-l2, #8b949e)')\n path.setAttribute('stroke-width', '1.5')\n svg.append(path)\n }\n for (const n of layout.nodes) {\n const g = document.createElementNS(ns, 'g')\n g.setAttribute('transform', `translate(${n.x},${n.y})`)\n const rect = document.createElementNS(ns, 'rect')\n const isLesson = n.id === 'lesson'\n const mastery = masteryBy.get(n.id)\n rect.setAttribute('width', String(n.w))\n rect.setAttribute('height', String(n.h))\n rect.setAttribute('rx', '6')\n rect.setAttribute('fill', mastery !== undefined && mastery < 70 ? 'var(--dsw-alias-state-warn-tertiary, #fef5e7)' : 'var(--dsw-alias-bg-layer-2, #f6f8fa)')\n rect.setAttribute('stroke', isLesson ? 'var(--dsw-alias-state-business-primary, #0969da)' : 'var(--dsw-alias-border-l2, #8b949e)')\n rect.setAttribute('stroke-width', isLesson ? '2' : '1')\n g.append(rect)\n n.lines.forEach((line, li) => {\n const label = document.createElementNS(ns, 'text')\n label.setAttribute('x', String(n.w / 2))\n label.setAttribute('y', String(n.h / 2 - (n.lines.length - 1) * 7 + li * 14 + 4))\n label.setAttribute('text-anchor', 'middle')\n label.setAttribute('font-size', isLesson ? '13' : '12')\n label.setAttribute('font-weight', isLesson ? '600' : '400')\n label.setAttribute('fill', 'var(--dsw-alias-label-primary, #1f2328)')\n label.textContent = line\n g.append(label)\n })\n const title = document.createElementNS(ns, 'title')\n const label = n.lines.join(' ')\n title.textContent = isLesson || mastery === undefined ? label : `${label} · ${tr('cmap.node.pct', { pct: mastery })}`\n g.append(title)\n svg.append(g)\n }\n container.append(svg)\n const legend = document.createElement('span')\n legend.className = 'lks-cmap-legend'\n const swatch = document.createElement('i')\n legend.append(swatch, document.createTextNode(tr('cmap.legend')))\n container.append(legend)\n}\n","/**\n * Vendored from LookatStudy shared/speech-text.ts (MIT License,\n * https://github.com/Kaiji-Z/LookatStudy), upstream v0.28.0 — includes the\n * v11.5 whole-display-group avoidance fix era. Unmodified except this\n * provenance header.\n *\n * speech-text —— TTS 朗读文本处理(纯函数,渲染层/主进程共用)。\n *\n * normalizeSpeechText: markdown → 可朗读纯文本。代码不读(围栏/行内整体移除),\n * 链接留文字,强调/标题/列表/引用/表格标记剥离 —— 导师\"念的是话,不是版面\"。\n *\n * splitSentences: 流式友好的切句。每次喂\"累积文本\"返回完整句 + 余量(幂等,StrictMode 安全);\n * flush=true 时尾句强制吐出。终止标点:中英句号/叹/问/分号 + 省略号;ASCII '.' 要求后跟\n * 空白且前字符非数字(3.14 不切)。超过 maxBuffer 仍无终止标点 → 在最后的软标点(、,;: 空白)\n * 处断开,再无则硬断 —— 保证\"导师边生成边念\"的流式管线永远不会饿死。\n *\n * v11.2 表意分隔:换行与 emoji 也是句界。无标点的段落/列表/短句流文本,\n * 旧逻辑的强制断句块会在显示层并组吞整段(高亮整段到结尾);现在逐行/逐 emoji\n * 成句,显示组另加长度上限兜底 —— karaoke 高亮粒度永远可控。\n */\n\n/** 终止标点(出现即成句,连续终止/右引号并吞进句尾) */\nconst HARD = new Set([\"。\", \"!\", \"?\", \"!\", \"?\", \";\", \";\", \"…\"]);\n/** 软标点(超长兜底断句位) */\nconst SOFT = new Set([\",\", \",\", \"、\", \":\", \":\", \" \", \"\\t\"]);\n/** v11.2 emoji 基字符范围(表意/象形/杂项符号/箭头符号区);修饰符(VS16/ZWJ/肤色)不单独成界 */\nconst EMOJI_CP_RE = /[\\u{1F000}-\\u{1FAFF}\\u{2600}-\\u{27BF}\\u{2B00}-\\u{2BFF}]/u;\nfunction isEmojiCp(cp: number | undefined): boolean {\n return cp != null && EMOJI_CP_RE.test(String.fromCodePoint(cp));\n}\n/** 句尾 emoji 序列(含 ZWJ 连写/VS16/肤色修饰) —— endsWithSentenceEnd 认它为显示终点 */\nconst EMOJI_TAIL_RE = /(?:[\\u{1F000}-\\u{1FAFF}\\u{2600}-\\u{27BF}\\u{2B00}-\\u{2BFF}](?:\\uFE0F|\\u200D[\\u{1F000}-\\u{1FAFF}\\u{2600}-\\u{27BF}\\u{2B00}-\\u{2BFF}]|[\\u{1F3FB}-\\u{1F3FF}])*)$/u;\n/** 句尾可并吞的右闭合符 */\nconst CLOSERS = new Set([\"”\", '\"', \"」\", \"』\", \"》\", \")\", \"】\", \"])\".slice(0, 1)]);\n\nexport function normalizeSpeechText(md: string): string {\n let s = md;\n // Windows CRLF 归一(换行是 v11.2 句界,\\r 不许混进来)\n s = s.replace(/\\r\\n?/g, \"\\n\");\n // 围栏代码块(``` / ~~~):整个移除,不朗读\n s = s.replace(/(?:^|\\n)[ \\t]*(?:```|~~~)[^\\n]*\\n[\\s\\S]*?(?:\\n[ \\t]*(?:```|~~~)[^\\n]*|\\n?$)/g, \"\\n\");\n // 行内代码\n s = s.replace(/`[^`\\n]*`/g, \"\");\n // 图片整体移除,链接留文字\n s = s.replace(/!\\[[^\\]]*\\]\\([^)\\n]*\\)/g, \"\");\n s = s.replace(/\\[([^\\]]+)\\]\\([^)\\n]*\\)/g, \"$1\");\n // 标题 / 列表 / 引用标记\n s = s.replace(/^[ \\t]{0,3}#{1,6}[ \\t]+/gm, \"\");\n s = s.replace(/^[ \\t]*(?:[-*+]|\\d+\\.)[ \\t]+/gm, \"\");\n s = s.replace(/^[ \\t]*>[ \\t]?/gm, \"\");\n // 表格:竖线换空格,对齐行(---)整行移除\n s = s.replace(/\\|/g, \" \");\n s = s.replace(/^[ \\t]*[-: ]{3,}[ \\t]*$/gm, \"\");\n // 强调 / 删除线\n s = s.replace(/(\\*\\*|__)(.*?)\\1/g, \"$2\");\n s = s.replace(/(?<![*\\w])(\\*|_)(?!\\s)(.+?)(?<!\\s)\\1(?![*\\w])/g, \"$2\");\n s = s.replace(/~~(.+?)~~/g, \"$1\");\n s = s.replace(/\\n{3,}/g, \"\\n\\n\");\n return s.trim();\n}\n\n/**\n * v11.2 朗读句表单一入口(v11.4 起仅合成侧使用):tts-service 用它切段;\n * 渲染层 karaoke **不再调用**(改吃 ttsAudio.sentence 权威原文,见\n * playedSentencePrefix)——句表从合成侧单向流出,显示侧零复算零分叉。\n */\nexport function speechSentencesOf(text: string): string[] {\n return splitSentences(normalizeSpeechText(text), { flush: true }).sentences;\n}\n\n/**\n * v11.4 已播句组前缀:karaoke 高亮文本 = 从当前块向前收拢到最近的\"句终点块\"\n * (不含),拼接**已播放**各块的原文。块文本由合成侧 ttsAudio.sentence 权威下发\n * (念什么高亮什么,渲染层零复算);强断句块(超长行被 maxBuffer 撕开的)按此\n * 并回同一视觉句,且只亮到当前进度——高亮随音频逐块生长,永不错句。\n */\nexport function playedSentencePrefix(texts: readonly string[], idx: number): string {\n if (idx < 0 || idx >= texts.length) return \"\";\n let s = idx;\n while (s > 0 && !endsWithSentenceEnd(texts[s - 1] ?? \"\")) s--;\n const parts: string[] = [];\n for (let i = s; i <= idx; i++) {\n const t = texts[i];\n if (t && t.trim()) parts.push(t);\n }\n return parts.join(\" \");\n}\n\nexport interface SplitOptions {\n /** 流结束:余量作为尾句吐出 */\n flush?: boolean;\n /** 无终止标点时的强制断句阈值(字符数) */\n maxBuffer?: number;\n}\n\nexport function splitSentences(\n text: string,\n opts: SplitOptions = {},\n): { sentences: string[]; rest: string } {\n const maxBuffer = Math.max(8, opts.maxBuffer ?? 120);\n const flush = opts.flush ?? false;\n const out: string[] = [];\n const n = text.length;\n let start = 0;\n let i = 0;\n let lastSoft = -1;\n\n const emit = (end: number) => {\n const piece = text.slice(start, end).trim();\n if (piece) out.push(piece);\n start = end;\n lastSoft = -1;\n };\n\n while (i < n) {\n const ch = text[i]!;\n // v11.2 emoji=句界:吞后续修饰符(VS16/ZWJ/肤色)与连写 emoji,整序列收进句尾\n const cp = text.codePointAt(i)!;\n if (isEmojiCp(cp)) {\n let j = i + (cp > 0xffff ? 2 : 1);\n while (j < n) {\n const cj = text.codePointAt(j)!;\n const ul = cj > 0xffff ? 2 : 1;\n if (cj === 0xfe0f || cj === 0x200d || (cj >= 0x1f3fb && cj <= 0x1f3ff) || isEmojiCp(cj)) {\n j += ul;\n continue;\n }\n break;\n }\n emit(j);\n i = j;\n continue;\n }\n // v11.2 换行=句界:整行成句(无标点的段落/列表不再被并组吞成整段);\n // 句尾保留 \\n 作为显示终点标记(endsWithSentenceEnd 认它,不被并组)\n if (ch === \"\\n\") {\n const piece = text.slice(start, i + 1).replace(/[^\\S\\n]+$/, \"\");\n if (piece.trim()) out.push(piece);\n start = i + 1;\n lastSoft = -1;\n i++;\n continue;\n }\n if (HARD.has(ch)) {\n let j = i + 1;\n while (j < n && (HARD.has(text[j]!) || CLOSERS.has(text[j]!))) j++;\n emit(j);\n i = j;\n continue;\n }\n if (ch === \".\") {\n const prev = i > 0 ? text[i - 1]! : \"\";\n const next = i + 1 < n ? text[i + 1]! : \"\";\n // ASCII 句点:后跟空白/结尾,且前一字符不是数字(小数点不切)\n if ((next === \"\" || /\\s/.test(next)) && !/\\d/.test(prev)) {\n let j = i + 1;\n while (j < n && (CLOSERS.has(text[j]!) || HARD.has(text[j]!))) j++;\n emit(j);\n i = j;\n continue;\n }\n }\n if (SOFT.has(ch)) lastSoft = i;\n if (i - start + 1 > maxBuffer) {\n if (lastSoft > start) {\n // 在最后的软标点处断(软标点本身不进上一句)。先存切割点 ——\n // emit 内部会重置 lastSoft,重置后再读就回卷到 0 造成重复段(实测踩过)\n const cut = lastSoft;\n emit(cut);\n start = cut + 1;\n } else {\n emit(i + 1);\n i++;\n continue;\n }\n }\n i++;\n }\n\n const rest = text.slice(start).trim();\n if (flush && rest) out.push(rest);\n return { sentences: out, rest: flush ? \"\" : rest };\n}\n\n/**\n * v9 块是否结束于\"真句子终点\"(剥尾部闭合符后是终止标点/ASCII 句点)。\n * 超长强制断句(maxBuffer 软标点兜底)产出的块**不以**句终点结尾——\n * TTS 分块 ≠ 显示句:渲染层 karaoke 用 groupSentenceChunks 把这类块\n * 与后续块并成一个显示句组,高亮整组,不在一句中间断开。\n */\nexport function endsWithSentenceEnd(chunk: string): boolean {\n // v11.2 句尾换行=整行句的显示终点标记 —— 必须在 trimEnd 之前查(它会剥掉 \\n)\n if (/\\n\\s*$/.test(chunk)) return true;\n const t = chunk.trimEnd();\n if (!t) return true;\n let i = t.length - 1;\n while (i >= 0 && CLOSERS.has(t[i]!)) i--;\n if (i < 0) return true;\n const ch = t[i]!;\n if (HARD.has(ch) || ch === \".\") return true;\n // v11.2 表意终点:句尾换行(整行句)或 emoji 序列(含修饰符尾巴)\n if (ch === \"\\n\") return true;\n return EMOJI_TAIL_RE.test(t);\n}\n\n/** v11.2 显示句组长度上限(字符):并组只为\"把被强制断句撕开的真句子缝回来\",\n * 不是无限吞 —— 超上限就保持独立组,karaoke 高亮粒度永远可控。 */\nexport const DISPLAY_GROUP_MAX = 160;\n\n/**\n * v0.28 显示句组跨度(纯):把\"TTS 块在容器文本中的匹配段 [start,end)\"扩展到\n * **整条显示句**的字符跨度——向前收拢到最近句终点之后,向后扩到最近句终点\n * (含闭合符/emoji 修饰尾)。v11.4 起朗读 mark 只覆盖\"已播前缀\",而伴学避让\n * (\"生物不压正在读的句\")需要整句的行盒;本函数与 splitSentences 共用同一套\n * 句界判定(HARD/ASCII 句点/换行/emoji),零第二份标点表。maxSpan 是单侧扫描\n * 上限,防病态无标点长文把跨度扩爆(扫不到界就停在窗口边,保守取小)。\n */\nexport function displayGroupSpanAround(\n text: string,\n start: number,\n end: number,\n maxSpan = 400,\n): { start: number; end: number } {\n const n = text.length;\n // text[i] 开头是否是一处句终点;是则返回该终点的 exclusive 结束下标(闭合符/\n // emoji 修饰已吞进终点),否则 -1。判定分支与 splitSentences 逐条同构。\n const hardEndAt = (i: number): number => {\n if (i < 0 || i >= n) return -1;\n const ch = text[i]!;\n if (ch === \"\\n\") return i + 1;\n if (HARD.has(ch)) {\n let j = i + 1;\n while (j < n && (HARD.has(text[j]!) || CLOSERS.has(text[j]!))) j++;\n return j;\n }\n if (ch === \".\") {\n const prev = i > 0 ? text[i - 1]! : \"\";\n const next = i + 1 < n ? text[i + 1]! : \"\";\n if ((next === \"\" || /\\s/.test(next)) && !/\\d/.test(prev)) {\n let j = i + 1;\n while (j < n && (CLOSERS.has(text[j]!) || HARD.has(text[j]!))) j++;\n return j;\n }\n return -1;\n }\n const cp = text.codePointAt(i)!;\n if (isEmojiCp(cp)) {\n let j = i + (cp > 0xffff ? 2 : 1);\n while (j < n) {\n const cj = text.codePointAt(j)!;\n const ul = cj > 0xffff ? 2 : 1;\n if (cj === 0xfe0f || cj === 0x200d || (cj >= 0x1f3fb && cj <= 0x1f3ff) || isEmojiCp(cj)) {\n j += ul;\n continue;\n }\n break;\n }\n return j;\n }\n return -1;\n };\n\n // 向前:找 start 之前最近的句终点,span 起点落在它的 exclusive 终点处;\n // 终点被块起点截断(e>start,理论上不该发生)时不动起点。扫到文本头都没有\n // 句界字符(如开头就是无标点短句)时,文本头本身就是界 → 起点=0;仅当因\n // maxSpan 提前截断才保守保持 start(界在窗口外,不外扩)。\n let s = start;\n {\n let found = false;\n for (let i = Math.min(start, n) - 1; i >= 0 && start - i <= maxSpan; i--) {\n const e = hardEndAt(i);\n if (e >= 0) {\n found = true;\n if (e <= start) s = e;\n break;\n }\n }\n if (!found && start <= maxSpan) s = 0;\n }\n\n // 向后:块段已以句终点结尾(终点块,含闭合符/\\n/emoji 尾)则终点只吞紧随的\n // 右闭合符(与 splitSentences\"右引号并吞进句尾\"一致)即止;否则找 end 起最近的\n // 句终点(含其尾巴),span 终点=其 exclusive 结束位;扫到上限/文本尾都没终点\n // (超长无标点段)则扩到文本尾。\n if (endsWithSentenceEnd(text.slice(0, Math.min(end, n)))) {\n let e = Math.min(end, n);\n while (e < n && CLOSERS.has(text[e]!)) e++;\n return { start: s, end: e };\n }\n for (let i = end; i < n && i - end <= maxSpan; i++) {\n const e = hardEndAt(i);\n if (e >= 0) return { start: s, end: e };\n }\n return { start: s, end: n };\n}\n\n/** TTS 分块序列 → 显示句组(每组合成一个真句子):[start,end] 闭区间块下标。 */\nexport function groupSentenceChunks(sentences: string[]): Array<{ start: number; end: number }> {\n const groups: Array<{ start: number; end: number }> = [];\n let i = 0;\n while (i < sentences.length) {\n let j = i;\n let len = sentences[i]!.length;\n while (\n j + 1 < sentences.length &&\n !endsWithSentenceEnd(sentences[j]!) &&\n len + sentences[j + 1]!.length <= DISPLAY_GROUP_MAX\n ) {\n j++;\n len += sentences[j]!.length;\n }\n groups.push({ start: i, end: j });\n i = j + 1;\n }\n return groups;\n}\n","/**\n * Vendored from LookatStudy shared/math-speech.ts (MIT License,\n * https://github.com/Kaiji-Z/LookatStudy), upstream v0.28.0. Unmodified\n * except this provenance header. zh-only by design upstream — the plugin's\n * read-aloud pipeline applies it for zh and strips $..$ delimiters for other\n * locales (conversion for more languages is future work).\n *\n * math-speech —— LaTeX 公式 → 中文口语(v0.19 朗读口语化,纯函数,verify 直测)。\n *\n * 只在**合成侧**应用(真正念出来的文本);karaoke 高亮/匹配层继续吃 `$..$` 原文\n * (DOM 侧 getTextModel 收 KaTeX annotation 的 TeX 源,两侧在 canonical 空间对齐)\n * ——念的是人话,亮的是原文,互不干扰。\n *\n * 规则表起步:常见记号(分式/根号/上下标/希腊字母/关系与算符)覆盖优先,\n * 未覆盖宏退化为逐字母(好于念\"反斜杠 f-r-a-c\")。\n */\n\n/** 单符号命令 → 中文(长命令先试,避免 \\le 被 \\leq 截断)。 */\nconst SYMBOLS: Array<[string, string]> = [\n [\"\\\\leq\", \"小于等于\"], [\"\\\\le\", \"小于等于\"], [\"\\\\geq\", \"大于等于\"], [\"\\\\ge\", \"大于等于\"],\n [\"\\\\neq\", \"不等于\"], [\"\\\\ne\", \"不等于\"], [\"\\\\approx\", \"约等于\"], [\"\\\\equiv\", \"恒等于\"],\n [\"\\\\pm\", \"正负\"], [\"\\\\times\", \"乘以\"], [\"\\\\cdot\", \"点乘\"], [\"\\\\div\", \"除以\"],\n [\"\\\\to\", \"趋于\"], [\"\\\\rightarrow\", \"趋于\"], [\"\\\\Rightarrow\", \"推出\"],\n [\"\\\\infty\", \"无穷\"], [\"\\\\sum\", \"求和\"], [\"\\\\prod\", \"连乘\"], [\"\\\\int\", \"积分\"],\n [\"\\\\lim\", \"极限\"], [\"\\\\log\", \"对数\"], [\"\\\\ln\", \"自然对数\"], [\"\\\\exp\", \"指数\"],\n [\"\\\\in\", \"属于\"], [\"\\\\subset\", \"包含于\"], [\"\\\\cup\", \"并\"], [\"\\\\cap\", \"交\"],\n [\"\\\\forall\", \"任意\"], [\"\\\\exists\", \"存在\"], [\"\\\\partial\", \"偏导\"], [\"\\\\nabla\", \"梯度\"],\n [\"\\\\angle\", \"角\"], [\"\\\\degree\", \"度\"], [\"\\\\cdots\", \"点点点\"], [\"\\\\ldots\", \"点点点\"],\n];\n\nconst GREEK: Record<string, string> = {\n alpha: \"阿尔法\", beta: \"贝塔\", gamma: \"伽马\", delta: \"德尔塔\", epsilon: \"艾普西隆\",\n zeta: \"泽塔\", eta: \"伊塔\", theta: \"西塔\", iota: \"约塔\", kappa: \"卡帕\", lambda: \"拉姆达\",\n mu: \"缪\", nu: \"纽\", xi: \"克西\", pi: \"派\", rho: \"柔\", sigma: \"西格马\", tau: \"陶\",\n phi: \"斐\", chi: \"凯\", psi: \"普西\", omega: \"欧米伽\",\n};\n\n/** 读取 `{...}` 花括号组(从 openIdx 的下一个字符起,返回内容与结束下标)。 */\nfunction readGroup(s: string, i: number): { body: string; end: number } | null {\n if (s[i] !== \"{\") return null;\n let depth = 0;\n for (let j = i; j < s.length; j++) {\n if (s[j] === \"{\") depth++;\n else if (s[j] === \"}\") {\n depth--;\n if (depth === 0) return { body: s.slice(i + 1, j), end: j + 1 };\n }\n }\n return null;\n}\n\n/** 单 token(花括号组或单字符)读取:返回 {body, end}。 */\nfunction readAtom(s: string, i: number): { body: string; end: number } {\n const g = readGroup(s, i);\n if (g) return g;\n return { body: s[i] ?? \"\", end: i + 1 };\n}\n\n/** LaTeX 片段 → 中文口语(递归:命令参数体内再走一遍)。 */\nexport function mathToSpokenZH(tex: string): string {\n let s = tex;\n // 无参修饰先清\n s = s.replace(/\\\\(?:left|right|displaystyle|limits)\\b/g, \" \");\n s = s.replace(/\\\\[,;!\\s]|\\\\quad|\\\\qquad|~/g, \" \");\n\n // 带参命令(迭代到不动点,支持嵌套如 \\frac{\\alpha}{\\beta})\n for (let guard = 0; guard < 12; guard++) {\n let next = s;\n // \\frac{a}{b} 家族 → \"a 分之 b\";\\binom → \"a 取 b\"(归一不许插空格,空格会被\n // readAtom 当成单字符参数吞掉首参)\n next = next.replace(/\\\\[dt]?frac(?![A-Za-z])/g, \"\\\\frac\");\n {\n let i: number;\n while ((i = next.indexOf(\"\\\\frac\")) >= 0) {\n const a = readAtom(next, i + 5);\n const b = readAtom(next, a.end);\n next = next.slice(0, i) + `${a.body} 分之 ${b.body} ` + next.slice(b.end);\n }\n }\n {\n let i: number;\n while ((i = next.indexOf(\"\\\\binom\")) >= 0) {\n const a = readAtom(next, i + 6);\n const b = readAtom(next, a.end);\n next = next.slice(0, i) + `${a.body} 取 ${b.body} ` + next.slice(b.end);\n }\n }\n // \\sqrt[n]{x} / \\sqrt{x}\n next = next.replace(/\\\\sqrt\\s*\\[( {[^{}]*} |[^\\s\\\\]+)\\s*\\]\\s*/g, (_m, n: string) => `${n.trim()} 次根号 `);\n {\n let i: number;\n while ((i = next.indexOf(\"\\\\sqrt\")) >= 0) {\n const a = readAtom(next, i + 5);\n next = next.slice(0, i) + `根号 ${a.body} ` + next.slice(a.end);\n }\n }\n // 包裹命令(取内容):\\text 直接留内容;\\mathbf 等去壳\n next = next.replace(/\\\\(?:mathbf|mathbb|mathrm|mathit|boldsymbol|text|operatorname)\\s*/g, (m) => (m.startsWith(\"\\\\text\") ? \"\" : \" \"));\n if (next === s) { s = next; break; }\n s = next;\n }\n\n // 上下标:前一个原子 ^{..}/^c → \"的 n 次方\";_{..}/_c → \"下标 n\"\n for (let guard = 0; guard < 8; guard++) {\n const next = s.replace(\n /(\\\\frac|[\\w)\\]}]|\\s)\\s*\\^\\s*(\\{[^{}]*\\}|[^\\s])|(\\\\frac|[\\w)\\]}]| )\\s*_\\s*(\\{[^{}]*\\}|[^\\s])/g,\n (_m, base1: string | undefined, sup: string | undefined, base2: string | undefined, sub: string | undefined) => {\n if (sup !== undefined) return `${base1} 的 ${sup.replace(/[{}]/g, \"\")} 次方`;\n return `${base2} 下标 ${sub!.replace(/[{}]/g, \"\")}`;\n },\n );\n if (next === s) break;\n s = next;\n }\n\n // 符号与希腊字母(长命令优先)\n for (const [cmd, zh] of SYMBOLS) s = s.split(cmd).join(` ${zh} `);\n s = s.replace(/\\\\([A-Za-z]+)\\b/g, (_m, name: string) => ` ${GREEK[name] ?? name} `);\n s = s.replace(/[{}]/g, \" \");\n return s.replace(/\\s+/g, \" \").trim();\n}\n\n/** 句子级入口:把 `$..$`/`$$..$$` 段替换为口语;段外文本逐字节不变。 */\nexport function speakMathInSentence(sentence: string): string {\n return sentence.replace(/\\$\\$([^$]+)\\$\\$|\\$([^$]+)\\$/g, (_m, block?: string, inline?: string) => {\n const spoken = mathToSpokenZH(block ?? inline ?? \"\");\n return spoken ? ` ${spoken} ` : \"\";\n });\n}\n","/**\n * The panel tutor's chat stream: folds a bound session's event window (the\n * same feed the host conversation assembles) into chat rows — structurally,\n * with zero host imports, because the panel renders OUTSIDE the host's slot\n * system. Durable `user/message` / `assistant/message` events become rows;\n * tool calls stay in the host conversation's toolview cards (the study view\n * never mirrored them); transient `text-delta` live chunks accumulate into\n * one streaming row per attempt so the tutor's reply grows in place.\n * @module dsh-plugin-lookatstudy/client/session-feed\n */\n\nimport type { ChatRow } from './views.tsx'\n\n/** Structural shapes of the host's event window (kept local — no host imports). */\nexport interface FeedEvent {\n readonly type: string\n readonly seq: number\n readonly data?: {\n readonly content?: readonly { readonly type?: string; readonly text?: string }[]\n readonly message?: { readonly content?: readonly { readonly kind?: string; readonly type?: string; readonly text?: string }[]; readonly blocks?: readonly { readonly kind?: string; readonly text?: string }[] }\n readonly chunk?: { readonly type?: string; readonly text?: string }\n readonly attemptId?: string\n }\n}\n\nexport interface FeedEntry {\n readonly type: 'event' | 'transient'\n readonly event: FeedEvent\n}\n\nexport interface FeedWindow {\n readonly entries: readonly FeedEntry[]\n readonly hasMore?: boolean\n}\n\nfunction userText(event: FeedEvent): string {\n const content = event.data?.content\n if (content === undefined) return ''\n return content\n .filter(part => (part.type ?? 'text') === 'text')\n .map(part => part.text ?? '')\n .join('\\n\\n')\n .trim()\n}\n\nfunction assistantText(event: FeedEvent): string {\n const message = event.data?.message\n if (message === undefined) return ''\n const blocks = message.content ?? message.blocks ?? []\n return blocks\n .filter(block => (block.kind ?? block.type) === 'text')\n .map(block => block.text ?? '')\n .join('\\n\\n')\n .trim()\n}\n\n/**\n * Fold one event-window snapshot into ordered chat rows. The transient\n * `text-delta` chunks of the most recent attempt accumulate into a single\n * streaming row (the durable `assistant/message` replaces it on settlement).\n * @param window - the bound session's event window snapshot, or undefined.\n */\nexport function feedRows(window: FeedWindow | undefined): ChatRow[] {\n if (window === undefined) return []\n const rows: ChatRow[] = []\n const streams = new Map<string, { text: string; anchor: number; settled: boolean }>()\n let latest: string | undefined\n for (const entry of window.entries) {\n const event = entry?.event\n if (event === undefined) continue\n if (entry.type === 'event') {\n if (event.type === 'user/message') {\n const text = userText(event)\n if (text !== '') rows.push({ key: `u${event.seq}`, role: 'user', text })\n } else if (event.type === 'assistant/message') {\n const text = assistantText(event)\n if (text !== '') rows.push({ key: `a${event.seq}`, role: 'assistant', text })\n }\n // tool/call, tool/result, step/*, assistant/attempt: the study view never\n // mirrored tool traffic — the host conversation's toolviews own it.\n // A durable row appended after an attempt's first chunk marks that\n // attempt settled — its streaming row must not linger.\n for (const s of streams.values()) {\n if (rows.length > s.anchor) s.settled = true\n }\n continue\n }\n // transient live chunk: accumulate text deltas per attempt. The streaming\n // row anchors where the attempt's FIRST chunk appeared in the durable\n // row order (deltas arrive between the rows that precede/follow them).\n const chunk = event.data?.chunk\n if (chunk === undefined || chunk.type !== 'text-delta') continue\n const attemptId = event.data?.attemptId ?? String(event.seq)\n const stream = streams.get(attemptId)\n if (stream === undefined) streams.set(attemptId, { text: chunk.text ?? '', anchor: rows.length, settled: false })\n else stream.text += chunk.text ?? ''\n latest = attemptId\n }\n const stream = latest !== undefined ? streams.get(latest) : undefined\n if (stream !== undefined && !stream.settled) {\n const text = stream.text.trim()\n if (text !== '') rows.splice(stream.anchor, 0, { key: 'streaming', role: 'streaming', text })\n }\n return rows\n}\n","/**\n * The read-aloud playback controller: walks a lesson's sentence queue over a\n * synthesizer engine, falling back to the browser's speechSynthesis the\n * moment the Edge synthesis path fails (offline, endpoint gone, 5xx). Plain\n * TypeScript with injected engines so the fallback decision is unit-testable\n * without audio hardware.\n * @module dsh-plugin-lookatstudy/client/readaloud\n */\n\n/** One speakable backend: Edge-over-dashboard-audio, or window.speechSynthesis. */\nexport interface SpeechEngine {\n /** Speak one sentence; resolves when playback finishes, rejects on failure. */\n speak(text: string): Promise<void>\n pause(): void\n resume(): void\n /** Hard-stop; the controller also drops its queue on stop(). */\n cancel(): void\n /**\n * Optional warm-up for an upcoming sentence (prefetch its synthesis) — the\n * controller fires it when playback starts so the next sentence never waits\n * on the network. Engines without it are skipped.\n */\n prewarm?(text: string): void\n}\n\nexport type ReadAloudEngineName = 'edge' | 'system'\nexport type ReadAloudState = 'idle' | 'speaking' | 'paused'\n\nexport interface ReadAloudStatus {\n index: number\n total: number\n engine: ReadAloudEngineName | null\n state: ReadAloudState\n /** Set once the primary engine failed and the system voice took over. */\n degraded: boolean\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise(resolve => { setTimeout(resolve, ms) })\n\nexport class ReadAloudController {\n private index = 0\n private stopped = false\n private paused = false\n private engine: ReadAloudEngineName | null = null\n private degraded = false\n private run: Promise<void> | null = null\n\n constructor(\n private readonly sentences: readonly string[],\n private readonly primary: SpeechEngine,\n private readonly fallback: SpeechEngine,\n private readonly notify?: (status: ReadAloudStatus) => void,\n ) {}\n\n private emit(state: ReadAloudState): void {\n this.notify?.({ index: this.index, total: this.sentences.length, engine: this.engine, state, degraded: this.degraded })\n }\n\n /** Speak the whole queue; the returned promise settles when done or stopped. */\n start(): Promise<void> {\n if (this.run !== null) return this.run\n this.stopped = false\n this.run = (async () => {\n this.engine = 'edge'\n this.emit('speaking')\n while (!this.stopped && this.index < this.sentences.length) {\n const text = this.sentences[this.index]!\n try {\n await this.speakOn(text)\n } catch {\n if (this.stopped) return\n if (this.engine === 'edge') {\n // The one-way ratchet: Edge fails → system voice finishes the lesson.\n this.degraded = true\n this.engine = 'system'\n this.emit('speaking')\n try {\n await this.speakOn(text)\n } catch {\n this.emit('idle')\n return // even the system voice is gone (no voices installed); give up honestly\n }\n } else {\n this.emit('idle')\n return\n }\n }\n if (this.stopped) return\n this.index += 1\n this.emit('speaking')\n }\n this.emit('idle')\n })()\n return this.run\n }\n\n private async speakOn(text: string): Promise<void> {\n const impl = this.engine === 'system' ? this.fallback : this.primary\n // Warm the NEXT sentence while this one plays — the next speak() then\n // hits the engine's own prefetch instead of the network.\n const next = this.sentences[this.index + 1]\n if (next !== undefined) impl.prewarm?.(next)\n await impl.speak(text)\n // A pause pressed mid-utterance holds the QUEUE too, not just the audio —\n // otherwise a long pause races on to the next sentence.\n while (this.paused && !this.stopped) await sleep(120)\n }\n\n pause(): void {\n if (this.stopped || this.paused) return\n this.paused = true\n ;(this.engine === 'system' ? this.fallback : this.primary).pause()\n this.emit('paused')\n }\n\n resume(): void {\n if (!this.paused) return\n this.paused = false\n ;(this.engine === 'system' ? this.fallback : this.primary).resume()\n this.emit('speaking')\n }\n\n /** Stop everything; the controller is single-use — build a fresh one to replay. */\n stop(): void {\n this.stopped = true\n this.paused = false\n this.primary.cancel()\n this.fallback.cancel()\n this.emit('idle')\n }\n\n get status(): ReadAloudStatus {\n return { index: this.index, total: this.sentences.length, engine: this.engine, state: this.stopped ? 'idle' : this.paused ? 'paused' : 'speaking', degraded: this.degraded }\n }\n}\n","/**\n * Shared pure helpers for the study panel's pieces: chat-row shapes, tree\n * status copy, quiz-option extraction, and section default expansion. The\n * panel itself lives in `panel.tsx`; since 0.14.0 nothing here touches the\n * host composer or the conversation view.\n * @module dsh-plugin-lookatstudy/client/views\n */\n\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport { tr, type StudyT } from './locale.ts'\n\n/** One rendered tutor chat row (fed by session-feed's fold). */\nexport interface ChatRow {\n readonly key: string\n readonly role: 'user' | 'assistant' | 'error' | 'streaming' | 'thinking'\n readonly text: string\n}\n\n/** Small inline error surface for failed write actions (shared with settings). */\nexport function ActionError({ error }: { error: string | null }): ReactNode {\n if (error === null) return null\n return createElement('div', { className: 'lks-propcard-err' }, error)\n}\n\n/**\n * Tooltip text for one course-tree status glyph. Pure (the translator is an\n * optional parameter defaulting to the active one, evaluated per call).\n */\nexport function statusTitle(kind: string, status: string, t: StudyT = tr): string {\n if (kind === 'exam') return t('status.exam')\n switch (status) {\n case 'mastered': return t('status.mastered')\n case 'in_progress': return t('status.in_progress')\n case 'available': return t('status.available')\n default: return t('status.locked')\n }\n}\n\n/**\n * Extract the last quiz option block (2–4 consecutive `A. …` `B. …` lines,\n * letters strictly consecutive from A) from an assistant reply — the raw text\n * of the tutor's question becomes the clickable answer surface. Pure.\n */\nexport function quizOptions(text: string): ReadonlyArray<{ letter: string; text: string }> {\n const runs: Array<Array<{ letter: string; text: string }>> = []\n let run: Array<{ letter: string; text: string }> = []\n const flush = (): void => {\n if (run.length >= 2) runs.push(run)\n run = []\n }\n for (const line of text.split('\\n')) {\n const match = /^([A-D])[.、:)]\\s+(.+)$/.exec(line.trim())\n if (match === null) {\n flush()\n continue\n }\n const nextLetter = run.length === 0 ? 'A' : String.fromCharCode(run[run.length - 1]!.letter.charCodeAt(0) + 1)\n if (match[1] === nextLetter) run.push({ letter: match[1], text: match[2]! })\n else {\n flush()\n if (match[1] === 'A') run.push({ letter: match[1], text: match[2]! })\n }\n }\n flush()\n return runs.at(-1) ?? []\n}\n\n/**\n * Default expansion for one rail section: collapsed when every study lesson is\n * done (mastered) or not yet reachable (locked). The focus lesson's section\n * stays open; exam nodes never force a section open. Pure.\n */\nexport function sectionDefaultOpen(section: { lessons: ReadonlyArray<{ kind: string; status: string; focus: boolean }> }): boolean {\n return section.lessons.some(l => l.focus || (l.kind !== 'exam' && l.status !== 'mastered' && l.status !== 'locked'))\n}\n","/**\n * The study panel — the sidebar-entry surface that takes over the center\n * column, arranged like upstream LookatStudy's app (App.tsx): 左 course rail\n * (tree/import/review) | 中 the tutor chat stream with its OWN composer (the\n * host composer is never involved) | 右 the notebook (讲解/概念图/笔记).\n *\n * Interaction model aligned with upstream: opening a lesson only FOCUSES it\n * (state-side attempt: in_progress + BKT seed + unlocks — zero LLM); the\n * tutor engages when the learner sends a message or taps a starter, which\n * lazily mints and stages the lesson's thread and prompts through the host's\n * session face. The host is exactly the conversation-model + agent-turn\n * engine; the thread's event window streams the reply back into the chat.\n * @module dsh-plugin-lookatstudy/client/panel\n */\n\nimport { createElement, useEffect, useRef, useState } from 'react'\nimport type { CSSProperties, KeyboardEvent as ReactKeyboardEvent, ReactNode } from 'react'\nimport {\n IconBoltFill16, IconBookFill16, IconCrownFill16, IconDownloadOutline16,\n IconGoalOutline16, IconGlobeOutline14, IconLoadingOutline16, IconLockFill16,\n IconRefreshOutline16, IconStarFill16, IconTrashOutline16,\n} from './icons.tsx'\nimport type { ClientContext, SessionPromptFace } from './faces.ts'\nimport { useStudy, storedTtsVoice } from './data.ts'\nimport { renderMarkdown } from '../markdown.ts'\nimport { enhanceRendered, setEnhanceDeps } from './enhance.ts'\nimport { renderLessonConceptMap } from './diagrams.ts'\nimport { speechSentencesOf } from '../vendor/speech-text.ts'\nimport { speakMathInSentence } from '../vendor/math-speech.ts'\nimport { feedRows } from './session-feed.ts'\nimport { ReadAloudController, type ReadAloudStatus, type SpeechEngine } from './readaloud.ts'\nimport { statusTitle, quizOptions, sectionDefaultOpen } from './views.tsx'\nimport { tr } from './locale.ts'\n\n/** The three souls in pill order (same shape as the study tab's pills). */\nconst MODES: ReadonlyArray<{ id: 'direct' | 'guide' | 'practice'; labelKey: string; hintKey: string }> = [\n { id: 'direct', labelKey: 'soul.direct', hintKey: 'soul.direct.hint' },\n { id: 'guide', labelKey: 'soul.guide', hintKey: 'soul.guide.hint' },\n { id: 'practice', labelKey: 'soul.practice', hintKey: 'soul.practice.hint' },\n]\n\nconst ZONES: ReadonlyArray<readonly [string, string]> = [\n ['understand', 'zone.understand'],\n ['record', 'zone.record'],\n ['practice', 'zone.practice'],\n]\n\nfunction statusIcon(kind: string, status: string): ReactNode {\n if (kind === 'exam') return createElement(IconGoalOutline16, { size: 14 })\n if (status === 'mastered') return createElement(IconCrownFill16, { size: 14 })\n if (status === 'in_progress') return createElement(IconBookFill16, { size: 14 })\n if (status === 'available') return createElement(IconStarFill16, { size: 14 })\n return createElement(IconLockFill16, { size: 14 })\n}\n\nfunction examOpen(lessons: ReadonlyArray<{ kind: string; masteryPct: number | null }>): boolean {\n return lessons.every(l => l.kind !== 'study' || (l.masteryPct ?? 0) >= 50)\n}\n\nfunction titleMatches(title: string, query: string): boolean {\n const keys = query.trim().toLowerCase().split(/\\s+/).filter(Boolean)\n return keys.every(k => title.toLowerCase().includes(k))\n}\n\nexport type PanelSend = (text: string) => void\n\nfunction textOf(blocks: readonly { type?: string; text?: string }[] | undefined): string {\n return (blocks ?? []).filter(b => (b.type ?? 'text') === 'text').map(b => b.text ?? '').join('\\n\\n').trim()\n}\n\n/** One rendered chat row (shared shape with the read-aloud era's renderer). */\nfunction chatRow(row: { key: string; role: string; text: string }, interactive?: { send: PanelSend }): ReactNode {\n if (row.role === 'user') {\n return createElement('div', { key: row.key, className: 'lks14-msg lks14-msg-user' }, row.text)\n }\n const cls = row.role === 'streaming' ? 'lks14-msg lks14-msg-assistant streaming' : 'lks14-msg lks14-msg-assistant'\n const body = createElement('div', {\n key: row.key,\n className: cls,\n dangerouslySetInnerHTML: { __html: renderMarkdown(row.text) },\n })\n const options = interactive === undefined || row.role === 'streaming' ? [] : quizOptions(row.text)\n if (options.length < 2) return body\n return createElement('div', { key: row.key, className: 'lks14-turn' },\n body,\n createElement('div', { className: 'lks14-quiz', title: tr('quiz.title') },\n ...options.map(opt => createElement('button', {\n key: opt.letter,\n className: 'lks14-opt',\n onClick: () => { interactive.send(tr('quiz.answer', { letter: opt.letter, text: opt.text })) },\n },\n createElement('span', { className: 'lks14-optletter' }, opt.letter),\n createElement('span', null, opt.text))),\n ))\n}\n\n/** Build the panel component bound to the client services. */\nexport function studyPanelView(ctx: ClientContext): () => ReactNode {\n return function StudyPanel(): ReactNode {\n return createElement(StudyPanelBody, { ctx, key: 'panel' })\n }\n}\n\ntype StudyData = ReturnType<typeof useStudy>['data']\n\nfunction StudyPanelBody({ ctx }: { ctx: ClientContext }): ReactNode {\n const { data, activate, setMode, setFocus, searchLessons, deleteCourse, deleteNote, bindLessonSession } = useStudy()\n const [sendError, setSendError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [rows, setRows] = useState<ReturnType<typeof feedRows>>([])\n const [draft, setDraft] = useState('')\n const lesson = data?.lesson ?? null\n const localBound = useRef<string | null>(null)\n const streamEl = useRef<HTMLDivElement | null>(null)\n\n const boundId = localBound.current ?? (lesson !== null ? data?.lessonSessions[lesson.lessonId] ?? null : null)\n\n // The tutor chat stream: subscribe to the staged lesson thread's event window.\n useEffect(() => {\n if (boundId === null) {\n setRows([])\n return\n }\n const binding = (ctx.sessions as { binding(id: string): { eventSource?: { getSnapshot(): unknown; subscribe(l: () => void): () => void } } | undefined }).binding(boundId)\n const source = binding?.eventSource\n if (source === undefined) {\n setRows([])\n return\n }\n const update = (): void => { setRows(feedRows(source.getSnapshot() as never)) }\n update()\n return source.subscribe(update)\n }, [boundId, ctx])\n\n // Keep the stream pinned to the latest row.\n useEffect(() => {\n const el = streamEl.current\n if (el !== null) el.scrollTop = el.scrollHeight\n }, [rows])\n\n /** The one send path: lazily stage the lesson thread, then prompt. */\n const send = (text: string): void => {\n void (async () => {\n if (busy || lesson === null || text.trim() === '') return\n setBusy(true)\n setSendError(null)\n try {\n if (data?.active !== true) await activate(true)\n let sessionId = localBound.current ?? data?.lessonSessions[lesson.lessonId]\n if (sessionId === undefined || sessionId === null) {\n const area = await fetch('/lookatstudy/api/study-workspace')\n if (!area.ok) throw new Error(`study area unavailable (HTTP ${String(area.status)})`)\n const { path } = await area.json() as { path: string }\n const workspace = await ctx.workspaces.create({ path })\n sessionId = await ctx.sessions.create({ workspaceId: workspace.workspaceId })\n localBound.current = sessionId\n await bindLessonSession(lesson.lessonId, sessionId)\n }\n // Stage the thread (its event window only opens while current); the\n // internal navigation must not hand the panel back.\n const shell = PANEL_SHELL.current\n if (shell !== null) {\n shell.suppressHandBack(() => { ctx.sessions.open(sessionId!) })\n } else {\n ctx.sessions.open(sessionId)\n }\n const actx = ctx.sessions.scope(sessionId)\n const face: SessionPromptFace | undefined = actx === undefined ? undefined : ctx.sessions.sessionOf(actx)\n if (face === undefined) throw new Error('lesson session is not addressable yet')\n const result = await face.prompt([{ type: 'text', text }], 'queue')\n if (!result.ok) throw new Error(`prompt rejected: ${result.error.code}: ${result.error.message}`)\n setDraft('')\n } catch (err) {\n setSendError(err instanceof Error ? err.message : String(err))\n } finally {\n setBusy(false)\n }\n })()\n }\n\n const body: ReactNode = createElement('div', { className: 'lks14-body' },\n createElement(CourseRail, { data, activate, setFocus, searchLessons, deleteCourse, send }),\n createElement(ChatPane, { data, lesson, rows, busy, sendError, draft, setDraft, send, setMode }),\n createElement(NotebookPane, { data, deleteNote }),\n )\n return createElement('div', { className: 'lks14', 'data-lks-panel': '' }, body)\n}\n\n/** Module-level shell handle so the panel can suppress hand-back on internal opens. */\nconst PANEL_SHELL: { current: { suppressHandBack(fn: () => void): void } | null } = { current: null }\nexport function setPanelShell(shell: { suppressHandBack(fn: () => void): void } | null): void {\n PANEL_SHELL.current = shell\n}\n\n/** 左栏:course picker, tree, review box, import. */\nfunction CourseRail({ data, activate, setFocus, searchLessons, deleteCourse, send }: {\n data: StudyData\n activate: (active: boolean) => Promise<void>\n setFocus: (id: string) => Promise<void>\n searchLessons: (query: string) => Promise<Array<{ lessonId: string; lessonTitle: string; snippet: string }>>\n deleteCourse: (courseId: string) => Promise<void>\n send: PanelSend\n}): ReactNode {\n const [selectedCourse, setSelectedCourse] = useState('')\n const [query, setQuery] = useState('')\n const [confirmDelete, setConfirmDelete] = useState(false)\n const [showImport, setShowImport] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const reportError = (err: unknown): void => { setError(err instanceof Error ? err.message : String(err)) }\n useEffect(() => {\n if (!confirmDelete) return\n const disarm = (): void => { setConfirmDelete(false) }\n const onKey = (e: KeyboardEvent): void => { if (e.key === 'Escape') disarm() }\n window.addEventListener('click', disarm)\n window.addEventListener('keydown', onKey)\n return () => {\n window.removeEventListener('click', disarm)\n window.removeEventListener('keydown', onKey)\n }\n }, [confirmDelete])\n\n const body: ReactNode = data === null\n ? createElement('div', { className: 'lks14-empty' }, tr('loading'))\n : data.courses.length === 0\n ? createElement('div', { className: 'lks14-empty' },\n tr('rail.empty.title'), createElement('br'),\n createElement('button', {\n className: 'lks-btn primary',\n style: { margin: '10px 0' },\n onClick: () => { send(tr('prompt.import', { url: 'https://github.com/microsoft/AI-For-Beginners' })) },\n }, createElement(IconDownloadOutline16, null), tr('rail.empty.demo')),\n createElement(ImportRow, { send }),\n )\n : (() => {\n const courseId = data.courses.some(c => c.courseId === selectedCourse)\n ? selectedCourse\n : data.courses[0]!.courseId\n const course = data.courses.find(c => c.courseId === courseId)!\n return createElement('div', null,\n createElement('div', { className: 'lks14-railhead' },\n data.courses.length > 1\n ? createElement('select', {\n className: 'lks-set-select',\n value: courseId,\n onChange: (e: { target: { value: string } }) => { setSelectedCourse(e.target.value); setConfirmDelete(false) },\n }, ...data.courses.map(c => createElement('option', { key: c.courseId, value: c.courseId }, c.title)))\n : createElement('div', { className: 'lks14-railtitle', title: course.title }, course.title),\n createElement('button', {\n className: `lks-btn ${confirmDelete ? 'primary' : 'ghost'}`,\n title: confirmDelete ? tr('rail.delete.title.confirm') : tr('rail.delete'),\n onClick: (e: { stopPropagation: () => void }) => {\n e.stopPropagation()\n if (!confirmDelete) { setConfirmDelete(true); return }\n setConfirmDelete(false)\n deleteCourse(courseId).then(() => { setSelectedCourse('') }, reportError)\n },\n }, confirmDelete ? tr('rail.delete.confirm') : createElement(IconTrashOutline16, { size: 14 })),\n ),\n createElement('div', { className: 'lks14-railsub' }, tr('rail.mastered', { mastered: course.mastered, total: course.total })),\n createElement('div', {\n className: `lks14-masterybar${course.avgMasteryPct === 100 ? ' gold' : ''}`,\n title: course.avgMasteryPct === null ? tr('rail.avg.none') : tr('rail.avg', { pct: course.avgMasteryPct }),\n }, createElement('i', { style: { transform: `scaleX(${(course.avgMasteryPct ?? 0) / 100})` } })),\n createElement('input', {\n className: 'lks14-search',\n type: 'search',\n placeholder: tr('rail.search'),\n value: query,\n onChange: (e: { target: { value: string } }) => { setQuery(e.target.value) },\n onKeyDown: (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (e.key !== 'Enter' || query.trim() === '') return\n for (const section of course.sections) {\n const hit = section.lessons.find(l => l.kind !== 'exam' && l.status !== 'locked' && titleMatches(l.title, query))\n if (hit !== undefined) {\n void setFocus(hit.id).catch(reportError)\n return\n }\n }\n void searchLessons(query).then(hits => {\n const hit = hits.find(h => h.lessonId.startsWith(`${courseId}:`)) ?? hits[0]\n if (hit !== undefined) { setQuery(''); void setFocus(hit.lessonId).catch(reportError) }\n }).catch(reportError)\n },\n }),\n data.dueCount > 0\n ? createElement('div', { className: 'lks14-duebox' },\n createElement(IconRefreshOutline16, { size: 13 }), tr('rail.due', { count: data.dueCount }),\n ...data.due.map(d => createElement('div', { key: d.lessonId, className: 'lks14-dueitem' },\n createElement('span', null, d.lessonTitle),\n d.overdueDays > 0 ? createElement('span', { className: 'lks14-over' }, tr('rail.due.over', { days: d.overdueDays })) : null)),\n createElement('button', {\n className: 'lks-btn ghost',\n style: { marginTop: '6px' },\n onClick: () => { send(tr('prompt.review')) },\n }, tr('rail.due.start')),\n )\n : null,\n ...course.sections.flatMap(section => {\n const examAllowed = examOpen(section.lessons)\n const lessons = section.lessons.filter(l => query.trim() === '' || titleMatches(l.title, query) || l.focus)\n if (lessons.length === 0) return []\n const open = query.trim() !== '' || sectionDefaultOpen(section)\n return [\n createElement('button', {\n key: section.title,\n type: 'button',\n className: 'lks14-sechead',\n 'aria-expanded': String(open),\n title: open ? tr('rail.section.collapse') : tr('rail.section.expand', { count: section.lessons.length }),\n onClick: () => { /* sections are always-open in the panel v1; the head is informational */ },\n },\n createElement('span', { className: 'lks14-secnum' }, String(section.index + 1)),\n createElement('span', { className: 'lks14-secheadt' }, section.title),\n createElement('span', { className: 'lks14-secheadc' }, open ? '▾' : '▸')),\n ...(open ? lessons.map(lesson => {\n const locked = lesson.status === 'locked' || (lesson.kind === 'exam' && !examAllowed)\n return createElement('button', {\n key: lesson.id,\n type: 'button',\n className: `lks14-node${lesson.focus ? ' focus' : ''}`,\n 'aria-disabled': locked || undefined,\n title: `${lesson.title} — ${statusTitle(lesson.kind, locked ? 'locked' : lesson.status)}`,\n onClick: () => {\n // Upstream alignment: selecting a lesson only FOCUSES it —\n // the state-side attempt runs host-side, zero LLM traffic.\n if (locked) return\n void setFocus(lesson.id).catch(reportError)\n },\n },\n createElement('span', { className: 'lks14-g' }, statusIcon(lesson.kind, locked ? 'locked' : lesson.status)),\n createElement('span', { className: 'lks14-t' }, lesson.title),\n lesson.weakConcepts > 0 ? createElement('span', { className: 'lks14-tag weak', title: tr('tag.weak', { count: lesson.weakConcepts }) }, createElement(IconBoltFill16, { size: 10 }), String(lesson.weakConcepts)) : null,\n lesson.masteryPct !== null\n ? createElement('span', { className: 'lks14-bar', title: tr('tag.mastery', { pct: lesson.masteryPct }) }, createElement('i', { style: { transform: `scaleX(${lesson.masteryPct / 100})` } }))\n : null,\n lesson.masteryPct !== null ? createElement('span', { className: 'lks14-pct' }, `${lesson.masteryPct}%`) : null,\n )\n }) : []),\n ]\n }),\n createElement('button', {\n className: 'lks-btn ghost',\n style: { marginTop: '10px' },\n onClick: () => { setShowImport(!showImport) },\n }, showImport ? tr('rail.import.close') : null, tr('rail.import.toggle')),\n showImport ? createElement(ImportRow, { send }) : null,\n )\n })()\n return createElement('div', { className: 'lks14-col lks14-rail' },\n createElement('div', { className: 'lks14-colhead' }, tr('col.rail')),\n body,\n createElement('div', { className: 'lks-propcard-err' }, error),\n )\n}\n\nfunction ImportRow({ send }: { send: PanelSend }): ReactNode {\n const [url, setUrl] = useState('')\n return createElement('div', { className: 'lks14-import' },\n createElement('div', { className: 'lks14-inputrow' },\n createElement('input', {\n className: 'lks14-search',\n type: 'url',\n placeholder: tr('rail.empty.placeholder'),\n value: url,\n onChange: (e: { target: { value: string } }) => { setUrl(e.target.value) },\n onKeyDown: (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter' && url.trim() !== '') send(tr('prompt.import', { url: url.trim() }))\n },\n }),\n createElement('button', {\n className: 'lks-btn primary',\n disabled: url.trim() === '',\n onClick: () => { send(tr('prompt.import', { url: url.trim() })) },\n }, tr('rail.empty.button')),\n ),\n createElement('div', { className: 'lks14-hint' },\n tr('rail.empty.hint'), createElement('br'),\n tr('rail.empty.hint2')),\n )\n}\n\n/** 中栏:the tutor chat stream with its own composer (upstream ChatStream + ChatComposer). */\nfunction ChatPane({ data, lesson, rows, busy, sendError, draft, setDraft, send, setMode }: {\n data: StudyData\n lesson: StudyData['lesson']\n rows: ReturnType<typeof feedRows>\n busy: boolean\n sendError: string | null\n draft: string\n setDraft: (text: string) => void\n send: PanelSend\n setMode: (mode: 'direct' | 'guide' | 'practice') => Promise<void>\n}): ReactNode {\n const streamEl = useRef<HTMLDivElement | null>(null)\n useEffect(() => {\n const el = streamEl.current\n if (el !== null) el.scrollTop = el.scrollHeight\n }, [rows.length])\n const dormant = data?.active !== true\n const lastAssistant = rows.reduce((acc, row, i) => row.role === 'assistant' ? i : acc, -1)\n const [error, setError] = useState<string | null>(null)\n const fire = (action: Promise<void>): void => {\n action.then(() => { setError(null) }, (err: unknown) => { setError(err instanceof Error ? err.message : String(err)) })\n }\n const starters = lesson?.starters ?? []\n const onComposerKey = (e: ReactKeyboardEvent<HTMLTextAreaElement>): void => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n if (draft.trim() !== '' && !busy) send(draft)\n }\n }\n return createElement('div', { className: 'lks14-col lks14-chat' },\n createElement('div', { className: 'lks14-colhead' },\n tr('col.tutor'),\n lesson !== null ? createElement('span', { className: 'lks14-chatlesson' }, lesson.title) : null,\n createElement('span', { className: 'lks14-pills' },\n ...MODES.map(mode => createElement('button', {\n key: mode.id,\n className: `lks14-pill${data?.mode === mode.id ? ' on' : ''}`,\n 'aria-pressed': String(data?.mode === mode.id),\n 'aria-disabled': dormant || undefined,\n title: dormant ? tr('tutor.dormant') : tr(mode.hintKey),\n onClick: () => { if (!dormant) fire(setMode(mode.id)) },\n }, tr(mode.labelKey))),\n ),\n ),\n createElement('div', { className: 'lks14-stream', ref: streamEl },\n rows.length === 0\n ? createElement('div', { className: 'lks14-empty' },\n dormant ? tr('tutor.dormant') : tr('tutor.empty'), createElement('br'), tr('tutor.empty.hint'))\n : rows.map((row, i) => chatRow(row, !dormant && i === lastAssistant ? { send } : undefined)),\n ),\n createElement('div', { className: 'lks14-starters' },\n ...starters.map(s => createElement('button', {\n key: s.label,\n className: 'lks14-starter',\n 'aria-disabled': dormant || undefined,\n title: dormant ? tr('tutor.dormant') : s.message,\n onClick: () => { if (!dormant) send(s.message) },\n }, s.label)),\n ),\n createElement('div', { className: 'lks14-composer' },\n createElement('textarea', {\n className: 'lks14-composertext',\n placeholder: busy ? tr('composer.busy') : tr('tutor.empty.hint'),\n value: draft,\n rows: 2,\n disabled: dormant,\n onChange: (e: { target: { value: string } }) => { setDraft(e.target.value) },\n onKeyDown: onComposerKey,\n }),\n createElement('button', {\n className: 'lks-btn primary',\n disabled: busy || dormant || draft.trim() === '',\n onClick: () => { send(draft) },\n }, busy ? createElement(IconLoadingOutline16, { size: 13, className: 'lks-spin' }) : null, tr('composer.send')),\n ),\n sendError !== null || error !== null\n ? createElement('div', { className: 'lks-propcard-err' }, sendError ?? error)\n : null,\n )\n}\n\n/** 右栏:the notebook — 讲解/概念图/笔记 tabs (upstream NotebookPanel arrangement). */\nfunction NotebookPane({ data, deleteNote }: { data: StudyData; deleteNote: (lessonId: string, noteId: string) => Promise<void> }): ReactNode {\n const lesson = data?.lesson ?? null\n const [tab, setTab] = useState<'teach' | 'cmap' | 'notes'>('teach')\n const [error, setError] = useState<string | null>(null)\n const [armedNote, setArmedNote] = useState<string | null>(null)\n const [read, setRead] = useState<ReadAloudStatus | null>(null)\n const readCtl = useRef<ReadAloudController | null>(null)\n const [readError, setReadError] = useState<string | null>(null)\n const { tts } = useStudy()\n const proseRef = useRef<HTMLDivElement | null>(null)\n const diagRef = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!armedNote) return\n const disarm = (): void => { setArmedNote(null) }\n const onKey = (e: KeyboardEvent): void => { if (e.key === 'Escape') disarm() }\n window.addEventListener('click', disarm)\n window.addEventListener('keydown', onKey)\n return () => {\n window.removeEventListener('click', disarm)\n window.removeEventListener('keydown', onKey)\n }\n }, [armedNote])\n\n const fire = (action: Promise<void>): void => {\n action.then(() => { setError(null) }, (err: unknown) => { setError(err instanceof Error ? err.message : String(err)) })\n }\n\n const startReading = (): void => {\n if (lesson === null || lesson.speechText.trim() === '') return\n readCtl.current?.stop()\n const sentences = speechSentencesOf(lesson.speechText)\n const voice = storedTtsVoice()\n // Edge-over-dashboard engine: per-run prefetch map (prewarm fills it, speak\n // consumes it) + a currentAudio ref so pause/resume reach real playback.\n const prefetch = new Map<string, Promise<Uint8Array>>()\n let currentAudio: HTMLAudioElement | null = null\n const fetchAudio = (text: string): Promise<Uint8Array> => {\n const hit = prefetch.get(text)\n if (hit !== undefined) { prefetch.delete(text); return hit }\n return tts(text, voice)\n }\n const edgeEngine: SpeechEngine = {\n speak(text: string): Promise<void> {\n return fetchAudio(text).then(buf => new Promise<void>((resolve, reject) => {\n const blobUrl = URL.createObjectURL(new Blob([buf], { type: 'audio/mpeg' }))\n const audio = new Audio(blobUrl)\n currentAudio = audio\n audio.onended = () => { URL.revokeObjectURL(blobUrl); if (currentAudio === audio) currentAudio = null; resolve() }\n audio.onerror = () => { URL.revokeObjectURL(blobUrl); if (currentAudio === audio) currentAudio = null; reject(new Error('audio playback failed')) }\n void audio.play().catch(reject)\n }))\n },\n pause(): void { currentAudio?.pause() },\n resume(): void { currentAudio?.play().catch(() => { /* mid-sentence resume raced the end */ }) },\n cancel(): void { currentAudio?.pause(); currentAudio = null },\n prewarm(text: string): void {\n if (!prefetch.has(text)) prefetch.set(text, tts(text, voice))\n },\n }\n // The system-voice fallback (0.13.0 dual-track): the controller's one-way\n // ratchet switches here the moment Edge synthesis fails.\n const systemEngine: SpeechEngine = {\n speak(text: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const synth = window.speechSynthesis\n if (synth === undefined) { reject(new Error('system speech unavailable')); return }\n const utter = new SpeechSynthesisUtterance(text)\n utter.lang = voice.startsWith('en') ? 'en-US' : 'zh-CN'\n utter.onend = () => { resolve() }\n utter.onerror = () => { reject(new Error('system speech failed')) }\n synth.speak(utter)\n })\n },\n pause(): void { window.speechSynthesis?.pause() },\n resume(): void { window.speechSynthesis?.resume() },\n cancel(): void { window.speechSynthesis?.cancel() },\n }\n const controller = new ReadAloudController(\n sentences.map(s => speakMathInSentence(s)),\n edgeEngine,\n systemEngine,\n s => setRead(s),\n )\n readCtl.current = controller\n void controller.start().catch((err: unknown) => { setReadError(err instanceof Error ? err.message : String(err)) })\n }\n const stopReading = (): void => {\n readCtl.current?.stop()\n setRead(null)\n }\n\n useEffect(() => {\n if (tab !== 'cmap' || diagRef.current === null || lesson === null) return\n const el = diagRef.current\n el.textContent = ''\n const run = lesson.concepts.length === 0\n ? Promise.reject(new Error('no concepts'))\n : renderLessonConceptMap(el, lesson.title, lesson.concepts.map(c => ({ title: c.title, masteryPct: c.masteryPct })))\n run.catch((err) => {\n el.textContent = lesson.concepts.length === 0 ? tr('bb.fallback.cmap.empty') : tr('bb.fallback.cmap')\n void err\n })\n }, [tab, lesson?.lessonId, lesson?.concepts.length])\n\n useEffect(() => {\n if (tab !== 'teach' || proseRef.current === null || lesson === null) return\n void enhanceRendered(proseRef.current).catch(() => { /* degrade */ })\n }, [tab, lesson?.html])\n\n const body: ReactNode = lesson === null\n ? createElement('div', { className: 'lks14-empty' }, tr('bb.empty'), createElement('br'), tr('bb.empty.hint'))\n : createElement('div', { className: 'lks14-notebody' },\n createElement('div', { className: 'lks14-lessonhead' },\n createElement('h2', null, lesson.title),\n createElement('div', { className: 'lks14-meta' },\n `${lesson.courseTitle} · ${statusTitle('study', lesson.status)}`\n + (lesson.masteryPct === null ? '' : ` · ${tr('bb.mastery', { pct: lesson.masteryPct })}`)),\n ),\n createElement('div', { className: 'lks14-viewtabs' },\n createElement('button', { className: `lks14-viewtab${tab === 'teach' ? ' on' : ''}`, 'aria-pressed': String(tab === 'teach'), onClick: () => { setTab('teach') } }, tr('viewtab.teach')),\n createElement('button', { className: `lks14-viewtab${tab === 'cmap' ? ' on' : ''}`, 'aria-pressed': String(tab === 'cmap'), title: tr('viewtab.cmap.title'), onClick: () => { setTab('cmap') } }, createElement(IconGlobeOutline14, { size: 13 }), tr('viewtab.cmap')),\n createElement('button', { className: `lks14-viewtab${tab === 'notes' ? ' on' : ''}`, 'aria-pressed': String(tab === 'notes'), onClick: () => { setTab('notes') } }, tr('bb.notes')),\n ),\n tab === 'teach'\n ? createElement('div', null,\n createElement('div', { className: 'lks-readbar' },\n createElement('button', {\n className: 'lks-btn ghost',\n style: { padding: '3px 8px', fontSize: '12.5px', flex: 'none' },\n title: read !== null && read.state === 'speaking' ? tr('read.pause') : tr('read.play'),\n onClick: () => {\n if (read !== null && read.state === 'speaking') { readCtl.current?.pause(); return }\n if (read !== null && read.state === 'paused') { readCtl.current?.resume(); return }\n startReading()\n },\n }, read !== null && read.state === 'speaking' ? tr('read.pause') : tr('read.play')),\n read !== null\n ? createElement('button', {\n className: 'lks-btn ghost',\n style: { padding: '3px 8px', fontSize: '12.5px', flex: 'none' },\n title: tr('read.stop'),\n onClick: stopReading,\n }, tr('read.stop'))\n : null,\n read !== null && read.state !== 'idle' && lesson !== null\n ? createElement('span', { className: 'lks-readbar-cur' },\n (speechSentencesOf(lesson.speechText)[read.index] ?? '').slice(0, 80))\n : null,\n readError !== null ? createElement('span', { className: 'lks-readbar-notice' }, readError) : null,\n read !== null && read.degraded ? createElement('span', { className: 'lks-readbar-notice' }, tr('read.engine.system')) : null,\n ),\n createElement('div', { className: 'lks14-prose', ref: proseRef, dangerouslySetInnerHTML: { __html: lesson.html } }),\n )\n : tab === 'cmap'\n ? createElement('div', { className: 'lks14-prose', ref: diagRef })\n : createElement('div', { className: 'lks14-zones' },\n lesson.notes.length === 0\n ? createElement('div', { className: 'lks14-empty', style: { padding: '16px 0' } as CSSProperties }, tr('bb.notes.empty'))\n : ZONES.filter(([zone]) => lesson.notes.some(n => n.zone === zone)).map(([zone, labelKey]) =>\n createElement('div', { key: zone, className: 'lks14-zone' },\n createElement('div', { className: 'lks14-zoneh' }, tr(labelKey)),\n ...lesson.notes.filter(n => n.zone === zone).map(n => createElement('div', { key: n.id, className: 'lks-note' },\n createElement('span', { className: 'lks-note-src' }, n.source),\n createElement('button', {\n className: `lks-note-del${armedNote === n.id ? ' armed' : ''}`,\n title: armedNote === n.id ? tr('note.delete.confirm') : tr('note.delete'),\n 'aria-label': armedNote === n.id ? tr('note.delete.confirm') : tr('note.delete'),\n onClick: (e: { stopPropagation: () => void }) => {\n e.stopPropagation()\n if (armedNote !== n.id) { setArmedNote(n.id); return }\n setArmedNote(null)\n fire(deleteNote(lesson.lessonId, n.id))\n },\n }, createElement(IconTrashOutline16, { size: 12 })),\n createElement('div', { className: 'lks-note-title' }, n.title),\n createElement('div', { className: 'lks-note-text', dangerouslySetInnerHTML: { __html: renderMarkdown(n.text) } }),\n n.quote !== null ? createElement('div', { className: 'lks-note-q' }, `“${n.quote}”`) : null,\n )),\n )),\n ),\n )\n return createElement('div', { className: 'lks14-col lks14-note' },\n createElement('div', { className: 'lks14-colhead' }, tr('col.bb')),\n body,\n createElement('div', { className: 'lks-propcard-err' }, error),\n )\n}\n","/**\n * The study shell entry — the panel-entry doctrine, stardeck-style: the study\n * surface is CROSS-WORKSPACE (plugin state is workspace-independent by\n * design), so it enters through a sidebar row and takes over the center\n * column, never living inside one conversation. The conversation slot family\n * is single-occupant and external plugins cannot declare shell slots, so both\n * the row and the view are DOM-level: a plain-DOM button (never disturbs the\n * shell's reconciliation) + a container appended to the center column as an\n * extra trailing child React never manages. Visibility toggles via an <html>\n * data attribute; sibling panels evict each other through the dsh-panel-activate\n * event.\n *\n * Differences from stardeck's original: our own attribute namespace, a\n * hand-back SUPPRESSION hook (the panel itself calls sessions.open to stage\n * the focus lesson's thread — an internal navigation must not close the\n * panel; only USER session navigation hands the column back), and dsw-token\n * styling via the injected stylesheet.\n * @module dsh-plugin-lookatstudy/client/shell-entry\n */\n\nimport { createElement } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport type { ReactNode } from 'react'\n\nconst ROW_ATTRIBUTE = 'data-dsh-lookatstudy-entry'\nconst ROW_SELECTOR = `[${ROW_ATTRIBUTE}]`\nconst VIEW_ATTRIBUTE = 'data-dsh-lookatstudy-view'\nconst ACTIVE_ATTR = 'data-dsh-lookatstudy-active'\n/** Sibling panels' activation attributes, evicted when the study panel opens. */\nconst OTHER_ACTIVE_ATTRS = ['data-dsh-warroom-active', 'data-dsh-taskboard-active', 'data-dsh-ssh-active'] as const\n/** Cross-plugin activation event; detail is the activating panel name. */\nconst ACTIVATE_EVENT = 'dsh-panel-activate'\nconst PANEL_NAME = 'lookatstudy'\n/** Family rows we order against (the sibling-panel entry block). */\nconst FAMILY_SELECTORS = ['[data-dsh-warroom-entry]', '[data-dsh-ssh-entry]', '[data-dsh-taskboard-entry]', ROW_SELECTOR] as const\n\nconst SIDEBAR_COLUMN_SELECTOR = '[data-pane=\"sidebar\"], [class*=\"sidebarCol\"]'\nconst CENTER_COLUMN_SELECTOR = '[data-pane=\"conversation\"], [class*=\"centerCol\"]'\n\n/** Minimal mounting face (injectable for tests). */\ninterface SidebarMountDeps {\n createRoot(container: HTMLElement): { render(node: ReactNode): void; unmount(): void }\n doc?: Document\n}\n\n/** The host session list: current-session changes are the hand-back trigger. */\ninterface SessionListFace {\n list?: {\n getSnapshot(): { current?: unknown }\n subscribe(listener: () => void): () => void\n }\n}\n\n/** The open/close state shared by the sidebar row and the view. */\nclass ShellOpenState {\n private open = false\n private readonly listeners = new Set<() => void>()\n isOpen(): boolean { return this.open }\n setOpen(next: boolean): void {\n if (this.open === next) return\n this.open = next\n for (const l of this.listeners) l()\n }\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n}\n\nfunction sidebarRoot(doc: Document): HTMLElement | undefined {\n const column = doc.querySelector<HTMLElement>(SIDEBAR_COLUMN_SELECTOR)\n if (column === null) return undefined\n const logoOwner = column.querySelector<HTMLElement>('[class*=\"logoRow\"]')?.parentElement\n return logoOwner ?? (column.firstElementChild as HTMLElement | undefined)\n}\n\nfunction newSessionButton(root: HTMLElement): HTMLButtonElement | undefined {\n const nested = root.querySelector<HTMLButtonElement>('button[class*=\"newSession\"]')\n if (nested !== null) return nested\n for (const child of root.children) {\n if (child.tagName === 'BUTTON') return child as HTMLButtonElement\n }\n return undefined\n}\n\n/** A 16px study glyph: an open book with a mastery spark. */\nconst STUDY_ICON = '<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path d=\"M2 3.2C3.2 2.4 4.9 2.2 8 3.4c3.1-1.2 4.8-1 6-.2v9.2c-1.2-.8-2.9-1-6 .2-3.1-1.2-4.8-1-6-.2V3.2z\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linejoin=\"round\"/><path d=\"M8 3.4v9.2\" stroke=\"currentColor\" stroke-width=\"1.2\"/></svg>'\n\n/** The shell surface handle: dispose unmounts everything, close yields the\n * center column back, suppressHandBack wraps internal session opens. */\nexport interface StudyShellHandle {\n dispose(): void\n close(): void\n /** Run `fn` without the session-change hand-back firing (internal opens). */\n suppressHandBack(fn: () => void): void\n}\n\n/**\n * Mount the study shell surface: sidebar row + center-column panel.\n * @param view - the panel COMPONENT (an element factory, e.g. studyPanelView(services)).\n * @param label - the entry label (locale-aware getter, re-read on paints).\n * @param dom - the DOM mounting faces (injectable for tests).\n * @param sessions - the host session list; USER navigation between sessions\n * hands the center column back (closes the panel). Hydration (the first\n * current arriving) and suppressed internal opens do not count.\n */\nexport function mountStudyShell(\n view: () => ReactNode,\n label: () => string,\n dom?: SidebarMountDeps,\n sessions?: SessionListFace,\n): StudyShellHandle {\n if (typeof document === 'undefined') {\n return { dispose: () => {}, close: () => {}, suppressHandBack: (fn) => { fn() } }\n }\n const doc = dom?.doc ?? document\n // DOM-level idempotency: an HMR re-injection must not double-mount.\n if (doc.querySelector(ROW_SELECTOR) !== null) {\n return { dispose: () => {}, close: () => {}, suppressHandBack: (fn) => { fn() } }\n }\n\n const state = new ShellOpenState()\n let suppressed = false\n\n // --- Sidebar row -------------------------------------------------------\n const row = doc.createElement('button')\n row.type = 'button'\n row.setAttribute(ROW_ATTRIBUTE, '')\n row.setAttribute('data-dsh-plugin', 'lookatstudy')\n row.setAttribute('data-dsh-part', 'sidebar-entry')\n row.className = 'lks14-sidebar-row'\n const paintRow = (): void => {\n const text = label()\n row.setAttribute('aria-label', text)\n row.setAttribute('title', text)\n row.innerHTML = `<span class=\"lks14-sidebar-icon\">${STUDY_ICON}</span><span class=\"lks14-sidebar-label\">${text}</span>`\n }\n paintRow()\n row.addEventListener('click', () => state.setOpen(!state.isOpen()))\n\n const placeRow = (): boolean => {\n const root = sidebarRoot(doc)\n if (root === undefined) return false\n const button = newSessionButton(root)\n if (button === undefined) return false\n if (row.parentElement !== root) {\n const logoRow = button.closest('[class*=\"logoRow\"]')\n const base = (logoRow !== null && logoRow.parentElement === root) ? logoRow : button\n const family = Array.from(root.children).filter(\n el => el instanceof HTMLElement && (el as HTMLElement).matches(FAMILY_SELECTORS.join(', ')),\n )\n const anchor = family.length > 0 ? family[family.length - 1]!.nextElementSibling : base.nextElementSibling\n root.insertBefore(row, anchor)\n }\n return true\n }\n\n // --- Center-column view ------------------------------------------------\n let container: HTMLElement | undefined\n let root: { render(node: ReactNode): void; unmount(): void } | undefined\n const roots = dom ?? { createRoot: (el: HTMLElement) => createRoot(el) }\n const ensureView = (): void => {\n if (container !== undefined) return\n const column = doc.querySelector<HTMLElement>(CENTER_COLUMN_SELECTOR)\n if (column === null) return\n container = doc.createElement('div')\n container.setAttribute(VIEW_ATTRIBUTE, '')\n container.setAttribute('data-dsh-plugin', 'lookatstudy')\n container.className = 'lks14-shell-view'\n column.appendChild(container)\n root = roots.createRoot(container)\n root.render(createElement(view))\n }\n\n // --- Visibility + sibling-panel mutual exclusion -----------------------\n const applyActive = (): void => {\n if (state.isOpen()) {\n for (const attr of OTHER_ACTIVE_ATTRS) doc.documentElement.removeAttribute(attr)\n doc.documentElement.setAttribute(ACTIVE_ATTR, '')\n doc.dispatchEvent(new CustomEvent(ACTIVATE_EVENT, { detail: PANEL_NAME }))\n } else {\n doc.documentElement.removeAttribute(ACTIVE_ATTR)\n }\n if (state.isOpen()) row.dataset.active = 'true'\n else delete row.dataset.active\n }\n const onOtherActivate = (event: Event): void => {\n const detail = (event as CustomEvent).detail\n if (detail !== PANEL_NAME) state.setOpen(false)\n }\n // Sidebar context clicks hand the center column back to the conversation\n // (capture phase: close before the shell processes the click).\n const SIDEBAR_ROW_SELECTOR = '[class*=\"sessionRow\"], [class*=\"projectRow\"], [class*=\"searchResultRow\"], [class*=\"searchResultWorkspace\"], [class*=\"newSession\"]'\n const onClickSidebarRow = (event: MouseEvent): void => {\n if (!state.isOpen()) return\n const target = event.target as HTMLElement | null\n if (target === null) return\n if (target.closest(SIDEBAR_ROW_SELECTOR) !== null) state.setOpen(false)\n }\n\n const bodyObserver = new MutationObserver(() => { placeRow(); ensureView() })\n bodyObserver.observe(doc.body, { childList: true, subtree: true })\n doc.addEventListener('click', onClickSidebarRow, true)\n doc.addEventListener(ACTIVATE_EVENT, onOtherActivate)\n // USER session navigation hands the center column back to the conversation\n // (the panel's own sessions.open runs under suppressHandBack, and the\n // hydration flip — undefined → first current — is not a navigation).\n let unsubscribeSessions: (() => void) | undefined\n if (sessions?.list !== undefined) {\n let lastCurrent = sessions.list.getSnapshot().current\n unsubscribeSessions = sessions.list.subscribe(() => {\n const next = sessions.list!.getSnapshot().current\n if (next === lastCurrent) return\n const wasHydration = lastCurrent === undefined\n lastCurrent = next\n if (shouldHandBack(suppressed, wasHydration)) state.setOpen(false)\n })\n }\n const unsubscribe = state.subscribe(applyActive)\n placeRow()\n ensureView()\n applyActive()\n\n return {\n close: () => state.setOpen(false),\n /** Internal session navigation (staging the lesson thread) must not hand\n * the column back — only USER navigation does. */\n suppressHandBack: (fn: () => void): void => {\n suppressed = true\n try { fn() } finally { suppressed = false }\n },\n dispose: () => {\n bodyObserver.disconnect()\n doc.removeEventListener('click', onClickSidebarRow, true)\n doc.removeEventListener(ACTIVATE_EVENT, onOtherActivate)\n unsubscribeSessions?.()\n unsubscribe()\n root?.unmount()\n container?.remove()\n row.remove()\n },\n }\n}\n\n/** Whether a session change should hand the center column back (suppressible). */\nexport function shouldHandBack(suppressed: boolean, wasHydration: boolean): boolean {\n return !suppressed && !wasHydration\n}\n","/**\n * The plugin's settings page: one `settings.section` entry riding the host's\n * settings shell. Teaching style and study mode write through the same host\n * routes the study tab uses (POST /api/mode, /api/active — the latter syncs\n * the tool registry before responding); stats and the state-file path are\n * read-only projections of the shared poll store. Pure display + the store's\n * existing write actions; no new routes, no new requests beyond the poll.\n * @module dsh-plugin-lookatstudy/client/settings\n */\n\nimport { createElement, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { useStudy, TTS_VOICES, storedTtsVoice, storeTtsVoice } from './data.ts'\nimport { tr } from './locale.ts'\nimport { ActionError } from './views.tsx'\n\n/** The three souls in pill order (same shape as the study tab's pills). */\nconst MODES: ReadonlyArray<{ id: 'direct' | 'guide' | 'practice'; labelKey: string; hintKey: string }> = [\n { id: 'direct', labelKey: 'soul.direct', hintKey: 'soul.direct.hint' },\n { id: 'guide', labelKey: 'soul.guide', hintKey: 'soul.guide.hint' },\n { id: 'practice', labelKey: 'soul.practice', hintKey: 'soul.practice.hint' },\n]\n\n/**\n * The settings section component. Props from the settings shell (`close`) are\n * unused — every control stays on the page.\n */\nexport function StudySettingsSection(): ReactNode {\n const { data, setMode, activate } = useStudy()\n const [error, setError] = useState<string | null>(null)\n const [voice, setVoice] = useState(storedTtsVoice)\n const fire = (action: Promise<void>): void => {\n action.then(() => { setError(null) }, (err: unknown) => { setError(err instanceof Error ? err.message : String(err)) })\n }\n const progress = data?.progress\n return createElement('div', { className: 'lks-root lks-settings' },\n createElement('section', { className: 'lks-set-row' },\n createElement('h3', null, tr('settings.mode')),\n createElement('p', { className: 'lks-set-hint' }, tr('settings.mode.hint')),\n createElement('div', { className: 'lks-pills' },\n ...MODES.map(mode => createElement('button', {\n key: mode.id,\n className: `lks-pill${data?.mode === mode.id ? ' on' : ''}`,\n title: tr(mode.hintKey),\n onClick: () => { fire(setMode(mode.id)) },\n }, tr(mode.labelKey))),\n ),\n ),\n createElement('section', { className: 'lks-set-row' },\n createElement('h3', null, tr('settings.studyMode')),\n createElement('p', { className: 'lks-set-hint' }, tr('settings.studyMode.hint')),\n createElement('div', null,\n createElement('button', {\n className: `lks-btn ${data?.active === true ? 'ghost' : 'primary'}`,\n onClick: () => { if (data !== null) fire(activate(!data.active)) },\n }, data?.active === true ? tr('settings.turnOff') : tr('settings.turnOn')),\n createElement('span', { className: 'lks-set-state' }, data?.active === true ? tr('settings.on') : tr('settings.off')),\n ),\n ),\n createElement('section', { className: 'lks-set-row' },\n createElement('h3', null, tr('settings.voice')),\n createElement('p', { className: 'lks-set-hint' }, tr('settings.voice.hint')),\n createElement('div', null,\n createElement('select', {\n className: 'lks-set-select',\n value: voice,\n onChange: (e: { target: { value: string } }) => { storeTtsVoice(e.target.value); setVoice(e.target.value) },\n }, ...TTS_VOICES.map(v => createElement('option', { key: v.id, value: v.id }, tr(v.labelKey)))),\n ),\n ),\n progress === undefined ? null : createElement('section', { className: 'lks-set-row' },\n createElement('h3', null, tr('settings.stats')),\n createElement('ul', { className: 'lks-set-stats' },\n createElement('li', null, tr('settings.stats.courses', { count: data?.courses.length ?? 0 })),\n createElement('li', null, tr('settings.stats.xp', { xp: progress.totalXp, level: progress.level, pct: progress.levelPct })),\n createElement('li', null, tr('settings.stats.today', { xp: progress.todayXp, goal: progress.dailyGoal })),\n createElement('li', null, tr('settings.stats.streak', { days: progress.streak, best: progress.longestStreak, freeze: progress.freezeCount })),\n ),\n ),\n createElement('section', { className: 'lks-set-row' },\n createElement('h3', null, tr('settings.about')),\n createElement('p', { className: 'lks-set-hint' }, tr('settings.about.hint')),\n data?.version ? createElement('a',\n { href: `https://github.com/Kaiji-Z/dsh-plugin-lookatstudy/releases/tag/v${data.version}`, target: '_blank', rel: 'noreferrer' },\n `v${data.version}`) : null,\n ),\n createElement('section', { className: 'lks-set-row' },\n createElement('h3', null, tr('settings.stateFile')),\n createElement('p', { className: 'lks-set-hint' }, tr('settings.stateFile.hint')),\n data === null ? null : createElement('code', { className: 'lks-set-path' }, data.statePath),\n ),\n createElement(ActionError, { error }),\n )\n}\n","/**\n * The composer dock status pill: one `conversation.composer.dock` entry (the\n * ambient readout band under the composer, next to the token stats) showing\n * due reviews / streak / level while the study surface is ACTIVE — dormant\n * installs render nothing at all, the same visibility the tool registry has.\n * Data rides the shared 3 s poll store (no extra requests). Display-only:\n * conversation-view tab switching is ui-conversation-private (the starter\n * button's limitation), so the tooltip points at the Study tab instead.\n * @module dsh-plugin-lookatstudy/client/dock\n */\n\nimport { createElement } from 'react'\nimport { IconBoltFill16, IconFlameFill16 } from './icons.tsx'\nimport type { ReactNode } from 'react'\nimport { useStudy } from './data.ts'\nimport { tr } from './locale.ts'\n\n/** Pure projection of the pill's segments (testable without React). `muted` marks zero-value segments — an empty state must not wear a warning color. */\nexport function dockSegments(progress: { dueCount: number; streak: number; level: number }): ReadonlyArray<{ key: string; text: string; muted: boolean }> {\n return [\n { key: 'due', text: tr('dock.due', { count: progress.dueCount }), muted: progress.dueCount === 0 },\n { key: 'streak', text: tr('dock.streak', { days: progress.streak }), muted: progress.streak === 0 },\n { key: 'lv', text: tr('dock.lv', { level: progress.level }), muted: false },\n ]\n}\n\n/** The dock entry component; renders nothing while data is loading or dormant. */\nexport function StudyDockPill(): ReactNode {\n const { data } = useStudy()\n if (data === null || data.active !== true) return null\n const segments = dockSegments({ dueCount: data.dueCount, streak: data.progress.streak, level: data.progress.level })\n return createElement('span', {\n className: 'lks-root lks-dockpill',\n title: tr('dock.title', { due: data.dueCount, streak: data.progress.streak, level: data.progress.level }),\n },\n ...segments.map(s => createElement('span', { key: s.key, className: `lks-dockseg lks-dock-${s.key}${s.muted ? ' lks-muted' : ''}` },\n s.key === 'due' ? createElement(IconBoltFill16, { size: 11 }) : s.key === 'streak' ? createElement(IconFlameFill16, { size: 11 }) : null, s.text)),\n )\n}\n","/**\n * Keyed `tool.call.toolview` entries: learning-aware cards for the\n * conversation tab's tool rows (our own study tab renders its own chips, but\n * learning also happens in the plain 对话 view). One generic card body over\n * the result's `meta` (the tool's presentationMeta lines — replay-safe by\n * construction) with a per-tool header; `study_record_answer` additionally\n * parses its args for a live ✓/✗ tone while the call is still running.\n * @module dsh-plugin-lookatstudy/client/toolviews\n */\n\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport { tr } from './locale.ts'\n\n/** Structural slice of ToolCallBlock the views consume. */\nexport interface ToolBlockFace {\n kind?: string\n name?: string\n argsRaw?: string\n content?: ReadonlyArray<{ type: string; text?: string }>\n isError?: boolean\n meta?: unknown\n}\n\n/** Props slice the slot passes (ToolCallOwnerProps). */\nexport interface ToolViewPropsFace {\n toolName: string\n block: ToolBlockFace\n}\n\n/** Header glyph per tool (missing tools simply use the generic row). */\nconst TOOL_GLYPHS: Record<string, string> = {\n study_record_answer: '✍️',\n study_lesson: '📘',\n study_due_reviews: '🔁',\n study_exam_result: '🎯',\n}\n\n/** The ✓/✗ tone from record_answer's args; null for other shapes. Pure. */\nexport function answerTone(argsRaw: string | undefined): { correct: boolean; concept: string } | null {\n if (argsRaw === undefined) return null\n try {\n const args = JSON.parse(argsRaw) as { concept?: unknown; correct?: unknown }\n return {\n correct: args.correct === true,\n concept: typeof args.concept === 'string' && args.concept !== '' ? args.concept : tr('chip.unattributed'),\n }\n } catch {\n return null\n }\n}\n\n/** Card lines: the result's meta (string[]) when present, else its text content. Pure. */\nexport function metaLines(block: ToolBlockFace): string[] {\n if (Array.isArray(block.meta)) {\n const lines = (block.meta as unknown[]).filter((x): x is string => typeof x === 'string')\n if (lines.length > 0) return lines\n }\n return (block.content ?? [])\n .filter(b => b.type === 'text' && typeof b.text === 'string')\n .map(b => (b.text as string).split('\\n'))\n .flat()\n .filter(l => l.trim() !== '')\n}\n\n/** Shared card body: header + lines; error results keep their text visible. */\nfunction studyToolCard(toolName: string, block: ToolBlockFace, extra?: ReactNode): ReactNode {\n const lines = metaLines(block)\n const glyph = TOOL_GLYPHS[toolName] ?? '🔧'\n return createElement('div', { className: `lks-tv${block.isError === true ? ' err' : ''}` },\n extra ?? null,\n createElement('span', { className: 'lks-tv-head' }, `${glyph} ${toolName}`),\n lines.length === 0\n ? null\n : createElement('div', { className: 'lks-tv-lines' },\n ...lines.map((line, i) => createElement('div', { key: String(i), className: 'lks-tv-line' }, line)),\n ),\n )\n}\n\n/** study_record_answer: live ✓/✗ tone from args + the graded line from meta. */\nexport function StudyAnswerView({ toolName, block }: ToolViewPropsFace): ReactNode {\n const tone = answerTone(block.argsRaw)\n const chip = tone === null ? null : createElement('span', { className: `lks-tv-chip ${tone.correct ? 'ok' : 'bad'}` },\n tr(tone.correct ? 'chip.correct' : 'chip.wrong', { concept: tone.concept }))\n return studyToolCard(toolName, block, chip)\n}\n\n/** Generic meta-line card for the read-type study tools. */\nexport function StudyLessonView({ toolName, block }: ToolViewPropsFace): ReactNode {\n return studyToolCard(toolName, block)\n}\n\n/** study_due_reviews list card. */\nexport function StudyDueView({ toolName, block }: ToolViewPropsFace): ReactNode {\n return studyToolCard(toolName, block)\n}\n\n/** Stars from the exam card's presentationMeta line (`N★`); null when absent. Pure. */\nexport function examStars(block: ToolBlockFace): { stars: number; best: number } | null {\n const lines = metaLines(block)\n for (const line of lines) {\n const m = /^([0-3])★\\s*\\(?(?:best\\s*([0-3])★)?/.exec(line.trim()) ?? /^(?:本次\\s*)?([0-3])★/.exec(line.trim())\n if (m !== null) return { stars: Number(m[1]), best: Number(m[2] ?? m[1]) }\n }\n return null\n}\n\n/** study_exam_result star card — and the failure moment finally gets its line. */\nexport function StudyExamView({ toolName, block }: ToolViewPropsFace): ReactNode {\n const r = examStars(block)\n const stars = r === null\n ? null\n : createElement('span', { className: 'lks-tv-stars' },\n [0, 1, 2].map(i => createElement('span', { key: i, className: i < r.stars ? 'full' : 'dim' }, '★')),\n createElement('span', { className: 'lks-tv-stars-label' }, tr('tv.exam.stars', { n: r.stars, best: r.best })))\n const fail = r !== null && r.stars === 0 ? createElement('div', { className: 'lks-tv-failnote' }, tr('tv.exam.fail')) : null\n return studyToolCard(toolName, block, createElement('span', null, stars, fail))\n}\n","/**\n * Browser half of dsh-plugin-lookatstudy (`dsh.client`) — since 0.14.0 the\n * study surface is a SIDEBAR-ENTRY PANEL (the stardeck panel-entry doctrine):\n * a 「学习」 row in the sidebar toggles a center-column takeover arranged like\n * upstream LookatStudy's app (rail | tutor chat | notebook). The host is the\n * conversation-model + agent-turn engine; the panel drives it through the\n * session face and renders from the thread's event window. Host conversation\n * surfaces we still ride: the settings section, the composer dock pill, and\n * the keyed tool cards (visible when a lesson thread is opened in the host\n * conversation). Styles inject once; the locale namespace rides the framework\n * service.\n * @module dsh-plugin-lookatstudy/client\n */\n\nimport type { ReactNode } from 'react'\nimport type { ClientContext } from './faces.ts'\nimport { ensureStudyStyles } from './styles.ts'\nimport { studyPanelView, setPanelShell } from './panel.tsx'\nimport { mountStudyShell } from './shell-entry.ts'\nimport { StudySettingsSection } from './settings.tsx'\nimport { StudyDockPill } from './dock.tsx'\nimport { StudyAnswerView, StudyDueView, StudyExamView, StudyLessonView, type ToolViewPropsFace } from './toolviews.tsx'\nimport { registerStudyLocale, setStudyTranslator, studyTranslator, tr, type LocaleServiceFace } from './locale.ts'\n\n/** The toolview components' expected props (structural slice of ToolCallViewProps). */\ntype ToolViewProps = ToolViewPropsFace\n\nexport { feedRows } from './session-feed.ts'\nexport type { ChatRow } from './views.tsx'\n\nexport const inject = ['slots', 'workspaces', 'sessions', 'locale']\n\n/**\n * Register the study surfaces: styles + locale once; the sidebar-entry panel\n * carries the whole study UI; the host-native extras (settings, dock, tool\n * cards) ride their slots as before.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ensureStudyStyles()\n const localeSvc = (ctx as { locale?: LocaleServiceFace }).locale\n ctx.effect(() => registerStudyLocale(localeSvc), 'lookatstudy.locale()')\n setStudyTranslator(studyTranslator(localeSvc))\n\n // The study panel: sidebar row in, center-column takeover out. The host\n // session list rides along so USER navigation hands the column back.\n const shell = mountStudyShell(studyPanelView(ctx), () => tr('tab.label'), undefined, ctx.sessions)\n setPanelShell(shell)\n ctx.effect(() => () => {\n setPanelShell(null)\n shell.dispose()\n }, 'lookatstudy.studyPanel()')\n\n ctx.slots.inject('settings.section', () => ctx.slots.register(\n { name: 'settings.section', id: 'lookatstudy', order: 30, label: () => tr('settings.nav'), locale: 'lookatstudy' },\n StudySettingsSection,\n ))\n ctx.slots.inject('conversation.composer.dock', () => ctx.slots.register(\n { name: 'conversation.composer.dock', id: 'lookatstudy-status', order: 10, locale: 'lookatstudy' },\n StudyDockPill,\n ))\n // Learning-aware cards for the conversation tab's tool rows (keyed by wire\n // tool name; unregistered study tools keep the generic row).\n const toolviews: ReadonlyArray<[string, (props: ToolViewProps) => ReactNode]> = [\n ['study_record_answer', StudyAnswerView],\n ['study_lesson', StudyLessonView],\n ['study_due_reviews', StudyDueView],\n ['study_exam_result', StudyExamView],\n ]\n for (const [tool, component] of toolviews) {\n ctx.slots.inject('tool.call.toolview', () => ctx.slots.register(\n { name: 'tool.call.toolview', key: tool, locale: 'lookatstudy' },\n component,\n ))\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["createElement","useSyncExternalStore","createElement","MODES","createElement","useState","useRef","createRoot","createElement","useState","createElement","createElement","createElement"],"sources":["../src/client/styles.ts","../src/client/icons.tsx","../src/client/data.ts","../src/markdown.ts","../src/vendor/mermaid-elk-rewrite.ts","../src/client/enhance.ts","../src/vendor/cmap-elk-layout.ts","../src/client/locale.ts","../src/client/diagrams.ts","../src/vendor/speech-text.ts","../src/vendor/math-speech.ts","../src/client/session-feed.ts","../src/client/readaloud.ts","../src/client/views.tsx","../src/client/panel.tsx","../src/client/shell-entry.ts","../src/client/settings.tsx","../src/client/dock.tsx","../src/client/toolviews.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * Style injection for the study surfaces: one `<style>` element carrying the\n * `.lks14-*` panel classes (the sidebar-entry center-takeover, arranged like\n * upstream LookatStudy) plus the shared `.lks-*` classes the host-native\n * surfaces still use (toolview cards, dock pill, settings section, note\n * cards, read-aloud bar). Authored against the dsh `--dsw-*` design tokens so\n * everything follows the app's theme instead of carrying its own palette.\n * CSS Modules are unavailable to patch-layer bundles, so the stylesheet is\n * injected once at client-plugin apply; the `data-` attribute keeps the\n * injection idempotent.\n * @module dsh-plugin-lookatstudy/client/styles\n */\n\n/** Marker attribute proving the stylesheet is already in the document. */\nconst STYLE_ID = 'data-dsh-plugin-lookatstudy'\n\n/**\n * The study stylesheet. The center-takeover visibility follows the stardeck\n * panel doctrine: the shell view is display:none until the `<html>` active\n * attribute flips, and the host conversation column's own children are hidden\n * behind `!important` so React never fights the takeover.\n */\nexport const STUDY_CSS = `\n/* State inks: small colored text must clear WCAG AA (4.5:1), but the host's\n raw state tokens (success/warn primaries) sit at 2.3–2.6:1 on light\n backgrounds, and success/error *label* aliases are not published by this\n host at all (the tv-chip used to reference them and silently inherit).\n Mixing the state color toward label-primary keeps the hue while pulling\n luminance to the readable pole — and works in BOTH themes, because\n label-primary is always the current theme's high-contrast ink. Declared on\n every surface family that consumes the inks: .lks-root (dock pill,\n settings) and .lks-tv (toolview cards, which render in the host\n conversation with no .lks-root ancestor). */\n.lks-root,.lks-tv{--lks-warn-ink:color-mix(in srgb,var(--dsw-alias-state-warn-label) 45%,var(--dsw-alias-label-primary));--lks-ok-ink:color-mix(in srgb,var(--dsw-alias-state-success-primary) 40%,var(--dsw-alias-label-primary));--lks-err-ink:color-mix(in srgb,var(--dsw-alias-state-error-primary) 55%,var(--dsw-alias-label-primary))}\n.lks-root{font-family:var(--dsw-font-family)}\n.lks-root :where(button){font-family:var(--dsw-font-family);cursor:pointer;border:none;background:none;padding:0}\n\n/* ── sidebar entry row (DOM-injected next to the session family) ── */\n.lks14-sidebar-row{display:flex;align-items:center;gap:9px;width:100%;padding:7px 10px;border:0;background:transparent;color:var(--dsw-alias-label-secondary);font-size:13px;font-family:var(--dsw-font-family);cursor:pointer;border-radius:8px;text-align:left}\n.lks14-sidebar-row:hover{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.lks14-sidebar-row[data-active='true']{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font-weight:600}\n.lks14-sidebar-row:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-sidebar-icon{display:inline-flex;align-items:center;flex:0 0 auto;color:currentColor}\n.lks14-sidebar-label{white-space:nowrap}\n/* Host sidebar collapsed to the icon rail: hide the label, center the icon. */\n[class*='_collapsed'] .lks14-sidebar-row{justify-content:center;padding:7px 0;gap:0}\n[class*='_collapsed'] .lks14-sidebar-label{display:none}\n\n/* ── center-column takeover (the study panel) ── */\n.lks14-shell-view{display:none}\nhtml[data-dsh-lookatstudy-active] .lks14-shell-view{display:flex;flex-direction:column;height:100%;min-height:0}\nhtml[data-dsh-lookatstudy-active] [data-pane='conversation'] > :not([data-dsh-lookatstudy-view]),\nhtml[data-dsh-lookatstudy-active] [class*='centerCol'] > :not([data-dsh-lookatstudy-view]){display:none !important}\n[data-dsh-lookatstudy-view]{container-type:inline-size}\n.lks14{flex:1;min-height:0;display:flex;flex-direction:column;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);overflow:hidden;font-family:var(--dsw-font-family)}\n.lks14 button{font-family:var(--dsw-font-family);cursor:pointer}\n.lks14-body{display:flex;flex:1;min-width:0;min-height:0}\n.lks14-col{display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden}\n.lks14-colhead{flex:none;display:flex;align-items:center;gap:8px;padding:10px 12px;font-size:13px;font-weight:600;color:var(--dsw-alias-label-secondary);border-bottom:1px solid var(--dsw-alias-border-l1)}\n\n/* 左 rail: course picker, tree, review, import */\n.lks14-rail{flex:0 0 240px;border-right:1px solid var(--dsw-alias-border-l1);padding:0 10px 10px;overflow-y:auto}\n.lks14-railhead{display:flex;align-items:center;gap:6px;margin-top:10px}\n.lks14-railtitle{font-weight:600;font-size:13.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1}\n.lks14-railsub{font-size:12px;color:var(--dsw-alias-label-secondary);margin:6px 0}\n.lks14-masterybar{height:5px;border-radius:3px;background:var(--dsw-alias-bg-layer-2);overflow:hidden;margin-bottom:8px}\n.lks14-masterybar i{display:block;height:100%;background:var(--dsw-alias-business-primary);transform-origin:left;transition:transform .3s}\n.lks14-masterybar.gold i{background:var(--dsw-alias-state-warn-primary)}\n.lks14-search{width:100%;box-sizing:border-box;font:inherit;font-size:12.5px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:5px 8px;margin:4px 0}\n.lks14-search:focus{outline:none;border-color:var(--dsw-alias-state-business-primary)}\n.lks14-duebox{border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:8px 10px;margin:8px 0;font-size:12.5px}\n.lks14-dueitem{display:flex;justify-content:space-between;gap:6px;padding:2px 0;color:var(--dsw-alias-label-secondary)}\n.lks14-over{color:var(--dsw-alias-state-error-primary)}\n.lks14-sechead{display:flex;align-items:center;gap:6px;width:100%;background:none;border:none;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12.5px;font-weight:600;padding:6px 2px;cursor:pointer;text-align:left}\n.lks14-sechead:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-secnum{flex:none;width:18px;height:18px;border-radius:5px;background:var(--dsw-alias-bg-layer-2);display:inline-flex;align-items:center;justify-content:center;font-size:11px}\n.lks14-secheadt{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.lks14-node{display:flex;align-items:center;gap:6px;width:100%;background:none;border:none;color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;padding:5px 6px;border-radius:8px;cursor:pointer;text-align:left}\n.lks14-node:hover{background:var(--dsw-alias-bg-layer-1)}\n.lks14-node:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-node.focus{background:var(--dsw-alias-bg-layer-2)}\n.lks14-node[aria-disabled='true']{color:var(--dsw-alias-label-tertiary);cursor:default}\n.lks14-g{flex:none;display:inline-flex}\n.lks14-t{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.lks14-tag{flex:none;display:inline-flex;align-items:center;gap:2px;font-size:10.5px;border-radius:6px;padding:1px 4px}\n.lks14-tag.weak{color:var(--dsw-alias-state-warn-primary)}\n.lks14-bar{flex:none;width:26px;height:4px;border-radius:2px;background:var(--dsw-alias-bg-layer-2);overflow:hidden}\n.lks14-bar i{display:block;height:100%;background:var(--dsw-alias-business-primary);transform-origin:left}\n.lks14-pct{flex:none;font-size:10.5px;color:var(--dsw-alias-label-tertiary)}\n.lks14-empty{color:var(--dsw-alias-label-tertiary);font-size:13px;padding:18px 6px;line-height:1.7}\n.lks14-import{margin:8px 0}\n.lks14-inputrow{display:flex;gap:6px}\n.lks14-hint{font-size:11.5px;color:var(--dsw-alias-label-tertiary);margin-top:6px;line-height:1.6}\n\n/* 中 chat: the tutor stream + its own composer (upstream ChatStream/ChatComposer) */\n.lks14-chat{flex:1 1 46%;min-width:340px;border-right:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-0, transparent)}\n.lks14-chatlesson{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:400;color:var(--dsw-alias-label-tertiary)}\n.lks14-pills{display:inline-flex;gap:2px}\n.lks14-pill{border:none;background:none;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:12px;padding:2px 8px;border-radius:7px;cursor:pointer}\n.lks14-pill:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-pill.on{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.lks14-stream{flex:1;overflow-y:auto;padding:12px 14px;display:flex;flex-direction:column;gap:10px}\n.lks14-msg{max-width:92%;line-height:1.65;font-size:13.5px}\n.lks14-msg-user{align-self:flex-end;background:var(--dsw-alias-bg-layer-2);border-radius:12px 12px 3px 12px;padding:7px 11px;white-space:pre-wrap}\n.lks14-msg-assistant{align-self:flex-start;background:var(--dsw-alias-bg-layer-1);border-radius:12px 12px 12px 3px;padding:8px 12px}\n.lks14-msg.streaming{opacity:.7}\n.lks14-msg-assistant p{margin:4px 0}\n.lks14-msg-assistant pre{overflow-x:auto;font-size:12px}\n.lks14-msg-assistant table{border-collapse:collapse;margin:6px 0;display:block;max-width:100%;overflow-x:auto}\n.lks14-msg-assistant th,.lks14-msg-assistant td{border:1px solid var(--dsw-alias-border-l2);padding:4px 8px;font-size:12px}\n.lks14-turn{max-width:92%;align-self:flex-start;display:flex;flex-direction:column;gap:6px}\n.lks14-quiz{display:flex;flex-direction:column;gap:4px}\n.lks14-opt{display:flex;gap:6px;align-items:baseline;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:9px;padding:6px 10px;font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);cursor:pointer;text-align:left}\n.lks14-opt:hover{background:var(--dsw-alias-bg-layer-2)}\n.lks14-opt:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-optletter{flex:none;font-weight:700;color:var(--dsw-alias-business-primary)}\n.lks14-starters{flex:none;display:flex;flex-wrap:wrap;gap:4px;padding:6px 12px}\n.lks14-starter{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;border-radius:14px;padding:3px 10px;cursor:pointer}\n.lks14-starter:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}\n.lks14-starter:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-starter[aria-disabled='true']{opacity:.5;cursor:default}\n.lks14-composer{flex:none;display:flex;gap:6px;align-items:flex-end;padding:8px 12px 10px;border-top:1px solid var(--dsw-alias-border-l1)}\n.lks14-composertext{flex:1;font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:7px 10px;resize:none;box-sizing:border-box}\n.lks14-composertext:focus{outline:none;border-color:var(--dsw-alias-state-business-primary)}\n\n/* 右 notebook: 讲解/概念图/笔记 (upstream NotebookPanel) */\n.lks14-note{flex:0 0 clamp(320px, 30vw, 460px);padding:0 12px 12px;overflow-y:auto}\n.lks14-lessonhead{margin:10px 0 6px}\n.lks14-lessonhead h2{margin:0 0 4px;font-size:16px}\n.lks14-meta{font-size:12px;color:var(--dsw-alias-label-secondary);margin:2px 0}\n.lks14-viewtabs{display:flex;gap:2px;margin:8px 0}\n.lks14-viewtab{border:none;background:none;color:var(--dsw-alias-label-tertiary);font:inherit;font-size:12.5px;padding:4px 10px;border-radius:8px;cursor:pointer;display:inline-flex;align-items:center;gap:4px}\n.lks14-viewtab:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks14-viewtab.on{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}\n.lks14-prose{font-size:13.5px;line-height:1.7;color:var(--dsw-alias-label-primary);min-height:200px}\n.lks14-prose h1,.lks14-prose h2,.lks14-prose h3{font-size:15px;margin:12px 0 4px}\n.lks14-prose p{margin:6px 0}\n.lks14-prose pre{overflow-x:auto;font-size:12px;background:var(--dsw-alias-bg-layer-1);border-radius:8px;padding:8px}\n.lks14-prose code{font-family:var(--dsw-font-markdown-code);background:var(--dsw-alias-bg-layer-1);border-radius:4px;padding:1px 4px;font-size:.95em}\n.lks14-prose table{border-collapse:collapse;margin:6px 0;display:block;max-width:100%;overflow-x:auto}\n.lks14-prose th,.lks14-prose td{border:1px solid var(--dsw-alias-border-l2);padding:4px 8px;font-size:12.5px}\n.lks14-prose svg{max-width:100%;height:auto}\n.lks14-prose .katex-display{overflow-x:auto}\n.lks14-zones{min-height:200px}\n.lks14-zone{margin-bottom:12px}\n.lks14-zoneh{font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-secondary);margin-bottom:6px}\n\n/* note cards (shared: panel notebook + settings-era shapes) */\n.lks-note{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:10px 14px;margin-bottom:8px}\n.lks-note .lks-note-src{float:right;font-size:11.5px;color:var(--dsw-alias-label-secondary)}\n.lks-note-del{float:right;clear:right;border:none;background:none;color:var(--dsw-alias-label-tertiary);cursor:pointer;padding:2px;border-radius:5px;line-height:0}\n.lks-note-del:hover{color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2)}\n.lks-note-del.armed{color:#fff;background:var(--dsw-alias-state-error-primary)}\n.lks-note .lks-note-title{font-weight:600;font-size:13px}\n.lks-note .lks-note-text{margin-top:4px;font-size:13px;color:var(--dsw-alias-label-secondary);line-height:1.65}\n.lks-note .lks-note-text p{margin:4px 0}\n.lks-note .lks-note-text table{border-collapse:collapse;margin:6px 0;display:block;max-width:100%;overflow-x:auto}\n.lks-note .lks-note-text th,.lks-note .lks-note-text td{border:1px solid var(--dsw-alias-border-l2);padding:4px 8px;font-size:12.5px}\n.lks-note .lks-note-text code{font-family:var(--dsw-font-markdown-code);background:var(--dsw-alias-bg-layer-3);border-radius:4px;padding:1px 4px;font-size:.95em}\n.lks-note .lks-note-q{margin-top:6px;color:var(--dsw-alias-label-tertiary);font-size:12px;border-left:2px solid var(--dsw-alias-state-warn-primary);padding-left:8px}\n\n/* shared buttons + inline error text */\n.lks-btn{display:inline-flex;align-items:center;gap:6px;border-radius:8px;padding:6px 14px;font-size:13.5px;font-weight:600;flex:none;font-family:var(--dsw-font-family);cursor:pointer}\n.lks-btn:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks-btn.primary{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 86%,#000);color:#fff}\n.lks-btn.primary:hover{filter:brightness(1.1)}\n.lks-btn.primary:disabled{opacity:.5;cursor:default;filter:none}\n.lks-btn.ghost{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);border:1px solid var(--dsw-alias-border-l2)}\n.lks-btn.ghost:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}\n.lks-propcard-err{color:var(--dsw-alias-state-error-primary);font-size:12px;margin-top:6px;flex:none}\n\n/* read-aloud bar (notebook teach pane) + busy spinner */\n@keyframes lks-spin{to{transform:rotate(360deg)}}\n.lks-spin{animation:lks-spin .8s linear infinite}\n.lks-readbar{display:flex;align-items:center;gap:6px;margin:0 0 8px;min-height:26px}\n.lks-readbar .lks-btn{display:inline-flex;align-items:center;gap:4px}\n.lks-readbar-cur{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12.5px;color:var(--dsw-alias-label-secondary);border-left:2px solid var(--dsw-alias-state-info-primary,var(--dsw-alias-business-primary));padding-left:8px}\n.lks-readbar-notice{flex:none;font-size:11.5px;color:var(--dsw-alias-label-tertiary)}\n\n/* concept map legend (amber = weak) */\n.lks-cmap-legend{display:inline-flex;align-items:center;gap:5px;font-size:12px;color:var(--dsw-alias-label-secondary);margin-top:4px}\n.lks-cmap-legend i{width:10px;height:10px;border-radius:3px;background:var(--dsw-alias-state-warn-tertiary,#fef5e7);border:1px solid var(--dsw-alias-state-warn-primary,#dd8629)}\n\n/* settings section (settings.section entry inside the host settings shell) */\n.lks-settings{display:flex;flex-direction:column;gap:18px;font-family:var(--dsw-font-family);color:var(--dsw-alias-label-primary)}\n.lks-set-row h3{margin:0 0 4px;font-size:15px;font-weight:600}\n.lks-set-hint{margin:0 0 10px;font-size:13px;color:var(--dsw-alias-label-secondary)}\n.lks-set-state{margin-left:10px;font-size:13px;color:var(--dsw-alias-label-secondary)}\n.lks-set-select{font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:5px 8px;max-width:100%}\n.lks-set-stats{margin:0;padding-left:18px;font-size:14px;line-height:1.9}\n.lks-set-path{font-family:var(--dsw-font-markdown-code);font-size:12.5px;background:var(--dsw-alias-bg-layer-3);border-radius:6px;padding:3px 8px;word-break:break-all}\n\n/* soul pills (settings reuse) */\n.lks-pills{display:inline-flex;align-items:center;height:28px}\n.lks-pill{display:inline-flex;align-items:center;gap:6px;height:28px;padding:0 10px;border:none;border-radius:24px;background:transparent;color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:20px;font-weight:500;transition:background .12s ease,color .12s ease;font-family:var(--dsw-font-family);cursor:pointer}\n.lks-pill:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}\n.lks-pill:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks-pill.on{background:var(--dsw-alias-state-business-tertiary);color:var(--dsw-alias-label-primary-bluish)}\n\n/* composer dock status pill (conversation.composer.dock entry) */\n.lks-dockpill{display:inline-flex;gap:10px;align-items:center;font-size:12px;color:var(--dsw-alias-label-secondary)}\n.lks-dockseg{white-space:nowrap;display:inline-flex;align-items:center;gap:3px}\n.lks-dock-due{color:var(--lks-warn-ink)}\n.lks-dock-streak{color:var(--dsw-alias-state-business-primary)}\n.lks-dockseg.lks-muted{color:var(--dsw-alias-label-secondary)}\n\n/* keyed tool.call.toolview cards (conversation tab tool rows) */\n.lks-tv{display:flex;flex-direction:column;gap:4px;padding:6px 10px;border-radius:10px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);font-size:13px}\n.lks-tv.err{border-color:var(--dsw-alias-state-error-primary)}\n.lks-tv-head{font-weight:600;color:var(--dsw-alias-label-secondary);font-size:12px}\n.lks-tv-chip{align-self:flex-start;border-radius:999px;padding:2px 10px;font-size:12.5px;font-weight:600}\n.lks-tv-chip.ok{background:var(--dsw-alias-state-success-tertiary);color:var(--lks-ok-ink)}\n.lks-tv-chip.bad{background:var(--dsw-alias-state-error-tertiary);color:var(--lks-err-ink)}\n.lks-tv-lines{display:flex;flex-direction:column;gap:4px}\n.lks-tv-line{color:var(--dsw-alias-label-secondary);line-height:1.6;white-space:pre-wrap}\n/* exam star card + the failure moment */\n.lks-tv-stars{display:inline-flex;align-items:center;gap:5px;font-size:13px;font-weight:600;color:var(--lks-warn-ink)}\n.lks-tv-stars .dim{opacity:.22}\n.lks-tv-stars-label{font-weight:400;color:var(--dsw-alias-label-secondary)}\n.lks-tv-failnote{margin-top:4px;font-size:12.5px;color:var(--dsw-alias-label-secondary);line-height:1.6}\n`\n\n/**\n * Inject the study stylesheet into `document.head` (idempotent).\n * @returns the stylesheet element (existing one when already injected).\n */\nexport function ensureStudyStyles(): HTMLStyleElement {\n const existing = document.head.querySelector(`style[${STYLE_ID}]`)\n if (existing !== null) return existing as HTMLStyleElement\n const style = document.createElement('style')\n style.setAttribute(STYLE_ID, '')\n style.textContent = STUDY_CSS\n document.head.appendChild(style)\n return style\n}\n","/**\n * ic_ds_* glyphs vendored from @deepseek-ai/dsh-client-ui-primitives\n * (src/icons/index.tsx, same figma source as the host's icon set) so the\n * plugin's client bundle stays self-contained per the dsh client-bundle\n * contract — only react is runtime-external; importing the host package\n * root would drag its CSS modules into the inlined bundle. Re-sync these\n * three paths if the host icon set redesigns them; scripts/verify.mjs\n * pins the path data.\n *\n * 2026-09-01 critique round: seven more ic_ds glyphs vendored (play/plus/\n * trash/refresh/globe/goal/warning) plus plugin-original learning-domain\n * companions (lock/star/book/crown/bolt/power/pin/flame) drawn in the same\n * language — those are NOT ic_ds, see their section comment below.\n * @module dsh-plugin-lookatstudy/client/icons\n */\n\nimport { createElement } from 'react'\n\n/** Shared props for every vendored glyph (mirrors the host's IconProps). */\ninterface IconProps {\n /** Square edge in px; defaults to the glyph's own drawn size. */\n size?: number\n /** Extra class for layout placement; color rides currentColor. */\n className?: string\n}\n\n/** ic_ds_think_outline_16 */\nexport function IconThinkOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', {\n d: 'M8.00192 6.64454C8.75026 6.64454 9.35732 7.25169 9.35739 8.00001C9.35739 8.74838 8.7503 9.35548 8.00192 9.35548C7.25367 9.35533 6.64743 8.74829 6.64743 8.00001C6.6475 7.25178 7.25371 6.64468 8.00192 6.64454Z',\n fill: 'currentColor',\n }),\n createElement('path', {\n 'fill-rule': 'evenodd',\n 'clip-rule': 'evenodd',\n d: 'M9.97165 1.29981C11.5853 0.718916 13.271 0.642197 14.3144 1.68555C15.3577 2.72902 15.2811 4.41466 14.7002 6.02833C14.4707 6.66561 14.1504 7.32937 13.75 8.00001C14.1504 8.67062 14.4707 9.33444 14.7002 9.97169C15.2811 11.5854 15.3578 13.271 14.3144 14.3145C13.271 15.3579 11.5854 15.2811 9.97165 14.7002C9.3344 14.4708 8.67059 14.1505 7.99997 13.75C7.32933 14.1505 6.66558 14.4708 6.02829 14.7002C4.41461 15.2811 2.72899 15.3578 1.68552 14.3145C0.642155 13.271 0.71887 11.5854 1.29977 9.97169C1.52915 9.33454 1.84865 8.67049 2.24899 8.00001C1.84866 7.32953 1.52915 6.66544 1.29977 6.02833C0.718852 4.41459 0.64207 2.729 1.68552 1.68555C2.72897 0.642112 4.41456 0.718887 6.02829 1.29981C6.66541 1.52918 7.32949 1.8487 7.99997 2.24903C8.67045 1.84869 9.33451 1.52919 9.97165 1.29981ZM12.9404 9.2129C12.4391 9.893 11.8616 10.5681 11.2148 11.2149C10.568 11.8616 9.89296 12.4391 9.21286 12.9404C9.62532 13.1579 10.0271 13.338 10.4121 13.4766C11.9146 14.0174 12.9172 13.8738 13.3955 13.3955C13.8737 12.9173 14.0174 11.9146 13.4765 10.4121C13.3379 10.0271 13.1578 9.62535 12.9404 9.2129ZM3.05856 9.2129C2.84121 9.62523 2.66197 10.0272 2.52341 10.4121C1.98252 11.9146 2.12627 12.9172 2.60446 13.3955C3.08278 13.8737 4.08544 14.0174 5.58786 13.4766C5.97264 13.338 6.37389 13.1577 6.7861 12.9404C6.10624 12.4393 5.43168 11.8614 4.78513 11.2149C4.13823 10.5679 3.55992 9.89313 3.05856 9.2129ZM7.99899 3.792C7.23179 4.31419 6.45306 4.95512 5.70407 5.70411C4.95509 6.45309 4.31415 7.23184 3.79196 7.99903C4.3143 8.76666 4.95471 9.54653 5.70407 10.2959C6.45309 11.0449 7.23271 11.6848 7.99997 12.207C8.76725 11.6848 9.54683 11.0449 10.2959 10.2959C11.0449 9.54686 11.6848 8.76729 12.207 8.00001C11.6848 7.23275 11.0449 6.45312 10.2959 5.70411C9.5465 4.95475 8.76662 4.31434 7.99899 3.792ZM5.58786 2.52344C4.08533 1.98255 3.08272 2.12625 2.60446 2.6045C2.12621 3.08275 1.98252 4.08536 2.52341 5.5879C2.66189 5.97253 2.8414 6.37409 3.05856 6.78614C3.55983 6.10611 4.1384 5.43189 4.78513 4.78516C5.43186 4.13843 6.10606 3.55987 6.7861 3.0586C6.37405 2.84144 5.97249 2.66192 5.58786 2.52344ZM13.3955 2.6045C12.9172 2.12631 11.9146 1.98257 10.4121 2.52344C10.0272 2.66201 9.62519 2.84125 9.21286 3.0586C9.8931 3.55996 10.5679 4.13827 11.2148 4.78516C11.8614 5.43172 12.4392 6.10627 12.9404 6.78614C13.1577 6.37393 13.338 5.97267 13.4765 5.5879C14.0174 4.08549 13.8736 3.08281 13.3955 2.6045Z',\n fill: 'currentColor',\n }))\n}\n\n/** ic_ds_loading_outline_16 */\nexport function IconLoadingOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', {\n d: 'M2.871 13.1286C0.0387669 10.2962 0.0387669 5.70383 2.871 2.87141C5.70341 0.0390029 10.2957 0.0391154 13.1282 2.87141L12.1387 3.86094C9.85292 1.57538 6.1469 1.57596 3.86123 3.86163C1.57573 6.14732 1.57573 9.85269 3.86123 12.1384C6.1469 14.424 9.85292 14.4246 12.1387 12.1391L13.1282 13.1286C10.2957 15.9609 5.70341 15.961 2.871 13.1286Z',\n fill: 'currentColor',\n }))\n}\n\n/** ic_ds_download_outline_16 */\nexport function IconDownloadOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', {\n d: 'M15.3695 11.411L15.1234 12.8866C14.8869 14.3042 13.6603 15.3436 12.223 15.3436H3.77673C2.33958 15.3434 1.1128 14.3042 0.876343 12.8866L0.630249 11.411L2.05408 11.1747L2.29919 12.6493C2.41973 13.3713 3.04475 13.9001 3.77673 13.9003H12.223C12.9551 13.9002 13.58 13.3713 13.7006 12.6493L13.9457 11.1747L15.3695 11.411ZM8.72205 8.994C8.77717 8.93934 8.83792 8.88106 8.90271 8.81627L12.4828 5.23424L13.5043 6.25572L9.92224 9.8358C9.6395 10.1185 9.38763 10.3732 9.15857 10.5575C8.91892 10.7503 8.63953 10.9224 8.2865 10.9784C8.09711 11.0083 7.90363 11.0083 7.71423 10.9784C7.36106 10.9224 7.0809 10.7503 6.84119 10.5575C6.61215 10.3732 6.36022 10.1185 6.07751 9.8358L2.49646 6.25572L3.51697 5.23424L7.09705 8.81627C7.16219 8.88142 7.22331 8.94006 7.27869 8.99498V1.3065H8.72205V8.994Z',\n fill: 'currentColor',\n }))\n}\n\n/** Vendored 2026-09-01 for the critique icon round (same figma source as the three above). */\n/** IconPlayOutline16 */\nexport function IconPlayOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M14.1446 8C14.1446 4.6062 11.3938 1.85539 8 1.85539C4.6062 1.85539 1.85539 4.6062 1.85539 8C1.85539 11.3938 4.6062 14.1446 8 14.1446C11.3938 14.1446 14.1446 11.3938 14.1446 8ZM15.511 8C15.511 12.148 12.148 15.511 8 15.511C3.85202 15.511 0.489014 12.148 0.489014 8C0.489014 3.85202 3.85202 0.489014 8 0.489014C12.148 0.489014 15.511 3.85202 15.511 8Z', fill: 'currentColor' }),\n createElement('path', { d: 'M10.5617 8.42578C10.852 8.21614 10.852 7.78386 10.5617 7.57422L7.25708 5.18751C6.90974 4.93666 6.42436 5.18484 6.42436 5.61329V10.3867C6.42436 10.8152 6.90974 11.0633 7.25708 10.8125L10.5617 8.42578Z', fill: 'currentColor' }))\n}\n\n/** IconPlusOutline16 */\nexport function IconPlusOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M8.64453 1.5V7.34961H14.5V8.65039H8.64453V14.5H7.34473V8.65039H1.5V7.34961H7.34473V1.5H8.64453Z', fill: 'currentColor' }))\n}\n\n/** IconTrashOutline16 */\nexport function IconTrashOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M14.4782 4.84067L14.2138 10.1152C14.1102 12.1872 14.067 13.0115 13.3866 13.9607C13.1044 14.3546 12.7498 14.6912 12.3424 14.9535C11.8239 15.2872 11.2415 15.4316 10.5585 15.4998C9.88727 15.5668 9.04946 15.5656 7.99998 15.5656C6.95051 15.5656 6.1127 15.5668 5.44142 15.4998C4.75851 15.4316 4.17602 15.2872 3.65753 14.9535C3.25012 14.6912 2.89559 14.3546 2.61332 13.9607C1.93296 13.0115 1.88979 12.1872 1.78619 10.1152L1.52179 4.84067L2.89006 4.77277L3.15343 10.0463C3.26221 12.2218 3.32452 12.6015 3.72646 13.1624C3.90825 13.4161 4.13686 13.6334 4.39927 13.8023C4.66204 13.9714 5.00263 14.0792 5.57825 14.1367C6.16562 14.1953 6.92298 14.1963 7.99998 14.1963C9.07699 14.1963 9.83434 14.1953 10.4217 14.1367C10.9973 14.0792 11.3379 13.9714 11.6007 13.8023C11.8631 13.6334 12.0917 13.4161 12.2735 13.1624C12.6755 12.6015 12.7378 12.2218 12.8465 10.0463L13.1099 4.77277L14.4782 4.84067ZM5.43011 6.22849H6.7994V11.3909H5.43011V6.22849ZM9.20056 6.22849H10.5699V11.3909H9.20056V6.22849ZM8.53597 0.434431C9.17976 0.434431 9.6522 0.426926 10.0966 0.571258C10.2357 0.616451 10.3717 0.672554 10.502 0.738948C10.9182 0.951107 11.2464 1.29099 11.7015 1.74612L12.4978 2.54136H15.3742V3.91169H0.625732V2.54136H3.50218L4.29845 1.74612C4.75358 1.29099 5.08174 0.951107 5.49801 0.738948C5.62831 0.672554 5.76425 0.616451 5.90334 0.571258C6.34776 0.426926 6.82021 0.434431 7.46399 0.434431H8.53597ZM7.46399 1.80476C6.73208 1.80476 6.51641 1.81187 6.32617 1.87369C6.25545 1.89667 6.18668 1.92533 6.12041 1.95907C5.96398 2.03878 5.82348 2.16253 5.44142 2.54136H10.5585C10.1765 2.16253 10.036 2.03878 9.87955 1.95907C9.81329 1.92533 9.74452 1.89667 9.6738 1.87369C9.48356 1.81187 9.26789 1.80476 8.53597 1.80476H7.46399Z', fill: 'currentColor' }))\n}\n\n/** IconRefreshOutline16 */\nexport function IconRefreshOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M7.92136 0.349152C10.3744 0.349234 12.5564 1.5052 13.9557 3.29894L15.1281 2.12759C15.3303 1.92546 15.6767 2.06943 15.6767 2.35538V5.53923C15.6766 5.71626 15.5329 5.85976 15.3559 5.86002H12.171C11.8854 5.8597 11.7426 5.51465 11.9443 5.31249L12.9641 4.29056C11.8237 2.74305 9.98908 1.74106 7.92136 1.74097C4.46436 1.74097 1.66233 4.543 1.66233 8C1.66233 11.457 4.46436 14.259 7.92136 14.259C11.3782 14.2589 14.1804 11.4569 14.1804 8H15.5722C15.5722 12.2251 12.1465 15.6507 7.92136 15.6508C3.69614 15.6508 0.270508 12.2252 0.270508 8C0.270508 3.77478 3.69614 0.349152 7.92136 0.349152Z', fill: 'currentColor' }))\n}\n\n/** IconGlobeOutline14 */\nexport function IconGlobeOutline14({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M7.00018 0.353516C10.6708 0.353535 13.6468 3.32958 13.6469 7.00018C13.6468 10.6708 10.6708 13.6468 7.00018 13.6469C3.32957 13.6468 0.353535 10.6708 0.353516 7.00018C0.353535 3.32957 3.32957 0.353531 7.00018 0.353516ZM5.44643 7.59661C5.49463 8.97506 5.70762 10.191 6.02136 11.0793C6.20141 11.5891 6.40328 11.9585 6.59898 12.1889C6.79501 12.4196 6.93213 12.454 7.00018 12.454C7.06822 12.454 7.20533 12.4197 7.40138 12.1889C7.59708 11.9585 7.79895 11.589 7.979 11.0793C8.29274 10.191 8.50574 8.97506 8.55394 7.59661H5.44643ZM1.57861 7.59661C1.80785 9.70467 3.2386 11.4509 5.1715 12.1388C5.07135 11.9317 4.97972 11.7098 4.89746 11.477C4.53084 10.4391 4.30224 9.0828 4.25357 7.59661H1.57861ZM9.74679 7.59661C9.69813 9.0828 9.46952 10.4391 9.1029 11.477C9.0206 11.7099 8.92818 11.9316 8.82797 12.1388C10.7613 11.4511 12.1925 9.70496 12.4218 7.59661H9.74679ZM5.1706 1.8616C3.23814 2.54963 1.80876 4.29604 1.5795 6.40376H4.25357C4.30224 4.91756 4.53083 3.56129 4.89746 2.5234C4.97968 2.29066 5.07051 2.0686 5.1706 1.8616ZM7.00018 1.54637C6.93213 1.54638 6.79503 1.5807 6.59898 1.81145C6.40332 2.04177 6.20139 2.41058 6.02136 2.92012C5.70754 3.80851 5.49461 5.02499 5.44643 6.40376H8.55394C8.50575 5.025 8.29282 3.80851 7.979 2.92012C7.79898 2.41059 7.59705 2.04177 7.40138 1.81145C7.20531 1.58067 7.06823 1.54637 7.00018 1.54637ZM8.82887 1.8616C8.92902 2.0687 9.02064 2.29053 9.1029 2.5234C9.46953 3.56129 9.69812 4.91756 9.74679 6.40376H12.4209C12.1916 4.29575 10.7618 2.54943 8.82887 1.8616Z', fill: 'currentColor' }))\n}\n\n/** IconGoalOutline16 */\nexport function IconGoalOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M8 0C8.31451 0 8.62464 0.019379 8.92969 0.0546875C8.48228 0.403371 8.0952 0.825758 7.78809 1.30469C4.18586 1.41664 1.2998 4.37061 1.2998 8C1.2998 11.7003 4.29969 14.7002 8 14.7002C11.6297 14.7002 14.5829 11.8136 14.6943 8.21094C15.1734 7.90377 15.5956 7.51688 15.9443 7.06934C15.9797 7.37473 16 7.68512 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0ZM7.0166 3.6084C7.00658 3.73765 7 3.86817 7 4C7 4.31845 7.03098 4.62973 7.08789 4.93164C5.76489 5.32438 4.7998 6.54958 4.7998 8C4.7998 9.76731 6.23269 11.2002 8 11.2002C9.45065 11.2002 10.6749 10.2345 11.0674 8.91113C11.3696 8.96818 11.6812 9 12 9C12.1315 9 12.2617 8.99239 12.3906 8.98242C11.9423 10.995 10.1477 12.5 8 12.5C5.51472 12.5 3.5 10.4853 3.5 8C3.5 5.85255 5.00435 4.05702 7.0166 3.6084Z', fill: 'currentColor' }),\n createElement('path', { d: 'M7.5 8.62109L9.12109 7', fill: 'currentColor' }),\n createElement('path', { d: 'M9.08245 3.35798L11.8651 0.575334C11.895 0.545384 11.9463 0.56391 11.9502 0.606086L12.2362 3.69859C12.2384 3.72259 12.2574 3.74159 12.2814 3.74378L15.3697 4.02583C15.4119 4.02968 15.4305 4.08101 15.4005 4.11098L12.618 6.89351C12.6086 6.90289 12.5959 6.90816 12.5826 6.90816L9.11781 6.90815C9.09019 6.90816 9.06781 6.88577 9.06781 6.85816L9.06781 3.39333C9.06781 3.38007 9.07308 3.36735 9.08245 3.35798Z', fill: 'currentColor' }))\n}\n\n/** IconWarningOutline16 */\nexport function IconWarningOutline16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M6.3002 3.32843L7.69986 3.32843L7.69986 7.79657H6.3002L6.3002 3.32843Z', fill: 'currentColor' }),\n createElement('path', { d: 'M6.3002 9.01935H7.69986V10.6711H6.3002V9.01935Z', fill: 'currentColor' }),\n createElement('path', { d: 'M12.6328 6.99976C12.6328 3.88874 10.111 1.36694 7 1.36694C3.88899 1.36695 1.3672 3.88875 1.36719 6.99976C1.36719 10.1108 3.88899 12.6326 7 12.6326C10.111 12.6326 12.6328 10.1108 12.6328 6.99976ZM13.8582 6.99976C13.8582 10.7873 10.7876 13.8579 7 13.8579C3.21244 13.8579 0.141846 10.7873 0.141846 6.99976C0.141857 3.2122 3.21245 0.141612 7 0.141602C10.7876 0.141602 13.8581 3.21219 13.8582 6.99976Z', fill: 'currentColor' }))\n}\n\n\n/**\n * Plugin-original companions for learning-domain glyphs the host set does not\n * carry (lock/star/book/crown/bolt/power/pin/flame) — authored here in the\n * same 16px currentColor filled language as the ic_ds set, but they are NOT\n * ic_ds glyphs and must be re-drawn (not re-synced) if the style drifts.\n */\n/** IconLockFill16 */\nexport function IconLockFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { 'fill-rule': 'evenodd', 'clip-rule': 'evenodd', d: 'M5 6.5V5a3 3 0 0 1 6 0v1.5h.6c.77 0 1.4.63 1.4 1.4v5.7c0 .77-.63 1.4-1.4 1.4H4.4A1.4 1.4 0 0 1 3 13.6V7.9c0-.77.63-1.4 1.4-1.4H5Zm1.444 0h3.112V5a1.556 1.556 0 0 0-3.112 0v1.5Z', fill: 'currentColor' }))\n}\n\n/** IconStarFill16 */\nexport function IconStarFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M8 1.6l1.86 3.77 4.16.6-3.01 2.94.71 4.14L8 11.42l-3.72 1.96.71-4.15-3.01-2.93 4.16-.6L8 1.6Z', fill: 'currentColor' }))\n}\n\n/** IconBookFill16 */\nexport function IconBookFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { 'fill-rule': 'evenodd', 'clip-rule': 'evenodd', d: 'M8 3.1C6.6 2.15 4.65 1.75 2.5 1.75c-.5 0-1 .03-1.5.1v10.7c.5-.07 1-.1 1.5-.1 2.15 0 4.1.4 5.5 1.35 1.4-.95 3.35-1.35 5.5-1.35.5 0 1 .03 1.5.1V1.85c-.5-.07-1-.1-1.5-.1-2.15 0-4.1.4-5.5 1.35ZM7.2 4.9v7.3C5.9 11.55 4.3 11.3 2.6 11.3V3.45c1.7 0 3.3.25 4.6 1.45Zm1.6 0c1.3-1.2 2.9-1.45 4.6-1.45v7.85c-1.7 0-3.3.25-4.6.9V4.9Z', fill: 'currentColor' }))\n}\n\n/** IconCrownFill16 */\nexport function IconCrownFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M1.7 11.2 1 4.9l3.6 2.2L8 2.6l3.4 4.5L15 4.9l-.7 6.3H1.7Zm.5 1.4h11.6v1.5H2.1v-1.5Z', fill: 'currentColor' }))\n}\n\n/** IconBoltFill16 */\nexport function IconBoltFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M9.6 1 3.4 9.3h3.4L6.3 15l6.3-8.5H9.2L9.6 1Z', fill: 'currentColor' }))\n}\n\n/** IconPowerFill16 */\nexport function IconPowerFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M7.2 1h1.6v5.6H7.2V1Z', fill: 'currentColor' }),\n createElement('path', { 'fill-rule': 'evenodd', 'clip-rule': 'evenodd', d: 'M4.55 3.15A6 6 0 1 0 11.45 3.15L12.5 4.2a7.5 7.5 0 1 1-9 0l1.05-1.05Z', fill: 'currentColor' }))\n}\n\n/** IconPinFill16 */\nexport function IconPinFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { 'fill-rule': 'evenodd', 'clip-rule': 'evenodd', d: 'M8 1.4a4.8 4.8 0 0 1 4.8 4.8c0 3.4-4.8 8.4-4.8 8.4S3.2 9.6 3.2 6.2A4.8 4.8 0 0 1 8 1.4Zm0 2.7a2.1 2.1 0 1 0 0 4.2 2.1 2.1 0 0 0 0-4.2Z', fill: 'currentColor' }))\n}\n\n/** IconFlameFill16 */\nexport function IconFlameFill16({ size = 16, className }: IconProps) {\n return createElement('svg', {\n width: size, height: size, className, viewBox: '0 0 16 16', fill: 'none', xmlns: 'http://www.w3.org/2000/svg',\n },\n createElement('path', { d: 'M8.9 1.2c.4 2.5-.9 3.9-2.1 5.2C5.6 7.7 4.4 9 4.4 10.9a3.9 3.9 0 0 0 7.8 0c0-1.6-.7-2.9-1.5-4-.3.8-.8 1.4-1.6 1.7.5-2.4-.2-5.4-1.2-7.4Z', fill: 'currentColor' }))\n}\n\n","/**\n * Shared client-side study store: one module-level poller over the host\n * plugin's `/lookatstudy/api/state` endpoint, plus the three write actions\n * (focus, mode, reverse message). Every seat component binds through\n * {@link useStudy}; subscribers share the single 3 s poll cycle and the last\n * snapshot, so mounting more controls costs no extra requests.\n * @module dsh-plugin-lookatstudy/client/data\n */\n\nimport { useSyncExternalStore } from 'react'\n\n/** Wire shape of `GET /lookatstudy/api/state` (structural mirror of the host's WorkbenchState). */\nexport interface StudyState {\n readonly active: boolean\n readonly mode: 'direct' | 'guide' | 'practice'\n readonly courses: ReadonlyArray<{\n readonly courseId: string\n readonly title: string\n readonly mastered: number\n readonly total: number\n readonly avgMasteryPct: number | null\n readonly sections: ReadonlyArray<{\n readonly title: string\n readonly index: number\n readonly lessons: ReadonlyArray<{\n readonly id: string\n readonly title: string\n readonly kind: 'study' | 'practice' | 'exam'\n readonly status: string\n readonly masteryPct: number | null\n readonly weakConcepts: number\n readonly frictionCount: number\n readonly due: boolean\n readonly focus: boolean\n }>\n }>\n }>\n readonly focusLessonId: string | null\n readonly lesson: {\n readonly lessonId: string\n readonly courseTitle: string\n readonly sectionTitle: string\n readonly title: string\n readonly status: string\n readonly masteryPct: number | null\n readonly strategy: string\n readonly concepts: ReadonlyArray<{ title: string; masteryPct: number; weak: boolean }>\n readonly starters: ReadonlyArray<{ label: string; message: string }>\n readonly notes: ReadonlyArray<{ id: string; zone: string; title: string; text: string; source: string; quote: string | null }>\n readonly html: string\n readonly markdown: string\n readonly speechText: string\n } | null\n readonly dueCount: number\n readonly due: ReadonlyArray<{ lessonId: string; lessonTitle: string; courseTitle: string; overdueDays: number }>\n readonly pendingProposals: ReadonlyArray<{ id: string; lessonTitle: string; rationale: string }>\n readonly memory: { global: string | null; lesson: string | null; pattern: string | null }\n readonly lessonSessions: Readonly<Record<string, string>>\n /** XP + streak block (mirrors study_courses; feeds the dock pill and the settings page). */\n readonly progress: {\n readonly totalXp: number\n readonly level: number\n readonly levelPct: number\n readonly todayXp: number\n readonly dailyGoal: number\n readonly streak: number\n readonly longestStreak: number\n readonly freezeCount: number\n }\n /** Absolute state-file path (read-only display). */\n readonly statePath: string\n /** Plugin version from the running build's package.json (settings About row). */\n readonly version: string\n}\n\n/** Poll cadence for the shared store; one cycle serves every mounted seat. */\nconst POLL_MS = 3_000\n\ntype Listener = () => void\n\nasync function fetchJson(url: string, init?: RequestInit): Promise<unknown> {\n const res = await fetch(url, init)\n const body: unknown = await res.json().catch(() => null)\n if (!res.ok) {\n const error = body !== null && typeof body === 'object' && 'error' in body && typeof body.error === 'string'\n ? body.error\n : `HTTP ${res.status}`\n throw new Error(error)\n }\n return body\n}\n\nclass StudyStore {\n private snapshot: StudyState | null = null\n private readonly listeners = new Set<Listener>()\n private timer: ReturnType<typeof setInterval> | undefined\n private inflight = false\n\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener)\n if (this.listeners.size === 1) {\n void this.tick()\n this.timer = setInterval(() => { void this.tick() }, POLL_MS)\n }\n return () => {\n this.listeners.delete(listener)\n if (this.listeners.size === 0 && this.timer !== undefined) {\n clearInterval(this.timer)\n this.timer = undefined\n }\n }\n }\n\n getSnapshot = (): StudyState | null => this.snapshot\n\n private emit(): void {\n for (const listener of this.listeners) listener()\n }\n\n private async tick(): Promise<void> {\n if (this.inflight) return\n this.inflight = true\n try {\n const next = await fetchJson('/lookatstudy/api/state') as StudyState\n this.snapshot = next\n this.emit()\n } catch {\n // Host route absent (headless composition) or transient failure: keep\n // the last snapshot; seats render their loading/empty states.\n } finally {\n this.inflight = false\n }\n }\n\n /** One immediate re-read (after a write action). */\n refresh(): void {\n void this.tick()\n }\n\n /** Switch the tutor's soul mode directly (host persists and applies it on the next request). */\n async setMode(mode: StudyState['mode']): Promise<void> {\n await fetchJson('/lookatstudy/api/mode', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ mode }),\n })\n this.refresh()\n }\n\n /**\n * Flip learning mode. The host syncs its tool registry before responding,\n * so awaiting this guarantees the `study_*` tools exist for the next prompt.\n */\n async activate(active: boolean): Promise<void> {\n await fetchJson('/lookatstudy/api/active', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ active }),\n })\n this.refresh()\n }\n\n /** Full-text lesson search (rail fallback when local title matching misses). */\n async searchLessons(query: string): Promise<Array<{ lessonId: string; lessonTitle: string; snippet: string }>> {\n const body = await fetchJson(`/lookatstudy/api/search?q=${encodeURIComponent(query)}`) as { matches?: Array<{ lessonId: string; lessonTitle: string; snippet: string }> }\n return body.matches ?? []\n }\n\n /** Point the tutor's focus at one lesson. */\n async setFocus(lessonId: string): Promise<void> {\n await fetchJson('/lookatstudy/api/focus', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ lessonId }),\n })\n this.refresh()\n }\n\n /** Record the dsh session backing one lesson (the simplified thread system). */\n async bindLessonSession(lessonId: string, sessionId: string): Promise<void> {\n await fetchJson('/lookatstudy/api/lesson-session', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ lessonId, sessionId }),\n })\n this.refresh()\n }\n\n /** Delete one course (and its proposals) from the host state. */\n async deleteCourse(courseId: string): Promise<void> {\n await fetchJson('/lookatstudy/api/course/delete', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ courseId }),\n })\n this.refresh()\n }\n\n /** Delete one notebook entry from a lesson's Cornell zones. */\n async deleteNote(lessonId: string, noteId: string): Promise<void> {\n await fetchJson('/lookatstudy/api/note/delete', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ lessonId, noteId }),\n })\n this.refresh()\n }\n\n /** Synthesize one speakable chunk to MP3 (host-side Edge TTS, cache-first). */\n async tts(text: string, voice?: string): Promise<ArrayBuffer> {\n const body = await fetchJson('/lookatstudy/api/tts', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ text, voice }),\n }) as { ok?: boolean; dataBase64?: string }\n const raw = body.dataBase64\n if (body.ok !== true || typeof raw !== 'string') throw new Error('tts response missing audio payload')\n // atob → Uint8Array (binary string decode), Blob-ready for <audio>.\n const bin = atob(raw)\n const bytes = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)\n return bytes.buffer\n }\n}\n\n/** The voices the dashboard route accepts (same allowlist server-side). */\nexport const TTS_VOICES: ReadonlyArray<{ id: string; labelKey: string }> = [\n { id: 'zh-CN-XiaoxiaoNeural', labelKey: 'voice.xiaoxiao' },\n { id: 'zh-CN-YunxiNeural', labelKey: 'voice.yunxi' },\n { id: 'zh-CN-YunyangNeural', labelKey: 'voice.yunyang' },\n { id: 'zh-CN-XiaoyiNeural', labelKey: 'voice.xiaoyi' },\n { id: 'en-US-AriaNeural', labelKey: 'voice.aria' },\n { id: 'en-US-GuyNeural', labelKey: 'voice.guy' },\n]\n\nconst TTS_VOICE_KEY = 'dsh-plugin-lookatstudy:tts-voice'\n\n/** Narrow a stored voice id to the allowlist; anything else falls back to 晓晓. Pure. */\nexport function normalizeStoredVoice(raw: string | null | undefined): string {\n return TTS_VOICES.some(v => v.id === raw) ? raw! : TTS_VOICES[0]!.id\n}\n\n/** The selected read-aloud voice (browser-local preference; host state untouched). */\nexport function storedTtsVoice(): string {\n try {\n return normalizeStoredVoice(typeof localStorage === 'undefined' ? null : localStorage.getItem(TTS_VOICE_KEY))\n } catch {\n return TTS_VOICES[0]!.id\n }\n}\n\nexport function storeTtsVoice(id: string): void {\n try {\n localStorage.setItem(TTS_VOICE_KEY, normalizeStoredVoice(id))\n } catch {\n // Privacy modes forbid storage writes; the picker still reflects this page's choice.\n }\n}\n\n/** The one shared store instance backing every study seat component. */\nexport const studyStore = new StudyStore()\n\n/** React binding: live snapshot plus the write actions. */\nexport function useStudy(): {\n data: StudyState | null\n activate: (active: boolean) => Promise<void>\n setMode: (mode: StudyState['mode']) => Promise<void>\n setFocus: (lessonId: string) => Promise<void>\n searchLessons: (query: string) => Promise<Array<{ lessonId: string; lessonTitle: string; snippet: string }>>\n deleteCourse: (courseId: string) => Promise<void>\n deleteNote: (lessonId: string, noteId: string) => Promise<void>\n bindLessonSession: (lessonId: string, sessionId: string) => Promise<void>\n} {\n const data = useSyncExternalStore(studyStore.subscribe, studyStore.getSnapshot, studyStore.getSnapshot)\n return {\n data,\n activate: studyStore.activate.bind(studyStore),\n setMode: studyStore.setMode.bind(studyStore),\n setFocus: studyStore.setFocus.bind(studyStore),\n searchLessons: studyStore.searchLessons.bind(studyStore),\n deleteCourse: studyStore.deleteCourse.bind(studyStore),\n deleteNote: studyStore.deleteNote.bind(studyStore),\n bindLessonSession: studyStore.bindLessonSession.bind(studyStore),\n }\n}\n","/**\n * Server-side markdown → HTML for the study workbench's 讲解 view. Escapes\n * every HTML character first, then renders a pragmatic GFM subset (headings,\n * fenced code, inline code, bold/italic, links, lists, blockquotes, tables,\n * hr, paragraphs). Lesson bodies are imported teaching material, so raw HTML\n * never passes through.\n * @module dsh-plugin-lookatstudy/markdown\n */\n\n/** Escape all HTML-significant characters. */\nfunction escapeHtml(text: string): string {\n return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"')\n}\n\n/** Render inline markup (code, bold, italic, links, images) over escaped text.\n * Image src allowlist: https?:// or data:image/ — folder imports inline local\n * images as data URLs (capped), GitHub imports reference jsDelivr; anything\n * else stays literal text (no broken relative images, no exotic schemes). */\nfunction inline(escaped: string): string {\n return escaped\n .replace(/`([^`]+)`/g, '<code>$1</code>')\n .replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n .replace(/\\*([^*]+)\\*/g, '<em>$1</em>')\n .replace(/!\\[([^\\]]*)\\]\\((https?:\\/\\/[^)\\s]+|data:image\\/[^)\\s]+)\\)/g, (_m, alt: string, src: string) => `<img src=\"${src}\" alt=\"${alt}\" loading=\"lazy\">`)\n .replace(/\\[([^\\]]+)\\]\\((https?:\\/\\/[^)\\s]+)\\)/g, '<a href=\"$2\" target=\"_blank\" rel=\"noreferrer\">$1</a>')\n}\n\n/** Interleave one body with its translation, paragraph by paragraph (upstream\n * translation-layout \"microsoft\" semantics: original paragraph then its\n * translation as a quote, tight pairing for bilingual reading; length\n * mismatches append the remainder in order). Pure. */\nexport function renderBilingual(bodyMd: string, translationMd: string): string {\n const par = (md: string): string[] => md.split(/\\n\\s*\\n/).map(p => p.trim()).filter(p => p !== '')\n const a = par(bodyMd)\n const b = par(translationMd)\n const out: string[] = []\n for (let i = 0; i < Math.max(a.length, b.length); i++) {\n if (a[i] !== undefined) out.push(a[i]!)\n if (b[i] !== undefined) out.push(`> ${b[i]!.split('\\n').join('\\n> ')}`)\n }\n return out.join('\\n\\n')\n}\n\n/** True when the line opens a GFM table row (pipes with a delimiter row next). */\nfunction isTableRow(line: string): boolean {\n return line.trim().startsWith('|') && line.trim().endsWith('|') && line.includes('|', 1)\n}\n\nfunction isDelimiterRow(line: string): boolean {\n const trimmed = line.trim()\n if (!trimmed.startsWith('|') || !trimmed.endsWith('|')) return false\n const cells = trimmed.replace(/^\\|/, '').replace(/\\|$/, '').split('|').map(c => c.trim())\n return cells.length > 0 && cells.every(c => /^:?-+:?$/.test(c))\n}\n\n/** Split a table row into trimmed cells (leading/trailing pipes removed). */\nfunction rowCells(line: string): string[] {\n return line.trim().replace(/^\\|/, '').replace(/\\|$/, '').split('|').map(c => c.trim())\n}\n\n/**\n * Render markdown text to sanitized HTML.\n * @param md - markdown source (lesson body).\n * @returns HTML string safe to inject into the page.\n */\nexport function renderMarkdown(md: string): string {\n const lines = escapeHtml(md).split(/\\r?\\n/)\n const out: string[] = []\n let i = 0\n\n const flushParagraph = (buffer: string[]): void => {\n if (buffer.length > 0) out.push(`<p>${inline(buffer.join(' '))}</p>`)\n }\n\n let paragraph: string[] = []\n while (i < lines.length) {\n const line = lines[i]!\n\n // Fenced code block\n const fence = /^```(\\w*)\\s*$/.exec(line.trim())\n if (fence) {\n flushParagraph(paragraph)\n paragraph = []\n const lang = fence[1] ?? ''\n const code: string[] = []\n i++\n while (i < lines.length && lines[i]!.trim() !== '```') {\n code.push(lines[i]!)\n i++\n }\n i++ // closing fence\n out.push(`<pre><code${lang === '' ? '' : ` class=\"lang-${lang}\"`}>${code.join('\\n')}</code></pre>`)\n continue\n }\n\n // Heading\n const heading = /^(#{1,6})\\s+(.*)$/.exec(line)\n if (heading) {\n flushParagraph(paragraph)\n paragraph = []\n const level = heading[1]!.length\n out.push(`<h${level}>${inline(heading[2]!)}</h${level}>`)\n i++\n continue\n }\n\n // Horizontal rule\n if (/^\\s*(-{3,}|\\*{3,}|_{3,})\\s*$/.test(line)) {\n flushParagraph(paragraph)\n paragraph = []\n out.push('<hr>')\n i++\n continue\n }\n\n // Blockquote\n if (/^\\s*>\\s?/.test(line)) {\n flushParagraph(paragraph)\n paragraph = []\n const quote: string[] = []\n while (i < lines.length && /^\\s*>\\s?/.test(lines[i]!)) {\n quote.push(lines[i]!.replace(/^\\s*>\\s?/, ''))\n i++\n }\n out.push(`<blockquote><p>${inline(quote.join(' '))}</p></blockquote>`)\n continue\n }\n\n // Unordered list\n if (/^\\s*[-*+]\\s+/.test(line)) {\n flushParagraph(paragraph)\n paragraph = []\n const items: string[] = []\n while (i < lines.length && /^\\s*[-*+]\\s+/.test(lines[i]!)) {\n items.push(`<li>${inline(lines[i]!.replace(/^\\s*[-*+]\\s+/, ''))}</li>`)\n i++\n }\n out.push(`<ul>${items.join('')}</ul>`)\n continue\n }\n\n // Ordered list\n if (/^\\s*\\d+\\.\\s+/.test(line)) {\n flushParagraph(paragraph)\n paragraph = []\n const items: string[] = []\n while (i < lines.length && /^\\s*\\d+\\.\\s+/.test(lines[i]!)) {\n items.push(`<li>${inline(lines[i]!.replace(/^\\s*\\d+\\.\\s+/, ''))}</li>`)\n i++\n }\n out.push(`<ol>${items.join('')}</ol>`)\n continue\n }\n\n // GFM table\n if (isTableRow(line) && i + 1 < lines.length && isDelimiterRow(lines[i + 1]!)) {\n flushParagraph(paragraph)\n paragraph = []\n const headers = rowCells(line)\n i += 2\n const rows: string[] = []\n while (i < lines.length && isTableRow(lines[i]!)) {\n const cells = rowCells(lines[i]!)\n rows.push(`<tr>${cells.map(c => `<td>${inline(c)}</td>`).join('')}</tr>`)\n i++\n }\n out.push(`<table><thead><tr>${headers.map(h => `<th>${inline(h)}</th>`).join('')}</tr></thead><tbody>${rows.join('')}</tbody></table>`)\n continue\n }\n\n // Blank line ends the paragraph\n if (line.trim() === '') {\n flushParagraph(paragraph)\n paragraph = []\n i++\n continue\n }\n\n paragraph.push(line.trim())\n i++\n }\n flushParagraph(paragraph)\n return out.join('\\n')\n}\n","// Vendored from LookatStudy src/renderer/lib/mermaid-elk-rewrite.ts (MIT License, https://github.com/Kaiji-Z/LookatStudy).\n// Unmodified except this provenance header (elkjs dynamic import target adapted to CDN).\n/**\n * mermaid-elk-rewrite —— flowchart → flowchart-elk 前缀改写(v0.21 纯函数)。\n *\n * 只在渲染层改写:LLM 出题提示词(draw_diagram)与语法修复回路\n * (diagram-repair-service)都继续产出/接收**原版 flowchart 语法**,零改动;\n * renderMermaid 喂给 mermaid 前把 flowchart/graph 声明行改成 flowchart-elk ——\n * 同一套语法换 ELK 布局引擎(正交路由,复杂流程图边交叉显著少于 dagre)。\n *\n * mermaid 机制:flowchart-elk 前缀 → detector 置 config.layout=\"elk\" → 查\n * registerLayoutLoaders 注册的 \"elk\" 算法;未注册则静默退 dagre 并 console.warn\n * (ui-test 靠该 warn 断言 ELK 真实生效)。\n *\n * 范围:只改首个内容行的 flowchart 家族声明;sequenceDiagram/stateDiagram-v2/\n * classDiagram 等其余图类型原样返回(ELK 布局只对 flowchart 家族注册)。\n */\n\n/** 首个内容行(跳过空行与 %% 注释/`%%{init}%%` 指令行)的 flowchart 声明改为\n * flowchart-elk;已是 flowchart-elk 则幂等返回;其他图类型不动。 */\nexport function rewriteFlowchartToElk(code: string): string {\n const lines = code.split(\"\\n\");\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? \"\";\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"%%\")) continue;\n if (/^flowchart-elk\\b/i.test(trimmed)) return code;\n if (/^(flowchart|graph)\\b/i.test(trimmed)) {\n lines[i] = line.replace(/^(\\s*)(flowchart|graph)\\b/i, \"$1flowchart-elk\");\n return lines.join(\"\\n\");\n }\n // 首个内容行不是 flowchart 家族 → 其他图类型,不改写\n return code;\n }\n return code;\n}\n","/**\n * Post-render blackboard enhancement (SPEC Phase 2): math (KaTeX), code\n * highlighting (shiki), and mermaid diagrams — every dependency loaded from a\n * CDN at runtime (zero-dependency mandate: nothing enters the bundle), every\n * failure degrading silently to the existing plain rendering. Upstream parity:\n * math-normalize runs host-side before renderMarkdown; the CDN + lazy-singleton\n * pattern follows upstream lazy-shiki/lazy-mermaid, pointed at esm.sh/cdnjs.\n *\n * Test seams: every loader is injectable via `setEnhanceDeps` so node:test\n * proves the enhance/degrade paths offline (the structural-test discipline:\n * prove a gate fails first, then trust its green).\n */\n\n/** Map a lang-xxx class to shiki's grammar id (upstream's curated set, aliases included). */\nconst LANG_ALIAS: Record<string, string> = {\n ts: 'typescript', tsx: 'tsx', js: 'javascript', jsx: 'jsx', mjs: 'javascript', cjs: 'javascript',\n py: 'python', rb: 'ruby', rs: 'rust', 'c++': 'cpp', cs: 'csharp', kt: 'kotlin', sh: 'bash', shell: 'bash', zsh: 'bash',\n yml: 'yaml', md: 'markdown', tex: 'latex', docker: 'dockerfile',\n}\n\nimport { rewriteFlowchartToElk } from '../vendor/mermaid-elk-rewrite.ts'\n\ntype KatexLike = { renderToString(tex: string, opts: Record<string, unknown>): string }\ntype ShikiLike = { codeToHtml(code: string, opts: Record<string, unknown>): Promise<string> }\ntype MermaidLike = { registerLayoutLoaders(l: unknown): void; initialize(cfg: Record<string, unknown>): void; render(id: string, code: string): Promise<{ svg: string }> }\n\nexport interface EnhanceDeps {\n loadKatex?: () => Promise<KatexLike>\n loadShiki?: () => Promise<ShikiLike>\n loadMermaid?: () => Promise<MermaidLike>\n}\nlet deps: EnhanceDeps = {}\nexport function setEnhanceDeps(d: EnhanceDeps | null): void { deps = d ?? {} }\n\n/* ---------------- CDN loaders (production defaults) ---------------- */\n\nconst CDN = {\n katexJs: 'https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js',\n katexCss: 'https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css',\n shiki: 'https://esm.sh/shiki@1.29.2',\n mermaid: 'https://esm.sh/mermaid@11.4.1',\n mermaidElk: 'https://esm.sh/@mermaid-js/layout-elk@0.1.7',\n}\n\nfunction loadScript(src: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const el = document.createElement('script')\n el.src = src\n el.onload = () => { resolve() }\n el.onerror = () => { reject(new Error(`script load failed: ${src}`)) }\n document.head.append(el)\n })\n}\n\nlet katexPromise: Promise<KatexLike> | null = null\nfunction defaultLoadKatex(): Promise<KatexLike> {\n katexPromise ??= (async () => {\n if (!document.querySelector(`link[data-lks-katex]`)) {\n const link = document.createElement('link')\n link.rel = 'stylesheet'\n link.href = CDN.katexCss\n link.setAttribute('data-lks-katex', '1')\n document.head.append(link)\n }\n if ((window as { katex?: KatexLike }).katex === undefined) await loadScript(CDN.katexJs)\n const katex = (window as { katex?: KatexLike }).katex\n if (katex === undefined) throw new Error('katex missing after load')\n return katex\n })()\n return katexPromise\n}\n\nlet shikiPromise: Promise<ShikiLike> | null = null\nfunction defaultLoadShiki(): Promise<ShikiLike> {\n shikiPromise ??= import(/* @vite-ignore */ CDN.shiki).then((mod: Record<string, unknown>) => {\n const create = mod.createHighlighter as ((opts: Record<string, unknown>) => Promise<ShikiLike>) | undefined\n if (create === undefined) throw new Error('shiki createHighlighter missing')\n return create({ themes: ['github-dark', 'github-light'], langs: ['typescript', 'javascript', 'python', 'bash', 'json', 'yaml', 'css', 'html', 'markdown', 'cpp', 'go', 'rust', 'java', 'sql'] })\n }) as Promise<ShikiLike>\n return shikiPromise\n}\n\nlet mermaidPromise: Promise<MermaidLike> | null = null\nfunction defaultLoadMermaid(): Promise<MermaidLike> {\n mermaidPromise ??= (async () => {\n const mod = await import(/* @vite-ignore */ CDN.mermaid) as Record<string, unknown>\n const mermaid = mod.default as MermaidLike\n try {\n const elk = await import(/* @vite-ignore */ CDN.mermaidElk) as Record<string, unknown>\n mermaid.registerLayoutLoaders(elk.default)\n } catch { /* ELK unavailable → mermaid falls back to dagre silently */ }\n mermaid.initialize({ startOnLoad: false, theme: 'base', securityLevel: 'loose' })\n return mermaid\n })()\n return mermaidPromise\n}\n\n/* ---------------- enhancers ---------------- */\n\n/** Split one text node's data around the FIRST math span; returns null when none. */\nfunction findMathSpan(text: string): { before: string; tex: string; after: string; display: boolean } | null {\n const display = /\\$\\$([^$]+)\\$\\$/.exec(text)\n const inline = /\\$([^$\\n]+)\\$/.exec(text)\n if (display === null && inline === null) return null\n const m = display !== null && (inline === null || display.index <= inline.index) ? { ...display, display: true } : inline === null ? null : { ...inline, display: false }\n if (m === null) return null\n return { before: text.slice(0, m.index), tex: m[1]!, after: text.slice(m.index + m[0].length), display: m.display }\n}\n\n/** KaTeX every $..$ / $$..$$ in the container; degrades to plain text on any failure. */\nexport async function enhanceMath(container: HTMLElement): Promise<number> {\n let katex: KatexLike\n try { katex = await (deps.loadKatex ?? defaultLoadKatex)() } catch { return 0 }\n let replaced = 0\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT)\n const targets: Text[] = []\n let node: Node | null\n while ((node = walker.nextNode()) !== null) {\n if (findMathSpan((node as Text).data) !== null && (node as Text).parentElement?.tagName !== 'CODE') targets.push(node as Text)\n }\n for (const t of targets) {\n const span = findMathSpan(t.data)\n if (span === null) continue\n try {\n const html = katex.renderToString(span.tex, { displayMode: span.display, throwOnError: false })\n const holder = document.createElement('span')\n holder.innerHTML = html\n if (span.before !== '') t.before(document.createTextNode(span.before))\n t.before(holder)\n t.data = span.after\n replaced++\n } catch { /* leave the text as-is */ }\n }\n return replaced\n}\n\n/** shiki-highlight every fenced code block; unknown langs and failures keep the plain <pre>. */\nexport async function enhanceCode(container: HTMLElement): Promise<number> {\n let shiki: ShikiLike\n try { shiki = await (deps.loadShiki ?? defaultLoadShiki)() } catch { return 0 }\n let replaced = 0\n const blocks = [...container.querySelectorAll<HTMLElement>('pre > code')]\n for (const code of blocks) {\n if (code.dataset.lksEnhanced === '1') continue\n const langClass = [...code.classList].find(c => c.startsWith('lang-'))\n if (langClass === undefined) continue\n const lang = LANG_ALIAS[langClass.slice(5)] ?? langClass.slice(5)\n try {\n const html = await shiki.codeToHtml(code.textContent ?? '', {\n lang,\n themes: { light: 'github-light', dark: 'github-dark' },\n defaultColor: false,\n cssVariables: { '--shiki-dark': 'var(--shiki-dark, #e6edf3)' },\n })\n code.dataset.lksEnhanced = '1'\n const pre = code.parentElement!\n const wrap = document.createElement('div')\n wrap.className = 'lks-shiki'\n wrap.innerHTML = html\n pre.replaceWith(wrap)\n replaced++\n } catch { code.dataset.lksEnhanced = '1' /* don't retry a failing lang */ }\n }\n return replaced\n}\n\n/** Render mermaid fence blocks to SVG (ELK layout via the registered loader);\n * the flowchart prefix rewrite is upstream's mermaid-elk-rewrite, vendored. */\nexport async function enhanceDiagrams(container: HTMLElement): Promise<number> {\n let mermaid: MermaidLike\n try { mermaid = await (deps.loadMermaid ?? defaultLoadMermaid)() } catch { return 0 }\n let replaced = 0\n const blocks = [...container.querySelectorAll<HTMLElement>('pre > code.lang-mermaid')]\n for (const code of blocks) {\n if (code.dataset.lksEnhanced === '1') continue\n code.dataset.lksEnhanced = '1'\n try {\n const { svg } = await mermaid.render(`lks-mermaid-${replaced}`, rewriteFlowchartToElk(code.textContent ?? ''))\n const wrap = document.createElement('div')\n wrap.className = 'lks-mermaid'\n wrap.innerHTML = svg\n code.parentElement!.replaceWith(wrap)\n replaced++\n } catch { /* keep the code block as-is */ }\n }\n return replaced\n}\n\n/** The full pass over one freshly-rendered container. Never throws. */\nexport async function enhanceRendered(container: HTMLElement): Promise<void> {\n try { await enhanceDiagrams(container) } catch { /* degrade */ }\n try { await enhanceCode(container) } catch { /* degrade */ }\n try { await enhanceMath(container) } catch { /* degrade */ }\n}\n\n","// Vendored from LookatStudy src/renderer/lib/cmap-elk-layout.ts (MIT License, https://github.com/Kaiji-Z/LookatStudy).\n// Unmodified except this provenance header (elkjs dynamic import target adapted to CDN).\n/**\n * cmap-elk-layout —— 概念图 v2 布局(ELK layered,复合分组 + 正交路由)。\n *\n * v0.12 径向布局的视觉评审结论:空白与拥挤并存/边交叉/无层级,且交叉消解要靠\n * 自研启发式。v0.21 换 elkjs(draw.io 新版同款 Eclipse Layout Kernel):\n * - 分组 = ELK 复合图(compound children):组容器盒 + 标题栏留白\n * - 连线 = elk.edgeRouting ORTHOGONAL:直角折线绕开盒子,结构性消灭\"线穿节点\"\n * - 分组来源:LLM show_concept_map 的可选 groups 字段;缺省/无效时客户端按\n * 邻接聚类兜底(clusterByAdjacency,确定性多源 BFS),不回头折腾 prompt\n *\n * 结构分层(全部可单测):\n * resolveGroups / clusterByAdjacency / cmNodeBox / buildElkGraph —— 纯函数\n * flattenElkResult —— 把 ELK 输出递归拍平成绝对坐标(复合嵌套只有一层)\n * layoutConceptMap —— 异步管线,elkjs 走动态 import(lazy chunk,同 mermaid)\n *\n * CJK 估宽/两行包裹沿用 v0.12 已验证的实现(estTextWidth/wrapLabel 原样迁移)。\n * 全链确定性:输入序驱动 + ELK 无随机源,verify-artifact-cmap-v2 断言两次运行\n * 逐坐标全等。\n */\n\n// ============================================================\n// 数据类型(与 artifact-harness ConceptMapData 对齐,groups 为 v2 增列)\n// ============================================================\n\nexport interface CmNode {\n id: string;\n label: string;\n}\nexport interface CmEdge {\n from: string;\n to: string;\n label?: string;\n}\nexport interface CmGroup {\n id: string;\n label: string;\n nodeIds: string[];\n}\n\n// ============================================================\n// 文本度量(v0.12 迁移,CJK 感知)\n// ============================================================\n\n/** 字符宽度估算:CJK 全宽,ASCII 约 0.58em,其余(BBOP…)同 ASCII。 */\nexport function estTextWidth(text: string, fontSize = 13): number {\n let w = 0;\n for (const ch of text) w += /[\\u2e80-\\u9fff\\uf900-\\ufaff\\uff00-\\uffef\\u3000-\\u303f]/.test(ch) ? fontSize : fontSize * 0.58;\n return w;\n}\n\nconst LINE_MAX_PX = 118;\n\n/** 标签包裹:≤2 行;优先自然断点(空格///-),CJK 连续按宽度一半硬拆;仍超宽截断。 */\nexport function wrapLabel(label: string): string[] {\n if (estTextWidth(label) <= LINE_MAX_PX) return [label];\n const breaks: number[] = [];\n for (let i = 0; i < label.length; i++) {\n if (/[\\s/\\-—·]/.test(label[i] ?? \"\")) breaks.push(i);\n }\n const mid = label.length / 2;\n let split = -1;\n let best = Infinity;\n for (const b of breaks) {\n const d = Math.abs(b + 1 - mid);\n if (d < best) {\n best = d;\n split = b;\n }\n }\n let l1: string;\n let l2: string;\n if (split >= 0) {\n l1 = label.slice(0, split + 1).trim();\n l2 = label.slice(split + 1).trim();\n } else {\n let acc = 0;\n let idx = 0;\n for (const ch of label) {\n acc += estTextWidth(ch);\n idx++;\n if (acc >= estTextWidth(label) / 2) break;\n }\n l1 = label.slice(0, idx);\n l2 = label.slice(idx);\n }\n if (estTextWidth(l1) <= LINE_MAX_PX && estTextWidth(l2) <= LINE_MAX_PX) return [l1, l2];\n const fit = (s: string) => {\n let out = \"\";\n for (const ch of s) {\n if (estTextWidth(out + ch + \"…\") > LINE_MAX_PX) break;\n out += ch;\n }\n return out + \"…\";\n };\n return [estTextWidth(l1) > LINE_MAX_PX ? fit(l1) : l1, estTextWidth(l2) > LINE_MAX_PX ? fit(l2) : l2];\n}\n\n/** 节点盒:hub(度数最大或 ≥4)字号/内边距更大;宽高由包裹后行宽决定。 */\nexport function cmNodeBox(label: string, hub: boolean): { width: number; height: number; lines: string[] } {\n const lines = wrapLabel(label);\n const fs = hub ? 14 : 13;\n const textW = Math.max(...lines.map((l) => estTextWidth(l, fs)));\n const padX = hub ? 30 : 24;\n const lh = 17;\n const padY = hub ? 18 : 14;\n return {\n width: Math.min(hub ? 200 : 180, Math.max(hub ? 92 : 76, textW + padX)),\n height: padY + lines.length * lh,\n lines,\n };\n}\n\n// ============================================================\n// 分组解析:LLM groups 优先,邻接聚类兜底\n// ============================================================\n\nexport interface ResolvedGroups {\n groups: CmGroup[];\n /** true = 来自客户端聚类(LLM 未给/无效) */\n fallback: boolean;\n}\n\n/**\n * 净化 LLM groups:丢弃指向不存在节点的 nodeIds、去重(先到先得)、\n * 剩余成员 < 2 的组丢弃;唯一覆盖全部节点的单组视为无效(一个巨容器没有信息量)。\n * 净化后为空 → clusterByAdjacency 兜底。\n */\nexport function resolveGroups(nodes: CmNode[], edges: CmEdge[], input?: CmGroup[] | null): ResolvedGroups {\n const ids = new Set(nodes.map((n) => n.id));\n if (Array.isArray(input) && input.length > 0) {\n const assigned = new Set<string>();\n const groups: CmGroup[] = [];\n for (const g of input) {\n if (!g || typeof g.id !== \"string\" || !g.id) continue;\n const nodeIds = Array.isArray(g.nodeIds)\n ? [...new Set(g.nodeIds.filter((x) => typeof x === \"string\" && ids.has(x) && !assigned.has(x)))]\n : [];\n if (nodeIds.length < 2) continue;\n nodeIds.forEach((x) => assigned.add(x));\n groups.push({ id: g.id, label: typeof g.label === \"string\" && g.label ? g.label : \"分组\", nodeIds });\n }\n if (groups.length > 0 && !(groups.length === 1 && assigned.size === nodes.length && nodes.length > 2)) {\n return { groups, fallback: false };\n }\n }\n return { groups: clusterByAdjacency(nodes, edges), fallback: true };\n}\n\n/**\n * 邻接聚类兜底(确定性):度数 top-k 节点做种子(k = clamp(ceil(n/4),1,3)),\n * 多源 BFS 就近归属(平手先出现的种子赢);孤立节点不归组(根层漂浮,不造假组)。\n * 组名 = 种子标签(≤5 字加\"相关\")。\n */\nexport function clusterByAdjacency(nodes: CmNode[], edges: CmEdge[]): CmGroup[] {\n if (nodes.length < 4) return []; // 太小不分组\n const index = new Map(nodes.map((n, i) => [n.id, i] as const));\n const adj = new Map<string, string[]>();\n for (const n of nodes) adj.set(n.id, []);\n const seenPair = new Set<string>();\n for (const e of edges) {\n if (!index.has(e.from) || !index.has(e.to) || e.from === e.to) continue;\n const key = [e.from, e.to].sort().join(\"→\");\n if (seenPair.has(key)) continue;\n seenPair.add(key);\n adj.get(e.from)!.push(e.to);\n adj.get(e.to)!.push(e.from);\n }\n const ranked = [...nodes].sort((a, b) => {\n const da = adj.get(a.id)!.length;\n const db = adj.get(b.id)!.length;\n return da !== db ? db - da : (index.get(a.id) ?? 0) - (index.get(b.id) ?? 0);\n });\n const k = Math.min(3, Math.max(1, Math.ceil(nodes.length / 4)));\n const seeds = ranked.slice(0, k).filter((s) => (adj.get(s.id)?.length ?? 0) > 0);\n if (seeds.length === 0) return [];\n const owner = new Map<string, number>(); // nodeId → seed 下标\n seeds.forEach((s, si) => owner.set(s.id, si));\n const queue = seeds.map((s) => s.id);\n while (queue.length) {\n const cur = queue.shift()!;\n const si = owner.get(cur)!;\n for (const nb of adj.get(cur) ?? []) {\n if (owner.has(nb)) continue;\n owner.set(nb, si);\n queue.push(nb);\n }\n }\n const groups: CmGroup[] = [];\n seeds.forEach((s, si) => {\n const nodeIds = nodes.filter((n) => owner.get(n.id) === si).map((n) => n.id);\n if (nodeIds.length < 2) return;\n groups.push({\n id: `grp-${s.id}`,\n label: s.label.length <= 5 ? `${s.label}相关` : s.label,\n nodeIds,\n });\n });\n return groups;\n}\n\n// ============================================================\n// ELK 图构建(纯函数,不触 elkjs —— 结构可脱离引擎直测)\n// ============================================================\n\n/** ELK JSON 的最小结构(只声明我们用到的字段;elkjs 原样吃任意附加字段)。 */\nexport interface ElkGraphJSON {\n id: string;\n layoutOptions?: Record<string, string>;\n width?: number;\n height?: number;\n x?: number;\n y?: number;\n children?: ElkGraphJSON[];\n edges?: {\n id: string;\n sources: string[];\n targets: string[];\n /** elkjs 在 hierarchyHandling=INCLUDE_CHILDREN 下把全部边挂到根,坐标属于 container 指向的容器 */\n container?: string;\n sections?: {\n startPoint: { x: number; y: number };\n endPoint: { x: number; y: number };\n bendPoints?: { x: number; y: number }[];\n }[];\n }[];\n}\n\n/** 组容器标题栏高度:ELK 组盒顶部 padding 要为标题留白。 */\nexport const GROUP_TITLE_PX = 34;\n\nexport function buildElkGraph(nodes: CmNode[], edges: CmEdge[], groups: CmGroup[]): ElkGraphJSON {\n const ids = new Set(nodes.map((n) => n.id));\n const degree = new Map<string, number>();\n for (const n of nodes) degree.set(n.id, 0);\n const seenPair = new Set<string>();\n const drawEdges: CmEdge[] = [];\n for (const e of edges) {\n if (!ids.has(e.from) || !ids.has(e.to) || e.from === e.to) continue;\n const key = [e.from, e.to].sort().join(\"→\");\n if (seenPair.has(key)) continue;\n seenPair.add(key);\n drawEdges.push(e);\n degree.set(e.from, degree.get(e.from)! + 1);\n degree.set(e.to, degree.get(e.to)! + 1);\n }\n let hubId = nodes[0]?.id ?? \"\";\n for (const n of nodes) if ((degree.get(n.id) ?? 0) > (degree.get(hubId) ?? 0)) hubId = n.id;\n\n const inGroup = new Map<string, string>(); // nodeId → groupId\n for (const g of groups) for (const nid of g.nodeIds) if (ids.has(nid) && !inGroup.has(nid)) inGroup.set(nid, g.id);\n\n const nodeChild = (n: CmNode): ElkGraphJSON => {\n const box = cmNodeBox(n.label, n.id === hubId || (degree.get(n.id) ?? 0) >= 4);\n return { id: n.id, width: Math.ceil(box.width), height: Math.ceil(box.height) };\n };\n\n const children: ElkGraphJSON[] = [];\n for (const g of groups) {\n const members = g.nodeIds.filter((nid) => inGroup.get(nid) === g.id && ids.has(nid));\n if (members.length === 0) continue;\n children.push({\n id: `g:${g.id}`,\n layoutOptions: { \"elk.padding\": `[top=${GROUP_TITLE_PX},left=14,bottom=12,right=14]` },\n children: members.map((nid) => nodeChild(nodes.find((n) => n.id === nid)!)),\n });\n }\n for (const n of nodes) {\n if (!inGroup.has(n.id)) children.push(nodeChild(n));\n }\n\n return {\n id: \"root\",\n layoutOptions: {\n \"elk.algorithm\": \"layered\",\n \"elk.direction\": \"DOWN\",\n \"elk.edgeRouting\": \"ORTHOGONAL\",\n // 复合图关键开关:layered 默认不处理嵌套(跨组边不路由、无 sections),\n // INCLUDE_CHILDREN 让整个复合图一次分层,跨组边才有正交折线\n \"elk.hierarchyHandling\": \"INCLUDE_CHILDREN\",\n \"elk.spacing.nodeNode\": \"34\",\n \"elk.spacing.edgeNode\": \"26\",\n \"elk.spacing.componentComponent\": \"48\",\n \"elk.layered.spacing.nodeNodeBetweenLayers\": \"56\",\n },\n children,\n edges: drawEdges.map((e, i) => ({ id: `e${i}`, sources: [e.from], targets: [e.to] })),\n };\n}\n\n// ============================================================\n// ELK 输出拍平(复合嵌套一层:root → 组盒 → 节点盒)\n// ============================================================\n\nexport interface CmapNodeGeo {\n id: string;\n x: number; // 左上角(绝对)\n y: number;\n w: number;\n h: number;\n lines: string[];\n hub: boolean;\n groupId: string | null;\n /** 颜色档 = 所属组序号(0-4 循环);无组 = -1(中性) */\n colorIdx: number;\n}\nexport interface CmapGroupGeo {\n id: string;\n label: string;\n x: number;\n y: number;\n w: number;\n h: number;\n colorIdx: number;\n}\nexport interface CmapEdgeGeo {\n from: string;\n to: string;\n label?: string;\n pts: { x: number; y: number }[];\n /** 边标签锚点(折线弧长中点);无标签为 null */\n labelPt: { x: number; y: number } | null;\n}\nexport interface CmapLayout {\n width: number;\n height: number;\n nodes: CmapNodeGeo[];\n groups: CmapGroupGeo[];\n edges: CmapEdgeGeo[];\n hubId: string;\n /** true = 分组来自客户端兜底(调试/未来 UI 提示用) */\n groupsFallback: boolean;\n}\n\n/** 折线弧长中点(标签锚点)。 */\nfunction polylineMidpoint(pts: { x: number; y: number }[]): { x: number; y: number } {\n if (pts.length === 0) return { x: 0, y: 0 };\n if (pts.length === 1) return pts[0];\n const segLens = [];\n let total = 0;\n for (let i = 1; i < pts.length; i++) {\n const l = Math.hypot(pts[i].x - pts[i - 1].x, pts[i].y - pts[i - 1].y);\n segLens.push(l);\n total += l;\n }\n let remain = total / 2;\n for (let i = 0; i < segLens.length; i++) {\n if (remain <= segLens[i] || i === segLens.length - 1) {\n const t = segLens[i] === 0 ? 0 : remain / segLens[i];\n return {\n x: pts[i].x + (pts[i + 1].x - pts[i].x) * t,\n y: pts[i].y + (pts[i + 1].y - pts[i].y) * t,\n };\n }\n remain -= segLens[i];\n }\n return pts[pts.length - 1];\n}\n\nexport function flattenElkResult(\n elkOut: ElkGraphJSON,\n nodes: CmNode[],\n edges: CmEdge[],\n groups: CmGroup[],\n groupsFallback: boolean,\n hubId: string,\n): CmapLayout {\n const nodesById = new Map(nodes.map((n) => [n.id, n] as const));\n const groupColor = new Map<string, number>();\n groups.forEach((g, i) => groupColor.set(g.id, i % 5));\n const groupById = new Map(groups.map((g) => [g.id, g] as const));\n const memberGroup = new Map<string, string>();\n for (const g of groups) for (const nid of g.nodeIds) if (!memberGroup.has(nid)) memberGroup.set(nid, g.id);\n\n const outNodes: CmapNodeGeo[] = [];\n const outGroups: CmapGroupGeo[] = [];\n const outEdges: CmapEdgeGeo[] = [];\n\n // elkjs 把边统一挂到根数组,坐标属于 e.container 指向的容器(INCLUDE_CHILDREN 下)——\n // 必须先收集\"容器 id → 绝对偏移\"表,再按 container 加偏移;按树位置加偏移会让组内边整体错位。\n const containerOff = new Map<string, { x: number; y: number }>([[\"root\", { x: 0, y: 0 }]]);\n const rawEdges: { e: NonNullable<ElkGraphJSON[\"edges\"]>[number]; fallback: { x: number; y: number } }[] = [];\n\n const walk = (elk: ElkGraphJSON, offX: number, offY: number, containerId: string | null) => {\n for (const child of elk.children ?? []) {\n const cx = offX + (child.x ?? 0);\n const cy = offY + (child.y ?? 0);\n if (child.children?.length) {\n const gid = child.id.startsWith(\"g:\") ? child.id.slice(2) : child.id;\n const meta = groupById.get(gid);\n outGroups.push({\n id: gid,\n label: meta?.label ?? \"分组\",\n x: cx,\n y: cy,\n w: child.width ?? 0,\n h: child.height ?? 0,\n colorIdx: groupColor.get(gid) ?? 0,\n });\n containerOff.set(child.id, { x: cx, y: cy });\n walk(child, cx, cy, gid);\n } else {\n const n = nodesById.get(child.id);\n if (!n) continue;\n const box = cmNodeBox(n.label, n.id === hubId);\n const gid = memberGroup.get(n.id) ?? null;\n outNodes.push({\n id: n.id,\n x: cx,\n y: cy,\n w: child.width ?? Math.ceil(box.width),\n h: child.height ?? Math.ceil(box.height),\n lines: box.lines,\n hub: n.id === hubId,\n groupId: containerId,\n colorIdx: gid != null ? groupColor.get(gid) ?? 0 : -1,\n });\n }\n }\n for (const e of elk.edges ?? []) {\n // 记录现场偏移兜底:理论上边也可挂在 LCA 自己的 edges 里(无 container 字段)\n rawEdges.push({ e, fallback: { x: offX, y: offY } });\n }\n };\n walk(elkOut, 0, 0, null);\n\n for (const { e, fallback } of rawEdges) {\n const sec = e.sections?.[0];\n if (!sec) continue;\n const off = (e.container != null ? containerOff.get(e.container) : undefined) ?? fallback;\n const pts = [sec.startPoint, ...(sec.bendPoints ?? []), sec.endPoint].map((p) => ({\n x: p.x + off.x,\n y: p.y + off.y,\n }));\n const idx = Number(e.id.slice(1));\n const src = edges[idx];\n if (!src) continue;\n outEdges.push({ from: src.from, to: src.to, label: src.label, pts, labelPt: src.label ? polylineMidpoint(pts) : null });\n }\n\n return {\n width: Math.ceil(elkOut.width ?? 0),\n height: Math.ceil(elkOut.height ?? 0),\n nodes: outNodes,\n groups: outGroups,\n edges: outEdges,\n hubId,\n groupsFallback,\n };\n}\n\n// ============================================================\n// 异步管线:elkjs 动态 import(独立 lazy chunk)\n// ============================================================\n\ntype ElkInstance = { layout: (graph: ElkGraphJSON) => Promise<ElkGraphJSON> };\nlet elkPromise: Promise<ElkInstance> | null = null;\n\n/** elkjs 单例:elk.bundled.js 主线程版(无 Worker,打包器友好,React Flow 生态同款)。 */\nfunction loadElk(): Promise<ElkInstance> {\n elkPromise ??= import(/* @vite-ignore */ \"https://esm.sh/elkjs@0.12/lib/elk.bundled.js\").then((mod: { default: unknown }) => {\n // interop 双环境:CJS(Node/tsx)default 在 mod.default;打包后 ESM 可能整包即构造器\n const m = mod as unknown as Record<string, unknown>;\n const Ctor = (m.default ?? m) as new () => ElkInstance;\n return new Ctor();\n });\n return elkPromise;\n}\n\n/** 概念图 → 分组解析 → ELK 布局 → 绝对坐标几何。输入相同则输出逐字段全等。 */\nexport async function layoutConceptMap(\n nodes: CmNode[],\n edges: CmEdge[],\n groups?: CmGroup[] | null,\n): Promise<CmapLayout> {\n const resolved = resolveGroups(nodes, edges, groups);\n const graph = buildElkGraph(nodes, edges, resolved.groups);\n const hubId = computeHubId(nodes, edges);\n const elk = await loadElk();\n const out = await elk.layout(graph);\n return flattenElkResult(out, nodes, edges, resolved.groups, resolved.fallback, hubId);\n}\n\nfunction computeHubId(nodes: CmNode[], edges: CmEdge[]): string {\n const ids = new Set(nodes.map((n) => n.id));\n const degree = new Map<string, number>();\n for (const n of nodes) degree.set(n.id, 0);\n const seen = new Set<string>();\n for (const e of edges) {\n if (!ids.has(e.from) || !ids.has(e.to) || e.from === e.to) continue;\n const key = [e.from, e.to].sort().join(\"→\");\n if (seen.has(key)) continue;\n seen.add(key);\n degree.set(e.from, degree.get(e.from)! + 1);\n degree.set(e.to, degree.get(e.to)! + 1);\n }\n let hubId = nodes[0]?.id ?? \"\";\n for (const n of nodes) if ((degree.get(n.id) ?? 0) > (degree.get(hubId) ?? 0)) hubId = n.id;\n return hubId;\n}\n","/**\n * The study tab's locale surface: one `lookatstudy` namespace registered with\n * the framework locale service (zh + en, key sets identical — the service\n * enforces balance at register time and this module's tests re-assert it).\n * `makeT` is the pure lookup (own chain: locale → zh → key, `{name}` template\n * interpolation — the same semantics as the service's translate) so unit\n * tests and non-slot code can translate without a live context;\n * `bindStudyT` prefers the service's stable `bind()` when present.\n * @module dsh-plugin-lookatstudy/client/locale\n */\n\n/** Flat key → template string, `{name}` placeholders (LocaleDict shape). */\nexport type StudyDict = Record<string, string>\n\n/** Simplified Chinese dictionary (also the fallback locale). */\nexport const ZH: StudyDict = {\n 'tab.label': '学习',\n 'col.rail': '课程',\n 'col.tutor': '导师',\n 'col.bb': '黑板',\n 'pane.rail': '课程',\n 'pane.tutor': '导师',\n 'pane.bb': '黑板',\n 'viewtab.teach': '讲解',\n 'viewtab.cmap': '概念图',\n 'viewtab.cmap.title': '本课概念之间的关系图',\n 'loading': '加载中…',\n 'start.title': '一键准备学习:建立学习工作区、开启会话并让导师就位',\n 'start.go': '开始学习',\n 'start.enter': '进入学习',\n 'start.busy': '正在准备学习区…',\n 'soul.direct': '直讲',\n 'soul.direct.hint': '精讲:先讲清楚,再确认懂没懂',\n 'soul.guide': '引导',\n 'soul.guide.hint': '引导:让你自己往前推一步,导师递台阶',\n 'soul.practice': '实战',\n 'soul.practice.hint': '实战:在真实世界的乱问题里学',\n 'zone.understand': '🧠 理解区 — 知识结构',\n 'zone.record': '📝 记录区 — 我的话',\n 'zone.practice': '✍️ 练习区 — 答题日志',\n 'rail.empty.title': '暂无课程',\n 'rail.empty.hint': '粘贴 markdown → 说「导入为课程」',\n 'rail.empty.hint2': '本地文件夹 → 说「导入 D:/path/to/folder」',\n 'rail.empty.placeholder': 'GitHub 仓库链接,如 microsoft/AI-For-Beginners',\n 'rail.empty.button': '导入',\n 'composer.send': '发送',\n 'composer.busy': '导师正在回复…',\n 'rail.empty.demo': '导入示例课程',\n 'rail.mastered': '毕业 {mastered}/{total} 课',\n 'rail.avg': '平均掌握度 {pct}%',\n 'rail.avg.none': '尚无掌握度数据',\n 'rail.search': '搜索课时…(多关键词空格分隔)',\n 'rail.locate.title': '在课程树中定位当前焦点课时(自动展开所在章节)',\n 'rail.locate': '回到当前课时',\n 'rail.due': '待复习 {count}',\n 'rail.due.over': '超{days}天',\n 'rail.due.start': '开始复习',\n 'rail.due.tag': '这课时的复习今天到期(SM-2)',\n 'rail.delete': '删除本课程',\n 'rail.delete.confirm': '确认删除?',\n 'rail.delete.title.confirm': '再点一次确认删除(含全部进度与笔记)。反悔前可先在设置页备份状态文件',\n 'note.delete': '删除本条笔记',\n 'note.delete.confirm': '确认删除?',\n 'read.play': '朗读本课',\n 'read.stop': '停止朗读',\n 'read.pause': '暂停朗读',\n 'read.resume': '继续朗读',\n 'read.engine.system': '网络合成不可用,已切换为系统语音',\n 'read.unavailable': '朗读不可用',\n 'rail.section.collapse': '折叠本章节',\n 'rail.section.expand': '展开本章节({count} 课时)',\n 'rail.section.count': '{count} 课',\n 'rail.lesson.opening': '正在打开课时会话…',\n 'rail.lesson.openHint': '(点击将打开本课专属会话)',\n 'rail.import.toggle': '导入课程',\n 'rail.import.close': '收起导入',\n 'tag.weak': '{count} 个薄弱知识点,测验会优先考察',\n 'tag.friction': '{count} 次卡点记录(你说\"不懂\"时导师记下的)',\n 'tag.mastery': '课时掌握度 {pct}%(取最薄弱知识点)',\n 'tag.mastery.short': '课时掌握度 = 最薄弱知识点的掌握度',\n 'status.exam': '章节测验:本节全部课时掌握度 ≥50% 后开放',\n 'status.mastered': '已毕业:掌握度 ≥90%(或你接受了掌握提案)',\n 'status.in_progress': '学习中:已开课,掌握度从 50% 起步',\n 'status.available': '可开始:已解锁,尚未学习',\n 'status.locked': '未解锁:先完成前面的课时',\n 'chip.unattributed': '未归因',\n 'chip.correct': '✓ 答对 · {concept}',\n 'chip.wrong': '✗ 答错 · {concept}',\n 'row.thinking': '导师思考中…',\n 'row.thinking.title': '导师正在推理,回复马上就来',\n 'quiz.title': '点击选项作答,也可以直接打字回答',\n 'quiz.answer': '选 {letter}:{text}',\n 'tutor.dormant': '学习模式已关闭 — 点右上「▶ 开始学习」开启导师,按钮恢复可用。',\n 'tutor.empty': '对话会出现在这里',\n 'tutor.empty.hint': '在下方输入框和导师说话',\n 'proposal.text': '导师提议你已掌握「{lesson}」:{rationale}',\n 'proposal.accept': '接受',\n 'proposal.decline': '再练练',\n 'bb.empty': '黑板还空着',\n 'bb.empty.hint': '在左侧课程树选择一课',\n 'bb.mastery': '掌握度 {pct}%',\n 'cmap.legend': '琥珀色 = 薄弱知识点(掌握度 <70%)',\n 'cmap.node.pct': '掌握度 {pct}%',\n 'bb.strategy': '本课学法',\n 'bb.fallback.cmap.empty': '概念图需要先由导师定义本课概念',\n 'bb.fallback.cmap': '概念图渲染不可用(布局引擎加载失败) — 已回退讲解视图',\n 'bb.notes': '笔记',\n 'bb.notes.empty': '这一课还没有笔记',\n 'tv.exam.stars': '本次 {n}★(历史最佳 {best}★)',\n 'tv.exam.fail': '这轮没拿到星——错题就是下一课的地图,从每题的解析看起。',\n 'active.off.title': '注册 study 工具并载入导师人格,之后普通对话和现在一样',\n 'active.on.title': '注销 study 工具与导师人格;学习进度保留,可随时重新开启',\n 'active.off': '▶ 开始学习',\n 'active.on': '⏻ 退出学习模式',\n 'prompt.kickoff': '开始学习:查看我的学习状态并打开当前焦点课时。如果我还没有课程,推荐我导入示例课程(AI-For-Beginners)并说明怎么开始。回复最后请提醒我点上方「学习」页签进入学习界面。',\n 'settings.nav': '学习',\n 'settings.mode': '教学风格',\n 'settings.mode.hint': '导师的讲解风格;也可在学习页随时切换',\n 'settings.studyMode': '学习模式',\n 'settings.voice': '朗读语音',\n 'settings.voice.hint': '「朗读本课」使用的神经网络声音(浏览器本地偏好)',\n 'voice.xiaoxiao': '晓晓 · 中文女声',\n 'voice.yunxi': '云希 · 中文男声',\n 'voice.yunyang': '云扬 · 中文男声(新闻)',\n 'voice.xiaoyi': '晓伊 · 中文女声(轻快)',\n 'voice.aria': 'Aria · English female',\n 'voice.guy': 'Guy · English male',\n 'settings.studyMode.hint': '开启后注册 study_* 工具并载入导师人格;进度与笔记始终保留',\n 'settings.on': '状态:已开启',\n 'settings.off': '状态:已关闭',\n 'settings.turnOn': '开启',\n 'settings.turnOff': '关闭',\n 'settings.stats': '学习统计',\n 'settings.stats.courses': '课程 {count} 个',\n 'settings.stats.xp': 'XP {xp} · Lv{level}({pct}%)',\n 'settings.stats.today': '今日 {xp}/{goal}',\n 'settings.stats.streak': '连续 {days} 天(最长 {best},剩冻结 {freeze})',\n 'settings.about': '关于',\n 'settings.about.hint': '当前运行的构建版本,点击查看该版更新内容。',\n 'settings.stateFile': '状态文件',\n 'settings.stateFile.hint': '学习进度存放于本机 JSON,换机可迁移',\n 'dock.title': '学习状态:待复习 {due} · 连续 {streak} 天 · Lv{level} — 点上方「学习」页签进入',\n 'dock.due': '{count}',\n 'dock.streak': '{days}天',\n 'dock.lv': 'Lv{level}',\n 'prompt.import': '导入课程:用 study_import_github 抓取 {url}',\n 'prompt.lesson': '学习「{title}」:用 study_lesson 打开这一课开始学习。',\n 'prompt.exam': '开始「{section}」的章节测验:按本节课时出题,答完逐题判分',\n 'prompt.review': '开始今天的复习,从最到期的课时开始',\n 'prompt.proposal.accept': '接受提案 {id} —— 确认标记这课为已掌握',\n 'prompt.proposal.decline': '拒绝提案 {id} —— 我想再练练',\n}\n\n/** English dictionary — key set must equal {@link ZH}'s (test-asserted). */\nexport const EN: StudyDict = {\n 'tab.label': 'Study',\n 'col.rail': 'Courses',\n 'col.tutor': 'Tutor',\n 'col.bb': 'Blackboard',\n 'pane.rail': 'Courses',\n 'pane.tutor': 'Tutor',\n 'pane.bb': 'Board',\n 'viewtab.teach': 'Teach',\n 'viewtab.cmap': 'Concepts',\n 'viewtab.cmap.title': 'The lesson\\'s concepts relate',\n 'loading': 'Loading…',\n 'start.title': 'One click to study: workspace + session + tutor, all set up',\n 'start.go': 'Start learning',\n 'start.enter': 'Enter study',\n 'start.busy': 'Preparing the study area…',\n 'soul.direct': 'Direct',\n 'soul.direct.hint': 'Explain first, then check understanding',\n 'soul.guide': 'Guide',\n 'soul.guide.hint': 'You push one step forward; the tutor hands you the stairs',\n 'soul.practice': 'Practice',\n 'soul.practice.hint': 'Learn inside real, messy problems',\n 'zone.understand': '🧠 Understand — knowledge structure',\n 'zone.record': '📝 Record — in my own words',\n 'zone.practice': '✍️ Practice — answer log',\n 'rail.empty.title': 'No courses yet',\n 'rail.empty.hint': 'Paste markdown → say \"import as a course\"',\n 'rail.empty.hint2': 'Local folder → say \"import D:/path/to/folder\"',\n 'rail.empty.placeholder': 'GitHub repo link, e.g. microsoft/AI-For-Beginners',\n 'rail.empty.button': 'Import',\n 'composer.send': 'Send',\n 'composer.busy': 'The tutor is replying…',\n 'rail.empty.demo': 'Import the demo course',\n 'rail.mastered': '{mastered}/{total} lessons graduated',\n 'rail.avg': 'avg mastery {pct}%',\n 'rail.avg.none': 'no mastery data yet',\n 'rail.search': 'Search lessons… (space-separated keywords)',\n 'rail.locate.title': 'Locate the current focus lesson in the tree (auto-expands its section)',\n 'rail.locate': 'Back to current lesson',\n 'rail.due': '{count} due',\n 'rail.due.over': '{days}d overdue',\n 'rail.due.start': 'Start reviewing',\n 'rail.due.tag': 'this lesson\\'s review is due today (SM-2)',\n 'rail.delete': 'Delete this course',\n 'rail.delete.confirm': 'Delete?',\n 'rail.delete.title.confirm': 'Click again to confirm (all progress and notes included). Back up via the state file in Settings first',\n 'note.delete': 'Delete this note',\n 'note.delete.confirm': 'Delete?',\n 'read.play': 'Read aloud',\n 'read.stop': 'Stop reading',\n 'read.pause': 'Pause reading',\n 'read.resume': 'Resume reading',\n 'read.engine.system': 'Network synthesis unavailable — switched to the system voice',\n 'read.unavailable': 'Read-aloud unavailable',\n 'rail.section.collapse': 'Collapse this section',\n 'rail.section.expand': 'Expand this section ({count} lessons)',\n 'rail.section.count': '{count} lessons',\n 'rail.lesson.opening': 'Opening the lesson session…',\n 'rail.lesson.openHint': ' (opens this lesson\\'s own session)',\n 'rail.import.toggle': 'Import a course',\n 'rail.import.close': 'Collapse import',\n 'tag.weak': '{count} weak concepts — quizzes target them first',\n 'tag.friction': '{count} friction marks (logged when you said you were stuck)',\n 'tag.mastery': 'lesson mastery {pct}% (the weakest concept)',\n 'tag.mastery.short': 'lesson mastery = the weakest concept\\'s mastery',\n 'status.exam': 'Section exam: opens when every lesson in the section reaches ≥50% mastery',\n 'status.mastered': 'Graduated: mastery ≥90% (or you accepted a mastery proposal)',\n 'status.in_progress': 'Learning: opened, mastery starts at 50%',\n 'status.available': 'Ready: unlocked, not started',\n 'status.locked': 'Locked: finish the preceding lessons first',\n 'chip.unattributed': 'unattributed',\n 'chip.correct': '✓ correct · {concept}',\n 'chip.wrong': '✗ wrong · {concept}',\n 'row.thinking': 'tutor is thinking…',\n 'row.thinking.title': 'The tutor is reasoning; the reply is coming',\n 'quiz.title': 'Click an option to answer, or just type',\n 'quiz.answer': 'Choose {letter}: {text}',\n 'tutor.dormant': 'Study mode is off — click \"▶ Start learning\" above to wake the tutor and re-enable these controls.',\n 'tutor.empty': 'The conversation will appear here',\n 'tutor.empty.hint': 'Talk to the tutor in the input below',\n 'proposal.text': 'The tutor proposes you have mastered \"{lesson}\": {rationale}',\n 'proposal.accept': 'Accept',\n 'proposal.decline': 'Keep practicing',\n 'bb.empty': 'The blackboard is empty',\n 'bb.empty.hint': 'Pick a lesson in the course tree on the left',\n 'bb.mastery': 'mastery {pct}%',\n 'cmap.legend': 'Amber = weak concept (mastery < 70%)',\n 'cmap.node.pct': 'mastery {pct}%',\n 'bb.strategy': 'Approach',\n 'bb.fallback.cmap.empty': 'The concept map needs the tutor to define this lesson\\'s concepts first',\n 'bb.fallback.cmap': 'Concept map unavailable (layout engine failed to load) — fell back to the teach view',\n 'bb.notes': 'Notes',\n 'bb.notes.empty': 'No notes for this lesson yet',\n 'tv.exam.stars': 'This attempt {n}★ (best {best}★)',\n 'tv.exam.fail': 'No stars this round — the wrong answers are the map for the next one; start with their explanations.',\n 'active.off.title': 'Register the study tools and load the tutor persona; plain chats stay as they are',\n 'active.on.title': 'Retire the study tools and tutor persona; progress is kept and you can re-enable anytime',\n 'active.off': '▶ Start learning',\n 'active.on': '⏻ Exit study mode',\n 'prompt.kickoff': 'Start learning: check my study state and open the current focus lesson. If I have no course yet, suggest importing the demo course (AI-For-Beginners) and explain how to begin. Remind me at the end to open the \"Study\" tab above to enter the study UI.',\n 'settings.nav': 'Study',\n 'settings.mode': 'Teaching style',\n 'settings.mode.hint': 'The tutor\\'s explanation style; switchable anytime in the study tab too',\n 'settings.studyMode': 'Study mode',\n 'settings.voice': 'Read-aloud voice',\n 'settings.voice.hint': 'The neural voice used by 朗读本课 (a browser-local preference)',\n 'voice.xiaoxiao': 'Xiaoxiao · Chinese female',\n 'voice.yunxi': 'Yunxi · Chinese male',\n 'voice.yunyang': 'Yunyang · Chinese male (news)',\n 'voice.xiaoyi': 'Xiaoyi · Chinese female (lively)',\n 'voice.aria': 'Aria · English female',\n 'voice.guy': 'Guy · English male',\n 'settings.studyMode.hint': 'On registers the study_* tools and the tutor persona; progress and notes are always kept',\n 'settings.on': 'Status: on',\n 'settings.off': 'Status: off',\n 'settings.turnOn': 'Turn on',\n 'settings.turnOff': 'Turn off',\n 'settings.stats': 'Study stats',\n 'settings.stats.courses': '{count} courses',\n 'settings.stats.xp': 'XP {xp} · Lv{level} ({pct}%)',\n 'settings.stats.today': 'Today {xp}/{goal}',\n 'settings.stats.streak': 'Streak {days}d (best {best}, {freeze} freeze left)',\n 'settings.about': 'About',\n 'settings.about.hint': 'The version of the running build — click to see its release notes.',\n 'settings.stateFile': 'State file',\n 'settings.stateFile.hint': 'Your progress lives in a local JSON file — copy it to migrate machines',\n 'dock.title': 'Study status: {due} due · {streak}d streak · Lv{level} — open the Study tab above to continue',\n 'dock.due': '{count}',\n 'dock.streak': '{days}d',\n 'dock.lv': 'Lv{level}',\n 'prompt.import': 'Import a course: fetch {url} with study_import_github',\n 'prompt.lesson': 'Study \"{title}\": open this lesson with study_lesson and start teaching.',\n 'prompt.exam': 'Start the section exam for \"{section}\": quiz me on this section\\'s lessons and grade each answer',\n 'prompt.review': 'Start today\\'s review, from the most overdue lesson first',\n 'prompt.proposal.accept': 'Accept proposal {id} — confirm marking this lesson as mastered',\n 'prompt.proposal.decline': 'Reject proposal {id} — I want to practice more',\n}\n\n/** Both dictionaries under their locale ids (the service's register shape). */\nexport const STUDY_DICTS = { zh: ZH, en: EN }\n\n/** The namespace id this plugin registers with the framework locale service. */\nexport const STUDY_NS = 'lookatstudy'\n\n/** Translate function for the study namespace. */\nexport type StudyT = (key: string, params?: Record<string, unknown>) => string\n\n/**\n * Pure translator with the service's semantics: own-locale dict → zh\n * fallback → the key itself, `{name}` interpolation left untouched when the\n * param is missing.\n * @param locale - active locale id ('zh'/'en'; unknown ids fall back to zh).\n * @returns a stable-shaped translate function.\n */\nexport function makeT(locale: string): StudyT {\n const dict = locale === 'en' ? EN : ZH\n return (key, params) => {\n const template = dict[key] ?? ZH[key] ?? key\n if (params === undefined) return template\n return template.replace(/\\{(\\w+)\\}/g, (match, name: string) =>\n name in params ? String(params[name]) : match)\n }\n}\n\n/** Minimal shape of the framework locale service this plugin consumes. */\nexport interface LocaleServiceFace {\n register(ns: string, dicts: Record<string, StudyDict>): () => void\n bind(ns: string): StudyT\n subscribe(fn: () => void): () => void\n}\n\n/**\n * Register the namespace with the framework locale service.\n * @param locale - the client locale service when present.\n * @returns the registration disposer (a no-op when the service is absent or\n * a hostile re-registration was refused).\n */\nexport function registerStudyLocale(locale?: LocaleServiceFace): () => void {\n if (locale === undefined) return () => {}\n try {\n return locale.register(STUDY_NS, STUDY_DICTS)\n } catch {\n // A namespace/locale already claimed by someone else must not break the\n // tab — fall back to the pure translator everywhere.\n return () => {}\n }\n}\n\n/**\n * The live translator: the service's stable `bind` when present (reads the\n * active locale at call time, so a locale-change re-render picks new\n * strings), the pure zh translator otherwise.\n * @param locale - the client locale service when present.\n */\nexport function studyTranslator(locale?: LocaleServiceFace): StudyT {\n return locale === undefined ? makeT('zh') : locale.bind(STUDY_NS)\n}\n\n/** The module's active translator — zh until the view factory installs the live bind. */\nlet activeT: StudyT = makeT('zh')\n\n/**\n * Install the client-wide translator (called once at client apply, before\n * any component renders).\n */\nexport function setStudyTranslator(t: StudyT): void {\n activeT = t\n}\n\n/**\n * Translate through the active translator — the import every client module\n * uses for user-visible copy. Pure helpers take an optional explicit `t`\n * defaulting to the current active translator, evaluated per call.\n */\nexport function tr(key: string, params?: Record<string, unknown>): string {\n return activeT(key, params)\n}\n","/**\n * Blackboard diagram views (SPEC Phase 2): the lesson concept map (ELK layered\n * layout via the vendored cmap-elk-layout + CDN elkjs, draw.io-flavored SVG\n * skin rendered plugin-side). Degrades honestly: any CDN/ELK failure leaves a\n * one-line notice instead of a broken pane.\n *\n * The lesson mind map view was RETIRED with upstream v0.26.0 (2026-09-01\n * port): body text that has headings already shows its structure, text without\n * headings yields a first-line-truncated node graph nobody can read — both\n * lose — and this concept map already covers the semantic-structure need at\n * higher quality. Its three CDN imports went with it.\n */\n\nimport { layoutConceptMap, type CmNode, type CmEdge } from '../vendor/cmap-elk-layout.ts'\nimport { tr } from './locale.ts'\n\n\n/** Lesson concept map: the lesson node + its concepts (weak ones accented),\n * laid out by the vendored ELK pipeline, skinned draw.io-style (plugin-original,\n * upstream's skin is coupled to its token pipeline). Mastery shades the nodes. */\nexport async function renderLessonConceptMap(\n container: HTMLElement,\n lessonTitle: string,\n concepts: ReadonlyArray<{ title: string; masteryPct: number }>,\n): Promise<void> {\n const nodes: CmNode[] = [{ id: 'lesson', label: lessonTitle }, ...concepts.map(c => ({ id: `c-${c.title}`, label: c.title }))]\n const edges: CmEdge[] = concepts.map(c => ({ from: 'lesson', to: `c-${c.title}` }))\n const layout = await layoutConceptMap(nodes, edges)\n const masteryBy = new Map(concepts.map(c => [`c-${c.title}`, c.masteryPct]))\n const ns = 'http://www.w3.org/2000/svg'\n const svg = document.createElementNS(ns, 'svg')\n svg.setAttribute('width', String(Math.max(320, layout.width)))\n svg.setAttribute('height', String(Math.max(200, layout.height)))\n svg.setAttribute('viewBox', `0 0 ${layout.width} ${layout.height}`)\n svg.setAttribute('style', 'max-width:100%;background:var(--dsw-alias-bg-layer-1, #fff);border:1px solid var(--dsw-alias-border-l2, #d0d7de);border-radius:8px')\n for (const e of layout.edges) {\n const path = document.createElementNS(ns, 'path')\n path.setAttribute('d', e.pts.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x},${p.y}`).join(' '))\n path.setAttribute('fill', 'none')\n path.setAttribute('stroke', 'var(--dsw-alias-border-l2, #8b949e)')\n path.setAttribute('stroke-width', '1.5')\n svg.append(path)\n }\n for (const n of layout.nodes) {\n const g = document.createElementNS(ns, 'g')\n g.setAttribute('transform', `translate(${n.x},${n.y})`)\n const rect = document.createElementNS(ns, 'rect')\n const isLesson = n.id === 'lesson'\n const mastery = masteryBy.get(n.id)\n rect.setAttribute('width', String(n.w))\n rect.setAttribute('height', String(n.h))\n rect.setAttribute('rx', '6')\n rect.setAttribute('fill', mastery !== undefined && mastery < 70 ? 'var(--dsw-alias-state-warn-tertiary, #fef5e7)' : 'var(--dsw-alias-bg-layer-2, #f6f8fa)')\n rect.setAttribute('stroke', isLesson ? 'var(--dsw-alias-state-business-primary, #0969da)' : 'var(--dsw-alias-border-l2, #8b949e)')\n rect.setAttribute('stroke-width', isLesson ? '2' : '1')\n g.append(rect)\n n.lines.forEach((line, li) => {\n const label = document.createElementNS(ns, 'text')\n label.setAttribute('x', String(n.w / 2))\n label.setAttribute('y', String(n.h / 2 - (n.lines.length - 1) * 7 + li * 14 + 4))\n label.setAttribute('text-anchor', 'middle')\n label.setAttribute('font-size', isLesson ? '13' : '12')\n label.setAttribute('font-weight', isLesson ? '600' : '400')\n label.setAttribute('fill', 'var(--dsw-alias-label-primary, #1f2328)')\n label.textContent = line\n g.append(label)\n })\n const title = document.createElementNS(ns, 'title')\n const label = n.lines.join(' ')\n title.textContent = isLesson || mastery === undefined ? label : `${label} · ${tr('cmap.node.pct', { pct: mastery })}`\n g.append(title)\n svg.append(g)\n }\n container.append(svg)\n const legend = document.createElement('span')\n legend.className = 'lks-cmap-legend'\n const swatch = document.createElement('i')\n legend.append(swatch, document.createTextNode(tr('cmap.legend')))\n container.append(legend)\n}\n","/**\n * Vendored from LookatStudy shared/speech-text.ts (MIT License,\n * https://github.com/Kaiji-Z/LookatStudy), upstream v0.28.0 — includes the\n * v11.5 whole-display-group avoidance fix era. Unmodified except this\n * provenance header.\n *\n * speech-text —— TTS 朗读文本处理(纯函数,渲染层/主进程共用)。\n *\n * normalizeSpeechText: markdown → 可朗读纯文本。代码不读(围栏/行内整体移除),\n * 链接留文字,强调/标题/列表/引用/表格标记剥离 —— 导师\"念的是话,不是版面\"。\n *\n * splitSentences: 流式友好的切句。每次喂\"累积文本\"返回完整句 + 余量(幂等,StrictMode 安全);\n * flush=true 时尾句强制吐出。终止标点:中英句号/叹/问/分号 + 省略号;ASCII '.' 要求后跟\n * 空白且前字符非数字(3.14 不切)。超过 maxBuffer 仍无终止标点 → 在最后的软标点(、,;: 空白)\n * 处断开,再无则硬断 —— 保证\"导师边生成边念\"的流式管线永远不会饿死。\n *\n * v11.2 表意分隔:换行与 emoji 也是句界。无标点的段落/列表/短句流文本,\n * 旧逻辑的强制断句块会在显示层并组吞整段(高亮整段到结尾);现在逐行/逐 emoji\n * 成句,显示组另加长度上限兜底 —— karaoke 高亮粒度永远可控。\n */\n\n/** 终止标点(出现即成句,连续终止/右引号并吞进句尾) */\nconst HARD = new Set([\"。\", \"!\", \"?\", \"!\", \"?\", \";\", \";\", \"…\"]);\n/** 软标点(超长兜底断句位) */\nconst SOFT = new Set([\",\", \",\", \"、\", \":\", \":\", \" \", \"\\t\"]);\n/** v11.2 emoji 基字符范围(表意/象形/杂项符号/箭头符号区);修饰符(VS16/ZWJ/肤色)不单独成界 */\nconst EMOJI_CP_RE = /[\\u{1F000}-\\u{1FAFF}\\u{2600}-\\u{27BF}\\u{2B00}-\\u{2BFF}]/u;\nfunction isEmojiCp(cp: number | undefined): boolean {\n return cp != null && EMOJI_CP_RE.test(String.fromCodePoint(cp));\n}\n/** 句尾 emoji 序列(含 ZWJ 连写/VS16/肤色修饰) —— endsWithSentenceEnd 认它为显示终点 */\nconst EMOJI_TAIL_RE = /(?:[\\u{1F000}-\\u{1FAFF}\\u{2600}-\\u{27BF}\\u{2B00}-\\u{2BFF}](?:\\uFE0F|\\u200D[\\u{1F000}-\\u{1FAFF}\\u{2600}-\\u{27BF}\\u{2B00}-\\u{2BFF}]|[\\u{1F3FB}-\\u{1F3FF}])*)$/u;\n/** 句尾可并吞的右闭合符 */\nconst CLOSERS = new Set([\"”\", '\"', \"」\", \"』\", \"》\", \")\", \"】\", \"])\".slice(0, 1)]);\n\nexport function normalizeSpeechText(md: string): string {\n let s = md;\n // Windows CRLF 归一(换行是 v11.2 句界,\\r 不许混进来)\n s = s.replace(/\\r\\n?/g, \"\\n\");\n // 围栏代码块(``` / ~~~):整个移除,不朗读\n s = s.replace(/(?:^|\\n)[ \\t]*(?:```|~~~)[^\\n]*\\n[\\s\\S]*?(?:\\n[ \\t]*(?:```|~~~)[^\\n]*|\\n?$)/g, \"\\n\");\n // 行内代码\n s = s.replace(/`[^`\\n]*`/g, \"\");\n // 图片整体移除,链接留文字\n s = s.replace(/!\\[[^\\]]*\\]\\([^)\\n]*\\)/g, \"\");\n s = s.replace(/\\[([^\\]]+)\\]\\([^)\\n]*\\)/g, \"$1\");\n // 标题 / 列表 / 引用标记\n s = s.replace(/^[ \\t]{0,3}#{1,6}[ \\t]+/gm, \"\");\n s = s.replace(/^[ \\t]*(?:[-*+]|\\d+\\.)[ \\t]+/gm, \"\");\n s = s.replace(/^[ \\t]*>[ \\t]?/gm, \"\");\n // 表格:竖线换空格,对齐行(---)整行移除\n s = s.replace(/\\|/g, \" \");\n s = s.replace(/^[ \\t]*[-: ]{3,}[ \\t]*$/gm, \"\");\n // 强调 / 删除线\n s = s.replace(/(\\*\\*|__)(.*?)\\1/g, \"$2\");\n s = s.replace(/(?<![*\\w])(\\*|_)(?!\\s)(.+?)(?<!\\s)\\1(?![*\\w])/g, \"$2\");\n s = s.replace(/~~(.+?)~~/g, \"$1\");\n s = s.replace(/\\n{3,}/g, \"\\n\\n\");\n return s.trim();\n}\n\n/**\n * v11.2 朗读句表单一入口(v11.4 起仅合成侧使用):tts-service 用它切段;\n * 渲染层 karaoke **不再调用**(改吃 ttsAudio.sentence 权威原文,见\n * playedSentencePrefix)——句表从合成侧单向流出,显示侧零复算零分叉。\n */\nexport function speechSentencesOf(text: string): string[] {\n return splitSentences(normalizeSpeechText(text), { flush: true }).sentences;\n}\n\n/**\n * v11.4 已播句组前缀:karaoke 高亮文本 = 从当前块向前收拢到最近的\"句终点块\"\n * (不含),拼接**已播放**各块的原文。块文本由合成侧 ttsAudio.sentence 权威下发\n * (念什么高亮什么,渲染层零复算);强断句块(超长行被 maxBuffer 撕开的)按此\n * 并回同一视觉句,且只亮到当前进度——高亮随音频逐块生长,永不错句。\n */\nexport function playedSentencePrefix(texts: readonly string[], idx: number): string {\n if (idx < 0 || idx >= texts.length) return \"\";\n let s = idx;\n while (s > 0 && !endsWithSentenceEnd(texts[s - 1] ?? \"\")) s--;\n const parts: string[] = [];\n for (let i = s; i <= idx; i++) {\n const t = texts[i];\n if (t && t.trim()) parts.push(t);\n }\n return parts.join(\" \");\n}\n\nexport interface SplitOptions {\n /** 流结束:余量作为尾句吐出 */\n flush?: boolean;\n /** 无终止标点时的强制断句阈值(字符数) */\n maxBuffer?: number;\n}\n\nexport function splitSentences(\n text: string,\n opts: SplitOptions = {},\n): { sentences: string[]; rest: string } {\n const maxBuffer = Math.max(8, opts.maxBuffer ?? 120);\n const flush = opts.flush ?? false;\n const out: string[] = [];\n const n = text.length;\n let start = 0;\n let i = 0;\n let lastSoft = -1;\n\n const emit = (end: number) => {\n const piece = text.slice(start, end).trim();\n if (piece) out.push(piece);\n start = end;\n lastSoft = -1;\n };\n\n while (i < n) {\n const ch = text[i]!;\n // v11.2 emoji=句界:吞后续修饰符(VS16/ZWJ/肤色)与连写 emoji,整序列收进句尾\n const cp = text.codePointAt(i)!;\n if (isEmojiCp(cp)) {\n let j = i + (cp > 0xffff ? 2 : 1);\n while (j < n) {\n const cj = text.codePointAt(j)!;\n const ul = cj > 0xffff ? 2 : 1;\n if (cj === 0xfe0f || cj === 0x200d || (cj >= 0x1f3fb && cj <= 0x1f3ff) || isEmojiCp(cj)) {\n j += ul;\n continue;\n }\n break;\n }\n emit(j);\n i = j;\n continue;\n }\n // v11.2 换行=句界:整行成句(无标点的段落/列表不再被并组吞成整段);\n // 句尾保留 \\n 作为显示终点标记(endsWithSentenceEnd 认它,不被并组)\n if (ch === \"\\n\") {\n const piece = text.slice(start, i + 1).replace(/[^\\S\\n]+$/, \"\");\n if (piece.trim()) out.push(piece);\n start = i + 1;\n lastSoft = -1;\n i++;\n continue;\n }\n if (HARD.has(ch)) {\n let j = i + 1;\n while (j < n && (HARD.has(text[j]!) || CLOSERS.has(text[j]!))) j++;\n emit(j);\n i = j;\n continue;\n }\n if (ch === \".\") {\n const prev = i > 0 ? text[i - 1]! : \"\";\n const next = i + 1 < n ? text[i + 1]! : \"\";\n // ASCII 句点:后跟空白/结尾,且前一字符不是数字(小数点不切)\n if ((next === \"\" || /\\s/.test(next)) && !/\\d/.test(prev)) {\n let j = i + 1;\n while (j < n && (CLOSERS.has(text[j]!) || HARD.has(text[j]!))) j++;\n emit(j);\n i = j;\n continue;\n }\n }\n if (SOFT.has(ch)) lastSoft = i;\n if (i - start + 1 > maxBuffer) {\n if (lastSoft > start) {\n // 在最后的软标点处断(软标点本身不进上一句)。先存切割点 ——\n // emit 内部会重置 lastSoft,重置后再读就回卷到 0 造成重复段(实测踩过)\n const cut = lastSoft;\n emit(cut);\n start = cut + 1;\n } else {\n emit(i + 1);\n i++;\n continue;\n }\n }\n i++;\n }\n\n const rest = text.slice(start).trim();\n if (flush && rest) out.push(rest);\n return { sentences: out, rest: flush ? \"\" : rest };\n}\n\n/**\n * v9 块是否结束于\"真句子终点\"(剥尾部闭合符后是终止标点/ASCII 句点)。\n * 超长强制断句(maxBuffer 软标点兜底)产出的块**不以**句终点结尾——\n * TTS 分块 ≠ 显示句:渲染层 karaoke 用 groupSentenceChunks 把这类块\n * 与后续块并成一个显示句组,高亮整组,不在一句中间断开。\n */\nexport function endsWithSentenceEnd(chunk: string): boolean {\n // v11.2 句尾换行=整行句的显示终点标记 —— 必须在 trimEnd 之前查(它会剥掉 \\n)\n if (/\\n\\s*$/.test(chunk)) return true;\n const t = chunk.trimEnd();\n if (!t) return true;\n let i = t.length - 1;\n while (i >= 0 && CLOSERS.has(t[i]!)) i--;\n if (i < 0) return true;\n const ch = t[i]!;\n if (HARD.has(ch) || ch === \".\") return true;\n // v11.2 表意终点:句尾换行(整行句)或 emoji 序列(含修饰符尾巴)\n if (ch === \"\\n\") return true;\n return EMOJI_TAIL_RE.test(t);\n}\n\n/** v11.2 显示句组长度上限(字符):并组只为\"把被强制断句撕开的真句子缝回来\",\n * 不是无限吞 —— 超上限就保持独立组,karaoke 高亮粒度永远可控。 */\nexport const DISPLAY_GROUP_MAX = 160;\n\n/**\n * v0.28 显示句组跨度(纯):把\"TTS 块在容器文本中的匹配段 [start,end)\"扩展到\n * **整条显示句**的字符跨度——向前收拢到最近句终点之后,向后扩到最近句终点\n * (含闭合符/emoji 修饰尾)。v11.4 起朗读 mark 只覆盖\"已播前缀\",而伴学避让\n * (\"生物不压正在读的句\")需要整句的行盒;本函数与 splitSentences 共用同一套\n * 句界判定(HARD/ASCII 句点/换行/emoji),零第二份标点表。maxSpan 是单侧扫描\n * 上限,防病态无标点长文把跨度扩爆(扫不到界就停在窗口边,保守取小)。\n */\nexport function displayGroupSpanAround(\n text: string,\n start: number,\n end: number,\n maxSpan = 400,\n): { start: number; end: number } {\n const n = text.length;\n // text[i] 开头是否是一处句终点;是则返回该终点的 exclusive 结束下标(闭合符/\n // emoji 修饰已吞进终点),否则 -1。判定分支与 splitSentences 逐条同构。\n const hardEndAt = (i: number): number => {\n if (i < 0 || i >= n) return -1;\n const ch = text[i]!;\n if (ch === \"\\n\") return i + 1;\n if (HARD.has(ch)) {\n let j = i + 1;\n while (j < n && (HARD.has(text[j]!) || CLOSERS.has(text[j]!))) j++;\n return j;\n }\n if (ch === \".\") {\n const prev = i > 0 ? text[i - 1]! : \"\";\n const next = i + 1 < n ? text[i + 1]! : \"\";\n if ((next === \"\" || /\\s/.test(next)) && !/\\d/.test(prev)) {\n let j = i + 1;\n while (j < n && (CLOSERS.has(text[j]!) || HARD.has(text[j]!))) j++;\n return j;\n }\n return -1;\n }\n const cp = text.codePointAt(i)!;\n if (isEmojiCp(cp)) {\n let j = i + (cp > 0xffff ? 2 : 1);\n while (j < n) {\n const cj = text.codePointAt(j)!;\n const ul = cj > 0xffff ? 2 : 1;\n if (cj === 0xfe0f || cj === 0x200d || (cj >= 0x1f3fb && cj <= 0x1f3ff) || isEmojiCp(cj)) {\n j += ul;\n continue;\n }\n break;\n }\n return j;\n }\n return -1;\n };\n\n // 向前:找 start 之前最近的句终点,span 起点落在它的 exclusive 终点处;\n // 终点被块起点截断(e>start,理论上不该发生)时不动起点。扫到文本头都没有\n // 句界字符(如开头就是无标点短句)时,文本头本身就是界 → 起点=0;仅当因\n // maxSpan 提前截断才保守保持 start(界在窗口外,不外扩)。\n let s = start;\n {\n let found = false;\n for (let i = Math.min(start, n) - 1; i >= 0 && start - i <= maxSpan; i--) {\n const e = hardEndAt(i);\n if (e >= 0) {\n found = true;\n if (e <= start) s = e;\n break;\n }\n }\n if (!found && start <= maxSpan) s = 0;\n }\n\n // 向后:块段已以句终点结尾(终点块,含闭合符/\\n/emoji 尾)则终点只吞紧随的\n // 右闭合符(与 splitSentences\"右引号并吞进句尾\"一致)即止;否则找 end 起最近的\n // 句终点(含其尾巴),span 终点=其 exclusive 结束位;扫到上限/文本尾都没终点\n // (超长无标点段)则扩到文本尾。\n if (endsWithSentenceEnd(text.slice(0, Math.min(end, n)))) {\n let e = Math.min(end, n);\n while (e < n && CLOSERS.has(text[e]!)) e++;\n return { start: s, end: e };\n }\n for (let i = end; i < n && i - end <= maxSpan; i++) {\n const e = hardEndAt(i);\n if (e >= 0) return { start: s, end: e };\n }\n return { start: s, end: n };\n}\n\n/** TTS 分块序列 → 显示句组(每组合成一个真句子):[start,end] 闭区间块下标。 */\nexport function groupSentenceChunks(sentences: string[]): Array<{ start: number; end: number }> {\n const groups: Array<{ start: number; end: number }> = [];\n let i = 0;\n while (i < sentences.length) {\n let j = i;\n let len = sentences[i]!.length;\n while (\n j + 1 < sentences.length &&\n !endsWithSentenceEnd(sentences[j]!) &&\n len + sentences[j + 1]!.length <= DISPLAY_GROUP_MAX\n ) {\n j++;\n len += sentences[j]!.length;\n }\n groups.push({ start: i, end: j });\n i = j + 1;\n }\n return groups;\n}\n","/**\n * Vendored from LookatStudy shared/math-speech.ts (MIT License,\n * https://github.com/Kaiji-Z/LookatStudy), upstream v0.28.0. Unmodified\n * except this provenance header. zh-only by design upstream — the plugin's\n * read-aloud pipeline applies it for zh and strips $..$ delimiters for other\n * locales (conversion for more languages is future work).\n *\n * math-speech —— LaTeX 公式 → 中文口语(v0.19 朗读口语化,纯函数,verify 直测)。\n *\n * 只在**合成侧**应用(真正念出来的文本);karaoke 高亮/匹配层继续吃 `$..$` 原文\n * (DOM 侧 getTextModel 收 KaTeX annotation 的 TeX 源,两侧在 canonical 空间对齐)\n * ——念的是人话,亮的是原文,互不干扰。\n *\n * 规则表起步:常见记号(分式/根号/上下标/希腊字母/关系与算符)覆盖优先,\n * 未覆盖宏退化为逐字母(好于念\"反斜杠 f-r-a-c\")。\n */\n\n/** 单符号命令 → 中文(长命令先试,避免 \\le 被 \\leq 截断)。 */\nconst SYMBOLS: Array<[string, string]> = [\n [\"\\\\leq\", \"小于等于\"], [\"\\\\le\", \"小于等于\"], [\"\\\\geq\", \"大于等于\"], [\"\\\\ge\", \"大于等于\"],\n [\"\\\\neq\", \"不等于\"], [\"\\\\ne\", \"不等于\"], [\"\\\\approx\", \"约等于\"], [\"\\\\equiv\", \"恒等于\"],\n [\"\\\\pm\", \"正负\"], [\"\\\\times\", \"乘以\"], [\"\\\\cdot\", \"点乘\"], [\"\\\\div\", \"除以\"],\n [\"\\\\to\", \"趋于\"], [\"\\\\rightarrow\", \"趋于\"], [\"\\\\Rightarrow\", \"推出\"],\n [\"\\\\infty\", \"无穷\"], [\"\\\\sum\", \"求和\"], [\"\\\\prod\", \"连乘\"], [\"\\\\int\", \"积分\"],\n [\"\\\\lim\", \"极限\"], [\"\\\\log\", \"对数\"], [\"\\\\ln\", \"自然对数\"], [\"\\\\exp\", \"指数\"],\n [\"\\\\in\", \"属于\"], [\"\\\\subset\", \"包含于\"], [\"\\\\cup\", \"并\"], [\"\\\\cap\", \"交\"],\n [\"\\\\forall\", \"任意\"], [\"\\\\exists\", \"存在\"], [\"\\\\partial\", \"偏导\"], [\"\\\\nabla\", \"梯度\"],\n [\"\\\\angle\", \"角\"], [\"\\\\degree\", \"度\"], [\"\\\\cdots\", \"点点点\"], [\"\\\\ldots\", \"点点点\"],\n];\n\nconst GREEK: Record<string, string> = {\n alpha: \"阿尔法\", beta: \"贝塔\", gamma: \"伽马\", delta: \"德尔塔\", epsilon: \"艾普西隆\",\n zeta: \"泽塔\", eta: \"伊塔\", theta: \"西塔\", iota: \"约塔\", kappa: \"卡帕\", lambda: \"拉姆达\",\n mu: \"缪\", nu: \"纽\", xi: \"克西\", pi: \"派\", rho: \"柔\", sigma: \"西格马\", tau: \"陶\",\n phi: \"斐\", chi: \"凯\", psi: \"普西\", omega: \"欧米伽\",\n};\n\n/** 读取 `{...}` 花括号组(从 openIdx 的下一个字符起,返回内容与结束下标)。 */\nfunction readGroup(s: string, i: number): { body: string; end: number } | null {\n if (s[i] !== \"{\") return null;\n let depth = 0;\n for (let j = i; j < s.length; j++) {\n if (s[j] === \"{\") depth++;\n else if (s[j] === \"}\") {\n depth--;\n if (depth === 0) return { body: s.slice(i + 1, j), end: j + 1 };\n }\n }\n return null;\n}\n\n/** 单 token(花括号组或单字符)读取:返回 {body, end}。 */\nfunction readAtom(s: string, i: number): { body: string; end: number } {\n const g = readGroup(s, i);\n if (g) return g;\n return { body: s[i] ?? \"\", end: i + 1 };\n}\n\n/** LaTeX 片段 → 中文口语(递归:命令参数体内再走一遍)。 */\nexport function mathToSpokenZH(tex: string): string {\n let s = tex;\n // 无参修饰先清\n s = s.replace(/\\\\(?:left|right|displaystyle|limits)\\b/g, \" \");\n s = s.replace(/\\\\[,;!\\s]|\\\\quad|\\\\qquad|~/g, \" \");\n\n // 带参命令(迭代到不动点,支持嵌套如 \\frac{\\alpha}{\\beta})\n for (let guard = 0; guard < 12; guard++) {\n let next = s;\n // \\frac{a}{b} 家族 → \"a 分之 b\";\\binom → \"a 取 b\"(归一不许插空格,空格会被\n // readAtom 当成单字符参数吞掉首参)\n next = next.replace(/\\\\[dt]?frac(?![A-Za-z])/g, \"\\\\frac\");\n {\n let i: number;\n while ((i = next.indexOf(\"\\\\frac\")) >= 0) {\n const a = readAtom(next, i + 5);\n const b = readAtom(next, a.end);\n next = next.slice(0, i) + `${a.body} 分之 ${b.body} ` + next.slice(b.end);\n }\n }\n {\n let i: number;\n while ((i = next.indexOf(\"\\\\binom\")) >= 0) {\n const a = readAtom(next, i + 6);\n const b = readAtom(next, a.end);\n next = next.slice(0, i) + `${a.body} 取 ${b.body} ` + next.slice(b.end);\n }\n }\n // \\sqrt[n]{x} / \\sqrt{x}\n next = next.replace(/\\\\sqrt\\s*\\[( {[^{}]*} |[^\\s\\\\]+)\\s*\\]\\s*/g, (_m, n: string) => `${n.trim()} 次根号 `);\n {\n let i: number;\n while ((i = next.indexOf(\"\\\\sqrt\")) >= 0) {\n const a = readAtom(next, i + 5);\n next = next.slice(0, i) + `根号 ${a.body} ` + next.slice(a.end);\n }\n }\n // 包裹命令(取内容):\\text 直接留内容;\\mathbf 等去壳\n next = next.replace(/\\\\(?:mathbf|mathbb|mathrm|mathit|boldsymbol|text|operatorname)\\s*/g, (m) => (m.startsWith(\"\\\\text\") ? \"\" : \" \"));\n if (next === s) { s = next; break; }\n s = next;\n }\n\n // 上下标:前一个原子 ^{..}/^c → \"的 n 次方\";_{..}/_c → \"下标 n\"\n for (let guard = 0; guard < 8; guard++) {\n const next = s.replace(\n /(\\\\frac|[\\w)\\]}]|\\s)\\s*\\^\\s*(\\{[^{}]*\\}|[^\\s])|(\\\\frac|[\\w)\\]}]| )\\s*_\\s*(\\{[^{}]*\\}|[^\\s])/g,\n (_m, base1: string | undefined, sup: string | undefined, base2: string | undefined, sub: string | undefined) => {\n if (sup !== undefined) return `${base1} 的 ${sup.replace(/[{}]/g, \"\")} 次方`;\n return `${base2} 下标 ${sub!.replace(/[{}]/g, \"\")}`;\n },\n );\n if (next === s) break;\n s = next;\n }\n\n // 符号与希腊字母(长命令优先)\n for (const [cmd, zh] of SYMBOLS) s = s.split(cmd).join(` ${zh} `);\n s = s.replace(/\\\\([A-Za-z]+)\\b/g, (_m, name: string) => ` ${GREEK[name] ?? name} `);\n s = s.replace(/[{}]/g, \" \");\n return s.replace(/\\s+/g, \" \").trim();\n}\n\n/** 句子级入口:把 `$..$`/`$$..$$` 段替换为口语;段外文本逐字节不变。 */\nexport function speakMathInSentence(sentence: string): string {\n return sentence.replace(/\\$\\$([^$]+)\\$\\$|\\$([^$]+)\\$/g, (_m, block?: string, inline?: string) => {\n const spoken = mathToSpokenZH(block ?? inline ?? \"\");\n return spoken ? ` ${spoken} ` : \"\";\n });\n}\n","/**\n * The panel tutor's chat stream: folds a bound session's event window (the\n * same feed the host conversation assembles) into chat rows — structurally,\n * with zero host imports, because the panel renders OUTSIDE the host's slot\n * system. Durable `user/message` / `assistant/message` events become rows;\n * tool calls stay in the host conversation's toolview cards (the study view\n * never mirrored them); transient `text-delta` live chunks accumulate into\n * one streaming row per attempt so the tutor's reply grows in place.\n * @module dsh-plugin-lookatstudy/client/session-feed\n */\n\nimport type { ChatRow } from './views.tsx'\n\n/** Structural shapes of the host's event window (kept local — no host imports). */\nexport interface FeedEvent {\n readonly type: string\n readonly seq: number\n readonly data?: {\n readonly content?: readonly { readonly type?: string; readonly text?: string }[]\n readonly message?: { readonly content?: readonly { readonly kind?: string; readonly type?: string; readonly text?: string }[]; readonly blocks?: readonly { readonly kind?: string; readonly text?: string }[] }\n readonly chunk?: { readonly type?: string; readonly text?: string }\n readonly attemptId?: string\n readonly source?: { readonly kind?: string }\n }\n}\n\nexport interface FeedEntry {\n readonly type: 'event' | 'transient'\n readonly event: FeedEvent\n}\n\nexport interface FeedWindow {\n readonly entries: readonly FeedEntry[]\n readonly hasMore?: boolean\n}\n\nfunction userText(event: FeedEvent): string {\n const content = event.data?.content\n if (content === undefined) return ''\n return content\n .filter(part => (part.type ?? 'text') === 'text')\n .map(part => part.text ?? '')\n .join('\\n\\n')\n .trim()\n}\n\nfunction assistantText(event: FeedEvent): string {\n const message = event.data?.message\n if (message === undefined) return ''\n const blocks = message.content ?? message.blocks ?? []\n return blocks\n .filter(block => (block.kind ?? block.type) === 'text')\n .map(block => block.text ?? '')\n .join('\\n\\n')\n .trim()\n}\n\n/**\n * Fold one event-window snapshot into ordered chat rows. The transient\n * `text-delta` chunks of the most recent attempt accumulate into a single\n * streaming row (the durable `assistant/message` replaces it on settlement).\n * @param window - the bound session's event window snapshot, or undefined.\n */\nexport function feedRows(window: FeedWindow | undefined): ChatRow[] {\n if (window === undefined) return []\n const rows: ChatRow[] = []\n const streams = new Map<string, { text: string; anchor: number; settled: boolean }>()\n let latest: string | undefined\n for (const entry of window.entries) {\n const event = entry?.event\n if (event === undefined) continue\n if (entry.type === 'event') {\n if (event.type === 'user/message') {\n // Machinery rides the log as user messages under their own source\n // kinds — plugin (runtime-context snapshots), agent-instructions\n // (<system-reminder> injections), tool (results). Only the learner's\n // own submits carry source.kind 'user' (live 0.14.0 catch: the panel\n // showed \"Current runtime context…\" and skill reminders as bubbles).\n if (event.data?.source?.kind !== 'user') continue\n const text = userText(event)\n if (text !== '') rows.push({ key: `u${event.seq}`, role: 'user', text })\n } else if (event.type === 'assistant/message') {\n const text = assistantText(event)\n if (text !== '') rows.push({ key: `a${event.seq}`, role: 'assistant', text })\n }\n // tool/call, tool/result, step/*, assistant/attempt: the study view never\n // mirrored tool traffic — the host conversation's toolviews own it.\n // A durable row appended after an attempt's first chunk marks that\n // attempt settled — its streaming row must not linger.\n for (const s of streams.values()) {\n if (rows.length > s.anchor) s.settled = true\n }\n continue\n }\n // transient live chunk: accumulate text deltas per attempt. The streaming\n // row anchors where the attempt's FIRST chunk appeared in the durable\n // row order (deltas arrive between the rows that precede/follow them).\n const chunk = event.data?.chunk\n if (chunk === undefined || chunk.type !== 'text-delta') continue\n const attemptId = event.data?.attemptId ?? String(event.seq)\n const stream = streams.get(attemptId)\n if (stream === undefined) streams.set(attemptId, { text: chunk.text ?? '', anchor: rows.length, settled: false })\n else stream.text += chunk.text ?? ''\n latest = attemptId\n }\n const stream = latest !== undefined ? streams.get(latest) : undefined\n if (stream !== undefined && !stream.settled) {\n const text = stream.text.trim()\n if (text !== '') rows.splice(stream.anchor, 0, { key: 'streaming', role: 'streaming', text })\n }\n return rows\n}\n","/**\n * The read-aloud playback controller: walks a lesson's sentence queue over a\n * synthesizer engine, falling back to the browser's speechSynthesis the\n * moment the Edge synthesis path fails (offline, endpoint gone, 5xx). Plain\n * TypeScript with injected engines so the fallback decision is unit-testable\n * without audio hardware.\n * @module dsh-plugin-lookatstudy/client/readaloud\n */\n\n/** One speakable backend: Edge-over-dashboard-audio, or window.speechSynthesis. */\nexport interface SpeechEngine {\n /** Speak one sentence; resolves when playback finishes, rejects on failure. */\n speak(text: string): Promise<void>\n pause(): void\n resume(): void\n /** Hard-stop; the controller also drops its queue on stop(). */\n cancel(): void\n /**\n * Optional warm-up for an upcoming sentence (prefetch its synthesis) — the\n * controller fires it when playback starts so the next sentence never waits\n * on the network. Engines without it are skipped.\n */\n prewarm?(text: string): void\n}\n\nexport type ReadAloudEngineName = 'edge' | 'system'\nexport type ReadAloudState = 'idle' | 'speaking' | 'paused'\n\nexport interface ReadAloudStatus {\n index: number\n total: number\n engine: ReadAloudEngineName | null\n state: ReadAloudState\n /** Set once the primary engine failed and the system voice took over. */\n degraded: boolean\n}\n\nconst sleep = (ms: number): Promise<void> => new Promise(resolve => { setTimeout(resolve, ms) })\n\nexport class ReadAloudController {\n private index = 0\n private stopped = false\n private paused = false\n private engine: ReadAloudEngineName | null = null\n private degraded = false\n private run: Promise<void> | null = null\n\n constructor(\n private readonly sentences: readonly string[],\n private readonly primary: SpeechEngine,\n private readonly fallback: SpeechEngine,\n private readonly notify?: (status: ReadAloudStatus) => void,\n ) {}\n\n private emit(state: ReadAloudState): void {\n this.notify?.({ index: this.index, total: this.sentences.length, engine: this.engine, state, degraded: this.degraded })\n }\n\n /** Speak the whole queue; the returned promise settles when done or stopped. */\n start(): Promise<void> {\n if (this.run !== null) return this.run\n this.stopped = false\n this.run = (async () => {\n this.engine = 'edge'\n this.emit('speaking')\n while (!this.stopped && this.index < this.sentences.length) {\n const text = this.sentences[this.index]!\n try {\n await this.speakOn(text)\n } catch {\n if (this.stopped) return\n if (this.engine === 'edge') {\n // The one-way ratchet: Edge fails → system voice finishes the lesson.\n this.degraded = true\n this.engine = 'system'\n this.emit('speaking')\n try {\n await this.speakOn(text)\n } catch {\n this.emit('idle')\n return // even the system voice is gone (no voices installed); give up honestly\n }\n } else {\n this.emit('idle')\n return\n }\n }\n if (this.stopped) return\n this.index += 1\n this.emit('speaking')\n }\n this.emit('idle')\n })()\n return this.run\n }\n\n private async speakOn(text: string): Promise<void> {\n const impl = this.engine === 'system' ? this.fallback : this.primary\n // Warm the NEXT sentence while this one plays — the next speak() then\n // hits the engine's own prefetch instead of the network.\n const next = this.sentences[this.index + 1]\n if (next !== undefined) impl.prewarm?.(next)\n await impl.speak(text)\n // A pause pressed mid-utterance holds the QUEUE too, not just the audio —\n // otherwise a long pause races on to the next sentence.\n while (this.paused && !this.stopped) await sleep(120)\n }\n\n pause(): void {\n if (this.stopped || this.paused) return\n this.paused = true\n ;(this.engine === 'system' ? this.fallback : this.primary).pause()\n this.emit('paused')\n }\n\n resume(): void {\n if (!this.paused) return\n this.paused = false\n ;(this.engine === 'system' ? this.fallback : this.primary).resume()\n this.emit('speaking')\n }\n\n /** Stop everything; the controller is single-use — build a fresh one to replay. */\n stop(): void {\n this.stopped = true\n this.paused = false\n this.primary.cancel()\n this.fallback.cancel()\n this.emit('idle')\n }\n\n get status(): ReadAloudStatus {\n return { index: this.index, total: this.sentences.length, engine: this.engine, state: this.stopped ? 'idle' : this.paused ? 'paused' : 'speaking', degraded: this.degraded }\n }\n}\n","/**\n * Shared pure helpers for the study panel's pieces: chat-row shapes, tree\n * status copy, quiz-option extraction, and section default expansion. The\n * panel itself lives in `panel.tsx`; since 0.14.0 nothing here touches the\n * host composer or the conversation view.\n * @module dsh-plugin-lookatstudy/client/views\n */\n\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport { tr, type StudyT } from './locale.ts'\n\n/** One rendered tutor chat row (fed by session-feed's fold). */\nexport interface ChatRow {\n readonly key: string\n readonly role: 'user' | 'assistant' | 'error' | 'streaming' | 'thinking'\n readonly text: string\n}\n\n/** Small inline error surface for failed write actions (shared with settings). */\nexport function ActionError({ error }: { error: string | null }): ReactNode {\n if (error === null) return null\n return createElement('div', { className: 'lks-propcard-err' }, error)\n}\n\n/**\n * Tooltip text for one course-tree status glyph. Pure (the translator is an\n * optional parameter defaulting to the active one, evaluated per call).\n */\nexport function statusTitle(kind: string, status: string, t: StudyT = tr): string {\n if (kind === 'exam') return t('status.exam')\n switch (status) {\n case 'mastered': return t('status.mastered')\n case 'in_progress': return t('status.in_progress')\n case 'available': return t('status.available')\n default: return t('status.locked')\n }\n}\n\n/**\n * Extract the last quiz option block (2–4 consecutive `A. …` `B. …` lines,\n * letters strictly consecutive from A) from an assistant reply — the raw text\n * of the tutor's question becomes the clickable answer surface. Pure.\n */\nexport function quizOptions(text: string): ReadonlyArray<{ letter: string; text: string }> {\n const runs: Array<Array<{ letter: string; text: string }>> = []\n let run: Array<{ letter: string; text: string }> = []\n const flush = (): void => {\n if (run.length >= 2) runs.push(run)\n run = []\n }\n for (const line of text.split('\\n')) {\n // Two accepted shapes. The space after the letter punctuation is OPTIONAL\n // (\"A.py 脚本\"), and a markdown table row (\"| **A** | `.py` 脚本 … |\")\n // parses too — the tutor drifts into tables despite the persona's format\n // directive (live 0.14.0 catch: the quiz rendered as plain text and the\n // options stayed unclickable).\n const plain = /^([A-D])[.、:)]\\s*(.+)$/.exec(line.trim())\n const table = /^\\|\\s*\\*{0,2}([A-D])\\*{0,2}\\s*\\|\\s*(.+?)\\s*\\|$/.exec(line.trim())\n const match = plain ?? (table !== null\n ? [table[0]!, table[1]!, table[2]!.replaceAll('**', '').replaceAll('`', '')] as RegExpExecArray\n : null)\n if (match === null) {\n flush()\n continue\n }\n const nextLetter = run.length === 0 ? 'A' : String.fromCharCode(run[run.length - 1]!.letter.charCodeAt(0) + 1)\n if (match[1] === nextLetter) run.push({ letter: match[1], text: match[2]! })\n else {\n flush()\n if (match[1] === 'A') run.push({ letter: match[1], text: match[2]! })\n }\n }\n flush()\n return runs.at(-1) ?? []\n}\n\n/**\n * Default expansion for one rail section: collapsed when every study lesson is\n * done (mastered) or not yet reachable (locked). The focus lesson's section\n * stays open; exam nodes never force a section open. Pure.\n */\nexport function sectionDefaultOpen(section: { lessons: ReadonlyArray<{ kind: string; status: string; focus: boolean }> }): boolean {\n return section.lessons.some(l => l.focus || (l.kind !== 'exam' && l.status !== 'mastered' && l.status !== 'locked'))\n}\n","/**\n * The study panel — the sidebar-entry surface that takes over the center\n * column, arranged like upstream LookatStudy's app (App.tsx): 左 course rail\n * (tree/import/review) | 中 the tutor chat stream with its OWN composer (the\n * host composer is never involved) | 右 the notebook (讲解/概念图/笔记).\n *\n * Interaction model aligned with upstream: opening a lesson only FOCUSES it\n * (state-side attempt: in_progress + BKT seed + unlocks — zero LLM); the\n * tutor engages when the learner sends a message or taps a starter, which\n * lazily mints and stages the lesson's thread and prompts through the host's\n * session face. The host is exactly the conversation-model + agent-turn\n * engine; the thread's event window streams the reply back into the chat.\n * @module dsh-plugin-lookatstudy/client/panel\n */\n\nimport { createElement, useEffect, useRef, useState } from 'react'\nimport type { CSSProperties, KeyboardEvent as ReactKeyboardEvent, ReactNode } from 'react'\nimport {\n IconBoltFill16, IconBookFill16, IconCrownFill16, IconDownloadOutline16,\n IconGoalOutline16, IconGlobeOutline14, IconLoadingOutline16, IconLockFill16,\n IconRefreshOutline16, IconStarFill16, IconTrashOutline16,\n} from './icons.tsx'\nimport type { ClientContext, SessionPromptFace } from './faces.ts'\nimport { useStudy, storedTtsVoice } from './data.ts'\nimport { renderMarkdown } from '../markdown.ts'\nimport { enhanceRendered, setEnhanceDeps } from './enhance.ts'\nimport { renderLessonConceptMap } from './diagrams.ts'\nimport { speechSentencesOf } from '../vendor/speech-text.ts'\nimport { speakMathInSentence } from '../vendor/math-speech.ts'\nimport { feedRows } from './session-feed.ts'\nimport { ReadAloudController, type ReadAloudStatus, type SpeechEngine } from './readaloud.ts'\nimport { statusTitle, quizOptions, sectionDefaultOpen } from './views.tsx'\nimport { tr } from './locale.ts'\n\n/** The three souls in pill order (same shape as the study tab's pills). */\nconst MODES: ReadonlyArray<{ id: 'direct' | 'guide' | 'practice'; labelKey: string; hintKey: string }> = [\n { id: 'direct', labelKey: 'soul.direct', hintKey: 'soul.direct.hint' },\n { id: 'guide', labelKey: 'soul.guide', hintKey: 'soul.guide.hint' },\n { id: 'practice', labelKey: 'soul.practice', hintKey: 'soul.practice.hint' },\n]\n\nconst ZONES: ReadonlyArray<readonly [string, string]> = [\n ['understand', 'zone.understand'],\n ['record', 'zone.record'],\n ['practice', 'zone.practice'],\n]\n\nfunction statusIcon(kind: string, status: string): ReactNode {\n if (kind === 'exam') return createElement(IconGoalOutline16, { size: 14 })\n if (status === 'mastered') return createElement(IconCrownFill16, { size: 14 })\n if (status === 'in_progress') return createElement(IconBookFill16, { size: 14 })\n if (status === 'available') return createElement(IconStarFill16, { size: 14 })\n return createElement(IconLockFill16, { size: 14 })\n}\n\nfunction examOpen(lessons: ReadonlyArray<{ kind: string; masteryPct: number | null }>): boolean {\n return lessons.every(l => l.kind !== 'study' || (l.masteryPct ?? 0) >= 50)\n}\n\nfunction titleMatches(title: string, query: string): boolean {\n const keys = query.trim().toLowerCase().split(/\\s+/).filter(Boolean)\n return keys.every(k => title.toLowerCase().includes(k))\n}\n\nexport type PanelSend = (text: string) => void\n\n/**\n * Whether the host session list actually knows a bound thread id. A restart\n * can drop young sessions from persistence; an empty byId means the list has\n * not hydrated yet (assume known — the send's own open surfaces a real error).\n * Pure over the injected list snapshot.\n */\nexport function sessionKnown(ctx: ClientContext, id: string): boolean {\n const byId = (ctx.sessions.list?.getSnapshot() as { byId?: Readonly<Record<string, unknown>> } | undefined)?.byId\n if (byId === undefined) return true\n return Object.keys(byId).length === 0 ? true : id in byId\n}\n\nfunction textOf(blocks: readonly { type?: string; text?: string }[] | undefined): string {\n return (blocks ?? []).filter(b => (b.type ?? 'text') === 'text').map(b => b.text ?? '').join('\\n\\n').trim()\n}\n\n/** One rendered chat row (shared shape with the read-aloud era's renderer). */\nfunction chatRow(row: { key: string; role: string; text: string }, interactive?: { send: PanelSend }): ReactNode {\n if (row.role === 'user') {\n return createElement('div', { key: row.key, className: 'lks14-msg lks14-msg-user' }, row.text)\n }\n const cls = row.role === 'streaming' ? 'lks14-msg lks14-msg-assistant streaming' : 'lks14-msg lks14-msg-assistant'\n const body = createElement('div', {\n key: row.key,\n className: cls,\n dangerouslySetInnerHTML: { __html: renderMarkdown(row.text) },\n })\n const options = interactive === undefined || row.role === 'streaming' ? [] : quizOptions(row.text)\n if (options.length < 2) return body\n return createElement('div', { key: row.key, className: 'lks14-turn' },\n body,\n createElement('div', { className: 'lks14-quiz', title: tr('quiz.title') },\n ...options.map(opt => createElement('button', {\n key: opt.letter,\n className: 'lks14-opt',\n onClick: () => { interactive.send(tr('quiz.answer', { letter: opt.letter, text: opt.text })) },\n },\n createElement('span', { className: 'lks14-optletter' }, opt.letter),\n createElement('span', null, opt.text))),\n ))\n}\n\n/** Build the panel component bound to the client services. */\nexport function studyPanelView(ctx: ClientContext): () => ReactNode {\n return function StudyPanel(): ReactNode {\n return createElement(StudyPanelBody, { ctx, key: 'panel' })\n }\n}\n\ntype StudyData = ReturnType<typeof useStudy>['data']\n\nfunction StudyPanelBody({ ctx }: { ctx: ClientContext }): ReactNode {\n const { data, activate, setMode, setFocus, searchLessons, deleteCourse, deleteNote, bindLessonSession } = useStudy()\n const [sendError, setSendError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [rows, setRows] = useState<ReturnType<typeof feedRows>>([])\n const [feedAttached, setFeedAttached] = useState(false)\n const [draft, setDraft] = useState('')\n const lesson = data?.lesson ?? null\n const localBound = useRef<string | null>(null)\n const streamEl = useRef<HTMLDivElement | null>(null)\n\n const boundId = localBound.current ?? (lesson !== null ? data?.lessonSessions[lesson.lessonId] ?? null : null)\n\n // The tutor chat stream: subscribe to the bound lesson thread's event window.\n // The host only opens a window for the STAGED (current) session, so when the\n // binding is absent (fresh page load / panel reopen), stage the thread first\n // — an internal navigation, suppressed so it never hands the column back —\n // and attach when the session list's current flips onto it. A binding the\n // host no longer knows (dropped by a restart) renders an empty stream; the\n // next send re-mints and re-binds. Nothing in here may throw into React.\n useEffect(() => {\n if (boundId === null) {\n setRows([])\n setFeedAttached(false)\n return\n }\n setFeedAttached(false)\n let disposed = false\n let offSource: (() => void) | undefined\n let offList: (() => void) | undefined\n const attach = (): boolean => {\n const binding = (ctx.sessions as { binding(id: string): { eventSource?: { getSnapshot(): unknown; subscribe(l: () => void): () => void } } | undefined }).binding(boundId)\n const source = binding?.eventSource\n if (source === undefined) return false\n const update = (): void => { setRows(feedRows(source.getSnapshot() as never)) }\n update()\n setFeedAttached(true)\n offSource = source.subscribe(update)\n return true\n }\n const stage = (): void => {\n if (!sessionKnown(ctx, boundId)) return\n try {\n const shell = PANEL_SHELL.current\n if (shell !== null) shell.suppressHandBack(() => { ctx.sessions.open(boundId) })\n else ctx.sessions.open(boundId)\n } catch { /* staging raced a shutdown; the next send re-mints */ }\n }\n // binding() is PURE resolution — it can succeed while the session is\n // still off-stage, leaving the window unopened and the stream empty\n // (live 0.14.0 catch: 'attached-empty' forever). The window opens ⟺ the\n // session is the list's CURRENT, so reconcile stage-first, then attach.\n const reconcile = (): boolean => {\n if (disposed || offSource !== undefined) return true\n const current = ctx.sessions.list?.getSnapshot().current\n if (current !== boundId) {\n stage()\n return false\n }\n return attach()\n }\n if (!reconcile()) {\n // Staging and the history pull are async, and a thread that already IS\n // current fires no list notification at all — retry briefly instead of\n // waiting for an event that may never come; the list subscription\n // covers later flips (staging completes, current moves elsewhere…).\n let tries = 0\n const timer = setInterval(() => {\n if (disposed || offSource !== undefined || reconcile() || ++tries >= 40) clearInterval(timer)\n }, 150)\n const list = ctx.sessions.list\n if (list !== undefined) {\n offList = list.subscribe(() => {\n if (disposed || reconcile()) {\n offList?.()\n offList = undefined\n }\n })\n }\n return () => {\n disposed = true\n clearInterval(timer)\n offSource?.()\n offList?.()\n }\n }\n return () => {\n disposed = true\n offSource?.()\n offList?.()\n }\n }, [boundId, ctx])\n\n // Keep the stream pinned to the latest row.\n useEffect(() => {\n const el = streamEl.current\n if (el !== null) el.scrollTop = el.scrollHeight\n }, [rows])\n\n /** The one send path: lazily stage the lesson thread, then prompt. */\n const send = (text: string): void => {\n void (async () => {\n if (busy || lesson === null || text.trim() === '') return\n setBusy(true)\n setSendError(null)\n try {\n if (data?.active !== true) await activate(true)\n let sessionId = localBound.current ?? data?.lessonSessions[lesson.lessonId] ?? null\n if (sessionId !== null && !sessionKnown(ctx, sessionId)) sessionId = null\n if (sessionId === null) {\n const area = await fetch('/lookatstudy/api/study-workspace')\n if (!area.ok) throw new Error(`study area unavailable (HTTP ${String(area.status)})`)\n const { path } = await area.json() as { path: string }\n const workspace = await ctx.workspaces.create({ path })\n sessionId = await ctx.sessions.create({ workspaceId: workspace.workspaceId })\n localBound.current = sessionId\n await bindLessonSession(lesson.lessonId, sessionId)\n }\n // Stage the thread (its event window only opens while current); the\n // internal navigation must not hand the panel back.\n const shell = PANEL_SHELL.current\n if (shell !== null) {\n shell.suppressHandBack(() => { ctx.sessions.open(sessionId!) })\n } else {\n ctx.sessions.open(sessionId)\n }\n const actx = ctx.sessions.scope(sessionId)\n const face: SessionPromptFace | undefined = actx === undefined ? undefined : ctx.sessions.sessionOf(actx)\n if (face === undefined) throw new Error('lesson session is not addressable yet')\n const result = await face.prompt([{ type: 'text', text }], 'queue')\n if (!result.ok) throw new Error(`prompt rejected: ${result.error.code}: ${result.error.message}`)\n setDraft('')\n } catch (err) {\n setSendError(err instanceof Error ? err.message : String(err))\n } finally {\n setBusy(false)\n }\n })()\n }\n\n const body: ReactNode = createElement('div', { className: 'lks14-body' },\n createElement(CourseRail, { data, activate, setFocus, searchLessons, deleteCourse, send }),\n createElement(ChatPane, { data, lesson, rows, feedAttached, bound: boundId !== null, busy, sendError, draft, setDraft, send, setMode }),\n createElement(NotebookPane, { data, deleteNote }),\n )\n return createElement('div', { className: 'lks14', 'data-lks-panel': '' }, body)\n}\n\n/** Module-level shell handle so the panel can suppress hand-back on internal opens. */\nconst PANEL_SHELL: { current: { suppressHandBack(fn: () => void): void } | null } = { current: null }\nexport function setPanelShell(shell: { suppressHandBack(fn: () => void): void } | null): void {\n PANEL_SHELL.current = shell\n}\n\n/** 左栏:course picker, tree, review box, import. */\nfunction CourseRail({ data, activate, setFocus, searchLessons, deleteCourse, send }: {\n data: StudyData\n activate: (active: boolean) => Promise<void>\n setFocus: (id: string) => Promise<void>\n searchLessons: (query: string) => Promise<Array<{ lessonId: string; lessonTitle: string; snippet: string }>>\n deleteCourse: (courseId: string) => Promise<void>\n send: PanelSend\n}): ReactNode {\n const [selectedCourse, setSelectedCourse] = useState('')\n const [query, setQuery] = useState('')\n const [confirmDelete, setConfirmDelete] = useState(false)\n const [showImport, setShowImport] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const reportError = (err: unknown): void => { setError(err instanceof Error ? err.message : String(err)) }\n useEffect(() => {\n if (!confirmDelete) return\n const disarm = (): void => { setConfirmDelete(false) }\n const onKey = (e: KeyboardEvent): void => { if (e.key === 'Escape') disarm() }\n window.addEventListener('click', disarm)\n window.addEventListener('keydown', onKey)\n return () => {\n window.removeEventListener('click', disarm)\n window.removeEventListener('keydown', onKey)\n }\n }, [confirmDelete])\n\n const body: ReactNode = data === null\n ? createElement('div', { className: 'lks14-empty' }, tr('loading'))\n : data.courses.length === 0\n ? createElement('div', { className: 'lks14-empty' },\n tr('rail.empty.title'), createElement('br'),\n createElement('button', {\n className: 'lks-btn primary',\n style: { margin: '10px 0' },\n onClick: () => { send(tr('prompt.import', { url: 'https://github.com/microsoft/AI-For-Beginners' })) },\n }, createElement(IconDownloadOutline16, null), tr('rail.empty.demo')),\n createElement(ImportRow, { send }),\n )\n : (() => {\n const courseId = data.courses.some(c => c.courseId === selectedCourse)\n ? selectedCourse\n : data.courses[0]!.courseId\n const course = data.courses.find(c => c.courseId === courseId)!\n return createElement('div', null,\n createElement('div', { className: 'lks14-railhead' },\n data.courses.length > 1\n ? createElement('select', {\n className: 'lks-set-select',\n value: courseId,\n onChange: (e: { target: { value: string } }) => { setSelectedCourse(e.target.value); setConfirmDelete(false) },\n }, ...data.courses.map(c => createElement('option', { key: c.courseId, value: c.courseId }, c.title)))\n : createElement('div', { className: 'lks14-railtitle', title: course.title }, course.title),\n createElement('button', {\n className: `lks-btn ${confirmDelete ? 'primary' : 'ghost'}`,\n title: confirmDelete ? tr('rail.delete.title.confirm') : tr('rail.delete'),\n onClick: (e: { stopPropagation: () => void }) => {\n e.stopPropagation()\n if (!confirmDelete) { setConfirmDelete(true); return }\n setConfirmDelete(false)\n deleteCourse(courseId).then(() => { setSelectedCourse('') }, reportError)\n },\n }, confirmDelete ? tr('rail.delete.confirm') : createElement(IconTrashOutline16, { size: 14 })),\n ),\n createElement('div', { className: 'lks14-railsub' }, tr('rail.mastered', { mastered: course.mastered, total: course.total })),\n createElement('div', {\n className: `lks14-masterybar${course.avgMasteryPct === 100 ? ' gold' : ''}`,\n title: course.avgMasteryPct === null ? tr('rail.avg.none') : tr('rail.avg', { pct: course.avgMasteryPct }),\n }, createElement('i', { style: { transform: `scaleX(${(course.avgMasteryPct ?? 0) / 100})` } })),\n createElement('input', {\n className: 'lks14-search',\n type: 'search',\n placeholder: tr('rail.search'),\n value: query,\n onChange: (e: { target: { value: string } }) => { setQuery(e.target.value) },\n onKeyDown: (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (e.key !== 'Enter' || query.trim() === '') return\n for (const section of course.sections) {\n const hit = section.lessons.find(l => l.kind !== 'exam' && l.status !== 'locked' && titleMatches(l.title, query))\n if (hit !== undefined) {\n void setFocus(hit.id).catch(reportError)\n return\n }\n }\n void searchLessons(query).then(hits => {\n const hit = hits.find(h => h.lessonId.startsWith(`${courseId}:`)) ?? hits[0]\n if (hit !== undefined) { setQuery(''); void setFocus(hit.lessonId).catch(reportError) }\n }).catch(reportError)\n },\n }),\n data.dueCount > 0\n ? createElement('div', { className: 'lks14-duebox' },\n createElement(IconRefreshOutline16, { size: 13 }), tr('rail.due', { count: data.dueCount }),\n ...data.due.map(d => createElement('div', { key: d.lessonId, className: 'lks14-dueitem' },\n createElement('span', null, d.lessonTitle),\n d.overdueDays > 0 ? createElement('span', { className: 'lks14-over' }, tr('rail.due.over', { days: d.overdueDays })) : null)),\n createElement('button', {\n className: 'lks-btn ghost',\n style: { marginTop: '6px' },\n onClick: () => { send(tr('prompt.review')) },\n }, tr('rail.due.start')),\n )\n : null,\n ...course.sections.flatMap(section => {\n const examAllowed = examOpen(section.lessons)\n const lessons = section.lessons.filter(l => query.trim() === '' || titleMatches(l.title, query) || l.focus)\n if (lessons.length === 0) return []\n const open = query.trim() !== '' || sectionDefaultOpen(section)\n return [\n createElement('button', {\n key: section.title,\n type: 'button',\n className: 'lks14-sechead',\n 'aria-expanded': String(open),\n title: open ? tr('rail.section.collapse') : tr('rail.section.expand', { count: section.lessons.length }),\n onClick: () => { /* sections are always-open in the panel v1; the head is informational */ },\n },\n createElement('span', { className: 'lks14-secnum' }, String(section.index + 1)),\n createElement('span', { className: 'lks14-secheadt' }, section.title),\n createElement('span', { className: 'lks14-secheadc' }, open ? '▾' : '▸')),\n ...(open ? lessons.map(lesson => {\n const locked = lesson.status === 'locked' || (lesson.kind === 'exam' && !examAllowed)\n return createElement('button', {\n key: lesson.id,\n type: 'button',\n className: `lks14-node${lesson.focus ? ' focus' : ''}`,\n 'aria-disabled': locked || undefined,\n title: `${lesson.title} — ${statusTitle(lesson.kind, locked ? 'locked' : lesson.status)}`,\n onClick: () => {\n // Upstream alignment: selecting a lesson only FOCUSES it —\n // the state-side attempt runs host-side, zero LLM traffic.\n if (locked) return\n void setFocus(lesson.id).catch(reportError)\n },\n },\n createElement('span', { className: 'lks14-g' }, statusIcon(lesson.kind, locked ? 'locked' : lesson.status)),\n createElement('span', { className: 'lks14-t' }, lesson.title),\n lesson.weakConcepts > 0 ? createElement('span', { className: 'lks14-tag weak', title: tr('tag.weak', { count: lesson.weakConcepts }) }, createElement(IconBoltFill16, { size: 10 }), String(lesson.weakConcepts)) : null,\n lesson.masteryPct !== null\n ? createElement('span', { className: 'lks14-bar', title: tr('tag.mastery', { pct: lesson.masteryPct }) }, createElement('i', { style: { transform: `scaleX(${lesson.masteryPct / 100})` } }))\n : null,\n lesson.masteryPct !== null ? createElement('span', { className: 'lks14-pct' }, `${lesson.masteryPct}%`) : null,\n )\n }) : []),\n ]\n }),\n createElement('button', {\n className: 'lks-btn ghost',\n style: { marginTop: '10px' },\n onClick: () => { setShowImport(!showImport) },\n }, showImport ? tr('rail.import.close') : null, tr('rail.import.toggle')),\n showImport ? createElement(ImportRow, { send }) : null,\n )\n })()\n return createElement('div', { className: 'lks14-col lks14-rail' },\n createElement('div', { className: 'lks14-colhead' }, tr('col.rail')),\n body,\n createElement('div', { className: 'lks-propcard-err' }, error),\n )\n}\n\nfunction ImportRow({ send }: { send: PanelSend }): ReactNode {\n const [url, setUrl] = useState('')\n return createElement('div', { className: 'lks14-import' },\n createElement('div', { className: 'lks14-inputrow' },\n createElement('input', {\n className: 'lks14-search',\n type: 'url',\n placeholder: tr('rail.empty.placeholder'),\n value: url,\n onChange: (e: { target: { value: string } }) => { setUrl(e.target.value) },\n onKeyDown: (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Enter' && url.trim() !== '') send(tr('prompt.import', { url: url.trim() }))\n },\n }),\n createElement('button', {\n className: 'lks-btn primary',\n disabled: url.trim() === '',\n onClick: () => { send(tr('prompt.import', { url: url.trim() })) },\n }, tr('rail.empty.button')),\n ),\n createElement('div', { className: 'lks14-hint' },\n tr('rail.empty.hint'), createElement('br'),\n tr('rail.empty.hint2')),\n )\n}\n\n/** 中栏:the tutor chat stream with its own composer (upstream ChatStream + ChatComposer). */\nfunction ChatPane({ data, lesson, rows, feedAttached, bound, busy, sendError, draft, setDraft, send, setMode }: {\n data: StudyData\n lesson: StudyData['lesson']\n rows: ReturnType<typeof feedRows>\n feedAttached: boolean\n bound: boolean\n busy: boolean\n sendError: string | null\n draft: string\n setDraft: (text: string) => void\n send: PanelSend\n setMode: (mode: 'direct' | 'guide' | 'practice') => Promise<void>\n}): ReactNode {\n const streamEl = useRef<HTMLDivElement | null>(null)\n useEffect(() => {\n const el = streamEl.current\n if (el !== null) el.scrollTop = el.scrollHeight\n }, [rows.length])\n const dormant = data?.active !== true\n const lastAssistant = rows.reduce((acc, row, i) => row.role === 'assistant' ? i : acc, -1)\n const [error, setError] = useState<string | null>(null)\n const fire = (action: Promise<void>): void => {\n action.then(() => { setError(null) }, (err: unknown) => { setError(err instanceof Error ? err.message : String(err)) })\n }\n const starters = lesson?.starters ?? []\n const onComposerKey = (e: ReactKeyboardEvent<HTMLTextAreaElement>): void => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n if (draft.trim() !== '' && !busy) send(draft)\n }\n }\n return createElement('div', { className: 'lks14-col lks14-chat' },\n createElement('div', { className: 'lks14-colhead' },\n tr('col.tutor'),\n lesson !== null ? createElement('span', { className: 'lks14-chatlesson' }, lesson.title) : null,\n createElement('span', { className: 'lks14-pills' },\n ...MODES.map(mode => createElement('button', {\n key: mode.id,\n className: `lks14-pill${data?.mode === mode.id ? ' on' : ''}`,\n 'aria-pressed': String(data?.mode === mode.id),\n 'aria-disabled': dormant || undefined,\n title: dormant ? tr('tutor.dormant') : tr(mode.hintKey),\n onClick: () => { if (!dormant) fire(setMode(mode.id)) },\n }, tr(mode.labelKey))),\n ),\n ),\n createElement('div', {\n className: 'lks14-stream',\n ref: streamEl,\n 'data-lks-feed': rows.length > 0 ? `rows:${String(rows.length)}` : bound ? (feedAttached ? 'attached-empty' : 'waiting') : 'no-thread',\n },\n rows.length === 0\n ? createElement('div', { className: 'lks14-empty' },\n dormant ? tr('tutor.dormant') : tr('tutor.empty'), createElement('br'), tr('tutor.empty.hint'))\n : rows.map((row, i) => chatRow(row, !dormant && i === lastAssistant ? { send } : undefined)),\n ),\n createElement('div', { className: 'lks14-starters' },\n ...starters.map(s => createElement('button', {\n key: s.label,\n className: 'lks14-starter',\n 'aria-disabled': dormant || undefined,\n title: dormant ? tr('tutor.dormant') : s.message,\n onClick: () => { if (!dormant) send(s.message) },\n }, s.label)),\n ),\n createElement('div', { className: 'lks14-composer' },\n createElement('textarea', {\n className: 'lks14-composertext',\n placeholder: busy ? tr('composer.busy') : tr('tutor.empty.hint'),\n value: draft,\n rows: 2,\n disabled: dormant,\n onChange: (e: { target: { value: string } }) => { setDraft(e.target.value) },\n onKeyDown: onComposerKey,\n }),\n createElement('button', {\n className: 'lks-btn primary',\n disabled: busy || dormant || draft.trim() === '',\n onClick: () => { send(draft) },\n }, busy ? createElement(IconLoadingOutline16, { size: 13, className: 'lks-spin' }) : null, tr('composer.send')),\n ),\n sendError !== null || error !== null\n ? createElement('div', { className: 'lks-propcard-err' }, sendError ?? error)\n : null,\n )\n}\n\n/** 右栏:the notebook — 讲解/概念图/笔记 tabs (upstream NotebookPanel arrangement). */\nfunction NotebookPane({ data, deleteNote }: { data: StudyData; deleteNote: (lessonId: string, noteId: string) => Promise<void> }): ReactNode {\n const lesson = data?.lesson ?? null\n const [tab, setTab] = useState<'teach' | 'cmap' | 'notes'>('teach')\n const [error, setError] = useState<string | null>(null)\n const [armedNote, setArmedNote] = useState<string | null>(null)\n const [read, setRead] = useState<ReadAloudStatus | null>(null)\n const readCtl = useRef<ReadAloudController | null>(null)\n const [readError, setReadError] = useState<string | null>(null)\n const { tts } = useStudy()\n const proseRef = useRef<HTMLDivElement | null>(null)\n const diagRef = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n if (!armedNote) return\n const disarm = (): void => { setArmedNote(null) }\n const onKey = (e: KeyboardEvent): void => { if (e.key === 'Escape') disarm() }\n window.addEventListener('click', disarm)\n window.addEventListener('keydown', onKey)\n return () => {\n window.removeEventListener('click', disarm)\n window.removeEventListener('keydown', onKey)\n }\n }, [armedNote])\n\n const fire = (action: Promise<void>): void => {\n action.then(() => { setError(null) }, (err: unknown) => { setError(err instanceof Error ? err.message : String(err)) })\n }\n\n const startReading = (): void => {\n if (lesson === null || lesson.speechText.trim() === '') return\n readCtl.current?.stop()\n const sentences = speechSentencesOf(lesson.speechText)\n const voice = storedTtsVoice()\n // Edge-over-dashboard engine: per-run prefetch map (prewarm fills it, speak\n // consumes it) + a currentAudio ref so pause/resume reach real playback.\n const prefetch = new Map<string, Promise<Uint8Array>>()\n let currentAudio: HTMLAudioElement | null = null\n const fetchAudio = (text: string): Promise<Uint8Array> => {\n const hit = prefetch.get(text)\n if (hit !== undefined) { prefetch.delete(text); return hit }\n return tts(text, voice)\n }\n const edgeEngine: SpeechEngine = {\n speak(text: string): Promise<void> {\n return fetchAudio(text).then(buf => new Promise<void>((resolve, reject) => {\n const blobUrl = URL.createObjectURL(new Blob([buf], { type: 'audio/mpeg' }))\n const audio = new Audio(blobUrl)\n currentAudio = audio\n audio.onended = () => { URL.revokeObjectURL(blobUrl); if (currentAudio === audio) currentAudio = null; resolve() }\n audio.onerror = () => { URL.revokeObjectURL(blobUrl); if (currentAudio === audio) currentAudio = null; reject(new Error('audio playback failed')) }\n void audio.play().catch(reject)\n }))\n },\n pause(): void { currentAudio?.pause() },\n resume(): void { currentAudio?.play().catch(() => { /* mid-sentence resume raced the end */ }) },\n cancel(): void { currentAudio?.pause(); currentAudio = null },\n prewarm(text: string): void {\n if (!prefetch.has(text)) prefetch.set(text, tts(text, voice))\n },\n }\n // The system-voice fallback (0.13.0 dual-track): the controller's one-way\n // ratchet switches here the moment Edge synthesis fails.\n const systemEngine: SpeechEngine = {\n speak(text: string): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n const synth = window.speechSynthesis\n if (synth === undefined) { reject(new Error('system speech unavailable')); return }\n const utter = new SpeechSynthesisUtterance(text)\n utter.lang = voice.startsWith('en') ? 'en-US' : 'zh-CN'\n utter.onend = () => { resolve() }\n utter.onerror = () => { reject(new Error('system speech failed')) }\n synth.speak(utter)\n })\n },\n pause(): void { window.speechSynthesis?.pause() },\n resume(): void { window.speechSynthesis?.resume() },\n cancel(): void { window.speechSynthesis?.cancel() },\n }\n const controller = new ReadAloudController(\n sentences.map(s => speakMathInSentence(s)),\n edgeEngine,\n systemEngine,\n s => setRead(s),\n )\n readCtl.current = controller\n void controller.start().catch((err: unknown) => { setReadError(err instanceof Error ? err.message : String(err)) })\n }\n const stopReading = (): void => {\n readCtl.current?.stop()\n setRead(null)\n }\n\n useEffect(() => {\n if (tab !== 'cmap' || diagRef.current === null || lesson === null) return\n const el = diagRef.current\n el.textContent = ''\n const run = lesson.concepts.length === 0\n ? Promise.reject(new Error('no concepts'))\n : renderLessonConceptMap(el, lesson.title, lesson.concepts.map(c => ({ title: c.title, masteryPct: c.masteryPct })))\n run.catch((err) => {\n el.textContent = lesson.concepts.length === 0 ? tr('bb.fallback.cmap.empty') : tr('bb.fallback.cmap')\n void err\n })\n }, [tab, lesson?.lessonId, lesson?.concepts.length])\n\n useEffect(() => {\n if (tab !== 'teach' || proseRef.current === null || lesson === null) return\n void enhanceRendered(proseRef.current).catch(() => { /* degrade */ })\n }, [tab, lesson?.html])\n\n const body: ReactNode = lesson === null\n ? createElement('div', { className: 'lks14-empty' }, tr('bb.empty'), createElement('br'), tr('bb.empty.hint'))\n : createElement('div', { className: 'lks14-notebody' },\n createElement('div', { className: 'lks14-lessonhead' },\n createElement('h2', null, lesson.title),\n createElement('div', { className: 'lks14-meta' },\n `${lesson.courseTitle} · ${statusTitle('study', lesson.status)}`\n + (lesson.masteryPct === null ? '' : ` · ${tr('bb.mastery', { pct: lesson.masteryPct })}`)),\n ),\n createElement('div', { className: 'lks14-viewtabs' },\n createElement('button', { className: `lks14-viewtab${tab === 'teach' ? ' on' : ''}`, 'aria-pressed': String(tab === 'teach'), onClick: () => { setTab('teach') } }, tr('viewtab.teach')),\n createElement('button', { className: `lks14-viewtab${tab === 'cmap' ? ' on' : ''}`, 'aria-pressed': String(tab === 'cmap'), title: tr('viewtab.cmap.title'), onClick: () => { setTab('cmap') } }, createElement(IconGlobeOutline14, { size: 13 }), tr('viewtab.cmap')),\n createElement('button', { className: `lks14-viewtab${tab === 'notes' ? ' on' : ''}`, 'aria-pressed': String(tab === 'notes'), onClick: () => { setTab('notes') } }, tr('bb.notes')),\n ),\n tab === 'teach'\n ? createElement('div', null,\n createElement('div', { className: 'lks-readbar' },\n createElement('button', {\n className: 'lks-btn ghost',\n style: { padding: '3px 8px', fontSize: '12.5px', flex: 'none' },\n title: read !== null && read.state === 'speaking' ? tr('read.pause') : tr('read.play'),\n onClick: () => {\n if (read !== null && read.state === 'speaking') { readCtl.current?.pause(); return }\n if (read !== null && read.state === 'paused') { readCtl.current?.resume(); return }\n startReading()\n },\n }, read !== null && read.state === 'speaking' ? tr('read.pause') : tr('read.play')),\n read !== null\n ? createElement('button', {\n className: 'lks-btn ghost',\n style: { padding: '3px 8px', fontSize: '12.5px', flex: 'none' },\n title: tr('read.stop'),\n onClick: stopReading,\n }, tr('read.stop'))\n : null,\n read !== null && read.state !== 'idle' && lesson !== null\n ? createElement('span', { className: 'lks-readbar-cur' },\n (speechSentencesOf(lesson.speechText)[read.index] ?? '').slice(0, 80))\n : null,\n readError !== null ? createElement('span', { className: 'lks-readbar-notice' }, readError) : null,\n read !== null && read.degraded ? createElement('span', { className: 'lks-readbar-notice' }, tr('read.engine.system')) : null,\n ),\n createElement('div', { className: 'lks14-prose', ref: proseRef, dangerouslySetInnerHTML: { __html: lesson.html } }),\n )\n : tab === 'cmap'\n ? createElement('div', { className: 'lks14-prose', ref: diagRef })\n : createElement('div', { className: 'lks14-zones' },\n lesson.notes.length === 0\n ? createElement('div', { className: 'lks14-empty', style: { padding: '16px 0' } as CSSProperties }, tr('bb.notes.empty'))\n : ZONES.filter(([zone]) => lesson.notes.some(n => n.zone === zone)).map(([zone, labelKey]) =>\n createElement('div', { key: zone, className: 'lks14-zone' },\n createElement('div', { className: 'lks14-zoneh' }, tr(labelKey)),\n ...lesson.notes.filter(n => n.zone === zone).map(n => createElement('div', { key: n.id, className: 'lks-note' },\n createElement('span', { className: 'lks-note-src' }, n.source),\n createElement('button', {\n className: `lks-note-del${armedNote === n.id ? ' armed' : ''}`,\n title: armedNote === n.id ? tr('note.delete.confirm') : tr('note.delete'),\n 'aria-label': armedNote === n.id ? tr('note.delete.confirm') : tr('note.delete'),\n onClick: (e: { stopPropagation: () => void }) => {\n e.stopPropagation()\n if (armedNote !== n.id) { setArmedNote(n.id); return }\n setArmedNote(null)\n fire(deleteNote(lesson.lessonId, n.id))\n },\n }, createElement(IconTrashOutline16, { size: 12 })),\n createElement('div', { className: 'lks-note-title' }, n.title),\n createElement('div', { className: 'lks-note-text', dangerouslySetInnerHTML: { __html: renderMarkdown(n.text) } }),\n n.quote !== null ? createElement('div', { className: 'lks-note-q' }, `“${n.quote}”`) : null,\n )),\n )),\n ),\n )\n return createElement('div', { className: 'lks14-col lks14-note' },\n createElement('div', { className: 'lks14-colhead' }, tr('col.bb')),\n body,\n createElement('div', { className: 'lks-propcard-err' }, error),\n )\n}\n","/**\n * The study shell entry — the panel-entry doctrine, stardeck-style: the study\n * surface is CROSS-WORKSPACE (plugin state is workspace-independent by\n * design), so it enters through a sidebar row and takes over the center\n * column, never living inside one conversation. The conversation slot family\n * is single-occupant and external plugins cannot declare shell slots, so both\n * the row and the view are DOM-level: a plain-DOM button (never disturbs the\n * shell's reconciliation) + a container appended to the center column as an\n * extra trailing child React never manages. Visibility toggles via an <html>\n * data attribute; sibling panels evict each other through the dsh-panel-activate\n * event.\n *\n * Differences from stardeck's original: our own attribute namespace, a\n * hand-back SUPPRESSION hook (the panel itself calls sessions.open to stage\n * the focus lesson's thread — an internal navigation must not close the\n * panel; only USER session navigation hands the column back), and dsw-token\n * styling via the injected stylesheet.\n * @module dsh-plugin-lookatstudy/client/shell-entry\n */\n\nimport { createElement } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport type { ReactNode } from 'react'\n\nconst ROW_ATTRIBUTE = 'data-dsh-lookatstudy-entry'\nconst ROW_SELECTOR = `[${ROW_ATTRIBUTE}]`\nconst VIEW_ATTRIBUTE = 'data-dsh-lookatstudy-view'\nconst ACTIVE_ATTR = 'data-dsh-lookatstudy-active'\n/** Sibling panels' activation attributes, evicted when the study panel opens. */\nconst OTHER_ACTIVE_ATTRS = ['data-dsh-warroom-active', 'data-dsh-taskboard-active', 'data-dsh-ssh-active'] as const\n/** Cross-plugin activation event; detail is the activating panel name. */\nconst ACTIVATE_EVENT = 'dsh-panel-activate'\nconst PANEL_NAME = 'lookatstudy'\n/** Family rows we order against (the sibling-panel entry block). */\nconst FAMILY_SELECTORS = ['[data-dsh-warroom-entry]', '[data-dsh-ssh-entry]', '[data-dsh-taskboard-entry]', ROW_SELECTOR] as const\n\nconst SIDEBAR_COLUMN_SELECTOR = '[data-pane=\"sidebar\"], [class*=\"sidebarCol\"]'\nconst CENTER_COLUMN_SELECTOR = '[data-pane=\"conversation\"], [class*=\"centerCol\"]'\n\n/** Minimal mounting face (injectable for tests). */\ninterface SidebarMountDeps {\n createRoot(container: HTMLElement): { render(node: ReactNode): void; unmount(): void }\n doc?: Document\n}\n\n/** The host session list: current-session changes are the hand-back trigger. */\ninterface SessionListFace {\n list?: {\n getSnapshot(): { current?: unknown }\n subscribe(listener: () => void): () => void\n }\n}\n\n/** The open/close state shared by the sidebar row and the view. */\nclass ShellOpenState {\n private open = false\n private readonly listeners = new Set<() => void>()\n isOpen(): boolean { return this.open }\n setOpen(next: boolean): void {\n if (this.open === next) return\n this.open = next\n for (const l of this.listeners) l()\n }\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n}\n\nfunction sidebarRoot(doc: Document): HTMLElement | undefined {\n const column = doc.querySelector<HTMLElement>(SIDEBAR_COLUMN_SELECTOR)\n if (column === null) return undefined\n const logoOwner = column.querySelector<HTMLElement>('[class*=\"logoRow\"]')?.parentElement\n return logoOwner ?? (column.firstElementChild as HTMLElement | undefined)\n}\n\nfunction newSessionButton(root: HTMLElement): HTMLButtonElement | undefined {\n const nested = root.querySelector<HTMLButtonElement>('button[class*=\"newSession\"]')\n if (nested !== null) return nested\n for (const child of root.children) {\n if (child.tagName === 'BUTTON') return child as HTMLButtonElement\n }\n return undefined\n}\n\n/** A 16px study glyph: an open book with a mastery spark. */\nconst STUDY_ICON = '<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\"><path d=\"M2 3.2C3.2 2.4 4.9 2.2 8 3.4c3.1-1.2 4.8-1 6-.2v9.2c-1.2-.8-2.9-1-6 .2-3.1-1.2-4.8-1-6-.2V3.2z\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linejoin=\"round\"/><path d=\"M8 3.4v9.2\" stroke=\"currentColor\" stroke-width=\"1.2\"/></svg>'\n\n/** The shell surface handle: dispose unmounts everything, close yields the\n * center column back, suppressHandBack wraps internal session opens. */\nexport interface StudyShellHandle {\n dispose(): void\n close(): void\n /** Run `fn` without the session-change hand-back firing (internal opens). */\n suppressHandBack(fn: () => void): void\n}\n\n/**\n * Mount the study shell surface: sidebar row + center-column panel.\n * @param view - the panel COMPONENT (an element factory, e.g. studyPanelView(services)).\n * @param label - the entry label (locale-aware getter, re-read on paints).\n * @param dom - the DOM mounting faces (injectable for tests).\n * @param sessions - the host session list; USER navigation between sessions\n * hands the center column back (closes the panel). Hydration (the first\n * current arriving) and suppressed internal opens do not count.\n */\nexport function mountStudyShell(\n view: () => ReactNode,\n label: () => string,\n dom?: SidebarMountDeps,\n sessions?: SessionListFace,\n): StudyShellHandle {\n if (typeof document === 'undefined') {\n return { dispose: () => {}, close: () => {}, suppressHandBack: (fn) => { fn() } }\n }\n const doc = dom?.doc ?? document\n // DOM-level idempotency: an HMR re-injection must not double-mount.\n if (doc.querySelector(ROW_SELECTOR) !== null) {\n return { dispose: () => {}, close: () => {}, suppressHandBack: (fn) => { fn() } }\n }\n\n const state = new ShellOpenState()\n let suppressed = false\n\n // --- Sidebar row -------------------------------------------------------\n const row = doc.createElement('button')\n row.type = 'button'\n row.setAttribute(ROW_ATTRIBUTE, '')\n row.setAttribute('data-dsh-plugin', 'lookatstudy')\n row.setAttribute('data-dsh-part', 'sidebar-entry')\n row.className = 'lks14-sidebar-row'\n const paintRow = (): void => {\n const text = label()\n row.setAttribute('aria-label', text)\n row.setAttribute('title', text)\n row.innerHTML = `<span class=\"lks14-sidebar-icon\">${STUDY_ICON}</span><span class=\"lks14-sidebar-label\">${text}</span>`\n }\n paintRow()\n row.addEventListener('click', () => state.setOpen(!state.isOpen()))\n\n const placeRow = (): boolean => {\n const root = sidebarRoot(doc)\n if (root === undefined) return false\n const button = newSessionButton(root)\n if (button === undefined) return false\n if (row.parentElement !== root) {\n const logoRow = button.closest('[class*=\"logoRow\"]')\n const base = (logoRow !== null && logoRow.parentElement === root) ? logoRow : button\n const family = Array.from(root.children).filter(\n el => el instanceof HTMLElement && (el as HTMLElement).matches(FAMILY_SELECTORS.join(', ')),\n )\n const anchor = family.length > 0 ? family[family.length - 1]!.nextElementSibling : base.nextElementSibling\n root.insertBefore(row, anchor)\n }\n return true\n }\n\n // --- Center-column view ------------------------------------------------\n let container: HTMLElement | undefined\n let root: { render(node: ReactNode): void; unmount(): void } | undefined\n const roots = dom ?? { createRoot: (el: HTMLElement) => createRoot(el) }\n const ensureView = (): void => {\n if (container !== undefined) return\n const column = doc.querySelector<HTMLElement>(CENTER_COLUMN_SELECTOR)\n if (column === null) return\n container = doc.createElement('div')\n container.setAttribute(VIEW_ATTRIBUTE, '')\n container.setAttribute('data-dsh-plugin', 'lookatstudy')\n container.className = 'lks14-shell-view'\n column.appendChild(container)\n root = roots.createRoot(container)\n root.render(createElement(view))\n }\n\n // --- Visibility + sibling-panel mutual exclusion -----------------------\n const applyActive = (): void => {\n if (state.isOpen()) {\n for (const attr of OTHER_ACTIVE_ATTRS) doc.documentElement.removeAttribute(attr)\n doc.documentElement.setAttribute(ACTIVE_ATTR, '')\n doc.dispatchEvent(new CustomEvent(ACTIVATE_EVENT, { detail: PANEL_NAME }))\n } else {\n doc.documentElement.removeAttribute(ACTIVE_ATTR)\n }\n if (state.isOpen()) row.dataset.active = 'true'\n else delete row.dataset.active\n }\n const onOtherActivate = (event: Event): void => {\n const detail = (event as CustomEvent).detail\n if (detail !== PANEL_NAME) state.setOpen(false)\n }\n // Sidebar context clicks hand the center column back to the conversation\n // (capture phase: close before the shell processes the click).\n const SIDEBAR_ROW_SELECTOR = '[class*=\"sessionRow\"], [class*=\"projectRow\"], [class*=\"searchResultRow\"], [class*=\"searchResultWorkspace\"], [class*=\"newSession\"]'\n const onClickSidebarRow = (event: MouseEvent): void => {\n if (!state.isOpen()) return\n const target = event.target as HTMLElement | null\n if (target === null) return\n if (target.closest(SIDEBAR_ROW_SELECTOR) !== null) state.setOpen(false)\n }\n\n const bodyObserver = new MutationObserver(() => { placeRow(); ensureView() })\n bodyObserver.observe(doc.body, { childList: true, subtree: true })\n doc.addEventListener('click', onClickSidebarRow, true)\n doc.addEventListener(ACTIVATE_EVENT, onOtherActivate)\n // USER session navigation hands the center column back to the conversation\n // (the panel's own sessions.open runs under suppressHandBack, and the\n // hydration flip — undefined → first current — is not a navigation).\n let unsubscribeSessions: (() => void) | undefined\n if (sessions?.list !== undefined) {\n let lastCurrent = sessions.list.getSnapshot().current\n unsubscribeSessions = sessions.list.subscribe(() => {\n const next = sessions.list!.getSnapshot().current\n if (next === lastCurrent) return\n const wasHydration = lastCurrent === undefined\n lastCurrent = next\n if (shouldHandBack(suppressed, wasHydration)) state.setOpen(false)\n })\n }\n const unsubscribe = state.subscribe(applyActive)\n placeRow()\n ensureView()\n applyActive()\n\n return {\n close: () => state.setOpen(false),\n /** Internal session navigation (staging the lesson thread) must not hand\n * the column back — only USER navigation does. */\n suppressHandBack: (fn: () => void): void => {\n suppressed = true\n try { fn() } finally { suppressed = false }\n },\n dispose: () => {\n bodyObserver.disconnect()\n doc.removeEventListener('click', onClickSidebarRow, true)\n doc.removeEventListener(ACTIVATE_EVENT, onOtherActivate)\n unsubscribeSessions?.()\n unsubscribe()\n root?.unmount()\n container?.remove()\n row.remove()\n },\n }\n}\n\n/** Whether a session change should hand the center column back (suppressible). */\nexport function shouldHandBack(suppressed: boolean, wasHydration: boolean): boolean {\n return !suppressed && !wasHydration\n}\n","/**\n * The plugin's settings page: one `settings.section` entry riding the host's\n * settings shell. Teaching style and study mode write through the same host\n * routes the study tab uses (POST /api/mode, /api/active — the latter syncs\n * the tool registry before responding); stats and the state-file path are\n * read-only projections of the shared poll store. Pure display + the store's\n * existing write actions; no new routes, no new requests beyond the poll.\n * @module dsh-plugin-lookatstudy/client/settings\n */\n\nimport { createElement, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { useStudy, TTS_VOICES, storedTtsVoice, storeTtsVoice } from './data.ts'\nimport { tr } from './locale.ts'\nimport { ActionError } from './views.tsx'\n\n/** The three souls in pill order (same shape as the study tab's pills). */\nconst MODES: ReadonlyArray<{ id: 'direct' | 'guide' | 'practice'; labelKey: string; hintKey: string }> = [\n { id: 'direct', labelKey: 'soul.direct', hintKey: 'soul.direct.hint' },\n { id: 'guide', labelKey: 'soul.guide', hintKey: 'soul.guide.hint' },\n { id: 'practice', labelKey: 'soul.practice', hintKey: 'soul.practice.hint' },\n]\n\n/**\n * The settings section component. Props from the settings shell (`close`) are\n * unused — every control stays on the page.\n */\nexport function StudySettingsSection(): ReactNode {\n const { data, setMode, activate } = useStudy()\n const [error, setError] = useState<string | null>(null)\n const [voice, setVoice] = useState(storedTtsVoice)\n const fire = (action: Promise<void>): void => {\n action.then(() => { setError(null) }, (err: unknown) => { setError(err instanceof Error ? err.message : String(err)) })\n }\n const progress = data?.progress\n return createElement('div', { className: 'lks-root lks-settings' },\n createElement('section', { className: 'lks-set-row' },\n createElement('h3', null, tr('settings.mode')),\n createElement('p', { className: 'lks-set-hint' }, tr('settings.mode.hint')),\n createElement('div', { className: 'lks-pills' },\n ...MODES.map(mode => createElement('button', {\n key: mode.id,\n className: `lks-pill${data?.mode === mode.id ? ' on' : ''}`,\n title: tr(mode.hintKey),\n onClick: () => { fire(setMode(mode.id)) },\n }, tr(mode.labelKey))),\n ),\n ),\n createElement('section', { className: 'lks-set-row' },\n createElement('h3', null, tr('settings.studyMode')),\n createElement('p', { className: 'lks-set-hint' }, tr('settings.studyMode.hint')),\n createElement('div', null,\n createElement('button', {\n className: `lks-btn ${data?.active === true ? 'ghost' : 'primary'}`,\n onClick: () => { if (data !== null) fire(activate(!data.active)) },\n }, data?.active === true ? tr('settings.turnOff') : tr('settings.turnOn')),\n createElement('span', { className: 'lks-set-state' }, data?.active === true ? tr('settings.on') : tr('settings.off')),\n ),\n ),\n createElement('section', { className: 'lks-set-row' },\n createElement('h3', null, tr('settings.voice')),\n createElement('p', { className: 'lks-set-hint' }, tr('settings.voice.hint')),\n createElement('div', null,\n createElement('select', {\n className: 'lks-set-select',\n value: voice,\n onChange: (e: { target: { value: string } }) => { storeTtsVoice(e.target.value); setVoice(e.target.value) },\n }, ...TTS_VOICES.map(v => createElement('option', { key: v.id, value: v.id }, tr(v.labelKey)))),\n ),\n ),\n progress === undefined ? null : createElement('section', { className: 'lks-set-row' },\n createElement('h3', null, tr('settings.stats')),\n createElement('ul', { className: 'lks-set-stats' },\n createElement('li', null, tr('settings.stats.courses', { count: data?.courses.length ?? 0 })),\n createElement('li', null, tr('settings.stats.xp', { xp: progress.totalXp, level: progress.level, pct: progress.levelPct })),\n createElement('li', null, tr('settings.stats.today', { xp: progress.todayXp, goal: progress.dailyGoal })),\n createElement('li', null, tr('settings.stats.streak', { days: progress.streak, best: progress.longestStreak, freeze: progress.freezeCount })),\n ),\n ),\n createElement('section', { className: 'lks-set-row' },\n createElement('h3', null, tr('settings.about')),\n createElement('p', { className: 'lks-set-hint' }, tr('settings.about.hint')),\n data?.version ? createElement('a',\n { href: `https://github.com/Kaiji-Z/dsh-plugin-lookatstudy/releases/tag/v${data.version}`, target: '_blank', rel: 'noreferrer' },\n `v${data.version}`) : null,\n ),\n createElement('section', { className: 'lks-set-row' },\n createElement('h3', null, tr('settings.stateFile')),\n createElement('p', { className: 'lks-set-hint' }, tr('settings.stateFile.hint')),\n data === null ? null : createElement('code', { className: 'lks-set-path' }, data.statePath),\n ),\n createElement(ActionError, { error }),\n )\n}\n","/**\n * The composer dock status pill: one `conversation.composer.dock` entry (the\n * ambient readout band under the composer, next to the token stats) showing\n * due reviews / streak / level while the study surface is ACTIVE — dormant\n * installs render nothing at all, the same visibility the tool registry has.\n * Data rides the shared 3 s poll store (no extra requests). Display-only:\n * conversation-view tab switching is ui-conversation-private (the starter\n * button's limitation), so the tooltip points at the Study tab instead.\n * @module dsh-plugin-lookatstudy/client/dock\n */\n\nimport { createElement } from 'react'\nimport { IconBoltFill16, IconFlameFill16 } from './icons.tsx'\nimport type { ReactNode } from 'react'\nimport { useStudy } from './data.ts'\nimport { tr } from './locale.ts'\n\n/** Pure projection of the pill's segments (testable without React). `muted` marks zero-value segments — an empty state must not wear a warning color. */\nexport function dockSegments(progress: { dueCount: number; streak: number; level: number }): ReadonlyArray<{ key: string; text: string; muted: boolean }> {\n return [\n { key: 'due', text: tr('dock.due', { count: progress.dueCount }), muted: progress.dueCount === 0 },\n { key: 'streak', text: tr('dock.streak', { days: progress.streak }), muted: progress.streak === 0 },\n { key: 'lv', text: tr('dock.lv', { level: progress.level }), muted: false },\n ]\n}\n\n/** The dock entry component; renders nothing while data is loading or dormant. */\nexport function StudyDockPill(): ReactNode {\n const { data } = useStudy()\n if (data === null || data.active !== true) return null\n const segments = dockSegments({ dueCount: data.dueCount, streak: data.progress.streak, level: data.progress.level })\n return createElement('span', {\n className: 'lks-root lks-dockpill',\n title: tr('dock.title', { due: data.dueCount, streak: data.progress.streak, level: data.progress.level }),\n },\n ...segments.map(s => createElement('span', { key: s.key, className: `lks-dockseg lks-dock-${s.key}${s.muted ? ' lks-muted' : ''}` },\n s.key === 'due' ? createElement(IconBoltFill16, { size: 11 }) : s.key === 'streak' ? createElement(IconFlameFill16, { size: 11 }) : null, s.text)),\n )\n}\n","/**\n * Keyed `tool.call.toolview` entries: learning-aware cards for the\n * conversation tab's tool rows (our own study tab renders its own chips, but\n * learning also happens in the plain 对话 view). One generic card body over\n * the result's `meta` (the tool's presentationMeta lines — replay-safe by\n * construction) with a per-tool header; `study_record_answer` additionally\n * parses its args for a live ✓/✗ tone while the call is still running.\n * @module dsh-plugin-lookatstudy/client/toolviews\n */\n\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport { tr } from './locale.ts'\n\n/** Structural slice of ToolCallBlock the views consume. */\nexport interface ToolBlockFace {\n kind?: string\n name?: string\n argsRaw?: string\n content?: ReadonlyArray<{ type: string; text?: string }>\n isError?: boolean\n meta?: unknown\n}\n\n/** Props slice the slot passes (ToolCallOwnerProps). */\nexport interface ToolViewPropsFace {\n toolName: string\n block: ToolBlockFace\n}\n\n/** Header glyph per tool (missing tools simply use the generic row). */\nconst TOOL_GLYPHS: Record<string, string> = {\n study_record_answer: '✍️',\n study_lesson: '📘',\n study_due_reviews: '🔁',\n study_exam_result: '🎯',\n}\n\n/** The ✓/✗ tone from record_answer's args; null for other shapes. Pure. */\nexport function answerTone(argsRaw: string | undefined): { correct: boolean; concept: string } | null {\n if (argsRaw === undefined) return null\n try {\n const args = JSON.parse(argsRaw) as { concept?: unknown; correct?: unknown }\n return {\n correct: args.correct === true,\n concept: typeof args.concept === 'string' && args.concept !== '' ? args.concept : tr('chip.unattributed'),\n }\n } catch {\n return null\n }\n}\n\n/** Card lines: the result's meta (string[]) when present, else its text content. Pure. */\nexport function metaLines(block: ToolBlockFace): string[] {\n if (Array.isArray(block.meta)) {\n const lines = (block.meta as unknown[]).filter((x): x is string => typeof x === 'string')\n if (lines.length > 0) return lines\n }\n return (block.content ?? [])\n .filter(b => b.type === 'text' && typeof b.text === 'string')\n .map(b => (b.text as string).split('\\n'))\n .flat()\n .filter(l => l.trim() !== '')\n}\n\n/** Shared card body: header + lines; error results keep their text visible. */\nfunction studyToolCard(toolName: string, block: ToolBlockFace, extra?: ReactNode): ReactNode {\n const lines = metaLines(block)\n const glyph = TOOL_GLYPHS[toolName] ?? '🔧'\n return createElement('div', { className: `lks-tv${block.isError === true ? ' err' : ''}` },\n extra ?? null,\n createElement('span', { className: 'lks-tv-head' }, `${glyph} ${toolName}`),\n lines.length === 0\n ? null\n : createElement('div', { className: 'lks-tv-lines' },\n ...lines.map((line, i) => createElement('div', { key: String(i), className: 'lks-tv-line' }, line)),\n ),\n )\n}\n\n/** study_record_answer: live ✓/✗ tone from args + the graded line from meta. */\nexport function StudyAnswerView({ toolName, block }: ToolViewPropsFace): ReactNode {\n const tone = answerTone(block.argsRaw)\n const chip = tone === null ? null : createElement('span', { className: `lks-tv-chip ${tone.correct ? 'ok' : 'bad'}` },\n tr(tone.correct ? 'chip.correct' : 'chip.wrong', { concept: tone.concept }))\n return studyToolCard(toolName, block, chip)\n}\n\n/** Generic meta-line card for the read-type study tools. */\nexport function StudyLessonView({ toolName, block }: ToolViewPropsFace): ReactNode {\n return studyToolCard(toolName, block)\n}\n\n/** study_due_reviews list card. */\nexport function StudyDueView({ toolName, block }: ToolViewPropsFace): ReactNode {\n return studyToolCard(toolName, block)\n}\n\n/** Stars from the exam card's presentationMeta line (`N★`); null when absent. Pure. */\nexport function examStars(block: ToolBlockFace): { stars: number; best: number } | null {\n const lines = metaLines(block)\n for (const line of lines) {\n const m = /^([0-3])★\\s*\\(?(?:best\\s*([0-3])★)?/.exec(line.trim()) ?? /^(?:本次\\s*)?([0-3])★/.exec(line.trim())\n if (m !== null) return { stars: Number(m[1]), best: Number(m[2] ?? m[1]) }\n }\n return null\n}\n\n/** study_exam_result star card — and the failure moment finally gets its line. */\nexport function StudyExamView({ toolName, block }: ToolViewPropsFace): ReactNode {\n const r = examStars(block)\n const stars = r === null\n ? null\n : createElement('span', { className: 'lks-tv-stars' },\n [0, 1, 2].map(i => createElement('span', { key: i, className: i < r.stars ? 'full' : 'dim' }, '★')),\n createElement('span', { className: 'lks-tv-stars-label' }, tr('tv.exam.stars', { n: r.stars, best: r.best })))\n const fail = r !== null && r.stars === 0 ? createElement('div', { className: 'lks-tv-failnote' }, tr('tv.exam.fail')) : null\n return studyToolCard(toolName, block, createElement('span', null, stars, fail))\n}\n","/**\n * Browser half of dsh-plugin-lookatstudy (`dsh.client`) — since 0.14.0 the\n * study surface is a SIDEBAR-ENTRY PANEL (the stardeck panel-entry doctrine):\n * a 「学习」 row in the sidebar toggles a center-column takeover arranged like\n * upstream LookatStudy's app (rail | tutor chat | notebook). The host is the\n * conversation-model + agent-turn engine; the panel drives it through the\n * session face and renders from the thread's event window. Host conversation\n * surfaces we still ride: the settings section, the composer dock pill, and\n * the keyed tool cards (visible when a lesson thread is opened in the host\n * conversation). Styles inject once; the locale namespace rides the framework\n * service.\n * @module dsh-plugin-lookatstudy/client\n */\n\nimport type { ReactNode } from 'react'\nimport type { ClientContext } from './faces.ts'\nimport { ensureStudyStyles } from './styles.ts'\nimport { studyPanelView, setPanelShell } from './panel.tsx'\nimport { mountStudyShell } from './shell-entry.ts'\nimport { StudySettingsSection } from './settings.tsx'\nimport { StudyDockPill } from './dock.tsx'\nimport { StudyAnswerView, StudyDueView, StudyExamView, StudyLessonView, type ToolViewPropsFace } from './toolviews.tsx'\nimport { registerStudyLocale, setStudyTranslator, studyTranslator, tr, type LocaleServiceFace } from './locale.ts'\n\n/** The toolview components' expected props (structural slice of ToolCallViewProps). */\ntype ToolViewProps = ToolViewPropsFace\n\nexport { feedRows } from './session-feed.ts'\nexport type { ChatRow } from './views.tsx'\n\nexport const inject = ['slots', 'workspaces', 'sessions', 'locale']\n\n/**\n * Register the study surfaces: styles + locale once; the sidebar-entry panel\n * carries the whole study UI; the host-native extras (settings, dock, tool\n * cards) ride their slots as before.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ensureStudyStyles()\n const localeSvc = (ctx as { locale?: LocaleServiceFace }).locale\n ctx.effect(() => registerStudyLocale(localeSvc), 'lookatstudy.locale()')\n setStudyTranslator(studyTranslator(localeSvc))\n\n // The study panel: sidebar row in, center-column takeover out. The host\n // session list rides along so USER navigation hands the column back.\n const shell = mountStudyShell(studyPanelView(ctx), () => tr('tab.label'), undefined, ctx.sessions)\n setPanelShell(shell)\n ctx.effect(() => () => {\n setPanelShell(null)\n shell.dispose()\n }, 'lookatstudy.studyPanel()')\n\n ctx.slots.inject('settings.section', () => ctx.slots.register(\n { name: 'settings.section', id: 'lookatstudy', order: 30, label: () => tr('settings.nav'), locale: 'lookatstudy' },\n StudySettingsSection,\n ))\n ctx.slots.inject('conversation.composer.dock', () => ctx.slots.register(\n { name: 'conversation.composer.dock', id: 'lookatstudy-status', order: 10, locale: 'lookatstudy' },\n StudyDockPill,\n ))\n // Learning-aware cards for the conversation tab's tool rows (keyed by wire\n // tool name; unregistered study tools keep the generic row).\n const toolviews: ReadonlyArray<[string, (props: ToolViewProps) => ReactNode]> = [\n ['study_record_answer', StudyAnswerView],\n ['study_lesson', StudyLessonView],\n ['study_due_reviews', StudyDueView],\n ['study_exam_result', StudyExamView],\n ]\n for (const [tool, component] of toolviews) {\n ctx.slots.inject('tool.call.toolview', () => ctx.slots.register(\n { name: 'tool.call.toolview', key: tool, locale: 'lookatstudy' },\n component,\n ))\n }\n}\n"],"mappings":"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"}
|