@xenosystem/blocks 0.13.7 → 0.13.8

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.
@@ -307,24 +307,24 @@ var ICONS = Object.freeze({
307
307
  var ICON_UNITS = Object.keys(ICONS);
308
308
 
309
309
  export {
310
- KindGlyph,
311
- VisibilityIcon,
312
- LockStateIcon,
310
+ TreeIcon,
311
+ EyedropperIcon,
312
+ SwapIcon,
313
+ EntryIcon,
314
+ XIcon,
315
+ TrashIcon,
316
+ UndoIcon,
317
+ RedoIcon,
313
318
  StopwatchIcon,
314
319
  DiamondIcon,
315
320
  PrevIcon,
316
321
  NextIcon,
317
322
  LinkIcon,
318
323
  ResetIcon,
319
- XIcon2 as XIcon,
320
- EntryIcon,
321
- XIcon as XIcon2,
322
- TrashIcon,
323
- UndoIcon,
324
- RedoIcon,
325
- EyedropperIcon,
326
- SwapIcon,
327
- TreeIcon,
324
+ XIcon2,
325
+ KindGlyph,
326
+ VisibilityIcon,
327
+ LockStateIcon,
328
328
  ICONS,
329
329
  ICON_UNITS
330
330
  };
@@ -1,4 +1,4 @@
1
- import { PanelModule, PanelManifest, LogSink } from '@xenosystem/block-sdk';
1
+ import { PanelModule, PanelManifest, BlockDeclaration, LogSink } from '@xenosystem/block-sdk';
2
2
  import * as _xenosystem_data_core from '@xenosystem/data-core';
3
3
  import { XenoColumn, XenoQuery, XenoResultSet, XenoValue, XenoRow, XenoAggFn, XenoValueFormat, XenoFilterGroup, XenoColumnRole, XenoSchemaCatalog, XenoFilter, XenoSourceSchema } from '@xenosystem/data-core';
4
4
  import { ReactNode } from 'react';
@@ -1762,24 +1762,153 @@ declare const FIELDS_PANEL_ID = "xeno.core.fields";
1762
1762
  declare const fieldsManifest: PanelManifest;
1763
1763
 
1764
1764
  /**
1765
- * The Fields panel view — column list, shelves, filter builder.
1765
+ * The Fields panel AS A DECLARATION — the tenth declared block, and the first DATA block: a schema's
1766
+ * columns, the shelves they are dropped on (rows / columns / values / sorts…), and the filter group,
1767
+ * over the host's data-core query model.
1766
1768
  *
1767
- * Composes `@xenosystem/components/chrome`; the host must ensure
1768
- * `@xenosystem/workbench/primitives.css` is present.
1769
+ * Everything drawn is data: the toolbar (a "sampled schema" badge, Auto-assign, the optional Apply),
1770
+ * the Fields section (a listbox of columns, focus-selectable, with a distinct-count badge), one
1771
+ * Section per shelf (entries as a plain list — each carries an aggregate `Select`, a direction
1772
+ * button or a granularity badge, and a remove button, so it is a `list`, never a `listbox`), the
1773
+ * Filters section (leaf rows with an operator `Select`, group rows, the combinator strip), the
1774
+ * status bar. Shelf hints and the empty-filters note are `chrome/Note`.
1775
+ *
1776
+ * Nothing is bound at runtime: every intent — focus, remove, aggregate, direction, add/remove/update
1777
+ * filter, combinator, auto-assign, apply — is a `ui` emit the runtime applies. A `Select` inside a
1778
+ * row emits its own change; the row is not selectable, so nothing competes.
1779
+ */
1780
+
1781
+ interface FieldsViewColumn {
1782
+ id: string;
1783
+ name: string;
1784
+ role: string;
1785
+ focused: boolean;
1786
+ stat?: string;
1787
+ statTitle?: string;
1788
+ }
1789
+ interface FieldsViewEntry {
1790
+ /** Flattened: a nested `each` cannot reach the outer item, so the entry carries its shelf. */
1791
+ shelfId: string;
1792
+ columnId: string;
1793
+ label: string;
1794
+ suggested: boolean;
1795
+ aggregated: boolean;
1796
+ fn: string;
1797
+ aggregates: {
1798
+ value: string;
1799
+ label: string;
1800
+ }[];
1801
+ sortable: boolean;
1802
+ direction: string;
1803
+ granularity?: string;
1804
+ removeLabel: string;
1805
+ }
1806
+ interface FieldsViewShelf {
1807
+ id: string;
1808
+ label: string;
1809
+ count: number;
1810
+ hasEntries: boolean;
1811
+ empty: boolean;
1812
+ hint: string;
1813
+ entries: FieldsViewEntry[];
1814
+ }
1815
+ interface FieldsViewFilter {
1816
+ key: string;
1817
+ index: number;
1818
+ leaf: boolean;
1819
+ label: string;
1820
+ op: string;
1821
+ ops: {
1822
+ value: string;
1823
+ label: string;
1824
+ }[];
1825
+ removeLabel: string;
1826
+ }
1827
+ interface FieldsViewModel {
1828
+ hasColumns: boolean;
1829
+ noColumns: boolean;
1830
+ sampleWarning?: string;
1831
+ sampled: boolean;
1832
+ manualApply: boolean;
1833
+ applyDisabled: boolean;
1834
+ columns: FieldsViewColumn[];
1835
+ shelves: FieldsViewShelf[];
1836
+ filters: FieldsViewFilter[];
1837
+ filterCount: number;
1838
+ hasFilters: boolean;
1839
+ noFilters: boolean;
1840
+ combinator: 'and' | 'or';
1841
+ countLabel: string;
1842
+ dirtyLabel?: string;
1843
+ }
1844
+ type FieldsUiIntent = {
1845
+ kind: 'autoAssign';
1846
+ } | {
1847
+ kind: 'apply';
1848
+ } | {
1849
+ kind: 'focus';
1850
+ columnId: string;
1851
+ } | {
1852
+ kind: 'remove';
1853
+ shelfId: string;
1854
+ columnId: string;
1855
+ } | {
1856
+ kind: 'aggregate';
1857
+ shelfId: string;
1858
+ columnId: string;
1859
+ fn: string;
1860
+ } | {
1861
+ kind: 'direction';
1862
+ shelfId: string;
1863
+ columnId: string;
1864
+ } | {
1865
+ kind: 'addFilter';
1866
+ } | {
1867
+ kind: 'addGroup';
1868
+ } | {
1869
+ kind: 'filterOp';
1870
+ index: number;
1871
+ op: string;
1872
+ } | {
1873
+ kind: 'removeFilter';
1874
+ index: number;
1875
+ } | {
1876
+ kind: 'combinator';
1877
+ combinator: 'and' | 'or';
1878
+ };
1879
+ interface FieldsViewOptions {
1880
+ showStats?: boolean;
1881
+ manualApply?: boolean;
1882
+ }
1883
+ /** Derive the view from the live controller — the runtime calls this on every draw. */
1884
+ declare function fieldsViewState(c: FieldsController, options?: FieldsViewOptions): FieldsViewModel;
1885
+ /** Apply a `ui` intent to the controller. */
1886
+ declare function applyFieldsUi(c: FieldsController, intent: FieldsUiIntent): void;
1887
+ declare const fieldsViewManifest: PanelManifest;
1888
+ declare const BEHAVIOURS: {
1889
+ 'data/fieldsPanelView': (inputs: Record<string, unknown>) => FieldsViewModel;
1890
+ };
1891
+ /** No runtime-bound units: every intent is a `ui` emit. */
1892
+ declare const FIELDS_RUNTIME_UNITS: readonly [];
1893
+ declare const FIELDS_DECLARATION: BlockDeclaration;
1894
+
1895
+ /**
1896
+ * The Fields panel view — the CODE VIEW of `FIELDS_DECLARATION`, drawing the same `FieldsViewModel`
1897
+ * byte-identical (the equivalence suite holds the two together). Nothing here reaches the controller
1898
+ * except through `applyFieldsUi`; there is no runtime-bound unit.
1899
+ *
1900
+ * Composes `@xenosystem/components/chrome`; the host must ensure `@xenosystem/workbench/primitives.css` is
1901
+ * present.
1769
1902
  *
1770
1903
  * @module
1771
1904
  */
1772
1905
 
1773
1906
  /** Props for {@link FieldsPanelView}. */
1774
- interface FieldsPanelViewProps {
1907
+ interface FieldsPanelViewProps extends FieldsViewOptions {
1775
1908
  controller: FieldsController;
1776
- /** Render live cardinality/null counts. */
1777
- showStats?: boolean;
1778
- /** Show an explicit Apply (config `autoApply: false`). */
1779
- manualApply?: boolean;
1780
1909
  }
1781
1910
  /** The Fields panel view. */
1782
- declare function FieldsPanelView({ controller, showStats, manualApply, }: FieldsPanelViewProps): ReactNode;
1911
+ declare function FieldsPanelView({ controller, showStats, manualApply }: FieldsPanelViewProps): ReactNode;
1783
1912
 
1784
1913
  /**
1785
1914
  * The Connectors controller — the data family's **resolver**.
@@ -3252,4 +3381,4 @@ interface ConnectorsPanelViewProps {
3252
3381
  /** The Connectors panel view. */
3253
3382
  declare function ConnectorsPanelView({ controller, emptyHint, }: ConnectorsPanelViewProps): ReactNode;
3254
3383
 
3255
- export { AGENT_HIDDEN_COMMANDS, ALL_AGGREGATES, ALL_CHART_TYPES, type BuildChartOptions, type BuildSelectOptions, type BuildSelectResult, CHART_EMPTY_FILL, CHART_PALETTE_STEPS, CHART_PANEL_ID, CONNECTORS_PANEL_ID, type CellAddress, ChartController, type ChartControllerOptions, type ChartHostBridge, type ChartPanelState, ChartPanelView, type ChartPanelViewProps, type ChartRenderContext, type ChartRenderInput, type ChartRenderOptions, type ChartRenderer, type ChartViewState, type ColumnLayout, type ColumnWindow, ConnectorExecuteInput, ConnectorExecuteOutput, ConnectorTransport, ConnectorsController, type ConnectorsControllerOptions, type ConnectorsHostBridge, ConnectorsPanelState, ConnectorsPanelView, type ConnectorsPanelViewProps, type ConnectorsRenderContext, ConnectorsViewState, type CreateChartPanelOptions, type CreateConnectorExecutorOptions, type CreateConnectorsPanelOptions, type CreateFieldsPanelOptions, type CreateMetricsPanelOptions, type CreateTablePanelOptions, type CredentialDraft, type CredentialErrors, DEFAULT_COLUMN_WIDTH, DEFAULT_PAGE_SIZE, DEFAULT_RANGES, type DeltaDirection, EMPTY_LAYOUT, EXPIRY_SKEW_MS, type ExecuteError, FIELDS_PANEL_ID, FieldsController, type FieldsControllerOptions, type FieldsHostBridge, type FieldsPanelState, FieldsPanelView, type FieldsPanelViewProps, type FieldsRenderContext, type FieldsViewState, type HostSqlResolver, IMPLEMENTED_VIEW_MODES, type LoopbackRequest, type LoopbackVerdict, METRICS_PANEL_ID, MIN_COLUMN_WIDTH, MONO_FILL_PALETTE, MONO_PALETTE, MetricsController, type MetricsControllerOptions, type MetricsHostBridge, type MetricsPanelState, MetricsPanelView, type MetricsPanelViewProps, type MetricsRenderContext, type MetricsViewState, type NumericConstraints, type OAuthStateRecord, type OAuthStateStore, type OAuthStateStoreOptions, type ParseCsvOptions, type ParseResult, type ParsedCsv, type PkcePair, PushdownSplit, type RefreshCoordinator, type ResolvedColumn, SHELF_PRESETS, type StoredOAuthToken, SubstrateFacilities, TABLE_PANEL_ID, TableController, type TableControllerOptions, type TableHostBridge, type TablePanelState, TablePanelView, type TablePanelViewProps, type TableRenderContext, type TableViewMode, type TableViewState, type TokenEndpointResponse, type VirtualWindow, type XenoAlertLevel, type XenoCalculatedFieldRequest, type XenoCellEdit, type XenoChartData, type XenoChartDrillDown, type XenoChartEncoding, type XenoChartExportRequest, type XenoChartHighlight, type XenoChartSeries, type XenoChartType, XenoConnection, XenoConnectionStatus, XenoConnectorFetch, XenoConnectorFileReader, XenoConnectorQueryRequest, XenoCredentialField, XenoCredentialFormSpec, XenoCredentialResult, type XenoFieldStat, type XenoMetricAlert, type XenoMetricRange, type XenoMetricSpec, type XenoMetricThreshold, type XenoMetricValue, XenoResolverErrorCode, XenoRestPaging, type XenoRowActivate, type XenoShelfEntry, type XenoShelfSpec, XenoSqlEngine, applyPrecision, base64Url, breachSideOf, buildChartData, buildRestUrl, buildSelect, chartManifest, chartPanel, clamp, classifyHttpStatus, coerceCsvValues, columnIndexAt, columnsOf, compileForHost, computeColumnWindow, computeWindow, connectorsManifest, connectorsPanel, constraintsOfColumn, createChartPanel, createConnectorExecutor, createConnectorsPanel, createFieldsPanel, createMetricsPanel, createOAuthStateStore, createPkcePair, createRefreshCoordinator, createTablePanel, credentialSpecProblem, defaultAggregateFor, defaultEncoding, deltaToneOf, emptyFilterGroup, entityOf, escapeLikePattern, executeCsvFile, executeDuckDb, executeGraphql, executeJsonFile, executeRest, executeSql, extractJsonPath, extractRows, fieldsManifest, fieldsPanel, firstGraphqlError, frozenWidth, getChartRenderer, isCategorical, isFieldVisible, isFilled, isLeafFilter, isLoopbackHost, isSafeTableName, mergeRefreshResponse, metricsManifest, metricsPanel, moveCell, moveColumn, needsRefresh, nextPageToken, normalizeLayout, normalizeNumeric, offerableAggregates, offerableFilterOps, parseCsv, parseJsonBody, parseJsonPath, parseLinkHeader, parseNumeric, pruneHiddenValues, queriesEqual, quoteIdent, quoteLiteral, redirectUriFor, renderFilterGroup, resolveChartColors, resolveColumns, rowsForMark, scrollLeftToReveal, scrollTopToReveal, setChartRenderer, setColumnVisible, setColumnWidth, setFrozenCount, sniffDelimiter, suggestedShelfFor, tableManifest, tablePanel, toChartJsConfig, toExecuteError, totalWidth, uniqueTableName, unsupportedMessage, validateCredentialDraft, validateEncoding, validateLoopbackRequest, visibleFields };
3384
+ export { AGENT_HIDDEN_COMMANDS, ALL_AGGREGATES, ALL_CHART_TYPES, type BuildChartOptions, type BuildSelectOptions, type BuildSelectResult, CHART_EMPTY_FILL, CHART_PALETTE_STEPS, CHART_PANEL_ID, CONNECTORS_PANEL_ID, type CellAddress, ChartController, type ChartControllerOptions, type ChartHostBridge, type ChartPanelState, ChartPanelView, type ChartPanelViewProps, type ChartRenderContext, type ChartRenderInput, type ChartRenderOptions, type ChartRenderer, type ChartViewState, type ColumnLayout, type ColumnWindow, ConnectorExecuteInput, ConnectorExecuteOutput, ConnectorTransport, ConnectorsController, type ConnectorsControllerOptions, type ConnectorsHostBridge, ConnectorsPanelState, ConnectorsPanelView, type ConnectorsPanelViewProps, type ConnectorsRenderContext, ConnectorsViewState, type CreateChartPanelOptions, type CreateConnectorExecutorOptions, type CreateConnectorsPanelOptions, type CreateFieldsPanelOptions, type CreateMetricsPanelOptions, type CreateTablePanelOptions, type CredentialDraft, type CredentialErrors, DEFAULT_COLUMN_WIDTH, DEFAULT_PAGE_SIZE, DEFAULT_RANGES, type DeltaDirection, EMPTY_LAYOUT, EXPIRY_SKEW_MS, type ExecuteError, BEHAVIOURS as FIELDS_BEHAVIOURS, FIELDS_DECLARATION, FIELDS_PANEL_ID, FIELDS_RUNTIME_UNITS, FieldsController, type FieldsControllerOptions, type FieldsHostBridge, type FieldsPanelState, FieldsPanelView, type FieldsPanelViewProps, type FieldsRenderContext, type FieldsUiIntent, type FieldsViewColumn, type FieldsViewEntry, type FieldsViewFilter, type FieldsViewModel, type FieldsViewOptions, type FieldsViewShelf, type FieldsViewState, type HostSqlResolver, IMPLEMENTED_VIEW_MODES, type LoopbackRequest, type LoopbackVerdict, METRICS_PANEL_ID, MIN_COLUMN_WIDTH, MONO_FILL_PALETTE, MONO_PALETTE, MetricsController, type MetricsControllerOptions, type MetricsHostBridge, type MetricsPanelState, MetricsPanelView, type MetricsPanelViewProps, type MetricsRenderContext, type MetricsViewState, type NumericConstraints, type OAuthStateRecord, type OAuthStateStore, type OAuthStateStoreOptions, type ParseCsvOptions, type ParseResult, type ParsedCsv, type PkcePair, PushdownSplit, type RefreshCoordinator, type ResolvedColumn, SHELF_PRESETS, type StoredOAuthToken, SubstrateFacilities, TABLE_PANEL_ID, TableController, type TableControllerOptions, type TableHostBridge, type TablePanelState, TablePanelView, type TablePanelViewProps, type TableRenderContext, type TableViewMode, type TableViewState, type TokenEndpointResponse, type VirtualWindow, type XenoAlertLevel, type XenoCalculatedFieldRequest, type XenoCellEdit, type XenoChartData, type XenoChartDrillDown, type XenoChartEncoding, type XenoChartExportRequest, type XenoChartHighlight, type XenoChartSeries, type XenoChartType, XenoConnection, XenoConnectionStatus, XenoConnectorFetch, XenoConnectorFileReader, XenoConnectorQueryRequest, XenoCredentialField, XenoCredentialFormSpec, XenoCredentialResult, type XenoFieldStat, type XenoMetricAlert, type XenoMetricRange, type XenoMetricSpec, type XenoMetricThreshold, type XenoMetricValue, XenoResolverErrorCode, XenoRestPaging, type XenoRowActivate, type XenoShelfEntry, type XenoShelfSpec, XenoSqlEngine, applyFieldsUi, applyPrecision, base64Url, breachSideOf, buildChartData, buildRestUrl, buildSelect, chartManifest, chartPanel, clamp, classifyHttpStatus, coerceCsvValues, columnIndexAt, columnsOf, compileForHost, computeColumnWindow, computeWindow, connectorsManifest, connectorsPanel, constraintsOfColumn, createChartPanel, createConnectorExecutor, createConnectorsPanel, createFieldsPanel, createMetricsPanel, createOAuthStateStore, createPkcePair, createRefreshCoordinator, createTablePanel, credentialSpecProblem, defaultAggregateFor, defaultEncoding, deltaToneOf, emptyFilterGroup, entityOf, escapeLikePattern, executeCsvFile, executeDuckDb, executeGraphql, executeJsonFile, executeRest, executeSql, extractJsonPath, extractRows, fieldsManifest, fieldsPanel, fieldsViewManifest, fieldsViewState, firstGraphqlError, frozenWidth, getChartRenderer, isCategorical, isFieldVisible, isFilled, isLeafFilter, isLoopbackHost, isSafeTableName, mergeRefreshResponse, metricsManifest, metricsPanel, moveCell, moveColumn, needsRefresh, nextPageToken, normalizeLayout, normalizeNumeric, offerableAggregates, offerableFilterOps, parseCsv, parseJsonBody, parseJsonPath, parseLinkHeader, parseNumeric, pruneHiddenValues, queriesEqual, quoteIdent, quoteLiteral, redirectUriFor, renderFilterGroup, resolveChartColors, resolveColumns, rowsForMark, scrollLeftToReveal, scrollTopToReveal, setChartRenderer, setColumnVisible, setColumnWidth, setFrozenCount, sniffDelimiter, suggestedShelfFor, tableManifest, tablePanel, toChartJsConfig, toExecuteError, totalWidth, uniqueTableName, unsupportedMessage, validateCredentialDraft, validateEncoding, validateLoopbackRequest, visibleFields };
@@ -6,15 +6,17 @@ import {
6
6
  quoteLiteral,
7
7
  renderFilterGroup
8
8
  } from "../chunk-DJJGZ6U4.js";
9
+ import {
10
+ ICONS
11
+ } from "../chunk-BNALWAAQ.js";
12
+ import "../chunk-GTN5EDY3.js";
9
13
  import {
10
14
  ChevronGlyph,
11
15
  DownloadGlyph,
12
16
  PlugGlyph,
13
- PlusGlyph,
14
17
  RefreshGlyph,
15
18
  SortAscGlyph,
16
- SortDescGlyph,
17
- XGlyph
19
+ SortDescGlyph
18
20
  } from "../chunk-K7YLLV2W.js";
19
21
  import "../chunk-2KG3PWR4.js";
20
22
 
@@ -197,8 +199,8 @@ function moveCell(from, delta, bounds) {
197
199
  }
198
200
 
199
201
  // src/data/table/numeric.ts
200
- function parseNumeric(text) {
201
- const trimmed = String(text).trim();
202
+ function parseNumeric(text2) {
203
+ const trimmed = String(text2).trim();
202
204
  if (trimmed.length === 0) return { ok: false, reason: "empty" };
203
205
  const match = /^[+-]?(\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/.exec(trimmed);
204
206
  if (!match) return { ok: false, reason: "nan" };
@@ -552,11 +554,11 @@ var TableController = class {
552
554
  return true;
553
555
  }
554
556
  /** Update the in-flight draft. Emits `editPreview` only when `livePreview` is on. */
555
- updateDraft(text) {
557
+ updateDraft(text2) {
556
558
  if (!this.editing) return;
557
- this.editing = { ...this.editing, draft: text };
559
+ this.editing = { ...this.editing, draft: text2 };
558
560
  if (this.livePreview) {
559
- const parsed = this.parseDraft(this.editing.columnId, text);
561
+ const parsed = this.parseDraft(this.editing.columnId, text2);
560
562
  if (parsed.ok) {
561
563
  this.host.emit("editPreview", this.buildEdit(this.editing, parsed.value));
562
564
  }
@@ -608,12 +610,12 @@ var TableController = class {
608
610
  };
609
611
  }
610
612
  /** Parse a draft according to the column's declared type. */
611
- parseDraft(columnId, text) {
613
+ parseDraft(columnId, text2) {
612
614
  const column = this.columnById(columnId);
613
- const trimmed = text.trim();
615
+ const trimmed = text2.trim();
614
616
  if (column?.type === "number" || column?.type === "integer" || column?.type === "duration") {
615
617
  if (trimmed === "") return { ok: true, value: null };
616
- const parsed = parseNumeric(text);
618
+ const parsed = parseNumeric(text2);
617
619
  if (!parsed.ok) return { ok: false, reason: parsed.reason };
618
620
  return { ok: true, value: normalizeNumeric(parsed.value, constraintsOfColumn(column)) };
619
621
  }
@@ -630,7 +632,7 @@ var TableController = class {
630
632
  value: trimmed === "" ? [] : trimmed.split(",").map((part) => part.trim()).filter(Boolean)
631
633
  };
632
634
  }
633
- return { ok: true, value: trimmed === "" ? null : text };
635
+ return { ok: true, value: trimmed === "" ? null : text2 };
634
636
  }
635
637
  rowById(rowId) {
636
638
  return this.result?.rows.find((r) => r.id === rowId);
@@ -3239,7 +3241,7 @@ function emptyFilterGroup() {
3239
3241
  function offerableFilterOps(column, sourceOps = []) {
3240
3242
  const common = ["eq", "ne", "isEmpty", "isNotEmpty", "in", "notIn"];
3241
3243
  const ordered = ["gt", "gte", "lt", "lte", "between"];
3242
- const text = ["contains", "notContains", "startsWith", "endsWith"];
3244
+ const text2 = ["contains", "notContains", "startsWith", "endsWith"];
3243
3245
  const temporal = ["relative"];
3244
3246
  let ops = [...common];
3245
3247
  switch (column?.type) {
@@ -3257,10 +3259,10 @@ function offerableFilterOps(column, sourceOps = []) {
3257
3259
  case "url":
3258
3260
  case "select":
3259
3261
  case "multiselect":
3260
- ops = [...ops, ...text];
3262
+ ops = [...ops, ...text2];
3261
3263
  break;
3262
3264
  default:
3263
- ops = [...ops, ...text, ...ordered];
3265
+ ops = [...ops, ...text2, ...ordered];
3264
3266
  }
3265
3267
  if (sourceOps.length === 0) return ops;
3266
3268
  return ops.filter((op) => sourceOps.includes(op));
@@ -3891,233 +3893,363 @@ var fieldsManifest = {
3891
3893
  import { useSyncExternalStore as useSyncExternalStore4 } from "react";
3892
3894
  import {
3893
3895
  Badge as Badge4,
3896
+ Column,
3894
3897
  EmptyState as EmptyState4,
3895
3898
  IconButton as IconButton3,
3899
+ Note,
3896
3900
  Row,
3897
3901
  RowList,
3898
3902
  ScrollArea as ScrollArea3,
3899
3903
  Section,
3900
3904
  SegmentedControl as SegmentedControl4,
3905
+ Select,
3901
3906
  StatusBar as StatusBar4,
3902
3907
  TextButton,
3903
3908
  Toolbar as Toolbar4,
3904
3909
  ToolbarGroup as ToolbarGroup3
3905
3910
  } from "@xenosystem/components/chrome";
3906
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
3907
- function FieldsPanelView({
3908
- controller,
3909
- showStats = true,
3910
- manualApply = false
3911
- }) {
3912
- const state = useSyncExternalStore4(controller.subscribe, controller.getState, controller.getState);
3913
- if (state.columns.length === 0) {
3914
- return /* @__PURE__ */ jsx4(
3915
- EmptyState4,
3911
+
3912
+ // src/data/fields/declaration.ts
3913
+ var COMBINATORS = [{ value: "and", label: "All" }, { value: "or", label: "Any" }];
3914
+ function fieldsViewState(c, options = {}) {
3915
+ const s = c.getState();
3916
+ const showStats = options.showStats ?? true;
3917
+ const manualApply = options.manualApply ?? false;
3918
+ const columnOf = (id) => s.columns.find((col) => col.id === id);
3919
+ const shelves = s.shelves.filter((shelf) => shelf.id !== "filters").map((shelf) => {
3920
+ const entries = s.entries[shelf.id] ?? [];
3921
+ return {
3922
+ id: shelf.id,
3923
+ label: shelf.label,
3924
+ count: entries.length,
3925
+ hasEntries: entries.length > 0,
3926
+ empty: entries.length === 0,
3927
+ hint: shelf.hint ?? `Drop a ${shelf.accepts.join(" or ")} here.`,
3928
+ entries: entries.map((entry) => {
3929
+ const column = columnOf(entry.columnId);
3930
+ return {
3931
+ shelfId: shelf.id,
3932
+ columnId: entry.columnId,
3933
+ label: entry.label ?? column?.name ?? entry.columnId,
3934
+ suggested: entry.suggested === true,
3935
+ aggregated: shelf.aggregated === true,
3936
+ fn: entry.fn ?? "count",
3937
+ aggregates: shelf.aggregated ? offerableAggregates(column, s.allowedAggregates).map((fn) => ({ value: fn, label: fn })) : [],
3938
+ sortable: !shelf.aggregated && shelf.id === "sorts",
3939
+ direction: entry.direction === "desc" ? "desc" : "asc",
3940
+ granularity: !shelf.aggregated && shelf.id !== "sorts" ? entry.granularity : void 0,
3941
+ removeLabel: `Remove ${column?.name ?? entry.columnId}`
3942
+ };
3943
+ })
3944
+ };
3945
+ });
3946
+ const filters = s.filters.filters.map((member, index) => {
3947
+ if (!isLeafFilter(member)) {
3948
+ return { key: `g${index}`, index, leaf: false, label: `(${member.combinator.toUpperCase()} group \u2014 ${member.filters.length})`, op: "", ops: [], removeLabel: "Remove group" };
3949
+ }
3950
+ const column = columnOf(member.columnId);
3951
+ return {
3952
+ key: `f${index}`,
3953
+ index,
3954
+ leaf: true,
3955
+ label: column?.name ?? member.columnId,
3956
+ op: member.op,
3957
+ ops: offerableFilterOps(column, s.allowedFilterOps).map((op) => ({ value: op, label: op })),
3958
+ removeLabel: "Remove filter"
3959
+ };
3960
+ });
3961
+ return {
3962
+ hasColumns: s.columns.length > 0,
3963
+ noColumns: s.columns.length === 0,
3964
+ sampleWarning: s.sampleWarning ?? void 0,
3965
+ sampled: !!s.sampleWarning,
3966
+ manualApply,
3967
+ applyDisabled: !s.dirty,
3968
+ columns: s.columns.map((column) => {
3969
+ const stat = showStats ? s.stats[column.id] : void 0;
3970
+ return { id: column.id, name: column.name, role: column.role ?? "dimension", focused: s.focusedColumnId === column.id, stat: stat ? String(stat.distinct ?? 0) : void 0, statTitle: stat ? `${stat.nulls ?? 0} empty` : void 0 };
3971
+ }),
3972
+ shelves,
3973
+ filters,
3974
+ filterCount: s.filters.filters.length,
3975
+ hasFilters: s.filters.filters.length > 0,
3976
+ noFilters: s.filters.filters.length === 0,
3977
+ combinator: s.filters.combinator,
3978
+ countLabel: `${s.columns.length} field${s.columns.length === 1 ? "" : "s"}`,
3979
+ dirtyLabel: s.dirty ? "unapplied" : void 0
3980
+ };
3981
+ }
3982
+ function applyFieldsUi(c, intent) {
3983
+ switch (intent.kind) {
3984
+ case "autoAssign":
3985
+ c.autoAssign();
3986
+ break;
3987
+ case "apply":
3988
+ c.apply(true);
3989
+ break;
3990
+ case "focus":
3991
+ c.focusColumn(intent.columnId);
3992
+ break;
3993
+ case "remove":
3994
+ c.removeFromShelf(intent.shelfId, intent.columnId);
3995
+ break;
3996
+ case "aggregate":
3997
+ c.setAggregate(intent.shelfId, intent.columnId, intent.fn);
3998
+ break;
3999
+ case "direction":
4000
+ c.toggleDirection(intent.shelfId, intent.columnId);
4001
+ break;
4002
+ case "addFilter": {
4003
+ const column = c.getState().columns[0];
4004
+ if (column) c.addFilter([], { columnId: column.id, op: "eq", value: null });
4005
+ break;
4006
+ }
4007
+ case "addGroup":
4008
+ c.addFilterGroup([]);
4009
+ break;
4010
+ case "filterOp":
4011
+ c.updateFilter([], intent.index, { op: intent.op });
4012
+ break;
4013
+ case "removeFilter":
4014
+ c.removeFilter([], intent.index);
4015
+ break;
4016
+ case "combinator":
4017
+ c.setCombinator([], intent.combinator);
4018
+ break;
4019
+ }
4020
+ }
4021
+ var fieldsViewManifest = {
4022
+ id: fieldsManifest.id,
4023
+ version: "1.0.0",
4024
+ title: "Fields",
4025
+ category: "data",
4026
+ layout: { slot: "side" },
4027
+ inputs: [{ id: "view", type: "object", label: "View state", description: "The FieldsViewModel the runtime derives from the controller on every draw." }],
4028
+ outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind, \u2026 } \u2014 applied by the runtime to the controller." }],
4029
+ commands: []
4030
+ };
4031
+ var EMPTY = { hasColumns: false, noColumns: true, sampled: false, manualApply: false, applyDisabled: true, columns: [], shelves: [], filters: [], filterCount: 0, hasFilters: false, noFilters: true, combinator: "and", countLabel: "0 fields" };
4032
+ var BEHAVIOURS = {
4033
+ "data/fieldsPanelView": (inputs) => inputs.view ?? EMPTY
4034
+ };
4035
+ var FIELDS_RUNTIME_UNITS = [];
4036
+ var text = (b) => ({ component: "text", props: { text: b } });
4037
+ var ui = (kind, fields = {}) => ({ emit: "ui", value: { object: { kind: { value: kind }, ...fields } } });
4038
+ var removeButton = (label, on) => ({
4039
+ component: "chrome/IconButton",
4040
+ props: { size: { value: "sm" }, icon: { node: { component: "icons/Close", props: { size: { value: 11 } } } }, label },
4041
+ on: { onClick: on }
4042
+ });
4043
+ var FIELDS_DECLARATION = {
4044
+ format: "xeno.block@1",
4045
+ manifest: fieldsViewManifest,
4046
+ behaviour: "data/fieldsPanelView",
4047
+ composition: {
4048
+ component: "fragment",
4049
+ children: [
4050
+ { component: "chrome/EmptyState", when: { input: "view.noColumns" }, props: { title: { value: "No fields" }, hint: { value: "Connect a source \u2014 its columns appear here, ready to drop onto a shelf." } } },
3916
4051
  {
3917
- title: "No fields",
3918
- hint: "Connect a source \u2014 its columns appear here, ready to drop onto a shelf."
4052
+ component: "chrome/Column",
4053
+ when: { input: "view.hasColumns" },
4054
+ children: [
4055
+ {
4056
+ component: "chrome/Toolbar",
4057
+ props: {
4058
+ left: { node: { component: "chrome/Badge", when: { input: "view.sampled" }, props: { tone: { value: "warning" }, title: { input: "view.sampleWarning" } }, children: [text({ value: "sampled schema" })] } },
4059
+ right: {
4060
+ node: {
4061
+ component: "chrome/ToolbarGroup",
4062
+ props: { end: { value: true } },
4063
+ children: [
4064
+ { component: "chrome/TextButton", on: { onClick: ui("autoAssign") }, children: [text({ value: "Auto-assign" })] },
4065
+ { component: "chrome/TextButton", when: { input: "view.manualApply" }, props: { strong: { value: true }, disabled: { input: "view.applyDisabled" } }, on: { onClick: ui("apply") }, children: [text({ value: "Apply" })] }
4066
+ ]
4067
+ }
4068
+ }
4069
+ }
4070
+ },
4071
+ {
4072
+ component: "chrome/ScrollArea",
4073
+ children: [
4074
+ {
4075
+ component: "chrome/Section",
4076
+ props: { title: { value: "Fields" }, flush: { value: true } },
4077
+ children: [
4078
+ {
4079
+ component: "chrome/RowList",
4080
+ props: { role: { value: "listbox" }, "aria-label": { value: "Fields" } },
4081
+ children: [
4082
+ {
4083
+ component: "chrome/Row",
4084
+ each: { input: "view.columns", key: "id" },
4085
+ props: { label: { item: "name" }, meta: { item: "role" }, selected: { item: "focused" }, trailing: { node: { component: "chrome/Badge", when: { item: "stat" }, props: { title: { item: "statTitle" } }, children: [text({ item: "stat" })] } } },
4086
+ on: { onClick: ui("focus", { columnId: { item: "id" } }) }
4087
+ }
4088
+ ]
4089
+ }
4090
+ ]
4091
+ },
4092
+ {
4093
+ component: "chrome/Section",
4094
+ each: { input: "view.shelves", key: "id" },
4095
+ props: { title: { item: "label" }, flush: { value: true }, badge: { node: { component: "chrome/Badge", when: { item: "hasEntries" }, children: [text({ item: "count" })] } } },
4096
+ children: [
4097
+ { component: "chrome/Note", when: { item: "empty" }, children: [text({ item: "hint" })] },
4098
+ {
4099
+ component: "chrome/RowList",
4100
+ when: { item: "hasEntries" },
4101
+ children: [
4102
+ {
4103
+ component: "chrome/Row",
4104
+ each: { item: "entries", key: "columnId" },
4105
+ props: {
4106
+ label: { item: "label" },
4107
+ dimmed: { item: "suggested" },
4108
+ meta: {
4109
+ node: {
4110
+ component: "fragment",
4111
+ children: [
4112
+ { component: "chrome/Select", when: { item: "aggregated" }, props: { size: { value: "sm" }, label: { value: "Aggregate" }, options: { item: "aggregates" }, value: { item: "fn" } }, on: { onChange: ui("aggregate", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" }, fn: { arg: "" } }) } },
4113
+ { component: "chrome/TextButton", when: { item: "sortable" }, on: { onClick: ui("direction", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" } }) }, children: [text({ item: "direction" })] },
4114
+ { component: "chrome/Badge", when: { item: "granularity" }, children: [text({ item: "granularity" })] }
4115
+ ]
4116
+ }
4117
+ },
4118
+ trailing: { node: removeButton({ item: "removeLabel" }, ui("remove", { shelfId: { item: "shelfId" }, columnId: { item: "columnId" } })) }
4119
+ }
4120
+ }
4121
+ ]
4122
+ }
4123
+ ]
4124
+ },
4125
+ {
4126
+ component: "chrome/Section",
4127
+ props: {
4128
+ title: { value: "Filters" },
4129
+ flush: { value: true },
4130
+ badge: { node: { component: "chrome/Badge", when: { input: "view.hasFilters" }, children: [text({ input: "view.filterCount" })] } },
4131
+ actions: { node: { component: "chrome/IconButton", props: { size: { value: "sm" }, icon: { node: { component: "icons/Plus", props: { size: { value: 11 } } } }, label: { value: "Add filter" } }, on: { onClick: ui("addFilter") } } }
4132
+ },
4133
+ children: [
4134
+ { component: "chrome/Note", when: { input: "view.noFilters" }, children: [text({ value: "No filters. Everything passes." })] },
4135
+ {
4136
+ component: "chrome/RowList",
4137
+ when: { input: "view.hasFilters" },
4138
+ children: [
4139
+ {
4140
+ component: "chrome/Row",
4141
+ each: { input: "view.filters", key: "key" },
4142
+ props: {
4143
+ label: { item: "label" },
4144
+ meta: { node: { component: "chrome/Select", when: { item: "leaf" }, props: { size: { value: "sm" }, label: { value: "Operator" }, options: { item: "ops" }, value: { item: "op" } }, on: { onChange: ui("filterOp", { index: { item: "index" }, op: { arg: "" } }) } } },
4145
+ trailing: { node: removeButton({ item: "removeLabel" }, ui("removeFilter", { index: { item: "index" } })) }
4146
+ }
4147
+ }
4148
+ ]
4149
+ },
4150
+ {
4151
+ component: "chrome/Toolbar",
4152
+ props: {
4153
+ divided: { value: false },
4154
+ left: { node: { component: "chrome/SegmentedControl", props: { size: { value: "sm" }, label: { value: "Combinator" }, options: { value: COMBINATORS }, value: { input: "view.combinator" } }, on: { onChange: ui("combinator", { combinator: { arg: "" } }) } } },
4155
+ right: { node: { component: "chrome/TextButton", on: { onClick: ui("addGroup") }, children: [text({ value: "+ group" })] } }
4156
+ }
4157
+ }
4158
+ ]
4159
+ }
4160
+ ]
4161
+ },
4162
+ { component: "chrome/StatusBar", props: { left: { input: "view.countLabel" }, right: { input: "view.dirtyLabel" } } }
4163
+ ]
3919
4164
  }
3920
- );
4165
+ ]
3921
4166
  }
3922
- return /* @__PURE__ */ jsxs4("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
4167
+ };
4168
+
4169
+ // src/data/fields/react/FieldsPanelView.tsx
4170
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
4171
+ var CloseGlyph = ICONS["icons/Close"];
4172
+ var PlusGlyph = ICONS["icons/Plus"];
4173
+ var COMBINATORS2 = [{ value: "and", label: "All" }, { value: "or", label: "Any" }];
4174
+ function FieldsPanelView({ controller, showStats, manualApply }) {
4175
+ useSyncExternalStore4(controller.subscribe, controller.getState, controller.getState);
4176
+ const view = fieldsViewState(controller, { showStats, manualApply });
4177
+ const ui2 = (intent) => applyFieldsUi(controller, intent);
4178
+ if (view.noColumns) {
4179
+ return /* @__PURE__ */ jsx4(EmptyState4, { title: "No fields", hint: "Connect a source \u2014 its columns appear here, ready to drop onto a shelf." });
4180
+ }
4181
+ return /* @__PURE__ */ jsxs4(Column, { children: [
3923
4182
  /* @__PURE__ */ jsx4(
3924
4183
  Toolbar4,
3925
4184
  {
3926
- left: state.sampleWarning ? (
3927
- // An inferred shape is a GUESS; building a query on a guessed column is how a
3928
- // dashboard breaks next Tuesday.
3929
- // Badge has no title prop, so the tooltip lives on a wrapper.
3930
- /* @__PURE__ */ jsx4("span", { title: state.sampleWarning, children: /* @__PURE__ */ jsx4(Badge4, { tone: "warning", children: "sampled schema" }) })
3931
- ) : void 0,
4185
+ left: view.sampled ? /* @__PURE__ */ jsx4(Badge4, { tone: "warning", title: view.sampleWarning, children: "sampled schema" }) : null,
3932
4186
  right: /* @__PURE__ */ jsxs4(ToolbarGroup3, { end: true, children: [
3933
- /* @__PURE__ */ jsx4(TextButton, { onClick: () => controller.autoAssign(), children: "Auto-assign" }),
3934
- manualApply ? /* @__PURE__ */ jsx4(TextButton, { strong: true, disabled: !state.dirty, onClick: () => controller.apply(true), children: "Apply" }) : null
4187
+ /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui2({ kind: "autoAssign" }), children: "Auto-assign" }),
4188
+ view.manualApply ? /* @__PURE__ */ jsx4(TextButton, { strong: true, disabled: view.applyDisabled, onClick: () => ui2({ kind: "apply" }), children: "Apply" }) : null
3935
4189
  ] })
3936
4190
  }
3937
4191
  ),
3938
4192
  /* @__PURE__ */ jsxs4(ScrollArea3, { children: [
3939
- /* @__PURE__ */ jsx4(Section, { title: "Fields", flush: true, children: /* @__PURE__ */ jsx4(RowList, { children: state.columns.map((column) => /* @__PURE__ */ jsx4(
4193
+ /* @__PURE__ */ jsx4(Section, { title: "Fields", flush: true, children: /* @__PURE__ */ jsx4(RowList, { role: "listbox", "aria-label": "Fields", children: view.columns.map((column) => /* @__PURE__ */ jsx4(
3940
4194
  Row,
3941
4195
  {
3942
4196
  label: column.name,
3943
- meta: column.role ?? "dimension",
3944
- selected: state.focusedColumnId === column.id,
3945
- onClick: () => controller.focusColumn(column.id),
3946
- trailing: showStats && state.stats[column.id] ? /* @__PURE__ */ jsx4("span", { title: `${state.stats[column.id]?.nulls ?? 0} empty`, children: /* @__PURE__ */ jsx4(Badge4, { children: String(state.stats[column.id]?.distinct ?? 0) }) }) : null
4197
+ meta: column.role,
4198
+ selected: column.focused,
4199
+ trailing: column.stat ? /* @__PURE__ */ jsx4(Badge4, { title: column.statTitle, children: column.stat }) : null,
4200
+ onClick: () => ui2({ kind: "focus", columnId: column.id })
3947
4201
  },
3948
4202
  column.id
3949
4203
  )) }) }),
3950
- state.shelves.filter((shelf) => shelf.id !== "filters").map((shelf) => {
3951
- const entries = state.entries[shelf.id] ?? [];
3952
- return /* @__PURE__ */ jsx4(
3953
- Section,
4204
+ view.shelves.map((shelf) => /* @__PURE__ */ jsxs4(Section, { title: shelf.label, flush: true, badge: shelf.hasEntries ? /* @__PURE__ */ jsx4(Badge4, { children: shelf.count }) : null, children: [
4205
+ shelf.empty ? /* @__PURE__ */ jsx4(Note, { children: shelf.hint }) : null,
4206
+ shelf.hasEntries ? /* @__PURE__ */ jsx4(RowList, { children: shelf.entries.map((entry) => /* @__PURE__ */ jsx4(
4207
+ Row,
3954
4208
  {
3955
- title: shelf.label,
3956
- badge: entries.length > 0 ? /* @__PURE__ */ jsx4(Badge4, { children: String(entries.length) }) : void 0,
3957
- flush: true,
3958
- children: entries.length === 0 ? /* @__PURE__ */ jsx4("div", { style: { padding: "4px 8px", fontSize: 10, opacity: 0.5 }, children: shelf.hint ?? `Drop a ${shelf.accepts.join(" or ")} here.` }) : /* @__PURE__ */ jsx4(RowList, { children: entries.map((entry) => {
3959
- const column = state.columns.find((c) => c.id === entry.columnId);
3960
- return /* @__PURE__ */ jsx4(
3961
- Row,
3962
- {
3963
- label: entry.label ?? column?.name ?? entry.columnId,
3964
- style: entry.suggested ? { opacity: 0.6 } : void 0,
3965
- meta: shelf.aggregated ? /* @__PURE__ */ jsx4(
3966
- AggregateSelect,
3967
- {
3968
- controller,
3969
- shelfId: shelf.id,
3970
- column,
3971
- value: entry.fn ?? "count",
3972
- allowed: state.allowedAggregates
3973
- }
3974
- ) : shelf.id === "sorts" ? /* @__PURE__ */ jsx4(
3975
- TextButton,
3976
- {
3977
- onClick: () => controller.toggleDirection(shelf.id, entry.columnId),
3978
- children: entry.direction === "desc" ? "desc" : "asc"
3979
- }
3980
- ) : entry.granularity ? /* @__PURE__ */ jsx4(Badge4, { children: entry.granularity }) : null,
3981
- trailing: /* @__PURE__ */ jsx4(
3982
- IconButton3,
3983
- {
3984
- size: "sm",
3985
- icon: /* @__PURE__ */ jsx4(XGlyph, {}),
3986
- label: `Remove ${column?.name ?? entry.columnId}`,
3987
- onClick: () => controller.removeFromShelf(shelf.id, entry.columnId)
3988
- }
3989
- )
3990
- },
3991
- entry.columnId
3992
- );
3993
- }) })
4209
+ label: entry.label,
4210
+ dimmed: entry.suggested,
4211
+ meta: /* @__PURE__ */ jsxs4(Fragment2, { children: [
4212
+ entry.aggregated ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Aggregate", options: entry.aggregates, value: entry.fn, onChange: (fn) => ui2({ kind: "aggregate", shelfId: entry.shelfId, columnId: entry.columnId, fn }) }) : null,
4213
+ entry.sortable ? /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui2({ kind: "direction", shelfId: entry.shelfId, columnId: entry.columnId }), children: entry.direction }) : null,
4214
+ entry.granularity ? /* @__PURE__ */ jsx4(Badge4, { children: entry.granularity }) : null
4215
+ ] }),
4216
+ trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: entry.removeLabel, onClick: () => ui2({ kind: "remove", shelfId: entry.shelfId, columnId: entry.columnId }) })
3994
4217
  },
3995
- shelf.id
3996
- );
3997
- }),
3998
- /* @__PURE__ */ jsx4(FilterBuilder, { controller })
3999
- ] }),
4000
- /* @__PURE__ */ jsx4(
4001
- StatusBar4,
4002
- {
4003
- left: `${state.columns.length} field${state.columns.length === 1 ? "" : "s"}`,
4004
- right: state.dirty ? "unapplied" : void 0
4005
- }
4006
- )
4007
- ] });
4008
- }
4009
- function AggregateSelect({
4010
- controller,
4011
- shelfId,
4012
- column,
4013
- value,
4014
- allowed
4015
- }) {
4016
- const options = offerableAggregates(column, allowed);
4017
- return /* @__PURE__ */ jsx4(
4018
- "select",
4019
- {
4020
- value,
4021
- onClick: (e) => e.stopPropagation(),
4022
- onChange: (e) => controller.setAggregate(shelfId, column?.id ?? "", e.target.value),
4023
- style: { font: "inherit", fontSize: 9, background: "transparent", color: "inherit", border: "none" },
4024
- children: options.map((fn) => /* @__PURE__ */ jsx4("option", { value: fn, children: fn }, fn))
4025
- }
4026
- );
4027
- }
4028
- function FilterBuilder({ controller }) {
4029
- const state = useSyncExternalStore4(controller.subscribe, controller.getState, controller.getState);
4030
- return /* @__PURE__ */ jsxs4(
4031
- Section,
4032
- {
4033
- title: "Filters",
4034
- badge: state.filters.filters.length > 0 ? /* @__PURE__ */ jsx4(Badge4, { children: String(state.filters.filters.length) }) : void 0,
4035
- actions: /* @__PURE__ */ jsx4(
4036
- IconButton3,
4218
+ entry.columnId
4219
+ )) }) : null
4220
+ ] }, shelf.id)),
4221
+ /* @__PURE__ */ jsxs4(
4222
+ Section,
4037
4223
  {
4038
- size: "sm",
4039
- icon: /* @__PURE__ */ jsx4(PlusGlyph, {}),
4040
- label: "Add filter",
4041
- onClick: () => {
4042
- const column = state.columns[0];
4043
- if (column) controller.addFilter([], { columnId: column.id, op: "eq", value: null });
4044
- }
4045
- }
4046
- ),
4047
- flush: true,
4048
- children: [
4049
- state.filters.filters.length === 0 ? /* @__PURE__ */ jsx4("div", { style: { padding: "4px 8px", fontSize: 10, opacity: 0.5 }, children: "No filters. Everything passes." }) : /* @__PURE__ */ jsx4(RowList, { children: state.filters.filters.map((member, index) => {
4050
- if (!isLeafFilter(member)) {
4051
- return /* @__PURE__ */ jsx4(
4224
+ title: "Filters",
4225
+ flush: true,
4226
+ badge: view.hasFilters ? /* @__PURE__ */ jsx4(Badge4, { children: view.filterCount }) : null,
4227
+ actions: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(PlusGlyph, { size: 11 }), label: "Add filter", onClick: () => ui2({ kind: "addFilter" }) }),
4228
+ children: [
4229
+ view.noFilters ? /* @__PURE__ */ jsx4(Note, { children: "No filters. Everything passes." }) : null,
4230
+ view.hasFilters ? /* @__PURE__ */ jsx4(RowList, { children: view.filters.map((filter) => /* @__PURE__ */ jsx4(
4052
4231
  Row,
4053
4232
  {
4054
- label: `(${member.combinator.toUpperCase()} group \u2014 ${member.filters.length})`,
4055
- trailing: /* @__PURE__ */ jsx4(
4056
- IconButton3,
4057
- {
4058
- size: "sm",
4059
- icon: /* @__PURE__ */ jsx4(XGlyph, {}),
4060
- label: "Remove group",
4061
- onClick: () => controller.removeFilter([], index)
4062
- }
4063
- )
4233
+ label: filter.label,
4234
+ meta: filter.leaf ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Operator", options: filter.ops, value: filter.op, onChange: (op) => ui2({ kind: "filterOp", index: filter.index, op }) }) : null,
4235
+ trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: filter.removeLabel, onClick: () => ui2({ kind: "removeFilter", index: filter.index }) })
4064
4236
  },
4065
- `g${index}`
4066
- );
4067
- }
4068
- const column = state.columns.find((c) => c.id === member.columnId);
4069
- return /* @__PURE__ */ jsx4(
4070
- Row,
4071
- {
4072
- label: column?.name ?? member.columnId,
4073
- meta: /* @__PURE__ */ jsx4(
4074
- "select",
4075
- {
4076
- value: member.op,
4077
- onClick: (e) => e.stopPropagation(),
4078
- onChange: (e) => controller.updateFilter([], index, { op: e.target.value }),
4079
- style: {
4080
- font: "inherit",
4081
- fontSize: 9,
4082
- background: "transparent",
4083
- color: "inherit",
4084
- border: "none"
4085
- },
4086
- children: offerableFilterOps(column, state.allowedFilterOps).map((op) => /* @__PURE__ */ jsx4("option", { value: op, children: op }, op))
4087
- }
4088
- ),
4089
- trailing: /* @__PURE__ */ jsx4(
4090
- IconButton3,
4091
- {
4092
- size: "sm",
4093
- icon: /* @__PURE__ */ jsx4(XGlyph, {}),
4094
- label: "Remove filter",
4095
- onClick: () => controller.removeFilter([], index)
4096
- }
4097
- )
4098
- },
4099
- `f${index}`
4100
- );
4101
- }) }),
4102
- /* @__PURE__ */ jsxs4("div", { style: { padding: "2px 8px", display: "flex", gap: 8 }, children: [
4103
- /* @__PURE__ */ jsx4(
4104
- SegmentedControl4,
4105
- {
4106
- size: "sm",
4107
- label: "Combinator",
4108
- options: [
4109
- { value: "and", label: "All" },
4110
- { value: "or", label: "Any" }
4111
- ],
4112
- value: state.filters.combinator,
4113
- onChange: (c) => controller.setCombinator([], c)
4114
- }
4115
- ),
4116
- /* @__PURE__ */ jsx4(TextButton, { onClick: () => controller.addFilterGroup([]), children: "+ group" })
4117
- ] })
4118
- ]
4119
- }
4120
- );
4237
+ filter.key
4238
+ )) }) : null,
4239
+ /* @__PURE__ */ jsx4(
4240
+ Toolbar4,
4241
+ {
4242
+ divided: false,
4243
+ left: /* @__PURE__ */ jsx4(SegmentedControl4, { size: "sm", label: "Combinator", options: COMBINATORS2, value: view.combinator, onChange: (combinator) => ui2({ kind: "combinator", combinator }) }),
4244
+ right: /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui2({ kind: "addGroup" }), children: "+ group" })
4245
+ }
4246
+ )
4247
+ ]
4248
+ }
4249
+ )
4250
+ ] }),
4251
+ /* @__PURE__ */ jsx4(StatusBar4, { left: view.countLabel, right: view.dirtyLabel })
4252
+ ] });
4121
4253
  }
4122
4254
 
4123
4255
  // src/data/fields/panel.ts
@@ -5245,10 +5377,10 @@ function parseJsonPath(path) {
5245
5377
  }
5246
5378
  return steps;
5247
5379
  }
5248
- function findBracketEnd(text, start) {
5380
+ function findBracketEnd(text2, start) {
5249
5381
  let quote = null;
5250
- for (let i = start + 1; i < text.length; i += 1) {
5251
- const char = text[i];
5382
+ for (let i = start + 1; i < text2.length; i += 1) {
5383
+ const char = text2[i];
5252
5384
  if (quote) {
5253
5385
  if (char === "\\") i += 1;
5254
5386
  else if (char === quote) quote = null;
@@ -5672,9 +5804,9 @@ function firstGraphqlError(errors) {
5672
5804
  }
5673
5805
 
5674
5806
  // src/data/connectors/executors/csv.ts
5675
- function sniffDelimiter(text) {
5807
+ function sniffDelimiter(text2) {
5676
5808
  const candidates = [",", ";", " ", "|"];
5677
- const line = firstLogicalLine(text);
5809
+ const line = firstLogicalLine(text2);
5678
5810
  let best = ",";
5679
5811
  let bestCount = 0;
5680
5812
  for (const candidate of candidates) {
@@ -5696,21 +5828,21 @@ function sniffDelimiter(text) {
5696
5828
  }
5697
5829
  return best;
5698
5830
  }
5699
- function firstLogicalLine(text) {
5831
+ function firstLogicalLine(text2) {
5700
5832
  let inQuotes = false;
5701
- for (let i = 0; i < text.length; i += 1) {
5702
- const char = text[i];
5833
+ for (let i = 0; i < text2.length; i += 1) {
5834
+ const char = text2[i];
5703
5835
  if (char === '"') {
5704
- if (inQuotes && text[i + 1] === '"') i += 1;
5836
+ if (inQuotes && text2[i + 1] === '"') i += 1;
5705
5837
  else inQuotes = !inQuotes;
5706
5838
  } else if ((char === "\n" || char === "\r") && !inQuotes) {
5707
- return text.slice(0, i);
5839
+ return text2.slice(0, i);
5708
5840
  }
5709
5841
  }
5710
- return text;
5842
+ return text2;
5711
5843
  }
5712
- function parseCsv(text, options = {}) {
5713
- const source = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
5844
+ function parseCsv(text2, options = {}) {
5845
+ const source = text2.charCodeAt(0) === 65279 ? text2.slice(1) : text2;
5714
5846
  const delimiter = options.delimiter && options.delimiter.length > 0 ? options.delimiter : sniffDelimiter(source);
5715
5847
  const quote = options.quote && options.quote.length > 0 ? options.quote : '"';
5716
5848
  const hasHeader = options.hasHeader ?? true;
@@ -5745,17 +5877,17 @@ function dedupe(names) {
5745
5877
  return count === 0 ? name : `${name}_${count + 1}`;
5746
5878
  });
5747
5879
  }
5748
- function scanRecords(text, delimiter, quote) {
5880
+ function scanRecords(text2, delimiter, quote) {
5749
5881
  const records = [];
5750
5882
  let fields = [];
5751
5883
  let field = "";
5752
5884
  let inQuotes = false;
5753
5885
  let i = 0;
5754
- while (i < text.length) {
5755
- const char = text[i];
5886
+ while (i < text2.length) {
5887
+ const char = text2[i];
5756
5888
  if (inQuotes) {
5757
5889
  if (char === quote) {
5758
- if (text[i + 1] === quote) {
5890
+ if (text2[i + 1] === quote) {
5759
5891
  field += quote;
5760
5892
  i += 2;
5761
5893
  continue;
@@ -5773,7 +5905,7 @@ function scanRecords(text, delimiter, quote) {
5773
5905
  i += 1;
5774
5906
  continue;
5775
5907
  }
5776
- if (text.startsWith(delimiter, i)) {
5908
+ if (text2.startsWith(delimiter, i)) {
5777
5909
  fields.push(field);
5778
5910
  field = "";
5779
5911
  i += delimiter.length;
@@ -5784,7 +5916,7 @@ function scanRecords(text, delimiter, quote) {
5784
5916
  records.push(fields);
5785
5917
  fields = [];
5786
5918
  field = "";
5787
- i += char === "\r" && text[i + 1] === "\n" ? 2 : 1;
5919
+ i += char === "\r" && text2[i + 1] === "\n" ? 2 : 1;
5788
5920
  continue;
5789
5921
  }
5790
5922
  field += char;
@@ -6935,7 +7067,10 @@ export {
6935
7067
  DEFAULT_RANGES,
6936
7068
  EMPTY_LAYOUT,
6937
7069
  EXPIRY_SKEW_MS,
7070
+ BEHAVIOURS as FIELDS_BEHAVIOURS,
7071
+ FIELDS_DECLARATION,
6938
7072
  FIELDS_PANEL_ID,
7073
+ FIELDS_RUNTIME_UNITS,
6939
7074
  FieldsController,
6940
7075
  FieldsPanelView,
6941
7076
  IMPLEMENTED_VIEW_MODES,
@@ -6950,6 +7085,7 @@ export {
6950
7085
  TABLE_PANEL_ID,
6951
7086
  TableController,
6952
7087
  TablePanelView,
7088
+ applyFieldsUi,
6953
7089
  applyPrecision,
6954
7090
  base64Url,
6955
7091
  breachSideOf,
@@ -7001,6 +7137,8 @@ export {
7001
7137
  facilitiesOf,
7002
7138
  fieldsManifest,
7003
7139
  fieldsPanel,
7140
+ fieldsViewManifest,
7141
+ fieldsViewState,
7004
7142
  firstGraphqlError,
7005
7143
  frozenWidth,
7006
7144
  getChartRenderer,
@@ -18,7 +18,7 @@ import {
18
18
  VisibilityIcon,
19
19
  XIcon,
20
20
  XIcon2
21
- } from "../chunk-6HJROQCN.js";
21
+ } from "../chunk-BNALWAAQ.js";
22
22
  import "../chunk-GTN5EDY3.js";
23
23
  import {
24
24
  ChevronGlyph,
@@ -3517,7 +3517,7 @@ function ListWidget({ field, controller }) {
3517
3517
  editable ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
3518
3518
  /* @__PURE__ */ jsx3(IconButton2, { size: "sm", icon: /* @__PURE__ */ jsx3("span", { "aria-hidden": true, children: "\u2191" }), label: "Move up", disabled: i === 0, onClick: () => move(i, i - 1) }),
3519
3519
  /* @__PURE__ */ jsx3(IconButton2, { size: "sm", icon: /* @__PURE__ */ jsx3("span", { "aria-hidden": true, children: "\u2193" }), label: "Move down", disabled: i === items.length - 1, onClick: () => move(i, i + 1) }),
3520
- /* @__PURE__ */ jsx3(IconButton2, { size: "sm", icon: /* @__PURE__ */ jsx3(XIcon, {}), label: "Remove", onClick: () => write(items.filter((_, j) => j !== i)) })
3520
+ /* @__PURE__ */ jsx3(IconButton2, { size: "sm", icon: /* @__PURE__ */ jsx3(XIcon2, {}), label: "Remove", onClick: () => write(items.filter((_, j) => j !== i)) })
3521
3521
  ] }) : null
3522
3522
  ] }, i)),
3523
3523
  editable ? /* @__PURE__ */ jsx3(TextButton, { onClick: () => write([...items, ""]), children: "+ Add" }) : null
@@ -4943,7 +4943,7 @@ function HistoryCancelButton({ entryId, controller }) {
4943
4943
  IconButton4,
4944
4944
  {
4945
4945
  size: "sm",
4946
- icon: /* @__PURE__ */ jsx5(XIcon2, {}),
4946
+ icon: /* @__PURE__ */ jsx5(XIcon, {}),
4947
4947
  label: "Cancel operation",
4948
4948
  pointerOnly: true,
4949
4949
  onClick: (e) => {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ICONS,
3
3
  ICON_UNITS
4
- } from "../chunk-6HJROQCN.js";
4
+ } from "../chunk-BNALWAAQ.js";
5
5
  import "../chunk-GTN5EDY3.js";
6
6
  import {
7
7
  Svg
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenosystem/blocks",
3
- "version": "0.13.7",
3
+ "version": "0.13.8",
4
4
  "description": "XENO Blocks — rung 3 of the front ladder: components composed into a contract-bearing unit (manifest: ports, intents, commands, agent surface), rendered by @xenosystem/workbench. ONE package, one subpath per family (/auth, /edit, /data, /time, /ops, /trust, /agent), one named export per block — XENO PACKAGE NAMING - STANDARD.md.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -64,7 +64,7 @@
64
64
  "peerDependencies": {
65
65
  "@duckdb/duckdb-wasm": ">=1.28.0",
66
66
  "@xenosystem/block-sdk": ">=0.3.4 <1",
67
- "@xenosystem/components": ">=0.19.0 <1",
67
+ "@xenosystem/components": ">=0.19.1 <1",
68
68
  "@xenosystem/data-core": "^0.1.0",
69
69
  "@xenosystem/elements": ">=0.0.1",
70
70
  "@xenosystem/elements-react": ">=0.0.1",
@@ -81,7 +81,7 @@
81
81
  "@types/react": "^19.0.0",
82
82
  "@types/react-dom": "^19.0.0",
83
83
  "@xenosystem/block-sdk": "^0.3.4",
84
- "@xenosystem/components": "^0.19.0",
84
+ "@xenosystem/components": "^0.19.1",
85
85
  "@xenosystem/data-core": "^0.1.0",
86
86
  "@xenosystem/elements": "0.0.1",
87
87
  "@xenosystem/elements-react": "0.0.1",