mastermind-md 0.1.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.
Files changed (52) hide show
  1. package/.claude/skills/master/SKILL.md +10 -0
  2. package/.claude/skills/mastermind/SKILL.md +61 -0
  3. package/.claude/skills/mastermind/reference/demo.md +35 -0
  4. package/LICENSE +21 -0
  5. package/README.md +135 -0
  6. package/assets/agent/global.md +12 -0
  7. package/bin/mastermind.js +17 -0
  8. package/dist/cli/index.js +1284 -0
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/server/index.js +1752 -0
  11. package/dist/server/index.js.map +1 -0
  12. package/dist/ui/assets/FindBar-CSKdPrxm.js +1 -0
  13. package/dist/ui/assets/RenameDialog-C6yP_6D8.js +1 -0
  14. package/dist/ui/assets/SettingsPanel-C6-wwvJr.js +1 -0
  15. package/dist/ui/assets/SourceEditor-DN44HaIZ.js +37 -0
  16. package/dist/ui/assets/TranslatedView-G29rKesM.js +1 -0
  17. package/dist/ui/assets/index-Bhf9eUP2.css +1 -0
  18. package/dist/ui/assets/index-cMAuoyhV.js +99 -0
  19. package/dist/ui/assets/jsx-runtime-BrnrUjgG.js +1 -0
  20. package/dist/ui/assets/react-DoK4WR2u.js +1 -0
  21. package/dist/ui/index.html +16 -0
  22. package/package.json +91 -0
  23. package/themes/carbon/theme.json +11 -0
  24. package/themes/carbon/tokens.css +67 -0
  25. package/themes/cobalt/theme.json +11 -0
  26. package/themes/cobalt/tokens.css +67 -0
  27. package/themes/fonts/BricolageGrotesque-Variable.woff2 +0 -0
  28. package/themes/fonts/CrimsonPro-Variable.woff2 +0 -0
  29. package/themes/fonts/Fraunces-Variable.woff2 +0 -0
  30. package/themes/fonts/GeistSans-Variable.woff2 +0 -0
  31. package/themes/fonts/HankenGrotesk-Variable.woff2 +0 -0
  32. package/themes/fonts/Inter-Variable.woff2 +0 -0
  33. package/themes/fonts/JetBrainsMono-Variable.woff2 +0 -0
  34. package/themes/fonts/Lora-Variable.woff2 +0 -0
  35. package/themes/fonts/Manrope-Variable.woff2 +0 -0
  36. package/themes/fonts/Newsreader-Variable.woff2 +0 -0
  37. package/themes/fonts/Outfit-Variable.woff2 +0 -0
  38. package/themes/fonts/SpaceGrotesk-Variable.woff2 +0 -0
  39. package/themes/fonts/SplineSansMono-Variable.woff2 +0 -0
  40. package/themes/fonts/UbuntuSansMono-Variable.woff2 +0 -0
  41. package/themes/grid/fonts/GeistMono-Variable.woff2 +0 -0
  42. package/themes/grid/fonts/SchibstedGrotesk-Variable.woff2 +0 -0
  43. package/themes/grid/theme.json +11 -0
  44. package/themes/grid/tokens.css +67 -0
  45. package/themes/nacht/theme.json +11 -0
  46. package/themes/nacht/tokens.css +67 -0
  47. package/themes/rose/theme.json +11 -0
  48. package/themes/rose/tokens.css +67 -0
  49. package/themes/sepia/theme.json +11 -0
  50. package/themes/sepia/tokens.css +67 -0
  51. package/themes/slate/theme.json +11 -0
  52. package/themes/slate/tokens.css +67 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/server/index.ts","../../package.json","../../src/shared/constants.ts","../../src/server/app.ts","../../src/server/config.ts","../../src/server/paths.ts","../../src/server/contain.ts","../../src/server/feedback.ts","../../src/server/files.ts","../../src/server/handback.ts","../../src/shared/critic/scanner.ts","../../src/shared/markdown/processor.ts","../../src/shared/markdown/exclusions.ts","../../src/shared/summary.ts","../../src/server/browsers.ts","../../src/server/themes.ts","../../src/server/log.ts","../../src/shared/blocks.ts","../../src/server/assist/index.ts","../../src/server/translate/cache.ts","../../src/server/translate/index.ts","../../src/server/watch.ts","../../src/shared/edits.ts","../../src/shared/critic/resolve.ts","../../src/shared/critic/suggest.ts","../../src/server/static.ts","../../src/server/sessions.ts","../../src/server/workspaces.ts","../../src/server/statefile.ts"],"sourcesContent":["import { serve } from '@hono/node-server'\nimport type { Hono } from 'hono'\nimport fsp from 'node:fs/promises'\nimport type { Server } from 'node:http'\nimport pkg from '../../package.json'\nimport { DAEMON_IDLE_EXIT_MS, DEFAULT_PORT, PORT_SCAN_LIMIT } from '../shared/constants'\nimport { createApp } from './app'\nimport { AssistRegistry } from './assist/index'\nimport { log } from './log'\nimport { SessionRegistry } from './sessions'\nimport { WorkspaceRegistry } from './workspaces'\nimport { clearServerState, readServerState, writeServerState } from './statefile'\nimport { startWatcher, stopWatcher } from './watch'\n\nfunction listenOnce(app: Hono, port: number): Promise<Server> {\n return new Promise((resolve, reject) => {\n const server = serve({ fetch: app.fetch, port, hostname: '127.0.0.1' }, () => resolve(server as Server)) as Server\n server.on('error', reject)\n })\n}\n\nasync function listenWithScan(app: Hono, base: number, attempts: number): Promise<{ server: Server; port: number }> {\n let lastErr: unknown\n for (let port = base; port < base + attempts; port++) {\n try {\n const server = await listenOnce(app, port)\n return { server, port }\n } catch (err) {\n lastErr = err\n if ((err as NodeJS.ErrnoException).code === 'EADDRINUSE') continue\n throw err\n }\n }\n throw lastErr instanceof Error ? lastErr : new Error('no free port found')\n}\n\nasync function isHealthyMastermind(port: number): Promise<boolean> {\n try {\n const res = await fetch(`http://127.0.0.1:${port}/api/health`, { signal: AbortSignal.timeout(1000) })\n if (!res.ok) return false\n const j = (await res.json()) as { ok?: boolean; pid?: number }\n return j.ok === true && typeof j.pid === 'number'\n } catch {\n return false\n }\n}\n\nasync function main(): Promise<void> {\n const pinned = process.env.MASTERMIND_PINNED === '1'\n const base = Number(process.env.MASTERMIND_PORT || '') || DEFAULT_PORT\n const startedAt = Date.now()\n let lastActivity = Date.now()\n\n const registry = new SessionRegistry({\n graceMs: Number(process.env.MASTERMIND_GRACE_MS || '') || undefined,\n neverOpenedMs: Number(process.env.MASTERMIND_NEVER_OPENED_MS || '') || undefined,\n })\n const assist = new AssistRegistry(registry)\n const workspaces = new WorkspaceRegistry()\n registry.onChange = () => {\n lastActivity = Date.now()\n }\n // tree dots: a session opening/closing or an agent attaching changes the\n // open/agent state of a file that a workspace tree may be showing\n registry.onSessionActivity = (session) => workspaces.notifyForPath(session.path, 'file-badge-changed')\n registry.onSessionOpened = (session) => {\n startWatcher(session, registry)\n workspaces.notifyForPath(session.path, 'file-badge-changed')\n }\n registry.onSessionClosed = (session) => {\n assist.cancelForSession(session.id)\n void stopWatcher(session)\n workspaces.notifyForPath(session.path, 'file-badge-changed')\n if (session.isDraft) {\n // abandoned blank draft → remove the placeholder\n void fsp\n .stat(session.path)\n .then((st) => (st.size === 0 ? fsp.unlink(session.path) : undefined))\n .catch(() => {})\n }\n }\n\n let shuttingDown = false\n let server: Server | null = null\n const shutdown = (reason: string): void => {\n if (shuttingDown) return\n shuttingDown = true\n log(`shutting down (${reason})`)\n registry.closeAll('shutdown')\n clearServerState(process.pid)\n server?.close()\n // SSE streams keep sockets open; don't wait on them forever\n setTimeout(() => process.exit(0), 500).unref()\n }\n\n const app = createApp({\n registry,\n workspaces,\n assist,\n version: pkg.version,\n startedAt,\n requestShutdown: shutdown,\n touch: () => {\n lastActivity = Date.now()\n },\n })\n\n const bound = await listenWithScan(app, base, pinned ? 1 : PORT_SCAN_LIMIT)\n server = bound.server\n\n // Spawn race: if another healthy daemon already owns the statefile, defer to it.\n const existing = readServerState()\n if (existing && existing.pid !== process.pid && (await isHealthyMastermind(existing.port))) {\n log(`another daemon is already healthy on port ${existing.port} — exiting`)\n server.close()\n process.exit(0)\n }\n\n writeServerState({ port: bound.port, pid: process.pid, version: pkg.version, startedAt })\n log(`mastermind v${pkg.version} listening on http://127.0.0.1:${bound.port} (pid ${process.pid})`)\n\n process.on('SIGTERM', () => shutdown('SIGTERM'))\n process.on('SIGINT', () => shutdown('SIGINT'))\n\n setInterval(() => {\n const idleFor = Date.now() - lastActivity\n // A tree open with no file selected has zero sessions but a live workspace\n // SSE conn — don't quit out from under that tab.\n if (registry.count() === 0 && workspaces.activeUiConnCount() === 0 && idleFor > DAEMON_IDLE_EXIT_MS) {\n shutdown(`idle for ${Math.round(idleFor / 60_000)} min with no sessions`)\n }\n }, 60_000).unref()\n}\n\nmain().catch((err: unknown) => {\n log('fatal:', err)\n process.exit(1)\n})\n","{\n \"name\": \"mastermind-md\",\n \"version\": \"0.1.0\",\n \"type\": \"module\",\n \"description\": \"Review Markdown with your coding agent — local-first, CriticMarkup review loop, bilingual, the file is the protocol\",\n \"license\": \"MIT\",\n \"author\": \"Kevin Ding <kevincentding@gmail.com>\",\n \"homepage\": \"https://github.com/Jingquank/Mastermind#readme\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/Jingquank/Mastermind.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/Jingquank/Mastermind/issues\"\n },\n \"keywords\": [\n \"markdown\",\n \"review\",\n \"criticmarkup\",\n \"ai-agents\",\n \"claude\",\n \"cli\",\n \"bilingual\",\n \"local-first\"\n ],\n \"engines\": {\n \"node\": \">=20\"\n },\n \"bin\": {\n \"mastermind\": \"bin/mastermind.js\",\n \"mastermind-md\": \"bin/mastermind.js\"\n },\n \"files\": [\n \"dist/\",\n \"bin/\",\n \"themes/\",\n \".claude/skills/\",\n \"assets/agent/\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"rm -rf dist && vite build && tsup\",\n \"dev\": \"concurrently -k \\\"MASTERMIND_PORT=5199 tsx watch src/server/index.ts\\\" \\\"vite\\\"\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest\",\n \"typecheck\": \"tsc -p tsconfig.json && tsc -p tsconfig.node.json\",\n \"prepare\": \"npm run build\",\n \"prepack\": \"npm run build\"\n },\n \"dependencies\": {\n \"@codemirror/lang-markdown\": \"^6.5.0\",\n \"@codemirror/language\": \"^6.12.3\",\n \"@codemirror/state\": \"^6.6.0\",\n \"@codemirror/view\": \"^6.43.1\",\n \"@hono/node-server\": \"^2.0.4\",\n \"@lezer/highlight\": \"^1.2.3\",\n \"@radix-ui/react-icons\": \"^1.3.2\",\n \"chokidar\": \"^5.0.0\",\n \"codemirror\": \"^6.0.2\",\n \"commander\": \"^15.0.0\",\n \"diff\": \"^9.0.0\",\n \"hono\": \"^4.12.25\",\n \"mdast-util-to-markdown\": \"^2.1.2\",\n \"radix-ui\": \"^1.5.0\",\n \"react\": \"^19.2.7\",\n \"react-dom\": \"^19.2.7\",\n \"remark-gfm\": \"^4.0.1\",\n \"remark-parse\": \"^11.0.0\",\n \"remark-stringify\": \"^11.0.0\",\n \"slot-text\": \"^0.2.2\",\n \"unified\": \"^11.0.5\",\n \"unist-util-visit\": \"^5.1.0\",\n \"zustand\": \"^5.0.14\"\n },\n \"devDependencies\": {\n \"@types/mdast\": \"^4.0.4\",\n \"@types/node\": \"^25.9.3\",\n \"@types/react\": \"^19.2.17\",\n \"@types/react-dom\": \"^19.2.3\",\n \"@types/unist\": \"^3.0.3\",\n \"@vitejs/plugin-react\": \"^6.0.2\",\n \"concurrently\": \"^10.0.3\",\n \"jsdom\": \"^29.1.1\",\n \"tsup\": \"^8.5.1\",\n \"tsx\": \"^4.22.4\",\n \"typescript\": \"^6.0.3\",\n \"vite\": \"^8.0.16\",\n \"vitest\": \"^4.1.8\"\n }\n}\n","export const APP_NAME = 'mastermind'\n\n/** Default port per spec (\"5173 or next free\"); server.json is the sole port authority. */\nexport const DEFAULT_PORT = 5173\nexport const PORT_SCAN_LIMIT = 20\n\nexport const SSE_PING_INTERVAL_MS = 15_000\nexport const SESSION_CLOSE_GRACE_MS = 30_000\nexport const SESSION_NEVER_OPENED_MS = 120_000\nexport const DAEMON_IDLE_EXIT_MS = 30 * 60_000\n\n/** Until the settings panel (M9) makes it configurable. */\nexport const DEFAULT_AUTHOR_TAG = 'ke'\n\n/** How long an agent-channel request waits for the agent to answer. */\nexport const ASSIST_TIMEOUT_MS = 120_000\n","import { Hono } from 'hono'\nimport { streamSSE } from 'hono/streaming'\nimport fs from 'node:fs/promises'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { SSE_PING_INTERVAL_MS } from '../shared/constants'\nimport type {\n CreateSessionRequest,\n CreateSessionResponse,\n CreateWorkspaceRequest,\n CreateWorkspaceResponse,\n FileMeta,\n FileResponse,\n HealthResponse,\n ReviewCounts,\n SessionMeta,\n SseEventName,\n TreeEntry,\n TreeResponse,\n WorkspaceMeta,\n WorkspaceSessionInfo,\n} from '../shared/types'\nimport { readConfig, redactConfig, updateConfig, type ConfigPatch } from './config'\nimport { relWithin, resolveWithin } from './contain'\nimport { processFeedbackLanguage } from './feedback'\nimport { writeSessionFile } from './files'\nimport { performHandback } from './handback'\nimport { detectBrowsers } from './browsers'\nimport { scanThemes } from './themes'\nimport { translateBlocks, type TranslateRequestBlock } from './translate/index'\nimport { startWatcher, stopWatcher } from './watch'\nimport { AssistError, type AssistRegistry } from './assist/index'\nimport { group, reviewCounts, scan } from '../shared/critic/scanner'\nimport { codeRanges } from '../shared/markdown/exclusions'\nimport { stripSummary } from '../shared/summary'\nimport { validateSuggestion } from '../shared/critic/suggest'\nimport { log } from './log'\nimport { themesDir, uiDir } from './paths'\nimport { type Conn, type ConnRole, SessionRegistry } from './sessions'\nimport { serveFile } from './static'\nimport type { WorkspaceRegistry } from './workspaces'\nimport type { AssistResultPayload } from '../shared/types'\n\nexport interface AppDeps {\n registry: SessionRegistry\n workspaces: WorkspaceRegistry\n assist: AssistRegistry\n version: string\n startedAt: number\n requestShutdown: (reason: string) => void\n touch: () => void\n}\n\nconst LOCAL_HOST_RE = /^(127\\.0\\.0\\.1|localhost|\\[::1\\])(:\\d+)?$/\nconst LOCAL_ORIGIN_RE = /^https?:\\/\\/(127\\.0\\.0\\.1|localhost|\\[::1\\])(:\\d+)?$/\n\n/** Tree entries never shown: VCS/tool dirs, deps, and every dotfile. */\nconst WORKSPACE_DENYLIST = new Set(['node_modules', '.git', '.mastermind'])\nfunction isHiddenEntry(name: string): boolean {\n return name.startsWith('.') || WORKSPACE_DENYLIST.has(name)\n}\n\n/**\n * Lazy per-file review badge, cached by (realpath, mtimeMs) so a static tree\n * doesn't re-scan on every viewport pass. Uses the exact handback count\n * pipeline so a badge matches what the reader and summary report.\n */\nconst fileMetaCache = new Map<string, FileMeta>()\nasync function workspaceFileMeta(rel: string, real: string, mtimeMs: number): Promise<FileMeta> {\n const key = `${real}\u0000${mtimeMs}`\n const hit = fileMetaCache.get(key)\n if (hit) return hit\n let counts: ReviewCounts | null = null\n if (real.toLowerCase().endsWith('.md')) {\n try {\n const body = stripSummary(await fs.readFile(real, 'utf8'))\n counts = reviewCounts(group(scan(body, codeRanges(body)), body))\n } catch {\n counts = null\n }\n }\n const meta: FileMeta = { rel, counts, mtimeMs }\n fileMetaCache.set(key, meta)\n return meta\n}\n\nexport function createApp(deps: AppDeps): Hono {\n const { registry, workspaces, assist } = deps\n const app = new Hono()\n\n // Guards: localhost-only Host (DNS rebinding) and same-machine Origin for writes (CSRF).\n app.use('*', async (c, next) => {\n deps.touch()\n const host = c.req.header('host') ?? ''\n if (!LOCAL_HOST_RE.test(host)) return c.text('forbidden host', 403)\n const origin = c.req.header('origin')\n if (origin && c.req.method !== 'GET' && !LOCAL_ORIGIN_RE.test(origin)) {\n return c.text('forbidden origin', 403)\n }\n await next()\n })\n\n app.get('/api/health', (c) => {\n const body: HealthResponse = {\n ok: true,\n pid: process.pid,\n version: deps.version,\n startedAt: deps.startedAt,\n }\n return c.json(body)\n })\n\n app.post('/api/admin/shutdown', (c) => {\n setTimeout(() => deps.requestShutdown('admin request'), 20)\n return c.body(null, 202)\n })\n\n app.get('/api/config', (c) => c.json(redactConfig(readConfig())))\n\n app.put('/api/config', async (c) => {\n let patch: ConfigPatch\n try {\n patch = await c.req.json<ConfigPatch>()\n } catch {\n return c.json({ error: 'invalid JSON body' }, 400)\n }\n const next = updateConfig(patch)\n for (const session of registry.all()) {\n registry.broadcast(session.id, 'config-changed', {})\n }\n return c.json(redactConfig(next))\n })\n\n app.get('/api/themes', async (c) => c.json(await scanThemes()))\n\n app.get('/api/browsers', (c) => c.json(detectBrowsers()))\n\n app.post('/api/translate', async (c) => {\n let body: { sessionId?: string; sourceLang?: string; targetLang?: string; blocks?: TranslateRequestBlock[] }\n try {\n body = await c.req.json()\n } catch {\n return c.json({ error: 'invalid JSON body' }, 400)\n }\n if (!body.sessionId || !body.targetLang || !Array.isArray(body.blocks)) {\n return c.json({ error: 'sessionId, targetLang, blocks required' }, 400)\n }\n const session = registry.get(body.sessionId)\n if (!session) return c.json({ error: 'session not found' }, 404)\n try {\n const { results, error } = await translateBlocks(\n session,\n body.blocks.slice(0, 50),\n body.sourceLang ?? 'auto-detected source language',\n body.targetLang,\n assist,\n )\n // 200 even when `error` is set: cached blocks come back regardless, and the\n // agent leg (no-agent/timeout) only fails the misses — never the whole batch.\n return c.json({ results, error })\n } catch (err) {\n const code = err instanceof AssistError ? err.code : 'provider'\n return c.json({ error: code }, 502)\n }\n })\n\n app.post('/api/sessions', async (c) => {\n let body: CreateSessionRequest\n try {\n body = await c.req.json<CreateSessionRequest>()\n } catch {\n return c.json({ error: 'invalid JSON body' }, 400)\n }\n\n let real: string\n let isDraft = false\n if (body.draft) {\n // blank draft: eagerly create untitled.md (one session model — real\n // path, real watcher); renamed on first save, unlinked if left empty\n const dir = body.dir ? path.resolve(body.dir) : os.homedir()\n const dirStat = await fs.stat(dir).catch(() => null)\n if (!dirStat?.isDirectory()) return c.json({ error: `not a directory: ${dir}` }, 400)\n let name = 'untitled.md'\n for (let n = 2; ; n++) {\n try {\n await fs.writeFile(path.join(dir, name), '', { flag: 'wx' })\n break\n } catch {\n name = `untitled-${n}.md`\n }\n }\n real = await fs.realpath(path.join(dir, name))\n isDraft = true\n } else {\n if (!body.path) return c.json({ error: 'path is required' }, 400)\n try {\n real = await fs.realpath(path.resolve(body.path))\n } catch {\n return c.json({ error: `file not found: ${body.path}` }, 404)\n }\n const st = await fs.stat(real)\n if (!st.isFile()) return c.json({ error: `not a file: ${real}` }, 400)\n }\n\n const { session, created } = registry.open(real, { isDraft })\n const resp: CreateSessionResponse = {\n sessionId: session.id,\n url: `http://${c.req.header('host')}/d/${session.id}`,\n created,\n isDraft: session.isDraft,\n }\n return c.json(resp)\n })\n\n app.get('/api/sessions/:id', async (c) => {\n const session = registry.get(c.req.param('id'))\n if (!session) return c.json({ error: 'session not found' }, 404)\n let mtimeMs = 0\n try {\n mtimeMs = (await fs.stat(session.path)).mtimeMs\n } catch {\n /* deleted on disk — still report the session */\n }\n const meta: SessionMeta = {\n sessionId: session.id,\n path: session.path,\n displayName: session.displayName,\n isDraft: session.isDraft,\n mtimeMs,\n agentWaiting: session.cliConns.size > 0,\n assistAvailable: registry.hasAssistListener(session),\n }\n return c.json(meta)\n })\n\n app.get('/api/sessions/:id/file', async (c) => {\n const session = registry.get(c.req.param('id'))\n if (!session) return c.json({ error: 'session not found' }, 404)\n try {\n const [content, st] = await Promise.all([fs.readFile(session.path, 'utf8'), fs.stat(session.path)])\n const body: FileResponse = { content, mtimeMs: st.mtimeMs }\n return c.json(body)\n } catch {\n return c.json({ error: 'file unreadable (deleted?)' }, 410)\n }\n })\n\n app.put('/api/sessions/:id/file', async (c) => {\n const session = registry.get(c.req.param('id'))\n if (!session) return c.json({ error: 'session not found' }, 404)\n let body: { content?: string; baseMtimeMs?: number }\n try {\n body = await c.req.json()\n } catch {\n return c.json({ error: 'invalid JSON body' }, 400)\n }\n if (typeof body.content !== 'string') return c.json({ error: 'content is required' }, 400)\n const processed = await processFeedbackLanguage(body.content)\n const result = await writeSessionFile(session, processed, body.baseMtimeMs)\n if (!result.ok) {\n return c.json({ error: 'file changed on disk', currentMtimeMs: result.currentMtimeMs }, 409)\n }\n // a save can change mark counts — refresh any workspace tree showing this file\n workspaces.notifyForPath(session.path, 'file-badge-changed')\n // content returned only when the feedback rule rewrote it — UI adopts\n return c.json({ mtimeMs: result.mtimeMs, content: processed !== body.content ? processed : undefined })\n })\n\n app.post('/api/sessions/:id/rename', async (c) => {\n const session = registry.get(c.req.param('id'))\n if (!session) return c.json({ error: 'session not found' }, 404)\n let body: { filename?: string }\n try {\n body = await c.req.json()\n } catch {\n return c.json({ error: 'invalid JSON body' }, 400)\n }\n let name = path.basename((body.filename ?? '').trim())\n if (!name || name === '.md' || name.startsWith('.')) return c.json({ error: 'invalid filename' }, 400)\n if (!name.endsWith('.md')) name += '.md'\n const target = path.join(path.dirname(session.path), name)\n const exists = await fs.stat(target).then(\n () => true,\n () => false,\n )\n if (exists) return c.json({ error: `${name} already exists`, code: 'exists' }, 409)\n\n await stopWatcher(session)\n await fs.rename(session.path, target)\n const real = await fs.realpath(target)\n registry.rekey(session, real)\n session.isDraft = false\n startWatcher(session, registry)\n log(`session ${session.id}: renamed to ${real}`)\n return c.json({ path: real, displayName: session.displayName })\n })\n\n app.post('/api/sessions/:id/handback', async (c) => {\n const session = registry.get(c.req.param('id'))\n if (!session) return c.json({ error: 'session not found' }, 404)\n let body: { content?: string; baseMtimeMs?: number; note?: string }\n try {\n body = await c.req.json()\n } catch {\n return c.json({ error: 'invalid JSON body' }, 400)\n }\n if (typeof body.content !== 'string') return c.json({ error: 'content is required' }, 400)\n const note = typeof body.note === 'string' ? body.note : undefined\n const result = await performHandback(session, body.content, body.baseMtimeMs, note)\n if (!result.ok) {\n return c.json({ error: 'file changed on disk', currentMtimeMs: result.currentMtimeMs }, 409)\n }\n log(`session ${session.id}: hand back — ${result.summaryLine}`)\n registry.broadcast(session.id, 'handback', {\n summaryLine: result.summaryLine,\n counts: result.counts,\n mtimeMs: result.mtimeMs,\n })\n // counts just changed on disk — refresh any workspace tree showing this file\n workspaces.notifyForPath(session.path, 'file-badge-changed')\n return c.json({\n mtimeMs: result.mtimeMs,\n counts: result.counts,\n summaryLine: result.summaryLine,\n })\n })\n\n // --- workspaces (file tree) ---\n //\n // A workspace is a read-only directory context. Every filesystem touch below\n // goes through resolveWithin() — the single containment chokepoint — so a\n // tree click can never read outside the root it was opened on.\n\n app.post('/api/workspaces', async (c) => {\n let body: CreateWorkspaceRequest\n try {\n body = await c.req.json<CreateWorkspaceRequest>()\n } catch {\n return c.json({ error: 'invalid JSON body' }, 400)\n }\n if (!body.root) return c.json({ error: 'root is required' }, 400)\n let root: string\n try {\n root = await fs.realpath(path.resolve(body.root))\n } catch {\n return c.json({ error: `directory not found: ${body.root}` }, 404)\n }\n if (!(await fs.stat(root)).isDirectory()) return c.json({ error: `not a directory: ${root}` }, 400)\n const ws = workspaces.open(root)\n const resp: CreateWorkspaceResponse = {\n workspaceId: ws.id,\n url: `http://${c.req.header('host')}/w/${ws.id}`,\n root: ws.root,\n displayName: ws.displayName,\n }\n return c.json(resp)\n })\n\n app.get('/api/workspaces/:wid', (c) => {\n const ws = workspaces.get(c.req.param('wid'))\n if (!ws) return c.json({ error: 'workspace not found' }, 404)\n const meta: WorkspaceMeta = { workspaceId: ws.id, root: ws.root, displayName: ws.displayName }\n return c.json(meta)\n })\n\n // one directory level (client recurses lazily — bounds cost on big trees)\n app.get('/api/workspaces/:wid/tree', async (c) => {\n const ws = workspaces.get(c.req.param('wid'))\n if (!ws) return c.json({ error: 'workspace not found' }, 404)\n const dir = c.req.query('dir') ?? ''\n const real = await resolveWithin(ws.root, dir)\n if (!real) return c.json({ error: 'forbidden path' }, 403)\n let dirents\n try {\n dirents = await fs.readdir(real, { withFileTypes: true })\n } catch {\n return c.json({ error: 'not a directory' }, 404)\n }\n const entries: TreeEntry[] = []\n for (const d of dirents) {\n if (isHiddenEntry(d.name)) continue\n const rel = dir ? `${dir}/${d.name}` : d.name\n let isDir = d.isDirectory()\n // a symlink must itself resolve back inside the root, or it's dropped\n if (d.isSymbolicLink()) {\n const target = await resolveWithin(ws.root, rel)\n if (!target) continue\n try {\n isDir = (await fs.stat(target)).isDirectory()\n } catch {\n continue\n }\n }\n entries.push({ name: d.name, rel, isDir, isMarkdown: !isDir && d.name.toLowerCase().endsWith('.md') })\n }\n // directories first, then case-insensitive by name\n entries.sort((a, b) => (a.isDir === b.isDir ? a.name.localeCompare(b.name) : a.isDir ? -1 : 1))\n const resp: TreeResponse = { dir, entries }\n return c.json(resp)\n })\n\n app.get('/api/workspaces/:wid/file-meta', async (c) => {\n const ws = workspaces.get(c.req.param('wid'))\n if (!ws) return c.json({ error: 'workspace not found' }, 404)\n const rel = c.req.query('path') ?? ''\n const real = await resolveWithin(ws.root, rel)\n if (!real) return c.json({ error: 'forbidden path' }, 403)\n let st\n try {\n st = await fs.stat(real)\n } catch {\n return c.json({ error: 'unreadable' }, 410)\n }\n if (!st.isFile()) return c.json({ error: 'not a file' }, 400)\n return c.json(await workspaceFileMeta(rel, real, st.mtimeMs))\n })\n\n // open sessions whose file lives under this root — drives the tree's\n // open / agent-waiting dots (a file open in a *different* tab still shows here)\n app.get('/api/workspaces/:wid/sessions', (c) => {\n const ws = workspaces.get(c.req.param('wid'))\n if (!ws) return c.json({ error: 'workspace not found' }, 404)\n const out: WorkspaceSessionInfo[] = []\n for (const s of registry.all()) {\n const rel = relWithin(ws.root, s.path)\n if (rel === null) continue\n out.push({\n rel,\n sessionId: s.id,\n agentWaiting: s.cliConns.size > 0,\n assistAvailable: registry.hasAssistListener(s),\n isDraft: s.isDraft,\n })\n }\n return c.json(out)\n })\n\n app.get('/api/workspaces/:wid/events', (c) => {\n const wid = c.req.param('wid')\n if (!workspaces.get(wid)) return c.json({ error: 'workspace not found' }, 404)\n return streamSSE(c, async (stream) => {\n let open = true\n const conn: Conn = {\n role: 'ui',\n send: async (event: SseEventName, data: unknown) => {\n await stream.writeSSE({ event, data: JSON.stringify(data) })\n },\n close: () => {\n open = false\n void stream.close()\n },\n }\n stream.onAbort(() => {\n open = false\n workspaces.detach(wid, conn)\n })\n if (!workspaces.attach(wid, conn)) {\n open = false\n return\n }\n log(`sse ui connected to workspace ${wid}`)\n while (open) {\n await stream.sleep(SSE_PING_INTERVAL_MS)\n if (!open) break\n try {\n await stream.writeSSE({ event: 'ping', data: '{}' })\n } catch {\n open = false\n }\n }\n workspaces.detach(wid, conn)\n })\n })\n\n // --- agent-channel (assist) ---\n\n // validated proposed-suggestion markup, keyed by request id (one-shot fetch by the UI)\n const suggestions = new Map<string, { markup: string }>()\n\n app.post('/api/assist', async (c) => {\n let body: { sessionId?: string; kind?: string; scope?: string; selection?: string; context?: string }\n try {\n body = await c.req.json()\n } catch {\n return c.json({ error: 'invalid JSON body' }, 400)\n }\n if (!body.sessionId || body.kind !== 'suggest' || typeof body.selection !== 'string') {\n return c.json({ error: 'sessionId, kind:suggest, selection required' }, 400)\n }\n const session = registry.get(body.sessionId)\n if (!session) return c.json({ error: 'session not found' }, 404)\n if (!assist.hasListener(session)) return c.json({ error: 'no-agent' }, 409)\n const scope = body.scope === 'section' || body.scope === 'document' ? body.scope : 'selection'\n const selection = body.selection\n const { id, result } = assist.enqueueWithId(session, { kind: 'suggest', scope, selection, context: body.context })\n result\n .then((payload) => {\n const ok = payload.kind === 'suggest' && validateSuggestion(payload.markup, selection).ok\n if (ok && payload.kind === 'suggest') suggestions.set(id, { markup: payload.markup })\n registry.broadcast(session.id, 'assist-result', { id, kind: 'suggest', ok }, ['ui'])\n })\n .catch((err: unknown) => {\n log(`assist suggest ${id.slice(0, 8)} failed: ${err instanceof AssistError ? err.code : 'error'}`)\n registry.broadcast(session.id, 'assist-result', { id, kind: 'suggest', ok: false }, ['ui'])\n })\n return c.json({ requestId: id })\n })\n\n app.get('/api/assist/:id/suggestion', (c) => {\n const s = suggestions.get(c.req.param('id'))\n if (!s) return c.json({ error: 'not found' }, 404)\n suggestions.delete(c.req.param('id')) // one-shot\n return c.json({ markup: s.markup })\n })\n\n app.get('/api/assist/pending', (c) => {\n const sessionId = c.req.query('sessionId')\n const session = sessionId ? registry.get(sessionId) : undefined\n if (!session) return c.json({ error: 'session not found' }, 404)\n return c.json({ requests: assist.pendingFor(session.id) })\n })\n\n app.post('/api/assist/:id/result', async (c) => {\n let payload: AssistResultPayload\n try {\n payload = await c.req.json<AssistResultPayload>()\n } catch {\n return c.json({ error: 'invalid JSON body' }, 400)\n }\n return assist.resolve(c.req.param('id'), payload) ? c.body(null, 204) : c.json({ error: 'unknown or expired' }, 404)\n })\n\n app.post('/api/assist/:id/error', async (c) => {\n let body: { reason?: string }\n try {\n body = await c.req.json()\n } catch {\n body = {}\n }\n return assist.fail(c.req.param('id'), body.reason ?? 'declined')\n ? c.body(null, 204)\n : c.json({ error: 'unknown or expired' }, 404)\n })\n\n app.get('/api/sessions/:id/events', (c) => {\n const sessionId = c.req.param('id')\n const role: ConnRole = c.req.query('role') === 'cli' ? 'cli' : 'ui'\n const assistCapable = role === 'cli' && c.req.query('assist') === '1'\n if (!registry.get(sessionId)) return c.json({ error: 'session not found' }, 404)\n\n return streamSSE(c, async (stream) => {\n let open = true\n const conn: Conn = {\n role,\n assistCapable,\n send: async (event: SseEventName, data: unknown) => {\n await stream.writeSSE({ event, data: JSON.stringify(data) })\n },\n close: () => {\n open = false\n void stream.close()\n },\n }\n stream.onAbort(() => {\n open = false\n registry.detach(sessionId, conn)\n })\n if (!registry.attach(sessionId, conn)) {\n open = false\n return\n }\n log(`sse ${role} connected to session ${sessionId}`)\n while (open) {\n await stream.sleep(SSE_PING_INTERVAL_MS)\n if (!open) break\n try {\n await stream.writeSSE({ event: 'ping', data: '{}' })\n } catch {\n open = false\n }\n }\n registry.detach(sessionId, conn)\n })\n })\n\n // --- static: themes, built UI, SPA fallback ---\n\n app.get('/themes/*', async (c) => {\n const rel = c.req.path.replace(/^\\/themes\\//, '')\n const res = await serveFile(themesDir, rel, 'no-cache')\n return res ?? c.notFound()\n })\n\n app.get('/assets/*', async (c) => {\n const rel = c.req.path.replace(/^\\//, '')\n const res = await serveFile(uiDir, rel, 'immutable')\n return res ?? c.notFound()\n })\n\n app.get('*', async (c) => {\n if (c.req.path.startsWith('/api/')) return c.notFound()\n // exact static file (favicon etc.), else SPA fallback to index.html\n const rel = c.req.path.replace(/^\\//, '')\n if (rel && !rel.includes('..') && path.extname(rel)) {\n const res = await serveFile(uiDir, rel, 'no-cache')\n if (res) return res\n return c.notFound()\n }\n const index = await serveFile(uiDir, 'index.html', 'no-store')\n return index ?? c.text('mastermind: UI not built — run npm run build', 500)\n })\n\n return app\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport { DEFAULT_AUTHOR_TAG } from '../shared/constants'\nimport type { ClientConfig, MastermindConfig } from '../shared/types'\nimport { configFilePath, ensureConfigDir } from './paths'\n\nexport const DEFAULT_CONFIG: MastermindConfig = {\n version: 1,\n theme: 'grid',\n fontSize: 16,\n lineHeight: 1.6,\n contentWidth: 736,\n authorTag: DEFAULT_AUTHOR_TAG,\n typeSet: 'grid',\n monoFont: 'geist',\n codeTheme: 'none',\n uiLang: 'en',\n langPair: { a: 'English', b: 'Simplified Chinese' },\n browser: '',\n grain: {},\n}\n\nexport function readConfig(): MastermindConfig {\n try {\n const raw = fs.readFileSync(configFilePath(), 'utf8')\n const parsed = JSON.parse(raw) as Partial<MastermindConfig>\n return { ...DEFAULT_CONFIG, ...parsed, version: 1 }\n } catch {\n return { ...DEFAULT_CONFIG }\n }\n}\n\nfunction persist(config: MastermindConfig): void {\n const dir = ensureConfigDir()\n const tmp = path.join(dir, `.config.json.${process.pid}.tmp`)\n fs.writeFileSync(tmp, JSON.stringify(config, null, 2))\n fs.renameSync(tmp, configFilePath())\n}\n\nexport type ConfigPatch = Partial<Omit<MastermindConfig, 'version'>>\n\nexport function updateConfig(patch: ConfigPatch): MastermindConfig {\n const next: MastermindConfig = { ...readConfig(), ...patch, version: 1 }\n persist(next)\n return next\n}\n\n/** The browser sees the whole config — there are no secrets to redact. */\nexport function redactConfig(config: MastermindConfig): ClientConfig {\n return config\n}\n","import { fileURLToPath } from 'node:url'\nimport path from 'node:path'\nimport os from 'node:os'\nimport fs from 'node:fs'\n\n/**\n * Package root, resolved relative to this module. Works from both the bundled\n * output (dist/cli/*, dist/server/*) and tsx dev runs (src/server/*) — all sit\n * exactly two levels below the repo root.\n */\nexport const pkgRoot = fileURLToPath(new URL('../../', import.meta.url))\n\nexport const uiDir = path.join(pkgRoot, 'dist', 'ui')\nexport const themesDir = path.join(pkgRoot, 'themes')\n\nexport function configDir(): string {\n return process.env.MASTERMIND_CONFIG_DIR ?? path.join(os.homedir(), '.config', 'mastermind')\n}\n\nexport function ensureConfigDir(): string {\n const dir = configDir()\n fs.mkdirSync(dir, { recursive: true })\n return dir\n}\n\nexport function stateFilePath(): string {\n return path.join(configDir(), 'server.json')\n}\n\nexport function serverLogPath(): string {\n return path.join(configDir(), 'server.log')\n}\n\nexport function configFilePath(): string {\n return path.join(configDir(), 'config.json')\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\n\n/**\n * Resolve `rel` inside `rootReal` (an already-realpath'd directory), returning\n * the realpath of the target ONLY if it stays within the root. Returns null on\n * any escape, missing target, or lexical attack.\n *\n * This is the single containment chokepoint for the workspace file-tree. A\n * traversal or symlink escape here would turn a localhost markdown tool into an\n * arbitrary-file-read server, so it stacks two independent defenses:\n *\n * 1. Lexical (before touching disk): reject absolute paths and any leading\n * `..` segment. Cheap, and it means a malicious `rel` never even reaches\n * the filesystem.\n * 2. Physical: realpath the candidate — which *follows symlinks* — and require\n * the result to be the root itself or live under `root + path.sep`. This is\n * the same idiom static.ts uses; the trailing separator is what defeats the\n * sibling-prefix attack (`/foo/bar` must not match `/foo/bar-evil`), and\n * realpath is what defeats a symlink inside the root that points outside it.\n *\n * `rel === ''` (or '.') resolves to the root itself — the tree's top level.\n */\nexport async function resolveWithin(rootReal: string, rel: string): Promise<string | null> {\n // 1. lexical defense — never let an absolute path or `..` escape reach disk\n if (path.isAbsolute(rel)) return null\n const normalized = path.normalize(rel)\n if (normalized === '..' || normalized.startsWith(`..${path.sep}`)) return null\n\n // 2. physical containment — realpath collapses symlinks to their true target,\n // so a link pointing outside the root resolves there and fails the check\n let real: string\n try {\n real = await fs.realpath(path.resolve(rootReal, normalized))\n } catch {\n return null // ENOENT / EACCES / dangling symlink — treat as not found\n }\n if (real !== rootReal && !real.startsWith(rootReal + path.sep)) return null\n return real\n}\n\n/**\n * If `realPath` is inside `rootReal`, return its forward-slashed path relative\n * to the root ('' for the root itself); otherwise null. Both inputs must be\n * already-realpath'd. Used to map an open session back onto a workspace's tree.\n */\nexport function relWithin(rootReal: string, realPath: string): string | null {\n if (realPath === rootReal) return ''\n if (!realPath.startsWith(rootReal + path.sep)) return null\n return realPath.slice(rootReal.length + 1).split(path.sep).join('/')\n}\n","/**\n * Feedback language handling used to translate comments into the document's\n * language on save, via a configurable third-party translation API. That path\n * has been removed (translation is now agent-only, and running it on every save\n * would block the save on an agent round-trip), so comments are saved exactly as\n * typed. Kept as a pass-through so the save route needs no special-casing.\n */\nexport async function processFeedbackLanguage(content: string): Promise<string> {\n return content\n}\n","import crypto from 'node:crypto'\nimport fs from 'node:fs/promises'\nimport type { Session } from './sessions'\n\nexport function sha256hex(content: string): string {\n return crypto.createHash('sha256').update(content, 'utf8').digest('hex')\n}\n\nexport type WriteResult = { ok: true; mtimeMs: number } | { ok: false; currentMtimeMs: number }\n\n/**\n * Compare-and-swap write: refuses (409 upstream) when the file's mtime no\n * longer matches what the client based its edit on. Records the content hash\n * so the watcher can swallow the echo of our own write.\n */\nexport async function writeSessionFile(\n session: Session,\n content: string,\n baseMtimeMs: number | undefined,\n): Promise<WriteResult> {\n if (baseMtimeMs !== undefined) {\n const st = await fs.stat(session.path).catch(() => null)\n if (st && Math.abs(st.mtimeMs - baseMtimeMs) > 0.001) {\n return { ok: false, currentMtimeMs: st.mtimeMs }\n }\n }\n session.lastSelfWriteHash = sha256hex(content)\n await fs.writeFile(session.path, content)\n const st = await fs.stat(session.path)\n return { ok: true, mtimeMs: st.mtimeMs }\n}\n","import fs from 'node:fs/promises'\nimport { group, reviewCounts, scan } from '../shared/critic/scanner'\nimport { codeRanges } from '../shared/markdown/exclusions'\nimport { stripSummary, summaryLine, upsertSummary } from '../shared/summary'\nimport type { ReviewCounts } from '../shared/types'\nimport { processFeedbackLanguage } from './feedback'\nimport { sha256hex } from './files'\nimport type { Session } from './sessions'\n\nexport interface HandbackResult {\n ok: true\n mtimeMs: number\n counts: ReviewCounts\n summaryLine: string\n}\n\nexport type HandbackOutcome = HandbackResult | { ok: false; currentMtimeMs: number }\n\n/**\n * The hand-back contract: strip any stale summary, count marks with the same\n * scanner the UI uses, write content + fresh summary block, and report the\n * one-line summary the CLI prints. The in-file block and the --wait stdout\n * line always agree by construction.\n */\nexport async function performHandback(\n session: Session,\n rawContent: string,\n baseMtimeMs: number | undefined,\n note?: string,\n): Promise<HandbackOutcome> {\n if (baseMtimeMs !== undefined) {\n const st = await fs.stat(session.path).catch(() => null)\n if (st && Math.abs(st.mtimeMs - baseMtimeMs) > 0.001) {\n return { ok: false, currentMtimeMs: st.mtimeMs }\n }\n }\n\n const content = await processFeedbackLanguage(rawContent)\n const body = stripSummary(content)\n const counts = reviewCounts(group(scan(body, codeRanges(body)), body))\n const final = upsertSummary(content, counts, new Date(), note)\n\n session.lastSelfWriteHash = sha256hex(final)\n await fs.writeFile(session.path, final)\n const st = await fs.stat(session.path)\n\n return { ok: true, mtimeMs: st.mtimeMs, counts, summaryLine: summaryLine(counts) }\n}\n","import type { CommentEntry, CriticKind, CriticSpan, Range, ReviewCountsDetail, ReviewItem } from './types'\n\nconst OPENERS: ReadonlyArray<readonly [string, CriticKind]> = [\n ['{++', 'ins'],\n ['{--', 'del'],\n ['{~~', 'sub'],\n ['{==', 'highlight'],\n ['{>>', 'comment'],\n]\n\nconst CLOSERS: Record<CriticKind, string> = {\n ins: '++}',\n del: '--}',\n sub: '~~}',\n highlight: '==}',\n comment: '<<}',\n}\n\n/** Start offsets of blank-line breaks (a span may not cross one). */\nfunction blankBreaks(text: string): number[] {\n const out: number[] = []\n const re = /\\r?\\n[ \\t]*\\r?\\n/g\n let m: RegExpExecArray | null\n while ((m = re.exec(text)) !== null) {\n out.push(m.index)\n re.lastIndex = m.index + 1 // overlapping blank runs all count\n }\n return out\n}\n\nfunction makeExcludeLookup(exclude: readonly Range[]): (pos: number) => Range | null {\n const sorted = [...exclude].sort((a, b) => a.start - b.start)\n return (pos: number) => {\n let lo = 0\n let hi = sorted.length - 1\n while (lo <= hi) {\n const mid = (lo + hi) >> 1\n const r = sorted[mid]!\n if (pos < r.start) hi = mid - 1\n else if (pos >= r.end) lo = mid + 1\n else return r\n }\n return null\n }\n}\n\nfunction escapedAt(text: string, bracePos: number): boolean {\n let backslashes = 0\n while (bracePos - 1 - backslashes >= 0 && text[bracePos - 1 - backslashes] === '\\\\') backslashes++\n return backslashes % 2 === 1\n}\n\n/**\n * Scan raw text for CriticMarkup spans.\n *\n * Rules: delimiters must not sit inside `exclude` ranges (code, html); span\n * content may contain newlines but never a blank line; `\\{` defeats an\n * opener; marks do not nest — the first valid closer wins.\n */\nexport function scan(text: string, exclude: readonly Range[] = []): CriticSpan[] {\n const spans: CriticSpan[] = []\n const breaks = blankBreaks(text)\n const excludeAt = makeExcludeLookup(exclude)\n let breakIdx = 0\n\n const findToken = (token: string, from: number, limit: number): number => {\n let k = from\n while (k < limit) {\n const j = text.indexOf(token, k)\n if (j === -1 || j >= limit) return -1\n const ex = excludeAt(j)\n if (ex) {\n k = ex.end\n continue\n }\n return j\n }\n return -1\n }\n\n let i = 0\n while (i < text.length) {\n const brace = text.indexOf('{', i)\n if (brace === -1) break\n i = brace\n\n const ex = excludeAt(i)\n if (ex) {\n i = ex.end\n continue\n }\n if (escapedAt(text, i)) {\n i += 1\n continue\n }\n\n const opener = OPENERS.find(([tok]) => text.startsWith(tok, i))\n if (!opener) {\n i += 1\n continue\n }\n const kind = opener[1]\n const innerStart = i + 3\n\n while (breakIdx < breaks.length && breaks[breakIdx]! < innerStart) breakIdx++\n const limit = breakIdx < breaks.length ? breaks[breakIdx]! : text.length\n\n if (kind === 'sub') {\n const firstCloser = findToken(CLOSERS.sub, innerStart, limit)\n const sep = findToken('~>', innerStart, limit)\n if (firstCloser === -1 || sep === -1 || sep >= firstCloser) {\n i += 1\n continue\n }\n spans.push({\n kind,\n start: i,\n end: firstCloser + 3,\n innerStart,\n innerEnd: firstCloser,\n oldStart: innerStart,\n oldEnd: sep,\n newStart: sep + 2,\n newEnd: firstCloser,\n })\n i = firstCloser + 3\n continue\n }\n\n const closer = findToken(CLOSERS[kind], innerStart, limit)\n if (closer === -1) {\n i += 1\n continue\n }\n spans.push({ kind, start: i, end: closer + 3, innerStart, innerEnd: closer })\n i = closer + 3\n }\n return spans\n}\n\nconst AUTHOR_RE = /^\\s*@([^\\s:@]{1,64}):\\s?/\n\n/**\n * Drop container prefixes (`> `, continuation indent) from a raw multi-line\n * span slice — they're markdown plumbing, not content.\n */\nexport function stripLinePrefixes(s: string): string {\n return s.replace(/\\n[ \\t]*(?:>[ \\t]?)*/g, '\\n')\n}\n\nexport function parseAuthor(content: string): { author: string | null; body: string } {\n const m = AUTHOR_RE.exec(content)\n if (!m) return { author: null, body: content }\n return { author: m[1]!, body: content.slice(m[0].length) }\n}\n\nfunction toEntry(span: CriticSpan, text: string): CommentEntry {\n const { author, body } = parseAuthor(stripLinePrefixes(text.slice(span.innerStart, span.innerEnd)))\n return { span, author, body }\n}\n\n/**\n * Group scanned spans into review items: ins/del/sub → suggestions;\n * `{==x==}{>>c<<}` → anchored thread; consecutive comments → one thread;\n * highlights without a trailing comment → standalone highlights.\n * Adjacency means zero characters between marks.\n */\nexport function group(spans: readonly CriticSpan[], text: string): ReviewItem[] {\n const items: ReviewItem[] = []\n let i = 0\n while (i < spans.length) {\n const s = spans[i]!\n if (s.kind === 'ins' || s.kind === 'del' || s.kind === 'sub') {\n items.push({ type: 'suggestion', span: s })\n i++\n continue\n }\n if (s.kind === 'highlight') {\n const next = spans[i + 1]\n if (!(next && next.kind === 'comment' && next.start === s.end)) {\n items.push({ type: 'highlight', span: s })\n i++\n continue\n }\n const comments: CommentEntry[] = []\n let j = i + 1\n let expectedStart = s.end\n while (spans[j] && spans[j]!.kind === 'comment' && spans[j]!.start === expectedStart) {\n comments.push(toEntry(spans[j]!, text))\n expectedStart = spans[j]!.end\n j++\n }\n items.push({ type: 'thread', anchor: s, comments, start: s.start, end: expectedStart })\n i = j\n continue\n }\n // orphan comment / thread start\n const comments: CommentEntry[] = [toEntry(s, text)]\n let j = i + 1\n let expectedStart = s.end\n while (spans[j] && spans[j]!.kind === 'comment' && spans[j]!.start === expectedStart) {\n comments.push(toEntry(spans[j]!, text))\n expectedStart = spans[j]!.end\n j++\n }\n items.push({ type: 'thread', anchor: null, comments, start: s.start, end: expectedStart })\n i = j\n }\n return items\n}\n\n/**\n * Counts for the review summary. Comments count every comment mark; edits\n * count ins+del+sub; highlights count only standalone highlights (an anchored\n * comment's highlight is the comment's anchor, not a separate annotation).\n */\nexport function reviewCounts(items: readonly ReviewItem[]): ReviewCountsDetail {\n let comments = 0\n let edits = 0\n let highlights = 0\n for (const item of items) {\n if (item.type === 'suggestion') edits++\n else if (item.type === 'highlight') highlights++\n else comments += item.comments.length\n }\n return { comments, edits, highlights }\n}\n","import type { Root } from 'mdast'\nimport remarkGfm from 'remark-gfm'\nimport remarkParse from 'remark-parse'\nimport { unified } from 'unified'\n\nconst processor = unified().use(remarkParse).use(remarkGfm).freeze()\n\n/** Parse markdown (GFM) to mdast with position offsets intact. */\nexport function parseMarkdown(text: string): Root {\n const tree = processor.parse(text)\n return processor.runSync(tree) as Root\n}\n","import type { Node, Parent } from 'unist'\nimport type { Range } from '../critic/types'\nimport { parseMarkdown } from './processor'\n\nconst EXCLUDED_TYPES = new Set(['code', 'inlineCode', 'html'])\n\n/**\n * Ranges the CriticMarkup scanner must treat as opaque: fenced/indented code,\n * inline code, and raw HTML — one notion of \"code\", derived from a plain\n * remark parse of the same text so it can never drift from rendering.\n */\nexport function codeRanges(text: string): Range[] {\n const tree = parseMarkdown(text)\n const out: Range[] = []\n const visit = (node: Node): void => {\n const start = node.position?.start?.offset\n const end = node.position?.end?.offset\n if (EXCLUDED_TYPES.has(node.type) && start !== undefined && end !== undefined) {\n out.push({ start, end })\n return\n }\n const children = (node as Parent).children\n if (children) for (const child of children) visit(child)\n }\n visit(tree)\n return out.sort((a, b) => a.start - b.start)\n}\n","import type { ReviewCountsDetail } from './critic/types'\n\nexport const SUMMARY_OPEN = '<!-- mastermind:summary -->'\nexport const SUMMARY_CLOSE = '<!-- /mastermind:summary -->'\n\nconst SUMMARY_RE = /[ \\t]*<!-- mastermind:summary -->[\\s\\S]*?<!-- \\/mastermind:summary -->[ \\t]*\\n?/g\n\n/** Remove every summary block (defensively — there must only ever be one). */\nexport function stripSummary(text: string): string {\n return text.replace(SUMMARY_RE, '')\n}\n\nexport function hasSummary(text: string): boolean {\n return text.includes(SUMMARY_OPEN)\n}\n\n/** \"3 comments, 2 suggested edits, 1 highlight\" — non-zero categories only. */\nexport function countsPhrase(counts: ReviewCountsDetail): string {\n const parts: string[] = []\n if (counts.comments > 0) parts.push(`${counts.comments} comment${counts.comments === 1 ? '' : 's'}`)\n if (counts.edits > 0) parts.push(`${counts.edits} suggested edit${counts.edits === 1 ? '' : 's'}`)\n if (counts.highlights > 0) parts.push(`${counts.highlights} highlight${counts.highlights === 1 ? '' : 's'}`)\n return parts.join(', ')\n}\n\n/** The one-line machine-readable stdout summary for `mastermind open --wait`. */\nexport function summaryLine(counts: ReviewCountsDetail): string {\n const phrase = countsPhrase(counts)\n return `mastermind: review complete — ${phrase || 'no marks'}`\n}\n\n/**\n * Hand-formatted local timestamp (never toLocaleString — the daemon's\n * locale/ICU env differs from the user's shell, and drift pollutes diffs).\n */\nexport function formatStamp(date: Date): string {\n const p = (n: number) => String(n).padStart(2, '0')\n return `${date.getFullYear()}-${p(date.getMonth() + 1)}-${p(date.getDate())} ${p(date.getHours())}:${p(date.getMinutes())}`\n}\n\n/**\n * Fold a free-form hand-back note into blockquote lines for the summary block.\n * Returns '' when there's nothing to add. Defangs the HTML-comment delimiters so\n * a pasted note can never break out of (or forge) the summary block, and folds\n * the note's own lines into the leading `> ` quote so the block stays one unit.\n */\nfunction noteLines(note: string | undefined): string {\n const safe = (note ?? '').replace(/<!--|-->/g, '').trim()\n if (!safe) return ''\n const quoted = safe.split('\\n').map((l) => `> ${l}`.trimEnd()).join('\\n')\n return `>\\n> **Note:** ${quoted.replace(/^> /, '')}\\n`\n}\n\nexport function buildSummaryBlock(counts: ReviewCountsDetail, date: Date, note?: string): string {\n const phrase = countsPhrase(counts)\n const detail = phrase\n ? `${phrase}. Open the CriticMarkup marks above for details.`\n : 'No inline marks — see the document itself for changes.'\n return `${SUMMARY_OPEN}\\n> **Review summary** (${formatStamp(date)})\\n> ${detail}\\n${noteLines(note)}${SUMMARY_CLOSE}\\n`\n}\n\n/**\n * Insert/replace the summary block: exactly one, replaced in place when one\n * already exists, appended at EOF otherwise.\n */\nexport function upsertSummary(text: string, counts: ReviewCountsDetail, date: Date, note?: string): string {\n const block = buildSummaryBlock(counts, date, note)\n const firstIdx = text.indexOf(SUMMARY_OPEN)\n if (firstIdx !== -1) {\n const before = stripSummary(text.slice(0, firstIdx))\n const after = stripSummary(text.slice(firstIdx))\n return before + block + after\n }\n let body = text\n if (body.length > 0 && !body.endsWith('\\n')) body += '\\n'\n if (body.length > 0 && !body.endsWith('\\n\\n')) body += '\\n'\n return body + block\n}\n","/**\n * Installed-browser detection for the \"Open in browser\" preference.\n *\n * Server/CLI-only (uses `node:fs`/`node:os`) — never import from `src/shared`.\n * macOS-only for now (matches the macOS-only launcher in `cli/index.ts`); other\n * platforms get an empty list and fall back to the system default browser.\n *\n * `id` is the `.app` bundle name, which is exactly the argument `open -a <id>` wants.\n */\n\nimport fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport type { BrowserInfo } from '../shared/types'\n\n// Known browsers by their .app name (= the `open -a <name>` argument).\nconst KNOWN_BROWSERS = [\n 'Safari',\n 'Google Chrome',\n 'Arc',\n 'Firefox',\n 'Brave Browser',\n 'Microsoft Edge',\n 'Vivaldi',\n 'Opera',\n 'Chromium',\n 'Zen',\n]\n\nfunction appExists(name: string): boolean {\n return [\n `/Applications/${name}.app`,\n `/System/Applications/${name}.app`,\n path.join(os.homedir(), 'Applications', `${name}.app`),\n ].some((p) => fs.existsSync(p))\n}\n\n/** Installed, launchable browsers on this Mac (empty on non-darwin). */\nexport function detectBrowsers(): BrowserInfo[] {\n if (process.platform !== 'darwin') return []\n return KNOWN_BROWSERS.filter(appExists).map((name) => ({ id: name, name }))\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\nimport type { ThemeFontInfo, ThemeInfo } from '../shared/types'\nimport { log } from './log'\nimport { themesDir } from './paths'\n\ninterface ThemeJson {\n id?: string\n name?: string\n appearance?: string\n fonts?: Array<{ family?: string; src?: string; weight?: string | number }>\n grain?: { enabled?: boolean; opacity?: number; tintOpacity?: number }\n swatch?: { bg?: unknown; ink?: unknown; accent?: unknown }\n}\n\n/** Accept a swatch only when all three preview hexes are present strings. */\nfunction parseSwatch(s: ThemeJson['swatch']): ThemeInfo['swatch'] {\n if (!s) return null\n const { bg, ink, accent } = s\n if (typeof bg === 'string' && typeof ink === 'string' && typeof accent === 'string') {\n return { bg, ink, accent }\n }\n return null\n}\n\n/**\n * Font srcs in theme.json are theme-relative (\"/fonts/X.woff2\" lives in the\n * theme's own folder); an explicit \"/themes/...\" src may reference another\n * theme's files (Night borrows the Editorial faces).\n */\nfunction resolveFontUrl(themeId: string, src: string): string {\n if (src.startsWith('/themes/')) return src\n return `/themes/${themeId}${src.startsWith('/') ? '' : '/'}${src}`\n}\n\n/** Scan themes/<id>/{theme.json,tokens.css}; dropping a folder in registers it. */\nexport async function scanThemes(): Promise<ThemeInfo[]> {\n let entries: string[]\n try {\n entries = await fs.readdir(themesDir)\n } catch {\n return []\n }\n const out: ThemeInfo[] = []\n for (const id of entries.sort()) {\n const dir = path.join(themesDir, id)\n // A directory without a theme.json isn't a theme — e.g. the shared\n // themes/fonts/ face directory. Skip it silently (not an error worth logging).\n try {\n const st = await fs.stat(dir)\n if (!st.isDirectory()) continue\n await fs.access(path.join(dir, 'theme.json'))\n } catch {\n continue\n }\n try {\n const [jsonRaw] = await Promise.all([\n fs.readFile(path.join(dir, 'theme.json'), 'utf8'),\n fs.access(path.join(dir, 'tokens.css')), // both files required\n ])\n const parsed = JSON.parse(jsonRaw) as ThemeJson\n const fonts: ThemeFontInfo[] = []\n for (const f of parsed.fonts ?? []) {\n if (!f.family || !f.src) continue\n fonts.push({ family: f.family, url: resolveFontUrl(id, f.src), weight: f.weight ?? 400 })\n }\n out.push({\n id,\n name: parsed.name ?? id,\n appearance: parsed.appearance === 'dark' ? 'dark' : 'light',\n grain: parsed.grain\n ? { enabled: parsed.grain.enabled ?? false, opacity: parsed.grain.opacity, tintOpacity: parsed.grain.tintOpacity }\n : null,\n fonts,\n swatch: parseSwatch(parsed.swatch),\n })\n } catch (err) {\n log(`theme ${id} skipped:`, err instanceof Error ? err.message : err)\n }\n }\n return out\n}\n","export function log(...args: unknown[]): void {\n console.error(`[${new Date().toISOString()}]`, ...args)\n}\n","import type { ListItem, RootContent } from 'mdast'\nimport { scan } from './critic/scanner'\nimport { codeRanges } from './markdown/exclusions'\nimport { parseMarkdown } from './markdown/processor'\n\nexport interface SourceBlock {\n hash: string\n text: string\n start: number\n end: number\n kind: string\n /** code/html blocks are never translated */\n translatable: boolean\n}\n\nconst UNTRANSLATABLE = new Set(['code', 'html', 'thematicBreak', 'definition'])\n\n/** sha-256 via WebCrypto — identical hashes in the browser and the daemon. */\nexport async function hashBlock(kind: string, text: string): Promise<string> {\n const data = new TextEncoder().encode(`${kind}\u0000${text}`)\n const digest = await globalThis.crypto.subtle.digest('SHA-256', data)\n return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n/**\n * Translation granularity: top-level blocks, with list items individually\n * (editing one item re-translates only that item). Block slices are raw\n * source — CriticMarkup travels through translation inside them.\n */\nexport async function segmentBlocks(text: string): Promise<SourceBlock[]> {\n const tree = parseMarkdown(text)\n const out: SourceBlock[] = []\n\n const push = async (node: RootContent | ListItem, kind: string): Promise<void> => {\n const start = node.position?.start?.offset\n const end = node.position?.end?.offset\n if (start === undefined || end === undefined || end <= start) return\n const slice = text.slice(start, end)\n out.push({\n hash: await hashBlock(kind, slice),\n text: slice,\n start,\n end,\n kind,\n translatable: !UNTRANSLATABLE.has(kind),\n })\n }\n\n for (const node of tree.children) {\n if (node.type === 'list') {\n for (const item of node.children) await push(item, 'listItem')\n continue\n }\n await push(node, node.type)\n }\n return out\n}\n\n/**\n * A translated block is acceptable only when its CriticMarkup structure\n * matches the original: same mark kinds, same order. Inner text may change\n * (it was translated); the syntax may not.\n */\nexport function validateTranslatedBlock(original: string, translated: string): boolean {\n const kinds = (t: string): string =>\n scan(t, codeRanges(t))\n .map((s) => s.kind)\n .join(',')\n return kinds(original) === kinds(translated)\n}\n\n/** Cheap doc-language heuristic for picking the toggle direction. */\nexport function detectDocScript(text: string): 'cjk' | 'latin' {\n const sample = text.slice(0, 4000)\n let cjk = 0\n let letters = 0\n for (const ch of sample) {\n const code = ch.codePointAt(0)!\n if (code >= 0x4e00 && code <= 0x9fff) cjk++\n if ((code >= 0x41 && code <= 0x7a) || (code >= 0x4e00 && code <= 0x9fff)) letters++\n }\n return letters > 0 && cjk / letters > 0.25 ? 'cjk' : 'latin'\n}\n","import crypto from 'node:crypto'\nimport { ASSIST_TIMEOUT_MS } from '../../shared/constants'\nimport type { AssistRequestEvent, AssistResultPayload } from '../../shared/types'\nimport { log } from '../log'\nimport type { Session, SessionRegistry } from '../sessions'\n\n/** The request body minus the server-assigned id (id is added at enqueue). */\nexport type AssistRequest =\n | { kind: 'translate'; sourceLang: string; targetLang: string; blocks: { hash: string; text: string }[] }\n | { kind: 'suggest'; scope: 'selection' | 'section' | 'document'; selection: string; context?: string }\n\nexport type AssistFailure = 'no-agent' | 'timeout' | 'cancelled' | 'agent-error'\n\nexport class AssistError extends Error {\n constructor(\n public readonly code: AssistFailure,\n detail?: string,\n ) {\n super(detail ? `${code}: ${detail}` : code)\n this.name = 'AssistError'\n }\n}\n\ninterface Pending {\n id: string\n sessionId: string\n request: AssistRequest\n createdAt: number\n resolve: (payload: AssistResultPayload) => void\n reject: (err: AssistError) => void\n timer: NodeJS.Timeout\n}\n\n/**\n * Routes LLM tasks to the user's own coding agent through the SSE/--wait\n * channel — no API. enqueue() broadcasts an `assist-request` to assist-capable\n * cli conns and returns a promise that settles when the agent POSTs a result\n * (or times out / the agent is gone / the session closes). Mirrors the waiter\n * model that already powers `--wait`.\n */\nexport class AssistRegistry {\n private pending = new Map<string, Pending>()\n\n constructor(\n private readonly sessions: SessionRegistry,\n private readonly timeoutMs = ASSIST_TIMEOUT_MS,\n ) {}\n\n hasListener(session: Session): boolean {\n return this.sessions.hasAssistListener(session)\n }\n\n enqueue(session: Session, request: AssistRequest, opts: { timeoutMs?: number } = {}): Promise<AssistResultPayload> {\n return this.enqueueWithId(session, request, opts).result\n }\n\n /** Like enqueue, but exposes the request id synchronously (the suggest route returns it now, settles later). */\n enqueueWithId(\n session: Session,\n request: AssistRequest,\n opts: { timeoutMs?: number } = {},\n ): { id: string; result: Promise<AssistResultPayload> } {\n if (!this.hasListener(session)) return { id: '', result: Promise.reject(new AssistError('no-agent')) }\n const id = crypto.randomUUID()\n const timeoutMs = opts.timeoutMs ?? this.timeoutMs\n const result = new Promise<AssistResultPayload>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this.pending.delete(id)) {\n session.pendingAssist.delete(id)\n reject(new AssistError('timeout'))\n }\n }, timeoutMs)\n this.pending.set(id, { id, sessionId: session.id, request, createdAt: Date.now(), resolve, reject, timer })\n session.pendingAssist.add(id)\n const event: AssistRequestEvent = { id, ...request }\n this.sessions.broadcast(session.id, 'assist-request', event, ['cli'])\n log(`assist: enqueued ${request.kind} ${id.slice(0, 8)} for session ${session.id}`)\n })\n return { id, result }\n }\n\n /** Agent delivered a result. Returns false if the id is unknown (expired / dup). */\n resolve(id: string, payload: AssistResultPayload): boolean {\n const p = this.pending.get(id)\n if (!p) return false\n this.settle(p)\n p.resolve(payload)\n return true\n }\n\n /** Agent declined/failed a request. */\n fail(id: string, reason: string): boolean {\n const p = this.pending.get(id)\n if (!p) return false\n this.settle(p)\n p.reject(new AssistError('agent-error', reason))\n return true\n }\n\n /** Reject every pending request for a session (called on close). */\n cancelForSession(sessionId: string): void {\n for (const p of [...this.pending.values()]) {\n if (p.sessionId === sessionId) {\n this.settle(p)\n p.reject(new AssistError('cancelled'))\n }\n }\n }\n\n /** Requests still open for a session — drained by `mastermind assist` on (re)connect. */\n pendingFor(sessionId: string): AssistRequestEvent[] {\n return [...this.pending.values()]\n .filter((p) => p.sessionId === sessionId)\n .map((p) => ({ id: p.id, ...p.request }) as AssistRequestEvent)\n }\n\n private settle(p: Pending): void {\n clearTimeout(p.timer)\n this.pending.delete(p.id)\n this.sessions.get(p.sessionId)?.pendingAssist.delete(p.id)\n }\n}\n","import path from 'node:path'\nimport fs from 'node:fs/promises'\n\n/**\n * On-disk translation cache. Translations are keyed by content hash (sha-256 of\n * the block) and target language, so they survive reloads and unrelated edits —\n * an unchanged block stays translated without re-asking the agent. Lives next to\n * the document under `.mastermind/translations/`, the same sibling dir the rest\n * of Mastermind's on-disk state uses (and which the workspace tree hides).\n */\nexport function cacheFile(realPath: string, targetLang: string): string {\n // targetLang is a language code (en, zh-CN, …); sanitize anyway for the filename.\n const safeLang = targetLang.replace(/[^a-zA-Z0-9_-]/g, '_') || 'x'\n return path.join(path.dirname(realPath), '.mastermind', 'translations', `${path.basename(realPath)}.${safeLang}.json`)\n}\n\n/** The cached `{ blockHash: translatedText }` map, or `{}` if none/unreadable. */\nexport async function loadCache(realPath: string, targetLang: string): Promise<Record<string, string>> {\n try {\n const raw = await fs.readFile(cacheFile(realPath, targetLang), 'utf8')\n const parsed: unknown = JSON.parse(raw)\n return parsed && typeof parsed === 'object' ? (parsed as Record<string, string>) : {}\n } catch {\n return {}\n }\n}\n\n/**\n * Merge fresh translations into the on-disk cache (read-modify-write). Best-effort:\n * a disk failure never breaks translation, it just doesn't persist. Content-addressed,\n * so superseded block versions linger harmlessly until pruned by a future cleanup.\n */\nexport async function saveCache(realPath: string, targetLang: string, entries: Record<string, string>): Promise<void> {\n if (Object.keys(entries).length === 0) return\n const file = cacheFile(realPath, targetLang)\n try {\n await fs.mkdir(path.dirname(file), { recursive: true })\n const merged = { ...(await loadCache(realPath, targetLang)), ...entries }\n await fs.writeFile(file, JSON.stringify(merged))\n } catch {\n // best-effort: the in-memory translation still works even if the cache can't persist\n }\n}\n","import { validateTranslatedBlock } from '../../shared/blocks'\nimport { AssistError, type AssistRegistry } from '../assist/index'\nimport type { Session } from '../sessions'\nimport { loadCache, saveCache } from './cache'\n\nexport interface TranslateRequestBlock {\n hash: string\n text: string\n}\n\nexport interface TranslateResult {\n hash: string\n text?: string\n error?: 'structure' | 'provider' | 'no-agent' | 'timeout' | 'cancel'\n cached: boolean\n}\n\nexport interface TranslateOutcome {\n results: TranslateResult[]\n /** set when blocks needed the agent but it couldn't serve them (e.g. 'no-agent'); cached blocks still come back */\n error?: string\n}\n\n/**\n * Translate blocks, on-disk cache first. Cached hits (by content hash + target\n * language) return immediately and work with no agent at all — that's what makes\n * the toggle survive reloads and run offline. Only true misses route ONE batched\n * request to the user's coding agent (the sole translation backend — no APIs),\n * and fresh results are persisted. A missing/slow agent fails only the misses;\n * cached blocks are unaffected, so translation degrades gracefully instead of\n * going all-or-nothing.\n */\nexport async function translateBlocks(\n session: Session,\n blocks: TranslateRequestBlock[],\n sourceLang: string,\n targetLang: string,\n assist: AssistRegistry,\n): Promise<TranslateOutcome> {\n if (blocks.length === 0) return { results: [] }\n\n const cache = await loadCache(session.path, targetLang)\n const results: TranslateResult[] = []\n const misses: TranslateRequestBlock[] = []\n for (const block of blocks) {\n const hit = cache[block.hash]\n if (hit !== undefined && validateTranslatedBlock(block.text, hit)) {\n results.push({ hash: block.hash, text: hit, cached: true })\n } else {\n misses.push(block)\n }\n }\n if (misses.length === 0) return { results }\n\n const fresh: Record<string, string> = {}\n let error: string | undefined\n try {\n const payload = await assist.enqueue(session, { kind: 'translate', sourceLang, targetLang, blocks: misses })\n const byHash = new Map(payload.kind === 'translate' ? payload.blocks.map((b) => [b.hash, b.text]) : [])\n for (const block of misses) {\n const text = byHash.get(block.hash)\n if (text === undefined) results.push({ hash: block.hash, error: 'provider', cached: false })\n else if (!validateTranslatedBlock(block.text, text)) results.push({ hash: block.hash, error: 'structure', cached: false })\n else {\n results.push({ hash: block.hash, text, cached: false })\n fresh[block.hash] = text\n }\n }\n } catch (err) {\n // no agent / timeout / cancel — fail only the misses, keep cached hits\n error = err instanceof AssistError ? err.code : 'provider'\n for (const block of misses) results.push({ hash: block.hash, error: error as TranslateResult['error'], cached: false })\n }\n\n await saveCache(session.path, targetLang, fresh)\n return { results, error }\n}\n","import { watch } from 'chokidar'\nimport fs from 'node:fs/promises'\nimport { sha256hex } from './files'\nimport { log } from './log'\nimport type { Session, SessionRegistry } from './sessions'\n\n/**\n * Per-session file watcher. Self-write echoes are swallowed by hash (not by\n * time window — awaitWriteFinish skews timing); unlink is tentative because\n * editors do atomic replace (unlink+add).\n */\nexport function startWatcher(session: Session, registry: SessionRegistry): void {\n const watcher = watch(session.path, {\n ignoreInitial: true,\n awaitWriteFinish: { stabilityThreshold: 150, pollInterval: 50 },\n })\n\n const onContent = async (): Promise<void> => {\n try {\n const [content, st] = await Promise.all([fs.readFile(session.path, 'utf8'), fs.stat(session.path)])\n if (sha256hex(content) === session.lastSelfWriteHash) return // our own write\n registry.broadcast(session.id, 'file-changed', { mtimeMs: st.mtimeMs })\n } catch {\n /* deleted between events — unlink handler covers it */\n }\n }\n\n watcher.on('change', () => void onContent())\n watcher.on('add', () => void onContent()) // file recreated after atomic replace\n watcher.on('unlink', () => {\n setTimeout(() => {\n void fs.access(session.path).catch(() => {\n log(`session ${session.id}: file deleted on disk`)\n registry.broadcast(session.id, 'file-deleted', {})\n })\n }, 500)\n })\n watcher.on('error', (err) => log(`watcher error for ${session.path}:`, err))\n\n session.watcher = watcher\n}\n\nexport async function stopWatcher(session: Session): Promise<void> {\n await session.watcher?.close().catch(() => {})\n session.watcher = null\n}\n","import type { TextEdit } from './types'\n\n/** Apply non-overlapping edits to a string (any order; applied right-to-left). */\nexport function applyTextEdits(text: string, edits: readonly TextEdit[]): string {\n const sorted = [...edits].sort((a, b) => b.from - a.from)\n let out = text\n let lastFrom = Infinity\n for (const e of sorted) {\n if (e.from > e.to) throw new Error(`invalid edit range ${e.from}..${e.to}`)\n if (e.to > lastFrom) throw new Error('overlapping edits')\n out = out.slice(0, e.from) + e.insert + out.slice(e.to)\n lastFrom = e.from\n }\n return out\n}\n","import { applyTextEdits } from '../edits'\nimport type { TextEdit } from '../types'\nimport type { CriticKind, CriticSpan } from './types'\n\n/**\n * Accept a suggestion: insertions keep their content, deletions disappear,\n * substitutions take the new arm. For annotations, accept resolves them:\n * highlights unwrap to their content, comments are removed.\n */\nexport function acceptEdit(text: string, span: CriticSpan): TextEdit {\n switch (span.kind) {\n case 'ins':\n return { from: span.start, to: span.end, insert: text.slice(span.innerStart, span.innerEnd) }\n case 'del':\n return { from: span.start, to: span.end, insert: '' }\n case 'sub':\n return { from: span.start, to: span.end, insert: text.slice(span.newStart!, span.newEnd!) }\n case 'highlight':\n return { from: span.start, to: span.end, insert: text.slice(span.innerStart, span.innerEnd) }\n case 'comment':\n return { from: span.start, to: span.end, insert: '' }\n }\n}\n\n/**\n * Reject a suggestion: insertions disappear, deletions keep their content,\n * substitutions keep the old arm. Annotations resolve the same way as accept.\n */\nexport function rejectEdit(text: string, span: CriticSpan): TextEdit {\n switch (span.kind) {\n case 'ins':\n return { from: span.start, to: span.end, insert: '' }\n case 'del':\n return { from: span.start, to: span.end, insert: text.slice(span.innerStart, span.innerEnd) }\n case 'sub':\n return { from: span.start, to: span.end, insert: text.slice(span.oldStart!, span.oldEnd!) }\n case 'highlight':\n return { from: span.start, to: span.end, insert: text.slice(span.innerStart, span.innerEnd) }\n case 'comment':\n return { from: span.start, to: span.end, insert: '' }\n }\n}\n\nconst SUGGESTION_KINDS: ReadonlySet<CriticKind> = new Set(['ins', 'del', 'sub'])\n\n/**\n * Resolve every span of the given kinds in one pass (defaults to suggestions\n * only — annotations are resolved per-card, not by Accept all).\n */\nexport function resolveAll(\n text: string,\n spans: readonly CriticSpan[],\n mode: 'accept' | 'reject',\n kinds: ReadonlySet<CriticKind> = SUGGESTION_KINDS,\n): string {\n const edits = spans\n .filter((s) => kinds.has(s.kind))\n .map((s) => (mode === 'accept' ? acceptEdit(text, s) : rejectEdit(text, s)))\n return applyTextEdits(text, edits)\n}\n","import { codeRanges } from '../markdown/exclusions'\nimport { applyTextEdits } from '../edits'\nimport { acceptEdit, rejectEdit } from './resolve'\nimport { scan } from './scanner'\nimport type { CriticSpan } from './types'\n\n/** Per-proposed-mark decision (keyed by index into the proposal's span list). */\nexport type Decision = 'accept' | 'reject' | { edit: string }\n\nexport interface SuggestionValidation {\n ok: boolean\n spans: CriticSpan[]\n reason?: 'no-marks' | 'wrong-kind' | 'rewrites-original'\n}\n\n/**\n * A returned suggestion is acceptable only if it ONLY adds review edits\n * (ins/del/sub — no comments/highlights, those are the user's job) AND its\n * rejected form reproduces the original selection exactly. The second check is\n * the load-bearing gate: it proves the agent only proposed marks and did not\n * silently rewrite untouched text (the one-direction back-door).\n */\nexport function validateSuggestion(markup: string, original: string): SuggestionValidation {\n const spans = scan(markup, codeRanges(markup))\n if (spans.length === 0) return { ok: false, spans, reason: 'no-marks' }\n if (!spans.every((s) => s.kind === 'ins' || s.kind === 'del' || s.kind === 'sub')) {\n return { ok: false, spans, reason: 'wrong-kind' }\n }\n const rejected = applyTextEdits(\n markup,\n spans.map((s) => rejectEdit(markup, s)),\n )\n if (rejected !== original) return { ok: false, spans, reason: 'rewrites-original' }\n return { ok: true, spans }\n}\n\n/**\n * Turn the proposed markup + the user's per-span decisions into the final\n * selection text. Undecided spans default to reject — nothing is applied\n * unless the user explicitly accepted it (the staging-gate guarantee).\n */\nexport function commitDecisions(markup: string, spans: readonly CriticSpan[], decisions: Map<number, Decision>): string {\n const edits = spans.map((span, i) => {\n const d = decisions.get(i) ?? 'reject'\n if (d === 'accept') return acceptEdit(markup, span)\n if (typeof d === 'object') return { from: span.start, to: span.end, insert: d.edit }\n return rejectEdit(markup, span)\n })\n return applyTextEdits(markup, edits)\n}\n\n/** How many spans are accepted/edited (i.e. will change the original). */\nexport function acceptedCount(spans: readonly CriticSpan[], decisions: Map<number, Decision>): number {\n let n = 0\n for (let i = 0; i < spans.length; i++) {\n const d = decisions.get(i)\n if (d === 'accept' || (d && typeof d === 'object')) n++\n }\n return n\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nconst MIME: Record<string, string> = {\n '.html': 'text/html; charset=utf-8',\n '.js': 'text/javascript; charset=utf-8',\n '.css': 'text/css; charset=utf-8',\n '.json': 'application/json; charset=utf-8',\n '.map': 'application/json',\n '.svg': 'image/svg+xml',\n '.png': 'image/png',\n '.ico': 'image/x-icon',\n '.woff2': 'font/woff2',\n '.woff': 'font/woff',\n '.ttf': 'font/ttf',\n '.txt': 'text/plain; charset=utf-8',\n '.md': 'text/markdown; charset=utf-8',\n}\n\n/**\n * Serve `relPath` from inside `root`, or return null when missing/escaping.\n * Hand-rolled (rather than serveStatic middleware) so cwd never matters and\n * traversal is contained explicitly.\n */\nexport async function serveFile(\n root: string,\n relPath: string,\n cache: 'immutable' | 'no-cache' | 'no-store' = 'no-cache',\n): Promise<Response | null> {\n const abs = path.normalize(path.join(root, relPath))\n if (abs !== root && !abs.startsWith(root + path.sep)) return null\n let data: Buffer\n try {\n data = await fs.readFile(abs)\n } catch {\n return null\n }\n const headers: Record<string, string> = {\n 'content-type': MIME[path.extname(abs).toLowerCase()] ?? 'application/octet-stream',\n 'cache-control':\n cache === 'immutable' ? 'public, max-age=31536000, immutable' : cache === 'no-store' ? 'no-store' : 'no-cache',\n }\n return new Response(new Uint8Array(data), { status: 200, headers })\n}\n","import path from 'node:path'\nimport fs from 'node:fs/promises'\nimport crypto from 'node:crypto'\nimport {\n SESSION_CLOSE_GRACE_MS,\n SESSION_NEVER_OPENED_MS,\n} from '../shared/constants'\nimport type { SessionCloseReason, SseEventName } from '../shared/types'\nimport { log } from './log'\n\nexport type ConnRole = 'ui' | 'cli'\n\n/**\n * Rounds was removed: clear any `.mastermind/history/<file>` snapshots written by\n * older versions so no orphaned history lingers. Best-effort — ignore every error\n * (missing dir, permissions). Prunes the now-empty `.mastermind/{history,}` too.\n */\nasync function pruneStaleHistory(realPath: string): Promise<void> {\n const dir = path.dirname(realPath)\n const mastermind = path.join(dir, '.mastermind')\n try {\n await fs.rm(path.join(mastermind, 'history', path.basename(realPath)), { recursive: true, force: true })\n // rmdir only succeeds when empty — leaves shared dirs alone if other files remain.\n await fs.rmdir(path.join(mastermind, 'history')).catch(() => {})\n await fs.rmdir(mastermind).catch(() => {})\n } catch {\n // best-effort cleanup; never block opening a session on it\n }\n}\n\nexport interface Conn {\n readonly role: ConnRole\n /** cli conns that opted into agent-channel work (SSE `assist=1`); answers translate/suggest requests. */\n readonly assistCapable?: boolean\n /** Push one SSE event to this connection. */\n readonly send: (event: SseEventName, data: unknown) => Promise<void>\n /** Force the underlying stream closed. */\n readonly close: () => void\n}\n\nexport interface Session {\n readonly id: string\n readonly path: string // realpath\n readonly displayName: string\n isDraft: boolean\n readonly createdAt: number\n /** sha256 of the last content the server itself wrote — used to swallow watcher echo. */\n lastSelfWriteHash: string | null\n /** chokidar handle, owned by server/watch.ts */\n watcher: { close(): Promise<void> } | null\n /** in-flight agent-channel request ids, cancelled on close (owned by assist registry) */\n readonly pendingAssist: Set<string>\n readonly uiConns: Set<Conn>\n readonly cliConns: Set<Conn>\n /* liveness bookkeeping */\n everConnected: boolean\n graceTimer: NodeJS.Timeout | null\n graceArmedAt: number\n graceRearmed: boolean\n neverOpenedTimer: NodeJS.Timeout | null\n closed: boolean\n}\n\nexport interface RegistryTimers {\n graceMs: number\n neverOpenedMs: number\n}\n\nexport class SessionRegistry {\n private byPath = new Map<string, Session>()\n private byId = new Map<string, Session>()\n private graceMs: number\n private neverOpenedMs: number\n\n constructor(timers?: Partial<RegistryTimers>) {\n this.graceMs = timers?.graceMs ?? SESSION_CLOSE_GRACE_MS\n this.neverOpenedMs = timers?.neverOpenedMs ?? SESSION_NEVER_OPENED_MS\n }\n\n /** Called whenever a session is created or destroyed (idle-exit tracking). */\n onChange: (() => void) | null = null\n /** Hook for setup when a session opens (start file watcher). */\n onSessionOpened: ((session: Session) => void) | null = null\n /** Hook for cleanup when a session closes (stop file watcher). */\n onSessionClosed: ((session: Session, reason: SessionCloseReason) => void) | null = null\n /** Hook when a session's tree-visible state changes (agent attach/detach) — lets workspaces refresh badges. */\n onSessionActivity: ((session: Session) => void) | null = null\n\n open(realPath: string, opts: { isDraft?: boolean } = {}): { session: Session; created: boolean } {\n const existing = this.byPath.get(realPath)\n if (existing) return { session: existing, created: false }\n\n const session: Session = {\n id: crypto.randomUUID(),\n path: realPath,\n displayName: path.basename(realPath),\n isDraft: opts.isDraft ?? false,\n createdAt: Date.now(),\n lastSelfWriteHash: null,\n watcher: null,\n pendingAssist: new Set(),\n uiConns: new Set(),\n cliConns: new Set(),\n everConnected: false,\n graceTimer: null,\n graceArmedAt: 0,\n graceRearmed: false,\n neverOpenedTimer: null,\n closed: false,\n }\n session.neverOpenedTimer = setTimeout(() => {\n if (!session.everConnected && !session.closed) {\n log(`session ${session.id} (${session.displayName}): no browser connected within timeout`)\n this.close(session.id, 'never-opened')\n }\n }, this.neverOpenedMs)\n this.byPath.set(realPath, session)\n this.byId.set(session.id, session)\n if (!session.isDraft) void pruneStaleHistory(realPath)\n log(`session ${session.id} opened for ${realPath}`)\n this.onSessionOpened?.(session)\n this.onChange?.()\n return { session, created: true }\n }\n\n get(id: string): Session | undefined {\n return this.byId.get(id)\n }\n\n getByPath(realPath: string): Session | undefined {\n return this.byPath.get(realPath)\n }\n\n count(): number {\n return this.byId.size\n }\n\n all(): Session[] {\n return [...this.byId.values()]\n }\n\n /** Re-key a session after rename-on-first-save. */\n rekey(session: Session, newRealPath: string): void {\n this.byPath.delete(session.path)\n const mutable = session as { path: string; displayName: string }\n mutable.path = newRealPath\n mutable.displayName = path.basename(newRealPath)\n this.byPath.set(newRealPath, session)\n }\n\n attach(sessionId: string, conn: Conn): boolean {\n const session = this.byId.get(sessionId)\n if (!session || session.closed) return false\n if (conn.role === 'ui') {\n session.uiConns.add(conn)\n session.everConnected = true\n if (session.neverOpenedTimer) {\n clearTimeout(session.neverOpenedTimer)\n session.neverOpenedTimer = null\n }\n this.disarmGrace(session)\n } else {\n session.cliConns.add(conn)\n this.broadcast(sessionId, 'waiters-changed', { count: session.cliConns.size }, ['ui'])\n if (conn.assistCapable) {\n this.broadcast(sessionId, 'assist-availability', { available: true }, ['ui'])\n }\n this.onSessionActivity?.(session)\n }\n return true\n }\n\n /** Any assist-capable cli conn currently listening on this session. */\n hasAssistListener(session: Session): boolean {\n for (const c of session.cliConns) if (c.assistCapable) return true\n return false\n }\n\n detach(sessionId: string, conn: Conn): void {\n const session = this.byId.get(sessionId)\n if (!session) return\n const hadCli = session.cliConns.has(conn)\n session.uiConns.delete(conn)\n session.cliConns.delete(conn)\n if (hadCli && !session.closed) {\n this.broadcast(sessionId, 'waiters-changed', { count: session.cliConns.size }, ['ui'])\n if (conn.assistCapable) {\n this.broadcast(sessionId, 'assist-availability', { available: this.hasAssistListener(session) }, ['ui'])\n }\n this.onSessionActivity?.(session)\n }\n if (conn.role === 'ui' && session.uiConns.size === 0 && session.everConnected && !session.closed) {\n this.armGrace(session)\n }\n }\n\n broadcast(sessionId: string, event: SseEventName, data: unknown, roles: ConnRole[] = ['ui', 'cli']): void {\n const session = this.byId.get(sessionId)\n if (!session) return\n const targets: Conn[] = []\n if (roles.includes('ui')) targets.push(...session.uiConns)\n if (roles.includes('cli')) targets.push(...session.cliConns)\n for (const conn of targets) {\n conn.send(event, data).catch(() => {\n // dead pipe — drop it; liveness timers handle the rest\n this.detach(sessionId, conn)\n })\n }\n }\n\n close(sessionId: string, reason: SessionCloseReason): void {\n const session = this.byId.get(sessionId)\n if (!session || session.closed) return\n session.closed = true\n if (session.graceTimer) clearTimeout(session.graceTimer)\n if (session.neverOpenedTimer) clearTimeout(session.neverOpenedTimer)\n this.broadcast(sessionId, 'session-closed', { reason })\n for (const conn of [...session.uiConns, ...session.cliConns]) {\n // give the event a beat to flush before tearing the stream down\n setTimeout(() => conn.close(), 50)\n }\n session.uiConns.clear()\n session.cliConns.clear()\n this.byPath.delete(session.path)\n this.byId.delete(sessionId)\n log(`session ${sessionId} closed (${reason})`)\n this.onSessionClosed?.(session, reason)\n this.onChange?.()\n }\n\n closeAll(reason: SessionCloseReason): void {\n for (const id of [...this.byId.keys()]) this.close(id, reason)\n }\n\n private armGrace(session: Session): void {\n this.disarmGrace(session)\n session.graceArmedAt = Date.now()\n session.graceRearmed = false\n session.graceTimer = setTimeout(() => this.graceFired(session), this.graceMs)\n }\n\n private disarmGrace(session: Session): void {\n if (session.graceTimer) {\n clearTimeout(session.graceTimer)\n session.graceTimer = null\n }\n }\n\n private graceFired(session: Session): void {\n session.graceTimer = null\n if (session.closed || session.uiConns.size > 0) return\n const elapsed = Date.now() - session.graceArmedAt\n // Timer overshot badly → the machine slept; give the browser one more\n // window to reconnect after wake instead of declaring the tab closed.\n if (elapsed > this.graceMs * 1.5 && !session.graceRearmed) {\n session.graceRearmed = true\n session.graceArmedAt = Date.now()\n session.graceTimer = setTimeout(() => this.graceFired(session), this.graceMs)\n return\n }\n this.close(session.id, 'tabs-closed')\n }\n}\n","import crypto from 'node:crypto'\nimport path from 'node:path'\nimport type { SseEventName } from '../shared/types'\nimport { relWithin } from './contain'\nimport { log } from './log'\nimport type { Conn } from './sessions'\n\nexport interface Workspace {\n readonly id: string\n /** realpath of the root directory — the containment anchor */\n readonly root: string\n readonly displayName: string\n readonly createdAt: number\n /** live tree connections (SSE), keep the daemon alive and receive updates */\n readonly uiConns: Set<Conn>\n}\n\n/**\n * Realpath-keyed directory contexts for the file-tree. A workspace is NOT a\n * session: it owns no file, no watcher, no liveness timers. It exists only to\n * (a) anchor the containment root and (b) hold a thin SSE channel so an open\n * tree keeps the daemon alive and can be told to refresh. Opening a file from\n * the tree mints an ordinary session through the existing SessionRegistry — the\n * session layer, `--wait`, handback, and idle-exit are all untouched.\n */\nexport class WorkspaceRegistry {\n private byRoot = new Map<string, Workspace>()\n private byId = new Map<string, Workspace>()\n\n /** Open (or reuse by realpath) a workspace for an already-realpath'd root. */\n open(rootReal: string): Workspace {\n const existing = this.byRoot.get(rootReal)\n if (existing) return existing\n const ws: Workspace = {\n id: crypto.randomUUID(),\n root: rootReal,\n displayName: path.basename(rootReal) || rootReal,\n createdAt: Date.now(),\n uiConns: new Set(),\n }\n this.byRoot.set(rootReal, ws)\n this.byId.set(ws.id, ws)\n log(`workspace ${ws.id} opened for ${rootReal}`)\n return ws\n }\n\n get(id: string): Workspace | undefined {\n return this.byId.get(id)\n }\n\n /** Total live tree connections across all workspaces — gates daemon idle-exit. */\n activeUiConnCount(): number {\n let n = 0\n for (const ws of this.byId.values()) n += ws.uiConns.size\n return n\n }\n\n attach(workspaceId: string, conn: Conn): boolean {\n const ws = this.byId.get(workspaceId)\n if (!ws) return false\n ws.uiConns.add(conn)\n return true\n }\n\n detach(workspaceId: string, conn: Conn): void {\n this.byId.get(workspaceId)?.uiConns.delete(conn)\n }\n\n broadcast(workspaceId: string, event: SseEventName, data: unknown): void {\n const ws = this.byId.get(workspaceId)\n if (!ws) return\n for (const conn of ws.uiConns) {\n conn.send(event, data).catch(() => this.detach(workspaceId, conn))\n }\n }\n\n /**\n * Tell every workspace that contains `realPath` that something about that file\n * changed (mark counts, open/agent state). The relative path is injected into\n * the event data so the tree can target the right row. Skips workspaces with\n * no live tree connection.\n */\n notifyForPath(realPath: string, event: SseEventName, dataBase: Record<string, unknown> = {}): void {\n for (const ws of this.byId.values()) {\n if (ws.uiConns.size === 0) continue\n const rel = relWithin(ws.root, realPath)\n if (rel === null) continue\n this.broadcast(ws.id, event, { ...dataBase, rel })\n }\n }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\nimport type { ServerState } from '../shared/types'\nimport { ensureConfigDir, stateFilePath } from './paths'\n\nexport function readServerState(): ServerState | null {\n try {\n const raw = fs.readFileSync(stateFilePath(), 'utf8')\n const parsed: unknown = JSON.parse(raw)\n if (\n typeof parsed === 'object' &&\n parsed !== null &&\n typeof (parsed as ServerState).port === 'number' &&\n typeof (parsed as ServerState).pid === 'number' &&\n typeof (parsed as ServerState).version === 'string' &&\n typeof (parsed as ServerState).startedAt === 'number'\n ) {\n return parsed as ServerState\n }\n return null\n } catch {\n return null\n }\n}\n\n/** Atomic write: tmp file + rename, so a concurrent reader never sees a torn file. */\nexport function writeServerState(state: ServerState): void {\n const dir = ensureConfigDir()\n const tmp = path.join(dir, `.server.json.${process.pid}.tmp`)\n fs.writeFileSync(tmp, JSON.stringify(state, null, 2))\n fs.renameSync(tmp, stateFilePath())\n}\n\n/**\n * Remove the statefile. When `onlyIfPid` is given, leave it alone unless it\n * still names that pid — a newer daemon may have already replaced it.\n */\nexport function clearServerState(onlyIfPid?: number): void {\n if (onlyIfPid !== undefined) {\n const current = readServerState()\n if (current && current.pid !== onlyIfPid) return\n }\n try {\n fs.unlinkSync(stateFilePath())\n } catch {\n /* already gone */\n }\n}\n\nexport function pidIsAlive(pid: number): boolean {\n try {\n process.kill(pid, 0)\n return true\n } catch {\n return false\n }\n}\n"],"mappings":"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","names":["fs","os","path","fs","path","fs","path","fs","path","fs","st","fs","comments","j","expectedStart","st","fs","fs","os","path","fs","path","fs","path","crypto","crypto","path","fs","fs","fs","fs","path","fs","path","os","path","fs","crypto","path","fs","crypto","crypto","path","crypto","path","fs","path","fs","path"]}
@@ -0,0 +1 @@
1
+ import{a as e,n as t}from"./react-DoK4WR2u.js";import{t as n}from"./jsx-runtime-BrnrUjgG.js";import{_ as r,g as i,j as a,m as o,u as s}from"./index-cMAuoyhV.js";var c=e(t(),1),l=n();function u({analysis:e,source:t,docRef:n,onClose:u}){let d=a(),[f,p]=(0,c.useState)(``),[m,h]=(0,c.useState)(`all`),[g,_]=(0,c.useState)(0),v=(0,c.useMemo)(()=>{let n=[];return e.spans.forEach((e,r)=>{let i,a;e.kind===`ins`||e.kind===`del`?(i=`suggestion`,a=t.slice(e.innerStart,e.innerEnd)):e.kind===`sub`?(i=`suggestion`,a=`${t.slice(e.oldStart,e.oldEnd)} ${t.slice(e.newStart,e.newEnd)}`):e.kind===`highlight`?(i=`highlight`,a=t.slice(e.innerStart,e.innerEnd)):(i=`comment`,a=s(t.slice(e.innerStart,e.innerEnd)).body),n.push({spanIndex:r,group:i,text:a})}),n},[e,t]),y=(0,c.useMemo)(()=>{let e=f.trim().toLowerCase();return v.filter(t=>(m===`all`||t.group===m)&&(e===``||t.text.toLowerCase().includes(e)))},[v,m,f]);(0,c.useEffect)(()=>{if(y.length===0)return;let e=(g%y.length+y.length)%y.length;n.current?.querySelectorAll(`.kbd-focus`).forEach(e=>e.classList.remove(`kbd-focus`)),o(n,y[e].spanIndex)?.classList.add(`kbd-focus`)},[g,y,n]),(0,c.useEffect)(()=>()=>n.current?.querySelectorAll(`.kbd-focus`).forEach(e=>e.classList.remove(`kbd-focus`)),[n]);let b=e=>_(t=>t+e),x=y.length?(g%y.length+y.length)%y.length+1:0;return(0,l.jsxs)(`div`,{className:`find-bar`,role:`search`,children:[(0,l.jsx)(`input`,{autoFocus:!0,className:`settings-input find-input`,placeholder:d(`findPlaceholder`),value:f,onChange:e=>{p(e.target.value),_(0)},onKeyDown:e=>{e.key===`Enter`?(e.preventDefault(),b(e.shiftKey?-1:1)):e.key===`Escape`&&(e.preventDefault(),u()),e.stopPropagation()}}),(0,l.jsx)(`div`,{className:`find-filters`,children:[`all`,`comment`,`suggestion`,`highlight`].map(e=>(0,l.jsx)(`button`,{type:`button`,className:`find-filter${m===e?` active`:``}`,onClick:()=>{h(e),_(0)},children:d(`find_${e}`)},e))}),(0,l.jsx)(`span`,{className:`find-count`,children:y.length?`${x}/${y.length}`:d(`findNone`)}),(0,l.jsx)(`button`,{type:`button`,className:`find-nav`,disabled:!y.length,onClick:()=>b(-1),"aria-label":`Previous`,children:(0,l.jsx)(r,{})}),(0,l.jsx)(`button`,{type:`button`,className:`find-nav`,disabled:!y.length,onClick:()=>b(1),"aria-label":`Next`,children:(0,l.jsx)(i,{})}),(0,l.jsx)(`button`,{type:`button`,className:`btn-ghost`,onClick:u,children:d(`close`)})]})}export{u as FindBar};
@@ -0,0 +1 @@
1
+ import{a as e,n as t}from"./react-DoK4WR2u.js";import{t as n}from"./jsx-runtime-BrnrUjgG.js";import{f as r,j as i}from"./index-cMAuoyhV.js";var a=e(t(),1),o=n();function s(){let e=r(e=>e.renameError),[t,n]=(0,a.useState)(``),s=i(),c=()=>{t.trim()&&r.getState().completeRename(t.trim())};return(0,o.jsx)(`div`,{className:`modal-backdrop`,onClick:()=>r.getState().cancelRename(),children:(0,o.jsxs)(`div`,{className:`modal`,role:`dialog`,"aria-modal":`true`,"aria-labelledby":`rename-title`,onClick:e=>e.stopPropagation(),onKeyDown:e=>{if(e.key===`Escape`&&r.getState().cancelRename(),e.key===`Tab`){let t=e.currentTarget.querySelectorAll(`input, button:not(:disabled)`);if(t.length===0)return;let n=t[0],r=t[t.length-1];e.shiftKey&&document.activeElement===n?(e.preventDefault(),r.focus()):!e.shiftKey&&document.activeElement===r&&(e.preventDefault(),n.focus())}},children:[(0,o.jsx)(`h4`,{id:`rename-title`,children:s(`nameDraft`)}),(0,o.jsx)(`input`,{autoFocus:!0,type:`text`,className:`settings-input`,placeholder:`plan.md`,"aria-label":s(`draftFilename`),value:t,onChange:e=>n(e.target.value),onKeyDown:e=>{e.key===`Enter`&&c()}}),e&&(0,o.jsx)(`p`,{className:`modal-error`,children:e===`__exists__`?s(`fileExists`):e}),(0,o.jsxs)(`div`,{className:`rail-actions`,children:[(0,o.jsx)(`button`,{type:`button`,onClick:()=>r.getState().cancelRename(),children:s(`cancel`)}),(0,o.jsx)(`button`,{type:`button`,className:`primary`,disabled:!t.trim(),onClick:c,children:s(`save`)})]})]})})}export{s as RenameDialog};
@@ -0,0 +1 @@
1
+ import{a as e,n as t}from"./react-DoK4WR2u.js";import{t as n}from"./jsx-runtime-BrnrUjgG.js";import{A as r,C as i,D as a,E as o,M as s,N as c,O as l,P as u,S as d,T as f,b as p,g as m,j as h,k as g,n as _,r as v,v as y,w as b,x}from"./index-cMAuoyhV.js";var S=e(t(),1),C=n();function w({title:e,onClose:t,ignoreSelector:n,children:r}){let i=h(),a=(0,S.useRef)(null),o=(0,S.useRef)(null);return(0,S.useEffect)(()=>{o.current=document.activeElement;let e=e=>{e.key===`Escape`&&(e.stopPropagation(),t())},r=e=>{let r=e.target;a.current&&!a.current.contains(r)&&!(n&&r.closest(n))&&t()};return window.addEventListener(`keydown`,e),document.addEventListener(`mousedown`,r),()=>{window.removeEventListener(`keydown`,e),document.removeEventListener(`mousedown`,r),o.current?.focus?.()}},[t,n]),(0,C.jsxs)(`div`,{className:`slide-over`,role:`dialog`,"aria-label":e,ref:a,children:[(0,C.jsxs)(`div`,{className:`slide-over-head`,children:[(0,C.jsx)(`span`,{className:`slide-over-title`,children:e}),(0,C.jsx)(`button`,{type:`button`,className:`btn-ghost slide-over-close`,onClick:t,"aria-label":i(`close`),children:(0,C.jsx)(y,{})})]}),(0,C.jsx)(`div`,{className:`slide-over-body`,children:r})]})}var T=e=>e.trim().toLowerCase(),E=300,D=168,O=8,k=6;function A({value:e,onChange:t,label:n}){let r=h(),[i,a]=(0,S.useState)(!1),[o,s]=(0,S.useState)(``),[c,l]=(0,S.useState)(0),u=(0,S.useRef)(null),d=(0,S.useRef)(null),f=(0,S.useRef)(null),[p,g]=(0,S.useState)(null),y=(0,S.useId)(),b=_(e),x=v(o),w=o.trim(),A=x.some(e=>T(e.name)===T(w)||T(e.native)===T(w)||T(e.code)===T(w)),j=w!==``&&!A,M=x.length+ +!!j;(0,S.useEffect)(()=>{if(!i)return;let e=e=>{u.current&&!u.current.contains(e.target)&&a(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[i]),(0,S.useLayoutEffect)(()=>{if(!i){g(null);return}let e=()=>{let e=d.current;if(!e)return;let t=e.getBoundingClientRect(),n=t.width,r=e.closest(`.slide-over`)?.getBoundingClientRect(),i=r?r.left:t.left;if(i-k-n>=O){let e=t.bottom-O,r=window.innerHeight-t.top-O,a=e>=r,o=Math.min(E,a?e:r),s=window.innerWidth-i+k;g(a?{right:s,width:n,bottom:window.innerHeight-t.bottom,maxHeight:o}:{right:s,width:n,top:t.top,maxHeight:o});return}let a=window.innerHeight-t.bottom-O,o=t.top-O,s=a<Math.min(E,D)&&o>a,c=Math.max(D,Math.min(E,s?o:a));g(s?{left:t.left,width:n,bottom:window.innerHeight-t.top+k,maxHeight:c}:{left:t.left,width:n,top:t.bottom+k,maxHeight:c})};return e(),window.addEventListener(`scroll`,e,!0),window.addEventListener(`resize`,e),()=>{window.removeEventListener(`scroll`,e,!0),window.removeEventListener(`resize`,e)}},[i]);function N(e){let n=e.trim();n&&t(n),a(!1),s(``)}function P(){s(``),l(0),a(!0)}function F(e){if(e.key===`Escape`){e.stopPropagation(),a(!1);return}if(e.key===`ArrowDown`)e.preventDefault(),l(e=>Math.min(e+1,M-1));else if(e.key===`ArrowUp`)e.preventDefault(),l(e=>Math.max(e-1,0));else if(e.key===`Enter`){e.preventDefault();let t=x[c];t?N(t.name):j&&N(w)}}return(0,C.jsxs)(`div`,{className:`lang-select`,ref:u,children:[(0,C.jsxs)(`button`,{ref:d,type:`button`,className:`settings-input lang-trigger`,"aria-haspopup":`listbox`,"aria-expanded":i,"aria-label":n,onClick:()=>i?a(!1):P(),children:[(0,C.jsxs)(`span`,{className:`lang-trigger-text`,children:[!b.custom&&b.native&&b.native!==b.name&&(0,C.jsx)(`span`,{className:`lang-native`,children:b.native}),(0,C.jsx)(`span`,{className:`lang-name`,children:b.name}),b.custom&&(0,C.jsx)(`span`,{className:`lang-custom-tag`,children:r(`langCustomTag`)})]}),(0,C.jsx)(m,{className:`lang-chevron`})]}),i&&p&&(0,C.jsxs)(`div`,{className:`lang-menu${p.top===void 0?` flip-up`:``}`,style:{position:`fixed`,left:p.left,right:p.right,width:p.width,top:p.top,bottom:p.bottom,maxHeight:p.maxHeight},children:[(0,C.jsx)(`input`,{ref:f,type:`text`,className:`settings-input lang-search`,role:`combobox`,"aria-controls":y,"aria-expanded":!0,"aria-autocomplete":`list`,placeholder:r(`langSearchPlaceholder`),value:o,autoFocus:!0,onChange:e=>{s(e.target.value),l(0)},onKeyDown:F}),(0,C.jsxs)(`ul`,{className:`lang-list`,id:y,role:`listbox`,"aria-label":n,children:[x.map((t,n)=>(0,C.jsxs)(`li`,{role:`option`,"aria-selected":T(t.name)===T(e)||T(t.code)===T(e),className:`lang-option${n===c?` active`:``}`,onMouseEnter:()=>l(n),onMouseDown:e=>{e.preventDefault(),N(t.name)},children:[(0,C.jsx)(`span`,{className:`lang-native`,children:t.native}),(0,C.jsx)(`span`,{className:`lang-name`,children:t.name})]},t.code)),j&&(0,C.jsxs)(`li`,{role:`option`,"aria-selected":!1,className:`lang-option lang-option-custom${c===x.length?` active`:``}`,onMouseEnter:()=>l(x.length),onMouseDown:e=>{e.preventDefault(),N(w)},children:[r(`langUseCustom`),` “`,w,`”`]}),x.length===0&&!j&&(0,C.jsx)(`li`,{className:`lang-empty`,children:r(`langNoMatch`)})]})]})]})}var j={low:.4,medium:.7,high:1},M={off:`grainOff`,low:`grainLow`,medium:`grainMedium`,high:`grainHigh`},N={fine:`textureFine`,soft:`textureSoft`,coarse:`textureCoarse`},P=[{v:14,a:10},{v:16,a:12.5},{v:18,a:15},{v:20,a:18}],F=[{v:1.45,gap:2},{v:1.6,gap:3.3},{v:1.8,gap:4.6}],I=[{v:640,w:6},{v:736,w:9.5},{v:860,w:13}];function L(e,t){return t.reduce((t,n)=>Math.abs(n-e)<Math.abs(t-e)?n:t)}function R({gap:e}){return(0,C.jsx)(`svg`,{width:16,height:16,viewBox:`0 0 16 16`,"aria-hidden":!0,focusable:!1,children:[8-e,8,8+e].map((e,t)=>(0,C.jsx)(`rect`,{x:3,y:e-.6,width:10,height:1.2,rx:.6,fill:`currentColor`},t))})}function z({theme:e,appearance:t}){let n=e.palettes?.[t],r=n?[n.keyword,n.function,n.string]:[`var(--color-text-faint)`,`var(--color-text-muted)`,`var(--color-text-faint)`],i=[10,7,9];return(0,C.jsx)(`svg`,{width:14,height:14,viewBox:`0 0 14 14`,"aria-hidden":!0,focusable:!1,style:{flex:`none`},children:r.map((e,t)=>(0,C.jsx)(`rect`,{x:2,y:3+t*3,width:i[t],height:1.6,rx:.8,fill:e},t))})}function B({s:e}){return e?(0,C.jsxs)(`svg`,{width:16,height:16,viewBox:`0 0 16 16`,"aria-hidden":!0,focusable:!1,style:{flex:`none`},children:[(0,C.jsx)(`rect`,{x:1,y:1,width:14,height:14,rx:2.5,fill:e.bg,stroke:`rgba(128,128,128,0.35)`,strokeWidth:1}),(0,C.jsx)(`rect`,{x:4,y:5,width:5,height:1.4,rx:.7,fill:e.ink}),(0,C.jsx)(`rect`,{x:4,y:8.5,width:8,height:1.2,rx:.6,fill:e.ink,opacity:.55}),(0,C.jsx)(`circle`,{cx:11.5,cy:5.7,r:1.6,fill:e.accent})]}):null}function V({w:e}){let t=(16-e)/2;return(0,C.jsx)(`svg`,{width:16,height:16,viewBox:`0 0 16 16`,"aria-hidden":!0,focusable:!1,children:[5,8,11].map((n,r)=>(0,C.jsx)(`rect`,{x:t,y:n-.6,width:e,height:1.2,rx:.6,fill:`currentColor`},r))})}function H({onClose:e}){let t=s(e=>e.config),n=s(e=>e.themes),m=s(e=>e.browsers),_=s(e=>e.update),v=h();if(!t)return null;let y=e=>void _(e),S=n.find(e=>e.id===t.theme);return(0,C.jsx)(w,{title:v(`settings`),onClose:e,ignoreSelector:`.floating-settings`,children:(0,C.jsxs)(`div`,{className:`settings-body`,children:[(0,C.jsxs)(`section`,{children:[(0,C.jsx)(`h4`,{children:v(`appearanceSection`)}),(0,C.jsxs)(`div`,{className:`settings-row`,children:[(0,C.jsx)(`span`,{children:v(`theme`)}),(0,C.jsx)(`div`,{className:`settings-theme-row`,children:n.map(e=>(0,C.jsxs)(`button`,{type:`button`,className:`theme-chip code-chip${t.theme===e.id?` active`:``}`,"aria-pressed":t.theme===e.id,onClick:()=>y({theme:e.id}),children:[(0,C.jsx)(B,{s:e.swatch}),e.name]},e.id))})]}),S?.grain&&(()=>{let e=t.grain[t.theme],{intensity:n,texture:r}=f(S,e),a=n=>y({grain:{...t.grain,[t.theme]:{...e,...n}}});return(0,C.jsxs)(C.Fragment,{children:[(0,C.jsxs)(`div`,{className:`settings-row`,children:[(0,C.jsxs)(`span`,{children:[v(`filmGrain`),` `,(0,C.jsx)(`span`,{className:`settings-val`,children:v(M[n])})]}),(0,C.jsx)(u,{className:`seg preset-seg`,type:`single`,value:n,onValueChange:e=>e&&a({intensity:e}),"aria-label":v(`filmGrain`),children:i.map(e=>(0,C.jsx)(c,{value:e,className:`seg-btn`,"aria-label":v(M[e]),title:v(M[e]),children:e===`off`?(0,C.jsx)(x,{}):(0,C.jsx)(d,{id:`grain-int-${e}`,texture:r,opacity:j[e]})},e))})]}),n!==`off`&&(0,C.jsxs)(`div`,{className:`settings-row`,children:[(0,C.jsxs)(`span`,{children:[v(`grainTexture`),` `,(0,C.jsx)(`span`,{className:`settings-val`,children:v(N[r])})]}),(0,C.jsx)(u,{className:`seg preset-seg`,type:`single`,value:r,onValueChange:e=>e&&a({texture:e}),"aria-label":v(`grainTexture`),children:b.map(e=>(0,C.jsx)(c,{value:e,className:`seg-btn`,"aria-label":v(N[e]),title:v(N[e]),children:(0,C.jsx)(d,{id:`grain-tex-${e}`,texture:e,opacity:.7})},e))})]})]})})(),(m.length>0||t.browser!==``&&!m.some(e=>e.id===t.browser))&&(0,C.jsxs)(`div`,{className:`settings-row`,children:[(0,C.jsx)(`span`,{children:v(`openInBrowser`)}),(0,C.jsxs)(`div`,{className:`settings-theme-row`,children:[(0,C.jsx)(`button`,{type:`button`,className:`theme-chip${t.browser===``?` active`:``}`,"aria-pressed":t.browser===``,onClick:()=>y({browser:``}),children:v(`browserSystemDefault`)}),m.map(e=>(0,C.jsx)(`button`,{type:`button`,className:`theme-chip${t.browser===e.id?` active`:``}`,"aria-pressed":t.browser===e.id,onClick:()=>y({browser:e.id}),children:e.name},e.id)),t.browser!==``&&!m.some(e=>e.id===t.browser)&&(0,C.jsx)(`button`,{type:`button`,className:`theme-chip active`,"aria-pressed":!0,title:t.browser,onClick:()=>y({browser:``}),children:t.browser})]})]})]}),(0,C.jsxs)(`section`,{children:[(0,C.jsx)(`h4`,{children:v(`readingSection`)}),(0,C.jsxs)(`div`,{className:`settings-row`,children:[(0,C.jsx)(`span`,{children:v(`typeface`)}),(0,C.jsx)(`div`,{className:`settings-theme-row`,children:r.map(e=>(0,C.jsx)(`button`,{type:`button`,className:`theme-chip${t.typeSet===e.id?` active`:``}`,"aria-pressed":t.typeSet===e.id,style:{fontFamily:a(e)},onClick:()=>y({typeSet:e.id}),children:e.name},e.id))})]}),(0,C.jsxs)(`div`,{className:`settings-row`,children:[(0,C.jsx)(`span`,{children:v(`codeFont`)}),(0,C.jsx)(`div`,{className:`settings-theme-row`,children:g.map(e=>(0,C.jsx)(`button`,{type:`button`,className:`theme-chip${t.monoFont===e.id?` active`:``}`,"aria-pressed":t.monoFont===e.id,style:{fontFamily:l(e)},onClick:()=>y({monoFont:e.id}),children:e.name},e.id))})]}),(0,C.jsxs)(`div`,{className:`settings-row`,children:[(0,C.jsx)(`span`,{children:v(`codeColor`)}),(0,C.jsx)(`div`,{className:`settings-theme-row`,children:o.map(e=>(0,C.jsxs)(`button`,{type:`button`,className:`theme-chip code-chip${t.codeTheme===e.id?` active`:``}`,"aria-pressed":t.codeTheme===e.id,onClick:()=>y({codeTheme:e.id}),children:[(0,C.jsx)(z,{theme:e,appearance:S?.appearance??`light`}),e.id===`none`?v(`codeColorNone`):e.name]},e.id))})]}),(0,C.jsxs)(`div`,{className:`settings-row`,children:[(0,C.jsxs)(`span`,{children:[v(`fontSize`),` `,(0,C.jsxs)(`span`,{className:`settings-val`,children:[t.fontSize,`px`]})]}),(0,C.jsx)(u,{className:`seg preset-seg`,type:`single`,value:String(L(t.fontSize,P.map(e=>e.v))),onValueChange:e=>e&&y({fontSize:Number(e)}),"aria-label":v(`fontSize`),children:P.map(e=>(0,C.jsx)(c,{value:String(e.v),className:`seg-btn`,"aria-label":`${e.v}px`,children:(0,C.jsx)(`span`,{className:`preset-A`,style:{fontSize:`${e.a}px`},children:`A`})},e.v))})]}),(0,C.jsxs)(`div`,{className:`settings-row`,children:[(0,C.jsxs)(`span`,{children:[v(`lineHeight`),` `,(0,C.jsx)(`span`,{className:`settings-val`,children:t.lineHeight.toFixed(2)})]}),(0,C.jsx)(u,{className:`seg preset-seg`,type:`single`,value:String(L(t.lineHeight,F.map(e=>e.v))),onValueChange:e=>e&&y({lineHeight:Number(e)}),"aria-label":v(`lineHeight`),children:F.map(e=>(0,C.jsx)(c,{value:String(e.v),className:`seg-btn`,"aria-label":e.v.toFixed(2),children:(0,C.jsx)(R,{gap:e.gap})},e.v))})]}),(0,C.jsxs)(`div`,{className:`settings-row`,children:[(0,C.jsxs)(`span`,{children:[v(`contentWidth`),` `,(0,C.jsxs)(`span`,{className:`settings-val`,children:[t.contentWidth,`px`]})]}),(0,C.jsx)(u,{className:`seg preset-seg`,type:`single`,value:String(L(t.contentWidth,I.map(e=>e.v))),onValueChange:e=>e&&y({contentWidth:Number(e)}),"aria-label":v(`contentWidth`),children:I.map(e=>(0,C.jsx)(c,{value:String(e.v),className:`seg-btn`,"aria-label":`${e.v}px`,children:(0,C.jsx)(V,{w:e.w})},e.v))})]})]}),(0,C.jsxs)(`section`,{children:[(0,C.jsx)(`h4`,{children:v(`reviewLangSection`)}),(0,C.jsxs)(`label`,{className:`settings-row`,children:[(0,C.jsx)(`span`,{children:v(`authorTag`)}),(0,C.jsx)(`input`,{type:`text`,className:`settings-input`,defaultValue:t.authorTag,spellCheck:!1,autoComplete:`off`,autoCapitalize:`off`,maxLength:32,title:v(`authorTagHint`),onBlur:e=>{let n=e.target.value.trim().replace(/^@/,``);n&&n!==t.authorTag&&y({authorTag:n})}})]}),(0,C.jsxs)(`div`,{className:`settings-row`,children:[(0,C.jsx)(`span`,{children:v(`uiLanguage`)}),(0,C.jsx)(`div`,{className:`settings-theme-row`,children:[`en`,`zh-CN`].map(e=>(0,C.jsx)(`button`,{type:`button`,className:`theme-chip${t.uiLang===e?` active`:``}`,"aria-pressed":t.uiLang===e,onClick:()=>y({uiLang:e}),children:e===`en`?`English`:`简体中文`},e))})]}),(0,C.jsxs)(`div`,{className:`settings-row`,children:[(0,C.jsx)(`span`,{children:v(`readingPair`)}),(0,C.jsxs)(`div`,{className:`settings-langpair`,children:[(0,C.jsx)(A,{label:v(`preferredLanguage`),value:t.langPair.a,onChange:e=>y({langPair:{...t.langPair,a:e}})}),(0,C.jsx)(`button`,{type:`button`,className:`btn-ghost settings-langswap`,"aria-label":v(`swapLangs`),onClick:()=>y({langPair:{a:t.langPair.b,b:t.langPair.a}}),children:(0,C.jsx)(p,{})}),(0,C.jsx)(A,{label:v(`secondaryLanguage`),value:t.langPair.b,onChange:e=>y({langPair:{...t.langPair,b:e}})})]})]})]})]})})}export{H as SettingsPanel};