@nivaro/react 0.3.23 → 0.3.25

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 (28) hide show
  1. package/dist/hooks/useSessionRecorder.js +1 -1
  2. package/dist/index.d.ts +50 -26
  3. package/dist/shared/components/FilePreviewLightbox.js +56 -35
  4. package/dist/shared/components/ItemEditForm.js +4309 -4263
  5. package/dist/shared/components/PageRenderer.js +63 -62
  6. package/dist/shared/components/ProfileView.js +555 -520
  7. package/dist/shared/components/RecordReadView.js +229 -229
  8. package/dist/shared/components/ReportView.js +123 -120
  9. package/dist/shared/components/ai/AiMarkdown.js +168 -148
  10. package/dist/shared/components/chat/RoomParts.js +120 -119
  11. package/dist/shared/components/chat/chat-core.js +228 -224
  12. package/dist/shared/components/item-edit/FieldRenderer.js +333 -333
  13. package/dist/shared/components/item-edit/FieldRow.js +413 -394
  14. package/dist/shared/components/item-edit/GroupSection.js +637 -635
  15. package/dist/shared/components/item-edit/InlineGridField.js +89 -90
  16. package/dist/shared/components/item-edit/QuickPicker.js +9 -9
  17. package/dist/shared/components/item-edit/RawEditSheet.js +80 -79
  18. package/dist/shared/components/item-edit/RowMatchPanel.js +1 -1
  19. package/dist/shared/components/item-edit/m2m-invalidate.js +19 -0
  20. package/dist/shared/components/item-edit/record-lookups.js +15 -0
  21. package/dist/shared/components/item-edit/render-stability.js +147 -0
  22. package/dist/shared/components/layout-editor/FormMapButton.js +5 -4
  23. package/dist/shared/components/notifications/DeliveryChips.js +27 -17
  24. package/dist/shared/components/profile/TeamsConnect.js +71 -0
  25. package/dist/shared/components/profile/primitives.js +4 -4
  26. package/dist/shared/lib/safe-href.js +10 -0
  27. package/dist/shared/lib/socket-realtime.js +107 -78
  28. package/package.json +6 -6
@@ -0,0 +1,71 @@
1
+ import { jsxs as t, Fragment as m, jsx as e } from "react/jsx-runtime";
2
+ import { useQueryClient as l, useMutation as c } from "@tanstack/react-query";
3
+ import { useState as u } from "react";
4
+ import { useNivaroClient as f } from "../../context.js";
5
+ import { del as x } from "../../lib/commands.js";
6
+ import { Popover as p, PopoverTrigger as g, PopoverContent as h } from "../ui/popover.js";
7
+ import { ConfirmButton as k } from "./primitives.js";
8
+ function P({ linked: a, linkedAt: r }) {
9
+ const i = f(), d = l(), [s, n] = u(!1), o = c({
10
+ mutationFn: () => (n(!1), i.request(x("/teams/link"))),
11
+ onError: () => n(!0),
12
+ onSettled: () => {
13
+ d.invalidateQueries({ queryKey: ["nvr-profile-teams"] });
14
+ }
15
+ });
16
+ return a ? /* @__PURE__ */ t(m, { children: [
17
+ /* @__PURE__ */ t(
18
+ "span",
19
+ {
20
+ className: "inline-flex flex-wrap items-center justify-center gap-1 text-[10.5px] font-normal normal-case tracking-normal text-emerald-700 dark:text-emerald-400",
21
+ "data-teams-connected": "",
22
+ title: r ? `Connected ${new Date(r).toLocaleString()}` : void 0,
23
+ children: [
24
+ "Connected ·",
25
+ " ",
26
+ /* @__PURE__ */ e(
27
+ k,
28
+ {
29
+ onConfirm: () => o.mutate(),
30
+ confirmLabel: "Disconnect Teams?",
31
+ disabled: o.isPending,
32
+ className: "font-medium text-slate-500 underline decoration-dotted underline-offset-2 hover:text-slate-800 dark:text-muted-foreground dark:hover:text-foreground",
33
+ armedClassName: "font-medium text-red-600 dark:text-red-400",
34
+ children: "Disconnect"
35
+ }
36
+ )
37
+ ]
38
+ }
39
+ ),
40
+ s && /* @__PURE__ */ e(
41
+ "p",
42
+ {
43
+ role: "alert",
44
+ "data-teams-disconnect-error": "",
45
+ className: "mt-0.5 text-[10.5px] font-normal normal-case tracking-normal text-red-600 dark:text-red-400",
46
+ children: "Could not disconnect Teams. Try again; if it keeps failing, ask an admin."
47
+ }
48
+ )
49
+ ] }) : /* @__PURE__ */ t(p, { children: [
50
+ /* @__PURE__ */ e(g, { asChild: !0, children: /* @__PURE__ */ e(
51
+ "button",
52
+ {
53
+ type: "button",
54
+ "data-teams-connect": "",
55
+ className: "rounded border border-slate-200 px-1.5 py-0.5 text-[10.5px] font-medium normal-case tracking-normal text-slate-600 hover:border-slate-400 hover:text-slate-800 dark:border-border dark:text-muted-foreground dark:hover:text-foreground",
56
+ children: "Connect Teams"
57
+ }
58
+ ) }),
59
+ /* @__PURE__ */ e(
60
+ h,
61
+ {
62
+ align: "end",
63
+ className: "w-64 text-[12px] font-normal normal-case tracking-normal text-slate-600 dark:text-muted-foreground",
64
+ children: "Open Nivaro in Teams and say hi, or sign in with Microsoft once, then turn categories on."
65
+ }
66
+ )
67
+ ] });
68
+ }
69
+ export {
70
+ P as TeamsConnect
71
+ };
@@ -1,5 +1,5 @@
1
1
  import { jsxs as c, jsx as a, Fragment as x } from "react/jsx-runtime";
2
- import { Children as f, useState as p, useEffect as g } from "react";
2
+ import { useState as f, useEffect as p, Children as g } from "react";
3
3
  import { useNavigation as b } from "../../context.js";
4
4
  import { cn as o } from "../../lib/utils.js";
5
5
  import { UserAvatar as k } from "../UserAvatar.js";
@@ -121,8 +121,8 @@ function P({
121
121
  title: l,
122
122
  disabled: s
123
123
  }) {
124
- const [i, m] = p(!1);
125
- return g(() => {
124
+ const [i, m] = f(!1);
125
+ return p(() => {
126
126
  if (!i) return;
127
127
  const u = window.setTimeout(() => m(!1), 3e3);
128
128
  return () => window.clearTimeout(u);
@@ -148,7 +148,7 @@ function T({ label: e, children: t }) {
148
148
  ] });
149
149
  }
150
150
  function B({ children: e, cols: t = 3 }) {
151
- const r = f.toArray(e).filter(Boolean).length, n = t === 3 ? 6 : t, d = (n - r % n) % n;
151
+ const r = g.toArray(e).filter(Boolean).length, n = t === 3 ? 6 : t, d = (n - r % n) % n;
152
152
  return /* @__PURE__ */ c(
153
153
  "div",
154
154
  {
@@ -0,0 +1,10 @@
1
+ function n(t) {
2
+ return /^\/(?![/\\])/.test(t) && !/[\s\\]/.test(t);
3
+ }
4
+ function e(t) {
5
+ return /^https?:\/\/\S+$/i.test(t) ? "external" : n(t) ? "internal" : null;
6
+ }
7
+ export {
8
+ n as isInAppPath,
9
+ e as safeLinkKind
10
+ };
@@ -1,11 +1,32 @@
1
- var N = Object.defineProperty;
2
- var U = (i, n, r) => n in i ? N(i, n, { enumerable: !0, configurable: !0, writable: !0, value: r }) : i[n] = r;
3
- var _ = (i, n, r) => U(i, typeof n != "symbol" ? n + "" : n, r);
4
- import { bindClientChaosSocket as k } from "./client-chaos.js";
5
- import { deliveryAckFor as L } from "./delivery-ack.js";
6
- import { forgetSettledReads as y } from "./request-gate.js";
7
- import { createRoomGapTracker as M, refetchEventsFor as W } from "./room-gaps.js";
8
- const p = {
1
+ var y = Object.defineProperty;
2
+ var I = (i, n, r) => n in i ? y(i, n, { enumerable: !0, configurable: !0, writable: !0, value: r }) : i[n] = r;
3
+ var N = (i, n, r) => I(i, typeof n != "symbol" ? n + "" : n, r);
4
+ import { bindClientChaosSocket as J } from "./client-chaos.js";
5
+ import { deliveryAckFor as O } from "./delivery-ack.js";
6
+ import { forgetSettledReads as L } from "./request-gate.js";
7
+ import { createRoomGapTracker as W, refetchEventsFor as $ } from "./room-gaps.js";
8
+ const U = 3e3;
9
+ function x(i = U) {
10
+ const n = /* @__PURE__ */ new Map();
11
+ return (r, d) => {
12
+ var g;
13
+ if (!(i > 0)) {
14
+ d();
15
+ return;
16
+ }
17
+ const a = n.has(r);
18
+ if (n.set(r, d), a) return;
19
+ const l = setTimeout(
20
+ () => {
21
+ const f = n.get(r);
22
+ n.delete(r), f == null || f();
23
+ },
24
+ Math.floor(Math.random() * i)
25
+ );
26
+ (g = l.unref) == null || g.call(l);
27
+ };
28
+ }
29
+ const j = {
9
30
  catchupFull: "nvr:catchup-full",
10
31
  accessChanged: "nvr:access-changed",
11
32
  configUpdate: "nvr:config-update",
@@ -13,7 +34,7 @@ const p = {
13
34
  };
14
35
  class C {
15
36
  constructor(n, r) {
16
- _(this, "refs", /* @__PURE__ */ new Map());
37
+ N(this, "refs", /* @__PURE__ */ new Map());
17
38
  this.join = n, this.leave = r;
18
39
  }
19
40
  add(n) {
@@ -31,154 +52,162 @@ class C {
31
52
  return [...this.refs.keys()];
32
53
  }
33
54
  }
34
- function G(i) {
35
- let n = null, r = 0, j = !1, l = null, a = { state: "live", transport: null, refreshed: null, resumed: null };
36
- const T = /* @__PURE__ */ new Set(), v = (e) => {
55
+ function z(i) {
56
+ let n = null, r = 0, d = !1, a = null, l = { state: "live", transport: null, refreshed: null, resumed: null };
57
+ const g = /* @__PURE__ */ new Set(), f = (e) => {
37
58
  var o;
38
59
  return ((o = i.windowEvents) == null ? void 0 : o[e]) !== !1;
39
- }, g = (e) => {
40
- const o = { ...a, ...e };
41
- if (!(o.state === a.state && o.transport === a.transport && o.refreshed === a.refreshed && o.resumed === a.resumed)) {
42
- a = o;
43
- for (const t of T) t(a);
60
+ }, b = x(i.reactionJitterMs ?? U), A = /* @__PURE__ */ new Set(), p = (e) => {
61
+ const o = { ...l, ...e };
62
+ if (!(o.state === l.state && o.transport === l.transport && o.refreshed === l.refreshed && o.resumed === l.resumed)) {
63
+ l = o;
64
+ for (const t of g) t(l);
44
65
  }
45
- }, R = () => {
66
+ }, _ = () => {
46
67
  var e, o, t;
47
68
  return ((t = (o = (e = n == null ? void 0 : n.io) == null ? void 0 : e.engine) == null ? void 0 : o.transport) == null ? void 0 : t.name) ?? null;
48
- }, b = (e, o) => {
49
- l && clearTimeout(l), l = null, g({ state: "live", transport: R(), refreshed: e, resumed: o });
50
- }, u = (e, o) => {
51
- typeof window > "u" || window.dispatchEvent(new CustomEvent(e, { detail: o }));
69
+ }, E = (e, o) => {
70
+ a && clearTimeout(a), a = null, p({ state: "live", transport: _(), refreshed: e, resumed: o });
52
71
  }, h = (e, o) => {
72
+ typeof window > "u" || window.dispatchEvent(new CustomEvent(e, { detail: o }));
73
+ }, m = (e, o) => {
53
74
  n != null && n.connected && n.emit(e, o);
54
- }, E = (e, o) => {
75
+ }, F = (e, o) => {
55
76
  var t;
56
77
  for (const s of ((t = n == null ? void 0 : n.listeners) == null ? void 0 : t.call(n, e)) ?? []) s(o);
57
- }, F = (e) => {
58
- for (const o of W(e)) E(o.event, o.payload);
59
- u("nvr:realtime-stale", { rooms: e });
60
- }, m = M({
61
- request: (e) => h("catchup:room", e),
62
- dispatch: E,
63
- refetch: F
64
- }), q = new C(
78
+ }, M = (e) => {
79
+ for (const o of $(e)) F(o.event, o.payload);
80
+ h("nvr:realtime-stale", { rooms: e });
81
+ }, w = W({
82
+ request: (e) => m("catchup:room", e),
83
+ dispatch: F,
84
+ refetch: M
85
+ }), R = new C(
65
86
  (e) => {
66
- m.reset([`collection:${e}`]), h("collection:join", { collection: e });
87
+ w.reset([`collection:${e}`]), m("collection:join", { collection: e });
67
88
  },
68
89
  (e) => {
69
- m.reset([`collection:${e}`]), h("collection:leave", { collection: e });
90
+ w.reset([`collection:${e}`]), m("collection:leave", { collection: e });
70
91
  }
71
- ), A = new C(
92
+ ), S = new C(
72
93
  // The Traffic Map page reads batched frames (api traffic-map-batch.ts); twin: admin
73
94
  // lib/socket.ts watchJoinPayload.
74
- (e) => h("admin:join", e === "traffic-map" ? { room: e, frames: "batch" } : { room: e }),
75
- (e) => h("admin:leave", { room: e })
76
- ), S = new C(
77
- (e) => h("ops:join", { topic: e }),
78
- (e) => h("ops:leave", { topic: e })
79
- ), w = () => {
95
+ (e) => m("admin:join", e === "traffic-map" ? { room: e, frames: "batch" } : { room: e }),
96
+ (e) => m("admin:leave", { room: e })
97
+ ), q = new C(
98
+ (e) => m("ops:join", { topic: e }),
99
+ (e) => m("ops:leave", { topic: e })
100
+ ), T = () => {
80
101
  var o;
81
102
  if (n) return n;
82
103
  const e = typeof i.socket == "function" ? i.socket() : i.socket;
83
- return n = e, k(e), j = e.connected, e.on("auth:ok", () => {
84
- if (m.reset(), q.rejoinAll(), A.rejoinAll(), S.rejoinAll(), j && r > 0) {
104
+ return n = e, J(e), d = e.connected, e.on("auth:ok", () => {
105
+ if (w.reset(), R.rejoinAll(), S.rejoinAll(), q.rejoinAll(), d && r > 0) {
85
106
  const t = r;
86
- g({ transport: R() }), l && clearTimeout(l), l = setTimeout(() => b(null, "unverified"), i.catchupTimeoutMs ?? 8e3), e.emit("catchup", { cursor: t });
107
+ p({ transport: _() }), a && clearTimeout(a), a = setTimeout(() => E(null, "unverified"), i.catchupTimeoutMs ?? 8e3), e.emit("catchup", { cursor: t });
87
108
  } else
88
- g({
109
+ p({
89
110
  state: "live",
90
- transport: R(),
111
+ transport: _(),
91
112
  refreshed: null,
92
- resumed: j && a.state === "reconnecting" ? "unverified" : null
113
+ resumed: d && l.state === "reconnecting" ? "unverified" : null
93
114
  });
94
- j = !0;
115
+ d = !0;
95
116
  }), e.on("catchup:cursor", (t) => {
96
117
  r === 0 && typeof (t == null ? void 0 : t.seq) == "number" && t.seq > 0 && (r = t.seq);
97
118
  }), e.on("disconnect", () => {
98
- l && clearTimeout(l), l = null, g({ state: "reconnecting", refreshed: null });
99
- }), e.on("connect_error", () => g({ state: "reconnecting", refreshed: null })), (o = e.onAny) == null || o.call(e, (t, s) => {
119
+ a && clearTimeout(a), a = null, p({ state: "reconnecting", refreshed: null });
120
+ }), e.on("connect_error", () => p({ state: "reconnecting", refreshed: null })), (o = e.onAny) == null || o.call(e, (t, s) => {
100
121
  const c = s == null ? void 0 : s._seq;
101
- typeof c == "number" && c > r && (r = c), m.observe(s);
102
- const d = L(s);
103
- d && e.emit("rt:ack", d);
104
- }), e.on("catchup:room:events", (t) => m.replayed(t)), e.on("realtime:stale", (t) => {
122
+ typeof c == "number" && c > r && (r = c), w.observe(s);
123
+ const v = O(s);
124
+ v && e.emit("rt:ack", v);
125
+ }), e.on("catchup:room:events", (t) => w.replayed(t)), e.on("realtime:stale", (t) => {
105
126
  const s = Array.isArray(t == null ? void 0 : t.rooms) ? t.rooms.filter((c) => typeof c == "string") : [];
106
- m.reset(s), t != null && t.all ? (v("catchupFull") && u(p.catchupFull, {}), u("nvr:realtime-stale", { rooms: s, all: !0 })) : s.length && F(s);
127
+ w.reset(s), t != null && t.all ? (f("catchupFull") && h(j.catchupFull, {}), h("nvr:realtime-stale", { rooms: s, all: !0 })) : s.length && M(s);
107
128
  }), e.on(
108
129
  "catchup:events",
109
130
  (t) => {
110
131
  var s;
111
132
  for (const c of (t == null ? void 0 : t.events) ?? []) {
112
133
  c.seq > r && (r = c.seq);
113
- for (const d of ((s = e.listeners) == null ? void 0 : s.call(e, c.event)) ?? []) d({ ...c.payload, _seq: c.seq });
134
+ for (const v of ((s = e.listeners) == null ? void 0 : s.call(e, c.event)) ?? []) v({ ...c.payload, _seq: c.seq });
114
135
  }
115
- b(null, "replayed");
136
+ E(null, "replayed");
116
137
  }
117
138
  ), e.on("catchup:full", (t) => {
118
- v("catchupFull") && u(p.catchupFull, {}), b(
139
+ f("catchupFull") && b("full", () => h(j.catchupFull, {})), E(
119
140
  (t == null ? void 0 : t.reason) === "slow-consumer" ? "Live updates resumed. This tab fell too far behind to send each change, so the lists and records on this page were reloaded." : "Live updates resumed. The connection was gone too long to replay each change, so the lists and records on this page were reloaded.",
120
141
  "reloaded"
121
142
  );
122
143
  }), e.on("access:changed", (t) => {
123
144
  var s;
124
- y(), (s = t == null ? void 0 : t.rooms_left) != null && s.length && v("accessChanged") && u(p.accessChanged, t);
145
+ if (L(), (s = t == null ? void 0 : t.rooms_left) != null && s.length && f("accessChanged")) {
146
+ for (const c of t.rooms_left) A.add(c);
147
+ b("access", () => {
148
+ const c = [...A];
149
+ A.clear(), h(j.accessChanged, { rooms_left: c });
150
+ });
151
+ }
125
152
  }), e.on("config:update", (t) => {
126
- y(), v("configUpdate") && u(p.configUpdate, t ?? {});
153
+ L(), f("configUpdate") && h(j.configUpdate, t ?? {});
127
154
  }), e.on("client:force-refresh", (t) => {
128
- v("forceRefresh") && u(p.forceRefresh, t ?? {});
155
+ f("forceRefresh") && h(j.forceRefresh, t ?? {});
129
156
  }), e;
130
157
  };
131
158
  return {
132
159
  subscribeCollections(e, o) {
133
- const t = w(), s = [...new Set(e.filter(Boolean))];
134
- for (const f of s) q.add(f);
135
- const c = (f) => {
136
- f != null && f.collection && s.includes(f.collection) && o(f);
160
+ const t = T(), s = [...new Set(e.filter(Boolean))];
161
+ for (const u of s) R.add(u);
162
+ const c = (u) => {
163
+ u != null && u.collection && s.includes(u.collection) && o(u);
137
164
  };
138
165
  t.on("collection:update", c);
139
- let d = !1;
166
+ let v = !1;
140
167
  return () => {
141
- if (!d) {
142
- d = !0, t.off("collection:update", c);
143
- for (const f of s) q.remove(f);
168
+ if (!v) {
169
+ v = !0, t.off("collection:update", c);
170
+ for (const u of s) R.remove(u);
144
171
  }
145
172
  };
146
173
  },
147
174
  on(e, o) {
148
- const t = w();
175
+ const t = T();
149
176
  return t.on(e, o), () => {
150
177
  t.off(e, o);
151
178
  };
152
179
  },
153
180
  emit(e, o) {
154
- if (w(), e === "ops:join" || e === "ops:leave") {
181
+ if (T(), e === "ops:join" || e === "ops:leave") {
155
182
  const t = String((o == null ? void 0 : o.topic) ?? "");
156
183
  if (!t) return;
157
- e === "ops:join" ? S.add(t) : S.remove(t);
184
+ e === "ops:join" ? q.add(t) : q.remove(t);
158
185
  return;
159
186
  }
160
187
  n == null || n.emit(e, o);
161
188
  },
162
189
  status() {
163
- return a;
190
+ return l;
164
191
  },
165
192
  onStatus(e) {
166
- return w(), T.add(e), () => {
167
- T.delete(e);
193
+ return T(), g.add(e), () => {
194
+ g.delete(e);
168
195
  };
169
196
  },
170
197
  joinWatch(e) {
171
- w(), A.add(e);
198
+ T(), S.add(e);
172
199
  let o = !1;
173
200
  return () => {
174
- o || (o = !0, A.remove(e));
201
+ o || (o = !0, S.remove(e));
175
202
  };
176
203
  },
177
204
  held() {
178
- return { collections: q.keys(), watch: A.keys(), ops: S.keys() };
205
+ return { collections: R.keys(), watch: S.keys(), ops: q.keys() };
179
206
  }
180
207
  };
181
208
  }
182
209
  export {
183
- G as createSocketRealtime
210
+ U as REACTION_JITTER_MS,
211
+ x as createJitteredReaction,
212
+ z as createSocketRealtime
184
213
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nivaro/react",
3
- "version": "0.3.23",
3
+ "version": "0.3.25",
4
4
  "description": "React hooks and components for Nivaro CMS forms",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -77,11 +77,11 @@
77
77
  "@radix-ui/react-switch": "^1.2.6",
78
78
  "@radix-ui/react-tabs": "^1.1.14",
79
79
  "@radix-ui/react-tooltip": "^1.2.7",
80
- "@tiptap/extension-link": "^3.26.0",
81
- "@tiptap/extension-placeholder": "^3.26.0",
82
- "@tiptap/extension-underline": "^3.26.0",
83
- "@tiptap/react": "^3.26.0",
84
- "@tiptap/starter-kit": "^3.26.0",
80
+ "@tiptap/extension-link": "^3.32.0",
81
+ "@tiptap/extension-placeholder": "^3.32.0",
82
+ "@tiptap/extension-underline": "^3.32.0",
83
+ "@tiptap/react": "^3.32.0",
84
+ "@tiptap/starter-kit": "^3.32.0",
85
85
  "class-variance-authority": "^0.7.0",
86
86
  "clsx": "^2.0.0",
87
87
  "cmdk": "^1.0.0",