@invana/blocks 0.0.32
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/README.md +52 -0
- package/dist/index.cjs +2095 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2245 -0
- package/dist/index.d.ts +2245 -0
- package/dist/index.js +2018 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2018 @@
|
|
|
1
|
+
import * as React18 from 'react';
|
|
2
|
+
import { HeatStrip, BarChartV, LineChart, BarChartH, Sparkline, HeatLane } from '@invana/charts';
|
|
3
|
+
import { Eyebrow, cn, ClarifyActions, ClarifyFootnote, TypographyH3, TypographyMuted, SectionHeader, Button, Gantt, PropertyList, PropertyRow, TraceList, TraceStep, ChatSessionDisclosure, ScopeLine, CannotAnswerCard, ProposalCard, MetricGrid, MetricTile, ArtifactCard, Badge, ArtifactTable, CitationList, CitationRow, ChatSessionDisclosureCode, ChatSessionDisclosureSteps, TimelineList, Avatar, AvatarFallback, Questionnaire, QuestionnaireProgress, QuestionnaireActions, QuestionnairePrevious, QuestionnaireSkip, QuestionnaireNext, QuestionnaireSubmit, QuestionnaireProgressBar, SegmentedControl, QuestionnaireItem, QuestionnaireTitle, QuestionnaireDescription, QuestionnaireChoices, QuestionnaireChoice, QuestionnaireChoiceTitle, QuestionnaireInput, CaveatNote, TimelineSection, TimelineEntry, StatusDot, CitationMarker, QuestionnaireChoiceDescription, Kbd } from '@invana/ui';
|
|
4
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
|
+
import { useForm } from 'react-hook-form';
|
|
6
|
+
import { Form, ObjectField } from '@invana/forms';
|
|
7
|
+
import { DataTable } from '@invana/tables';
|
|
8
|
+
|
|
9
|
+
// src/kinds.ts
|
|
10
|
+
var BLOCKS = [
|
|
11
|
+
{ id: "confirm", name: "Confirm", returns: "boolean", tier: "today" },
|
|
12
|
+
{ id: "single", name: "Single choice", returns: "string", tier: "today" },
|
|
13
|
+
{ id: "multi", name: "Multiple choice", returns: "string[]", tier: "today" },
|
|
14
|
+
{ id: "quick", name: "Quick pick", returns: "string", tier: "today" },
|
|
15
|
+
{ id: "period", name: "Period", returns: "{from, to, label}", tier: "today" },
|
|
16
|
+
{ id: "number", name: "Number", returns: "number", tier: "today" },
|
|
17
|
+
{ id: "short", name: "Short text", returns: "string", tier: "today" },
|
|
18
|
+
{ id: "long", name: "Long text", returns: "string", tier: "today" },
|
|
19
|
+
{ id: "entity", name: "Entity pick", returns: "id[]", tier: "next" },
|
|
20
|
+
{ id: "scale", name: "Scale", returns: "1\u20135", tier: "today" },
|
|
21
|
+
{ id: "multistep", name: "Multi-step", returns: "Record<id, value>", tier: "today" },
|
|
22
|
+
{ id: "form", name: "Form", returns: "Record<field, value>", tier: "next" },
|
|
23
|
+
{ id: "weights", name: "Weights", returns: "Record<objective, 0\u2013100>", tier: "today" },
|
|
24
|
+
{ id: "approval", name: "Approval", returns: "approve | reject", tier: "today" },
|
|
25
|
+
{ id: "interpretation", name: "Interpretation", returns: "Record<slot, value>", tier: "today" },
|
|
26
|
+
{ id: "fork", name: "Reading fork", returns: "readingId", tier: "next" },
|
|
27
|
+
{ id: "plan", name: "Plan preview", returns: "stepId[]", tier: "next" },
|
|
28
|
+
{ id: "modelspec", name: "Model spec", returns: "{outcome, predictors[], group?, controls[]}", tier: "next" },
|
|
29
|
+
{ id: "hypothesis", name: "Hypothesis", returns: "{test, tails, alpha}", tier: "next" },
|
|
30
|
+
{ id: "range", name: "Range", returns: "{min, max}", tier: "next" },
|
|
31
|
+
{ id: "suggestions", name: "Suggestions", returns: "string", tier: "today" },
|
|
32
|
+
{ id: "narrative", name: "Narrative", tier: "today" },
|
|
33
|
+
{ id: "metric", name: "Metric", tier: "today" },
|
|
34
|
+
{ id: "grid", name: "Metric grid", tier: "today" },
|
|
35
|
+
{ id: "table", name: "Table preview", tier: "today" },
|
|
36
|
+
{ id: "attr", name: "Attribute matrix", tier: "today" },
|
|
37
|
+
{ id: "record", name: "Record", tier: "today" },
|
|
38
|
+
{ id: "ranked", name: "Ranked list", tier: "today" },
|
|
39
|
+
{ id: "timeseries", name: "Time series", tier: "today" },
|
|
40
|
+
{ id: "bars", name: "Bar comparison", tier: "next" },
|
|
41
|
+
{ id: "waterfall", name: "Bridge", tier: "next" },
|
|
42
|
+
{ id: "matrix", name: "Matrix", tier: "next" },
|
|
43
|
+
{ id: "funnel", name: "Funnel", tier: "next" },
|
|
44
|
+
{ id: "histogram", name: "Distribution", tier: "next" },
|
|
45
|
+
{ id: "timeline", name: "Timeline", tier: "today" },
|
|
46
|
+
{ id: "subgraph", name: "Subgraph", tier: "later" },
|
|
47
|
+
{ id: "method", name: "Method", tier: "today" },
|
|
48
|
+
{ id: "citations", name: "Citations", tier: "today" },
|
|
49
|
+
{ id: "files", name: "Files", tier: "today" },
|
|
50
|
+
{ id: "proposal", name: "Proposal", tier: "today" },
|
|
51
|
+
{ id: "cannot", name: "Cannot answer", tier: "today" },
|
|
52
|
+
{ id: "caveat", name: "Caveat", tier: "today" },
|
|
53
|
+
{ id: "scope", name: "Scope line", tier: "today" },
|
|
54
|
+
{ id: "checks", name: "Checks", tier: "next" },
|
|
55
|
+
{ id: "trace", name: "Progress trace", tier: "today" },
|
|
56
|
+
{ id: "activity", name: "Layer activity", tier: "today" },
|
|
57
|
+
{ id: "gantt", name: "Task timeline", tier: "today" },
|
|
58
|
+
{ id: "heatstrip", name: "Heat strip", tier: "today" },
|
|
59
|
+
{ id: "test", name: "Test result", tier: "next" },
|
|
60
|
+
{ id: "coef", name: "Coefficients", tier: "next" },
|
|
61
|
+
{ id: "forest", name: "Forest plot", tier: "next" },
|
|
62
|
+
{ id: "scatter", name: "Scatter", tier: "next" },
|
|
63
|
+
{ id: "box", name: "Box plot", tier: "next" },
|
|
64
|
+
{ id: "correlation", name: "Correlation", tier: "next" },
|
|
65
|
+
{ id: "control", name: "Control chart", tier: "next" },
|
|
66
|
+
{ id: "pareto", name: "Pareto", tier: "next" },
|
|
67
|
+
{ id: "survival", name: "Survival curve", tier: "next" },
|
|
68
|
+
{ id: "tornado", name: "Tornado", tier: "next" },
|
|
69
|
+
{ id: "decomposition", name: "Decomposition", tier: "next" },
|
|
70
|
+
{ id: "modeleval", name: "Model evaluation", tier: "next" },
|
|
71
|
+
{ id: "pivot", name: "Pivot", tier: "next" },
|
|
72
|
+
{ id: "profile", name: "Column profile", tier: "next" },
|
|
73
|
+
{ id: "evidence", name: "Evidence strength", tier: "next" },
|
|
74
|
+
{ id: "dumbbell", name: "Dumbbell", tier: "next" },
|
|
75
|
+
{ id: "quantiles", name: "Quantiles", tier: "today" }
|
|
76
|
+
];
|
|
77
|
+
var ASK_KINDS = BLOCKS.filter((b) => "returns" in b).map((b) => b.id);
|
|
78
|
+
var ANSWER_KINDS = BLOCKS.filter((b) => !("returns" in b)).map((b) => b.id);
|
|
79
|
+
|
|
80
|
+
// src/values.ts
|
|
81
|
+
var VALUE_TYPES = [
|
|
82
|
+
{ id: "quantity", name: "Quantity with unit", example: "2,480 kg/ha \xB7 26 d" },
|
|
83
|
+
{ id: "currency", name: "Currency", example: "\xA384k \xB7 \u2212\xA3620k \xB7 \xA34.1M" },
|
|
84
|
+
{ id: "percent", name: "Share", example: "76% \xB7 8.2%" },
|
|
85
|
+
{ id: "pp", name: "Percentage points", example: "+4.8 pts \xB7 \u25BC 1.8 pts" },
|
|
86
|
+
{ id: "bp", name: "Basis points", example: "+12 bp" },
|
|
87
|
+
{ id: "ratio", name: "Ratio or index", example: "OR 1.29 \xB7 beta 0.38 \xB7 1.3\xD7" },
|
|
88
|
+
{ id: "rate", name: "Rate per N", example: "8.2 per 100 discharges" },
|
|
89
|
+
{ id: "count", name: "Count with n", example: "1,284 plots \xB7 n = 1,061" },
|
|
90
|
+
{ id: "duration", name: "Duration", example: "1.2 s \xB7 3 min \xB7 26 d" },
|
|
91
|
+
{ id: "estimate", name: "Estimate with interval", example: "1.29 (95% CI 1.04\u20131.60)" },
|
|
92
|
+
{ id: "pvalue", name: "p-value", example: "p = 0.02 \xB7 p < 0.001" },
|
|
93
|
+
{ id: "score", name: "Ordinal score", example: "7 / 9 \xB7 4 of 5" },
|
|
94
|
+
{ id: "time", name: "Point in time", example: "as of 29 Sep 06:00 \xB7 Q3 2026" }
|
|
95
|
+
];
|
|
96
|
+
var VARIANT = {
|
|
97
|
+
primary: "default",
|
|
98
|
+
secondary: "outline",
|
|
99
|
+
ghost: "ghost",
|
|
100
|
+
link: "link"
|
|
101
|
+
};
|
|
102
|
+
function ActionRow({
|
|
103
|
+
actions,
|
|
104
|
+
onAction
|
|
105
|
+
}) {
|
|
106
|
+
return /* @__PURE__ */ jsx(ClarifyActions, { className: "w-full", children: actions.map((a) => /* @__PURE__ */ jsxs(React18.Fragment, { children: [
|
|
107
|
+
a.push ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "flex-1" }) : null,
|
|
108
|
+
/* @__PURE__ */ jsx(
|
|
109
|
+
Button,
|
|
110
|
+
{
|
|
111
|
+
size: "xs",
|
|
112
|
+
variant: VARIANT[a.variant ?? "secondary"],
|
|
113
|
+
onClick: () => onAction(a.id),
|
|
114
|
+
children: a.label
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
] }, a.id)) });
|
|
118
|
+
}
|
|
119
|
+
function strong(text) {
|
|
120
|
+
return text.split(/(\*\*[^*]+\*\*)/).filter(Boolean).map(
|
|
121
|
+
(part, i) => part.startsWith("**") && part.endsWith("**") ? /* @__PURE__ */ jsx("strong", { children: part.slice(2, -2) }, i) : part
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
var DIRECTION = /([▲▼]\s?[+−-]?[\d.,]+(?:\s?(?:pts|pp|bps|%))?)/;
|
|
125
|
+
var MARKER = /(\[\d+\])/;
|
|
126
|
+
function tone(figure2) {
|
|
127
|
+
return figure2.startsWith("\u25B2") ? "text-primary" : "text-destructive";
|
|
128
|
+
}
|
|
129
|
+
function prose(text, marker) {
|
|
130
|
+
let key = 0;
|
|
131
|
+
const plain = (part) => part.split(MARKER).filter(Boolean).flatMap((piece) => {
|
|
132
|
+
const cite = piece.match(/^\[(\d+)\]$/);
|
|
133
|
+
if (cite) return [/* @__PURE__ */ jsx(React18.Fragment, { children: marker(Number(cite[1])) }, key++)];
|
|
134
|
+
return piece.split(DIRECTION).filter(Boolean).map(
|
|
135
|
+
(bit) => DIRECTION.test(bit) && /^[▲▼]/.test(bit) ? /* @__PURE__ */ jsx("span", { className: tone(bit), children: bit }, key++) : /* @__PURE__ */ jsx(React18.Fragment, { children: bit }, key++)
|
|
136
|
+
);
|
|
137
|
+
});
|
|
138
|
+
return text.split(/(\*\*[^*]+\*\*)/).filter(Boolean).flatMap((part) => {
|
|
139
|
+
if (!(part.startsWith("**") && part.endsWith("**"))) return plain(part);
|
|
140
|
+
const figure2 = part.slice(2, -2);
|
|
141
|
+
return [
|
|
142
|
+
/* @__PURE__ */ jsx("strong", { className: /^[▲▼]/.test(figure2) ? tone(figure2) : void 0, children: figure2 }, key++)
|
|
143
|
+
];
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
var RATE_TONE = { hot: "text-info", bad: "text-destructive" };
|
|
147
|
+
var lit = (lane) => lane.cells.some((v) => v != null && v > 0);
|
|
148
|
+
function LaneRow({
|
|
149
|
+
lane,
|
|
150
|
+
depth,
|
|
151
|
+
open,
|
|
152
|
+
onToggle,
|
|
153
|
+
stale,
|
|
154
|
+
cell,
|
|
155
|
+
pin,
|
|
156
|
+
onPin
|
|
157
|
+
}) {
|
|
158
|
+
const name = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
159
|
+
/* @__PURE__ */ jsx("span", { className: "truncate @max-[300px]/activity:hidden", children: lane.label }),
|
|
160
|
+
/* @__PURE__ */ jsx("span", { className: "truncate @min-[301px]/activity:hidden", children: lane.short ?? lane.label })
|
|
161
|
+
] });
|
|
162
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
163
|
+
onToggle ? /* @__PURE__ */ jsxs(
|
|
164
|
+
"button",
|
|
165
|
+
{
|
|
166
|
+
type: "button",
|
|
167
|
+
"aria-expanded": open,
|
|
168
|
+
onClick: onToggle,
|
|
169
|
+
className: "flex min-w-0 items-center gap-1 text-left hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
170
|
+
children: [
|
|
171
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "w-2.5 shrink-0 text-muted-foreground", children: open ? "\u25BE" : "\u25B8" }),
|
|
172
|
+
name
|
|
173
|
+
]
|
|
174
|
+
}
|
|
175
|
+
) : /* @__PURE__ */ jsx(
|
|
176
|
+
"span",
|
|
177
|
+
{
|
|
178
|
+
className: cn(
|
|
179
|
+
"flex min-w-0 items-center",
|
|
180
|
+
(lane.off || depth > 0) && "text-muted-foreground",
|
|
181
|
+
depth > 0 && "pl-3.5 text-sm"
|
|
182
|
+
),
|
|
183
|
+
children: name
|
|
184
|
+
}
|
|
185
|
+
),
|
|
186
|
+
/* @__PURE__ */ jsx(
|
|
187
|
+
HeatLane,
|
|
188
|
+
{
|
|
189
|
+
"aria-label": lane.label,
|
|
190
|
+
values: lane.off ? lane.cells.map(() => null) : lane.cells,
|
|
191
|
+
marks: lane.marks,
|
|
192
|
+
stale,
|
|
193
|
+
cellLabel: cell ? (i) => `${lane.label} \xB7 cell ${i + 1} \xB7 ${cell}` : void 0,
|
|
194
|
+
pinned: pin?.lane === lane.id ? pin.at : null,
|
|
195
|
+
onPin: stale ? void 0 : (at) => onPin(at == null ? null : { lane: lane.id, at })
|
|
196
|
+
}
|
|
197
|
+
),
|
|
198
|
+
/* @__PURE__ */ jsx(
|
|
199
|
+
"span",
|
|
200
|
+
{
|
|
201
|
+
className: cn(
|
|
202
|
+
"truncate text-right font-mono text-sm text-muted-foreground tabular-nums",
|
|
203
|
+
!stale && lane.rateTone && RATE_TONE[lane.rateTone]
|
|
204
|
+
),
|
|
205
|
+
children: stale ? "\u2014" : lane.rate
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
] });
|
|
209
|
+
}
|
|
210
|
+
function ActivityBlock({ spec, onAction }) {
|
|
211
|
+
const stale = spec.state === "stale";
|
|
212
|
+
const [opened, setOpened] = React18.useState(
|
|
213
|
+
() => new Set(spec.lanes.filter((l) => l.open).map((l) => l.id))
|
|
214
|
+
);
|
|
215
|
+
const [pin, setPin] = React18.useState(
|
|
216
|
+
spec.pinned ? { lane: spec.pinned.lane, at: spec.pinned.at } : null
|
|
217
|
+
);
|
|
218
|
+
const toggle = (id) => setOpened((s) => {
|
|
219
|
+
const next = new Set(s);
|
|
220
|
+
if (next.has(id)) next.delete(id);
|
|
221
|
+
else next.add(id);
|
|
222
|
+
return next;
|
|
223
|
+
});
|
|
224
|
+
const pinCell = (next) => {
|
|
225
|
+
setPin(next);
|
|
226
|
+
if (next) onAction?.("pin", next);
|
|
227
|
+
};
|
|
228
|
+
const record = spec.pinned && pin && spec.pinned.lane === pin.lane && spec.pinned.at === pin.at ? spec.pinned : null;
|
|
229
|
+
const total = spec.lanes.length;
|
|
230
|
+
const lighted = spec.lanes.filter(lit).length;
|
|
231
|
+
return /* @__PURE__ */ jsxs("div", { className: "@container/activity flex min-w-0 flex-col gap-2", children: [
|
|
232
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[minmax(0,7rem)_minmax(0,1fr)_minmax(2.5rem,auto)] items-center gap-x-2 gap-y-1.5 @max-[300px]/activity:grid-cols-[4rem_minmax(0,1fr)_minmax(1.5rem,auto)]", children: [
|
|
233
|
+
spec.bands?.length ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
234
|
+
/* @__PURE__ */ jsx("span", {}),
|
|
235
|
+
/* @__PURE__ */ jsx("div", { className: "flex min-w-0 gap-px text-xs", children: spec.bands.map((b, i) => /* @__PURE__ */ jsx(
|
|
236
|
+
"span",
|
|
237
|
+
{
|
|
238
|
+
style: { flex: b.span },
|
|
239
|
+
className: cn(
|
|
240
|
+
"min-w-0 truncate rounded-[2px] px-1",
|
|
241
|
+
b.current ? "bg-info/15 text-foreground" : "bg-muted text-muted-foreground"
|
|
242
|
+
),
|
|
243
|
+
children: b.label
|
|
244
|
+
},
|
|
245
|
+
i
|
|
246
|
+
)) }),
|
|
247
|
+
/* @__PURE__ */ jsx("span", { className: "text-right text-xs text-muted-foreground", children: spec.state === "live" ? "now" : "" })
|
|
248
|
+
] }) : null,
|
|
249
|
+
spec.lanes.map((lane) => {
|
|
250
|
+
const open = opened.has(lane.id);
|
|
251
|
+
const kids = lane.children?.length ? lane.children : null;
|
|
252
|
+
return /* @__PURE__ */ jsxs(React18.Fragment, { children: [
|
|
253
|
+
/* @__PURE__ */ jsx(
|
|
254
|
+
LaneRow,
|
|
255
|
+
{
|
|
256
|
+
lane,
|
|
257
|
+
depth: 0,
|
|
258
|
+
open,
|
|
259
|
+
onToggle: kids ? () => toggle(lane.id) : void 0,
|
|
260
|
+
stale,
|
|
261
|
+
cell: spec.cell,
|
|
262
|
+
pin,
|
|
263
|
+
onPin: pinCell
|
|
264
|
+
}
|
|
265
|
+
),
|
|
266
|
+
open && kids ? kids.map((child) => /* @__PURE__ */ jsx(
|
|
267
|
+
LaneRow,
|
|
268
|
+
{
|
|
269
|
+
lane: child,
|
|
270
|
+
depth: 1,
|
|
271
|
+
open: false,
|
|
272
|
+
stale,
|
|
273
|
+
cell: spec.cell,
|
|
274
|
+
pin,
|
|
275
|
+
onPin: pinCell
|
|
276
|
+
},
|
|
277
|
+
child.id
|
|
278
|
+
)) : null
|
|
279
|
+
] }, lane.id);
|
|
280
|
+
}),
|
|
281
|
+
spec.axis?.length ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
282
|
+
/* @__PURE__ */ jsx("span", {}),
|
|
283
|
+
/* @__PURE__ */ jsx("div", { className: "flex min-w-0 justify-between text-xs text-muted-foreground tabular-nums", children: spec.axis.map((a, i) => /* @__PURE__ */ jsx("span", { children: a }, i)) }),
|
|
284
|
+
/* @__PURE__ */ jsx("span", {})
|
|
285
|
+
] }) : null
|
|
286
|
+
] }),
|
|
287
|
+
record ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5 rounded-control border border-border bg-muted/40 p-2", children: [
|
|
288
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-baseline gap-2", children: [
|
|
289
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate font-medium", children: record.title }),
|
|
290
|
+
record.time ? /* @__PURE__ */ jsx("span", { className: "shrink-0 font-mono text-sm text-muted-foreground", children: record.time }) : null
|
|
291
|
+
] }),
|
|
292
|
+
record.query ? /* @__PURE__ */ jsx("code", { className: "truncate font-mono text-sm", children: record.query }) : null,
|
|
293
|
+
record.rows?.length ? /* @__PURE__ */ jsx(PropertyList, { children: record.rows.map((r) => /* @__PURE__ */ jsx(PropertyRow, { label: r.label, children: r.value }, r.label)) }) : null,
|
|
294
|
+
record.note || record.action ? /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2 text-sm text-muted-foreground", children: [
|
|
295
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: record.note }),
|
|
296
|
+
record.action ? /* @__PURE__ */ jsx(
|
|
297
|
+
Button,
|
|
298
|
+
{
|
|
299
|
+
variant: "link",
|
|
300
|
+
size: "xs",
|
|
301
|
+
className: "h-auto p-0",
|
|
302
|
+
onClick: () => onAction?.("action", record.action.id),
|
|
303
|
+
children: record.action.label
|
|
304
|
+
}
|
|
305
|
+
) : null
|
|
306
|
+
] }) : null
|
|
307
|
+
] }) : null,
|
|
308
|
+
spec.legend ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-3 text-sm text-muted-foreground", children: [
|
|
309
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
310
|
+
/* @__PURE__ */ jsx("i", { "aria-hidden": true, className: "size-2 rounded-[1px] bg-info" }),
|
|
311
|
+
"touched"
|
|
312
|
+
] }),
|
|
313
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
314
|
+
/* @__PURE__ */ jsx("i", { "aria-hidden": true, className: "size-2 rounded-[1px] bg-destructive" }),
|
|
315
|
+
"refused"
|
|
316
|
+
] }),
|
|
317
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
318
|
+
/* @__PURE__ */ jsx("i", { "aria-hidden": true, className: "size-2 rounded-[1px] bg-warning" }),
|
|
319
|
+
"left the boundary"
|
|
320
|
+
] })
|
|
321
|
+
] }) : null,
|
|
322
|
+
spec.hint ? /* @__PURE__ */ jsx("p", { className: cn("text-sm text-muted-foreground", stale && "text-warning"), children: strong(spec.hint) }) : null,
|
|
323
|
+
spec.actions?.length ? /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2 border-t border-border pt-1.5 text-sm text-muted-foreground", children: [
|
|
324
|
+
/* @__PURE__ */ jsxs("span", { className: "shrink-0 tabular-nums", children: [
|
|
325
|
+
total,
|
|
326
|
+
" layers \xB7 ",
|
|
327
|
+
lighted,
|
|
328
|
+
" lit"
|
|
329
|
+
] }),
|
|
330
|
+
/* @__PURE__ */ jsx(ActionRow, { actions: spec.actions, onAction: (id) => onAction?.("action", id) })
|
|
331
|
+
] }) : null
|
|
332
|
+
] });
|
|
333
|
+
}
|
|
334
|
+
var decimals = (v) => (String(v).split(".")[1] ?? "").length;
|
|
335
|
+
var MUTED = "color-mix(in srgb, var(--color-muted-foreground) 45%, transparent)";
|
|
336
|
+
function BarsBlock({ spec }) {
|
|
337
|
+
const single = spec.series.length === 1;
|
|
338
|
+
const places = Math.max(0, ...spec.series.flatMap((s) => s.values.map(decimals)));
|
|
339
|
+
const write = (v) => v.toLocaleString("en-GB", { minimumFractionDigits: places, maximumFractionDigits: places }) + (spec.unit === "%" ? "%" : "");
|
|
340
|
+
const highlight = spec.highlight == null ? -1 : spec.groups.indexOf(spec.highlight);
|
|
341
|
+
const called = single && highlight >= 0;
|
|
342
|
+
return /* @__PURE__ */ jsx(
|
|
343
|
+
BarChartV,
|
|
344
|
+
{
|
|
345
|
+
"aria-label": spec.series.map((s) => s.name).join(", ") + (spec.unit ? `, ${spec.unit}` : ""),
|
|
346
|
+
variant: "comparison",
|
|
347
|
+
height: 80,
|
|
348
|
+
labelMode: called ? "last" : "none",
|
|
349
|
+
color: called ? "var(--color-muted-foreground)" : "var(--color-primary)",
|
|
350
|
+
highlightColor: "var(--color-primary)",
|
|
351
|
+
highlightIndex: called ? highlight : null,
|
|
352
|
+
target: spec.target ? { ...spec.target, color: "var(--color-warning)" } : void 0,
|
|
353
|
+
planLabel: spec.plan?.name,
|
|
354
|
+
series: single && !spec.plan ? void 0 : spec.series.map((s) => ({
|
|
355
|
+
name: s.name,
|
|
356
|
+
color: s.muted ? MUTED : "var(--color-primary)"
|
|
357
|
+
})),
|
|
358
|
+
data: spec.groups.map(
|
|
359
|
+
(label, i) => single ? {
|
|
360
|
+
label,
|
|
361
|
+
value: spec.series[0].values[i] ?? 0,
|
|
362
|
+
display: write(spec.series[0].values[i] ?? 0),
|
|
363
|
+
plan: spec.plan?.values[i]
|
|
364
|
+
} : {
|
|
365
|
+
label,
|
|
366
|
+
values: spec.series.map((s) => s.values[i] ?? 0),
|
|
367
|
+
display: spec.series.map((s) => write(s.values[i] ?? 0))
|
|
368
|
+
}
|
|
369
|
+
)
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
function Chips({
|
|
374
|
+
items,
|
|
375
|
+
layout,
|
|
376
|
+
sent,
|
|
377
|
+
onSelect
|
|
378
|
+
}) {
|
|
379
|
+
const stack = layout === "stack";
|
|
380
|
+
return /* @__PURE__ */ jsx(
|
|
381
|
+
"div",
|
|
382
|
+
{
|
|
383
|
+
className: cn(
|
|
384
|
+
"flex gap-1.5",
|
|
385
|
+
stack ? "flex-col" : "flex-wrap @max-[22rem]:flex-col"
|
|
386
|
+
),
|
|
387
|
+
children: items.map((text) => {
|
|
388
|
+
const done = sent?.includes(text);
|
|
389
|
+
return /* @__PURE__ */ jsxs(
|
|
390
|
+
Button,
|
|
391
|
+
{
|
|
392
|
+
type: "button",
|
|
393
|
+
size: "xs",
|
|
394
|
+
variant: "outline",
|
|
395
|
+
disabled: done,
|
|
396
|
+
className: cn(
|
|
397
|
+
// A follow-up is a whole prompt and can outrun the line: it wraps
|
|
398
|
+
// within the thread rather than running past it.
|
|
399
|
+
"h-auto min-h-control-xs max-w-full items-start whitespace-normal py-0.5 text-left font-normal",
|
|
400
|
+
stack ? "w-full justify-start" : "rounded-full @max-[22rem]:w-full @max-[22rem]:justify-start @max-[22rem]:rounded-control"
|
|
401
|
+
),
|
|
402
|
+
onClick: () => onSelect?.(text),
|
|
403
|
+
children: [
|
|
404
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-muted-foreground", children: "\u21B3" }),
|
|
405
|
+
text
|
|
406
|
+
]
|
|
407
|
+
},
|
|
408
|
+
text
|
|
409
|
+
);
|
|
410
|
+
})
|
|
411
|
+
}
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
var SuggestionChips = React18.forwardRef(
|
|
415
|
+
({ items, groups, layout, sent, lead, onSelect, className, ...props }, ref) => /* @__PURE__ */ jsxs("div", { ref, className: cn("@container flex flex-col gap-1.5", className), ...props, children: [
|
|
416
|
+
lead != null ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: lead }) : null,
|
|
417
|
+
groups?.length ? groups.map((group, i) => /* @__PURE__ */ jsxs(React18.Fragment, { children: [
|
|
418
|
+
/* @__PURE__ */ jsx(Eyebrow, { children: group.label }),
|
|
419
|
+
/* @__PURE__ */ jsx(Chips, { items: group.items, layout, sent, onSelect })
|
|
420
|
+
] }, i)) : items?.length ? /* @__PURE__ */ jsx(Chips, { items, layout, sent, onSelect }) : null
|
|
421
|
+
] })
|
|
422
|
+
);
|
|
423
|
+
SuggestionChips.displayName = "SuggestionChips";
|
|
424
|
+
function CannotBlock({ spec, onAction }) {
|
|
425
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
426
|
+
/* @__PURE__ */ jsx(
|
|
427
|
+
CannotAnswerCard,
|
|
428
|
+
{
|
|
429
|
+
remedy: spec.remedy,
|
|
430
|
+
label: spec.partial ? "answered in part" : void 0,
|
|
431
|
+
tone: spec.partial ? "info" : "default",
|
|
432
|
+
children: spec.reason
|
|
433
|
+
}
|
|
434
|
+
),
|
|
435
|
+
spec.nearest?.length ? /* @__PURE__ */ jsx(
|
|
436
|
+
SuggestionChips,
|
|
437
|
+
{
|
|
438
|
+
lead: "I can answer instead",
|
|
439
|
+
items: spec.nearest,
|
|
440
|
+
onSelect: (text) => onAction?.("prompt", text)
|
|
441
|
+
}
|
|
442
|
+
) : null
|
|
443
|
+
] });
|
|
444
|
+
}
|
|
445
|
+
var TONE = {
|
|
446
|
+
warning: "warning",
|
|
447
|
+
info: "info",
|
|
448
|
+
bad: "destructive"
|
|
449
|
+
};
|
|
450
|
+
function Note({
|
|
451
|
+
note,
|
|
452
|
+
onAction
|
|
453
|
+
}) {
|
|
454
|
+
return /* @__PURE__ */ jsx(
|
|
455
|
+
CaveatNote,
|
|
456
|
+
{
|
|
457
|
+
label: note.label,
|
|
458
|
+
tone: TONE[note.tone ?? "warning"],
|
|
459
|
+
action: note.action?.label,
|
|
460
|
+
onAction: note.action ? () => onAction(note.action.id) : void 0,
|
|
461
|
+
children: note.text
|
|
462
|
+
}
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
function CaveatBlock({ spec, onAction }) {
|
|
466
|
+
const [open, setOpen] = React18.useState(!spec.folded);
|
|
467
|
+
const act = (id) => onAction?.("action", id);
|
|
468
|
+
if (!spec.items) return /* @__PURE__ */ jsx(Note, { note: spec, onAction: act });
|
|
469
|
+
const notes = spec.items.map((note, i) => /* @__PURE__ */ jsx(Note, { note, onAction: act }, i));
|
|
470
|
+
if (!spec.folded) return /* @__PURE__ */ jsx(Fragment, { children: notes });
|
|
471
|
+
return /* @__PURE__ */ jsx(
|
|
472
|
+
ChatSessionDisclosure,
|
|
473
|
+
{
|
|
474
|
+
variant: "inline",
|
|
475
|
+
label: `${spec.items.length} caveats`,
|
|
476
|
+
meta: open ? void 0 : spec.items.map((n) => n.label).join(" \xB7 "),
|
|
477
|
+
open,
|
|
478
|
+
onOpenChange: setOpen,
|
|
479
|
+
children: /* @__PURE__ */ jsx(Fragment, { children: notes })
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// src/format.ts
|
|
485
|
+
function figureText(figure2) {
|
|
486
|
+
if (typeof figure2 === "string") return figure2;
|
|
487
|
+
if (typeof figure2 === "number") return figure2.toLocaleString("en-GB");
|
|
488
|
+
const n = figure2.value.toLocaleString("en-GB");
|
|
489
|
+
return figure2.unit ? `${n} ${figure2.unit}` : n;
|
|
490
|
+
}
|
|
491
|
+
function metricValue(value) {
|
|
492
|
+
return value == null ? "\u2014" : figureText(value);
|
|
493
|
+
}
|
|
494
|
+
var CAPTION_TONE = {
|
|
495
|
+
good: "success",
|
|
496
|
+
bad: "error",
|
|
497
|
+
warn: "warning",
|
|
498
|
+
neutral: void 0
|
|
499
|
+
};
|
|
500
|
+
var BADGE_TONE = {
|
|
501
|
+
good: "success",
|
|
502
|
+
bad: "destructive",
|
|
503
|
+
warn: "warning",
|
|
504
|
+
neutral: "muted"
|
|
505
|
+
};
|
|
506
|
+
function gaugeProps(gauge) {
|
|
507
|
+
if (!gauge) return {};
|
|
508
|
+
const min = gauge.min ?? 0;
|
|
509
|
+
const span = gauge.max - min || 1;
|
|
510
|
+
const fill = (gauge.value - min) / span;
|
|
511
|
+
if (gauge.target == null) return { meter: fill };
|
|
512
|
+
const write = (n) => `${n.toLocaleString("en-GB")}${gauge.unit ?? ""}`;
|
|
513
|
+
return {
|
|
514
|
+
gauge: {
|
|
515
|
+
fill,
|
|
516
|
+
mark: (gauge.target - min) / span,
|
|
517
|
+
labels: [write(min), `target ${write(gauge.target)}`, write(gauge.max)]
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
function CitationsBlock({ spec, active = spec.active }) {
|
|
522
|
+
const [open, setOpen] = React18.useState(!spec.folded);
|
|
523
|
+
const counts = spec.sources.map((s) => s.count);
|
|
524
|
+
const total = counts.every((c) => typeof c === "number") ? counts.reduce((a, b) => a + b, 0) : void 0;
|
|
525
|
+
const list = /* @__PURE__ */ jsx(CitationList, { note: spec.note, noteTone: total === 0 ? "warning" : "muted", children: spec.sources.map((source, i) => /* @__PURE__ */ jsx(
|
|
526
|
+
CitationRow,
|
|
527
|
+
{
|
|
528
|
+
marker: i + 1,
|
|
529
|
+
active: active === i + 1,
|
|
530
|
+
detail: source.detail,
|
|
531
|
+
count: source.count == null ? void 0 : figureText(source.count),
|
|
532
|
+
children: source.label
|
|
533
|
+
},
|
|
534
|
+
i
|
|
535
|
+
)) });
|
|
536
|
+
if (!spec.folded) return list;
|
|
537
|
+
return /* @__PURE__ */ jsx(
|
|
538
|
+
ChatSessionDisclosure,
|
|
539
|
+
{
|
|
540
|
+
variant: "inline",
|
|
541
|
+
label: `${spec.sources.length} sources`,
|
|
542
|
+
meta: total != null ? `${total.toLocaleString("en-GB")} records` : void 0,
|
|
543
|
+
open,
|
|
544
|
+
onOpenChange: setOpen,
|
|
545
|
+
children: list
|
|
546
|
+
}
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
var figure = (value, tone2) => typeof value === "string" || typeof value === "number" ? /* @__PURE__ */ jsx("b", { className: cn("font-mono font-normal text-foreground", tone2 === "warning" && "text-warning"), children: value }) : value;
|
|
550
|
+
var ConfirmCard = React18.forwardRef(
|
|
551
|
+
({ question, description, heading, cost, costAs = "line", seamless, caveat, hint, className, children, ...props }, ref) => /* @__PURE__ */ jsxs("div", { ref, className: cn("flex flex-col gap-2", className), ...props, children: [
|
|
552
|
+
/* @__PURE__ */ jsx("p", { className: heading || description ? "font-semibold" : void 0, children: question }),
|
|
553
|
+
description ? /* @__PURE__ */ jsx("p", { className: "-mt-1.5 text-sm text-muted-foreground", children: description }) : null,
|
|
554
|
+
cost?.length && costAs === "strip" ? /* @__PURE__ */ jsx(
|
|
555
|
+
"dl",
|
|
556
|
+
{
|
|
557
|
+
className: cn(
|
|
558
|
+
"grid",
|
|
559
|
+
// Seamless: it reaches out by a cell's padding and clips that off,
|
|
560
|
+
// so the outer cells are flush.
|
|
561
|
+
seamless ? "-mx-2 -my-1 [clip-path:inset(0.25rem_0.5rem)]" : "overflow-hidden rounded-md border border-border"
|
|
562
|
+
),
|
|
563
|
+
style: { gridTemplateColumns: `repeat(${cost.length}, minmax(0, 1fr))` },
|
|
564
|
+
children: cost.map((c, i) => /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col border-e border-border/60 px-2 py-1 last:border-e-0", children: [
|
|
565
|
+
/* @__PURE__ */ jsx("dt", { className: "truncate text-xs text-muted-foreground", children: c.label }),
|
|
566
|
+
/* @__PURE__ */ jsx("dd", { className: cn("font-mono font-medium tabular-nums", c.tone === "warning" && "text-warning"), children: c.value })
|
|
567
|
+
] }, i))
|
|
568
|
+
}
|
|
569
|
+
) : cost?.length ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: cost.map((c, i) => /* @__PURE__ */ jsxs(React18.Fragment, { children: [
|
|
570
|
+
i > 0 ? " \xB7 " : null,
|
|
571
|
+
figure(c.value, c.tone),
|
|
572
|
+
" ",
|
|
573
|
+
c.label
|
|
574
|
+
] }, i)) }) : null,
|
|
575
|
+
caveat,
|
|
576
|
+
/* @__PURE__ */ jsx(ClarifyActions, { children }),
|
|
577
|
+
hint ? /* @__PURE__ */ jsx(ClarifyFootnote, { children: hint }) : null
|
|
578
|
+
] })
|
|
579
|
+
);
|
|
580
|
+
ConfirmCard.displayName = "ConfirmCard";
|
|
581
|
+
var isHeading = (text) => Boolean(text.heading || text.description);
|
|
582
|
+
function AskQuestion({ text, aside }) {
|
|
583
|
+
const question = /* @__PURE__ */ jsx("p", { className: isHeading(text) ? "font-semibold" : void 0, children: strong(text.question) });
|
|
584
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
585
|
+
aside ? /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-2", children: [
|
|
586
|
+
question,
|
|
587
|
+
aside
|
|
588
|
+
] }) : question,
|
|
589
|
+
text.description ? /* @__PURE__ */ jsx("p", { className: "-mt-1.5 text-sm text-muted-foreground", children: text.description }) : null
|
|
590
|
+
] });
|
|
591
|
+
}
|
|
592
|
+
function hintText(text) {
|
|
593
|
+
return text.split(/(\*\*[^*]+\*\*|`[^`]+`)/).filter(Boolean).map((part, i) => {
|
|
594
|
+
if (part.startsWith("**") && part.endsWith("**"))
|
|
595
|
+
return /* @__PURE__ */ jsx("b", { className: "font-mono font-normal text-foreground", children: part.slice(2, -2) }, i);
|
|
596
|
+
if (part.startsWith("`") && part.endsWith("`")) return /* @__PURE__ */ jsx(Kbd, { children: part.slice(1, -1) }, i);
|
|
597
|
+
return part;
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
function AskLink({ children, onClick }) {
|
|
601
|
+
return /* @__PURE__ */ jsx(
|
|
602
|
+
"button",
|
|
603
|
+
{
|
|
604
|
+
type: "button",
|
|
605
|
+
onClick,
|
|
606
|
+
className: "shrink-0 whitespace-nowrap font-sans text-xs font-normal text-primary hover:underline focus-visible:underline focus-visible:outline-none",
|
|
607
|
+
children
|
|
608
|
+
}
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
function AskHint({ children, tone: tone2 }) {
|
|
612
|
+
if (!children) return null;
|
|
613
|
+
return /* @__PURE__ */ jsx(ClarifyFootnote, { className: tone2 === "warning" ? "text-warning" : tone2 === "error" ? "text-destructive" : void 0, children: hintText(children) });
|
|
614
|
+
}
|
|
615
|
+
var FIGURE_TONE = {
|
|
616
|
+
good: "success",
|
|
617
|
+
bad: "error",
|
|
618
|
+
warn: "warning",
|
|
619
|
+
neutral: void 0
|
|
620
|
+
};
|
|
621
|
+
function Icon({ path }) {
|
|
622
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: 1.3, strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx("path", { d: path }) });
|
|
623
|
+
}
|
|
624
|
+
function choiceParts(o) {
|
|
625
|
+
return {
|
|
626
|
+
props: {
|
|
627
|
+
detail: o.figure ? void 0 : o.note ?? o.detail,
|
|
628
|
+
lead: o.icon ? /* @__PURE__ */ jsx(Icon, { path: o.icon }) : o.lead,
|
|
629
|
+
figure: o.figure && { value: o.figure.value, unit: o.figure.unit, tone: o.figure.tone && FIGURE_TONE[o.figure.tone] }
|
|
630
|
+
},
|
|
631
|
+
body: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
632
|
+
/* @__PURE__ */ jsx(QuestionnaireChoiceTitle, { children: o.label }),
|
|
633
|
+
o.description ? /* @__PURE__ */ jsx(QuestionnaireChoiceDescription, { children: o.description }) : null
|
|
634
|
+
] })
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
function pickText(o, value) {
|
|
638
|
+
if (!o) return value == null || value === "" ? "\u2014" : String(value);
|
|
639
|
+
if (o.summary) return o.summary;
|
|
640
|
+
return o.detail ? `${o.label} \xB7 ${o.detail}` : o.label;
|
|
641
|
+
}
|
|
642
|
+
function joinPicks(picked) {
|
|
643
|
+
if (!picked.length) return "\u2014";
|
|
644
|
+
const summarised = picked.some((o) => o.summary);
|
|
645
|
+
return picked.map((o) => o.summary ?? o.label).join(summarised ? " \xB7 " : ", ");
|
|
646
|
+
}
|
|
647
|
+
function AskSummary({
|
|
648
|
+
rows,
|
|
649
|
+
change,
|
|
650
|
+
onChange,
|
|
651
|
+
children
|
|
652
|
+
}) {
|
|
653
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
654
|
+
/* @__PURE__ */ jsx(PropertyList, { labelWidth: "auto", variant: "summary", children: rows.map((r) => /* @__PURE__ */ jsx(PropertyRow, { label: r.label, mono: true, children: r.value }, r.label)) }),
|
|
655
|
+
children,
|
|
656
|
+
change ? /* @__PURE__ */ jsx(ClarifyActions, { children: /* @__PURE__ */ jsx(Button, { size: "xs", variant: "ghost", onClick: onChange, children: change }) }) : null
|
|
657
|
+
] });
|
|
658
|
+
}
|
|
659
|
+
function costOf(cost) {
|
|
660
|
+
return cost?.map((c) => ({
|
|
661
|
+
label: c.label,
|
|
662
|
+
value: c.value.includes("**") ? hintText(c.value) : c.value,
|
|
663
|
+
tone: c.tone === "warn" || c.tone === "bad" ? "warning" : void 0
|
|
664
|
+
}));
|
|
665
|
+
}
|
|
666
|
+
function ConfirmAsk({ spec, state = "pending", value: given, onAction }) {
|
|
667
|
+
const pending = state === "pending";
|
|
668
|
+
if (!pending) {
|
|
669
|
+
const yes = given === void 0 ? spec.default ?? true : given === true;
|
|
670
|
+
const words = yes ? spec.settled?.yes ?? spec.yes : spec.settled?.no ?? spec.no;
|
|
671
|
+
return /* @__PURE__ */ jsx(AskSummary, { rows: [{ label: spec.label ?? "Decision", value: words }], children: /* @__PURE__ */ jsx(AskHint, { children: spec.hint }) });
|
|
672
|
+
}
|
|
673
|
+
const chosen = spec.default ?? true;
|
|
674
|
+
const end = spec.align === "end";
|
|
675
|
+
const button = (value) => /* @__PURE__ */ jsx(
|
|
676
|
+
Button,
|
|
677
|
+
{
|
|
678
|
+
size: "xs",
|
|
679
|
+
variant: value === chosen ? "default" : value === false && spec.dismiss ? "ghost" : "outline",
|
|
680
|
+
className: end && value === chosen ? "ms-auto" : void 0,
|
|
681
|
+
onClick: () => onAction?.("reply", value),
|
|
682
|
+
children: value ? spec.yes : spec.no
|
|
683
|
+
},
|
|
684
|
+
String(value)
|
|
685
|
+
);
|
|
686
|
+
const order = end ? [!chosen, chosen] : [chosen, !chosen];
|
|
687
|
+
return /* @__PURE__ */ jsx(
|
|
688
|
+
ConfirmCard,
|
|
689
|
+
{
|
|
690
|
+
question: strong(spec.question),
|
|
691
|
+
description: spec.description,
|
|
692
|
+
heading: spec.heading,
|
|
693
|
+
cost: costOf(spec.cost),
|
|
694
|
+
costAs: spec.costAs,
|
|
695
|
+
seamless: true,
|
|
696
|
+
caveat: spec.caveat ? /* @__PURE__ */ jsx(CaveatNote, { label: spec.caveat.label, children: spec.caveat.text }) : void 0,
|
|
697
|
+
hint: spec.hint ? hintText(spec.hint) : void 0,
|
|
698
|
+
children: order.map(button)
|
|
699
|
+
}
|
|
700
|
+
);
|
|
701
|
+
}
|
|
702
|
+
function FileGlyph() {
|
|
703
|
+
return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "1.3", "aria-hidden": true, children: [
|
|
704
|
+
/* @__PURE__ */ jsx("path", { d: "M4 1.5h5.5L13 5v9.5H4z" }),
|
|
705
|
+
/* @__PURE__ */ jsx("path", { d: "M9.5 1.5V5H13" })
|
|
706
|
+
] });
|
|
707
|
+
}
|
|
708
|
+
var noteOf = (file) => file.note ?? file.size;
|
|
709
|
+
function FilesBlock({ spec, onAction }) {
|
|
710
|
+
const download = (file) => onAction?.("download", file.digest ?? file.name);
|
|
711
|
+
if (spec.files.length === 1) {
|
|
712
|
+
const file = spec.files[0];
|
|
713
|
+
return /* @__PURE__ */ jsx(
|
|
714
|
+
ArtifactCard,
|
|
715
|
+
{
|
|
716
|
+
file: { name: file.name, size: file.size, digest: file.digest },
|
|
717
|
+
note: noteOf(file),
|
|
718
|
+
aside: file.status ? /* @__PURE__ */ jsx(Badge, { variant: "soft", size: "xs", tone: BADGE_TONE[file.status.tone ?? "neutral"], className: "font-normal", children: file.status.label }) : spec.download ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "xs", onClick: () => download(file), children: "Download" }) : void 0
|
|
719
|
+
}
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
return /* @__PURE__ */ jsx(
|
|
723
|
+
ArtifactTable,
|
|
724
|
+
{
|
|
725
|
+
variant: "list",
|
|
726
|
+
files: spec.files.map((file) => ({
|
|
727
|
+
name: file.name,
|
|
728
|
+
size: file.size,
|
|
729
|
+
digest: spec.download ? void 0 : file.digest,
|
|
730
|
+
icon: spec.download ? /* @__PURE__ */ jsx(FileGlyph, {}) : void 0
|
|
731
|
+
})),
|
|
732
|
+
onDownload: spec.download ? (_, i) => download(spec.files[i]) : void 0
|
|
733
|
+
}
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
var FIELD_TYPE = {
|
|
737
|
+
number: "number",
|
|
738
|
+
text: "text",
|
|
739
|
+
date: "text",
|
|
740
|
+
select: "select"
|
|
741
|
+
};
|
|
742
|
+
function boundsRule(f) {
|
|
743
|
+
if (f.above == null && f.below == null) return void 0;
|
|
744
|
+
return {
|
|
745
|
+
validate: (v) => {
|
|
746
|
+
const n = Number(v);
|
|
747
|
+
if (f.above != null && !(n > f.above)) return `Must be above ${f.above}`;
|
|
748
|
+
if (f.below != null && !(n < f.below)) return `Must be below ${f.below}`;
|
|
749
|
+
return true;
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
var toField = (f) => ({
|
|
754
|
+
name: f.name,
|
|
755
|
+
type: FIELD_TYPE[f.type],
|
|
756
|
+
label: f.label,
|
|
757
|
+
unit: f.unit,
|
|
758
|
+
aside: f.aside,
|
|
759
|
+
description: f.hint,
|
|
760
|
+
group: f.group,
|
|
761
|
+
rules: boundsRule(f),
|
|
762
|
+
placeholder: ""
|
|
763
|
+
});
|
|
764
|
+
function withUnit(value, unit) {
|
|
765
|
+
if (value == null || value === "") return "\u2014";
|
|
766
|
+
if (!unit) return String(value);
|
|
767
|
+
return /^[A-Za-z]/.test(unit) ? `${value} ${unit}` : `${value}${unit}`;
|
|
768
|
+
}
|
|
769
|
+
function FormAsk({ spec, state = "pending", value: given, onAction }) {
|
|
770
|
+
const auto = React18.useId();
|
|
771
|
+
const pending = state === "pending";
|
|
772
|
+
const [editing, setEditing] = React18.useState(false);
|
|
773
|
+
const current = given ?? {};
|
|
774
|
+
const top = spec.labels === "top";
|
|
775
|
+
const defaults = Object.fromEntries(
|
|
776
|
+
spec.fields.map((f) => [f.name, pending ? f.default : current[f.name]])
|
|
777
|
+
);
|
|
778
|
+
const form = useForm({ defaultValues: { values: defaults }, mode: "onChange" });
|
|
779
|
+
if (!pending && !editing) {
|
|
780
|
+
return /* @__PURE__ */ jsx(
|
|
781
|
+
AskSummary,
|
|
782
|
+
{
|
|
783
|
+
rows: spec.fields.map((f) => ({ label: f.label, value: withUnit(current[f.name], f.unit) })),
|
|
784
|
+
change: state === "answered" ? "Change inputs" : void 0,
|
|
785
|
+
onChange: () => setEditing(true)
|
|
786
|
+
}
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
return /* @__PURE__ */ jsxs(Form, { ...form, children: [
|
|
790
|
+
/* @__PURE__ */ jsx(AskQuestion, { text: spec }),
|
|
791
|
+
/* @__PURE__ */ jsx(
|
|
792
|
+
"form",
|
|
793
|
+
{
|
|
794
|
+
id: `${auto}-form`,
|
|
795
|
+
onSubmit: form.handleSubmit(({ values }) => {
|
|
796
|
+
onAction?.(editing ? "change" : "reply", values);
|
|
797
|
+
setEditing(false);
|
|
798
|
+
}),
|
|
799
|
+
children: /* @__PURE__ */ jsx(
|
|
800
|
+
ObjectField,
|
|
801
|
+
{
|
|
802
|
+
control: form.control,
|
|
803
|
+
name: "values",
|
|
804
|
+
fields: spec.fields.map(toField),
|
|
805
|
+
labelPosition: top ? "top" : "side",
|
|
806
|
+
size: "xs",
|
|
807
|
+
columns: top ? 2 : 1,
|
|
808
|
+
fit: "container",
|
|
809
|
+
groupAs: "section"
|
|
810
|
+
}
|
|
811
|
+
)
|
|
812
|
+
}
|
|
813
|
+
),
|
|
814
|
+
/* @__PURE__ */ jsx(AskHint, { children: spec.hint }),
|
|
815
|
+
/* @__PURE__ */ jsx(ClarifyActions, { children: /* @__PURE__ */ jsx(
|
|
816
|
+
Button,
|
|
817
|
+
{
|
|
818
|
+
type: "submit",
|
|
819
|
+
form: `${auto}-form`,
|
|
820
|
+
size: "xs",
|
|
821
|
+
className: top ? "ms-auto" : void 0,
|
|
822
|
+
disabled: !form.formState.isValid,
|
|
823
|
+
children: spec.submit ?? "Submit"
|
|
824
|
+
}
|
|
825
|
+
) })
|
|
826
|
+
] });
|
|
827
|
+
}
|
|
828
|
+
var openKeys = (tasks) => Object.fromEntries(
|
|
829
|
+
tasks.flatMap((t) => [...t.open ? [[t.key, true]] : [], ...Object.entries(openKeys(t.subtasks ?? []))])
|
|
830
|
+
);
|
|
831
|
+
var allKeys = (tasks) => tasks.flatMap((t) => [t.key, ...allKeys(t.subtasks ?? [])]);
|
|
832
|
+
var toBar = ({ chip, ...bar }) => ({
|
|
833
|
+
...bar,
|
|
834
|
+
chip: chip ? /* @__PURE__ */ jsx(Badge, { variant: "soft", tone: BADGE_TONE[chip.tone ?? "neutral"], children: chip.label }) : void 0
|
|
835
|
+
});
|
|
836
|
+
var STEP_TICK = (n) => `step ${Math.round(n)}`;
|
|
837
|
+
var STEPS = (n) => Math.round(n) === 1 ? "1 step" : `${Math.round(n)} steps`;
|
|
838
|
+
var toRow = ({ open: _open, subtasks, segments, ...task }) => ({
|
|
839
|
+
...task,
|
|
840
|
+
bars: segments?.map(toBar),
|
|
841
|
+
rows: subtasks?.map(toRow)
|
|
842
|
+
});
|
|
843
|
+
function GanttBlock({ spec, onAction }) {
|
|
844
|
+
const [selected, setSelected] = React18.useState(spec.selected ?? null);
|
|
845
|
+
const [expanded, setExpanded] = React18.useState(() => openKeys(spec.tasks));
|
|
846
|
+
const keys = allKeys(spec.tasks);
|
|
847
|
+
const flagged = openKeys(spec.tasks);
|
|
848
|
+
const openedAmong = (among) => among.filter((key) => flagged[key]).sort().join("\n");
|
|
849
|
+
const [seen, setSeen] = React18.useState(() => new Set(keys));
|
|
850
|
+
const [lastOpened, setLastOpened] = React18.useState(() => openedAmong(keys));
|
|
851
|
+
const fresh = keys.filter((key) => !seen.has(key));
|
|
852
|
+
const reflagged = openedAmong(keys.filter((key) => seen.has(key))) !== lastOpened;
|
|
853
|
+
if (fresh.length || reflagged) {
|
|
854
|
+
setSeen(new Set(keys));
|
|
855
|
+
setLastOpened(openedAmong(keys));
|
|
856
|
+
if (reflagged) setExpanded(flagged);
|
|
857
|
+
else {
|
|
858
|
+
const arrived = fresh.filter((key) => flagged[key]);
|
|
859
|
+
if (arrived.length)
|
|
860
|
+
setExpanded((e) => e === true ? e : { ...e, ...Object.fromEntries(arrived.map((k) => [k, true])) });
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
const rows = React18.useMemo(() => spec.tasks.map(toRow), [spec.tasks]);
|
|
864
|
+
const pick = (key) => {
|
|
865
|
+
setSelected(key);
|
|
866
|
+
onAction?.("select", key);
|
|
867
|
+
};
|
|
868
|
+
return /* @__PURE__ */ jsx(
|
|
869
|
+
Gantt,
|
|
870
|
+
{
|
|
871
|
+
rows,
|
|
872
|
+
spanMs: spec.spanMs,
|
|
873
|
+
nowMs: spec.nowMs,
|
|
874
|
+
openEnded: spec.openEnded,
|
|
875
|
+
ticks: spec.ticks,
|
|
876
|
+
formatTick: spec.scale === "seq" ? STEP_TICK : void 0,
|
|
877
|
+
formatDuration: spec.scale === "seq" ? STEPS : void 0,
|
|
878
|
+
labelWidth: spec.labelWidth,
|
|
879
|
+
durationWidth: spec.durationWidth,
|
|
880
|
+
palette: spec.palette,
|
|
881
|
+
seams: spec.seams,
|
|
882
|
+
density: spec.density,
|
|
883
|
+
expanded,
|
|
884
|
+
onExpandedChange: setExpanded,
|
|
885
|
+
selectedKey: selected,
|
|
886
|
+
onSelectRow: onAction ? pick : void 0,
|
|
887
|
+
onSelectBar: onAction ? pick : void 0
|
|
888
|
+
}
|
|
889
|
+
);
|
|
890
|
+
}
|
|
891
|
+
var columnsFor = (n) => n === 4 ? 2 : Math.min(n, 3);
|
|
892
|
+
function GridBlock({ spec }) {
|
|
893
|
+
return /* @__PURE__ */ jsx(
|
|
894
|
+
MetricGrid,
|
|
895
|
+
{
|
|
896
|
+
joined: true,
|
|
897
|
+
seamless: true,
|
|
898
|
+
minTileWidth: spec.minTileWidth,
|
|
899
|
+
columns: spec.minTileWidth ? void 0 : columnsFor(spec.tiles.length),
|
|
900
|
+
children: spec.tiles.map((tile) => /* @__PURE__ */ jsx(
|
|
901
|
+
MetricTile,
|
|
902
|
+
{
|
|
903
|
+
variant: "figure",
|
|
904
|
+
label: tile.label,
|
|
905
|
+
value: metricValue(tile.value),
|
|
906
|
+
tone: tile.value == null ? "muted" : void 0,
|
|
907
|
+
caption: tile.delta,
|
|
908
|
+
captionTone: tile.tone ? CAPTION_TONE[tile.tone] : void 0,
|
|
909
|
+
flagged: tile.flag,
|
|
910
|
+
...gaugeProps(tile.gauge)
|
|
911
|
+
},
|
|
912
|
+
tile.label
|
|
913
|
+
))
|
|
914
|
+
}
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
var COLOR = {
|
|
918
|
+
good: "var(--color-success)",
|
|
919
|
+
bad: "var(--color-destructive)",
|
|
920
|
+
warn: "var(--color-warning)",
|
|
921
|
+
neutral: "var(--color-muted-foreground)"
|
|
922
|
+
};
|
|
923
|
+
var toRow2 = ({ id, label, cells, children }) => ({
|
|
924
|
+
key: id,
|
|
925
|
+
label,
|
|
926
|
+
cells,
|
|
927
|
+
children: children?.map(toRow2)
|
|
928
|
+
});
|
|
929
|
+
var openKeys2 = (rows) => Object.fromEntries(
|
|
930
|
+
rows.flatMap((r) => [...r.open ? [[r.id, true]] : [], ...Object.entries(openKeys2(r.children ?? []))])
|
|
931
|
+
);
|
|
932
|
+
function HeatStripBlock({ spec }) {
|
|
933
|
+
const states = React18.useMemo(
|
|
934
|
+
() => spec.states.map((s) => ({ key: s.key, label: s.label, hollow: s.hollow, color: COLOR[s.tone ?? "neutral"] })),
|
|
935
|
+
[spec.states]
|
|
936
|
+
);
|
|
937
|
+
const rows = React18.useMemo(() => spec.rows?.map(toRow2), [spec.rows]);
|
|
938
|
+
const defaultExpanded = React18.useMemo(() => openKeys2(spec.rows ?? []), [spec.rows]);
|
|
939
|
+
return /* @__PURE__ */ jsx(
|
|
940
|
+
HeatStrip,
|
|
941
|
+
{
|
|
942
|
+
cells: spec.cells,
|
|
943
|
+
rows,
|
|
944
|
+
states,
|
|
945
|
+
ticks: spec.ticks,
|
|
946
|
+
defaultExpanded
|
|
947
|
+
}
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
function MethodBlock({ spec }) {
|
|
951
|
+
const [open, setOpen] = React18.useState(spec.open ?? false);
|
|
952
|
+
return /* @__PURE__ */ jsx(
|
|
953
|
+
ChatSessionDisclosure,
|
|
954
|
+
{
|
|
955
|
+
variant: "inline",
|
|
956
|
+
label: spec.label ?? "method",
|
|
957
|
+
meta: spec.meta ?? (open ? void 0 : spec.code?.replace(/\*\*/g, "")),
|
|
958
|
+
open,
|
|
959
|
+
onOpenChange: setOpen,
|
|
960
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
961
|
+
spec.code ? /* @__PURE__ */ jsx(ChatSessionDisclosureCode, { children: strong(spec.code) }) : null,
|
|
962
|
+
spec.facts?.length ? /* @__PURE__ */ jsx(PropertyList, { labelWidth: "auto", variant: "summary", children: spec.facts.map((fact) => /* @__PURE__ */ jsx(PropertyRow, { label: fact.label, mono: true, children: fact.value }, fact.label)) }) : null,
|
|
963
|
+
spec.steps?.length ? /* @__PURE__ */ jsx(ChatSessionDisclosureSteps, { steps: spec.steps }) : null
|
|
964
|
+
] })
|
|
965
|
+
}
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
function MetricBlock({ spec }) {
|
|
969
|
+
return /* @__PURE__ */ jsx(
|
|
970
|
+
MetricTile,
|
|
971
|
+
{
|
|
972
|
+
variant: "hero",
|
|
973
|
+
label: spec.label,
|
|
974
|
+
value: metricValue(spec.value),
|
|
975
|
+
tone: spec.value == null ? "muted" : void 0,
|
|
976
|
+
caption: spec.delta,
|
|
977
|
+
captionTone: spec.tone ? CAPTION_TONE[spec.tone] : void 0,
|
|
978
|
+
...gaugeProps(spec.gauge),
|
|
979
|
+
aside: spec.trend?.length ? /* @__PURE__ */ jsx(
|
|
980
|
+
Sparkline,
|
|
981
|
+
{
|
|
982
|
+
values: spec.trend,
|
|
983
|
+
width: 96,
|
|
984
|
+
height: 32,
|
|
985
|
+
strokeWidth: 1.4,
|
|
986
|
+
endMarker: false,
|
|
987
|
+
color: "color-mix(in srgb, var(--color-foreground) 70%, transparent)",
|
|
988
|
+
label: `${spec.label}, recent run`
|
|
989
|
+
}
|
|
990
|
+
) : void 0
|
|
991
|
+
}
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
function submitLabel(question, count, submit) {
|
|
995
|
+
if (submit) return submit.replace("{count}", String(count));
|
|
996
|
+
const q = question.trim();
|
|
997
|
+
return q.endsWith("?") ? `Use ${count} selected` : `${q} ${count}`;
|
|
998
|
+
}
|
|
999
|
+
function MultiAsk({ spec, state = "pending", value: given, id: idProp, onAction }) {
|
|
1000
|
+
const auto = React18.useId();
|
|
1001
|
+
const id = idProp ?? auto;
|
|
1002
|
+
const pending = state === "pending";
|
|
1003
|
+
const [editing, setEditing] = React18.useState(false);
|
|
1004
|
+
const [picked, setPicked] = React18.useState(
|
|
1005
|
+
pending ? spec.default ?? [] : given ?? []
|
|
1006
|
+
);
|
|
1007
|
+
if (!pending && !editing) {
|
|
1008
|
+
const value = given ?? spec.default ?? [];
|
|
1009
|
+
const text = joinPicks(spec.options.filter((o) => value.includes(o.value)));
|
|
1010
|
+
return /* @__PURE__ */ jsx(
|
|
1011
|
+
AskSummary,
|
|
1012
|
+
{
|
|
1013
|
+
rows: [{ label: spec.label ?? "Answer", value: text || "\u2014" }],
|
|
1014
|
+
change: state === "answered" ? "Change answer" : void 0,
|
|
1015
|
+
onChange: () => setEditing(true)
|
|
1016
|
+
}
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
const full = spec.max != null && picked.length >= spec.max;
|
|
1020
|
+
const short = spec.min != null && picked.length < spec.min;
|
|
1021
|
+
const toggle = (v) => setPicked((now) => now.includes(v) ? now.filter((x) => x !== v) : [...now, v]);
|
|
1022
|
+
const all = spec.options.filter((o) => !o.disabled).map((o) => o.value);
|
|
1023
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1024
|
+
/* @__PURE__ */ jsx(
|
|
1025
|
+
Questionnaire,
|
|
1026
|
+
{
|
|
1027
|
+
id: `${auto}-form`,
|
|
1028
|
+
onSubmit: (e) => {
|
|
1029
|
+
e.preventDefault();
|
|
1030
|
+
if (short) return;
|
|
1031
|
+
const value = spec.options.map((o) => o.value).filter((v) => picked.includes(v));
|
|
1032
|
+
onAction?.(editing ? "change" : "reply", value);
|
|
1033
|
+
setEditing(false);
|
|
1034
|
+
},
|
|
1035
|
+
children: /* @__PURE__ */ jsxs(QuestionnaireItem, { name: id, multiple: true, children: [
|
|
1036
|
+
/* @__PURE__ */ jsxs(
|
|
1037
|
+
QuestionnaireTitle,
|
|
1038
|
+
{
|
|
1039
|
+
className: cn(
|
|
1040
|
+
isHeading(spec) && "font-semibold",
|
|
1041
|
+
spec.selectAll && "flex w-full items-baseline justify-between gap-2"
|
|
1042
|
+
),
|
|
1043
|
+
children: [
|
|
1044
|
+
strong(spec.question),
|
|
1045
|
+
spec.selectAll ? /* @__PURE__ */ jsx(AskLink, { onClick: () => setPicked(spec.max != null ? all.slice(0, spec.max) : all), children: "Select all" }) : null
|
|
1046
|
+
]
|
|
1047
|
+
}
|
|
1048
|
+
),
|
|
1049
|
+
spec.description ? /* @__PURE__ */ jsx(QuestionnaireDescription, { children: spec.description }) : null,
|
|
1050
|
+
/* @__PURE__ */ jsx(QuestionnaireChoices, { children: spec.options.map((o) => {
|
|
1051
|
+
const checked = picked.includes(o.value);
|
|
1052
|
+
const { props, body } = choiceParts(o);
|
|
1053
|
+
return /* @__PURE__ */ jsx(
|
|
1054
|
+
QuestionnaireChoice,
|
|
1055
|
+
{
|
|
1056
|
+
value: o.value,
|
|
1057
|
+
...props,
|
|
1058
|
+
disabled: o.disabled || full && !checked,
|
|
1059
|
+
checked,
|
|
1060
|
+
onChange: () => toggle(o.value),
|
|
1061
|
+
children: body
|
|
1062
|
+
},
|
|
1063
|
+
o.value
|
|
1064
|
+
);
|
|
1065
|
+
}) })
|
|
1066
|
+
] })
|
|
1067
|
+
}
|
|
1068
|
+
),
|
|
1069
|
+
/* @__PURE__ */ jsx(AskHint, { children: spec.hint }),
|
|
1070
|
+
/* @__PURE__ */ jsxs(ClarifyActions, { children: [
|
|
1071
|
+
spec.max != null ? full && spec.max < spec.options.length ? /* @__PURE__ */ jsx(AskHint, { tone: "warning", children: "Limit reached \xB7 clear one to swap" }) : /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: hintText(`**${picked.length}** of ${spec.max}`) }) : null,
|
|
1072
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", form: `${auto}-form`, size: "xs", className: "ms-auto", disabled: short, children: submitLabel(spec.question, picked.length, spec.submit) })
|
|
1073
|
+
] })
|
|
1074
|
+
] });
|
|
1075
|
+
}
|
|
1076
|
+
function Placeholder({ kind, options, className }) {
|
|
1077
|
+
const { kind: _kind, ...rest } = options ?? {};
|
|
1078
|
+
const json = JSON.stringify(rest, null, 2);
|
|
1079
|
+
return /* @__PURE__ */ jsxs(
|
|
1080
|
+
"div",
|
|
1081
|
+
{
|
|
1082
|
+
"data-placeholder": kind,
|
|
1083
|
+
className: cn("flex min-w-0 flex-col gap-1 border border-dashed border-border p-2", className),
|
|
1084
|
+
children: [
|
|
1085
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
|
|
1086
|
+
"No renderer yet for the block ",
|
|
1087
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-foreground", children: kind })
|
|
1088
|
+
] }),
|
|
1089
|
+
json !== "{}" ? /* @__PURE__ */ jsx("pre", { className: "max-h-40 overflow-auto font-mono text-sm text-muted-foreground", children: json }) : null
|
|
1090
|
+
]
|
|
1091
|
+
}
|
|
1092
|
+
);
|
|
1093
|
+
}
|
|
1094
|
+
function StepTitle({ step }) {
|
|
1095
|
+
if (!("question" in step)) return null;
|
|
1096
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1097
|
+
/* @__PURE__ */ jsx(QuestionnaireTitle, { className: "font-semibold", children: strong(step.question ?? "") }),
|
|
1098
|
+
"description" in step && step.description ? /* @__PURE__ */ jsx(QuestionnaireDescription, { children: step.description }) : null
|
|
1099
|
+
] });
|
|
1100
|
+
}
|
|
1101
|
+
function StepItem({
|
|
1102
|
+
step,
|
|
1103
|
+
value,
|
|
1104
|
+
onStatusChange
|
|
1105
|
+
}) {
|
|
1106
|
+
const item = { name: step.id, required: step.required, onStatusChange };
|
|
1107
|
+
switch (step.kind) {
|
|
1108
|
+
case "single":
|
|
1109
|
+
case "multi": {
|
|
1110
|
+
const multiple = step.kind === "multi";
|
|
1111
|
+
const chosen = (v) => multiple ? Array.isArray(value) && value.includes(v) : value === v;
|
|
1112
|
+
return /* @__PURE__ */ jsxs(QuestionnaireItem, { ...item, multiple, children: [
|
|
1113
|
+
/* @__PURE__ */ jsx(StepTitle, { step }),
|
|
1114
|
+
/* @__PURE__ */ jsx(QuestionnaireChoices, { children: step.options.map((o) => {
|
|
1115
|
+
const { props, body } = choiceParts(o);
|
|
1116
|
+
return /* @__PURE__ */ jsx(
|
|
1117
|
+
QuestionnaireChoice,
|
|
1118
|
+
{
|
|
1119
|
+
value: o.value,
|
|
1120
|
+
...props,
|
|
1121
|
+
disabled: o.disabled,
|
|
1122
|
+
defaultChecked: chosen(o.value),
|
|
1123
|
+
children: body
|
|
1124
|
+
},
|
|
1125
|
+
o.value
|
|
1126
|
+
);
|
|
1127
|
+
}) })
|
|
1128
|
+
] });
|
|
1129
|
+
}
|
|
1130
|
+
case "number":
|
|
1131
|
+
case "short":
|
|
1132
|
+
case "long":
|
|
1133
|
+
return /* @__PURE__ */ jsxs(QuestionnaireItem, { ...item, children: [
|
|
1134
|
+
/* @__PURE__ */ jsx(StepTitle, { step }),
|
|
1135
|
+
/* @__PURE__ */ jsx(
|
|
1136
|
+
QuestionnaireInput,
|
|
1137
|
+
{
|
|
1138
|
+
type: step.kind === "number" ? "number" : "text",
|
|
1139
|
+
unit: step.kind === "number" ? step.unit : void 0,
|
|
1140
|
+
min: step.kind === "number" ? step.min : void 0,
|
|
1141
|
+
max: step.kind === "number" ? step.max : void 0,
|
|
1142
|
+
step: step.kind === "number" ? step.step : void 0,
|
|
1143
|
+
defaultValue: value == null ? void 0 : String(value),
|
|
1144
|
+
"aria-label": step.question
|
|
1145
|
+
}
|
|
1146
|
+
),
|
|
1147
|
+
"hint" in step ? /* @__PURE__ */ jsx(AskHint, { children: step.hint }) : null
|
|
1148
|
+
] });
|
|
1149
|
+
default:
|
|
1150
|
+
return /* @__PURE__ */ jsx(QuestionnaireItem, { ...item, children: /* @__PURE__ */ jsx(Placeholder, { kind: step.kind, options: step }) });
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
function numberText(value, unit) {
|
|
1154
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
1155
|
+
const text = Number.isFinite(n) ? n.toLocaleString("en-GB") : String(value);
|
|
1156
|
+
if (!unit) return text;
|
|
1157
|
+
return /^[A-Za-z]/.test(unit) ? `${text} ${unit}` : `${text}${unit}`;
|
|
1158
|
+
}
|
|
1159
|
+
function answerText(step, value) {
|
|
1160
|
+
if (value == null || value === "" || Array.isArray(value) && !value.length) return "\u2014";
|
|
1161
|
+
switch (step.kind) {
|
|
1162
|
+
case "single":
|
|
1163
|
+
case "quick": {
|
|
1164
|
+
const o = step.options.find((x) => x.value === value);
|
|
1165
|
+
return o ? "summary" in o && o.summary || o.label : String(value);
|
|
1166
|
+
}
|
|
1167
|
+
case "multi": {
|
|
1168
|
+
const values = Array.isArray(value) ? value : [value];
|
|
1169
|
+
return joinPicks(values.map((v) => step.options.find((x) => x.value === v) ?? { value: String(v), label: String(v) }));
|
|
1170
|
+
}
|
|
1171
|
+
case "number":
|
|
1172
|
+
return numberText(value, step.unit);
|
|
1173
|
+
default:
|
|
1174
|
+
return typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
var labelOf = (step) => "label" in step && step.label || step.id.charAt(0).toUpperCase() + step.id.slice(1);
|
|
1178
|
+
function collect(steps, form) {
|
|
1179
|
+
const data = new FormData(form);
|
|
1180
|
+
const out = {};
|
|
1181
|
+
for (const step of steps) {
|
|
1182
|
+
const all = data.getAll(step.id).map(String).filter(Boolean);
|
|
1183
|
+
if (step.kind === "multi") out[step.id] = all;
|
|
1184
|
+
else if (!all.length) continue;
|
|
1185
|
+
else if (step.kind === "number") out[step.id] = Number(all[0]);
|
|
1186
|
+
else out[step.id] = all[0];
|
|
1187
|
+
}
|
|
1188
|
+
return out;
|
|
1189
|
+
}
|
|
1190
|
+
function MultistepAsk({ spec, state = "pending", value: given, onAction }) {
|
|
1191
|
+
const pending = state === "pending";
|
|
1192
|
+
const [editing, setEditing] = React18.useState(false);
|
|
1193
|
+
const steps = spec.steps;
|
|
1194
|
+
const [item, setItem] = React18.useState(steps[0]?.id ?? "");
|
|
1195
|
+
const [status, setStatus] = React18.useState(
|
|
1196
|
+
() => Object.fromEntries(
|
|
1197
|
+
steps.filter((s) => "default" in s && s.default != null).map((s) => [s.id, "answered"])
|
|
1198
|
+
)
|
|
1199
|
+
);
|
|
1200
|
+
const [review, setReview] = React18.useState(null);
|
|
1201
|
+
const formRef = React18.useRef(null);
|
|
1202
|
+
const current = given ?? {};
|
|
1203
|
+
if (!pending && !editing) {
|
|
1204
|
+
return /* @__PURE__ */ jsx(
|
|
1205
|
+
AskSummary,
|
|
1206
|
+
{
|
|
1207
|
+
rows: steps.map((step2) => ({ label: labelOf(step2), value: answerText(step2, current[step2.id]) })),
|
|
1208
|
+
change: state === "answered" ? "Change answers" : void 0,
|
|
1209
|
+
onChange: () => setEditing(true)
|
|
1210
|
+
}
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
const send = (value) => {
|
|
1214
|
+
onAction?.(editing ? "change" : "reply", value);
|
|
1215
|
+
setEditing(false);
|
|
1216
|
+
setReview(null);
|
|
1217
|
+
};
|
|
1218
|
+
const initial = (step2) => editing ? current[step2.id] : "default" in step2 ? step2.default : void 0;
|
|
1219
|
+
const index = Math.max(0, steps.findIndex((s) => s.id === item));
|
|
1220
|
+
const step = steps[index];
|
|
1221
|
+
const last = index === steps.length - 1;
|
|
1222
|
+
const blocked = Boolean(step?.required && status[step.id] !== "answered");
|
|
1223
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1224
|
+
/* @__PURE__ */ jsxs(
|
|
1225
|
+
Questionnaire,
|
|
1226
|
+
{
|
|
1227
|
+
ref: formRef,
|
|
1228
|
+
item,
|
|
1229
|
+
onItemChange: setItem,
|
|
1230
|
+
hidden: review != null,
|
|
1231
|
+
onSubmit: (e) => {
|
|
1232
|
+
e.preventDefault();
|
|
1233
|
+
send(collect(steps, e.currentTarget));
|
|
1234
|
+
},
|
|
1235
|
+
children: [
|
|
1236
|
+
/* @__PURE__ */ jsx(QuestionnaireProgress, {}),
|
|
1237
|
+
steps.map((s) => /* @__PURE__ */ jsx(
|
|
1238
|
+
StepItem,
|
|
1239
|
+
{
|
|
1240
|
+
step: s,
|
|
1241
|
+
value: initial(s),
|
|
1242
|
+
onStatusChange: (st) => setStatus((now) => ({ ...now, [s.id]: st }))
|
|
1243
|
+
},
|
|
1244
|
+
s.id
|
|
1245
|
+
)),
|
|
1246
|
+
blocked ? /* @__PURE__ */ jsx(AskHint, { tone: "error", children: "Pick one to continue \xB7 this step is required" }) : null,
|
|
1247
|
+
/* @__PURE__ */ jsxs(QuestionnaireActions, { children: [
|
|
1248
|
+
/* @__PURE__ */ jsx(QuestionnairePrevious, {}),
|
|
1249
|
+
!last && !step?.required ? /* @__PURE__ */ jsx(QuestionnaireSkip, {}) : null,
|
|
1250
|
+
/* @__PURE__ */ jsx(QuestionnaireNext, { disabled: blocked }),
|
|
1251
|
+
last && spec.review ? /* @__PURE__ */ jsx(
|
|
1252
|
+
Button,
|
|
1253
|
+
{
|
|
1254
|
+
type: "button",
|
|
1255
|
+
size: "xs",
|
|
1256
|
+
className: "col-start-3 row-start-1 justify-self-end",
|
|
1257
|
+
disabled: blocked,
|
|
1258
|
+
onClick: () => formRef.current && setReview(collect(steps, formRef.current)),
|
|
1259
|
+
children: "Review"
|
|
1260
|
+
}
|
|
1261
|
+
) : /* @__PURE__ */ jsx(QuestionnaireSubmit, { disabled: blocked })
|
|
1262
|
+
] })
|
|
1263
|
+
]
|
|
1264
|
+
}
|
|
1265
|
+
),
|
|
1266
|
+
review ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1267
|
+
/* @__PURE__ */ jsx(QuestionnaireProgressBar, { label: "Review", value: 1 }),
|
|
1268
|
+
/* @__PURE__ */ jsx(PropertyList, { labelWidth: "auto", variant: "summary", children: steps.map((s) => /* @__PURE__ */ jsx(PropertyRow, { label: labelOf(s), mono: true, children: /* @__PURE__ */ jsxs("span", { className: "flex items-baseline gap-2", children: [
|
|
1269
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1", children: answerText(s, review[s.id]) }),
|
|
1270
|
+
/* @__PURE__ */ jsx(
|
|
1271
|
+
AskLink,
|
|
1272
|
+
{
|
|
1273
|
+
onClick: () => {
|
|
1274
|
+
setItem(s.id);
|
|
1275
|
+
setReview(null);
|
|
1276
|
+
},
|
|
1277
|
+
children: "Edit"
|
|
1278
|
+
}
|
|
1279
|
+
)
|
|
1280
|
+
] }) }, s.id)) }),
|
|
1281
|
+
/* @__PURE__ */ jsxs(QuestionnaireActions, { children: [
|
|
1282
|
+
/* @__PURE__ */ jsx(Button, { type: "button", size: "xs", variant: "outline", className: "justify-self-start", onClick: () => setReview(null), children: "Previous" }),
|
|
1283
|
+
/* @__PURE__ */ jsx(Button, { type: "button", size: "xs", className: "col-start-3 justify-self-end", onClick: () => send(review), children: `Submit ${steps.length} answers` })
|
|
1284
|
+
] })
|
|
1285
|
+
] }) : null
|
|
1286
|
+
] });
|
|
1287
|
+
}
|
|
1288
|
+
function NarrativeBlock({ spec, active = spec.active, onActiveChange, trailing }) {
|
|
1289
|
+
const marker = (n) => /* @__PURE__ */ jsx(
|
|
1290
|
+
CitationMarker,
|
|
1291
|
+
{
|
|
1292
|
+
"data-active": active === n || void 0,
|
|
1293
|
+
onMouseEnter: () => onActiveChange?.(n),
|
|
1294
|
+
onMouseLeave: () => onActiveChange?.(void 0),
|
|
1295
|
+
children: n
|
|
1296
|
+
},
|
|
1297
|
+
`cite-${n}`
|
|
1298
|
+
);
|
|
1299
|
+
return /* @__PURE__ */ jsxs("p", { children: [
|
|
1300
|
+
prose(spec.text, marker),
|
|
1301
|
+
spec.cites?.map(marker),
|
|
1302
|
+
trailing
|
|
1303
|
+
] });
|
|
1304
|
+
}
|
|
1305
|
+
function ProposalBlock({ spec, onAction }) {
|
|
1306
|
+
return /* @__PURE__ */ jsxs(
|
|
1307
|
+
ProposalCard,
|
|
1308
|
+
{
|
|
1309
|
+
flush: true,
|
|
1310
|
+
seamless: true,
|
|
1311
|
+
title: spec.heading,
|
|
1312
|
+
done: spec.done?.label,
|
|
1313
|
+
consequence: spec.consequence,
|
|
1314
|
+
actions: spec.actions.length ? /* @__PURE__ */ jsx(
|
|
1315
|
+
ActionRow,
|
|
1316
|
+
{
|
|
1317
|
+
actions: spec.actions,
|
|
1318
|
+
onAction: (id) => onAction?.("action", id)
|
|
1319
|
+
}
|
|
1320
|
+
) : void 0,
|
|
1321
|
+
children: [
|
|
1322
|
+
spec.rows?.length ? /* @__PURE__ */ jsx(PropertyList, { labelWidth: "auto", variant: "summary", children: spec.rows.map((row) => /* @__PURE__ */ jsx(PropertyRow, { label: row.label, mono: true, children: row.value }, row.label)) }) : null,
|
|
1323
|
+
spec.figures?.length ? /* @__PURE__ */ jsx(MetricGrid, { joined: true, seamless: true, minTileWidth: 72, children: spec.figures.map((f) => /* @__PURE__ */ jsx(MetricTile, { variant: "figure", label: f.label, value: f.value }, f.label)) }) : null
|
|
1324
|
+
]
|
|
1325
|
+
}
|
|
1326
|
+
);
|
|
1327
|
+
}
|
|
1328
|
+
function QuickAsk({ spec, state = "pending", value: given, onAction }) {
|
|
1329
|
+
const pending = state === "pending";
|
|
1330
|
+
const [editing, setEditing] = React18.useState(false);
|
|
1331
|
+
const keyed = Boolean(spec.more?.length);
|
|
1332
|
+
const rows = [
|
|
1333
|
+
{ ...spec, key: spec.label ?? spec.question },
|
|
1334
|
+
...(spec.more ?? []).map((r) => ({ ...r, key: r.id }))
|
|
1335
|
+
];
|
|
1336
|
+
const settled = () => {
|
|
1337
|
+
const value = given;
|
|
1338
|
+
if (keyed) return value ?? {};
|
|
1339
|
+
return { [rows[0].key]: value ?? spec.default };
|
|
1340
|
+
};
|
|
1341
|
+
const [picked, setPicked] = React18.useState(
|
|
1342
|
+
() => pending ? Object.fromEntries(rows.map((r) => [r.key, r.default])) : settled()
|
|
1343
|
+
);
|
|
1344
|
+
if (!pending && !editing) {
|
|
1345
|
+
const value = settled();
|
|
1346
|
+
return /* @__PURE__ */ jsx(
|
|
1347
|
+
AskSummary,
|
|
1348
|
+
{
|
|
1349
|
+
rows: rows.map((r) => ({
|
|
1350
|
+
label: r.label ?? r.question,
|
|
1351
|
+
value: r.options.find((o) => o.value === value[r.key])?.label ?? value[r.key] ?? "\u2014"
|
|
1352
|
+
})),
|
|
1353
|
+
change: state === "answered" ? keyed ? "Change answers" : "Change answer" : void 0,
|
|
1354
|
+
onChange: () => setEditing(true)
|
|
1355
|
+
}
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
const pick = (key, v) => {
|
|
1359
|
+
const next = { ...picked, [key]: v };
|
|
1360
|
+
setPicked(next);
|
|
1361
|
+
if (rows.some((r) => next[r.key] == null)) return;
|
|
1362
|
+
const value = keyed ? next : v;
|
|
1363
|
+
onAction?.(editing ? "change" : "reply", value);
|
|
1364
|
+
setEditing(false);
|
|
1365
|
+
};
|
|
1366
|
+
return /* @__PURE__ */ jsx(Fragment, { children: rows.map((r, i) => /* @__PURE__ */ jsxs(React18.Fragment, { children: [
|
|
1367
|
+
i === 0 ? /* @__PURE__ */ jsx(AskQuestion, { text: spec }) : /* @__PURE__ */ jsx("p", { className: "mt-1", children: r.question }),
|
|
1368
|
+
/* @__PURE__ */ jsx(
|
|
1369
|
+
SegmentedControl,
|
|
1370
|
+
{
|
|
1371
|
+
size: "sm",
|
|
1372
|
+
"aria-label": r.question,
|
|
1373
|
+
variant: "solid",
|
|
1374
|
+
stretch: spec.stretch,
|
|
1375
|
+
options: r.options,
|
|
1376
|
+
defaultValue: picked[r.key] ?? null,
|
|
1377
|
+
onValueChange: (v) => pick(r.key, v)
|
|
1378
|
+
}
|
|
1379
|
+
),
|
|
1380
|
+
/* @__PURE__ */ jsx(AskHint, { children: r.hint })
|
|
1381
|
+
] }, r.key)) });
|
|
1382
|
+
}
|
|
1383
|
+
function RankedBlock({ spec }) {
|
|
1384
|
+
return /* @__PURE__ */ jsx(
|
|
1385
|
+
BarChartH,
|
|
1386
|
+
{
|
|
1387
|
+
variant: "ranked",
|
|
1388
|
+
color: "var(--color-primary)",
|
|
1389
|
+
diverging: spec.diverging,
|
|
1390
|
+
data: spec.items.map((item) => ({
|
|
1391
|
+
label: item.label,
|
|
1392
|
+
value: item.value,
|
|
1393
|
+
display: item.display ?? figureText(item.value),
|
|
1394
|
+
muted: item.muted
|
|
1395
|
+
}))
|
|
1396
|
+
}
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1399
|
+
function Rows({ rows }) {
|
|
1400
|
+
return /* @__PURE__ */ jsx(PropertyList, { labelWidth: "auto", variant: "summary", children: rows.map((row) => /* @__PURE__ */ jsxs(PropertyRow, { label: row.label, mono: row.mono ?? true, children: [
|
|
1401
|
+
row.value,
|
|
1402
|
+
row.source ? /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
1403
|
+
" \xB7 ",
|
|
1404
|
+
row.source
|
|
1405
|
+
] }) : null
|
|
1406
|
+
] }, row.label)) });
|
|
1407
|
+
}
|
|
1408
|
+
function RecordBlock({ spec }) {
|
|
1409
|
+
const { header } = spec;
|
|
1410
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1.5", children: [
|
|
1411
|
+
header ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1412
|
+
header.initials ? /* @__PURE__ */ jsx(Avatar, { className: "size-7 border border-border", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "text-xs font-semibold text-muted-foreground", children: header.initials }) }) : null,
|
|
1413
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 truncate font-semibold", children: header.title }),
|
|
1414
|
+
header.status ? /* @__PURE__ */ jsx(
|
|
1415
|
+
Badge,
|
|
1416
|
+
{
|
|
1417
|
+
variant: "soft",
|
|
1418
|
+
size: "xs",
|
|
1419
|
+
tone: BADGE_TONE[header.status.tone ?? "neutral"],
|
|
1420
|
+
className: "ms-auto font-normal",
|
|
1421
|
+
children: header.status.label
|
|
1422
|
+
}
|
|
1423
|
+
) : null
|
|
1424
|
+
] }) : null,
|
|
1425
|
+
spec.rows?.length ? /* @__PURE__ */ jsx(Rows, { rows: spec.rows }) : null,
|
|
1426
|
+
spec.groups?.map((group, i) => /* @__PURE__ */ jsxs("div", { className: i > 0 || spec.rows?.length ? "mt-1 flex flex-col gap-1" : "flex flex-col gap-1", children: [
|
|
1427
|
+
/* @__PURE__ */ jsx(Eyebrow, { children: group.label }),
|
|
1428
|
+
/* @__PURE__ */ jsx(Rows, { rows: group.rows })
|
|
1429
|
+
] }, group.label))
|
|
1430
|
+
] });
|
|
1431
|
+
}
|
|
1432
|
+
function ScopeBlock({ spec, fixed, onAction }) {
|
|
1433
|
+
const stale = spec.parts.some((p) => typeof p === "object" && p.mark === "stale");
|
|
1434
|
+
return /* @__PURE__ */ jsx(
|
|
1435
|
+
ScopeLine,
|
|
1436
|
+
{
|
|
1437
|
+
parts: spec.parts,
|
|
1438
|
+
fixedParts: fixed,
|
|
1439
|
+
defaultOpenPart: spec.openPart,
|
|
1440
|
+
note: spec.hint,
|
|
1441
|
+
noteTone: stale ? "warning" : "muted",
|
|
1442
|
+
seamless: true,
|
|
1443
|
+
onPartChange: (part, value) => onAction?.("scope", { part, value })
|
|
1444
|
+
}
|
|
1445
|
+
);
|
|
1446
|
+
}
|
|
1447
|
+
var OTHER = "\0other";
|
|
1448
|
+
function SingleAsk({ spec, state = "pending", value: given, id: idProp, onAction }) {
|
|
1449
|
+
const auto = React18.useId();
|
|
1450
|
+
const id = idProp ?? auto;
|
|
1451
|
+
const pending = state === "pending";
|
|
1452
|
+
const [editing, setEditing] = React18.useState(false);
|
|
1453
|
+
const [picked, setPicked] = React18.useState(spec.default);
|
|
1454
|
+
const settledValue = given ?? spec.default;
|
|
1455
|
+
if (!pending && !editing) {
|
|
1456
|
+
const option = spec.options.find((o) => o.value === settledValue);
|
|
1457
|
+
return /* @__PURE__ */ jsx(
|
|
1458
|
+
AskSummary,
|
|
1459
|
+
{
|
|
1460
|
+
rows: [{ label: spec.label ?? "Answer", value: pickText(option, settledValue) }],
|
|
1461
|
+
change: state === "answered" ? "Change answer" : void 0,
|
|
1462
|
+
onChange: () => setEditing(true)
|
|
1463
|
+
}
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
const send = (value) => {
|
|
1467
|
+
onAction?.(editing ? "change" : "reply", value);
|
|
1468
|
+
setEditing(false);
|
|
1469
|
+
};
|
|
1470
|
+
const pick = (value) => {
|
|
1471
|
+
setPicked(value);
|
|
1472
|
+
if (value !== OTHER && !spec.submit) send(value);
|
|
1473
|
+
};
|
|
1474
|
+
const other = picked === OTHER;
|
|
1475
|
+
const buttons = spec.submit || spec.skippable || other;
|
|
1476
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1477
|
+
/* @__PURE__ */ jsx(
|
|
1478
|
+
Questionnaire,
|
|
1479
|
+
{
|
|
1480
|
+
onSubmit: (e) => {
|
|
1481
|
+
e.preventDefault();
|
|
1482
|
+
if (other) {
|
|
1483
|
+
const text = new FormData(e.currentTarget).getAll(id).map(String).find((v) => v !== OTHER);
|
|
1484
|
+
if (text?.trim()) send(text.trim());
|
|
1485
|
+
} else if (picked) send(picked);
|
|
1486
|
+
},
|
|
1487
|
+
id: `${auto}-form`,
|
|
1488
|
+
children: /* @__PURE__ */ jsxs(QuestionnaireItem, { name: id, children: [
|
|
1489
|
+
/* @__PURE__ */ jsx(QuestionnaireTitle, { className: isHeading(spec) ? "font-semibold" : void 0, children: strong(spec.question) }),
|
|
1490
|
+
spec.description ? /* @__PURE__ */ jsx(QuestionnaireDescription, { children: spec.description }) : null,
|
|
1491
|
+
/* @__PURE__ */ jsxs(QuestionnaireChoices, { children: [
|
|
1492
|
+
spec.options.map((o) => {
|
|
1493
|
+
const { props, body } = choiceParts(o);
|
|
1494
|
+
return /* @__PURE__ */ jsx(
|
|
1495
|
+
QuestionnaireChoice,
|
|
1496
|
+
{
|
|
1497
|
+
value: o.value,
|
|
1498
|
+
...props,
|
|
1499
|
+
disabled: o.disabled,
|
|
1500
|
+
checked: picked === o.value,
|
|
1501
|
+
onChange: () => pick(o.value),
|
|
1502
|
+
children: body
|
|
1503
|
+
},
|
|
1504
|
+
o.value
|
|
1505
|
+
);
|
|
1506
|
+
}),
|
|
1507
|
+
spec.other ? /* @__PURE__ */ jsx(QuestionnaireChoice, { value: OTHER, checked: other, onChange: () => pick(OTHER), children: /* @__PURE__ */ jsx(QuestionnaireChoiceTitle, { children: "Other\u2026" }) }) : null
|
|
1508
|
+
] }),
|
|
1509
|
+
other ? /* @__PURE__ */ jsx(QuestionnaireInput, { "aria-label": "Your answer", autoFocus: true }) : null
|
|
1510
|
+
] })
|
|
1511
|
+
}
|
|
1512
|
+
),
|
|
1513
|
+
/* @__PURE__ */ jsx(AskHint, { children: spec.hint }),
|
|
1514
|
+
buttons ? /* @__PURE__ */ jsxs(ClarifyActions, { align: "end", children: [
|
|
1515
|
+
spec.skippable && !other ? /* @__PURE__ */ jsx(Button, { size: "xs", variant: "ghost", onClick: () => onAction?.("skip"), children: "Skip" }) : null,
|
|
1516
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", form: `${auto}-form`, size: "xs", disabled: !picked, children: other ? "Use this" : spec.submit ?? "Use this" })
|
|
1517
|
+
] }) : null
|
|
1518
|
+
] });
|
|
1519
|
+
}
|
|
1520
|
+
function SuggestionsAsk({ spec, state = "pending", value: given, onAction }) {
|
|
1521
|
+
const open = state === "pending" || state === "answered";
|
|
1522
|
+
const picked = typeof given === "string" ? [given] : [];
|
|
1523
|
+
const all = spec.groups?.length ? spec.groups.flatMap((g) => g.items) : spec.items ?? [];
|
|
1524
|
+
return /* @__PURE__ */ jsx(
|
|
1525
|
+
SuggestionChips,
|
|
1526
|
+
{
|
|
1527
|
+
items: spec.items,
|
|
1528
|
+
groups: spec.groups,
|
|
1529
|
+
layout: spec.layout,
|
|
1530
|
+
sent: open ? picked : all,
|
|
1531
|
+
onSelect: (text) => onAction?.("reply", text)
|
|
1532
|
+
}
|
|
1533
|
+
);
|
|
1534
|
+
}
|
|
1535
|
+
var FITS = 5;
|
|
1536
|
+
var COLUMN_MIN = 66;
|
|
1537
|
+
function cellText(cell) {
|
|
1538
|
+
if (cell == null) return void 0;
|
|
1539
|
+
if (typeof cell !== "object" || !("value" in cell) || "type" in cell) return figureText(cell);
|
|
1540
|
+
return figureText(cell.value);
|
|
1541
|
+
}
|
|
1542
|
+
function CellText({ cell }) {
|
|
1543
|
+
if (cell == null) return /* @__PURE__ */ jsx(Fragment, { children: "\u2014" });
|
|
1544
|
+
if (typeof cell !== "object" || !("value" in cell) || "type" in cell) {
|
|
1545
|
+
return /* @__PURE__ */ jsx(Fragment, { children: figureText(cell) });
|
|
1546
|
+
}
|
|
1547
|
+
return /* @__PURE__ */ jsx(
|
|
1548
|
+
"span",
|
|
1549
|
+
{
|
|
1550
|
+
className: cn(
|
|
1551
|
+
cell.tone === "good" && "text-success",
|
|
1552
|
+
cell.tone === "bad" && "text-destructive",
|
|
1553
|
+
cell.strong && "font-semibold"
|
|
1554
|
+
),
|
|
1555
|
+
children: figureText(cell.value)
|
|
1556
|
+
}
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
function TableBlock({ spec, onAction }) {
|
|
1560
|
+
const truncated = spec.total != null && spec.total > spec.rows.length;
|
|
1561
|
+
const highlighted = new Set(spec.highlight?.map((i) => spec.rows[i]));
|
|
1562
|
+
const data = spec.totals ? [...spec.rows, spec.totals] : spec.rows;
|
|
1563
|
+
const columns = spec.columns.map((c) => {
|
|
1564
|
+
const sorted = spec.sort?.key === c.key ? spec.sort.dir : void 0;
|
|
1565
|
+
return {
|
|
1566
|
+
id: c.key,
|
|
1567
|
+
header: sorted ? () => /* @__PURE__ */ jsx("span", { className: "text-foreground", children: `${c.label} ${sorted === "asc" ? "\u25B4" : "\u25BE"}` }) : c.label,
|
|
1568
|
+
accessorFn: (row) => row[c.key],
|
|
1569
|
+
cell: (ctx) => /* @__PURE__ */ jsx(CellText, { cell: ctx.getValue() }),
|
|
1570
|
+
meta: { align: c.align, cellClassName: cn("whitespace-nowrap", c.mono && "font-mono") }
|
|
1571
|
+
};
|
|
1572
|
+
});
|
|
1573
|
+
const { rowKey } = spec;
|
|
1574
|
+
const keyOf = (row) => rowKey != null && row !== spec.totals ? cellText(row[rowKey]) : void 0;
|
|
1575
|
+
const noun = [spec.noun, spec.note].filter(Boolean).join(" \xB7 ") || void 0;
|
|
1576
|
+
return /* @__PURE__ */ jsx(
|
|
1577
|
+
DataTable,
|
|
1578
|
+
{
|
|
1579
|
+
columns,
|
|
1580
|
+
data,
|
|
1581
|
+
density: "compact",
|
|
1582
|
+
seamless: true,
|
|
1583
|
+
enableSorting: false,
|
|
1584
|
+
enableColumnVisibility: false,
|
|
1585
|
+
minWidth: spec.columns.length > FITS ? spec.columns.length * COLUMN_MIN : void 0,
|
|
1586
|
+
isRowHighlighted: highlighted.size ? (row) => highlighted.has(row) : void 0,
|
|
1587
|
+
isTotalRow: spec.totals ? (row) => row === spec.totals : void 0,
|
|
1588
|
+
isRowSelected: spec.selected != null ? (row) => keyOf(row) === spec.selected : void 0,
|
|
1589
|
+
onRowClick: rowKey != null && onAction ? (row) => {
|
|
1590
|
+
const key = keyOf(row);
|
|
1591
|
+
if (key != null) onAction("select", key);
|
|
1592
|
+
} : void 0,
|
|
1593
|
+
preview: {
|
|
1594
|
+
total: spec.total,
|
|
1595
|
+
noun,
|
|
1596
|
+
onOpen: truncated && onAction ? () => onAction("open") : void 0
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
);
|
|
1600
|
+
}
|
|
1601
|
+
var DOT_TONE = {
|
|
1602
|
+
good: "success",
|
|
1603
|
+
bad: "error",
|
|
1604
|
+
warn: "warning",
|
|
1605
|
+
neutral: "muted"
|
|
1606
|
+
};
|
|
1607
|
+
function Events({ events }) {
|
|
1608
|
+
return /* @__PURE__ */ jsx(Fragment, { children: events.map((event, i) => /* @__PURE__ */ jsxs(React18.Fragment, { children: [
|
|
1609
|
+
event.section && event.section !== events[i - 1]?.section ? /* @__PURE__ */ jsx(TimelineSection, { children: event.section }) : null,
|
|
1610
|
+
/* @__PURE__ */ jsxs(
|
|
1611
|
+
TimelineEntry,
|
|
1612
|
+
{
|
|
1613
|
+
when: event.when,
|
|
1614
|
+
marker: /* @__PURE__ */ jsx(StatusDot, { tone: DOT_TONE[event.tone ?? "neutral"], size: "md" }),
|
|
1615
|
+
highlight: event.highlight ? "error" : void 0,
|
|
1616
|
+
nested: event.children?.length ? /* @__PURE__ */ jsx(Events, { events: event.children }) : void 0,
|
|
1617
|
+
defaultOpen: event.open,
|
|
1618
|
+
children: [
|
|
1619
|
+
event.text,
|
|
1620
|
+
event.detail ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: event.detail }) : null
|
|
1621
|
+
]
|
|
1622
|
+
}
|
|
1623
|
+
)
|
|
1624
|
+
] }, i)) });
|
|
1625
|
+
}
|
|
1626
|
+
function TimelineBlock({ spec }) {
|
|
1627
|
+
return /* @__PURE__ */ jsx(TimelineList, { variant: "compact", children: /* @__PURE__ */ jsx(Events, { events: spec.events }) });
|
|
1628
|
+
}
|
|
1629
|
+
var RING_COLOR = {
|
|
1630
|
+
good: "var(--color-success)",
|
|
1631
|
+
bad: "var(--color-destructive)",
|
|
1632
|
+
warn: "var(--color-warning)",
|
|
1633
|
+
neutral: "var(--color-muted-foreground)"
|
|
1634
|
+
};
|
|
1635
|
+
function TimeseriesBlock({ spec }) {
|
|
1636
|
+
const series = spec.series[0];
|
|
1637
|
+
if (!series) return null;
|
|
1638
|
+
const labels = series.points.map(([at2]) => at2);
|
|
1639
|
+
const values = series.points.map(([, value]) => value);
|
|
1640
|
+
const at = (label) => label == null ? -1 : labels.indexOf(label);
|
|
1641
|
+
const forecastFrom = at(spec.forecastFrom);
|
|
1642
|
+
const breaches = spec.reference ? values.flatMap((value, index) => value != null && value > spec.reference.value ? [{ index, color: RING_COLOR.bad }] : []) : [];
|
|
1643
|
+
const format = (value) => {
|
|
1644
|
+
const n = figureText(Math.round(value * 10) / 10);
|
|
1645
|
+
return spec.unit ? `${n} ${spec.unit}` : n;
|
|
1646
|
+
};
|
|
1647
|
+
return /* @__PURE__ */ jsx(
|
|
1648
|
+
LineChart,
|
|
1649
|
+
{
|
|
1650
|
+
"aria-label": series.name,
|
|
1651
|
+
values,
|
|
1652
|
+
name: series.name,
|
|
1653
|
+
compare: spec.series.slice(1).map((other) => ({
|
|
1654
|
+
name: other.name,
|
|
1655
|
+
// Aligned on the first series' periods: a period it lacks is a gap.
|
|
1656
|
+
values: labels.map((label) => other.points.find(([at2]) => at2 === label)?.[1] ?? null)
|
|
1657
|
+
})),
|
|
1658
|
+
labels,
|
|
1659
|
+
format,
|
|
1660
|
+
zero: false,
|
|
1661
|
+
color: "var(--color-foreground)",
|
|
1662
|
+
band: spec.band && { ...spec.band, color: "var(--color-primary)" },
|
|
1663
|
+
reference: spec.reference,
|
|
1664
|
+
highlights: [...(spec.marks ?? []).map((mark) => ({ index: at(mark.at), color: RING_COLOR[mark.tone ?? "neutral"] })), ...breaches].filter((mark) => mark.index >= 0),
|
|
1665
|
+
forecastFrom: forecastFrom >= 0 ? forecastFrom : void 0,
|
|
1666
|
+
forecastLabel: spec.forecastLabel
|
|
1667
|
+
}
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
function TraceBlock({ spec, onAction }) {
|
|
1671
|
+
const [open, setOpen] = React18.useState(!spec.folded);
|
|
1672
|
+
const list = /* @__PURE__ */ jsx(TraceList, { variant: "progress", children: spec.steps.map((step, i) => /* @__PURE__ */ jsx(
|
|
1673
|
+
TraceStep,
|
|
1674
|
+
{
|
|
1675
|
+
name: step.label,
|
|
1676
|
+
duration: step.detail,
|
|
1677
|
+
status: step.state,
|
|
1678
|
+
error: step.error
|
|
1679
|
+
},
|
|
1680
|
+
step.id ?? i
|
|
1681
|
+
)) });
|
|
1682
|
+
const actions = spec.actions?.length ? /* @__PURE__ */ jsx(ActionRow, { actions: spec.actions, onAction: (id) => onAction?.("action", id) }) : null;
|
|
1683
|
+
if (!spec.folded) {
|
|
1684
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1685
|
+
list,
|
|
1686
|
+
actions
|
|
1687
|
+
] });
|
|
1688
|
+
}
|
|
1689
|
+
return /* @__PURE__ */ jsx(
|
|
1690
|
+
ChatSessionDisclosure,
|
|
1691
|
+
{
|
|
1692
|
+
variant: "inline",
|
|
1693
|
+
label: `${spec.steps.length} steps`,
|
|
1694
|
+
meta: spec.summary,
|
|
1695
|
+
open,
|
|
1696
|
+
onOpenChange: setOpen,
|
|
1697
|
+
children: list
|
|
1698
|
+
}
|
|
1699
|
+
);
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
// src/registry.ts
|
|
1703
|
+
var BLOCK_RENDERERS = {
|
|
1704
|
+
confirm: ConfirmAsk,
|
|
1705
|
+
single: SingleAsk,
|
|
1706
|
+
multi: MultiAsk,
|
|
1707
|
+
quick: QuickAsk,
|
|
1708
|
+
period: null,
|
|
1709
|
+
number: null,
|
|
1710
|
+
short: null,
|
|
1711
|
+
long: null,
|
|
1712
|
+
entity: null,
|
|
1713
|
+
scale: null,
|
|
1714
|
+
multistep: MultistepAsk,
|
|
1715
|
+
form: FormAsk,
|
|
1716
|
+
weights: null,
|
|
1717
|
+
approval: null,
|
|
1718
|
+
interpretation: null,
|
|
1719
|
+
fork: null,
|
|
1720
|
+
plan: null,
|
|
1721
|
+
modelspec: null,
|
|
1722
|
+
hypothesis: null,
|
|
1723
|
+
range: null,
|
|
1724
|
+
suggestions: SuggestionsAsk,
|
|
1725
|
+
narrative: NarrativeBlock,
|
|
1726
|
+
metric: MetricBlock,
|
|
1727
|
+
grid: GridBlock,
|
|
1728
|
+
table: TableBlock,
|
|
1729
|
+
attr: null,
|
|
1730
|
+
record: RecordBlock,
|
|
1731
|
+
ranked: RankedBlock,
|
|
1732
|
+
timeseries: TimeseriesBlock,
|
|
1733
|
+
bars: BarsBlock,
|
|
1734
|
+
waterfall: null,
|
|
1735
|
+
matrix: null,
|
|
1736
|
+
funnel: null,
|
|
1737
|
+
histogram: null,
|
|
1738
|
+
timeline: TimelineBlock,
|
|
1739
|
+
subgraph: null,
|
|
1740
|
+
method: MethodBlock,
|
|
1741
|
+
citations: CitationsBlock,
|
|
1742
|
+
files: FilesBlock,
|
|
1743
|
+
proposal: ProposalBlock,
|
|
1744
|
+
cannot: CannotBlock,
|
|
1745
|
+
caveat: CaveatBlock,
|
|
1746
|
+
scope: ScopeBlock,
|
|
1747
|
+
checks: null,
|
|
1748
|
+
trace: TraceBlock,
|
|
1749
|
+
activity: ActivityBlock,
|
|
1750
|
+
gantt: GanttBlock,
|
|
1751
|
+
heatstrip: HeatStripBlock,
|
|
1752
|
+
test: null,
|
|
1753
|
+
coef: null,
|
|
1754
|
+
forest: null,
|
|
1755
|
+
scatter: null,
|
|
1756
|
+
box: null,
|
|
1757
|
+
correlation: null,
|
|
1758
|
+
control: null,
|
|
1759
|
+
pareto: null,
|
|
1760
|
+
survival: null,
|
|
1761
|
+
tornado: null,
|
|
1762
|
+
decomposition: null,
|
|
1763
|
+
modeleval: null,
|
|
1764
|
+
pivot: null,
|
|
1765
|
+
profile: null,
|
|
1766
|
+
evidence: null,
|
|
1767
|
+
dumbbell: null,
|
|
1768
|
+
quantiles: null
|
|
1769
|
+
};
|
|
1770
|
+
|
|
1771
|
+
// src/stream/patch.ts
|
|
1772
|
+
var BlockPatchError = class extends Error {
|
|
1773
|
+
};
|
|
1774
|
+
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1775
|
+
function merge(target, fields) {
|
|
1776
|
+
const next = { ...target, ...fields };
|
|
1777
|
+
for (const [name, value] of Object.entries(fields)) if (value === null) delete next[name];
|
|
1778
|
+
return next;
|
|
1779
|
+
}
|
|
1780
|
+
var identity = (item) => isObj(item) ? item.key ?? item.id : void 0;
|
|
1781
|
+
var where = (path, depth) => JSON.stringify(path.slice(0, depth));
|
|
1782
|
+
function indexOn(list, segment, path, depth) {
|
|
1783
|
+
const at = typeof segment === "number" ? segment : list.findIndex((item) => identity(item) === segment);
|
|
1784
|
+
if (at < 0 || at >= list.length) throw new BlockPatchError(`No item ${JSON.stringify(segment)} at ${where(path, depth)}.`);
|
|
1785
|
+
return at;
|
|
1786
|
+
}
|
|
1787
|
+
function update(node, path, depth, fn) {
|
|
1788
|
+
if (depth === path.length) return fn(node);
|
|
1789
|
+
const segment = path[depth];
|
|
1790
|
+
if (Array.isArray(node)) {
|
|
1791
|
+
const at = indexOn(node, segment, path, depth);
|
|
1792
|
+
const next = node.slice();
|
|
1793
|
+
next[at] = update(node[at], path, depth + 1, fn);
|
|
1794
|
+
return next;
|
|
1795
|
+
}
|
|
1796
|
+
if (isObj(node) && typeof segment === "string") {
|
|
1797
|
+
return { ...node, [segment]: update(node[segment], path, depth + 1, fn) };
|
|
1798
|
+
}
|
|
1799
|
+
throw new BlockPatchError(`Nothing to read ${JSON.stringify(segment)} from at ${where(path, depth)}.`);
|
|
1800
|
+
}
|
|
1801
|
+
function patchValue(value, patch) {
|
|
1802
|
+
const at = JSON.stringify(patch.at ?? []);
|
|
1803
|
+
switch (patch.op) {
|
|
1804
|
+
case "set":
|
|
1805
|
+
if (!isObj(value)) throw new BlockPatchError(`"set": ${at} is not an object.`);
|
|
1806
|
+
return merge(value, patch.fields);
|
|
1807
|
+
case "append":
|
|
1808
|
+
if (value !== void 0 && typeof value !== "string") throw new BlockPatchError(`"append": ${at} is not text.`);
|
|
1809
|
+
return (value ?? "") + patch.text;
|
|
1810
|
+
case "push":
|
|
1811
|
+
if (value !== void 0 && !Array.isArray(value)) throw new BlockPatchError(`"push": ${at} is not a list.`);
|
|
1812
|
+
return [...value ?? [], ...patch.items];
|
|
1813
|
+
case "upsert": {
|
|
1814
|
+
if (value !== void 0 && !Array.isArray(value)) throw new BlockPatchError(`"upsert": ${at} is not a list.`);
|
|
1815
|
+
const id = identity(patch.item);
|
|
1816
|
+
if (id === void 0) throw new BlockPatchError(`"upsert": the item at ${at} has no key or id.`);
|
|
1817
|
+
const list = value ?? [];
|
|
1818
|
+
const found = list.findIndex((item) => identity(item) === id);
|
|
1819
|
+
if (found < 0) return [...list, merge({}, patch.item)];
|
|
1820
|
+
return list.map((item, i) => i === found ? merge(item, patch.item) : item);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
}
|
|
1824
|
+
function applyBlockPatch(spec, patch) {
|
|
1825
|
+
const next = update(spec, patch.at ?? [], 0, (value) => patchValue(value, patch));
|
|
1826
|
+
return "kind" in spec ? { ...next, kind: spec.kind } : next;
|
|
1827
|
+
}
|
|
1828
|
+
function applyBlockPatches(spec, patches) {
|
|
1829
|
+
return patches.reduce(applyBlockPatch, spec);
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
// src/stream/source.ts
|
|
1833
|
+
var toArray = (chunk) => Array.isArray(chunk) ? chunk : [chunk];
|
|
1834
|
+
async function* patchesOf(source) {
|
|
1835
|
+
for await (const chunk of source) yield toArray(chunk);
|
|
1836
|
+
}
|
|
1837
|
+
async function* fromNdjson(input) {
|
|
1838
|
+
const body = input instanceof Response ? input.body : input;
|
|
1839
|
+
if (!body) return;
|
|
1840
|
+
const reader = body.getReader();
|
|
1841
|
+
const decoder = new TextDecoder();
|
|
1842
|
+
let buffered = "";
|
|
1843
|
+
try {
|
|
1844
|
+
for (; ; ) {
|
|
1845
|
+
const { value, done } = await reader.read();
|
|
1846
|
+
buffered += decoder.decode(value, { stream: !done });
|
|
1847
|
+
let newline = buffered.indexOf("\n");
|
|
1848
|
+
while (newline >= 0) {
|
|
1849
|
+
const line = buffered.slice(0, newline).trim();
|
|
1850
|
+
buffered = buffered.slice(newline + 1);
|
|
1851
|
+
if (line) yield JSON.parse(line);
|
|
1852
|
+
newline = buffered.indexOf("\n");
|
|
1853
|
+
}
|
|
1854
|
+
if (done) break;
|
|
1855
|
+
}
|
|
1856
|
+
const rest = buffered.trim();
|
|
1857
|
+
if (rest) yield JSON.parse(rest);
|
|
1858
|
+
} finally {
|
|
1859
|
+
reader.releaseLock();
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
async function* fromEventSource(source, { event = "message", done = "done", close = true } = {}) {
|
|
1863
|
+
const queue = [];
|
|
1864
|
+
let finished = false;
|
|
1865
|
+
let failure;
|
|
1866
|
+
let wake;
|
|
1867
|
+
const notify = () => {
|
|
1868
|
+
wake?.();
|
|
1869
|
+
wake = void 0;
|
|
1870
|
+
};
|
|
1871
|
+
const onData = (e) => {
|
|
1872
|
+
try {
|
|
1873
|
+
queue.push(JSON.parse(e.data));
|
|
1874
|
+
} catch (error) {
|
|
1875
|
+
failure = error;
|
|
1876
|
+
}
|
|
1877
|
+
notify();
|
|
1878
|
+
};
|
|
1879
|
+
const onDone = () => {
|
|
1880
|
+
finished = true;
|
|
1881
|
+
notify();
|
|
1882
|
+
};
|
|
1883
|
+
const onError = () => {
|
|
1884
|
+
if (source.readyState === EventSource.CLOSED) onDone();
|
|
1885
|
+
};
|
|
1886
|
+
source.addEventListener(event, onData);
|
|
1887
|
+
source.addEventListener(done, onDone);
|
|
1888
|
+
source.addEventListener("error", onError);
|
|
1889
|
+
try {
|
|
1890
|
+
for (; ; ) {
|
|
1891
|
+
if (failure) throw failure;
|
|
1892
|
+
const next = queue.shift();
|
|
1893
|
+
if (next) {
|
|
1894
|
+
yield next;
|
|
1895
|
+
continue;
|
|
1896
|
+
}
|
|
1897
|
+
if (finished) return;
|
|
1898
|
+
await new Promise((resolve) => wake = resolve);
|
|
1899
|
+
}
|
|
1900
|
+
} finally {
|
|
1901
|
+
source.removeEventListener(event, onData);
|
|
1902
|
+
source.removeEventListener(done, onDone);
|
|
1903
|
+
source.removeEventListener("error", onError);
|
|
1904
|
+
if (close) source.close();
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
function sleep(ms, signal) {
|
|
1908
|
+
return new Promise((resolve) => {
|
|
1909
|
+
if (signal?.aborted || ms <= 0) return resolve();
|
|
1910
|
+
const timer = setTimeout(done, ms);
|
|
1911
|
+
function done() {
|
|
1912
|
+
clearTimeout(timer);
|
|
1913
|
+
signal?.removeEventListener("abort", done);
|
|
1914
|
+
resolve();
|
|
1915
|
+
}
|
|
1916
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
1917
|
+
});
|
|
1918
|
+
}
|
|
1919
|
+
function scriptFrames(script) {
|
|
1920
|
+
const frames = [];
|
|
1921
|
+
for (const step of [...script].sort((a, b) => a.at - b.at)) {
|
|
1922
|
+
const last = frames[frames.length - 1];
|
|
1923
|
+
if (last?.at === step.at) last.patches.push(...toArray(step.patch));
|
|
1924
|
+
else frames.push({ at: step.at, patches: toArray(step.patch) });
|
|
1925
|
+
}
|
|
1926
|
+
return frames;
|
|
1927
|
+
}
|
|
1928
|
+
async function* playScript(script, { speed = 1, signal } = {}) {
|
|
1929
|
+
let clock = 0;
|
|
1930
|
+
for (const frame of scriptFrames(script)) {
|
|
1931
|
+
if (signal?.aborted) return;
|
|
1932
|
+
await sleep((frame.at - clock) / speed, signal);
|
|
1933
|
+
if (signal?.aborted) return;
|
|
1934
|
+
clock = frame.at;
|
|
1935
|
+
yield frame.patches;
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
var offsetScript = (script, ms) => script.map((s) => ({ ...s, at: s.at + ms }));
|
|
1939
|
+
var scriptLength = (script) => script.reduce((m, s) => Math.max(m, s.at), 0);
|
|
1940
|
+
function useStreamedSpec(spec, stream, apply, { onEnd, onError, stopPatches } = {}) {
|
|
1941
|
+
const [base, setBase] = React18.useState(spec);
|
|
1942
|
+
const [live, setLive] = React18.useState(spec);
|
|
1943
|
+
if (stream && base !== spec) {
|
|
1944
|
+
setBase(spec);
|
|
1945
|
+
setLive(spec);
|
|
1946
|
+
}
|
|
1947
|
+
const current = React18.useRef(spec);
|
|
1948
|
+
const abort = React18.useRef(null);
|
|
1949
|
+
const latest = React18.useRef({ apply, onEnd, onError, stopPatches });
|
|
1950
|
+
React18.useLayoutEffect(() => {
|
|
1951
|
+
latest.current = { apply, onEnd, onError, stopPatches };
|
|
1952
|
+
current.current = live;
|
|
1953
|
+
});
|
|
1954
|
+
const set = React18.useCallback((next) => {
|
|
1955
|
+
current.current = next;
|
|
1956
|
+
setLive(next);
|
|
1957
|
+
}, []);
|
|
1958
|
+
React18.useEffect(() => {
|
|
1959
|
+
if (!stream) return;
|
|
1960
|
+
const controller = new AbortController();
|
|
1961
|
+
abort.current = controller;
|
|
1962
|
+
void (async () => {
|
|
1963
|
+
try {
|
|
1964
|
+
const source = typeof stream === "function" ? stream(controller.signal) : stream;
|
|
1965
|
+
for await (const batch of patchesOf(source)) {
|
|
1966
|
+
if (controller.signal.aborted) return;
|
|
1967
|
+
set(latest.current.apply(current.current, batch));
|
|
1968
|
+
}
|
|
1969
|
+
if (!controller.signal.aborted) latest.current.onEnd?.(current.current);
|
|
1970
|
+
} catch (error) {
|
|
1971
|
+
if (!controller.signal.aborted) latest.current.onError?.(error);
|
|
1972
|
+
} finally {
|
|
1973
|
+
if (abort.current === controller) abort.current = null;
|
|
1974
|
+
}
|
|
1975
|
+
})();
|
|
1976
|
+
return () => controller.abort();
|
|
1977
|
+
}, [stream, set]);
|
|
1978
|
+
const stop = React18.useCallback(() => {
|
|
1979
|
+
if (!abort.current) return;
|
|
1980
|
+
abort.current.abort();
|
|
1981
|
+
abort.current = null;
|
|
1982
|
+
const { apply: apply2, stopPatches: stopPatches2 } = latest.current;
|
|
1983
|
+
if (stopPatches2) set(apply2(current.current, stopPatches2(current.current)));
|
|
1984
|
+
}, [set]);
|
|
1985
|
+
return { live: stream ? live : spec, stop };
|
|
1986
|
+
}
|
|
1987
|
+
function useBlockStream(spec, stream, options) {
|
|
1988
|
+
return useStreamedSpec(spec, stream, applyBlockPatches, options);
|
|
1989
|
+
}
|
|
1990
|
+
function Block({ spec, stream, onStreamEnd, onStreamError, ...props }) {
|
|
1991
|
+
const { live } = useBlockStream(spec, stream, { onEnd: onStreamEnd, onError: onStreamError });
|
|
1992
|
+
const Renderer = BLOCK_RENDERERS[live.kind];
|
|
1993
|
+
if (!Renderer) return /* @__PURE__ */ jsx(Placeholder, { kind: live.kind, options: live });
|
|
1994
|
+
return /* @__PURE__ */ jsx(Renderer, { spec: live, ...props });
|
|
1995
|
+
}
|
|
1996
|
+
var Page = React18.forwardRef(({ spec, onAction, className, ...props }, ref) => /* @__PURE__ */ jsxs("article", { ref, className: cn("flex min-w-0 flex-col gap-6", className), ...props, children: [
|
|
1997
|
+
spec.title || spec.description ? /* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-1", children: [
|
|
1998
|
+
spec.title ? /* @__PURE__ */ jsx(TypographyH3, { children: spec.title }) : null,
|
|
1999
|
+
spec.description ? /* @__PURE__ */ jsx(TypographyMuted, { children: spec.description }) : null
|
|
2000
|
+
] }) : null,
|
|
2001
|
+
spec.sections.map((section, s) => /* @__PURE__ */ jsxs("section", { className: "flex min-w-0 flex-col gap-3", children: [
|
|
2002
|
+
section.title ? /* @__PURE__ */ jsx(SectionHeader, { title: section.title }) : null,
|
|
2003
|
+
section.description ? /* @__PURE__ */ jsx(TypographyMuted, { children: section.description }) : null,
|
|
2004
|
+
section.blocks.map((block, b) => /* @__PURE__ */ jsx(
|
|
2005
|
+
Block,
|
|
2006
|
+
{
|
|
2007
|
+
spec: block,
|
|
2008
|
+
onAction: onAction ? (action, value) => onAction(action, value === void 0 ? { section: s, block: b } : { section: s, block: b, value }) : void 0
|
|
2009
|
+
},
|
|
2010
|
+
b
|
|
2011
|
+
))
|
|
2012
|
+
] }, s))
|
|
2013
|
+
] }));
|
|
2014
|
+
Page.displayName = "Page";
|
|
2015
|
+
|
|
2016
|
+
export { ANSWER_KINDS, ASK_KINDS, ActionRow, ActivityBlock, BADGE_TONE, BLOCKS, BLOCK_RENDERERS, BarsBlock, Block, BlockPatchError, CAPTION_TONE, CannotBlock, CaveatBlock, CitationsBlock, ConfirmAsk, ConfirmCard, FilesBlock, FormAsk, GanttBlock, GridBlock, HeatStripBlock, MethodBlock, MetricBlock, MultiAsk, MultistepAsk, NarrativeBlock, Page, Placeholder, ProposalBlock, QuickAsk, RankedBlock, RecordBlock, ScopeBlock, SingleAsk, SuggestionChips, SuggestionsAsk, TableBlock, TimelineBlock, TimeseriesBlock, TraceBlock, VALUE_TYPES, applyBlockPatch, applyBlockPatches, figureText, fromEventSource, fromNdjson, metricValue, offsetScript, patchesOf, playScript, prose, scriptFrames, scriptLength, strong, useBlockStream, useStreamedSpec };
|
|
2017
|
+
//# sourceMappingURL=index.js.map
|
|
2018
|
+
//# sourceMappingURL=index.js.map
|