dsh-diagnostic-tutor 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +316 -79
- package/lib/api.js +364 -78
- package/lib/api.js.map +1 -1
- package/lib/block-types.js +25 -0
- package/lib/block-types.js.map +1 -0
- package/lib/client.js +9952 -821
- package/lib/client.js.map +1 -1
- package/lib/diagnostics.js +170 -0
- package/lib/diagnostics.js.map +1 -0
- package/lib/execution-key.js +91 -0
- package/lib/execution-key.js.map +1 -0
- package/lib/handoff.js +44 -1
- package/lib/handoff.js.map +1 -1
- package/lib/index.js +87 -51
- package/lib/index.js.map +1 -1
- package/lib/insights.js +82 -0
- package/lib/insights.js.map +1 -0
- package/lib/interactions.js +70 -0
- package/lib/interactions.js.map +1 -0
- package/lib/journal.js +122 -0
- package/lib/journal.js.map +1 -0
- package/lib/lesson-markdown.js +72 -0
- package/lib/lesson-markdown.js.map +1 -0
- package/lib/lesson.js +152 -3
- package/lib/lesson.js.map +1 -1
- package/lib/math.js +126 -0
- package/lib/math.js.map +1 -0
- package/lib/path-api.js +140 -0
- package/lib/path-api.js.map +1 -0
- package/lib/path-runtime.js +361 -0
- package/lib/path-runtime.js.map +1 -0
- package/lib/path-tools.js +107 -0
- package/lib/path-tools.js.map +1 -0
- package/lib/path.js +179 -0
- package/lib/path.js.map +1 -0
- package/lib/plan-tools.js +89 -0
- package/lib/plan-tools.js.map +1 -0
- package/lib/plan.js +82 -0
- package/lib/plan.js.map +1 -0
- package/lib/project-session.js +48 -0
- package/lib/project-session.js.map +1 -0
- package/lib/prompt.js +4 -19
- package/lib/prompt.js.map +1 -1
- package/lib/review-sync.js +51 -0
- package/lib/review-sync.js.map +1 -0
- package/lib/review-tools.js +115 -0
- package/lib/review-tools.js.map +1 -0
- package/lib/review.js +103 -0
- package/lib/review.js.map +1 -0
- package/lib/source-schema.js +24 -0
- package/lib/source-schema.js.map +1 -0
- package/lib/source-tools.js +61 -0
- package/lib/source-tools.js.map +1 -0
- package/lib/sources.js +78 -0
- package/lib/sources.js.map +1 -0
- package/lib/state.js +353 -47
- package/lib/state.js.map +1 -1
- package/lib/tools.js +620 -123
- package/lib/tools.js.map +1 -1
- package/lib/types/api.d.ts +10 -3
- package/lib/types/api.d.ts.map +1 -1
- package/lib/types/block-types.d.ts +12 -0
- package/lib/types/block-types.d.ts.map +1 -0
- package/lib/types/contract.d.ts +81 -7
- package/lib/types/contract.d.ts.map +1 -1
- package/lib/types/diagnostics.d.ts +95 -0
- package/lib/types/diagnostics.d.ts.map +1 -0
- package/lib/types/execution-key.d.ts +59 -0
- package/lib/types/execution-key.d.ts.map +1 -0
- package/lib/types/handoff.d.ts +2 -2
- package/lib/types/handoff.d.ts.map +1 -1
- package/lib/types/index.d.ts +19 -14
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/insights.d.ts +57 -0
- package/lib/types/insights.d.ts.map +1 -0
- package/lib/types/interactions.d.ts +23 -0
- package/lib/types/interactions.d.ts.map +1 -0
- package/lib/types/journal.d.ts +57 -0
- package/lib/types/journal.d.ts.map +1 -0
- package/lib/types/lesson-markdown.d.ts +11 -0
- package/lib/types/lesson-markdown.d.ts.map +1 -0
- package/lib/types/lesson.d.ts +697 -5
- package/lib/types/lesson.d.ts.map +1 -1
- package/lib/types/math.d.ts +50 -0
- package/lib/types/math.d.ts.map +1 -0
- package/lib/types/path-api.d.ts +5 -0
- package/lib/types/path-api.d.ts.map +1 -0
- package/lib/types/path-runtime.d.ts +1558 -0
- package/lib/types/path-runtime.d.ts.map +1 -0
- package/lib/types/path-tools.d.ts +4 -0
- package/lib/types/path-tools.d.ts.map +1 -0
- package/lib/types/path.d.ts +744 -0
- package/lib/types/path.d.ts.map +1 -0
- package/lib/types/plan-tools.d.ts +4 -0
- package/lib/types/plan-tools.d.ts.map +1 -0
- package/lib/types/plan.d.ts +53 -0
- package/lib/types/plan.d.ts.map +1 -0
- package/lib/types/project-session.d.ts +33 -0
- package/lib/types/project-session.d.ts.map +1 -0
- package/lib/types/prompt.d.ts.map +1 -1
- package/lib/types/review-sync.d.ts +17 -0
- package/lib/types/review-sync.d.ts.map +1 -0
- package/lib/types/review-tools.d.ts +4 -0
- package/lib/types/review-tools.d.ts.map +1 -0
- package/lib/types/review.d.ts +65 -0
- package/lib/types/review.d.ts.map +1 -0
- package/lib/types/source-schema.d.ts +31 -0
- package/lib/types/source-schema.d.ts.map +1 -0
- package/lib/types/source-tools.d.ts +4 -0
- package/lib/types/source-tools.d.ts.map +1 -0
- package/lib/types/sources.d.ts +33 -0
- package/lib/types/sources.d.ts.map +1 -0
- package/lib/types/state.d.ts +607 -23
- package/lib/types/state.d.ts.map +1 -1
- package/lib/types/tools.d.ts +3 -1
- package/lib/types/tools.d.ts.map +1 -1
- package/lib/types/udt.d.ts +20 -24
- package/lib/types/udt.d.ts.map +1 -1
- package/lib/types/version.d.ts +11 -0
- package/lib/types/version.d.ts.map +1 -0
- package/lib/types/vocabulary.d.ts +2 -2
- package/lib/udt.js +72 -19
- package/lib/udt.js.map +1 -1
- package/lib/version.js +26 -0
- package/lib/version.js.map +1 -0
- package/package.json +27 -22
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useState","realApi.fetchOverview","realApi.fetchNode","realApi.fetchLesson","realApi.startFocus","realApi.reportObserved","realApi.downloadExport","realApi.resetState","useState","useRef","useCallback","useMemo"],"sources":["../src/client/blocks.tsx","../src/client/model.ts","../src/client/api.ts","../src/client/use-learning.ts","../src/client/app.tsx","../src/client/tab.tsx","../src/client/styles.ts","../src/client/index.tsx"],"sourcesContent":["/**\n * Block renderers.\n *\n * The registry is the whole extension story: a block type is a key in\n * `BLOCK_RENDERERS`, so Formula, Code, Comparison, Practice or Resource arrive\n * as one more entry and never as a rewrite of the lesson renderer.\n *\n * Unknown types are the interesting case. A lesson authored by a newer host\n * will contain blocks this build has never heard of, and that must degrade —\n * the block is announced by type and left legible, rather than throwing inside\n * React and blanking the whole panel.\n *\n * Note what is absent: no percentage, no score, no stars, no progress bar.\n * State is always a word from the skill's vocabulary plus a coloured mark.\n */\n\nimport { useState, type ReactNode } from 'react'\n\nimport type { Block, HandoffView, NextStepView } from '../contract.js'\n\n/** Props every renderer receives. */\nexport interface BlockRenderProps<B extends Block = Block> {\n block: B\n}\n\ntype Renderer = (props: BlockRenderProps) => ReactNode\n\n/**\n * Inline formatting: `**bold**`, `` `code` ``, and inline math.\n *\n * Math is handled because the teaching brain writes it by convention —\n * `\\(...\\)` inline and `\\[...\\]` display are the skill's own rule — and a\n * STEM surface that prints the delimiters verbatim is unreadable.\n *\n * This is *styling*, not typesetting: the span is set apart and given a\n * monospace face so the expression is legible. Real math rendering needs a\n * typesetter (KaTeX or MathML), which is a later decision, not a silent gap.\n */\nconst INLINE_PATTERN = /(\\*\\*[^*]+\\*\\*|`[^`]+`|\\\\\\([^)]*\\\\\\))/g\n\nfunction inline(text: string): ReactNode[] {\n return text\n .split(INLINE_PATTERN)\n .filter(Boolean)\n .map((part, index) => {\n if (part.startsWith('**') && part.endsWith('**')) {\n return <strong key={index}>{part.slice(2, -2)}</strong>\n }\n if (part.startsWith('`') && part.endsWith('`')) return <code key={index}>{part.slice(1, -1)}</code>\n if (part.startsWith('\\\\(') && part.endsWith('\\\\)')) {\n return (\n <span className=\"dt-math\" key={index}>\n {part.slice(2, -2)}\n </span>\n )\n }\n return part\n })\n}\n\n/** One line of a text body, classified. */\ntype Line =\n | { kind: 'heading'; level: 2 | 3; text: string }\n | { kind: 'bullet'; text: string }\n | { kind: 'ordered'; marker: string; text: string }\n | { kind: 'math'; text: string }\n | { kind: 'text'; text: string }\n\n/**\n * Classify one line.\n *\n * The teaching brain writes ordinary light markdown — `###` headings, `-`\n * bullets, `1.` steps — because that is how a person writes an explanation.\n * Printing the markers verbatim is what makes a lesson look like a Markdown\n * renderer instead of a lesson, so they are recognised here.\n *\n * This adds **no block type**: it is how the existing text and check blocks are\n * displayed, which is the part of the surface the learner actually reads.\n */\nfunction classify(line: string): Line {\n const heading = /^(#{2,3})\\s+(.*)$/.exec(line)\n if (heading) return { kind: 'heading', level: heading[1] === '##' ? 2 : 3, text: heading[2]! }\n // Display math is written with escaped delimiters: \\[...\\]\n const trimmed = line.trim()\n if (trimmed.startsWith('\\\\[') && trimmed.endsWith('\\\\]')) {\n return { kind: 'math', text: trimmed.slice(2, -2).trim() }\n }\n const bullet = /^\\s*[-*•]\\s+(.*)$/.exec(line)\n if (bullet) return { kind: 'bullet', text: bullet[1]! }\n const ordered = /^\\s*(\\d+)[.)]\\s+(.*)$/.exec(line)\n if (ordered) return { kind: 'ordered', marker: ordered[1]!, text: ordered[2]! }\n return { kind: 'text', text: line }\n}\n\n/** Group consecutive lines into runs that render as one element. */\nfunction toRuns(md: string): Line[][] {\n const runs: Line[][] = []\n let current: Line[] = []\n let currentKind: Line['kind'] | null = null\n const flush = (): void => {\n if (current.length > 0) runs.push(current)\n current = []\n currentKind = null\n }\n for (const raw of md.split(/\\n/)) {\n const line = raw.trimEnd()\n if (line.trim().length === 0) {\n flush()\n continue\n }\n const parsed = classify(line)\n // Three things join the run in progress: a wrapped prose line, and a bullet\n // or ordered step continuing a list of the same kind. Everything else —\n // headings, display math, a change of list kind — starts a new run, so a\n // heading never swallows the prose under it.\n const continues =\n parsed.kind === 'text'\n ? currentKind === 'text'\n : (parsed.kind === 'bullet' || parsed.kind === 'ordered') && currentKind === parsed.kind\n if (!continues) flush()\n current.push(parsed)\n currentKind = parsed.kind\n }\n flush()\n return runs\n}\n\n/**\n * Render a text body as a lesson rather than as source.\n *\n * Blank-line separated runs become paragraphs, `###` becomes a real heading,\n * `-` and `1.` become real lists, and `\\[...\\]` becomes a display-math panel.\n */\nfunction Paragraphs({ md }: { md: string }): ReactNode {\n return (\n <>\n {toRuns(md).map((run, index) => {\n const first = run[0]!\n if (first.kind === 'heading') {\n const Tag = first.level === 2 ? 'h3' : 'h4'\n return (\n <Tag className={`dt-md-h${first.level}`} key={index}>\n {inline(first.text)}\n </Tag>\n )\n }\n if (first.kind === 'math') {\n return (\n <div className=\"dt-math-block\" key={index}>\n {first.text}\n </div>\n )\n }\n if (first.kind === 'bullet') {\n return (\n <ul className=\"dt-md-ul\" key={index}>\n {run.map((line, inner) => (\n <li key={inner}>{inline(line.text)}</li>\n ))}\n </ul>\n )\n }\n if (first.kind === 'ordered') {\n return (\n <ol className=\"dt-md-ol\" key={index}>\n {run.map((line, inner) => (\n <li key={inner}>{inline(line.text)}</li>\n ))}\n </ol>\n )\n }\n return (\n <p className=\"dt-block-md\" key={index}>\n {inline(run.map((line) => line.text).join(' '))}\n </p>\n )\n })}\n </>\n )\n}\n\nfunction TextBlockView({ block }: BlockRenderProps): ReactNode {\n if (block.type !== 'text') return null\n return (\n <div className=\"dt-block dt-block-text\">\n <Paragraphs md={block.content.md} />\n </div>\n )\n}\n\nfunction ExampleBlockView({ block }: BlockRenderProps): ReactNode {\n if (block.type !== 'example') return null\n return (\n <div className=\"dt-block dt-block-example\">\n <p className=\"dt-block-label\">Worked example</p>\n <h4>{block.content.title}</h4>\n <ol>\n {block.content.steps.map((step, index) => (\n <li key={index}>{inline(step)}</li>\n ))}\n </ol>\n {block.content.takeaway !== undefined && (\n <p className=\"dt-takeaway\">{inline(block.content.takeaway)}</p>\n )}\n </div>\n )\n}\n\nfunction DiagramBlockView({ block }: BlockRenderProps): ReactNode {\n if (block.type !== 'diagram') return null\n const isMermaid = block.content.format === 'mermaid'\n return (\n <div className=\"dt-block dt-block-diagram\">\n <p className=\"dt-block-label\">Diagram</p>\n {isMermaid && (\n <p className=\"dt-caption\">\n Source shown as text — this build ships no <code>mermaid</code> renderer.\n </p>\n )}\n <pre className=\"dt-pre\">{block.content.spec}</pre>\n {block.content.caption !== undefined && <p className=\"dt-caption\">{block.content.caption}</p>}\n </div>\n )\n}\n\n/**\n * The check.\n *\n * Rendered as an invitation rather than a field: this surface has no input box,\n * so the block has to make it obvious that the next move is the learner's and\n * that it happens in the chat. The wording says so instead of leaving a dead\n * question sitting in a panel.\n */\nfunction CheckBlockView({ block }: BlockRenderProps): ReactNode {\n if (block.type !== 'check') return null\n return (\n <div className=\"dt-block dt-block-check\">\n <div className=\"dt-check-head\">\n <span className=\"dt-check-tag\">Your turn</span>\n <span className=\"dt-check-where\">answer in the chat →</span>\n </div>\n <div className=\"dt-check-body\">\n <Paragraphs md={block.content.prompt} />\n </div>\n {block.content.hint !== undefined && (\n <p className=\"dt-check-hint\">\n <span>Hint</span>\n {inline(block.content.hint)}\n </p>\n )}\n </div>\n )\n}\n\n/**\n * The registry. Adding a block type means adding one entry here.\n *\n * Deliberately a plain object rather than a switch: a `Map`/record makes the\n * extension point visible and keeps the fallback impossible to forget.\n */\nexport const BLOCK_RENDERERS: Record<string, Renderer> = {\n text: TextBlockView,\n example: ExampleBlockView,\n diagram: DiagramBlockView,\n check: CheckBlockView,\n}\n\n/** Shown for a block type this build does not know. Never throws. */\nfunction UnknownBlockView({ block }: BlockRenderProps): ReactNode {\n const type = (block as { type?: unknown }).type\n return (\n <div className=\"dt-block dt-block-unknown\">\n <strong>{typeof type === 'string' ? type : 'unknown'}</strong> block — this build has no\n renderer for it. Update the plugin to see it.\n </div>\n )\n}\n\n/**\n * Render one block, degrading for anything unrecognised.\n *\n * @param props.block - the block to render.\n * @returns the rendered block.\n */\nexport function BlockView({ block }: { block: Block }): ReactNode {\n const renderer = BLOCK_RENDERERS[block.type] ?? UnknownBlockView\n return <div data-block-type={block.type}>{renderer({ block })}</div>\n}\n\n/**\n * Render a whole lesson's blocks in order.\n *\n * @param props.blocks - the blocks to render.\n * @returns the rendered list.\n */\nexport function LessonBody({ blocks }: { blocks: Block[] }): ReactNode {\n return (\n <>\n {blocks.map((block) => (\n <BlockView key={block.id} block={block} />\n ))}\n </>\n )\n}\n\n/**\n * The recommendation card.\n *\n * The learner reads *why* before they move, and nothing moves until they press\n * the button — the tutor decides, the runtime stores, the learner chooses. That\n * ordering is the whole point of the card existing rather than an automatic\n * jump.\n *\n * Wording follows the decision: a move names where it goes, a stay says so\n * plainly. No percentage, no score, no completion estimate — how far along the\n * learner is lives in the node's state and evidence, and nowhere else.\n */\nexport function NextStepCard({\n nextStep,\n onContinue,\n busy,\n}: {\n nextStep: NextStepView\n onContinue: () => void\n busy?: boolean\n}): ReactNode {\n const moves = nextStep.targetNodeId !== null\n return (\n <div className=\"dt-next\">\n <p className=\"dt-next-label\">\n <span className=\"dt-next-arrow\" aria-hidden=\"true\">\n ↓\n </span>\n Next best step\n </p>\n <p className=\"dt-next-from\">\n <span className=\"dt-next-tick\" aria-hidden=\"true\">\n ✓\n </span>\n {nextStep.fromNodeTitle} — {nextStep.action.replace(/-/g, ' ')}\n </p>\n <p className=\"dt-next-target\">\n {moves ? (\n <>\n Next: <b>{nextStep.targetNodeTitle}</b>\n </>\n ) : (\n <>\n Next: <b>Stay on {nextStep.fromNodeTitle}</b>\n </>\n )}\n </p>\n <p className=\"dt-next-why\">\n <span className=\"dt-next-why-label\">Why:</span> {nextStep.reason}\n </p>\n <button type=\"button\" className=\"dt-primary\" onClick={onContinue} disabled={busy === true}>\n {busy === true ? 'Starting…' : moves ? 'Continue learning' : 'Continue'}\n </button>\n </div>\n )\n}\n\n/**\n * The handoff progress line.\n *\n * A model turn is not instant, and a silent wait is indistinguishable from a\n * broken button — so the wait is narrated: which stage it is in, how long it\n * has been, and how many attempts. When it goes quiet the learner gets a retry\n * rather than a dead end, and retrying **never touches the focus**: the record\n * is a statement about the wait, never about where they are.\n *\n * The elapsed time is shown because it is honest. A tutor that takes ninety\n * seconds should look like a tutor that takes ninety seconds, not like a hang.\n */\nexport function HandoffLine({\n handoff,\n onRetry,\n}: {\n handoff: HandoffView\n onRetry: () => void\n}): ReactNode {\n const retryable = handoff.phase === 'failed' || handoff.phase === 'stalled'\n const seconds = Math.round(handoff.elapsedMs / 1000)\n return (\n <div className=\"dt-handoff\" data-phase={handoff.phase}>\n <span className=\"dt-handoff-dot\" aria-hidden=\"true\" />\n <span className=\"dt-handoff-label\">{handoff.label}</span>\n <span className=\"dt-handoff-time\">{seconds}s</span>\n {handoff.attempts > 1 && <span className=\"dt-handoff-try\">attempt {handoff.attempts}</span>}\n {handoff.detail !== undefined && <span className=\"dt-handoff-detail\">{handoff.detail}</span>}\n {retryable && (\n <button type=\"button\" className=\"dt-handoff-retry\" onClick={onRetry}>\n Ask again\n </button>\n )}\n </div>\n )\n}\n\n/**\n * The learner's controls over their own data.\n *\n * Both promises this project makes about state are kept here: that it is\n * **visible and exportable**, and that it can be **deleted**. An export that\n * only existed as an API route would not be a promise kept, and a delete that\n * happened on one click would be a trap — so the second click is the\n * confirmation, inline, with no dialog to dismiss by reflex.\n */\nexport function DataFooter({\n onExport,\n onReset,\n busy,\n}: {\n onExport: () => void\n onReset: () => void\n busy?: boolean\n}): ReactNode {\n const [confirming, setConfirming] = useState(false)\n\n if (confirming) {\n return (\n <div className=\"dt-data dt-data-confirm\">\n <p className=\"dt-data-ask\">\n Delete your goal, your map, every lesson and all the evidence behind it? This cannot\n be undone.\n </p>\n <div className=\"dt-data-row\">\n <button\n type=\"button\"\n className=\"dt-danger\"\n onClick={() => {\n setConfirming(false)\n onReset()\n }}\n disabled={busy === true}\n >\n {busy === true ? 'Deleting…' : 'Yes, delete everything'}\n </button>\n <button type=\"button\" className=\"dt-quiet\" onClick={() => setConfirming(false)}>\n Cancel\n </button>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"dt-data\">\n <button type=\"button\" className=\"dt-quiet\" onClick={onExport} disabled={busy === true}>\n Export my data\n </button>\n <button type=\"button\" className=\"dt-quiet\" onClick={() => setConfirming(true)} disabled={busy === true}>\n Delete everything\n </button>\n <span className=\"dt-data-note\">\n Stored on this machine only. Nothing is sent anywhere.\n </span>\n </div>\n )\n}\n","/**\n * Pure map model for the panel.\n *\n * Kept free of React and of `fetch` so the interesting logic — turning a flat\n * node list into a tree, ordering it, labelling states — is testable in a plain\n * node environment. The component below it is then mostly rendering.\n */\n\nimport type { NodeView } from '../contract.js'\n\n/** One node plus its children, in render order. */\nexport interface MapTreeNode {\n node: NodeView\n children: MapTreeNode[]\n depth: number\n}\n\n/**\n * Build the map tree from a flat node list.\n *\n * Ordering is insertion order within each level, which is the order the nodes\n * were diagnosed in — the map reads as a history, not as a syllabus.\n *\n * Nodes whose parent is missing are promoted to roots rather than dropped: a\n * dangling reference is a data problem, and silently hiding the node would be\n * worse than showing it at the top level.\n *\n * @param nodes - the flat node views.\n * @returns the roots, each with its subtree.\n */\nexport function buildMapTree(nodes: readonly NodeView[]): MapTreeNode[] {\n const byId = new Map<string, NodeView>()\n for (const node of nodes) byId.set(node.id, node)\n\n const childrenOf = new Map<string | null, NodeView[]>()\n for (const node of nodes) {\n const parent = node.parentId !== null && byId.has(node.parentId) ? node.parentId : null\n const bucket = childrenOf.get(parent) ?? []\n bucket.push(node)\n childrenOf.set(parent, bucket)\n }\n\n const rendered = new Set<string>()\n\n const build = (parentId: string | null, depth: number, ancestors: ReadonlySet<string>): MapTreeNode[] =>\n (childrenOf.get(parentId) ?? [])\n // A cycle would otherwise recurse forever. The store forbids one, but the\n // panel must not hang if it ever meets one.\n .filter((node) => !ancestors.has(node.id))\n .map((node) => {\n rendered.add(node.id)\n const nextAncestors = new Set(ancestors).add(node.id)\n return { node, depth, children: build(node.id, depth + 1, nextAncestors) }\n })\n\n const roots = build(null, 0, new Set())\n\n // Anything unreachable from a root is rendered as a root of its own. Without\n // this, a node caught in a parent cycle is never visited and disappears from\n // the panel entirely — dropping a node silently is worse than showing it in\n // the wrong place.\n for (const node of nodes) {\n if (rendered.has(node.id)) continue\n rendered.add(node.id)\n roots.push({ node, depth: 0, children: build(node.id, 1, new Set([node.id])) })\n }\n\n return roots\n}\n\n/**\n * Flatten a tree back into render order, carrying depth.\n *\n * @param roots - the tree.\n * @returns every node in top-down, left-to-right order.\n */\nexport function flattenTree(roots: readonly MapTreeNode[]): MapTreeNode[] {\n const out: MapTreeNode[] = []\n const walk = (nodes: readonly MapTreeNode[]): void => {\n for (const entry of nodes) {\n out.push(entry)\n walk(entry.children)\n }\n }\n walk(roots)\n return out\n}\n\n/** Human label for a relation, in the map's own terms. */\nexport function relationLabel(relation: string): string {\n switch (relation) {\n case 'goal':\n return 'goal'\n case 'part-of':\n return 'part of'\n case 'prerequisite':\n return 'prerequisite'\n case 'related':\n return 'related'\n default:\n return relation\n }\n}\n\n/**\n * Only `confirmed` reads as a filled mark; every other state is an outline.\n *\n * There are deliberately no numeric or graded progressions here — the seven\n * words are the entire vocabulary.\n */\nexport function isFilledState(state: string): boolean {\n return state === 'confirmed'\n}\n\n/** Short, non-learner-facing explanation of what a state means, for the detail pane. */\nexport function stateExplanation(state: string): string {\n switch (state) {\n case 'unconfirmed':\n return 'No evidence yet — nothing has been observed for this node.'\n case 'explained':\n return 'It has been explained, but that alone does not prove understanding.'\n case 'practiced':\n return 'Something was attempted at least once.'\n case 'checked':\n return 'A check or near-transfer question was asked.'\n case 'weak':\n return 'Partial or unstable — worth repairing before moving on.'\n case 'blocked':\n return 'Cannot proceed: something here is missing or misunderstood.'\n case 'confirmed':\n return 'Supported by a check or transfer observation. Re-checked, not assumed.'\n default:\n return 'Unknown state.'\n }\n}\n\n/** The seven states, in the order the skill introduces them. */\nexport const STATE_ORDER = [\n 'unconfirmed',\n 'explained',\n 'practiced',\n 'checked',\n 'weak',\n 'blocked',\n 'confirmed',\n] as const\n","/**\n * The browser half's HTTP client.\n *\n * Plain `fetch` against this plugin's own prefix. No storage access, no DSH\n * internals — the browser has exactly the three calls the panel makes.\n *\n * Every response is checked for the `{ ok }` envelope, so an HTTP 200 carrying\n * a failure cannot be mistaken for data.\n */\n\nimport type {\n FocusResponse,\n HandoffView,\n LessonResponse,\n NodeDetailResponse,\n OverviewResponse,\n} from '../contract.js'\n\n/** Route prefix owned by this plugin; must match the host's `API_PREFIX`. */\nconst BASE = '/diagnostic-tutor/api'\n\n/** Requests are local; anything slower than this is a problem worth surfacing. */\nconst TIMEOUT_MS = 8000\n\nasync function request<T>(path: string, init?: RequestInit): Promise<T> {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), TIMEOUT_MS)\n try {\n const response = await fetch(`${BASE}${path}`, {\n ...init,\n signal: controller.signal,\n headers: { accept: 'application/json', ...(init?.headers ?? {}) },\n })\n\n let body: unknown\n try {\n body = await response.json()\n } catch {\n throw new Error(`unreadable response (HTTP ${response.status})`)\n }\n\n // Narrow by inspection rather than by generic: `T` is unconstrained, so a\n // union type would not discriminate.\n const envelope = body as { ok?: unknown; error?: { code?: string; message?: string } }\n if (!response.ok || envelope.ok === false) {\n throw new Error(\n envelope.error?.message ?? envelope.error?.code ?? `HTTP ${response.status}`,\n )\n }\n return body as T\n } finally {\n clearTimeout(timer)\n }\n}\n\n/** The current course and its whole diagnosis map. */\nexport function fetchOverview(): Promise<OverviewResponse> {\n return request<OverviewResponse>('/overview')\n}\n\n/** One node, its evidence, its parent and its children. */\nexport function fetchNode(nodeId: string): Promise<NodeDetailResponse> {\n return request<NodeDetailResponse>(`/node?id=${encodeURIComponent(nodeId)}`)\n}\n\n/** The lesson the tutor has written for a node; `lesson: null` until it has. */\nexport function fetchLesson(nodeId: string): Promise<LessonResponse> {\n return request<LessonResponse>(`/lesson?nodeId=${encodeURIComponent(nodeId)}`)\n}\n\n/**\n * Save everything the learner owns to a file.\n *\n * Deliberately a full download rather than a call that returns data: the point\n * of an export is that the learner ends up holding it, and a file they can move,\n * read and keep is the only version of that which survives this app.\n *\n * @returns the filename the browser was asked to save.\n */\nexport async function downloadExport(): Promise<string> {\n const response = await fetch(`${BASE}/export`, { headers: { accept: 'application/json' } })\n if (!response.ok) throw new Error(`export failed (HTTP ${response.status})`)\n const text = await response.text()\n\n const stamp = new Date().toISOString().slice(0, 10)\n const name = `dsh-diagnostic-tutor-${stamp}.json`\n const url = URL.createObjectURL(new Blob([text], { type: 'application/json' }))\n try {\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = name\n document.body.appendChild(anchor)\n anchor.click()\n anchor.remove()\n } finally {\n // Revoking immediately is safe because the click has already handed the\n // blob to the browser's download machinery.\n URL.revokeObjectURL(url)\n }\n return name\n}\n\n/**\n * Delete everything and return to first run.\n *\n * @returns nothing; the caller re-reads the overview.\n */\nexport function resetState(): Promise<{ ok: true; reset: true }> {\n return request<{ ok: true; reset: true }>('/reset', { method: 'POST' })\n}\n\n/**\n * Tell the host a surface has rendered the lesson.\n *\n * The last leg of the timing chain: everything before it is measured on the\n * host, and this is the only part only the browser can answer.\n */\nexport function reportObserved(nodeId: string): Promise<{ handoff: HandoffView | null }> {\n return request<{ handoff: HandoffView | null }>('/handoff/observed', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ nodeId }),\n })\n}\n\n/**\n * What the Start learning button does.\n *\n * Records the focus and asks the host to wake the tutor. `sessionId` is the\n * session the panel is showing; without it the focus is still recorded and the\n * response says the tutor was not reached.\n */\nexport function startFocus(nodeId: string, sessionId?: string): Promise<FocusResponse> {\n return request<FocusResponse>('/focus', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(sessionId === undefined ? { nodeId } : { nodeId, sessionId }),\n })\n}\n","/**\n * Shared learning state for both surfaces.\n *\n * The plugin renders the same runtime in two places — the full-page panel and\n * the docked right-sidebar tab — and both need identical behaviour: select a\n * node, start learning, follow the tutor. Duplicating that would mean two\n * implementations of the focus hand-off and two copies of the polling loop,\n * which is exactly how two surfaces drift apart.\n *\n * So the behaviour lives here once, and the components are presentation.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\n\nimport type {\n FocusView,\n HandoffView,\n LessonRecord,\n NextStepView,\n NodeDetailResponse,\n OverviewResponse,\n} from '../contract.js'\nimport * as realApi from './api.js'\n\n/** The calls the surfaces make; injectable so preview and tests can fake them. */\nexport interface PanelClient {\n fetchOverview(): Promise<OverviewResponse>\n fetchNode(nodeId: string): Promise<NodeDetailResponse>\n fetchLesson(nodeId: string): Promise<LessonResponse>\n startFocus(nodeId: string, sessionId?: string): Promise<FocusResponse>\n /** Optional: only the real client can report it. */\n reportObserved?(nodeId: string): Promise<unknown>\n /** Optional: only the real client can save a file. */\n downloadExport?(): Promise<string>\n /** Optional: only the real client can delete state. */\n resetState?(): Promise<unknown>\n}\n\ntype LessonResponse = Awaited<ReturnType<typeof realApi.fetchLesson>>\ntype FocusResponse = Awaited<ReturnType<typeof realApi.startFocus>>\n\nexport const defaultClient: PanelClient = {\n fetchOverview: realApi.fetchOverview,\n fetchNode: realApi.fetchNode,\n fetchLesson: realApi.fetchLesson,\n startFocus: realApi.startFocus,\n reportObserved: realApi.reportObserved,\n downloadExport: realApi.downloadExport,\n resetState: realApi.resetState,\n}\n\n/**\n * How often the surfaces re-read state while a focus is active.\n *\n * Polling rather than a push channel: DSH exposes no generic host→client push\n * for third-party plugins, and the published UI plugins poll for the same\n * reason. The interval only runs while something is being learned, so an idle\n * surface makes no requests at all.\n */\nexport const POLL_INTERVAL_MS = 2000\n\nexport interface LearningState {\n readonly overview: OverviewResponse | null\n readonly focus: FocusView | null\n /** The tutor's recommendation, when the learner has not acted on it yet. */\n readonly nextStep: NextStepView | null\n /** How the current handoff is going, for the progress line. */\n readonly handoff: HandoffView | null\n /**\n * Whether a teaching brain is installed: `true`, confidently `false`, or\n * `null` when this scope cannot see the catalog. Only `false` may warn.\n */\n readonly teachingBrain: boolean | null\n /** The node whose detail is shown; the focus when nothing is picked. */\n readonly selectedId: string | null\n readonly detail: NodeDetailResponse | null\n readonly lesson: LessonRecord | null\n readonly note: string | null\n readonly error: string | null\n readonly starting: boolean\n readonly loading: boolean\n select(nodeId: string): void\n /** Start learning on the selected node. */\n start(): void\n /** Start learning on a named node — how a recommendation is acted on. */\n continueTo(nodeId: string): void\n dismissLesson(): void\n /** Save everything to a file. */\n exportData(): void\n /** Delete everything and re-read from empty. */\n resetAll(): void\n /** A one-line account of the last data action, for the surface to show. */\n readonly dataNote: string | null\n}\n\n/**\n * Read and follow the learning runtime.\n *\n * @param options.client - the API to talk to.\n * @param options.sessionId - the session the tutor should be woken in.\n * @param options.initialOverview - pre-supplied state, so the preview and tests\n * skip the first fetch.\n * @returns the state and the two actions the surfaces offer.\n */\nexport function useLearning(options: {\n client: PanelClient\n sessionId?: string | undefined\n initialOverview?: OverviewResponse | undefined\n /** Called once a focus has been recorded, so a surface can react to it. */\n onStarted?: (() => void) | undefined\n}): LearningState {\n const { client, sessionId, initialOverview, onStarted } = options\n\n const [overview, setOverview] = useState<OverviewResponse | null>(initialOverview ?? null)\n const [selectedId, setSelectedId] = useState<string | null>(null)\n const [detail, setDetail] = useState<NodeDetailResponse | null>(null)\n const [lesson, setLesson] = useState<LessonRecord | null>(null)\n const [note, setNote] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [starting, setStarting] = useState(false)\n const [loading, setLoading] = useState(initialOverview === undefined)\n\n const focus = overview?.focus ?? null\n const focusNodeId = focus?.nodeId ?? null\n\n // Which node's detail is worth keeping fresh. The focus wins, so the panel\n // follows the tutor's node even when the learner is browsing elsewhere.\n const liveNodeId = focusNodeId ?? selectedId\n const liveRef = useRef<string | null>(liveNodeId)\n liveRef.current = liveNodeId\n /** The node whose lesson has already been reported as seen. */\n const observedRef = useRef<string | null>(null)\n\n useEffect(() => {\n if (initialOverview !== undefined) return\n let live = true\n client\n .fetchOverview()\n .then((data) => {\n if (live) setOverview(data)\n })\n .catch((cause: Error) => {\n if (live) setError(cause.message)\n })\n .finally(() => {\n if (live) setLoading(false)\n })\n return () => {\n live = false\n }\n }, [client, initialOverview])\n\n const select = useCallback(\n (nodeId: string) => {\n setSelectedId(nodeId)\n setError(null)\n client\n .fetchNode(nodeId)\n .then(setDetail)\n .catch((cause: Error) => setError(cause.message))\n },\n [client],\n )\n\n const beginFocus = useCallback(\n (nodeId: string) => {\n setStarting(true)\n setError(null)\n setNote(null)\n client\n .startFocus(nodeId, sessionId)\n .then(async (result) => {\n setNote(\n result.prompted\n ? 'Started. The tutor is teaching this node in the chat — the surface updates as it writes.'\n : `Focus recorded, but the tutor was not woken (${result.promptReason ?? 'unknown reason'}). Say anything in the chat to continue.`,\n )\n // Re-read the overview rather than trusting the response: the focus\n // lives in stored state, and the surfaces follow stored state. Without\n // this the panel would never see its own focus, and the polling effect\n // below — which keys off it — would never start.\n const [nextOverview, nextLesson] = await Promise.all([\n client.fetchOverview(),\n client.fetchLesson(result.focus.nodeId),\n ])\n setOverview(nextOverview)\n setLesson(nextLesson.lesson)\n onStarted?.()\n })\n .catch((cause: Error) => setError(cause.message))\n .finally(() => setStarting(false))\n },\n [client, sessionId, onStarted],\n )\n\n const start = useCallback(() => {\n if (selectedId === null) return\n beginFocus(selectedId)\n }, [beginFocus, selectedId])\n\n const [dataNote, setDataNote] = useState<string | null>(null)\n\n /** Re-read everything, the same way the initial load does. */\n const refresh = useCallback(\n () =>\n client\n .fetchOverview()\n .then((data) => {\n setOverview(data)\n setError(null)\n return data\n })\n .catch((cause: Error) => {\n setError(cause.message)\n return null\n }),\n [client],\n )\n\n const exportData = useCallback(() => {\n setDataNote(null)\n void client\n .downloadExport?.()\n .then((name) => setDataNote(`Saved ${name}.`))\n .catch((cause: Error) => setDataNote(`Could not export: ${cause.message}`))\n }, [client])\n\n const resetAll = useCallback(() => {\n setDataNote(null)\n setDetail(null)\n setLesson(null)\n void client\n .resetState?.()\n .then(refresh)\n .then(() => setDataNote('Everything was deleted.'))\n .catch((cause: Error) => setDataNote(`Could not delete: ${cause.message}`))\n }, [client, refresh])\n\n useEffect(() => {\n if (focus === null) return\n const nodeId = focus.nodeId\n let live = true\n\n const tick = async (): Promise<void> => {\n try {\n const [nextOverview, nextLesson] = await Promise.all([\n client.fetchOverview(),\n client.fetchLesson(nodeId),\n ])\n if (!live) return\n setOverview(nextOverview)\n setLesson(nextLesson.lesson)\n // The evidence trail and the node's state are what a check changes.\n const watched = liveRef.current\n if (watched !== null) setDetail(await client.fetchNode(watched))\n\n // Tell the host the lesson reached a screen. Reported once per node, so\n // the timing chain records the first sighting rather than every poll.\n if (nextLesson.lesson !== null && observedRef.current !== nodeId) {\n observedRef.current = nodeId\n void client.reportObserved?.(nodeId)?.catch(() => {})\n }\n } catch {\n // A failed poll is not worth surfacing; the next one may succeed.\n }\n }\n\n void tick()\n const timer = setInterval(() => void tick(), POLL_INTERVAL_MS)\n return () => {\n live = false\n clearInterval(timer)\n }\n }, [client, focus])\n\n return {\n overview,\n focus,\n nextStep: overview?.nextStep ?? null,\n handoff: overview?.handoff ?? null,\n teachingBrain: overview?.teachingBrain ?? null,\n selectedId,\n detail,\n lesson,\n note,\n error,\n starting,\n loading,\n select,\n start,\n continueTo: beginFocus,\n exportData,\n resetAll,\n dataNote,\n dismissLesson: useCallback(() => setLesson(null), []),\n }\n}\n","/**\n * The full-page learning panel.\n *\n * Three columns, left to right, that read as one sentence: **the course and its\n * diagnosis map → the node you picked → the learning surface for it.** This is\n * the \"focus mode\" view — the whole runtime at once.\n *\n * The docked tab (`tab.tsx`) renders the same state in the right sidebar so the\n * learner can keep the chat open while working. Both run on `useLearning`, so\n * they cannot disagree about what is focused or what the tutor wrote.\n *\n * ---------------------------------------------------------------------------\n * Who decides what\n * ---------------------------------------------------------------------------\n * The panel never writes teaching content and never decides what happens next.\n * Pressing **Start learning** records a focus and wakes the tutor; the blocks\n * that appear are whatever the tutor wrote through `udt_lesson_update`; the\n * evidence and state changes are whatever the tutor recorded through\n * `udt_map_update`. The panel's whole job is to show the current truth and to\n * keep showing it as it changes.\n */\n\nimport type { ReactNode } from 'react'\n\nimport { DataFooter, HandoffLine, LessonBody, NextStepCard } from './blocks.jsx'\nimport { buildMapTree, flattenTree, isFilledState, relationLabel, stateExplanation } from './model.js'\nimport type { PanelClient } from './use-learning.js'\nimport { defaultClient, useLearning } from './use-learning.js'\n\nexport type { PanelClient } from './use-learning.js'\n\nexport interface LearningPanelProps {\n /** Defaults to the real HTTP client. */\n client?: PanelClient\n /** Pre-supplied overview, so the preview and tests skip the first fetch. */\n initialOverview?: Parameters<typeof useLearning>[0]['initialOverview']\n /** The session the tutor should be woken in, when the host half knows one. */\n sessionId?: string | undefined\n /**\n * Show the conversation again.\n *\n * This panel fills the main column, which is also where the chat lives, so\n * answering a check means leaving it. Supplied by the client plugin through\n * the layout service; absent in the preview and in tests.\n */\n onOpenChat?: (() => void) | undefined\n /** Called once a focus is recorded — used to dock the side tab. */\n onFocusStarted?: (() => void) | undefined\n}\n\n/** The status pill plus its mark. Never a number. */\nfunction StateMark({ state }: { state: string }): ReactNode {\n return (\n <>\n <span className={`dt-dot dt-state-${state}`} data-filled={isFilledState(state)} />\n <span className={`dt-state dt-state-${state}`}>{state}</span>\n </>\n )\n}\n\n/**\n * The panel.\n *\n * @param props - injectable client, optional pre-supplied overview, session id.\n * @returns the three-pane learning surface.\n */\nexport function LearningPanel({\n client,\n initialOverview,\n sessionId,\n onOpenChat,\n onFocusStarted,\n}: LearningPanelProps): ReactNode {\n const state = useLearning({\n client: client ?? defaultClient,\n sessionId,\n initialOverview,\n onStarted: onFocusStarted,\n })\n const { overview, focus, nextStep, handoff, teachingBrain, dataNote, selectedId, detail, lesson, note, error, starting, loading } =\n state\n const rows = flattenTree(buildMapTree(overview?.nodes ?? []))\n const confirmed = (overview?.nodes ?? []).filter((node) => node.state === 'confirmed').length\n\n // First use, same as the docked tab: a blank three-column page reads as a\n // broken page. One question and the sentence that starts everything.\n if (overview?.course == null) {\n return (\n <div className=\"dt-root dt-root-welcome\">\n <div className=\"dt-welcome\">\n <p className=\"dt-welcome-eyebrow\">Universal Diagnostic Tutor</p>\n <h1 className=\"dt-welcome-title\">What do you want to learn?</h1>\n <p className=\"dt-welcome-body\">\n Works with the Universal Diagnostic Tutor skill. Start by telling the tutor what you\n want to learn — in your own words, in the chat. Then this page fills in: a map of what\n you actually know, the lesson, and the evidence behind every status.\n </p>\n <div className=\"dt-welcome-sample\">\n <span className=\"dt-welcome-sample-label\">Try</span>\n <span className=\"dt-welcome-sample-text\">\n I want to learn machine learning. I know some Python, but my math is weak.\n </span>\n </div>\n <p className=\"dt-welcome-note\">\n If tutoring doesn't start, make sure the UDT skill is available to your active DSH\n agent.\n </p>\n <p className=\"dt-welcome-note\">\n No account, no scores, no streak. Your goal, your map and the evidence behind it stay\n on this machine, and are yours to keep or delete.\n </p>\n <DataFooter onExport={state.exportData} onReset={state.resetAll} />\n {teachingBrain === false && (\n <p className=\"dt-notice\">\n <b>The Universal Diagnostic Tutor skill was not found.</b> This panel still records\n and shows your learning state, but no lesson will be written until the skill is\n available to your active DSH agent.\n </p>\n )}\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"dt-root\">\n {/* ---- course + map ---- */}\n <div className=\"dt-pane\">\n <p className=\"dt-eyebrow\">Current course</p>\n <h1 className=\"dt-course-title\">{overview?.course?.title ?? 'No goal yet'}</h1>\n {overview?.course != null && (\n <p className=\"dt-goal\">\n <b>Goal:</b> {overview.course.goal}\n </p>\n )}\n\n <p className=\"dt-section-title\">\n Diagnosis map <span>{rows.length} nodes</span>\n </p>\n {rows.length === 0 && (\n <p className=\"dt-empty\">\n Nothing on the map yet. State a goal in the chat and the runtime will record it.\n </p>\n )}\n {rows.map(({ node, depth }) => (\n <button\n key={node.id}\n type=\"button\"\n className=\"dt-node\"\n style={{ marginLeft: `${depth * 12}px` }}\n aria-current={node.id === selectedId}\n data-focused={node.id === focus?.nodeId}\n onClick={() => state.select(node.id)}\n >\n <span className={`dt-dot dt-state-${node.state}`} data-filled={isFilledState(node.state)} />\n <span className=\"dt-node-title\">\n {node.title}\n <span className=\"dt-node-rel\">\n {relationLabel(node.relation)} · {node.evidenceCount} evidence\n </span>\n </span>\n <span className={`dt-state dt-state-${node.state}`}>{node.state}</span>\n </button>\n ))}\n <p className=\"dt-caption\">\n {confirmed} of {rows.length} confirmed by evidence.\n </p>\n </div>\n\n {/* ---- node detail ---- */}\n <div className=\"dt-pane dt-detail\">\n {detail === null ? (\n <p className=\"dt-empty\">\n {loading ? 'Loading…' : 'Pick a node on the map to see why it is there.'}\n </p>\n ) : (\n <>\n <p className=\"dt-eyebrow\">Selected node</p>\n <h2>{detail.node.title}</h2>\n <div className=\"dt-meta\">\n <StateMark state={detail.node.state} />\n <span className=\"dt-chip\">{relationLabel(detail.node.relation)}</span>\n {detail.parent !== null && <span className=\"dt-chip\">parent: {detail.parent.title}</span>}\n {detail.children.length > 0 && (\n <span className=\"dt-chip\">{detail.children.length} children</span>\n )}\n </div>\n\n <p className=\"dt-why\">{stateExplanation(detail.node.state)}</p>\n\n <p className=\"dt-section-title\">\n Evidence <span>{detail.node.evidence.length}</span>\n </p>\n {detail.node.evidence.length === 0 ? (\n <p className=\"dt-empty\">Nothing recorded yet.</p>\n ) : (\n <ul className=\"dt-evidence\">\n {detail.node.evidence.map((entry, index) => (\n <li key={index}>\n <div className=\"kind\">\n {entry.kind}\n {entry.readiness !== undefined && ` · ${entry.readiness}`}\n </div>\n {entry.note !== undefined && <div>{entry.note}</div>}\n <div className=\"when\">{entry.at}</div>\n </li>\n ))}\n </ul>\n )}\n\n {note !== null && <p className=\"dt-caption\">{note}</p>}\n {error !== null && <p className=\"dt-empty\">{error}</p>}\n <button type=\"button\" className=\"dt-primary\" onClick={state.start} disabled={starting}>\n {starting\n ? 'Starting…'\n : focus !== null && focus.nodeId === selectedId\n ? 'Learning in progress'\n : 'Start learning'}\n </button>\n </>\n )}\n </div>\n\n {/* ---- learning surface ---- */}\n <div className=\"dt-pane\">\n <div className=\"dt-lesson-head\">\n <p className=\"dt-eyebrow\">Learning surface</p>\n {lesson !== null && <span className=\"dt-origin\">{lesson.origin}</span>}\n </div>\n {lesson === null ? (\n focus !== null ? (\n <>\n <p className=\"dt-empty\">The tutor is preparing this node…</p>\n <p className=\"dt-caption\">\n The teaching appears here as it is written, while the conversation continues in the\n chat.\n </p>\n </>\n ) : (\n <p className=\"dt-empty\">\n Choose a node and press <b>Start learning</b>. The teaching appears here, and the\n conversation stays in the chat.\n </p>\n )\n ) : (\n <>\n <h2>{lesson.title}</h2>\n <div style={{ marginTop: 14 }}>\n <LessonBody blocks={lesson.blocks} />\n </div>\n </>\n )}\n {handoff !== null && lesson === null && focus !== null && (\n <HandoffLine handoff={handoff} onRetry={() => state.continueTo(handoff.targetNodeId)} />\n )}\n {nextStep !== null && (\n <NextStepCard\n nextStep={nextStep}\n busy={starting}\n onContinue={() => state.continueTo(nextStep.targetNodeId ?? nextStep.fromNodeId)}\n />\n )}\n {onOpenChat !== undefined && (\n <button type=\"button\" className=\"dt-primary\" onClick={onOpenChat}>\n Answer in the chat\n </button>\n )}\n {lesson !== null && (\n <button type=\"button\" className=\"dt-secondary\" onClick={state.dismissLesson}>\n Back to the node\n </button>\n )}\n {dataNote !== null && <p className=\"dt-caption\">{dataNote}</p>}\n <DataFooter onExport={state.exportData} onReset={state.resetAll} />\n </div>\n </div>\n )\n}\n","/**\n * The docked Learning tab.\n *\n * This is the surface that makes the loop usable. The full-page panel occupies\n * the main column, which is also where the conversation lives — so with only\n * that panel, answering a check means leaving the lesson. The right sidebar is\n * a *separate column*, so the same runtime sits beside the chat and the learner\n * never has to switch.\n *\n * Layout is deliberately narrow-first and ordered by what the learner needs\n * next: **the lesson, then the recommendation, then the map, then the\n * evidence.** The lesson leads because it is the teaching; the map and the\n * evidence are reference material the learner consults, not the main event.\n *\n * It runs on the same `useLearning` state as the full panel, so the two cannot\n * disagree about what is focused or what the tutor wrote.\n */\n\nimport { useEffect, useMemo, type ReactNode } from 'react'\n\nimport type { NodeView, OverviewResponse } from '../contract.js'\nimport { DataFooter, HandoffLine, LessonBody, NextStepCard } from './blocks.jsx'\nimport { buildMapTree, flattenTree, relationLabel, stateExplanation } from './model.js'\nimport type { PanelClient } from './use-learning.js'\nimport { defaultClient, useLearning } from './use-learning.js'\n\n/** The tab chip's text. */\nexport function LearningTabTitle(): ReactNode {\n return <>Learning</>\n}\n\nfunction CompactMap({\n nodes,\n selectedId,\n focusNodeId,\n onSelect,\n}: {\n nodes: NodeView[]\n selectedId: string | null\n focusNodeId: string | null\n onSelect: (id: string) => void\n}): ReactNode {\n const rows = useMemo(() => flattenTree(buildMapTree(nodes)), [nodes])\n const confirmed = nodes.filter((node) => node.state === 'confirmed').length\n\n return (\n <>\n <p className=\"dt-tab-section\">\n Diagnosis map <span>{nodes.length}</span>\n </p>\n {rows.length === 0 && <p className=\"dt-empty\">No map yet — state a goal in the chat.</p>}\n <div className=\"dt-tree\">\n {rows.map(({ node, depth }) => (\n <button\n key={node.id}\n type=\"button\"\n className=\"dt-tab-node\"\n style={{ paddingLeft: `${6 + depth * 14}px` }}\n // Depth as data, so the stylesheet can draw the guide without\n // measuring anything.\n data-depth={depth > 0 ? Math.min(depth, 3) : undefined}\n data-attention={node.state === 'blocked' || node.state === 'weak' ? 'true' : undefined}\n aria-current={node.id === selectedId}\n data-focused={node.id === focusNodeId}\n onClick={() => onSelect(node.id)}\n >\n <span className={`dt-dot dt-state-${node.state}`} data-filled={node.state === 'confirmed'} />\n <span className=\"dt-tab-node-title\">{node.title}</span>\n <span className={`dt-tab-state dt-state-${node.state}`}>{node.state}</span>\n </button>\n ))}\n </div>\n <p className=\"dt-caption\">\n {confirmed} of {nodes.length} confirmed by evidence.\n </p>\n </>\n )\n}\n\nexport interface LearningTabProps {\n /** Defaults to the real HTTP client. */\n client?: PanelClient\n /** Injected by the session scope; the tab asks the tutor in this session. */\n sessionId?: string | undefined\n /** Pre-supplied state, so the preview and tests skip the first fetch. */\n initialOverview?: OverviewResponse | undefined\n}\n\n/**\n * The docked learning surface.\n *\n * @param props - injectable client and the session the tab belongs to.\n * @returns the stacked node / map / lesson column.\n */\nexport function LearningTab({ client, sessionId, initialOverview }: LearningTabProps): ReactNode {\n const state = useLearning({ client: client ?? defaultClient, sessionId, initialOverview })\n const { overview, focus, nextStep, handoff, teachingBrain, dataNote, selectedId, detail, lesson, note, error, starting, loading, select } =\n state\n\n // Show something on first paint: the focused node if there is one, else the\n // map's first row. `select` is a stable callback, so this runs on arrival\n // rather than on every render.\n useEffect(() => {\n if (selectedId !== null) return\n const first = overview?.nodes?.[0]?.id\n if (first !== undefined) select(focus?.nodeId ?? first)\n }, [overview, focus, selectedId, select])\n\n const shownId = focus?.nodeId ?? selectedId\n\n const hasCourse = overview?.course != null\n const rows = overview?.nodes ?? []\n\n // First use: no goal yet. A blank panel would read as a broken panel, so it\n // says what to do and shows the exact sentence that starts everything. No\n // wizard — the chat is the input, and the shortest path there is one line of\n // text the learner can copy.\n if (!hasCourse) {\n return (\n <div className=\"dt-tab\">\n <div className=\"dt-welcome\">\n <p className=\"dt-welcome-eyebrow\">Universal Diagnostic Tutor</p>\n <h2 className=\"dt-welcome-title\">What do you want to learn?</h2>\n <p className=\"dt-welcome-body\">\n Works with the Universal Diagnostic Tutor skill. Start by telling the tutor what you\n want to learn — in your own words, in the chat.\n </p>\n <div className=\"dt-welcome-sample\">\n <span className=\"dt-welcome-sample-label\">Try</span>\n <span className=\"dt-welcome-sample-text\">\n I want to learn machine learning. I know some Python, but my math is weak.\n </span>\n </div>\n <p className=\"dt-welcome-note\">\n If tutoring doesn't start, make sure the UDT skill is available to your active DSH\n agent.\n </p>\n <p className=\"dt-welcome-note\">\n No account, no scores, no streak. Your goal, your map and the evidence behind it\n stay on this machine, and are yours to keep or delete.\n </p>\n <DataFooter onExport={state.exportData} onReset={state.resetAll} />\n {teachingBrain === false && (\n <p className=\"dt-notice\">\n <b>The Universal Diagnostic Tutor skill was not found.</b> This panel still records and\n shows your learning state, but no lesson will be written until the skill is available\n to your active DSH agent.\n </p>\n )}\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"dt-tab\">\n {/* ---- now learning ---- */}\n {detail === null ? (\n // The header frame stays even before the first fetch lands, so the\n // surface never looks like an empty box on a slow connection.\n <header className=\"dt-now\">\n <p className=\"dt-now-eyebrow\">Now learning</p>\n <p className=\"dt-empty\">\n {loading ? 'Loading…' : 'Pick a node on the map below to begin.'}\n </p>\n </header>\n ) : (\n <header className=\"dt-now\" data-state={detail.node.state}>\n <p className=\"dt-now-eyebrow\">Now learning</p>\n <h2 className=\"dt-now-title\">{detail.node.title}</h2>\n <div className=\"dt-now-meta\">\n <span className={`dt-tab-state dt-state-${detail.node.state}`}>{detail.node.state}</span>\n <span className=\"dt-now-rel\">{relationLabel(detail.node.relation)}</span>\n {detail.parent !== null && <span className=\"dt-now-rel\">in {detail.parent.title}</span>}\n </div>\n <p className=\"dt-now-note\">{stateExplanation(detail.node.state)}</p>\n <button\n type=\"button\"\n className=\"dt-primary dt-now-action\"\n onClick={state.start}\n disabled={starting || shownId === null}\n >\n {starting\n ? 'Starting…'\n : focus !== null && focus.nodeId === shownId\n ? 'Keep going'\n : 'Start learning'}\n </button>\n {note !== null && <p className=\"dt-caption\">{note}</p>}\n {error !== null && <p className=\"dt-empty\">{error}</p>}\n </header>\n )}\n\n {/* ---- the teaching ---- */}\n {/* Always present, even before it has content: the surface names its three\n regions so a first-time learner can see what will fill them. */}\n <section className=\"dt-lesson\">\n <p className=\"dt-tab-section\">Learning surface</p>\n {handoff !== null && (\n <HandoffLine handoff={handoff} onRetry={() => state.continueTo(handoff.targetNodeId)} />\n )}\n {lesson === null ? (\n <p className=\"dt-empty\">\n {focus !== null\n ? 'The tutor is preparing this node…'\n : 'Press Start learning and the teaching appears here, while the chat stays open beside it.'}\n </p>\n ) : (\n <article className=\"dt-lesson-body\">\n <div className=\"dt-lesson-head\">\n <h3 className=\"dt-lesson-title\">{lesson.title}</h3>\n <span className=\"dt-origin\">{lesson.origin}</span>\n </div>\n <LessonBody blocks={lesson.blocks} />\n </article>\n )}\n </section>\n\n {/* ---- where to go next ---- */}\n {nextStep !== null && (\n <NextStepCard\n nextStep={nextStep}\n busy={starting}\n // Acting on the recommendation starts the focus it names — or, for a\n // stay, re-opens the same node so the tutor picks the thread back up.\n onContinue={() => {\n const target = nextStep.targetNodeId ?? nextStep.fromNodeId\n select(target)\n state.continueTo(target)\n }}\n />\n )}\n\n <CompactMap\n nodes={rows}\n selectedId={shownId}\n focusNodeId={focus?.nodeId ?? null}\n onSelect={select}\n />\n\n {dataNote !== null && <p className=\"dt-caption\">{dataNote}</p>}\n\n {/* ---- the evidence behind this node ---- */}\n {detail !== null && detail.node.evidence.length > 0 && (\n <>\n <p className=\"dt-tab-section\">\n Evidence <span>{detail.node.evidence.length}</span>\n </p>\n <ul className=\"dt-evidence\">\n {detail.node.evidence.map((entry, index) => (\n <li key={index}>\n <span className=\"dt-ev-kind\">{entry.kind}</span>\n {entry.readiness !== undefined && (\n <span className=\"dt-ev-readiness\">{entry.readiness.replace(/-/g, ' ')}</span>\n )}\n {entry.note !== undefined && <span className=\"dt-ev-note\">{entry.note}</span>}\n </li>\n ))}\n </ul>\n </>\n )}\n\n <DataFooter onExport={state.exportData} onReset={state.resetAll} />\n </div>\n )\n}\n","/**\n * The panel's stylesheet.\n *\n * Injected as a `<style data-plugin>` element rather than imported as CSS,\n * because styles never travel through the client bundler: the module table\n * serves one JavaScript file per plugin, so a separate CSS artifact has nowhere\n * to live. This is what the published UI plugins do too.\n *\n * The web styling guide forbids Tailwind and component libraries here, and\n * recommends the design system's semantic custom properties. Every one of those\n * tokens is used with a fallback, so the same stylesheet renders correctly in\n * the standalone preview where no DSH theme is present.\n */\n\n/** Marker attribute so the sheet is injected exactly once, and is findable. */\nexport const STYLE_ATTR = 'data-diagnostic-tutor-style'\n\nconst CSS = `.dt-root {\n --dt-bg: var(--dsw-alias-bg-base, #ffffff);\n --dt-surface: var(--dsw-alias-bg-elevated, #f7f8fa);\n --dt-sunken: var(--dsw-alias-bg-sunken, #f1f3f6);\n --dt-border: var(--dsw-alias-border-secondary, #e3e6ea);\n --dt-border-strong: var(--dsw-alias-border-primary, #cfd4dc);\n --dt-text: var(--dsw-alias-label-primary, #1c1f23);\n --dt-muted: var(--dsw-alias-label-secondary, #6b7280);\n --dt-accent: var(--dsw-alias-brand-primary, #4f46e5);\n --dt-good: #1f8b4c;\n --dt-warn: #b7791f;\n --dt-stop: #c0392b;\n --dt-radius: 10px;\n\n display: grid;\n grid-template-columns: minmax(260px, 300px) minmax(280px, 1fr) minmax(320px, 1.15fr);\n gap: 0;\n height: 100%;\n min-height: 0;\n background: var(--dt-bg);\n color: var(--dt-text);\n font-size: 13px;\n line-height: 1.6;\n text-align: left;\n}\n@media (max-width: 1080px) {\n .dt-root { grid-template-columns: 1fr; height: auto; }\n}\n\n.dt-pane { min-width: 0; min-height: 0; overflow: auto; padding: 16px 18px 40px; }\n.dt-pane + .dt-pane { border-left: 1px solid var(--dt-border); }\n@media (max-width: 1080px) {\n .dt-pane + .dt-pane { border-left: 0; border-top: 1px solid var(--dt-border); }\n}\n\n/* ---- shared type ------------------------------------------------------- */\n.dt-eyebrow,\n.dt-tab-section,\n.dt-now-eyebrow,\n.dt-welcome-eyebrow,\n.dt-block-label,\n.dt-next-label {\n font-size: 10px;\n letter-spacing: .1em;\n text-transform: uppercase;\n color: var(--dt-muted);\n margin: 0;\n}\n\n.dt-course-title { font-size: 17px; font-weight: 640; margin: 0 0 6px; letter-spacing: -.01em; }\n.dt-goal {\n margin: 0 0 16px; padding: 9px 11px; border-radius: var(--dt-radius);\n background: var(--dt-surface); border: 1px solid var(--dt-border);\n color: var(--dt-muted); font-size: 12px;\n}\n.dt-goal b { color: var(--dt-text); font-weight: 560; }\n\n.dt-section-title {\n display: flex; align-items: baseline; gap: 8px;\n font-size: 10px; letter-spacing: .1em; text-transform: uppercase;\n color: var(--dt-muted); margin: 22px 0 8px;\n}\n.dt-section-title span { text-transform: none; letter-spacing: 0; font-size: 11px; }\n\n.dt-caption { color: var(--dt-muted); font-size: 11px; margin: 6px 0 0; }\n.dt-empty { color: var(--dt-muted); font-size: 12.5px; margin: 8px 0; }\n.dt-cta-hint { margin-top: 14px; line-height: 1.6; }\n\n/* ---- first use --------------------------------------------------------- */\n/* The full-page variant has no columns to fill, so the question centres. */\n.dt-root-welcome { grid-template-columns: 1fr; place-items: center; }\n.dt-root-welcome .dt-welcome { max-width: 520px; padding: 8px 24px; }\n.dt-welcome { padding: 28px 4px 8px; }\n.dt-welcome-eyebrow { color: var(--dt-accent); }\n.dt-welcome-title {\n font-size: 21px; font-weight: 660; letter-spacing: -.015em;\n margin: 8px 0 10px; line-height: 1.25;\n}\n.dt-welcome-body { margin: 0 0 18px; color: var(--dt-muted); font-size: 13px; line-height: 1.65; }\n.dt-welcome-sample {\n border: 1px solid var(--dt-accent); border-radius: var(--dt-radius);\n background: var(--dt-surface); padding: 11px 13px;\n}\n.dt-welcome-sample-label {\n display: block; font-size: 10px; letter-spacing: .1em; text-transform: uppercase;\n color: var(--dt-accent); margin-bottom: 6px;\n}\n.dt-welcome-sample-text { font-size: 13px; line-height: 1.55; }\n.dt-welcome-note {\n margin: 18px 0 0; padding-top: 14px; border-top: 1px solid var(--dt-border);\n color: var(--dt-muted); font-size: 11.5px; line-height: 1.6;\n}\n\n/* Shown when nothing will teach: honest, actionable, and not a crash. */\n.dt-notice {\n margin: 16px 0 0; padding: 11px 13px; border-radius: var(--dt-radius);\n border: 1px solid var(--dt-warn);\n background: color-mix(in srgb, var(--dt-warn) 8%, var(--dt-bg));\n color: var(--dt-text); font-size: 12px; line-height: 1.6;\n}\n.dt-notice b { font-weight: 640; }\n\n/* ---- learner data controls --------------------------------------------- */\n/* Quiet by default: these are rights, not calls to action. They sit below the\n teaching so they are findable without competing with it. */\n.dt-data {\n margin-top: 26px; padding-top: 14px; border-top: 1px solid var(--dt-border);\n display: flex; flex-wrap: wrap; align-items: center; gap: 6px;\n}\n.dt-quiet {\n padding: 4px 10px; border-radius: 7px; border: 1px solid var(--dt-border);\n background: transparent; color: var(--dt-muted);\n font: inherit; font-size: 11.5px; cursor: pointer;\n}\n.dt-quiet:hover { background: var(--dt-surface); color: var(--dt-text); }\n.dt-quiet:disabled { opacity: .5; cursor: default; }\n.dt-data-note { flex-basis: 100%; color: var(--dt-muted); font-size: 11px; line-height: 1.5; }\n.dt-data-confirm {\n border-top-color: var(--dt-stop);\n background: color-mix(in srgb, var(--dt-stop) 6%, transparent);\n border-radius: var(--dt-radius); padding: 12px 13px; margin-top: 26px;\n}\n.dt-data-ask { margin: 0 0 10px; font-size: 12px; line-height: 1.6; }\n.dt-data-row { display: flex; gap: 8px; flex-wrap: wrap; }\n.dt-danger {\n padding: 5px 12px; border-radius: 7px; border: 1px solid var(--dt-stop);\n background: var(--dt-stop); color: #fff;\n font: inherit; font-size: 11.5px; font-weight: 550; cursor: pointer;\n}\n.dt-danger:disabled { opacity: .55; cursor: default; }\n\n/* ---- now learning ------------------------------------------------------ */\n.dt-now {\n padding: 13px 14px 14px;\n border: 1px solid var(--dt-border);\n border-radius: var(--dt-radius);\n background: var(--dt-surface);\n}\n/* The state is the headline fact about the node, so it colours the card edge. */\n.dt-now[data-state=\"blocked\"] { border-left: 3px solid var(--dt-stop); }\n.dt-now[data-state=\"weak\"] { border-left: 3px solid var(--dt-warn); }\n.dt-now[data-state=\"confirmed\"] { border-left: 3px solid var(--dt-good); }\n.dt-now[data-state=\"checked\"] { border-left: 3px solid #0f8f8f; }\n.dt-now[data-state=\"practiced\"] { border-left: 3px solid #5b53d6; }\n.dt-now[data-state=\"explained\"] { border-left: 3px solid #2f6feb; }\n.dt-now[data-state=\"unconfirmed\"] { border-left: 3px solid var(--dt-border-strong); }\n\n.dt-now-eyebrow { color: var(--dt-muted); }\n.dt-now-title {\n font-size: 17px; font-weight: 650; letter-spacing: -.015em;\n margin: 6px 0 8px; line-height: 1.28; overflow-wrap: anywhere;\n}\n.dt-now-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 9px; }\n.dt-now-rel { color: var(--dt-muted); font-size: 11.5px; }\n.dt-now-note { margin: 0; color: var(--dt-muted); font-size: 12px; line-height: 1.6; }\n.dt-now-action { margin-top: 13px; width: 100%; }\n\n/* ---- diagnosis map ----------------------------------------------------- */\n.dt-tree { display: flex; flex-direction: column; gap: 1px; }\n.dt-tab-node {\n display: grid; grid-template-columns: auto 1fr auto;\n align-items: center; gap: 8px; width: 100%;\n padding: 6px 8px; margin: 0; border-radius: 7px;\n border: 1px solid transparent; border-left: 3px solid transparent;\n background: transparent; color: inherit; font: inherit;\n text-align: left; cursor: pointer; position: relative;\n}\n/* Depth as a drawn guide rather than only as indentation: a nested node should\n read as nested at a glance. */\n.dt-tab-node[data-depth]::before {\n content: ''; position: absolute; left: 4px; top: 0; bottom: 0;\n width: 1px; background: var(--dt-border);\n}\n.dt-tab-node[data-depth=\"2\"]::before { left: 18px; }\n.dt-tab-node[data-depth=\"3\"]::before { left: 32px; }\n.dt-tab-node:hover { background: var(--dt-surface); }\n.dt-tab-node[aria-current=\"true\"] {\n background: var(--dt-surface); border-color: var(--dt-border-strong);\n border-left-color: var(--dt-accent);\n}\n/* A blocker should be findable without reading every row. */\n.dt-tab-node[data-attention] { background: rgba(192, 57, 43, .05); }\n.dt-tab-node[data-attention]:hover { background: rgba(192, 57, 43, .09); }\n.dt-tab-node-title { min-width: 0; overflow-wrap: anywhere; line-height: 1.45; }\n.dt-tab-node[data-depth=\"0\"] > .dt-tab-node-title { font-weight: 600; }\n\n.dt-dot {\n width: 8px; height: 8px; border-radius: 50%;\n border: 1.5px solid currentColor; background: transparent; flex: none;\n}\n.dt-dot[data-filled=\"true\"] { background: currentColor; }\n\n.dt-state-unconfirmed { color: #8b93a1; }\n.dt-state-explained { color: #2f6feb; }\n.dt-state-practiced { color: #5b53d6; }\n.dt-state-checked { color: #0f8f8f; }\n.dt-state-weak { color: var(--dt-warn); }\n.dt-state-blocked { color: var(--dt-stop); }\n.dt-state-confirmed { color: var(--dt-good); }\n\n/* A tinted chip scans far better than coloured words alone. */\n.dt-tab-state {\n font-size: 10.5px; font-weight: 560; letter-spacing: .01em;\n padding: 1px 7px; border-radius: 999px;\n border: 1px solid currentColor; white-space: nowrap;\n background: color-mix(in srgb, currentColor 10%, transparent);\n}\n.dt-state { font-style: normal; }\n\n/* ---- node detail (full panel) ------------------------------------------ */\n.dt-detail h2 { font-size: 16px; margin: 0 0 8px; font-weight: 650; letter-spacing: -.01em; }\n.dt-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 12px; }\n.dt-chip {\n font-size: 11px; color: var(--dt-muted);\n border: 1px solid var(--dt-border); border-radius: 999px; padding: 1px 8px;\n}\n.dt-why { margin: 0 0 14px; color: var(--dt-muted); font-size: 12.5px; line-height: 1.6; }\n.dt-node {\n display: grid; grid-template-columns: auto 1fr auto;\n align-items: center; gap: 8px; width: 100%;\n padding: 6px 8px; margin: 1px 0; border-radius: 7px;\n border: 1px solid transparent; background: transparent;\n color: inherit; font: inherit; text-align: left; cursor: pointer;\n}\n.dt-node:hover { background: var(--dt-surface); }\n.dt-node[aria-current=\"true\"] { background: var(--dt-surface); border-color: var(--dt-border-strong); }\n.dt-node-title { min-width: 0; overflow-wrap: anywhere; }\n.dt-node-rel { display: block; color: var(--dt-muted); font-size: 11px; font-weight: 400; }\n\n/* ---- evidence ---------------------------------------------------------- */\n/* Compact on purpose: this is a record, not a reading surface. One row per\n entry, with the note clamped so a long one cannot bury the next. */\n.dt-evidence { list-style: none; margin: 0; padding: 0; }\n.dt-evidence li {\n display: grid; grid-template-columns: auto 1fr; gap: 2px 8px;\n padding: 7px 0 7px 10px; border-left: 2px solid var(--dt-border);\n font-size: 11.5px; line-height: 1.5;\n}\n.dt-evidence li + li { margin-top: 2px; }\n.dt-ev-kind {\n font-weight: 600; font-size: 11px; color: var(--dt-text);\n text-transform: capitalize;\n}\n.dt-ev-readiness {\n font-size: 10.5px; color: var(--dt-muted);\n border: 1px solid var(--dt-border); border-radius: 999px; padding: 0 6px;\n justify-self: start;\n}\n.dt-ev-note {\n grid-column: 1 / -1; color: var(--dt-muted);\n display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* ---- buttons ----------------------------------------------------------- */\n.dt-primary {\n padding: 7px 14px; border-radius: 8px; border: 1px solid transparent;\n background: var(--dt-accent); color: #fff;\n font: inherit; font-weight: 550; cursor: pointer;\n}\n.dt-primary:hover { filter: brightness(1.07); }\n.dt-primary:disabled { opacity: .5; cursor: default; filter: none; }\n.dt-secondary {\n margin-top: 8px; padding: 6px 12px; border-radius: 8px;\n border: 1px solid var(--dt-border); background: transparent;\n color: inherit; font: inherit; font-size: 12px; cursor: pointer;\n}\n.dt-secondary:hover { background: var(--dt-surface); }\n\n/* ---- lesson ------------------------------------------------------------ */\n.dt-lesson { margin-top: 20px; }\n.dt-lesson-body {\n border: 1px solid var(--dt-border); border-radius: var(--dt-radius);\n background: var(--dt-bg); padding: 16px 16px 6px;\n}\n.dt-lesson-head {\n display: flex; align-items: baseline; justify-content: space-between; gap: 8px;\n padding-bottom: 10px; margin-bottom: 14px;\n border-bottom: 1px solid var(--dt-border);\n}\n.dt-lesson-title { font-size: 15px; font-weight: 650; margin: 0; letter-spacing: -.01em; line-height: 1.35; }\n.dt-tab-lesson-title { font-weight: 600; }\n.dt-origin {\n flex: none; font-size: 9.5px; letter-spacing: .08em; text-transform: uppercase;\n color: var(--dt-muted); border: 1px solid var(--dt-border);\n border-radius: 999px; padding: 1px 7px;\n}\n\n/* ---- blocks ------------------------------------------------------------ */\n.dt-block { margin: 0 0 18px; }\n/* A readable measure: full-width prose in a docked panel is tiring. */\n.dt-block-md { margin: 0 0 11px; line-height: 1.66; max-width: 62ch; }\n.dt-block-md:last-child { margin-bottom: 0; }\n.dt-block-md strong { font-weight: 640; }\n\n/* Headings the teaching brain writes inline as ### become real headings. */\n.dt-md-h2 {\n font-size: 15px; font-weight: 650; letter-spacing: -.01em;\n margin: 20px 0 9px; line-height: 1.35;\n}\n.dt-md-h2:first-child, .dt-md-h3:first-child { margin-top: 0; }\n.dt-md-h3 {\n font-size: 13.5px; font-weight: 650; color: var(--dt-text);\n margin: 18px 0 7px; line-height: 1.4;\n}\n.dt-md-ul, .dt-md-ol { margin: 0 0 12px; padding-left: 20px; max-width: 62ch; }\n.dt-md-ul li, .dt-md-ol li { margin-bottom: 5px; line-height: 1.6; }\n.dt-md-ul { list-style: none; padding-left: 4px; }\n.dt-md-ul li { position: relative; padding-left: 16px; }\n.dt-md-ul li::before {\n content: ''; position: absolute; left: 3px; top: .62em;\n width: 4px; height: 4px; border-radius: 50%; background: var(--dt-border-strong);\n}\n.dt-md-ol { list-style: decimal; }\n.dt-md-ol li::marker { color: var(--dt-muted); font-variant-numeric: tabular-nums; }\n\n.dt-block-label {\n color: var(--dt-accent); margin-bottom: 7px;\n}\n\n/* Worked example: a distinct panel, because it is a different kind of reading\n from the prose around it. */\n.dt-block-example {\n border: 1px solid var(--dt-border); border-left: 3px solid var(--dt-border-strong);\n border-radius: var(--dt-radius); background: var(--dt-surface);\n padding: 12px 14px 13px;\n}\n.dt-block-example h4 { margin: 0 0 9px; font-size: 13px; font-weight: 620; line-height: 1.4; }\n.dt-block-example ol { margin: 0; padding-left: 19px; max-width: 62ch; }\n.dt-block-example li { margin-bottom: 6px; line-height: 1.6; }\n.dt-block-example li::marker { color: var(--dt-muted); font-variant-numeric: tabular-nums; }\n.dt-takeaway {\n margin: 11px 0 0; padding-top: 9px; border-top: 1px dashed var(--dt-border);\n font-size: 12px; color: var(--dt-text);\n}\n\n/* Diagram: monospace deserves its own frame so it reads as a figure. */\n.dt-block-diagram { padding: 0; }\n.dt-pre {\n margin: 0; padding: 12px 14px; overflow-x: auto;\n background: var(--dt-sunken); border: 1px solid var(--dt-border);\n border-radius: var(--dt-radius);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px; line-height: 1.6; white-space: pre;\n}\n\n/* Check: this is the moment the lesson hands over to the learner, so it gets\n the strongest treatment on the surface. */\n.dt-block-check {\n border: 1px solid var(--dt-accent); border-radius: var(--dt-radius);\n background: color-mix(in srgb, var(--dt-accent) 5%, var(--dt-bg));\n padding: 0; overflow: hidden;\n}\n.dt-check-head {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 9px 14px; background: var(--dt-accent); color: #fff;\n}\n.dt-check-tag {\n font-size: 10px; letter-spacing: .12em; text-transform: uppercase; font-weight: 650;\n}\n.dt-check-where { font-size: 11px; opacity: .9; }\n.dt-check-body { padding: 13px 14px 4px; }\n.dt-check-body .dt-block-md:last-child { margin-bottom: 11px; }\n.dt-check-hint {\n margin: 0; padding: 9px 14px 11px; border-top: 1px dashed var(--dt-border);\n font-size: 11.5px; color: var(--dt-muted);\n}\n.dt-check-hint span {\n font-size: 10px; letter-spacing: .1em; text-transform: uppercase;\n margin-right: 7px; color: var(--dt-accent);\n}\n\n/* ---- math (styled, not typeset) ---------------------------------------- */\n.dt-math {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: .95em; padding: 0 3px; border-radius: 4px;\n background: var(--dt-sunken); white-space: nowrap;\n}\n.dt-math-block {\n margin: 0 0 12px; padding: 11px 13px; overflow-x: auto;\n background: var(--dt-sunken); border: 1px solid var(--dt-border);\n border-radius: var(--dt-radius);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12px; white-space: pre-wrap;\n}\n\n/* ---- handoff progress -------------------------------------------------- */\n.dt-handoff {\n display: flex; align-items: center; gap: 8px; flex-wrap: wrap;\n margin: 0 0 12px; padding: 8px 11px; border-radius: 8px;\n background: var(--dt-surface); border: 1px solid var(--dt-border);\n font-size: 12px;\n}\n.dt-handoff-dot {\n width: 7px; height: 7px; border-radius: 50%; background: var(--dt-accent);\n animation: dt-pulse 1.4s ease-in-out infinite;\n}\n.dt-handoff[data-phase=\"lesson-ready\"] {\n border-color: color-mix(in srgb, var(--dt-good) 45%, var(--dt-border));\n background: color-mix(in srgb, var(--dt-good) 7%, var(--dt-bg));\n}\n.dt-handoff[data-phase=\"lesson-ready\"] .dt-handoff-dot { background: var(--dt-good); animation: none; }\n.dt-handoff[data-phase=\"failed\"] .dt-handoff-dot,\n.dt-handoff[data-phase=\"stalled\"] .dt-handoff-dot { background: var(--dt-stop); animation: none; }\n@keyframes dt-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .3 } }\n.dt-handoff-label { font-weight: 550; }\n.dt-handoff-time, .dt-handoff-try, .dt-handoff-detail { color: var(--dt-muted); font-size: 11px; }\n.dt-handoff-detail { flex-basis: 100%; }\n.dt-handoff-retry {\n margin-left: auto; padding: 3px 10px; border-radius: 6px;\n border: 1px solid var(--dt-border); background: transparent;\n color: inherit; font: inherit; font-size: 11px; cursor: pointer;\n}\n.dt-handoff-retry:hover { background: var(--dt-bg); }\n@media (prefers-reduced-motion: reduce) { .dt-handoff-dot { animation: none } }\n\n/* ---- next best step ---------------------------------------------------- */\n/* The conclusion of a node. It should be the most deliberate thing on the\n surface: what just finished, where it sends you, why, and one button. */\n.dt-next {\n margin: 20px 0 4px; padding: 14px 15px 15px; border-radius: var(--dt-radius);\n border: 1px solid var(--dt-accent);\n background: color-mix(in srgb, var(--dt-accent) 6%, var(--dt-bg));\n box-shadow: 0 1px 2px rgba(0, 0, 0, .04);\n}\n.dt-next-label {\n display: flex; align-items: center; gap: 6px;\n color: var(--dt-accent); font-weight: 650; margin-bottom: 10px;\n}\n.dt-next-arrow { font-size: 12px; }\n.dt-next-from { margin: 0 0 8px; color: var(--dt-muted); font-size: 11.5px; }\n.dt-next-tick { color: var(--dt-good); margin-right: 5px; }\n.dt-next-target {\n margin: 0 0 10px; font-size: 14.5px; line-height: 1.4; letter-spacing: -.01em;\n}\n.dt-next-target b { font-weight: 660; }\n.dt-next-why { margin: 0; font-size: 12px; line-height: 1.6; color: var(--dt-text); }\n.dt-next-why-label { color: var(--dt-muted); }\n.dt-next .dt-primary { margin-top: 14px; width: 100%; }\n\n/* ---- docked tab -------------------------------------------------------- */\n.dt-tab { padding: 14px 14px 40px; font-size: 12.5px; color: var(--dt-text); text-align: left; }\n.dt-tab-head { display: flex; align-items: baseline; gap: 8px; justify-content: space-between; }\n.dt-tab-title { font-size: 15px; font-weight: 650; margin: 0; }\n.dt-tab-meta { color: var(--dt-muted); font-size: 11px; margin: 4px 0 8px; }\n.dt-tab-action { margin-top: 10px; }\n.dt-tab-section {\n display: flex; align-items: baseline; gap: 8px;\n margin: 24px 0 8px; padding-top: 14px;\n border-top: 1px solid var(--dt-border);\n}\n.dt-tab-section span { text-transform: none; letter-spacing: 0; font-size: 11px; }\n\n.dt-block-unknown {\n border: 1px dashed var(--dt-border); border-radius: 8px;\n padding: 10px 12px; color: var(--dt-muted);\n}\n\n`\n\n/**\n * Inject the stylesheet once.\n *\n * @returns a disposer that removes the element, so an unload leaves no residue.\n */\nexport function injectStyles(): () => void {\n const existing = document.querySelector(`style[${STYLE_ATTR}]`)\n if (existing) return () => {}\n\n const element = document.createElement('style')\n element.setAttribute(STYLE_ATTR, '')\n element.textContent = CSS\n document.head.appendChild(element)\n return () => element.remove()\n}\n","/**\n * dsh-diagnostic-tutor — browser half.\n *\n * Registers two things, which together are the whole navigation story:\n *\n * - an icon in the left sidebar (`sidebar.panellist`, a `list`), whose `id`\n * addresses the matching main panel;\n * - the panel itself (`main`, a `keyed` slot) under that same id.\n *\n * The framework supplies the button chrome and the panel switching; this module\n * only says what the icon looks like and what the panel renders. No DSH\n * component is re-implemented and no DSH internal is reached into.\n *\n * Both registrations go through `ctx.slots.inject(...)`, which is mandatory\n * rather than stylistic: registering into a slot before its owner has declared\n * it throws.\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\n// Type-only side-effect imports. `ctx.slots` is declared by ui-renderer, and\n// each UI package contributes its own `SlotMap` rows through its `/client`\n// subpath — so these are what make `ctx.slots` and the slot keys typecheck.\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-layout/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar-right/client'\nimport type { ReactNode } from 'react'\n\nimport { LearningPanel } from './app.js'\nimport { fetchOverview } from './api.js'\nimport { LearningTab, LearningTabTitle } from './tab.jsx'\nimport { injectStyles } from './styles.js'\n\n/** Client module name; distinct from the host plugin's name. */\nexport const name = 'diagnostic-tutor-client'\n\n/**\n * `slots` is the only service needed.\n *\n * Declared here rather than in the host half because it is a *client* service:\n * the browser half runs in its own Cordis context against the page's registry.\n */\nexport const inject = ['slots', 'layout', 'sidebarRightTabs']\n\n/**\n * Identifier shared by the sidebar entry and the main panel.\n *\n * These two must match — the sidebar's list `id` is what the layout uses to\n * select the `main` key — so they are derived from one constant.\n */\nexport const PANEL_ID = 'diagnostic-tutor'\n\n/**\n * The docked tab's identity.\n *\n * A tab type has two names: `id` is the implementation's own identity and is\n * what its body and title register under; `kind` is the discriminator `openTab`\n * names. They are the same string here because this plugin ships exactly one\n * implementation of exactly one kind.\n */\nexport const TAB_ID = 'diagnostic-tutor'\n\n/** The sidebar icon: a small node graph, drawn inline so it needs no assets. */\nfunction PanelIcon({ size, active }: PropsRuntime<'sidebar.panellist'>): ReactNode {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={active ? 1.6 : 1.3}\n aria-hidden=\"true\"\n >\n <circle cx=\"8\" cy=\"3\" r=\"2\" />\n <circle cx=\"3.5\" cy=\"12\" r=\"2\" />\n <circle cx=\"12.5\" cy=\"12\" r=\"2\" />\n <path d=\"M8 5v2.2M8 7.2 4.6 10.3M8 7.2l3.4 3.1\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\n/**\n * The panel body, with the session the tutor should be woken in threaded\n * through, plus a way back to the conversation.\n *\n * That last part matters more than it looks. The map, the node detail and the\n * learning surface occupy the **main** column, which is the same column the\n * conversation lives in — so while the panel is open the learner cannot type.\n * Checks are answered in the chat, so the surface needs a door back to it, and\n * `ctx.layout.selectPanel(null)` is the documented way to show the conversation\n * again.\n *\n * `useSessions` is part of the standard kit every root-scoped slot receives, so\n * the panel can name the session it is looking at without any plumbing of its\n * own. The host needs it to reach the right agent when the learner presses\n * Start learning.\n */\nfunction MainPanel({\n useSessions,\n onOpenChat,\n onFocusStarted,\n}: {\n useSessions?: ((select: (state: SessionListLike) => unknown) => unknown) | undefined\n onOpenChat?: (() => void) | undefined\n onFocusStarted?: (() => void) | undefined\n}): ReactNode {\n const sessionId = useSessions?.((state) => state.ids[0])\n return (\n <LearningPanel\n {...(typeof sessionId === 'string' ? { sessionId } : {})}\n {...(onOpenChat === undefined ? {} : { onOpenChat })}\n {...(onFocusStarted === undefined ? {} : { onFocusStarted })}\n />\n )\n}\n\n/** The slice of the session-list snapshot this panel reads. */\ninterface SessionListLike {\n ids: string[]\n}\n\n/** Props the slot supplies; re-declared for the bound wrapper. */\ninterface MainPanelProps {\n useSessions?: ((select: (state: SessionListLike) => unknown) => unknown) | undefined\n}\n\n/**\n * Load the browser half.\n *\n * @param ctx - the client Cordis context.\n */\nexport function apply(ctx: Context): void {\n // The sheet is injected once and removed with the plugin.\n ctx.effect(() => injectStyles())\n\n ctx.slots.inject('sidebar.panellist', () =>\n ctx.slots.register(\n {\n name: 'sidebar.panellist',\n id: PANEL_ID,\n order: 40,\n label: 'Learn',\n },\n PanelIcon,\n ),\n )\n\n // Captured once: the panel needs *actions*, and the standard props a slot\n // receives only expose reads.\n const layout = ctx.get('layout')\n\n ctx.slots.inject('main', () =>\n ctx.slots.register(\n {\n name: 'main',\n key: PANEL_ID,\n },\n function BoundMainPanel(props: MainPanelProps): ReactNode {\n return (\n <MainPanel\n {...props}\n onOpenChat={() => {\n // Show the conversation, then dock: the session surface that the\n // right sidebar needs comes back with it.\n layout?.selectPanel(null)\n openDockedTab()\n }}\n onFocusStarted={() => openDockedTab()}\n />\n )\n },\n ),\n )\n\n // ---------------------------------------------------------------------------\n // The docked tab.\n //\n // This is what lets the chat and the learning surface coexist. The full panel\n // takes the main column — the same column the conversation lives in — so with\n // only that panel the learner must leave the lesson to answer a check. The\n // right sidebar is a separate column, so the same runtime sits beside the\n // chat.\n //\n // Registration is two stages, and the order is enforced by the framework:\n // declare the tab type, then register the body and title under the type's own\n // `id`, because that is the key the seats dispatch on.\n // ---------------------------------------------------------------------------\n ctx.sidebarRightTabs.register({\n id: TAB_ID,\n kind: TAB_ID,\n // A page type, not a resource viewer: it has no address globs, so it is\n // opened by kind and never competes to render someone else's resource.\n title: () => 'Learning',\n priority: 'extension',\n })\n\n /**\n * Dock the tab, retrying briefly.\n *\n * The right sidebar hosts **session-scoped** tabs, so it can only accept one\n * while a session surface is mounted — and while the full panel occupies the\n * main column, no session surface exists. Returning to the conversation\n * mounts one, so the useful moment to dock is just after that. Retrying\n * covers the gap between the click and the surface mounting.\n */\n const openDockedTab = (attempt = 0): void => {\n // Resolved with `get`, not read as a property: this plugin injects the\n // *registry*, not the navigation controller, and reading a service that was\n // never injected throws rather than returning undefined.\n const sidebarRight = ctx.get('sidebarRight')\n if (!sidebarRight) return\n try {\n sidebarRight.openTab(TAB_ID)\n } catch {\n if (attempt < 8) setTimeout(() => openDockedTab(attempt + 1), 400)\n }\n }\n\n ctx.slots.inject('sidebar.right.pane.tab', () =>\n ctx.slots.register({ name: 'sidebar.right.pane.tab', key: TAB_ID }, LearningTab),\n )\n ctx.slots.inject('sidebar.right.pane.tab.title', () =>\n ctx.slots.register({ name: 'sidebar.right.pane.tab.title', key: TAB_ID }, LearningTabTitle),\n )\n\n // Resume: if a focus is already active — a session being picked back up —\n // have the surface docked and waiting rather than hidden behind a click.\n void fetchOverview()\n .then((overview) => {\n if (overview.course !== null && overview.focus !== null) openDockedTab()\n })\n .catch(() => {\n // The API may not be mounted (a client-only page); nothing to open.\n })\n}\n\nexport { LearningPanel } from './app.js'\nexport { LearningTab, LearningTabTitle } from './tab.jsx'\nexport { LessonBody, BlockView, BLOCK_RENDERERS } from './blocks.js'\nexport type { PanelClient } from './app.js'\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["BASE","useSyncExternalStore","en","zh","createElement","en","zh","useState","en","zh","BASE","request","en","zh","codeFor","Fragment","useState","useRef","useCallback","useState","useCallback","useState","useCallback","realApi.fetchOverview","realApi.fetchNode","realApi.fetchLesson","realApi.startFocus","realApi.selectProject","realApi.resolveProjectSession","realApi.reportObserved","realApi.downloadExport","realApi.resetState","useRef","useState","useCallback","useState","useMemo","useState","useState","useSyncExternalStore","useMemo","useState","useState"],"sources":["../src/client/i18n.ts","../src/client/learning-text.ts","../src/client/mathml.tsx","../src/block-types.ts","../src/client/lesson-text.ts","../src/client/map-text.ts","../src/client/model.ts","../src/client/blocks.tsx","../src/client/design-icons.tsx","../src/client/design-text.ts","../src/client/language-toggle.tsx","../src/client/path-events.ts","../src/client/today.tsx","../src/client/path-client.ts","../src/client/path-text.ts","../src/client/path-fit.ts","../src/client/tutor-error.ts","../src/client/path-page.tsx","../src/client/review-panel.tsx","../src/client/insights-panel.tsx","../src/client/api.ts","../src/client/use-learning.ts","../src/client/map-panel.tsx","../src/client/use-session-activity.ts","../src/client/course-learning.tsx","../src/client/use-session.ts","../src/client/tab.tsx","../src/client/design-styles.ts","../src/client/path-styles.ts","../src/client/styles.ts","../src/client/visuals.tsx","../src/client/panel-text.ts","../src/client/app.tsx","../src/client/index.tsx"],"sourcesContent":["/**\n * Interface language for this plugin's browser surfaces.\n *\n * The learner's choice is stored in two places, for two different reasons:\n *\n * - the host's learner profile (`preferredLanguage`), because that is the\n * preference that survives a restart and follows the learner across\n * conversations and profiles; and\n * - `localStorage`, so the very first render after a reload does not flash\n * English while the preference request is in flight.\n *\n * The host is authoritative. A stored local value is only a starting guess;\n * `hydrateLanguage()` replaces it as soon as the host answers. A failed request\n * keeps the local choice — the UI never flips back because the network did.\n *\n * No DSH internals are reached into here: this is a browser-side preference for\n * the plugin's own copy. It does not translate the harness, the model's\n * teaching, or the learner's own content.\n */\n\nimport { useSyncExternalStore } from 'react'\n\n/** The languages this build ships copy for. */\nexport type Lang = 'en' | 'zh'\n\n/** Route prefix owned by this plugin; must match the host's `API_PREFIX`. */\nconst BASE = '/diagnostic-tutor/api'\n\n/** Where the fallback copy lives. Namespaced so another plugin cannot collide. */\nexport const STORAGE_KEY = 'dsh-diagnostic-tutor.language'\n\n/** Narrow an arbitrary stored/response value to a language we render. */\nfunction asLang(value: unknown): Lang | null {\n return value === 'en' || value === 'zh' ? value : null\n}\n\n/** The saved local choice, when there is one and storage is reachable. */\nfunction storedLang(): Lang | null {\n try {\n return asLang(globalThis.localStorage?.getItem(STORAGE_KEY))\n } catch {\n // Private modes and sandboxed frames can throw on access; the preference is\n // a convenience, never a requirement.\n return null\n }\n}\n\n/**\n * A first guess before the host is asked.\n *\n * Chinese is selected only for a Chinese browser/UI locale; everything else\n * starts in English, which is the language every surface already shipped in.\n * The learner can always change it with the toggle.\n */\nfunction detectedLang(): Lang {\n const tags = [globalThis.navigator?.language ?? '', globalThis.document?.documentElement?.lang ?? '']\n return tags.some((tag) => /^zh/i.test(tag)) ? 'zh' : 'en'\n}\n\nlet current: Lang = storedLang() ?? detectedLang()\n\n/**\n * Whether a real DSH host is behind this page.\n *\n * Only the plugin's `apply()` turns this on, so the preview, tests and any\n * embedded surface that renders components directly never make a request they\n * cannot satisfy — and the fixture harness's \"no learner-data request\" check\n * stays honest.\n */\nlet hostPersistence = false\n\n/** Enable saving the preference to the host. Called once by the real client. */\nexport function enableLanguagePersistence(): void {\n hostPersistence = true\n}\n\n/**\n * Whether the learner chose a language in this page session.\n *\n * A slow preference read must not overwrite a click that happened while it was\n * in flight: the learner's latest explicit action wins over the stored value.\n */\nlet userChoice = false\n\n/** Everything that renders copy subscribes to this set. */\nconst listeners = new Set<() => void>()\n\nfunction announce(): void {\n for (const listener of listeners) listener()\n}\n\n/** Apply a language without persisting it (used for the host's saved value). */\nfunction adopt(lang: Lang): void {\n if (lang === current) return\n current = lang\n announce()\n}\n\n/** Remember locally, then tell the host — never blocking the UI on either. */\nfunction persist(lang: Lang): void {\n try {\n globalThis.localStorage?.setItem(STORAGE_KEY, lang)\n } catch {\n // Best effort only, same reasoning as `storedLang`.\n }\n if (!hostPersistence) return\n try {\n void fetch(`${BASE}/preferences/language`, {\n method: 'POST',\n headers: { accept: 'application/json', 'content-type': 'application/json' },\n body: JSON.stringify({ language: lang }),\n }).catch(() => {\n // The local choice still stands; the next toggle will try again.\n })\n } catch {\n // A missing `fetch` (tests, exotic frames) is not a reason to lose the choice.\n }\n}\n\n/** The language the plugin is rendering in. */\nexport function getLang(): Lang {\n return current\n}\n\n/**\n * Change the interface language.\n *\n * Applied immediately so the button responds on the same click, then saved.\n *\n * @param lang - the language to switch to.\n */\nexport function setLang(lang: Lang): void {\n if (lang === current) return\n current = lang\n userChoice = true\n announce()\n persist(lang)\n}\n\n/** Subscribe to changes. Exported for `useSyncExternalStore` callers and tests. */\nexport function subscribeLang(listener: () => void): () => void {\n listeners.add(listener)\n return () => listeners.delete(listener)\n}\n\n/** The current language as React state. */\nexport function useLang(): Lang {\n return useSyncExternalStore(subscribeLang, getLang, getLang)\n}\n\n/**\n * Read the learner's saved preference from the host.\n *\n * Called once when the real client plugin loads, never from the preview or from\n * tests — those mount components directly and have no host to ask. A failure,\n * an older host without the route, or an unrecognised value leaves the\n * detected/local guess in place.\n */\nexport async function hydrateLanguage(): Promise<void> {\n try {\n const response = await fetch(`${BASE}/preferences`, { headers: { accept: 'application/json' } })\n if (!response.ok) return\n const body = (await response.json()) as { ok?: unknown; language?: unknown }\n if (body !== null && typeof body === 'object' && body.ok === false) return\n const language = asLang(body?.language)\n // The learner's own click while this was in flight outranks the stored value.\n if (language !== null && !userChoice) adopt(language)\n } catch {\n // No host, no problem: the UI keeps working in the detected language.\n }\n}\n\n/** Test seam: restore a clean store between cases. */\nexport function resetLanguageForTests(): void {\n current = 'en'\n hostPersistence = false\n userChoice = false\n try {\n globalThis.localStorage?.removeItem(STORAGE_KEY)\n } catch {\n // Nothing to clean if storage was never reachable.\n }\n announce()\n}\n","/**\n * Interface copy shared by the lesson surfaces that predate the course route:\n * the docked Current-lesson tab, the standalone full-page panel, the next-step\n * card, the handoff line and the welcome feature row.\n *\n * English is the copy those surfaces already shipped with. Chinese is the same\n * interface, not a translation of the learner's content: node titles, lesson\n * markdown and model reasons stay exactly as stored.\n *\n * The tab and the full page keep their own larger tables (`tab-text.ts`,\n * `panel-text.ts`); this module holds only what more than one of them renders.\n */\n\nimport { useLang } from './i18n.js'\n\nconst en = {\n // ---- sidebar / tab identity ------------------------------------------\n learnLabel: 'Learn',\n learnAria: 'Learn',\n noSessionNotice: 'Open or create a conversation to start learning.',\n tabTitle: 'Current lesson',\n tabTitleFull: 'Current lesson',\n\n // ---- the next-step card ---------------------------------------------\n nextBestStep: 'Next best step',\n nextPrefix: 'Next:',\n stayOn: (title: string) => `Stay on ${title}`,\n why: 'Why:',\n starting: 'Starting…',\n\n // ---- the handoff line -----------------------------------------------\n handoffLabel: (phase: string): string => {\n switch (phase) {\n case 'failed':\n return 'Could not reach the tutor'\n case 'lesson-ready':\n return 'Lesson ready'\n case 'stalled':\n return 'The tutor has gone quiet'\n case 'tutor-working':\n return 'Tutor working…'\n case 'tutor-requested':\n return 'Tutor requested…'\n case 'focus-recorded':\n return 'Focus recorded'\n default:\n return phase\n }\n },\n handoffAttempts: (count: number) => `attempt ${count}`,\n askAgain: 'Ask again',\n\n // ---- the welcome feature row ---------------------------------------\n featureMapTitle: 'Diagnosis map',\n featureMapBody: 'grown one node at a time',\n featureLessonTitle: 'A lesson',\n featureLessonBody: 'written beside the chat',\n featureEvidenceTitle: 'Evidence',\n featureEvidenceBody: 'behind every state',\n}\n\nconst zh: typeof en = {\n // ---- sidebar / tab identity ------------------------------------------\n learnLabel: '学习',\n learnAria: '学习',\n noSessionNotice: '请先打开或新建一个对话,再开始学习。',\n tabTitle: '当前课次',\n tabTitleFull: '当前课次',\n\n // ---- the next-step card ---------------------------------------------\n nextBestStep: '下一步建议',\n nextPrefix: '下一步:',\n stayOn: (title) => `继续停留在「${title}」`,\n why: '原因:',\n starting: '正在开始…',\n\n // ---- the handoff line -----------------------------------------------\n handoffLabel: (phase) => {\n switch (phase) {\n case 'failed':\n return '未能联系到导师'\n case 'lesson-ready':\n return '教学内容已就绪'\n case 'stalled':\n return '导师没有继续响应'\n case 'tutor-working':\n return '导师正在处理…'\n case 'tutor-requested':\n return '已请求导师…'\n case 'focus-recorded':\n return '已记录学习焦点'\n default:\n return phase\n }\n },\n handoffAttempts: (count) => `第 ${count} 次尝试`,\n askAgain: '再问一次',\n\n // ---- the welcome feature row ---------------------------------------\n featureMapTitle: '诊断地图',\n featureMapBody: '一次只展开一个节点',\n featureLessonTitle: '教学内容',\n featureLessonBody: '写在对话旁边',\n featureEvidenceTitle: '证据',\n featureEvidenceBody: '支撑每一个状态',\n}\n\n/** The shared copy in the current interface language. */\nexport function useLearningText(): typeof en {\n return useLang() === 'zh' ? zh : en\n}\n\n/** The docked tab's title, for the tab registry that cannot use a hook. */\nexport function tabTitleFor(lang: 'en' | 'zh'): string {\n return lang === 'zh' ? zh.tabTitle : en.tabTitle\n}\n\n/** Feedback the store returns after an action, in the interface language. */\nexport interface RuntimeCopy {\n readonly started: string\n readonly unknownReason: string\n readonly focusRecorded: (reason: string) => string\n readonly saved: (name: string) => string\n readonly exportFailed: (reason: string) => string\n readonly everythingDeleted: string\n readonly deleteFailed: (reason: string) => string\n}\n\n/**\n * The learner-facing status lines the shared store produces.\n *\n * `use-learning.ts` is not a component, so it cannot subscribe with a hook;\n * it reads the current language when it writes the line. A line already on\n * screen keeps the language it was written in until the next action, which is\n * the same behaviour as a status message anywhere else.\n */\nexport function runtimeCopy(lang: 'en' | 'zh'): RuntimeCopy {\n if (lang === 'zh') {\n return {\n started: '已开始。导师正在对话中教学本节点,界面会随内容更新。',\n unknownReason: '未知原因',\n focusRecorded: (reason) => `已记录学习焦点,但未能唤醒导师(${reason})。在对话中说点什么即可继续。`,\n saved: (name) => `已保存 ${name}。`,\n exportFailed: (reason) => `导出失败:${reason}`,\n everythingDeleted: '已删除全部数据。',\n deleteFailed: (reason) => `删除失败:${reason}`,\n }\n }\n return {\n started: 'Started. The tutor is teaching this node in the chat — the surface updates as it writes.',\n unknownReason: 'unknown reason',\n focusRecorded: (reason) => `Focus recorded, but the tutor was not woken (${reason}). Say anything in the chat to continue.`,\n saved: (name) => `Saved ${name}.`,\n exportFailed: (reason) => `Could not export: ${reason}`,\n everythingDeleted: 'Everything was deleted.',\n deleteFailed: (reason) => `Could not delete: ${reason}`,\n }\n}\n","/**\n * A deliberately small MathML renderer.\n *\n * The host owns the conversion from TeX to MathML; the browser must not hand a\n * raw markup string to `dangerouslySetInnerHTML`. Instead this module parses the\n * stored MathML in the DOM, walks it, and rebuilds only the elements and\n * attributes on an allow-list. Anything unknown is unwrapped to its text, and\n * script/event/link attributes are dropped.\n */\n\nimport { createElement, type ReactNode } from 'react'\n\nconst ALLOWED_TAGS = new Set([\n 'math', 'semantics', 'annotation', 'annotation-xml', 'mrow', 'mi', 'mo', 'mn', 'mtext',\n 'mspace', 'mfrac', 'msqrt', 'mroot', 'msub', 'msup', 'msubsup', 'munder', 'mover',\n 'munderover', 'mtable', 'mtr', 'mtd', 'mlabeledtr', 'mpadded', 'mphantom', 'menclose',\n 'mmultiscripts', 'mstyle', 'merror',\n])\n\nconst ALLOWED_ATTRS = new Set([\n 'xmlns', 'display', 'mathvariant', 'stretchy', 'fence', 'separator', 'lspace', 'rspace',\n 'accent', 'accentunder', 'linethickness', 'columnalign', 'rowspacing', 'columnspacing',\n 'scriptlevel', 'displaystyle', 'encoding', 'width', 'height', 'depth', 'largeop',\n 'movablelimits', 'symmetric', 'maxsize', 'minsize', 'form', 'notation', 'rowalign',\n 'columnlines', 'rowlines', 'frame', 'framespacing',\n])\n\nfunction allowedAttribute(name: string): boolean {\n return ALLOWED_ATTRS.has(name) && !name.startsWith('on')\n}\n\nfunction convert(node: Node, key: number): ReactNode {\n if (node.nodeType === 3 /* Node.TEXT_NODE */) return node.textContent ?? ''\n if (node.nodeType !== 1 /* Node.ELEMENT_NODE */) return null\n const element = node as Element\n const tag = element.tagName.toLowerCase()\n const children = [...element.childNodes].map((child, index) => convert(child, index))\n if (!ALLOWED_TAGS.has(tag)) return children\n const props: Record<string, string> = {}\n for (const attribute of [...element.attributes]) {\n if (allowedAttribute(attribute.name)) props[attribute.name] = attribute.value\n }\n return createElement(tag, { ...props, key }, ...children)\n}\n\n/** Render a host-produced MathML subtree, falling back to the caller's styled TeX. */\nexport function MathMLView({\n mathml,\n fallback,\n}: {\n mathml: string | undefined\n fallback: ReactNode\n}): ReactNode {\n if (mathml === undefined || mathml.length === 0) return fallback\n if (typeof DOMParser === 'undefined') return fallback\n try {\n const document = new DOMParser().parseFromString(mathml, 'text/html')\n const root = document.querySelector('math')\n if (root === null) return fallback\n return convert(root, 0)\n } catch {\n return fallback\n }\n}\n","/**\n * The block-type names the current build understands.\n *\n * Kept in a zod-free module so the browser half can import the runtime guard\n * without dragging the host's schema (and zod) into the single-file client\n * bundle. `lesson.ts` owns validation; this module owns the shared vocabulary.\n */\n\nimport type { KnownBlock, StoredBlock } from './lesson.js'\n\nexport const KNOWN_BLOCK_TYPE_NAMES = [\n 'text',\n 'example',\n 'diagram',\n 'check',\n 'formula',\n 'code',\n 'quiz',\n 'flashcard',\n 'resource',\n 'comparison',\n] as const\n\nconst KNOWN = new Set<string>(KNOWN_BLOCK_TYPE_NAMES)\n\n/** Narrow stored data to the renderer registry without importing the validation library. */\nexport function isKnownBlock(block: StoredBlock): block is KnownBlock {\n return KNOWN.has(block.type)\n}\n","/**\n * Interface copy for the lesson surface.\n *\n * Only the chrome is kept here: the blocks' labels, the course card's status\n * lines, the empty states and the learner's data controls. Lesson titles, the\n * teaching itself and anything the learner wrote are content, not interface,\n * and are never selected from this table.\n *\n * The English side is the copy this surface already shipped with, unchanged —\n * the table is a move, not an edit. The Chinese side is the same interface in\n * Simplified Chinese, using one vocabulary throughout: 课次 for a lesson and\n * 学习路线 for a route, with 证据 and 导师 matching the map's wording.\n */\n\nimport { useLang } from './i18n.js'\n\nconst en = {\n // ---- lesson blocks ---------------------------------------------------\n workedExample: 'Worked example',\n diagram: 'Diagram',\n formula: 'Formula',\n code: 'Code',\n quiz: 'Quiz',\n flashcard: 'Flashcard',\n resource: 'Resource',\n comparison: 'Comparison',\n sourceRef: 'Sources',\n sourcePreview: 'Source preview',\n flashcardFront: 'Front',\n flashcardBack: 'Back',\n quizAnswerInChat: 'Answer in the chat; the tutor will respond there.',\n submitAnswer: 'Record my answer',\n answerRecorded: 'Answer recorded — the tutor will respond in the chat.',\n showAnswer: 'Show the answer',\n flashcardAgain: 'Again',\n flashcardGotIt: 'Got it',\n answerFailed: 'Could not record this answer. Try again.',\n mermaidSourcePrefix: 'Source shown as text — this build ships no ',\n mermaidSourceSuffix: ' renderer.',\n yourTurn: 'Your turn',\n answerInChatHint: 'answer in the chat →',\n hint: 'Hint',\n unknownType: 'unknown',\n unknownBlockAfter: ' block — this build has no renderer for it. Update the plugin to see it.',\n\n // ---- the learner's data ----------------------------------------------\n deleteConfirm:\n 'Delete your goal, your map, every lesson and all the evidence behind it? This cannot be undone.',\n deleting: 'Deleting…',\n deleteYes: 'Yes, delete everything',\n cancel: 'Cancel',\n exportData: 'Export my data',\n deleteAll: 'Delete everything',\n dataNote: 'Stored on this machine only. Nothing is sent anywhere.',\n\n // ---- the course card -------------------------------------------------\n learningRoute: 'Your learning route',\n lesson: 'Lesson',\n lessonOf: 'of',\n routeVersion: 'Route v',\n lessonCompleted: 'Lesson completed · evidence recorded',\n lessonPaused: 'Lesson paused',\n currentLesson: 'This conversation’s current lesson',\n newerRoutePrefix: 'A newer route exists. This lesson remains on v',\n newerRouteSuffix: '.',\n openCourseRoute: 'Open course route',\n tutorOrigin: 'Tutor',\n preparingTitle: 'The tutor is preparing this lesson',\n routeReadyTitle: 'Your course route is ready',\n buildRouteTitle: 'Build a route around your goal',\n preparingBody:\n 'The teaching appears here as it is written. Answer the tutor’s questions in the chat.',\n routeReadyBody: 'Browse any lesson Overview in Learning map, then choose where to begin.',\n buildRouteBody:\n 'Set your target, available time and background in Learning map, then ask for an initial diagnosis and course outline.',\n promptPending: 'The teaching request has not reached a live tutor yet.',\n promptRetry: ' Open the course route to retry.',\n lessonOutcome: 'Lesson outcome',\n outcomeNote: 'Completing a lesson does not automatically confirm its knowledge points.',\n chooseNextLesson: 'Choose the next lesson',\n answerInChat: 'Answer in the chat',\n}\n\nconst zh: typeof en = {\n // ---- lesson blocks ---------------------------------------------------\n workedExample: '例题解析',\n diagram: '示意图',\n formula: '公式',\n code: '代码',\n quiz: '小测',\n flashcard: '闪卡',\n resource: '学习资料',\n comparison: '对比',\n sourceRef: '来源',\n sourcePreview: '来源预览',\n flashcardFront: '正面',\n flashcardBack: '背面',\n quizAnswerInChat: '在对话中回答,导师会在对话里回应。',\n submitAnswer: '记录我的回答',\n answerRecorded: '回答已记录 —— 导师会在对话里回应。',\n showAnswer: '显示答案',\n flashcardAgain: '再来一次',\n flashcardGotIt: '记住了',\n answerFailed: '记录失败,请重试。',\n mermaidSourcePrefix: '源码以文本形式显示 —— 本构建未附带 ',\n mermaidSourceSuffix: ' 渲染器。',\n yourTurn: '轮到你了',\n answerInChatHint: '在对话中回答 →',\n hint: '提示',\n unknownType: '未知',\n unknownBlockAfter: ' 区块 —— 本构建没有它的渲染器。请更新插件以查看。',\n\n // ---- the learner's data ----------------------------------------------\n deleteConfirm: '要删除你的目标、你的地图、所有课次以及背后的全部证据吗?此操作无法撤销。',\n deleting: '正在删除…',\n deleteYes: '是,全部删除',\n cancel: '取消',\n exportData: '导出我的数据',\n deleteAll: '全部删除',\n dataNote: '仅存储在本机。不会发送到任何地方。',\n\n // ---- the course card -------------------------------------------------\n learningRoute: '你的学习路线',\n lesson: '课次',\n lessonOf: '/',\n routeVersion: '学习路线 v',\n lessonCompleted: '课次已完成 · 证据已记录',\n lessonPaused: '课次已暂停',\n currentLesson: '本次会话的当前课次',\n newerRoutePrefix: '已有更新的学习路线。本课次仍保留在 v',\n newerRouteSuffix: '。',\n openCourseRoute: '打开学习路线',\n tutorOrigin: '导师',\n preparingTitle: '导师正在准备本课次',\n routeReadyTitle: '你的学习路线已就绪',\n buildRouteTitle: '围绕你的目标构建学习路线',\n preparingBody: '教学内容会在此处随写随显。请在对话中回答导师的问题。',\n routeReadyBody: '在学习地图中浏览任意课次概览,然后选择从哪里开始。',\n buildRouteBody: '在学习地图中设置你的目标、可用时间和背景,然后请求初始诊断和课程大纲。',\n promptPending: '教学请求尚未送达在线的导师。',\n promptRetry: '打开学习路线重试。',\n lessonOutcome: '课次成果',\n outcomeNote: '完成课次不会自动确认其知识点。',\n chooseNextLesson: '选择下一课次',\n answerInChat: '在对话中回答',\n}\n\n/**\n * The copy for the lesson surface in the learner's chosen language.\n *\n * Re-renders when the language changes, so the panel switches in place.\n *\n * @returns the active language's interface strings.\n */\nexport function useLessonText(): typeof en {\n return useLang() === 'zh' ? zh : en\n}\n","/**\n * Copy for the Learning map page, in both languages this build ships.\n *\n * The map is a reference surface, so its copy is kept in one place instead of\n * being spread through the component: the English strings here are the ones\n * the page has always rendered, and the Chinese strings say the same thing\n * rather than describing a different map.\n *\n * Counts are functions, not placeholders in a template, because the two\n * languages put the number in different places. A sentence assembled from a\n * fixed prefix and suffix would read as translated English in one of them.\n *\n * Data is deliberately absent: course and node titles, goals, notes, IDs and\n * timestamps are the learner's record and stay exactly as stored.\n */\n\nimport { type Lang, useLang } from './i18n.js'\n\nconst en = {\n pageTitle: 'Learning map',\n mapViews: 'Learning map views',\n noProject: 'no project open',\n openLesson: 'Open current lesson',\n backToChat: 'Back to the conversation',\n courseRoute: 'Course route',\n knowledgeEvidence: 'Knowledge & evidence',\n\n emptyMap: 'Nothing on the map yet. State a goal in the chat.',\n loadingMap: 'Loading your map…',\n nothingSelected: 'Nothing selected',\n pickNode:\n 'Pick a node on the left and this column shows what is recorded against it, and why it is in that state.',\n selectedNode: 'Selected node',\n whereThisSits: 'Where this sits',\n evidence: (n: number) => `Evidence ${n}`,\n nothingRecorded: 'Nothing recorded yet.',\n teachingNode: 'This is the node the tutor is teaching.',\n\n welcomeTitle: 'Nothing to map yet.',\n welcomeBody:\n 'State a goal in the chat and the tutor will grow the diagnosis map here, one diagnosed node at a time.',\n orOpenProject: 'Or open a project',\n confirmedOf: (n: number, m: number) => `${n} of ${m} confirmed`,\n goToChat: 'Go to the chat',\n\n evidenceCount: (n: number) => `${n} evidence`,\n parent: (title: string) => `parent: ${title}`,\n children: (n: number) => `${n} children`,\n\n diagnosisMap: 'Diagnosis map',\n sharedAcrossConversations: 'Shared across conversations',\n goalLabel: 'Goal:',\n thisConversation: 'This conversation',\n noFocus: 'No focus yet. Pick a node below to start learning here.',\n census: (n: number) => `${n} nodes`,\n confirmedByEvidence: (n: number) => `${n} confirmed by evidence`,\n mapGrowth:\n 'The map grows only as diagnosis reveals something. A node moves off its born state because evidence was recorded, never because it was listed.',\n}\n\nconst zh: typeof en = {\n pageTitle: '学习地图',\n mapViews: '学习地图视图',\n noProject: '未打开项目',\n openLesson: '打开当前课次',\n backToChat: '返回会话',\n courseRoute: '学习路线',\n knowledgeEvidence: '知识与证据',\n\n emptyMap: '地图上还没有内容。在对话中说明你的目标。',\n loadingMap: '正在加载你的学习地图…',\n nothingSelected: '未选择节点',\n pickNode: '在左侧选择一个节点,这里会显示与该节点相关的记录,以及它处于当前状态的原因。',\n selectedNode: '已选节点',\n whereThisSits: '所在位置',\n evidence: (n: number) => `证据 ${n}`,\n nothingRecorded: '还没有记录。',\n teachingNode: '这是导师当前正在教学的节点。',\n\n welcomeTitle: '还没有可绘制的内容。',\n welcomeBody:\n '在对话中说明目标,导师会在这里逐步展开诊断地图,每次加入一个经过诊断的节点。',\n orOpenProject: '或打开一个项目',\n confirmedOf: (n: number, m: number) => `已确认 ${n} / ${m}`,\n goToChat: '前往对话',\n\n evidenceCount: (n: number) => `${n} 条证据`,\n parent: (title: string) => `上级:${title}`,\n children: (n: number) => `${n} 个子节点`,\n\n diagnosisMap: '诊断地图',\n sharedAcrossConversations: '跨会话共享',\n goalLabel: '目标:',\n thisConversation: '本次会话',\n noFocus: '尚未确定本会话的学习节点。在下方选择一个节点,即可从这里开始学习。',\n census: (n: number) => `${n} 个节点`,\n confirmedByEvidence: (n: number) => `已由证据确认 ${n} 个`,\n mapGrowth:\n '地图只会随着诊断的发现而扩展。节点之所以会离开初始状态,是因为记录了证据,而不是因为被列在地图上。',\n}\n\n/** The map's copy in the current interface language. */\nexport function useMapText(): typeof en {\n return useLang() === 'zh' ? zh : en\n}\n\n/** The seven node states, as a word a learner can read. */\nexport function stateLabel(state: string, lang: Lang): string {\n if (lang === 'zh') {\n switch (state) {\n case 'unconfirmed':\n return '未确认'\n case 'explained':\n return '已讲解'\n case 'practiced':\n return '已练习'\n case 'checked':\n return '已检查'\n case 'weak':\n return '薄弱'\n case 'blocked':\n return '受阻'\n case 'confirmed':\n return '已确认'\n default:\n return state\n }\n }\n // English already displays the stored word, unchanged.\n return state\n}\n\n/** The kinds of observation that can be recorded against a node. */\nexport function evidenceKindLabel(kind: string, lang: Lang): string {\n if (lang === 'zh') {\n switch (kind) {\n case 'goal-stated':\n return '目标陈述'\n case 'diagnosis':\n return '诊断'\n case 'explanation':\n return '讲解'\n case 'practice':\n return '练习'\n case 'check':\n return '检查'\n case 'transfer':\n return '迁移'\n default:\n // An unrecognised kind is data from a newer runtime, not copy we own;\n // showing it unhyphenated is more honest than inventing a translation.\n return kind.replace(/-/g, ' ')\n }\n }\n switch (kind) {\n case 'goal-stated':\n return 'goal stated'\n case 'diagnosis':\n return 'diagnosis'\n case 'explanation':\n return 'explanation'\n case 'practice':\n return 'practice'\n case 'check':\n return 'check'\n case 'transfer':\n return 'transfer'\n default:\n return kind.replace(/-/g, ' ')\n }\n}\n\n/** The readiness a tutor recorded alongside an observation. */\nexport function readinessLabel(readiness: string, lang: Lang): string {\n if (lang === 'zh') {\n switch (readiness) {\n case 'advance':\n return '可以继续'\n case 'advance-with-caution':\n return '谨慎继续'\n case 'review-first':\n return '先复习'\n case 'step-down':\n return '回退一步'\n case 'diagnose-again':\n return '重新诊断'\n case 'more-practice':\n return '继续练习'\n default:\n return readiness.replace(/-/g, ' ')\n }\n }\n switch (readiness) {\n case 'advance':\n return 'advance'\n case 'advance-with-caution':\n return 'advance with caution'\n case 'review-first':\n return 'review first'\n case 'step-down':\n return 'step down'\n case 'diagnose-again':\n return 'diagnose again'\n case 'more-practice':\n return 'more practice'\n default:\n return readiness.replace(/-/g, ' ')\n }\n}\n","/**\n * Pure map model for the panel.\n *\n * Kept free of React and of `fetch` so the interesting logic — turning a flat\n * node list into a tree, ordering it, labelling states — is testable in a plain\n * node environment. The component below it is then mostly rendering.\n */\n\nimport type { HandoffView, LessonRecord, NodeView } from '../contract.js'\nimport type { Lang } from './i18n.js'\n\n/** One node plus its children, in render order. */\nexport interface MapTreeNode {\n node: NodeView\n children: MapTreeNode[]\n depth: number\n}\n\n/**\n * Build the map tree from a flat node list.\n *\n * Ordering is insertion order within each level, which is the order the nodes\n * were diagnosed in — the map reads as a history, not as a syllabus.\n *\n * Nodes whose parent is missing are promoted to roots rather than dropped: a\n * dangling reference is a data problem, and silently hiding the node would be\n * worse than showing it at the top level.\n *\n * @param nodes - the flat node views.\n * @returns the roots, each with its subtree.\n */\nexport function buildMapTree(nodes: readonly NodeView[]): MapTreeNode[] {\n const byId = new Map<string, NodeView>()\n for (const node of nodes) byId.set(node.id, node)\n\n const childrenOf = new Map<string | null, NodeView[]>()\n for (const node of nodes) {\n const parent = node.parentId !== null && byId.has(node.parentId) ? node.parentId : null\n const bucket = childrenOf.get(parent) ?? []\n bucket.push(node)\n childrenOf.set(parent, bucket)\n }\n\n const rendered = new Set<string>()\n\n const build = (parentId: string | null, depth: number, ancestors: ReadonlySet<string>): MapTreeNode[] =>\n (childrenOf.get(parentId) ?? [])\n // A cycle would otherwise recurse forever. The store forbids one, but the\n // panel must not hang if it ever meets one.\n .filter((node) => !ancestors.has(node.id))\n .map((node) => {\n rendered.add(node.id)\n const nextAncestors = new Set(ancestors).add(node.id)\n return { node, depth, children: build(node.id, depth + 1, nextAncestors) }\n })\n\n const roots = build(null, 0, new Set())\n\n // Anything unreachable from a root is rendered as a root of its own. Without\n // this, a node caught in a parent cycle is never visited and disappears from\n // the panel entirely — dropping a node silently is worse than showing it in\n // the wrong place.\n for (const node of nodes) {\n if (rendered.has(node.id)) continue\n rendered.add(node.id)\n roots.push({ node, depth: 0, children: build(node.id, 1, new Set([node.id])) })\n }\n\n return roots\n}\n\n/**\n * Flatten a tree back into render order, carrying depth.\n *\n * @param roots - the tree.\n * @returns every node in top-down, left-to-right order.\n */\nexport function flattenTree(roots: readonly MapTreeNode[]): MapTreeNode[] {\n const out: MapTreeNode[] = []\n const walk = (nodes: readonly MapTreeNode[]): void => {\n for (const entry of nodes) {\n out.push(entry)\n walk(entry.children)\n }\n }\n walk(roots)\n return out\n}\n\n/** Human label for a relation, in the map's own terms. */\nexport function relationLabel(relation: string, lang: Lang = 'en'): string {\n if (lang === 'zh') {\n switch (relation) {\n case 'goal':\n return '目标'\n case 'part-of':\n return '组成部分'\n case 'prerequisite':\n return '先修'\n case 'related':\n return '相关'\n default:\n return relation\n }\n }\n switch (relation) {\n case 'goal':\n return 'goal'\n case 'part-of':\n return 'part of'\n case 'prerequisite':\n return 'prerequisite'\n case 'related':\n return 'related'\n default:\n return relation\n }\n}\n\n/**\n * Only `confirmed` reads as a filled mark; every other state is an outline.\n *\n * There are deliberately no numeric or graded progressions here — the seven\n * words are the entire vocabulary.\n */\nexport function isFilledState(state: string): boolean {\n return state === 'confirmed'\n}\n\n/**\n * Short, non-learner-facing explanation of what a state means, for the detail pane.\n *\n * `unconfirmed` used to read \"No evidence yet — nothing has been observed for\n * this node\", which the panel then printed directly above a list headed\n * \"Evidence 3\". Both sentences were about the same node and only one of them\n * could be true. The state definition is the skill's — no evidence *that\n * establishes the concept* — while observations of other kinds (a diagnosis,\n * a stated goal) are recorded against a node all the time and simply do not\n * move it. So the explanation now describes the state rather than counting\n * what is on the page.\n */\nexport function stateExplanation(state: string, lang: Lang = 'en'): string {\n if (lang === 'zh') {\n switch (state) {\n case 'unconfirmed':\n return '尚未确立——这里记录的任何内容都还没有使它脱离这一状态。'\n case 'explained':\n return '已经讲解过,但仅凭这一点并不能证明已经理解。'\n case 'practiced':\n return '至少尝试过一次。'\n case 'checked':\n return '已经提出了检查题或近迁移问题。'\n case 'weak':\n return '部分掌握或不稳定——在继续之前值得修补。'\n case 'blocked':\n return '无法继续:这里缺少某些前提,或存在误解。'\n case 'confirmed':\n return '由检查或迁移观察支持。经过重新核验,而非想当然。'\n default:\n return '未知状态。'\n }\n }\n switch (state) {\n case 'unconfirmed':\n return 'Not established yet — nothing recorded here has moved it off this state.'\n case 'explained':\n return 'It has been explained, but that alone does not prove understanding.'\n case 'practiced':\n return 'Something was attempted at least once.'\n case 'checked':\n return 'A check or near-transfer question was asked.'\n case 'weak':\n return 'Partial or unstable — worth repairing before moving on.'\n case 'blocked':\n return 'Cannot proceed: something here is missing or misunderstood.'\n case 'confirmed':\n return 'Supported by a check or transfer observation. Re-checked, not assumed.'\n default:\n return 'Unknown state.'\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* The conversation's own state, as both surfaces must agree on it */\n/* -------------------------------------------------------------------------- */\n\n/** One control the conversation may offer. */\nexport interface ConversationControl {\n kind: 'start' | 'in-progress'\n label: string\n /**\n * The node the control acts on.\n *\n * The focus when there is one: the control sits under \"this conversation\", so\n * browsing the map must not silently retarget what pressing it starts.\n */\n nodeId: string\n}\n\n/**\n * Which control this conversation offers, given what is actually true.\n *\n * There are exactly three states, and they must not overlap. The bug this\n * function exists to end: with a recommendation pending, the surfaces showed\n * `Start learning` **and** `Continue learning` at the same time and told the\n * learner to press the former — while the recommendation was the move the tutor\n * had just recorded, and starting a node the learner had not been sent to would\n * quietly record a different focus.\n *\n * - **a next step is pending** — no start control at all. The recommendation\n * owns the next move, and its own button is the only thing to press.\n * - **nothing is focused** — `Start learning`. This is the only state that may\n * say it: before anything has started, that is the literal next action.\n * - **a node is focused, no next step** — `Learning in progress`, which names\n * the state instead of inviting a start that has already happened.\n *\n * @param input.focusNodeId - the node this conversation is learning, if any.\n * @param input.shownNodeId - the node on screen, used when nothing is focused.\n * @param input.hasNextStep - whether the tutor recorded a recommendation.\n * @returns the control to render, or `null` when none belongs on screen.\n */\nexport function conversationControl(input: {\n focusNodeId: string | null\n shownNodeId: string | null\n hasNextStep: boolean\n}, lang: Lang = 'en'): ConversationControl | null {\n if (input.hasNextStep) return null\n if (input.focusNodeId !== null) {\n return {\n kind: 'in-progress',\n label: lang === 'zh' ? '正在学习' : 'Learning in progress',\n nodeId: input.focusNodeId,\n }\n }\n if (input.shownNodeId === null) return null\n return {\n kind: 'start',\n label: lang === 'zh' ? '开始学习' : 'Start learning',\n nodeId: input.shownNodeId,\n }\n}\n\n/**\n * What the teaching region says while there is no lesson to show.\n *\n * A pending recommendation outranks a focus: the tutor has finished this turn\n * and recorded where to go next, so claiming a lesson is being written would\n * describe a turn that is not running, and `Press Start learning` would name a\n * control that is not on the page.\n */\nexport type LessonPlaceholder = 'preparing' | 'press-start' | 'press-continue'\n\nexport function lessonPlaceholder(input: {\n focusNodeId: string | null\n hasNextStep: boolean\n}): LessonPlaceholder {\n if (input.hasNextStep) return 'press-continue'\n if (input.focusNodeId !== null) return 'preparing'\n return 'press-start'\n}\n\n/**\n * The label on the recommendation's own button.\n *\n * Shared with the card that renders it, so a sentence pointing at the button\n * cannot name a different verb: a stay says `Continue`, a move says\n * `Continue learning`.\n */\nexport function continuationLabel(nextStep: { targetNodeId: string | null } | null, lang: Lang = 'en'): string {\n if (lang === 'zh') return nextStep !== null && nextStep.targetNodeId === null ? '继续' : '继续学习'\n return nextStep !== null && nextStep.targetNodeId === null ? 'Continue' : 'Continue learning'\n}\n\n/**\n * Whether the handoff line belongs on screen.\n *\n * `Lesson ready` is a claim about a lesson. It was printed by the docked tab\n * above that very lesson, and by both surfaces after the learner had moved on —\n * the copy disagreeing with the page it sat on. The line narrates a *wait*, so\n * it is visible only while the wait is real: something is focused and no lesson\n * has arrived yet.\n *\n * A failed or stalled wait keeps its line (and its retry) even when the focus\n * has moved: those phases are statements about the wait, never about where the\n * learner is — the same rule the handoff record itself follows.\n */\nexport function handoffLineVisible(input: {\n handoff: HandoffView | null\n lesson: LessonRecord | null\n focusNodeId: string | null\n}): boolean {\n if (input.handoff === null || input.lesson !== null || input.focusNodeId === null) return false\n if (input.handoff.phase === 'failed' || input.handoff.phase === 'stalled') return true\n return input.handoff.targetNodeId === input.focusNodeId\n}\n\n/**\n * When something was recorded, in words rather than in ISO 8601.\n *\n * The surfaces used to print `entry.at` verbatim, so the evidence list — the\n * part of the panel whose whole job is to make the record legible — was\n * punctuated by `2026-09-28T05:27:53.946Z`. Milliseconds and a `Z` are for\n * machines; a learner reading their own history wants \"3 days ago\", and wants\n * the exact moment only when it is close enough to matter.\n *\n * Pure and clock-injectable, so the tests are not a race against the calendar.\n *\n * @param at - the ISO timestamp from the record.\n * @param now - the reference instant; defaults to the current time.\n * @param lang - the interface language; the instant itself is never translated.\n * @returns a relative phrase for anything recent, otherwise a plain date.\n */\nexport function formatWhen(at: string, now: number = Date.now(), lang: Lang = 'en'): string {\n const then = Date.parse(at)\n if (Number.isNaN(then)) return at\n const seconds = Math.round((now - then) / 1000)\n // A clock skew between the host and the browser shows up as a negative age;\n // \"in 3 seconds\" on a recorded observation is worse than saying nothing.\n if (lang === 'zh') {\n if (seconds < 45) return '刚刚'\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return `${minutes} 分钟前`\n const hours = Math.round(minutes / 60)\n if (hours < 24) return `${hours} 小时前`\n const days = Math.round(hours / 24)\n if (days < 7) return days === 1 ? '昨天' : `${days} 天前`\n const date = new Date(then)\n const sameYear = new Date(now).getFullYear() === date.getFullYear()\n return date.toLocaleDateString('zh-CN', {\n day: 'numeric',\n month: 'short',\n ...(sameYear ? {} : { year: 'numeric' }),\n })\n }\n if (seconds < 45) return 'just now'\n const minutes = Math.round(seconds / 60)\n if (minutes < 60) return minutes === 1 ? '1 minute ago' : `${minutes} minutes ago`\n const hours = Math.round(minutes / 60)\n if (hours < 24) return hours === 1 ? '1 hour ago' : `${hours} hours ago`\n const days = Math.round(hours / 24)\n if (days < 7) return days === 1 ? 'yesterday' : `${days} days ago`\n const date = new Date(then)\n const sameYear = new Date(now).getFullYear() === date.getFullYear()\n return date.toLocaleDateString(undefined, {\n day: 'numeric',\n month: 'short',\n ...(sameYear ? {} : { year: 'numeric' }),\n })\n}\n\n/**\n * A wait, in units a person reads.\n *\n * The handoff line counts seconds, which is right while a model turn is in\n * flight and wrong the moment a record outlives its turn: a restored session\n * printed \"Lesson ready 119803s\", which is thirty-three hours rendered as a\n * number nobody converts. Past a minute, the unit changes.\n *\n * @param ms - elapsed milliseconds.\n * @returns a short duration string, or an empty string when there is nothing\n * meaningful to show yet.\n */\nexport function formatDuration(ms: number, lang: Lang = 'en'): string {\n const seconds = Math.round(ms / 1000)\n if (seconds < 1) return ''\n const s = lang === 'zh' ? '秒' : 's'\n const m = lang === 'zh' ? '分' : 'm'\n const h = lang === 'zh' ? '时' : 'h'\n if (seconds < 60) return `${seconds}${s}`\n const minutes = Math.floor(seconds / 60)\n if (minutes < 60) {\n const rest = seconds % 60\n return rest === 0 ? `${minutes}${m}` : `${minutes}${m} ${rest}${s}`\n }\n const hours = Math.floor(minutes / 60)\n const restMinutes = minutes % 60\n return restMinutes === 0 ? `${hours}${h}` : `${hours}${h} ${restMinutes}${m}`\n}\n\n/** The seven states, in the order the skill introduces them. */\nexport const STATE_ORDER = [\n 'unconfirmed',\n 'explained',\n 'practiced',\n 'checked',\n 'weak',\n 'blocked',\n 'confirmed',\n] as const\n","/**\n * Block renderers.\n *\n * The registry is the whole extension story: a block type is a key in\n * `BLOCK_RENDERERS`, so Formula, Code, Comparison, Practice or Resource arrive\n * as one more entry and never as a rewrite of the lesson renderer.\n *\n * Unknown types are the interesting case. A lesson authored by a newer host\n * will contain blocks this build has never heard of, and that must degrade —\n * the block is announced by type and left legible, rather than throwing inside\n * React and blanking the whole panel.\n *\n * Note what is absent: no percentage, no score, no stars, no progress bar.\n * State is always a word from the skill's vocabulary plus a coloured mark.\n */\n\nimport { useState, type ReactNode } from 'react'\n\nimport { MathMLView } from './mathml.jsx'\n\nimport type { Block, HandoffView, KnownBlock, NextStepView, StoredBlock } from '../contract.js'\nimport { isKnownBlock } from '../block-types.js'\nimport { useLang } from './i18n.js'\nimport { useLearningText } from './learning-text.js'\nimport { useLessonText } from './lesson-text.js'\nimport { readinessLabel } from './map-text.js'\nimport { continuationLabel, formatDuration } from './model.js'\n\n/** Props every renderer receives. */\nexport interface BlockInteraction {\n attemptId: string\n loadSource?(sourceId: string): Promise<{ id: string; title: string; locator: string; content?: string }>\n responses: readonly {\n blockId: string\n kind: string\n answers: readonly { questionId: string; value: string }[]\n submittedAt: string\n }[]\n submit(blockId: string, answers: { questionId: string; value: string }[]): Promise<void>\n}\n\nexport interface BlockRenderProps<B extends KnownBlock = KnownBlock> {\n block: B\n interaction?: BlockInteraction | undefined\n}\n\ntype Renderer = (props: BlockRenderProps<KnownBlock>) => ReactNode\n\n/**\n * Inline formatting: `**bold**`, `*italic*`, `` `code` ``, and inline math.\n *\n * Math is handled because the teaching brain writes it by convention —\n * `\\\\(...\\\\)` inline and `\\\\[...\\\\]` display are the skill's own rule — and a\n * STEM surface that prints the delimiters verbatim is unreadable.\n *\n * Emphasis is handled for the same reason, and it was the more visible miss:\n * a single asterisk is the most ordinary thing a writer types, so `*Machine\n * Learning*` arrived in the lesson with its asterisks still attached. Bold has\n * to be matched first — `**x**` also starts with a `*` — which the ordering of\n * the alternation below does for free.\n *\n * This is *styling*, not typesetting: the span is set apart and given a\n * monospace face so the expression is legible. Real math rendering needs a\n * typesetter (KaTeX or MathML), which is a later decision, not a silent gap.\n *\n * The inline pattern is non-greedy to the closing delimiter rather than\n * \"anything but a close parenthesis\", which is what it used to be and what\n * `\\\\((m,n)\\\\cdot(n,p)\\\\)` — a real expression out of a real lesson — broke:\n * the first `)` ended the candidate match, nothing matched, and the lesson\n * printed the delimiters and the expression as source.\n */\nconst INLINE_PATTERN = /(\\*\\*[^*]+\\*\\*|\\*[^*\\n]+\\*|`[^`]+`|\\\\\\([\\s\\S]*?\\\\\\))/g\n\nfunction inline(text: string, math?: readonly { tex: string; display: boolean; mathml: string }[]): ReactNode[] {\n return text\n .split(INLINE_PATTERN)\n .filter(Boolean)\n .map((part, index) => {\n if (part.startsWith('**') && part.endsWith('**')) {\n return <strong key={index}>{part.slice(2, -2)}</strong>\n }\n if (part.length > 2 && part.startsWith('*') && part.endsWith('*')) {\n return <em key={index}>{part.slice(1, -1)}</em>\n }\n if (part.startsWith('`') && part.endsWith('`')) return <code key={index}>{part.slice(1, -1)}</code>\n if (part.startsWith('\\\\(') && part.endsWith('\\\\)')) {\n const tex = part.slice(2, -2).trim()\n const fragment = math?.find((entry) => !entry.display && entry.tex === tex)\n return (\n <span className=\"dt-math\" key={index}>\n <MathMLView\n mathml={fragment?.mathml}\n fallback={<span className=\"dt-math-source\">{part.slice(2, -2)}</span>}\n />\n </span>\n )\n }\n return part\n })\n}\n\n/** One line of a text body, classified. */\ntype Line =\n | { kind: 'heading'; level: 2 | 3; text: string }\n | { kind: 'bullet'; text: string }\n | { kind: 'ordered'; marker: string; text: string }\n | { kind: 'math'; text: string }\n | { kind: 'text'; text: string }\n | { kind: 'table'; rows: string[][] }\n | { kind: 'code'; text: string; language: string }\n\n/** Split one pipe-table row into trimmed cells. */\nfunction tableCells(line: string): string[] {\n return line.replace(/^\\|/, '').replace(/\\|$/, '').split('|').map((cell) => cell.trim())\n}\n\n/** The `| --- | :---: |` separator that turns pipe rows into a table. */\nfunction tableSeparator(cells: string[]): boolean {\n return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell))\n}\n\n/**\n * Classify one line.\n *\n * The teaching brain writes ordinary light markdown — `###` headings, `-`\n * bullets, `1.` steps — because that is how a person writes an explanation.\n * Printing the markers verbatim is what makes a lesson look like a Markdown\n * renderer instead of a lesson, so they are recognised here.\n *\n * This adds **no block type**: it is how the existing text and check blocks are\n * displayed, which is the part of the surface the learner actually reads.\n */\nfunction classify(line: string): Line {\n const heading = /^(#{2,3})\\s+(.*)$/.exec(line)\n if (heading) return { kind: 'heading', level: heading[1] === '##' ? 2 : 3, text: heading[2]! }\n // Display math is written with escaped delimiters: \\[...\\]\n const trimmed = line.trim()\n if (trimmed.startsWith('\\\\[') && trimmed.endsWith('\\\\]')) {\n return { kind: 'math', text: trimmed.slice(2, -2).trim() }\n }\n const bullet = /^\\s*[-*•]\\s+(.*)$/.exec(line)\n if (bullet) return { kind: 'bullet', text: bullet[1]! }\n const ordered = /^\\s*(\\d+)[.)]\\s+(.*)$/.exec(line)\n if (ordered) return { kind: 'ordered', marker: ordered[1]!, text: ordered[2]! }\n return { kind: 'text', text: line }\n}\n\n/**\n * Group consecutive lines into runs that render as one element.\n *\n * Display math gets a pre-pass because it is the one construct that is not\n * line-shaped. `\\\\[ ... \\\\]` opens and closes on its own lines, and a matrix or\n * a derivation routinely puts several lines between them — a real lesson\n * contains\n *\n * \\\\[X=\\\\begin{bmatrix}\n * 1 & 2 \\\\\\\\\n * 3 & 4\n * \\\\end{bmatrix}\\\\]\n *\n * and a line-at-a-time classifier sees four ordinary lines: the delimiters, the\n * `&` alignment and the `\\\\\\\\` row breaks all print verbatim, and the matrix a\n * learner most needs to read is the one that arrives as source.\n *\n * An unterminated `\\\\[` is deliberately *not* consumed. Printing one stray\n * delimiter is a small blemish; swallowing the rest of a lesson into a code\n * panel is a lost lesson.\n */\nfunction toRuns(md: string): Line[][] {\n const runs: Line[][] = []\n let current: Line[] = []\n let currentKind: Line['kind'] | null = null\n const flush = (): void => {\n if (current.length > 0) runs.push(current)\n current = []\n currentKind = null\n }\n const lines = md.split(/\\n/)\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index]!.trimEnd()\n if (line.trim().length === 0) {\n flush()\n continue\n }\n\n const trimmed = line.trim()\n // Fenced code is line-shaped and must be consumed before ordinary prose.\n if (trimmed.startsWith('```')) {\n const language = trimmed.slice(3).trim()\n const body: string[] = []\n let cursor = index + 1\n let closed = false\n while (cursor < lines.length) {\n const next = lines[cursor]!\n if (next.trim().startsWith('```')) { cursor += 1; closed = true; break }\n body.push(next)\n cursor += 1\n }\n if (closed) {\n flush()\n runs.push([{ kind: 'code', text: body.join('\\n'), language }])\n index = cursor - 1\n continue\n }\n }\n // A pipe table is a header row, a separator row, and data rows. Treating\n // it as prose is the difference between a readable table and a wall of `|`.\n if (trimmed.startsWith('|') && trimmed.includes('|')) {\n const raw: string[] = []\n let cursor = index\n while (cursor < lines.length && lines[cursor]!.trim().startsWith('|') && lines[cursor]!.trim().includes('|')) {\n raw.push(lines[cursor]!.trim())\n cursor += 1\n }\n const rows = raw.map(tableCells)\n flush()\n if (rows.length >= 2 && tableSeparator(rows[1]!)) {\n runs.push([{ kind: 'table', rows: [rows[0]!, ...rows.slice(2)] }])\n } else {\n for (const row of raw) runs.push([{ kind: 'text', text: row }])\n }\n index = cursor - 1\n continue\n }\n if (trimmed.startsWith('\\\\[') && !trimmed.includes('\\\\]')) {\n const span = [trimmed]\n let cursor = index + 1\n let closed = false\n while (cursor < lines.length) {\n const next = lines[cursor]!.trim()\n span.push(next)\n cursor += 1\n if (next.includes('\\\\]')) {\n closed = true\n break\n }\n }\n if (closed) {\n const joined = span.join('\\n')\n const body = joined.slice(2, joined.indexOf('\\\\]'))\n flush()\n runs.push([{ kind: 'math', text: body.trim() }])\n // Anything after the closing delimiter is prose again, not math.\n const rest = joined.slice(joined.indexOf('\\\\]') + 2).trim()\n index = cursor - 1\n if (rest.length > 0) {\n runs.push([{ kind: 'text', text: rest }])\n }\n continue\n }\n }\n\n const parsed = classify(line)\n // Three things join the run in progress: a wrapped prose line, and a bullet\n // or ordered step continuing a list of the same kind. Everything else —\n // headings, display math, a change of list kind — starts a new run, so a\n // heading never swallows the prose under it.\n const continues =\n parsed.kind === 'text'\n ? currentKind === 'text'\n : (parsed.kind === 'bullet' || parsed.kind === 'ordered') && currentKind === parsed.kind\n if (!continues) flush()\n current.push(parsed)\n currentKind = parsed.kind\n }\n flush()\n return runs\n}\n\n/**\n * Render a text body as a lesson rather than as source.\n *\n * Blank-line separated runs become paragraphs, `###` becomes a real heading,\n * `-` and `1.` become real lists, and `\\[...\\]` becomes a display-math panel.\n */\nfunction Paragraphs({ md, math }: { md: string; math?: readonly { tex: string; display: boolean; mathml: string }[] | undefined }): ReactNode {\n return (\n <>\n {toRuns(md).map((run, index) => {\n const first = run[0]!\n if (first.kind === 'heading') {\n const Tag = first.level === 2 ? 'h3' : 'h4'\n return (\n <Tag className={`dt-md-h${first.level}`} key={index}>\n {inline(first.text, math)}\n </Tag>\n )\n }\n if (first.kind === 'math') {\n const fragment = math?.find((entry) => entry.display && entry.tex === first.text)\n return (\n <div className=\"dt-math-block\" key={index}>\n <MathMLView mathml={fragment?.mathml} fallback={<span className=\"dt-math-source\">{first.text}</span>} />\n </div>\n )\n }\n if (first.kind === 'code') {\n return (\n <pre className=\"dt-md-code\" key={index}>\n <code>{first.text}</code>\n </pre>\n )\n }\n if (first.kind === 'table') {\n const [head, ...body] = first.rows\n return (\n <div className=\"dt-md-table-wrap\" key={index}>\n <table className=\"dt-md-table\">\n <thead><tr>{(head ?? []).map((cell, column) => <th key={column}>{inline(cell, math)}</th>)}</tr></thead>\n <tbody>\n {body.map((row, rowIndex) => <tr key={rowIndex}>{row.map((cell, column) => <td key={column}>{inline(cell, math)}</td>)}</tr>)}\n </tbody>\n </table>\n </div>\n )\n }\n if (first.kind === 'bullet') {\n return (\n <ul className=\"dt-md-ul\" key={index}>\n {run.map((line, inner) => (\n <li key={inner}>{inline('text' in line ? line.text : '', math)}</li>\n ))}\n </ul>\n )\n }\n if (first.kind === 'ordered') {\n return (\n <ol className=\"dt-md-ol\" key={index}>\n {run.map((line, inner) => (\n <li key={inner}>{inline('text' in line ? line.text : '', math)}</li>\n ))}\n </ol>\n )\n }\n return (\n <p className=\"dt-block-md\" key={index}>\n {inline(run.map((line) => ('text' in line ? line.text : '')).join(' '), math)}\n </p>\n )\n })}\n </>\n )\n}\n\nfunction TextBlockView({ block }: BlockRenderProps): ReactNode {\n if (block.type !== 'text') return null\n return (\n <div className=\"dt-block dt-block-text\">\n <Paragraphs md={block.content.md} math={block.content.math ?? undefined} />\n </div>\n )\n}\n\n/** A label with a small mark, so block kinds are recognisable before reading. */\nfunction BlockLabel({ icon, children }: { icon: ReactNode; children: ReactNode }): ReactNode {\n return (\n <p className=\"dt-block-label\">\n <span className=\"dt-block-label-icon\" aria-hidden=\"true\">\n {icon}\n </span>\n {children}\n </p>\n )\n}\n\nfunction ExampleBlockView({ block }: BlockRenderProps): ReactNode {\n const text = useLessonText()\n if (block.type !== 'example') return null\n return (\n <div className=\"dt-block dt-block-example\">\n <BlockLabel\n icon={\n <svg viewBox=\"0 0 16 16\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.4\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M6 12.5h4M6.4 14h3.2M8 2a4 4 0 0 1 2.4 7.2c-.4.3-.4.6-.4 1H6c0-.4 0-.7-.4-1A4 4 0 0 1 8 2z\" />\n </svg>\n }\n >\n {text.workedExample}\n </BlockLabel>\n <h4>{block.content.title}</h4>\n <ol>\n {block.content.steps.map((step, index) => (\n <li key={index}>{inline(step)}</li>\n ))}\n </ol>\n {block.content.takeaway !== undefined && (\n <p className=\"dt-takeaway\">{inline(block.content.takeaway)}</p>\n )}\n </div>\n )\n}\n\nfunction DiagramBlockView({ block }: BlockRenderProps): ReactNode {\n const text = useLessonText()\n if (block.type !== 'diagram') return null\n const isMermaid = block.content.format === 'mermaid'\n return (\n <div className=\"dt-block dt-block-diagram\">\n <BlockLabel\n icon={\n <svg viewBox=\"0 0 16 16\" width=\"12\" height=\"12\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.4\" strokeLinecap=\"round\">\n <rect x=\"2\" y=\"2.5\" width=\"4.5\" height=\"3.5\" rx=\"1\" />\n <rect x=\"9.5\" y=\"10\" width=\"4.5\" height=\"3.5\" rx=\"1\" />\n <path d=\"M6.5 4.2h3a2 2 0 0 1 2 2v3.8\" />\n </svg>\n }\n >\n {text.diagram}\n </BlockLabel>\n {isMermaid && (\n <p className=\"dt-caption\">\n {text.mermaidSourcePrefix}\n <code>mermaid</code>\n {text.mermaidSourceSuffix}\n </p>\n )}\n <pre className=\"dt-pre\">{block.content.spec}</pre>\n {block.content.caption !== undefined && <p className=\"dt-caption\">{block.content.caption}</p>}\n </div>\n )\n}\n\n/**\n * The check.\n *\n * Rendered as an invitation rather than a field: this surface has no input box,\n * so the block has to make it obvious that the next move is the learner's and\n * that it happens in the chat. The wording says so instead of leaving a dead\n * question sitting in a panel.\n */\nfunction CheckBlockView({ block }: BlockRenderProps): ReactNode {\n const text = useLessonText()\n if (block.type !== 'check') return null\n return (\n <div className=\"dt-block dt-block-check\">\n <div className=\"dt-check-head\">\n <span className=\"dt-check-tag\">{text.yourTurn}</span>\n <span className=\"dt-check-where\">{text.answerInChatHint}</span>\n </div>\n <div className=\"dt-check-body\">\n <Paragraphs md={block.content.prompt} />\n </div>\n {block.content.hint !== undefined && (\n <p className=\"dt-check-hint\">\n <span>{text.hint}</span>\n {inline(block.content.hint)}\n </p>\n )}\n </div>\n )\n}\n\nfunction FormulaBlockView({ block }: BlockRenderProps): ReactNode {\n const text = useLessonText()\n if (block.type !== 'formula') return null\n return (\n <div className=\"dt-block dt-block-formula\">\n <BlockLabel icon={<span aria-hidden=\"true\">∑</span>}>{text.formula}</BlockLabel>\n <div className=\"dt-formula-body\">\n <MathMLView\n mathml={block.content.mathml}\n fallback={<div className=\"dt-math-block\"><span className=\"dt-math-source\">{block.content.tex}</span></div>}\n />\n </div>\n {block.content.caption !== undefined && <p className=\"dt-caption\">{block.content.caption}</p>}\n </div>\n )\n}\n\nfunction CodeBlockView({ block }: BlockRenderProps): ReactNode {\n const text = useLessonText()\n if (block.type !== 'code') return null\n return (\n <div className=\"dt-block dt-block-code\">\n <BlockLabel icon={<span aria-hidden=\"true\">⌘</span>}>\n {text.code}{block.content.language === undefined ? '' : ` · ${block.content.language}`}\n </BlockLabel>\n <pre className=\"dt-pre\"><code>{block.content.source}</code></pre>\n {block.content.caption !== undefined && <p className=\"dt-caption\">{block.content.caption}</p>}\n </div>\n )\n}\n\nfunction QuizBlockView({ block, interaction }: BlockRenderProps): ReactNode {\n const text = useLessonText()\n const [values, setValues] = useState<Record<string, string>>({})\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState('')\n const [sent, setSent] = useState(false)\n if (block.type !== 'quiz') return null\n const recorded = interaction?.responses.find((response) => response.blockId === block.id)\n const submitted = sent || recorded !== undefined\n const select = (questionId: string, value: string) => setValues((current) => ({ ...current, [questionId]: value }))\n const complete = block.content.questions.every((question) => {\n const value = recorded?.answers.find((answer) => answer.questionId === question.id)?.value ?? values[question.id]\n return typeof value === 'string' && value.length > 0\n })\n const submit = async () => {\n if (interaction === undefined || busy || !complete) return\n setBusy(true); setError('')\n try {\n await interaction.submit(block.id, block.content.questions.map((question) => ({\n questionId: question.id,\n value: recorded?.answers.find((answer) => answer.questionId === question.id)?.value ?? values[question.id] ?? '',\n })))\n setSent(true)\n } catch {\n setError(text.answerFailed)\n } finally { setBusy(false) }\n }\n return (\n <div className=\"dt-block dt-block-quiz\">\n <BlockLabel icon={<span aria-hidden=\"true\">?</span>}>{text.quiz}{block.content.title === undefined ? '' : ` · ${block.content.title}`}</BlockLabel>\n {block.content.questions.map((question, index) => {\n const chosen = recorded?.answers.find((answer) => answer.questionId === question.id)?.value ?? values[question.id] ?? ''\n return (\n <div className=\"dt-quiz-question\" key={question.id}>\n <p className=\"dt-quiz-prompt\"><b>{index + 1}.</b> {inline(question.prompt)}</p>\n {question.options !== undefined\n ? <div className=\"dt-quiz-options\" role=\"radiogroup\" aria-label={question.prompt}>\n {question.options.map((option) => (\n <label className=\"dt-quiz-option\" key={option}>\n <input type=\"radio\" name={`${block.id}-${question.id}`} value={option} checked={chosen === option} disabled={submitted || interaction === undefined} onChange={() => select(question.id, option)} />\n <span>{inline(option)}</span>\n </label>\n ))}\n </div>\n : <input className=\"dt-quiz-input\" aria-label={question.prompt} value={chosen} disabled={submitted || interaction === undefined} onChange={(event) => select(question.id, event.target.value)} />}\n </div>\n )\n })}\n {interaction === undefined\n ? <p className=\"dt-caption\">{text.quizAnswerInChat}</p>\n : submitted\n ? <p className=\"dt-quiz-recorded\" role=\"status\">{text.answerRecorded}</p>\n : <button type=\"button\" className=\"dt-secondary\" disabled={busy || !complete} onClick={() => void submit()}>{text.submitAnswer}</button>}\n {error !== '' && <p className=\"dt-caption\" role=\"alert\">{error}</p>}\n </div>\n )\n}\n\nfunction FlashcardBlockView({ block, interaction }: BlockRenderProps): ReactNode {\n const text = useLessonText()\n const [revealed, setRevealed] = useState(false)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState('')\n if (block.type !== 'flashcard') return null\n const recorded = interaction?.responses.find((response) => response.blockId === block.id)\n const submit = async (value: 'again' | 'got-it') => {\n if (interaction === undefined || busy) return\n setBusy(true); setError('')\n try {\n await interaction.submit(block.id, [{ questionId: block.id, value }])\n setRevealed(true)\n } catch {\n setError(text.answerFailed)\n } finally { setBusy(false) }\n }\n return (\n <div className=\"dt-block dt-block-flashcard\">\n <BlockLabel icon={<span aria-hidden=\"true\">▣</span>}>{text.flashcard}</BlockLabel>\n <div className=\"dt-flashcard\">\n <p className=\"dt-flashcard-front\">{inline(block.content.front)}</p>\n {interaction === undefined\n ? <details><summary>{text.showAnswer}</summary><p className=\"dt-flashcard-back\">{inline(block.content.back)}</p></details>\n : (revealed || recorded !== undefined)\n ? <p className=\"dt-flashcard-back\">{inline(block.content.back)}</p>\n : <button type=\"button\" className=\"dt-secondary\" onClick={() => setRevealed(true)}>{text.showAnswer}</button>}\n {interaction !== undefined && (revealed || recorded !== undefined) && recorded === undefined && (\n <div className=\"dt-flashcard-actions\">\n <button type=\"button\" className=\"dt-secondary\" disabled={busy} onClick={() => void submit('again')}>{text.flashcardAgain}</button>\n <button type=\"button\" className=\"dt-secondary\" disabled={busy} onClick={() => void submit('got-it')}>{text.flashcardGotIt}</button>\n </div>\n )}\n {recorded !== undefined && <p className=\"dt-quiz-recorded\" role=\"status\">{text.answerRecorded}</p>}\n </div>\n {block.content.tags !== undefined && <p className=\"dt-caption\">{block.content.tags.join(' · ')}</p>}\n {error !== '' && <p className=\"dt-caption\" role=\"alert\">{error}</p>}\n </div>\n )\n}\n\nfunction ResourceBlockView({ block }: BlockRenderProps): ReactNode {\n const text = useLessonText()\n if (block.type !== 'resource') return null\n return (\n <div className=\"dt-block dt-block-resource\">\n <BlockLabel icon={<span aria-hidden=\"true\">↗</span>}>{text.resource}</BlockLabel>\n <p className=\"dt-resource-title\">{inline(block.content.title)}</p>\n {(block.content.locator !== undefined || block.content.sourceId !== undefined) && (\n <p className=\"dt-caption\">{text.sourceRef}{block.content.locator === undefined ? '' : ` · ${block.content.locator}`}</p>\n )}\n {block.content.url !== undefined && <p className=\"dt-caption\">{block.content.url}</p>}\n {block.content.note !== undefined && <p className=\"dt-caption\">{inline(block.content.note)}</p>}\n </div>\n )\n}\n\nfunction ComparisonBlockView({ block }: BlockRenderProps): ReactNode {\n const text = useLessonText()\n if (block.type !== 'comparison') return null\n return (\n <div className=\"dt-block dt-block-comparison\">\n <BlockLabel icon={<span aria-hidden=\"true\">⇄</span>}>{text.comparison}</BlockLabel>\n <div className=\"dt-md-table-wrap\">\n <table className=\"dt-md-table\">\n <thead><tr>{block.content.columns.map((column) => <th key={column}>{inline(column)}</th>)}</tr></thead>\n <tbody>\n {block.content.rows.map((row, rowIndex) => (\n <tr key={rowIndex}>{row.map((cell, column) => <td key={column}>{inline(cell)}</td>)}</tr>\n ))}\n </tbody>\n </table>\n </div>\n {block.content.caption !== undefined && <p className=\"dt-caption\">{block.content.caption}</p>}\n </div>\n )\n}\n\n/**\n * The registry. Adding a block type means adding one entry here.\n *\n * Deliberately a plain object rather than a switch: a `Map`/record makes the\n * extension point visible and keeps the fallback impossible to forget.\n */\nexport const BLOCK_RENDERERS: Record<string, Renderer> = {\n text: TextBlockView,\n example: ExampleBlockView,\n diagram: DiagramBlockView,\n check: CheckBlockView,\n formula: FormulaBlockView,\n code: CodeBlockView,\n quiz: QuizBlockView,\n flashcard: FlashcardBlockView,\n resource: ResourceBlockView,\n comparison: ComparisonBlockView,\n}\n\n/** Shown for a block type this build does not know. Never throws. */\nfunction UnknownBlockView({ block }: { block: StoredBlock }): ReactNode {\n const text = useLessonText()\n const type = (block as { type?: unknown }).type\n return (\n <div className=\"dt-block dt-block-unknown\">\n <strong>{typeof type === 'string' ? type : text.unknownType}</strong>\n {text.unknownBlockAfter}\n </div>\n )\n}\n\n/**\n * Render one block, degrading for anything unrecognised.\n *\n * @param props.block - the block to render.\n * @returns the rendered block.\n */\nfunction SourceRefs({ block, interaction }: { block: StoredBlock; interaction?: BlockInteraction | undefined }): ReactNode {\n const text = useLessonText()\n const [open, setOpen] = useState<string | null>(null)\n const [preview, setPreview] = useState<{ title: string; locator: string; content: string } | null>(null)\n const [error, setError] = useState('')\n const refs = block.sourceRefs ?? []\n if (refs.length === 0) return null\n const load = async (sourceId: string) => {\n if (open === sourceId) { setOpen(null); setPreview(null); return }\n setOpen(sourceId); setPreview(null); setError('')\n if (interaction?.loadSource === undefined) return\n try {\n const source = await interaction.loadSource(sourceId)\n setPreview({ title: source.title, locator: source.locator, content: source.content ?? '' })\n } catch { setError(text.answerFailed) }\n }\n return (\n <div className=\"dt-source-refs\">\n <p className=\"dt-block-label\">{text.sourceRef}</p>\n {refs.map((ref) => (\n <button type=\"button\" className=\"dt-source-ref\" key={`${ref.sourceId}:${ref.locator}`} onClick={() => void load(ref.sourceId)}>\n {ref.locator}\n </button>\n ))}\n {open !== null && preview !== null && (\n <div className=\"dt-source-preview\">\n <p className=\"dt-source-preview-title\">{preview.title}</p>\n <p className=\"dt-caption\">{preview.locator}</p>\n <pre>{preview.content.slice(0, 4000)}</pre>\n </div>\n )}\n {error !== '' && <p className=\"dt-caption\" role=\"alert\">{error}</p>}\n </div>\n )\n}\n\nexport function BlockView({ block, interaction }: { block: StoredBlock; interaction?: BlockInteraction | undefined }): ReactNode {\n if (!isKnownBlock(block)) {\n return <div data-block-type={block.type}><UnknownBlockView block={block} /><SourceRefs block={block} interaction={interaction} /></div>\n }\n const renderer = BLOCK_RENDERERS[block.type]\n if (renderer === undefined) {\n return <div data-block-type={block.type}><UnknownBlockView block={block} /><SourceRefs block={block} interaction={interaction} /></div>\n }\n return <div data-block-type={block.type}>{renderer({ block, interaction })}<SourceRefs block={block} interaction={interaction} /></div>\n}\n\n/**\n * Render a whole lesson's blocks in order.\n *\n * @param props.blocks - the blocks to render.\n * @returns the rendered list.\n */\nexport function LessonBody({ blocks, interaction }: { blocks: readonly StoredBlock[]; interaction?: BlockInteraction | undefined }): ReactNode {\n return (\n <>\n {blocks.map((block) => (\n <BlockView key={block.id} block={block} interaction={interaction} />\n ))}\n </>\n )\n}\n\n/**\n * The recommendation card.\n *\n * The learner reads *why* before they move, and nothing moves until they press\n * the button — the tutor decides, the runtime stores, the learner chooses. That\n * ordering is the whole point of the card existing rather than an automatic\n * jump.\n *\n * Wording follows the decision: a move names where it goes, a stay says so\n * plainly. No percentage, no score, no completion estimate — how far along the\n * learner is lives in the node's state and evidence, and nowhere else.\n */\nexport function NextStepCard({\n nextStep,\n onContinue,\n busy,\n}: {\n nextStep: NextStepView\n onContinue: () => void\n busy?: boolean\n}): ReactNode {\n const text = useLearningText()\n const lang = useLang()\n const moves = nextStep.targetNodeId !== null\n return (\n <div className=\"dt-next\">\n <p className=\"dt-next-label\">\n <span className=\"dt-next-arrow\" aria-hidden=\"true\">\n ↓\n </span>\n {text.nextBestStep}\n </p>\n <p className=\"dt-next-from\">\n <span className=\"dt-next-tick\" aria-hidden=\"true\">\n ✓\n </span>\n {nextStep.fromNodeTitle} — {readinessLabel(nextStep.action, lang)}\n </p>\n <p className=\"dt-next-target\">\n {moves ? (\n <>\n {text.nextPrefix} <b>{nextStep.targetNodeTitle}</b>\n </>\n ) : (\n <>\n {text.nextPrefix} <b>{text.stayOn(nextStep.fromNodeTitle)}</b>\n </>\n )}\n </p>\n <p className=\"dt-next-why\">\n <span className=\"dt-next-why-label\">{text.why}</span> {nextStep.reason}\n </p>\n <button type=\"button\" className=\"dt-primary\" onClick={onContinue} disabled={busy === true}>\n {/* The label comes from the shared rule, so a sentence elsewhere that\n points at this button cannot name a different verb. */}\n {busy === true ? text.starting : continuationLabel(nextStep, lang)}\n </button>\n </div>\n )\n}\n\n/**\n * The handoff progress line.\n *\n * A model turn is not instant, and a silent wait is indistinguishable from a\n * broken button — so the wait is narrated: which stage it is in, how long it\n * has been, and how many attempts. When it goes quiet the learner gets a retry\n * rather than a dead end, and retrying **never touches the focus**: the record\n * is a statement about the wait, never about where they are.\n *\n * The elapsed time is shown because it is honest. A tutor that takes ninety\n * seconds should look like a tutor that takes ninety seconds, not like a hang —\n * and a record restored from three days ago should not look like one either,\n * which is why the unit changes once the wait is no longer a wait being\n * watched.\n */\nexport function HandoffLine({\n handoff,\n onRetry,\n}: {\n handoff: HandoffView\n onRetry: () => void\n}): ReactNode {\n const text = useLearningText()\n const lang = useLang()\n const retryable = handoff.phase === 'failed' || handoff.phase === 'stalled'\n const elapsed = formatDuration(handoff.elapsedMs, lang)\n return (\n <div className=\"dt-handoff\" data-phase={handoff.phase}>\n <span className=\"dt-handoff-dot\" aria-hidden=\"true\" />\n {/* The phase is the stable part; the host's label is English. */}\n <span className=\"dt-handoff-label\">{text.handoffLabel(handoff.phase)}</span>\n {elapsed !== '' && <span className=\"dt-handoff-time\">{elapsed}</span>}\n {handoff.attempts > 1 && <span className=\"dt-handoff-try\">{text.handoffAttempts(handoff.attempts)}</span>}\n {handoff.detail !== undefined && <span className=\"dt-handoff-detail\">{handoff.detail}</span>}\n {retryable && (\n <button type=\"button\" className=\"dt-handoff-retry\" onClick={onRetry}>\n {text.askAgain}\n </button>\n )}\n </div>\n )\n}\n\n/**\n * The learner's controls over their own data.\n *\n * Both promises this project makes about state are kept here: that it is\n * **visible and exportable**, and that it can be **deleted**. An export that\n * only existed as an API route would not be a promise kept, and a delete that\n * happened on one click would be a trap — so the second click is the\n * confirmation, inline, with no dialog to dismiss by reflex.\n */\nexport function DataFooter({\n onExport,\n onReset,\n busy,\n}: {\n onExport: () => void\n onReset: () => void\n busy?: boolean\n}): ReactNode {\n const text = useLessonText()\n const [confirming, setConfirming] = useState(false)\n\n if (confirming) {\n return (\n <div className=\"dt-data dt-data-confirm\">\n <p className=\"dt-data-ask\">{text.deleteConfirm}</p>\n <div className=\"dt-data-row\">\n <button\n type=\"button\"\n className=\"dt-danger\"\n onClick={() => {\n setConfirming(false)\n onReset()\n }}\n disabled={busy === true}\n >\n {busy === true ? text.deleting : text.deleteYes}\n </button>\n <button type=\"button\" className=\"dt-quiet\" onClick={() => setConfirming(false)}>\n {text.cancel}\n </button>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"dt-data\">\n <button type=\"button\" className=\"dt-quiet\" onClick={onExport} disabled={busy === true}>\n {text.exportData}\n </button>\n <button type=\"button\" className=\"dt-quiet\" onClick={() => setConfirming(true)} disabled={busy === true}>\n {text.deleteAll}\n </button>\n <span className=\"dt-data-note\">{text.dataNote}</span>\n </div>\n )\n}\n","/** Minimal stroke icons for the design-system surfaces. */\nimport type { ReactNode } from 'react'\n\nexport type IconName =\n | 'map'\n | 'chat'\n | 'route'\n | 'book'\n | 'arrow-left'\n | 'arrow-right'\n | 'chevron-right'\n | 'check'\n | 'clock'\n | 'search'\n | 'grid'\n | 'info'\n | 'warning'\n | 'close'\n | 'target'\n | 'spark'\n | 'layers'\n | 'code'\n | 'planet'\n | 'graph'\n | 'file'\n\nexport function Icon({ name, size = 20, className }: { name: IconName; size?: number; className?: string }): ReactNode {\n const common = {\n width: size, height: size, viewBox: '0 0 24 24', fill: 'none',\n stroke: 'currentColor', strokeWidth: 1.7, strokeLinecap: 'round' as const, strokeLinejoin: 'round' as const,\n className, 'aria-hidden': true as const, focusable: 'false' as const,\n }\n switch (name) {\n case 'map': return <svg {...common}><path d=\"M9 4 3 6.5v13L9 17l6 2.5 6-2.5v-13L15 6.5 9 4Z\" /><path d=\"M9 4v13M15 6.5v13\" /></svg>\n case 'chat': return <svg {...common}><path d=\"M21 11.5a8.4 8.4 0 0 1-8.5 8.3 9 9 0 0 1-3.8-.8L3 21l1.9-4.5A8 8 0 0 1 4 11.5 8.4 8.4 0 0 1 12.5 3 8.4 8.4 0 0 1 21 11.5Z\" /></svg>\n case 'route': return <svg {...common}><circle cx=\"6\" cy=\"18\" r=\"2.4\" /><circle cx=\"18\" cy=\"6\" r=\"2.4\" /><path d=\"M8.4 18h4.1a3 3 0 0 0 0-6H8a3 3 0 0 1 0-6h7.6\" /></svg>\n case 'book': return <svg {...common}><path d=\"M4 5.5A2.5 2.5 0 0 1 6.5 3H18v15H6.5A2.5 2.5 0 0 0 4 20.5v-15Z\" /><path d=\"M4 20.5A2.5 2.5 0 0 1 6.5 18H18v3H6.5A2.5 2.5 0 0 1 4 20.5Z\" /></svg>\n case 'arrow-left': return <svg {...common}><path d=\"M19 12H5M11 6l-6 6 6 6\" /></svg>\n case 'arrow-right': return <svg {...common}><path d=\"M5 12h14M13 6l6 6-6 6\" /></svg>\n case 'chevron-right': return <svg {...common}><path d=\"m9 5 7 7-7 7\" /></svg>\n case 'check': return <svg {...common}><path d=\"m4 12.5 5 5L20 6.5\" /></svg>\n case 'clock': return <svg {...common}><circle cx=\"12\" cy=\"12\" r=\"8.4\" /><path d=\"M12 7v5.4l3.4 2\" /></svg>\n case 'search': return <svg {...common}><circle cx=\"11\" cy=\"11\" r=\"6.6\" /><path d=\"m16 16 4.2 4.2\" /></svg>\n case 'grid': return <svg {...common}><rect x=\"4\" y=\"4\" width=\"7\" height=\"7\" rx=\"1.4\" /><rect x=\"13\" y=\"4\" width=\"7\" height=\"7\" rx=\"1.4\" /><rect x=\"4\" y=\"13\" width=\"7\" height=\"7\" rx=\"1.4\" /><rect x=\"13\" y=\"13\" width=\"7\" height=\"7\" rx=\"1.4\" /></svg>\n case 'info': return <svg {...common}><circle cx=\"12\" cy=\"12\" r=\"8.6\" /><path d=\"M12 11v5M12 7.8v.2\" /></svg>\n case 'warning': return <svg {...common}><path d=\"M12 3 2.8 20h18.4L12 3Z\" /><path d=\"M12 9v5M12 17.2v.2\" /></svg>\n case 'close': return <svg {...common}><path d=\"m6 6 12 12M18 6 6 18\" /></svg>\n case 'target': return <svg {...common}><circle cx=\"12\" cy=\"12\" r=\"8.4\" /><circle cx=\"12\" cy=\"12\" r=\"4\" /><circle cx=\"12\" cy=\"12\" r=\".8\" /></svg>\n case 'spark': return <svg {...common}><path d=\"M12 3v4M12 17v4M3 12h4M17 12h4M5.6 5.6l2.8 2.8M15.6 15.6l2.8 2.8M18.4 5.6l-2.8 2.8M8.4 15.6l-2.8 2.8\" /></svg>\n case 'layers': return <svg {...common}><path d=\"m12 3 8.5 4.7L12 12.4 3.5 7.7 12 3Z\" /><path d=\"m3.5 12.3 8.5 4.7 8.5-4.7\" /><path d=\"m3.5 16.7 8.5 4.7 8.5-4.7\" /></svg>\n case 'code': return <svg {...common}><path d=\"m8 7-5 5 5 5M16 7l5 5-5 5M14 4l-4 16\" /></svg>\n case 'planet': return <svg {...common}><circle cx=\"12\" cy=\"11\" r=\"6\" /><path d=\"M5.2 14.8C2.6 16.2 1.4 18 2.2 19.1c1.2 1.6 5.9.4 10.6-2.7 4.6-3 8-6.6 7.4-8.3-.4-1.2-2.6-1.2-5.4-.2\" /></svg>\n case 'graph': return <svg {...common}><circle cx=\"5.5\" cy=\"17\" r=\"2.2\" /><circle cx=\"12\" cy=\"6.5\" r=\"2.2\" /><circle cx=\"18.5\" cy=\"17\" r=\"2.2\" /><path d=\"M7.4 15.5 10.4 8.6M13.4 8.4l3.7 6.8M7.7 17h8.6\" /></svg>\n case 'file': return <svg {...common}><path d=\"M6 3h7l5 5v13H6V3Z\" /><path d=\"M13 3v5h5\" /></svg>\n }\n}\n","/** Copy for the design-system surfaces: library, route page, knowledge, lesson tab. */\nimport { useLang } from './i18n.js'\n\nconst en = {\n pageTitle: 'Learning map',\n backToChat: 'Back to chat',\n\n // Shell / tabs\n tabRoute: 'Learning route',\n tabKnowledge: 'Knowledge & evidence',\n loading: 'Loading…',\n retry: 'Retry',\n\n // Library\n libraryEyebrow: 'Your learning projects',\n libraryTitle: 'Welcome back',\n libraryBody: 'Tell the chat what you want to learn, and we will build a personal route.',\n libraryPromptTitle: 'Tell the chat what you want to learn',\n libraryPromptExample: 'For example: I want to learn the basics of machine learning.',\n librarySection: 'My learning projects',\n libraryEmptyTitle: 'Start your learning',\n libraryEmptyBody: 'Tell the chat what you want to learn.',\n libraryNoProjects: 'No learning projects yet.',\n openProject: 'Open',\n continueLearning: 'Continue learning',\n statusAccepted: 'Route accepted',\n statusActive: 'In progress',\n statusLegacy: 'Legacy project',\n lastStudied: (when: string): string => `Last studied: ${when}`,\n whenToday: 'today',\n whenYesterday: 'yesterday',\n whenDays: (days: number): string => `${days} days ago`,\n whenWeeks: (weeks: number): string => `${weeks} weeks ago`,\n projectMetaAccepted: (version: number): string => `Route v${version} · accepted`,\n projectMetaActive: 'Building the route',\n projectMetaLegacy: 'Diagnosis map only',\n\n // Stage bar\n currentStage: 'Current stage',\n stepOf: (step: number, total: number): string => `Step ${step} of ${total}`,\n stages: ['Goal & time', 'Initial diagnosis', 'Learning route', 'Learning'],\n stageGoalTitle: 'Goal & time',\n stageDiagnosisTitle: 'Initial diagnosis',\n stageRouteTitle: 'Learning route',\n stageLearningTitle: 'Learning',\n stageGoalDesc: 'Set the outcome and the time you can really spend.',\n stageDiagnosisDesc: 'Find out what you already know.',\n stageRouteDesc: 'Generate a personal route.',\n stageLearningDesc: 'Start learning with targeted guidance.',\n stageHelper: 'These choices help the tutor',\n helperMatchTitle: 'Match the content',\n helperMatchBody: 'The right concepts and examples for your goal and your starting point.',\n helperPaceTitle: 'Plan the pace',\n helperPaceBody: 'Weekly and per-session study time turns into a realistic plan.',\n helperPracticeTitle: 'Practice toward the outcome',\n helperPracticeBody: 'Practice and project work are built around the outcome you named.',\n\n // Goal form\n goalTitle: 'Set your learning goal',\n goalHelp: 'The tutor uses this to shape the route, difficulty and practice.',\n saveGoal: 'Save goal',\n goalSaved: 'Goal saved.',\n fieldPurpose: 'Learning goal',\n fieldOutcomes: 'What you want to be able to do',\n fieldBackground: 'Current background',\n fieldWeekly: 'Time available each week',\n fieldSession: 'Time per session',\n fieldPurposePlaceholder: 'For example: master the core ideas of machine learning and build a small project.',\n fieldOutcomesPlaceholder: 'For example: build a small classifier and explain the errors it makes.',\n fieldBackgroundPlaceholder: 'What you have studied, tried, or found difficult.',\n purposeExamples: 'The clearer the outcome, the better the route fits you.',\n charCount: (length: number, max: number): string => `${length} / ${max}`,\n moreSettings: 'More settings (optional)',\n fieldTargetDate: 'Target date',\n fieldDurationWeeks: 'Weeks available',\n fieldTimeZone: 'Time zone',\n fieldPreferences: 'Preferences',\n noDeadline: 'No fixed deadline',\n flexibilityFlexible: 'Flexible',\n flexibilityFixed: 'Fixed',\n goalNeedsOutcome: 'Add a goal and at least one outcome before saving.',\n goalDraftSaved: 'Draft saved.',\n unsavedChanges: 'Unsaved changes. They stay here until you save.',\n\n // Diagnosis\n diagnosisTitle: 'Start the initial diagnosis',\n diagnosisBody: 'The tutor learns your baseline in the conversation.',\n requestDiagnosis: 'Request diagnosis',\n skipDiagnosis: 'Skip diagnosis',\n diagnosisHowTitle: 'How the initial diagnosis works',\n diagnosisHow: [\n { title: 'Background', body: 'The tutor asks about your goal, experience, and what you want to focus on.' },\n { title: 'Questions', body: 'You answer a few questions in the conversation so the tutor can size your baseline.' },\n { title: 'Result', body: 'The tutor records a starting point and uses it as evidence for the route.' },\n ],\n diagnosisHintTitle: 'Questions appear in the conversation',\n diagnosisHintBody: 'They may be plain text or an option card you can click.',\n diagnosisSummary: 'Diagnosis summary',\n diagnosisReady: 'Ready',\n diagnosisSkipped: 'Skipped',\n diagnosisInProgress: 'In progress',\n diagnosisWaitingTitle: 'Waiting for the tutor…',\n diagnosisWaitingHint: 'The tutor will join this conversation shortly. You can wait or open the chat.',\n elapsed: (duration: string): string => `Waiting ${duration}`,\n openChat: 'Open chat',\n waitingTutor: 'Waiting for the tutor to join…',\n waitingTutorHint: 'You can read the lesson on the right while you wait.',\n tutorNoQuestions: 'No questions yet? Retry',\n diagnosisFailed: 'The tutor could not finish the diagnosis',\n retryWithChat: 'Retry and open the chat',\n\n // Route proposal\n proposalTitle: 'Review and accept the learning route',\n proposalBody: 'Built from your goal and current diagnosis.',\n acceptRoute: 'Accept route',\n requestRoute: 'Generate learning route',\n sessionMismatch: (count: number): string => `${count} lessons longer than one session`,\n requestRevision: 'Request revision',\n routeVersion: (version: number): string => `Route v${version}`,\n pendingAccept: 'Awaiting acceptance',\n plannedNote: 'Plan only, not evidence of mastery.',\n totalDuration: 'Estimated total',\n weeklySuggestion: 'Suggested weekly',\n routeListTitle: 'Learning route',\n expandAll: 'Expand all',\n collapseAll: 'Collapse all',\n startFirstBody: 'Start the first lesson; the tutor teaches it in the conversation.',\n moduleMeta: (hours: string, lessons: number): string => `${hours} · ${lessons} lessons`,\n lessonMinutes: (minutes: string): string => `About ${minutes}`,\n lessonStatusPlanned: 'Planned',\n lessonStatusCurrent: 'In progress',\n lessonStatusCompleted: 'Completed',\n lessonPreview: 'Lesson preview',\n currentLesson: 'Current lesson',\n lessonContents: 'In this lesson',\n answerInChat: 'Answer in the chat',\n answerInChatBody: 'Read the material, then answer in the conversation for immediate feedback.',\n prerequisite: 'Prerequisite',\n closeRail: 'Close',\n startLesson: 'Start this lesson',\n startNextLesson: 'Start next lesson',\n continueLesson: 'Continue this lesson',\n resumeLesson: 'Continue this lesson',\n pauseLesson: 'Pause this lesson',\n lessonPaused: 'Paused',\n unitsWritten: (count: number): string => count === 0 ? 'No units yet' : `${count} unit${count === 1 ? '' : 's'} taught`,\n lastTaught: (title: string): string => `Last taught: ${title}`,\n resumeTeaching: 'Let the tutor continue',\n checkIn: 'Check in with me',\n explainBack: 'Let me explain it back',\n teacherLeads: 'The tutor leads this lesson in the conversation.',\n teacherLeadsHint: 'The tab keeps the reference material; answers happen in the chat.',\n lessonPausedNotice: 'Paused. Continue from the lesson card whenever you are ready.',\n lessonPausedResumeHint: 'Paused. Press Continue this lesson to reconnect the tutor.',\n tutorResuming: 'The tutor is reconnecting to this lesson…',\n tabTutorUnreachable: 'The tutor could not be reached. Open the chat and try again.',\n today: 'Today',\n tabReview: 'Review',\n tabInsights: 'Insights',\n insightsEyebrow: 'Read-only facts',\n insightsTitle: 'Learning insights',\n insightsSub: 'Counts from stored evidence, review history and attempts. No score, percentage or mastery claim.',\n insightsLoading: 'Loading insights…',\n insightsFailed: 'Insights could not be loaded.',\n insightsReview: 'Review',\n insightsReviewDue: (overdue: number, total: number): string => `${overdue} overdue · ${total} total cards`,\n insightsLapsed: 'Cards revisited most',\n insightsGoReview: 'Open Review',\n insightsEvidence: 'Evidence',\n insightsLast7: (count: number): string => `${count} in the last 7 days`,\n insightsWeakNodes: 'Recorded as weak',\n insightsNoWeak: 'No node is currently marked weak or blocked.',\n insightsEvidenceCount: (count: number): string => `${count} evidence`,\n insightsRecentReviews: 'Recent reviews',\n insightsNoneYet: 'No recorded activity yet.',\n insightsActivity: 'Recent attempts',\n insightsAttemptDetail: (units: number, responses: number): string => `${units} units · ${responses} answers`,\n insightsPlan: (open: number, due: number): string => `${open} open plan items · ${due} due today`,\n insightsSources: (count: number): string => `${count} sources`,\n\n reviewEyebrow: 'Spaced review',\n reviewTitle: 'Due now',\n reviewSub: 'Reveal the back, then grade your recall. The runtime only schedules; it does not mark mastery.',\n reviewLoading: 'Loading the review queue…',\n reviewEmpty: 'Nothing due right now',\n reviewEmptyBody: 'Review cards appear here when their due time arrives. Add cards from a lesson with the tutor.',\n reviewShowBack: 'Show the back',\n reviewAgain: 'Again',\n reviewAgainHint: '10 min',\n reviewHard: 'Hard',\n reviewHardHint: 'shorter',\n reviewGood: 'Good',\n reviewGoodHint: 'normal',\n reviewEasy: 'Easy',\n reviewEasyHint: 'longer',\n reviewDue: (count: number): string => count === 1 ? '1 card due' : `${count} cards due`,\n reviewMore: (count: number): string => `${count} more due`,\n reviewFailed: 'The review queue could not be loaded.',\n reviewRetry: 'Retry',\n reviewUndo: 'Undo last grade',\n reviewUndone: 'Grade recorded.',\n reviewUndoFailed: 'Could not undo the last grade.',\n backToRoute: 'Back to learning route',\n sourceRef: 'Sources',\n todayTitle: (count: number): string => count === 1 ? '1 plan item' : `${count} plan items`,\n todayMore: (count: number): string => `${count} more`,\n exportCalendar: 'Export calendar',\n planExported: 'Calendar exported.',\n markTaskDone: 'Done',\n planExportFailed: 'Calendar export failed. Try again.',\n tabPauseFailed: 'Could not pause this lesson. Refresh and try again.',\n answerFailed: 'Could not record this answer. Try again.',\n routeCompleteTitle: 'This conversation finished the route',\n routeCompleteBody: (done: number, total: number): string => `You completed ${done} of ${total} lessons.`,\n masteryNoticeTitle: 'Completing lessons is not mastery',\n masteryNoticeBody: 'Inspect the evidence in the project, or keep practising what is weak.',\n viewEvidence: 'View learning evidence',\n noRouteTitle: 'No route yet',\n noRouteBody: 'Routes come from the goal and diagnosis. Return to the chat to continue.',\n emptyLessonTitle: 'No lesson started',\n emptyLessonBody: 'The current lesson will appear here once the tutor starts it.',\n\n // Waiting / errors / versions\n nextStep: 'Next step',\n routeWaitingTitle: 'Waiting for the tutor…',\n routeWaitingBody: 'The route is being prepared in the conversation. You can watch the chat.',\n routeFailedTitle: 'The tutor could not finish this turn',\n errorBilling: 'The model service has no balance. Switch to a working model or top up, then retry.',\n errorAuth: 'The model rejected the credentials. Check the model provider and try again.',\n errorRate: 'The model is rate-limited right now. Wait a moment, then retry.',\n errorNetwork: 'The network connection failed. Check your connection, then retry.',\n errorContext: 'The conversation is too long for this model. Start a shorter conversation or switch models.',\n errorOther: 'The turn failed. Open the chat to inspect the error, then retry.',\n versionSubtitle: 'Current lesson · this conversation has a different version',\n versionTitle: (current: number, accepted: number): string => `This conversation uses v${current} · the project accepted v${accepted}`,\n versionBody: (current: number, accepted: number): string => `The route has a newer version. This conversation stays on v${current}; it will not move to v${accepted} automatically. Continue here, or view the latest route in the project.`,\n continueConversation: 'Continue this conversation',\n viewLatestRoute: 'View the latest route',\n\n // Knowledge\n knowledgeTitle: (count: number): string => `Knowledge (${count})`,\n searchKnowledge: 'Search knowledge',\n relatedPosition: 'Where it sits',\n prerequisiteKnowledge: 'Prerequisites',\n evidenceTitle: (count: number): string => `Evidence (${count})`,\n noEvidence: 'No evidence recorded yet.',\n evidenceInfoTitle: 'Mastery needs evidence',\n evidenceInfoBody: 'Whether this is truly mastered is judged from practice, conversation, and other evidence together.',\n noNodesYet: 'No knowledge nodes yet.',\n selectNode: 'Pick a knowledge point to see its evidence.',\n nodeStateUnconfirmed: 'To confirm',\n nodeStateExplained: 'Explained',\n nodeStatePracticed: 'Practised',\n nodeStateChecked: 'Checked',\n nodeStateWeak: 'Weak',\n nodeStateBlocked: 'Blocked',\n nodeStateConfirmed: 'Confirmed',\n\n // Lesson tab\n tabTitle: 'Current lesson',\n tabEmptyTitle: 'No lesson yet',\n tabEmptyBody: 'Start a lesson in the Learning map and the tutor-written material will appear here.',\n openLearningMap: 'Open learning map',\n lessonConcept: 'Concept',\n lessonExample: 'Example',\n lessonDiagram: 'Diagram',\n lessonCheck: 'Check',\n lessonWaiting: 'The tutor is preparing this lesson…',\n lessonWaitingBody: 'Stay in the conversation; the material appears here as soon as it is written.',\n lessonComplete: 'Completed',\n lessonInterrupted: 'Paused',\n routeLabel: (version: number): string => `Route v${version}`,\n sessionProgress: (done: number, total: number): string => `This conversation ${done}/${total} lessons`,\n projectProgress: (done: number, total: number): string => `${done}/${total} lessons completed`,\n lessonProject: 'Project',\n checkAnswerHint: 'Answer in the conversation',\n outcomeLabel: 'Lesson outcome',\n backToMap: 'Learning map',\n preparingRoute: 'No route yet',\n preparingRouteBody: 'Return to the chat and ask the tutor for a learning route.',\n}\n\nconst zh: typeof en = {\n pageTitle: '学习地图',\n backToChat: '返回对话',\n\n tabRoute: '学习路线',\n tabKnowledge: '知识与证据',\n loading: '加载中…',\n retry: '重试',\n\n libraryEyebrow: '你的学习项目',\n libraryTitle: '欢迎回来',\n libraryBody: '在对话中说出你想学什么,我们会为你生成个性化的学习路线。',\n libraryPromptTitle: '在对话中说出你想学什么',\n libraryPromptExample: '例如:我想学习机器学习的基础知识。',\n librarySection: '我的学习项目',\n libraryEmptyTitle: '开始你的学习',\n libraryEmptyBody: '在对话中说出你想学什么。',\n libraryNoProjects: '还没有学习项目。',\n openProject: '打开',\n continueLearning: '继续学习',\n statusAccepted: '已接受路线',\n statusActive: '进行中',\n statusLegacy: '旧项目',\n lastStudied: (when: string): string => `上次学习:${when}`,\n whenToday: '今天',\n whenYesterday: '昨天',\n whenDays: (days: number): string => `${days} 天前`,\n whenWeeks: (weeks: number): string => `${weeks} 周前`,\n projectMetaAccepted: (version: number): string => `路线 v${version} · 已接受`,\n projectMetaActive: '路线正在生成',\n projectMetaLegacy: '只有诊断地图',\n\n currentStage: '当前阶段',\n stepOf: (step: number, total: number): string => `第 ${step} / ${total} 步`,\n stages: ['目标与时间', '初始诊断', '学习路线', '开始学习'],\n stageGoalTitle: '目标与时间',\n stageDiagnosisTitle: '初始诊断',\n stageRouteTitle: '学习路线',\n stageLearningTitle: '开始学习',\n stageGoalDesc: '设定你真的能投入的目标和时间。',\n stageDiagnosisDesc: '了解你已经会什么。',\n stageRouteDesc: '生成一条属于你的学习路线。',\n stageLearningDesc: '开始学习并获得针对性辅导。',\n stageHelper: '这些选择将帮助导师',\n helperMatchTitle: '匹配合适的内容',\n helperMatchBody: '根据你的目标和基础,选择合适的知识点与示例。',\n helperPaceTitle: '安排学习节奏',\n helperPaceBody: '结合可用时间,规划每周和每次的学习量。',\n helperPracticeTitle: '设计练习与应用',\n helperPracticeBody: '围绕你的成果目标,提供针对性的练习和项目任务。',\n\n goalTitle: '设定学习目标',\n goalHelp: '导师会据此设计路线、难度和练习。',\n saveGoal: '保存目标',\n goalSaved: '目标已保存。',\n fieldPurpose: '学习目标',\n fieldOutcomes: '希望达到的成果',\n fieldBackground: '当前基础',\n fieldWeekly: '每周可用时间',\n fieldSession: '单次学习时间',\n fieldPurposePlaceholder: '例如:掌握机器学习的核心概念和常用方法,能独立完成一个小项目。',\n fieldOutcomesPlaceholder: '例如:能独立完成小型项目,并能解释关键方法的原理与应用。',\n fieldBackgroundPlaceholder: '你学过什么、尝试过什么、哪里觉得困难。',\n purposeExamples: '目标越具体,路线越贴合你。',\n charCount: (length: number, max: number): string => `${length} / ${max}`,\n moreSettings: '更多设置(可选)',\n fieldTargetDate: '目标日期',\n fieldDurationWeeks: '可用周数',\n fieldTimeZone: '时区',\n fieldPreferences: '偏好',\n noDeadline: '没有固定期限',\n flexibilityFlexible: '灵活',\n flexibilityFixed: '固定',\n goalNeedsOutcome: '保存前请填写目标和至少一个成果。',\n goalDraftSaved: '草稿已保存。',\n unsavedChanges: '有未保存的修改,保存后会显示下一步。',\n\n diagnosisTitle: '开始初始诊断',\n diagnosisBody: '导师会通过对话了解你的基础。',\n requestDiagnosis: '请求初诊',\n skipDiagnosis: '跳过初诊',\n diagnosisHowTitle: '初始诊断将如何进行',\n diagnosisHow: [\n { title: '了解背景', body: '导师会询问你的学习目标、相关经验和关注点。' },\n { title: '回答问题', body: '你将在对话中回答几个问题,帮助导师了解你的基础。' },\n { title: '形成诊断', body: '导师会综合你的回答,确定起点并作为后续路线的依据。' },\n ],\n diagnosisHintTitle: '问题会在对话中出现',\n diagnosisHintBody: '问题可能是文本形式,也可能是可选择的选项卡片。',\n diagnosisSummary: '初诊摘要',\n diagnosisReady: '已就绪',\n diagnosisSkipped: '已跳过',\n diagnosisInProgress: '进行中',\n diagnosisWaitingTitle: '正在等待导师接入…',\n diagnosisWaitingHint: '导师将很快加入对话,你可以等待或打开对话。',\n elapsed: (duration: string): string => `已等待 ${duration}`,\n openChat: '打开对话',\n waitingTutor: '正在等待导师接入…',\n waitingTutorHint: '等待时可以看右侧的课次内容。',\n tutorNoQuestions: '还没有问题?重试',\n diagnosisFailed: '导师没能完成初诊',\n retryWithChat: '重试并打开对话',\n\n proposalTitle: '查看并接受学习路线',\n proposalBody: '根据你的目标与当前初诊生成。',\n acceptRoute: '接受路线',\n requestRoute: '生成学习路线',\n sessionMismatch: (count: number): string => `${count} 课超出单次时长`,\n requestRevision: '请求修订',\n routeVersion: (version: number): string => `路线 v${version}`,\n pendingAccept: '待接受',\n plannedNote: '只是计划,不代表已掌握。',\n totalDuration: '预计总时长',\n weeklySuggestion: '每周建议',\n routeListTitle: '学习路线',\n expandAll: '展开全部',\n collapseAll: '收起全部',\n startFirstBody: '从第一课开始,导师会在对话里带你学。',\n moduleMeta: (hours: string, lessons: number): string => `${hours} · ${lessons} 个课次`,\n lessonMinutes: (minutes: string): string => `约 ${minutes}`,\n lessonStatusPlanned: '尚未开始',\n lessonStatusCurrent: '进行中',\n lessonStatusCompleted: '已完成',\n lessonPreview: '课次预览',\n currentLesson: '当前课次',\n lessonContents: '本课内容',\n answerInChat: '在对话中回答',\n answerInChatBody: '读完后在对话中回答,立即获得反馈。',\n prerequisite: '前置知识',\n closeRail: '关闭',\n startLesson: '开始这一课',\n startNextLesson: '开始下一课',\n continueLesson: '继续这一课',\n resumeLesson: '继续这一课',\n pauseLesson: '暂停这一课',\n lessonPaused: '已暂停',\n unitsWritten: (count: number): string => count === 0 ? '还没有讲解' : `已讲 ${count} 段`,\n lastTaught: (title: string): string => `最近讲解:${title}`,\n resumeTeaching: '让导师继续讲',\n checkIn: '检查我',\n explainBack: '换我讲给你听',\n teacherLeads: '本课由导师在对话里带学。',\n teacherLeadsHint: '右侧保存讲解参考;回答和检查都在对话里进行。',\n lessonPausedNotice: '已暂停。准备好后从课次卡继续。',\n lessonPausedResumeHint: '已暂停。点「继续这一课」让导师接上。',\n tutorResuming: '导师正在接上这一课…',\n tabTutorUnreachable: '暂时联系不上导师。打开对话后重试。',\n today: '今天',\n tabReview: '复习',\n tabInsights: '洞察',\n insightsEyebrow: '只读事实',\n insightsTitle: '学习洞察',\n insightsSub: '来自已存证据、复习历史与课次尝试的计数。没有分数、百分比或掌握断言。',\n insightsLoading: '正在加载洞察…',\n insightsFailed: '洞察加载失败。',\n insightsReview: '复习',\n insightsReviewDue: (overdue: number, total: number): string => `${overdue} 张逾期 · 共 ${total} 张卡`,\n insightsLapsed: '反复回看的卡',\n insightsGoReview: '打开复习',\n insightsEvidence: '证据',\n insightsLast7: (count: number): string => `最近 7 天 ${count} 条`,\n insightsWeakNodes: '已记录为薄弱',\n insightsNoWeak: '目前没有标记为 weak 或 blocked 的节点。',\n insightsEvidenceCount: (count: number): string => `${count} 条证据`,\n insightsRecentReviews: '最近复习',\n insightsNoneYet: '还没有记录。',\n insightsActivity: '最近课次',\n insightsAttemptDetail: (units: number, responses: number): string => `${units} 个单元 · ${responses} 次回答`,\n insightsPlan: (open: number, due: number): string => `${open} 项开放计划 · 今天 ${due} 项到期`,\n insightsSources: (count: number): string => `${count} 个来源`,\n\n reviewEyebrow: '间隔复习',\n reviewTitle: '现在到期',\n reviewSub: '先看正面,再翻到背面并给这次回忆打分。运行时只调度,不判定掌握。',\n reviewLoading: '正在加载复习队列…',\n reviewEmpty: '现在没有到期复习',\n reviewEmptyBody: '到达复习时间的卡片会出现在这里。可以在课次里让导师添加复习卡。',\n reviewShowBack: '翻到背面',\n reviewAgain: '再来一次',\n reviewAgainHint: '10 分钟',\n reviewHard: '困难',\n reviewHardHint: '较短',\n reviewGood: '记得',\n reviewGoodHint: '正常',\n reviewEasy: '很简单',\n reviewEasyHint: '更长',\n reviewDue: (count: number): string => `${count} 张到期`,\n reviewMore: (count: number): string => `还有 ${count} 张到期`,\n reviewFailed: '复习队列加载失败。',\n reviewRetry: '重试',\n reviewUndo: '撤销上一次评分',\n reviewUndone: '评分已记录。',\n reviewUndoFailed: '撤销失败。',\n backToRoute: '返回学习路线',\n sourceRef: '来源',\n todayTitle: (count: number): string => `${count} 项计划`,\n todayMore: (count: number): string => `还有 ${count} 项`,\n exportCalendar: '导出日历',\n planExported: '日历已导出。',\n markTaskDone: '完成',\n planExportFailed: '日历导出失败,请重试。',\n tabPauseFailed: '暂停失败,请刷新后重试。',\n answerFailed: '记录回答失败,请重试。',\n routeCompleteTitle: '本会话已完成这条路线',\n routeCompleteBody: (done: number, total: number): string => `你已完成 ${done}/${total} 课次。`,\n masteryNoticeTitle: '完成课次不等于掌握',\n masteryNoticeBody: '在项目中查看证据,或继续练习薄弱处。',\n viewEvidence: '查看学习证据',\n noRouteTitle: '尚无学习路线',\n noRouteBody: '路线由目标与初诊生成,回对话继续。',\n emptyLessonTitle: '尚未开始课次',\n emptyLessonBody: '导师开始当前课次后,内容会出现在这里。',\n\n nextStep: '下一步',\n routeWaitingTitle: '正在等待导师接入…',\n routeWaitingBody: '路线正在对话中生成,你可以查看对话。',\n routeFailedTitle: '导师没能完成这一步',\n errorBilling: '模型服务余额不足。换一个可用模型或充值后再重试。',\n errorAuth: '模型服务拒绝了凭据。检查模型配置后再试。',\n errorRate: '模型服务当前限流。稍等片刻再重试。',\n errorNetwork: '网络连接失败。检查网络设置后再重试。',\n errorContext: '对话长度超出当前模型的上下文。开始更短的对话或切换模型。',\n errorOther: '这一步失败了。打开对话查看错误,然后重试。',\n versionSubtitle: '当前课次 · 会话版本不同',\n versionTitle: (current: number, accepted: number): string => `本会话使用 v${current} · 项目已接受 v${accepted}`,\n versionBody: (current: number, accepted: number): string => `你的学习路线已有更新。当前会话仍在使用 v${current},不会自动迁移到 v${accepted}。你可以继续当前会话,或返回项目查看最新路线。`,\n continueConversation: '继续当前会话',\n viewLatestRoute: '查看项目中的最新路线',\n\n knowledgeTitle: (count: number): string => `知识节点 (${count})`,\n searchKnowledge: '搜索知识点',\n relatedPosition: '相关位置',\n prerequisiteKnowledge: '前置知识',\n evidenceTitle: (count: number): string => `证据 (${count})`,\n noEvidence: '还没有记录证据。',\n evidenceInfoTitle: '掌握状态需要检查证据',\n evidenceInfoBody: '是否真正掌握该知识点,需要结合练习、对话等多方面证据进行综合判断。',\n noNodesYet: '还没有知识节点。',\n selectNode: '选择一个知识点查看证据。',\n nodeStateUnconfirmed: '待确认',\n nodeStateExplained: '已解释',\n nodeStatePracticed: '已练习',\n nodeStateChecked: '已检查',\n nodeStateWeak: '薄弱',\n nodeStateBlocked: '受阻',\n nodeStateConfirmed: '已确认',\n\n tabTitle: '当前课次',\n tabEmptyTitle: '尚未开始课次',\n tabEmptyBody: '在学习地图中开始一课,导师写的内容会出现在这里。',\n openLearningMap: '打开学习地图',\n lessonConcept: '概念',\n lessonExample: '例子',\n lessonDiagram: '图示',\n lessonCheck: '检查',\n lessonWaiting: '导师正在准备这一课…',\n lessonWaitingBody: '留在对话里,内容写好后会出现在这里。',\n lessonComplete: '已完成',\n lessonInterrupted: '已暂停',\n routeLabel: (version: number): string => `路线 v${version}`,\n sessionProgress: (done: number, total: number): string => `本会话 ${done}/${total} 课次`,\n projectProgress: (done: number, total: number): string => `${done}/${total} 课次已完成`,\n lessonProject: '项目',\n checkAnswerHint: '在对话中回答',\n outcomeLabel: '课次结果',\n backToMap: '学习地图',\n preparingRoute: '尚无路线',\n preparingRouteBody: '返回对话,让导师为你生成学习路线。',\n}\n\nexport type DesignText = typeof en\n\n/** Copy for the design-system surfaces in the active language. */\nexport function useDesignText(): DesignText {\n return useLang() === 'zh' ? zh : en\n}\n\n/** Node state label for the knowledge tab. */\nexport function nodeStateText(state: string, text: DesignText): string {\n switch (state) {\n case 'unconfirmed': return text.nodeStateUnconfirmed\n case 'explained': return text.nodeStateExplained\n case 'practiced': return text.nodeStatePracticed\n case 'checked': return text.nodeStateChecked\n case 'weak': return text.nodeStateWeak\n case 'blocked': return text.nodeStateBlocked\n case 'confirmed': return text.nodeStateConfirmed\n default: return state\n }\n}\n","/**\n * The language switch.\n *\n * Shows the *other* language, because that is what pressing it does: a button\n * labelled with the language you are already reading is a label, not a control.\n * The accessible name states both the action and the destination, so a screen\n * reader hears \"switch to Chinese\" rather than \"中文\".\n *\n * The two languages are written in themselves (`中文`, `EN`) rather than\n * translated — a learner who cannot read the current interface still recognises\n * the name of their own language.\n */\n\nimport type { ReactNode } from 'react'\n\nimport { setLang, useLang } from './i18n.js'\n\n/** Props let the header that hosts it adjust the surrounding rhythm. */\nexport interface LanguageToggleProps {\n /** Extra class for the hosting header's own layout. */\n className?: string | undefined\n}\n\n/**\n * Render the toggle.\n *\n * @param props - optional extra class.\n * @returns a button that flips the interface language.\n */\nexport function LanguageToggle({ className }: LanguageToggleProps): ReactNode {\n const lang = useLang()\n const next = lang === 'zh' ? 'en' : 'zh'\n const classes = ['dt-lang-toggle', className].filter(Boolean).join(' ')\n return (\n <button\n type=\"button\"\n className={classes}\n data-lang={lang}\n aria-label={next === 'zh' ? 'Switch to Chinese' : 'Switch to English'}\n title={next === 'zh' ? 'Switch to Chinese' : 'Switch to English'}\n onClick={() => setLang(next)}\n >\n {next === 'zh' ? '中文' : 'EN'}\n </button>\n )\n}\n","/** Cross-surface signals so pause/resume updates the map and the lesson tab together. */\nexport const PATH_PAUSED_EVENT = 'udt:path-paused'\nexport const PATH_RESUMED_EVENT = 'udt:path-resumed'\n\nexport function announcePathState(paused: boolean): void {\n if (typeof window === 'undefined') return\n window.dispatchEvent(new Event(paused ? PATH_PAUSED_EVENT : PATH_RESUMED_EVENT))\n}\n","/** The learner's Today card: plan items and a calendar export. No teaching judgement. */\nimport type { ReactNode } from 'react'\n\nimport type { TaskView } from '../contract.js'\nimport { useDesignText } from './design-text.js'\nimport { useLang } from './i18n.js'\nimport { Icon } from './design-icons.jsx'\n\nfunction dueLabel(value: string, lang: 'en' | 'zh'): string {\n const date = new Date(value)\n if (!Number.isFinite(date.getTime())) return value\n try {\n return new Intl.DateTimeFormat(lang === 'zh' ? 'zh-CN' : 'en-US', {\n month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit',\n }).format(date)\n } catch { return value }\n}\n\nexport function TodayCard({ tasks, onExport, onComplete }: { tasks: readonly TaskView[]; onExport?: (() => void) | undefined; onComplete?: ((taskId: string) => void) | undefined }): ReactNode {\n const text = useDesignText()\n const lang = useLang()\n const open = tasks.filter((task) => task.status === 'open')\n if (open.length === 0) return null\n return (\n <section className=\"dtx-today\" aria-label={text.today}>\n <div className=\"dtx-today-head\">\n <div>\n <p className=\"dtx-today-eyebrow\">{text.today}</p>\n <h2>{text.todayTitle(open.length)}</h2>\n </div>\n {onExport !== undefined && (\n <button type=\"button\" className=\"dtx-link\" onClick={onExport}>\n <Icon name=\"clock\" size={14} /> {text.exportCalendar}\n </button>\n )}\n </div>\n <ul className=\"dtx-today-list\">\n {open.slice(0, 3).map((task) => (\n <li key={task.id} className=\"dtx-today-task\">\n <span className=\"dtx-today-dot\" aria-hidden=\"true\" />\n <span className=\"dtx-today-title\">{task.title}</span>\n {task.dueAt !== undefined && <span className=\"dtx-today-due\">{dueLabel(task.dueAt, lang)}{task.estimateMin === undefined ? '' : ` · ${task.estimateMin} min`}</span>}\n {onComplete !== undefined && (\n <button type=\"button\" className=\"dtx-today-done\" aria-label={`${text.markTaskDone}: ${task.title}`} onClick={() => onComplete(task.id)}>\n <Icon name=\"check\" size={13} /> {text.markTaskDone}\n </button>\n )}\n </li>\n ))}\n </ul>\n {open.length > 3 && <p className=\"dtx-today-more\">{text.todayMore(open.length - 3)}</p>}\n </section>\n )\n}\n","/** Browser-only path transport. Keep every server-model import erased. */\nimport type { GoalBrief, PathView } from '../path.js'\n\nconst BASE = '/diagnostic-tutor/api/path'\nconst API = '/diagnostic-tutor/api'\n\nexport interface PathWriteParams {\n sessionId: string\n courseId: string\n expectedRevision: number\n}\nexport interface SaveBriefParams extends PathWriteParams { brief: GoalBrief }\nexport interface PathRequestParams extends PathWriteParams {\n kind: 'diagnosis' | 'plan'\n operationId: string\n}\nexport interface AcceptPathParams extends PathWriteParams { version: number }\nexport interface AnswerBlockParams extends PathWriteParams {\n attemptId: string\n blockId: string\n answers: { questionId: string; value: string }[]\n}\nexport interface StartPathParams extends AcceptPathParams {\n itemId: string\n operationId: string\n review?: boolean\n resume?: boolean\n checkin?: boolean\n teachBack?: boolean\n}\nexport interface SourceView {\n id: string\n kind: 'text' | 'file' | 'markdown'\n title: string\n locator: string\n sha256: string\n bytes: number\n createdAt: string\n status: 'ready' | 'error'\n content?: string\n}\nexport interface ReviewGradeParams {\n cardId: string\n rating: 'again' | 'hard' | 'good' | 'easy'\n}\nexport interface TaskMutation {\n taskId: string\n status?: 'open' | 'done' | 'archived'\n}\nexport interface PlanIcs {\n filename: string\n text: string\n}\n\nexport interface PromptedPathView extends PathView {\n prompted: boolean\n promptReason?: string\n}\nexport interface PathClient {\n get(courseId: string, sessionId?: string): Promise<PathView>\n saveBrief(params: SaveBriefParams): Promise<PathView>\n request(params: PathRequestParams): Promise<PromptedPathView>\n skipAssessment(params: PathWriteParams): Promise<PathView>\n accept(params: AcceptPathParams): Promise<PathView>\n start(params: StartPathParams): Promise<PromptedPathView>\n /** Pause the active attempt without ending it. */\n pause?(params: PathWriteParams): Promise<PathView>\n /** Record one quiz/flashcard submission; never grades or changes state. */\n answer?(params: AnswerBlockParams): Promise<PathView>\n /** Read one stored source for a footnote preview. */\n source?(sourceId: string): Promise<{ ok: true; source: SourceView }>\n /** Download the open, dated Today plan as an ICS calendar. */\n exportPlan?(): Promise<PlanIcs>\n /** Mark one plan item done (or reopen it). Plan state only; never teaching state. */\n updateTask?(params: TaskMutation): Promise<{ ok: true; tasks: import('../contract.js').TaskView[] }>\n /** Due review cards; read-only. */\n reviewNext?(): Promise<{ ok: true; cards: import('../contract.js').ReviewCardView[]; count: number }>\n /** Record one review grade and reschedule the card. */\n reviewGrade?(params: ReviewGradeParams): Promise<{ ok: true; card: import('../contract.js').ReviewCardView; remaining: number }>\n /** Read-only course insights for the Insights tab. */\n insights?(courseId: string): Promise<import('../contract.js').InsightsView>\n /** Undo the latest grade for one card when its log carries the prior schedule. */\n reviewUndo?(cardId: string): Promise<{ ok: true; card: import('../contract.js').ReviewCardView; remaining: number }>\n}\n\n/** Retain HTTP status so a stale write can refresh without losing form edits. */\nexport class PathClientError extends Error {\n constructor(message: string, readonly code: string, readonly status: number) {\n super(message)\n this.name = 'PathClientError'\n }\n}\n\nasync function request<T extends PathView>(suffix: string, body?: unknown): Promise<T> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 12_000)\n try {\n const response = await fetch(`${BASE}${suffix}`, {\n method: body === undefined ? 'GET' : 'POST',\n headers: body === undefined\n ? { accept: 'application/json' }\n : { accept: 'application/json', 'content-type': 'application/json' },\n ...(body === undefined ? {} : { body: JSON.stringify(body) }),\n signal: controller.signal,\n })\n let value: unknown\n try { value = await response.json() } catch {\n throw new PathClientError(`Unreadable server response (HTTP ${response.status}).`, 'invalid_response', response.status)\n }\n const envelope = value as { ok?: unknown; error?: { code?: string; message?: string }; path?: unknown }\n if (!response.ok || envelope?.ok === false) {\n throw new PathClientError(envelope?.error?.message ?? `Request failed (HTTP ${response.status}).`, envelope?.error?.code ?? 'request_failed', response.status)\n }\n if (envelope?.ok !== true || !envelope.path) {\n throw new PathClientError('The server did not return a learning path.', 'invalid_response', response.status)\n }\n return value as T\n } finally { clearTimeout(timeout) }\n}\n\nasync function requestSource(sourceId: string): Promise<{ ok: true; source: SourceView }> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 12_000)\n try {\n const response = await fetch(`${API}/source?id=${encodeURIComponent(sourceId)}`, { headers: { accept: 'application/json' }, signal: controller.signal })\n const value = await response.json() as { ok?: unknown; source?: unknown; error?: { message?: string; code?: string } }\n if (!response.ok || value?.ok !== true || value.source === undefined) {\n throw new PathClientError(value?.error?.message ?? `Source request failed (HTTP ${response.status}).`, value?.error?.code ?? 'source_failed', response.status)\n }\n return value as { ok: true; source: SourceView }\n } finally { clearTimeout(timeout) }\n}\n\nasync function requestTaskMutation(params: TaskMutation): Promise<{ ok: true; tasks: import('../contract.js').TaskView[] }> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 12_000)\n try {\n const response = await fetch(`${API}/plan/task`, {\n method: 'POST',\n headers: { accept: 'application/json', 'content-type': 'application/json' },\n body: JSON.stringify(params),\n signal: controller.signal,\n })\n const value = await response.json() as { ok?: unknown; tasks?: unknown; error?: { message?: string; code?: string } }\n if (!response.ok || value?.ok !== true || !Array.isArray(value.tasks)) {\n throw new PathClientError(value?.error?.message ?? `Plan update failed (HTTP ${response.status}).`, value?.error?.code ?? 'plan_update_failed', response.status)\n }\n return value as { ok: true; tasks: import('../contract.js').TaskView[] }\n } finally { clearTimeout(timeout) }\n}\n\nasync function requestInsights(courseId: string): Promise<import('../contract.js').InsightsView> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 12_000)\n try {\n const response = await fetch(`${API}/insights?courseId=${encodeURIComponent(courseId)}`, { headers: { accept: 'application/json' }, signal: controller.signal })\n const value = await response.json() as { ok?: unknown; error?: { message?: string; code?: string } }\n if (!response.ok || value?.ok !== true) {\n throw new PathClientError(value?.error?.message ?? `Insights request failed (HTTP ${response.status}).`, value?.error?.code ?? 'insights_failed', response.status)\n }\n return value as import('../contract.js').InsightsView\n } finally { clearTimeout(timeout) }\n}\n\nasync function requestReviewNext(): Promise<{ ok: true; cards: import('../contract.js').ReviewCardView[]; count: number }> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 12_000)\n try {\n const response = await fetch(`${API}/review`, { headers: { accept: 'application/json' }, signal: controller.signal })\n const value = await response.json() as { ok?: unknown; cards?: unknown; count?: unknown; error?: { message?: string; code?: string } }\n if (!response.ok || value?.ok !== true || !Array.isArray(value.cards)) {\n throw new PathClientError(value?.error?.message ?? `Review request failed (HTTP ${response.status}).`, value?.error?.code ?? 'review_failed', response.status)\n }\n return { ok: true, cards: value.cards as import('../contract.js').ReviewCardView[], count: typeof value.count === 'number' ? value.count : value.cards.length }\n } finally { clearTimeout(timeout) }\n}\n\nasync function requestReviewUndo(cardId: string): Promise<{ ok: true; card: import('../contract.js').ReviewCardView; remaining: number }> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 12_000)\n try {\n const response = await fetch(`${API}/review/undo`, {\n method: 'POST',\n headers: { accept: 'application/json', 'content-type': 'application/json' },\n body: JSON.stringify({ cardId }),\n signal: controller.signal,\n })\n const value = await response.json() as { ok?: unknown; card?: unknown; remaining?: unknown; error?: { message?: string; code?: string } }\n if (!response.ok || value?.ok !== true || value.card === undefined) {\n throw new PathClientError(value?.error?.message ?? `Review undo failed (HTTP ${response.status}).`, value?.error?.code ?? 'review_undo_failed', response.status)\n }\n return { ok: true, card: value.card as import('../contract.js').ReviewCardView, remaining: typeof value.remaining === 'number' ? value.remaining : 0 }\n } finally { clearTimeout(timeout) }\n}\n\nasync function requestReviewGrade(params: ReviewGradeParams): Promise<{ ok: true; card: import('../contract.js').ReviewCardView; remaining: number }> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 12_000)\n try {\n const response = await fetch(`${API}/review/grade`, {\n method: 'POST',\n headers: { accept: 'application/json', 'content-type': 'application/json' },\n body: JSON.stringify(params),\n signal: controller.signal,\n })\n const value = await response.json() as { ok?: unknown; card?: unknown; remaining?: unknown; error?: { message?: string; code?: string } }\n if (!response.ok || value?.ok !== true || value.card === undefined) {\n throw new PathClientError(value?.error?.message ?? `Review grade failed (HTTP ${response.status}).`, value?.error?.code ?? 'review_grade_failed', response.status)\n }\n return { ok: true, card: value.card as import('../contract.js').ReviewCardView, remaining: typeof value.remaining === 'number' ? value.remaining : 0 }\n } finally { clearTimeout(timeout) }\n}\n\nasync function requestPlanIcs(): Promise<PlanIcs> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 12_000)\n try {\n const response = await fetch(`${API}/plan/ics`, { headers: { accept: 'text/calendar' }, signal: controller.signal })\n if (!response.ok) throw new PathClientError(`Calendar export failed (HTTP ${response.status}).`, 'calendar_failed', response.status)\n return { filename: 'dsh-diagnostic-tutor-plan.ics', text: await response.text() }\n } finally { clearTimeout(timeout) }\n}\n\nexport const defaultPathClient: PathClient = {\n get(courseId, sessionId) {\n const query = new URLSearchParams({ courseId })\n if (sessionId) query.set('sessionId', sessionId)\n return request(`?${query.toString()}`)\n },\n saveBrief: (params) => request('/brief', params),\n request: (params) => request<PromptedPathView>('/request', params),\n skipAssessment: (params) => request('/skip-assessment', params),\n accept: (params) => request('/accept', params),\n start: (params) => request<PromptedPathView>('/start', params),\n pause: (params) => request('/pause', params),\n answer: (params) => request('/answer', params),\n source: requestSource,\n exportPlan: requestPlanIcs,\n updateTask: requestTaskMutation,\n reviewNext: requestReviewNext,\n reviewGrade: requestReviewGrade,\n reviewUndo: requestReviewUndo,\n insights: requestInsights,\n}\n","/**\n * Copy for the course-path page, in the two interface languages this build\n * ships.\n *\n * `en` is the source of truth: every string here is the copy the page already\n * renders, unchanged. `zh` is typed `typeof en`, so a missing or renamed key is\n * a type error rather than an English string leaking into the Chinese UI.\n *\n * Only interface copy lives here. Route, module and lesson titles, summaries,\n * overviews, outcomes, outline lines, practice and assessment text, goal-brief\n * text, topics, IDs and node titles are learner/tutor content from props and\n * are never translated.\n *\n * Pure helpers in `path-page.tsx` receive the text object (or `lang`) as a\n * parameter; only components may call `usePathText()`.\n */\nimport type { TutorErrorKind } from './tutor-error.js'\n\nimport { useLang } from './i18n.js'\n\n/** The goal-brief fields the time-plan sentence renders. */\ninterface TimePlanBrief {\n targetDate: string | null\n timeZone: string\n durationWeeks: number | null\n deadline: 'none' | 'flexible' | 'fixed'\n weeklyMinutes: number | null\n sessionMinutes: number | null\n}\n\nconst en = {\n // Page shell and header.\n pageLabel: (title: string) => `${title} curriculum`,\n learningPath: 'Learning path',\n learningPathIntro: 'A complete route to browse, with teaching and checks one lesson at a time.',\n stagebarLabel: 'Progress through this project',\n stageGoal: 'Goal & time',\n stageDiagnosis: 'Initial diagnosis',\n stageRoute: 'Learning route',\n stageLearning: 'Learning',\n stageDone: 'done',\n stageCurrent: 'current',\n stagePending: 'not started',\n stageGoalHint: 'Save your goal and available time first. The rest of the page stays hidden until you do.',\n stageDiagnosisHint: 'The tutor is finding your starting point in the conversation. Answer there; the map and the route update after the diagnosis is recorded.',\n stageRouteHint: 'Diagnosis is ready. Review the proposed route, then accept it when it fits.',\n stageLearningHint: 'A route is accepted. Browse any lesson Overview, then start one when you are ready.',\n nextStepEyebrow: 'Next step',\n nextStepProgress: (step: number, total: number) => `Step ${step} of ${total}`,\n nextGoalTitle: 'Set the goal and time budget',\n nextGoalBody: 'One short form — purpose, outcomes and weekly time. The route is built from it.',\n nextDiagnosisTitle: 'Ask the tutor for a quick check',\n nextDiagnosisBody: 'The tutor asks a few questions, then uses them for the route. You can skip.',\n nextWaitingTitle: 'Answer the tutor’s questions',\n nextWaitingBody: 'The diagnosis is running in the conversation. Answer there, or skip.',\n nextRetryDiagnosisLink: 'No questions yet? Retry',\n nextWaitingRunningTitle: 'The tutor is preparing a few questions',\n nextWaitingRunningBody: 'The tutor is thinking. Answer once the questions appear, or skip.',\n nextDiagnosisFailedTitle: 'The tutor could not finish the check',\n nextDiagnosisFailedBody: (advice: string, reason: string): string => `${advice} Provider error: ${reason}`,\n waitingFor: (duration: string): string => `Waiting ${duration}.`,\n durationDays: (value: number) => `${value} d`,\n tutorFailureAdvice: (kind: TutorErrorKind): string => {\n switch (kind) {\n case 'billing': return 'The model provider reported insufficient balance. Switch to a working model or top up the provider, then retry.'\n case 'auth': return 'The model provider rejected the credentials. Check the API key or sign-in, then retry.'\n case 'rate-limit': return 'The model provider is rate limiting. Wait a moment, then retry.'\n case 'network': return 'The model provider could not be reached. Check the network, then retry.'\n case 'context': return 'The conversation is too long for this model. Start a fresh conversation or choose a larger-context model, then retry.'\n default: return 'The tutor turn failed. Retrying wakes the tutor again with the current model and opens the conversation.'\n }\n },\n nextRetryDiagnosis: 'Retry and open conversation',\n nextRouteWaitingTitle: 'The tutor is preparing the route',\n nextRouteWaitingBody: 'The proposal is being prepared in the conversation. You can keep this page open.',\n nextRouteTitle: 'Generate the learning route',\n nextRouteBody: 'The proposal orders lessons from your goal, the diagnosis and your available time. Nothing starts until you accept it.',\n nextProposalTitle: 'The route proposal is ready',\n nextProposalBody: (version: number) => `Review v${version}, then accept to unlock lessons.`,\n nextAcceptedTitle: 'Start the first lesson',\n nextAcceptedBody: (title: string) => `Start with “${title}”.`,\n nextStartNextTitle: 'Start the next lesson',\n nextStartNextBody: (done: number, total: number, title: string) => `${done}/${total} done. Next: “${title}”.`,\n nextStartNextAction: 'Start next lesson',\n nextCompleteTitle: 'This route is complete',\n nextCompleteBody: 'All lessons have a completed attempt. Ask the tutor what comes next.',\n routeEffort: (lessons: number, duration: string): string => `${lessons} lessons · ${duration}`,\n routeWeeks: (weeksMin: number, weeksMax: number): string => `≈${weeksMin}–${weeksMax} weeks`,\n routeSessionMismatch: (count: number, sessionMinutes: number): string => `${count} lessons over ${sessionMinutes}-min sessions`,\n routeProgress: (completed: number, total: number): string => `${completed}/${total} done`,\n nextContinueTitle: 'Continue the current lesson',\n nextContinueBody: (title: string) => `Current: “${title}”.`,\n nextSetGoal: 'Set goal and time',\n nextRequestDiagnosis: 'Request initial diagnosis',\n nextRequestRoute: 'Request route proposal',\n nextAcceptVersion: (version: number) => `Accept version ${version}`,\n nextStartFirst: 'Start first lesson',\n nextOpenLesson: 'Continue this lesson',\n projectOverview: 'Project Overview',\n goalAndTimeSettings: 'Goal and time settings',\n readOnlyNotice: 'Browsing only. Open a conversation with this project selected to save a goal, request the tutor, accept a route, or start a lesson.',\n updatesPaused: 'Updates paused: ',\n couldNotLoadPath: 'Could not load this path: ',\n retryLoading: 'Retry loading',\n requestSavedTutorUnreached: 'The request was saved, but the tutor was not reached: ',\n previousRequestFailed: (kind: 'plan' | 'diagnosis') => `The previous ${kind === 'plan' ? 'route' : 'diagnosis'} request did not reach the tutor:`,\n retryTutorRequest: 'Retry tutor request',\n retryTutorHelp: 'Retry sends a new operation only when you click.',\n loadingPath: 'Loading the saved learning path…',\n unsavedKept: 'Your unsaved goal edits are kept on this page.',\n returnToDraft: 'Return to goal draft',\n noReason: 'No reason supplied.',\n tutorUnreachable: 'The tutor could not be reached.',\n staleProjectError: 'This project changed in another conversation. The latest version has been refreshed; your unsaved goal edits are kept. Review it and click your action again.',\n requestFailed: 'The request could not be completed.',\n\n // Status vocabularies.\n attemptStatus: (status: 'requested' | 'working' | 'completed' | 'interrupted'): string => status,\n assessmentStatus: (status: 'in-progress' | 'ready' | 'skipped'): string => status,\n nodeState: (state: string): string => state,\n edgeKind: (kind: 'required' | 'optional'): string => kind,\n statusPlanned: 'Planned · overview available',\n statusCurrent: (status: string) => `Current · ${status}`,\n statusCompletedAttempt: 'Completed attempt',\n statusInterruptedAttempt: 'Interrupted attempt',\n currentAttemptSuffix: ' · current attempt',\n\n // Goal-brief validation.\n wholeNumber: (label: string, min: number, max: number) => `${label} must be a whole number from ${min} to ${max}, or left blank.`,\n invalidTimeZone: 'Enter a valid IANA time zone, such as America/New_York or UTC.',\n timeZoneRequired: 'Choose a time zone before saving.',\n invalidTargetDate: 'Choose a valid target date, or select No deadline.',\n durationRequired: 'Enter a duration in weeks, or select No deadline.',\n outcomeLimits: 'Use up to 20 outcomes, with at most 2,000 characters each.',\n\n // Durations and small shared fragments.\n durationMinutes: (value: number) => `${value} min`,\n durationHours: (value: number) => `${value} h`,\n notRecorded: 'Not recorded yet.',\n evidence: (index: number) => `evidence ${index}`,\n\n // Goal wizard.\n wizardLabel: 'Learning goal wizard',\n wizardEyebrow: 'Your goal and time',\n wizardTitle: 'Build a route around what you want to do',\n closeWizard: 'Close wizard',\n wizardIntro: 'Save at any step. You can leave your purpose, outcomes, background, budget, and preferences unfinished.',\n wizardRecovered: 'Saved goal draft recovered from this project.',\n wizardStepsLabel: 'Goal wizard steps',\n wizardSteps: ['Goal', 'Background', 'Time', 'Preferences'],\n purposeLabel: 'What will you use this for?',\n purposePlaceholder: 'A work project, exam, interview, or something you want to understand…',\n purposeHelp: 'The situation and why it matters to you. You can decide later.',\n outcomesLabel: 'What should you be able to do?',\n outcomesPlaceholder: 'One observable outcome per line, such as implement a model and explain its errors.',\n outcomesHelp: 'Describe something you could explain, solve, build, or apply. Blank is fine for a draft.',\n backgroundLabel: 'What have you already studied or tried?',\n backgroundPlaceholder: 'What feels familiar? What is difficult or uncertain?',\n backgroundHelp: 'This is your self-report. The tutor will distinguish it from observed evidence.',\n targetTimeLabel: 'Target time',\n timeNone: 'No deadline / not sure yet',\n timeDate: 'A target date',\n timeWeeks: 'A number of weeks',\n timeZoneLabel: 'Time zone',\n timeZonePlaceholder: 'America/New_York',\n timeZoneHelp: 'IANA time zone; saved alongside your date.',\n targetDateLabel: 'Target date',\n durationWeeksLabel: 'Duration in weeks',\n durationWeeksPlaceholder: 'For example, 8',\n deadlineLabel: 'How firm is this time?',\n deadlineFlexible: 'Flexible target',\n deadlineFixed: 'Fixed deadline',\n weeklyMinutesField: 'Minutes available each week',\n weeklyMinutesErrorLabel: 'Weekly minutes',\n notSureYet: 'Not sure yet',\n weeklyHelp: 'Include practice and review.',\n sessionMinutesField: 'Preferred minutes per session',\n sessionMinutesErrorLabel: 'Session minutes',\n preferencesLabel: 'Materials, language, scope, and learning preferences',\n preferencesPlaceholder: 'Topics to include, a textbook you use, preferred language, or the kinds of practice you want…',\n back: 'Back',\n next: 'Next',\n saving: 'Saving…',\n saveDraft: 'Save draft',\n saveAndClose: 'Save and close',\n unsavedChanges: 'Unsaved changes',\n savedToProject: 'Saved to this project',\n draftNotSaved: 'Draft not saved yet',\n saveReadOnly: 'Open a conversation with this project selected to save your goal.',\n\n // Initial diagnosis.\n initialDiagnosis: 'Initial diagnosis',\n assessmentNotRequested: 'Not requested',\n assessmentSkipped: 'You chose to skip the initial assessment. Some foundations remain unverified; the route should make those assumptions visible.',\n strengths: 'Strengths',\n gaps: 'Gaps',\n uncertainties: 'Uncertainties',\n noStrengths: 'No strengths recorded yet.',\n noGaps: 'No gaps recorded yet.',\n noUncertainties: 'No uncertainties recorded yet.',\n evidenceReferences: 'Evidence references',\n assessmentHelp: 'The tutor uses your answers and existing evidence to choose a useful starting point. Background notes are self-reports until checked.',\n diagnosisWaiting: 'Diagnosis requested. Waiting for the tutor in your conversation.',\n diagnosisPendingTitle: 'Initial diagnosis in progress',\n diagnosisPendingBody: 'The tutor has asked you something in the conversation. Answer there; the diagnosis and the knowledge map update only after the answer is recorded.',\n diagnosisDoneSummary: 'Initial diagnosis summary',\n routeSettingsSummary: 'Route versions and settings',\n noCurriculumDiagnosis: 'The learning route appears here after the initial diagnosis is complete. Finish it in the conversation, or skip it.',\n noCurriculumRoute: 'Diagnosis is ready. Use the Next step card above; the map appears here once the tutor proposes a route.',\n diagnosisNeedsGoal: 'Save a purpose and at least one observable outcome before requesting diagnosis.',\n diagnosisNeedsSave: 'Save your goal edits before requesting the tutor.',\n requestDiagnosisAgain: 'Request diagnosis again',\n requestNewDiagnosis: 'Request a new diagnosis',\n requestDiagnosis: 'Request diagnosis',\n skipAssessment: 'Skip initial assessment',\n assessmentSkippedNotice: 'Initial assessment explicitly skipped. The route will retain unverified assumptions.',\n assessmentStale: 'The previous assessment belongs to an earlier goal draft. Request a new diagnosis or explicitly skip for the current goal.',\n\n // Route canvas and ordered list.\n routeCanvasLabel: 'Curriculum route and lesson list',\n searchLessonsLabel: 'Search curriculum lessons',\n searchPlaceholder: 'Search lessons or topics',\n zoomOutLabel: 'Zoom out curriculum',\n zoomInLabel: 'Zoom in curriculum',\n fitRoute: 'Fit route',\n currentLesson: 'Current lesson',\n canvasHelp: 'Drag the canvas to pan. Focus the canvas and use arrow keys to pan, plus or minus to zoom, or Home to fit. Every lesson is also available in the ordered list below.',\n canvasLabel: 'Curriculum route canvas',\n edgesLabel: 'Directed prerequisite connections; solid lines are required and dashed lines are optional',\n edgeTitle: (kind: string, from: string | undefined, to: string | undefined) => `${kind} prerequisite: ${from} to ${to}`,\n expand: 'Expand',\n collapse: 'Collapse',\n moduleToggle: (collapsed: boolean, title: string | undefined) => `${collapsed ? 'Expand' : 'Collapse'} module ${title}`,\n overviewLesson: (index: number, title: string) => `Overview lesson ${index}: ${title}`,\n requiredPrerequisite: 'Required prerequisite →',\n optionalPrerequisite: 'Optional prerequisite →',\n lessonOrderLegend: 'Numbers show recommended lesson order',\n expandAllModules: 'Expand all modules',\n collapseAllModules: 'Collapse all modules',\n allOverviews: 'All lesson Overviews',\n allOverviewsHelp: 'Browse any lesson. Opening an Overview keeps your current lesson in place.',\n matchingLessons: (count: number) => `${count} matching ${count === 1 ? 'lesson' : 'lessons'}. The list includes results from collapsed modules.`,\n lessonListLabel: 'Curriculum lesson list',\n browseLesson: (index: number, title: string) => `Browse lesson ${index}: ${title}`,\n\n // Attempt history.\n historyLabel: 'Lesson attempt history',\n historyTitle: 'Lesson attempts and saved teaching units',\n noAttempts: 'No lesson attempts recorded in this conversation.',\n savedUnits: (count: number) => `${count} saved ${count === 1 ? 'unit' : 'units'}`,\n attemptLabel: 'Attempt',\n attemptWord: 'attempt',\n lessonLabel: 'lesson',\n knowledgeLabel: 'knowledge',\n tutorNotReached: 'Tutor was not reached:',\n snapshot: (id: string) => `snapshot ${id}`,\n savedAt: 'Saved',\n lessonReference: 'lesson reference',\n nodeLabel: 'node',\n\n // Lesson detail.\n lessonOf: (index: number, total: number) => `Lesson ${index} of ${total}`,\n version: (version: number) => `version ${version}`,\n routeVersion: (version: number) => `Route version ${version}`,\n lessonIdLabel: 'Lesson ID:',\n estimatedEffortTitle: 'Estimated effort',\n estimatedEffortLabel: 'estimated effort',\n learnOutcomes: 'What you should be able to do',\n lessonOutline: 'Lesson outline',\n practice: 'Practice',\n assessmentHeading: 'Assessment',\n prerequisites: 'Prerequisites',\n noPrerequisites: 'No prerequisite lessons specified.',\n somePrerequisitesIncomplete: 'Some prerequisite lessons have no completed attempt. You can browse freely; ask the tutor for a short check if you want to begin here.',\n linkedKnowledge: 'Linked knowledge',\n notDiagnosed: 'Not yet diagnosed',\n noLinkedNodes: 'No diagnosed knowledge nodes linked yet.',\n plannedTopics: (topics: string) => `Planned topics: ${topics}. Planning a topic does not establish its knowledge state.`,\n overviewBeforeTeaching: 'This Overview is available before teaching content exists. Teaching units are written when you start the lesson.',\n acceptToStart: 'Accept this route to start its lessons. You can continue browsing every Overview.',\n startReadOnly: 'Open a conversation with this project selected to start or review a lesson.',\n continueLesson: 'Continue lesson',\n startLesson: 'Start lesson',\n reviewLesson: 'Review lesson',\n previousOverview: 'Previous Overview',\n nextOverview: 'Next Overview',\n\n // Module detail.\n moduleOverview: (version: number) => `Module Overview · version ${version}`,\n lessons: (count: number) => `${count} lessons`,\n moduleOutcomes: 'Module outcomes',\n milestoneAssessment: 'Milestone assessment',\n crossModulePrerequisites: 'Prerequisite lessons from other modules',\n noCrossModulePrerequisites: 'No cross-module prerequisite lessons specified.',\n lessonOverviews: 'Lesson Overviews',\n\n // Project detail.\n projectOverviewVersion: (version: number) => `Project Overview · version ${version}`,\n acceptedRoute: 'Accepted route',\n previouslyAcceptedRoute: 'Previously accepted route',\n draftRoute: 'Draft route',\n plannedLessons: 'planned lessons',\n completedAttemptLessons: 'lessons with a completed attempt',\n intendedOutcomes: 'Your intended outcomes',\n timePlan: (brief: TimePlanBrief | null): string => [\n brief?.targetDate\n ? `Target ${brief.targetDate} (${brief.timeZone}); ${brief.deadline} deadline.`\n : brief?.durationWeeks\n ? `${brief.durationWeeks} weeks; ${brief.deadline} target.`\n : 'No deadline selected.',\n brief?.weeklyMinutes ? `${brief.weeklyMinutes} minutes per week.` : 'Weekly budget not specified.',\n brief?.sessionMinutes ? `${brief.sessionMinutes} minutes per session preferred.` : '',\n ].filter(Boolean).join(' '),\n timePlanHeading: 'Time plan',\n planningAssumptions: 'Planning assumptions',\n noAssumptions: 'No planning assumptions recorded.',\n whyVersion: 'Why this version was proposed',\n modulesAndMilestones: 'Modules and milestones',\n browseFirstLesson: 'Browse the first lesson',\n browseCurrentLesson: 'Browse your current lesson',\n completedNote: 'Completed attempts describe recorded activity. Knowledge states depend on evidence; estimated time does not promise mastery.',\n\n // Pinned lesson card.\n pinnedLessonLabel: 'Current pinned lesson',\n pinnedAttemptEyebrow: 'This conversation\\'s pinned attempt',\n newerAcceptedRoute: (accepted: number, attempt: number) => `The accepted route is now version ${accepted}. This conversation remains on version ${attempt} until you explicitly start a lesson from another route.`,\n viewPinnedOverview: 'View pinned lesson Overview',\n browseAcceptedVersion: (version: number) => `Browse accepted version ${version}`,\n openCurrentConversation: 'Open current conversation',\n pinnedRequestFailed: 'The pinned lesson request did not reach the tutor:',\n retryCurrentLesson: 'Retry current lesson request',\n\n // Knowledge reference.\n knowledgeReferenceLabel: 'Linked knowledge evidence reference',\n closeKnowledgeReference: 'Close knowledge reference',\n evidenceEntries: (count: number) => `${count} recorded evidence entries`,\n knowledgeIdLabel: 'Knowledge ID:',\n knowledgeIdHelp: '. Open the Knowledge tab to inspect the recorded observations. A curriculum topic alone does not confirm understanding.',\n\n // Route proposals.\n routeProposalsLabel: 'Route proposals',\n routeVersions: 'Route versions',\n buildRoute: 'Build your curriculum route',\n waitingProposal: 'Waiting for a tutor proposal',\n noVersionsHelp: 'Save your goal, then complete or explicitly skip the initial diagnosis. The tutor can propose a complete outline with every lesson Overview before the lesson bodies are written.',\n needsAssessmentHelp: 'The current goal needs an assessment or your explicit skip before a plan can be requested.',\n routeToBrowse: 'Route to browse',\n routeVersionToBrowse: 'Route version to browse',\n versionOption: (version: number, accepted: boolean, previouslyAccepted: boolean, title: string) => `Version ${version} · ${accepted ? 'accepted' : previouslyAccepted ? 'previously accepted' : 'draft'} · ${title}`,\n requestPlanAgain: 'Request plan again',\n requestRevisedPlan: 'Request a revised plan',\n requestRouteProposal: 'Request a route proposal',\n acceptRouteVersion: (version: number) => `Accept route version ${version}`,\n routeAcceptedNotice: (version: number) => `Route version ${version} accepted. Start a lesson explicitly when ready.`,\n routeAcceptedHelp: 'This route is accepted. Lesson starts remain your choice.',\n routeProposalHelp: 'Browsing a proposal does not accept it or start a lesson.',\n routeRevisionWarning: ' This version was based on an earlier goal or assessment; request a revision before accepting it.',\n newerDraftHelp: 'A newer draft is available in the version selector. Your current view and attempt have been kept.',\n\n // Effort and capacity.\n capacityLabel: 'Estimated effort and available capacity',\n capacityTitle: (range: string) => `Estimated effort: ${range}`,\n capacityNeedBudget: 'Add a weekly budget and a target date or duration to compare available time.',\n capacityAvailable: (minutes: string) => `Approximate available budget: ${minutes} in your selected time window.`,\n capacityNote: 'Estimates include the planned lessons; time alone does not establish mastery.',\n conflictMinimum: 'The minimum estimated effort exceeds your available time.',\n conflictMaximum: 'The upper estimate exceeds your available time.',\n conflictAdvice: 'You can adjust scope, extend the timeline, increase available time, or ask the tutor for an adjusted proposal.',\n editGoalScope: 'Edit goal or scope',\n editTimeBudget: 'Edit time budget',\n requestAdjustedPlan: 'Request an adjusted plan',\n\n // Empty route and shared actions.\n routeEmptyEyebrow: 'Your route will appear here',\n tutorPreparing: 'The tutor is preparing a proposal',\n noCurriculum: 'No curriculum has been proposed yet',\n routeEmptyHelp: 'Existing knowledge and teaching remain available from the Knowledge tab and conversation.',\n openConversation: 'Open conversation',\n askNeedsGoal: 'Save your purpose and observable outcomes before requesting the tutor.',\n askNeedsAssessment: 'Complete or explicitly skip the assessment for the saved goal before requesting a plan.',\n requestSent: (kind: 'plan' | 'diagnosis') => `${kind === 'plan' ? 'Route proposal' : 'Diagnosis'} request sent to the tutor in your conversation.`,\n lessonRequested: 'Lesson requested in your conversation.',\n goalSaved: 'Goal draft saved. You can request diagnosis when you are ready.',\n draftSaved: 'Incomplete goal draft saved. Return to add your purpose and observable outcomes when ready.',\n}\n\nconst zh: typeof en = {\n // Page shell and header.\n pageLabel: (title) => `${title}课程大纲`,\n learningPath: '学习路线',\n learningPathIntro: '完整的学习路线,可逐课次浏览,并以课次为单位进行教学与检查。',\n stagebarLabel: '项目学习进度',\n stageGoal: '目标与时间',\n stageDiagnosis: '初始诊断',\n stageRoute: '学习路线',\n stageLearning: '开始学习',\n stageDone: '已完成',\n stageCurrent: '当前',\n stagePending: '未开始',\n stageGoalHint: '先保存你的目标和可用时间。完成之前,页面其余部分先不展开。',\n stageDiagnosisHint: '导师正在对话中了解你的起点。请在对话里回答;诊断记录之后,知识地图和学习路线才会更新。',\n stageRouteHint: '初诊已完成。先看看导师提出的学习路线,合适就接受。',\n stageLearningHint: '学习路线已接受。可以浏览任意课次概览,准备好后开始一课。',\n nextStepEyebrow: '下一步',\n nextStepProgress: (step, total) => `第 ${step} 步,共 ${total} 步`,\n nextGoalTitle: '先定目标和时间',\n nextGoalBody: '只有一小段表单:目的、产出和每周时间。路线就从这里来。',\n nextDiagnosisTitle: '先让导师做一次快速初诊',\n nextDiagnosisBody: '导师会问几个问题,路线会用到结果;也可以跳过。',\n nextWaitingTitle: '去对话回答导师的问题',\n nextWaitingBody: '初诊在对话里进行,请回答;也可以跳过。',\n nextRetryDiagnosisLink: '还没有问题?重试',\n nextWaitingRunningTitle: '导师正在准备初诊问题',\n nextWaitingRunningBody: '导师正在对话里思考,问题出现后回答;也可以跳过。',\n nextDiagnosisFailedTitle: '导师没能完成初诊',\n nextDiagnosisFailedBody: (advice, reason) => `${advice}(原始错误:${reason})`,\n waitingFor: (duration) => `已等待 ${duration}。`,\n durationDays: (value) => `${value} 天`,\n tutorFailureAdvice: (kind) => {\n switch (kind) {\n case 'billing': return '模型服务余额不足。换一个可用模型或充值后再重试。'\n case 'auth': return '模型服务鉴权失败。检查 API Key 或登录状态后再重试。'\n case 'rate-limit': return '模型服务限流了。稍等片刻再重试。'\n case 'network': return '连不上模型服务。检查网络后再重试。'\n case 'context': return '对话上下文对这个模型来说太长了。开一个新对话或换更大上下文的模型后再重试。'\n default: return '导师回合失败了。重试会用当前模型再次唤醒导师,并打开对话。'\n }\n },\n nextRetryDiagnosis: '重试并打开对话',\n nextRouteWaitingTitle: '导师正在准备路线',\n nextRouteWaitingBody: '学习路线方案正在对话中生成,可以先把这一页留着。',\n nextRouteTitle: '生成学习路线',\n nextRouteBody: '路线会根据你的目标、初诊结果和可用时间排出课次。接受之前不会开始任何内容。',\n nextProposalTitle: '学习路线方案已生成',\n nextProposalBody: (version) => `看版本 ${version},接受后开始。`,\n nextAcceptedTitle: '开始第一课',\n nextAcceptedBody: (title) => `从「${title}」开始。`,\n nextStartNextTitle: '开始下一课',\n nextStartNextBody: (done, total, title) => `已完成 ${done}/${total},下一课「${title}」。`,\n nextStartNextAction: '开始下一课',\n nextCompleteTitle: '这条路线已完成',\n nextCompleteBody: '所有课次都已有完成的尝试。可以让导师决定接下来学什么。',\n routeEffort: (lessons, duration) => `${lessons} 课 · ${duration}`,\n routeWeeks: (weeksMin, weeksMax) => `≈${weeksMin}–${weeksMax} 周`,\n routeSessionMismatch: (count, sessionMinutes) => `${count} 课超出单次 ${sessionMinutes} 分钟`,\n routeProgress: (completed, total) => `${completed}/${total} 已完成`,\n nextContinueTitle: '继续当前课次',\n nextContinueBody: (title) => `当前课次:「${title}」。`,\n nextSetGoal: '设置目标与时间',\n nextRequestDiagnosis: '请求初诊',\n nextRequestRoute: '请求路线方案',\n nextAcceptVersion: (version) => `接受版本 ${version}`,\n nextStartFirst: '开始第一课',\n nextOpenLesson: '继续这一课',\n projectOverview: '项目总览',\n goalAndTimeSettings: '目标与时间设置',\n readOnlyNotice: '仅供浏览。请打开已选中此项目的对话,以保存目标、请求导师、接受学习路线或开始课次。',\n updatesPaused: '更新已暂停:',\n couldNotLoadPath: '无法加载此学习路线:',\n retryLoading: '重新加载',\n requestSavedTutorUnreached: '请求已保存,但未能联系到导师:',\n previousRequestFailed: (kind) => `之前的${kind === 'plan' ? '学习路线' : '初诊'}请求未能送达导师:`,\n retryTutorRequest: '重试导师请求',\n retryTutorHelp: '仅当你点击时,重试才会发送新操作。',\n loadingPath: '正在加载已保存的学习路线…',\n unsavedKept: '你尚未保存的目标修改会保留在此页面上。',\n returnToDraft: '返回目标草稿',\n noReason: '未提供原因。',\n tutorUnreachable: '无法联系到导师。',\n staleProjectError: '此项目在另一个对话中发生了更改。已刷新到最新版本;你尚未保存的目标修改已保留。请查看后再次点击你的操作。',\n requestFailed: '无法完成此请求。',\n\n // Status vocabularies.\n attemptStatus: (status) => {\n switch (status) {\n case 'requested': return '已请求'\n case 'working': return '进行中'\n case 'completed': return '已完成'\n case 'interrupted': return '已中断'\n default: return status\n }\n },\n assessmentStatus: (status) => {\n switch (status) {\n case 'in-progress': return '进行中'\n case 'ready': return '已就绪'\n case 'skipped': return '已跳过'\n default: return status\n }\n },\n nodeState: (state) => {\n switch (state) {\n case 'unconfirmed': return '未确认'\n case 'explained': return '已讲解'\n case 'practiced': return '已练习'\n case 'checked': return '已检查'\n case 'weak': return '薄弱'\n case 'blocked': return '受阻'\n case 'confirmed': return '已确认'\n default: return state\n }\n },\n edgeKind: (kind) => kind === 'required' ? '必修' : '选修',\n statusPlanned: '已计划 · 可查看课次概览',\n statusCurrent: (status) => `当前 · ${status}`,\n statusCompletedAttempt: '已完成尝试',\n statusInterruptedAttempt: '已中断尝试',\n currentAttemptSuffix: ' · 当前尝试',\n\n // Goal-brief validation.\n wholeNumber: (label, min, max) => `${label}必须是从 ${min} 到 ${max} 的整数,或留空。`,\n invalidTimeZone: '请输入有效的 IANA 时区,例如 America/New_York 或 UTC。',\n timeZoneRequired: '保存前请选择时区。',\n invalidTargetDate: '请选择有效的目标日期,或选择“没有截止日期”。',\n durationRequired: '请输入持续周数,或选择“没有截止日期”。',\n outcomeLimits: '最多 20 个学习成果,每个不超过 2,000 个字符。',\n\n // Durations and small shared fragments.\n durationMinutes: (value) => `${value} 分钟`,\n durationHours: (value) => `${value} 小时`,\n notRecorded: '尚未记录。',\n evidence: (index) => `证据 ${index}`,\n\n // Goal wizard.\n wizardLabel: '学习目标向导',\n wizardEyebrow: '你的目标与时间',\n wizardTitle: '围绕你想做的事构建学习路线',\n closeWizard: '关闭向导',\n wizardIntro: '可在任一步骤保存。目的、学习成果、背景、时间预算和偏好都可以稍后再补。',\n wizardRecovered: '已从此项目恢复保存的目标草稿。',\n wizardStepsLabel: '目标向导步骤',\n wizardSteps: ['目标', '背景', '时间', '偏好'],\n purposeLabel: '你打算用它做什么?',\n purposePlaceholder: '工作项目、考试、面试,或你一直想弄明白的某个问题…',\n purposeHelp: '说明你的情况和它为什么重要。也可以稍后再决定。',\n outcomesLabel: '你希望能做到什么?',\n outcomesPlaceholder: '每行一个可观察的学习成果,例如实现一个模型并解释它的误差。',\n outcomesHelp: '描述你可以解释、解决、构建或应用的内容。草稿阶段留空也可以。',\n backgroundLabel: '你已经学过或尝试过什么?',\n backgroundPlaceholder: '哪些内容比较熟悉?哪些地方困难或不确定?',\n backgroundHelp: '这是你的自述。导师会将它与观察到的事实证据区分开。',\n targetTimeLabel: '目标时间',\n timeNone: '没有截止日期 / 尚未确定',\n timeDate: '指定目标日期',\n timeWeeks: '指定周数',\n timeZoneLabel: '时区',\n timeZonePlaceholder: 'America/New_York',\n timeZoneHelp: 'IANA 时区;会与你的日期一起保存。',\n targetDateLabel: '目标日期',\n durationWeeksLabel: '持续周数',\n durationWeeksPlaceholder: '例如 8',\n deadlineLabel: '这个时间有多严格?',\n deadlineFlexible: '灵活目标',\n deadlineFixed: '固定截止日期',\n weeklyMinutesField: '每周可用分钟数',\n weeklyMinutesErrorLabel: '每周分钟数',\n notSureYet: '尚未确定',\n weeklyHelp: '请包含练习和复习。',\n sessionMinutesField: '每次倾向的分钟数',\n sessionMinutesErrorLabel: '每次分钟数',\n preferencesLabel: '材料、语言、范围和学习偏好',\n preferencesPlaceholder: '希望包含的主题、使用的教材、偏好语言,或你想要的练习类型…',\n back: '上一步',\n next: '下一步',\n saving: '保存中…',\n saveDraft: '保存草稿',\n saveAndClose: '保存并关闭',\n unsavedChanges: '有未保存的更改',\n savedToProject: '已保存到此项目',\n draftNotSaved: '草稿尚未保存',\n saveReadOnly: '请打开已选中此项目的对话以保存目标。',\n\n // Initial diagnosis.\n initialDiagnosis: '初诊',\n assessmentNotRequested: '未请求',\n assessmentSkipped: '你选择了跳过初诊。部分基础仍未验证;学习路线应让这些假设保持可见。',\n strengths: '优势',\n gaps: '差距',\n uncertainties: '不确定之处',\n noStrengths: '尚无优势记录。',\n noGaps: '尚无差距记录。',\n noUncertainties: '尚无不确定事项记录。',\n evidenceReferences: '证据引用',\n assessmentHelp: '导师会根据你的回答和已有证据选择有用的起点。在核实之前,背景说明只是你的自述。',\n diagnosisWaiting: '已请求初诊。正在等待对话中的导师。',\n diagnosisPendingTitle: '初诊进行中',\n diagnosisPendingBody: '导师已经在对话里向你提问或布置了检查题。请在对话中回答;回答被记录之后,初诊和知识地图才会更新。',\n diagnosisDoneSummary: '初诊摘要',\n routeSettingsSummary: '路线版本与设置',\n noCurriculumDiagnosis: '初诊完成后,这里才会显示学习路线。请先在对话里完成初诊,或选择跳过。',\n noCurriculumRoute: '初诊已完成。用上方的「下一步」请求学习路线;导师提出后这里会显示地图。',\n diagnosisNeedsGoal: '请求初诊前,请先保存目的和至少一个可观察的学习成果。',\n diagnosisNeedsSave: '请求导师前,请先保存目标修改。',\n requestDiagnosisAgain: '再次请求初诊',\n requestNewDiagnosis: '请求新的初诊',\n requestDiagnosis: '请求初诊',\n skipAssessment: '跳过初诊',\n assessmentSkippedNotice: '已明确跳过初诊。学习路线将保留尚未验证的假设。',\n assessmentStale: '先前的初诊属于更早的目标草稿。请为当前目标请求新的初诊,或明确跳过。',\n\n // Route canvas and ordered list.\n routeCanvasLabel: '学习路线与课次列表',\n searchLessonsLabel: '搜索课次',\n searchPlaceholder: '搜索课次或主题',\n zoomOutLabel: '缩小学习路线画布',\n zoomInLabel: '放大学习路线画布',\n fitRoute: '适应整条路线',\n currentLesson: '当前课次',\n canvasHelp: '拖动画布可平移。聚焦画布后,用方向键平移,用加号或减号缩放,或按 Home 键适应整条路线。下方按顺序列出了全部课次。',\n canvasLabel: '学习路线画布',\n edgesLabel: '有向先修关系;实线表示必修,虚线表示选修',\n edgeTitle: (kind, from, to) => `${kind}先修:${from} → ${to}`,\n expand: '展开',\n collapse: '收起',\n moduleToggle: (collapsed, title) => `${collapsed ? '展开' : '收起'}模块 ${title}`,\n overviewLesson: (index, title) => `课次概览(第 ${index} 课):${title}`,\n requiredPrerequisite: '必修先修 →',\n optionalPrerequisite: '选修先修 →',\n lessonOrderLegend: '数字表示推荐的课次顺序',\n expandAllModules: '展开全部模块',\n collapseAllModules: '收起全部模块',\n allOverviews: '全部课次概览',\n allOverviewsHelp: '可浏览任意课次。打开课次概览不会改变当前课次。',\n matchingLessons: (count) => `找到 ${count} 个匹配课次。列表中也包含已收起模块内的结果。`,\n lessonListLabel: '课次列表',\n browseLesson: (index, title) => `浏览第 ${index} 课:${title}`,\n\n // Attempt history.\n historyLabel: '课次尝试历史',\n historyTitle: '课次尝试与已保存的教学单元',\n noAttempts: '本次对话中尚无课次尝试记录。',\n savedUnits: (count) => `${count} 个已保存单元`,\n attemptLabel: '尝试',\n attemptWord: '尝试',\n lessonLabel: '课次',\n knowledgeLabel: '知识',\n tutorNotReached: '未能联系到导师:',\n snapshot: (id) => `快照 ${id}`,\n savedAt: '保存于',\n lessonReference: '课次引用',\n nodeLabel: '节点',\n\n // Lesson detail.\n lessonOf: (index, total) => `第 ${index} 课,共 ${total} 课`,\n version: (version) => `版本 ${version}`,\n routeVersion: (version) => `学习路线版本 ${version}`,\n lessonIdLabel: '课次 ID:',\n estimatedEffortTitle: '预估用时',\n estimatedEffortLabel: '预估用时',\n learnOutcomes: '你应能做到什么',\n lessonOutline: '课次大纲',\n practice: '练习',\n assessmentHeading: '评估',\n prerequisites: '先修',\n noPrerequisites: '未指定先修课次。',\n somePrerequisitesIncomplete: '部分先修课次还没有已完成的尝试。你可以自由浏览;如果希望从这里开始,可以请导师做一次简短检查。',\n linkedKnowledge: '关联知识',\n notDiagnosed: '尚未诊断',\n noLinkedNodes: '尚未关联已诊断的知识节点。',\n plannedTopics: (topics) => `计划主题:${topics}。计划一个主题并不代表已确认其知识状态。`,\n overviewBeforeTeaching: '本课次概览在教学内容生成前即可查看。教学单元会在你开始课次后写入。',\n acceptToStart: '接受此学习路线后即可开始其课次。你仍然可以浏览所有课次概览。',\n startReadOnly: '请打开已选中此项目的对话,以开始或复习课次。',\n continueLesson: '继续课次',\n startLesson: '开始课次',\n reviewLesson: '复习课次',\n previousOverview: '上一课次概览',\n nextOverview: '下一课次概览',\n\n // Module detail.\n moduleOverview: (version) => `模块总览 · 版本 ${version}`,\n lessons: (count) => `${count} 个课次`,\n moduleOutcomes: '模块学习成果',\n milestoneAssessment: '里程碑评估',\n crossModulePrerequisites: '来自其他模块的先修课次',\n noCrossModulePrerequisites: '未指定跨模块的先修课次。',\n lessonOverviews: '课次概览',\n\n // Project detail.\n projectOverviewVersion: (version) => `项目总览 · 版本 ${version}`,\n acceptedRoute: '已接受的学习路线',\n previouslyAcceptedRoute: '此前已接受的学习路线',\n draftRoute: '学习路线草稿',\n plannedLessons: '计划课次',\n completedAttemptLessons: '有已完成尝试的课次',\n intendedOutcomes: '你期望的学习成果',\n timePlan: (brief) => [\n brief?.targetDate\n ? `目标日期 ${brief.targetDate}(${brief.timeZone});截止日期${brief.deadline === 'fixed' ? '固定' : '灵活'}。`\n : brief?.durationWeeks\n ? `${brief.durationWeeks} 周;目标${brief.deadline === 'fixed' ? '固定' : '灵活'}。`\n : '未选择截止日期。',\n brief?.weeklyMinutes ? `每周 ${brief.weeklyMinutes} 分钟。` : '未指定每周时间预算。',\n brief?.sessionMinutes ? `希望每次 ${brief.sessionMinutes} 分钟。` : '',\n ].filter(Boolean).join(''),\n timePlanHeading: '时间计划',\n planningAssumptions: '规划假设',\n noAssumptions: '尚无规划假设记录。',\n whyVersion: '提出此版本的原因',\n modulesAndMilestones: '模块与里程碑',\n browseFirstLesson: '浏览第一课',\n browseCurrentLesson: '浏览当前课次',\n completedNote: '已完成的尝试描述的是已记录的活动。知识状态取决于证据;预估用时并不承诺掌握。',\n\n // Pinned lesson card.\n pinnedLessonLabel: '当前固定课次',\n pinnedAttemptEyebrow: '本对话固定的尝试',\n newerAcceptedRoute: (accepted, attempt) => `已接受的学习路线现在是版本 ${accepted}。在你明确从其他路线开始课次之前,本对话仍停留在版本 ${attempt}。`,\n viewPinnedOverview: '查看固定的课次概览',\n browseAcceptedVersion: (version) => `浏览已接受版本 ${version}`,\n openCurrentConversation: '打开当前对话',\n pinnedRequestFailed: '固定课次的请求未能送达导师:',\n retryCurrentLesson: '重试当前课次请求',\n\n // Knowledge reference.\n knowledgeReferenceLabel: '关联知识证据参考',\n closeKnowledgeReference: '关闭知识参考',\n evidenceEntries: (count) => `${count} 条已记录证据`,\n knowledgeIdLabel: '知识 ID:',\n knowledgeIdHelp: '。打开“知识”标签页查看已记录的观察。仅凭课程大纲中的一个主题不能确认理解。',\n\n // Route proposals.\n routeProposalsLabel: '学习路线建议',\n routeVersions: '学习路线版本',\n buildRoute: '构建你的课程学习路线',\n waitingProposal: '正在等待导师建议',\n noVersionsHelp: '先保存目标,然后完成初诊或明确跳过。导师可以先提出包含每个课次概览的完整大纲,之后才会写入课次正文。',\n needsAssessmentHelp: '当前目标需要完成初诊或明确跳过后,才能请求方案。',\n routeToBrowse: '要浏览的学习路线',\n routeVersionToBrowse: '要浏览的学习路线版本',\n versionOption: (version, accepted, previouslyAccepted, title) => `版本 ${version} · ${accepted ? '已接受' : previouslyAccepted ? '此前已接受' : '草稿'} · ${title}`,\n requestPlanAgain: '再次请求方案',\n requestRevisedPlan: '请求修订方案',\n requestRouteProposal: '请求学习路线方案',\n acceptRouteVersion: (version) => `接受学习路线版本 ${version}`,\n routeAcceptedNotice: (version) => `已接受学习路线版本 ${version}。准备好后请明确开始课次。`,\n routeAcceptedHelp: '此学习路线已被接受。是否开始课次仍由你决定。',\n routeProposalHelp: '浏览方案不会接受它,也不会开始课次。',\n routeRevisionWarning: ' 此版本基于更早的目标或初诊;接受前请先请求修订。',\n newerDraftHelp: '版本选择器中有更新的草稿。你当前的视图和尝试已保留。',\n\n // Effort and capacity.\n capacityLabel: '预估用时与可用时间',\n capacityTitle: (range) => `预估用时:${range}`,\n capacityNeedBudget: '添加每周时间预算,以及目标日期或时长,以便比较可用时间。',\n capacityAvailable: (minutes) => `在你选择的时间范围内,可用时间预算约为 ${minutes}。`,\n capacityNote: '估算包含计划课次;仅凭时间并不能确认掌握。',\n conflictMinimum: '最低预估用时超过你的可用时间。',\n conflictMaximum: '最高预估用时超过你的可用时间。',\n conflictAdvice: '你可以调整范围、延长时间线、增加可用时间,或请导师给出调整后的方案。',\n editGoalScope: '编辑目标或范围',\n editTimeBudget: '编辑时间预算',\n requestAdjustedPlan: '请求调整后的方案',\n\n // Empty route and shared actions.\n routeEmptyEyebrow: '你的学习路线将显示在这里',\n tutorPreparing: '导师正在准备方案',\n noCurriculum: '尚未提出课程大纲',\n routeEmptyHelp: '已有知识和教学内容仍可在“知识”标签页和对话中查看。',\n openConversation: '打开对话',\n askNeedsGoal: '请先保存你的目的和可观察的学习成果,再请求导师。',\n askNeedsAssessment: '请先完成已保存目标的评估,或明确跳过,再请求方案。',\n requestSent: (kind) => `${kind === 'plan' ? '学习路线方案' : '初诊'}请求已发送到对话中的导师。`,\n lessonRequested: '已在对话中请求课次。',\n goalSaved: '目标草稿已保存。准备好后可以请求初诊。',\n draftSaved: '未完成的目标草稿已保存。准备好后请回来补充目的和可观察的学习成果。',\n}\n\n/** The copy object every component and helper on the path page reads. */\nexport type PathText = typeof en\n\n/** The path page's copy in the learner's current interface language. */\nexport function usePathText(): typeof en {\n return useLang() === 'zh' ? zh : en\n}\n","/**\n * Route vs. budget fit, computed from the plan and the saved goal brief.\n *\n * The tutor proposes lesson lengths; the runtime can only compare them with\n * what the learner said they can do in one sitting and in one week. This is a\n * display/advice calculation — never an automatic trim.\n */\n\nimport type { Curriculum, GoalBrief } from '../path.js'\n\n/** One lesson longer than the learner's preferred session. */\nexport interface LongLesson {\n readonly id: string\n readonly title: string\n readonly max: number\n}\n\n/** The plan's effort compared with the goal brief. */\nexport interface RouteFit {\n readonly lessons: number\n readonly totalMin: number\n readonly totalMax: number\n /** Null when the brief did not state a session length. */\n readonly sessionMinutes: number | null\n /** Null when the brief did not state a weekly budget. */\n readonly weeklyMinutes: number | null\n /** Whole weeks at the weekly budget, one decimal; null without a budget. */\n readonly weeksMin: number | null\n readonly weeksMax: number | null\n /** Lessons whose longest estimate exceeds the session preference. */\n readonly longLessons: readonly LongLesson[]\n}\n\n/**\n * Compare one plan with the saved goal brief.\n *\n * @param plan - the selected curriculum.\n * @param brief - the saved goal brief, when there is one.\n * @returns effort, weekly duration and lessons that do not fit a session.\n */\nexport function routeFit(plan: Curriculum, brief: GoalBrief | null): RouteFit {\n const totalMin = plan.items.reduce((sum, item) => sum + item.minutes.min, 0)\n const totalMax = plan.items.reduce((sum, item) => sum + item.minutes.max, 0)\n const sessionMinutes = brief?.sessionMinutes ?? null\n const weeklyMinutes = brief?.weeklyMinutes ?? null\n return {\n lessons: plan.items.length,\n totalMin,\n totalMax,\n sessionMinutes,\n weeklyMinutes,\n weeksMin: weeklyMinutes === null || weeklyMinutes <= 0 ? null : Number((totalMin / weeklyMinutes).toFixed(1)),\n weeksMax: weeklyMinutes === null || weeklyMinutes <= 0 ? null : Number((totalMax / weeklyMinutes).toFixed(1)),\n longLessons: sessionMinutes === null || sessionMinutes <= 0\n ? []\n : plan.items\n .filter((item) => item.minutes.max > sessionMinutes)\n .map((item) => ({ id: item.id, title: item.title, max: item.minutes.max })),\n }\n}\n","/**\n * Turn provider errors into an actionable category for the learner.\n *\n * A raw provider message (\"403 insufficient balance\", \"429\", \"invalid api\n * key\") is truthful but unhelpful. The classifier only reads the message; the\n * localized advice lives in path-text so both languages stay together.\n */\n\n/** The failure buckets the route page can explain. */\nexport type TutorErrorKind = 'billing' | 'auth' | 'rate-limit' | 'network' | 'context' | 'other'\n\nconst PATTERNS: readonly { kind: TutorErrorKind; pattern: RegExp }[] = [\n { kind: 'billing', pattern: /insufficient balance|billing|payment required|quota exceeded|credit/i },\n { kind: 'auth', pattern: /invalid api key|unauthorized|authentication|invalid token|401|403|forbidden|permission denied|sign in|sign-in/i },\n { kind: 'rate-limit', pattern: /rate limit|rate-limit|429|too many requests|overloaded/i },\n { kind: 'network', pattern: /network|fetch failed|econn|etimedout|timed out|timeout|connection|dns|socket/i },\n { kind: 'context', pattern: /context overflow|context length|token limit|too long|maximum context|compaction/i },\n]\n\n/**\n * Classify one raw tutor-turn error.\n *\n * @param raw - provider error message as recorded.\n * @returns the most specific bucket, or `other`.\n */\nexport function classifyTutorError(raw: string): TutorErrorKind {\n for (const entry of PATTERNS) if (entry.pattern.test(raw)) return entry.kind\n return 'other'\n}\n","/** Curriculum browsing and explicit learner actions; never selects a project. */\nimport { Fragment, useCallback, useEffect, useRef, useState, type FormEvent, type ReactNode } from 'react'\nimport type { NodeView, TaskView } from '../contract.js'\nimport type { Assessment, CourseAttempt, Curriculum, CurriculumItem, GoalBrief, PathView } from '../path.js'\nimport { Icon } from './design-icons.jsx'\nimport { useDesignText } from './design-text.js'\nimport { announcePathState } from './path-events.js'\nimport { TodayCard } from './today.jsx'\nimport type { DesignText } from './design-text.js'\nimport { useLang } from './i18n.js'\nimport type { Lang } from './i18n.js'\nimport { defaultPathClient, PathClientError } from './path-client.js'\nimport type { PathClient, PathWriteParams, PromptedPathView } from './path-client.js'\nimport { usePathText } from './path-text.js'\nimport type { PathText } from './path-text.js'\nimport { routeFit } from './path-fit.js'\nimport type { RouteFit } from './path-fit.js'\nimport { classifyTutorError } from './tutor-error.js'\nimport type { SessionActivity } from './use-session-activity.js'\n\nexport interface PathPageProps {\n courseId: string\n title: string\n nodes: NodeView[]\n sessionId?: string | undefined\n onOpenChat?: (() => void) | undefined\n onStarted?: (() => void) | undefined\n onOpenLesson?: (() => void) | undefined\n onOpenEvidence?: (() => void) | undefined\n sessionActivity?: SessionActivity | undefined\n client?: PathClient | undefined\n tasks?: readonly TaskView[] | undefined\n}\n\ntype GoalDraft = {\n purpose: string; outcomes: string; background: string; timeZone: string\n targetDate: string; durationWeeks: string; weeklyMinutes: string; sessionMinutes: string; preferences: string\n}\ntype PromptIssue = { kind: 'diagnosis' | 'plan'; message: string } | {\n kind: 'start'; message: string; version: number; itemId: string; review: boolean; resume?: boolean; checkin?: boolean\n}\n\nconst WEEKLY_OPTIONS = [\n { value: '', zh: '未设置', en: 'Not set' },\n { value: '120', zh: '1–3 小时', en: '1–3 hours' },\n { value: '300', zh: '3–5 小时', en: '3–5 hours' },\n { value: '480', zh: '5–8 小时', en: '5–8 hours' },\n { value: '720', zh: '8–12 小时', en: '8–12 hours' },\n { value: '900', zh: '12 小时以上', en: '12+ hours' },\n]\nconst EMPTY_TASKS: readonly TaskView[] = []\n\nconst SESSION_OPTIONS = [\n { value: '', zh: '未设置', en: 'Not set' },\n { value: '30', zh: '15–30 分钟', en: '15–30 min' },\n { value: '45', zh: '30–60 分钟', en: '30–60 min' },\n { value: '75', zh: '60–90 分钟', en: '60–90 min' },\n { value: '105', zh: '90 分钟以上', en: '90+ min' },\n]\n\nfunction initialZone(): string {\n try { return Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC' } catch { return 'UTC' }\n}\nfunction draftFromBrief(brief: GoalBrief | null): GoalDraft {\n return {\n purpose: brief?.purpose ?? '', outcomes: brief?.outcomes.join('\\n') ?? '', background: brief?.background ?? '',\n timeZone: brief?.timeZone ?? initialZone(), targetDate: brief?.targetDate ?? '',\n durationWeeks: brief?.durationWeeks?.toString() ?? '',\n weeklyMinutes: brief?.weeklyMinutes?.toString() ?? '', sessionMinutes: brief?.sessionMinutes?.toString() ?? '',\n preferences: brief?.preferences ?? '',\n }\n}\nfunction numericDraft(value: string, label: string, min: number, max: number, text: PathText): number | null {\n if (!value.trim()) return null\n const number = Number(value)\n if (!Number.isInteger(number) || number < min || number > max) throw new Error(text.wholeNumber(label, min, max))\n return number\n}\nfunction briefFromDraft(draft: GoalDraft, text: PathText): GoalBrief {\n const timeZone = draft.timeZone.trim()\n try { new Intl.DateTimeFormat('en', { timeZone }).format() } catch { throw new Error(text.invalidTimeZone) }\n if (!timeZone) throw new Error(text.timeZoneRequired)\n const targetDate = draft.targetDate || null\n const durationWeeks = draft.durationWeeks ? numericDraft(draft.durationWeeks, text.durationWeeksLabel, 1, 260, text) : null\n if (targetDate !== null && durationWeeks !== null) throw new Error(text.invalidTargetDate)\n if (draft.targetDate !== '' && (!/^\\d{4}-\\d{2}-\\d{2}$/.test(draft.targetDate) || Number.isNaN(Date.parse(`${draft.targetDate}T12:00:00Z`)) || new Date(`${draft.targetDate}T12:00:00Z`).toISOString().slice(0, 10) !== draft.targetDate)) {\n throw new Error(text.invalidTargetDate)\n }\n const outcomes = draft.outcomes.split('\\n').map((line) => line.trim()).filter(Boolean)\n if (outcomes.length > 20 || outcomes.some((line) => line.length > 2000)) throw new Error(text.outcomeLimits)\n return {\n purpose: draft.purpose, outcomes, background: draft.background, timeZone, targetDate, durationWeeks,\n deadline: targetDate === null && durationWeeks === null ? 'none' : 'flexible',\n weeklyMinutes: numericDraft(draft.weeklyMinutes, text.weeklyMinutesErrorLabel, 15, 10080, text),\n sessionMinutes: numericDraft(draft.sessionMinutes, text.sessionMinutesErrorLabel, 5, 480, text), preferences: draft.preferences,\n }\n}\nfunction ready(brief: GoalBrief | null): boolean { return !!brief?.purpose.trim() && !!brief.outcomes.length }\nfunction operationId(): string {\n if (typeof globalThis.crypto?.randomUUID === 'function') return globalThis.crypto.randomUUID()\n return `path-${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`\n}\nfunction messageOf(error: unknown, text: PathText): string { return error instanceof Error ? error.message : text.requestFailed }\nfunction duration(value: number, lang: Lang): string {\n if (value < 120) return lang === 'zh' ? `${value} 分钟` : `${value} min`\n const hours = Number((value / 60).toFixed(1))\n return lang === 'zh' ? `${hours} 小时` : `${hours} h`\n}\nfunction range(value: { min: number; max: number }, lang: Lang): string { return `${duration(value.min, lang)}–${duration(value.max, lang)}` }\nfunction waitDuration(value: number, lang: Lang): string {\n if (value < 60) return duration(value, lang)\n if (value < 60 * 48) return lang === 'zh' ? `${Number((value / 60).toFixed(1))} 小时` : `${Number((value / 60).toFixed(1))} h`\n return lang === 'zh' ? `${Number((value / 1440).toFixed(1))} 天` : `${Number((value / 1440).toFixed(1))} d`\n}\nfunction when(value: string, lang: Lang): string {\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toLocaleString(lang === 'zh' ? 'zh-CN' : undefined)\n}\nfunction latestAttempt(attempts: readonly CourseAttempt[], version: number, itemId: string): CourseAttempt | undefined {\n return [...attempts].reverse().find((attempt) => attempt.version === version && attempt.itemId === itemId)\n}\ntype LessonState = 'planned' | 'current' | 'completed' | 'interrupted'\nfunction lessonState(attempts: readonly CourseAttempt[], activeId: string | null, version: number, itemId: string): LessonState {\n const attempt = latestAttempt(attempts, version, itemId)\n if (attempt === undefined) return 'planned'\n if (attempt.id === activeId && attempt.status !== 'completed' && attempt.status !== 'interrupted') return 'current'\n if (attempt.status === 'completed') return 'completed'\n if (attempt.status === 'interrupted') return 'interrupted'\n return 'current'\n}\nfunction lessonStateLabel(state: LessonState, text: DesignText): string {\n if (state === 'completed') return text.lessonStatusCompleted\n if (state === 'current') return text.lessonStatusCurrent\n return text.lessonStatusPlanned\n}\nfunction attemptDone(attempts: readonly CourseAttempt[], version: number): Set<string> {\n return new Set(attempts.filter((attempt) => attempt.version === version && attempt.status === 'completed').map((attempt) => attempt.itemId))\n}\nfunction codeFor(plan: Curriculum, item: CurriculumItem): string {\n const moduleIndex = plan.modules.findIndex((module) => module.id === item.moduleId)\n const within = plan.items.filter((entry) => entry.moduleId === item.moduleId).findIndex((entry) => entry.id === item.id)\n return `${moduleIndex + 1}.${within + 1}`\n}\n\nfunction Button({ children, primary, ...props }: React.ButtonHTMLAttributes<HTMLButtonElement> & { primary?: boolean }): ReactNode {\n return <button type=\"button\" className={primary ? 'dtx-primary' : 'dtx-secondary'} {...props}>{children}</button>\n}\n\nfunction StageBar({ stage, text }: { stage: 'goal' | 'diagnosis' | 'route' | 'learning'; text: DesignText }): ReactNode {\n const index = ['goal', 'diagnosis', 'route', 'learning'].indexOf(stage)\n return (\n <nav className=\"dtx-stagebar\" aria-label={text.stages.join(' · ')}>\n {text.stages.map((label, position) => {\n const state = position < index ? 'done' : position === index ? 'current' : 'pending'\n return (\n <Fragment key={label}>\n <div className=\"dtx-stage\" data-state={state}>\n <span className=\"dtx-stage-num\">{state === 'done' ? <Icon name=\"check\" size={15} /> : position + 1}</span>\n <span className=\"dtx-stage-label\">{label}</span>\n </div>\n {position < text.stages.length - 1 && <span className=\"dtx-stage-line\" />}\n </Fragment>\n )\n })}\n </nav>\n )\n}\n\nfunction NextCard({ kind, title, body, note, primary, secondary, tertiary, children, layout }: {\n kind?: 'waiting' | 'error' | 'version' | 'complete' | 'proposal' | undefined\n title: string\n body?: string\n note?: string | undefined\n primary?: { label: string; run: () => void; disabled?: boolean } | undefined\n secondary?: { label: string; run: () => void; disabled?: boolean } | undefined\n tertiary?: { label: string; run: () => void; disabled?: boolean } | undefined\n children?: ReactNode\n layout?: 'row' | 'stack'\n}): ReactNode {\n const text = useDesignText()\n const boxed = kind === 'waiting' || kind === 'error'\n return (\n <section className=\"dtx-next\" data-kind={kind ?? 'normal'} data-layout={layout ?? 'row'} aria-label={text.nextStep}>\n {kind === 'complete' && <span className=\"dtx-complete-icon\"><Icon name=\"check\" size={24} /></span>}\n <div className=\"dtx-next-copy\">\n <p className=\"dtx-next-eyebrow\">{text.nextStep}</p>\n <h2>{title}</h2>\n {body !== undefined && !boxed && <p className=\"dtx-next-body\">{body}</p>}\n {note !== undefined && <p className=\"dtx-next-note\">{note}</p>}\n {kind === 'waiting' && <div className=\"dtx-wait-box\">\n <span className=\"dtx-spinner\" />\n <span><span className=\"dtx-wait-title\">{body ?? text.waitingTutor}</span><span className=\"dtx-wait-sub\">{note ?? text.waitingTutorHint}</span></span>\n </div>}\n {kind === 'error' && <div className=\"dtx-error-box\">\n <span className=\"dtx-error-title\"><Icon name=\"warning\" size={16} />{title}</span>\n <span className=\"dtx-error-body\">{body ?? text.errorOther}</span>\n </div>}\n {children}\n </div>\n {(primary !== undefined || secondary !== undefined || tertiary !== undefined) && <div className=\"dtx-next-actions\">\n {primary !== undefined && <button type=\"button\" className=\"dtx-primary\" disabled={primary.disabled} onClick={primary.run}>{primary.label}</button>}\n {secondary !== undefined && <button type=\"button\" className=\"dtx-secondary\" disabled={secondary.disabled} onClick={secondary.run}>{secondary.label}</button>}\n {tertiary !== undefined && <button type=\"button\" className=\"dtx-link\" disabled={tertiary.disabled} onClick={tertiary.run}>{tertiary.label}</button>}\n </div>}\n </section>\n )\n}\n\nfunction GoalForm({ draft, dirty, busy, readOnly, onChange, onSave, text, lang }: {\n draft: GoalDraft; dirty: boolean; busy: boolean; readOnly: boolean\n onChange: (field: keyof GoalDraft, value: string) => void\n onSave: () => void\n text: DesignText\n lang: Lang\n}): ReactNode {\n const field = (name: keyof GoalDraft) => ({\n value: draft[name],\n disabled: readOnly,\n onChange: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => onChange(name, event.target.value),\n })\n const options = (entries: { value: string; zh: string; en: string }[]) => entries.map((entry) => <option key={entry.value} value={entry.value}>{lang === 'zh' ? entry.zh : entry.en}</option>)\n return (\n <form className=\"dtx-card dtx-form-card\" onSubmit={(event: FormEvent) => { event.preventDefault(); onSave() }}>\n <div className=\"dtx-form-head\">\n <div>\n <h2>{text.goalTitle}</h2>\n <p>{text.goalHelp}</p>\n </div>\n <button type=\"submit\" className=\"dtx-primary\" disabled={busy || readOnly}>{text.saveGoal}</button>\n </div>\n {dirty && <p className=\"dtx-small dtx-muted\" style={{ marginTop: 10 }}>{text.unsavedChanges}</p>}\n <div className=\"dtx-form\">\n <div className=\"dtx-field\">\n <label htmlFor=\"dtx-purpose\">{text.fieldPurpose}</label>\n <textarea id=\"dtx-purpose\" className=\"dtx-input\" maxLength={2000} {...field('purpose')} placeholder={text.fieldPurposePlaceholder} />\n <p className=\"dtx-counter\">{text.charCount(draft.purpose.length, 2000)}</p>\n </div>\n <div className=\"dtx-field\">\n <label htmlFor=\"dtx-outcomes\">{text.fieldOutcomes}</label>\n <textarea id=\"dtx-outcomes\" className=\"dtx-input\" {...field('outcomes')} placeholder={text.fieldOutcomesPlaceholder} />\n </div>\n <div className=\"dtx-field\">\n <label htmlFor=\"dtx-background\">{text.fieldBackground}</label>\n <textarea id=\"dtx-background\" className=\"dtx-input\" {...field('background')} placeholder={text.fieldBackgroundPlaceholder} />\n </div>\n <div className=\"dtx-form-grid\">\n <div className=\"dtx-field\">\n <label htmlFor=\"dtx-weekly\">{text.fieldWeekly}</label>\n <select id=\"dtx-weekly\" className=\"dtx-input\" {...field('weeklyMinutes')}>{options(WEEKLY_OPTIONS)}</select>\n </div>\n <div className=\"dtx-field\">\n <label htmlFor=\"dtx-session\">{text.fieldSession}</label>\n <select id=\"dtx-session\" className=\"dtx-input\" {...field('sessionMinutes')}>{options(SESSION_OPTIONS)}</select>\n </div>\n </div>\n <details className=\"dtx-more\">\n <summary>{text.moreSettings}</summary>\n <div className=\"dtx-more-grid\">\n <div className=\"dtx-field\">\n <label htmlFor=\"dtx-target\">{text.fieldTargetDate}</label>\n <input id=\"dtx-target\" type=\"date\" className=\"dtx-input\" {...field('targetDate')} />\n </div>\n <div className=\"dtx-field\">\n <label htmlFor=\"dtx-weeks\">{text.fieldDurationWeeks}</label>\n <input id=\"dtx-weeks\" type=\"number\" min={1} max={260} className=\"dtx-input\" {...field('durationWeeks')} />\n </div>\n <div className=\"dtx-field\">\n <label htmlFor=\"dtx-tz\">{text.fieldTimeZone}</label>\n <input id=\"dtx-tz\" className=\"dtx-input\" {...field('timeZone')} />\n </div>\n <div className=\"dtx-field\">\n <label htmlFor=\"dtx-preferences\">{text.fieldPreferences}</label>\n <input id=\"dtx-preferences\" className=\"dtx-input\" {...field('preferences')} />\n </div>\n </div>\n </details>\n </div>\n </form>\n )\n}\n\nfunction GoalRail({ text }: { text: DesignText }): ReactNode {\n const titles = [text.stageGoalTitle, text.stageDiagnosisTitle, text.stageRouteTitle, text.stageLearningTitle]\n return (\n <>\n <section className=\"dtx-card dtx-rail-card\">\n <p className=\"dtx-rail-eyebrow\">{text.currentStage}</p>\n <h2 className=\"dtx-stage-current-title\">{text.stageGoalTitle}</h2>\n <p className=\"dtx-stage-current-step\">{text.stepOf(1, 4)}</p>\n <div className=\"dtx-stage-steps\">\n {titles.map((title, index) => <div key={title} className=\"dtx-stage-step\" data-current={index === 0 ? 'true' : undefined}>\n <span className=\"dtx-stage-step-num\">{index + 1}</span>\n <span>{title}</span>\n </div>)}\n </div>\n <div className=\"dtx-rail-divider\" />\n <h3 className=\"dtx-rail-section\">{text.stageHelper}</h3>\n {[\n { icon: 'book' as const, title: text.helperMatchTitle, body: text.helperMatchBody },\n { icon: 'clock' as const, title: text.helperPaceTitle, body: text.helperPaceBody },\n { icon: 'target' as const, title: text.helperPracticeTitle, body: text.helperPracticeBody },\n ].map((tip) => <div className=\"dtx-tip\" key={tip.title}>\n <span className=\"dtx-tip-icon\"><Icon name={tip.icon} size={18} /></span>\n <span><span className=\"dtx-tip-title\">{tip.title}</span><span className=\"dtx-tip-body\">{tip.body}</span></span>\n </div>)}\n </section>\n </>\n )\n}\n\nfunction DiagnosisInfo({ text }: { text: DesignText }): ReactNode {\n return (\n <section className=\"dtx-card dtx-info-card\">\n <h3>{text.diagnosisHowTitle}</h3>\n <div className=\"dtx-how-steps\">\n {text.diagnosisHow.map((step, index) => <div className=\"dtx-how-step\" key={step.title}>\n <span className=\"dtx-how-num\">{index + 1}</span>\n <span><span className=\"dtx-how-title\">{step.title}</span><span className=\"dtx-how-body\" style={{ display: 'block' }}>{step.body}</span></span>\n </div>)}\n </div>\n <div className=\"dtx-hint-box\">\n <span className=\"dtx-hint-icon\"><Icon name=\"chat\" size={18} /></span>\n <span><span className=\"dtx-hint-title\">{text.diagnosisHintTitle}</span><span className=\"dtx-hint-body\" style={{ display: 'block' }}>{text.diagnosisHintBody}</span></span>\n </div>\n </section>\n )\n}\n\nfunction EmptyRail({ title, body, action, text }: { title: string; body: string; action?: { label: string; run: () => void } | undefined; text: DesignText }): ReactNode {\n return (\n <section className=\"dtx-card dtx-rail-empty\">\n <span className=\"dtx-rail-empty-icon\"><Icon name=\"book\" size={30} /></span>\n <p className=\"dtx-rail-empty-title\">{title}</p>\n <p className=\"dtx-rail-empty-body\">{body}</p>\n {action !== undefined && <button type=\"button\" className=\"dtx-secondary\" onClick={action.run}>{action.label}</button>}\n <span className=\"dtx-small dtx-muted\" style={{ marginTop: 12 }}>{text.currentLesson}</span>\n </section>\n )\n}\n\nfunction RouteModules({ plan, attempts, activeAttemptId, selectedItemId, onSelect, version, showProgress, text, lang }: {\n plan: Curriculum\n attempts: readonly CourseAttempt[]\n activeAttemptId: string | null\n selectedItemId: string | null\n onSelect: (id: string) => void\n version: number\n showProgress: boolean\n text: DesignText\n lang: Lang\n}): ReactNode {\n const done = attemptDone(attempts, plan.version)\n const modules = plan.modules.map((module) => {\n const items = plan.items.filter((item) => item.moduleId === module.id)\n return { module, items, done: items.length > 0 && items.every((item) => done.has(item.id)) }\n })\n const activeItemId = activeAttemptId === null ? null : attempts.find((attempt) => attempt.id === activeAttemptId)?.itemId ?? null\n const currentModuleId = activeItemId === null ? null : plan.items.find((item) => item.id === activeItemId)?.moduleId ?? null\n const initial = () => new Set(currentModuleId === null ? (modules[0] === undefined ? [] : [modules[0].module.id]) : [currentModuleId])\n const [expanded, setExpanded] = useState<Set<string>>(initial)\n useEffect(() => { setExpanded(initial()) }, [plan.version])\n const allOpen = modules.length > 0 && modules.every((entry) => expanded.has(entry.module.id))\n const toggle = (id: string) => setExpanded((current) => {\n const next = new Set(current)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n return next\n })\n return (\n <section className=\"dtx-card dtx-route-card\">\n <div className=\"dtx-route-head\">\n <h3>{text.routeListTitle}</h3>\n <div className=\"dtx-route-meta\">\n {showProgress ? <><span>{text.sessionProgress(done.size, plan.items.length)}</span><span>·</span><span>{text.routeLabel(version)}</span></> : <span>{text.routeLabel(version)}</span>}\n <button type=\"button\" className=\"dtx-link\" onClick={() => setExpanded(allOpen ? new Set() : new Set(modules.map((entry) => entry.module.id)))}>\n {allOpen ? text.collapseAll : text.expandAll}\n </button>\n </div>\n </div>\n <div className=\"dtx-modules\">\n {modules.map(({ module, items, done: moduleDone }) => {\n const moduleMinutes = items.reduce((sum, item) => sum + item.minutes.min, 0)\n const open = expanded.has(module.id)\n const current = module.id === currentModuleId\n return (\n <div className=\"dtx-module\" data-done={moduleDone ? 'true' : undefined} data-open={open ? 'true' : undefined} data-current={current ? 'true' : undefined} key={module.id}>\n <button type=\"button\" className=\"dtx-module-toggle\" aria-expanded={open} onClick={() => toggle(module.id)}>\n <span className=\"dtx-module-num\">{moduleDone ? <Icon name=\"check\" size={14} /> : plan.modules.findIndex((entry) => entry.id === module.id) + 1}</span>\n <span className=\"dtx-module-main\">\n <span className=\"dtx-module-title\">{module.title}</span>\n {open && <span className=\"dtx-module-desc\">{module.overview}</span>}\n </span>\n <span className=\"dtx-module-meta\">{text.moduleMeta(duration(moduleMinutes, lang), items.length)}</span>\n <span className=\"dtx-module-chevron\"><Icon name=\"chevron-right\" size={15} /></span>\n </button>\n {open && <div className=\"dtx-lessons\">\n {items.map((item) => {\n const state = lessonState(attempts, activeAttemptId, plan.version, item.id)\n return (\n <button type=\"button\" className=\"dtx-lesson\" data-state={state} aria-current={item.id === selectedItemId || undefined} onClick={() => onSelect(item.id)} key={item.id}>\n <span className=\"dtx-lesson-state\">{state === 'completed' ? <Icon name=\"check\" size={11} /> : null}</span>\n <span className=\"dtx-lesson-body\">\n <span className=\"dtx-lesson-top\">\n <span className=\"dtx-lesson-code\">{codeFor(plan, item)}</span>\n <span className=\"dtx-lesson-title\">{item.title}</span>\n <span className=\"dtx-lesson-minutes\">{duration(item.minutes.min, lang)}</span>\n </span>\n <span className=\"dtx-lesson-topics\">{item.topics.slice(0, 3).join('、')}</span>\n </span>\n </button>\n )\n })}\n </div>}\n </div>\n )\n })}\n </div>\n </section>\n )\n}\n\nfunction timeText(minutes: number, text: DesignText, lang: Lang): string {\n return `${text.totalDuration} ${duration(minutes, lang)}`\n}\n\nfunction LessonRail({ plan, item, attempt, activeAttempt, mode, version, text, lang, onStart, onOpenChat, onOpenLesson, readOnly }: {\n plan: Curriculum\n item: CurriculumItem\n attempt: CourseAttempt | undefined\n activeAttempt: CourseAttempt | undefined\n mode: 'preview' | 'current'\n version: number\n text: DesignText\n lang: Lang\n onStart: (() => void) | undefined\n onOpenChat: (() => void) | undefined\n onOpenLesson: (() => void) | undefined\n readOnly: boolean\n}): ReactNode {\n const module = plan.modules.find((entry) => entry.id === item.moduleId)\n const state = lateState(attempt, activeAttempt, item)\n const title = mode === 'current' ? text.currentLesson : text.lessonPreview\n const outline = item.outline.slice(0, 3)\n return (\n <section className=\"dtx-card dtx-rail-card\">\n <div className=\"dtx-rail-head\">\n <p className=\"dtx-rail-eyebrow\">{title}</p>\n </div>\n <h2 className=\"dtx-rail-title\">{item.title}</h2>\n <p className=\"dtx-rail-sub\">{module?.title ?? ''}{mode === 'current' ? ` · ${codeFor(plan, item)}` : ` · ${text.lessonMinutes(duration(item.minutes.min, lang))}`}</p>\n <div className=\"dtx-rail-pills\">\n <span className=\"dtx-pill\" data-tone={state === 'completed' ? 'green' : state === 'current' ? 'blue' : 'gray'}>{lessonStateLabel(state, text)}</span>\n <span className=\"dtx-pill\" data-tone=\"gray\">{text.routeLabel(version)}</span>\n </div>\n <div className=\"dtx-rail-divider\" />\n <h3 className=\"dtx-rail-section\">{text.lessonContents}</h3>\n {outline.map((line, index) => <div className=\"dtx-rail-item\" key={index}>\n <span className=\"dtx-rail-item-icon\"><Icon name={index === 0 ? 'book' : index === 1 ? 'grid' : 'spark'} size={17} /></span>\n <span><span className=\"dtx-rail-item-title\">{line}</span><span className=\"dtx-rail-item-desc\" style={{ display: 'block' }}>{item.topics.slice(index, index + 2).join('、')}</span></span>\n </div>)}\n {mode === 'preview' && <p className=\"dtx-rail-note\">{text.plannedNote}</p>}\n {mode === 'current' && <div className=\"dtx-answer-card\">\n <p className=\"dtx-answer-title\"><Icon name=\"chat\" size={16} />{text.answerInChat}</p>\n <p className=\"dtx-answer-body\">{text.answerInChatBody}</p>\n </div>}\n </section>\n )\n}\nfunction lateState(attempt: CourseAttempt | undefined, activeAttempt: CourseAttempt | undefined, item: CurriculumItem): LessonState {\n if (attempt !== undefined && attempt.id === activeAttempt?.id && attempt.status !== 'completed') return 'current'\n if (attempt?.status === 'completed') return 'completed'\n if (attempt?.status === 'interrupted') return 'interrupted'\n return 'planned'\n}\n\nfunction ProposalCard({ plan, brief, text, lang, busy, readOnly, canRequest, onAccept, onRevise, versions, selectedVersion, onSelectVersion }: {\n plan: Curriculum\n brief: GoalBrief | null\n text: DesignText\n lang: Lang\n busy: boolean\n readOnly: boolean\n canRequest: boolean\n onAccept: () => void\n onRevise: () => void\n versions: number[]\n selectedVersion: number | null\n onSelectVersion: (version: number) => void\n}): ReactNode {\n const fit = routeFit(plan, brief)\n return (\n <section className=\"dtx-proposal\">\n <span className=\"dtx-proposal-icon\"><Icon name=\"route\" size={24} /></span>\n <div className=\"dtx-proposal-copy\">\n <h2>{text.proposalTitle}</h2>\n <p>{text.proposalBody}</p>\n </div>\n <div className=\"dtx-proposal-stats\">\n <span><span className=\"dtx-stat-label\">{text.totalDuration}</span><br /><span className=\"dtx-stat-value\">{duration(fit.totalMin, lang)}–{duration(fit.totalMax, lang)}</span></span>\n {fit.weeklyMinutes !== null && <span><span className=\"dtx-stat-label\">{text.weeklySuggestion}</span><br /><span className=\"dtx-stat-value\">{duration(fit.weeklyMinutes, lang)}</span></span>}\n <span><span className=\"dtx-stat-label\">{text.routeVersion(plan.version)}</span><br /><span className=\"dtx-stat-value\">{text.pendingAccept}</span></span>\n </div>\n <div className=\"dtx-proposal-actions\">\n <button type=\"button\" className=\"dtx-primary\" disabled={busy || readOnly} onClick={onAccept}>{text.acceptRoute}</button>\n <button type=\"button\" className=\"dtx-link\" disabled={!canRequest || busy} onClick={onRevise}>{text.requestRevision}</button>\n {versions.length > 1 && <select className=\"dtx-input\" aria-label={text.routeListTitle} value={selectedVersion ?? plan.version} onChange={(event) => onSelectVersion(Number(event.target.value))} style={{ height: 32, marginTop: 4 }}>\n {versions.map((version) => <option key={version} value={version}>{text.routeVersion(version)}</option>)}\n </select>}\n </div>\n </section>\n )\n}\n\nexport function PathPage(props: PathPageProps): ReactNode {\n const text = useDesignText()\n const pathText = usePathText()\n const lang = useLang()\n return <PathPageBody {...props} designText={text} pathText={pathText} lang={lang} />\n}\n\nfunction PathPageBody({ courseId, title, sessionId, onOpenChat, onStarted, onOpenLesson, onOpenEvidence, sessionActivity, client = defaultPathClient, tasks = EMPTY_TASKS, designText: text, pathText, lang }: PathPageProps & { designText: DesignText; pathText: PathText; lang: Lang }): ReactNode {\n const [view, setView] = useState<PathView | null>(null)\n const [draft, setDraft] = useState<GoalDraft>(() => draftFromBrief(null))\n const [dirty, setDirty] = useState(false)\n const [selectedVersion, setSelectedVersion] = useState<number | null>(null)\n const [selectedItemId, setSelectedItemId] = useState<string | null>(null)\n const [busy, setBusy] = useState<string | null>(null)\n const [error, setError] = useState('')\n const [pollError, setPollError] = useState('')\n const [notice, setNotice] = useState('')\n const [promptIssue, setPromptIssue] = useState<PromptIssue | null>(null)\n const [taskList, setTaskList] = useState<readonly TaskView[]>(tasks)\n useEffect(() => { setTaskList(tasks) }, [tasks])\n const currentView = useRef<PathView | null>(null)\n const dirtyRef = useRef(false)\n const editGeneration = useRef(0)\n const generation = useRef(0)\n const readSequence = useRef(0)\n const reading = useRef(false)\n const mutationEpoch = useRef(0)\n const live = useRef(false)\n const locked = useRef(false)\n const initialized = useRef(false)\n const synchronizedBrief = useRef('')\n const readOnly = !sessionId\n\n const adopt = useCallback((next: PathView) => {\n if (next.path.courseId !== courseId || next.run && (next.run.sessionId !== sessionId || next.run.courseId !== courseId)) return\n const previous = currentView.current\n if (previous && next.path.revision < previous.path.revision) return\n const run = previous?.run && next.run && next.run.revision < previous.run.revision ? previous.run : next.run\n const accepted = { ...next, run }\n currentView.current = accepted\n setView(accepted)\n const serialized = JSON.stringify(next.path.brief)\n if (!dirtyRef.current && synchronizedBrief.current !== serialized) {\n synchronizedBrief.current = serialized\n setDraft(draftFromBrief(next.path.brief))\n }\n if (!initialized.current) {\n initialized.current = true\n const active = next.run?.attempts.find((attempt) => attempt.id === next.run?.activeAttemptId)\n setSelectedVersion(active?.version ?? next.path.acceptedVersion ?? next.path.curricula.at(-1)?.version ?? null)\n } else setSelectedVersion((version) => version ?? next.path.acceptedVersion ?? next.path.curricula.at(-1)?.version ?? null)\n }, [courseId, sessionId])\n\n const refresh = useCallback(async (force = false) => {\n if ((locked.current || reading.current) && !force) return\n const scope = generation.current\n const epoch = mutationEpoch.current\n const sequence = ++readSequence.current\n reading.current = true\n try {\n const next = await client.get(courseId, sessionId)\n if (!live.current || scope !== generation.current || epoch !== mutationEpoch.current || sequence !== readSequence.current) return\n adopt(next)\n setPollError('')\n } catch (failure) {\n if (live.current && scope === generation.current && epoch === mutationEpoch.current && sequence === readSequence.current) setPollError(messageOf(failure, pathText))\n } finally {\n if (scope === generation.current && sequence === readSequence.current) reading.current = false\n }\n }, [adopt, client, courseId, sessionId, pathText])\n useEffect(() => {\n live.current = true\n generation.current += 1\n reading.current = false\n locked.current = false\n setBusy(null)\n void refresh()\n const timer = setInterval(() => { void refresh() }, 3000)\n return () => { live.current = false; generation.current += 1; clearInterval(timer) }\n }, [refresh])\n\n const mutate = async (name: string, task: (params: PathWriteParams) => Promise<PathView>, success?: (result: PathView) => void) => {\n if (locked.current || !sessionId || !currentView.current) return\n locked.current = true\n const scope = generation.current\n mutationEpoch.current += 1\n setBusy(name); setError(''); setNotice(''); setPromptIssue(null)\n try {\n const result = await task({ sessionId, courseId, expectedRevision: currentView.current.path.revision })\n if (!live.current || scope !== generation.current) return\n adopt(result)\n success?.(result)\n } catch (failure) {\n if (!live.current || scope !== generation.current) return\n if (failure instanceof PathClientError && failure.status === 409) {\n await refresh(true)\n if (live.current && scope === generation.current) setError(pathText.staleProjectError)\n } else setError(messageOf(failure, pathText))\n } finally {\n if (live.current && scope === generation.current) { locked.current = false; setBusy(null) }\n }\n }\n const changeDraft = (field: keyof GoalDraft, value: string) => {\n dirtyRef.current = true; editGeneration.current += 1; setDirty(true)\n setDraft((previous) => ({ ...previous, [field]: value }))\n }\n const saveBrief = () => {\n let brief: GoalBrief\n try { brief = briefFromDraft(draft, pathText) } catch (failure) { setError(messageOf(failure, pathText)); return }\n if (!ready(brief)) { setError(text.goalNeedsOutcome); return }\n const edit = editGeneration.current\n void mutate('save', (params) => client.saveBrief({ ...params, brief }), (result) => {\n if (edit === editGeneration.current) {\n dirtyRef.current = false; setDirty(false)\n synchronizedBrief.current = JSON.stringify(result.path.brief)\n setDraft(draftFromBrief(result.path.brief))\n }\n setNotice(ready(result.path.brief) ? text.goalSaved : pathText.draftSaved)\n })\n }\n const ask = (kind: 'diagnosis' | 'plan') => {\n const path = currentView.current?.path\n if (!path || dirtyRef.current || !ready(path.brief)) { setError(pathText.askNeedsGoal); return }\n if (kind === 'plan' && (!path.assessment || path.assessment.briefRevision !== path.briefRevision || !['ready', 'skipped'].includes(path.assessment.status))) { setError(pathText.askNeedsAssessment); return }\n void mutate(`request-${kind}`, (params) => client.request({ ...params, kind, operationId: operationId() }), (result) => {\n const response = result as PromptedPathView\n if (!response.prompted) setPromptIssue({ kind, message: response.promptReason ?? pathText.tutorUnreachable })\n else setNotice(pathText.requestSent(kind))\n })\n }\n const start = (version: number, itemId: string, review: boolean, intent?: 'resume' | 'checkin') => {\n void mutate('start', (params) => client.start({\n ...params, version, itemId, operationId: operationId(),\n ...(review ? { review: true } : {}),\n ...(intent === 'resume' ? { resume: true } : {}),\n ...(intent === 'checkin' ? { resume: true, checkin: true } : {}),\n }), (result) => {\n const response = result as PromptedPathView\n if (!response.prompted) setPromptIssue({\n kind: 'start', version, itemId, review,\n ...(intent === 'resume' ? { resume: true } : {}),\n ...(intent === 'checkin' ? { resume: true, checkin: true } : {}),\n message: response.promptReason ?? pathText.tutorUnreachable,\n })\n else { announcePathState(false); setNotice(pathText.lessonRequested); onStarted?.() }\n })\n }\n const acceptVersion = (version: number) => {\n void mutate('accept', (params) => client.accept({ ...params, version }), () => { setNotice(pathText.routeAcceptedNotice(version)); setSelectedVersion(version) })\n }\n const skipAssessment = () => { void mutate('skip', (params) => client.skipAssessment(params), () => setNotice(pathText.assessmentSkippedNotice)) }\n\n const activeAttempt = view?.run?.attempts.find((attempt) => attempt.id === view.run?.activeAttemptId)\n const savedReady = ready(view?.path.brief ?? null)\n const assessmentCurrent = !!view?.path.assessment && view.path.assessment.briefRevision === view.path.briefRevision\n const assessment = assessmentCurrent ? view?.path.assessment ?? null : null\n const latestDiagnosis = view?.path.requests.findLast((request) => request.kind === 'diagnosis' && request.briefRevision === view.path.briefRevision)\n const latestPlan = view?.path.requests.findLast((request) => request.kind === 'plan' && request.briefRevision === view.path.briefRevision)\n const diagnosisResolved = !!assessment && assessment.status !== 'in-progress' && (!latestDiagnosis || assessment.updatedAt >= latestDiagnosis.createdAt)\n const planResolved = !!latestPlan && !!view?.path.curricula.some((entry) => entry.briefRevision === view.path.briefRevision && entry.assessmentRevision === view.path.assessmentRevision && entry.createdAt >= latestPlan.createdAt)\n const diagnosisWaiting = !!latestDiagnosis?.prompted && !diagnosisResolved\n const planWaiting = !!latestPlan?.prompted && !planResolved\n const savedPromptFailure = promptIssue !== null ? null : latestPlan !== undefined && !latestPlan.prompted && !planResolved ? latestPlan : latestDiagnosis !== undefined && !latestDiagnosis.prompted && !diagnosisResolved ? latestDiagnosis : null\n const versions = view?.path.curricula ?? []\n const acceptedPlan = versions.find((entry) => entry.version === view?.path.acceptedVersion) ?? null\n const latestProposal = versions.at(-1)\n const plan = versions.find((entry) => entry.version === selectedVersion) ?? latestProposal\n const busyAction = busy !== null\n const hasAcceptedRoute = view?.path.acceptedVersion != null\n const stage: 'goal' | 'diagnosis' | 'route' | 'learning' = !savedReady ? 'goal' : !assessmentCurrent || !['ready', 'skipped'].includes(assessment?.status ?? '') ? 'diagnosis' : hasAcceptedRoute ? 'learning' : 'route'\n const activePlan = stage === 'learning' ? acceptedPlan : plan ?? null\n const doneSet = activePlan === null ? new Set<string>() : attemptDone(view?.run?.attempts ?? [], activePlan.version)\n const doneItems = activePlan?.items.filter((item) => doneSet.has(item.id)) ?? []\n const nextLesson = activePlan?.items.find((item) => !doneSet.has(item.id)) ?? null\n const defaultItemId = activeAttempt?.itemId ?? nextLesson?.id ?? activePlan?.items[0]?.id ?? null\n useEffect(() => {\n if (activePlan === null) return\n setSelectedItemId((current) => current !== null && activePlan.items.some((item) => item.id === current) ? current : defaultItemId)\n }, [activePlan?.version, defaultItemId])\n const selectedItem = activePlan?.items.find((item) => item.id === selectedItemId) ?? activePlan?.items.find((item) => item.id === defaultItemId) ?? null\n const activeItem = activePlan?.items.find((item) => item.id === activeAttempt?.itemId) ?? null\n\n const diagnosisAgeMinutes = view !== null && latestDiagnosis !== undefined\n ? Math.max(0, Math.round((Date.parse(view.now) - Date.parse(latestDiagnosis.createdAt)) / 60_000))\n : 0\n const liveFailure = sessionActivity !== undefined && !sessionActivity.running ? sessionActivity.lastAgentError : null\n const diagnosisFailure = diagnosisWaiting ? latestDiagnosis?.failure ?? liveFailure : null\n const startWaiting = activeAttempt !== undefined && activeAttempt.units.length === 0 && activeAttempt.status !== 'completed' && activeAttempt.status !== 'interrupted' && promptIssue === null\n const startFailure = activeAttempt !== undefined && !activeAttempt.prompted && promptIssue?.kind !== 'start' ? activeAttempt.promptReason ?? pathText.noReason : null\n const versionConflict = activeAttempt !== undefined && view?.path.acceptedVersion != null && activeAttempt.version !== view.path.acceptedVersion\n\n const retryDiagnosisAndOpen = () => {\n const path = currentView.current?.path\n if (!path || dirtyRef.current || !ready(path.brief)) { onOpenChat?.(); return }\n void mutate('request-diagnosis', (params) => client.request({ ...params, kind: 'diagnosis', operationId: operationId() }), (result) => {\n const response = result as PromptedPathView\n if (!response.prompted) setPromptIssue({ kind: 'diagnosis', message: response.promptReason ?? pathText.tutorUnreachable })\n onOpenChat?.()\n })\n }\n const retryPlan = () => ask('plan')\n const retryStart = () => {\n if (activeAttempt === undefined) return\n start(activeAttempt.version, activeAttempt.itemId, false, 'resume')\n }\n const resumeActive = (checkin = false) => {\n if (activeAttempt === undefined) return\n start(activeAttempt.version, activeAttempt.itemId, false, checkin ? 'checkin' : 'resume')\n }\n const completeTask = (taskId: string) => {\n if (client.updateTask === undefined) return\n void (async () => {\n try {\n const result = await client.updateTask!({ taskId, status: 'done' })\n setTaskList(result.tasks)\n } catch {\n setNotice(text.planExportFailed)\n }\n })()\n }\n\n const exportPlan = () => {\n if (client.exportPlan === undefined) return\n void (async () => {\n try {\n const file = await client.exportPlan!()\n const blob = new Blob([file.text], { type: 'text/calendar;charset=utf-8' })\n const url = URL.createObjectURL(blob)\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = file.filename\n anchor.click()\n setTimeout(() => URL.revokeObjectURL(url), 0)\n setNotice(text.planExported)\n } catch {\n setNotice(text.planExportFailed)\n }\n })()\n }\n\n const pauseActive = () => {\n if (activeAttempt === undefined || client.pause === undefined) return\n void mutate('pause', (params) => client.pause!(params), () => { announcePathState(true); setNotice(text.lessonPausedNotice) })\n }\n const errorAdvice = (raw: string): string => {\n const kind = classifyTutorError(raw)\n if (kind === 'billing') return text.errorBilling\n if (kind === 'auth') return text.errorAuth\n if (kind === 'rate-limit') return text.errorRate\n if (kind === 'network') return text.errorNetwork\n if (kind === 'context') return text.errorContext\n return text.errorOther\n }\n const fit = plan === undefined || view === null ? null : routeFit(plan, view.path.brief)\n\n let nextCard: ReactNode = null\n if (view === null) nextCard = null\n else if (readOnly) nextCard = <NextCard title={text.goalTitle} body={pathText.readOnlyNotice} />\n else if (stage === 'goal') nextCard = null\n else if (stage === 'diagnosis') {\n if (diagnosisWaiting && diagnosisFailure !== null) {\n nextCard = <NextCard kind=\"error\" layout=\"stack\" title={text.diagnosisFailed} body={errorAdvice(diagnosisFailure)}\n primary={{ label: text.retryWithChat, run: retryDiagnosisAndOpen, disabled: busyAction || dirty }}\n secondary={{ label: text.skipDiagnosis, run: skipAssessment, disabled: busyAction }} />\n } else if (diagnosisWaiting) {\n const running = sessionActivity?.running === true\n nextCard = <NextCard kind=\"waiting\" layout=\"stack\" title={text.diagnosisWaitingTitle} body={text.diagnosisWaitingHint}\n note={diagnosisAgeMinutes >= 1 ? text.elapsed(waitDuration(diagnosisAgeMinutes, lang)) : undefined}\n primary={onOpenChat === undefined ? undefined : { label: text.openChat, run: onOpenChat }}\n secondary={{ label: text.skipDiagnosis, run: skipAssessment, disabled: busyAction }}\n tertiary={running || diagnosisAgeMinutes < 1 ? undefined : { label: text.tutorNoQuestions, run: retryDiagnosisAndOpen, disabled: busyAction || dirty }} />\n } else {\n nextCard = <NextCard title={text.diagnosisTitle} body={text.diagnosisBody}\n primary={{ label: text.requestDiagnosis, run: () => ask('diagnosis'), disabled: busyAction || dirty }}\n secondary={{ label: text.skipDiagnosis, run: skipAssessment, disabled: busyAction }} />\n }\n } else if (stage === 'route') {\n if (savedPromptFailure !== null) {\n nextCard = <NextCard kind=\"error\" layout=\"stack\" title={text.routeFailedTitle} body={errorAdvice(savedPromptFailure.reason ?? '')}\n primary={{ label: text.retry, run: () => ask(savedPromptFailure.kind), disabled: busyAction || dirty }} />\n } else if (planWaiting) {\n nextCard = <NextCard kind=\"waiting\" layout=\"stack\" title={text.routeWaitingTitle} body={text.routeWaitingBody}\n primary={onOpenChat === undefined ? undefined : { label: text.openChat, run: onOpenChat }} />\n } else if (plan !== undefined && plan.briefRevision === view.path.briefRevision && plan.assessmentRevision === view.path.assessmentRevision) {\n nextCard = <ProposalCard plan={plan} brief={view.path.brief} text={text} lang={lang} busy={busyAction} readOnly={readOnly}\n canRequest={!busyAction && !dirty && !readOnly}\n onAccept={() => acceptVersion(plan.version)} onRevise={() => ask('plan')}\n versions={versions.map((entry) => entry.version)} selectedVersion={selectedVersion} onSelectVersion={setSelectedVersion} />\n } else {\n nextCard = <NextCard title={text.proposalTitle} body={text.proposalBody}\n primary={{ label: text.requestRoute, run: () => ask('plan'), disabled: !savedReady || busyAction || dirty || readOnly }}\n secondary={undefined} />\n }\n } else {\n if (versionConflict) {\n nextCard = <NextCard kind=\"version\" layout=\"stack\" title={activeItem?.title ?? text.currentLesson} body={text.versionSubtitle}\n children={<div className=\"dtx-version-box\"><p className=\"dtx-version-title\"><Icon name=\"warning\" size={16} />{text.versionTitle(activeAttempt!.version, view.path.acceptedVersion!)}</p><p className=\"dtx-version-body\">{text.versionBody(activeAttempt!.version, view.path.acceptedVersion!)}</p></div>}\n primary={{ label: text.continueConversation, run: onOpenChat ?? (() => {}) }}\n tertiary={{ label: text.viewLatestRoute, run: () => { setSelectedVersion(view.path.acceptedVersion!); setSelectedItemId(null) } }} />\n } else if (startFailure !== null) {\n nextCard = <NextCard kind=\"error\" layout=\"stack\" title={text.routeFailedTitle} body={startFailure}\n primary={{ label: pathText.retryCurrentLesson ?? text.retry, run: retryStart, disabled: busyAction || readOnly }}\n secondary={onOpenChat === undefined ? undefined : { label: text.openChat, run: onOpenChat }} />\n } else if (startWaiting) {\n const age = Math.max(0, Math.round((Date.now() - Date.parse(activeAttempt!.updatedAt)) / 60_000))\n nextCard = <NextCard kind=\"waiting\" layout=\"stack\" title={text.waitingTutor} body={text.waitingTutorHint}\n note={age >= 1 ? text.elapsed(waitDuration(age, lang)) : undefined}\n primary={onOpenChat === undefined ? undefined : { label: text.openChat, run: onOpenChat }} />\n } else if (acceptedPlan !== null && nextLesson === null) {\n nextCard = <NextCard kind=\"complete\" title={text.routeCompleteTitle} body={text.routeCompleteBody(doneItems.length, acceptedPlan.items.length)}\n children={<div className=\"dtx-hint-box\"><span className=\"dtx-hint-icon\"><Icon name=\"info\" size={18} /></span><span><span className=\"dtx-hint-title\">{text.masteryNoticeTitle}</span><span className=\"dtx-hint-body\" style={{ display: 'block' }}>{text.masteryNoticeBody}</span></span></div>}\n primary={onOpenEvidence === undefined ? undefined : { label: text.viewEvidence, run: onOpenEvidence }}\n secondary={onOpenChat === undefined ? undefined : { label: text.backToChat, run: onOpenChat }} />\n } else if (activeAttempt !== undefined && activeItem !== null) {\n const paused = activeAttempt.status === 'interrupted'\n const lastUnit = activeAttempt.units.at(-1)?.lesson.title\n const contextLine = [\n text.currentLesson,\n paused ? text.lessonPaused : text.lessonStatusCurrent,\n text.unitsWritten(activeAttempt.units.length),\n ...(lastUnit === undefined ? [] : [text.lastTaught(lastUnit)]),\n ].join(' · ')\n nextCard = <NextCard title={activeItem.title} body={contextLine}\n primary={{ label: paused ? text.resumeLesson : text.continueLesson, run: () => resumeActive(false), disabled: busyAction || readOnly }}\n secondary={!paused && client.pause !== undefined\n ? { label: text.pauseLesson, run: pauseActive, disabled: busyAction || readOnly }\n : undefined}\n tertiary={paused ? undefined : { label: text.checkIn, run: () => resumeActive(true), disabled: busyAction || readOnly }} />\n } else if (activePlan !== null && nextLesson !== null) {\n nextCard = <NextCard title={nextLesson.title} body={doneItems.length === 0 ? text.startFirstBody : text.sessionProgress(doneItems.length, activePlan.items.length)}\n primary={{ label: doneItems.length === 0 ? text.startLesson : text.startNextLesson, run: () => start(activePlan.version, nextLesson.id, false), disabled: busyAction || readOnly }} />\n } else {\n nextCard = <NextCard title={text.proposalTitle} body={text.noRouteBody} primary={onOpenChat === undefined ? undefined : { label: text.openChat, run: onOpenChat }} />\n }\n }\n\n const rail: ReactNode = (() => {\n if (stage === 'goal') return <GoalRail text={text} />\n if (stage === 'diagnosis') return <EmptyRail title={text.emptyLessonTitle} body={text.emptyLessonBody} text={text} action={undefined} />\n if (stage === 'route' && plan === undefined) return <EmptyRail title={text.noRouteTitle} body={text.noRouteBody} text={text} action={onOpenChat === undefined ? undefined : { label: text.openChat, run: onOpenChat }} />\n if (activePlan === null || selectedItem === null) return <EmptyRail title={text.emptyLessonTitle} body={text.emptyLessonBody} text={text} />\n const attempt = latestAttempt(view?.run?.attempts ?? [], activePlan.version, selectedItem.id)\n const mode = stage === 'learning' && activeAttempt !== undefined && activeAttempt.itemId === selectedItem.id ? 'current' : 'preview'\n return <LessonRail plan={activePlan} item={selectedItem} attempt={attempt} activeAttempt={activeAttempt} mode={mode} version={activePlan.version}\n text={text} lang={lang}\n onStart={stage === 'learning' && activeAttempt === undefined ? () => start(activePlan.version, selectedItem.id, false) : undefined}\n onOpenChat={onOpenChat} onOpenLesson={onOpenLesson} readOnly={readOnly} />\n })()\n\n const assessmentSummary = assessment !== null && stage !== 'diagnosis' ? (\n <details className=\"dtx-card dtx-form-card\" style={{ padding: 0 }}>\n <summary style={{ cursor: 'pointer', padding: '14px 18px', fontSize: 13, color: 'var(--dt-muted,#6b7280)' }}>{text.diagnosisSummary} · {assessment.status === 'skipped' ? text.diagnosisSkipped : text.diagnosisReady}</summary>\n <div style={{ padding: '0 18px 18px', fontSize: 13 }}>\n <p style={{ marginTop: 0 }}>{assessment.summary}</p>\n {assessment.strengths.length > 0 && <p><b>{text.prerequisiteKnowledge}:</b> {assessment.strengths.join(' · ')}</p>}\n {assessment.gaps.length > 0 && <p><b>{text.diagnosisFailed}:</b> {assessment.gaps.join(' · ')}</p>}\n </div>\n </details>\n ) : null\n\n const routeSettings = versions.length > 0 && stage !== 'goal' ? (\n <details className=\"dtx-card dtx-form-card\" style={{ padding: 0 }}>\n <summary style={{ cursor: 'pointer', padding: '14px 18px', fontSize: 13, color: 'var(--dt-muted,#6b7280)' }}>{text.routeListTitle} · {text.routeVersion(selectedVersion ?? versions.at(-1)?.version ?? 1)}</summary>\n <div style={{ padding: '0 18px 18px', fontSize: 13, display: 'flex', gap: 10, flexWrap: 'wrap', alignItems: 'center' }}>\n <select className=\"dtx-input\" style={{ width: 180, height: 36 }} value={selectedVersion ?? ''} onChange={(event) => { setSelectedVersion(Number(event.target.value)); setSelectedItemId(null) }}>\n {versions.map((entry) => <option key={entry.version} value={entry.version}>{text.routeVersion(entry.version)}{entry.version === view?.path.acceptedVersion ? ` · ${text.statusAccepted}` : ''}</option>)}\n </select>\n {stage === 'learning' && <button type=\"button\" className=\"dtx-secondary\" disabled={busyAction || dirty || readOnly} onClick={() => ask('plan')}>{text.requestRevision}</button>}\n </div>\n </details>\n ) : null\n\n return (\n <div className=\"dtx-content\" aria-label={pathText.pageLabel(title)}>\n {readOnly && <p className=\"dtx-notice\">{pathText.readOnlyNotice}</p>}\n {error !== '' && <p className=\"dtx-alert\" role=\"alert\">{error}</p>}\n {pollError !== '' && <p className=\"dtx-alert\" role=\"status\">{view === null ? pathText.couldNotLoadPath : pathText.updatesPaused}{pollError} <button type=\"button\" className=\"dtx-link\" onClick={() => { void refresh() }}>{pathText.retryLoading}</button></p>}\n {notice !== '' && <p className=\"dtx-status\" role=\"status\">{notice}</p>}\n {promptIssue !== null && <p className=\"dtx-alert\" role=\"alert\">{promptIssue.message} <button type=\"button\" className=\"dtx-link\" disabled={busyAction || readOnly} onClick={() => { if (promptIssue.kind === 'start') start(promptIssue.version, promptIssue.itemId, promptIssue.review, promptIssue.checkin === true ? 'checkin' : promptIssue.resume === true ? 'resume' : undefined); else ask(promptIssue.kind) }}>{pathText.retryTutorRequest}</button></p>}\n {view === null\n ? pollError === '' && <p className=\"dtx-status\" role=\"status\">{pathText.loadingPath}</p>\n : <div className=\"dtx-route\">\n <div className=\"dtx-main\">\n <TodayCard tasks={taskList} onExport={client.exportPlan === undefined ? undefined : exportPlan} onComplete={client.updateTask === undefined ? undefined : completeTask} />\n <StageBar stage={stage} text={text} />\n {stage === 'goal' ? <GoalForm draft={draft} dirty={dirty} busy={busy === 'save'} readOnly={readOnly} onChange={changeDraft} onSave={saveBrief} text={text} lang={lang} /> : nextCard}\n {stage === 'diagnosis' && !diagnosisWaiting && diagnosisFailure === null && <DiagnosisInfo text={text} />}\n {activePlan !== null && (stage === 'route' || stage === 'learning') && <RouteModules plan={activePlan} attempts={view.run?.attempts ?? []} activeAttemptId={view.run?.activeAttemptId ?? null}\n selectedItemId={selectedItemId} onSelect={setSelectedItemId} version={activePlan.version} showProgress={stage === 'learning'} text={text} lang={lang} />}\n {stage !== 'goal' && assessmentSummary}\n {stage !== 'goal' && routeSettings}\n {fit !== null && fit.longLessons.length > 0 && <p className=\"dtx-small dtx-muted\">{text.sessionMismatch(fit.longLessons.length)}</p>}\n </div>\n <aside className=\"dtx-rail\">{rail}</aside>\n </div>}\n </div>\n )\n}\n","/** The Review tab: due cards, reveal and four grades. Scheduling only. */\nimport { useCallback, useEffect, useState, type ReactNode } from 'react'\n\nimport type { ReviewCardView } from '../contract.js'\nimport { useDesignText } from './design-text.js'\nimport type { PathClient } from './path-client.js'\nimport { Icon } from './design-icons.jsx'\n\nexport function ReviewPage({ client, onOpenRoute }: { client: PathClient; onOpenRoute?: (() => void) | undefined }): ReactNode {\n const text = useDesignText()\n const [cards, setCards] = useState<ReviewCardView[]>([])\n const [loading, setLoading] = useState(true)\n const [revealed, setRevealed] = useState(false)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState('')\n const [undo, setUndo] = useState<string | null>(null)\n const load = useCallback(async () => {\n if (client.reviewNext === undefined) { setLoading(false); return }\n setLoading(true); setError('')\n try {\n const result = await client.reviewNext()\n setCards(result.cards)\n setRevealed(false)\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : text.reviewFailed)\n } finally { setLoading(false) }\n }, [client, text.reviewFailed])\n useEffect(() => { void load() }, [load])\n\n const card = cards[0]\n const grade = async (rating: 'again' | 'hard' | 'good' | 'easy') => {\n if (client.reviewGrade === undefined || card === undefined || busy) return\n setBusy(true); setError('')\n try {\n const result = await client.reviewGrade({ cardId: card.id, rating })\n setUndo(card.id)\n setCards((current) => current.slice(1))\n setRevealed(false)\n if (result.remaining > 0) void load()\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : text.reviewFailed)\n } finally { setBusy(false) }\n }\n\n return (\n <div className=\"dtx-review\">\n <header className=\"dtx-review-head\">\n <div>\n <p className=\"dtx-review-eyebrow\">{text.reviewEyebrow}</p>\n <h1>{text.reviewTitle}</h1>\n <p className=\"dtx-review-sub\">{text.reviewSub}</p>\n </div>\n {onOpenRoute !== undefined && <button type=\"button\" className=\"dtx-link\" onClick={onOpenRoute}>{text.backToRoute}</button>}\n </header>\n {loading && <p className=\"dtx-status\">{text.reviewLoading}</p>}\n {error !== '' && <div className=\"dtx-alert\" role=\"alert\">{error} <button type=\"button\" className=\"dtx-link\" onClick={() => void load()}>{text.reviewRetry}</button></div>}\n {undo !== null && <div className=\"dtx-review-undo\" role=\"status\"><span>{text.reviewUndone}</span><button type=\"button\" className=\"dtx-link\" onClick={() => { void (async () => { if (client.reviewUndo === undefined) return; try { await client.reviewUndo(undo); setUndo(null); await load() } catch { setError(text.reviewUndoFailed) } })() }}>{text.reviewUndo}</button></div>}\n {!loading && error === '' && card === undefined && (\n <div className=\"dtx-review-empty\">\n <span className=\"dtx-tab-empty-icon\"><Icon name=\"check\" size={30} /></span>\n <h2>{text.reviewEmpty}</h2>\n <p>{text.reviewEmptyBody}</p>\n {onOpenRoute !== undefined && <button type=\"button\" className=\"dtx-primary\" onClick={onOpenRoute}>{text.backToRoute}</button>}\n </div>\n )}\n {card !== undefined && (\n <section className=\"dtx-review-card\" aria-label={text.reviewTitle}>\n <p className=\"dtx-review-count\">{text.reviewDue(cards.length)}</p>\n <div className=\"dtx-review-front\">{card.front}</div>\n {!revealed\n ? <button type=\"button\" className=\"dtx-secondary\" onClick={() => setRevealed(true)}>{text.reviewShowBack}</button>\n : <>\n <div className=\"dtx-review-back\">{card.back}</div>\n {card.sourceRefs !== undefined && <p className=\"dtx-caption\">{text.sourceRef} · {card.sourceRefs.map((ref) => ref.locator).join(' · ')}</p>}\n <div className=\"dtx-review-grades\">\n <button type=\"button\" className=\"dtx-grade dtx-grade-again\" disabled={busy} onClick={() => void grade('again')}>{text.reviewAgain}<span>{text.reviewAgainHint}</span></button>\n <button type=\"button\" className=\"dtx-grade dtx-grade-hard\" disabled={busy} onClick={() => void grade('hard')}>{text.reviewHard}<span>{text.reviewHardHint}</span></button>\n <button type=\"button\" className=\"dtx-grade dtx-grade-good\" disabled={busy} onClick={() => void grade('good')}>{text.reviewGood}<span>{text.reviewGoodHint}</span></button>\n <button type=\"button\" className=\"dtx-grade dtx-grade-easy\" disabled={busy} onClick={() => void grade('easy')}>{text.reviewEasy}<span>{text.reviewEasyHint}</span></button>\n </div>\n </>}\n {cards.length > 1 && <p className=\"dtx-review-more\">{text.reviewMore(cards.length - 1)}</p>}\n </section>\n )}\n </div>\n )\n}\n","/** The Insights tab: read-only facts. No score, percentage or mastery claim. */\nimport { useCallback, useEffect, useState, type ReactNode } from 'react'\n\nimport type { InsightsView } from '../contract.js'\nimport { useDesignText } from './design-text.js'\nimport { Icon } from './design-icons.jsx'\nimport type { PathClient } from './path-client.js'\n\nfunction when(iso: string): string {\n const date = new Date(iso)\n if (!Number.isFinite(date.getTime())) return iso\n try { return new Intl.DateTimeFormat(undefined, { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }).format(date) } catch { return iso }\n}\n\nexport function InsightsPage({ client, courseId, onOpenReview, onOpenRoute }: {\n client: PathClient\n courseId: string\n onOpenReview?: (() => void) | undefined\n onOpenRoute?: (() => void) | undefined\n}): ReactNode {\n const text = useDesignText()\n const [data, setData] = useState<InsightsView | null>(null)\n const [loading, setLoading] = useState(true)\n const [error, setError] = useState('')\n const load = useCallback(async () => {\n if (client.insights === undefined) { setLoading(false); return }\n setLoading(true); setError('')\n try { setData(await client.insights(courseId)) } catch (cause) { setError(cause instanceof Error ? cause.message : text.insightsFailed) } finally { setLoading(false) }\n }, [client, courseId, text.insightsFailed])\n useEffect(() => { void load() }, [load])\n\n return (\n <div className=\"dtx-insights\">\n <header className=\"dtx-insights-head\">\n <div>\n <p className=\"dtx-insights-eyebrow\">{text.insightsEyebrow}</p>\n <h1>{text.insightsTitle}</h1>\n <p className=\"dtx-insights-sub\">{text.insightsSub}</p>\n </div>\n {onOpenRoute !== undefined && <button type=\"button\" className=\"dtx-link\" onClick={onOpenRoute}>{text.backToRoute}</button>}\n </header>\n {loading && <p className=\"dtx-status\">{text.insightsLoading}</p>}\n {error !== '' && <div className=\"dtx-alert\" role=\"alert\">{error} <button type=\"button\" className=\"dtx-link\" onClick={() => void load()}>{text.reviewRetry}</button></div>}\n {data !== null && (\n <div className=\"dtx-insights-grid\">\n <section className=\"dtx-insight-card dtx-insight-review\">\n <h2><Icon name=\"layers\" size={16} /> {text.insightsReview}</h2>\n <p className=\"dtx-insight-big\">{data.review.due}</p>\n <p className=\"dtx-insight-note\">{text.insightsReviewDue(data.review.overdue, data.review.total)}</p>\n {data.review.lapsed.length > 0 && <>\n <h3>{text.insightsLapsed}</h3>\n <ul>{data.review.lapsed.map((card) => <li key={card.id}><span>{card.front}</span><b>{card.lapses}×</b></li>)}</ul>\n </>}\n {onOpenReview !== undefined && <button type=\"button\" className=\"dtx-link\" onClick={onOpenReview}>{text.insightsGoReview}</button>}\n </section>\n <section className=\"dtx-insight-card\">\n <h2><Icon name=\"check\" size={16} /> {text.insightsEvidence}</h2>\n <p className=\"dtx-insight-big\">{data.evidence.total}</p>\n <p className=\"dtx-insight-note\">{text.insightsLast7(data.evidence.last7Days)}</p>\n <div className=\"dtx-insight-chips\">{data.evidence.byKind.map((entry) => <span key={entry.kind}>{entry.kind} · {entry.count}</span>)}</div>\n </section>\n <section className=\"dtx-insight-card\">\n <h2><Icon name=\"warning\" size={16} /> {text.insightsWeakNodes}</h2>\n {data.weakNodes.length === 0 ? <p className=\"dtx-insight-note\">{text.insightsNoWeak}</p> : <ul className=\"dtx-insight-lines\">{data.weakNodes.map((node) => <li key={node.id}><span>{node.title}</span><b>{node.state} · {text.insightsEvidenceCount(node.evidenceCount)}</b></li>)}</ul>}\n </section>\n <section className=\"dtx-insight-card\">\n <h2><Icon name=\"clock\" size={16} /> {text.insightsRecentReviews}</h2>\n {data.recentReviews.length === 0 ? <p className=\"dtx-insight-note\">{text.insightsNoneYet}</p> : <ul className=\"dtx-insight-lines\">{data.recentReviews.slice(0, 5).map((entry, index) => <li key={`${entry.at}:${index}`}><span>{entry.front}</span><b>{entry.rating} · {when(entry.at)}</b></li>)}</ul>}\n </section>\n <section className=\"dtx-insight-card\">\n <h2><Icon name=\"book\" size={16} /> {text.insightsActivity}</h2>\n {data.recentAttempts.length === 0 ? <p className=\"dtx-insight-note\">{text.insightsNoneYet}</p> : <ul className=\"dtx-insight-lines\">{data.recentAttempts.map((attempt) => <li key={attempt.attemptId}><span>{attempt.status}</span><b>{text.insightsAttemptDetail(attempt.units, attempt.responses)} · {when(attempt.updatedAt)}</b></li>)}</ul>}\n <p className=\"dtx-insight-note\">{text.insightsPlan(data.tasks.open, data.tasks.dueToday)} · {text.insightsSources(data.sources.count)}</p>\n </section>\n </div>\n )}\n </div>\n )\n}\n","/**\n * The browser half's HTTP client.\n *\n * Plain `fetch` against this plugin's own prefix. No storage access, no DSH\n * internals — the browser has exactly the three calls the panel makes.\n *\n * Every response is checked for the `{ ok }` envelope, so an HTTP 200 carrying\n * a failure cannot be mistaken for data.\n */\n\nimport type {\n FocusResponse,\n HandoffView,\n LessonResponse,\n NodeDetailResponse,\n OverviewResponse,\n ProjectSessionResponse,\n} from '../contract.js'\n\n/** Route prefix owned by this plugin; must match the host's `API_PREFIX`. */\nconst BASE = '/diagnostic-tutor/api'\n\n/** Requests are local; anything slower than this is a problem worth surfacing. */\nconst TIMEOUT_MS = 8000\n\nasync function request<T>(path: string, init?: RequestInit): Promise<T> {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), TIMEOUT_MS)\n try {\n const response = await fetch(`${BASE}${path}`, {\n ...init,\n signal: controller.signal,\n headers: { accept: 'application/json', ...(init?.headers ?? {}) },\n })\n\n let body: unknown\n try {\n body = await response.json()\n } catch {\n throw new Error(`unreadable response (HTTP ${response.status})`)\n }\n\n // Narrow by inspection rather than by generic: `T` is unconstrained, so a\n // union type would not discriminate.\n const envelope = body as { ok?: unknown; error?: { code?: string; message?: string } }\n if (!response.ok || envelope.ok === false) {\n throw new Error(\n envelope.error?.message ?? envelope.error?.code ?? `HTTP ${response.status}`,\n )\n }\n return body as T\n } finally {\n clearTimeout(timer)\n }\n}\n\n/**\n * The current course and its whole diagnosis map.\n *\n * The conversation is named because learning state belongs to one: the course,\n * the map and the lessons are shared, but where *this* conversation has got to\n * is not. Without it the server cannot tell whose focus to report and would\n * have to guess — which is how a lesson started in one conversation appeared\n * under another.\n *\n * @param sessionId - the conversation asking, when one is open.\n */\nexport function fetchOverview(sessionId?: string): Promise<OverviewResponse> {\n const query = sessionId === undefined || sessionId.length === 0 ? '' : `?sessionId=${encodeURIComponent(sessionId)}`\n return request<OverviewResponse>(`/overview${query}`)\n}\n\n/**\n * Choose what this conversation learns.\n *\n * Sending no course selects \"start something new\": a real state, not the\n * absence of one, and the one the learner lands in when they want the tutor to\n * teach them something that does not exist yet.\n *\n * @param sessionId - the conversation making the choice.\n * @param courseId - the project to open, or undefined for a new one.\n */\nexport function selectProject(sessionId: string, courseId?: string): Promise<unknown> {\n return request('/project', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(courseId === undefined ? { sessionId } : { sessionId, courseId }),\n })\n}\n\n/**\n * Which conversation should resume a project's learning.\n *\n * A project is shared but its focus, runs and teaching attempts belong to a\n * conversation, so \"enter learning\" asks before binding the project to the\n * conversation currently on screen.\n */\nexport function resolveProjectSession(courseId: string, sessionId?: string): Promise<ProjectSessionResponse> {\n const query = new URLSearchParams({ courseId })\n if (sessionId !== undefined && sessionId.length > 0) query.set('sessionId', sessionId)\n return request<ProjectSessionResponse>(`/project/session?${query.toString()}`)\n}\n\n/** One node, its evidence, its parent and its children. */\nexport function fetchNode(nodeId: string): Promise<NodeDetailResponse> {\n return request<NodeDetailResponse>(`/node?id=${encodeURIComponent(nodeId)}`)\n}\n\n/** The lesson the tutor has written for a node; `lesson: null` until it has. */\nexport function fetchLesson(nodeId: string): Promise<LessonResponse> {\n return request<LessonResponse>(`/lesson?nodeId=${encodeURIComponent(nodeId)}`)\n}\n\n/**\n * Save everything the learner owns to a file.\n *\n * Deliberately a full download rather than a call that returns data: the point\n * of an export is that the learner ends up holding it, and a file they can move,\n * read and keep is the only version of that which survives this app.\n *\n * @returns the filename the browser was asked to save.\n */\nexport async function downloadExport(): Promise<string> {\n const response = await fetch(`${BASE}/export`, { headers: { accept: 'application/json' } })\n if (!response.ok) throw new Error(`export failed (HTTP ${response.status})`)\n const text = await response.text()\n\n const stamp = new Date().toISOString().slice(0, 10)\n const name = `dsh-diagnostic-tutor-${stamp}.json`\n const url = URL.createObjectURL(new Blob([text], { type: 'application/json' }))\n try {\n const anchor = document.createElement('a')\n anchor.href = url\n anchor.download = name\n document.body.appendChild(anchor)\n anchor.click()\n anchor.remove()\n } finally {\n // Revoking immediately is safe because the click has already handed the\n // blob to the browser's download machinery.\n URL.revokeObjectURL(url)\n }\n return name\n}\n\n/**\n * Delete everything and return to first run.\n *\n * @returns nothing; the caller re-reads the overview.\n */\nexport function resetState(): Promise<{ ok: true; reset: true }> {\n return request<{ ok: true; reset: true }>('/reset', { method: 'POST' })\n}\n\n/**\n * Tell the host a surface has rendered the lesson.\n *\n * The last leg of the timing chain: everything before it is measured on the\n * host, and this is the only part only the browser can answer.\n */\nexport function reportObserved(\n nodeId: string,\n sessionId: string,\n): Promise<{ handoff: HandoffView | null }> {\n // The conversation is required, and it is required for a reason: a handoff\n // belongs to one, so a report that named only the node would be ambiguous the\n // moment two conversations wait on the same target. The host rejects it, and\n // rightly — but this call used to omit it and quietly collected a 400 on\n // every lesson's first render.\n return request<{ handoff: HandoffView | null }>('/handoff/observed', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ nodeId, sessionId }),\n })\n}\n\n/**\n * What the Start learning button does.\n *\n * Records the focus and asks the host to wake the tutor. `sessionId` is the\n * session the panel is showing; without it the focus is still recorded and the\n * response says the tutor was not reached.\n */\nexport function startFocus(nodeId: string, sessionId?: string): Promise<FocusResponse> {\n return request<FocusResponse>('/focus', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(sessionId === undefined ? { nodeId } : { nodeId, sessionId }),\n })\n}\n","import type { PathClient } from './path-client.js'\nimport { defaultPathClient } from './path-client.js'\n/**\n * Shared learning state for both surfaces.\n *\n * The plugin renders the same runtime in two places — the full-page panel and\n * the docked right-sidebar tab — and both need identical behaviour: select a\n * node, start learning, follow the tutor. Duplicating that would mean two\n * implementations of the focus hand-off and two copies of the polling loop,\n * which is exactly how two surfaces drift apart.\n *\n * So the behaviour lives here once, and the components are presentation.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\n\nimport type {\n FocusView,\n HandoffView,\n LessonRecord,\n NextStepView,\n NodeDetailResponse,\n OverviewResponse,\n ProjectCardView,\n} from '../contract.js'\nimport * as realApi from './api.js'\nimport { getLang } from './i18n.js'\nimport { runtimeCopy } from './learning-text.js'\n\n/** The calls the surfaces make; injectable so preview and tests can fake them. */\nexport interface PanelClient {\n /** Optional planning capability; legacy clients remain node-only. */\n pathClient?: PathClient\n\n fetchOverview(sessionId?: string): Promise<OverviewResponse>\n fetchNode(nodeId: string): Promise<NodeDetailResponse>\n fetchLesson(nodeId: string): Promise<LessonResponse>\n startFocus(nodeId: string, sessionId?: string): Promise<FocusResponse>\n /** Optional: only the real client can change which project is open. */\n selectProject?(sessionId: string, courseId?: string): Promise<unknown>\n /**\n * Optional: resolve the conversation that should resume a project.\n *\n * Without it (preview, unit tests) \"enter learning\" falls back to opening the\n * project in this conversation.\n */\n resolveProjectSession?(courseId: string, sessionId?: string): Promise<{ sessionId: string | null }>\n /** Optional: only the real client can report it. */\n reportObserved?(nodeId: string, sessionId: string): Promise<unknown>\n /** Optional: only the real client can save a file. */\n downloadExport?(): Promise<string>\n /** Optional: only the real client can delete state. */\n resetState?(): Promise<unknown>\n}\n\ntype LessonResponse = Awaited<ReturnType<typeof realApi.fetchLesson>>\ntype FocusResponse = Awaited<ReturnType<typeof realApi.startFocus>>\n\nfunction requestScope(sessionId: string | undefined) {\n return {\n sessionId,\n revision: 0,\n overviewIssued: 0,\n overviewApplied: 0,\n detailRevision: 0,\n detailIssued: 0,\n detailApplied: 0,\n pendingAction: false,\n }\n}\n\ntype RequestScope = ReturnType<typeof requestScope>\n\nfunction overviewRequest(scope: RequestScope) {\n return { scope, revision: scope.revision, sequence: ++scope.overviewIssued }\n}\n\ntype OverviewRequest = ReturnType<typeof overviewRequest>\n\nfunction orderedWrite<T>(\n queues: Map<string | undefined, Promise<void>>,\n scope: RequestScope,\n write: () => Promise<T>,\n): Promise<T> {\n const result = (queues.get(scope.sessionId) ?? Promise.resolve()).then(write)\n // Only the writes are ordered; a slow follow-up read must not hold the next\n // choice up, and a failed write must not prevent the next choice being sent.\n queues.set(scope.sessionId, result.then(() => {}, () => {}))\n return result\n}\n\nexport const defaultClient: PanelClient = {\n pathClient: defaultPathClient,\n fetchOverview: realApi.fetchOverview,\n fetchNode: realApi.fetchNode,\n fetchLesson: realApi.fetchLesson,\n startFocus: realApi.startFocus,\n selectProject: realApi.selectProject,\n resolveProjectSession: realApi.resolveProjectSession,\n reportObserved: realApi.reportObserved,\n downloadExport: realApi.downloadExport,\n resetState: realApi.resetState,\n}\n\n/**\n * How often a surface re-reads its conversation's state.\n *\n * Polling rather than a push channel: DSH exposes no generic host→client push\n * for third-party plugins, and the published UI plugins poll for the same\n * reason. An open conversation must also refresh before it has a focus: the\n * tutor can create or adopt its project while the surface shows the library.\n */\nexport const POLL_INTERVAL_MS = 2000\n\nexport interface LearningState {\n /** Every Learning Project the learner has, for the library. */\n library: ProjectCardView[]\n /** Opens one in this conversation, or the blank slate when given none. */\n openProject: (courseId?: string) => void\n /**\n * Enters a project for learning: resumes the conversation that was last\n * studying it when there is one, instead of rebinding it to this one.\n */\n enterProject: (courseId: string) => void\n readonly overview: OverviewResponse | null\n readonly focus: FocusView | null\n /** The tutor's recommendation, when the learner has not acted on it yet. */\n readonly nextStep: NextStepView | null\n /** How the current handoff is going, for the progress line. */\n readonly handoff: HandoffView | null\n /**\n * Whether a teaching brain is installed: `true`, confidently `false`, or\n * `null` when this scope cannot see the catalog. Only `false` may warn.\n */\n readonly teachingBrain: boolean | null\n /** The node whose detail is shown; the focus when nothing is picked. */\n readonly selectedId: string | null\n readonly detail: NodeDetailResponse | null\n readonly lesson: LessonRecord | null\n readonly note: string | null\n readonly error: string | null\n readonly starting: boolean\n readonly loading: boolean\n select(nodeId: string): void\n /** Start learning on the selected node. */\n start(): void\n /** Start learning on a named node — how a recommendation is acted on. */\n continueTo(nodeId: string): void\n dismissLesson(): void\n /** Save everything to a file. */\n exportData(): void\n /** Delete everything and re-read from empty. */\n resetAll(): void\n /** A one-line account of the last data action, for the surface to show. */\n readonly dataNote: string | null\n}\n\n/**\n * Read and follow the learning runtime.\n *\n * @param options.client - the API to talk to.\n * @param options.sessionId - the session the tutor should be woken in.\n * @param options.initialOverview - pre-supplied state, so the preview and tests\n * skip the first fetch.\n * @returns the state and the two actions the surfaces offer.\n */\nexport function useLearning(options: {\n client: PanelClient\n sessionId?: string | undefined\n initialOverview?: OverviewResponse | undefined\n /** Called once a focus has been recorded, so a surface can react to it. */\n onStarted?: (() => void) | undefined\n /** Called when \"enter learning\" resolves to a different conversation. */\n onOpenSession?: ((sessionId: string) => void) | undefined\n}): LearningState {\n const { client, sessionId, initialOverview, onStarted, onOpenSession } = options\n\n // A request belongs to the scope that started it. Replacing this token on\n // every switch also rejects an old A response after an A → B → A journey.\n const scopeRef = useRef(requestScope(sessionId))\n if (scopeRef.current.sessionId !== sessionId) scopeRef.current = requestScope(sessionId)\n // Writes keep their order for the lifetime of the surface, even when the\n // learner leaves a session and returns while its earlier writes are pending.\n const writeQueuesRef = useRef(new Map<string | undefined, Promise<void>>())\n\n const [snapshot, setSnapshot] = useState({\n scope: scopeRef.current,\n data: initialOverview ?? null,\n })\n // Hide the previous conversation's data on the switch render, before the\n // effect clears the local detail and the new overview has arrived.\n const currentScope = snapshot.scope === scopeRef.current\n const overview = currentScope ? snapshot.data : null\n const overviewRef = useRef(overview)\n overviewRef.current = overview\n const currentRequest = useCallback((request: OverviewRequest) => (\n scopeRef.current === request.scope &&\n request.scope.revision === request.revision &&\n request.sequence >= request.scope.overviewApplied\n ), [])\n const setOverview = useCallback((request: OverviewRequest, data: OverviewResponse): boolean => {\n if (!currentRequest(request)) return false\n request.scope.overviewApplied = request.sequence\n const previous = overviewRef.current\n const projectChanged = previous?.course?.id !== data.course?.id\n if (projectChanged) {\n setSelectedId(null)\n }\n if (projectChanged || previous?.focus?.nodeId !== data.focus?.nodeId) {\n ++request.scope.detailRevision\n setDetail(null)\n setLesson(null)\n setNote(null)\n }\n overviewRef.current = data\n setSnapshot({ scope: request.scope, data })\n setLoading(false)\n return true\n }, [currentRequest])\n const [selectedId, setSelectedId] = useState<string | null>(null)\n const [detail, setDetail] = useState<NodeDetailResponse | null>(null)\n const [lesson, setLesson] = useState<LessonRecord | null>(null)\n const [note, setNote] = useState<string | null>(null)\n const [error, setError] = useState<string | null>(null)\n const [starting, setStarting] = useState(false)\n const [loading, setLoading] = useState(initialOverview === undefined)\n\n const focus = overview?.focus ?? null\n const focusNodeId = focus?.nodeId ?? null\n\n // Which node's detail is worth keeping fresh. The focus wins, so the panel\n // follows the tutor's node even when the learner is browsing elsewhere.\n const liveNodeId = focusNodeId ?? selectedId\n const liveRef = useRef<string | null>(liveNodeId)\n liveRef.current = liveNodeId\n /** The node whose lesson has already been reported as seen. */\n const observedRef = useRef<string | null>(null)\n const firstLoadRef = useRef(true)\n\n useEffect(() => {\n if (firstLoadRef.current) {\n firstLoadRef.current = false\n if (initialOverview !== undefined) return\n }\n const scope = scopeRef.current\n ++scope.revision\n ++scope.detailRevision\n scope.pendingAction = false\n const request = overviewRequest(scope)\n let live = true\n setSnapshot({ scope, data: null })\n setSelectedId(null)\n setDetail(null)\n setLesson(null)\n setNote(null)\n setError(null)\n setStarting(false)\n setLoading(true)\n observedRef.current = null\n client\n .fetchOverview(sessionId)\n .then((data) => {\n if (live) setOverview(request, data)\n })\n .catch((cause: Error) => {\n if (live && currentRequest(request)) setError(cause.message)\n })\n .finally(() => {\n if (live && currentRequest(request)) setLoading(false)\n })\n return () => {\n live = false\n }\n }, [client, initialOverview, sessionId, setOverview, currentRequest])\n\n const select = useCallback(\n (nodeId: string) => {\n const scope = scopeRef.current\n const revision = ++scope.detailRevision\n const sequence = ++scope.detailIssued\n setSelectedId(nodeId)\n setDetail(null)\n setError(null)\n client\n .fetchNode(nodeId)\n .then((data) => {\n if (\n scopeRef.current === scope && scope.detailRevision === revision &&\n sequence >= scope.detailApplied &&\n overviewRef.current?.nodes.some((node) => node.id === nodeId)\n ) {\n scope.detailApplied = sequence\n setDetail(data)\n }\n })\n .catch((cause: Error) => {\n if (scopeRef.current === scope && scope.detailRevision === revision) setError(cause.message)\n })\n },\n [client],\n )\n\n const beginFocus = useCallback(\n (nodeId: string) => {\n const scope = scopeRef.current\n // A user action invalidates reads made before it. Queue the writes too:\n // two quick choices must reach the runtime in the learner's order.\n const revision = ++scope.revision\n ++scope.detailRevision\n scope.pendingAction = true\n setStarting(true)\n setError(null)\n setNote(null)\n void (async () => {\n try {\n const result = await orderedWrite(writeQueuesRef.current, scope, () => client.startFocus(nodeId, scope.sessionId))\n if (scopeRef.current !== scope || scope.revision !== revision) return\n // Re-read stored focus instead of treating a write response as the\n // current state: the tutor may already have moved to another node.\n const request = overviewRequest(scope)\n const [nextOverview, nextLesson] = await Promise.all([\n client.fetchOverview(scope.sessionId),\n client.fetchLesson(result.focus.nodeId),\n ])\n if (!setOverview(request, nextOverview)) return\n if (nextOverview.focus?.nodeId !== result.focus.nodeId) {\n setLesson(null)\n setNote(null)\n return\n }\n const copy = runtimeCopy(getLang())\n setNote(\n result.prompted\n ? copy.started\n : copy.focusRecorded(result.promptReason ?? copy.unknownReason),\n )\n setLesson(nextLesson.lesson)\n onStarted?.()\n } catch (cause) {\n if (scopeRef.current === scope && scope.revision === revision) setError((cause as Error).message)\n } finally {\n if (scope.revision === revision) scope.pendingAction = false\n if (scopeRef.current === scope && scope.revision === revision) setStarting(false)\n }\n })()\n },\n [client, onStarted, setOverview],\n )\n\n const start = useCallback(() => {\n if (selectedId === null) return\n beginFocus(selectedId)\n }, [beginFocus, selectedId])\n\n const [dataNote, setDataNote] = useState<string | null>(null)\n\n /** Re-read everything, the same way the initial load does. */\n const refresh = useCallback(\n () => {\n const scope = scopeRef.current\n const request = overviewRequest(scope)\n return client\n .fetchOverview(scope.sessionId)\n .then((data) => {\n if (!setOverview(request, data)) return null\n setError(null)\n return data\n })\n .catch((cause: Error) => {\n if (currentRequest(request)) setError(cause.message)\n return null\n })\n },\n [client, setOverview, currentRequest],\n )\n\n const exportData = useCallback(() => {\n setDataNote(null)\n const copy = runtimeCopy(getLang())\n void client\n .downloadExport?.()\n .then((name) => setDataNote(copy.saved(name)))\n .catch((cause: Error) => setDataNote(copy.exportFailed(cause.message)))\n }, [client])\n\n const resetAll = useCallback(() => {\n setDataNote(null)\n setDetail(null)\n setLesson(null)\n const copy = runtimeCopy(getLang())\n void client\n .resetState?.()\n .then(refresh)\n .then(() => setDataNote(copy.everythingDeleted))\n .catch((cause: Error) => setDataNote(copy.deleteFailed(cause.message)))\n }, [client, refresh])\n\n useEffect(() => {\n if (focusNodeId === null) return\n const scope = scopeRef.current\n const nodeId = focusNodeId\n let live = true\n\n const tick = async (): Promise<void> => {\n if (scope.pendingAction) return\n const request = overviewRequest(scope)\n try {\n const [nextOverview, nextLesson] = await Promise.all([\n client.fetchOverview(sessionId),\n client.fetchLesson(nodeId),\n ])\n if (!live || !setOverview(request, nextOverview)) return\n if (nextOverview.focus?.nodeId !== nodeId) {\n setLesson(null)\n return\n }\n setLesson(nextLesson.lesson)\n // The evidence trail and the node's state are what a check changes.\n const watched = liveRef.current\n if (watched !== null) {\n const revision = scope.detailRevision\n const sequence = ++scope.detailIssued\n const nextDetail = await client.fetchNode(watched)\n if (\n !live || scopeRef.current !== scope || scope.revision !== request.revision ||\n scope.detailRevision !== revision ||\n liveRef.current !== watched || sequence < scope.detailApplied\n ) return\n scope.detailApplied = sequence\n setDetail(nextDetail)\n }\n\n // Tell the host the lesson reached a screen. Reported once per node, so\n // the timing chain records the first sighting rather than every poll.\n //\n // Only when a conversation is open: the report is about that\n // conversation's wait, and without one there is nothing to report —\n // sending it anyway would be a request that can only be refused.\n const observedIn = sessionId\n if (nextLesson.lesson !== null && observedIn !== undefined && observedRef.current !== nodeId) {\n observedRef.current = nodeId\n void client.reportObserved?.(nodeId, observedIn)?.catch(() => {})\n }\n } catch {\n // A failed poll is not worth surfacing; the next one may succeed.\n }\n }\n\n void tick()\n const timer = setInterval(() => void tick(), POLL_INTERVAL_MS)\n return () => {\n live = false\n clearInterval(timer)\n }\n }, [client, focusNodeId, sessionId, setOverview])\n\n // The tutor writes a goal independently of this UI. Without this read, a\n // library or \"start something new\" tab never sees the resulting binding.\n useEffect(() => {\n if (sessionId === undefined || focusNodeId !== null) return\n const scope = scopeRef.current\n let live = true\n const timer = setInterval(() => {\n if (scope.pendingAction) return\n const request = overviewRequest(scope)\n void client.fetchOverview(sessionId).then((data) => {\n if (live) setOverview(request, data)\n }).catch(() => {})\n }, POLL_INTERVAL_MS)\n return () => {\n live = false\n clearInterval(timer)\n }\n }, [client, focusNodeId, sessionId, setOverview])\n\n /**\n * Open a Learning Project in this conversation.\n *\n * Nothing is fabricated: the server records the choice and the panel re-reads,\n * so a project with no focus here still reads \"no learning focus yet\". The\n * re-read is immediate rather than waiting for the poll, because the learner\n * just pressed something and a two-second silence reads as a dead button.\n */\n const openProject = useCallback(\n (courseId?: string) => {\n const scope = scopeRef.current\n const { sessionId } = scope\n const choose = client.selectProject\n if (sessionId === undefined || choose === undefined) return\n const revision = ++scope.revision\n ++scope.detailRevision\n scope.pendingAction = true\n setStarting(false)\n setNote(null)\n void (async () => {\n try {\n await orderedWrite(writeQueuesRef.current, scope, () => choose(sessionId, courseId))\n if (scopeRef.current !== scope || scope.revision !== revision) return\n const request = overviewRequest(scope)\n const next = await client.fetchOverview(sessionId)\n if (!setOverview(request, next)) return\n setLesson(null)\n setDetail(null)\n setSelectedId(null)\n } catch {\n // The next poll will show whatever the server actually holds.\n } finally {\n if (scope.revision === revision) scope.pendingAction = false\n }\n })()\n },\n [client, setOverview],\n )\n\n /**\n * Enter a project for learning.\n *\n * A project is shared, but its focus and runs belong to a conversation. If a\n * stored conversation was last learning it, opening the project here would\n * start a second, empty attempt; so this asks the host which conversation\n * that is and, when it differs, hands navigation to the surface. Otherwise it\n * falls back to the ordinary \"open here\" write.\n */\n const enterProject = useCallback(\n (courseId: string) => {\n const scope = scopeRef.current\n const current = scope.sessionId\n const resolve = client.resolveProjectSession\n if (current === undefined || resolve === undefined || onOpenSession === undefined) {\n openProject(courseId)\n return\n }\n void (async () => {\n try {\n const resolved = await resolve(courseId, current)\n if (resolved.sessionId !== null && resolved.sessionId !== current && onOpenSession(resolved.sessionId)) return\n } catch {\n // A project that cannot be resolved is still openable here.\n }\n openProject(courseId)\n })()\n },\n [client, onOpenSession, openProject],\n )\n\n return {\n overview,\n /** The learner's projects, shared like the maps themselves. */\n library: overview?.library ?? [],\n openProject,\n enterProject,\n focus,\n nextStep: overview?.nextStep ?? null,\n handoff: overview?.handoff ?? null,\n teachingBrain: overview?.teachingBrain ?? null,\n selectedId: currentScope ? selectedId : null,\n detail: currentScope ? detail : null,\n lesson: currentScope ? lesson : null,\n note: currentScope ? note : null,\n error: currentScope ? error : null,\n starting: currentScope && starting,\n loading: !currentScope || loading,\n select,\n start,\n continueTo: beginFocus,\n exportData,\n resetAll,\n dataNote,\n dismissLesson: useCallback(() => setLesson(null), []),\n }\n}\n","/**\n * The Learning map page, redesigned as one page with a persistent context rail.\n *\n * Two tabs share one shell: the route (learning path) and the knowledge map.\n * The shell owns the header, tabs and library; the route tab owns its own\n * main/rail split. Nothing here decides anything — every value comes from the\n * same `useLearning` / `PathPage` runtime the other surfaces read.\n */\n\nimport { useEffect, useMemo, useState, type ReactNode } from 'react'\n\nimport { DataFooter } from './blocks.jsx'\nimport { Icon } from './design-icons.jsx'\nimport type { IconName } from './design-icons.jsx'\nimport { useDesignText, nodeStateText } from './design-text.js'\nimport type { DesignText } from './design-text.js'\nimport { useLang } from './i18n.js'\nimport { LanguageToggle } from './language-toggle.js'\nimport { evidenceKindLabel } from './map-text.js'\nimport { formatWhen, relationLabel, stateExplanation } from './model.js'\nimport { PathPage } from './path-page.js'\nimport { ReviewPage } from './review-panel.jsx'\nimport { InsightsPage } from './insights-panel.jsx'\nimport type { PathClient } from './path-client.js'\nimport { defaultPathClient } from './path-client.js'\nimport type { NodeDetailResponse, NodeView, ProjectCardView } from '../contract.js'\nimport type { PanelClient } from './use-learning.js'\nimport type { SessionActivity } from './use-session-activity.js'\nimport { defaultClient, useLearning } from './use-learning.js'\n\n/** The key the map page registers under in `main`, and its panellist id. */\nexport const MAP_PANEL_ID = 'diagnostic-tutor-map'\n\nexport interface LearningMapPanelProps {\n client?: PanelClient\n initialOverview?: Parameters<typeof useLearning>[0]['initialOverview']\n sessionId?: string | undefined\n onOpenChat?: (() => void) | undefined\n onOpenLesson?: (() => void) | undefined\n onOpenSession?: ((sessionId: string) => boolean) | undefined\n sessionActivity?: SessionActivity | undefined\n pathClient?: PathClient | undefined\n}\n\ntype MapView = 'curriculum' | 'knowledge' | 'review' | 'insights'\n\nfunction whenText(iso: string, text: DesignText): string {\n const stamp = Date.parse(iso)\n if (!Number.isFinite(stamp)) return ''\n const days = Math.max(0, Math.floor((Date.now() - stamp) / 86_400_000))\n if (days < 1) return text.whenToday\n if (days < 2) return text.whenYesterday\n if (days < 7) return text.whenDays(days)\n return text.whenWeeks(Math.max(1, Math.floor(days / 7)))\n}\n\nfunction projectIcon(title: string): IconName {\n if (/天文|astro|物理|physics|宇宙|太空/i.test(title)) return 'planet'\n if (/机器学习|machine|math|数学|算法|数据|data|经济|金融|code|编程/i.test(title)) return 'graph'\n return 'book'\n}\n\nfunction statusOf(project: ProjectCardView): 'accepted' | 'active' | 'legacy' {\n if (project.status !== undefined) return project.status\n return project.confirmed > 0 ? 'active' : 'legacy'\n}\n\nfunction StatusPill({ status, text }: { status: 'accepted' | 'active' | 'legacy'; text: DesignText }): ReactNode {\n const label = status === 'accepted' ? text.statusAccepted : status === 'active' ? text.statusActive : text.statusLegacy\n return (\n <span className=\"dtx-pill\" data-tone={status === 'accepted' ? 'green' : status === 'active' ? 'blue' : 'gray'}>\n {status === 'accepted' ? <Icon name=\"check\" size={13} /> : status === 'legacy' ? <Icon name=\"clock\" size={13} /> : <Icon name=\"route\" size={13} />}\n {label}\n </span>\n )\n}\n\nfunction LibraryPage({ library, onOpenChat, onOpen, error }: {\n library: ProjectCardView[]\n onOpenChat?: (() => void) | undefined\n onOpen: (id: string) => void\n error: string | null\n}): ReactNode {\n const text = useDesignText()\n if (error !== null) return <div className=\"dtx-empty-page\"><p className=\"dtx-alert\">{error}</p></div>\n if (library.length === 0) {\n return <div className=\"dtx-empty-page\">\n <div className=\"dtx-empty-icon\"><Icon name=\"book\" size={38} /></div>\n <h1 className=\"dtx-empty-title\">{text.libraryEmptyTitle}</h1>\n <p className=\"dtx-empty-body\">{text.libraryEmptyBody}</p>\n {onOpenChat !== undefined && <button type=\"button\" className=\"dtx-primary\" onClick={onOpenChat}>{text.backToChat}</button>}\n </div>\n }\n return (\n <>\n <section className=\"dtx-card dtx-hero\">\n <p className=\"dtx-eyebrow\">{text.libraryEyebrow}</p>\n <h1>{text.libraryTitle}</h1>\n <p>{text.libraryBody}</p>\n <div className=\"dtx-hero-row\">\n <div className=\"dtx-hero-prompt\">\n <span className=\"dtx-hero-prompt-icon\"><Icon name=\"chat\" size={18} /></span>\n <span>\n <span className=\"dtx-hero-prompt-title\">{text.libraryPromptTitle}</span>\n <span className=\"dtx-hero-prompt-sub\">{text.libraryPromptExample}</span>\n </span>\n </div>\n {onOpenChat !== undefined && <button type=\"button\" className=\"dtx-primary\" onClick={onOpenChat}>{text.backToChat}</button>}\n </div>\n </section>\n <div className=\"dtx-section-head\"><h2>{text.librarySection}</h2></div>\n <div className=\"dtx-project-list\">\n {library.map((project) => {\n const status = statusOf(project)\n return (\n <button key={project.id} type=\"button\" className=\"dtx-project-row\" onClick={() => onOpen(project.id)}>\n <span className=\"dtx-project-icon\" data-kind={projectIcon(project.title)}><Icon name={projectIcon(project.title)} size={28} /></span>\n <span className=\"dtx-project-main\">\n <span className=\"dtx-project-title\">{project.title}</span>\n <span className=\"dtx-project-meta\">{text.lastStudied(whenText(project.updatedAt, text))}</span>\n </span>\n <span className=\"dtx-project-status\"><StatusPill status={status} text={text} /></span>\n <span className=\"dtx-project-action\">{text.continueLearning}<Icon name=\"chevron-right\" size={16} /></span>\n </button>\n )\n })}\n </div>\n </>\n )\n}\n\nfunction statePillTone(state: string): 'green' | 'blue' | 'gray' | 'amber' {\n if (state === 'confirmed' || state === 'checked' || state === 'practiced') return 'green'\n if (state === 'explained') return 'blue'\n if (state === 'weak' || state === 'blocked') return 'amber'\n return 'gray'\n}\n\nfunction NodeIcon({ state }: { state: string }): ReactNode {\n if (state === 'confirmed' || state === 'checked' || state === 'practiced') return <span className=\"dtx-node-icon\" data-state={state}><Icon name=\"check\" size={13} /></span>\n if (state === 'weak' || state === 'blocked') return <span className=\"dtx-node-icon\" data-state={state}><Icon name=\"warning\" size={13} /></span>\n if (state === 'explained') return <span className=\"dtx-node-icon\" data-state={state}><span style={{ width: 6, height: 6, borderRadius: 999, background: 'currentColor' }} /></span>\n return <span className=\"dtx-node-icon\" data-state={state} />\n}\n\nfunction KnowledgeDetail({ detail, loading, error, text, lang }: {\n detail: NodeDetailResponse | null\n loading: boolean\n error: string | null\n text: DesignText\n lang: 'en' | 'zh'\n}): ReactNode {\n if (detail === null) {\n return <div className=\"dtx-knowledge-empty\">\n <div className=\"dtx-rail-empty-icon\"><Icon name=\"layers\" size={28} /></div>\n {error !== null ? <p>{error}</p> : <p>{loading ? text.loading : text.selectNode}</p>}\n </div>\n }\n const node = detail.node\n const prereq = detail.parent !== null && (node.relation === 'prerequisite' || node.relation === 'part-of') ? [detail.parent] : []\n return (\n <>\n <div className=\"dtx-detail-head\">\n <h2>{node.title}</h2>\n <span className=\"dtx-pill\" data-tone={statePillTone(node.state)}>{nodeStateText(node.state, text)}</span>\n </div>\n <p className=\"dtx-detail-desc\">{stateExplanation(node.state, lang)}</p>\n <div className=\"dtx-detail-section\">\n <h3>{text.relatedPosition}</h3>\n <div className=\"dtx-breadcrumb\">\n <Icon name=\"layers\" size={15} />\n {detail.parent !== null && <><span>{detail.parent.title}</span><span>›</span></>}\n <b>{node.title}</b>\n </div>\n </div>\n {prereq.length > 0 && <div className=\"dtx-detail-section\">\n <h3>{text.prerequisiteKnowledge}</h3>\n <div className=\"dtx-chips\">\n {prereq.map((entry) => <span key={entry.id} className=\"dtx-chip\" data-state={entry.state === 'confirmed' ? 'confirmed' : undefined}>\n <NodeIcon state={entry.state} />{entry.title}\n </span>)}\n </div>\n </div>}\n <div className=\"dtx-detail-section\">\n <h3>{text.evidenceTitle(node.evidence.length)}</h3>\n {node.evidence.length === 0 && <p className=\"dtx-muted dtx-small\">{text.noEvidence}</p>}\n <div className=\"dtx-evidence\">\n {[...node.evidence].reverse().map((entry, index) => (\n <div className=\"dtx-evidence-card\" key={index}>\n <span className=\"dtx-evidence-icon\"><Icon name={entry.kind === 'practice' || entry.kind === 'check' ? 'book' : 'chat'} size={17} /></span>\n <span className=\"dtx-evidence-main\">\n <span className=\"dtx-evidence-title\">{evidenceKindLabel(entry.kind, lang)}</span>\n {entry.note !== undefined && entry.note !== '' && <span className=\"dtx-evidence-note\">{entry.note}</span>}\n </span>\n <span className=\"dtx-evidence-side\">\n <span className=\"dtx-pill\" data-tone={statePillTone(node.state)}>{nodeStateText(node.state, text)}</span>\n <span className=\"dtx-evidence-when\">{formatWhen(entry.at, Date.now(), lang)}</span>\n </span>\n </div>\n ))}\n </div>\n </div>\n <div className=\"dtx-info-box\">\n <span className=\"dtx-info-icon\"><Icon name=\"info\" size={17} /></span>\n <span><span className=\"dtx-info-title\">{text.evidenceInfoTitle}</span><span className=\"dtx-info-body\">{text.evidenceInfoBody}</span></span>\n </div>\n </>\n )\n}\n\nfunction KnowledgePage({ nodes, selectedId, detail, loading, error, onSelect }: {\n nodes: NodeView[]\n selectedId: string | null\n detail: NodeDetailResponse | null\n loading: boolean\n error: string | null\n onSelect: (id: string) => void\n}): ReactNode {\n const text = useDesignText()\n const lang = useLang()\n const [query, setQuery] = useState('')\n const filtered = useMemo(() => {\n const needle = query.trim().toLowerCase()\n return needle === '' ? nodes : nodes.filter((node) => node.title.toLowerCase().includes(needle))\n }, [nodes, query])\n return (\n <div className=\"dtx-content\">\n <div className=\"dtx-knowledge\">\n <section className=\"dtx-card dtx-knowledge-list\">\n <header className=\"dtx-knowledge-head\">\n <h2>{text.knowledgeTitle(nodes.length)}</h2>\n <label className=\"dtx-search\">\n <Icon name=\"search\" size={16} />\n <input value={query} onChange={(event) => setQuery(event.target.value)} placeholder={text.searchKnowledge} aria-label={text.searchKnowledge} />\n </label>\n </header>\n <div className=\"dtx-node-list\">\n {filtered.length === 0 && <p className=\"dtx-muted dtx-small\" style={{ padding: '16px 18px' }}>{text.noNodesYet}</p>}\n {filtered.map((node) => (\n <button key={node.id} type=\"button\" className=\"dtx-node-row\" aria-current={node.id === selectedId || undefined} onClick={() => onSelect(node.id)}>\n <NodeIcon state={node.state} />\n <span className=\"dtx-node-main\">\n <span className=\"dtx-node-title\">{node.title}</span>\n <span className=\"dtx-node-sub\">{relationLabel(node.relation, lang)} · {node.evidenceCount}</span>\n </span>\n <span className=\"dtx-pill\" data-tone={statePillTone(node.state)}>{nodeStateText(node.state, text)}</span>\n <span className=\"dtx-node-chevron\"><Icon name=\"chevron-right\" size={16} /></span>\n </button>\n ))}\n </div>\n </section>\n <section className=\"dtx-card dtx-knowledge-detail\">\n <KnowledgeDetail detail={detail} loading={loading} error={error} text={text} lang={lang} />\n </section>\n </div>\n </div>\n )\n}\n\n/**\n * The map page shell.\n */\nexport function LearningMapPanel({\n client,\n initialOverview,\n sessionId,\n onOpenChat,\n onOpenLesson,\n onOpenSession,\n sessionActivity,\n pathClient,\n}: LearningMapPanelProps): ReactNode {\n const text = useDesignText()\n const [page, setPage] = useState<MapView>('curriculum')\n const state = useLearning({ client: client ?? defaultClient, sessionId, initialOverview, onOpenSession })\n const { overview, focus, detail, selectedId, note, error, loading, library } = state\n const reviewClient = pathClient ?? defaultClient.pathClient ?? defaultPathClient\n\n const arrival = selectedId\n const select = state.select\n useEffect(() => {\n if (arrival !== null) return\n const first = overview?.nodes?.[0]?.id\n if (first !== undefined) select(focus?.nodeId ?? first)\n }, [arrival, select, overview, focus])\n\n const enterCurrentLesson = (): void => {\n const courseId = overview?.course?.id\n const resolve = (client ?? defaultClient).resolveProjectSession\n if (courseId !== undefined && sessionId !== undefined && resolve !== undefined && onOpenSession !== undefined) {\n void (async () => {\n try {\n const resolved = await resolve(courseId, sessionId)\n if (resolved.sessionId !== null && resolved.sessionId !== sessionId && onOpenSession(resolved.sessionId)) return\n } catch { /* stay here */ }\n onOpenLesson?.()\n })()\n return\n }\n onOpenLesson?.()\n }\n\n if (overview?.course == null) {\n return (\n <div className=\"dtx-map\">\n <header className=\"dtx-topbar\">\n <div className=\"dtx-topbar-title\"><span className=\"dtx-topbar-page\">{text.pageTitle}</span></div>\n <div className=\"dtx-topbar-actions\"><LanguageToggle /></div>\n </header>\n <div className=\"dtx-scroll\">\n <div className=\"dtx-content\">\n <LibraryPage library={library} onOpenChat={onOpenChat} onOpen={(id) => void state.enterProject(id)} error={error} />\n <footer className=\"dtx-small dtx-muted\" style={{ marginTop: 28 }}><DataFooter onExport={state.exportData} onReset={state.resetAll} /></footer>\n </div>\n </div>\n </div>\n )\n }\n\n const nodes = overview.nodes ?? []\n return (\n <div className=\"dtx-map\">\n <header className=\"dtx-topbar\">\n <div className=\"dtx-topbar-title\">\n <span className=\"dtx-topbar-page\">{text.pageTitle}</span>\n <span className=\"dtx-topbar-divider\" aria-hidden=\"true\" />\n <span className=\"dtx-topbar-project\">{overview.course.title}</span>\n </div>\n <div className=\"dtx-topbar-actions\">\n {onOpenChat !== undefined && <button type=\"button\" className=\"dtx-back\" onClick={onOpenChat}><Icon name=\"arrow-left\" size={16} /><span>{text.backToChat}</span></button>}\n <LanguageToggle />\n </div>\n </header>\n <nav className=\"dtx-tabs\" aria-label={text.pageTitle}>\n <button type=\"button\" className=\"dtx-tabbtn\" aria-current={page === 'curriculum' ? 'page' : undefined} onClick={() => setPage('curriculum')}>{text.tabRoute}</button>\n <button type=\"button\" className=\"dtx-tabbtn\" aria-current={page === 'knowledge' ? 'page' : undefined} onClick={() => setPage('knowledge')}>{text.tabKnowledge}</button>\n <button type=\"button\" className=\"dtx-tabbtn\" aria-current={page === 'review' ? 'page' : undefined} onClick={() => setPage('review')}>{text.tabReview}</button>\n <button type=\"button\" className=\"dtx-tabbtn\" aria-current={page === 'insights' ? 'page' : undefined} onClick={() => setPage('insights')}>{text.tabInsights}</button>\n </nav>\n <div className=\"dtx-scroll\">\n {page === 'curriculum'\n ? <PathPage courseId={overview.course.id} title={overview.course.title} nodes={nodes}\n sessionId={sessionId} client={pathClient} onOpenChat={onOpenChat} tasks={overview.tasks ?? []}\n onStarted={enterCurrentLesson} onOpenLesson={onOpenLesson === undefined ? undefined : enterCurrentLesson}\n onOpenEvidence={() => setPage('knowledge')}\n {...(sessionActivity === undefined ? {} : { sessionActivity })} />\n : page === 'knowledge'\n ? <KnowledgePage nodes={nodes} selectedId={selectedId} detail={detail} loading={loading} error={error} onSelect={select} />\n : page === 'review'\n ? <ReviewPage client={reviewClient} onOpenRoute={() => setPage('curriculum')} />\n : <InsightsPage client={reviewClient} courseId={overview.course.id} onOpenRoute={() => setPage('curriculum')} onOpenReview={() => setPage('review')} />}\n {page === 'curriculum' && <div className=\"dtx-content\" style={{ paddingTop: 0 }}><footer className=\"dtx-small dtx-muted\"><DataFooter onExport={state.exportData} onReset={state.resetAll} /></footer></div>}\n </div>\n </div>\n )\n}\n","/**\n * Live turn state for the conversation the map page is showing.\n *\n * The diagnosis flow wakes the tutor from the map page, but the model turn runs\n * in the chat. Without this, the page can only say \"go answer the questions\" —\n * even while the tutor is still thinking, and even after the turn failed (for\n * example a provider balance error). Reading the Session Controller's snapshot\n * gives the page the two facts it needs: `running` and `lastAgentError`.\n *\n * Read through the bare service name with a structural type, and only when the\n * host provides it: the preview and unit tests have no Session Controller, and\n * they must keep today's behaviour.\n */\n\nimport { useEffect, useState } from 'react'\n\n/** The slice of a session snapshot this plugin needs. */\nexport interface SessionActivity {\n readonly running: boolean\n readonly lastAgentError: string | null\n}\n\n/** The slice of the Session Controller service this plugin needs. */\nexport interface SessionActivitySource {\n binding(sessionId: string): {\n readonly session: {\n getSnapshot(): SessionActivity\n }\n } | undefined\n}\n\n/**\n * Follow the current conversation's turn state.\n *\n * Polls instead of subscribing: the snapshot object is cheap to read, the\n * surface already polls its own path state, and this keeps the structural type\n * to two fields instead of the whole observable contract.\n *\n * @param source - the Session Controller, when the host exposes it.\n * @param sessionId - the conversation the map page is showing.\n * @returns the latest turn state, or `undefined` when it cannot be observed.\n */\nexport function useSessionActivity(\n source: SessionActivitySource | undefined,\n sessionId: string | undefined,\n): SessionActivity | undefined {\n const [activity, setActivity] = useState<SessionActivity | undefined>(undefined)\n\n useEffect(() => {\n if (source === undefined || sessionId === undefined || sessionId.length === 0) {\n setActivity(undefined)\n return\n }\n let live = true\n const read = (): void => {\n try {\n const snapshot = source.binding(sessionId)?.session.getSnapshot()\n if (!live) return\n setActivity((previous) => {\n const next = snapshot === undefined ? undefined : { running: snapshot.running, lastAgentError: snapshot.lastAgentError }\n return previous?.running === next?.running && previous?.lastAgentError === next?.lastAgentError ? previous : next\n })\n } catch {\n // A binding that is not ready yet leaves the last observation in place.\n }\n }\n read()\n const timer = setInterval(read, 1000)\n return () => {\n live = false\n clearInterval(timer)\n }\n }, [source, sessionId])\n\n return activity\n}\n","/** Current course attempt beside chat; curriculum browsing stays in Learning map. */\nimport { useEffect, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { PathView } from '../path.js'\nimport type { PathClient } from './path-client.js'\nimport { DataFooter, LessonBody } from './blocks.js'\nimport { LanguageToggle } from './language-toggle.js'\nimport { useLessonText } from './lesson-text.js'\nimport { PATH_PAUSED_EVENT, PATH_RESUMED_EVENT } from './path-events.js'\n\nexport function useCoursePath(client: PathClient | undefined, courseId: string | undefined, sessionId?: string): PathView | null {\n const scope = `${courseId ?? ''}:${sessionId ?? ''}`\n const [snapshot, setSnapshot] = useState<{ scope: string; view: PathView } | null>(null)\n useEffect(() => {\n if (!client || !courseId) return\n let live = true\n let pending = false\n const refresh = async () => {\n if (pending) return\n pending = true\n try {\n const view = await client.get(courseId, sessionId)\n if (live && view.path.courseId === courseId && (!view.run || view.run.sessionId === sessionId)) setSnapshot((old) => old?.scope === scope && old.view.path.revision > view.path.revision ? old : { scope, view })\n } catch { /* Existing node-only learning remains usable when planning is unavailable. */ }\n finally { pending = false }\n }\n void refresh()\n const timer = setInterval(() => { void refresh() }, 2000)\n const signal = () => { void refresh() }\n window.addEventListener(PATH_PAUSED_EVENT, signal)\n window.addEventListener(PATH_RESUMED_EVENT, signal)\n return () => {\n live = false\n clearInterval(timer)\n window.removeEventListener(PATH_PAUSED_EVENT, signal)\n window.removeEventListener(PATH_RESUMED_EVENT, signal)\n }\n }, [client, courseId, scope, sessionId])\n return snapshot?.scope === scope ? snapshot.view : null\n}\n\nexport function CourseLearning({ view, title, compact = false, onOpenMap, onOpenChat, onExport, onReset }: {\n view: PathView; title: string; compact?: boolean; onOpenMap?: (() => void) | undefined;\n onOpenChat?: (() => void) | undefined; onExport: () => void; onReset: () => void;\n}): ReactNode {\n const text = useLessonText()\n const attempt = view.run?.attempts.find((entry) => entry.id === view.run?.activeAttemptId)\n const route = view.path.curricula.find((entry) => entry.version === (attempt?.version ?? view.path.acceptedVersion))\n const index = route?.items.findIndex((entry) => entry.id === attempt?.itemId) ?? -1\n const item = route?.items[index]\n const module = route?.modules.find((entry) => entry.id === item?.moduleId)\n // Replace supersedes a displayed node's unit, not its stored historical snapshots.\n const latest = new Map<string, NonNullable<typeof attempt>['units'][number]>()\n for (const unit of attempt?.units ?? []) latest.set(unit.lesson.nodeId, unit)\n const content = <>\n <header className=\"dt-course-card\">\n <p className=\"dt-eyebrow\">{title}</p>\n <h2 className=\"dt-course-title\">{item?.title ?? text.learningRoute}</h2>\n {item && route && <p className=\"dt-shared\">{module?.title} · {text.lesson} {index + 1} {text.lessonOf} {route.items.length} · {text.routeVersion}{route.version}</p>}\n {attempt && <p className=\"dt-caption\">{attempt.status === 'completed' ? text.lessonCompleted : attempt.status === 'interrupted' ? text.lessonPaused : text.currentLesson}</p>}\n {attempt && view.path.acceptedVersion !== attempt.version && <p className=\"dt-caption\">{text.newerRoutePrefix}{attempt.version}{text.newerRouteSuffix}</p>}\n {onOpenMap && <button className=\"dt-secondary\" type=\"button\" onClick={onOpenMap}>{text.openCourseRoute}</button>}{' '}\n <LanguageToggle />\n </header>\n {latest.size ? [...latest.values()].map((unit) => <article key={unit.lesson.nodeId} className=\"dt-lesson-body dt-course-unit\">\n <div className=\"dt-lesson-head\"><h3 className=\"dt-lesson-title\">{unit.lesson.title}</h3><span className=\"dt-origin\">{text.tutorOrigin}</span></div>\n <LessonBody blocks={unit.lesson.blocks} />\n </article>) : <div className=\"dt-empty-state\">\n <p className=\"dt-empty-title\">{attempt ? text.preparingTitle : route ? text.routeReadyTitle : text.buildRouteTitle}</p>\n <p className=\"dt-empty\">{attempt ? text.preparingBody : route ? text.routeReadyBody : text.buildRouteBody}</p>\n {attempt && !attempt.prompted && <p className=\"dt-notice\">{attempt.promptReason ?? text.promptPending}{text.promptRetry}</p>}\n </div>}\n {attempt?.outcome && <div className=\"dt-next\"><p className=\"dt-next-label\">{text.lessonOutcome}</p><p>{attempt.outcome}</p><p className=\"dt-caption\">{text.outcomeNote}</p>{onOpenMap && <button type=\"button\" className=\"dt-primary\" onClick={onOpenMap}>{text.chooseNextLesson}</button>}</div>}\n {onOpenChat && <button type=\"button\" className=\"dt-secondary\" onClick={onOpenChat}>{text.answerInChat}</button>}\n <DataFooter onExport={onExport} onReset={onReset} />\n </>\n return compact ? <div className=\"dt-tab\">{content}</div> : <div className=\"dt-root-wrap\"><div className=\"dt-course-reading\">{content}</div></div>\n}\n","/**\n * Which conversation is the learner actually looking at.\n *\n * This is not a detail. The panel wakes an agent when the learner presses\n * *Start learning*, and waking the wrong conversation is worse than waking\n * none: the tutor answers somewhere the learner is not, and the panel looks\n * broken while a different chat quietly changes.\n *\n * The id is **not** available from the slot props: `main` and\n * `sidebar.panellist` are root-scoped, and only session-scoped seats receive\n * `sessionId`. The supported root-scope route is the session scope adapter,\n * whose `current` binding carries the scope identity as `key` — `undefined`\n * when nothing is selected. `ui-session` exposes exactly that as\n * `ctx.get('uiSession').adapter`.\n *\n * The earlier version of this file did not exist, and the panel used\n * `state.ids[0]` — the first row of the session list. That is a different\n * session as soon as anything newer exists, which is most of the time.\n */\n\nimport { useSyncExternalStore } from 'react'\n\n/** The part of the session scope adapter this hook reads. */\ninterface ScopeAdapterLike {\n readonly current: {\n getSnapshot(): { readonly key?: string | undefined }\n subscribe(fn: () => void): () => void\n }\n}\n\n/**\n * The id of the conversation the learner has open.\n *\n * @param uiSession - `ctx.get('uiSession')`, when the profile mounts it.\n * @returns the session id, or `undefined` when no conversation is selected.\n */\nexport function useCurrentSessionId(uiSession: unknown): string | undefined {\n const adapter = (uiSession as { adapter?: ScopeAdapterLike } | undefined)?.adapter\n // Hooks cannot be called conditionally, so the subscription is always set up\n // and simply reports nothing when there is no adapter to read.\n const key = useSyncExternalStore(\n (onChange) => adapter?.current.subscribe(onChange) ?? (() => {}),\n () => adapter?.current.getSnapshot().key,\n () => undefined,\n )\n return typeof key === 'string' && key.length > 0 ? key : undefined\n}\n","/**\n * The docked Current lesson tab.\n *\n * One lesson, beside the chat: the teaching material the tutor wrote for the\n * active attempt, the session's progress, and the green \"answer in the chat\"\n * card. The planning map lives in the Learning map page; this surface stays\n * narrow and only renders the lesson itself.\n */\n\nimport { useEffect, useMemo, useState, type ReactNode } from 'react'\n\nimport type { CourseAttempt, Curriculum, CurriculumItem } from '../path.js'\nimport { LessonBody } from './blocks.jsx'\nimport type { BlockInteraction } from './blocks.jsx'\nimport { isKnownBlock } from '../block-types.js'\nimport { Icon } from './design-icons.jsx'\nimport { useDesignText } from './design-text.js'\nimport { LanguageToggle } from './language-toggle.js'\nimport { useLang } from './i18n.js'\nimport { useCoursePath } from './course-learning.js'\nimport type { PanelClient } from './use-learning.js'\nimport { defaultClient, useLearning } from './use-learning.js'\nimport { useCurrentSessionId } from './use-session.js'\nimport { announcePathState, PATH_PAUSED_EVENT, PATH_RESUMED_EVENT } from './path-events.js'\nimport type { OverviewResponse } from '../contract.js'\n\n/** The tab chip's text. */\nexport function LearningTabTitle(): ReactNode {\n return useDesignText().tabTitle\n}\n\nexport interface LearningTabProps {\n client?: PanelClient\n sessionId?: string | undefined\n uiSession?: unknown\n initialOverview?: OverviewResponse | undefined\n onOpenMapPage?: (() => void) | undefined\n onOpenSession?: ((sessionId: string) => boolean) | undefined\n /** Return to the conversation, where the tutor actually teaches. */\n onOpenChat?: (() => void) | undefined\n}\n\nfunction duration(value: number, lang: 'en' | 'zh'): string {\n if (value < 120) return lang === 'zh' ? `${value} 分钟` : `${value} min`\n return lang === 'zh' ? `${Number((value / 60).toFixed(1))} 小时` : `${Number((value / 60).toFixed(1))} h`\n}\nfunction codeFor(plan: Curriculum, item: CurriculumItem): string {\n const moduleIndex = plan.modules.findIndex((module) => module.id === item.moduleId)\n const within = plan.items.filter((entry) => entry.moduleId === item.moduleId).findIndex((entry) => entry.id === item.id)\n return `${moduleIndex + 1}.${within + 1}`\n}\nfunction attemptFor(attempts: readonly CourseAttempt[], activeId: string | null): CourseAttempt | undefined {\n return attempts.find((attempt) => attempt.id === activeId)\n}\n\nfunction EmptyTab({ title, body, action }: { title: string; body: string; action?: { label: string; run: () => void } | undefined }): ReactNode {\n return (\n <div className=\"dtx-tab\">\n <div className=\"dtx-tab-inner dtx-tab-empty\">\n <span className=\"dtx-tab-empty-icon\"><Icon name=\"book\" size={32} /></span>\n <h2>{title}</h2>\n <p>{body}</p>\n {action !== undefined && <button type=\"button\" className=\"dtx-primary\" onClick={action.run}>{action.label}</button>}\n <div style={{ marginTop: 18 }}><LanguageToggle /></div>\n </div>\n </div>\n )\n}\n\n/**\n * The docked learning surface.\n */\nexport function LearningTab({\n client,\n sessionId,\n uiSession,\n initialOverview,\n onOpenMapPage,\n onOpenSession,\n onOpenChat,\n}: LearningTabProps): ReactNode {\n const text = useDesignText()\n const lang = useLang()\n const liveSession = useCurrentSessionId(uiSession)\n const activeSession = uiSession === undefined ? sessionId : liveSession\n const learning = useLearning({ client: client ?? defaultClient, sessionId: activeSession, initialOverview, onOpenSession })\n const courseId = learning.overview?.course?.id\n const view = useCoursePath((client ?? defaultClient).pathClient, courseId, activeSession)\n const attempt = useMemo(() => {\n const active = view?.run?.attempts.find((entry) => entry.id === view.run?.activeAttemptId)\n return active ?? attemptFor(view?.run?.attempts ?? [], view?.run?.activeAttemptId ?? null)\n }, [view])\n const route = useMemo(() => {\n if (view === null) return null\n const version = attempt?.version ?? view.path.acceptedVersion\n return view.path.curricula.find((entry) => entry.version === version) ?? null\n }, [view, attempt])\n const item = route?.items.find((entry) => entry.id === attempt?.itemId) ?? null\n const module = route?.modules.find((entry) => entry.id === item?.moduleId) ?? null\n const units = useMemo(() => {\n const latest = new Map<string, NonNullable<typeof attempt>['units'][number]>()\n for (const unit of attempt?.units ?? []) latest.set(unit.lesson.nodeId, unit)\n return [...latest.values()]\n }, [attempt])\n const done = view === null ? 0 : new Set((view.run?.attempts ?? []).filter((entry) => entry.status === 'completed' && entry.version === (attempt?.version ?? view.path.acceptedVersion)).map((entry) => entry.itemId)).size\n const pathClient = (client ?? defaultClient).pathClient\n const [busy, setBusy] = useState(false)\n const [notice, setNotice] = useState('')\n const [pausedLocal, setPausedLocal] = useState(false)\n const operationId = () => typeof globalThis.crypto?.randomUUID === 'function' ? globalThis.crypto.randomUUID() : `tab-${Date.now().toString(36)}`\n const nudge = async (kind: 'continue' | 'checkin' | 'teachback') => {\n if (pathClient === undefined || view === null || attempt === undefined || courseId === undefined || activeSession === undefined) return\n setBusy(true); setNotice('')\n try {\n const result = await pathClient.start({\n sessionId: activeSession, courseId, expectedRevision: view.path.revision,\n version: attempt.version, itemId: attempt.itemId, operationId: operationId(),\n resume: true, ...(kind === 'checkin' ? { checkin: true } : {}), ...(kind === 'teachback' ? { teachBack: true } : {}),\n })\n if (!result.prompted) setNotice(result.promptReason ?? text.tabTutorUnreachable)\n else { announcePathState(false); setPausedLocal(false); onOpenChat?.() }\n } catch (error) {\n setNotice(error instanceof Error ? error.message : text.tabTutorUnreachable)\n } finally { setBusy(false) }\n }\n const pause = async () => {\n if (pathClient?.pause === undefined || view === null || courseId === undefined || activeSession === undefined) return\n setBusy(true); setNotice('')\n try {\n await pathClient.pause({ sessionId: activeSession, courseId, expectedRevision: view.path.revision })\n announcePathState(true)\n setPausedLocal(true)\n setNotice(text.lessonPausedNotice)\n } catch (error) {\n setNotice(error instanceof Error ? error.message : text.tabPauseFailed)\n } finally { setBusy(false) }\n }\n\n useEffect(() => {\n if (attempt?.status === 'interrupted') setPausedLocal(true)\n else if (attempt?.status === 'working' || attempt?.status === 'requested') setPausedLocal(false)\n }, [attempt?.status])\n useEffect(() => {\n const paused = () => setPausedLocal(true)\n const resumed = () => setPausedLocal(false)\n window.addEventListener(PATH_PAUSED_EVENT, paused)\n window.addEventListener(PATH_RESUMED_EVENT, resumed)\n return () => {\n window.removeEventListener(PATH_PAUSED_EVENT, paused)\n window.removeEventListener(PATH_RESUMED_EVENT, resumed)\n }\n }, [])\n\n const blockInteraction: BlockInteraction | undefined = attempt === undefined || view === null || pathClient?.answer === undefined || courseId === undefined || activeSession === undefined\n ? undefined\n : {\n attemptId: attempt.id,\n responses: attempt.responses ?? [],\n ...(pathClient.source === undefined ? {} : {\n loadSource: async (sourceId: string) => (await pathClient.source!(sourceId)).source,\n }),\n submit: async (blockId, answers) => {\n try {\n await pathClient.answer!({\n sessionId: activeSession, courseId, expectedRevision: view.path.revision,\n attemptId: attempt.id, blockId, answers,\n })\n } catch (error) {\n setNotice(error instanceof Error ? error.message : text.answerFailed)\n throw error\n }\n },\n }\n\n if (courseId === undefined) {\n return <EmptyTab title={text.tabEmptyTitle} body={text.tabEmptyBody} action={onOpenMapPage === undefined ? undefined : { label: text.openLearningMap, run: onOpenMapPage }} />\n }\n if (view === null) {\n return <EmptyTab title={text.lessonWaiting} body={text.lessonWaitingBody} action={onOpenMapPage === undefined ? undefined : { label: text.openLearningMap, run: onOpenMapPage }} />\n }\n if (attempt === undefined || route === null || item === null) {\n return <EmptyTab title={text.emptyLessonTitle} body={text.emptyLessonBody} action={onOpenMapPage === undefined ? undefined : { label: text.openLearningMap, run: onOpenMapPage }} />\n }\n\n const paused = pausedLocal || attempt.status === 'interrupted'\n const status = attempt.status === 'completed' ? text.lessonComplete : paused ? text.lessonInterrupted : text.lessonStatusCurrent\n const tone = attempt.status === 'completed' ? 'green' : paused ? 'amber' : 'blue'\n return (\n <div className=\"dtx-tab\">\n <div className=\"dtx-tab-inner\">\n <h2>{item.title}</h2>\n <p className=\"dtx-tab-head-meta\">{learning.overview?.course?.title ?? ''}{module !== null ? ` · ${module.title}` : ''} · {codeFor(route, item)}</p>\n <div className=\"dtx-tab-pills\">\n <span className=\"dtx-pill\" data-tone={tone}>{status}</span>\n <span className=\"dtx-pill\" data-tone=\"gray\">{text.routeLabel(route.version)}</span>\n <span className=\"dtx-pill\" data-tone=\"gray\">{text.unitsWritten(attempt.units.length)}</span>\n </div>\n <p className=\"dtx-tab-teachhint\">{text.teacherLeads} {text.teacherLeadsHint}</p>\n <div className=\"dtx-tab-progress\"><span>{text.sessionProgress(done, route.items.length)}</span><span>{text.routeLabel(route.version)}</span></div>\n <div className=\"dtx-tab-actions\">\n {paused\n ? <>\n <button type=\"button\" className=\"dtx-primary\" disabled={busy} onClick={() => void nudge('continue')}>{text.resumeLesson}</button>\n {onOpenChat !== undefined && <button type=\"button\" className=\"dtx-link\" onClick={onOpenChat}>{text.backToChat}</button>}\n </>\n : <>\n <button type=\"button\" className=\"dtx-primary\" disabled={busy || attempt.status === 'completed'} onClick={() => void nudge('continue')}>{text.resumeTeaching}</button>\n <button type=\"button\" className=\"dtx-secondary\" disabled={busy || attempt.status === 'completed' || attempt.units.length === 0} onClick={() => void nudge('teachback')}>{text.explainBack}</button>\n <button type=\"button\" className=\"dtx-secondary\" disabled={busy || attempt.status === 'completed'} onClick={() => void nudge('checkin')}>{text.checkIn}</button>\n {attempt.status !== 'completed' && pathClient?.pause !== undefined && <button type=\"button\" className=\"dtx-link\" disabled={busy} onClick={() => void pause()}>{text.pauseLesson}</button>}\n {onOpenChat !== undefined && <button type=\"button\" className=\"dtx-link\" onClick={onOpenChat}>{text.backToChat}</button>}\n </>}\n </div>\n {notice !== '' ? <p className=\"dtx-tab-notice\">{notice}</p>\n : paused ? <p className=\"dtx-tab-notice\">{text.lessonPausedResumeHint}</p> : null}\n <div className=\"dtx-tab-divider\" />\n {units.length === 0\n ? <div className=\"dtx-tab-empty\" style={{ minHeight: 260 }}>\n <span className=\"dtx-tab-empty-icon\"><Icon name=\"spark\" size={28} /></span>\n <h2>{text.lessonWaiting}</h2>\n <p>{text.lessonWaitingBody}</p>\n {onOpenMapPage !== undefined && <button type=\"button\" className=\"dtx-secondary\" onClick={onOpenMapPage}>{text.openLearningMap}</button>}\n </div>\n : <>\n <h3 className=\"dtx-tab-section\"><span className=\"dtx-section-icon\"><Icon name=\"book\" size={16} /></span>{text.lessonContents}</h3>\n {units.map((unit) => <article key={unit.lesson.nodeId}>\n {units.length > 1 && <h3 className=\"dtx-tab-section\">{unit.lesson.title}</h3>}\n {unit.lesson.blocks.map((block) => isKnownBlock(block) && block.type === 'diagram'\n ? <details className=\"dtx-tab-fold\" key={block.id}>\n <summary>{text.lessonDiagram}{block.content.caption !== undefined ? ` · ${block.content.caption}` : ''}</summary>\n <LessonBody blocks={[block]} interaction={blockInteraction} />\n </details>\n : <LessonBody key={block.id} blocks={[block]} interaction={blockInteraction} />)}\n </article>)}\n </>}\n {attempt.outcome !== null && <div className=\"dtx-tab-outcome\"><b>{text.outcomeLabel}</b>{attempt.outcome}</div>}\n {onOpenMapPage !== undefined && <div className=\"dtx-tab-linkrow\" style={{ marginTop: 18 }}>\n <button type=\"button\" className=\"dtx-secondary\" onClick={onOpenMapPage}><Icon name=\"map\" size={15} />{text.openLearningMap}</button>\n <LanguageToggle />\n </div>}\n </div>\n </div>\n )\n}\n","/** Design-system CSS for the Learning map / route / knowledge / lesson surfaces. */\nexport const DESIGN_CSS = `\n/* ---------- shell ---------- */\n.dtx-map { display:flex; flex-direction:column; height:100%; min-height:0; background:var(--dt-sunken,#f5f6f8); color:var(--dt-text,#1c1f23); font-size:13px; line-height:1.55; text-align:left; }\n.dtx-map * { box-sizing:border-box; }\n.dtx-map button { font:inherit; cursor:pointer; }\n.dtx-map button:disabled { cursor:default; opacity:.5; }\n.dtx-map :focus-visible { outline:2px solid var(--dt-brand,#4d6bfe); outline-offset:2px; }\n.dtx-topbar { flex:none; height:64px; display:flex; align-items:center; gap:14px; padding:0 30px; background:var(--dt-bg,#fff); border-bottom:1px solid var(--dt-border,#e8eaee); }\n.dtx-topbar-title { display:flex; align-items:center; gap:14px; min-width:0; }\n.dtx-topbar-page { font-size:17px; font-weight:700; letter-spacing:-.01em; white-space:nowrap; }\n.dtx-topbar-divider { width:1px; height:22px; background:var(--dt-border-strong,#d7dbe2); }\n.dtx-topbar-project { font-size:16px; color:var(--dt-text,#1c1f23); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n.dtx-topbar-actions { margin-left:auto; display:flex; align-items:center; gap:12px; }\n.dtx-back { display:inline-flex; align-items:center; gap:7px; height:36px; padding:0 14px; border:1px solid var(--dt-border,#dfe3ea); border-radius:10px; background:var(--dt-bg,#fff); color:var(--dt-text,#1c1f23); font-size:13px !important; }\n.dtx-back:hover { background:var(--dt-surface,#f7f8fa); }\n.dtx-tabs { flex:none; display:flex; gap:28px; padding:0 30px; background:var(--dt-bg,#fff); border-bottom:1px solid var(--dt-border,#e8eaee); }\n.dtx-tabbtn { position:relative; height:52px; display:inline-flex; align-items:center; padding:0; border:0; background:transparent; color:var(--dt-muted,#6b7280); font-size:13.5px; font-weight:500; }\n.dtx-tabbtn[aria-current] { color:var(--dt-brand-ink,#3155f5); font-weight:600; }\n.dtx-tabbtn[aria-current]::after { content:''; position:absolute; left:0; right:0; bottom:0; height:3px; border-radius:3px 3px 0 0; background:var(--dt-brand,#4d6bfe); }\n.dtx-scroll { flex:1; min-height:0; overflow:auto; overscroll-behavior:contain; }\n.dtx-content { width:100%; max-width:1280px; margin:0 auto; padding:20px 28px 44px; }\n.dtx-muted { color:var(--dt-muted,#6b7280); }\n.dtx-small { font-size:12px; }\n.dtx-eyebrow { margin:0; font-size:12px; font-weight:600; letter-spacing:.02em; color:var(--dt-muted,#6b7280); }\n.dtx-card { background:var(--dt-bg,#fff); border:1px solid var(--dt-border,#e8eaee); border-radius:14px; }\n.dtx-iconbtn { display:inline-flex; align-items:center; justify-content:center; width:34px; height:34px; padding:0; border:0; border-radius:8px; background:transparent; color:var(--dt-muted,#6b7280); }\n.dtx-iconbtn:hover { background:var(--dt-surface,#f3f4f6); color:var(--dt-text,#1c1f23); }\n.dtx-primary { display:inline-flex; align-items:center; justify-content:center; gap:8px; min-height:44px; padding:0 20px; border:1px solid var(--dt-accent,#111827); border-radius:10px; background:var(--dt-accent,#111827); color:var(--dt-on-accent,#fff); font-size:13.5px !important; font-weight:600; }\n.dtx-primary:hover:not(:disabled) { filter:brightness(.94); }\n.dtx-secondary { display:inline-flex; align-items:center; justify-content:center; gap:8px; min-height:42px; padding:0 18px; border:1px solid var(--dt-border,#dfe3ea); border-radius:10px; background:var(--dt-bg,#fff); color:var(--dt-text,#1c1f23); font-size:13px !important; }\n.dtx-secondary:hover:not(:disabled) { background:var(--dt-surface,#f7f8fa); }\n.dtx-link { display:inline-flex; align-items:center; gap:5px; padding:0; border:0; background:transparent; color:var(--dt-brand-ink,#3155f5); font-size:12.5px !important; font-weight:500; }\n.dtx-link:hover { text-decoration:underline; }\n.dtx-pill { display:inline-flex; align-items:center; gap:5px; height:26px; padding:0 10px; border-radius:999px; border:1px solid transparent; font-size:12px; font-weight:600; white-space:nowrap; }\n.dtx-pill[data-tone=green] { color:#15803d; background:#ecfdf3; border-color:#bbf7d0; }\n.dtx-pill[data-tone=blue] { color:#3155f5; background:#eef2ff; border-color:#c7d2fe; }\n.dtx-pill[data-tone=gray] { color:#4b5563; background:#f3f4f6; border-color:#e5e7eb; }\n.dtx-pill[data-tone=amber] { color:#a16207; background:#fffbeb; border-color:#fde68a; }\n.dtx-pill[data-tone=red] { color:#b91c1c; background:#fef2f2; border-color:#fecaca; }\n.dtx-empty-page { min-height:calc(100vh - 190px); display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding:40px 20px; }\n.dtx-empty-icon { display:flex; align-items:center; justify-content:center; width:92px; height:92px; border-radius:20px; background:var(--dt-surface,#f1f2f4); color:#6b7280; margin-bottom:22px; }\n.dtx-empty-title { margin:0 0 8px; font-size:24px; letter-spacing:-.02em; }\n.dtx-empty-body { margin:0 0 22px; color:var(--dt-muted,#6b7280); font-size:13.5px; }\n\n/* ---------- library ---------- */\n.dtx-hero { padding:22px 24px; }\n.dtx-hero h1 { margin:6px 0 8px; font-size:26px; letter-spacing:-.025em; line-height:1.25; }\n.dtx-hero p { margin:0; color:var(--dt-muted,#6b7280); font-size:13.5px; }\n.dtx-hero-row { display:flex; align-items:stretch; gap:16px; margin-top:18px; }\n.dtx-hero-prompt { flex:1; min-width:0; display:flex; align-items:center; gap:16px; padding:18px 20px; border-radius:12px; background:var(--dt-surface,#f7f8fa); border:1px solid var(--dt-border,#eceef2); color:var(--dt-muted,#6b7280); }\n.dtx-hero-prompt .dtx-hero-prompt-icon { display:flex; align-items:center; justify-content:center; width:38px; height:38px; border-radius:50%; border:1px solid #cfd4dd; color:#4b5563; flex:none; }\n.dtx-hero-prompt-title { display:block; color:var(--dt-text,#1c1f23); font-size:13.5px; font-weight:600; }\n.dtx-hero-prompt-sub { display:block; font-size:12.5px; margin-top:2px; }\n.dtx-section-head { display:flex; align-items:center; justify-content:space-between; gap:16px; margin:22px 0 12px; }\n.dtx-section-head h2 { margin:0; font-size:17px; letter-spacing:-.01em; }\n.dtx-project-list { display:flex; flex-direction:column; gap:10px; }\n.dtx-project-row { display:flex; align-items:center; gap:14px; padding:13px 16px; background:var(--dt-bg,#fff); border:1px solid var(--dt-border,#e8eaee); border-radius:12px; text-align:left; color:inherit; }\n.dtx-project-row:hover { border-color:#cfd6e2; box-shadow:0 2px 10px rgba(15,23,42,.04); }\n.dtx-project-icon { display:flex; align-items:center; justify-content:center; width:48px; height:48px; border-radius:11px; flex:none; }\n.dtx-project-icon[data-kind=graph] { background:#e9f9f0; color:#15803d; }\n.dtx-project-icon[data-kind=planet] { background:#eef2ff; color:#3155f5; }\n.dtx-project-icon[data-kind=book] { background:#f3f4f6; color:#4b5563; }\n.dtx-project-main { min-width:0; display:flex; flex-direction:column; gap:3px; }\n.dtx-project-title { font-size:16px; font-weight:700; letter-spacing:-.01em; color:var(--dt-text,#1c1f23); }\n.dtx-project-meta { font-size:12.5px; color:var(--dt-muted,#6b7280); }\n.dtx-project-status { margin-left:auto; }\n.dtx-project-action { margin-left:6px; display:inline-flex; align-items:center; justify-content:center; gap:6px; height:36px; padding:0 14px; border:1px solid #c7d2fe; border-radius:9px; background:#eef2ff; color:#3155f5; font-size:12.5px !important; font-weight:600; flex:none; }\n.dtx-project-action:hover { background:#e0e7ff; }\n\n/* ---------- stage bar ---------- */\n.dtx-stagebar { display:flex; align-items:center; gap:8px; padding:11px 14px; min-width:0; overflow-x:auto; scrollbar-width:none; background:var(--dt-bg,#fff); border:1px solid var(--dt-border,#e8eaee); border-radius:12px; }\n.dtx-stagebar::-webkit-scrollbar { display:none; }\n.dtx-stage { display:flex; align-items:center; gap:10px; min-width:0; }\n.dtx-stage-num { display:inline-flex; align-items:center; justify-content:center; width:30px; height:30px; border-radius:50%; flex:none; border:1px solid #d3d8e0; background:#f3f4f6; color:#6b7280; font-size:12.5px; font-weight:700; }\n.dtx-stage-label { font-size:13.5px; font-weight:500; color:#9aa1ac; white-space:nowrap; }\n.dtx-stage[data-state=done] .dtx-stage-num { background:#22a06b; border-color:#22a06b; color:#fff; }\n.dtx-stage[data-state=done] .dtx-stage-label { color:var(--dt-text,#1c1f23); font-weight:600; }\n.dtx-stage[data-state=done] .dtx-stage-check { color:#22a06b; }\n.dtx-stage[data-state=current] .dtx-stage-num { background:var(--dt-brand,#4d6bfe); border-color:var(--dt-brand,#4d6bfe); color:#fff; box-shadow:0 0 0 4px rgba(77,107,254,.12); }\n.dtx-stage[data-state=current] .dtx-stage-label { color:var(--dt-brand-ink,#3155f5); font-weight:600; }\n.dtx-stage-line { flex:1 1 40px; min-width:24px; height:1px; background:#d8dce3; }\n.dtx-stagebar .dtx-stage-line:last-child { display:none; }\n\n/* ---------- route layout ---------- */\n.dtx-route { display:flex; align-items:flex-start; gap:22px; }\n.dtx-main { flex:1; min-width:0; display:flex; flex-direction:column; gap:16px; }\n.dtx-rail { flex:0 0 330px; width:330px; position:sticky; top:18px; display:flex; flex-direction:column; gap:16px; }\n.dtx-notice { padding:10px 14px; border-radius:10px; font-size:12.5px; background:#eef2ff; border:1px solid #c7d2fe; color:#3155f5; }\n.dtx-alert { padding:10px 14px; border-radius:10px; font-size:12.5px; background:#fef2f2; border:1px solid #fecaca; color:#b91c1c; display:flex; align-items:center; gap:10px; flex-wrap:wrap; }\n.dtx-alert .dtx-link { color:#b91c1c; }\n.dtx-status { padding:8px 12px; border-radius:10px; font-size:12px; background:var(--dt-surface,#f7f8fa); color:var(--dt-muted,#6b7280); }\n\n/* ---------- next step / special states ---------- */\n.dtx-next { padding:18px 20px; border-radius:12px; border:1px solid #c7d2fe; background:#f5f7ff; display:flex; align-items:center; gap:18px; }\n.dtx-next-copy { flex:1; min-width:0; }\n.dtx-next-eyebrow { margin:0 0 6px; color:var(--dt-brand-ink,#3155f5); font-size:12.5px; font-weight:600; }\n.dtx-next h2 { margin:0 0 5px; font-size:21px; line-height:1.3; letter-spacing:-.02em; color:var(--dt-text,#1c1f23); }\n.dtx-next-body { margin:0; color:var(--dt-muted,#6b7280); font-size:13.5px; }\n.dtx-next-note { margin:7px 0 0; color:var(--dt-muted,#6b7280); font-size:12px; }\n.dtx-next-actions { flex:none; display:flex; flex-direction:column; align-items:stretch; gap:8px; min-width:172px; }\n.dtx-next-actions .dtx-primary { min-height:46px; }\n.dtx-next-actions .dtx-secondary { min-height:40px; }\n.dtx-next-link { align-self:center; margin-top:2px; }\n.dtx-next[data-kind=waiting] { border-color:#dbe3f4; background:#f7f9fc; }\n.dtx-wait-box { margin-top:16px; display:flex; align-items:center; gap:14px; padding:16px 18px; border:1px solid #dfe6f3; border-radius:12px; background:var(--dt-bg,#fff); }\n.dtx-spinner { width:28px; height:28px; flex:none; border-radius:50%; border:3px solid #dbe4f7; border-top-color:var(--dt-brand,#4d6bfe); animation:dtx-spin 1s linear infinite; }\n@keyframes dtx-spin { to { transform:rotate(360deg); } }\n.dtx-wait-title { font-size:13.5px; font-weight:600; }\n.dtx-wait-sub { font-size:12px; color:var(--dt-muted,#6b7280); margin-top:2px; }\n.dtx-next[data-kind=error] { background:#fff; border-color:#fecaca; }\n.dtx-error-box { margin-top:16px; padding:16px 18px; border-radius:12px; border:1px solid #fecaca; background:#fff1f2; }\n.dtx-error-title { display:flex; align-items:center; gap:8px; color:#b91c1c; font-size:13.5px; font-weight:700; }\n.dtx-error-body { color:#b91c1c; font-size:12.5px; margin-top:4px; }\n.dtx-next[data-kind=version] { background:#fff; border-color:#fde68a; }\n.dtx-version-box { margin-top:16px; padding:14px 16px; border-radius:12px; border:1px solid #fde68a; background:#fffbeb; }\n.dtx-version-title { color:#a16207; font-size:13px; font-weight:700; display:flex; gap:8px; align-items:center; }\n.dtx-version-body { color:#92400e; font-size:12px; margin-top:4px; }\n.dtx-next[data-kind=complete] { background:#f3fbf6; border-color:#bbf7d0; }\n.dtx-complete-icon { display:flex; align-items:center; justify-content:center; width:52px; height:52px; border-radius:50%; background:#22a06b; color:#fff; flex:none; }\n.dtx-proposal { display:flex; align-items:center; gap:16px; padding:18px 20px; border-radius:12px; border:1px solid #c7d2fe; background:#f5f7ff; }\n.dtx-proposal-icon { display:flex; align-items:center; justify-content:center; width:48px; height:48px; border-radius:12px; background:#e5eaff; color:#3155f5; flex:none; }\n.dtx-proposal-copy { flex:1; min-width:0; }\n.dtx-proposal-copy h2 { margin:0 0 4px; font-size:18px; letter-spacing:-.015em; }\n.dtx-proposal-copy p { margin:0; color:var(--dt-muted,#6b7280); font-size:12.5px; }\n.dtx-proposal-stats { display:flex; align-items:center; gap:18px; padding:0 20px; border-left:1px solid #dbe3f4; flex:none; }\n.dtx-stat-label { font-size:11.5px; color:var(--dt-muted,#6b7280); }\n.dtx-stat-value { font-size:13px; font-weight:600; color:var(--dt-text,#1c1f23); }\n.dtx-proposal-actions { flex:none; display:flex; flex-direction:column; gap:6px; align-items:stretch; }\n.dtx-proposal-actions .dtx-primary { min-height:42px; }\n.dtx-proposal-actions .dtx-link { align-self:center; }\n\n/* ---------- goal form ---------- */\n.dtx-form-card { padding:20px 22px; }\n.dtx-form-head { display:flex; align-items:flex-start; justify-content:space-between; gap:18px; }\n.dtx-form-head h2 { margin:0 0 5px; font-size:22px; letter-spacing:-.025em; }\n.dtx-form-head p { margin:0; color:var(--dt-muted,#6b7280); font-size:13px; }\n.dtx-form { display:flex; flex-direction:column; gap:16px; margin-top:20px; }\n.dtx-field label { display:block; font-size:13px; font-weight:700; margin-bottom:8px; }\n.dtx-input { display:block; width:100%; min-height:46px; padding:11px 14px; border:1px solid #dfe3ea; border-radius:10px; background:var(--dt-bg,#fff); color:var(--dt-text,#1c1f23); font:inherit; font-size:13.5px; }\ntextarea.dtx-input { resize:vertical; min-height:78px; line-height:1.6; }\n.dtx-input:focus { outline:none; border-color:var(--dt-brand,#4d6bfe); box-shadow:0 0 0 3px rgba(77,107,254,.12); }\n.dtx-input:disabled { background:var(--dt-surface,#f5f6f8); opacity:.75; }\n.dtx-counter { margin-top:6px; text-align:right; color:var(--dt-muted,#6b7280); font-size:11.5px; }\n.dtx-field-help { margin-top:6px; color:var(--dt-muted,#6b7280); font-size:12px; }\n.dtx-more { border-top:1px solid var(--dt-border,#eceef2); padding-top:16px; }\n.dtx-more summary { cursor:pointer; color:var(--dt-muted,#6b7280); font-size:12.5px; font-weight:600; }\n.dtx-form-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:14px; }\n.dtx-more-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:14px; margin-top:14px; }\n.dtx-dirty { padding:12px 14px; border-radius:10px; border:1px solid #fde68a; background:#fffbeb; color:#92400e; font-size:12.5px; }\n\n/* ---------- diagnosis info ---------- */\n.dtx-info-card { padding:22px 24px; }\n.dtx-info-card h3 { margin:0 0 16px; font-size:17px; letter-spacing:-.01em; }\n.dtx-how-steps { display:flex; flex-direction:column; gap:0; }\n.dtx-how-step { display:flex; gap:14px; padding:12px 0; position:relative; }\n.dtx-how-step + .dtx-how-step { border-top:1px dashed #e5e8ee; }\n.dtx-how-num { display:flex; align-items:center; justify-content:center; width:26px; height:26px; border-radius:50%; background:#eef2ff; color:#3155f5; font-size:12px; font-weight:700; flex:none; }\n.dtx-how-title { font-size:13.5px; font-weight:700; }\n.dtx-how-body { color:var(--dt-muted,#6b7280); font-size:12.5px; margin-top:2px; }\n.dtx-hint-box { display:flex; gap:12px; align-items:flex-start; margin-top:18px; padding:14px 16px; border-radius:12px; background:#f5f7ff; border:1px solid #dbe3f4; }\n.dtx-hint-box .dtx-hint-icon { color:#3155f5; flex:none; margin-top:1px; }\n.dtx-hint-title { font-size:12.5px; font-weight:700; }\n.dtx-hint-body { color:var(--dt-muted,#6b7280); font-size:12px; margin-top:2px; }\n\n/* ---------- route modules ---------- */\n.dtx-route-card { padding:18px 20px; }\n.dtx-route-head { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:6px; }\n.dtx-route-head h3 { margin:0; font-size:18px; letter-spacing:-.015em; }\n.dtx-route-meta { color:var(--dt-muted,#6b7280); font-size:12px; display:flex; gap:10px; align-items:center; flex-wrap:wrap; }\n.dtx-route-note { margin:0 0 14px; color:var(--dt-muted,#6b7280); font-size:12px; }\n.dtx-module-toggle { display:grid; grid-template-columns:auto minmax(0,1fr) auto auto; column-gap:12px; align-items:start; width:100%; padding:0; border:0; background:transparent; color:inherit; text-align:left; }\n.dtx-module-toggle:hover .dtx-module-title { color:var(--dt-brand-ink,#3155f5); }\n.dtx-module-main { min-width:0; display:flex; flex-direction:column; }\n.dtx-module-chevron { display:inline-flex; align-items:center; color:var(--dt-muted,#6b7280); margin-top:3px; transition:transform .15s ease; }\n.dtx-module[data-open=true] .dtx-module-chevron { transform:rotate(90deg); }\n.dtx-module[data-current=true] { border-color:var(--dt-brand-line,#c7d2fe); background:#f7f9ff; }\n.dtx-module[data-current=true] .dtx-module-num { background:var(--dt-brand,#4d6bfe); color:#fff; }\n.dtx-module-desc { display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; }\n.dtx-route-meta .dtx-link { margin-left:2px; font-size:12px !important; }\n.dtx-modules { display:flex; flex-direction:column; gap:10px; }\n.dtx-module { padding:11px 13px 13px; border-radius:11px; background:var(--dt-surface,#f6f7f9); border:1px solid var(--dt-border,#eceef2); }\n.dtx-module-head { display:grid; grid-template-columns:auto minmax(0,1fr) auto; column-gap:12px; align-items:start; }\n.dtx-module-num { display:flex; align-items:center; justify-content:center; width:28px; height:28px; border-radius:50%; flex:none; background:#dfe6ff; color:#3155f5; font-size:12.5px; font-weight:700; }\n.dtx-module[data-done=true] .dtx-module-num { background:#dcfce7; color:#15803d; }\n.dtx-module-title { font-size:14px; font-weight:700; line-height:1.4; }\n.dtx-module-desc { color:var(--dt-muted,#6b7280); font-size:12px; margin-top:2px; }\n.dtx-module-meta { margin-left:auto; color:var(--dt-muted,#6b7280); font-size:12px; white-space:nowrap; padding-top:3px; }\n.dtx-lessons { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:8px 18px; margin-top:9px; }\n.dtx-lesson-row { display:flex; align-items:stretch; gap:10px; }\n.dtx-lesson-arrow { display:flex; align-items:center; justify-content:center; width:22px; flex:none; color:#aab1bd; }\n.dtx-lesson { flex:1; min-width:0; display:flex; align-items:flex-start; gap:9px; padding:9px 11px; text-align:left; border:1px solid var(--dt-border,#e2e6ec); border-radius:9px; background:var(--dt-bg,#fff); color:inherit; }\n.dtx-lesson:hover { border-color:#c8d0de; }\n.dtx-lesson[data-state=current] { border-color:var(--dt-brand,#4d6bfe); box-shadow:inset 0 0 0 1px var(--dt-brand,#4d6bfe); background:#f7f9ff; }\n.dtx-lesson[data-state=completed] { background:#f4fcf7; border-color:#c6ecd5; }\n.dtx-lesson[data-state=interrupted] { border-color:#fde68a; background:#fffdf3; }\n.dtx-lesson-state { width:18px; height:18px; border-radius:50%; border:1.6px solid #c5ccd8; flex:none; margin-top:2px; display:flex; align-items:center; justify-content:center; color:#fff; }\n.dtx-lesson[data-state=current] .dtx-lesson-state { border-color:var(--dt-brand,#4d6bfe); background:var(--dt-brand,#4d6bfe); }\n.dtx-lesson[data-state=completed] .dtx-lesson-state { border-color:#22a06b; background:#22a06b; }\n.dtx-lesson[data-state=interrupted] .dtx-lesson-state { border-color:#d97706; background:#d97706; }\n.dtx-lesson-body { min-width:0; }\n.dtx-lesson-top { display:flex; align-items:baseline; gap:8px; }\n.dtx-lesson-code { color:var(--dt-muted,#6b7280); font-size:11.5px; font-variant-numeric:tabular-nums; }\n.dtx-lesson-title { font-size:13px; font-weight:700; color:var(--dt-text,#1c1f23); }\n.dtx-lesson-minutes { margin-left:auto; flex:none; color:var(--dt-muted,#6b7280); font-size:11.5px; white-space:nowrap; }\n.dtx-lesson-topics { margin-top:3px; color:var(--dt-muted,#6b7280); font-size:11.5px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n\n/* ---------- rail ---------- */\n.dtx-rail-card { padding:16px; }\n.dtx-rail-head { display:flex; align-items:center; justify-content:space-between; gap:12px; }\n.dtx-rail-eyebrow { margin:0; color:var(--dt-muted,#6b7280); font-size:12px; font-weight:600; }\n.dtx-rail-title { margin:6px 0 2px; font-size:19px; letter-spacing:-.02em; line-height:1.3; }\n.dtx-rail-sub { color:var(--dt-muted,#6b7280); font-size:12.5px; }\n.dtx-rail-pills { display:flex; gap:7px; flex-wrap:wrap; margin:12px 0 14px; }\n.dtx-rail-progress { display:flex; align-items:center; justify-content:space-between; gap:10px; padding:11px 13px; border-radius:10px; background:var(--dt-surface,#f6f7f9); color:var(--dt-muted,#6b7280); font-size:12px; }\n.dtx-rail-progress b { color:var(--dt-text,#1c1f23); font-weight:600; }\n.dtx-rail-divider { height:1px; background:var(--dt-border,#eceef2); margin:16px 0; }\n.dtx-rail-section { margin:0 0 10px; display:flex; align-items:center; gap:8px; font-size:12.5px; font-weight:700; }\n.dtx-rail-item { display:flex; align-items:flex-start; gap:8px; padding:8px 10px; border-radius:9px; background:var(--dt-surface,#f7f8fa); margin-bottom:7px; }\n.dtx-rail-item-icon { color:#3155f5; flex:none; margin-top:1px; }\n.dtx-rail-item-title { font-size:12.5px; font-weight:600; color:var(--dt-text,#1c1f23); }\n.dtx-rail-item-desc { font-size:11.5px; color:var(--dt-muted,#6b7280); margin-top:2px; display:-webkit-box; -webkit-line-clamp:1; -webkit-box-orient:vertical; overflow:hidden; }\n.dtx-rail-note { margin-top:14px; padding:12px 14px; border-radius:10px; background:#f5f7ff; border:1px solid #dbe3f4; color:#3155f5; font-size:12px; }\n.dtx-answer-card { margin-top:14px; padding:14px 16px; border-radius:12px; background:#f0fdf4; border:1px solid #bbf7d0; }\n.dtx-answer-title { display:flex; align-items:center; gap:8px; color:#15803d; font-size:12.5px; font-weight:700; }\n.dtx-answer-body { color:#166534; font-size:12px; margin-top:3px; }\n.dtx-rail-empty { min-height:360px; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding:40px 22px; }\n.dtx-rail-empty-icon { display:flex; align-items:center; justify-content:center; width:64px; height:64px; border-radius:16px; background:#f1f2f4; color:#6b7280; margin-bottom:16px; }\n.dtx-rail-empty-title { font-size:14.5px; font-weight:600; }\n.dtx-rail-empty-body { color:var(--dt-muted,#6b7280); font-size:12.5px; margin:4px 0 16px; }\n.dtx-stage-steps { display:flex; flex-direction:column; gap:10px; margin:16px 0 20px; }\n.dtx-stage-step { display:flex; align-items:flex-start; gap:11px; padding:9px 10px; border-radius:9px; color:var(--dt-muted,#6b7280); font-size:12.5px; }\n.dtx-stage-step[data-current=true] { background:#eef2ff; color:var(--dt-brand-ink,#3155f5); font-weight:600; }\n.dtx-stage-step-num { display:flex; align-items:center; justify-content:center; width:22px; height:22px; border-radius:50%; border:1px solid #d3d8e0; font-size:10.5px; font-weight:700; flex:none; }\n.dtx-stage-step[data-current=true] .dtx-stage-step-num { border-color:var(--dt-brand,#4d6bfe); background:var(--dt-brand,#4d6bfe); color:#fff; }\n.dtx-tip { display:flex; gap:10px; margin-top:12px; }\n.dtx-tip-icon { display:flex; align-items:center; justify-content:center; width:34px; height:34px; border-radius:10px; background:#eef2ff; color:#3155f5; flex:none; }\n.dtx-tip-title { font-size:12.5px; font-weight:600; }\n.dtx-tip-body { color:var(--dt-muted,#6b7280); font-size:11.5px; margin-top:1px; }\n.dtx-stage-current-title { font-size:21px; font-weight:700; letter-spacing:-.02em; margin:6px 0 0; }\n.dtx-stage-current-step { color:var(--dt-muted,#6b7280); font-size:12.5px; margin-top:2px; }\n\n/* ---------- knowledge ---------- */\n.dtx-knowledge { display:flex; align-items:stretch; gap:20px; }\n.dtx-knowledge-list { flex:0 0 460px; display:flex; flex-direction:column; padding:0; overflow:hidden; }\n.dtx-knowledge-head { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:14px 16px; border-bottom:1px solid var(--dt-border,#eceef2); }\n.dtx-knowledge-head h2 { margin:0; font-size:16px; }\n.dtx-search { position:relative; width:220px; }\n.dtx-search svg { position:absolute; left:11px; top:50%; transform:translateY(-50%); color:var(--dt-muted,#6b7280); }\n.dtx-search input { width:100%; height:36px; padding:0 10px 0 34px; border:1px solid var(--dt-border,#dfe3ea); border-radius:9px; background:var(--dt-bg,#fff); color:var(--dt-text,#1c1f23); font:inherit; font-size:12.5px; }\n.dtx-node-list { overflow:auto; flex:1; }\n.dtx-node-row { width:100%; display:flex; align-items:center; gap:10px; padding:11px 15px; border:0; border-bottom:1px solid var(--dt-border,#f0f2f5); background:transparent; text-align:left; color:inherit; }\n.dtx-node-row:hover { background:var(--dt-surface,#f8f9fb); }\n.dtx-node-row[aria-current] { background:#eef2ff; box-shadow:inset 3px 0 0 var(--dt-brand,#4d6bfe); }\n.dtx-node-icon { width:24px; height:24px; border-radius:50%; flex:none; display:flex; align-items:center; justify-content:center; border:1.6px solid #c5ccd8; color:#fff; }\n.dtx-node-icon[data-state=confirmed], .dtx-node-icon[data-state=checked], .dtx-node-icon[data-state=practiced] { border-color:#22a06b; background:#22a06b; }\n.dtx-node-icon[data-state=explained] { border-color:#22a06b; color:#22a06b; }\n.dtx-node-icon[data-state=weak], .dtx-node-icon[data-state=blocked] { border-color:#d97706; background:#d97706; }\n.dtx-node-row[aria-current] .dtx-node-icon { border-color:var(--dt-brand,#4d6bfe); background:var(--dt-brand,#4d6bfe); }\n.dtx-node-main { min-width:0; flex:1; }\n.dtx-node-title { display:block; font-size:13.5px; font-weight:600; }\n.dtx-node-sub { display:block; color:var(--dt-muted,#6b7280); font-size:12px; margin-top:1px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n.dtx-node-row .dtx-pill { flex:none; }\n.dtx-node-chevron { color:#aab1bd; flex:none; }\n.dtx-knowledge-detail { flex:1; min-width:0; padding:22px 24px; overflow:auto; }\n.dtx-detail-head h2 { margin:0; font-size:23px; letter-spacing:-.025em; display:inline; }\n.dtx-detail-head { display:flex; align-items:center; gap:12px; flex-wrap:wrap; }\n.dtx-detail-desc { color:var(--dt-muted,#6b7280); font-size:13px; line-height:1.7; margin:10px 0 0; }\n.dtx-detail-section { margin-top:24px; }\n.dtx-detail-section h3 { margin:0 0 10px; font-size:13px; font-weight:700; }\n.dtx-breadcrumb { display:flex; align-items:center; gap:8px; padding:11px 14px; border-radius:10px; background:var(--dt-surface,#f6f7f9); color:var(--dt-muted,#6b7280); font-size:12.5px; flex-wrap:wrap; }\n.dtx-breadcrumb b { color:var(--dt-text,#1c1f23); font-weight:600; }\n.dtx-chips { display:flex; gap:8px; flex-wrap:wrap; }\n.dtx-chip { display:inline-flex; align-items:center; gap:6px; height:32px; padding:0 12px; border-radius:8px; border:1px solid var(--dt-border,#e2e6ec); background:var(--dt-bg,#fff); font-size:12px; color:var(--dt-text,#1c1f23); }\n.dtx-chip[data-state=confirmed] { border-color:#c6ecd5; background:#f4fcf7; }\n.dtx-evidence { display:flex; flex-direction:column; gap:10px; }\n.dtx-evidence-card { display:flex; align-items:flex-start; gap:12px; padding:14px 16px; border:1px solid var(--dt-border,#e8eaee); border-radius:12px; background:var(--dt-bg,#fff); }\n.dtx-evidence-icon { width:36px; height:36px; border-radius:9px; background:#eef2ff; color:#3155f5; display:flex; align-items:center; justify-content:center; flex:none; }\n.dtx-evidence-main { flex:1; min-width:0; }\n.dtx-evidence-title { font-size:12.5px; font-weight:600; }\n.dtx-evidence-note { color:var(--dt-muted,#6b7280); font-size:12px; margin-top:2px; }\n.dtx-evidence-side { text-align:right; flex:none; }\n.dtx-evidence-when { color:var(--dt-muted,#6b7280); font-size:11px; margin-top:6px; }\n.dtx-info-box { display:flex; gap:11px; align-items:flex-start; margin-top:18px; padding:14px 16px; border-radius:12px; background:#f5f7ff; border:1px solid #dbe3f4; }\n.dtx-info-box .dtx-info-icon { color:#3155f5; flex:none; margin-top:1px; }\n.dtx-info-title { color:#3155f5; font-size:12.5px; font-weight:700; }\n.dtx-info-body { color:#4b5563; font-size:12px; margin-top:2px; }\n.dtx-knowledge-empty { flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; color:var(--dt-muted,#6b7280); font-size:12.5px; padding:40px; text-align:center; }\n\n/* ---------- lesson tab ---------- */\n.dtx-tab { flex:1 1 auto; min-height:0; height:100%; overflow-y:auto; overflow-x:auto; overscroll-behavior:contain; background:var(--dt-bg,#fff); color:var(--dt-text,#1c1f23); font-size:13px; line-height:1.6; padding:18px 18px 30px; text-align:left; }\n.dtx-tab * { box-sizing:border-box; }\n.dtx-tab h2 { margin:8px 0 0; font-size:21px; letter-spacing:-.025em; line-height:1.3; }\n.dtx-tab-head-meta { color:var(--dt-muted,#6b7280); font-size:12px; margin-top:3px; }\n.dtx-tab-pills { display:flex; gap:7px; flex-wrap:wrap; margin-top:11px; }\n.dtx-tab-progress { display:flex; margin-top:12px; padding:11px 13px; border-radius:10px; background:var(--dt-surface,#f6f7f9); color:var(--dt-muted,#6b7280); font-size:12px; justify-content:space-between; gap:10px; }\n.dtx-tab-divider { height:1px; background:var(--dt-border,#eceef2); margin:18px 0; }\n.dtx-tab-section { margin:20px 0 10px; display:flex; align-items:center; gap:9px; font-size:13px; font-weight:700; }\n.dtx-tab-section .dtx-section-icon { color:#3155f5; }\n.dtx-tab-text { color:var(--dt-text,#1c1f23); font-size:13px; line-height:1.75; }\n.dtx-tab-text + .dtx-tab-text { margin-top:10px; }\n.dtx-tab-concept { padding:12px 14px; border-radius:10px; background:var(--dt-surface,#f7f8fa); color:var(--dt-text,#1c1f23); font-size:12.5px; line-height:1.65; }\n.dtx-tab-diagram { border-radius:10px; border:1px solid var(--dt-border,#e8eaee); background:#fafbfc; padding:14px; overflow:auto; font-size:12px; }\n.dtx-tab-example { padding:13px 14px; border-radius:10px; background:#f7f8fa; border:1px solid var(--dt-border,#eceef2); }\n.dtx-tab-example ol { margin:8px 0 0; padding-left:20px; color:var(--dt-muted,#6b7280); font-size:12.5px; }\n.dtx-tab-check { padding:14px 16px; border-radius:12px; background:#f0fdf4; border:1px solid #bbf7d0; }\n.dtx-tab-check-title { color:#15803d; font-size:12.5px; font-weight:700; display:flex; gap:8px; align-items:center; }\n.dtx-tab-check-body { color:#166534; font-size:12.5px; margin-top:4px; line-height:1.65; }\n.dtx-tab-empty { min-height:420px; display:flex; flex-direction:column; align-items:center; justify-content:center; text-align:center; padding:40px 20px; }\n.dtx-tab-empty-icon { width:78px; height:78px; border-radius:18px; background:#f1f2f4; color:#6b7280; display:flex; align-items:center; justify-content:center; margin-bottom:18px; }\n.dtx-tab-empty h2 { margin:0 0 6px; font-size:18px; }\n.dtx-tab-empty p { color:var(--dt-muted,#6b7280); font-size:12.5px; margin:0 0 18px; }\n.dtx-tab-linkrow { display:flex; gap:10px; flex-wrap:wrap; }\n.dtx-tab-outcome { margin-top:16px; padding:13px 14px; border-radius:10px; background:var(--dt-surface,#f7f8fa); }\n.dtx-tab-outcome b { display:block; font-size:12px; color:var(--dt-muted,#6b7280); margin-bottom:3px; }\n\n\n/* stacked next-step variants (waiting / error / version / complete) */\n.dtx-next[data-layout=stack] { flex-direction:column; align-items:stretch; }\n.dtx-next[data-layout=stack] .dtx-next-actions { flex-direction:row; justify-content:center; align-items:center; min-width:0; flex-wrap:wrap; }\n.dtx-next[data-layout=stack] .dtx-next-copy { display:flex; flex-direction:column; }\n.dtx-next[data-layout=stack] .dtx-next-body { margin-bottom:2px; }\n\n/* a long ASCII diagram is reference material: keep the lesson short, expand on demand */\n.dtx-tab-fold { margin:12px 0; padding:10px 13px; border:1px solid var(--dt-border,#e8eaee); border-radius:10px; background:var(--dt-surface,#f7f8fa); }\n.dtx-tab-fold summary { cursor:pointer; color:var(--dt-brand-ink,#3155f5); font-size:12.5px; font-weight:600; }\n.dtx-tab-fold[open] summary { margin-bottom:8px; }\n.dtx-tab-fold .dt-block { margin:4px 0 0; }\n\n/* override the legacy check-block chrome inside the new tab */\n.dtx-tab .dt-check-head { background:transparent; color:#15803d; padding:0; }\n.dtx-tab .dt-check-tag { color:#15803d; font-size:11px; letter-spacing:.04em; }\n.dtx-tab .dt-check-where { color:#15803d; opacity:1; font-size:11.5px; }\n.dtx-tab .dt-check-body { padding:8px 0 0; }\n.dtx-tab .dt-check-body .dt-block-md:last-child { margin-bottom:0; }\n.dtx-tab .dt-check-hint { margin:0; padding:9px 0 0; border-top:1px dashed #bbf7d0; background:transparent; color:#15803d; }\n.dtx-tab .dt-rail-progress { display:none; }\n\n/* insights tab */\n.dtx-insights { max-width:980px; margin:0 auto; padding:24px 26px 40px; }\n.dtx-insights-head { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:18px; }\n.dtx-insights-eyebrow { margin:0 0 4px; color:var(--dt-brand-ink,#3b56d9); font-size:11.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; }\n.dtx-insights-head h1 { margin:0; font-size:26px; }\n.dtx-insights-sub { margin:6px 0 0; max-width:620px; color:var(--dt-muted,#6b7280); font-size:12.5px; line-height:1.6; }\n.dtx-insights-grid { display:grid; grid-template-columns:repeat(2, minmax(0,1fr)); gap:12px; }\n.dtx-insight-card { padding:16px 18px; border:1px solid var(--dt-border,#e8eaee); border-radius:12px; background:var(--dt-bg,#fff); }\n.dtx-insight-card h2 { display:flex; align-items:center; gap:6px; margin:0 0 8px; font-size:13px; }\n.dtx-insight-card h3 { margin:14px 0 4px; color:var(--dt-muted,#6b7280); font-size:11px; text-transform:uppercase; letter-spacing:.04em; }\n.dtx-insight-big { margin:0; font-size:30px; font-weight:700; line-height:1.1; }\n.dtx-insight-note { margin:4px 0 0; color:var(--dt-muted,#6b7280); font-size:12px; line-height:1.55; }\n.dtx-insight-card ul { margin:8px 0 0; padding:0; list-style:none; }\n.dtx-insight-card li { display:flex; align-items:baseline; justify-content:space-between; gap:10px; padding:5px 0; border-top:1px solid var(--dt-border,#e8eaee); font-size:12px; }\n.dtx-insight-card li span { overflow-wrap:anywhere; }\n.dtx-insight-card li b { color:var(--dt-muted,#6b7280); font-weight:500; white-space:nowrap; font-size:11px; }\n.dtx-insight-chips { display:flex; flex-wrap:wrap; gap:5px; margin-top:8px; }\n.dtx-insight-chips span { padding:2px 6px; border:1px solid var(--dt-border,#e8eaee); border-radius:999px; color:var(--dt-muted,#6b7280); font-size:10.5px; }\n.dtx-insight-card .dtx-link { margin-top:10px; display:inline-flex; }\n@media (max-width:760px) { .dtx-insights { padding:18px 14px 32px; } .dtx-insights-grid { grid-template-columns:1fr; } .dtx-insights-head { flex-direction:column; } }\n\n/* review tab */\n.dtx-review { max-width:760px; margin:0 auto; padding:24px 26px 40px; }\n.dtx-review-head { display:flex; align-items:flex-start; justify-content:space-between; gap:16px; margin-bottom:18px; }\n.dtx-review-eyebrow { margin:0 0 4px; color:var(--dt-brand-ink,#3b56d9); font-size:11.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; }\n.dtx-review-head h1 { margin:0; font-size:26px; }\n.dtx-review-sub { margin:6px 0 0; max-width:560px; color:var(--dt-muted,#6b7280); font-size:12.5px; line-height:1.6; }\n.dtx-review-empty { text-align:center; padding:50px 20px; border:1px dashed var(--dt-border,#e8eaee); border-radius:14px; background:var(--dt-surface,#f7f8fa); }\n.dtx-review-empty h2 { margin:12px 0 4px; font-size:20px; }\n.dtx-review-empty p { margin:0 auto 16px; max-width:420px; color:var(--dt-muted,#6b7280); font-size:13px; }\n.dtx-review-card { padding:24px; border:1px solid var(--dt-border,#e8eaee); border-radius:14px; background:var(--dt-bg,#fff); box-shadow:0 1px 2px rgba(0,0,0,.03); }\n.dtx-review-count { margin:0 0 14px; color:var(--dt-muted,#6b7280); font-size:12px; }\n.dtx-review-front { font-size:22px; font-weight:650; line-height:1.45; margin-bottom:18px; }\n.dtx-review-back { margin-top:18px; padding-top:16px; border-top:1px solid var(--dt-border,#e8eaee); font-size:15px; line-height:1.65; }\n.dtx-review-grades { display:grid; grid-template-columns:repeat(4, minmax(0,1fr)); gap:8px; margin-top:20px; }\n.dtx-grade { display:flex; flex-direction:column; align-items:flex-start; gap:2px; min-height:52px; padding:8px 10px; border:1px solid var(--dt-border,#e8eaee); border-radius:9px; background:var(--dt-bg,#fff); color:var(--dt-text,#1c1f23); cursor:pointer; text-align:left; }\n.dtx-grade span { color:var(--dt-muted,#6b7280); font-size:10.5px; }\n.dtx-grade-again { border-color:#fca5a5; } .dtx-grade-hard { border-color:#fcd34d; } .dtx-grade-good { border-color:#93c5fd; } .dtx-grade-easy { border-color:#86efac; }\n.dtx-review-undo { display:flex; align-items:center; gap:10px; margin:0 0 14px; padding:8px 10px; border:1px solid var(--dt-border,#e8eaee); border-radius:8px; background:var(--dt-surface,#f7f8fa); font-size:12px; }\n.dtx-review-more { margin:14px 0 0; color:var(--dt-muted,#6b7280); font-size:12px; }\n@media (max-width:620px) { .dtx-review { padding:18px 14px 32px; } .dtx-review-head { flex-direction:column; } .dtx-review-grades { grid-template-columns:repeat(2, minmax(0,1fr)); } }\n\n.dtx-today { margin-bottom:12px;.dtx-today { margin-bottom:12px; padding:14px 16px; background:var(--dt-surface,#f7f8fa); border:1px solid var(--dt-border,#e8eaee); border-radius:12px; }\n.dtx-today-head { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; }\n.dtx-today-eyebrow { margin:0 0 2px; color:var(--dt-brand-ink,#3b56d9); font-size:11.5px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; }\n.dtx-today h2 { margin:0; font-size:16px; }\n.dtx-today-list { margin:10px 0 0; padding:0; list-style:none; }\n.dtx-today-task { display:flex; align-items:baseline; gap:8px; min-height:28px; border-top:1px solid var(--dt-border,#e8eaee); padding:6px 0; font-size:12.5px; }\n.dtx-today-task:first-child { border-top:0; }\n.dtx-today-dot { width:7px; height:7px; border-radius:50%; background:var(--dt-brand,#4d6bfe); flex:none; }\n.dtx-today-title { flex:1 1 auto; min-width:0; overflow-wrap:anywhere; }\n.dtx-today-due { color:var(--dt-muted,#6b7280); font-size:11.5px; white-space:nowrap; }\n.dtx-today-done { display:inline-flex; align-items:center; gap:4px; padding:2px 7px; border:1px solid var(--dt-border,#e8eaee); border-radius:999px; background:var(--dt-bg,#fff); color:var(--dt-muted,#6b7280); font-size:11px; cursor:pointer; white-space:nowrap; }\n.dtx-today-done:hover { color:var(--dt-good,#1f8b4c); border-color:var(--dt-good,#1f8b4c); }\n.dtx-today-more { margin:6px 0 0; color:var(--dt-muted,#6b7280); font-size:11.5px; }\n.dtx-today .dtx-link { display:inline-flex; align-items:center; gap:5px; font-size:12px; }\n\n.dtx-tab-teachhint {.dtx-tab-teachhint { margin:8px 0 0; color:var(--dt-muted,#6b7280); font-size:12px; }\n.dtx-tab-actions { display:flex; align-items:center; gap:8px; flex-wrap:wrap; margin-top:10px; }\n.dtx-tab-actions .dtx-primary, .dtx-tab-actions .dtx-secondary { min-height:36px; padding:0 12px; font-size:12.5px !important; }\n.dtx-tab-notice { margin:8px 0 0; padding:8px 10px; border-radius:8px; background:#fffbeb; border:1px solid #fde68a; color:#92400e; font-size:12px; }\n\n/* the lesson is a reading surface: cap line length when the tab is wide */\n.dtx-tab-inner { width:100%; max-width:720px; margin:0 auto; }\n.dtx-tab .dt-md-table-wrap { margin:12px 0 14px; overflow-x:auto; border:1px solid var(--dt-border,#e8eaee); border-radius:10px; }\n.dtx-tab .dt-md-table { width:100%; border-collapse:collapse; font-size:12.5px; line-height:1.55; }\n.dtx-tab .dt-md-table th, .dtx-tab .dt-md-table td { padding:8px 10px; border-bottom:1px solid var(--dt-border,#eceef2); text-align:left; vertical-align:top; }\n.dtx-tab .dt-md-table th { background:var(--dt-surface,#f7f8fa); color:var(--dt-text,#1c1f23); font-weight:700; white-space:nowrap; }\n.dtx-tab .dt-md-table tr:last-child td { border-bottom:0; }\n.dtx-tab .dt-md-code { margin:12px 0; padding:12px 14px; border-radius:10px; background:#0f172a; color:#e2e8f0; overflow-x:auto; font:12px/1.6 ui-monospace,SFMono-Regular,Menlo,monospace; }\n.dtx-tab .dt-md-code code { font:inherit; white-space:pre; }\n.dtx-tab .dt-md-h2, .dtx-tab .dt-md-h3 { margin:20px 0 8px; font-size:15px; line-height:1.4; letter-spacing:-.01em; }\n.dtx-tab .dt-md-ul, .dtx-tab .dt-md-ol { margin:8px 0 12px; padding-left:20px; }\n.dtx-tab .dt-md-ul li, .dtx-tab .dt-md-ol li { margin:3px 0; }\n.dtx-tab .dt-block { margin:0 0 16px; }\n.dtx-tab .dt-block + .dt-block { margin-top:20px; }\n.dtx-tab .dt-block-label { margin-bottom:8px; }\n\n/* lesson blocks inside the docked tab */\n.dtx-tab .dt-block { margin:0 0 14px; }\n.dtx-tab .dt-block-label { margin:0 0 7px; display:flex; align-items:center; gap:6px; color:#3155f5; font-size:11.5px; font-weight:700; letter-spacing:0; text-transform:none; }\n.dtx-tab .dt-block-label-icon { display:inline-flex; color:#3155f5; }\n.dtx-tab .dt-block-md { font-size:13px; line-height:1.75; color:var(--dt-text,#1c1f23); }\n.dtx-tab .dt-block-md p { margin:0 0 10px; }\n.dtx-tab .dt-block-md p:last-child { margin-bottom:0; }\n.dtx-tab .dt-block-md h2, .dtx-tab .dt-block-md h3 { margin:16px 0 8px; font-size:14.5px; }\n.dtx-tab .dt-block-md ul, .dtx-tab .dt-block-md ol { margin:6px 0 10px; padding-left:22px; }\n.dtx-tab .dt-block-md li { margin:3px 0; }\n.dtx-tab .dt-block-text { padding:0; border-radius:0; background:transparent; }\n.dtx-tab .dt-block-diagram { border:1px solid var(--dt-border,#e8eaee); border-radius:10px; background:#fafbfc; padding:12px; overflow:auto; }\n.dtx-tab .dt-block-diagram .dt-pre { margin:0; white-space:pre-wrap; font-size:11.5px; line-height:1.6; }\n.dtx-tab .dt-block-example { border:1px solid var(--dt-border,#eceef2); border-radius:10px; background:#f7f8fa; padding:13px 14px; }\n.dtx-tab .dt-block-check { border:1px solid #bbf7d0; border-radius:12px; background:#f0fdf4; padding:14px 16px; }\n.dtx-tab .dt-check-head { display:flex; align-items:center; gap:8px; }\n.dtx-tab .dt-check-body { color:#166534; font-size:12.5px; line-height:1.65; margin-top:4px; }\n.dtx-tab .dt-check-hint { color:#15803d; font-size:11.5px; margin-top:6px; }\n.dtx-tab .dt-check-where { color:#15803d; font-size:11.5px; }\n.dtx-tab .dt-check-tag { color:#15803d; font-size:11.5px; font-weight:700; }\n.dtx-tab .dt-caption { color:var(--dt-muted,#6b7280); font-size:11.5px; }\n\n\n.dtx-tip-title, .dtx-tip-body, .dtx-hint-title, .dtx-hint-body,\n.dtx-info-title, .dtx-info-body, .dtx-how-title, .dtx-how-body,\n.dtx-evidence-title, .dtx-evidence-note, .dtx-rail-item-title, .dtx-rail-item-desc,\n.dtx-module-title, .dtx-module-desc { display:block; }\n.dtx-stagebar .dtx-stage { flex:0 0 auto; }\n.dtx-stagebar .dtx-stage-line { flex:1 1 18px; min-width:14px; height:1px; background:#d8dce3; }\n.dtx-next[data-kind=complete] .dtx-next-copy { display:flex; flex-direction:column; }\n\n\n.dtx-main, .dtx-route-card, .dtx-module, .dtx-lesson, .dtx-next, .dtx-card, .dtx-rail-card { min-width:0; }\n.dtx-lesson-top { flex-wrap:nowrap; gap:6px; align-items:baseline; }\n.dtx-lesson-title { flex:1 1 auto; min-width:0; overflow-wrap:anywhere; }\n.dtx-module-head, .dtx-route-head { flex-wrap:wrap; }\n.dtx-module-meta { text-align:right; }\n.dtx-route-meta { flex-wrap:wrap; }\n.dtx-next h2 { overflow-wrap:anywhere; }\n\n.dt-block-code .dt-pre { margin-top:6px; }\n.dt-block-quiz { padding:12px 14px; }\n.dt-source-refs { margin-top:8px; border-top:1px dashed var(--dt-border,#e8eaee); padding-top:8px; }\n.dt-source-ref { display:inline-block; margin:2px 6px 2px 0; padding:3px 8px; border:1px solid var(--dt-border,#e8eaee); border-radius:999px; background:var(--dt-surface,#f7f8fa); color:var(--dt-brand-ink,#3155f5); font-size:11.5px; cursor:pointer; }\n.dt-source-preview { margin-top:8px; padding:10px 12px; border:1px solid var(--dt-border,#e8eaee); border-radius:8px; background:var(--dt-surface,#f7f8fa); }\n.dt-source-preview-title { margin:0 0 2px; font-weight:600; }\n.dt-source-preview pre { max-height:240px; overflow:auto; margin:8px 0 0; white-space:pre-wrap; font:12px/1.55 ui-monospace,SFMono-Regular,Menlo,monospace; }\n.dt-quiz-option { display:flex; align-items:flex-start; gap:8px; margin:4px 0; cursor:pointer; }\n.dt-quiz-option input { margin-top:4px; }\n.dt-quiz-input { width:100%; margin:4px 0 8px; padding:8px 10px; border:1px solid var(--dt-border,#e8eaee); border-radius:7px; background:var(--dt-bg,#fff); color:var(--dt-text,#1c1f23); font:inherit; }\n.dt-quiz-recorded { margin:8px 0 0; color:var(--dt-good,#1f8b4c); font-size:12px; }\n.dt-flashcard-actions { display:flex; gap:8px; flex-wrap:wrap; margin-top:8px; }\n.dt-flashcard-front { margin:6px 0; font-weight:600; }\n.dt-block-quiz .dt-quiz-question { margin:10px 0; }\n.dt-quiz-prompt { margin:0 0 6px; }\n.dt-quiz-options { margin:0 0 8px; padding-left:22px; }\n.dt-quiz-options li { margin:3px 0; }\n.dt-block-flashcard .dt-flashcard { margin-top:6px; padding:10px 12px; border:1px solid var(--dt-border,#e8eaee); border-radius:8px; }\n.dt-flashcard summary { cursor:pointer; font-weight:600; }\n.dt-flashcard-back { margin:8px 0 0; color:var(--dt-muted,#6b7280); }\n.dt-resource-title { margin:6px 0 2px; font-weight:600; }\n.dt-block-comparison .dt-md-table-wrap { margin-top:8px; }\n\n.dt-block-formula { padding:12px 14px; }\n.dt-block-formula .dt-formula-body { margin-top:6px; overflow-x:auto; font-size:1.05em; }\n.dt-block-formula .dt-formula-body math { display:block; margin:0 auto; }\n.dt-math math { display:inline-block; vertical-align:middle; }\n.dt-math-source { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; font-size:.95em; }\n\n/* tables and fenced code are produced by the shared block renderer, so style\n them wherever a lesson body is shown (old inline panel included). */\n.dt-md-table-wrap { margin:12px 0 14px; overflow-x:auto; border:1px solid var(--dt-border,#e8eaee); border-radius:10px; }\n.dt-md-table { width:100%; border-collapse:collapse; font-size:12.5px; line-height:1.55; }\n.dt-md-table th, .dt-md-table td { padding:8px 10px; border-bottom:1px solid var(--dt-border,#eceef2); text-align:left; vertical-align:top; }\n.dt-md-table th { background:var(--dt-surface,#f7f8fa); color:var(--dt-text,#1c1f23); font-weight:700; white-space:nowrap; }\n.dt-md-table tr:last-child td { border-bottom:0; }\n.dt-md-code { margin:12px 0; padding:12px 14px; border-radius:10px; background:#0f172a; color:#e2e8f0; overflow-x:auto; font:12px/1.6 ui-monospace,SFMono-Regular,Menlo,monospace; }\n.dt-md-code code { font:inherit; white-space:pre; }\n\n/* ---------- responsive ---------- */\n@media (max-width:1120px) {\n .dtx-stagebar { gap:6px; padding:10px 12px; }\n .dtx-stage { gap:7px; }\n .dtx-stage-num { width:26px; height:26px; font-size:11.5px; }\n .dtx-stage-label { font-size:12.5px; }\n .dtx-stagebar .dtx-stage-line { min-width:10px; }\n .dtx-rail { flex-basis:300px; width:300px; }\n .dtx-topbar, .dtx-tabs, .dtx-content { padding-left:22px; padding-right:22px; }\n .dtx-knowledge-list { flex-basis:400px; }\n}\n@media (max-width:900px) {\n .dtx-route { flex-direction:column; align-items:stretch; }\n .dtx-module-toggle { grid-template-columns:auto minmax(0,1fr) auto; }\n .dtx-module-meta { grid-column:2; grid-row:2; text-align:left; padding-top:2px; }\n .dtx-module-chevron { grid-column:3; grid-row:1; }\n .dtx-rail { position:static; width:100%; flex-basis:auto; }\n .dtx-knowledge { flex-direction:column; }\n .dtx-knowledge-list { flex-basis:auto; }\n .dtx-stage-label { font-size:12.5px; }\n .dtx-topbar-project { font-size:14.5px; }\n .dtx-next { align-items:flex-start; flex-direction:column; }\n .dtx-next-actions { width:100%; min-width:0; flex-direction:row; flex-wrap:wrap; }\n .dtx-proposal { flex-wrap:wrap; }\n .dtx-proposal-stats { border-left:0; padding:0; flex:1 1 100%; }\n .dtx-proposal-actions { flex-direction:row; width:100%; }\n .dtx-hero-row { flex-direction:column; }\n}\n@media (max-width:620px) {\n .dtx-topbar { height:56px; padding:0 14px; }\n .dtx-tabs { padding:0 14px; gap:18px; }\n .dtx-content { padding:16px 14px 40px; }\n .dtx-topbar-page { font-size:15px; }\n .dtx-topbar-divider { display:none; }\n .dtx-topbar-project { font-size:13.5px; }\n .dtx-back span { display:none; }\n .dtx-stagebar { display:grid; grid-template-columns:1fr 1fr; gap:6px 12px; overflow:visible; padding:10px 12px; }\n .dtx-stage { min-width:0; }\n .dtx-stage-label { font-size:11.5px; white-space:normal; }\n .dtx-stage-num { width:24px; height:24px; font-size:11px; }\n .dtx-stage-line { display:none; }\n .dtx-hero { padding:20px; }\n .dtx-hero h1 { font-size:22px; }\n .dtx-project-row { flex-wrap:wrap; gap:12px; }\n .dtx-project-action { width:100%; margin-left:0; }\n .dtx-next h2 { font-size:19px; }\n .dtx-form-card, .dtx-route-card, .dtx-info-card, .dtx-knowledge-detail { padding:18px; }\n .dtx-more-grid { grid-template-columns:1fr; }\n .dtx-lesson-topics { white-space:normal; }\n .dtx-lessons { grid-template-columns:1fr; }\n .dtx-next h2, .dtx-rail-title, .dtx-detail-head h2 { overflow-wrap:anywhere; }\n}\n`\n","/** Append to the plugin's existing injected sheet; no separate CSS asset. */\nexport const PATH_CSS = `\n.dt-path-tabs { display:flex; flex:none; gap:8px; padding:10px 18px; border-bottom:1px solid var(--dt-border); }\n.dt-path-tabs button { font:inherit; color:var(--dt-muted); background:transparent; border:1px solid transparent; padding:7px 12px; border-radius:8px; cursor:pointer; }\n.dt-path-tabs button[aria-current] { color:var(--dt-text); background:var(--dt-brand-soft,#f2f5ff); border-color:var(--dt-brand-line,#9daffd); box-shadow:inset 0 -2px 0 var(--dt-brand,#4d6bfe); font-weight:600; }\n.dt-map-root-wrap > .dt-path-page { flex:1 1 auto; height:auto; }\n.dt-course-reading { width:100%; max-width:900px; margin:0 auto; padding:22px; overflow:auto; min-height:0; box-sizing:border-box; }\n.dt-course-unit { margin-bottom:18px; }\n.dt-map-shortcut { width:100%; margin-top:20px; }\n\n.dt-path-page { position:relative; display:flex; flex-direction:column; min-width:0; min-height:0; height:100%; overflow:auto; overscroll-behavior:contain; color:var(--dt-text,#1c1f23); background:var(--dt-bg,#fff); font-size:13px; line-height:1.6; text-align:left; }\n.dt-path-page * { box-sizing:border-box; }\n.dt-path-page button,.dt-path-page input,.dt-path-page textarea,.dt-path-page select { font:inherit; }\n.dt-path-page button { cursor:pointer; }\n.dt-path-page button:disabled { cursor:default; opacity:.55; }\n.dt-path-page :focus-visible { outline:2px solid var(--dt-brand,#4d6bfe); outline-offset:3px; }\n.dt-path-header { flex:none; display:flex; align-items:flex-start; justify-content:space-between; flex-wrap:wrap; gap:16px; padding:22px 24px 16px; border-bottom:1px solid var(--dt-border,#e3e6ea); }\n.dt-path-header-minimal { justify-content:flex-end; align-items:center; padding:8px 24px 0; border-bottom:0; }\n.dt-path-header-minimal .dt-path-actions { margin:0; }\n.dt-path-header h2 { font-size:22px; letter-spacing:-.025em; line-height:1.3; margin:4px 0 7px; }\n.dt-path-eyebrow { color:var(--dt-muted,#6b7280); text-transform:uppercase; letter-spacing:.1em; font-size:10px; font-weight:600; margin:0; }\n.dt-path-muted { color:var(--dt-muted,#6b7280); }\n.dt-path-small { font-size:12px; }\n.dt-path-page p { margin:7px 0 12px; }\n.dt-path-page h3 { margin:0 0 10px; font-size:17px; line-height:1.4; }\n.dt-path-page h4 { margin:19px 0 6px; font-size:13px; }\n.dt-path-actions { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }\n.dt-path-button { border:1px solid var(--dt-border-strong,#cfd4dc); border-radius:7px; padding:7px 11px; min-height:36px; background:var(--dt-bg,#fff); color:var(--dt-text,#1c1f23); text-align:center; }\n.dt-path-button:hover:not(:disabled) { background:var(--dt-surface,#f7f8fa); border-color:var(--dt-brand-line,#9daffd); }\n.dt-path-button[data-primary=true] { background:var(--dt-accent,#4d6bfe); color:var(--dt-on-accent,#fff); border-color:var(--dt-accent,#4d6bfe); }\n.dt-path-button[data-primary=true]:hover:not(:disabled) { filter:brightness(.93); background:var(--dt-accent,#4d6bfe); }\n.dt-path-button[aria-pressed=true] { border-color:var(--dt-brand,#4d6bfe); box-shadow:inset 0 0 0 1px var(--dt-brand,#4d6bfe); }\n.dt-path-link { border:0; padding:2px 0; color:var(--dt-brand-ink,#3b56d9); background:transparent; text-decoration:underline; text-underline-offset:3px; text-align:left; }\n.dt-path-link:hover { color:var(--dt-brand,#4d6bfe); }\n.dt-path-status,.dt-path-alert { margin:12px 24px 0; padding:11px 14px; border:1px solid var(--dt-border-strong,#cfd4dc); border-radius:8px; background:var(--dt-surface,#f7f8fa); overflow-wrap:anywhere; }\n.dt-path-alert { border-left:3px solid var(--dt-st-blocked,#c0392b); }\n.dt-path-tag { display:inline-flex; align-items:center; gap:5px; padding:2px 7px; border-radius:4px; border:1px solid var(--dt-border,#e3e6ea); font-size:11px; line-height:1.5; color:var(--dt-text,#1c1f23); background:var(--dt-surface,#f7f8fa); }\n.dt-path-body { flex:none; padding:16px 24px 24px; display:flex; flex-direction:column; gap:12px; min-width:0; }\n.dt-path-card { border:1px solid var(--dt-border,#e3e6ea); border-radius:12px; background:var(--dt-bg,#fff); padding:18px; min-width:0; }\n.dt-path-card[data-current=true] { border-color:var(--dt-brand-line,#9daffd); background:var(--dt-surface,#f7f8fa); }\n.dt-path-card-head { display:flex; justify-content:space-between; align-items:flex-start; flex-wrap:wrap; gap:10px; margin-bottom:10px; }\n.dt-path-card-head h3 { margin:0; }\n.dt-path-wizard { background:var(--dt-surface,#f7f8fa); }\n.dt-path-steps { list-style:none; padding:0; display:flex; gap:5px; margin:14px 0 20px; }\n.dt-path-steps li { flex:1; min-width:0; }\n.dt-path-step { width:100%; border:0; border-top:3px solid var(--dt-border-strong,#cfd4dc); padding:7px 4px; color:var(--dt-muted,#6b7280); background:transparent; text-align:left; font-size:12px; }\n.dt-path-step[aria-current=step] { border-color:var(--dt-brand,#4d6bfe); color:var(--dt-text,#1c1f23); font-weight:600; }\n.dt-path-fields { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:16px; margin:12px 0 20px; }\n.dt-path-field { display:flex; flex-direction:column; gap:5px; min-width:0; font-weight:550; }\n.dt-path-field[data-wide=true] { grid-column:1/-1; }\n.dt-path-field small { font-weight:400; color:var(--dt-muted,#6b7280); }\n.dt-path-field input,.dt-path-field textarea,.dt-path-field select,.dt-path-search,.dt-path-version { width:100%; min-width:0; border:1px solid var(--dt-border-strong,#cfd4dc); border-radius:7px; padding:8px 10px; color:var(--dt-text,#1c1f23); background:var(--dt-bg,#fff); }\n.dt-path-field textarea { min-height:96px; resize:vertical; }\n.dt-path-check { display:flex; align-items:center; gap:8px; font-weight:400; }\n.dt-path-summary { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:12px; margin:16px 0; }\n.dt-path-summary div { border-left:2px solid var(--dt-border-strong,#cfd4dc); padding-left:11px; }\n.dt-path-summary strong { display:block; font-size:18px; line-height:1.5; font-weight:600; }\n.dt-path-summary span { color:var(--dt-muted,#6b7280); font-size:12px; }\n.dt-path-columns { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:18px; }\n.dt-path-columns h4 { margin-top:0; }\n.dt-path-list { margin:8px 0 14px; padding-left:21px; }\n.dt-path-list li { padding:2px 0; }\n.dt-path-evidence { display:flex; gap:7px; flex-wrap:wrap; margin-top:10px; }\n.dt-path-version-row { display:flex; align-items:end; flex-wrap:wrap; gap:12px; }\n.dt-path-version-row label { flex:1; max-width:420px; display:flex; flex-direction:column; gap:5px; }\n.dt-path-conflict { border-left:3px solid var(--dt-st-weak,#b45309); background:var(--dt-surface,#f7f8fa); }\n.dt-path-workspace { display:grid; grid-template-columns:minmax(0,1.45fr) minmax(280px,1fr); align-items:start; gap:18px; min-width:0; }\n.dt-path-map-side { min-width:0; }\n.dt-path-tools { display:flex; align-items:center; flex-wrap:wrap; gap:7px; padding:11px; border:1px solid var(--dt-border,#e3e6ea); border-radius:10px 10px 0 0; background:var(--dt-surface,#f7f8fa); }\n.dt-path-search { flex:1; min-width:140px; }\n.dt-path-tools .dt-path-button { padding:4px 8px; min-height:32px; font-size:12px; }\n.dt-path-canvas { position:relative; height:440px; max-height:58vh; min-height:260px; width:100%; overflow:hidden; border:1px solid var(--dt-border,#e3e6ea); border-top:0; border-radius:0 0 10px 10px; background:var(--dt-sunken,#f1f3f6); touch-action:none; cursor:grab; }\n.dt-path-canvas[data-panning=true] { cursor:grabbing; }\n.dt-path-stage { position:absolute; top:0; left:0; transform-origin:0 0; }\n.dt-path-svg { position:absolute; top:0; left:0; overflow:visible; pointer-events:none; }\n.dt-path-module-bg { fill:var(--dt-bg,#fff); stroke:var(--dt-border-strong,#cfd4dc); stroke-width:1.5; }\n.dt-path-edge { fill:none; stroke:var(--dt-muted,#6b7280); stroke-width:2; }\n.dt-path-edge[data-optional=true] { stroke-dasharray:7 5; }\n.dt-path-arrow { fill:var(--dt-muted,#6b7280); }\n.dt-path-module-label { position:absolute; top:18px; width:280px; display:flex; align-items:center; justify-content:space-between; gap:6px; padding:7px; }\n.dt-path-module-label .dt-path-link { font-weight:650; font-size:14px; overflow-wrap:anywhere; }\n.dt-path-module-label .dt-path-button { font-size:11px; min-height:30px; padding:3px 6px; }\n.dt-path-node { position:absolute; display:flex; align-items:flex-start; gap:10px; width:276px; height:92px; padding:12px; border:1.5px solid var(--dt-border-strong,#cfd4dc); border-radius:10px; color:var(--dt-text,#1c1f23); background:var(--dt-bg,#fff); text-align:left; box-shadow:0 2px 3px #00000008; }\n.dt-path-node:hover { border-color:var(--dt-brand,#4d6bfe); }\n.dt-path-node[aria-pressed=true] { border-color:var(--dt-brand,#4d6bfe); box-shadow:0 0 0 2px var(--dt-brand-line,#9daffd); }\n.dt-path-node[data-current=true] { background:var(--dt-brand-soft,#f2f5ff); border-color:var(--dt-brand,#4d6bfe); border-width:2px; }\n.dt-path-number { flex:0 0 27px; height:27px; border-radius:50%; background:var(--dt-sunken,#f1f3f6); display:inline-flex; align-items:center; justify-content:center; font-size:12px; font-weight:650; }\n.dt-path-node-text { flex:1; min-width:0; }\n.dt-path-node-title { display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; font-weight:600; font-size:13px; line-height:1.4; }\n.dt-path-node-status { display:block; font-size:11px; color:var(--dt-muted,#6b7280); margin-top:5px; }\n.dt-path-legend { display:flex; gap:14px; flex-wrap:wrap; color:var(--dt-muted,#6b7280); font-size:11px; margin:9px 0; }\n.dt-path-legend span { display:inline-flex; gap:5px; align-items:center; }\n.dt-path-legend-line { display:inline-block; width:22px; border-top:2px solid currentColor; }\n.dt-path-legend-line[data-optional=true] { border-top-style:dashed; }\n.dt-path-module-controls { display:flex; gap:7px; flex-wrap:wrap; margin:12px 0; }\n.dt-path-outline { max-height:360px; overflow:auto; overscroll-behavior:contain; border-top:1px solid var(--dt-border,#e3e6ea); margin-top:12px; }\n.dt-path-ordered { padding-left:0; list-style:none; margin:0; }\n.dt-path-ordered li { margin:0; border-bottom:1px solid var(--dt-border,#e3e6ea); }\n.dt-path-row { border:0; background:transparent; color:var(--dt-text,#1c1f23); display:flex; align-items:center; gap:10px; width:100%; padding:10px 6px; text-align:left; }\n.dt-path-row:hover { background:var(--dt-brand-soft,#f2f5ff); }\n.dt-path-row[aria-pressed=true] { background:var(--dt-brand-soft,#f2f5ff); }\n.dt-path-row-copy { flex:1; min-width:0; }\n.dt-path-row-copy strong { display:block; font-weight:550; }\n.dt-path-row-copy small { color:var(--dt-muted,#6b7280); }\n.dt-path-detail { max-height:min(640px,46vh); overflow:auto; overscroll-behavior:contain; scroll-margin-top:16px; }\n.dt-path-detail:focus { outline-offset:1px; }\n.dt-path-detail h3 { font-size:20px; }\n.dt-path-module-card { display:block; width:100%; margin:8px 0; padding:12px; border:1px solid var(--dt-border,#e3e6ea); border-radius:8px; background:var(--dt-surface,#f7f8fa); color:var(--dt-text,#1c1f23); text-align:left; }\n.dt-path-module-card strong,.dt-path-module-card small { display:block; }\n.dt-path-module-card small { color:var(--dt-muted,#6b7280); margin-top:4px; }\n.dt-path-knowledge { padding:0; list-style:none; margin:8px 0; }\n.dt-path-knowledge li { display:flex; justify-content:space-between; gap:10px; flex-wrap:wrap; padding:7px 0; border-bottom:1px solid var(--dt-border,#e3e6ea); }\n.dt-path-knowledge code { font-size:11px; overflow-wrap:anywhere; }\n.dt-path-history details { border:1px solid var(--dt-border,#e3e6ea); border-radius:8px; padding:10px 12px; margin:9px 0; }\n.dt-path-history summary { cursor:pointer; overflow-wrap:anywhere; }\n.dt-path-history summary strong { font-weight:550; }\n.dt-path-snapshot { border-left:2px solid var(--dt-border-strong,#cfd4dc); padding:1px 0 1px 12px; margin:10px 0; overflow-wrap:anywhere; }\n.dt-path-snapshot pre { white-space:pre-wrap; font-family:inherit; font-size:12px; max-height:240px; overflow:auto; margin:6px 0; color:var(--dt-text,#1c1f23); }\n.dt-path-sr-only { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }\n@media (max-width:1050px) { .dt-path-workspace { grid-template-columns:1fr; } .dt-path-detail { max-height:min(620px,46vh); } }\n\n.dt-path-stagebar { border:1px solid var(--dt-brand-line,#9daffd); border-radius:10px; padding:12px 14px; background:var(--dt-brand-soft,#f2f5ff); }\n.dt-path-stagebar ol { list-style:none; display:flex; flex-wrap:wrap; gap:8px 14px; margin:0; padding:0; }\n.dt-path-stagebar li { display:flex; align-items:center; gap:6px; color:var(--dt-muted,#6b7280); font-size:12px; }\n.dt-path-stagebar li[data-state=current] { color:var(--dt-text,#1c1f23); font-weight:600; }\n.dt-path-stagebar li[data-state=done] { color:var(--dt-text,#1c1f23); }\n.dt-path-stagebar li small { font-weight:400; color:var(--dt-muted,#6b7280); }\n.dt-path-stagebar li[data-state=current] small { color:var(--dt-brand-ink,#3b56d9); }\n.dt-path-stage-number { display:inline-flex; align-items:center; justify-content:center; width:20px; height:20px; border-radius:50%; border:1px solid var(--dt-border-strong,#cfd4dc); font-size:11px; font-weight:600; }\n.dt-path-stagebar li[data-state=done] .dt-path-stage-number { background:var(--dt-brand,#4d6bfe); border-color:var(--dt-brand,#4d6bfe); color:var(--dt-on-accent,#fff); }\n.dt-path-stagebar li[data-state=current] .dt-path-stage-number { border-color:var(--dt-brand,#4d6bfe); color:var(--dt-brand,#4d6bfe); }\n.dt-path-stagebar p { margin:8px 0 0; }\n.dt-path-fold { border:1px solid var(--dt-border,#e3e6ea); border-radius:10px; background:var(--dt-bg,#fff); }\n.dt-path-fold > summary { cursor:pointer; padding:11px 14px; font-weight:550; color:var(--dt-text,#1c1f23); }\n.dt-path-fold[open] > summary { border-bottom:1px solid var(--dt-border,#e3e6ea); }\n.dt-path-fold > .dt-path-card { border:0; border-radius:0; }\n.dt-path-pending { border-left:3px solid var(--dt-brand,#4d6bfe); background:var(--dt-brand-soft,#f2f5ff); }\n.dt-path-next { display:flex; align-items:flex-start; justify-content:space-between; gap:18px; padding:14px 16px; border:1px solid var(--dt-brand-line,#9daffd); border-left:4px solid var(--dt-brand,#4d6bfe); border-radius:12px; background:linear-gradient(180deg, color-mix(in srgb, var(--dt-brand,#4d6bfe) 9%, var(--dt-bg,#fff)), var(--dt-bg,#fff)); }\n.dt-path-next-copy { min-width:0; }\n.dt-path-next-copy h3 { margin:2px 0 5px; font-size:17px; line-height:1.3; }\n.dt-path-next-copy p { margin:0; max-width:64ch; }\n.dt-path-next-copy p.dt-path-eyebrow { color:var(--dt-brand-ink,#3b56d9); margin-bottom:2px; }\n.dt-path-next-copy .dt-path-next-link { margin-top:4px; font-size:12.5px; }\n.dt-path-next-copy .dt-path-next-note { margin-top:4px; }\n.dt-path-next-actions { display:flex; flex-direction:column; align-items:stretch; gap:8px; flex:none; min-width:172px; }\n.dt-path-next-actions .dt-path-button { white-space:nowrap; }\n.dt-path-fit-warn { color:var(--dt-warn,#b7791f); }\n@media (max-width:620px) { .dt-path-next { flex-direction:column; } .dt-path-next-actions { flex-direction:row; flex-wrap:wrap; width:100%; min-width:0; } }\n@media (max-width:620px) { .dt-path-header { padding:16px; } .dt-path-body { padding:16px; } .dt-path-status,.dt-path-alert { margin:10px 16px 0; } .dt-path-fields,.dt-path-columns { grid-template-columns:1fr; } .dt-path-steps { flex-wrap:wrap; } .dt-path-steps li { flex-basis:40%; } .dt-path-summary { grid-template-columns:1fr; } .dt-path-card { padding:14px; } .dt-path-canvas { height:330px; } }\n@media (prefers-reduced-motion:reduce) { .dt-path-page * { animation:none!important; transition:none!important; scroll-behavior:auto!important; } }\n/* Interface-language switch: the same chrome as the bar's other buttons, and\n never wider than its own two characters. */\n.dt-lang-toggle { flex:none; min-height:28px; padding:5px 12px; border-radius:7px; border:1px solid var(--dt-border-strong,#cfd4dc); background:var(--dt-bg,#fff); color:inherit; font:inherit; font-size:12px; font-weight:550; cursor:pointer; }\n.dt-lang-toggle:hover { background:var(--dt-surface,#f7f8fa); }\n.dt-lang-toggle:focus-visible { outline:2px solid var(--dt-brand,#4d6bfe); outline-offset:3px; }\n`\n","/**\n * The panel's stylesheet.\n *\n * Injected as a `<style data-plugin>` element rather than imported as CSS,\n * because styles never travel through the client bundler: the module table\n * serves one JavaScript file per plugin, so a separate CSS artifact has nowhere\n * to live. This is what the published UI plugins do too.\n *\n * The web styling guide forbids Tailwind and component libraries here, and\n * recommends the design system's semantic custom properties. Every one of those\n * tokens is used with a fallback, so the same stylesheet renders correctly in\n * the standalone preview where no DSH theme is present.\n */\n\nimport { DESIGN_CSS } from './design-styles.js'\nimport { PATH_CSS } from './path-styles.js'\n\n/** Marker attribute so the sheet is injected exactly once, and is findable. */\nexport const STYLE_ATTR = 'data-diagnostic-tutor-style'\n\nconst CSS = `/* The tokens are declared on every root the plugin renders: the panel's\n wrapper, the panel grid, and the docked tab.\n\n Each of the three was added for the same reason, one at a time. The wrapper\n was added later and declared none of them, so var(--dt-bg, #ffffff) fell back\n to white on the toolbar and the footer while the columns went dark — a light\n frame around a dark panel, in dark mode only. The tab was never added at all,\n and it is the surface the learner actually keeps open beside the chat: every\n var(--dt-*) inside it resolved to nothing, so the docked tab had no card\n surfaces, no borders and no primary button. A custom property that does not\n resolve makes the whole declaration invalid at computed-value time, which is\n a silent, total loss of styling rather than a visible fallback.\n\n So: anything that renders as a root of the plugin belongs in this list. */\n.dt-root-wrap,\n.dt-root,\n.dt-tab,\n.dtx-map,\n.dtx-tab {\n /* Token names verified against the shipped design system\n (packages/client/ui-theme/src/styles/design-platform.css). Three of the\n earlier names did not exist (bg-elevated, bg-sunken, border-secondary,\n border-primary), so every one of them silently took its hardcoded light\n fallback, and the panel kept light cards inside a dark frame. A fallback\n that is a colour turns a typo into a theme bug. */\n --dt-bg: var(--dsw-alias-bg-base, #ffffff);\n --dt-surface: var(--dsw-alias-bg-layer-1, #f7f8fa);\n --dt-sunken: var(--dsw-alias-bg-module-platform, #f1f3f6);\n --dt-border: var(--dsw-alias-border-l2, #e3e6ea);\n --dt-border-strong: var(--dsw-alias-border-l3, #cfd4dc);\n --dt-text: var(--dsw-alias-label-primary, #1c1f23);\n --dt-muted: var(--dsw-alias-label-secondary, #6b7280);\n /* The brand token resolves to near-black in light mode and near-white in\n dark, which is a foreground colour — not an accent. The button fill is the\n one that stays a button. */\n --dt-accent: var(--dsw-alias-button-primary-fill, #4d6bfe);\n /* …and the fill needs the design system's own foreground, because the fill\n inverts with the theme and a hardcoded #fff does not. White on near-white\n is a blank button: that is what the primary control was in dark mode. */\n --dt-on-accent: var(--dsw-alias-label-primary-foreground, #ffffff);\n /* Accent used as a card edge rather than as ink. A full-strength accent\n border is a black outline in light mode and a white one in dark, which\n shouts louder than the content it frames. */\n --dt-accent-line: color-mix(in srgb, var(--dt-accent) 38%, var(--dt-border));\n /* DeepSeek brand accents. The host's brand token resolves to an ink in both\n themes, so it cannot carry the blue; these are the plugin's own accent and\n are used sparingly — focus, selection, the current stage, action links —\n never as a large fill. The primary *action* keeps the host's near-black\n fill: on DeepSeek surfaces the blue marks, it does not act. */\n --dt-brand: #4d6bfe;\n --dt-brand-ink: #3b56d9;\n --dt-brand-soft: color-mix(in srgb, var(--dt-brand) 10%, var(--dt-bg));\n --dt-brand-line: color-mix(in srgb, var(--dt-brand) 42%, var(--dt-border));\n --dt-good: var(--dsw-alias-state-success-primary, #1f8b4c);\n --dt-warn: var(--dsw-alias-state-warn-primary, #b7791f);\n --dt-stop: var(--dsw-alias-state-error-primary, #c0392b);\n\n /* ---------------------------------------------------------------------\n The seven state colours.\n\n These are a reading palette, not a decoration: the state word is the only\n thing that says how far a node has come, so it has to be legible at the\n 10.5–12px it is actually set in. The design system's own success and warn\n tokens are green-500 and amber-500 in BOTH themes, which is 2.2:1 and\n 2.1:1 on a white panel — the confirmed and weak labels were the two\n hardest things on the map to read in light mode, which is precisely\n backwards.\n\n So the palette is per theme: darker inks in light, brighter inks in dark.\n Every value below is at least 5:1 against its own panel background.\n --------------------------------------------------------------------- */\n --dt-st-unconfirmed: #5f6672;\n --dt-st-explained: #2563eb;\n --dt-st-practiced: #4f46e5;\n --dt-st-checked: #0f766e;\n --dt-st-weak: #b45309;\n --dt-st-blocked: #c0392b;\n --dt-st-confirmed: #15803d;\n\n --dt-radius: 10px;\n\n background: var(--dt-bg);\n color: var(--dt-text);\n font-size: 13px;\n line-height: 1.6;\n text-align: left;\n}\n\n/* The dark half of the state palette. Declared on the theme attribute itself\n rather than on the roots, so it also reaches the preview's own legend — and\n it only ever defines --dt-* names, so it cannot leak into the host's\n styling. DSH switches themes with this attribute; it is not a media query. */\nbody[data-ds-dark-theme] {\n /* DeepSeek's dark-theme blue steps one shade lighter so selection still reads\n on the near-black canvas. */\n --dt-brand: #6a84ff;\n --dt-brand-ink: #8fa4ff;\n --dt-st-unconfirmed: #9aa3b2;\n --dt-st-explained: #7aa2f7;\n --dt-st-practiced: #a5a0f0;\n --dt-st-checked: #4fd1c5;\n --dt-st-weak: #f0a94a;\n --dt-st-blocked: #f2706f;\n --dt-st-confirmed: #4ade80;\n}\n\n/* The roots declare their own brand tokens (below), so the dark theme has to\n restate them at the same level; an inherited body value is shadowed. */\nbody[data-ds-dark-theme] .dt-root-wrap,\nbody[data-ds-dark-theme] .dt-root,\nbody[data-ds-dark-theme] .dt-tab,\nbody[data-ds-dark-theme] .dtx-map,\nbody[data-ds-dark-theme] .dtx-tab {\n --dt-brand: #6a84ff;\n --dt-brand-ink: #8fa4ff;\n --dt-brand-soft: color-mix(in srgb, var(--dt-brand) 14%, var(--dt-bg));\n --dt-brand-line: color-mix(in srgb, var(--dt-brand) 45%, var(--dt-border));\n}\n\n.dt-root {\n display: grid;\n grid-template-columns: minmax(272px, 320px) minmax(280px, 1fr) minmax(320px, 1.15fr);\n grid-template-rows: minmax(0, 1fr);\n gap: 0;\n height: 100%;\n min-height: 0;\n min-width: 0;\n overflow: hidden;\n}\n@media (max-width: 1080px) {\n .dt-root { grid-template-columns: 1fr; grid-template-rows: none; height: auto; overflow: visible; }\n}\n\n.dt-pane { min-width: 0; min-height: 0; overflow: auto; overscroll-behavior-y: contain; padding: 16px 18px 40px; }\n.dt-pane + .dt-pane { border-left: 1px solid var(--dt-border); }\n@media (max-width: 1080px) {\n .dt-pane + .dt-pane { border-left: 0; border-top: 1px solid var(--dt-border); }\n}\n\n/* ---- shared type ------------------------------------------------------- */\n.dt-eyebrow,\n.dt-tab-section,\n.dt-now-eyebrow,\n.dt-welcome-eyebrow,\n.dt-block-label,\n.dt-next-label {\n font-size: 10px;\n letter-spacing: .1em;\n text-transform: uppercase;\n color: var(--dt-muted);\n margin: 0;\n}\n\n.dt-course-title { font-size: 17px; font-weight: 640; margin: 0 0 6px; letter-spacing: -.01em; }\n.dt-goal {\n margin: 0 0 16px; padding: 9px 11px; border-radius: var(--dt-radius);\n background: var(--dt-surface); border: 1px solid var(--dt-border);\n color: var(--dt-muted); font-size: 12px;\n}\n.dt-goal b { color: var(--dt-text); font-weight: 560; }\n\n.dt-section-title {\n display: flex; align-items: baseline; gap: 8px;\n font-size: 10px; letter-spacing: .1em; text-transform: uppercase;\n color: var(--dt-muted); margin: 22px 0 8px;\n}\n.dt-section-title span { text-transform: none; letter-spacing: 0; font-size: 11px; }\n\n.dt-caption { color: var(--dt-muted); font-size: 11px; margin: 6px 0 0; }\n.dt-empty { color: var(--dt-muted); font-size: 12.5px; margin: 8px 0; }\n.dt-cta-hint { margin-top: 14px; line-height: 1.6; }\n\n/* A region that has nothing in it yet.\n\n Pane three is empty on every arrival, and a bare grey sentence in a large\n white column reads as a failure rather than as a place waiting to be filled.\n A dashed frame says \"reserved\" instead, and costs nothing when it is\n replaced by the lesson. */\n.dt-empty-state {\n border: 1px dashed var(--dt-border-strong); border-radius: var(--dt-radius);\n padding: 14px 15px; margin-top: 10px; background: color-mix(in srgb, var(--dt-surface) 55%, transparent);\n}\n.dt-empty-state .dt-empty { margin: 0; color: var(--dt-text); }\n.dt-empty-state .dt-caption { margin-top: 6px; }\n.dt-empty-state .dt-primary { margin-top: 12px; }\n.dt-empty-title { font-size: 12.5px; font-weight: 620; margin: 0 0 5px; color: var(--dt-text); }\n\n/* ---- the two layers ---------------------------------------------------- */\n/* Shared knowledge on top, this conversation's position below. They have two\n owners, so they get two headings — one heading over both is what made a new\n chat look like the learner's old session. */\n.dt-eyebrow-here { margin-top: 18px; }\n.dt-shared {\n margin: 2px 0 0; font-size: 11.5px; color: var(--dt-muted);\n}\n.dt-here { display: block; }\n.dt-here-course { margin: 2px 0 0; font-size: 17px; font-weight: 620; line-height: 1.3; }\n.dt-here-empty {\n margin: 6px 0 0; font-size: 12.5px; line-height: 1.55; color: var(--dt-muted);\n}\n.dt-here-focus {\n display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 6px 0 0;\n font-size: 13px;\n}\n.dt-here-node { font-weight: 600; overflow-wrap: anywhere; }\n.dt-here-action { margin-top: 12px; width: 100%; }\n\n/* ---- the learning library ---------------------------------------------- */\n/* Projects, not folders: each row is something the learner is part-way\n through, and the only two facts on it are its name and how far it got. */\n.dt-library { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }\n.dt-project {\n display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left;\n padding: 10px 12px; border: 1px solid var(--dt-border); border-radius: 9px;\n background: var(--dt-bg); color: inherit; font: inherit; cursor: pointer;\n}\n.dt-project:hover { background: var(--dt-brand-soft); border-color: var(--dt-brand-line); }\n.dt-project:focus-visible { outline: 2px solid var(--dt-brand, #4d6bfe); outline-offset: 2px; }\n.dt-project-name { font-size: 13px; font-weight: 600; }\n.dt-project-count { font-size: 11.5px; color: var(--dt-muted); }\n.dt-project-go { font-size: 11.5px; color: var(--dt-muted); margin-top: 2px; }\n.dt-project:hover .dt-project-go { color: var(--dt-text); }\n\n/* One level up. Deliberately quiet: it is an escape, not an action. */\n.dt-back {\n display: inline-block; margin: 0 0 10px; padding: 2px 0; border: 0;\n background: none; color: var(--dt-muted); font: inherit; font-size: 11.5px; cursor: pointer;\n}\n.dt-back:hover { color: var(--dt-brand-ink); text-decoration: underline; }\n.dt-back:focus-visible { outline: 2px solid var(--dt-brand, #4d6bfe); outline-offset: 2px; border-radius: 4px; }\n\n/* ---- the sidebar's Learn action ---------------------------------------- */\n/* These are not \"inspired by\" DSH's sidebar row; they are its values, read out\n of the shipped stylesheet in the app bundle. The reference is .panelRow in\n @deepseek-ai/dsh-client-ui-sidebar — the row Plugins uses:\n border-radius var(--dsw-radius-md); min-height 36px; gap 8px;\n margin 0 2px; padding 7px 8px; line-height 22px;\n hover/active background var(--dsw-alias-interactive-bg-hover);\n focus-visible outline var(--dsw-focus-ring-width) solid\n var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary))\n with outline-offset -2px\n Learn cannot BE a panelRow — a panellist entry is contractually a main-panel\n switch — so it reproduces one, in the one seat that has action semantics. */\n.dt-learn-wrap { display: flex; flex-direction: column; width: 100%; min-width: 0; }\n.dt-learn {\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: 8px;\n min-height: 36px;\n margin: 0 2px;\n padding: 7px 8px;\n border: none;\n border-radius: var(--dsw-radius-md);\n background: none;\n color: var(--dsw-alias-label-primary);\n font: inherit;\n line-height: 22px;\n text-align: left;\n cursor: pointer;\n}\n.dt-learn:hover { background: var(--dt-brand-soft); }\n.dt-learn:focus-visible {\n outline: var(--dsw-focus-ring-width) solid\n var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));\n outline-offset: -2px;\n}\n.dt-learn > svg { flex: none; }\n.dt-learn-label { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }\n/* .collapsed .panelRow is a 36px square with no margin or padding. */\n.dt-learn-wrap[data-wide='false'] .dt-learn { width: 36px; height: 36px; margin: 0; padding: 0; justify-content: center; }\n.dt-learn-wrap[data-wide='false'] .dt-learn-label { display: none; }\n/* The reason a press did nothing, said where the press happened. */\n.dt-learn-notice {\n margin: 0; padding: 0 10px 4px; font-size: 11.5px; line-height: 1.45;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* ---- the two layers ---------------------------------------------------- */\n/* Shared knowledge on top, this conversation's position below. They have two\n owners, so they get two headings — one heading over both is what made a new\n chat look like the learner's old session. */\n.dt-eyebrow-here { margin-top: 18px; }\n.dt-shared {\n margin: 2px 0 0; font-size: 11.5px; color: var(--dt-muted);\n}\n.dt-here { display: block; }\n.dt-here-course { margin: 2px 0 0; font-size: 17px; font-weight: 620; line-height: 1.3; }\n.dt-here-empty {\n margin: 6px 0 0; font-size: 12.5px; line-height: 1.55; color: var(--dt-muted);\n}\n.dt-here-focus {\n display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 6px 0 0;\n font-size: 13px;\n}\n.dt-here-node { font-weight: 600; overflow-wrap: anywhere; }\n.dt-here-action { margin-top: 12px; width: 100%; }\n\n/* ---- the learning library ---------------------------------------------- */\n/* Projects, not folders: each row is something the learner is part-way\n through, and the only two facts on it are its name and how far it got. */\n.dt-library { list-style: none; margin: 10px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }\n.dt-project {\n display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left;\n padding: 10px 12px; border: 1px solid var(--dt-border); border-radius: 9px;\n background: var(--dt-bg); color: inherit; font: inherit; cursor: pointer;\n}\n.dt-project:hover { background: var(--dt-brand-soft); border-color: var(--dt-brand-line); }\n.dt-project:focus-visible { outline: 2px solid var(--dt-brand, #4d6bfe); outline-offset: 2px; }\n.dt-project-name { font-size: 13px; font-weight: 600; }\n.dt-project-count { font-size: 11.5px; color: var(--dt-muted); }\n.dt-project-go { font-size: 11.5px; color: var(--dt-muted); margin-top: 2px; }\n.dt-project:hover .dt-project-go { color: var(--dt-text); }\n\n/* One level up. Deliberately quiet: it is an escape, not an action. */\n.dt-back {\n display: inline-block; margin: 0 0 10px; padding: 2px 0; border: 0;\n background: none; color: var(--dt-muted); font: inherit; font-size: 11.5px; cursor: pointer;\n}\n.dt-back:hover { color: var(--dt-brand-ink); text-decoration: underline; }\n.dt-back:focus-visible { outline: 2px solid var(--dt-brand, #4d6bfe); outline-offset: 2px; border-radius: 4px; }\n\n/* ---- the sidebar's Learn action ---------------------------------------- */\n/* It sits at the sidebar foot, above Settings, because that is the only\n action-shaped seat the sidebar offers — a panellist entry is contractually a\n main-panel switch, which is what used to replace the conversation. */\n.dt-learn-wrap { display: flex; flex-direction: column; gap: 4px; width: 100%; }\n.dt-learn {\n display: flex; align-items: center; gap: 9px; width: 100%;\n padding: 7px 9px; border: 0; border-radius: 8px; background: transparent;\n color: inherit; font: inherit; font-size: 13px; text-align: left; cursor: pointer;\n}\n.dt-learn:hover { background: var(--dt-brand-soft); }\n.dt-learn svg { flex: none; }\n.dt-learn-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }\n/* The reason a press did nothing, said where the press happened. */\n.dt-learn-notice {\n margin: 0; padding: 0 9px 4px; font-size: 11.5px; line-height: 1.45;\n color: var(--dt-muted);\n}\n\n/* ---- panel chrome ------------------------------------------------------ */\n/* The page always says what it is and how to get back. The failure this\n answers is concrete: a learner clicks Learn, the conversation is replaced,\n and the first thought is \"where did my chat go?\". */\n/* DSH bounds the slot and clips overflow. Each surface must own its scrolling. */\n.dt-root-wrap {\n display: flex; flex-direction: column; height: 100%; min-height: 0; min-width: 0;\n overflow: auto; overscroll-behavior-y: contain; background: var(--dt-bg, #fff);\n}\n.dt-root-wrap > .dt-root { flex: 1 1 auto; min-height: 0; }\n@media (max-width: 1080px) {\n /* Stacked panes scroll together, including the toolbar and data controls. */\n .dt-root-wrap > .dt-root { flex: none; }\n .dt-pane { overflow: visible; }\n}\n/* Rights live under the work, not in the middle of it. */\n.dt-foot {\n flex: none; padding: 0 16px 14px; background: var(--dt-bg, #fff);\n}\n.dt-foot .dt-data { margin-top: 0; }\n.dt-bar {\n display: flex; align-items: center; justify-content: space-between; gap: 12px;\n flex: none; padding: 9px 16px; border-bottom: 1px solid var(--dt-border, #e3e6ea);\n background: var(--dt-surface, #f7f8fa);\n}\n.dt-bar-id { display: flex; align-items: baseline; gap: 7px; min-width: 0; }\n.dt-bar-name { font-weight: 620; font-size: 13px; color: var(--dt-text, #1c1f23); }\n.dt-bar-sep { color: var(--dt-border-strong, #cfd4dc); }\n.dt-bar-note { color: var(--dt-muted, #6b7280); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.dt-bar-back {\n flex: none; min-height: 28px; padding: 5px 12px; border-radius: 7px;\n border: 1px solid var(--dt-border-strong, #cfd4dc); background: var(--dt-bg, #fff);\n color: inherit; font: inherit; font-size: 12px; font-weight: 550; cursor: pointer;\n}\n.dt-bar-back:hover { background: var(--dt-surface, #f7f8fa); }\n/* Secondary: everything in the tab works without it. */\n.dt-tab-expand {\n float: right; padding: 2px 8px; border-radius: 6px;\n border: 1px solid var(--dt-border); background: var(--dt-bg);\n color: var(--dt-muted); font: inherit; font-size: 11.5px; cursor: pointer;\n}\n.dt-tab-expand:hover { color: var(--dt-brand-ink); border-color: var(--dt-brand-line); }\n@media (max-width: 640px) { .dt-bar-note { display: none; } }\n\n/* ---- first use --------------------------------------------------------- */\n/* The full-page variant has no columns to fill, so the question centres. */\n.dt-root-welcome { grid-template-columns: 1fr; grid-template-rows: auto; place-items: center; overflow: visible; }\n/* A long project list must grow inside the wrapper rather than shrink and clip. */\n.dt-root-wrap > .dt-root-welcome { flex: 1 0 auto; height: auto; }\n.dt-root-welcome .dt-welcome { max-width: 520px; padding: 8px 24px; }\n.dt-welcome { padding: 28px 4px 8px; }\n.dt-welcome-eyebrow { color: var(--dt-accent); }\n.dt-welcome-title {\n font-size: 21px; font-weight: 660; letter-spacing: -.015em;\n margin: 8px 0 10px; line-height: 1.25;\n}\n.dt-welcome-body { margin: 0 0 18px; color: var(--dt-muted); font-size: 13px; line-height: 1.65; }\n.dt-welcome-sample {\n border: 1px solid var(--dt-accent-line); border-radius: var(--dt-radius);\n background: var(--dt-surface); padding: 11px 13px;\n}\n.dt-welcome-sample-label {\n display: block; font-size: 10px; letter-spacing: .1em; text-transform: uppercase;\n color: var(--dt-accent); margin-bottom: 6px;\n}\n.dt-welcome-sample-text { font-size: 13px; line-height: 1.55; }\n.dt-welcome-note {\n margin: 18px 0 0; padding-top: 14px; border-top: 1px solid var(--dt-border);\n color: var(--dt-muted); font-size: 11.5px; line-height: 1.6;\n}\n\n/* Shown when nothing will teach: honest, actionable, and not a crash. */\n.dt-notice {\n margin: 16px 0 0; padding: 11px 13px; border-radius: var(--dt-radius);\n border: 1px solid color-mix(in srgb, var(--dt-st-weak) 55%, var(--dt-border));\n background: color-mix(in srgb, var(--dt-st-weak) 8%, var(--dt-bg));\n color: var(--dt-text); font-size: 12px; line-height: 1.6;\n}\n.dt-notice b { font-weight: 640; }\n\n/* ---- learner data controls --------------------------------------------- */\n/* Quiet by default: these are rights, not calls to action. They sit below the\n teaching so they are findable without competing with it. */\n.dt-data {\n padding-top: 14px; border-top: 1px solid var(--dt-border);\n display: flex; flex-wrap: wrap; align-items: center; gap: 6px;\n}\n.dt-quiet {\n min-height: 28px; padding: 4px 10px; border-radius: 7px; border: 1px solid var(--dt-border);\n background: transparent; color: var(--dt-muted);\n font: inherit; font-size: 11.5px; cursor: pointer;\n}\n.dt-quiet:hover { background: var(--dt-surface); color: var(--dt-text); }\n.dt-quiet:disabled { opacity: .5; cursor: default; }\n.dt-data-note { flex-basis: 100%; color: var(--dt-muted); font-size: 11px; line-height: 1.5; }\n.dt-data-confirm {\n border-top-color: var(--dt-stop);\n background: color-mix(in srgb, var(--dt-stop) 6%, transparent);\n border-radius: var(--dt-radius); padding: 12px 13px;\n}\n.dt-data-ask { margin: 0 0 10px; font-size: 12px; line-height: 1.6; }\n.dt-data-row { display: flex; gap: 8px; flex-wrap: wrap; }\n.dt-danger {\n min-height: 28px; padding: 5px 12px; border-radius: 7px; border: 1px solid transparent;\n background: var(--dt-stop); color: var(--dt-on-accent);\n font: inherit; font-size: 11.5px; font-weight: 550; cursor: pointer;\n}\n.dt-danger:disabled { opacity: .55; cursor: default; }\n\n/* ---- now learning ------------------------------------------------------ */\n.dt-now {\n padding: 13px 14px 14px;\n border: 1px solid var(--dt-border);\n border-radius: var(--dt-radius);\n background: var(--dt-surface);\n}\n/* The state is the headline fact about the node, so it colours the card edge. */\n.dt-now[data-state=\"blocked\"] { border-left: 3px solid var(--dt-st-blocked); }\n.dt-now[data-state=\"weak\"] { border-left: 3px solid var(--dt-st-weak); }\n.dt-now[data-state=\"confirmed\"] { border-left: 3px solid var(--dt-st-confirmed); }\n.dt-now[data-state=\"checked\"] { border-left: 3px solid var(--dt-st-checked); }\n.dt-now[data-state=\"practiced\"] { border-left: 3px solid var(--dt-st-practiced); }\n.dt-now[data-state=\"explained\"] { border-left: 3px solid var(--dt-st-explained); }\n.dt-now[data-state=\"unconfirmed\"] { border-left: 3px solid var(--dt-border-strong); }\n\n.dt-now-eyebrow { color: var(--dt-muted); }\n.dt-now-title {\n font-size: 17px; font-weight: 650; letter-spacing: -.015em;\n margin: 6px 0 8px; line-height: 1.28; overflow-wrap: anywhere;\n}\n.dt-now-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 9px; }\n.dt-now-rel { color: var(--dt-muted); font-size: 11.5px; }\n.dt-now-note { margin: 0; color: var(--dt-muted); font-size: 12px; line-height: 1.6; }\n/* Scoped to the card so it wins over the shared button styles: the card's\n control is full-width whatever weight it is currently carrying. */\n.dt-now .dt-now-action { margin-top: 13px; width: 100%; }\n\n/* ---- diagnosis map ----------------------------------------------------- */\n/* One row style for both surfaces.\n *\n * The full panel and the docked tab draw the same map from the same tree, and\n * they used to draw it differently: the tab had depth guides, a tinted state\n * chip and a blocker tint, and the panel — the surface with the most room to\n * show them — had none of it. Two presentations of one structure is how a\n * learner learns that the map means different things in different places, so\n * the row is defined once here and the panel borrows it. */\n.dt-tree { display: flex; flex-direction: column; gap: 1px; }\n.dt-tab-node,\n.dt-node {\n display: grid; grid-template-columns: auto 1fr auto;\n align-items: center; gap: 8px; width: 100%;\n padding: 6px 8px; margin: 0; border-radius: 7px;\n border: 1px solid transparent; border-left: 3px solid transparent;\n background: transparent; color: inherit; font: inherit;\n text-align: left; cursor: pointer; position: relative;\n}\n/* Depth as a drawn guide rather than only as indentation: a nested node should\n read as nested at a glance. */\n.dt-tab-node[data-depth]::before,\n.dt-node[data-depth]::before {\n content: ''; position: absolute; left: 4px; top: 0; bottom: 0;\n width: 1px; background: var(--dt-border);\n}\n.dt-tab-node[data-depth=\"2\"]::before,\n.dt-node[data-depth=\"2\"]::before { left: 18px; }\n.dt-tab-node[data-depth=\"3\"]::before,\n.dt-node[data-depth=\"3\"]::before { left: 32px; }\n.dt-tab-node:hover,\n.dt-node:hover { background: var(--dt-surface); }\n.dt-tab-node[aria-current=\"true\"],\n.dt-node[aria-current=\"true\"] {\n background: var(--dt-surface); border-color: var(--dt-border-strong);\n border-left-color: var(--dt-accent);\n}\n/* A blocker should be findable without reading every row. */\n.dt-tab-node[data-attention],\n.dt-node[data-attention] { background: color-mix(in srgb, var(--dt-st-blocked) 7%, transparent); }\n.dt-tab-node[data-attention]:hover,\n.dt-node[data-attention]:hover { background: color-mix(in srgb, var(--dt-st-blocked) 12%, transparent); }\n/* A blocker that is merely selected should still look like a blocker. */\n.dt-tab-node[data-attention][aria-current=\"true\"],\n.dt-node[data-attention][aria-current=\"true\"] {\n background: color-mix(in srgb, var(--dt-st-blocked) 10%, var(--dt-surface));\n}\n/* Where the learner actually is.\n *\n * [data-focused] was computed and rendered by both surfaces from the start and\n * had no rule at all, so the one fact the map exists to answer — which node am\n * I working on — was the one fact it did not show. It reads as a marker rather\n * than as a selection: the selected row is where the detail pane is looking,\n * the focused row is where the tutor is teaching. */\n.dt-tab-node[data-focused]:not([aria-current=\"true\"]),\n.dt-node[data-focused]:not([aria-current=\"true\"]) { background: color-mix(in srgb, var(--dt-accent) 6%, transparent); }\n.dt-tab-node[data-focused]::after,\n.dt-node[data-focused]::after {\n content: ''; position: absolute; right: 6px; top: 50%; transform: translateY(-50%);\n width: 3px; height: 60%; border-radius: 2px; background: var(--dt-accent);\n}\n/* The marker sits where the state column ends, so the row keeps its width. */\n.dt-tab-node[data-focused] > .dt-tab-state,\n.dt-node[data-focused] > .dt-tab-state { margin-right: 9px; }\n.dt-tab-node-title,\n.dt-node-title { min-width: 0; overflow-wrap: anywhere; word-break: break-word; line-height: 1.45; }\n.dt-tab-node[data-depth=\"0\"] > .dt-tab-node-title,\n.dt-node[data-depth=\"0\"] > .dt-node-title { font-weight: 600; }\n\n.dt-dot {\n width: 8px; height: 8px; border-radius: 50%;\n border: 1.5px solid currentColor; background: transparent; flex: none;\n}\n.dt-dot[data-filled=\"true\"] { background: currentColor; }\n\n/* Each falls back to its light value, so a state word rendered outside the\n panel's own subtree — the preview's legend — is still the right colour. */\n.dt-state-unconfirmed { color: var(--dt-st-unconfirmed, #5f6672); }\n.dt-state-explained { color: var(--dt-st-explained, #2563eb); }\n.dt-state-practiced { color: var(--dt-st-practiced, #4f46e5); }\n.dt-state-checked { color: var(--dt-st-checked, #0f766e); }\n.dt-state-weak { color: var(--dt-st-weak, #b45309); }\n.dt-state-blocked { color: var(--dt-st-blocked, #c0392b); }\n.dt-state-confirmed { color: var(--dt-st-confirmed, #15803d); }\n\n/* A tinted chip scans far better than coloured words alone. */\n.dt-tab-state {\n font-size: 10.5px; font-weight: 560; letter-spacing: .01em;\n padding: 1px 7px; border-radius: 999px;\n border: 1px solid currentColor; white-space: nowrap;\n background: color-mix(in srgb, currentColor 10%, transparent);\n}\n.dt-state { font-style: normal; }\n\n/* ---- node detail (full panel) ------------------------------------------ */\n.dt-detail h2 { font-size: 16px; margin: 0 0 8px; font-weight: 650; letter-spacing: -.01em; }\n.dt-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 12px; }\n.dt-chip {\n font-size: 11px; color: var(--dt-muted);\n border: 1px solid var(--dt-border); border-radius: 999px; padding: 1px 8px;\n}\n.dt-why { margin: 0 0 14px; color: var(--dt-muted); font-size: 12.5px; line-height: 1.6; }\n/* The map row itself is defined once, under \"diagnosis map\" above. */\n/* At narrow widths the three-column panel collapses to one, and a grid row\n that keeps its three tracks pushes the pane sideways instead of wrapping. */\n@media (max-width: 1080px) {\n .dt-node { grid-template-columns: auto minmax(0, 1fr) auto; }\n .dt-node-title { max-width: 100%; }\n}\n/* One line, truncated rather than wrapped: the relation line is a caption on\n the title, and at the map's width it used to break after \"1\" and leave the\n word \"evidence\" alone on a third line, which reads as a layout fault. */\n.dt-node-rel {\n display: block; color: var(--dt-muted); font-size: 11px; font-weight: 400;\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n\n/* ---- evidence ---------------------------------------------------------- */\n/* Compact on purpose: this is a record, not a reading surface. One row per\n entry, with the note clamped so a long one cannot bury the next. */\n.dt-evidence { list-style: none; margin: 0; padding: 0; }\n.dt-evidence li {\n display: grid; grid-template-columns: auto 1fr; gap: 2px 8px;\n padding: 7px 0 7px 10px; border-left: 2px solid var(--dt-border);\n font-size: 11.5px; line-height: 1.5;\n}\n.dt-evidence li + li { margin-top: 2px; }\n.dt-ev-kind {\n font-weight: 600; font-size: 11px; color: var(--dt-text);\n text-transform: capitalize;\n}\n.dt-ev-readiness {\n font-size: 10.5px; color: var(--dt-muted);\n border: 1px solid var(--dt-border); border-radius: 999px; padding: 0 6px;\n justify-self: start;\n}\n.dt-ev-note {\n grid-column: 1 / -1; color: var(--dt-muted);\n display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;\n overflow: hidden;\n}\n/* When it happened, in the units a person reads rather than in ISO 8601. */\n.dt-ev-when {\n grid-column: 1 / -1; color: var(--dt-muted); font-size: 10.5px;\n font-variant-numeric: tabular-nums;\n}\n\n/* ---- buttons ----------------------------------------------------------- */\n/* One filled control per view, and its foreground comes from the theme.\n a hardcoded white on a fill that inverts with the theme is what made the primary\n button a blank white pill in dark mode. */\n.dt-primary {\n min-height: 32px; padding: 7px 14px; border-radius: 8px; border: 1px solid transparent;\n background: var(--dt-accent); color: var(--dt-on-accent);\n font: inherit; font-weight: 550; cursor: pointer;\n}\n.dt-primary:hover:not(:disabled) { filter: brightness(1.07); }\n/* A disabled control should read as unavailable, not as a pressed black pill\n with unreadable text on it. */\n.dt-primary:disabled {\n cursor: default; filter: none;\n background: var(--dt-sunken); color: var(--dt-muted);\n border-color: var(--dt-border);\n}\n.dt-secondary {\n margin-top: 8px; padding: 6px 12px; border-radius: 8px;\n border: 1px solid var(--dt-border-strong); background: transparent;\n color: inherit; font: inherit; font-size: 12px; cursor: pointer;\n}\n.dt-secondary:hover { background: var(--dt-surface); border-color: var(--dt-brand-line); }\n.dt-secondary:disabled { opacity: .55; cursor: default; }\n\n/* ---- lesson ------------------------------------------------------------ */\n.dt-lesson { margin-top: 20px; }\n.dt-lesson-body {\n border: 1px solid var(--dt-border); border-radius: var(--dt-radius);\n background: var(--dt-bg); padding: 16px 16px 6px;\n}\n.dt-lesson-head {\n display: flex; align-items: baseline; justify-content: space-between; gap: 8px;\n padding-bottom: 10px; margin-bottom: 14px;\n border-bottom: 1px solid var(--dt-border);\n}\n.dt-lesson-title { font-size: 15px; font-weight: 650; margin: 0 0 10px; letter-spacing: -.01em; line-height: 1.35; }\n.dt-lesson-head .dt-lesson-title { margin: 0; }\n.dt-tab-lesson-title { font-weight: 600; }\n.dt-origin {\n flex: none; font-size: 10px; letter-spacing: .08em; text-transform: uppercase;\n color: var(--dt-muted); border: 1px solid var(--dt-border);\n border-radius: 999px; padding: 1px 7px;\n}\n\n/* ---- blocks ------------------------------------------------------------ */\n.dt-block { margin: 0 0 18px; }\n/* A readable measure: full-width prose in a docked panel is tiring. */\n.dt-block-md { margin: 0 0 11px; line-height: 1.66; max-width: 62ch; }\n.dt-block-md:last-child { margin-bottom: 0; }\n.dt-block-md strong { font-weight: 640; }\n/* Emphasis the teaching brain writes as *…*. It arrives inline with the prose,\n so it has to be styled rather than stripped: left alone it prints its own\n asterisks, which is the one thing that makes a lesson look like source. */\n.dt-block-md em, .dt-block-example em, .dt-check-body em { font-style: italic; }\n.dt-block-text { margin-bottom: 18px; }\n.dt-block-text:last-child { margin-bottom: 0; }\n\n/* Headings the teaching brain writes inline as ### become real headings. */\n.dt-md-h2 {\n font-size: 15px; font-weight: 650; letter-spacing: -.01em;\n margin: 20px 0 9px; line-height: 1.35;\n}\n.dt-md-h2:first-child, .dt-md-h3:first-child { margin-top: 0; }\n.dt-md-h3 {\n font-size: 13.5px; font-weight: 650; color: var(--dt-text);\n margin: 18px 0 7px; line-height: 1.4;\n}\n.dt-md-ul, .dt-md-ol { margin: 0 0 12px; padding-left: 20px; max-width: 62ch; }\n.dt-md-ul li, .dt-md-ol li { margin-bottom: 5px; line-height: 1.6; }\n.dt-md-ul { list-style: none; padding-left: 4px; }\n.dt-md-ul li { position: relative; padding-left: 16px; }\n.dt-md-ul li::before {\n content: ''; position: absolute; left: 3px; top: .62em;\n width: 4px; height: 4px; border-radius: 50%; background: var(--dt-border-strong);\n}\n.dt-md-ol { list-style: decimal; }\n.dt-md-ol li::marker { color: var(--dt-muted); font-variant-numeric: tabular-nums; }\n\n.dt-block-label {\n color: var(--dt-accent); margin-bottom: 7px;\n}\n\n/* Inline code.\n\n The teaching brain writes identifiers and shapes as inline code far more often\n than it writes display math, and only the math had a treatment — so the\n most common inline token in a programming lesson was the one that looked\n like an accident. Same frame as .dt-math, because to a reader they are the\n same kind of thing: a literal. */\n.dt-block-md code, .dt-block-example code, .dt-check-body code, .dt-caption code,\n.dt-block-diagram code, .dt-takeaway code {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: .92em; padding: 1px 4px; border-radius: 4px;\n background: var(--dt-sunken); border: 1px solid var(--dt-border);\n /* Wrapping, not nowrap. A lesson writes whole expressions inline — measured\n against a real one, an expression like np.array([[1, 2, 3]]) pushed straight out of the\n pane and the check block clipped it. A chip that cannot wrap is a chip\n that has to fit, and inline code never has to fit. */\n overflow-wrap: anywhere;\n}\n\n/* Worked example: a distinct panel, because it is a different kind of reading\n from the prose around it. */\n.dt-block-example {\n border: 1px solid var(--dt-border); border-left: 3px solid var(--dt-border-strong);\n border-radius: var(--dt-radius); background: var(--dt-surface);\n padding: 12px 14px 13px;\n}\n.dt-block-example h4 { margin: 0 0 9px; font-size: 13px; font-weight: 620; line-height: 1.4; }\n.dt-block-example ol { margin: 0; padding-left: 19px; max-width: 62ch; }\n.dt-block-example li { margin-bottom: 6px; line-height: 1.6; }\n.dt-block-example li::marker { color: var(--dt-muted); font-variant-numeric: tabular-nums; }\n.dt-takeaway {\n margin: 11px 0 0; padding-top: 9px; border-top: 1px dashed var(--dt-border);\n font-size: 12px; color: var(--dt-text);\n}\n\n/* Diagram: monospace deserves its own frame so it reads as a figure. */\n.dt-block-diagram { padding: 0; }\n.dt-pre {\n margin: 0; padding: 12px 14px; overflow-x: auto;\n background: var(--dt-sunken); border: 1px solid var(--dt-border);\n border-radius: var(--dt-radius);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 11.5px; line-height: 1.6; white-space: pre;\n}\n\n/* Check: this is the moment the lesson hands over to the learner, so it gets\n the strongest treatment on the surface. */\n.dt-block-check {\n border: 1px solid var(--dt-accent-line); border-radius: var(--dt-radius);\n background: color-mix(in srgb, var(--dt-accent) 5%, var(--dt-bg));\n padding: 0; overflow: hidden;\n}\n.dt-check-head {\n display: flex; align-items: center; justify-content: space-between; gap: 8px;\n padding: 9px 14px; background: var(--dt-accent); color: var(--dt-on-accent);\n}\n.dt-check-tag {\n font-size: 10px; letter-spacing: .12em; text-transform: uppercase; font-weight: 650;\n}\n.dt-check-where { font-size: 11px; opacity: .9; }\n.dt-check-body { padding: 13px 14px 4px; }\n.dt-check-body .dt-block-md:last-child { margin-bottom: 11px; }\n.dt-check-hint {\n margin: 0; padding: 9px 14px 11px; border-top: 1px dashed var(--dt-border);\n font-size: 11.5px; color: var(--dt-muted);\n}\n.dt-check-hint span {\n font-size: 10px; letter-spacing: .1em; text-transform: uppercase;\n margin-right: 7px; color: var(--dt-brand-ink, #3b56d9);\n}\n\n/* ---- math (styled, not typeset) ---------------------------------------- */\n.dt-math {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: .95em; padding: 0 3px; border-radius: 4px;\n background: var(--dt-sunken); overflow-wrap: anywhere;\n}\n.dt-math-block {\n margin: 0 0 12px; padding: 11px 13px; overflow-x: auto;\n background: var(--dt-sunken); border: 1px solid var(--dt-border);\n border-radius: var(--dt-radius);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12px; white-space: pre-wrap;\n}\n\n/* ---- handoff progress -------------------------------------------------- */\n.dt-handoff {\n display: flex; align-items: center; gap: 8px; flex-wrap: wrap;\n margin: 0 0 12px; padding: 8px 11px; border-radius: 8px;\n background: var(--dt-surface); border: 1px solid var(--dt-border);\n font-size: 12px;\n}\n.dt-handoff-dot {\n width: 7px; height: 7px; border-radius: 50%; background: var(--dt-accent);\n animation: dt-pulse 1.4s ease-in-out infinite;\n}\n.dt-handoff[data-phase=\"lesson-ready\"] {\n border-color: color-mix(in srgb, var(--dt-good) 45%, var(--dt-border));\n background: color-mix(in srgb, var(--dt-good) 7%, var(--dt-bg));\n}\n.dt-handoff[data-phase=\"lesson-ready\"] .dt-handoff-dot { background: var(--dt-good); animation: none; }\n.dt-handoff[data-phase=\"failed\"] .dt-handoff-dot,\n.dt-handoff[data-phase=\"stalled\"] .dt-handoff-dot { background: var(--dt-stop); animation: none; }\n@keyframes dt-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .3 } }\n.dt-handoff-label { font-weight: 550; }\n.dt-handoff-time, .dt-handoff-try, .dt-handoff-detail { color: var(--dt-muted); font-size: 11px; }\n.dt-handoff-detail { flex-basis: 100%; }\n.dt-handoff-retry {\n margin-left: auto; min-height: 28px; padding: 3px 10px; border-radius: 6px;\n border: 1px solid var(--dt-border); background: transparent;\n color: inherit; font: inherit; font-size: 11px; cursor: pointer;\n}\n.dt-handoff-retry:hover { background: var(--dt-bg); }\n@media (prefers-reduced-motion: reduce) { .dt-handoff-dot { animation: none } }\n\n/* ---- next best step ---------------------------------------------------- */\n/* The conclusion of a node. It should be the most deliberate thing on the\n surface: what just finished, where it sends you, why, and one button. */\n.dt-next {\n margin: 20px 0 4px; padding: 14px 15px 15px; border-radius: var(--dt-radius);\n border: 1px solid var(--dt-accent-line);\n background: color-mix(in srgb, var(--dt-accent) 6%, var(--dt-bg));\n box-shadow: 0 1px 2px rgba(0, 0, 0, .04);\n}\n.dt-next-label {\n display: flex; align-items: center; gap: 6px;\n color: var(--dt-accent); font-weight: 650; margin-bottom: 10px;\n}\n.dt-next-arrow { font-size: 12px; }\n.dt-next-from { margin: 0 0 8px; color: var(--dt-muted); font-size: 11.5px; }\n.dt-next-tick { color: var(--dt-st-confirmed); margin-right: 5px; }\n.dt-next-target {\n margin: 0 0 10px; font-size: 14.5px; line-height: 1.4; letter-spacing: -.01em;\n}\n.dt-next-target b { font-weight: 660; }\n.dt-next-why { margin: 0; font-size: 12px; line-height: 1.6; color: var(--dt-text); }\n.dt-next-why-label { color: var(--dt-muted); }\n.dt-next .dt-primary { margin-top: 14px; width: 100%; }\n\n/* ---- docked tab -------------------------------------------------------- */\n.dt-tab {\n box-sizing: border-box; height: 100%; min-height: 0; min-width: 0;\n overflow: auto; overscroll-behavior-y: contain;\n padding: 14px 14px 40px; font-size: 12.5px; color: var(--dt-text); text-align: left;\n}\n.dt-tab-head { display: flex; align-items: baseline; gap: 8px; justify-content: space-between; }\n.dt-tab-title { font-size: 15px; font-weight: 650; margin: 0; }\n.dt-tab-meta { color: var(--dt-muted); font-size: 11px; margin: 4px 0 8px; }\n.dt-tab-action { margin-top: 10px; }\n.dt-tab-section {\n display: flex; align-items: baseline; gap: 8px;\n margin: 24px 0 8px; padding-top: 14px;\n border-top: 1px solid var(--dt-border);\n}\n.dt-tab-section span { text-transform: none; letter-spacing: 0; font-size: 11px; }\n\n.dt-block-unknown {\n border: 1px dashed var(--dt-border); border-radius: 8px;\n padding: 10px 12px; color: var(--dt-muted);\n}\n\n/* ---------------------------------------------------------------------------\n Visual layer\n ---------------------------------------------------------------------------\n Everything below is presentation only: the state model, the copy and the\n structure are the ones above. The surfaces were technically complete but\n read thin — a white page, a flat list, and a lesson made of unstyled runs —\n so this layer gives the same truth a shape: a cover for the course, a glyph\n and a distribution bar for the map, a timeline for evidence, and figures for\n the states where there is nothing to show yet.\n\n No numbers, bars or rings represent \"progress\". The only bar here is a\n census of state words: how many nodes are in each of the seven states the\n skill defines. It answers \"what does my map look like\", not \"how far am I\".\n --------------------------------------------------------------------------- */\n\n/* The whole surface sits on a very quiet field, so cards read as objects\n rather than as holes in the page. Kept near the background colour on purpose:\n a decorative wash must never compete with 12px lesson prose. */\n.dt-root-wrap,\n.dt-tab {\n background-image:\n radial-gradient(ellipse 60% 40% at 80% -10%, color-mix(in srgb, var(--dt-accent) 4%, transparent), transparent 70%),\n radial-gradient(ellipse 50% 35% at 0% 0%, color-mix(in srgb, var(--dt-accent) 3%, transparent), transparent 70%);\n background-repeat: no-repeat;\n}\n\n/* ---- the course cover ------------------------------------------------ */\n.dt-course-card {\n position: relative; overflow: hidden;\n margin: 0 0 14px; padding: 15px 15px 13px;\n border: 1px solid var(--dt-border); border-radius: 13px;\n background:\n linear-gradient(155deg, color-mix(in srgb, var(--dt-accent) 7%, var(--dt-surface)), var(--dt-surface) 62%);\n}\n.dt-course-orbit {\n position: absolute; right: 0; top: -18px; width: 120px; height: 78px;\n color: var(--dt-accent); opacity: .2; pointer-events: none;\n}\n.dt-course-card > p,\n.dt-course-card > h1 { position: relative; }\n.dt-course-card .dt-eyebrow { color: var(--dt-accent); }\n.dt-course-card .dt-course-title { margin-top: 6px; font-size: 18px; }\n.dt-course-card .dt-goal { margin-bottom: 0; }\n\n/* ---- this conversation, as its own object ---------------------------- */\n.dt-here-card {\n padding: 12px 14px 13px; margin: 0 0 4px;\n border: 1px solid var(--dt-border); border-radius: 12px;\n background: var(--dt-surface);\n}\n.dt-here-card .dt-eyebrow-here { margin-top: 0; }\n.dt-here-card .dt-here-empty { margin-top: 5px; }\n.dt-here-card .dt-here-action { margin-top: 11px; }\n\n/* ---- the state census ------------------------------------------------ */\n.dt-statebar { margin: 10px 0 14px; }\n.dt-statebar-track {\n display: flex; gap: 2px; height: 8px;\n border-radius: 999px; overflow: hidden;\n background: var(--dt-sunken);\n}\n.dt-statebar-seg {\n display: block; min-width: 5px; height: 100%;\n background: currentColor; border-radius: 999px;\n transition: filter .15s ease;\n}\n.dt-statebar-seg:hover { filter: brightness(1.12); }\n.dt-statebar-legend {\n display: flex; flex-wrap: wrap; gap: 4px 10px;\n list-style: none; margin: 8px 0 0; padding: 0;\n font-size: 10.5px; color: var(--dt-muted);\n}\n.dt-statebar-legend li { display: inline-flex; align-items: center; gap: 4px; }\n.dt-statebar-legend b { font-weight: 620; color: var(--dt-text); font-variant-numeric: tabular-nums; }\n.dt-statebar-key {\n width: 7px; height: 7px; border-radius: 2px;\n background: currentColor; flex: none;\n}\n\n/* ---- the map rows ---------------------------------------------------- */\n/* A state mark is a small badge, not a dot: the word says which state, the\n glyph says what kind of thing it is. */\n.dt-dot {\n width: 24px; height: 24px; border-radius: 8px;\n display: grid; place-items: center;\n border: 1px solid color-mix(in srgb, currentColor 30%, var(--dt-border));\n background: color-mix(in srgb, currentColor 10%, transparent);\n flex: none;\n transition: background .15s ease, transform .15s ease;\n}\n.dt-dot[data-filled=\"true\"] { background: color-mix(in srgb, currentColor 16%, transparent); }\n.dt-glyph-svg { display: block; }\n.dt-node:hover .dt-dot,\n.dt-tab-node:hover .dt-dot { transform: scale(1.06); }\n\n.dt-node,\n.dt-tab-node {\n transition: background .15s ease, border-color .15s ease, box-shadow .15s ease;\n}\n.dt-node { padding-top: 7px; padding-bottom: 7px; }\n.dt-node[aria-current=\"true\"],\n.dt-tab-node[aria-current=\"true\"] { box-shadow: inset 0 0 0 1px var(--dt-border); }\n.dt-node-title { font-size: 12.5px; }\n.dt-node-rel { margin-top: 1px; }\n\n/* The depth guide gets a node at its foot, so nesting reads as a branch\n rather than as indentation. */\n.dt-node[data-depth]::before,\n.dt-tab-node[data-depth]::before {\n background: linear-gradient(var(--dt-border), color-mix(in srgb, var(--dt-accent) 45%, var(--dt-border)));\n width: 1.5px;\n}\n\n/* ---- node detail ----------------------------------------------------- */\n.dt-detail-hero { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 10px; }\n.dt-detail-glyph {\n width: 42px; height: 42px; border-radius: 13px;\n display: grid; place-items: center; flex: none;\n border: 1px solid color-mix(in srgb, currentColor 32%, var(--dt-border));\n background: color-mix(in srgb, currentColor 9%, transparent);\n}\n.dt-detail-glyph .dt-glyph-svg { width: 21px; height: 21px; }\n.dt-detail-hero-id { min-width: 0; }\n.dt-detail .dt-detail-hero h2 { margin: 3px 0 0; font-size: 18px; }\n.dt-detail .dt-why {\n margin: 0 0 4px; padding: 11px 13px;\n border: 1px solid var(--dt-border); border-radius: 11px;\n background: var(--dt-surface);\n color: var(--dt-text); font-size: 12.5px;\n}\n\n/* Evidence is a record, so it reads as a rail with entries on it rather than\n as a stack of unowned lines. */\n.dt-evidence { margin-left: 2px; }\n.dt-evidence li {\n position: relative; margin-left: 4px; padding-left: 15px;\n border-left: 1px solid var(--dt-border);\n}\n.dt-evidence li::before {\n content: ''; position: absolute; left: -4px; top: 11px;\n width: 7px; height: 7px; border-radius: 50%;\n background: var(--dt-bg);\n border: 1.5px solid var(--dt-accent);\n}\n.dt-evidence li:first-child::before { background: var(--dt-accent); }\n.dt-evidence li + li { margin-top: 4px; }\n\n/* ---- lesson as a reading surface ------------------------------------- */\n.dt-lesson-body {\n border-radius: 13px;\n box-shadow: 0 1px 2px rgba(0, 0, 0, .035), 0 8px 24px -18px rgba(0, 0, 0, .35);\n}\n.dt-lesson-head {\n margin-bottom: 16px;\n padding: 0 0 11px;\n}\n.dt-lesson-head .dt-eyebrow { color: var(--dt-accent); }\n.dt-lesson-title { font-size: 16px; }\n\n/* Prose blocks get a quiet rail: a lesson is a sequence, and a sequence with\n no edge reads as one long paragraph. */\n.dt-block-text { position: relative; padding-left: 13px; }\n.dt-block-text::before {\n content: ''; position: absolute; left: 0; top: 4px; bottom: 4px; width: 2px;\n border-radius: 2px;\n background: linear-gradient(var(--dt-border-strong), transparent);\n}\n.dt-block-label { display: flex; align-items: center; gap: 5px; }\n.dt-block-label-icon { display: inline-flex; color: var(--dt-accent); }\n.dt-block-example { border-left-color: var(--dt-accent); }\n.dt-block-example .dt-block-label { color: var(--dt-accent); }\n.dt-block-diagram .dt-pre {\n border-radius: 10px;\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--dt-border) 60%, transparent);\n}\n.dt-block-check { box-shadow: 0 6px 18px -14px color-mix(in srgb, var(--dt-accent) 70%, transparent); }\n\n/* ---- empty places ---------------------------------------------------- */\n.dt-empty-art {\n display: block; width: 92px; height: 80px; margin: 2px auto 10px;\n color: var(--dt-accent); opacity: .5;\n}\n.dt-empty-state .dt-empty-art { margin-top: 0; }\n.dt-welcome > .dt-empty-art {\n width: 84px; height: 74px; margin: 0 0 12px; opacity: .6;\n}\n.dt-tab .dt-empty-art { width: 74px; height: 64px; opacity: .45; margin-bottom: 8px; }\n.dt-here > .dt-empty-art { margin: 2px auto 6px; }\n.dt-empty-state { background: color-mix(in srgb, var(--dt-surface) 70%, transparent); }\n\n/* ---- first use: a place, not a form ---------------------------------- */\n.dt-root-welcome { position: relative; }\n.dt-root-welcome::before {\n content: ''; position: absolute; inset: 0; pointer-events: none;\n background-image: radial-gradient(circle, color-mix(in srgb, var(--dt-accent) 16%, transparent) 1px, transparent 1.6px);\n background-size: 44px 44px;\n -webkit-mask-image: radial-gradient(ellipse 56% 46% at 50% 34%, #000, transparent 78%);\n mask-image: radial-gradient(ellipse 56% 46% at 50% 34%, #000, transparent 78%);\n opacity: .55;\n}\n.dt-root-welcome > * { position: relative; }\n.dt-root-welcome {\n background-image:\n radial-gradient(circle at 50% 12%, color-mix(in srgb, var(--dt-accent) 7%, transparent), transparent 46%),\n radial-gradient(circle at 12% 92%, color-mix(in srgb, var(--dt-accent) 4%, transparent), transparent 40%);\n background-repeat: no-repeat;\n}\n.dt-welcome-sample { position: relative; padding-left: 36px; }\n.dt-welcome-sample::before {\n content: '“'; position: absolute; left: 13px; top: 2px;\n font-size: 30px; line-height: 1; color: var(--dt-accent); opacity: .45;\n font-family: Georgia, serif;\n}\n\n/* ---- the docked tab -------------------------------------------------- */\n.dt-tab .dt-statebar { margin: 8px 0 12px; }\n.dt-tab-section { position: relative; }\n.dt-tab .dt-lesson-body { padding: 13px 13px 4px; box-shadow: none; }\n.dt-tab .dt-block-text { padding-left: 10px; }\n.dt-tab .dt-dot { width: 21px; height: 21px; border-radius: 7px; }\n.dt-tab .dt-glyph-svg { width: 12px; height: 12px; }\n.dt-tab .dt-detail-glyph { display: none; }\n\n/* ---- the three things that fill this surface -------------------------- */\n.dt-features {\n list-style: none; margin: 18px 0 20px; padding: 0;\n display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px;\n}\n.dt-features li {\n display: flex; flex-direction: column; gap: 3px;\n padding: 11px 12px 12px;\n border: 1px solid var(--dt-border); border-radius: 11px;\n background: var(--dt-surface);\n}\n.dt-feature-icon { color: var(--dt-accent); margin-bottom: 2px; line-height: 0; }\n.dt-features b { font-size: 12px; font-weight: 620; }\n.dt-features li > span:last-child { font-size: 11px; color: var(--dt-muted); line-height: 1.45; }\n@media (max-width: 620px) { .dt-features { grid-template-columns: 1fr; } }\n\n/* ---- where this node sits -------------------------------------------- */\n.dt-locator {\n margin: 14px 0 4px; padding: 12px 13px 13px;\n border: 1px solid var(--dt-border); border-radius: 12px;\n background:\n linear-gradient(160deg, color-mix(in srgb, var(--dt-accent) 4%, var(--dt-surface)), var(--dt-surface) 70%);\n}\n.dt-locator .dt-eyebrow { margin-bottom: 9px; }\n.dt-locator-chain {\n position: relative; display: flex; flex-direction: column; gap: 5px;\n padding-left: 3px;\n}\n.dt-locator-chain::before {\n content: ''; position: absolute; left: 15px; top: 9px; bottom: 9px; width: 1px;\n background: linear-gradient(var(--dt-border-strong), var(--dt-border));\n}\n.dt-locator-item {\n position: relative; display: flex; align-items: center; gap: 8px;\n min-width: 0; width: 100%; text-align: left;\n padding: 5px 9px 5px 6px; border-radius: 8px;\n border: 1px solid transparent; background: var(--dt-bg);\n color: inherit; font: inherit; font-size: 12px;\n}\nbutton.dt-locator-item { cursor: pointer; }\nbutton.dt-locator-item:hover { background: var(--dt-surface); border-color: var(--dt-border); }\n.dt-locator-current {\n border-color: var(--dt-border-strong);\n box-shadow: 0 1px 2px rgba(0, 0, 0, .04);\n font-weight: 600;\n}\n.dt-locator-glyph {\n width: 21px; height: 21px; border-radius: 7px;\n display: grid; place-items: center; flex: none;\n border: 1px solid color-mix(in srgb, currentColor 30%, var(--dt-border));\n background: color-mix(in srgb, currentColor 10%, transparent);\n}\n.dt-locator-glyph .dt-glyph-svg { width: 13px; height: 13px; }\n.dt-locator-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }\n.dt-locator-current .dt-locator-title { color: var(--dt-text); }\n.dt-locator-parent .dt-locator-title,\n.dt-locator-child .dt-locator-title { color: var(--dt-muted); }\n.dt-locator-children { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; }\n\n/* ---- the docked tab's course cover ----------------------------------- */\n.dt-tab-cover {\n position: relative; overflow: hidden;\n margin: 2px 0 14px; padding: 12px 13px 11px;\n border: 1px solid var(--dt-border); border-radius: 12px;\n background:\n linear-gradient(155deg, color-mix(in srgb, var(--dt-accent) 6%, var(--dt-surface)), var(--dt-surface) 62%);\n}\n.dt-tab-cover > p,\n.dt-tab-cover > h2 { position: relative; }\n.dt-tab-cover .dt-eyebrow { color: var(--dt-accent); }\n.dt-tab-cover .dt-here-course { margin-top: 4px; font-size: 16px; }\n.dt-tab-cover .dt-course-orbit { width: 104px; height: 68px; opacity: .14; }\n\n/* ---- the map page ----------------------------------------------------- */\n/* The map is a reference surface and the lesson is a reading surface, so the\n map gets the whole main column: tree on the left, the selected node's\n evidence on the right. Same components, different amount of room. */\n.dt-bar-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }\n.dt-map-root-wrap { overflow: hidden; }\n.dt-map-root {\n display: grid; grid-template-columns: minmax(320px, 1.15fr) minmax(300px, 1fr);\n flex: 1 1 auto; min-height: 0; min-width: 0; overflow: hidden;\n}\n.dt-map-pane {\n min-width: 0; min-height: 0; overflow: auto; overscroll-behavior-y: contain;\n padding: 20px 22px 40px;\n}\n.dt-map-detail {\n min-width: 0; min-height: 0; overflow: auto; overscroll-behavior-y: contain;\n padding: 20px 22px 40px;\n border-left: 1px solid var(--dt-border);\n background: color-mix(in srgb, var(--dt-surface) 45%, var(--dt-bg));\n}\n.dt-map-cover { margin-bottom: 18px; }\n.dt-map-list { display: flex; flex-direction: column; gap: 2px; margin-top: 4px; }\n.dt-map-row { border-radius: 9px; }\n.dt-map-list .dt-node { padding-top: 9px; padding-bottom: 9px; font-size: 13px; }\n.dt-map-list .dt-node-title { font-size: 13px; }\n.dt-map-list .dt-node-rel { font-size: 11px; }\n.dt-map-pane .dt-eyebrow-here { margin-top: 14px; }\n@media (max-width: 900px) {\n .dt-map-root { grid-template-columns: 1fr; overflow: visible; }\n .dt-map-pane, .dt-map-detail { overflow: visible; padding: 16px 18px 34px; }\n .dt-map-detail { border-left: 0; border-top: 1px solid var(--dt-border); }\n .dt-map-root-wrap { overflow: auto; }\n}\n\n/* ---- motion ---------------------------------------------------------- */\n/* Entrance only, and only when the system allows it: this surface is read for\n minutes at a time, so nothing loops or draws attention after arrival. */\n@media (prefers-reduced-motion: no-preference) {\n .dt-node,\n .dt-tab-node,\n .dt-block,\n .dt-lesson-body,\n .dt-course-card,\n .dt-here-card,\n .dt-detail-hero {\n animation: dt-rise .26s cubic-bezier(.2, .7, .3, 1) both;\n }\n @keyframes dt-rise {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: none; }\n }\n}\n\n`\n\n/**\n * Inject the stylesheet once.\n *\n * @returns a disposer that removes the element, so an unload leaves no residue.\n */\nexport function injectStyles(): () => void {\n const existing = document.querySelector(`style[${STYLE_ATTR}]`)\n if (existing) return () => {}\n\n const element = document.createElement('style')\n element.setAttribute(STYLE_ATTR, '')\n element.textContent = CSS + PATH_CSS + DESIGN_CSS\n document.head.appendChild(element)\n return () => element.remove()\n}\n","/**\n * Visual pieces shared by both Learning surfaces.\n *\n * The runtime is deliberately number-free — state is a word, never a score —\n * so the visual language has to carry what numbers usually would: shape,\n * colour, proportion and connection. Nothing here decides anything; these\n * components only make the state the runtime already has easier to *see*.\n *\n * Everything is inline SVG. The client ships as a single classic script with\n * nowhere to serve assets from, so an icon is a path, not a file.\n */\n\nimport type { ReactNode } from 'react'\n\nimport { useLang } from './i18n.js'\nimport { useLearningText } from './learning-text.js'\nimport { stateLabel } from './map-text.js'\n\n/** The skill's seven states, ordered from settled to needing attention. */\nexport const STATE_ORDER = [\n 'confirmed',\n 'checked',\n 'practiced',\n 'explained',\n 'weak',\n 'blocked',\n 'unconfirmed',\n] as const\n\n/** One node as these components need it: a state word. */\nexport interface StatedNode {\n state: string\n}\n\n/** Count nodes per state, in display order, omitting states with no nodes. */\nexport function stateCounts(nodes: readonly StatedNode[]): { state: string; count: number }[] {\n return STATE_ORDER.map((state) => ({\n state,\n count: nodes.filter((node) => node.state === state).length,\n })).filter((entry) => entry.count > 0)\n}\n\nconst GLYPH_PATHS: Record<string, ReactNode> = {\n // Deliberately not a filled tick: a node is never \"done\" here, only observed.\n unconfirmed: (\n <>\n <circle cx=\"12\" cy=\"12\" r=\"7.5\" strokeDasharray=\"3 3.2\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.6\" fill=\"currentColor\" stroke=\"none\" />\n </>\n ),\n explained: (\n <>\n <path d=\"M4.5 5.5h5.2c.8 0 1.3.6 1.3 1.3V19c0-.8-.5-1.3-1.3-1.3H4.5z\" />\n <path d=\"M19.5 5.5h-5.2c-.8 0-1.3.6-1.3 1.3V19c0-.8.5-1.3 1.3-1.3h5.2z\" />\n </>\n ),\n practiced: (\n <>\n <path d=\"M5 19l3.6-.8L18 8.8a1.9 1.9 0 0 0 0-2.7l-.6-.5a1.9 1.9 0 0 0-2.7 0L5.4 15z\" />\n <path d=\"M14 7.2l2.9 2.8\" />\n </>\n ),\n checked: (\n <>\n <circle cx=\"12\" cy=\"12\" r=\"8\" />\n <path d=\"M8.4 12.3l2.4 2.4 4.8-5\" />\n </>\n ),\n weak: (\n <>\n <path d=\"M12 4.6l7.6 13.4H4.4z\" />\n <path d=\"M12 10v3.6\" />\n <circle cx=\"12\" cy=\"15.9\" r=\"0.7\" fill=\"currentColor\" stroke=\"none\" />\n </>\n ),\n blocked: (\n <>\n <rect x=\"5.8\" y=\"10.6\" width=\"12.4\" height=\"8.6\" rx=\"2\" />\n <path d=\"M8.8 10.6V8.2a3.2 3.2 0 0 1 6.4 0v2.4\" />\n </>\n ),\n confirmed: (\n <>\n <path d=\"M12 3.8l2.1 1.5 2.6-.1 1 2.4 2.1 1.5-.6 2.5.6 2.5-2.1 1.5-1 2.4-2.6-.1L12 19.4l-2.1-1.5-2.6.1-1-2.4-2.1-1.5.6-2.5-.6-2.5 2.1-1.5 1-2.4 2.6.1z\" />\n <path d=\"M8.5 12.2l2.3 2.2 4.6-4.7\" />\n </>\n ),\n}\n\n/**\n * One state as a mark, not a colour swatch.\n *\n * A chip says the word; this says what kind of thing the word is. Kept to a\n * 24-unit grid so every glyph reads at the same weight in a row.\n */\nexport function StateGlyph({ state }: { state: string }): ReactNode {\n return (\n <svg\n className=\"dt-glyph-svg\"\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.7\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n {GLYPH_PATHS[state] ?? GLYPH_PATHS.unconfirmed}\n </svg>\n )\n}\n\n/**\n * The shape of the map, as one bar.\n *\n * Not a progress bar: the segments are the seven states themselves, sized by\n * how many nodes are in each. A learner can see at a glance that the map is\n * mostly unconfirmed and where the blockers are, without any of it becoming a\n * percentage of \"done\".\n */\nexport function StateBar({ nodes }: { nodes: readonly StatedNode[] }): ReactNode {\n const lang = useLang()\n const counts = stateCounts(nodes)\n const total = counts.reduce((sum, entry) => sum + entry.count, 0)\n if (total === 0) return null\n const word = (state: string) => stateLabel(state, lang)\n const census = lang === 'zh' ? `${total} 个节点:` : `${total} nodes: `\n return (\n <div className=\"dt-statebar\">\n <div\n className=\"dt-statebar-track\"\n role=\"img\"\n aria-label={`${census}${counts.map((entry) => lang === 'zh' ? `${entry.count} 个${word(entry.state)}` : `${entry.count} ${entry.state}`).join(lang === 'zh' ? ',' : ', ')}`}\n >\n {counts.map((entry) => (\n <span\n key={entry.state}\n className={`dt-statebar-seg dt-state-${entry.state}`}\n style={{ flexGrow: entry.count }}\n title={lang === 'zh' ? `${entry.count} 个${word(entry.state)}` : `${entry.count} ${entry.state}`}\n />\n ))}\n </div>\n <ul className=\"dt-statebar-legend\">\n {counts.map((entry) => (\n <li key={entry.state} className={`dt-state-${entry.state}`}>\n <span className=\"dt-statebar-key\" aria-hidden=\"true\" />\n {word(entry.state)}\n <b>{entry.count}</b>\n </li>\n ))}\n </ul>\n </div>\n )\n}\n\n/** One node as the locator needs it. */\nexport interface LocatorItem {\n id: string\n title: string\n state: string\n}\n\n/**\n * Where one node sits in the map.\n *\n * The left column already draws the whole tree; this answers the narrower\n * question the middle column is about — \"what is this node connected to\" —\n * without making the learner scan back across the page. Parent, node and\n * children are selectable, because seeing a neighbour is usually followed by\n * wanting to read it.\n */\nexport function NodeLocator({\n parent,\n node,\n children,\n onSelect,\n label = 'Where this sits',\n}: {\n parent: LocatorItem | null\n node: LocatorItem\n children: readonly LocatorItem[]\n onSelect: (id: string) => void\n /** What to call this block; translated by the surface that hosts it. */\n label?: string\n}): ReactNode {\n if (parent === null && children.length === 0) return null\n const item = (entry: LocatorItem, kind: 'parent' | 'child' | 'current'): ReactNode =>\n kind === 'current' ? (\n <span className={`dt-locator-item dt-locator-current dt-state-${entry.state}`}>\n <span className=\"dt-locator-glyph\" aria-hidden=\"true\">\n <StateGlyph state={entry.state} />\n </span>\n <span className=\"dt-locator-title\">{entry.title}</span>\n </span>\n ) : (\n <button\n key={`${kind}:${entry.id}`}\n type=\"button\"\n className={`dt-locator-item dt-locator-${kind} dt-state-${entry.state}`}\n onClick={() => onSelect(entry.id)}\n >\n <span className=\"dt-locator-glyph\" aria-hidden=\"true\">\n <StateGlyph state={entry.state} />\n </span>\n <span className=\"dt-locator-title\">{entry.title}</span>\n </button>\n )\n\n return (\n <div className=\"dt-locator\">\n <p className=\"dt-eyebrow\">{label}</p>\n <div className=\"dt-locator-chain\">\n {parent !== null && item(parent, 'parent')}\n {item(node, 'current')}\n {children.length > 0 && (\n <ul className=\"dt-locator-children\">\n {children.map((entry) => (\n <li key={entry.id}>{item(entry, 'child')}</li>\n ))}\n </ul>\n )}\n </div>\n </div>\n )\n}\n\n/** The three things that fill this surface, as one glance. */\nexport function FeatureRow(): ReactNode {\n const text = useLearningText()\n return (\n <ul className=\"dt-features\">\n <li>\n <span className=\"dt-feature-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"5\" r=\"2.4\" />\n <circle cx=\"5.5\" cy=\"18\" r=\"2.4\" />\n <circle cx=\"18.5\" cy=\"18\" r=\"2.4\" />\n <path d=\"M12 7.4v3.1M10.1 11.3l-3.2 4.4M13.9 11.3l3.2 4.4\" />\n </svg>\n </span>\n <b>{text.featureMapTitle}</b>\n <span>{text.featureMapBody}</span>\n </li>\n <li>\n <span className=\"dt-feature-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M5 4.5h6c.8 0 1.3.6 1.3 1.3V20c0-.8-.5-1.3-1.3-1.3H5z\" />\n <path d=\"M19 4.5h-6c-.8 0-1.3.6-1.3 1.3V20c0-.8.5-1.3 1.3-1.3h6z\" />\n </svg>\n </span>\n <b>{text.featureLessonTitle}</b>\n <span>{text.featureLessonBody}</span>\n </li>\n <li>\n <span className=\"dt-feature-icon\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M7 4v16\" strokeDasharray=\"2 2.4\" />\n <circle cx=\"7\" cy=\"8\" r=\"2.2\" />\n <path d=\"M12 8h8M12 15h5\" opacity=\".7\" />\n <circle cx=\"7\" cy=\"15\" r=\"1.7\" strokeDasharray=\"1.8 1.8\" />\n </svg>\n </span>\n <b>{text.featureEvidenceTitle}</b>\n <span>{text.featureEvidenceBody}</span>\n </li>\n </ul>\n )\n}\n\n/** The kinds of empty place this surface can be in. */\nexport type EmptyKind = 'map' | 'lesson' | 'evidence' | 'welcome' | 'library'\n\nconst ART: Record<EmptyKind, ReactNode> = {\n // A constellation with nothing lit yet: the shape of a map before diagnosis.\n map: (\n <>\n <circle cx=\"32\" cy=\"26\" r=\"4\" />\n <circle cx=\"18\" cy=\"46\" r=\"3\" />\n <circle cx=\"46\" cy=\"46\" r=\"3\" />\n <circle cx=\"50\" cy=\"24\" r=\"2\" />\n <path d=\"M32 26l-14 20M32 26l14 20M46 46l4-22\" strokeDasharray=\"2.5 3\" />\n <path d=\"M8 60h64\" opacity=\".4\" />\n </>\n ),\n // An open book with a spark over the page that is still blank.\n lesson: (\n <>\n <path d=\"M14 24h20c2 0 3 1.4 3 3v30H17c-1.7 0-3 1.3-3 3z\" />\n <path d=\"M58 24H38c-2 0-3 1.4-3 3v30h20c1.7 0 3 1.3 3 3z\" />\n <path d=\"M50 12l1.6 4.4L56 18l-4.4 1.6L50 24l-1.6-4.4L44 18l4.4-1.6z\" strokeDasharray=\"2 2\" />\n </>\n ),\n // A rail with one observation on it: evidence is a record, not a chart.\n evidence: (\n <>\n <path d=\"M22 10v44\" strokeDasharray=\"2.5 3\" />\n <circle cx=\"22\" cy=\"22\" r=\"4\" />\n <path d=\"M32 22h26M32 33h18\" opacity=\".55\" />\n <circle cx=\"22\" cy=\"42\" r=\"3\" strokeDasharray=\"2 2\" />\n <path d=\"M32 42h20\" opacity=\".35\" />\n </>\n ),\n // The first question, drawn as a target with an orbit: aim, then diagnose.\n welcome: (\n <>\n <circle cx=\"40\" cy=\"36\" r=\"20\" strokeDasharray=\"2.5 4\" opacity=\".55\" />\n <circle cx=\"40\" cy=\"36\" r=\"10\" />\n <circle cx=\"40\" cy=\"36\" r=\"2.4\" fill=\"currentColor\" stroke=\"none\" />\n <path d=\"M40 8v7M40 57v7M12 36h7M61 36h7\" opacity=\".55\" />\n <circle cx=\"62\" cy=\"14\" r=\"2.4\" />\n <circle cx=\"18\" cy=\"58\" r=\"1.8\" opacity=\".7\" />\n </>\n ),\n library: (\n <>\n <rect x=\"14\" y=\"20\" width=\"14\" height=\"34\" rx=\"2\" />\n <rect x=\"33\" y=\"14\" width=\"14\" height=\"40\" rx=\"2\" />\n <rect x=\"52\" y=\"26\" width=\"12\" height=\"28\" rx=\"2\" strokeDasharray=\"3 3\" />\n <path d=\"M10 58h52\" opacity=\".5\" />\n </>\n ),\n}\n\n/**\n * Line art for a place that has nothing in it yet.\n *\n * Decorative and `aria-hidden` on purpose: the sentence beside it is the\n * information; this only keeps an empty column from reading as a broken one.\n */\nexport function EmptyArt({ kind }: { kind: EmptyKind }): ReactNode {\n return (\n <svg\n className=\"dt-empty-art\"\n viewBox=\"0 0 80 70\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n {ART[kind]}\n </svg>\n )\n}\n\n/** The map page's sidebar icon: a folded map, not another node graph. */\nexport function MapPageIcon({ size, active = false }: { size: number; active?: boolean }): ReactNode {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={active ? 1.6 : 1.3}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path d=\"M2.4 3.9l3.5-1.2 4.2 1.2 3.5-1.2v9.6l-3.5 1.2-4.2-1.2-3.5 1.2z\" />\n <path d=\"M5.9 2.7v9.6M10.1 3.9v9.6\" opacity=\".7\" />\n <circle cx=\"8\" cy=\"7.4\" r=\"1.15\" />\n </svg>\n )\n}\n","/**\n * Interface copy for the standalone full-page learning panel.\n *\n * English is the copy the panel already shipped with, unchanged — the table is\n * a move, not an edit. Chinese is the same interface in Simplified Chinese,\n * using the vocabulary the map and lesson surfaces already use: 会话 for a\n * conversation, 导师 for the tutor, 诊断地图 for the map, 证据 for evidence and\n * 课次 for a lesson.\n *\n * Only chrome lives here. Course and node titles, goals, lesson markdown and\n * the model's notes and reasons are the learner's record and are never\n * selected from this table.\n *\n * Counts are functions because the two languages put the number in different\n * places; a template assembled from a fixed prefix and suffix would read as\n * translated English in one of them.\n */\n\nimport { useLang } from './i18n.js'\n\nconst en = {\n // ---- the header ------------------------------------------------------\n learning: 'Learning',\n conversationInSidebar: 'your conversation stays in the sidebar',\n noConversation: 'no conversation selected',\n learningMap: 'Learning map',\n backToChat: 'Back to the conversation',\n\n // ---- first use: chosen nothing --------------------------------------\n yourLearning: 'Your learning',\n yourLearningProjects: 'Your learning projects',\n projectsShared: 'Shared across your conversations.',\n product: 'Universal Diagnostic Tutor',\n welcomeTitle: 'What do you want to learn?',\n welcomeBody:\n 'Works with the Universal Diagnostic Tutor skill. Start by telling the tutor what you want to learn — in your own words, in the chat. Then this page fills in: a map of what you actually know, the lesson, and the evidence behind every status.',\n tryLabel: 'Try',\n trySample: 'I want to learn machine learning. I know some Python, but my math is weak.',\n skillNote:\n 'If tutoring doesn\\'t start, make sure the UDT skill is available to your active DSH agent.',\n dataNote:\n 'No account, no scores, no streak. Your goal, your map and the evidence behind it stay on this machine, and are yours to keep or delete.',\n warningTitle: 'The Universal Diagnostic Tutor skill was not found.',\n warningBody:\n 'This panel still records and shows your learning state, but no lesson will be written until the skill is available to your active DSH agent.',\n\n // ---- first use: chosen something new --------------------------------\n startNew: 'Start something new',\n startNewNote: 'the tutor creates the project from what you say',\n startNewTitle: 'Tell the tutor what you want to learn in Chat.',\n startNewBody:\n 'When the tutor records a goal there, it becomes this conversation’s project. Nothing is created here, and your other projects are untouched.',\n orContinue: 'Or continue one',\n goToChat: 'Go to the chat',\n\n // ---- the learner's projects ------------------------------------------\n confirmedOf: (confirmed: number, total: number): string => `${confirmed} of ${total} confirmed`,\n projectGo: (confirmed: number): string => (confirmed === 0 ? 'Open' : 'Continue'),\n\n // ---- course + map ----------------------------------------------------\n noGoal: 'No goal yet',\n sharedAcross: 'Shared across conversations',\n goalLabel: 'Goal:',\n thisConversation: 'This conversation',\n noFocus: 'No learning focus yet. Pick a node below to start learning here.',\n startNoSessionTitle: 'Open or create a conversation first — that is where the tutor replies.',\n startNoSessionBody:\n 'Open a conversation first. The tutor teaches in the chat, so a node cannot be started without one.',\n starting: 'Starting…',\n diagnosisMap: 'Diagnosis map',\n census: (count: number): string => `${count} nodes`,\n emptyMap: 'Nothing on the map yet. State a goal in the chat and the runtime will record it.',\n evidenceCount: (count: number): string => `${count} evidence`,\n confirmedByEvidence: (confirmed: number, total: number): string =>\n `${confirmed} of ${total} confirmed by evidence.`,\n\n // ---- selected node ---------------------------------------------------\n loadingMap: 'Loading your map…',\n nothingSelected: 'Nothing selected',\n pickNode:\n 'Pick a node on the map and this column shows what is recorded against it, and why it is in that state.',\n selectedNode: 'Selected node',\n parent: (title: string): string => `parent: ${title}`,\n children: (count: number): string => `${count} children`,\n whereThisSits: 'Where this sits',\n evidence: 'Evidence',\n nothingRecorded: 'Nothing recorded yet.',\n teachingNode: 'This is the node the tutor is teaching. The lesson is on the right.',\n\n // ---- learning surface ------------------------------------------------\n learningSurface: 'Learning surface',\n lessonOrigin: (origin: string): string => origin,\n preparing: 'The tutor is preparing this node…',\n preparingBody:\n 'The teaching appears here as it is written, while the conversation continues in the chat.',\n nothingTaught: 'Nothing is being taught yet',\n pressContinuePrefix: 'The tutor has recorded the next step. Press ',\n pressContinueSuffix: ' and the teaching appears here, and the conversation stays in the chat.',\n pressStartPrefix: 'Choose a node on the map and press ',\n pressStartSuffix: '. The teaching appears here, and the conversation stays in the chat.',\n startLearning: 'Start learning',\n answerInChat: 'Answer in the chat',\n backToNode: 'Back to the node',\n}\n\nconst zh: typeof en = {\n // ---- the header ------------------------------------------------------\n learning: '学习',\n conversationInSidebar: '你的对话保留在侧边栏中',\n noConversation: '未选择会话',\n learningMap: '学习地图',\n backToChat: '返回会话',\n\n // ---- first use: chosen nothing --------------------------------------\n yourLearning: '你的学习',\n yourLearningProjects: '你的学习项目',\n projectsShared: '在你的所有会话之间共享。',\n product: 'Universal Diagnostic Tutor',\n welcomeTitle: '你想学什么?',\n welcomeBody:\n '与 Universal Diagnostic Tutor 技能配合使用。先在上方对话中,用你自己的话告诉导师你想学什么。随后这个页面会逐步填入内容:一张反映你实际掌握情况的地图、课次,以及支撑每一个状态的证据。',\n tryLabel: '试试',\n trySample: '我想学习机器学习。我会一些 Python,但数学基础比较薄弱。',\n skillNote: '如果教学没有开始,请确认你的当前 DSH 智能体可以使用 UDT 技能。',\n dataNote:\n '无需账户,没有分数,也没有连续打卡。你的目标、地图以及背后的证据都保存在这台机器上,你可以自行保留或删除。',\n warningTitle: '未找到 Universal Diagnostic Tutor 技能。',\n warningBody:\n '这个面板仍会记录并显示你的学习状态,但在你的当前 DSH 智能体可以使用该技能之前,不会写入任何课次。',\n\n // ---- first use: chosen something new --------------------------------\n startNew: '开始新的学习',\n startNewNote: '导师会根据你说的内容创建项目',\n startNewTitle: '在对话中告诉导师你想学什么。',\n startNewBody:\n '导师在对话中记录目标后,它就会成为本次会话的项目。这里不会创建任何内容,你的其他项目不会受到影响。',\n orContinue: '或继续一个项目',\n goToChat: '前往对话',\n\n // ---- the learner's projects ------------------------------------------\n confirmedOf: (confirmed, total) => `已确认 ${confirmed} / ${total}`,\n projectGo: (confirmed) => (confirmed === 0 ? '打开' : '继续'),\n\n // ---- course + map ----------------------------------------------------\n noGoal: '还没有目标',\n sharedAcross: '跨会话共享',\n goalLabel: '目标:',\n thisConversation: '本次会话',\n noFocus: '尚未确定本次会话的学习焦点。在下方选择一个节点,即可从这里开始学习。',\n startNoSessionTitle: '请先打开或新建一个会话——导师会在那里回复。',\n startNoSessionBody: '请先打开一个会话。导师在对话中教学,因此没有会话就无法开始一个节点。',\n starting: '正在开始…',\n diagnosisMap: '诊断地图',\n census: (count) => `${count} 个节点`,\n emptyMap: '地图上还没有内容。在对话中说明你的目标,系统会记录它。',\n evidenceCount: (count) => `${count} 条证据`,\n confirmedByEvidence: (confirmed, total) => `共 ${total} 个节点,已由证据确认 ${confirmed} 个。`,\n\n // ---- selected node ---------------------------------------------------\n loadingMap: '正在加载你的学习地图…',\n nothingSelected: '未选择节点',\n pickNode: '在地图上选择一个节点,这一列会显示与该节点相关的记录,以及它处于当前状态的原因。',\n selectedNode: '已选节点',\n parent: (title) => `上级:${title}`,\n children: (count) => `${count} 个子节点`,\n whereThisSits: '所在位置',\n evidence: '证据',\n nothingRecorded: '还没有记录。',\n teachingNode: '这是导师当前正在教学的节点。教学内容在右侧。',\n\n // ---- learning surface ------------------------------------------------\n learningSurface: '学习界面',\n lessonOrigin: (origin) => {\n switch (origin) {\n case 'tutor':\n return '导师'\n case 'prototype':\n return '原型'\n default:\n // An origin from a newer runtime is a word we do not own; showing the\n // stored value is more honest than inventing a translation.\n return origin\n }\n },\n preparing: '导师正在准备这个节点…',\n preparingBody: '教学内容会随写随显,对话仍在聊天中继续。',\n nothingTaught: '还没有开始教学',\n pressContinuePrefix: '导师已记录下一步。点击',\n pressContinueSuffix: ',教学内容就会出现在这里,对话仍留在聊天中。',\n pressStartPrefix: '在地图上选择一个节点并点击',\n pressStartSuffix: '。教学内容会出现在这里,对话仍留在聊天中。',\n startLearning: '开始学习',\n answerInChat: '在对话中回答',\n backToNode: '返回节点',\n}\n\n/** The standalone panel's copy in the current interface language. */\nexport function usePanelText(): typeof en {\n return useLang() === 'zh' ? zh : en\n}\n","import { CourseLearning, useCoursePath } from './course-learning.js'\n/**\n * The standalone full-page learning panel.\n *\n * NOTE: the plugin no longer registers this as a main panel. The product now\n * has one main page (`Learning map`) and one lesson surface (the docked tab),\n * so the same lesson is not offered in two places. This component is kept as\n * the standalone full-page renderer used by the preview and the tests.\n *\n * Three columns, left to right, that read as one sentence: **the course and its\n * diagnosis map → the node you picked → the learning surface for it.** This is\n * the \"focus mode\" view — the whole runtime at once.\n *\n * The docked tab (`tab.tsx`) renders the same state in the right sidebar so the\n * learner can keep the chat open while working. Both run on `useLearning`, so\n * they cannot disagree about what is focused or what the tutor wrote.\n *\n * ---------------------------------------------------------------------------\n * Who decides what\n * ---------------------------------------------------------------------------\n * The panel never writes teaching content and never decides what happens next.\n * Pressing **Start learning** records a focus and wakes the tutor; the blocks\n * that appear are whatever the tutor wrote through `udt_lesson_update`; the\n * evidence and state changes are whatever the tutor recorded through\n * `udt_map_update`. The panel's whole job is to show the current truth and to\n * keep showing it as it changes.\n */\n\nimport { useEffect, type ReactNode } from 'react'\n\nimport { DataFooter, HandoffLine, LessonBody, NextStepCard } from './blocks.jsx'\nimport { useLang, type Lang } from './i18n.js'\nimport { LanguageToggle } from './language-toggle.js'\nimport { evidenceKindLabel, readinessLabel, stateLabel } from './map-text.js'\nimport {\n buildMapTree,\n continuationLabel,\n conversationControl,\n flattenTree,\n formatWhen,\n handoffLineVisible,\n isFilledState,\n lessonPlaceholder,\n relationLabel,\n stateExplanation,\n} from './model.js'\nimport { usePanelText } from './panel-text.js'\nimport type { PanelClient } from './use-learning.js'\nimport { defaultClient, useLearning } from './use-learning.js'\nimport { EmptyArt, FeatureRow, NodeLocator, StateBar, StateGlyph } from './visuals.jsx'\n\nexport type { PanelClient } from './use-learning.js'\n\n/** A node state as one chip: the mark, and the word. Never a number. */\nfunction StateChip({ state, lang }: { state: string; lang: Lang }): ReactNode {\n return (\n <span className={`dt-tab-state dt-state-${state}`}>\n {stateLabel(state, lang)}\n </span>\n )\n}\n\nexport interface LearningPanelProps {\n /** Defaults to the real HTTP client. */\n client?: PanelClient\n /** Pre-supplied overview, so the preview and tests skip the first fetch. */\n initialOverview?: Parameters<typeof useLearning>[0]['initialOverview']\n /** The session the tutor should be woken in, when the host half knows one. */\n sessionId?: string | undefined\n /**\n * Show the conversation again.\n *\n * This panel fills the main column, which is also where the chat lives, so\n * answering a check means leaving it. Supplied by the client plugin through\n * the layout service; absent in the preview and in tests.\n */\n onOpenChat?: (() => void) | undefined\n /** Called once a focus is recorded — used to dock the side tab. */\n onFocusStarted?: (() => void) | undefined\n /**\n * Open the diagnosis map as its own page.\n *\n * The map is a reference surface, and the teaching column is for reading a\n * lesson: they are different jobs, so the map gets a page of its own instead\n * of sharing this one. Absent in the preview and in tests.\n */\n onOpenMapPage?: (() => void) | undefined\n /** Switch to the conversation a project's learning belongs to; false when the shell cannot navigate. */\n onOpenSession?: ((sessionId: string) => boolean) | undefined\n}\n\n/** The status pill plus its mark. Never a number. */\nfunction StateMark({ state, lang }: { state: string; lang: Lang }): ReactNode {\n return (\n <>\n <span className={`dt-dot dt-state-${state}`} data-filled={isFilledState(state)} />\n <span className={`dt-state dt-state-${state}`}>{stateLabel(state, lang)}</span>\n </>\n )\n}\n\n/**\n * The panel.\n *\n * @param props - injectable client, optional pre-supplied overview, session id.\n * @returns the three-pane learning surface.\n */\nexport function LearningPanel({\n client,\n initialOverview,\n sessionId,\n onOpenChat,\n onFocusStarted,\n onOpenMapPage,\n onOpenSession,\n}: LearningPanelProps): ReactNode {\n const text = usePanelText()\n const lang = useLang()\n const state = useLearning({\n client: client ?? defaultClient,\n sessionId,\n initialOverview,\n onStarted: onFocusStarted,\n onOpenSession,\n })\n\n const coursePath = useCoursePath((client ?? defaultClient).pathClient, state.overview?.course?.id, sessionId)\n\n // Pick something on arrival, the same way the docked tab does. Without this\n // the middle and right panes sit empty until the learner works out that the\n // map rows are clickable — which reads as a broken page, not an invitation.\n const arrival = state.selectedId\n const select = state.select\n useEffect(() => {\n if (arrival !== null) return\n const first = state.overview?.nodes?.[0]?.id\n if (first !== undefined) select(state.focus?.nodeId ?? first)\n }, [arrival, select, state.overview, state.focus])\n const { overview, focus, nextStep, handoff, teachingBrain, dataNote, selectedId, detail, lesson, note, error, starting, loading, library } =\n state\n\n // The focused node's title and state, read from the map rather than fetched:\n // the panel already holds every node, and the focus is one of them.\n const focusNode = focus === null ? undefined : overview?.nodes?.find((n) => n.id === focus.nodeId)\n const focusNodeTitle = focusNode?.title ?? null\n const focusNodeState = focusNode?.state ?? null\n const rows = flattenTree(buildMapTree(overview?.nodes ?? []))\n const confirmed = (overview?.nodes ?? []).filter((node) => node.state === 'confirmed').length\n // \"Already learning this one\" is a statement about the tutor's focus, not\n // about what the learner has selected.\n const isFocused = focus !== null && focus.nodeId === selectedId\n\n /**\n * This conversation's control, its teaching-region state, and whether the\n * handoff line describes a wait that is still real.\n *\n * Derived, not re-decided per surface: the docked tab renders the same three\n * answers from the same three functions, so the two cannot drift into telling\n * a learner to press `Start learning` while a recommendation is pending.\n */\n const control = conversationControl({\n focusNodeId: focus?.nodeId ?? null,\n shownNodeId: selectedId,\n hasNextStep: nextStep !== null,\n }, lang)\n const placeholder = lessonPlaceholder({\n focusNodeId: focus?.nodeId ?? null,\n hasNextStep: nextStep !== null,\n })\n const handoffVisible = handoffLineVisible({\n handoff,\n lesson,\n focusNodeId: focus?.nodeId ?? null,\n })\n\n // First use, same as the docked tab: a blank three-column page reads as a\n // broken page. One question and the sentence that starts everything.\n // Chosen to start something new: a state, not an empty runtime. It gets its\n // own screen so pressing the control visibly does something — both this and\n // \"chosen nothing\" have no course, and a surface reading only `course`\n // renders them identically.\n if (overview?.course && coursePath && (coursePath.path.brief || coursePath.run)) {\n return <CourseLearning view={coursePath} title={overview.course.title}\n onOpenMap={onOpenMapPage} onOpenChat={onOpenChat} onExport={state.exportData} onReset={state.resetAll} />\n }\n\n if (overview?.selection === 'new') {\n return (\n <div className=\"dt-root-wrap\">\n <header className=\"dt-bar\">\n <div className=\"dt-bar-id\">\n <span className=\"dt-bar-name\">{text.startNew}</span>\n <span className=\"dt-bar-sep\" aria-hidden=\"true\">\n ·\n </span>\n <span className=\"dt-bar-note\">{text.startNewNote}</span>\n </div>\n <div className=\"dt-bar-actions\">\n {onOpenChat !== undefined && (\n <button type=\"button\" className=\"dt-bar-back\" onClick={onOpenChat}>\n {text.backToChat}\n </button>\n )}\n <LanguageToggle />\n </div>\n </header>\n <div className=\"dt-root dt-root-welcome\">\n <div className=\"dt-welcome\">\n <EmptyArt kind=\"welcome\" />\n <p className=\"dt-welcome-eyebrow\">{text.startNew}</p>\n <h1 className=\"dt-welcome-title\">{text.startNewTitle}</h1>\n <p className=\"dt-welcome-body\">{text.startNewBody}</p>\n <FeatureRow />\n {library.length > 0 && (\n <>\n <p className=\"dt-section-title\">{text.orContinue}</p>\n <ul className=\"dt-library\">\n {library.map((project) => (\n <li key={project.id}>\n <button type=\"button\" className=\"dt-project\" onClick={() => void state.enterProject(project.id)}>\n <span className=\"dt-project-name\">{project.title}</span>\n <span className=\"dt-project-count\">\n {text.confirmedOf(project.confirmed, project.total)}\n </span>\n </button>\n </li>\n ))}\n </ul>\n </>\n )}\n {onOpenChat !== undefined && (\n <button type=\"button\" className=\"dt-primary dt-here-action\" onClick={onOpenChat}>\n {text.goToChat}\n </button>\n )}\n </div>\n </div>\n </div>\n )\n }\n\n if (overview?.course == null) {\n return (\n <div className=\"dt-root-wrap\">\n <header className=\"dt-bar\">\n <div className=\"dt-bar-id\">\n <span className=\"dt-bar-name\">{text.learning}</span>\n <span className=\"dt-bar-sep\" aria-hidden=\"true\">\n ·\n </span>\n <span className=\"dt-bar-note\">{text.conversationInSidebar}</span>\n </div>\n <div className=\"dt-bar-actions\">\n {onOpenChat !== undefined && (\n <button type=\"button\" className=\"dt-bar-back\" onClick={onOpenChat}>\n {text.backToChat}\n </button>\n )}\n <LanguageToggle />\n </div>\n </header>\n <div className=\"dt-root dt-root-welcome\">\n <div className=\"dt-welcome\">\n <EmptyArt kind=\"welcome\" />\n {/* The library first. A full view with no project open is not an\n empty runtime — the learner's projects are real, and this is\n where they are listed. The welcome copy below only appears when\n there is genuinely nothing yet. */}\n {library.length > 0 && (\n <>\n <p className=\"dt-welcome-eyebrow\">{text.yourLearning}</p>\n <h1 className=\"dt-welcome-title\">{text.yourLearningProjects}</h1>\n <p className=\"dt-welcome-body\">{text.projectsShared}</p>\n <ul className=\"dt-library\">\n {library.map((project) => (\n <li key={project.id}>\n <button type=\"button\" className=\"dt-project\" onClick={() => void state.enterProject(project.id)}>\n <span className=\"dt-project-name\">{project.title}</span>\n <span className=\"dt-project-count\">\n {text.confirmedOf(project.confirmed, project.total)}\n </span>\n <span className=\"dt-project-go\">\n {text.projectGo(project.confirmed)} →\n </span>\n </button>\n </li>\n ))}\n </ul>\n </>\n )}\n <p className=\"dt-welcome-eyebrow\">{text.product}</p>\n <h1 className=\"dt-welcome-title\">{text.welcomeTitle}</h1>\n <p className=\"dt-welcome-body\">{text.welcomeBody}</p>\n <FeatureRow />\n <div className=\"dt-welcome-sample\">\n <span className=\"dt-welcome-sample-label\">{text.tryLabel}</span>\n <span className=\"dt-welcome-sample-text\">{text.trySample}</span>\n </div>\n <p className=\"dt-welcome-note\">{text.skillNote}</p>\n <p className=\"dt-welcome-note\">{text.dataNote}</p>\n {/* The warning belongs with the copy it qualifies, not under the\n data controls — Export and Delete are about state, not about the\n missing skill. */}\n {teachingBrain === false && (\n <p className=\"dt-notice\">\n <b>{text.warningTitle}</b> {text.warningBody}\n </p>\n )}\n <DataFooter onExport={state.exportData} onReset={state.resetAll} />\n </div>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"dt-root-wrap\">\n <header className=\"dt-bar\">\n <div className=\"dt-bar-id\">\n <span className=\"dt-bar-name\">{text.learning}</span>\n <span className=\"dt-bar-sep\" aria-hidden=\"true\">\n ·\n </span>\n <span className=\"dt-bar-note\">\n {sessionId === undefined\n ? text.noConversation\n : text.conversationInSidebar}\n </span>\n </div>\n <div className=\"dt-bar-actions\">\n {onOpenMapPage !== undefined && (\n <button type=\"button\" className=\"dt-bar-back\" onClick={onOpenMapPage}>\n {text.learningMap}\n </button>\n )}\n {onOpenChat !== undefined && (\n <button type=\"button\" className=\"dt-bar-back\" onClick={onOpenChat}>\n {/* Named plainly: the fear this button answers is \"did I lose my chat?\" */}\n {text.backToChat}\n </button>\n )}\n <LanguageToggle />\n </div>\n </header>\n <div className=\"dt-root\">\n {/* ---- course + map ---- */}\n <div className=\"dt-pane\">\n {/* Two layers, because they have two owners. The course and its map\n belong to the learner and every conversation shares them; where\n THIS conversation has got to belongs to this conversation alone.\n One heading over both is what made a brand-new chat look as though\n it had become the learner's old machine-learning session. */}\n <header className=\"dt-course-card\">\n {/* Decorative orbit lines: the header is the map's cover, and an\n empty white rectangle above a list reads as missing content. */}\n <svg className=\"dt-course-orbit\" viewBox=\"0 0 120 78\" aria-hidden=\"true\" focusable=\"false\">\n <circle cx=\"98\" cy=\"14\" r=\"30\" />\n <circle cx=\"98\" cy=\"14\" r=\"19\" strokeDasharray=\"2.5 4\" />\n <circle cx=\"98\" cy=\"14\" r=\"2.4\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"70\" cy=\"46\" r=\"5\" strokeDasharray=\"2 3\" />\n </svg>\n <p className=\"dt-eyebrow\">{text.yourLearning}</p>\n <h1 className=\"dt-course-title\">{overview?.course?.title ?? text.noGoal}</h1>\n {overview?.course != null && (\n <>\n <p className=\"dt-shared\">{text.sharedAcross}</p>\n <p className=\"dt-goal\">\n <b>{text.goalLabel}</b> {overview.course.goal}\n </p>\n </>\n )}\n </header>\n\n <section className=\"dt-here-card\">\n <p className=\"dt-eyebrow dt-eyebrow-here\">{text.thisConversation}</p>\n {focus === null ? (\n <p className=\"dt-here-empty\">{text.noFocus}</p>\n ) : (\n <p className=\"dt-here-focus\">\n <span className=\"dt-here-node\">{focusNodeTitle ?? focus.nodeId}</span>\n {focusNodeState !== null && (\n <span className={`dt-tab-state dt-state-${focusNodeState}`}>{stateLabel(focusNodeState, lang)}</span>\n )}\n </p>\n )}\n {/* Waking a tutor means naming a conversation, so it lives under THIS\n CONVERSATION, not under the shared course — and it acts on the node\n it describes: the focus when there is one, the selection only before\n anything has started. With a recommendation pending it is not\n rendered at all: the card below owns the move, and two controls for\n one next step is how the page came to ask which one to press. */}\n {control !== null && (\n <button\n type=\"button\"\n className=\"dt-primary dt-here-action\"\n onClick={() => state.continueTo(control.nodeId)}\n disabled={starting || sessionId === undefined}\n {...(sessionId === undefined\n ? { title: text.startNoSessionTitle }\n : {})}\n >\n {starting ? text.starting : control.label}\n </button>\n )}\n {sessionId === undefined && (\n <p className=\"dt-caption\">{text.startNoSessionBody}</p>\n )}\n </section>\n\n <p className=\"dt-section-title\">\n {text.diagnosisMap} <span>{text.census(rows.length)}</span>\n </p>\n <StateBar nodes={overview?.nodes ?? []} />\n {rows.length === 0 && (\n <>\n <EmptyArt kind=\"map\" />\n <p className=\"dt-empty\">{text.emptyMap}</p>\n </>\n )}\n {rows.map(({ node, depth }) => (\n <button\n key={node.id}\n type=\"button\"\n className=\"dt-node\"\n // Padding, not margin: the row is `width: 100%`, so an outer\n // margin makes it wider than its pane and pushes it off screen at\n // narrow widths.\n style={{ paddingLeft: `${8 + depth * 14}px` }}\n // Depth and attention as data, so the stylesheet draws the guide\n // and the blocker tint without measuring anything — the same two\n // attributes the docked tab has always set.\n data-depth={depth > 0 ? Math.min(depth, 3) : undefined}\n data-attention={node.state === 'blocked' || node.state === 'weak' ? 'true' : undefined}\n aria-current={node.id === selectedId}\n // Undefined rather than false: React stringifies a boolean onto a\n // data-* attribute, so `data-focused={false}` renders\n // data-focused=\"false\" — which every [data-focused] rule matches.\n data-focused={node.id === focus?.nodeId ? 'true' : undefined}\n onClick={() => state.select(node.id)}\n >\n <span className={`dt-dot dt-state-${node.state}`} data-filled={isFilledState(node.state)}>\n <StateGlyph state={node.state} />\n </span>\n <span className=\"dt-node-title\">\n {node.title}\n <span className=\"dt-node-rel\">\n {relationLabel(node.relation, lang)} · {text.evidenceCount(node.evidenceCount)}\n </span>\n </span>\n <StateChip state={node.state} lang={lang} />\n </button>\n ))}\n <p className=\"dt-caption\">{text.confirmedByEvidence(confirmed, rows.length)}</p>\n </div>\n\n {/* ---- node detail ---- */}\n <div className=\"dt-pane dt-detail\">\n {detail === null ? (\n <div className=\"dt-empty-state\">\n <EmptyArt kind=\"evidence\" />\n <p className=\"dt-empty-title\">\n {loading ? text.loadingMap : text.nothingSelected}\n </p>\n <p className=\"dt-empty\">{text.pickNode}</p>\n </div>\n ) : (\n <>\n <div className=\"dt-detail-hero\">\n <span className={`dt-detail-glyph dt-state-${detail.node.state}`} aria-hidden=\"true\">\n <StateGlyph state={detail.node.state} />\n </span>\n <div className=\"dt-detail-hero-id\">\n <p className=\"dt-eyebrow\">{text.selectedNode}</p>\n <h2>{detail.node.title}</h2>\n </div>\n </div>\n <div className=\"dt-meta\">\n <StateMark state={detail.node.state} lang={lang} />\n <span className=\"dt-chip\">{relationLabel(detail.node.relation, lang)}</span>\n {detail.parent !== null && (\n <span className=\"dt-chip\">{text.parent(detail.parent.title)}</span>\n )}\n {detail.children.length > 0 && (\n <span className=\"dt-chip\">{text.children(detail.children.length)}</span>\n )}\n </div>\n\n <p className=\"dt-why\">{stateExplanation(detail.node.state, lang)}</p>\n\n <NodeLocator\n parent={\n detail.parent === null\n ? null\n : { id: detail.parent.id, title: detail.parent.title, state: detail.parent.state }\n }\n node={{ id: detail.node.id, title: detail.node.title, state: detail.node.state }}\n children={detail.children.map((child) => ({\n id: child.id,\n title: child.title,\n state: child.state,\n }))}\n onSelect={state.select}\n label={text.whereThisSits}\n />\n\n <p className=\"dt-section-title\">\n {text.evidence} <span>{detail.node.evidence.length}</span>\n </p>\n {detail.node.evidence.length === 0 ? (\n <>\n <EmptyArt kind=\"evidence\" />\n <p className=\"dt-empty\">{text.nothingRecorded}</p>\n </>\n ) : (\n <ul className=\"dt-evidence\">\n {detail.node.evidence.map((entry, index) => (\n <li key={index}>\n <span className=\"dt-ev-kind\">{evidenceKindLabel(entry.kind, lang)}</span>\n {entry.readiness !== undefined && (\n <span className=\"dt-ev-readiness\">{readinessLabel(entry.readiness, lang)}</span>\n )}\n {entry.note !== undefined && <span className=\"dt-ev-note\">{entry.note}</span>}\n <span className=\"dt-ev-when\">{formatWhen(entry.at, Date.now(), lang)}</span>\n </li>\n ))}\n </ul>\n )}\n\n {note !== null && <p className=\"dt-caption\">{note}</p>}\n {error !== null && <p className=\"dt-empty\">{error}</p>}\n {/* Starting belongs to THIS CONVERSATION, so the control lives in\n that block on the left rather than here. Keeping a second one\n next to the teaching would put two primary-weight controls on\n one screen and leave the learner guessing which starts what. */}\n {isFocused && <p className=\"dt-caption\">{text.teachingNode}</p>}\n </>\n )}\n </div>\n\n {/* ---- learning surface ---- */}\n <div className=\"dt-pane\">\n <div className=\"dt-lesson-head\">\n <p className=\"dt-eyebrow\">{text.learningSurface}</p>\n {lesson !== null && <span className=\"dt-origin\">{text.lessonOrigin(lesson.origin)}</span>}\n </div>\n {lesson === null ? (\n placeholder === 'preparing' ? (\n <div className=\"dt-empty-state\">\n <EmptyArt kind=\"lesson\" />\n <p className=\"dt-empty\">{text.preparing}</p>\n <p className=\"dt-caption\">{text.preparingBody}</p>\n </div>\n ) : (\n <div className=\"dt-empty-state\">\n <EmptyArt kind=\"lesson\" />\n <p className=\"dt-empty-title\">{text.nothingTaught}</p>\n {/* The invitation names the action that actually exists. With a\n recommendation pending there is no start control to press, and\n a focused node whose tutor has already handed off is not a turn\n that is still being written. */}\n <p className=\"dt-empty\">\n {placeholder === 'press-continue' ? (\n <>\n {text.pressContinuePrefix}\n <b>{continuationLabel(nextStep, lang)}</b>\n {text.pressContinueSuffix}\n </>\n ) : (\n <>\n {text.pressStartPrefix}\n <b>{text.startLearning}</b>\n {text.pressStartSuffix}\n </>\n )}\n </p>\n </div>\n )\n ) : (\n <>\n <h2 className=\"dt-lesson-title\">{lesson.title}</h2>\n <LessonBody blocks={lesson.blocks} />\n </>\n )}\n {/* The wait line is for a wait: with the lesson on screen it would be\n announcing what the page already shows. */}\n {handoffVisible && handoff !== null && (\n <HandoffLine handoff={handoff} onRetry={() => state.continueTo(handoff.targetNodeId)} />\n )}\n {nextStep !== null && (\n <NextStepCard\n nextStep={nextStep}\n busy={starting}\n onContinue={() => state.continueTo(nextStep.targetNodeId ?? nextStep.fromNodeId)}\n />\n )}\n {/* One filled control per column. When the tutor has recorded a next\n step, that step is what the learner should press; answering in the\n chat is the way back to the conversation, which is navigation\n rather than a recommendation, so it steps down. With no\n recommendation it is the only thing left to do, and takes the\n primary weight. */}\n {onOpenChat !== undefined && (\n <button\n type=\"button\"\n className={nextStep === null && lesson !== null ? 'dt-primary' : 'dt-secondary'}\n onClick={onOpenChat}\n >\n {text.answerInChat}\n </button>\n )}\n {lesson !== null && (\n <button type=\"button\" className=\"dt-secondary\" onClick={state.dismissLesson}>\n {text.backToNode}\n </button>\n )}\n {dataNote !== null && <p className=\"dt-caption\">{dataNote}</p>}\n </div>\n </div>\n {/* Rights, not calls to action: under everything, findable, and out of\n the way of the teaching. */}\n <footer className=\"dt-foot\">\n <DataFooter onExport={state.exportData} onReset={state.resetAll} />\n </footer>\n </div>\n )\n}\n","/**\n * dsh-diagnostic-tutor — browser half.\n *\n * Registers the whole navigation story, which is now two destinations:\n *\n * - **Learning map**: an icon in the left sidebar (`sidebar.panellist`, a\n * `list`) whose `id` addresses the matching main panel, plus that panel\n * (`main`, a `keyed` slot, key `diagnostic-tutor-map`);\n * - **Current lesson**: a sidebar action (`sidebar.footer.action`) that opens\n * a session-scoped tab in the right sidebar, `LearningTab`, plus that tab's\n * body and title.\n *\n * There is no full-page lesson panel: one page for browsing and one surface for\n * learning keeps the chat in the main column and avoids a second place that\n * renders the same lesson. The framework supplies the button chrome and the\n * panel switching; this module only says what the icons look like and what each\n * surface renders. No DSH component is re-implemented and no DSH internal is\n * reached into.\n *\n * Every registration goes through `ctx.slots.inject(...)`, which is mandatory\n * rather than stylistic: registering into a slot before its owner has declared\n * it throws.\n */\n\nimport type { Context } from '@deepseek-ai/cordis'\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\n// Type-only side-effect imports. `ctx.slots` is declared by ui-renderer, and\n// each UI package contributes its own `SlotMap` rows through its `/client`\n// subpath — so these are what make `ctx.slots` and the slot keys typecheck.\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-layout/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar-right/client'\nimport type { ReactNode } from 'react'\n\nimport { useState } from 'react'\n\nimport { enableLanguagePersistence, getLang, hydrateLanguage } from './i18n.js'\nimport { tabTitleFor, useLearningText } from './learning-text.js'\nimport { LearningMapPanel, MAP_PANEL_ID } from './map-panel.js'\nimport { useSessionActivity } from './use-session-activity.js'\nimport type { SessionActivitySource } from './use-session-activity.js'\nimport { LearningTab, LearningTabTitle } from './tab.jsx'\nimport { useCurrentSessionId } from './use-session.js'\nimport { injectStyles } from './styles.js'\nimport { MapPageIcon } from './visuals.jsx'\n\n/** Client module name; distinct from the host plugin's name. */\nexport const name = 'diagnostic-tutor-client'\n\n/**\n * `slots` is the only service needed.\n *\n * Declared here rather than in the host half because it is a *client* service:\n * the browser half runs in its own Cordis context against the page's registry.\n */\nexport const inject = ['slots', 'layout', 'sidebarRightTabs', 'uiSession']\n\n/** What the sidebar says when there is no conversation to learn in. */\nexport const NO_SESSION_NOTICE = 'Open or create a conversation to start learning.'\n\n/**\n * The sidebar's Learn entry.\n *\n * It opens the docked tab and never the main column. The conversation is where\n * the teaching happens, so a control that made it disappear was answering a\n * question — \"show me my learning\" — with an answer nobody asked for.\n *\n * With no conversation open there is nothing to dock beside, so it says so\n * rather than opening an empty surface: the tutor teaches in the chat, and a\n * panel with no chat next to it has nowhere to teach.\n */\nexport function LearnAction({\n wide,\n uiSession,\n onOpen,\n}: {\n wide: boolean\n uiSession: unknown\n onOpen: () => void\n}): ReactNode {\n const sessionId = useCurrentSessionId(uiSession)\n const text = useLearningText()\n // Whether the reason is on screen, not the sentence itself: a language switch\n // must not leave an English notice behind.\n const [missingSession, setMissingSession] = useState(false)\n\n return (\n <div className=\"dt-learn-wrap\" data-wide={wide}>\n <button\n type=\"button\"\n className=\"dt-learn\"\n // No `aria-current` and no active fill. The native row's active state\n // is defined as \"this panel is selected in the main column\", and this\n // control deliberately never selects one — borrowing the state would\n // claim something untrue. `:focus-visible` gives keyboard users the\n // same ring the native row shows.\n aria-label={wide ? undefined : text.learnAria}\n title={sessionId === undefined ? text.noSessionNotice : text.learnLabel}\n onClick={() => {\n if (sessionId === undefined) {\n // Not a disabled button with no explanation: the reason is the\n // whole message, and it is short enough to just say.\n setMissingSession(true)\n return\n }\n setMissingSession(false)\n onOpen()\n }}\n >\n {/* The panellist sizes its glyph 16 wide / 18 collapsed. */}\n <PanelIcon size={wide ? 16 : 18} />\n <span className=\"dt-learn-label\">{text.learnLabel}</span>\n </button>\n {wide && missingSession && <p className=\"dt-learn-notice\">{text.noSessionNotice}</p>}\n </div>\n )\n}\n\n/**\n * The docked tab's identity.\n *\n * A tab type has two names: `id` is the implementation's own identity and is\n * what its body and title register under; `kind` is the discriminator `openTab`\n * names. They are the same string here because this plugin ships exactly one\n * implementation of exactly one kind.\n */\nexport const TAB_ID = 'diagnostic-tutor'\n\n/** The mark: a small node graph, drawn inline so it needs no assets. */\nfunction PanelIcon({ size, active = false }: { size: number; active?: boolean }): ReactNode {\n return (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={active ? 1.6 : 1.3}\n aria-hidden=\"true\"\n >\n <circle cx=\"8\" cy=\"3\" r=\"2\" />\n <circle cx=\"3.5\" cy=\"12\" r=\"2\" />\n <circle cx=\"12.5\" cy=\"12\" r=\"2\" />\n <path d=\"M8 5v2.2M8 7.2 4.6 10.3M8 7.2l3.4 3.1\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\n/** Props the `main` slot supplies; identity comes from the session scope adapter. */\ninterface MainPanelProps {\n /** Standard root props; declared so a future reader does not reintroduce a list scan. */\n useSessions?: unknown\n}\n\n/**\n * Load the browser half.\n *\n * @param ctx - the client Cordis context.\n */\nexport function apply(ctx: Context): void {\n // The sheet is injected once and removed with the plugin.\n ctx.effect(() => injectStyles())\n\n // Captured once: the panel needs *actions*, and the standard props a slot\n // receives only expose reads.\n const layout = ctx.get('layout')\n // Read, not injected as a prop: the session scope adapter is how a\n // root-scoped seat learns which conversation is open.\n const uiSession = ctx.get('uiSession')\n // Optional: only a real web shell has the Session Controller. The map page\n // uses it to say whether the tutor is still thinking or the turn failed.\n const sessions = (() => {\n try {\n return ctx.reflect.get('sessions') as SessionActivitySource | undefined\n } catch {\n return undefined\n }\n })()\n\n // Read the saved interface language once, from the real host only. The\n // preview's stub context has no layout service, which is precisely how this\n // stays out of the fixture harness (and out of its no-API-request check).\n if (layout) {\n enableLanguagePersistence()\n void hydrateLanguage()\n }\n\n /**\n * `MAP_PANEL_ID` as the layout names it.\n *\n * `MainPanelId` is a compile-time-only brand — the value is the same string\n * at runtime — so a plugin that owns the id constant can name it here instead\n * of depending on the brand package. Derived from the service rather than\n * imported, so this file still pulls in no `@deepseek-ai/*` value.\n */\n type PanelRef = Parameters<NonNullable<typeof layout>['selectPanel']>[0]\n const mapPanelRef = MAP_PANEL_ID as unknown as PanelRef\n\n // ---------------------------------------------------------------------------\n // The map page's sidebar entry.\n //\n // A panellist id addresses a main panel, so this one replaces the main column\n // with the diagnosis map — which is the point of a separate page. It is\n // deliberately NOT the Learn action: Learn opens the docked tab and leaves\n // the conversation alone, while this entry is the learner choosing to go and\n // look at the map.\n // ---------------------------------------------------------------------------\n ctx.slots.inject('sidebar.panellist', () =>\n ctx.slots.register(\n { name: 'sidebar.panellist', id: MAP_PANEL_ID, order: 41, label: 'Learning map' },\n function BoundMapIcon(props: { size: number; active: boolean }): ReactNode {\n return <MapPageIcon size={props.size} active={props.active} />\n },\n ),\n )\n\n // ---------------------------------------------------------------------------\n // The sidebar entry.\n //\n // This is a `sidebar.footer.action`, NOT a `sidebar.panellist` entry, and the\n // difference is the whole point. A panellist `id` is documented as the\n // \"matching main panel key\" and the shell's click handler is\n // `layout.selectPanel(id)` — so a panellist entry CANNOT do anything except\n // replace the main column. Putting Learn there is what made pressing it\n // replace the conversation, which reads as the chat having been lost.\n //\n // This action opens the docked tab instead and leaves the main column alone.\n // ---------------------------------------------------------------------------\n ctx.slots.inject('sidebar.footer.action', () =>\n ctx.slots.register({ name: 'sidebar.footer.action', id: 'diagnostic-tutor-learn', order: 40 }, function BoundLearn(props: {\n wide: boolean\n }): ReactNode {\n return <LearnAction wide={props.wide} uiSession={uiSession} onOpen={openDockedTab} />\n }),\n )\n\n // ---------------------------------------------------------------------------\n // The docked tab.\n //\n // This is the only lesson surface now. An earlier version also offered a\n // full-page panel in the main column; that was a second way to read the same\n // lesson and a third destination to choose from, so it was removed. The\n // conversation keeps the main column; the lesson sits beside it here, where\n // answering a check never means leaving the lesson.\n //\n // Registration is two stages, and the order is enforced by the framework:\n // declare the tab type, then register the body and title under the type's own\n // `id`, because that is the key the seats dispatch on.\n // ---------------------------------------------------------------------------\n ctx.sidebarRightTabs.register({\n id: TAB_ID,\n kind: TAB_ID,\n // A page type, not a resource viewer: it has no address globs, so it is\n // opened by kind and never competes to render someone else's resource.\n // Evaluated when the tab chrome renders; the title seat below is a live\n // component so an in-place language switch updates the visible chip too.\n title: () => tabTitleFor(getLang()),\n priority: 'extension',\n })\n\n /**\n * Dock the tab, retrying briefly.\n *\n * The right sidebar hosts **session-scoped** tabs, so it can only accept one\n * while a session surface is mounted. Returning to the conversation mounts\n * one, so the useful moment to dock is just after that. Retrying covers the\n * gap between the click and the surface mounting.\n */\n const openDockedTab = (attempt = 0): void => {\n // Resolved with `get`, not read as a property: this plugin injects the\n // *registry*, not the navigation controller, and reading a service that was\n // never injected throws rather than returning undefined.\n const sidebarRight = ctx.get('sidebarRight')\n if (!sidebarRight) return\n try {\n sidebarRight.openTab(TAB_ID)\n } catch {\n if (attempt < 8) setTimeout(() => openDockedTab(attempt + 1), 400)\n }\n }\n\n /**\n * Switch to the conversation a project's learning belongs to.\n *\n * Resolved by the host from stored focus/run/selection records, then handed\n * to the shell's own session navigation (`uiWorkspace.openSession`). The\n * service is optional: a profile without it keeps the old behaviour of\n * opening the project in the current conversation.\n *\n * @returns whether navigation was actually requested; false means fall back.\n */\n const openLearningSession = (target: string): boolean => {\n const uiWorkspace = (() => {\n try {\n return ctx.reflect.get('uiWorkspace') as { openSession?: (sessionId: string) => void } | undefined\n } catch {\n return undefined\n }\n })()\n if (uiWorkspace?.openSession === undefined) return false\n try {\n uiWorkspace.openSession(target)\n } catch {\n return false\n }\n // The tab is session-scoped: dock it once the target conversation's surface\n // is mounted, so \"enter learning\" lands on the lesson too.\n setTimeout(() => openDockedTab(), 600)\n return true\n }\n\n // The map page: the plugin's only main panel.\n //\n // Route and knowledge live here; the lesson lives in the docked tab. \"Open\n // current lesson\" therefore leaves this page and returns to the conversation\n // with the lesson tab open, instead of replacing one main column with another.\n ctx.slots.inject('main', () =>\n ctx.slots.register(\n { name: 'main', key: MAP_PANEL_ID as unknown as NonNullable<PanelRef> },\n function BoundMapPanel(props: MainPanelProps): ReactNode {\n const sessionId = useCurrentSessionId(uiSession)\n const sessionActivity = useSessionActivity(sessions, sessionId)\n return (\n <LearningMapPanel\n {...props}\n {...(sessionId === undefined ? {} : { sessionId })}\n {...(sessionActivity === undefined ? {} : { sessionActivity })}\n onOpenChat={() => layout?.selectPanel(null)}\n onOpenLesson={() => {\n layout?.selectPanel(null)\n openDockedTab()\n }}\n onOpenSession={openLearningSession}\n />\n )\n },\n ),\n )\n\n ctx.slots.inject('sidebar.right.pane.tab', () =>\n ctx.slots.register({ name: 'sidebar.right.pane.tab', key: TAB_ID }, function BoundTab(\n props: PropsRuntime<'sidebar.right.pane.tab'>,\n ): ReactNode {\n return (\n <LearningTab\n {...props}\n uiSession={uiSession}\n onOpenMapPage={() => layout?.selectPanel(mapPanelRef)}\n onOpenChat={() => layout?.selectPanel(null)}\n onOpenSession={openLearningSession}\n />\n )\n }),\n )\n ctx.slots.inject('sidebar.right.pane.tab.title', () =>\n ctx.slots.register({ name: 'sidebar.right.pane.tab.title', key: TAB_ID }, LearningTabTitle),\n )\n\n // The surface is NOT docked on load.\n //\n // An earlier version opened it whenever a focus existed, on the theory that\n // resuming should be visible. In practice it meant installing this plugin\n // silently added a column to the layout of anyone with learning state — the\n // first thing a returning user sees is their window rearranged by something\n // they did not ask for. The tab opens when the learner acts: pressing\n // *Start learning*, or continuing to a next step.\n}\n\nexport { LearningPanel } from './app.js'\nexport { LearningMapPanel, MAP_PANEL_ID } from './map-panel.js'\nexport { PathPage } from './path-page.js'\nexport { LearningTab, LearningTabTitle } from './tab.jsx'\nexport { LessonBody, BlockView, BLOCK_RENDERERS } from './blocks.js'\nexport type { PanelClient } from './app.js'\n"],"mappings":"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"}
|