@nivaro/react 0.3.24 → 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.
- package/dist/hooks/useSessionRecorder.js +1 -1
- package/dist/index.d.ts +45 -26
- package/dist/shared/components/FilePreviewLightbox.js +56 -35
- package/dist/shared/components/ItemEditForm.js +4309 -4263
- package/dist/shared/components/PageRenderer.js +63 -62
- package/dist/shared/components/RecordReadView.js +229 -229
- package/dist/shared/components/ReportView.js +123 -120
- package/dist/shared/components/ai/AiMarkdown.js +168 -148
- package/dist/shared/components/chat/RoomParts.js +120 -119
- package/dist/shared/components/chat/chat-core.js +228 -224
- package/dist/shared/components/item-edit/FieldRenderer.js +333 -333
- package/dist/shared/components/item-edit/FieldRow.js +413 -394
- package/dist/shared/components/item-edit/GroupSection.js +637 -635
- package/dist/shared/components/item-edit/InlineGridField.js +89 -90
- package/dist/shared/components/item-edit/QuickPicker.js +9 -9
- package/dist/shared/components/item-edit/RawEditSheet.js +80 -79
- package/dist/shared/components/item-edit/RowMatchPanel.js +1 -1
- package/dist/shared/components/item-edit/m2m-invalidate.js +19 -0
- package/dist/shared/components/item-edit/record-lookups.js +15 -0
- package/dist/shared/components/item-edit/render-stability.js +147 -0
- package/dist/shared/components/layout-editor/FormMapButton.js +5 -4
- package/dist/shared/lib/safe-href.js +10 -0
- package/dist/shared/lib/socket-realtime.js +107 -78
- package/package.json +6 -6
|
@@ -9,7 +9,7 @@ import { noteReplayEvent as _e, createReplayCheckoutState as De } from "../share
|
|
|
9
9
|
import { emitReplayTap as Te, setReplayRecorderMode as N, registerReplayCapture as qe, clipEndOffset as Le, withClipKind as Pe } from "../shared/lib/replay-capture.js";
|
|
10
10
|
import { instrumentConsole as Ne, instrumentRequests as Fe, redactSecrets as Me } from "../shared/lib/replay-context.js";
|
|
11
11
|
import { instrumentSocket as Oe } from "../shared/lib/replay-socket.js";
|
|
12
|
-
const Ue = "nivaro-react@0.3.
|
|
12
|
+
const Ue = "nivaro-react@0.3.25", j = 1e4, We = 150, He = 3e3, Ke = 600, $e = (n) => `nvr_replay_resume${n ? `:${n}` : ""}`, G = () => typeof navigator > "u" || navigator.onLine !== !1;
|
|
13
13
|
function Be(n) {
|
|
14
14
|
return typeof n.url != "string" || typeof fetch != "function" ? null : (f, a) => {
|
|
15
15
|
const u = typeof n.getToken == "function" ? n.getToken() : void 0, d = { "Content-Type": "application/json" };
|
package/dist/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { ListResponse } from '@nivaro/sdk';
|
|
|
13
13
|
import type { LucideIcon } from 'lucide-react';
|
|
14
14
|
import { ManagedUser } from '@nivaro/sdk';
|
|
15
15
|
import { MutableRefObject } from 'react';
|
|
16
|
+
import { NamedExoticComponent } from 'react';
|
|
16
17
|
import { NivaroClient } from '@nivaro/sdk';
|
|
17
18
|
import { Query } from '@nivaro/sdk';
|
|
18
19
|
import { QueryClient } from '@tanstack/react-query';
|
|
@@ -3070,30 +3071,32 @@ export declare interface FieldInterfaceProps {
|
|
|
3070
3071
|
itemId: string | null;
|
|
3071
3072
|
}
|
|
3072
3073
|
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3074
|
+
/** Memoized: rows pass it stable props, so a row re-render that changes
|
|
3075
|
+
* nothing about this field's control skips it. */
|
|
3076
|
+
export declare const FieldRenderer: NamedExoticComponent< {
|
|
3077
|
+
field: CMSField;
|
|
3078
|
+
value: unknown;
|
|
3079
|
+
onChange: (v: unknown) => void;
|
|
3080
|
+
relations: CMSRelation[];
|
|
3081
|
+
collection: string;
|
|
3082
|
+
itemId: string;
|
|
3083
|
+
cascadeFilter?: Record<string, unknown>;
|
|
3084
|
+
requiredParentLabel?: string | null;
|
|
3085
|
+
/** Cascade parents currently narrowing an M2O picker (see RelationCombobox). */
|
|
3086
|
+
narrowedBy?: {
|
|
3087
|
+
labels: string[];
|
|
3088
|
+
keys: string[];
|
|
3089
|
+
onClear?: (key: string) => void;
|
|
3090
|
+
};
|
|
3091
|
+
onCountChange?: (count: number) => void;
|
|
3092
|
+
displayOnly?: boolean;
|
|
3093
|
+
prefillParentId?: string;
|
|
3094
|
+
/** M2O pickers: a floating first option (see RelationCombobox `pinned`). */
|
|
3095
|
+
pinnedOption?: {
|
|
3096
|
+
id: unknown;
|
|
3097
|
+
tag?: string;
|
|
3098
|
+
} | null;
|
|
3099
|
+
}>;
|
|
3097
3100
|
|
|
3098
3101
|
export declare type FieldState = {
|
|
3099
3102
|
value: unknown;
|
|
@@ -5075,6 +5078,13 @@ export declare interface ItemEditFormProps {
|
|
|
5075
5078
|
/** Extra rows for the record's More menu, above Delete (e.g. a host's Edit layout).
|
|
5076
5079
|
* Rows that open something outside the menu set `data-nvr-menu-close`. */
|
|
5077
5080
|
moreMenuItems?: ReactNode;
|
|
5081
|
+
/** Which view the record shows (Summary or Edit) and the collection's Summary layout id
|
|
5082
|
+
* (null = none; Summary then renders the edit layout read-only). Hosts use it so their
|
|
5083
|
+
* Edit layout opens the layout the person is looking at. */
|
|
5084
|
+
onViewModeChange?: (view: {
|
|
5085
|
+
mode: 'summary' | 'edit';
|
|
5086
|
+
summaryLayoutId: number | null;
|
|
5087
|
+
}) => void;
|
|
5078
5088
|
/** Scroll to + flash a field on load (deep links: ?focus=<field>). */
|
|
5079
5089
|
focusField?: string | null;
|
|
5080
5090
|
/**
|
|
@@ -7308,8 +7318,10 @@ export declare function PretendDownSection({ scope, options }: {
|
|
|
7308
7318
|
|
|
7309
7319
|
/**
|
|
7310
7320
|
* Inline attachment preview — click a file, see it, without a download round
|
|
7311
|
-
* trip. Images
|
|
7312
|
-
*
|
|
7321
|
+
* trip. Images (SVG too, drawn by an img tag, which runs no script), PDFs,
|
|
7322
|
+
* video, audio and plain text render in an overlay; anything else (HTML, XML,
|
|
7323
|
+
* Office files…) gets an honest "no preview" panel with the Download action
|
|
7324
|
+
* front and center. Plain text loads in a fully sandboxed iframe.
|
|
7313
7325
|
*
|
|
7314
7326
|
* Plain portal to document.body at z-[130] (above popovers at 110 and the
|
|
7315
7327
|
* tooltip layer at 120). The asset URL is the same cookie/session-authed
|
|
@@ -9537,6 +9549,13 @@ export declare interface SocketRealtimeOptions {
|
|
|
9537
9549
|
windowEvents?: Partial<Record<SocketRealtimeWindowEvent, boolean>>;
|
|
9538
9550
|
/** How long a reconnect waits for its catch-up answer before it counts as live again. */
|
|
9539
9551
|
catchupTimeoutMs?: number;
|
|
9552
|
+
/**
|
|
9553
|
+
* Upper bound of the random delay before a `catchup:full` or `access:changed` window event
|
|
9554
|
+
* (default REACTION_JITTER_MS; 0 = at once). Both land on every tab at the same moment after a
|
|
9555
|
+
* deploy or a role change, and each makes the tab refetch its lists — spread over a few
|
|
9556
|
+
* seconds they stop arriving at the API as one wave.
|
|
9557
|
+
*/
|
|
9558
|
+
reactionJitterMs?: number;
|
|
9540
9559
|
}
|
|
9541
9560
|
|
|
9542
9561
|
export declare type SocketRealtimeWindowEvent = 'catchupFull' | 'accessChanged' | 'configUpdate' | 'forceRefresh';
|
|
@@ -1,77 +1,98 @@
|
|
|
1
|
-
import { jsx as t, jsxs as
|
|
2
|
-
import { Download as
|
|
3
|
-
import { useEffect as
|
|
1
|
+
import { jsx as t, jsxs as s } from "react/jsx-runtime";
|
|
2
|
+
import { Download as d, X as x } from "lucide-react";
|
|
3
|
+
import { useEffect as u } from "react";
|
|
4
4
|
import { createPortal as h } from "react-dom";
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const p = /* @__PURE__ */ new Set([
|
|
6
|
+
"text/plain",
|
|
7
|
+
"text/csv",
|
|
8
|
+
"text/tab-separated-values",
|
|
9
|
+
"text/markdown",
|
|
10
|
+
"text/x-markdown",
|
|
11
|
+
"text/x-log"
|
|
12
|
+
]);
|
|
13
|
+
function o(e) {
|
|
14
|
+
return String(e ?? "").split(";")[0].trim().toLowerCase();
|
|
7
15
|
}
|
|
8
|
-
function
|
|
16
|
+
function m(e) {
|
|
17
|
+
return p.has(o(e));
|
|
18
|
+
}
|
|
19
|
+
function N(e) {
|
|
20
|
+
if (!e) return !1;
|
|
21
|
+
const r = o(e);
|
|
22
|
+
return r.startsWith("image/") || r === "application/pdf" || r.startsWith("video/") || r.startsWith("audio/") || m(r);
|
|
23
|
+
}
|
|
24
|
+
function w(e) {
|
|
9
25
|
return e == null || !Number.isFinite(e) ? null : e < 1024 ? `${e} B` : e < 1024 * 1024 ? `${(e / 1024).toFixed(1)} KB` : `${(e / (1024 * 1024)).toFixed(1)} MB`;
|
|
10
26
|
}
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
m(() => {
|
|
16
|
-
const a = (l) => {
|
|
17
|
-
l.key === "Escape" && (l.stopPropagation(), s());
|
|
27
|
+
function y({ file: e, onClose: r }) {
|
|
28
|
+
u(() => {
|
|
29
|
+
const n = (c) => {
|
|
30
|
+
c.key === "Escape" && (c.stopPropagation(), r());
|
|
18
31
|
};
|
|
19
|
-
return window.addEventListener("keydown",
|
|
20
|
-
}, [
|
|
21
|
-
const
|
|
32
|
+
return window.addEventListener("keydown", n, !0), () => window.removeEventListener("keydown", n, !0);
|
|
33
|
+
}, [r]);
|
|
34
|
+
const a = e.type ?? "", l = w(e.size);
|
|
22
35
|
let i;
|
|
23
|
-
return
|
|
36
|
+
return a.startsWith("image/") ? i = /* @__PURE__ */ t(
|
|
24
37
|
"img",
|
|
25
38
|
{
|
|
26
39
|
src: e.url,
|
|
27
40
|
alt: e.name,
|
|
28
41
|
className: "max-h-[82vh] max-w-full rounded object-contain"
|
|
29
42
|
}
|
|
30
|
-
) :
|
|
43
|
+
) : o(a) === "application/pdf" ? i = /* @__PURE__ */ t(
|
|
44
|
+
"iframe",
|
|
45
|
+
{
|
|
46
|
+
src: e.url,
|
|
47
|
+
title: e.name,
|
|
48
|
+
className: "h-[82vh] w-[min(920px,92vw)] rounded border-0 bg-white"
|
|
49
|
+
}
|
|
50
|
+
) : m(a) ? i = /* @__PURE__ */ t(
|
|
31
51
|
"iframe",
|
|
32
52
|
{
|
|
33
53
|
src: e.url,
|
|
34
54
|
title: e.name,
|
|
55
|
+
sandbox: "",
|
|
35
56
|
className: "h-[82vh] w-[min(920px,92vw)] rounded border-0 bg-white"
|
|
36
57
|
}
|
|
37
|
-
) :
|
|
58
|
+
) : a.startsWith("video/") ? i = /* @__PURE__ */ t("video", { src: e.url, controls: !0, className: "max-h-[82vh] max-w-full rounded" }) : a.startsWith("audio/") ? i = /* @__PURE__ */ t("audio", { src: e.url, controls: !0, className: "w-[min(480px,90vw)]" }) : i = /* @__PURE__ */ s("div", { className: "flex w-[min(420px,90vw)] flex-col items-center gap-3 rounded-lg bg-white p-8 text-center dark:bg-card", children: [
|
|
38
59
|
/* @__PURE__ */ t("p", { className: "text-[13px] font-medium text-slate-700 dark:text-slate-200", children: e.name }),
|
|
39
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ s("p", { className: "text-[12px] text-slate-500 dark:text-slate-400", children: [
|
|
40
61
|
"No inline preview for this file type",
|
|
41
|
-
|
|
62
|
+
a ? ` (${a})` : "",
|
|
42
63
|
"."
|
|
43
64
|
] }),
|
|
44
|
-
/* @__PURE__ */
|
|
65
|
+
/* @__PURE__ */ s(
|
|
45
66
|
"a",
|
|
46
67
|
{
|
|
47
68
|
href: `${e.url}?download=1`,
|
|
48
69
|
className: "inline-flex items-center gap-1.5 rounded-md bg-nvr-cyan px-3 py-1.5 text-[12.5px] font-medium text-white hover:brightness-110",
|
|
49
70
|
children: [
|
|
50
|
-
/* @__PURE__ */ t(
|
|
71
|
+
/* @__PURE__ */ t(d, { className: "h-3.5 w-3.5" }),
|
|
51
72
|
" Download"
|
|
52
73
|
]
|
|
53
74
|
}
|
|
54
75
|
)
|
|
55
76
|
] }), h(
|
|
56
77
|
// biome-ignore lint/a11y/useKeyWithClickEvents: Escape handled globally above
|
|
57
|
-
/* @__PURE__ */
|
|
78
|
+
/* @__PURE__ */ s(
|
|
58
79
|
"div",
|
|
59
80
|
{
|
|
60
81
|
className: "fixed inset-0 z-[130] flex flex-col bg-black/70 backdrop-blur-[2px]",
|
|
61
|
-
onClick:
|
|
82
|
+
onClick: r,
|
|
62
83
|
"data-file-lightbox": !0,
|
|
63
84
|
children: [
|
|
64
|
-
/* @__PURE__ */
|
|
85
|
+
/* @__PURE__ */ s(
|
|
65
86
|
"div",
|
|
66
87
|
{
|
|
67
88
|
className: "flex items-center gap-3 px-4 py-2.5",
|
|
68
|
-
onClick: (
|
|
89
|
+
onClick: (n) => n.stopPropagation(),
|
|
69
90
|
onKeyDown: () => {
|
|
70
91
|
},
|
|
71
92
|
children: [
|
|
72
|
-
/* @__PURE__ */
|
|
93
|
+
/* @__PURE__ */ s("p", { className: "min-w-0 flex-1 truncate text-[13px] font-medium text-white", children: [
|
|
73
94
|
e.name,
|
|
74
|
-
|
|
95
|
+
l && /* @__PURE__ */ t("span", { className: "ml-2 text-[11px] font-normal text-white/60", children: l })
|
|
75
96
|
] }),
|
|
76
97
|
/* @__PURE__ */ t(
|
|
77
98
|
"a",
|
|
@@ -79,17 +100,17 @@ function b({
|
|
|
79
100
|
href: `${e.url}?download=1`,
|
|
80
101
|
title: "Download",
|
|
81
102
|
className: "rounded p-1.5 text-white/80 hover:bg-white/10 hover:text-white",
|
|
82
|
-
children: /* @__PURE__ */ t(
|
|
103
|
+
children: /* @__PURE__ */ t(d, { className: "h-4 w-4" })
|
|
83
104
|
}
|
|
84
105
|
),
|
|
85
106
|
/* @__PURE__ */ t(
|
|
86
107
|
"button",
|
|
87
108
|
{
|
|
88
109
|
type: "button",
|
|
89
|
-
onClick:
|
|
110
|
+
onClick: r,
|
|
90
111
|
title: "Close (Esc)",
|
|
91
112
|
className: "rounded p-1.5 text-white/80 hover:bg-white/10 hover:text-white",
|
|
92
|
-
children: /* @__PURE__ */ t(
|
|
113
|
+
children: /* @__PURE__ */ t(x, { className: "h-4 w-4" })
|
|
93
114
|
}
|
|
94
115
|
)
|
|
95
116
|
]
|
|
@@ -99,7 +120,7 @@ function b({
|
|
|
99
120
|
"div",
|
|
100
121
|
{
|
|
101
122
|
className: "flex min-h-0 flex-1 items-center justify-center p-4 pt-0",
|
|
102
|
-
onClick: (
|
|
123
|
+
onClick: (n) => n.stopPropagation(),
|
|
103
124
|
onKeyDown: () => {
|
|
104
125
|
},
|
|
105
126
|
children: i
|
|
@@ -112,6 +133,6 @@ function b({
|
|
|
112
133
|
);
|
|
113
134
|
}
|
|
114
135
|
export {
|
|
115
|
-
|
|
116
|
-
|
|
136
|
+
y as FilePreviewLightbox,
|
|
137
|
+
N as canPreviewFile
|
|
117
138
|
};
|