@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.
Files changed (36) hide show
  1. package/dist/{chunk-WBZS7I6V.js → chunk-AI2LKTA4.js} +30 -2
  2. package/dist/chunk-AI2LKTA4.js.map +7 -0
  3. package/dist/{chunk-ZOGIPYYM.js → chunk-BNA2MLFE.js} +153 -18
  4. package/dist/chunk-BNA2MLFE.js.map +7 -0
  5. package/dist/{chunk-VDIHQ2NS.js → chunk-HTAPMIK6.js} +11 -3
  6. package/dist/chunk-HTAPMIK6.js.map +7 -0
  7. package/dist/{chunk-S7VBQE6Y.js → chunk-TNIHD62H.js} +115 -31
  8. package/dist/chunk-TNIHD62H.js.map +7 -0
  9. package/dist/core/auth.d.ts +2 -0
  10. package/dist/core/index.d.ts +1 -1
  11. package/dist/core/use-cartridge-info.d.ts +1 -0
  12. package/dist/editor/editor-themes.d.ts +8 -0
  13. package/dist/editor/index.d.ts +2 -0
  14. package/dist/editor/index.js +7 -3
  15. package/dist/editor/nbt-editor.d.ts +4 -0
  16. package/dist/graph/index.js +2 -2
  17. package/dist/index.js +20 -8
  18. package/dist/index.js.map +2 -2
  19. package/dist/styles.css +1 -1
  20. package/dist/table/column-widths.d.ts +2 -0
  21. package/dist/table/index.js +2 -2
  22. package/package.json +3 -2
  23. package/src/core/auth.ts +40 -0
  24. package/src/core/data-store.ts +5 -1
  25. package/src/core/index.ts +2 -0
  26. package/src/core/use-cartridge-info.ts +15 -4
  27. package/src/editor/editor-themes.ts +77 -0
  28. package/src/editor/index.ts +2 -0
  29. package/src/editor/nbt-editor.tsx +60 -26
  30. package/src/graph/diagram.tsx +16 -0
  31. package/src/table/column-widths.ts +34 -0
  32. package/src/table/data-table.tsx +148 -16
  33. package/dist/chunk-S7VBQE6Y.js.map +0 -7
  34. package/dist/chunk-VDIHQ2NS.js.map +0 -7
  35. package/dist/chunk-WBZS7I6V.js.map +0 -7
  36. 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._applySearch();
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-WBZS7I6V.js.map
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": "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",
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-WBZS7I6V.js";
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 colWidth(name) {
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 }) : !stream.connected ? /* @__PURE__ */ jsx2("span", { children: "connecting\u2026" }) : /* @__PURE__ */ jsxs2("span", { children: [
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: totalColW }, children: [
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] uppercase tracking-wider text-muted-foreground",
357
- style: { width: totalColW },
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: GUTTER_W },
485
+ style: { width: "var(--gw)" },
363
486
  className: "shrink-0 border-r border-border"
364
487
  }
365
488
  ),
366
- columns.map((c) => /* @__PURE__ */ jsx2(
489
+ columns.map((c, ci) => /* @__PURE__ */ jsxs2(
367
490
  "div",
368
491
  {
369
- style: { width: colWidth(c.name) },
370
- className: "flex items-center overflow-hidden border-r border-border px-2",
371
- children: /* @__PURE__ */ jsx2("span", { className: "truncate", children: c.name })
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: totalColW, position: "relative" }, children: rows.slice(start, end).map((row, i) => {
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: totalColW
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: GUTTER_W },
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: colWidth(c.name) },
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-ZOGIPYYM.js.map
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-WBZS7I6V.js";
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-VDIHQ2NS.js.map
633
+ //# sourceMappingURL=chunk-HTAPMIK6.js.map