@xenosystem/blocks 0.13.13 → 0.13.14

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.
@@ -470,7 +470,6 @@ export {
470
470
  Svg,
471
471
  RefreshGlyph,
472
472
  ChevronGlyph,
473
- PlugGlyph,
474
473
  SortAscGlyph,
475
474
  SortDescGlyph,
476
475
  ExpandGlyph,
@@ -1151,7 +1151,7 @@ interface ChartViewOptions {
1151
1151
  declare function chartViewState(c: ChartController, options?: ChartViewOptions): ChartViewModel;
1152
1152
  declare function applyChartUi(c: ChartController, intent: ChartUiIntent): void;
1153
1153
  declare const chartViewManifest: PanelManifest;
1154
- declare const BEHAVIOURS$2: {
1154
+ declare const BEHAVIOURS$3: {
1155
1155
  'data/chartPanelView': (inputs: Record<string, unknown>) => ChartViewModel;
1156
1156
  };
1157
1157
  /** The runtime-bound unit: the plot surface the host renderer draws into. */
@@ -1519,7 +1519,7 @@ interface MetricsViewOptions {
1519
1519
  declare function metricsViewState(c: MetricsController, options?: MetricsViewOptions): MetricsViewModel;
1520
1520
  declare function applyMetricsUi(c: MetricsController, intent: MetricsUiIntent): void;
1521
1521
  declare const metricsViewManifest: PanelManifest;
1522
- declare const BEHAVIOURS$1: {
1522
+ declare const BEHAVIOURS$2: {
1523
1523
  'data/metricsPanelView': (inputs: Record<string, unknown>) => MetricsViewModel;
1524
1524
  };
1525
1525
  declare const METRICS_RUNTIME_UNITS: readonly [];
@@ -2005,7 +2005,7 @@ declare function fieldsViewState(c: FieldsController, options?: FieldsViewOption
2005
2005
  /** Apply a `ui` intent to the controller. */
2006
2006
  declare function applyFieldsUi(c: FieldsController, intent: FieldsUiIntent): void;
2007
2007
  declare const fieldsViewManifest: PanelManifest;
2008
- declare const BEHAVIOURS: {
2008
+ declare const BEHAVIOURS$1: {
2009
2009
  'data/fieldsPanelView': (inputs: Record<string, unknown>) => FieldsViewModel;
2010
2010
  };
2011
2011
  /** No runtime-bound units: every intent is a `ui` emit. */
@@ -2085,6 +2085,8 @@ declare class ConnectorsController {
2085
2085
  private lastQueries;
2086
2086
  private selectedId;
2087
2087
  private form;
2088
+ /** The form's draft values. In the controller, not React state, so the declaration can bind them — and cleared with the form. */
2089
+ private draft;
2088
2090
  /** Newest outstanding request per `(connectionId, requestorPanelId)`. */
2089
2091
  private readonly outstanding;
2090
2092
  /** In-flight executions, keyed by cache key — the single-flight map. */
@@ -2151,6 +2153,10 @@ declare class ConnectorsController {
2151
2153
  select(id: string | null): void;
2152
2154
  /** The host asks for a credential. The panel renders the descriptor; it knows no platform names. */
2153
2155
  requireCredential(spec: XenoCredentialFormSpec): void;
2156
+ /** The draft as typed so far. Never emitted except through {@link submitCredential}. */
2157
+ getCredentialDraft(): Record<string, string | number | boolean>;
2158
+ /** One field of the draft changed. */
2159
+ setCredentialDraft(key: string, value: string | number | boolean): void;
2154
2160
  /**
2155
2161
  * Submit a credential form.
2156
2162
  *
@@ -3475,30 +3481,109 @@ interface RefreshCoordinator<T> {
3475
3481
  declare function createRefreshCoordinator<T>(): RefreshCoordinator<T>;
3476
3482
 
3477
3483
  /**
3478
- * The Connectors panel view — connection list, health, and the credential form.
3484
+ * The Connectors panel AS A DECLARATION — the sixteenth declared block, and the first with a FORM
3485
+ * branch: the list of data sources, or — when the host asks for a credential — the credential form.
3479
3486
  *
3480
- * Composes `@xenosystem/components/chrome`. **The host must import
3481
- * `@xenosystem/workbench/primitives.css`** (or let the workbench inject it) or this renders unstyled with
3482
- * no error.
3483
- *
3484
- * Two things this view deliberately does NOT have:
3487
+ * Everything drawn is data: the list (a toolbar with Refresh all, a listbox of sources with their
3488
+ * kind and a health badge, the empty state, the status bar) and the form (the title, the host's
3489
+ * message, the redirect URI VERBATIM as a selectable `Code` block — rendering the real string is
3490
+ * what prevents a redirect-URI mismatch — one `Field` per visible field with the right control:
3491
+ * `Input` for text / password / number, `Select`, `Toggle`; the validation error as the hint; the
3492
+ * docs `Link`; Cancel / Submit) or the form's "cannot be set up" state. The draft lives in the
3493
+ * controller and is cleared with the form. No runtime-bound unit.
3494
+ */
3495
+
3496
+ type Tone = 'neutral' | 'info' | 'success' | 'warning' | 'error';
3497
+ interface ConnectorsViewSource {
3498
+ id: string;
3499
+ name: string;
3500
+ kind: string;
3501
+ selected: boolean;
3502
+ disabled: boolean;
3503
+ title?: string;
3504
+ health: string;
3505
+ tone: Tone;
3506
+ }
3507
+ interface ConnectorsViewField {
3508
+ key: string;
3509
+ label: string;
3510
+ hint?: string;
3511
+ kind: 'text' | 'password' | 'number' | 'select' | 'boolean' | 'textarea';
3512
+ isText: boolean;
3513
+ isSelect: boolean;
3514
+ isBoolean: boolean;
3515
+ inputType: 'text' | 'password' | 'number';
3516
+ text: string;
3517
+ checked: boolean;
3518
+ placeholder?: string;
3519
+ options: {
3520
+ value: string;
3521
+ label: string;
3522
+ }[];
3523
+ }
3524
+ interface ConnectorsViewModel {
3525
+ listing: boolean;
3526
+ sources: ConnectorsViewSource[];
3527
+ empty: boolean;
3528
+ emptyHint: string;
3529
+ listed: boolean;
3530
+ countLabel: string;
3531
+ busyLabel?: string;
3532
+ form: boolean;
3533
+ formTitle: string;
3534
+ formBroken: boolean;
3535
+ formProblem?: string;
3536
+ formReady: boolean;
3537
+ formMessage?: string;
3538
+ redirectUri?: string;
3539
+ fields: ConnectorsViewField[];
3540
+ docsUrl?: string;
3541
+ submitDisabled: boolean;
3542
+ }
3543
+ type ConnectorsUiIntent = {
3544
+ kind: 'refreshAll';
3545
+ } | {
3546
+ kind: 'select';
3547
+ id: string;
3548
+ } | {
3549
+ kind: 'field';
3550
+ key: string;
3551
+ value: string | number | boolean;
3552
+ } | {
3553
+ kind: 'cancel';
3554
+ } | {
3555
+ kind: 'submit';
3556
+ };
3557
+ interface ConnectorsViewOptions {
3558
+ emptyHint?: string;
3559
+ }
3560
+ /** Derive the view from the live controller — the runtime calls this on every draw. */
3561
+ declare function connectorsViewState(c: ConnectorsController, options?: ConnectorsViewOptions): ConnectorsViewModel;
3562
+ declare function applyConnectorsUi(c: ConnectorsController, intent: ConnectorsUiIntent): void;
3563
+ declare const connectorsViewManifest: PanelManifest;
3564
+ declare const BEHAVIOURS: {
3565
+ 'data/connectorsPanelView': (inputs: Record<string, unknown>) => ConnectorsViewModel;
3566
+ };
3567
+ declare const CONNECTORS_RUNTIME_UNITS: readonly [];
3568
+ declare const CONNECTORS_DECLARATION: BlockDeclaration;
3569
+
3570
+ /**
3571
+ * The Connectors panel view — the CODE VIEW of `CONNECTORS_DECLARATION`, drawing the same
3572
+ * `ConnectorsViewModel` byte-identical (the equivalence suite holds the two together). No
3573
+ * runtime-bound unit; every intent goes through `applyConnectorsUi`. The credential draft lives in
3574
+ * the controller and is cleared with the form — the values never sit in React state.
3485
3575
  *
3486
- * - **No secret reveal.** `xeno-hub`'s `EnvironmentSecretsSection` exposes stored secrets behind an
3487
- * eye-toggle; that affordance does not come with this panel. The panel never receives a stored
3488
- * secret, so there is nothing to reveal.
3489
- * - **No masked value round-trip.** A form field is uncontrolled-by-design local state that is
3490
- * cleared at submit; nothing here reads a value back from a saved document.
3576
+ * Composes `@xenosystem/components/chrome`; the host must ensure `@xenosystem/workbench/primitives.css` is
3577
+ * present.
3491
3578
  *
3492
3579
  * @module
3493
3580
  */
3494
3581
 
3495
3582
  /** Props for {@link ConnectorsPanelView}. */
3496
- interface ConnectorsPanelViewProps {
3583
+ interface ConnectorsPanelViewProps extends ConnectorsViewOptions {
3497
3584
  controller: ConnectorsController;
3498
- /** Hint shown when no source is configured. */
3499
- emptyHint?: string;
3500
3585
  }
3501
3586
  /** The Connectors panel view. */
3502
- declare function ConnectorsPanelView({ controller, emptyHint, }: ConnectorsPanelViewProps): ReactNode;
3587
+ declare function ConnectorsPanelView({ controller, emptyHint }: ConnectorsPanelViewProps): ReactNode;
3503
3588
 
3504
- export { AGENT_HIDDEN_COMMANDS, ALL_AGGREGATES, ALL_CHART_TYPES, type BuildChartOptions, type BuildSelectOptions, type BuildSelectResult, BEHAVIOURS$2 as CHART_BEHAVIOURS, CHART_DECLARATION, CHART_EMPTY_FILL, CHART_PALETTE_STEPS, CHART_PANEL_ID, CHART_RUNTIME_UNITS, CHART_TYPE_LABELS, CONNECTORS_PANEL_ID, type CellAddress, ChartController, type ChartControllerOptions, type ChartHostBridge, type ChartPanelState, ChartPanelView, type ChartPanelViewProps, ChartPlot, type ChartRenderContext, type ChartRenderInput, type ChartRenderOptions, type ChartRenderer, type ChartUiIntent, type ChartViewModel, type ChartViewOptions, 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_CHART_OPTIONS, 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, BEHAVIOURS$1 as METRICS_BEHAVIOURS, METRICS_DECLARATION, METRICS_PANEL_ID, METRICS_RUNTIME_UNITS, MIN_COLUMN_WIDTH, MONO_FILL_PALETTE, MONO_PALETTE, MetricsController, type MetricsControllerOptions, type MetricsHostBridge, type MetricsPanelState, MetricsPanelView, type MetricsPanelViewProps, type MetricsRenderContext, type MetricsUiIntent, type MetricsViewModel, type MetricsViewOptions, type MetricsViewState, type MetricsViewTile, 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, applyChartUi, applyFieldsUi, applyMetricsUi, applyPrecision, base64Url, breachSideOf, buildChartData, buildRestUrl, buildSelect, chartManifest, chartPanel, chartRuntimeComponents, chartViewManifest, chartViewState, 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, metricsViewManifest, metricsViewState, 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 };
3589
+ export { AGENT_HIDDEN_COMMANDS, ALL_AGGREGATES, ALL_CHART_TYPES, type BuildChartOptions, type BuildSelectOptions, type BuildSelectResult, BEHAVIOURS$3 as CHART_BEHAVIOURS, CHART_DECLARATION, CHART_EMPTY_FILL, CHART_PALETTE_STEPS, CHART_PANEL_ID, CHART_RUNTIME_UNITS, CHART_TYPE_LABELS, BEHAVIOURS as CONNECTORS_BEHAVIOURS, CONNECTORS_DECLARATION, CONNECTORS_PANEL_ID, CONNECTORS_RUNTIME_UNITS, type CellAddress, ChartController, type ChartControllerOptions, type ChartHostBridge, type ChartPanelState, ChartPanelView, type ChartPanelViewProps, ChartPlot, type ChartRenderContext, type ChartRenderInput, type ChartRenderOptions, type ChartRenderer, type ChartUiIntent, type ChartViewModel, type ChartViewOptions, type ChartViewState, type ColumnLayout, type ColumnWindow, ConnectorExecuteInput, ConnectorExecuteOutput, ConnectorTransport, ConnectorsController, type ConnectorsControllerOptions, type ConnectorsHostBridge, ConnectorsPanelState, ConnectorsPanelView, type ConnectorsPanelViewProps, type ConnectorsRenderContext, type ConnectorsUiIntent, type ConnectorsViewField, type ConnectorsViewModel, type ConnectorsViewOptions, type ConnectorsViewSource, ConnectorsViewState, type CreateChartPanelOptions, type CreateConnectorExecutorOptions, type CreateConnectorsPanelOptions, type CreateFieldsPanelOptions, type CreateMetricsPanelOptions, type CreateTablePanelOptions, type CredentialDraft, type CredentialErrors, DEFAULT_CHART_OPTIONS, DEFAULT_COLUMN_WIDTH, DEFAULT_PAGE_SIZE, DEFAULT_RANGES, type DeltaDirection, EMPTY_LAYOUT, EXPIRY_SKEW_MS, type ExecuteError, BEHAVIOURS$1 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, BEHAVIOURS$2 as METRICS_BEHAVIOURS, METRICS_DECLARATION, METRICS_PANEL_ID, METRICS_RUNTIME_UNITS, MIN_COLUMN_WIDTH, MONO_FILL_PALETTE, MONO_PALETTE, MetricsController, type MetricsControllerOptions, type MetricsHostBridge, type MetricsPanelState, MetricsPanelView, type MetricsPanelViewProps, type MetricsRenderContext, type MetricsUiIntent, type MetricsViewModel, type MetricsViewOptions, type MetricsViewState, type MetricsViewTile, 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, applyChartUi, applyConnectorsUi, applyFieldsUi, applyMetricsUi, applyPrecision, base64Url, breachSideOf, buildChartData, buildRestUrl, buildSelect, chartManifest, chartPanel, chartRuntimeComponents, chartViewManifest, chartViewState, clamp, classifyHttpStatus, coerceCsvValues, columnIndexAt, columnsOf, compileForHost, computeColumnWindow, computeWindow, connectorsManifest, connectorsPanel, connectorsViewManifest, connectorsViewState, 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, metricsViewManifest, metricsViewState, 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 };
@@ -9,11 +9,10 @@ import {
9
9
  import {
10
10
  ChevronGlyph,
11
11
  ICONS,
12
- PlugGlyph,
13
12
  RefreshGlyph,
14
13
  SortAscGlyph,
15
14
  SortDescGlyph
16
- } from "../chunk-6XVFYPVH.js";
15
+ } from "../chunk-PI5WOATA.js";
17
16
  import "../chunk-2KG3PWR4.js";
18
17
 
19
18
  // src/data/table/panel.ts
@@ -195,8 +194,8 @@ function moveCell(from, delta, bounds) {
195
194
  }
196
195
 
197
196
  // src/data/table/numeric.ts
198
- function parseNumeric(text4) {
199
- const trimmed = String(text4).trim();
197
+ function parseNumeric(text5) {
198
+ const trimmed = String(text5).trim();
200
199
  if (trimmed.length === 0) return { ok: false, reason: "empty" };
201
200
  const match = /^[+-]?(\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/.exec(trimmed);
202
201
  if (!match) return { ok: false, reason: "nan" };
@@ -550,11 +549,11 @@ var TableController = class {
550
549
  return true;
551
550
  }
552
551
  /** Update the in-flight draft. Emits `editPreview` only when `livePreview` is on. */
553
- updateDraft(text4) {
552
+ updateDraft(text5) {
554
553
  if (!this.editing) return;
555
- this.editing = { ...this.editing, draft: text4 };
554
+ this.editing = { ...this.editing, draft: text5 };
556
555
  if (this.livePreview) {
557
- const parsed = this.parseDraft(this.editing.columnId, text4);
556
+ const parsed = this.parseDraft(this.editing.columnId, text5);
558
557
  if (parsed.ok) {
559
558
  this.host.emit("editPreview", this.buildEdit(this.editing, parsed.value));
560
559
  }
@@ -606,12 +605,12 @@ var TableController = class {
606
605
  };
607
606
  }
608
607
  /** Parse a draft according to the column's declared type. */
609
- parseDraft(columnId, text4) {
608
+ parseDraft(columnId, text5) {
610
609
  const column = this.columnById(columnId);
611
- const trimmed = text4.trim();
610
+ const trimmed = text5.trim();
612
611
  if (column?.type === "number" || column?.type === "integer" || column?.type === "duration") {
613
612
  if (trimmed === "") return { ok: true, value: null };
614
- const parsed = parseNumeric(text4);
613
+ const parsed = parseNumeric(text5);
615
614
  if (!parsed.ok) return { ok: false, reason: parsed.reason };
616
615
  return { ok: true, value: normalizeNumeric(parsed.value, constraintsOfColumn(column)) };
617
616
  }
@@ -628,7 +627,7 @@ var TableController = class {
628
627
  value: trimmed === "" ? [] : trimmed.split(",").map((part) => part.trim()).filter(Boolean)
629
628
  };
630
629
  }
631
- return { ok: true, value: trimmed === "" ? null : text4 };
630
+ return { ok: true, value: trimmed === "" ? null : text5 };
632
631
  }
633
632
  rowById(rowId) {
634
633
  return this.result?.rows.find((r) => r.id === rowId);
@@ -2375,16 +2374,16 @@ function chartRuntimeComponents(controller, options) {
2375
2374
  function ChartPanelView({ controller, options, emptyHint }) {
2376
2375
  useSyncExternalStore2(controller.subscribe, controller.getState, controller.getState);
2377
2376
  const view = chartViewState(controller, { emptyHint });
2378
- const ui4 = (intent) => applyChartUi(controller, intent);
2377
+ const ui5 = (intent) => applyChartUi(controller, intent);
2379
2378
  return /* @__PURE__ */ jsxs2(Column, { children: [
2380
2379
  /* @__PURE__ */ jsx2(
2381
2380
  Toolbar2,
2382
2381
  {
2383
- left: /* @__PURE__ */ jsx2(SegmentedControl2, { size: "sm", label: "Chart type", options: view.types, value: view.type, onChange: (type) => ui4({ kind: "type", type }) }),
2382
+ left: /* @__PURE__ */ jsx2(SegmentedControl2, { size: "sm", label: "Chart type", options: view.types, value: view.type, onChange: (type) => ui5({ kind: "type", type }) }),
2384
2383
  right: /* @__PURE__ */ jsxs2(ToolbarGroup2, { end: true, children: [
2385
2384
  view.partial ? /* @__PURE__ */ jsx2(Badge2, { tone: "warning", title: "Some measures have not synced yet.", children: "partial" }) : null,
2386
2385
  view.aggregating ? /* @__PURE__ */ jsx2(Badge2, { tone: "warning", title: view.aggregatingHint, children: "aggregating" }) : null,
2387
- /* @__PURE__ */ jsx2(IconButton2, { icon: /* @__PURE__ */ jsx2(DownloadGlyph, {}), label: "Export chart", onClick: () => ui4({ kind: "export" }) })
2386
+ /* @__PURE__ */ jsx2(IconButton2, { icon: /* @__PURE__ */ jsx2(DownloadGlyph, {}), label: "Export chart", onClick: () => ui5({ kind: "export" }) })
2388
2387
  ] })
2389
2388
  }
2390
2389
  ),
@@ -3088,12 +3087,12 @@ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
3088
3087
  function MetricsPanelView({ controller, ...options }) {
3089
3088
  useSyncExternalStore3(controller.subscribe, controller.getState, controller.getState);
3090
3089
  const view = metricsViewState(controller, options);
3091
- const ui4 = (intent) => applyMetricsUi(controller, intent);
3090
+ const ui5 = (intent) => applyMetricsUi(controller, intent);
3092
3091
  return /* @__PURE__ */ jsxs3(Column2, { children: [
3093
3092
  view.hasRanges ? /* @__PURE__ */ jsx3(
3094
3093
  Toolbar3,
3095
3094
  {
3096
- left: /* @__PURE__ */ jsx3(SegmentedControl3, { size: "sm", label: "Range", options: view.ranges, value: view.rangeId, onChange: (rangeId) => ui4({ kind: "range", rangeId }) }),
3095
+ left: /* @__PURE__ */ jsx3(SegmentedControl3, { size: "sm", label: "Range", options: view.ranges, value: view.rangeId, onChange: (rangeId) => ui5({ kind: "range", rangeId }) }),
3097
3096
  right: view.partial ? /* @__PURE__ */ jsx3(Badge3, { tone: "warning", title: view.partialHint, children: "partial" }) : null
3098
3097
  }
3099
3098
  ) : null,
@@ -3111,7 +3110,7 @@ function MetricsPanelView({ controller, ...options }) {
3111
3110
  tile.partial ? /* @__PURE__ */ jsx3(Text, { role: "meta", children: view.partialHint }) : null,
3112
3111
  tile.hasSpark ? /* @__PURE__ */ jsx3(Sparkline, { data: tile.spark, label: tile.sparkLabel }) : null
3113
3112
  ] }),
3114
- onActivate: () => ui4({ kind: "activate", metricId: tile.id })
3113
+ onActivate: () => ui5({ kind: "activate", metricId: tile.id })
3115
3114
  },
3116
3115
  tile.id
3117
3116
  )) }) }) : null,
@@ -3313,7 +3312,7 @@ function emptyFilterGroup() {
3313
3312
  function offerableFilterOps(column, sourceOps = []) {
3314
3313
  const common = ["eq", "ne", "isEmpty", "isNotEmpty", "in", "notIn"];
3315
3314
  const ordered = ["gt", "gte", "lt", "lte", "between"];
3316
- const text4 = ["contains", "notContains", "startsWith", "endsWith"];
3315
+ const text5 = ["contains", "notContains", "startsWith", "endsWith"];
3317
3316
  const temporal = ["relative"];
3318
3317
  let ops = [...common];
3319
3318
  switch (column?.type) {
@@ -3331,10 +3330,10 @@ function offerableFilterOps(column, sourceOps = []) {
3331
3330
  case "url":
3332
3331
  case "select":
3333
3332
  case "multiselect":
3334
- ops = [...ops, ...text4];
3333
+ ops = [...ops, ...text5];
3335
3334
  break;
3336
3335
  default:
3337
- ops = [...ops, ...text4, ...ordered];
3336
+ ops = [...ops, ...text5, ...ordered];
3338
3337
  }
3339
3338
  if (sourceOps.length === 0) return ops;
3340
3339
  return ops.filter((op) => sourceOps.includes(op));
@@ -4246,7 +4245,7 @@ var COMBINATORS2 = [{ value: "and", label: "All" }, { value: "or", label: "Any"
4246
4245
  function FieldsPanelView({ controller, showStats, manualApply }) {
4247
4246
  useSyncExternalStore4(controller.subscribe, controller.getState, controller.getState);
4248
4247
  const view = fieldsViewState(controller, { showStats, manualApply });
4249
- const ui4 = (intent) => applyFieldsUi(controller, intent);
4248
+ const ui5 = (intent) => applyFieldsUi(controller, intent);
4250
4249
  if (view.noColumns) {
4251
4250
  return /* @__PURE__ */ jsx4(EmptyState4, { title: "No fields", hint: "Connect a source \u2014 its columns appear here, ready to drop onto a shelf." });
4252
4251
  }
@@ -4256,8 +4255,8 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4256
4255
  {
4257
4256
  left: view.sampled ? /* @__PURE__ */ jsx4(Badge4, { tone: "warning", title: view.sampleWarning, children: "sampled schema" }) : null,
4258
4257
  right: /* @__PURE__ */ jsxs4(ToolbarGroup3, { end: true, children: [
4259
- /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui4({ kind: "autoAssign" }), children: "Auto-assign" }),
4260
- view.manualApply ? /* @__PURE__ */ jsx4(TextButton, { strong: true, disabled: view.applyDisabled, onClick: () => ui4({ kind: "apply" }), children: "Apply" }) : null
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
4261
4260
  ] })
4262
4261
  }
4263
4262
  ),
@@ -4269,7 +4268,7 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4269
4268
  meta: column.role,
4270
4269
  selected: column.focused,
4271
4270
  trailing: column.stat ? /* @__PURE__ */ jsx4(Badge4, { title: column.statTitle, children: column.stat }) : null,
4272
- onClick: () => ui4({ kind: "focus", columnId: column.id })
4271
+ onClick: () => ui5({ kind: "focus", columnId: column.id })
4273
4272
  },
4274
4273
  column.id
4275
4274
  )) }) }),
@@ -4281,11 +4280,11 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4281
4280
  label: entry.label,
4282
4281
  dimmed: entry.suggested,
4283
4282
  meta: /* @__PURE__ */ jsxs4(Fragment2, { children: [
4284
- entry.aggregated ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Aggregate", options: entry.aggregates, value: entry.fn, onChange: (fn) => ui4({ kind: "aggregate", shelfId: entry.shelfId, columnId: entry.columnId, fn }) }) : null,
4285
- entry.sortable ? /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui4({ kind: "direction", shelfId: entry.shelfId, columnId: entry.columnId }), children: entry.direction }) : null,
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,
4286
4285
  entry.granularity ? /* @__PURE__ */ jsx4(Badge4, { children: entry.granularity }) : null
4287
4286
  ] }),
4288
- trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: entry.removeLabel, onClick: () => ui4({ kind: "remove", shelfId: entry.shelfId, columnId: entry.columnId }) })
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 }) })
4289
4288
  },
4290
4289
  entry.columnId
4291
4290
  )) }) : null
@@ -4296,15 +4295,15 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4296
4295
  title: "Filters",
4297
4296
  flush: true,
4298
4297
  badge: view.hasFilters ? /* @__PURE__ */ jsx4(Badge4, { children: view.filterCount }) : null,
4299
- actions: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(PlusGlyph, { size: 11 }), label: "Add filter", onClick: () => ui4({ kind: "addFilter" }) }),
4298
+ actions: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(PlusGlyph, { size: 11 }), label: "Add filter", onClick: () => ui5({ kind: "addFilter" }) }),
4300
4299
  children: [
4301
4300
  view.noFilters ? /* @__PURE__ */ jsx4(Note, { children: "No filters. Everything passes." }) : null,
4302
4301
  view.hasFilters ? /* @__PURE__ */ jsx4(RowList, { children: view.filters.map((filter) => /* @__PURE__ */ jsx4(
4303
4302
  Row,
4304
4303
  {
4305
4304
  label: filter.label,
4306
- meta: filter.leaf ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Operator", options: filter.ops, value: filter.op, onChange: (op) => ui4({ kind: "filterOp", index: filter.index, op }) }) : null,
4307
- trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: filter.removeLabel, onClick: () => ui4({ kind: "removeFilter", index: filter.index }) })
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 }) })
4308
4307
  },
4309
4308
  filter.key
4310
4309
  )) }) : null,
@@ -4312,8 +4311,8 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4312
4311
  Toolbar4,
4313
4312
  {
4314
4313
  divided: false,
4315
- left: /* @__PURE__ */ jsx4(SegmentedControl4, { size: "sm", label: "Combinator", options: COMBINATORS2, value: view.combinator, onChange: (combinator) => ui4({ kind: "combinator", combinator }) }),
4316
- right: /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui4({ kind: "addGroup" }), children: "+ group" })
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" })
4317
4316
  }
4318
4317
  )
4319
4318
  ]
@@ -4604,6 +4603,8 @@ var ConnectorsController = class {
4604
4603
  lastQueries = /* @__PURE__ */ new Map();
4605
4604
  selectedId = null;
4606
4605
  form = null;
4606
+ /** The form's draft values. In the controller, not React state, so the declaration can bind them — and cleared with the form. */
4607
+ draft = {};
4607
4608
  /** Newest outstanding request per `(connectionId, requestorPanelId)`. */
4608
4609
  outstanding = /* @__PURE__ */ new Map();
4609
4610
  /** In-flight executions, keyed by cache key — the single-flight map. */
@@ -4778,6 +4779,17 @@ var ConnectorsController = class {
4778
4779
  /** The host asks for a credential. The panel renders the descriptor; it knows no platform names. */
4779
4780
  requireCredential(spec) {
4780
4781
  this.form = spec;
4782
+ this.draft = {};
4783
+ this.notify();
4784
+ }
4785
+ /** The draft as typed so far. Never emitted except through {@link submitCredential}. */
4786
+ getCredentialDraft() {
4787
+ return this.draft;
4788
+ }
4789
+ /** One field of the draft changed. */
4790
+ setCredentialDraft(key, value) {
4791
+ if (!this.form) return;
4792
+ this.draft = { ...this.draft, [key]: value };
4781
4793
  this.notify();
4782
4794
  }
4783
4795
  /**
@@ -4797,6 +4809,7 @@ var ConnectorsController = class {
4797
4809
  };
4798
4810
  this.host.emit("credentialSubmit", submission);
4799
4811
  this.form = null;
4812
+ this.draft = {};
4800
4813
  this.notify();
4801
4814
  return true;
4802
4815
  }
@@ -4804,6 +4817,7 @@ var ConnectorsController = class {
4804
4817
  cancelCredential() {
4805
4818
  if (!this.form) return;
4806
4819
  this.form = null;
4820
+ this.draft = {};
4807
4821
  this.notify();
4808
4822
  }
4809
4823
  /**
@@ -5449,10 +5463,10 @@ function parseJsonPath(path) {
5449
5463
  }
5450
5464
  return steps;
5451
5465
  }
5452
- function findBracketEnd(text4, start) {
5466
+ function findBracketEnd(text5, start) {
5453
5467
  let quote = null;
5454
- for (let i = start + 1; i < text4.length; i += 1) {
5455
- const char = text4[i];
5468
+ for (let i = start + 1; i < text5.length; i += 1) {
5469
+ const char = text5[i];
5456
5470
  if (quote) {
5457
5471
  if (char === "\\") i += 1;
5458
5472
  else if (char === quote) quote = null;
@@ -5876,9 +5890,9 @@ function firstGraphqlError(errors) {
5876
5890
  }
5877
5891
 
5878
5892
  // src/data/connectors/executors/csv.ts
5879
- function sniffDelimiter(text4) {
5893
+ function sniffDelimiter(text5) {
5880
5894
  const candidates = [",", ";", " ", "|"];
5881
- const line = firstLogicalLine(text4);
5895
+ const line = firstLogicalLine(text5);
5882
5896
  let best = ",";
5883
5897
  let bestCount = 0;
5884
5898
  for (const candidate of candidates) {
@@ -5900,21 +5914,21 @@ function sniffDelimiter(text4) {
5900
5914
  }
5901
5915
  return best;
5902
5916
  }
5903
- function firstLogicalLine(text4) {
5917
+ function firstLogicalLine(text5) {
5904
5918
  let inQuotes = false;
5905
- for (let i = 0; i < text4.length; i += 1) {
5906
- const char = text4[i];
5919
+ for (let i = 0; i < text5.length; i += 1) {
5920
+ const char = text5[i];
5907
5921
  if (char === '"') {
5908
- if (inQuotes && text4[i + 1] === '"') i += 1;
5922
+ if (inQuotes && text5[i + 1] === '"') i += 1;
5909
5923
  else inQuotes = !inQuotes;
5910
5924
  } else if ((char === "\n" || char === "\r") && !inQuotes) {
5911
- return text4.slice(0, i);
5925
+ return text5.slice(0, i);
5912
5926
  }
5913
5927
  }
5914
- return text4;
5928
+ return text5;
5915
5929
  }
5916
- function parseCsv(text4, options = {}) {
5917
- const source = text4.charCodeAt(0) === 65279 ? text4.slice(1) : text4;
5930
+ function parseCsv(text5, options = {}) {
5931
+ const source = text5.charCodeAt(0) === 65279 ? text5.slice(1) : text5;
5918
5932
  const delimiter = options.delimiter && options.delimiter.length > 0 ? options.delimiter : sniffDelimiter(source);
5919
5933
  const quote = options.quote && options.quote.length > 0 ? options.quote : '"';
5920
5934
  const hasHeader = options.hasHeader ?? true;
@@ -5949,17 +5963,17 @@ function dedupe(names) {
5949
5963
  return count === 0 ? name : `${name}_${count + 1}`;
5950
5964
  });
5951
5965
  }
5952
- function scanRecords(text4, delimiter, quote) {
5966
+ function scanRecords(text5, delimiter, quote) {
5953
5967
  const records = [];
5954
5968
  let fields = [];
5955
5969
  let field = "";
5956
5970
  let inQuotes = false;
5957
5971
  let i = 0;
5958
- while (i < text4.length) {
5959
- const char = text4[i];
5972
+ while (i < text5.length) {
5973
+ const char = text5[i];
5960
5974
  if (inQuotes) {
5961
5975
  if (char === quote) {
5962
- if (text4[i + 1] === quote) {
5976
+ if (text5[i + 1] === quote) {
5963
5977
  field += quote;
5964
5978
  i += 2;
5965
5979
  continue;
@@ -5977,7 +5991,7 @@ function scanRecords(text4, delimiter, quote) {
5977
5991
  i += 1;
5978
5992
  continue;
5979
5993
  }
5980
- if (text4.startsWith(delimiter, i)) {
5994
+ if (text5.startsWith(delimiter, i)) {
5981
5995
  fields.push(field);
5982
5996
  field = "";
5983
5997
  i += delimiter.length;
@@ -5988,7 +6002,7 @@ function scanRecords(text4, delimiter, quote) {
5988
6002
  records.push(fields);
5989
6003
  fields = [];
5990
6004
  field = "";
5991
- i += char === "\r" && text4[i + 1] === "\n" ? 2 : 1;
6005
+ i += char === "\r" && text5[i + 1] === "\n" ? 2 : 1;
5992
6006
  continue;
5993
6007
  }
5994
6008
  field += char;
@@ -6554,18 +6568,26 @@ var connectorsManifest = {
6554
6568
  };
6555
6569
 
6556
6570
  // src/data/connectors/react/ConnectorsPanelView.tsx
6557
- import { useState as useState2, useSyncExternalStore as useSyncExternalStore5 } from "react";
6571
+ import { useSyncExternalStore as useSyncExternalStore5 } from "react";
6558
6572
  import {
6559
6573
  Badge as Badge5,
6574
+ Code,
6575
+ Column as Column4,
6560
6576
  EmptyState as EmptyState5,
6561
6577
  Field,
6562
6578
  FieldGroup,
6563
6579
  IconButton as IconButton4,
6580
+ Input,
6581
+ Link,
6582
+ Note as Note2,
6564
6583
  Row as Row2,
6565
6584
  RowList as RowList2,
6566
6585
  ScrollArea as ScrollArea4,
6586
+ Select as Select2,
6567
6587
  StatusBar as StatusBar5,
6588
+ Text as Text2,
6568
6589
  TextButton as TextButton2,
6590
+ Toggle,
6569
6591
  Toolbar as Toolbar5,
6570
6592
  ToolbarGroup as ToolbarGroup4
6571
6593
  } from "@xenosystem/components/chrome";
@@ -6631,183 +6653,254 @@ function credentialSpecProblem(spec) {
6631
6653
  return null;
6632
6654
  }
6633
6655
 
6634
- // src/data/connectors/react/ConnectorsPanelView.tsx
6635
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
6636
- var TONE = {
6637
- unknown: "neutral",
6638
- ok: "success",
6639
- degraded: "warning",
6640
- unconfigured: "info",
6641
- unsupported: "warning",
6642
- error: "error",
6643
- expired: "warning"
6644
- };
6645
- function ConnectorsPanelView({
6646
- controller,
6647
- emptyHint
6648
- }) {
6649
- const state = useSyncExternalStore5(controller.subscribe, controller.getState, controller.getState);
6650
- if (state.form) {
6651
- return /* @__PURE__ */ jsx5(CredentialForm, { controller });
6656
+ // src/data/connectors/declaration.ts
6657
+ var TONE = { unknown: "neutral", ok: "success", degraded: "warning", unconfigured: "info", unsupported: "warning", error: "error", expired: "warning" };
6658
+ function connectorsViewState(c, options = {}) {
6659
+ const s = c.getState();
6660
+ const form = s.form;
6661
+ const draft = c.getCredentialDraft();
6662
+ const problem = form ? credentialSpecProblem(form) : null;
6663
+ const shown = form && !problem ? visibleFields(form, draft) : [];
6664
+ const errors = form && !problem ? validateCredentialDraft(form, draft) : {};
6665
+ return {
6666
+ listing: !form,
6667
+ sources: form ? [] : s.connections.map(({ connection, status }) => ({
6668
+ id: connection.id,
6669
+ name: connection.name,
6670
+ kind: connection.kind,
6671
+ selected: connection.id === s.selectedId,
6672
+ disabled: status.health === "unsupported",
6673
+ title: status.message,
6674
+ health: status.health,
6675
+ tone: TONE[status.health] ?? "neutral"
6676
+ })),
6677
+ empty: !form && s.connections.length === 0,
6678
+ emptyHint: options.emptyHint ?? "Paste CSV or JSON to start with an inline source \u2014 no setup needed.",
6679
+ listed: !form && s.connections.length > 0,
6680
+ countLabel: `${s.connections.length} source${s.connections.length === 1 ? "" : "s"}`,
6681
+ busyLabel: s.busy.length > 0 ? "querying\u2026" : void 0,
6682
+ form: !!form,
6683
+ formTitle: form?.title ?? "",
6684
+ formBroken: !!form && !!problem,
6685
+ formProblem: problem ?? void 0,
6686
+ formReady: !!form && !problem,
6687
+ formMessage: form?.message,
6688
+ redirectUri: form?.redirectUri,
6689
+ fields: shown.map((field) => {
6690
+ const kind = field.type === "textarea" ? "textarea" : field.type;
6691
+ return {
6692
+ key: field.key,
6693
+ label: field.label,
6694
+ hint: errors[field.key] ?? field.description,
6695
+ kind,
6696
+ isText: kind === "text" || kind === "password" || kind === "number" || kind === "textarea",
6697
+ isSelect: kind === "select",
6698
+ isBoolean: kind === "boolean",
6699
+ inputType: kind === "password" ? "password" : kind === "number" ? "number" : "text",
6700
+ text: String(draft[field.key] ?? ""),
6701
+ checked: draft[field.key] === true,
6702
+ placeholder: field.placeholder,
6703
+ options: field.options ?? []
6704
+ };
6705
+ }),
6706
+ docsUrl: form?.docsUrl,
6707
+ submitDisabled: Object.keys(errors).length > 0
6708
+ };
6709
+ }
6710
+ function applyConnectorsUi(c, intent) {
6711
+ switch (intent.kind) {
6712
+ case "refreshAll":
6713
+ void c.refreshAll();
6714
+ break;
6715
+ case "select":
6716
+ c.select(intent.id);
6717
+ break;
6718
+ case "field":
6719
+ c.setCredentialDraft(intent.key, intent.value);
6720
+ break;
6721
+ case "cancel":
6722
+ c.cancelCredential();
6723
+ break;
6724
+ case "submit": {
6725
+ const form = c.getState().form;
6726
+ if (form) c.submitCredential(pruneHiddenValues(form, c.getCredentialDraft()));
6727
+ break;
6728
+ }
6652
6729
  }
6653
- return /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
6654
- /* @__PURE__ */ jsx5(
6655
- Toolbar5,
6730
+ }
6731
+ var connectorsViewManifest = {
6732
+ id: connectorsManifest.id,
6733
+ version: "1.0.0",
6734
+ title: "Connectors",
6735
+ category: "data",
6736
+ layout: { slot: "side" },
6737
+ inputs: [{ id: "view", type: "object", label: "View state", description: "The ConnectorsViewModel the runtime derives from the controller on every draw." }],
6738
+ outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind, \u2026 } \u2014 applied by the runtime to the controller." }],
6739
+ commands: []
6740
+ };
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
6744
+ };
6745
+ 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 } } });
6748
+ var CONNECTORS_DECLARATION = {
6749
+ format: "xeno.block@1",
6750
+ manifest: connectorsViewManifest,
6751
+ behaviour: "data/connectorsPanelView",
6752
+ composition: {
6753
+ component: "fragment",
6754
+ children: [
6656
6755
  {
6657
- right: /* @__PURE__ */ jsx5(ToolbarGroup4, { end: true, children: /* @__PURE__ */ jsx5(
6658
- IconButton4,
6756
+ component: "chrome/Column",
6757
+ when: { input: "view.listing" },
6758
+ 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") } }] } } } },
6760
+ { component: "chrome/EmptyState", when: { input: "view.empty" }, props: { title: { value: "No sources" }, hint: { input: "view.emptyHint" } } },
6659
6761
  {
6660
- icon: /* @__PURE__ */ jsx5(RefreshGlyph, {}),
6661
- label: "Refresh all",
6662
- onClick: () => void controller.refreshAll()
6663
- }
6664
- ) })
6665
- }
6666
- ),
6667
- state.connections.length === 0 ? /* @__PURE__ */ jsx5(
6668
- EmptyState5,
6762
+ component: "chrome/ScrollArea",
6763
+ when: { input: "view.listed" },
6764
+ children: [
6765
+ {
6766
+ component: "chrome/RowList",
6767
+ props: { role: { value: "listbox" }, "aria-label": { value: "Data sources" } },
6768
+ children: [
6769
+ {
6770
+ component: "chrome/Row",
6771
+ 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" } }) }
6774
+ }
6775
+ ]
6776
+ }
6777
+ ]
6778
+ },
6779
+ { component: "chrome/StatusBar", props: { left: { input: "view.countLabel" }, right: { input: "view.busyLabel" } } }
6780
+ ]
6781
+ },
6669
6782
  {
6670
- title: "No sources",
6671
- hint: emptyHint ?? "Paste CSV or JSON to start with an inline source \u2014 no setup needed."
6783
+ component: "chrome/Column",
6784
+ when: { input: "view.form" },
6785
+ children: [
6786
+ { component: "chrome/Toolbar", props: { left: { node: { component: "chrome/Text", props: { role: { value: "title" } }, children: [text4({ input: "view.formTitle" })] } } } },
6787
+ { component: "chrome/EmptyState", when: { input: "view.formBroken" }, props: { title: { value: "This connection cannot be set up" }, hint: { input: "view.formProblem" } } },
6788
+ {
6789
+ component: "chrome/Toolbar",
6790
+ 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" })] }] } } }
6792
+ },
6793
+ {
6794
+ component: "chrome/ScrollArea",
6795
+ when: { input: "view.formReady" },
6796
+ children: [
6797
+ { component: "chrome/Note", when: { input: "view.formMessage" }, children: [text4({ input: "view.formMessage" })] },
6798
+ {
6799
+ component: "fragment",
6800
+ when: { input: "view.redirectUri" },
6801
+ children: [
6802
+ { component: "chrome/Note", children: [text4({ value: "Add this exact redirect URI to the provider:" })] },
6803
+ // 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" })] }
6805
+ ]
6806
+ },
6807
+ {
6808
+ component: "chrome/FieldGroup",
6809
+ children: [
6810
+ {
6811
+ component: "chrome/Field",
6812
+ each: { input: "view.fields", key: "key" },
6813
+ props: { label: { item: "label" }, hint: { item: "hint" } },
6814
+ 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: "" } }) } }
6818
+ ]
6819
+ }
6820
+ ]
6821
+ },
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?" })] }] }
6823
+ ]
6824
+ },
6825
+ {
6826
+ component: "chrome/Toolbar",
6827
+ when: { input: "view.formReady" },
6828
+ props: {
6829
+ divided: { value: false },
6830
+ right: {
6831
+ node: {
6832
+ component: "chrome/ToolbarGroup",
6833
+ props: { end: { value: true } },
6834
+ 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" })] }
6837
+ ]
6838
+ }
6839
+ }
6840
+ }
6841
+ }
6842
+ ]
6672
6843
  }
6673
- ) : /* @__PURE__ */ jsx5(ScrollArea4, { children: /* @__PURE__ */ jsx5(RowList2, { role: "listbox", "aria-label": "Data sources", children: state.connections.map(({ connection, status }) => {
6674
- const disabled = status.health === "unsupported";
6675
- return /* @__PURE__ */ jsx5(
6844
+ ]
6845
+ }
6846
+ };
6847
+
6848
+ // src/data/connectors/react/ConnectorsPanelView.tsx
6849
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
6850
+ var PlugGlyph = ICONS["icons/Plug"];
6851
+ var RefreshGlyph2 = ICONS["icons/Refresh"];
6852
+ function ConnectorsPanelView({ controller, emptyHint }) {
6853
+ useSyncExternalStore5(controller.subscribe, controller.getState, controller.getState);
6854
+ const view = connectorsViewState(controller, { emptyHint });
6855
+ const ui5 = (intent) => applyConnectorsUi(controller, intent);
6856
+ 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" }) }) }) }),
6859
+ view.empty ? /* @__PURE__ */ jsx5(EmptyState5, { title: "No sources", hint: view.emptyHint }) : null,
6860
+ view.listed ? /* @__PURE__ */ jsx5(ScrollArea4, { children: /* @__PURE__ */ jsx5(RowList2, { role: "listbox", "aria-label": "Data sources", children: view.sources.map((source) => /* @__PURE__ */ jsx5(
6676
6861
  Row2,
6677
6862
  {
6678
6863
  icon: /* @__PURE__ */ jsx5(PlugGlyph, {}),
6679
- label: connection.name,
6680
- meta: connection.kind,
6681
- selected: connection.id === state.selectedId,
6682
- disabled,
6683
- title: status.message,
6684
- onClick: () => controller.select(connection.id),
6685
- trailing: /* @__PURE__ */ jsx5(Badge5, { tone: TONE[status.health], children: status.health })
6864
+ label: source.name,
6865
+ meta: source.kind,
6866
+ selected: source.selected,
6867
+ disabled: source.disabled,
6868
+ title: source.title,
6869
+ trailing: /* @__PURE__ */ jsx5(Badge5, { tone: source.tone, children: source.health }),
6870
+ onClick: () => ui5({ kind: "select", id: source.id })
6686
6871
  },
6687
- connection.id
6688
- );
6689
- }) }) }),
6690
- /* @__PURE__ */ jsx5(
6691
- StatusBar5,
6692
- {
6693
- left: `${state.connections.length} source${state.connections.length === 1 ? "" : "s"}`,
6694
- right: state.busy.length > 0 ? "querying\u2026" : void 0
6695
- }
6696
- )
6697
- ] });
6698
- }
6699
- function CredentialForm({ controller }) {
6700
- const state = useSyncExternalStore5(controller.subscribe, controller.getState, controller.getState);
6701
- const [values, setValues] = useState2({});
6702
- const form = state.form;
6703
- if (!form) return null;
6704
- const problem = credentialSpecProblem(form);
6705
- if (problem) {
6706
- return /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
6707
- /* @__PURE__ */ jsx5(Toolbar5, { left: /* @__PURE__ */ jsx5("span", { style: { fontSize: 11 }, children: form.title }) }),
6708
- /* @__PURE__ */ jsx5(EmptyState5, { title: "This connection cannot be set up", hint: problem }),
6709
- /* @__PURE__ */ jsx5(
6872
+ source.id
6873
+ )) }) }) : null,
6874
+ /* @__PURE__ */ jsx5(StatusBar5, { left: view.countLabel, right: view.busyLabel })
6875
+ ] }) : null,
6876
+ view.form ? /* @__PURE__ */ jsxs5(Column4, { children: [
6877
+ /* @__PURE__ */ jsx5(Toolbar5, { left: /* @__PURE__ */ jsx5(Text2, { role: "title", children: view.formTitle }) }),
6878
+ 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,
6880
+ view.formReady ? /* @__PURE__ */ jsxs5(ScrollArea4, { children: [
6881
+ view.formMessage ? /* @__PURE__ */ jsx5(Note2, { children: view.formMessage }) : null,
6882
+ view.redirectUri ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
6883
+ /* @__PURE__ */ jsx5(Note2, { children: "Add this exact redirect URI to the provider:" }),
6884
+ /* @__PURE__ */ jsx5(Code, { variant: "flush", children: view.redirectUri })
6885
+ ] }) : null,
6886
+ /* @__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
6890
+ ] }, field.key)) }),
6891
+ view.docsUrl ? /* @__PURE__ */ jsx5(Note2, { children: /* @__PURE__ */ jsx5(Link, { href: view.docsUrl, children: "Where do I get this?" }) }) : null
6892
+ ] }) : null,
6893
+ view.formReady ? /* @__PURE__ */ jsx5(
6710
6894
  Toolbar5,
6711
6895
  {
6712
6896
  divided: false,
6713
- right: /* @__PURE__ */ jsx5(ToolbarGroup4, { end: true, children: /* @__PURE__ */ jsx5(TextButton2, { onClick: () => controller.cancelCredential(), children: "Close" }) })
6714
- }
6715
- )
6716
- ] });
6717
- }
6718
- const shown = visibleFields(form, values);
6719
- const errors = validateCredentialDraft(form, values);
6720
- const invalid = Object.keys(errors).length > 0;
6721
- return /* @__PURE__ */ jsxs5("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
6722
- /* @__PURE__ */ jsx5(Toolbar5, { left: /* @__PURE__ */ jsx5("span", { style: { fontSize: 11 }, children: form.title }) }),
6723
- /* @__PURE__ */ jsxs5(ScrollArea4, { children: [
6724
- form.message ? /* @__PURE__ */ jsx5("div", { style: { padding: 8, fontSize: 11, opacity: 0.8 }, children: form.message }) : null,
6725
- form.redirectUri ? /* @__PURE__ */ jsxs5("div", { style: { padding: 8, fontSize: 11 }, children: [
6726
- /* @__PURE__ */ jsx5("div", { style: { opacity: 0.7, marginBottom: 4 }, children: "Add this exact redirect URI to the provider:" }),
6727
- /* @__PURE__ */ jsx5(
6728
- "code",
6729
- {
6730
- style: {
6731
- display: "block",
6732
- padding: "4px 6px",
6733
- background: "var(--xeno-canvas)",
6734
- borderRadius: 2,
6735
- userSelect: "all",
6736
- wordBreak: "break-all"
6737
- },
6738
- children: form.redirectUri
6739
- }
6740
- )
6741
- ] }) : null,
6742
- /* @__PURE__ */ jsx5(FieldGroup, { children: shown.map((field) => /* @__PURE__ */ jsx5(Field, { label: field.label, hint: errors[field.key] ?? field.description, children: field.type === "boolean" ? /* @__PURE__ */ jsx5(
6743
- "input",
6744
- {
6745
- type: "checkbox",
6746
- checked: values[field.key] === true,
6747
- onChange: (e) => setValues((v) => ({ ...v, [field.key]: e.target.checked }))
6748
- }
6749
- ) : field.type === "select" ? /* @__PURE__ */ jsxs5(
6750
- "select",
6751
- {
6752
- value: String(values[field.key] ?? ""),
6753
- onChange: (e) => setValues((v) => ({ ...v, [field.key]: e.target.value })),
6754
- children: [
6755
- /* @__PURE__ */ jsx5("option", { value: "" }),
6756
- field.options?.map((o) => /* @__PURE__ */ jsx5("option", { value: o.value, children: o.label }, o.value))
6757
- ]
6897
+ 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" })
6900
+ ] })
6758
6901
  }
6759
- ) : /* @__PURE__ */ jsx5(
6760
- "input",
6761
- {
6762
- type: field.type === "password" ? "password" : "text",
6763
- value: String(values[field.key] ?? ""),
6764
- placeholder: field.placeholder,
6765
- autoComplete: "off",
6766
- onChange: (e) => setValues((v) => ({ ...v, [field.key]: e.target.value })),
6767
- style: {
6768
- width: "100%",
6769
- background: "var(--xeno-canvas)",
6770
- border: "1px solid var(--xeno-border)",
6771
- borderRadius: 2,
6772
- color: "inherit",
6773
- font: "inherit",
6774
- padding: "0 6px",
6775
- height: 20
6776
- }
6777
- }
6778
- ) }, field.key)) }),
6779
- form.docsUrl ? /* @__PURE__ */ jsx5("div", { style: { padding: 8 }, children: /* @__PURE__ */ jsx5("a", { href: form.docsUrl, target: "_blank", rel: "noreferrer", style: { fontSize: 10 }, children: "Where do I get this?" }) }) : null
6780
- ] }),
6781
- /* @__PURE__ */ jsx5(
6782
- Toolbar5,
6783
- {
6784
- divided: false,
6785
- right: /* @__PURE__ */ jsxs5(ToolbarGroup4, { end: true, children: [
6786
- /* @__PURE__ */ jsx5(
6787
- TextButton2,
6788
- {
6789
- onClick: () => {
6790
- setValues({});
6791
- controller.cancelCredential();
6792
- },
6793
- children: "Cancel"
6794
- }
6795
- ),
6796
- /* @__PURE__ */ jsx5(
6797
- TextButton2,
6798
- {
6799
- strong: true,
6800
- disabled: invalid,
6801
- onClick: () => {
6802
- controller.submitCredential(pruneHiddenValues(form, values));
6803
- setValues({});
6804
- },
6805
- children: "Connect"
6806
- }
6807
- )
6808
- ] })
6809
- }
6810
- )
6902
+ ) : null
6903
+ ] }) : null
6811
6904
  ] });
6812
6905
  }
6813
6906
 
@@ -7132,7 +7225,10 @@ export {
7132
7225
  CHART_PANEL_ID,
7133
7226
  CHART_RUNTIME_UNITS,
7134
7227
  CHART_TYPE_LABELS,
7228
+ BEHAVIOURS4 as CONNECTORS_BEHAVIOURS,
7229
+ CONNECTORS_DECLARATION,
7135
7230
  CONNECTORS_PANEL_ID,
7231
+ CONNECTORS_RUNTIME_UNITS,
7136
7232
  ChartController,
7137
7233
  ChartPanelView,
7138
7234
  ChartPlot,
@@ -7167,6 +7263,7 @@ export {
7167
7263
  TableController,
7168
7264
  TablePanelView,
7169
7265
  applyChartUi,
7266
+ applyConnectorsUi,
7170
7267
  applyFieldsUi,
7171
7268
  applyMetricsUi,
7172
7269
  applyPrecision,
@@ -7192,6 +7289,8 @@ export {
7192
7289
  computeWindow,
7193
7290
  connectorsManifest,
7194
7291
  connectorsPanel,
7292
+ connectorsViewManifest,
7293
+ connectorsViewState,
7195
7294
  constraintsOfColumn,
7196
7295
  createChartPanel,
7197
7296
  createConnectorExecutor,
@@ -22,7 +22,7 @@ import {
22
22
  VisibilityIcon,
23
23
  XIcon,
24
24
  XIcon2
25
- } from "../chunk-6XVFYPVH.js";
25
+ } from "../chunk-PI5WOATA.js";
26
26
  import "../chunk-2KG3PWR4.js";
27
27
 
28
28
  // src/edit/layers/panel.ts
@@ -2,7 +2,7 @@ import {
2
2
  ICONS,
3
3
  ICON_UNITS,
4
4
  Svg
5
- } from "../chunk-6XVFYPVH.js";
5
+ } from "../chunk-PI5WOATA.js";
6
6
  import "../chunk-2KG3PWR4.js";
7
7
  export {
8
8
  ICONS,
package/dist/ops/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  } from "../chunk-WE6A2DTY.js";
9
9
  import {
10
10
  ICONS
11
- } from "../chunk-6XVFYPVH.js";
11
+ } from "../chunk-PI5WOATA.js";
12
12
  import "../chunk-2KG3PWR4.js";
13
13
 
14
14
  // src/ops/console/panel.ts
@@ -13,7 +13,7 @@ import {
13
13
  StopIcon,
14
14
  TRANSPORT_ICONS,
15
15
  TRANSPORT_RUNTIME_UNITS
16
- } from "../chunk-6XVFYPVH.js";
16
+ } from "../chunk-PI5WOATA.js";
17
17
  import {
18
18
  __reExport
19
19
  } from "../chunk-2KG3PWR4.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xenosystem/blocks",
3
- "version": "0.13.13",
3
+ "version": "0.13.14",
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.23.0 <1",
67
+ "@xenosystem/components": ">=0.24.0 <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.23.0",
84
+ "@xenosystem/components": "^0.24.0",
85
85
  "@xenosystem/data-core": "^0.1.0",
86
86
  "@xenosystem/elements": "0.0.1",
87
87
  "@xenosystem/elements-react": "0.0.1",