dsh-plugin-lookatstudy 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["createElement","useSyncExternalStore","useState","createElement","createElement","useRef","useState","createElement","useState","useRef","createElement","useRef","useState","createElement","MODES","createElement","useState","useRef","createRoot","createElement","useState","createElement","createElement","createElement"],"sources":["../src/client/upstream-theme.ts","../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/toast.ts","../src/client/quizcard.tsx","../src/client/artifact-cards.tsx","../src/client/companion.tsx","../src/client/highlights.ts","../src/client/views.tsx","../src/vendor/map-layout.ts","../src/client/maprail.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 * The upstream LookatStudy skin (0.16.0 P9a) — the 1:1 visual port. Upstream\n * is a Duolingo-style dark-first design system defined in its tailwind.config\n * + index.css :root; this module carries those EXACT tokens (scoped to the\n * panel root `.lks-ui`) and redefines the panel's class vocabulary to match\n * upstream's components: the three-column surface ladder (rail darkest / chat\n * surface-1 / notebook surface-2, no borders — depth by color step), the\n * floating blur app-header, 3D push-down buttons, the claude.ai-style\n * composer capsule, chat bubbles, typing dots, and the toast capsules.\n * Host-chrome surfaces (dock pill, settings, toolviews) keep the dsw tokens —\n * only the study panel wears the upstream skin.\n * @module dsh-plugin-lookatstudy/client/upstream-theme\n */\n\nexport const UPSTREAM_CSS = `\n/* ── upstream design tokens (index.css :root, verbatim values) ── */\n.lks-ui{\n --brand:#58CC01;--brand-rgb:88 204 2;--brand-dark:#46A302;--brand-light:#7ED957;\n --brand-glow:#58CC0159;--brand-ring:#58CC0126;\n --accent:#1DB0F6;--accent-rgb:28 176 246;--accent-dark:#098CDC;--accent-light:#65C6FF;\n --accent-glow:#1DB0F64C;--accent-ring:#1DB0F626;\n --gold:#FFC801;--gold-rgb:255 200 0;--gold-dark:#D59800;--gold-light:#FFE294;\n --gold-glow:#FFC80173;--gold-ring:#FFC80133;\n --warning:#FF4B4B;--warning-rgb:255 75 75;--warning-dark:#D01C29;--warning-light:#FF7E77;\n --warning-glow:#FF4B4B4C;--warning-ring:#FF4B4B26;--warning-tint:#FF4B4B1A;--warning-tint-border:#FF4B4B40;\n --review:#FA6E1D;--review-rgb:255 122 26;--review-glow:#FA6E1D26;--review-tint:#FA6E1D1A;\n --exam:#A855F7;--exam-rgb:168 85 247;--exam-dark:#7E22CE;--exam-light:#C084FC;--exam-locked:#524569;\n --exam-glow:#A855F759;--exam-ring:#A855F726;\n --surface-rail:#08090B;--surface-rail-rgb:8 9 11;\n --surface-0:#0C0D0F;--surface-0-rgb:12 13 15;\n --surface-1:#111114;--surface-1-rgb:17 17 20;\n --surface-2:#1A1A1D;--surface-2-rgb:26 26 29;\n --surface-3:#2A2B2E;--surface-3-rgb:42 43 46;\n --ink:#F1F2F4;--ink-rgb:245 245 250;\n --ink-strong:#FAFAFA;--ink-strong-rgb:250 250 250;\n --ink-muted:#9D9EA2;--ink-muted-rgb:166 166 176;\n --ink-faint:#88898C;--ink-faint-rgb:136 137 140;\n --border:#252629;--border-rgb:37 38 41;--border-faint:#1A1A1D;--border-faint-rgb:26 26 29;\n --shadow-rgb:0 0 0;--inner-highlight:rgb(255 255 255/0.55);\n --ease-out-expo:cubic-bezier(0.16,1,0.3,1);\n --ease-out-back:cubic-bezier(0.34,1.2,0.64,1);\n --ease-spring:cubic-bezier(0.34,1.56,0.64,1);\n --ease-out-quart:cubic-bezier(0.25,1,0.5,1);\n font-family:'DIN Round',system-ui,-apple-system,sans-serif;\n background:var(--surface-1);color:var(--ink);\n font-feature-settings:'cv02','cv03','cv04','cv11';\n -webkit-font-smoothing:antialiased;\n}\n/* the six-step type scale (rem, follows the host base) */\n.lks-ui .lks-t-caption{font-size:.75rem}.lks-ui .lks-t-label{font-size:.825rem}\n.lks-ui .lks-t-body{font-size:.875rem}.lks-ui .lks-t-lead{font-size:1rem}\n.lks-ui .lks-t-title{font-size:1.125rem}.lks-ui .lks-t-hero{font-size:1.5rem}\n\n/* ── the column ladder: no borders, depth by surface step (v0.6) ── */\n.lks-ui .lks14-rail{background:var(--surface-rail);border-right:none}\n.lks-ui .lks14-chat{background:var(--surface-1);border-right:none}\n.lks-ui .lks14-note{background:var(--surface-2)}\n.lks-ui .lks14-colhead{color:var(--ink-muted);border-bottom:1px solid var(--border-faint);letter-spacing:.02em;text-transform:none;font-size:.825rem}\n\n/* ── the floating app-header over the right half (blur + gradient fade) ── */\n.lks-ui .lks14-appheader{position:relative;z-index:30;flex:none;display:flex;align-items:center;height:46px;padding:0 14px;gap:10px}\n.lks-ui .lks14-appheader::before{content:'';position:absolute;inset:0;z-index:-1;backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);\n -webkit-mask:linear-gradient(to bottom,black 0%,black 65%,transparent 100%);mask:linear-gradient(to bottom,black 0%,black 65%,transparent 100%);\n background:linear-gradient(to bottom,rgb(var(--surface-1-rgb)/0.85) 0%,rgb(var(--surface-1-rgb)/0.55) 60%,rgb(var(--surface-1-rgb)/0.15) 85%,transparent 100%)}\n.lks-ui .lks-hdr-title{flex:1;min-width:0;font-size:.825rem;font-weight:700;color:var(--ink-strong);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.lks-ui .lks-hdr-xp{display:flex;align-items:center;gap:5px;flex:none}\n.lks-ui .lks-hdr-xpbar{width:92px;height:10px;border-radius:999px;background:rgb(var(--ink-rgb)/0.08);overflow:hidden;box-shadow:inset 0 2px 3px rgb(var(--shadow-rgb)/0.3)}\n.lks-ui .lks-hdr-xpbar i{display:block;height:100%;background:linear-gradient(90deg,var(--gold),var(--gold-light));border-radius:999px;transition:width .5s var(--ease-out-expo)}\n.lks-ui .lks-hdr-stat{display:inline-flex;align-items:center;gap:4px;font-size:.825rem;font-weight:700;color:var(--ink);flex:none}\n.lks-ui .lks-hdr-streak{color:var(--review)}\n\n/* ── 3D push-down buttons (the signature style) ── */\n.lks-ui .lks-btn{border-radius:1rem;font-weight:700;transition:all .15s var(--ease-spring)}\n.lks-ui .lks-btn.primary{background:var(--brand);color:#fff;box-shadow:0 4px 0 0 var(--brand-dark)}\n.lks-ui .lks-btn.primary:hover{background:var(--brand-light)}\n.lks-ui .lks-btn.primary:active{transform:translateY(2px);box-shadow:0 1px 0 0 var(--brand-dark)}\n.lks-ui .lks-btn.primary:disabled{background:rgb(var(--ink-rgb)/0.15);color:var(--ink-faint);box-shadow:none;transform:none;cursor:not-allowed}\n.lks-ui .lks-btn.ghost{background:rgb(var(--ink-rgb)/0.06);color:var(--ink);border:none;box-shadow:0 3px 0 0 rgb(var(--shadow-rgb)/0.2)}\n.lks-ui .lks-btn.ghost:hover{color:var(--ink-strong);background:rgb(var(--ink-rgb)/0.1)}\n.lks-ui .lks-btn.ghost:active{transform:translateY(2px);box-shadow:0 1px 0 0 rgb(var(--shadow-rgb)/0.2)}\n\n/* ── chat stream: bubbles + typing dots ── */\n.lks-ui .lks14-msg-user{background:rgb(var(--ink-rgb)/0.04);border-radius:1rem 1rem .375rem 1rem;padding:.625rem 1rem;color:var(--ink-strong);animation:lks-msg-enter .2s var(--ease-out-expo)}\n.lks-ui .lks14-msg-assistant{background:var(--surface-2);border:1px solid var(--border-faint);border-radius:1rem;padding:1rem;box-shadow:0 1px 3px -1px rgb(var(--shadow-rgb)/0.08);animation:lks-msg-enter .2s var(--ease-out-expo);font-size:.875rem;line-height:1.65}\n.lks-ui .lks14-msg.streaming{opacity:1;border-style:dashed}\n@keyframes lks-msg-enter{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}\n.lks-ui .lks14-thinking{gap:5px}\n@keyframes lks-typing-dot{0%,60%,100%{opacity:.3;transform:translateY(0)}30%{opacity:1;transform:translateY(-4px)}}\n.lks-ui .lks14-thinking i{width:6px;height:6px;border-radius:999px;background:var(--brand);display:inline-block;animation:lks-typing-dot 1.2s ease-in-out infinite}\n.lks-ui .lks14-thinking i:nth-child(2){animation-delay:.15s}\n.lks-ui .lks14-thinking i:nth-child(3){animation-delay:.3s}\n/* quiz options wear the neutral pill + brand hover */\n.lks-ui .lks-qcard-opt{background:rgb(var(--ink-rgb)/0.05);border:2px solid transparent;border-radius:.75rem;color:var(--ink)}\n.lks-ui .lks-qcard-opt:not(:disabled):hover{border-color:var(--brand)}\n.lks-ui .lks-qcard-opt.right{border-color:var(--brand);color:var(--brand-light);background:rgb(var(--brand-rgb)/0.08)}\n.lks-ui .lks-qcard-opt.wrong{border-color:var(--warning);color:var(--warning-light);background:var(--warning-tint)}\n.lks-ui .lks-qcard-next{background:var(--brand);color:#fff;box-shadow:0 3px 0 0 var(--brand-dark);border-radius:1rem}\n.lks-ui .lks-qcard-next:active{transform:translateY(2px);box-shadow:0 1px 0 0 var(--brand-dark)}\n.lks-ui .lks-qcard-action{background:rgb(var(--ink-rgb)/0.06);border:none;color:var(--ink)}\n.lks-ui .lks-qcard-action:hover{color:var(--ink-strong);background:rgb(var(--ink-rgb)/0.1)}\n\n/* ── the composer capsule (claude.ai style) + circular 3D send ── */\n.lks-ui .lks14-composer{border-top:none;background:linear-gradient(to bottom,transparent 0%,var(--surface-1) 40%)}\n.lks-ui .lks14-composer-card{flex:1;display:flex;align-items:flex-end;gap:8px;background:rgb(var(--ink-rgb)/0.05);border-radius:1rem;padding:.625rem .75rem .5rem .875rem;transition:background .15s}\n.lks-ui .lks14-composer-card:focus-within{background:rgb(var(--ink-rgb)/0.07)}\n.lks-ui .lks14-composertext{background:transparent;border:none;box-shadow:none;color:var(--ink-strong);font-size:.875rem}\n.lks-ui .lks14-composertext:focus{border-color:transparent}\n.lks-ui .lks14-composertext::placeholder{color:var(--ink-faint)}\n.lks-ui .lks-btn-send{width:36px;height:36px;border-radius:999px !important;padding:0 !important;display:inline-flex;align-items:center;justify-content:center;flex:none;background:var(--brand);color:#fff;box-shadow:0 3px 0 0 var(--brand-dark);border:none;cursor:pointer;transition:all .15s var(--ease-spring)}\n.lks-ui .lks-btn-send:hover{background:var(--brand-light)}\n.lks-ui .lks-btn-send:active{transform:translateY(2px);box-shadow:0 1px 0 0 var(--brand-dark)}\n.lks-ui .lks-btn-send:disabled{background:rgb(var(--ink-rgb)/0.15);color:var(--ink-faint);box-shadow:none;transform:none;cursor:not-allowed}\n\n/* ── toast capsules on the upstream tokens ── */\n.lks-ui .lks-toast{background:var(--surface-0);color:var(--ink-strong);border:1px solid var(--border);box-shadow:0 8px 24px -4px rgb(var(--shadow-rgb)/0.18),0 2px 6px -2px rgb(var(--shadow-rgb)/0.12)}\n.lks-ui .lks-toast .lks-toast-glyph.ok{color:var(--brand)}\n.lks-ui .lks-toast .lks-toast-glyph.warn{color:var(--gold)}\n.lks-ui .lks-toast .lks-toast-glyph.err{color:var(--warning)}\n.lks-ui .lks-toast .lks-toast-glyph.info{color:var(--accent)}\n.lks-ui .lks-toast-action{color:var(--brand)}\n\n/* ── rail: signpost section heads + due box ── */\n.lks-ui .lks14-sechead{background:rgb(var(--surface-rail-rgb)/0.55);backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px);border:1px solid rgb(var(--gold-rgb)/0.18);border-top-color:rgb(var(--gold-rgb)/0.35);border-radius:.75rem;padding:7px 10px;margin:12px 0 6px;box-shadow:0 2px 8px rgb(var(--shadow-rgb)/0.35),inset 0 1px 0 rgb(255 255 255/0.06);color:var(--gold-light)}\n.lks-ui .lks14-secnum{background:rgb(var(--gold-rgb)/0.15);color:var(--gold-light)}\n.lks-ui .lks14-node{color:var(--ink);border-radius:.75rem}\n.lks-ui .lks14-node:hover{background:rgb(var(--ink-rgb)/0.05)}\n.lks-ui .lks14-node.focus{background:rgb(var(--accent-rgb)/0.1);outline:1px solid rgb(var(--accent-rgb)/0.25)}\n.lks-ui .lks14-node[aria-disabled='true']{color:var(--ink-faint)}\n.lks-ui .lks14-search,.lks-ui .lks-set-select{background:rgb(var(--ink-rgb)/0.05);border:1px solid var(--border-faint);color:var(--ink-strong)}\n.lks-ui .lks14-search:focus{border-color:var(--accent)}\n.lks-ui .lks14-duebox{background:var(--review-tint);border:1px solid rgb(var(--review-rgb)/0.25);color:var(--ink)}\n.lks-ui .lks14-over{color:var(--warning-light)}\n.lks-ui .lks14-tag.weak{color:var(--gold-light)}\n.lks-ui .lks14-masterybar{background:rgb(var(--ink-rgb)/0.08)}\n.lks-ui .lks14-masterybar i{background:var(--gold)}\n.lks-ui .lks14-bar{background:rgb(var(--ink-rgb)/0.08)}\n.lks-ui .lks14-bar i{background:var(--gold)}\n.lks-ui .lks14-pill{color:var(--ink-muted);border-radius:999px}\n.lks-ui .lks14-pill.on{background:rgb(var(--accent-rgb)/0.12);color:var(--accent-light)}\n\n/* ── notebook: teach prose + view tabs ── */\n.lks-ui .lks14-viewtab{color:var(--ink-muted);border-radius:999px}\n.lks-ui .lks14-viewtab.on{background:rgb(var(--ink-rgb)/0.08);color:var(--ink-strong)}\n.lks-ui .lks14-prose{color:var(--ink);font-size:.9375rem;line-height:1.65}\n.lks-ui .lks14-prose h1,.lks-ui .lks-note-text h1{font-size:1.25rem;font-weight:800;color:var(--ink-strong)}\n.lks-ui .lks14-prose h2,.lks-ui .lks-note-text h2{font-size:1.1rem;font-weight:700;color:var(--ink-strong);border-bottom:1px solid var(--border);padding-bottom:.3em}\n.lks-ui .lks14-prose h3,.lks-ui .lks-note-text h3{font-size:1rem;font-weight:700;color:var(--ink)}\n.lks-ui .lks14-prose pre,.lks-ui .lks-note-text pre{background:var(--surface-0);border:1px solid var(--border);color:var(--ink)}\n.lks-ui .lks14-prose code,.lks-ui .lks-note-text code{background:rgb(var(--surface-3-rgb)/0.6);color:var(--accent-light)}\n.lks-ui .lks14-prose blockquote{border-left:3px solid var(--accent);background:rgb(var(--accent-rgb)/0.06);color:var(--ink-muted)}\n.lks-ui .lks14-prose table thead{border-bottom:2px solid var(--brand)}\n.lks-ui .lks14-prose th{color:var(--ink-strong)}\n.lks-ui .lks14-prose tbody tr:nth-child(odd){background:rgb(var(--surface-3-rgb)/0.3)}\n.lks-ui .lks14-prose td,.lks-ui .lks14-prose th{border-color:var(--border)}\n.lks-ui .lks-note{background:var(--surface-1);border:1px solid var(--border-faint)}\n.lks-ui .lks-note .lks-note-q{border-left-color:var(--gold)}\n.lks-ui .lks14-zoneh{color:var(--ink-muted)}\n.lks-ui mark.lks-hl{background:rgb(var(--gold-rgb)/0.25);border-bottom:2px solid var(--gold)}\n\n/* ── artifact + rate + proposal cards on surface-card language ── */\n.lks-ui .lks-acard,.lks-ui .lks-qcard{background:var(--surface-2);border:1px solid var(--border-faint);border-radius:1rem;box-shadow:0 1px 3px -1px rgb(var(--shadow-rgb)/0.08)}\n.lks-ui .lks-acard-head,.lks-ui .lks-qcard-head{color:var(--ink-muted)}\n.lks-ui .lks-acard-table th{background:transparent;border-bottom:2px solid var(--brand);color:var(--ink-strong)}\n.lks-ui .lks-acard-table td{border-bottom:1px solid var(--border)}\n.lks-ui .lks-acard-code{background:var(--surface-0);border:1px solid var(--border)}\n.lks-ui .lks-acard-line i{color:var(--ink-faint)}\n.lks-ui .lks-acard-note{border-left-color:var(--accent);color:var(--ink-muted)}\n.lks-ui .lks-acard-modal-body{background:var(--surface-0);border-color:var(--border)}\n.lks-ui .lks-acard-guess-opt{background:rgb(var(--ink-rgb)/0.05);border:2px solid transparent;color:var(--ink)}\n.lks-ui .lks-acard-guess-opt:not(:disabled):hover{border-color:var(--accent)}\n.lks-ui .lks-acard-guess-opt.picked{border-color:var(--accent);background:rgb(var(--accent-rgb)/0.1);color:var(--accent-light)}\n.lks-ui .lks-ratecard{background:rgb(var(--review-rgb)/0.08);border:1px solid rgb(var(--review-rgb)/0.25)}\n.lks-ui .lks-ratecard-title{color:var(--ink-strong)}\n.lks-ui .lks-ratecard-opt{background:rgb(var(--ink-rgb)/0.05);border:2px solid transparent;color:var(--ink)}\n.lks-ui .lks-ratecard-opt:not(:disabled):hover{border-color:var(--brand)}\n.lks-ui .lks-ratecard-opt.best{background:var(--gold);color:#3A2E00;box-shadow:0 3px 0 0 var(--gold-dark);border-color:transparent}\n.lks-ui .lks-ratecard-opt.best:active{transform:translateY(2px);box-shadow:0 1px 0 0 var(--gold-dark)}\n.lks-ui .lks-propbanner{background:rgb(var(--gold-rgb)/0.06);border:1px solid rgb(var(--gold-rgb)/0.3);color:var(--ink)}\n.lks-ui .lks-viewtab-badge{background:var(--brand);color:#fff}\n\n/* ── the empty states + readbar + switcher ── */\n.lks-ui .lks14-empty{color:var(--ink-muted)}\n.lks-ui .lks-readbar-cur{border-left-color:var(--accent);color:var(--ink-muted)}\n.lks-ui .lks14-switch{background:rgb(var(--ink-rgb)/0.06);border:1px solid var(--border-faint)}\n.lks-ui .lks14-switch-btn.on{background:var(--brand);color:#fff}\n.lks-ui .lks14-searchpanel{background:var(--surface-0);border:1px solid var(--border);box-shadow:0 8px 24px -4px rgb(var(--shadow-rgb)/0.18)}\n.lks-ui .lks14-searchrow:hover{background:rgb(var(--ink-rgb)/0.05)}\n.lks-ui .lks14-searchrow-snip{color:var(--ink-muted)}\n.lks-ui .lks-propcard-err{color:var(--warning-light)}\n.lks-ui .lks14-thinking{color:var(--ink-muted)}\n\n/* ══════════ P9b: the balloon course map (upstream v0.28 MapRail, static path) ══════════ */\n.lks-ui{--exam-locked-mix:#A7A2B2}\n/* the sky: upstream's canvas presets reduced to layered gradients (essence port;\n deterministic per-course via pickSky) */\n.lks-ui .lks-map{position:relative;min-height:100%;background-repeat:no-repeat}\n.lks-ui .lks-sky-day{background-image:\n radial-gradient(1.5px 1.5px at 18% 12%,rgb(255 255 255/0.5),transparent 100%),\n radial-gradient(1px 1px at 64% 8%,rgb(255 255 255/0.35),transparent 100%),\n radial-gradient(1px 1px at 82% 22%,rgb(255 255 255/0.3),transparent 100%),\n radial-gradient(120px 60px at 78% 6%,rgb(124 172 255/0.08),transparent 100%),\n linear-gradient(to bottom,#16233F 0%,#0F1B33 55%,var(--surface-rail) 100%)}\n.lks-ui .lks-sky-dusk{background-image:\n radial-gradient(1.5px 1.5px at 24% 10%,rgb(255 255 255/0.45),transparent 100%),\n radial-gradient(1px 1px at 70% 16%,rgb(255 255 255/0.3),transparent 100%),\n radial-gradient(140px 70px at 22% 10%,rgb(255 160 90/0.07),transparent 100%),\n linear-gradient(to bottom,#2A2140 0%,#1D1A38 50%,var(--surface-rail) 100%)}\n.lks-ui .lks-sky-night{background-image:\n radial-gradient(1.5px 1.5px at 12% 18%,rgb(255 255 255/0.55),transparent 100%),\n radial-gradient(1px 1px at 38% 6%,rgb(255 255 255/0.4),transparent 100%),\n radial-gradient(1px 1px at 58% 24%,rgb(255 255 255/0.35),transparent 100%),\n radial-gradient(1.5px 1.5px at 88% 10%,rgb(255 255 255/0.5),transparent 100%),\n radial-gradient(1px 1px at 76% 30%,rgb(255 255 255/0.3),transparent 100%),\n radial-gradient(90px 90px at 84% 8%,rgb(200 214 255/0.06),transparent 100%),\n linear-gradient(to bottom,#0B1024 0%,#0A0D1C 55%,var(--surface-rail) 100%)}\n.lks-ui .lks-mapsec{padding:12px 8px 4px}\n/* the signpost (map-signpost verbatim): frosted board with a gold rim */\n.lks-ui .lks-signpost{display:flex;align-items:center;gap:8px;width:100%;padding:6px 10px;border-radius:8px;font:inherit;cursor:pointer;text-align:left;\n background:rgb(var(--surface-rail-rgb)/0.55);backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px);\n border:1px solid rgb(var(--gold-rgb)/0.18);border-top-color:rgb(var(--gold-rgb)/0.35);\n box-shadow:0 2px 8px rgb(var(--shadow-rgb)/0.35),inset 0 1px 0 rgb(255 255 255/0.06)}\n.lks-ui .lks-signpost-num{width:24px;height:24px;border-radius:999px;background:var(--gold);color:#201500;font-size:11px;font-weight:800;display:flex;align-items:center;justify-content:center;flex:none;box-shadow:0 0 0 2px rgb(var(--gold-rgb)/0.4)}\n.lks-ui .lks-signpost-title{flex:1;font-size:12.5px;font-weight:700;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-shadow:0 1px 2px rgba(0,0,0,0.7)}\n.lks-ui .lks-signpost-caret{color:rgb(var(--ink-rgb)/0.5);font-size:10px}\n.lks-ui .lks-mapfield{position:relative}\n.lks-ui .lks-mapropes{position:absolute;inset:0;width:100%;pointer-events:none;overflow:visible}\n.lks-ui .lks-mapnode{position:absolute;width:110px;display:flex;flex-direction:column;align-items:center;\n animation:lks-balloon-bob var(--bob-duration,6s) ease-in-out infinite;animation-delay:var(--bob-delay,0s)}\n@keyframes lks-balloon-bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}\n@keyframes lks-path-draw{from{stroke-dashoffset:1}to{stroke-dashoffset:0}}\n@media (prefers-reduced-motion: reduce){.lks-ui .lks-mapnode{animation:none}.lks-ui .lks-bubble-available{animation:none}}\n/* the bubbles (lesson-bubble verbatim): 3D sphere lighting + ::before inner highlight */\n.lks-ui .lks-bubble{position:relative;width:56px;height:56px;border-radius:999px;border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0;transition:all .2s var(--ease-out-back);background-position:30% 25%}\n.lks-ui .lks-bubble::before{content:'';position:absolute;top:12%;left:22%;width:38%;height:28%;border-radius:50%;\n background:radial-gradient(ellipse at center,var(--inner-highlight) 0%,rgb(255 255 255/0) 70%);pointer-events:none;z-index:1}\n.lks-ui .lks-bubble[aria-disabled='true']{cursor:not-allowed}\n.lks-ui .lks-bubble.selected{outline:4px solid var(--accent);outline-offset:3px}\n.lks-ui .lks-bubble-glyph{position:relative;z-index:2;color:#fff;display:inline-flex;filter:drop-shadow(0 1px 2px rgba(0,0,0,0.4))}\n.lks-ui .lks-bubble-glyph.dim{opacity:.5}\n.lks-ui .lks-bubble-ring{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg);pointer-events:none}\n.lks-ui .lks-bubble-due{position:absolute;top:-4px;right:-4px;width:16px;height:16px;border-radius:999px;background:var(--review);color:#fff;font-size:10px;font-weight:700;display:flex;align-items:center;justify-content:center;box-shadow:0 0 0 2px var(--surface-rail);z-index:3}\n.lks-ui .lks-bubble-name{margin-top:4px;max-width:120px;font-size:11px;font-weight:700;color:#fff;background:rgb(var(--brand-rgb)/0.9);padding:2px 6px;border-radius:6px;text-align:center;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n/* locked: heavy sunken stone */\n.lks-ui .lks-bubble-locked{background:radial-gradient(circle at 35% 30%,var(--surface-3) 0%,var(--surface-2) 60%,var(--surface-1) 100%);\n box-shadow:inset 0 3px 6px rgb(var(--shadow-rgb)/0.5),inset 0 -2px 4px rgb(255 255 255/0.04),0 2px 4px rgb(var(--shadow-rgb)/0.3)}\n.lks-ui .lks-bubble-locked::before{background:radial-gradient(ellipse at center,rgb(255 255 255/0.12) 0%,rgb(255 255 255/0) 70%)}\n/* available: bright glowing sphere, your move */\n.lks-ui .lks-bubble-available{background:radial-gradient(circle at 32% 28%,var(--brand-light) 0%,var(--brand) 45%,var(--brand-dark) 100%);\n box-shadow:0 0 0 4px var(--brand-ring),0 0 16px var(--brand-glow),0 4px 10px rgb(var(--shadow-rgb)/0.25)}\n.lks-ui .lks-bubble-available:hover{transform:translateY(-3px) scale(1.03);\n box-shadow:0 0 0 5px rgb(var(--brand-rgb)/0.2),0 0 22px rgb(var(--brand-rgb)/0.5),0 6px 14px rgb(var(--shadow-rgb)/0.3)}\n.lks-ui .lks-bubble-available:active{transform:translateY(2px) scale(0.97);\n box-shadow:0 0 0 3px var(--brand-ring),0 0 10px rgb(var(--brand-rgb)/0.3),0 2px 6px rgb(var(--shadow-rgb)/0.2)}\n@keyframes lks-bubble-pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.04)}}\n.lks-ui .lks-bubble-available{animation:lks-bubble-pulse 2.4s ease-in-out infinite}\n/* in_progress: blue in motion */\n.lks-ui .lks-bubble-in-progress{background:radial-gradient(circle at 32% 28%,var(--accent-light) 0%,var(--accent) 45%,var(--accent-dark) 100%);\n box-shadow:0 0 0 4px var(--accent-ring),0 0 14px var(--accent-glow),0 4px 10px rgb(var(--shadow-rgb)/0.25)}\n.lks-ui .lks-bubble-in-progress:hover{transform:translateY(-2px) scale(1.03);\n box-shadow:0 0 0 5px rgb(var(--accent-rgb)/0.2),0 0 20px rgb(var(--accent-rgb)/0.45),0 6px 14px rgb(var(--shadow-rgb)/0.3)}\n/* mastered: gold crown halo, scaled up */\n.lks-ui .lks-bubble-mastered{background:radial-gradient(circle at 32% 28%,var(--gold-light) 0%,var(--gold) 45%,var(--gold-dark) 100%);\n box-shadow:0 0 0 4px var(--gold-ring),0 0 22px var(--gold-glow),0 0 40px rgb(var(--gold-rgb)/0.15),0 4px 12px rgb(var(--shadow-rgb)/0.25);transform:scale(1.1)}\n.lks-ui .lks-bubble-mastered::before{background:radial-gradient(ellipse at center,rgb(255 255 255/0.7) 0%,rgb(255 255 255/0) 70%)}\n.lks-ui .lks-bubble-mastered:hover{transform:scale(1.13) translateY(-2px);\n box-shadow:0 0 0 5px rgb(var(--gold-rgb)/0.25),0 0 28px rgb(var(--gold-rgb)/0.55),0 0 50px rgb(var(--gold-rgb)/0.2),0 6px 16px rgb(var(--shadow-rgb)/0.3)}\n/* exam: the section boss */\n.lks-ui .lks-exam{background:radial-gradient(circle at 32% 28%,var(--exam-light) 0%,var(--exam) 45%,var(--exam-dark) 100%);\n box-shadow:0 0 0 4px var(--exam-ring),0 0 16px var(--exam-glow),0 4px 10px rgb(var(--shadow-rgb)/0.25)}\n.lks-ui .lks-exam-locked{background:radial-gradient(circle at 35% 30%,var(--exam-locked) 0%,var(--exam-locked-mix) 60%,var(--surface-1) 100%);\n box-shadow:inset 0 3px 6px rgb(var(--shadow-rgb)/0.5),inset 0 -2px 4px rgb(255 255 255/0.04),0 2px 4px rgb(var(--shadow-rgb)/0.3)}\n.lks-ui .lks-exam-passed{background:radial-gradient(circle at 32% 28%,rgb(var(--exam-light-rgb,192 132 252)) 0%,var(--exam-light) 40%,var(--exam) 100%);\n box-shadow:0 0 0 4px var(--gold-ring),0 0 18px var(--exam-glow),0 0 28px rgb(var(--gold-rgb)/0.15),0 4px 12px rgb(var(--shadow-rgb)/0.25)}\n/* the floating rail head card (upstream map-header card) */\n.lks-ui .lks14-railhead{background:rgb(var(--surface-rail-rgb)/0.55);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid rgb(255 255 255/0.06);border-radius:12px;padding:8px 10px}\n.lks-ui .lks14-railtitle{color:#fff;font-weight:800;font-size:.875rem;text-shadow:0 1px 3px rgba(0,0,0,0.6)}\n.lks-ui .lks14-railsub{color:rgb(var(--ink-rgb)/0.6)}\n.lks-ui .lks14-masterybar{height:10px;background:rgb(0 0 0/0.4);border-radius:999px;box-shadow:inset 0 0 0 1px rgb(255 255 255/0.1);overflow:hidden}\n.lks-ui .lks14-masterybar i{background:var(--brand)}\n.lks-ui .lks14-masterybar.gold i{background:var(--gold)}\n.lks-ui .lks14-search{background:rgb(0 0 0/0.25);border:1px solid rgb(255 255 255/0.15);color:var(--ink-strong);border-radius:10px}\n.lks-ui .lks14-search:focus{border-color:var(--brand);outline:none}\n.lks-ui .lks14-search::placeholder{color:rgb(var(--ink-rgb)/0.35)}\n`\n","/**\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 */\nimport { UPSTREAM_CSS } from './upstream-theme.ts'\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,.lks14{--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{position:relative;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-righthalf{flex:1;min-width:0;display:flex;flex-direction:column;min-height:0}\n.lks14-row{flex:1;min-height:0;display:flex}\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-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\n/* toast capsules (P6, upstream Toast port): top-center under the panel head,\n container inert to pointers, items auto; exit is an animation handshake —\n reduced-motion keeps a .01ms duration so the end event still fires. */\n.lks-toasts{position:absolute;top:44px;left:50%;transform:translateX(-50%);z-index:40;display:flex;flex-direction:column;align-items:center;gap:6px;pointer-events:none;max-width:min(440px,90%)}\n.lks-toast{pointer-events:auto;display:flex;align-items:center;gap:8px;max-width:100%;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:6px 8px 6px 14px;font-size:13px;line-height:1.4;box-shadow:0 6px 24px rgba(0,0,0,.14);animation:lks-toast-in .18s ease}\n.lks-toast.exiting{animation:lks-toast-out .18s ease forwards}\n.lks-toast .lks-toast-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.lks-toast .lks-toast-glyph{flex:none}\n.lks-toast .lks-toast-glyph.ok{color:var(--lks-ok-ink)}\n.lks-toast .lks-toast-glyph.warn{color:var(--lks-warn-ink)}\n.lks-toast .lks-toast-glyph.err{color:var(--lks-err-ink)}\n.lks-toast .lks-toast-glyph.info{color:var(--dsw-alias-state-business-primary)}\n.lks-toast-action{border:none;background:none;color:var(--dsw-alias-state-business-primary);font-weight:600;font-size:13px;cursor:pointer;padding:4px 8px;border-radius:999px;flex:none}\n.lks-toast-action:hover{background:var(--dsw-alias-interactive-bg-hover)}\n.lks-toast-action:focus-visible,.lks-toast-close:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks-toast-close{border:none;background:none;color:var(--dsw-alias-label-tertiary);cursor:pointer;width:22px;height:22px;display:inline-flex;align-items:center;justify-content:center;border-radius:50%;font-size:15px;line-height:1;padding:0;flex:none}\n.lks-toast-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}\n@keyframes lks-toast-in{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}\n@keyframes lks-toast-out{to{opacity:0;transform:translateY(-6px)}}\n@media (prefers-reduced-motion:reduce){.lks-toast{animation-duration:.01ms}}\n\n\n\n\n\n\n\n\n/* the companion creature (P7): bottom-right of the panel; gentle bob idle,\n bounce while talking, pop on celebration; reduced-motion = static (a11y). */\n.lks-companion{position:absolute;right:18px;bottom:14px;z-index:20;width:56px;height:56px;cursor:pointer;filter:drop-shadow(0 4px 10px rgba(0,0,0,.18));animation:lks-comp-bob 3.2s ease-in-out infinite}\n.lks-companion:focus-visible{outline:none;filter:drop-shadow(0 0 0 2px var(--dsw-alias-state-business-primary)) drop-shadow(0 4px 10px rgba(0,0,0,.18))}\n.lks-companion.mood-talking{animation:lks-comp-talk .6s ease-in-out infinite}\n.lks-companion.mood-celebrating{animation:lks-comp-pop .5s cubic-bezier(.34,1.56,.64,1) 3}\n.lks-companion.mood-encouraging{animation:lks-comp-nudge 1.2s ease-in-out 2}\n@keyframes lks-comp-bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}\n@keyframes lks-comp-talk{0%,100%{transform:translateY(0) scale(1)}50%{transform:translateY(-7px) scale(1.06)}}\n@keyframes lks-comp-pop{0%{transform:scale(1)}40%{transform:scale(1.25) rotate(-8deg)}100%{transform:scale(1)}}\n@keyframes lks-comp-nudge{0%,100%{transform:translateX(0)}25%{transform:translateX(-5px) rotate(-4deg)}75%{transform:translateX(5px) rotate(4deg)}}\n@media (prefers-reduced-motion:reduce){.lks-companion{animation:none}}\n\n/* P5: proposal banner (ConfirmCard semantics) + thinking row + narrow switch */\n.lks-propbanner{flex:none;display:flex;align-items:center;gap:8px;margin:8px 12px 0;padding:8px 12px;border-radius:10px;background:var(--dsw-alias-state-business-tertiary);border:1px solid var(--dsw-alias-border-l2);font-size:12.5px;color:var(--dsw-alias-label-primary-bluish)}\n.lks-propbanner .lks-propbanner-why{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.lks14-thinking{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--dsw-alias-label-secondary);padding:2px 2px}\n.lks14-switch{display:none;flex-direction:row;gap:2px;align-self:center;margin:8px 0 0;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:3px}\n.lks14-switch-btn{border-radius:999px;padding:4px 16px;font-size:13px;color:var(--dsw-alias-label-secondary);background:transparent}\n.lks14-switch-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}\n.lks14-switch-btn.on{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 86%,#000);color:#fff;font-weight:600}\n@container (max-width: 900px){\n .lks14-switch{display:flex}\n .lks14-body{flex-direction:column}\n .lks14-rail{flex:none;max-height:32vh;border-right:none;border-bottom:1px solid var(--dsw-alias-border-l1)}\n .lks14-chat{min-width:0}\n .lks14-note{flex:1 1 auto;min-height:0}\n .lks14-body[data-pane='rail'] .lks14-righthalf{display:none}\n .lks14-body[data-pane='chat'] .lks14-rail,.lks14-body[data-pane='chat'] .lks14-note,\n .lks14-body[data-pane='note'] .lks14-rail,.lks14-body[data-pane='note'] .lks14-chat{display:none}\n .lks14-body[data-pane='rail'] .lks14-rail{display:flex;max-height:none;flex:1 1 auto}\n .lks14-body[data-pane='note'] .lks14-note{display:flex}\n}\n\n/* rail search results panel (P4, upstream CourseSearchPanel) */\n.lks14-searchpanel{margin:4px 0 6px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}\n.lks14-searchrow{display:flex;flex-direction:column;align-items:flex-start;gap:1px;width:100%;text-align:left;font:inherit;padding:6px 10px;cursor:pointer;border-bottom:1px solid var(--dsw-alias-border-l1)}\n.lks14-searchrow:last-child{border-bottom:none}\n.lks14-searchrow:hover{background:var(--dsw-alias-interactive-bg-hover)}\n.lks14-searchrow-title{font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-primary)}\n.lks14-searchrow-course{font-size:10.5px;color:var(--dsw-alias-label-tertiary)}\n.lks14-searchrow-snip{font-size:11px;color:var(--dsw-alias-label-secondary);line-height:1.5;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}\n\n/* review self-rating card (P3, upstream SelfRatingCard: three Memrise levels) */\n.lks-ratecard{flex:none;display:flex;flex-direction:column;gap:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-state-business-tertiary);padding:10px 12px;margin-bottom:8px}\n.lks-ratecard-title{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary-bluish)}\n.lks-ratecard-opts{display:flex;gap:6px}\n.lks-ratecard-opt{flex:1;font:inherit;font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:9px;padding:7px 10px;cursor:pointer}\n.lks-ratecard-opt:hover{border-color:var(--dsw-alias-state-business-primary)}\n.lks-ratecard-opt.again:hover{border-color:var(--dsw-alias-state-error-primary)}\n.lks-ratecard-opt.best{background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 86%,#000);color:#fff;border-color:transparent}\n.lks-ratecard-opt.best:hover{filter:brightness(1.1)}\n.lks-ratecard-opt:disabled{opacity:.6;cursor:default}\n.lks14-dueitem{font:inherit;text-align:left}\n\n/* selection-to-note (P2, upstream selection flow): the popover floats over\n the prose; persisted highlights are soft amber marks. */\n.lks14-prosewrap{position:relative}\n.lks-quote-btn{position:absolute;z-index:12;display:flex;gap:2px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;box-shadow:0 6px 24px rgba(0,0,0,.16);transform:translateX(-50%)}\n.lks-quote-btn button{border:none;background:none;color:var(--dsw-alias-label-primary);font:inherit;font-size:12.5px;font-weight:600;padding:6px 10px;cursor:pointer;border-radius:10px}\n.lks-quote-btn button:hover{background:var(--dsw-alias-interactive-bg-hover)}\n.lks-quote-btn button + button{border-left:1px solid var(--dsw-alias-border-l1)}\nmark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border-bottom:2px solid var(--dsw-alias-state-warn-primary);padding:0 1px}\n\n/* artifact cards (P1b, upstream artifacts port): chat column + notebook zones */\n.lks-acard{flex:none;margin:2px 12px 8px;border:1px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:10px 12px;display:flex;flex-direction:column;gap:8px;overflow:hidden}\n.lks-acard-head{display:flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-secondary)}\n.lks-acard-expand{margin-left:auto;border:none;background:none;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:14px;padding:2px 6px;border-radius:6px}\n.lks-acard-expand:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}\n.lks-acard-table{border-collapse:collapse;display:block;max-width:100%;overflow-x:auto}\n.lks-acard-table th,.lks-acard-table td{border:1px solid var(--dsw-alias-border-l2);padding:4px 9px;font-size:12.5px;text-align:left}\n.lks-acard-table th{background:var(--dsw-alias-bg-layer-2)}\n.lks-acard-code{background:var(--dsw-alias-markdown-code-block);border-radius:8px;padding:8px 0;overflow-x:auto;font-family:var(--dsw-font-markdown-code-block-small);font-size:12px;line-height:1.6;margin:0}\n.lks-acard-line{display:block;white-space:pre}\n.lks-acard-line i{display:inline-block;width:2.2em;color:var(--dsw-alias-label-tertiary);font-style:normal;text-align:right;padding-right:8px;user-select:none}\n.lks-acard-notes{display:flex;flex-direction:column;gap:5px}\n.lks-acard-note{font-size:12.5px;line-height:1.6;color:var(--dsw-alias-label-secondary);border-left:2px solid var(--dsw-alias-state-business-primary);padding-left:8px}\n.lks-acard-note b{color:var(--dsw-alias-label-primary);font-weight:600;margin-right:4px}\n.lks-acard-diagram{overflow-x:auto;text-align:center}\n.lks-acard-diagram svg{max-width:100%;height:auto}\n.lks-acard-modal{position:fixed;inset:0;z-index:90;background:rgba(0,0,0,.5);display:flex;align-items:center;justify-content:center;padding:32px}\n.lks-acard-modal-body{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);border-radius:14px;max-width:900px;max-height:100%;overflow:auto;padding:18px;display:flex;flex-direction:column;gap:10px}\n.lks-acard-modal-title{display:flex;align-items:center;font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary)}\n.lks-acard-guess-prompt{font-size:13.5px;line-height:1.6;color:var(--dsw-alias-label-primary)}\n.lks-acard-guess-opts{display:flex;gap:8px}\n.lks-acard-guess-opt{flex:1;font:inherit;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:10px 12px;cursor:pointer}\n.lks-acard-guess-opt:not(:disabled):hover{border-color:var(--dsw-alias-state-business-primary)}\n.lks-acard-guess-opt.picked{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-tertiary)}\n.lks-acard-guess-wait{font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.lks-acard-stage{margin-top:14px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l1)}\n.lks-acard-stage .lks-acard{margin:0}\n.lks14-zone .lks-acard{margin:0 0 8px}\n/* notebook tab badge (unseen artifacts) */\n.lks-viewtab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;border-radius:8px;background:var(--dsw-alias-state-business-primary);color:#fff;font-size:10px;font-weight:700;margin-left:5px;padding:0 4px}\n\n/* practice card (P1, upstream QuizArtifact port): chat column, below the stream */\n.lks-qcard{flex:none;margin:2px 12px 8px;border:1px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:10px 12px;display:flex;flex-direction:column;gap:8px}\n.lks-qcard-head{display:flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-secondary)}\n.lks-qcard-count{margin-left:auto;font-weight:400;color:var(--dsw-alias-label-tertiary)}\n.lks-qcard-q{display:flex;flex-direction:column;gap:6px}\n.lks-qcard-prompt{font-size:13.5px;line-height:1.55;color:var(--dsw-alias-label-primary)}\n.lks-qcard-opts{display:flex;flex-direction:column;gap:4px}\n.lks-qcard-opt{text-align:left;font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:9px;padding:6px 10px;cursor:pointer}\n.lks-qcard-opt:not(:disabled):hover{border-color:var(--dsw-alias-state-business-primary)}\n.lks-qcard-opt:disabled{cursor:default}\n.lks-qcard-opt.right{border-color:var(--dsw-alias-state-success-primary);color:var(--lks-ok-ink)}\n.lks-qcard-opt.wrong{border-color:var(--dsw-alias-state-error-primary);color:var(--lks-err-ink)}\n.lks-qcard-opt.dim{opacity:.75}\n.lks-qcard-expl{font-size:12.5px;line-height:1.6;color:var(--dsw-alias-label-secondary);border-left:2px solid var(--dsw-alias-state-business-primary);padding-left:8px}\n.lks-qcard-next{margin-top:2px;border:none;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 86%,#000);color:#fff;font:inherit;font-size:12.5px;font-weight:600;border-radius:8px;padding:5px 14px;cursor:pointer}\n.lks-qcard-next:hover{filter:brightness(1.1)}\n.lks-qcard-review .lks-qcard-q.review{padding:6px 0;border-bottom:1px dashed var(--dsw-alias-border-l1)}\n.lks-qcard-review .lks-qcard-q.review.wrong .lks-qcard-prompt{color:var(--lks-err-ink)}\n.lks-qcard-ans{font-size:12.5px;color:var(--dsw-alias-label-secondary)}\n.lks-qcard-actions{display:flex;flex-wrap:wrap;gap:6px}\n.lks-qcard-action{display:inline-flex;align-items:center;gap:5px;font:inherit;font-size:12.5px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:4px 12px;cursor:pointer}\n.lks-qcard-action:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}\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 + UPSTREAM_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 artifacts: ReadonlyArray<{ id: string; artifactType: string; title: string; data: Record<string, unknown> }>\n readonly due: boolean\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 /** Save a learner selection as a record-zone note (the highlight anchor is the quote). */\n async addUserNote(lessonId: string, quote: string): Promise<void> {\n await fetchJson('/lookatstudy/api/note/user', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ lessonId, quote }),\n })\n this.refresh()\n }\n\n /** Record one SM-2 self-rating (1 again / 4 remembered / 5 mastered). */\n async recordReview(lessonId: string, quality: 1 | 4 | 5): Promise<void> {\n await fetchJson('/lookatstudy/api/review', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ lessonId, quality }),\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 addUserNote: (lessonId: string, quote: string) => Promise<void>\n recordReview: (lessonId: string, quality: 1 | 4 | 5) => 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 addUserNote: studyStore.addUserNote.bind(studyStore),\n recordReview: studyStore.recordReview.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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\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*&gt;\\s?/.test(line)) {\n flushParagraph(paragraph)\n paragraph = []\n const quote: string[] = []\n while (i < lines.length && /^\\s*&gt;\\s?/.test(lines[i]!)) {\n quote.push(lines[i]!.replace(/^\\s*&gt;\\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.chat': '对话',\n 'pane.note': '黑板',\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 'quiz.card.title': '练习',\n 'quiz.progress': '{cur}/{total}',\n 'quiz.next': '下一题',\n 'quiz.finish': '看成绩单',\n 'quiz.score': '成绩:{correct}/{total}',\n 'quiz.right': '答对了',\n 'quiz.wrong': '答错了',\n 'quiz.youChose': '你选了',\n 'quiz.answer': '正确答案',\n 'quiz.hook': '练习卡完成:{correct}/{total} 正确。请简短点评,针对错题讲讲思路。',\n 'quiz.action.explain-wrong': '讲讲错题',\n 'quiz.action.retry': '再来一组',\n 'quiz.action.go-deeper': '深入原理',\n 'quiz.action.mark-mastered': '标记我掌握了',\n 'quiz.action.markMastered.hint': '会让导师出综合题检验,通过即标记掌握',\n 'quiz.action.next-topic': '下一个知识点',\n 'artifact.lines': '第 {from}-{to} 行:',\n 'artifact.expand': '放大查看',\n 'artifact.guess.title': '猜一猜',\n 'artifact.guess.pick': '我选「{label}」',\n 'artifact.guess.wait': '已记下你的直觉——导师下回合揭晓,看看你想得对不对',\n 'artifact.sedimented': '有新的学习产物沉淀进笔记',\n 'zone.artifacts': 'AI 产物',\n 'note.quote.ask': '提问这段',\n 'note.quote.save': '加到笔记',\n 'note.quote.template': '请讲解这段:「{text}」',\n 'note.saved': '已存入记录区笔记',\n 'review.rate.title': '复习自评——这课现在记得多牢?',\n 'review.again': '再来一次',\n 'review.remembered': '记住了',\n 'review.mastered': '完全掌握',\n 'review.done.good': '已安排下次复习',\n 'review.done.again': '明天再来一遍',\n 'review.jump': '点开这一课去复习',\n 'review.nudge': '有 {n} 课到了复习时间,先回来练一遍?',\n 'review.nudge.go': '去复习',\n 'rail.search.jump': '打开这一课',\n 'proposal.banner': '《{lesson}》可以提前毕业了',\n 'proposal.accept': '接受',\n 'proposal.decline': '再练练',\n 'proposal.accept.msg': '我接受《{lesson}》的掌握度提议,帮我标记为掌握。',\n 'proposal.decline.msg': '《{lesson}》我先再练练,暂不接受提前毕业。',\n 'tutor.thinking': '导师思考中…',\n 'header.xp': '经验值 {xp}',\n 'header.streak': '连续学习天数',\n 'header.level': '等级',\n 'map.node.locked': '锁定 — 先完成前面的课时',\n 'map.node.due': '到了复习时间',\n 'map.exam.locked': '考试未解锁 — 本章节全部课时掌握度 ≥50% 后开放',\n 'companion.poke': '戳一戳伴学伙伴',\n 'companion.poked': '伴学伙伴向你眨了眨眼',\n 'companion.form.ember': '小焰',\n 'companion.form.frost': '霜绒',\n 'companion.form.moss': '苔芽',\n 'companion.form.star': '星尘',\n 'companion.form.ink': '墨墨',\n 'settings.companion': '伴学伙伴',\n 'settings.companion.hint': '选择学习面板右下角的伴学生物形态。',\n 'quiz.msg.explain-wrong': '我刚才有题答错了,帮我讲讲为什么错、正确的思路是什么。',\n 'quiz.msg.retry': '再来一组类似的题巩固一下。',\n 'quiz.msg.go-deeper': '这组我答得不错,帮我深入讲讲背后的原理和容易混淆的地方。',\n 'quiz.msg.mark-mastered': '这课我觉得掌握了,帮我确认一下——出个综合题检验,通过了就标记为掌握。',\n 'quiz.msg.next-topic': '进入下一个知识点。',\n 'toast.region': '通知',\n 'toast.close': '关闭',\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.chat': 'Chat',\n 'pane.note': '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 'quiz.card.title': 'Practice',\n 'quiz.progress': '{cur}/{total}',\n 'quiz.next': 'Next',\n 'quiz.finish': 'See score',\n 'quiz.score': 'Score: {correct}/{total}',\n 'quiz.right': 'Correct',\n 'quiz.wrong': 'Wrong',\n 'quiz.youChose': 'You chose',\n 'quiz.answer': 'Answer',\n 'quiz.hook': 'Practice card finished: {correct}/{total} correct. Please comment briefly and walk through the wrong ones.',\n 'quiz.action.explain-wrong': 'Explain my wrong answers',\n 'quiz.action.retry': 'Another set',\n 'quiz.action.go-deeper': 'Go deeper',\n 'quiz.action.mark-mastered': 'Mark me as mastered',\n 'quiz.action.markMastered.hint': 'Asks the tutor to verify with a final question, then marks mastery',\n 'quiz.action.next-topic': 'Next topic',\n 'artifact.lines': 'Lines {from}-{to}: ',\n 'artifact.expand': 'Expand',\n 'artifact.guess.title': 'Take a guess',\n 'artifact.guess.pick': 'I pick \"{label}\"',\n 'artifact.guess.wait': 'Your instinct is noted — the tutor reveals next turn',\n 'artifact.sedimented': 'A new artifact settled into the notebook',\n 'zone.artifacts': 'AI artifacts',\n 'note.quote.ask': 'Ask about this',\n 'note.quote.save': 'Add note',\n 'note.quote.template': 'Please explain this passage: \"{text}\"',\n 'note.saved': 'Saved to the record zone',\n 'review.rate.title': 'Review self-check — how well do you remember?',\n 'review.again': 'Again',\n 'review.remembered': 'Remembered',\n 'review.mastered': 'Mastered',\n 'review.done.good': 'Next review scheduled',\n 'review.done.again': 'Back again tomorrow',\n 'review.jump': 'Open this lesson to review',\n 'review.nudge': '{n} lessons are due for review — come practice first?',\n 'review.nudge.go': 'Review now',\n 'rail.search.jump': 'Open this lesson',\n 'proposal.banner': '{lesson} is ready to graduate early',\n 'proposal.accept': 'Accept',\n 'proposal.decline': 'Keep practicing',\n 'proposal.accept.msg': 'I accept the mastery proposal for \"{lesson}\" — mark it mastered.',\n 'proposal.decline.msg': 'I want more practice on \"{lesson}\" before graduating.',\n 'tutor.thinking': 'The tutor is thinking…',\n 'header.xp': '{xp} XP',\n 'header.streak': 'Day streak',\n 'header.level': 'Level',\n 'map.node.locked': 'Locked — finish the previous lessons first',\n 'map.node.due': 'Due for review',\n 'map.exam.locked': 'Exam locked — every lesson in this section needs ≥50% mastery',\n 'companion.poke': 'Poke the companion',\n 'companion.poked': 'The companion winks at you',\n 'companion.form.ember': 'Ember',\n 'companion.form.frost': 'Frost',\n 'companion.form.moss': 'Moss',\n 'companion.form.star': 'Stardust',\n 'companion.form.ink': 'Ink',\n 'settings.companion': 'Companion',\n 'settings.companion.hint': 'Choose the companion creature in the study panel corner.',\n 'quiz.msg.explain-wrong': 'I got some questions wrong — walk me through why and the right line of thinking.',\n 'quiz.msg.retry': 'Give me another similar set to consolidate.',\n 'quiz.msg.go-deeper': 'I did well on this set — go deeper into the principles and common confusions.',\n 'quiz.msg.mark-mastered': 'I feel I have mastered this lesson — verify me with a final question and mark it mastered if I pass.',\n 'quiz.msg.next-topic': 'Move on to the next topic.',\n 'toast.region': 'Notifications',\n 'toast.close': 'Close',\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 * The panel's toast notifications — the upstream Toast.tsx port (0.15.0 P6):\n * severity capsules with per-severity durations (error outlasts success —\n * the learner needs time to read it), an optional action, dismiss, an\n * exit-animation handshake between store and view, and an injected scheduler\n * so all timing logic runs under plain node:test. One subscribable store per\n * panel; the view lives in panel.tsx (StudyToastStack).\n * @module dsh-plugin-lookatstudy/client/toast\n */\n\nexport type ToastSeverity = 'default' | 'success' | 'error' | 'warning' | 'info'\n\nexport interface ToastAction {\n readonly label: string\n readonly onClick?: () => void\n}\n\nexport interface ToastItem {\n readonly id: number\n readonly message: string\n readonly severity: ToastSeverity\n readonly duration: number\n readonly action?: ToastAction\n /** The exit animation is playing; the view calls finish() on its end. */\n exiting: boolean\n}\n\nexport interface ShowToastOpts {\n severity?: ToastSeverity\n duration?: number\n action?: ToastAction\n}\n\n/** Per-severity defaults (upstream Toast.tsx): error 6s, warning 5s, rest 4s. */\nexport const SEVERITY_DURATION: Readonly<Record<ToastSeverity, number>> = {\n default: 4000,\n success: 4000,\n info: 4000,\n warning: 5000,\n error: 6000,\n}\n\n/** Schedule a callback; returns its canceller. Tests inject a manual one. */\nexport type ToastScheduler = (fn: () => void, ms: number) => () => void\n\nconst browserScheduler: ToastScheduler = (fn, ms) => {\n const handle = window.setTimeout(fn, ms)\n return () => { window.clearTimeout(handle) }\n}\n\nexport class ToastStore {\n private items: ToastItem[] = []\n private nextId = 0\n private readonly listeners = new Set<() => void>()\n private readonly cancellers = new Map<number, () => void>()\n private readonly scheduler: ToastScheduler\n\n constructor(scheduler: ToastScheduler = browserScheduler) {\n this.scheduler = scheduler\n }\n\n getSnapshot(): readonly ToastItem[] { return this.items }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n private emit(): void {\n for (const listener of this.listeners) listener()\n }\n\n /** Enqueue one toast; its auto-exit is scheduled at its duration. */\n show(message: string, opts?: ShowToastOpts): number {\n const severity = opts?.severity ?? 'default'\n const id = ++this.nextId\n const item: ToastItem = {\n id,\n message,\n severity,\n duration: opts?.duration ?? SEVERITY_DURATION[severity],\n action: opts?.action,\n exiting: false,\n }\n this.items = [...this.items, item]\n this.cancellers.set(id, this.scheduler(() => this.startExit(id), item.duration))\n this.emit()\n return id\n }\n\n /** Begin the exit (dismiss click or the duration timer); cancels the timer. */\n startExit(id: number): void {\n const cancel = this.cancellers.get(id)\n if (cancel !== undefined) {\n cancel()\n this.cancellers.delete(id)\n }\n this.items = this.items.map(t => (t.id === id ? { ...t, exiting: true } : t))\n this.emit()\n }\n\n /** Actually remove — called by the view when the exit animation ends. */\n finish(id: number): void {\n this.items = this.items.filter(t => t.id !== id)\n this.cancellers.delete(id)\n this.emit()\n }\n\n /** Drop everything now (the panel unmounted — nothing outlives the panel). */\n clear(): void {\n for (const cancel of this.cancellers.values()) cancel()\n this.cancellers.clear()\n this.items = []\n this.emit()\n }\n}\n\n/** The panel-wide singleton stack; consumers call showStudyToast. */\nexport const toastStore = new ToastStore()\n\nexport function showStudyToast(message: string, opts?: ShowToastOpts): number {\n return toastStore.show(message, opts)\n}\n","/**\n * The interactive practice card (0.15.0 P1, upstream QuizArtifact port):\n * local judging, per-artifact progress in localStorage, the completion score,\n * and the post-quiz action set — the deterministic half of \"what next after\n * answering\" (never fewer than two exits; upstream post-quiz-actions.ts).\n * Pure logic + one React card; thresholds mirror state.ts (0.9 graduation /\n * 0.85 near — the client bundle cannot import the host-side state module).\n * @module dsh-plugin-lookatstudy/client/quizcard\n */\n\nimport { createElement, useEffect, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { IconBoltFill16, IconBookFill16, IconCrownFill16, IconRefreshOutline16, IconStarFill16 } from './icons.tsx'\nimport { tr } from './locale.ts'\n\n/** Mirror of state.ts MASTERED_THRESHOLD (graduation). */\nexport const MASTERED = 0.9\n/** Mirror of state.ts NEAR_MASTERED_THRESHOLD (proposal zone). */\nexport const NEAR_MASTERED = 0.85\n\nexport type PostQuizActionId = 'explain-wrong' | 'retry' | 'go-deeper' | 'mark-mastered' | 'next-topic'\n\nexport interface PostQuizAction {\n id: PostQuizActionId\n /** true = the action asks the tutor to test/graduate (mark-mastered only). */\n advancesMastery: boolean\n}\n\n/**\n * The exits after one finished card — never fewer than two (the anti-dead-end\n * promise). Wrong answers → patch the hole first; perfect + near-mastered →\n * offer the mastery exit; perfect + already graduated → move on.\n * Pure port of upstream getPostQuizActions.\n */\nexport function getPostQuizActions(score: { correct: number; total: number }, mastery: number | null): PostQuizAction[] {\n const { correct, total } = score\n const allCorrect = total > 0 && correct === total\n const hasWrong = total > 0 && correct < total\n const alreadyMastered = mastery !== null && mastery >= MASTERED\n const nearMastered = mastery !== null && mastery >= NEAR_MASTERED\n if (hasWrong) return [{ id: 'explain-wrong', advancesMastery: false }, { id: 'retry', advancesMastery: false }]\n if (allCorrect && alreadyMastered) return [{ id: 'next-topic', advancesMastery: false }, { id: 'go-deeper', advancesMastery: false }]\n if (allCorrect && nearMastered) return [{ id: 'mark-mastered', advancesMastery: true }, { id: 'go-deeper', advancesMastery: false }]\n if (allCorrect) return [{ id: 'go-deeper', advancesMastery: false }, { id: 'retry', advancesMastery: false }]\n return [{ id: 'explain-wrong', advancesMastery: false }, { id: 'retry', advancesMastery: false }]\n}\n\n/** The quiz payload shape (structural slice of the sanitized artifact data). */\nexport interface QuizData {\n readonly questions: ReadonlyArray<{ readonly prompt: string; readonly options: readonly string[]; readonly answer: number; readonly explanation: string }>\n readonly title?: string\n readonly warnings?: readonly string[]\n}\n\n/** Progress under localStorage: chosen option per question (-1 unanswered). */\nexport interface QuizProgress {\n readonly answers: number[]\n readonly done: boolean\n}\n\n/** localStorage key (upstream quizProgressKey semantics: per artifact). */\nexport function quizProgressKey(lessonId: string, artifactId: string): string {\n return `dsh-plugin-lookatstudy:quiz:${lessonId}:${artifactId}`\n}\n\nexport function loadQuizProgress(lessonId: string, artifactId: string, questionCount: number, storage: Pick<Storage, 'getItem'> = localStorage): QuizProgress {\n try {\n const raw = storage.getItem(quizProgressKey(lessonId, artifactId))\n if (raw === null) return { answers: Array<number>(questionCount).fill(-1), done: false }\n const parsed = JSON.parse(raw) as { answers?: unknown; done?: unknown }\n const answers = Array.isArray(parsed.answers)\n ? parsed.answers.map(a => (typeof a === 'number' ? a : -1))\n : []\n const padded = Array<number>(questionCount).fill(-1)\n for (let i = 0; i < Math.min(answers.length, questionCount); i += 1) padded[i] = answers[i]!\n return { answers: padded, done: parsed.done === true }\n } catch {\n return { answers: Array<number>(questionCount).fill(-1), done: false }\n }\n}\n\nexport function saveQuizProgress(lessonId: string, artifactId: string, progress: QuizProgress, storage: Pick<Storage, 'setItem'> = localStorage): void {\n try {\n storage.setItem(quizProgressKey(lessonId, artifactId), JSON.stringify(progress))\n } catch { /* storage unavailable — progress degrades to in-memory */ }\n}\n\n/** The score over recorded answers (pure). */\nexport function quizScore(data: QuizData, answers: readonly number[]): { correct: number; total: number } {\n let correct = 0\n let total = 0\n data.questions.forEach((q, i) => {\n const choice = answers[i]\n if (choice === undefined || choice < 0) return\n total += 1\n if (choice === q.answer) correct += 1\n })\n return { correct, total }\n}\n\nconst ACTION_ICONS: Record<PostQuizActionId, (props: { size?: number }) => ReactNode> = {\n 'explain-wrong': IconBookFill16,\n 'retry': IconRefreshOutline16,\n 'go-deeper': IconBoltFill16,\n 'mark-mastered': IconCrownFill16,\n 'next-topic': IconStarFill16,\n}\n\n/**\n * The card: one question at a time, click-to-answer (instant local judge +\n * explanation reveal — the cursor is explicit, so the reveal is never skipped),\n * progress persisted per artifact; the score screen is an explicit step, and\n * on first arrival its completion hook goes to the tutor exactly once.\n */\nexport function QuizCard({ lessonId, artifactId, data, masteryPct, send, onFinished }: {\n lessonId: string\n artifactId: string\n data: QuizData\n masteryPct: number | null\n send: (text: string) => void\n /** Fired once when the score screen lands; carries allCorrect. */\n onFinished?: (allCorrect: boolean) => void\n}): ReactNode {\n const questions = data.questions\n const [progress, setProgress] = useState<QuizProgress>(() => loadQuizProgress(lessonId, artifactId, questions.length))\n const [cursor, setCursor] = useState(0)\n const [showScore, setShowScore] = useState(false)\n const [hookSent, setHookSent] = useState(false)\n\n // (Re)load when the artifact identity changes (lesson switch).\n useEffect(() => {\n const next = loadQuizProgress(lessonId, artifactId, questions.length)\n setProgress(next)\n setShowScore(next.done)\n const firstUnanswered = next.answers.findIndex(a => a < 0)\n setCursor(firstUnanswered === -1 ? questions.length - 1 : firstUnanswered)\n setHookSent(false)\n }, [lessonId, artifactId, questions.length])\n\n const allAnswered = progress.answers.every(a => a >= 0)\n const score = quizScore(data, progress.answers)\n const mastery = masteryPct === null ? null : masteryPct / 100\n\n // The completion hook: once, when the score screen first shows with a full card.\n useEffect(() => {\n if (!showScore || !allAnswered || hookSent) return\n setHookSent(true)\n onFinished?.(score.correct === score.total)\n const next = { answers: [...progress.answers], done: true }\n setProgress(next)\n saveQuizProgress(lessonId, artifactId, next)\n send(tr('quiz.hook', { correct: score.correct, total: score.total }))\n }, [showScore, allAnswered, hookSent, lessonId, artifactId, progress.answers, score.correct, score.total, send])\n\n const answer = (choice: number): void => {\n const next = { answers: [...progress.answers], done: progress.done }\n next.answers[cursor] = choice\n setProgress(next)\n saveQuizProgress(lessonId, artifactId, next)\n }\n\n if (showScore && allAnswered) {\n const actions = getPostQuizActions(score, mastery)\n return createElement('div', { className: 'lks-qcard', 'data-lks-quiz': artifactId },\n createElement('div', { className: 'lks-qcard-head' },\n createElement(IconStarFill16, { size: 14 }),\n tr('quiz.score', { correct: score.correct, total: score.total })),\n createElement('div', { className: 'lks-qcard-review' },\n ...questions.map((q, i) => {\n const choice = progress.answers[i] ?? -1\n const right = choice === q.answer\n return createElement('div', { key: i, className: `lks-qcard-q review${right ? '' : ' wrong'}` },\n createElement('div', { className: 'lks-qcard-prompt' }, `${i + 1}. ${q.prompt}`),\n createElement('div', { className: 'lks-qcard-ans' },\n right ? '' : `${tr('quiz.youChose')}:${q.options[choice] ?? '—'} · `,\n `${tr('quiz.answer')}:${q.options[q.answer] ?? '—'}`),\n createElement('div', { className: 'lks-qcard-expl' }, q.explanation))\n })),\n createElement('div', { className: 'lks-qcard-actions' },\n ...actions.map(a => createElement('button', {\n key: a.id,\n className: 'lks-qcard-action',\n title: a.advancesMastery ? tr('quiz.action.markMastered.hint') : undefined,\n onClick: () => { send(tr(`quiz.msg.${a.id}`)) },\n }, createElement(ACTION_ICONS[a.id]!, { size: 13 }), tr(`quiz.action.${a.id}`)))),\n )\n }\n\n const q = questions[cursor]!\n const chosen = progress.answers[cursor] ?? -1\n const options = q.options.map((opt, i) => {\n const picked = chosen === i\n const isRight = i === q.answer\n const tone = chosen < 0 ? '' : picked ? (isRight ? ' right' : ' wrong') : isRight ? ' right dim' : ''\n return createElement('button', {\n key: i,\n className: `lks-qcard-opt${tone}`,\n disabled: chosen >= 0,\n onClick: () => { answer(i) },\n }, opt)\n })\n const explanation = chosen < 0 ? null : createElement('div', { className: 'lks-qcard-expl' },\n `${chosen === q.answer ? tr('quiz.right') : tr('quiz.wrong')} — ${q.explanation}`,\n createElement('div', { style: { marginTop: '6px' } },\n createElement('button', {\n className: 'lks-qcard-next',\n onClick: () => {\n if (cursor + 1 < questions.length) setCursor(cursor + 1)\n else setShowScore(true)\n },\n }, cursor + 1 < questions.length ? tr('quiz.next') : tr('quiz.finish'))))\n return createElement('div', { className: 'lks-qcard', 'data-lks-quiz': artifactId },\n createElement('div', { className: 'lks-qcard-head' },\n createElement(IconBookFill16, { size: 14 }),\n data.title ?? tr('quiz.card.title'),\n createElement('span', { className: 'lks-qcard-count' }, tr('quiz.progress', { cur: cursor + 1, total: questions.length }))),\n createElement('div', { className: 'lks-qcard-q' },\n createElement('div', { className: 'lks-qcard-prompt' }, q.prompt),\n createElement('div', { className: 'lks-qcard-opts' }, ...options),\n explanation,\n ),\n )\n}\n","/**\n * The artifact cards (0.15.0 P1b, upstream artifacts port): compare tables,\n * code walkthroughs, mermaid diagrams (with the expand modal), and the\n * opening guess — plus the seen-tracking that feeds the notebook badge and\n * the \"new artifact\" toast. Rendering is lazy where it is heavy (mermaid via\n * the enhance pipeline); everything else is plain dsw-token DOM.\n * @module dsh-plugin-lookatstudy/client/artifact-cards\n */\n\nimport { createElement, useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { tr } from './locale.ts'\n\n/** One artifact row from the state feed. */\nexport interface ArtifactRow {\n readonly id: string\n readonly artifactType: string\n readonly title: string\n readonly data: Record<string, unknown>\n}\n\n/** localStorage key for the artifact ids the learner has already seen. */\nexport function seenArtifactsKey(lessonId: string): string {\n return `dsh-plugin-lookatstudy:seen-artifacts:${lessonId}`\n}\n\n/** Which artifact ids are new (not yet seen); pure over the stored set. */\nexport function unseenArtifacts(lessonId: string, artifacts: readonly ArtifactRow[], storage: Pick<Storage, 'getItem'> = localStorage): string[] {\n let seen: Set<string>\n try {\n const raw = storage.getItem(seenArtifactsKey(lessonId))\n seen = new Set(Array.isArray(JSON.parse(raw ?? '[]')) ? JSON.parse(raw ?? '[]') as unknown[] : [])\n } catch {\n seen = new Set()\n }\n return artifacts.filter(a => !seen.has(a.id)).map(a => a.id)\n}\n\n/** Mark artifact ids seen (opening the notebook counts as seeing them). */\nexport function markArtifactsSeen(lessonId: string, ids: readonly string[], storage: Pick<Storage, 'setItem'> = localStorage): void {\n try {\n let seen: string[] = []\n try {\n const parsed = JSON.parse(storage.getItem(seenArtifactsKey(lessonId)) ?? '[]') as unknown\n if (Array.isArray(parsed)) seen = parsed.filter((x): x is string => typeof x === 'string')\n } catch { /* junk storage starts blank */ }\n storage.setItem(seenArtifactsKey(lessonId), JSON.stringify([...new Set([...seen, ...ids])]))\n } catch { /* storage unavailable */ }\n}\n\n/** The cards render mermaid through the shared CDN pipeline\n * (enhanceDiagrams consumes pre > code.lang-mermaid blocks). */\nasync function renderMermaidInto(el: HTMLElement, code: string, onError: () => void): Promise<void> {\n try {\n const { enhanceDiagrams } = await import('./enhance.ts')\n el.textContent = ''\n const pre = document.createElement('pre')\n const codeEl = document.createElement('code')\n codeEl.className = 'lang-mermaid'\n codeEl.textContent = code\n pre.appendChild(codeEl)\n el.appendChild(pre)\n const count = await enhanceDiagrams(el)\n if (count === 0) onError()\n } catch {\n onError()\n }\n}\n\n/** Compare table card (headers + rows). */\nexport function CompareTableCard({ artifact }: { artifact: ArtifactRow }): ReactNode {\n const headers = (artifact.data.headers ?? []) as string[]\n const rows = (artifact.data.rows ?? []) as string[][]\n return createElement('div', { className: 'lks-acard', 'data-lks-artifact': artifact.id },\n createElement('div', { className: 'lks-acard-head' }, artifact.title),\n createElement('table', { className: 'lks-acard-table' },\n createElement('thead', null, createElement('tr', null, ...headers.map((h, i) => createElement('th', { key: i }, h)))),\n createElement('tbody', null, ...rows.map((row, r) => createElement('tr', { key: r }, ...row.map((c, i) => createElement('td', { key: i }, c)))))))\n}\n\n/** Code walkthrough card: line-numbered code + per-segment notes. */\nexport function CodeWalkthroughCard({ artifact }: { artifact: ArtifactRow }): ReactNode {\n const language = typeof artifact.data.language === 'string' ? artifact.data.language : 'text'\n const code = typeof artifact.data.code === 'string' ? artifact.data.code : ''\n const annotations = (artifact.data.annotations ?? []) as Array<{ lineStart: number; lineEnd: number; note: string }>\n const lines = code.split('\\n')\n return createElement('div', { className: 'lks-acard', 'data-lks-artifact': artifact.id },\n createElement('div', { className: 'lks-acard-head' }, `${artifact.title} · ${language}`),\n createElement('pre', { className: 'lks-acard-code' },\n ...lines.map((line, i) => createElement('span', { key: i, className: 'lks-acard-line' },\n createElement('i', null, String(i + 1)), line, '\\n'))),\n createElement('div', { className: 'lks-acard-notes' },\n ...annotations.map((a, i) => createElement('div', { key: i, className: 'lks-acard-note' },\n createElement('b', null, `${tr('artifact.lines', { from: a.lineStart, to: a.lineEnd })}`), a.note))))\n}\n\n/** Mermaid diagram card with an expand modal (upstream DiagramViewerModal). */\nexport function DiagramCard({ artifact }: { artifact: ArtifactRow }): ReactNode {\n const mermaid = typeof artifact.data.mermaid === 'string' ? artifact.data.mermaid : ''\n const bodyRef = useRef<HTMLDivElement | null>(null)\n const [failed, setFailed] = useState(false)\n const [expanded, setExpanded] = useState(false)\n useEffect(() => {\n if (bodyRef.current === null) return\n void renderMermaidInto(bodyRef.current, mermaid, () => { setFailed(true) })\n }, [mermaid])\n const body = failed\n ? createElement('pre', { className: 'lks-acard-code' }, mermaid)\n : createElement('div', { className: 'lks-acard-diagram', ref: bodyRef })\n return createElement('div', { className: 'lks-acard', 'data-lks-artifact': artifact.id },\n createElement('div', { className: 'lks-acard-head' }, artifact.title,\n createElement('button', { className: 'lks-acard-expand', title: tr('artifact.expand'), onClick: () => { setExpanded(true) } }, '⤢')),\n body,\n expanded\n ? createElement('div', {\n className: 'lks-acard-modal',\n onClick: () => { setExpanded(false) },\n },\n createElement('div', { className: 'lks-acard-modal-body', onClick: (e: { stopPropagation: () => void }) => { e.stopPropagation() } },\n createElement('div', { className: 'lks-acard-modal-title' }, artifact.title,\n createElement('button', { className: 'lks-acard-expand', onClick: () => { setExpanded(false) } }, '×')),\n failed ? createElement('pre', { className: 'lks-acard-code' }, mermaid) : createElement('div', {\n ref: (el: HTMLDivElement | null) => {\n if (el !== null) void renderMermaidInto(el, mermaid, () => { setFailed(true) })\n },\n })))\n : null)\n}\n\n/** The opening guess: two big options, click = pick (sent to the tutor, who\n * reveals next turn); unscored — no right/wrong language anywhere. */\nexport function GuessCard({ artifact, send }: { artifact: ArtifactRow; send: (text: string) => void }): ReactNode {\n const prompt = typeof artifact.data.prompt === 'string' ? artifact.data.prompt : ''\n const options = (artifact.data.options ?? []) as Array<{ id: string; label: string }>\n const [picked, setPicked] = useState<string | null>(null)\n useEffect(() => { setPicked(null) }, [artifact.id])\n return createElement('div', { className: 'lks-acard lks-acard-guess', 'data-lks-artifact': artifact.id },\n createElement('div', { className: 'lks-acard-head' }, tr('artifact.guess.title')),\n createElement('div', { className: 'lks-acard-guess-prompt' }, prompt),\n createElement('div', { className: 'lks-acard-guess-opts' },\n ...options.map(opt => createElement('button', {\n key: opt.id,\n className: `lks-acard-guess-opt${picked === opt.id ? ' picked' : ''}`,\n disabled: picked !== null,\n onClick: () => {\n setPicked(opt.id)\n send(tr('artifact.guess.pick', { label: opt.label }))\n },\n }, opt.label))),\n picked !== null ? createElement('div', { className: 'lks-acard-guess-wait' }, tr('artifact.guess.wait')) : null)\n}\n\n/** Dispatch one artifact row to its card. */\nexport function ArtifactCard({ artifact, send }: { artifact: ArtifactRow; send: (text: string) => void }): ReactNode {\n switch (artifact.artifactType) {\n case 'compare_table': return createElement(CompareTableCard, { artifact })\n case 'code_walkthrough': return createElement(CodeWalkthroughCard, { artifact })\n case 'diagram': return createElement(DiagramCard, { artifact })\n case 'guess': return createElement(GuessCard, { artifact, send })\n default: return null\n }\n}\n","/**\n * The companion creature — the upstream 伴学生物's minimal honest DOM port\n * (0.15.0 P7): a small being in the panel's bottom-right corner with five\n * forms, a pure mood machine (idle / talking / celebrating / encouraging),\n * and the a11y iron rule carried over — reduced-motion renders it static.\n * Upstream's Electron-native life (mic envelopes, typing squeeze, drag-throw)\n * has no web-panel equivalent and stays unported by design.\n * @module dsh-plugin-lookatstudy/client/companion\n */\n\nimport { createElement, useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { tr } from './locale.ts'\n\nexport type CompanionFormId = 'ember' | 'frost' | 'moss' | 'star' | 'ink'\nexport type CompanionMood = 'idle' | 'talking' | 'celebrating' | 'encouraging'\n\nexport const COMPANION_FORMS: ReadonlyArray<{ id: CompanionFormId; labelKey: string; body: string; accent: string }> = [\n { id: 'ember', labelKey: 'companion.form.ember', body: '#f59e0b', accent: '#dc2626' },\n { id: 'frost', labelKey: 'companion.form.frost', body: '#7dd3fc', accent: '#0284c7' },\n { id: 'moss', labelKey: 'companion.form.moss', body: '#86efac', accent: '#16a34a' },\n { id: 'star', labelKey: 'companion.form.star', body: '#c4b5fd', accent: '#7c3aed' },\n { id: 'ink', labelKey: 'companion.form.ink', body: '#cbd5e1', accent: '#334155' },\n]\n\n/** Normalize a stored form id; junk falls back to 小焰 (upstream's default). */\nexport function normalizeCompanionForm(stored: string | null): CompanionFormId {\n return COMPANION_FORMS.some(f => f.id === stored) ? stored as CompanionFormId : 'ember'\n}\n\nexport function storedCompanionForm(): CompanionFormId {\n try {\n return normalizeCompanionForm(localStorage.getItem('dsh-plugin-lookatstudy:companion-form'))\n } catch {\n return 'ember'\n }\n}\n\nexport function storeCompanionForm(id: CompanionFormId): void {\n try {\n localStorage.setItem('dsh-plugin-lookatstudy:companion-form', id)\n } catch { /* storage unavailable — the default renders */ }\n}\n\n/**\n * The mood machine — pure. `celebrate` and `encourage` are momentary (the\n * view decays them back through `decay`); `talking` latches while read-aloud\n * runs; poke is a self-echoing celebration.\n */\nexport function nextCompanionMood(current: CompanionMood, event: 'talk-start' | 'talk-end' | 'celebrate' | 'encourage' | 'decay' | 'poke'): CompanionMood {\n switch (event) {\n case 'talk-start': return 'talking'\n case 'talk-end': return current === 'talking' ? 'idle' : current\n case 'celebrate':\n case 'poke': return 'celebrating'\n case 'encourage': return 'encouraging'\n case 'decay': return current === 'celebrating' || current === 'encouraging' ? 'idle' : current\n default: return current\n }\n}\n\n/** One creature face — the shared skin every form wears. */\nfunction CompanionFace({ form, mood }: { form: CompanionFormId; mood: CompanionMood }): ReactNode {\n const skin = COMPANION_FORMS.find(f => f.id === form) ?? COMPANION_FORMS[0]!\n // eyes: happy arcs when celebrating; plain dots otherwise\n const eye = mood === 'celebrating'\n ? 'M -3.4 0 q 1.7 -2.6 3.4 0'\n : 'M -3.4 -1.4 a 1.4 1.4 0 1 0 0.01 0'\n const mouth = mood === 'celebrating'\n ? 'M 20 21 q 4 4 8 0'\n : mood === 'encouraging'\n ? 'M 21 22 q 3 -2.5 6 0'\n : 'M 21 21 q 3 2.4 6 0'\n return createElement('svg', { width: 64, height: 64, viewBox: '0 0 64 64', 'aria-hidden': 'true' },\n createElement('g', null,\n createElement('circle', { cx: 32, cy: 34, r: 20, fill: skin.body, stroke: skin.accent, strokeWidth: 2 }),\n form === 'ember'\n ? createElement('path', { d: 'M32 4 q6 8 3 12 q8 -2 9 6 q4 -1 4 4 l-2 4 H18 l-2 -4 q0 -5 4 -4 q1 -8 9 -6 q-3 -4 3 -12z', fill: skin.accent, opacity: 0.85 })\n : form === 'star'\n ? createElement('path', { d: 'M32 2 l4 8 9 1 -6.5 6 1.5 9 -8.5 -4.5 -8 4.5 1.5 -9 -6.5 -6 9 -1z', fill: skin.accent, opacity: 0.85 })\n : form === 'moss'\n ? createElement('path', { d: 'M32 2 q-2 6 2 10 q-8 0 -8 6 H40 q0 -6 -8 -6 q4 -4 0 -10z', fill: skin.accent, opacity: 0.85 })\n : form === 'ink'\n ? createElement('path', { d: 'M32 2 q8 12 8 18 a8 8 0 1 1 -16 0 q0 -6 8 -18z', fill: skin.accent, opacity: 0.55 })\n : createElement('path', { d: 'M18 12 l3 5 5 -1 -1 5 5 3 -5 3 1 5 -5 -1 -3 5 -3 -5 -5 1 1 -5 -5 -3 5 -3 -1 -5 5 1z', fill: skin.accent, opacity: 0.7 }),\n createElement('path', { d: eye, transform: 'translate(25 30)', stroke: skin.accent, strokeWidth: 1.6, fill: mood === 'celebrating' ? 'none' : skin.accent, strokeLinecap: 'round' }),\n createElement('path', { d: eye, transform: 'translate(14 30)', stroke: skin.accent, strokeWidth: 1.6, fill: mood === 'celebrating' ? 'none' : skin.accent, strokeLinecap: 'round' }),\n createElement('path', { d: mouth, stroke: skin.accent, strokeWidth: 1.8, fill: 'none', strokeLinecap: 'round' })))\n}\n\n/** The creature: fixed bottom-right inside the panel, mood-animated, pokeable. */\nexport function Companion({ mood, onPoke }: { mood: CompanionMood; onPoke?: () => void }): ReactNode {\n const form = storedCompanionForm()\n return createElement('div', {\n className: `lks-companion mood-${mood}`,\n 'data-lks-companion': form,\n title: tr('companion.poke'),\n role: 'button',\n tabIndex: 0,\n 'aria-label': tr('companion.poke'),\n onClick: () => { onPoke?.() },\n onKeyDown: (e: { key: string; preventDefault: () => void }) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onPoke?.()\n }\n },\n }, createElement(CompanionFace, { form, mood }))\n}\n\n/** The hook driving one creature instance: event → mood, momentary decay. */\nexport function useCompanionMood(): { mood: CompanionMood; emit: (event: Parameters<typeof nextCompanionMood>[1]) => void } {\n const [mood, setMood] = useState<CompanionMood>('idle')\n const moodRef = useRef(mood)\n moodRef.current = mood\n const emit = (event: Parameters<typeof nextCompanionMood>[1]): void => {\n const next = nextCompanionMood(moodRef.current, event)\n if (next !== moodRef.current) {\n moodRef.current = next\n setMood(next)\n }\n }\n // momentary moods decay after 2.4s (upstream celebration 落点 timing, compressed)\n useEffect(() => {\n if (mood !== 'celebrating' && mood !== 'encouraging') return\n const timer = setTimeout(() => {\n moodRef.current = nextCompanionMood(moodRef.current, 'decay')\n setMood(moodRef.current)\n }, 2400)\n return () => { clearTimeout(timer) }\n }, [mood])\n return { mood, emit }\n}\n","/**\n * 讲解区画线定位(0.15.0 P2, upstream highlightText.ts v0.3.3 方案移植):\n * 上游历经多轮后放弃绝对字符偏移(mark 存在/清除与 React 重渲会让两个\n * model 不一致导致错位),改用「选区文字 + 前后文指纹」在纯文本 model 上\n * 做文本搜索。这里同样:高亮来源 = 记录区笔记的 quote 本身 —— 删笔记即删\n * 高亮,无需独立锚点状态。纯函数部分(定位与分段规划)node:test 直测。\n * @module dsh-plugin-lookatstudy/client/highlights\n */\n\n/** One prose text node mapped into the model's coordinate space. */\nexport interface ModelNode<T = unknown> {\n readonly node: T\n readonly start: number\n readonly end: number\n}\n\nexport interface TextModel<T = unknown> {\n readonly text: string\n readonly nodes: readonly ModelNode<T>[]\n}\n\n/**\n * Locate a selection inside the model text: exact `selected` text search,\n * disambiguated by a fuzzy `surrounding` fingerprint (the same sentence\n * appearing twice resolves to the occurrence whose context matches).\n * @returns the global [start, end) in model.text, or null when absent.\n */\nexport function locateInModel(model: TextModel, selected: string, surrounding: string | undefined): { start: number; end: number } | null {\n const needle = selected.trim()\n if (needle.length < 2) return null\n const hits: number[] = []\n let from = 0\n for (;;) {\n const hit = model.text.indexOf(needle, from)\n if (hit === -1) break\n hits.push(hit)\n from = hit + 1\n }\n if (hits.length === 0) return null\n if (hits.length === 1 || surrounding === undefined || surrounding.trim() === '') {\n return { start: hits[0]!, end: hits[0]! + needle.length }\n }\n // fingerprint: surrounding = ~30 chars around the selection; the right\n // occurrence is the one maximizing overlap with that fingerprint.\n const finger = surrounding.replace(/\\s+/g, '')\n let best = hits[0]!\n let bestScore = -1\n for (const hit of hits) {\n const context = model.text.slice(Math.max(0, hit - 30), hit + needle.length + 30).replace(/\\s+/g, '')\n let score = 0\n for (let i = 0; i < finger.length; i += 4) {\n if (context.includes(finger.slice(i, i + 4))) score += 1\n }\n if (score > bestScore) {\n bestScore = score\n best = hit\n }\n }\n return { start: best, end: best + needle.length }\n}\n\n/** Plan the per-node segments covering global [start, end) — pure. */\nexport function planSegments(nodes: readonly ModelNode[], start: number, end: number): Array<{ index: number; localStart: number; localEnd: number }> {\n const segments: Array<{ index: number; localStart: number; localEnd: number }> = []\n nodes.forEach((entry, index) => {\n if (entry.end <= start || entry.start >= end) return\n segments.push({\n index,\n localStart: Math.max(0, start - entry.start),\n localEnd: Math.min(entry.end - entry.start, end - entry.start),\n })\n })\n return segments\n}\n\n/** Build the DOM text model for the prose container (the walker rules mirror\n * upstream: skip script/style and KaTeX glyph layers; skip existing marks so\n * save-time and apply-time models stay symmetric). */\nexport function getTextModel(container: HTMLElement, opts?: { includeMarks?: boolean }): TextModel<Text> {\n const nodes: ModelNode<Text>[] = []\n let text = ''\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, {\n acceptNode(node) {\n const parent = (node as Text).parentElement\n if (parent === null) return NodeFilter.FILTER_REJECT\n const tag = parent.tagName\n if (tag === 'SCRIPT' || tag === 'STYLE') return NodeFilter.FILTER_REJECT\n if (parent.closest('.katex-html')) return NodeFilter.FILTER_REJECT\n if (parent.closest('.katex-mathml') !== null && parent.closest('annotation') === null) return NodeFilter.FILTER_REJECT\n if (opts?.includeMarks !== true && parent.closest('mark.lks-hl') !== null) return NodeFilter.FILTER_REJECT\n return NodeFilter.FILTER_ACCEPT\n },\n })\n let n: Node | null\n while ((n = walker.nextNode()) !== null) {\n const t = n as Text\n const content = t.textContent ?? ''\n nodes.push({ node: t, start: text.length, end: text.length + content.length })\n text += content\n }\n return { text, nodes }\n}\n\n/** The mark class applied to persisted highlights. */\nexport const HIGHLIGHT_MARK = 'lks-hl'\n\n/**\n * Apply one highlight range to the live DOM: split the boundary text nodes\n * and wrap the covering segments in marks. Returns the created marks.\n */\nexport function wrapRange(model: TextModel<Text>, start: number, end: number): HTMLElement[] {\n const marks: HTMLElement[] = []\n for (const seg of planSegments(model.nodes, start, end)) {\n const entry = model.nodes[seg.index]!\n const node = entry.node\n if (node.parentElement === null) continue\n if (node.parentElement.closest(`mark.${HIGHLIGHT_MARK}`) !== null) continue\n const mid = seg.localStart > 0 ? node.splitText(seg.localStart) : node\n const localEnd = seg.localEnd - seg.localStart\n if (mid.textContent !== null && mid.textContent.length > localEnd) mid.splitText(localEnd)\n const mark = document.createElement('mark')\n mark.className = HIGHLIGHT_MARK\n mid.parentElement!.insertBefore(mark, mid)\n mark.appendChild(mid)\n marks.push(mark)\n }\n return marks\n}\n\n/**\n * Render the persisted highlights for one lesson: each record-zone note with a\n * quote anchors a mark. Idempotent per call — call on a freshly rendered\n * (pre-mark) container; the model skips existing marks so re-entry is safe.\n * @returns how many notes got at least one mark.\n */\nexport function applyHighlights(container: HTMLElement, quotes: readonly string[]): number {\n let applied = 0\n for (const quote of quotes) {\n const trimmed = quote.trim()\n if (trimmed.length < 2) continue\n const model = getTextModel(container)\n const located = locateInModel(model, trimmed, undefined)\n if (located === null) continue\n if (wrapRange(model, located.start, located.end).length > 0) applied += 1\n }\n return applied\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/** One row of the rail search results panel. */\nexport interface RailSearchRow {\n readonly lessonId: string\n readonly title: string\n readonly snippet: string\n readonly courseTitle: string\n readonly fromTitle: boolean\n}\n\n/**\n * Merge the rail's two search tracks into the results panel: title hits\n * (lessons whose name matches — they are also filtered into the tree) first,\n * then full-text hits not already covered, snippets carried for the body\n * matches. Pure; empty queries yield no panel.\n */\nexport function mergeRailSearch(\n query: string,\n lessons: ReadonlyArray<{ id: string; title: string; status: string; kind: string }>,\n textHits: ReadonlyArray<{ lessonId: string; lessonTitle: string; snippet: string; courseTitle: string }>,\n): RailSearchRow[] {\n const keys = query.trim().toLowerCase().split(/\\s+/).filter(Boolean)\n if (keys.length === 0) return []\n const rows: RailSearchRow[] = []\n const seen = new Set<string>()\n const matches = (title: string): boolean => keys.every(k => title.toLowerCase().includes(k))\n for (const lesson of lessons) {\n if (lesson.kind === 'exam' || lesson.status === 'locked' || !matches(lesson.title)) continue\n rows.push({ lessonId: lesson.id, title: lesson.title, snippet: '', courseTitle: '', fromTitle: true })\n seen.add(lesson.id)\n }\n for (const hit of textHits) {\n if (seen.has(hit.lessonId)) continue\n rows.push({ lessonId: hit.lessonId, title: hit.lessonTitle, snippet: hit.snippet, courseTitle: hit.courseTitle, fromTitle: false })\n seen.add(hit.lessonId)\n }\n return rows.slice(0, 12)\n}\n\n/**\n * A section's effective openness: the user's explicit toggle (persisted per\n * course) overrides the frontier-based default. Pure.\n */\nexport function effectiveOpen(\n sectionTitle: string,\n defaultOpen: boolean,\n overrides: Readonly<Record<string, boolean>>,\n): boolean {\n return overrides[sectionTitle] ?? defaultOpen\n}\n\n/**\n * The narrow-mode pane picker: only valid ids, remembering the last choice\n * (upstream's narrow switcher semantics). Pure.\n */\nexport function pickNarrowPane(stored: string | null): 'rail' | 'chat' | 'note' {\n return stored === 'rail' || stored === 'chat' || stored === 'note' ? stored : 'chat'\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 * VENDORED from LookatStudy v0.28.0 (src/renderer/lib/mapLayout.ts) — verbatim.\n * Provenance: https://github.com/Upston-Liu/LookatStudy, dark-first port round\n * 2026-09-06; upstream file is pure (zero imports) and test-frozen there\n * (verify-map-layout T11). Only the module docblock moves into this header.\n *\n * mapLayout —— 选关地图节点布局引擎(纯函数,易测易调)。\n *\n * v0.6:从\"三布局模式 + 手动切换器\"重做为单一\"漂浮气球\"布局。\n * 节点像飘在空中的气球,位置带轻微种子确定性抖动(用 section id 哈希),\n * 每次渲染位置完全一致,不会乱跳,但又不整齐规整。\n * 连接用下垂的贝塞尔绳子(模拟重力),不再是 S 形箭头。\n *\n * 坐标系:相对章节容器左上角(0,0),x 横向 y 纵向,单位 px。\n * 节点尺寸:球 56px(w-14 h-14),卡片宽 110px(含名字)。\n */\n\n/** 单个节点的布局结果(相对章节容器)。 */\nexport interface NodeLayout {\n /** 节点中心 x(px,相对章节容器左上) */\n x: number\n /** 节点中心 y(px,相对章节容器左上) */\n y: number\n /** 该节点在 lessons 数组里的索引 */\n index: number\n}\n\n/** 两节点间的连接段(供 SVG path 绘制)。 */\nexport interface PathSegment {\n /** 起点(上一节点中心) */\n from: { x: number; y: number }\n /** 终点(下一节点中心) */\n to: { x: number; y: number }\n /** 段索引(from 节点的 index) */\n index: number\n}\n\nexport interface SectionLayout {\n nodes: NodeLayout[]\n segments: PathSegment[]\n /** 该 section 容器所需高度(px)。贪心防重叠后按最后节点真实底边算,\n * 供 MapRail 精确设 minHeight / SVG height(不再用估算)。 */\n height: number\n}\n\n/** 容器宽度(章节内可用宽度,扣除 padding)。MapRail 章节容器 px-2。 */\nconst CONTAINER_WIDTH = 268 // 300px rail - padding\nconst NODE_RADIUS = 28 // w-14 h-14 = 56px,半径 28\nconst NODE_NAME_H = 20 // 球下方的 lesson 名字行高度\n/** 节点可视盒高(球 56 + 名字 20)。**必须与 MapRail 渲染的 NODE_H 一致** —— 重叠判定基于此。 */\nexport const NODE_BOX_H = NODE_RADIUS * 2 + NODE_NAME_H // 76\n/** 相邻节点中心最小纵向间距 = 盒高 + 呼吸。贪心推下保证 ≥ 此值 → 视觉永不重叠。 */\nexport const MIN_GAP_Y = NODE_BOX_H + 8 // 84\nconst NODE_SPACING_Y = 104 // 基线间距(略 > MIN_GAP_Y,多数情况无需推下,保持自然飘感)\nconst BALLOON_MARGIN = 18 // 气球左右留白(减小让抖动幅度更明显,不再呆板直线)\n/** 首节点中心下限(防顶撞上方章节路牌)。MapRail 渲染偏移 +12 + NODE_H/2=38 → wrapper 顶 = y-26 ≥ 8。 */\nconst TOP_PAD = NODE_RADIUS + 6 // 34\n/** y 轴抖动幅度(px)。贪心兜底后,jitter 不再是重叠源,仅负责高低参差。 */\nconst Y_JITTER = 26\n/** 绳子下垂量(px,模拟重力)。让贝塞尔像被拽下来的绳子,而非 S 形箭头。 */\nconst ROPE_SAG = 22\n\n/**\n * 确定性字符串哈希(FNV-1a 变体,32-bit)。\n * 同输入永远同输出 → 同一节点 id 每次渲染算出的抖动相同 → 气球位置稳定不跳。\n * 用于气球横向抖动 + bob 相位 + 星点位置 + 天体色切。\n */\nexport function hashStr(s: string): number {\n let h = 0x811c9dc5 // FNV offset basis\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i)\n // 等价 h *= 16777619,用 Math.imul 避免 32-bit 溢出丢精度\n h = Math.imul(h, 0x01000193)\n }\n // 雪崩搅拌(avalanche finalizer):FNV-1a 对\"末位递增\"输入(seed:0, seed:1, seed:2…)\n // 输出近乎线性相关 → 相邻气球哈希接近 → 排成直线。加 xorshift + imul 搅拌彻底打散。\n h ^= h >>> 16\n h = Math.imul(h, 0x85ebca6b)\n h ^= h >>> 13\n h = Math.imul(h, 0xc2b2ae35)\n h ^= h >>> 16\n return h >>> 0\n}\n\n/**\n * 算某 section 的气球节点坐标 + 绳子连接路径。\n * 容器高度由 nodes 数量决定(调用方据此设容器 min-height)。\n *\n * 抖动算法:v0.6 同时在 x 和 y 两轴抖动 → 气球真正\"飘\"在空中,\n * 不再是呆板的直线/微弯列。x 大幅抖动(用满容器宽度),y 在基线上下 ±Y_JITTER。\n * 两个独立哈希(x 用 seed:i,y 用 seed:i:y)→ 抖动方向不相关,分布更自然。\n *\n * v0.8 防重叠:抖动后跑一趟**贪心向下推**(确定性,只增 y,保持单调),\n * 1) 首节点中心不低于 TOP_PAD —— 防顶撞上方章节路牌;\n * 2) 相邻节点中心纵向距 ≥ MIN_GAP_Y(= 盒高 + 8px 呼吸)—— 视觉盒永不重叠。\n * 任何 seed / 任何课数都保证不重叠(verify-map-layout T11 跨 240 种子穷举验证)。\n */\nexport function computeBalloonLayout(\n lessonCount: number,\n containerWidth: number = CONTAINER_WIDTH,\n seed: string = '',\n): SectionLayout {\n const nodes: NodeLayout[] = []\n const centerX = containerWidth / 2\n const maxDrift = containerWidth / 2 - BALLOON_MARGIN - NODE_RADIUS\n\n // 第一遍:基线 + 确定性抖动(x/y 双轴,两独立哈希 → 方向不相关,分布自然)\n for (let i = 0; i < lessonCount; i++) {\n const baseY = TOP_PAD + i * NODE_SPACING_Y\n // x 抖动:哈希归一化到 [-1,1],×maxDrift → 大幅左右飘\n const hx = hashStr(`${seed}:${i}`)\n const normX = (hx / 0xffffffff) * 2 - 1 // [-1, 1]\n const x = centerX + normX * maxDrift\n // y 抖动:另一独立哈希,±Y_JITTER 围绕基线 → 高低参差\n const hy = hashStr(`${seed}:${i}:y`)\n const normY = (hy / 0xffffffff) * 2 - 1 // [-1, 1]\n const y = baseY + normY * Y_JITTER\n nodes.push({ x, y, index: i })\n }\n\n // 第二遍:贪心防重叠(确定性,只向下推 → y 仍单调,与原 T4 不变量兼容)\n if (nodes.length > 0) {\n // 首节点下限:防顶撞章节路牌\n if (nodes[0]!.y < TOP_PAD) nodes[0]!.y = TOP_PAD\n // 相邻中心距 ≥ MIN_GAP_Y:用已定稿的前驱 y 推后继(级联正确)\n for (let i = 1; i < nodes.length; i++) {\n const minY = nodes[i - 1]!.y + MIN_GAP_Y\n if (nodes[i]!.y < minY) nodes[i]!.y = minY\n }\n }\n\n // 绳子段:相邻节点(用防重叠后的最终坐标)\n const segments: PathSegment[] = []\n for (let i = 0; i < nodes.length - 1; i++) {\n segments.push({ from: nodes[i]!, to: nodes[i + 1]!, index: i })\n }\n\n // 容器精确高度:最后节点底边 + 余量(供 MapRail 设 minHeight / SVG height)\n const height = nodes.length === 0\n ? 40\n : nodes[nodes.length - 1]!.y + NODE_BOX_H / 2 + 10\n\n return { nodes, segments, height }\n}\n\n/** 容器所需高度的**保守上界**估算。\n * 实际精确高度请用 `computeBalloonLayout(...).height`(贪心后按真实底边算)。\n * 本函数供拿不到 layout 的调用方做兜底估算;单调递增(verify T7)。 */\nexport function sectionHeight(lessonCount: number): number {\n if (lessonCount === 0) return 40\n return TOP_PAD + (lessonCount - 1) * MIN_GAP_Y + NODE_BOX_H / 2 + 10\n}\n\n/**\n * 两节点间的绳子贝塞尔(下垂)。\n * 控制点:y 中点 + ROPE_SAG(向下垂,模拟重力);x 用各自端点 x。\n * 比原 S 形软,像被拽下来的绳子。\n */\nexport function balloonSegmentToPath(seg: PathSegment): string {\n const { from, to } = seg\n const controlY = (from.y + to.y) / 2 + ROPE_SAG\n return `M ${from.x} ${from.y} C ${from.x} ${controlY}, ${to.x} ${controlY}, ${to.x} ${to.y}`\n}\n","/**\n * MapRail balloon map (P9b) — the upstream v0.28 course map ported onto the\n * plugin's rail: deterministic balloon layout (vendored mapLayout engine),\n * sagging bezier ropes with path-draw on walked segments, state bubbles with\n * 3D sphere lighting, signpost section heads, and the sky backdrop. This is\n * upstream's own reduced-motion presentation (static layout + balloon-bob\n * CSS), which is exactly the deterministic half of its physics map.\n */\nimport { createElement, useEffect, useRef, useState, type CSSProperties, type ReactNode } from 'react'\nimport { computeBalloonLayout, balloonSegmentToPath, hashStr, NODE_BOX_H } from '../vendor/map-layout.ts'\nimport { IconBookFill16, IconCrownFill16, IconGoalOutline16, IconLockFill16, IconStarFill16 } from './icons.tsx'\nimport { tr } from './locale.ts'\n\n/** The rail lesson projection (a structural slice of StudyData's section lessons). */\nexport interface MapLesson {\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 due: boolean\n readonly focus: boolean\n}\n\n/** Upstream bubbleClass port: the four study states + the three exam variants.\n * Status→class goes through a table (in_progress → in-progress, like upstream's switch). */\nconst STATUS_CLASS: Record<string, string> = { locked: 'locked', available: 'available', in_progress: 'in-progress', mastered: 'mastered' }\n\nexport function bubbleClasses(lesson: Pick<MapLesson, 'kind' | 'status'>, examAllowed: boolean): string {\n if (lesson.kind === 'exam') {\n if (!examAllowed) return 'lks-bubble lks-exam-locked'\n return lesson.status === 'mastered' ? 'lks-bubble lks-exam-passed' : 'lks-bubble lks-exam'\n }\n return `lks-bubble lks-bubble-${STATUS_CLASS[lesson.status] ?? 'locked'}`\n}\n\n/** A rope segment is \"walked\" when its FROM node has been opened\n * (upstream ropeStyleIsPassed: mastered | in_progress | available). */\nexport function ropePassed(from: Pick<MapLesson, 'status'>): boolean {\n return from.status === 'mastered' || from.status === 'in_progress' || from.status === 'available'\n}\n\n/** Deterministic sky preset per course (upstream pickPreset's CSS-essence port). */\nexport function pickSky(courseId: string): 'day' | 'dusk' | 'night' {\n return (['day', 'dusk', 'night'] as const)[hashStr(courseId) % 3]\n}\n\n/** One section: signpost head (still the plugin's collapse toggle) + the balloon field. */\nexport function MapSectionView({ section, examAllowed, open, onToggle, onJump }: {\n section: { title: string; index: number; lessons: readonly MapLesson[] }\n examAllowed: boolean\n open: boolean\n onToggle: () => void\n onJump: (lessonId: string) => void\n}): ReactNode {\n const pathRef = useRef<HTMLDivElement | null>(null)\n const [containerW, setContainerW] = useState(268)\n useEffect(() => {\n if (pathRef.current === null) return\n const measure = (): void => {\n const w = pathRef.current?.clientWidth ?? 268\n setContainerW(w > 0 ? w : 268)\n }\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(pathRef.current)\n return () => { ro.disconnect() }\n }, [open])\n\n const lessons = section.lessons\n const layout = computeBalloonLayout(lessons.length, containerW, section.title)\n\n return createElement('section', { className: 'lks-mapsec' },\n createElement('button', {\n type: 'button',\n className: 'lks-signpost',\n 'aria-expanded': String(open),\n title: open ? tr('rail.section.collapse') : tr('rail.section.expand', { count: section.lessons.length }),\n onClick: onToggle,\n },\n createElement('span', { className: 'lks-signpost-num' }, String(section.index + 1)),\n createElement('span', { className: 'lks-signpost-title' }, section.title),\n createElement('span', { className: 'lks-signpost-caret' }, open ? '▾' : '▸')),\n open && lessons.length > 0\n ? createElement('div', { ref: pathRef, className: 'lks-mapfield', style: { minHeight: `${String(layout.height)}px` } },\n createElement('svg', {\n className: 'lks-mapropes',\n 'aria-hidden': 'true',\n style: { height: `${String(layout.height)}px` },\n },\n ...layout.segments.map(seg => {\n const passed = ropePassed(lessons[seg.index] ?? { status: 'locked' })\n return createElement('path', {\n key: seg.index,\n d: balloonSegmentToPath(seg),\n stroke: passed ? 'var(--brand)' : 'var(--ink-faint)',\n 'stroke-width': passed ? '4' : '2.5',\n 'stroke-opacity': passed ? '0.6' : '0.5',\n fill: 'none',\n 'stroke-linecap': 'round',\n 'stroke-dasharray': passed ? 'none' : '3 7',\n style: passed ? ({ pathLength: 1, animation: 'lks-path-draw 600ms var(--ease-out-expo)' } as CSSProperties) : undefined,\n })\n })),\n ...lessons.map((lesson, i) => {\n const node = layout.nodes[i]\n if (node === undefined) return null\n const h = hashStr(lesson.id)\n const locked = lesson.status === 'locked' || (lesson.kind === 'exam' && !examAllowed)\n const showRing = lesson.kind !== 'exam' && (lesson.status === 'in_progress' || lesson.status === 'mastered')\n const ringPct = lesson.status === 'mastered' ? 100 : lesson.status === 'in_progress' ? (lesson.masteryPct ?? 0) : 0\n return createElement('div', {\n key: lesson.id,\n className: 'lks-mapnode',\n style: {\n left: `${String(node.x - 55)}px`,\n top: `${String(node.y - NODE_BOX_H / 2 + 10)}px`,\n '--bob-delay': `${String((h % 40) / 10)}s`,\n '--bob-duration': `${String(5 + (h % 30) / 10)}s`,\n } as CSSProperties,\n },\n createElement('button', {\n type: 'button',\n className: `${bubbleClasses(lesson, examAllowed)}${lesson.focus ? ' selected' : ''}`,\n 'aria-disabled': locked || undefined,\n title: `${lesson.title} — ${lesson.kind === 'exam' && !examAllowed ? tr('map.exam.locked') : locked ? tr('map.node.locked') : lesson.due ? tr('map.node.due') : lesson.title}`,\n onClick: () => { if (!locked) onJump(lesson.id) },\n },\n showRing\n ? createElement('svg', { className: 'lks-bubble-ring', viewBox: '0 0 56 56', 'aria-hidden': 'true' },\n createElement('circle', { cx: 28, cy: 28, r: 25, fill: 'none', stroke: 'rgb(255 255 255 / 0.2)', 'stroke-width': 2.5 }),\n createElement('circle', {\n cx: 28, cy: 28, r: 25, fill: 'none',\n stroke: lesson.status === 'mastered' ? '#ffc800' : 'white',\n 'stroke-width': 2.5, 'stroke-linecap': 'round',\n 'stroke-dasharray': `${String((ringPct / 100) * 157)} 157`,\n }))\n : null,\n lesson.kind === 'exam'\n ? (locked\n ? createElement(IconLockFill16, { size: 22, className: 'lks-bubble-glyph dim' })\n : createElement(IconGoalOutline16, { size: 24, className: 'lks-bubble-glyph' }))\n : lesson.status === 'locked'\n ? createElement(IconLockFill16, { size: 20, className: 'lks-bubble-glyph dim' })\n : lesson.status === 'mastered'\n ? createElement(IconCrownFill16, { size: 24, className: 'lks-bubble-glyph' })\n : lesson.status === 'in_progress'\n ? createElement(IconBookFill16, { size: 24, className: 'lks-bubble-glyph' })\n : createElement(IconStarFill16, { size: 24, className: 'lks-bubble-glyph' }),\n lesson.due && !locked && lesson.kind !== 'exam'\n ? createElement('span', { className: 'lks-bubble-due', 'aria-label': tr('map.node.due') }, '!')\n : null),\n lesson.focus\n ? createElement('div', { className: 'lks-bubble-name' }, lesson.title)\n : null)\n }))\n : null,\n )\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, IconWarningOutline16,\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 { toastStore, type ToastItem, type ToastSeverity } from './toast.ts'\nimport { QuizCard, type QuizData } from './quizcard.tsx'\nimport { ArtifactCard, markArtifactsSeen, unseenArtifacts, type ArtifactRow } from './artifact-cards.tsx'\nimport { showStudyToast } from './toast.ts'\nimport { Companion, useCompanionMood } from './companion.tsx'\nimport { applyHighlights, getTextModel, locateInModel } from './highlights.ts'\nimport { statusTitle, quizOptions, sectionDefaultOpen, mergeRailSearch, effectiveOpen, pickNarrowPane } from './views.tsx'\nimport { MapSectionView, pickSky } from './maprail.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\n/** Toast severity glyph (P6): the state color rides the icon, text stays ink. */\nfunction toastIcon(severity: ToastSeverity): ReactNode {\n if (severity === 'success') return createElement(IconStarFill16, { size: 14, className: 'lks-toast-glyph ok' })\n if (severity === 'warning') return createElement(IconWarningOutline16, { size: 14, className: 'lks-toast-glyph warn' })\n if (severity === 'error') return createElement(IconWarningOutline16, { size: 14, className: 'lks-toast-glyph err' })\n if (severity === 'info') return createElement(IconGlobeOutline14, { size: 13, className: 'lks-toast-glyph info' })\n return null\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 [narrowPane, setNarrowPane] = useState<'rail' | 'chat' | 'note'>(pickNarrowPane(null))\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 review nudge (upstream review.nudge): due reviews pull the learner\n // back — one toast per panel open, never repeated while it stays open.\n const nudged = useRef(false)\n useEffect(() => {\n if (nudged.current || (data?.dueCount ?? 0) === 0) return\n nudged.current = true\n showStudyToast(tr('review.nudge', { n: data!.dueCount }), { severity: 'warning', action: { label: tr('review.nudge.go'), onClick: () => {\n const first = data?.due[0]\n if (first !== undefined) void setFocus(first.lessonId).catch(() => { /* the poll will resync */ })\n } } })\n }, [data?.dueCount, data?.due, setFocus])\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 companion = useCompanionMood()\n const setCompanionEvent = companion.emit\n const progress = data?.progress ?? null\n // The upstream v0.6 ladder: rail full-height on surface-rail; the right half\n // = floating app-header + the chat/notebook row (chat surface-1, notebook\n // surface-2) — depth by color step, no borders.\n const body: ReactNode = createElement('div', { className: 'lks14-body', 'data-pane': narrowPane },\n createElement(CourseRail, { data, activate, setFocus, searchLessons, deleteCourse, send }),\n createElement('div', { className: 'lks14-righthalf' },\n createElement('div', { className: 'lks14-appheader' },\n createElement('span', { className: 'lks-hdr-title' }, lesson?.courseTitle ?? tr('tab.label')),\n createElement('span', { className: 'lks-hdr-xp', title: tr('header.xp', { xp: progress?.totalXp ?? 0 }) },\n createElement('span', { className: 'lks-hdr-stat' }, '\\u26a1'),\n createElement('span', { className: 'lks-hdr-xpbar' }, createElement('i', { style: { width: `${Math.min(100, progress?.levelPct ?? 0)}%` } }))),\n createElement('span', { className: 'lks-hdr-stat lks-hdr-streak', title: tr('header.streak') }, '\\ud83d\\udd25', String(progress?.streak ?? 0)),\n createElement('span', { className: 'lks-hdr-stat', title: tr('header.level') }, `Lv${String(progress?.level ?? 1)}`),\n ),\n createElement('div', { className: 'lks14-row' },\n createElement(ChatPane, { data, lesson, rows, feedAttached, bound: boundId !== null, busy, sendError, draft, setDraft, send, setMode, narrowPane, companionEvent: setCompanionEvent }),\n createElement(NotebookPane, { data, deleteNote, send, companionEvent: setCompanionEvent }),\n ),\n ),\n )\n return createElement('div', { className: 'lks14 lks-ui', 'data-lks-panel': '' },\n createElement(Companion, { mood: companion.mood, onPoke: () => {\n setCompanionEvent('poke')\n showStudyToast(tr('companion.poked'), { severity: 'success' })\n } }),\n createElement('div', { className: 'lks14-switch' },\n ...(['rail', 'chat', 'note'] as const).map(pane => createElement('button', {\n key: pane,\n className: `lks14-switch-btn${narrowPane === pane ? ' on' : ''}`,\n 'aria-pressed': String(narrowPane === pane),\n onClick: () => { setNarrowPane(pane) },\n }, tr(`pane.${pane}`))),\n ),\n body, createElement(StudyToastStack))\n}\n\n/** P6: the panel's toast stack (upstream Toast port) — severity capsules,\n * top-center, auto-dismiss with an exit-animation handshake; the store clears\n * on unmount so no toast outlives the panel. */\nfunction StudyToastStack(): ReactNode {\n const [items, setItems] = useState<readonly ToastItem[]>([])\n useEffect(() => {\n const update = (): void => { setItems([...toastStore.getSnapshot()]) }\n update()\n const unsubscribe = toastStore.subscribe(update)\n return () => {\n unsubscribe()\n toastStore.clear()\n }\n }, [])\n return createElement('div', { className: 'lks-toasts', role: 'region', 'aria-live': 'polite', 'aria-label': tr('toast.region') },\n ...items.map(t => createElement('div', {\n key: t.id,\n className: `lks-toast${t.exiting ? ' exiting' : ''}`,\n 'data-severity': t.severity,\n onAnimationEnd: t.exiting ? () => { toastStore.finish(t.id) } : undefined,\n },\n toastIcon(t.severity),\n createElement('span', { className: 'lks-toast-text' }, t.message),\n t.action === undefined ? null : createElement('button', {\n className: 'lks-toast-action',\n onClick: () => {\n t.action?.onClick?.()\n toastStore.startExit(t.id)\n },\n }, t.action.label),\n createElement('button', {\n className: 'lks-toast-close',\n 'aria-label': tr('toast.close'),\n onClick: () => { toastStore.startExit(t.id) },\n }, '×'),\n )))\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 [searchRows, setSearchRows] = useState<ReturnType<typeof mergeRailSearch>>([])\n const [sectionOverrides, setSectionOverrides] = useState<Record<string, boolean>>({})\n const toggleSection = (title: string, next: boolean): void => {\n setSectionOverrides(cur => ({ ...cur, [title]: next }))\n }\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 // Live search: title hits from the loaded tree + debounced full-text hits\n // merge into the results panel (upstream CourseSearchPanel semantics).\n useEffect(() => {\n if (query.trim().length < 2 || data === null) {\n setSearchRows([])\n return\n }\n let cancelled = false\n const timer = setTimeout(() => {\n void searchLessons(query).then(textHits => {\n if (cancelled) return\n const tree = data.courses.find(c => c.courseId === (data.courses.some(x => x.courseId === selectedCourse) ? selectedCourse : data.courses[0]?.courseId))\n const lessons = (tree?.sections ?? []).flatMap(s => s.lessons)\n setSearchRows(mergeRailSearch(query, lessons.map(l => ({ id: l.id, title: l.title, status: l.status, kind: l.kind })), textHits))\n }).catch(() => { if (!cancelled) setSearchRows([]) })\n }, 250)\n return () => { cancelled = true; clearTimeout(timer) }\n }, [query, selectedCourse, data, searchLessons])\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', { className: `lks-map lks-sky-${pickSky(courseId)}` },\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 === 'Escape') { setQuery(''); setSearchRows([]) }\n if (e.key !== 'Enter' || query.trim() === '') return\n const first = searchRows[0]\n if (first !== undefined) {\n setQuery('')\n setSearchRows([])\n void setFocus(first.lessonId).catch(reportError)\n }\n },\n }),\n searchRows.length > 0\n ? createElement('div', { className: 'lks14-searchpanel' },\n ...searchRows.map(row => createElement('button', {\n key: row.lessonId,\n className: 'lks14-searchrow',\n title: tr('rail.search.jump'),\n onClick: () => {\n setQuery('')\n setSearchRows([])\n void setFocus(row.lessonId).catch(reportError)\n },\n },\n createElement('span', { className: 'lks14-searchrow-title' }, row.title),\n row.courseTitle !== '' ? createElement('span', { className: 'lks14-searchrow-course' }, row.courseTitle) : null,\n row.snippet !== '' ? createElement('span', { className: 'lks14-searchrow-snip' }, row.snippet) : null)))\n : null,\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('button', {\n key: d.lessonId,\n className: 'lks14-dueitem',\n title: tr('review.jump'),\n onClick: () => { void setFocus(d.lessonId).catch(reportError) },\n },\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() !== '' || effectiveOpen(section.title, sectionDefaultOpen(section), sectionOverrides)\n return [createElement(MapSectionView, {\n key: section.title,\n section: { title: section.title, index: section.index, lessons },\n examAllowed,\n open,\n onToggle: () => { toggleSection(section.title, !open) },\n // Upstream alignment: tapping a bubble only FOCUSES the lesson —\n // the state-side attempt runs host-side, zero LLM traffic.\n onJump: (id: string) => { void setFocus(id).catch(reportError) },\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, narrowPane, companionEvent }: {\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 narrowPane: 'rail' | 'chat' | 'note'\n companionEvent: (event: 'talk-start' | 'talk-end' | 'celebrate' | 'encourage' | 'decay' | 'poke') => 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 const proposal = data?.pendingProposals[0] ?? null\n return createElement('div', { className: 'lks14-col lks14-chat' },\n proposal !== null\n ? createElement('div', { className: 'lks-propbanner' },\n createElement(IconCrownFill16, { size: 14 }),\n createElement('span', { className: 'lks-propbanner-why' },\n tr('proposal.banner', { lesson: proposal.lessonTitle }), ' — ', proposal.rationale),\n createElement('button', {\n className: 'lks-btn primary',\n style: { padding: '4px 12px', fontSize: '12.5px' },\n onClick: () => { send(tr('proposal.accept.msg', { lesson: proposal.lessonTitle })) },\n }, tr('proposal.accept')),\n createElement('button', {\n className: 'lks-btn ghost',\n style: { padding: '4px 12px', fontSize: '12.5px' },\n onClick: () => { send(tr('proposal.decline.msg', { lesson: proposal.lessonTitle })) },\n }, tr('proposal.decline')))\n : null,\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 rows.length > 0 && rows[rows.length - 1]!.role === 'user'\n ? createElement('div', { className: 'lks14-thinking' }, createElement('i', null), createElement('i', null), createElement('i', null))\n : null,\n ),\n ...(lesson?.artifacts ?? [])\n .filter(a => a.artifactType === 'quiz')\n .map(a => createElement(QuizCard, {\n key: a.id,\n lessonId: lesson.lessonId,\n artifactId: a.id,\n data: a.data as unknown as QuizData,\n masteryPct: lesson.masteryPct,\n send,\n onFinished: (allCorrect: boolean) => { companionEvent(allCorrect ? 'celebrate' : 'encourage') },\n })),\n ...(lesson?.artifacts ?? [])\n .filter(a => a.artifactType !== 'quiz')\n .map(a => createElement(ArtifactCard, { key: a.id, artifact: a as ArtifactRow, send })),\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('div', { className: 'lks14-composer-card' },\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-send',\n 'aria-label': tr('composer.send'),\n disabled: busy || dormant || draft.trim() === '',\n onClick: () => { send(draft) },\n }, busy ? createElement(IconLoadingOutline16, { size: 16, className: 'lks-spin' }) : '↑'),\n ),\n sendError !== null || error !== null\n ? createElement('div', { className: 'lks-propcard-err' }, sendError ?? error)\n : null,\n ),\n )\n}\n\n/** 右栏:the notebook — 讲解/概念图/笔记 tabs (upstream NotebookPanel arrangement). */\nfunction NotebookPane({ data, deleteNote, send, companionEvent }: { data: StudyData; deleteNote: (lessonId: string, noteId: string) => Promise<void>; send: PanelSend; companionEvent: (event: 'talk-start' | 'talk-end' | 'celebrate' | 'encourage' | 'decay' | 'poke') => 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, addUserNote, recordReview } = 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 // Canvas semantics (upstream): artifacts sediment into the notebook — the\n // 笔记 tab wears a badge for unseen ones, a toast announces each arrival\n // once, and opening the tab marks them seen.\n const artifacts: ArtifactRow[] = (lesson?.artifacts ?? []) as ArtifactRow[]\n const [, setUnseenTick] = useState(0)\n const unseen = unseenArtifacts(lesson?.lessonId ?? '', artifacts)\n const lastAnnounced = useRef<ReadonlySet<string>>(new Set())\n useEffect(() => {\n const fresh = unseen.filter(id => !lastAnnounced.current.has(id))\n if (fresh.length === 0) return\n lastAnnounced.current = new Set([...lastAnnounced.current, ...fresh])\n showStudyToast(tr('artifact.sedimented'), { severity: 'info' })\n setUnseenTick(Date.now())\n }, [unseen.join(','), lesson?.lessonId]) // eslint-disable-line react-hooks/exhaustive-deps\n const heavy = artifacts.filter(a => a.artifactType === 'compare_table' || a.artifactType === 'diagram' || a.artifactType === 'code_walkthrough')\n const latestHeavy = heavy.length > 0 ? heavy[heavy.length - 1]! : null\n\n // ── 画线笔记 (P2, upstream selection flow) ─────────────────────────────\n // The popover appears on pointerup / settle (never mid-drag), hides 250ms\n // after the selection clears (a tap on the button clears first — an\n // immediate hide would swallow the click).\n const [quoteBtn, setQuoteBtn] = useState<{ x: number; y: number; text: string; surrounding: string } | null>(null)\n const recordQuotes = (lesson?.notes ?? []).filter(n => n.zone === 'record' && n.quote !== null && n.quote.trim().length >= 2).map(n => n.quote!)\n\n const evaluateSelection = (): void => {\n const prose = proseRef.current\n const selection = window.getSelection()\n if (prose === null || selection === null || selection.rangeCount === 0) return\n const text = selection.toString().trim()\n if (text.length < 2 || text.length > 600) return\n const range = selection.getRangeAt(0)\n if (prose.contains(range.commonAncestorContainer) !== true) return\n const model = getTextModel(prose)\n const rect = range.getBoundingClientRect()\n const box = prose.getBoundingClientRect()\n const modelText = model.text\n const idx = locateInModel(model, text, undefined)?.start ?? modelText.indexOf(text)\n const surrounding = idx >= 0 ? modelText.slice(Math.max(0, idx - 30), idx + text.length + 30) : text\n setQuoteBtn({\n x: Math.min(Math.max(rect.left - box.left + rect.width / 2, 90), Math.max(box.width - 90, 90)),\n y: rect.top - box.top - 38,\n text,\n surrounding,\n })\n }\n\n useEffect(() => {\n const SETTLE = 250\n let settleTimer: ReturnType<typeof setTimeout> | null = null\n let hideTimer: ReturnType<typeof setTimeout> | null = null\n let gesture = false\n const selectionHasText = (): boolean => (window.getSelection()?.toString().trim().length ?? 0) >= 2\n const onChange = (): void => {\n if (selectionHasText()) {\n if (hideTimer !== null) { clearTimeout(hideTimer); hideTimer = null }\n setQuoteBtn(cur => (cur === null ? cur : null))\n if (settleTimer !== null) clearTimeout(settleTimer)\n settleTimer = setTimeout(() => {\n settleTimer = null\n if (!gesture && selectionHasText()) evaluateSelection()\n }, SETTLE)\n } else if (hideTimer === null) {\n hideTimer = setTimeout(() => { setQuoteBtn(cur => (cur === null ? cur : null)) }, 250)\n }\n }\n const onDown = (e: PointerEvent): void => {\n const el = e.target as Element | null\n if (el?.closest?.('[data-lks-quote-btn]') !== null) return\n gesture = true\n }\n const onUp = (): void => {\n gesture = false\n if (settleTimer !== null) { clearTimeout(settleTimer); settleTimer = null }\n if (hideTimer === null && selectionHasText()) evaluateSelection()\n }\n document.addEventListener('selectionchange', onChange)\n window.addEventListener('pointerdown', onDown, true)\n window.addEventListener('pointerup', onUp, true)\n window.addEventListener('pointercancel', onUp, true)\n return () => {\n document.removeEventListener('selectionchange', onChange)\n window.removeEventListener('pointerdown', onDown, true)\n window.removeEventListener('pointerup', onUp, true)\n window.removeEventListener('pointercancel', onUp, true)\n if (settleTimer !== null) clearTimeout(settleTimer)\n if (hideTimer !== null) clearTimeout(hideTimer)\n }\n }, [])\n\n // Persisted highlights: record-zone quotes anchor marks; applied after the\n // enhance pass settles (its DOM mutations change the text model).\n const [enhanceTick, setEnhanceTick] = useState(0)\n const [rateBusy, setRateBusy] = useState(false)\n\n const startReading = (): void => {\n if (lesson === null || lesson.speechText.trim() === '') return\n companionEvent('talk-start')\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 companionEvent('talk-end')\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)\n .catch(() => { /* degrade */ })\n .finally(() => {\n // the highlight model must be computed post-enhance (shiki/katex/mermaid\n // mutate the DOM, changing the text model)\n applyHighlights(proseRef.current, recordQuotes)\n setEnhanceTick(Date.now())\n })\n }, [tab, lesson?.html]) // eslint-disable-line react-hooks/exhaustive-deps\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', {\n className: `lks14-viewtab${tab === 'notes' ? ' on' : ''}`,\n 'aria-pressed': String(tab === 'notes'),\n onClick: () => {\n setTab('notes')\n if (lesson !== null && unseen.length > 0) {\n markArtifactsSeen(lesson.lessonId, unseen)\n setUnseenTick(Date.now())\n }\n },\n }, tr('bb.notes'), unseen.length > 0 ? createElement('span', { className: 'lks-viewtab-badge' }, String(unseen.length)) : null),\n ),\n tab === 'teach'\n ? createElement('div', null,\n lesson.due ? createElement('div', { className: 'lks-ratecard', 'data-lks-rate': lesson.lessonId },\n createElement('div', { className: 'lks-ratecard-title' }, tr('review.rate.title')),\n createElement('div', { className: 'lks-ratecard-opts' },\n ...([['1', 'review.again'], ['4', 'review.remembered'], ['5', 'review.mastered']] as const).map(([q, key]) =>\n createElement('button', {\n key: q,\n className: `lks-ratecard-opt${q === '1' ? ' again' : q === '5' ? ' best' : ''}`,\n disabled: rateBusy,\n onClick: () => {\n setRateBusy(true)\n recordReview(lesson.lessonId, Number(q) as 1 | 4 | 5)\n .then(() => {\n companionEvent(Number(q) >= 4 ? 'celebrate' : 'encourage')\n showStudyToast(Number(q) >= 4 ? tr('review.done.good', { days: 1 }) : tr('review.done.again'), { severity: Number(q) >= 4 ? 'success' : 'warning' })\n setRateBusy(false)\n })\n .catch((err: unknown) => {\n setError(err instanceof Error ? err.message : String(err))\n setRateBusy(false)\n })\n },\n }, tr(key)))) ,\n ) : 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-prosewrap' },\n createElement('div', { className: 'lks14-prose', ref: proseRef, dangerouslySetInnerHTML: { __html: lesson.html } }),\n quoteBtn !== null\n ? createElement('div', {\n className: 'lks-quote-btn',\n 'data-lks-quote-btn': '',\n style: { left: `${quoteBtn.x}px`, top: `${quoteBtn.y}px` },\n },\n createElement('button', {\n onClick: () => {\n const truncated = quoteBtn.text.length > 200 ? `${quoteBtn.text.slice(0, 200)}…` : quoteBtn.text\n send(tr('note.quote.template', { text: truncated }))\n setQuoteBtn(null)\n window.getSelection()?.removeAllRanges()\n },\n }, tr('note.quote.ask')),\n createElement('button', {\n onClick: () => {\n if (lesson !== null) {\n void addUserNote(lesson.lessonId, quoteBtn.text).catch((err: unknown) => { setError(err instanceof Error ? err.message : String(err)) })\n void applyHighlights(proseRef.current!, [quoteBtn.text])\n showStudyToast(tr('note.saved'), { severity: 'success' })\n }\n setQuoteBtn(null)\n window.getSelection()?.removeAllRanges()\n },\n }, tr('note.quote.save')))\n : null),\n latestHeavy !== null ? createElement('div', { className: 'lks-acard-stage' },\n createElement(ArtifactCard, { artifact: latestHeavy, send: () => {} }))\n : null,\n )\n : tab === 'cmap'\n ? createElement('div', { className: 'lks14-prose', ref: diagRef })\n : createElement('div', { className: 'lks14-zones' },\n (() => {\n const understand = artifacts.filter(a => a.artifactType === 'compare_table' || a.artifactType === 'diagram' || a.artifactType === 'code_walkthrough')\n if (understand.length === 0) return null\n return createElement('div', { key: 'artifacts', className: 'lks14-zone' },\n createElement('div', { className: 'lks14-zoneh' }, tr('zone.artifacts')),\n ...understand.map(a => createElement(ArtifactCard, { key: a.id, artifact: a, send: () => {} })))\n })(),\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 { COMPANION_FORMS, normalizeCompanionForm, storeCompanionForm, storedCompanionForm } from './companion.tsx'\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 [companion, setCompanion] = useState(storedCompanionForm)\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.companion')),\n createElement('p', { className: 'lks-set-hint' }, tr('settings.companion.hint')),\n createElement('div', null,\n createElement('select', {\n className: 'lks-set-select',\n value: companion,\n onChange: (e: { target: { value: string } }) => { storeCompanionForm(normalizeCompanionForm(e.target.value)); setCompanion(normalizeCompanionForm(e.target.value)) },\n }, ...COMPANION_FORMS.map(f => createElement('option', { key: f.id, value: f.id }, tr(f.labelKey)))),\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","useState","createElement","createElement","useRef","useState","createElement","useState","useRef","createElement","useRef","useState","createElement","MODES","createElement","useState","useRef","createRoot","createElement","useState","createElement","createElement","createElement"],"sources":["../src/client/upstream-theme.ts","../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/toast.ts","../src/client/quizcard.tsx","../src/client/artifact-cards.tsx","../src/client/companion.tsx","../src/client/highlights.ts","../src/client/views.tsx","../src/vendor/map-layout.ts","../src/client/maprail.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 * The upstream LookatStudy skin (0.16.0 P9a) — the 1:1 visual port. Upstream\n * is a Duolingo-style dark-first design system defined in its tailwind.config\n * + index.css :root; this module carries those EXACT tokens (scoped to the\n * panel root `.lks-ui`) and redefines the panel's class vocabulary to match\n * upstream's components: the three-column surface ladder (rail darkest / chat\n * surface-1 / notebook surface-2, no borders — depth by color step), the\n * floating blur app-header, 3D push-down buttons, the claude.ai-style\n * composer capsule, chat bubbles, typing dots, and the toast capsules.\n * Host-chrome surfaces (dock pill, settings, toolviews) keep the dsw tokens —\n * only the study panel wears the upstream skin.\n * @module dsh-plugin-lookatstudy/client/upstream-theme\n */\n\nexport const UPSTREAM_CSS = `\n/* ── upstream design tokens (index.css :root, verbatim values) ── */\n.lks-ui{\n --brand:#58CC01;--brand-rgb:88 204 2;--brand-dark:#46A302;--brand-light:#7ED957;\n --brand-glow:#58CC0159;--brand-ring:#58CC0126;\n --accent:#1DB0F6;--accent-rgb:28 176 246;--accent-dark:#098CDC;--accent-light:#65C6FF;\n --accent-glow:#1DB0F64C;--accent-ring:#1DB0F626;\n --gold:#FFC801;--gold-rgb:255 200 0;--gold-dark:#D59800;--gold-light:#FFE294;\n --gold-glow:#FFC80173;--gold-ring:#FFC80133;\n --warning:#FF4B4B;--warning-rgb:255 75 75;--warning-dark:#D01C29;--warning-light:#FF7E77;\n --warning-glow:#FF4B4B4C;--warning-ring:#FF4B4B26;--warning-tint:#FF4B4B1A;--warning-tint-border:#FF4B4B40;\n --review:#FA6E1D;--review-rgb:255 122 26;--review-glow:#FA6E1D26;--review-tint:#FA6E1D1A;\n --exam:#A855F7;--exam-rgb:168 85 247;--exam-dark:#7E22CE;--exam-light:#C084FC;--exam-locked:#524569;\n --exam-glow:#A855F759;--exam-ring:#A855F726;\n --surface-rail:#08090B;--surface-rail-rgb:8 9 11;\n --surface-0:#0C0D0F;--surface-0-rgb:12 13 15;\n --surface-1:#111114;--surface-1-rgb:17 17 20;\n --surface-2:#1A1A1D;--surface-2-rgb:26 26 29;\n --surface-3:#2A2B2E;--surface-3-rgb:42 43 46;\n --ink:#F1F2F4;--ink-rgb:245 245 250;\n --ink-strong:#FAFAFA;--ink-strong-rgb:250 250 250;\n --ink-muted:#9D9EA2;--ink-muted-rgb:166 166 176;\n --ink-faint:#88898C;--ink-faint-rgb:136 137 140;\n --border:#252629;--border-rgb:37 38 41;--border-faint:#1A1A1D;--border-faint-rgb:26 26 29;\n --shadow-rgb:0 0 0;--inner-highlight:rgb(255 255 255/0.55);\n --ease-out-expo:cubic-bezier(0.16,1,0.3,1);\n --ease-out-back:cubic-bezier(0.34,1.2,0.64,1);\n --ease-spring:cubic-bezier(0.34,1.56,0.64,1);\n --ease-out-quart:cubic-bezier(0.25,1,0.5,1);\n font-family:'DIN Round',system-ui,-apple-system,sans-serif;\n background:var(--surface-1);color:var(--ink);\n font-feature-settings:'cv02','cv03','cv04','cv11';\n -webkit-font-smoothing:antialiased;\n}\n/* the six-step type scale (rem, follows the host base) */\n.lks-ui .lks-t-caption{font-size:.75rem}.lks-ui .lks-t-label{font-size:.825rem}\n.lks-ui .lks-t-body{font-size:.875rem}.lks-ui .lks-t-lead{font-size:1rem}\n.lks-ui .lks-t-title{font-size:1.125rem}.lks-ui .lks-t-hero{font-size:1.5rem}\n\n/* ── the column ladder: no borders, depth by surface step (v0.6) ── */\n.lks-ui .lks14-rail{background:var(--surface-rail);border-right:none}\n.lks-ui .lks14-chat{background:var(--surface-1);border-right:none}\n.lks-ui .lks14-note{background:var(--surface-2)}\n.lks-ui .lks14-colhead{color:var(--ink-muted);border-bottom:1px solid var(--border-faint);letter-spacing:.02em;text-transform:none;font-size:.825rem}\n\n/* ── the floating app-header over the right half (blur + gradient fade) ── */\n.lks-ui .lks14-appheader{position:relative;z-index:30;flex:none;display:flex;align-items:center;height:46px;padding:0 14px;gap:10px}\n.lks-ui .lks14-appheader::before{content:'';position:absolute;inset:0;z-index:-1;backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);\n -webkit-mask:linear-gradient(to bottom,black 0%,black 65%,transparent 100%);mask:linear-gradient(to bottom,black 0%,black 65%,transparent 100%);\n background:linear-gradient(to bottom,rgb(var(--surface-1-rgb)/0.85) 0%,rgb(var(--surface-1-rgb)/0.55) 60%,rgb(var(--surface-1-rgb)/0.15) 85%,transparent 100%)}\n.lks-ui .lks-hdr-title{flex:1;min-width:0;font-size:.825rem;font-weight:700;color:var(--ink-strong);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.lks-ui .lks-hdr-xp{display:flex;align-items:center;gap:5px;flex:none}\n.lks-ui .lks-hdr-xpbar{width:92px;height:10px;border-radius:999px;background:rgb(var(--ink-rgb)/0.08);overflow:hidden;box-shadow:inset 0 2px 3px rgb(var(--shadow-rgb)/0.3)}\n.lks-ui .lks-hdr-xpbar i{display:block;height:100%;background:linear-gradient(90deg,var(--gold),var(--gold-light));border-radius:999px;transition:width .5s var(--ease-out-expo)}\n.lks-ui .lks-hdr-stat{display:inline-flex;align-items:center;gap:4px;font-size:.825rem;font-weight:700;color:var(--ink);flex:none}\n.lks-ui .lks-hdr-streak{color:var(--review)}\n\n/* ── 3D push-down buttons (the signature style) ── */\n.lks-ui .lks-btn{border-radius:1rem;font-weight:700;transition:all .15s var(--ease-spring)}\n.lks-ui .lks-btn.primary{background:var(--brand);color:#fff;box-shadow:0 4px 0 0 var(--brand-dark)}\n.lks-ui .lks-btn.primary:hover{background:var(--brand-light)}\n.lks-ui .lks-btn.primary:active{transform:translateY(2px);box-shadow:0 1px 0 0 var(--brand-dark)}\n.lks-ui .lks-btn.primary:disabled{background:rgb(var(--ink-rgb)/0.15);color:var(--ink-faint);box-shadow:none;transform:none;cursor:not-allowed}\n.lks-ui .lks-btn.ghost{background:rgb(var(--ink-rgb)/0.06);color:var(--ink);border:none;box-shadow:0 3px 0 0 rgb(var(--shadow-rgb)/0.2)}\n.lks-ui .lks-btn.ghost:hover{color:var(--ink-strong);background:rgb(var(--ink-rgb)/0.1)}\n.lks-ui .lks-btn.ghost:active{transform:translateY(2px);box-shadow:0 1px 0 0 rgb(var(--shadow-rgb)/0.2)}\n\n/* ── chat stream: bubbles + typing dots ── */\n.lks-ui .lks14-msg-user{background:rgb(var(--ink-rgb)/0.04);border-radius:1rem 1rem .375rem 1rem;padding:.625rem 1rem;color:var(--ink-strong);animation:lks-msg-enter .2s var(--ease-out-expo)}\n.lks-ui .lks14-msg-assistant{background:var(--surface-2);border:1px solid var(--border-faint);border-radius:1rem;padding:1rem;box-shadow:0 1px 3px -1px rgb(var(--shadow-rgb)/0.08);animation:lks-msg-enter .2s var(--ease-out-expo);font-size:.875rem;line-height:1.65}\n.lks-ui .lks14-msg.streaming{opacity:1;border-style:dashed}\n@keyframes lks-msg-enter{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}\n.lks-ui .lks14-thinking{gap:5px}\n@keyframes lks-typing-dot{0%,60%,100%{opacity:.3;transform:translateY(0)}30%{opacity:1;transform:translateY(-4px)}}\n.lks-ui .lks14-thinking i{width:6px;height:6px;border-radius:999px;background:var(--brand);display:inline-block;animation:lks-typing-dot 1.2s ease-in-out infinite}\n.lks-ui .lks14-thinking i:nth-child(2){animation-delay:.15s}\n.lks-ui .lks14-thinking i:nth-child(3){animation-delay:.3s}\n/* quiz options wear the neutral pill + brand hover */\n.lks-ui .lks-qcard-opt{background:rgb(var(--ink-rgb)/0.05);border:2px solid transparent;border-radius:.75rem;color:var(--ink)}\n.lks-ui .lks-qcard-opt:not(:disabled):hover{border-color:var(--brand)}\n.lks-ui .lks-qcard-opt.right{border-color:var(--brand);color:var(--brand-light);background:rgb(var(--brand-rgb)/0.08)}\n.lks-ui .lks-qcard-opt.wrong{border-color:var(--warning);color:var(--warning-light);background:var(--warning-tint)}\n.lks-ui .lks-qcard-next{background:var(--brand);color:#fff;box-shadow:0 3px 0 0 var(--brand-dark);border-radius:1rem}\n.lks-ui .lks-qcard-next:active{transform:translateY(2px);box-shadow:0 1px 0 0 var(--brand-dark)}\n.lks-ui .lks-qcard-action{background:rgb(var(--ink-rgb)/0.06);border:none;color:var(--ink)}\n.lks-ui .lks-qcard-action:hover{color:var(--ink-strong);background:rgb(var(--ink-rgb)/0.1)}\n\n/* ── the composer capsule (claude.ai style) + circular 3D send ── */\n.lks-ui .lks14-composer{border-top:none;background:linear-gradient(to bottom,transparent 0%,var(--surface-1) 40%)}\n.lks-ui .lks14-composer-card{flex:1;display:flex;align-items:flex-end;gap:8px;background:rgb(var(--ink-rgb)/0.05);border-radius:1rem;padding:.625rem .75rem .5rem .875rem;transition:background .15s}\n.lks-ui .lks14-composer-card:focus-within{background:rgb(var(--ink-rgb)/0.07)}\n.lks-ui .lks14-composertext{background:transparent;border:none;box-shadow:none;color:var(--ink-strong);font-size:.875rem}\n.lks-ui .lks14-composertext:focus{border-color:transparent}\n.lks-ui .lks14-composertext::placeholder{color:var(--ink-faint)}\n.lks-ui .lks-btn-send{width:36px;height:36px;border-radius:999px !important;padding:0 !important;display:inline-flex;align-items:center;justify-content:center;flex:none;background:var(--brand);color:#fff;box-shadow:0 3px 0 0 var(--brand-dark);border:none;cursor:pointer;transition:all .15s var(--ease-spring)}\n.lks-ui .lks-btn-send:hover{background:var(--brand-light)}\n.lks-ui .lks-btn-send:active{transform:translateY(2px);box-shadow:0 1px 0 0 var(--brand-dark)}\n.lks-ui .lks-btn-send:disabled{background:rgb(var(--ink-rgb)/0.15);color:var(--ink-faint);box-shadow:none;transform:none;cursor:not-allowed}\n\n/* ── toast capsules on the upstream tokens ── */\n.lks-ui .lks-toast{background:var(--surface-0);color:var(--ink-strong);border:1px solid var(--border);box-shadow:0 8px 24px -4px rgb(var(--shadow-rgb)/0.18),0 2px 6px -2px rgb(var(--shadow-rgb)/0.12)}\n.lks-ui .lks-toast .lks-toast-glyph.ok{color:var(--brand)}\n.lks-ui .lks-toast .lks-toast-glyph.warn{color:var(--gold)}\n.lks-ui .lks-toast .lks-toast-glyph.err{color:var(--warning)}\n.lks-ui .lks-toast .lks-toast-glyph.info{color:var(--accent)}\n.lks-ui .lks-toast-action{color:var(--brand)}\n\n/* ── rail: signpost section heads + due box ── */\n.lks-ui .lks14-sechead{background:rgb(var(--surface-rail-rgb)/0.55);backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px);border:1px solid rgb(var(--gold-rgb)/0.18);border-top-color:rgb(var(--gold-rgb)/0.35);border-radius:.75rem;padding:7px 10px;margin:12px 0 6px;box-shadow:0 2px 8px rgb(var(--shadow-rgb)/0.35),inset 0 1px 0 rgb(255 255 255/0.06);color:var(--gold-light)}\n.lks-ui .lks14-secnum{background:rgb(var(--gold-rgb)/0.15);color:var(--gold-light)}\n.lks-ui .lks14-node{color:var(--ink);border-radius:.75rem}\n.lks-ui .lks14-node:hover{background:rgb(var(--ink-rgb)/0.05)}\n.lks-ui .lks14-node.focus{background:rgb(var(--accent-rgb)/0.1);outline:1px solid rgb(var(--accent-rgb)/0.25)}\n.lks-ui .lks14-node[aria-disabled='true']{color:var(--ink-faint)}\n.lks-ui .lks14-search,.lks-ui .lks-set-select{background:rgb(var(--ink-rgb)/0.05);border:1px solid var(--border-faint);color:var(--ink-strong)}\n.lks-ui .lks14-search:focus{border-color:var(--accent)}\n.lks-ui .lks14-duebox{background:var(--review-tint);border:1px solid rgb(var(--review-rgb)/0.25);color:var(--ink)}\n.lks-ui .lks14-over{color:var(--warning-light)}\n.lks-ui .lks14-tag.weak{color:var(--gold-light)}\n.lks-ui .lks14-masterybar{background:rgb(var(--ink-rgb)/0.08)}\n.lks-ui .lks14-masterybar i{background:var(--gold)}\n.lks-ui .lks14-bar{background:rgb(var(--ink-rgb)/0.08)}\n.lks-ui .lks14-bar i{background:var(--gold)}\n.lks-ui .lks14-pill{color:var(--ink-muted);border-radius:999px}\n.lks-ui .lks14-pill.on{background:rgb(var(--accent-rgb)/0.12);color:var(--accent-light)}\n\n/* ── notebook: teach prose + view tabs ── */\n.lks-ui .lks14-viewtab{color:var(--ink-muted);border-radius:999px}\n.lks-ui .lks14-viewtab.on{background:rgb(var(--ink-rgb)/0.08);color:var(--ink-strong)}\n.lks-ui .lks14-prose{color:var(--ink);font-size:.9375rem;line-height:1.65}\n.lks-ui .lks14-prose h1,.lks-ui .lks-note-text h1{font-size:1.25rem;font-weight:800;color:var(--ink-strong)}\n.lks-ui .lks14-prose h2,.lks-ui .lks-note-text h2{font-size:1.1rem;font-weight:700;color:var(--ink-strong);border-bottom:1px solid var(--border);padding-bottom:.3em}\n.lks-ui .lks14-prose h3,.lks-ui .lks-note-text h3{font-size:1rem;font-weight:700;color:var(--ink)}\n.lks-ui .lks14-prose pre,.lks-ui .lks-note-text pre{background:var(--surface-0);border:1px solid var(--border);color:var(--ink)}\n.lks-ui .lks14-prose code,.lks-ui .lks-note-text code{background:rgb(var(--surface-3-rgb)/0.6);color:var(--accent-light)}\n.lks-ui .lks14-prose blockquote{border-left:3px solid var(--accent);background:rgb(var(--accent-rgb)/0.06);color:var(--ink-muted)}\n.lks-ui .lks14-prose table thead{border-bottom:2px solid var(--brand)}\n.lks-ui .lks14-prose th{color:var(--ink-strong)}\n.lks-ui .lks14-prose tbody tr:nth-child(odd){background:rgb(var(--surface-3-rgb)/0.3)}\n.lks-ui .lks14-prose td,.lks-ui .lks14-prose th{border-color:var(--border)}\n.lks-ui .lks-note{background:var(--surface-1);border:1px solid var(--border-faint)}\n.lks-ui .lks-note .lks-note-q{border-left-color:var(--gold)}\n.lks-ui .lks14-zoneh{color:var(--ink-muted)}\n.lks-ui mark.lks-hl{background:rgb(var(--gold-rgb)/0.25);border-bottom:2px solid var(--gold)}\n\n/* ── artifact + rate + proposal cards on surface-card language ── */\n.lks-ui .lks-acard,.lks-ui .lks-qcard{background:var(--surface-2);border:1px solid var(--border-faint);border-radius:1rem;box-shadow:0 1px 3px -1px rgb(var(--shadow-rgb)/0.08)}\n.lks-ui .lks-acard-head,.lks-ui .lks-qcard-head{color:var(--ink-muted)}\n.lks-ui .lks-acard-table th{background:transparent;border-bottom:2px solid var(--brand);color:var(--ink-strong)}\n.lks-ui .lks-acard-table td{border-bottom:1px solid var(--border)}\n.lks-ui .lks-acard-code{background:var(--surface-0);border:1px solid var(--border)}\n.lks-ui .lks-acard-line i{color:var(--ink-faint)}\n.lks-ui .lks-acard-note{border-left-color:var(--accent);color:var(--ink-muted)}\n.lks-ui .lks-acard-modal-body{background:var(--surface-0);border-color:var(--border)}\n.lks-ui .lks-acard-guess-opt{background:rgb(var(--ink-rgb)/0.05);border:2px solid transparent;color:var(--ink)}\n.lks-ui .lks-acard-guess-opt:not(:disabled):hover{border-color:var(--accent)}\n.lks-ui .lks-acard-guess-opt.picked{border-color:var(--accent);background:rgb(var(--accent-rgb)/0.1);color:var(--accent-light)}\n.lks-ui .lks-ratecard{background:rgb(var(--review-rgb)/0.08);border:1px solid rgb(var(--review-rgb)/0.25)}\n.lks-ui .lks-ratecard-title{color:var(--ink-strong)}\n.lks-ui .lks-ratecard-opt{background:rgb(var(--ink-rgb)/0.05);border:2px solid transparent;color:var(--ink)}\n.lks-ui .lks-ratecard-opt:not(:disabled):hover{border-color:var(--brand)}\n.lks-ui .lks-ratecard-opt.best{background:var(--gold);color:#3A2E00;box-shadow:0 3px 0 0 var(--gold-dark);border-color:transparent}\n.lks-ui .lks-ratecard-opt.best:active{transform:translateY(2px);box-shadow:0 1px 0 0 var(--gold-dark)}\n.lks-ui .lks-propbanner{background:rgb(var(--gold-rgb)/0.06);border:1px solid rgb(var(--gold-rgb)/0.3);color:var(--ink)}\n.lks-ui .lks-viewtab-badge{background:var(--brand);color:#fff}\n\n/* ── the empty states + readbar + switcher ── */\n.lks-ui .lks14-empty{color:var(--ink-muted)}\n.lks-ui .lks-readbar-cur{border-left-color:var(--accent);color:var(--ink-muted)}\n.lks-ui .lks14-switch{background:rgb(var(--ink-rgb)/0.06);border:1px solid var(--border-faint)}\n.lks-ui .lks14-switch-btn.on{background:var(--brand);color:#fff}\n.lks-ui .lks14-searchpanel{background:var(--surface-0);border:1px solid var(--border);box-shadow:0 8px 24px -4px rgb(var(--shadow-rgb)/0.18)}\n.lks-ui .lks14-searchrow:hover{background:rgb(var(--ink-rgb)/0.05)}\n.lks-ui .lks14-searchrow-snip{color:var(--ink-muted)}\n.lks-ui .lks-propcard-err{color:var(--warning-light)}\n.lks-ui .lks14-thinking{color:var(--ink-muted)}\n\n/* ══════════ P9b: the balloon course map (upstream v0.28 MapRail, static path) ══════════ */\n.lks-ui{--exam-locked-mix:#A7A2B2}\n/* the sky: upstream's canvas presets reduced to layered gradients (essence port;\n deterministic per-course via pickSky) */\n.lks-ui .lks-map{position:relative;min-height:100%;background-repeat:no-repeat}\n.lks-ui .lks-sky-day{background-image:\n radial-gradient(1.5px 1.5px at 18% 12%,rgb(255 255 255/0.5),transparent 100%),\n radial-gradient(1px 1px at 64% 8%,rgb(255 255 255/0.35),transparent 100%),\n radial-gradient(1px 1px at 82% 22%,rgb(255 255 255/0.3),transparent 100%),\n radial-gradient(120px 60px at 78% 6%,rgb(124 172 255/0.08),transparent 100%),\n linear-gradient(to bottom,#16233F 0%,#0F1B33 55%,var(--surface-rail) 100%)}\n.lks-ui .lks-sky-dusk{background-image:\n radial-gradient(1.5px 1.5px at 24% 10%,rgb(255 255 255/0.45),transparent 100%),\n radial-gradient(1px 1px at 70% 16%,rgb(255 255 255/0.3),transparent 100%),\n radial-gradient(140px 70px at 22% 10%,rgb(255 160 90/0.07),transparent 100%),\n linear-gradient(to bottom,#2A2140 0%,#1D1A38 50%,var(--surface-rail) 100%)}\n.lks-ui .lks-sky-night{background-image:\n radial-gradient(1.5px 1.5px at 12% 18%,rgb(255 255 255/0.55),transparent 100%),\n radial-gradient(1px 1px at 38% 6%,rgb(255 255 255/0.4),transparent 100%),\n radial-gradient(1px 1px at 58% 24%,rgb(255 255 255/0.35),transparent 100%),\n radial-gradient(1.5px 1.5px at 88% 10%,rgb(255 255 255/0.5),transparent 100%),\n radial-gradient(1px 1px at 76% 30%,rgb(255 255 255/0.3),transparent 100%),\n radial-gradient(90px 90px at 84% 8%,rgb(200 214 255/0.06),transparent 100%),\n linear-gradient(to bottom,#0B1024 0%,#0A0D1C 55%,var(--surface-rail) 100%)}\n.lks-ui .lks-mapsec{padding:12px 8px 4px}\n/* the signpost (map-signpost verbatim): frosted board with a gold rim */\n.lks-ui .lks-signpost{display:flex;align-items:center;gap:8px;width:100%;padding:6px 10px;border-radius:8px;font:inherit;cursor:pointer;text-align:left;\n background:rgb(var(--surface-rail-rgb)/0.55);backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px);\n border:1px solid rgb(var(--gold-rgb)/0.18);border-top-color:rgb(var(--gold-rgb)/0.35);\n box-shadow:0 2px 8px rgb(var(--shadow-rgb)/0.35),inset 0 1px 0 rgb(255 255 255/0.06)}\n.lks-ui .lks-signpost-num{width:24px;height:24px;border-radius:999px;background:var(--gold);color:#201500;font-size:11px;font-weight:800;display:flex;align-items:center;justify-content:center;flex:none;box-shadow:0 0 0 2px rgb(var(--gold-rgb)/0.4)}\n.lks-ui .lks-signpost-title{flex:1;font-size:12.5px;font-weight:700;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-shadow:0 1px 2px rgba(0,0,0,0.7)}\n.lks-ui .lks-signpost-caret{color:rgb(var(--ink-rgb)/0.5);font-size:10px}\n.lks-ui .lks-mapfield{position:relative}\n.lks-ui .lks-mapropes{position:absolute;inset:0;width:100%;pointer-events:none;overflow:visible}\n.lks-ui .lks-mapnode{position:absolute;width:110px;display:flex;flex-direction:column;align-items:center;\n animation:lks-balloon-bob var(--bob-duration,6s) ease-in-out infinite;animation-delay:var(--bob-delay,0s)}\n@keyframes lks-balloon-bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}\n@keyframes lks-path-draw{from{stroke-dashoffset:1}to{stroke-dashoffset:0}}\n@media (prefers-reduced-motion: reduce){.lks-ui .lks-mapnode{animation:none}.lks-ui .lks-bubble-available{animation:none}}\n/* the bubbles (lesson-bubble verbatim): 3D sphere lighting + ::before inner highlight */\n.lks-ui .lks-bubble{position:relative;width:56px;height:56px;border-radius:999px;border:none;display:flex;align-items:center;justify-content:center;cursor:pointer;padding:0;transition:all .2s var(--ease-out-back);background-position:30% 25%}\n.lks-ui .lks-bubble::before{content:'';position:absolute;top:12%;left:22%;width:38%;height:28%;border-radius:50%;\n background:radial-gradient(ellipse at center,var(--inner-highlight) 0%,rgb(255 255 255/0) 70%);pointer-events:none;z-index:1}\n.lks-ui .lks-bubble[aria-disabled='true']{cursor:not-allowed}\n.lks-ui .lks-bubble.selected{outline:4px solid var(--accent);outline-offset:3px}\n.lks-ui .lks-bubble-glyph{position:relative;z-index:2;color:#fff;display:inline-flex;filter:drop-shadow(0 1px 2px rgba(0,0,0,0.4))}\n.lks-ui .lks-bubble-glyph.dim{opacity:.5}\n.lks-ui .lks-bubble-ring{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg);pointer-events:none}\n.lks-ui .lks-bubble-due{position:absolute;top:-4px;right:-4px;width:16px;height:16px;border-radius:999px;background:var(--review);color:#fff;font-size:10px;font-weight:700;display:flex;align-items:center;justify-content:center;box-shadow:0 0 0 2px var(--surface-rail);z-index:3}\n.lks-ui .lks-bubble-name{margin-top:4px;max-width:120px;font-size:11px;font-weight:700;color:#fff;background:rgb(var(--brand-rgb)/0.9);padding:2px 6px;border-radius:6px;text-align:center;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n/* locked: heavy sunken stone */\n.lks-ui .lks-bubble-locked{background:radial-gradient(circle at 35% 30%,var(--surface-3) 0%,var(--surface-2) 60%,var(--surface-1) 100%);\n box-shadow:inset 0 3px 6px rgb(var(--shadow-rgb)/0.5),inset 0 -2px 4px rgb(255 255 255/0.04),0 2px 4px rgb(var(--shadow-rgb)/0.3)}\n.lks-ui .lks-bubble-locked::before{background:radial-gradient(ellipse at center,rgb(255 255 255/0.12) 0%,rgb(255 255 255/0) 70%)}\n/* available: bright glowing sphere, your move */\n.lks-ui .lks-bubble-available{background:radial-gradient(circle at 32% 28%,var(--brand-light) 0%,var(--brand) 45%,var(--brand-dark) 100%);\n box-shadow:0 0 0 4px var(--brand-ring),0 0 16px var(--brand-glow),0 4px 10px rgb(var(--shadow-rgb)/0.25)}\n.lks-ui .lks-bubble-available:hover{transform:translateY(-3px) scale(1.03);\n box-shadow:0 0 0 5px rgb(var(--brand-rgb)/0.2),0 0 22px rgb(var(--brand-rgb)/0.5),0 6px 14px rgb(var(--shadow-rgb)/0.3)}\n.lks-ui .lks-bubble-available:active{transform:translateY(2px) scale(0.97);\n box-shadow:0 0 0 3px var(--brand-ring),0 0 10px rgb(var(--brand-rgb)/0.3),0 2px 6px rgb(var(--shadow-rgb)/0.2)}\n@keyframes lks-bubble-pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.04)}}\n.lks-ui .lks-bubble-available{animation:lks-bubble-pulse 2.4s ease-in-out infinite}\n/* in_progress: blue in motion */\n.lks-ui .lks-bubble-in-progress{background:radial-gradient(circle at 32% 28%,var(--accent-light) 0%,var(--accent) 45%,var(--accent-dark) 100%);\n box-shadow:0 0 0 4px var(--accent-ring),0 0 14px var(--accent-glow),0 4px 10px rgb(var(--shadow-rgb)/0.25)}\n.lks-ui .lks-bubble-in-progress:hover{transform:translateY(-2px) scale(1.03);\n box-shadow:0 0 0 5px rgb(var(--accent-rgb)/0.2),0 0 20px rgb(var(--accent-rgb)/0.45),0 6px 14px rgb(var(--shadow-rgb)/0.3)}\n/* mastered: gold crown halo, scaled up */\n.lks-ui .lks-bubble-mastered{background:radial-gradient(circle at 32% 28%,var(--gold-light) 0%,var(--gold) 45%,var(--gold-dark) 100%);\n box-shadow:0 0 0 4px var(--gold-ring),0 0 22px var(--gold-glow),0 0 40px rgb(var(--gold-rgb)/0.15),0 4px 12px rgb(var(--shadow-rgb)/0.25);transform:scale(1.1)}\n.lks-ui .lks-bubble-mastered::before{background:radial-gradient(ellipse at center,rgb(255 255 255/0.7) 0%,rgb(255 255 255/0) 70%)}\n.lks-ui .lks-bubble-mastered:hover{transform:scale(1.13) translateY(-2px);\n box-shadow:0 0 0 5px rgb(var(--gold-rgb)/0.25),0 0 28px rgb(var(--gold-rgb)/0.55),0 0 50px rgb(var(--gold-rgb)/0.2),0 6px 16px rgb(var(--shadow-rgb)/0.3)}\n/* exam: the section boss */\n.lks-ui .lks-exam{background:radial-gradient(circle at 32% 28%,var(--exam-light) 0%,var(--exam) 45%,var(--exam-dark) 100%);\n box-shadow:0 0 0 4px var(--exam-ring),0 0 16px var(--exam-glow),0 4px 10px rgb(var(--shadow-rgb)/0.25)}\n.lks-ui .lks-exam-locked{background:radial-gradient(circle at 35% 30%,var(--exam-locked) 0%,var(--exam-locked-mix) 60%,var(--surface-1) 100%);\n box-shadow:inset 0 3px 6px rgb(var(--shadow-rgb)/0.5),inset 0 -2px 4px rgb(255 255 255/0.04),0 2px 4px rgb(var(--shadow-rgb)/0.3)}\n.lks-ui .lks-exam-passed{background:radial-gradient(circle at 32% 28%,rgb(var(--exam-light-rgb,192 132 252)) 0%,var(--exam-light) 40%,var(--exam) 100%);\n box-shadow:0 0 0 4px var(--gold-ring),0 0 18px var(--exam-glow),0 0 28px rgb(var(--gold-rgb)/0.15),0 4px 12px rgb(var(--shadow-rgb)/0.25)}\n/* the floating rail head card (upstream map-header card) */\n.lks-ui .lks14-railhead{background:rgb(var(--surface-rail-rgb)/0.55);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border:1px solid rgb(255 255 255/0.06);border-radius:12px;padding:8px 10px}\n.lks-ui .lks14-railtitle{color:#fff;font-weight:800;font-size:.875rem;text-shadow:0 1px 3px rgba(0,0,0,0.6)}\n.lks-ui .lks14-railsub{color:rgb(var(--ink-rgb)/0.6)}\n.lks-ui .lks14-masterybar{height:10px;background:rgb(0 0 0/0.4);border-radius:999px;box-shadow:inset 0 0 0 1px rgb(255 255 255/0.1);overflow:hidden}\n.lks-ui .lks14-masterybar i{background:var(--brand)}\n.lks-ui .lks14-masterybar.gold i{background:var(--gold)}\n.lks-ui .lks14-search{background:rgb(0 0 0/0.25);border:1px solid rgb(255 255 255/0.15);color:var(--ink-strong);border-radius:10px}\n.lks-ui .lks14-search:focus{border-color:var(--brand);outline:none}\n.lks-ui .lks14-search::placeholder{color:rgb(var(--ink-rgb)/0.35)}\n\n/* ══════════ A-track (P10a): island zeroing — the 27+6 un-skinned leftovers ══════════ */\n/* buttons reset to UA buttontext/buttonface (black/light) — the panel's ink must flow through */\n.lks-ui button{color:inherit;background-color:transparent;border-color:transparent}\n/* A6 token completion: rgb-channel companions + concept-map palette (upstream :root) */\n.lks-ui{--brand-dark-rgb:70 163 2;--brand-light-rgb:126 217 87;\n --accent-dark-rgb:10 140 220;--accent-light-rgb:94 212 255;\n --gold-dark-rgb:217 158 0;--gold-light-rgb:255 230 128;\n --warning-dark-rgb:230 43 43;--warning-light-rgb:255 117 117;\n --exam-dark-rgb:126 34 206;--exam-light-rgb:192 132 252;\n --cm-c0-fill:#1C3352;--cm-c0-line:#6F9FD4;--cm-c1-fill:#1F3A29;--cm-c1-line:#7FB37F;\n --cm-c2-fill:#42301A;--cm-c2-line:#CFA05E;--cm-c3-fill:#3D3620;--cm-c3-line:#CBB75E;\n --cm-c4-fill:#33294A;--cm-c4-line:#A48DCB}\n/* A1 in-chat quiz options (click = send answer; neutral pill, hover brand) */\n.lks-ui .lks14-opt{background:rgb(var(--ink-rgb)/0.05);border:2px solid transparent;color:var(--ink)}\n.lks-ui .lks14-opt:hover{border-color:var(--brand);background:rgb(var(--ink-rgb)/0.05)}\n.lks-ui .lks14-optletter{color:var(--brand-light)}\n.lks-ui .lks14-starter{background:transparent;border:1px solid var(--border-faint);color:var(--ink-muted)}\n.lks-ui .lks14-starter:hover{background:rgb(var(--ink-rgb)/0.06);color:var(--ink);border-color:var(--border)}\n.lks-ui .lks14-chatlesson{color:var(--ink-muted)}\n.lks-ui .lks14-meta{color:var(--ink-muted)}\n.lks-ui .lks14-dueitem{color:var(--ink-muted)}\n.lks-ui .lks14-hint{color:var(--ink-faint)}\n.lks-ui .lks-readbar-notice{color:var(--ink-faint)}\n.lks-ui .lks-cmap-legend{color:var(--ink-muted)}\n/* A2 quiz-card body family */\n.lks-ui .lks-qcard-prompt{color:var(--ink)}\n.lks-ui .lks-qcard-count{color:var(--ink-faint)}\n.lks-ui .lks-qcard-ans{color:var(--ink-faint)}\n.lks-ui .lks-qcard-expl{color:var(--ink-muted);border-left-color:var(--accent)}\n.lks-ui .lks-qcard-prompt.review.wrong{color:var(--warning-light)}\n.lks-ui .lks-qcard-q.review{border-bottom-color:var(--border)}\n/* A2 artifact bits */\n.lks-ui .lks-acard-expand{color:var(--ink-faint);border:none;background:none}\n.lks-ui .lks-acard-expand:hover{color:var(--ink);background:rgb(var(--ink-rgb)/0.06)}\n.lks-ui .lks-acard-modal-title{color:var(--ink-strong)}\n.lks-ui .lks-acard-guess-prompt{color:var(--ink)}\n.lks-ui .lks-acard-guess-wait{color:var(--ink-faint)}\n.lks-ui .lks-acard-note b{color:var(--ink-strong)}\n.lks-ui .lks-acard-stage{border-top-color:var(--border)}\n/* A2 note family */\n.lks-ui .lks-note-text{color:var(--ink-muted)}\n.lks-ui .lks-note-text th,.lks-ui .lks-note-text td{border-color:var(--border)}\n.lks-ui .lks-note-src{color:var(--ink-faint)}\n.lks-ui .lks-note-del{color:var(--ink-faint)}\n.lks-ui .lks-note-del:hover{color:var(--warning-light);background:rgb(var(--warning-rgb)/0.08)}\n.lks-ui .lks-note-del.armed{color:#fff;background:var(--warning)}\n.lks-ui .lks-note-q{color:var(--ink-muted)}\n.lks-ui .lks-note-title{color:var(--ink-strong)}\n/* A3 selection popover → surface-0 floating card */\n.lks-ui .lks-quote-btn{background:var(--surface-0);border:1px solid var(--border);box-shadow:0 4px 12px -2px rgb(var(--shadow-rgb)/0.14),0 1px 3px -1px rgb(var(--shadow-rgb)/0.08);border-radius:10px;overflow:hidden;animation:lks-confirm-enter 160ms var(--ease-out-back)}\n.lks-ui .lks-quote-btn button{color:var(--ink)}\n.lks-ui .lks-quote-btn button:hover{background:rgb(var(--ink-rgb)/0.06)}\n.lks-ui .lks-quote-btn button + button{border-left-color:var(--border)}\n/* A4 prose links / markers / strong / hr */\n.lks-ui .lks14-prose a,.lks-ui .lks-note-text a{color:rgb(var(--accent-rgb));text-decoration:none;font-weight:500}\n.lks-ui .lks14-prose a:hover,.lks-ui .lks-note-text a:hover{text-decoration:underline}\n.lks-ui .lks14-prose li::marker,.lks-ui .lks-note-text li::marker{color:rgb(var(--brand-rgb))}\n.lks-ui .lks14-prose strong,.lks-ui .lks-note-text strong{color:var(--ink-strong)}\n.lks-ui .lks14-prose hr,.lks-ui .lks-note-text hr{border-color:var(--border)}\n/* A8 table borders inside bubbles/cards/notes */\n.lks-ui .lks14-msg-assistant th,.lks-ui .lks14-msg-assistant td{border-color:var(--border)}\n.lks-ui .lks-acard-table th,.lks-ui .lks-acard-table td{border-color:var(--border)}\n.lks-ui .lks-acard-table th{border-bottom-color:var(--brand)}\n/* A5 scoped scrollbar (host-light chrome otherwise scrolls inside dark columns) */\n.lks-ui ::-webkit-scrollbar{width:8px;height:8px}\n.lks-ui ::-webkit-scrollbar-track{background:transparent}\n.lks-ui ::-webkit-scrollbar-thumb{background:var(--border);border-radius:9999px;border:2px solid transparent;background-clip:padding-box}\n.lks-ui ::-webkit-scrollbar-thumb:hover{background:var(--surface-3);background-clip:padding-box}\n/* A7 feedback/ambient animations (upstream values verbatim) */\n@keyframes lks-crown-sparkle{0%,100%{transform:scale(1);filter:drop-shadow(0 0 0 var(--gold))}50%{transform:scale(1.08);filter:drop-shadow(0 0 6px var(--gold-glow))}}\n.lks-ui .lks-bubble-mastered .lks-bubble-glyph{animation:lks-crown-sparkle 1.6s ease-in-out infinite;display:inline-flex}\n@keyframes lks-energy-breathe{0%,100%{opacity:.78;filter:drop-shadow(0 0 0 var(--gold))}50%{opacity:1;filter:drop-shadow(0 0 4px var(--gold-glow))}}\n@keyframes lks-flame-flicker{0%,100%{transform:scale(1) rotate(-2deg)}25%{transform:scale(1.06) rotate(1deg)}50%{transform:scale(.97) rotate(-1deg)}75%{transform:scale(1.04) rotate(2deg)}}\n.lks-ui .lks-hdr-xp .lks-hdr-glyph{display:inline-flex;animation:lks-energy-breathe 2s ease-in-out infinite}\n.lks-ui .lks-hdr-streak .lks-hdr-glyph{display:inline-flex;animation:lks-flame-flicker 1.2s ease-in-out infinite}\n@keyframes lks-answer-correct{0%{transform:scale(1)}40%{transform:scale(1.05)}100%{transform:scale(1)}}\n@keyframes lks-answer-wrong{0%,100%{transform:translateX(0)}20%,60%{transform:translateX(-3px)}40%,80%{transform:translateX(3px)}}\n.lks-ui .lks-qcard-opt.right{animation:lks-answer-correct 350ms var(--ease-spring)}\n.lks-ui .lks-qcard-opt.wrong{animation:lks-answer-wrong 320ms ease-in-out}\n@keyframes lks-confirm-enter{from{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}\n/* A7 toast enter/exit at upstream values (replaces the base sheet's simplified pair) */\n@keyframes lks-toast-in-up{from{opacity:0;transform:translateY(-12px) scale(.96)}to{opacity:1;transform:translateY(0) scale(1)}}\n@keyframes lks-toast-out-up{from{opacity:1;transform:translateY(0) scale(1)}to{opacity:0;transform:translateY(-8px) scale(.97)}}\n.lks-ui .lks-toast{animation:lks-toast-in-up 220ms var(--ease-out-back)}\n.lks-ui .lks-toast.exiting{animation:lks-toast-out-up 180ms var(--ease-out-quart) forwards}\n.lks-ui .lks-toast-close{color:var(--ink-faint)}\n.lks-ui .lks-toast-close:hover{color:var(--ink);background:rgb(var(--ink-rgb)/0.08)}\n/* search rows: complete the half-covered pair */\n.lks-ui .lks14-searchrow{border-bottom-color:var(--border)}\n.lks-ui .lks14-searchrow-title{color:var(--ink-strong)}\n.lks-ui .lks14-searchrow-course{color:var(--ink-faint)}\n\n/* ══════════ B-track (P10b): the upstream rail frame + column skeletons ══════════ */\n/* the floating topbar (absolute over the scrolling panes) */\n.lks-ui .lks14-railtop{position:absolute;top:0;left:0;right:0;z-index:40;padding:8px;pointer-events:none}\n.lks-ui .lks-railtabs,.lks-ui .lks14-railhead{pointer-events:auto}\n.lks-ui .lks-railtabs{display:flex;padding:4px;border-radius:10px;gap:4px;margin-bottom:8px;\n background:rgb(var(--surface-rail-rgb)/0.55);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px)}\n.lks-ui .lks-railtab{flex:1;display:flex;align-items:center;justify-content:center;padding:6px 0;border-radius:8px;\n font-size:12.5px;font-weight:700;color:rgb(255 255 255/0.5)}\n.lks-ui .lks-railtab.on{background:rgb(var(--brand-rgb)/0.2);color:var(--brand)}\n/* the glass title card: title row / mastery row / entry pills row */\n.lks-ui .lks14-railhead{display:flex;flex-direction:column;gap:6px}\n.lks-ui .lks14-railcard-row{display:flex;align-items:center;gap:8px}\n.lks-ui .lks14-railpct{flex:none;font-size:12px;font-weight:800;color:#fff;font-variant-numeric:tabular-nums;text-shadow:0 1px 3px rgba(0,0,0,0.6)}\n.lks-ui .lks14-masterybar{flex:1}\n.lks-ui .lks-railpill{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border-radius:999px;\n background:rgb(255 255 255/0.05);box-shadow:inset 0 0 0 1px rgb(255 255 255/0.1);color:rgb(var(--ink-rgb)/0.6);font-size:11px;font-weight:700}\n.lks-ui .lks-railpill:hover{background:rgb(255 255 255/0.1)}\n.lks-ui .lks-railpill.due{background:rgb(var(--review-rgb)/0.2);box-shadow:inset 0 0 0 1px rgb(var(--review-rgb)/0.3);color:var(--review)}\n.lks-ui .lks-railpill-n{font-weight:800;font-variant-numeric:tabular-nums}\n/* the sliding two-pane body */\n.lks-ui .lks14-railbody{position:absolute;inset:0;overflow:hidden;z-index:10}\n.lks-ui .lks14-railtrack{display:flex;height:100%;width:200%;transition:transform .3s var(--ease-out-expo)}\n.lks-ui .lks14-railpane{width:50%;height:100%;position:relative}\n.lks-ui .lks14-railscroll{height:100%;overflow-y:auto;overflow-x:hidden;padding:196px 8px 16px}\n.lks-ui .lks-mapsec-list{display:flex;flex-direction:column;gap:24px}\n.lks-ui .lks-mapsec{padding:12px 8px 0}\n.lks-ui .lks14-railpane-import{height:100%;overflow-y:auto;padding:64px 12px 12px}\n.lks-ui .lks14-raillist{display:flex;flex-direction:column;gap:8px;margin-bottom:10px}\n.lks-ui .lks14-railcourse{text-align:left;padding:10px 12px;border-radius:12px;background:rgb(255 255 255/0.05);display:flex;flex-direction:column;gap:2px}\n.lks-ui .lks14-railcourse:hover{background:rgb(255 255 255/0.1)}\n.lks-ui .lks14-railcourse.on{background:rgb(var(--brand-rgb)/0.12);box-shadow:inset 0 0 0 1px rgb(var(--brand-rgb)/0.4)}\n.lks-ui .lks14-railcourse-title{font-size:13px;font-weight:700;color:var(--ink-strong);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.lks-ui .lks14-railcourse.on .lks14-railcourse-title{color:var(--brand)}\n.lks-ui .lks14-railcourse-sub{font-size:10.5px;color:rgb(var(--ink-rgb)/0.4)}\n.lks-ui .lks14-raildemo{margin-bottom:10px}\n/* the full-rail overlays (search / review) */\n.lks-ui .lks-railoverlay{position:absolute;inset:0;z-index:50;display:flex;flex-direction:column;gap:8px;padding:12px;overflow-y:auto;\n background:rgb(var(--surface-rail-rgb)/0.92);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px)}\n.lks-ui .lks-railoverlay-head{display:flex;gap:8px;align-items:center}\n.lks-ui .lks-railoverlay-head .lks14-search{flex:1}\n/* chat: thin current-lesson row (ThreadSwitcher empty-state voice) */\n.lks-ui .lks14-lessonrow{flex:none;padding:6px 14px;font-size:12px;font-weight:700;color:var(--ink-muted);opacity:.75}\n/* composer: the soul pills ride the capsule's first row */\n.lks-ui .lks14-soulrow{display:flex;align-items:center;gap:6px;padding:0 2px 6px}\n.lks-ui .lks14-soullabel{flex:none;font-size:11px;color:var(--ink-faint)}\n/* B6: assistant text is full-width prose (no card); user bubbles right-align */\n.lks-ui .lks14-msg-assistant{background:none;border:none;box-shadow:none;border-radius:0;padding:0;max-width:80ch;font-size:.9375rem}\n.lks-ui .lks14-msg-assistant.streaming{border:none;opacity:.85}\n.lks-ui .lks14-msg-user{align-self:flex-end;max-width:85%}\n/* B8: the empty state is a centered card with a 3D CTA */\n.lks-ui .lks14-emptycard{margin:auto;display:flex;flex-direction:column;align-items:center;gap:10px;text-align:center;padding:24px;max-width:320px}\n.lks-ui .lks14-emptycard-title{font-size:1rem;font-weight:800;color:var(--ink-strong)}\n.lks-ui .lks14-emptycard-hint{font-size:.825rem;color:var(--ink-muted);line-height:1.6}\n/* B5: notebook tab capsule + 960px reading column */\n.lks-ui .lks14-notebody{margin:0 auto;max-width:960px;width:100%;padding:12px 20px 24px;box-sizing:border-box}\n.lks-ui .lks14-viewtabs{background:rgb(var(--ink-rgb)/0.08);border-radius:999px;padding:3px;align-self:flex-start}\n.lks-ui .lks14-readbar{position:sticky;top:0;z-index:20;background:var(--surface-2);border-radius:10px}\n`\n","/**\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 */\nimport { UPSTREAM_CSS } from './upstream-theme.ts'\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,.lks14{--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{position:relative;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-righthalf{flex:1;min-width:0;display:flex;flex-direction:column;min-height:0}\n.lks14-row{flex:1;min-height:0;display:flex}\n.lks14-col{display:flex;flex-direction:column;min-width:0;min-height:0;overflow:hidden}\n\n/* 左 rail: course picker, tree, review, import */\n.lks14-rail{flex:0 0 300px;border-right:none;padding:0;overflow:hidden;position:relative}\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-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:0 0 auto;width:clamp(480px,45%,800px);min-width:0;border-right:none}\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:20px 20px 24px;display:flex;flex-direction:column;gap:24px}\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:nowrap;gap:6px;padding:0 20px 6px;overflow-x:auto}\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:1 1 auto;min-width:440px;padding:0;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:0 0 12px}\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\n/* toast capsules (P6, upstream Toast port): top-center under the panel head,\n container inert to pointers, items auto; exit is an animation handshake —\n reduced-motion keeps a .01ms duration so the end event still fires. */\n.lks-toasts{position:absolute;top:44px;left:50%;transform:translateX(-50%);z-index:40;display:flex;flex-direction:column;align-items:center;gap:6px;pointer-events:none;max-width:min(440px,90%)}\n.lks-toast{pointer-events:auto;display:flex;align-items:center;gap:8px;max-width:100%;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:6px 8px 6px 14px;font-size:13px;line-height:1.4;box-shadow:0 6px 24px rgba(0,0,0,.14);animation:lks-toast-in .18s ease}\n.lks-toast.exiting{animation:lks-toast-out .18s ease forwards}\n.lks-toast .lks-toast-text{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.lks-toast .lks-toast-glyph{flex:none}\n.lks-toast .lks-toast-glyph.ok{color:var(--lks-ok-ink)}\n.lks-toast .lks-toast-glyph.warn{color:var(--lks-warn-ink)}\n.lks-toast .lks-toast-glyph.err{color:var(--lks-err-ink)}\n.lks-toast .lks-toast-glyph.info{color:var(--dsw-alias-state-business-primary)}\n.lks-toast-action{border:none;background:none;color:var(--dsw-alias-state-business-primary);font-weight:600;font-size:13px;cursor:pointer;padding:4px 8px;border-radius:999px;flex:none}\n.lks-toast-action:hover{background:var(--dsw-alias-interactive-bg-hover)}\n.lks-toast-action:focus-visible,.lks-toast-close:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-state-business-primary);outline:none}\n.lks-toast-close{border:none;background:none;color:var(--dsw-alias-label-tertiary);cursor:pointer;width:22px;height:22px;display:inline-flex;align-items:center;justify-content:center;border-radius:50%;font-size:15px;line-height:1;padding:0;flex:none}\n.lks-toast-close:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}\n@keyframes lks-toast-in{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}\n@keyframes lks-toast-out{to{opacity:0;transform:translateY(-6px)}}\n@media (prefers-reduced-motion:reduce){.lks-toast{animation-duration:.01ms}}\n\n\n\n\n\n\n\n\n/* the companion creature (P7): bottom-right of the panel; gentle bob idle,\n bounce while talking, pop on celebration; reduced-motion = static (a11y). */\n.lks-companion{position:absolute;right:18px;bottom:14px;z-index:20;width:56px;height:56px;cursor:pointer;filter:drop-shadow(0 4px 10px rgba(0,0,0,.18));animation:lks-comp-bob 3.2s ease-in-out infinite}\n.lks-companion:focus-visible{outline:none;filter:drop-shadow(0 0 0 2px var(--dsw-alias-state-business-primary)) drop-shadow(0 4px 10px rgba(0,0,0,.18))}\n.lks-companion.mood-talking{animation:lks-comp-talk .6s ease-in-out infinite}\n.lks-companion.mood-celebrating{animation:lks-comp-pop .5s cubic-bezier(.34,1.56,.64,1) 3}\n.lks-companion.mood-encouraging{animation:lks-comp-nudge 1.2s ease-in-out 2}\n@keyframes lks-comp-bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-4px)}}\n@keyframes lks-comp-talk{0%,100%{transform:translateY(0) scale(1)}50%{transform:translateY(-7px) scale(1.06)}}\n@keyframes lks-comp-pop{0%{transform:scale(1)}40%{transform:scale(1.25) rotate(-8deg)}100%{transform:scale(1)}}\n@keyframes lks-comp-nudge{0%,100%{transform:translateX(0)}25%{transform:translateX(-5px) rotate(-4deg)}75%{transform:translateX(5px) rotate(4deg)}}\n@media (prefers-reduced-motion:reduce){.lks-companion{animation:none}}\n\n/* P5: proposal banner (ConfirmCard semantics) + thinking row + narrow switch */\n.lks-propbanner{flex:none;display:flex;align-items:center;gap:8px;margin:8px 12px 0;padding:8px 12px;border-radius:10px;background:var(--dsw-alias-state-business-tertiary);border:1px solid var(--dsw-alias-border-l2);font-size:12.5px;color:var(--dsw-alias-label-primary-bluish)}\n.lks-propbanner .lks-propbanner-why{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.lks14-thinking{display:flex;align-items:center;gap:8px;font-size:12.5px;color:var(--dsw-alias-label-secondary);padding:2px 2px}\n.lks14-switch{display:none;flex-direction:row;gap:2px;align-self:center;margin:8px 0 0;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:3px}\n.lks14-switch-btn{border-radius:999px;padding:4px 16px;font-size:13px;color:var(--dsw-alias-label-secondary);background:transparent}\n.lks14-switch-btn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}\n.lks14-switch-btn.on{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 86%,#000);color:#fff;font-weight:600}\n@container (max-width: 900px){\n .lks14-switch{display:flex}\n .lks14-body{flex-direction:column}\n .lks14-rail{flex:none;max-height:32vh;border-right:none;border-bottom:1px solid var(--dsw-alias-border-l1)}\n .lks14-chat{min-width:0;width:auto;flex:1 1 auto}\n .lks14-note{flex:1 1 auto;min-height:0}\n .lks14-body[data-pane='rail'] .lks14-righthalf{display:none}\n .lks14-body[data-pane='chat'] .lks14-rail,.lks14-body[data-pane='chat'] .lks14-note,\n .lks14-body[data-pane='note'] .lks14-rail,.lks14-body[data-pane='note'] .lks14-chat{display:none}\n .lks14-body[data-pane='rail'] .lks14-rail{display:flex;max-height:none;flex:1 1 auto}\n .lks14-body[data-pane='note'] .lks14-note{display:flex}\n}\n\n/* rail search results panel (P4, upstream CourseSearchPanel) */\n.lks14-searchpanel{margin:4px 0 6px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;background:var(--dsw-alias-bg-layer-1);overflow:hidden}\n.lks14-searchrow{display:flex;flex-direction:column;align-items:flex-start;gap:1px;width:100%;text-align:left;font:inherit;padding:6px 10px;cursor:pointer;border-bottom:1px solid var(--dsw-alias-border-l1)}\n.lks14-searchrow:last-child{border-bottom:none}\n.lks14-searchrow:hover{background:var(--dsw-alias-interactive-bg-hover)}\n.lks14-searchrow-title{font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-primary)}\n.lks14-searchrow-course{font-size:10.5px;color:var(--dsw-alias-label-tertiary)}\n.lks14-searchrow-snip{font-size:11px;color:var(--dsw-alias-label-secondary);line-height:1.5;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}\n\n/* review self-rating card (P3, upstream SelfRatingCard: three Memrise levels) */\n.lks-ratecard{flex:none;display:flex;flex-direction:column;gap:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-state-business-tertiary);padding:10px 12px;margin-bottom:8px}\n.lks-ratecard-title{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary-bluish)}\n.lks-ratecard-opts{display:flex;gap:6px}\n.lks-ratecard-opt{flex:1;font:inherit;font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l1);border-radius:9px;padding:7px 10px;cursor:pointer}\n.lks-ratecard-opt:hover{border-color:var(--dsw-alias-state-business-primary)}\n.lks-ratecard-opt.again:hover{border-color:var(--dsw-alias-state-error-primary)}\n.lks-ratecard-opt.best{background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 86%,#000);color:#fff;border-color:transparent}\n.lks-ratecard-opt.best:hover{filter:brightness(1.1)}\n.lks-ratecard-opt:disabled{opacity:.6;cursor:default}\n.lks14-dueitem{font:inherit;text-align:left}\n\n/* selection-to-note (P2, upstream selection flow): the popover floats over\n the prose; persisted highlights are soft amber marks. */\n.lks14-prosewrap{position:relative}\n.lks-quote-btn{position:absolute;z-index:12;display:flex;gap:2px;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);border-radius:10px;box-shadow:0 6px 24px rgba(0,0,0,.16);transform:translateX(-50%)}\n.lks-quote-btn button{border:none;background:none;color:var(--dsw-alias-label-primary);font:inherit;font-size:12.5px;font-weight:600;padding:6px 10px;cursor:pointer;border-radius:10px}\n.lks-quote-btn button:hover{background:var(--dsw-alias-interactive-bg-hover)}\n.lks-quote-btn button + button{border-left:1px solid var(--dsw-alias-border-l1)}\nmark.lks-hl{background:var(--dsw-alias-state-warn-tertiary);color:inherit;border-bottom:2px solid var(--dsw-alias-state-warn-primary);padding:0 1px}\n\n/* artifact cards (P1b, upstream artifacts port): chat column + notebook zones */\n.lks-acard{flex:none;margin:2px 12px 8px;border:1px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:10px 12px;display:flex;flex-direction:column;gap:8px;overflow:hidden}\n.lks-acard-head{display:flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-secondary)}\n.lks-acard-expand{margin-left:auto;border:none;background:none;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:14px;padding:2px 6px;border-radius:6px}\n.lks-acard-expand:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2)}\n.lks-acard-table{border-collapse:collapse;display:block;max-width:100%;overflow-x:auto}\n.lks-acard-table th,.lks-acard-table td{border:1px solid var(--dsw-alias-border-l2);padding:4px 9px;font-size:12.5px;text-align:left}\n.lks-acard-table th{background:var(--dsw-alias-bg-layer-2)}\n.lks-acard-code{background:var(--dsw-alias-markdown-code-block);border-radius:8px;padding:8px 0;overflow-x:auto;font-family:var(--dsw-font-markdown-code-block-small);font-size:12px;line-height:1.6;margin:0}\n.lks-acard-line{display:block;white-space:pre}\n.lks-acard-line i{display:inline-block;width:2.2em;color:var(--dsw-alias-label-tertiary);font-style:normal;text-align:right;padding-right:8px;user-select:none}\n.lks-acard-notes{display:flex;flex-direction:column;gap:5px}\n.lks-acard-note{font-size:12.5px;line-height:1.6;color:var(--dsw-alias-label-secondary);border-left:2px solid var(--dsw-alias-state-business-primary);padding-left:8px}\n.lks-acard-note b{color:var(--dsw-alias-label-primary);font-weight:600;margin-right:4px}\n.lks-acard-diagram{overflow-x:auto;text-align:center}\n.lks-acard-diagram svg{max-width:100%;height:auto}\n.lks-acard-modal{position:fixed;inset:0;z-index:90;background:rgba(0,0,0,.5);display:flex;align-items:center;justify-content:center;padding:32px}\n.lks-acard-modal-body{background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);border-radius:14px;max-width:900px;max-height:100%;overflow:auto;padding:18px;display:flex;flex-direction:column;gap:10px}\n.lks-acard-modal-title{display:flex;align-items:center;font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary)}\n.lks-acard-guess-prompt{font-size:13.5px;line-height:1.6;color:var(--dsw-alias-label-primary)}\n.lks-acard-guess-opts{display:flex;gap:8px}\n.lks-acard-guess-opt{flex:1;font:inherit;font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;padding:10px 12px;cursor:pointer}\n.lks-acard-guess-opt:not(:disabled):hover{border-color:var(--dsw-alias-state-business-primary)}\n.lks-acard-guess-opt.picked{border-color:var(--dsw-alias-state-business-primary);background:var(--dsw-alias-state-business-tertiary)}\n.lks-acard-guess-wait{font-size:12px;color:var(--dsw-alias-label-tertiary)}\n.lks-acard-stage{margin-top:14px;padding-top:10px;border-top:1px solid var(--dsw-alias-border-l1)}\n.lks-acard-stage .lks-acard{margin:0}\n.lks14-zone .lks-acard{margin:0 0 8px}\n/* notebook tab badge (unseen artifacts) */\n.lks-viewtab-badge{display:inline-flex;align-items:center;justify-content:center;min-width:16px;height:16px;border-radius:8px;background:var(--dsw-alias-state-business-primary);color:#fff;font-size:10px;font-weight:700;margin-left:5px;padding:0 4px}\n\n/* practice card (P1, upstream QuizArtifact port): chat column, below the stream */\n.lks-qcard{flex:none;margin:2px 12px 8px;border:1px solid var(--dsw-alias-border-l1);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:10px 12px;display:flex;flex-direction:column;gap:8px}\n.lks-qcard-head{display:flex;align-items:center;gap:6px;font-size:12.5px;font-weight:600;color:var(--dsw-alias-label-secondary)}\n.lks-qcard-count{margin-left:auto;font-weight:400;color:var(--dsw-alias-label-tertiary)}\n.lks-qcard-q{display:flex;flex-direction:column;gap:6px}\n.lks-qcard-prompt{font-size:13.5px;line-height:1.55;color:var(--dsw-alias-label-primary)}\n.lks-qcard-opts{display:flex;flex-direction:column;gap:4px}\n.lks-qcard-opt{text-align:left;font:inherit;font-size:13px;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:9px;padding:6px 10px;cursor:pointer}\n.lks-qcard-opt:not(:disabled):hover{border-color:var(--dsw-alias-state-business-primary)}\n.lks-qcard-opt:disabled{cursor:default}\n.lks-qcard-opt.right{border-color:var(--dsw-alias-state-success-primary);color:var(--lks-ok-ink)}\n.lks-qcard-opt.wrong{border-color:var(--dsw-alias-state-error-primary);color:var(--lks-err-ink)}\n.lks-qcard-opt.dim{opacity:.75}\n.lks-qcard-expl{font-size:12.5px;line-height:1.6;color:var(--dsw-alias-label-secondary);border-left:2px solid var(--dsw-alias-state-business-primary);padding-left:8px}\n.lks-qcard-next{margin-top:2px;border:none;background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 86%,#000);color:#fff;font:inherit;font-size:12.5px;font-weight:600;border-radius:8px;padding:5px 14px;cursor:pointer}\n.lks-qcard-next:hover{filter:brightness(1.1)}\n.lks-qcard-review .lks-qcard-q.review{padding:6px 0;border-bottom:1px dashed var(--dsw-alias-border-l1)}\n.lks-qcard-review .lks-qcard-q.review.wrong .lks-qcard-prompt{color:var(--lks-err-ink)}\n.lks-qcard-ans{font-size:12.5px;color:var(--dsw-alias-label-secondary)}\n.lks-qcard-actions{display:flex;flex-wrap:wrap;gap:6px}\n.lks-qcard-action{display:inline-flex;align-items:center;gap:5px;font:inherit;font-size:12.5px;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:999px;padding:4px 12px;cursor:pointer}\n.lks-qcard-action:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3)}\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 + UPSTREAM_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/** IconArrowUpFill16 — the 3D send button glyph (upstream: lucide Send direction) */\nexport function IconArrowUpFill16({ 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 2.6 13.2 8h-3v5.4H5.8V8h-3L8 2.6Z', fill: 'currentColor' }))\n}\n\n/** IconCloseFill16 — toast/modal close */\nexport function IconCloseFill16({ 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: 'M3.4 2 2 3.4 6.6 8 2 12.6 3.4 14 8 9.4l4.6 4.6 1.4-1.4L9.4 8 14 3.4 12.6 2 8 6.6 3.4 2Z', fill: 'currentColor' }))\n}\n\n/** IconMaximizeOutline16 — artifact expand (upstream: lucide Maximize2 corners) */\nexport function IconMaximizeOutline16({ 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.5 2H14v4.5M6.5 14H2V9.5M14 2 9.2 6.8M2 14l4.8-4.8', stroke: 'currentColor', 'stroke-width': 1.6, 'stroke-linecap': 'round', 'stroke-linejoin': 'round' }))\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 artifacts: ReadonlyArray<{ id: string; artifactType: string; title: string; data: Record<string, unknown> }>\n readonly due: boolean\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 /** Save a learner selection as a record-zone note (the highlight anchor is the quote). */\n async addUserNote(lessonId: string, quote: string): Promise<void> {\n await fetchJson('/lookatstudy/api/note/user', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ lessonId, quote }),\n })\n this.refresh()\n }\n\n /** Record one SM-2 self-rating (1 again / 4 remembered / 5 mastered). */\n async recordReview(lessonId: string, quality: 1 | 4 | 5): Promise<void> {\n await fetchJson('/lookatstudy/api/review', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ lessonId, quality }),\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 addUserNote: (lessonId: string, quote: string) => Promise<void>\n recordReview: (lessonId: string, quality: 1 | 4 | 5) => 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 addUserNote: studyStore.addUserNote.bind(studyStore),\n recordReview: studyStore.recordReview.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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;')\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*&gt;\\s?/.test(line)) {\n flushParagraph(paragraph)\n paragraph = []\n const quote: string[] = []\n while (i < lines.length && /^\\s*&gt;\\s?/.test(lines[i]!)) {\n quote.push(lines[i]!.replace(/^\\s*&gt;\\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.chat': '对话',\n 'pane.note': '黑板',\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 'quiz.card.title': '练习',\n 'quiz.progress': '{cur}/{total}',\n 'quiz.next': '下一题',\n 'quiz.finish': '看成绩单',\n 'quiz.score': '成绩:{correct}/{total}',\n 'quiz.right': '答对了',\n 'quiz.wrong': '答错了',\n 'quiz.youChose': '你选了',\n 'quiz.answer': '正确答案',\n 'quiz.hook': '练习卡完成:{correct}/{total} 正确。请简短点评,针对错题讲讲思路。',\n 'quiz.action.explain-wrong': '讲讲错题',\n 'quiz.action.retry': '再来一组',\n 'quiz.action.go-deeper': '深入原理',\n 'quiz.action.mark-mastered': '标记我掌握了',\n 'quiz.action.markMastered.hint': '会让导师出综合题检验,通过即标记掌握',\n 'quiz.action.next-topic': '下一个知识点',\n 'artifact.lines': '第 {from}-{to} 行:',\n 'artifact.expand': '放大查看',\n 'artifact.guess.title': '猜一猜',\n 'artifact.guess.pick': '我选「{label}」',\n 'artifact.guess.wait': '已记下你的直觉——导师下回合揭晓,看看你想得对不对',\n 'artifact.sedimented': '有新的学习产物沉淀进笔记',\n 'zone.artifacts': 'AI 产物',\n 'note.quote.ask': '提问这段',\n 'note.quote.save': '加到笔记',\n 'note.quote.template': '请讲解这段:「{text}」',\n 'note.saved': '已存入记录区笔记',\n 'review.rate.title': '复习自评——这课现在记得多牢?',\n 'review.again': '再来一次',\n 'review.remembered': '记住了',\n 'review.mastered': '完全掌握',\n 'review.done.good': '已安排下次复习',\n 'review.done.again': '明天再来一遍',\n 'review.jump': '点开这一课去复习',\n 'review.nudge': '有 {n} 课到了复习时间,先回来练一遍?',\n 'review.nudge.go': '去复习',\n 'rail.search.jump': '打开这一课',\n 'proposal.banner': '《{lesson}》可以提前毕业了',\n 'proposal.accept': '接受',\n 'proposal.decline': '再练练',\n 'proposal.accept.msg': '我接受《{lesson}》的掌握度提议,帮我标记为掌握。',\n 'proposal.decline.msg': '《{lesson}》我先再练练,暂不接受提前毕业。',\n 'tutor.thinking': '导师思考中…',\n 'header.xp': '经验值 {xp}',\n 'header.streak': '连续学习天数',\n 'header.level': '等级',\n 'map.node.locked': '锁定 — 先完成前面的课时',\n 'map.node.due': '到了复习时间',\n 'map.exam.locked': '考试未解锁 — 本章节全部课时掌握度 ≥50% 后开放',\n 'map.tab.map': '地图',\n 'map.tab.import': '导入',\n 'map.search.label': '搜索',\n 'map.review.label': '复习',\n 'map.overlay.close': '关闭',\n 'chat.start': '开始学习',\n 'chat.start.hint': '从当前课时的第一个开场动作开始',\n 'prompt.start': '你好,我们开始学这一课吧:先给我一个两三句话的导览,再从第一个知识点讲起。',\n 'mode.label': '风格:',\n 'companion.poke': '戳一戳伴学伙伴',\n 'companion.poked': '伴学伙伴向你眨了眨眼',\n 'companion.form.ember': '小焰',\n 'companion.form.frost': '霜绒',\n 'companion.form.moss': '苔芽',\n 'companion.form.star': '星尘',\n 'companion.form.ink': '墨墨',\n 'settings.companion': '伴学伙伴',\n 'settings.companion.hint': '选择学习面板右下角的伴学生物形态。',\n 'quiz.msg.explain-wrong': '我刚才有题答错了,帮我讲讲为什么错、正确的思路是什么。',\n 'quiz.msg.retry': '再来一组类似的题巩固一下。',\n 'quiz.msg.go-deeper': '这组我答得不错,帮我深入讲讲背后的原理和容易混淆的地方。',\n 'quiz.msg.mark-mastered': '这课我觉得掌握了,帮我确认一下——出个综合题检验,通过了就标记为掌握。',\n 'quiz.msg.next-topic': '进入下一个知识点。',\n 'toast.region': '通知',\n 'toast.close': '关闭',\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.none': '暂时没有到期的复习,保持节奏!',\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.chat': 'Chat',\n 'pane.note': '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 'quiz.card.title': 'Practice',\n 'quiz.progress': '{cur}/{total}',\n 'quiz.next': 'Next',\n 'quiz.finish': 'See score',\n 'quiz.score': 'Score: {correct}/{total}',\n 'quiz.right': 'Correct',\n 'quiz.wrong': 'Wrong',\n 'quiz.youChose': 'You chose',\n 'quiz.answer': 'Answer',\n 'quiz.hook': 'Practice card finished: {correct}/{total} correct. Please comment briefly and walk through the wrong ones.',\n 'quiz.action.explain-wrong': 'Explain my wrong answers',\n 'quiz.action.retry': 'Another set',\n 'quiz.action.go-deeper': 'Go deeper',\n 'quiz.action.mark-mastered': 'Mark me as mastered',\n 'quiz.action.markMastered.hint': 'Asks the tutor to verify with a final question, then marks mastery',\n 'quiz.action.next-topic': 'Next topic',\n 'artifact.lines': 'Lines {from}-{to}: ',\n 'artifact.expand': 'Expand',\n 'artifact.guess.title': 'Take a guess',\n 'artifact.guess.pick': 'I pick \"{label}\"',\n 'artifact.guess.wait': 'Your instinct is noted — the tutor reveals next turn',\n 'artifact.sedimented': 'A new artifact settled into the notebook',\n 'zone.artifacts': 'AI artifacts',\n 'note.quote.ask': 'Ask about this',\n 'note.quote.save': 'Add note',\n 'note.quote.template': 'Please explain this passage: \"{text}\"',\n 'note.saved': 'Saved to the record zone',\n 'review.rate.title': 'Review self-check — how well do you remember?',\n 'review.again': 'Again',\n 'review.remembered': 'Remembered',\n 'review.mastered': 'Mastered',\n 'review.done.good': 'Next review scheduled',\n 'review.done.again': 'Back again tomorrow',\n 'review.jump': 'Open this lesson to review',\n 'review.nudge': '{n} lessons are due for review — come practice first?',\n 'review.nudge.go': 'Review now',\n 'rail.search.jump': 'Open this lesson',\n 'proposal.banner': '{lesson} is ready to graduate early',\n 'proposal.accept': 'Accept',\n 'proposal.decline': 'Keep practicing',\n 'proposal.accept.msg': 'I accept the mastery proposal for \"{lesson}\" — mark it mastered.',\n 'proposal.decline.msg': 'I want more practice on \"{lesson}\" before graduating.',\n 'tutor.thinking': 'The tutor is thinking…',\n 'header.xp': '{xp} XP',\n 'header.streak': 'Day streak',\n 'header.level': 'Level',\n 'map.node.locked': 'Locked — finish the previous lessons first',\n 'map.node.due': 'Due for review',\n 'map.exam.locked': 'Exam locked — every lesson in this section needs ≥50% mastery',\n 'map.tab.map': 'Map',\n 'map.tab.import': 'Import',\n 'map.search.label': 'Search',\n 'map.review.label': 'Review',\n 'map.overlay.close': 'Close',\n 'chat.start': 'Start learning',\n 'chat.start.hint': 'Begin with this lesson\\u2019s first starter move',\n 'prompt.start': 'Hi — let\\u2019s start this lesson: give me a two-sentence tour, then begin with the first concept.',\n 'mode.label': 'Style: ',\n 'companion.poke': 'Poke the companion',\n 'companion.poked': 'The companion winks at you',\n 'companion.form.ember': 'Ember',\n 'companion.form.frost': 'Frost',\n 'companion.form.moss': 'Moss',\n 'companion.form.star': 'Stardust',\n 'companion.form.ink': 'Ink',\n 'settings.companion': 'Companion',\n 'settings.companion.hint': 'Choose the companion creature in the study panel corner.',\n 'quiz.msg.explain-wrong': 'I got some questions wrong — walk me through why and the right line of thinking.',\n 'quiz.msg.retry': 'Give me another similar set to consolidate.',\n 'quiz.msg.go-deeper': 'I did well on this set — go deeper into the principles and common confusions.',\n 'quiz.msg.mark-mastered': 'I feel I have mastered this lesson — verify me with a final question and mark it mastered if I pass.',\n 'quiz.msg.next-topic': 'Move on to the next topic.',\n 'toast.region': 'Notifications',\n 'toast.close': 'Close',\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.none': 'Nothing due right now — keep the streak!',\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 * The panel's toast notifications — the upstream Toast.tsx port (0.15.0 P6):\n * severity capsules with per-severity durations (error outlasts success —\n * the learner needs time to read it), an optional action, dismiss, an\n * exit-animation handshake between store and view, and an injected scheduler\n * so all timing logic runs under plain node:test. One subscribable store per\n * panel; the view lives in panel.tsx (StudyToastStack).\n * @module dsh-plugin-lookatstudy/client/toast\n */\n\nexport type ToastSeverity = 'default' | 'success' | 'error' | 'warning' | 'info'\n\nexport interface ToastAction {\n readonly label: string\n readonly onClick?: () => void\n}\n\nexport interface ToastItem {\n readonly id: number\n readonly message: string\n readonly severity: ToastSeverity\n readonly duration: number\n readonly action?: ToastAction\n /** The exit animation is playing; the view calls finish() on its end. */\n exiting: boolean\n}\n\nexport interface ShowToastOpts {\n severity?: ToastSeverity\n duration?: number\n action?: ToastAction\n}\n\n/** Per-severity defaults (upstream Toast.tsx): error 6s, warning 5s, rest 4s. */\nexport const SEVERITY_DURATION: Readonly<Record<ToastSeverity, number>> = {\n default: 4000,\n success: 4000,\n info: 4000,\n warning: 5000,\n error: 6000,\n}\n\n/** Schedule a callback; returns its canceller. Tests inject a manual one. */\nexport type ToastScheduler = (fn: () => void, ms: number) => () => void\n\nconst browserScheduler: ToastScheduler = (fn, ms) => {\n const handle = window.setTimeout(fn, ms)\n return () => { window.clearTimeout(handle) }\n}\n\nexport class ToastStore {\n private items: ToastItem[] = []\n private nextId = 0\n private readonly listeners = new Set<() => void>()\n private readonly cancellers = new Map<number, () => void>()\n private readonly scheduler: ToastScheduler\n\n constructor(scheduler: ToastScheduler = browserScheduler) {\n this.scheduler = scheduler\n }\n\n getSnapshot(): readonly ToastItem[] { return this.items }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n private emit(): void {\n for (const listener of this.listeners) listener()\n }\n\n /** Enqueue one toast; its auto-exit is scheduled at its duration. */\n show(message: string, opts?: ShowToastOpts): number {\n const severity = opts?.severity ?? 'default'\n const id = ++this.nextId\n const item: ToastItem = {\n id,\n message,\n severity,\n duration: opts?.duration ?? SEVERITY_DURATION[severity],\n action: opts?.action,\n exiting: false,\n }\n this.items = [...this.items, item]\n this.cancellers.set(id, this.scheduler(() => this.startExit(id), item.duration))\n this.emit()\n return id\n }\n\n /** Begin the exit (dismiss click or the duration timer); cancels the timer. */\n startExit(id: number): void {\n const cancel = this.cancellers.get(id)\n if (cancel !== undefined) {\n cancel()\n this.cancellers.delete(id)\n }\n this.items = this.items.map(t => (t.id === id ? { ...t, exiting: true } : t))\n this.emit()\n }\n\n /** Actually remove — called by the view when the exit animation ends. */\n finish(id: number): void {\n this.items = this.items.filter(t => t.id !== id)\n this.cancellers.delete(id)\n this.emit()\n }\n\n /** Drop everything now (the panel unmounted — nothing outlives the panel). */\n clear(): void {\n for (const cancel of this.cancellers.values()) cancel()\n this.cancellers.clear()\n this.items = []\n this.emit()\n }\n}\n\n/** The panel-wide singleton stack; consumers call showStudyToast. */\nexport const toastStore = new ToastStore()\n\nexport function showStudyToast(message: string, opts?: ShowToastOpts): number {\n return toastStore.show(message, opts)\n}\n","/**\n * The interactive practice card (0.15.0 P1, upstream QuizArtifact port):\n * local judging, per-artifact progress in localStorage, the completion score,\n * and the post-quiz action set — the deterministic half of \"what next after\n * answering\" (never fewer than two exits; upstream post-quiz-actions.ts).\n * Pure logic + one React card; thresholds mirror state.ts (0.9 graduation /\n * 0.85 near — the client bundle cannot import the host-side state module).\n * @module dsh-plugin-lookatstudy/client/quizcard\n */\n\nimport { createElement, useEffect, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { IconBoltFill16, IconBookFill16, IconCrownFill16, IconRefreshOutline16, IconStarFill16 } from './icons.tsx'\nimport { tr } from './locale.ts'\n\n/** Mirror of state.ts MASTERED_THRESHOLD (graduation). */\nexport const MASTERED = 0.9\n/** Mirror of state.ts NEAR_MASTERED_THRESHOLD (proposal zone). */\nexport const NEAR_MASTERED = 0.85\n\nexport type PostQuizActionId = 'explain-wrong' | 'retry' | 'go-deeper' | 'mark-mastered' | 'next-topic'\n\nexport interface PostQuizAction {\n id: PostQuizActionId\n /** true = the action asks the tutor to test/graduate (mark-mastered only). */\n advancesMastery: boolean\n}\n\n/**\n * The exits after one finished card — never fewer than two (the anti-dead-end\n * promise). Wrong answers → patch the hole first; perfect + near-mastered →\n * offer the mastery exit; perfect + already graduated → move on.\n * Pure port of upstream getPostQuizActions.\n */\nexport function getPostQuizActions(score: { correct: number; total: number }, mastery: number | null): PostQuizAction[] {\n const { correct, total } = score\n const allCorrect = total > 0 && correct === total\n const hasWrong = total > 0 && correct < total\n const alreadyMastered = mastery !== null && mastery >= MASTERED\n const nearMastered = mastery !== null && mastery >= NEAR_MASTERED\n if (hasWrong) return [{ id: 'explain-wrong', advancesMastery: false }, { id: 'retry', advancesMastery: false }]\n if (allCorrect && alreadyMastered) return [{ id: 'next-topic', advancesMastery: false }, { id: 'go-deeper', advancesMastery: false }]\n if (allCorrect && nearMastered) return [{ id: 'mark-mastered', advancesMastery: true }, { id: 'go-deeper', advancesMastery: false }]\n if (allCorrect) return [{ id: 'go-deeper', advancesMastery: false }, { id: 'retry', advancesMastery: false }]\n return [{ id: 'explain-wrong', advancesMastery: false }, { id: 'retry', advancesMastery: false }]\n}\n\n/** The quiz payload shape (structural slice of the sanitized artifact data). */\nexport interface QuizData {\n readonly questions: ReadonlyArray<{ readonly prompt: string; readonly options: readonly string[]; readonly answer: number; readonly explanation: string }>\n readonly title?: string\n readonly warnings?: readonly string[]\n}\n\n/** Progress under localStorage: chosen option per question (-1 unanswered). */\nexport interface QuizProgress {\n readonly answers: number[]\n readonly done: boolean\n}\n\n/** localStorage key (upstream quizProgressKey semantics: per artifact). */\nexport function quizProgressKey(lessonId: string, artifactId: string): string {\n return `dsh-plugin-lookatstudy:quiz:${lessonId}:${artifactId}`\n}\n\nexport function loadQuizProgress(lessonId: string, artifactId: string, questionCount: number, storage: Pick<Storage, 'getItem'> = localStorage): QuizProgress {\n try {\n const raw = storage.getItem(quizProgressKey(lessonId, artifactId))\n if (raw === null) return { answers: Array<number>(questionCount).fill(-1), done: false }\n const parsed = JSON.parse(raw) as { answers?: unknown; done?: unknown }\n const answers = Array.isArray(parsed.answers)\n ? parsed.answers.map(a => (typeof a === 'number' ? a : -1))\n : []\n const padded = Array<number>(questionCount).fill(-1)\n for (let i = 0; i < Math.min(answers.length, questionCount); i += 1) padded[i] = answers[i]!\n return { answers: padded, done: parsed.done === true }\n } catch {\n return { answers: Array<number>(questionCount).fill(-1), done: false }\n }\n}\n\nexport function saveQuizProgress(lessonId: string, artifactId: string, progress: QuizProgress, storage: Pick<Storage, 'setItem'> = localStorage): void {\n try {\n storage.setItem(quizProgressKey(lessonId, artifactId), JSON.stringify(progress))\n } catch { /* storage unavailable — progress degrades to in-memory */ }\n}\n\n/** The score over recorded answers (pure). */\nexport function quizScore(data: QuizData, answers: readonly number[]): { correct: number; total: number } {\n let correct = 0\n let total = 0\n data.questions.forEach((q, i) => {\n const choice = answers[i]\n if (choice === undefined || choice < 0) return\n total += 1\n if (choice === q.answer) correct += 1\n })\n return { correct, total }\n}\n\nconst ACTION_ICONS: Record<PostQuizActionId, (props: { size?: number }) => ReactNode> = {\n 'explain-wrong': IconBookFill16,\n 'retry': IconRefreshOutline16,\n 'go-deeper': IconBoltFill16,\n 'mark-mastered': IconCrownFill16,\n 'next-topic': IconStarFill16,\n}\n\n/**\n * The card: one question at a time, click-to-answer (instant local judge +\n * explanation reveal — the cursor is explicit, so the reveal is never skipped),\n * progress persisted per artifact; the score screen is an explicit step, and\n * on first arrival its completion hook goes to the tutor exactly once.\n */\nexport function QuizCard({ lessonId, artifactId, data, masteryPct, send, onFinished }: {\n lessonId: string\n artifactId: string\n data: QuizData\n masteryPct: number | null\n send: (text: string) => void\n /** Fired once when the score screen lands; carries allCorrect. */\n onFinished?: (allCorrect: boolean) => void\n}): ReactNode {\n const questions = data.questions\n const [progress, setProgress] = useState<QuizProgress>(() => loadQuizProgress(lessonId, artifactId, questions.length))\n const [cursor, setCursor] = useState(0)\n const [showScore, setShowScore] = useState(false)\n const [hookSent, setHookSent] = useState(false)\n\n // (Re)load when the artifact identity changes (lesson switch).\n useEffect(() => {\n const next = loadQuizProgress(lessonId, artifactId, questions.length)\n setProgress(next)\n setShowScore(next.done)\n const firstUnanswered = next.answers.findIndex(a => a < 0)\n setCursor(firstUnanswered === -1 ? questions.length - 1 : firstUnanswered)\n setHookSent(false)\n }, [lessonId, artifactId, questions.length])\n\n const allAnswered = progress.answers.every(a => a >= 0)\n const score = quizScore(data, progress.answers)\n const mastery = masteryPct === null ? null : masteryPct / 100\n\n // The completion hook: once, when the score screen first shows with a full card.\n useEffect(() => {\n if (!showScore || !allAnswered || hookSent) return\n setHookSent(true)\n onFinished?.(score.correct === score.total)\n const next = { answers: [...progress.answers], done: true }\n setProgress(next)\n saveQuizProgress(lessonId, artifactId, next)\n send(tr('quiz.hook', { correct: score.correct, total: score.total }))\n }, [showScore, allAnswered, hookSent, lessonId, artifactId, progress.answers, score.correct, score.total, send])\n\n const answer = (choice: number): void => {\n const next = { answers: [...progress.answers], done: progress.done }\n next.answers[cursor] = choice\n setProgress(next)\n saveQuizProgress(lessonId, artifactId, next)\n }\n\n if (showScore && allAnswered) {\n const actions = getPostQuizActions(score, mastery)\n return createElement('div', { className: 'lks-qcard', 'data-lks-quiz': artifactId },\n createElement('div', { className: 'lks-qcard-head' },\n createElement(IconStarFill16, { size: 14 }),\n tr('quiz.score', { correct: score.correct, total: score.total })),\n createElement('div', { className: 'lks-qcard-review' },\n ...questions.map((q, i) => {\n const choice = progress.answers[i] ?? -1\n const right = choice === q.answer\n return createElement('div', { key: i, className: `lks-qcard-q review${right ? '' : ' wrong'}` },\n createElement('div', { className: 'lks-qcard-prompt' }, `${i + 1}. ${q.prompt}`),\n createElement('div', { className: 'lks-qcard-ans' },\n right ? '' : `${tr('quiz.youChose')}:${q.options[choice] ?? '—'} · `,\n `${tr('quiz.answer')}:${q.options[q.answer] ?? '—'}`),\n createElement('div', { className: 'lks-qcard-expl' }, q.explanation))\n })),\n createElement('div', { className: 'lks-qcard-actions' },\n ...actions.map(a => createElement('button', {\n key: a.id,\n className: 'lks-qcard-action',\n title: a.advancesMastery ? tr('quiz.action.markMastered.hint') : undefined,\n onClick: () => { send(tr(`quiz.msg.${a.id}`)) },\n }, createElement(ACTION_ICONS[a.id]!, { size: 13 }), tr(`quiz.action.${a.id}`)))),\n )\n }\n\n const q = questions[cursor]!\n const chosen = progress.answers[cursor] ?? -1\n const options = q.options.map((opt, i) => {\n const picked = chosen === i\n const isRight = i === q.answer\n const tone = chosen < 0 ? '' : picked ? (isRight ? ' right' : ' wrong') : isRight ? ' right dim' : ''\n return createElement('button', {\n key: i,\n className: `lks-qcard-opt${tone}`,\n disabled: chosen >= 0,\n onClick: () => { answer(i) },\n }, opt)\n })\n const explanation = chosen < 0 ? null : createElement('div', { className: 'lks-qcard-expl' },\n `${chosen === q.answer ? tr('quiz.right') : tr('quiz.wrong')} — ${q.explanation}`,\n createElement('div', { style: { marginTop: '6px' } },\n createElement('button', {\n className: 'lks-qcard-next',\n onClick: () => {\n if (cursor + 1 < questions.length) setCursor(cursor + 1)\n else setShowScore(true)\n },\n }, cursor + 1 < questions.length ? tr('quiz.next') : tr('quiz.finish'))))\n return createElement('div', { className: 'lks-qcard', 'data-lks-quiz': artifactId },\n createElement('div', { className: 'lks-qcard-head' },\n createElement(IconBookFill16, { size: 14 }),\n data.title ?? tr('quiz.card.title'),\n createElement('span', { className: 'lks-qcard-count' }, tr('quiz.progress', { cur: cursor + 1, total: questions.length }))),\n createElement('div', { className: 'lks-qcard-q' },\n createElement('div', { className: 'lks-qcard-prompt' }, q.prompt),\n createElement('div', { className: 'lks-qcard-opts' }, ...options),\n explanation,\n ),\n )\n}\n","/**\n * The artifact cards (0.15.0 P1b, upstream artifacts port): compare tables,\n * code walkthroughs, mermaid diagrams (with the expand modal), and the\n * opening guess — plus the seen-tracking that feeds the notebook badge and\n * the \"new artifact\" toast. Rendering is lazy where it is heavy (mermaid via\n * the enhance pipeline); everything else is plain dsw-token DOM.\n * @module dsh-plugin-lookatstudy/client/artifact-cards\n */\n\nimport { createElement, useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { tr } from './locale.ts'\nimport { IconCloseFill16, IconMaximizeOutline16 } from './icons.tsx'\n\n/** One artifact row from the state feed. */\nexport interface ArtifactRow {\n readonly id: string\n readonly artifactType: string\n readonly title: string\n readonly data: Record<string, unknown>\n}\n\n/** localStorage key for the artifact ids the learner has already seen. */\nexport function seenArtifactsKey(lessonId: string): string {\n return `dsh-plugin-lookatstudy:seen-artifacts:${lessonId}`\n}\n\n/** Which artifact ids are new (not yet seen); pure over the stored set. */\nexport function unseenArtifacts(lessonId: string, artifacts: readonly ArtifactRow[], storage: Pick<Storage, 'getItem'> = localStorage): string[] {\n let seen: Set<string>\n try {\n const raw = storage.getItem(seenArtifactsKey(lessonId))\n seen = new Set(Array.isArray(JSON.parse(raw ?? '[]')) ? JSON.parse(raw ?? '[]') as unknown[] : [])\n } catch {\n seen = new Set()\n }\n return artifacts.filter(a => !seen.has(a.id)).map(a => a.id)\n}\n\n/** Mark artifact ids seen (opening the notebook counts as seeing them). */\nexport function markArtifactsSeen(lessonId: string, ids: readonly string[], storage: Pick<Storage, 'setItem'> = localStorage): void {\n try {\n let seen: string[] = []\n try {\n const parsed = JSON.parse(storage.getItem(seenArtifactsKey(lessonId)) ?? '[]') as unknown\n if (Array.isArray(parsed)) seen = parsed.filter((x): x is string => typeof x === 'string')\n } catch { /* junk storage starts blank */ }\n storage.setItem(seenArtifactsKey(lessonId), JSON.stringify([...new Set([...seen, ...ids])]))\n } catch { /* storage unavailable */ }\n}\n\n/** The cards render mermaid through the shared CDN pipeline\n * (enhanceDiagrams consumes pre > code.lang-mermaid blocks). */\nasync function renderMermaidInto(el: HTMLElement, code: string, onError: () => void): Promise<void> {\n try {\n const { enhanceDiagrams } = await import('./enhance.ts')\n el.textContent = ''\n const pre = document.createElement('pre')\n const codeEl = document.createElement('code')\n codeEl.className = 'lang-mermaid'\n codeEl.textContent = code\n pre.appendChild(codeEl)\n el.appendChild(pre)\n const count = await enhanceDiagrams(el)\n if (count === 0) onError()\n } catch {\n onError()\n }\n}\n\n/** Compare table card (headers + rows). */\nexport function CompareTableCard({ artifact }: { artifact: ArtifactRow }): ReactNode {\n const headers = (artifact.data.headers ?? []) as string[]\n const rows = (artifact.data.rows ?? []) as string[][]\n return createElement('div', { className: 'lks-acard', 'data-lks-artifact': artifact.id },\n createElement('div', { className: 'lks-acard-head' }, artifact.title),\n createElement('table', { className: 'lks-acard-table' },\n createElement('thead', null, createElement('tr', null, ...headers.map((h, i) => createElement('th', { key: i }, h)))),\n createElement('tbody', null, ...rows.map((row, r) => createElement('tr', { key: r }, ...row.map((c, i) => createElement('td', { key: i }, c)))))))\n}\n\n/** Code walkthrough card: line-numbered code + per-segment notes. */\nexport function CodeWalkthroughCard({ artifact }: { artifact: ArtifactRow }): ReactNode {\n const language = typeof artifact.data.language === 'string' ? artifact.data.language : 'text'\n const code = typeof artifact.data.code === 'string' ? artifact.data.code : ''\n const annotations = (artifact.data.annotations ?? []) as Array<{ lineStart: number; lineEnd: number; note: string }>\n const lines = code.split('\\n')\n return createElement('div', { className: 'lks-acard', 'data-lks-artifact': artifact.id },\n createElement('div', { className: 'lks-acard-head' }, `${artifact.title} · ${language}`),\n createElement('pre', { className: 'lks-acard-code' },\n ...lines.map((line, i) => createElement('span', { key: i, className: 'lks-acard-line' },\n createElement('i', null, String(i + 1)), line, '\\n'))),\n createElement('div', { className: 'lks-acard-notes' },\n ...annotations.map((a, i) => createElement('div', { key: i, className: 'lks-acard-note' },\n createElement('b', null, `${tr('artifact.lines', { from: a.lineStart, to: a.lineEnd })}`), a.note))))\n}\n\n/** Mermaid diagram card with an expand modal (upstream DiagramViewerModal). */\nexport function DiagramCard({ artifact }: { artifact: ArtifactRow }): ReactNode {\n const mermaid = typeof artifact.data.mermaid === 'string' ? artifact.data.mermaid : ''\n const bodyRef = useRef<HTMLDivElement | null>(null)\n const [failed, setFailed] = useState(false)\n const [expanded, setExpanded] = useState(false)\n useEffect(() => {\n if (bodyRef.current === null) return\n void renderMermaidInto(bodyRef.current, mermaid, () => { setFailed(true) })\n }, [mermaid])\n const body = failed\n ? createElement('pre', { className: 'lks-acard-code' }, mermaid)\n : createElement('div', { className: 'lks-acard-diagram', ref: bodyRef })\n return createElement('div', { className: 'lks-acard', 'data-lks-artifact': artifact.id },\n createElement('div', { className: 'lks-acard-head' }, artifact.title,\n createElement('button', { className: 'lks-acard-expand', title: tr('artifact.expand'), onClick: () => { setExpanded(true) } }, createElement(IconMaximizeOutline16, { size: 13 }))),\n body,\n expanded\n ? createElement('div', {\n className: 'lks-acard-modal',\n onClick: () => { setExpanded(false) },\n },\n createElement('div', { className: 'lks-acard-modal-body', onClick: (e: { stopPropagation: () => void }) => { e.stopPropagation() } },\n createElement('div', { className: 'lks-acard-modal-title' }, artifact.title,\n createElement('button', { className: 'lks-acard-expand', onClick: () => { setExpanded(false) } }, createElement(IconCloseFill16, { size: 13 }))),\n failed ? createElement('pre', { className: 'lks-acard-code' }, mermaid) : createElement('div', {\n ref: (el: HTMLDivElement | null) => {\n if (el !== null) void renderMermaidInto(el, mermaid, () => { setFailed(true) })\n },\n })))\n : null)\n}\n\n/** The opening guess: two big options, click = pick (sent to the tutor, who\n * reveals next turn); unscored — no right/wrong language anywhere. */\nexport function GuessCard({ artifact, send }: { artifact: ArtifactRow; send: (text: string) => void }): ReactNode {\n const prompt = typeof artifact.data.prompt === 'string' ? artifact.data.prompt : ''\n const options = (artifact.data.options ?? []) as Array<{ id: string; label: string }>\n const [picked, setPicked] = useState<string | null>(null)\n useEffect(() => { setPicked(null) }, [artifact.id])\n return createElement('div', { className: 'lks-acard lks-acard-guess', 'data-lks-artifact': artifact.id },\n createElement('div', { className: 'lks-acard-head' }, tr('artifact.guess.title')),\n createElement('div', { className: 'lks-acard-guess-prompt' }, prompt),\n createElement('div', { className: 'lks-acard-guess-opts' },\n ...options.map(opt => createElement('button', {\n key: opt.id,\n className: `lks-acard-guess-opt${picked === opt.id ? ' picked' : ''}`,\n disabled: picked !== null,\n onClick: () => {\n setPicked(opt.id)\n send(tr('artifact.guess.pick', { label: opt.label }))\n },\n }, opt.label))),\n picked !== null ? createElement('div', { className: 'lks-acard-guess-wait' }, tr('artifact.guess.wait')) : null)\n}\n\n/** Dispatch one artifact row to its card. */\nexport function ArtifactCard({ artifact, send }: { artifact: ArtifactRow; send: (text: string) => void }): ReactNode {\n switch (artifact.artifactType) {\n case 'compare_table': return createElement(CompareTableCard, { artifact })\n case 'code_walkthrough': return createElement(CodeWalkthroughCard, { artifact })\n case 'diagram': return createElement(DiagramCard, { artifact })\n case 'guess': return createElement(GuessCard, { artifact, send })\n default: return null\n }\n}\n","/**\n * The companion creature — the upstream 伴学生物's minimal honest DOM port\n * (0.15.0 P7): a small being in the panel's bottom-right corner with five\n * forms, a pure mood machine (idle / talking / celebrating / encouraging),\n * and the a11y iron rule carried over — reduced-motion renders it static.\n * Upstream's Electron-native life (mic envelopes, typing squeeze, drag-throw)\n * has no web-panel equivalent and stays unported by design.\n * @module dsh-plugin-lookatstudy/client/companion\n */\n\nimport { createElement, useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { tr } from './locale.ts'\n\nexport type CompanionFormId = 'ember' | 'frost' | 'moss' | 'star' | 'ink'\nexport type CompanionMood = 'idle' | 'talking' | 'celebrating' | 'encouraging'\n\nexport const COMPANION_FORMS: ReadonlyArray<{ id: CompanionFormId; labelKey: string; body: string; accent: string }> = [\n { id: 'ember', labelKey: 'companion.form.ember', body: '#f59e0b', accent: '#dc2626' },\n { id: 'frost', labelKey: 'companion.form.frost', body: '#7dd3fc', accent: '#0284c7' },\n { id: 'moss', labelKey: 'companion.form.moss', body: '#86efac', accent: '#16a34a' },\n { id: 'star', labelKey: 'companion.form.star', body: '#c4b5fd', accent: '#7c3aed' },\n { id: 'ink', labelKey: 'companion.form.ink', body: '#cbd5e1', accent: '#334155' },\n]\n\n/** Normalize a stored form id; junk falls back to 小焰 (upstream's default). */\nexport function normalizeCompanionForm(stored: string | null): CompanionFormId {\n return COMPANION_FORMS.some(f => f.id === stored) ? stored as CompanionFormId : 'ember'\n}\n\nexport function storedCompanionForm(): CompanionFormId {\n try {\n return normalizeCompanionForm(localStorage.getItem('dsh-plugin-lookatstudy:companion-form'))\n } catch {\n return 'ember'\n }\n}\n\nexport function storeCompanionForm(id: CompanionFormId): void {\n try {\n localStorage.setItem('dsh-plugin-lookatstudy:companion-form', id)\n } catch { /* storage unavailable — the default renders */ }\n}\n\n/**\n * The mood machine — pure. `celebrate` and `encourage` are momentary (the\n * view decays them back through `decay`); `talking` latches while read-aloud\n * runs; poke is a self-echoing celebration.\n */\nexport function nextCompanionMood(current: CompanionMood, event: 'talk-start' | 'talk-end' | 'celebrate' | 'encourage' | 'decay' | 'poke'): CompanionMood {\n switch (event) {\n case 'talk-start': return 'talking'\n case 'talk-end': return current === 'talking' ? 'idle' : current\n case 'celebrate':\n case 'poke': return 'celebrating'\n case 'encourage': return 'encouraging'\n case 'decay': return current === 'celebrating' || current === 'encouraging' ? 'idle' : current\n default: return current\n }\n}\n\n/** One creature face — the shared skin every form wears. */\nfunction CompanionFace({ form, mood }: { form: CompanionFormId; mood: CompanionMood }): ReactNode {\n const skin = COMPANION_FORMS.find(f => f.id === form) ?? COMPANION_FORMS[0]!\n // eyes: happy arcs when celebrating; plain dots otherwise\n const eye = mood === 'celebrating'\n ? 'M -3.4 0 q 1.7 -2.6 3.4 0'\n : 'M -3.4 -1.4 a 1.4 1.4 0 1 0 0.01 0'\n const mouth = mood === 'celebrating'\n ? 'M 20 21 q 4 4 8 0'\n : mood === 'encouraging'\n ? 'M 21 22 q 3 -2.5 6 0'\n : 'M 21 21 q 3 2.4 6 0'\n return createElement('svg', { width: 64, height: 64, viewBox: '0 0 64 64', 'aria-hidden': 'true' },\n createElement('g', null,\n createElement('circle', { cx: 32, cy: 34, r: 20, fill: skin.body, stroke: skin.accent, strokeWidth: 2 }),\n form === 'ember'\n ? createElement('path', { d: 'M32 4 q6 8 3 12 q8 -2 9 6 q4 -1 4 4 l-2 4 H18 l-2 -4 q0 -5 4 -4 q1 -8 9 -6 q-3 -4 3 -12z', fill: skin.accent, opacity: 0.85 })\n : form === 'star'\n ? createElement('path', { d: 'M32 2 l4 8 9 1 -6.5 6 1.5 9 -8.5 -4.5 -8 4.5 1.5 -9 -6.5 -6 9 -1z', fill: skin.accent, opacity: 0.85 })\n : form === 'moss'\n ? createElement('path', { d: 'M32 2 q-2 6 2 10 q-8 0 -8 6 H40 q0 -6 -8 -6 q4 -4 0 -10z', fill: skin.accent, opacity: 0.85 })\n : form === 'ink'\n ? createElement('path', { d: 'M32 2 q8 12 8 18 a8 8 0 1 1 -16 0 q0 -6 8 -18z', fill: skin.accent, opacity: 0.55 })\n : createElement('path', { d: 'M18 12 l3 5 5 -1 -1 5 5 3 -5 3 1 5 -5 -1 -3 5 -3 -5 -5 1 1 -5 -5 -3 5 -3 -1 -5 5 1z', fill: skin.accent, opacity: 0.7 }),\n createElement('path', { d: eye, transform: 'translate(25 30)', stroke: skin.accent, strokeWidth: 1.6, fill: mood === 'celebrating' ? 'none' : skin.accent, strokeLinecap: 'round' }),\n createElement('path', { d: eye, transform: 'translate(14 30)', stroke: skin.accent, strokeWidth: 1.6, fill: mood === 'celebrating' ? 'none' : skin.accent, strokeLinecap: 'round' }),\n createElement('path', { d: mouth, stroke: skin.accent, strokeWidth: 1.8, fill: 'none', strokeLinecap: 'round' })))\n}\n\n/** The creature: fixed bottom-right inside the panel, mood-animated, pokeable. */\nexport function Companion({ mood, onPoke }: { mood: CompanionMood; onPoke?: () => void }): ReactNode {\n const form = storedCompanionForm()\n return createElement('div', {\n className: `lks-companion mood-${mood}`,\n 'data-lks-companion': form,\n title: tr('companion.poke'),\n role: 'button',\n tabIndex: 0,\n 'aria-label': tr('companion.poke'),\n onClick: () => { onPoke?.() },\n onKeyDown: (e: { key: string; preventDefault: () => void }) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onPoke?.()\n }\n },\n }, createElement(CompanionFace, { form, mood }))\n}\n\n/** The hook driving one creature instance: event → mood, momentary decay. */\nexport function useCompanionMood(): { mood: CompanionMood; emit: (event: Parameters<typeof nextCompanionMood>[1]) => void } {\n const [mood, setMood] = useState<CompanionMood>('idle')\n const moodRef = useRef(mood)\n moodRef.current = mood\n const emit = (event: Parameters<typeof nextCompanionMood>[1]): void => {\n const next = nextCompanionMood(moodRef.current, event)\n if (next !== moodRef.current) {\n moodRef.current = next\n setMood(next)\n }\n }\n // momentary moods decay after 2.4s (upstream celebration 落点 timing, compressed)\n useEffect(() => {\n if (mood !== 'celebrating' && mood !== 'encouraging') return\n const timer = setTimeout(() => {\n moodRef.current = nextCompanionMood(moodRef.current, 'decay')\n setMood(moodRef.current)\n }, 2400)\n return () => { clearTimeout(timer) }\n }, [mood])\n return { mood, emit }\n}\n","/**\n * 讲解区画线定位(0.15.0 P2, upstream highlightText.ts v0.3.3 方案移植):\n * 上游历经多轮后放弃绝对字符偏移(mark 存在/清除与 React 重渲会让两个\n * model 不一致导致错位),改用「选区文字 + 前后文指纹」在纯文本 model 上\n * 做文本搜索。这里同样:高亮来源 = 记录区笔记的 quote 本身 —— 删笔记即删\n * 高亮,无需独立锚点状态。纯函数部分(定位与分段规划)node:test 直测。\n * @module dsh-plugin-lookatstudy/client/highlights\n */\n\n/** One prose text node mapped into the model's coordinate space. */\nexport interface ModelNode<T = unknown> {\n readonly node: T\n readonly start: number\n readonly end: number\n}\n\nexport interface TextModel<T = unknown> {\n readonly text: string\n readonly nodes: readonly ModelNode<T>[]\n}\n\n/**\n * Locate a selection inside the model text: exact `selected` text search,\n * disambiguated by a fuzzy `surrounding` fingerprint (the same sentence\n * appearing twice resolves to the occurrence whose context matches).\n * @returns the global [start, end) in model.text, or null when absent.\n */\nexport function locateInModel(model: TextModel, selected: string, surrounding: string | undefined): { start: number; end: number } | null {\n const needle = selected.trim()\n if (needle.length < 2) return null\n const hits: number[] = []\n let from = 0\n for (;;) {\n const hit = model.text.indexOf(needle, from)\n if (hit === -1) break\n hits.push(hit)\n from = hit + 1\n }\n if (hits.length === 0) return null\n if (hits.length === 1 || surrounding === undefined || surrounding.trim() === '') {\n return { start: hits[0]!, end: hits[0]! + needle.length }\n }\n // fingerprint: surrounding = ~30 chars around the selection; the right\n // occurrence is the one maximizing overlap with that fingerprint.\n const finger = surrounding.replace(/\\s+/g, '')\n let best = hits[0]!\n let bestScore = -1\n for (const hit of hits) {\n const context = model.text.slice(Math.max(0, hit - 30), hit + needle.length + 30).replace(/\\s+/g, '')\n let score = 0\n for (let i = 0; i < finger.length; i += 4) {\n if (context.includes(finger.slice(i, i + 4))) score += 1\n }\n if (score > bestScore) {\n bestScore = score\n best = hit\n }\n }\n return { start: best, end: best + needle.length }\n}\n\n/** Plan the per-node segments covering global [start, end) — pure. */\nexport function planSegments(nodes: readonly ModelNode[], start: number, end: number): Array<{ index: number; localStart: number; localEnd: number }> {\n const segments: Array<{ index: number; localStart: number; localEnd: number }> = []\n nodes.forEach((entry, index) => {\n if (entry.end <= start || entry.start >= end) return\n segments.push({\n index,\n localStart: Math.max(0, start - entry.start),\n localEnd: Math.min(entry.end - entry.start, end - entry.start),\n })\n })\n return segments\n}\n\n/** Build the DOM text model for the prose container (the walker rules mirror\n * upstream: skip script/style and KaTeX glyph layers; skip existing marks so\n * save-time and apply-time models stay symmetric). */\nexport function getTextModel(container: HTMLElement, opts?: { includeMarks?: boolean }): TextModel<Text> {\n const nodes: ModelNode<Text>[] = []\n let text = ''\n const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, {\n acceptNode(node) {\n const parent = (node as Text).parentElement\n if (parent === null) return NodeFilter.FILTER_REJECT\n const tag = parent.tagName\n if (tag === 'SCRIPT' || tag === 'STYLE') return NodeFilter.FILTER_REJECT\n if (parent.closest('.katex-html')) return NodeFilter.FILTER_REJECT\n if (parent.closest('.katex-mathml') !== null && parent.closest('annotation') === null) return NodeFilter.FILTER_REJECT\n if (opts?.includeMarks !== true && parent.closest('mark.lks-hl') !== null) return NodeFilter.FILTER_REJECT\n return NodeFilter.FILTER_ACCEPT\n },\n })\n let n: Node | null\n while ((n = walker.nextNode()) !== null) {\n const t = n as Text\n const content = t.textContent ?? ''\n nodes.push({ node: t, start: text.length, end: text.length + content.length })\n text += content\n }\n return { text, nodes }\n}\n\n/** The mark class applied to persisted highlights. */\nexport const HIGHLIGHT_MARK = 'lks-hl'\n\n/**\n * Apply one highlight range to the live DOM: split the boundary text nodes\n * and wrap the covering segments in marks. Returns the created marks.\n */\nexport function wrapRange(model: TextModel<Text>, start: number, end: number): HTMLElement[] {\n const marks: HTMLElement[] = []\n for (const seg of planSegments(model.nodes, start, end)) {\n const entry = model.nodes[seg.index]!\n const node = entry.node\n if (node.parentElement === null) continue\n if (node.parentElement.closest(`mark.${HIGHLIGHT_MARK}`) !== null) continue\n const mid = seg.localStart > 0 ? node.splitText(seg.localStart) : node\n const localEnd = seg.localEnd - seg.localStart\n if (mid.textContent !== null && mid.textContent.length > localEnd) mid.splitText(localEnd)\n const mark = document.createElement('mark')\n mark.className = HIGHLIGHT_MARK\n mid.parentElement!.insertBefore(mark, mid)\n mark.appendChild(mid)\n marks.push(mark)\n }\n return marks\n}\n\n/**\n * Render the persisted highlights for one lesson: each record-zone note with a\n * quote anchors a mark. Idempotent per call — call on a freshly rendered\n * (pre-mark) container; the model skips existing marks so re-entry is safe.\n * @returns how many notes got at least one mark.\n */\nexport function applyHighlights(container: HTMLElement, quotes: readonly string[]): number {\n let applied = 0\n for (const quote of quotes) {\n const trimmed = quote.trim()\n if (trimmed.length < 2) continue\n const model = getTextModel(container)\n const located = locateInModel(model, trimmed, undefined)\n if (located === null) continue\n if (wrapRange(model, located.start, located.end).length > 0) applied += 1\n }\n return applied\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/** One row of the rail search results panel. */\nexport interface RailSearchRow {\n readonly lessonId: string\n readonly title: string\n readonly snippet: string\n readonly courseTitle: string\n readonly fromTitle: boolean\n}\n\n/**\n * Merge the rail's two search tracks into the results panel: title hits\n * (lessons whose name matches — they are also filtered into the tree) first,\n * then full-text hits not already covered, snippets carried for the body\n * matches. Pure; empty queries yield no panel.\n */\nexport function mergeRailSearch(\n query: string,\n lessons: ReadonlyArray<{ id: string; title: string; status: string; kind: string }>,\n textHits: ReadonlyArray<{ lessonId: string; lessonTitle: string; snippet: string; courseTitle: string }>,\n): RailSearchRow[] {\n const keys = query.trim().toLowerCase().split(/\\s+/).filter(Boolean)\n if (keys.length === 0) return []\n const rows: RailSearchRow[] = []\n const seen = new Set<string>()\n const matches = (title: string): boolean => keys.every(k => title.toLowerCase().includes(k))\n for (const lesson of lessons) {\n if (lesson.kind === 'exam' || lesson.status === 'locked' || !matches(lesson.title)) continue\n rows.push({ lessonId: lesson.id, title: lesson.title, snippet: '', courseTitle: '', fromTitle: true })\n seen.add(lesson.id)\n }\n for (const hit of textHits) {\n if (seen.has(hit.lessonId)) continue\n rows.push({ lessonId: hit.lessonId, title: hit.lessonTitle, snippet: hit.snippet, courseTitle: hit.courseTitle, fromTitle: false })\n seen.add(hit.lessonId)\n }\n return rows.slice(0, 12)\n}\n\n/**\n * A section's effective openness: the user's explicit toggle (persisted per\n * course) overrides the frontier-based default. Pure.\n */\nexport function effectiveOpen(\n sectionTitle: string,\n defaultOpen: boolean,\n overrides: Readonly<Record<string, boolean>>,\n): boolean {\n return overrides[sectionTitle] ?? defaultOpen\n}\n\n/**\n * The narrow-mode pane picker: only valid ids, remembering the last choice\n * (upstream's narrow switcher semantics). Pure.\n */\nexport function pickNarrowPane(stored: string | null): 'rail' | 'chat' | 'note' {\n return stored === 'rail' || stored === 'chat' || stored === 'note' ? stored : 'chat'\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 * VENDORED from LookatStudy v0.28.0 (src/renderer/lib/mapLayout.ts) — verbatim.\n * Provenance: https://github.com/Upston-Liu/LookatStudy, dark-first port round\n * 2026-09-06; upstream file is pure (zero imports) and test-frozen there\n * (verify-map-layout T11). Only the module docblock moves into this header.\n *\n * mapLayout —— 选关地图节点布局引擎(纯函数,易测易调)。\n *\n * v0.6:从\"三布局模式 + 手动切换器\"重做为单一\"漂浮气球\"布局。\n * 节点像飘在空中的气球,位置带轻微种子确定性抖动(用 section id 哈希),\n * 每次渲染位置完全一致,不会乱跳,但又不整齐规整。\n * 连接用下垂的贝塞尔绳子(模拟重力),不再是 S 形箭头。\n *\n * 坐标系:相对章节容器左上角(0,0),x 横向 y 纵向,单位 px。\n * 节点尺寸:球 56px(w-14 h-14),卡片宽 110px(含名字)。\n */\n\n/** 单个节点的布局结果(相对章节容器)。 */\nexport interface NodeLayout {\n /** 节点中心 x(px,相对章节容器左上) */\n x: number\n /** 节点中心 y(px,相对章节容器左上) */\n y: number\n /** 该节点在 lessons 数组里的索引 */\n index: number\n}\n\n/** 两节点间的连接段(供 SVG path 绘制)。 */\nexport interface PathSegment {\n /** 起点(上一节点中心) */\n from: { x: number; y: number }\n /** 终点(下一节点中心) */\n to: { x: number; y: number }\n /** 段索引(from 节点的 index) */\n index: number\n}\n\nexport interface SectionLayout {\n nodes: NodeLayout[]\n segments: PathSegment[]\n /** 该 section 容器所需高度(px)。贪心防重叠后按最后节点真实底边算,\n * 供 MapRail 精确设 minHeight / SVG height(不再用估算)。 */\n height: number\n}\n\n/** 容器宽度(章节内可用宽度,扣除 padding)。MapRail 章节容器 px-2。 */\nconst CONTAINER_WIDTH = 268 // 300px rail - padding\nconst NODE_RADIUS = 28 // w-14 h-14 = 56px,半径 28\nconst NODE_NAME_H = 20 // 球下方的 lesson 名字行高度\n/** 节点可视盒高(球 56 + 名字 20)。**必须与 MapRail 渲染的 NODE_H 一致** —— 重叠判定基于此。 */\nexport const NODE_BOX_H = NODE_RADIUS * 2 + NODE_NAME_H // 76\n/** 相邻节点中心最小纵向间距 = 盒高 + 呼吸。贪心推下保证 ≥ 此值 → 视觉永不重叠。 */\nexport const MIN_GAP_Y = NODE_BOX_H + 8 // 84\nconst NODE_SPACING_Y = 104 // 基线间距(略 > MIN_GAP_Y,多数情况无需推下,保持自然飘感)\nconst BALLOON_MARGIN = 18 // 气球左右留白(减小让抖动幅度更明显,不再呆板直线)\n/** 首节点中心下限(防顶撞上方章节路牌)。MapRail 渲染偏移 +12 + NODE_H/2=38 → wrapper 顶 = y-26 ≥ 8。 */\nconst TOP_PAD = NODE_RADIUS + 6 // 34\n/** y 轴抖动幅度(px)。贪心兜底后,jitter 不再是重叠源,仅负责高低参差。 */\nconst Y_JITTER = 26\n/** 绳子下垂量(px,模拟重力)。让贝塞尔像被拽下来的绳子,而非 S 形箭头。 */\nconst ROPE_SAG = 22\n\n/**\n * 确定性字符串哈希(FNV-1a 变体,32-bit)。\n * 同输入永远同输出 → 同一节点 id 每次渲染算出的抖动相同 → 气球位置稳定不跳。\n * 用于气球横向抖动 + bob 相位 + 星点位置 + 天体色切。\n */\nexport function hashStr(s: string): number {\n let h = 0x811c9dc5 // FNV offset basis\n for (let i = 0; i < s.length; i++) {\n h ^= s.charCodeAt(i)\n // 等价 h *= 16777619,用 Math.imul 避免 32-bit 溢出丢精度\n h = Math.imul(h, 0x01000193)\n }\n // 雪崩搅拌(avalanche finalizer):FNV-1a 对\"末位递增\"输入(seed:0, seed:1, seed:2…)\n // 输出近乎线性相关 → 相邻气球哈希接近 → 排成直线。加 xorshift + imul 搅拌彻底打散。\n h ^= h >>> 16\n h = Math.imul(h, 0x85ebca6b)\n h ^= h >>> 13\n h = Math.imul(h, 0xc2b2ae35)\n h ^= h >>> 16\n return h >>> 0\n}\n\n/**\n * 算某 section 的气球节点坐标 + 绳子连接路径。\n * 容器高度由 nodes 数量决定(调用方据此设容器 min-height)。\n *\n * 抖动算法:v0.6 同时在 x 和 y 两轴抖动 → 气球真正\"飘\"在空中,\n * 不再是呆板的直线/微弯列。x 大幅抖动(用满容器宽度),y 在基线上下 ±Y_JITTER。\n * 两个独立哈希(x 用 seed:i,y 用 seed:i:y)→ 抖动方向不相关,分布更自然。\n *\n * v0.8 防重叠:抖动后跑一趟**贪心向下推**(确定性,只增 y,保持单调),\n * 1) 首节点中心不低于 TOP_PAD —— 防顶撞上方章节路牌;\n * 2) 相邻节点中心纵向距 ≥ MIN_GAP_Y(= 盒高 + 8px 呼吸)—— 视觉盒永不重叠。\n * 任何 seed / 任何课数都保证不重叠(verify-map-layout T11 跨 240 种子穷举验证)。\n */\nexport function computeBalloonLayout(\n lessonCount: number,\n containerWidth: number = CONTAINER_WIDTH,\n seed: string = '',\n): SectionLayout {\n const nodes: NodeLayout[] = []\n const centerX = containerWidth / 2\n const maxDrift = containerWidth / 2 - BALLOON_MARGIN - NODE_RADIUS\n\n // 第一遍:基线 + 确定性抖动(x/y 双轴,两独立哈希 → 方向不相关,分布自然)\n for (let i = 0; i < lessonCount; i++) {\n const baseY = TOP_PAD + i * NODE_SPACING_Y\n // x 抖动:哈希归一化到 [-1,1],×maxDrift → 大幅左右飘\n const hx = hashStr(`${seed}:${i}`)\n const normX = (hx / 0xffffffff) * 2 - 1 // [-1, 1]\n const x = centerX + normX * maxDrift\n // y 抖动:另一独立哈希,±Y_JITTER 围绕基线 → 高低参差\n const hy = hashStr(`${seed}:${i}:y`)\n const normY = (hy / 0xffffffff) * 2 - 1 // [-1, 1]\n const y = baseY + normY * Y_JITTER\n nodes.push({ x, y, index: i })\n }\n\n // 第二遍:贪心防重叠(确定性,只向下推 → y 仍单调,与原 T4 不变量兼容)\n if (nodes.length > 0) {\n // 首节点下限:防顶撞章节路牌\n if (nodes[0]!.y < TOP_PAD) nodes[0]!.y = TOP_PAD\n // 相邻中心距 ≥ MIN_GAP_Y:用已定稿的前驱 y 推后继(级联正确)\n for (let i = 1; i < nodes.length; i++) {\n const minY = nodes[i - 1]!.y + MIN_GAP_Y\n if (nodes[i]!.y < minY) nodes[i]!.y = minY\n }\n }\n\n // 绳子段:相邻节点(用防重叠后的最终坐标)\n const segments: PathSegment[] = []\n for (let i = 0; i < nodes.length - 1; i++) {\n segments.push({ from: nodes[i]!, to: nodes[i + 1]!, index: i })\n }\n\n // 容器精确高度:最后节点底边 + 余量(供 MapRail 设 minHeight / SVG height)\n const height = nodes.length === 0\n ? 40\n : nodes[nodes.length - 1]!.y + NODE_BOX_H / 2 + 10\n\n return { nodes, segments, height }\n}\n\n/** 容器所需高度的**保守上界**估算。\n * 实际精确高度请用 `computeBalloonLayout(...).height`(贪心后按真实底边算)。\n * 本函数供拿不到 layout 的调用方做兜底估算;单调递增(verify T7)。 */\nexport function sectionHeight(lessonCount: number): number {\n if (lessonCount === 0) return 40\n return TOP_PAD + (lessonCount - 1) * MIN_GAP_Y + NODE_BOX_H / 2 + 10\n}\n\n/**\n * 两节点间的绳子贝塞尔(下垂)。\n * 控制点:y 中点 + ROPE_SAG(向下垂,模拟重力);x 用各自端点 x。\n * 比原 S 形软,像被拽下来的绳子。\n */\nexport function balloonSegmentToPath(seg: PathSegment): string {\n const { from, to } = seg\n const controlY = (from.y + to.y) / 2 + ROPE_SAG\n return `M ${from.x} ${from.y} C ${from.x} ${controlY}, ${to.x} ${controlY}, ${to.x} ${to.y}`\n}\n","/**\n * MapRail balloon map (P9b) — the upstream v0.28 course map ported onto the\n * plugin's rail: deterministic balloon layout (vendored mapLayout engine),\n * sagging bezier ropes with path-draw on walked segments, state bubbles with\n * 3D sphere lighting, signpost section heads, and the sky backdrop. This is\n * upstream's own reduced-motion presentation (static layout + balloon-bob\n * CSS), which is exactly the deterministic half of its physics map.\n */\nimport { createElement, useEffect, useRef, useState, type CSSProperties, type ReactNode } from 'react'\nimport { computeBalloonLayout, balloonSegmentToPath, hashStr, NODE_BOX_H } from '../vendor/map-layout.ts'\nimport { IconBookFill16, IconCrownFill16, IconGoalOutline16, IconLockFill16, IconStarFill16 } from './icons.tsx'\nimport { tr } from './locale.ts'\n\n/** The rail lesson projection (a structural slice of StudyData's section lessons). */\nexport interface MapLesson {\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 due: boolean\n readonly focus: boolean\n}\n\n/** Upstream bubbleClass port: the four study states + the three exam variants.\n * Status→class goes through a table (in_progress → in-progress, like upstream's switch). */\nconst STATUS_CLASS: Record<string, string> = { locked: 'locked', available: 'available', in_progress: 'in-progress', mastered: 'mastered' }\n\nexport function bubbleClasses(lesson: Pick<MapLesson, 'kind' | 'status'>, examAllowed: boolean): string {\n if (lesson.kind === 'exam') {\n if (!examAllowed) return 'lks-bubble lks-exam-locked'\n return lesson.status === 'mastered' ? 'lks-bubble lks-exam-passed' : 'lks-bubble lks-exam'\n }\n return `lks-bubble lks-bubble-${STATUS_CLASS[lesson.status] ?? 'locked'}`\n}\n\n/** A rope segment is \"walked\" when its FROM node has been opened\n * (upstream ropeStyleIsPassed: mastered | in_progress | available). */\nexport function ropePassed(from: Pick<MapLesson, 'status'>): boolean {\n return from.status === 'mastered' || from.status === 'in_progress' || from.status === 'available'\n}\n\n/** Deterministic sky preset per course (upstream pickPreset's CSS-essence port). */\nexport function pickSky(courseId: string): 'day' | 'dusk' | 'night' {\n return (['day', 'dusk', 'night'] as const)[hashStr(courseId) % 3]\n}\n\n/** One section: signpost head (still the plugin's collapse toggle) + the balloon field. */\nexport function MapSectionView({ section, examAllowed, open, onToggle, onJump }: {\n section: { title: string; index: number; lessons: readonly MapLesson[] }\n examAllowed: boolean\n open: boolean\n onToggle: () => void\n onJump: (lessonId: string) => void\n}): ReactNode {\n const pathRef = useRef<HTMLDivElement | null>(null)\n const [containerW, setContainerW] = useState(268)\n useEffect(() => {\n if (pathRef.current === null) return\n const measure = (): void => {\n const w = pathRef.current?.clientWidth ?? 268\n setContainerW(w > 0 ? w : 268)\n }\n measure()\n const ro = new ResizeObserver(measure)\n ro.observe(pathRef.current)\n return () => { ro.disconnect() }\n }, [open])\n\n const lessons = section.lessons\n const layout = computeBalloonLayout(lessons.length, containerW, section.title)\n\n return createElement('section', { className: 'lks-mapsec' },\n createElement('button', {\n type: 'button',\n className: 'lks-signpost',\n 'aria-expanded': String(open),\n title: open ? tr('rail.section.collapse') : tr('rail.section.expand', { count: section.lessons.length }),\n onClick: onToggle,\n },\n createElement('span', { className: 'lks-signpost-num' }, String(section.index + 1)),\n createElement('span', { className: 'lks-signpost-title' }, section.title),\n createElement('span', { className: 'lks-signpost-caret' }, open ? '▾' : '▸')),\n open && lessons.length > 0\n ? createElement('div', { ref: pathRef, className: 'lks-mapfield', style: { minHeight: `${String(layout.height)}px` } },\n createElement('svg', {\n className: 'lks-mapropes',\n 'aria-hidden': 'true',\n style: { height: `${String(layout.height)}px` },\n },\n ...layout.segments.map(seg => {\n const passed = ropePassed(lessons[seg.index] ?? { status: 'locked' })\n return createElement('path', {\n key: seg.index,\n d: balloonSegmentToPath(seg),\n stroke: passed ? 'var(--brand)' : 'var(--ink-faint)',\n 'stroke-width': passed ? '4' : '2.5',\n 'stroke-opacity': passed ? '0.6' : '0.5',\n fill: 'none',\n 'stroke-linecap': 'round',\n 'stroke-dasharray': passed ? 'none' : '3 7',\n style: passed ? ({ pathLength: 1, animation: 'lks-path-draw 600ms var(--ease-out-expo)' } as CSSProperties) : undefined,\n })\n })),\n ...lessons.map((lesson, i) => {\n const node = layout.nodes[i]\n if (node === undefined) return null\n const h = hashStr(lesson.id)\n const locked = lesson.status === 'locked' || (lesson.kind === 'exam' && !examAllowed)\n const showRing = lesson.kind !== 'exam' && (lesson.status === 'in_progress' || lesson.status === 'mastered')\n const ringPct = lesson.status === 'mastered' ? 100 : lesson.status === 'in_progress' ? (lesson.masteryPct ?? 0) : 0\n return createElement('div', {\n key: lesson.id,\n className: 'lks-mapnode',\n style: {\n left: `${String(node.x - 55)}px`,\n top: `${String(node.y - NODE_BOX_H / 2 + 10)}px`,\n '--bob-delay': `${String((h % 40) / 10)}s`,\n '--bob-duration': `${String(5 + (h % 30) / 10)}s`,\n } as CSSProperties,\n },\n createElement('button', {\n type: 'button',\n className: `${bubbleClasses(lesson, examAllowed)}${lesson.focus ? ' selected' : ''}`,\n 'aria-disabled': locked || undefined,\n title: `${lesson.title} — ${lesson.kind === 'exam' && !examAllowed ? tr('map.exam.locked') : locked ? tr('map.node.locked') : lesson.due ? tr('map.node.due') : lesson.title}`,\n onClick: () => { if (!locked) onJump(lesson.id) },\n },\n showRing\n ? createElement('svg', { className: 'lks-bubble-ring', viewBox: '0 0 56 56', 'aria-hidden': 'true' },\n createElement('circle', { cx: 28, cy: 28, r: 25, fill: 'none', stroke: 'rgb(255 255 255 / 0.2)', 'stroke-width': 2.5 }),\n createElement('circle', {\n cx: 28, cy: 28, r: 25, fill: 'none',\n stroke: lesson.status === 'mastered' ? '#ffc800' : 'white',\n 'stroke-width': 2.5, 'stroke-linecap': 'round',\n 'stroke-dasharray': `${String((ringPct / 100) * 157)} 157`,\n }))\n : null,\n lesson.kind === 'exam'\n ? (locked\n ? createElement(IconLockFill16, { size: 22, className: 'lks-bubble-glyph dim' })\n : createElement(IconGoalOutline16, { size: 24, className: 'lks-bubble-glyph' }))\n : lesson.status === 'locked'\n ? createElement(IconLockFill16, { size: 20, className: 'lks-bubble-glyph dim' })\n : lesson.status === 'mastered'\n ? createElement(IconCrownFill16, { size: 24, className: 'lks-bubble-glyph' })\n : lesson.status === 'in_progress'\n ? createElement(IconBookFill16, { size: 24, className: 'lks-bubble-glyph' })\n : createElement(IconStarFill16, { size: 24, className: 'lks-bubble-glyph' }),\n lesson.due && !locked && lesson.kind !== 'exam'\n ? createElement('span', { className: 'lks-bubble-due', 'aria-label': tr('map.node.due') }, '!')\n : null),\n lesson.focus\n ? createElement('div', { className: 'lks-bubble-name' }, lesson.title)\n : null)\n }))\n : null,\n )\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, IconFlameFill16, IconArrowUpFill16, IconCloseFill16,\n IconGoalOutline16, IconGlobeOutline14, IconLoadingOutline16, IconLockFill16,\n IconRefreshOutline16, IconStarFill16, IconTrashOutline16, IconWarningOutline16,\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 { toastStore, type ToastItem, type ToastSeverity } from './toast.ts'\nimport { QuizCard, type QuizData } from './quizcard.tsx'\nimport { ArtifactCard, markArtifactsSeen, unseenArtifacts, type ArtifactRow } from './artifact-cards.tsx'\nimport { showStudyToast } from './toast.ts'\nimport { Companion, useCompanionMood } from './companion.tsx'\nimport { applyHighlights, getTextModel, locateInModel } from './highlights.ts'\nimport { statusTitle, quizOptions, sectionDefaultOpen, mergeRailSearch, effectiveOpen, pickNarrowPane } from './views.tsx'\nimport { MapSectionView, pickSky } from './maprail.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\n/** Toast severity glyph (P6): the state color rides the icon, text stays ink. */\nfunction toastIcon(severity: ToastSeverity): ReactNode {\n if (severity === 'success') return createElement(IconStarFill16, { size: 14, className: 'lks-toast-glyph ok' })\n if (severity === 'warning') return createElement(IconWarningOutline16, { size: 14, className: 'lks-toast-glyph warn' })\n if (severity === 'error') return createElement(IconWarningOutline16, { size: 14, className: 'lks-toast-glyph err' })\n if (severity === 'info') return createElement(IconGlobeOutline14, { size: 13, className: 'lks-toast-glyph info' })\n return null\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 [narrowPane, setNarrowPane] = useState<'rail' | 'chat' | 'note'>(pickNarrowPane(null))\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 review nudge (upstream review.nudge): due reviews pull the learner\n // back — one toast per panel open, never repeated while it stays open.\n const nudged = useRef(false)\n useEffect(() => {\n if (nudged.current || (data?.dueCount ?? 0) === 0) return\n nudged.current = true\n showStudyToast(tr('review.nudge', { n: data!.dueCount }), { severity: 'warning', action: { label: tr('review.nudge.go'), onClick: () => {\n const first = data?.due[0]\n if (first !== undefined) void setFocus(first.lessonId).catch(() => { /* the poll will resync */ })\n } } })\n }, [data?.dueCount, data?.due, setFocus])\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 companion = useCompanionMood()\n const setCompanionEvent = companion.emit\n const progress = data?.progress ?? null\n // The upstream v0.6 ladder: rail full-height on surface-rail; the right half\n // = floating app-header + the chat/notebook row (chat surface-1, notebook\n // surface-2) — depth by color step, no borders.\n const body: ReactNode = createElement('div', { className: 'lks14-body', 'data-pane': narrowPane },\n createElement(CourseRail, { data, activate, setFocus, searchLessons, deleteCourse, send }),\n createElement('div', { className: 'lks14-righthalf' },\n createElement('div', { className: 'lks14-appheader' },\n createElement('span', { className: 'lks-hdr-title' }, lesson?.courseTitle ?? tr('tab.label')),\n createElement('span', { className: 'lks-hdr-xp', title: tr('header.xp', { xp: progress?.totalXp ?? 0 }) },\n createElement('span', { className: 'lks-hdr-stat lks-hdr-glyph' }, createElement(IconBoltFill16, { size: 14 })),\n createElement('span', { className: 'lks-hdr-xpbar' }, createElement('i', { style: { width: `${Math.min(100, progress?.levelPct ?? 0)}%` } }))),\n createElement('span', { className: 'lks-hdr-stat lks-hdr-streak', title: tr('header.streak') },\n createElement('span', { className: 'lks-hdr-glyph' }, createElement(IconFlameFill16, { size: 14 })),\n String(progress?.streak ?? 0)),\n createElement('span', { className: 'lks-hdr-stat', title: tr('header.level') }, `Lv${String(progress?.level ?? 1)}`),\n ),\n createElement('div', { className: 'lks14-row' },\n createElement(ChatPane, { data, lesson, rows, feedAttached, bound: boundId !== null, busy, sendError, draft, setDraft, send, setMode, narrowPane, companionEvent: setCompanionEvent }),\n createElement(NotebookPane, { data, deleteNote, send, companionEvent: setCompanionEvent }),\n ),\n ),\n )\n return createElement('div', { className: 'lks14 lks-ui', 'data-lks-panel': '' },\n createElement(Companion, { mood: companion.mood, onPoke: () => {\n setCompanionEvent('poke')\n showStudyToast(tr('companion.poked'), { severity: 'success' })\n } }),\n createElement('div', { className: 'lks14-switch' },\n ...(['rail', 'chat', 'note'] as const).map(pane => createElement('button', {\n key: pane,\n className: `lks14-switch-btn${narrowPane === pane ? ' on' : ''}`,\n 'aria-pressed': String(narrowPane === pane),\n onClick: () => { setNarrowPane(pane) },\n }, tr(`pane.${pane}`))),\n ),\n body, createElement(StudyToastStack))\n}\n\n/** P6: the panel's toast stack (upstream Toast port) — severity capsules,\n * top-center, auto-dismiss with an exit-animation handshake; the store clears\n * on unmount so no toast outlives the panel. */\nfunction StudyToastStack(): ReactNode {\n const [items, setItems] = useState<readonly ToastItem[]>([])\n useEffect(() => {\n const update = (): void => { setItems([...toastStore.getSnapshot()]) }\n update()\n const unsubscribe = toastStore.subscribe(update)\n return () => {\n unsubscribe()\n toastStore.clear()\n }\n }, [])\n return createElement('div', { className: 'lks-toasts', role: 'region', 'aria-live': 'polite', 'aria-label': tr('toast.region') },\n ...items.map(t => createElement('div', {\n key: t.id,\n className: `lks-toast${t.exiting ? ' exiting' : ''}`,\n 'data-severity': t.severity,\n onAnimationEnd: t.exiting ? () => { toastStore.finish(t.id) } : undefined,\n },\n toastIcon(t.severity),\n createElement('span', { className: 'lks-toast-text' }, t.message),\n t.action === undefined ? null : createElement('button', {\n className: 'lks-toast-action',\n onClick: () => {\n t.action?.onClick?.()\n toastStore.startExit(t.id)\n },\n }, t.action.label),\n createElement('button', {\n className: 'lks-toast-close',\n 'aria-label': tr('toast.close'),\n onClick: () => { toastStore.startExit(t.id) },\n }, createElement(IconCloseFill16, { size: 12 })),\n )))\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 [searchRows, setSearchRows] = useState<ReturnType<typeof mergeRailSearch>>([])\n const [sectionOverrides, setSectionOverrides] = useState<Record<string, boolean>>({})\n const toggleSection = (title: string, next: boolean): void => {\n setSectionOverrides(cur => ({ ...cur, [title]: next }))\n }\n const [confirmDelete, setConfirmDelete] = useState(false)\n const [error, setError] = useState<string | null>(null)\n // B1: the upstream rail frame — a map/import tab capsule over two sliding\n // panes; search and review open as full-rail overlays from the title card.\n const [panel, setPanel] = useState<'map' | 'import'>('map')\n const [searchOpen, setSearchOpen] = useState(false)\n const [reviewOpen, setReviewOpen] = useState(false)\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 // Live search: title hits from the loaded tree + debounced full-text hits\n // merge into the results panel (upstream CourseSearchPanel semantics).\n useEffect(() => {\n if (query.trim().length < 2 || data === null) {\n setSearchRows([])\n return\n }\n let cancelled = false\n const timer = setTimeout(() => {\n void searchLessons(query).then(textHits => {\n if (cancelled) return\n const tree = data.courses.find(c => c.courseId === (data.courses.some(x => x.courseId === selectedCourse) ? selectedCourse : data.courses[0]?.courseId))\n const lessons = (tree?.sections ?? []).flatMap(s => s.lessons)\n setSearchRows(mergeRailSearch(query, lessons.map(l => ({ id: l.id, title: l.title, status: l.status, kind: l.kind })), textHits))\n }).catch(() => { if (!cancelled) setSearchRows([]) })\n }, 250)\n return () => { cancelled = true; clearTimeout(timer) }\n }, [query, selectedCourse, data, searchLessons])\n\n const courseId = data !== null && data.courses.length > 0\n ? (data.courses.some(c => c.courseId === selectedCourse) ? selectedCourse : data.courses[0]!.courseId)\n : null\n const course = data?.courses.find(c => c.courseId === courseId) ?? null\n\n // The floating topbar: tab capsule + (map pane) the glass title card.\n const topbar: ReactNode = data === null ? null : createElement('div', { className: 'lks14-railtop' },\n createElement('div', { className: 'lks-railtabs' },\n ...(['map', 'import'] as const).map(tab => createElement('button', {\n key: tab,\n className: `lks-railtab${panel === tab ? ' on' : ''}`,\n 'aria-pressed': String(panel === tab),\n onClick: () => { setPanel(tab) },\n }, tr(tab === 'map' ? 'map.tab.map' : 'map.tab.import')))),\n panel === 'map' && course !== null\n ? createElement('div', { className: 'lks14-railhead' },\n createElement('div', { className: 'lks14-railcard-row' },\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-railcard-row' },\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('span', { className: 'lks14-railpct' }, `${String(course.avgMasteryPct ?? 0)}%`),\n ),\n createElement('div', { className: 'lks14-railcard-row' },\n createElement('button', {\n className: 'lks-railpill',\n title: tr('rail.search'),\n onClick: () => { setSearchOpen(true) },\n }, createElement(IconGlobeOutline14, { size: 13 }), tr('map.search.label')),\n createElement('button', {\n className: `lks-railpill${data.dueCount > 0 ? ' due' : ''}`,\n title: data.dueCount > 0 ? tr('rail.due', { count: data.dueCount }) : tr('map.review.label'),\n onClick: () => { setReviewOpen(true) },\n }, createElement(IconBookFill16, { size: 13 }), tr('map.review.label'),\n data.dueCount > 0 ? createElement('span', { className: 'lks-railpill-n' }, String(data.dueCount)) : null),\n ),\n )\n : null,\n )\n\n // The map pane: sections under the floating chrome (pt reserves its height).\n const mapPane: ReactNode = data === null\n ? createElement('div', { className: 'lks14-empty' }, tr('loading'))\n : course === null\n ? null\n : createElement('div', { className: 'lks14-railscroll' },\n createElement('div', { className: 'lks-mapsec-list' },\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() !== '' || effectiveOpen(section.title, sectionDefaultOpen(section), sectionOverrides)\n return [createElement(MapSectionView, {\n key: section.title,\n section: { title: section.title, index: section.index, lessons },\n examAllowed,\n open,\n onToggle: () => { toggleSection(section.title, !open) },\n // Upstream alignment: tapping a bubble only FOCUSES the lesson —\n // the state-side attempt runs host-side, zero LLM traffic.\n onJump: (id: string) => { void setFocus(id).catch(reportError) },\n })]\n })))\n\n // The import pane: course rows (multi-course), the URL row, the demo course.\n const importPane: ReactNode = data === null ? null : createElement('div', { className: 'lks14-railpane-import' },\n data.courses.length > 1\n ? createElement('div', { className: 'lks14-raillist' },\n ...data.courses.map(c => createElement('button', {\n key: c.courseId,\n className: `lks14-railcourse${c.courseId === courseId ? ' on' : ''}`,\n onClick: () => { setSelectedCourse(c.courseId); setPanel('map') },\n },\n createElement('span', { className: 'lks14-railcourse-title' }, c.title),\n createElement('span', { className: 'lks14-railcourse-sub' }, tr('rail.mastered', { mastered: c.mastered, total: c.total })),\n )))\n : null,\n data.courses.length === 0\n ? createElement('div', { className: 'lks14-empty' }, tr('rail.empty.title'))\n : null,\n createElement('button', {\n className: 'lks-btn ghost lks14-raildemo',\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 // Search overlay: pill opens it; Esc/Enter/click-through close or jump.\n const searchOverlay: ReactNode = searchOpen && data !== null\n ? createElement('div', { className: 'lks-railoverlay' },\n createElement('div', { className: 'lks-railoverlay-head' },\n createElement('input', {\n className: 'lks14-search',\n type: 'search',\n placeholder: tr('rail.search'),\n value: query,\n autoFocus: true,\n onChange: (e: { target: { value: string } }) => { setQuery(e.target.value) },\n onKeyDown: (e: ReactKeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'Escape') { setQuery(''); setSearchRows([]); setSearchOpen(false) }\n if (e.key !== 'Enter' || query.trim() === '') return\n const first = searchRows[0]\n if (first !== undefined) {\n setQuery('')\n setSearchRows([])\n setSearchOpen(false)\n void setFocus(first.lessonId).catch(reportError)\n }\n },\n }),\n createElement('button', {\n className: 'lks-btn ghost',\n onClick: () => { setQuery(''); setSearchRows([]); setSearchOpen(false) },\n }, tr('map.overlay.close'))),\n searchRows.length > 0\n ? createElement('div', { className: 'lks14-searchpanel' },\n ...searchRows.map(row => createElement('button', {\n key: row.lessonId,\n className: 'lks14-searchrow',\n title: tr('rail.search.jump'),\n onClick: () => {\n setQuery('')\n setSearchRows([])\n setSearchOpen(false)\n void setFocus(row.lessonId).catch(reportError)\n },\n },\n createElement('span', { className: 'lks14-searchrow-title' }, row.title),\n row.courseTitle !== '' ? createElement('span', { className: 'lks14-searchrow-course' }, row.courseTitle) : null,\n row.snippet !== '' ? createElement('span', { className: 'lks14-searchrow-snip' }, row.snippet) : null)))\n : null)\n : null\n\n // Review overlay: the due list + start-review action (upstream's review entry).\n const reviewOverlay: ReactNode = reviewOpen && data !== null\n ? createElement('div', { className: 'lks-railoverlay' },\n createElement('div', { className: 'lks-railoverlay-head' },\n createElement('span', { className: 'lks14-railtitle' }, data.dueCount > 0 ? tr('rail.due', { count: data.dueCount }) : tr('map.review.label')),\n createElement('button', {\n className: 'lks-btn ghost',\n onClick: () => { setReviewOpen(false) },\n }, tr('map.overlay.close'))),\n ...data.due.map(d => createElement('button', {\n key: d.lessonId,\n className: 'lks14-dueitem',\n title: tr('review.jump'),\n onClick: () => { void setFocus(d.lessonId).catch(reportError) },\n },\n createElement('span', null, d.lessonTitle),\n d.overdueDays > 0 ? createElement('span', { className: 'lks14-over' }, tr('rail.due.over', { days: d.overdueDays })) : null)),\n data.dueCount > 0\n ? createElement('button', {\n className: 'lks-btn primary',\n onClick: () => { setReviewOpen(false); send(tr('prompt.review')) },\n }, tr('rail.due.start'))\n : createElement('div', { className: 'lks14-empty' }, tr('rail.due.none')))\n : null\n\n // Upstream: no course → the import pane is the home pane.\n const effectivePanel = data !== null && data.courses.length === 0 ? 'import' : panel\n return createElement('div', { className: `lks14-col lks14-rail${courseId !== null ? ` lks-sky-${pickSky(courseId)}` : ''}` },\n topbar,\n createElement('div', { className: 'lks14-railbody' },\n createElement('div', {\n className: 'lks14-railtrack',\n style: { transform: effectivePanel === 'map' ? 'translateX(0)' : 'translateX(-50%)' },\n },\n createElement('div', { className: 'lks14-railpane' }, mapPane),\n createElement('div', { className: 'lks14-railpane' }, importPane),\n ),\n ),\n searchOverlay,\n reviewOverlay,\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, narrowPane, companionEvent }: {\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 narrowPane: 'rail' | 'chat' | 'note'\n companionEvent: (event: 'talk-start' | 'talk-end' | 'celebrate' | 'encourage' | 'decay' | 'poke') => 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 const proposal = data?.pendingProposals[0] ?? null\n return createElement('div', { className: 'lks14-col lks14-chat' },\n proposal !== null\n ? createElement('div', { className: 'lks-propbanner' },\n createElement(IconCrownFill16, { size: 14 }),\n createElement('span', { className: 'lks-propbanner-why' },\n tr('proposal.banner', { lesson: proposal.lessonTitle }), ' — ', proposal.rationale),\n createElement('button', {\n className: 'lks-btn primary',\n style: { padding: '4px 12px', fontSize: '12.5px' },\n onClick: () => { send(tr('proposal.accept.msg', { lesson: proposal.lessonTitle })) },\n }, tr('proposal.accept')),\n createElement('button', {\n className: 'lks-btn ghost',\n style: { padding: '4px 12px', fontSize: '12.5px' },\n onClick: () => { send(tr('proposal.decline.msg', { lesson: proposal.lessonTitle })) },\n }, tr('proposal.decline')))\n : null,\n // B4: upstream has no per-column header — a thin current-lesson row instead\n // (ThreadSwitcher empty-state style); the mode pills live in the composer.\n lesson !== null || !dormant\n ? createElement('div', { className: 'lks14-lessonrow' },\n lesson !== null ? createElement('span', null, lesson.title) : createElement('span', null, tr('col.tutor')))\n : null,\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-emptycard' },\n createElement('div', { className: 'lks14-emptycard-title' }, dormant ? tr('tutor.dormant') : tr('tutor.empty')),\n createElement('div', { className: 'lks14-emptycard-hint' }, tr('tutor.empty.hint')),\n !dormant\n ? createElement('button', {\n className: 'lks-btn primary',\n title: tr('chat.start.hint'),\n onClick: () => { send(starters[0]?.message ?? tr('prompt.start')) },\n }, tr('chat.start'))\n : null)\n : rows.map((row, i) => chatRow(row, !dormant && i === lastAssistant ? { send } : undefined)),\n rows.length > 0 && rows[rows.length - 1]!.role === 'user'\n ? createElement('div', { className: 'lks14-thinking' }, createElement('i', null), createElement('i', null), createElement('i', null))\n : null,\n ),\n ...(lesson?.artifacts ?? [])\n .filter(a => a.artifactType === 'quiz')\n .map(a => createElement(QuizCard, {\n key: a.id,\n lessonId: lesson.lessonId,\n artifactId: a.id,\n data: a.data as unknown as QuizData,\n masteryPct: lesson.masteryPct,\n send,\n onFinished: (allCorrect: boolean) => { companionEvent(allCorrect ? 'celebrate' : 'encourage') },\n })),\n ...(lesson?.artifacts ?? [])\n .filter(a => a.artifactType !== 'quiz')\n .map(a => createElement(ArtifactCard, { key: a.id, artifact: a as ArtifactRow, send })),\n // B7: starters appear only after the conversation starts (upstream gates\n // them on messages>0 — the empty state carries the CTA instead).\n rows.length > 0\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 : null,\n createElement('div', { className: 'lks14-composer' },\n createElement('div', { className: 'lks14-composer-card' },\n // B4: the soul pills are the composer's first row (upstream ChatComposer).\n createElement('div', { className: 'lks14-soulrow' },\n createElement('span', { className: 'lks14-soullabel' }, tr('mode.label')),\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 }, mode.id === 'direct' ? createElement(IconBoltFill16, { size: 12 }) : mode.id === 'guide' ? createElement(IconGoalOutline16, { size: 12 }) : createElement(IconBookFill16, { size: 12 }), tr(mode.labelKey))))),\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-send',\n 'aria-label': tr('composer.send'),\n disabled: busy || dormant || draft.trim() === '',\n onClick: () => { send(draft) },\n }, busy ? createElement(IconLoadingOutline16, { size: 16, className: 'lks-spin' }) : createElement(IconArrowUpFill16, { size: 18 })),\n ),\n sendError !== null || error !== null\n ? createElement('div', { className: 'lks-propcard-err' }, sendError ?? error)\n : null,\n ),\n )\n}\n\n/** 右栏:the notebook — 讲解/概念图/笔记 tabs (upstream NotebookPanel arrangement). */\nfunction NotebookPane({ data, deleteNote, send, companionEvent }: { data: StudyData; deleteNote: (lessonId: string, noteId: string) => Promise<void>; send: PanelSend; companionEvent: (event: 'talk-start' | 'talk-end' | 'celebrate' | 'encourage' | 'decay' | 'poke') => 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, addUserNote, recordReview } = 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 // Canvas semantics (upstream): artifacts sediment into the notebook — the\n // 笔记 tab wears a badge for unseen ones, a toast announces each arrival\n // once, and opening the tab marks them seen.\n const artifacts: ArtifactRow[] = (lesson?.artifacts ?? []) as ArtifactRow[]\n const [, setUnseenTick] = useState(0)\n const unseen = unseenArtifacts(lesson?.lessonId ?? '', artifacts)\n const lastAnnounced = useRef<ReadonlySet<string>>(new Set())\n useEffect(() => {\n const fresh = unseen.filter(id => !lastAnnounced.current.has(id))\n if (fresh.length === 0) return\n lastAnnounced.current = new Set([...lastAnnounced.current, ...fresh])\n showStudyToast(tr('artifact.sedimented'), { severity: 'info' })\n setUnseenTick(Date.now())\n }, [unseen.join(','), lesson?.lessonId]) // eslint-disable-line react-hooks/exhaustive-deps\n const heavy = artifacts.filter(a => a.artifactType === 'compare_table' || a.artifactType === 'diagram' || a.artifactType === 'code_walkthrough')\n const latestHeavy = heavy.length > 0 ? heavy[heavy.length - 1]! : null\n\n // ── 画线笔记 (P2, upstream selection flow) ─────────────────────────────\n // The popover appears on pointerup / settle (never mid-drag), hides 250ms\n // after the selection clears (a tap on the button clears first — an\n // immediate hide would swallow the click).\n const [quoteBtn, setQuoteBtn] = useState<{ x: number; y: number; text: string; surrounding: string } | null>(null)\n const recordQuotes = (lesson?.notes ?? []).filter(n => n.zone === 'record' && n.quote !== null && n.quote.trim().length >= 2).map(n => n.quote!)\n\n const evaluateSelection = (): void => {\n const prose = proseRef.current\n const selection = window.getSelection()\n if (prose === null || selection === null || selection.rangeCount === 0) return\n const text = selection.toString().trim()\n if (text.length < 2 || text.length > 600) return\n const range = selection.getRangeAt(0)\n if (prose.contains(range.commonAncestorContainer) !== true) return\n const model = getTextModel(prose)\n const rect = range.getBoundingClientRect()\n const box = prose.getBoundingClientRect()\n const modelText = model.text\n const idx = locateInModel(model, text, undefined)?.start ?? modelText.indexOf(text)\n const surrounding = idx >= 0 ? modelText.slice(Math.max(0, idx - 30), idx + text.length + 30) : text\n setQuoteBtn({\n x: Math.min(Math.max(rect.left - box.left + rect.width / 2, 90), Math.max(box.width - 90, 90)),\n y: rect.top - box.top - 38,\n text,\n surrounding,\n })\n }\n\n useEffect(() => {\n const SETTLE = 250\n let settleTimer: ReturnType<typeof setTimeout> | null = null\n let hideTimer: ReturnType<typeof setTimeout> | null = null\n let gesture = false\n const selectionHasText = (): boolean => (window.getSelection()?.toString().trim().length ?? 0) >= 2\n const onChange = (): void => {\n if (selectionHasText()) {\n if (hideTimer !== null) { clearTimeout(hideTimer); hideTimer = null }\n setQuoteBtn(cur => (cur === null ? cur : null))\n if (settleTimer !== null) clearTimeout(settleTimer)\n settleTimer = setTimeout(() => {\n settleTimer = null\n if (!gesture && selectionHasText()) evaluateSelection()\n }, SETTLE)\n } else if (hideTimer === null) {\n hideTimer = setTimeout(() => { setQuoteBtn(cur => (cur === null ? cur : null)) }, 250)\n }\n }\n const onDown = (e: PointerEvent): void => {\n const el = e.target as Element | null\n if (el?.closest?.('[data-lks-quote-btn]') !== null) return\n gesture = true\n }\n const onUp = (): void => {\n gesture = false\n if (settleTimer !== null) { clearTimeout(settleTimer); settleTimer = null }\n if (hideTimer === null && selectionHasText()) evaluateSelection()\n }\n document.addEventListener('selectionchange', onChange)\n window.addEventListener('pointerdown', onDown, true)\n window.addEventListener('pointerup', onUp, true)\n window.addEventListener('pointercancel', onUp, true)\n return () => {\n document.removeEventListener('selectionchange', onChange)\n window.removeEventListener('pointerdown', onDown, true)\n window.removeEventListener('pointerup', onUp, true)\n window.removeEventListener('pointercancel', onUp, true)\n if (settleTimer !== null) clearTimeout(settleTimer)\n if (hideTimer !== null) clearTimeout(hideTimer)\n }\n }, [])\n\n // Persisted highlights: record-zone quotes anchor marks; applied after the\n // enhance pass settles (its DOM mutations change the text model).\n const [enhanceTick, setEnhanceTick] = useState(0)\n const [rateBusy, setRateBusy] = useState(false)\n\n const startReading = (): void => {\n if (lesson === null || lesson.speechText.trim() === '') return\n companionEvent('talk-start')\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 companionEvent('talk-end')\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)\n .catch(() => { /* degrade */ })\n .finally(() => {\n // the highlight model must be computed post-enhance (shiki/katex/mermaid\n // mutate the DOM, changing the text model)\n applyHighlights(proseRef.current, recordQuotes)\n setEnhanceTick(Date.now())\n })\n }, [tab, lesson?.html]) // eslint-disable-line react-hooks/exhaustive-deps\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-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', {\n className: `lks14-viewtab${tab === 'notes' ? ' on' : ''}`,\n 'aria-pressed': String(tab === 'notes'),\n onClick: () => {\n setTab('notes')\n if (lesson !== null && unseen.length > 0) {\n markArtifactsSeen(lesson.lessonId, unseen)\n setUnseenTick(Date.now())\n }\n },\n }, tr('bb.notes'), unseen.length > 0 ? createElement('span', { className: 'lks-viewtab-badge' }, String(unseen.length)) : null),\n ),\n // B5: the lesson head sits under the tab capsule (upstream ContentTab kicker)\n tab !== 'notes'\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 : null,\n tab === 'teach'\n ? createElement('div', null,\n lesson.due ? createElement('div', { className: 'lks-ratecard', 'data-lks-rate': lesson.lessonId },\n createElement('div', { className: 'lks-ratecard-title' }, tr('review.rate.title')),\n createElement('div', { className: 'lks-ratecard-opts' },\n ...([['1', 'review.again'], ['4', 'review.remembered'], ['5', 'review.mastered']] as const).map(([q, key]) =>\n createElement('button', {\n key: q,\n className: `lks-ratecard-opt${q === '1' ? ' again' : q === '5' ? ' best' : ''}`,\n disabled: rateBusy,\n onClick: () => {\n setRateBusy(true)\n recordReview(lesson.lessonId, Number(q) as 1 | 4 | 5)\n .then(() => {\n companionEvent(Number(q) >= 4 ? 'celebrate' : 'encourage')\n showStudyToast(Number(q) >= 4 ? tr('review.done.good', { days: 1 }) : tr('review.done.again'), { severity: Number(q) >= 4 ? 'success' : 'warning' })\n setRateBusy(false)\n })\n .catch((err: unknown) => {\n setError(err instanceof Error ? err.message : String(err))\n setRateBusy(false)\n })\n },\n }, tr(key)))) ,\n ) : 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-prosewrap' },\n createElement('div', { className: 'lks14-prose', ref: proseRef, dangerouslySetInnerHTML: { __html: lesson.html } }),\n quoteBtn !== null\n ? createElement('div', {\n className: 'lks-quote-btn',\n 'data-lks-quote-btn': '',\n style: { left: `${quoteBtn.x}px`, top: `${quoteBtn.y}px` },\n },\n createElement('button', {\n onClick: () => {\n const truncated = quoteBtn.text.length > 200 ? `${quoteBtn.text.slice(0, 200)}…` : quoteBtn.text\n send(tr('note.quote.template', { text: truncated }))\n setQuoteBtn(null)\n window.getSelection()?.removeAllRanges()\n },\n }, tr('note.quote.ask')),\n createElement('button', {\n onClick: () => {\n if (lesson !== null) {\n void addUserNote(lesson.lessonId, quoteBtn.text).catch((err: unknown) => { setError(err instanceof Error ? err.message : String(err)) })\n void applyHighlights(proseRef.current!, [quoteBtn.text])\n showStudyToast(tr('note.saved'), { severity: 'success' })\n }\n setQuoteBtn(null)\n window.getSelection()?.removeAllRanges()\n },\n }, tr('note.quote.save')))\n : null),\n latestHeavy !== null ? createElement('div', { className: 'lks-acard-stage' },\n createElement(ArtifactCard, { artifact: latestHeavy, send: () => {} }))\n : null,\n )\n : tab === 'cmap'\n ? createElement('div', { className: 'lks14-prose', ref: diagRef })\n : createElement('div', { className: 'lks14-zones' },\n (() => {\n const understand = artifacts.filter(a => a.artifactType === 'compare_table' || a.artifactType === 'diagram' || a.artifactType === 'code_walkthrough')\n if (understand.length === 0) return null\n return createElement('div', { key: 'artifacts', className: 'lks14-zone' },\n createElement('div', { className: 'lks14-zoneh' }, tr('zone.artifacts')),\n ...understand.map(a => createElement(ArtifactCard, { key: a.id, artifact: a, send: () => {} })))\n })(),\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 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 { COMPANION_FORMS, normalizeCompanionForm, storeCompanionForm, storedCompanionForm } from './companion.tsx'\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 [companion, setCompanion] = useState(storedCompanionForm)\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.companion')),\n createElement('p', { className: 'lks-set-hint' }, tr('settings.companion.hint')),\n createElement('div', null,\n createElement('select', {\n className: 'lks-set-select',\n value: companion,\n onChange: (e: { target: { value: string } }) => { storeCompanionForm(normalizeCompanionForm(e.target.value)); setCompanion(normalizeCompanionForm(e.target.value)) },\n }, ...COMPANION_FORMS.map(f => createElement('option', { key: f.id, value: f.id }, tr(f.labelKey)))),\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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"}