@validation-os/dashboard 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  "use client";
2
2
 
3
+ // src/dashboard-app.tsx
4
+ import { useCallback as useCallback4, useEffect as useEffect5, useState as useState8 } from "react";
5
+
3
6
  // src/labels.ts
4
7
  var REGISTER_LABEL = {
5
8
  assumptions: "Assumptions",
@@ -28,44 +31,78 @@ var REGISTER_ORDER = [
28
31
  "glossary",
29
32
  "people"
30
33
  ];
34
+ var REGISTER_SUBTITLE = {
35
+ assumptions: "Falsifiable beliefs the plan rests on. Risk = Impact \xD7 (1 \u2212 max(0, Confidence)/100).",
36
+ experiments: "The tests that move Confidence in the beliefs.",
37
+ readings: "The evidence logged against experiments and beliefs.",
38
+ goals: "The outcomes the plan is steering toward.",
39
+ decisions: "The choices made, and what they rest on.",
40
+ glossary: "The shared vocabulary for this venture.",
41
+ people: "The people the plan touches."
42
+ };
43
+ var REGISTER_ICON = {
44
+ assumptions: "\u25CE",
45
+ experiments: "\u2697",
46
+ readings: "\u25A4",
47
+ goals: "\u25C7",
48
+ decisions: "\xA7",
49
+ glossary: "A",
50
+ people: "\u263A"
51
+ };
52
+ var REGISTER_GROUPS = [
53
+ {
54
+ label: "Registers",
55
+ registers: ["assumptions", "experiments", "readings", "goals", "decisions", "glossary"]
56
+ },
57
+ { label: "Reference", registers: ["people"] }
58
+ ];
31
59
 
32
- // src/register-counts.tsx
60
+ // src/register-browser.tsx
61
+ import { useState as useState6 } from "react";
62
+
63
+ // src/drawer-shell.tsx
64
+ import { useEffect, useRef } from "react";
33
65
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
34
- function RegisterCounts({ counts, caption, hrefFor }) {
35
- const registers = REGISTER_ORDER.filter(
36
- (r) => counts[r] !== void 0
37
- );
38
- return /* @__PURE__ */ jsxs("section", { "aria-label": "Register counts", children: [
39
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4", children: registers.map((register) => /* @__PURE__ */ jsx(
40
- StatTile,
41
- {
42
- label: REGISTER_LABEL[register],
43
- value: counts[register] ?? 0,
44
- href: hrefFor?.(register)
45
- },
46
- register
47
- )) }),
48
- caption ? /* @__PURE__ */ jsx("p", { className: "mt-4 text-sm text-neutral-500 dark:text-neutral-400", children: caption }) : null
49
- ] });
50
- }
51
- function StatTile({
52
- label,
53
- value,
54
- href
66
+ function DrawerShell({
67
+ open,
68
+ onClose,
69
+ ariaLabel,
70
+ children
55
71
  }) {
56
- const body = /* @__PURE__ */ jsxs(Fragment, { children: [
57
- /* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-neutral-500 dark:text-neutral-400", children: label }),
58
- /* @__PURE__ */ jsx("div", { className: "mt-1 text-3xl font-semibold tabular-nums text-neutral-900 dark:text-neutral-50", children: value.toLocaleString() })
72
+ const panelRef = useRef(null);
73
+ useEffect(() => {
74
+ if (!open) return;
75
+ const onKey = (e) => {
76
+ if (e.key === "Escape") onClose();
77
+ };
78
+ document.addEventListener("keydown", onKey);
79
+ panelRef.current?.focus();
80
+ return () => document.removeEventListener("keydown", onKey);
81
+ }, [open, onClose]);
82
+ if (!open) return null;
83
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
84
+ /* @__PURE__ */ jsx(
85
+ "button",
86
+ {
87
+ type: "button",
88
+ "aria-label": "Close",
89
+ onClick: onClose,
90
+ className: "vos-scrim"
91
+ }
92
+ ),
93
+ /* @__PURE__ */ jsx(
94
+ "aside",
95
+ {
96
+ ref: panelRef,
97
+ role: "dialog",
98
+ "aria-modal": "true",
99
+ "aria-label": ariaLabel,
100
+ tabIndex: -1,
101
+ className: "vos-drawer",
102
+ children
103
+ }
104
+ )
59
105
  ] });
60
- const className = "block rounded-xl border border-neutral-200 bg-white p-5 dark:border-neutral-800 dark:bg-neutral-900";
61
- return href ? /* @__PURE__ */ jsx(
62
- "a",
63
- {
64
- href,
65
- className: `${className} transition-colors hover:border-neutral-300 hover:bg-neutral-50 dark:hover:border-neutral-700 dark:hover:bg-neutral-800`,
66
- children: body
67
- }
68
- ) : /* @__PURE__ */ jsx("div", { className, children: body });
69
106
  }
70
107
 
71
108
  // src/columns.ts
@@ -74,13 +111,19 @@ function derivedField(field) {
74
111
  }
75
112
  var COLUMNS = {
76
113
  assumptions: [
77
- { key: "Title", header: "Assumption" },
114
+ { key: "Title", header: "Belief" },
115
+ {
116
+ key: "Status",
117
+ header: "Status",
118
+ kind: "status"
119
+ },
78
120
  { key: "Impact", header: "Impact", align: "right" },
79
121
  {
80
122
  key: "confidence",
81
123
  header: "Confidence",
82
124
  align: "right",
83
125
  derived: true,
126
+ kind: "confidence",
84
127
  accessor: derivedField("confidence")
85
128
  },
86
129
  {
@@ -88,13 +131,13 @@ var COLUMNS = {
88
131
  header: "Risk",
89
132
  align: "right",
90
133
  derived: true,
134
+ kind: "risk",
91
135
  accessor: derivedField("risk")
92
- },
93
- { key: "Status", header: "Status" }
136
+ }
94
137
  ],
95
138
  experiments: [
96
139
  { key: "Title", header: "Experiment" },
97
- { key: "Status", header: "Status" },
140
+ { key: "Status", header: "Status", kind: "status" },
98
141
  { key: "Feasibility", header: "Feasibility" }
99
142
  ],
100
143
  readings: [
@@ -111,16 +154,16 @@ var COLUMNS = {
111
154
  ],
112
155
  goals: [
113
156
  { key: "Title", header: "Goal" },
114
- { key: "Status", header: "Status" },
157
+ { key: "Status", header: "Status", kind: "status" },
115
158
  { key: "Outcome", header: "Outcome" }
116
159
  ],
117
160
  decisions: [
118
161
  { key: "Title", header: "Decision" },
119
- { key: "Status", header: "Status" }
162
+ { key: "Status", header: "Status", kind: "status" }
120
163
  ],
121
164
  glossary: [
122
165
  { key: "Title", header: "Term" },
123
- { key: "Status", header: "Status" }
166
+ { key: "Status", header: "Status", kind: "status" }
124
167
  ],
125
168
  people: [
126
169
  { key: "Name", header: "Name" },
@@ -186,8 +229,244 @@ function derivedLabel(key) {
186
229
  return DERIVED_LABEL[key] ?? fieldLabel(key);
187
230
  }
188
231
 
232
+ // src/primitives.ts
233
+ var GOOD_STATUS = /* @__PURE__ */ new Set([
234
+ "live",
235
+ "concluded",
236
+ "closed",
237
+ "done",
238
+ "resolved",
239
+ "accepted",
240
+ "adopted",
241
+ "shipped"
242
+ ]);
243
+ var WARN_STATUS = /* @__PURE__ */ new Set([
244
+ "testing",
245
+ "running",
246
+ "in progress",
247
+ "in-progress",
248
+ "draft"
249
+ ]);
250
+ var CRIT_STATUS = /* @__PURE__ */ new Set([
251
+ "invalidated",
252
+ "rejected",
253
+ "blocked",
254
+ "failed",
255
+ "at risk"
256
+ ]);
257
+ function statusTone(status) {
258
+ if (!status) return "neutral";
259
+ const s = status.trim().toLowerCase();
260
+ if (GOOD_STATUS.has(s)) return "good";
261
+ if (WARN_STATUS.has(s)) return "warn";
262
+ if (CRIT_STATUS.has(s)) return "crit";
263
+ return "neutral";
264
+ }
265
+ var RISK_CRIT = 60;
266
+ var RISK_WARN = 30;
267
+ function riskLevel(risk) {
268
+ if (risk >= RISK_CRIT) return "crit";
269
+ if (risk >= RISK_WARN) return "warn";
270
+ return "good";
271
+ }
272
+ function riskFraction(risk) {
273
+ if (!Number.isFinite(risk)) return 0;
274
+ return Math.max(0, Math.min(100, risk)) / 100;
275
+ }
276
+ function confidenceTone(confidence) {
277
+ return confidence < 0 ? "crit" : "good";
278
+ }
279
+ function derivedTone(field, value) {
280
+ if (field === "confidence") return confidenceTone(value);
281
+ if (field === "risk") return riskLevel(value);
282
+ return "neutral";
283
+ }
284
+ function heroToneClass(tone) {
285
+ if (tone === "crit") return "vos-text-crit";
286
+ if (tone === "warn") return "vos-text-warn";
287
+ return "";
288
+ }
289
+ function formatSigned(n) {
290
+ const r = Math.round(n);
291
+ return r > 0 ? `+${r}` : String(r);
292
+ }
293
+ function formatCount(n) {
294
+ return n.toLocaleString();
295
+ }
296
+ function sparklinePath(values, width, height, min, max) {
297
+ if (values.length < 2) return "";
298
+ const lo = min ?? Math.min(...values, 0);
299
+ const hi = max ?? Math.max(...values, 0);
300
+ const span = hi - lo || 1;
301
+ const inset = 2;
302
+ return values.map((v, i) => {
303
+ const x = i / (values.length - 1) * (width - inset * 2) + inset;
304
+ const y = height - inset - (v - lo) / span * (height - inset * 2);
305
+ return `${i ? "L" : "M"}${x.toFixed(1)} ${y.toFixed(1)}`;
306
+ }).join(" ");
307
+ }
308
+ function sparklineY(value, height, lo, hi) {
309
+ const span = hi - lo || 1;
310
+ const inset = 2;
311
+ return height - inset - (value - lo) / span * (height - inset * 2);
312
+ }
313
+
314
+ // src/primitives-view.tsx
315
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
316
+ var PILL_CLASS = {
317
+ good: "vos-pill vos-pill-good",
318
+ warn: "vos-pill vos-pill-warn",
319
+ crit: "vos-pill vos-pill-crit",
320
+ accent: "vos-pill vos-pill-accent",
321
+ neutral: "vos-pill vos-pill-neutral"
322
+ };
323
+ function StatusPill({ status }) {
324
+ if (!status) return /* @__PURE__ */ jsx2("span", { className: "vos-muted", children: "\u2014" });
325
+ return /* @__PURE__ */ jsx2("span", { className: PILL_CLASS[statusTone(status)], children: status });
326
+ }
327
+ var FILL_CLASS = {
328
+ good: "vos-fill-good",
329
+ warn: "vos-fill-warn",
330
+ crit: "vos-fill-crit",
331
+ accent: "vos-fill-good",
332
+ neutral: "vos-fill-good"
333
+ };
334
+ var TEXT_CLASS = {
335
+ good: "vos-text-good",
336
+ warn: "vos-text-warn",
337
+ crit: "vos-text-crit",
338
+ accent: "vos-text-good",
339
+ neutral: ""
340
+ };
341
+ function RiskBar({ risk }) {
342
+ const level = riskLevel(risk);
343
+ const pct = Math.round(riskFraction(risk) * 100);
344
+ return /* @__PURE__ */ jsxs2("span", { className: "vos-metric-cell", children: [
345
+ /* @__PURE__ */ jsx2(
346
+ "span",
347
+ {
348
+ className: "vos-risk-bar",
349
+ role: "img",
350
+ "aria-label": `Risk ${Math.round(risk)} of 100`,
351
+ children: /* @__PURE__ */ jsx2("i", { className: FILL_CLASS[level], style: { width: `${pct}%` } })
352
+ }
353
+ ),
354
+ /* @__PURE__ */ jsx2("b", { className: `vos-metric-num ${TEXT_CLASS[level]}`, children: Math.round(risk) })
355
+ ] });
356
+ }
357
+ function ConfidenceCell({
358
+ confidence,
359
+ history
360
+ }) {
361
+ const tone = confidenceTone(confidence);
362
+ return /* @__PURE__ */ jsxs2("span", { className: "vos-metric-cell", children: [
363
+ history && history.length >= 2 ? /* @__PURE__ */ jsx2(
364
+ Sparkline,
365
+ {
366
+ values: history,
367
+ width: 46,
368
+ height: 16,
369
+ min: -100,
370
+ max: 100,
371
+ tone
372
+ }
373
+ ) : null,
374
+ /* @__PURE__ */ jsx2("b", { className: `vos-metric-num ${TEXT_CLASS[tone]}`, children: formatSigned(confidence) })
375
+ ] });
376
+ }
377
+ var STROKE_VAR = {
378
+ good: "var(--vos-good)",
379
+ warn: "var(--vos-warn)",
380
+ crit: "var(--vos-crit)",
381
+ accent: "var(--vos-accent)",
382
+ neutral: "var(--vos-muted)"
383
+ };
384
+ function Sparkline({
385
+ values,
386
+ width = 240,
387
+ height = 44,
388
+ min,
389
+ max,
390
+ tone = "good",
391
+ fill = false,
392
+ ariaLabel
393
+ }) {
394
+ const d = sparklinePath(values, width, height, min, max);
395
+ if (!d) return null;
396
+ const lo = min ?? Math.min(...values, 0);
397
+ const hi = max ?? Math.max(...values, 0);
398
+ const stroke = STROKE_VAR[tone];
399
+ const last = values[values.length - 1];
400
+ const lastX = width - 2;
401
+ const lastY = sparklineY(last, height, lo, hi);
402
+ const zeroY = sparklineY(0, height, lo, hi);
403
+ const showBaseline = lo < 0 && hi > 0;
404
+ return /* @__PURE__ */ jsxs2(
405
+ "svg",
406
+ {
407
+ className: "vos-spark",
408
+ width,
409
+ height,
410
+ viewBox: `0 0 ${width} ${height}`,
411
+ preserveAspectRatio: "none",
412
+ role: "img",
413
+ "aria-label": ariaLabel ?? `Trend, now ${formatSigned(last)}`,
414
+ children: [
415
+ fill ? /* @__PURE__ */ jsx2(
416
+ "path",
417
+ {
418
+ d: `${d} L ${width - 2} ${height} L 2 ${height} Z`,
419
+ fill: stroke,
420
+ opacity: "0.13"
421
+ }
422
+ ) : null,
423
+ showBaseline ? /* @__PURE__ */ jsx2(
424
+ "line",
425
+ {
426
+ x1: 2,
427
+ x2: width - 2,
428
+ y1: zeroY,
429
+ y2: zeroY,
430
+ stroke: "var(--vos-border-strong)",
431
+ strokeWidth: 1,
432
+ strokeDasharray: "3 3"
433
+ }
434
+ ) : null,
435
+ /* @__PURE__ */ jsx2("path", { d, fill: "none", stroke, strokeWidth: 2 }),
436
+ /* @__PURE__ */ jsx2("circle", { cx: lastX, cy: lastY, r: 3, fill: stroke })
437
+ ]
438
+ }
439
+ );
440
+ }
441
+ function StatTile({
442
+ label,
443
+ value,
444
+ sub,
445
+ onClick,
446
+ active
447
+ }) {
448
+ const body = /* @__PURE__ */ jsxs2(Fragment2, { children: [
449
+ /* @__PURE__ */ jsx2("div", { className: "vos-tile-label", children: label }),
450
+ /* @__PURE__ */ jsx2("div", { className: "vos-tile-value", children: formatCount(value) }),
451
+ sub ? /* @__PURE__ */ jsx2("div", { className: "vos-tile-sub", children: sub }) : null
452
+ ] });
453
+ if (onClick) {
454
+ return /* @__PURE__ */ jsx2(
455
+ "button",
456
+ {
457
+ type: "button",
458
+ className: "vos-tile",
459
+ onClick,
460
+ "aria-pressed": active,
461
+ children: body
462
+ }
463
+ );
464
+ }
465
+ return /* @__PURE__ */ jsx2("div", { className: "vos-tile", children: body });
466
+ }
467
+
189
468
  // src/register-table.tsx
190
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
469
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
191
470
  function RegisterTable({
192
471
  register,
193
472
  records,
@@ -196,21 +475,21 @@ function RegisterTable({
196
475
  }) {
197
476
  const columns = columnsFor(register);
198
477
  if (records.length === 0) {
199
- return /* @__PURE__ */ jsx2("p", { className: "rounded-xl border border-dashed border-neutral-300 p-8 text-center text-sm text-neutral-500 dark:border-neutral-700 dark:text-neutral-400", children: "No records yet." });
478
+ return /* @__PURE__ */ jsx3("p", { className: "vos-empty", children: "No records yet." });
200
479
  }
201
- return /* @__PURE__ */ jsx2("div", { className: "overflow-x-auto rounded-xl border border-neutral-200 dark:border-neutral-800", children: /* @__PURE__ */ jsxs2("table", { className: "w-full border-collapse text-left text-sm", children: [
202
- /* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsx2("tr", { className: "border-b border-neutral-200 bg-neutral-50 dark:border-neutral-800 dark:bg-neutral-900", children: columns.map((c) => /* @__PURE__ */ jsx2(
480
+ return /* @__PURE__ */ jsx3("div", { className: "vos-card vos-table-scroll", children: /* @__PURE__ */ jsxs3("table", { className: "vos-table", children: [
481
+ /* @__PURE__ */ jsx3("thead", { children: /* @__PURE__ */ jsx3("tr", { children: columns.map((c) => /* @__PURE__ */ jsx3(
203
482
  "th",
204
483
  {
205
484
  scope: "col",
206
- className: `px-4 py-2.5 font-medium text-neutral-500 dark:text-neutral-400 ${c.align === "right" ? "text-right tabular-nums" : "text-left"}`,
485
+ className: c.align === "right" ? "vos-r" : void 0,
207
486
  children: c.header
208
487
  },
209
488
  c.key
210
489
  )) }) }),
211
- /* @__PURE__ */ jsx2("tbody", { children: records.map((record) => {
490
+ /* @__PURE__ */ jsx3("tbody", { children: records.map((record) => {
212
491
  const isSelected = record.id === selectedId;
213
- return /* @__PURE__ */ jsx2(
492
+ return /* @__PURE__ */ jsx3(
214
493
  "tr",
215
494
  {
216
495
  onClick: () => onRowClick?.(record.id),
@@ -222,75 +501,43 @@ function RegisterTable({
222
501
  } : void 0,
223
502
  tabIndex: onRowClick ? 0 : void 0,
224
503
  "aria-selected": isSelected,
225
- className: `border-b border-neutral-100 last:border-0 focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500 dark:border-neutral-900 ${onRowClick ? "cursor-pointer" : ""} ${isSelected ? "bg-blue-50 dark:bg-blue-950/40" : "hover:bg-neutral-50 dark:hover:bg-neutral-900/60"}`,
226
- children: columns.map((c, i) => {
227
- const raw = cellValue(c, record);
228
- const text = i === 0 && (raw === null || raw === void 0 || raw === "") ? primaryLabel(record) : formatValue(raw);
229
- return /* @__PURE__ */ jsx2(
230
- "td",
231
- {
232
- className: `px-4 py-2.5 ${c.align === "right" ? "text-right tabular-nums text-neutral-700 dark:text-neutral-300" : "text-left"} ${i === 0 ? "font-medium text-neutral-900 dark:text-neutral-100" : "text-neutral-600 dark:text-neutral-400"}`,
233
- children: text
234
- },
235
- c.key
236
- );
237
- })
504
+ className: isSelected ? "is-selected" : void 0,
505
+ children: columns.map((c, i) => /* @__PURE__ */ jsx3(
506
+ "td",
507
+ {
508
+ className: c.align === "right" ? "vos-r" : void 0,
509
+ children: /* @__PURE__ */ jsx3(Cell, { column: c, record, headline: i === 0 })
510
+ },
511
+ c.key
512
+ ))
238
513
  },
239
514
  record.id
240
515
  );
241
516
  }) })
242
517
  ] }) });
243
518
  }
519
+ function Cell({
520
+ column,
521
+ record,
522
+ headline
523
+ }) {
524
+ const raw = cellValue(column, record);
525
+ if (column.kind === "status") {
526
+ return /* @__PURE__ */ jsx3(StatusPill, { status: raw == null ? null : String(raw) });
527
+ }
528
+ if (column.kind === "risk") {
529
+ return typeof raw === "number" ? /* @__PURE__ */ jsx3(RiskBar, { risk: raw }) : /* @__PURE__ */ jsx3("span", { className: "vos-muted", children: "\u2014" });
530
+ }
531
+ if (column.kind === "confidence") {
532
+ return typeof raw === "number" ? /* @__PURE__ */ jsx3(ConfidenceCell, { confidence: raw }) : /* @__PURE__ */ jsx3("span", { className: "vos-muted", children: "\u2014" });
533
+ }
534
+ const text = headline && (raw === null || raw === void 0 || raw === "") ? primaryLabel(record) : formatValue(raw);
535
+ return /* @__PURE__ */ jsx3("span", { className: headline ? "vos-ttl" : void 0, children: text });
536
+ }
244
537
 
245
538
  // src/record-drawer.tsx
246
539
  import { useEffect as useEffect3, useState as useState2 } from "react";
247
540
 
248
- // src/drawer-shell.tsx
249
- import { useEffect, useRef } from "react";
250
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
251
- function DrawerShell({
252
- open,
253
- onClose,
254
- ariaLabel,
255
- scroll = true,
256
- children
257
- }) {
258
- const panelRef = useRef(null);
259
- useEffect(() => {
260
- if (!open) return;
261
- const onKey = (e) => {
262
- if (e.key === "Escape") onClose();
263
- };
264
- document.addEventListener("keydown", onKey);
265
- panelRef.current?.focus();
266
- return () => document.removeEventListener("keydown", onKey);
267
- }, [open, onClose]);
268
- if (!open) return null;
269
- return /* @__PURE__ */ jsxs3("div", { className: "fixed inset-0 z-50 flex justify-end", children: [
270
- /* @__PURE__ */ jsx3(
271
- "button",
272
- {
273
- type: "button",
274
- "aria-label": "Close",
275
- onClick: onClose,
276
- className: "absolute inset-0 bg-neutral-950/30 backdrop-blur-sm"
277
- }
278
- ),
279
- /* @__PURE__ */ jsx3(
280
- "div",
281
- {
282
- ref: panelRef,
283
- role: "dialog",
284
- "aria-modal": "true",
285
- "aria-label": ariaLabel,
286
- tabIndex: -1,
287
- className: `relative flex h-full w-full max-w-md flex-col border-l border-neutral-200 bg-white shadow-xl outline-none dark:border-neutral-800 dark:bg-neutral-950 ${scroll ? "overflow-y-auto" : "overflow-hidden"}`,
288
- children
289
- }
290
- )
291
- ] });
292
- }
293
-
294
541
  // src/edit.ts
295
542
  var CONFLICT_MESSAGE = "Someone edited this while you had it open \u2014 your changes are safe, take a look before saving again.";
296
543
  var t = (key, label) => ({ key, label, kind: "text" });
@@ -573,7 +820,7 @@ function buildUnderstanding(assumption, readings, experiments) {
573
820
  }
574
821
 
575
822
  // src/understanding-panel.tsx
576
- import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
823
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
577
824
  function UnderstandingPanel({
578
825
  assumption,
579
826
  basePath
@@ -599,135 +846,91 @@ function UnderstandingPanel({
599
846
  ...u.otherMovers.map((m) => m.magnitude),
600
847
  0.01
601
848
  );
602
- return /* @__PURE__ */ jsxs4("div", { className: "mt-2 space-y-4 rounded-lg bg-neutral-50 p-3 dark:bg-neutral-900", children: [
849
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
603
850
  u.experiments.length ? /* @__PURE__ */ jsxs4("section", { children: [
604
- /* @__PURE__ */ jsx4(Heading, { children: "What's moving Confidence" }),
605
- /* @__PURE__ */ jsx4("ul", { className: "space-y-2.5", children: u.experiments.map((e) => /* @__PURE__ */ jsxs4("li", { children: [
606
- /* @__PURE__ */ jsx4(
607
- PushRow,
608
- {
609
- label: e.title ?? `Experiment ${e.experimentId}`,
610
- contribution: e.contribution,
611
- magnitude: e.magnitude,
612
- max: maxMagnitude
613
- }
614
- ),
615
- /* @__PURE__ */ jsx4("p", { className: "mt-1 text-xs text-neutral-500 dark:text-neutral-400", children: experimentDetail(e) })
616
- ] }, e.experimentId)) })
851
+ /* @__PURE__ */ jsx4("div", { className: "vos-why-section-title", children: "What's moving Confidence" }),
852
+ u.experiments.map((e) => /* @__PURE__ */ jsx4(
853
+ PushRow,
854
+ {
855
+ label: e.title ?? `Experiment ${e.experimentId}`,
856
+ note: experimentDetail(e),
857
+ contribution: e.contribution,
858
+ magnitude: e.magnitude,
859
+ max: maxMagnitude,
860
+ done: e.done
861
+ },
862
+ e.experimentId
863
+ ))
617
864
  ] }) : null,
618
865
  u.otherMovers.length ? /* @__PURE__ */ jsxs4("section", { children: [
619
- /* @__PURE__ */ jsx4(Heading, { children: "Other evidence" }),
620
- /* @__PURE__ */ jsx4("ul", { className: "space-y-2.5", children: u.otherMovers.map((m) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsx4(
866
+ /* @__PURE__ */ jsx4("div", { className: "vos-why-section-title", children: "Other evidence" }),
867
+ u.otherMovers.map((m) => /* @__PURE__ */ jsx4(
621
868
  PushRow,
622
869
  {
623
870
  label: otherMoverLabel(m),
624
871
  contribution: m.contribution,
625
872
  magnitude: m.magnitude,
626
873
  max: maxMagnitude
627
- }
628
- ) }, m.key)) })
874
+ },
875
+ m.key
876
+ ))
629
877
  ] }) : null,
630
- /* @__PURE__ */ jsxs4("section", { children: [
631
- /* @__PURE__ */ jsx4(Heading, { children: "Confidence over time" }),
632
- /* @__PURE__ */ jsx4(Trajectory, { points: u.trajectory })
633
- ] })
878
+ /* @__PURE__ */ jsx4(Trajectory, { points: u.trajectory })
634
879
  ] });
635
880
  }
636
881
  function PushRow({
637
882
  label,
883
+ note,
638
884
  contribution,
639
885
  magnitude,
640
- max
886
+ max,
887
+ done
641
888
  }) {
642
889
  const up = contribution >= 0;
643
890
  const moving = magnitude > 0;
644
- return /* @__PURE__ */ jsxs4(Fragment2, { children: [
645
- /* @__PURE__ */ jsxs4("div", { className: "flex items-baseline justify-between gap-2", children: [
646
- /* @__PURE__ */ jsx4("span", { className: "truncate text-sm text-neutral-800 dark:text-neutral-200", children: label }),
647
- moving ? /* @__PURE__ */ jsxs4(
648
- "span",
649
- {
650
- className: `shrink-0 text-sm font-semibold tabular-nums ${up ? "text-emerald-600 dark:text-emerald-400" : "text-rose-600 dark:text-rose-400"}`,
651
- children: [
652
- up ? "+" : "",
653
- contribution
654
- ]
655
- }
656
- ) : /* @__PURE__ */ jsx4("span", { className: "shrink-0 text-xs text-neutral-400", children: "no readings yet" })
657
- ] }),
658
- /* @__PURE__ */ jsx4("div", { className: "mt-1 h-1.5 rounded-full bg-neutral-200 dark:bg-neutral-800", children: /* @__PURE__ */ jsx4(
659
- "div",
891
+ const width = Math.round(magnitude / max * 50);
892
+ const fill = up ? { left: "50%", width: `${width}%`, background: "var(--vos-good)" } : { right: "50%", width: `${width}%`, background: "var(--vos-crit)" };
893
+ return /* @__PURE__ */ jsxs4("div", { className: "vos-mover", children: [
894
+ /* @__PURE__ */ jsx4("span", { className: "vos-mover-name", children: label }),
895
+ note ? /* @__PURE__ */ jsx4("span", { className: `vos-mover-note ${done ? "vos-text-good" : "vos-text-warn"}`, children: note }) : null,
896
+ /* @__PURE__ */ jsx4("span", { className: "vos-track vos-signed", children: moving ? /* @__PURE__ */ jsx4("i", { style: fill }) : null }),
897
+ /* @__PURE__ */ jsx4(
898
+ "span",
660
899
  {
661
- className: `h-full rounded-full ${up ? "bg-emerald-500" : "bg-rose-500"}`,
662
- style: { width: `${magnitude / max * 100}%` }
900
+ className: "vos-mover-val",
901
+ style: { color: up ? "var(--vos-good)" : "var(--vos-crit)" },
902
+ children: moving ? formatSigned(contribution) : "\u2014"
663
903
  }
664
- ) })
904
+ )
665
905
  ] });
666
906
  }
667
907
  function Trajectory({ points }) {
668
908
  if (points.length < 2) {
669
- return /* @__PURE__ */ jsx4("p", { className: "text-xs text-neutral-500 dark:text-neutral-400", children: points.length === 1 ? `One dated reading so far \u2014 Confidence ${points[0].confidence} on ${points[0].date}.` : "No dated readings yet to chart a trajectory." });
909
+ return /* @__PURE__ */ jsx4("p", { className: "vos-hint", children: points.length === 1 ? `One dated reading so far \u2014 Confidence ${formatSigned(points[0].confidence)} on ${points[0].date}.` : "No dated readings yet to chart a trajectory." });
670
910
  }
671
- return /* @__PURE__ */ jsx4(Sparkline, { points });
672
- }
673
- var SPARK_W = 260;
674
- var SPARK_H = 44;
675
- function Sparkline({ points }) {
676
- const n = points.length;
677
- const x = (i) => i / (n - 1) * SPARK_W;
678
- const y = (c) => (100 - c) / 200 * SPARK_H;
679
- const path = points.map((p, i) => `${x(i)},${y(p.confidence)}`).join(" ");
680
911
  const first = points[0];
681
- const last = points[n - 1];
682
- return /* @__PURE__ */ jsxs4("div", { children: [
683
- /* @__PURE__ */ jsxs4(
684
- "svg",
912
+ const last = points[points.length - 1];
913
+ const values = points.map((p) => p.confidence);
914
+ return /* @__PURE__ */ jsxs4("div", { className: "vos-traj", children: [
915
+ /* @__PURE__ */ jsx4("div", { className: "vos-traj-head", children: /* @__PURE__ */ jsx4("span", { className: "vos-lbl", children: "Confidence over time" }) }),
916
+ /* @__PURE__ */ jsx4(
917
+ Sparkline,
685
918
  {
686
- viewBox: `0 0 ${SPARK_W} ${SPARK_H}`,
687
- className: "w-full",
688
- role: "img",
689
- "aria-label": `Confidence moved from ${first.confidence} to ${last.confidence}`,
690
- children: [
691
- /* @__PURE__ */ jsx4(
692
- "line",
693
- {
694
- x1: 0,
695
- x2: SPARK_W,
696
- y1: y(0),
697
- y2: y(0),
698
- className: "stroke-neutral-300 dark:stroke-neutral-700",
699
- strokeWidth: 1,
700
- strokeDasharray: "3 3"
701
- }
702
- ),
703
- /* @__PURE__ */ jsx4(
704
- "polyline",
705
- {
706
- points: path,
707
- fill: "none",
708
- className: "stroke-neutral-700 dark:stroke-neutral-200",
709
- strokeWidth: 1.5,
710
- strokeLinejoin: "round",
711
- strokeLinecap: "round"
712
- }
713
- ),
714
- /* @__PURE__ */ jsx4(
715
- "circle",
716
- {
717
- cx: x(n - 1),
718
- cy: y(last.confidence),
719
- r: 2.5,
720
- className: "fill-neutral-900 dark:fill-neutral-50"
721
- }
722
- )
723
- ]
919
+ values,
920
+ width: 260,
921
+ height: 44,
922
+ min: -100,
923
+ max: 100,
924
+ tone: confidenceTone(last.confidence),
925
+ fill: true,
926
+ ariaLabel: `Confidence moved from ${formatSigned(first.confidence)} to ${formatSigned(last.confidence)}`
724
927
  }
725
928
  ),
726
- /* @__PURE__ */ jsxs4("div", { className: "mt-1 flex justify-between text-[11px] text-neutral-400", children: [
929
+ /* @__PURE__ */ jsxs4("div", { className: "vos-traj-foot", children: [
727
930
  /* @__PURE__ */ jsx4("span", { children: first.date }),
728
- /* @__PURE__ */ jsxs4("span", { className: "tabular-nums text-neutral-600 dark:text-neutral-300", children: [
931
+ /* @__PURE__ */ jsxs4("span", { className: "vos-num", children: [
729
932
  "now ",
730
- last.confidence
933
+ formatSigned(last.confidence)
731
934
  ] })
732
935
  ] })
733
936
  ] });
@@ -745,16 +948,21 @@ function otherMoverLabel(m) {
745
948
  if (m.kind === "goal") return `Goal ${m.goalId ?? ""}`.trim();
746
949
  return m.readingCount === 1 ? "A direct reading" : "Direct readings";
747
950
  }
748
- function Heading({ children }) {
749
- return /* @__PURE__ */ jsx4("h4", { className: "mb-2 text-xs font-medium uppercase tracking-wide text-neutral-400", children });
750
- }
751
951
  function Muted({ children }) {
752
- return /* @__PURE__ */ jsx4("p", { className: "mt-2 rounded-lg bg-neutral-50 p-3 text-xs leading-relaxed text-neutral-600 dark:bg-neutral-900 dark:text-neutral-300", children });
952
+ return /* @__PURE__ */ jsx4("p", { className: "vos-hint", children });
753
953
  }
754
954
 
755
955
  // src/record-drawer.tsx
756
- import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
956
+ import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
757
957
  var META_FIELDS = /* @__PURE__ */ new Set(["id", "version", "createdAt", "updatedAt", "derived"]);
958
+ var DERIVED_SUB = {
959
+ confidence: "Signed average of concluded readings",
960
+ risk: "Impact \xD7 (1 \u2212 Confidence\u207A/100)",
961
+ derivedImpact: "Impact re-weighted by links",
962
+ impact: "Impact re-weighted by links",
963
+ strength: "of the evidence base",
964
+ sourceQuality: "of the source"
965
+ };
758
966
  function RecordDrawer({
759
967
  register,
760
968
  record,
@@ -820,65 +1028,58 @@ function RecordDrawer({
820
1028
  onClose,
821
1029
  ariaLabel: `${REGISTER_LABEL[register]} record`,
822
1030
  children: [
823
- /* @__PURE__ */ jsxs5("header", { className: "flex items-start justify-between gap-4 border-b border-neutral-200 p-5 dark:border-neutral-800", children: [
824
- /* @__PURE__ */ jsxs5("div", { children: [
825
- /* @__PURE__ */ jsx5("p", { className: "text-xs font-medium uppercase tracking-wide text-neutral-400", children: REGISTER_LABEL[register] }),
826
- /* @__PURE__ */ jsx5("h2", { className: "mt-1 text-lg font-semibold text-neutral-900 dark:text-neutral-50", children: record ? primaryLabel(record) : loading ? "Loading\u2026" : "\u2014" })
1031
+ /* @__PURE__ */ jsxs5("header", { className: "vos-drawer-header", children: [
1032
+ /* @__PURE__ */ jsxs5("div", { style: { flex: 1, minWidth: 0 }, children: [
1033
+ /* @__PURE__ */ jsx5("p", { className: "vos-drawer-eyebrow", children: REGISTER_LABEL[register] }),
1034
+ /* @__PURE__ */ jsx5("h2", { className: "vos-drawer-title", children: record ? primaryLabel(record) : loading ? "Loading\u2026" : "\u2014" })
827
1035
  ] }),
828
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
829
- record && !editing ? /* @__PURE__ */ jsx5(
830
- "button",
831
- {
832
- type: "button",
833
- onClick: startEditing,
834
- className: "rounded-md border border-neutral-300 px-2.5 py-1 text-sm font-medium text-neutral-700 hover:bg-neutral-100 dark:border-neutral-700 dark:text-neutral-200 dark:hover:bg-neutral-900",
835
- children: "Edit"
836
- }
837
- ) : null,
838
- /* @__PURE__ */ jsx5(
839
- "button",
840
- {
841
- type: "button",
842
- onClick: onClose,
843
- className: "rounded-md px-2 py-1 text-sm text-neutral-500 hover:bg-neutral-100 dark:hover:bg-neutral-900",
844
- children: "Close"
845
- }
846
- )
847
- ] })
848
- ] }),
849
- /* @__PURE__ */ jsx5("div", { className: "flex-1 p-5", children: loading ? /* @__PURE__ */ jsx5("p", { className: "text-sm text-neutral-500", children: "Loading record\u2026" }) : error ? /* @__PURE__ */ jsx5("p", { className: "text-sm text-red-600 dark:text-red-400", children: error }) : !record ? /* @__PURE__ */ jsx5("p", { className: "text-sm text-neutral-500", children: "No record." }) : /* @__PURE__ */ jsxs5(Fragment3, { children: [
850
- derived ? /* @__PURE__ */ jsxs5("section", { className: "mb-6", children: [
851
- /* @__PURE__ */ jsx5("h3", { className: "mb-2 text-xs font-medium uppercase tracking-wide text-neutral-400", children: "Computed \xB7 not editable" }),
852
- /* @__PURE__ */ jsx5("dl", { className: "grid grid-cols-3 gap-3", children: Object.entries(derived).map(([key, value]) => /* @__PURE__ */ jsxs5(
853
- "div",
854
- {
855
- className: "rounded-lg border border-neutral-200 bg-neutral-50 p-3 dark:border-neutral-800 dark:bg-neutral-900",
856
- children: [
857
- /* @__PURE__ */ jsx5("dt", { className: "text-xs text-neutral-500 dark:text-neutral-400", children: derivedLabel(key) }),
858
- /* @__PURE__ */ jsx5("dd", { className: "mt-1 text-xl font-semibold tabular-nums text-neutral-900 dark:text-neutral-50", children: formatValue(value) })
859
- ]
860
- },
861
- key
862
- )) }),
863
- "confidence" in derived ? /* @__PURE__ */ jsx5(
864
- WhyReveal,
865
- {
866
- open: why,
867
- onToggle: () => setWhy((w) => !w),
868
- assumption: record,
869
- basePath
870
- }
871
- ) : null
1036
+ record ? /* @__PURE__ */ jsxs5("span", { className: "vos-verbadge", children: [
1037
+ "v",
1038
+ formatValue(record.version)
872
1039
  ] }) : null,
873
- conflict ? /* @__PURE__ */ jsx5(ConflictBanner, { message: conflict, onReload: reloadLatest }) : null,
874
- saveError ? /* @__PURE__ */ jsx5(
875
- "p",
1040
+ record && !editing ? /* @__PURE__ */ jsx5(
1041
+ "button",
876
1042
  {
877
- role: "alert",
878
- className: "mb-4 rounded-lg border border-red-200 bg-red-50 p-3 text-sm text-red-700 dark:border-red-900 dark:bg-red-950/40 dark:text-red-300",
879
- children: saveError
1043
+ type: "button",
1044
+ onClick: startEditing,
1045
+ className: "vos-btn vos-btn-ghost vos-btn-sm",
1046
+ children: "Edit"
880
1047
  }
881
1048
  ) : null,
1049
+ /* @__PURE__ */ jsx5(
1050
+ "button",
1051
+ {
1052
+ type: "button",
1053
+ onClick: onClose,
1054
+ className: "vos-iconbtn",
1055
+ "aria-label": "Close",
1056
+ children: "\u2715"
1057
+ }
1058
+ )
1059
+ ] }),
1060
+ /* @__PURE__ */ jsx5("div", { className: "vos-drawer-body", children: loading ? /* @__PURE__ */ jsx5("p", { className: "vos-muted", children: "Loading record\u2026" }) : error ? /* @__PURE__ */ jsx5("p", { className: "vos-error", children: error }) : !record ? /* @__PURE__ */ jsx5("p", { className: "vos-muted", children: "No record." }) : /* @__PURE__ */ jsxs5(Fragment4, { children: [
1061
+ derived ? /* @__PURE__ */ jsxs5("div", { children: [
1062
+ /* @__PURE__ */ jsxs5("div", { className: "vos-derived", children: [
1063
+ /* @__PURE__ */ jsxs5("div", { className: "vos-derived-head", children: [
1064
+ "Derived",
1065
+ /* @__PURE__ */ jsx5("span", { className: "vos-lock", children: "\u{1F512} computed on write \u2014 not editable" })
1066
+ ] }),
1067
+ /* @__PURE__ */ jsx5("div", { className: "vos-dgrid", children: Object.entries(derived).map(([key, value]) => /* @__PURE__ */ jsx5(
1068
+ DerivedCell,
1069
+ {
1070
+ field: key,
1071
+ value,
1072
+ showWhy: key === "confidence",
1073
+ whyOpen: why,
1074
+ onWhy: () => setWhy((w) => !w)
1075
+ },
1076
+ key
1077
+ )) })
1078
+ ] }),
1079
+ "confidence" in derived && why ? /* @__PURE__ */ jsx5("div", { className: "vos-why-panel", children: /* @__PURE__ */ jsx5(UnderstandingPanel, { assumption: record, basePath }) }) : null
1080
+ ] }) : null,
1081
+ conflict ? /* @__PURE__ */ jsx5(ConflictBanner, { message: conflict, onReload: reloadLatest }) : null,
1082
+ saveError ? /* @__PURE__ */ jsx5("p", { role: "alert", className: "vos-banner vos-banner-crit", children: saveError }) : null,
882
1083
  editing ? /* @__PURE__ */ jsx5(
883
1084
  EditFields,
884
1085
  {
@@ -886,20 +1087,20 @@ function RecordDrawer({
886
1087
  draft,
887
1088
  onField: setField
888
1089
  }
889
- ) : /* @__PURE__ */ jsx5("dl", { className: "divide-y divide-neutral-100 dark:divide-neutral-900", children: fields.map((key) => /* @__PURE__ */ jsxs5("div", { className: "grid grid-cols-3 gap-3 py-2.5", children: [
890
- /* @__PURE__ */ jsx5("dt", { className: "text-sm text-neutral-500 dark:text-neutral-400", children: fieldLabel(key) }),
891
- /* @__PURE__ */ jsx5("dd", { className: "col-span-2 text-sm text-neutral-800 dark:text-neutral-200", children: formatValue(record[key]) })
1090
+ ) : /* @__PURE__ */ jsx5("div", { className: "vos-detail-list", children: fields.map((key) => /* @__PURE__ */ jsxs5("div", { className: "vos-detail-row", children: [
1091
+ /* @__PURE__ */ jsx5("span", { className: "vos-detail-k", children: fieldLabel(key) }),
1092
+ /* @__PURE__ */ jsx5("span", { className: "vos-detail-v", children: formatValue(record[key]) })
892
1093
  ] }, key)) })
893
1094
  ] }) }),
894
1095
  record && !loading && !error && !editing ? children : null,
895
- record && editing ? /* @__PURE__ */ jsxs5("footer", { className: "flex items-center justify-end gap-2 border-t border-neutral-200 p-5 dark:border-neutral-800", children: [
1096
+ record && editing ? /* @__PURE__ */ jsxs5("footer", { className: "vos-drawer-footer", children: [
896
1097
  /* @__PURE__ */ jsx5(
897
1098
  "button",
898
1099
  {
899
1100
  type: "button",
900
1101
  onClick: cancelEditing,
901
1102
  disabled: saving,
902
- className: "rounded-md px-3 py-1.5 text-sm text-neutral-600 hover:bg-neutral-100 disabled:opacity-50 dark:text-neutral-300 dark:hover:bg-neutral-900",
1103
+ className: "vos-btn vos-btn-ghost vos-btn-sm",
903
1104
  children: "Cancel"
904
1105
  }
905
1106
  ),
@@ -909,11 +1110,11 @@ function RecordDrawer({
909
1110
  type: "button",
910
1111
  onClick: onSave,
911
1112
  disabled: saving,
912
- className: "rounded-md bg-neutral-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-neutral-700 disabled:opacity-50 dark:bg-neutral-100 dark:text-neutral-900 dark:hover:bg-neutral-300",
1113
+ className: "vos-btn vos-btn-sm",
913
1114
  children: saving ? "Saving\u2026" : "Save"
914
1115
  }
915
1116
  )
916
- ] }) : record ? /* @__PURE__ */ jsxs5("footer", { className: "border-t border-neutral-200 p-5 text-xs text-neutral-400 dark:border-neutral-800", children: [
1117
+ ] }) : record ? /* @__PURE__ */ jsxs5("footer", { className: "vos-drawer-footer", children: [
917
1118
  record.id,
918
1119
  " \xB7 updated ",
919
1120
  formatValue(record.updatedAt)
@@ -922,56 +1123,56 @@ function RecordDrawer({
922
1123
  }
923
1124
  );
924
1125
  }
925
- function WhyReveal({
926
- open,
927
- onToggle,
928
- assumption,
929
- basePath
1126
+ function DerivedCell({
1127
+ field,
1128
+ value,
1129
+ showWhy,
1130
+ whyOpen,
1131
+ onWhy
930
1132
  }) {
931
- return /* @__PURE__ */ jsxs5("div", { className: "mt-3", children: [
932
- /* @__PURE__ */ jsx5(
933
- "button",
934
- {
935
- type: "button",
936
- onClick: onToggle,
937
- "aria-expanded": open,
938
- className: "text-xs font-medium text-neutral-500 underline underline-offset-2 hover:text-neutral-800 dark:text-neutral-400 dark:hover:text-neutral-100",
939
- children: "Why?"
940
- }
941
- ),
942
- open ? /* @__PURE__ */ jsx5(UnderstandingPanel, { assumption, basePath }) : null
1133
+ const num2 = typeof value === "number" ? value : null;
1134
+ let toneClass = "";
1135
+ let display = formatValue(value);
1136
+ if (num2 !== null) {
1137
+ toneClass = heroToneClass(derivedTone(field, num2));
1138
+ display = field === "confidence" ? formatSigned(num2) : String(Math.round(num2));
1139
+ }
1140
+ return /* @__PURE__ */ jsxs5("div", { className: "vos-dcell", children: [
1141
+ /* @__PURE__ */ jsxs5("div", { className: "vos-dcell-k", children: [
1142
+ derivedLabel(field),
1143
+ showWhy ? /* @__PURE__ */ jsxs5(
1144
+ "button",
1145
+ {
1146
+ type: "button",
1147
+ className: "vos-why",
1148
+ onClick: onWhy,
1149
+ "aria-expanded": whyOpen,
1150
+ children: [
1151
+ "Why? ",
1152
+ whyOpen ? "\u25B4" : "\u25BE"
1153
+ ]
1154
+ }
1155
+ ) : null
1156
+ ] }),
1157
+ /* @__PURE__ */ jsx5("div", { className: `vos-dcell-v ${toneClass}`, children: display }),
1158
+ DERIVED_SUB[field] ? /* @__PURE__ */ jsx5("div", { className: "vos-dcell-sub", children: DERIVED_SUB[field] }) : null
943
1159
  ] });
944
1160
  }
945
1161
  function ConflictBanner({
946
1162
  message,
947
1163
  onReload
948
1164
  }) {
949
- return /* @__PURE__ */ jsxs5(
950
- "div",
951
- {
952
- role: "alert",
953
- className: "mb-4 rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-900 dark:bg-amber-950/40",
954
- children: [
955
- /* @__PURE__ */ jsx5("p", { className: "text-sm text-amber-800 dark:text-amber-200", children: message }),
956
- /* @__PURE__ */ jsx5(
957
- "button",
958
- {
959
- type: "button",
960
- onClick: onReload,
961
- className: "mt-2 rounded-md border border-amber-300 px-2.5 py-1 text-xs font-medium text-amber-900 hover:bg-amber-100 dark:border-amber-800 dark:text-amber-100 dark:hover:bg-amber-900/40",
962
- children: "Reload the latest"
963
- }
964
- )
965
- ]
966
- }
967
- );
1165
+ return /* @__PURE__ */ jsxs5("div", { role: "alert", className: "vos-banner vos-banner-warn", children: [
1166
+ /* @__PURE__ */ jsx5("div", { className: "vos-banner-body", children: /* @__PURE__ */ jsx5("span", { children: message }) }),
1167
+ /* @__PURE__ */ jsx5("button", { type: "button", onClick: onReload, children: "Review the latest" })
1168
+ ] });
968
1169
  }
969
1170
  function EditFields({
970
1171
  register,
971
1172
  draft,
972
1173
  onField
973
1174
  }) {
974
- return /* @__PURE__ */ jsx5("div", { className: "space-y-4", children: editableFields(register).map((field) => /* @__PURE__ */ jsx5(
1175
+ return /* @__PURE__ */ jsx5("div", { className: "vos-field-stack", children: editableFields(register).map((field) => /* @__PURE__ */ jsx5(
975
1176
  FieldInput,
976
1177
  {
977
1178
  field,
@@ -981,22 +1182,14 @@ function EditFields({
981
1182
  field.key
982
1183
  )) });
983
1184
  }
984
- var INPUT_CLASS = "w-full rounded-md border border-neutral-300 bg-white px-2.5 py-1.5 text-sm text-neutral-900 focus:border-neutral-500 focus:outline-none dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-50";
985
1185
  function FieldInput({
986
1186
  field,
987
1187
  value,
988
1188
  onChange
989
1189
  }) {
990
1190
  const id = `field-${field.key.replace(/\s+/g, "-")}`;
991
- return /* @__PURE__ */ jsxs5("div", { children: [
992
- /* @__PURE__ */ jsx5(
993
- "label",
994
- {
995
- htmlFor: id,
996
- className: "mb-1 block text-xs font-medium text-neutral-500 dark:text-neutral-400",
997
- children: field.label
998
- }
999
- ),
1191
+ return /* @__PURE__ */ jsxs5("div", { className: "vos-field", children: [
1192
+ /* @__PURE__ */ jsx5("label", { htmlFor: id, children: field.label }),
1000
1193
  field.kind === "textarea" ? /* @__PURE__ */ jsx5(
1001
1194
  "textarea",
1002
1195
  {
@@ -1004,7 +1197,7 @@ function FieldInput({
1004
1197
  value: String(value ?? ""),
1005
1198
  onChange: (e) => onChange(e.target.value),
1006
1199
  rows: 3,
1007
- className: INPUT_CLASS
1200
+ className: "vos-input"
1008
1201
  }
1009
1202
  ) : field.kind === "select" ? /* @__PURE__ */ jsxs5(
1010
1203
  "select",
@@ -1012,7 +1205,7 @@ function FieldInput({
1012
1205
  id,
1013
1206
  value: String(value ?? ""),
1014
1207
  onChange: (e) => onChange(e.target.value),
1015
- className: INPUT_CLASS,
1208
+ className: "vos-input",
1016
1209
  children: [
1017
1210
  field.nullable ? /* @__PURE__ */ jsx5("option", { value: "", children: "\u2014" }) : null,
1018
1211
  field.options?.map((opt) => /* @__PURE__ */ jsx5("option", { value: opt, children: opt }, opt))
@@ -1025,7 +1218,7 @@ function FieldInput({
1025
1218
  type: field.kind === "number" ? "number" : "text",
1026
1219
  value: String(value ?? ""),
1027
1220
  onChange: (e) => onChange(e.target.value),
1028
- className: INPUT_CLASS
1221
+ className: "vos-input"
1029
1222
  }
1030
1223
  )
1031
1224
  ] });
@@ -1153,8 +1346,8 @@ function toCreatePayload(register, draft) {
1153
1346
  }
1154
1347
 
1155
1348
  // src/field-styles.ts
1156
- var FIELD_LABEL_CLASS = "block text-xs font-medium uppercase tracking-wide text-neutral-500 dark:text-neutral-400";
1157
- var FIELD_CONTROL_CLASS = "mt-1 w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm text-neutral-900 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100";
1349
+ var FIELD_LABEL_CLASS = "vos-field-label";
1350
+ var FIELD_CONTROL_CLASS = "vos-input";
1158
1351
 
1159
1352
  // src/use-mutations.ts
1160
1353
  import { useCallback as useCallback2, useState as useState3 } from "react";
@@ -1238,8 +1431,8 @@ function RecordForm({
1238
1431
  } catch {
1239
1432
  }
1240
1433
  };
1241
- return /* @__PURE__ */ jsxs6("form", { onSubmit, className: "flex h-full flex-col", children: [
1242
- /* @__PURE__ */ jsxs6("div", { className: "flex-1 space-y-4 overflow-y-auto p-5", children: [
1434
+ return /* @__PURE__ */ jsxs6("form", { onSubmit, className: "vos-form", children: [
1435
+ /* @__PURE__ */ jsxs6("div", { className: "vos-form-body", children: [
1243
1436
  fields.map((field) => /* @__PURE__ */ jsx6(
1244
1437
  Field,
1245
1438
  {
@@ -1249,25 +1442,17 @@ function RecordForm({
1249
1442
  },
1250
1443
  field.key
1251
1444
  )),
1252
- error ? /* @__PURE__ */ jsx6("p", { className: "text-sm text-red-600 dark:text-red-400", children: error }) : null
1445
+ error ? /* @__PURE__ */ jsx6("p", { className: "vos-error", children: error }) : null
1253
1446
  ] }),
1254
- /* @__PURE__ */ jsxs6("footer", { className: "flex items-center justify-end gap-2 border-t border-neutral-200 p-5 dark:border-neutral-800", children: [
1255
- /* @__PURE__ */ jsx6(
1256
- "button",
1257
- {
1258
- type: "button",
1259
- onClick: onCancel,
1260
- className: "rounded-md px-3 py-2 text-sm text-neutral-600 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-900",
1261
- children: "Cancel"
1262
- }
1263
- ),
1447
+ /* @__PURE__ */ jsxs6("footer", { className: "vos-drawer-footer", children: [
1448
+ /* @__PURE__ */ jsx6("button", { type: "button", onClick: onCancel, className: "vos-btn vos-btn-ghost vos-btn-sm", children: "Cancel" }),
1264
1449
  /* @__PURE__ */ jsx6(
1265
1450
  "button",
1266
1451
  {
1267
1452
  type: "submit",
1268
1453
  disabled: missing.length > 0 || saving,
1269
1454
  title: missing.length > 0 ? `Fill in: ${missing.join(", ")}` : void 0,
1270
- className: "rounded-md bg-blue-600 px-3 py-2 text-sm font-medium text-white hover:bg-blue-500 disabled:cursor-not-allowed disabled:opacity-50",
1455
+ className: "vos-btn vos-btn-sm",
1271
1456
  children: saving ? "Creating\u2026" : `Create ${REGISTER_LABEL[register]}`
1272
1457
  }
1273
1458
  )
@@ -1280,10 +1465,10 @@ function Field({
1280
1465
  onChange
1281
1466
  }) {
1282
1467
  const id = `field-${field.key}`;
1283
- return /* @__PURE__ */ jsxs6("div", { children: [
1468
+ return /* @__PURE__ */ jsxs6("div", { className: "vos-field", children: [
1284
1469
  /* @__PURE__ */ jsxs6("label", { htmlFor: id, className: FIELD_LABEL_CLASS, children: [
1285
1470
  field.label,
1286
- field.required ? /* @__PURE__ */ jsx6("span", { className: "text-red-500", children: " *" }) : null
1471
+ field.required ? /* @__PURE__ */ jsx6("span", { className: "vos-req", children: " *" }) : null
1287
1472
  ] }),
1288
1473
  field.kind === "textarea" ? /* @__PURE__ */ jsx6(
1289
1474
  "textarea",
@@ -1379,9 +1564,9 @@ function RelationEditor({
1379
1564
  } catch {
1380
1565
  }
1381
1566
  };
1382
- return /* @__PURE__ */ jsxs7("section", { className: "border-t border-neutral-200 p-5 dark:border-neutral-800", children: [
1383
- /* @__PURE__ */ jsx7("h3", { className: "mb-2 text-xs font-medium uppercase tracking-wide text-neutral-400", children: "Link a record" }),
1384
- /* @__PURE__ */ jsxs7("div", { className: "space-y-3", children: [
1567
+ return /* @__PURE__ */ jsxs7("section", { className: "vos-relation", children: [
1568
+ /* @__PURE__ */ jsx7("h3", { className: "vos-sectitle", children: "Link a record" }),
1569
+ /* @__PURE__ */ jsxs7("div", { className: "vos-field-stack", children: [
1385
1570
  /* @__PURE__ */ jsxs7(
1386
1571
  "select",
1387
1572
  {
@@ -1411,14 +1596,15 @@ function RelationEditor({
1411
1596
  ]
1412
1597
  }
1413
1598
  ) : null,
1414
- error ? /* @__PURE__ */ jsx7("p", { className: "text-sm text-red-600 dark:text-red-400", children: error }) : null,
1599
+ error ? /* @__PURE__ */ jsx7("p", { className: "vos-error", children: error }) : null,
1415
1600
  /* @__PURE__ */ jsx7(
1416
1601
  "button",
1417
1602
  {
1418
1603
  type: "button",
1419
1604
  onClick: onLink,
1420
1605
  disabled: !active || !targetId || linking,
1421
- className: "rounded-md bg-blue-600 px-3 py-2 text-sm font-medium text-white hover:bg-blue-500 disabled:cursor-not-allowed disabled:opacity-50",
1606
+ className: "vos-btn vos-btn-sm",
1607
+ style: { alignSelf: "flex-start" },
1422
1608
  children: linking ? "Linking\u2026" : "Link"
1423
1609
  }
1424
1610
  )
@@ -1427,9 +1613,12 @@ function RelationEditor({
1427
1613
  }
1428
1614
 
1429
1615
  // src/register-browser.tsx
1430
- import { useState as useState6 } from "react";
1431
1616
  import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1432
- function RegisterBrowser({ register, basePath }) {
1617
+ function RegisterBrowser({
1618
+ register,
1619
+ basePath,
1620
+ subtitle
1621
+ }) {
1433
1622
  const { records, loading, error, refresh: refreshList } = useList(
1434
1623
  register,
1435
1624
  basePath
@@ -1443,26 +1632,39 @@ function RegisterBrowser({ register, basePath }) {
1443
1632
  refresh: refreshRecord
1444
1633
  } = useRecord(register, openId, basePath);
1445
1634
  return /* @__PURE__ */ jsxs8("div", { children: [
1446
- /* @__PURE__ */ jsxs8("div", { className: "mb-3 flex items-center justify-between", children: [
1447
- /* @__PURE__ */ jsx8("h1", { className: "text-lg font-semibold text-neutral-900 dark:text-neutral-50", children: REGISTER_LABEL[register] }),
1635
+ /* @__PURE__ */ jsxs8("div", { className: "vos-head", children: [
1636
+ /* @__PURE__ */ jsxs8("div", { children: [
1637
+ /* @__PURE__ */ jsx8("h1", { children: REGISTER_LABEL[register] }),
1638
+ subtitle ? /* @__PURE__ */ jsx8("p", { children: subtitle }) : null
1639
+ ] }),
1640
+ /* @__PURE__ */ jsx8("div", { className: "vos-spacer" }),
1641
+ /* @__PURE__ */ jsx8(
1642
+ "button",
1643
+ {
1644
+ type: "button",
1645
+ onClick: () => refreshList(),
1646
+ className: "vos-btn vos-btn-ghost",
1647
+ children: "\u21BB Refresh"
1648
+ }
1649
+ ),
1448
1650
  /* @__PURE__ */ jsxs8(
1449
1651
  "button",
1450
1652
  {
1451
1653
  type: "button",
1452
1654
  onClick: () => setCreating(true),
1453
- className: "rounded-md bg-blue-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-blue-500",
1655
+ className: "vos-btn",
1454
1656
  children: [
1455
- "New ",
1657
+ "+ New ",
1456
1658
  REGISTER_SINGULAR[register]
1457
1659
  ]
1458
1660
  }
1459
1661
  )
1460
1662
  ] }),
1461
- loading && !records ? /* @__PURE__ */ jsxs8("p", { className: "text-sm text-neutral-500", children: [
1663
+ loading && !records ? /* @__PURE__ */ jsxs8("p", { className: "vos-muted", children: [
1462
1664
  "Loading ",
1463
1665
  REGISTER_LABEL[register].toLowerCase(),
1464
1666
  "\u2026"
1465
- ] }) : error ? /* @__PURE__ */ jsx8("p", { className: "text-sm text-red-600 dark:text-red-400", children: error }) : /* @__PURE__ */ jsx8(
1667
+ ] }) : error ? /* @__PURE__ */ jsx8("p", { className: "vos-error", children: error }) : /* @__PURE__ */ jsx8(
1466
1668
  RegisterTable,
1467
1669
  {
1468
1670
  register,
@@ -1505,12 +1707,11 @@ function RegisterBrowser({ register, basePath }) {
1505
1707
  open: creating,
1506
1708
  onClose: () => setCreating(false),
1507
1709
  ariaLabel: `New ${REGISTER_SINGULAR[register]} record`,
1508
- scroll: false,
1509
1710
  children: [
1510
- /* @__PURE__ */ jsxs8("header", { className: "border-b border-neutral-200 p-5 dark:border-neutral-800", children: [
1511
- /* @__PURE__ */ jsx8("p", { className: "text-xs font-medium uppercase tracking-wide text-neutral-400", children: "New" }),
1512
- /* @__PURE__ */ jsx8("h2", { className: "mt-1 text-lg font-semibold text-neutral-900 dark:text-neutral-50", children: REGISTER_SINGULAR[register] })
1513
- ] }),
1711
+ /* @__PURE__ */ jsx8("header", { className: "vos-drawer-header", children: /* @__PURE__ */ jsxs8("div", { children: [
1712
+ /* @__PURE__ */ jsx8("p", { className: "vos-drawer-eyebrow", children: "New" }),
1713
+ /* @__PURE__ */ jsx8("h2", { className: "vos-drawer-title", children: REGISTER_SINGULAR[register] })
1714
+ ] }) }),
1514
1715
  /* @__PURE__ */ jsx8(
1515
1716
  RecordForm,
1516
1717
  {
@@ -1530,6 +1731,38 @@ function RegisterBrowser({ register, basePath }) {
1530
1731
  ] });
1531
1732
  }
1532
1733
 
1734
+ // src/register-nav.tsx
1735
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1736
+ function RegisterNav({
1737
+ active,
1738
+ onSelect,
1739
+ counts,
1740
+ registers
1741
+ }) {
1742
+ const groups = REGISTER_GROUPS.map((g) => ({
1743
+ ...g,
1744
+ registers: registers ? g.registers.filter((r) => registers.includes(r)) : g.registers
1745
+ })).filter((g) => g.registers.length > 0);
1746
+ return /* @__PURE__ */ jsx9("nav", { className: "vos-nav", "aria-label": "Registers", children: groups.map((group) => /* @__PURE__ */ jsxs9("div", { children: [
1747
+ /* @__PURE__ */ jsx9("div", { className: "vos-nav-group", children: group.label }),
1748
+ group.registers.map((register) => /* @__PURE__ */ jsxs9(
1749
+ "button",
1750
+ {
1751
+ type: "button",
1752
+ className: `vos-nav-item ${register === active ? "is-active" : ""}`,
1753
+ "aria-current": register === active ? "page" : void 0,
1754
+ onClick: () => onSelect(register),
1755
+ children: [
1756
+ /* @__PURE__ */ jsx9("span", { className: "vos-nav-ic", "aria-hidden": "true", children: REGISTER_ICON[register] }),
1757
+ REGISTER_LABEL[register],
1758
+ /* @__PURE__ */ jsx9("span", { className: "vos-nav-count vos-num", children: counts?.[register] !== void 0 ? formatCount(counts[register] ?? 0) : "\xB7" })
1759
+ ]
1760
+ },
1761
+ register
1762
+ ))
1763
+ ] }, group.label)) });
1764
+ }
1765
+
1533
1766
  // src/use-counts.ts
1534
1767
  import { useCallback as useCallback3, useEffect as useEffect4, useState as useState7 } from "react";
1535
1768
  function useCounts(basePath = "/api") {
@@ -1560,32 +1793,172 @@ function useCounts(basePath = "/api") {
1560
1793
  const refresh = useCallback3(() => setTick((t2) => t2 + 1), []);
1561
1794
  return { counts, loading, error, refresh };
1562
1795
  }
1796
+
1797
+ // src/dashboard-app.tsx
1798
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1799
+ function initialsOf(name) {
1800
+ const parts = name.trim().split(/\s+/);
1801
+ const first = parts[0]?.[0] ?? "";
1802
+ const last = parts.length > 1 ? parts[parts.length - 1]?.[0] ?? "" : "";
1803
+ return (first + last).toUpperCase() || "?";
1804
+ }
1805
+ function registerFromHash(available) {
1806
+ if (typeof window === "undefined") return null;
1807
+ const hash = window.location.hash.replace(/^#\/?/, "");
1808
+ return available.includes(hash) ? hash : null;
1809
+ }
1810
+ function ValidationOSDashboard({ config = {} }) {
1811
+ const {
1812
+ basePath = "/api",
1813
+ backendLabel,
1814
+ branding,
1815
+ agentLabel,
1816
+ user,
1817
+ registers = REGISTER_ORDER
1818
+ } = config;
1819
+ const [active, setActive] = useState8(
1820
+ () => registerFromHash(registers) ?? registers[0] ?? "assumptions"
1821
+ );
1822
+ const { counts } = useCounts(basePath);
1823
+ useEffect5(() => {
1824
+ if (typeof window === "undefined") return;
1825
+ const onHash = () => {
1826
+ const next = registerFromHash(registers);
1827
+ if (next) setActive(next);
1828
+ };
1829
+ window.addEventListener("hashchange", onHash);
1830
+ return () => window.removeEventListener("hashchange", onHash);
1831
+ }, [registers]);
1832
+ const select = useCallback4((register) => {
1833
+ setActive(register);
1834
+ if (typeof window !== "undefined") {
1835
+ window.location.hash = register;
1836
+ }
1837
+ }, []);
1838
+ const toggleTheme = useCallback4(() => {
1839
+ const root = document.documentElement;
1840
+ const current = root.getAttribute("data-theme") ?? (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
1841
+ root.setAttribute("data-theme", current === "dark" ? "light" : "dark");
1842
+ }, []);
1843
+ const brandName = branding?.name ?? "Validation-OS";
1844
+ const brandMark = branding?.initials ?? "V";
1845
+ return /* @__PURE__ */ jsxs10("div", { className: "vos-app", children: [
1846
+ /* @__PURE__ */ jsxs10("div", { className: "vos-brand", children: [
1847
+ /* @__PURE__ */ jsx10("span", { className: "vos-brand-dot", children: branding?.logoUrl ? /* @__PURE__ */ jsx10("img", { src: branding.logoUrl, alt: "" }) : brandMark }),
1848
+ " ",
1849
+ brandName
1850
+ ] }),
1851
+ /* @__PURE__ */ jsxs10("div", { className: "vos-topbar", children: [
1852
+ backendLabel ? /* @__PURE__ */ jsxs10("div", { className: "vos-backend", children: [
1853
+ /* @__PURE__ */ jsx10("span", { className: "vos-live-dot" }),
1854
+ " Backend: ",
1855
+ /* @__PURE__ */ jsx10("b", { children: backendLabel })
1856
+ ] }) : null,
1857
+ agentLabel ? /* @__PURE__ */ jsxs10("span", { className: "vos-hint", children: [
1858
+ "Agent: ",
1859
+ /* @__PURE__ */ jsx10("b", { style: { color: "var(--vos-text)" }, children: agentLabel })
1860
+ ] }) : null,
1861
+ /* @__PURE__ */ jsx10("div", { className: "vos-spacer" }),
1862
+ /* @__PURE__ */ jsx10("button", { type: "button", className: "vos-iconbtn", onClick: toggleTheme, children: "\u25D0 Theme" }),
1863
+ user?.name ? /* @__PURE__ */ jsxs10("div", { className: "vos-user", children: [
1864
+ /* @__PURE__ */ jsx10("span", { className: "vos-avatar", children: initialsOf(user.name) }),
1865
+ /* @__PURE__ */ jsxs10("div", { children: [
1866
+ /* @__PURE__ */ jsx10("span", { className: "vos-user-name", children: user.name }),
1867
+ user.caption ? /* @__PURE__ */ jsx10("small", { children: user.caption }) : null
1868
+ ] })
1869
+ ] }) : null
1870
+ ] }),
1871
+ /* @__PURE__ */ jsx10(
1872
+ RegisterNav,
1873
+ {
1874
+ active,
1875
+ onSelect: select,
1876
+ counts,
1877
+ registers
1878
+ }
1879
+ ),
1880
+ /* @__PURE__ */ jsx10("main", { className: "vos-main", children: /* @__PURE__ */ jsx10(
1881
+ RegisterBrowser,
1882
+ {
1883
+ register: active,
1884
+ basePath,
1885
+ subtitle: REGISTER_SUBTITLE[active]
1886
+ },
1887
+ active
1888
+ ) })
1889
+ ] });
1890
+ }
1891
+
1892
+ // src/register-counts.tsx
1893
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
1894
+ function RegisterCounts({
1895
+ counts,
1896
+ caption,
1897
+ onSelect,
1898
+ active
1899
+ }) {
1900
+ const registers = REGISTER_ORDER.filter(
1901
+ (r) => counts[r] !== void 0
1902
+ );
1903
+ return /* @__PURE__ */ jsxs11("section", { "aria-label": "Register counts", children: [
1904
+ /* @__PURE__ */ jsx11("div", { className: "vos-tile-grid", children: registers.map((register) => /* @__PURE__ */ jsx11(
1905
+ StatTile,
1906
+ {
1907
+ label: REGISTER_LABEL[register],
1908
+ value: counts[register] ?? 0,
1909
+ onClick: onSelect ? () => onSelect(register) : void 0,
1910
+ active: active === register
1911
+ },
1912
+ register
1913
+ )) }),
1914
+ caption ? /* @__PURE__ */ jsx11("p", { className: "vos-hint", style: { marginTop: 16 }, children: caption }) : null
1915
+ ] });
1916
+ }
1563
1917
  export {
1564
1918
  CONFLICT_MESSAGE,
1919
+ ConfidenceCell,
1920
+ REGISTER_GROUPS,
1921
+ REGISTER_ICON,
1565
1922
  REGISTER_LABEL,
1566
1923
  REGISTER_ORDER,
1567
1924
  REGISTER_SINGULAR,
1925
+ REGISTER_SUBTITLE,
1926
+ RISK_CRIT,
1927
+ RISK_WARN,
1568
1928
  RecordDrawer,
1569
1929
  RecordForm,
1570
1930
  RegisterBrowser,
1571
1931
  RegisterCounts,
1932
+ RegisterNav,
1572
1933
  RegisterTable,
1573
1934
  RelationEditor,
1935
+ RiskBar,
1936
+ Sparkline,
1937
+ StatTile,
1938
+ StatusPill,
1574
1939
  UnderstandingPanel,
1940
+ ValidationOSDashboard,
1575
1941
  buildPatch,
1576
1942
  buildUnderstanding,
1577
1943
  cellValue,
1578
1944
  columnsFor,
1945
+ confidenceTone,
1579
1946
  draftFrom,
1580
1947
  editableFields,
1581
1948
  emptyDraft,
1582
1949
  formFieldsFor,
1950
+ formatCount,
1951
+ formatSigned,
1583
1952
  formatValue,
1584
1953
  hasEdits,
1585
1954
  interpretSave,
1586
1955
  linkChoicesFrom,
1587
1956
  missingRequired,
1588
1957
  primaryLabel,
1958
+ riskFraction,
1959
+ riskLevel,
1960
+ sparklinePath,
1961
+ statusTone,
1589
1962
  toCreatePayload,
1590
1963
  useCounts,
1591
1964
  useCreate,