@nivaro/react 0.3.36 → 0.3.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/components/slots/CommentsSlot.js +44 -36
  2. package/dist/full.css +1 -1
  3. package/dist/hooks/useSessionRecorder.js +1 -1
  4. package/dist/index.d.ts +9 -1
  5. package/dist/shared/components/AgentBadge.js +26 -0
  6. package/dist/shared/components/AgentQuestionsCard.js +198 -0
  7. package/dist/shared/components/ItemEditForm.js +3329 -3263
  8. package/dist/shared/components/McpConnectionScope.js +480 -0
  9. package/dist/shared/components/MyWorkView.js +223 -208
  10. package/dist/shared/components/RecordReadView.js +620 -575
  11. package/dist/shared/components/chat/ChatPanel.js +683 -671
  12. package/dist/shared/components/imports/DownloadSampleButton.js +69 -0
  13. package/dist/shared/components/imports/ImportConsole.js +106 -104
  14. package/dist/shared/components/imports/RunDetailSheet.js +11 -10
  15. package/dist/shared/components/item-edit/FieldAffordances.js +32 -32
  16. package/dist/shared/components/item-edit/FormTour.js +279 -0
  17. package/dist/shared/components/item-edit/GroupSection.js +377 -360
  18. package/dist/shared/components/item-edit/PersonalDefaultControl.js +28 -28
  19. package/dist/shared/components/item-edit/entry/EntryStrip.js +30 -26
  20. package/dist/shared/components/item-edit/form-density.js +36 -0
  21. package/dist/shared/components/item-edit/useFormUsageBeacon.js +59 -46
  22. package/dist/shared/components/item-edit/usePersonalDefaults.js +312 -296
  23. package/dist/shared/components/layout-editor/AddPanel.js +349 -192
  24. package/dist/shared/components/layout-editor/DesignNode.js +126 -112
  25. package/dist/shared/components/layout-editor/EditorTopBar.js +123 -103
  26. package/dist/shared/components/layout-editor/LayoutEditor.js +829 -751
  27. package/dist/shared/components/layout-editor/PlaceTools.js +334 -0
  28. package/dist/shared/components/layout-editor/SlashInsert.js +252 -0
  29. package/dist/shared/components/layout-editor/StructureMenu.js +285 -242
  30. package/dist/shared/components/layout-editor/add-target.js +54 -43
  31. package/dist/shared/components/layout-editor/ai-forms/AiComposer.js +238 -155
  32. package/dist/shared/components/layout-editor/ai-forms/AiFormsButton.js +1 -1
  33. package/dist/shared/components/layout-editor/ai-forms/ai-forms-api.js +15 -12
  34. package/dist/shared/components/layout-editor/dnd/LayoutDndProvider.js +347 -189
  35. package/dist/shared/components/layout-editor/dnd/TrashZone.js +60 -32
  36. package/dist/shared/components/layout-editor/dnd/UndoChip.js +76 -0
  37. package/dist/shared/components/layout-editor/dnd/merge-drop.js +33 -0
  38. package/dist/shared/components/layout-editor/dnd/multi-drop.js +87 -0
  39. package/dist/shared/components/layout-editor/dnd/node.js +115 -105
  40. package/dist/shared/components/layout-editor/dnd/sensors.js +23 -0
  41. package/dist/shared/components/layout-editor/dnd/trash-drop.js +34 -28
  42. package/dist/shared/components/layout-editor/dnd/trash-links.js +97 -0
  43. package/dist/shared/components/layout-editor/even-row.js +86 -0
  44. package/dist/shared/components/layout-editor/field-list.js +99 -0
  45. package/dist/shared/components/layout-editor/format-painter.js +102 -0
  46. package/dist/shared/components/layout-editor/grid-guides.js +15 -13
  47. package/dist/shared/components/layout-editor/inspector/BehaviourSettings.js +150 -132
  48. package/dist/shared/components/layout-editor/inspector/BulkInspector.js +57 -53
  49. package/dist/shared/components/layout-editor/inspector/EvenRowAction.js +31 -0
  50. package/dist/shared/components/layout-editor/inspector/GroupInspector.js +154 -141
  51. package/dist/shared/components/layout-editor/inspector/Inspector.js +209 -178
  52. package/dist/shared/components/layout-editor/inspector/LookTab.js +106 -98
  53. package/dist/shared/components/layout-editor/inspector/RulesTab.js +38 -37
  54. package/dist/shared/components/layout-editor/inspector/kit.js +280 -223
  55. package/dist/shared/components/layout-editor/keyboard-move.js +15 -15
  56. package/dist/shared/components/layout-editor/lint/LabelStyle.js +201 -0
  57. package/dist/shared/components/layout-editor/lint/LintPanel.js +132 -84
  58. package/dist/shared/components/layout-editor/lint/lint-api.js +65 -28
  59. package/dist/shared/components/layout-editor/nav/NavTools.js +14 -12
  60. package/dist/shared/components/layout-editor/nav/ZoomControls.js +145 -0
  61. package/dist/shared/components/layout-editor/nav/useEditorNav.js +34 -32
  62. package/dist/shared/components/layout-editor/nav/view-prefs.js +100 -15
  63. package/dist/shared/components/layout-editor/outputs/LayoutFile.js +89 -0
  64. package/dist/shared/components/layout-editor/outputs/OutputsButton.js +272 -195
  65. package/dist/shared/components/layout-editor/outputs/layout-file-plan.js +176 -0
  66. package/dist/shared/components/layout-editor/outputs/output-api.js +71 -37
  67. package/dist/shared/components/layout-editor/overview/describe-conditions.js +16 -11
  68. package/dist/shared/components/layout-editor/overview/purpose.js +5 -5
  69. package/dist/shared/components/layout-editor/parity-keys.json.js +1 -1
  70. package/dist/shared/components/layout-editor/pin.js +37 -19
  71. package/dist/shared/components/layout-editor/place-clip.js +257 -0
  72. package/dist/shared/components/layout-editor/preview/PreviewModes.js +27 -26
  73. package/dist/shared/components/layout-editor/preview/PreviewPanes.js +148 -117
  74. package/dist/shared/components/layout-editor/preview/SampleRecord.js +113 -0
  75. package/dist/shared/components/layout-editor/preview/preview-model.js +79 -60
  76. package/dist/shared/components/layout-editor/preview/sample-record.js +105 -0
  77. package/dist/shared/components/layout-editor/removed-log.js +151 -0
  78. package/dist/shared/components/layout-editor/reuse/LibraryRows.js +109 -78
  79. package/dist/shared/components/layout-editor/reuse/linked-sections.js +373 -0
  80. package/dist/shared/components/layout-editor/revert-element.js +140 -0
  81. package/dist/shared/components/layout-editor/section-structure.js +193 -170
  82. package/dist/shared/components/layout-editor/settings/EntrySection.js +63 -45
  83. package/dist/shared/components/layout-editor/settings/LayoutSection.js +63 -32
  84. package/dist/shared/components/layout-editor/settings/ResolveSettings.js +179 -137
  85. package/dist/shared/components/layout-editor/settings/WhoSection.js +18 -17
  86. package/dist/shared/components/layout-editor/settings/settings-registry.js +24 -0
  87. package/dist/shared/components/layout-editor/shortcuts.js +21 -16
  88. package/dist/shared/components/layout-editor/structure-ops.js +221 -195
  89. package/dist/shared/components/layout-editor/usage/UsagePanel.js +133 -85
  90. package/dist/shared/components/layout-editor/usage/refusal-lens.js +130 -0
  91. package/dist/shared/components/layout-editor/usage/usage-api.js +12 -11
  92. package/dist/shared/components/panels/CommentPanel.js +47 -45
  93. package/dist/shared/components/panels/RevisionsPanel.js +66 -64
  94. package/dist/shared/components/panels/TaskPanel.js +395 -332
  95. package/dist/shared/components/profile/AgentsPanel.js +196 -147
  96. package/dist/shared/components/profile/ConnectionRollback.js +329 -0
  97. package/dist/shared/components/profile/MyDefaultsCard.js +526 -520
  98. package/dist/shared/lib/entry-config.js +22 -22
  99. package/dist/shared/lib/form-usage-beacon.js +91 -55
  100. package/dist/shared/lib/layout-lint.js +530 -284
  101. package/dist/shared/lib/layout-resolve.js +58 -44
  102. package/package.json +1 -1
@@ -1,76 +1,77 @@
1
- import { jsxs as s, jsx as t, Fragment as W } from "react/jsx-runtime";
2
- import { ChevronsDownUp as Y, Rows3 as z } from "lucide-react";
3
- import { useState as _, useMemo as u } from "react";
4
- import { UNGROUPED_KEY as B } from "../../../lib/layout-ops.js";
5
- import { titleCase as C } from "../../../lib/utils.js";
6
- import { Skeleton as $ } from "../../ui/skeleton.js";
7
- import { Toggle as H } from "../settings/controls.js";
8
- import { AbTestSection as J } from "./AbTestSection.js";
9
- import { suggestCollapse as Q, rateText as V } from "./suggest-collapse.js";
10
- import { suggestSections as X } from "./suggest-sections.js";
11
- import { useLayoutUsageToday as Z, useLayoutFriction as ee, useCoChanges as te, formatDuration as ae } from "./usage-api.js";
12
- const i = "px-3 pb-1.5 pt-4 text-[12px] font-semibold text-slate-700 dark:text-foreground", d = "px-3 text-[12px] leading-snug text-slate-500 dark:text-muted-foreground";
13
- function c({ label: n, value: m }) {
1
+ import { jsxs as s, jsx as t, Fragment as T } from "react/jsx-runtime";
2
+ import { ChevronsDownUp as J, Rows3 as Q } from "lucide-react";
3
+ import { useState as j, useMemo as b } from "react";
4
+ import { UNGROUPED_KEY as V } from "../../../lib/layout-ops.js";
5
+ import { titleCase as M } from "../../../lib/utils.js";
6
+ import { Skeleton as P } from "../../ui/skeleton.js";
7
+ import { Toggle as R } from "../settings/controls.js";
8
+ import { AbTestSection as X } from "./AbTestSection.js";
9
+ import { useRefusalLens as Z, useSaveRefusals as ee, refusalTone as te, refusalPct as ae, refusalTip as se, REFUSAL_MIN_ATTEMPTS as le } from "./refusal-lens.js";
10
+ import { suggestCollapse as re, rateText as ne } from "./suggest-collapse.js";
11
+ import { suggestSections as oe } from "./suggest-sections.js";
12
+ import { useLayoutUsageToday as de, useLayoutFriction as ie, useCoChanges as ce, formatDuration as me } from "./usage-api.js";
13
+ const c = "px-3 pb-1.5 pt-4 text-[12px] font-semibold text-slate-700 dark:text-foreground", o = "px-3 text-[12px] leading-snug text-slate-500 dark:text-muted-foreground";
14
+ function m({ label: r, value: p }) {
14
15
  return /* @__PURE__ */ s("div", { className: "flex flex-col gap-0.5 bg-white px-2.5 py-2 dark:bg-card", children: [
15
- /* @__PURE__ */ t("span", { className: "text-[11px] text-slate-500 dark:text-muted-foreground", children: n }),
16
- /* @__PURE__ */ t("span", { className: "text-[14px] font-semibold tabular-nums text-slate-900 dark:text-foreground", children: m })
16
+ /* @__PURE__ */ t("span", { className: "text-[11px] text-slate-500 dark:text-muted-foreground", children: r }),
17
+ /* @__PURE__ */ t("span", { className: "text-[14px] font-semibold tabular-nums text-slate-900 dark:text-foreground", children: p })
17
18
  ] });
18
19
  }
19
- function j({ children: n }) {
20
- return /* @__PURE__ */ t("div", { className: "mx-3 grid grid-cols-3 gap-px overflow-hidden rounded-lg border border-slate-200 bg-slate-200 dark:border-border dark:bg-border", children: n });
20
+ function U({ children: r }) {
21
+ return /* @__PURE__ */ t("div", { className: "mx-3 grid grid-cols-3 gap-px overflow-hidden rounded-lg border border-slate-200 bg-slate-200 dark:border-border dark:bg-border", children: r });
21
22
  }
22
- function ge({
23
- bundle: n,
24
- collection: m,
25
- fields: b,
26
- apply: N,
27
- showFill: M,
28
- onShowFill: U,
29
- onOpenLayout: D
23
+ function Le({
24
+ bundle: r,
25
+ collection: p,
26
+ fields: k,
27
+ apply: y,
28
+ showFill: D,
29
+ onShowFill: E,
30
+ onOpenLayout: F
30
31
  }) {
31
- var S;
32
- const v = Number(n.layout.id), { data: o, isLoading: P } = Z(v), { data: a, isLoading: p } = ee(v), { data: h, isLoading: T } = te(m), [k, R] = _(() => /* @__PURE__ */ new Set()), [E, F] = _(!1), y = u(() => {
33
- const e = new Map(b.map((l) => [l.field, l.label || C(l.field)]));
34
- return (l) => e.get(l) ?? C(l);
35
- }, [b]), g = (e) => {
32
+ var C;
33
+ const h = Number(r.layout.id), { data: d, isLoading: O } = de(h), { data: a, isLoading: g } = ie(h), { data: u, isLoading: A } = ce(p), w = Z(h), { data: n, isLoading: q } = ee(h), [L, G] = j(() => /* @__PURE__ */ new Set()), [I, W] = j(!1), N = b(() => {
34
+ const e = new Map(k.map((l) => [l.field, l.label || M(l.field)]));
35
+ return (l) => e.get(l) ?? M(l);
36
+ }, [k]), x = (e) => {
36
37
  var l;
37
- return e == null || e === B ? "Ungrouped" : ((l = n.groups.find((x) => x.key === e)) == null ? void 0 : l.label) ?? e;
38
- }, O = (e) => e === "__form__" ? "Whole form" : e === "__general__" ? "General" : g(e), A = u(() => new Set(n.placements.map((e) => e.field)), [n.placements]), w = u(
39
- () => X(n, (h == null ? void 0 : h.pairs) ?? []).filter((e) => !k.has(e.id)),
40
- [n, h, k]
41
- ), r = u(
42
- () => Q(n, (a == null ? void 0 : a.sections) ?? []),
43
- [n, a == null ? void 0 : a.sections]
44
- ), f = (a == null ? void 0 : a.steps) ?? [], G = Math.max(1, ...f.map((e) => e.avg_ms)), L = ((a == null ? void 0 : a.changed_after_save.fields) ?? []).slice(0, 8), q = o && !o.ready || a && !a.ready;
38
+ return e == null || e === V ? "Ungrouped" : ((l = r.groups.find((f) => f.key === e)) == null ? void 0 : l.label) ?? e;
39
+ }, K = (e) => e === "__form__" ? "Whole form" : e === "__general__" ? "General" : x(e), S = b(() => new Set(r.placements.map((e) => e.field)), [r.placements]), _ = b(
40
+ () => oe(r, (u == null ? void 0 : u.pairs) ?? []).filter((e) => !L.has(e.id)),
41
+ [r, u, L]
42
+ ), i = b(
43
+ () => re(r, (a == null ? void 0 : a.sections) ?? []),
44
+ [r, a == null ? void 0 : a.sections]
45
+ ), v = (a == null ? void 0 : a.steps) ?? [], Y = Math.max(1, ...v.map((e) => e.avg_ms)), $ = ((a == null ? void 0 : a.changed_after_save.fields) ?? []).slice(0, 8), z = d && !d.ready || a && !a.ready;
45
46
  return /* @__PURE__ */ s("div", { "data-editor-usage": "", className: "flex flex-col pb-4", children: [
46
47
  /* @__PURE__ */ s("div", { className: "flex items-center justify-between gap-2 px-3 pt-3", children: [
47
48
  /* @__PURE__ */ t("span", { className: "text-[12.5px] text-slate-700 dark:text-foreground", children: "Show how full each field is" }),
48
- /* @__PURE__ */ t(H, { label: "Show how full each field is", checked: M, onChange: U })
49
+ /* @__PURE__ */ t(R, { label: "Show how full each field is", checked: D, onChange: E })
49
50
  ] }),
50
- /* @__PURE__ */ t("p", { className: `${d} pt-1`, children: "Each field on the form shows the share of records that have a value." }),
51
- q && /* @__PURE__ */ t("p", { role: "note", className: `${d} pt-3`, children: "Usage is not being recorded on this instance yet (its database needs the latest update)." }),
52
- /* @__PURE__ */ t("p", { className: i, children: "Today" }),
53
- P ? /* @__PURE__ */ t($, { className: "mx-3 h-12 rounded-lg" }) : /* @__PURE__ */ s(j, { children: [
54
- /* @__PURE__ */ t(c, { label: "Opens", value: ((o == null ? void 0 : o.opens) ?? 0).toLocaleString() }),
55
- /* @__PURE__ */ t(c, { label: "People", value: ((o == null ? void 0 : o.people) ?? 0).toLocaleString() }),
56
- /* @__PURE__ */ t(c, { label: "Records", value: ((o == null ? void 0 : o.records) ?? 0).toLocaleString() })
51
+ /* @__PURE__ */ t("p", { className: `${o} pt-1`, children: "Each field on the form shows the share of records that have a value." }),
52
+ z && /* @__PURE__ */ t("p", { role: "note", className: `${o} pt-3`, children: "Usage is not being recorded on this instance yet (its database needs the latest update)." }),
53
+ /* @__PURE__ */ t("p", { className: c, children: "Today" }),
54
+ O ? /* @__PURE__ */ t(P, { className: "mx-3 h-12 rounded-lg" }) : /* @__PURE__ */ s(U, { children: [
55
+ /* @__PURE__ */ t(m, { label: "Opens", value: ((d == null ? void 0 : d.opens) ?? 0).toLocaleString() }),
56
+ /* @__PURE__ */ t(m, { label: "People", value: ((d == null ? void 0 : d.people) ?? 0).toLocaleString() }),
57
+ /* @__PURE__ */ t(m, { label: "Records", value: ((d == null ? void 0 : d.records) ?? 0).toLocaleString() })
57
58
  ] }),
58
- /* @__PURE__ */ s("p", { className: i, children: [
59
+ /* @__PURE__ */ s("p", { className: c, children: [
59
60
  "Last ",
60
61
  (a == null ? void 0 : a.days) ?? 30,
61
62
  " days"
62
63
  ] }),
63
- p ? /* @__PURE__ */ t($, { className: "mx-3 h-12 rounded-lg" }) : /* @__PURE__ */ s(j, { children: [
64
- /* @__PURE__ */ t(c, { label: "Opens", value: ((a == null ? void 0 : a.opens) ?? 0).toLocaleString() }),
65
- /* @__PURE__ */ t(c, { label: "Saves", value: ((a == null ? void 0 : a.saves) ?? 0).toLocaleString() }),
66
- /* @__PURE__ */ t(c, { label: "Left unfinished", value: ((a == null ? void 0 : a.abandons) ?? 0).toLocaleString() })
64
+ g ? /* @__PURE__ */ t(P, { className: "mx-3 h-12 rounded-lg" }) : /* @__PURE__ */ s(U, { children: [
65
+ /* @__PURE__ */ t(m, { label: "Opens", value: ((a == null ? void 0 : a.opens) ?? 0).toLocaleString() }),
66
+ /* @__PURE__ */ t(m, { label: "Saves", value: ((a == null ? void 0 : a.saves) ?? 0).toLocaleString() }),
67
+ /* @__PURE__ */ t(m, { label: "Left unfinished", value: ((a == null ? void 0 : a.abandons) ?? 0).toLocaleString() })
67
68
  ] }),
68
- /* @__PURE__ */ t("p", { className: i, children: "Time per step" }),
69
- f.length === 0 ? /* @__PURE__ */ t("p", { className: d, children: p ? "Loading…" : "No visits recorded yet." }) : /* @__PURE__ */ t("ul", { "data-usage-steps": "", className: "flex flex-col gap-2 px-3", children: f.map((e) => /* @__PURE__ */ s("li", { className: "flex flex-col gap-1", children: [
69
+ /* @__PURE__ */ t("p", { className: c, children: "Time per step" }),
70
+ v.length === 0 ? /* @__PURE__ */ t("p", { className: o, children: g ? "Loading…" : "No visits recorded yet." }) : /* @__PURE__ */ t("ul", { "data-usage-steps": "", className: "flex flex-col gap-2 px-3", children: v.map((e) => /* @__PURE__ */ s("li", { className: "flex flex-col gap-1", children: [
70
71
  /* @__PURE__ */ s("div", { className: "flex items-baseline justify-between gap-2 text-[12px]", children: [
71
- /* @__PURE__ */ t("span", { className: "truncate text-slate-800 dark:text-foreground", children: O(e.key) }),
72
+ /* @__PURE__ */ t("span", { className: "truncate text-slate-800 dark:text-foreground", children: K(e.key) }),
72
73
  /* @__PURE__ */ s("span", { className: "shrink-0 tabular-nums text-slate-500 dark:text-muted-foreground", children: [
73
- ae(e.avg_ms),
74
+ me(e.avg_ms),
74
75
  e.abandons > 0 && /* @__PURE__ */ s("span", { className: "text-amber-700 dark:text-amber-300", children: [
75
76
  " · ",
76
77
  e.abandons,
@@ -82,20 +83,20 @@ function ge({
82
83
  "div",
83
84
  {
84
85
  className: "h-full rounded-full bg-slate-400 dark:bg-slate-500",
85
- style: { width: `${Math.max(2, Math.round(e.avg_ms / G * 100))}%` }
86
+ style: { width: `${Math.max(2, Math.round(e.avg_ms / Y * 100))}%` }
86
87
  }
87
88
  ) })
88
89
  ] }, e.key)) }),
89
- /* @__PURE__ */ t("p", { className: i, children: "Changed soon after saving" }),
90
- L.length === 0 ? /* @__PURE__ */ t("p", { className: d, children: p ? "Loading…" : "No field was changed within a day of a record being created." }) : /* @__PURE__ */ s(W, { children: [
91
- /* @__PURE__ */ t("ul", { "data-usage-changed": "", className: "flex flex-col px-3", children: L.map((e) => /* @__PURE__ */ s(
90
+ /* @__PURE__ */ t("p", { className: c, children: "Changed soon after saving" }),
91
+ $.length === 0 ? /* @__PURE__ */ t("p", { className: o, children: g ? "Loading…" : "No field was changed within a day of a record being created." }) : /* @__PURE__ */ s(T, { children: [
92
+ /* @__PURE__ */ t("ul", { "data-usage-changed": "", className: "flex flex-col px-3", children: $.map((e) => /* @__PURE__ */ s(
92
93
  "li",
93
94
  {
94
95
  className: "flex items-baseline justify-between gap-2 border-b border-slate-100 py-1 text-[12px] last:border-0 dark:border-border",
95
96
  children: [
96
97
  /* @__PURE__ */ s("span", { className: "min-w-0 truncate text-slate-800 dark:text-foreground", children: [
97
- y(e.field),
98
- !A.has(e.field) && /* @__PURE__ */ s("span", { className: "text-slate-400 dark:text-muted-foreground", children: [
98
+ N(e.field),
99
+ !S.has(e.field) && /* @__PURE__ */ s("span", { className: "text-slate-400 dark:text-muted-foreground", children: [
99
100
  " ",
100
101
  "· not on this form"
101
102
  ] })
@@ -108,29 +109,76 @@ function ge({
108
109
  },
109
110
  e.field
110
111
  )) }),
111
- /* @__PURE__ */ s("p", { className: `${d} pt-1`, children: [
112
+ /* @__PURE__ */ s("p", { className: `${o} pt-1`, children: [
112
113
  "Edits within 24 hours of a record being created, last 90 days",
113
114
  (a == null ? void 0 : a.changed_after_save.scope) === "collection" ? ", across every form of this collection." : ", on records opened with this form."
114
115
  ] })
115
116
  ] }),
116
- /* @__PURE__ */ t("p", { className: i, children: "Rarely opened sections" }),
117
- p ? /* @__PURE__ */ t("p", { className: d, children: "Loading…" }) : r.candidates.length === 0 || E ? /* @__PURE__ */ t("p", { className: d, children: (S = a == null ? void 0 : a.sections) != null && S.length ? "No suggestion. People open every section that starts open often enough." : "No section visits recorded yet." }) : /* @__PURE__ */ s(
117
+ /* @__PURE__ */ t("p", { className: c, children: "Where saves fail" }),
118
+ /* @__PURE__ */ s("div", { className: "flex items-center justify-between gap-2 px-3", children: [
119
+ /* @__PURE__ */ t("span", { className: "text-[12.5px] text-slate-700 dark:text-foreground", children: "Show where saves fail" }),
120
+ /* @__PURE__ */ t(R, { label: "Show where saves fail", checked: w.on, onChange: w.setOn })
121
+ ] }),
122
+ q ? /* @__PURE__ */ t("p", { className: `${o} pt-1`, children: "Loading…" }) : !n || n.fields.length === 0 ? /* @__PURE__ */ t("p", { className: `${o} pt-1`, children: n != null && n.attempts ? `No save was refused in the last ${n.days} days.` : "No saves recorded yet." }) : /* @__PURE__ */ s(T, { children: [
123
+ /* @__PURE__ */ t("ul", { "data-usage-refusals": "", className: "flex flex-col px-3 pt-1", children: n.fields.slice(0, 8).map((e) => {
124
+ const l = te(e.rate);
125
+ return /* @__PURE__ */ s(
126
+ "li",
127
+ {
128
+ "data-usage-refusal": e.field,
129
+ "data-tip": se(e, n.attempts, n.days),
130
+ className: "flex items-baseline justify-between gap-2 border-b border-slate-100 py-1 text-[12px] last:border-0 dark:border-border",
131
+ children: [
132
+ /* @__PURE__ */ s("span", { className: "min-w-0 truncate text-slate-800 dark:text-foreground", children: [
133
+ N(e.field),
134
+ !S.has(e.field) && /* @__PURE__ */ s("span", { className: "text-slate-400 dark:text-muted-foreground", children: [
135
+ " ",
136
+ "· not on this form"
137
+ ] })
138
+ ] }),
139
+ /* @__PURE__ */ s(
140
+ "span",
141
+ {
142
+ className: l === "high" ? "shrink-0 tabular-nums text-red-700 dark:text-red-300" : l === "some" ? "shrink-0 tabular-nums text-amber-700 dark:text-amber-300" : "shrink-0 tabular-nums text-slate-500 dark:text-muted-foreground",
143
+ children: [
144
+ ae(e.rate),
145
+ " of saves"
146
+ ]
147
+ }
148
+ )
149
+ ]
150
+ },
151
+ e.field
152
+ );
153
+ }) }),
154
+ /* @__PURE__ */ s("p", { className: `${o} pt-1`, children: [
155
+ n.refused.toLocaleString(),
156
+ " of ",
157
+ n.attempts.toLocaleString(),
158
+ " saves were refused in the last ",
159
+ n.days,
160
+ " days — a required field left empty, or a value a rule turned down.",
161
+ n.attempts < le && " Too few saves yet to tint the form."
162
+ ] })
163
+ ] }),
164
+ /* @__PURE__ */ t("p", { className: c, children: "Rarely opened sections" }),
165
+ g ? /* @__PURE__ */ t("p", { className: o, children: "Loading…" }) : i.candidates.length === 0 || I ? /* @__PURE__ */ t("p", { className: o, children: (C = a == null ? void 0 : a.sections) != null && C.length ? "No suggestion. People open every section that starts open often enough." : "No section visits recorded yet." }) : /* @__PURE__ */ s(
118
166
  "div",
119
167
  {
120
168
  "data-usage-collapse": "",
121
169
  className: "mx-3 flex flex-col gap-2 rounded-lg border border-slate-200 bg-white p-2.5 dark:border-border dark:bg-card",
122
170
  children: [
123
171
  /* @__PURE__ */ s("p", { className: "m-0 text-[12px] leading-snug text-slate-700 dark:text-foreground", children: [
124
- /* @__PURE__ */ t(Y, { className: "mr-1 inline h-3.5 w-3.5 align-[-2px] text-slate-400" }),
172
+ /* @__PURE__ */ t(J, { className: "mr-1 inline h-3.5 w-3.5 align-[-2px] text-slate-400" }),
125
173
  "People rarely open",
126
174
  " ",
127
- r.candidates.length === 1 ? "this section" : "these sections",
175
+ i.candidates.length === 1 ? "this section" : "these sections",
128
176
  ". Starting",
129
177
  " ",
130
- r.candidates.length === 1 ? "it" : "them",
178
+ i.candidates.length === 1 ? "it" : "them",
131
179
  " collapsed keeps the form shorter."
132
180
  ] }),
133
- /* @__PURE__ */ t("ul", { className: "m-0 flex list-none flex-col p-0", children: r.candidates.map((e) => /* @__PURE__ */ s(
181
+ /* @__PURE__ */ t("ul", { className: "m-0 flex list-none flex-col p-0", children: i.candidates.map((e) => /* @__PURE__ */ s(
134
182
  "li",
135
183
  {
136
184
  "data-usage-collapse-section": e.key,
@@ -139,7 +187,7 @@ function ge({
139
187
  /* @__PURE__ */ t("span", { className: "truncate text-slate-800 dark:text-foreground", children: e.label }),
140
188
  /* @__PURE__ */ s("span", { className: "tabular-nums text-[11.5px] text-slate-500 dark:text-muted-foreground", children: [
141
189
  "opened in ",
142
- V(e.rate),
190
+ ne(e.rate),
143
191
  " of ",
144
192
  e.shown.toLocaleString(),
145
193
  " visits"
@@ -153,18 +201,18 @@ function ge({
153
201
  "button",
154
202
  {
155
203
  type: "button",
156
- onClick: () => N(r.ops, {
157
- label: r.candidates.length === 1 ? `Collapse ${r.candidates[0].label}` : `Collapse ${r.candidates.length} rarely opened sections`
204
+ onClick: () => y(i.ops, {
205
+ label: i.candidates.length === 1 ? `Collapse ${i.candidates[0].label}` : `Collapse ${i.candidates.length} rarely opened sections`
158
206
  }),
159
207
  className: "inline-flex h-7 items-center rounded-md bg-nvr-cyan px-2.5 text-[12px] font-medium text-white transition-opacity hover:opacity-90",
160
- children: r.candidates.length === 1 ? "Start it collapsed" : `Start all ${r.candidates.length} collapsed`
208
+ children: i.candidates.length === 1 ? "Start it collapsed" : `Start all ${i.candidates.length} collapsed`
161
209
  }
162
210
  ),
163
211
  /* @__PURE__ */ t(
164
212
  "button",
165
213
  {
166
214
  type: "button",
167
- onClick: () => F(!0),
215
+ onClick: () => W(!0),
168
216
  className: "inline-flex h-7 items-center rounded-md px-2 text-[12px] text-slate-500 transition-colors hover:bg-muted hover:text-slate-800 dark:text-muted-foreground dark:hover:text-foreground",
169
217
  children: "Not now"
170
218
  }
@@ -173,15 +221,15 @@ function ge({
173
221
  ]
174
222
  }
175
223
  ),
176
- /* @__PURE__ */ s("p", { className: `${d} pt-1`, children: [
224
+ /* @__PURE__ */ s("p", { className: `${o} pt-1`, children: [
177
225
  "A section counts as opened when someone expands it, clicks in it or types in it. Last",
178
226
  " ",
179
227
  (a == null ? void 0 : a.days) ?? 30,
180
228
  " days."
181
229
  ] }),
182
- /* @__PURE__ */ t("p", { className: i, children: "Suggested sections" }),
183
- T ? /* @__PURE__ */ t("p", { className: d, children: "Loading…" }) : w.length === 0 ? /* @__PURE__ */ t("p", { className: d, children: "No suggestions. Fields people change together already sit together on this form." }) : /* @__PURE__ */ t("ul", { "data-usage-suggestions": "", className: "flex flex-col gap-2 px-3", children: w.map((e) => {
184
- const l = e.fields.map(y), x = l.length > 1 ? `${l.slice(0, -1).join(", ")} and ${l[l.length - 1]}` : l[0], I = [...new Set(Object.values(e.from).map(g))];
230
+ /* @__PURE__ */ t("p", { className: c, children: "Suggested sections" }),
231
+ A ? /* @__PURE__ */ t("p", { className: o, children: "Loading…" }) : _.length === 0 ? /* @__PURE__ */ t("p", { className: o, children: "No suggestions. Fields people change together already sit together on this form." }) : /* @__PURE__ */ t("ul", { "data-usage-suggestions": "", className: "flex flex-col gap-2 px-3", children: _.map((e) => {
232
+ const l = e.fields.map(N), f = l.length > 1 ? `${l.slice(0, -1).join(", ")} and ${l[l.length - 1]}` : l[0], B = [...new Set(Object.values(e.from).map(x))];
185
233
  return /* @__PURE__ */ s(
186
234
  "li",
187
235
  {
@@ -189,11 +237,11 @@ function ge({
189
237
  className: "flex flex-col gap-2 rounded-lg border border-slate-200 bg-white p-2.5 dark:border-border dark:bg-card",
190
238
  children: [
191
239
  /* @__PURE__ */ s("p", { className: "m-0 text-[12px] leading-snug text-slate-700 dark:text-foreground", children: [
192
- /* @__PURE__ */ t(z, { className: "mr-1 inline h-3.5 w-3.5 align-[-2px] text-slate-400" }),
240
+ /* @__PURE__ */ t(Q, { className: "mr-1 inline h-3.5 w-3.5 align-[-2px] text-slate-400" }),
193
241
  "People usually change ",
194
- /* @__PURE__ */ t("b", { className: "font-semibold", children: x }),
242
+ /* @__PURE__ */ t("b", { className: "font-semibold", children: f }),
195
243
  " together, but they sit in ",
196
- I.join(" and "),
244
+ B.join(" and "),
197
245
  "."
198
246
  ] }),
199
247
  /* @__PURE__ */ s("div", { className: "flex items-center gap-1.5", children: [
@@ -201,11 +249,11 @@ function ge({
201
249
  "button",
202
250
  {
203
251
  type: "button",
204
- onClick: () => N(e.ops, { label: `Move ${x} into ${g(e.target)}` }),
252
+ onClick: () => y(e.ops, { label: `Move ${f} into ${x(e.target)}` }),
205
253
  className: "inline-flex h-7 items-center rounded-md bg-nvr-cyan px-2.5 text-[12px] font-medium text-white transition-opacity hover:opacity-90",
206
254
  children: [
207
255
  "Move into ",
208
- g(e.target)
256
+ x(e.target)
209
257
  ]
210
258
  }
211
259
  ),
@@ -213,7 +261,7 @@ function ge({
213
261
  "button",
214
262
  {
215
263
  type: "button",
216
- onClick: () => R((K) => new Set(K).add(e.id)),
264
+ onClick: () => G((H) => new Set(H).add(e.id)),
217
265
  className: "inline-flex h-7 items-center rounded-md px-2 text-[12px] text-slate-500 transition-colors hover:bg-muted hover:text-slate-800 dark:text-muted-foreground dark:hover:text-foreground",
218
266
  children: "Not now"
219
267
  }
@@ -224,9 +272,9 @@ function ge({
224
272
  e.id
225
273
  );
226
274
  }) }),
227
- /* @__PURE__ */ t(J, { bundle: n, collection: m, onOpenLayout: D })
275
+ /* @__PURE__ */ t(X, { bundle: r, collection: p, onOpenLayout: F })
228
276
  ] });
229
277
  }
230
278
  export {
231
- ge as UsagePanel
279
+ Le as UsagePanel
232
280
  };
@@ -0,0 +1,130 @@
1
+ import { jsx as g, jsxs as k } from "react/jsx-runtime";
2
+ import { useQuery as x } from "@tanstack/react-query";
3
+ import { useMemo as f, useCallback as S, useSyncExternalStore as w } from "react";
4
+ import { useNivaroClient as y } from "../../../context.js";
5
+ import { get as R } from "../../../lib/commands.js";
6
+ import { cn as T } from "../../../lib/utils.js";
7
+ import { usageKeys as $ } from "./usage-api.js";
8
+ function L(e, r = !0) {
9
+ const t = y();
10
+ return x({
11
+ queryKey: $.refusals(e ?? 0),
12
+ queryFn: () => t.request(R(`/layout-usage/${e}/refusals`)).then((n) => (n == null ? void 0 : n.data) ?? null),
13
+ enabled: r && e != null,
14
+ staleTime: 5 * 6e4,
15
+ retry: !1
16
+ });
17
+ }
18
+ const q = 0.05, E = 0.2, N = 5;
19
+ function p(e) {
20
+ return e >= E ? "high" : e >= q ? "some" : "none";
21
+ }
22
+ function m(e) {
23
+ const r = e * 100;
24
+ return r > 0 && r < 1 ? "<1%" : `${Math.round(r)}%`;
25
+ }
26
+ function v(e, r, t) {
27
+ const n = [
28
+ e.required > 0 ? `${e.required} left empty while required` : null,
29
+ e.invalid > 0 ? `${e.invalid} broke another rule` : null
30
+ ].filter(Boolean);
31
+ return `Refused ${e.refusals.toLocaleString()} of ${r.toLocaleString()} saves in ${t} days: ${n.join(", ")}`;
32
+ }
33
+ const F = {
34
+ none: "",
35
+ some: "bg-amber-50/70 ring-1 ring-amber-300 dark:bg-amber-500/10 dark:ring-amber-500/40",
36
+ high: "bg-red-50/70 ring-1 ring-red-300 dark:bg-red-500/10 dark:ring-red-500/40"
37
+ }, M = {
38
+ none: "bg-white text-slate-500 ring-slate-200 dark:bg-card dark:text-muted-foreground dark:ring-border",
39
+ some: "bg-amber-50 text-amber-800 ring-amber-200 dark:bg-amber-500/15 dark:text-amber-200 dark:ring-amber-500/30",
40
+ high: "bg-red-50 text-red-700 ring-red-200 dark:bg-red-500/15 dark:text-red-200 dark:ring-red-500/30"
41
+ };
42
+ function A({
43
+ f: e,
44
+ attempts: r,
45
+ days: t
46
+ }) {
47
+ const n = p(e.rate);
48
+ return /* @__PURE__ */ k(
49
+ "span",
50
+ {
51
+ "data-design-refusal": m(e.rate),
52
+ "data-refusal-tone": n,
53
+ "data-tip": v(e, r, t),
54
+ className: T(
55
+ "pointer-events-auto inline-flex items-center rounded px-1 text-[10.5px] tabular-nums ring-1",
56
+ M[n]
57
+ ),
58
+ children: [
59
+ m(e.rate),
60
+ " refused",
61
+ /* @__PURE__ */ g("span", { className: "sr-only", children: " saves on this field" })
62
+ ]
63
+ }
64
+ );
65
+ }
66
+ function C(e, r) {
67
+ if (!e || e.attempts < N || r.kind !== "field" && r.kind !== "header") return null;
68
+ const t = e.fields.find((n) => n.field === r.field);
69
+ return !t || t.refusals === 0 ? null : {
70
+ className: F[p(t.rate)],
71
+ chip: /* @__PURE__ */ g(A, { f: t, attempts: e.attempts, days: e.days })
72
+ };
73
+ }
74
+ const b = "nvr:layout-editor:show-refusals", i = /* @__PURE__ */ new Set();
75
+ let a = null;
76
+ function I() {
77
+ if (a != null) return a;
78
+ try {
79
+ a = window.localStorage.getItem(b) === "1";
80
+ } catch {
81
+ a = !1;
82
+ }
83
+ return a;
84
+ }
85
+ function _(e) {
86
+ a = e;
87
+ try {
88
+ window.localStorage.setItem(b, e ? "1" : "0");
89
+ } catch {
90
+ }
91
+ for (const r of i) r();
92
+ }
93
+ function j() {
94
+ return [w(
95
+ (r) => (i.add(r), () => i.delete(r)),
96
+ I,
97
+ () => !1
98
+ ), _];
99
+ }
100
+ function G(e) {
101
+ const [r, t] = j(), { data: n = null } = L(e, r), o = f(
102
+ () => r && n ? (s) => C(n, s) : null,
103
+ [r, n]
104
+ ), u = f(() => /* @__PURE__ */ new WeakMap(), [o]), h = S(
105
+ (s) => {
106
+ if (!o) return s;
107
+ if (!s) return o;
108
+ const l = u.get(s);
109
+ if (l) return l;
110
+ const d = (c) => s(c) ?? o(c);
111
+ return u.set(s, d), d;
112
+ },
113
+ [o, u]
114
+ );
115
+ return { on: r, setOn: t, data: n, wrap: h };
116
+ }
117
+ export {
118
+ E as REFUSAL_HIGH,
119
+ q as REFUSAL_LOW,
120
+ N as REFUSAL_MIN_ATTEMPTS,
121
+ A as RefusalChip,
122
+ C as refusalOverlayFor,
123
+ m as refusalPct,
124
+ v as refusalTip,
125
+ p as refusalTone,
126
+ _ as setShowRefusals,
127
+ G as useRefusalLens,
128
+ L as useSaveRefusals,
129
+ j as useShowRefusals
130
+ };
@@ -1,22 +1,23 @@
1
1
  import { useQuery as a } from "@tanstack/react-query";
2
- import { useState as d, useCallback as f, useMemo as y } from "react";
2
+ import { useState as f, useCallback as d, useMemo as y } from "react";
3
3
  import { useNivaroClient as s } from "../../../context.js";
4
4
  import { get as u } from "../../../lib/commands.js";
5
- const i = {
5
+ const l = {
6
6
  fill: (e) => ["layout-editor", "usage", "fill", e],
7
7
  today: (e) => ["layout-editor", "usage", "today", e],
8
8
  friction: (e) => ["layout-editor", "usage", "friction", e],
9
- coChanges: (e) => ["layout-editor", "usage", "co-changes", e]
9
+ coChanges: (e) => ["layout-editor", "usage", "co-changes", e],
10
+ refusals: (e) => ["layout-editor", "usage", "refusals", e]
10
11
  };
11
12
  function $(e, n = !0) {
12
13
  const r = s();
13
14
  return a({
14
- queryKey: i.fill(e),
15
+ queryKey: l.fill(e),
15
16
  queryFn: async () => {
16
17
  const t = await r.request(u(`/data-model/${encodeURIComponent(e)}/field-usage`)), o = t == null ? void 0 : t.data;
17
18
  return {
18
19
  total: Number((o == null ? void 0 : o.total) ?? 0),
19
- pct: new Map(((o == null ? void 0 : o.fields) ?? []).map((l) => [l.field, Number(l.pct)]))
20
+ pct: new Map(((o == null ? void 0 : o.fields) ?? []).map((i) => [i.field, Number(i.pct)]))
20
21
  };
21
22
  },
22
23
  enabled: n && !!e,
@@ -30,7 +31,7 @@ function q(e, n) {
30
31
  function F(e, n = !0) {
31
32
  const r = s();
32
33
  return a({
33
- queryKey: i.today(e ?? 0),
34
+ queryKey: l.today(e ?? 0),
34
35
  queryFn: () => r.request(u(`/layout-usage/${e}/today`)).then((t) => (t == null ? void 0 : t.data) ?? null),
35
36
  enabled: n && e != null,
36
37
  staleTime: 6e4,
@@ -48,7 +49,7 @@ function w(e) {
48
49
  function C(e, n = !0) {
49
50
  const r = s();
50
51
  return a({
51
- queryKey: i.friction(e),
52
+ queryKey: l.friction(e),
52
53
  queryFn: () => r.request(u(`/layout-usage/${e}/friction`)).then((t) => (t == null ? void 0 : t.data) ?? null),
53
54
  enabled: n,
54
55
  staleTime: 5 * 6e4,
@@ -58,7 +59,7 @@ function C(e, n = !0) {
58
59
  function T(e, n = !0) {
59
60
  const r = s();
60
61
  return a({
61
- queryKey: i.coChanges(e),
62
+ queryKey: l.coChanges(e),
62
63
  queryFn: () => r.request(
63
64
  u(`/layout-usage/co-changes/${encodeURIComponent(e)}`)
64
65
  ).then((t) => (t == null ? void 0 : t.data) ?? null),
@@ -76,13 +77,13 @@ function b(e) {
76
77
  }
77
78
  const c = "nvr:layout-editor:show-fill";
78
79
  function K() {
79
- const [e, n] = d(() => {
80
+ const [e, n] = f(() => {
80
81
  try {
81
82
  return window.localStorage.getItem(c) === "1";
82
83
  } catch {
83
84
  return !1;
84
85
  }
85
- }), r = f((t) => {
86
+ }), r = d((t) => {
86
87
  n(t);
87
88
  try {
88
89
  window.localStorage.setItem(c, t ? "1" : "0");
@@ -94,7 +95,7 @@ function K() {
94
95
  export {
95
96
  q as fillText,
96
97
  b as formatDuration,
97
- i as usageKeys,
98
+ l as usageKeys,
98
99
  w as usageTodayText,
99
100
  T as useCoChanges,
100
101
  $ as useFieldFillRates,