@xenosystem/blocks 0.13.20 → 0.13.22

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.
@@ -194,8 +194,8 @@ function moveCell(from, delta, bounds) {
194
194
  }
195
195
 
196
196
  // src/data/table/numeric.ts
197
- function parseNumeric(text5) {
198
- const trimmed = String(text5).trim();
197
+ function parseNumeric(text6) {
198
+ const trimmed = String(text6).trim();
199
199
  if (trimmed.length === 0) return { ok: false, reason: "empty" };
200
200
  const match = /^[+-]?(\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/.exec(trimmed);
201
201
  if (!match) return { ok: false, reason: "nan" };
@@ -276,6 +276,18 @@ var TableController = class {
276
276
  }
277
277
  /* ── Subscription ──────────────────────────────────────────────────────── */
278
278
  /** Subscribe to state changes. Returns an unsubscribe function. */
279
+ /** The scroller's measured viewport — the view state windows rows AND columns from it (`table/Scroller` reports it). */
280
+ viewport = { width: 0, height: 0, scrollTop: 0, scrollLeft: 0 };
281
+ /** The scroller reports its viewport; the view state windows the grid from it. */
282
+ setViewport(viewport) {
283
+ const v = this.viewport;
284
+ if (v.width === viewport.width && v.height === viewport.height && v.scrollTop === viewport.scrollTop && v.scrollLeft === viewport.scrollLeft) return;
285
+ this.viewport = { ...viewport };
286
+ this.notify();
287
+ }
288
+ getViewport() {
289
+ return this.viewport;
290
+ }
279
291
  subscribe = (listener) => {
280
292
  this.listeners.add(listener);
281
293
  return () => this.listeners.delete(listener);
@@ -549,11 +561,11 @@ var TableController = class {
549
561
  return true;
550
562
  }
551
563
  /** Update the in-flight draft. Emits `editPreview` only when `livePreview` is on. */
552
- updateDraft(text5) {
564
+ updateDraft(text6) {
553
565
  if (!this.editing) return;
554
- this.editing = { ...this.editing, draft: text5 };
566
+ this.editing = { ...this.editing, draft: text6 };
555
567
  if (this.livePreview) {
556
- const parsed = this.parseDraft(this.editing.columnId, text5);
568
+ const parsed = this.parseDraft(this.editing.columnId, text6);
557
569
  if (parsed.ok) {
558
570
  this.host.emit("editPreview", this.buildEdit(this.editing, parsed.value));
559
571
  }
@@ -605,12 +617,12 @@ var TableController = class {
605
617
  };
606
618
  }
607
619
  /** Parse a draft according to the column's declared type. */
608
- parseDraft(columnId, text5) {
620
+ parseDraft(columnId, text6) {
609
621
  const column = this.columnById(columnId);
610
- const trimmed = text5.trim();
622
+ const trimmed = text6.trim();
611
623
  if (column?.type === "number" || column?.type === "integer" || column?.type === "duration") {
612
624
  if (trimmed === "") return { ok: true, value: null };
613
- const parsed = parseNumeric(text5);
625
+ const parsed = parseNumeric(text6);
614
626
  if (!parsed.ok) return { ok: false, reason: parsed.reason };
615
627
  return { ok: true, value: normalizeNumeric(parsed.value, constraintsOfColumn(column)) };
616
628
  }
@@ -627,7 +639,7 @@ var TableController = class {
627
639
  value: trimmed === "" ? [] : trimmed.split(",").map((part) => part.trim()).filter(Boolean)
628
640
  };
629
641
  }
630
- return { ok: true, value: trimmed === "" ? null : text5 };
642
+ return { ok: true, value: trimmed === "" ? null : text6 };
631
643
  }
632
644
  rowById(rowId) {
633
645
  return this.result?.rows.find((r) => r.id === rowId);
@@ -929,19 +941,34 @@ var tableManifest = {
929
941
  };
930
942
 
931
943
  // src/data/table/react/TablePanelView.tsx
932
- import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
944
+ import { useCallback, useEffect, useRef, useSyncExternalStore } from "react";
933
945
  import {
934
946
  Badge,
947
+ Column,
935
948
  EmptyState,
936
949
  ErrorState,
950
+ Grid,
951
+ GridCell,
952
+ GridHeader,
953
+ GridHeaderCell,
954
+ GridRow,
955
+ GridSpacer,
937
956
  IconButton,
957
+ Input,
938
958
  LoadingState,
959
+ PivotCell,
960
+ PivotRow,
961
+ PivotTable,
939
962
  ScrollArea,
940
963
  SegmentedControl,
941
964
  StatusBar,
965
+ Text,
942
966
  Toolbar,
943
- ToolbarGroup
967
+ ToolbarGroup,
968
+ VirtualWindow
944
969
  } from "@xenosystem/components/chrome";
970
+
971
+ // src/data/table/declaration.ts
945
972
  import { alignmentFor, formatValue as formatValue2 } from "@xenosystem/data-core";
946
973
 
947
974
  // src/data/table/virtualize.ts
@@ -968,448 +995,445 @@ function safe(n, fallback) {
968
995
  return typeof n === "number" && Number.isFinite(n) ? n : fallback;
969
996
  }
970
997
 
971
- // src/data/table/react/TablePanelView.tsx
972
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
973
- function TablePanelView({
974
- controller,
975
- showRowNumbers = false,
976
- rowHeight = 24,
977
- showFooter = true,
978
- zebra = false
979
- }) {
980
- const state = useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);
981
- const scrollRef = useRef(null);
982
- const [viewport, setViewport] = useState({ width: 0, height: 0, scrollTop: 0, scrollLeft: 0 });
983
- useEffect(() => {
984
- const element = scrollRef.current;
985
- if (!element || typeof ResizeObserver === "undefined") return;
986
- const observer = new ResizeObserver(() => {
987
- setViewport((v) => ({ ...v, width: element.clientWidth, height: element.clientHeight }));
988
- });
989
- observer.observe(element);
990
- setViewport((v) => ({ ...v, width: element.clientWidth, height: element.clientHeight }));
991
- return () => observer.disconnect();
992
- }, []);
993
- const onScroll = useCallback((event) => {
994
- const el = event.currentTarget;
995
- setViewport((v) => ({ ...v, scrollTop: el.scrollTop, scrollLeft: el.scrollLeft }));
996
- }, []);
997
- const columns = useMemo(() => controller.resolvedColumns(), [controller, state]);
998
- const result = state.result;
999
- if (!result || result.status === "idle") {
1000
- return /* @__PURE__ */ jsx(Frame, { controller, state, children: /* @__PURE__ */ jsx(
1001
- EmptyState,
1002
- {
1003
- title: "No data",
1004
- hint: "Wire a source into this table, or refresh to run the current query."
1005
- }
1006
- ) });
1007
- }
1008
- if (result.status === "error") {
1009
- return /* @__PURE__ */ jsx(Frame, { controller, state, children: /* @__PURE__ */ jsx(
1010
- ErrorState,
1011
- {
1012
- title: result.error?.code ? `Query failed (${result.error.code})` : "Query failed",
1013
- message: result.error?.message,
1014
- onRetry: result.error?.retryable === false ? void 0 : () => controller.refresh()
1015
- }
1016
- ) });
1017
- }
1018
- if (result.status === "loading" && result.rows.length === 0) {
1019
- return /* @__PURE__ */ jsx(Frame, { controller, state, children: /* @__PURE__ */ jsx(LoadingState, { label: "Running query\u2026" }) });
1020
- }
1021
- const body = state.viewMode === "pivot" ? /* @__PURE__ */ jsx(PivotBody, { controller, columns, groups: result.groups ?? [] }) : result.rows.length === 0 ? /* @__PURE__ */ jsx(
1022
- EmptyState,
1023
- {
1024
- title: "No rows",
1025
- hint: "Every row was filtered out. Clear a filter, or widen the time window."
1026
- }
1027
- ) : /* @__PURE__ */ jsx(
1028
- GridBody,
1029
- {
1030
- controller,
1031
- columns,
1032
- rowHeight,
1033
- zebra,
1034
- showRowNumbers,
1035
- viewport,
1036
- scrollRef,
1037
- onScroll
1038
- }
1039
- );
1040
- return /* @__PURE__ */ jsx(Frame, { controller, state, showFooter, children: body });
1041
- }
1042
- function Frame({
1043
- controller,
1044
- state,
1045
- children,
1046
- showFooter = true
1047
- }) {
998
+ // src/data/table/declaration.ts
999
+ var GUTTER_WIDTH = 44;
1000
+ var MODES = [{ value: "grid", label: "Grid" }, { value: "pivot", label: "Pivot" }];
1001
+ function tableViewState(c, options = {}) {
1002
+ const { rowHeight = 24, showFooter = true, zebra = false, showRowNumbers = false } = options;
1003
+ const state = c.getState();
1048
1004
  const result = state.result;
1049
1005
  const page = result?.page;
1006
+ const viewport = c.getViewport();
1007
+ const idle = !result || result.status === "idle";
1008
+ const failed = !idle && result.status === "error";
1009
+ const loading = !idle && !failed && result.status === "loading" && result.rows.length === 0;
1010
+ const active = !idle && !failed && !loading;
1011
+ const pivot = active && state.viewMode === "pivot";
1012
+ const groups = result?.groups ?? [];
1013
+ const pivotEmpty = pivot && groups.length === 0;
1014
+ const noRows = active && !pivot && result.rows.length === 0;
1015
+ const grid = active && !pivot && result.rows.length > 0;
1016
+ const columns = c.resolvedColumns();
1017
+ const sorts = c.effectiveQuery().sorts ?? [];
1018
+ const header = (col) => {
1019
+ const sort = sorts.find((s) => s.columnId === col.column.id);
1020
+ return {
1021
+ key: col.column.id,
1022
+ columnId: col.column.id,
1023
+ name: col.column.name,
1024
+ width: col.width,
1025
+ frozen: col.frozen,
1026
+ offset: col.offset,
1027
+ sort: sort ? sort.direction : null,
1028
+ sortAsc: sort?.direction === "asc",
1029
+ sortDesc: sort?.direction === "desc",
1030
+ sortable: col.column.sortable !== false,
1031
+ title: col.column.description ?? col.column.name
1032
+ };
1033
+ };
1034
+ const rowWindow = computeWindow({ scrollTop: viewport.scrollTop, viewportHeight: viewport.height || rowHeight * 20, rowHeight, rowCount: result?.rows.length ?? 0 });
1035
+ const colWindow = computeColumnWindow(columns, viewport.scrollLeft, viewport.width || 800);
1036
+ const frozen = columns.filter((col) => col.frozen);
1037
+ const visible = columns.slice(colWindow.startIndex, colWindow.endIndex).filter((col) => !col.frozen);
1038
+ const width = totalWidth(columns);
1039
+ const editing = state.editing;
1040
+ const cell = (rowId, values, col) => {
1041
+ const isEditing = editing?.rowId === rowId && editing.columnId === col.column.id;
1042
+ return {
1043
+ key: col.column.id,
1044
+ rowId,
1045
+ columnId: col.column.id,
1046
+ width: col.width,
1047
+ align: alignmentFor(col.column),
1048
+ frozen: col.frozen,
1049
+ offset: col.offset,
1050
+ editing: isEditing,
1051
+ draft: isEditing ? editing.draft : "",
1052
+ text: isEditing ? "" : formatValue2(values[col.column.id] ?? null, col.column)
1053
+ };
1054
+ };
1055
+ const rows = grid ? result.rows.slice(rowWindow.startIndex, rowWindow.endIndex).map((row, i) => {
1056
+ const rowIndex = rowWindow.startIndex + i;
1057
+ return {
1058
+ key: row.id,
1059
+ id: row.id,
1060
+ height: rowHeight,
1061
+ width,
1062
+ selected: state.selection.includes(row.id),
1063
+ zebra: zebra && rowIndex % 2 === 1,
1064
+ hasGutter: showRowNumbers,
1065
+ ordinal: String((page?.offset ?? 0) + rowIndex + 1),
1066
+ gutterWidth: GUTTER_WIDTH,
1067
+ frozenCells: frozen.map((col) => cell(row.id, row.values, col)),
1068
+ cells: visible.map((col) => cell(row.id, row.values, col))
1069
+ };
1070
+ }) : [];
1071
+ const pivotRows = [];
1072
+ if (pivot && !pivotEmpty) {
1073
+ const walk = (group, depth) => {
1074
+ pivotRows.push({
1075
+ key: group.key.join("/"),
1076
+ label: group.label || "\u2014",
1077
+ depth,
1078
+ total: false,
1079
+ cells: columns.map((col) => {
1080
+ const v = group.aggregates?.[col.column.id];
1081
+ return { key: col.column.id, text: v === null || v === void 0 ? "" : formatValue2(v, col.column) };
1082
+ })
1083
+ });
1084
+ for (const child of group.children ?? []) walk(child, depth + 1);
1085
+ };
1086
+ for (const group of groups) walk(group, 0);
1087
+ const totals = result?.totals;
1088
+ if (totals) pivotRows.push({ key: "__total", label: "Total", depth: 0, total: true, cells: columns.map((col) => ({ key: col.column.id, text: formatValue2(totals[col.column.id] ?? null, col.column) })) });
1089
+ }
1050
1090
  const total = page?.total;
1051
1091
  const from = page ? page.offset + 1 : 0;
1052
1092
  const to = page ? page.offset + (result?.rows.length ?? 0) : 0;
1053
- return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
1054
- /* @__PURE__ */ jsx(
1055
- Toolbar,
1093
+ return {
1094
+ modes: MODES,
1095
+ mode: state.viewMode === "pivot" ? "pivot" : "grid",
1096
+ partial: result?.status === "partial",
1097
+ loadingBadge: result?.status !== "partial" && state.pending,
1098
+ prevDisabled: (page?.offset ?? 0) === 0,
1099
+ nextDisabled: !page?.hasMore,
1100
+ idle,
1101
+ failed,
1102
+ failedTitle: failed && result.error?.code ? `Query failed (${result.error.code})` : "Query failed",
1103
+ failedMessage: failed ? result.error?.message : void 0,
1104
+ retryable: failed && result.error?.retryable !== false,
1105
+ failedNoRetry: failed && result.error?.retryable === false,
1106
+ loading,
1107
+ pivot,
1108
+ pivotEmpty,
1109
+ noRows,
1110
+ grid,
1111
+ width,
1112
+ headerHeight: rowHeight,
1113
+ frozenHeaders: grid ? frozen.map(header) : [],
1114
+ headers: grid ? visible.map(header) : [],
1115
+ spacerWidth: colWindow.offsetLeft,
1116
+ totalHeight: rowWindow.totalHeight,
1117
+ offsetTop: rowWindow.offsetTop,
1118
+ rows,
1119
+ rowCount: result?.rows.length ?? 0,
1120
+ pivotHeads: pivot && !pivotEmpty ? columns.map((col) => ({ key: col.column.id, name: col.column.name })) : [],
1121
+ pivotRows,
1122
+ footerShown: showFooter,
1123
+ countLabel: total === void 0 ? `${to - from + 1} rows` : `${from}\u2013${to} of ${total}`,
1124
+ selectedLabel: state.selection.length > 0 ? `${state.selection.length} selected` : void 0
1125
+ };
1126
+ }
1127
+ function applyTableUi(c, intent) {
1128
+ switch (intent.kind) {
1129
+ case "mode":
1130
+ c.setViewMode(intent.mode);
1131
+ break;
1132
+ case "prev":
1133
+ c.stepPage(-1);
1134
+ break;
1135
+ case "next":
1136
+ c.stepPage(1);
1137
+ break;
1138
+ case "refresh":
1139
+ c.refresh();
1140
+ break;
1141
+ case "sort":
1142
+ c.toggleSort(intent.columnId, !!intent.shiftKey);
1143
+ break;
1144
+ case "rowClick":
1145
+ c.select([intent.id], !!intent.ctrlKey || !!intent.metaKey);
1146
+ break;
1147
+ case "rowDouble":
1148
+ c.activateRow(intent.id);
1149
+ break;
1150
+ case "cellDouble":
1151
+ if (!c.beginEdit(intent.rowId, intent.columnId)) c.activateRow(intent.rowId, intent.columnId);
1152
+ break;
1153
+ case "draft":
1154
+ c.updateDraft(intent.value);
1155
+ break;
1156
+ case "editKey": {
1157
+ if (intent.key === "Enter") c.commitEdit();
1158
+ else if (intent.key === "Escape") c.cancelEdit();
1159
+ break;
1160
+ }
1161
+ case "commitEdit":
1162
+ c.commitEdit();
1163
+ break;
1164
+ }
1165
+ }
1166
+ var tableViewManifest = {
1167
+ id: tableManifest.id,
1168
+ version: "1.0.0",
1169
+ title: "Table",
1170
+ category: "data",
1171
+ layout: { slot: "center" },
1172
+ inputs: [{ id: "view", type: "object", label: "View state", description: "The TableViewModel the runtime derives from the controller on every draw." }],
1173
+ outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind, mode?, columnId?, id?, rowId?, value?, key?, shiftKey?, ctrlKey?, metaKey? } \u2014 applied by the runtime to the controller." }],
1174
+ commands: []
1175
+ };
1176
+ var EMPTY = {
1177
+ modes: MODES,
1178
+ mode: "grid",
1179
+ partial: false,
1180
+ loadingBadge: false,
1181
+ prevDisabled: true,
1182
+ nextDisabled: true,
1183
+ idle: true,
1184
+ failed: false,
1185
+ failedTitle: "",
1186
+ retryable: false,
1187
+ failedNoRetry: false,
1188
+ loading: false,
1189
+ pivot: false,
1190
+ pivotEmpty: false,
1191
+ noRows: false,
1192
+ grid: false,
1193
+ width: 0,
1194
+ headerHeight: 24,
1195
+ frozenHeaders: [],
1196
+ headers: [],
1197
+ spacerWidth: 0,
1198
+ totalHeight: 0,
1199
+ offsetTop: 0,
1200
+ rows: [],
1201
+ rowCount: 0,
1202
+ pivotHeads: [],
1203
+ pivotRows: [],
1204
+ footerShown: true,
1205
+ countLabel: "0 rows"
1206
+ };
1207
+ var BEHAVIOURS = {
1208
+ "data/tablePanelView": (inputs) => inputs.view ?? EMPTY
1209
+ };
1210
+ var text = (b) => ({ component: "text", props: { text: b } });
1211
+ var ui = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
1212
+ var HEADER_CELL = (list) => ({
1213
+ component: "chrome/GridHeaderCell",
1214
+ each: { input: list, key: "key" },
1215
+ props: {
1216
+ width: { item: "width" },
1217
+ frozen: { item: "frozen" },
1218
+ offset: { item: "offset" },
1219
+ sort: { item: "sort" },
1220
+ sortable: { item: "sortable" },
1221
+ title: { item: "title" },
1222
+ sortIcon: { node: { component: "fragment", children: [{ component: "icons/SortAsc", when: { item: "sortAsc" } }, { component: "icons/SortDesc", when: { item: "sortDesc" } }] } }
1223
+ },
1224
+ children: [text({ item: "name" })],
1225
+ on: { onClick: ui("sort", { columnId: { item: "columnId" }, shiftKey: { arg: "shiftKey" } }) }
1226
+ });
1227
+ var CELL = (list) => ({
1228
+ component: "chrome/GridCell",
1229
+ each: { item: list, key: "key" },
1230
+ props: { width: { item: "width" }, align: { item: "align" }, frozen: { item: "frozen" }, offset: { item: "offset" }, editing: { item: "editing" } },
1231
+ children: [
1232
+ { component: "chrome/Input", when: { item: "editing" }, props: { autoFocus: { value: true }, value: { item: "draft" }, label: { value: "Cell value" } }, on: { onChange: ui("draft", { value: { arg: "" } }), onBlur: ui("commitEdit"), onKeyDown: ui("editKey", { key: { arg: "key" } }) } },
1233
+ { component: "text", when: { not: { item: "editing" } }, props: { text: { item: "text" } } }
1234
+ ],
1235
+ on: { onDoubleClick: ui("cellDouble", { rowId: { item: "rowId" }, columnId: { item: "columnId" } }) }
1236
+ });
1237
+ var TABLE_DECLARATION = {
1238
+ format: "xeno.block@1",
1239
+ manifest: tableViewManifest,
1240
+ behaviour: "data/tablePanelView",
1241
+ composition: {
1242
+ component: "chrome/Column",
1243
+ children: [
1056
1244
  {
1057
- left: /* @__PURE__ */ jsxs(Fragment, { children: [
1058
- /* @__PURE__ */ jsx(
1059
- SegmentedControl,
1060
- {
1061
- size: "sm",
1062
- label: "View mode",
1063
- options: [
1064
- { value: "grid", label: "Grid" },
1065
- { value: "pivot", label: "Pivot" }
1066
- ],
1067
- value: state.viewMode === "pivot" ? "pivot" : "grid",
1068
- onChange: (mode) => controller.setViewMode(mode)
1069
- }
1070
- ),
1071
- result?.status === "partial" ? /* @__PURE__ */ jsx(Badge, { tone: "warning", children: "Partial" }) : state.pending ? /* @__PURE__ */ jsx(Badge, { children: "Loading" }) : null
1072
- ] }),
1073
- right: /* @__PURE__ */ jsxs(ToolbarGroup, { end: true, children: [
1074
- /* @__PURE__ */ jsx(
1075
- IconButton,
1076
- {
1077
- icon: /* @__PURE__ */ jsx(ChevronGlyph, { direction: "left" }),
1078
- label: "Previous page",
1079
- disabled: (page?.offset ?? 0) === 0,
1080
- onClick: () => controller.stepPage(-1)
1081
- }
1082
- ),
1083
- /* @__PURE__ */ jsx(
1084
- IconButton,
1085
- {
1086
- icon: /* @__PURE__ */ jsx(ChevronGlyph, { direction: "right" }),
1087
- label: "Next page",
1088
- disabled: !page?.hasMore,
1089
- onClick: () => controller.stepPage(1)
1245
+ component: "chrome/Toolbar",
1246
+ props: {
1247
+ left: {
1248
+ node: {
1249
+ component: "fragment",
1250
+ children: [
1251
+ { component: "chrome/SegmentedControl", props: { size: { value: "sm" }, label: { value: "View mode" }, options: { input: "view.modes" }, value: { input: "view.mode" } }, on: { onChange: ui("mode", { mode: { arg: "" } }) } },
1252
+ { component: "chrome/Badge", when: { input: "view.partial" }, props: { tone: { value: "warning" } }, children: [text({ value: "Partial" })] },
1253
+ { component: "chrome/Badge", when: { input: "view.loadingBadge" }, children: [text({ value: "Loading" })] }
1254
+ ]
1090
1255
  }
1091
- ),
1092
- /* @__PURE__ */ jsx(
1093
- IconButton,
1094
- {
1095
- icon: /* @__PURE__ */ jsx(RefreshGlyph, {}),
1096
- label: "Refresh",
1097
- onClick: () => controller.refresh()
1256
+ },
1257
+ right: {
1258
+ node: {
1259
+ component: "chrome/ToolbarGroup",
1260
+ props: { end: { value: true } },
1261
+ children: [
1262
+ { component: "chrome/IconButton", props: { icon: { node: { component: "icons/Chevron", props: { direction: { value: "left" } } } }, label: { value: "Previous page" }, disabled: { input: "view.prevDisabled" } }, on: { onClick: ui("prev") } },
1263
+ { component: "chrome/IconButton", props: { icon: { node: { component: "icons/Chevron", props: { direction: { value: "right" } } } }, label: { value: "Next page" }, disabled: { input: "view.nextDisabled" } }, on: { onClick: ui("next") } },
1264
+ { component: "chrome/IconButton", props: { icon: { node: { component: "icons/Refresh" } }, label: { value: "Refresh" } }, on: { onClick: ui("refresh") } }
1265
+ ]
1098
1266
  }
1099
- )
1100
- ] })
1101
- }
1102
- ),
1103
- children,
1104
- showFooter ? /* @__PURE__ */ jsx(
1105
- StatusBar,
1267
+ }
1268
+ }
1269
+ },
1270
+ { component: "chrome/EmptyState", when: { input: "view.idle" }, props: { title: { value: "No data" }, hint: { value: "Wire a source into this table, or refresh to run the current query." } } },
1271
+ { component: "chrome/ErrorState", when: { input: "view.retryable" }, props: { title: { input: "view.failedTitle" }, message: { input: "view.failedMessage" } }, on: { onRetry: ui("refresh") } },
1272
+ // a failure the host marked non-retryable offers no retry — the handler is absent, not disabled
1273
+ { component: "chrome/ErrorState", when: { input: "view.failedNoRetry" }, props: { title: { input: "view.failedTitle" }, message: { input: "view.failedMessage" } } },
1274
+ { component: "chrome/LoadingState", when: { input: "view.loading" }, props: { label: { value: "Running query\u2026" } } },
1275
+ { component: "chrome/EmptyState", when: { input: "view.pivotEmpty" }, props: { title: { value: "No cross-tab" }, hint: { value: "Set a pivot spec \u2014 rows, columns and at least one measure \u2014 to build a cross-tab." } } },
1276
+ { component: "chrome/EmptyState", when: { input: "view.noRows" }, props: { title: { value: "No rows" }, hint: { value: "Every row was filtered out. Clear a filter, or widen the time window." } } },
1106
1277
  {
1107
- left: total === void 0 ? `${to - from + 1} rows` : `${from}\u2013${to} of ${total}`,
1108
- right: state.selection.length > 0 ? `${state.selection.length} selected` : void 0
1109
- }
1110
- ) : null
1111
- ] });
1112
- }
1113
- function GridBody({
1114
- controller,
1115
- columns,
1116
- rowHeight,
1117
- zebra,
1118
- showRowNumbers,
1119
- viewport,
1120
- scrollRef,
1121
- onScroll
1122
- }) {
1123
- const state = controller.getState();
1124
- const result = state.result;
1125
- if (!result) return null;
1126
- const rowWindow = computeWindow({
1127
- scrollTop: viewport.scrollTop,
1128
- viewportHeight: viewport.height || rowHeight * 20,
1129
- rowHeight,
1130
- rowCount: result.rows.length
1131
- });
1132
- const colWindow = computeColumnWindow(columns, viewport.scrollLeft, viewport.width || 800);
1133
- const frozen = columns.filter((c) => c.frozen);
1134
- const visible = columns.slice(colWindow.startIndex, colWindow.endIndex);
1135
- const width = totalWidth(columns);
1136
- const sorts = controller.effectiveQuery().sorts ?? [];
1137
- return /* @__PURE__ */ jsx(ScrollArea, { children: /* @__PURE__ */ jsxs(
1138
- "div",
1139
- {
1140
- ref: scrollRef,
1141
- onScroll,
1142
- style: { position: "relative", height: "100%", overflow: "auto" },
1143
- children: [
1144
- /* @__PURE__ */ jsxs(
1145
- "div",
1278
+ component: "chrome/ScrollArea",
1279
+ when: { input: "view.pivot" },
1280
+ children: [
1146
1281
  {
1147
- style: {
1148
- position: "sticky",
1149
- top: 0,
1150
- zIndex: 2,
1151
- display: "flex",
1152
- width,
1153
- height: rowHeight,
1154
- background: "var(--xeno-panel-header)"
1155
- },
1282
+ component: "chrome/PivotTable",
1283
+ when: { not: { input: "view.pivotEmpty" } },
1156
1284
  children: [
1157
- frozen.map((c) => /* @__PURE__ */ jsx(HeaderCell, { resolved: c, controller, sorts, sticky: true }, c.column.id)),
1158
- /* @__PURE__ */ jsx("div", { style: { width: colWindow.offsetLeft, flex: "0 0 auto" } }),
1159
- visible.filter((c) => !c.frozen).map((c) => /* @__PURE__ */ jsx(HeaderCell, { resolved: c, controller, sorts }, c.column.id))
1285
+ { component: "chrome/PivotRow", props: { head: { value: true } }, children: [{ component: "chrome/PivotCell", props: { head: { value: true } }, children: [text({ value: "\xA0" })] }, { component: "chrome/PivotCell", each: { input: "view.pivotHeads", key: "key" }, children: [text({ item: "name" })] }] },
1286
+ {
1287
+ component: "chrome/PivotRow",
1288
+ each: { input: "view.pivotRows", key: "key" },
1289
+ props: { total: { item: "total" } },
1290
+ children: [
1291
+ { component: "chrome/PivotCell", props: { head: { value: true }, depth: { item: "depth" } }, children: [text({ item: "label" })] },
1292
+ { component: "chrome/PivotCell", each: { item: "cells", key: "key" }, props: { numeric: { value: true } }, children: [text({ item: "text" })] }
1293
+ ]
1294
+ }
1160
1295
  ]
1161
1296
  }
1162
- ),
1163
- /* @__PURE__ */ jsx("div", { style: { height: rowWindow.totalHeight, width, position: "relative" }, children: /* @__PURE__ */ jsx("div", { style: { transform: `translateY(${rowWindow.offsetTop}px)` }, children: result.rows.slice(rowWindow.startIndex, rowWindow.endIndex).map((row, i) => {
1164
- const rowIndex = rowWindow.startIndex + i;
1165
- const selected = state.selection.includes(row.id);
1166
- return /* @__PURE__ */ jsxs(
1167
- "div",
1297
+ ]
1298
+ },
1299
+ {
1300
+ component: "table/Scroller",
1301
+ when: { input: "view.grid" },
1302
+ children: [{
1303
+ component: "chrome/Grid",
1304
+ props: { label: { value: "Rows" }, rowCount: { input: "view.rowCount" } },
1305
+ children: [
1306
+ { component: "chrome/GridHeader", props: { width: { input: "view.width" }, height: { input: "view.headerHeight" } }, children: [HEADER_CELL("view.frozenHeaders"), { component: "chrome/GridSpacer", props: { width: { input: "view.spacerWidth" } } }, HEADER_CELL("view.headers")] },
1168
1307
  {
1169
- role: "row",
1170
- "aria-selected": selected,
1171
- onClick: (e) => controller.select([row.id], e.ctrlKey || e.metaKey),
1172
- onDoubleClick: () => controller.activateRow(row.id),
1173
- style: {
1174
- display: "flex",
1175
- height: rowHeight,
1176
- width,
1177
- background: selected ? "var(--xeno-border)" : zebra && rowIndex % 2 === 1 ? "var(--xeno-border-subtle)" : "transparent"
1178
- },
1308
+ component: "chrome/VirtualWindow",
1309
+ props: { totalHeight: { input: "view.totalHeight" }, offsetTop: { input: "view.offsetTop" } },
1179
1310
  children: [
1180
- showRowNumbers ? /* @__PURE__ */ jsx(
1181
- "div",
1182
- {
1183
- role: "gridcell",
1184
- "aria-hidden": true,
1185
- style: {
1186
- width: 44,
1187
- flex: "0 0 44px",
1188
- textAlign: "right",
1189
- paddingRight: 6,
1190
- opacity: 0.35,
1191
- fontVariantNumeric: "tabular-nums",
1192
- userSelect: "none",
1193
- alignSelf: "center"
1194
- },
1195
- children: (result.page?.offset ?? 0) + rowIndex + 1
1196
- }
1197
- ) : null,
1198
- frozen.map((c) => /* @__PURE__ */ jsx(Cell, { controller, row, resolved: c, sticky: true }, c.column.id)),
1199
- /* @__PURE__ */ jsx("div", { style: { width: colWindow.offsetLeft, flex: "0 0 auto" } }),
1200
- visible.filter((c) => !c.frozen).map((c) => /* @__PURE__ */ jsx(Cell, { controller, row, resolved: c }, c.column.id))
1311
+ {
1312
+ component: "chrome/GridRow",
1313
+ each: { input: "view.rows", key: "key" },
1314
+ props: { height: { item: "height" }, width: { item: "width" }, selected: { item: "selected" }, zebra: { item: "zebra" } },
1315
+ on: { onClick: ui("rowClick", { id: { item: "id" }, ctrlKey: { arg: "ctrlKey" }, metaKey: { arg: "metaKey" } }), onDoubleClick: ui("rowDouble", { id: { item: "id" } }) },
1316
+ children: [
1317
+ { component: "chrome/GridCell", when: { item: "hasGutter" }, props: { width: { item: "gutterWidth" }, gutter: { value: true } }, children: [text({ item: "ordinal" })] },
1318
+ CELL("frozenCells"),
1319
+ { component: "chrome/GridSpacer", props: { width: { input: "view.spacerWidth" } } },
1320
+ CELL("cells")
1321
+ ]
1322
+ }
1201
1323
  ]
1202
- },
1203
- row.id
1204
- );
1205
- }) }) })
1206
- ]
1207
- }
1208
- ) });
1209
- }
1210
- function HeaderCell({
1211
- resolved,
1212
- controller,
1213
- sorts,
1214
- sticky
1215
- }) {
1216
- const sort = sorts.find((s) => s.columnId === resolved.column.id);
1217
- return /* @__PURE__ */ jsxs(
1218
- "div",
1219
- {
1220
- role: "columnheader",
1221
- "aria-sort": sort ? sort.direction === "asc" ? "ascending" : "descending" : "none",
1222
- onClick: (e) => controller.toggleSort(resolved.column.id, e.shiftKey),
1223
- title: resolved.column.description ?? resolved.column.name,
1224
- style: {
1225
- width: resolved.width,
1226
- flex: "0 0 auto",
1227
- display: "flex",
1228
- alignItems: "center",
1229
- gap: 4,
1230
- padding: "0 6px",
1231
- boxSizing: "border-box",
1232
- fontSize: 9,
1233
- fontWeight: 600,
1234
- letterSpacing: "0.06em",
1235
- textTransform: "uppercase",
1236
- color: "var(--xeno-muted)",
1237
- cursor: resolved.column.sortable === false ? "default" : "pointer",
1238
- position: sticky ? "sticky" : void 0,
1239
- left: sticky ? resolved.offset : void 0,
1240
- zIndex: sticky ? 1 : void 0,
1241
- background: sticky ? "var(--xeno-panel-header)" : void 0,
1242
- borderRight: "1px solid var(--xeno-border)",
1243
- userSelect: "none"
1324
+ }
1325
+ ]
1326
+ }]
1244
1327
  },
1245
- children: [
1246
- /* @__PURE__ */ jsx(
1247
- "span",
1248
- {
1249
- style: { flex: "1 1 auto", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
1250
- children: resolved.column.name
1251
- }
1252
- ),
1253
- sort ? sort.direction === "asc" ? /* @__PURE__ */ jsx(SortAscGlyph, {}) : /* @__PURE__ */ jsx(SortDescGlyph, {}) : null
1254
- ]
1255
- }
1256
- );
1257
- }
1258
- function Cell({
1259
- controller,
1260
- row,
1261
- resolved,
1262
- sticky
1263
- }) {
1264
- const state = controller.getState();
1265
- const column = resolved.column;
1266
- const editing = state.editing?.rowId === row.id && state.editing.columnId === column.id ? state.editing : null;
1267
- const style = {
1268
- width: resolved.width,
1269
- flex: "0 0 auto",
1270
- display: "flex",
1271
- alignItems: "center",
1272
- justifyContent: alignmentFor(column) === "right" ? "flex-end" : alignmentFor(column) === "center" ? "center" : "flex-start",
1273
- padding: "0 6px",
1274
- boxSizing: "border-box",
1275
- fontSize: 11,
1276
- color: "var(--xeno-text)",
1277
- fontVariantNumeric: alignmentFor(column) === "right" ? "tabular-nums" : void 0,
1278
- overflow: "hidden",
1279
- whiteSpace: "nowrap",
1280
- textOverflow: "ellipsis",
1281
- position: sticky ? "sticky" : void 0,
1282
- left: sticky ? resolved.offset : void 0,
1283
- zIndex: sticky ? 1 : void 0,
1284
- background: sticky ? "var(--xeno-canvas)" : void 0,
1285
- borderRight: "1px solid var(--xeno-border)"
1286
- };
1287
- if (editing) {
1288
- return /* @__PURE__ */ jsx("div", { style, children: /* @__PURE__ */ jsx(
1289
- "input",
1290
1328
  {
1291
- autoFocus: true,
1292
- value: editing.draft,
1293
- onChange: (e) => controller.updateDraft(e.target.value),
1294
- onBlur: () => controller.commitEdit(),
1295
- onFocus: (e) => e.currentTarget.select(),
1296
- onKeyDown: (e) => {
1297
- if (e.key === "Enter") {
1298
- e.preventDefault();
1299
- controller.commitEdit();
1300
- } else if (e.key === "Escape") {
1301
- e.preventDefault();
1302
- controller.cancelEdit();
1303
- }
1304
- },
1305
- style: {
1306
- width: "100%",
1307
- height: "100%",
1308
- border: "none",
1309
- outline: "none",
1310
- background: "var(--xeno-canvas)",
1311
- color: "inherit",
1312
- font: "inherit",
1313
- textAlign: alignmentFor(column) === "right" ? "right" : "left",
1314
- padding: 0
1329
+ component: "chrome/StatusBar",
1330
+ when: { input: "view.footerShown" },
1331
+ props: {
1332
+ left: { node: { component: "chrome/Text", props: { role: { value: "number" } }, children: [text({ input: "view.countLabel" })] } },
1333
+ right: { node: { component: "chrome/Text", when: { input: "view.selectedLabel" }, props: { role: { value: "meta" } }, children: [text({ input: "view.selectedLabel" })] } }
1315
1334
  }
1316
1335
  }
1317
- ) });
1336
+ ]
1318
1337
  }
1319
- return /* @__PURE__ */ jsx(
1320
- "div",
1338
+ };
1339
+
1340
+ // src/data/table/react/TablePanelView.tsx
1341
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1342
+ function TablePanelView({ controller, ...options }) {
1343
+ useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);
1344
+ const view = tableViewState(controller, options);
1345
+ const ui6 = (intent) => applyTableUi(controller, intent);
1346
+ const header = (h) => /* @__PURE__ */ jsx(
1347
+ GridHeaderCell,
1321
1348
  {
1322
- role: "gridcell",
1323
- style,
1324
- onDoubleClick: (e) => {
1325
- e.stopPropagation();
1326
- if (!controller.beginEdit(row.id, column.id)) controller.activateRow(row.id, column.id);
1327
- },
1328
- children: formatValue2(row.values[column.id] ?? null, column)
1329
- }
1349
+ width: h.width,
1350
+ frozen: h.frozen,
1351
+ offset: h.offset,
1352
+ sort: h.sort,
1353
+ sortable: h.sortable,
1354
+ title: h.title,
1355
+ sortIcon: /* @__PURE__ */ jsxs(Fragment, { children: [
1356
+ h.sortAsc ? /* @__PURE__ */ jsx(SortAscGlyph, {}) : null,
1357
+ h.sortDesc ? /* @__PURE__ */ jsx(SortDescGlyph, {}) : null
1358
+ ] }),
1359
+ onClick: (e) => ui6({ kind: "sort", columnId: h.columnId, shiftKey: e.shiftKey }),
1360
+ children: h.name
1361
+ },
1362
+ h.key
1330
1363
  );
1331
- }
1332
- function PivotBody({
1333
- controller,
1334
- columns,
1335
- groups
1336
- }) {
1337
- const state = controller.getState();
1338
- const totals = state.result?.totals;
1339
- if (groups.length === 0) {
1340
- return /* @__PURE__ */ jsx(
1341
- EmptyState,
1364
+ const cell = (c) => /* @__PURE__ */ jsxs(GridCell, { width: c.width, align: c.align, frozen: c.frozen, offset: c.offset, editing: c.editing, onDoubleClick: () => ui6({ kind: "cellDouble", rowId: c.rowId, columnId: c.columnId }), children: [
1365
+ c.editing ? /* @__PURE__ */ jsx(Input, { autoFocus: true, value: c.draft, label: "Cell value", onChange: (value) => ui6({ kind: "draft", value }), onBlur: () => ui6({ kind: "commitEdit" }), onKeyDown: (e) => ui6({ kind: "editKey", key: e.key }) }) : null,
1366
+ !c.editing ? c.text : null
1367
+ ] }, c.key);
1368
+ return /* @__PURE__ */ jsxs(Column, { children: [
1369
+ /* @__PURE__ */ jsx(
1370
+ Toolbar,
1342
1371
  {
1343
- title: "No cross-tab",
1344
- hint: "Set a pivot spec \u2014 rows, columns and at least one measure \u2014 to build a cross-tab."
1372
+ left: /* @__PURE__ */ jsxs(Fragment, { children: [
1373
+ /* @__PURE__ */ jsx(SegmentedControl, { size: "sm", label: "View mode", options: view.modes, value: view.mode, onChange: (mode) => ui6({ kind: "mode", mode }) }),
1374
+ view.partial ? /* @__PURE__ */ jsx(Badge, { tone: "warning", children: "Partial" }) : null,
1375
+ view.loadingBadge ? /* @__PURE__ */ jsx(Badge, { children: "Loading" }) : null
1376
+ ] }),
1377
+ right: /* @__PURE__ */ jsxs(ToolbarGroup, { end: true, children: [
1378
+ /* @__PURE__ */ jsx(IconButton, { icon: /* @__PURE__ */ jsx(ChevronGlyph, { direction: "left" }), label: "Previous page", disabled: view.prevDisabled, onClick: () => ui6({ kind: "prev" }) }),
1379
+ /* @__PURE__ */ jsx(IconButton, { icon: /* @__PURE__ */ jsx(ChevronGlyph, { direction: "right" }), label: "Next page", disabled: view.nextDisabled, onClick: () => ui6({ kind: "next" }) }),
1380
+ /* @__PURE__ */ jsx(IconButton, { icon: /* @__PURE__ */ jsx(RefreshGlyph, {}), label: "Refresh", onClick: () => ui6({ kind: "refresh" }) })
1381
+ ] })
1345
1382
  }
1346
- );
1347
- }
1348
- return /* @__PURE__ */ jsx(ScrollArea, { children: /* @__PURE__ */ jsxs("div", { style: { display: "table", width: "100%", fontSize: 11 }, children: [
1349
- /* @__PURE__ */ jsxs("div", { style: { display: "table-row", position: "sticky", top: 0 }, children: [
1350
- /* @__PURE__ */ jsx(PivotHeadCell, { children: "\xA0" }),
1351
- columns.map((c) => /* @__PURE__ */ jsx(PivotHeadCell, { children: c.column.name }, c.column.id))
1352
- ] }),
1353
- groups.map((group) => /* @__PURE__ */ jsx(PivotRow, { group, columns, depth: 0 }, group.key.join("/"))),
1354
- totals ? /* @__PURE__ */ jsxs("div", { style: { display: "table-row", fontWeight: 600 }, children: [
1355
- /* @__PURE__ */ jsx(PivotCell, { head: true, children: "Total" }),
1356
- columns.map((c) => /* @__PURE__ */ jsx(PivotCell, { numeric: true, children: formatValue2(totals[c.column.id] ?? null, c.column) }, c.column.id))
1357
- ] }) : null
1358
- ] }) });
1359
- }
1360
- function PivotRow({
1361
- group,
1362
- columns,
1363
- depth
1364
- }) {
1365
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1366
- /* @__PURE__ */ jsxs("div", { style: { display: "table-row" }, children: [
1367
- /* @__PURE__ */ jsx(PivotCell, { head: true, indent: depth, children: group.label || "\u2014" }),
1368
- columns.map((c) => /* @__PURE__ */ jsx(PivotCell, { numeric: true, children: group.aggregates?.[c.column.id] === null || group.aggregates?.[c.column.id] === void 0 ? "" : formatValue2(group.aggregates[c.column.id] ?? null, c.column) }, c.column.id))
1369
- ] }),
1370
- group.children?.map((child) => /* @__PURE__ */ jsx(PivotRow, { group: child, columns, depth: depth + 1 }, child.key.join("/")))
1383
+ ),
1384
+ view.idle ? /* @__PURE__ */ jsx(EmptyState, { title: "No data", hint: "Wire a source into this table, or refresh to run the current query." }) : null,
1385
+ view.retryable ? /* @__PURE__ */ jsx(ErrorState, { title: view.failedTitle, message: view.failedMessage, onRetry: () => ui6({ kind: "refresh" }) }) : null,
1386
+ view.failedNoRetry ? /* @__PURE__ */ jsx(ErrorState, { title: view.failedTitle, message: view.failedMessage }) : null,
1387
+ view.loading ? /* @__PURE__ */ jsx(LoadingState, { label: "Running query\u2026" }) : null,
1388
+ view.pivotEmpty ? /* @__PURE__ */ jsx(EmptyState, { title: "No cross-tab", hint: "Set a pivot spec \u2014 rows, columns and at least one measure \u2014 to build a cross-tab." }) : null,
1389
+ view.noRows ? /* @__PURE__ */ jsx(EmptyState, { title: "No rows", hint: "Every row was filtered out. Clear a filter, or widen the time window." }) : null,
1390
+ view.pivot ? /* @__PURE__ */ jsx(ScrollArea, { children: !view.pivotEmpty ? /* @__PURE__ */ jsxs(PivotTable, { children: [
1391
+ /* @__PURE__ */ jsxs(PivotRow, { head: true, children: [
1392
+ /* @__PURE__ */ jsx(PivotCell, { head: true, children: "\xA0" }),
1393
+ view.pivotHeads.map((h) => /* @__PURE__ */ jsx(PivotCell, { children: h.name }, h.key))
1394
+ ] }),
1395
+ view.pivotRows.map((r) => /* @__PURE__ */ jsxs(PivotRow, { total: r.total, children: [
1396
+ /* @__PURE__ */ jsx(PivotCell, { head: true, depth: r.depth, children: r.label }),
1397
+ r.cells.map((c) => /* @__PURE__ */ jsx(PivotCell, { numeric: true, children: c.text }, c.key))
1398
+ ] }, r.key))
1399
+ ] }) : null }) : null,
1400
+ view.grid ? /* @__PURE__ */ jsx(TableScroller, { controller, children: /* @__PURE__ */ jsxs(Grid, { label: "Rows", rowCount: view.rowCount, children: [
1401
+ /* @__PURE__ */ jsxs(GridHeader, { width: view.width, height: view.headerHeight, children: [
1402
+ view.frozenHeaders.map(header),
1403
+ /* @__PURE__ */ jsx(GridSpacer, { width: view.spacerWidth }),
1404
+ view.headers.map(header)
1405
+ ] }),
1406
+ /* @__PURE__ */ jsx(VirtualWindow, { totalHeight: view.totalHeight, offsetTop: view.offsetTop, children: view.rows.map((r) => /* @__PURE__ */ jsxs(GridRow, { height: r.height, width: r.width, selected: r.selected, zebra: r.zebra, onClick: (e) => ui6({ kind: "rowClick", id: r.id, ctrlKey: e.ctrlKey, metaKey: e.metaKey }), onDoubleClick: () => ui6({ kind: "rowDouble", id: r.id }), children: [
1407
+ r.hasGutter ? /* @__PURE__ */ jsx(GridCell, { width: r.gutterWidth, gutter: true, children: r.ordinal }) : null,
1408
+ r.frozenCells.map(cell),
1409
+ /* @__PURE__ */ jsx(GridSpacer, { width: view.spacerWidth }),
1410
+ r.cells.map(cell)
1411
+ ] }, r.key)) })
1412
+ ] }) }) : null,
1413
+ view.footerShown ? /* @__PURE__ */ jsx(StatusBar, { left: /* @__PURE__ */ jsx(Text, { role: "number", children: view.countLabel }), right: view.selectedLabel ? /* @__PURE__ */ jsx(Text, { role: "meta", children: view.selectedLabel }) : null }) : null
1371
1414
  ] });
1372
1415
  }
1373
- function PivotHeadCell({ children }) {
1374
- return /* @__PURE__ */ jsx(
1375
- "div",
1376
- {
1377
- style: {
1378
- display: "table-cell",
1379
- padding: "2px 6px",
1380
- fontSize: 9,
1381
- fontWeight: 600,
1382
- letterSpacing: "0.06em",
1383
- textTransform: "uppercase",
1384
- color: "var(--xeno-muted)",
1385
- background: "var(--xeno-panel-header)",
1386
- textAlign: "right"
1387
- },
1388
- children
1389
- }
1390
- );
1391
- }
1392
- function PivotCell({
1393
- children,
1394
- head,
1395
- numeric,
1396
- indent = 0
1397
- }) {
1398
- return /* @__PURE__ */ jsx(
1399
- "div",
1400
- {
1401
- style: {
1402
- display: "table-cell",
1403
- padding: "2px 6px",
1404
- paddingLeft: head ? 6 + indent * 12 : 6,
1405
- textAlign: numeric ? "right" : "left",
1406
- fontVariantNumeric: numeric ? "tabular-nums" : void 0,
1407
- color: head ? "var(--xeno-text)" : "var(--xeno-text)",
1408
- borderTop: "1px solid var(--xeno-border)"
1409
- },
1410
- children
1411
- }
1412
- );
1416
+ function TableScroller({ controller, children }) {
1417
+ const scrollRef = useRef(null);
1418
+ const report = useCallback((el) => controller.setViewport({ width: el.clientWidth, height: el.clientHeight, scrollTop: el.scrollTop, scrollLeft: el.scrollLeft }), [controller]);
1419
+ useEffect(() => {
1420
+ const el = scrollRef.current;
1421
+ if (!el) return;
1422
+ report(el);
1423
+ const RO = globalThis.ResizeObserver;
1424
+ if (!RO) return;
1425
+ const ro = new RO(() => report(el));
1426
+ ro.observe(el);
1427
+ return () => ro.disconnect();
1428
+ }, [report]);
1429
+ const onScroll = useCallback((e) => report(e.currentTarget), [report]);
1430
+ return /* @__PURE__ */ jsx(ScrollArea, { ref: scrollRef, onScroll, children });
1431
+ }
1432
+ var TABLE_RUNTIME_UNITS = ["table/Scroller"];
1433
+ function tableRuntimeComponents(controller) {
1434
+ return {
1435
+ "table/Scroller": ({ children }) => /* @__PURE__ */ jsx(TableScroller, { controller, children })
1436
+ };
1413
1437
  }
1414
1438
 
1415
1439
  // src/data/table/panel.ts
@@ -2201,7 +2225,7 @@ var chartManifest = {
2201
2225
 
2202
2226
  // src/data/chart/react/ChartPanelView.tsx
2203
2227
  import { useEffect as useEffect2, useRef as useRef2, useSyncExternalStore as useSyncExternalStore2 } from "react";
2204
- import { Badge as Badge2, Column, EmptyState as EmptyState2, ErrorState as ErrorState2, IconButton as IconButton2, PlotSurface, SegmentedControl as SegmentedControl2, StatusBar as StatusBar2, Toolbar as Toolbar2, ToolbarGroup as ToolbarGroup2 } from "@xenosystem/components/chrome";
2228
+ import { Badge as Badge2, Column as Column2, EmptyState as EmptyState2, ErrorState as ErrorState2, IconButton as IconButton2, PlotSurface, SegmentedControl as SegmentedControl2, StatusBar as StatusBar2, Toolbar as Toolbar2, ToolbarGroup as ToolbarGroup2 } from "@xenosystem/components/chrome";
2205
2229
 
2206
2230
  // src/data/chart/renderer.ts
2207
2231
  var renderer = null;
@@ -2302,13 +2326,13 @@ var chartViewManifest = {
2302
2326
  outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind: type | export, \u2026 } \u2014 applied by the runtime to the controller." }],
2303
2327
  commands: []
2304
2328
  };
2305
- var EMPTY = { types: [], type: "bar", partial: false, aggregating: false, errored: false, empty: true, emptyHint: "Wire a result into this chart to plot it.", unplottable: false, noRenderer: false, plot: false };
2306
- var BEHAVIOURS = {
2307
- "data/chartPanelView": (inputs) => inputs.view ?? EMPTY
2329
+ var EMPTY2 = { types: [], type: "bar", partial: false, aggregating: false, errored: false, empty: true, emptyHint: "Wire a result into this chart to plot it.", unplottable: false, noRenderer: false, plot: false };
2330
+ var BEHAVIOURS2 = {
2331
+ "data/chartPanelView": (inputs) => inputs.view ?? EMPTY2
2308
2332
  };
2309
2333
  var CHART_RUNTIME_UNITS = ["chart/Plot"];
2310
- var text = (b) => ({ component: "text", props: { text: b } });
2311
- var ui = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
2334
+ var text2 = (b) => ({ component: "text", props: { text: b } });
2335
+ var ui2 = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
2312
2336
  var CHART_DECLARATION = {
2313
2337
  format: "xeno.block@1",
2314
2338
  manifest: chartViewManifest,
@@ -2319,15 +2343,15 @@ var CHART_DECLARATION = {
2319
2343
  {
2320
2344
  component: "chrome/Toolbar",
2321
2345
  props: {
2322
- left: { node: { component: "chrome/SegmentedControl", props: { size: { value: "sm" }, label: { value: "Chart type" }, options: { input: "view.types" }, value: { input: "view.type" } }, on: { onChange: ui("type", { type: { arg: "" } }) } } },
2346
+ left: { node: { component: "chrome/SegmentedControl", props: { size: { value: "sm" }, label: { value: "Chart type" }, options: { input: "view.types" }, value: { input: "view.type" } }, on: { onChange: ui2("type", { type: { arg: "" } }) } } },
2323
2347
  right: {
2324
2348
  node: {
2325
2349
  component: "chrome/ToolbarGroup",
2326
2350
  props: { end: { value: true } },
2327
2351
  children: [
2328
- { component: "chrome/Badge", when: { input: "view.partial" }, props: { tone: { value: "warning" }, title: { value: "Some measures have not synced yet." } }, children: [text({ value: "partial" })] },
2329
- { component: "chrome/Badge", when: { input: "view.aggregating" }, props: { tone: { value: "warning" }, title: { input: "view.aggregatingHint" } }, children: [text({ value: "aggregating" })] },
2330
- { component: "chrome/IconButton", props: { icon: { node: { component: "icons/Download" } }, label: { value: "Export chart" } }, on: { onClick: ui("export") } }
2352
+ { component: "chrome/Badge", when: { input: "view.partial" }, props: { tone: { value: "warning" }, title: { value: "Some measures have not synced yet." } }, children: [text2({ value: "partial" })] },
2353
+ { component: "chrome/Badge", when: { input: "view.aggregating" }, props: { tone: { value: "warning" }, title: { input: "view.aggregatingHint" } }, children: [text2({ value: "aggregating" })] },
2354
+ { component: "chrome/IconButton", props: { icon: { node: { component: "icons/Download" } }, label: { value: "Export chart" } }, on: { onClick: ui2("export") } }
2331
2355
  ]
2332
2356
  }
2333
2357
  }
@@ -2374,16 +2398,16 @@ function chartRuntimeComponents(controller, options) {
2374
2398
  function ChartPanelView({ controller, options, emptyHint }) {
2375
2399
  useSyncExternalStore2(controller.subscribe, controller.getState, controller.getState);
2376
2400
  const view = chartViewState(controller, { emptyHint });
2377
- const ui5 = (intent) => applyChartUi(controller, intent);
2378
- return /* @__PURE__ */ jsxs2(Column, { children: [
2401
+ const ui6 = (intent) => applyChartUi(controller, intent);
2402
+ return /* @__PURE__ */ jsxs2(Column2, { children: [
2379
2403
  /* @__PURE__ */ jsx2(
2380
2404
  Toolbar2,
2381
2405
  {
2382
- left: /* @__PURE__ */ jsx2(SegmentedControl2, { size: "sm", label: "Chart type", options: view.types, value: view.type, onChange: (type) => ui5({ kind: "type", type }) }),
2406
+ left: /* @__PURE__ */ jsx2(SegmentedControl2, { size: "sm", label: "Chart type", options: view.types, value: view.type, onChange: (type) => ui6({ kind: "type", type }) }),
2383
2407
  right: /* @__PURE__ */ jsxs2(ToolbarGroup2, { end: true, children: [
2384
2408
  view.partial ? /* @__PURE__ */ jsx2(Badge2, { tone: "warning", title: "Some measures have not synced yet.", children: "partial" }) : null,
2385
2409
  view.aggregating ? /* @__PURE__ */ jsx2(Badge2, { tone: "warning", title: view.aggregatingHint, children: "aggregating" }) : null,
2386
- /* @__PURE__ */ jsx2(IconButton2, { icon: /* @__PURE__ */ jsx2(DownloadGlyph, {}), label: "Export chart", onClick: () => ui5({ kind: "export" }) })
2410
+ /* @__PURE__ */ jsx2(IconButton2, { icon: /* @__PURE__ */ jsx2(DownloadGlyph, {}), label: "Export chart", onClick: () => ui6({ kind: "export" }) })
2387
2411
  ] })
2388
2412
  }
2389
2413
  ),
@@ -2937,7 +2961,7 @@ var metricsManifest = {
2937
2961
  import { useSyncExternalStore as useSyncExternalStore3 } from "react";
2938
2962
  import {
2939
2963
  Badge as Badge3,
2940
- Column as Column2,
2964
+ Column as Column3,
2941
2965
  Delta,
2942
2966
  EmptyState as EmptyState3,
2943
2967
  ErrorState as ErrorState3,
@@ -2947,7 +2971,7 @@ import {
2947
2971
  Stack,
2948
2972
  StatTile,
2949
2973
  StatusBar as StatusBar3,
2950
- Text,
2974
+ Text as Text2,
2951
2975
  TileGrid,
2952
2976
  Toolbar as Toolbar3
2953
2977
  } from "@xenosystem/components/chrome";
@@ -3019,13 +3043,13 @@ var metricsViewManifest = {
3019
3043
  outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind: range | activate, \u2026 } \u2014 applied by the runtime to the controller." }],
3020
3044
  commands: []
3021
3045
  };
3022
- var EMPTY2 = { hasRanges: false, ranges: [], rangeId: "", partial: false, partialHint: "", errored: false, empty: true, emptyHint: "Configure a metric, or wire a result into this panel.", ready: false, layout: "grid", columns: 4, tiles: [], countLabel: "0 metrics" };
3023
- var BEHAVIOURS2 = {
3024
- "data/metricsPanelView": (inputs) => inputs.view ?? EMPTY2
3046
+ var EMPTY3 = { hasRanges: false, ranges: [], rangeId: "", partial: false, partialHint: "", errored: false, empty: true, emptyHint: "Configure a metric, or wire a result into this panel.", ready: false, layout: "grid", columns: 4, tiles: [], countLabel: "0 metrics" };
3047
+ var BEHAVIOURS3 = {
3048
+ "data/metricsPanelView": (inputs) => inputs.view ?? EMPTY3
3025
3049
  };
3026
3050
  var METRICS_RUNTIME_UNITS = [];
3027
- var text2 = (b) => ({ component: "text", props: { text: b } });
3028
- var ui2 = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
3051
+ var text3 = (b) => ({ component: "text", props: { text: b } });
3052
+ var ui3 = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
3029
3053
  var METRICS_DECLARATION = {
3030
3054
  format: "xeno.block@1",
3031
3055
  manifest: metricsViewManifest,
@@ -3037,8 +3061,8 @@ var METRICS_DECLARATION = {
3037
3061
  component: "chrome/Toolbar",
3038
3062
  when: { input: "view.hasRanges" },
3039
3063
  props: {
3040
- left: { node: { component: "chrome/SegmentedControl", props: { size: { value: "sm" }, label: { value: "Range" }, options: { input: "view.ranges" }, value: { input: "view.rangeId" } }, on: { onChange: ui2("range", { rangeId: { arg: "" } }) } } },
3041
- right: { node: { component: "chrome/Badge", when: { input: "view.partial" }, props: { tone: { value: "warning" }, title: { input: "view.partialHint" } }, children: [text2({ value: "partial" })] } }
3064
+ left: { node: { component: "chrome/SegmentedControl", props: { size: { value: "sm" }, label: { value: "Range" }, options: { input: "view.ranges" }, value: { input: "view.rangeId" } }, on: { onChange: ui3("range", { rangeId: { arg: "" } }) } } },
3065
+ right: { node: { component: "chrome/Badge", when: { input: "view.partial" }, props: { tone: { value: "warning" }, title: { input: "view.partialHint" } }, children: [text3({ value: "partial" })] } }
3042
3066
  }
3043
3067
  },
3044
3068
  { component: "chrome/ErrorState", when: { input: "view.errored" }, props: { title: { value: "Could not load metrics" }, message: { input: "view.errorMessage" } } },
@@ -3058,20 +3082,20 @@ var METRICS_DECLARATION = {
3058
3082
  title: { item: "title" },
3059
3083
  label: { item: "label" },
3060
3084
  value: { item: "value" },
3061
- icon: { node: { component: "chrome/Badge", when: { item: "hasLevel" }, props: { tone: { item: "tone" } }, children: [text2({ item: "level" })] } },
3085
+ icon: { node: { component: "chrome/Badge", when: { item: "hasLevel" }, props: { tone: { item: "tone" } }, children: [text3({ item: "level" })] } },
3062
3086
  sub: {
3063
3087
  node: {
3064
3088
  component: "chrome/Stack",
3065
3089
  props: { direction: { value: "row" }, pad: { value: "none" } },
3066
3090
  children: [
3067
3091
  { component: "chrome/Delta", when: { item: "hasDelta" }, props: { value: { item: "delta" }, tone: { item: "deltaTone" } } },
3068
- { component: "chrome/Text", when: { item: "partial" }, props: { role: { value: "meta" } }, children: [text2({ input: "view.partialHint" })] },
3092
+ { component: "chrome/Text", when: { item: "partial" }, props: { role: { value: "meta" } }, children: [text3({ input: "view.partialHint" })] },
3069
3093
  { component: "chrome/Sparkline", when: { item: "hasSpark" }, props: { data: { item: "spark" }, label: { item: "sparkLabel" } } }
3070
3094
  ]
3071
3095
  }
3072
3096
  }
3073
3097
  },
3074
- on: { onActivate: ui2("activate", { metricId: { item: "id" } }) }
3098
+ on: { onActivate: ui3("activate", { metricId: { item: "id" } }) }
3075
3099
  }
3076
3100
  ]
3077
3101
  }
@@ -3087,12 +3111,12 @@ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
3087
3111
  function MetricsPanelView({ controller, ...options }) {
3088
3112
  useSyncExternalStore3(controller.subscribe, controller.getState, controller.getState);
3089
3113
  const view = metricsViewState(controller, options);
3090
- const ui5 = (intent) => applyMetricsUi(controller, intent);
3091
- return /* @__PURE__ */ jsxs3(Column2, { children: [
3114
+ const ui6 = (intent) => applyMetricsUi(controller, intent);
3115
+ return /* @__PURE__ */ jsxs3(Column3, { children: [
3092
3116
  view.hasRanges ? /* @__PURE__ */ jsx3(
3093
3117
  Toolbar3,
3094
3118
  {
3095
- left: /* @__PURE__ */ jsx3(SegmentedControl3, { size: "sm", label: "Range", options: view.ranges, value: view.rangeId, onChange: (rangeId) => ui5({ kind: "range", rangeId }) }),
3119
+ left: /* @__PURE__ */ jsx3(SegmentedControl3, { size: "sm", label: "Range", options: view.ranges, value: view.rangeId, onChange: (rangeId) => ui6({ kind: "range", rangeId }) }),
3096
3120
  right: view.partial ? /* @__PURE__ */ jsx3(Badge3, { tone: "warning", title: view.partialHint, children: "partial" }) : null
3097
3121
  }
3098
3122
  ) : null,
@@ -3107,10 +3131,10 @@ function MetricsPanelView({ controller, ...options }) {
3107
3131
  icon: tile.hasLevel ? /* @__PURE__ */ jsx3(Badge3, { tone: tile.tone, children: tile.level }) : null,
3108
3132
  sub: /* @__PURE__ */ jsxs3(Stack, { direction: "row", pad: "none", children: [
3109
3133
  tile.hasDelta ? /* @__PURE__ */ jsx3(Delta, { value: tile.delta, tone: tile.deltaTone }) : null,
3110
- tile.partial ? /* @__PURE__ */ jsx3(Text, { role: "meta", children: view.partialHint }) : null,
3134
+ tile.partial ? /* @__PURE__ */ jsx3(Text2, { role: "meta", children: view.partialHint }) : null,
3111
3135
  tile.hasSpark ? /* @__PURE__ */ jsx3(Sparkline, { data: tile.spark, label: tile.sparkLabel }) : null
3112
3136
  ] }),
3113
- onActivate: () => ui5({ kind: "activate", metricId: tile.id })
3137
+ onActivate: () => ui6({ kind: "activate", metricId: tile.id })
3114
3138
  },
3115
3139
  tile.id
3116
3140
  )) }) }) : null,
@@ -3312,7 +3336,7 @@ function emptyFilterGroup() {
3312
3336
  function offerableFilterOps(column, sourceOps = []) {
3313
3337
  const common = ["eq", "ne", "isEmpty", "isNotEmpty", "in", "notIn"];
3314
3338
  const ordered = ["gt", "gte", "lt", "lte", "between"];
3315
- const text5 = ["contains", "notContains", "startsWith", "endsWith"];
3339
+ const text6 = ["contains", "notContains", "startsWith", "endsWith"];
3316
3340
  const temporal = ["relative"];
3317
3341
  let ops = [...common];
3318
3342
  switch (column?.type) {
@@ -3330,10 +3354,10 @@ function offerableFilterOps(column, sourceOps = []) {
3330
3354
  case "url":
3331
3355
  case "select":
3332
3356
  case "multiselect":
3333
- ops = [...ops, ...text5];
3357
+ ops = [...ops, ...text6];
3334
3358
  break;
3335
3359
  default:
3336
- ops = [...ops, ...text5, ...ordered];
3360
+ ops = [...ops, ...text6, ...ordered];
3337
3361
  }
3338
3362
  if (sourceOps.length === 0) return ops;
3339
3363
  return ops.filter((op) => sourceOps.includes(op));
@@ -3964,7 +3988,7 @@ var fieldsManifest = {
3964
3988
  import { useSyncExternalStore as useSyncExternalStore4 } from "react";
3965
3989
  import {
3966
3990
  Badge as Badge4,
3967
- Column as Column3,
3991
+ Column as Column4,
3968
3992
  EmptyState as EmptyState4,
3969
3993
  IconButton as IconButton3,
3970
3994
  Note,
@@ -4099,13 +4123,13 @@ var fieldsViewManifest = {
4099
4123
  outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind, \u2026 } \u2014 applied by the runtime to the controller." }],
4100
4124
  commands: []
4101
4125
  };
4102
- var EMPTY3 = { hasColumns: false, noColumns: true, sampled: false, manualApply: false, applyDisabled: true, columns: [], shelves: [], filters: [], filterCount: 0, hasFilters: false, noFilters: true, combinator: "and", countLabel: "0 fields" };
4103
- var BEHAVIOURS3 = {
4104
- "data/fieldsPanelView": (inputs) => inputs.view ?? EMPTY3
4126
+ var EMPTY4 = { hasColumns: false, noColumns: true, sampled: false, manualApply: false, applyDisabled: true, columns: [], shelves: [], filters: [], filterCount: 0, hasFilters: false, noFilters: true, combinator: "and", countLabel: "0 fields" };
4127
+ var BEHAVIOURS4 = {
4128
+ "data/fieldsPanelView": (inputs) => inputs.view ?? EMPTY4
4105
4129
  };
4106
4130
  var FIELDS_RUNTIME_UNITS = [];
4107
- var text3 = (b) => ({ component: "text", props: { text: b } });
4108
- var ui3 = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
4131
+ var text4 = (b) => ({ component: "text", props: { text: b } });
4132
+ var ui4 = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
4109
4133
  var removeButton = (label, on) => ({
4110
4134
  component: "chrome/IconButton",
4111
4135
  props: { size: { value: "sm" }, icon: { node: { component: "icons/Close", props: { size: { value: 11 } } } }, label },
@@ -4126,14 +4150,14 @@ var FIELDS_DECLARATION = {
4126
4150
  {
4127
4151
  component: "chrome/Toolbar",
4128
4152
  props: {
4129
- left: { node: { component: "chrome/Badge", when: { input: "view.sampled" }, props: { tone: { value: "warning" }, title: { input: "view.sampleWarning" } }, children: [text3({ value: "sampled schema" })] } },
4153
+ left: { node: { component: "chrome/Badge", when: { input: "view.sampled" }, props: { tone: { value: "warning" }, title: { input: "view.sampleWarning" } }, children: [text4({ value: "sampled schema" })] } },
4130
4154
  right: {
4131
4155
  node: {
4132
4156
  component: "chrome/ToolbarGroup",
4133
4157
  props: { end: { value: true } },
4134
4158
  children: [
4135
- { component: "chrome/TextButton", on: { onClick: ui3("autoAssign") }, children: [text3({ value: "Auto-assign" })] },
4136
- { component: "chrome/TextButton", when: { input: "view.manualApply" }, props: { strong: { value: true }, disabled: { input: "view.applyDisabled" } }, on: { onClick: ui3("apply") }, children: [text3({ value: "Apply" })] }
4159
+ { component: "chrome/TextButton", on: { onClick: ui4("autoAssign") }, children: [text4({ value: "Auto-assign" })] },
4160
+ { component: "chrome/TextButton", when: { input: "view.manualApply" }, props: { strong: { value: true }, disabled: { input: "view.applyDisabled" } }, on: { onClick: ui4("apply") }, children: [text4({ value: "Apply" })] }
4137
4161
  ]
4138
4162
  }
4139
4163
  }
@@ -4153,8 +4177,8 @@ var FIELDS_DECLARATION = {
4153
4177
  {
4154
4178
  component: "chrome/Row",
4155
4179
  each: { input: "view.columns", key: "id" },
4156
- props: { label: { item: "name" }, meta: { item: "role" }, selected: { item: "focused" }, trailing: { node: { component: "chrome/Badge", when: { item: "stat" }, props: { title: { item: "statTitle" } }, children: [text3({ item: "stat" })] } } },
4157
- on: { onClick: ui3("focus", { columnId: { item: "id" } }) }
4180
+ props: { label: { item: "name" }, meta: { item: "role" }, selected: { item: "focused" }, trailing: { node: { component: "chrome/Badge", when: { item: "stat" }, props: { title: { item: "statTitle" } }, children: [text4({ item: "stat" })] } } },
4181
+ on: { onClick: ui4("focus", { columnId: { item: "id" } }) }
4158
4182
  }
4159
4183
  ]
4160
4184
  }
@@ -4163,9 +4187,9 @@ var FIELDS_DECLARATION = {
4163
4187
  {
4164
4188
  component: "chrome/Section",
4165
4189
  each: { input: "view.shelves", key: "id" },
4166
- props: { title: { item: "label" }, flush: { value: true }, badge: { node: { component: "chrome/Badge", when: { item: "hasEntries" }, children: [text3({ item: "count" })] } } },
4190
+ props: { title: { item: "label" }, flush: { value: true }, badge: { node: { component: "chrome/Badge", when: { item: "hasEntries" }, children: [text4({ item: "count" })] } } },
4167
4191
  children: [
4168
- { component: "chrome/Note", when: { item: "empty" }, children: [text3({ item: "hint" })] },
4192
+ { component: "chrome/Note", when: { item: "empty" }, children: [text4({ item: "hint" })] },
4169
4193
  {
4170
4194
  component: "chrome/RowList",
4171
4195
  when: { item: "hasEntries" },
@@ -4180,13 +4204,13 @@ var FIELDS_DECLARATION = {
4180
4204
  node: {
4181
4205
  component: "fragment",
4182
4206
  children: [
4183
- { component: "chrome/Select", when: { item: "aggregated" }, props: { size: { value: "sm" }, label: { value: "Aggregate" }, options: { item: "aggregates" }, value: { item: "fn" } }, on: { onChange: ui3("aggregate", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" }, fn: { arg: "" } }) } },
4184
- { component: "chrome/TextButton", when: { item: "sortable" }, on: { onClick: ui3("direction", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" } }) }, children: [text3({ item: "direction" })] },
4185
- { component: "chrome/Badge", when: { item: "granularity" }, children: [text3({ item: "granularity" })] }
4207
+ { component: "chrome/Select", when: { item: "aggregated" }, props: { size: { value: "sm" }, label: { value: "Aggregate" }, options: { item: "aggregates" }, value: { item: "fn" } }, on: { onChange: ui4("aggregate", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" }, fn: { arg: "" } }) } },
4208
+ { component: "chrome/TextButton", when: { item: "sortable" }, on: { onClick: ui4("direction", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" } }) }, children: [text4({ item: "direction" })] },
4209
+ { component: "chrome/Badge", when: { item: "granularity" }, children: [text4({ item: "granularity" })] }
4186
4210
  ]
4187
4211
  }
4188
4212
  },
4189
- trailing: { node: removeButton({ item: "removeLabel" }, ui3("remove", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" } })) }
4213
+ trailing: { node: removeButton({ item: "removeLabel" }, ui4("remove", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" } })) }
4190
4214
  }
4191
4215
  }
4192
4216
  ]
@@ -4198,11 +4222,11 @@ var FIELDS_DECLARATION = {
4198
4222
  props: {
4199
4223
  title: { value: "Filters" },
4200
4224
  flush: { value: true },
4201
- badge: { node: { component: "chrome/Badge", when: { input: "view.hasFilters" }, children: [text3({ input: "view.filterCount" })] } },
4202
- actions: { node: { component: "chrome/IconButton", props: { size: { value: "sm" }, icon: { node: { component: "icons/Plus", props: { size: { value: 11 } } } }, label: { value: "Add filter" } }, on: { onClick: ui3("addFilter") } } }
4225
+ badge: { node: { component: "chrome/Badge", when: { input: "view.hasFilters" }, children: [text4({ input: "view.filterCount" })] } },
4226
+ actions: { node: { component: "chrome/IconButton", props: { size: { value: "sm" }, icon: { node: { component: "icons/Plus", props: { size: { value: 11 } } } }, label: { value: "Add filter" } }, on: { onClick: ui4("addFilter") } } }
4203
4227
  },
4204
4228
  children: [
4205
- { component: "chrome/Note", when: { input: "view.noFilters" }, children: [text3({ value: "No filters. Everything passes." })] },
4229
+ { component: "chrome/Note", when: { input: "view.noFilters" }, children: [text4({ value: "No filters. Everything passes." })] },
4206
4230
  {
4207
4231
  component: "chrome/RowList",
4208
4232
  when: { input: "view.hasFilters" },
@@ -4212,8 +4236,8 @@ var FIELDS_DECLARATION = {
4212
4236
  each: { input: "view.filters", key: "key" },
4213
4237
  props: {
4214
4238
  label: { item: "label" },
4215
- meta: { node: { component: "chrome/Select", when: { item: "leaf" }, props: { size: { value: "sm" }, label: { value: "Operator" }, options: { item: "ops" }, value: { item: "op" } }, on: { onChange: ui3("filterOp", { index: { item: "index" }, op: { arg: "" } }) } } },
4216
- trailing: { node: removeButton({ item: "removeLabel" }, ui3("removeFilter", { index: { item: "index" } })) }
4239
+ meta: { node: { component: "chrome/Select", when: { item: "leaf" }, props: { size: { value: "sm" }, label: { value: "Operator" }, options: { item: "ops" }, value: { item: "op" } }, on: { onChange: ui4("filterOp", { index: { item: "index" }, op: { arg: "" } }) } } },
4240
+ trailing: { node: removeButton({ item: "removeLabel" }, ui4("removeFilter", { index: { item: "index" } })) }
4217
4241
  }
4218
4242
  }
4219
4243
  ]
@@ -4222,8 +4246,8 @@ var FIELDS_DECLARATION = {
4222
4246
  component: "chrome/Toolbar",
4223
4247
  props: {
4224
4248
  divided: { value: false },
4225
- left: { node: { component: "chrome/SegmentedControl", props: { size: { value: "sm" }, label: { value: "Combinator" }, options: { value: COMBINATORS }, value: { input: "view.combinator" } }, on: { onChange: ui3("combinator", { combinator: { arg: "" } }) } } },
4226
- right: { node: { component: "chrome/TextButton", on: { onClick: ui3("addGroup") }, children: [text3({ value: "+ group" })] } }
4249
+ left: { node: { component: "chrome/SegmentedControl", props: { size: { value: "sm" }, label: { value: "Combinator" }, options: { value: COMBINATORS }, value: { input: "view.combinator" } }, on: { onChange: ui4("combinator", { combinator: { arg: "" } }) } } },
4250
+ right: { node: { component: "chrome/TextButton", on: { onClick: ui4("addGroup") }, children: [text4({ value: "+ group" })] } }
4227
4251
  }
4228
4252
  }
4229
4253
  ]
@@ -4245,18 +4269,18 @@ var COMBINATORS2 = [{ value: "and", label: "All" }, { value: "or", label: "Any"
4245
4269
  function FieldsPanelView({ controller, showStats, manualApply }) {
4246
4270
  useSyncExternalStore4(controller.subscribe, controller.getState, controller.getState);
4247
4271
  const view = fieldsViewState(controller, { showStats, manualApply });
4248
- const ui5 = (intent) => applyFieldsUi(controller, intent);
4272
+ const ui6 = (intent) => applyFieldsUi(controller, intent);
4249
4273
  if (view.noColumns) {
4250
4274
  return /* @__PURE__ */ jsx4(EmptyState4, { title: "No fields", hint: "Connect a source \u2014 its columns appear here, ready to drop onto a shelf." });
4251
4275
  }
4252
- return /* @__PURE__ */ jsxs4(Column3, { children: [
4276
+ return /* @__PURE__ */ jsxs4(Column4, { children: [
4253
4277
  /* @__PURE__ */ jsx4(
4254
4278
  Toolbar4,
4255
4279
  {
4256
4280
  left: view.sampled ? /* @__PURE__ */ jsx4(Badge4, { tone: "warning", title: view.sampleWarning, children: "sampled schema" }) : null,
4257
4281
  right: /* @__PURE__ */ jsxs4(ToolbarGroup3, { end: true, children: [
4258
- /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui5({ kind: "autoAssign" }), children: "Auto-assign" }),
4259
- view.manualApply ? /* @__PURE__ */ jsx4(TextButton, { strong: true, disabled: view.applyDisabled, onClick: () => ui5({ kind: "apply" }), children: "Apply" }) : null
4282
+ /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui6({ kind: "autoAssign" }), children: "Auto-assign" }),
4283
+ view.manualApply ? /* @__PURE__ */ jsx4(TextButton, { strong: true, disabled: view.applyDisabled, onClick: () => ui6({ kind: "apply" }), children: "Apply" }) : null
4260
4284
  ] })
4261
4285
  }
4262
4286
  ),
@@ -4268,7 +4292,7 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4268
4292
  meta: column.role,
4269
4293
  selected: column.focused,
4270
4294
  trailing: column.stat ? /* @__PURE__ */ jsx4(Badge4, { title: column.statTitle, children: column.stat }) : null,
4271
- onClick: () => ui5({ kind: "focus", columnId: column.id })
4295
+ onClick: () => ui6({ kind: "focus", columnId: column.id })
4272
4296
  },
4273
4297
  column.id
4274
4298
  )) }) }),
@@ -4280,11 +4304,11 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4280
4304
  label: entry.label,
4281
4305
  dimmed: entry.suggested,
4282
4306
  meta: /* @__PURE__ */ jsxs4(Fragment2, { children: [
4283
- entry.aggregated ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Aggregate", options: entry.aggregates, value: entry.fn, onChange: (fn) => ui5({ kind: "aggregate", shelfId: entry.shelfId, columnId: entry.columnId, fn }) }) : null,
4284
- entry.sortable ? /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui5({ kind: "direction", shelfId: entry.shelfId, columnId: entry.columnId }), children: entry.direction }) : null,
4307
+ entry.aggregated ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Aggregate", options: entry.aggregates, value: entry.fn, onChange: (fn) => ui6({ kind: "aggregate", shelfId: entry.shelfId, columnId: entry.columnId, fn }) }) : null,
4308
+ entry.sortable ? /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui6({ kind: "direction", shelfId: entry.shelfId, columnId: entry.columnId }), children: entry.direction }) : null,
4285
4309
  entry.granularity ? /* @__PURE__ */ jsx4(Badge4, { children: entry.granularity }) : null
4286
4310
  ] }),
4287
- trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: entry.removeLabel, onClick: () => ui5({ kind: "remove", shelfId: entry.shelfId, columnId: entry.columnId }) })
4311
+ trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: entry.removeLabel, onClick: () => ui6({ kind: "remove", shelfId: entry.shelfId, columnId: entry.columnId }) })
4288
4312
  },
4289
4313
  entry.columnId
4290
4314
  )) }) : null
@@ -4295,15 +4319,15 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4295
4319
  title: "Filters",
4296
4320
  flush: true,
4297
4321
  badge: view.hasFilters ? /* @__PURE__ */ jsx4(Badge4, { children: view.filterCount }) : null,
4298
- actions: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(PlusGlyph, { size: 11 }), label: "Add filter", onClick: () => ui5({ kind: "addFilter" }) }),
4322
+ actions: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(PlusGlyph, { size: 11 }), label: "Add filter", onClick: () => ui6({ kind: "addFilter" }) }),
4299
4323
  children: [
4300
4324
  view.noFilters ? /* @__PURE__ */ jsx4(Note, { children: "No filters. Everything passes." }) : null,
4301
4325
  view.hasFilters ? /* @__PURE__ */ jsx4(RowList, { children: view.filters.map((filter) => /* @__PURE__ */ jsx4(
4302
4326
  Row,
4303
4327
  {
4304
4328
  label: filter.label,
4305
- meta: filter.leaf ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Operator", options: filter.ops, value: filter.op, onChange: (op) => ui5({ kind: "filterOp", index: filter.index, op }) }) : null,
4306
- trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: filter.removeLabel, onClick: () => ui5({ kind: "removeFilter", index: filter.index }) })
4329
+ meta: filter.leaf ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Operator", options: filter.ops, value: filter.op, onChange: (op) => ui6({ kind: "filterOp", index: filter.index, op }) }) : null,
4330
+ trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: filter.removeLabel, onClick: () => ui6({ kind: "removeFilter", index: filter.index }) })
4307
4331
  },
4308
4332
  filter.key
4309
4333
  )) }) : null,
@@ -4311,8 +4335,8 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4311
4335
  Toolbar4,
4312
4336
  {
4313
4337
  divided: false,
4314
- left: /* @__PURE__ */ jsx4(SegmentedControl4, { size: "sm", label: "Combinator", options: COMBINATORS2, value: view.combinator, onChange: (combinator) => ui5({ kind: "combinator", combinator }) }),
4315
- right: /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui5({ kind: "addGroup" }), children: "+ group" })
4338
+ left: /* @__PURE__ */ jsx4(SegmentedControl4, { size: "sm", label: "Combinator", options: COMBINATORS2, value: view.combinator, onChange: (combinator) => ui6({ kind: "combinator", combinator }) }),
4339
+ right: /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui6({ kind: "addGroup" }), children: "+ group" })
4316
4340
  }
4317
4341
  )
4318
4342
  ]
@@ -5463,10 +5487,10 @@ function parseJsonPath(path) {
5463
5487
  }
5464
5488
  return steps;
5465
5489
  }
5466
- function findBracketEnd(text5, start) {
5490
+ function findBracketEnd(text6, start) {
5467
5491
  let quote = null;
5468
- for (let i = start + 1; i < text5.length; i += 1) {
5469
- const char = text5[i];
5492
+ for (let i = start + 1; i < text6.length; i += 1) {
5493
+ const char = text6[i];
5470
5494
  if (quote) {
5471
5495
  if (char === "\\") i += 1;
5472
5496
  else if (char === quote) quote = null;
@@ -5890,9 +5914,9 @@ function firstGraphqlError(errors) {
5890
5914
  }
5891
5915
 
5892
5916
  // src/data/connectors/executors/csv.ts
5893
- function sniffDelimiter(text5) {
5917
+ function sniffDelimiter(text6) {
5894
5918
  const candidates = [",", ";", " ", "|"];
5895
- const line = firstLogicalLine(text5);
5919
+ const line = firstLogicalLine(text6);
5896
5920
  let best = ",";
5897
5921
  let bestCount = 0;
5898
5922
  for (const candidate of candidates) {
@@ -5914,21 +5938,21 @@ function sniffDelimiter(text5) {
5914
5938
  }
5915
5939
  return best;
5916
5940
  }
5917
- function firstLogicalLine(text5) {
5941
+ function firstLogicalLine(text6) {
5918
5942
  let inQuotes = false;
5919
- for (let i = 0; i < text5.length; i += 1) {
5920
- const char = text5[i];
5943
+ for (let i = 0; i < text6.length; i += 1) {
5944
+ const char = text6[i];
5921
5945
  if (char === '"') {
5922
- if (inQuotes && text5[i + 1] === '"') i += 1;
5946
+ if (inQuotes && text6[i + 1] === '"') i += 1;
5923
5947
  else inQuotes = !inQuotes;
5924
5948
  } else if ((char === "\n" || char === "\r") && !inQuotes) {
5925
- return text5.slice(0, i);
5949
+ return text6.slice(0, i);
5926
5950
  }
5927
5951
  }
5928
- return text5;
5952
+ return text6;
5929
5953
  }
5930
- function parseCsv(text5, options = {}) {
5931
- const source = text5.charCodeAt(0) === 65279 ? text5.slice(1) : text5;
5954
+ function parseCsv(text6, options = {}) {
5955
+ const source = text6.charCodeAt(0) === 65279 ? text6.slice(1) : text6;
5932
5956
  const delimiter = options.delimiter && options.delimiter.length > 0 ? options.delimiter : sniffDelimiter(source);
5933
5957
  const quote = options.quote && options.quote.length > 0 ? options.quote : '"';
5934
5958
  const hasHeader = options.hasHeader ?? true;
@@ -5963,17 +5987,17 @@ function dedupe(names) {
5963
5987
  return count === 0 ? name : `${name}_${count + 1}`;
5964
5988
  });
5965
5989
  }
5966
- function scanRecords(text5, delimiter, quote) {
5990
+ function scanRecords(text6, delimiter, quote) {
5967
5991
  const records = [];
5968
5992
  let fields = [];
5969
5993
  let field = "";
5970
5994
  let inQuotes = false;
5971
5995
  let i = 0;
5972
- while (i < text5.length) {
5973
- const char = text5[i];
5996
+ while (i < text6.length) {
5997
+ const char = text6[i];
5974
5998
  if (inQuotes) {
5975
5999
  if (char === quote) {
5976
- if (text5[i + 1] === quote) {
6000
+ if (text6[i + 1] === quote) {
5977
6001
  field += quote;
5978
6002
  i += 2;
5979
6003
  continue;
@@ -5991,7 +6015,7 @@ function scanRecords(text5, delimiter, quote) {
5991
6015
  i += 1;
5992
6016
  continue;
5993
6017
  }
5994
- if (text5.startsWith(delimiter, i)) {
6018
+ if (text6.startsWith(delimiter, i)) {
5995
6019
  fields.push(field);
5996
6020
  field = "";
5997
6021
  i += delimiter.length;
@@ -6002,7 +6026,7 @@ function scanRecords(text5, delimiter, quote) {
6002
6026
  records.push(fields);
6003
6027
  fields = [];
6004
6028
  field = "";
6005
- i += char === "\r" && text5[i + 1] === "\n" ? 2 : 1;
6029
+ i += char === "\r" && text6[i + 1] === "\n" ? 2 : 1;
6006
6030
  continue;
6007
6031
  }
6008
6032
  field += char;
@@ -6572,12 +6596,12 @@ import { useSyncExternalStore as useSyncExternalStore5 } from "react";
6572
6596
  import {
6573
6597
  Badge as Badge5,
6574
6598
  Code,
6575
- Column as Column4,
6599
+ Column as Column5,
6576
6600
  EmptyState as EmptyState5,
6577
6601
  Field,
6578
6602
  FieldGroup,
6579
6603
  IconButton as IconButton4,
6580
- Input,
6604
+ Input as Input2,
6581
6605
  Link,
6582
6606
  Note as Note2,
6583
6607
  Row as Row2,
@@ -6585,7 +6609,7 @@ import {
6585
6609
  ScrollArea as ScrollArea4,
6586
6610
  Select as Select2,
6587
6611
  StatusBar as StatusBar5,
6588
- Text as Text2,
6612
+ Text as Text3,
6589
6613
  TextButton as TextButton2,
6590
6614
  Toggle,
6591
6615
  Toolbar as Toolbar5,
@@ -6738,13 +6762,13 @@ var connectorsViewManifest = {
6738
6762
  outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind, \u2026 } \u2014 applied by the runtime to the controller." }],
6739
6763
  commands: []
6740
6764
  };
6741
- var EMPTY4 = { listing: true, sources: [], empty: true, emptyHint: "Paste CSV or JSON to start with an inline source \u2014 no setup needed.", listed: false, countLabel: "0 sources", form: false, formTitle: "", formBroken: false, formReady: false, fields: [], submitDisabled: true };
6742
- var BEHAVIOURS4 = {
6743
- "data/connectorsPanelView": (inputs) => inputs.view ?? EMPTY4
6765
+ var EMPTY5 = { listing: true, sources: [], empty: true, emptyHint: "Paste CSV or JSON to start with an inline source \u2014 no setup needed.", listed: false, countLabel: "0 sources", form: false, formTitle: "", formBroken: false, formReady: false, fields: [], submitDisabled: true };
6766
+ var BEHAVIOURS5 = {
6767
+ "data/connectorsPanelView": (inputs) => inputs.view ?? EMPTY5
6744
6768
  };
6745
6769
  var CONNECTORS_RUNTIME_UNITS = [];
6746
- var text4 = (b) => ({ component: "text", props: { text: b } });
6747
- var ui4 = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
6770
+ var text5 = (b) => ({ component: "text", props: { text: b } });
6771
+ var ui5 = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
6748
6772
  var CONNECTORS_DECLARATION = {
6749
6773
  format: "xeno.block@1",
6750
6774
  manifest: connectorsViewManifest,
@@ -6756,7 +6780,7 @@ var CONNECTORS_DECLARATION = {
6756
6780
  component: "chrome/Column",
6757
6781
  when: { input: "view.listing" },
6758
6782
  children: [
6759
- { component: "chrome/Toolbar", props: { right: { node: { component: "chrome/ToolbarGroup", props: { end: { value: true } }, children: [{ component: "chrome/IconButton", props: { icon: { node: { component: "icons/Refresh" } }, label: { value: "Refresh all" } }, on: { onClick: ui4("refreshAll") } }] } } } },
6783
+ { component: "chrome/Toolbar", props: { right: { node: { component: "chrome/ToolbarGroup", props: { end: { value: true } }, children: [{ component: "chrome/IconButton", props: { icon: { node: { component: "icons/Refresh" } }, label: { value: "Refresh all" } }, on: { onClick: ui5("refreshAll") } }] } } } },
6760
6784
  { component: "chrome/EmptyState", when: { input: "view.empty" }, props: { title: { value: "No sources" }, hint: { input: "view.emptyHint" } } },
6761
6785
  {
6762
6786
  component: "chrome/ScrollArea",
@@ -6769,8 +6793,8 @@ var CONNECTORS_DECLARATION = {
6769
6793
  {
6770
6794
  component: "chrome/Row",
6771
6795
  each: { input: "view.sources", key: "id" },
6772
- props: { icon: { node: { component: "icons/Plug" } }, label: { item: "name" }, meta: { item: "kind" }, selected: { item: "selected" }, disabled: { item: "disabled" }, title: { item: "title" }, trailing: { node: { component: "chrome/Badge", props: { tone: { item: "tone" } }, children: [text4({ item: "health" })] } } },
6773
- on: { onClick: ui4("select", { id: { item: "id" } }) }
6796
+ props: { icon: { node: { component: "icons/Plug" } }, label: { item: "name" }, meta: { item: "kind" }, selected: { item: "selected" }, disabled: { item: "disabled" }, title: { item: "title" }, trailing: { node: { component: "chrome/Badge", props: { tone: { item: "tone" } }, children: [text5({ item: "health" })] } } },
6797
+ on: { onClick: ui5("select", { id: { item: "id" } }) }
6774
6798
  }
6775
6799
  ]
6776
6800
  }
@@ -6783,25 +6807,25 @@ var CONNECTORS_DECLARATION = {
6783
6807
  component: "chrome/Column",
6784
6808
  when: { input: "view.form" },
6785
6809
  children: [
6786
- { component: "chrome/Toolbar", props: { left: { node: { component: "chrome/Text", props: { role: { value: "title" } }, children: [text4({ input: "view.formTitle" })] } } } },
6810
+ { component: "chrome/Toolbar", props: { left: { node: { component: "chrome/Text", props: { role: { value: "title" } }, children: [text5({ input: "view.formTitle" })] } } } },
6787
6811
  { component: "chrome/EmptyState", when: { input: "view.formBroken" }, props: { title: { value: "This connection cannot be set up" }, hint: { input: "view.formProblem" } } },
6788
6812
  {
6789
6813
  component: "chrome/Toolbar",
6790
6814
  when: { input: "view.formBroken" },
6791
- props: { divided: { value: false }, right: { node: { component: "chrome/ToolbarGroup", props: { end: { value: true } }, children: [{ component: "chrome/TextButton", on: { onClick: ui4("cancel") }, children: [text4({ value: "Close" })] }] } } }
6815
+ props: { divided: { value: false }, right: { node: { component: "chrome/ToolbarGroup", props: { end: { value: true } }, children: [{ component: "chrome/TextButton", on: { onClick: ui5("cancel") }, children: [text5({ value: "Close" })] }] } } }
6792
6816
  },
6793
6817
  {
6794
6818
  component: "chrome/ScrollArea",
6795
6819
  when: { input: "view.formReady" },
6796
6820
  children: [
6797
- { component: "chrome/Note", when: { input: "view.formMessage" }, children: [text4({ input: "view.formMessage" })] },
6821
+ { component: "chrome/Note", when: { input: "view.formMessage" }, children: [text5({ input: "view.formMessage" })] },
6798
6822
  {
6799
6823
  component: "fragment",
6800
6824
  when: { input: "view.redirectUri" },
6801
6825
  children: [
6802
- { component: "chrome/Note", children: [text4({ value: "Add this exact redirect URI to the provider:" })] },
6826
+ { component: "chrome/Note", children: [text5({ value: "Add this exact redirect URI to the provider:" })] },
6803
6827
  // VERBATIM. Rendering the real string is what prevents a redirect-URI mismatch.
6804
- { component: "chrome/Code", props: { variant: { value: "flush" } }, children: [text4({ input: "view.redirectUri" })] }
6828
+ { component: "chrome/Code", props: { variant: { value: "flush" } }, children: [text5({ input: "view.redirectUri" })] }
6805
6829
  ]
6806
6830
  },
6807
6831
  {
@@ -6812,14 +6836,14 @@ var CONNECTORS_DECLARATION = {
6812
6836
  each: { input: "view.fields", key: "key" },
6813
6837
  props: { label: { item: "label" }, hint: { item: "hint" } },
6814
6838
  children: [
6815
- { component: "chrome/Input", when: { item: "isText" }, props: { type: { item: "inputType" }, value: { item: "text" }, placeholder: { item: "placeholder" }, autoComplete: { value: "off" }, label: { item: "label" } }, on: { onChange: ui4("field", { key: { item: "key" }, value: { arg: "" } }) } },
6816
- { component: "chrome/Select", when: { item: "isSelect" }, props: { options: { item: "options" }, value: { item: "text" }, placeholder: { value: "" }, label: { item: "label" } }, on: { onChange: ui4("field", { key: { item: "key" }, value: { arg: "" } }) } },
6817
- { component: "chrome/Toggle", when: { item: "isBoolean" }, props: { checked: { item: "checked" }, label: { item: "label" } }, on: { onChange: ui4("field", { key: { item: "key" }, value: { arg: "" } }) } }
6839
+ { component: "chrome/Input", when: { item: "isText" }, props: { type: { item: "inputType" }, value: { item: "text" }, placeholder: { item: "placeholder" }, autoComplete: { value: "off" }, label: { item: "label" } }, on: { onChange: ui5("field", { key: { item: "key" }, value: { arg: "" } }) } },
6840
+ { component: "chrome/Select", when: { item: "isSelect" }, props: { options: { item: "options" }, value: { item: "text" }, placeholder: { value: "" }, label: { item: "label" } }, on: { onChange: ui5("field", { key: { item: "key" }, value: { arg: "" } }) } },
6841
+ { component: "chrome/Toggle", when: { item: "isBoolean" }, props: { checked: { item: "checked" }, label: { item: "label" } }, on: { onChange: ui5("field", { key: { item: "key" }, value: { arg: "" } }) } }
6818
6842
  ]
6819
6843
  }
6820
6844
  ]
6821
6845
  },
6822
- { component: "chrome/Note", when: { input: "view.docsUrl" }, children: [{ component: "chrome/Link", props: { href: { input: "view.docsUrl" } }, children: [text4({ value: "Where do I get this?" })] }] }
6846
+ { component: "chrome/Note", when: { input: "view.docsUrl" }, children: [{ component: "chrome/Link", props: { href: { input: "view.docsUrl" } }, children: [text5({ value: "Where do I get this?" })] }] }
6823
6847
  ]
6824
6848
  },
6825
6849
  {
@@ -6832,8 +6856,8 @@ var CONNECTORS_DECLARATION = {
6832
6856
  component: "chrome/ToolbarGroup",
6833
6857
  props: { end: { value: true } },
6834
6858
  children: [
6835
- { component: "chrome/TextButton", on: { onClick: ui4("cancel") }, children: [text4({ value: "Cancel" })] },
6836
- { component: "chrome/TextButton", props: { strong: { value: true }, disabled: { input: "view.submitDisabled" } }, on: { onClick: ui4("submit") }, children: [text4({ value: "Connect" })] }
6859
+ { component: "chrome/TextButton", on: { onClick: ui5("cancel") }, children: [text5({ value: "Cancel" })] },
6860
+ { component: "chrome/TextButton", props: { strong: { value: true }, disabled: { input: "view.submitDisabled" } }, on: { onClick: ui5("submit") }, children: [text5({ value: "Connect" })] }
6837
6861
  ]
6838
6862
  }
6839
6863
  }
@@ -6852,10 +6876,10 @@ var RefreshGlyph2 = ICONS["icons/Refresh"];
6852
6876
  function ConnectorsPanelView({ controller, emptyHint }) {
6853
6877
  useSyncExternalStore5(controller.subscribe, controller.getState, controller.getState);
6854
6878
  const view = connectorsViewState(controller, { emptyHint });
6855
- const ui5 = (intent) => applyConnectorsUi(controller, intent);
6879
+ const ui6 = (intent) => applyConnectorsUi(controller, intent);
6856
6880
  return /* @__PURE__ */ jsxs5(Fragment3, { children: [
6857
- view.listing ? /* @__PURE__ */ jsxs5(Column4, { children: [
6858
- /* @__PURE__ */ jsx5(Toolbar5, { right: /* @__PURE__ */ jsx5(ToolbarGroup4, { end: true, children: /* @__PURE__ */ jsx5(IconButton4, { icon: /* @__PURE__ */ jsx5(RefreshGlyph2, {}), label: "Refresh all", onClick: () => ui5({ kind: "refreshAll" }) }) }) }),
6881
+ view.listing ? /* @__PURE__ */ jsxs5(Column5, { children: [
6882
+ /* @__PURE__ */ jsx5(Toolbar5, { right: /* @__PURE__ */ jsx5(ToolbarGroup4, { end: true, children: /* @__PURE__ */ jsx5(IconButton4, { icon: /* @__PURE__ */ jsx5(RefreshGlyph2, {}), label: "Refresh all", onClick: () => ui6({ kind: "refreshAll" }) }) }) }),
6859
6883
  view.empty ? /* @__PURE__ */ jsx5(EmptyState5, { title: "No sources", hint: view.emptyHint }) : null,
6860
6884
  view.listed ? /* @__PURE__ */ jsx5(ScrollArea4, { children: /* @__PURE__ */ jsx5(RowList2, { role: "listbox", "aria-label": "Data sources", children: view.sources.map((source) => /* @__PURE__ */ jsx5(
6861
6885
  Row2,
@@ -6867,16 +6891,16 @@ function ConnectorsPanelView({ controller, emptyHint }) {
6867
6891
  disabled: source.disabled,
6868
6892
  title: source.title,
6869
6893
  trailing: /* @__PURE__ */ jsx5(Badge5, { tone: source.tone, children: source.health }),
6870
- onClick: () => ui5({ kind: "select", id: source.id })
6894
+ onClick: () => ui6({ kind: "select", id: source.id })
6871
6895
  },
6872
6896
  source.id
6873
6897
  )) }) }) : null,
6874
6898
  /* @__PURE__ */ jsx5(StatusBar5, { left: view.countLabel, right: view.busyLabel })
6875
6899
  ] }) : null,
6876
- view.form ? /* @__PURE__ */ jsxs5(Column4, { children: [
6877
- /* @__PURE__ */ jsx5(Toolbar5, { left: /* @__PURE__ */ jsx5(Text2, { role: "title", children: view.formTitle }) }),
6900
+ view.form ? /* @__PURE__ */ jsxs5(Column5, { children: [
6901
+ /* @__PURE__ */ jsx5(Toolbar5, { left: /* @__PURE__ */ jsx5(Text3, { role: "title", children: view.formTitle }) }),
6878
6902
  view.formBroken ? /* @__PURE__ */ jsx5(EmptyState5, { title: "This connection cannot be set up", hint: view.formProblem }) : null,
6879
- view.formBroken ? /* @__PURE__ */ jsx5(Toolbar5, { divided: false, right: /* @__PURE__ */ jsx5(ToolbarGroup4, { end: true, children: /* @__PURE__ */ jsx5(TextButton2, { onClick: () => ui5({ kind: "cancel" }), children: "Close" }) }) }) : null,
6903
+ view.formBroken ? /* @__PURE__ */ jsx5(Toolbar5, { divided: false, right: /* @__PURE__ */ jsx5(ToolbarGroup4, { end: true, children: /* @__PURE__ */ jsx5(TextButton2, { onClick: () => ui6({ kind: "cancel" }), children: "Close" }) }) }) : null,
6880
6904
  view.formReady ? /* @__PURE__ */ jsxs5(ScrollArea4, { children: [
6881
6905
  view.formMessage ? /* @__PURE__ */ jsx5(Note2, { children: view.formMessage }) : null,
6882
6906
  view.redirectUri ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
@@ -6884,9 +6908,9 @@ function ConnectorsPanelView({ controller, emptyHint }) {
6884
6908
  /* @__PURE__ */ jsx5(Code, { variant: "flush", children: view.redirectUri })
6885
6909
  ] }) : null,
6886
6910
  /* @__PURE__ */ jsx5(FieldGroup, { children: view.fields.map((field) => /* @__PURE__ */ jsxs5(Field, { label: field.label, hint: field.hint, children: [
6887
- field.isText ? /* @__PURE__ */ jsx5(Input, { type: field.inputType, value: field.text, placeholder: field.placeholder, autoComplete: "off", label: field.label, onChange: (value) => ui5({ kind: "field", key: field.key, value }) }) : null,
6888
- field.isSelect ? /* @__PURE__ */ jsx5(Select2, { options: field.options, value: field.text, placeholder: "", label: field.label, onChange: (value) => ui5({ kind: "field", key: field.key, value }) }) : null,
6889
- field.isBoolean ? /* @__PURE__ */ jsx5(Toggle, { checked: field.checked, label: field.label, onChange: (value) => ui5({ kind: "field", key: field.key, value }) }) : null
6911
+ field.isText ? /* @__PURE__ */ jsx5(Input2, { type: field.inputType, value: field.text, placeholder: field.placeholder, autoComplete: "off", label: field.label, onChange: (value) => ui6({ kind: "field", key: field.key, value }) }) : null,
6912
+ field.isSelect ? /* @__PURE__ */ jsx5(Select2, { options: field.options, value: field.text, placeholder: "", label: field.label, onChange: (value) => ui6({ kind: "field", key: field.key, value }) }) : null,
6913
+ field.isBoolean ? /* @__PURE__ */ jsx5(Toggle, { checked: field.checked, label: field.label, onChange: (value) => ui6({ kind: "field", key: field.key, value }) }) : null
6890
6914
  ] }, field.key)) }),
6891
6915
  view.docsUrl ? /* @__PURE__ */ jsx5(Note2, { children: /* @__PURE__ */ jsx5(Link, { href: view.docsUrl, children: "Where do I get this?" }) }) : null
6892
6916
  ] }) : null,
@@ -6895,8 +6919,8 @@ function ConnectorsPanelView({ controller, emptyHint }) {
6895
6919
  {
6896
6920
  divided: false,
6897
6921
  right: /* @__PURE__ */ jsxs5(ToolbarGroup4, { end: true, children: [
6898
- /* @__PURE__ */ jsx5(TextButton2, { onClick: () => ui5({ kind: "cancel" }), children: "Cancel" }),
6899
- /* @__PURE__ */ jsx5(TextButton2, { strong: true, disabled: view.submitDisabled, onClick: () => ui5({ kind: "submit" }), children: "Connect" })
6922
+ /* @__PURE__ */ jsx5(TextButton2, { onClick: () => ui6({ kind: "cancel" }), children: "Cancel" }),
6923
+ /* @__PURE__ */ jsx5(TextButton2, { strong: true, disabled: view.submitDisabled, onClick: () => ui6({ kind: "submit" }), children: "Connect" })
6900
6924
  ] })
6901
6925
  }
6902
6926
  ) : null
@@ -7218,14 +7242,14 @@ export {
7218
7242
  AGENT_HIDDEN_COMMANDS,
7219
7243
  ALL_AGGREGATES,
7220
7244
  ALL_CHART_TYPES,
7221
- BEHAVIOURS as CHART_BEHAVIOURS,
7245
+ BEHAVIOURS2 as CHART_BEHAVIOURS,
7222
7246
  CHART_DECLARATION,
7223
7247
  CHART_EMPTY_FILL,
7224
7248
  CHART_PALETTE_STEPS,
7225
7249
  CHART_PANEL_ID,
7226
7250
  CHART_RUNTIME_UNITS,
7227
7251
  CHART_TYPE_LABELS,
7228
- BEHAVIOURS4 as CONNECTORS_BEHAVIOURS,
7252
+ BEHAVIOURS5 as CONNECTORS_BEHAVIOURS,
7229
7253
  CONNECTORS_DECLARATION,
7230
7254
  CONNECTORS_PANEL_ID,
7231
7255
  CONNECTORS_RUNTIME_UNITS,
@@ -7241,14 +7265,14 @@ export {
7241
7265
  DEFAULT_RANGES,
7242
7266
  EMPTY_LAYOUT,
7243
7267
  EXPIRY_SKEW_MS,
7244
- BEHAVIOURS3 as FIELDS_BEHAVIOURS,
7268
+ BEHAVIOURS4 as FIELDS_BEHAVIOURS,
7245
7269
  FIELDS_DECLARATION,
7246
7270
  FIELDS_PANEL_ID,
7247
7271
  FIELDS_RUNTIME_UNITS,
7248
7272
  FieldsController,
7249
7273
  FieldsPanelView,
7250
7274
  IMPLEMENTED_VIEW_MODES,
7251
- BEHAVIOURS2 as METRICS_BEHAVIOURS,
7275
+ BEHAVIOURS3 as METRICS_BEHAVIOURS,
7252
7276
  METRICS_DECLARATION,
7253
7277
  METRICS_PANEL_ID,
7254
7278
  METRICS_RUNTIME_UNITS,
@@ -7259,14 +7283,19 @@ export {
7259
7283
  MetricsPanelView,
7260
7284
  SECRET_HEADER_NAMES,
7261
7285
  SHELF_PRESETS,
7286
+ BEHAVIOURS as TABLE_BEHAVIOURS,
7287
+ TABLE_DECLARATION,
7262
7288
  TABLE_PANEL_ID,
7289
+ TABLE_RUNTIME_UNITS,
7263
7290
  TableController,
7264
7291
  TablePanelView,
7292
+ TableScroller,
7265
7293
  applyChartUi,
7266
7294
  applyConnectorsUi,
7267
7295
  applyFieldsUi,
7268
7296
  applyMetricsUi,
7269
7297
  applyPrecision,
7298
+ applyTableUi,
7270
7299
  base64Url,
7271
7300
  breachSideOf,
7272
7301
  browserFetch,
@@ -7372,6 +7401,9 @@ export {
7372
7401
  suggestedShelfFor,
7373
7402
  tableManifest,
7374
7403
  tablePanel,
7404
+ tableRuntimeComponents,
7405
+ tableViewManifest,
7406
+ tableViewState,
7375
7407
  toChartJsConfig,
7376
7408
  toExecuteError,
7377
7409
  totalWidth,