@tangle-network/agent-app 0.45.47 → 0.45.49

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 (94) hide show
  1. package/dist/{DesignCanvas-P2ZVK6FM.js → DesignCanvas-JRBCAWHW.js} +2 -2
  2. package/dist/{DesignCanvasEditor-DGNCA54P.js → DesignCanvasEditor-J2OLS22G.js} +4 -4
  3. package/dist/{IntakeInterview-3ZIQMZS6.js → IntakeInterview-OKT6DCBN.js} +2 -2
  4. package/dist/InvitationsPanel-OFCYVSAE.js +9 -0
  5. package/dist/InviteAcceptPage-BDOO4JSE.js +7 -0
  6. package/dist/MembersPanel-3ZK2GCMC.js +9 -0
  7. package/dist/{TimelineEditor-OTNPJ3SN.js → TimelineEditor-52YEPDX5.js} +2 -2
  8. package/dist/VaultPane-IGJKB6CX.js +7 -0
  9. package/dist/assistant/index.d.ts +36 -11
  10. package/dist/assistant/index.js +235 -90
  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 +2 -1
  14. package/dist/chat-react/index.js.map +1 -1
  15. package/dist/{chunk-TL37E5YY.js → chunk-33BN7P57.js} +6 -6
  16. package/dist/chunk-33BN7P57.js.map +1 -0
  17. package/dist/{chunk-IUSO2XRE.js → chunk-5MI2SKQH.js} +14 -15
  18. package/dist/chunk-5MI2SKQH.js.map +1 -0
  19. package/dist/{chunk-65OZT566.js → chunk-7LJYCHXK.js} +29 -27
  20. package/dist/chunk-7LJYCHXK.js.map +1 -0
  21. package/dist/chunk-AULM4X7U.js +77 -0
  22. package/dist/chunk-AULM4X7U.js.map +1 -0
  23. package/dist/{chunk-GNPN6ED4.js → chunk-CJHIAZKK.js} +128 -166
  24. package/dist/chunk-CJHIAZKK.js.map +1 -0
  25. package/dist/{chunk-54FYYXAX.js → chunk-ENLRJYVW.js} +21 -18
  26. package/dist/chunk-ENLRJYVW.js.map +1 -0
  27. package/dist/{chunk-F4XLTVQ3.js → chunk-GRDLGQ6A.js} +19 -18
  28. package/dist/chunk-GRDLGQ6A.js.map +1 -0
  29. package/dist/{chunk-BT4QFCTB.js → chunk-HSBJB46C.js} +13 -1
  30. package/dist/chunk-HSBJB46C.js.map +1 -0
  31. package/dist/{chunk-VOJB7MVG.js → chunk-I2CA5N7V.js} +70 -28
  32. package/dist/chunk-I2CA5N7V.js.map +1 -0
  33. package/dist/chunk-MLG6XKPV.js +44 -0
  34. package/dist/chunk-MLG6XKPV.js.map +1 -0
  35. package/dist/{chunk-IN6LNZ7O.js → chunk-Q2QKEV6J.js} +7 -7
  36. package/dist/{chunk-IN6LNZ7O.js.map → chunk-Q2QKEV6J.js.map} +1 -1
  37. package/dist/{chunk-B62EZF52.js → chunk-UMV45ZDF.js} +3 -3
  38. package/dist/{chunk-JJJEO4ND.js → chunk-VWTODXRW.js} +2 -2
  39. package/dist/{chunk-JJJEO4ND.js.map → chunk-VWTODXRW.js.map} +1 -1
  40. package/dist/{chunk-VCPZ3HTN.js → chunk-W7P5KV6O.js} +51 -27
  41. package/dist/chunk-W7P5KV6O.js.map +1 -0
  42. package/dist/{chunk-ZX5UT4HF.js → chunk-ZAKKG6WI.js} +354 -273
  43. package/dist/chunk-ZAKKG6WI.js.map +1 -0
  44. package/dist/{chunk-PE67O2LH.js → chunk-ZGY6QB4F.js} +21 -16
  45. package/dist/chunk-ZGY6QB4F.js.map +1 -0
  46. package/dist/design-canvas-react/index.js +18 -16
  47. package/dist/design-canvas-react/index.js.map +1 -1
  48. package/dist/design-canvas-react/lazy.js +1 -1
  49. package/dist/intakes-react/index.js +1 -1
  50. package/dist/intakes-react/lazy.js +1 -1
  51. package/dist/platform/index.d.ts +1 -1
  52. package/dist/platform/index.js +16 -16
  53. package/dist/platform/index.js.map +1 -1
  54. package/dist/sequences-react/index.js +2 -2
  55. package/dist/studio/index.d.ts +13 -1
  56. package/dist/studio/index.js +13 -1
  57. package/dist/studio-react/index.js +115 -78
  58. package/dist/studio-react/index.js.map +1 -1
  59. package/dist/studio-react/studio.css +0 -56
  60. package/dist/teams-react/index.js +4 -3
  61. package/dist/teams-react/lazy.js +3 -3
  62. package/dist/theme/index.js +1 -1
  63. package/dist/theme/tokens.css +15 -13
  64. package/dist/vault/index.js +1 -1
  65. package/dist/vault/lazy.js +1 -1
  66. package/dist/web-react/index.d.ts +16 -12
  67. package/dist/web-react/index.js +6 -4
  68. package/dist/work-product-react/index.js +9 -27
  69. package/dist/work-product-react/index.js.map +1 -1
  70. package/package.json +3 -3
  71. package/dist/InvitationsPanel-F56UOQXA.js +0 -8
  72. package/dist/InviteAcceptPage-P3QJC3C6.js +0 -7
  73. package/dist/MembersPanel-XNLF3SA4.js +0 -8
  74. package/dist/VaultPane-MUWGYVJ7.js +0 -7
  75. package/dist/chunk-54FYYXAX.js.map +0 -1
  76. package/dist/chunk-65OZT566.js.map +0 -1
  77. package/dist/chunk-BT4QFCTB.js.map +0 -1
  78. package/dist/chunk-F4XLTVQ3.js.map +0 -1
  79. package/dist/chunk-GNPN6ED4.js.map +0 -1
  80. package/dist/chunk-IUSO2XRE.js.map +0 -1
  81. package/dist/chunk-PE67O2LH.js.map +0 -1
  82. package/dist/chunk-TL37E5YY.js.map +0 -1
  83. package/dist/chunk-VCPZ3HTN.js.map +0 -1
  84. package/dist/chunk-VOJB7MVG.js.map +0 -1
  85. package/dist/chunk-ZX5UT4HF.js.map +0 -1
  86. /package/dist/{DesignCanvas-P2ZVK6FM.js.map → DesignCanvas-JRBCAWHW.js.map} +0 -0
  87. /package/dist/{DesignCanvasEditor-DGNCA54P.js.map → DesignCanvasEditor-J2OLS22G.js.map} +0 -0
  88. /package/dist/{IntakeInterview-3ZIQMZS6.js.map → IntakeInterview-OKT6DCBN.js.map} +0 -0
  89. /package/dist/{InvitationsPanel-F56UOQXA.js.map → InvitationsPanel-OFCYVSAE.js.map} +0 -0
  90. /package/dist/{InviteAcceptPage-P3QJC3C6.js.map → InviteAcceptPage-BDOO4JSE.js.map} +0 -0
  91. /package/dist/{MembersPanel-XNLF3SA4.js.map → MembersPanel-3ZK2GCMC.js.map} +0 -0
  92. /package/dist/{TimelineEditor-OTNPJ3SN.js.map → TimelineEditor-52YEPDX5.js.map} +0 -0
  93. /package/dist/{VaultPane-MUWGYVJ7.js.map → VaultPane-IGJKB6CX.js.map} +0 -0
  94. /package/dist/{chunk-B62EZF52.js.map → chunk-UMV45ZDF.js.map} +0 -0
@@ -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/MembersPanel.tsx
6
9
  import { useState } from "react";
7
10
  import { Fragment, 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
  function MembersPanel({
14
12
  members,
15
13
  currentRole,
@@ -61,6 +59,13 @@ function MembersPanel({
61
59
  }
62
60
  }
63
61
  return /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-4", children: [
62
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
63
+ /* @__PURE__ */ jsx("h4", { className: "text-xs font-semibold text-[var(--text-primary)]", children: "Members" }),
64
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-[var(--text-muted)]", children: [
65
+ members.length,
66
+ " total"
67
+ ] })
68
+ ] }),
64
69
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
65
70
  members.map((member) => /* @__PURE__ */ jsx(
66
71
  MemberRow,
@@ -92,13 +97,11 @@ function MembersPanel({
92
97
  }
93
98
  ),
94
99
  /* @__PURE__ */ jsx(
95
- "select",
100
+ RoleSelect,
96
101
  {
97
102
  value: inviteRole,
98
- "aria-label": "Invite role",
99
- onChange: (event) => setInviteRole(event.target.value),
100
- className: "rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-2 py-1.5 text-xs text-[var(--text-secondary)]",
101
- children: ASSIGNABLE.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
103
+ ariaLabel: "Invite role",
104
+ onChange: setInviteRole
102
105
  }
103
106
  ),
104
107
  /* @__PURE__ */ jsx(
@@ -107,7 +110,7 @@ function MembersPanel({
107
110
  type: "button",
108
111
  onClick: () => void submitInvite(),
109
112
  disabled: inviting || !inviteEmail.trim(),
110
- className: "rounded bg-[var(--brand-primary)] px-3 py-1.5 text-sm text-white disabled:opacity-50",
113
+ className: "rounded bg-[var(--brand-primary)] px-3 py-1.5 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50",
111
114
  children: inviting ? "Inviting\u2026" : "Invite"
112
115
  }
113
116
  )
@@ -127,20 +130,18 @@ function MemberRow({ member, canManage, onChangeRole, onRemove }) {
127
130
  /* @__PURE__ */ jsxs("div", { children: [
128
131
  /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-2 text-sm font-medium text-[var(--text-primary)]", children: [
129
132
  label,
130
- pending && /* @__PURE__ */ jsx("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)]", children: "Pending" })
133
+ pending && /* @__PURE__ */ jsx("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)]", children: "Pending" })
131
134
  ] }),
132
135
  member.name && member.email && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: member.email })
133
136
  ] })
134
137
  ] }),
135
138
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: isOwner ? /* @__PURE__ */ jsx("span", { className: "rounded border border-[var(--border-default)] px-2 py-0.5 text-xs text-[var(--text-secondary)]", children: member.inherited ? "Org Admin" : "Owner" }) : editable ? /* @__PURE__ */ jsxs(Fragment, { children: [
136
139
  /* @__PURE__ */ jsx(
137
- "select",
140
+ RoleSelect,
138
141
  {
139
142
  value: member.role,
140
- "aria-label": `Role for ${label}`,
141
- onChange: (event) => onChangeRole(member.id, event.target.value),
142
- className: "rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-2 py-1 text-xs text-[var(--text-secondary)]",
143
- children: ASSIGNABLE.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
143
+ ariaLabel: `Role for ${label}`,
144
+ onChange: (role) => onChangeRole(member.id, role)
144
145
  }
145
146
  ),
146
147
  /* @__PURE__ */ jsx(
@@ -160,4 +161,4 @@ function MemberRow({ member, canManage, onChangeRole, onRemove }) {
160
161
  export {
161
162
  MembersPanel
162
163
  };
163
- //# sourceMappingURL=chunk-F4XLTVQ3.js.map
164
+ //# sourceMappingURL=chunk-GRDLGQ6A.js.map
@@ -0,0 +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-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":[]}
@@ -11,6 +11,12 @@ var DESTINATIONS = [
11
11
  { id: "x", label: "X", providerIds: ["twitter"], fields: "Short copy, mentions, thread option" }
12
12
  ];
13
13
  var CADENCES = ["Manual approval", "Publish now", "Daily creative drop", "Weekly series"];
14
+ var IMAGE_QUALITIES = ["low", "medium", "high", "auto"];
15
+ var IMAGE_SIZE_HINT = "1024x1024, 1536x1024, 1024x1536, or auto";
16
+ var IMAGE_SIZE_PATTERN = String.raw`(\d{3,4}x\d{3,4}|auto)`;
17
+ var VIDEO_DURATIONS = ["4", "6", "8", "10", "12"];
18
+ var VIDEO_RESOLUTIONS = ["720p", "1080p"];
19
+ var VIDEO_ASPECT_RATIOS = ["16:9", "9:16", "1:1"];
14
20
  var MIN_IMAGE_COUNT = 1;
15
21
  var MAX_IMAGE_COUNT = 4;
16
22
  function relativeTime(date) {
@@ -266,6 +272,12 @@ export {
266
272
  isGenerationType,
267
273
  DESTINATIONS,
268
274
  CADENCES,
275
+ IMAGE_QUALITIES,
276
+ IMAGE_SIZE_HINT,
277
+ IMAGE_SIZE_PATTERN,
278
+ VIDEO_DURATIONS,
279
+ VIDEO_RESOLUTIONS,
280
+ VIDEO_ASPECT_RATIOS,
269
281
  MIN_IMAGE_COUNT,
270
282
  MAX_IMAGE_COUNT,
271
283
  relativeTime,
@@ -290,4 +302,4 @@ export {
290
302
  failedOptimisticGeneration,
291
303
  normalizeImageCount
292
304
  };
293
- //# sourceMappingURL=chunk-BT4QFCTB.js.map
305
+ //# sourceMappingURL=chunk-HSBJB46C.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/studio/generation.ts"],"sourcesContent":["// Minimal structural view of a connected integration — `isDestinationConnected`\n// only reads the connection status and the provider/connector identifiers, so\n// the logic layer declares its own shape instead of depending on the\n// design-system package. A sandbox-ui `IntegrationConnection` is assignable to\n// this; `connectorId` is optional because the engine type leaves it undefined\n// for connections established without a named connector.\n/** Define the structure for a studio integration connection with status and provider identifiers */\nexport interface StudioIntegrationConnection {\n status: string\n providerId: string\n connectorId?: string\n}\n\n/** Define generation categories for media including image, video, speech, avatar, and transcription */\nexport type GenerationType = 'image' | 'video' | 'speech' | 'avatar' | 'transcription'\n\n/** Define possible states representing the progress of a generation process */\nexport type GenerationStatus = 'pending' | 'running' | 'succeeded' | 'failed'\n\n/** Define possible status values for a media model's availability and accessibility */\nexport type MediaModelStatus = 'available' | 'limited' | 'unavailable'\n\n/** Define the structure for a generation entity including its metadata and creation details */\nexport interface Generation {\n id: string\n type: string\n prompt: string\n result: string | null\n model: string | null\n cost: number | null\n createdAt: Date | null\n metadata: Record<string, unknown> | null\n}\n\n/** Describe media model option properties including id, name, type, status, and optional provider and reason */\nexport interface MediaModelOption {\n id: string\n name: string\n provider?: string\n type: GenerationType\n status: MediaModelStatus\n reason?: string\n}\n\n/** Represent media model catalog with default values, model options, and optional error message */\nexport interface MediaModelCatalogResponse {\n defaults: Record<GenerationType, string>\n models: Record<GenerationType, MediaModelOption[]>\n error?: string\n}\n\n/** Define the structure for configuring package publishing details and evaluation criteria */\nexport interface PublishPackage {\n caption: string\n description: string\n mentions: string[]\n destinations: string[]\n cadence: string\n workflowDraft: boolean\n evalContract: {\n artifactType: string\n deterministicChecks: string[]\n }\n}\n\n/** Define a destination for publishing content with identifiers, label, provider IDs, and fields */\nexport interface PublishDestination {\n id: string\n label: string\n providerIds: string[]\n fields: string\n}\n\n// Order drives the type filter tabs and the composer segmented control\n/** Provide an array of supported generation types for media and content processing */\nexport const GENERATION_TYPES: readonly GenerationType[] = ['image', 'video', 'avatar', 'speech', 'transcription']\n\n/** Resolve whether a string value matches a valid GenerationType */\nexport function isGenerationType(value: string): value is GenerationType {\n return (GENERATION_TYPES as readonly string[]).includes(value)\n}\n\n/** List available social media platforms with their publishing fields and provider identifiers */\nexport const DESTINATIONS: PublishDestination[] = [\n { id: 'instagram', label: 'Instagram', providerIds: ['instagram'], fields: 'Caption, hashtags, crop' },\n { id: 'tiktok', label: 'TikTok', providerIds: ['tiktok'], fields: 'Caption, audio note, vertical video' },\n { id: 'youtube-shorts', label: 'YouTube Shorts', providerIds: ['youtube'], fields: 'Title, description, vertical video' },\n { id: 'linkedin', label: 'LinkedIn', providerIds: ['linkedin'], fields: 'Caption, link, creator/company page' },\n { id: 'x', label: 'X', providerIds: ['twitter'], fields: 'Short copy, mentions, thread option' },\n]\n\n/** Provide an array of predefined cadence options for scheduling or approval processes */\nexport const CADENCES = ['Manual approval', 'Publish now', 'Daily creative drop', 'Weekly series']\n\n// Enumerated generation options — the values the tangle media models (OpenAI-shaped\n// image/video APIs) actually accept. Composers render these as selects so an\n// enumerated field can never carry free text the provider would reject.\n/** Provide the valid image quality values for generation requests */\nexport const IMAGE_QUALITIES = ['low', 'medium', 'high', 'auto'] as const\n/** Provide a placeholder hint listing the accepted image size values */\nexport const IMAGE_SIZE_HINT = '1024x1024, 1536x1024, 1024x1536, or auto'\n/** Provide a validation pattern for the free-form image size field (WxH or auto) */\nexport const IMAGE_SIZE_PATTERN = String.raw`(\\d{3,4}x\\d{3,4}|auto)`\n/** Provide the valid video duration values (seconds) for generation requests */\nexport const VIDEO_DURATIONS = ['4', '6', '8', '10', '12'] as const\n/** Provide the valid video resolution values for generation requests */\nexport const VIDEO_RESOLUTIONS = ['720p', '1080p'] as const\n/** Provide the valid video aspect ratio values for generation requests */\nexport const VIDEO_ASPECT_RATIOS = ['16:9', '9:16', '1:1'] as const\n\n/** Define the minimum number of images required for processing or validation */\nexport const MIN_IMAGE_COUNT = 1\n/** Define the maximum number of images allowed for upload or display */\nexport const MAX_IMAGE_COUNT = 4\n\n/** Resolve a human-readable relative time string from a given date or return an empty string if null */\nexport function relativeTime(date: Date | null): string {\n if (!date) return ''\n const now = Date.now()\n const diff = now - new Date(date).getTime()\n const minutes = Math.floor(diff / 60000)\n if (minutes < 1) return 'just now'\n if (minutes < 60) return `${minutes}m ago`\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return `${hours}h ago`\n const days = Math.floor(hours / 24)\n if (days < 7) return `${days}d ago`\n return new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })\n}\n\n/** Resolve the output directory path based on the specified generation type */\nexport function outputPathFor(type: GenerationType): string {\n if (type === 'image') return 'generated/images'\n if (type === 'video') return 'generated/videos'\n if (type === 'avatar') return 'generated/avatars'\n if (type === 'speech') return 'generated/audio'\n return 'generated/transcripts'\n}\n\n/** Resolve the vault path string from a Generation object or return null if unavailable */\nexport function generationVaultPath(generation: Generation): string | null {\n const value = generation.metadata?.vaultPath\n return typeof value === 'string' && value.trim() ? value.trim() : null\n}\n\n/** Build a PublishPackage object from caption, description, mentions, cadence, and destinations inputs */\nexport function buildPublishPackage({\n caption,\n postDescription,\n mentions,\n cadence,\n destinations,\n}: {\n caption: string\n postDescription: string\n mentions: string\n cadence: string\n destinations: string[]\n}): PublishPackage | null {\n const trimmedCaption = caption.trim()\n const trimmedDescription = postDescription.trim()\n const parsedMentions = mentions.split(',').map((item) => item.trim()).filter(Boolean)\n const selectedDestinations = destinations.filter(Boolean)\n const trimmedCadence = cadence.trim() || 'Manual approval'\n const hasPublishContent = Boolean(\n trimmedCaption\n || trimmedDescription\n || parsedMentions.length > 0\n || selectedDestinations.length > 0\n || trimmedCadence !== 'Manual approval',\n )\n if (!hasPublishContent) return null\n\n return {\n caption: trimmedCaption,\n description: trimmedDescription,\n mentions: parsedMentions,\n destinations: selectedDestinations,\n cadence: trimmedCadence,\n workflowDraft: trimmedCadence !== 'Manual approval',\n evalContract: {\n artifactType: 'publish_package',\n deterministicChecks: ['has_asset', 'has_destination', 'has_caption_or_description', 'has_cadence'],\n },\n }\n}\n\n/** Determine if a value conforms to the PublishPackage structure with optional metadata fields */\nexport function isPublishPackage(value: unknown): value is { caption?: string; description?: string; mentions?: string[]; destinations?: string[]; cadence?: string } {\n if (!value || typeof value !== 'object') return false\n const publishPackage = value as {\n caption?: unknown\n description?: unknown\n mentions?: unknown\n destinations?: unknown\n cadence?: unknown\n }\n const destinations = Array.isArray(publishPackage.destinations) ? publishPackage.destinations.filter(Boolean) : []\n const mentions = Array.isArray(publishPackage.mentions) ? publishPackage.mentions.filter(Boolean) : []\n return Boolean(\n destinations.length > 0\n || mentions.length > 0\n || (typeof publishPackage.caption === 'string' && publishPackage.caption.trim())\n || (typeof publishPackage.description === 'string' && publishPackage.description.trim())\n || (typeof publishPackage.cadence === 'string' && publishPackage.cadence.trim() && publishPackage.cadence.trim() !== 'Manual approval'),\n )\n}\n\n/** Determine if a destination has any active connections in the given list of studio integration connections */\nexport function isDestinationConnected(\n destination: PublishDestination,\n connections: StudioIntegrationConnection[],\n): boolean {\n return connections.some((connection) => connection.status === 'connected'\n && (destination.providerIds.includes(connection.providerId)\n || (connection.connectorId !== undefined && destination.providerIds.includes(connection.connectorId))))\n}\n\n/** Resolve selected models by applying defaults for missing or unavailable entries in the catalog */\nexport function selectedModelsWithDefaults(\n current: Partial<Record<GenerationType, string>>,\n catalog: MediaModelCatalogResponse,\n): Partial<Record<GenerationType, string>> {\n const next = { ...current }\n for (const key of GENERATION_TYPES) {\n const models = catalog.models[key] ?? []\n const currentOption = models.find((model) => model.id === next[key])\n // Reset when: no selection, selection not in catalog, or selection is unavailable.\n // This ensures the Generate button is never stuck disabled when routeable\n // models exist but the stored default isn't one of them.\n if (!next[key] || !currentOption || currentOption.status === 'unavailable') {\n next[key] = preferredModelId(key, catalog) ?? ''\n }\n }\n return next\n}\n\n/** Resolve the preferred model ID for a given generation type from the media model catalog */\nexport function preferredModelId(type: GenerationType, catalog: MediaModelCatalogResponse | null): string | undefined {\n if (!catalog) return undefined\n const models = catalog.models[type] ?? []\n const preferred = catalog.defaults[type]\n return models.find((model) => model.id === preferred)?.id\n ?? models.find((model) => model.status !== 'unavailable')?.id\n ?? models[0]?.id\n}\n\n/** Resolve the appropriate status message for a media model based on loading state and availability */\nexport function modelMessage(model: MediaModelOption | undefined, loading: boolean, count: number): string | null {\n if (loading) return 'Loading media models...'\n if (count === 0) return 'No models are available for this media type.'\n if (!model) return 'Select a model.'\n if (model.status === 'unavailable') return model.reason ?? 'This model is not configured.'\n if (model.status === 'limited') return model.reason ? `Limited: ${model.reason}` : 'Limited availability.'\n return null\n}\n\n/** Define fields required to configure and request various types of media generation */\nexport interface GenerationRequestFields {\n workspaceId: string\n clientRequestId: string\n type: GenerationType\n model: string\n prompt: string\n negativePrompt: string\n outputPath: string\n publishPackage: PublishPackage | null\n image: { size: string; quality: string; count: number }\n video: { duration: string; resolution: string; aspectRatio: string; referenceImageUrl: string }\n speech: { voice: string }\n avatar: { audioUrl: string; imageUrl: string; avatarId: string }\n transcription: { audioUrl: string; language: string; responseFormat: string; temperature: string }\n}\n\n// image.count must already be normalized — it is also the optimistic-card count on the caller side\n/** Build the request body object for a generation operation from provided fields */\nexport function buildGenerationRequestBody(fields: GenerationRequestFields): Record<string, unknown> {\n const body: Record<string, unknown> = {\n workspaceId: fields.workspaceId,\n clientRequestId: fields.clientRequestId,\n type: fields.type,\n model: fields.model,\n prompt: fields.prompt.trim(),\n negativePrompt: fields.negativePrompt.trim() || undefined,\n outputPath: fields.outputPath.trim() || undefined,\n }\n if (fields.publishPackage) body.publishPackage = fields.publishPackage\n if (fields.type === 'image') Object.assign(body, {\n size: fields.image.size,\n quality: fields.image.quality,\n n: fields.image.count,\n })\n if (fields.type === 'video') {\n const duration = Number(fields.video.duration)\n Object.assign(body, {\n // omit (let the API default) rather than serialize NaN → null on bad input\n duration: Number.isFinite(duration) ? duration : undefined,\n resolution: fields.video.resolution,\n aspectRatio: fields.video.aspectRatio.trim() || undefined,\n referenceImageUrl: fields.video.referenceImageUrl.trim() || undefined,\n })\n }\n if (fields.type === 'speech') Object.assign(body, { voice: fields.speech.voice })\n if (fields.type === 'avatar') Object.assign(body, {\n audioUrl: fields.avatar.audioUrl.trim(),\n imageUrl: fields.avatar.imageUrl.trim() || undefined,\n avatarId: fields.avatar.avatarId.trim() || undefined,\n })\n if (fields.type === 'transcription') {\n const temperature = Number(fields.transcription.temperature)\n Object.assign(body, {\n audioUrl: fields.transcription.audioUrl.trim(),\n language: fields.transcription.language.trim() || undefined,\n responseFormat: fields.transcription.responseFormat,\n // omit (let the API default) rather than serialize NaN → null on bad input\n temperature: Number.isFinite(temperature) ? temperature : undefined,\n })\n }\n return body\n}\n\n/** Resolve the current status of a generation based on its metadata and result fields */\nexport function generationStatus(generation: Generation): GenerationStatus {\n const metadata = generation.metadata ?? {}\n const status = typeof metadata.generationStatus === 'string' ? metadata.generationStatus : ''\n if (status === 'pending' || status === 'running' || status === 'failed' || status === 'succeeded') return status\n return generation.result ? 'succeeded' : 'pending'\n}\n\n/** Resolve and return the first user-safe error message from generation metadata or null if none exist */\nexport function generationError(generation: Generation): string | null {\n const metadata = generation.metadata ?? {}\n if (typeof metadata.providerError === 'string' && metadata.providerError.trim()) {\n return userSafeGenerationMessage(metadata.providerError)\n }\n if (typeof metadata.storageError === 'string' && metadata.storageError.trim()) {\n return metadata.storageError\n }\n return null\n}\n\nfunction generationClientRequestId(generation: Generation): string | null {\n const metadata = generation.metadata ?? {}\n return typeof metadata.clientRequestId === 'string' && metadata.clientRequestId.trim()\n ? metadata.clientRequestId\n : null\n}\n\nfunction generationBatchSlotKey(generation: Generation): string | null {\n const metadata = generation.metadata ?? {}\n const batchId = typeof metadata.batchId === 'string' && metadata.batchId.trim() ? metadata.batchId : null\n return batchId && typeof metadata.outputIndex === 'number'\n ? `${batchId}:${metadata.outputIndex}`\n : null\n}\n\n/** Resolve a unique merge key from a generation using batch slot or client request ID */\nexport function generationMergeKey(generation: Generation): string | null {\n return generationBatchSlotKey(generation) ?? generationClientRequestId(generation)\n}\n\n/** Merge a new generation into the current list by replacing or prepending it based on matching keys */\nexport function mergeLiveGeneration(current: Generation[], generation: Generation): Generation[] {\n const mergeKey = generationMergeKey(generation)\n const existingIndex = current.findIndex((item) => (\n item.id === generation.id\n || (mergeKey && generationMergeKey(item) === mergeKey)\n ))\n if (existingIndex === -1) return [generation, ...current]\n\n const next = [...current]\n next[existingIndex] = generation\n return next\n}\n\n// Overlay in-flight `live` generations on the loader's rows: each live row leads\n// (prefer the matching loader row by merge key / id so it carries the freshest\n// server state), then the remaining loader rows that no live row already\n// represents — deduped by BOTH id and merge key so a server row and its\n// optimistic twin never both appear. Returns `loader` unchanged when nothing is\n// live. Drives the canvas, library, and polling off one list.\n/** Merge two Generation arrays prioritizing live entries and matching by merge keys or IDs */\nexport function mergeLoaderAndLive(loader: Generation[], live: Generation[]): Generation[] {\n if (live.length === 0) return loader\n const leading = live.map((generation) => {\n const mergeKey = generationMergeKey(generation)\n return mergeKey\n ? loader.find((gen) => generationMergeKey(gen) === mergeKey) ?? generation\n : loader.find((gen) => gen.id === generation.id) ?? generation\n })\n const leadingIds = new Set(leading.map((gen) => gen.id))\n const leadingMergeKeys = new Set(leading\n .map((gen) => generationMergeKey(gen))\n .filter((id): id is string => Boolean(id)))\n return [\n ...leading,\n ...loader.filter((gen) => (\n !leadingIds.has(gen.id)\n && !leadingMergeKeys.has(generationMergeKey(gen) ?? '')\n )),\n ]\n}\n\n/** Determine if a generation ID indicates a local generation */\nexport function isLocalGeneration(generation: Generation): boolean {\n return generation.id.startsWith('local-')\n}\n\nfunction generationOutputIndex(generation: Generation): number {\n const value = generation.metadata?.outputIndex\n return typeof value === 'number' ? value : 0\n}\n\n// The most-recent run: all generations sharing the leading item's clientRequestId\n// (a multi-image batch), ordered by output slot. Falls back to the single leading\n// item when no request id is present. Drives the result canvas.\n/** Resolve and return the latest batch of generations grouped and sorted by client request ID and output index */\nexport function latestBatchOf(generations: Generation[]): Generation[] {\n const first = generations[0]\n if (!first) return []\n const key = generationClientRequestId(first)\n const batch = key\n ? generations.filter((generation) => generationClientRequestId(generation) === key)\n : [first]\n return [...batch].sort((a, b) => generationOutputIndex(a) - generationOutputIndex(b))\n}\n\n/** Resolve a user-safe generation message by filtering sensitive or error-related content */\nexport function userSafeGenerationMessage(message?: string): string {\n if (!message) return 'Generation failed'\n if (/Tangle API key is invalid or expired/i.test(message)) return message\n if (/(api[_ -]?key|secret|token|credential|env|configured|configuration)/i.test(message)) {\n return 'Generation failed'\n }\n return message\n}\n\n/** Generate content optimistically based on input parameters and optional model and output details */\nexport function optimisticGeneration({\n type,\n prompt,\n model,\n clientRequestId,\n outputIndex,\n outputCount,\n}: {\n type: GenerationType\n prompt: string\n model?: string\n clientRequestId: string\n outputIndex?: number\n outputCount?: number\n}): Generation {\n const batchId = outputIndex == null ? undefined : clientRequestId\n return {\n id: outputIndex == null ? `local-${clientRequestId}` : `local-${clientRequestId}-${outputIndex}`,\n type,\n prompt,\n result: null,\n model: model ?? null,\n cost: null,\n createdAt: new Date(),\n metadata: {\n generationStatus: 'pending',\n provider: type,\n clientRequestId,\n batchId,\n outputIndex,\n outputCount,\n },\n }\n}\n\n/** Mark a generation as failed with updated status and error information */\nexport function failedOptimisticGeneration(generation: Generation): Generation {\n return {\n ...generation,\n metadata: {\n ...(generation.metadata ?? {}),\n generationStatus: 'failed',\n providerError: 'Generation failed',\n },\n }\n}\n\n/** Normalize a value to a finite integer within the allowed image count range */\nexport function normalizeImageCount(value: unknown): number {\n const numeric = typeof value === 'number' ? value : Number(value)\n if (!Number.isFinite(numeric)) return MIN_IMAGE_COUNT\n return Math.min(Math.max(Math.trunc(numeric), MIN_IMAGE_COUNT), MAX_IMAGE_COUNT)\n}\n"],"mappings":"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","names":[]}
@@ -4,7 +4,27 @@ import {
4
4
 
5
5
  // src/intakes-react/components/IntakeInterview.tsx
6
6
  import { useEffect, useState } from "react";
7
- import { jsx, jsxs } from "react/jsx-runtime";
7
+
8
+ // src/intakes-react/components/BrandMark.tsx
9
+ import { lazy, Suspense } from "react";
10
+ import { jsx } from "react/jsx-runtime";
11
+ function MarkSpacer({ size }) {
12
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": true, style: { display: "inline-block", width: size, height: size } });
13
+ }
14
+ var LazyKnot = lazy(async () => {
15
+ try {
16
+ const mod = await import("./brand/index.js");
17
+ return { default: mod.TangleKnot };
18
+ } catch {
19
+ return { default: MarkSpacer };
20
+ }
21
+ });
22
+ function BrandMark({ size, className }) {
23
+ return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(MarkSpacer, { size }), children: /* @__PURE__ */ jsx(LazyKnot, { size, className }) });
24
+ }
25
+
26
+ // src/intakes-react/components/IntakeInterview.tsx
27
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
8
28
  function IntakeInterview({
9
29
  view: initialView,
10
30
  onAnswer,
@@ -63,35 +83,42 @@ function IntakeInterview({
63
83
  }
64
84
  return /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-5", children: [
65
85
  /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-1", children: [
66
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-[var(--text-primary)]", children: view.title }),
67
- view.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--text-muted)]", children: view.description }),
68
- /* @__PURE__ */ jsx(ProgressBar, { answered: view.progress.answered, total: view.progress.total })
86
+ /* @__PURE__ */ jsxs("span", { className: "mb-1 flex items-center gap-2 text-[var(--text-muted)]", children: [
87
+ /* @__PURE__ */ jsx2(BrandMark, { size: 20, className: "shrink-0" }),
88
+ /* @__PURE__ */ jsx2("span", { className: "text-xs font-semibold uppercase tracking-[0.05em]", children: "Tangle Intake" })
89
+ ] }),
90
+ /* @__PURE__ */ jsx2("h2", { className: "text-xl font-semibold text-[var(--text-primary)]", children: view.title }),
91
+ view.description && /* @__PURE__ */ jsx2("p", { className: "text-sm text-[var(--text-muted)]", children: view.description }),
92
+ /* @__PURE__ */ jsx2(ProgressBar, { answered: view.progress.answered, total: view.progress.total })
69
93
  ] }),
70
- view.completed ? /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--text-secondary)]", children: "Thanks \u2014 your intake is complete." }) : question ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
94
+ view.completed ? /* @__PURE__ */ jsxs("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", children: [
95
+ /* @__PURE__ */ jsx2("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": true, children: /* @__PURE__ */ jsx2("polyline", { points: "20 6 9 17 4 12" }) }),
96
+ /* @__PURE__ */ jsx2("p", { className: "text-sm text-[var(--text-primary)]", children: "You're all set \u2014 every answer is saved. You can close this panel." })
97
+ ] }) : question ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
71
98
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
72
- /* @__PURE__ */ jsx("p", { className: "text-base font-medium text-[var(--text-primary)]", children: question.prompt }),
73
- question.help && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--text-muted)]", children: question.help })
99
+ /* @__PURE__ */ jsx2("p", { className: "text-sm font-medium text-[var(--text-primary)]", children: question.prompt }),
100
+ question.help && /* @__PURE__ */ jsx2("p", { className: "text-xs text-[var(--text-muted)]", children: question.help })
74
101
  ] }),
75
- /* @__PURE__ */ jsx(AnswerField, { question, value: draft, onChange: setDraft, onSubmit: () => void submit() }),
76
- /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
102
+ /* @__PURE__ */ jsx2(AnswerField, { question, value: draft, onChange: setDraft, onSubmit: () => void submit() }),
103
+ /* @__PURE__ */ jsx2("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx2(
77
104
  "button",
78
105
  {
79
106
  type: "button",
80
107
  onClick: () => void submit(),
81
- disabled: busy,
82
- className: "rounded bg-[var(--brand-primary)] px-4 py-1.5 text-sm text-white disabled:opacity-50",
108
+ disabled: busy || isDraftEmpty(draft),
109
+ className: "rounded bg-[var(--brand-primary)] px-4 py-1.5 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50",
83
110
  children: busy ? "Saving\u2026" : "Continue"
84
111
  }
85
112
  ) })
86
113
  ] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
87
- /* @__PURE__ */ jsx("p", { className: "text-sm text-[var(--text-secondary)]", children: "That's everything. Ready to finish?" }),
88
- /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
114
+ /* @__PURE__ */ jsx2("p", { className: "text-sm text-[var(--text-secondary)]", children: "That's everything. Ready to finish?" }),
115
+ /* @__PURE__ */ jsx2("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx2(
89
116
  "button",
90
117
  {
91
118
  type: "button",
92
119
  onClick: () => void finish(),
93
120
  disabled: busy,
94
- className: "rounded bg-[var(--brand-primary)] px-4 py-1.5 text-sm text-white disabled:opacity-50",
121
+ className: "rounded bg-[var(--brand-primary)] px-4 py-1.5 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50",
95
122
  children: busy ? "Finishing\u2026" : "Finish"
96
123
  }
97
124
  ) })
@@ -102,7 +129,7 @@ function AnswerField({ question, value, onChange, onSubmit }) {
102
129
  const inputClass = "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)]";
103
130
  switch (question.type) {
104
131
  case "long-text":
105
- return /* @__PURE__ */ jsx(
132
+ return /* @__PURE__ */ jsx2(
106
133
  "textarea",
107
134
  {
108
135
  rows: 4,
@@ -113,18 +140,18 @@ function AnswerField({ question, value, onChange, onSubmit }) {
113
140
  }
114
141
  );
115
142
  case "boolean":
116
- return /* @__PURE__ */ jsx("div", { className: "flex gap-2", children: [{ v: true, l: "Yes" }, { v: false, l: "No" }].map((opt) => /* @__PURE__ */ jsx(
143
+ return /* @__PURE__ */ jsx2("div", { className: "flex gap-2", children: [{ v: true, l: "Yes" }, { v: false, l: "No" }].map((opt) => /* @__PURE__ */ jsx2(
117
144
  "button",
118
145
  {
119
146
  type: "button",
120
147
  onClick: () => onChange(opt.v),
121
- className: `rounded border px-4 py-1.5 text-sm ${value === opt.v ? "border-[var(--brand-primary)] bg-[var(--brand-primary)] text-white" : "border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]"}`,
148
+ className: `rounded border px-4 py-1.5 text-sm ${value === opt.v ? "border-[var(--brand-primary)] bg-[var(--brand-primary)] text-[hsl(var(--primary-foreground))]" : "border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]"}`,
122
149
  children: opt.l
123
150
  },
124
151
  opt.l
125
152
  )) });
126
153
  case "number":
127
- return /* @__PURE__ */ jsx(
154
+ return /* @__PURE__ */ jsx2(
128
155
  "input",
129
156
  {
130
157
  type: "number",
@@ -138,34 +165,40 @@ function AnswerField({ question, value, onChange, onSubmit }) {
138
165
  }
139
166
  );
140
167
  case "single-select":
141
- return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1.5", children: (question.options ?? []).map((option) => /* @__PURE__ */ jsx(
168
+ return /* @__PURE__ */ jsx2("div", { className: "flex flex-col gap-1.5", children: (question.options ?? []).map((option) => /* @__PURE__ */ jsxs(
142
169
  "button",
143
170
  {
144
171
  type: "button",
145
172
  onClick: () => onChange(option.value),
146
- className: `rounded border px-3 py-2 text-left text-sm ${value === option.value ? "border-[var(--brand-primary)] bg-[var(--bg-input)] text-[var(--text-primary)]" : "border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]"}`,
147
- children: option.label
173
+ className: `flex items-center gap-2 rounded border px-3 py-2 text-left text-sm ${value === option.value ? "border-[var(--brand-primary)] bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)] text-[var(--text-primary)]" : "border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]"}`,
174
+ children: [
175
+ value === option.value && /* @__PURE__ */ jsx2(ChoiceCheck, {}),
176
+ option.label
177
+ ]
148
178
  },
149
179
  option.value
150
180
  )) });
151
181
  case "multi-select": {
152
182
  const selected = Array.isArray(value) ? value : [];
153
- return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1.5", children: (question.options ?? []).map((option) => {
183
+ return /* @__PURE__ */ jsx2("div", { className: "flex flex-col gap-1.5", children: (question.options ?? []).map((option) => {
154
184
  const on = selected.includes(option.value);
155
- return /* @__PURE__ */ jsx(
185
+ return /* @__PURE__ */ jsxs(
156
186
  "button",
157
187
  {
158
188
  type: "button",
159
189
  onClick: () => onChange(on ? selected.filter((v) => v !== option.value) : [...selected, option.value]),
160
- className: `rounded border px-3 py-2 text-left text-sm ${on ? "border-[var(--brand-primary)] bg-[var(--bg-input)] text-[var(--text-primary)]" : "border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]"}`,
161
- children: option.label
190
+ className: `flex items-center gap-2 rounded border px-3 py-2 text-left text-sm ${on ? "border-[var(--brand-primary)] bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)] text-[var(--text-primary)]" : "border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]"}`,
191
+ children: [
192
+ on && /* @__PURE__ */ jsx2(ChoiceCheck, {}),
193
+ option.label
194
+ ]
162
195
  },
163
196
  option.value
164
197
  );
165
198
  }) });
166
199
  }
167
200
  default:
168
- return /* @__PURE__ */ jsx(
201
+ return /* @__PURE__ */ jsx2(
169
202
  "input",
170
203
  {
171
204
  type: question.type === "email" ? "email" : question.type === "url" ? "url" : "text",
@@ -180,10 +213,13 @@ function AnswerField({ question, value, onChange, onSubmit }) {
180
213
  );
181
214
  }
182
215
  }
216
+ function ChoiceCheck() {
217
+ return /* @__PURE__ */ jsx2("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": true, children: /* @__PURE__ */ jsx2("polyline", { points: "20 6 9 17 4 12" }) });
218
+ }
183
219
  function ProgressBar({ answered, total }) {
184
220
  const pct = total > 0 ? Math.round(answered / total * 100) : 0;
185
221
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
186
- /* @__PURE__ */ jsx("div", { className: "h-1.5 flex-1 overflow-hidden rounded-full bg-[var(--bg-input)]", children: /* @__PURE__ */ jsx("div", { className: "h-full rounded-full bg-[var(--brand-primary)] transition-all", style: { width: `${pct}%` } }) }),
222
+ /* @__PURE__ */ jsx2("div", { className: "h-1.5 flex-1 overflow-hidden rounded-full bg-[var(--bg-input)]", children: /* @__PURE__ */ jsx2("div", { className: "h-full rounded-full bg-[var(--brand-primary)] transition-all", style: { width: `${pct}%` } }) }),
187
223
  /* @__PURE__ */ jsxs("span", { className: "text-xs text-[var(--text-muted)]", children: [
188
224
  answered,
189
225
  "/",
@@ -199,6 +235,12 @@ function currentAnswer(view) {
199
235
  function defaultDraft(question) {
200
236
  return question.type === "multi-select" ? [] : null;
201
237
  }
238
+ function isDraftEmpty(value) {
239
+ if (value === null || value === void 0) return true;
240
+ if (typeof value === "string") return value.trim() === "";
241
+ if (Array.isArray(value)) return value.length === 0;
242
+ return false;
243
+ }
202
244
  function normalize(question, value) {
203
245
  if (typeof value === "string" && (question.type === "text" || question.type === "long-text" || question.type === "url" || question.type === "email")) {
204
246
  return value.trim();
@@ -209,4 +251,4 @@ function normalize(question, value) {
209
251
  export {
210
252
  IntakeInterview
211
253
  };
212
- //# sourceMappingURL=chunk-VOJB7MVG.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":";;;;;AAcA,SAAS,WAAW,gBAAgB;;;ACRpC,SAAS,MAAM,gBAAgB;AAUtB;AADT,SAAS,WAAW,EAAE,KAAK,GAAqB;AAC9C,SAAO,oBAAC,UAAK,eAAW,MAAC,OAAO,EAAE,SAAS,gBAAgB,OAAO,MAAM,QAAQ,KAAK,GAAG;AAC1F;AAEA,IAAM,WAAW,KAAK,YAAY;AAChC,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,kBAAa;AACtC,WAAO,EAAE,SAAS,IAAI,WAA4C;AAAA,EACpE,QAAQ;AACN,WAAO,EAAE,SAAS,WAA4C;AAAA,EAChE;AACF,CAAC;AAEM,SAAS,UAAU,EAAE,MAAM,UAAU,GAAmB;AAC7D,SACE,oBAAC,YAAS,UAAU,oBAAC,cAAW,MAAY,GAC1C,8BAAC,YAAS,MAAY,WAAsB,GAC9C;AAEJ;;;ADoDQ,SACE,OAAAA,MADF;AAlED,SAAS,gBAAgB;AAAA,EAC9B,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,CAAC,MAAM,OAAO,IAAI,SAAqB,WAAW;AACxD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA4B,cAAc,WAAW,CAAC;AAChF,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAEhD,YAAU,MAAM;AACd,YAAQ,WAAW;AACnB,aAAS,cAAc,WAAW,CAAC;AAAA,EACrC,GAAG,CAAC,WAAW,CAAC;AAEhB,YAAU,MAAM;AACd,QAAI,KAAK,aAAa,CAAC,WAAW;AAChC,mBAAa,IAAI;AACjB,eAAS;AAAA,IACX;AAAA,EACF,GAAG,CAAC,KAAK,WAAW,WAAW,MAAM,CAAC;AAEtC,WAAS,OAAO,MAA2B,SAAiB;AAC1D,eAAW,EAAE,MAAM,QAAQ,CAAC;AAAA,EAC9B;AAEA,QAAM,WAAW,KAAK;AAEtB,iBAAe,SAAS;AACtB,QAAI,CAAC,YAAY,KAAM;AACvB,UAAM,WAAW,eAAe,UAAU,KAAK;AAC/C,QAAI,CAAC,SAAS,IAAI;AAChB,aAAO,SAAS,kBAAkB,SAAS,MAAM,EAAE;AACnD;AAAA,IACF;AACA,YAAQ,IAAI;AACZ,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,EAAE,YAAY,SAAS,IAAI,OAAO,UAAU,UAAU,KAAK,EAAE,CAAC;AAC1F,cAAQ,IAAI;AACZ,eAAS,cAAc,IAAI,CAAC;AAAA,IAC9B,SAAS,KAAK;AACZ,aAAO,SAAS,eAAe,QAAQ,IAAI,UAAU,uBAAuB;AAAA,IAC9E,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,iBAAe,SAAS;AACtB,QAAI,KAAM;AACV,YAAQ,IAAI;AACZ,QAAI;AACF,YAAM,OAAO,MAAM,WAAW;AAC9B,cAAQ,IAAI;AACZ,aAAO,WAAW,UAAU;AAAA,IAC9B,SAAS,KAAK;AACZ,aAAO,SAAS,eAAe,QAAQ,IAAI,UAAU,kBAAkB;AAAA,IACzE,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,SACE,qBAAC,aAAQ,WAAU,uBACjB;AAAA,yBAAC,YAAO,WAAU,uBAChB;AAAA,2BAAC,UAAK,WAAU,yDACd;AAAA,wBAAAA,KAAC,aAAU,MAAM,IAAI,WAAU,YAAW;AAAA,QAC1C,gBAAAA,KAAC,UAAK,WAAU,qDAAoD,2BAAa;AAAA,SACnF;AAAA,MACA,gBAAAA,KAAC,QAAG,WAAU,oDAAoD,eAAK,OAAM;AAAA,MAC5E,KAAK,eAAe,gBAAAA,KAAC,OAAE,WAAU,oCAAoC,eAAK,aAAY;AAAA,MACvF,gBAAAA,KAAC,eAAY,UAAU,KAAK,SAAS,UAAU,OAAO,KAAK,SAAS,OAAO;AAAA,OAC7E;AAAA,IAEC,KAAK,YACJ,qBAAC,SAAI,WAAU,0HACb;AAAA,sBAAAA,KAAC,SAAI,SAAQ,aAAY,WAAU,uDAAsD,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACnM,0BAAAA,KAAC,cAAS,QAAO,kBAAiB,GACpC;AAAA,MACA,gBAAAA,KAAC,OAAE,WAAU,sCAAqC,oFAAiE;AAAA,OACrH,IACE,WACF,qBAAC,SAAI,WAAU,uBACb;AAAA,2BAAC,SAAI,WAAU,uBACb;AAAA,wBAAAA,KAAC,OAAE,WAAU,kDAAkD,mBAAS,QAAO;AAAA,QAC9E,SAAS,QAAQ,gBAAAA,KAAC,OAAE,WAAU,oCAAoC,mBAAS,MAAK;AAAA,SACnF;AAAA,MAEA,gBAAAA,KAAC,eAAY,UAAoB,OAAO,OAAO,UAAU,UAAU,UAAU,MAAM,KAAK,OAAO,GAAG;AAAA,MAElG,gBAAAA,KAAC,SAAI,WAAU,oBACb,0BAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,KAAK,OAAO;AAAA,UAC3B,UAAU,QAAQ,aAAa,KAAK;AAAA,UACpC,WAAU;AAAA,UAET,iBAAO,iBAAY;AAAA;AAAA,MACtB,GACF;AAAA,OACF,IAEA,qBAAC,SAAI,WAAU,uBACb;AAAA,sBAAAA,KAAC,OAAE,WAAU,wCAAuC,iDAAmC;AAAA,MACvF,gBAAAA,KAAC,SAAI,WAAU,oBACb,0BAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,KAAK,OAAO;AAAA,UAC3B,UAAU;AAAA,UACV,WAAU;AAAA,UAET,iBAAO,oBAAe;AAAA;AAAA,MACzB,GACF;AAAA,OACF;AAAA,KAEJ;AAEJ;AASA,SAAS,YAAY,EAAE,UAAU,OAAO,UAAU,SAAS,GAAqB;AAC9E,QAAM,aACJ;AAEF,UAAQ,SAAS,MAAM;AAAA,IACrB,KAAK;AACH,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM;AAAA,UACN,cAAY,SAAS;AAAA,UACrB,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,UAC3C,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,UAChD,WAAW;AAAA;AAAA,MACb;AAAA,IAEJ,KAAK;AACH,aACE,gBAAAA,KAAC,SAAI,WAAU,cACZ,WAAC,EAAE,GAAG,MAAM,GAAG,MAAM,GAAG,EAAE,GAAG,OAAO,GAAG,KAAK,CAAC,EAAE,IAAI,CAAC,QACnD,gBAAAA;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,SAAS,MAAM,SAAS,IAAI,CAAC;AAAA,UAC7B,WAAW,sCACT,UAAU,IAAI,IACV,kGACA,kFACN;AAAA,UAEC,cAAI;AAAA;AAAA,QATA,IAAI;AAAA,MAUX,CACD,GACH;AAAA,IAEJ,KAAK;AACH,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,cAAY,SAAS;AAAA,UACrB,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,UAC3C,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,UAAU,KAAK,OAAO,OAAO,MAAM,OAAO,KAAK,CAAC;AAAA,UAC3F,WAAW,CAAC,UAAU;AAAE,gBAAI,MAAM,QAAQ,QAAS,UAAS;AAAA,UAAE;AAAA,UAC9D,WAAW;AAAA;AAAA,MACb;AAAA,IAEJ,KAAK;AACH,aACE,gBAAAA,KAAC,SAAI,WAAU,yBACX,oBAAS,WAAW,CAAC,GAAG,IAAI,CAAC,WAC7B;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,SAAS,MAAM,SAAS,OAAO,KAAK;AAAA,UACpC,WAAW,sEACT,UAAU,OAAO,QACb,0HACA,kFACN;AAAA,UAEC;AAAA,sBAAU,OAAO,SAAS,gBAAAA,KAAC,eAAY;AAAA,YACvC,OAAO;AAAA;AAAA;AAAA,QAVH,OAAO;AAAA,MAWd,CACD,GACH;AAAA,IAEJ,KAAK,gBAAgB;AACnB,YAAM,WAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;AACjD,aACE,gBAAAA,KAAC,SAAI,WAAU,yBACX,oBAAS,WAAW,CAAC,GAAG,IAAI,CAAC,WAAW;AACxC,cAAM,KAAK,SAAS,SAAS,OAAO,KAAK;AACzC,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,SAAS,MACP,SAAS,KAAK,SAAS,OAAO,CAAC,MAAM,MAAM,OAAO,KAAK,IAAI,CAAC,GAAG,UAAU,OAAO,KAAK,CAAC;AAAA,YAExF,WAAW,sEACT,KACI,0HACA,kFACN;AAAA,YAEC;AAAA,oBAAM,gBAAAA,KAAC,eAAY;AAAA,cACnB,OAAO;AAAA;AAAA;AAAA,UAZH,OAAO;AAAA,QAad;AAAA,MAEJ,CAAC,GACH;AAAA,IAEJ;AAAA,IACA;AACE,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,SAAS,SAAS,UAAU,UAAU,SAAS,SAAS,QAAQ,QAAQ;AAAA,UAC9E,cAAY,SAAS;AAAA,UACrB,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,UAC3C,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,UAChD,WAAW,CAAC,UAAU;AAAE,gBAAI,MAAM,QAAQ,QAAS,UAAS;AAAA,UAAE;AAAA,UAC9D,WAAW;AAAA;AAAA,MACb;AAAA,EAEN;AACF;AAGA,SAAS,cAAc;AACrB,SACE,gBAAAA,KAAC,SAAI,SAAQ,aAAY,WAAU,oDAAmD,MAAK,QAAO,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MAChM,0BAAAA,KAAC,cAAS,QAAO,kBAAiB,GACpC;AAEJ;AAEA,SAAS,YAAY,EAAE,UAAU,MAAM,GAAwC;AAC7E,QAAM,MAAM,QAAQ,IAAI,KAAK,MAAO,WAAW,QAAS,GAAG,IAAI;AAC/D,SACE,qBAAC,SAAI,WAAU,2BACb;AAAA,oBAAAA,KAAC,SAAI,WAAU,kEACb,0BAAAA,KAAC,SAAI,WAAU,gEAA+D,OAAO,EAAE,OAAO,GAAG,GAAG,IAAI,GAAG,GAC7G;AAAA,IACA,qBAAC,UAAK,WAAU,oCAAoC;AAAA;AAAA,MAAS;AAAA,MAAE;AAAA,OAAM;AAAA,KACvE;AAEJ;AAEA,SAAS,cAAc,MAAqC;AAC1D,QAAM,KAAK,KAAK,cAAc;AAC9B,MAAI,CAAC,GAAI,QAAO;AAChB,SAAO,KAAK,QAAQ,EAAE,KAAK,aAAa,KAAK,YAAa;AAC5D;AAEA,SAAS,aAAa,UAA6C;AACjE,SAAO,SAAS,SAAS,iBAAiB,CAAC,IAAI;AACjD;AAIA,SAAS,aAAa,OAAmC;AACvD,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,KAAK,MAAM;AACvD,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,WAAW;AAClD,SAAO;AACT;AAGA,SAAS,UAAU,UAA0B,OAA6C;AACxF,MAAI,OAAO,UAAU,aAAa,SAAS,SAAS,UAAU,SAAS,SAAS,eAAe,SAAS,SAAS,SAAS,SAAS,SAAS,UAAU;AACpJ,WAAO,MAAM,KAAK;AAAA,EACpB;AACA,SAAO;AACT;","names":["jsx"]}
@@ -0,0 +1,44 @@
1
+ // src/web-react/provider-logo.tsx
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ var LOGOS = {
4
+ anthropic: { viewBox: "0 0 24 24", fill: "#D97757", paths: ["M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z"] },
5
+ google: { viewBox: "0 0 24 24", fill: "#8E75B2", paths: ["M11.04 19.32Q12 21.51 12 24q0-2.49.93-4.68.96-2.19 2.58-3.81t3.81-2.55Q21.51 12 24 12q-2.49 0-4.68-.93a12.3 12.3 0 0 1-3.81-2.58 12.3 12.3 0 0 1-2.58-3.81Q12 2.49 12 0q0 2.49-.96 4.68-.93 2.19-2.55 3.81a12.3 12.3 0 0 1-3.81 2.58Q2.49 12 0 12q2.49 0 4.68.96 2.19.93 3.81 2.55t2.55 3.81"] },
6
+ deepseek: { viewBox: "0 0 24 24", fill: "#4D6BFE", paths: ["M23.748 4.651c-.254-.124-.364.113-.512.233-.051.04-.094.09-.137.137-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.155-.708-.311-.955-.65-.172-.24-.219-.509-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.094.172.187.129.323-.082.28-.18.553-.266.833-.055.179-.137.218-.328.14a5.5 5.5 0 0 1-1.737-1.179c-.857-.828-1.631-1.743-2.597-2.46a12 12 0 0 0-.689-.47c-.985-.957.13-1.743.387-1.836.27-.098.094-.433-.778-.428-.872.003-1.67.295-2.687.685a3 3 0 0 1-.465.136 9.6 9.6 0 0 0-2.883-.101c-1.885.21-3.39 1.1-4.497 2.622C.082 8.776-.231 10.854.152 13.02c.403 2.284 1.568 4.175 3.36 5.653 1.857 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.132-.284 4.994-1.86.47.234.962.328 1.78.398.629.058 1.235-.031 1.705-.129.735-.155.684-.836.418-.961-2.155-1.004-1.682-.595-2.112-.926 1.095-1.295 2.768-3.598 3.284-6.733.05-.346.115-.834.108-1.114-.004-.171.035-.238.23-.257a4.2 4.2 0 0 0 1.545-.475c1.397-.763 1.96-2.016 2.093-3.517.02-.23-.004-.467-.247-.588M11.58 18.168c-2.088-1.642-3.101-2.183-3.52-2.16-.39.024-.32.472-.234.763.09.288.207.487.371.74.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.168-1.361-.801-2.5-1.86-3.301-3.306-.775-1.393-1.225-2.888-1.299-4.482-.02-.385.094-.522.477-.592a4.7 4.7 0 0 1 1.53-.038c2.131.311 3.946 1.264 5.467 2.774.868.86 1.525 1.887 2.202 2.89.72 1.066 1.494 2.082 2.48 2.915.348.291.626.513.892.677-.802.09-2.14.109-3.055-.615zm1.001-6.44a.306.306 0 0 1 .415-.287.3.3 0 0 1 .113.074.3.3 0 0 1 .086.214c0 .17-.136.307-.308.307a.303.303 0 0 1-.306-.307m3.11 1.596c-.2.081-.4.151-.591.16a1.25 1.25 0 0 1-.798-.254c-.274-.23-.47-.358-.551-.758a1.7 1.7 0 0 1 .015-.588c.07-.327-.007-.537-.238-.727-.188-.156-.426-.199-.689-.199a.6.6 0 0 1-.254-.078.253.253 0 0 1-.114-.358 1 1 0 0 1 .192-.21c.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.392.451.462.576.685.915.176.264.336.536.446.848.066.194-.02.353-.25.45"] },
7
+ mistral: { viewBox: "0 0 24 24", fill: "#FA520F", paths: ["M17.143 3.429v3.428h-3.429v3.429h-3.428V6.857H6.857V3.43H3.43v13.714H0v3.428h10.286v-3.428H6.857v-3.429h3.429v3.429h3.429v-3.429h3.428v3.429h-3.428v3.428H24v-3.428h-3.43V3.429z"] },
8
+ xai: { viewBox: "0 0 24 24", fill: "#000000", paths: ["M14.234 10.162 22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299-.929-1.329L3.076 1.56h3.182l5.965 8.532.929 1.329 7.754 11.09h-3.182z"] },
9
+ nvidia: { viewBox: "0 0 24 24", fill: "#76B900", paths: ["M8.948 8.798v-1.43a6.7 6.7 0 0 1 .424-.018c3.922-.124 6.493 3.374 6.493 3.374s-2.774 3.851-5.75 3.851c-.398 0-.787-.062-1.158-.185v-4.346c1.528.185 1.837.857 2.747 2.385l2.04-1.714s-1.492-1.952-4-1.952a6.016 6.016 0 0 0-.796.035m0-4.735v2.138l.424-.027c5.45-.185 9.01 4.47 9.01 4.47s-4.08 4.964-8.33 4.964c-.37 0-.733-.035-1.095-.097v1.325c.3.035.61.062.91.062 3.957 0 6.82-2.023 9.593-4.408.459.371 2.34 1.263 2.73 1.652-2.633 2.208-8.772 3.984-12.253 3.984-.335 0-.653-.018-.971-.053v1.864H24V4.063zm0 10.326v1.131c-3.657-.654-4.673-4.46-4.673-4.46s1.758-1.944 4.673-2.262v1.237H8.94c-1.528-.186-2.73 1.245-2.73 1.245s.68 2.412 2.739 3.11M2.456 10.9s2.164-3.197 6.5-3.533V6.201C4.153 6.59 0 10.653 0 10.653s2.35 6.802 8.948 7.42v-1.237c-4.84-.6-6.492-5.936-6.492-5.936z"] },
10
+ meta: { viewBox: "0 0 24 24", fill: "#0467DF", paths: ["M6.915 4.03c-1.968 0-3.683 1.28-4.871 3.113C.704 9.208 0 11.883 0 14.449c0 .706.07 1.369.21 1.973a6.624 6.624 0 0 0 .265.86 5.297 5.297 0 0 0 .371.761c.696 1.159 1.818 1.927 3.593 1.927 1.497 0 2.633-.671 3.965-2.444.76-1.012 1.144-1.626 2.663-4.32l.756-1.339.186-.325c.061.1.121.196.183.3l2.152 3.595c.724 1.21 1.665 2.556 2.47 3.314 1.046.987 1.992 1.22 3.06 1.22 1.075 0 1.876-.355 2.455-.843a3.743 3.743 0 0 0 .81-.973c.542-.939.861-2.127.861-3.745 0-2.72-.681-5.357-2.084-7.45-1.282-1.912-2.957-2.93-4.716-2.93-1.047 0-2.088.467-3.053 1.308-.652.57-1.257 1.29-1.82 2.05-.69-.875-1.335-1.547-1.958-2.056-1.182-.966-2.315-1.303-3.454-1.303zm10.16 2.053c1.147 0 2.188.758 2.992 1.999 1.132 1.748 1.647 4.195 1.647 6.4 0 1.548-.368 2.9-1.839 2.9-.58 0-1.027-.23-1.664-1.004-.496-.601-1.343-1.878-2.832-4.358l-.617-1.028a44.908 44.908 0 0 0-1.255-1.98c.07-.109.141-.224.211-.327 1.12-1.667 2.118-2.602 3.358-2.602zm-10.201.553c1.265 0 2.058.791 2.675 1.446.307.327.737.871 1.234 1.579l-1.02 1.566c-.757 1.163-1.882 3.017-2.837 4.338-1.191 1.649-1.81 1.817-2.486 1.817-.524 0-1.038-.237-1.383-.794-.263-.426-.464-1.13-.464-2.046 0-2.221.63-4.535 1.66-6.088.454-.687.964-1.226 1.533-1.533a2.264 2.264 0 0 1 1.088-.285z"] },
11
+ moonshotai: { viewBox: "0 0 24 24", fill: "#16191E", paths: ["m1.053 16.91 9.538 2.55a21 20.981 0 0 0 .06 2.031l5.956 1.592a12 11.99 0 0 1-15.554-6.172m-1.02-5.79 11.352 3.035a21 20.981 0 0 0-.469 2.01l10.817 2.89a12 11.99 0 0 1-1.845 2.004L.658 15.918a12 11.99 0 0 1-.625-4.796m1.593-5.146L13.573 9.17a21 20.981 0 0 0-1.01 1.874l11.297 3.02a21 20.981 0 0 1-.67 2.362l-11.55-3.087L.125 10.26a12 11.99 0 0 1 1.499-4.285ZM6.067 1.58l11.285 3.016a21 20.981 0 0 0-1.688 1.719l7.824 2.091a21 20.981 0 0 1 .513 2.664L2.107 5.218a12 11.99 0 0 1 3.96-3.638M21.68 4.866 7.222 1.003A12 11.99 0 0 1 21.68 4.866"] },
12
+ openai: { viewBox: "0 0 256 260", fill: "#10A37F", paths: ["M239.184 106.203a64.72 64.72 0 0 0-5.576-53.103C219.452 28.459 191 15.784 163.213 21.74A65.586 65.586 0 0 0 52.096 45.22a64.72 64.72 0 0 0-43.23 31.36c-14.31 24.602-11.061 55.634 8.033 76.74a64.67 64.67 0 0 0 5.525 53.102c14.174 24.65 42.644 37.324 70.446 31.36a64.72 64.72 0 0 0 48.754 21.744c28.481.025 53.714-18.361 62.414-45.481a64.77 64.77 0 0 0 43.229-31.36c14.137-24.558 10.875-55.423-8.083-76.483m-97.56 136.338a48.4 48.4 0 0 1-31.105-11.255l1.535-.87l51.67-29.825a8.6 8.6 0 0 0 4.247-7.367v-72.85l21.845 12.636c.218.111.37.32.409.563v60.367c-.056 26.818-21.783 48.545-48.601 48.601M37.158 197.93a48.35 48.35 0 0 1-5.781-32.589l1.534.921l51.722 29.826a8.34 8.34 0 0 0 8.441 0l63.181-36.425v25.221a.87.87 0 0 1-.358.665l-52.335 30.184c-23.257 13.398-52.97 5.431-66.404-17.803M23.549 85.38a48.5 48.5 0 0 1 25.58-21.333v61.39a8.29 8.29 0 0 0 4.195 7.316l62.874 36.272l-21.845 12.636a.82.82 0 0 1-.767 0L41.353 151.53c-23.211-13.454-31.171-43.144-17.804-66.405zm179.466 41.695l-63.08-36.63L161.73 77.86a.82.82 0 0 1 .768 0l52.233 30.184a48.6 48.6 0 0 1-7.316 87.635v-61.391a8.54 8.54 0 0 0-4.4-7.213m21.742-32.69l-1.535-.922l-51.619-30.081a8.39 8.39 0 0 0-8.492 0L99.98 99.808V74.587a.72.72 0 0 1 .307-.665l52.233-30.133a48.652 48.652 0 0 1 72.236 50.391zM88.061 139.097l-21.845-12.585a.87.87 0 0 1-.41-.614V65.685a48.652 48.652 0 0 1 79.757-37.346l-1.535.87l-51.67 29.825a8.6 8.6 0 0 0-4.246 7.367zm11.868-25.58L128.067 97.3l28.188 16.218v32.434l-28.086 16.218l-28.188-16.218z"] }
13
+ };
14
+ var ALIASES = {
15
+ moonshot: "moonshotai",
16
+ deepseek_ai: "deepseek",
17
+ "x-ai": "xai",
18
+ "meta-llama": "meta"
19
+ };
20
+ var MONOGRAM = {
21
+ cohere: { bg: "#fae8ff", fg: "#c026d3" },
22
+ groq: { bg: "#fce7f3", fg: "#db2777" },
23
+ cerebras: { bg: "#ccfbf1", fg: "#0d9488" },
24
+ zai: { bg: "#ede9fe", fg: "#7c3aed" },
25
+ "z-ai": { bg: "#ede9fe", fg: "#7c3aed" },
26
+ tuner: { bg: "#dbeafe", fg: "#2563eb" }
27
+ };
28
+ function ProviderLogo({ provider, size = 16 }) {
29
+ const key = ALIASES[provider ?? ""] ?? provider ?? "";
30
+ const logo = LOGOS[key];
31
+ if (logo) {
32
+ return /* @__PURE__ */ jsx("svg", { width: size, height: size, viewBox: logo.viewBox, role: "img", "aria-label": key, children: logo.paths.map((d, i) => /* @__PURE__ */ jsx("path", { d, fill: logo.fill }, i)) });
33
+ }
34
+ const mono = MONOGRAM[key];
35
+ return /* @__PURE__ */ jsxs("svg", { width: size, height: size, viewBox: "0 0 16 16", role: "img", "aria-label": key || "model", children: [
36
+ /* @__PURE__ */ jsx("rect", { width: "16", height: "16", rx: "4", style: { fill: mono ? mono.bg : "hsl(var(--secondary))" } }),
37
+ /* @__PURE__ */ jsx("text", { x: "8", y: "11.6", textAnchor: "middle", style: { fill: mono ? mono.fg : "hsl(var(--muted-foreground))" }, fontSize: "9", fontWeight: "700", fontFamily: "system-ui, sans-serif", children: (key || "?").charAt(0).toUpperCase() })
38
+ ] });
39
+ }
40
+
41
+ export {
42
+ ProviderLogo
43
+ };
44
+ //# sourceMappingURL=chunk-MLG6XKPV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/web-react/provider-logo.tsx"],"sourcesContent":["/**\n * Provider brand marks — real logo path data (simple-icons / SVG Logos, both\n * CC0) inlined so the picker shows actual provider identity instead of\n * colored-initial monograms. Providers without a usable mark fall back to a\n * tinted monogram chip. Aliases (z-ai/zai, moonshot/moonshotai, deepseek_ai)\n * normalize to one entry.\n */\n\nimport type { ReactNode } from 'react'\n\ninterface LogoDef {\n viewBox: string\n fill: string\n paths: string[]\n}\n\nconst LOGOS: Record<string, LogoDef> = {\n anthropic: { viewBox: '0 0 24 24', fill: '#D97757', paths: [\"M17.3041 3.541h-3.6718l6.696 16.918H24Zm-10.6082 0L0 20.459h3.7442l1.3693-3.5527h7.0052l1.3693 3.5528h3.7442L10.5363 3.5409Zm-.3712 10.2232 2.2914-5.9456 2.2914 5.9456Z\"] },\n google: { viewBox: '0 0 24 24', fill: '#8E75B2', paths: [\"M11.04 19.32Q12 21.51 12 24q0-2.49.93-4.68.96-2.19 2.58-3.81t3.81-2.55Q21.51 12 24 12q-2.49 0-4.68-.93a12.3 12.3 0 0 1-3.81-2.58 12.3 12.3 0 0 1-2.58-3.81Q12 2.49 12 0q0 2.49-.96 4.68-.93 2.19-2.55 3.81a12.3 12.3 0 0 1-3.81 2.58Q2.49 12 0 12q2.49 0 4.68.96 2.19.93 3.81 2.55t2.55 3.81\"] },\n deepseek: { viewBox: '0 0 24 24', fill: '#4D6BFE', paths: [\"M23.748 4.651c-.254-.124-.364.113-.512.233-.051.04-.094.09-.137.137-.372.397-.806.657-1.373.626-.829-.046-1.537.214-2.163.848-.133-.782-.575-1.248-1.247-1.548-.352-.155-.708-.311-.955-.65-.172-.24-.219-.509-.305-.774-.055-.16-.11-.323-.293-.35-.2-.031-.278.136-.356.276-.313.572-.434 1.202-.422 1.84.027 1.436.633 2.58 1.838 3.393.137.094.172.187.129.323-.082.28-.18.553-.266.833-.055.179-.137.218-.328.14a5.5 5.5 0 0 1-1.737-1.179c-.857-.828-1.631-1.743-2.597-2.46a12 12 0 0 0-.689-.47c-.985-.957.13-1.743.387-1.836.27-.098.094-.433-.778-.428-.872.003-1.67.295-2.687.685a3 3 0 0 1-.465.136 9.6 9.6 0 0 0-2.883-.101c-1.885.21-3.39 1.1-4.497 2.622C.082 8.776-.231 10.854.152 13.02c.403 2.284 1.568 4.175 3.36 5.653 1.857 1.533 3.997 2.284 6.438 2.14 1.482-.085 3.132-.284 4.994-1.86.47.234.962.328 1.78.398.629.058 1.235-.031 1.705-.129.735-.155.684-.836.418-.961-2.155-1.004-1.682-.595-2.112-.926 1.095-1.295 2.768-3.598 3.284-6.733.05-.346.115-.834.108-1.114-.004-.171.035-.238.23-.257a4.2 4.2 0 0 0 1.545-.475c1.397-.763 1.96-2.016 2.093-3.517.02-.23-.004-.467-.247-.588M11.58 18.168c-2.088-1.642-3.101-2.183-3.52-2.16-.39.024-.32.472-.234.763.09.288.207.487.371.74.114.167.192.416-.113.603-.673.416-1.842-.14-1.897-.168-1.361-.801-2.5-1.86-3.301-3.306-.775-1.393-1.225-2.888-1.299-4.482-.02-.385.094-.522.477-.592a4.7 4.7 0 0 1 1.53-.038c2.131.311 3.946 1.264 5.467 2.774.868.86 1.525 1.887 2.202 2.89.72 1.066 1.494 2.082 2.48 2.915.348.291.626.513.892.677-.802.09-2.14.109-3.055-.615zm1.001-6.44a.306.306 0 0 1 .415-.287.3.3 0 0 1 .113.074.3.3 0 0 1 .086.214c0 .17-.136.307-.308.307a.303.303 0 0 1-.306-.307m3.11 1.596c-.2.081-.4.151-.591.16a1.25 1.25 0 0 1-.798-.254c-.274-.23-.47-.358-.551-.758a1.7 1.7 0 0 1 .015-.588c.07-.327-.007-.537-.238-.727-.188-.156-.426-.199-.689-.199a.6.6 0 0 1-.254-.078.253.253 0 0 1-.114-.358 1 1 0 0 1 .192-.21c.356-.202.767-.136 1.146.016.352.144.618.408 1.001.782.392.451.462.576.685.915.176.264.336.536.446.848.066.194-.02.353-.25.45\"] },\n mistral: { viewBox: '0 0 24 24', fill: '#FA520F', paths: [\"M17.143 3.429v3.428h-3.429v3.429h-3.428V6.857H6.857V3.43H3.43v13.714H0v3.428h10.286v-3.428H6.857v-3.429h3.429v3.429h3.429v-3.429h3.428v3.429h-3.428v3.428H24v-3.428h-3.43V3.429z\"] },\n xai: { viewBox: '0 0 24 24', fill: '#000000', paths: [\"M14.234 10.162 22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299-.929-1.329L3.076 1.56h3.182l5.965 8.532.929 1.329 7.754 11.09h-3.182z\"] },\n nvidia: { viewBox: '0 0 24 24', fill: '#76B900', paths: [\"M8.948 8.798v-1.43a6.7 6.7 0 0 1 .424-.018c3.922-.124 6.493 3.374 6.493 3.374s-2.774 3.851-5.75 3.851c-.398 0-.787-.062-1.158-.185v-4.346c1.528.185 1.837.857 2.747 2.385l2.04-1.714s-1.492-1.952-4-1.952a6.016 6.016 0 0 0-.796.035m0-4.735v2.138l.424-.027c5.45-.185 9.01 4.47 9.01 4.47s-4.08 4.964-8.33 4.964c-.37 0-.733-.035-1.095-.097v1.325c.3.035.61.062.91.062 3.957 0 6.82-2.023 9.593-4.408.459.371 2.34 1.263 2.73 1.652-2.633 2.208-8.772 3.984-12.253 3.984-.335 0-.653-.018-.971-.053v1.864H24V4.063zm0 10.326v1.131c-3.657-.654-4.673-4.46-4.673-4.46s1.758-1.944 4.673-2.262v1.237H8.94c-1.528-.186-2.73 1.245-2.73 1.245s.68 2.412 2.739 3.11M2.456 10.9s2.164-3.197 6.5-3.533V6.201C4.153 6.59 0 10.653 0 10.653s2.35 6.802 8.948 7.42v-1.237c-4.84-.6-6.492-5.936-6.492-5.936z\"] },\n meta: { viewBox: '0 0 24 24', fill: '#0467DF', paths: [\"M6.915 4.03c-1.968 0-3.683 1.28-4.871 3.113C.704 9.208 0 11.883 0 14.449c0 .706.07 1.369.21 1.973a6.624 6.624 0 0 0 .265.86 5.297 5.297 0 0 0 .371.761c.696 1.159 1.818 1.927 3.593 1.927 1.497 0 2.633-.671 3.965-2.444.76-1.012 1.144-1.626 2.663-4.32l.756-1.339.186-.325c.061.1.121.196.183.3l2.152 3.595c.724 1.21 1.665 2.556 2.47 3.314 1.046.987 1.992 1.22 3.06 1.22 1.075 0 1.876-.355 2.455-.843a3.743 3.743 0 0 0 .81-.973c.542-.939.861-2.127.861-3.745 0-2.72-.681-5.357-2.084-7.45-1.282-1.912-2.957-2.93-4.716-2.93-1.047 0-2.088.467-3.053 1.308-.652.57-1.257 1.29-1.82 2.05-.69-.875-1.335-1.547-1.958-2.056-1.182-.966-2.315-1.303-3.454-1.303zm10.16 2.053c1.147 0 2.188.758 2.992 1.999 1.132 1.748 1.647 4.195 1.647 6.4 0 1.548-.368 2.9-1.839 2.9-.58 0-1.027-.23-1.664-1.004-.496-.601-1.343-1.878-2.832-4.358l-.617-1.028a44.908 44.908 0 0 0-1.255-1.98c.07-.109.141-.224.211-.327 1.12-1.667 2.118-2.602 3.358-2.602zm-10.201.553c1.265 0 2.058.791 2.675 1.446.307.327.737.871 1.234 1.579l-1.02 1.566c-.757 1.163-1.882 3.017-2.837 4.338-1.191 1.649-1.81 1.817-2.486 1.817-.524 0-1.038-.237-1.383-.794-.263-.426-.464-1.13-.464-2.046 0-2.221.63-4.535 1.66-6.088.454-.687.964-1.226 1.533-1.533a2.264 2.264 0 0 1 1.088-.285z\"] },\n moonshotai: { viewBox: '0 0 24 24', fill: '#16191E', paths: [\"m1.053 16.91 9.538 2.55a21 20.981 0 0 0 .06 2.031l5.956 1.592a12 11.99 0 0 1-15.554-6.172m-1.02-5.79 11.352 3.035a21 20.981 0 0 0-.469 2.01l10.817 2.89a12 11.99 0 0 1-1.845 2.004L.658 15.918a12 11.99 0 0 1-.625-4.796m1.593-5.146L13.573 9.17a21 20.981 0 0 0-1.01 1.874l11.297 3.02a21 20.981 0 0 1-.67 2.362l-11.55-3.087L.125 10.26a12 11.99 0 0 1 1.499-4.285ZM6.067 1.58l11.285 3.016a21 20.981 0 0 0-1.688 1.719l7.824 2.091a21 20.981 0 0 1 .513 2.664L2.107 5.218a12 11.99 0 0 1 3.96-3.638M21.68 4.866 7.222 1.003A12 11.99 0 0 1 21.68 4.866\"] },\n openai: { viewBox: '0 0 256 260', fill: '#10A37F', paths: [\"M239.184 106.203a64.72 64.72 0 0 0-5.576-53.103C219.452 28.459 191 15.784 163.213 21.74A65.586 65.586 0 0 0 52.096 45.22a64.72 64.72 0 0 0-43.23 31.36c-14.31 24.602-11.061 55.634 8.033 76.74a64.67 64.67 0 0 0 5.525 53.102c14.174 24.65 42.644 37.324 70.446 31.36a64.72 64.72 0 0 0 48.754 21.744c28.481.025 53.714-18.361 62.414-45.481a64.77 64.77 0 0 0 43.229-31.36c14.137-24.558 10.875-55.423-8.083-76.483m-97.56 136.338a48.4 48.4 0 0 1-31.105-11.255l1.535-.87l51.67-29.825a8.6 8.6 0 0 0 4.247-7.367v-72.85l21.845 12.636c.218.111.37.32.409.563v60.367c-.056 26.818-21.783 48.545-48.601 48.601M37.158 197.93a48.35 48.35 0 0 1-5.781-32.589l1.534.921l51.722 29.826a8.34 8.34 0 0 0 8.441 0l63.181-36.425v25.221a.87.87 0 0 1-.358.665l-52.335 30.184c-23.257 13.398-52.97 5.431-66.404-17.803M23.549 85.38a48.5 48.5 0 0 1 25.58-21.333v61.39a8.29 8.29 0 0 0 4.195 7.316l62.874 36.272l-21.845 12.636a.82.82 0 0 1-.767 0L41.353 151.53c-23.211-13.454-31.171-43.144-17.804-66.405zm179.466 41.695l-63.08-36.63L161.73 77.86a.82.82 0 0 1 .768 0l52.233 30.184a48.6 48.6 0 0 1-7.316 87.635v-61.391a8.54 8.54 0 0 0-4.4-7.213m21.742-32.69l-1.535-.922l-51.619-30.081a8.39 8.39 0 0 0-8.492 0L99.98 99.808V74.587a.72.72 0 0 1 .307-.665l52.233-30.133a48.652 48.652 0 0 1 72.236 50.391zM88.061 139.097l-21.845-12.585a.87.87 0 0 1-.41-.614V65.685a48.652 48.652 0 0 1 79.757-37.346l-1.535.87l-51.67 29.825a8.6 8.6 0 0 0-4.246 7.367zm11.868-25.58L128.067 97.3l28.188 16.218v32.434l-28.086 16.218l-28.188-16.218z\"] },\n}\n\nconst ALIASES: Record<string, string> = {\n moonshot: 'moonshotai',\n deepseek_ai: 'deepseek',\n 'x-ai': 'xai',\n 'meta-llama': 'meta',\n}\n\nconst MONOGRAM: Record<string, { bg: string; fg: string }> = {\n cohere: { bg: '#fae8ff', fg: '#c026d3' },\n groq: { bg: '#fce7f3', fg: '#db2777' },\n cerebras: { bg: '#ccfbf1', fg: '#0d9488' },\n zai: { bg: '#ede9fe', fg: '#7c3aed' },\n 'z-ai': { bg: '#ede9fe', fg: '#7c3aed' },\n tuner: { bg: '#dbeafe', fg: '#2563eb' },\n}\n\nexport interface ProviderLogoProps {\n provider?: string\n size?: number\n}\n\n/** Real brand mark when we have one; tinted monogram otherwise. */\nexport function ProviderLogo({ provider, size = 16 }: ProviderLogoProps): ReactNode {\n const key = ALIASES[provider ?? ''] ?? provider ?? ''\n const logo = LOGOS[key]\n if (logo) {\n return (\n <svg width={size} height={size} viewBox={logo.viewBox} role=\"img\" aria-label={key}>\n {logo.paths.map((d, i) => (\n <path key={i} d={d} fill={logo.fill} />\n ))}\n </svg>\n )\n }\n // Unknown providers fall back to a neutral monogram drawn from theme tokens\n // (secondary tile, muted glyph) — the previous hardcoded grays (#f3f4f6 /\n // #6b7280) were tuned for light mode and read as a washed-out hole on dark.\n // `style` rather than the `fill` attribute so the var() references resolve.\n const mono = MONOGRAM[key]\n return (\n <svg width={size} height={size} viewBox=\"0 0 16 16\" role=\"img\" aria-label={key || 'model'}>\n <rect width=\"16\" height=\"16\" rx=\"4\" style={{ fill: mono ? mono.bg : 'hsl(var(--secondary))' }} />\n <text x=\"8\" y=\"11.6\" textAnchor=\"middle\" style={{ fill: mono ? mono.fg : 'hsl(var(--muted-foreground))' }} fontSize=\"9\" fontWeight=\"700\" fontFamily=\"system-ui, sans-serif\">\n {(key || '?').charAt(0).toUpperCase()}\n </text>\n </svg>\n )\n}\n"],"mappings":";AAyDU,cAWN,YAXM;AAzCV,IAAM,QAAiC;AAAA,EACrC,WAAW,EAAE,SAAS,aAAa,MAAM,WAAW,OAAO,CAAC,0KAA0K,EAAE;AAAA,EACxO,QAAQ,EAAE,SAAS,aAAa,MAAM,WAAW,OAAO,CAAC,8RAA8R,EAAE;AAAA,EACzV,UAAU,EAAE,SAAS,aAAa,MAAM,WAAW,OAAO,CAAC,w7DAAw7D,EAAE;AAAA,EACr/D,SAAS,EAAE,SAAS,aAAa,MAAM,WAAW,OAAO,CAAC,kLAAkL,EAAE;AAAA,EAC9O,KAAK,EAAE,SAAS,aAAa,MAAM,WAAW,OAAO,CAAC,oMAAoM,EAAE;AAAA,EAC5P,QAAQ,EAAE,SAAS,aAAa,MAAM,WAAW,OAAO,CAAC,qwBAAqwB,EAAE;AAAA,EACh0B,MAAM,EAAE,SAAS,aAAa,MAAM,WAAW,OAAO,CAAC,ksCAAksC,EAAE;AAAA,EAC3vC,YAAY,EAAE,SAAS,aAAa,MAAM,WAAW,OAAO,CAAC,2hBAA2hB,EAAE;AAAA,EAC1lB,QAAQ,EAAE,SAAS,eAAe,MAAM,WAAW,OAAO,CAAC,28CAA28C,EAAE;AAC1gD;AAEA,IAAM,UAAkC;AAAA,EACtC,UAAU;AAAA,EACV,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,cAAc;AAChB;AAEA,IAAM,WAAuD;AAAA,EAC3D,QAAQ,EAAE,IAAI,WAAW,IAAI,UAAU;AAAA,EACvC,MAAM,EAAE,IAAI,WAAW,IAAI,UAAU;AAAA,EACrC,UAAU,EAAE,IAAI,WAAW,IAAI,UAAU;AAAA,EACzC,KAAK,EAAE,IAAI,WAAW,IAAI,UAAU;AAAA,EACpC,QAAQ,EAAE,IAAI,WAAW,IAAI,UAAU;AAAA,EACvC,OAAO,EAAE,IAAI,WAAW,IAAI,UAAU;AACxC;AAQO,SAAS,aAAa,EAAE,UAAU,OAAO,GAAG,GAAiC;AAClF,QAAM,MAAM,QAAQ,YAAY,EAAE,KAAK,YAAY;AACnD,QAAM,OAAO,MAAM,GAAG;AACtB,MAAI,MAAM;AACR,WACE,oBAAC,SAAI,OAAO,MAAM,QAAQ,MAAM,SAAS,KAAK,SAAS,MAAK,OAAM,cAAY,KAC3E,eAAK,MAAM,IAAI,CAAC,GAAG,MAClB,oBAAC,UAAa,GAAM,MAAM,KAAK,QAApB,CAA0B,CACtC,GACH;AAAA,EAEJ;AAKA,QAAM,OAAO,SAAS,GAAG;AACzB,SACE,qBAAC,SAAI,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,OAAM,cAAY,OAAO,SAChF;AAAA,wBAAC,UAAK,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI,OAAO,EAAE,MAAM,OAAO,KAAK,KAAK,wBAAwB,GAAG;AAAA,IAC/F,oBAAC,UAAK,GAAE,KAAI,GAAE,QAAO,YAAW,UAAS,OAAO,EAAE,MAAM,OAAO,KAAK,KAAK,+BAA+B,GAAG,UAAS,KAAI,YAAW,OAAM,YAAW,yBAChJ,kBAAO,KAAK,OAAO,CAAC,EAAE,YAAY,GACtC;AAAA,KACF;AAEJ;","names":[]}
@@ -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