@office-kit/pptx-dev 0.9.1 → 0.13.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 +100 -20
- package/dist/animation-player.js +1 -0
- package/dist/build-cpmt-r9Q.mjs +131 -0
- package/dist/build-cpmt-r9Q.mjs.map +1 -0
- package/dist/cli.mjs +1914 -123
- package/dist/cli.mjs.map +1 -1
- package/dist/editor-chunks/agent-tools-emRGeP2H-WN4XUNCN.js +425 -0
- package/dist/editor-chunks/chunk-Q3KT5ZGJ.js +738 -0
- package/dist/editor.js +24 -0
- package/dist/index.d.mts +36 -1
- package/dist/index.mjs +1 -1
- package/dist/media-player.js +1 -0
- package/dist/{src-YH6kAXbA.mjs → src-DASlwPDd.mjs} +84 -7
- package/dist/src-DASlwPDd.mjs.map +1 -0
- package/dist/terminal-client.js +18 -18
- package/dist/worker.mjs +1 -65
- package/dist/worker.mjs.map +1 -1
- package/package.json +8 -7
- package/dist/src-YH6kAXbA.mjs.map +0 -1
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/history.ts","../src/text-edit.ts","../src/visual-review.ts","../src/styles.ts","../src/page.ts","../src/agent-page.ts","../src/authoring-guidance.ts","../src/terminal.ts","../src/chat.ts","../src/server.ts","../src/cli.ts"],"sourcesContent":["import { lstat, mkdir, readFile, readdir, realpath, unlink, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative, sep } from 'node:path';\n\nconst SNAPSHOT_LIMIT = 64 * 1024 * 1024;\nconst HISTORY_LIMIT = 128 * 1024 * 1024;\nconst ENTRY_LIMIT = 50;\nconst ignored = new Set(['node_modules', '.git', 'dist', '.office-kit']);\nexport const historyFile = (name: string) =>\n !name.split(sep).some((part) => ignored.has(part)) &&\n /\\.([cm]?[jt]sx?|json|css|md|ya?ml|pptx|png|jpe?g|gif|bmp|tiff?|emf|wmf|svg)$/i.test(name);\ntype Snapshot = Map<string, Buffer>;\ntype Change = { file: string; before: Buffer | undefined; after: Buffer | undefined };\ntype Entry = { label: string; changes: Change[] };\nconst equal = (a?: Buffer, b?: Buffer) => a === b || (!!a && !!b && a.equals(b));\n\n/** Session-local project history. Overlapping agent turns form one transaction. */\nexport async function createHistory(directory: string, notify: () => void = () => {}) {\n const root = await realpath(directory);\n let baseline: Snapshot = new Map();\n let initialized = false;\n const past: Entry[] = [],\n future: Entry[] = [];\n const active = new Set<string>();\n let label = '';\n let error: string | null = null;\n let queue = Promise.resolve();\n let restoring = false;\n function serial<T>(action: () => Promise<T>): Promise<T> {\n const result = queue.then(action);\n queue = result.then(\n () => {},\n () => {},\n );\n return result\n .catch((cause) => {\n error = cause instanceof Error ? cause.message : String(cause);\n throw cause;\n })\n .finally(notify);\n }\n async function snapshot(): Promise<Snapshot> {\n const files = new Map<string, Buffer>();\n let bytes = 0;\n async function visit(dir: string): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n await Promise.all(\n entries.map(async (item) => {\n if (ignored.has(item.name) || item.isSymbolicLink()) return;\n const file = join(dir, item.name);\n if (item.isDirectory()) return visit(file);\n if (!item.isFile() || !historyFile(file)) return;\n const info = await lstat(file);\n bytes += info.size;\n if (bytes > SNAPSHOT_LIMIT)\n throw new Error(\n 'Undo history exceeds 64 MiB of project source/assets. Move unused assets outside the deck folder.',\n );\n if (info.isSymbolicLink() || (await realpath(file)) !== file)\n throw new Error('Project paths changed while recording history. Try again.');\n const content = await readFile(file);\n files.set(relative(root, file), content);\n }),\n );\n }\n await visit(root);\n return files;\n }\n function record(next: Snapshot, description: string) {\n if (!initialized) {\n baseline = next;\n initialized = true;\n error = null;\n return;\n }\n const changes: Change[] = [];\n for (const file of new Set([...baseline.keys(), ...next.keys()])) {\n const before = baseline.get(file),\n after = next.get(file);\n if (!equal(before, after)) changes.push({ file, before, after });\n }\n if (changes.length) {\n past.push({ label: description, changes });\n future.length = 0;\n const size = () =>\n past.reduce(\n (sum, item) =>\n sum +\n item.changes.reduce(\n (n, change) => n + (change.before?.length ?? 0) + (change.after?.length ?? 0),\n 0,\n ),\n 0,\n );\n while (past.length > ENTRY_LIMIT || (past.length > 1 && size() > HISTORY_LIMIT)) past.shift();\n }\n baseline = next;\n error = null;\n }\n async function contents(file: string): Promise<Buffer | undefined> {\n const path = join(root, file);\n let parent = dirname(path);\n while (true) {\n try {\n if ((await realpath(parent)) !== parent)\n throw new Error('Undo refused: a project directory is now a symbolic link.');\n break;\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code !== 'ENOENT') throw cause;\n parent = dirname(parent);\n }\n }\n try {\n const info = await lstat(path);\n if (!info.isFile() || info.isSymbolicLink())\n throw new Error('Undo refused: a project file changed type.');\n return await readFile(path);\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw cause;\n }\n }\n async function replace(change: Change, expected?: Buffer, next?: Buffer) {\n if (!equal(await contents(change.file), expected))\n throw new Error(`History conflict in ${change.file}. Newer changes were preserved.`);\n const file = join(root, change.file);\n if (next) {\n await mkdir(dirname(file), { recursive: true });\n await writeFile(file, next);\n } else await unlink(file);\n }\n try {\n record(await snapshot(), 'Initial state');\n } catch (cause) {\n error = cause instanceof Error ? cause.message : String(cause);\n }\n return {\n state: () => ({\n undo: past.at(-1)?.label ?? null,\n redo: future.at(-1)?.label ?? null,\n busy: active.size > 0 || restoring,\n error,\n }),\n capture: () =>\n serial(async () => {\n if (!active.size && !restoring) record(await snapshot(), 'Source edit');\n }),\n begin: (id: string, description: string) =>\n serial(async () => {\n if (active.has(id)) return;\n if (!active.size) {\n record(await snapshot(), 'Source edit');\n label = description;\n } else label = 'Concurrent edits';\n active.add(id);\n }),\n end: (id: string) =>\n serial(async () => {\n if (!active.delete(id) || active.size) return;\n record(await snapshot(), label);\n }),\n move: (direction: 'undo' | 'redo') =>\n serial(async () => {\n if (active.size)\n throw new Error(\n 'Wait for all agents and text edits to finish before using Undo or Redo.',\n );\n // A save made outside the preview is also an edit; never silently overwrite it.\n record(await snapshot(), 'Source edit');\n const from = direction === 'undo' ? past : future;\n const to = direction === 'undo' ? future : past;\n const item = from.at(-1);\n if (!item) throw new Error(`Nothing to ${direction}.`);\n const before = (c: Change) => (direction === 'undo' ? c.after : c.before);\n const after = (c: Change) => (direction === 'undo' ? c.before : c.after);\n const applied: Change[] = [];\n restoring = true;\n try {\n await Promise.all(\n item.changes.map(async (change) => {\n if (!equal(await contents(change.file), before(change)))\n throw new Error(\n `History conflict in ${change.file}. Newer changes were preserved.`,\n );\n }),\n );\n // Ordered writes let a failed transaction restore only the files it wrote.\n for (const change of item.changes) {\n await replace(change, before(change), after(change));\n applied.push(change);\n }\n for (const change of item.changes) {\n const content = after(change);\n if (content) baseline.set(change.file, content);\n else baseline.delete(change.file);\n }\n from.pop();\n to.push(item);\n error = null;\n } catch (cause) {\n const failures = [];\n for (const change of applied.reverse()) {\n try {\n await replace(change, after(change), before(change));\n } catch (rollback) {\n failures.push(String(rollback));\n }\n }\n if (failures.length)\n throw new Error(`${String(cause)}; rollback incomplete: ${failures.join('; ')}`);\n throw cause;\n } finally {\n restoring = false;\n }\n }),\n };\n}\n","import { planTextEdit, verifyTextEdit, type TextEditPlan } from '@office-kit/pptx-dsl/source-edit';\nimport { readFile, writeFile, realpath } from 'node:fs/promises';\nimport { dirname, relative, isAbsolute } from 'node:path';\n\ninterface Preview {\n revision: number;\n slides: string[];\n slideTexts: string[];\n dependencies: string[];\n}\ntype Change = Extract<TextEditPlan, { ok: true }>['change'];\n\nconst planErrors: Record<Extract<TextEditPlan, { ok: false }>['reason'], string> = {\n invalid: 'Invalid text edit.',\n 'not-unique-on-slide': 'Text cannot be located uniquely on this slide. Use Apply with AI.',\n 'not-found': 'Text is computed or has multiple source matches. Use Apply with AI.',\n ambiguous: 'Text is computed or has multiple source matches. Use Apply with AI.',\n};\n\n/** Literal edits are deliberately conservative: computed and ambiguous text goes to the agent. */\nexport function createTextEditor(\n entry: string,\n preview: () => Preview,\n verify: () => Promise<string | null>,\n) {\n let busy = false;\n async function restore(change: Change) {\n if (\n (await realpath(change.path)) !== change.path ||\n (await readFile(change.path, 'utf8')) !== change.after\n )\n throw new Error('Source changed during editing. Your newer changes were preserved.');\n await writeFile(change.path, change.before);\n }\n return {\n async edit(value: { revision: number; slide: number; before: string; after: string }) {\n if (busy) throw new Error('A text edit is already running.');\n busy = true;\n try {\n const current = preview();\n if (value.revision !== current.revision)\n throw new Error('Preview changed. Select the text again.');\n if (\n !Number.isInteger(value.slide) ||\n !current.slides[value.slide] ||\n typeof value.before !== 'string' ||\n typeof value.after !== 'string'\n )\n throw new Error(planErrors.invalid);\n const root = await realpath(dirname(entry));\n const files = await Promise.all(\n [...new Set(current.dependencies)]\n .filter((file) => /\\.[cm]?[jt]sx?$/.test(file))\n .map(async (file) => {\n const path = await realpath(file),\n local = relative(root, path);\n if (\n local.startsWith('..') ||\n isAbsolute(local) ||\n local.split(/[\\\\/]/).includes('node_modules')\n )\n return null;\n return { path, source: await readFile(path, 'utf8') };\n }),\n );\n const plan = planTextEdit({\n files: files.filter((file) => file !== null),\n slideText: current.slideTexts[value.slide]!,\n before: value.before,\n after: value.after,\n });\n if (!plan.ok) throw new Error(planErrors[plan.reason]);\n const { change } = plan;\n if (\n preview().revision !== current.revision ||\n (await realpath(change.path)) !== change.path ||\n (await readFile(change.path, 'utf8')) !== change.before\n )\n throw new Error('Source changed. Select the text again.');\n await writeFile(change.path, change.after);\n try {\n const error = await verify();\n if (error) throw new Error('The text change did not build.');\n if (\n !verifyTextEdit({\n previous: current,\n next: preview(),\n slide: value.slide,\n before: value.before,\n after: value.after,\n }).ok\n )\n throw new Error(\n 'This source affects other text or slides. Use Apply with AI for a local edit.',\n );\n if (\n (await realpath(change.path)) !== change.path ||\n (await readFile(change.path, 'utf8')) !== change.after\n )\n throw new Error('Source changed during editing.');\n } catch (error) {\n await restore(change);\n await verify();\n throw error;\n }\n } finally {\n busy = false;\n }\n },\n };\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { chromium, type Browser } from 'playwright';\n\nexport interface VisualReview {\n prompt: string;\n images: string[];\n}\n/** Each session compares against its own turn's preview, including offscreen slides. */\nexport function createVisualReviewer(entry: string, slides: () => string[]) {\n let before: string[] = [];\n let rounds = 0;\n return {\n begin() {\n before = slides().slice();\n rounds = 0;\n },\n async next(): Promise<VisualReview | undefined> {\n const current = slides().slice();\n const changed = current.flatMap((svg, index) =>\n svg !== before[index] ? [{ svg, index }] : [],\n );\n if (!changed.length) return;\n if (rounds >= 3)\n throw new Error(\n 'Visual review reached its correction limit. Review the latest slide manually before continuing.',\n );\n const round = rounds + 1;\n let browser: Browser;\n try {\n browser = await chromium.launch({\n channel: process.env.PLAYWRIGHT_CHANNEL || 'chrome',\n headless: true,\n });\n } catch {\n try {\n browser = await chromium.launch({ headless: true });\n } catch {\n throw new Error(\n 'Visual review needs Chrome or a Playwright Chromium installation. Install Chrome and retry. The source edit was saved; visual review was not completed.',\n );\n }\n }\n try {\n const directory = join(dirname(entry), '.office-kit', 'reviews', randomUUID());\n await mkdir(directory, { recursive: true });\n const page = await browser.newPage({\n viewport: { width: 1600, height: 1000 },\n deviceScaleFactor: 1,\n });\n // Decks may reference remote assets. Review must not make network requests or execute SVG scripts.\n await page.route('**/*', (route) => route.abort());\n const images: string[] = [];\n for (const { svg, index } of changed) {\n await page.setContent(\n '<meta http-equiv=\"Content-Security-Policy\" content=\"default-src \\'none\\'; style-src \\'unsafe-inline\\'; img-src data: blob:; font-src data:\"><style>html,body{margin:0}svg{display:block;width:1600px;height:auto}</style>' +\n svg,\n );\n await page.evaluate(() => document.fonts.ready.then(() => undefined));\n const file = join(directory, `slide-${index + 1}.png`);\n await page.locator('svg').first().screenshot({ path: file, timeout: 15000 });\n images.push(file);\n }\n const prompt = `Visual review after the edit (round ${round}/3). Open every attached screenshot (or read each local PNG path below) before finishing. Check clipping, overlap, alignment, spacing, legibility and foreground/background contrast. ${round === 3 ? 'This is the final review: do not edit further; report any remaining defects to the user.' : 'Fix concrete design defects only on these affected slides;'} preserve the user's exact text and unrelated work. Do not claim visual verification unless you inspected the images. If there are no defects, finish without edits. Screenshots: ${JSON.stringify(images)}`;\n await writeFile(join(directory, 'review.txt'), prompt);\n before = current;\n rounds = round;\n return { prompt, images };\n } finally {\n await browser.close();\n }\n },\n };\n}\n","export const previewStyles = `*{box-sizing:border-box}\nbody{margin:0;height:100dvh;overflow:hidden;background:#e9eaf4;color:#dce1f1;font:13px system-ui;display:grid;grid-template-rows:60px minmax(0,1fr) 42px}\nbutton,a,select{font:inherit;color:inherit}button,select,.download{border:1px solid #30364d;border-radius:9px;background:#191e30;padding:7px 12px;text-decoration:none;cursor:pointer}\nbutton:hover:not(:disabled),.download:hover{background:#292f48}button:disabled{opacity:.4;cursor:default}\n:focus-visible{outline:2px solid #9b87ff;outline-offset:3px}\nheader{display:flex;align-items:center;gap:16px;padding:0 20px;background:linear-gradient(110deg,#171b2f,#211c38 60%,#171b2f);border-bottom:1px solid #38304d;min-width:0}\n.brand{font-weight:700;font-size:17px;white-space:nowrap}.badge{font-size:11px;color:#b2a4fb;background:#342c54;border-radius:4px;padding:3px 6px}\n#status{flex:1;color:#9ba5be;min-width:0}#present{background:#7154e8;color:#191e30;border-color:#7154e8}\n.workspace{--filmstrip-width:190px;--chat-width:38vw;display:grid;grid-template-columns:var(--filmstrip-width) minmax(0,1fr) clamp(280px,var(--chat-width),calc(100vw - var(--filmstrip-width) - 240px));min-height:0}\n.filmstrip{background:#111625;border-right:1px solid #30364d;overflow:auto;overscroll-behavior:contain;padding:16px 12px}\n.filmstrip h2{margin:0 0 12px 26px;text-transform:uppercase;letter-spacing:.1em;font-size:10px;color:#8490ac;font-weight:600}\n#thumbnails{display:flex;flex-direction:column;gap:12px;margin:0;padding:0;list-style:none}\n.thumbnail{display:flex;align-items:flex-start;gap:8px;width:100%;padding:4px 3px;border:0;background:transparent;border-radius:5px;text-align:left}\n.slide-number{width:18px;flex:none;text-align:right;font-size:11px;color:#8995b0;padding-top:5px}\n.thumbnail img{display:block;min-width:0;width:calc(100% - 26px);background:white;box-shadow:0 1px 4px #080b1933;border:2px solid transparent;border-radius:3px;aspect-ratio:var(--slide-ratio,16/9);object-fit:contain}\n.thumbnail[aria-current=\"true\"]{background:#31274e}.thumbnail[aria-current=\"true\"] img{border-color:#9b87ff}.thumbnail[aria-current=\"true\"] .slide-number{color:#b6a5ff;font-weight:700}\nmain{min-width:0;min-height:0;display:flex;flex-direction:column}\n#error{flex:none;max-height:30%;overflow:auto;background:#fff0ef;color:#922e25;margin:0;padding:16px 20px;white-space:pre-wrap;border-bottom:1px solid #e8bbb7}\n#error[hidden]{display:none}\n#stage{background:radial-gradient(ellipse at 20% 15%,#ddd8f6aa,transparent 65%),radial-gradient(#a0a4bf33 1px,transparent 1px),#edeef6;background-size:auto,18px 18px,auto;flex:1;min-height:0;overflow:auto;display:flex;padding:32px;overscroll-behavior:contain}\n#slide{position:relative;flex:none;margin:auto;background:white;box-shadow:0 20px 70px #28234825,0 2px 8px #28234815;overflow:hidden}\n#slide{user-select:text;-webkit-user-select:text}\n#empty{margin:auto;color:#8995b0}\nfooter{display:flex;align-items:center;gap:14px;padding:0 16px;background:#191e30;border-top:1px solid #30364d;font-size:12px}\n#count{min-width:90px}footer .hint{flex:1;color:#8995b0}footer button{padding:3px 10px}footer select{padding:3px 8px}\n#presentation-controls{display:none}\nbody.presenting{grid-template-rows:minmax(0,1fr);background:#111}\n.presenting header,.presenting footer,.presenting .filmstrip,.presenting #chat{display:none}\n.presenting .workspace{grid-template-columns:minmax(0,1fr)}.presenting #stage{padding:0;background:#111}.presenting #slide{box-shadow:none}\n.presenting #presentation-controls{display:flex;position:fixed;bottom:16px;left:50%;transform:translateX(-50%);align-items:center;gap:12px;background:#202735e8;color:white;padding:6px;border-radius:8px;opacity:0;transition:opacity .15s}\n.presenting #presentation-controls:hover,.presenting #presentation-controls:focus-within{opacity:1}\n#presentation-controls button{background:transparent;color:white;border-color:#5d6575}\n@media(max-width:700px){.workspace{grid-template-columns:140px minmax(0,1fr)}.filmstrip{padding:12px 5px}header{padding:0 12px;gap:10px}.badge,footer .hint{display:none}#stage{padding:16px}footer{gap:8px}#status{font-size:11px}.download{padding:7px 8px}}\n#chat{position:relative;min-width:0;min-height:0;display:flex;flex-direction:column;background:#191e30;border-left:1px solid #30364d}\n#chat-resizer{position:absolute;left:-5px;top:0;bottom:0;width:10px;z-index:3;cursor:col-resize;touch-action:none}#chat-resizer::after{content:\"\";position:absolute;left:4px;top:0;bottom:0;width:2px}#chat-resizer:hover::after,#chat-resizer:focus-visible::after,.resizing-chat #chat-resizer::after{background:#9b87ff}body.resizing-chat,body.resizing-chat *{cursor:col-resize!important;user-select:none!important}\n.chat-heading{display:flex;align-items:center;gap:8px;padding:16px;border-bottom:1px solid #2a3046}.chat-heading strong{flex:1}.chat-heading button{padding:5px 8px}\n#messages{flex:1;overflow:auto;padding:16px;display:flex;flex-direction:column;gap:16px;overscroll-behavior:contain}\n.chat-message{white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.6}.chat-message.user{background:#302752;border-radius:8px;padding:10px 12px}.chat-message small{display:block;color:#a4afc6;font-size:11px;margin-bottom:4px}.chat-intro{color:#8995b0;line-height:1.7}\n#chat-form{padding:14px;border-top:1px solid #2a3046}#chat-context{font-size:11px;color:#9b87ff;margin-bottom:8px}#chat-input{font:inherit;resize:vertical;min-height:90px;max-height:200px;width:100%;padding:10px;border:1px solid #30364d;border-radius:8px} .chat-actions{display:flex;gap:8px;margin-top:8px;align-items:center}.chat-actions select{min-width:0;flex:1}#chat-send{background:#7154e8;color:white}#chat-status{padding:0 14px 12px;color:#9ba5be;font-size:11px;white-space:pre-wrap;max-height:90px;overflow:auto}\nbody.chat-hidden .workspace{grid-template-columns:190px minmax(0,1fr)}body.chat-hidden #chat{display:none}\nbody.presenting .workspace{grid-template-columns:minmax(0,1fr)}\n@media(max-width:1000px){.workspace{--filmstrip-width:120px;--chat-width:380px}.filmstrip{padding:12px 5px}#stage{padding:16px}}\n@media(max-width:700px){#chat-resizer{display:none}.workspace{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(160px,1fr) minmax(200px,1fr)}.filmstrip{display:none}#chat{border-top:1px solid #30364d}.chat-heading{padding:8px 12px}#chat-form{padding:8px 12px}#chat-input{min-height:50px}body.chat-hidden .workspace,.presenting .workspace{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr)}.brand{font-size:14px}header{gap:6px}.download{font-size:11px}}\n@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}\n\n[hidden]{display:none!important}\n.chat-heading{padding:10px 14px}.chat-heading select{flex:1;min-width:0;font-weight:600;border:0;padding-left:0}\n#chat-context{margin:0;padding:8px 14px;border-bottom:1px solid #2a3046;color:#9ba5be;font-size:11px}\n#claude-panel,#codex-panel{display:flex;flex-direction:column;flex:1;min-height:0}\n#claude-panel{background:#171b24;color:#e4e8f0}\n.terminal-toolbar{display:flex;align-items:center;gap:8px;padding:8px 12px;font-size:11px;min-height:40px}.terminal-toolbar span{flex:1;overflow-wrap:anywhere}.terminal-toolbar button{font-size:11px;padding:5px 8px;background:#262d3b;border-color:#45516a;color:#e4e8f0}\n.terminal-toolbar button:hover:not(:disabled){background:#35415a}\n#terminal{flex:1;min-height:0;overflow:hidden;padding:0}.xterm{height:100%}\n#chat-input{min-height:70px;resize:vertical}.chat-shortcut{flex:1;color:#9098a7;font-size:10px}\n.markdown{white-space:normal;line-height:1.65}.markdown>:first-child{margin-top:0}.markdown>:last-child{margin-bottom:0}.markdown p{margin:0 0 .8em}.markdown pre{overflow:auto;padding:10px;background:#22283b;border:1px solid #2a3046;border-radius:6px;white-space:pre}.markdown code{font-size:.9em;background:#22283b;border-radius:3px;padding:1px 4px}.markdown pre code{padding:0}.markdown ul,.markdown ol{padding-left:22px}.markdown blockquote{margin-left:0;padding-left:12px;border-left:3px solid #30364d;color:#9ba5be}.markdown table{display:block;overflow:auto;border-collapse:collapse}.markdown th,.markdown td{padding:6px 8px;border:1px solid #30364d}.markdown h1,.markdown h2,.markdown h3{font-size:1.1em}.markdown a{color:#b6a5ff;text-decoration:underline}\n\n.brand{display:flex;align-items:center;gap:10px;letter-spacing:-.5px}.brand svg{width:32px;height:32px;flex:none}.brand em{font-style:normal;font-weight:400;color:#aca4ce}.badge{text-transform:uppercase;letter-spacing:.14em;font-size:9px;border:1px solid #61508866;border-radius:5px}#status{font-size:11px}#present,#chat-send{color:#fff;background:linear-gradient(120deg,#8061ed,#634de0);border-color:#a38aef55;box-shadow:0 3px 15px #7455e82a}#toggle-chat[aria-expanded=\"true\"]{background:#35294f;border-color:#6f528b;color:#d6c5ff}.download{background:transparent;color:#bcc5dc}header button,header .download{font-size:12px}.filmstrip{background:linear-gradient(180deg,#161b2c,#111625)}.thumbnail{transition:background .15s}.thumbnail[aria-current=\"true\"]{box-shadow:inset 0 0 0 1px #9980ef33}.thumbnail[aria-current=\"true\"] img{box-shadow:0 0 16px #9567ed22}footer{background:#141929;color:#b9c2d8}footer .hint{font-size:10px}#count{font-size:11px;font-weight:600}#chat{background:#101422}.workspace-heading{display:flex;align-items:center;justify-content:space-between;padding:12px 14px;color:#c7bee5;font-size:10px;font-weight:600;letter-spacing:.12em}.workspace-heading b{font-size:9px;color:#89cdbc;font-weight:500;letter-spacing:.08em}.workspace-heading b::before{content:\"\";display:inline-block;width:5px;height:5px;background:#86d5bb;border-radius:50%;margin-right:6px;box-shadow:0 0 8px #86d5bb66}.chat-heading{background:#191e30}.chat-heading select{background:transparent;color:#e4e5f3;font-size:12px}.chat-heading option{background:#191e30}#chat-context{background:#1a1e30;color:#a59abb;font-size:10px}.terminal-toolbar{gap:6px;padding:8px;font-size:10px}.terminal-toolbar button{white-space:nowrap}#terminal-start{background:#6c52c9;border-color:#9678e9}.terminal-frame{display:flex;flex:1;min-height:0;min-width:0;padding:4px 8px 8px;overflow:hidden}#terminal{width:100%;min-width:0}#chat-input{color:#e2e5f3;background:#131827;border-color:#3b3f58}#chat-input:focus{outline:none;border-color:#9b87ff;box-shadow:0 0 0 3px #9b87ff18}.chat-message.user{border:1px solid #67509655}.chat-intro{font-size:12px}.markdown pre{border-color:#3a4059}.markdown code{color:#d2c6fa}#error{background:#38242b;color:#ffb8b0}\n@media(max-width:700px){.brand svg{width:26px;height:26px}.brand{gap:6px}.workspace-heading{padding:7px 12px}header .download{font-size:10px;padding:7px}.badge{display:none}}\n`;\n","import { previewStyles } from './styles.ts';\nexport const page = `<!doctype html>\n<html lang=\"en\">\n<meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\">\n<title>Office Kit — PowerPoint preview</title>\n<link rel=\"stylesheet\" href=\"/terminal.css\">\n<style>\n${previewStyles}\n.slide-edit-tools{display:flex;gap:6px;padding:8px 14px;flex-shrink:0}.slide-edit-tools{align-items:center;flex-wrap:wrap}.slide-edit-tools [data-undo],.slide-edit-tools [data-redo]{width:32px;height:32px;padding:0;display:grid;place-items:center;border:1px solid transparent;border-radius:8px;background:transparent;color:#625873;box-shadow:none}.slide-edit-tools [data-undo]:hover:not(:disabled),.slide-edit-tools [data-redo]:hover:not(:disabled){background:#ede7fa;border-color:#d6cbea;color:#513399}.slide-edit-tools button:disabled{opacity:.35}.slide-edit-tools button:focus-visible{outline:2px solid #8966df;outline-offset:2px}.slide-edit-tools [hidden]{display:none}.slide-edit-tools span{font-size:11px;color:#625873;flex:1}.slide-edit-tools small{font-size:11px;color:#634396}.slide-hover{position:fixed;border:1px solid #b094ff;pointer-events:none;z-index:19;border-radius:2px}.slide-text-input{position:fixed;z-index:22;resize:none;background:#202438;color:#fff;border:2px solid #b094ff;border-radius:3px;padding:0;box-shadow:0 0 0 3px #b094ff30;line-height:1.15}.slide-text-input[hidden],.slide-hover[hidden]{display:none}.slide-selection{position:fixed;border:2px solid #b094ff;background:#a080ff18;pointer-events:none;z-index:20;box-shadow:0 0 0 1px #201632}.slide-edit-panel{position:fixed;z-index:21;width:min(380px,calc(100vw - 16px));padding:14px;background:#202438;border:1px solid #776591;border-radius:14px;box-shadow:0 16px 50px #0008;display:flex;flex-direction:column;gap:12px}.slide-edit-panel[hidden],.slide-selection[hidden]{display:none}.selection-heading,.selection-actions{display:flex;align-items:center;gap:8px}.selection-heading strong{flex:1}.slide-edit-panel textarea{width:100%;min-height:90px;max-height:35vh;resize:vertical;background:#141a2b;color:#f4efff;border:1px solid #514969;border-radius:8px;padding:10px;font:inherit}.selection-actions{flex-wrap:wrap}.selection-actions select{min-width:80px;flex:1}.slide-edit-panel small{color:#d1c9e3;line-height:1.5}.presenting .slide-edit-tools{display:none}\n\n#agent-workspace{position:relative;flex:1;min-height:0;min-width:0;display:flex;overflow:auto}.agent-pane{position:absolute;padding:3px 5px;display:flex;flex-direction:column;flex:1;min-width:0;min-height:0;overflow:hidden}.agent-pane iframe{border:0;width:100%;flex:1;min-height:0;border-radius:0 0 9px 9px}.agent-tools{display:flex;gap:4px;align-items:center;flex-wrap:wrap;padding:6px;background:#20253a;border:1px solid #34334e;border-radius:9px 9px 0 0}.agent-tools span{flex:1;font-size:10px;letter-spacing:.04em;color:#b3abc9}.agent-tools svg{width:14px;height:14px}.agent-pane:focus-within .agent-tools{border-color:#8b6ed577;background:#29243e}.agent-tools button{font-size:11px;padding:5px;background:transparent;border:0;display:grid;place-items:center}.agent-divider{position:absolute;z-index:2;background:#101422;cursor:col-resize;touch-action:none}.agent-divider.vertical{cursor:row-resize}.agent-divider:hover,.agent-divider:focus-visible{background:#9b87ff}.splitting-agents iframe,.resizing-chat iframe{pointer-events:none}\n</style>\n<header><span class=\"brand\"><svg viewBox=\"0 0 36 36\" aria-hidden=\"true\"><defs><linearGradient id=\"brand-gradient\" x2=\"1\" y2=\"1\"><stop stop-color=\"#c39aff\"/><stop offset=\"1\" stop-color=\"#7165f4\"/></linearGradient></defs><rect x=\"2\" y=\"2\" width=\"32\" height=\"32\" rx=\"10\" fill=\"url(#brand-gradient)\"/><path d=\"M11 10h9l6 6v10H11z\" fill=\"none\" stroke=\"white\" stroke-width=\"1.8\" stroke-linejoin=\"round\"/><path d=\"M20 10v7h6M15 21h7M15 25h4\" fill=\"none\" stroke=\"white\" stroke-width=\"1.8\" stroke-linecap=\"round\"/><path d=\"m28 3 1.2 3.8L33 8l-3.8 1.2L28 13l-1.2-3.8L23 8l3.8-1.2z\" fill=\"#e7dbff\"/></svg>Office <em>Kit</em></span><span class=\"badge\">Studio</span><span id=\"status\" role=\"status\">Building…</span><button id=\"toggle-chat\" aria-expanded=\"true\" aria-controls=\"chat\">✦ Agents</button><button id=\"present\" disabled>Present</button><a class=\"download\" href=\"/deck.pptx\">Download PPTX</a></header>\n<div class=\"workspace\">\n<nav class=\"filmstrip\" aria-label=\"Slides\"><h2>Slides</h2><ol id=\"thumbnails\"></ol></nav>\n<main aria-label=\"Slide viewer\"><pre id=\"error\" role=\"alert\" hidden></pre><div id=\"stage\" tabindex=\"-1\"><div id=\"empty\">Waiting for slides…</div><div id=\"slide\" hidden></div></div></main>\n<aside id=\"chat\" aria-label=\"Slide chat\">\n<div id=\"chat-resizer\" role=\"separator\" tabindex=\"0\" aria-label=\"Chat width\" aria-orientation=\"vertical\" aria-controls=\"chat\" title=\"Drag to resize · Double-click to reset\"></div>\n<div class=\"workspace-heading\"><span>✦ AI WORKSPACE</span><b>LOCAL</b></div><div id=\"agent-workspace\"></div><div id=\"chat-context\" hidden></div></aside>\n</div>\n<footer><span id=\"count\" aria-live=\"polite\">No slides</span><span class=\"hint\">Select an area to ask AI · Edit text directly</span><button id=\"prev\" aria-label=\"Previous slide\" disabled>‹</button><button id=\"next\" aria-label=\"Next slide\" disabled>›</button><label for=\"zoom\">Zoom</label><select id=\"zoom\"><option value=\"fit\">Fit</option><option value=\"0.5\">50%</option><option value=\"0.75\">75%</option><option value=\"1\">100%</option><option value=\"1.25\">125%</option><option value=\"1.5\">150%</option><option value=\"2\">200%</option></select></footer>\n<div id=\"presentation-controls\"><button id=\"present-prev\" aria-label=\"Previous slide\">‹</button><span id=\"present-count\"></span><button id=\"present-next\" aria-label=\"Next slide\">›</button><button id=\"exit-present\">Exit · Esc</button></div>\n<script>\nlet state={slides:[],error:null,aspectRatio:16/9},index=0,urls=[],presenting=false;\nlet displayedSvg;\nconst byId=id=>document.getElementById(id);\nconst stage=byId('stage'),slide=byId('slide'),thumbnails=byId('thumbnails');\nconst workspace=document.querySelector('.workspace'),chatResizer=byId('chat-resizer');\nconst chatWidthKey='office-kit-chat-width',minChatWidth=280,minStageWidth=240;\nfunction chatWidthLimits(){return {min:minChatWidth,max:Math.max(minChatWidth,workspace.clientWidth-document.querySelector('.filmstrip').getBoundingClientRect().width-minStageWidth)};}\nfunction updateChatWidthAria(){\n const {min,max}=chatWidthLimits();\n chatResizer.setAttribute('aria-valuemin',String(min));chatResizer.setAttribute('aria-valuemax',String(Math.round(max)));\n const width=Math.round(byId('chat').getBoundingClientRect().width);\n chatResizer.setAttribute('aria-valuenow',String(width));chatResizer.setAttribute('aria-valuetext',width+' pixels');\n}\nfunction setChatWidth(width){const {min,max}=chatWidthLimits();workspace.style.setProperty('--chat-width',Math.min(max,Math.max(min,width))+'px');}\nfunction saveChatWidth(){try{const width=workspace.style.getPropertyValue('--chat-width');if(width)localStorage.setItem(chatWidthKey,String(parseFloat(width)));else localStorage.removeItem(chatWidthKey);}catch(error){console.warn('Could not save chat width',error);}}\ntry{const saved=Number(localStorage.getItem(chatWidthKey));if(Number.isFinite(saved)&&saved>=minChatWidth)workspace.style.setProperty('--chat-width',saved+'px');}catch(error){console.warn('Could not restore chat width',error);}\nlet chatDrag=null;\nchatResizer.onpointerdown=event=>{\n if(event.button!==0||!event.isPrimary)return;\n event.preventDefault();chatResizer.focus();chatDrag={id:event.pointerId,x:event.clientX,width:byId('chat').getBoundingClientRect().width};\n chatResizer.setPointerCapture(event.pointerId);document.body.classList.add('resizing-chat');\n};\nchatResizer.onpointermove=event=>{if(chatDrag?.id===event.pointerId)setChatWidth(chatDrag.width+chatDrag.x-event.clientX);};\nfunction finishChatResize(){if(!chatDrag)return;chatDrag=null;document.body.classList.remove('resizing-chat');saveChatWidth();}\nchatResizer.onpointerup=finishChatResize;chatResizer.onpointercancel=finishChatResize;chatResizer.onlostpointercapture=finishChatResize;\nchatResizer.ondblclick=()=>{workspace.style.removeProperty('--chat-width');saveChatWidth();};\nchatResizer.onkeydown=event=>{\n const {min,max}=chatWidthLimits(),width=byId('chat').getBoundingClientRect().width,step=event.shiftKey?50:10;\n let next;\n if(event.key==='ArrowLeft')next=width+step;else if(event.key==='ArrowRight')next=width-step;else if(event.key==='Home')next=min;else if(event.key==='End')next=max;else return;\n event.preventDefault();event.stopPropagation();setChatWidth(next);saveChatWidth();\n};\nnew ResizeObserver(updateChatWidthAria).observe(byId('chat'));\n\n// The slide lives in a shadow root rather than a sandboxed iframe so its text\n// (XHTML inside <foreignObject>) can be selected and copied while the deck's\n// SVG stays out of the viewer's own DOM and CSS. Keyboard events still reach the\n// document, so arrow-key navigation keeps working after clicking into a slide.\nconst canvas=slide.attachShadow({mode:'open'});\nfunction resize(){\n if(!state.slides.length)return;\n const style=getComputedStyle(stage);\n const width=Math.max(1,stage.clientWidth-parseFloat(style.paddingLeft)-parseFloat(style.paddingRight));\n const height=Math.max(1,stage.clientHeight-parseFloat(style.paddingTop)-parseFloat(style.paddingBottom));\n const ratio=state.aspectRatio;\n const zoom=byId('zoom').value;\n const slideWidth=presenting||zoom==='fit'?Math.min(width,height*ratio):1280*Number(zoom);\n slide.style.width=slideWidth+'px';slide.style.height=slideWidth/ratio+'px';\n}\nfunction selectSlide(next,focusThumbnail=false,reveal=true){\n index=Math.max(0,Math.min(next,state.slides.length-1));\n const count=state.slides.length?'Slide '+(index+1)+' of '+state.slides.length:'No slides';\n byId('chat-context').textContent=state.slides.length?count:'Whole project';\n byId('chat-context').dataset.focus=JSON.stringify({slide:state.slides.length?index:null,revision:state.revision??0});\n window.dispatchEvent(new Event('agent-focus'));\n byId('count').textContent=count;byId('present-count').textContent=count;\n for(const id of ['prev','present-prev'])byId(id).disabled=index===0;\n for(const id of ['next','present-next'])byId(id).disabled=index>=state.slides.length-1;\n byId('present').disabled=!state.slides.length;\n byId('zoom').disabled=!state.slides.length;\n slide.hidden=!state.slides.length;byId('empty').hidden=!!state.slides.length;\n const svg=state.slides[index];\n if(svg!==displayedSvg){\n displayedSvg=svg;\n canvas.innerHTML=svg?'<style>svg{display:block;width:100%;height:100%}</style>'+svg:'';\n }\n slide.setAttribute('aria-label','Slide '+(index+1));\n for(const [position,item] of Array.from(thumbnails.children).entries()){\n const button=item.firstElementChild,selected=position===index;\n button.setAttribute('aria-current',String(selected));button.tabIndex=selected?0:-1;\n if(selected){if(reveal)button.scrollIntoView({block:'nearest'});if(focusThumbnail)button.focus({preventScroll:true});}\n }\n resize();\n}\nfunction update(updated){\n const focusedThumbnail=thumbnails.contains(document.activeElement);\n const previous=state;\n state=updated;\n byId('status').textContent=state.error?'Build failed · showing last successful output':state.building?'Updating…':state.slides.length+' slides · Live';\n byId('error').textContent=state.error||'';byId('error').hidden=!state.error;\n document.documentElement.style.setProperty('--slide-ratio',String(state.aspectRatio));\n for(let i=state.slides.length;i<urls.length;i++){\n URL.revokeObjectURL(urls[i]);thumbnails.lastElementChild.remove();\n }\n urls.length=state.slides.length;\n state.slides.forEach((svg,i)=>{\n if(svg===previous.slides[i])return;\n const oldUrl=urls[i];\n urls[i]=URL.createObjectURL(new Blob([svg],{type:'image/svg+xml'}));\n let item=thumbnails.children[i];\n if(!item){\n item=document.createElement('li');\n const button=document.createElement('button'),number=document.createElement('span'),image=document.createElement('img');\n button.className='thumbnail';button.setAttribute('aria-label','Slide '+(i+1));button.onclick=()=>selectSlide(i,true);\n number.className='slide-number';number.textContent=String(i+1);\n image.alt='';image.draggable=false;image.loading='lazy';\n button.append(number,image);item.append(button);thumbnails.append(item);\n }\n item.querySelector('img').src=urls[i];\n if(oldUrl)URL.revokeObjectURL(oldUrl);\n });\n selectSlide(index,focusedThumbnail,false);\n if(!state.slides.length&&presenting)void exitPresentation();\n}\nfunction setPresenting(value){\n presenting=value;document.body.classList.toggle('presenting',value);resize();\n if(value)stage.focus();else{\n byId('present').focus();\n thumbnails.children[index]?.firstElementChild.scrollIntoView({block:'nearest'});\n }\n}\nasync function exitPresentation(){\n setPresenting(false);\n if(document.fullscreenElement)await document.exitFullscreen();\n}\nbyId('present').onclick=async()=>{\n setPresenting(true);\n try{await document.documentElement.requestFullscreen();}\n catch{byId('exit-present').textContent='Exit view · Esc';}\n};\nbyId('exit-present').onclick=exitPresentation;\ndocument.addEventListener('fullscreenchange',()=>{if(!document.fullscreenElement&&presenting)setPresenting(false);});\nfor(const id of ['prev','present-prev'])byId(id).onclick=()=>selectSlide(index-1);\nfor(const id of ['next','present-next'])byId(id).onclick=()=>selectSlide(index+1);\nbyId('zoom').onchange=resize;\nstage.onclick=()=>{if(presenting&&!getSelection().toString())selectSlide(index+1);};\ndocument.addEventListener('keydown',event=>{\n if(event.key==='Escape'&&presenting){event.preventDefault();void exitPresentation();return;}\n if(event.altKey||event.ctrlKey||event.metaKey||event.target.closest('select,input,textarea,[contenteditable]'))return;\n let next=index;\n if(['ArrowLeft','ArrowUp','PageUp'].includes(event.key))next--;\n else if(['ArrowRight','ArrowDown','PageDown'].includes(event.key))next++;\n else if(event.key==='Home')next=0;\n else if(event.key==='End')next=state.slides.length-1;\n else if(event.key===' '&&presenting&&!event.target.closest('button,a'))next+=event.shiftKey?-1:1;\n else return;\n event.preventDefault();selectSlide(next,thumbnails.contains(document.activeElement));\n});\nnew ResizeObserver(resize).observe(stage);\nlet refreshId=0;\nasync function refresh(){\n const id=++refreshId;\n try{const response=await fetch('/state'+(state.revision===undefined?'':'?since='+state.revision));if(!response.ok)throw new Error('Preview unavailable');const updated=await response.json();if(id===refreshId){if(updated.changes){updated.slides=state.slides.slice(0,updated.count);updated.slides.length=updated.count;for(const [position,svg] of Object.entries(updated.changes))updated.slides[Number(position)]=svg;}update(updated);}}\n catch{if(id===refreshId)byId('status').textContent='Reconnecting…';}\n}\nbyId('toggle-chat').onclick=()=>{const hidden=document.body.classList.toggle('chat-hidden');byId('toggle-chat').setAttribute('aria-expanded',String(!hidden));resize();};\nconst events=new EventSource('/events');events.onmessage=event=>{if(event.data==='history'){window.dispatchEvent(new Event('agent-history'));return;}if(event.data==='chat'){window.dispatchEvent(new Event('agent-chat'));return;}if(event.data==='ready'){delete state.revision;}void refresh();};events.onerror=()=>{byId('status').textContent='Reconnecting…'};refresh();\n</script><script type=\"module\" src=\"/terminal.js\"></script></html>`;\n","import { previewStyles } from './styles.ts';\nexport const agentPage = `<!doctype html><html lang=\"en\"><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><link rel=\"stylesheet\" href=\"/terminal.css\"><style>${previewStyles}\nbody{display:flex;flex-direction:column;background:#191e30;height:100dvh;min-width:0;overflow:auto;overflow-x:hidden} .chat-heading{flex-wrap:wrap} .chat-heading select{min-width:100px}.chat-heading,#chat-context{flex-shrink:0}#claude-panel{min-height:100px}#codex-panel{min-height:240px}\n</style><div class=\"chat-heading\"><select id=\"chat-provider\" aria-label=\"AI provider\"><option value=\"claude\">Claude Code</option><option value=\"codex\">Codex</option></select><button id=\"chat-reset\" hidden>New chat</button></div>\n<div id=\"chat-context\">No slide selected</div>\n<section id=\"claude-panel\" aria-label=\"Claude Code terminal\"><div class=\"terminal-toolbar\"><span id=\"terminal-status\" role=\"status\">Start Claude Code to edit your slides.</span><button id=\"terminal-start\">Start</button><button id=\"terminal-stop\" hidden>End session</button></div><div class=\"terminal-frame\"><div id=\"terminal\"></div></div></section>\n<div id=\"codex-panel\" hidden>\n<div id=\"messages\" role=\"log\" aria-label=\"Conversation\"><p class=\"chat-intro\">Describe what you want to change. The current slide is included automatically. You can also ask about other slides or the whole deck.<br><br>Uses your locally installed Codex. Sign in through its CLI first. Sending a message allows it to edit this project.</p></div>\n<form id=\"chat-form\"><textarea id=\"chat-input\" aria-label=\"Message\" maxlength=\"16000\" placeholder=\"Ask for a change…\" required></textarea><div class=\"chat-actions\"><span class=\"chat-shortcut\">⌘ / Ctrl + Enter</span><button id=\"chat-stop\" type=\"button\" hidden>Stop</button><button id=\"chat-send\" type=\"submit\">Send</button></div></form>\n<div id=\"chat-status\" role=\"status\">Ready</div>\n</div>\n<script>\nconst byId=id=>document.getElementById(id),base=location.pathname;\nlet focus={slide:null,revision:0};\nwindow.addEventListener('message',event=>{\n if(event.origin!==location.origin||event.source!==parent)return;\n if(event.data?.type==='focus'){focus=event.data.focus;byId('chat-context').dataset.focus=JSON.stringify(focus);byId('chat-context').textContent=event.data.label;}\n if(event.data?.type==='chat')void refreshChat();\n if(event.data?.type==='inline-edit'&&byId('chat-provider').value==='codex'){\n void (async()=>{try{if(chatBusy)throw new Error('This agent is working. Wait for it to finish.');await chatAction('/chat',{message:event.data.message,provider:'codex',...event.data.focus});parent.postMessage({type:'inline-result',id:event.data.id},location.origin);}catch(error){parent.postMessage({type:'inline-result',id:event.data.id,error:error.message},location.origin);}})();\n }\n\n});\nparent.postMessage({type:'agent-ready'},location.origin);\nlet chatRequest=0,chatBusy=false,lastMessages='';\nfunction renderChat(chat){\n chatBusy=chat.running;\n byId('chat-send').disabled=chat.running;\n byId('chat-reset').disabled=chat.running;\n if(byId('chat-provider').value==='codex')byId('chat-provider').disabled=chat.running;\n byId('chat-stop').hidden=!chat.running;\n byId('chat-status').textContent=chat.status;\n\n const serialized=JSON.stringify(chat.messages);\n if(serialized===lastMessages)return;\n lastMessages=serialized;\n const log=byId('messages'),atBottom=log.scrollHeight-log.scrollTop-log.clientHeight<60;\n log.replaceChildren();\n if(!chat.messages.length){const intro=document.createElement('p');intro.className='chat-intro';intro.textContent='Ask for a focused slide edit or a change across the deck. Uses your locally installed CLI and its login. Sending allows edits to this project.';log.append(intro);}\n for(const message of chat.messages){\n const item=document.createElement('div'),label=document.createElement('small'),body=document.createElement('div');\n item.className='chat-message '+message.role;\n label.textContent=message.role==='user'?'You · '+(message.context?.slide?'Slide '+message.context.slide:'Whole project'):'Assistant';\n if(message.role==='assistant'&&message.html){body.className='markdown';body.innerHTML=message.html;}else body.textContent=message.text||(chat.running?'Working…':'No response');item.append(label,body);log.append(item);\n }\n if(atBottom)log.scrollTop=log.scrollHeight;\n}\nasync function refreshChat(){\n const id=++chatRequest;\n try{const response=await fetch(base+'/chat');if(!response.ok)throw new Error('Chat unavailable');const chat=await response.json();if(id===chatRequest)renderChat(chat);}\n catch(error){if(id===chatRequest)byId('chat-status').textContent=error.message;}\n}\nasync function chatAction(path,payload){\n ++chatRequest;\n const response=await fetch(base+path,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});\n const result=await response.json();if(!response.ok)throw new Error(result.error||'Chat request failed');\n await refreshChat();\n}\nbyId('chat-form').onsubmit=async event=>{\n event.preventDefault();if(chatBusy)return;\n const input=byId('chat-input'),message=input.value.trim();if(!message)return;\n chatBusy=true;byId('chat-send').disabled=true;\n try{await chatAction('/chat',{message,provider:byId('chat-provider').value,...focus});if(input.value.trim()===message)input.value='';}\n catch(error){byId('chat-status').textContent=error.message;chatBusy=false;byId('chat-send').disabled=false;}\n};\nbyId('chat-input').onkeydown=event=>{if(event.key==='Enter'&&(event.metaKey||event.ctrlKey)&&!event.isComposing){event.preventDefault();byId('chat-form').requestSubmit();}};\nfor(const action of ['stop','reset'])byId('chat-'+action).onclick=()=>chatAction('/chat/'+action,{}).catch(error=>{byId('chat-status').textContent=error.message;});\nbyId('chat-provider').onchange=()=>{try{sessionStorage.setItem('office-kit-provider'+base,byId('chat-provider').value);}catch{}const claude=byId('chat-provider').value==='claude';byId('claude-panel').hidden=!claude;byId('codex-panel').hidden=claude;byId('chat-reset').hidden=claude;};\ntry{byId('chat-provider').value=sessionStorage.getItem('office-kit-provider'+base)||'claude';}catch{}\nbyId('chat-provider').onchange();\nvoid refreshChat();\n</script><script type=\"module\" src=\"/terminal.js\"></script></html>`;\n","/** Shared guidance for agents editing a deck through the preview. */\nexport const authoringGuidance = `For lists and tables of contents, use Text paragraphs/bullets or mapped Text rows. Bullets and TableOfContents are not built-in DSL components: inspect installed exports, import existing local helpers or define them. Do not use Raw for these layouts.\nChoose foreground and actual background colors together. Explicitly set readable text colors on slides, cards, tables, chart labels and footnotes; check inherited theme styles. Aim for 4.5:1 contrast for normal text and 3:1 for large text (18pt or 14pt bold). Use a solid backing over images or gradients if needed. Inspect rendered slides; compilation alone cannot verify contrast.`;\n","import type { VisualReview } from './visual-review.ts';\nimport { authoringGuidance } from './authoring-guidance.ts';\nimport { randomBytes } from 'node:crypto';\nimport { dirname, resolve, join } from 'node:path';\nimport { createRequire } from 'node:module';\nimport { stat, chmod } from 'node:fs/promises';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { IPty } from 'node-pty';\nimport type { FocusContext } from './chat.ts';\n\nconst OUTPUT_LIMIT = 2_000_000;\nconst REQUEST_LIMIT = 128_000;\n\n/** A real interactive Claude session, shared across reloads of its owning tab. */\nexport function createTerminal(\n entry: string,\n busy = () => false,\n base = '',\n verify: () => Promise<string | null> = async () => null,\n review?: { begin(): void; next(): Promise<VisualReview | undefined> },\n history?: { begin(): Promise<void>; end(): Promise<void> },\n) {\n let repairs = 0;\n let working = false;\n let process: IPty | undefined;\n let owner = '';\n let output = '';\n let context: FocusContext | undefined;\n let status = 'Start Claude Code to edit your slides.';\n let completion = Promise.resolve();\n let closing = false;\n let stopping = false;\n let starting = false;\n const token = randomBytes(32).toString('hex');\n const clients = new Set<ServerResponse>();\n const snapshot = () => ({ running: !!process, owner, status });\n const emit = (event: string, value: unknown) => {\n for (const client of clients) {\n if (!client.write(`event: ${event}\\ndata: ${JSON.stringify(value)}\\n\\n`)) {\n clients.delete(client);\n client.end();\n }\n }\n };\n async function stop() {\n if (!process || stopping) return completion;\n stopping = true;\n status = 'Stopping…';\n emit('state', snapshot());\n const running = process;\n running.kill();\n const timer = setTimeout(() => {\n if (process === running) running.kill('SIGKILL');\n }, 2000);\n await completion;\n clearTimeout(timer);\n }\n async function handle(\n request: IncomingMessage,\n response: ServerResponse,\n focus: (index: number | null, revision: number) => FocusContext,\n ) {\n const json = (code: number, value: unknown) => {\n response.writeHead(code, { 'Content-Type': 'application/json' });\n response.end(JSON.stringify(value));\n };\n if (request.url === '/terminal/context' || request.url === '/terminal/verify') {\n if (request.method !== 'POST' || request.headers.authorization !== `Bearer ${token}`) {\n json(403, { error: 'Invalid hook credentials' });\n return;\n }\n request.resume();\n if (request.url === '/terminal/verify') {\n if (closing || stopping || !process) {\n json(200, {});\n return;\n }\n let error: string | null;\n try {\n error = await verify();\n } catch (cause) {\n error = cause instanceof Error ? cause.message : String(cause);\n }\n if (closing || stopping || !process) {\n json(200, {});\n return;\n }\n if (error && repairs < 3) {\n repairs++;\n status = `Repairing preview (${repairs}/3)…`;\n json(200, {\n decision: 'block',\n reason: `The actual preview build failed. Repair the source before finishing (attempt ${repairs}/3). Preserve unrelated edits, inspect the installed DSL exports and local components before changing imports. Do not hide the error or remove requested content. No Raw is needed for lists or an agenda: use Text paragraphs/bullets or mapped Text rows. Build error:\\n${error.slice(0, 16000)}`,\n });\n } else {\n if (!error && review) {\n try {\n const visual = await review.next();\n if (closing || stopping || !process) {\n json(200, {});\n return;\n }\n if (visual) {\n status = 'Reviewing slide screenshots…';\n json(200, { decision: 'block', reason: visual.prompt });\n emit('state', snapshot());\n return;\n }\n } catch (cause) {\n error = cause instanceof Error ? cause.message : String(cause);\n }\n }\n try {\n await history?.end();\n } catch (cause) {\n error = 'History: ' + String(cause);\n }\n working = false;\n status = error ? 'Preview verification incomplete: ' + error : 'Preview build verified';\n json(200, {});\n }\n emit('state', snapshot());\n return;\n }\n try {\n await history?.begin();\n } catch (cause) {\n json(409, { error: String(cause) });\n return;\n }\n repairs = 0;\n working = true;\n review?.begin();\n json(200, {\n hookSpecificOutput: {\n hookEventName: 'UserPromptSubmit',\n additionalContext: `You are editing the local PowerPoint TSX project at ${resolve(entry)}.\nRead the project's authoring instructions. Make focused source patches and preserve unrelated work.\nThe preview rebuilds on save; do not start another dev server. A Stop hook checks the actual build and returns errors for repair.\n${authoringGuidance}\nThe focused slide is context, NOT a restriction. For \"this slide\", edit only its relevant source. For other slides or deck-wide requests, locate the relevant source or shared theme.\nSlide numbers are 1-based. Verify source before editing; dependency paths are candidates, not an exact slide-to-file mapping.\nPreview context captured with the latest terminal input: ${JSON.stringify(context)}`,\n },\n });\n return;\n }\n const origin = `http://${request.headers.host}`;\n if (\n request.headers['sec-fetch-site'] === 'cross-site' ||\n (request.headers.origin && request.headers.origin !== origin)\n ) {\n json(403, { error: 'Same-origin requests only' });\n return;\n }\n if (request.method === 'GET' && request.url === '/terminal/events') {\n response.writeHead(200, { 'Content-Type': 'text/event-stream' });\n response.write(`event: replay\\ndata: ${JSON.stringify(output)}\\n\\n`);\n response.write(`event: state\\ndata: ${JSON.stringify(snapshot())}\\n\\n`);\n clients.add(response);\n request.on('close', () => clients.delete(response));\n return;\n }\n if (\n request.method !== 'POST' ||\n request.headers.origin !== origin ||\n request.headers['content-type'] !== 'application/json'\n ) {\n json(403, { error: 'Same-origin JSON requests only' });\n return;\n }\n try {\n let body = '';\n request.setEncoding('utf8');\n for await (const chunk of request) {\n body += chunk;\n if (Buffer.byteLength(body) > REQUEST_LIMIT) throw new Error('Request too large');\n }\n const value = JSON.parse(body);\n if (!value || typeof value.client !== 'string' || !/^[\\w-]{1,64}$/.test(value.client))\n throw new Error('Invalid terminal client');\n if (closing || starting || stopping || (process && owner !== value.client)) {\n json(409, {\n error: stopping\n ? 'Claude Code is stopping'\n : 'Claude Code is open in another tab. Use that tab to control it.',\n });\n return;\n }\n const size = () => {\n if (\n !Number.isInteger(value.cols) ||\n value.cols < 2 ||\n value.cols > 500 ||\n !Number.isInteger(value.rows) ||\n value.rows < 1 ||\n value.rows > 300\n )\n throw new Error('Invalid terminal dimensions');\n };\n const updateFocus = () => {\n if (\n !(value.slide === null || (Number.isInteger(value.slide) && value.slide >= 0)) ||\n !Number.isInteger(value.revision)\n )\n throw new Error('Invalid preview context');\n context = focus(value.slide, value.revision);\n };\n if (request.url === '/terminal/start') {\n if (busy()) throw new Error('A Codex edit is already running');\n size();\n updateFocus();\n if (!process) {\n starting = true;\n try {\n // Lazy loading leaves preview/export usable if a native PTY cannot load.\n // node-pty 1.1.0 ships its macOS spawn helper without executable bits.\n // Restore the packaged helper's mode before its first launch.\n if (globalThis.process.platform === 'darwin') {\n const root = dirname(createRequire(import.meta.url).resolve('node-pty/package.json'));\n const helper = join(\n root,\n 'prebuilds',\n `darwin-${globalThis.process.arch}`,\n 'spawn-helper',\n );\n const info = await stat(helper).catch((error: NodeJS.ErrnoException) => {\n if (error.code === 'ENOENT') return undefined;\n throw error;\n });\n if (info && !(info.mode & 0o100)) await chmod(helper, info.mode | 0o100);\n }\n const pty = await import('node-pty');\n if (process || closing) throw new Error('Terminal state changed. Try again.');\n const settings = {\n hooks: {\n Stop: [\n {\n hooks: [\n {\n type: 'http',\n url: `${origin}${base}/terminal/verify`,\n headers: { Authorization: `Bearer ${token}` },\n timeout: 120,\n },\n ],\n },\n ],\n UserPromptSubmit: [\n {\n hooks: [\n {\n type: 'http',\n url: `${origin}${base}/terminal/context`,\n headers: { Authorization: `Bearer ${token}` },\n timeout: 5,\n },\n ],\n },\n ],\n },\n };\n const env = { ...globalThis.process.env };\n // This is an independent interactive session, even if the preview was started by Claude.\n delete env.CLAUDECODE;\n process = pty.spawn('claude', ['--settings', JSON.stringify(settings)], {\n cwd: dirname(resolve(entry)),\n name: 'xterm-256color',\n env,\n cols: value.cols,\n rows: value.rows,\n });\n } finally {\n starting = false;\n }\n owner = value.client;\n output = '';\n status = 'Claude Code';\n emit('replay', '');\n process.onData((data) => {\n output = (output + data).slice(-OUTPUT_LIMIT);\n emit('output', data);\n });\n completion = new Promise<void>((done) =>\n process!.onExit(async ({ exitCode }) => {\n process = undefined;\n working = false;\n stopping = false;\n status = `Claude Code exited (${exitCode}). Start to open a new session.`;\n try {\n await history?.end();\n } catch (cause) {\n status += ' History: ' + String(cause);\n }\n emit('state', snapshot());\n done();\n }),\n );\n }\n } else if (request.url === '/terminal/prompt') {\n if (!process) throw new Error('Start Claude Code in the selected agent pane first.');\n if (working)\n throw new Error('Claude Code is working. Wait for the current turn to finish.');\n if (\n typeof value.message !== 'string' ||\n !value.message.trim() ||\n value.message.length > 16000 ||\n [...value.message].some((character: string) => {\n const code = character.charCodeAt(0);\n return (code < 32 && code !== 9 && code !== 10) || code === 127;\n })\n )\n throw new Error('Invalid inline prompt');\n updateFocus();\n working = true;\n try {\n await history?.begin();\n } catch (cause) {\n working = false;\n throw cause;\n }\n if (!process || closing || stopping) {\n await history?.end();\n working = false;\n throw new Error('Claude Code stopped before submitting.');\n }\n process.write('\\x1b[200~' + value.message + '\\x1b[201~');\n await new Promise((done) => setTimeout(done, 100));\n if (!process || stopping) throw new Error('Claude Code stopped before submitting.');\n process.write('\\r');\n } else if (request.url === '/terminal/input') {\n if (!process) throw new Error('Start Claude Code first');\n if (typeof value.data !== 'string' || value.data.length > 32000)\n throw new Error('Invalid terminal input');\n updateFocus();\n process.write(value.data);\n if (value.data.includes('\\x03')) {\n working = false;\n await history?.end();\n }\n } else if (request.url === '/terminal/resize') {\n size();\n process?.resize(value.cols, value.rows);\n } else if (request.url === '/terminal/stop') {\n await stop();\n } else {\n json(404, { error: 'Not found' });\n return;\n }\n emit('state', snapshot());\n json(200, snapshot());\n } catch (cause) {\n json(400, { error: cause instanceof Error ? cause.message : 'Invalid terminal request' });\n }\n }\n return {\n handle,\n isRunning: () => starting || !!process,\n async close() {\n closing = true;\n await stop();\n for (const client of clients) client.end();\n clients.clear();\n },\n };\n}\n","import type { VisualReview } from './visual-review.ts';\nimport { authoringGuidance } from './authoring-guidance.ts';\nimport MarkdownIt from 'markdown-it';\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { dirname, resolve } from 'node:path';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nexport type Provider = 'codex';\nconst markdown = new MarkdownIt({ html: false, linkify: false });\n// Do not fetch remote images from model output.\nmarkdown.renderer.rules.image = (tokens, index) =>\n markdown.utils.escapeHtml(tokens[index]?.content ?? '');\nexport interface FocusContext {\n slide: number | null;\n count: number;\n revision: number;\n text: string;\n entry: string;\n files: string[];\n buildError: string | null;\n}\ninterface Message {\n role: 'user' | 'assistant';\n text: string;\n context?: FocusContext;\n}\n\n/** One shared conversation and one editing process per dev server. */\nexport function createChat(\n entry: string,\n notify: () => void,\n busy = () => false,\n verify: () => Promise<string | null> = async () => null,\n review?: { begin(): void; next(): Promise<VisualReview | undefined> },\n reviewImages: (prompt: string) => string[] = () => [],\n history?: { begin(): Promise<void>; end(): Promise<void> },\n) {\n let starting = false;\n let closing = false;\n let messages: Message[] = [];\n let provider: Provider = 'codex';\n let child: ChildProcess | undefined;\n let status = 'Ready';\n let stopping = false;\n let killTimer: ReturnType<typeof setTimeout> | undefined;\n let completion: Promise<void> = Promise.resolve();\n const snapshot = () => ({\n messages: messages.map((message) => ({\n ...message,\n ...(message.role === 'assistant' ? { html: markdown.render(message.text) } : {}),\n })),\n provider,\n running: starting || !!child,\n status,\n });\n function stop() {\n if (!child || stopping) return;\n stopping = true;\n status = 'Stopping…';\n const processToStop = child;\n // During verification the child has exited, but the turn still owns the\n // conversation. Cancel repair without signalling a potentially reused PID.\n if (processToStop.exitCode !== null || processToStop.signalCode !== null) {\n notify();\n return;\n }\n const kill = (signal: NodeJS.Signals) => {\n try {\n if (process.platform !== 'win32' && processToStop.pid)\n process.kill(-processToStop.pid, signal);\n else processToStop.kill(signal);\n } catch {\n /* Already exited. */\n }\n };\n kill('SIGTERM');\n killTimer = setTimeout(() => kill('SIGKILL'), 2000);\n notify();\n }\n function start(\n text: string,\n selected: Provider,\n context: FocusContext,\n repairs = 0,\n images: string[] = [],\n reviewing = false,\n ) {\n if (!repairs && !reviewing) review?.begin();\n provider = selected;\n const user: Message = { role: 'user', text, context };\n // Keep recent conversational intent, including each turn's original focus.\n const conversation = messages.slice(-12).map((message) => ({\n ...message,\n text: message.text.slice(-12000),\n }));\n const prompt = `You are editing a local PowerPoint TSX project in ${dirname(resolve(entry))}.\nRead the project's authoring instructions. Make focused patches to the TSX source, preserving unrelated work.\nThe preview rebuilds automatically on save; do not start another dev server.\n${authoringGuidance}\nThe attached focus is context, NOT a restriction: use it for references such as \"this slide\". For other slides or deck-wide requests, find the relevant source or shared theme instead. Do not rewrite the whole deck for a local edit.\nSlide numbers are 1-based and refer to the last successful preview at send time. Source may have changed since then; verify it before editing. Files are dependency candidates, not an exact slide-to-source mapping.\nRespond in the user's language, briefly describing edits or answering their question. If permissions prevent an action, explain that instead of claiming success.\nRecent conversation (JSON): ${JSON.stringify(conversation)}\nCurrent request and preview context (JSON): ${JSON.stringify(user)}`;\n messages.push(user);\n const answer: Message = { role: 'assistant', text: '' };\n messages.push(answer);\n if (messages.length > 100) messages = messages.slice(-100);\n status = 'Working…';\n stopping = false;\n const args = ['exec', '--json', '--sandbox', 'workspace-write', '--skip-git-repo-check', '-'];\n for (const image of images) args.splice(args.length - 1, 0, '--image', image);\n child = spawn(selected, args, {\n cwd: dirname(resolve(entry)),\n stdio: ['pipe', 'pipe', 'pipe'],\n detached: process.platform !== 'win32',\n shell: false,\n });\n const running = child;\n let buffer = '',\n stderr = '',\n failure = '',\n sawResult = false;\n function append(value: string) {\n answer.text = (answer.text + (answer.text ? '\\n\\n' : '') + value).slice(-100000);\n notify();\n }\n function consume(line: string) {\n if (!line.trim()) return;\n try {\n const event = JSON.parse(line);\n if (event.type === 'item.completed' && event.item?.type === 'agent_message')\n append(event.item.text ?? '');\n if (event.type === 'item.started') {\n status = 'Editing / inspecting…';\n notify();\n }\n if (event.type === 'turn.completed') sawResult = true;\n if (event.type === 'turn.failed' || event.type === 'error')\n failure = event.error?.message ?? event.message ?? 'Agent failed';\n } catch {\n failure = 'Unexpected CLI output. Update your CLI and try again.';\n }\n }\n running.stdout!.setEncoding('utf8');\n running.stdout!.on('data', (chunk: string) => {\n buffer += chunk;\n let end: number;\n while ((end = buffer.indexOf('\\n')) !== -1) {\n consume(buffer.slice(0, end));\n buffer = buffer.slice(end + 1);\n }\n if (buffer.length > 2_000_000) {\n failure = 'CLI output exceeded the limit.';\n stop();\n }\n });\n running.stderr!.setEncoding('utf8');\n running.stderr!.on('data', (chunk: string) => {\n stderr = (stderr + chunk).slice(-8000);\n });\n running.stdin!.on('error', () => {\n /* Early process exit is handled below. */\n });\n running.stdin!.end(prompt);\n completion = new Promise<void>((done) => {\n running.on('error', (error) => {\n failure = `Could not run ${selected}. Install it and log in from your terminal first. ${error.message}`;\n });\n running.on('close', async (code) => {\n if (buffer.trim()) consume(buffer);\n clearTimeout(killTimer);\n status = stopping\n ? 'Stopped. Saved edits remain in the preview.'\n : failure ||\n (code !== 0\n ? stderr || `${selected} exited with code ${code}`\n : !sawResult\n ? 'CLI exited without a completed result.'\n : 'Done');\n if (status === 'Done') {\n status = 'Verifying preview…';\n notify();\n let buildError: string | null;\n try {\n buildError = await verify();\n } catch (cause) {\n buildError = cause instanceof Error ? cause.message : String(cause);\n }\n if (!stopping && buildError && repairs < 3) {\n child = undefined;\n start(\n `The actual preview build failed. Repair the source and preserve unrelated work (attempt ${repairs + 1}/3). Do not remove requested content or hide the error.\\n${buildError.slice(0, 16000)}`,\n selected,\n { ...context, buildError },\n repairs + 1,\n [],\n reviewing,\n );\n await completion;\n done();\n return;\n }\n let reviewFailure = '';\n if (!stopping && !buildError && review) {\n try {\n const visual = await review.next();\n if (!stopping && visual) {\n child = undefined;\n start(visual.prompt, selected, context, 0, visual.images, true);\n await completion;\n done();\n return;\n }\n } catch (error) {\n reviewFailure = error instanceof Error ? error.message : String(error);\n }\n }\n status = stopping\n ? 'Stopped. Saved edits remain in the preview.'\n : buildError\n ? 'Preview still has errors after 3 repair attempts.\\n' + buildError.slice(0, 16000)\n : reviewFailure || 'Done';\n }\n try {\n await history?.end();\n } catch (cause) {\n status = 'History: ' + String(cause);\n }\n if (status !== 'Done') append(status);\n child = undefined;\n notify();\n done();\n });\n });\n notify();\n }\n async function handle(\n request: IncomingMessage,\n response: ServerResponse,\n focus: (index: number | null, revision: number) => FocusContext,\n ) {\n const json = (code: number, value: unknown) => {\n response.writeHead(code, { 'Content-Type': 'application/json' });\n response.end(JSON.stringify(value));\n };\n if (request.method === 'GET' && request.url === '/chat') {\n json(200, snapshot());\n return;\n }\n if (request.method !== 'POST') {\n json(405, { error: 'Method not allowed' });\n return;\n }\n // Same-origin JSON prevents other sites from invoking local editing tools.\n if (\n request.headers.origin !== `http://${request.headers.host}` ||\n request.headers['content-type']?.split(';')[0] !== 'application/json'\n ) {\n json(403, { error: 'Same-origin JSON required' });\n return;\n }\n try {\n let body = '';\n request.setEncoding('utf8');\n for await (const chunk of request) {\n body += chunk;\n if (Buffer.byteLength(body) > 32000) {\n json(413, { error: 'Message too large' });\n return;\n }\n }\n const value = JSON.parse(body);\n if (request.url === '/chat/stop') {\n stop();\n json(200, snapshot());\n return;\n }\n if (child || starting || closing) {\n json(409, { error: 'An edit is already running' });\n return;\n }\n if (request.url === '/chat/reset') {\n messages = [];\n status = 'Ready';\n notify();\n json(200, snapshot());\n return;\n }\n if (request.url !== '/chat') {\n json(404, { error: 'Not found' });\n return;\n }\n if (\n typeof value.message !== 'string' ||\n !value.message.trim() ||\n value.message.length > 16000 ||\n value.provider !== 'codex' ||\n !(value.slide === null || (Number.isInteger(value.slide) && value.slide >= 0)) ||\n !Number.isInteger(value.revision)\n ) {\n json(400, { error: 'Invalid chat request' });\n return;\n }\n if (busy()) {\n json(409, { error: 'End the Claude Code session before starting a Codex edit' });\n return;\n }\n const context = focus(value.slide, value.revision);\n starting = true;\n try {\n await history?.begin();\n if (closing) {\n await history?.end();\n throw new Error('Agent is closing');\n }\n start(value.message.trim(), value.provider, context, 0, reviewImages(value.message.trim()));\n } finally {\n starting = false;\n }\n json(202, snapshot());\n } catch (cause) {\n json(400, { error: cause instanceof Error ? cause.message : 'Invalid request' });\n }\n }\n return {\n handle,\n isRunning: () => starting || !!child,\n async close() {\n closing = true;\n stop();\n await completion;\n },\n };\n}\n","import { createHistory, historyFile } from './history.ts';\nimport { createTextEditor } from './text-edit.ts';\nimport { createVisualReviewer } from './visual-review.ts';\nimport { createServer, type ServerResponse } from 'node:http';\nimport { watch } from 'node:fs';\nimport { dirname, resolve, sep } from 'node:path';\nimport type { BuildResult } from './build.ts';\nimport { createDeckBuilder } from './build-runner.ts';\nimport { page } from './page.ts';\nimport { agentPage } from './agent-page.ts';\nimport { readFile } from 'node:fs/promises';\nimport { createTerminal } from './terminal.ts';\nimport { createChat } from './chat.ts';\n\nexport async function serveDeck(entry: string, port = 4173) {\n let latest: BuildResult | undefined;\n let error: string | null = null;\n let building = false;\n let revision = 0;\n let generation = 0;\n let patch: Record<number, string> = {};\n let pending = false;\n let closed = false;\n const clients = new Set<ServerResponse>();\n const sessions = new Map<\n string,\n {\n chat: ReturnType<typeof createChat>;\n terminal: ReturnType<typeof createTerminal>;\n closing: boolean;\n }\n >();\n const history = await createHistory(dirname(resolve(entry)), () => {\n for (const client of clients) client.write('data: history\\n\\n');\n });\n let textEdits = 0;\n const capturedReviews = new Map<string, string[]>();\n function session(id: string) {\n let result = sessions.get(id);\n if (!result) {\n const review = createVisualReviewer(resolve(entry), () => latest?.slides ?? []);\n const turnHistory = {\n begin: () => history.begin('agent:' + id, 'AI edit'),\n end: () => history.end('agent:' + id),\n };\n const chat = createChat(\n entry,\n () => {\n for (const client of clients) client.write('data: chat\\n\\n');\n },\n () => terminal.isRunning(),\n verify,\n review,\n (prompt) => capturedReviews.get(prompt) ?? [],\n turnHistory,\n );\n const terminal = createTerminal(\n entry,\n () => chat.isRunning(),\n id ? '/agents/' + id : '',\n verify,\n review,\n turnHistory,\n );\n result = { chat, terminal, closing: false };\n sessions.set(id, result);\n }\n return result;\n }\n const textEditor = createTextEditor(\n resolve(entry),\n () => {\n if (!latest || building || pending || timer || error)\n throw new Error('Wait for a successful preview build.');\n return { ...latest, revision };\n },\n verify,\n );\n const server = createServer((request, response) => {\n const host = request.headers.host;\n if (host !== `127.0.0.1:${actualPort}` && host !== `localhost:${actualPort}`) {\n response.writeHead(403).end();\n return;\n }\n response.setHeader('Cache-Control', 'no-store');\n const match = request.url?.match(\n /^\\/agents\\/([\\w-]{1,64})(\\/(?:chat(?:\\/(?:stop|reset))?|terminal\\/(?:start|input|prompt|resize|stop|events|context|verify)|close))?$/,\n );\n if (request.url?.startsWith('/agents/') && !match) {\n response.writeHead(404).end();\n return;\n }\n if (request.url === '/history' && request.method === 'GET') {\n response\n .writeHead(200, { 'Content-Type': 'application/json' })\n .end(JSON.stringify(history.state()));\n return;\n }\n if (\n request.url === '/text-edit' ||\n request.url === '/history/undo' ||\n request.url === '/history/redo'\n ) {\n if (\n request.method !== 'POST' ||\n request.headers.origin !== `http://${host}` ||\n request.headers['content-type'] !== 'application/json'\n ) {\n response.writeHead(403).end();\n return;\n }\n void (async () => {\n let body = '';\n request.setEncoding('utf8');\n for await (const chunk of request) {\n body += chunk;\n if (Buffer.byteLength(body) > 100000) throw new Error('Request too large');\n }\n if (request.url !== '/text-edit') {\n await history.move(request.url === '/history/undo' ? 'undo' : 'redo');\n const buildError = await verify();\n response\n .writeHead(200, { 'Content-Type': 'application/json' })\n .end(JSON.stringify({ ...history.state(), buildError }));\n return;\n }\n const manualReview = createVisualReviewer(resolve(entry), () => latest?.slides ?? []);\n manualReview.begin();\n const editId = 'text:' + ++textEdits;\n await history.begin(editId, 'Text edit');\n try {\n await textEditor.edit(JSON.parse(body));\n } finally {\n await history.end(editId);\n }\n let review;\n let reviewError;\n try {\n review = await manualReview.next();\n if (review) {\n capturedReviews.set(review.prompt, review.images);\n if (capturedReviews.size > 64)\n capturedReviews.delete(capturedReviews.keys().next().value!);\n }\n } catch (cause) {\n reviewError = cause instanceof Error ? cause.message : String(cause);\n }\n response\n .writeHead(200, { 'Content-Type': 'application/json' })\n .end(JSON.stringify({ review, reviewError }));\n })().catch((cause) =>\n response\n .writeHead(400, { 'Content-Type': 'application/json' })\n .end(JSON.stringify({ error: cause instanceof Error ? cause.message : String(cause) })),\n );\n return;\n }\n const id = match?.[1] ?? '';\n if (match) {\n if (\n request.headers['sec-fetch-site'] === 'cross-site' ||\n (request.headers.origin && request.headers.origin !== `http://${host}`)\n ) {\n response.writeHead(403).end();\n return;\n }\n if (sessions.get(id)?.closing) {\n response.writeHead(409).end('{\"error\":\"Agent is closing\"}');\n return;\n }\n if (match[2] === '/close') {\n if (\n request.method !== 'POST' ||\n request.headers.origin !== `http://${host}` ||\n request.headers['content-type'] !== 'application/json'\n ) {\n response.writeHead(403).end();\n return;\n }\n request.resume();\n const current = sessions.get(id);\n void (async () => {\n if (current) {\n current.closing = true;\n await Promise.all([current.terminal.close(), current.chat.close()]);\n sessions.delete(id);\n }\n response.writeHead(200).end('{}');\n })().catch(() => response.writeHead(500).end());\n return;\n }\n if (!sessions.has(id) && sessions.size >= 8) {\n response.writeHead(429).end('Too many agent sessions. Close an existing pane.');\n return;\n }\n if (!match[2]) {\n if (request.method !== 'GET') {\n response.writeHead(405).end();\n return;\n }\n session(id);\n response.writeHead(200, { 'Content-Type': 'text/html' }).end(agentPage);\n return;\n }\n if (!sessions.has(id)) {\n response.writeHead(404).end();\n return;\n }\n request.url = match[2];\n }\n\n if (\n request.url === '/chat' ||\n request.url?.startsWith('/chat/') ||\n request.url?.startsWith('/terminal/')\n ) {\n const current = session(id);\n const handler = request.url.startsWith('/terminal/') ? current.terminal : current.chat;\n void handler.handle(request, response, (index, viewedRevision) => {\n if (viewedRevision !== revision)\n throw new Error('Preview changed. Review the current slide and send again.');\n if (index !== null && (!latest || index >= latest.slides.length))\n throw new Error('Slide no longer exists.');\n return {\n slide: index === null ? null : index + 1,\n count: latest?.slides.length ?? 0,\n revision,\n text: index === null ? '' : (latest?.slideTexts[index] ?? '').slice(0, 12000),\n entry: resolve(entry),\n files: latest?.dependencies ?? [resolve(entry)],\n buildError: error,\n };\n });\n } else if (\n request.method === 'GET' &&\n ['/terminal.js', '/terminal.css'].includes(request.url ?? '')\n ) {\n const asset = request.url === '/terminal.js' ? 'terminal-client.js' : 'terminal-client.css';\n void readFile(new URL(asset, import.meta.url)).then(\n (bytes) => {\n response.writeHead(200, {\n 'Content-Type': asset.endsWith('.css') ? 'text/css' : 'text/javascript',\n });\n response.end(bytes);\n },\n () => response.writeHead(500).end('Terminal assets unavailable. Rebuild pptx-dev.'),\n );\n } else if (request.method !== 'GET') {\n response.writeHead(405).end();\n } else if (request.url === '/events') {\n response.writeHead(200, { 'Content-Type': 'text/event-stream' });\n response.write('data: ready\\n\\n');\n clients.add(response);\n request.on('close', () => clients.delete(response));\n } else if (request.url === '/state' || request.url?.startsWith('/state?')) {\n const since = new URL(request.url, 'http://localhost').searchParams.get('since');\n const incremental =\n since !== null && (since === String(revision) || since === String(revision - 1));\n response.writeHead(200, { 'Content-Type': 'application/json' });\n response.end(\n JSON.stringify({\n revision,\n building,\n ...(incremental\n ? {\n changes: since === String(revision) ? {} : patch,\n count: latest?.slides.length ?? 0,\n }\n : { slides: latest?.slides ?? [] }),\n aspectRatio: latest?.aspectRatio ?? 16 / 9,\n error,\n diagnostics: latest?.diagnostics ?? [],\n }),\n );\n } else if (request.url === '/deck.pptx' && latest) {\n response.writeHead(200, {\n 'Content-Type': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n 'Content-Disposition': 'attachment; filename=\"deck.pptx\"',\n });\n response.end(latest.bytes);\n } else if (request.url === '/') {\n response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n response.end(page);\n } else {\n response.writeHead(404).end();\n }\n });\n async function verify() {\n // A fresh build also covers writes whose watcher notification has not arrived yet.\n void rebuild();\n const deadline = Date.now() + 30000;\n while (!closed && (building || timer || pending)) {\n if (Date.now() >= deadline)\n return 'Preview verification timed out. Inspect the build before claiming success.';\n await new Promise((done) => setTimeout(done, 50));\n }\n return closed ? 'Preview server closed.' : error;\n }\n async function rebuild() {\n if (closed) return;\n if (building) {\n pending = true;\n return;\n }\n building = true;\n const started = generation;\n for (const client of clients) client.write('data: building\\n\\n');\n try {\n const result = await builder.build();\n if (started === generation && !closed) {\n patch = {};\n result.slides.forEach((svg, index) => {\n if (svg !== latest?.slides[index]) patch[index] = svg;\n });\n latest = result;\n revision++;\n error = null;\n }\n } catch (cause) {\n if (started === generation)\n error = cause instanceof Error ? (cause.stack ?? cause.message) : String(cause);\n } finally {\n building = false;\n void history.capture().catch(() => {\n /* The history endpoint exposes capture failures. */\n });\n for (const client of clients) client.write('data: updated\\n\\n');\n if (pending && !timer) {\n pending = false;\n void rebuild();\n }\n }\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n const root = dirname(resolve(entry));\n const watcher = watch(root, { recursive: true }, (_, filename) => {\n if (\n !filename ||\n filename\n .split(sep)\n .some((part) => ['node_modules', '.git', 'dist', '.office-kit'].includes(part))\n )\n return;\n if (!historyFile(filename)) return;\n generation++;\n void builder.cancel();\n clearTimeout(timer);\n timer = setTimeout(() => {\n timer = undefined;\n pending = false;\n void rebuild();\n }, 30);\n });\n const builder = createDeckBuilder(entry);\n let actualPort = port;\n try {\n await new Promise<void>((resolveListen, reject) => {\n server.once('error', reject);\n server.listen(port, '127.0.0.1', () => {\n server.off('error', reject);\n resolveListen();\n });\n });\n } catch (cause) {\n watcher.close();\n await builder.close();\n throw cause;\n }\n const address = server.address();\n if (address && typeof address !== 'string') actualPort = address.port;\n await rebuild();\n return {\n url: `http://127.0.0.1:${actualPort}`,\n async close() {\n closed = true;\n clearTimeout(timer);\n watcher.close();\n await Promise.all(\n [...sessions.values()].flatMap(({ terminal, chat }) => [terminal.close(), chat.close()]),\n );\n await builder.close();\n for (const client of clients) client.end();\n await new Promise<void>((done, reject) =>\n server.close((cause) => (cause ? reject(cause) : done())),\n );\n },\n };\n}\n","#!/usr/bin/env node\nimport { resolve } from 'node:path';\nimport { exportDeck } from './index.ts';\nimport { serveDeck } from './server.ts';\nimport { initProject } from './init.ts';\nimport { inspectTemplate } from './inspect.ts';\n\nconst usage = `Usage: office-pptx init <new-directory>\n office-pptx dev <deck.tsx> [--port 4173]\n office-pptx build <deck.tsx> [--out deck.pptx]\n office-pptx inspect <template.pptx>`;\nconst [command, entry, ...args] = process.argv.slice(2);\ntry {\n if (command === '--help' || command === '-h') {\n console.log(usage);\n } else {\n if (!entry) throw new Error(usage);\n const option = command === 'build' ? '--out' : command === 'dev' ? '--port' : undefined;\n if (args.length && (args.length !== 2 || !option || args[0] !== option || !args[1])) {\n throw new Error(`Unexpected arguments: ${args.join(' ')}\\n${usage}`);\n }\n if (command === 'init') {\n const directory = await initProject(entry);\n console.log(\n `Created ${directory}\\nInside that directory, run npm install once.\\nStart development: npm run dev (or npx office-pptx dev deck.tsx).`,\n );\n } else if (command === 'inspect') {\n console.log(JSON.stringify(await inspectTemplate(entry), null, 2));\n } else if (command === 'build') {\n const output = resolve(args[1] ?? 'deck.pptx');\n const result = await exportDeck(entry, output);\n console.log(`Wrote ${output} (${result.slides.length} slides)`);\n } else if (command === 'dev') {\n const port = args[1] === undefined ? 4173 : Number(args[1]);\n if (!Number.isInteger(port) || port < 0 || port > 65535)\n throw new Error('Port must be an integer between 0 and 65535.');\n const server = await serveDeck(entry, port);\n console.log(`Preview: ${server.url}`);\n const stop = () => {\n void server.close().then(() => process.exit(0));\n };\n process.once('SIGINT', stop);\n process.once('SIGTERM', stop);\n } else {\n throw new Error(usage);\n }\n }\n} catch (cause) {\n console.error(cause instanceof Error ? cause.message : cause);\n process.exitCode = 1;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":[],"sources":["../src/history.ts","../src/text-edit.ts","../src/deck-diff.ts","../src/source-sync.ts","../src/visual-review.ts","../src/preview-i18n.ts","../src/transition-script.ts","../src/styles.ts","../src/page.ts","../src/presenter-page.ts","../src/agent-page.ts","../src/authoring-guidance.ts","../src/terminal.ts","../src/chat.ts","../src/server.ts","../src/cli.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport {\n chmod,\n lstat,\n mkdir,\n readFile,\n readdir,\n realpath,\n rename,\n rm,\n unlink,\n writeFile,\n} from 'node:fs/promises';\nimport { dirname, join, relative, sep } from 'node:path';\n\nconst SNAPSHOT_LIMIT = 64 * 1024 * 1024;\nconst HISTORY_LIMIT = 128 * 1024 * 1024;\nconst ENTRY_LIMIT = 50;\nconst ignored = new Set(['node_modules', '.git', 'dist', '.office-kit']);\nexport const historyFile = (name: string) =>\n !name.split(sep).some((part) => ignored.has(part)) &&\n /\\.([cm]?[jt]sx?|json|css|md|ya?ml|pptx|png|jpe?g|gif|bmp|tiff?|emf|wmf|svg)$/i.test(name);\ntype Snapshot = Map<string, Buffer>;\ntype Change = { file: string; before: Buffer | undefined; after: Buffer | undefined };\ntype Entry = { label: string; changes: Change[] };\nconst equal = (a?: Buffer, b?: Buffer) => a === b || (!!a && !!b && a.equals(b));\n\n/** Session-local project history. Overlapping agent turns form one transaction. */\nexport async function createHistory(directory: string, notify: () => void = () => {}) {\n const root = await realpath(directory);\n let baseline: Snapshot = new Map();\n let initialized = false;\n const past: Entry[] = [],\n future: Entry[] = [];\n const active = new Set<string>();\n let label = '';\n let error: string | null = null;\n let queue = Promise.resolve();\n let restoring = false;\n function serial<T>(action: () => Promise<T>): Promise<T> {\n const result = queue.then(action);\n queue = result.then(\n () => {},\n () => {},\n );\n return result\n .catch((cause) => {\n error = cause instanceof Error ? cause.message : String(cause);\n throw cause;\n })\n .finally(notify);\n }\n async function snapshot(): Promise<Snapshot> {\n const files = new Map<string, Buffer>();\n let bytes = 0;\n async function visit(dir: string): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true });\n await Promise.all(\n entries.map(async (item) => {\n if (ignored.has(item.name) || item.isSymbolicLink()) return;\n const file = join(dir, item.name);\n if (item.isDirectory()) return visit(file);\n if (!item.isFile() || !historyFile(file)) return;\n const info = await lstat(file);\n bytes += info.size;\n if (bytes > SNAPSHOT_LIMIT)\n throw new Error(\n 'Undo history exceeds 64 MiB of project source/assets. Move unused assets outside the deck folder.',\n );\n if (info.isSymbolicLink() || (await realpath(file)) !== file)\n throw new Error('Project paths changed while recording history. Try again.');\n const content = await readFile(file);\n files.set(relative(root, file), content);\n }),\n );\n }\n await visit(root);\n return files;\n }\n function record(next: Snapshot, description: string) {\n if (!initialized) {\n baseline = next;\n initialized = true;\n error = null;\n return;\n }\n const changes: Change[] = [];\n for (const file of new Set([...baseline.keys(), ...next.keys()])) {\n const before = baseline.get(file),\n after = next.get(file);\n if (!equal(before, after)) changes.push({ file, before, after });\n }\n if (changes.length) {\n past.push({ label: description, changes });\n future.length = 0;\n const size = () =>\n past.reduce(\n (sum, item) =>\n sum +\n item.changes.reduce(\n (n, change) => n + (change.before?.length ?? 0) + (change.after?.length ?? 0),\n 0,\n ),\n 0,\n );\n while (past.length > ENTRY_LIMIT || (past.length > 1 && size() > HISTORY_LIMIT)) past.shift();\n }\n baseline = next;\n error = null;\n }\n async function contents(file: string): Promise<Buffer | undefined> {\n const path = join(root, file);\n let parent = dirname(path);\n while (true) {\n try {\n if ((await realpath(parent)) !== parent)\n throw new Error('Undo refused: a project directory is now a symbolic link.');\n break;\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code !== 'ENOENT') throw cause;\n parent = dirname(parent);\n }\n }\n try {\n const info = await lstat(path);\n if (!info.isFile() || info.isSymbolicLink())\n throw new Error('Undo refused: a project file changed type.');\n return await readFile(path);\n } catch (cause) {\n if ((cause as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw cause;\n }\n }\n async function replace(change: Change, expected?: Buffer, next?: Buffer) {\n if (!equal(await contents(change.file), expected))\n throw new Error(`History conflict in ${change.file}. Newer changes were preserved.`);\n const file = join(root, change.file);\n if (!next) return unlink(file);\n await mkdir(dirname(file), { recursive: true });\n // Write beside the target and rename over it, so a failed write leaves the\n // file whole and the rollback below only has to revert completed writes.\n // The `.tmp` suffix is outside `historyFile`, so snapshots never record it.\n const temporary = `${file}.${randomUUID()}.tmp`;\n try {\n await writeFile(temporary, next, { flag: 'wx' });\n const mode = await lstat(file).then(\n (info) => info.mode,\n (cause: NodeJS.ErrnoException) => {\n if (cause.code === 'ENOENT') return undefined;\n throw cause;\n },\n );\n if (mode !== undefined) await chmod(temporary, mode & 0o7777);\n // Re-check after the slow write. Without OS file locking, an external\n // save between this read and `rename` can still be overwritten.\n if (!equal(await contents(change.file), expected))\n throw new Error(`History conflict in ${change.file}. Newer changes were preserved.`);\n await rename(temporary, file);\n } finally {\n await rm(temporary, { force: true });\n }\n }\n try {\n record(await snapshot(), 'Initial state');\n } catch (cause) {\n error = cause instanceof Error ? cause.message : String(cause);\n }\n return {\n state: () => ({\n undo: past.at(-1)?.label ?? null,\n redo: future.at(-1)?.label ?? null,\n busy: active.size > 0 || restoring,\n error,\n }),\n capture: () =>\n serial(async () => {\n if (!active.size && !restoring) record(await snapshot(), 'Source edit');\n }),\n begin: (id: string, description: string) =>\n serial(async () => {\n if (active.has(id)) return;\n if (!active.size) {\n record(await snapshot(), 'Source edit');\n label = description;\n } else label = 'Concurrent edits';\n active.add(id);\n }),\n end: (id: string) =>\n serial(async () => {\n if (!active.delete(id) || active.size) return;\n record(await snapshot(), label);\n }),\n move: (direction: 'undo' | 'redo') =>\n serial(async () => {\n if (active.size)\n throw new Error(\n 'Wait for all agents and text edits to finish before using Undo or Redo.',\n );\n // A save made outside the preview is also an edit; never silently overwrite it.\n record(await snapshot(), 'Source edit');\n const from = direction === 'undo' ? past : future;\n const to = direction === 'undo' ? future : past;\n const item = from.at(-1);\n if (!item) throw new Error(`Nothing to ${direction}.`);\n const before = (c: Change) => (direction === 'undo' ? c.after : c.before);\n const after = (c: Change) => (direction === 'undo' ? c.before : c.after);\n const applied: Change[] = [];\n restoring = true;\n try {\n await Promise.all(\n item.changes.map(async (change) => {\n if (!equal(await contents(change.file), before(change)))\n throw new Error(\n `History conflict in ${change.file}. Newer changes were preserved.`,\n );\n }),\n );\n // Ordered writes let a failed transaction restore only the files it wrote.\n for (const change of item.changes) {\n await replace(change, before(change), after(change));\n applied.push(change);\n }\n for (const change of item.changes) {\n const content = after(change);\n if (content) baseline.set(change.file, content);\n else baseline.delete(change.file);\n }\n from.pop();\n to.push(item);\n error = null;\n } catch (cause) {\n const failures = [];\n for (const change of applied.reverse()) {\n try {\n await replace(change, after(change), before(change));\n } catch (rollback) {\n failures.push(String(rollback));\n }\n }\n if (failures.length)\n throw new Error(`${String(cause)}; rollback incomplete: ${failures.join('; ')}`);\n throw cause;\n } finally {\n restoring = false;\n }\n }),\n };\n}\n","import { planTextEdit, verifyTextEdit, type TextEditPlan } from '@office-kit/pptx-dsl/source-edit';\nimport { readFile, writeFile, realpath } from 'node:fs/promises';\nimport { dirname, relative, isAbsolute } from 'node:path';\n\ninterface Preview {\n revision: number;\n slides: string[];\n slideTexts: string[];\n dependencies: string[];\n}\ntype Change = Extract<TextEditPlan, { ok: true }>['change'];\n\nconst planErrors: Record<Extract<TextEditPlan, { ok: false }>['reason'], string> = {\n invalid: 'Invalid text edit.',\n 'not-unique-on-slide': 'Text cannot be located uniquely on this slide. Use Apply with AI.',\n 'not-found': 'Text is computed or has multiple source matches. Use Apply with AI.',\n ambiguous: 'Text is computed or has multiple source matches. Use Apply with AI.',\n};\n\nconst message = (error: unknown) => (error instanceof Error ? error.message : String(error));\n\n/** Literal edits are deliberately conservative: computed and ambiguous text goes to the agent. */\nexport function createTextEditor(\n entry: string,\n preview: () => Preview,\n verify: () => Promise<string | null>,\n) {\n let busy = false;\n async function restore(change: Change) {\n if (\n (await realpath(change.path)) !== change.path ||\n (await readFile(change.path, 'utf8')) !== change.after\n )\n throw new Error('Source changed during editing. Your newer changes were preserved.');\n await writeFile(change.path, change.before);\n }\n return {\n async edit(value: { revision: number; slide: number; before: string; after: string }) {\n if (busy) throw new Error('A text edit is already running.');\n busy = true;\n try {\n const current = preview();\n if (value.revision !== current.revision)\n throw new Error('Preview changed. Select the text again.');\n if (\n !Number.isInteger(value.slide) ||\n !current.slides[value.slide] ||\n typeof value.before !== 'string' ||\n typeof value.after !== 'string'\n )\n throw new Error(planErrors.invalid);\n const root = await realpath(dirname(entry));\n const files = await Promise.all(\n [...new Set(current.dependencies)]\n .filter((file) => /\\.[cm]?[jt]sx?$/.test(file))\n .map(async (file) => {\n const path = await realpath(file),\n local = relative(root, path);\n if (\n local.startsWith('..') ||\n isAbsolute(local) ||\n local.split(/[\\\\/]/).includes('node_modules')\n )\n return null;\n return { path, source: await readFile(path, 'utf8') };\n }),\n );\n const plan = planTextEdit({\n files: files.filter((file) => file !== null),\n slideText: current.slideTexts[value.slide]!,\n before: value.before,\n after: value.after,\n });\n if (!plan.ok) throw new Error(planErrors[plan.reason]);\n const { change } = plan;\n if (\n preview().revision !== current.revision ||\n (await realpath(change.path)) !== change.path ||\n (await readFile(change.path, 'utf8')) !== change.before\n )\n throw new Error('Source changed. Select the text again.');\n await writeFile(change.path, change.after);\n try {\n const error = await verify();\n if (error) throw new Error('The text change did not build.');\n if (\n !verifyTextEdit({\n previous: current,\n next: preview(),\n slide: value.slide,\n before: value.before,\n after: value.after,\n }).ok\n )\n throw new Error(\n 'This source affects other text or slides. Use Apply with AI for a local edit.',\n );\n if (\n (await realpath(change.path)) !== change.path ||\n (await readFile(change.path, 'utf8')) !== change.after\n )\n throw new Error('Source changed during editing.');\n } catch (error) {\n try {\n await restore(change);\n await verify();\n } catch (cleanup) {\n // Keep the reason the edit was refused; the cleanup failure alone\n // would hide it.\n throw new Error(`${message(error)} ${message(cleanup)}`, { cause: error });\n }\n throw error;\n }\n } finally {\n busy = false;\n }\n },\n };\n}\n","import {\n getGroupChildren,\n getShapeBounds,\n getShapeId,\n getShapeKind,\n getShapeRotation,\n getShapeXmlString,\n getSlides,\n getSlideShapes,\n getSlideXmlString,\n type PresentationData,\n type SlideShapeData,\n} from '@office-kit/pptx';\nimport type { PropEdit } from '@office-kit/pptx-dsl/source-edit';\n\nexport type DeckChange =\n | { kind: 'geometry'; slide: number; shapeId: number; edits: PropEdit[] }\n | { kind: 'unsupported'; slide: number | null; shapeId: number | null; description: string };\n\nconst ANGLE_UNITS = 60000;\n\ninterface Located {\n shape: SlideShapeData;\n /** Group members are positioned in their group's child space, not the slide's. */\n grouped: boolean;\n}\nfunction shapesById(shapes: readonly SlideShapeData[]): Map<number, Located> {\n const result = new Map<number, Located>();\n const visit = (shape: SlideShapeData, grouped: boolean) => {\n result.set(getShapeId(shape), { shape, grouped });\n for (const child of getGroupChildren(shape)) visit(child, true);\n };\n for (const shape of shapes) visit(shape, false);\n return result;\n}\n\n// The shape's own transform carries what geometry edits change; everything\n// else in its XML must match for the change to be geometry alone.\nfunction withoutTransform(xml: string): string {\n return xml\n .replace(/(<a:xfrm\\b[^>]*?) rot=\"-?\\d+\"/, '$1')\n .replace(/<a:off x=\"-?\\d+\" y=\"-?\\d+\"\\/>/, '')\n .replace(/<a:ext cx=\"\\d+\" cy=\"\\d+\"\\/>/, '');\n}\n\n// Shapes are compared one by one; the rest of the slide (background, timing,\n// transition) must match as a whole.\nfunction withoutShapes(xml: string): string {\n return xml.replace(/<p:spTree>[\\s\\S]*<\\/p:spTree>/, '');\n}\n\nfunction geometryEdits(source: SlideShapeData, edited: SlideShapeData): PropEdit[] {\n const before = getShapeBounds(source);\n const after = getShapeBounds(edited);\n const edits: PropEdit[] = [];\n if (before && after) {\n const pairs = [\n ['x', before.x, after.x],\n ['y', before.y, after.y],\n ['width', before.w, after.w],\n ['height', before.h, after.h],\n ] as const;\n for (const [prop, from, to] of pairs) if (from !== to) edits.push({ prop, from, to });\n }\n const fromAngle = Math.round(getShapeRotation(source) * ANGLE_UNITS);\n const toAngle = Math.round(getShapeRotation(edited) * ANGLE_UNITS);\n if (fromAngle !== toAngle) edits.push({ prop: 'rotation', from: fromAngle, to: toAngle });\n return edits;\n}\n\n/**\n * What the editor changed relative to the deck built from source. Shapes are\n * matched by slide index and shape id — the editor edits the deck the server\n * built, so ids agree. A change this diff cannot state as typed props is\n * reported as `unsupported`, never dropped.\n */\nexport function diffDecks(source: PresentationData, edited: PresentationData): DeckChange[] {\n const changes: DeckChange[] = [];\n const sourceSlides = getSlides(source);\n const editedSlides = getSlides(edited);\n if (sourceSlides.length !== editedSlides.length)\n changes.push({\n kind: 'unsupported',\n slide: null,\n shapeId: null,\n description: `slide count ${sourceSlides.length} → ${editedSlides.length}`,\n });\n const count = Math.min(sourceSlides.length, editedSlides.length);\n for (let slide = 0; slide < count; slide++) {\n const sourceSlide = sourceSlides[slide]!;\n const editedSlide = editedSlides[slide]!;\n if (\n withoutShapes(getSlideXmlString(sourceSlide)) !==\n withoutShapes(getSlideXmlString(editedSlide))\n )\n changes.push({ kind: 'unsupported', slide, shapeId: null, description: 'slide properties' });\n const before = shapesById(getSlideShapes(sourceSlide));\n const after = shapesById(getSlideShapes(editedSlide));\n for (const [shapeId, { shape, grouped }] of after) {\n const original = before.get(shapeId)?.shape;\n if (!original) {\n changes.push({ kind: 'unsupported', slide, shapeId, description: 'shape added' });\n continue;\n }\n // A group's XML holds its members, which are compared on their own.\n if (getShapeKind(shape) === 'group') {\n if (geometryEdits(original, shape).length)\n changes.push({ kind: 'unsupported', slide, shapeId, description: 'group transform' });\n continue;\n }\n if (\n withoutTransform(getShapeXmlString(original)) !== withoutTransform(getShapeXmlString(shape))\n )\n changes.push({\n kind: 'unsupported',\n slide,\n shapeId,\n description: 'properties other than position, size and rotation',\n });\n const edits = geometryEdits(original, shape);\n if (edits.length && grouped)\n changes.push({\n kind: 'unsupported',\n slide,\n shapeId,\n description: 'grouped shape geometry',\n });\n else if (edits.length) changes.push({ kind: 'geometry', slide, shapeId, edits });\n }\n for (const shapeId of before.keys())\n if (!after.has(shapeId))\n changes.push({ kind: 'unsupported', slide, shapeId, description: 'shape removed' });\n }\n return changes;\n}\n","import { loadPresentation } from '@office-kit/pptx';\nimport type { JsxSource } from '@office-kit/pptx-dsl';\nimport { planPropEdit } from '@office-kit/pptx-dsl/source-edit';\nimport { readFile, realpath, writeFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, relative } from 'node:path';\nimport type { BuildResult } from './build.ts';\nimport { diffDecks, type DeckChange } from './deck-diff.ts';\nimport { sourceFingerprint } from './fingerprint.ts';\n\nexport interface SyncResult {\n /** Changes now stated in the TSX source. */\n written: DeckChange[];\n /** Changes the source does not state yet; the caller keeps them in the sidecar. */\n pending: (DeckChange & { reason?: string })[];\n /** The rebuilt source produces exactly the edited deck, so no sidecar is needed. */\n complete: boolean;\n}\n\ninterface Target {\n change: Extract<DeckChange, { kind: 'geometry' }>;\n anchor: JsxSource;\n}\n\nconst key = (change: DeckChange) =>\n `${change.kind}:${change.slide}:${change.shapeId}:${change.kind === 'unsupported' ? change.description : ''}`;\n\n/**\n * Writes the editor's changes back into the TSX source as typed prop edits.\n * Every write is verified by rebuilding: the changes it claims must disappear\n * from the diff and no other change may appear, or every file is restored.\n */\nexport function createSourceSync(\n entry: string,\n source: () => BuildResult | undefined,\n rebuild: () => Promise<string | null>,\n) {\n let busy = false;\n\n async function localFile(fileName: string): Promise<string | null> {\n const root = await realpath(dirname(entry));\n let path: string;\n try {\n path = await realpath(fileName);\n } catch {\n return null;\n }\n const local = relative(root, path);\n if (\n local.startsWith('..') ||\n isAbsolute(local) ||\n local.split(/[\\\\/]/).includes('node_modules')\n )\n return null;\n return path;\n }\n\n return {\n async sync(edited: Uint8Array): Promise<SyncResult> {\n if (busy) throw new Error('A source write-back is already running.');\n busy = true;\n try {\n const built = source();\n if (!built?.shapeSources) throw new Error('The source deck is not built yet.');\n const shapeSources = built.shapeSources;\n const editedDeck = await loadPresentation(edited);\n const changes = diffDecks(await loadPresentation(built.bytes), editedDeck);\n // An element evaluated more than once (a `.map` callback, a reused\n // component) made several shapes; its attributes are not one shape's.\n const uses = new Map<string, number>();\n for (const bySlide of Object.values(shapeSources))\n for (const chain of Object.values(bySlide)) {\n const at = chain.at(-1);\n if (at) {\n const id = `${at.fileName}:${at.lineNumber}:${at.columnNumber}`;\n uses.set(id, (uses.get(id) ?? 0) + 1);\n }\n }\n\n const pending: SyncResult['pending'] = [];\n const targets: Target[] = [];\n for (const change of changes) {\n if (change.kind !== 'geometry') {\n pending.push(change);\n continue;\n }\n const anchor = shapeSources[change.slide]?.[change.shapeId]?.at(-1);\n if (!anchor) {\n pending.push({ ...change, reason: 'no source element' });\n continue;\n }\n if (\n (uses.get(`${anchor.fileName}:${anchor.lineNumber}:${anchor.columnNumber}`) ?? 0) > 1\n ) {\n pending.push({ ...change, reason: 'element makes several shapes' });\n continue;\n }\n targets.push({ change, anchor });\n }\n\n // Plan per file, later elements first, so an edit never shifts the\n // position of an anchor still to be planned.\n const byFile = new Map<string, Target[]>();\n for (const target of targets) {\n const path = await localFile(target.anchor.fileName);\n if (!path) {\n pending.push({ ...target.change, reason: 'source outside the project' });\n continue;\n }\n byFile.set(path, [...(byFile.get(path) ?? []), target]);\n }\n const files: { path: string; before: string; after: string }[] = [];\n const written: DeckChange[] = [];\n for (const [path, fileTargets] of byFile) {\n const before = await readFile(path, 'utf8');\n let text = before;\n fileTargets.sort(\n (a, b) =>\n b.anchor.lineNumber - a.anchor.lineNumber ||\n b.anchor.columnNumber - a.anchor.columnNumber,\n );\n for (const { change, anchor } of fileTargets) {\n const plan = planPropEdit({\n file: { path, source: text },\n anchor: { path, lineNumber: anchor.lineNumber, columnNumber: anchor.columnNumber },\n edits: change.edits,\n });\n if (!plan.ok) {\n pending.push({ ...change, reason: plan.reason });\n continue;\n }\n text = plan.change.after;\n written.push(change);\n }\n if (text !== before) files.push({ path, before, after: text });\n }\n\n if (files.length === 0) return { written: [], pending, complete: false };\n\n const restore = async () => {\n for (const file of files) {\n if ((await readFile(file.path, 'utf8')) !== file.after)\n throw new Error(\n 'Source changed during write-back. Your newer changes were preserved.',\n );\n }\n for (const file of files) await writeFile(file.path, file.before);\n await rebuild();\n };\n for (const file of files) {\n if ((await readFile(file.path, 'utf8')) !== file.before)\n throw new Error('Source changed. Save again.');\n }\n for (const file of files) await writeFile(file.path, file.after);\n try {\n const error = await rebuild();\n if (error) throw new Error('The written source did not build: ' + error);\n const rebuilt = source();\n if (!rebuilt) throw new Error('The written source did not build.');\n const remaining = diffDecks(await loadPresentation(rebuilt.bytes), editedDeck);\n const expected = new Set(pending.map(key));\n if (remaining.some((change) => !expected.has(key(change))))\n throw new Error('The written source changed more than the edited shapes.');\n return {\n written,\n pending,\n complete:\n remaining.length === 0 &&\n sourceFingerprint(rebuilt.bytes) === sourceFingerprint(edited),\n };\n } catch (error) {\n await restore();\n throw error;\n }\n } finally {\n busy = false;\n }\n },\n };\n}\n","import { mkdir, writeFile } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport { chromium, type Browser } from 'playwright';\n\nexport interface VisualReview {\n prompt: string;\n images: string[];\n}\n/** Each session compares against its own turn's preview, including offscreen slides. */\nexport function createVisualReviewer(entry: string, slides: () => string[]) {\n let before: string[] = [];\n let rounds = 0;\n return {\n begin() {\n before = slides().slice();\n rounds = 0;\n },\n async next(): Promise<VisualReview | undefined> {\n const current = slides().slice();\n const changed = current.flatMap((svg, index) =>\n svg !== before[index] ? [{ svg, index }] : [],\n );\n if (!changed.length) return;\n if (rounds >= 3)\n throw new Error(\n 'Visual review reached its correction limit. Review the latest slide manually before continuing.',\n );\n const round = rounds + 1;\n let browser: Browser;\n try {\n browser = await chromium.launch({\n channel: process.env.PLAYWRIGHT_CHANNEL || 'chrome',\n headless: true,\n });\n } catch {\n try {\n browser = await chromium.launch({ headless: true });\n } catch {\n throw new Error(\n 'Visual review needs Chrome or a Playwright Chromium installation. Install Chrome and retry. The source edit was saved; visual review was not completed.',\n );\n }\n }\n try {\n const directory = join(dirname(entry), '.office-kit', 'reviews', randomUUID());\n await mkdir(directory, { recursive: true });\n const page = await browser.newPage({\n viewport: { width: 1600, height: 1000 },\n deviceScaleFactor: 1,\n });\n // Decks may reference remote assets. Review must not make network requests or execute SVG scripts.\n await page.route('**/*', (route) => route.abort());\n const images: string[] = [];\n for (const { svg, index } of changed) {\n await page.setContent(\n '<meta http-equiv=\"Content-Security-Policy\" content=\"default-src \\'none\\'; style-src \\'unsafe-inline\\'; img-src data: blob:; font-src data:\"><style>html,body{margin:0}svg{display:block;width:1600px;height:auto}</style>' +\n svg,\n );\n await page.evaluate(() => document.fonts.ready.then(() => undefined));\n const file = join(directory, `slide-${index + 1}.png`);\n await page.locator('svg').first().screenshot({ path: file, timeout: 15000 });\n images.push(file);\n }\n const prompt = `Visual review after the edit (round ${round}/3). Open every attached screenshot (or read each local PNG path below) before finishing. Check clipping, overlap, alignment, spacing, legibility and foreground/background contrast. ${round === 3 ? 'This is the final review: do not edit further; report any remaining defects to the user.' : 'Fix concrete design defects only on these affected slides;'} preserve the user's exact text and unrelated work. Do not claim visual verification unless you inspected the images. If there are no defects, finish without edits. Screenshots: ${JSON.stringify(images)}`;\n await writeFile(join(directory, 'review.txt'), prompt);\n before = current;\n rounds = round;\n return { prompt, images };\n } finally {\n await browser.close();\n }\n },\n };\n}\n","/** Labels for the preview shell; the editor sends its active locale via editor-focus. */\nexport const previewI18nScript = `\nlet previewLocale=navigator.language?.toLowerCase().startsWith('ja')?'ja':'en';\ntry{const stored=localStorage.getItem('ok-editor-locale');if(stored==='ja'||stored==='en')previewLocale=stored;}catch{}\nlet connectionLost=false;\nconst previewJapanese={\n 'Normal':'標準','Normal (Esc)':'標準 (Esc)','Agents':'エージェント','Close Agents':'エージェントを閉じる','Presentation views':'プレゼンテーションの表示','Present':'プレゼンテーション','Presenter view':'発表者ビュー',\n 'Download PPTX':'PPTXをダウンロード','Slides':'スライド','Slide viewer':'スライドビューアー','Presentation editor':'プレゼンテーション編集',\n 'Chat width':'チャットの幅','Drag to resize · Double-click to reset':'ドラッグで幅を変更・ダブルクリックでリセット',\n 'Previous slide':'前のスライド','Next slide':'次のスライド','Zoom':'ズーム','Fit':'画面に合わせる',\n 'Exit · Esc':'終了 · Esc','Exit view · Esc':'表示を終了 · Esc','Slide position':'スライドの位置','No slides':'スライドがありません','Whole project':'プロジェクト全体',\n 'Waiting for slides…':'スライドを待っています…','Building…':'ビルド中…','Updating…':'更新中…','Reconnecting…':'再接続中…',\n 'Build failed · showing last successful output':'ビルドに失敗しました・最後に成功した結果を表示中',\n 'Changes appear automatically · Text can be selected and copied':'変更は自動的に反映されます・文字を選択してコピーできます',\n 'Skipped during presentation':'プレゼンテーションでスキップされます',\n 'Allow popups to open presenter view.':'発表者ビューを開くにはポップアップを許可してください。',\n 'Presentation preview':'プレゼンテーションのプレビュー'\n};\nconst pt=key=>previewLocale==='ja'?(previewJapanese[key]??key):key;\nconst slideLabel=i=>previewLocale==='ja'?'スライド '+(i+1):'Slide '+(i+1);\nconst slideCount=()=>!state.slides.length?pt('No slides'):previewLocale==='ja'?'スライド '+(index+1)+' / '+state.slides.length:'Slide '+(index+1)+' of '+state.slides.length;\nfunction updatePreviewStatus(){\n byId('status').textContent=connectionLost?pt('Reconnecting…'):state.error?pt('Build failed · showing last successful output'):state.building?pt('Updating…'):previewLocale==='ja'?state.slides.length+' スライド・ライブ':state.slides.length+' slides · Live';\n}\nfunction updatePreviewLabels(){\n document.documentElement.lang=previewLocale;document.title=pt('Presentation preview');\n const labels={'#present':'Present','#presenter':'Presenter view','.download':'Download PPTX','#toggle-chat span':'Agents','#agents-title':'Agents','.filmstrip h2':'Slides','#empty':'Waiting for slides…','footer .hint':'Changes appear automatically · Text can be selected and copied','label[for=\"zoom\"]':'Zoom','#zoom option[value=\"fit\"]':'Fit'};\n for(const [selector,key] of Object.entries(labels))document.querySelector(selector).textContent=pt(key);\n for(const [selector,key] of Object.entries({'.filmstrip':'Slides','main':'Slide viewer','#chat-resizer':'Chat width','#close-chat':'Close Agents','#toggle-editor':'Normal','.status-views':'Presentation views','#prev':'Previous slide','#present-prev':'Previous slide','#next':'Next slide','#present-next':'Next slide'}))document.querySelector(selector).setAttribute('aria-label',pt(key));\n byId('editor-frame').title=pt('Presentation editor');byId('chat-resizer').title=pt('Drag to resize · Double-click to reset');\n byId('exit-present').textContent=pt('Exit · Esc');\n byId('close-chat').title=pt('Close Agents');byId('toggle-editor').title=pt('Normal (Esc)');\n byId('count').textContent=slideCount();byId('present-count').textContent=slideCount();\n updateAnimationNotice();\n slide.setAttribute('aria-label',slideLabel(index));\n for(const [i,item] of Array.from(thumbnails.children).entries()){\n item.firstElementChild.setAttribute('aria-label',slideLabel(i));\n item.firstElementChild.title=state.hiddenSlides?.[i]?pt('Skipped during presentation'):'';\n }\n updateChatWidthAria();updatePreviewStatus();\n}\n`;\n","/** Presentation-only visual transitions. All layers stay inside the clipped slide. */\nexport const transitionScript = `\nlet transitionAnimations=[],transitionCleanup;\nconst reducedMotion=matchMedia('(prefers-reduced-motion: reduce)');\nfunction cancelTransition(){\n const cleanup=transitionCleanup;transitionCleanup=undefined;\n for(const animation of transitionAnimations)animation.cancel();\n transitionAnimations=[];cleanup?.();\n}\nreducedMotion.addEventListener('change',()=>{if(reducedMotion.matches){cancelTransition();scheduleAdvance();}});\nfunction renderSlide(svg,options){\n const previous=displayedSvg;\n cancelTransition();\n // What the viewer last saw, not the drawing the slide started as: leaving a\n // slide part-way through its build must not flash the effects still to come.\n const previousMarkup=canvas.innerHTML;\n displayedSvg=svg;\n const style='<style>svg{display:block;width:100%;height:100%}.transition-layer{position:absolute;inset:0;background:white;overflow:hidden}.transition-old{pointer-events:none}</style>';\n canvas.innerHTML=svg?style+svg:'';\n const playableEffects=['fade','push','wipe','cover','pull','zoom','split','circle','diamond','plus','blinds','comb','checker','strips','randomBar','dissolve','wedge','newsflash','wheel'];\n // The reference desktop app (2010+) effects (p14 / p15 / p159) play as the nearest flat\n // effect above: their 3-D turns, particles and page curls are not drawn.\n const approximations={morph:'fade',flash:'fade',reveal:'fade',prstTrans:'fade',honeycomb:'dissolve',glitter:'dissolve',vortex:'dissolve',shred:'dissolve',ripple:'circle',switch:'push',flip:'push',gallery:'push',conveyor:'push',ferris:'push',pan:'push',prism:'push',doors:'split',window:'split',warp:'zoom',flythrough:'zoom',wheelReverse:'wheel'};\n const alias=Object.hasOwn(approximations,options?.effect??'')?approximations[options.effect]:undefined;\n if(alias)options=alias==='split'\n ?{...options,effect:alias,orientation:options.direction==='horz'?'horz':'vert',direction:'out'}\n :{...options,effect:alias};\n // Choose per visit without replacing the persisted random transition or its timing.\n const effect=options?.effect==='random'?playableEffects[Math.floor(Math.random()*playableEffects.length)]:options?.effect;\n if(!previous||!svg||reducedMotion.matches||(effect==='cut'&&!options.thruBlack)||!(effect==='cut'||playableEffects.includes(effect)))return;\n const incoming=document.createElement('div'),outgoing=document.createElement('div');\n incoming.className='transition-layer';incoming.innerHTML=svg;\n outgoing.className='transition-layer transition-old';outgoing.innerHTML=previousMarkup;\n outgoing.setAttribute('aria-hidden','true');outgoing.inert=true;\n canvas.innerHTML=style;canvas.append(outgoing,incoming);\n // The reference desktop app plays fast/med/slow as 0.5/0.75/1 s; p14:dur overrides them.\n // No spd is the schema default, fast.\n const duration=options.durationMs??(options.speed==='slow'?1000:options.speed==='med'?750:500);\n const timing={duration,easing:'ease-in-out',fill:'both'};\n const animate=(node,frames,easing=timing.easing)=>transitionAnimations.push(node.animate(frames,{...timing,easing}));\n const vectors={l:[-100,0],r:[100,0],u:[0,-100],d:[0,100],lu:[-100,-100],ru:[100,-100],ld:[-100,100],rd:[100,100]};\n const [x,y]=vectors[options.direction]??vectors.l;\n const shift=(a,b)=>'translate('+a+'%,'+b+'%)';\n if(effect==='cut'){\n slide.style.background='black';outgoing.style.visibility='hidden';\n animate(incoming,[{opacity:0},{opacity:1}],'steps(1,end)');\n }else if(effect==='fade'){\n if(options.thruBlack){\n outgoing.style.background='black';incoming.style.background='black';slide.style.background='black';\n animate(outgoing,[{opacity:1,offset:0},{opacity:0,offset:.5},{opacity:0,offset:1}]);\n animate(incoming,[{opacity:0,offset:0},{opacity:0,offset:.5},{opacity:1,offset:1}]);\n }else animate(incoming,[{opacity:0},{opacity:1}]);\n }else if(effect==='push'||effect==='cover'){\n animate(incoming,[{transform:shift(-x,-y)},{transform:shift(0,0)}]);\n if(effect==='push')animate(outgoing,[{transform:shift(0,0)},{transform:shift(x,y)}]);\n }else if(effect==='pull'){\n canvas.append(outgoing);\n animate(outgoing,[{transform:shift(0,0)},{transform:shift(x,y)}]);\n }else if(effect==='wipe'){\n const clips={l:'inset(0 0 0 100%)',r:'inset(0 100% 0 0)',u:'inset(100% 0 0 0)',d:'inset(0 0 100% 0)'};\n animate(incoming,[{clipPath:clips[options.direction]??clips.l},{clipPath:'inset(0 0 0 0)'}]);\n }else if(effect==='blinds'||effect==='comb'){\n const vertical=options.direction==='vert';\n const bands=(progress)=>{\n const points=[];\n for(let band=0;band<8;band++){\n const start=band*12.5,end=start+12.5;\n const left=effect==='comb'&&band%2?100*(1-progress):0;\n const right=effect==='comb'&&band%2?100:100*progress;\n const rect=effect==='blinds'\n ?[[start,0],[start+12.5*progress,0],[start+12.5*progress,100],[start,100]]\n :[[left,start],[right,start],[right,end],[left,end]];\n // Return along the same bridge so disconnected bands share one clip polygon.\n points.push([0,0],...rect,rect[0],[0,0]);\n }\n return 'polygon('+points.map(([x,y])=>vertical?y+'% '+x+'%':x+'% '+y+'%').join(',')+')';\n };\n animate(incoming,[{clipPath:bands(0)},{clipPath:bands(1)}]);\n }else if(effect==='checker'){\n const cells=(progress)=>{\n const points=[];\n for(let row=0;row<6;row++)for(let col=0;col<8;col++){\n const amount=Math.max(0,Math.min(1,2*progress-(row+col)%2));\n const left=col*100/8,top=row*100/6;\n const right=(col+(options.direction==='vert'?1:amount))*100/8;\n const bottom=(row+(options.direction==='vert'?amount:1))*100/6;\n // Keep zero-area bridges on cell boundaries to avoid diagonal antialiasing artifacts.\n points.push([0,0],[left,0],[left,top],[right,top],[right,bottom],[left,bottom],[left,top],[left,0],[0,0]);\n }\n return 'polygon('+points.map(([x,y])=>x+'% '+y+'%').join(',')+')';\n };\n animate(incoming,[{clipPath:cells(0)},{clipPath:cells(.5)},{clipPath:cells(1)}]);\n }else if(effect==='newsflash'){\n animate(incoming,[{transform:'rotate(720deg) scale(0)'},{transform:'rotate(0deg) scale(1)'}]);\n }else if(effect==='wheel'){\n const spokes=options.spokes??4;\n if(spokes===0)animate(incoming,[{opacity:0},{opacity:1}]);\n else{\n const stages=64,sector=360/spokes;\n // A repeating mask keeps memory bounded even for the schema's maximum unsigned integer.\n const mask=(progress)=>'repeating-conic-gradient(#000 0deg '+sector*progress+'deg,transparent '+sector*progress+'deg '+sector+'deg)';\n // Gradient images are discrete values; explicit stages avoid a halfway-only image swap.\n animate(incoming,Array.from({length:stages+1},(_,step)=>({maskImage:mask(step/stages),offset:step/stages,easing:'steps(1,end)'})),'linear');\n }\n }else if(effect==='wedge'){\n const segments=32;\n const wedge=(progress)=>{\n const points=['50% 50%'];\n for(let point=0;point<=segments;point++){\n const angle=-Math.PI/2+Math.PI*progress*(2*point/segments-1);\n points.push((50+100*Math.cos(angle))+'% '+(50+100*Math.sin(angle))+'%');\n }\n return 'polygon('+points.join(',')+')';\n };\n // Intermediate angles keep the radial boundaries rotating instead of shrinking across the slide.\n animate(incoming,Array.from({length:segments+1},(_,step)=>({clipPath:wedge(step/segments),offset:step/segments})));\n }else if(effect==='dissolve'){\n const columns=32,stages=16,bounds=incoming.getBoundingClientRect();\n const rows=Math.max(1,Math.round(columns*bounds.height/bounds.width)),count=columns*rows;\n const order=Array.from({length:count},(_,i)=>i);\n for(let i=count-1;i>0;i--){\n const j=Math.floor(Math.random()*(i+1));\n [order[i],order[j]]=[order[j],order[i]];\n }\n const cells=(step)=>{\n const points=['0% 0%'];\n for(let i=0;i<Math.floor(count*step/stages);i++){\n const cell=order[i],col=cell%columns,row=Math.floor(cell/columns);\n const left=100*col/columns,right=100*(col+1)/columns,top=100*row/rows,bottom=100*(row+1)/rows;\n // Retrace bridges along cell boundaries to avoid diagonal clipping artifacts.\n for(const [x,y] of [[left,0],[left,top],[right,top],[right,bottom],[left,bottom],[left,top],[left,0],[0,0]])\n points.push(x+'% '+y+'%');\n }\n return step===0?'polygon(0% 0%,0% 0%,0% 0%)':'polygon('+points.join(',')+')';\n };\n // Discrete masks add whole squares instead of morphing unrelated polygon vertices.\n animate(incoming,Array.from({length:stages+1},(_,step)=>({clipPath:cells(step),offset:step/stages,easing:'steps(1,end)'})),'linear');\n }else if(effect==='randomBar'){\n const bands=16,vertical=options.direction==='vert';\n const ranks=Array.from({length:bands},(_,i)=>i);\n for(let i=bands-1;i>0;i--){\n const j=Math.floor(Math.random()*(i+1));\n [ranks[i],ranks[j]]=[ranks[j],ranks[i]];\n }\n const bars=(step)=>{\n const points=[];\n for(let band=0;band<bands;band++){\n const top=100*band/bands,bottom=top+100/bands*Math.max(0,Math.min(1,step-ranks[band]));\n // Retrace the edge bridge so disconnected bars do not clip diagonal slivers.\n for(const [x,y] of [[0,0],[0,top],[100,top],[100,bottom],[0,bottom],[0,top],[0,0]])\n points.push((vertical?y:x)+'% '+(vertical?x:y)+'%');\n }\n return 'polygon('+points.join(',')+')';\n };\n animate(incoming,Array.from({length:bands+1},(_,step)=>({clipPath:bars(step),offset:step/bands})));\n }else if(effect==='strips'){\n const direction=options.direction??'lu';\n const strips=(progress)=>{\n const points=[];\n for(let band=0;band<8;band++){\n const top=band*12.5,bottom=(band+1)*12.5;\n const right=100*Math.max(0,Math.min(1,(15*progress-band)/8));\n points.push([0,0],[0,top],[right,top],[right,bottom],[0,bottom],[0,top],[0,0]);\n }\n return 'polygon('+points.map(([x,y])=>(direction.startsWith('l')?100-x:x)+'% '+(direction.endsWith('u')?100-y:y)+'%').join(',')+')';\n };\n // Include every band's start/end so interpolation preserves the staggered wipe.\n animate(incoming,Array.from({length:16},(_,step)=>({clipPath:strips(step/15),offset:step/15})));\n }else if(effect==='split'){\n const collapsed=options.orientation==='vert'?'inset(0 50%)':'inset(50% 0)';\n const expanded='inset(0 0)';\n if(options.direction==='in'){\n canvas.append(outgoing);\n animate(outgoing,[{clipPath:expanded},{clipPath:collapsed}]);\n }else animate(incoming,[{clipPath:collapsed},{clipPath:expanded}]);\n }else if(effect==='circle'){\n animate(incoming,[{clipPath:'circle(0% at 50% 50%)'},{clipPath:'circle(100% at 50% 50%)'}]);\n }else if(effect==='diamond'){\n animate(incoming,[{clipPath:'polygon(50% 50%,50% 50%,50% 50%,50% 50%)'},{clipPath:'polygon(50% -50%,150% 50%,50% 150%,-50% 50%)'}]);\n }else if(effect==='plus'){\n const cross=(low,high)=>'polygon('+low+'% 0%,'+high+'% 0%,'+high+'% '+low+'%,100% '+low+'%,100% '+high+'%,'+high+'% '+high+'%,'+high+'% 100%,'+low+'% 100%,'+low+'% '+high+'%,0% '+high+'%,0% '+low+'%,'+low+'% '+low+'%)';\n animate(incoming,[{clipPath:cross(50,50)},{clipPath:cross(0,100)}]);\n }else if(effect==='zoom'){\n animate(incoming,[{transform:options.direction==='out'?'scale(2)':'scale(0.1)',opacity:0},{transform:'scale(1)',opacity:1}]);\n }\n clearTimeout(advanceTimer);advanceKey=null;\n const cleanup=()=>{\n outgoing.remove();\n // The incoming layer keeps its own nodes rather than being re-parsed into\n // the canvas: the slide's build may already be running on them — a leading\n // 'with previous' effect starts as the slide appears — and new nodes would\n // lose both the animation and what it was hiding. Dropping the class is\n // enough to leave no transition layer behind.\n incoming.className='';incoming.style.cssText='position:absolute;inset:0';\n slide.style.background='';\n };\n transitionCleanup=cleanup;\n Promise.all(transitionAnimations.map(animation=>animation.finished)).then(()=>{\n if(transitionCleanup!==cleanup)return;\n cancelTransition();scheduleAdvance();\n },()=>{});\n}\n`;\n","// The shell and the agent panes sit next to the editor, so they share its\n// design tokens. These values are copied from packages/editor/src/ui/tokens.css\n// (the shell is plain HTML and cannot import the editor's stylesheet);\n// test/shell-tokens.test.mjs fails when the two drift apart.\nexport const tokenStyles = `:root{\n--ok-accent:#b4235a;--ok-accent-2:#d6336c;--ok-bg:#f4f5f7;--ok-panel:#ffffff;--ok-panel-2:#f9fafb;--ok-ribbon:#f7f8fa;--ok-ribbon-active:#ffffff;\n--ok-border:#dfe2e7;--ok-border-strong:#c9cdd6;--ok-text:#15171c;--ok-text-2:#545b69;--ok-text-3:#8a909c;--ok-hover:#eef0f3;--ok-selected:#fdebf1;--ok-selected-border:#b4235a;\n--ok-canvas-bg:#d3d6dc;--ok-danger:#c92a2a;--ok-radius:3px;--ok-radius-lg:6px;\n--ok-shadow:0 1px 2px rgba(16, 18, 23, 0.08), 0 0 0 1px rgba(16, 18, 23, 0.04);--ok-shadow-lg:0 12px 32px -8px rgba(16, 18, 23, 0.22), 0 2px 6px rgba(16, 18, 23, 0.08);\n--ok-font:system-ui, -apple-system, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;--ok-mono:ui-monospace, 'SF Mono', 'Cascadia Code', 'Consolas', monospace;\n--shell-alert-bg:#fff0cd;--shell-alert-text:#583b00;\ncolor-scheme:light;\n}\n@media (prefers-color-scheme: dark){:root{\ncolor-scheme:dark;\n--ok-accent:#e85a90;--ok-accent-2:#ff9cbf;--ok-bg:#1f1f1f;--ok-panel:#2b2b2b;--ok-panel-2:#252525;--ok-ribbon:#2a2a2a;--ok-ribbon-active:#333333;\n--ok-border:#3d3d3d;--ok-border-strong:#555555;--ok-text:#f0f0f0;--ok-text-2:#c4c4c4;--ok-text-3:#8f8f8f;--ok-hover:#3a3a3a;--ok-selected:#3a2230;--ok-selected-border:#ff7aa8;\n--ok-canvas-bg:#1b1b1b;--ok-danger:#f1707a;\n--ok-shadow:0 1px 2px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(255, 255, 255, 0.04);--ok-shadow-lg:0 12px 32px -8px rgba(0, 0, 0, 0.6), 0 2px 6px rgba(0, 0, 0, 0.35);\n--shell-alert-bg:#4a3a12;--shell-alert-text:#ffe2a3;\n}}\n*{box-sizing:border-box}\nbody{margin:0;background:var(--ok-bg);color:var(--ok-text);font:13px var(--ok-font);-webkit-font-smoothing:antialiased}\nbutton,a,select,textarea{font:inherit;color:inherit}\nbutton,select,.download{border:1px solid transparent;border-radius:var(--ok-radius);background:transparent;padding:3px 8px;text-decoration:none;cursor:pointer;white-space:nowrap}\nbutton:hover:not(:disabled),.download:hover{background:var(--ok-hover);border-color:var(--ok-border)}\nbutton:active:not(:disabled){background:var(--ok-selected)}\nbutton[aria-pressed=\"true\"]{background:var(--ok-selected);border-color:var(--ok-selected-border)}\nbutton:disabled{color:var(--ok-text-3);cursor:default}\nselect{border-color:var(--ok-border-strong);background:var(--ok-panel);padding:2px 6px}\nselect:hover:not(:disabled){border-color:var(--ok-border-strong)}\n:focus-visible{outline:2px solid var(--ok-selected-border);outline-offset:1px}\n.primary{background:var(--ok-accent);border-color:var(--ok-accent);color:#fff}\n.primary:hover:not(:disabled){background:var(--ok-accent-2);border-color:var(--ok-accent-2)}\n.primary:disabled{background:var(--ok-hover);border-color:var(--ok-border);color:var(--ok-text-3)}\n[hidden]{display:none!important}\n@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}\n`;\n\nexport const previewStyles = `${tokenStyles}\nbody{height:100dvh;overflow:hidden;display:grid;grid-template-rows:minmax(0,1fr) 26px}\n.workspace{--filmstrip-width:190px;--chat-width:38vw;display:grid;grid-template-columns:var(--filmstrip-width) minmax(0,1fr) clamp(280px,var(--chat-width),calc(100vw - var(--filmstrip-width) - 240px));min-height:0}\n.filmstrip{background:var(--ok-panel-2);border-right:1px solid var(--ok-border);overflow:auto;overscroll-behavior:contain;padding:8px}\n.filmstrip h2{margin:4px 0 8px 26px;font-size:11px;font-weight:600;color:var(--ok-text-2)}\n#thumbnails{display:flex;flex-direction:column;gap:6px;margin:0;padding:0;list-style:none}\n.thumbnail{display:flex;align-items:flex-start;gap:6px;width:100%;padding:6px 6px 8px 4px;border:0;background:transparent;border-radius:0;text-align:left}\n.thumbnail:hover:not(:disabled){background:transparent;border-color:transparent}\n.slide-number{width:16px;flex:none;text-align:right;font-size:11px;color:var(--ok-text-2);padding-top:2px}\n.thumbnail img{display:block;min-width:0;width:calc(100% - 22px);background:white;border:1px solid var(--ok-border);border-radius:4px;aspect-ratio:var(--slide-ratio,16/9);object-fit:contain}\n.thumbnail:hover img{outline:3px solid var(--ok-border-strong);outline-offset:2px}\n.thumbnail[aria-current=\"true\"] img{outline:3px solid var(--ok-accent);outline-offset:2px}.thumbnail[aria-current=\"true\"] .slide-number{color:var(--ok-accent);font-weight:600}\n.thumbnail[data-skipped=\"true\"] .slide-number{text-decoration:line-through}\n.thumbnail[data-skipped=\"true\"] img{opacity:.6}\nmain{min-width:0;min-height:0;display:flex;flex-direction:column}\n#error{flex:none;max-height:30%;overflow:auto;background:var(--shell-alert-bg);color:var(--shell-alert-text);margin:0;padding:8px 12px;font:12px var(--ok-mono);white-space:pre-wrap;border-bottom:1px solid var(--ok-border)}\n#stage{background:var(--ok-canvas-bg);flex:1;min-height:0;overflow:auto;display:flex;padding:32px;overscroll-behavior:contain}\n#slide{position:relative;flex:none;margin:auto;background:white;box-shadow:var(--ok-shadow-lg);overflow:hidden;user-select:text;-webkit-user-select:text}\n#empty{margin:auto;color:var(--ok-text-2)}\nfooter{display:flex;align-items:center;gap:6px;padding:0 12px;background:var(--ok-ribbon);border-top:1px solid var(--ok-border);color:var(--ok-text-2);font-size:11px;min-width:0;white-space:nowrap}\nfooter button,footer .download,footer select{font-size:11px;padding:1px 6px;line-height:16px}\nfooter .hint{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;color:var(--ok-text-3)}\n#status{min-width:0;overflow:hidden;text-overflow:ellipsis}\n.status-views{display:flex;gap:2px}\nfooter svg{display:block;fill:none;stroke:currentColor;stroke-width:1.1}\n#toggle-editor{padding:2px 6px}\n#toggle-chat svg{width:13px;height:13px}#toggle-chat[aria-pressed=\"true\"] svg{fill:var(--ok-accent);stroke:var(--ok-accent)}#toggle-chat{display:inline-flex;align-items:center;gap:4px}\n#notice{position:fixed;z-index:40;left:50%;bottom:36px;transform:translateX(-50%);max-width:min(480px,calc(100vw - 32px));padding:8px 12px;border:1px solid var(--ok-border);border-radius:var(--ok-radius-lg);background:var(--ok-panel);color:var(--ok-text);box-shadow:var(--ok-shadow-lg);font-size:12px}\n#presentation-controls{display:none}\nbody.presenting{grid-template-rows:minmax(0,1fr);background:#111}\n.presenting footer,.presenting .filmstrip,.presenting #chat{display:none}\n.presenting .workspace{grid-template-columns:minmax(0,1fr)}.presenting #stage{padding:0;background:#111}.presenting #slide{box-shadow:none}\n.presenting #presentation-controls{display:flex;position:fixed;bottom:16px;left:50%;transform:translateX(-50%);align-items:center;gap:12px;background:#202735e8;color:white;padding:6px;border-radius:8px;opacity:0;transition:opacity .15s}\n.presenting #presentation-controls:hover,.presenting #presentation-controls:focus-within{opacity:1}\n.presenting.browse-scrollbar #stage{padding-right:18px}\n#presentation-scrollbar{position:fixed;z-index:4;top:0;bottom:0;right:0;width:16px;touch-action:none;border:1px solid #5d6575;background:#202735e8;cursor:pointer}\n#presentation-scrollbar span{position:absolute;left:3px;right:3px;min-height:8px;border-radius:5px;background:#c4c4c4;pointer-events:none}\n.kiosk-presenting #present-prev,.kiosk-presenting #present-next{display:none}\n#presentation-controls button{background:transparent;color:white;border-color:#5d6575;padding:5px 10px}\n#presentation-controls button:hover:not(:disabled){background:#ffffff1f}\n#present-note{max-width:38ch;color:#ffd9a8;font-size:11px;line-height:1.35}\n#chat{position:relative;min-width:0;min-height:0;display:flex;flex-direction:column;background:var(--ok-panel);color:var(--ok-text);border-left:1px solid var(--ok-border)}\n#chat-resizer{position:absolute;left:-4px;top:0;bottom:0;width:7px;z-index:3;cursor:col-resize;touch-action:none}#chat-resizer::after{content:\"\";position:absolute;left:3px;top:0;bottom:0;width:1px}#chat-resizer:hover::after,#chat-resizer:focus-visible::after,.resizing-chat #chat-resizer::after{background:var(--ok-selected-border)}#chat-resizer:focus-visible{outline:none}\nbody.resizing-chat,body.resizing-chat *{cursor:col-resize!important;user-select:none!important}\n.pane-head{display:flex;align-items:center;gap:8px;padding:10px 12px;border-bottom:1px solid var(--ok-border);background:var(--ok-panel);flex:none}\n.pane-head strong{font-size:13px;font-weight:600}\n.pane-close{margin-left:auto;padding:0 4px;border:none;background:none;color:var(--ok-text-2);font-size:20px;line-height:20px}\n.pane-close:hover:not(:disabled){background:var(--ok-hover);border-color:transparent;color:var(--ok-text)}\nbody.chat-hidden .workspace{grid-template-columns:var(--filmstrip-width) minmax(0,1fr)}body.chat-hidden #chat{display:none}\nbody.presenting .workspace{grid-template-columns:minmax(0,1fr)}\n@media(max-width:1000px){.workspace{--filmstrip-width:120px;--chat-width:380px}#stage{padding:16px}}\n@media(max-width:700px){#chat-resizer{display:none}.workspace{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(160px,1fr) minmax(200px,1fr)}.filmstrip{display:none}#chat{border-left:0;border-top:1px solid var(--ok-border)}body.chat-hidden .workspace,.presenting .workspace{grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr)}footer .hint,#status,.download{display:none}#stage{padding:16px}}\n`;\n\n/** The agent page renders inside one agent pane of the docked Agents task pane. */\nexport const agentStyles = `${tokenStyles}\nbody{display:flex;flex-direction:column;background:var(--ok-panel);height:100dvh;min-width:0;overflow:auto;overflow-x:hidden}\n.chat-heading{display:flex;align-items:center;flex-wrap:wrap;gap:6px;padding:6px 12px 2px;flex-shrink:0}\n.chat-heading select{flex:1;min-width:100px;font-size:12px}\n#chat-context{flex-shrink:0;margin:0;padding:4px 12px 6px;color:var(--ok-text-2);font-size:11px;border-bottom:1px solid var(--ok-border)}\n#claude-panel,#codex-panel{display:flex;flex-direction:column;flex:1;min-height:0}\n#claude-panel{min-height:100px}#codex-panel{min-height:240px}\n.terminal-toolbar{display:flex;align-items:center;gap:6px;padding:6px 12px;min-height:34px;font-size:11px;color:var(--ok-text-2)}\n.terminal-toolbar span{flex:1;overflow-wrap:anywhere}\n.terminal-toolbar button{font-size:11px;padding:2px 8px;border-color:var(--ok-border-strong)}\n.terminal-frame{display:flex;flex:1;min-height:0;min-width:0;padding:2px 4px 4px 12px;overflow:hidden}\n#terminal{flex:1;width:100%;min-width:0;min-height:0;overflow:hidden;padding:0}.xterm{height:100%}\n/* xterm's stylesheet paints the viewport black; the rows below the last full line show it. */\n#terminal .xterm-viewport{background-color:var(--ok-panel)!important}\n#messages{flex:1;overflow:auto;padding:12px;display:flex;flex-direction:column;gap:12px;overscroll-behavior:contain}\n.chat-message{white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.55}.chat-message.user{background:var(--ok-panel-2);border:1px solid var(--ok-border);border-radius:var(--ok-radius-lg);padding:8px 10px}\n.chat-message small{display:block;color:var(--ok-text-2);font-size:11px;margin-bottom:4px}.chat-intro{color:var(--ok-text-2);line-height:1.6;font-size:12px}\n#chat-form{padding:8px 12px;border-top:1px solid var(--ok-border)}\n#chat-input{resize:vertical;min-height:70px;max-height:200px;width:100%;padding:6px 8px;border:1px solid var(--ok-border-strong);border-radius:var(--ok-radius);background:var(--ok-panel);color:var(--ok-text)}\n#chat-input:focus{outline:2px solid var(--ok-selected-border);outline-offset:-1px}\n.chat-actions{display:flex;gap:6px;margin-top:6px;align-items:center}.chat-actions button{border-color:var(--ok-border-strong)}.chat-actions .primary{border-color:var(--ok-accent)}\n.chat-shortcut{flex:1;color:var(--ok-text-3);font-size:10px}\n#chat-status{padding:0 12px 8px;color:var(--ok-text-2);font-size:11px;white-space:pre-wrap;max-height:90px;overflow:auto}\n.markdown{white-space:normal;line-height:1.6}.markdown>:first-child{margin-top:0}.markdown>:last-child{margin-bottom:0}.markdown p{margin:0 0 .8em}\n.markdown pre{overflow:auto;padding:8px;background:var(--ok-panel-2);border:1px solid var(--ok-border);border-radius:var(--ok-radius);white-space:pre;font-family:var(--ok-mono)}\n.markdown code{font:.9em var(--ok-mono);background:var(--ok-panel-2);border-radius:3px;padding:1px 4px}.markdown pre code{padding:0}\n.markdown ul,.markdown ol{padding-left:22px}.markdown blockquote{margin-left:0;padding-left:12px;border-left:3px solid var(--ok-border);color:var(--ok-text-2)}\n.markdown table{display:block;overflow:auto;border-collapse:collapse}.markdown th,.markdown td{padding:6px 8px;border:1px solid var(--ok-border)}\n.markdown h1,.markdown h2,.markdown h3{font-size:1.1em}.markdown a{color:var(--ok-accent);text-decoration:underline}\n`;\n","import { previewI18nScript } from './preview-i18n.ts';\nimport { transitionScript } from './transition-script.ts';\nimport { previewStyles } from './styles.ts';\nexport const page = `<!doctype html>\n<html lang=\"en\">\n<meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\">\n<title>Presentation preview</title>\n<link rel=\"stylesheet\" href=\"/terminal.css\">\n<style>\n${previewStyles}\n.slide-edit-tools{display:flex;align-items:center;flex-wrap:wrap;gap:4px;padding:4px 12px;flex-shrink:0;background:var(--ok-ribbon);border-bottom:1px solid var(--ok-border)}.slide-edit-tools [data-undo],.slide-edit-tools [data-redo]{width:26px;height:26px;padding:0;display:grid;place-items:center;color:var(--ok-text-2)}.slide-edit-tools [data-undo]:hover:not(:disabled),.slide-edit-tools [data-redo]:hover:not(:disabled){color:var(--ok-text)}.slide-edit-tools span{font-size:11px;color:var(--ok-text-2);flex:1}.slide-edit-tools small{font-size:11px;color:var(--ok-accent)}.slide-hover{position:fixed;border:1px solid var(--ok-selected-border);pointer-events:none;z-index:19;border-radius:2px}.slide-text-input{position:fixed;z-index:22;resize:none;background:var(--ok-panel);color:var(--ok-text);border:2px solid var(--ok-selected-border);border-radius:var(--ok-radius);padding:0;line-height:1.15}.slide-selection{position:fixed;border:2px solid var(--ok-selected-border);background:color-mix(in srgb,var(--ok-selected-border) 10%,transparent);pointer-events:none;z-index:20}.slide-edit-panel{position:fixed;z-index:21;width:min(380px,calc(100vw - 16px));padding:12px;background:var(--ok-panel);color:var(--ok-text);border:1px solid var(--ok-border);border-radius:var(--ok-radius-lg);box-shadow:var(--ok-shadow-lg);display:flex;flex-direction:column;gap:10px}.selection-heading,.selection-actions{display:flex;align-items:center;gap:6px}.selection-heading strong{flex:1}.slide-edit-panel textarea{width:100%;min-height:90px;max-height:35vh;resize:vertical;background:var(--ok-panel);color:var(--ok-text);border:1px solid var(--ok-border-strong);border-radius:var(--ok-radius);padding:6px 8px}.slide-edit-panel textarea:focus{outline:2px solid var(--ok-selected-border);outline-offset:-1px}.selection-actions{flex-wrap:wrap}.selection-actions select{min-width:80px;flex:1}.selection-actions button{border-color:var(--ok-border-strong)}.slide-edit-panel small{color:var(--ok-text-2);line-height:1.5}.presenting .slide-edit-tools{display:none}\n.editing:not(.presenting) .slide-edit-tools{display:none}\n\n#agent-workspace{position:relative;flex:1;min-height:0;min-width:0;display:flex;overflow:auto}.agent-pane{position:absolute;display:flex;flex-direction:column;flex:1;min-width:0;min-height:0;overflow:hidden}.agent-pane iframe{border:0;width:100%;flex:1;min-height:0;background:var(--ok-panel)}.agent-tools{display:flex;gap:2px;align-items:center;padding:3px 6px 3px 12px;min-height:30px;background:var(--ok-panel-2);border-bottom:1px solid var(--ok-border)}.agent-tools span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;font-weight:600;color:var(--ok-text-2)}.agent-pane:focus-within .agent-tools span{color:var(--ok-text)}.agent-tools button{width:24px;height:24px;padding:0;display:grid;place-items:center;color:var(--ok-text-2)}.agent-tools button:hover:not(:disabled){color:var(--ok-text)}.agent-tools svg{width:14px;height:14px}.agent-divider{position:absolute;z-index:2;background:transparent;cursor:col-resize;touch-action:none}.agent-divider.vertical{cursor:row-resize}.agent-divider::after{content:\"\";position:absolute;background:var(--ok-border)}.agent-divider.horizontal::after{top:0;bottom:0;left:50%;width:1px}.agent-divider.vertical::after{left:0;right:0;top:50%;height:1px}.agent-divider:hover::after,.agent-divider:focus-visible::after{background:var(--ok-selected-border)}.agent-divider.horizontal:hover::after,.agent-divider.horizontal:focus-visible::after{width:2px}.agent-divider.vertical:hover::after,.agent-divider.vertical:focus-visible::after{height:2px}.agent-divider:focus-visible{outline:none}.splitting-agents iframe,.resizing-chat iframe{pointer-events:none}\n#editor-frame{display:none;border:0;width:100%;height:100%;flex:1;min-height:0}\nbody.editing:not(.presenting){grid-template-rows:minmax(0,1fr)}\n.editing:not(.presenting) .workspace{--filmstrip-width:0px;grid-template-columns:minmax(0,1fr) clamp(280px,var(--chat-width),calc(100vw - 500px))}\n.editing.chat-hidden:not(.presenting) .workspace{grid-template-columns:minmax(0,1fr)}\n.editing:not(.presenting) .filmstrip,.editing:not(.presenting) footer,.editing:not(.presenting) #stage{display:none}\n.editing:not(.presenting) #editor-frame{display:block}\n/* Too narrow to dock beside the editor: the pane floats over its right edge. */\n@media(max-width:900px){.editing:not(.presenting) .workspace{grid-template-columns:minmax(0,1fr)}.editing:not(.presenting) #chat{position:fixed;z-index:30;top:0;right:0;bottom:0;width:min(380px,100vw);box-shadow:var(--ok-shadow-lg)}.editing:not(.presenting) #chat-resizer{display:none}}\n</style>\n<body class=\"editing chat-hidden\">\n<div class=\"workspace\">\n<nav class=\"filmstrip\" aria-label=\"Slides\"><h2>Slides</h2><ol id=\"thumbnails\"></ol></nav>\n<main aria-label=\"Slide viewer\"><iframe id=\"editor-frame\" title=\"Presentation editor\"></iframe><pre id=\"error\" role=\"alert\" hidden></pre><div id=\"stage\" tabindex=\"-1\"><div id=\"empty\">Waiting for slides…</div><div id=\"slide\" hidden></div></div></main>\n<aside id=\"chat\" aria-labelledby=\"agents-title\">\n<div id=\"chat-resizer\" role=\"separator\" tabindex=\"0\" aria-label=\"Chat width\" aria-orientation=\"vertical\" aria-controls=\"chat\" title=\"Drag to resize · Double-click to reset\"></div>\n<div class=\"pane-head\"><strong id=\"agents-title\">Agents</strong><button id=\"close-chat\" class=\"pane-close\" aria-label=\"Close Agents\" title=\"Close Agents\">×</button></div><div id=\"agent-workspace\"></div><div id=\"chat-context\" hidden></div></aside>\n</div>\n<footer><span id=\"count\" aria-live=\"polite\">No slides</span><span id=\"status\" role=\"status\">Building…</span><span class=\"hint\">Select an area to ask AI · Edit text directly</span><button id=\"prev\" aria-label=\"Previous slide\" disabled>‹</button><button id=\"next\" aria-label=\"Next slide\" disabled>›</button><button id=\"present\" disabled>Present</button><button id=\"presenter\" disabled>Presenter view</button><a class=\"download\" href=\"/deck.pptx\">Download PPTX</a><button id=\"toggle-chat\" aria-pressed=\"false\" aria-controls=\"chat\"><svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M8 1.5 9.4 6.6 14.5 8 9.4 9.4 8 14.5 6.6 9.4 1.5 8 6.6 6.6Z\" stroke-linejoin=\"round\"/></svg><span>Agents</span></button><span class=\"status-views\" role=\"group\" aria-label=\"Presentation views\"><button id=\"toggle-editor\" aria-label=\"Normal\" title=\"Normal (Esc)\"><svg width=\"16\" height=\"12\" viewBox=\"0 0 16 12\" aria-hidden=\"true\"><rect x=\".5\" y=\".5\" width=\"15\" height=\"11\" rx=\"1\"/><path d=\"M5 1v10\"/></svg></button></span><label for=\"zoom\">Zoom</label><select id=\"zoom\"><option value=\"fit\">Fit</option><option value=\"0.5\">50%</option><option value=\"0.75\">75%</option><option value=\"1\">100%</option><option value=\"1.25\">125%</option><option value=\"1.5\">150%</option><option value=\"2\">200%</option></select></footer><div id=\"notice\" role=\"alert\" hidden></div>\n<div id=\"presentation-scrollbar\" role=\"scrollbar\" aria-label=\"Slide position\" aria-controls=\"slide\" aria-orientation=\"vertical\" aria-valuemin=\"0\" aria-valuemax=\"0\" aria-valuenow=\"0\" tabindex=\"0\" hidden><span></span></div><div id=\"presentation-controls\"><button id=\"present-prev\" aria-label=\"Previous slide\">‹</button><span id=\"present-count\"></span><span id=\"rehearsal-timer\" role=\"timer\" aria-label=\"Rehearsal time\" hidden></span><span id=\"present-note\" role=\"status\" hidden></span><button id=\"animation-retry\" hidden></button><button id=\"present-next\" aria-label=\"Next slide\">›</button><button id=\"exit-present\">Exit · Esc</button></div>\n<script>\nlet state={slides:[],error:null,aspectRatio:16/9,showProperties:null,customShows:[]},index=0,urls=[],presenting=false;\nlet showOrder=[],showCursor=0,lastViewed=null,linkedShowId=null;\nlet showReturns=[];\n// The fullscreen request of the show now starting. Starting or ending any show\n// (Present, presenter view, Escape) drops it, so a grant arriving after that\n// belongs to no show and is released instead of adopted.\nlet presentationFullscreen=false,fullscreenRequest=null;\nfunction loopShow(){return showReturns.length===0&&(state.showProperties?.loop||state.showProperties?.mode?.kind==='kiosk');}\nlet displayedSvg;\nlet presenterWindow;\n${previewI18nScript}\nfunction rebuildShowOrder(){\n const previousSlide=showOrder[showCursor];\n const settings=state.showProperties;\n let order;\n if(linkedShowId!==null){\n order=state.customShows?.find(show=>show.id===linkedShowId)?.slideIndices??[];\n }else if(settings?.slides?.kind==='range'){\n const start=Math.max(1,settings.slides.start),end=Math.min(state.slides.length,settings.slides.end);\n order=Array.from({length:Math.max(0,end-start+1)},(_,offset)=>start-1+offset);\n }else if(settings?.slides?.kind==='customShow'){\n order=state.customShows?.find(show=>show.id===settings.slides.id)?.slideIndices??[];\n }else order=Array.from({length:state.slides.length},(_,slide)=>slide);\n showOrder=order.filter(slide=>slide>=0&&slide<state.slides.length);\n if(previousSlide!==undefined){\n if(showOrder[showCursor]===previousSlide)return;\n const candidates=showOrder.map((slide,position)=>slide===previousSlide?position:-1).filter(position=>position>=0);\n if(candidates.length){showCursor=candidates.reduce((best,position)=>Math.abs(position-showCursor)<Math.abs(best-showCursor)?position:best,candidates[0]);return;}\n }\n const current=showOrder.indexOf(index);\n if(current>=0)showCursor=current;\n else showCursor=Math.min(showCursor,Math.max(0,showOrder.length-1));\n}\nfunction showSlideAt(position,step=1,skipHidden=presenting){\n if(!showOrder.length)return -1;\n let cursor=position;\n for(let n=0;n<showOrder.length;n++){\n if(cursor<0||cursor>=showOrder.length){\n if(loopShow())cursor=(cursor+showOrder.length)%showOrder.length;\n else return -1;\n }\n const candidate=showOrder[cursor];\n if(!skipHidden||!state.hiddenSlides?.[candidate])return candidate;\n cursor+=step;\n }\n return -1;\n}\nfunction firstShowSlide(){return showSlideAt(0,1,true);}\nfunction nextShowPosition(step){\n if(!showOrder.length)return -1;\n let cursor=showCursor+step;\n for(let n=0;n<showOrder.length;n++){\n if(cursor<0||cursor>=showOrder.length){\n if(loopShow())cursor=(cursor+showOrder.length)%showOrder.length;\n else return -1;\n }\n if(!presenting||!state.hiddenSlides?.[showOrder[cursor]])return cursor;\n cursor+=step;\n }\n return -1;\n}\nfunction nextShowSlide(step){\n const position=nextShowPosition(step);\n return position<0?-1:showOrder[position];\n}\nfunction browsePositions(){\n return showOrder.map((slide,position)=>({slide,position})).filter(({slide})=>!state.hiddenSlides?.[slide]);\n}\nfunction syncBrowseScrollbar(){\n const scrollbar=byId('presentation-scrollbar');\n const enabled=presenting&&state.showProperties?.mode?.kind==='browse'&&state.showProperties?.mode?.showScrollbar===true;\n scrollbar.setAttribute('aria-label',pt('Slide position'));\n scrollbar.hidden=!enabled;\n document.body.classList.toggle('browse-scrollbar',enabled);\n if(!enabled)return;\n const positions=browsePositions();\n const max=Math.max(0,positions.length-1);\n scrollbar.setAttribute('aria-valuemax',String(max));\n const current=positions.findIndex(({position})=>position===showCursor);\n const value=Math.max(0,current);\n scrollbar.setAttribute('aria-valuenow',String(value));\n const thumbPercent=positions.length?Math.max(8,100/positions.length):100;\n scrollbar.firstElementChild.style.height=thumbPercent+'%';\n scrollbar.firstElementChild.style.top=(max?(value/max)*(100-thumbPercent):0)+'%';\n scrollbar.setAttribute('aria-valuetext',positions.length?slideLabel(positions[value].slide):pt('No slides'));\n}\nfunction findSlide(start,step,skipHidden=presenting){\n if(skipHidden&&presenting)return showSlideAt(showCursor+step,step);\n for(let i=start;i>=0&&i<state.slides.length;i+=step)if(!skipHidden||!state.hiddenSlides?.[i])return i;\n return -1;\n}\nfunction moveSlide(step,skipHidden=presenting,focusThumbnail=false){\n // The slide has effects and the player is still on its way: moving on now\n // would skip them, so the click waits rather than costing the viewer content.\n if(animationsWaiting())return;\n // A build comes before the slide: the click that would move on first plays\n // whatever the current slide still has to show, in either direction.\n if(animationPlayer&&(step>0?animationPlayer.advance():animationPlayer.back()))return;\n const previousShowCursor=showCursor;\n const nextPosition=skipHidden&&presenting?nextShowPosition(step):-1;\n const next=skipHidden&&presenting?(nextPosition<0?-1:showOrder[nextPosition]):findSlide(index+step,step,skipHidden);\n // Arriving backwards lands on a slide that has already played out, the way\n // the reference desktop app shows it; arriving forwards starts its build from the top.\n if(next>=0){\n selectSlide(next,focusThumbnail,true,step<0?'end':'start',nextPosition);\n if(presenting&&step>0&&nextPosition>=0&&nextPosition<=previousShowCursor&&loopShow())scheduleKioskRestart();\n }\n else if(presenting&&step>0&&linkedShowId!==null)void exitPresentation();\n}\nlet advanceTimer,advanceKey;\nlet kioskRestartTimer,kioskRestartKey;\nfunction clearKioskRestart(){\n clearTimeout(kioskRestartTimer);kioskRestartTimer=undefined;kioskRestartKey=null;\n}\nfunction scheduleKioskRestart(){\n clearKioskRestart();\n const restart=state.showProperties?.mode?.kind==='kiosk'?Number(state.showProperties.mode.restart):NaN;\n // The OOXML attribute is an unsigned duration, but the schema does not\n // define a special meaning for zero. Do not turn an unconfigured/zero value\n // into a busy immediate restart loop; only a positive duration schedules it.\n if(!presenting||!Number.isFinite(restart)||restart<=0)return;\n const key={};kioskRestartKey=key;\n const deadline=performance.now()+restart;\n function tick(){\n if(kioskRestartKey!==key||!presenting)return;\n const remaining=deadline-performance.now();\n if(remaining>0){kioskRestartTimer=setTimeout(tick,Math.min(remaining,2147483647));return;}\n const first=firstShowSlide();\n if(first>=0){\n const position=showOrder.indexOf(first);\n clearTimeout(advanceTimer);advanceTimer=undefined;advanceKey=null;\n selectSlide(first,false,true,'start',position);\n scheduleKioskRestart();\n }else clearKioskRestart();\n }\n kioskRestartTimer=setTimeout(tick,Math.min(restart,2147483647));\n}\nfunction scheduleAdvance(){\n const delay=state.transitions?.[index]?.advanceAfterMs;\n // A slide that still has effects to play — or one whose last click is still\n // playing out — is not finished, whatever its transition says about\n // advancing itself.\n const settled=!animationsWaiting()&&(!animationPlayer||!(animationPlayer.pending||animationPlayer.running));\n const key=presenting&&state.showProperties?.useTimings!==false&&settled&&(nextShowSlide(1)>=0||linkedShowId!==null)&&Number.isFinite(delay)&&delay>=0?index+':'+showCursor+':'+delay:null;\n if(key===advanceKey)return;\n clearTimeout(advanceTimer);advanceKey=key;\n if(key===null)return;\n // OOXML unsigned milliseconds can exceed the browser's signed timer limit.\n const deadline=performance.now()+delay;\n function tick(){\n const remaining=deadline-performance.now();\n if(remaining>0)advanceTimer=setTimeout(tick,Math.min(remaining,2147483647));\n else {advanceKey=null;moveSlide(1);}\n }\n advanceTimer=setTimeout(tick,Math.min(delay,2147483647));\n}\nconst byId=id=>document.getElementById(id);\n// Next and previous move through the slide's own build before they move\n// through the deck, so the last slide's remaining effects are still reachable\n// and the first step back is the one inside this slide.\n// True while this slide has effects but the player has not arrived yet. Its\n// clicks belong to the build, so they are held rather than spent on the deck.\nfunction animationsWaiting(){return state.showProperties?.showAnimation!==false&&animationLoad==='loading'&&presenting&&animationStepsAt(index).length>0;}\nfunction animationsPending(){return state.showProperties?.showAnimation!==false&&(animationsWaiting()||Boolean(animationPlayer&&animationPlayer.pending));}\nfunction animationsBehind(){return Boolean(animationPlayer&&animationPlayer.cursor>0);}\nfunction updateNavigationButtons(){\n for(const id of ['prev','present-prev'])byId(id).disabled=presenting?nextShowSlide(-1)<0&&!animationsBehind():findSlide(index-1,-1)<0&&!animationsBehind();\n for(const id of ['next','present-next'])byId(id).disabled=presenting?nextShowSlide(1)<0&&linkedShowId===null&&!animationsPending():findSlide(index+1,1)<0&&!animationsPending();\n byId('present').disabled=firstShowSlide()<0;\n byId('presenter').disabled=byId('present').disabled;\n}\nconst stage=byId('stage'),slide=byId('slide'),thumbnails=byId('thumbnails');\nconst workspace=document.querySelector('.workspace'),chatResizer=byId('chat-resizer');\nconst chatWidthKey='office-kit-chat-width',minChatWidth=280,minStageWidth=240;\nfunction chatWidthLimits(){return {min:minChatWidth,max:Math.max(minChatWidth,workspace.clientWidth-document.querySelector('.filmstrip').getBoundingClientRect().width-minStageWidth)};}\nfunction updateChatWidthAria(){\n const {min,max}=chatWidthLimits();\n chatResizer.setAttribute('aria-valuemin',String(min));chatResizer.setAttribute('aria-valuemax',String(Math.round(max)));\n const width=Math.round(byId('chat').getBoundingClientRect().width);\n chatResizer.setAttribute('aria-valuenow',String(width));chatResizer.setAttribute('aria-valuetext',width+(previewLocale==='ja'?' ピクセル':' pixels'));\n}\nfunction setChatWidth(width){const {min,max}=chatWidthLimits();workspace.style.setProperty('--chat-width',Math.min(max,Math.max(min,width))+'px');}\nfunction saveChatWidth(){try{const width=workspace.style.getPropertyValue('--chat-width');if(width)localStorage.setItem(chatWidthKey,String(parseFloat(width)));else localStorage.removeItem(chatWidthKey);}catch(error){console.warn('Could not save chat width',error);}}\ntry{const saved=Number(localStorage.getItem(chatWidthKey));if(Number.isFinite(saved)&&saved>=minChatWidth)workspace.style.setProperty('--chat-width',saved+'px');}catch(error){console.warn('Could not restore chat width',error);}\nlet chatDrag=null;\nchatResizer.onpointerdown=event=>{\n if(event.button!==0||!event.isPrimary)return;\n event.preventDefault();chatResizer.focus();chatDrag={id:event.pointerId,x:event.clientX,width:byId('chat').getBoundingClientRect().width};\n chatResizer.setPointerCapture(event.pointerId);document.body.classList.add('resizing-chat');\n};\nchatResizer.onpointermove=event=>{if(chatDrag?.id===event.pointerId)setChatWidth(chatDrag.width+chatDrag.x-event.clientX);};\nfunction finishChatResize(){if(!chatDrag)return;chatDrag=null;document.body.classList.remove('resizing-chat');saveChatWidth();}\nchatResizer.onpointerup=finishChatResize;chatResizer.onpointercancel=finishChatResize;chatResizer.onlostpointercapture=finishChatResize;\nchatResizer.ondblclick=()=>{workspace.style.removeProperty('--chat-width');saveChatWidth();};\nchatResizer.onkeydown=event=>{\n const {min,max}=chatWidthLimits(),width=byId('chat').getBoundingClientRect().width,step=event.shiftKey?50:10;\n let next;\n if(event.key==='ArrowLeft')next=width+step;else if(event.key==='ArrowRight')next=width-step;else if(event.key==='Home')next=min;else if(event.key==='End')next=max;else return;\n event.preventDefault();event.stopPropagation();setChatWidth(next);saveChatWidth();\n};\nnew ResizeObserver(updateChatWidthAria).observe(byId('chat'));\n\n// The slide lives in a shadow root rather than a sandboxed iframe so its text\n// (XHTML inside <foreignObject>) can be selected and copied while the deck's\n// SVG stays out of the viewer's own DOM and CSS. Keyboard events still reach the\n// document, so arrow-key navigation keeps working after clicking into a slide.\nconst canvas=slide.attachShadow({mode:'open'});\n${transitionScript}\n// The slide's object animations. One player per slide, kept while the slide and\n// its timing stay as they are: an unrelated rebuild (new speaker notes, say)\n// must not throw away how far the viewer has clicked through a build.\n//\n// The player is the editor's module, served as one build, so the show, the\n// presenter window and the editor panel cannot disagree about a timing tree.\nlet animationPlayer,animationPlayerKey,createAnimationPlayer,pendingAnimationPosition;\n// 'loading' | 'ready' | 'failed'. A slide show holds its clicks while the\n// player is on its way; if it never arrives, the viewer is told so and can ask\n// for it again rather than watching a deck quietly skip its animations.\nlet animationLoad='loading',animationLoadAttempt=0;\nfunction loadAnimationPlayer(){\n if(animationLoad==='ready')return;\n animationLoad='loading';updateAnimationNotice();\n // A module that failed to load stays failed in the browser's module map, so\n // asking again means asking for a different URL.\n const url='/animation-player.js'+(animationLoadAttempt++?'?retry='+animationLoadAttempt:'');\n import(url).then(module=>{\n createAnimationPlayer=module.createAnimationPlayer;animationLoad='ready';\n const position=pendingAnimationPosition;pendingAnimationPosition=undefined;\n if(position!==undefined)syncAnimationPlayer(position);\n updateNavigationButtons();updateAnimationNotice();scheduleAdvance();\n },error=>{\n animationLoad='failed';console.warn('Could not load the animation player',error);\n updateNavigationButtons();updateAnimationNotice();scheduleAdvance();\n });\n}\nconst animationStepsAt=i=>state.animations?.[i]??[];\nconst animationKeyAt=i=>i+'\\u0000'+(state.slides[i]??'')+'\\u0000'+JSON.stringify(animationStepsAt(i));\n// A transition draws the arriving slide into a layer of its own, beside a layer\n// holding the slide being left — whose shapes carry ids of their own, from a\n// different slide's id space. Only the arriving one is this slide.\nconst slideRoot=()=>canvas.querySelector('.transition-layer:not(.transition-old)')??canvas;\nlet mediaPlayer,mediaPlayerKey,mediaPlayerConfigKey,createMediaPlayer,mediaLoading=false,mediaGeneration=0;\nconst backgroundMedia=[];\nconst backgroundMediaRoot=document.createElement('div');\nbackgroundMediaRoot.hidden=true;\nstage.append(backgroundMediaRoot);\nconst mediaRetry=document.createElement('button');\nmediaRetry.hidden=true;\nbyId('presentation-controls').append(mediaRetry);\nmediaRetry.onclick=()=>syncMediaPlayer();\nfunction disposeCurrentMediaPlayer(){\n mediaPlayer?.dispose();mediaPlayer=undefined;mediaPlayerKey=undefined;mediaPlayerConfigKey=undefined;\n}\nfunction disposeMediaPlayer(){\n disposeCurrentMediaPlayer();\n for(const retained of backgroundMedia)retained.player.dispose();\n backgroundMedia.length=0;\n}\nfunction leaveMediaSlide(previousIndex,previousCursor){\n if(!presenting){disposeMediaPlayer();return;}\n if(mediaPlayer){backgroundMedia.push({player:mediaPlayer,index:previousIndex,cursor:previousCursor});mediaPlayer=undefined;mediaPlayerKey=undefined;mediaPlayerConfigKey=undefined;}\n for(let i=backgroundMedia.length-1;i>=0;i--){\n const retained=backgroundMedia[i];\n if(index===retained.index||!retained.player.retainAcrossSlides(showCursor-retained.cursor,backgroundMediaRoot)){\n retained.player.dispose();backgroundMedia.splice(i,1);\n }\n }\n}\nfunction syncMediaPlayer(){\n const clips=(state.media??[]).filter(clip=>clip.slideIndex===index&&clip.kind!=='online');\n if(!presenting){disposeMediaPlayer();mediaRetry.hidden=true;return;}\n if(!clips.length){disposeCurrentMediaPlayer();mediaRetry.hidden=true;return;}\n if(!createMediaPlayer){\n if(mediaLoading)return;\n mediaLoading=true;mediaRetry.hidden=true;\n import('/media-player.js?attempt='+Date.now()).then(module=>{\n createMediaPlayer=module.createMediaPlayer;mediaLoading=false;syncMediaPlayer();\n },error=>{\n mediaLoading=false;console.warn('Could not load the media player',error);\n mediaRetry.textContent=previewLocale==='ja'?'メディアを読み込めませんでした · 再試行':'Media could not load · Retry';\n mediaRetry.hidden=!presenting;\n });\n return;\n }\n const key=JSON.stringify([index,clips]);\n if(mediaPlayerConfigKey===key)return;\n disposeCurrentMediaPlayer();\n mediaPlayer=createMediaPlayer({root:slideRoot(),overlayRoot:stage,clips,locale:previewLocale});\n mediaPlayerConfigKey=key;\n mediaPlayerKey=String(++mediaGeneration)+':'+key;\n updatePresenter();\n}\nfunction updateAnimationNotice(){\n const note=byId('present-note'),retry=byId('animation-retry');\n const slideHasEffects=presenting&&state.showProperties?.showAnimation!==false&&animationStepsAt(index).length>0;\n retry.hidden=!(slideHasEffects&&animationLoad==='failed');\n retry.textContent=previewLocale==='ja'?'再試行':'Try again';\n if(slideHasEffects&&animationLoad==='failed'){\n note.hidden=false;\n note.textContent=previewLocale==='ja'\n ?'アニメーションを読み込めませんでした。このスライドの効果は再生されません。'\n :'The animations could not be loaded, so this slide’s effects are not played.';\n return;\n }\n if(animationsWaiting()){\n note.hidden=false;\n note.textContent=previewLocale==='ja'?'アニメーションを準備中…':'Preparing animations…';\n return;\n }\n const skipped=animationPlayer?animationPlayer.unsupported:[];\n note.hidden=skipped.length===0;\n note.textContent=skipped.length===0?'':previewLocale==='ja'\n ?'このスライドの '+skipped.length+' 件のアニメーションは未対応のため再生されません'\n :skipped.length===1\n ?'1 animation on this slide is not supported yet, so it is not played'\n :skipped.length+' animations on this slide are not supported yet, so they are not played';\n}\nfunction syncAnimationPlayer(position){\n // Animations belong to the slide show. The still preview shows the slide as\n // it ends, which is what the renderer drew.\n const wanted=presenting&&state.showProperties?.showAnimation!==false&&animationStepsAt(index).length>0;\n if(!wanted){\n if(animationPlayer){animationPlayer.dispose();animationPlayer=undefined;animationPlayerKey=undefined;}\n updateAnimationNotice();\n return;\n }\n if(!createAnimationPlayer){\n // Still on its way, or it never arrived. The slide keeps the picture the\n // renderer drew; this position is applied the moment the player is there.\n pendingAnimationPosition=position;\n updateAnimationNotice();\n return;\n }\n const key=animationKeyAt(index);\n if(!animationPlayer||animationPlayerKey!==key){\n // A changed timing makes the old cursor meaningless — the effects it\n // counted are not the effects there are now.\n animationPlayer?.dispose();\n animationPlayer=createAnimationPlayer({\n root:slideRoot,\n steps:animationStepsAt(index),\n reducedMotion:()=>reducedMotion.matches,\n onChange:()=>{updateNavigationButtons();updatePresenter();scheduleAdvance();},\n });\n animationPlayerKey=key;\n position==='end'?animationPlayer.finish():animationPlayer.reset();\n updateAnimationNotice();\n return;\n }\n if(position==='end')animationPlayer.finish();\n else if(position==='start')animationPlayer.reset();\n // 'keep' leaves the cursor where the viewer left it.\n updateAnimationNotice();\n}\nfunction resize(){\n if(!state.slides.length)return;\n const style=getComputedStyle(stage);\n const width=Math.max(1,stage.clientWidth-parseFloat(style.paddingLeft)-parseFloat(style.paddingRight));\n const height=Math.max(1,stage.clientHeight-parseFloat(style.paddingTop)-parseFloat(style.paddingBottom));\n const ratio=state.aspectRatio;\n const zoom=byId('zoom').value;\n const slideWidth=presenting||zoom==='fit'?Math.min(width,height*ratio):1280*Number(zoom);\n slide.style.width=slideWidth+'px';slide.style.height=slideWidth/ratio+'px';\n}\nfunction selectSlide(next,focusThumbnail=false,reveal=true,position='start',showPosition=-1){\n const previousIndex=index,previousCursor=showCursor;\n index=Math.max(0,Math.min(next,state.slides.length-1));\n if(showPosition>=0)showCursor=showPosition;\n else if(presenting){if(showOrder[showCursor]!==index){const naturalPosition=showOrder.indexOf(index);if(naturalPosition>=0)showCursor=naturalPosition;}}\n else {const naturalPosition=showOrder.indexOf(index);if(naturalPosition>=0)showCursor=naturalPosition;}\n if(presenting&&state.hiddenSlides?.[index]){\n const forwardPosition=nextShowPosition(1),backwardPosition=forwardPosition<0?nextShowPosition(-1):-1;\n const position=forwardPosition>=0?forwardPosition:backwardPosition;\n if(position>=0){showCursor=position;index=showOrder[position];}\n }\n if(presenting&&(previousIndex!==index||previousCursor!==showCursor))lastViewed={index:previousIndex,cursor:previousCursor};\n const count=slideCount();\n byId('chat-context').textContent=state.slides.length?count:pt('Whole project');\n byId('chat-context').dataset.focus=JSON.stringify({slide:state.slides.length?index:null,revision:state.revision??0});\n window.dispatchEvent(new Event('agent-focus'));\n if(document.body.classList.contains('editing')&&editorFocus)applyEditorFocus();\n byId('count').textContent=count;byId('present-count').textContent=count;\n document.body.classList.toggle('kiosk-presenting',presenting&&state.showProperties?.mode?.kind==='kiosk');\n byId('zoom').disabled=!state.slides.length;\n slide.hidden=!state.slides.length;byId('empty').hidden=!!state.slides.length;\n const svg=state.slides[index];\n if(previousIndex!==index||previousCursor!==showCursor)leaveMediaSlide(previousIndex,previousCursor);\n else if(svg!==displayedSvg||position!=='keep')disposeMediaPlayer();\n if(svg!==displayedSvg||previousIndex!==index){\n renderSlide(svg,presenting&&previousIndex!==index?state.transitions?.[index]:null);\n }\n syncAnimationPlayer(position);\n syncMediaPlayer();\n updateNavigationButtons();\n slide.setAttribute('aria-label',slideLabel(index));\n for(const [position,item] of Array.from(thumbnails.children).entries()){\n const button=item.firstElementChild,selected=position===index;\n button.setAttribute('aria-current',String(selected));button.tabIndex=selected?0:-1;\n button.dataset.skipped=String(!!state.hiddenSlides?.[position]);\n button.title=state.hiddenSlides?.[position]?pt('Skipped during presentation'):'';\n if(selected){if(reveal)button.scrollIntoView({block:'nearest'});if(focusThumbnail)button.focus({preventScroll:true});}\n }\n syncBrowseScrollbar();\n resize();\n if(!transitionCleanup)scheduleAdvance();\n updatePresenter();\n}\nfunction update(updated){\n const focusedThumbnail=thumbnails.contains(document.activeElement);\n const previous=state;\n if(JSON.stringify(previous.media)!==JSON.stringify(updated.media))disposeMediaPlayer();\n state=updated;\n rebuildShowOrder();\n connectionLost=false;updatePreviewStatus();\n byId('error').textContent=state.error||'';byId('error').hidden=!state.error;\n document.documentElement.style.setProperty('--slide-ratio',String(state.aspectRatio));\n for(let i=state.slides.length;i<urls.length;i++){\n URL.revokeObjectURL(urls[i]);thumbnails.lastElementChild.remove();\n }\n urls.length=state.slides.length;\n state.slides.forEach((svg,i)=>{\n if(svg===previous.slides[i])return;\n const oldUrl=urls[i];\n urls[i]=URL.createObjectURL(new Blob([svg],{type:'image/svg+xml'}));\n let item=thumbnails.children[i];\n if(!item){\n item=document.createElement('li');\n const button=document.createElement('button'),number=document.createElement('span'),image=document.createElement('img');\n button.className='thumbnail';button.setAttribute('aria-label',slideLabel(i));button.onclick=()=>selectSlide(i,true);\n number.className='slide-number';number.textContent=String(i+1);\n image.alt='';image.draggable=false;image.loading='lazy';\n button.append(number,image);item.append(button);thumbnails.append(item);\n }\n item.querySelector('img').src=urls[i];\n if(oldUrl)URL.revokeObjectURL(oldUrl);\n });\n if(presenting&&firstShowSlide()<0)void exitPresentation();\n selectSlide(index,focusedThumbnail,false,'keep');\n if(presenting&&(previous.showProperties?.mode?.kind!==state.showProperties?.mode?.kind||previous.showProperties?.mode?.restart!==state.showProperties?.mode?.restart))scheduleKioskRestart();\n}\nfunction setPresenting(value,from){\n if(!value){presentationFullscreen=false;finishRehearsal();}\n fullscreenRequest=null;\n clearKioskRestart();\n linkedShowId=null;showReturns=[];\n lastViewed=null;\n cancelTransition();\n rebuildShowOrder();\n presenting=value&&firstShowSlide()>=0;document.body.classList.toggle('presenting',presenting);\n if(presenting){const first=from??firstShowSlide();if(first>=0){index=first;showCursor=showOrder.findIndex(slide=>slide===first);}}\n selectSlide(index);\n if(presenting)scheduleKioskRestart();\n if(presenting)stage.focus();else{\n byId('present').focus();\n thumbnails.children[index]?.firstElementChild.scrollIntoView({block:'nearest'});\n }\n}\nfunction returnToLastViewed(){\n if(!presenting||!lastViewed||lastViewed.index>=state.slides.length)return;\n const target=lastViewed;\n selectSlide(target.index,false,true,'start',showOrder[target.cursor]===target.index?target.cursor:-1);\n}\nfunction followNavigationLink(href){\n const directions={'#pptx-next-slide':1,'#pptx-prev-slide':-1,'#pptx-first-slide':0,'#pptx-last-slide':0};\n if(!Object.hasOwn(directions,href))return false;\n const step=directions[href];\n let position=-1,target=-1;\n if(step){\n position=presenting?nextShowPosition(step):-1;\n target=presenting?(position>=0?showOrder[position]:-1):findSlide(index+step,step,true);\n }else{\n const first=href==='#pptx-first-slide';\n target=presenting?showSlideAt(first?0:showOrder.length-1,first?1:-1):findSlide(first?0:state.slides.length-1,first?1:-1,true);\n if(presenting)position=first?showOrder.indexOf(target):showOrder.lastIndexOf(target);\n }\n if(target>=0)selectSlide(target,false,true,'start',position);\n else if(presenting&&step>0)void exitPresentation();\n return true;\n}\nfunction launchCustomShow(id,returnToShow){\n if(!presenting)return;\n const show=state.customShows?.find(show=>show.id===id);\n const first=show?.slideIndices.find(slide=>slide>=0&&slide<state.slides.length&&!state.hiddenSlides?.[slide]);\n if(first===undefined)return;\n const caller={id:linkedShowId,index,cursor:showCursor,lastViewed,animation:animationPlayer?.progress};\n if(returnToShow)showReturns.push(caller);else showReturns=[];\n linkedShowId=id;showCursor=0;lastViewed=null;\n rebuildShowOrder();\n selectSlide(first,false,true,'start',showOrder.indexOf(first));\n lastViewed=null;\n}\nfunction followCustomShowLink(href){\n if(!href.startsWith('#pptx-custom-show?'))return false;\n const params=new URLSearchParams(href.slice('#pptx-custom-show?'.length));\n const id=params.get('id');\n if(id!==null&&/^[0-9]+$/.test(id))launchCustomShow(Number(id),params.get('return')==='true');\n return true;\n}\n// Slide Show ▸ Rehearse Timings: time each slide while presenting, then hand\n// the times to the editor, which asks whether to keep them as slide timings.\nlet rehearsal=null;\nconst clock=ms=>{const total=Math.floor(ms/1000),two=n=>String(n).padStart(2,'0');return Math.floor(total/3600)+':'+two(Math.floor(total/60)%60)+':'+two(total%60);};\nfunction rehearsalTick(){\n if(!rehearsal)return;\n const now=performance.now();\n // The show's cursor names the slide on screen; a custom show can repeat one.\n const showing=showOrder[showCursor]??index;\n if(showing!==rehearsal.slide){rehearsal.times.set(rehearsal.slide,(rehearsal.times.get(rehearsal.slide)??0)+now-rehearsal.since);rehearsal.slide=showing;rehearsal.since=now;}\n byId('rehearsal-timer').textContent=clock(now-rehearsal.since)+' · '+clock(now-rehearsal.started);\n}\nfunction startRehearsal(){\n const now=performance.now();\n rehearsal={times:new Map(),slide:showOrder[showCursor]??index,since:now,started:now,timer:setInterval(rehearsalTick,200)};\n byId('rehearsal-timer').hidden=false;rehearsalTick();\n}\nfunction finishRehearsal(){\n if(!rehearsal)return;\n rehearsalTick();\n const now=performance.now();\n rehearsal.times.set(rehearsal.slide,(rehearsal.times.get(rehearsal.slide)??0)+now-rehearsal.since);\n clearInterval(rehearsal.timer);byId('rehearsal-timer').hidden=true;\n const timings=[...rehearsal.times].map(([slide,ms])=>({slide,ms:Math.round(ms)}));\n rehearsal=null;\n editorFrame.contentWindow?.postMessage({type:'rehearsal-timings',timings},location.origin);\n}\nasync function exitPresentation(){\n if(presenting&&showReturns.length){\n const caller=showReturns.pop();\n linkedShowId=caller.id;showCursor=caller.cursor;\n rebuildShowOrder();\n selectSlide(caller.index,false,true,'start',caller.cursor);\n lastViewed=caller.lastViewed;\n if(caller.animation&&animationPlayer)animationPlayer.resume(caller.animation.cursor,caller.animation.elapsed);\n return;\n }\n setPresenting(false);\n if(document.fullscreenElement)await document.exitFullscreen();\n}\nasync function startPresentation(from){\n setPresenting(true,from);\n if(state.showProperties?.mode?.kind==='browse')return;\n const request=fullscreenRequest={};\n try{await document.documentElement.requestFullscreen();}\n catch{if(fullscreenRequest===request){fullscreenRequest=null;byId('exit-present').textContent=pt('Exit view · Esc');}}\n}\nbyId('present').onclick=()=>startPresentation();\n// Messages that used to sit in the shell's header now float over whichever\n// view is showing, so they are not lost while the editor fills the window.\nconst NOTICE_MS=6000;\nlet noticeTimer;\nfunction showNotice(text){const notice=byId('notice');notice.textContent=text;notice.hidden=false;clearTimeout(noticeTimer);noticeTimer=setTimeout(()=>{notice.hidden=true;},NOTICE_MS);}\nfunction updatePresenter(){\n if(!presenterWindow||presenterWindow.closed)return;\n // The presenter runs the same player over its own copy of the slide, from the\n // same cursor and the same moment within the stop in hand, so what it shows\n // is what the audience sees — including an effect still fading in.\n const progress=animationPlayer?animationPlayer.progress:null;\n const animationSteps=animationPlayer?animationStepsAt(index):null;\n presenterWindow.postMessage({type:'presenter-state',index,count:state.slides.length,animationSteps,current:state.slides[index]??null,next:state.slides[nextShowSlide(1)]??null,hasPrevious:nextShowSlide(-1)>=0||animationsBehind(),hasNext:nextShowSlide(1)>=0||linkedShowId!==null||animationsPending(),animation:progress,notes:state.notes?.[index]??'',aspectRatio:state.aspectRatio,locale:previewLocale,presenting,media:presenting?(state.media??[]).filter(clip=>clip.slideIndex===index):[],mediaKey:mediaPlayerKey,mediaProgress:mediaPlayer?.progress??[]},location.origin);\n}\nsetInterval(()=>{\n if(!presenterWindow||presenterWindow.closed||!mediaPlayer)return;\n presenterWindow.postMessage({type:'presenter-media-state',index,mediaKey:mediaPlayerKey,mediaProgress:mediaPlayer.progress},location.origin);\n},250);\nbyId('presenter').onclick=()=>{\n if(presenterWindow&&!presenterWindow.closed){setPresenting(true);presenterWindow.focus();return;}\n presenterWindow=window.open('/presenter','office-kit-presenter','popup,width=1100,height=800');\n if(presenterWindow)setPresenting(true);\n else showNotice(pt('Allow popups to open presenter view.'));\n};\nwindow.addEventListener('message',event=>{\n if(event.origin!==location.origin||event.source!==presenterWindow||event.data?.type!=='presenter-command')return;\n if(event.data.action==='ready')updatePresenter();\n else if(event.data.action==='media'){\n const command=event.data.index;\n if(!presenting||!mediaPlayer||event.data.mediaKey!==mediaPlayerKey||!command||!Number.isInteger(command.shapeId)||!['play','pause','seek'].includes(command.action))return;\n if(command.action==='seek'&&(!Number.isFinite(command.time)||command.time<0))return;\n mediaPlayer.command(command);updatePresenter();\n }\n else if(event.data.action==='next')moveSlide(1,true);\n else if(event.data.action==='previous')moveSlide(-1,true);\n else if(event.data.action==='exit')void exitPresentation();\n else if(event.data.action==='lastViewed')returnToLastViewed();\n else if(event.data.action==='navigation'&&typeof event.data.index==='string')followNavigationLink(event.data.index);\n else if(event.data.action==='customShow'&&typeof event.data.index==='string')followCustomShowLink(event.data.index);\n else if(event.data.action==='jump'&&Number.isInteger(event.data.index)&&event.data.index>=0&&event.data.index<state.slides.length)selectSlide(event.data.index);\n});\nbyId('exit-present').onclick=exitPresentation;\nfunction scrollToBrowsePosition(value){\n const positions=browsePositions();\n const position=positions[Math.max(0,Math.min(positions.length-1,value))];\n if(position&&position.position!==showCursor)selectSlide(position.slide,false,true,'start',position.position);\n}\nconst browseScrollbar=byId('presentation-scrollbar');\nlet scrollbarDragOffset;\nfunction dragBrowseScrollbar(event){\n const rect=browseScrollbar.getBoundingClientRect();\n const thumb=browseScrollbar.firstElementChild.getBoundingClientRect();\n const travel=rect.height-thumb.height;\n if(travel<=0)return;\n const max=Number(browseScrollbar.getAttribute('aria-valuemax'));\n scrollToBrowsePosition(Math.round(((event.clientY-rect.top-scrollbarDragOffset)/travel)*max));\n}\nbrowseScrollbar.onpointerdown=event=>{\n if(event.button!==0)return;\n event.preventDefault();browseScrollbar.focus();\n const thumb=browseScrollbar.firstElementChild.getBoundingClientRect();\n scrollbarDragOffset=event.clientY>=thumb.top&&event.clientY<=thumb.bottom?event.clientY-thumb.top:thumb.height/2;\n browseScrollbar.setPointerCapture(event.pointerId);\n dragBrowseScrollbar(event);\n};\nbrowseScrollbar.onpointermove=event=>{\n if(browseScrollbar.hasPointerCapture(event.pointerId))dragBrowseScrollbar(event);\n};\nbrowseScrollbar.onpointerup=event=>{\n if(browseScrollbar.hasPointerCapture(event.pointerId))browseScrollbar.releasePointerCapture(event.pointerId);\n};\nbrowseScrollbar.onkeydown=event=>{\n const current=Number(browseScrollbar.getAttribute('aria-valuenow'));\n const max=Number(browseScrollbar.getAttribute('aria-valuemax'));\n let next;\n if(event.key==='ArrowDown'||event.key==='PageDown')next=current+1;\n else if(event.key==='ArrowUp'||event.key==='PageUp')next=current-1;\n else if(event.key==='Home')next=0;\n else if(event.key==='End')next=max;\n else return;\n event.preventDefault();event.stopPropagation();scrollToBrowsePosition(next);\n};\nbyId('animation-retry').onclick=()=>{loadAnimationPlayer();};\nloadAnimationPlayer();\ndocument.addEventListener('fullscreenchange',()=>{\n // Only the page itself is ever requested here. A native video control can put\n // its <video> into fullscreen (and return to the page) on its own; that is the\n // viewer's choice, not a stale grant.\n if(document.fullscreenElement){\n if(document.fullscreenElement!==document.documentElement||presentationFullscreen)return;\n if(fullscreenRequest){fullscreenRequest=null;presentationFullscreen=true;}\n else void document.exitFullscreen();\n }\n else if(presentationFullscreen){presentationFullscreen=false;if(presenting)setPresenting(false);}\n});\nfor(const id of ['prev','present-prev'])byId(id).onclick=()=>{if(!presenting||state.showProperties?.mode?.kind!=='kiosk')moveSlide(-1);};\nfor(const id of ['next','present-next'])byId(id).onclick=()=>{if(!presenting||state.showProperties?.mode?.kind!=='kiosk')moveSlide(1);};\nbyId('zoom').onchange=resize;\nstage.onclick=event=>{\n const link=event.composedPath().find(node=>node instanceof Element&&node.localName==='a');\n if(link){\n const href=link.getAttribute('href')??link.getAttributeNS('http://www.w3.org/1999/xlink','href')??'';\n if(followNavigationLink(href)||followCustomShowLink(href)){event.preventDefault();return;}\n if(href==='#pptx-end-show'||href==='#pptx-last-slide-viewed'){\n event.preventDefault();\n if(!presenting)return;\n if(href==='#pptx-end-show'){void exitPresentation();return;}\n returnToLastViewed();\n return;\n }\n if(href.startsWith('#slide-')){\n event.preventDefault();\n const number=Number(href.slice(7));\n if(Number.isInteger(number)&&number>=1&&number<=state.slides.length)selectSlide(number-1);\n }\n return;\n }\n if(!presenting||state.showProperties?.mode?.kind==='kiosk'||getSelection().toString())return;\n // 'advClick' says whether a click moves to the next *slide*. A build still\n // belongs to this one, so its remaining effects play either way.\n if(animationsPending()||state.transitions?.[index]?.advanceOnClick!==false)moveSlide(1);\n};\ndocument.addEventListener('keydown',event=>{\n if(event.key==='Escape'&&presenting){event.preventDefault();void exitPresentation();return;}\n // As in the reference desktop app's Reading View, Esc goes back to Normal view, unless it\n // is closing the viewer's own text field or area selection first.\n if(event.key==='Escape'&&!document.body.classList.contains('editing')&&!event.target.closest('select,input,textarea,[contenteditable]')&&document.querySelector('.slide-edit-panel')?.hidden!==false){event.preventDefault();chooseView(true);return;}\n if(presenting&&state.showProperties?.mode?.kind==='kiosk')return;\n if(event.altKey||event.ctrlKey||event.metaKey||event.target.closest('select,input,textarea,[contenteditable]'))return;\n const focusThumbnail=thumbnails.contains(document.activeElement);\n let step=0,jump=-1,jumpPosition=-1;\n if(['ArrowLeft','ArrowUp','PageUp'].includes(event.key))step=-1;\n else if(['ArrowRight','ArrowDown','PageDown'].includes(event.key))step=1;\n else if(event.key==='Home'){\n jump=presenting?firstShowSlide():findSlide(0,1);\n if(presenting)jumpPosition=showOrder.findIndex(slide=>slide===jump&&!state.hiddenSlides?.[slide]);\n }\n else if(event.key==='End'){\n jump=presenting?showSlideAt(showOrder.length-1,-1):findSlide(state.slides.length-1,-1);\n if(presenting)jumpPosition=showOrder.findLastIndex(slide=>slide===jump&&!state.hiddenSlides?.[slide]);\n }\n else if(event.key===' '&&presenting&&!event.target.closest('button,a'))step=event.shiftKey?-1:1;\n else return;\n event.preventDefault();\n // Home and End are a jump, not a step: they land on a slide with its build\n // at the start rather than walking through the one in hand.\n if(step!==0)moveSlide(step,presenting,focusThumbnail);\n else if(jump>=0)selectSlide(jump,focusThumbnail,true,'start',jumpPosition);\n});\nnew ResizeObserver(resize).observe(stage);\nlet refreshId=0;\nasync function refresh(){\n const id=++refreshId;\n try{const response=await fetch('/state'+(state.revision===undefined?'':'?since='+state.revision));if(!response.ok)throw new Error('Preview unavailable');const updated=await response.json();if(id===refreshId){if(updated.changes){updated.slides=state.slides.slice(0,updated.count);updated.slides.length=updated.count;for(const [position,svg] of Object.entries(updated.changes))updated.slides[Number(position)]=svg;}update(updated);}}\n catch{if(id===refreshId){connectionLost=true;updatePreviewStatus();}}\n}\nconst editorFrame=byId('editor-frame');\nlet editorFocus;\n// The preview revision the editor last saw; it reports this apart from its focus.\nlet editorRevision=0;\n// The Agents task pane opens on demand, like the reference desktop app's panes; the editor's\n// Agents button (in its tab row) mirrors that state, so every change is reported back.\nconst agentsKey='office-kit-agents-open';\nfunction agentsOpen(){return !document.body.classList.contains('chat-hidden');}\nfunction postAgentsState(focusButton=false){editorFrame.contentWindow?.postMessage({type:'host-panes',agents:agentsOpen(),focus:focusButton},location.origin);}\nfunction setAgentsOpen(open,{focusPane=false,returnFocus=false}={}){\n document.body.classList.toggle('chat-hidden',!open);\n byId('toggle-chat').setAttribute('aria-pressed',String(open));\n try{sessionStorage.setItem(agentsKey,open?'1':'0');}catch(error){console.warn('Could not save the Agents pane state',error);}\n postAgentsState(returnFocus&&document.body.classList.contains('editing'));\n resize();\n if(open&&focusPane)byId('close-chat').focus();\n else if(returnFocus){if(document.body.classList.contains('editing'))editorFrame.focus();else byId('toggle-chat').focus();}\n}\nbyId('toggle-chat').onclick=()=>setAgentsOpen(!agentsOpen());\nbyId('close-chat').onclick=()=>setAgentsOpen(false,{returnFocus:true});\ntry{if(sessionStorage.getItem(agentsKey)==='1')setAgentsOpen(true);}catch(error){console.warn('Could not restore the Agents pane state',error);}\nfunction setEditorMode(editing){\n document.body.classList.toggle('editing',editing);\n if(editing&&!editorFrame.getAttribute('src'))editorFrame.src='/editor';\n if(editing&&editorFocus)applyEditorFocus();else selectSlide(index);\n resize();\n}\n// The rendered viewer is the editor's Reading View; Normal (or Esc) returns.\nfunction chooseView(editing){\n setEditorMode(editing);\n try{sessionStorage.setItem('office-kit-view',editing?'editor':'preview');}catch(error){console.warn('Could not save view preference',error);}\n if(editing)editorFrame.focus();else stage.focus();\n}\nbyId('toggle-editor').onclick=()=>chooseView(true);\ntry{setEditorMode(sessionStorage.getItem('office-kit-view')!=='preview');}catch(error){console.warn('Could not restore view preference',error);setEditorMode(true);}\nfunction applyEditorFocus(){\n if(!editorFocus)return;\n index=Math.max(0,editorFocus.slide);\n byId('chat-context').textContent=(editorFocus.locale==='ja'?'スライド ':'Slide ')+(index+1)+' / '+editorFocus.count+(editorFocus.dirty?' · '+(editorFocus.locale==='ja'?'未保存':'Unsaved'):'');\n byId('chat-context').dataset.focus=JSON.stringify({slide:editorFocus.count?index:null,revision:editorFocus.dirty?-1:editorRevision});\n window.dispatchEvent(new Event('agent-focus'));\n}\nwindow.addEventListener('message',event=>{\n if(event.origin!==location.origin||event.source!==editorFrame.contentWindow||event.data?.type!=='editor-command')return;\n const slide=event.data.slide;\n if(event.data.action==='agents'){setAgentsOpen(!agentsOpen(),{focusPane:true});return;}\n if(event.data.action==='reading'){\n if(Number.isInteger(slide)&&slide>=0&&slide<state.slides.length)index=slide;\n chooseView(false);\n return;\n }\n // The editor's Slide Show tab; the click's user activation reaches this page,\n // so fullscreen and the presenter popup are still allowed.\n if(byId('present').disabled)return;\n if(event.data.action==='start')void startPresentation();\n else if(event.data.action==='current'&&Number.isInteger(slide)&&slide>=0&&slide<state.slides.length)void startPresentation(slide);\n else if(event.data.action==='presenter')byId('presenter').click();\n else if(event.data.action==='rehearse'){void startPresentation();if(presenting)startRehearsal();}\n});\nwindow.addEventListener('message',event=>{\n if(event.origin!==location.origin||event.source!==editorFrame.contentWindow||event.data?.type!=='editor-revision'||!Number.isInteger(event.data.revision))return;\n editorRevision=event.data.revision;\n if(document.body.classList.contains('editing'))applyEditorFocus();\n});\nwindow.addEventListener('message',event=>{\n if(event.origin!==location.origin||event.source!==editorFrame.contentWindow||event.data?.type!=='editor-focus')return;\n editorFocus=event.data;\n if(previewLocale!==editorFocus.locale&&(editorFocus.locale==='ja'||editorFocus.locale==='en')){previewLocale=editorFocus.locale;updatePreviewLabels();}\n updatePresenter();\n if(document.body.classList.contains('editing'))applyEditorFocus();\n postAgentsState();\n});\nupdatePreviewLabels();\nconst events=new EventSource('/events');events.onmessage=event=>{if(event.data==='history'){window.dispatchEvent(new Event('agent-history'));return;}if(event.data==='chat'){window.dispatchEvent(new Event('agent-chat'));return;}if(event.data==='ready'){delete state.revision;}void refresh();};events.onerror=()=>{connectionLost=true;updatePreviewStatus()};refresh();\n</script><script type=\"module\" src=\"/terminal.js\"></script></html>`;\n","/** Separate window so speaker notes never appear on the audience surface. */\nexport const presenterPage = `<!doctype html>\n<html lang=\"en\"><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\">\n<title>Presenter view</title>\n<style>\n*{box-sizing:border-box}body{margin:0;background:#141929;color:#e4e8f0;font:15px system-ui,sans-serif;height:100vh;display:flex;flex-direction:column}header,footer{display:flex;align-items:center;gap:14px;padding:16px;flex-wrap:wrap}h1{font-size:18px;margin:0;flex:1}h2{font-size:13px;color:#b9c2d8;margin:0 0 10px}button{font:inherit;color:inherit;background:#29243e;border:1px solid #675487;border-radius:6px;padding:8px 12px;cursor:pointer}button:disabled{opacity:.4;cursor:default}button:focus-visible{outline:2px solid #bca3ff;outline-offset:3px}main{display:grid;grid-template-columns:minmax(0,3fr) minmax(240px,2fr);gap:20px;padding:0 20px;flex:1;min-height:0;overflow:auto}section{min-width:0}#current{position:relative}#current [hidden]{display:none!important}#current,#next{background:#090c14;aspect-ratio:16/9}#current svg,#next svg{width:100%;height:100%}#notes{white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.6;max-height:40vh;overflow:auto;padding:14px;background:#1e2538;border-radius:8px}#notes-title{margin-top:20px}#count{flex:1}#message{padding:0 20px;color:#ffc9bd}#elapsed{font-variant-numeric:tabular-nums}footer{justify-content:flex-end}@media(max-width:640px){main{grid-template-columns:1fr}#notes{max-height:none}}\n</style>\n<header><h1 id=\"title\">Presenter view</h1><span id=\"timer-label\">Elapsed</span><output id=\"elapsed\">00:00</output><button id=\"reset\">Reset timer</button></header>\n<p id=\"message\" role=\"status\">Connecting to presentation…</p>\n<main><section><h2 id=\"current-title\">Current slide</h2><div id=\"current\"></div><button id=\"media-retry\" hidden></button><h2 id=\"notes-title\">Speaker notes</h2><div id=\"notes\" tabindex=\"0\"></div></section><section><h2 id=\"next-title\">Next slide</h2><div id=\"next\"></div><p id=\"end\" hidden></p></section></main>\n<footer><span id=\"count\" aria-live=\"polite\"></span><button id=\"prev\" disabled>Previous</button><button id=\"next-button\" disabled>Next</button><button id=\"exit\">Exit presentation</button></footer>\n<script>\nconst byId=id=>document.getElementById(id);\nconst canvases={current:byId('current').attachShadow({mode:'open'}),next:byId('next').attachShadow({mode:'open'})};\nconst displayed={};\nlet locale='en',started=performance.now(),connected=false;\nconst text=(en,ja)=>locale==='ja'?ja:en;\n// The presenter shows the slide the audience is looking at, so it runs the very\n// same player over its own copy, resumed at the same point in the same stop,\n// rather than being told after the fact what is on screen.\nlet makePlayer,animationPlayer,animationKey,animationGeneration,lastState;\nconst presenterReducedMotion=matchMedia('(prefers-reduced-motion: reduce)');\nimport('/animation-player.js').then(module=>{makePlayer=module.createAnimationPlayer;if(lastState)syncAnimation(lastState);},()=>{});\nfunction syncAnimation(data){\n const steps=data.presenting?data.animationSteps:null;\n if(!steps||!steps.length||!makePlayer){\n if(animationPlayer){animationPlayer.dispose();animationPlayer=undefined;animationKey=undefined;animationGeneration=undefined;}\n return;\n }\n const key=data.index+'\\\\u0000'+(data.current??'')+'\\\\u0000'+JSON.stringify(steps);\n if(key!==animationKey){\n animationPlayer?.dispose();\n animationPlayer=makePlayer({root:canvases.current,steps,reducedMotion:()=>presenterReducedMotion.matches});\n animationKey=key;animationGeneration=undefined;\n }\n const progress=data.animation;\n if(!progress)return;\n // Follow the audience only when it actually moved. The effects inside a stop\n // run on the same clock here, so re-running them on every passing report would\n // restart a fade; a seek that lands on the cursor it is already on — jumping\n // to the end of the stop in hand, say — still has to be followed, which is\n // what the generation says and the cursor alone does not.\n if(progress.generation===animationGeneration)return;\n animationGeneration=progress.generation;\n animationPlayer.resume(progress.cursor,progress.elapsed);\n}\nlet makeMediaPlayer,mediaPlayer,mediaOverlay,mediaKey,mediaLoading=false;\nfunction disposeMedia(){mediaPlayer?.dispose();mediaOverlay?.remove();mediaPlayer=undefined;mediaOverlay=undefined;mediaKey=undefined;}\nfunction syncMedia(data){\n const clips=(data.media??[]).filter(clip=>clip.kind!=='online');\n if(!data.presenting||!data.mediaKey||!clips.length){disposeMedia();byId('media-retry').hidden=true;return;}\n if(!makeMediaPlayer){\n if(mediaLoading)return;\n mediaLoading=true;byId('media-retry').hidden=true;\n import('/media-player.js?attempt='+Date.now()).then(module=>{\n makeMediaPlayer=module.createMediaPlayer;mediaLoading=false;if(lastState)syncMedia(lastState);\n },error=>{\n mediaLoading=false;console.warn('Could not load presenter media',error);\n byId('media-retry').textContent=text('Media could not load · Retry','メディアを読み込めませんでした · 再試行');\n byId('media-retry').hidden=!lastState?.presenting;\n });\n return;\n }\n const key=data.mediaKey+'\\\\u0000'+data.current;\n if(key!==mediaKey){\n disposeMedia();\n // Overlays must live inside the canvas shadow tree; the host has no slot\n // and would not render children appended to its light DOM.\n mediaOverlay=document.createElement('div');canvases.current.append(mediaOverlay);\n mediaPlayer=makeMediaPlayer({root:canvases.current,overlayRoot:mediaOverlay,clips,locale,\n onCommand:command=>{\n if(window.opener&&!window.opener.closed)window.opener.postMessage({type:'presenter-command',action:'media',index:command,mediaKey:data.mediaKey},location.origin);\n }});\n mediaKey=key;\n }\n mediaPlayer.sync(data.mediaProgress??[]);\n}\nbyId('media-retry').onclick=()=>{if(lastState)syncMedia(lastState);};\nfunction send(action,index){if(window.opener&&!window.opener.closed)window.opener.postMessage({type:'presenter-command',action,index},location.origin);}\nfor(const canvas of Object.values(canvases))canvas.addEventListener('click',event=>{\n const link=event.composedPath().find(node=>node instanceof Element&&node.localName==='a');\n const href=link?.getAttribute('href')??link?.getAttributeNS('http://www.w3.org/1999/xlink','href')??'';\n if(['#pptx-next-slide','#pptx-prev-slide','#pptx-first-slide','#pptx-last-slide'].includes(href)){\n event.preventDefault();send('navigation',href);\n }else if(href.startsWith('#pptx-custom-show?')){\n event.preventDefault();send('customShow',href);\n }else if(href==='#pptx-end-show'||href==='#pptx-last-slide-viewed'){\n event.preventDefault();send(href==='#pptx-end-show'?'exit':'lastViewed');\n }else if(href.startsWith('#slide-')){\n event.preventDefault();\n const number=Number(href.slice(7));\n if(Number.isInteger(number)&&number>=1)send('jump',number-1);\n }\n});\nfunction update(data){\n connected=true;locale=data.locale;document.documentElement.lang=locale;\n for(const [id,en,ja] of [['title','Presenter view','発表者ビュー'],['timer-label','Elapsed','経過時間'],['reset','Reset timer','タイマーをリセット'],['current-title','Current slide','現在のスライド'],['notes-title','Speaker notes','発表者ノート'],['next-title','Next slide','次のスライド'],['prev','Previous','前へ'],['next-button','Next','次へ'],['exit','Exit presentation','プレゼンテーションを終了']])byId(id).textContent=text(en,ja);\n document.title=text('Presenter view','発表者ビュー');\n byId('message').textContent=data.presenting?'':text('Presentation is stopped.','プレゼンテーションは停止中です。');\n const animation=data.animation&&data.animation.stops>0\n ? ' · '+text('Click '+data.animation.cursor+' of '+data.animation.stops,'クリック '+data.animation.cursor+' / '+data.animation.stops)\n : '';\n byId('count').textContent=data.count?text('Slide '+(data.index+1)+' of '+data.count,'スライド '+(data.index+1)+' / '+data.count)+animation:text('No slides','スライドがありません');\n for(const name of ['current','next']){\n const svg=data[name];\n if(displayed[name]!==svg){if(name==='current')disposeMedia();displayed[name]=svg;canvases[name].innerHTML=svg?'<style>svg{display:block;width:100%;height:100%}</style>'+svg:'';}\n byId(name).style.aspectRatio=String(data.aspectRatio);byId(name).hidden=!svg;\n }\n byId('notes').textContent=data.notes||text('No speaker notes.','発表者ノートはありません。');\n byId('prev').disabled=!data.hasPrevious;\n // The next click may be an effect on this slide rather than the next slide.\n byId('next-button').disabled=!(data.hasNext??data.next);\n byId('exit').disabled=!data.presenting;\n byId('end').hidden=!!(data.hasNext??data.next);byId('end').textContent=text('End of presentation','プレゼンテーションの最後です');\n lastState=data;syncAnimation(data);syncMedia(data);\n}\nwindow.addEventListener('message',event=>{\n if(event.origin!==location.origin||event.source!==window.opener)return;\n if(event.data?.type==='presenter-state')update(event.data);\n else if(event.data?.type==='presenter-media-state'&&lastState&&event.data.index===lastState.index&&event.data.mediaKey===lastState.mediaKey){\n lastState.mediaProgress=event.data.mediaProgress;mediaPlayer?.sync(lastState.mediaProgress);\n }\n});\nbyId('prev').onclick=()=>send('previous');byId('next-button').onclick=()=>send('next');byId('exit').onclick=()=>send('exit');\nbyId('reset').onclick=()=>{started=performance.now();byId('elapsed').textContent='00:00';};\ndocument.addEventListener('keydown',event=>{\n if(event.altKey||event.ctrlKey||event.metaKey||(event.key!=='Escape'&&event.target.closest('button')))return;\n const action=['ArrowRight','ArrowDown','PageDown',' '].includes(event.key)?'next':['ArrowLeft','ArrowUp','PageUp'].includes(event.key)?'previous':event.key==='Escape'?'exit':null;\n if(action){event.preventDefault();send(action);}\n});\nsetInterval(()=>{\n const seconds=Math.floor((performance.now()-started)/1000);byId('elapsed').textContent=String(Math.floor(seconds/60)).padStart(2,'0')+':'+String(seconds%60).padStart(2,'0');\n if(!window.opener||window.opener.closed){disposeMedia();byId('message').textContent=text('The presentation window is closed.','プレゼンテーションのウィンドウは閉じられています。');for(const id of ['prev','next-button','exit'])byId(id).disabled=true;}\n else if(!connected)send('ready');\n},250);\nsend('ready');\n</script></html>`;\n","import { agentStyles } from './styles.ts';\nexport const agentPage = `<!doctype html><html lang=\"en\"><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><link rel=\"stylesheet\" href=\"/terminal.css\"><style>${agentStyles}\n</style><div class=\"chat-heading\"><select id=\"chat-provider\" aria-label=\"AI provider\"><option value=\"claude\">Claude Code</option><option value=\"codex\">Codex</option></select><button id=\"chat-reset\" hidden>New chat</button></div>\n<div id=\"chat-context\">No slide selected</div>\n<section id=\"claude-panel\" aria-label=\"Claude Code terminal\"><div class=\"terminal-toolbar\"><span id=\"terminal-status\" role=\"status\">Start Claude Code to edit your slides.</span><button id=\"terminal-start\" class=\"primary\">Start</button><button id=\"terminal-stop\" hidden>End session</button></div><div class=\"terminal-frame\"><div id=\"terminal\"></div></div></section>\n<div id=\"codex-panel\" hidden>\n<div id=\"messages\" role=\"log\" aria-label=\"Conversation\"><p class=\"chat-intro\">Describe what you want to change. The current slide is included automatically. You can also ask about other slides or the whole deck.<br><br>Uses your locally installed Codex. Sign in through its CLI first. Sending a message allows it to edit this project.</p></div>\n<form id=\"chat-form\"><textarea id=\"chat-input\" aria-label=\"Message\" maxlength=\"16000\" placeholder=\"Ask for a change…\" required></textarea><div class=\"chat-actions\"><span class=\"chat-shortcut\">⌘ / Ctrl + Enter</span><button id=\"chat-stop\" type=\"button\" hidden>Stop</button><button id=\"chat-send\" class=\"primary\" type=\"submit\">Send</button></div></form>\n<div id=\"chat-status\" role=\"status\">Ready</div>\n</div>\n<script>\nconst byId=id=>document.getElementById(id),base=location.pathname;\nlet focus={slide:null,revision:0};\nwindow.addEventListener('message',event=>{\n if(event.origin!==location.origin||event.source!==parent)return;\n if(event.data?.type==='focus'){focus=event.data.focus;byId('chat-context').dataset.focus=JSON.stringify(focus);byId('chat-context').textContent=event.data.label;}\n if(event.data?.type==='chat')void refreshChat();\n if(event.data?.type==='inline-edit'&&byId('chat-provider').value==='codex'){\n void (async()=>{try{if(chatBusy)throw new Error('This agent is working. Wait for it to finish.');await chatAction('/chat',{message:event.data.message,provider:'codex',...event.data.focus});parent.postMessage({type:'inline-result',id:event.data.id},location.origin);}catch(error){parent.postMessage({type:'inline-result',id:event.data.id,error:error.message},location.origin);}})();\n }\n\n});\nparent.postMessage({type:'agent-ready'},location.origin);\nlet chatRequest=0,chatBusy=false,lastMessages='';\nfunction renderChat(chat){\n chatBusy=chat.running;\n byId('chat-send').disabled=chat.running;\n byId('chat-reset').disabled=chat.running;\n if(byId('chat-provider').value==='codex')byId('chat-provider').disabled=chat.running;\n byId('chat-stop').hidden=!chat.running;\n byId('chat-status').textContent=chat.status;\n\n const serialized=JSON.stringify(chat.messages);\n if(serialized===lastMessages)return;\n lastMessages=serialized;\n const log=byId('messages'),atBottom=log.scrollHeight-log.scrollTop-log.clientHeight<60;\n log.replaceChildren();\n if(!chat.messages.length){const intro=document.createElement('p');intro.className='chat-intro';intro.textContent='Ask for a focused slide edit or a change across the deck. Uses your locally installed CLI and its login. Sending allows edits to this project.';log.append(intro);}\n for(const message of chat.messages){\n const item=document.createElement('div'),label=document.createElement('small'),body=document.createElement('div');\n item.className='chat-message '+message.role;\n label.textContent=message.role==='user'?'You · '+(message.context?.slide?'Slide '+message.context.slide:'Whole project'):'Assistant';\n if(message.role==='assistant'&&message.html){body.className='markdown';body.innerHTML=message.html;}else body.textContent=message.text||(chat.running?'Working…':'No response');item.append(label,body);log.append(item);\n }\n if(atBottom)log.scrollTop=log.scrollHeight;\n}\nasync function refreshChat(){\n const id=++chatRequest;\n try{const response=await fetch(base+'/chat');if(!response.ok)throw new Error('Chat unavailable');const chat=await response.json();if(id===chatRequest)renderChat(chat);}\n catch(error){if(id===chatRequest)byId('chat-status').textContent=error.message;}\n}\nasync function chatAction(path,payload){\n ++chatRequest;\n const response=await fetch(base+path,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});\n const result=await response.json();if(!response.ok)throw new Error(result.error||'Chat request failed');\n await refreshChat();\n}\nbyId('chat-form').onsubmit=async event=>{\n event.preventDefault();if(chatBusy)return;\n const input=byId('chat-input'),message=input.value.trim();if(!message)return;\n chatBusy=true;byId('chat-send').disabled=true;\n try{await chatAction('/chat',{message,provider:byId('chat-provider').value,...focus});if(input.value.trim()===message)input.value='';}\n catch(error){byId('chat-status').textContent=error.message;chatBusy=false;byId('chat-send').disabled=false;}\n};\nbyId('chat-input').onkeydown=event=>{if(event.key==='Enter'&&(event.metaKey||event.ctrlKey)&&!event.isComposing){event.preventDefault();byId('chat-form').requestSubmit();}};\nfor(const action of ['stop','reset'])byId('chat-'+action).onclick=()=>chatAction('/chat/'+action,{}).catch(error=>{byId('chat-status').textContent=error.message;});\nbyId('chat-provider').onchange=()=>{try{sessionStorage.setItem('office-kit-provider'+base,byId('chat-provider').value);}catch{}const claude=byId('chat-provider').value==='claude';byId('claude-panel').hidden=!claude;byId('codex-panel').hidden=claude;byId('chat-reset').hidden=claude;};\ntry{byId('chat-provider').value=sessionStorage.getItem('office-kit-provider'+base)||'claude';}catch{}\nbyId('chat-provider').onchange();\nvoid refreshChat();\n</script><script type=\"module\" src=\"/terminal.js\"></script></html>`;\n","/** Shared guidance for agents editing a deck through the preview. */\nexport const authoringGuidance = `For lists and tables of contents, use Text paragraphs/bullets or mapped Text rows. Bullets and TableOfContents are not built-in DSL components: inspect installed exports, import existing local helpers or define them. Do not use Raw for these layouts.\nChoose foreground and actual background colors together. Explicitly set readable text colors on slides, cards, tables, chart labels and footnotes; check inherited theme styles. Aim for 4.5:1 contrast for normal text and 3:1 for large text (18pt or 14pt bold). Use a solid backing over images or gradients if needed. Inspect rendered slides; compilation alone cannot verify contrast.`;\n","import type { VisualReview } from './visual-review.ts';\nimport { authoringGuidance } from './authoring-guidance.ts';\nimport { randomBytes } from 'node:crypto';\nimport { dirname, resolve, join } from 'node:path';\nimport { createRequire } from 'node:module';\nimport { stat, chmod } from 'node:fs/promises';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { IPty } from 'node-pty';\nimport type { FocusContext } from './chat.ts';\n\nconst OUTPUT_LIMIT = 2_000_000;\nconst REQUEST_LIMIT = 128_000;\n\n/** A real interactive Claude session, shared across reloads of its owning tab. */\nexport function createTerminal(\n entry: string,\n busy = () => false,\n base = '',\n verify: () => Promise<string | null> = async () => null,\n review?: { begin(): void; next(): Promise<VisualReview | undefined> },\n history?: { begin(): Promise<void>; end(): Promise<void> },\n) {\n let repairs = 0;\n let working = false;\n let process: IPty | undefined;\n let owner = '';\n let output = '';\n let context: FocusContext | undefined;\n let status = 'Start Claude Code to edit your slides.';\n let completion = Promise.resolve();\n let closing = false;\n let stopping = false;\n let starting = false;\n const token = randomBytes(32).toString('hex');\n const clients = new Set<ServerResponse>();\n const snapshot = () => ({ running: !!process, owner, status });\n const emit = (event: string, value: unknown) => {\n for (const client of clients) {\n if (!client.write(`event: ${event}\\ndata: ${JSON.stringify(value)}\\n\\n`)) {\n clients.delete(client);\n client.end();\n }\n }\n };\n async function stop() {\n if (!process || stopping) return completion;\n stopping = true;\n status = 'Stopping…';\n emit('state', snapshot());\n const running = process;\n running.kill();\n const timer = setTimeout(() => {\n if (process === running) running.kill('SIGKILL');\n }, 2000);\n await completion;\n clearTimeout(timer);\n }\n async function handle(\n request: IncomingMessage,\n response: ServerResponse,\n focus: (index: number | null, revision: number) => FocusContext,\n ) {\n const json = (code: number, value: unknown) => {\n response.writeHead(code, { 'Content-Type': 'application/json' });\n response.end(JSON.stringify(value));\n };\n if (request.url === '/terminal/context' || request.url === '/terminal/verify') {\n if (request.method !== 'POST' || request.headers.authorization !== `Bearer ${token}`) {\n json(403, { error: 'Invalid hook credentials' });\n return;\n }\n request.resume();\n if (request.url === '/terminal/verify') {\n if (closing || stopping || !process) {\n json(200, {});\n return;\n }\n let error: string | null;\n try {\n error = await verify();\n } catch (cause) {\n error = cause instanceof Error ? cause.message : String(cause);\n }\n if (closing || stopping || !process) {\n json(200, {});\n return;\n }\n if (error && repairs < 3) {\n repairs++;\n status = `Repairing preview (${repairs}/3)…`;\n json(200, {\n decision: 'block',\n reason: `The actual preview build failed. Repair the source before finishing (attempt ${repairs}/3). Preserve unrelated edits, inspect the installed DSL exports and local components before changing imports. Do not hide the error or remove requested content. No Raw is needed for lists or an agenda: use Text paragraphs/bullets or mapped Text rows. Build error:\\n${error.slice(0, 16000)}`,\n });\n } else {\n if (!error && review) {\n try {\n const visual = await review.next();\n if (closing || stopping || !process) {\n json(200, {});\n return;\n }\n if (visual) {\n status = 'Reviewing slide screenshots…';\n json(200, { decision: 'block', reason: visual.prompt });\n emit('state', snapshot());\n return;\n }\n } catch (cause) {\n error = cause instanceof Error ? cause.message : String(cause);\n }\n }\n try {\n await history?.end();\n } catch (cause) {\n error = 'History: ' + String(cause);\n }\n working = false;\n status = error ? 'Preview verification incomplete: ' + error : 'Preview build verified';\n json(200, {});\n }\n emit('state', snapshot());\n return;\n }\n try {\n await history?.begin();\n } catch (cause) {\n json(409, { error: String(cause) });\n return;\n }\n repairs = 0;\n working = true;\n review?.begin();\n json(200, {\n hookSpecificOutput: {\n hookEventName: 'UserPromptSubmit',\n additionalContext: `You are editing the local presentation TSX project at ${resolve(entry)}.\nRead the project's authoring instructions. Make focused source patches and preserve unrelated work.\nThe preview rebuilds on save; do not start another dev server. A Stop hook checks the actual build and returns errors for repair.\n${authoringGuidance}\nThe focused slide is context, NOT a restriction. For \"this slide\", edit only its relevant source. For other slides or deck-wide requests, locate the relevant source or shared theme.\nSlide numbers are 1-based. Verify source before editing; dependency paths are candidates, not an exact slide-to-file mapping.\nPreview context captured with the latest terminal input: ${JSON.stringify(context)}`,\n },\n });\n return;\n }\n const origin = `http://${request.headers.host}`;\n if (\n request.headers['sec-fetch-site'] === 'cross-site' ||\n (request.headers.origin && request.headers.origin !== origin)\n ) {\n json(403, { error: 'Same-origin requests only' });\n return;\n }\n if (request.method === 'GET' && request.url === '/terminal/events') {\n response.writeHead(200, { 'Content-Type': 'text/event-stream' });\n response.write(`event: replay\\ndata: ${JSON.stringify(output)}\\n\\n`);\n response.write(`event: state\\ndata: ${JSON.stringify(snapshot())}\\n\\n`);\n clients.add(response);\n request.on('close', () => clients.delete(response));\n return;\n }\n if (\n request.method !== 'POST' ||\n request.headers.origin !== origin ||\n request.headers['content-type'] !== 'application/json'\n ) {\n json(403, { error: 'Same-origin JSON requests only' });\n return;\n }\n try {\n let body = '';\n request.setEncoding('utf8');\n for await (const chunk of request) {\n body += chunk;\n if (Buffer.byteLength(body) > REQUEST_LIMIT) throw new Error('Request too large');\n }\n const value = JSON.parse(body);\n if (!value || typeof value.client !== 'string' || !/^[\\w-]{1,64}$/.test(value.client))\n throw new Error('Invalid terminal client');\n if (closing || starting || stopping || (process && owner !== value.client)) {\n json(409, {\n error: stopping\n ? 'Claude Code is stopping'\n : 'Claude Code is open in another tab. Use that tab to control it.',\n });\n return;\n }\n const size = () => {\n if (\n !Number.isInteger(value.cols) ||\n value.cols < 2 ||\n value.cols > 500 ||\n !Number.isInteger(value.rows) ||\n value.rows < 1 ||\n value.rows > 300\n )\n throw new Error('Invalid terminal dimensions');\n };\n const updateFocus = () => {\n if (\n !(value.slide === null || (Number.isInteger(value.slide) && value.slide >= 0)) ||\n !Number.isInteger(value.revision)\n )\n throw new Error('Invalid preview context');\n context = focus(value.slide, value.revision);\n };\n if (request.url === '/terminal/start') {\n if (busy()) throw new Error('A Codex edit is already running');\n size();\n updateFocus();\n if (!process) {\n starting = true;\n try {\n // Lazy loading leaves preview/export usable if a native PTY cannot load.\n // node-pty 1.1.0 ships its macOS spawn helper without executable bits.\n // Restore the packaged helper's mode before its first launch.\n if (globalThis.process.platform === 'darwin') {\n const root = dirname(createRequire(import.meta.url).resolve('node-pty/package.json'));\n const helper = join(\n root,\n 'prebuilds',\n `darwin-${globalThis.process.arch}`,\n 'spawn-helper',\n );\n const info = await stat(helper).catch((error: NodeJS.ErrnoException) => {\n if (error.code === 'ENOENT') return undefined;\n throw error;\n });\n if (info && !(info.mode & 0o100)) await chmod(helper, info.mode | 0o100);\n }\n const pty = await import('node-pty');\n if (process || closing) throw new Error('Terminal state changed. Try again.');\n const settings = {\n hooks: {\n Stop: [\n {\n hooks: [\n {\n type: 'http',\n url: `${origin}${base}/terminal/verify`,\n headers: { Authorization: `Bearer ${token}` },\n timeout: 120,\n },\n ],\n },\n ],\n UserPromptSubmit: [\n {\n hooks: [\n {\n type: 'http',\n url: `${origin}${base}/terminal/context`,\n headers: { Authorization: `Bearer ${token}` },\n timeout: 5,\n },\n ],\n },\n ],\n },\n };\n const env = { ...globalThis.process.env };\n // This is an independent interactive session, even if the preview was started by Claude.\n delete env.CLAUDECODE;\n process = pty.spawn('claude', ['--settings', JSON.stringify(settings)], {\n cwd: dirname(resolve(entry)),\n name: 'xterm-256color',\n env,\n cols: value.cols,\n rows: value.rows,\n });\n } finally {\n starting = false;\n }\n owner = value.client;\n output = '';\n status = 'Claude Code';\n emit('replay', '');\n process.onData((data) => {\n output = (output + data).slice(-OUTPUT_LIMIT);\n emit('output', data);\n });\n completion = new Promise<void>((done) =>\n process!.onExit(async ({ exitCode }) => {\n process = undefined;\n working = false;\n stopping = false;\n status = `Claude Code exited (${exitCode}). Start to open a new session.`;\n try {\n await history?.end();\n } catch (cause) {\n status += ' History: ' + String(cause);\n }\n emit('state', snapshot());\n done();\n }),\n );\n }\n } else if (request.url === '/terminal/prompt') {\n if (!process) throw new Error('Start Claude Code in the selected agent pane first.');\n if (working)\n throw new Error('Claude Code is working. Wait for the current turn to finish.');\n if (\n typeof value.message !== 'string' ||\n !value.message.trim() ||\n value.message.length > 16000 ||\n [...value.message].some((character: string) => {\n const code = character.charCodeAt(0);\n return (code < 32 && code !== 9 && code !== 10) || code === 127;\n })\n )\n throw new Error('Invalid inline prompt');\n updateFocus();\n working = true;\n try {\n await history?.begin();\n } catch (cause) {\n working = false;\n throw cause;\n }\n if (!process || closing || stopping) {\n await history?.end();\n working = false;\n throw new Error('Claude Code stopped before submitting.');\n }\n process.write('\\x1b[200~' + value.message + '\\x1b[201~');\n await new Promise((done) => setTimeout(done, 100));\n if (!process || stopping) throw new Error('Claude Code stopped before submitting.');\n process.write('\\r');\n } else if (request.url === '/terminal/input') {\n if (!process) throw new Error('Start Claude Code first');\n if (typeof value.data !== 'string' || value.data.length > 32000)\n throw new Error('Invalid terminal input');\n updateFocus();\n process.write(value.data);\n if (value.data.includes('\\x03')) {\n working = false;\n await history?.end();\n }\n } else if (request.url === '/terminal/resize') {\n size();\n process?.resize(value.cols, value.rows);\n } else if (request.url === '/terminal/stop') {\n await stop();\n } else {\n json(404, { error: 'Not found' });\n return;\n }\n emit('state', snapshot());\n json(200, snapshot());\n } catch (cause) {\n json(400, { error: cause instanceof Error ? cause.message : 'Invalid terminal request' });\n }\n }\n return {\n handle,\n isRunning: () => starting || !!process,\n async close() {\n closing = true;\n await stop();\n for (const client of clients) client.end();\n clients.clear();\n },\n };\n}\n","import type { VisualReview } from './visual-review.ts';\nimport { authoringGuidance } from './authoring-guidance.ts';\nimport MarkdownIt from 'markdown-it';\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { dirname, resolve } from 'node:path';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nexport type Provider = 'codex';\nconst markdown = new MarkdownIt({ html: false, linkify: false });\n// Do not fetch remote images from model output.\nmarkdown.renderer.rules.image = (tokens, index) =>\n markdown.utils.escapeHtml(tokens[index]?.content ?? '');\nexport interface FocusContext {\n slide: number | null;\n count: number;\n revision: number;\n text: string;\n entry: string;\n files: string[];\n buildError: string | null;\n}\ninterface Message {\n role: 'user' | 'assistant';\n text: string;\n context?: FocusContext;\n}\n\n/** One shared conversation and one editing process per dev server. */\nexport function createChat(\n entry: string,\n notify: () => void,\n busy = () => false,\n verify: () => Promise<string | null> = async () => null,\n review?: { begin(): void; next(): Promise<VisualReview | undefined> },\n reviewImages: (prompt: string) => string[] = () => [],\n history?: { begin(): Promise<void>; end(): Promise<void> },\n) {\n let starting = false;\n let closing = false;\n let messages: Message[] = [];\n let provider: Provider = 'codex';\n let child: ChildProcess | undefined;\n let status = 'Ready';\n let stopping = false;\n let killTimer: ReturnType<typeof setTimeout> | undefined;\n let completion: Promise<void> = Promise.resolve();\n const snapshot = () => ({\n messages: messages.map((message) => ({\n ...message,\n ...(message.role === 'assistant' ? { html: markdown.render(message.text) } : {}),\n })),\n provider,\n running: starting || !!child,\n status,\n });\n function stop() {\n if (!child || stopping) return;\n stopping = true;\n status = 'Stopping…';\n const processToStop = child;\n // During verification the child has exited, but the turn still owns the\n // conversation. Cancel repair without signalling a potentially reused PID.\n if (processToStop.exitCode !== null || processToStop.signalCode !== null) {\n notify();\n return;\n }\n const kill = (signal: NodeJS.Signals) => {\n try {\n if (process.platform !== 'win32' && processToStop.pid)\n process.kill(-processToStop.pid, signal);\n else processToStop.kill(signal);\n } catch {\n /* Already exited. */\n }\n };\n kill('SIGTERM');\n killTimer = setTimeout(() => kill('SIGKILL'), 2000);\n notify();\n }\n function start(\n text: string,\n selected: Provider,\n context: FocusContext,\n repairs = 0,\n images: string[] = [],\n reviewing = false,\n ) {\n if (!repairs && !reviewing) review?.begin();\n provider = selected;\n const user: Message = { role: 'user', text, context };\n // Keep recent conversational intent, including each turn's original focus.\n const conversation = messages.slice(-12).map((message) => ({\n ...message,\n text: message.text.slice(-12000),\n }));\n const prompt = `You are editing a local presentation TSX project in ${dirname(resolve(entry))}.\nRead the project's authoring instructions. Make focused patches to the TSX source, preserving unrelated work.\nThe preview rebuilds automatically on save; do not start another dev server.\n${authoringGuidance}\nThe attached focus is context, NOT a restriction: use it for references such as \"this slide\". For other slides or deck-wide requests, find the relevant source or shared theme instead. Do not rewrite the whole deck for a local edit.\nSlide numbers are 1-based and refer to the last successful preview at send time. Source may have changed since then; verify it before editing. Files are dependency candidates, not an exact slide-to-source mapping.\nRespond in the user's language, briefly describing edits or answering their question. If permissions prevent an action, explain that instead of claiming success.\nRecent conversation (JSON): ${JSON.stringify(conversation)}\nCurrent request and preview context (JSON): ${JSON.stringify(user)}`;\n messages.push(user);\n const answer: Message = { role: 'assistant', text: '' };\n messages.push(answer);\n if (messages.length > 100) messages = messages.slice(-100);\n status = 'Working…';\n stopping = false;\n const args = ['exec', '--json', '--sandbox', 'workspace-write', '--skip-git-repo-check', '-'];\n for (const image of images) args.splice(args.length - 1, 0, '--image', image);\n child = spawn(selected, args, {\n cwd: dirname(resolve(entry)),\n stdio: ['pipe', 'pipe', 'pipe'],\n detached: process.platform !== 'win32',\n shell: false,\n });\n const running = child;\n let buffer = '',\n stderr = '',\n failure = '',\n sawResult = false;\n function append(value: string) {\n answer.text = (answer.text + (answer.text ? '\\n\\n' : '') + value).slice(-100000);\n notify();\n }\n function consume(line: string) {\n if (!line.trim()) return;\n try {\n const event = JSON.parse(line);\n if (event.type === 'item.completed' && event.item?.type === 'agent_message')\n append(event.item.text ?? '');\n if (event.type === 'item.started') {\n status = 'Editing / inspecting…';\n notify();\n }\n if (event.type === 'turn.completed') sawResult = true;\n if (event.type === 'turn.failed' || event.type === 'error')\n failure = event.error?.message ?? event.message ?? 'Agent failed';\n } catch {\n failure = 'Unexpected CLI output. Update your CLI and try again.';\n }\n }\n running.stdout!.setEncoding('utf8');\n running.stdout!.on('data', (chunk: string) => {\n buffer += chunk;\n let end: number;\n while ((end = buffer.indexOf('\\n')) !== -1) {\n consume(buffer.slice(0, end));\n buffer = buffer.slice(end + 1);\n }\n if (buffer.length > 2_000_000) {\n failure = 'CLI output exceeded the limit.';\n stop();\n }\n });\n running.stderr!.setEncoding('utf8');\n running.stderr!.on('data', (chunk: string) => {\n stderr = (stderr + chunk).slice(-8000);\n });\n running.stdin!.on('error', () => {\n /* Early process exit is handled below. */\n });\n running.stdin!.end(prompt);\n completion = new Promise<void>((done) => {\n running.on('error', (error) => {\n failure = `Could not run ${selected}. Install it and log in from your terminal first. ${error.message}`;\n });\n running.on('close', async (code) => {\n if (buffer.trim()) consume(buffer);\n clearTimeout(killTimer);\n status = stopping\n ? 'Stopped. Saved edits remain in the preview.'\n : failure ||\n (code !== 0\n ? stderr || `${selected} exited with code ${code}`\n : !sawResult\n ? 'CLI exited without a completed result.'\n : 'Done');\n if (status === 'Done') {\n status = 'Verifying preview…';\n notify();\n let buildError: string | null;\n try {\n buildError = await verify();\n } catch (cause) {\n buildError = cause instanceof Error ? cause.message : String(cause);\n }\n if (!stopping && buildError && repairs < 3) {\n child = undefined;\n start(\n `The actual preview build failed. Repair the source and preserve unrelated work (attempt ${repairs + 1}/3). Do not remove requested content or hide the error.\\n${buildError.slice(0, 16000)}`,\n selected,\n { ...context, buildError },\n repairs + 1,\n [],\n reviewing,\n );\n await completion;\n done();\n return;\n }\n let reviewFailure = '';\n if (!stopping && !buildError && review) {\n try {\n const visual = await review.next();\n if (!stopping && visual) {\n child = undefined;\n start(visual.prompt, selected, context, 0, visual.images, true);\n await completion;\n done();\n return;\n }\n } catch (error) {\n reviewFailure = error instanceof Error ? error.message : String(error);\n }\n }\n status = stopping\n ? 'Stopped. Saved edits remain in the preview.'\n : buildError\n ? 'Preview still has errors after 3 repair attempts.\\n' + buildError.slice(0, 16000)\n : reviewFailure || 'Done';\n }\n try {\n await history?.end();\n } catch (cause) {\n status = 'History: ' + String(cause);\n }\n if (status !== 'Done') append(status);\n child = undefined;\n notify();\n done();\n });\n });\n notify();\n }\n async function handle(\n request: IncomingMessage,\n response: ServerResponse,\n focus: (index: number | null, revision: number) => FocusContext,\n ) {\n const json = (code: number, value: unknown) => {\n response.writeHead(code, { 'Content-Type': 'application/json' });\n response.end(JSON.stringify(value));\n };\n if (request.method === 'GET' && request.url === '/chat') {\n json(200, snapshot());\n return;\n }\n if (request.method !== 'POST') {\n json(405, { error: 'Method not allowed' });\n return;\n }\n // Same-origin JSON prevents other sites from invoking local editing tools.\n if (\n request.headers.origin !== `http://${request.headers.host}` ||\n request.headers['content-type']?.split(';')[0] !== 'application/json'\n ) {\n json(403, { error: 'Same-origin JSON required' });\n return;\n }\n try {\n let body = '';\n request.setEncoding('utf8');\n for await (const chunk of request) {\n body += chunk;\n if (Buffer.byteLength(body) > 32000) {\n json(413, { error: 'Message too large' });\n return;\n }\n }\n const value = JSON.parse(body);\n if (request.url === '/chat/stop') {\n stop();\n json(200, snapshot());\n return;\n }\n if (child || starting || closing) {\n json(409, { error: 'An edit is already running' });\n return;\n }\n if (request.url === '/chat/reset') {\n messages = [];\n status = 'Ready';\n notify();\n json(200, snapshot());\n return;\n }\n if (request.url !== '/chat') {\n json(404, { error: 'Not found' });\n return;\n }\n if (\n typeof value.message !== 'string' ||\n !value.message.trim() ||\n value.message.length > 16000 ||\n value.provider !== 'codex' ||\n !(value.slide === null || (Number.isInteger(value.slide) && value.slide >= 0)) ||\n !Number.isInteger(value.revision)\n ) {\n json(400, { error: 'Invalid chat request' });\n return;\n }\n if (busy()) {\n json(409, { error: 'End the Claude Code session before starting a Codex edit' });\n return;\n }\n const context = focus(value.slide, value.revision);\n starting = true;\n try {\n await history?.begin();\n if (closing) {\n await history?.end();\n throw new Error('Agent is closing');\n }\n start(value.message.trim(), value.provider, context, 0, reviewImages(value.message.trim()));\n } finally {\n starting = false;\n }\n json(202, snapshot());\n } catch (cause) {\n json(400, { error: cause instanceof Error ? cause.message : 'Invalid request' });\n }\n }\n return {\n handle,\n isRunning: () => starting || !!child,\n async close() {\n closing = true;\n stop();\n await completion;\n },\n };\n}\n","import { createHistory, historyFile } from './history.ts';\nimport { createTextEditor } from './text-edit.ts';\nimport { createSourceSync, type SyncResult } from './source-sync.ts';\nimport { createVisualReviewer } from './visual-review.ts';\nimport { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { createHash, randomUUID } from 'node:crypto';\nimport { watch } from 'node:fs';\nimport { basename, dirname, resolve, sep } from 'node:path';\nimport { renderDeck, type BuildResult } from './build.ts';\nimport { editorStore, type SavedEdits } from './editor-store.ts';\nimport { sourceFingerprint } from './fingerprint.ts';\nimport { mergeDecks, type DeckConflict } from '@office-kit/pptx-editor/merge';\nimport { createDeckBuilder } from './build-runner.ts';\nimport { page } from './page.ts';\nimport { presenterPage } from './presenter-page.ts';\nimport { agentPage } from './agent-page.ts';\nimport { readFile } from 'node:fs/promises';\nimport { createTerminal } from './terminal.ts';\nimport { createChat } from './chat.ts';\n\ntype CachedMedia = {\n bytes: Buffer;\n contentType: string;\n};\n\nconst embeddedMediaUrl = /^data:([^;,]+);base64,(.*)$/s;\n\nfunction cacheEmbeddedMedia(media: BuildResult['media']) {\n const cache = new Map<string, CachedMedia>();\n const published = media.map((item) => {\n if (item.kind === 'online') return item;\n const match = embeddedMediaUrl.exec(item.src);\n if (!match) return item;\n const bytes = Buffer.from(match[2]!, 'base64');\n const hash = createHash('sha256').update(bytes).digest('hex');\n // Repeated clips intentionally share one immutable resource.\n if (!cache.has(hash)) {\n cache.set(hash, {\n bytes,\n contentType: item.contentType ?? match[1]!,\n });\n }\n return { ...item, src: `/media/${hash}` };\n });\n return { cache, published };\n}\n\nfunction mediaRange(value: string | undefined, length: number) {\n if (!value) return { start: 0, end: length - 1, partial: false };\n const match = /^bytes=(\\d*)-(\\d*)$/.exec(value);\n if (!match || (!match[1] && !match[2]) || length === 0) return null;\n let start: number;\n let end: number;\n if (!match[1]) {\n const suffix = Number(match[2]);\n if (!Number.isSafeInteger(suffix) || suffix <= 0) return null;\n start = Math.max(0, length - suffix);\n end = length - 1;\n } else {\n start = Number(match[1]);\n end = match[2] ? Number(match[2]) : length - 1;\n if (\n !Number.isSafeInteger(start) ||\n !Number.isSafeInteger(end) ||\n start > end ||\n start >= length\n )\n return null;\n end = Math.min(end, length - 1);\n }\n return { start, end, partial: true };\n}\n\n// Files `build-editor.mjs` puts next to the CLI, served as they are. The\n// animation player is one build shared by the preview page, the presenter\n// window and the editor panel, so all three play a slide the same way.\n// Documents the editor may still be editing, by revision. An editor that saves\n// edits made on one of these after the source rebuilt gets them merged.\nconst REMEMBERED_DOCUMENTS = 32;\n\n// Parts of `editor.js` it loads on demand; the name pattern keeps requests inside that folder.\nconst EDITOR_CHUNK = /^\\/editor-chunks\\/[\\w-]+\\.js$/;\n\nconst BUNDLED_ASSETS: Record<string, string> = {\n '/terminal.js': 'terminal-client.js',\n '/terminal.css': 'terminal-client.css',\n '/editor.js': 'editor.js',\n '/animation-player.js': 'animation-player.js',\n '/media-player.js': 'media-player.js',\n};\n\nexport async function serveDeck(entry: string, port = 4173) {\n let latest: BuildResult | undefined;\n let source: BuildResult | undefined;\n let sourceHash: string | undefined;\n let documentHash: string | undefined;\n let mediaCache = new Map<string, CachedMedia>();\n let publishedMedia: BuildResult['media'] = [];\n const store = editorStore(entry);\n let saved = await store.read();\n // Why the saved edits could not be merged with the current source.\n let conflicts: DeckConflict[] = [];\n const documents = new Map<string, Uint8Array>();\n let uploads = 0;\n function remember(id: string, bytes: Uint8Array) {\n documents.set(id, bytes);\n if (documents.size > REMEMBERED_DOCUMENTS) documents.delete(documents.keys().next().value!);\n }\n const serverId = randomUUID();\n const projectId = createHash('sha256').update(resolve(entry)).digest('hex');\n let publishing = Promise.resolve();\n // Rebuilds and HTTP writes publish in order, including their atomic disk write.\n function publish<T>(fn: () => Promise<T>): Promise<T> {\n const result = publishing.then(fn);\n publishing = result.then(\n () => {},\n () => {},\n );\n return result;\n }\n let error: string | null = null;\n let building = false;\n let revision = 0;\n let generation = 0;\n let patch: Record<number, string> = {};\n let pending = false;\n let closed = false;\n const clients = new Set<ServerResponse>();\n const sessions = new Map<\n string,\n {\n chat: ReturnType<typeof createChat>;\n terminal: ReturnType<typeof createTerminal>;\n closing: boolean;\n }\n >();\n const history = await createHistory(dirname(resolve(entry)), () => {\n for (const client of clients) client.write('data: history\\n\\n');\n });\n let textEdits = 0;\n const capturedReviews = new Map<string, string[]>();\n function session(id: string) {\n let result = sessions.get(id);\n if (!result) {\n const review = createVisualReviewer(resolve(entry), () => latest?.slides ?? []);\n const turnHistory = {\n begin: () => history.begin('agent:' + id, 'AI edit'),\n end: () => history.end('agent:' + id),\n };\n const chat = createChat(\n entry,\n () => {\n for (const client of clients) client.write('data: chat\\n\\n');\n },\n () => terminal.isRunning(),\n verify,\n review,\n (prompt) => capturedReviews.get(prompt) ?? [],\n turnHistory,\n );\n const terminal = createTerminal(\n entry,\n () => chat.isRunning(),\n id ? '/agents/' + id : '',\n verify,\n review,\n turnHistory,\n );\n result = { chat, terminal, closing: false };\n sessions.set(id, result);\n }\n return result;\n }\n function editorState() {\n const conflict = !!saved && !!sourceHash && saved.sourceHash !== sourceHash;\n return {\n projectId,\n revision: `${serverId}:${revision}`,\n previewRevision: revision,\n documentHash,\n sourceHash,\n fileName: basename(entry).replace(/\\.[^.]+$/, '') + '.pptx',\n hasEdits: !!saved,\n conflict,\n conflicts: conflict ? conflicts : [],\n building,\n error,\n available: !!latest,\n };\n }\n function update(result: BuildResult) {\n patch = {};\n result.slides.forEach((svg, index) => {\n if (svg !== latest?.slides[index]) patch[index] = svg;\n });\n latest = result;\n ({ cache: mediaCache, published: publishedMedia } = cacheEmbeddedMedia(result.media));\n documentHash = sourceFingerprint(result.bytes);\n revision++;\n remember(`${serverId}:${revision}`, result.bytes);\n }\n function notify() {\n for (const client of clients) client.write('data: updated\\n\\n');\n }\n type Upload =\n | { ok: true; bytes: Uint8Array; merged: boolean; source: BuildResult; sourceHash: string }\n | { ok: false; refused: 'busy' | 'stale' | 'conflict'; conflicts: DeckConflict[] };\n /**\n * Accepts an editor upload. An upload based on an older document the editor\n * loaded (the source rebuilt meanwhile) is merged with the current one.\n */\n function acceptUpload(bytes: Uint8Array, basis: string | undefined, keepEdits: boolean): Upload {\n if (!source || !sourceHash || !latest || building || error || closed)\n return { ok: false, refused: 'busy', conflicts: [] };\n const state = editorState();\n if (state.conflict && !keepEdits)\n return { ok: false, refused: 'conflict', conflicts: state.conflicts };\n const accepted = { ok: true, source, sourceHash } as const;\n if (basis === state.revision) return { ...accepted, bytes, merged: false };\n const based = basis !== undefined && !keepEdits ? documents.get(basis) : undefined;\n if (!based) return { ok: false, refused: 'stale', conflicts: [] };\n const merged = mergeDecks(based, bytes, latest.bytes);\n return merged.ok\n ? { ...accepted, bytes: merged.bytes, merged: true }\n : { ok: false, refused: 'conflict', conflicts: merged.conflicts };\n }\n async function edit(request: IncomingMessage, response: ServerResponse) {\n const json = (status: number, message?: string, extra: Record<string, unknown> = {}) => {\n response.writeHead(status, { 'Content-Type': 'application/json' });\n response.end(JSON.stringify({ ...editorState(), ...(message ? { message } : {}), ...extra }));\n };\n const refuse = (upload: Extract<Upload, { ok: false }>) =>\n json(\n 409,\n upload.refused === 'conflict'\n ? 'These edits conflict with changes in the source.'\n : 'The preview changed. Review the current source before saving.',\n { refused: upload.refused, mergeConflicts: upload.conflicts },\n );\n if (\n request.headers.origin !== `http://${request.headers.host}` ||\n request.headers['sec-fetch-site'] === 'cross-site'\n ) {\n request.resume();\n json(403, 'Editor requests must come from this preview.');\n return;\n }\n const useSource = request.url === '/editor/resolve' && request.method === 'POST';\n const save = request.url === '/editor/document' && request.method === 'PUT';\n if (!useSource && !save) {\n request.resume();\n json(405);\n return;\n }\n if (save && request.headers['content-type'] !== 'application/octet-stream') {\n request.resume();\n json(415);\n return;\n }\n const chunks: Buffer[] = [];\n let length = 0;\n const MAX_EDITOR_BYTES = 64 * 1024 * 1024;\n const basis = request.headers['if-match'];\n const keepEdits = request.headers['x-editor-resolve'] === 'edits';\n // Remembers what the editor uploaded, so later edits made on top of it\n // while the merged result was on its way can merge again.\n const uploaded = (bytes: Uint8Array, upload: Extract<Upload, { ok: true }>) => {\n if (!upload.merged) return {};\n const id = `${serverId}:upload:${++uploads}`;\n remember(id, bytes);\n return { merged: true, uploadRevision: id };\n };\n try {\n for await (const chunk of request) {\n length += chunk.length;\n if (length > MAX_EDITOR_BYTES) {\n json(413, 'The editor upload exceeds 64 MiB.');\n return;\n }\n chunks.push(chunk);\n }\n if (save && sourceSync) {\n const received = new Uint8Array(Buffer.concat(chunks));\n const upload = acceptUpload(received, basis, keepEdits);\n if (!upload.ok) {\n refuse(upload);\n return;\n }\n const bytes = upload.bytes;\n // Runs outside `publish`: verifying a write rebuilds, and a rebuild\n // publishes too. A refused write still saves the edits to the sidecar.\n let synced: SyncResult;\n try {\n synced = await sourceSync.sync(bytes);\n } catch (cause) {\n const description = cause instanceof Error ? cause.message : String(cause);\n synced = {\n written: [],\n pending: [{ kind: 'unsupported', slide: null, shapeId: null, description }],\n complete: false,\n };\n }\n await publish(async () => {\n if (!source || !sourceHash || closed) {\n json(409, 'The preview changed. Review the current source before saving.', {\n refused: 'busy',\n });\n return;\n }\n if (synced.complete) {\n await store.clear();\n saved = undefined;\n update(source);\n } else {\n let result: BuildResult;\n try {\n result = (await renderDeck(bytes, source.dependencies)).result;\n } catch (cause) {\n json(400, cause instanceof Error ? cause.message : String(cause));\n return;\n }\n const edits = { sourceHash, base: source.bytes, bytes };\n await store.write(edits);\n saved = edits;\n update(result);\n }\n json(200, undefined, {\n ...uploaded(received, upload),\n writeBack: { written: synced.written.length, pending: synced.pending },\n });\n notify();\n });\n return;\n }\n await publish(async () => {\n if (useSource) {\n if (\n !source ||\n !sourceHash ||\n building ||\n error ||\n closed ||\n basis !== editorState().revision\n ) {\n json(409, 'The preview changed. Review the current source before saving.', {\n refused: 'busy',\n });\n return;\n }\n await store.clear();\n saved = undefined;\n update(source);\n json(200);\n notify();\n return;\n }\n const received = new Uint8Array(Buffer.concat(chunks));\n const upload = acceptUpload(received, basis, keepEdits);\n if (!upload.ok) {\n refuse(upload);\n return;\n }\n const started = generation;\n let result: BuildResult;\n try {\n result = (await renderDeck(upload.bytes, upload.source.dependencies)).result;\n } catch (cause) {\n json(400, cause instanceof Error ? cause.message : String(cause));\n return;\n }\n if (started !== generation) {\n json(409, 'Source changed during save.', { refused: 'busy' });\n return;\n }\n const edits = {\n sourceHash: upload.sourceHash,\n base: upload.source.bytes,\n bytes: upload.bytes,\n };\n await store.write(edits);\n saved = edits;\n update(result);\n json(200, undefined, uploaded(received, upload));\n notify();\n });\n } catch (cause) {\n if (!response.headersSent) json(500, cause instanceof Error ? cause.message : String(cause));\n }\n }\n // Behind a flag until every property the editor can change has a DSL prop\n // (docs/tsx-write-back.md); until then the sidecar alone holds edits.\n const sourceSync =\n process.env.OFFICE_KIT_TSX_WRITE_BACK === '1'\n ? createSourceSync(resolve(entry), () => source, verify)\n : undefined;\n const textEditor = createTextEditor(\n resolve(entry),\n () => {\n if (!latest || building || pending || timer || error)\n throw new Error('Wait for a successful preview build.');\n return { ...latest, revision };\n },\n verify,\n );\n const server = createServer((request, response) => {\n const host = request.headers.host;\n if (host !== `127.0.0.1:${actualPort}` && host !== `localhost:${actualPort}`) {\n response.writeHead(403).end();\n return;\n }\n response.setHeader('Cache-Control', 'no-store');\n const match = request.url?.match(\n /^\\/agents\\/([\\w-]{1,64})(\\/(?:chat(?:\\/(?:stop|reset))?|terminal\\/(?:start|input|prompt|resize|stop|events|context|verify)|close))?$/,\n );\n if (request.url?.startsWith('/agents/') && !match) {\n response.writeHead(404).end();\n return;\n }\n const mediaMatch = request.url?.match(/^\\/media\\/([a-f0-9]{64})(?:\\?.*)?$/);\n if (mediaMatch) {\n if (request.method !== 'GET') {\n response.writeHead(405).end();\n return;\n }\n const cached = mediaCache.get(mediaMatch[1]!);\n if (!cached) {\n response.writeHead(404).end();\n return;\n }\n const range = mediaRange(request.headers.range, cached.bytes.length);\n const immutableHeaders = {\n 'Accept-Ranges': 'bytes',\n 'Cache-Control': 'public, max-age=31536000, immutable',\n 'Content-Type': cached.contentType,\n };\n if (!range) {\n response\n .writeHead(416, {\n ...immutableHeaders,\n 'Content-Range': `bytes */${cached.bytes.length}`,\n })\n .end();\n return;\n }\n const bytes = cached.bytes.subarray(range.start, range.end + 1);\n response.writeHead(range.partial ? 206 : 200, {\n ...immutableHeaders,\n 'Content-Length': bytes.length,\n ...(range.partial\n ? { 'Content-Range': `bytes ${range.start}-${range.end}/${cached.bytes.length}` }\n : {}),\n });\n response.end(bytes);\n return;\n }\n if (request.url === '/history' && request.method === 'GET') {\n response\n .writeHead(200, { 'Content-Type': 'application/json' })\n .end(JSON.stringify(history.state()));\n return;\n }\n if (\n request.url === '/text-edit' ||\n request.url === '/history/undo' ||\n request.url === '/history/redo'\n ) {\n if (\n request.method !== 'POST' ||\n request.headers.origin !== `http://${host}` ||\n request.headers['content-type'] !== 'application/json'\n ) {\n response.writeHead(403).end();\n return;\n }\n void (async () => {\n let body = '';\n request.setEncoding('utf8');\n for await (const chunk of request) {\n body += chunk;\n if (Buffer.byteLength(body) > 100000) throw new Error('Request too large');\n }\n if (request.url !== '/text-edit') {\n await history.move(request.url === '/history/undo' ? 'undo' : 'redo');\n const buildError = await verify();\n response\n .writeHead(200, { 'Content-Type': 'application/json' })\n .end(JSON.stringify({ ...history.state(), buildError }));\n return;\n }\n const manualReview = createVisualReviewer(resolve(entry), () => latest?.slides ?? []);\n manualReview.begin();\n const editId = 'text:' + ++textEdits;\n await history.begin(editId, 'Text edit');\n try {\n await textEditor.edit(JSON.parse(body));\n } finally {\n await history.end(editId);\n }\n let review;\n let reviewError;\n try {\n review = await manualReview.next();\n if (review) {\n capturedReviews.set(review.prompt, review.images);\n if (capturedReviews.size > 64)\n capturedReviews.delete(capturedReviews.keys().next().value!);\n }\n } catch (cause) {\n reviewError = cause instanceof Error ? cause.message : String(cause);\n }\n response\n .writeHead(200, { 'Content-Type': 'application/json' })\n .end(JSON.stringify({ review, reviewError }));\n })().catch((cause) =>\n response\n .writeHead(400, { 'Content-Type': 'application/json' })\n .end(JSON.stringify({ error: cause instanceof Error ? cause.message : String(cause) })),\n );\n return;\n }\n const id = match?.[1] ?? '';\n if (match) {\n if (\n request.headers['sec-fetch-site'] === 'cross-site' ||\n (request.headers.origin && request.headers.origin !== `http://${host}`)\n ) {\n response.writeHead(403).end();\n return;\n }\n if (sessions.get(id)?.closing) {\n response.writeHead(409).end('{\"error\":\"Agent is closing\"}');\n return;\n }\n if (match[2] === '/close') {\n if (\n request.method !== 'POST' ||\n request.headers.origin !== `http://${host}` ||\n request.headers['content-type'] !== 'application/json'\n ) {\n response.writeHead(403).end();\n return;\n }\n request.resume();\n const current = sessions.get(id);\n void (async () => {\n if (current) {\n current.closing = true;\n await Promise.all([current.terminal.close(), current.chat.close()]);\n sessions.delete(id);\n }\n response.writeHead(200).end('{}');\n })().catch(() => response.writeHead(500).end());\n return;\n }\n if (!sessions.has(id) && sessions.size >= 8) {\n response.writeHead(429).end('Too many agent sessions. Close an existing pane.');\n return;\n }\n if (!match[2]) {\n if (request.method !== 'GET') {\n response.writeHead(405).end();\n return;\n }\n session(id);\n response.writeHead(200, { 'Content-Type': 'text/html' }).end(agentPage);\n return;\n }\n if (!sessions.has(id)) {\n response.writeHead(404).end();\n return;\n }\n request.url = match[2];\n }\n\n if (\n request.url === '/chat' ||\n request.url?.startsWith('/chat/') ||\n request.url?.startsWith('/terminal/')\n ) {\n const current = session(id);\n const handler = request.url.startsWith('/terminal/') ? current.terminal : current.chat;\n void handler.handle(request, response, (index, viewedRevision) => {\n if (viewedRevision !== revision)\n throw new Error('Preview changed. Review the current slide and send again.');\n if (index !== null && (!latest || index >= latest.slides.length))\n throw new Error('Slide no longer exists.');\n return {\n slide: index === null ? null : index + 1,\n count: latest?.slides.length ?? 0,\n revision,\n text: index === null ? '' : (latest?.slideTexts[index] ?? '').slice(0, 12000),\n entry: resolve(entry),\n files: latest?.dependencies ?? [resolve(entry)],\n buildError: error,\n };\n });\n } else if (\n request.method === 'GET' &&\n // A retry asks for the player again with a query, so the browser fetches\n // it rather than handing back the module load that failed.\n BUNDLED_ASSETS[(request.url ?? '').split('?')[0]!] !== undefined\n ) {\n const asset = BUNDLED_ASSETS[(request.url ?? '').split('?')[0]!]!;\n void readFile(new URL(asset, import.meta.url)).then(\n (bytes) => {\n response.writeHead(200, {\n 'Content-Type': asset.endsWith('.css') ? 'text/css' : 'text/javascript',\n });\n response.end(bytes);\n },\n () => response.writeHead(500).end('Preview assets unavailable. Rebuild pptx-dev.'),\n );\n } else if (request.method === 'GET' && EDITOR_CHUNK.test(request.url ?? '')) {\n void readFile(new URL(`.${request.url}`, import.meta.url)).then(\n (bytes) => response.writeHead(200, { 'Content-Type': 'text/javascript' }).end(bytes),\n () => response.writeHead(404).end(),\n );\n } else if (request.url?.startsWith('/editor/') && request.method !== 'GET') {\n void edit(request, response);\n } else if (request.method === 'GET' && request.url === '/editor/state') {\n response\n .writeHead(200, { 'Content-Type': 'application/json' })\n .end(JSON.stringify(editorState()));\n } else if (\n request.method === 'GET' &&\n (request.url === '/editor/base' || request.url?.startsWith('/editor/document?revision='))\n ) {\n // What the editor merges against: the source build the saved edits were\n // made from, or a document it loaded earlier.\n const bytes =\n request.url === '/editor/base'\n ? saved?.base\n : documents.get(\n new URL(request.url, 'http://localhost').searchParams.get('revision') ?? '',\n );\n if (!bytes) {\n response.writeHead(404).end();\n return;\n }\n response.writeHead(200, { 'Content-Type': 'application/octet-stream' });\n response.end(bytes);\n } else if (\n request.method === 'GET' &&\n ['/editor/document', '/editor/source'].includes(request.url ?? '')\n ) {\n const result = request.url === '/editor/source' ? source : latest;\n if (!result) {\n response.writeHead(503).end();\n return;\n }\n response.writeHead(200, {\n 'Content-Type': 'application/octet-stream',\n ETag: editorState().revision,\n });\n response.end(result.bytes);\n } else if (request.method !== 'GET') {\n response.writeHead(405).end();\n } else if (request.url === '/events') {\n response.writeHead(200, { 'Content-Type': 'text/event-stream' });\n response.write('data: ready\\n\\n');\n clients.add(response);\n request.on('close', () => clients.delete(response));\n } else if (request.url === '/state' || request.url?.startsWith('/state?')) {\n const since = new URL(request.url, 'http://localhost').searchParams.get('since');\n const incremental =\n since !== null && (since === String(revision) || since === String(revision - 1));\n response.writeHead(200, { 'Content-Type': 'application/json' });\n response.end(\n JSON.stringify({\n revision,\n building,\n ...(incremental\n ? {\n changes: since === String(revision) ? {} : patch,\n count: latest?.slides.length ?? 0,\n }\n : { slides: latest?.slides ?? [] }),\n aspectRatio: latest?.aspectRatio ?? 16 / 9,\n transitions: latest?.transitions ?? [],\n animations: latest?.animations ?? [],\n media: publishedMedia,\n showProperties: latest?.showProperties ?? null,\n customShows: latest?.customShows ?? [],\n notes: latest?.notes ?? [],\n hiddenSlides: latest?.hiddenSlides ?? [],\n error,\n diagnostics: latest?.diagnostics ?? [],\n }),\n );\n } else if (request.url === '/deck.pptx' && latest) {\n response.writeHead(200, {\n 'Content-Type': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n 'Content-Disposition': 'attachment; filename=\"deck.pptx\"',\n });\n response.end(latest.bytes);\n } else if (request.url === '/editor') {\n response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n response.end(\n '<!doctype html><html><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width\"><title>Presentation editor</title><body><script type=\"module\" src=\"/editor.js\"></script></body></html>',\n );\n } else if (request.url === '/presenter') {\n response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n response.end(presenterPage);\n } else if (request.url === '/') {\n response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n response.end(page);\n } else {\n response.writeHead(404).end();\n }\n });\n async function verify() {\n // A fresh build also covers writes whose watcher notification has not arrived yet.\n void rebuild();\n const deadline = Date.now() + 30000;\n while (!closed && (building || timer || pending)) {\n if (Date.now() >= deadline)\n return 'Preview verification timed out. Inspect the build before claiming success.';\n await new Promise((done) => setTimeout(done, 50));\n }\n return closed ? 'Preview server closed.' : error;\n }\n async function rebuild() {\n if (closed) return;\n if (building) {\n pending = true;\n return;\n }\n building = true;\n const started = generation;\n for (const client of clients) client.write('data: building\\n\\n');\n try {\n const result = await builder.build();\n await publish(async () => {\n if (started !== generation || closed) return;\n const nextHash = sourceFingerprint(result.bytes);\n let unmerged: DeckConflict[] = [];\n // `null` when the merged edits are exactly the new source.\n let merged: (SavedEdits & { base: Uint8Array }) | null | undefined;\n if (saved?.base && saved.sourceHash !== nextHash) {\n const merge = mergeDecks(saved.base, saved.bytes, result.bytes);\n if (!merge.ok) unmerged = merge.conflicts;\n else if (sourceFingerprint(merge.bytes) === nextHash) merged = null;\n else merged = { sourceHash: nextHash, base: result.bytes, bytes: merge.bytes };\n }\n const edits = merged === undefined ? saved : (merged ?? undefined);\n const edited = edits ? (await renderDeck(edits.bytes, result.dependencies)).result : result;\n if (started !== generation || closed) return;\n if (merged) await store.write(merged);\n else if (merged === null) await store.clear();\n saved = edits;\n source = result;\n sourceHash = nextHash;\n conflicts = unmerged;\n update(edited);\n error = null;\n });\n } catch (cause) {\n if (started === generation)\n error = cause instanceof Error ? (cause.stack ?? cause.message) : String(cause);\n } finally {\n building = false;\n void history.capture().catch(() => {\n /* The history endpoint exposes capture failures. */\n });\n for (const client of clients) client.write('data: updated\\n\\n');\n if (pending && !timer) {\n pending = false;\n void rebuild();\n }\n }\n }\n let timer: ReturnType<typeof setTimeout> | undefined;\n const root = dirname(resolve(entry));\n const watcher = watch(root, { recursive: true }, (_, filename) => {\n if (\n !filename ||\n filename\n .split(sep)\n .some((part) => ['node_modules', '.git', 'dist', '.office-kit'].includes(part))\n )\n return;\n if (!historyFile(filename)) return;\n generation++;\n void builder.cancel();\n clearTimeout(timer);\n timer = setTimeout(() => {\n timer = undefined;\n pending = false;\n void rebuild();\n }, 30);\n });\n const builder = createDeckBuilder(entry);\n let actualPort = port;\n try {\n await new Promise<void>((resolveListen, reject) => {\n server.once('error', reject);\n server.listen(port, '127.0.0.1', () => {\n server.off('error', reject);\n resolveListen();\n });\n });\n } catch (cause) {\n watcher.close();\n await builder.close();\n throw cause;\n }\n const address = server.address();\n if (address && typeof address !== 'string') actualPort = address.port;\n await rebuild();\n return {\n url: `http://127.0.0.1:${actualPort}`,\n async close() {\n closed = true;\n clearTimeout(timer);\n watcher.close();\n await Promise.all(\n [...sessions.values()].flatMap(({ terminal, chat }) => [terminal.close(), chat.close()]),\n );\n await builder.close();\n await publishing;\n for (const client of clients) client.end();\n await new Promise<void>((done, reject) =>\n server.close((cause) => (cause ? reject(cause) : done())),\n );\n },\n };\n}\n","#!/usr/bin/env node\nimport { resolve } from 'node:path';\nimport { exportDeck } from './index.ts';\nimport { serveDeck } from './server.ts';\nimport { initProject } from './init.ts';\nimport { inspectTemplate } from './inspect.ts';\n\nconst usage = `Usage: office-pptx init <new-directory>\n office-pptx dev <deck.tsx> [--port 4173]\n office-pptx build <deck.tsx> [--out deck.pptx]\n office-pptx inspect <template.pptx>`;\nconst [command, entry, ...args] = process.argv.slice(2);\ntry {\n if (command === '--help' || command === '-h') {\n console.log(usage);\n } else {\n if (!entry) throw new Error(usage);\n const option = command === 'build' ? '--out' : command === 'dev' ? '--port' : undefined;\n if (args.length && (args.length !== 2 || !option || args[0] !== option || !args[1])) {\n throw new Error(`Unexpected arguments: ${args.join(' ')}\\n${usage}`);\n }\n if (command === 'init') {\n const directory = await initProject(entry);\n console.log(\n `Created ${directory}\\nInside that directory, run npm install once.\\nStart development: npm run dev (or npx office-pptx dev deck.tsx).`,\n );\n } else if (command === 'inspect') {\n console.log(JSON.stringify(await inspectTemplate(entry), null, 2));\n } else if (command === 'build') {\n const output = resolve(args[1] ?? 'deck.pptx');\n const result = await exportDeck(entry, output);\n console.log(`Wrote ${output} (${result.slides.length} slides)`);\n } else if (command === 'dev') {\n const port = args[1] === undefined ? 4173 : Number(args[1]);\n if (!Number.isInteger(port) || port < 0 || port > 65535)\n throw new Error('Port must be an integer between 0 and 65535.');\n const server = await serveDeck(entry, port);\n console.log(`Preview: ${server.url}`);\n const stop = () => {\n void server.close().then(() => process.exit(0));\n };\n process.once('SIGINT', stop);\n process.once('SIGTERM', stop);\n } else {\n throw new Error(usage);\n }\n }\n} catch (cause) {\n console.error(cause instanceof Error ? cause.message : cause);\n process.exitCode = 1;\n}\n"],"mappings":"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"}
|