@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.
- package/dist/{DesignCanvas-P2ZVK6FM.js → DesignCanvas-QY2CA5GI.js} +2 -2
- package/dist/{DesignCanvasEditor-DGNCA54P.js → DesignCanvasEditor-CJ7PLX7A.js} +3 -3
- package/dist/{IntakeInterview-3ZIQMZS6.js → IntakeInterview-NJXKH35Q.js} +2 -2
- package/dist/InvitationsPanel-F2VY4TSL.js +9 -0
- package/dist/InviteAcceptPage-UT4452KT.js +7 -0
- package/dist/MembersPanel-N5CBOUXC.js +9 -0
- package/dist/{TimelineEditor-OTNPJ3SN.js → TimelineEditor-IYNKBSUX.js} +2 -2
- package/dist/assistant/index.d.ts +35 -10
- package/dist/assistant/index.js +234 -89
- package/dist/assistant/index.js.map +1 -1
- package/dist/chat-react/index.js +2 -1
- package/dist/chat-react/index.js.map +1 -1
- package/dist/{chunk-IUSO2XRE.js → chunk-5BACNOVR.js} +13 -14
- package/dist/chunk-5BACNOVR.js.map +1 -0
- package/dist/chunk-AULM4X7U.js +77 -0
- package/dist/chunk-AULM4X7U.js.map +1 -0
- package/dist/{chunk-UM6ODCLN.js → chunk-C7AQW6US.js} +346 -249
- package/dist/chunk-C7AQW6US.js.map +1 -0
- package/dist/{chunk-VCPZ3HTN.js → chunk-DB2D2JBQ.js} +51 -27
- package/dist/chunk-DB2D2JBQ.js.map +1 -0
- package/dist/{chunk-TL37E5YY.js → chunk-DS2WE62K.js} +3 -3
- package/dist/{chunk-TL37E5YY.js.map → chunk-DS2WE62K.js.map} +1 -1
- package/dist/{chunk-PE67O2LH.js → chunk-GT4GTRGQ.js} +18 -13
- package/dist/chunk-GT4GTRGQ.js.map +1 -0
- package/dist/{chunk-BT4QFCTB.js → chunk-HSBJB46C.js} +13 -1
- package/dist/chunk-HSBJB46C.js.map +1 -0
- package/dist/{chunk-54FYYXAX.js → chunk-J6OJTFNE.js} +13 -10
- package/dist/chunk-J6OJTFNE.js.map +1 -0
- package/dist/chunk-MLG6XKPV.js +44 -0
- package/dist/chunk-MLG6XKPV.js.map +1 -0
- package/dist/{chunk-F4XLTVQ3.js → chunk-R6KGLGEJ.js} +18 -17
- package/dist/chunk-R6KGLGEJ.js.map +1 -0
- package/dist/{chunk-B62EZF52.js → chunk-RILGAFEL.js} +3 -3
- package/dist/{chunk-65OZT566.js → chunk-T7XPIBCX.js} +16 -14
- package/dist/chunk-T7XPIBCX.js.map +1 -0
- package/dist/{chunk-VOJB7MVG.js → chunk-XPFNEKHS.js} +70 -28
- package/dist/chunk-XPFNEKHS.js.map +1 -0
- package/dist/{chunk-GNPN6ED4.js → chunk-XVRHISF3.js} +127 -165
- package/dist/chunk-XVRHISF3.js.map +1 -0
- package/dist/design-canvas-react/index.js +14 -12
- package/dist/design-canvas-react/index.js.map +1 -1
- package/dist/design-canvas-react/lazy.js +1 -1
- package/dist/intakes-react/index.js +1 -1
- package/dist/intakes-react/lazy.js +1 -1
- package/dist/sequences-react/index.js +2 -2
- package/dist/studio/index.d.ts +13 -1
- package/dist/studio/index.js +13 -1
- package/dist/studio-react/index.js +100 -63
- package/dist/studio-react/index.js.map +1 -1
- package/dist/studio-react/studio.css +0 -56
- package/dist/teams-react/index.js +4 -3
- package/dist/teams-react/lazy.js +3 -3
- package/dist/theme/tokens.css +173 -0
- package/dist/web-react/index.d.ts +15 -11
- package/dist/web-react/index.js +6 -4
- package/dist/work-product-react/index.js +3 -21
- package/dist/work-product-react/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/InvitationsPanel-F56UOQXA.js +0 -8
- package/dist/InviteAcceptPage-P3QJC3C6.js +0 -7
- package/dist/MembersPanel-XNLF3SA4.js +0 -8
- package/dist/chunk-54FYYXAX.js.map +0 -1
- package/dist/chunk-65OZT566.js.map +0 -1
- package/dist/chunk-BT4QFCTB.js.map +0 -1
- package/dist/chunk-F4XLTVQ3.js.map +0 -1
- package/dist/chunk-GNPN6ED4.js.map +0 -1
- package/dist/chunk-IUSO2XRE.js.map +0 -1
- package/dist/chunk-PE67O2LH.js.map +0 -1
- package/dist/chunk-UM6ODCLN.js.map +0 -1
- package/dist/chunk-VCPZ3HTN.js.map +0 -1
- package/dist/chunk-VOJB7MVG.js.map +0 -1
- /package/dist/{DesignCanvas-P2ZVK6FM.js.map → DesignCanvas-QY2CA5GI.js.map} +0 -0
- /package/dist/{DesignCanvasEditor-DGNCA54P.js.map → DesignCanvasEditor-CJ7PLX7A.js.map} +0 -0
- /package/dist/{IntakeInterview-3ZIQMZS6.js.map → IntakeInterview-NJXKH35Q.js.map} +0 -0
- /package/dist/{InvitationsPanel-F56UOQXA.js.map → InvitationsPanel-F2VY4TSL.js.map} +0 -0
- /package/dist/{InviteAcceptPage-P3QJC3C6.js.map → InviteAcceptPage-UT4452KT.js.map} +0 -0
- /package/dist/{MembersPanel-XNLF3SA4.js.map → MembersPanel-N5CBOUXC.js.map} +0 -0
- /package/dist/{TimelineEditor-OTNPJ3SN.js.map → TimelineEditor-IYNKBSUX.js.map} +0 -0
- /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
|
-
|
|
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__ */
|
|
67
|
-
|
|
68
|
-
|
|
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__ */
|
|
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__ */
|
|
73
|
-
question.help && /* @__PURE__ */
|
|
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__ */
|
|
76
|
-
/* @__PURE__ */
|
|
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-
|
|
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__ */
|
|
88
|
-
/* @__PURE__ */
|
|
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-
|
|
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__ */
|
|
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__ */
|
|
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-
|
|
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__ */
|
|
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__ */
|
|
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(--
|
|
147
|
-
children:
|
|
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__ */
|
|
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__ */
|
|
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(--
|
|
161
|
-
children:
|
|
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__ */
|
|
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__ */
|
|
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-
|
|
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"]}
|