@tangle-network/agent-app 0.45.48 → 0.45.50

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 (78) hide show
  1. package/dist/{DesignCanvas-QY2CA5GI.js → DesignCanvas-JRBCAWHW.js} +2 -2
  2. package/dist/{DesignCanvasEditor-CJ7PLX7A.js → DesignCanvasEditor-J2OLS22G.js} +4 -4
  3. package/dist/{IntakeInterview-NJXKH35Q.js → IntakeInterview-OKT6DCBN.js} +2 -2
  4. package/dist/{InvitationsPanel-F2VY4TSL.js → InvitationsPanel-OFCYVSAE.js} +2 -2
  5. package/dist/InviteAcceptPage-BDOO4JSE.js +7 -0
  6. package/dist/{MembersPanel-N5CBOUXC.js → MembersPanel-3ZK2GCMC.js} +2 -2
  7. package/dist/{TimelineEditor-IYNKBSUX.js → TimelineEditor-52YEPDX5.js} +2 -2
  8. package/dist/VaultPane-IGJKB6CX.js +7 -0
  9. package/dist/assistant/index.d.ts +18 -2
  10. package/dist/assistant/index.js +45 -14
  11. package/dist/assistant/index.js.map +1 -1
  12. package/dist/{billing-BibxgALe.d.ts → billing-BuNlaKlE.d.ts} +15 -23
  13. package/dist/chat-react/index.js +1 -1
  14. package/dist/{chunk-DS2WE62K.js → chunk-33BN7P57.js} +5 -5
  15. package/dist/chunk-33BN7P57.js.map +1 -0
  16. package/dist/{chunk-5BACNOVR.js → chunk-5MI2SKQH.js} +2 -2
  17. package/dist/chunk-5MI2SKQH.js.map +1 -0
  18. package/dist/{chunk-C7AQW6US.js → chunk-7HY4LX7O.js} +820 -208
  19. package/dist/chunk-7HY4LX7O.js.map +1 -0
  20. package/dist/{chunk-T7XPIBCX.js → chunk-7LJYCHXK.js} +16 -16
  21. package/dist/chunk-7LJYCHXK.js.map +1 -0
  22. package/dist/{chunk-XVRHISF3.js → chunk-CJHIAZKK.js} +5 -5
  23. package/dist/chunk-CJHIAZKK.js.map +1 -0
  24. package/dist/{chunk-J6OJTFNE.js → chunk-ENLRJYVW.js} +13 -13
  25. package/dist/chunk-ENLRJYVW.js.map +1 -0
  26. package/dist/{chunk-R6KGLGEJ.js → chunk-GRDLGQ6A.js} +2 -2
  27. package/dist/{chunk-R6KGLGEJ.js.map → chunk-GRDLGQ6A.js.map} +1 -1
  28. package/dist/{chunk-XPFNEKHS.js → chunk-I2CA5N7V.js} +2 -2
  29. package/dist/chunk-I2CA5N7V.js.map +1 -0
  30. package/dist/{chunk-IN6LNZ7O.js → chunk-Q2QKEV6J.js} +7 -7
  31. package/dist/{chunk-IN6LNZ7O.js.map → chunk-Q2QKEV6J.js.map} +1 -1
  32. package/dist/{chunk-RILGAFEL.js → chunk-UMV45ZDF.js} +3 -3
  33. package/dist/{chunk-JJJEO4ND.js → chunk-VWTODXRW.js} +2 -2
  34. package/dist/{chunk-JJJEO4ND.js.map → chunk-VWTODXRW.js.map} +1 -1
  35. package/dist/{chunk-DB2D2JBQ.js → chunk-W7P5KV6O.js} +2 -2
  36. package/dist/{chunk-DB2D2JBQ.js.map → chunk-W7P5KV6O.js.map} +1 -1
  37. package/dist/{chunk-GT4GTRGQ.js → chunk-ZGY6QB4F.js} +5 -5
  38. package/dist/{chunk-GT4GTRGQ.js.map → chunk-ZGY6QB4F.js.map} +1 -1
  39. package/dist/design-canvas-react/index.js +7 -7
  40. package/dist/design-canvas-react/index.js.map +1 -1
  41. package/dist/design-canvas-react/lazy.js +1 -1
  42. package/dist/intakes-react/index.js +1 -1
  43. package/dist/intakes-react/lazy.js +1 -1
  44. package/dist/platform/index.d.ts +1 -1
  45. package/dist/platform/index.js +16 -16
  46. package/dist/platform/index.js.map +1 -1
  47. package/dist/sequences-react/index.js +2 -2
  48. package/dist/studio-react/index.js +23 -23
  49. package/dist/studio-react/index.js.map +1 -1
  50. package/dist/teams-react/index.js +3 -3
  51. package/dist/teams-react/lazy.js +3 -3
  52. package/dist/theme/index.js +1 -1
  53. package/dist/theme/tokens.css +15 -13
  54. package/dist/vault/index.js +1 -1
  55. package/dist/vault/lazy.js +1 -1
  56. package/dist/web-react/index.d.ts +359 -5
  57. package/dist/web-react/index.js +44 -3
  58. package/dist/work-product-react/index.js +7 -7
  59. package/dist/work-product-react/index.js.map +1 -1
  60. package/package.json +1 -1
  61. package/dist/InviteAcceptPage-UT4452KT.js +0 -7
  62. package/dist/VaultPane-MUWGYVJ7.js +0 -7
  63. package/dist/chunk-5BACNOVR.js.map +0 -1
  64. package/dist/chunk-C7AQW6US.js.map +0 -1
  65. package/dist/chunk-DS2WE62K.js.map +0 -1
  66. package/dist/chunk-J6OJTFNE.js.map +0 -1
  67. package/dist/chunk-T7XPIBCX.js.map +0 -1
  68. package/dist/chunk-XPFNEKHS.js.map +0 -1
  69. package/dist/chunk-XVRHISF3.js.map +0 -1
  70. /package/dist/{DesignCanvas-QY2CA5GI.js.map → DesignCanvas-JRBCAWHW.js.map} +0 -0
  71. /package/dist/{DesignCanvasEditor-CJ7PLX7A.js.map → DesignCanvasEditor-J2OLS22G.js.map} +0 -0
  72. /package/dist/{IntakeInterview-NJXKH35Q.js.map → IntakeInterview-OKT6DCBN.js.map} +0 -0
  73. /package/dist/{InvitationsPanel-F2VY4TSL.js.map → InvitationsPanel-OFCYVSAE.js.map} +0 -0
  74. /package/dist/{InviteAcceptPage-UT4452KT.js.map → InviteAcceptPage-BDOO4JSE.js.map} +0 -0
  75. /package/dist/{MembersPanel-N5CBOUXC.js.map → MembersPanel-3ZK2GCMC.js.map} +0 -0
  76. /package/dist/{TimelineEditor-IYNKBSUX.js.map → TimelineEditor-52YEPDX5.js.map} +0 -0
  77. /package/dist/{VaultPane-MUWGYVJ7.js.map → VaultPane-IGJKB6CX.js.map} +0 -0
  78. /package/dist/{chunk-RILGAFEL.js.map → chunk-UMV45ZDF.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/teams-react/components/MembersPanel.tsx"],"sourcesContent":["/**\n * Workspace members panel: list members, invite by email + role, change a\n * member's role, remove a member. Fully callback-driven — the host supplies the\n * data and the async `onInvite`/`onChangeRole`/`onRemove` callbacks (backed by\n * `./teams/members-api`), so this imports no app router, fetch client, or toast.\n * Styled with the shipped Tangle Quiet tokens (`var(--*)`).\n *\n * Role gating mirrors the API: only admins/owners see role selects and the\n * remove control; inherited org owners and explicit owners are not editable\n * here (org-level concern). The invite role select offers `admin` only to\n * admins/owners.\n */\n\nimport { useState } from 'react'\nimport type { WorkspaceRole } from '../../teams/roles'\nimport { hasWorkspaceRole } from '../../teams/roles'\nimport type { MemberView, MembersPanelProps } from '../contracts'\nimport { RoleSelect } from './RoleSelect'\n\nexport function MembersPanel({\n members,\n currentRole,\n onInvite,\n onChangeRole,\n onRemove,\n onNotice,\n showInviteForm = true,\n}: MembersPanelProps) {\n const [inviteEmail, setInviteEmail] = useState('')\n const [inviteRole, setInviteRole] = useState<WorkspaceRole>('editor')\n const [inviting, setInviting] = useState(false)\n\n const canManage = hasWorkspaceRole(currentRole, 'admin')\n\n function notify(kind: 'success' | 'error', message: string) {\n onNotice?.({ kind, message })\n }\n\n async function submitInvite() {\n const email = inviteEmail.trim()\n if (!email || inviting) return\n setInviting(true)\n try {\n const result = await onInvite({ email, role: inviteRole })\n if (result && 'inviteUrl' in result && result.inviteUrl) {\n notify('success', `Invite link ready for ${email}`)\n } else {\n notify('success', `Invited ${email}`)\n }\n setInviteEmail('')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to invite')\n } finally {\n setInviting(false)\n }\n }\n\n async function changeRole(memberId: string, role: WorkspaceRole) {\n try {\n await onChangeRole({ memberId, role })\n notify('success', 'Role updated')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to update role')\n }\n }\n\n async function remove(memberId: string) {\n try {\n await onRemove({ memberId })\n notify('success', 'Member removed')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to remove member')\n }\n }\n\n return (\n <section className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <h4 className=\"text-xs font-semibold text-[var(--text-primary)]\">Members</h4>\n <span className=\"text-xs text-[var(--text-muted)]\">{members.length} total</span>\n </div>\n <div className=\"flex flex-col gap-2\">\n {members.map((member) => (\n <MemberRow\n key={member.id}\n member={member}\n canManage={canManage}\n onChangeRole={changeRole}\n onRemove={remove}\n />\n ))}\n {members.length === 0 && (\n <p className=\"text-sm text-[var(--text-muted)]\">No team members yet.</p>\n )}\n </div>\n\n {canManage && showInviteForm && (\n <div className=\"flex flex-col gap-1.5 border-t border-[var(--border-default)] pt-3\">\n <label className=\"text-xs font-medium text-[var(--text-muted)]\">Invite member</label>\n <div className=\"flex gap-2\">\n <input\n type=\"email\"\n placeholder=\"Email address\"\n value={inviteEmail}\n aria-label=\"Invite email address\"\n onChange={(event) => setInviteEmail(event.target.value)}\n onKeyDown={(event) => { if (event.key === 'Enter') void submitInvite() }}\n className=\"flex-1 rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-3 py-1.5 text-sm text-[var(--text-primary)] placeholder:text-[var(--text-muted)]\"\n />\n <RoleSelect\n value={inviteRole}\n ariaLabel=\"Invite role\"\n onChange={setInviteRole}\n />\n <button\n type=\"button\"\n onClick={() => void submitInvite()}\n disabled={inviting || !inviteEmail.trim()}\n className=\"rounded bg-[var(--brand-primary)] px-3 py-1.5 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50\"\n >\n {inviting ? 'Inviting…' : 'Invite'}\n </button>\n </div>\n </div>\n )}\n </section>\n )\n}\n\ninterface MemberRowProps {\n member: MemberView\n canManage: boolean\n onChangeRole(memberId: string, role: WorkspaceRole): void\n onRemove(memberId: string): void\n}\n\nfunction MemberRow({ member, canManage, onChangeRole, onRemove }: MemberRowProps) {\n const pending = member.acceptedAt == null\n const label = member.name ?? member.email ?? 'Unknown'\n const initial = (member.name?.[0] ?? member.email?.[0] ?? '?').toUpperCase()\n const isOwner = member.role === 'owner'\n const editable = canManage && !isOwner && !member.inherited\n\n return (\n <div className=\"flex items-center justify-between border-b border-[var(--border-default)] py-2 last:border-0\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-8 w-8 items-center justify-center rounded-full bg-[var(--bg-input)] text-xs font-semibold text-[var(--text-secondary)]\">\n {initial}\n </div>\n <div>\n <p className=\"flex items-center gap-2 text-sm font-medium text-[var(--text-primary)]\">\n {label}\n {pending && (\n <span className=\"rounded border border-[var(--border-default)] px-1.5 py-0.5 text-[11px] font-semibold uppercase tracking-[0.05em] text-[var(--text-muted)]\">\n Pending\n </span>\n )}\n </p>\n {member.name && member.email && (\n <p className=\"text-xs text-[var(--text-muted)]\">{member.email}</p>\n )}\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {isOwner ? (\n <span className=\"rounded border border-[var(--border-default)] px-2 py-0.5 text-xs text-[var(--text-secondary)]\">\n {member.inherited ? 'Org Admin' : 'Owner'}\n </span>\n ) : editable ? (\n <>\n <RoleSelect\n value={member.role}\n ariaLabel={`Role for ${label}`}\n onChange={(role) => onChangeRole(member.id, role)}\n />\n <button\n type=\"button\"\n aria-label={`Remove ${label}`}\n onClick={() => onRemove(member.id)}\n className=\"rounded px-2 py-1 text-xs text-[var(--text-danger)] hover:bg-[var(--border-default)]\"\n >\n Remove\n </button>\n </>\n ) : (\n <span className=\"rounded border border-[var(--border-default)] px-2 py-0.5 text-xs capitalize text-[var(--text-secondary)]\">\n {member.role}\n </span>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/teams-react/components/MembersPanel.tsx"],"sourcesContent":["/**\n * Workspace members panel: list members, invite by email + role, change a\n * member's role, remove a member. Fully callback-driven — the host supplies the\n * data and the async `onInvite`/`onChangeRole`/`onRemove` callbacks (backed by\n * `./teams/members-api`), so this imports no app router, fetch client, or toast.\n * Styled with the shipped Tangle Quiet tokens (`var(--*)`).\n *\n * Role gating mirrors the API: only admins/owners see role selects and the\n * remove control; inherited org owners and explicit owners are not editable\n * here (org-level concern). The invite role select offers `admin` only to\n * admins/owners.\n */\n\nimport { useState } from 'react'\nimport type { WorkspaceRole } from '../../teams/roles'\nimport { hasWorkspaceRole } from '../../teams/roles'\nimport type { MemberView, MembersPanelProps } from '../contracts'\nimport { RoleSelect } from './RoleSelect'\n\nexport function MembersPanel({\n members,\n currentRole,\n onInvite,\n onChangeRole,\n onRemove,\n onNotice,\n showInviteForm = true,\n}: MembersPanelProps) {\n const [inviteEmail, setInviteEmail] = useState('')\n const [inviteRole, setInviteRole] = useState<WorkspaceRole>('editor')\n const [inviting, setInviting] = useState(false)\n\n const canManage = hasWorkspaceRole(currentRole, 'admin')\n\n function notify(kind: 'success' | 'error', message: string) {\n onNotice?.({ kind, message })\n }\n\n async function submitInvite() {\n const email = inviteEmail.trim()\n if (!email || inviting) return\n setInviting(true)\n try {\n const result = await onInvite({ email, role: inviteRole })\n if (result && 'inviteUrl' in result && result.inviteUrl) {\n notify('success', `Invite link ready for ${email}`)\n } else {\n notify('success', `Invited ${email}`)\n }\n setInviteEmail('')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to invite')\n } finally {\n setInviting(false)\n }\n }\n\n async function changeRole(memberId: string, role: WorkspaceRole) {\n try {\n await onChangeRole({ memberId, role })\n notify('success', 'Role updated')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to update role')\n }\n }\n\n async function remove(memberId: string) {\n try {\n await onRemove({ memberId })\n notify('success', 'Member removed')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to remove member')\n }\n }\n\n return (\n <section className=\"flex flex-col gap-4\">\n <div className=\"flex items-center justify-between\">\n <h4 className=\"text-xs font-semibold text-[var(--text-primary)]\">Members</h4>\n <span className=\"text-xs text-[var(--text-muted)]\">{members.length} total</span>\n </div>\n <div className=\"flex flex-col gap-2\">\n {members.map((member) => (\n <MemberRow\n key={member.id}\n member={member}\n canManage={canManage}\n onChangeRole={changeRole}\n onRemove={remove}\n />\n ))}\n {members.length === 0 && (\n <p className=\"text-sm text-[var(--text-muted)]\">No team members yet.</p>\n )}\n </div>\n\n {canManage && showInviteForm && (\n <div className=\"flex flex-col gap-1.5 border-t border-[var(--border-default)] pt-3\">\n <label className=\"text-xs font-medium text-[var(--text-muted)]\">Invite member</label>\n <div className=\"flex gap-2\">\n <input\n type=\"email\"\n placeholder=\"Email address\"\n value={inviteEmail}\n aria-label=\"Invite email address\"\n onChange={(event) => setInviteEmail(event.target.value)}\n onKeyDown={(event) => { if (event.key === 'Enter') void submitInvite() }}\n className=\"flex-1 rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-3 py-1.5 text-sm text-[var(--text-primary)] placeholder:text-[var(--text-muted)]\"\n />\n <RoleSelect\n value={inviteRole}\n ariaLabel=\"Invite role\"\n onChange={setInviteRole}\n />\n <button\n type=\"button\"\n onClick={() => void submitInvite()}\n disabled={inviting || !inviteEmail.trim()}\n className=\"rounded bg-[var(--brand-primary)] px-3 py-1.5 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50\"\n >\n {inviting ? 'Inviting…' : 'Invite'}\n </button>\n </div>\n </div>\n )}\n </section>\n )\n}\n\ninterface MemberRowProps {\n member: MemberView\n canManage: boolean\n onChangeRole(memberId: string, role: WorkspaceRole): void\n onRemove(memberId: string): void\n}\n\nfunction MemberRow({ member, canManage, onChangeRole, onRemove }: MemberRowProps) {\n const pending = member.acceptedAt == null\n const label = member.name ?? member.email ?? 'Unknown'\n const initial = (member.name?.[0] ?? member.email?.[0] ?? '?').toUpperCase()\n const isOwner = member.role === 'owner'\n const editable = canManage && !isOwner && !member.inherited\n\n return (\n <div className=\"flex items-center justify-between border-b border-[var(--border-default)] py-2 last:border-0\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-8 w-8 items-center justify-center rounded-full bg-[var(--bg-input)] text-xs font-semibold text-[var(--text-secondary)]\">\n {initial}\n </div>\n <div>\n <p className=\"flex items-center gap-2 text-sm font-medium text-[var(--text-primary)]\">\n {label}\n {pending && (\n <span className=\"rounded border border-[var(--border-default)] px-1.5 py-0.5 text-xs font-semibold uppercase tracking-[0.05em] text-[var(--text-muted)]\">\n Pending\n </span>\n )}\n </p>\n {member.name && member.email && (\n <p className=\"text-xs text-[var(--text-muted)]\">{member.email}</p>\n )}\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {isOwner ? (\n <span className=\"rounded border border-[var(--border-default)] px-2 py-0.5 text-xs text-[var(--text-secondary)]\">\n {member.inherited ? 'Org Admin' : 'Owner'}\n </span>\n ) : editable ? (\n <>\n <RoleSelect\n value={member.role}\n ariaLabel={`Role for ${label}`}\n onChange={(role) => onChangeRole(member.id, role)}\n />\n <button\n type=\"button\"\n aria-label={`Remove ${label}`}\n onClick={() => onRemove(member.id)}\n className=\"rounded px-2 py-1 text-xs text-[var(--text-danger)] hover:bg-[var(--border-default)]\"\n >\n Remove\n </button>\n </>\n ) : (\n <span className=\"rounded border border-[var(--border-default)] px-2 py-0.5 text-xs capitalize text-[var(--text-secondary)]\">\n {member.role}\n </span>\n )}\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":[]}
@@ -85,7 +85,7 @@ function IntakeInterview({
85
85
  /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-1", children: [
86
86
  /* @__PURE__ */ jsxs("span", { className: "mb-1 flex items-center gap-2 text-[var(--text-muted)]", children: [
87
87
  /* @__PURE__ */ jsx2(BrandMark, { size: 20, className: "shrink-0" }),
88
- /* @__PURE__ */ jsx2("span", { className: "text-[11px] font-semibold uppercase tracking-[0.05em]", children: "Tangle Intake" })
88
+ /* @__PURE__ */ jsx2("span", { className: "text-xs font-semibold uppercase tracking-[0.05em]", children: "Tangle Intake" })
89
89
  ] }),
90
90
  /* @__PURE__ */ jsx2("h2", { className: "text-xl font-semibold text-[var(--text-primary)]", children: view.title }),
91
91
  view.description && /* @__PURE__ */ jsx2("p", { className: "text-sm text-[var(--text-muted)]", children: view.description }),
@@ -251,4 +251,4 @@ function normalize(question, value) {
251
251
  export {
252
252
  IntakeInterview
253
253
  };
254
- //# sourceMappingURL=chunk-XPFNEKHS.js.map
254
+ //# sourceMappingURL=chunk-I2CA5N7V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/intakes-react/components/IntakeInterview.tsx","../src/intakes-react/components/BrandMark.tsx"],"sourcesContent":["/**\n * Agent-led intake interview: asks one question at a time, advancing as each\n * answer is saved. Fully callback-driven — the host supplies the loaded `view`\n * and the async `onAnswer` / `onComplete` callbacks (backed by `./intakes/api`),\n * so this imports no app router, fetch client, or toast. Styled with the\n * shipped Tangle Quiet tokens (`var(--*)`).\n *\n * The server owns traversal: each `onAnswer` resolves with the next view (next\n * question + progress), so the component never re-derives the graph — it renders\n * `view.nextQuestion`, and when that is null and the view is completable it\n * shows the finish action. Local validation only gates the submit button; the\n * authoritative validation runs in the store.\n */\n\nimport { useEffect, useState } from 'react'\nimport type { IntakeAnswerValue, IntakeQuestion } from '../../intakes/model'\nimport { validateAnswer } from '../../intakes/model'\nimport type { IntakeInterviewProps, IntakeView } from '../contracts'\nimport { BrandMark } from './BrandMark'\n\nexport function IntakeInterview({\n view: initialView,\n onAnswer,\n onComplete,\n onDone,\n onNotice,\n}: IntakeInterviewProps) {\n const [view, setView] = useState<IntakeView>(initialView)\n const [draft, setDraft] = useState<IntakeAnswerValue>(currentAnswer(initialView))\n const [busy, setBusy] = useState(false)\n const [doneFired, setDoneFired] = useState(false)\n\n useEffect(() => {\n setView(initialView)\n setDraft(currentAnswer(initialView))\n }, [initialView])\n\n useEffect(() => {\n if (view.completed && !doneFired) {\n setDoneFired(true)\n onDone?.()\n }\n }, [view.completed, doneFired, onDone])\n\n function notify(kind: 'success' | 'error', message: string) {\n onNotice?.({ kind, message })\n }\n\n const question = view.nextQuestion\n\n async function submit() {\n if (!question || busy) return\n const validity = validateAnswer(question, draft)\n if (!validity.ok) {\n notify('error', `Please answer: ${validity.reason}`)\n return\n }\n setBusy(true)\n try {\n const next = await onAnswer({ questionId: question.id, value: normalize(question, draft) })\n setView(next)\n setDraft(currentAnswer(next))\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to save answer')\n } finally {\n setBusy(false)\n }\n }\n\n async function finish() {\n if (busy) return\n setBusy(true)\n try {\n const next = await onComplete()\n setView(next)\n notify('success', 'All set.')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to finish')\n } finally {\n setBusy(false)\n }\n }\n\n return (\n <section className=\"flex flex-col gap-5\">\n <header className=\"flex flex-col gap-1\">\n <span className=\"mb-1 flex items-center gap-2 text-[var(--text-muted)]\">\n <BrandMark size={20} className=\"shrink-0\" />\n <span className=\"text-xs font-semibold uppercase tracking-[0.05em]\">Tangle Intake</span>\n </span>\n <h2 className=\"text-xl font-semibold text-[var(--text-primary)]\">{view.title}</h2>\n {view.description && <p className=\"text-sm text-[var(--text-muted)]\">{view.description}</p>}\n <ProgressBar answered={view.progress.answered} total={view.progress.total} />\n </header>\n\n {view.completed ? (\n <div className=\"flex items-center gap-2.5 rounded-lg border border-[hsl(var(--success)/0.4)] bg-[hsl(var(--success)/0.08)] px-3.5 py-3\">\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4 shrink-0 text-[var(--surface-success-text)]\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n <p className=\"text-sm text-[var(--text-primary)]\">You're all set — every answer is saved. You can close this panel.</p>\n </div>\n ) : question ? (\n <div className=\"flex flex-col gap-3\">\n <div className=\"flex flex-col gap-1\">\n <p className=\"text-sm font-medium text-[var(--text-primary)]\">{question.prompt}</p>\n {question.help && <p className=\"text-xs text-[var(--text-muted)]\">{question.help}</p>}\n </div>\n\n <AnswerField question={question} value={draft} onChange={setDraft} onSubmit={() => void submit()} />\n\n <div className=\"flex justify-end\">\n <button\n type=\"button\"\n onClick={() => void submit()}\n disabled={busy || isDraftEmpty(draft)}\n className=\"rounded bg-[var(--brand-primary)] px-4 py-1.5 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50\"\n >\n {busy ? 'Saving…' : 'Continue'}\n </button>\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col gap-3\">\n <p className=\"text-sm text-[var(--text-secondary)]\">That's everything. Ready to finish?</p>\n <div className=\"flex justify-end\">\n <button\n type=\"button\"\n onClick={() => void finish()}\n disabled={busy}\n className=\"rounded bg-[var(--brand-primary)] px-4 py-1.5 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50\"\n >\n {busy ? 'Finishing…' : 'Finish'}\n </button>\n </div>\n </div>\n )}\n </section>\n )\n}\n\ninterface AnswerFieldProps {\n question: IntakeQuestion\n value: IntakeAnswerValue\n onChange(value: IntakeAnswerValue): void\n onSubmit(): void\n}\n\nfunction AnswerField({ question, value, onChange, onSubmit }: AnswerFieldProps) {\n const inputClass =\n 'rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-3 py-2 text-sm text-[var(--text-primary)] placeholder:text-[var(--text-muted)]'\n\n switch (question.type) {\n case 'long-text':\n return (\n <textarea\n rows={4}\n aria-label={question.prompt}\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => onChange(event.target.value)}\n className={inputClass}\n />\n )\n case 'boolean':\n return (\n <div className=\"flex gap-2\">\n {[{ v: true, l: 'Yes' }, { v: false, l: 'No' }].map((opt) => (\n <button\n key={opt.l}\n type=\"button\"\n onClick={() => onChange(opt.v)}\n className={`rounded border px-4 py-1.5 text-sm ${\n value === opt.v\n ? 'border-[var(--brand-primary)] bg-[var(--brand-primary)] text-[hsl(var(--primary-foreground))]'\n : 'border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]'\n }`}\n >\n {opt.l}\n </button>\n ))}\n </div>\n )\n case 'number':\n return (\n <input\n type=\"number\"\n aria-label={question.prompt}\n value={typeof value === 'number' ? value : ''}\n onChange={(event) => onChange(event.target.value === '' ? null : Number(event.target.value))}\n onKeyDown={(event) => { if (event.key === 'Enter') onSubmit() }}\n className={inputClass}\n />\n )\n case 'single-select':\n return (\n <div className=\"flex flex-col gap-1.5\">\n {(question.options ?? []).map((option) => (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => onChange(option.value)}\n className={`flex items-center gap-2 rounded border px-3 py-2 text-left text-sm ${\n value === option.value\n ? 'border-[var(--brand-primary)] bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)] text-[var(--text-primary)]'\n : 'border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]'\n }`}\n >\n {value === option.value && <ChoiceCheck />}\n {option.label}\n </button>\n ))}\n </div>\n )\n case 'multi-select': {\n const selected = Array.isArray(value) ? value : []\n return (\n <div className=\"flex flex-col gap-1.5\">\n {(question.options ?? []).map((option) => {\n const on = selected.includes(option.value)\n return (\n <button\n key={option.value}\n type=\"button\"\n onClick={() =>\n onChange(on ? selected.filter((v) => v !== option.value) : [...selected, option.value])\n }\n className={`flex items-center gap-2 rounded border px-3 py-2 text-left text-sm ${\n on\n ? 'border-[var(--brand-primary)] bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)] text-[var(--text-primary)]'\n : 'border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]'\n }`}\n >\n {on && <ChoiceCheck />}\n {option.label}\n </button>\n )\n })}\n </div>\n )\n }\n default:\n return (\n <input\n type={question.type === 'email' ? 'email' : question.type === 'url' ? 'url' : 'text'}\n aria-label={question.prompt}\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => onChange(event.target.value)}\n onKeyDown={(event) => { if (event.key === 'Enter') onSubmit() }}\n className={inputClass}\n />\n )\n }\n}\n\n/** Leading check for a selected choice option (single/multi-select). */\nfunction ChoiceCheck() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-3.5 w-3.5 shrink-0 text-[var(--brand-primary)]\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n )\n}\n\nfunction ProgressBar({ answered, total }: { answered: number; total: number }) {\n const pct = total > 0 ? Math.round((answered / total) * 100) : 0\n return (\n <div className=\"flex items-center gap-2\">\n <div className=\"h-1.5 flex-1 overflow-hidden rounded-full bg-[var(--bg-input)]\">\n <div className=\"h-full rounded-full bg-[var(--brand-primary)] transition-all\" style={{ width: `${pct}%` }} />\n </div>\n <span className=\"text-xs text-[var(--text-muted)]\">{answered}/{total}</span>\n </div>\n )\n}\n\nfunction currentAnswer(view: IntakeView): IntakeAnswerValue {\n const id = view.nextQuestion?.id\n if (!id) return null\n return view.answers[id] ?? defaultDraft(view.nextQuestion!)\n}\n\nfunction defaultDraft(question: IntakeQuestion): IntakeAnswerValue {\n return question.type === 'multi-select' ? [] : null\n}\n\n/** No answer yet — null, empty text, or an empty multi-select. `false` and `0`\n * are real answers and never gate Continue. */\nfunction isDraftEmpty(value: IntakeAnswerValue): boolean {\n if (value === null || value === undefined) return true\n if (typeof value === 'string') return value.trim() === ''\n if (Array.isArray(value)) return value.length === 0\n return false\n}\n\n/** Trim text answers before submit; pass others through unchanged. */\nfunction normalize(question: IntakeQuestion, value: IntakeAnswerValue): IntakeAnswerValue {\n if (typeof value === 'string' && (question.type === 'text' || question.type === 'long-text' || question.type === 'url' || question.type === 'email')) {\n return value.trim()\n }\n return value\n}\n","/**\n * Load the canonical Tangle knot only when an intakes surface renders it.\n * `/brand` imports the optional `@tangle-network/brand` peer, so a failed load\n * renders a fixed-size spacer instead of crashing the panel.\n */\n\nimport { lazy, Suspense } from 'react'\nimport type { ComponentType } from 'react'\n\nexport interface BrandMarkProps {\n size: number\n className?: string\n}\n\n/** Preserve the mark's footprint while its optional package loads or is absent. */\nfunction MarkSpacer({ size }: { size: number }) {\n return <span aria-hidden style={{ display: 'inline-block', width: size, height: size }} />\n}\n\nconst LazyKnot = lazy(async () => {\n try {\n const mod = await import('../../brand')\n return { default: mod.TangleKnot as ComponentType<BrandMarkProps> }\n } catch {\n return { default: MarkSpacer as ComponentType<BrandMarkProps> }\n }\n})\n\nexport function BrandMark({ size, className }: BrandMarkProps) {\n return (\n <Suspense fallback={<MarkSpacer size={size} />}>\n <LazyKnot size={size} className={className} />\n </Suspense>\n )\n}\n"],"mappings":"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","names":["jsx"]}
@@ -44,21 +44,21 @@ var lightTheme = {
44
44
  var darkTheme = {
45
45
  background: "234.5 21.2% 9%",
46
46
  foreground: "233.2 14.8% 93.4%",
47
- card: "234.1 15.5% 13.1%",
47
+ card: "233.8 12.9% 15.4%",
48
48
  cardForeground: "233.2 14.8% 93.4%",
49
- popover: "233.6 9.1% 21.7%",
49
+ popover: "233.6 9.3% 21.2%",
50
50
  popoverForeground: "233.2 14.8% 93.4%",
51
51
  primary: "239 84% 74%",
52
52
  // Inverted, not copied from light: white on this fill measures 3.02:1.
53
53
  primaryForeground: "234.9 28.5% 6.4%",
54
- secondary: "233.8 11.4% 17.8%",
54
+ secondary: "233.7 10.7% 19.3%",
55
55
  secondaryForeground: "233.2 14.8% 93.4%",
56
- muted: "233.8 11.4% 17.8%",
56
+ muted: "233.7 10.7% 19.3%",
57
57
  mutedForeground: "239.6 1.5% 62.5%",
58
- accent: "233.8 11.4% 17.8%",
58
+ accent: "233.7 10.7% 19.3%",
59
59
  accentForeground: "233.2 14.8% 93.4%",
60
60
  destructive: "348 90% 68%",
61
- destructiveForeground: "234.1 15.5% 13.1%",
61
+ destructiveForeground: "234.2 17.6% 11.3%",
62
62
  border: "233.4 7% 25.6%",
63
63
  input: "234.2 17.6% 11.3%",
64
64
  ring: "239 84% 74%",
@@ -141,4 +141,4 @@ export {
141
141
  themeColor,
142
142
  themeToCssVars
143
143
  };
144
- //# sourceMappingURL=chunk-IN6LNZ7O.js.map
144
+ //# sourceMappingURL=chunk-Q2QKEV6J.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/theme/theme.ts"],"sourcesContent":["/**\n * Typed mirror of tokens.css for runtime/JS theming. The canonical source is\n * tokens.css (`import '@tangle-network/agent-app/styles'`); this module is for\n * apps that compute theme variables in JS, or read color values where CSS\n * custom properties cannot reach — notably Konva canvas render code, which\n * paints to a bitmap and cannot resolve `var(--…)`.\n *\n * Values are shadcn-style HSL channel triples (\"H S% L%\"); wrap with `color()`.\n *\n * The neutral triples are the RESOLVED form of tokens.css's `--neutral-*` ramp\n * (that file states them as `var(--neutral-NN-hsl)`; JS has no cascade to\n * resolve through, so they are inlined here). `tests/theme/tokens-contract.test.ts`\n * resolves the CSS one level and fails if this mirror has drifted.\n */\n\nexport interface AgentAppTheme {\n background: string\n foreground: string\n card: string\n cardForeground: string\n popover: string\n popoverForeground: string\n primary: string\n primaryForeground: string\n secondary: string\n secondaryForeground: string\n muted: string\n mutedForeground: string\n accent: string\n accentForeground: string\n destructive: string\n destructiveForeground: string\n border: string\n input: string\n ring: string\n success: string\n successForeground: string\n warning: string\n warningForeground: string\n /**\n * Warning as TEXT on a warning-TINTED surface, as a channel triple. Optional\n * so a consumer's existing `AgentAppTheme` literal still type-checks;\n * {@link themeToCssVars} falls back to `warningForeground`, which is what the\n * surface used before this token existed.\n */\n warningStrong?: string\n /** Full CSS color (not a triple) — the canvas/scene backdrop. */\n canvasBackdrop: string\n /**\n * Border tiers as FULL CSS colors (they are `color-mix()` results, not\n * triples). Optional so a consumer's existing `AgentAppTheme` literal still\n * type-checks; {@link themeToCssVars} falls back to full-strength\n * `hsl(var(--border))`, which can never erase an edge.\n */\n borderSoft?: string\n /** @see borderSoft */\n cardEdge?: string\n /** Konva render palette — full hex colors the bitmap canvas paints with\n * (it cannot resolve `var(--…)`). NOT emitted by themeToCssVars. */\n canvasRender: CanvasRenderPalette\n}\n\n/**\n * Colors the Konva design-canvas paints directly. Konva renders to a bitmap\n * and cannot read CSS custom properties, so these are full hex strings sourced\n * from the active theme and threaded through the canvas components.\n */\nexport interface CanvasRenderPalette {\n /** Grid line color (GridLayer). */\n grid: string\n /** Grid-snap guide line (SnapGuidesOverlay, kind 'grid'). */\n snapGrid: string\n /** Saved ruler-guide snap line (kind 'guide'). */\n snapGuide: string\n /** Page edge/center snap line (kinds 'page-edge'/'page-center'). */\n snapPage: string\n /** Element edge/center snap line (kinds 'element-edge'/'element-center'). */\n snapElement: string\n /** Transformer border + anchor stroke (SelectionLayer). */\n selectionStroke: string\n /** Transformer anchor fill (SelectionLayer). */\n selectionAnchorFill: string\n /** Video placeholder fill (ElementNode VideoNode). */\n placeholderFill: string\n /** Video placeholder stroke (ElementNode VideoNode). */\n placeholderStroke: string\n /** Broken/loading image placeholder fill (ElementNode ImageNode). */\n brokenFill: string\n /** Broken/loading image placeholder stroke (ElementNode ImageNode). */\n brokenStroke: string\n}\n\n/** Define a light color theme with specific background, foreground, and accent color values */\nexport const lightTheme: AgentAppTheme = {\n background: '233.2 14.8% 93.4%',\n foreground: '234.9 28.5% 6.4%',\n card: '0 0% 100%',\n cardForeground: '234.9 28.5% 6.4%',\n popover: '0 0% 100%',\n popoverForeground: '234.9 28.5% 6.4%',\n primary: '245 62% 57%',\n primaryForeground: '0 0% 100%',\n secondary: '233.2 19.9% 95.9%',\n secondaryForeground: '234.2 17.6% 11.3%',\n muted: '233.2 19.9% 95.9%',\n mutedForeground: '239.7 1.3% 38.2%',\n accent: '233.2 19.9% 95.9%',\n accentForeground: '234.2 17.6% 11.3%',\n destructive: '0 72% 41%',\n destructiveForeground: '0 0% 100%',\n border: '233.1 6.8% 81.4%',\n input: '233.1 6.8% 81.4%',\n ring: '245 62% 57%',\n success: '160 84% 26%',\n successForeground: '0 0% 100%',\n warning: '41 96% 38%',\n warningForeground: '38 92% 12%',\n warningStrong: '41 96% 27%',\n canvasBackdrop: 'hsl(233.1 12.5% 90.9%)',\n borderSoft: 'color-mix(in oklch, hsl(var(--border)) 40%, transparent)',\n cardEdge: 'color-mix(in oklch, hsl(var(--border)) 60%, transparent)',\n canvasRender: {\n grid: '#c0c0c0',\n snapGrid: '#a0a0a0',\n snapGuide: '#3b82f6',\n snapPage: '#f59e0b',\n snapElement: '#f43f5e',\n selectionStroke: '#00a1ff',\n selectionAnchorFill: '#ffffff',\n placeholderFill: '#1f2937',\n placeholderStroke: '#374151',\n brokenFill: '#e5e7eb',\n brokenStroke: '#9ca3af',\n },\n}\n\n/** Define a dark color scheme for the Agent app interface with specific background and foreground hues */\nexport const darkTheme: AgentAppTheme = {\n background: '234.5 21.2% 9%',\n foreground: '233.2 14.8% 93.4%',\n card: '234.1 15.5% 13.1%',\n cardForeground: '233.2 14.8% 93.4%',\n popover: '233.6 9.1% 21.7%',\n popoverForeground: '233.2 14.8% 93.4%',\n primary: '239 84% 74%',\n // Inverted, not copied from light: white on this fill measures 3.02:1.\n primaryForeground: '234.9 28.5% 6.4%',\n secondary: '233.8 11.4% 17.8%',\n secondaryForeground: '233.2 14.8% 93.4%',\n muted: '233.8 11.4% 17.8%',\n mutedForeground: '239.6 1.5% 62.5%',\n accent: '233.8 11.4% 17.8%',\n accentForeground: '233.2 14.8% 93.4%',\n destructive: '348 90% 68%',\n destructiveForeground: '234.1 15.5% 13.1%',\n border: '233.4 7% 25.6%',\n input: '234.2 17.6% 11.3%',\n ring: '239 84% 74%',\n success: '160 70% 52%',\n successForeground: '160 84% 10%',\n warning: '40 94% 56%',\n warningForeground: '38 92% 12%',\n warningStrong: '40 94% 56%',\n canvasBackdrop: 'hsl(234.9 28.5% 6.4%)',\n // The lifted border (L 0.365) leaves room to soften: 60% still reads 1.265\n // on the card, where the old border at full strength read 1.158.\n borderSoft: 'color-mix(in oklch, hsl(var(--border)) 60%, transparent)',\n cardEdge: 'color-mix(in oklch, hsl(var(--border)) 80%, transparent)',\n canvasRender: {\n grid: '#3a3a3a',\n snapGrid: '#5a5a5a',\n snapGuide: '#3b82f6',\n snapPage: '#f59e0b',\n snapElement: '#f43f5e',\n selectionStroke: '#00a1ff',\n selectionAnchorFill: '#e5e7eb',\n placeholderFill: '#2a2f3a',\n placeholderStroke: '#3f4654',\n brokenFill: '#262b33',\n brokenStroke: '#4b5563',\n },\n}\n\n/**\n * Wrap a channel triple in `hsl()`; pass through values already in a color form.\n * `oklch(…)` / `oklab(…)` / `color-mix(…)` are recognised because the ramp is\n * authored in oklch and the border tiers are colour mixes — wrapping either in\n * `hsl()` yields an invalid colour that paints as nothing.\n */\nexport function themeColor(value: string): string {\n return /^(hsl|rgb|oklch|oklab|lch|lab|color|color-mix|#)/.test(value) ? value : `hsl(${value})`\n}\n\n/**\n * Map a theme to the full CSS-variable set (shadcn triples + canvas/sequences\n * aliases + canvas surface). Apply at runtime to scope a theme without loading\n * tokens.css: `Object.assign(el.style, themeToCssVars(darkTheme))`.\n */\nexport function themeToCssVars(theme: AgentAppTheme): Record<string, string> {\n return {\n '--background': theme.background,\n '--foreground': theme.foreground,\n '--card': theme.card,\n '--card-foreground': theme.cardForeground,\n '--popover': theme.popover,\n '--popover-foreground': theme.popoverForeground,\n '--primary': theme.primary,\n '--primary-foreground': theme.primaryForeground,\n '--secondary': theme.secondary,\n '--secondary-foreground': theme.secondaryForeground,\n '--muted': theme.muted,\n '--muted-foreground': theme.mutedForeground,\n '--accent': theme.accent,\n '--accent-foreground': theme.accentForeground,\n '--destructive': theme.destructive,\n '--destructive-foreground': theme.destructiveForeground,\n '--border': theme.border,\n '--input': theme.input,\n '--ring': theme.ring,\n '--success': theme.success,\n '--success-foreground': theme.successForeground,\n '--warning': theme.warning,\n '--warning-foreground': theme.warningForeground,\n // Falls back to the pairing the tinted surfaces used before this token\n // existed, so an unset value restores the previous look rather than an\n // unthemed one.\n '--warning-strong': theme.warningStrong ?? theme.warningForeground,\n '--bg-input': `hsl(${theme.card})`,\n '--text-primary': `hsl(${theme.foreground})`,\n '--text-secondary': `hsl(${theme.secondaryForeground})`,\n '--text-muted': `hsl(${theme.mutedForeground})`,\n '--text-danger': `hsl(${theme.destructive})`,\n '--border-default': `hsl(${theme.border})`,\n '--brand-primary': `hsl(${theme.primary})`,\n '--editor-selection-background': `hsl(${theme.primary})`,\n '--editor-selection-foreground': `hsl(${theme.background})`,\n '--canvas-backdrop': theme.canvasBackdrop,\n // Full strength is the safe default: an unset tier can under-draw an edge,\n // never erase one.\n '--border-soft': theme.borderSoft ?? `hsl(${theme.border})`,\n '--card-edge': theme.cardEdge ?? `hsl(${theme.border})`,\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/theme/theme.ts"],"sourcesContent":["/**\n * Typed mirror of tokens.css for runtime/JS theming. The canonical source is\n * tokens.css (`import '@tangle-network/agent-app/styles'`); this module is for\n * apps that compute theme variables in JS, or read color values where CSS\n * custom properties cannot reach — notably Konva canvas render code, which\n * paints to a bitmap and cannot resolve `var(--…)`.\n *\n * Values are shadcn-style HSL channel triples (\"H S% L%\"); wrap with `color()`.\n *\n * The neutral triples are the RESOLVED form of tokens.css's `--neutral-*` ramp\n * (that file states them as `var(--neutral-NN-hsl)`; JS has no cascade to\n * resolve through, so they are inlined here). `tests/theme/tokens-contract.test.ts`\n * resolves the CSS one level and fails if this mirror has drifted.\n */\n\nexport interface AgentAppTheme {\n background: string\n foreground: string\n card: string\n cardForeground: string\n popover: string\n popoverForeground: string\n primary: string\n primaryForeground: string\n secondary: string\n secondaryForeground: string\n muted: string\n mutedForeground: string\n accent: string\n accentForeground: string\n destructive: string\n destructiveForeground: string\n border: string\n input: string\n ring: string\n success: string\n successForeground: string\n warning: string\n warningForeground: string\n /**\n * Warning as TEXT on a warning-TINTED surface, as a channel triple. Optional\n * so a consumer's existing `AgentAppTheme` literal still type-checks;\n * {@link themeToCssVars} falls back to `warningForeground`, which is what the\n * surface used before this token existed.\n */\n warningStrong?: string\n /** Full CSS color (not a triple) — the canvas/scene backdrop. */\n canvasBackdrop: string\n /**\n * Border tiers as FULL CSS colors (they are `color-mix()` results, not\n * triples). Optional so a consumer's existing `AgentAppTheme` literal still\n * type-checks; {@link themeToCssVars} falls back to full-strength\n * `hsl(var(--border))`, which can never erase an edge.\n */\n borderSoft?: string\n /** @see borderSoft */\n cardEdge?: string\n /** Konva render palette — full hex colors the bitmap canvas paints with\n * (it cannot resolve `var(--…)`). NOT emitted by themeToCssVars. */\n canvasRender: CanvasRenderPalette\n}\n\n/**\n * Colors the Konva design-canvas paints directly. Konva renders to a bitmap\n * and cannot read CSS custom properties, so these are full hex strings sourced\n * from the active theme and threaded through the canvas components.\n */\nexport interface CanvasRenderPalette {\n /** Grid line color (GridLayer). */\n grid: string\n /** Grid-snap guide line (SnapGuidesOverlay, kind 'grid'). */\n snapGrid: string\n /** Saved ruler-guide snap line (kind 'guide'). */\n snapGuide: string\n /** Page edge/center snap line (kinds 'page-edge'/'page-center'). */\n snapPage: string\n /** Element edge/center snap line (kinds 'element-edge'/'element-center'). */\n snapElement: string\n /** Transformer border + anchor stroke (SelectionLayer). */\n selectionStroke: string\n /** Transformer anchor fill (SelectionLayer). */\n selectionAnchorFill: string\n /** Video placeholder fill (ElementNode VideoNode). */\n placeholderFill: string\n /** Video placeholder stroke (ElementNode VideoNode). */\n placeholderStroke: string\n /** Broken/loading image placeholder fill (ElementNode ImageNode). */\n brokenFill: string\n /** Broken/loading image placeholder stroke (ElementNode ImageNode). */\n brokenStroke: string\n}\n\n/** Define a light color theme with specific background, foreground, and accent color values */\nexport const lightTheme: AgentAppTheme = {\n background: '233.2 14.8% 93.4%',\n foreground: '234.9 28.5% 6.4%',\n card: '0 0% 100%',\n cardForeground: '234.9 28.5% 6.4%',\n popover: '0 0% 100%',\n popoverForeground: '234.9 28.5% 6.4%',\n primary: '245 62% 57%',\n primaryForeground: '0 0% 100%',\n secondary: '233.2 19.9% 95.9%',\n secondaryForeground: '234.2 17.6% 11.3%',\n muted: '233.2 19.9% 95.9%',\n mutedForeground: '239.7 1.3% 38.2%',\n accent: '233.2 19.9% 95.9%',\n accentForeground: '234.2 17.6% 11.3%',\n destructive: '0 72% 41%',\n destructiveForeground: '0 0% 100%',\n border: '233.1 6.8% 81.4%',\n input: '233.1 6.8% 81.4%',\n ring: '245 62% 57%',\n success: '160 84% 26%',\n successForeground: '0 0% 100%',\n warning: '41 96% 38%',\n warningForeground: '38 92% 12%',\n warningStrong: '41 96% 27%',\n canvasBackdrop: 'hsl(233.1 12.5% 90.9%)',\n borderSoft: 'color-mix(in oklch, hsl(var(--border)) 40%, transparent)',\n cardEdge: 'color-mix(in oklch, hsl(var(--border)) 60%, transparent)',\n canvasRender: {\n grid: '#c0c0c0',\n snapGrid: '#a0a0a0',\n snapGuide: '#3b82f6',\n snapPage: '#f59e0b',\n snapElement: '#f43f5e',\n selectionStroke: '#00a1ff',\n selectionAnchorFill: '#ffffff',\n placeholderFill: '#1f2937',\n placeholderStroke: '#374151',\n brokenFill: '#e5e7eb',\n brokenStroke: '#9ca3af',\n },\n}\n\n/** Define a dark color scheme for the Agent app interface with specific background and foreground hues */\nexport const darkTheme: AgentAppTheme = {\n background: '234.5 21.2% 9%',\n foreground: '233.2 14.8% 93.4%',\n card: '233.8 12.9% 15.4%',\n cardForeground: '233.2 14.8% 93.4%',\n popover: '233.6 9.3% 21.2%',\n popoverForeground: '233.2 14.8% 93.4%',\n primary: '239 84% 74%',\n // Inverted, not copied from light: white on this fill measures 3.02:1.\n primaryForeground: '234.9 28.5% 6.4%',\n secondary: '233.7 10.7% 19.3%',\n secondaryForeground: '233.2 14.8% 93.4%',\n muted: '233.7 10.7% 19.3%',\n mutedForeground: '239.6 1.5% 62.5%',\n accent: '233.7 10.7% 19.3%',\n accentForeground: '233.2 14.8% 93.4%',\n destructive: '348 90% 68%',\n destructiveForeground: '234.2 17.6% 11.3%',\n border: '233.4 7% 25.6%',\n input: '234.2 17.6% 11.3%',\n ring: '239 84% 74%',\n success: '160 70% 52%',\n successForeground: '160 84% 10%',\n warning: '40 94% 56%',\n warningForeground: '38 92% 12%',\n warningStrong: '40 94% 56%',\n canvasBackdrop: 'hsl(234.9 28.5% 6.4%)',\n // The lifted border (L 0.365) leaves room to soften: 60% still reads 1.265\n // on the card, where the old border at full strength read 1.158.\n borderSoft: 'color-mix(in oklch, hsl(var(--border)) 60%, transparent)',\n cardEdge: 'color-mix(in oklch, hsl(var(--border)) 80%, transparent)',\n canvasRender: {\n grid: '#3a3a3a',\n snapGrid: '#5a5a5a',\n snapGuide: '#3b82f6',\n snapPage: '#f59e0b',\n snapElement: '#f43f5e',\n selectionStroke: '#00a1ff',\n selectionAnchorFill: '#e5e7eb',\n placeholderFill: '#2a2f3a',\n placeholderStroke: '#3f4654',\n brokenFill: '#262b33',\n brokenStroke: '#4b5563',\n },\n}\n\n/**\n * Wrap a channel triple in `hsl()`; pass through values already in a color form.\n * `oklch(…)` / `oklab(…)` / `color-mix(…)` are recognised because the ramp is\n * authored in oklch and the border tiers are colour mixes — wrapping either in\n * `hsl()` yields an invalid colour that paints as nothing.\n */\nexport function themeColor(value: string): string {\n return /^(hsl|rgb|oklch|oklab|lch|lab|color|color-mix|#)/.test(value) ? value : `hsl(${value})`\n}\n\n/**\n * Map a theme to the full CSS-variable set (shadcn triples + canvas/sequences\n * aliases + canvas surface). Apply at runtime to scope a theme without loading\n * tokens.css: `Object.assign(el.style, themeToCssVars(darkTheme))`.\n */\nexport function themeToCssVars(theme: AgentAppTheme): Record<string, string> {\n return {\n '--background': theme.background,\n '--foreground': theme.foreground,\n '--card': theme.card,\n '--card-foreground': theme.cardForeground,\n '--popover': theme.popover,\n '--popover-foreground': theme.popoverForeground,\n '--primary': theme.primary,\n '--primary-foreground': theme.primaryForeground,\n '--secondary': theme.secondary,\n '--secondary-foreground': theme.secondaryForeground,\n '--muted': theme.muted,\n '--muted-foreground': theme.mutedForeground,\n '--accent': theme.accent,\n '--accent-foreground': theme.accentForeground,\n '--destructive': theme.destructive,\n '--destructive-foreground': theme.destructiveForeground,\n '--border': theme.border,\n '--input': theme.input,\n '--ring': theme.ring,\n '--success': theme.success,\n '--success-foreground': theme.successForeground,\n '--warning': theme.warning,\n '--warning-foreground': theme.warningForeground,\n // Falls back to the pairing the tinted surfaces used before this token\n // existed, so an unset value restores the previous look rather than an\n // unthemed one.\n '--warning-strong': theme.warningStrong ?? theme.warningForeground,\n '--bg-input': `hsl(${theme.card})`,\n '--text-primary': `hsl(${theme.foreground})`,\n '--text-secondary': `hsl(${theme.secondaryForeground})`,\n '--text-muted': `hsl(${theme.mutedForeground})`,\n '--text-danger': `hsl(${theme.destructive})`,\n '--border-default': `hsl(${theme.border})`,\n '--brand-primary': `hsl(${theme.primary})`,\n '--editor-selection-background': `hsl(${theme.primary})`,\n '--editor-selection-foreground': `hsl(${theme.background})`,\n '--canvas-backdrop': theme.canvasBackdrop,\n // Full strength is the safe default: an unset tier can under-draw an edge,\n // never erase one.\n '--border-soft': theme.borderSoft ?? `hsl(${theme.border})`,\n '--card-edge': theme.cardEdge ?? `hsl(${theme.border})`,\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,14 +1,14 @@
1
1
  // src/design-canvas-react/lazy.tsx
2
2
  import { lazy } from "react";
3
3
  var DesignCanvasLazy = lazy(
4
- () => import("./DesignCanvasEditor-CJ7PLX7A.js").then((m) => ({ default: m.DesignCanvasEditor }))
4
+ () => import("./DesignCanvasEditor-J2OLS22G.js").then((m) => ({ default: m.DesignCanvasEditor }))
5
5
  );
6
6
  var DesignCanvasChromeLazy = lazy(
7
- () => import("./DesignCanvas-QY2CA5GI.js").then((m) => ({ default: m.DesignCanvas }))
7
+ () => import("./DesignCanvas-JRBCAWHW.js").then((m) => ({ default: m.DesignCanvas }))
8
8
  );
9
9
 
10
10
  export {
11
11
  DesignCanvasLazy,
12
12
  DesignCanvasChromeLazy
13
13
  };
14
- //# sourceMappingURL=chunk-RILGAFEL.js.map
14
+ //# sourceMappingURL=chunk-UMV45ZDF.js.map
@@ -882,7 +882,7 @@ function SourceEditor({
882
882
  }) {
883
883
  return /* @__PURE__ */ jsxs2("div", { className: "flex h-full min-h-0 flex-col bg-background", children: [
884
884
  /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-2", children: [
885
- /* @__PURE__ */ jsx2("p", { className: "truncate font-mono text-[11px] text-muted-foreground", children: path }),
885
+ /* @__PURE__ */ jsx2("p", { className: "truncate font-mono text-xs text-muted-foreground", children: path }),
886
886
  /* @__PURE__ */ jsxs2("div", { className: "flex shrink-0 items-center gap-2", children: [
887
887
  /* @__PURE__ */ jsx2("span", { className: "rounded-full border border-border bg-background px-2.5 py-1 text-xs text-muted-foreground", children: dirty ? "Unsaved changes" : "Saved" }),
888
888
  /* @__PURE__ */ jsx2(
@@ -914,4 +914,4 @@ export {
914
914
  ConfirmDialog,
915
915
  VaultPane
916
916
  };
917
- //# sourceMappingURL=chunk-JJJEO4ND.js.map
917
+ //# sourceMappingURL=chunk-VWTODXRW.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/vault/VaultPane.tsx","../src/vault/ConfirmDialog.tsx"],"sourcesContent":["/**\n * The shared 3-pane vault: tree | artifact viewer | optional agent dock. This is\n * pure shell MECHANISM — selection, the dirty-guard + pending-nav state machine,\n * rich/source editor modes, create/delete/refresh, skeletons, an error boundary,\n * and an empty state. It renders NO file tree and NO artifact viewer of its own:\n * those arrive through the `renderTree` / `renderArtifact` / `renderDock` seams,\n * so a product wires sandbox-ui's RichFileTree + FileArtifactPane in ~10 lines.\n *\n * Data flows exclusively through `port` (a `VaultDataPort`). The pane never\n * imports a fetch client, a router, a toast system, or a markdown library — the\n * optional `codec` seam supplies rich/source parsing (identity passthrough by\n * default). Chrome uses the shared theme tokens (bg-card, border-border, …).\n */\n\nimport {\n Component,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ErrorInfo,\n type MouseEvent,\n type ReactNode,\n} from 'react'\nimport { Download, Folder, Trash2 } from 'lucide-react'\nimport { ConfirmDialog } from './ConfirmDialog'\nimport type {\n VaultEditorMode,\n VaultFile,\n VaultMarkdownCodec,\n VaultOperation,\n VaultOperationFailure,\n VaultOperationPhase,\n VaultPaneProps,\n VaultRichParts,\n VaultTreeNode,\n} from './contracts'\n\nconst IDENTITY_CODEC: VaultMarkdownCodec = {\n parse: (raw) => raw,\n serialize: (parts) => (typeof parts === 'string' ? parts : String(parts ?? '')),\n}\n\ntype PendingNav = { type: 'open'; path: string } | { type: 'close' } | null\n\ninterface TreeRefreshContext {\n operation: Extract<VaultOperation, 'list' | 'create' | 'delete'>\n phase: VaultOperationPhase\n path?: string\n selectAfterRecovery?: string\n}\n\ninterface TreeFailureState {\n failure: VaultOperationFailure\n context: TreeRefreshContext\n}\n\nconst LIST_CONTEXT: TreeRefreshContext = { operation: 'list', phase: 'operation' }\n\nfunction operationMessage(error: unknown, fallback: string): string {\n return error instanceof Error && error.message ? error.message : fallback\n}\n\nfunction treeFailureMessage(failure: VaultOperationFailure): string {\n if (failure.phase !== 'post-mutation-refresh') return failure.message\n const completed = failure.operation === 'create' ? 'created' : 'deleted'\n return `The file was ${completed}, but the Vault couldn't refresh. ${failure.message}`\n}\n\ninterface TreePaths {\n files: Set<string>\n directories: Set<string>\n}\n\n// Directories are collected alongside files because a click on one has to be\n// RECOGNIZED to be answered — dropping them here is what made a folder row a\n// dead target: the path resolved to nothing and every downstream branch was\n// keyed on a file.\nfunction collectTreePaths(nodes: VaultTreeNode[], into: TreePaths): TreePaths {\n for (const node of nodes) {\n if (node.type === 'file') into.files.add(node.path)\n else into.directories.add(node.path)\n if (node.children) collectTreePaths(node.children, into)\n }\n return into\n}\n\nfunction resolveFilePath(rawPath: string, filePaths: Set<string>): string | null {\n if (filePaths.has(rawPath)) return rawPath\n const path = rawPath.replace(/^\\/+|\\/+$/g, '')\n return filePaths.has(path) ? path : null\n}\n\nfunction resolveTreePath(rawPath: string, paths: TreePaths): { path: string; type: 'file' | 'directory' } | null {\n const file = resolveFilePath(rawPath, paths.files)\n if (file) return { path: file, type: 'file' }\n if (paths.directories.has(rawPath)) return { path: rawPath, type: 'directory' }\n const trimmed = rawPath.replace(/^\\/+|\\/+$/g, '')\n return paths.directories.has(trimmed) ? { path: trimmed, type: 'directory' } : null\n}\n\nfunction findDirectory(nodes: VaultTreeNode[], path: string): VaultTreeNode | null {\n for (const node of nodes) {\n if (node.type === 'directory' && node.path === path) return node\n const found = node.children ? findDirectory(node.children, path) : null\n if (found) return found\n }\n return null\n}\n\n/** The clicked tree row, files and directories alike. The type travels with the\n * path so the caller routes on it instead of discarding everything that is not\n * a file. */\nfunction treeClickTarget(event: MouseEvent<HTMLElement>): { path: string; type: string } | null {\n const read = (el: HTMLElement) => {\n const path = el.dataset.itemPath\n return path ? { path, type: el.dataset.itemType ?? '' } : null\n }\n\n const path = event.nativeEvent.composedPath?.() ?? []\n for (const item of path) {\n if (!(item instanceof HTMLElement)) continue\n if (item.dataset.type !== 'item') continue\n return read(item)\n }\n\n const target = event.target instanceof HTMLElement\n ? event.target.closest('[data-type=\"item\"]')\n : null\n return target instanceof HTMLElement ? read(target) : null\n}\n\n// Case-insensitive name filter over the tree: files survive when their name\n// matches; a directory survives whole (with all its children) when its own name\n// matches, otherwise only when some descendant survives.\nfunction filterNodes(nodes: VaultTreeNode[], q: string): VaultTreeNode[] {\n const out: VaultTreeNode[] = []\n for (const node of nodes) {\n if (node.type === 'file') {\n if (node.name.toLowerCase().includes(q)) out.push(node)\n } else if (node.name.toLowerCase().includes(q)) {\n out.push(node)\n } else {\n const children = filterNodes(node.children ?? [], q)\n if (children.length > 0) out.push({ ...node, children })\n }\n }\n return out\n}\n\nclass EditorErrorBoundary extends Component<{ children: ReactNode; onReset?: () => void }, { error: unknown }> {\n state: { error: unknown } = { error: null }\n static getDerivedStateFromError(error: unknown) {\n return { error }\n }\n componentDidCatch(error: unknown, info: ErrorInfo) {\n console.error('Vault crashed:', error, info)\n }\n render() {\n if (this.state.error) {\n const msg = this.state.error instanceof Error\n ? this.state.error.message\n : typeof this.state.error === 'string'\n ? this.state.error\n : 'Something went wrong loading the vault'\n return (\n <div className=\"flex h-full flex-1 flex-col items-center justify-center p-8 text-center\">\n <h3 className=\"mb-1 text-sm font-medium text-foreground\">Vault failed to load</h3>\n <p className=\"mb-4 max-w-xs text-xs text-muted-foreground\">{String(msg)}</p>\n <button\n type=\"button\"\n onClick={() => { this.setState({ error: null }); this.props.onReset?.() }}\n className=\"inline-flex h-8 items-center rounded-md border border-border px-3 text-xs font-medium text-foreground transition-colors hover:bg-muted\"\n >\n Try again\n </button>\n </div>\n )\n }\n return this.props.children\n }\n}\n\n/**\n * A skeleton is a picture of a wait, and a picture is exactly what a screen\n * reader cannot see. `aria-hidden` on the shimmer bars is right — they carry no\n * information — but hiding them without saying anything else is what left the\n * whole load silent. The container announces the wait instead: `aria-busy` for\n * the state, a `status` live region so arrival is reported, and a real text\n * label, since a live region with only decorative children announces nothing.\n */\nfunction SkeletonRegion({ label, className, children }: { label: string; className: string; children: ReactNode }) {\n // The live region is a SIBLING of the shimmer, not a wrapper around it: the\n // shimmer container keeps its exact classes and its exact position in the\n // parent's layout, so adding the announcement cannot move a pixel. Wrapping\n // it would put a new box between `space-y-*` and the bars it spaces.\n return (\n <>\n <span role=\"status\" aria-live=\"polite\" aria-busy={true} className=\"sr-only\">\n {label}\n </span>\n <div className={className} aria-hidden=\"true\">\n {children}\n </div>\n </>\n )\n}\n\nfunction TreeSkeleton() {\n return (\n <SkeletonRegion label=\"Loading files…\" className=\"space-y-2 p-4\">\n {[32, 48, 40, 52].map((w, i) => (\n <div key={i} className=\"h-4 animate-pulse rounded bg-muted\" style={{ width: `${w * 4}px` }} />\n ))}\n </SkeletonRegion>\n )\n}\n\nfunction EditorSkeleton() {\n return (\n <SkeletonRegion label=\"Loading file…\" className=\"space-y-3 p-8\">\n <div className=\"h-5 w-1/2 animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-full animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-3/4 animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-2/3 animate-pulse rounded bg-muted\" />\n </SkeletonRegion>\n )\n}\n\nfunction EmptyState() {\n return (\n <div className=\"flex h-full flex-col items-center justify-center p-8 text-center\">\n <h3 className=\"text-sm font-medium text-foreground\">Open a vault document</h3>\n <p className=\"mt-1 max-w-xs text-xs text-muted-foreground\">\n Select a file from the directory, or create a new one.\n </p>\n </div>\n )\n}\n\nfunction ReadErrorState({ message, onRetry }: { message: string; onRetry: () => void }) {\n return (\n <div className=\"flex h-full flex-col items-center justify-center gap-3 p-8 text-center\">\n <div>\n <h3 className=\"text-sm font-medium text-foreground\">Couldn't open this file</h3>\n <p className=\"mt-1 max-w-md text-xs text-muted-foreground\">{message}</p>\n </div>\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"inline-flex h-8 items-center rounded-md border border-border px-3 text-xs font-medium text-foreground transition-colors hover:bg-muted\"\n >\n Retry\n </button>\n </div>\n )\n}\n\nfunction TreeErrorState({ message, onRetry }: { message: string; onRetry: () => void }) {\n return (\n <div role=\"alert\" className=\"flex h-full flex-col items-center justify-center gap-3 p-6 text-center\">\n <div>\n <h3 className=\"text-sm font-medium text-foreground\">Couldn't load the Vault</h3>\n <p className=\"mt-1 max-w-xs text-xs text-muted-foreground\">{message}</p>\n </div>\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"inline-flex h-8 items-center rounded-md border border-border px-3 text-xs font-medium text-foreground transition-colors hover:bg-muted\"\n >\n Retry\n </button>\n </div>\n )\n}\n\nfunction OperationErrorAlert({\n message,\n retryLabel,\n onRetry,\n onDismiss,\n}: {\n message: string\n retryLabel: string\n onRetry: () => void\n onDismiss: () => void\n}) {\n return (\n <div\n role=\"alert\"\n className=\"flex shrink-0 items-center justify-between gap-3 border-b border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive\"\n >\n <span className=\"min-w-0 flex-1\">{message}</span>\n <div className=\"flex shrink-0 items-center gap-3\">\n <button type=\"button\" aria-label={retryLabel} onClick={onRetry} className=\"font-medium underline-offset-2 hover:underline\">\n Retry\n </button>\n <button type=\"button\" onClick={onDismiss} className=\"underline-offset-2 hover:underline\">\n Dismiss\n </button>\n </div>\n </div>\n )\n}\n\nexport function VaultPane(props: VaultPaneProps) {\n const {\n port,\n renderTree,\n renderArtifact,\n renderDock,\n canWrite = true,\n selectedPath: controlledPath,\n onSelectedPathChange,\n onOperationError,\n codec,\n className,\n dockToggle,\n refreshKey,\n headerActions,\n onDownloadFile,\n pathBarClassName,\n } = props\n\n const activeCodec = codec ?? IDENTITY_CODEC\n const controlled = controlledPath !== undefined\n const isMarkdownCapable = codec !== undefined\n // `false` → a persistent dock (no toggle, always open with the selected file).\n const persistentDock = dockToggle === false\n const dockToggleCfg = dockToggle ? dockToggle : { label: 'Discuss', disabledWhenDirty: true }\n\n const [tree, setTree] = useState<VaultTreeNode[]>([])\n const [treeLoading, setTreeLoading] = useState(true)\n const [treeLoaded, setTreeLoaded] = useState(false)\n const [treeError, setTreeError] = useState<TreeFailureState | null>(null)\n const [internalPath, setInternalPath] = useState<string | null>(null)\n const selectedPath = controlled ? (controlledPath ?? null) : internalPath\n\n const [selectedFile, setSelectedFile] = useState<VaultFile | null>(null)\n const [fileLoading, setFileLoading] = useState(false)\n const [readError, setReadError] = useState<string | null>(null)\n const [reloadNonce, setReloadNonce] = useState(0)\n const [saving, setSaving] = useState(false)\n const [saveError, setSaveError] = useState<VaultOperationFailure | null>(null)\n\n const [editorMode, setEditorMode] = useState<VaultEditorMode>('rich')\n const [richDraft, setRichDraft] = useState<VaultRichParts>('')\n const [sourceDraft, setSourceDraft] = useState('')\n const [isDirty, setIsDirty] = useState(false)\n\n const [createOpen, setCreateOpen] = useState(false)\n const [newPath, setNewPath] = useState('')\n const [creating, setCreating] = useState(false)\n const [createError, setCreateError] = useState<VaultOperationFailure | null>(null)\n const [deleteOpen, setDeleteOpen] = useState(false)\n const [deleting, setDeleting] = useState(false)\n const [deleteError, setDeleteError] = useState<VaultOperationFailure | null>(null)\n const [dockOpen, setDockOpen] = useState(false)\n const [pendingNav, setPendingNav] = useState<PendingNav>(null)\n const [query, setQuery] = useState('')\n const [folderPath, setFolderPath] = useState<string | null>(null)\n\n const savedContentRef = useRef('')\n const loadedPathRef = useRef<string | null>(null)\n const onOperationErrorRef = useRef(onOperationError)\n onOperationErrorRef.current = onOperationError\n\n const treePaths = useMemo(\n () => collectTreePaths(tree, { files: new Set<string>(), directories: new Set<string>() }),\n [tree],\n )\n const filePaths = treePaths.files\n const resolvedSelectedPath = useMemo(\n () => selectedPath ? resolveFilePath(selectedPath, filePaths) : null,\n [selectedPath, filePaths],\n )\n // The clicked folder, resolved against the CURRENT tree — a folder that a\n // refresh removed stops being the active one on its own, so neither the\n // search scope nor the create target can point at a directory that is gone.\n const activeFolderNode = useMemo(\n () => (folderPath ? findDirectory(tree, folderPath) : null),\n [tree, folderPath],\n )\n const activeFolder = activeFolderNode?.path ?? null\n const treeRoot = useMemo<VaultTreeNode>(\n () => ({ name: 'Vault', path: '', type: 'directory', children: tree }),\n [tree],\n )\n // With no query the whole vault stays on screen: the tree renderer owns\n // expansion (both sandbox-ui trees keep it internal), so re-rooting on a\n // plain folder click would fight the expand the click already performs. The\n // folder scopes the SEARCH, which is where a narrowed list is what the reader\n // asked for.\n const visibleRoot = useMemo<VaultTreeNode>(() => {\n const q = query.trim().toLowerCase()\n if (!q) return treeRoot\n const base = activeFolderNode ?? treeRoot\n return { ...base, children: filterNodes(base.children ?? [], q) }\n }, [treeRoot, activeFolderNode, query])\n\n const commitPath = useCallback(\n (next: string | null) => {\n if (!controlled) setInternalPath(next)\n onSelectedPathChange?.(next)\n },\n [controlled, onSelectedPathChange],\n )\n\n const reportFailure = useCallback((\n operation: VaultOperation,\n phase: VaultOperationPhase,\n error: unknown,\n fallback: string,\n path?: string,\n ): VaultOperationFailure => {\n const failure: VaultOperationFailure = {\n operation,\n phase,\n path,\n message: operationMessage(error, fallback),\n cause: error,\n }\n try {\n onOperationErrorRef.current?.(failure)\n } catch (callbackError) {\n console.error('Vault onOperationError callback failed:', callbackError)\n }\n return failure\n }, [])\n\n const refresh = useCallback(async (context: TreeRefreshContext = LIST_CONTEXT): Promise<boolean> => {\n setTreeLoading(true)\n setTreeError(null)\n try {\n setTree(await port.listTree())\n setTreeLoaded(true)\n return true\n } catch (error) {\n const failure = reportFailure(\n context.operation,\n context.phase,\n error,\n 'Failed to load the Vault',\n context.path,\n )\n setTreeError({ failure, context })\n return false\n } finally {\n setTreeLoading(false)\n }\n }, [port, reportFailure])\n\n useEffect(() => {\n setTree([])\n setTreeLoaded(false)\n setTreeError(null)\n }, [port])\n\n useEffect(() => {\n void refresh()\n }, [refresh, refreshKey])\n\n const retryTree = useCallback(async () => {\n if (!treeError) return\n const { context } = treeError\n const recovered = await refresh(context)\n if (recovered && context.selectAfterRecovery) commitPath(context.selectAfterRecovery)\n }, [treeError, refresh, commitPath])\n\n useEffect(() => {\n if (!selectedPath) {\n setSelectedFile(null)\n setFileLoading(false)\n setReadError(null)\n loadedPathRef.current = null\n return\n }\n if (treeLoading || !treeLoaded) return\n if (!resolvedSelectedPath) {\n commitPath(null)\n setSelectedFile(null)\n setFileLoading(false)\n setReadError(null)\n loadedPathRef.current = null\n return\n }\n let cancelled = false\n const path = resolvedSelectedPath\n if (path !== selectedPath) commitPath(path)\n setFileLoading(true)\n setReadError(null)\n void (async () => {\n try {\n const file = await port.readFile(path)\n if (!cancelled) setSelectedFile(file)\n } catch (err) {\n // Surface read failures instead of making them indistinguishable from\n // the intentionally empty \"no file selected\" state.\n if (!cancelled) {\n const failure = reportFailure('read', 'operation', err, 'Failed to read file', path)\n setSelectedFile(null)\n setReadError(failure.message)\n }\n } finally {\n if (!cancelled) setFileLoading(false)\n }\n })()\n return () => {\n cancelled = true\n }\n }, [port, selectedPath, resolvedSelectedPath, treeLoading, treeLoaded, refreshKey, reloadNonce, commitPath, reportFailure])\n\n useEffect(() => {\n if (!selectedFile) {\n loadedPathRef.current = null\n savedContentRef.current = ''\n setRichDraft('')\n setSourceDraft('')\n setEditorMode('rich')\n setIsDirty(false)\n setSaveError(null)\n return\n }\n const pathChanged = loadedPathRef.current !== selectedFile.path\n loadedPathRef.current = selectedFile.path\n savedContentRef.current = selectedFile.content\n setRichDraft(activeCodec.parse(selectedFile.content))\n setSourceDraft(selectedFile.content)\n if (pathChanged) setEditorMode('rich')\n setIsDirty(false)\n setSaveError(null)\n setDockOpen(false)\n }, [selectedFile, activeCodec])\n\n const guardedOpen = useCallback(\n (path: string) => {\n if (path === selectedPath) return\n if (isDirty) {\n setPendingNav({ type: 'open', path })\n return\n }\n commitPath(path)\n },\n [isDirty, selectedPath, commitPath],\n )\n\n // Clicking a folder makes it the vault's active folder: the search narrows to\n // it and a new file lands inside it. Clicking it again clears that — the same\n // row is the way back out, so the gesture is reversible where it was made.\n const toggleFolder = useCallback((path: string) => {\n setFolderPath((current) => (current === path ? null : path))\n }, [])\n\n // Some tree models keep their original selection callback while resetting\n // paths internally. Keep the callable stable, but have it execute the latest\n // path validation and dirty-guard logic.\n const selectFileRef = useRef<(path: string) => void>(() => {})\n selectFileRef.current = (rawPath: string) => {\n const target = resolveTreePath(rawPath, treePaths)\n if (!target) return\n if (target.type === 'file') {\n guardedOpen(target.path)\n return\n }\n toggleFolder(target.path)\n }\n const handleTreeSelect = useCallback((path: string) => selectFileRef.current(path), [])\n\n const guardedClose = useCallback(() => {\n if (isDirty) {\n setPendingNav({ type: 'close' })\n return\n }\n commitPath(null)\n setSelectedFile(null)\n }, [isDirty, commitPath])\n\n const confirmDiscard = useCallback(() => {\n const nav = pendingNav\n setPendingNav(null)\n setIsDirty(false)\n if (!nav) return\n if (nav.type === 'open') {\n commitPath(nav.path)\n } else {\n commitPath(null)\n setSelectedFile(null)\n }\n }, [pendingNav, commitPath])\n\n const showRichMode = useCallback(() => {\n setEditorMode((mode) => {\n if (mode === 'rich') return mode\n setRichDraft(activeCodec.parse(sourceDraft))\n setIsDirty(sourceDraft !== savedContentRef.current)\n return 'rich'\n })\n }, [activeCodec, sourceDraft])\n\n const showSourceMode = useCallback(() => {\n setEditorMode((mode) => {\n if (mode === 'source') return mode\n const content = isDirty ? activeCodec.serialize(richDraft) : savedContentRef.current\n setSourceDraft(content)\n setIsDirty(content !== savedContentRef.current)\n return 'source'\n })\n }, [activeCodec, isDirty, richDraft])\n\n const onSourceChange = useCallback((next: string) => {\n setSourceDraft(next)\n setIsDirty(next !== savedContentRef.current)\n }, [])\n\n const onRichChange = useCallback((next: VaultRichParts) => {\n setRichDraft(next)\n setIsDirty(activeCodec.serialize(next) !== savedContentRef.current)\n }, [activeCodec])\n\n const saveCurrent = useCallback(async () => {\n if (!selectedFile) return\n const content = editorMode === 'source' ? sourceDraft : activeCodec.serialize(richDraft)\n setSaving(true)\n setSaveError(null)\n try {\n await port.writeFile(selectedFile.path, content)\n savedContentRef.current = content\n setSelectedFile({ ...selectedFile, content })\n setSourceDraft(content)\n setRichDraft(activeCodec.parse(content))\n setIsDirty(false)\n } catch (error) {\n setSaveError(reportFailure('save', 'operation', error, 'Failed to save file', selectedFile.path))\n } finally {\n setSaving(false)\n }\n }, [selectedFile, editorMode, sourceDraft, richDraft, activeCodec, port, reportFailure])\n\n const handleCreate = useCallback(async () => {\n const trimmed = newPath.trim()\n // Same rule as the confirm button, enforced here too: the dialog can also be\n // confirmed by keyboard, and a directory path is not a file to create.\n if (!trimmed || !(trimmed.split('/').pop()?.trim())) return\n setCreating(true)\n setCreateError(null)\n try {\n const created = await port.createFile(trimmed)\n setCreateOpen(false)\n setNewPath('')\n const refreshed = await refresh({\n operation: 'create',\n phase: 'post-mutation-refresh',\n path: created,\n selectAfterRecovery: created,\n })\n if (refreshed) commitPath(created)\n } catch (error) {\n setCreateError(reportFailure('create', 'operation', error, 'Failed to create file', trimmed))\n } finally {\n setCreating(false)\n }\n }, [newPath, port, refresh, commitPath, reportFailure])\n\n const handleDelete = useCallback(async () => {\n if (!selectedFile) return\n const path = selectedFile.path\n setDeleting(true)\n setDeleteError(null)\n try {\n await port.deleteFile(path)\n setDeleteOpen(false)\n setIsDirty(false)\n commitPath(null)\n setSelectedFile(null)\n await refresh({ operation: 'delete', phase: 'post-mutation-refresh', path })\n } catch (error) {\n setDeleteError(reportFailure('delete', 'operation', error, 'Failed to delete file', path))\n } finally {\n setDeleting(false)\n }\n }, [selectedFile, port, refresh, commitPath, reportFailure])\n\n const createFileName = newPath.trim().split('/').pop()?.trim() ?? ''\n\n let treeContent: ReactNode\n if (treeLoading || (!treeLoaded && !treeError)) {\n treeContent = <TreeSkeleton />\n } else if (!treeLoaded && treeError) {\n treeContent = <TreeErrorState message={treeFailureMessage(treeError.failure)} onRetry={() => void retryTree()} />\n } else {\n treeContent = (\n <>\n {treeError && (\n <OperationErrorAlert\n message={treeFailureMessage(treeError.failure)}\n retryLabel=\"Retry vault refresh\"\n onRetry={() => void retryTree()}\n onDismiss={() => setTreeError(null)}\n />\n )}\n {renderTree({\n root: visibleRoot,\n selectedPath: resolvedSelectedPath ?? undefined,\n onSelect: handleTreeSelect,\n })}\n </>\n )\n }\n\n return (\n <EditorErrorBoundary onReset={() => { commitPath(null); setSelectedFile(null) }}>\n <div className={`flex min-h-0 flex-1 overflow-hidden ${className ?? ''}`}>\n <div className=\"flex w-[23rem] min-w-[23rem] flex-col border-r border-border bg-background\">\n <div className=\"flex items-center gap-2 border-b border-border px-4 py-3\">\n <div className=\"min-w-0 flex-1\">\n <input\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={activeFolder ? `Search ${activeFolder}…` : 'Search…'}\n aria-label=\"Search vault\"\n className=\"h-8 w-full rounded-md border border-border bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground\"\n />\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n {headerActions}\n <button\n type=\"button\"\n aria-label=\"Refresh vault\"\n onClick={() => void refresh()}\n className=\"inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n ↻\n </button>\n {canWrite && (\n <button\n type=\"button\"\n aria-label={activeFolder ? `New vault file in ${activeFolder}` : 'New vault file'}\n onClick={() => { setCreateError(null); setNewPath(activeFolder ? `${activeFolder}/` : ''); setCreateOpen(true) }}\n className=\"inline-flex h-8 w-8 items-center justify-center rounded-md bg-primary text-primary-foreground transition-colors hover:bg-primary/90\"\n >\n +\n </button>\n )}\n </div>\n </div>\n {activeFolder && (\n <div className=\"flex items-center gap-1.5 border-b border-border bg-muted/40 px-4 py-1.5 text-xs\">\n <Folder className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <span data-vault-folder className=\"min-w-0 flex-1 truncate font-medium text-foreground\" title={activeFolder}>\n {activeFolder}\n </span>\n <button\n type=\"button\"\n aria-label=\"Clear the active folder\"\n onClick={() => setFolderPath(null)}\n className=\"shrink-0 rounded px-1 text-muted-foreground underline-offset-2 transition-colors hover:text-foreground hover:underline\"\n >\n Clear\n </button>\n </div>\n )}\n <div\n className=\"flex-1 overflow-y-auto\"\n onClickCapture={(event) => {\n const target = treeClickTarget(event)\n if (target) handleTreeSelect(target.path)\n }}\n >\n {treeContent}\n </div>\n </div>\n\n <div className=\"flex min-w-0 flex-1 flex-col overflow-hidden\">\n {selectedFile && (\n <div className={`flex shrink-0 items-center justify-between border-b border-border px-4 py-1.5 ${pathBarClassName ?? 'bg-card'}`}>\n <span data-vault-path className=\"truncate text-xs font-medium text-foreground\">{selectedFile.path}</span>\n <div className=\"flex items-center gap-1\">\n {canWrite && isMarkdownCapable && (\n <div className=\"mr-1 flex items-center gap-1\">\n <button\n type=\"button\"\n aria-label=\"Edit as rich text\"\n aria-pressed={editorMode === 'rich'}\n onClick={showRichMode}\n className={`inline-flex h-7 items-center rounded px-2 text-xs transition-colors ${\n editorMode === 'rich'\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n Rich\n </button>\n <button\n type=\"button\"\n aria-label=\"Edit as source\"\n aria-pressed={editorMode === 'source'}\n onClick={showSourceMode}\n className={`inline-flex h-7 items-center rounded px-2 text-xs transition-colors ${\n editorMode === 'source'\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n Source\n </button>\n </div>\n )}\n {renderDock && !persistentDock && (\n <button\n type=\"button\"\n aria-label={dockToggleCfg.label}\n aria-pressed={dockOpen}\n disabled={(dockToggleCfg.disabledWhenDirty ?? true) && isDirty}\n title={(dockToggleCfg.disabledWhenDirty ?? true) && isDirty ? 'Save your changes first' : (dockToggleCfg.title ?? dockToggleCfg.label)}\n onClick={() => setDockOpen((v) => !v)}\n className={`inline-flex items-center gap-1 rounded px-2 py-0.5 text-xs transition-colors disabled:pointer-events-none disabled:opacity-40 ${\n dockOpen\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n {dockToggleCfg.label}\n </button>\n )}\n {onDownloadFile && (\n <button\n type=\"button\"\n aria-label=\"Download this file\"\n title=\"Download file\"\n onClick={() => onDownloadFile(selectedFile)}\n className=\"inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <Download className=\"h-4 w-4\" />\n </button>\n )}\n {canWrite && (\n <button\n type=\"button\"\n aria-label=\"Delete this file\"\n title=\"Delete file\"\n onClick={() => { setDeleteError(null); setDeleteOpen(true) }}\n className=\"inline-flex h-7 w-7 items-center justify-center rounded text-destructive/70 transition-colors hover:bg-destructive/10 hover:text-destructive\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n )}\n </div>\n </div>\n )}\n {selectedFile && saveError && (\n <OperationErrorAlert\n message={saveError.message}\n retryLabel=\"Retry save\"\n onRetry={() => void saveCurrent()}\n onDismiss={() => setSaveError(null)}\n />\n )}\n <div className=\"flex-1 overflow-hidden\">\n {fileLoading ? (\n <EditorSkeleton />\n ) : readError ? (\n <ReadErrorState message={readError} onRetry={() => setReloadNonce((n) => n + 1)} />\n ) : selectedFile && canWrite && isMarkdownCapable && editorMode === 'source' ? (\n <SourceEditor\n path={selectedFile.path}\n content={sourceDraft}\n saving={saving}\n dirty={isDirty}\n onChange={onSourceChange}\n onSave={() => void saveCurrent()}\n />\n ) : selectedFile ? (\n renderArtifact({\n file: selectedFile,\n loading: false,\n mode: editorMode,\n canWrite,\n richDraft,\n dirty: isDirty,\n onRichChange,\n onSave: () => void saveCurrent(),\n })\n ) : (\n <EmptyState />\n )}\n </div>\n </div>\n\n {renderDock && selectedFile && renderDock({\n file: selectedFile,\n open: persistentDock ? true : dockOpen,\n onClose: persistentDock ? () => {} : () => setDockOpen(false),\n })}\n\n <ConfirmDialog\n open={createOpen}\n title=\"Create vault file\"\n description={activeFolder ? `Add a new document to ${activeFolder}.` : 'Add a new document to this vault.'}\n confirmLabel={creating ? 'Creating…' : 'Create'}\n // A prefilled folder is a path with no file name yet, so emptiness is\n // not the test — `folder/` would otherwise be sent to the port as a\n // file to create.\n confirmDisabled={creating || !createFileName}\n onConfirm={() => void handleCreate()}\n onCancel={() => { setCreateOpen(false); setNewPath(''); setCreateError(null) }}\n >\n <div className=\"space-y-2\">\n <input\n value={newPath}\n autoFocus\n onChange={(e) => setNewPath(e.target.value)}\n placeholder=\"e.g. playbooks/new-strategy.md\"\n aria-label=\"New file path\"\n className=\"h-9 w-full rounded-md border border-border bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground\"\n />\n {createError && <p role=\"alert\" className=\"text-xs text-destructive\">{createError.message}</p>}\n </div>\n </ConfirmDialog>\n\n <ConfirmDialog\n open={deleteOpen}\n title=\"Delete file?\"\n description={`This permanently removes ${selectedFile?.path ?? 'this file'} from the vault.`}\n confirmLabel={deleting ? 'Deleting…' : 'Delete file'}\n confirmDisabled={deleting}\n destructive\n onConfirm={() => void handleDelete()}\n onCancel={() => { setDeleteOpen(false); setDeleteError(null) }}\n >\n {deleteError && <p role=\"alert\" className=\"text-xs text-destructive\">{deleteError.message}</p>}\n </ConfirmDialog>\n\n <ConfirmDialog\n open={pendingNav !== null}\n title=\"Discard unsaved changes?\"\n description=\"Your edits to this document haven't been saved. Continue and lose them?\"\n confirmLabel=\"Discard changes\"\n destructive\n onConfirm={confirmDiscard}\n onCancel={() => setPendingNav(null)}\n />\n </div>\n </EditorErrorBoundary>\n )\n}\n\nfunction SourceEditor({\n path,\n content,\n saving,\n dirty,\n onChange,\n onSave,\n}: {\n path: string\n content: string\n saving: boolean\n dirty: boolean\n onChange: (content: string) => void\n onSave: () => void\n}) {\n return (\n <div className=\"flex h-full min-h-0 flex-col bg-background\">\n <div className=\"flex items-center justify-between gap-2 border-b border-border px-4 py-2\">\n <p className=\"truncate font-mono text-[11px] text-muted-foreground\">{path}</p>\n <div className=\"flex shrink-0 items-center gap-2\">\n <span className=\"rounded-full border border-border bg-background px-2.5 py-1 text-xs text-muted-foreground\">\n {dirty ? 'Unsaved changes' : 'Saved'}\n </span>\n <button\n type=\"button\"\n onClick={onSave}\n disabled={saving || !dirty}\n className=\"inline-flex h-7 items-center rounded-md bg-primary px-2 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50\"\n >\n {saving ? 'Saving…' : 'Save'}\n </button>\n </div>\n </div>\n <textarea\n value={content}\n onChange={(event) => onChange(event.target.value)}\n spellCheck={false}\n aria-label=\"Source editor\"\n // Full-bleed editor: keep the floor's ring but draw it inside the border\n // box, since an outward ring on a `flex-1` pane child is clipped.\n className=\"min-h-0 flex-1 resize-none border-0 bg-background p-4 font-mono text-sm leading-6 text-foreground focus-visible:[outline-offset:-2px]\"\n />\n </div>\n )\n}\n","/**\n * Self-contained confirm dialog — no dialog library. A focus-trapped modal with\n * Esc-to-cancel and Enter-to-confirm, used for create / delete / discard-unsaved\n * flows so the pane carries zero UI-kit dependency.\n */\n\nimport { useEffect, useRef, type ReactNode } from 'react'\n\nexport interface ConfirmDialogProps {\n open: boolean\n title: string\n description?: ReactNode\n confirmLabel?: string\n cancelLabel?: string\n /** Styles the confirm button as a destructive action. */\n destructive?: boolean\n /** Disables the confirm button (e.g. while the action is in flight). */\n confirmDisabled?: boolean\n onConfirm: () => void\n onCancel: () => void\n /** Optional body (e.g. an input field for the create flow). */\n children?: ReactNode\n}\n\nexport function ConfirmDialog({\n open,\n title,\n description,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n destructive = false,\n confirmDisabled = false,\n onConfirm,\n onCancel,\n children,\n}: ConfirmDialogProps) {\n const panelRef = useRef<HTMLDivElement>(null)\n const confirmRef = useRef<HTMLButtonElement>(null)\n\n useEffect(() => {\n if (!open) return\n confirmRef.current?.focus()\n }, [open])\n\n if (!open) return null\n\n function onKeyDown(event: React.KeyboardEvent) {\n if (event.key === 'Escape') {\n event.preventDefault()\n onCancel()\n return\n }\n if (event.key === 'Enter' && !confirmDisabled) {\n event.preventDefault()\n onConfirm()\n return\n }\n if (event.key !== 'Tab') return\n const focusable = panelRef.current?.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), input:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n )\n if (!focusable || focusable.length === 0) return\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n const active = document.activeElement as HTMLElement | null\n if (event.shiftKey && active === first) {\n event.preventDefault()\n last?.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first?.focus()\n }\n }\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\"\n onMouseDown={(e) => { if (e.target === e.currentTarget) onCancel() }}\n >\n <div\n ref={panelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n onKeyDown={onKeyDown}\n className=\"w-full max-w-md rounded-lg border border-border bg-card p-5 shadow-lg\"\n >\n <h2 className=\"text-sm font-semibold text-foreground\">{title}</h2>\n {description && <p className=\"mt-1.5 text-xs text-muted-foreground\">{description}</p>}\n {children && <div className=\"mt-3\">{children}</div>}\n <div className=\"mt-5 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"inline-flex h-8 items-center rounded-md px-3 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n {cancelLabel}\n </button>\n <button\n ref={confirmRef}\n type=\"button\"\n onClick={onConfirm}\n disabled={confirmDisabled}\n className={`inline-flex h-8 items-center rounded-md px-3 text-xs font-medium transition-colors disabled:pointer-events-none disabled:opacity-50 ${\n destructive\n ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90'\n : 'bg-primary text-primary-foreground hover:bg-primary/90'\n }`}\n >\n {confirmLabel}\n </button>\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":";AAcA;AAAA,EACE;AAAA,EACA;AAAA,EACA,aAAAA;AAAA,EACA;AAAA,EACA,UAAAC;AAAA,EACA;AAAA,OAIK;AACP,SAAS,UAAU,QAAQ,cAAc;;;ACnBzC,SAAS,WAAW,cAA8B;AAiF1C,cAGA,YAHA;AA/DD,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,cAAc;AAAA,EACd,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,WAAW,OAAuB,IAAI;AAC5C,QAAM,aAAa,OAA0B,IAAI;AAEjD,YAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,eAAW,SAAS,MAAM;AAAA,EAC5B,GAAG,CAAC,IAAI,CAAC;AAET,MAAI,CAAC,KAAM,QAAO;AAElB,WAAS,UAAU,OAA4B;AAC7C,QAAI,MAAM,QAAQ,UAAU;AAC1B,YAAM,eAAe;AACrB,eAAS;AACT;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,WAAW,CAAC,iBAAiB;AAC7C,YAAM,eAAe;AACrB,gBAAU;AACV;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,MAAO;AACzB,UAAM,YAAY,SAAS,SAAS;AAAA,MAClC;AAAA,IACF;AACA,QAAI,CAAC,aAAa,UAAU,WAAW,EAAG;AAC1C,UAAM,QAAQ,UAAU,CAAC;AACzB,UAAM,OAAO,UAAU,UAAU,SAAS,CAAC;AAC3C,UAAM,SAAS,SAAS;AACxB,QAAI,MAAM,YAAY,WAAW,OAAO;AACtC,YAAM,eAAe;AACrB,YAAM,MAAM;AAAA,IACd,WAAW,CAAC,MAAM,YAAY,WAAW,MAAM;AAC7C,YAAM,eAAe;AACrB,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,aAAa,CAAC,MAAM;AAAE,YAAI,EAAE,WAAW,EAAE,cAAe,UAAS;AAAA,MAAE;AAAA,MAEnE;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,MAAK;AAAA,UACL,cAAW;AAAA,UACX,cAAY;AAAA,UACZ;AAAA,UACA,WAAU;AAAA,UAEV;AAAA,gCAAC,QAAG,WAAU,yCAAyC,iBAAM;AAAA,YAC5D,eAAe,oBAAC,OAAE,WAAU,wCAAwC,uBAAY;AAAA,YAChF,YAAY,oBAAC,SAAI,WAAU,QAAQ,UAAS;AAAA,YAC7C,qBAAC,SAAI,WAAU,+BACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,WAAU;AAAA,kBAET;AAAA;AAAA,cACH;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,KAAK;AAAA,kBACL,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,UAAU;AAAA,kBACV,WAAW,uIACT,cACI,uEACA,wDACN;AAAA,kBAEC;AAAA;AAAA,cACH;AAAA,eACF;AAAA;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;;;ADoDQ,SA+BJ,UA9BM,OAAAC,MADF,QAAAC,aAAA;AAhIR,IAAM,iBAAqC;AAAA,EACzC,OAAO,CAAC,QAAQ;AAAA,EAChB,WAAW,CAAC,UAAW,OAAO,UAAU,WAAW,QAAQ,OAAO,SAAS,EAAE;AAC/E;AAgBA,IAAM,eAAmC,EAAE,WAAW,QAAQ,OAAO,YAAY;AAEjF,SAAS,iBAAiB,OAAgB,UAA0B;AAClE,SAAO,iBAAiB,SAAS,MAAM,UAAU,MAAM,UAAU;AACnE;AAEA,SAAS,mBAAmB,SAAwC;AAClE,MAAI,QAAQ,UAAU,wBAAyB,QAAO,QAAQ;AAC9D,QAAM,YAAY,QAAQ,cAAc,WAAW,YAAY;AAC/D,SAAO,gBAAgB,SAAS,qCAAqC,QAAQ,OAAO;AACtF;AAWA,SAAS,iBAAiB,OAAwB,MAA4B;AAC5E,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,OAAQ,MAAK,MAAM,IAAI,KAAK,IAAI;AAAA,QAC7C,MAAK,YAAY,IAAI,KAAK,IAAI;AACnC,QAAI,KAAK,SAAU,kBAAiB,KAAK,UAAU,IAAI;AAAA,EACzD;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,SAAiB,WAAuC;AAC/E,MAAI,UAAU,IAAI,OAAO,EAAG,QAAO;AACnC,QAAM,OAAO,QAAQ,QAAQ,cAAc,EAAE;AAC7C,SAAO,UAAU,IAAI,IAAI,IAAI,OAAO;AACtC;AAEA,SAAS,gBAAgB,SAAiB,OAAuE;AAC/G,QAAM,OAAO,gBAAgB,SAAS,MAAM,KAAK;AACjD,MAAI,KAAM,QAAO,EAAE,MAAM,MAAM,MAAM,OAAO;AAC5C,MAAI,MAAM,YAAY,IAAI,OAAO,EAAG,QAAO,EAAE,MAAM,SAAS,MAAM,YAAY;AAC9E,QAAM,UAAU,QAAQ,QAAQ,cAAc,EAAE;AAChD,SAAO,MAAM,YAAY,IAAI,OAAO,IAAI,EAAE,MAAM,SAAS,MAAM,YAAY,IAAI;AACjF;AAEA,SAAS,cAAc,OAAwB,MAAoC;AACjF,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,eAAe,KAAK,SAAS,KAAM,QAAO;AAC5D,UAAM,QAAQ,KAAK,WAAW,cAAc,KAAK,UAAU,IAAI,IAAI;AACnE,QAAI,MAAO,QAAO;AAAA,EACpB;AACA,SAAO;AACT;AAKA,SAAS,gBAAgB,OAAuE;AAC9F,QAAM,OAAO,CAAC,OAAoB;AAChC,UAAMC,QAAO,GAAG,QAAQ;AACxB,WAAOA,QAAO,EAAE,MAAAA,OAAM,MAAM,GAAG,QAAQ,YAAY,GAAG,IAAI;AAAA,EAC5D;AAEA,QAAM,OAAO,MAAM,YAAY,eAAe,KAAK,CAAC;AACpD,aAAW,QAAQ,MAAM;AACvB,QAAI,EAAE,gBAAgB,aAAc;AACpC,QAAI,KAAK,QAAQ,SAAS,OAAQ;AAClC,WAAO,KAAK,IAAI;AAAA,EAClB;AAEA,QAAM,SAAS,MAAM,kBAAkB,cACnC,MAAM,OAAO,QAAQ,oBAAoB,IACzC;AACJ,SAAO,kBAAkB,cAAc,KAAK,MAAM,IAAI;AACxD;AAKA,SAAS,YAAY,OAAwB,GAA4B;AACvE,QAAM,MAAuB,CAAC;AAC9B,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,QAAQ;AACxB,UAAI,KAAK,KAAK,YAAY,EAAE,SAAS,CAAC,EAAG,KAAI,KAAK,IAAI;AAAA,IACxD,WAAW,KAAK,KAAK,YAAY,EAAE,SAAS,CAAC,GAAG;AAC9C,UAAI,KAAK,IAAI;AAAA,IACf,OAAO;AACL,YAAM,WAAW,YAAY,KAAK,YAAY,CAAC,GAAG,CAAC;AACnD,UAAI,SAAS,SAAS,EAAG,KAAI,KAAK,EAAE,GAAG,MAAM,SAAS,CAAC;AAAA,IACzD;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,sBAAN,cAAkC,UAA6E;AAAA,EAC7G,QAA4B,EAAE,OAAO,KAAK;AAAA,EAC1C,OAAO,yBAAyB,OAAgB;AAC9C,WAAO,EAAE,MAAM;AAAA,EACjB;AAAA,EACA,kBAAkB,OAAgB,MAAiB;AACjD,YAAQ,MAAM,kBAAkB,OAAO,IAAI;AAAA,EAC7C;AAAA,EACA,SAAS;AACP,QAAI,KAAK,MAAM,OAAO;AACpB,YAAM,MAAM,KAAK,MAAM,iBAAiB,QACpC,KAAK,MAAM,MAAM,UACjB,OAAO,KAAK,MAAM,UAAU,WAC1B,KAAK,MAAM,QACX;AACN,aACE,gBAAAD,MAAC,SAAI,WAAU,2EACb;AAAA,wBAAAD,KAAC,QAAG,WAAU,4CAA2C,kCAAoB;AAAA,QAC7E,gBAAAA,KAAC,OAAE,WAAU,+CAA+C,iBAAO,GAAG,GAAE;AAAA,QACxE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM;AAAE,mBAAK,SAAS,EAAE,OAAO,KAAK,CAAC;AAAG,mBAAK,MAAM,UAAU;AAAA,YAAE;AAAA,YACxE,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,SACF;AAAA,IAEJ;AACA,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;AAUA,SAAS,eAAe,EAAE,OAAO,WAAW,SAAS,GAA8D;AAKjH,SACE,gBAAAC,MAAA,YACE;AAAA,oBAAAD,KAAC,UAAK,MAAK,UAAS,aAAU,UAAS,aAAW,MAAM,WAAU,WAC/D,iBACH;AAAA,IACA,gBAAAA,KAAC,SAAI,WAAsB,eAAY,QACpC,UACH;AAAA,KACF;AAEJ;AAEA,SAAS,eAAe;AACtB,SACE,gBAAAA,KAAC,kBAAe,OAAM,uBAAiB,WAAU,iBAC9C,WAAC,IAAI,IAAI,IAAI,EAAE,EAAE,IAAI,CAAC,GAAG,MACxB,gBAAAA,KAAC,SAAY,WAAU,sCAAqC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC,KAAK,KAA/E,CAAkF,CAC7F,GACH;AAEJ;AAEA,SAAS,iBAAiB;AACxB,SACE,gBAAAC,MAAC,kBAAe,OAAM,sBAAgB,WAAU,iBAC9C;AAAA,oBAAAD,KAAC,SAAI,WAAU,4CAA2C;AAAA,IAC1D,gBAAAA,KAAC,SAAI,WAAU,6CAA4C;AAAA,IAC3D,gBAAAA,KAAC,SAAI,WAAU,4CAA2C;AAAA,IAC1D,gBAAAA,KAAC,SAAI,WAAU,4CAA2C;AAAA,KAC5D;AAEJ;AAEA,SAAS,aAAa;AACpB,SACE,gBAAAC,MAAC,SAAI,WAAU,oEACb;AAAA,oBAAAD,KAAC,QAAG,WAAU,uCAAsC,mCAAqB;AAAA,IACzE,gBAAAA,KAAC,OAAE,WAAU,+CAA8C,oEAE3D;AAAA,KACF;AAEJ;AAEA,SAAS,eAAe,EAAE,SAAS,QAAQ,GAA6C;AACtF,SACE,gBAAAC,MAAC,SAAI,WAAU,0EACb;AAAA,oBAAAA,MAAC,SACC;AAAA,sBAAAD,KAAC,QAAG,WAAU,uCAAsC,qCAAuB;AAAA,MAC3E,gBAAAA,KAAC,OAAE,WAAU,+CAA+C,mBAAQ;AAAA,OACtE;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAU;AAAA,QACX;AAAA;AAAA,IAED;AAAA,KACF;AAEJ;AAEA,SAAS,eAAe,EAAE,SAAS,QAAQ,GAA6C;AACtF,SACE,gBAAAC,MAAC,SAAI,MAAK,SAAQ,WAAU,0EAC1B;AAAA,oBAAAA,MAAC,SACC;AAAA,sBAAAD,KAAC,QAAG,WAAU,uCAAsC,qCAAuB;AAAA,MAC3E,gBAAAA,KAAC,OAAE,WAAU,+CAA+C,mBAAQ;AAAA,OACtE;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAU;AAAA,QACX;AAAA;AAAA,IAED;AAAA,KACF;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MAEV;AAAA,wBAAAD,KAAC,UAAK,WAAU,kBAAkB,mBAAQ;AAAA,QAC1C,gBAAAC,MAAC,SAAI,WAAU,oCACb;AAAA,0BAAAD,KAAC,YAAO,MAAK,UAAS,cAAY,YAAY,SAAS,SAAS,WAAU,kDAAiD,mBAE3H;AAAA,UACA,gBAAAA,KAAC,YAAO,MAAK,UAAS,SAAS,WAAW,WAAU,sCAAqC,qBAEzF;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAEO,SAAS,UAAU,OAAuB;AAC/C,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,cAAc,SAAS;AAC7B,QAAM,aAAa,mBAAmB;AACtC,QAAM,oBAAoB,UAAU;AAEpC,QAAM,iBAAiB,eAAe;AACtC,QAAM,gBAAgB,aAAa,aAAa,EAAE,OAAO,WAAW,mBAAmB,KAAK;AAE5F,QAAM,CAAC,MAAM,OAAO,IAAI,SAA0B,CAAC,CAAC;AACpD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,IAAI;AACnD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAkC,IAAI;AACxE,QAAM,CAAC,cAAc,eAAe,IAAI,SAAwB,IAAI;AACpE,QAAM,eAAe,aAAc,kBAAkB,OAAQ;AAE7D,QAAM,CAAC,cAAc,eAAe,IAAI,SAA2B,IAAI;AACvE,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AACpD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAwB,IAAI;AAC9D,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,CAAC;AAChD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,QAAM,CAAC,WAAW,YAAY,IAAI,SAAuC,IAAI;AAE7E,QAAM,CAAC,YAAY,aAAa,IAAI,SAA0B,MAAM;AACpE,QAAM,CAAC,WAAW,YAAY,IAAI,SAAyB,EAAE;AAC7D,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,EAAE;AACjD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,EAAE;AACzC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,aAAa,cAAc,IAAI,SAAuC,IAAI;AACjF,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,aAAa,cAAc,IAAI,SAAuC,IAAI;AACjF,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAqB,IAAI;AAC7D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAwB,IAAI;AAEhE,QAAM,kBAAkBG,QAAO,EAAE;AACjC,QAAM,gBAAgBA,QAAsB,IAAI;AAChD,QAAM,sBAAsBA,QAAO,gBAAgB;AACnD,sBAAoB,UAAU;AAE9B,QAAM,YAAY;AAAA,IAChB,MAAM,iBAAiB,MAAM,EAAE,OAAO,oBAAI,IAAY,GAAG,aAAa,oBAAI,IAAY,EAAE,CAAC;AAAA,IACzF,CAAC,IAAI;AAAA,EACP;AACA,QAAM,YAAY,UAAU;AAC5B,QAAM,uBAAuB;AAAA,IAC3B,MAAM,eAAe,gBAAgB,cAAc,SAAS,IAAI;AAAA,IAChE,CAAC,cAAc,SAAS;AAAA,EAC1B;AAIA,QAAM,mBAAmB;AAAA,IACvB,MAAO,aAAa,cAAc,MAAM,UAAU,IAAI;AAAA,IACtD,CAAC,MAAM,UAAU;AAAA,EACnB;AACA,QAAM,eAAe,kBAAkB,QAAQ;AAC/C,QAAM,WAAW;AAAA,IACf,OAAO,EAAE,MAAM,SAAS,MAAM,IAAI,MAAM,aAAa,UAAU,KAAK;AAAA,IACpE,CAAC,IAAI;AAAA,EACP;AAMA,QAAM,cAAc,QAAuB,MAAM;AAC/C,UAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,QAAI,CAAC,EAAG,QAAO;AACf,UAAM,OAAO,oBAAoB;AACjC,WAAO,EAAE,GAAG,MAAM,UAAU,YAAY,KAAK,YAAY,CAAC,GAAG,CAAC,EAAE;AAAA,EAClE,GAAG,CAAC,UAAU,kBAAkB,KAAK,CAAC;AAEtC,QAAM,aAAa;AAAA,IACjB,CAAC,SAAwB;AACvB,UAAI,CAAC,WAAY,iBAAgB,IAAI;AACrC,6BAAuB,IAAI;AAAA,IAC7B;AAAA,IACA,CAAC,YAAY,oBAAoB;AAAA,EACnC;AAEA,QAAM,gBAAgB,YAAY,CAChC,WACA,OACA,OACA,UACA,SAC0B;AAC1B,UAAM,UAAiC;AAAA,MACrC;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,iBAAiB,OAAO,QAAQ;AAAA,MACzC,OAAO;AAAA,IACT;AACA,QAAI;AACF,0BAAoB,UAAU,OAAO;AAAA,IACvC,SAAS,eAAe;AACtB,cAAQ,MAAM,2CAA2C,aAAa;AAAA,IACxE;AACA,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,YAAY,OAAO,UAA8B,iBAAmC;AAClG,mBAAe,IAAI;AACnB,iBAAa,IAAI;AACjB,QAAI;AACF,cAAQ,MAAM,KAAK,SAAS,CAAC;AAC7B,oBAAc,IAAI;AAClB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,UAAU;AAAA,QACd,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACV;AACA,mBAAa,EAAE,SAAS,QAAQ,CAAC;AACjC,aAAO;AAAA,IACT,UAAE;AACA,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,MAAM,aAAa,CAAC;AAExB,EAAAC,WAAU,MAAM;AACd,YAAQ,CAAC,CAAC;AACV,kBAAc,KAAK;AACnB,iBAAa,IAAI;AAAA,EACnB,GAAG,CAAC,IAAI,CAAC;AAET,EAAAA,WAAU,MAAM;AACd,SAAK,QAAQ;AAAA,EACf,GAAG,CAAC,SAAS,UAAU,CAAC;AAExB,QAAM,YAAY,YAAY,YAAY;AACxC,QAAI,CAAC,UAAW;AAChB,UAAM,EAAE,QAAQ,IAAI;AACpB,UAAM,YAAY,MAAM,QAAQ,OAAO;AACvC,QAAI,aAAa,QAAQ,oBAAqB,YAAW,QAAQ,mBAAmB;AAAA,EACtF,GAAG,CAAC,WAAW,SAAS,UAAU,CAAC;AAEnC,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,cAAc;AACjB,sBAAgB,IAAI;AACpB,qBAAe,KAAK;AACpB,mBAAa,IAAI;AACjB,oBAAc,UAAU;AACxB;AAAA,IACF;AACA,QAAI,eAAe,CAAC,WAAY;AAChC,QAAI,CAAC,sBAAsB;AACzB,iBAAW,IAAI;AACf,sBAAgB,IAAI;AACpB,qBAAe,KAAK;AACpB,mBAAa,IAAI;AACjB,oBAAc,UAAU;AACxB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,UAAM,OAAO;AACb,QAAI,SAAS,aAAc,YAAW,IAAI;AAC1C,mBAAe,IAAI;AACnB,iBAAa,IAAI;AACjB,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,OAAO,MAAM,KAAK,SAAS,IAAI;AACrC,YAAI,CAAC,UAAW,iBAAgB,IAAI;AAAA,MACtC,SAAS,KAAK;AAGZ,YAAI,CAAC,WAAW;AACd,gBAAM,UAAU,cAAc,QAAQ,aAAa,KAAK,uBAAuB,IAAI;AACnF,0BAAgB,IAAI;AACpB,uBAAa,QAAQ,OAAO;AAAA,QAC9B;AAAA,MACF,UAAE;AACA,YAAI,CAAC,UAAW,gBAAe,KAAK;AAAA,MACtC;AAAA,IACF,GAAG;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,MAAM,cAAc,sBAAsB,aAAa,YAAY,YAAY,aAAa,YAAY,aAAa,CAAC;AAE1H,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,cAAc;AACjB,oBAAc,UAAU;AACxB,sBAAgB,UAAU;AAC1B,mBAAa,EAAE;AACf,qBAAe,EAAE;AACjB,oBAAc,MAAM;AACpB,iBAAW,KAAK;AAChB,mBAAa,IAAI;AACjB;AAAA,IACF;AACA,UAAM,cAAc,cAAc,YAAY,aAAa;AAC3D,kBAAc,UAAU,aAAa;AACrC,oBAAgB,UAAU,aAAa;AACvC,iBAAa,YAAY,MAAM,aAAa,OAAO,CAAC;AACpD,mBAAe,aAAa,OAAO;AACnC,QAAI,YAAa,eAAc,MAAM;AACrC,eAAW,KAAK;AAChB,iBAAa,IAAI;AACjB,gBAAY,KAAK;AAAA,EACnB,GAAG,CAAC,cAAc,WAAW,CAAC;AAE9B,QAAM,cAAc;AAAA,IAClB,CAAC,SAAiB;AAChB,UAAI,SAAS,aAAc;AAC3B,UAAI,SAAS;AACX,sBAAc,EAAE,MAAM,QAAQ,KAAK,CAAC;AACpC;AAAA,MACF;AACA,iBAAW,IAAI;AAAA,IACjB;AAAA,IACA,CAAC,SAAS,cAAc,UAAU;AAAA,EACpC;AAKA,QAAM,eAAe,YAAY,CAAC,SAAiB;AACjD,kBAAc,CAAC,YAAa,YAAY,OAAO,OAAO,IAAK;AAAA,EAC7D,GAAG,CAAC,CAAC;AAKL,QAAM,gBAAgBD,QAA+B,MAAM;AAAA,EAAC,CAAC;AAC7D,gBAAc,UAAU,CAAC,YAAoB;AAC3C,UAAM,SAAS,gBAAgB,SAAS,SAAS;AACjD,QAAI,CAAC,OAAQ;AACb,QAAI,OAAO,SAAS,QAAQ;AAC1B,kBAAY,OAAO,IAAI;AACvB;AAAA,IACF;AACA,iBAAa,OAAO,IAAI;AAAA,EAC1B;AACA,QAAM,mBAAmB,YAAY,CAAC,SAAiB,cAAc,QAAQ,IAAI,GAAG,CAAC,CAAC;AAEtF,QAAM,eAAe,YAAY,MAAM;AACrC,QAAI,SAAS;AACX,oBAAc,EAAE,MAAM,QAAQ,CAAC;AAC/B;AAAA,IACF;AACA,eAAW,IAAI;AACf,oBAAgB,IAAI;AAAA,EACtB,GAAG,CAAC,SAAS,UAAU,CAAC;AAExB,QAAM,iBAAiB,YAAY,MAAM;AACvC,UAAM,MAAM;AACZ,kBAAc,IAAI;AAClB,eAAW,KAAK;AAChB,QAAI,CAAC,IAAK;AACV,QAAI,IAAI,SAAS,QAAQ;AACvB,iBAAW,IAAI,IAAI;AAAA,IACrB,OAAO;AACL,iBAAW,IAAI;AACf,sBAAgB,IAAI;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,YAAY,UAAU,CAAC;AAE3B,QAAM,eAAe,YAAY,MAAM;AACrC,kBAAc,CAAC,SAAS;AACtB,UAAI,SAAS,OAAQ,QAAO;AAC5B,mBAAa,YAAY,MAAM,WAAW,CAAC;AAC3C,iBAAW,gBAAgB,gBAAgB,OAAO;AAClD,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,aAAa,WAAW,CAAC;AAE7B,QAAM,iBAAiB,YAAY,MAAM;AACvC,kBAAc,CAAC,SAAS;AACtB,UAAI,SAAS,SAAU,QAAO;AAC9B,YAAM,UAAU,UAAU,YAAY,UAAU,SAAS,IAAI,gBAAgB;AAC7E,qBAAe,OAAO;AACtB,iBAAW,YAAY,gBAAgB,OAAO;AAC9C,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,aAAa,SAAS,SAAS,CAAC;AAEpC,QAAM,iBAAiB,YAAY,CAAC,SAAiB;AACnD,mBAAe,IAAI;AACnB,eAAW,SAAS,gBAAgB,OAAO;AAAA,EAC7C,GAAG,CAAC,CAAC;AAEL,QAAM,eAAe,YAAY,CAAC,SAAyB;AACzD,iBAAa,IAAI;AACjB,eAAW,YAAY,UAAU,IAAI,MAAM,gBAAgB,OAAO;AAAA,EACpE,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,cAAc,YAAY,YAAY;AAC1C,QAAI,CAAC,aAAc;AACnB,UAAM,UAAU,eAAe,WAAW,cAAc,YAAY,UAAU,SAAS;AACvF,cAAU,IAAI;AACd,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,KAAK,UAAU,aAAa,MAAM,OAAO;AAC/C,sBAAgB,UAAU;AAC1B,sBAAgB,EAAE,GAAG,cAAc,QAAQ,CAAC;AAC5C,qBAAe,OAAO;AACtB,mBAAa,YAAY,MAAM,OAAO,CAAC;AACvC,iBAAW,KAAK;AAAA,IAClB,SAAS,OAAO;AACd,mBAAa,cAAc,QAAQ,aAAa,OAAO,uBAAuB,aAAa,IAAI,CAAC;AAAA,IAClG,UAAE;AACA,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,cAAc,YAAY,aAAa,WAAW,aAAa,MAAM,aAAa,CAAC;AAEvF,QAAM,eAAe,YAAY,YAAY;AAC3C,UAAM,UAAU,QAAQ,KAAK;AAG7B,QAAI,CAAC,WAAW,CAAE,QAAQ,MAAM,GAAG,EAAE,IAAI,GAAG,KAAK,EAAI;AACrD,gBAAY,IAAI;AAChB,mBAAe,IAAI;AACnB,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,WAAW,OAAO;AAC7C,oBAAc,KAAK;AACnB,iBAAW,EAAE;AACb,YAAM,YAAY,MAAM,QAAQ;AAAA,QAC9B,WAAW;AAAA,QACX,OAAO;AAAA,QACP,MAAM;AAAA,QACN,qBAAqB;AAAA,MACvB,CAAC;AACD,UAAI,UAAW,YAAW,OAAO;AAAA,IACnC,SAAS,OAAO;AACd,qBAAe,cAAc,UAAU,aAAa,OAAO,yBAAyB,OAAO,CAAC;AAAA,IAC9F,UAAE;AACA,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,SAAS,MAAM,SAAS,YAAY,aAAa,CAAC;AAEtD,QAAM,eAAe,YAAY,YAAY;AAC3C,QAAI,CAAC,aAAc;AACnB,UAAM,OAAO,aAAa;AAC1B,gBAAY,IAAI;AAChB,mBAAe,IAAI;AACnB,QAAI;AACF,YAAM,KAAK,WAAW,IAAI;AAC1B,oBAAc,KAAK;AACnB,iBAAW,KAAK;AAChB,iBAAW,IAAI;AACf,sBAAgB,IAAI;AACpB,YAAM,QAAQ,EAAE,WAAW,UAAU,OAAO,yBAAyB,KAAK,CAAC;AAAA,IAC7E,SAAS,OAAO;AACd,qBAAe,cAAc,UAAU,aAAa,OAAO,yBAAyB,IAAI,CAAC;AAAA,IAC3F,UAAE;AACA,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,cAAc,MAAM,SAAS,YAAY,aAAa,CAAC;AAE3D,QAAM,iBAAiB,QAAQ,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI,GAAG,KAAK,KAAK;AAElE,MAAI;AACJ,MAAI,eAAgB,CAAC,cAAc,CAAC,WAAY;AAC9C,kBAAc,gBAAAH,KAAC,gBAAa;AAAA,EAC9B,WAAW,CAAC,cAAc,WAAW;AACnC,kBAAc,gBAAAA,KAAC,kBAAe,SAAS,mBAAmB,UAAU,OAAO,GAAG,SAAS,MAAM,KAAK,UAAU,GAAG;AAAA,EACjH,OAAO;AACL,kBACE,gBAAAC,MAAA,YACG;AAAA,mBACC,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,mBAAmB,UAAU,OAAO;AAAA,UAC7C,YAAW;AAAA,UACX,SAAS,MAAM,KAAK,UAAU;AAAA,UAC9B,WAAW,MAAM,aAAa,IAAI;AAAA;AAAA,MACpC;AAAA,MAED,WAAW;AAAA,QACV,MAAM;AAAA,QACN,cAAc,wBAAwB;AAAA,QACtC,UAAU;AAAA,MACZ,CAAC;AAAA,OACH;AAAA,EAEJ;AAEA,SACE,gBAAAA,KAAC,uBAAoB,SAAS,MAAM;AAAE,eAAW,IAAI;AAAG,oBAAgB,IAAI;AAAA,EAAE,GAC5E,0BAAAC,MAAC,SAAI,WAAW,uCAAuC,aAAa,EAAE,IACpE;AAAA,oBAAAA,MAAC,SAAI,WAAU,8EACb;AAAA,sBAAAA,MAAC,SAAI,WAAU,4DACb;AAAA,wBAAAD,KAAC,SAAI,WAAU,kBACb,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,OAAO;AAAA,YACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,YACxC,aAAa,eAAe,UAAU,YAAY,WAAM;AAAA,YACxD,cAAW;AAAA,YACX,WAAU;AAAA;AAAA,QACZ,GACF;AAAA,QACA,gBAAAC,MAAC,SAAI,WAAU,oCACZ;AAAA;AAAA,UACD,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAW;AAAA,cACX,SAAS,MAAM,KAAK,QAAQ;AAAA,cAC5B,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,UACC,YACC,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAY,eAAe,qBAAqB,YAAY,KAAK;AAAA,cACjE,SAAS,MAAM;AAAE,+BAAe,IAAI;AAAG,2BAAW,eAAe,GAAG,YAAY,MAAM,EAAE;AAAG,8BAAc,IAAI;AAAA,cAAE;AAAA,cAC/G,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,WAEJ;AAAA,SACF;AAAA,MACC,gBACC,gBAAAC,MAAC,SAAI,WAAU,oFACb;AAAA,wBAAAD,KAAC,UAAO,WAAU,8CAA6C,eAAY,QAAO;AAAA,QAClF,gBAAAA,KAAC,UAAK,qBAAiB,MAAC,WAAU,uDAAsD,OAAO,cAC5F,wBACH;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAW;AAAA,YACX,SAAS,MAAM,cAAc,IAAI;AAAA,YAChD,WAAU;AAAA,YACI;AAAA;AAAA,QAED;AAAA,SACF;AAAA,MAEF,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,gBAAgB,CAAC,UAAU;AACzB,kBAAM,SAAS,gBAAgB,KAAK;AACpC,gBAAI,OAAQ,kBAAiB,OAAO,IAAI;AAAA,UAC1C;AAAA,UAEC;AAAA;AAAA,MACH;AAAA,OACF;AAAA,IAEA,gBAAAC,MAAC,SAAI,WAAU,gDACZ;AAAA,sBACC,gBAAAA,MAAC,SAAI,WAAW,iFAAiF,oBAAoB,SAAS,IAC5H;AAAA,wBAAAD,KAAC,UAAK,mBAAe,MAAC,WAAU,gDAAgD,uBAAa,MAAK;AAAA,QAClG,gBAAAC,MAAC,SAAI,WAAU,2BACZ;AAAA,sBAAY,qBACX,gBAAAA,MAAC,SAAI,WAAU,gCACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,cAAW;AAAA,gBACX,gBAAc,eAAe;AAAA,gBAC7B,SAAS;AAAA,gBACT,WAAW,uEACT,eAAe,SACX,uCACA,4DACN;AAAA,gBACD;AAAA;AAAA,YAED;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,cAAW;AAAA,gBACX,gBAAc,eAAe;AAAA,gBAC7B,SAAS;AAAA,gBACT,WAAW,uEACT,eAAe,WACX,uCACA,4DACN;AAAA,gBACD;AAAA;AAAA,YAED;AAAA,aACF;AAAA,UAED,cAAc,CAAC,kBACd,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAY,cAAc;AAAA,cAC1B,gBAAc;AAAA,cACd,WAAW,cAAc,qBAAqB,SAAS;AAAA,cACvD,QAAQ,cAAc,qBAAqB,SAAS,UAAU,4BAA6B,cAAc,SAAS,cAAc;AAAA,cAChI,SAAS,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;AAAA,cACpC,WAAW,iIACT,WACI,uCACA,4DACN;AAAA,cAEC,wBAAc;AAAA;AAAA,UACjB;AAAA,UAED,kBACC,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAW;AAAA,cACX,OAAM;AAAA,cACN,SAAS,MAAM,eAAe,YAAY;AAAA,cAC1C,WAAU;AAAA,cAEV,0BAAAA,KAAC,YAAS,WAAU,WAAU;AAAA;AAAA,UAChC;AAAA,UAED,YACC,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAW;AAAA,cACX,OAAM;AAAA,cACN,SAAS,MAAM;AAAE,+BAAe,IAAI;AAAG,8BAAc,IAAI;AAAA,cAAE;AAAA,cAC3D,WAAU;AAAA,cAEV,0BAAAA,KAAC,UAAO,WAAU,WAAU;AAAA;AAAA,UAC9B;AAAA,WAEJ;AAAA,SACF;AAAA,MAED,gBAAgB,aACf,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,UAAU;AAAA,UACnB,YAAW;AAAA,UACX,SAAS,MAAM,KAAK,YAAY;AAAA,UAChC,WAAW,MAAM,aAAa,IAAI;AAAA;AAAA,MACpC;AAAA,MAEF,gBAAAA,KAAC,SAAI,WAAU,0BACZ,wBACC,gBAAAA,KAAC,kBAAe,IACd,YACF,gBAAAA,KAAC,kBAAe,SAAS,WAAW,SAAS,MAAM,eAAe,CAAC,MAAM,IAAI,CAAC,GAAG,IAC/E,gBAAgB,YAAY,qBAAqB,eAAe,WAClE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,aAAa;AAAA,UACnB,SAAS;AAAA,UACT;AAAA,UACA,OAAO;AAAA,UACP,UAAU;AAAA,UACV,QAAQ,MAAM,KAAK,YAAY;AAAA;AAAA,MACjC,IACE,eACF,eAAe;AAAA,QACb,MAAM;AAAA,QACN,SAAS;AAAA,QACT,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP;AAAA,QACA,QAAQ,MAAM,KAAK,YAAY;AAAA,MACjC,CAAC,IAED,gBAAAA,KAAC,cAAW,GAEhB;AAAA,OACF;AAAA,IAEC,cAAc,gBAAgB,WAAW;AAAA,MACxC,MAAM;AAAA,MACN,MAAM,iBAAiB,OAAO;AAAA,MAC9B,SAAS,iBAAiB,MAAM;AAAA,MAAC,IAAI,MAAM,YAAY,KAAK;AAAA,IAC9D,CAAC;AAAA,IAED,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,OAAM;AAAA,QACN,aAAa,eAAe,yBAAyB,YAAY,MAAM;AAAA,QACvE,cAAc,WAAW,mBAAc;AAAA,QAIvC,iBAAiB,YAAY,CAAC;AAAA,QAC9B,WAAW,MAAM,KAAK,aAAa;AAAA,QACnC,UAAU,MAAM;AAAE,wBAAc,KAAK;AAAG,qBAAW,EAAE;AAAG,yBAAe,IAAI;AAAA,QAAE;AAAA,QAE7E,0BAAAC,MAAC,SAAI,WAAU,aACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,WAAS;AAAA,cACT,UAAU,CAAC,MAAM,WAAW,EAAE,OAAO,KAAK;AAAA,cAC1C,aAAY;AAAA,cACZ,cAAW;AAAA,cACX,WAAU;AAAA;AAAA,UACZ;AAAA,UACC,eAAe,gBAAAA,KAAC,OAAE,MAAK,SAAQ,WAAU,4BAA4B,sBAAY,SAAQ;AAAA,WAC5F;AAAA;AAAA,IACF;AAAA,IAEA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,OAAM;AAAA,QACN,aAAa,4BAA4B,cAAc,QAAQ,WAAW;AAAA,QAC1E,cAAc,WAAW,mBAAc;AAAA,QACvC,iBAAiB;AAAA,QACjB,aAAW;AAAA,QACX,WAAW,MAAM,KAAK,aAAa;AAAA,QACnC,UAAU,MAAM;AAAE,wBAAc,KAAK;AAAG,yBAAe,IAAI;AAAA,QAAE;AAAA,QAE5D,yBAAe,gBAAAA,KAAC,OAAE,MAAK,SAAQ,WAAU,4BAA4B,sBAAY,SAAQ;AAAA;AAAA,IAC5F;AAAA,IAEA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,eAAe;AAAA,QACrB,OAAM;AAAA,QACN,aAAY;AAAA,QACZ,cAAa;AAAA,QACb,aAAW;AAAA,QACX,WAAW;AAAA,QACX,UAAU,MAAM,cAAc,IAAI;AAAA;AAAA,IACpC;AAAA,KACF,GACF;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,SACE,gBAAAC,MAAC,SAAI,WAAU,8CACb;AAAA,oBAAAA,MAAC,SAAI,WAAU,4EACb;AAAA,sBAAAD,KAAC,OAAE,WAAU,wDAAwD,gBAAK;AAAA,MAC1E,gBAAAC,MAAC,SAAI,WAAU,oCACb;AAAA,wBAAAD,KAAC,UAAK,WAAU,6FACb,kBAAQ,oBAAoB,SAC/B;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS;AAAA,YACT,UAAU,UAAU,CAAC;AAAA,YACrB,WAAU;AAAA,YAET,mBAAS,iBAAY;AAAA;AAAA,QACxB;AAAA,SACF;AAAA,OACF;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,QAChD,YAAY;AAAA,QACZ,cAAW;AAAA,QAGX,WAAU;AAAA;AAAA,IACZ;AAAA,KACF;AAEJ;","names":["useEffect","useRef","jsx","jsxs","path","useRef","useEffect"]}
1
+ {"version":3,"sources":["../src/vault/VaultPane.tsx","../src/vault/ConfirmDialog.tsx"],"sourcesContent":["/**\n * The shared 3-pane vault: tree | artifact viewer | optional agent dock. This is\n * pure shell MECHANISM — selection, the dirty-guard + pending-nav state machine,\n * rich/source editor modes, create/delete/refresh, skeletons, an error boundary,\n * and an empty state. It renders NO file tree and NO artifact viewer of its own:\n * those arrive through the `renderTree` / `renderArtifact` / `renderDock` seams,\n * so a product wires sandbox-ui's RichFileTree + FileArtifactPane in ~10 lines.\n *\n * Data flows exclusively through `port` (a `VaultDataPort`). The pane never\n * imports a fetch client, a router, a toast system, or a markdown library — the\n * optional `codec` seam supplies rich/source parsing (identity passthrough by\n * default). Chrome uses the shared theme tokens (bg-card, border-border, …).\n */\n\nimport {\n Component,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ErrorInfo,\n type MouseEvent,\n type ReactNode,\n} from 'react'\nimport { Download, Folder, Trash2 } from 'lucide-react'\nimport { ConfirmDialog } from './ConfirmDialog'\nimport type {\n VaultEditorMode,\n VaultFile,\n VaultMarkdownCodec,\n VaultOperation,\n VaultOperationFailure,\n VaultOperationPhase,\n VaultPaneProps,\n VaultRichParts,\n VaultTreeNode,\n} from './contracts'\n\nconst IDENTITY_CODEC: VaultMarkdownCodec = {\n parse: (raw) => raw,\n serialize: (parts) => (typeof parts === 'string' ? parts : String(parts ?? '')),\n}\n\ntype PendingNav = { type: 'open'; path: string } | { type: 'close' } | null\n\ninterface TreeRefreshContext {\n operation: Extract<VaultOperation, 'list' | 'create' | 'delete'>\n phase: VaultOperationPhase\n path?: string\n selectAfterRecovery?: string\n}\n\ninterface TreeFailureState {\n failure: VaultOperationFailure\n context: TreeRefreshContext\n}\n\nconst LIST_CONTEXT: TreeRefreshContext = { operation: 'list', phase: 'operation' }\n\nfunction operationMessage(error: unknown, fallback: string): string {\n return error instanceof Error && error.message ? error.message : fallback\n}\n\nfunction treeFailureMessage(failure: VaultOperationFailure): string {\n if (failure.phase !== 'post-mutation-refresh') return failure.message\n const completed = failure.operation === 'create' ? 'created' : 'deleted'\n return `The file was ${completed}, but the Vault couldn't refresh. ${failure.message}`\n}\n\ninterface TreePaths {\n files: Set<string>\n directories: Set<string>\n}\n\n// Directories are collected alongside files because a click on one has to be\n// RECOGNIZED to be answered — dropping them here is what made a folder row a\n// dead target: the path resolved to nothing and every downstream branch was\n// keyed on a file.\nfunction collectTreePaths(nodes: VaultTreeNode[], into: TreePaths): TreePaths {\n for (const node of nodes) {\n if (node.type === 'file') into.files.add(node.path)\n else into.directories.add(node.path)\n if (node.children) collectTreePaths(node.children, into)\n }\n return into\n}\n\nfunction resolveFilePath(rawPath: string, filePaths: Set<string>): string | null {\n if (filePaths.has(rawPath)) return rawPath\n const path = rawPath.replace(/^\\/+|\\/+$/g, '')\n return filePaths.has(path) ? path : null\n}\n\nfunction resolveTreePath(rawPath: string, paths: TreePaths): { path: string; type: 'file' | 'directory' } | null {\n const file = resolveFilePath(rawPath, paths.files)\n if (file) return { path: file, type: 'file' }\n if (paths.directories.has(rawPath)) return { path: rawPath, type: 'directory' }\n const trimmed = rawPath.replace(/^\\/+|\\/+$/g, '')\n return paths.directories.has(trimmed) ? { path: trimmed, type: 'directory' } : null\n}\n\nfunction findDirectory(nodes: VaultTreeNode[], path: string): VaultTreeNode | null {\n for (const node of nodes) {\n if (node.type === 'directory' && node.path === path) return node\n const found = node.children ? findDirectory(node.children, path) : null\n if (found) return found\n }\n return null\n}\n\n/** The clicked tree row, files and directories alike. The type travels with the\n * path so the caller routes on it instead of discarding everything that is not\n * a file. */\nfunction treeClickTarget(event: MouseEvent<HTMLElement>): { path: string; type: string } | null {\n const read = (el: HTMLElement) => {\n const path = el.dataset.itemPath\n return path ? { path, type: el.dataset.itemType ?? '' } : null\n }\n\n const path = event.nativeEvent.composedPath?.() ?? []\n for (const item of path) {\n if (!(item instanceof HTMLElement)) continue\n if (item.dataset.type !== 'item') continue\n return read(item)\n }\n\n const target = event.target instanceof HTMLElement\n ? event.target.closest('[data-type=\"item\"]')\n : null\n return target instanceof HTMLElement ? read(target) : null\n}\n\n// Case-insensitive name filter over the tree: files survive when their name\n// matches; a directory survives whole (with all its children) when its own name\n// matches, otherwise only when some descendant survives.\nfunction filterNodes(nodes: VaultTreeNode[], q: string): VaultTreeNode[] {\n const out: VaultTreeNode[] = []\n for (const node of nodes) {\n if (node.type === 'file') {\n if (node.name.toLowerCase().includes(q)) out.push(node)\n } else if (node.name.toLowerCase().includes(q)) {\n out.push(node)\n } else {\n const children = filterNodes(node.children ?? [], q)\n if (children.length > 0) out.push({ ...node, children })\n }\n }\n return out\n}\n\nclass EditorErrorBoundary extends Component<{ children: ReactNode; onReset?: () => void }, { error: unknown }> {\n state: { error: unknown } = { error: null }\n static getDerivedStateFromError(error: unknown) {\n return { error }\n }\n componentDidCatch(error: unknown, info: ErrorInfo) {\n console.error('Vault crashed:', error, info)\n }\n render() {\n if (this.state.error) {\n const msg = this.state.error instanceof Error\n ? this.state.error.message\n : typeof this.state.error === 'string'\n ? this.state.error\n : 'Something went wrong loading the vault'\n return (\n <div className=\"flex h-full flex-1 flex-col items-center justify-center p-8 text-center\">\n <h3 className=\"mb-1 text-sm font-medium text-foreground\">Vault failed to load</h3>\n <p className=\"mb-4 max-w-xs text-xs text-muted-foreground\">{String(msg)}</p>\n <button\n type=\"button\"\n onClick={() => { this.setState({ error: null }); this.props.onReset?.() }}\n className=\"inline-flex h-8 items-center rounded-md border border-border px-3 text-xs font-medium text-foreground transition-colors hover:bg-muted\"\n >\n Try again\n </button>\n </div>\n )\n }\n return this.props.children\n }\n}\n\n/**\n * A skeleton is a picture of a wait, and a picture is exactly what a screen\n * reader cannot see. `aria-hidden` on the shimmer bars is right — they carry no\n * information — but hiding them without saying anything else is what left the\n * whole load silent. The container announces the wait instead: `aria-busy` for\n * the state, a `status` live region so arrival is reported, and a real text\n * label, since a live region with only decorative children announces nothing.\n */\nfunction SkeletonRegion({ label, className, children }: { label: string; className: string; children: ReactNode }) {\n // The live region is a SIBLING of the shimmer, not a wrapper around it: the\n // shimmer container keeps its exact classes and its exact position in the\n // parent's layout, so adding the announcement cannot move a pixel. Wrapping\n // it would put a new box between `space-y-*` and the bars it spaces.\n return (\n <>\n <span role=\"status\" aria-live=\"polite\" aria-busy={true} className=\"sr-only\">\n {label}\n </span>\n <div className={className} aria-hidden=\"true\">\n {children}\n </div>\n </>\n )\n}\n\nfunction TreeSkeleton() {\n return (\n <SkeletonRegion label=\"Loading files…\" className=\"space-y-2 p-4\">\n {[32, 48, 40, 52].map((w, i) => (\n <div key={i} className=\"h-4 animate-pulse rounded bg-muted\" style={{ width: `${w * 4}px` }} />\n ))}\n </SkeletonRegion>\n )\n}\n\nfunction EditorSkeleton() {\n return (\n <SkeletonRegion label=\"Loading file…\" className=\"space-y-3 p-8\">\n <div className=\"h-5 w-1/2 animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-full animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-3/4 animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-2/3 animate-pulse rounded bg-muted\" />\n </SkeletonRegion>\n )\n}\n\nfunction EmptyState() {\n return (\n <div className=\"flex h-full flex-col items-center justify-center p-8 text-center\">\n <h3 className=\"text-sm font-medium text-foreground\">Open a vault document</h3>\n <p className=\"mt-1 max-w-xs text-xs text-muted-foreground\">\n Select a file from the directory, or create a new one.\n </p>\n </div>\n )\n}\n\nfunction ReadErrorState({ message, onRetry }: { message: string; onRetry: () => void }) {\n return (\n <div className=\"flex h-full flex-col items-center justify-center gap-3 p-8 text-center\">\n <div>\n <h3 className=\"text-sm font-medium text-foreground\">Couldn't open this file</h3>\n <p className=\"mt-1 max-w-md text-xs text-muted-foreground\">{message}</p>\n </div>\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"inline-flex h-8 items-center rounded-md border border-border px-3 text-xs font-medium text-foreground transition-colors hover:bg-muted\"\n >\n Retry\n </button>\n </div>\n )\n}\n\nfunction TreeErrorState({ message, onRetry }: { message: string; onRetry: () => void }) {\n return (\n <div role=\"alert\" className=\"flex h-full flex-col items-center justify-center gap-3 p-6 text-center\">\n <div>\n <h3 className=\"text-sm font-medium text-foreground\">Couldn't load the Vault</h3>\n <p className=\"mt-1 max-w-xs text-xs text-muted-foreground\">{message}</p>\n </div>\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"inline-flex h-8 items-center rounded-md border border-border px-3 text-xs font-medium text-foreground transition-colors hover:bg-muted\"\n >\n Retry\n </button>\n </div>\n )\n}\n\nfunction OperationErrorAlert({\n message,\n retryLabel,\n onRetry,\n onDismiss,\n}: {\n message: string\n retryLabel: string\n onRetry: () => void\n onDismiss: () => void\n}) {\n return (\n <div\n role=\"alert\"\n className=\"flex shrink-0 items-center justify-between gap-3 border-b border-destructive/30 bg-destructive/10 px-3 py-2 text-xs text-destructive\"\n >\n <span className=\"min-w-0 flex-1\">{message}</span>\n <div className=\"flex shrink-0 items-center gap-3\">\n <button type=\"button\" aria-label={retryLabel} onClick={onRetry} className=\"font-medium underline-offset-2 hover:underline\">\n Retry\n </button>\n <button type=\"button\" onClick={onDismiss} className=\"underline-offset-2 hover:underline\">\n Dismiss\n </button>\n </div>\n </div>\n )\n}\n\nexport function VaultPane(props: VaultPaneProps) {\n const {\n port,\n renderTree,\n renderArtifact,\n renderDock,\n canWrite = true,\n selectedPath: controlledPath,\n onSelectedPathChange,\n onOperationError,\n codec,\n className,\n dockToggle,\n refreshKey,\n headerActions,\n onDownloadFile,\n pathBarClassName,\n } = props\n\n const activeCodec = codec ?? IDENTITY_CODEC\n const controlled = controlledPath !== undefined\n const isMarkdownCapable = codec !== undefined\n // `false` → a persistent dock (no toggle, always open with the selected file).\n const persistentDock = dockToggle === false\n const dockToggleCfg = dockToggle ? dockToggle : { label: 'Discuss', disabledWhenDirty: true }\n\n const [tree, setTree] = useState<VaultTreeNode[]>([])\n const [treeLoading, setTreeLoading] = useState(true)\n const [treeLoaded, setTreeLoaded] = useState(false)\n const [treeError, setTreeError] = useState<TreeFailureState | null>(null)\n const [internalPath, setInternalPath] = useState<string | null>(null)\n const selectedPath = controlled ? (controlledPath ?? null) : internalPath\n\n const [selectedFile, setSelectedFile] = useState<VaultFile | null>(null)\n const [fileLoading, setFileLoading] = useState(false)\n const [readError, setReadError] = useState<string | null>(null)\n const [reloadNonce, setReloadNonce] = useState(0)\n const [saving, setSaving] = useState(false)\n const [saveError, setSaveError] = useState<VaultOperationFailure | null>(null)\n\n const [editorMode, setEditorMode] = useState<VaultEditorMode>('rich')\n const [richDraft, setRichDraft] = useState<VaultRichParts>('')\n const [sourceDraft, setSourceDraft] = useState('')\n const [isDirty, setIsDirty] = useState(false)\n\n const [createOpen, setCreateOpen] = useState(false)\n const [newPath, setNewPath] = useState('')\n const [creating, setCreating] = useState(false)\n const [createError, setCreateError] = useState<VaultOperationFailure | null>(null)\n const [deleteOpen, setDeleteOpen] = useState(false)\n const [deleting, setDeleting] = useState(false)\n const [deleteError, setDeleteError] = useState<VaultOperationFailure | null>(null)\n const [dockOpen, setDockOpen] = useState(false)\n const [pendingNav, setPendingNav] = useState<PendingNav>(null)\n const [query, setQuery] = useState('')\n const [folderPath, setFolderPath] = useState<string | null>(null)\n\n const savedContentRef = useRef('')\n const loadedPathRef = useRef<string | null>(null)\n const onOperationErrorRef = useRef(onOperationError)\n onOperationErrorRef.current = onOperationError\n\n const treePaths = useMemo(\n () => collectTreePaths(tree, { files: new Set<string>(), directories: new Set<string>() }),\n [tree],\n )\n const filePaths = treePaths.files\n const resolvedSelectedPath = useMemo(\n () => selectedPath ? resolveFilePath(selectedPath, filePaths) : null,\n [selectedPath, filePaths],\n )\n // The clicked folder, resolved against the CURRENT tree — a folder that a\n // refresh removed stops being the active one on its own, so neither the\n // search scope nor the create target can point at a directory that is gone.\n const activeFolderNode = useMemo(\n () => (folderPath ? findDirectory(tree, folderPath) : null),\n [tree, folderPath],\n )\n const activeFolder = activeFolderNode?.path ?? null\n const treeRoot = useMemo<VaultTreeNode>(\n () => ({ name: 'Vault', path: '', type: 'directory', children: tree }),\n [tree],\n )\n // With no query the whole vault stays on screen: the tree renderer owns\n // expansion (both sandbox-ui trees keep it internal), so re-rooting on a\n // plain folder click would fight the expand the click already performs. The\n // folder scopes the SEARCH, which is where a narrowed list is what the reader\n // asked for.\n const visibleRoot = useMemo<VaultTreeNode>(() => {\n const q = query.trim().toLowerCase()\n if (!q) return treeRoot\n const base = activeFolderNode ?? treeRoot\n return { ...base, children: filterNodes(base.children ?? [], q) }\n }, [treeRoot, activeFolderNode, query])\n\n const commitPath = useCallback(\n (next: string | null) => {\n if (!controlled) setInternalPath(next)\n onSelectedPathChange?.(next)\n },\n [controlled, onSelectedPathChange],\n )\n\n const reportFailure = useCallback((\n operation: VaultOperation,\n phase: VaultOperationPhase,\n error: unknown,\n fallback: string,\n path?: string,\n ): VaultOperationFailure => {\n const failure: VaultOperationFailure = {\n operation,\n phase,\n path,\n message: operationMessage(error, fallback),\n cause: error,\n }\n try {\n onOperationErrorRef.current?.(failure)\n } catch (callbackError) {\n console.error('Vault onOperationError callback failed:', callbackError)\n }\n return failure\n }, [])\n\n const refresh = useCallback(async (context: TreeRefreshContext = LIST_CONTEXT): Promise<boolean> => {\n setTreeLoading(true)\n setTreeError(null)\n try {\n setTree(await port.listTree())\n setTreeLoaded(true)\n return true\n } catch (error) {\n const failure = reportFailure(\n context.operation,\n context.phase,\n error,\n 'Failed to load the Vault',\n context.path,\n )\n setTreeError({ failure, context })\n return false\n } finally {\n setTreeLoading(false)\n }\n }, [port, reportFailure])\n\n useEffect(() => {\n setTree([])\n setTreeLoaded(false)\n setTreeError(null)\n }, [port])\n\n useEffect(() => {\n void refresh()\n }, [refresh, refreshKey])\n\n const retryTree = useCallback(async () => {\n if (!treeError) return\n const { context } = treeError\n const recovered = await refresh(context)\n if (recovered && context.selectAfterRecovery) commitPath(context.selectAfterRecovery)\n }, [treeError, refresh, commitPath])\n\n useEffect(() => {\n if (!selectedPath) {\n setSelectedFile(null)\n setFileLoading(false)\n setReadError(null)\n loadedPathRef.current = null\n return\n }\n if (treeLoading || !treeLoaded) return\n if (!resolvedSelectedPath) {\n commitPath(null)\n setSelectedFile(null)\n setFileLoading(false)\n setReadError(null)\n loadedPathRef.current = null\n return\n }\n let cancelled = false\n const path = resolvedSelectedPath\n if (path !== selectedPath) commitPath(path)\n setFileLoading(true)\n setReadError(null)\n void (async () => {\n try {\n const file = await port.readFile(path)\n if (!cancelled) setSelectedFile(file)\n } catch (err) {\n // Surface read failures instead of making them indistinguishable from\n // the intentionally empty \"no file selected\" state.\n if (!cancelled) {\n const failure = reportFailure('read', 'operation', err, 'Failed to read file', path)\n setSelectedFile(null)\n setReadError(failure.message)\n }\n } finally {\n if (!cancelled) setFileLoading(false)\n }\n })()\n return () => {\n cancelled = true\n }\n }, [port, selectedPath, resolvedSelectedPath, treeLoading, treeLoaded, refreshKey, reloadNonce, commitPath, reportFailure])\n\n useEffect(() => {\n if (!selectedFile) {\n loadedPathRef.current = null\n savedContentRef.current = ''\n setRichDraft('')\n setSourceDraft('')\n setEditorMode('rich')\n setIsDirty(false)\n setSaveError(null)\n return\n }\n const pathChanged = loadedPathRef.current !== selectedFile.path\n loadedPathRef.current = selectedFile.path\n savedContentRef.current = selectedFile.content\n setRichDraft(activeCodec.parse(selectedFile.content))\n setSourceDraft(selectedFile.content)\n if (pathChanged) setEditorMode('rich')\n setIsDirty(false)\n setSaveError(null)\n setDockOpen(false)\n }, [selectedFile, activeCodec])\n\n const guardedOpen = useCallback(\n (path: string) => {\n if (path === selectedPath) return\n if (isDirty) {\n setPendingNav({ type: 'open', path })\n return\n }\n commitPath(path)\n },\n [isDirty, selectedPath, commitPath],\n )\n\n // Clicking a folder makes it the vault's active folder: the search narrows to\n // it and a new file lands inside it. Clicking it again clears that — the same\n // row is the way back out, so the gesture is reversible where it was made.\n const toggleFolder = useCallback((path: string) => {\n setFolderPath((current) => (current === path ? null : path))\n }, [])\n\n // Some tree models keep their original selection callback while resetting\n // paths internally. Keep the callable stable, but have it execute the latest\n // path validation and dirty-guard logic.\n const selectFileRef = useRef<(path: string) => void>(() => {})\n selectFileRef.current = (rawPath: string) => {\n const target = resolveTreePath(rawPath, treePaths)\n if (!target) return\n if (target.type === 'file') {\n guardedOpen(target.path)\n return\n }\n toggleFolder(target.path)\n }\n const handleTreeSelect = useCallback((path: string) => selectFileRef.current(path), [])\n\n const guardedClose = useCallback(() => {\n if (isDirty) {\n setPendingNav({ type: 'close' })\n return\n }\n commitPath(null)\n setSelectedFile(null)\n }, [isDirty, commitPath])\n\n const confirmDiscard = useCallback(() => {\n const nav = pendingNav\n setPendingNav(null)\n setIsDirty(false)\n if (!nav) return\n if (nav.type === 'open') {\n commitPath(nav.path)\n } else {\n commitPath(null)\n setSelectedFile(null)\n }\n }, [pendingNav, commitPath])\n\n const showRichMode = useCallback(() => {\n setEditorMode((mode) => {\n if (mode === 'rich') return mode\n setRichDraft(activeCodec.parse(sourceDraft))\n setIsDirty(sourceDraft !== savedContentRef.current)\n return 'rich'\n })\n }, [activeCodec, sourceDraft])\n\n const showSourceMode = useCallback(() => {\n setEditorMode((mode) => {\n if (mode === 'source') return mode\n const content = isDirty ? activeCodec.serialize(richDraft) : savedContentRef.current\n setSourceDraft(content)\n setIsDirty(content !== savedContentRef.current)\n return 'source'\n })\n }, [activeCodec, isDirty, richDraft])\n\n const onSourceChange = useCallback((next: string) => {\n setSourceDraft(next)\n setIsDirty(next !== savedContentRef.current)\n }, [])\n\n const onRichChange = useCallback((next: VaultRichParts) => {\n setRichDraft(next)\n setIsDirty(activeCodec.serialize(next) !== savedContentRef.current)\n }, [activeCodec])\n\n const saveCurrent = useCallback(async () => {\n if (!selectedFile) return\n const content = editorMode === 'source' ? sourceDraft : activeCodec.serialize(richDraft)\n setSaving(true)\n setSaveError(null)\n try {\n await port.writeFile(selectedFile.path, content)\n savedContentRef.current = content\n setSelectedFile({ ...selectedFile, content })\n setSourceDraft(content)\n setRichDraft(activeCodec.parse(content))\n setIsDirty(false)\n } catch (error) {\n setSaveError(reportFailure('save', 'operation', error, 'Failed to save file', selectedFile.path))\n } finally {\n setSaving(false)\n }\n }, [selectedFile, editorMode, sourceDraft, richDraft, activeCodec, port, reportFailure])\n\n const handleCreate = useCallback(async () => {\n const trimmed = newPath.trim()\n // Same rule as the confirm button, enforced here too: the dialog can also be\n // confirmed by keyboard, and a directory path is not a file to create.\n if (!trimmed || !(trimmed.split('/').pop()?.trim())) return\n setCreating(true)\n setCreateError(null)\n try {\n const created = await port.createFile(trimmed)\n setCreateOpen(false)\n setNewPath('')\n const refreshed = await refresh({\n operation: 'create',\n phase: 'post-mutation-refresh',\n path: created,\n selectAfterRecovery: created,\n })\n if (refreshed) commitPath(created)\n } catch (error) {\n setCreateError(reportFailure('create', 'operation', error, 'Failed to create file', trimmed))\n } finally {\n setCreating(false)\n }\n }, [newPath, port, refresh, commitPath, reportFailure])\n\n const handleDelete = useCallback(async () => {\n if (!selectedFile) return\n const path = selectedFile.path\n setDeleting(true)\n setDeleteError(null)\n try {\n await port.deleteFile(path)\n setDeleteOpen(false)\n setIsDirty(false)\n commitPath(null)\n setSelectedFile(null)\n await refresh({ operation: 'delete', phase: 'post-mutation-refresh', path })\n } catch (error) {\n setDeleteError(reportFailure('delete', 'operation', error, 'Failed to delete file', path))\n } finally {\n setDeleting(false)\n }\n }, [selectedFile, port, refresh, commitPath, reportFailure])\n\n const createFileName = newPath.trim().split('/').pop()?.trim() ?? ''\n\n let treeContent: ReactNode\n if (treeLoading || (!treeLoaded && !treeError)) {\n treeContent = <TreeSkeleton />\n } else if (!treeLoaded && treeError) {\n treeContent = <TreeErrorState message={treeFailureMessage(treeError.failure)} onRetry={() => void retryTree()} />\n } else {\n treeContent = (\n <>\n {treeError && (\n <OperationErrorAlert\n message={treeFailureMessage(treeError.failure)}\n retryLabel=\"Retry vault refresh\"\n onRetry={() => void retryTree()}\n onDismiss={() => setTreeError(null)}\n />\n )}\n {renderTree({\n root: visibleRoot,\n selectedPath: resolvedSelectedPath ?? undefined,\n onSelect: handleTreeSelect,\n })}\n </>\n )\n }\n\n return (\n <EditorErrorBoundary onReset={() => { commitPath(null); setSelectedFile(null) }}>\n <div className={`flex min-h-0 flex-1 overflow-hidden ${className ?? ''}`}>\n <div className=\"flex w-[23rem] min-w-[23rem] flex-col border-r border-border bg-background\">\n <div className=\"flex items-center gap-2 border-b border-border px-4 py-3\">\n <div className=\"min-w-0 flex-1\">\n <input\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={activeFolder ? `Search ${activeFolder}…` : 'Search…'}\n aria-label=\"Search vault\"\n className=\"h-8 w-full rounded-md border border-border bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground\"\n />\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n {headerActions}\n <button\n type=\"button\"\n aria-label=\"Refresh vault\"\n onClick={() => void refresh()}\n className=\"inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n ↻\n </button>\n {canWrite && (\n <button\n type=\"button\"\n aria-label={activeFolder ? `New vault file in ${activeFolder}` : 'New vault file'}\n onClick={() => { setCreateError(null); setNewPath(activeFolder ? `${activeFolder}/` : ''); setCreateOpen(true) }}\n className=\"inline-flex h-8 w-8 items-center justify-center rounded-md bg-primary text-primary-foreground transition-colors hover:bg-primary/90\"\n >\n +\n </button>\n )}\n </div>\n </div>\n {activeFolder && (\n <div className=\"flex items-center gap-1.5 border-b border-border bg-muted/40 px-4 py-1.5 text-xs\">\n <Folder className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" aria-hidden=\"true\" />\n <span data-vault-folder className=\"min-w-0 flex-1 truncate font-medium text-foreground\" title={activeFolder}>\n {activeFolder}\n </span>\n <button\n type=\"button\"\n aria-label=\"Clear the active folder\"\n onClick={() => setFolderPath(null)}\n className=\"shrink-0 rounded px-1 text-muted-foreground underline-offset-2 transition-colors hover:text-foreground hover:underline\"\n >\n Clear\n </button>\n </div>\n )}\n <div\n className=\"flex-1 overflow-y-auto\"\n onClickCapture={(event) => {\n const target = treeClickTarget(event)\n if (target) handleTreeSelect(target.path)\n }}\n >\n {treeContent}\n </div>\n </div>\n\n <div className=\"flex min-w-0 flex-1 flex-col overflow-hidden\">\n {selectedFile && (\n <div className={`flex shrink-0 items-center justify-between border-b border-border px-4 py-1.5 ${pathBarClassName ?? 'bg-card'}`}>\n <span data-vault-path className=\"truncate text-xs font-medium text-foreground\">{selectedFile.path}</span>\n <div className=\"flex items-center gap-1\">\n {canWrite && isMarkdownCapable && (\n <div className=\"mr-1 flex items-center gap-1\">\n <button\n type=\"button\"\n aria-label=\"Edit as rich text\"\n aria-pressed={editorMode === 'rich'}\n onClick={showRichMode}\n className={`inline-flex h-7 items-center rounded px-2 text-xs transition-colors ${\n editorMode === 'rich'\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n Rich\n </button>\n <button\n type=\"button\"\n aria-label=\"Edit as source\"\n aria-pressed={editorMode === 'source'}\n onClick={showSourceMode}\n className={`inline-flex h-7 items-center rounded px-2 text-xs transition-colors ${\n editorMode === 'source'\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n Source\n </button>\n </div>\n )}\n {renderDock && !persistentDock && (\n <button\n type=\"button\"\n aria-label={dockToggleCfg.label}\n aria-pressed={dockOpen}\n disabled={(dockToggleCfg.disabledWhenDirty ?? true) && isDirty}\n title={(dockToggleCfg.disabledWhenDirty ?? true) && isDirty ? 'Save your changes first' : (dockToggleCfg.title ?? dockToggleCfg.label)}\n onClick={() => setDockOpen((v) => !v)}\n className={`inline-flex items-center gap-1 rounded px-2 py-0.5 text-xs transition-colors disabled:pointer-events-none disabled:opacity-40 ${\n dockOpen\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted hover:text-foreground'\n }`}\n >\n {dockToggleCfg.label}\n </button>\n )}\n {onDownloadFile && (\n <button\n type=\"button\"\n aria-label=\"Download this file\"\n title=\"Download file\"\n onClick={() => onDownloadFile(selectedFile)}\n className=\"inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n <Download className=\"h-4 w-4\" />\n </button>\n )}\n {canWrite && (\n <button\n type=\"button\"\n aria-label=\"Delete this file\"\n title=\"Delete file\"\n onClick={() => { setDeleteError(null); setDeleteOpen(true) }}\n className=\"inline-flex h-7 w-7 items-center justify-center rounded text-destructive/70 transition-colors hover:bg-destructive/10 hover:text-destructive\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n )}\n </div>\n </div>\n )}\n {selectedFile && saveError && (\n <OperationErrorAlert\n message={saveError.message}\n retryLabel=\"Retry save\"\n onRetry={() => void saveCurrent()}\n onDismiss={() => setSaveError(null)}\n />\n )}\n <div className=\"flex-1 overflow-hidden\">\n {fileLoading ? (\n <EditorSkeleton />\n ) : readError ? (\n <ReadErrorState message={readError} onRetry={() => setReloadNonce((n) => n + 1)} />\n ) : selectedFile && canWrite && isMarkdownCapable && editorMode === 'source' ? (\n <SourceEditor\n path={selectedFile.path}\n content={sourceDraft}\n saving={saving}\n dirty={isDirty}\n onChange={onSourceChange}\n onSave={() => void saveCurrent()}\n />\n ) : selectedFile ? (\n renderArtifact({\n file: selectedFile,\n loading: false,\n mode: editorMode,\n canWrite,\n richDraft,\n dirty: isDirty,\n onRichChange,\n onSave: () => void saveCurrent(),\n })\n ) : (\n <EmptyState />\n )}\n </div>\n </div>\n\n {renderDock && selectedFile && renderDock({\n file: selectedFile,\n open: persistentDock ? true : dockOpen,\n onClose: persistentDock ? () => {} : () => setDockOpen(false),\n })}\n\n <ConfirmDialog\n open={createOpen}\n title=\"Create vault file\"\n description={activeFolder ? `Add a new document to ${activeFolder}.` : 'Add a new document to this vault.'}\n confirmLabel={creating ? 'Creating…' : 'Create'}\n // A prefilled folder is a path with no file name yet, so emptiness is\n // not the test — `folder/` would otherwise be sent to the port as a\n // file to create.\n confirmDisabled={creating || !createFileName}\n onConfirm={() => void handleCreate()}\n onCancel={() => { setCreateOpen(false); setNewPath(''); setCreateError(null) }}\n >\n <div className=\"space-y-2\">\n <input\n value={newPath}\n autoFocus\n onChange={(e) => setNewPath(e.target.value)}\n placeholder=\"e.g. playbooks/new-strategy.md\"\n aria-label=\"New file path\"\n className=\"h-9 w-full rounded-md border border-border bg-background px-3 text-sm text-foreground placeholder:text-muted-foreground\"\n />\n {createError && <p role=\"alert\" className=\"text-xs text-destructive\">{createError.message}</p>}\n </div>\n </ConfirmDialog>\n\n <ConfirmDialog\n open={deleteOpen}\n title=\"Delete file?\"\n description={`This permanently removes ${selectedFile?.path ?? 'this file'} from the vault.`}\n confirmLabel={deleting ? 'Deleting…' : 'Delete file'}\n confirmDisabled={deleting}\n destructive\n onConfirm={() => void handleDelete()}\n onCancel={() => { setDeleteOpen(false); setDeleteError(null) }}\n >\n {deleteError && <p role=\"alert\" className=\"text-xs text-destructive\">{deleteError.message}</p>}\n </ConfirmDialog>\n\n <ConfirmDialog\n open={pendingNav !== null}\n title=\"Discard unsaved changes?\"\n description=\"Your edits to this document haven't been saved. Continue and lose them?\"\n confirmLabel=\"Discard changes\"\n destructive\n onConfirm={confirmDiscard}\n onCancel={() => setPendingNav(null)}\n />\n </div>\n </EditorErrorBoundary>\n )\n}\n\nfunction SourceEditor({\n path,\n content,\n saving,\n dirty,\n onChange,\n onSave,\n}: {\n path: string\n content: string\n saving: boolean\n dirty: boolean\n onChange: (content: string) => void\n onSave: () => void\n}) {\n return (\n <div className=\"flex h-full min-h-0 flex-col bg-background\">\n <div className=\"flex items-center justify-between gap-2 border-b border-border px-4 py-2\">\n <p className=\"truncate font-mono text-xs text-muted-foreground\">{path}</p>\n <div className=\"flex shrink-0 items-center gap-2\">\n <span className=\"rounded-full border border-border bg-background px-2.5 py-1 text-xs text-muted-foreground\">\n {dirty ? 'Unsaved changes' : 'Saved'}\n </span>\n <button\n type=\"button\"\n onClick={onSave}\n disabled={saving || !dirty}\n className=\"inline-flex h-7 items-center rounded-md bg-primary px-2 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50\"\n >\n {saving ? 'Saving…' : 'Save'}\n </button>\n </div>\n </div>\n <textarea\n value={content}\n onChange={(event) => onChange(event.target.value)}\n spellCheck={false}\n aria-label=\"Source editor\"\n // Full-bleed editor: keep the floor's ring but draw it inside the border\n // box, since an outward ring on a `flex-1` pane child is clipped.\n className=\"min-h-0 flex-1 resize-none border-0 bg-background p-4 font-mono text-sm leading-6 text-foreground focus-visible:[outline-offset:-2px]\"\n />\n </div>\n )\n}\n","/**\n * Self-contained confirm dialog — no dialog library. A focus-trapped modal with\n * Esc-to-cancel and Enter-to-confirm, used for create / delete / discard-unsaved\n * flows so the pane carries zero UI-kit dependency.\n */\n\nimport { useEffect, useRef, type ReactNode } from 'react'\n\nexport interface ConfirmDialogProps {\n open: boolean\n title: string\n description?: ReactNode\n confirmLabel?: string\n cancelLabel?: string\n /** Styles the confirm button as a destructive action. */\n destructive?: boolean\n /** Disables the confirm button (e.g. while the action is in flight). */\n confirmDisabled?: boolean\n onConfirm: () => void\n onCancel: () => void\n /** Optional body (e.g. an input field for the create flow). */\n children?: ReactNode\n}\n\nexport function ConfirmDialog({\n open,\n title,\n description,\n confirmLabel = 'Confirm',\n cancelLabel = 'Cancel',\n destructive = false,\n confirmDisabled = false,\n onConfirm,\n onCancel,\n children,\n}: ConfirmDialogProps) {\n const panelRef = useRef<HTMLDivElement>(null)\n const confirmRef = useRef<HTMLButtonElement>(null)\n\n useEffect(() => {\n if (!open) return\n confirmRef.current?.focus()\n }, [open])\n\n if (!open) return null\n\n function onKeyDown(event: React.KeyboardEvent) {\n if (event.key === 'Escape') {\n event.preventDefault()\n onCancel()\n return\n }\n if (event.key === 'Enter' && !confirmDisabled) {\n event.preventDefault()\n onConfirm()\n return\n }\n if (event.key !== 'Tab') return\n const focusable = panelRef.current?.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), input:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n )\n if (!focusable || focusable.length === 0) return\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n const active = document.activeElement as HTMLElement | null\n if (event.shiftKey && active === first) {\n event.preventDefault()\n last?.focus()\n } else if (!event.shiftKey && active === last) {\n event.preventDefault()\n first?.focus()\n }\n }\n\n return (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4\"\n onMouseDown={(e) => { if (e.target === e.currentTarget) onCancel() }}\n >\n <div\n ref={panelRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n onKeyDown={onKeyDown}\n className=\"w-full max-w-md rounded-lg border border-border bg-card p-5 shadow-lg\"\n >\n <h2 className=\"text-sm font-semibold text-foreground\">{title}</h2>\n {description && <p className=\"mt-1.5 text-xs text-muted-foreground\">{description}</p>}\n {children && <div className=\"mt-3\">{children}</div>}\n <div className=\"mt-5 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"inline-flex h-8 items-center rounded-md px-3 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\"\n >\n {cancelLabel}\n </button>\n <button\n ref={confirmRef}\n type=\"button\"\n onClick={onConfirm}\n disabled={confirmDisabled}\n className={`inline-flex h-8 items-center rounded-md px-3 text-xs font-medium transition-colors disabled:pointer-events-none disabled:opacity-50 ${\n destructive\n ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90'\n : 'bg-primary text-primary-foreground hover:bg-primary/90'\n }`}\n >\n {confirmLabel}\n </button>\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":";AAcA;AAAA,EACE;AAAA,EACA;AAAA,EACA,aAAAA;AAAA,EACA;AAAA,EACA,UAAAC;AAAA,EACA;AAAA,OAIK;AACP,SAAS,UAAU,QAAQ,cAAc;;;ACnBzC,SAAS,WAAW,cAA8B;AAiF1C,cAGA,YAHA;AA/DD,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,cAAc;AAAA,EACd,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,WAAW,OAAuB,IAAI;AAC5C,QAAM,aAAa,OAA0B,IAAI;AAEjD,YAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,eAAW,SAAS,MAAM;AAAA,EAC5B,GAAG,CAAC,IAAI,CAAC;AAET,MAAI,CAAC,KAAM,QAAO;AAElB,WAAS,UAAU,OAA4B;AAC7C,QAAI,MAAM,QAAQ,UAAU;AAC1B,YAAM,eAAe;AACrB,eAAS;AACT;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,WAAW,CAAC,iBAAiB;AAC7C,YAAM,eAAe;AACrB,gBAAU;AACV;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,MAAO;AACzB,UAAM,YAAY,SAAS,SAAS;AAAA,MAClC;AAAA,IACF;AACA,QAAI,CAAC,aAAa,UAAU,WAAW,EAAG;AAC1C,UAAM,QAAQ,UAAU,CAAC;AACzB,UAAM,OAAO,UAAU,UAAU,SAAS,CAAC;AAC3C,UAAM,SAAS,SAAS;AACxB,QAAI,MAAM,YAAY,WAAW,OAAO;AACtC,YAAM,eAAe;AACrB,YAAM,MAAM;AAAA,IACd,WAAW,CAAC,MAAM,YAAY,WAAW,MAAM;AAC7C,YAAM,eAAe;AACrB,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,aAAa,CAAC,MAAM;AAAE,YAAI,EAAE,WAAW,EAAE,cAAe,UAAS;AAAA,MAAE;AAAA,MAEnE;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,MAAK;AAAA,UACL,cAAW;AAAA,UACX,cAAY;AAAA,UACZ;AAAA,UACA,WAAU;AAAA,UAEV;AAAA,gCAAC,QAAG,WAAU,yCAAyC,iBAAM;AAAA,YAC5D,eAAe,oBAAC,OAAE,WAAU,wCAAwC,uBAAY;AAAA,YAChF,YAAY,oBAAC,SAAI,WAAU,QAAQ,UAAS;AAAA,YAC7C,qBAAC,SAAI,WAAU,+BACb;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,WAAU;AAAA,kBAET;AAAA;AAAA,cACH;AAAA,cACA;AAAA,gBAAC;AAAA;AAAA,kBACC,KAAK;AAAA,kBACL,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,UAAU;AAAA,kBACV,WAAW,uIACT,cACI,uEACA,wDACN;AAAA,kBAEC;AAAA;AAAA,cACH;AAAA,eACF;AAAA;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;;;ADoDQ,SA+BJ,UA9BM,OAAAC,MADF,QAAAC,aAAA;AAhIR,IAAM,iBAAqC;AAAA,EACzC,OAAO,CAAC,QAAQ;AAAA,EAChB,WAAW,CAAC,UAAW,OAAO,UAAU,WAAW,QAAQ,OAAO,SAAS,EAAE;AAC/E;AAgBA,IAAM,eAAmC,EAAE,WAAW,QAAQ,OAAO,YAAY;AAEjF,SAAS,iBAAiB,OAAgB,UAA0B;AAClE,SAAO,iBAAiB,SAAS,MAAM,UAAU,MAAM,UAAU;AACnE;AAEA,SAAS,mBAAmB,SAAwC;AAClE,MAAI,QAAQ,UAAU,wBAAyB,QAAO,QAAQ;AAC9D,QAAM,YAAY,QAAQ,cAAc,WAAW,YAAY;AAC/D,SAAO,gBAAgB,SAAS,qCAAqC,QAAQ,OAAO;AACtF;AAWA,SAAS,iBAAiB,OAAwB,MAA4B;AAC5E,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,OAAQ,MAAK,MAAM,IAAI,KAAK,IAAI;AAAA,QAC7C,MAAK,YAAY,IAAI,KAAK,IAAI;AACnC,QAAI,KAAK,SAAU,kBAAiB,KAAK,UAAU,IAAI;AAAA,EACzD;AACA,SAAO;AACT;AAEA,SAAS,gBAAgB,SAAiB,WAAuC;AAC/E,MAAI,UAAU,IAAI,OAAO,EAAG,QAAO;AACnC,QAAM,OAAO,QAAQ,QAAQ,cAAc,EAAE;AAC7C,SAAO,UAAU,IAAI,IAAI,IAAI,OAAO;AACtC;AAEA,SAAS,gBAAgB,SAAiB,OAAuE;AAC/G,QAAM,OAAO,gBAAgB,SAAS,MAAM,KAAK;AACjD,MAAI,KAAM,QAAO,EAAE,MAAM,MAAM,MAAM,OAAO;AAC5C,MAAI,MAAM,YAAY,IAAI,OAAO,EAAG,QAAO,EAAE,MAAM,SAAS,MAAM,YAAY;AAC9E,QAAM,UAAU,QAAQ,QAAQ,cAAc,EAAE;AAChD,SAAO,MAAM,YAAY,IAAI,OAAO,IAAI,EAAE,MAAM,SAAS,MAAM,YAAY,IAAI;AACjF;AAEA,SAAS,cAAc,OAAwB,MAAoC;AACjF,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,eAAe,KAAK,SAAS,KAAM,QAAO;AAC5D,UAAM,QAAQ,KAAK,WAAW,cAAc,KAAK,UAAU,IAAI,IAAI;AACnE,QAAI,MAAO,QAAO;AAAA,EACpB;AACA,SAAO;AACT;AAKA,SAAS,gBAAgB,OAAuE;AAC9F,QAAM,OAAO,CAAC,OAAoB;AAChC,UAAMC,QAAO,GAAG,QAAQ;AACxB,WAAOA,QAAO,EAAE,MAAAA,OAAM,MAAM,GAAG,QAAQ,YAAY,GAAG,IAAI;AAAA,EAC5D;AAEA,QAAM,OAAO,MAAM,YAAY,eAAe,KAAK,CAAC;AACpD,aAAW,QAAQ,MAAM;AACvB,QAAI,EAAE,gBAAgB,aAAc;AACpC,QAAI,KAAK,QAAQ,SAAS,OAAQ;AAClC,WAAO,KAAK,IAAI;AAAA,EAClB;AAEA,QAAM,SAAS,MAAM,kBAAkB,cACnC,MAAM,OAAO,QAAQ,oBAAoB,IACzC;AACJ,SAAO,kBAAkB,cAAc,KAAK,MAAM,IAAI;AACxD;AAKA,SAAS,YAAY,OAAwB,GAA4B;AACvE,QAAM,MAAuB,CAAC;AAC9B,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,SAAS,QAAQ;AACxB,UAAI,KAAK,KAAK,YAAY,EAAE,SAAS,CAAC,EAAG,KAAI,KAAK,IAAI;AAAA,IACxD,WAAW,KAAK,KAAK,YAAY,EAAE,SAAS,CAAC,GAAG;AAC9C,UAAI,KAAK,IAAI;AAAA,IACf,OAAO;AACL,YAAM,WAAW,YAAY,KAAK,YAAY,CAAC,GAAG,CAAC;AACnD,UAAI,SAAS,SAAS,EAAG,KAAI,KAAK,EAAE,GAAG,MAAM,SAAS,CAAC;AAAA,IACzD;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAM,sBAAN,cAAkC,UAA6E;AAAA,EAC7G,QAA4B,EAAE,OAAO,KAAK;AAAA,EAC1C,OAAO,yBAAyB,OAAgB;AAC9C,WAAO,EAAE,MAAM;AAAA,EACjB;AAAA,EACA,kBAAkB,OAAgB,MAAiB;AACjD,YAAQ,MAAM,kBAAkB,OAAO,IAAI;AAAA,EAC7C;AAAA,EACA,SAAS;AACP,QAAI,KAAK,MAAM,OAAO;AACpB,YAAM,MAAM,KAAK,MAAM,iBAAiB,QACpC,KAAK,MAAM,MAAM,UACjB,OAAO,KAAK,MAAM,UAAU,WAC1B,KAAK,MAAM,QACX;AACN,aACE,gBAAAD,MAAC,SAAI,WAAU,2EACb;AAAA,wBAAAD,KAAC,QAAG,WAAU,4CAA2C,kCAAoB;AAAA,QAC7E,gBAAAA,KAAC,OAAE,WAAU,+CAA+C,iBAAO,GAAG,GAAE;AAAA,QACxE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM;AAAE,mBAAK,SAAS,EAAE,OAAO,KAAK,CAAC;AAAG,mBAAK,MAAM,UAAU;AAAA,YAAE;AAAA,YACxE,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,SACF;AAAA,IAEJ;AACA,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;AAUA,SAAS,eAAe,EAAE,OAAO,WAAW,SAAS,GAA8D;AAKjH,SACE,gBAAAC,MAAA,YACE;AAAA,oBAAAD,KAAC,UAAK,MAAK,UAAS,aAAU,UAAS,aAAW,MAAM,WAAU,WAC/D,iBACH;AAAA,IACA,gBAAAA,KAAC,SAAI,WAAsB,eAAY,QACpC,UACH;AAAA,KACF;AAEJ;AAEA,SAAS,eAAe;AACtB,SACE,gBAAAA,KAAC,kBAAe,OAAM,uBAAiB,WAAU,iBAC9C,WAAC,IAAI,IAAI,IAAI,EAAE,EAAE,IAAI,CAAC,GAAG,MACxB,gBAAAA,KAAC,SAAY,WAAU,sCAAqC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC,KAAK,KAA/E,CAAkF,CAC7F,GACH;AAEJ;AAEA,SAAS,iBAAiB;AACxB,SACE,gBAAAC,MAAC,kBAAe,OAAM,sBAAgB,WAAU,iBAC9C;AAAA,oBAAAD,KAAC,SAAI,WAAU,4CAA2C;AAAA,IAC1D,gBAAAA,KAAC,SAAI,WAAU,6CAA4C;AAAA,IAC3D,gBAAAA,KAAC,SAAI,WAAU,4CAA2C;AAAA,IAC1D,gBAAAA,KAAC,SAAI,WAAU,4CAA2C;AAAA,KAC5D;AAEJ;AAEA,SAAS,aAAa;AACpB,SACE,gBAAAC,MAAC,SAAI,WAAU,oEACb;AAAA,oBAAAD,KAAC,QAAG,WAAU,uCAAsC,mCAAqB;AAAA,IACzE,gBAAAA,KAAC,OAAE,WAAU,+CAA8C,oEAE3D;AAAA,KACF;AAEJ;AAEA,SAAS,eAAe,EAAE,SAAS,QAAQ,GAA6C;AACtF,SACE,gBAAAC,MAAC,SAAI,WAAU,0EACb;AAAA,oBAAAA,MAAC,SACC;AAAA,sBAAAD,KAAC,QAAG,WAAU,uCAAsC,qCAAuB;AAAA,MAC3E,gBAAAA,KAAC,OAAE,WAAU,+CAA+C,mBAAQ;AAAA,OACtE;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAU;AAAA,QACX;AAAA;AAAA,IAED;AAAA,KACF;AAEJ;AAEA,SAAS,eAAe,EAAE,SAAS,QAAQ,GAA6C;AACtF,SACE,gBAAAC,MAAC,SAAI,MAAK,SAAQ,WAAU,0EAC1B;AAAA,oBAAAA,MAAC,SACC;AAAA,sBAAAD,KAAC,QAAG,WAAU,uCAAsC,qCAAuB;AAAA,MAC3E,gBAAAA,KAAC,OAAE,WAAU,+CAA+C,mBAAQ;AAAA,OACtE;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAU;AAAA,QACX;AAAA;AAAA,IAED;AAAA,KACF;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MAEV;AAAA,wBAAAD,KAAC,UAAK,WAAU,kBAAkB,mBAAQ;AAAA,QAC1C,gBAAAC,MAAC,SAAI,WAAU,oCACb;AAAA,0BAAAD,KAAC,YAAO,MAAK,UAAS,cAAY,YAAY,SAAS,SAAS,WAAU,kDAAiD,mBAE3H;AAAA,UACA,gBAAAA,KAAC,YAAO,MAAK,UAAS,SAAS,WAAW,WAAU,sCAAqC,qBAEzF;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAEO,SAAS,UAAU,OAAuB;AAC/C,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,cAAc,SAAS;AAC7B,QAAM,aAAa,mBAAmB;AACtC,QAAM,oBAAoB,UAAU;AAEpC,QAAM,iBAAiB,eAAe;AACtC,QAAM,gBAAgB,aAAa,aAAa,EAAE,OAAO,WAAW,mBAAmB,KAAK;AAE5F,QAAM,CAAC,MAAM,OAAO,IAAI,SAA0B,CAAC,CAAC;AACpD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,IAAI;AACnD,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAkC,IAAI;AACxE,QAAM,CAAC,cAAc,eAAe,IAAI,SAAwB,IAAI;AACpE,QAAM,eAAe,aAAc,kBAAkB,OAAQ;AAE7D,QAAM,CAAC,cAAc,eAAe,IAAI,SAA2B,IAAI;AACvE,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AACpD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAwB,IAAI;AAC9D,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,CAAC;AAChD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAC1C,QAAM,CAAC,WAAW,YAAY,IAAI,SAAuC,IAAI;AAE7E,QAAM,CAAC,YAAY,aAAa,IAAI,SAA0B,MAAM;AACpE,QAAM,CAAC,WAAW,YAAY,IAAI,SAAyB,EAAE;AAC7D,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,EAAE;AACjD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,EAAE;AACzC,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,aAAa,cAAc,IAAI,SAAuC,IAAI;AACjF,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,aAAa,cAAc,IAAI,SAAuC,IAAI;AACjF,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,CAAC,YAAY,aAAa,IAAI,SAAqB,IAAI;AAC7D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,EAAE;AACrC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAwB,IAAI;AAEhE,QAAM,kBAAkBG,QAAO,EAAE;AACjC,QAAM,gBAAgBA,QAAsB,IAAI;AAChD,QAAM,sBAAsBA,QAAO,gBAAgB;AACnD,sBAAoB,UAAU;AAE9B,QAAM,YAAY;AAAA,IAChB,MAAM,iBAAiB,MAAM,EAAE,OAAO,oBAAI,IAAY,GAAG,aAAa,oBAAI,IAAY,EAAE,CAAC;AAAA,IACzF,CAAC,IAAI;AAAA,EACP;AACA,QAAM,YAAY,UAAU;AAC5B,QAAM,uBAAuB;AAAA,IAC3B,MAAM,eAAe,gBAAgB,cAAc,SAAS,IAAI;AAAA,IAChE,CAAC,cAAc,SAAS;AAAA,EAC1B;AAIA,QAAM,mBAAmB;AAAA,IACvB,MAAO,aAAa,cAAc,MAAM,UAAU,IAAI;AAAA,IACtD,CAAC,MAAM,UAAU;AAAA,EACnB;AACA,QAAM,eAAe,kBAAkB,QAAQ;AAC/C,QAAM,WAAW;AAAA,IACf,OAAO,EAAE,MAAM,SAAS,MAAM,IAAI,MAAM,aAAa,UAAU,KAAK;AAAA,IACpE,CAAC,IAAI;AAAA,EACP;AAMA,QAAM,cAAc,QAAuB,MAAM;AAC/C,UAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,QAAI,CAAC,EAAG,QAAO;AACf,UAAM,OAAO,oBAAoB;AACjC,WAAO,EAAE,GAAG,MAAM,UAAU,YAAY,KAAK,YAAY,CAAC,GAAG,CAAC,EAAE;AAAA,EAClE,GAAG,CAAC,UAAU,kBAAkB,KAAK,CAAC;AAEtC,QAAM,aAAa;AAAA,IACjB,CAAC,SAAwB;AACvB,UAAI,CAAC,WAAY,iBAAgB,IAAI;AACrC,6BAAuB,IAAI;AAAA,IAC7B;AAAA,IACA,CAAC,YAAY,oBAAoB;AAAA,EACnC;AAEA,QAAM,gBAAgB,YAAY,CAChC,WACA,OACA,OACA,UACA,SAC0B;AAC1B,UAAM,UAAiC;AAAA,MACrC;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS,iBAAiB,OAAO,QAAQ;AAAA,MACzC,OAAO;AAAA,IACT;AACA,QAAI;AACF,0BAAoB,UAAU,OAAO;AAAA,IACvC,SAAS,eAAe;AACtB,cAAQ,MAAM,2CAA2C,aAAa;AAAA,IACxE;AACA,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AAEL,QAAM,UAAU,YAAY,OAAO,UAA8B,iBAAmC;AAClG,mBAAe,IAAI;AACnB,iBAAa,IAAI;AACjB,QAAI;AACF,cAAQ,MAAM,KAAK,SAAS,CAAC;AAC7B,oBAAc,IAAI;AAClB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,UAAU;AAAA,QACd,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,MACV;AACA,mBAAa,EAAE,SAAS,QAAQ,CAAC;AACjC,aAAO;AAAA,IACT,UAAE;AACA,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,MAAM,aAAa,CAAC;AAExB,EAAAC,WAAU,MAAM;AACd,YAAQ,CAAC,CAAC;AACV,kBAAc,KAAK;AACnB,iBAAa,IAAI;AAAA,EACnB,GAAG,CAAC,IAAI,CAAC;AAET,EAAAA,WAAU,MAAM;AACd,SAAK,QAAQ;AAAA,EACf,GAAG,CAAC,SAAS,UAAU,CAAC;AAExB,QAAM,YAAY,YAAY,YAAY;AACxC,QAAI,CAAC,UAAW;AAChB,UAAM,EAAE,QAAQ,IAAI;AACpB,UAAM,YAAY,MAAM,QAAQ,OAAO;AACvC,QAAI,aAAa,QAAQ,oBAAqB,YAAW,QAAQ,mBAAmB;AAAA,EACtF,GAAG,CAAC,WAAW,SAAS,UAAU,CAAC;AAEnC,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,cAAc;AACjB,sBAAgB,IAAI;AACpB,qBAAe,KAAK;AACpB,mBAAa,IAAI;AACjB,oBAAc,UAAU;AACxB;AAAA,IACF;AACA,QAAI,eAAe,CAAC,WAAY;AAChC,QAAI,CAAC,sBAAsB;AACzB,iBAAW,IAAI;AACf,sBAAgB,IAAI;AACpB,qBAAe,KAAK;AACpB,mBAAa,IAAI;AACjB,oBAAc,UAAU;AACxB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,UAAM,OAAO;AACb,QAAI,SAAS,aAAc,YAAW,IAAI;AAC1C,mBAAe,IAAI;AACnB,iBAAa,IAAI;AACjB,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,OAAO,MAAM,KAAK,SAAS,IAAI;AACrC,YAAI,CAAC,UAAW,iBAAgB,IAAI;AAAA,MACtC,SAAS,KAAK;AAGZ,YAAI,CAAC,WAAW;AACd,gBAAM,UAAU,cAAc,QAAQ,aAAa,KAAK,uBAAuB,IAAI;AACnF,0BAAgB,IAAI;AACpB,uBAAa,QAAQ,OAAO;AAAA,QAC9B;AAAA,MACF,UAAE;AACA,YAAI,CAAC,UAAW,gBAAe,KAAK;AAAA,MACtC;AAAA,IACF,GAAG;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,MAAM,cAAc,sBAAsB,aAAa,YAAY,YAAY,aAAa,YAAY,aAAa,CAAC;AAE1H,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,cAAc;AACjB,oBAAc,UAAU;AACxB,sBAAgB,UAAU;AAC1B,mBAAa,EAAE;AACf,qBAAe,EAAE;AACjB,oBAAc,MAAM;AACpB,iBAAW,KAAK;AAChB,mBAAa,IAAI;AACjB;AAAA,IACF;AACA,UAAM,cAAc,cAAc,YAAY,aAAa;AAC3D,kBAAc,UAAU,aAAa;AACrC,oBAAgB,UAAU,aAAa;AACvC,iBAAa,YAAY,MAAM,aAAa,OAAO,CAAC;AACpD,mBAAe,aAAa,OAAO;AACnC,QAAI,YAAa,eAAc,MAAM;AACrC,eAAW,KAAK;AAChB,iBAAa,IAAI;AACjB,gBAAY,KAAK;AAAA,EACnB,GAAG,CAAC,cAAc,WAAW,CAAC;AAE9B,QAAM,cAAc;AAAA,IAClB,CAAC,SAAiB;AAChB,UAAI,SAAS,aAAc;AAC3B,UAAI,SAAS;AACX,sBAAc,EAAE,MAAM,QAAQ,KAAK,CAAC;AACpC;AAAA,MACF;AACA,iBAAW,IAAI;AAAA,IACjB;AAAA,IACA,CAAC,SAAS,cAAc,UAAU;AAAA,EACpC;AAKA,QAAM,eAAe,YAAY,CAAC,SAAiB;AACjD,kBAAc,CAAC,YAAa,YAAY,OAAO,OAAO,IAAK;AAAA,EAC7D,GAAG,CAAC,CAAC;AAKL,QAAM,gBAAgBD,QAA+B,MAAM;AAAA,EAAC,CAAC;AAC7D,gBAAc,UAAU,CAAC,YAAoB;AAC3C,UAAM,SAAS,gBAAgB,SAAS,SAAS;AACjD,QAAI,CAAC,OAAQ;AACb,QAAI,OAAO,SAAS,QAAQ;AAC1B,kBAAY,OAAO,IAAI;AACvB;AAAA,IACF;AACA,iBAAa,OAAO,IAAI;AAAA,EAC1B;AACA,QAAM,mBAAmB,YAAY,CAAC,SAAiB,cAAc,QAAQ,IAAI,GAAG,CAAC,CAAC;AAEtF,QAAM,eAAe,YAAY,MAAM;AACrC,QAAI,SAAS;AACX,oBAAc,EAAE,MAAM,QAAQ,CAAC;AAC/B;AAAA,IACF;AACA,eAAW,IAAI;AACf,oBAAgB,IAAI;AAAA,EACtB,GAAG,CAAC,SAAS,UAAU,CAAC;AAExB,QAAM,iBAAiB,YAAY,MAAM;AACvC,UAAM,MAAM;AACZ,kBAAc,IAAI;AAClB,eAAW,KAAK;AAChB,QAAI,CAAC,IAAK;AACV,QAAI,IAAI,SAAS,QAAQ;AACvB,iBAAW,IAAI,IAAI;AAAA,IACrB,OAAO;AACL,iBAAW,IAAI;AACf,sBAAgB,IAAI;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,YAAY,UAAU,CAAC;AAE3B,QAAM,eAAe,YAAY,MAAM;AACrC,kBAAc,CAAC,SAAS;AACtB,UAAI,SAAS,OAAQ,QAAO;AAC5B,mBAAa,YAAY,MAAM,WAAW,CAAC;AAC3C,iBAAW,gBAAgB,gBAAgB,OAAO;AAClD,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,aAAa,WAAW,CAAC;AAE7B,QAAM,iBAAiB,YAAY,MAAM;AACvC,kBAAc,CAAC,SAAS;AACtB,UAAI,SAAS,SAAU,QAAO;AAC9B,YAAM,UAAU,UAAU,YAAY,UAAU,SAAS,IAAI,gBAAgB;AAC7E,qBAAe,OAAO;AACtB,iBAAW,YAAY,gBAAgB,OAAO;AAC9C,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,aAAa,SAAS,SAAS,CAAC;AAEpC,QAAM,iBAAiB,YAAY,CAAC,SAAiB;AACnD,mBAAe,IAAI;AACnB,eAAW,SAAS,gBAAgB,OAAO;AAAA,EAC7C,GAAG,CAAC,CAAC;AAEL,QAAM,eAAe,YAAY,CAAC,SAAyB;AACzD,iBAAa,IAAI;AACjB,eAAW,YAAY,UAAU,IAAI,MAAM,gBAAgB,OAAO;AAAA,EACpE,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,cAAc,YAAY,YAAY;AAC1C,QAAI,CAAC,aAAc;AACnB,UAAM,UAAU,eAAe,WAAW,cAAc,YAAY,UAAU,SAAS;AACvF,cAAU,IAAI;AACd,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,KAAK,UAAU,aAAa,MAAM,OAAO;AAC/C,sBAAgB,UAAU;AAC1B,sBAAgB,EAAE,GAAG,cAAc,QAAQ,CAAC;AAC5C,qBAAe,OAAO;AACtB,mBAAa,YAAY,MAAM,OAAO,CAAC;AACvC,iBAAW,KAAK;AAAA,IAClB,SAAS,OAAO;AACd,mBAAa,cAAc,QAAQ,aAAa,OAAO,uBAAuB,aAAa,IAAI,CAAC;AAAA,IAClG,UAAE;AACA,gBAAU,KAAK;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,cAAc,YAAY,aAAa,WAAW,aAAa,MAAM,aAAa,CAAC;AAEvF,QAAM,eAAe,YAAY,YAAY;AAC3C,UAAM,UAAU,QAAQ,KAAK;AAG7B,QAAI,CAAC,WAAW,CAAE,QAAQ,MAAM,GAAG,EAAE,IAAI,GAAG,KAAK,EAAI;AACrD,gBAAY,IAAI;AAChB,mBAAe,IAAI;AACnB,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,WAAW,OAAO;AAC7C,oBAAc,KAAK;AACnB,iBAAW,EAAE;AACb,YAAM,YAAY,MAAM,QAAQ;AAAA,QAC9B,WAAW;AAAA,QACX,OAAO;AAAA,QACP,MAAM;AAAA,QACN,qBAAqB;AAAA,MACvB,CAAC;AACD,UAAI,UAAW,YAAW,OAAO;AAAA,IACnC,SAAS,OAAO;AACd,qBAAe,cAAc,UAAU,aAAa,OAAO,yBAAyB,OAAO,CAAC;AAAA,IAC9F,UAAE;AACA,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,SAAS,MAAM,SAAS,YAAY,aAAa,CAAC;AAEtD,QAAM,eAAe,YAAY,YAAY;AAC3C,QAAI,CAAC,aAAc;AACnB,UAAM,OAAO,aAAa;AAC1B,gBAAY,IAAI;AAChB,mBAAe,IAAI;AACnB,QAAI;AACF,YAAM,KAAK,WAAW,IAAI;AAC1B,oBAAc,KAAK;AACnB,iBAAW,KAAK;AAChB,iBAAW,IAAI;AACf,sBAAgB,IAAI;AACpB,YAAM,QAAQ,EAAE,WAAW,UAAU,OAAO,yBAAyB,KAAK,CAAC;AAAA,IAC7E,SAAS,OAAO;AACd,qBAAe,cAAc,UAAU,aAAa,OAAO,yBAAyB,IAAI,CAAC;AAAA,IAC3F,UAAE;AACA,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,cAAc,MAAM,SAAS,YAAY,aAAa,CAAC;AAE3D,QAAM,iBAAiB,QAAQ,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI,GAAG,KAAK,KAAK;AAElE,MAAI;AACJ,MAAI,eAAgB,CAAC,cAAc,CAAC,WAAY;AAC9C,kBAAc,gBAAAH,KAAC,gBAAa;AAAA,EAC9B,WAAW,CAAC,cAAc,WAAW;AACnC,kBAAc,gBAAAA,KAAC,kBAAe,SAAS,mBAAmB,UAAU,OAAO,GAAG,SAAS,MAAM,KAAK,UAAU,GAAG;AAAA,EACjH,OAAO;AACL,kBACE,gBAAAC,MAAA,YACG;AAAA,mBACC,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,mBAAmB,UAAU,OAAO;AAAA,UAC7C,YAAW;AAAA,UACX,SAAS,MAAM,KAAK,UAAU;AAAA,UAC9B,WAAW,MAAM,aAAa,IAAI;AAAA;AAAA,MACpC;AAAA,MAED,WAAW;AAAA,QACV,MAAM;AAAA,QACN,cAAc,wBAAwB;AAAA,QACtC,UAAU;AAAA,MACZ,CAAC;AAAA,OACH;AAAA,EAEJ;AAEA,SACE,gBAAAA,KAAC,uBAAoB,SAAS,MAAM;AAAE,eAAW,IAAI;AAAG,oBAAgB,IAAI;AAAA,EAAE,GAC5E,0BAAAC,MAAC,SAAI,WAAW,uCAAuC,aAAa,EAAE,IACpE;AAAA,oBAAAA,MAAC,SAAI,WAAU,8EACb;AAAA,sBAAAA,MAAC,SAAI,WAAU,4DACb;AAAA,wBAAAD,KAAC,SAAI,WAAU,kBACb,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,OAAO;AAAA,YACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,YACxC,aAAa,eAAe,UAAU,YAAY,WAAM;AAAA,YACxD,cAAW;AAAA,YACX,WAAU;AAAA;AAAA,QACZ,GACF;AAAA,QACA,gBAAAC,MAAC,SAAI,WAAU,oCACZ;AAAA;AAAA,UACD,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAW;AAAA,cACX,SAAS,MAAM,KAAK,QAAQ;AAAA,cAC5B,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,UACC,YACC,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAY,eAAe,qBAAqB,YAAY,KAAK;AAAA,cACjE,SAAS,MAAM;AAAE,+BAAe,IAAI;AAAG,2BAAW,eAAe,GAAG,YAAY,MAAM,EAAE;AAAG,8BAAc,IAAI;AAAA,cAAE;AAAA,cAC/G,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,WAEJ;AAAA,SACF;AAAA,MACC,gBACC,gBAAAC,MAAC,SAAI,WAAU,oFACb;AAAA,wBAAAD,KAAC,UAAO,WAAU,8CAA6C,eAAY,QAAO;AAAA,QAClF,gBAAAA,KAAC,UAAK,qBAAiB,MAAC,WAAU,uDAAsD,OAAO,cAC5F,wBACH;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,cAAW;AAAA,YACX,SAAS,MAAM,cAAc,IAAI;AAAA,YAChD,WAAU;AAAA,YACI;AAAA;AAAA,QAED;AAAA,SACF;AAAA,MAEF,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,gBAAgB,CAAC,UAAU;AACzB,kBAAM,SAAS,gBAAgB,KAAK;AACpC,gBAAI,OAAQ,kBAAiB,OAAO,IAAI;AAAA,UAC1C;AAAA,UAEC;AAAA;AAAA,MACH;AAAA,OACF;AAAA,IAEA,gBAAAC,MAAC,SAAI,WAAU,gDACZ;AAAA,sBACC,gBAAAA,MAAC,SAAI,WAAW,iFAAiF,oBAAoB,SAAS,IAC5H;AAAA,wBAAAD,KAAC,UAAK,mBAAe,MAAC,WAAU,gDAAgD,uBAAa,MAAK;AAAA,QAClG,gBAAAC,MAAC,SAAI,WAAU,2BACZ;AAAA,sBAAY,qBACX,gBAAAA,MAAC,SAAI,WAAU,gCACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,cAAW;AAAA,gBACX,gBAAc,eAAe;AAAA,gBAC7B,SAAS;AAAA,gBACT,WAAW,uEACT,eAAe,SACX,uCACA,4DACN;AAAA,gBACD;AAAA;AAAA,YAED;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,cAAW;AAAA,gBACX,gBAAc,eAAe;AAAA,gBAC7B,SAAS;AAAA,gBACT,WAAW,uEACT,eAAe,WACX,uCACA,4DACN;AAAA,gBACD;AAAA;AAAA,YAED;AAAA,aACF;AAAA,UAED,cAAc,CAAC,kBACd,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAY,cAAc;AAAA,cAC1B,gBAAc;AAAA,cACd,WAAW,cAAc,qBAAqB,SAAS;AAAA,cACvD,QAAQ,cAAc,qBAAqB,SAAS,UAAU,4BAA6B,cAAc,SAAS,cAAc;AAAA,cAChI,SAAS,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;AAAA,cACpC,WAAW,iIACT,WACI,uCACA,4DACN;AAAA,cAEC,wBAAc;AAAA;AAAA,UACjB;AAAA,UAED,kBACC,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAW;AAAA,cACX,OAAM;AAAA,cACN,SAAS,MAAM,eAAe,YAAY;AAAA,cAC1C,WAAU;AAAA,cAEV,0BAAAA,KAAC,YAAS,WAAU,WAAU;AAAA;AAAA,UAChC;AAAA,UAED,YACC,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAW;AAAA,cACX,OAAM;AAAA,cACN,SAAS,MAAM;AAAE,+BAAe,IAAI;AAAG,8BAAc,IAAI;AAAA,cAAE;AAAA,cAC3D,WAAU;AAAA,cAEV,0BAAAA,KAAC,UAAO,WAAU,WAAU;AAAA;AAAA,UAC9B;AAAA,WAEJ;AAAA,SACF;AAAA,MAED,gBAAgB,aACf,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,UAAU;AAAA,UACnB,YAAW;AAAA,UACX,SAAS,MAAM,KAAK,YAAY;AAAA,UAChC,WAAW,MAAM,aAAa,IAAI;AAAA;AAAA,MACpC;AAAA,MAEF,gBAAAA,KAAC,SAAI,WAAU,0BACZ,wBACC,gBAAAA,KAAC,kBAAe,IACd,YACF,gBAAAA,KAAC,kBAAe,SAAS,WAAW,SAAS,MAAM,eAAe,CAAC,MAAM,IAAI,CAAC,GAAG,IAC/E,gBAAgB,YAAY,qBAAqB,eAAe,WAClE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,aAAa;AAAA,UACnB,SAAS;AAAA,UACT;AAAA,UACA,OAAO;AAAA,UACP,UAAU;AAAA,UACV,QAAQ,MAAM,KAAK,YAAY;AAAA;AAAA,MACjC,IACE,eACF,eAAe;AAAA,QACb,MAAM;AAAA,QACN,SAAS;AAAA,QACT,MAAM;AAAA,QACN;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP;AAAA,QACA,QAAQ,MAAM,KAAK,YAAY;AAAA,MACjC,CAAC,IAED,gBAAAA,KAAC,cAAW,GAEhB;AAAA,OACF;AAAA,IAEC,cAAc,gBAAgB,WAAW;AAAA,MACxC,MAAM;AAAA,MACN,MAAM,iBAAiB,OAAO;AAAA,MAC9B,SAAS,iBAAiB,MAAM;AAAA,MAAC,IAAI,MAAM,YAAY,KAAK;AAAA,IAC9D,CAAC;AAAA,IAED,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,OAAM;AAAA,QACN,aAAa,eAAe,yBAAyB,YAAY,MAAM;AAAA,QACvE,cAAc,WAAW,mBAAc;AAAA,QAIvC,iBAAiB,YAAY,CAAC;AAAA,QAC9B,WAAW,MAAM,KAAK,aAAa;AAAA,QACnC,UAAU,MAAM;AAAE,wBAAc,KAAK;AAAG,qBAAW,EAAE;AAAG,yBAAe,IAAI;AAAA,QAAE;AAAA,QAE7E,0BAAAC,MAAC,SAAI,WAAU,aACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,WAAS;AAAA,cACT,UAAU,CAAC,MAAM,WAAW,EAAE,OAAO,KAAK;AAAA,cAC1C,aAAY;AAAA,cACZ,cAAW;AAAA,cACX,WAAU;AAAA;AAAA,UACZ;AAAA,UACC,eAAe,gBAAAA,KAAC,OAAE,MAAK,SAAQ,WAAU,4BAA4B,sBAAY,SAAQ;AAAA,WAC5F;AAAA;AAAA,IACF;AAAA,IAEA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,OAAM;AAAA,QACN,aAAa,4BAA4B,cAAc,QAAQ,WAAW;AAAA,QAC1E,cAAc,WAAW,mBAAc;AAAA,QACvC,iBAAiB;AAAA,QACjB,aAAW;AAAA,QACX,WAAW,MAAM,KAAK,aAAa;AAAA,QACnC,UAAU,MAAM;AAAE,wBAAc,KAAK;AAAG,yBAAe,IAAI;AAAA,QAAE;AAAA,QAE5D,yBAAe,gBAAAA,KAAC,OAAE,MAAK,SAAQ,WAAU,4BAA4B,sBAAY,SAAQ;AAAA;AAAA,IAC5F;AAAA,IAEA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,eAAe;AAAA,QACrB,OAAM;AAAA,QACN,aAAY;AAAA,QACZ,cAAa;AAAA,QACb,aAAW;AAAA,QACX,WAAW;AAAA,QACX,UAAU,MAAM,cAAc,IAAI;AAAA;AAAA,IACpC;AAAA,KACF,GACF;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AACD,SACE,gBAAAC,MAAC,SAAI,WAAU,8CACb;AAAA,oBAAAA,MAAC,SAAI,WAAU,4EACb;AAAA,sBAAAD,KAAC,OAAE,WAAU,oDAAoD,gBAAK;AAAA,MACtE,gBAAAC,MAAC,SAAI,WAAU,oCACb;AAAA,wBAAAD,KAAC,UAAK,WAAU,6FACb,kBAAQ,oBAAoB,SAC/B;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS;AAAA,YACT,UAAU,UAAU,CAAC;AAAA,YACrB,WAAU;AAAA,YAET,mBAAS,iBAAY;AAAA;AAAA,QACxB;AAAA,SACF;AAAA,OACF;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,QAChD,YAAY;AAAA,QACZ,cAAW;AAAA,QAGX,WAAU;AAAA;AAAA,IACZ;AAAA,KACF;AAEJ;","names":["useEffect","useRef","jsx","jsxs","path","useRef","useEffect"]}
@@ -155,7 +155,7 @@ function Shell({ title, body, children }) {
155
155
  return /* @__PURE__ */ jsxs("div", { className: "mx-auto flex w-full max-w-sm flex-col", children: [
156
156
  /* @__PURE__ */ jsxs("span", { className: "mb-4 flex items-center gap-2 text-[var(--text-muted)]", children: [
157
157
  /* @__PURE__ */ jsx2(BrandMark, { size: 22, className: "shrink-0" }),
158
- /* @__PURE__ */ jsx2("span", { className: "text-[11px] font-semibold uppercase tracking-[0.05em]", children: "Tangle Teams" })
158
+ /* @__PURE__ */ jsx2("span", { className: "text-xs font-semibold uppercase tracking-[0.05em]", children: "Tangle Teams" })
159
159
  ] }),
160
160
  /* @__PURE__ */ jsx2("h1", { className: "mb-1 text-xl font-semibold tracking-tight text-[var(--text-primary)]", children: title }),
161
161
  body && /* @__PURE__ */ jsx2("p", { className: "mb-6 text-sm text-[var(--text-secondary)]", children: body }),
@@ -189,4 +189,4 @@ function SecondaryButton({ children, onClick }) {
189
189
  export {
190
190
  InviteAcceptPage
191
191
  };
192
- //# sourceMappingURL=chunk-DB2D2JBQ.js.map
192
+ //# sourceMappingURL=chunk-W7P5KV6O.js.map