@spencerboucher/switchyard 0.7.0 → 0.9.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 (149) hide show
  1. package/README.md +3 -3
  2. package/dist/bridge/lines.js +33 -0
  3. package/dist/bridge/switchyard-terminal.js +155 -23
  4. package/dist/switchyard.mjs +3879 -2658
  5. package/dist/terminal/holder.js +142 -51
  6. package/package.json +1 -1
  7. package/web-dist/apple-touch-icon.png +0 -0
  8. package/web-dist/assets/AutomationView-D7Syv1En.js +2 -0
  9. package/web-dist/assets/AutomationView-D7Syv1En.js.map +1 -0
  10. package/web-dist/assets/Checkbox-D6tRfEeI.js +2 -0
  11. package/web-dist/assets/Checkbox-D6tRfEeI.js.map +1 -0
  12. package/web-dist/assets/Copy-B6XSAIUd.js +2 -0
  13. package/web-dist/assets/Copy-B6XSAIUd.js.map +1 -0
  14. package/web-dist/assets/DangerZone-DfYlxDC1.js +2 -0
  15. package/web-dist/assets/DangerZone-DfYlxDC1.js.map +1 -0
  16. package/web-dist/assets/Field-D-Ty7fKT.js +2 -0
  17. package/web-dist/assets/Field-D-Ty7fKT.js.map +1 -0
  18. package/web-dist/assets/InteractionCard-DF1EcSpm.js +6 -0
  19. package/web-dist/assets/InteractionCard-DF1EcSpm.js.map +1 -0
  20. package/web-dist/assets/Menu-BfOszNC4.js +2 -0
  21. package/web-dist/assets/Menu-BfOszNC4.js.map +1 -0
  22. package/web-dist/assets/Overlay-DUQwh6_b.js +2 -0
  23. package/web-dist/assets/Overlay-DUQwh6_b.js.map +1 -0
  24. package/web-dist/assets/Priority-CHmzfoHg.js +2 -0
  25. package/web-dist/assets/Priority-CHmzfoHg.js.map +1 -0
  26. package/web-dist/assets/{SegmentedControl-D-2BCZzd.js → SegmentedControl-C_hEVQMB.js} +2 -2
  27. package/web-dist/assets/{SegmentedControl-D-2BCZzd.js.map → SegmentedControl-C_hEVQMB.js.map} +1 -1
  28. package/web-dist/assets/Select-Cfql4Nwu.js +2 -0
  29. package/web-dist/assets/Select-Cfql4Nwu.js.map +1 -0
  30. package/web-dist/assets/Switch-DBfzmqkm.js +2 -0
  31. package/web-dist/assets/Switch-DBfzmqkm.js.map +1 -0
  32. package/web-dist/assets/Tabs-BNuL3_Et.js +184 -0
  33. package/web-dist/assets/Tabs-BNuL3_Et.js.map +1 -0
  34. package/web-dist/assets/TextArea-CKybrQyn.js +2 -0
  35. package/web-dist/assets/TextArea-CKybrQyn.js.map +1 -0
  36. package/web-dist/assets/TextLink-OzzyP2-p.js +2 -0
  37. package/web-dist/assets/TextLink-OzzyP2-p.js.map +1 -0
  38. package/web-dist/assets/arrow-down-C-iLrN8I.js +2 -0
  39. package/web-dist/assets/arrow-down-C-iLrN8I.js.map +1 -0
  40. package/web-dist/assets/arrow-up-Dj-VTKx4.js +2 -0
  41. package/web-dist/assets/arrow-up-Dj-VTKx4.js.map +1 -0
  42. package/web-dist/assets/attention-V86Gye3I.js +2 -0
  43. package/web-dist/assets/attention-V86Gye3I.js.map +1 -0
  44. package/web-dist/assets/automations-Cw5Gacgm.js +2 -0
  45. package/web-dist/assets/automations-Cw5Gacgm.js.map +1 -0
  46. package/web-dist/assets/board-B8X7TYYR.js +10 -0
  47. package/web-dist/assets/board-B8X7TYYR.js.map +1 -0
  48. package/web-dist/assets/code-BKroYJmR.js +12 -0
  49. package/web-dist/assets/code-BKroYJmR.js.map +1 -0
  50. package/web-dist/assets/describe-D6KLGKWa.js +5 -0
  51. package/web-dist/assets/describe-D6KLGKWa.js.map +1 -0
  52. package/web-dist/assets/enroll-DwT7AcuS.js +2 -0
  53. package/web-dist/assets/enroll-DwT7AcuS.js.map +1 -0
  54. package/web-dist/assets/index-B3F7WcYf.css +2 -0
  55. package/web-dist/assets/index-hxvDvCS7.js +12 -0
  56. package/web-dist/assets/index-hxvDvCS7.js.map +1 -0
  57. package/web-dist/assets/logic-B46fwD-8.js +2 -0
  58. package/web-dist/assets/logic-B46fwD-8.js.map +1 -0
  59. package/web-dist/assets/more-DQR5uu1c.js +2 -0
  60. package/web-dist/assets/more-DQR5uu1c.js.map +1 -0
  61. package/web-dist/assets/queue-DUg1rS8t.js +2 -0
  62. package/web-dist/assets/queue-DUg1rS8t.js.map +1 -0
  63. package/web-dist/assets/repo-XzzzEI0d.js +2 -0
  64. package/web-dist/assets/repo-XzzzEI0d.js.map +1 -0
  65. package/web-dist/assets/server-BqZ-w6M8.js +2 -0
  66. package/web-dist/assets/server-BqZ-w6M8.js.map +1 -0
  67. package/web-dist/assets/settings-DyUu0tJM.js +8 -0
  68. package/web-dist/assets/settings-DyUu0tJM.js.map +1 -0
  69. package/web-dist/assets/states-ZhrxQa3K.js +56 -0
  70. package/web-dist/assets/states-ZhrxQa3K.js.map +1 -0
  71. package/web-dist/assets/terminal-BOV_MXPZ.js +2 -0
  72. package/web-dist/assets/terminal-BOV_MXPZ.js.map +1 -0
  73. package/web-dist/assets/terminal-login-S5CAd5xA.js +2 -0
  74. package/web-dist/assets/terminal-login-S5CAd5xA.js.map +1 -0
  75. package/web-dist/assets/useBackTo-BIrCq3W2.js +2 -0
  76. package/web-dist/assets/useBackTo-BIrCq3W2.js.map +1 -0
  77. package/web-dist/assets/useNow-Dj7oEiwn.js +2 -0
  78. package/web-dist/assets/useNow-Dj7oEiwn.js.map +1 -0
  79. package/web-dist/assets/useResource-CDGSq2At.js +2 -0
  80. package/web-dist/assets/useResource-CDGSq2At.js.map +1 -0
  81. package/web-dist/icon-192.png +0 -0
  82. package/web-dist/icon-512.png +0 -0
  83. package/web-dist/icon.svg +20 -8
  84. package/web-dist/index.html +3 -4
  85. package/web-dist/manifest.webmanifest +1 -1
  86. package/web-dist/maskable-512.png +0 -0
  87. package/web-dist/sw.js +1 -1
  88. package/web-dist/sw.js.map +1 -1
  89. package/web-dist/assets/AutomationView-BIW_pO0M.js +0 -2
  90. package/web-dist/assets/AutomationView-BIW_pO0M.js.map +0 -1
  91. package/web-dist/assets/Badge-BiDp7NKI.js +0 -2
  92. package/web-dist/assets/Badge-BiDp7NKI.js.map +0 -1
  93. package/web-dist/assets/InteractionCard-CWxyDv81.js +0 -6
  94. package/web-dist/assets/InteractionCard-CWxyDv81.js.map +0 -1
  95. package/web-dist/assets/Menu-BDt1bxwz.js +0 -2
  96. package/web-dist/assets/Menu-BDt1bxwz.js.map +0 -1
  97. package/web-dist/assets/Tabs-ByiPklAw.js +0 -184
  98. package/web-dist/assets/Tabs-ByiPklAw.js.map +0 -1
  99. package/web-dist/assets/TextArea-BYr88SnV.js +0 -2
  100. package/web-dist/assets/TextArea-BYr88SnV.js.map +0 -1
  101. package/web-dist/assets/arrow-left-C6YWM6y6.js +0 -2
  102. package/web-dist/assets/arrow-left-C6YWM6y6.js.map +0 -1
  103. package/web-dist/assets/attention-Bya8XHKI.js +0 -2
  104. package/web-dist/assets/attention-Bya8XHKI.js.map +0 -1
  105. package/web-dist/assets/automations-DiXKRcnE.js +0 -2
  106. package/web-dist/assets/automations-DiXKRcnE.js.map +0 -1
  107. package/web-dist/assets/board-BrLaJwLJ.js +0 -10
  108. package/web-dist/assets/board-BrLaJwLJ.js.map +0 -1
  109. package/web-dist/assets/code-DKLO8bzz.js +0 -11
  110. package/web-dist/assets/code-DKLO8bzz.js.map +0 -1
  111. package/web-dist/assets/copy-oWgGHmkS.js +0 -2
  112. package/web-dist/assets/copy-oWgGHmkS.js.map +0 -1
  113. package/web-dist/assets/describe-DnvH6_fl.js +0 -5
  114. package/web-dist/assets/describe-DnvH6_fl.js.map +0 -1
  115. package/web-dist/assets/dist-BE_V9dwJ.js +0 -56
  116. package/web-dist/assets/dist-BE_V9dwJ.js.map +0 -1
  117. package/web-dist/assets/enroll-DUDpbJoB.js +0 -2
  118. package/web-dist/assets/enroll-DUDpbJoB.js.map +0 -1
  119. package/web-dist/assets/index-DvRcMGEe.css +0 -2
  120. package/web-dist/assets/index-vIzH1m8u.js +0 -11
  121. package/web-dist/assets/index-vIzH1m8u.js.map +0 -1
  122. package/web-dist/assets/logic-xb-QF00E.js +0 -2
  123. package/web-dist/assets/logic-xb-QF00E.js.map +0 -1
  124. package/web-dist/assets/more-BP5x45cs.js +0 -2
  125. package/web-dist/assets/more-BP5x45cs.js.map +0 -1
  126. package/web-dist/assets/now--XjJC37b.js +0 -2
  127. package/web-dist/assets/now--XjJC37b.js.map +0 -1
  128. package/web-dist/assets/plus-BLP-wIYD.js +0 -2
  129. package/web-dist/assets/plus-BLP-wIYD.js.map +0 -1
  130. package/web-dist/assets/save-DEvZPWlS.js +0 -2
  131. package/web-dist/assets/save-DEvZPWlS.js.map +0 -1
  132. package/web-dist/assets/send-BKjswHxB.js +0 -2
  133. package/web-dist/assets/send-BKjswHxB.js.map +0 -1
  134. package/web-dist/assets/server-SxFxIScj.js +0 -2
  135. package/web-dist/assets/server-SxFxIScj.js.map +0 -1
  136. package/web-dist/assets/settings-D8T1esWq.js +0 -7
  137. package/web-dist/assets/settings-D8T1esWq.js.map +0 -1
  138. package/web-dist/assets/shared-CZ7fcu_B.js +0 -3
  139. package/web-dist/assets/shared-CZ7fcu_B.js.map +0 -1
  140. package/web-dist/assets/sliders-horizontal-DAoirila.js +0 -2
  141. package/web-dist/assets/sliders-horizontal-DAoirila.js.map +0 -1
  142. package/web-dist/assets/states--geW2Ntl.js +0 -2
  143. package/web-dist/assets/states--geW2Ntl.js.map +0 -1
  144. package/web-dist/assets/terminal-login-BJxV78au.js +0 -2
  145. package/web-dist/assets/terminal-login-BJxV78au.js.map +0 -1
  146. package/web-dist/assets/trash-C6X2OLrI.js +0 -2
  147. package/web-dist/assets/trash-C6X2OLrI.js.map +0 -1
  148. package/web-dist/assets/useNow-jxdoOC_1.js +0 -2
  149. package/web-dist/assets/useNow-jxdoOC_1.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InteractionCard-DF1EcSpm.js","names":["createContext","useContext","useMemo","useState","useMemo","useId","_Fragment","useId","useState","useState","_Fragment","useRef","__toESM","require_react","require_jsx_runtime","cn","isApiError","Button","isFieldVisible","formatRelative","ErrorState","Spinner","apiPost","errorMessage","useSyncStore","ArrowUp","Check","ChevronRight","LoaderCircle","StatusChip","CircleX","CircleCheck","useBreakpoint","Sheet","IconButton","TextArea","TextInput","TextLink","CodeBlock","headingSlug","Tabs","Markdown","TabPanel","__iconData","FileCode","createLucideIcon","import_react","IntentContext","useIntent","import_jsx_runtime","ConfirmRenderer","node","disabled","onRespond","busy","stripPrefix","p","HUNK","parseUnifiedDiff","files","file","oldNo","newNo","oldLeft","newLeft","inHunk","consumed","start","f","lines","patch","line","m","l","h","MARKER","GUTTER_BG","DiffView","given","className","showFileHeaders","ref","selectable","onLineClick","marked","isLineMarked","li","fi","initialFormState","out","fields","fieldValue","st","field","vals","visibleFields","values","state","buildValues","v","validateForm","errors","n","chooseSelect","value","chooseSelectOther","toggleMulti","count","max","FormRenderer","setState","attempted","setAttempted","visible","locked","setField","s","name","fs","e","FieldView","id","errId","common","error","onChange","multi","selectedPreview","o","hint","OptionCard","checked","option","onToggle","UINodeRenderer","handlers","parent","hasOwn","content","NodeView","stripUndefined","TONE_TEXT","ListView","c","i","r","cell","ci","ri","SectionView","TabsView","ProgressView","RefView","ActionsView","Children","nodes","Tag","item","prefix","active","setActive","current","t","status","Icon","statusText","pct","refHref","href","refValue","label","onOpenFile","a","onAction","UNSUPPORTED_COPY","DEFAULT_ACTIONS","findActions","found","isUnsupported","unsupportedView","offered","messageMode","runtime","hasInteractive","byLabel","by","InteractionCard","fromProps","upsert","setBusy","setError","alreadyAnswered","setAlreadyAnswered","lastResponse","setLastResponse","acked","setAcked","interaction","pending","unsupported","sessionRuntime","composing","setComposing","message","setMessage","respond","response","res","onAnswered","other","sendMessage","text","bp","isDocumentPresentation","DocumentInteraction","readOnlyReason","sticky","flush","UnsupportedBody","actionId","view","answered","onSend","onCancelMessage","splitPresentation","last","rest","scrollTo","calm","el","useOnScreen","setVisible","io","firstMarkdown","md","leadingHeading","documentSections","fence","useActiveSection","pinned","setPinned","key","ids","els","root","entries","release","shown","bodyRef","startRef","optionsRef","optionsVisible","sheet","setSheet","split","heading","sections","pinSection","options","changes","question","title","answer","variant","tone","showBar","wrapBtn","jump","mobile"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.61.0_@types+react@19.3.0_react@19.3.1/node_modules/lucide-react/dist/esm/icons/file-code.mjs","../../src/components/intent/context.ts","../../src/components/intent/ConfirmRenderer.tsx","../../src/components/intent/diff.ts","../../src/components/intent/DiffView.tsx","../../src/components/intent/formLogic.ts","../../src/components/intent/FormRenderer.tsx","../../src/components/intent/UINodeRenderer.tsx","../../src/components/intent/unsupported.ts","../../src/components/intent/InteractionCard.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.61.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"file-code\",\n size: 24,\n node: [\n [\n \"path\",\n {\n d: \"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\",\n key: \"1oefj6\"\n }\n ],\n [\"path\", { d: \"M14 2v5a1 1 0 0 0 1 1h5\", key: \"wfsgrz\" }],\n [\"path\", { d: \"M10 12.5 8 15l2 2.5\", key: \"1tg20x\" }],\n [\"path\", { d: \"m14 12.5 2 2.5-2 2.5\", key: \"yinavb\" }]\n ]\n};\n__iconData.node;\nconst FileCode = createLucideIcon(__iconData);\n\nexport { __iconData, FileCode as default };\n//# sourceMappingURL=file-code.mjs.map\n","import type { InteractionResponse } from \"@switchyard/protocol\";\nimport { createContext, useContext } from \"react\";\n\n/** Handlers available to a rendered UINode tree. */\nexport interface IntentHandlers {\n /** Interaction context: form submit, confirm, cancel and action buttons respond here. */\n onRespond?: (response: InteractionResponse) => void;\n /** Panel/tool-card context: `actions` buttons call this when there is no `onRespond`. */\n onAction?: (actionId: string) => void;\n /** File refs call this when provided (e.g. open in the inspector); otherwise render as text. */\n onOpenFile?: (path: string) => void;\n /** A response is in flight: inputs stay visible, buttons show busy. */\n busy?: boolean;\n /** Read-only (already answered, expired, read-only session). */\n disabled?: boolean;\n}\n\nexport const IntentContext = createContext<IntentHandlers>({});\nexport const useIntent = () => useContext(IntentContext);\n","import type { UINode } from \"@switchyard/protocol\";\nimport { Button } from \"../ui/Button\";\nimport { useIntent } from \"./context\";\nimport { Markdown } from \"./Markdown\";\n\ntype ConfirmNode = Extract<UINode, { t: \"confirm\" }>;\n\n/** Yes/no question; responds `{kind: \"confirm\", confirmed}`. */\nexport function ConfirmRenderer({ node }: { node: ConfirmNode }) {\n const { onRespond, busy, disabled } = useIntent();\n return (\n <div className=\"flex flex-col gap-3\">\n <Markdown text={node.message} />\n {!disabled && (\n <div className=\"flex flex-wrap gap-2\">\n <Button\n variant={node.destructive ? \"danger\" : \"primary\"}\n busy={busy}\n onClick={() => onRespond?.({ kind: \"confirm\", confirmed: true })}\n >\n {node.confirmLabel || \"Confirm\"}\n </Button>\n <Button\n disabled={busy}\n onClick={() => onRespond?.({ kind: \"confirm\", confirmed: false })}\n >\n {node.cancelLabel || \"Cancel\"}\n </Button>\n </div>\n )}\n </div>\n );\n}\n","/** Unified diff parsing for DiffView (pure; tested without a DOM). */\n\nexport type DiffLineKind = \"add\" | \"del\" | \"context\" | \"hunk\" | \"meta\";\n\nexport interface DiffLine {\n kind: DiffLineKind;\n text: string;\n oldLine: number | null;\n newLine: number | null;\n}\n\nexport interface DiffFile {\n /** Display path (new path, or old path for deletions). */\n path: string;\n oldPath: string | null;\n newPath: string | null;\n status: \"modified\" | \"added\" | \"deleted\" | \"renamed\" | \"binary\";\n additions: number;\n deletions: number;\n lines: DiffLine[];\n}\n\nfunction stripPrefix(p: string): string | null {\n const t = p.trim().split(\"\\t\")[0]!;\n if (t === \"/dev/null\") return null;\n return t.replace(/^[ab]\\//, \"\");\n}\n\nconst HUNK = /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@/;\n\nexport function parseUnifiedDiff(patch: string): DiffFile[] {\n const files: DiffFile[] = [];\n let file: DiffFile | null = null;\n let oldNo = 0;\n let newNo = 0;\n let oldLeft = 0;\n let newLeft = 0;\n let inHunk = false;\n const consumed = () => {\n if (oldLeft <= 0 && newLeft <= 0) inHunk = false;\n };\n const start = (path = \"\"): DiffFile => {\n const f: DiffFile = {\n path,\n oldPath: null,\n newPath: null,\n status: \"modified\",\n additions: 0,\n deletions: 0,\n lines: [],\n };\n files.push(f);\n inHunk = false;\n return f;\n };\n const lines = patch.replace(/\\r\\n/g, \"\\n\").split(\"\\n\");\n if (lines[lines.length - 1] === \"\") lines.pop();\n for (const line of lines) {\n if (line.startsWith(\"diff --git \")) {\n const m = /^diff --git a\\/(.+?) b\\/(.+)$/.exec(line);\n file = start(m?.[2] ?? \"\");\n if (m) {\n file.oldPath = m[1]!;\n file.newPath = m[2]!;\n }\n continue;\n }\n if (!inHunk && line.startsWith(\"--- \")) {\n if (!file || file.lines.some((l) => l.kind !== \"meta\")) file = start();\n file.oldPath = stripPrefix(line.slice(4));\n if (file.oldPath === null) file.status = \"added\";\n continue;\n }\n if (!inHunk && line.startsWith(\"+++ \") && file) {\n file.newPath = stripPrefix(line.slice(4));\n if (file.newPath === null) file.status = \"deleted\";\n file.path = file.newPath ?? file.oldPath ?? file.path;\n continue;\n }\n const h = HUNK.exec(line);\n if (h) {\n if (!file) file = start();\n oldNo = Number(h[1]);\n newNo = Number(h[3]);\n oldLeft = h[2] === undefined ? 1 : Number(h[2]);\n newLeft = h[4] === undefined ? 1 : Number(h[4]);\n inHunk = oldLeft > 0 || newLeft > 0;\n file.lines.push({ kind: \"hunk\", text: line, oldLine: null, newLine: null });\n continue;\n }\n if (!file) file = start();\n if (inHunk && line.startsWith(\"+\")) {\n file.lines.push({ kind: \"add\", text: line.slice(1), oldLine: null, newLine: newNo++ });\n file.additions++;\n newLeft--;\n consumed();\n } else if (inHunk && line.startsWith(\"-\")) {\n file.lines.push({ kind: \"del\", text: line.slice(1), oldLine: oldNo++, newLine: null });\n file.deletions++;\n oldLeft--;\n consumed();\n } else if (inHunk && (line.startsWith(\" \") || line === \"\")) {\n file.lines.push({\n kind: \"context\",\n text: line.slice(1),\n oldLine: oldNo++,\n newLine: newNo++,\n });\n oldLeft--;\n newLeft--;\n consumed();\n } else if (line.startsWith(\"\\\\\")) {\n file.lines.push({ kind: \"meta\", text: line, oldLine: null, newLine: null });\n } else {\n // Header lines (index, mode, rename, Binary files ...).\n inHunk = false;\n if (line.startsWith(\"new file mode\")) file.status = \"added\";\n else if (line.startsWith(\"deleted file mode\")) file.status = \"deleted\";\n else if (line.startsWith(\"rename from \")) {\n file.status = \"renamed\";\n file.oldPath = line.slice(12);\n } else if (line.startsWith(\"rename to \")) {\n file.status = \"renamed\";\n file.newPath = line.slice(10);\n file.path = file.newPath;\n } else if (line.startsWith(\"Binary files\")) file.status = \"binary\";\n file.lines.push({ kind: \"meta\", text: line, oldLine: null, newLine: null });\n }\n }\n return files;\n}\n","import { useMemo } from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { type DiffFile, type DiffLine, parseUnifiedDiff } from \"./diff\";\n\nexport interface DiffLineRef {\n file: string;\n line: DiffLine;\n}\n\nexport interface DiffViewProps {\n /** Unified diff text (one or more files). */\n patch?: string;\n /** Pre-parsed files (alternative to `patch`). */\n files?: DiffFile[];\n /** Show a header per file (default true). */\n showFileHeaders?: boolean;\n /** Enables line selection (e.g. review comments). Called for add/context/del lines. */\n onLineClick?: (ref: DiffLineRef) => void;\n /** Highlight lines (e.g. lines with comments). */\n isLineMarked?: (ref: DiffLineRef) => boolean;\n className?: string;\n}\n\nconst MARKER: Record<DiffLine[\"kind\"], string> = {\n add: \"+\",\n del: \"-\",\n context: \" \",\n hunk: \"\",\n meta: \"\",\n};\n\n/** Opaque gutter backgrounds (the gutter is sticky, so it must cover scrolled text). */\nconst GUTTER_BG: Record<DiffLine[\"kind\"], string> = {\n add: \"bg-diff-add\",\n del: \"bg-diff-del\",\n hunk: \"bg-diff-hunk\",\n context: \"bg-surface\",\n meta: \"bg-surface\",\n};\n\n/**\n * Unified diff with old/new line numbers, +/- text markers (not color alone)\n * and tinted rows. Horizontal scrolling is confined to the diff block, and the line-number\n * gutter is sticky so it stays visible while a long line is scrolled.\n */\nexport function DiffView({\n patch,\n files: given,\n showFileHeaders = true,\n onLineClick,\n isLineMarked,\n className,\n}: DiffViewProps) {\n const files = useMemo(() => given ?? parseUnifiedDiff(patch ?? \"\"), [given, patch]);\n if (files.length === 0) {\n return <p className=\"text-sm text-muted\">No changes.</p>;\n }\n return (\n <div className={cn(\"flex min-w-0 flex-col gap-3\", className)}>\n {files.map((f, fi) => (\n <section\n // biome-ignore lint/suspicious/noArrayIndexKey: files can repeat paths in odd patches\n key={`${f.path}-${fi}`}\n className=\"min-w-0 overflow-hidden rounded-md border border-border\"\n aria-label={`Diff of ${f.path || \"file\"}`}\n >\n {showFileHeaders && (\n <header className=\"flex min-w-0 items-center gap-2 border-b border-border bg-surface-2 px-3 py-1.5 text-sm\">\n <span className=\"min-w-0 flex-1 truncate font-mono\" title={f.path}>\n {f.status === \"renamed\" && f.oldPath ? `${f.oldPath} → ${f.path}` : f.path}\n </span>\n <span className=\"shrink-0 font-mono text-xs\">\n <span className=\"text-success\">\n +{f.additions}\n <span className=\"sr-only\"> added</span>\n </span>{\" \"}\n <span className=\"text-danger\">\n -{f.deletions}\n <span className=\"sr-only\"> deleted</span>\n </span>\n </span>\n </header>\n )}\n {f.status === \"binary\" ? (\n <p className=\"px-3 py-2 text-sm text-muted\">Binary file not shown.</p>\n ) : (\n <div className=\"max-w-full overflow-x-auto overscroll-x-contain\">\n <table className=\"w-full border-collapse font-mono text-xs leading-5\">\n <tbody>\n {f.lines.map((l, li) => {\n const ref = { file: f.path, line: l };\n const selectable =\n !!onLineClick &&\n (l.kind === \"add\" || l.kind === \"del\" || l.kind === \"context\");\n const marked = isLineMarked?.(ref) ?? false;\n return (\n <tr\n // biome-ignore lint/suspicious/noArrayIndexKey: diff lines are positional\n key={li}\n className={cn(\n l.kind === \"add\" && \"bg-diff-add\",\n l.kind === \"del\" && \"bg-diff-del\",\n l.kind === \"hunk\" && \"bg-diff-hunk text-muted\",\n l.kind === \"meta\" && \"text-muted\",\n marked && \"outline outline-1 -outline-offset-1 outline-warning\",\n )}\n >\n <td\n className={cn(\n \"sticky left-0 z-[1] w-[6.25rem] min-w-[6.25rem] select-none whitespace-nowrap p-0\",\n GUTTER_BG[l.kind],\n )}\n >\n <span className=\"flex\">\n <span className=\"w-10 shrink-0 border-r border-border px-1.5 text-right text-subtle tabular-nums\">\n {l.oldLine ?? \"\"}\n </span>\n <span className=\"w-10 shrink-0 border-r border-border px-1.5 text-right text-subtle tabular-nums\">\n {l.newLine ?? \"\"}\n </span>\n <span\n aria-hidden\n className={cn(\n \"w-5 shrink-0 text-center\",\n l.kind === \"add\" && \"text-success\",\n l.kind === \"del\" && \"text-danger\",\n )}\n >\n {MARKER[l.kind]}\n </span>\n {(l.kind === \"add\" || l.kind === \"del\") && (\n <span className=\"sr-only\">\n {l.kind === \"add\" ? \"added\" : \"deleted\"}\n </span>\n )}\n </span>\n </td>\n <td className=\"whitespace-pre pr-3\">\n {selectable ? (\n <button\n type=\"button\"\n data-diff-line={l.newLine ?? l.oldLine ?? undefined}\n // Keyboard focus scrolls the line into view clear of the sticky gutter.\n className=\"block w-full scroll-ml-[6.25rem] cursor-pointer text-left whitespace-pre hover:underline focus-visible:underline\"\n // A pointer click must not scroll the diff sideways (the button spans\n // the whole long line): skip the focus scroll, then focus in place.\n onMouseDown={(e) => e.preventDefault()}\n onClick={(e) => {\n e.currentTarget.focus({ preventScroll: true });\n onLineClick?.(ref);\n }}\n aria-label={`Comment on line ${l.newLine ?? l.oldLine}`}\n >\n {l.text || \" \"}\n </button>\n ) : (\n l.text\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n )}\n </section>\n ))}\n </div>\n );\n}\n","import { type Field, type FormValue, isFieldVisible } from \"@switchyard/protocol\";\n\n/**\n * Pure form state logic for FormRenderer (tested without a DOM).\n *\n * Each field keeps its raw UI state; `buildValues` turns it into the\n * submitted `InteractionResponse` values, omitting hidden fields.\n */\n\nexport interface FieldState {\n /** Selected option values (select: at most one). */\n selected: string[];\n /** \"Other\" chosen (select/multiselect with allowOther). */\n otherChosen: boolean;\n otherText: string;\n /** Text field value. */\n text: string;\n}\n\nexport type FormState = Record<string, FieldState>;\n\nexport function initialFormState(fields: Field[]): FormState {\n const out: FormState = {};\n for (const f of fields) {\n out[f.name] = {\n selected: [],\n otherChosen: false,\n otherText: \"\",\n text: f.kind === \"text\" ? (f.prefill ?? \"\") : \"\",\n };\n }\n return out;\n}\n\n/** The value a field contributes, or undefined when nothing is chosen. */\nexport function fieldValue(field: Field, st: FieldState | undefined): FormValue | undefined {\n if (!st) return undefined;\n switch (field.kind) {\n case \"text\":\n return st.text;\n case \"select\":\n if (st.otherChosen) return { other: st.otherText };\n return st.selected[0];\n case \"multiselect\": {\n const vals = [...st.selected];\n // Multiselect \"other\" text is submitted as an extra entry.\n if (st.otherChosen && st.otherText.trim()) vals.push(st.otherText.trim());\n return vals;\n }\n }\n}\n\n/**\n * Visible fields in order. `showIf` refers to an earlier field, so visibility\n * is evaluated against the values of fields that are themselves visible.\n */\nexport function visibleFields(fields: Field[], state: FormState): Field[] {\n const values: Record<string, FormValue | undefined> = {};\n const out: Field[] = [];\n for (const f of fields) {\n if (!isFieldVisible(f, values)) continue;\n out.push(f);\n values[f.name] = fieldValue(f, state[f.name]);\n }\n return out;\n}\n\n/** Submitted values: visible fields only. */\nexport function buildValues(fields: Field[], state: FormState): Record<string, FormValue> {\n const out: Record<string, FormValue> = {};\n for (const f of visibleFields(fields, state)) {\n const v = fieldValue(f, state[f.name]);\n if (v !== undefined) out[f.name] = v;\n }\n return out;\n}\n\n/** Validation errors keyed by field name (visible fields only). Empty when valid. */\nexport function validateForm(fields: Field[], state: FormState): Record<string, string> {\n const errors: Record<string, string> = {};\n for (const f of visibleFields(fields, state)) {\n const st = state[f.name];\n if (!st) continue;\n if (f.kind === \"select\") {\n if (st.otherChosen) {\n if (!st.otherText.trim()) errors[f.name] = \"Enter a value or choose an option\";\n } else if (st.selected.length === 0) errors[f.name] = \"Choose an option\";\n } else if (f.kind === \"multiselect\") {\n const n = st.selected.length + (st.otherChosen && st.otherText.trim() ? 1 : 0);\n if (st.otherChosen && !st.otherText.trim()) errors[f.name] = \"Enter a value for Other\";\n else if (f.min !== undefined && n < f.min) errors[f.name] = `Choose at least ${f.min}`;\n else if (f.max !== undefined && n > f.max) errors[f.name] = `Choose at most ${f.max}`;\n }\n }\n return errors;\n}\n\n/** Choose an option of a select (clears \"other\"). */\nexport function chooseSelect(st: FieldState, value: string): FieldState {\n return { ...st, selected: [value], otherChosen: false };\n}\n\n/** Choose \"other\" on a select. */\nexport function chooseSelectOther(st: FieldState): FieldState {\n return { ...st, selected: [], otherChosen: true };\n}\n\n/** Toggle a multiselect option, respecting `max` (a toggle past max is ignored). */\nexport function toggleMulti(st: FieldState, value: string, max?: number): FieldState {\n if (st.selected.includes(value)) {\n return { ...st, selected: st.selected.filter((v) => v !== value) };\n }\n const count = st.selected.length + (st.otherChosen ? 1 : 0);\n if (max !== undefined && count >= max) return st;\n return { ...st, selected: [...st.selected, value] };\n}\n","import type { Field, FieldOption, UINode } from \"@switchyard/protocol\";\nimport { Check } from \"lucide-react\";\nimport { useId, useMemo, useState } from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { Button } from \"../ui/Button\";\nimport { TextArea, TextInput } from \"../ui/TextArea\";\nimport { useIntent } from \"./context\";\nimport {\n buildValues,\n chooseSelect,\n chooseSelectOther,\n type FieldState,\n type FormState,\n initialFormState,\n toggleMulti,\n validateForm,\n visibleFields,\n} from \"./formLogic\";\n\ntype FormNode = Extract<UINode, { t: \"form\" }>;\n\nexport interface FormRendererProps {\n node: FormNode;\n className?: string;\n}\n\n/**\n * Structured question form. Selects render as radio cards, multiselects as\n * toggle cards (never comma-separated entry), `allowOther` adds a free-text\n * option, `showIf` hides dependent fields (omitted from the values).\n * Submits `{kind: \"form\", values}` through the IntentContext.\n */\nexport function FormRenderer({ node, className }: FormRendererProps) {\n const { onRespond, busy, disabled } = useIntent();\n const [state, setState] = useState<FormState>(() => initialFormState(node.fields));\n const [attempted, setAttempted] = useState(false);\n const visible = useMemo(() => visibleFields(node.fields, state), [node.fields, state]);\n const errors = useMemo(() => validateForm(node.fields, state), [node.fields, state]);\n const locked = disabled || busy;\n const setField = (name: string, fs: FieldState) => setState((s) => ({ ...s, [name]: fs }));\n\n return (\n <form\n className={cn(\"flex flex-col gap-4\", className)}\n noValidate\n onSubmit={(e) => {\n e.preventDefault();\n setAttempted(true);\n if (Object.keys(errors).length > 0 || locked) return;\n onRespond?.({ kind: \"form\", values: buildValues(node.fields, state) });\n }}\n >\n {visible.map((f) => (\n <FieldView\n key={f.name}\n field={f}\n state={state[f.name]!}\n onChange={(fs) => setField(f.name, fs)}\n error={attempted ? errors[f.name] : undefined}\n disabled={locked}\n />\n ))}\n {!disabled && (\n <div className=\"flex flex-wrap items-center gap-2\">\n <Button type=\"submit\" variant=\"primary\" busy={busy}>\n {node.submitLabel || \"Submit\"}\n </Button>\n <Button\n type=\"button\"\n variant=\"secondary\"\n disabled={busy}\n onClick={() => onRespond?.({ kind: \"cancel\" })}\n >\n {node.cancelLabel || \"Cancel\"}\n </Button>\n {attempted && Object.keys(errors).length > 0 && (\n <span role=\"alert\" className=\"text-sm text-danger\">\n Complete the highlighted fields.\n </span>\n )}\n </div>\n )}\n </form>\n );\n}\n\nfunction FieldView({\n field,\n state,\n onChange,\n error,\n disabled,\n}: {\n field: Field;\n state: FieldState;\n onChange: (s: FieldState) => void;\n error?: string;\n disabled?: boolean;\n}) {\n const id = useId();\n const errId = `${id}-err`;\n if (field.kind === \"text\") {\n const common = {\n id,\n value: state.text,\n placeholder: field.placeholder,\n disabled,\n \"aria-invalid\": !!error || undefined,\n \"aria-describedby\": error ? errId : undefined,\n };\n return (\n <div className=\"flex flex-col gap-1.5\">\n <label htmlFor={id} className=\"text-sm font-medium\">\n {field.label}\n </label>\n {field.multiline ? (\n <TextArea\n {...common}\n minRows={3}\n onChange={(e) => onChange({ ...state, text: e.target.value })}\n />\n ) : (\n <TextInput {...common} onChange={(e) => onChange({ ...state, text: e.target.value })} />\n )}\n {error && (\n <p id={errId} className=\"text-sm text-danger\">\n {error}\n </p>\n )}\n </div>\n );\n }\n\n const multi = field.kind === \"multiselect\";\n const selectedPreview =\n !multi && state.selected[0]\n ? field.options.find((o) => o.value === state.selected[0])?.preview\n : undefined;\n const hint =\n multi && (field.min !== undefined || field.max !== undefined)\n ? field.min !== undefined && field.max !== undefined\n ? `Choose ${field.min}–${field.max}`\n : field.min !== undefined\n ? `Choose at least ${field.min}`\n : `Choose up to ${field.max}`\n : multi\n ? \"Choose any\"\n : undefined;\n\n return (\n <fieldset\n className=\"flex min-w-0 flex-col gap-2\"\n aria-describedby={error ? errId : undefined}\n aria-invalid={!!error || undefined}\n >\n <legend className=\"mb-2 text-sm font-medium\">\n {field.label}\n {hint && <span className=\"ml-2 font-normal text-muted\">{hint}</span>}\n </legend>\n <div className=\"grid gap-2\">\n {field.options.map((o) => (\n <OptionCard\n key={o.value}\n name={id}\n option={o}\n multi={multi}\n checked={state.selected.includes(o.value)}\n disabled={disabled}\n onToggle={() =>\n onChange(\n multi\n ? toggleMulti(\n state,\n o.value,\n field.kind === \"multiselect\" ? field.max : undefined,\n )\n : chooseSelect(state, o.value),\n )\n }\n />\n ))}\n {field.allowOther && (\n <OptionCard\n name={id}\n option={{ value: \"__other__\", label: field.allowOther.label || \"Other\" }}\n multi={multi}\n checked={state.otherChosen}\n disabled={disabled}\n onToggle={() =>\n onChange(\n multi ? { ...state, otherChosen: !state.otherChosen } : chooseSelectOther(state),\n )\n }\n />\n )}\n </div>\n {field.allowOther && state.otherChosen && (\n <TextInput\n aria-label={field.allowOther.label || \"Other\"}\n placeholder={field.allowOther.placeholder ?? \"Type your answer\"}\n value={state.otherText}\n disabled={disabled}\n autoFocus\n onChange={(e) => onChange({ ...state, otherText: e.target.value })}\n />\n )}\n {selectedPreview && (\n <pre className=\"max-h-64 max-w-full overflow-auto rounded-md border border-border bg-surface-2 px-3 py-2 text-xs leading-5\">\n {selectedPreview}\n </pre>\n )}\n {error && (\n <p id={errId} className=\"text-sm text-danger\">\n {error}\n </p>\n )}\n </fieldset>\n );\n}\n\nfunction OptionCard({\n name,\n option,\n multi,\n checked,\n disabled,\n onToggle,\n}: {\n name: string;\n option: FieldOption;\n multi: boolean;\n checked: boolean;\n disabled?: boolean;\n onToggle: () => void;\n}) {\n return (\n <label\n className={cn(\n \"tap flex min-w-0 cursor-pointer items-start gap-3 rounded-lg border px-3 py-2.5 text-sm\",\n \"has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-accent\",\n checked ? \"border-accent bg-accent-soft\" : \"border-border bg-surface hover:bg-surface-2\",\n disabled && \"cursor-not-allowed opacity-70\",\n )}\n >\n <input\n type={multi ? \"checkbox\" : \"radio\"}\n name={name}\n value={option.value}\n checked={checked}\n disabled={disabled}\n onChange={onToggle}\n className=\"sr-only\"\n />\n <span\n aria-hidden\n className={cn(\n \"mt-0.5 inline-flex size-4 shrink-0 items-center justify-center border\",\n multi ? \"rounded\" : \"rounded-full\",\n checked ? \"border-accent bg-accent text-accent-fg\" : \"border-border-strong bg-surface\",\n )}\n >\n {checked &&\n (multi ? (\n <Check className=\"size-3\" />\n ) : (\n <span className=\"size-1.5 rounded-full bg-accent-fg\" />\n ))}\n </span>\n <span className=\"min-w-0 flex-1\">\n <span className=\"block font-medium break-words\">{option.label}</span>\n {option.description && (\n <span className=\"block text-muted break-words\">{option.description}</span>\n )}\n </span>\n </label>\n );\n}\n","import type { UINode, UIRef } from \"@switchyard/protocol\";\nimport { ChevronRight, CircleCheck, CircleX, FileCode, LoaderCircle } from \"lucide-react\";\nimport { useId, useState } from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { Button } from \"../ui/Button\";\nimport { Spinner } from \"../ui/Spinner\";\nimport { TabPanel, Tabs } from \"../ui/Tabs\";\nimport { TextLink } from \"../ui/TextLink\";\nimport { CodeBlock } from \"./CodeBlock\";\nimport { ConfirmRenderer } from \"./ConfirmRenderer\";\nimport { IntentContext, type IntentHandlers, useIntent } from \"./context\";\nimport { DiffView } from \"./DiffView\";\nimport { FormRenderer } from \"./FormRenderer\";\nimport { Markdown } from \"./Markdown\";\n\nexport interface UINodeRendererProps extends IntentHandlers {\n node: UINode;\n className?: string;\n}\n\n/**\n * Renders a portable UINode tree. Interactive nodes (form, confirm, actions)\n * respond through the handlers given here (or an enclosing IntentContext).\n */\nexport function UINodeRenderer({ node, className, ...handlers }: UINodeRendererProps) {\n const parent = useIntent();\n const hasOwn = Object.values(handlers).some((v) => v !== undefined);\n const content = (\n <div className={cn(\"flex min-w-0 flex-col gap-3\", className)}>\n <NodeView node={node} />\n </div>\n );\n if (!hasOwn) return content;\n return (\n <IntentContext.Provider value={{ ...parent, ...stripUndefined(handlers) }}>\n {content}\n </IntentContext.Provider>\n );\n}\n\nfunction stripUndefined<T extends object>(o: T): Partial<T> {\n return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined)) as Partial<T>;\n}\n\nconst TONE_TEXT = {\n default: \"text-text\",\n muted: \"text-muted\",\n success: \"text-success\",\n warning: \"text-warning\",\n danger: \"text-danger\",\n} as const;\n\nexport function NodeView({ node }: { node: UINode }) {\n switch (node.t) {\n case \"text\":\n return (\n <p\n className={cn(\n \"text-sm break-words whitespace-pre-wrap\",\n TONE_TEXT[node.tone ?? \"default\"],\n )}\n >\n {node.text}\n </p>\n );\n case \"markdown\":\n return <Markdown text={node.text} />;\n case \"code\":\n return <CodeBlock text={node.text} lang={node.lang} maxHeight=\"24rem\" />;\n case \"diff\":\n return <DiffView patch={node.patch} />;\n case \"list\":\n return <ListView node={node} />;\n case \"table\":\n return (\n <div className=\"max-w-full overflow-x-auto overscroll-x-contain rounded-md border border-border\">\n <table className=\"w-full border-collapse text-sm\">\n <thead className=\"bg-surface-2\">\n <tr>\n {node.columns.map((c, i) => (\n <th\n // biome-ignore lint/suspicious/noArrayIndexKey: columns are positional\n key={i}\n scope=\"col\"\n className=\"border-b border-border px-3 py-1.5 text-left font-medium\"\n >\n {c}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {node.rows.map((r, ri) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: rows are positional\n <tr key={ri} className=\"border-b border-border last:border-b-0\">\n {r.map((cell, ci) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: cells are positional\n <td key={ci} className=\"px-3 py-1.5 align-top break-words\">\n {cell}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n case \"section\":\n return <SectionView node={node} />;\n case \"tabs\":\n return <TabsView node={node} />;\n case \"progress\":\n return <ProgressView node={node} />;\n case \"ref\":\n return <RefView refValue={node.ref} label={node.label} />;\n case \"form\":\n return <FormRenderer node={node} />;\n case \"confirm\":\n return <ConfirmRenderer node={node} />;\n case \"actions\":\n return <ActionsView node={node} />;\n default:\n return null;\n }\n}\n\nfunction Children({ nodes }: { nodes: UINode[] }) {\n return (\n <>\n {nodes.map((n, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: UINode trees have no ids\n <NodeView key={i} node={n} />\n ))}\n </>\n );\n}\n\nfunction ListView({ node }: { node: Extract<UINode, { t: \"list\" }> }) {\n const Tag = node.ordered ? \"ol\" : \"ul\";\n return (\n <Tag\n className={cn(\n \"flex flex-col gap-1.5 pl-5 text-sm\",\n node.ordered ? \"list-decimal\" : \"list-disc\",\n )}\n >\n {node.items.map((item, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: list items are positional\n <li key={i} className=\"min-w-0\">\n <div className=\"flex min-w-0 flex-col gap-2\">\n <NodeView node={item} />\n </div>\n </li>\n ))}\n </Tag>\n );\n}\n\nfunction SectionView({ node }: { node: Extract<UINode, { t: \"section\" }> }) {\n if (node.collapsible) {\n return (\n <details className=\"group min-w-0\" open>\n <summary className=\"tap flex cursor-pointer list-none items-center gap-1.5 py-1 text-sm font-medium [&::-webkit-details-marker]:hidden\">\n <ChevronRight\n aria-hidden\n className=\"size-4 shrink-0 transition-transform group-open:rotate-90\"\n />\n {node.title ?? \"Details\"}\n </summary>\n <div className=\"flex min-w-0 flex-col gap-3 border-l border-border pl-3 ml-2 mt-1\">\n <Children nodes={node.children} />\n </div>\n </details>\n );\n }\n return (\n <section className=\"flex min-w-0 flex-col gap-2\">\n {node.title && <h3 className=\"text-sm font-semibold\">{node.title}</h3>}\n <Children nodes={node.children} />\n </section>\n );\n}\n\nfunction TabsView({ node }: { node: Extract<UINode, { t: \"tabs\" }> }) {\n const prefix = useId();\n const [active, setActive] = useState(node.tabs[0]?.id ?? \"\");\n const current = node.tabs.find((t) => t.id === active) ?? node.tabs[0];\n if (!current) return null;\n return (\n <div className=\"flex min-w-0 flex-col gap-2\">\n <Tabs\n label=\"Sections\"\n idPrefix={prefix}\n tabs={node.tabs.map((t) => ({ id: t.id, label: t.label }))}\n value={current.id}\n onChange={setActive}\n scrollable\n />\n <TabPanel idPrefix={prefix} id={current.id} className=\"flex min-w-0 flex-col gap-3\">\n <Children nodes={current.children} />\n </TabPanel>\n </div>\n );\n}\n\nfunction ProgressView({ node }: { node: Extract<UINode, { t: \"progress\" }> }) {\n const status = node.status ?? \"running\";\n const Icon = status === \"ok\" ? CircleCheck : status === \"error\" ? CircleX : LoaderCircle;\n const statusText = status === \"ok\" ? \"Done\" : status === \"error\" ? \"Failed\" : \"In progress\";\n const pct = node.value === undefined ? null : Math.round(node.value * 100);\n return (\n <div className=\"flex min-w-0 flex-col gap-1\">\n <div className=\"flex items-center gap-2 text-sm\">\n {status === \"running\" ? (\n <Spinner className=\"size-4 text-info\" />\n ) : (\n <Icon\n aria-hidden\n className={cn(\n \"size-4 shrink-0\",\n status === \"ok\" && \"text-success\",\n status === \"error\" && \"text-danger\",\n )}\n />\n )}\n <span className=\"min-w-0 flex-1 truncate\">{node.label}</span>\n <span className=\"shrink-0 text-xs text-muted\">\n {statusText}\n {pct !== null ? ` · ${pct}%` : \"\"}\n </span>\n </div>\n {pct !== null && (\n <div\n role=\"progressbar\"\n aria-label={node.label}\n aria-valuenow={pct}\n aria-valuemin={0}\n aria-valuemax={100}\n className=\"h-1.5 overflow-hidden rounded-full bg-surface-3\"\n >\n <div\n className={cn(\n \"h-full rounded-full\",\n status === \"error\" ? \"bg-danger\" : status === \"ok\" ? \"bg-success\" : \"bg-accent\",\n )}\n style={{ width: `${pct}%` }}\n />\n </div>\n )}\n </div>\n );\n}\n\nexport function refHref(ref: UIRef): string | null {\n switch (ref.kind) {\n case \"session\":\n return `/code/${encodeURIComponent(ref.sessionId)}`;\n case \"task\":\n return `/tasks/${encodeURIComponent(ref.taskId)}`;\n default:\n return null;\n }\n}\n\nfunction RefView({ refValue, label }: { refValue: UIRef; label: string }) {\n const { onOpenFile } = useIntent();\n const href = refHref(refValue);\n if (href) {\n return (\n <TextLink to={href} size=\"sm\">\n {label}\n </TextLink>\n );\n }\n if (refValue.kind === \"file\") {\n if (onOpenFile) {\n return (\n <TextLink\n size=\"sm\"\n variant=\"inline\"\n icon={<FileCode aria-hidden className=\"size-4 shrink-0\" />}\n onClick={() => onOpenFile(refValue.path)}\n >\n <span className=\"truncate\">{label || refValue.path}</span>\n </TextLink>\n );\n }\n return (\n <span\n className=\"inline-flex min-w-0 items-center gap-1 font-mono text-sm\"\n title={refValue.path}\n >\n <FileCode aria-hidden className=\"size-4 shrink-0 text-muted\" />\n <span className=\"truncate\">{label || refValue.path}</span>\n </span>\n );\n }\n if (refValue.kind !== \"native_session\") return <span className=\"text-sm\">{label}</span>;\n return (\n <span className=\"text-sm\" title={refValue.sessionFile}>\n {label} <span className=\"text-muted\">(native Pi session, not imported)</span>\n </span>\n );\n}\n\nfunction ActionsView({ node }: { node: Extract<UINode, { t: \"actions\" }> }) {\n const { onRespond, onAction, busy, disabled } = useIntent();\n if (disabled) return null;\n return (\n <div className=\"flex min-w-0 flex-col gap-2 sm:flex-row sm:flex-wrap\">\n {node.actions.map((a) => (\n <Button\n key={a.id}\n className=\"h-auto min-h-9 min-w-0 justify-start py-1.5 text-left whitespace-normal sm:justify-center sm:text-center\"\n variant={a.tone === \"primary\" ? \"primary\" : a.tone === \"danger\" ? \"danger\" : \"secondary\"}\n disabled={busy || (!onRespond && !onAction)}\n onClick={() => {\n if (onRespond) onRespond({ kind: \"action\", actionId: a.id });\n else onAction?.(a.id);\n }}\n >\n {a.label}\n </Button>\n ))}\n </div>\n );\n}\n","import type { Interaction, UINode } from \"@switchyard/protocol\";\n\n/** What actually happened when an extension opened a terminal-only `ctx.ui.custom()` dialog. */\nexport const UNSUPPORTED_COPY =\n \"An extension opened a terminal-only dialog. In RPC mode Pi gave it no answer, so the extension continued without one.\";\n\nexport interface UnsupportedAction {\n id: \"abort\" | \"message\";\n label: string;\n tone: \"primary\" | \"secondary\" | \"danger\";\n}\n\nconst DEFAULT_ACTIONS: UnsupportedAction[] = [\n { id: \"abort\", label: \"Stop the run\", tone: \"danger\" },\n { id: \"message\", label: \"Send a message to Pi\", tone: \"primary\" },\n];\n\nfunction findActions(node: UINode): Extract<UINode, { t: \"actions\" }> | null {\n if (node.t === \"actions\") return node;\n if (node.t === \"section\") {\n for (const c of node.children) {\n const found = findActions(c);\n if (found) return found;\n }\n }\n return null;\n}\n\nexport interface UnsupportedView {\n /** True until a person chose one of the alternatives. */\n answerable: boolean;\n actions: UnsupportedAction[];\n copy: string;\n}\n\nexport function isUnsupported(i: Interaction): boolean {\n return i.state === \"unsupported\" || i.source.kind === \"unsupported_custom\";\n}\n\n/** The alternatives the host offers for an unsupported dialog (stop the run, message). */\nexport function unsupportedView(i: Interaction): UnsupportedView {\n const node = findActions(i.presentation);\n const offered = (node?.actions ?? [])\n .filter(\n (a): a is typeof a & { id: \"abort\" | \"message\" } => a.id === \"abort\" || a.id === \"message\",\n )\n .map((a) => ({\n id: a.id,\n label: a.label,\n tone: (a.tone === \"default\"\n ? \"secondary\"\n : (a.tone ?? (a.id === \"abort\" ? \"danger\" : \"primary\"))) as UnsupportedAction[\"tone\"],\n }));\n return {\n answerable: i.state === \"unsupported\" && i.resolution === null,\n actions: offered.length > 0 ? offered : DEFAULT_ACTIONS,\n copy: UNSUPPORTED_COPY,\n };\n}\n\n/** A follow-up while Pi is still working queues behind the current turn. */\nexport function messageMode(runtime: string | undefined): \"prompt\" | \"followUp\" {\n return runtime === \"running\" || runtime === \"waiting_input\" ? \"followUp\" : \"prompt\";\n}\n","import type { Interaction, InteractionResponse, UINode } from \"@switchyard/protocol\";\nimport { ArrowUp } from \"lucide-react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useBreakpoint } from \"../../app/useBreakpoint\";\nimport { cn } from \"../../lib/cn\";\nimport { formatRelative } from \"../../lib/time\";\nimport { apiPost, errorMessage, isApiError } from \"../../sync/api\";\nimport { useSyncStore } from \"../../sync/store\";\nimport { Button } from \"../ui/Button\";\nimport { IconButton } from \"../ui/IconButton\";\nimport { Sheet } from \"../ui/Overlay\";\nimport { StatusChip } from \"../ui/StatusChip\";\nimport { ErrorState } from \"../ui/states\";\nimport { TextArea } from \"../ui/TextArea\";\nimport { TextLink } from \"../ui/TextLink\";\nimport { headingSlug, Markdown } from \"./Markdown\";\nimport { UINodeRenderer } from \"./UINodeRenderer\";\nimport { isUnsupported, messageMode, unsupportedView } from \"./unsupported\";\n\nexport interface InteractionCardProps {\n interaction: Interaction;\n /** Read-only session or host offline: show the question without answer controls. */\n readOnlyReason?: string | null;\n className?: string;\n /** Called after the coordinator acknowledged an answer. */\n onAnswered?: (interaction: Interaction) => void;\n /** DOM id (the \"Needs input\" strip scrolls to it). */\n id?: string;\n /** Stick the document header to the top of its scroller (false when the page scrolls). */\n sticky?: boolean;\n /** The page itself scrolls (no padded scroller): pin the header and bar flush. */\n flush?: boolean;\n}\n\nfunction hasInteractive(node: UINode): boolean {\n switch (node.t) {\n case \"form\":\n case \"confirm\":\n case \"actions\":\n return true;\n case \"list\":\n return node.items.some(hasInteractive);\n case \"section\":\n return node.children.some(hasInteractive);\n case \"tabs\":\n return node.tabs.some((t) => t.children.some(hasInteractive));\n default:\n return false;\n }\n}\n\nfunction byLabel(by: string): string {\n if (by === \"host\") return \"the host\";\n if (by === \"pi\") return \"Pi\";\n return \"a device\";\n}\n\n/**\n * A runtime interaction (extension dialog). Posts the answer with the\n * interaction revision and waits for the acknowledgement; there is no\n * optimistic resolution. A 409 shows \"Already answered: <summary>\".\n */\nexport function InteractionCard({\n interaction: fromProps,\n readOnlyReason,\n className,\n onAnswered,\n id,\n sticky,\n flush,\n}: InteractionCardProps) {\n const upsert = useSyncStore((s) => s.upsertInteraction);\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<unknown>(null);\n const [alreadyAnswered, setAlreadyAnswered] = useState<string | null>(null);\n const [lastResponse, setLastResponse] = useState<InteractionResponse | null>(null);\n // The acknowledged interaction from the answer/409 response, until the prop catches up.\n const [acked, setAcked] = useState<Interaction | null>(null);\n const interaction =\n acked && acked.id === fromProps.id && acked.revision > fromProps.revision ? acked : fromProps;\n\n const pending = interaction.state === \"pending\";\n const unsupported = isUnsupported(interaction);\n const sessionRuntime = useSyncStore((s) => s.sessions[interaction.sessionId]?.runtime);\n const [composing, setComposing] = useState(false);\n const [message, setMessage] = useState(\"\");\n\n const respond = async (response: InteractionResponse): Promise<boolean> => {\n setBusy(true);\n setError(null);\n setLastResponse(response);\n try {\n const res = await apiPost<{ status: string; interaction: Interaction }>(\n `/api/interactions/${encodeURIComponent(interaction.id)}/answer`,\n { revision: interaction.revision, response },\n );\n if (res?.interaction) {\n upsert(res.interaction);\n setAcked(res.interaction);\n onAnswered?.(res.interaction);\n }\n return true;\n } catch (e) {\n if (isApiError(e, \"already_answered\")) {\n const other = (e.details as { interaction?: Interaction } | undefined)?.interaction;\n if (other) {\n upsert(other);\n setAcked(other);\n }\n setAlreadyAnswered(other?.resolution?.summary ?? \"answered on another device\");\n } else {\n setError(e);\n }\n return false;\n } finally {\n setBusy(false);\n }\n };\n\n /** \"Send a message to Pi\": deliver the message first, then record the choice. */\n const sendMessage = async () => {\n const text = message.trim();\n if (!text) return;\n setBusy(true);\n setError(null);\n try {\n await apiPost(`/api/sessions/${encodeURIComponent(interaction.sessionId)}/prompt`, {\n text,\n mode: messageMode(sessionRuntime),\n });\n } catch (e) {\n setError(e);\n setBusy(false);\n return;\n }\n setBusy(false);\n if (await respond({ kind: \"action\", actionId: \"message\" })) {\n setComposing(false);\n setMessage(\"\");\n }\n };\n\n const bp = useBreakpoint();\n if (\n pending &&\n !alreadyAnswered &&\n !unsupported &&\n isDocumentPresentation(interaction.presentation)\n ) {\n return (\n <DocumentInteraction\n id={id}\n interaction={interaction}\n node={interaction.presentation}\n onRespond={(r) => void respond(r)}\n busy={busy}\n disabled={!!readOnlyReason}\n mobile={bp === \"mobile\"}\n sticky={sticky ?? true}\n flush={flush ?? false}\n className={className}\n />\n );\n }\n\n return (\n <article\n id={id}\n aria-label={`Question: ${interaction.title}`}\n className={cn(\n \"flex min-w-0 flex-col gap-3 border-l-2 py-1 pl-3\",\n pending ? \"border-warning\" : \"border-border\",\n className,\n )}\n >\n <header className=\"flex min-w-0 flex-wrap items-start gap-2\">\n <h3 className=\"min-w-0 flex-1 text-sm font-semibold break-words\">{interaction.title}</h3>\n <StatusChip kind=\"interaction\" value={interaction.state} size=\"sm\" />\n </header>\n\n {alreadyAnswered && (\n <p role=\"status\" className=\"rounded-md bg-surface-2 px-3 py-2 text-sm\">\n Already answered: {alreadyAnswered}\n </p>\n )}\n\n {unsupported ? (\n <UnsupportedBody\n interaction={interaction}\n readOnlyReason={readOnlyReason ?? null}\n busy={busy}\n answered={alreadyAnswered !== null}\n composing={composing}\n message={message}\n setMessage={setMessage}\n onAction={(actionId) => {\n if (actionId === \"message\") setComposing(true);\n else void respond({ kind: \"action\", actionId });\n }}\n onSend={() => void sendMessage()}\n onCancelMessage={() => setComposing(false)}\n />\n ) : pending && !alreadyAnswered ? (\n <>\n {readOnlyReason && <p className=\"text-sm text-muted\">{readOnlyReason}</p>}\n <UINodeRenderer\n node={interaction.presentation}\n onRespond={(r) => void respond(r)}\n busy={busy}\n disabled={!!readOnlyReason}\n />\n {!readOnlyReason && !hasInteractive(interaction.presentation) && (\n <div>\n <Button busy={busy} onClick={() => void respond({ kind: \"cancel\" })}>\n Dismiss\n </Button>\n </div>\n )}\n {busy && (\n <p role=\"status\" className=\"text-sm text-muted\">\n Sending answer… waiting for the host to confirm.\n </p>\n )}\n </>\n ) : alreadyAnswered ? null : interaction.resolution ? (\n <p className=\"text-sm text-muted\">\n {interaction.state === \"resolved\" ? \"Answered\" : \"Closed\"} by{\" \"}\n {byLabel(interaction.resolution.by)} {formatRelative(interaction.resolution.at)}:{\" \"}\n <span className=\"text-text\">{interaction.resolution.summary}</span>\n </p>\n ) : (\n <p className=\"text-sm text-muted\">\n {interaction.state === \"expired\"\n ? \"This question expired before it was answered (Pi closed the dialog).\"\n : \"This question was canceled.\"}\n </p>\n )}\n\n {error !== null && (\n <ErrorState\n compact\n title=\"Answer not delivered\"\n error={errorMessage(error)}\n onRetry={lastResponse ? () => void respond(lastResponse) : undefined}\n />\n )}\n </article>\n );\n}\n\n/** A terminal-only dialog: what happened, and the alternatives the host offers until one is chosen. */\nfunction UnsupportedBody({\n interaction,\n readOnlyReason,\n busy,\n answered,\n composing,\n message,\n setMessage,\n onAction,\n onSend,\n onCancelMessage,\n}: {\n interaction: Interaction;\n readOnlyReason: string | null;\n busy: boolean;\n answered: boolean;\n composing: boolean;\n message: string;\n setMessage: (t: string) => void;\n onAction: (actionId: \"abort\" | \"message\") => void;\n onSend: () => void;\n onCancelMessage: () => void;\n}) {\n const view = unsupportedView(interaction);\n return (\n <div className=\"flex min-w-0 flex-col gap-2\">\n <p className=\"text-sm text-muted\">{view.copy}</p>\n {interaction.resolution ? (\n <p className=\"text-sm text-muted\">\n Chosen by {byLabel(interaction.resolution.by)} {formatRelative(interaction.resolution.at)}\n : <span className=\"text-text\">{interaction.resolution.summary}</span>\n </p>\n ) : !view.answerable || answered ? null : readOnlyReason ? (\n <p className=\"text-sm text-muted\">{readOnlyReason}</p>\n ) : composing ? (\n <div className=\"flex min-w-0 flex-col gap-2\">\n <TextArea\n aria-label=\"Message for Pi\"\n value={message}\n onChange={(e) => setMessage(e.target.value)}\n rows={3}\n autoFocus\n placeholder=\"Tell Pi what to do instead\"\n />\n <div className=\"flex flex-wrap gap-2\">\n <Button variant=\"primary\" busy={busy} disabled={!message.trim()} onClick={onSend}>\n Send message\n </Button>\n <Button disabled={busy} onClick={onCancelMessage}>\n Cancel\n </Button>\n </div>\n </div>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {view.actions.map((a) => (\n <Button\n key={a.id}\n variant={a.tone}\n busy={busy && a.id === \"abort\"}\n disabled={busy}\n onClick={() => onAction(a.id)}\n >\n {a.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n );\n}\n\n/** A content section that ends in a row of actions (a plan with its options), if `node` is one. */\nexport function splitPresentation(node: UINode): { body: UINode; actions: UINode } | null {\n if (node.t !== \"section\" || node.children.length < 2) return null;\n const last = node.children[node.children.length - 1];\n if (last?.t !== \"actions\") return null;\n const rest = node.children.slice(0, -1);\n if (rest.some(hasInteractive)) return null;\n return { body: { ...node, children: rest }, actions: last };\n}\n\nfunction scrollTo(el: HTMLElement | null, block: \"start\" | \"center\") {\n const calm = window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches;\n el?.scrollIntoView({ behavior: calm ? \"auto\" : \"smooth\", block });\n}\n\n/** True while `el` is (at least partly) on screen; assumed visible without IntersectionObserver. */\nfunction useOnScreen(ref: React.RefObject<HTMLElement | null>): boolean {\n const [visible, setVisible] = useState(true);\n useEffect(() => {\n const el = ref.current;\n if (!el || typeof IntersectionObserver === \"undefined\") return;\n const io = new IntersectionObserver(([e]) => setVisible(!!e?.isIntersecting));\n io.observe(el);\n return () => io.disconnect();\n }, [ref]);\n return visible;\n}\n\nfunction firstMarkdown(node: UINode): string | null {\n if (node.t === \"markdown\") return node.text;\n if (node.t === \"section\") {\n for (const c of node.children) {\n const m = firstMarkdown(c);\n if (m !== null) return m;\n }\n }\n return null;\n}\n\n/** A long document with trailing options (a plan awaiting approval). */\nexport function isDocumentPresentation(node: UINode): boolean {\n if (!splitPresentation(node)) return false;\n const md = firstMarkdown(node);\n return md !== null && (md.length >= 400 || md.split(\"\\n\").length >= 12);\n}\n\n/** The first heading of a document, its text and the markdown without it. */\nexport function leadingHeading(md: string): { title: string | null; rest: string } {\n const m = /^\\s*#{1,6}\\s+(.+?)\\s*(?:\\n+|$)/.exec(md);\n return m?.[1] ? { title: m[1], rest: md.slice(m[0].length) } : { title: null, rest: md };\n}\n\n/** `##` headings of a document, for the outline. */\nexport function documentSections(md: string): { text: string; id: string }[] {\n const out: { text: string; id: string }[] = [];\n let fence = false;\n for (const line of md.split(\"\\n\")) {\n if (/^\\s*```/.test(line)) fence = !fence;\n const m = !fence ? /^##\\s+(.+?)\\s*$/.exec(line) : null;\n if (m?.[1]) out.push({ text: m[1], id: headingSlug(m[1]) });\n }\n return out;\n}\n\n/** Id of the outline entry whose heading is the last one scrolled past the top. */\nfunction useActiveSection(ids: string[], root: React.RefObject<HTMLElement | null>) {\n const [active, setActive] = useState<string | null>(null);\n const [pinned, setPinned] = useState<string | null>(null);\n const key = ids.join(\"|\");\n // biome-ignore lint/correctness/useExhaustiveDependencies: `key` stands for `ids`\n useEffect(() => {\n if (typeof IntersectionObserver === \"undefined\" || !ids.length) return;\n const els = ids\n .map((i) => root.current?.querySelector<HTMLElement>(`#${CSS.escape(i)}`))\n .filter((e): e is HTMLElement => !!e);\n const io = new IntersectionObserver(\n (entries) => {\n for (const e of entries) if (e.isIntersecting) setActive(e.target.id);\n },\n { rootMargin: \"-56px 0px -70% 0px\" },\n );\n for (const e of els) io.observe(e);\n // A click on an entry near the end cannot scroll it to the top: it stays selected until the\n // reader scrolls again.\n const release = () => setPinned(null);\n window.addEventListener(\"wheel\", release, { passive: true });\n window.addEventListener(\"touchmove\", release, { passive: true });\n window.addEventListener(\"keydown\", release);\n return () => {\n io.disconnect();\n window.removeEventListener(\"wheel\", release);\n window.removeEventListener(\"touchmove\", release);\n window.removeEventListener(\"keydown\", release);\n };\n }, [key, root]);\n const shown = pinned ?? active;\n // Mark the heading in the text that the outline highlights.\n // biome-ignore lint/correctness/useExhaustiveDependencies: `key` stands for `ids`\n useEffect(() => {\n const r = root.current;\n if (!r) return;\n for (const h of r.querySelectorAll<HTMLElement>(\"h2[data-active]\")) delete h.dataset.active;\n if (shown) {\n const el = r.querySelector<HTMLElement>(`#${CSS.escape(shown)}`);\n if (el) el.dataset.active = \"true\";\n }\n }, [shown, key, root]);\n return [shown, setPinned] as const;\n}\n\n/**\n * Document question (a plan awaiting approval): no card of its own. The plan's own heading titles\n * it, the question labels the answers, revisions come first. Phone: compact header, prose, a bar\n * with the answers pinned to the bottom. Desktop: a section outline beside prose at a readable\n * measure, and a wrapping bar of answers. The bar hides while the inline options are on screen.\n */\nfunction DocumentInteraction({\n id,\n interaction,\n node,\n onRespond,\n busy,\n disabled,\n mobile,\n sticky,\n flush,\n className,\n}: {\n id?: string;\n interaction: Interaction;\n node: UINode;\n onRespond: (r: InteractionResponse) => void;\n busy: boolean;\n disabled: boolean;\n mobile: boolean;\n sticky: boolean;\n flush: boolean;\n className?: string;\n}) {\n const bodyRef = useRef<HTMLDivElement>(null);\n const startRef = useRef<HTMLDivElement>(null);\n const optionsRef = useRef<HTMLElement>(null);\n const optionsVisible = useOnScreen(optionsRef);\n const [sheet, setSheet] = useState(false);\n const split = splitPresentation(node);\n const md =\n split?.body.t === \"section\"\n ? (split.body.children.find((c) => c.t === \"markdown\") as\n | Extract<UINode, { t: \"markdown\" }>\n | undefined)\n : undefined;\n const { title: heading, rest } = leadingHeading(md?.text ?? \"\");\n const sections = documentSections(rest);\n const [active, pinSection] = useActiveSection(\n sections.map((s) => s.id),\n bodyRef,\n );\n if (split?.body.t !== \"section\") return null;\n const options = (split.actions as Extract<UINode, { t: \"actions\" }>).actions;\n const changes = split.body.children.find((c) => c.t === \"section\");\n const question = interaction.title;\n const title = heading ?? question;\n const answer = (actionId: string) => {\n setSheet(false);\n onRespond({ kind: \"action\", actionId });\n };\n const variant = (i: number, tone?: string) =>\n i === 0 || tone === \"primary\" ? \"primary\" : tone === \"danger\" ? \"danger\" : \"secondary\";\n const showBar = !disabled && !optionsVisible;\n const wrapBtn = \"h-auto! min-h-9 min-w-0 py-1.5 text-left leading-tight whitespace-normal!\";\n const jump = (el: HTMLElement | null) => scrollTo(el, \"start\");\n return (\n <article\n id={id}\n data-testid=\"document-interaction\"\n aria-label={`Question: ${question}`}\n className={cn(\n \"w-full min-w-0 self-stretch border-t border-border\",\n\n className,\n )}\n >\n <header\n className={cn(\n \"z-10 flex min-w-0 items-center gap-2 border-b border-border bg-bg px-4 py-2 md:px-6\",\n sticky && (flush ? \"md:sticky md:top-0\" : \"max-md:static md:sticky md:-top-3\"),\n )}\n >\n <h3 className=\"min-w-0 flex-1 truncate text-[15px] leading-snug font-semibold md:text-sm\">\n {title}\n </h3>\n {mobile && (\n <StatusChip\n kind=\"interaction\"\n value={interaction.state}\n size=\"sm\"\n className=\"[&>span]:sr-only\"\n />\n )}\n {!disabled && !optionsVisible && (\n <TextLink\n size=\"xs\"\n className=\"shrink-0\"\n onClick={() => scrollTo(optionsRef.current, \"center\")}\n >\n Jump to options ↓\n </TextLink>\n )}\n </header>\n {changes && (\n <aside className=\"mx-4 mt-3 rounded-md bg-warning-soft px-3 py-2 text-sm md:mx-6\">\n <UINodeRenderer node={changes} />\n </aside>\n )}\n <div className=\"px-4 py-4 md:grid md:grid-cols-[13rem_minmax(0,1fr)] 2xl:grid-cols-[15rem_minmax(0,1fr)] md:gap-8 md:px-6\">\n {sections.length > 0 && (\n <nav aria-label=\"Plan sections\" className=\"hidden md:block\">\n <ol className=\"sticky top-16 flex flex-col gap-0.5 border-l border-border text-sm\">\n <li>\n <button\n type=\"button\"\n className=\"-ml-px block w-full truncate border-l-2 border-transparent py-1 pl-3 text-left text-muted hover:text-text\"\n onClick={() => jump(startRef.current)}\n >\n Start\n </button>\n </li>\n {sections.map((s) => (\n <li key={s.id}>\n <button\n type=\"button\"\n aria-current={active === s.id}\n className=\"-ml-px block w-full truncate border-l-2 border-transparent py-1 pl-3 text-left text-muted hover:text-text aria-[current=true]:border-accent aria-[current=true]:text-text\"\n onClick={() => {\n pinSection(s.id);\n jump(\n bodyRef.current?.querySelector<HTMLElement>(`#${CSS.escape(s.id)}`) ?? null,\n );\n }}\n >\n {s.text}\n </button>\n </li>\n ))}\n </ol>\n </nav>\n )}\n <div ref={startRef} className=\"min-w-0 scroll-mt-14\">\n <div\n ref={bodyRef}\n className=\"sy-prose-compact max-w-[80ch] min-w-0 2xl:max-w-[100ch] min-[1800px]:text-base [&_h2]:scroll-mt-14\"\n >\n <Markdown text={rest} headingIds className=\"text-inherit\" />\n </div>\n </div>\n </div>\n <section\n ref={optionsRef}\n data-testid=\"plan-options\"\n aria-label={question}\n className=\"border-t border-border px-4 py-3 md:px-6\"\n >\n <p className=\"mb-2 text-sm font-medium\">{question}</p>\n {!disabled && (\n <div className=\"flex min-w-0 flex-col gap-2 md:flex-row md:flex-wrap\">\n {options.map((o, i) => (\n <Button\n key={o.id}\n variant={variant(i, o.tone)}\n size=\"md\"\n disabled={busy}\n className={cn(wrapBtn, \"md:text-center\")}\n onClick={() => answer(o.id)}\n >\n {o.label}\n </Button>\n ))}\n </div>\n )}\n <TextLink\n size=\"xs\"\n className=\"mt-2\"\n icon={<ArrowUp aria-hidden className=\"size-3.5\" />}\n onClick={() => jump(startRef.current)}\n >\n Back to the plan start\n </TextLink>\n </section>\n {showBar && (\n <div\n data-testid=\"answer-bar\"\n className={cn(\n \"z-10 flex min-w-0 items-center gap-2 border-t border-border bg-bg px-4 py-2 md:gap-3 md:px-6\",\n sticky && !flush ? \"sticky -bottom-3\" : \"sticky bottom-0\",\n )}\n >\n {mobile ? (\n <>\n <IconButton\n label=\"Plan start\"\n icon={<ArrowUp className=\"size-4\" />}\n variant=\"secondary\"\n onClick={() => jump(startRef.current)}\n />\n {options.length > 2 ? (\n <>\n <Button\n variant=\"primary\"\n disabled={busy}\n className={cn(wrapBtn, \"flex-1\")}\n onClick={() => answer(options[0]!.id)}\n >\n {options[0]!.label}\n </Button>\n <Button variant=\"secondary\" onClick={() => setSheet(true)}>\n More ({options.length - 1})\n </Button>\n </>\n ) : (\n options.map((o, i) => (\n <Button\n key={o.id}\n variant={variant(i, o.tone)}\n disabled={busy}\n className={cn(wrapBtn, \"flex-1\")}\n onClick={() => answer(o.id)}\n >\n {o.label}\n </Button>\n ))\n )}\n </>\n ) : (\n <>\n <span\n className=\"max-w-[16rem] min-w-0 shrink truncate text-sm font-medium\"\n title={question}\n >\n {question}\n </span>\n <div className=\"grid min-w-0 flex-1 grid-cols-[repeat(auto-fit,minmax(11rem,1fr))] gap-2\">\n {options.map((o, i) => (\n <Button\n key={o.id}\n variant={variant(i, o.tone)}\n disabled={busy}\n className={cn(wrapBtn, \"w-full md:text-center\")}\n onClick={() => answer(o.id)}\n >\n {o.label}\n </Button>\n ))}\n </div>\n <IconButton\n label=\"Plan start\"\n icon={<ArrowUp className=\"size-4\" />}\n variant=\"secondary\"\n size=\"sm\"\n onClick={() => jump(startRef.current)}\n />\n </>\n )}\n </div>\n )}\n <Sheet open={sheet} onClose={() => setSheet(false)} title={question}>\n <div className=\"flex flex-col gap-2\">\n {options.map((o, i) => (\n <Button\n key={o.id}\n block\n variant={variant(i, o.tone)}\n disabled={busy}\n className={wrapBtn}\n onClick={() => answer(o.id)}\n >\n {o.label}\n </Button>\n ))}\n </div>\n </Sheet>\n </article>\n );\n}\n"],"ignoreList":[0],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{_ as e,m as t,n,t as r}from"./jsx-runtime-D13aM9HY.js";import{_ as i}from"./states-ZhrxQa3K.js";import{i as a}from"./Copy-B6XSAIUd.js";var o={name:`chevron-down`,size:24,node:[[`path`,{d:`m6 9 6 6 6-6`,key:`qrunsl`}]]};o.node;var s=n(o),c={name:`panel-right-open`,size:24,node:[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M15 3v18`,key:`14nvp0`}],[`path`,{d:`m10 15-3-3 3-3`,key:`1pgupc`}]]};c.node;var l=n(c),u=e(t(),1),d=r();function f({trigger:e,items:t,align:n=`end`,side:r=`bottom`,label:o,className:s}){let[c,l]=(0,u.useState)(!1),f=(0,u.useId)(),m=(0,u.useRef)(null),h=(0,u.useRef)(null),g=e=>p(h.current,e);(0,u.useEffect)(()=>{if(!c)return;requestAnimationFrame(()=>p(h.current,`first`));let e=e=>{!h.current?.contains(e.target)&&!m.current?.contains(e.target)&&l(!1)};return document.addEventListener(`pointerdown`,e),()=>document.removeEventListener(`pointerdown`,e)},[c]);let _=(e=!0)=>{l(!1),e&&m.current?.focus()};return(0,d.jsxs)(`div`,{className:i(`relative inline-flex`,s),children:[e({ref:m,onClick:()=>l(e=>!e),onKeyDown:e=>{(e.key===`ArrowDown`||e.key===`ArrowUp`)&&(e.preventDefault(),l(!0))},"aria-haspopup":`menu`,"aria-expanded":c,"aria-controls":f}),c&&(0,d.jsx)(`div`,{ref:h,id:f,role:`menu`,"aria-label":o,tabIndex:-1,className:i(`absolute z-40 min-w-48 max-w-[min(20rem,calc(100vw-1rem))] rounded-lg border border-border bg-surface py-1 shadow-pop`,r===`top`?`bottom-full mb-1`:`top-full mt-1`,n===`end`?`right-0`:`left-0`),onKeyDown:e=>{e.key===`ArrowDown`?(e.preventDefault(),g(1)):e.key===`ArrowUp`?(e.preventDefault(),g(-1)):e.key===`Home`?(e.preventDefault(),g(`first`)):e.key===`End`?(e.preventDefault(),g(`last`)):e.key===`Escape`?(e.preventDefault(),e.stopPropagation(),_()):e.key===`Tab`&&_(!1)},children:t.map((e,t)=>e===`separator`?(0,d.jsx)(`hr`,{className:`my-1 border-t border-border`},`sep-${t}`):`heading`in e?(0,d.jsx)(`div`,{role:`presentation`,className:`px-3 pt-1.5 pb-1 text-xs font-medium text-muted`,children:e.heading},e.id):(0,d.jsxs)(`button`,{type:`button`,role:e.kind===`radio`?`menuitemradio`:e.kind===`checkbox`?`menuitemcheckbox`:`menuitem`,"aria-checked":e.kind?!!e.checked:void 0,disabled:e.disabled,className:i(`tap flex w-full items-start gap-2 px-3 py-2 text-left text-sm hover:bg-surface-2 focus:bg-surface-2 focus:outline-none disabled:opacity-55`,e.danger?`text-danger`:`text-text`),onClick:()=>{_(),e.onSelect()},children:[e.kind?(0,d.jsx)(`span`,{"aria-hidden":!0,className:`mt-0.5 size-4 shrink-0`,children:e.checked&&(0,d.jsx)(a,{className:`size-4`})}):e.icon&&(0,d.jsx)(`span`,{className:`mt-0.5 shrink-0`,children:e.icon}),(0,d.jsxs)(`span`,{className:`min-w-0`,children:[(0,d.jsx)(`span`,{className:`block`,children:e.label}),e.hint&&(0,d.jsx)(`span`,{className:`block text-xs text-muted`,children:e.hint})]})]},e.id))})]})}function p(e,t){let n=[...e?.querySelectorAll(`[role^="menuitem"]:not([disabled])`)??[]];if(n.length===0)return;let r=n.indexOf(document.activeElement),i=0;i=t===`first`?0:t===`last`?n.length-1:(r+t+n.length)%n.length,n[i].focus()}export{l as n,s as r,f as t};
2
+ //# sourceMappingURL=Menu-BfOszNC4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Menu-BfOszNC4.js","names":["__iconData","useState","useId","useRef","__toESM","require_react","require_jsx_runtime","cn","Check","__iconData$1","ChevronDown","createLucideIcon","__iconData","PanelRightOpen","import_react","import_jsx_runtime","Menu","open","setOpen","id","btnRef","menuRef","focusItem","focusMenuItem","dir","onDoc","close","refocus","className","trigger","o","label","side","align","items","item","i","els","menu","idx","next"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.61.0_@types+react@19.3.0_react@19.3.1/node_modules/lucide-react/dist/esm/icons/chevron-down.mjs","../../../../node_modules/.pnpm/lucide-react@1.61.0_@types+react@19.3.0_react@19.3.1/node_modules/lucide-react/dist/esm/icons/panel-right-open.mjs","../../src/components/ui/Menu.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.61.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"chevron-down\",\n size: 24,\n node: [[\"path\", { d: \"m6 9 6 6 6-6\", key: \"qrunsl\" }]]\n};\n__iconData.node;\nconst ChevronDown = createLucideIcon(__iconData);\n\nexport { __iconData, ChevronDown as default };\n//# sourceMappingURL=chevron-down.mjs.map\n","/**\n * @license lucide-react v1.61.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"panel-right-open\",\n size: 24,\n node: [\n [\"rect\", { width: \"18\", height: \"18\", x: \"3\", y: \"3\", rx: \"2\", key: \"afitv7\" }],\n [\"path\", { d: \"M15 3v18\", key: \"14nvp0\" }],\n [\"path\", { d: \"m10 15-3-3 3-3\", key: \"1pgupc\" }]\n ]\n};\n__iconData.node;\nconst PanelRightOpen = createLucideIcon(__iconData);\n\nexport { __iconData, PanelRightOpen as default };\n//# sourceMappingURL=panel-right-open.mjs.map\n","import { Check } from \"lucide-react\";\nimport { type ReactNode, useEffect, useId, useRef, useState } from \"react\";\nimport { cn } from \"../../lib/cn\";\n\nexport interface MenuItem {\n id: string;\n label: string;\n icon?: ReactNode;\n onSelect: () => void;\n danger?: boolean;\n disabled?: boolean;\n /** Short explanation shown under the label (e.g. why it is disabled). */\n hint?: string;\n /** A radio or checkbox item shows a check mark when `checked`. */\n kind?: \"radio\" | \"checkbox\";\n checked?: boolean;\n}\n\n/** A non-interactive label above a group of items. */\nexport interface MenuHeading {\n id: string;\n heading: string;\n}\n\nexport interface MenuProps {\n /** Render the trigger; spread the given props onto a button. */\n trigger: (props: {\n ref: React.Ref<HTMLButtonElement>;\n onClick: () => void;\n onKeyDown: (e: React.KeyboardEvent) => void;\n \"aria-haspopup\": \"menu\";\n \"aria-expanded\": boolean;\n \"aria-controls\": string;\n }) => ReactNode;\n items: (MenuItem | MenuHeading | \"separator\")[];\n align?: \"start\" | \"end\";\n /** Open below (default) or above the trigger. */\n side?: \"bottom\" | \"top\";\n /** Extra classes for the wrapper (e.g. `w-full`). */\n className?: string;\n /** Accessible name of the menu. */\n label: string;\n}\n\n/** Popup menu with arrow-key navigation, Escape and outside click to close. */\nexport function Menu({\n trigger,\n items,\n align = \"end\",\n side = \"bottom\",\n label,\n className,\n}: MenuProps) {\n const [open, setOpen] = useState(false);\n const id = useId();\n const btnRef = useRef<HTMLButtonElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n\n const focusItem = (dir: 1 | -1 | \"first\" | \"last\") => focusMenuItem(menuRef.current, dir);\n\n useEffect(() => {\n if (!open) return;\n requestAnimationFrame(() => focusMenuItem(menuRef.current, \"first\"));\n const onDoc = (e: PointerEvent) => {\n if (\n !menuRef.current?.contains(e.target as Node) &&\n !btnRef.current?.contains(e.target as Node)\n ) {\n setOpen(false);\n }\n };\n document.addEventListener(\"pointerdown\", onDoc);\n return () => document.removeEventListener(\"pointerdown\", onDoc);\n }, [open]);\n\n const close = (refocus = true) => {\n setOpen(false);\n if (refocus) btnRef.current?.focus();\n };\n\n return (\n <div className={cn(\"relative inline-flex\", className)}>\n {trigger({\n ref: btnRef,\n onClick: () => setOpen((o) => !o),\n onKeyDown: (e) => {\n if (e.key === \"ArrowDown\" || e.key === \"ArrowUp\") {\n e.preventDefault();\n setOpen(true);\n }\n },\n \"aria-haspopup\": \"menu\",\n \"aria-expanded\": open,\n \"aria-controls\": id,\n })}\n {open && (\n <div\n ref={menuRef}\n id={id}\n role=\"menu\"\n aria-label={label}\n tabIndex={-1}\n className={cn(\n \"absolute z-40 min-w-48 max-w-[min(20rem,calc(100vw-1rem))] rounded-lg border border-border bg-surface py-1 shadow-pop\",\n side === \"top\" ? \"bottom-full mb-1\" : \"top-full mt-1\",\n align === \"end\" ? \"right-0\" : \"left-0\",\n )}\n onKeyDown={(e) => {\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n focusItem(1);\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n focusItem(-1);\n } else if (e.key === \"Home\") {\n e.preventDefault();\n focusItem(\"first\");\n } else if (e.key === \"End\") {\n e.preventDefault();\n focusItem(\"last\");\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n e.stopPropagation();\n close();\n } else if (e.key === \"Tab\") {\n close(false);\n }\n }}\n >\n {items.map((item, i) =>\n item === \"separator\" ? (\n // biome-ignore lint/suspicious/noArrayIndexKey: separators have no identity\n <hr key={`sep-${i}`} className=\"my-1 border-t border-border\" />\n ) : \"heading\" in item ? (\n <div\n key={item.id}\n role=\"presentation\"\n className=\"px-3 pt-1.5 pb-1 text-xs font-medium text-muted\"\n >\n {item.heading}\n </div>\n ) : (\n // biome-ignore lint/a11y/useAriaPropsSupportedByRole: aria-checked is only set with the menuitemradio/menuitemcheckbox roles\n <button\n key={item.id}\n type=\"button\"\n role={\n item.kind === \"radio\"\n ? \"menuitemradio\"\n : item.kind === \"checkbox\"\n ? \"menuitemcheckbox\"\n : \"menuitem\"\n }\n aria-checked={item.kind ? !!item.checked : undefined}\n disabled={item.disabled}\n className={cn(\n \"tap flex w-full items-start gap-2 px-3 py-2 text-left text-sm hover:bg-surface-2 focus:bg-surface-2 focus:outline-none disabled:opacity-55\",\n item.danger ? \"text-danger\" : \"text-text\",\n )}\n onClick={() => {\n close();\n item.onSelect();\n }}\n >\n {item.kind ? (\n <span aria-hidden className=\"mt-0.5 size-4 shrink-0\">\n {item.checked && <Check className=\"size-4\" />}\n </span>\n ) : (\n item.icon && <span className=\"mt-0.5 shrink-0\">{item.icon}</span>\n )}\n <span className=\"min-w-0\">\n <span className=\"block\">{item.label}</span>\n {item.hint && <span className=\"block text-xs text-muted\">{item.hint}</span>}\n </span>\n </button>\n ),\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction focusMenuItem(menu: HTMLElement | null, dir: 1 | -1 | \"first\" | \"last\"): void {\n const els = [\n ...(menu?.querySelectorAll<HTMLElement>('[role^=\"menuitem\"]:not([disabled])') ?? []),\n ];\n if (els.length === 0) return;\n const idx = els.indexOf(document.activeElement as HTMLElement);\n let next = 0;\n if (dir === \"first\") next = 0;\n else if (dir === \"last\") next = els.length - 1;\n else next = (idx + dir + els.length) % els.length;\n els[next]!.focus();\n}\n"],"ignoreList":[0,1],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{_ as e,m as t,t as n}from"./jsx-runtime-D13aM9HY.js";import{_ as r,a as i,d as a,n as o,yt as s}from"./states-ZhrxQa3K.js";import{j as c,rt as l}from"./index-hxvDvCS7.js";var u=l(),d=e(t(),1),f=n(),p=(0,d.forwardRef)(function({label:e,icon:t,variant:n=`ghost`,size:i=`md`,className:a,type:o,...s},c){return(0,f.jsx)(`button`,{ref:c,type:o??`button`,"aria-label":e,title:e,className:r(`tap inline-flex shrink-0 items-center justify-center rounded-md border transition-colors`,`disabled:opacity-55 disabled:pointer-events-none`,i===`sm`?`size-8`:`size-9`,n===`ghost`&&`border-transparent text-muted hover:bg-surface-2 hover:text-text`,n===`secondary`&&`border-border bg-surface text-text hover:bg-surface-2`,n===`danger`&&`border-transparent text-danger hover:bg-danger-soft`,a),...s,children:t})}),m=`a[href], area[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), iframe, [tabindex]:not([tabindex="-1"]), [contenteditable="true"]`;function h(e){return[...e.querySelectorAll(m)].filter(e=>!e.hasAttribute(`inert`)&&e.getClientRects().length>0)}function g(e,t,n,r){let i=(0,d.useRef)(n);i.current=n;let a=(0,d.useRef)(null),o=(0,d.useRef)(!1);t&&!o.current&&typeof document<`u`&&(a.current=document.activeElement),o.current=t,(0,d.useEffect)(()=>{if(!t)return;let n=e.current;if(!n)return;let o=a.current,s=o&&!n.contains(o)?o:document.activeElement,c=r?.current??h(n)[0]??n,l=requestAnimationFrame(()=>{(r?.current||!n.contains(document.activeElement))&&c.focus({preventScroll:!0})}),u=e=>{if(e.key===`Escape`&&i.current){e.stopPropagation(),i.current();return}if(e.key!==`Tab`)return;let t=h(n);if(t.length===0){e.preventDefault(),n.focus();return}let r=t[0],a=t[t.length-1],o=document.activeElement;e.shiftKey&&(o===r||!n.contains(o))?(e.preventDefault(),a.focus()):e.shiftKey||o!==a&&n.contains(o)||(e.preventDefault(),r.focus())};return n.addEventListener(`keydown`,u),()=>{cancelAnimationFrame(l),n.removeEventListener(`keydown`,u),s&&document.contains(s)&&s.focus({preventScroll:!0})}},[t,e,r])}function _(e){(0,d.useEffect)(()=>{if(!e)return;let t=document.body.style.overflow;return document.body.style.overflow=`hidden`,()=>{document.body.style.overflow=t}},[e])}function v({placement:e,open:t,onClose:n,title:i,description:a,children:o,footer:s,initialFocus:l,dismissOnBackdrop:m=!0,headerActions:h,className:v,flush:y,bodyClassName:b,fullHeight:x,style:S}){let C=(0,d.useRef)(null),w=(0,d.useId)(),T=(0,d.useId)();return g(C,t,n,l),_(t),t?(0,u.createPortal)((0,f.jsxs)(`div`,{className:r(`fixed inset-0 z-50 flex`,e===`right`&&`justify-end`,e===`bottom`&&`items-end`,e===`center`&&`items-center justify-center p-4`),children:[(0,f.jsx)(`div`,{className:`absolute inset-0 bg-overlay`,"aria-hidden":!0,onClick:m?n:void 0}),(0,f.jsxs)(`div`,{ref:C,role:`dialog`,"aria-modal":`true`,"aria-labelledby":w,"aria-describedby":a?T:void 0,tabIndex:-1,style:S,className:r(`relative flex flex-col bg-surface text-text shadow-pop outline-none`,e===`right`&&`h-full max-w-full border-l border-border safe-pt safe-pb`,e===`bottom`&&(x?`h-dvh w-full safe-pt safe-pb`:`max-h-[90dvh] w-full rounded-t-xl border-t border-border safe-pb`),e===`center`&&`max-h-[min(90dvh,720px)] w-full max-w-lg rounded-xl border border-border`,v),children:[e===`bottom`&&!x&&(0,f.jsx)(`div`,{className:`flex justify-center pt-2`,"aria-hidden":!0,children:(0,f.jsx)(`div`,{className:`h-1 w-10 rounded-full bg-border-strong`})}),(0,f.jsxs)(`div`,{className:`flex items-start gap-2 border-b border-border px-4 py-3`,children:[(0,f.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,f.jsx)(`h2`,{id:w,className:`text-base font-semibold leading-6`,children:i}),a&&(0,f.jsx)(`p`,{id:T,className:`text-sm text-muted`,children:a})]}),h,(0,f.jsx)(p,{label:`Close`,icon:(0,f.jsx)(c,{className:`size-4`}),onClick:n})]}),(0,f.jsx)(`div`,{className:r(`min-h-0 flex-1 overflow-y-auto overscroll-contain`,!y&&`px-4 py-3`,b),children:o}),s&&(0,f.jsx)(`div`,{className:`flex flex-wrap items-center justify-end gap-2 border-t border-border px-4 py-3`,children:s})]})]}),document.body):null}function y({width:e=520,...t}){return(0,f.jsx)(v,{...t,placement:`right`,style:{width:`min(${e}px, 100vw)`}})}function b(e){return(0,f.jsx)(v,{...e,placement:`bottom`})}function x(e){return(0,f.jsx)(v,{...e,placement:`center`})}function S({open:e,onClose:t,title:n,description:r,confirmLabel:c,onConfirm:l,busy:u,error:d,errorTitle:p,danger:m=!0,confirmIcon:h,confirmDisabled:g,disabledReason:_,children:v}){return(0,f.jsx)(x,{open:e,onClose:u?()=>void 0:t,dismissOnBackdrop:!u,title:n,description:r,footer:(0,f.jsxs)(f.Fragment,{children:[g&&_&&(0,f.jsx)(`p`,{className:`mr-auto min-w-0 flex-1 basis-full text-xs text-muted sm:basis-auto`,children:_}),(0,f.jsx)(a,{onClick:t,disabled:u,children:`Cancel`}),(0,f.jsx)(a,{variant:m?`danger`:`primary`,icon:h,busy:u,disabled:g,onClick:l,children:c})]}),children:(0,f.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[v,p?!!d&&(0,f.jsx)(o,{compact:!0,title:p,error:s(d)}):(0,f.jsx)(i,{error:d})]})})}export{g as a,b as i,x as n,p as o,y as r,S as t};
2
+ //# sourceMappingURL=Overlay-DUQwh6_b.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Overlay-DUQwh6_b.js","names":["forwardRef","useRef","useRef","useId","createPortal","_Fragment","__toESM","require_react","require_jsx_runtime","cn","InlineError","Button","ErrorState","errorMessage","X","require_react_dom","import_react_dom","import_react","import_jsx_runtime","IconButton","rest","type","label","size","variant","className","icon","FOCUSABLE","focusableWithin","root","el","useFocusTrap","escRef","onEscape","triggerRef","wasActive","active","ref","captured","previous","target","initialFocus","raf","onKey","items","first","last","activeEl","useScrollLock","prev","Surface","titleId","descId","open","onClose","placement","dismissOnBackdrop","description","fullHeight","title","headerActions","flush","bodyClassName","footer","Drawer","props","width","Sheet","Dialog","ConfirmDialog","busy","confirmDisabled","disabledReason","danger","confirmIcon","onConfirm","confirmLabel","children","errorTitle","error"],"sources":["../../src/components/ui/IconButton.tsx","../../src/components/ui/useFocusTrap.ts","../../src/components/ui/Overlay.tsx"],"sourcesContent":["import { type ButtonHTMLAttributes, forwardRef, type ReactNode } from \"react\";\nimport { cn } from \"../../lib/cn\";\n\nexport interface IconButtonProps\n extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"aria-label\"> {\n /** Accessible name (also the tooltip). Required: icon-only controls need a label. */\n label: string;\n icon: ReactNode;\n variant?: \"ghost\" | \"secondary\" | \"danger\";\n size?: \"sm\" | \"md\";\n}\n\nexport const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(function IconButton(\n { label, icon, variant = \"ghost\", size = \"md\", className, type, ...rest },\n ref,\n) {\n return (\n <button\n ref={ref}\n type={type ?? \"button\"}\n aria-label={label}\n title={label}\n className={cn(\n \"tap inline-flex shrink-0 items-center justify-center rounded-md border transition-colors\",\n \"disabled:opacity-55 disabled:pointer-events-none\",\n size === \"sm\" ? \"size-8\" : \"size-9\",\n variant === \"ghost\" && \"border-transparent text-muted hover:bg-surface-2 hover:text-text\",\n variant === \"secondary\" && \"border-border bg-surface text-text hover:bg-surface-2\",\n variant === \"danger\" && \"border-transparent text-danger hover:bg-danger-soft\",\n className,\n )}\n {...rest}\n >\n {icon}\n </button>\n );\n});\n","import { type RefObject, useEffect, useRef } from \"react\";\n\nconst FOCUSABLE =\n 'a[href], area[href], button:not([disabled]), input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), iframe, [tabindex]:not([tabindex=\"-1\"]), [contenteditable=\"true\"]';\n\nexport function focusableWithin(root: HTMLElement): HTMLElement[] {\n return [...root.querySelectorAll<HTMLElement>(FOCUSABLE)].filter(\n (el) => !el.hasAttribute(\"inert\") && el.getClientRects().length > 0,\n );\n}\n\n/**\n * Modal focus management: moves focus into `ref` when `active`, keeps Tab\n * inside it, closes on Escape, and returns focus to the element that had it\n * (the trigger) when deactivated.\n */\nexport function useFocusTrap(\n ref: RefObject<HTMLElement | null>,\n active: boolean,\n onEscape?: () => void,\n initialFocus?: RefObject<HTMLElement | null>,\n): void {\n const escRef = useRef(onEscape);\n escRef.current = onEscape;\n // Remember the trigger while rendering the opening frame: by the time effects run, an\n // `autoFocus` field inside the surface has already taken focus.\n const triggerRef = useRef<HTMLElement | null>(null);\n const wasActive = useRef(false);\n if (active && !wasActive.current && typeof document !== \"undefined\") {\n triggerRef.current = document.activeElement as HTMLElement | null;\n }\n wasActive.current = active;\n useEffect(() => {\n if (!active) return;\n const root = ref.current;\n if (!root) return;\n const captured = triggerRef.current;\n const previous =\n captured && !root.contains(captured)\n ? captured\n : (document.activeElement as HTMLElement | null);\n const target = initialFocus?.current ?? focusableWithin(root)[0] ?? root;\n // Defer so the element is laid out (portals, transitions).\n // An `autoFocus` field inside keeps its focus unless an explicit initial focus was given.\n const raf = requestAnimationFrame(() => {\n if (!initialFocus?.current && root.contains(document.activeElement)) return;\n target.focus({ preventScroll: true });\n });\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\" && escRef.current) {\n e.stopPropagation();\n escRef.current();\n return;\n }\n if (e.key !== \"Tab\") return;\n const items = focusableWithin(root);\n if (items.length === 0) {\n e.preventDefault();\n root.focus();\n return;\n }\n const first = items[0]!;\n const last = items[items.length - 1]!;\n const activeEl = document.activeElement;\n if (e.shiftKey && (activeEl === first || !root.contains(activeEl))) {\n e.preventDefault();\n last.focus();\n } else if (!e.shiftKey && (activeEl === last || !root.contains(activeEl))) {\n e.preventDefault();\n first.focus();\n }\n };\n root.addEventListener(\"keydown\", onKey);\n return () => {\n cancelAnimationFrame(raf);\n root.removeEventListener(\"keydown\", onKey);\n if (previous && document.contains(previous)) previous.focus({ preventScroll: true });\n };\n }, [active, ref, initialFocus]);\n}\n\n/** Lock body scroll while a modal surface is open. */\nexport function useScrollLock(active: boolean): void {\n useEffect(() => {\n if (!active) return;\n const prev = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = prev;\n };\n }, [active]);\n}\n","import { X } from \"lucide-react\";\nimport { type ReactNode, type RefObject, useId, useRef } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../../lib/cn\";\nimport { errorMessage } from \"../../sync/api\";\nimport { Button } from \"./Button\";\nimport { IconButton } from \"./IconButton\";\nimport { ErrorState, InlineError } from \"./states\";\nimport { useFocusTrap, useScrollLock } from \"./useFocusTrap\";\n\nexport interface OverlayProps {\n open: boolean;\n onClose: () => void;\n /** Visible heading; also the accessible name. */\n title: ReactNode;\n /** Accessible description shown under the title. */\n description?: ReactNode;\n children?: ReactNode;\n /** Sticky footer (actions). */\n footer?: ReactNode;\n /** Element to focus first (defaults to the first focusable). */\n initialFocus?: RefObject<HTMLElement | null>;\n /** Close when the backdrop is clicked (default true). */\n dismissOnBackdrop?: boolean;\n /** Extra header controls before the close button. */\n headerActions?: ReactNode;\n className?: string;\n /** No padding around the body (for content that draws its own edges). */\n flush?: boolean;\n /** Extra body classes. Additive only: it cannot override the padding, use `flush`. */\n bodyClassName?: string;\n /** Bottom sheet only: fill the viewport height. */\n fullHeight?: boolean;\n}\n\ntype Placement = \"right\" | \"bottom\" | \"center\";\n\nfunction Surface({\n placement,\n open,\n onClose,\n title,\n description,\n children,\n footer,\n initialFocus,\n dismissOnBackdrop = true,\n headerActions,\n className,\n flush,\n bodyClassName,\n fullHeight,\n style,\n}: OverlayProps & { placement: Placement; style?: React.CSSProperties }) {\n const ref = useRef<HTMLDivElement>(null);\n const titleId = useId();\n const descId = useId();\n useFocusTrap(ref, open, onClose, initialFocus);\n useScrollLock(open);\n if (!open) return null;\n return createPortal(\n <div\n className={cn(\n \"fixed inset-0 z-50 flex\",\n placement === \"right\" && \"justify-end\",\n placement === \"bottom\" && \"items-end\",\n placement === \"center\" && \"items-center justify-center p-4\",\n )}\n >\n <div\n className=\"absolute inset-0 bg-overlay\"\n aria-hidden\n onClick={dismissOnBackdrop ? onClose : undefined}\n />\n <div\n ref={ref}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={description ? descId : undefined}\n tabIndex={-1}\n style={style}\n className={cn(\n \"relative flex flex-col bg-surface text-text shadow-pop outline-none\",\n placement === \"right\" && \"h-full max-w-full border-l border-border safe-pt safe-pb\",\n placement === \"bottom\" &&\n (fullHeight\n ? \"h-dvh w-full safe-pt safe-pb\"\n : \"max-h-[90dvh] w-full rounded-t-xl border-t border-border safe-pb\"),\n placement === \"center\" &&\n \"max-h-[min(90dvh,720px)] w-full max-w-lg rounded-xl border border-border\",\n className,\n )}\n >\n {placement === \"bottom\" && !fullHeight && (\n <div className=\"flex justify-center pt-2\" aria-hidden>\n <div className=\"h-1 w-10 rounded-full bg-border-strong\" />\n </div>\n )}\n <div className=\"flex items-start gap-2 border-b border-border px-4 py-3\">\n <div className=\"min-w-0 flex-1\">\n <h2 id={titleId} className=\"text-base font-semibold leading-6\">\n {title}\n </h2>\n {description && (\n <p id={descId} className=\"text-sm text-muted\">\n {description}\n </p>\n )}\n </div>\n {headerActions}\n <IconButton label=\"Close\" icon={<X className=\"size-4\" />} onClick={onClose} />\n </div>\n <div\n className={cn(\n \"min-h-0 flex-1 overflow-y-auto overscroll-contain\",\n !flush && \"px-4 py-3\",\n bodyClassName,\n )}\n >\n {children}\n </div>\n {footer && (\n <div className=\"flex flex-wrap items-center justify-end gap-2 border-t border-border px-4 py-3\">\n {footer}\n </div>\n )}\n </div>\n </div>,\n document.body,\n );\n}\n\nexport interface DrawerProps extends OverlayProps {\n /** Width in px (default 520, the task drawer width). Never wider than the viewport. */\n width?: number;\n}\n\n/** Right-side drawer with focus trap, Escape to close, and focus returned to the trigger. */\nexport function Drawer({ width = 520, ...props }: DrawerProps) {\n return <Surface {...props} placement=\"right\" style={{ width: `min(${width}px, 100vw)` }} />;\n}\n\n/** Bottom sheet for mobile; same accessibility behavior as Drawer. */\nexport function Sheet(props: OverlayProps) {\n return <Surface {...props} placement=\"bottom\" />;\n}\n\n/** Centered modal dialog. */\nexport function Dialog(props: OverlayProps) {\n return <Surface {...props} placement=\"center\" />;\n}\n\nexport interface ConfirmDialogProps {\n open: boolean;\n onClose: () => void;\n title: ReactNode;\n description?: ReactNode;\n confirmLabel: string;\n onConfirm: () => void;\n busy?: boolean;\n error?: unknown;\n /** Heading for the error; renders a titled error panel instead of the plain message. */\n errorTitle?: string;\n danger?: boolean;\n /** Icon before the confirm label. */\n confirmIcon?: ReactNode;\n confirmDisabled?: boolean;\n /** Shown next to the actions when the confirm button is disabled. */\n disabledReason?: ReactNode;\n children?: ReactNode;\n}\n\n/**\n * Confirmation dialog. Never closes on its own: the caller closes it after the\n * server acknowledges (no optimistic success). It cannot be dismissed while busy.\n */\nexport function ConfirmDialog({\n open,\n onClose,\n title,\n description,\n confirmLabel,\n onConfirm,\n busy,\n error,\n errorTitle,\n danger = true,\n confirmIcon,\n confirmDisabled,\n disabledReason,\n children,\n}: ConfirmDialogProps) {\n return (\n <Dialog\n open={open}\n onClose={busy ? () => undefined : onClose}\n dismissOnBackdrop={!busy}\n title={title}\n description={description}\n footer={\n <>\n {confirmDisabled && disabledReason && (\n <p className=\"mr-auto min-w-0 flex-1 basis-full text-xs text-muted sm:basis-auto\">\n {disabledReason}\n </p>\n )}\n <Button onClick={onClose} disabled={busy}>\n Cancel\n </Button>\n <Button\n variant={danger ? \"danger\" : \"primary\"}\n icon={confirmIcon}\n busy={busy}\n disabled={confirmDisabled}\n onClick={onConfirm}\n >\n {confirmLabel}\n </Button>\n </>\n }\n >\n <div className=\"flex flex-col gap-3\">\n {children}\n {errorTitle ? (\n !!error && <ErrorState compact title={errorTitle} error={errorMessage(error)} />\n ) : (\n <InlineError error={error} />\n )}\n </div>\n </Dialog>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import{n as e,t}from"./jsx-runtime-D13aM9HY.js";import{_ as n,lt as r}from"./states-ZhrxQa3K.js";import{t as i}from"./arrow-down-C-iLrN8I.js";import{t as a}from"./arrow-up-Dj-VTKx4.js";import{A as o}from"./index-hxvDvCS7.js";import{t as s}from"./Select-Cfql4Nwu.js";var c={name:`chevrons-up`,size:24,node:[[`path`,{d:`m17 11-5-5-5 5`,key:`e8nh98`}],[`path`,{d:`m17 18-5-5-5 5`,key:`2avn1x`}]]};c.node;var l=e(c),u={name:`minus`,size:24,node:[[`path`,{d:`M5 12h14`,key:`1ays0h`}]]};u.node;var d=e(u),f=t(),p={0:{label:r[0],icon:l,tone:`danger`},1:{label:r[1],icon:a,tone:`warning`},2:{label:r[2],icon:d,tone:`neutral`},3:{label:r[3],icon:i,tone:`neutral`}};function m({priority:e,className:t,colored:r=!0}){let i=p[e]??p[2],a=i.icon;return(0,f.jsx)(a,{"aria-hidden":!0,className:n(`shrink-0`,r&&o[i.tone],t??`size-4`)})}function h({priority:e,className:t,suffix:r,muted:i}){let a=p[e]??p[2];return(0,f.jsxs)(`span`,{className:n(`inline-flex items-center gap-1`,i?`text-muted`:o[a.tone],t),children:[(0,f.jsx)(m,{priority:e,className:`size-3.5`}),a.label,r]})}function g({value:e,onChange:t,...n}){return(0,f.jsx)(s,{...n,value:e,onChange:e=>t(Number(e.target.value)),children:r.map((e,t)=>(0,f.jsx)(`option`,{value:t,children:e},e))})}export{h as n,g as r,m as t};
2
+ //# sourceMappingURL=Priority-CHmzfoHg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Priority-CHmzfoHg.js","names":["__iconData","createLucideIcon","cn","PRIORITY_LABELS","ArrowDown","ArrowUp","TONE_TEXT","Select","__iconData$1","ChevronsUp","__iconData","Minus","import_jsx_runtime","require_jsx_runtime","PRIORITY_INFO","PriorityIcon","info","priority","Icon","colored","className","PriorityLabel","muted","suffix","PrioritySelect","rest","onChange","i","label"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.61.0_@types+react@19.3.0_react@19.3.1/node_modules/lucide-react/dist/esm/icons/chevrons-up.mjs","../../../../node_modules/.pnpm/lucide-react@1.61.0_@types+react@19.3.0_react@19.3.1/node_modules/lucide-react/dist/esm/icons/minus.mjs","../../src/components/ui/Priority.tsx"],"sourcesContent":["/**\n * @license lucide-react v1.61.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"chevrons-up\",\n size: 24,\n node: [\n [\"path\", { d: \"m17 11-5-5-5 5\", key: \"e8nh98\" }],\n [\"path\", { d: \"m17 18-5-5-5 5\", key: \"2avn1x\" }]\n ]\n};\n__iconData.node;\nconst ChevronsUp = createLucideIcon(__iconData);\n\nexport { __iconData, ChevronsUp as default };\n//# sourceMappingURL=chevrons-up.mjs.map\n","/**\n * @license lucide-react v1.61.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconData = {\n name: \"minus\",\n size: 24,\n node: [[\"path\", { d: \"M5 12h14\", key: \"1ays0h\" }]]\n};\n__iconData.node;\nconst Minus = createLucideIcon(__iconData);\n\nexport { __iconData, Minus as default };\n//# sourceMappingURL=minus.mjs.map\n","import type { Priority } from \"@switchyard/protocol\";\nimport { PRIORITY_LABELS } from \"@switchyard/protocol\";\nimport { ArrowDown, ArrowUp, ChevronsUp, Minus } from \"lucide-react\";\nimport { cn } from \"../../lib/cn\";\nimport { TONE_TEXT } from \"./Badge\";\nimport { Select, type SelectProps } from \"./Select\";\nimport type { StatusInfo } from \"./StatusChip\";\n\nexport const PRIORITY_INFO: Record<Priority, StatusInfo> = {\n 0: { label: PRIORITY_LABELS[0], icon: ChevronsUp, tone: \"danger\" },\n 1: { label: PRIORITY_LABELS[1], icon: ArrowUp, tone: \"warning\" },\n 2: { label: PRIORITY_LABELS[2], icon: Minus, tone: \"neutral\" },\n 3: { label: PRIORITY_LABELS[3], icon: ArrowDown, tone: \"neutral\" },\n};\n\n/** The priority's icon. `className` replaces the default size (size-4). */\nexport function PriorityIcon({\n priority,\n className,\n colored = true,\n}: {\n priority: Priority;\n className?: string;\n /** Tint with the priority's tone (default true). */\n colored?: boolean;\n}) {\n const info = PRIORITY_INFO[priority] ?? PRIORITY_INFO[2];\n const Icon = info.icon;\n return (\n <Icon\n aria-hidden\n className={cn(\"shrink-0\", colored && TONE_TEXT[info.tone], className ?? \"size-4\")}\n />\n );\n}\n\n/** Icon plus label. The text takes the tone unless `muted`; the icon always does. */\nexport function PriorityLabel({\n priority,\n className,\n suffix,\n muted,\n}: {\n priority: Priority;\n className?: string;\n /** Appended to the label, e.g. \" priority\". */\n suffix?: string;\n muted?: boolean;\n}) {\n const info = PRIORITY_INFO[priority] ?? PRIORITY_INFO[2];\n return (\n <span\n className={cn(\n \"inline-flex items-center gap-1\",\n muted ? \"text-muted\" : TONE_TEXT[info.tone],\n className,\n )}\n >\n <PriorityIcon priority={priority} className=\"size-3.5\" />\n {info.label}\n {suffix}\n </span>\n );\n}\n\n/** Select of the four priorities. Give it a label with Field or a <label>. */\nexport function PrioritySelect({\n value,\n onChange,\n ...rest\n}: Omit<SelectProps, \"value\" | \"onChange\" | \"children\"> & {\n value: Priority;\n onChange(p: Priority): void;\n}) {\n return (\n <Select {...rest} value={value} onChange={(e) => onChange(Number(e.target.value) as Priority)}>\n {PRIORITY_LABELS.map((label, i) => (\n <option key={label} value={i}>\n {label}\n </option>\n ))}\n </Select>\n );\n}\n"],"ignoreList":[0,1],"mappings":"A,0QASA,IAAMA,EAAa,CACjB,KAAM,cACN,KAAM,GACN,KAAM,CACJ,CAAC,OAAQ,CAAE,EAAG,iBAAkB,IAAK,QAAS,CAAC,EAC/C,CAAC,OAAQ,CAAE,EAAG,iBAAkB,IAAK,QAAS,CAAC,CACjD,CACF,EACAA,EAAW,KACX,IAAMS,EAAaR,EAAiBD,CAAU,ECTxCU,EAAa,CACjB,KAAM,QACN,KAAM,GACN,KAAM,CAAC,CAAC,OAAQ,CAAE,EAAG,WAAY,IAAK,QAAS,CAAC,CAAC,CACnD,EACAA,EAAW,KACX,IAAMC,EAAQV,EAAiBS,CAAU,E,MCP5BI,EAA8C,CACzD,EAAG,CAAE,MAAOX,EAAgB,GAAI,KAAMM,EAAY,KAAM,QAAS,EACjE,EAAG,CAAE,MAAON,EAAgB,GAAI,KAAME,EAAS,KAAM,SAAU,EAC/D,EAAG,CAAE,MAAOF,EAAgB,GAAI,KAAMQ,EAAO,KAAM,SAAU,EAC7D,EAAG,CAAE,MAAOR,EAAgB,GAAI,KAAMC,EAAW,KAAM,SAAU,CACnE,EAGA,SAAgBW,EAAa,CAC3B,WACA,YACA,UAAU,IAMT,CACD,IAAMC,EAAOF,EAAcG,IAAaH,EAAc,GAChDI,EAAOF,EAAK,KAClB,OACE,EAAAJ,EAAA,IAAA,CAACM,EAAD,CACE,cAAA,GACA,UAAWhB,EAAG,WAAYiB,GAAWb,EAAUU,EAAK,MAAOI,GAAa,QAAQ,CACjF,CAAA,CAEL,CAGA,SAAgBC,EAAc,CAC5B,WACA,YACA,SACA,SAOC,CACD,IAAML,EAAOF,EAAcG,IAAaH,EAAc,GACtD,OACE,EAAAF,EAAA,KAAA,CAAC,OAAD,CACE,UAAWV,EACT,iCACAoB,EAAQ,aAAehB,EAAUU,EAAK,MACtCI,CACF,EALF,SAAA,EAOE,EAAAR,EAAA,IAAA,CAACG,EAAD,CAAwB,WAAU,UAAU,UAAY,CAAA,EACvDC,EAAK,MACLO,CACG,C,EAEV,CAGA,SAAgBC,EAAe,CAC7B,QACA,WACA,GAAGC,GAIF,CACD,OACE,EAAAb,EAAA,IAAA,CAACL,EAAD,CAAQ,GAAIkB,EAAa,QAAO,SAAW,GAAMC,EAAS,OAAO,EAAE,OAAO,KAAK,CAAa,EACzF,SAAAvB,EAAgB,KAAK,EAAO,KAC3B,EAAAS,EAAA,IAAA,CAAC,SAAD,CAAoB,MAAOe,EACxB,SAAAC,CACK,EAFKA,CAEL,CACT,CACK,CAAA,CAEZ,C"}
@@ -1,2 +1,2 @@
1
- import{t as e}from"./jsx-runtime-D13aM9HY.js";import{h as t}from"./states--geW2Ntl.js";var n=e();function r({options:e,value:r,onChange:i,label:a,className:o,block:s}){let c=e.findIndex(e=>e.value===r);return(0,n.jsx)(`div`,{role:`radiogroup`,"aria-label":a,className:t(`inline-flex rounded-lg border border-border bg-surface-2 p-0.5`,s&&`flex w-full`,o),onKeyDown:t=>{let n=-1;t.key===`ArrowRight`||t.key===`ArrowDown`?n=(c+1)%e.length:(t.key===`ArrowLeft`||t.key===`ArrowUp`)&&(n=(c-1+e.length)%e.length),!(n<0)&&(t.preventDefault(),i(e[n].value),t.currentTarget.querySelectorAll(`[role="radio"]`)[n]?.focus())},children:e.map(e=>{let a=e.value===r;return(0,n.jsxs)(`button`,{type:`button`,role:`radio`,"aria-checked":a,tabIndex:a?0:-1,onClick:()=>i(e.value),className:t(`tap inline-flex min-h-8 flex-1 items-center justify-center gap-1.5 rounded-md px-3 text-sm font-medium whitespace-nowrap`,a?`bg-surface text-text shadow-sm border border-border`:`text-muted hover:text-text border border-transparent`),children:[e.icon,e.label,e.badge]},e.value)})})}export{r as t};
2
- //# sourceMappingURL=SegmentedControl-D-2BCZzd.js.map
1
+ import{t as e}from"./jsx-runtime-D13aM9HY.js";import{_ as t}from"./states-ZhrxQa3K.js";var n=e();function r({options:e,value:r,onChange:i,label:a,className:o,block:s}){let c=e.findIndex(e=>e.value===r);return(0,n.jsx)(`div`,{role:`radiogroup`,"aria-label":a,className:t(`inline-flex rounded-lg border border-border bg-surface-2 p-0.5`,s&&`flex w-full`,o),onKeyDown:t=>{let n=-1;t.key===`ArrowRight`||t.key===`ArrowDown`?n=(c+1)%e.length:(t.key===`ArrowLeft`||t.key===`ArrowUp`)&&(n=(c-1+e.length)%e.length),!(n<0)&&(t.preventDefault(),i(e[n].value),t.currentTarget.querySelectorAll(`[role="radio"]`)[n]?.focus())},children:e.map(e=>{let a=e.value===r;return(0,n.jsxs)(`button`,{type:`button`,role:`radio`,"aria-checked":a,tabIndex:a?0:-1,onClick:()=>i(e.value),className:t(`tap inline-flex min-h-8 flex-1 items-center justify-center gap-1.5 rounded-md px-3 text-sm font-medium whitespace-nowrap`,a?`bg-surface text-text shadow-sm border border-border`:`text-muted hover:text-text border border-transparent`),children:[e.icon,e.label,e.badge]},e.value)})})}export{r as t};
2
+ //# sourceMappingURL=SegmentedControl-C_hEVQMB.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SegmentedControl-D-2BCZzd.js","names":["require_jsx_runtime","cn","import_jsx_runtime","SegmentedControl","idx","options","o","value","label","block","className","next","e","onChange","selected"],"sources":["../../src/components/ui/SegmentedControl.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../../lib/cn\";\n\nexport interface SegmentOption<T extends string> {\n value: T;\n label: ReactNode;\n icon?: ReactNode;\n badge?: ReactNode;\n}\n\nexport interface SegmentedControlProps<T extends string> {\n options: SegmentOption<T>[];\n value: T;\n onChange: (value: T) => void;\n /** Accessible name. */\n label: string;\n className?: string;\n block?: boolean;\n}\n\n/** Radio-group style switcher (e.g. mobile Chat / Changes / Details). */\nexport function SegmentedControl<T extends string>({\n options,\n value,\n onChange,\n label,\n className,\n block,\n}: SegmentedControlProps<T>) {\n const idx = options.findIndex((o) => o.value === value);\n return (\n <div\n role=\"radiogroup\"\n aria-label={label}\n className={cn(\n \"inline-flex rounded-lg border border-border bg-surface-2 p-0.5\",\n block && \"flex w-full\",\n className,\n )}\n onKeyDown={(e) => {\n let next = -1;\n if (e.key === \"ArrowRight\" || e.key === \"ArrowDown\") next = (idx + 1) % options.length;\n else if (e.key === \"ArrowLeft\" || e.key === \"ArrowUp\")\n next = (idx - 1 + options.length) % options.length;\n if (next < 0) return;\n e.preventDefault();\n onChange(options[next]!.value);\n const btns = e.currentTarget.querySelectorAll<HTMLElement>('[role=\"radio\"]');\n btns[next]?.focus();\n }}\n >\n {options.map((o) => {\n const selected = o.value === value;\n return (\n // biome-ignore lint/a11y/useSemanticElements: button-based radio group (WAI-ARIA radio pattern)\n <button\n key={o.value}\n type=\"button\"\n role=\"radio\"\n aria-checked={selected}\n tabIndex={selected ? 0 : -1}\n onClick={() => onChange(o.value)}\n className={cn(\n \"tap inline-flex min-h-8 flex-1 items-center justify-center gap-1.5 rounded-md px-3 text-sm font-medium whitespace-nowrap\",\n selected\n ? \"bg-surface text-text shadow-sm border border-border\"\n : \"text-muted hover:text-text border border-transparent\",\n )}\n >\n {o.icon}\n {o.label}\n {o.badge}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":"A,iGAqBA,SAAgBG,EAAmC,CACjD,UACA,QACA,WACA,QACA,YACA,SAC2B,CAC3B,IAAMC,EAAMC,EAAQ,UAAW,GAAMC,EAAE,QAAUC,CAAK,EACtD,OACE,EAAAL,EAAA,IAAA,CAAC,MAAD,CACE,KAAK,aACL,aAAYM,EACZ,UAAWP,EACT,iEACAQ,GAAS,cACTC,CACF,EACA,UAAY,GAAM,CAChB,IAAIC,EAAO,GACPC,EAAE,MAAQ,cAAgBA,EAAE,MAAQ,YAAa,GAAQR,EAAM,GAAKC,EAAQ,QACvEO,EAAE,MAAQ,aAAeA,EAAE,MAAQ,aAC1C,GAAQR,EAAM,EAAIC,EAAQ,QAAUA,EAAQ,QAC1C,IAAO,KACXO,EAAE,eAAe,EACjBC,EAASR,EAAQM,EAAK,CAAE,KAAK,EAE7BC,EADe,cAAc,iBAA8B,gBAC3D,CAAA,CAAKD,EAAK,EAAE,MAAM,EACpB,EAEC,SAAAN,EAAQ,IAAK,GAAM,CAClB,IAAMS,EAAWR,EAAE,QAAUC,EAC7B,OAEE,EAAAL,EAAA,KAAA,CAAC,SAAD,CAEE,KAAK,SACL,KAAK,QACL,eAAcY,EACd,SAAUA,EAAW,EAAI,GACzB,YAAeD,EAASP,EAAE,KAAK,EAC/B,UAAWL,EACT,2HACAa,EACI,sDACA,sDACN,EAZF,SAAA,CAcGR,EAAE,KACFA,EAAE,MACFA,EAAE,KACG,CAhBD,EAAAA,EAAE,KAgBD,CAEZ,CAAC,CACE,CAAA,CAET,C"}
1
+ {"version":3,"file":"SegmentedControl-C_hEVQMB.js","names":["require_jsx_runtime","cn","import_jsx_runtime","SegmentedControl","idx","options","o","value","label","block","className","next","e","onChange","selected"],"sources":["../../src/components/ui/SegmentedControl.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../../lib/cn\";\n\nexport interface SegmentOption<T extends string> {\n value: T;\n label: ReactNode;\n icon?: ReactNode;\n badge?: ReactNode;\n}\n\nexport interface SegmentedControlProps<T extends string> {\n options: SegmentOption<T>[];\n value: T;\n onChange: (value: T) => void;\n /** Accessible name. */\n label: string;\n className?: string;\n block?: boolean;\n}\n\n/** Radio-group style switcher (e.g. mobile Chat / Changes / Details). */\nexport function SegmentedControl<T extends string>({\n options,\n value,\n onChange,\n label,\n className,\n block,\n}: SegmentedControlProps<T>) {\n const idx = options.findIndex((o) => o.value === value);\n return (\n <div\n role=\"radiogroup\"\n aria-label={label}\n className={cn(\n \"inline-flex rounded-lg border border-border bg-surface-2 p-0.5\",\n block && \"flex w-full\",\n className,\n )}\n onKeyDown={(e) => {\n let next = -1;\n if (e.key === \"ArrowRight\" || e.key === \"ArrowDown\") next = (idx + 1) % options.length;\n else if (e.key === \"ArrowLeft\" || e.key === \"ArrowUp\")\n next = (idx - 1 + options.length) % options.length;\n if (next < 0) return;\n e.preventDefault();\n onChange(options[next]!.value);\n const btns = e.currentTarget.querySelectorAll<HTMLElement>('[role=\"radio\"]');\n btns[next]?.focus();\n }}\n >\n {options.map((o) => {\n const selected = o.value === value;\n return (\n // biome-ignore lint/a11y/useSemanticElements: button-based radio group (WAI-ARIA radio pattern)\n <button\n key={o.value}\n type=\"button\"\n role=\"radio\"\n aria-checked={selected}\n tabIndex={selected ? 0 : -1}\n onClick={() => onChange(o.value)}\n className={cn(\n \"tap inline-flex min-h-8 flex-1 items-center justify-center gap-1.5 rounded-md px-3 text-sm font-medium whitespace-nowrap\",\n selected\n ? \"bg-surface text-text shadow-sm border border-border\"\n : \"text-muted hover:text-text border border-transparent\",\n )}\n >\n {o.icon}\n {o.label}\n {o.badge}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":"A,iGAqBA,SAAgBG,EAAmC,CACjD,UACA,QACA,WACA,QACA,YACA,SAC2B,CAC3B,IAAMC,EAAMC,EAAQ,UAAW,GAAMC,EAAE,QAAUC,CAAK,EACtD,OACE,EAAAL,EAAA,IAAA,CAAC,MAAD,CACE,KAAK,aACL,aAAYM,EACZ,UAAWP,EACT,iEACAQ,GAAS,cACTC,CACF,EACA,UAAY,GAAM,CAChB,IAAIC,EAAO,GACPC,EAAE,MAAQ,cAAgBA,EAAE,MAAQ,YAAa,GAAQR,EAAM,GAAKC,EAAQ,QACvEO,EAAE,MAAQ,aAAeA,EAAE,MAAQ,aAC1C,GAAQR,EAAM,EAAIC,EAAQ,QAAUA,EAAQ,QAC1C,IAAO,KACXO,EAAE,eAAe,EACjBC,EAASR,EAAQM,EAAK,CAAE,KAAK,EAE7BC,EADe,cAAc,iBAA8B,gBAC3D,CAAA,CAAKD,EAAK,EAAE,MAAM,EACpB,EAEC,SAAAN,EAAQ,IAAK,GAAM,CAClB,IAAMS,EAAWR,EAAE,QAAUC,EAC7B,OAEE,EAAAL,EAAA,KAAA,CAAC,SAAD,CAEE,KAAK,SACL,KAAK,QACL,eAAcY,EACd,SAAUA,EAAW,EAAI,GACzB,YAAeD,EAASP,EAAE,KAAK,EAC/B,UAAWL,EACT,2HACAa,EACI,sDACA,sDACN,EAZF,SAAA,CAcGR,EAAE,KACFA,EAAE,MACFA,EAAE,KACG,CAhBD,EAAAA,EAAE,KAgBD,CAEZ,CAAC,CACE,CAAA,CAET,C"}
@@ -0,0 +1,2 @@
1
+ import{_ as e,m as t,t as n}from"./jsx-runtime-D13aM9HY.js";import{_ as r}from"./states-ZhrxQa3K.js";import{t as i}from"./TextArea-CKybrQyn.js";var a=e(t(),1),o=n(),s=(0,a.forwardRef)(function({className:e,width:t=`full`,children:n,...a},s){return(0,o.jsx)(`select`,{ref:s,className:r(`tap block h-9 min-w-0 px-2.5`,t===`auto`?`w-auto`:`w-full`,i,e),...a,children:n})});export{s as t};
2
+ //# sourceMappingURL=Select-Cfql4Nwu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Select-Cfql4Nwu.js","names":["forwardRef","__toESM","require_react","require_jsx_runtime","cn","CONTROL_CLASSES","import_react","import_jsx_runtime","Select","rest","width","className"],"sources":["../../src/components/ui/Select.tsx"],"sourcesContent":["import { forwardRef, type SelectHTMLAttributes } from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { CONTROL_CLASSES } from \"./TextArea\";\n\nexport interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {\n /** \"full\" (default) fills its container; \"auto\" sizes to content for inline toolbars. */\n width?: \"full\" | \"auto\";\n}\n\n/** Native select with the same look, focus ring and invalid state as TextInput. */\nexport const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select(\n { className, width = \"full\", children, ...rest },\n ref,\n) {\n return (\n <select\n ref={ref}\n className={cn(\n \"tap block h-9 min-w-0 px-2.5\",\n width === \"auto\" ? \"w-auto\" : \"w-full\",\n CONTROL_CLASSES,\n className,\n )}\n {...rest}\n >\n {children}\n </select>\n );\n});\n"],"mappings":"A,qKAUaQ,GAAA,EAASR,EAAAA,WAAAA,CAA2C,SAC/D,CAAE,YAAW,QAAQ,OAAQ,WAAU,GAAGS,GAC1C,EACA,CACA,OACE,EAAAF,EAAA,IAAA,CAAC,SAAD,CACO,MACL,UAAWH,EACT,+BACAM,IAAU,OAAS,SAAW,SAC9BL,EACAM,CACF,EACA,GAAIF,EAEH,UACK,CAAA,CAEZ,CAAC,E"}
@@ -0,0 +1,2 @@
1
+ import{_ as e,m as t,t as n}from"./jsx-runtime-D13aM9HY.js";import{_ as r,p as i}from"./states-ZhrxQa3K.js";var a=e(t(),1),o=n();function s({checked:e}){return(0,o.jsx)(`span`,{"aria-hidden":!0,className:r(`relative inline-flex h-6 w-10 shrink-0 items-center rounded-full border transition-colors`,e?`border-accent bg-accent`:`border-border-strong bg-surface-2`),children:(0,o.jsx)(`span`,{className:r(`absolute size-4 rounded-full shadow-sm transition-transform`,e?`translate-x-5 bg-accent-fg`:`translate-x-1 bg-muted`)})})}function c({checked:e,onChange:t,label:n,onText:a=`On`,offText:c=`Off`,busy:l,disabled:u,id:d,className:f,...p}){return(0,o.jsxs)(`button`,{id:d,type:`button`,role:`switch`,"aria-checked":e,"aria-label":n,"aria-busy":l||void 0,"aria-invalid":p[`aria-invalid`],"aria-describedby":p[`aria-describedby`],disabled:u||l,onClick:()=>t(!e),className:r(`tap inline-flex min-h-9 shrink-0 items-center gap-2 rounded-md px-1.5 text-sm`,`hover:bg-surface-2 disabled:opacity-60`,f),children:[(0,o.jsx)(s,{checked:e}),l?(0,o.jsx)(i,{className:`size-3.5`}):null,(0,o.jsx)(`span`,{className:e?`text-text`:`text-muted`,children:e?a:c})]})}function l({checked:e,onChange:t,label:n,description:i,busy:c,disabled:l,id:u,className:d,...f}){let p=(0,a.useId)(),m=u??p,h=`${m}-desc`,g=[i?h:null,f[`aria-describedby`]].filter(Boolean).join(` `),_=l||c;return(0,o.jsxs)(`div`,{className:r(`flex min-w-0 items-start gap-3`,d),children:[(0,o.jsx)(`button`,{id:m,type:`button`,role:`switch`,"aria-checked":e,"aria-busy":c||void 0,"aria-invalid":f[`aria-invalid`],"aria-describedby":g||void 0,disabled:_,onClick:()=>t(!e),className:r(`tap mt-0.5 rounded-full`,_&&`cursor-not-allowed opacity-60`),children:(0,o.jsx)(s,{checked:e})}),(0,o.jsxs)(`label`,{htmlFor:m,className:r(`min-w-0 flex-1`,_?`cursor-not-allowed opacity-60`:`cursor-pointer`),children:[(0,o.jsxs)(`span`,{className:`block text-sm font-medium`,children:[n,` `,(0,o.jsxs)(`span`,{"aria-hidden":!0,className:`font-normal text-muted`,children:[`(`,e?`On`:`Off`,`)`]})]}),i&&(0,o.jsx)(`span`,{id:h,className:`block text-sm text-muted`,children:i})]})]})}export{l as n,c as t};
2
+ //# sourceMappingURL=Switch-DBfzmqkm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Switch-DBfzmqkm.js","names":["useId","__toESM","require_react","require_jsx_runtime","cn","Spinner","import_react","import_jsx_runtime","SwitchTrack","checked","Switch","aria","label","busy","disabled","onChange","className","onText","offText","SwitchRow","generated","switchId","id","descId","describedBy","description","off"],"sources":["../../src/components/ui/Switch.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { useId } from \"react\";\nimport { cn } from \"../../lib/cn\";\nimport { Spinner } from \"./Spinner\";\n\ninterface SwitchBase {\n checked: boolean;\n onChange(next: boolean): void;\n busy?: boolean;\n disabled?: boolean;\n id?: string;\n \"aria-invalid\"?: true;\n \"aria-describedby\"?: string;\n className?: string;\n}\n\nfunction SwitchTrack({ checked }: { checked: boolean }) {\n return (\n <span\n aria-hidden\n className={cn(\n \"relative inline-flex h-6 w-10 shrink-0 items-center rounded-full border transition-colors\",\n checked ? \"border-accent bg-accent\" : \"border-border-strong bg-surface-2\",\n )}\n >\n <span\n className={cn(\n \"absolute size-4 rounded-full shadow-sm transition-transform\",\n checked ? \"translate-x-5 bg-accent-fg\" : \"translate-x-1 bg-muted\",\n )}\n />\n </span>\n );\n}\n\n/** Compact on/off switch: the track plus text that states the current value. */\nexport function Switch({\n checked,\n onChange,\n label,\n onText = \"On\",\n offText = \"Off\",\n busy,\n disabled,\n id,\n className,\n ...aria\n}: SwitchBase & {\n /** Accessible name. */\n label: string;\n onText?: string;\n offText?: string;\n}) {\n return (\n <button\n id={id}\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={label}\n aria-busy={busy || undefined}\n aria-invalid={aria[\"aria-invalid\"]}\n aria-describedby={aria[\"aria-describedby\"]}\n disabled={disabled || busy}\n onClick={() => onChange(!checked)}\n className={cn(\n \"tap inline-flex min-h-9 shrink-0 items-center gap-2 rounded-md px-1.5 text-sm\",\n \"hover:bg-surface-2 disabled:opacity-60\",\n className,\n )}\n >\n <SwitchTrack checked={checked} />\n {busy ? <Spinner className=\"size-3.5\" /> : null}\n <span className={checked ? \"text-text\" : \"text-muted\"}>{checked ? onText : offText}</span>\n </button>\n );\n}\n\n/** Switch row: the track, a visible label with \"(On)/(Off)\" and a description; the whole row is clickable. */\nexport function SwitchRow({\n checked,\n onChange,\n label,\n description,\n busy,\n disabled,\n id,\n className,\n ...aria\n}: SwitchBase & { label: ReactNode; description?: ReactNode }) {\n const generated = useId();\n const switchId = id ?? generated;\n const descId = `${switchId}-desc`;\n const describedBy = [description ? descId : null, aria[\"aria-describedby\"]]\n .filter(Boolean)\n .join(\" \");\n const off = disabled || busy;\n return (\n <div className={cn(\"flex min-w-0 items-start gap-3\", className)}>\n <button\n id={switchId}\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-busy={busy || undefined}\n aria-invalid={aria[\"aria-invalid\"]}\n aria-describedby={describedBy || undefined}\n disabled={off}\n onClick={() => onChange(!checked)}\n className={cn(\"tap mt-0.5 rounded-full\", off && \"cursor-not-allowed opacity-60\")}\n >\n <SwitchTrack checked={checked} />\n </button>\n <label\n htmlFor={switchId}\n className={cn(\"min-w-0 flex-1\", off ? \"cursor-not-allowed opacity-60\" : \"cursor-pointer\")}\n >\n <span className=\"block text-sm font-medium\">\n {label}{\" \"}\n <span aria-hidden className=\"font-normal text-muted\">\n ({checked ? \"On\" : \"Off\"})\n </span>\n </span>\n {description && (\n <span id={descId} className=\"block text-sm text-muted\">\n {description}\n </span>\n )}\n </label>\n </div>\n );\n}\n"],"mappings":"A,iIAgBA,SAASQ,EAAY,CAAE,WAAiC,CACtD,OACE,EAAAD,EAAA,IAAA,CAAC,OAAD,CACE,cAAA,GACA,UAAWH,EACT,4FACAK,EAAU,0BAA4B,mCACxC,EAEA,UAAA,EAAAF,EAAA,IAAA,CAAC,OAAD,CACE,UAAWH,EACT,8DACAK,EAAU,6BAA+B,wBAC3C,CACD,CAAA,CACG,CAAA,CAEV,CAGA,SAAgBC,EAAO,CACrB,UACA,WACA,QACA,SAAS,KACT,UAAU,MACV,OACA,WACA,KACA,YACA,GAAGC,GAMF,CACD,OACE,EAAAJ,EAAA,KAAA,CAAC,SAAD,CACM,KACJ,KAAK,SACL,KAAK,SACL,eAAcE,EACd,aAAYG,EACZ,YAAWC,GAAQ,IAAA,GACnB,eAAcF,EAAK,gBACnB,mBAAkBA,EAAK,oBACvB,SAAUG,GAAYD,EACtB,YAAeE,EAAS,CAACN,CAAO,EAChC,UAAWL,EACT,gFACA,yCACAY,CACF,EAfF,SAAA,EAiBE,EAAAT,EAAA,IAAA,CAACC,EAAD,CAAsB,SAAU,CAAA,EAC/BK,GAAO,EAAAN,EAAA,IAAA,CAACF,EAAD,CAAS,UAAU,UAAY,CAAA,EAAI,MAC3C,EAAAE,EAAA,IAAA,CAAC,OAAD,CAAM,UAAWE,EAAU,YAAc,aAAe,SAAAA,EAAUQ,EAASC,CAAc,CAAA,CACnF,C,EAEZ,CAGA,SAAgBC,EAAU,CACxB,UACA,WACA,QACA,cACA,OACA,WACA,KACA,YACA,GAAGR,GAC0D,CAC7D,IAAMS,GAAA,EAAYpB,EAAAA,MAAAA,CAAM,EAClBqB,EAAWC,GAAMF,EACjBG,EAAS,GAAGF,EAAS,OACrBG,EAAc,CAACC,EAAcF,EAAS,KAAMZ,EAAK,mBAAmB,CAAC,CACxE,OAAO,OAAO,CAAC,CACf,KAAK,GAAG,EACLe,EAAMZ,GAAYD,EACxB,OACE,EAAAN,EAAA,KAAA,CAAC,MAAD,CAAK,UAAWH,EAAG,iCAAkCY,CAAS,EAA9D,SAAA,EACE,EAAAT,EAAA,IAAA,CAAC,SAAD,CACE,GAAIc,EACJ,KAAK,SACL,KAAK,SACL,eAAcZ,EACd,YAAWI,GAAQ,IAAA,GACnB,eAAcF,EAAK,gBACnB,mBAAkBa,GAAe,IAAA,GACjC,SAAUE,EACV,YAAeX,EAAS,CAACN,CAAO,EAChC,UAAWL,EAAG,0BAA2BsB,GAAO,+BAA+B,EAE/E,UAAA,EAAAnB,EAAA,IAAA,CAACC,EAAD,CAAsB,SAAU,CAAA,CAC1B,CAAA,GACR,EAAAD,EAAA,KAAA,CAAC,QAAD,CACE,QAASc,EACT,UAAWjB,EAAG,iBAAkBsB,EAAM,gCAAkC,gBAAgB,EAF1F,SAAA,EAIE,EAAAnB,EAAA,KAAA,CAAC,OAAD,CAAM,UAAU,4BAAhB,SAAA,CACGK,EAAO,KACR,EAAAL,EAAA,KAAA,CAAC,OAAD,CAAM,cAAA,GAAY,UAAU,yBAA5B,SAAA,CAAqD,IACjDE,EAAU,KAAO,MAAM,GACrB,C,EACF,CACL,CAAA,EAAAgB,IACC,EAAAlB,EAAA,IAAA,CAAC,OAAD,CAAM,GAAIgB,EAAQ,UAAU,2BACzB,SAAAE,CACG,CAAA,CAEH,CACJ,CAAA,CAAA,C,EAET,C"}