@nbt-dev/components 0.0.8 → 0.0.9

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.
@@ -249,6 +249,7 @@ var DataTable = ({ cart, entity, searchFields, onSelectRow }) => {
249
249
  stream.setOnRender(() => setTick((n) => n + 1));
250
250
  return () => stream.setOnRender(null);
251
251
  }, [stream]);
252
+ const hasScroller = stream.columns.length > 0;
252
253
  React2.useEffect(() => {
253
254
  const el = scrollerRef.current;
254
255
  if (!el) return;
@@ -256,7 +257,7 @@ var DataTable = ({ cart, entity, searchFields, onSelectRow }) => {
256
257
  ro.observe(el);
257
258
  setViewportH(el.clientHeight);
258
259
  return () => ro.disconnect();
259
- }, []);
260
+ }, [hasScroller]);
260
261
  const rows = stream.store.rows;
261
262
  const columns = stream.columns;
262
263
  const totalH = rows.length * ROW_H;
@@ -464,4 +465,4 @@ export {
464
465
  value_popover_default,
465
466
  data_table_default
466
467
  };
467
- //# sourceMappingURL=chunk-ZOGIPYYM.js.map
468
+ //# sourceMappingURL=chunk-235TYE66.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"],
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\";\n\nconst ROW_H = 22;\nconst OVERSCAN = 6;\nconst MIN_COL_W = 80;\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 colWidth(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\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\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 totalColW = GUTTER_W + columns.reduce((s, c) => s + colWidth(c.name), 0);\n const idColIndex = columns.findIndex((c) => c.name === \"id\");\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 ) : !stream.connected ? (\n <span>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={{ width: totalColW }}>\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] uppercase tracking-wider text-muted-foreground\"\n style={{ width: totalColW }}\n >\n <div\n style={{ width: GUTTER_W }}\n className=\"shrink-0 border-r border-border\"\n />\n {columns.map((c) => (\n <div\n key={c.name}\n style={{ width: colWidth(c.name) }}\n className=\"flex items-center overflow-hidden border-r border-border px-2\"\n >\n <span className=\"truncate\">{c.name}</span>\n </div>\n ))}\n </div>\n\n <div style={{ height: totalH, width: totalColW, 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: totalColW,\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: GUTTER_W }}\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: colWidth(c.name) }}\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"],
5
+ "mappings": "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",
6
+ "names": ["React", "jsx", "jsxs", "React"]
7
+ }
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  cn,
19
19
  data_table_default,
20
20
  value_popover_default
21
- } from "./chunk-ZOGIPYYM.js";
21
+ } from "./chunk-235TYE66.js";
22
22
  import {
23
23
  BulkStreamProvider,
24
24
  DevToolsConfigProvider,
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  data_table_default,
4
4
  value_popover_default
5
- } from "../chunk-ZOGIPYYM.js";
5
+ } from "../chunk-235TYE66.js";
6
6
  import "../chunk-WBZS7I6V.js";
7
7
  export {
8
8
  data_table_default as DataTable,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbt-dev/components",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "Reusable React building blocks for NBT-console apps: the CodeMirror NBT editor (+LSP), the entity graph, and the live data table.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -43,6 +43,12 @@ const DataTable: React.FC<DataTableProps> = ({ cart, entity, searchFields, onSel
43
43
  return () => stream.setOnRender(null);
44
44
  }, [stream]);
45
45
 
46
+ // Re-run once the scroller actually mounts. On first load columns are empty,
47
+ // so the "Waiting for schema…" placeholder renders instead of the scroller —
48
+ // scrollerRef is null and the observer never attaches, leaving viewportH at 0
49
+ // (only OVERSCAN rows windowed). Keying on columns-present re-attaches the
50
+ // moment the schema arrives and the scroller appears.
51
+ const hasScroller = stream.columns.length > 0;
46
52
  React.useEffect(() => {
47
53
  const el = scrollerRef.current;
48
54
  if (!el) return;
@@ -50,7 +56,7 @@ const DataTable: React.FC<DataTableProps> = ({ cart, entity, searchFields, onSel
50
56
  ro.observe(el);
51
57
  setViewportH(el.clientHeight);
52
58
  return () => ro.disconnect();
53
- }, []);
59
+ }, [hasScroller]);
54
60
 
55
61
  const rows = stream.store.rows;
56
62
  const columns = stream.columns;
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/table/data-table.tsx", "../src/core/utils.ts", "../src/table/value-popover.tsx"],
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\";\n\nconst ROW_H = 22;\nconst OVERSCAN = 6;\nconst MIN_COL_W = 80;\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 colWidth(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\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\n React.useEffect(() => {\n stream.setOnRender(() => setTick((n) => n + 1));\n return () => stream.setOnRender(null);\n }, [stream]);\n\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 }, []);\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 totalColW = GUTTER_W + columns.reduce((s, c) => s + colWidth(c.name), 0);\n const idColIndex = columns.findIndex((c) => c.name === \"id\");\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 ) : !stream.connected ? (\n <span>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={{ width: totalColW }}>\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] uppercase tracking-wider text-muted-foreground\"\n style={{ width: totalColW }}\n >\n <div\n style={{ width: GUTTER_W }}\n className=\"shrink-0 border-r border-border\"\n />\n {columns.map((c) => (\n <div\n key={c.name}\n style={{ width: colWidth(c.name) }}\n className=\"flex items-center overflow-hidden border-r border-border px-2\"\n >\n <span className=\"truncate\">{c.name}</span>\n </div>\n ))}\n </div>\n\n <div style={{ height: totalH, width: totalColW, 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: totalColW,\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: GUTTER_W }}\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: colWidth(c.name) }}\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"],
5
- "mappings": "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",
6
- "names": ["React", "jsx", "jsxs", "React"]
7
- }