@xenosystem/blocks 0.18.1 → 0.19.1

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.
@@ -203,7 +203,7 @@ function UngroupGlyph({ size } = {}) {
203
203
  // src/icons/named.tsx
204
204
  import { jsx as jsx3 } from "react/jsx-runtime";
205
205
  function NamedIcon({ name, size }) {
206
- const Icon = ICONS[`icons/${name}`];
206
+ const Icon = name === "Named" ? void 0 : ICONS[`icons/${name}`];
207
207
  return Icon ? /* @__PURE__ */ jsx3(Icon, { size }) : /* @__PURE__ */ jsx3(Svg, { size, children: /* @__PURE__ */ jsx3("rect", { x: "9", y: "9", width: "6", height: "6", rx: "1" }) });
208
208
  }
209
209
 
@@ -154,6 +154,22 @@ interface XenoRowActivate {
154
154
  /** The column the activation happened in, when it was a cell. */
155
155
  columnId?: string;
156
156
  }
157
+ /** An action declared by the host, never performed by the table. */
158
+ interface XenoTableRowAction {
159
+ id: string;
160
+ label: string;
161
+ disabled?: boolean;
162
+ }
163
+ /** One row's action list. The rowActions input replaces the entire list of these entries. */
164
+ interface XenoTableRowActions {
165
+ rowId: string;
166
+ actions: XenoTableRowAction[];
167
+ }
168
+ /** The host authorizes and performs this intent against its current state. */
169
+ interface XenoRowActionIntent {
170
+ rowId: string;
171
+ actionId: string;
172
+ }
157
173
  /** The panel's serialized state. `.xapp` stores this; it never stores a result. */
158
174
  interface TablePanelState {
159
175
  /** The query — **this persists**. */
@@ -226,6 +242,7 @@ declare class TableController {
226
242
  private readonly livePreview;
227
243
  private readonly listeners;
228
244
  private result;
245
+ private rowActions;
229
246
  private query;
230
247
  private viewMode;
231
248
  private layout;
@@ -346,6 +363,12 @@ declare class TableController {
346
363
  } | null): void;
347
364
  /** Emit a row activation (double-click / Enter) — the BACK-view execution trigger. */
348
365
  activateRow(rowId: string, columnId?: string): void;
366
+ /** Full replacement, copied and validated before mutation. Invalid wires leave prior actions intact. */
367
+ setRowActions(value: unknown): boolean;
368
+ /** Actions are unavailable while loading or for a row no longer in the current result. */
369
+ actionsForRow(rowId: string): readonly XenoTableRowAction[];
370
+ /** One guard for UI and agent routes. Never mutates the result or grants host authorization. */
371
+ runRowAction(rowId: string, actionId: string): boolean;
349
372
  /**
350
373
  * Begin editing a cell.
351
374
  *
@@ -741,6 +764,11 @@ interface TableViewRow {
741
764
  gutterWidth: number;
742
765
  frozenCells: TableViewCell[];
743
766
  cells: TableViewCell[];
767
+ hasActions: boolean;
768
+ actionsLabel: string;
769
+ actions: (XenoTableRowAction & {
770
+ rowId: string;
771
+ })[];
744
772
  }
745
773
  interface TableViewPivotRow {
746
774
  key: string;
@@ -778,6 +806,9 @@ interface TableViewModel {
778
806
  frozenHeaders: TableViewHeaderCell[];
779
807
  headers: TableViewHeaderCell[];
780
808
  spacerWidth: number;
809
+ actionsShown: boolean;
810
+ actionsWidth: number;
811
+ trailingSpacerWidth: number;
781
812
  totalHeight: number;
782
813
  offsetTop: number;
783
814
  rows: TableViewRow[];
@@ -809,9 +840,15 @@ type TableUiIntent = {
809
840
  id: string;
810
841
  ctrlKey?: boolean;
811
842
  metaKey?: boolean;
843
+ targetTag?: string;
812
844
  } | {
813
845
  kind: 'rowDouble';
814
846
  id: string;
847
+ targetTag?: string;
848
+ } | {
849
+ kind: 'rowAction';
850
+ rowId: string;
851
+ actionId: string;
815
852
  } | {
816
853
  kind: 'cellDouble';
817
854
  rowId: string;
@@ -824,6 +861,8 @@ type TableUiIntent = {
824
861
  key: string;
825
862
  } | {
826
863
  kind: 'commitEdit';
864
+ } | {
865
+ kind: 'actionCellDouble';
827
866
  };
828
867
  /** Derive the view model from the live controller — the runtime calls this on every draw. */
829
868
  declare function tableViewState(c: TableController, options?: TableViewOptions): TableViewModel;
@@ -3787,4 +3826,4 @@ interface ConnectorsPanelViewProps extends ConnectorsViewOptions {
3787
3826
  /** The Connectors panel view. */
3788
3827
  declare function ConnectorsPanelView({ controller, emptyHint }: ConnectorsPanelViewProps): ReactNode;
3789
3828
 
3790
- 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, BEHAVIOURS$4 as TABLE_BEHAVIOURS, TABLE_DECLARATION, TABLE_PANEL_ID, TABLE_RUNTIME_UNITS, TableController, type TableControllerOptions, type TableHostBridge, type TablePanelState, TablePanelView, type TablePanelViewProps, type TableRenderContext, TableScroller, type TableUiIntent, type TableViewCell, type TableViewHeaderCell, type TableViewMode, type TableViewModel, type TableViewOptions, type TableViewPivotRow, type TableViewRow, 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, applyTableUi, 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, tableRuntimeComponents, tableViewManifest, tableViewState, toChartJsConfig, toExecuteError, totalWidth, uniqueTableName, unsupportedMessage, validateCredentialDraft, validateEncoding, validateLoopbackRequest, visibleFields };
3829
+ 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, BEHAVIOURS$4 as TABLE_BEHAVIOURS, TABLE_DECLARATION, TABLE_PANEL_ID, TABLE_RUNTIME_UNITS, TableController, type TableControllerOptions, type TableHostBridge, type TablePanelState, TablePanelView, type TablePanelViewProps, type TableRenderContext, TableScroller, type TableUiIntent, type TableViewCell, type TableViewHeaderCell, type TableViewMode, type TableViewModel, type TableViewOptions, type TableViewPivotRow, type TableViewRow, 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 XenoRowActionIntent, type XenoRowActivate, type XenoShelfEntry, type XenoShelfSpec, XenoSqlEngine, type XenoTableRowAction, type XenoTableRowActions, applyChartUi, applyConnectorsUi, applyFieldsUi, applyMetricsUi, applyPrecision, applyTableUi, 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, tableRuntimeComponents, tableViewManifest, tableViewState, toChartJsConfig, toExecuteError, totalWidth, uniqueTableName, unsupportedMessage, validateCredentialDraft, validateEncoding, validateLoopbackRequest, visibleFields };
@@ -12,7 +12,7 @@ import {
12
12
  RefreshGlyph,
13
13
  SortAscGlyph,
14
14
  SortDescGlyph
15
- } from "../chunk-6STT7CAM.js";
15
+ } from "../chunk-6ZIWULSK.js";
16
16
  import "../chunk-2KG3PWR4.js";
17
17
 
18
18
  // src/data/table/panel.ts
@@ -249,6 +249,7 @@ var TableController = class {
249
249
  livePreview;
250
250
  listeners = /* @__PURE__ */ new Set();
251
251
  result = null;
252
+ rowActions = /* @__PURE__ */ new Map();
252
253
  query;
253
254
  viewMode;
254
255
  layout;
@@ -537,6 +538,37 @@ var TableController = class {
537
538
  const payload = { rowId, columnId };
538
539
  this.host.emit("rowActivate", payload);
539
540
  }
541
+ /** Full replacement, copied and validated before mutation. Invalid wires leave prior actions intact. */
542
+ setRowActions(value) {
543
+ if (!Array.isArray(value)) return false;
544
+ const next = /* @__PURE__ */ new Map();
545
+ for (const entry of value) {
546
+ if (!entry || typeof entry !== "object" || typeof entry.rowId !== "string" || !entry.rowId.trim() || !Array.isArray(entry.actions) || next.has(entry.rowId)) return false;
547
+ const actions = [];
548
+ const ids = /* @__PURE__ */ new Set();
549
+ for (const action of entry.actions) {
550
+ if (!action || typeof action !== "object" || typeof action.id !== "string" || !action.id.trim() || typeof action.label !== "string" || !action.label.trim() || action.disabled !== void 0 && typeof action.disabled !== "boolean" || ids.has(action.id)) return false;
551
+ ids.add(action.id);
552
+ actions.push(Object.freeze({ id: action.id, label: action.label, disabled: action.disabled }));
553
+ }
554
+ next.set(entry.rowId, Object.freeze(actions));
555
+ }
556
+ this.rowActions = next;
557
+ this.notify();
558
+ return true;
559
+ }
560
+ /** Actions are unavailable while loading or for a row no longer in the current result. */
561
+ actionsForRow(rowId) {
562
+ if (!this.result || !["ready", "partial"].includes(this.result.status) || this.outstandingId !== null || !this.rowById(rowId)) return [];
563
+ return this.rowActions.get(rowId) ?? [];
564
+ }
565
+ /** One guard for UI and agent routes. Never mutates the result or grants host authorization. */
566
+ runRowAction(rowId, actionId) {
567
+ const action = this.actionsForRow(rowId).find((a) => a.id === actionId);
568
+ if (!action || action.disabled) return false;
569
+ this.host.emit("rowAction", { rowId, actionId });
570
+ return true;
571
+ }
540
572
  /* ── Cell editing (intents only) ───────────────────────────────────────── */
541
573
  /**
542
574
  * Begin editing a cell.
@@ -681,6 +713,7 @@ var TableController = class {
681
713
  /** Drop listeners and in-flight state. */
682
714
  dispose() {
683
715
  this.listeners.clear();
716
+ this.rowActions.clear();
684
717
  this.outstandingId = null;
685
718
  this.editing = null;
686
719
  }
@@ -712,6 +745,13 @@ var tableManifest = {
712
745
  description: "Virtualized grid and pivot cross-tab over a XenoResultSet. Sorts, filters, groups, pages and edits by intent \u2014 the host resolves the query and writes the data.",
713
746
  defaultSlot: "center",
714
747
  inputs: [
748
+ {
749
+ id: "rowActions",
750
+ name: "Row actions",
751
+ type: "array",
752
+ multiple: false,
753
+ description: "Full replacement [{rowId, actions: [{id, label, disabled?}]}]. Separate from result data; [] clears all. Malformed input is refused."
754
+ },
715
755
  {
716
756
  id: "result",
717
757
  name: "Result",
@@ -739,6 +779,12 @@ var tableManifest = {
739
779
  }
740
780
  ],
741
781
  outputs: [
782
+ {
783
+ id: "rowAction",
784
+ name: "Row action",
785
+ type: "object",
786
+ description: "{rowId, actionId} \u2014 an enabled declared action on a current row. The host authorizes and performs it, then pushes updated data and actions."
787
+ },
742
788
  {
743
789
  id: "queryRequest",
744
790
  name: "Query Request",
@@ -774,6 +820,15 @@ var tableManifest = {
774
820
  }
775
821
  ],
776
822
  commands: [
823
+ {
824
+ id: "run_row_action",
825
+ title: "Run row action",
826
+ description: "Request an enabled action on a current row; false when unavailable.",
827
+ parameters: {
828
+ rowId: { type: "string", required: true, description: "Stable row id." },
829
+ actionId: { type: "string", required: true, description: "Declared action id." }
830
+ }
831
+ },
777
832
  {
778
833
  id: "refresh",
779
834
  title: "Refresh",
@@ -956,6 +1011,7 @@ import {
956
1011
  IconButton,
957
1012
  Input,
958
1013
  LoadingState,
1014
+ TextButton,
959
1015
  PivotCell,
960
1016
  PivotRow,
961
1017
  PivotTable,
@@ -1035,7 +1091,11 @@ function tableViewState(c, options = {}) {
1035
1091
  const colWindow = computeColumnWindow(columns, viewport.scrollLeft, viewport.width || 800);
1036
1092
  const frozen = columns.filter((col) => col.frozen);
1037
1093
  const visible = columns.slice(colWindow.startIndex, colWindow.endIndex).filter((col) => !col.frozen);
1038
- const width = totalWidth(columns);
1094
+ const actionsShown = grid && result.rows.some((row) => c.actionsForRow(row.id).length > 0);
1095
+ const actionsWidth = Math.max(1, ...(result?.rows ?? []).map((row) => c.actionsForRow(row.id).length)) * 160;
1096
+ const dataWidth = totalWidth(columns);
1097
+ const width = dataWidth + (actionsShown ? actionsWidth : 0);
1098
+ const trailingSpacerWidth = actionsShown ? Math.max(0, dataWidth - colWindow.offsetLeft - [...frozen, ...visible].reduce((sum, col) => sum + col.width, 0)) : 0;
1039
1099
  const editing = state.editing;
1040
1100
  const cell = (rowId, values, col) => {
1041
1101
  const isEditing = editing?.rowId === rowId && editing.columnId === col.column.id;
@@ -1065,7 +1125,10 @@ function tableViewState(c, options = {}) {
1065
1125
  ordinal: String((page?.offset ?? 0) + rowIndex + 1),
1066
1126
  gutterWidth: GUTTER_WIDTH,
1067
1127
  frozenCells: frozen.map((col) => cell(row.id, row.values, col)),
1068
- cells: visible.map((col) => cell(row.id, row.values, col))
1128
+ cells: visible.map((col) => cell(row.id, row.values, col)),
1129
+ hasActions: c.actionsForRow(row.id).length > 0,
1130
+ actionsLabel: `Actions for ${row.id}`,
1131
+ actions: c.actionsForRow(row.id).map((a) => ({ ...a, rowId: row.id }))
1069
1132
  };
1070
1133
  }) : [];
1071
1134
  const pivotRows = [];
@@ -1113,6 +1176,9 @@ function tableViewState(c, options = {}) {
1113
1176
  frozenHeaders: grid ? frozen.map(header) : [],
1114
1177
  headers: grid ? visible.map(header) : [],
1115
1178
  spacerWidth: colWindow.offsetLeft,
1179
+ actionsShown,
1180
+ actionsWidth,
1181
+ trailingSpacerWidth,
1116
1182
  totalHeight: rowWindow.totalHeight,
1117
1183
  offsetTop: rowWindow.offsetTop,
1118
1184
  rows,
@@ -1142,10 +1208,13 @@ function applyTableUi(c, intent) {
1142
1208
  c.toggleSort(intent.columnId, !!intent.shiftKey);
1143
1209
  break;
1144
1210
  case "rowClick":
1145
- c.select([intent.id], !!intent.ctrlKey || !!intent.metaKey);
1211
+ if (intent.targetTag !== "BUTTON") c.select([intent.id], !!intent.ctrlKey || !!intent.metaKey);
1146
1212
  break;
1147
1213
  case "rowDouble":
1148
- c.activateRow(intent.id);
1214
+ if (intent.targetTag !== "BUTTON") c.activateRow(intent.id);
1215
+ break;
1216
+ case "rowAction":
1217
+ c.runRowAction(intent.rowId, intent.actionId);
1149
1218
  break;
1150
1219
  case "cellDouble":
1151
1220
  if (!c.beginEdit(intent.rowId, intent.columnId)) c.activateRow(intent.rowId, intent.columnId);
@@ -1189,6 +1258,9 @@ var EMPTY = {
1189
1258
  noRows: false,
1190
1259
  grid: false,
1191
1260
  width: 0,
1261
+ actionsShown: false,
1262
+ actionsWidth: 96,
1263
+ trailingSpacerWidth: 0,
1192
1264
  headerHeight: 24,
1193
1265
  frozenHeaders: [],
1194
1266
  headers: [],
@@ -1301,7 +1373,7 @@ var TABLE_DECLARATION = {
1301
1373
  component: "chrome/Grid",
1302
1374
  props: { label: { value: "Rows" }, rowCount: { input: "view.rowCount" } },
1303
1375
  children: [
1304
- { component: "chrome/GridHeader", props: { width: { input: "view.width" }, height: { input: "view.headerHeight" } }, children: [HEADER_CELL("view.frozenHeaders"), { component: "chrome/GridSpacer", props: { width: { input: "view.spacerWidth" } } }, HEADER_CELL("view.headers")] },
1376
+ { component: "chrome/GridHeader", props: { width: { input: "view.width" }, height: { input: "view.headerHeight" } }, children: [HEADER_CELL("view.frozenHeaders"), { component: "chrome/GridSpacer", props: { width: { input: "view.spacerWidth" } } }, HEADER_CELL("view.headers"), { component: "chrome/GridSpacer", when: { input: "view.actionsShown" }, props: { width: { input: "view.trailingSpacerWidth" } } }, { component: "chrome/GridHeaderCell", when: { input: "view.actionsShown" }, props: { width: { input: "view.actionsWidth" }, sortable: { value: false } }, children: [text({ value: "Actions" })] }] },
1305
1377
  {
1306
1378
  component: "chrome/VirtualWindow",
1307
1379
  props: { totalHeight: { input: "view.totalHeight" }, offsetTop: { input: "view.offsetTop" } },
@@ -1310,12 +1382,16 @@ var TABLE_DECLARATION = {
1310
1382
  component: "chrome/GridRow",
1311
1383
  each: { input: "view.rows", key: "key" },
1312
1384
  props: { height: { item: "height" }, width: { item: "width" }, selected: { item: "selected" }, zebra: { item: "zebra" } },
1313
- on: { onClick: ui("rowClick", { id: { item: "id" }, ctrlKey: { arg: "ctrlKey" }, metaKey: { arg: "metaKey" } }), onDoubleClick: ui("rowDouble", { id: { item: "id" } }) },
1385
+ on: { onClick: ui("rowClick", { id: { item: "id" }, ctrlKey: { arg: "ctrlKey" }, metaKey: { arg: "metaKey" }, targetTag: { arg: "target.tagName" } }), onDoubleClick: ui("rowDouble", { id: { item: "id" }, targetTag: { arg: "target.tagName" } }) },
1314
1386
  children: [
1315
1387
  { component: "chrome/GridCell", when: { item: "hasGutter" }, props: { width: { item: "gutterWidth" }, gutter: { value: true } }, children: [text({ item: "ordinal" })] },
1316
1388
  CELL("frozenCells"),
1317
1389
  { component: "chrome/GridSpacer", props: { width: { input: "view.spacerWidth" } } },
1318
- CELL("cells")
1390
+ CELL("cells"),
1391
+ { component: "chrome/GridSpacer", when: { input: "view.actionsShown" }, props: { width: { input: "view.trailingSpacerWidth" } } },
1392
+ { component: "chrome/GridCell", when: { input: "view.actionsShown" }, props: { width: { input: "view.actionsWidth" } }, on: { onDoubleClick: ui("actionCellDouble") }, children: [
1393
+ { component: "chrome/ToolbarGroup", children: [{ component: "chrome/TextButton", each: { item: "actions", key: "id" }, props: { disabled: { item: "disabled" } }, children: [text({ item: "label" })], on: { onClick: ui("rowAction", { rowId: { item: "rowId" }, actionId: { item: "id" } }) } }] }
1394
+ ] }
1319
1395
  ]
1320
1396
  }
1321
1397
  ]
@@ -1399,13 +1475,18 @@ function TablePanelView({ controller, ...options }) {
1399
1475
  /* @__PURE__ */ jsxs(GridHeader, { width: view.width, height: view.headerHeight, children: [
1400
1476
  view.frozenHeaders.map(header),
1401
1477
  /* @__PURE__ */ jsx(GridSpacer, { width: view.spacerWidth }),
1402
- view.headers.map(header)
1478
+ view.headers.map(header),
1479
+ view.actionsShown ? /* @__PURE__ */ jsx(GridSpacer, { width: view.trailingSpacerWidth }) : null,
1480
+ view.actionsShown ? /* @__PURE__ */ jsx(GridHeaderCell, { width: view.actionsWidth, sortable: false, children: "Actions" }) : null
1403
1481
  ] }),
1404
- /* @__PURE__ */ jsx(VirtualWindow, { totalHeight: view.totalHeight, offsetTop: view.offsetTop, children: view.rows.map((r) => /* @__PURE__ */ jsxs(GridRow, { height: r.height, width: r.width, selected: r.selected, zebra: r.zebra, onClick: (e) => ui6({ kind: "rowClick", id: r.id, ctrlKey: e.ctrlKey, metaKey: e.metaKey }), onDoubleClick: () => ui6({ kind: "rowDouble", id: r.id }), children: [
1482
+ /* @__PURE__ */ jsx(VirtualWindow, { totalHeight: view.totalHeight, offsetTop: view.offsetTop, children: view.rows.map((r) => /* @__PURE__ */ jsxs(GridRow, { height: r.height, width: r.width, selected: r.selected, zebra: r.zebra, onClick: (e) => ui6({ kind: "rowClick", id: r.id, ctrlKey: e.ctrlKey, metaKey: e.metaKey, targetTag: e.target.tagName }), onDoubleClick: () => ui6({ kind: "rowDouble", id: r.id }), children: [
1405
1483
  r.hasGutter ? /* @__PURE__ */ jsx(GridCell, { width: r.gutterWidth, gutter: true, children: r.ordinal }) : null,
1406
1484
  r.frozenCells.map(cell),
1407
1485
  /* @__PURE__ */ jsx(GridSpacer, { width: view.spacerWidth }),
1408
- r.cells.map(cell)
1486
+ r.cells.map(cell),
1487
+ view.actionsShown ? /* @__PURE__ */ jsx(GridSpacer, { width: view.trailingSpacerWidth }) : null,
1488
+ view.actionsShown ? /* @__PURE__ */ jsx(GridCell, { width: view.actionsWidth, onDoubleClick: () => {
1489
+ }, children: /* @__PURE__ */ jsx(ToolbarGroup, { children: r.actions.map((a) => /* @__PURE__ */ jsx(TextButton, { disabled: a.disabled, onClick: () => ui6({ kind: "rowAction", rowId: a.rowId, actionId: a.id }), children: a.label }, a.id)) }) }) : null
1409
1490
  ] }, r.key)) })
1410
1491
  ] }) }) : null,
1411
1492
  view.footerShown ? /* @__PURE__ */ jsx(StatusBar, { left: /* @__PURE__ */ jsx(Text, { role: "number", children: view.countLabel }), right: view.selectedLabel ? /* @__PURE__ */ jsx(Text, { role: "meta", children: view.selectedLabel }) : null }) : null
@@ -1500,12 +1581,16 @@ function createTablePanel(options = {}) {
1500
1581
  }
1501
1582
  } else if (portId === "query") {
1502
1583
  if (isRecord(value)) controller.setQuery(value);
1584
+ } else if (portId === "rowActions") {
1585
+ controller.setRowActions(value);
1503
1586
  } else if (portId === "delta") {
1504
1587
  controller.deliverDelta(value);
1505
1588
  }
1506
1589
  },
1507
1590
  async onCommand(commandId, params) {
1508
1591
  switch (commandId) {
1592
+ case "run_row_action":
1593
+ return typeof params.rowId === "string" && typeof params.actionId === "string" && controller.runRowAction(params.rowId, params.actionId);
1509
1594
  case "refresh":
1510
1595
  controller.refresh();
1511
1596
  return;
@@ -3993,7 +4078,7 @@ import {
3993
4078
  SegmentedControl as SegmentedControl4,
3994
4079
  Select,
3995
4080
  StatusBar as StatusBar4,
3996
- TextButton,
4081
+ TextButton as TextButton2,
3997
4082
  Toolbar as Toolbar4,
3998
4083
  ToolbarGroup as ToolbarGroup3
3999
4084
  } from "@xenosystem/components/chrome";
@@ -4271,8 +4356,8 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4271
4356
  {
4272
4357
  left: view.sampled ? /* @__PURE__ */ jsx4(Badge4, { tone: "warning", title: view.sampleWarning, children: "sampled schema" }) : null,
4273
4358
  right: /* @__PURE__ */ jsxs4(ToolbarGroup3, { end: true, children: [
4274
- /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui6({ kind: "autoAssign" }), children: "Auto-assign" }),
4275
- view.manualApply ? /* @__PURE__ */ jsx4(TextButton, { strong: true, disabled: view.applyDisabled, onClick: () => ui6({ kind: "apply" }), children: "Apply" }) : null
4359
+ /* @__PURE__ */ jsx4(TextButton2, { onClick: () => ui6({ kind: "autoAssign" }), children: "Auto-assign" }),
4360
+ view.manualApply ? /* @__PURE__ */ jsx4(TextButton2, { strong: true, disabled: view.applyDisabled, onClick: () => ui6({ kind: "apply" }), children: "Apply" }) : null
4276
4361
  ] })
4277
4362
  }
4278
4363
  ),
@@ -4297,7 +4382,7 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4297
4382
  dimmed: entry.suggested,
4298
4383
  meta: /* @__PURE__ */ jsxs4(Fragment2, { children: [
4299
4384
  entry.aggregated ? /* @__PURE__ */ jsx4(Select, { size: "sm", label: "Aggregate", options: entry.aggregates, value: entry.fn, onChange: (fn) => ui6({ kind: "aggregate", shelfId: entry.shelfId, columnId: entry.columnId, fn }) }) : null,
4300
- entry.sortable ? /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui6({ kind: "direction", shelfId: entry.shelfId, columnId: entry.columnId }), children: entry.direction }) : null,
4385
+ entry.sortable ? /* @__PURE__ */ jsx4(TextButton2, { onClick: () => ui6({ kind: "direction", shelfId: entry.shelfId, columnId: entry.columnId }), children: entry.direction }) : null,
4301
4386
  entry.granularity ? /* @__PURE__ */ jsx4(Badge4, { children: entry.granularity }) : null
4302
4387
  ] }),
4303
4388
  trailing: /* @__PURE__ */ jsx4(IconButton3, { size: "sm", icon: /* @__PURE__ */ jsx4(CloseGlyph, { size: 11 }), label: entry.removeLabel, onClick: () => ui6({ kind: "remove", shelfId: entry.shelfId, columnId: entry.columnId }) })
@@ -4328,7 +4413,7 @@ function FieldsPanelView({ controller, showStats, manualApply }) {
4328
4413
  {
4329
4414
  divided: false,
4330
4415
  left: /* @__PURE__ */ jsx4(SegmentedControl4, { size: "sm", label: "Combinator", options: COMBINATORS2, value: view.combinator, onChange: (combinator) => ui6({ kind: "combinator", combinator }) }),
4331
- right: /* @__PURE__ */ jsx4(TextButton, { onClick: () => ui6({ kind: "addGroup" }), children: "+ group" })
4416
+ right: /* @__PURE__ */ jsx4(TextButton2, { onClick: () => ui6({ kind: "addGroup" }), children: "+ group" })
4332
4417
  }
4333
4418
  )
4334
4419
  ]
@@ -6602,7 +6687,7 @@ import {
6602
6687
  Select as Select2,
6603
6688
  StatusBar as StatusBar5,
6604
6689
  Text as Text3,
6605
- TextButton as TextButton2,
6690
+ TextButton as TextButton3,
6606
6691
  Toggle,
6607
6692
  Toolbar as Toolbar5,
6608
6693
  ToolbarGroup as ToolbarGroup4
@@ -6890,7 +6975,7 @@ function ConnectorsPanelView({ controller, emptyHint }) {
6890
6975
  view.form ? /* @__PURE__ */ jsxs5(Column5, { children: [
6891
6976
  /* @__PURE__ */ jsx5(Toolbar5, { left: /* @__PURE__ */ jsx5(Text3, { role: "title", children: view.formTitle }) }),
6892
6977
  view.formBroken ? /* @__PURE__ */ jsx5(EmptyState5, { title: "This connection cannot be set up", hint: view.formProblem }) : null,
6893
- view.formBroken ? /* @__PURE__ */ jsx5(Toolbar5, { divided: false, right: /* @__PURE__ */ jsx5(ToolbarGroup4, { end: true, children: /* @__PURE__ */ jsx5(TextButton2, { onClick: () => ui6({ kind: "cancel" }), children: "Close" }) }) }) : null,
6978
+ view.formBroken ? /* @__PURE__ */ jsx5(Toolbar5, { divided: false, right: /* @__PURE__ */ jsx5(ToolbarGroup4, { end: true, children: /* @__PURE__ */ jsx5(TextButton3, { onClick: () => ui6({ kind: "cancel" }), children: "Close" }) }) }) : null,
6894
6979
  view.formReady ? /* @__PURE__ */ jsxs5(ScrollArea4, { children: [
6895
6980
  view.formMessage ? /* @__PURE__ */ jsx5(Note2, { children: view.formMessage }) : null,
6896
6981
  view.redirectUri ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
@@ -6909,8 +6994,8 @@ function ConnectorsPanelView({ controller, emptyHint }) {
6909
6994
  {
6910
6995
  divided: false,
6911
6996
  right: /* @__PURE__ */ jsxs5(ToolbarGroup4, { end: true, children: [
6912
- /* @__PURE__ */ jsx5(TextButton2, { onClick: () => ui6({ kind: "cancel" }), children: "Cancel" }),
6913
- /* @__PURE__ */ jsx5(TextButton2, { strong: true, disabled: view.submitDisabled, onClick: () => ui6({ kind: "submit" }), children: "Connect" })
6997
+ /* @__PURE__ */ jsx5(TextButton3, { onClick: () => ui6({ kind: "cancel" }), children: "Cancel" }),
6998
+ /* @__PURE__ */ jsx5(TextButton3, { strong: true, disabled: view.submitDisabled, onClick: () => ui6({ kind: "submit" }), children: "Connect" })
6914
6999
  ] })
6915
7000
  }
6916
7001
  ) : null
@@ -23,7 +23,7 @@ import {
23
23
  VisibilityIcon,
24
24
  XIcon,
25
25
  XIcon2
26
- } from "../chunk-6STT7CAM.js";
26
+ } from "../chunk-6ZIWULSK.js";
27
27
  import "../chunk-2KG3PWR4.js";
28
28
 
29
29
  // src/edit/layers/panel.ts
@@ -2,7 +2,7 @@ import {
2
2
  ICONS,
3
3
  ICON_UNITS,
4
4
  Svg
5
- } from "../chunk-6STT7CAM.js";
5
+ } from "../chunk-6ZIWULSK.js";
6
6
  import "../chunk-2KG3PWR4.js";
7
7
  export {
8
8
  ICONS,
package/dist/ops/index.js CHANGED
@@ -10,7 +10,7 @@ import {
10
10
  ICONS,
11
11
  PlusGlyph,
12
12
  XGlyph
13
- } from "../chunk-6STT7CAM.js";
13
+ } from "../chunk-6ZIWULSK.js";
14
14
  import "../chunk-2KG3PWR4.js";
15
15
 
16
16
  // 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-6STT7CAM.js";
16
+ } from "../chunk-6ZIWULSK.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.18.1",
3
+ "version": "0.19.1",
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",