@tangle-network/agent-app 0.45.47 → 0.45.48

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-P2ZVK6FM.js → DesignCanvas-QY2CA5GI.js} +2 -2
  2. package/dist/{DesignCanvasEditor-DGNCA54P.js → DesignCanvasEditor-CJ7PLX7A.js} +3 -3
  3. package/dist/{IntakeInterview-3ZIQMZS6.js → IntakeInterview-NJXKH35Q.js} +2 -2
  4. package/dist/InvitationsPanel-F2VY4TSL.js +9 -0
  5. package/dist/InviteAcceptPage-UT4452KT.js +7 -0
  6. package/dist/MembersPanel-N5CBOUXC.js +9 -0
  7. package/dist/{TimelineEditor-OTNPJ3SN.js → TimelineEditor-IYNKBSUX.js} +2 -2
  8. package/dist/assistant/index.d.ts +35 -10
  9. package/dist/assistant/index.js +234 -89
  10. package/dist/assistant/index.js.map +1 -1
  11. package/dist/chat-react/index.js +2 -1
  12. package/dist/chat-react/index.js.map +1 -1
  13. package/dist/{chunk-IUSO2XRE.js → chunk-5BACNOVR.js} +13 -14
  14. package/dist/chunk-5BACNOVR.js.map +1 -0
  15. package/dist/chunk-AULM4X7U.js +77 -0
  16. package/dist/chunk-AULM4X7U.js.map +1 -0
  17. package/dist/{chunk-ZX5UT4HF.js → chunk-C7AQW6US.js} +321 -240
  18. package/dist/chunk-C7AQW6US.js.map +1 -0
  19. package/dist/{chunk-VCPZ3HTN.js → chunk-DB2D2JBQ.js} +51 -27
  20. package/dist/chunk-DB2D2JBQ.js.map +1 -0
  21. package/dist/{chunk-TL37E5YY.js → chunk-DS2WE62K.js} +3 -3
  22. package/dist/{chunk-TL37E5YY.js.map → chunk-DS2WE62K.js.map} +1 -1
  23. package/dist/{chunk-PE67O2LH.js → chunk-GT4GTRGQ.js} +18 -13
  24. package/dist/chunk-GT4GTRGQ.js.map +1 -0
  25. package/dist/{chunk-BT4QFCTB.js → chunk-HSBJB46C.js} +13 -1
  26. package/dist/chunk-HSBJB46C.js.map +1 -0
  27. package/dist/{chunk-54FYYXAX.js → chunk-J6OJTFNE.js} +13 -10
  28. package/dist/chunk-J6OJTFNE.js.map +1 -0
  29. package/dist/chunk-MLG6XKPV.js +44 -0
  30. package/dist/chunk-MLG6XKPV.js.map +1 -0
  31. package/dist/{chunk-F4XLTVQ3.js → chunk-R6KGLGEJ.js} +18 -17
  32. package/dist/chunk-R6KGLGEJ.js.map +1 -0
  33. package/dist/{chunk-B62EZF52.js → chunk-RILGAFEL.js} +3 -3
  34. package/dist/{chunk-65OZT566.js → chunk-T7XPIBCX.js} +16 -14
  35. package/dist/chunk-T7XPIBCX.js.map +1 -0
  36. package/dist/{chunk-VOJB7MVG.js → chunk-XPFNEKHS.js} +70 -28
  37. package/dist/chunk-XPFNEKHS.js.map +1 -0
  38. package/dist/{chunk-GNPN6ED4.js → chunk-XVRHISF3.js} +127 -165
  39. package/dist/chunk-XVRHISF3.js.map +1 -0
  40. package/dist/design-canvas-react/index.js +14 -12
  41. package/dist/design-canvas-react/index.js.map +1 -1
  42. package/dist/design-canvas-react/lazy.js +1 -1
  43. package/dist/intakes-react/index.js +1 -1
  44. package/dist/intakes-react/lazy.js +1 -1
  45. package/dist/sequences-react/index.js +2 -2
  46. package/dist/studio/index.d.ts +13 -1
  47. package/dist/studio/index.js +13 -1
  48. package/dist/studio-react/index.js +100 -63
  49. package/dist/studio-react/index.js.map +1 -1
  50. package/dist/studio-react/studio.css +0 -56
  51. package/dist/teams-react/index.js +4 -3
  52. package/dist/teams-react/lazy.js +3 -3
  53. package/dist/web-react/index.d.ts +15 -11
  54. package/dist/web-react/index.js +6 -4
  55. package/dist/work-product-react/index.js +3 -21
  56. package/dist/work-product-react/index.js.map +1 -1
  57. package/package.json +3 -3
  58. package/dist/InvitationsPanel-F56UOQXA.js +0 -8
  59. package/dist/InviteAcceptPage-P3QJC3C6.js +0 -7
  60. package/dist/MembersPanel-XNLF3SA4.js +0 -8
  61. package/dist/chunk-54FYYXAX.js.map +0 -1
  62. package/dist/chunk-65OZT566.js.map +0 -1
  63. package/dist/chunk-BT4QFCTB.js.map +0 -1
  64. package/dist/chunk-F4XLTVQ3.js.map +0 -1
  65. package/dist/chunk-GNPN6ED4.js.map +0 -1
  66. package/dist/chunk-IUSO2XRE.js.map +0 -1
  67. package/dist/chunk-PE67O2LH.js.map +0 -1
  68. package/dist/chunk-VCPZ3HTN.js.map +0 -1
  69. package/dist/chunk-VOJB7MVG.js.map +0 -1
  70. package/dist/chunk-ZX5UT4HF.js.map +0 -1
  71. /package/dist/{DesignCanvas-P2ZVK6FM.js.map → DesignCanvas-QY2CA5GI.js.map} +0 -0
  72. /package/dist/{DesignCanvasEditor-DGNCA54P.js.map → DesignCanvasEditor-CJ7PLX7A.js.map} +0 -0
  73. /package/dist/{IntakeInterview-3ZIQMZS6.js.map → IntakeInterview-NJXKH35Q.js.map} +0 -0
  74. /package/dist/{InvitationsPanel-F56UOQXA.js.map → InvitationsPanel-F2VY4TSL.js.map} +0 -0
  75. /package/dist/{InviteAcceptPage-P3QJC3C6.js.map → InviteAcceptPage-UT4452KT.js.map} +0 -0
  76. /package/dist/{MembersPanel-XNLF3SA4.js.map → MembersPanel-N5CBOUXC.js.map} +0 -0
  77. /package/dist/{TimelineEditor-OTNPJ3SN.js.map → TimelineEditor-IYNKBSUX.js.map} +0 -0
  78. /package/dist/{chunk-B62EZF52.js.map → chunk-RILGAFEL.js.map} +0 -0
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  AgentSessionControls,
3
3
  useComposerAttachments
4
- } from "../chunk-GNPN6ED4.js";
4
+ } from "../chunk-XVRHISF3.js";
5
+ import "../chunk-MLG6XKPV.js";
5
6
  import {
6
7
  ATTACHMENT_ACCEPT
7
8
  } from "../chunk-CCVG2TL6.js";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/chat-react/entry-composer.tsx","../../src/chat-react/composer-mode-controls.tsx"],"sourcesContent":["import { useState, type ReactNode } from 'react'\nimport { AgentComposer } from '@tangle-network/sandbox-ui/chat'\nimport { ATTACHMENT_ACCEPT, useComposerAttachments } from '../web-react/use-composer-attachments'\nimport type { UseFileMentionsResult } from '../web-react/use-file-mentions'\nimport type { ChatAttachmentInput, FileMention } from '../chat-routes/wire'\nimport {\n AgentSessionControls,\n type AgentSessionControlsProps,\n} from '../web-react/agent-session-controls'\nimport { ComposerModeControls } from './composer-mode-controls'\nimport type { ComposerPlanModeSelection } from './types'\n\nexport interface EntryComposerProps {\n /** The one line above the input. Domain copy — always a product parameter. */\n heading?: ReactNode\n /** Optional supporting line under the heading. */\n subheading?: ReactNode\n placeholder?: string\n initialValue?: string\n sendLabel?: string\n disabled?: boolean\n /**\n * Agent identity (backend/model/effort). Pass it and the control row renders;\n * omit it and the composer ships without one. Omitting is a real choice for a\n * surface with nothing to choose — it should never be an oversight, which is\n * why this is one prop rather than a free-form slot a caller can forget.\n *\n * This is the CANONICAL picker cluster: `AgentSessionControls` from\n * `/web-react` (its model menu IS the canonical `ModelPicker`). The legacy\n * sandbox-ui adapter that used to back this prop was removed; the props\n * mapping for migrating a stored selection lives in\n * `docs/ui-picker-canon.md`.\n */\n agent?: AgentSessionControlsProps\n /**\n * Product-specific behavioral controls docked on the LEFT. A mode is an\n * on/off switch, not a value picker, so it sits apart from agent identity.\n * For the standard plan-approval control, prefer `planMode`.\n */\n modes?: ReactNode\n /**\n * Standard plan-approval mode. Pass only when the selected backend supports\n * it; omitted means the control is not rendered.\n */\n planMode?: ComposerPlanModeSelection\n /**\n * Upload endpoint for staged attachments. Omit and the attach affordance is\n * hidden — a composer with no place to put a file must not offer one.\n */\n uploadUrl?: string\n accept?: string\n onAttachmentError?: (reason: string) => void\n onRejectFiles?: Parameters<typeof AgentComposer>[0]['onRejectFiles']\n /**\n * `@`-file mentions. The hook itself is shared (`useFileMentions`), but the\n * cold-box policy around it — whether pressing `@` is worth starting a\n * sandbox — is a per-surface product call, so the result is injected rather\n * than created here.\n */\n mentions?: UseFileMentionsResult\n mentionPopoverClassName?: string\n /** Rendered under the composer: suggestion pills, an onboarding nudge, a\n * disclaimer. All domain copy. */\n footer?: ReactNode\n /** Block submit until a persisted selection has resolved against the catalog,\n * so the first turn cannot go out under the wrong model. Defaults to true. */\n ready?: boolean\n onSubmit: (\n prompt: string,\n attachments: ChatAttachmentInput[],\n mentions: FileMention[],\n ) => void\n className?: string\n composerClassName?: string\n /** Max width of the centered column. Defaults to the fleet's 820px. */\n maxWidth?: number\n}\n\n/**\n * `EntryComposer` — the centered \"what do you want to work on?\" surface a\n * product shows before a conversation exists (a new thread, an empty session,\n * a workspace overview).\n *\n * It exists because three products each grew their own, and they drifted into\n * three different capability sets rather than three different looks: one lost\n * the attach button, one lost the model and effort pickers entirely, one\n * hand-rolled a `<textarea>` and wired pickers from three separate packages\n * into one row. The composer, the pickers, the attachment queue and the mention\n * index were all ALREADY shared — what was not shared was the assembly, so\n * every product re-derived which controls an entry surface gets, and each\n * re-derivation dropped a different one.\n *\n * Mechanism lives here: layout, the attachment queue, the mention wiring, the\n * submit gate (nothing sends while an upload is pending or failed, or before\n * the model selection has resolved). Domain stays a parameter: `heading`,\n * `placeholder`, `footer` (suggestion pills, disclaimers) and the selections\n * themselves.\n *\n * The docked in-thread composer is NOT this component — it renders\n * `AgentComposer` directly with the same canonical `AgentSessionControls`,\n * because a docked composer has a transcript above it and no hero layout.\n */\nexport function EntryComposer({\n heading,\n subheading,\n placeholder,\n initialValue = '',\n sendLabel,\n disabled,\n agent,\n modes,\n planMode,\n uploadUrl,\n accept = ATTACHMENT_ACCEPT,\n onAttachmentError,\n onRejectFiles,\n mentions,\n mentionPopoverClassName,\n footer,\n ready = true,\n onSubmit,\n className,\n composerClassName,\n maxWidth = 820,\n}: EntryComposerProps) {\n const [value, setValue] = useState(initialValue)\n const attachments = useComposerAttachments({\n uploadUrl,\n enabled: !!uploadUrl,\n onReject: onAttachmentError,\n onError: onAttachmentError,\n })\n\n function submit(prompt: string) {\n if (!ready || disabled) return\n const next = prompt.trim()\n // A pending or failed upload must not send: the turn would reference an\n // attachment the store does not have. Surface why rather than no-op'ing.\n if (attachments.hasPending || attachments.hasError) {\n if (attachments.blockReason) onAttachmentError?.(attachments.blockReason)\n return\n }\n if (!next && attachments.references.length === 0) return\n onSubmit(next, attachments.references, mentions?.mentions ?? [])\n setValue('')\n attachments.clear()\n mentions?.clearMentions()\n }\n\n return (\n <div\n className={\n className ??\n 'relative flex flex-1 flex-col items-center justify-center overflow-hidden bg-background px-5'\n }\n >\n <div className=\"w-full\" style={{ maxWidth }}>\n {heading ? (\n <h2 className=\"mb-4 text-center text-[1.75rem] font-medium tracking-tight text-foreground\">\n {heading}\n </h2>\n ) : null}\n {subheading ? (\n <p className=\"mx-auto mb-9 max-w-md text-center text-sm leading-relaxed text-muted-foreground\">\n {subheading}\n </p>\n ) : (\n heading ? <div className=\"mb-7\" /> : null\n )}\n <AgentComposer\n className={composerClassName ?? 'vt-composer'}\n value={value}\n onChange={setValue}\n onSubmit={() => submit(value)}\n placeholder={placeholder}\n sendLabel={sendLabel}\n disabled={disabled}\n autoFocus\n canSubmitAttachmentsOnly\n accept={accept}\n attachments={attachments.composerFiles}\n onAttach={uploadUrl ? (files) => void attachments.addFiles(files) : undefined}\n onRejectFiles={onRejectFiles}\n onRemoveFile={attachments.removeAttachment}\n onRetryFile={attachments.retry}\n mention={\n mentions\n ? {\n ...mentions.mention,\n // Sidebar tone + hairline so the popover reads as app chrome\n // (the same treatment the picker menus get) instead of the\n // brighter overlay tone the component defaults to.\n popoverClassName:\n mentionPopoverClassName ??\n 'bg-surface-container-low border-[var(--md3-outline-variant)]',\n }\n : undefined\n }\n controls={modes ?? <ComposerModeControls planMode={planMode} />}\n trailing={\n agent ? <AgentSessionControls {...agent} /> : undefined\n }\n />\n {footer ? <div className=\"mt-7\">{footer}</div> : null}\n </div>\n </div>\n )\n}\n","import { ListChecks } from 'lucide-react'\nimport { cn } from '@tangle-network/sandbox-ui/utils'\nimport type { ComposerPlanModeSelection } from './types'\n\nexport interface ComposerModeControlsProps {\n /**\n * Plan-approval mode. Products pass it only when the selected backend can\n * propose a plan and wait for approval; omitted means nothing renders.\n */\n planMode?: ComposerPlanModeSelection\n}\n\n/**\n * The shared plan-mode toggle for the left side of an agent composer.\n * Plan mode is a behavioral switch, not part of the profile/backend/model/\n * thinking identity controls on the right.\n */\nexport function ComposerModeControls({ planMode }: ComposerModeControlsProps) {\n if (!planMode) return null\n\n return (\n <button\n type=\"button\"\n aria-pressed={planMode.enabled}\n disabled={planMode.saving}\n onClick={() => planMode.setEnabled(!planMode.enabled)}\n title=\"Plan mode: the agent proposes a plan you approve before it executes\"\n className={cn(\n // Inset ring: this chip sits in a composer row that clips its overflow,\n // so an outward ring loses three of its four sides. Only the offset is\n // overridden — width and colour stay with the tokens.\n 'inline-flex h-7 items-center gap-1 rounded-full border px-2.5 text-xs transition-colors focus-visible:[outline-offset:-2px]',\n planMode.enabled\n ? 'border-primary/50 bg-primary/10 text-primary'\n : 'border-border bg-transparent text-muted-foreground hover:text-foreground',\n planMode.saving && 'opacity-60',\n )}\n >\n <ListChecks className=\"h-3.5 w-3.5\" />\n Plan\n </button>\n )\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
1
+ {"version":3,"sources":["../../src/chat-react/entry-composer.tsx","../../src/chat-react/composer-mode-controls.tsx"],"sourcesContent":["import { useState, type ReactNode } from 'react'\nimport { AgentComposer } from '@tangle-network/sandbox-ui/chat'\nimport { ATTACHMENT_ACCEPT, useComposerAttachments } from '../web-react/use-composer-attachments'\nimport type { UseFileMentionsResult } from '../web-react/use-file-mentions'\nimport type { ChatAttachmentInput, FileMention } from '../chat-routes/wire'\nimport {\n AgentSessionControls,\n type AgentSessionControlsProps,\n} from '../web-react/agent-session-controls'\nimport { ComposerModeControls } from './composer-mode-controls'\nimport type { ComposerPlanModeSelection } from './types'\n\nexport interface EntryComposerProps {\n /** The one line above the input. Domain copy — always a product parameter. */\n heading?: ReactNode\n /** Optional supporting line under the heading. */\n subheading?: ReactNode\n placeholder?: string\n initialValue?: string\n sendLabel?: string\n disabled?: boolean\n /**\n * Agent identity (backend/model/effort). Pass it and the control row renders;\n * omit it and the composer ships without one. Omitting is a real choice for a\n * surface with nothing to choose — it should never be an oversight, which is\n * why this is one prop rather than a free-form slot a caller can forget.\n *\n * This is the CANONICAL picker cluster: `AgentSessionControls` from\n * `/web-react` (its model menu IS the canonical `ModelPicker`). The legacy\n * sandbox-ui adapter that used to back this prop was removed; the props\n * mapping for migrating a stored selection lives in\n * `docs/ui-picker-canon.md`.\n */\n agent?: AgentSessionControlsProps\n /**\n * Product-specific behavioral controls docked on the LEFT. A mode is an\n * on/off switch, not a value picker, so it sits apart from agent identity.\n * For the standard plan-approval control, prefer `planMode`.\n */\n modes?: ReactNode\n /**\n * Standard plan-approval mode. Pass only when the selected backend supports\n * it; omitted means the control is not rendered.\n */\n planMode?: ComposerPlanModeSelection\n /**\n * Upload endpoint for staged attachments. Omit and the attach affordance is\n * hidden — a composer with no place to put a file must not offer one.\n */\n uploadUrl?: string\n accept?: string\n onAttachmentError?: (reason: string) => void\n onRejectFiles?: Parameters<typeof AgentComposer>[0]['onRejectFiles']\n /**\n * `@`-file mentions. The hook itself is shared (`useFileMentions`), but the\n * cold-box policy around it — whether pressing `@` is worth starting a\n * sandbox — is a per-surface product call, so the result is injected rather\n * than created here.\n */\n mentions?: UseFileMentionsResult\n mentionPopoverClassName?: string\n /** Rendered under the composer: suggestion pills, an onboarding nudge, a\n * disclaimer. All domain copy. */\n footer?: ReactNode\n /** Block submit until a persisted selection has resolved against the catalog,\n * so the first turn cannot go out under the wrong model. Defaults to true. */\n ready?: boolean\n onSubmit: (\n prompt: string,\n attachments: ChatAttachmentInput[],\n mentions: FileMention[],\n ) => void\n className?: string\n composerClassName?: string\n /** Max width of the centered column. Defaults to the fleet's 820px. */\n maxWidth?: number\n}\n\n/**\n * `EntryComposer` — the centered \"what do you want to work on?\" surface a\n * product shows before a conversation exists (a new thread, an empty session,\n * a workspace overview).\n *\n * It exists because three products each grew their own, and they drifted into\n * three different capability sets rather than three different looks: one lost\n * the attach button, one lost the model and effort pickers entirely, one\n * hand-rolled a `<textarea>` and wired pickers from three separate packages\n * into one row. The composer, the pickers, the attachment queue and the mention\n * index were all ALREADY shared — what was not shared was the assembly, so\n * every product re-derived which controls an entry surface gets, and each\n * re-derivation dropped a different one.\n *\n * Mechanism lives here: layout, the attachment queue, the mention wiring, the\n * submit gate (nothing sends while an upload is pending or failed, or before\n * the model selection has resolved). Domain stays a parameter: `heading`,\n * `placeholder`, `footer` (suggestion pills, disclaimers) and the selections\n * themselves.\n *\n * The docked in-thread composer is NOT this component — it renders\n * `AgentComposer` directly with the same canonical `AgentSessionControls`,\n * because a docked composer has a transcript above it and no hero layout.\n */\nexport function EntryComposer({\n heading,\n subheading,\n placeholder,\n initialValue = '',\n sendLabel,\n disabled,\n agent,\n modes,\n planMode,\n uploadUrl,\n accept = ATTACHMENT_ACCEPT,\n onAttachmentError,\n onRejectFiles,\n mentions,\n mentionPopoverClassName,\n footer,\n ready = true,\n onSubmit,\n className,\n composerClassName,\n maxWidth = 820,\n}: EntryComposerProps) {\n const [value, setValue] = useState(initialValue)\n const attachments = useComposerAttachments({\n uploadUrl,\n enabled: !!uploadUrl,\n onReject: onAttachmentError,\n onError: onAttachmentError,\n })\n\n function submit(prompt: string) {\n if (!ready || disabled) return\n const next = prompt.trim()\n // A pending or failed upload must not send: the turn would reference an\n // attachment the store does not have. Surface why rather than no-op'ing.\n if (attachments.hasPending || attachments.hasError) {\n if (attachments.blockReason) onAttachmentError?.(attachments.blockReason)\n return\n }\n if (!next && attachments.references.length === 0) return\n onSubmit(next, attachments.references, mentions?.mentions ?? [])\n setValue('')\n attachments.clear()\n mentions?.clearMentions()\n }\n\n return (\n <div\n className={\n className ??\n 'relative flex flex-1 flex-col items-center justify-center overflow-hidden bg-background px-5'\n }\n >\n <div className=\"w-full\" style={{ maxWidth }}>\n {heading ? (\n <h2 className=\"mb-4 text-center text-[1.75rem] font-medium tracking-tight text-foreground\">\n {heading}\n </h2>\n ) : null}\n {subheading ? (\n <p className=\"mx-auto mb-9 max-w-md text-center text-sm leading-relaxed text-muted-foreground\">\n {subheading}\n </p>\n ) : (\n heading ? <div className=\"mb-7\" /> : null\n )}\n <AgentComposer\n className={composerClassName ?? 'vt-composer'}\n value={value}\n onChange={setValue}\n onSubmit={() => submit(value)}\n placeholder={placeholder}\n sendLabel={sendLabel}\n disabled={disabled}\n autoFocus\n canSubmitAttachmentsOnly\n accept={accept}\n attachments={attachments.composerFiles}\n onAttach={uploadUrl ? (files) => void attachments.addFiles(files) : undefined}\n onRejectFiles={onRejectFiles}\n onRemoveFile={attachments.removeAttachment}\n onRetryFile={attachments.retry}\n mention={\n mentions\n ? {\n ...mentions.mention,\n // Sidebar tone + hairline so the popover reads as app chrome\n // (the same treatment the picker menus get) instead of the\n // brighter overlay tone the component defaults to.\n popoverClassName:\n mentionPopoverClassName ??\n 'bg-surface-container-low border-[var(--md3-outline-variant)]',\n }\n : undefined\n }\n controls={modes ?? <ComposerModeControls planMode={planMode} />}\n trailing={\n agent ? <AgentSessionControls {...agent} /> : undefined\n }\n />\n {footer ? <div className=\"mt-7\">{footer}</div> : null}\n </div>\n </div>\n )\n}\n","import { ListChecks } from 'lucide-react'\nimport { cn } from '@tangle-network/sandbox-ui/utils'\nimport type { ComposerPlanModeSelection } from './types'\n\nexport interface ComposerModeControlsProps {\n /**\n * Plan-approval mode. Products pass it only when the selected backend can\n * propose a plan and wait for approval; omitted means nothing renders.\n */\n planMode?: ComposerPlanModeSelection\n}\n\n/**\n * The shared plan-mode toggle for the left side of an agent composer.\n * Plan mode is a behavioral switch, not part of the profile/backend/model/\n * thinking identity controls on the right.\n */\nexport function ComposerModeControls({ planMode }: ComposerModeControlsProps) {\n if (!planMode) return null\n\n return (\n <button\n type=\"button\"\n aria-pressed={planMode.enabled}\n disabled={planMode.saving}\n onClick={() => planMode.setEnabled(!planMode.enabled)}\n title=\"Plan mode: the agent proposes a plan you approve before it executes\"\n className={cn(\n // Inset ring: this chip sits in a composer row that clips its overflow,\n // so an outward ring loses three of its four sides. Only the offset is\n // overridden — width and colour stay with the tokens.\n 'inline-flex h-7 items-center gap-1 rounded-full border px-2.5 text-xs transition-colors focus-visible:[outline-offset:-2px]',\n planMode.enabled\n ? 'border-primary/50 bg-primary/10 text-primary'\n : 'border-border bg-transparent text-muted-foreground hover:text-foreground',\n planMode.saving && 'opacity-60',\n )}\n >\n <ListChecks className=\"h-3.5 w-3.5\" />\n Plan\n </button>\n )\n}\n"],"mappings":"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","names":["jsx","jsxs"]}
@@ -1,3 +1,6 @@
1
+ import {
2
+ RoleSelect
3
+ } from "./chunk-AULM4X7U.js";
1
4
  import {
2
5
  hasWorkspaceRole
3
6
  } from "./chunk-6XIAPIW6.js";
@@ -5,11 +8,6 @@ import {
5
8
  // src/teams-react/components/InvitationsPanel.tsx
6
9
  import { useState } from "react";
7
10
  import { jsx, jsxs } from "react/jsx-runtime";
8
- var ASSIGNABLE = [
9
- { value: "viewer", label: "Viewer" },
10
- { value: "editor", label: "Editor" },
11
- { value: "admin", label: "Admin" }
12
- ];
13
11
  var BADGE_BASE = "inline-flex items-center rounded border px-2 py-0.5 text-[11px] font-semibold uppercase tracking-[0.05em]";
14
12
  var STATUS_BADGE = {
15
13
  accepted: "border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]",
@@ -22,6 +20,9 @@ var EMAIL_BADGE = {
22
20
  not_sent: "border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]",
23
21
  failed: "border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]"
24
22
  };
23
+ function capitalize(text) {
24
+ return text.length === 0 ? text : text[0].toUpperCase() + text.slice(1);
25
+ }
25
26
  function InvitationsPanel({
26
27
  invitations,
27
28
  currentRole,
@@ -103,13 +104,11 @@ function InvitationsPanel({
103
104
  }
104
105
  ),
105
106
  /* @__PURE__ */ jsx(
106
- "select",
107
+ RoleSelect,
107
108
  {
108
109
  value: inviteRole,
109
- "aria-label": "Invite role",
110
- onChange: (event) => setInviteRole(event.target.value),
111
- className: "rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-2 py-1.5 text-xs text-[var(--text-secondary)]",
112
- children: ASSIGNABLE.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
110
+ ariaLabel: "Invite role",
111
+ onChange: setInviteRole
113
112
  }
114
113
  ),
115
114
  /* @__PURE__ */ jsx(
@@ -118,7 +117,7 @@ function InvitationsPanel({
118
117
  type: "button",
119
118
  onClick: () => void submitInvite(),
120
119
  disabled: inviting || !inviteEmail.trim(),
121
- className: "rounded bg-[var(--brand-primary)] px-3 py-1.5 text-sm text-white disabled:opacity-50",
120
+ className: "rounded bg-[var(--brand-primary)] px-3 py-1.5 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50",
122
121
  children: inviting ? "Inviting\u2026" : "Invite"
123
122
  }
124
123
  )
@@ -159,8 +158,8 @@ function InvitationRow({ invitation, canManage, busy, onCopy, onResend, onRevoke
159
158
  /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
160
159
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
161
160
  /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-[var(--text-primary)]", children: invitation.email }),
162
- /* @__PURE__ */ jsxs("p", { className: "text-xs capitalize text-[var(--text-muted)]", children: [
163
- invitation.permissions,
161
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-[var(--text-muted)]", children: [
162
+ capitalize(invitation.permissions),
164
163
  " \xB7 expires ",
165
164
  expiry
166
165
  ] }),
@@ -209,4 +208,4 @@ function InvitationRow({ invitation, canManage, busy, onCopy, onResend, onRevoke
209
208
  export {
210
209
  InvitationsPanel
211
210
  };
212
- //# sourceMappingURL=chunk-IUSO2XRE.js.map
211
+ //# sourceMappingURL=chunk-5BACNOVR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/teams-react/components/InvitationsPanel.tsx"],"sourcesContent":["/**\n * Email-invitation panel: invite by email + role, and a history of every\n * invitation (pending / accepted / expired / revoked) with its email-delivery\n * status. Pending rows expose copy-link / resend / revoke. Fully callback-driven\n * — the host supplies the data and the async `onInvite` / `onResend` / `onRevoke`\n * callbacks (backed by `./teams/invitations-api`), so this imports no app router,\n * fetch client, or toast. Styled with the shipped Tangle Quiet tokens (`var(--*)`).\n *\n * Mount alongside a list-only `MembersPanel` (`showInviteForm={false}`): the\n * members panel shows accepted members, this owns the invite flow + pending list.\n */\n\nimport { useState } from 'react'\nimport type { WorkspaceRole } from '../../teams/roles'\nimport { hasWorkspaceRole } from '../../teams/roles'\nimport type { InvitationView, InvitationsPanelProps } from '../contracts'\nimport { RoleSelect } from './RoleSelect'\n\nconst BADGE_BASE = 'inline-flex items-center rounded border px-2 py-0.5 text-[11px] font-semibold uppercase tracking-[0.05em]'\n\n// Lifecycle status: green = in, amber = awaiting, red = killed, muted = lapsed.\nconst STATUS_BADGE: Record<InvitationView['status'], string> = {\n accepted: 'border-[var(--surface-success-border)] bg-[var(--surface-success-bg)] text-[var(--surface-success-text)]',\n pending: 'border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]',\n revoked: 'border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]',\n expired: 'border-[var(--border-default)] text-[var(--text-muted)]',\n}\n\n// Delivery status: red = bounced, amber = not yet attempted, muted = the normal happy path.\nconst EMAIL_BADGE: Record<InvitationView['emailStatus'], string> = {\n sent: 'border-[var(--border-default)] text-[var(--text-muted)]',\n not_sent: 'border-[var(--surface-warning-border)] bg-[var(--surface-warning-bg)] text-[var(--surface-warning-text)]',\n failed: 'border-[var(--surface-danger-border)] bg-[var(--surface-danger-bg)] text-[var(--surface-danger-text)]',\n}\n\n/** Uppercase the role's first letter in JS (not CSS `capitalize`, which would\n * also hit the lowercase \"expires\" in the same line). */\nfunction capitalize(text: string): string {\n return text.length === 0 ? text : text[0]!.toUpperCase() + text.slice(1)\n}\n\nexport function InvitationsPanel({\n invitations,\n currentRole,\n onInvite,\n onResend,\n onRevoke,\n onCopy,\n onNotice,\n}: InvitationsPanelProps) {\n const [inviteEmail, setInviteEmail] = useState('')\n const [inviteRole, setInviteRole] = useState<WorkspaceRole>('editor')\n const [inviting, setInviting] = useState(false)\n const [busyId, setBusyId] = useState<string | null>(null)\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 await onInvite({ email, role: inviteRole })\n notify('success', `Invitation sent to ${email}`)\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 copyLink(inviteUrl: string) {\n try {\n if (onCopy) await onCopy({ inviteUrl })\n else await navigator.clipboard.writeText(inviteUrl)\n notify('success', 'Invite link copied')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to copy link')\n }\n }\n\n async function resend(invitationId: string) {\n setBusyId(invitationId)\n try {\n await onResend({ invitationId })\n notify('success', 'Invitation resent')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to resend invitation')\n } finally {\n setBusyId(null)\n }\n }\n\n async function revoke(invitationId: string) {\n setBusyId(invitationId)\n try {\n await onRevoke({ invitationId })\n notify('success', 'Invitation revoked')\n } catch (err) {\n notify('error', err instanceof Error ? err.message : 'Failed to revoke invitation')\n } finally {\n setBusyId(null)\n }\n }\n\n return (\n <section className=\"flex flex-col gap-4\">\n {canManage && (\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"text-xs font-medium text-[var(--text-muted)]\">Invite by email</label>\n <div className=\"flex gap-2\">\n <input\n type=\"email\"\n placeholder=\"colleague@example.com\"\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\n <div className=\"flex flex-col gap-2 border-t border-[var(--border-default)] pt-3\">\n <div className=\"flex items-center justify-between\">\n <h4 className=\"text-xs font-semibold text-[var(--text-primary)]\">Invitation history</h4>\n <span className=\"text-xs text-[var(--text-muted)]\">{invitations.length} total</span>\n </div>\n {invitations.length === 0 ? (\n <p className=\"text-sm text-[var(--text-muted)]\">No invitations yet.</p>\n ) : (\n invitations.map((invitation) => (\n <InvitationRow\n key={invitation.id}\n invitation={invitation}\n canManage={canManage}\n busy={busyId === invitation.id}\n onCopy={() => void copyLink(invitation.inviteUrl)}\n onResend={() => void resend(invitation.id)}\n onRevoke={() => void revoke(invitation.id)}\n />\n ))\n )}\n </div>\n </section>\n )\n}\n\ninterface InvitationRowProps {\n invitation: InvitationView\n canManage: boolean\n busy: boolean\n onCopy(): void\n onResend(): void\n onRevoke(): void\n}\n\nfunction InvitationRow({ invitation, canManage, busy, onCopy, onResend, onRevoke }: InvitationRowProps) {\n const isPending = invitation.status === 'pending'\n const emailFailed = invitation.emailStatus === 'failed'\n const expiry = new Date(invitation.expiresAt).toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n })\n\n return (\n <div className=\"rounded-lg border border-[var(--border-default)] bg-[var(--bg-input)] p-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium text-[var(--text-primary)]\">{invitation.email}</p>\n <p className=\"text-xs text-[var(--text-muted)]\">\n {capitalize(invitation.permissions)} · expires {expiry}\n </p>\n {emailFailed && (\n <p className=\"mt-1 text-xs text-[var(--surface-danger-text)]\">Email was not sent — copy the link to share it.</p>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <span className={`${BADGE_BASE} ${STATUS_BADGE[invitation.status]}`}>\n {invitation.status}\n </span>\n <span className={`${BADGE_BASE} ${EMAIL_BADGE[invitation.emailStatus]}`}>\n {invitation.emailStatus.replace('_', ' ')}\n </span>\n </div>\n </div>\n {canManage && isPending && (\n <div className=\"mt-3 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={onCopy}\n disabled={busy}\n className=\"rounded border border-[var(--border-default)] px-2 py-1 text-xs text-[var(--text-secondary)] hover:text-[var(--text-primary)] disabled:opacity-50\"\n >\n Copy link\n </button>\n <button\n type=\"button\"\n onClick={onResend}\n disabled={busy}\n className=\"rounded border border-[var(--border-default)] px-2 py-1 text-xs text-[var(--text-secondary)] hover:text-[var(--text-primary)] disabled:opacity-50\"\n >\n {busy ? 'Working…' : 'Resend'}\n </button>\n <button\n type=\"button\"\n onClick={onRevoke}\n disabled={busy}\n className=\"rounded px-2 py-1 text-xs text-[var(--text-danger)] hover:bg-[var(--border-default)] disabled:opacity-50\"\n >\n Revoke\n </button>\n </div>\n )}\n </div>\n )\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,77 @@
1
+ // src/teams-react/components/RoleSelect.tsx
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ var ASSIGNABLE_ROLES = [
5
+ { value: "viewer", label: "Viewer" },
6
+ { value: "editor", label: "Editor" },
7
+ { value: "admin", label: "Admin" }
8
+ ];
9
+ function ChevronDownGlyph({ className }) {
10
+ return /* @__PURE__ */ jsx("svg", { className, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) });
11
+ }
12
+ function RoleSelect({ value, onChange, ariaLabel, disabled }) {
13
+ const [open, setOpen] = useState(false);
14
+ const ref = useRef(null);
15
+ const current = ASSIGNABLE_ROLES.find((option) => option.value === value);
16
+ useEffect(() => {
17
+ if (!open) return;
18
+ function onDocPointer(event) {
19
+ if (ref.current && !ref.current.contains(event.target)) setOpen(false);
20
+ }
21
+ function onKey(event) {
22
+ if (event.key === "Escape") setOpen(false);
23
+ }
24
+ document.addEventListener("pointerdown", onDocPointer);
25
+ document.addEventListener("keydown", onKey);
26
+ return () => {
27
+ document.removeEventListener("pointerdown", onDocPointer);
28
+ document.removeEventListener("keydown", onKey);
29
+ };
30
+ }, [open]);
31
+ return /* @__PURE__ */ jsxs("div", { ref, className: "relative", children: [
32
+ /* @__PURE__ */ jsxs(
33
+ "button",
34
+ {
35
+ type: "button",
36
+ disabled,
37
+ "aria-haspopup": "listbox",
38
+ "aria-expanded": open,
39
+ "aria-label": ariaLabel,
40
+ onClick: () => setOpen((v) => !v),
41
+ className: "flex items-center justify-between gap-1.5 rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-2 py-1.5 text-xs text-[var(--text-secondary)] hover:border-[var(--brand-primary)] disabled:cursor-default disabled:opacity-40",
42
+ children: [
43
+ /* @__PURE__ */ jsx("span", { children: current?.label ?? value }),
44
+ /* @__PURE__ */ jsx(ChevronDownGlyph, { className: "h-3 w-3 text-[var(--text-muted)]" })
45
+ ]
46
+ }
47
+ ),
48
+ open ? /* @__PURE__ */ jsx(
49
+ "div",
50
+ {
51
+ role: "listbox",
52
+ "aria-label": ariaLabel,
53
+ className: "absolute right-0 top-full z-50 mt-1 flex w-32 flex-col rounded border border-[var(--card-edge)] bg-[hsl(var(--popover))] py-1 shadow-[var(--shadow-overlay)]",
54
+ children: ASSIGNABLE_ROLES.map((option) => /* @__PURE__ */ jsx(
55
+ "button",
56
+ {
57
+ type: "button",
58
+ role: "option",
59
+ "aria-selected": option.value === value,
60
+ onClick: () => {
61
+ onChange(option.value);
62
+ setOpen(false);
63
+ },
64
+ className: `px-3 py-1.5 text-left text-xs hover:bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)] ${option.value === value ? "text-[var(--brand-primary)]" : "text-[var(--text-primary)]"}`,
65
+ children: option.label
66
+ },
67
+ option.value
68
+ ))
69
+ }
70
+ ) : null
71
+ ] });
72
+ }
73
+
74
+ export {
75
+ RoleSelect
76
+ };
77
+ //# sourceMappingURL=chunk-AULM4X7U.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/teams-react/components/RoleSelect.tsx"],"sourcesContent":["/**\n * Shared role dropdown for the teams panels — the popover-listbox pattern the\n * design system requires in place of a native `<select>` (same contract as the\n * design-canvas Toolbar's SelectControl: bordered trigger, chevron glyph, and a\n * `role=\"listbox\"` panel on the L3 popover surface with click-outside /\n * Escape-to-close). Both panels assign from the same workspace-role set, so the\n * options live here too.\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport type { WorkspaceRole } from '../../teams/roles'\n\nconst ASSIGNABLE_ROLES: ReadonlyArray<{ value: WorkspaceRole; label: string }> = [\n { value: 'viewer', label: 'Viewer' },\n { value: 'editor', label: 'Editor' },\n { value: 'admin', label: 'Admin' },\n]\n\nfunction ChevronDownGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n )\n}\n\nexport interface RoleSelectProps {\n value: WorkspaceRole\n onChange(role: WorkspaceRole): void\n /** Accessible name (no visible label — the panels label the row). */\n ariaLabel: string\n disabled?: boolean\n}\n\nexport function RoleSelect({ value, onChange, ariaLabel, disabled }: RoleSelectProps) {\n const [open, setOpen] = useState(false)\n const ref = useRef<HTMLDivElement>(null)\n const current = ASSIGNABLE_ROLES.find((option) => option.value === value)\n\n useEffect(() => {\n if (!open) return\n function onDocPointer(event: PointerEvent) {\n if (ref.current && !ref.current.contains(event.target as Node)) setOpen(false)\n }\n function onKey(event: KeyboardEvent) {\n if (event.key === 'Escape') setOpen(false)\n }\n document.addEventListener('pointerdown', onDocPointer)\n document.addEventListener('keydown', onKey)\n return () => {\n document.removeEventListener('pointerdown', onDocPointer)\n document.removeEventListener('keydown', onKey)\n }\n }, [open])\n\n return (\n <div ref={ref} className=\"relative\">\n <button\n type=\"button\"\n disabled={disabled}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-label={ariaLabel}\n onClick={() => setOpen((v) => !v)}\n className=\"flex items-center justify-between gap-1.5 rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-2 py-1.5 text-xs text-[var(--text-secondary)] hover:border-[var(--brand-primary)] disabled:cursor-default disabled:opacity-40\"\n >\n <span>{current?.label ?? value}</span>\n <ChevronDownGlyph className=\"h-3 w-3 text-[var(--text-muted)]\" />\n </button>\n {open ? (\n <div\n role=\"listbox\"\n aria-label={ariaLabel}\n className=\"absolute right-0 top-full z-50 mt-1 flex w-32 flex-col rounded border border-[var(--card-edge)] bg-[hsl(var(--popover))] py-1 shadow-[var(--shadow-overlay)]\"\n >\n {ASSIGNABLE_ROLES.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n role=\"option\"\n aria-selected={option.value === value}\n onClick={() => {\n onChange(option.value)\n setOpen(false)\n }}\n className={`px-3 py-1.5 text-left text-xs hover:bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)] ${\n option.value === value ? 'text-[var(--brand-primary)]' : 'text-[var(--text-primary)]'\n }`}\n >\n {option.label}\n </button>\n ))}\n </div>\n ) : null}\n </div>\n )\n}\n"],"mappings":";AASA,SAAS,WAAW,QAAQ,gBAAgB;AAYtC,cAoCA,YApCA;AATN,IAAM,mBAA2E;AAAA,EAC/E,EAAE,OAAO,UAAU,OAAO,SAAS;AAAA,EACnC,EAAE,OAAO,UAAU,OAAO,SAAS;AAAA,EACnC,EAAE,OAAO,SAAS,OAAO,QAAQ;AACnC;AAEA,SAAS,iBAAiB,EAAE,UAAU,GAA2B;AAC/D,SACE,oBAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ,8BAAC,UAAK,GAAE,gBAAe,GACzB;AAEJ;AAUO,SAAS,WAAW,EAAE,OAAO,UAAU,WAAW,SAAS,GAAoB;AACpF,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,MAAM,OAAuB,IAAI;AACvC,QAAM,UAAU,iBAAiB,KAAK,CAAC,WAAW,OAAO,UAAU,KAAK;AAExE,YAAU,MAAM;AACd,QAAI,CAAC,KAAM;AACX,aAAS,aAAa,OAAqB;AACzC,UAAI,IAAI,WAAW,CAAC,IAAI,QAAQ,SAAS,MAAM,MAAc,EAAG,SAAQ,KAAK;AAAA,IAC/E;AACA,aAAS,MAAM,OAAsB;AACnC,UAAI,MAAM,QAAQ,SAAU,SAAQ,KAAK;AAAA,IAC3C;AACA,aAAS,iBAAiB,eAAe,YAAY;AACrD,aAAS,iBAAiB,WAAW,KAAK;AAC1C,WAAO,MAAM;AACX,eAAS,oBAAoB,eAAe,YAAY;AACxD,eAAS,oBAAoB,WAAW,KAAK;AAAA,IAC/C;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAET,SACE,qBAAC,SAAI,KAAU,WAAU,YACvB;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL;AAAA,QACA,iBAAc;AAAA,QACd,iBAAe;AAAA,QACf,cAAY;AAAA,QACZ,SAAS,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;AAAA,QAChC,WAAU;AAAA,QAEV;AAAA,8BAAC,UAAM,mBAAS,SAAS,OAAM;AAAA,UAC/B,oBAAC,oBAAiB,WAAU,oCAAmC;AAAA;AAAA;AAAA,IACjE;AAAA,IACC,OACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,cAAY;AAAA,QACZ,WAAU;AAAA,QAET,2BAAiB,IAAI,CAAC,WACrB;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,iBAAe,OAAO,UAAU;AAAA,YAChC,SAAS,MAAM;AACb,uBAAS,OAAO,KAAK;AACrB,sBAAQ,KAAK;AAAA,YACf;AAAA,YACA,WAAW,oGACT,OAAO,UAAU,QAAQ,gCAAgC,4BAC3D;AAAA,YAEC,iBAAO;AAAA;AAAA,UAZH,OAAO;AAAA,QAad,CACD;AAAA;AAAA,IACH,IACE;AAAA,KACN;AAEJ;","names":[]}