@tangle-network/agent-app 0.45.46 → 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 (79) 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-UM6ODCLN.js → chunk-C7AQW6US.js} +346 -249
  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/theme/tokens.css +173 -0
  54. package/dist/web-react/index.d.ts +15 -11
  55. package/dist/web-react/index.js +6 -4
  56. package/dist/work-product-react/index.js +3 -21
  57. package/dist/work-product-react/index.js.map +1 -1
  58. package/package.json +3 -3
  59. package/dist/InvitationsPanel-F56UOQXA.js +0 -8
  60. package/dist/InviteAcceptPage-P3QJC3C6.js +0 -7
  61. package/dist/MembersPanel-XNLF3SA4.js +0 -8
  62. package/dist/chunk-54FYYXAX.js.map +0 -1
  63. package/dist/chunk-65OZT566.js.map +0 -1
  64. package/dist/chunk-BT4QFCTB.js.map +0 -1
  65. package/dist/chunk-F4XLTVQ3.js.map +0 -1
  66. package/dist/chunk-GNPN6ED4.js.map +0 -1
  67. package/dist/chunk-IUSO2XRE.js.map +0 -1
  68. package/dist/chunk-PE67O2LH.js.map +0 -1
  69. package/dist/chunk-UM6ODCLN.js.map +0 -1
  70. package/dist/chunk-VCPZ3HTN.js.map +0 -1
  71. package/dist/chunk-VOJB7MVG.js.map +0 -1
  72. /package/dist/{DesignCanvas-P2ZVK6FM.js.map → DesignCanvas-QY2CA5GI.js.map} +0 -0
  73. /package/dist/{DesignCanvasEditor-DGNCA54P.js.map → DesignCanvasEditor-CJ7PLX7A.js.map} +0 -0
  74. /package/dist/{IntakeInterview-3ZIQMZS6.js.map → IntakeInterview-NJXKH35Q.js.map} +0 -0
  75. /package/dist/{InvitationsPanel-F56UOQXA.js.map → InvitationsPanel-F2VY4TSL.js.map} +0 -0
  76. /package/dist/{InviteAcceptPage-P3QJC3C6.js.map → InviteAcceptPage-UT4452KT.js.map} +0 -0
  77. /package/dist/{MembersPanel-XNLF3SA4.js.map → MembersPanel-N5CBOUXC.js.map} +0 -0
  78. /package/dist/{TimelineEditor-OTNPJ3SN.js.map → TimelineEditor-IYNKBSUX.js.map} +0 -0
  79. /package/dist/{chunk-B62EZF52.js.map → chunk-RILGAFEL.js.map} +0 -0
@@ -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-[11px] 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-XPFNEKHS.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-[11px] font-semibold uppercase tracking-[0.05em]\">Tangle Intake</span>\n </span>\n <h2 className=\"text-xl font-semibold text-[var(--text-primary)]\">{view.title}</h2>\n {view.description && <p className=\"text-sm text-[var(--text-muted)]\">{view.description}</p>}\n <ProgressBar answered={view.progress.answered} total={view.progress.total} />\n </header>\n\n {view.completed ? (\n <div className=\"flex items-center gap-2.5 rounded-lg border border-[hsl(var(--success)/0.4)] bg-[hsl(var(--success)/0.08)] px-3.5 py-3\">\n <svg viewBox=\"0 0 24 24\" className=\"h-4 w-4 shrink-0 text-[var(--surface-success-text)]\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n <p className=\"text-sm text-[var(--text-primary)]\">You're all set — every answer is saved. You can close this panel.</p>\n </div>\n ) : question ? (\n <div className=\"flex flex-col gap-3\">\n <div className=\"flex flex-col gap-1\">\n <p className=\"text-sm font-medium text-[var(--text-primary)]\">{question.prompt}</p>\n {question.help && <p className=\"text-xs text-[var(--text-muted)]\">{question.help}</p>}\n </div>\n\n <AnswerField question={question} value={draft} onChange={setDraft} onSubmit={() => void submit()} />\n\n <div className=\"flex justify-end\">\n <button\n type=\"button\"\n onClick={() => void submit()}\n disabled={busy || isDraftEmpty(draft)}\n className=\"rounded bg-[var(--brand-primary)] px-4 py-1.5 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50\"\n >\n {busy ? 'Saving…' : 'Continue'}\n </button>\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col gap-3\">\n <p className=\"text-sm text-[var(--text-secondary)]\">That's everything. Ready to finish?</p>\n <div className=\"flex justify-end\">\n <button\n type=\"button\"\n onClick={() => void finish()}\n disabled={busy}\n className=\"rounded bg-[var(--brand-primary)] px-4 py-1.5 text-sm text-[hsl(var(--primary-foreground))] disabled:opacity-50\"\n >\n {busy ? 'Finishing…' : 'Finish'}\n </button>\n </div>\n </div>\n )}\n </section>\n )\n}\n\ninterface AnswerFieldProps {\n question: IntakeQuestion\n value: IntakeAnswerValue\n onChange(value: IntakeAnswerValue): void\n onSubmit(): void\n}\n\nfunction AnswerField({ question, value, onChange, onSubmit }: AnswerFieldProps) {\n const inputClass =\n 'rounded border border-[var(--border-default)] bg-[var(--bg-input)] px-3 py-2 text-sm text-[var(--text-primary)] placeholder:text-[var(--text-muted)]'\n\n switch (question.type) {\n case 'long-text':\n return (\n <textarea\n rows={4}\n aria-label={question.prompt}\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => onChange(event.target.value)}\n className={inputClass}\n />\n )\n case 'boolean':\n return (\n <div className=\"flex gap-2\">\n {[{ v: true, l: 'Yes' }, { v: false, l: 'No' }].map((opt) => (\n <button\n key={opt.l}\n type=\"button\"\n onClick={() => onChange(opt.v)}\n className={`rounded border px-4 py-1.5 text-sm ${\n value === opt.v\n ? 'border-[var(--brand-primary)] bg-[var(--brand-primary)] text-[hsl(var(--primary-foreground))]'\n : 'border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]'\n }`}\n >\n {opt.l}\n </button>\n ))}\n </div>\n )\n case 'number':\n return (\n <input\n type=\"number\"\n aria-label={question.prompt}\n value={typeof value === 'number' ? value : ''}\n onChange={(event) => onChange(event.target.value === '' ? null : Number(event.target.value))}\n onKeyDown={(event) => { if (event.key === 'Enter') onSubmit() }}\n className={inputClass}\n />\n )\n case 'single-select':\n return (\n <div className=\"flex flex-col gap-1.5\">\n {(question.options ?? []).map((option) => (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => onChange(option.value)}\n className={`flex items-center gap-2 rounded border px-3 py-2 text-left text-sm ${\n value === option.value\n ? 'border-[var(--brand-primary)] bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)] text-[var(--text-primary)]'\n : 'border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]'\n }`}\n >\n {value === option.value && <ChoiceCheck />}\n {option.label}\n </button>\n ))}\n </div>\n )\n case 'multi-select': {\n const selected = Array.isArray(value) ? value : []\n return (\n <div className=\"flex flex-col gap-1.5\">\n {(question.options ?? []).map((option) => {\n const on = selected.includes(option.value)\n return (\n <button\n key={option.value}\n type=\"button\"\n onClick={() =>\n onChange(on ? selected.filter((v) => v !== option.value) : [...selected, option.value])\n }\n className={`flex items-center gap-2 rounded border px-3 py-2 text-left text-sm ${\n on\n ? 'border-[var(--brand-primary)] bg-[color-mix(in_srgb,var(--brand-primary)_10%,transparent)] text-[var(--text-primary)]'\n : 'border-[var(--border-default)] bg-[var(--bg-input)] text-[var(--text-secondary)]'\n }`}\n >\n {on && <ChoiceCheck />}\n {option.label}\n </button>\n )\n })}\n </div>\n )\n }\n default:\n return (\n <input\n type={question.type === 'email' ? 'email' : question.type === 'url' ? 'url' : 'text'}\n aria-label={question.prompt}\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => onChange(event.target.value)}\n onKeyDown={(event) => { if (event.key === 'Enter') onSubmit() }}\n className={inputClass}\n />\n )\n }\n}\n\n/** Leading check for a selected choice option (single/multi-select). */\nfunction ChoiceCheck() {\n return (\n <svg viewBox=\"0 0 24 24\" className=\"h-3.5 w-3.5 shrink-0 text-[var(--brand-primary)]\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n )\n}\n\nfunction ProgressBar({ answered, total }: { answered: number; total: number }) {\n const pct = total > 0 ? Math.round((answered / total) * 100) : 0\n return (\n <div className=\"flex items-center gap-2\">\n <div className=\"h-1.5 flex-1 overflow-hidden rounded-full bg-[var(--bg-input)]\">\n <div className=\"h-full rounded-full bg-[var(--brand-primary)] transition-all\" style={{ width: `${pct}%` }} />\n </div>\n <span className=\"text-xs text-[var(--text-muted)]\">{answered}/{total}</span>\n </div>\n )\n}\n\nfunction currentAnswer(view: IntakeView): IntakeAnswerValue {\n const id = view.nextQuestion?.id\n if (!id) return null\n return view.answers[id] ?? defaultDraft(view.nextQuestion!)\n}\n\nfunction defaultDraft(question: IntakeQuestion): IntakeAnswerValue {\n return question.type === 'multi-select' ? [] : null\n}\n\n/** No answer yet — null, empty text, or an empty multi-select. `false` and `0`\n * are real answers and never gate Continue. */\nfunction isDraftEmpty(value: IntakeAnswerValue): boolean {\n if (value === null || value === undefined) return true\n if (typeof value === 'string') return value.trim() === ''\n if (Array.isArray(value)) return value.length === 0\n return false\n}\n\n/** Trim text answers before submit; pass others through unchanged. */\nfunction normalize(question: IntakeQuestion, value: IntakeAnswerValue): IntakeAnswerValue {\n if (typeof value === 'string' && (question.type === 'text' || question.type === 'long-text' || question.type === 'url' || question.type === 'email')) {\n return value.trim()\n }\n return value\n}\n","/**\n * Load the canonical Tangle knot only when an intakes surface renders it.\n * `/brand` imports the optional `@tangle-network/brand` peer, so a failed load\n * renders a fixed-size spacer instead of crashing the panel.\n */\n\nimport { lazy, Suspense } from 'react'\nimport type { ComponentType } from 'react'\n\nexport interface BrandMarkProps {\n size: number\n className?: string\n}\n\n/** Preserve the mark's footprint while its optional package loads or is absent. */\nfunction MarkSpacer({ size }: { size: number }) {\n return <span aria-hidden style={{ display: 'inline-block', width: size, height: size }} />\n}\n\nconst LazyKnot = lazy(async () => {\n try {\n const mod = await import('../../brand')\n return { default: mod.TangleKnot as ComponentType<BrandMarkProps> }\n } catch {\n return { default: MarkSpacer as ComponentType<BrandMarkProps> }\n }\n})\n\nexport function BrandMark({ size, className }: BrandMarkProps) {\n return (\n <Suspense fallback={<MarkSpacer size={size} />}>\n <LazyKnot size={size} className={className} />\n </Suspense>\n )\n}\n"],"mappings":"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","names":["jsx"]}