@nbt-dev/components 0.0.8 → 0.0.10
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/{chunk-WBZS7I6V.js → chunk-AI2LKTA4.js} +30 -2
- package/dist/chunk-AI2LKTA4.js.map +7 -0
- package/dist/{chunk-ZOGIPYYM.js → chunk-BNA2MLFE.js} +153 -18
- package/dist/chunk-BNA2MLFE.js.map +7 -0
- package/dist/{chunk-VDIHQ2NS.js → chunk-HTAPMIK6.js} +11 -3
- package/dist/chunk-HTAPMIK6.js.map +7 -0
- package/dist/{chunk-S7VBQE6Y.js → chunk-TNIHD62H.js} +115 -31
- package/dist/chunk-TNIHD62H.js.map +7 -0
- package/dist/core/auth.d.ts +2 -0
- package/dist/core/index.d.ts +1 -1
- package/dist/core/use-cartridge-info.d.ts +1 -0
- package/dist/editor/editor-themes.d.ts +8 -0
- package/dist/editor/index.d.ts +2 -0
- package/dist/editor/index.js +7 -3
- package/dist/editor/nbt-editor.d.ts +4 -0
- package/dist/graph/index.js +2 -2
- package/dist/index.js +20 -8
- package/dist/index.js.map +2 -2
- package/dist/styles.css +1 -1
- package/dist/table/column-widths.d.ts +2 -0
- package/dist/table/index.js +2 -2
- package/package.json +3 -2
- package/src/core/auth.ts +40 -0
- package/src/core/data-store.ts +5 -1
- package/src/core/index.ts +2 -0
- package/src/core/use-cartridge-info.ts +15 -4
- package/src/editor/editor-themes.ts +77 -0
- package/src/editor/index.ts +2 -0
- package/src/editor/nbt-editor.tsx +60 -26
- package/src/graph/diagram.tsx +16 -0
- package/src/table/column-widths.ts +34 -0
- package/src/table/data-table.tsx +148 -16
- package/dist/chunk-S7VBQE6Y.js.map +0 -7
- package/dist/chunk-VDIHQ2NS.js.map +0 -7
- package/dist/chunk-WBZS7I6V.js.map +0 -7
- package/dist/chunk-ZOGIPYYM.js.map +0 -7
|
@@ -58,6 +58,31 @@ async function fetchWhoAmI(apiBaseUrl, signal) {
|
|
|
58
58
|
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
|
59
59
|
return await r.json();
|
|
60
60
|
}
|
|
61
|
+
async function fetchDevtoolsSettings(apiBaseUrl, signal) {
|
|
62
|
+
try {
|
|
63
|
+
const r = await fetch(`${apiBaseUrl}/_console/settings`, {
|
|
64
|
+
signal,
|
|
65
|
+
credentials: "include",
|
|
66
|
+
headers: authHeaders()
|
|
67
|
+
});
|
|
68
|
+
if (!r.ok) return {};
|
|
69
|
+
const j = await r.json();
|
|
70
|
+
return j && typeof j === "object" ? j : {};
|
|
71
|
+
} catch {
|
|
72
|
+
return {};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function saveDevtoolsSettings(apiBaseUrl, blob) {
|
|
76
|
+
try {
|
|
77
|
+
await fetch(`${apiBaseUrl}/_console/settings`, {
|
|
78
|
+
method: "PUT",
|
|
79
|
+
credentials: "include",
|
|
80
|
+
headers: authHeaders({ "content-type": "application/json" }),
|
|
81
|
+
body: JSON.stringify(blob)
|
|
82
|
+
});
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
}
|
|
61
86
|
async function devToolsSignIn(apiBaseUrl, email, password) {
|
|
62
87
|
const r = await fetch(`${apiBaseUrl}/api/auth/user/signin`, {
|
|
63
88
|
method: "POST",
|
|
@@ -217,7 +242,8 @@ var BulkDataStore = class {
|
|
|
217
242
|
if (delta.op === FRAME_DELTA_UPD && delta.rowData) {
|
|
218
243
|
const idx = this._findRowById(target, delta.rowData);
|
|
219
244
|
if (idx >= 0) target[idx] = delta.rowData;
|
|
220
|
-
if (this.searchActive) this.
|
|
245
|
+
if (!this.searchActive) this.rows = this._fullRows;
|
|
246
|
+
else this._applySearch();
|
|
221
247
|
return;
|
|
222
248
|
}
|
|
223
249
|
if (delta.op === FRAME_DELTA_DEL && delta.id !== void 0) {
|
|
@@ -717,6 +743,8 @@ export {
|
|
|
717
743
|
clearDevToolsToken,
|
|
718
744
|
authHeaders,
|
|
719
745
|
fetchWhoAmI,
|
|
746
|
+
fetchDevtoolsSettings,
|
|
747
|
+
saveDevtoolsSettings,
|
|
720
748
|
devToolsSignIn,
|
|
721
749
|
devToolsSignUp,
|
|
722
750
|
devToolsSignOut,
|
|
@@ -725,4 +753,4 @@ export {
|
|
|
725
753
|
useBulkRowCounts,
|
|
726
754
|
useBulkSubscription
|
|
727
755
|
};
|
|
728
|
-
//# sourceMappingURL=chunk-
|
|
756
|
+
//# sourceMappingURL=chunk-AI2LKTA4.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/core/config.tsx", "../src/core/auth.ts", "../src/core/use-bulk-stream.ts", "../src/generated/bulk-protocol.ts", "../src/core/data-store.ts", "../src/core/bulk-decoder.ts"],
|
|
4
|
+
"sourcesContent": ["import React from \"react\";\n\n// Runtime config for the devtools, replacing the inspector's compile-time\n// `NIMBIT_API_ENDPOINT` define. Hosts pass the console's base URL once via\n// <NimbitDevTools apiBaseUrl=\"\u2026\" /> and every fetch/WebSocket derives from it.\n// Empty string => same-origin (the inspector default when served by the console).\nexport type DevToolsConfig = {\n apiBaseUrl: string;\n};\n\nconst DevToolsConfigContext = React.createContext<DevToolsConfig>({\n apiBaseUrl: \"\",\n});\n\nexport const DevToolsConfigProvider: React.FC<{\n apiBaseUrl?: string;\n children: React.ReactNode;\n}> = ({ apiBaseUrl = \"\", children }) => {\n const value = React.useMemo(() => ({ apiBaseUrl }), [apiBaseUrl]);\n return (\n <DevToolsConfigContext.Provider value={value}>\n {children}\n </DevToolsConfigContext.Provider>\n );\n};\n\nexport function useDevToolsConfig(): DevToolsConfig {\n return React.useContext(DevToolsConfigContext);\n}\n\n// Turn an http(s) base into a ws(s) base. Empty => derive from window.location.\nexport function wsBaseFrom(apiBaseUrl: string): string {\n if (!apiBaseUrl) {\n const loc = window.location;\n const proto = loc.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n return `${proto}//${loc.host}`;\n }\n return apiBaseUrl.replace(/^http(s?):/, \"ws$1:\");\n}\n", "// Devtools-managed auth. The panel can run embedded in a host page that has no\n// session of its own, so it owns its credential rather than riding the host's\n// `session_token` cookie: a token obtained via `signin` is stored in\n// localStorage and attached as a Bearer header (HTTP) or in the `auth-<b64url>`\n// WebSocket subprotocol. On an unconfigured/dev console the backend leaves\n// devtools open, so a missing token is fine there.\n\nconst TOKEN_KEY = \"nbt_devtools_token\";\n\nexport function getDevToolsToken(): string | null {\n try {\n return localStorage.getItem(TOKEN_KEY);\n } catch {\n return null;\n }\n}\n\nexport function setDevToolsToken(token: string): void {\n try {\n localStorage.setItem(TOKEN_KEY, token);\n } catch {\n /* storage unavailable (private mode / SSR) \u2014 token stays in-memory only */\n }\n}\n\nexport function clearDevToolsToken(): void {\n try {\n localStorage.removeItem(TOKEN_KEY);\n } catch {\n /* ignore */\n }\n}\n\n// Merge the stored Bearer token into a header bag (if present).\nexport function authHeaders(base?: Record<string, string>): Record<string, string> {\n const h: Record<string, string> = { ...(base ?? {}) };\n const t = getDevToolsToken();\n if (t) h[\"Authorization\"] = `Bearer ${t}`;\n return h;\n}\n\nexport type WhoAmI = {\n // node has real auth configured (else devtools is open / local dev)\n configured: boolean;\n // request carries a valid session\n authenticated: boolean;\n // caller may use devtools (open node, or a console operator)\n isAdmin: boolean;\n // at least one user exists; when false the panel offers a first-admin signup\n claimed: boolean;\n};\n\nexport async function fetchWhoAmI(apiBaseUrl: string, signal?: AbortSignal): Promise<WhoAmI> {\n const r = await fetch(`${apiBaseUrl}/_console/whoami`, {\n signal,\n credentials: \"include\",\n headers: authHeaders(),\n });\n if (!r.ok) throw new Error(`HTTP ${r.status}`);\n return (await r.json()) as WhoAmI;\n}\n\n// Per-operator devtools settings, stored server-side on the operator's user row\n// (GET/PUT /_console/settings). The blob is an opaque key/value object the\n// console persists verbatim. On an unconfigured/dev node, or when not signed in,\n// GET returns {} and PUT is rejected \u2014 callers keep their localStorage copy.\nexport async function fetchDevtoolsSettings(\n apiBaseUrl: string,\n signal?: AbortSignal,\n): Promise<Record<string, unknown>> {\n try {\n const r = await fetch(`${apiBaseUrl}/_console/settings`, {\n signal,\n credentials: \"include\",\n headers: authHeaders(),\n });\n if (!r.ok) return {};\n const j = await r.json();\n return j && typeof j === \"object\" ? (j as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n}\n\n// Best-effort persist \u2014 the caller's localStorage already holds the value, so a\n// failure here (offline, dev node, not signed in) is non-fatal.\nexport async function saveDevtoolsSettings(\n apiBaseUrl: string,\n blob: Record<string, unknown>,\n): Promise<void> {\n try {\n await fetch(`${apiBaseUrl}/_console/settings`, {\n method: \"PUT\",\n credentials: \"include\",\n headers: authHeaders({ \"content-type\": \"application/json\" }),\n body: JSON.stringify(blob),\n });\n } catch {\n /* non-fatal \u2014 localStorage is the source of truth for the live session */\n }\n}\n\n// Sign in against the console's auth cartridge and stash the returned token.\n// Throws with a human-readable message on failure.\nexport async function devToolsSignIn(\n apiBaseUrl: string,\n email: string,\n password: string,\n): Promise<void> {\n const r = await fetch(`${apiBaseUrl}/api/auth/user/signin`, {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ email, password }),\n });\n if (!r.ok) {\n throw new Error(r.status === 401 ? \"Invalid email or password\" : `Sign-in failed (HTTP ${r.status})`);\n }\n const j = (await r.json()) as { token?: string };\n if (!j.token) throw new Error(\"Sign-in returned no token\");\n setDevToolsToken(j.token);\n}\n\n// Bootstrap signup \u2014 only meaningful while the instance is unclaimed (the first\n// user becomes console admin). Stashes the returned token like signin.\nexport async function devToolsSignUp(\n apiBaseUrl: string,\n name: string,\n email: string,\n password: string,\n): Promise<void> {\n const r = await fetch(`${apiBaseUrl}/api/auth/user/signup`, {\n method: \"POST\",\n credentials: \"include\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ name, email, password }),\n });\n if (!r.ok) {\n let detail = `HTTP ${r.status}`;\n if (r.status === 409) detail = \"That email is already registered\";\n else {\n try {\n const j = (await r.json()) as { error?: string };\n if (j?.error) detail = j.error;\n } catch {\n /* non-JSON body */\n }\n }\n throw new Error(`Sign-up failed: ${detail}`);\n }\n const j = (await r.json()) as { token?: string };\n if (!j.token) throw new Error(\"Sign-up returned no token\");\n setDevToolsToken(j.token);\n}\n\nexport function devToolsSignOut(): void {\n clearDevToolsToken();\n}\n\nfunction base64UrlEncode(value: string): string {\n if (typeof btoa === \"function\") {\n return btoa(value).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/g, \"\");\n }\n throw new Error(\"No base64 encoder available for WS auth\");\n}\n\n// WebSocket subprotocols for an authenticated devtools stream: a leading\n// (non-auth) protocol name the server echoes, plus the `auth-<b64url>` token\n// part it decodes. Without a stored token we offer only the name \u2014 fine on an\n// open/dev console, rejected on a configured one.\nexport function wsAuthProtocols(name: string): string[] {\n const t = getDevToolsToken();\n return t ? [name, `auth-${base64UrlEncode(t)}`] : [name];\n}\n", "import React, {\n createContext,\n useContext,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { BulkDataStore } from \"./data-store\";\nimport {\n getFrameType,\n getFrameSid,\n parseSchema,\n parseDataChunk,\n parseDelta,\n parseError,\n encodeStreamCmd,\n} from \"./bulk-decoder\";\nimport {\n FRAME_SCHEMA,\n FRAME_DATA,\n FRAME_DATA_END,\n FRAME_DELTA_INS,\n FRAME_DELTA_UPD,\n FRAME_DELTA_DEL,\n FRAME_ERROR,\n type ColumnDef,\n} from \"../generated/bulk-protocol\";\nimport type { BulkRegistry } from \"./use-cartridge-info\";\nimport { useDevToolsConfig, wsBaseFrom } from \"./config\";\nimport { getDevToolsToken } from \"./auth\";\n\n// One per-entity table view, demultiplexed off the shared socket by sid. The\n// object is stable across renders and mutated in place; consumers re-render via\n// the listener set (and the cheap per-chunk onRender for virtualization).\ntype View = {\n sid: number;\n cart: string;\n entity: string;\n store: BulkDataStore;\n columns: ColumnDef[];\n totalRows: number;\n loadedRows: number;\n streaming: boolean;\n error: string | null;\n streamRequested: boolean; // sent a `sub` (full stream) at least once\n onRender: (() => void) | null;\n listeners: Set<() => void>;\n};\n\nexport type BulkSubscription = {\n store: BulkDataStore;\n connected: boolean;\n streaming: boolean;\n error: string | null;\n columns: ColumnDef[];\n totalRows: number;\n loadedRows: number;\n search: (q: string) => void;\n clearSearch: () => void;\n setOnRender: (fn: (() => void) | null) => void;\n};\n\ntype Ctx = {\n getView: (cart: string, entity: string) => View;\n ensureStreamed: (view: View) => void;\n search: (view: View, query: string) => void;\n clearSearch: (view: View) => void;\n subscribe: (view: View, cb: () => void) => () => void;\n connected: boolean;\n error: string | null;\n};\n\nconst BulkStreamContext = createContext<Ctx | null>(null);\n\nfunction base64UrlEncode(value: string): string {\n if (typeof btoa === \"function\") {\n return btoa(value).replace(/\\+/g, \"-\").replace(/\\//g, \"_\").replace(/=+$/g, \"\");\n }\n throw new Error(\"No base64 encoder available for bulk WS auth\");\n}\n\nfunction bulkAuthProtocols(token: string): string[] {\n return [\"nimbit-bulk\", `auth-${base64UrlEncode(token)}`];\n}\n\nlet _wsTokenCache: { apiBaseUrl: string; token: string; at: number } | null = null;\nconst WS_TOKEN_TTL_MS = 60_000;\n\n// Resolve the session token to forward in the WS subprotocol. Prefer the\n// devtools-managed token (set by its own sign-in); otherwise fall back to the\n// host page's cookie session via auth.Session.current. Returns null when there\n// is neither \u2014 on an unconfigured/dev console the WS upgrade is open anyway, so\n// we still connect without an auth subprotocol.\nasync function fetchWsToken(\n signal: AbortSignal,\n apiBaseUrl: string,\n): Promise<string | null> {\n const own = getDevToolsToken();\n if (own) return own;\n const now = Date.now();\n if (_wsTokenCache?.apiBaseUrl === apiBaseUrl && now - _wsTokenCache.at < WS_TOKEN_TTL_MS) {\n return _wsTokenCache.token;\n }\n const r = await fetch(`${apiBaseUrl}/api/auth/session/current`, {\n method: \"POST\",\n signal,\n credentials: \"include\",\n headers: { \"content-type\": \"application/json\" },\n });\n if (!r.ok) return null;\n const j = (await r.json()) as { session?: { token?: string } };\n const token = j.session?.token;\n if (!token) return null;\n _wsTokenCache = { apiBaseUrl, token, at: now };\n return token;\n}\n\nfunction invalidateWsToken() {\n _wsTokenCache = null;\n}\n\nfunction notify(view: View) {\n view.listeners.forEach((cb) => cb());\n}\n\ntype Props = { registry: BulkRegistry; children: React.ReactNode };\n\nexport function BulkStreamProvider({ registry, children }: Props): React.ReactElement {\n const { apiBaseUrl } = useDevToolsConfig();\n const wsRef = useRef<WebSocket | null>(null);\n const sendQueueRef = useRef<string[]>([]);\n const sidCounterRef = useRef(1);\n const viewsBySidRef = useRef<Map<number, View>>(new Map());\n const viewsByKeyRef = useRef<Map<string, View>>(new Map());\n const preloadedRef = useRef(false);\n\n const [connected, setConnected] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const sendCmd = (cmd: string) => {\n const ws = wsRef.current;\n if (ws && ws.readyState === WebSocket.OPEN) ws.send(cmd);\n else sendQueueRef.current.push(cmd);\n };\n\n const flushQueue = () => {\n const ws = wsRef.current;\n if (!ws || ws.readyState !== WebSocket.OPEN) return;\n const q = sendQueueRef.current;\n sendQueueRef.current = [];\n for (const cmd of q) ws.send(cmd);\n };\n\n const getView = (cart: string, entity: string): View => {\n // The server registers entity ops under \"<cart>/<entity-lower>\" (route\n // path casing), so normalize here \u2014 preload and subscription then share one\n // view per entity regardless of the caller's casing.\n const entLower = entity.toLowerCase();\n const key = `${cart}/${entLower}`;\n const existing = viewsByKeyRef.current.get(key);\n if (existing) return existing;\n const sid = sidCounterRef.current++;\n const view: View = {\n sid,\n cart,\n entity: entLower,\n store: new BulkDataStore(),\n columns: [],\n totalRows: 0,\n loadedRows: 0,\n streaming: false,\n error: null,\n streamRequested: false,\n onRender: null,\n listeners: new Set(),\n };\n viewsByKeyRef.current.set(key, view);\n viewsBySidRef.current.set(sid, view);\n return view;\n };\n\n const ensureStreamed = (view: View) => {\n if (view.streamRequested) return;\n view.streamRequested = true;\n sendCmd(encodeStreamCmd(view.sid, view.cart, view.entity));\n };\n\n const handleMessage = (ev: MessageEvent) => {\n const buf = ev.data as ArrayBuffer;\n const sid = getFrameSid(buf);\n const view = viewsBySidRef.current.get(sid);\n if (!view) return;\n const ft = getFrameType(buf);\n const store = view.store;\n switch (ft) {\n case FRAME_SCHEMA: {\n const schema = parseSchema(buf);\n store.applySchema(schema);\n view.columns = schema.columns;\n view.totalRows = schema.totalRows;\n view.loadedRows = 0;\n view.streaming = true;\n notify(view);\n break;\n }\n case FRAME_DATA:\n {\n const rows = parseDataChunk(buf, store.columns);\n store.appendChunk(rows);\n view.loadedRows = store.getRowCount();\n view.onRender?.();\n break;\n }\n case FRAME_DATA_END:\n view.streaming = false;\n notify(view);\n break;\n case FRAME_DELTA_INS:\n case FRAME_DELTA_UPD:\n case FRAME_DELTA_DEL: {\n const delta = parseDelta(buf, store.columns);\n store.applyDelta(delta);\n view.totalRows = store.totalRows;\n view.loadedRows = store.getRowCount();\n view.onRender?.();\n notify(view);\n break;\n }\n case FRAME_ERROR:\n view.error = parseError(buf);\n view.streaming = false;\n notify(view);\n break;\n }\n };\n\n // Open one shared socket for the provider's lifetime.\n useEffect(() => {\n const ac = new AbortController();\n let cancelled = false;\n let opened = false;\n let ws: WebSocket | null = null;\n sendQueueRef.current = [];\n sidCounterRef.current = 1;\n viewsBySidRef.current.clear();\n viewsByKeyRef.current.clear();\n preloadedRef.current = false;\n setConnected(false);\n setError(null);\n\n (async () => {\n let token: string | null = null;\n try {\n token = await fetchWsToken(ac.signal, apiBaseUrl);\n } catch {\n // Token lookup failed; still attempt the connection \u2014 an unconfigured\n // dev console accepts it, a configured one will close it (handled below).\n }\n if (cancelled) return;\n // With a token, authenticate via the subprotocol; without one, connect\n // bare (dev/unconfigured consoles synthesize auth on their side).\n ws = token\n ? new WebSocket(`${wsBaseFrom(apiBaseUrl)}/_ws/bulk`, bulkAuthProtocols(token))\n : new WebSocket(`${wsBaseFrom(apiBaseUrl)}/_ws/bulk`, [\"nimbit-bulk\"]);\n ws.binaryType = \"arraybuffer\";\n wsRef.current = ws;\n ws.onopen = () => {\n if (cancelled || wsRef.current !== ws) return;\n opened = true;\n setConnected(true);\n setError(null);\n flushQueue();\n };\n ws.onmessage = handleMessage;\n ws.onerror = () => {};\n ws.onclose = () => {\n if (cancelled || wsRef.current !== ws) return;\n setConnected(false);\n wsRef.current = null;\n if (!opened) {\n invalidateWsToken();\n setError(\"WebSocket connection closed\");\n }\n };\n })();\n\n return () => {\n cancelled = true;\n ac.abort();\n if (ws) {\n ws.onopen = ws.onmessage = ws.onerror = ws.onclose = null;\n try {\n ws.close();\n } catch {}\n if (wsRef.current === ws) wsRef.current = null;\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [apiBaseUrl]);\n\n // The live protocol exposes only sub/unsub. Subscribe every known entity once\n // so schema, row counts, snapshots, and deltas stay available to all tabs.\n useEffect(() => {\n if (!connected) return;\n if (preloadedRef.current) return;\n const keys = Object.keys(registry);\n if (keys.length === 0) return;\n preloadedRef.current = true;\n for (const cart of keys) {\n for (const ent of registry[cart] ?? []) {\n const view = getView(cart, ent.name);\n ensureStreamed(view);\n }\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [connected, registry]);\n\n const search = (view: View, query: string) => {\n view.store.search(query);\n view.totalRows = view.store.totalRows;\n view.loadedRows = view.store.getRowCount();\n notify(view);\n view.onRender?.();\n };\n\n const clearSearch = (view: View) => {\n view.store.exitSearch();\n view.totalRows = view.store.totalRows;\n view.loadedRows = view.store.getRowCount();\n notify(view);\n view.onRender?.();\n };\n\n const subscribe = (view: View, cb: () => void) => {\n view.listeners.add(cb);\n return () => {\n view.listeners.delete(cb);\n };\n };\n\n const ctx: Ctx = {\n getView,\n ensureStreamed,\n search,\n clearSearch,\n subscribe,\n connected,\n error,\n };\n\n return React.createElement(BulkStreamContext.Provider, { value: ctx }, children);\n}\n\n// Row counts for every entity in the registry, sourced from the SCHEMA preload\n// the provider already runs on connect (FRAME_SCHEMA carries totalRows). No row\n// streaming is triggered \u2014 we subscribe to each view but never ensureStreamed.\n// Keyed by the graph id `cart:Entity` (original casing) for the Diagram tab.\nexport function useBulkRowCounts(registry: BulkRegistry): Record<string, number> {\n const ctx = useContext(BulkStreamContext);\n if (!ctx) throw new Error(\"useBulkRowCounts must be used within BulkStreamProvider\");\n const [, force] = useState(0);\n\n const pairs: Array<{ id: string; view: View }> = [];\n for (const cart of Object.keys(registry)) {\n for (const ent of registry[cart] ?? []) {\n pairs.push({ id: `${cart}:${ent.name}`, view: ctx.getView(cart, ent.name) });\n }\n }\n\n const key = pairs.map((p) => p.id).join(\"|\");\n useEffect(() => {\n const unsubs = pairs.map((p) => ctx.subscribe(p.view, () => force((n) => n + 1)));\n return () => unsubs.forEach((u) => u());\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [key]);\n\n const counts: Record<string, number> = {};\n for (const p of pairs) counts[p.id] = p.view.totalRows;\n return counts;\n}\n\n// Keyed to a single entity. The view (schema + rows + deltas) is shared and\n// cached in the provider, so switching tabs is instant once visited.\nexport function useBulkSubscription(cart: string, entity: string): BulkSubscription {\n const ctx = useContext(BulkStreamContext);\n if (!ctx) throw new Error(\"useBulkSubscription must be used within BulkStreamProvider\");\n\n const view = ctx.getView(cart, entity);\n const [, force] = useState(0);\n\n useEffect(() => {\n const unsub = ctx.subscribe(view, () => force((n) => n + 1));\n ctx.ensureStreamed(view);\n return unsub;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [view]);\n\n return {\n store: view.store,\n connected: ctx.connected,\n streaming: view.streaming,\n error: view.error ?? ctx.error,\n columns: view.columns,\n totalRows: view.totalRows,\n loadedRows: view.loadedRows,\n search: (q: string) => ctx.search(view, q),\n clearSearch: () => ctx.clearSearch(view),\n setOnRender: (fn) => {\n view.onRender = fn;\n },\n };\n}\n", "// Wire constants mirrored from modules/cart/ws.jai (CART_WS_* + cart_ws_type_byte).\n\n// Frame types (first byte of each WS binary message).\nexport const FRAME_SCHEMA = 0x01;\nexport const FRAME_DATA = 0x02;\nexport const FRAME_DATA_END = 0x03;\nexport const FRAME_DELTA_INS = 0x04;\nexport const FRAME_DELTA_UPD = 0x05;\nexport const FRAME_DELTA_DEL = 0x06;\nexport const FRAME_ERROR = 0xff;\n\n// Column type bytes (schema frame, per column).\nexport const TYPE_U8 = 0x01;\nexport const TYPE_U16 = 0x02;\nexport const TYPE_U32 = 0x03;\nexport const TYPE_U64 = 0x04;\nexport const TYPE_S8 = 0x05;\nexport const TYPE_S16 = 0x06;\nexport const TYPE_S32 = 0x07;\nexport const TYPE_S64 = 0x08;\nexport const TYPE_BOOL = 0x09;\nexport const TYPE_FLOAT32 = 0x0a;\nexport const TYPE_FLOAT64 = 0x0b;\nexport const TYPE_STRING = 0x0c;\nexport const TYPE_DATETIME = 0x0d;\nexport const TYPE_DOCUMENT = 0x0e;\n\nexport type ColumnDef = {\n name: string;\n type: number;\n fixedSize: number; // 0 for variable-width (string, document)\n};\n\nexport type SchemaFrame = {\n totalRows: number;\n columns: ColumnDef[];\n};\n\nexport type DeltaFrame = {\n op: number;\n rowData?: string[];\n id?: string; // DELETE carries the row id as a len-prefixed string\n};\n\nexport function fixedSizeForType(type: number): number {\n switch (type) {\n case TYPE_U8: return 1;\n case TYPE_U16: return 2;\n case TYPE_U32: return 4;\n case TYPE_U64: return 8;\n case TYPE_S8: return 1;\n case TYPE_S16: return 2;\n case TYPE_S32: return 4;\n case TYPE_S64: return 8;\n case TYPE_BOOL: return 1;\n case TYPE_FLOAT32: return 4;\n case TYPE_FLOAT64: return 8;\n case TYPE_STRING: return 0;\n case TYPE_DATETIME: return 8;\n case TYPE_DOCUMENT: return 0;\n default: return 0;\n }\n}\n", "import {\n FRAME_DELTA_INS,\n FRAME_DELTA_UPD,\n FRAME_DELTA_DEL,\n type ColumnDef,\n type SchemaFrame,\n type DeltaFrame,\n} from \"../generated/bulk-protocol\";\n\nexport class BulkDataStore {\n columns: ColumnDef[] = [];\n rows: string[][] = [];\n totalRows = 0;\n searchActive = false;\n\n private _fullRows: string[][] = [];\n private _fullTotalRows = 0;\n private _idColIndex = -1;\n private _query = \"\";\n\n applySchema(schema: SchemaFrame): void {\n this.columns = schema.columns;\n this.totalRows = schema.totalRows;\n this.rows = [];\n this._fullRows = [];\n this._fullTotalRows = schema.totalRows;\n this.searchActive = false;\n this._query = \"\";\n this._idColIndex = schema.columns.findIndex((c) => c.name === \"id\");\n }\n\n appendChunk(chunk: string[][]): void {\n for (let i = 0; i < chunk.length; i++) {\n const row = chunk[i]!;\n this._fullRows.push(row);\n if (!this.searchActive) this.rows.push(row);\n }\n }\n\n applyDelta(delta: DeltaFrame): void {\n const target = this._fullRows;\n if (delta.op === FRAME_DELTA_INS && delta.rowData) {\n target.push(delta.rowData);\n this._fullTotalRows++;\n this.totalRows++;\n if (!this.searchActive) this.rows = this._fullRows;\n else this._applySearch();\n return;\n }\n if (delta.op === FRAME_DELTA_UPD && delta.rowData) {\n const idx = this._findRowById(target, delta.rowData);\n if (idx >= 0) target[idx] = delta.rowData;\n // `rows` is a distinct array holding the same row refs as `_fullRows`;\n // replacing the ref above only touches `_fullRows`. Re-point `rows` at it\n // (search path re-derives) so the new row reaches the renderer.\n if (!this.searchActive) this.rows = this._fullRows;\n else this._applySearch();\n return;\n }\n if (delta.op === FRAME_DELTA_DEL && delta.id !== undefined) {\n const idStr = String(delta.id);\n const idx = this._findRowByIdStr(target, idStr);\n if (idx >= 0) {\n target.splice(idx, 1);\n this._fullTotalRows--;\n this.totalRows--;\n if (!this.searchActive) this.rows = this._fullRows;\n else this._applySearch();\n }\n return;\n }\n }\n\n search(query: string): void {\n const q = query.trim().toLowerCase();\n if (!q) { this.exitSearch(); return; }\n this.searchActive = true;\n this._query = q;\n this._applySearch();\n }\n\n exitSearch(): void {\n if (!this.searchActive) return;\n this.rows = this._fullRows;\n this.totalRows = this._fullTotalRows;\n this.searchActive = false;\n this._query = \"\";\n }\n\n getRowCount(): number {\n return this.rows.length;\n }\n\n private _findRowById(rows: string[][], rowData: string[]): number {\n if (this._idColIndex < 0) return -1;\n const id = rowData[this._idColIndex];\n for (let i = 0; i < rows.length; i++) {\n if (rows[i]![this._idColIndex] === id) return i;\n }\n return -1;\n }\n\n private _findRowByIdStr(rows: string[][], idStr: string): number {\n if (this._idColIndex < 0) return -1;\n for (let i = 0; i < rows.length; i++) {\n if (rows[i]![this._idColIndex] === idStr) return i;\n }\n return -1;\n }\n\n private _applySearch(): void {\n this.rows = this._fullRows.filter((row) =>\n row.some((value) => value.toLowerCase().includes(this._query)),\n );\n this.totalRows = this.rows.length;\n }\n}\n", "// Wire-format decoders + client\u2192server command encoders for the multiplexed\n// bulk WS protocol. Frame opcodes and column-type bytes live in\n// @/generated/bulk-protocol (kept in sync with modules/cart/ws.jai).\n//\n// Every server\u2192client binary frame is [op:u8][sid:u16 LE][payload]. The sid is\n// the client-chosen subscription id; one socket multiplexes many entities, and\n// the provider routes each frame to the matching view by sid (getFrameSid).\n// Only the parse/encode bodies are hand-maintained here.\n\nimport {\n FRAME_SCHEMA,\n FRAME_DATA,\n FRAME_DELTA_DEL,\n FRAME_DELTA_INS,\n FRAME_DELTA_UPD,\n TYPE_U8,\n TYPE_S8,\n TYPE_BOOL,\n TYPE_U16,\n TYPE_S16,\n TYPE_U32,\n TYPE_S32,\n TYPE_FLOAT32,\n TYPE_U64,\n TYPE_S64,\n TYPE_FLOAT64,\n TYPE_DATETIME,\n TYPE_STRING,\n TYPE_DOCUMENT,\n fixedSizeForType,\n type ColumnDef,\n type SchemaFrame,\n type DeltaFrame,\n} from \"../generated/bulk-protocol\";\n\nconst textDecoder = new TextDecoder();\n\n// Bytes between the op byte and the frame payload: the u16 LE subscription id.\nconst SID_BYTES = 2;\n// Offset of the payload in every server\u2192client frame (op + sid).\nconst HEAD = 1 + SID_BYTES;\n\nfunction epochToDate(epoch: number): Date {\n const abs = Math.abs(epoch);\n if (abs < 10_000_000_000) return new Date(epoch * 1000);\n if (abs < 10_000_000_000_000) return new Date(epoch);\n if (abs < 10_000_000_000_000_000) return new Date(epoch / 1000);\n return new Date(epoch / 1_000_000);\n}\n\nfunction formatCellValue(view: DataView, offset: number, col: ColumnDef): [string, number] {\n switch (col.type) {\n case TYPE_U8: return [String(view.getUint8(offset)), 1];\n case TYPE_S8: return [String(view.getInt8(offset)), 1];\n case TYPE_BOOL: return [view.getUint8(offset) ? \"true\" : \"false\", 1];\n case TYPE_U16: return [String(view.getUint16(offset, true)), 2];\n case TYPE_S16: return [String(view.getInt16(offset, true)), 2];\n case TYPE_U32: return [String(view.getUint32(offset, true)), 4];\n case TYPE_S32: return [String(view.getInt32(offset, true)), 4];\n case TYPE_FLOAT32: return [String(view.getFloat32(offset, true)), 4];\n case TYPE_U64: return [String(view.getBigUint64(offset, true)), 8];\n case TYPE_S64: return [String(view.getBigInt64(offset, true)), 8];\n case TYPE_FLOAT64: return [String(view.getFloat64(offset, true)), 8];\n case TYPE_DATETIME: {\n const epoch = Number(view.getBigInt64(offset, true));\n if (epoch === 0) return [\"\", 8];\n return [epochToDate(epoch).toISOString(), 8];\n }\n case TYPE_STRING:\n case TYPE_DOCUMENT: {\n const len = view.getUint32(offset, true);\n const bytes = new Uint8Array(view.buffer, view.byteOffset + offset + 4, len);\n return [textDecoder.decode(bytes), 4 + len];\n }\n default:\n return [\"\", 0];\n }\n}\n\nexport function getFrameType(buf: ArrayBuffer): number {\n return new DataView(buf).getUint8(0);\n}\n\n// The subscription id this frame belongs to (u16 LE right after the op byte).\nexport function getFrameSid(buf: ArrayBuffer): number {\n return new DataView(buf).getUint16(1, true);\n}\n\nexport function parseSchema(buf: ArrayBuffer): SchemaFrame {\n const view = new DataView(buf);\n let offset = 0;\n const frameType = view.getUint8(offset); offset += 1;\n if (frameType !== FRAME_SCHEMA) {\n throw new Error(`Expected SCHEMA frame (0x01), got 0x${frameType.toString(16)}`);\n }\n offset += SID_BYTES;\n const totalRows = view.getUint32(offset, true); offset += 4;\n const columnCount = view.getUint16(offset, true); offset += 2;\n const columns: ColumnDef[] = [];\n for (let i = 0; i < columnCount; i++) {\n const type = view.getUint8(offset); offset += 1;\n const nameLen = view.getUint16(offset, true); offset += 2;\n const nameBytes = new Uint8Array(buf, offset, nameLen);\n const name = textDecoder.decode(nameBytes);\n offset += nameLen;\n columns.push({ name, type, fixedSize: fixedSizeForType(type) });\n }\n return { totalRows, columns };\n}\n\nexport function parseDataChunk(buf: ArrayBuffer, columns: ColumnDef[]): string[][] {\n const view = new DataView(buf);\n let offset = 0;\n const frameType = view.getUint8(offset); offset += 1;\n if (frameType !== FRAME_DATA) {\n throw new Error(`Expected DATA frame, got 0x${frameType.toString(16)}`);\n }\n offset += SID_BYTES;\n const rowCount = view.getUint16(offset, true); offset += 2;\n const rows: string[][] = [];\n for (let r = 0; r < rowCount; r++) {\n const row: string[] = [];\n for (let c = 0; c < columns.length; c++) {\n const [value, bytesRead] = formatCellValue(view, offset, columns[c]!);\n row.push(value);\n offset += bytesRead;\n }\n rows.push(row);\n }\n return rows;\n}\n\nexport function parseDelta(buf: ArrayBuffer, columns: ColumnDef[]): DeltaFrame {\n const view = new DataView(buf);\n let offset = 0;\n const op = view.getUint8(offset); offset += 1;\n offset += SID_BYTES;\n if (op === FRAME_DELTA_DEL) {\n // [id_len:u16 LE][id_bytes] \u2014 the row id is a string (ULID), not an int.\n const len = view.getUint16(offset, true); offset += 2;\n const bytes = new Uint8Array(buf, offset, len);\n return { op, id: textDecoder.decode(bytes) };\n }\n if (op === FRAME_DELTA_INS || op === FRAME_DELTA_UPD) {\n const rowData: string[] = [];\n for (let c = 0; c < columns.length; c++) {\n const [value, bytesRead] = formatCellValue(view, offset, columns[c]!);\n rowData.push(value);\n offset += bytesRead;\n }\n return { op, rowData };\n }\n throw new Error(`Unknown delta op 0x${op.toString(16)}`);\n}\n\nexport function parseError(buf: ArrayBuffer): string {\n const view = new DataView(buf);\n // [0xFF][sid:u16][msg_len:u16 LE][msg_bytes]\n const len = view.getUint16(HEAD, true);\n const bytes = new Uint8Array(buf, HEAD + 2, len);\n return textDecoder.decode(bytes);\n}\n\n// --- client \u2192 server command encoders ---\n// Each command carries the subscription id; sub/schema also name the target.\n\nexport function encodeStreamCmd(\n sid: number,\n cart: string,\n entity: string,\n opts?: { sort?: string; sort_desc?: boolean; filters?: Record<string, string> },\n): string {\n return JSON.stringify({ cmd: \"sub\", sid, cart, entity, ...opts });\n}\n\nexport function encodeUnsubCmd(sid: number): string {\n return JSON.stringify({ cmd: \"unsub\", sid });\n}\n"],
|
|
5
|
+
"mappings": ";;;AAAA,OAAO,WAAW;AAoBd;AAVJ,IAAM,wBAAwB,MAAM,cAA8B;AAAA,EAChE,YAAY;AACd,CAAC;AAEM,IAAM,yBAGR,CAAC,EAAE,aAAa,IAAI,SAAS,MAAM;AACtC,QAAM,QAAQ,MAAM,QAAQ,OAAO,EAAE,WAAW,IAAI,CAAC,UAAU,CAAC;AAChE,SACE,oBAAC,sBAAsB,UAAtB,EAA+B,OAC7B,UACH;AAEJ;AAEO,SAAS,oBAAoC;AAClD,SAAO,MAAM,WAAW,qBAAqB;AAC/C;AAGO,SAAS,WAAW,YAA4B;AACrD,MAAI,CAAC,YAAY;AACf,UAAM,MAAM,OAAO;AACnB,UAAM,QAAQ,IAAI,aAAa,WAAW,SAAS;AACnD,WAAO,GAAG,KAAK,KAAK,IAAI,IAAI;AAAA,EAC9B;AACA,SAAO,WAAW,QAAQ,cAAc,OAAO;AACjD;;;AC/BA,IAAM,YAAY;AAEX,SAAS,mBAAkC;AAChD,MAAI;AACF,WAAO,aAAa,QAAQ,SAAS;AAAA,EACvC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,iBAAiB,OAAqB;AACpD,MAAI;AACF,iBAAa,QAAQ,WAAW,KAAK;AAAA,EACvC,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,qBAA2B;AACzC,MAAI;AACF,iBAAa,WAAW,SAAS;AAAA,EACnC,QAAQ;AAAA,EAER;AACF;AAGO,SAAS,YAAY,MAAuD;AACjF,QAAM,IAA4B,EAAE,GAAI,QAAQ,CAAC,EAAG;AACpD,QAAM,IAAI,iBAAiB;AAC3B,MAAI,EAAG,GAAE,eAAe,IAAI,UAAU,CAAC;AACvC,SAAO;AACT;AAaA,eAAsB,YAAY,YAAoB,QAAuC;AAC3F,QAAM,IAAI,MAAM,MAAM,GAAG,UAAU,oBAAoB;AAAA,IACrD;AAAA,IACA,aAAa;AAAA,IACb,SAAS,YAAY;AAAA,EACvB,CAAC;AACD,MAAI,CAAC,EAAE,GAAI,OAAM,IAAI,MAAM,QAAQ,EAAE,MAAM,EAAE;AAC7C,SAAQ,MAAM,EAAE,KAAK;AACvB;AAMA,eAAsB,sBACpB,YACA,QACkC;AAClC,MAAI;AACF,UAAM,IAAI,MAAM,MAAM,GAAG,UAAU,sBAAsB;AAAA,MACvD;AAAA,MACA,aAAa;AAAA,MACb,SAAS,YAAY;AAAA,IACvB,CAAC;AACD,QAAI,CAAC,EAAE,GAAI,QAAO,CAAC;AACnB,UAAM,IAAI,MAAM,EAAE,KAAK;AACvB,WAAO,KAAK,OAAO,MAAM,WAAY,IAAgC,CAAC;AAAA,EACxE,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAIA,eAAsB,qBACpB,YACA,MACe;AACf,MAAI;AACF,UAAM,MAAM,GAAG,UAAU,sBAAsB;AAAA,MAC7C,QAAQ;AAAA,MACR,aAAa;AAAA,MACb,SAAS,YAAY,EAAE,gBAAgB,mBAAmB,CAAC;AAAA,MAC3D,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAAA,EACH,QAAQ;AAAA,EAER;AACF;AAIA,eAAsB,eACpB,YACA,OACA,UACe;AACf,QAAM,IAAI,MAAM,MAAM,GAAG,UAAU,yBAAyB;AAAA,IAC1D,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,SAAS,CAAC;AAAA,EAC1C,CAAC;AACD,MAAI,CAAC,EAAE,IAAI;AACT,UAAM,IAAI,MAAM,EAAE,WAAW,MAAM,8BAA8B,wBAAwB,EAAE,MAAM,GAAG;AAAA,EACtG;AACA,QAAM,IAAK,MAAM,EAAE,KAAK;AACxB,MAAI,CAAC,EAAE,MAAO,OAAM,IAAI,MAAM,2BAA2B;AACzD,mBAAiB,EAAE,KAAK;AAC1B;AAIA,eAAsB,eACpB,YACA,MACA,OACA,UACe;AACf,QAAM,IAAI,MAAM,MAAM,GAAG,UAAU,yBAAyB;AAAA,IAC1D,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,EAAE,MAAM,OAAO,SAAS,CAAC;AAAA,EAChD,CAAC;AACD,MAAI,CAAC,EAAE,IAAI;AACT,QAAI,SAAS,QAAQ,EAAE,MAAM;AAC7B,QAAI,EAAE,WAAW,IAAK,UAAS;AAAA,SAC1B;AACH,UAAI;AACF,cAAMA,KAAK,MAAM,EAAE,KAAK;AACxB,YAAIA,IAAG,MAAO,UAASA,GAAE;AAAA,MAC3B,QAAQ;AAAA,MAER;AAAA,IACF;AACA,UAAM,IAAI,MAAM,mBAAmB,MAAM,EAAE;AAAA,EAC7C;AACA,QAAM,IAAK,MAAM,EAAE,KAAK;AACxB,MAAI,CAAC,EAAE,MAAO,OAAM,IAAI,MAAM,2BAA2B;AACzD,mBAAiB,EAAE,KAAK;AAC1B;AAEO,SAAS,kBAAwB;AACtC,qBAAmB;AACrB;AAEA,SAAS,gBAAgB,OAAuB;AAC9C,MAAI,OAAO,SAAS,YAAY;AAC9B,WAAO,KAAK,KAAK,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,QAAQ,EAAE;AAAA,EAC/E;AACA,QAAM,IAAI,MAAM,yCAAyC;AAC3D;AAMO,SAAS,gBAAgB,MAAwB;AACtD,QAAM,IAAI,iBAAiB;AAC3B,SAAO,IAAI,CAAC,MAAM,QAAQ,gBAAgB,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI;AACzD;;;AC7KA,OAAOC;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACHA,IAAM,eAAe;AACrB,IAAM,aAAa;AACnB,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AACxB,IAAM,cAAc;AAGpB,IAAM,UAAU;AAChB,IAAM,WAAW;AACjB,IAAM,WAAW;AACjB,IAAM,WAAW;AACjB,IAAM,UAAU;AAChB,IAAM,WAAW;AACjB,IAAM,WAAW;AACjB,IAAM,WAAW;AACjB,IAAM,YAAY;AAClB,IAAM,eAAe;AACrB,IAAM,eAAe;AACrB,IAAM,cAAc;AACpB,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AAmBtB,SAAS,iBAAiB,MAAsB;AACrD,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAS,aAAO;AAAA,IACrB,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAS,aAAO;AAAA,IACrB,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAU,aAAO;AAAA,IACtB,KAAK;AAAW,aAAO;AAAA,IACvB,KAAK;AAAc,aAAO;AAAA,IAC1B,KAAK;AAAc,aAAO;AAAA,IAC1B,KAAK;AAAa,aAAO;AAAA,IACzB,KAAK;AAAe,aAAO;AAAA,IAC3B,KAAK;AAAe,aAAO;AAAA,IAC3B;AAAS,aAAO;AAAA,EAClB;AACF;;;ACrDO,IAAM,gBAAN,MAAoB;AAAA,EAApB;AACL,mBAAuB,CAAC;AACxB,gBAAmB,CAAC;AACpB,qBAAY;AACZ,wBAAe;AAEf,SAAQ,YAAwB,CAAC;AACjC,SAAQ,iBAAiB;AACzB,SAAQ,cAAc;AACtB,SAAQ,SAAS;AAAA;AAAA,EAEjB,YAAY,QAA2B;AACrC,SAAK,UAAU,OAAO;AACtB,SAAK,YAAY,OAAO;AACxB,SAAK,OAAO,CAAC;AACb,SAAK,YAAY,CAAC;AAClB,SAAK,iBAAiB,OAAO;AAC7B,SAAK,eAAe;AACpB,SAAK,SAAS;AACd,SAAK,cAAc,OAAO,QAAQ,UAAU,CAAC,MAAM,EAAE,SAAS,IAAI;AAAA,EACpE;AAAA,EAEA,YAAY,OAAyB;AACnC,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,YAAM,MAAM,MAAM,CAAC;AACnB,WAAK,UAAU,KAAK,GAAG;AACvB,UAAI,CAAC,KAAK,aAAc,MAAK,KAAK,KAAK,GAAG;AAAA,IAC5C;AAAA,EACF;AAAA,EAEA,WAAW,OAAyB;AAClC,UAAM,SAAS,KAAK;AACpB,QAAI,MAAM,OAAO,mBAAmB,MAAM,SAAS;AACjD,aAAO,KAAK,MAAM,OAAO;AACzB,WAAK;AACL,WAAK;AACL,UAAI,CAAC,KAAK,aAAc,MAAK,OAAO,KAAK;AAAA,UACpC,MAAK,aAAa;AACvB;AAAA,IACF;AACA,QAAI,MAAM,OAAO,mBAAmB,MAAM,SAAS;AACjD,YAAM,MAAM,KAAK,aAAa,QAAQ,MAAM,OAAO;AACnD,UAAI,OAAO,EAAG,QAAO,GAAG,IAAI,MAAM;AAIlC,UAAI,CAAC,KAAK,aAAc,MAAK,OAAO,KAAK;AAAA,UACpC,MAAK,aAAa;AACvB;AAAA,IACF;AACA,QAAI,MAAM,OAAO,mBAAmB,MAAM,OAAO,QAAW;AAC1D,YAAM,QAAQ,OAAO,MAAM,EAAE;AAC7B,YAAM,MAAM,KAAK,gBAAgB,QAAQ,KAAK;AAC9C,UAAI,OAAO,GAAG;AACZ,eAAO,OAAO,KAAK,CAAC;AACpB,aAAK;AACL,aAAK;AACL,YAAI,CAAC,KAAK,aAAc,MAAK,OAAO,KAAK;AAAA,YACpC,MAAK,aAAa;AAAA,MACzB;AACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,OAAO,OAAqB;AAC1B,UAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,QAAI,CAAC,GAAG;AAAE,WAAK,WAAW;AAAG;AAAA,IAAQ;AACrC,SAAK,eAAe;AACpB,SAAK,SAAS;AACd,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,aAAmB;AACjB,QAAI,CAAC,KAAK,aAAc;AACxB,SAAK,OAAO,KAAK;AACjB,SAAK,YAAY,KAAK;AACtB,SAAK,eAAe;AACpB,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,cAAsB;AACpB,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA,EAEQ,aAAa,MAAkB,SAA2B;AAChE,QAAI,KAAK,cAAc,EAAG,QAAO;AACjC,UAAM,KAAK,QAAQ,KAAK,WAAW;AACnC,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAI,KAAK,CAAC,EAAG,KAAK,WAAW,MAAM,GAAI,QAAO;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,gBAAgB,MAAkB,OAAuB;AAC/D,QAAI,KAAK,cAAc,EAAG,QAAO;AACjC,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAI,KAAK,CAAC,EAAG,KAAK,WAAW,MAAM,MAAO,QAAO;AAAA,IACnD;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,eAAqB;AAC3B,SAAK,OAAO,KAAK,UAAU;AAAA,MAAO,CAAC,QACjC,IAAI,KAAK,CAAC,UAAU,MAAM,YAAY,EAAE,SAAS,KAAK,MAAM,CAAC;AAAA,IAC/D;AACA,SAAK,YAAY,KAAK,KAAK;AAAA,EAC7B;AACF;;;ACjFA,IAAM,cAAc,IAAI,YAAY;AAGpC,IAAM,YAAY;AAElB,IAAM,OAAO,IAAI;AAEjB,SAAS,YAAY,OAAqB;AACxC,QAAM,MAAM,KAAK,IAAI,KAAK;AAC1B,MAAI,MAAM,KAAgB,QAAO,IAAI,KAAK,QAAQ,GAAI;AACtD,MAAI,MAAM,KAAoB,QAAO,IAAI,KAAK,KAAK;AACnD,MAAI,MAAM,KAAwB,QAAO,IAAI,KAAK,QAAQ,GAAI;AAC9D,SAAO,IAAI,KAAK,QAAQ,GAAS;AACnC;AAEA,SAAS,gBAAgB,MAAgB,QAAgB,KAAkC;AACzF,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AAAS,aAAO,CAAC,OAAO,KAAK,SAAS,MAAM,CAAC,GAAG,CAAC;AAAA,IACtD,KAAK;AAAS,aAAO,CAAC,OAAO,KAAK,QAAQ,MAAM,CAAC,GAAG,CAAC;AAAA,IACrD,KAAK;AAAW,aAAO,CAAC,KAAK,SAAS,MAAM,IAAI,SAAS,SAAS,CAAC;AAAA,IACnE,KAAK;AAAU,aAAO,CAAC,OAAO,KAAK,UAAU,QAAQ,IAAI,CAAC,GAAG,CAAC;AAAA,IAC9D,KAAK;AAAU,aAAO,CAAC,OAAO,KAAK,SAAS,QAAQ,IAAI,CAAC,GAAG,CAAC;AAAA,IAC7D,KAAK;AAAU,aAAO,CAAC,OAAO,KAAK,UAAU,QAAQ,IAAI,CAAC,GAAG,CAAC;AAAA,IAC9D,KAAK;AAAU,aAAO,CAAC,OAAO,KAAK,SAAS,QAAQ,IAAI,CAAC,GAAG,CAAC;AAAA,IAC7D,KAAK;AAAc,aAAO,CAAC,OAAO,KAAK,WAAW,QAAQ,IAAI,CAAC,GAAG,CAAC;AAAA,IACnE,KAAK;AAAU,aAAO,CAAC,OAAO,KAAK,aAAa,QAAQ,IAAI,CAAC,GAAG,CAAC;AAAA,IACjE,KAAK;AAAU,aAAO,CAAC,OAAO,KAAK,YAAY,QAAQ,IAAI,CAAC,GAAG,CAAC;AAAA,IAChE,KAAK;AAAc,aAAO,CAAC,OAAO,KAAK,WAAW,QAAQ,IAAI,CAAC,GAAG,CAAC;AAAA,IACnE,KAAK,eAAe;AAClB,YAAM,QAAQ,OAAO,KAAK,YAAY,QAAQ,IAAI,CAAC;AACnD,UAAI,UAAU,EAAG,QAAO,CAAC,IAAI,CAAC;AAC9B,aAAO,CAAC,YAAY,KAAK,EAAE,YAAY,GAAG,CAAC;AAAA,IAC7C;AAAA,IACA,KAAK;AAAA,IACL,KAAK,eAAe;AAClB,YAAM,MAAM,KAAK,UAAU,QAAQ,IAAI;AACvC,YAAM,QAAQ,IAAI,WAAW,KAAK,QAAQ,KAAK,aAAa,SAAS,GAAG,GAAG;AAC3E,aAAO,CAAC,YAAY,OAAO,KAAK,GAAG,IAAI,GAAG;AAAA,IAC5C;AAAA,IACA;AACE,aAAO,CAAC,IAAI,CAAC;AAAA,EACjB;AACF;AAEO,SAAS,aAAa,KAA0B;AACrD,SAAO,IAAI,SAAS,GAAG,EAAE,SAAS,CAAC;AACrC;AAGO,SAAS,YAAY,KAA0B;AACpD,SAAO,IAAI,SAAS,GAAG,EAAE,UAAU,GAAG,IAAI;AAC5C;AAEO,SAAS,YAAY,KAA+B;AACzD,QAAM,OAAO,IAAI,SAAS,GAAG;AAC7B,MAAI,SAAS;AACb,QAAM,YAAY,KAAK,SAAS,MAAM;AAAG,YAAU;AACnD,MAAI,cAAc,cAAc;AAC9B,UAAM,IAAI,MAAM,uCAAuC,UAAU,SAAS,EAAE,CAAC,EAAE;AAAA,EACjF;AACA,YAAU;AACV,QAAM,YAAY,KAAK,UAAU,QAAQ,IAAI;AAAG,YAAU;AAC1D,QAAM,cAAc,KAAK,UAAU,QAAQ,IAAI;AAAG,YAAU;AAC5D,QAAM,UAAuB,CAAC;AAC9B,WAAS,IAAI,GAAG,IAAI,aAAa,KAAK;AACpC,UAAM,OAAO,KAAK,SAAS,MAAM;AAAG,cAAU;AAC9C,UAAM,UAAU,KAAK,UAAU,QAAQ,IAAI;AAAG,cAAU;AACxD,UAAM,YAAY,IAAI,WAAW,KAAK,QAAQ,OAAO;AACrD,UAAM,OAAO,YAAY,OAAO,SAAS;AACzC,cAAU;AACV,YAAQ,KAAK,EAAE,MAAM,MAAM,WAAW,iBAAiB,IAAI,EAAE,CAAC;AAAA,EAChE;AACA,SAAO,EAAE,WAAW,QAAQ;AAC9B;AAEO,SAAS,eAAe,KAAkB,SAAkC;AACjF,QAAM,OAAO,IAAI,SAAS,GAAG;AAC7B,MAAI,SAAS;AACb,QAAM,YAAY,KAAK,SAAS,MAAM;AAAG,YAAU;AACnD,MAAI,cAAc,YAAY;AAC5B,UAAM,IAAI,MAAM,8BAA8B,UAAU,SAAS,EAAE,CAAC,EAAE;AAAA,EACxE;AACA,YAAU;AACV,QAAM,WAAW,KAAK,UAAU,QAAQ,IAAI;AAAG,YAAU;AACzD,QAAM,OAAmB,CAAC;AAC1B,WAAS,IAAI,GAAG,IAAI,UAAU,KAAK;AACjC,UAAM,MAAgB,CAAC;AACvB,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,YAAM,CAAC,OAAO,SAAS,IAAI,gBAAgB,MAAM,QAAQ,QAAQ,CAAC,CAAE;AACpE,UAAI,KAAK,KAAK;AACd,gBAAU;AAAA,IACZ;AACA,SAAK,KAAK,GAAG;AAAA,EACf;AACA,SAAO;AACT;AAEO,SAAS,WAAW,KAAkB,SAAkC;AAC7E,QAAM,OAAO,IAAI,SAAS,GAAG;AAC7B,MAAI,SAAS;AACb,QAAM,KAAK,KAAK,SAAS,MAAM;AAAG,YAAU;AAC5C,YAAU;AACV,MAAI,OAAO,iBAAiB;AAE1B,UAAM,MAAM,KAAK,UAAU,QAAQ,IAAI;AAAG,cAAU;AACpD,UAAM,QAAQ,IAAI,WAAW,KAAK,QAAQ,GAAG;AAC7C,WAAO,EAAE,IAAI,IAAI,YAAY,OAAO,KAAK,EAAE;AAAA,EAC7C;AACA,MAAI,OAAO,mBAAmB,OAAO,iBAAiB;AACpD,UAAM,UAAoB,CAAC;AAC3B,aAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;AACvC,YAAM,CAAC,OAAO,SAAS,IAAI,gBAAgB,MAAM,QAAQ,QAAQ,CAAC,CAAE;AACpE,cAAQ,KAAK,KAAK;AAClB,gBAAU;AAAA,IACZ;AACA,WAAO,EAAE,IAAI,QAAQ;AAAA,EACvB;AACA,QAAM,IAAI,MAAM,sBAAsB,GAAG,SAAS,EAAE,CAAC,EAAE;AACzD;AAEO,SAAS,WAAW,KAA0B;AACnD,QAAM,OAAO,IAAI,SAAS,GAAG;AAE7B,QAAM,MAAM,KAAK,UAAU,MAAM,IAAI;AACrC,QAAM,QAAQ,IAAI,WAAW,KAAK,OAAO,GAAG,GAAG;AAC/C,SAAO,YAAY,OAAO,KAAK;AACjC;AAKO,SAAS,gBACd,KACA,MACA,QACA,MACQ;AACR,SAAO,KAAK,UAAU,EAAE,KAAK,OAAO,KAAK,MAAM,QAAQ,GAAG,KAAK,CAAC;AAClE;;;AHrGA,IAAM,oBAAoB,cAA0B,IAAI;AAExD,SAASC,iBAAgB,OAAuB;AAC9C,MAAI,OAAO,SAAS,YAAY;AAC9B,WAAO,KAAK,KAAK,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,OAAO,GAAG,EAAE,QAAQ,QAAQ,EAAE;AAAA,EAC/E;AACA,QAAM,IAAI,MAAM,8CAA8C;AAChE;AAEA,SAAS,kBAAkB,OAAyB;AAClD,SAAO,CAAC,eAAe,QAAQA,iBAAgB,KAAK,CAAC,EAAE;AACzD;AAEA,IAAI,gBAA0E;AAC9E,IAAM,kBAAkB;AAOxB,eAAe,aACb,QACA,YACwB;AACxB,QAAM,MAAM,iBAAiB;AAC7B,MAAI,IAAK,QAAO;AAChB,QAAM,MAAM,KAAK,IAAI;AACrB,MAAI,eAAe,eAAe,cAAc,MAAM,cAAc,KAAK,iBAAiB;AACxF,WAAO,cAAc;AAAA,EACvB;AACA,QAAM,IAAI,MAAM,MAAM,GAAG,UAAU,6BAA6B;AAAA,IAC9D,QAAQ;AAAA,IACR;AAAA,IACA,aAAa;AAAA,IACb,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,EAChD,CAAC;AACD,MAAI,CAAC,EAAE,GAAI,QAAO;AAClB,QAAM,IAAK,MAAM,EAAE,KAAK;AACxB,QAAM,QAAQ,EAAE,SAAS;AACzB,MAAI,CAAC,MAAO,QAAO;AACnB,kBAAgB,EAAE,YAAY,OAAO,IAAI,IAAI;AAC7C,SAAO;AACT;AAEA,SAAS,oBAAoB;AAC3B,kBAAgB;AAClB;AAEA,SAAS,OAAO,MAAY;AAC1B,OAAK,UAAU,QAAQ,CAAC,OAAO,GAAG,CAAC;AACrC;AAIO,SAAS,mBAAmB,EAAE,UAAU,SAAS,GAA8B;AACpF,QAAM,EAAE,WAAW,IAAI,kBAAkB;AACzC,QAAM,QAAQ,OAAyB,IAAI;AAC3C,QAAM,eAAe,OAAiB,CAAC,CAAC;AACxC,QAAM,gBAAgB,OAAO,CAAC;AAC9B,QAAM,gBAAgB,OAA0B,oBAAI,IAAI,CAAC;AACzD,QAAM,gBAAgB,OAA0B,oBAAI,IAAI,CAAC;AACzD,QAAM,eAAe,OAAO,KAAK;AAEjC,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AAEtD,QAAM,UAAU,CAAC,QAAgB;AAC/B,UAAM,KAAK,MAAM;AACjB,QAAI,MAAM,GAAG,eAAe,UAAU,KAAM,IAAG,KAAK,GAAG;AAAA,QAClD,cAAa,QAAQ,KAAK,GAAG;AAAA,EACpC;AAEA,QAAM,aAAa,MAAM;AACvB,UAAM,KAAK,MAAM;AACjB,QAAI,CAAC,MAAM,GAAG,eAAe,UAAU,KAAM;AAC7C,UAAM,IAAI,aAAa;AACvB,iBAAa,UAAU,CAAC;AACxB,eAAW,OAAO,EAAG,IAAG,KAAK,GAAG;AAAA,EAClC;AAEA,QAAM,UAAU,CAAC,MAAc,WAAyB;AAItD,UAAM,WAAW,OAAO,YAAY;AACpC,UAAM,MAAM,GAAG,IAAI,IAAI,QAAQ;AAC/B,UAAM,WAAW,cAAc,QAAQ,IAAI,GAAG;AAC9C,QAAI,SAAU,QAAO;AACrB,UAAM,MAAM,cAAc;AAC1B,UAAM,OAAa;AAAA,MACjB;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,OAAO,IAAI,cAAc;AAAA,MACzB,SAAS,CAAC;AAAA,MACV,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,OAAO;AAAA,MACP,iBAAiB;AAAA,MACjB,UAAU;AAAA,MACV,WAAW,oBAAI,IAAI;AAAA,IACrB;AACA,kBAAc,QAAQ,IAAI,KAAK,IAAI;AACnC,kBAAc,QAAQ,IAAI,KAAK,IAAI;AACnC,WAAO;AAAA,EACT;AAEA,QAAM,iBAAiB,CAAC,SAAe;AACrC,QAAI,KAAK,gBAAiB;AAC1B,SAAK,kBAAkB;AACvB,YAAQ,gBAAgB,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,CAAC;AAAA,EAC3D;AAEA,QAAM,gBAAgB,CAAC,OAAqB;AAC1C,UAAM,MAAM,GAAG;AACf,UAAM,MAAM,YAAY,GAAG;AAC3B,UAAM,OAAO,cAAc,QAAQ,IAAI,GAAG;AAC1C,QAAI,CAAC,KAAM;AACX,UAAM,KAAK,aAAa,GAAG;AAC3B,UAAM,QAAQ,KAAK;AACnB,YAAQ,IAAI;AAAA,MACV,KAAK,cAAc;AACjB,cAAM,SAAS,YAAY,GAAG;AAC9B,cAAM,YAAY,MAAM;AACxB,aAAK,UAAU,OAAO;AACtB,aAAK,YAAY,OAAO;AACxB,aAAK,aAAa;AAClB,aAAK,YAAY;AACjB,eAAO,IAAI;AACX;AAAA,MACF;AAAA,MACA,KAAK,YACL;AACE,cAAM,OAAO,eAAe,KAAK,MAAM,OAAO;AAC9C,cAAM,YAAY,IAAI;AACtB,aAAK,aAAa,MAAM,YAAY;AACpC,aAAK,WAAW;AAChB;AAAA,MACF;AAAA,MACA,KAAK;AACH,aAAK,YAAY;AACjB,eAAO,IAAI;AACX;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,iBAAiB;AACpB,cAAM,QAAQ,WAAW,KAAK,MAAM,OAAO;AAC3C,cAAM,WAAW,KAAK;AACtB,aAAK,YAAY,MAAM;AACvB,aAAK,aAAa,MAAM,YAAY;AACpC,aAAK,WAAW;AAChB,eAAO,IAAI;AACX;AAAA,MACF;AAAA,MACA,KAAK;AACH,aAAK,QAAQ,WAAW,GAAG;AAC3B,aAAK,YAAY;AACjB,eAAO,IAAI;AACX;AAAA,IACJ;AAAA,EACF;AAGA,YAAU,MAAM;AACd,UAAM,KAAK,IAAI,gBAAgB;AAC/B,QAAI,YAAY;AAChB,QAAI,SAAS;AACb,QAAI,KAAuB;AAC3B,iBAAa,UAAU,CAAC;AACxB,kBAAc,UAAU;AACxB,kBAAc,QAAQ,MAAM;AAC5B,kBAAc,QAAQ,MAAM;AAC5B,iBAAa,UAAU;AACvB,iBAAa,KAAK;AAClB,aAAS,IAAI;AAEb,KAAC,YAAY;AACX,UAAI,QAAuB;AAC3B,UAAI;AACF,gBAAQ,MAAM,aAAa,GAAG,QAAQ,UAAU;AAAA,MAClD,QAAQ;AAAA,MAGR;AACA,UAAI,UAAW;AAGf,WAAK,QACD,IAAI,UAAU,GAAG,WAAW,UAAU,CAAC,aAAa,kBAAkB,KAAK,CAAC,IAC5E,IAAI,UAAU,GAAG,WAAW,UAAU,CAAC,aAAa,CAAC,aAAa,CAAC;AACvE,SAAG,aAAa;AAChB,YAAM,UAAU;AAChB,SAAG,SAAS,MAAM;AAChB,YAAI,aAAa,MAAM,YAAY,GAAI;AACvC,iBAAS;AACT,qBAAa,IAAI;AACjB,iBAAS,IAAI;AACb,mBAAW;AAAA,MACb;AACA,SAAG,YAAY;AACf,SAAG,UAAU,MAAM;AAAA,MAAC;AACpB,SAAG,UAAU,MAAM;AACjB,YAAI,aAAa,MAAM,YAAY,GAAI;AACvC,qBAAa,KAAK;AAClB,cAAM,UAAU;AAChB,YAAI,CAAC,QAAQ;AACX,4BAAkB;AAClB,mBAAS,6BAA6B;AAAA,QACxC;AAAA,MACF;AAAA,IACF,GAAG;AAEH,WAAO,MAAM;AACX,kBAAY;AACZ,SAAG,MAAM;AACT,UAAI,IAAI;AACN,WAAG,SAAS,GAAG,YAAY,GAAG,UAAU,GAAG,UAAU;AACrD,YAAI;AACF,aAAG,MAAM;AAAA,QACX,QAAQ;AAAA,QAAC;AACT,YAAI,MAAM,YAAY,GAAI,OAAM,UAAU;AAAA,MAC5C;AAAA,IACF;AAAA,EAEF,GAAG,CAAC,UAAU,CAAC;AAIf,YAAU,MAAM;AACd,QAAI,CAAC,UAAW;AAChB,QAAI,aAAa,QAAS;AAC1B,UAAM,OAAO,OAAO,KAAK,QAAQ;AACjC,QAAI,KAAK,WAAW,EAAG;AACvB,iBAAa,UAAU;AACvB,eAAW,QAAQ,MAAM;AACvB,iBAAW,OAAO,SAAS,IAAI,KAAK,CAAC,GAAG;AACtC,cAAM,OAAO,QAAQ,MAAM,IAAI,IAAI;AACnC,uBAAe,IAAI;AAAA,MACrB;AAAA,IACF;AAAA,EAEF,GAAG,CAAC,WAAW,QAAQ,CAAC;AAExB,QAAM,SAAS,CAAC,MAAY,UAAkB;AAC5C,SAAK,MAAM,OAAO,KAAK;AACvB,SAAK,YAAY,KAAK,MAAM;AAC5B,SAAK,aAAa,KAAK,MAAM,YAAY;AACzC,WAAO,IAAI;AACX,SAAK,WAAW;AAAA,EAClB;AAEA,QAAM,cAAc,CAAC,SAAe;AAClC,SAAK,MAAM,WAAW;AACtB,SAAK,YAAY,KAAK,MAAM;AAC5B,SAAK,aAAa,KAAK,MAAM,YAAY;AACzC,WAAO,IAAI;AACX,SAAK,WAAW;AAAA,EAClB;AAEA,QAAM,YAAY,CAAC,MAAY,OAAmB;AAChD,SAAK,UAAU,IAAI,EAAE;AACrB,WAAO,MAAM;AACX,WAAK,UAAU,OAAO,EAAE;AAAA,IAC1B;AAAA,EACF;AAEA,QAAM,MAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAOC,OAAM,cAAc,kBAAkB,UAAU,EAAE,OAAO,IAAI,GAAG,QAAQ;AACjF;AAMO,SAAS,iBAAiB,UAAgD;AAC/E,QAAM,MAAM,WAAW,iBAAiB;AACxC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,yDAAyD;AACnF,QAAM,CAAC,EAAE,KAAK,IAAI,SAAS,CAAC;AAE5B,QAAM,QAA2C,CAAC;AAClD,aAAW,QAAQ,OAAO,KAAK,QAAQ,GAAG;AACxC,eAAW,OAAO,SAAS,IAAI,KAAK,CAAC,GAAG;AACtC,YAAM,KAAK,EAAE,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,IAAI,MAAM,IAAI,QAAQ,MAAM,IAAI,IAAI,EAAE,CAAC;AAAA,IAC7E;AAAA,EACF;AAEA,QAAM,MAAM,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,GAAG;AAC3C,YAAU,MAAM;AACd,UAAM,SAAS,MAAM,IAAI,CAAC,MAAM,IAAI,UAAU,EAAE,MAAM,MAAM,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC;AAChF,WAAO,MAAM,OAAO,QAAQ,CAAC,MAAM,EAAE,CAAC;AAAA,EAExC,GAAG,CAAC,GAAG,CAAC;AAER,QAAM,SAAiC,CAAC;AACxC,aAAW,KAAK,MAAO,QAAO,EAAE,EAAE,IAAI,EAAE,KAAK;AAC7C,SAAO;AACT;AAIO,SAAS,oBAAoB,MAAc,QAAkC;AAClF,QAAM,MAAM,WAAW,iBAAiB;AACxC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,4DAA4D;AAEtF,QAAM,OAAO,IAAI,QAAQ,MAAM,MAAM;AACrC,QAAM,CAAC,EAAE,KAAK,IAAI,SAAS,CAAC;AAE5B,YAAU,MAAM;AACd,UAAM,QAAQ,IAAI,UAAU,MAAM,MAAM,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;AAC3D,QAAI,eAAe,IAAI;AACvB,WAAO;AAAA,EAET,GAAG,CAAC,IAAI,CAAC;AAET,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,WAAW,IAAI;AAAA,IACf,WAAW,KAAK;AAAA,IAChB,OAAO,KAAK,SAAS,IAAI;AAAA,IACzB,SAAS,KAAK;AAAA,IACd,WAAW,KAAK;AAAA,IAChB,YAAY,KAAK;AAAA,IACjB,QAAQ,CAAC,MAAc,IAAI,OAAO,MAAM,CAAC;AAAA,IACzC,aAAa,MAAM,IAAI,YAAY,IAAI;AAAA,IACvC,aAAa,CAAC,OAAO;AACnB,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AACF;",
|
|
6
|
+
"names": ["j", "React", "base64UrlEncode", "React"]
|
|
7
|
+
}
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
TYPE_U8,
|
|
16
16
|
useBulkSubscription,
|
|
17
17
|
useDevToolsConfig
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-AI2LKTA4.js";
|
|
19
19
|
|
|
20
20
|
// src/table/data-table.tsx
|
|
21
21
|
import React2 from "react";
|
|
@@ -221,21 +221,114 @@ var ValuePopover = ({
|
|
|
221
221
|
};
|
|
222
222
|
var value_popover_default = ValuePopover;
|
|
223
223
|
|
|
224
|
+
// src/table/column-widths.ts
|
|
225
|
+
var KEY = "nbt_devtools_colw:v1";
|
|
226
|
+
function readBlob() {
|
|
227
|
+
try {
|
|
228
|
+
const raw = localStorage.getItem(KEY);
|
|
229
|
+
if (!raw) return {};
|
|
230
|
+
const parsed = JSON.parse(raw);
|
|
231
|
+
return typeof parsed === "object" && parsed ? parsed : {};
|
|
232
|
+
} catch {
|
|
233
|
+
return {};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function loadColWidths(cart, entity) {
|
|
237
|
+
const blob = readBlob();
|
|
238
|
+
return blob[`${cart}:${entity}`] ?? {};
|
|
239
|
+
}
|
|
240
|
+
function saveColWidth(cart, entity, col, w) {
|
|
241
|
+
try {
|
|
242
|
+
const blob = readBlob();
|
|
243
|
+
const k = `${cart}:${entity}`;
|
|
244
|
+
(blob[k] ?? (blob[k] = {}))[col] = w;
|
|
245
|
+
localStorage.setItem(KEY, JSON.stringify(blob));
|
|
246
|
+
} catch {
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
224
250
|
// src/table/data-table.tsx
|
|
225
251
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
226
252
|
var ROW_H = 22;
|
|
227
253
|
var OVERSCAN = 6;
|
|
228
254
|
var MIN_COL_W = 80;
|
|
255
|
+
var MAX_COL_W = 600;
|
|
229
256
|
var GUTTER_W = 32;
|
|
230
257
|
var READONLY_FIELDS = /* @__PURE__ */ new Set(["id", "createdAt", "updatedAt"]);
|
|
231
258
|
var READONLY_TYPES = /* @__PURE__ */ new Set([TYPE_DATETIME, TYPE_DOCUMENT]);
|
|
232
|
-
function
|
|
259
|
+
function defaultColWidth(name) {
|
|
233
260
|
if (name === "id") return 220;
|
|
234
261
|
if (name === "createdAt" || name === "updatedAt") return 180;
|
|
235
262
|
if (name === "email" || name === "phone") return 180;
|
|
236
263
|
if (name.endsWith("At")) return 180;
|
|
237
264
|
return Math.max(MIN_COL_W, Math.min(240, name.length * 9 + 24));
|
|
238
265
|
}
|
|
266
|
+
var measureCanvas = null;
|
|
267
|
+
function measureCtx() {
|
|
268
|
+
if (!measureCanvas) measureCanvas = document.createElement("canvas");
|
|
269
|
+
return measureCanvas.getContext("2d");
|
|
270
|
+
}
|
|
271
|
+
var ColResizeHandle = ({ index, startWidth, scrollerRef, onCommit, onAutoFit }) => {
|
|
272
|
+
const onPointerDown = (e) => {
|
|
273
|
+
e.preventDefault();
|
|
274
|
+
e.stopPropagation();
|
|
275
|
+
const root = scrollerRef.current;
|
|
276
|
+
if (!root) return;
|
|
277
|
+
const handle = e.currentTarget;
|
|
278
|
+
handle.setPointerCapture(e.pointerId);
|
|
279
|
+
const startX = e.clientX;
|
|
280
|
+
let last = startWidth;
|
|
281
|
+
let moved = false;
|
|
282
|
+
let raf = 0;
|
|
283
|
+
let pending = null;
|
|
284
|
+
const flush = () => {
|
|
285
|
+
raf = 0;
|
|
286
|
+
if (pending == null) return;
|
|
287
|
+
last = pending;
|
|
288
|
+
root.style.setProperty(`--cw-${index}`, `${pending}px`);
|
|
289
|
+
pending = null;
|
|
290
|
+
};
|
|
291
|
+
const onMove = (ev) => {
|
|
292
|
+
ev.preventDefault();
|
|
293
|
+
moved = true;
|
|
294
|
+
pending = Math.max(MIN_COL_W, Math.min(MAX_COL_W, startWidth + (ev.clientX - startX)));
|
|
295
|
+
if (!raf) raf = requestAnimationFrame(flush);
|
|
296
|
+
};
|
|
297
|
+
const onUp = () => {
|
|
298
|
+
if (raf) cancelAnimationFrame(raf);
|
|
299
|
+
flush();
|
|
300
|
+
if (moved) onCommit(last);
|
|
301
|
+
handle.removeEventListener("pointermove", onMove);
|
|
302
|
+
handle.removeEventListener("pointerup", onUp);
|
|
303
|
+
handle.removeEventListener("pointercancel", onUp);
|
|
304
|
+
try {
|
|
305
|
+
handle.releasePointerCapture(e.pointerId);
|
|
306
|
+
} catch {
|
|
307
|
+
}
|
|
308
|
+
document.body.style.userSelect = "";
|
|
309
|
+
document.body.style.cursor = "";
|
|
310
|
+
};
|
|
311
|
+
document.body.style.userSelect = "none";
|
|
312
|
+
document.body.style.cursor = "ew-resize";
|
|
313
|
+
handle.addEventListener("pointermove", onMove);
|
|
314
|
+
handle.addEventListener("pointerup", onUp);
|
|
315
|
+
handle.addEventListener("pointercancel", onUp);
|
|
316
|
+
};
|
|
317
|
+
return /* @__PURE__ */ jsx2(
|
|
318
|
+
"div",
|
|
319
|
+
{
|
|
320
|
+
onPointerDown,
|
|
321
|
+
onDoubleClick: (e) => {
|
|
322
|
+
e.preventDefault();
|
|
323
|
+
e.stopPropagation();
|
|
324
|
+
onAutoFit();
|
|
325
|
+
},
|
|
326
|
+
title: "Drag to resize \xB7 double-click to fit",
|
|
327
|
+
style: { touchAction: "none" },
|
|
328
|
+
className: "absolute right-0 top-0 bottom-0 z-20 w-1.5 cursor-ew-resize hover:bg-accent/40"
|
|
329
|
+
}
|
|
330
|
+
);
|
|
331
|
+
};
|
|
239
332
|
var DataTable = ({ cart, entity, searchFields, onSelectRow }) => {
|
|
240
333
|
const stream = useBulkSubscription(cart, entity);
|
|
241
334
|
const scrollerRef = React2.useRef(null);
|
|
@@ -245,10 +338,15 @@ var DataTable = ({ cart, entity, searchFields, onSelectRow }) => {
|
|
|
245
338
|
const [selected, setSelected] = React2.useState(null);
|
|
246
339
|
const [popover, setPopover] = React2.useState(null);
|
|
247
340
|
const [tick, setTick] = React2.useState(0);
|
|
341
|
+
const [overrides, setOverrides] = React2.useState(
|
|
342
|
+
() => loadColWidths(cart, entity)
|
|
343
|
+
);
|
|
344
|
+
const widthFor = (name) => overrides[name] ?? defaultColWidth(name);
|
|
248
345
|
React2.useEffect(() => {
|
|
249
346
|
stream.setOnRender(() => setTick((n) => n + 1));
|
|
250
347
|
return () => stream.setOnRender(null);
|
|
251
348
|
}, [stream]);
|
|
349
|
+
const hasScroller = stream.columns.length > 0;
|
|
252
350
|
React2.useEffect(() => {
|
|
253
351
|
const el = scrollerRef.current;
|
|
254
352
|
if (!el) return;
|
|
@@ -256,7 +354,7 @@ var DataTable = ({ cart, entity, searchFields, onSelectRow }) => {
|
|
|
256
354
|
ro.observe(el);
|
|
257
355
|
setViewportH(el.clientHeight);
|
|
258
356
|
return () => ro.disconnect();
|
|
259
|
-
}, []);
|
|
357
|
+
}, [hasScroller]);
|
|
260
358
|
const rows = stream.store.rows;
|
|
261
359
|
const columns = stream.columns;
|
|
262
360
|
const totalH = rows.length * ROW_H;
|
|
@@ -279,8 +377,33 @@ var DataTable = ({ cart, entity, searchFields, onSelectRow }) => {
|
|
|
279
377
|
onSelectRow(out);
|
|
280
378
|
};
|
|
281
379
|
const searchDisabled = searchFields.length === 0;
|
|
282
|
-
const totalColW = GUTTER_W + columns.reduce((s, c) => s + colWidth(c.name), 0);
|
|
283
380
|
const idColIndex = columns.findIndex((c) => c.name === "id");
|
|
381
|
+
const widthVars = React2.useMemo(() => {
|
|
382
|
+
const style = { ["--gw"]: `${GUTTER_W}px` };
|
|
383
|
+
const parts = ["var(--gw)"];
|
|
384
|
+
columns.forEach((c, i) => {
|
|
385
|
+
style[`--cw-${i}`] = `${widthFor(c.name)}px`;
|
|
386
|
+
parts.push(`var(--cw-${i})`);
|
|
387
|
+
});
|
|
388
|
+
style["--total-w"] = `calc(${parts.join(" + ")})`;
|
|
389
|
+
return style;
|
|
390
|
+
}, [columns, overrides]);
|
|
391
|
+
const commitWidth = (name, w) => {
|
|
392
|
+
setOverrides((o) => ({ ...o, [name]: w }));
|
|
393
|
+
saveColWidth(cart, entity, name, w);
|
|
394
|
+
};
|
|
395
|
+
const autoFit = (colIndex, name) => {
|
|
396
|
+
const ctx = measureCtx();
|
|
397
|
+
const root = scrollerRef.current;
|
|
398
|
+
if (!ctx || !root) return;
|
|
399
|
+
ctx.font = `12px ${getComputedStyle(root).fontFamily}`;
|
|
400
|
+
let w = ctx.measureText(name).width;
|
|
401
|
+
const cap = Math.min(rows.length, 1e3);
|
|
402
|
+
for (let r = 0; r < cap; r++) w = Math.max(w, ctx.measureText(rows[r]?.[colIndex] ?? "").width);
|
|
403
|
+
const fitted = Math.max(MIN_COL_W, Math.min(MAX_COL_W, Math.ceil(w) + 18));
|
|
404
|
+
root.style.setProperty(`--cw-${colIndex}`, `${fitted}px`);
|
|
405
|
+
commitWidth(name, fitted);
|
|
406
|
+
};
|
|
284
407
|
const scrollRowIntoView = (r) => {
|
|
285
408
|
const el = scrollerRef.current;
|
|
286
409
|
if (!el) return;
|
|
@@ -330,7 +453,7 @@ var DataTable = ({ cart, entity, searchFields, onSelectRow }) => {
|
|
|
330
453
|
)
|
|
331
454
|
}
|
|
332
455
|
),
|
|
333
|
-
/* @__PURE__ */ jsx2("div", { className: "shrink-0 text-[11px] text-muted-foreground tabular-nums", children: stream.error ? /* @__PURE__ */ jsx2("span", { className: "text-red-400", children: stream.error }) :
|
|
456
|
+
/* @__PURE__ */ jsx2("div", { className: "shrink-0 text-[11px] text-muted-foreground tabular-nums", children: stream.error ? /* @__PURE__ */ jsx2("span", { className: "text-red-400", children: stream.error }) : columns.length === 0 ? /* @__PURE__ */ jsx2("span", { children: stream.connected ? "loading\u2026" : "connecting\u2026" }) : /* @__PURE__ */ jsxs2("span", { children: [
|
|
334
457
|
stream.loadedRows.toLocaleString(),
|
|
335
458
|
" / ",
|
|
336
459
|
stream.totalRows.toLocaleString(),
|
|
@@ -349,33 +472,45 @@ var DataTable = ({ cart, entity, searchFields, onSelectRow }) => {
|
|
|
349
472
|
if (popover) setPopover(null);
|
|
350
473
|
},
|
|
351
474
|
className: "group/table relative min-h-0 flex-1 overflow-auto font-mono outline-none",
|
|
352
|
-
children: /* @__PURE__ */ jsxs2("div", { style: { width:
|
|
475
|
+
children: /* @__PURE__ */ jsxs2("div", { style: { ...widthVars, width: "var(--total-w)" }, children: [
|
|
353
476
|
/* @__PURE__ */ jsxs2(
|
|
354
477
|
"div",
|
|
355
478
|
{
|
|
356
|
-
className: "sticky top-0 z-10 flex h-6 select-none border-b border-border bg-background text-[11px]
|
|
357
|
-
style: { width:
|
|
479
|
+
className: "sticky top-0 z-10 flex h-6 select-none border-b border-border bg-background text-[11px] text-muted-foreground",
|
|
480
|
+
style: { width: "var(--total-w)" },
|
|
358
481
|
children: [
|
|
359
482
|
/* @__PURE__ */ jsx2(
|
|
360
483
|
"div",
|
|
361
484
|
{
|
|
362
|
-
style: { width:
|
|
485
|
+
style: { width: "var(--gw)" },
|
|
363
486
|
className: "shrink-0 border-r border-border"
|
|
364
487
|
}
|
|
365
488
|
),
|
|
366
|
-
columns.map((c) => /* @__PURE__ */
|
|
489
|
+
columns.map((c, ci) => /* @__PURE__ */ jsxs2(
|
|
367
490
|
"div",
|
|
368
491
|
{
|
|
369
|
-
style: { width:
|
|
370
|
-
className: "flex items-center overflow-hidden border-r border-border px-2",
|
|
371
|
-
children:
|
|
492
|
+
style: { width: `var(--cw-${ci})` },
|
|
493
|
+
className: "relative flex items-center overflow-hidden border-r border-border px-2",
|
|
494
|
+
children: [
|
|
495
|
+
/* @__PURE__ */ jsx2("span", { className: "truncate", children: c.name }),
|
|
496
|
+
/* @__PURE__ */ jsx2(
|
|
497
|
+
ColResizeHandle,
|
|
498
|
+
{
|
|
499
|
+
index: ci,
|
|
500
|
+
startWidth: widthFor(c.name),
|
|
501
|
+
scrollerRef,
|
|
502
|
+
onCommit: (w) => commitWidth(c.name, w),
|
|
503
|
+
onAutoFit: () => autoFit(ci, c.name)
|
|
504
|
+
}
|
|
505
|
+
)
|
|
506
|
+
]
|
|
372
507
|
},
|
|
373
508
|
c.name
|
|
374
509
|
))
|
|
375
510
|
]
|
|
376
511
|
}
|
|
377
512
|
),
|
|
378
|
-
/* @__PURE__ */ jsx2("div", { style: { height: totalH, width:
|
|
513
|
+
/* @__PURE__ */ jsx2("div", { style: { height: totalH, width: "var(--total-w)", position: "relative" }, children: rows.slice(start, end).map((row, i) => {
|
|
379
514
|
const rowIdx = start + i;
|
|
380
515
|
return /* @__PURE__ */ jsxs2(
|
|
381
516
|
"div",
|
|
@@ -383,7 +518,7 @@ var DataTable = ({ cart, entity, searchFields, onSelectRow }) => {
|
|
|
383
518
|
style: {
|
|
384
519
|
top: rowIdx * ROW_H,
|
|
385
520
|
height: ROW_H,
|
|
386
|
-
width:
|
|
521
|
+
width: "var(--total-w)"
|
|
387
522
|
},
|
|
388
523
|
className: cn(
|
|
389
524
|
"absolute left-0 flex text-[12px] leading-none",
|
|
@@ -396,7 +531,7 @@ var DataTable = ({ cart, entity, searchFields, onSelectRow }) => {
|
|
|
396
531
|
type: "button",
|
|
397
532
|
title: "Open row detail",
|
|
398
533
|
onClick: () => handleRowClick(rowIdx),
|
|
399
|
-
style: { width:
|
|
534
|
+
style: { width: "var(--gw)" },
|
|
400
535
|
className: cn(
|
|
401
536
|
"flex shrink-0 items-center justify-center border-r border-border/60",
|
|
402
537
|
"cursor-pointer text-[10px] tabular-nums text-muted-foreground/60",
|
|
@@ -433,7 +568,7 @@ var DataTable = ({ cart, entity, searchFields, onSelectRow }) => {
|
|
|
433
568
|
editable
|
|
434
569
|
});
|
|
435
570
|
},
|
|
436
|
-
style: { width:
|
|
571
|
+
style: { width: `var(--cw-${ci})` },
|
|
437
572
|
className: cn(
|
|
438
573
|
"flex cursor-pointer items-center overflow-hidden border-r border-border/60 px-2",
|
|
439
574
|
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground",
|
|
@@ -464,4 +599,4 @@ export {
|
|
|
464
599
|
value_popover_default,
|
|
465
600
|
data_table_default
|
|
466
601
|
};
|
|
467
|
-
//# sourceMappingURL=chunk-
|
|
602
|
+
//# sourceMappingURL=chunk-BNA2MLFE.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/table/data-table.tsx", "../src/core/utils.ts", "../src/table/value-popover.tsx", "../src/table/column-widths.ts"],
|
|
4
|
+
"sourcesContent": ["import React from \"react\";\nimport { useBulkSubscription } from \"../core/use-bulk-stream\";\nimport { cn } from \"../core/utils\";\nimport { TYPE_DATETIME, TYPE_DOCUMENT } from \"../generated/bulk-protocol\";\nimport ValuePopover, { type ValuePopoverData } from \"./value-popover\";\nimport { loadColWidths, saveColWidth } from \"./column-widths\";\n\nconst ROW_H = 22;\nconst OVERSCAN = 6;\nconst MIN_COL_W = 80;\nconst MAX_COL_W = 600;\nconst GUTTER_W = 32;\n\nconst READONLY_FIELDS = new Set([\"id\", \"createdAt\", \"updatedAt\"]);\nconst READONLY_TYPES = new Set([TYPE_DATETIME, TYPE_DOCUMENT]);\n\ntype DataTableProps = {\n cart: string;\n entity: string;\n searchFields: readonly string[];\n onSelectRow: (rowJson: Record<string, string>) => void;\n};\n\nfunction defaultColWidth(name: string): number {\n if (name === \"id\") return 220;\n if (name === \"createdAt\" || name === \"updatedAt\") return 180;\n if (name === \"email\" || name === \"phone\") return 180;\n if (name.endsWith(\"At\")) return 180;\n return Math.max(MIN_COL_W, Math.min(240, name.length * 9 + 24));\n}\n\n// Shared offscreen canvas for measuring text width during auto-fit (no DOM reflow).\nlet measureCanvas: HTMLCanvasElement | null = null;\nfunction measureCtx(): CanvasRenderingContext2D | null {\n if (!measureCanvas) measureCanvas = document.createElement(\"canvas\");\n return measureCanvas.getContext(\"2d\");\n}\n\n// Drag handle pinned to a header cell's right edge. Resizes by mutating the\n// column's CSS var on the scroll root each frame (no React churn across the\n// virtualized rows), committing the final width on release. Double-click\n// auto-fits the column to its loaded content.\nconst ColResizeHandle: React.FC<{\n index: number;\n startWidth: number;\n scrollerRef: React.RefObject<HTMLDivElement | null>;\n onCommit: (w: number) => void;\n onAutoFit: () => void;\n}> = ({ index, startWidth, scrollerRef, onCommit, onAutoFit }) => {\n const onPointerDown = (e: React.PointerEvent) => {\n e.preventDefault();\n e.stopPropagation();\n const root = scrollerRef.current;\n if (!root) return;\n const handle = e.currentTarget as HTMLElement;\n handle.setPointerCapture(e.pointerId);\n const startX = e.clientX;\n let last = startWidth;\n let moved = false;\n let raf = 0;\n let pending: number | null = null;\n const flush = () => {\n raf = 0;\n if (pending == null) return;\n last = pending;\n root.style.setProperty(`--cw-${index}`, `${pending}px`);\n pending = null;\n };\n const onMove = (ev: PointerEvent) => {\n ev.preventDefault();\n moved = true;\n pending = Math.max(MIN_COL_W, Math.min(MAX_COL_W, startWidth + (ev.clientX - startX)));\n if (!raf) raf = requestAnimationFrame(flush);\n };\n const onUp = () => {\n if (raf) cancelAnimationFrame(raf);\n flush();\n if (moved) onCommit(last);\n handle.removeEventListener(\"pointermove\", onMove);\n handle.removeEventListener(\"pointerup\", onUp);\n handle.removeEventListener(\"pointercancel\", onUp);\n try {\n handle.releasePointerCapture(e.pointerId);\n } catch {\n /* already released */\n }\n document.body.style.userSelect = \"\";\n document.body.style.cursor = \"\";\n };\n document.body.style.userSelect = \"none\";\n document.body.style.cursor = \"ew-resize\";\n handle.addEventListener(\"pointermove\", onMove);\n handle.addEventListener(\"pointerup\", onUp);\n handle.addEventListener(\"pointercancel\", onUp);\n };\n return (\n <div\n onPointerDown={onPointerDown}\n onDoubleClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n onAutoFit();\n }}\n title=\"Drag to resize \u00B7 double-click to fit\"\n style={{ touchAction: \"none\" }}\n className=\"absolute right-0 top-0 bottom-0 z-20 w-1.5 cursor-ew-resize hover:bg-accent/40\"\n />\n );\n};\n\nconst DataTable: React.FC<DataTableProps> = ({ cart, entity, searchFields, onSelectRow }) => {\n const stream = useBulkSubscription(cart, entity);\n const scrollerRef = React.useRef<HTMLDivElement | null>(null);\n const [scrollTop, setScrollTop] = React.useState(0);\n const [viewportH, setViewportH] = React.useState(0);\n const [query, setQuery] = React.useState(\"\");\n const [selected, setSelected] = React.useState<{ r: number; c: number } | null>(null);\n const [popover, setPopover] = React.useState<ValuePopoverData | null>(null);\n // Bump on every chunk arrival without re-rendering each FRAME_DATA.\n const [tick, setTick] = React.useState(0);\n // Per-column width overrides (px), keyed by field name. Seeded from\n // localStorage; remounts on cart/entity change (keyed at the call site).\n const [overrides, setOverrides] = React.useState<Record<string, number>>(\n () => loadColWidths(cart, entity),\n );\n const widthFor = (name: string) => overrides[name] ?? defaultColWidth(name);\n\n React.useEffect(() => {\n stream.setOnRender(() => setTick((n) => n + 1));\n return () => stream.setOnRender(null);\n }, [stream]);\n\n // Re-run once the scroller actually mounts. On first load columns are empty,\n // so the \"Waiting for schema\u2026\" placeholder renders instead of the scroller \u2014\n // scrollerRef is null and the observer never attaches, leaving viewportH at 0\n // (only OVERSCAN rows windowed). Keying on columns-present re-attaches the\n // moment the schema arrives and the scroller appears.\n const hasScroller = stream.columns.length > 0;\n React.useEffect(() => {\n const el = scrollerRef.current;\n if (!el) return;\n const ro = new ResizeObserver(() => setViewportH(el.clientHeight));\n ro.observe(el);\n setViewportH(el.clientHeight);\n return () => ro.disconnect();\n }, [hasScroller]);\n\n const rows = stream.store.rows;\n const columns = stream.columns;\n\n const totalH = rows.length * ROW_H;\n const start = Math.max(0, Math.floor(scrollTop / ROW_H) - OVERSCAN);\n const visibleCount = Math.max(0, Math.ceil(viewportH / ROW_H) + OVERSCAN * 2);\n const end = Math.min(rows.length, start + visibleCount);\n\n const onSearchKey = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (e.key === \"Enter\") {\n stream.search(query.trim());\n } else if (e.key === \"Escape\") {\n setQuery(\"\");\n stream.clearSearch();\n }\n };\n\n const handleRowClick = (rowIdx: number) => {\n const r = rows[rowIdx];\n if (!r) return;\n const out: Record<string, string> = {};\n for (let i = 0; i < columns.length; i++) out[columns[i]!.name] = r[i] ?? \"\";\n onSelectRow(out);\n };\n\n const searchDisabled = searchFields.length === 0;\n const idColIndex = columns.findIndex((c) => c.name === \"id\");\n\n // Drive every width off CSS vars on the scroll root so a drag mutates one\n // property and resizes the whole virtualized column with no React re-render.\n const widthVars = React.useMemo(() => {\n const style: React.CSSProperties & Record<string, string> = { [\"--gw\"]: `${GUTTER_W}px` };\n const parts = [\"var(--gw)\"];\n columns.forEach((c, i) => {\n style[`--cw-${i}`] = `${widthFor(c.name)}px`;\n parts.push(`var(--cw-${i})`);\n });\n style[\"--total-w\"] = `calc(${parts.join(\" + \")})`;\n return style;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [columns, overrides]);\n\n const commitWidth = (name: string, w: number) => {\n setOverrides((o) => ({ ...o, [name]: w }));\n saveColWidth(cart, entity, name, w);\n };\n\n const autoFit = (colIndex: number, name: string) => {\n const ctx = measureCtx();\n const root = scrollerRef.current;\n if (!ctx || !root) return;\n ctx.font = `12px ${getComputedStyle(root).fontFamily}`;\n let w = ctx.measureText(name).width;\n const cap = Math.min(rows.length, 1000);\n for (let r = 0; r < cap; r++) w = Math.max(w, ctx.measureText(rows[r]?.[colIndex] ?? \"\").width);\n // px-2 padding (16) + border slack (2).\n const fitted = Math.max(MIN_COL_W, Math.min(MAX_COL_W, Math.ceil(w) + 18));\n root.style.setProperty(`--cw-${colIndex}`, `${fitted}px`);\n commitWidth(name, fitted);\n };\n\n const scrollRowIntoView = (r: number) => {\n const el = scrollerRef.current;\n if (!el) return;\n const top = r * ROW_H;\n if (top < el.scrollTop) el.scrollTop = top;\n else if (top + ROW_H > el.scrollTop + el.clientHeight)\n el.scrollTop = top + ROW_H - el.clientHeight;\n };\n\n const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {\n if (rows.length === 0 || columns.length === 0) return;\n if ((e.metaKey || e.ctrlKey) && (e.key === \"c\" || e.key === \"C\")) {\n if (!selected) return;\n const v = rows[selected.r]?.[selected.c] ?? \"\";\n void navigator.clipboard?.writeText(v);\n e.preventDefault();\n return;\n }\n const cur = selected ?? { r: start, c: 0 };\n let { r, c } = cur;\n if (e.key === \"ArrowUp\") r--;\n else if (e.key === \"ArrowDown\") r++;\n else if (e.key === \"ArrowLeft\") c--;\n else if (e.key === \"ArrowRight\") c++;\n else return;\n e.preventDefault();\n r = Math.max(0, Math.min(rows.length - 1, r));\n c = Math.max(0, Math.min(columns.length - 1, c));\n setSelected({ r, c });\n setPopover(null);\n scrollRowIntoView(r);\n };\n\n return (\n <div className=\"flex h-full flex-col bg-background\">\n <div className=\"flex h-7 shrink-0 items-center gap-2 border-b border-border px-2\">\n <input\n type=\"text\"\n value={query}\n disabled={searchDisabled}\n onChange={(e) => setQuery(e.target.value)}\n onKeyDown={onSearchKey}\n placeholder={\n searchDisabled\n ? \"Search disabled (no @@search declared)\"\n : `Search ${searchFields.join(\", \")} \u23CE`\n }\n className={cn(\n \"h-5 flex-1 rounded-sm border border-border bg-background px-2 text-[12px] outline-none\",\n \"placeholder:text-muted-foreground/70 focus:border-accent-foreground/30\",\n searchDisabled && \"opacity-50 cursor-not-allowed\",\n )}\n />\n <div className=\"shrink-0 text-[11px] text-muted-foreground tabular-nums\">\n {stream.error ? (\n <span className=\"text-red-400\">{stream.error}</span>\n ) : columns.length === 0 ? (\n <span>{stream.connected ? \"loading\u2026\" : \"connecting\u2026\"}</span>\n ) : (\n <span>\n {stream.loadedRows.toLocaleString()} / {stream.totalRows.toLocaleString()} rows\n {stream.streaming ? \" \u00B7 streaming\" : \"\"}\n </span>\n )}\n </div>\n </div>\n\n {columns.length === 0 ? (\n <div className=\"flex flex-1 items-center justify-center text-[12px] text-muted-foreground\">\n {stream.error ? stream.error : \"Waiting for schema\u2026\"}\n </div>\n ) : (\n <div\n ref={scrollerRef}\n tabIndex={0}\n onKeyDown={onKeyDown}\n onScroll={(e) => {\n setScrollTop((e.target as HTMLDivElement).scrollTop);\n if (popover) setPopover(null);\n }}\n className=\"group/table relative min-h-0 flex-1 overflow-auto font-mono outline-none\"\n >\n <div style={{ ...widthVars, width: \"var(--total-w)\" }}>\n {/* Sticky header lives inside the scroller so it shares the native\n horizontal scroll with the rows \u2014 no JS transform sync, no jitter. */}\n <div\n className=\"sticky top-0 z-10 flex h-6 select-none border-b border-border bg-background text-[11px] text-muted-foreground\"\n style={{ width: \"var(--total-w)\" }}\n >\n <div\n style={{ width: \"var(--gw)\" }}\n className=\"shrink-0 border-r border-border\"\n />\n {columns.map((c, ci) => (\n <div\n key={c.name}\n style={{ width: `var(--cw-${ci})` }}\n className=\"relative flex items-center overflow-hidden border-r border-border px-2\"\n >\n <span className=\"truncate\">{c.name}</span>\n <ColResizeHandle\n index={ci}\n startWidth={widthFor(c.name)}\n scrollerRef={scrollerRef}\n onCommit={(w) => commitWidth(c.name, w)}\n onAutoFit={() => autoFit(ci, c.name)}\n />\n </div>\n ))}\n </div>\n\n <div style={{ height: totalH, width: \"var(--total-w)\", position: \"relative\" }}>\n {rows.slice(start, end).map((row, i) => {\n const rowIdx = start + i;\n return (\n <div\n key={rowIdx}\n style={{\n top: rowIdx * ROW_H,\n height: ROW_H,\n width: \"var(--total-w)\",\n }}\n className={cn(\n \"absolute left-0 flex text-[12px] leading-none\",\n rowIdx % 2 === 0 ? \"bg-background\" : \"bg-muted/20\",\n )}\n >\n <button\n type=\"button\"\n title=\"Open row detail\"\n onClick={() => handleRowClick(rowIdx)}\n style={{ width: \"var(--gw)\" }}\n className={cn(\n \"flex shrink-0 items-center justify-center border-r border-border/60\",\n \"cursor-pointer text-[10px] tabular-nums text-muted-foreground/60\",\n \"hover:bg-accent hover:text-accent-foreground\",\n )}\n >\n {rowIdx + 1}\n </button>\n {columns.map((c, ci) => {\n const isSel = selected?.r === rowIdx && selected?.c === ci;\n return (\n <div\n key={c.name}\n data-selected={isSel}\n onClick={(e) => {\n e.stopPropagation();\n setSelected({ r: rowIdx, c: ci });\n setPopover(null);\n }}\n onDoubleClick={(e) => {\n e.stopPropagation();\n setSelected({ r: rowIdx, c: ci });\n const editable =\n idColIndex >= 0 &&\n !READONLY_FIELDS.has(c.name) &&\n !READONLY_TYPES.has(c.type);\n setPopover({\n r: rowIdx,\n c: ci,\n colName: c.name,\n value: row[ci] ?? \"\",\n rect: (e.currentTarget as HTMLElement).getBoundingClientRect(),\n cart,\n entity,\n rowId: idColIndex >= 0 ? row[idColIndex] ?? \"\" : \"\",\n colType: c.type,\n editable,\n });\n }}\n style={{ width: `var(--cw-${ci})` }}\n className={cn(\n \"flex cursor-pointer items-center overflow-hidden border-r border-border/60 px-2\",\n \"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground\",\n \"data-[selected=true]:[box-shadow:inset_0_0_0_1px_#60a5fa]\",\n )}\n title={row[ci]}\n >\n <span className=\"truncate\">{row[ci]}</span>\n </div>\n );\n })}\n </div>\n );\n })}\n </div>\n </div>\n </div>\n )}\n {popover ? <ValuePopover data={popover} onClose={() => setPopover(null)} /> : null}\n {/* tick is bumped on each data chunk to force a re-render of the rows. */}\n {tick < 0 && <span />}\n </div>\n );\n};\n\nexport default DataTable;\n", "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n", "import React from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { cn } from \"../core/utils\";\nimport { useDevToolsConfig } from \"../core/config\";\nimport {\n TYPE_U8,\n TYPE_U16,\n TYPE_U32,\n TYPE_U64,\n TYPE_S8,\n TYPE_S16,\n TYPE_S32,\n TYPE_S64,\n TYPE_BOOL,\n TYPE_FLOAT32,\n TYPE_FLOAT64,\n} from \"../generated/bulk-protocol\";\n\nconst WIDTH = 420;\n\nexport type ValuePopoverData = {\n r: number;\n c: number;\n colName: string;\n value: string;\n rect: DOMRect;\n cart: string;\n entity: string;\n rowId: string;\n colType: number;\n editable: boolean;\n};\n\nconst INT_TYPES = new Set([\n TYPE_U8, TYPE_U16, TYPE_U32, TYPE_U64,\n TYPE_S8, TYPE_S16, TYPE_S32, TYPE_S64,\n]);\nconst FLOAT_TYPES = new Set([TYPE_FLOAT32, TYPE_FLOAT64]);\n\nfunction position(rect: DOMRect): React.CSSProperties {\n const margin = 12;\n const left = Math.max(\n margin,\n Math.min(rect.left, window.innerWidth - WIDTH - margin),\n );\n const roomBelow = window.innerHeight - rect.bottom;\n const top = roomBelow >= 180 ? rect.bottom + 6 : Math.max(margin, rect.top - 188);\n return { left, top, width: WIDTH };\n}\n\n// Coerce the edited string to the JSON value the update handler expects for the\n// column's wire type. Returns the value or an error string.\nfunction coerce(draft: string, colType: number): { value: unknown } | { error: string } {\n if (colType === TYPE_BOOL) return { value: draft === \"true\" };\n if (INT_TYPES.has(colType)) {\n const n = Number(draft);\n if (!Number.isInteger(n)) return { error: \"Expected an integer\" };\n return { value: n };\n }\n if (FLOAT_TYPES.has(colType)) {\n const n = Number(draft);\n if (!Number.isFinite(n)) return { error: \"Expected a number\" };\n return { value: n };\n }\n return { value: draft };\n}\n\nconst ValuePopover: React.FC<{ data: ValuePopoverData; onClose: () => void }> = ({\n data,\n onClose,\n}) => {\n const { apiBaseUrl } = useDevToolsConfig();\n const [copied, setCopied] = React.useState(false);\n const [draft, setDraft] = React.useState(data.value);\n const [saving, setSaving] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n\n React.useEffect(() => {\n setDraft(data.value);\n setError(null);\n }, [data.value, data.r, data.c]);\n\n React.useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", onKey);\n return () => document.removeEventListener(\"keydown\", onKey);\n }, [onClose]);\n\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(data.value);\n setCopied(true);\n } catch {\n /* clipboard unavailable */\n }\n };\n\n const save = async () => {\n const result = coerce(draft, data.colType);\n if (\"error\" in result) {\n setError(result.error);\n return;\n }\n setSaving(true);\n setError(null);\n try {\n const r = await fetch(\n `${apiBaseUrl}/api/${data.cart}/${data.entity.toLowerCase()}/${encodeURIComponent(data.rowId)}`,\n {\n method: \"PUT\",\n credentials: \"include\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({ [data.colName]: result.value }),\n },\n );\n if (!r.ok) {\n setError((await r.text()) || `HTTP ${r.status}`);\n setSaving(false);\n return;\n }\n // Success: the update handler broadcasts FRAME_DELTA_UPD, which refreshes\n // the row in place \u2014 just close.\n onClose();\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e));\n setSaving(false);\n }\n };\n\n const isBool = data.colType === TYPE_BOOL;\n const isNumeric = INT_TYPES.has(data.colType) || FLOAT_TYPES.has(data.colType);\n\n return createPortal(\n <>\n <div className=\"fixed inset-0 z-[60]\" onClick={onClose} />\n <div\n className={cn(\n \"nimbit-devtools dark fixed z-[61] rounded-md border border-border bg-popover p-3\",\n \"text-[12px] text-popover-foreground shadow-lg\",\n )}\n style={position(data.rect)}\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"mb-2 flex items-center justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"truncate font-medium text-foreground\">{data.colName}</div>\n <div className=\"text-[11px] text-muted-foreground\">Row {data.r + 1}</div>\n </div>\n <div className=\"flex shrink-0 items-center gap-1.5\">\n <button\n type=\"button\"\n onClick={copy}\n className={cn(\n \"rounded border border-border px-2 py-1 text-[11px] leading-none\",\n \"hover:bg-accent hover:text-accent-foreground\",\n )}\n >\n {copied ? \"Copied\" : \"Copy\"}\n </button>\n {data.editable ? (\n <button\n type=\"button\"\n onClick={save}\n disabled={saving}\n className={cn(\n \"rounded border border-border bg-primary/90 px-2 py-1 text-[11px] leading-none text-primary-foreground\",\n \"hover:bg-primary disabled:opacity-50\",\n )}\n >\n {saving ? \"Saving\u2026\" : \"Save\"}\n </button>\n ) : null}\n </div>\n </div>\n\n {data.editable ? (\n <>\n {isBool ? (\n <select\n value={draft}\n disabled={saving}\n onChange={(e) => setDraft(e.target.value)}\n className=\"w-full rounded-sm border border-border bg-background px-2 py-1 text-[12px] outline-none focus:border-accent-foreground/30\"\n >\n <option value=\"true\">true</option>\n <option value=\"false\">false</option>\n </select>\n ) : isNumeric ? (\n <input\n type=\"number\"\n value={draft}\n disabled={saving}\n autoFocus\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") void save();\n }}\n className=\"w-full rounded-sm border border-border bg-background px-2 py-1 font-mono text-[12px] outline-none focus:border-accent-foreground/30\"\n />\n ) : (\n <textarea\n value={draft}\n disabled={saving}\n autoFocus\n onChange={(e) => setDraft(e.target.value)}\n className=\"max-h-48 min-h-[3rem] w-full resize-y overflow-auto rounded-sm border border-border bg-background p-2 font-mono text-[11px] leading-5 outline-none focus:border-accent-foreground/30\"\n />\n )}\n {error ? (\n <div className=\"mt-2 break-words text-[11px] text-red-400\">{error}</div>\n ) : null}\n </>\n ) : (\n <div className=\"max-h-48 overflow-auto rounded-sm bg-muted/40 p-2 font-mono text-[11px] leading-5 whitespace-pre-wrap break-words\">\n {data.value.length > 0 ? (\n data.value\n ) : (\n <span className=\"font-sans italic text-muted-foreground\">(empty)</span>\n )}\n </div>\n )}\n </div>\n </>,\n document.body,\n );\n};\n\nexport default ValuePopover;\n", "// Per-table column-width overrides, persisted to localStorage. Keyed by\n// `<cart>:<entity>` so each table keeps its own widths; the inner map is\n// fieldName -> pixel width. A missing entry means \"use the default heuristic\".\n\nconst KEY = \"nbt_devtools_colw:v1\";\n\ntype Blob = Record<string, Record<string, number>>;\n\nfunction readBlob(): Blob {\n try {\n const raw = localStorage.getItem(KEY);\n if (!raw) return {};\n const parsed = JSON.parse(raw);\n return typeof parsed === \"object\" && parsed ? (parsed as Blob) : {};\n } catch {\n return {};\n }\n}\n\nexport function loadColWidths(cart: string, entity: string): Record<string, number> {\n const blob = readBlob();\n return blob[`${cart}:${entity}`] ?? {};\n}\n\nexport function saveColWidth(cart: string, entity: string, col: string, w: number): void {\n try {\n const blob = readBlob();\n const k = `${cart}:${entity}`;\n (blob[k] ??= {})[col] = w;\n localStorage.setItem(KEY, JSON.stringify(blob));\n } catch {\n /* storage unavailable (private mode / SSR) \u2014 widths stay in-memory only */\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["React", "jsx", "jsxs", "React"]
|
|
7
|
+
}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
authHeaders,
|
|
4
4
|
useBulkRowCounts,
|
|
5
5
|
useDevToolsConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-AI2LKTA4.js";
|
|
7
7
|
|
|
8
8
|
// src/graph/diagram.tsx
|
|
9
9
|
import React from "react";
|
|
@@ -15,7 +15,8 @@ import {
|
|
|
15
15
|
ReactFlow,
|
|
16
16
|
ReactFlowProvider,
|
|
17
17
|
useNodesInitialized,
|
|
18
|
-
useReactFlow
|
|
18
|
+
useReactFlow,
|
|
19
|
+
useStore
|
|
19
20
|
} from "@xyflow/react";
|
|
20
21
|
|
|
21
22
|
// src/graph/entity-node.tsx
|
|
@@ -404,9 +405,16 @@ var nodeTypes = { entity: EntityNode };
|
|
|
404
405
|
var FitOnReady = ({ fitKey }) => {
|
|
405
406
|
const initialized = useNodesInitialized();
|
|
406
407
|
const { fitView } = useReactFlow();
|
|
408
|
+
const width = useStore((s) => s.width);
|
|
409
|
+
const height = useStore((s) => s.height);
|
|
407
410
|
React.useEffect(() => {
|
|
408
411
|
if (initialized) fitView({ padding: 0.25, duration: 200 });
|
|
409
412
|
}, [initialized, fitKey, fitView]);
|
|
413
|
+
React.useEffect(() => {
|
|
414
|
+
if (!initialized || width === 0 || height === 0) return;
|
|
415
|
+
const t = setTimeout(() => fitView({ padding: 0.25, duration: 200 }), 120);
|
|
416
|
+
return () => clearTimeout(t);
|
|
417
|
+
}, [width, height, initialized, fitView]);
|
|
410
418
|
return null;
|
|
411
419
|
};
|
|
412
420
|
function useContracts() {
|
|
@@ -622,4 +630,4 @@ export {
|
|
|
622
630
|
filterEntityGraphModel,
|
|
623
631
|
DiagramView
|
|
624
632
|
};
|
|
625
|
-
//# sourceMappingURL=chunk-
|
|
633
|
+
//# sourceMappingURL=chunk-HTAPMIK6.js.map
|