@updog/data-editor 0.1.41 → 0.1.43

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.
Files changed (4) hide show
  1. package/index.css +1 -1
  2. package/index.d.ts +480 -475
  3. package/index.js +2910 -2828
  4. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -443,9 +443,6 @@ declare var export_default = {
443
443
  primaryKey: "Primary key",
444
444
  },
445
445
  uploadFile: {
446
- title: "Upload file",
447
- text: "Upload a CSV, TSV, Excel, JSON or XML file to import data",
448
- textDynamic: "Upload a {{formats}} file to import data",
449
446
  parseError: "Failed to parse file",
450
447
  clickToUpload: "Click to upload",
451
448
  orDragAndDrop: "or drag and drop",
@@ -462,8 +459,6 @@ declare var export_default = {
462
459
  fetchError: "Failed to load data",
463
460
  },
464
461
  matchColumns: {
465
- title: "Match columns",
466
- text: "Map imported columns to existing columns",
467
462
  banner: "Some columns could not be automatically matched",
468
463
  importedColumns: "Imported columns",
469
464
  matchedCount: "{{matched}}/{{total}} matched",
@@ -484,25 +479,26 @@ declare var export_default = {
484
479
  "Unmatched columns won't be imported. Match this column to keep the data. You can transform columns after importing.",
485
480
  },
486
481
  sheetSelection: {
487
- title: "Select sheet",
488
- text: "This file contains multiple sheets. Choose which sheet to import.",
489
482
  rowCount: "{{count}} rows",
490
483
  emptySheet: "Empty sheet",
491
484
  },
492
485
  matchValues: {
493
- title: "Match values",
494
- text: "Review how imported values map to your column options. Adjust any mappings that need a different match.",
495
486
  importedValues: "Imported values",
496
487
  matchedCount: "{{matched}}/{{total}} matched",
497
488
  targetValue: "Target value",
498
489
  selectValuePlaceholder: "Select value",
499
490
  showMatched: "Show matched",
500
491
  allMatched: "All values are matched",
501
- unmatchedWarning: "Unmatched values won't be imported",
492
+ unmatchedWarning:
493
+ "Unmatched values won't be imported. Match this value to keep the data. You can edit values after importing.",
494
+ createOption: "Create option",
495
+ createOptionTitle: "Create option",
496
+ createOptionNameLabel: "Option name",
497
+ createOptionNamePlaceholder: "Enter option name",
498
+ createOptionNameTaken: "This option already exists",
499
+ createOptionSubmit: "Create",
502
500
  },
503
501
  primaryKey: {
504
- title: "Select primary key",
505
- text: "Choose the column that uniquely identifies each row",
506
502
  none: "No primary key",
507
503
  noneHint:
508
504
  "All rows will be appended as new entries without deduplication.",
@@ -720,6 +716,11 @@ type SelectEditorCell = {
720
716
  type: "select";
721
717
  /** The list of options shown in the dropdown. Each string is both the stored value and the display label. */
722
718
  options: string[];
719
+ /**
720
+ * Let users add options via "Create option" (matching step + grid); never
721
+ * created implicitly. Defaults to true; false for a strict closed enum.
722
+ */
723
+ enableCustomValue?: boolean;
723
724
  };
724
725
  /** Number input cell with locale-aware formatting. */
725
726
  type NumberEditorCell = {
@@ -1246,78 +1247,6 @@ declare class ChunkedProcessor<T> {
1246
1247
  cancel(): void;
1247
1248
  }
1248
1249
 
1249
- type RegisterSourceOptions = {
1250
- name: string;
1251
- id?: DataSourceId;
1252
- isDeletable?: boolean;
1253
- isInitialData?: boolean;
1254
- };
1255
- type MergeEntry<TRow> = {
1256
- row: TRow;
1257
- sourceId: DataSourceId;
1258
- isNew: boolean;
1259
- isEdited: boolean;
1260
- };
1261
- type RemovalPlan<TRow> = {
1262
- rowsToDelete: Set<TRowId>;
1263
- rowsToRestore: Array<{
1264
- rowId: TRowId;
1265
- row: TRow;
1266
- originalSourceId: DataSourceId;
1267
- isNew: boolean;
1268
- isEdited: boolean;
1269
- }>;
1270
- };
1271
- type ExtendedRemovalPlan<TRow> = RemovalPlan<TRow> & {
1272
- sourceId: DataSourceId;
1273
- repairedEntries: Array<{
1274
- sourceId: DataSourceId;
1275
- rowId: TRowId;
1276
- before: MergeEntry<TRow>;
1277
- after: MergeEntry<TRow> | null;
1278
- }>;
1279
- };
1280
- declare class SourceManager<TRow extends DataEditorRow = DataEditorRow> {
1281
- private readonly _defaultSourceId;
1282
- private readonly overrides;
1283
- private readonly sources;
1284
- private readonly mergedRows;
1285
- getSourceId(rowId: TRowId): DataSourceId;
1286
- setSourceId(rowId: TRowId, sourceId: DataSourceId): void;
1287
- deleteSourceId(rowId: TRowId): void;
1288
- getOverrides(): ReadonlyMap<TRowId, DataSourceId>;
1289
- register(options: RegisterSourceOptions): DataSourceId;
1290
- /**
1291
- * Re-insert a source using a full captured state, preserving
1292
- * isVisible, isLoading, rowCount, etc. Used by SourceLifecycle.restore.
1293
- * If the source already exists, overwrites its state.
1294
- */
1295
- restoreState(state: DataSourceState): void;
1296
- has(sourceId: DataSourceId): boolean;
1297
- get(sourceId: DataSourceId): DataSourceState | undefined;
1298
- delete(sourceId: DataSourceId): void;
1299
- setLoading(sourceId: DataSourceId, isLoading: boolean): void;
1300
- finalizeAllSources(): void;
1301
- values(): IterableIterator<DataSourceState>;
1302
- getHiddenSourceIds(): Set<DataSourceId>;
1303
- saveMergeSnapshot(sourceId: DataSourceId, rowId: TRowId, existingRow: TRow, previousSourceId: DataSourceId, isNew: boolean, isEdited: boolean): void;
1304
- /**
1305
- * Public so commands can re-install merge entries during undo of a remove.
1306
- */
1307
- restoreMergeEntry(sourceId: DataSourceId, rowId: TRowId, entry: MergeEntry<TRow>): void;
1308
- /**
1309
- * Pure — computes the full removal plan without mutating any state.
1310
- * Callers run applyRemovalPlan(plan) to commit.
1311
- */
1312
- planRemoval(sourceId: DataSourceId): ExtendedRemovalPlan<TRow> | null;
1313
- /**
1314
- * Mutates internal state per plan produced by planRemoval.
1315
- */
1316
- applyRemovalPlan(plan: ExtendedRemovalPlan<TRow>): void;
1317
- clear(): void;
1318
- private getUniqueName;
1319
- }
1320
-
1321
1250
  /**
1322
1251
  * Internal contract: the surface a Scale client exposes to the SDK's data
1323
1252
  * layer. Implemented by `ScaleClient`. Not part of the public SDK API —
@@ -1340,185 +1269,143 @@ type ScaleClientApi<TRow extends DataEditorRow = DataEditorRow, TFilters = Recor
1340
1269
  scrollSensitivity?: number;
1341
1270
  };
1342
1271
 
1343
- type ServerDataManagerDeps<TRow extends DataEditorRow = DataEditorRow> = {
1344
- clear(): void;
1345
- setLoading(isLoading: boolean): void;
1346
- registerSource(options: RegisterSourceOptions): DataSourceId;
1347
- setSourceLoading(sourceId: DataSourceId, isLoading: boolean): void;
1348
- getLocalRowCount(): number;
1349
- replaceServerRows(sourceId: DataSourceId, rows: ServerRow<TRow>[], offset: number, counts?: ServerQueryCounts): void;
1350
- appendServerRows(rows: ServerRow<TRow>[], counts?: ServerQueryCounts): void;
1351
- prependServerRows(rows: ServerRow<TRow>[], offset: number, counts?: ServerQueryCounts): void;
1352
- applyServerRowMeta(rows: ServerRow<TRow>[], counts?: ServerQueryCounts): void;
1353
- };
1354
- type FetchDirection = "forward" | "backward" | "jump";
1355
- declare class ServerDataManager<TRow extends DataEditorRow = DataEditorRow> {
1356
- private _offset;
1357
- private _totalCount;
1358
- private _isFetching;
1359
- private readonly _pageSize;
1360
- private readonly _maxBufferRows;
1361
- private _filters;
1362
- private _sources;
1363
- private _sort;
1364
- private _filterOptions;
1365
- private _filterOptionsFetched;
1366
- private _abortController;
1367
- private _syncAbort;
1368
- private _lastVisibleStart;
1369
- private _debouncedFetch;
1370
- private readonly _config;
1371
- private readonly _dataStoreRef;
1372
- private readonly _sourceLabel;
1373
- private _onChanged;
1374
- constructor(config: ScaleClientApi<TRow>, dataStoreRef: ServerDataManagerDeps<TRow>, sourceLabel: string);
1375
- get offset(): number;
1376
- get totalCount(): number | null;
1377
- get isFetching(): boolean;
1378
- get pageSize(): number;
1379
- get maxBufferRows(): number;
1380
- setOnChanged(callback: () => void): void;
1381
- setOffset(offset: number): void;
1382
- setTotalCount(count: number): void;
1383
- private setFetching;
1384
- getExcess(currentCount: number, newCount: number): number;
1385
- shouldFetch(visibleStart: number, visibleEnd: number, loadedCount: number): FetchDirection | null;
1386
- /**
1387
- * Full reload — abort in-flight, clear store, fetch first page.
1388
- * Called on initial load, search/filter/sort changes, and resetFilters.
1389
- */
1390
- reload(): void;
1391
- /**
1392
- * Scroll-driven pagination with velocity-based debouncing.
1393
- * Called from CanvasGrid on every scroll event.
1394
- */
1395
- handleScroll(visibleStart: number, visibleEnd: number): void;
1396
- /**
1397
- * Fetch a single page based on scroll position and current window state.
1398
- */
1399
- private fetchPage;
1400
- /**
1401
- * Re-query the current viewport and replace row data, metadata, and counts.
1402
- * Called after successful edits and find-and-replace mutations.
1403
- * Each call aborts the previous in-flight sync.
1404
- */
1405
- syncCurrentView(): void;
1406
- /**
1407
- * Merge filter keys into server filter state and reload.
1408
- * Called by DataStore.setFilters() in server mode and by filter components.
1409
- */
1410
- setFilters(filters: Partial<Filters>): void;
1411
- /**
1412
- * Set sort state and reload.
1413
- */
1414
- setSort(sort: SortState): void;
1415
- /**
1416
- * Restrict query to visible sources and reload.
1417
- * Pass `undefined` to include all sources.
1418
- */
1419
- setSources(sources: string[] | undefined): void;
1420
- /**
1421
- * Clear all filters and sort, then reload.
1422
- */
1423
- resetFilters(): void;
1424
- /**
1425
- * One-time fetch of filter option dictionaries for sidebar filter controls.
1426
- */
1427
- fetchFilterOptions(): void;
1428
- getFilterOptions(): FilterOptionsResponse | null;
1429
- get onEdit(): (params: EditParams, options?: ServerCallOptions) => Promise<EditResponse | void>;
1430
- get filters(): Record<string, unknown>;
1431
- get sort(): SortState;
1432
- get sources(): string[] | undefined;
1433
- get onSourceRemove(): ((params: SourceRemoveParams) => Promise<void>) | undefined;
1434
- get onColumnDelete(): ((params: ColumnDeleteParams) => Promise<EditResponse | void>) | undefined;
1435
- get onColumnEdit(): ((params: ColumnEditParams) => Promise<EditResponse | void>) | undefined;
1436
- get hasExport(): boolean;
1437
- private _exportAbortController;
1438
- export(format: DataEditorFormat, allRows: boolean, rtl: boolean): Promise<void>;
1439
- clear(): void;
1440
- destroy(): void;
1441
- }
1442
-
1443
1272
  /**
1444
- * A post-resolution selection rectangle in stable coordinate space.
1445
- * Produced by grid-layer resolvers from CellRange[] in grid-index space.
1446
- * Consumed by DataStore operations and server sync.
1273
+ * A single operation the LLM wants to apply to rows in the current filtered view.
1274
+ *
1275
+ * - `edit` `fn` is `(r, ctx) => void`. Mutates `r` in place. Changed fields
1276
+ * become column deltas. Rows with no changes are no-ops.
1277
+ * - `delete` — `fn` is `(r, ctx) => boolean`. Truthy means "flag this row for
1278
+ * deletion". Soft delete via `DeleteRowCommand`.
1447
1279
  */
1448
- type SelectionRect = {
1449
- readonly fields: readonly string[];
1450
- readonly rowIds: readonly TRowId[];
1280
+ type ChatOp = {
1281
+ action: "edit";
1282
+ fn: string;
1283
+ } | {
1284
+ action: "delete";
1285
+ fn: string;
1451
1286
  };
1452
-
1453
1287
  /**
1454
- * ServerEditBuilder Stateless coordinate translator for server-delegated edits.
1455
- *
1456
- * Converts frontend coordinates (TRowId, column index, grid ranges) into
1457
- * `EditParams` with `Region[]` that the server can interpret.
1458
- *
1459
- * Does NOT call `onEdit`. Only builds params.
1460
- * DataStore calls the builder, then sends the result to the server.
1288
+ * A single chunk in the stream returned from `DataEditorChat.onMessage`.
1461
1289
  *
1462
- * Responsibilities:
1463
- * - TRowId ServerRowId translation via primaryKey
1464
- * - Grid index column ID translation via columns array
1465
- * - Filter/sort context attachment from ServerDataManager
1290
+ * - `status` — progress message shown while processing (e.g. "Analyzing 500 rows...").
1291
+ * - `message` chat reply shown to the user.
1292
+ * - `rows` updated rows to apply to the grid. Matched by `primaryKey`.
1293
+ * - `ops` — array of per-row operations (edits and/or deletes) to apply in order.
1466
1294
  */
1467
-
1468
- type ServerEditDeps<TRow extends DataEditorRow = DataEditorRow> = {
1469
- getPrimaryKey: () => string;
1470
- getRowById: (id: TRowId) => TRow | undefined;
1471
- getColumnIds: () => string[];
1472
- getFilters: () => Record<string, unknown>;
1473
- getSort: () => SortState;
1474
- getLockedColumns: () => ReadonlyMap<string, ColumnLockMode>;
1295
+ type ChatResponseChunk<TRow extends DataEditorRow = DataEditorRow> = {
1296
+ type: "status";
1297
+ content: string;
1298
+ } | {
1299
+ type: "message";
1300
+ content: string;
1301
+ } | {
1302
+ type: "rows";
1303
+ content: TRow[];
1304
+ } | {
1305
+ type: "ops";
1306
+ content: ChatOp[];
1475
1307
  };
1476
- declare class ServerEditBuilder<TRow extends DataEditorRow = DataEditorRow> {
1477
- private readonly _deps;
1478
- constructor(deps: ServerEditDeps<TRow>);
1479
- resolveServerRowId(rowId: TRowId): ServerRowId | undefined;
1480
- buildRegion(rowIds: TRowId[], columnIds: string[]): Region;
1481
- /**
1482
- * Collapse rowIds × columnIds into minimal Region[].
1483
- * - All columns → omit column fields (row-only regions).
1484
- * - Contiguous columns in schema order → single fromColumn/toColumn span.
1485
- * - Non-contiguous → one region per contiguous column group.
1486
- * Rows are expressed as fromRow/toRow using first/last of the provided array.
1487
- */
1488
- buildRegions(rowIds: TRowId[], columnIds: string[]): Region[];
1489
- /**
1490
- * Collapse columnIds into minimal column-only Region[] (all rows implied).
1491
- * - All columns `{ allSelected: true }`.
1492
- * - Contiguous in schema order → single `{ fromColumn, toColumn }`.
1493
- * - Non-contiguous one region per contiguous group.
1494
- */
1495
- buildColumnRegions(columnIds: string[]): Region[];
1496
- /**
1497
- * Build minimal Region[] from multiple selection rectangles.
1498
- * Each rect is collapsed independently, preserving disjoint selections.
1499
- */
1500
- buildRegionsFromRects(rects: SelectionRect[]): Region[];
1501
- buildAllSelectedRegion(): Region;
1502
- buildColumnRegion(columnId: string): Region;
1503
- buildRowRegion(fromRowId: TRowId, toRowId: TRowId): Region;
1504
- cellEdit(rowId: TRowId, field: string, value: unknown): EditParams;
1505
- clear(target: Region[]): EditParams;
1506
- paste(source: Region[], target: Region[], cut?: boolean): EditParams;
1507
- pasteExternal(target: Region[], values: unknown[][]): EditParams;
1508
- fill(source: Region[], target: Region[]): EditParams;
1509
- transform(target: Region[], transform: TransformParams): EditParams;
1510
- deleteRows(rowRanges: [TRowId, TRowId][]): EditParams;
1511
- restoreRows(rowRanges: [TRowId, TRowId][]): EditParams;
1512
- deleteAllRows(): EditParams;
1513
- restoreAllRows(): EditParams;
1514
- insertRow(anchorRowId: TRowId | undefined, position: InsertParams["position"], values: unknown[][], columnIds: string[]): EditParams;
1308
+ /** Status of a row in the chat sample, relative to its origin snapshot. */
1309
+ type ChatRowStatus = "new" | "edited" | "original";
1310
+ /** A sample row handed to the chat callback, with its current status and validation errors. */
1311
+ type ChatRow<TRow extends DataEditorRow = DataEditorRow> = {
1312
+ /** Row data keyed by column ID. */
1313
+ data: TRow;
1314
+ /** Whether the row was newly added, edited, or is unchanged. */
1315
+ status: ChatRowStatus;
1316
+ /** Validation errors keyed by column ID. */
1317
+ errors: Record<string, string[]>;
1318
+ /** The source this row belongs to. */
1319
+ source: string;
1320
+ };
1321
+ /** Aggregated error count across the current view, grouped by field and message. */
1322
+ type ChatErrorSummary = {
1323
+ /** Column ID where the error occurred. */
1324
+ field: string;
1325
+ /** The validation message. */
1326
+ message: string;
1327
+ /** How many rows hit this error. */
1328
+ count: number;
1329
+ /** A few example values that triggered the error. */
1330
+ examples: string[];
1331
+ };
1332
+ /**
1333
+ * Context about the current dataset, passed to `loadSuggestions` and extended
1334
+ * into `ChatContext` for `onMessage`.
1335
+ */
1336
+ type ChatDataContext<TRow extends DataEditorRow = DataEditorRow> = {
1337
+ /** Full column definitions. */
1338
+ columns: DataEditorColumn[];
1339
+ /** Row identifier field. */
1340
+ primaryKey: keyof TRow;
1341
+ /** Total rows in the dataset. */
1342
+ totalRowCount: number;
1343
+ /** Rows in the current filtered view. */
1344
+ filteredRowCount: number;
1345
+ /** Sample rows, with status and errors. Size controlled by `sampleSize`. */
1346
+ sample: ChatRow<TRow>[];
1347
+ /** Aggregated error counts by field and message. */
1348
+ errorSummary: ChatErrorSummary[];
1349
+ /** Access all rows. Use for full-dataset operations. */
1350
+ getRows: () => ChatRow<TRow>[];
1351
+ };
1352
+ /**
1353
+ * The full context passed to `DataEditorChat.onMessage` when the user sends
1354
+ * a prompt. Includes the prompt itself and all dataset context.
1355
+ */
1356
+ type ChatContext<TRow extends DataEditorRow = DataEditorRow> = ChatDataContext<TRow> & {
1357
+ /** The user's chat prompt. */
1358
+ message: string;
1359
+ };
1360
+ /**
1361
+ * Bring-your-own-AI chat configuration. When provided via the `chat` prop,
1362
+ * the editor shows a chat panel alongside the grid. You own the AI
1363
+ * integration; the SDK hands you dataset context and renders the streamed
1364
+ * response.
1365
+ *
1366
+ * @example
1367
+ * ```ts
1368
+ * chat={{
1369
+ * sampleSize: 50,
1370
+ * onMessage: async function* (context) {
1371
+ * yield { type: "status", content: "Thinking..." };
1372
+ * const res = await fetch("/api/ai", {
1373
+ * method: "POST",
1374
+ * body: JSON.stringify({
1375
+ * prompt: context.message,
1376
+ * columns: context.columns,
1377
+ * sample: context.sample.map(r => r.data),
1378
+ * errors: context.errorSummary,
1379
+ * }),
1380
+ * }).then(r => r.json());
1381
+ * yield { type: "rows", content: res.updatedRows };
1382
+ * yield { type: "ops", content: res.ops };
1383
+ * yield { type: "message", content: res.reply };
1384
+ * },
1385
+ * }}
1386
+ * ```
1387
+ */
1388
+ type DataEditorChat<TRow extends DataEditorRow = DataEditorRow> = {
1515
1389
  /**
1516
- * Returns null when all columns are selected (caller decides representation).
1517
- * Otherwise returns contiguous column spans as `{ fromColumn, toColumn }` regions.
1390
+ * How many rows to include in the context sample. The SDK picks a
1391
+ * representative slice including rows with errors. When omitted, the SDK decides.
1518
1392
  */
1519
- private collapseColumns;
1520
- private viewContext;
1521
- }
1393
+ sampleSize?: number;
1394
+ /** Title shown above the suggestion list when the chat is empty. */
1395
+ emptyTitle?: string;
1396
+ /** How many suggestions to request from `loadSuggestions`. */
1397
+ suggestionsCount?: number;
1398
+ /** Optional prompt generator for the empty-state suggestion chips. */
1399
+ loadSuggestions?: (context: ChatDataContext<TRow>) => Promise<string[]>;
1400
+ /**
1401
+ * Called when the user sends a message. Receives full dataset context.
1402
+ * Returns an async iterable of response chunks — the SDK streams them
1403
+ * into the UI.
1404
+ */
1405
+ onMessage: (context: ChatContext<TRow>) => AsyncIterable<ChatResponseChunk<TRow>>;
1406
+ /** Called when the user cancels a pending request. Use to abort your API call. */
1407
+ onCancel?: () => void;
1408
+ };
1522
1409
 
1523
1410
  /**
1524
1411
  * Categories of internal errors surfaced through the `onError` callback.
@@ -1559,6 +1446,341 @@ declare class ErrorHandler {
1559
1446
  handleError(error: UpdogError): void;
1560
1447
  }
1561
1448
 
1449
+ type FormulaCellContext = {
1450
+ value: unknown;
1451
+ field: string;
1452
+ rowId: TRowId;
1453
+ getField: (field: string) => unknown;
1454
+ /**
1455
+ * Positional arguments for expression-compiled formulas.
1456
+ * Populated by the expression evaluator when invoking multi-arg function
1457
+ * calls. Single-arg formulas (UPPER, TRIM, etc.) still read from `value`.
1458
+ * Undefined for all non-expression call sites — existing code is unaffected.
1459
+ */
1460
+ args?: readonly unknown[];
1461
+ };
1462
+ type FormulaParamType = "string" | "number" | "boolean" | "select";
1463
+ type FormulaParam = {
1464
+ name: string;
1465
+ label: string;
1466
+ type: FormulaParamType;
1467
+ required?: boolean;
1468
+ defaultValue?: unknown;
1469
+ options?: Array<{
1470
+ id: string;
1471
+ text: string;
1472
+ }>;
1473
+ };
1474
+ type ColumnInputKind = "single" | "multiple";
1475
+ type ColumnInput = {
1476
+ name: string;
1477
+ label: string;
1478
+ kind: ColumnInputKind;
1479
+ required?: boolean;
1480
+ };
1481
+ type FormulaCategory = "text" | "number" | "logic" | "custom";
1482
+ type FormulaArity = {
1483
+ /** Minimum number of positional arguments. 0 means "callable with no args". */
1484
+ min: number;
1485
+ /** Maximum number of positional arguments. Use Number.POSITIVE_INFINITY for variadic. */
1486
+ max: number;
1487
+ };
1488
+ type FormulaBase = {
1489
+ name: string;
1490
+ label: string;
1491
+ category: FormulaCategory;
1492
+ description?: string;
1493
+ columns?: ColumnInput[];
1494
+ params: FormulaParam[];
1495
+ /**
1496
+ * The call signature of this formula when invoked from the expression
1497
+ * language. Required. For shortcut formulas (UPPER, TRIM, ...) use
1498
+ * { min: 1, max: 1 }. For CLEAR use { min: 0, max: 0 }. For MERGE use
1499
+ * { min: 2, max: Number.POSITIVE_INFINITY }.
1500
+ */
1501
+ arity: FormulaArity;
1502
+ /** Parameter signature shown in autocomplete, without the function name. e.g. "(text, count)" */
1503
+ syntax?: string;
1504
+ /**
1505
+ * Whether this formula may be invoked from the expression parser.
1506
+ * Defaults to true when omitted. MERGE and SPLIT set this to false
1507
+ * because they have dedicated modals that supply their non-expression
1508
+ * params (column lists, separator, ...).
1509
+ */
1510
+ expressionCallable?: boolean;
1511
+ };
1512
+ type CellFormula = FormulaBase & {
1513
+ kind: "cell";
1514
+ compute: (ctx: FormulaCellContext, params: Record<string, unknown>) => unknown;
1515
+ };
1516
+ type RowFormula = FormulaBase & {
1517
+ kind: "row";
1518
+ targetFields: (params: Record<string, unknown>) => string[];
1519
+ compute: (ctx: FormulaCellContext, params: Record<string, unknown>) => Record<string, unknown>;
1520
+ };
1521
+ type FormulaDefinition = CellFormula | RowFormula;
1522
+
1523
+ declare class FormulaRegistry {
1524
+ private readonly formulas;
1525
+ register(formula: FormulaDefinition): void;
1526
+ get(name: string): FormulaDefinition | undefined;
1527
+ getAll(): FormulaDefinition[];
1528
+ getByCategory(category: string): FormulaDefinition[];
1529
+ getExpressionCallable(): FormulaDefinition[];
1530
+ }
1531
+
1532
+ type RegisterSourceOptions = {
1533
+ name: string;
1534
+ id?: DataSourceId;
1535
+ isDeletable?: boolean;
1536
+ isInitialData?: boolean;
1537
+ };
1538
+ type MergeEntry<TRow> = {
1539
+ row: TRow;
1540
+ sourceId: DataSourceId;
1541
+ isNew: boolean;
1542
+ isEdited: boolean;
1543
+ };
1544
+ type RemovalPlan<TRow> = {
1545
+ rowsToDelete: Set<TRowId>;
1546
+ rowsToRestore: Array<{
1547
+ rowId: TRowId;
1548
+ row: TRow;
1549
+ originalSourceId: DataSourceId;
1550
+ isNew: boolean;
1551
+ isEdited: boolean;
1552
+ }>;
1553
+ };
1554
+ type ExtendedRemovalPlan<TRow> = RemovalPlan<TRow> & {
1555
+ sourceId: DataSourceId;
1556
+ repairedEntries: Array<{
1557
+ sourceId: DataSourceId;
1558
+ rowId: TRowId;
1559
+ before: MergeEntry<TRow>;
1560
+ after: MergeEntry<TRow> | null;
1561
+ }>;
1562
+ };
1563
+ declare class SourceManager<TRow extends DataEditorRow = DataEditorRow> {
1564
+ private readonly _defaultSourceId;
1565
+ private readonly overrides;
1566
+ private readonly sources;
1567
+ private readonly mergedRows;
1568
+ getSourceId(rowId: TRowId): DataSourceId;
1569
+ setSourceId(rowId: TRowId, sourceId: DataSourceId): void;
1570
+ deleteSourceId(rowId: TRowId): void;
1571
+ getOverrides(): ReadonlyMap<TRowId, DataSourceId>;
1572
+ register(options: RegisterSourceOptions): DataSourceId;
1573
+ /**
1574
+ * Re-insert a source using a full captured state, preserving
1575
+ * isVisible, isLoading, rowCount, etc. Used by SourceLifecycle.restore.
1576
+ * If the source already exists, overwrites its state.
1577
+ */
1578
+ restoreState(state: DataSourceState): void;
1579
+ has(sourceId: DataSourceId): boolean;
1580
+ get(sourceId: DataSourceId): DataSourceState | undefined;
1581
+ delete(sourceId: DataSourceId): void;
1582
+ setLoading(sourceId: DataSourceId, isLoading: boolean): void;
1583
+ finalizeAllSources(): void;
1584
+ values(): IterableIterator<DataSourceState>;
1585
+ getHiddenSourceIds(): Set<DataSourceId>;
1586
+ saveMergeSnapshot(sourceId: DataSourceId, rowId: TRowId, existingRow: TRow, previousSourceId: DataSourceId, isNew: boolean, isEdited: boolean): void;
1587
+ /**
1588
+ * Public so commands can re-install merge entries during undo of a remove.
1589
+ */
1590
+ restoreMergeEntry(sourceId: DataSourceId, rowId: TRowId, entry: MergeEntry<TRow>): void;
1591
+ /**
1592
+ * Pure — computes the full removal plan without mutating any state.
1593
+ * Callers run applyRemovalPlan(plan) to commit.
1594
+ */
1595
+ planRemoval(sourceId: DataSourceId): ExtendedRemovalPlan<TRow> | null;
1596
+ /**
1597
+ * Mutates internal state per plan produced by planRemoval.
1598
+ */
1599
+ applyRemovalPlan(plan: ExtendedRemovalPlan<TRow>): void;
1600
+ clear(): void;
1601
+ private getUniqueName;
1602
+ }
1603
+
1604
+ type ServerDataManagerDeps<TRow extends DataEditorRow = DataEditorRow> = {
1605
+ clear(): void;
1606
+ setLoading(isLoading: boolean): void;
1607
+ registerSource(options: RegisterSourceOptions): DataSourceId;
1608
+ setSourceLoading(sourceId: DataSourceId, isLoading: boolean): void;
1609
+ getLocalRowCount(): number;
1610
+ replaceServerRows(sourceId: DataSourceId, rows: ServerRow<TRow>[], offset: number, counts?: ServerQueryCounts): void;
1611
+ appendServerRows(rows: ServerRow<TRow>[], counts?: ServerQueryCounts): void;
1612
+ prependServerRows(rows: ServerRow<TRow>[], offset: number, counts?: ServerQueryCounts): void;
1613
+ applyServerRowMeta(rows: ServerRow<TRow>[], counts?: ServerQueryCounts): void;
1614
+ };
1615
+ type FetchDirection = "forward" | "backward" | "jump";
1616
+ declare class ServerDataManager<TRow extends DataEditorRow = DataEditorRow> {
1617
+ private _offset;
1618
+ private _totalCount;
1619
+ private _isFetching;
1620
+ private readonly _pageSize;
1621
+ private readonly _maxBufferRows;
1622
+ private _filters;
1623
+ private _sources;
1624
+ private _sort;
1625
+ private _filterOptions;
1626
+ private _filterOptionsFetched;
1627
+ private _abortController;
1628
+ private _syncAbort;
1629
+ private _lastVisibleStart;
1630
+ private _debouncedFetch;
1631
+ private readonly _config;
1632
+ private readonly _dataStoreRef;
1633
+ private readonly _sourceLabel;
1634
+ private _onChanged;
1635
+ constructor(config: ScaleClientApi<TRow>, dataStoreRef: ServerDataManagerDeps<TRow>, sourceLabel: string);
1636
+ get offset(): number;
1637
+ get totalCount(): number | null;
1638
+ get isFetching(): boolean;
1639
+ get pageSize(): number;
1640
+ get maxBufferRows(): number;
1641
+ setOnChanged(callback: () => void): void;
1642
+ setOffset(offset: number): void;
1643
+ setTotalCount(count: number): void;
1644
+ private setFetching;
1645
+ getExcess(currentCount: number, newCount: number): number;
1646
+ shouldFetch(visibleStart: number, visibleEnd: number, loadedCount: number): FetchDirection | null;
1647
+ /**
1648
+ * Full reload — abort in-flight, clear store, fetch first page.
1649
+ * Called on initial load, search/filter/sort changes, and resetFilters.
1650
+ */
1651
+ reload(): void;
1652
+ /**
1653
+ * Scroll-driven pagination with velocity-based debouncing.
1654
+ * Called from CanvasGrid on every scroll event.
1655
+ */
1656
+ handleScroll(visibleStart: number, visibleEnd: number): void;
1657
+ /**
1658
+ * Fetch a single page based on scroll position and current window state.
1659
+ */
1660
+ private fetchPage;
1661
+ /**
1662
+ * Re-query the current viewport and replace row data, metadata, and counts.
1663
+ * Called after successful edits and find-and-replace mutations.
1664
+ * Each call aborts the previous in-flight sync.
1665
+ */
1666
+ syncCurrentView(): void;
1667
+ /**
1668
+ * Merge filter keys into server filter state and reload.
1669
+ * Called by DataStore.setFilters() in server mode and by filter components.
1670
+ */
1671
+ setFilters(filters: Partial<Filters>): void;
1672
+ /**
1673
+ * Set sort state and reload.
1674
+ */
1675
+ setSort(sort: SortState): void;
1676
+ /**
1677
+ * Restrict query to visible sources and reload.
1678
+ * Pass `undefined` to include all sources.
1679
+ */
1680
+ setSources(sources: string[] | undefined): void;
1681
+ /**
1682
+ * Clear all filters and sort, then reload.
1683
+ */
1684
+ resetFilters(): void;
1685
+ /**
1686
+ * One-time fetch of filter option dictionaries for sidebar filter controls.
1687
+ */
1688
+ fetchFilterOptions(): void;
1689
+ getFilterOptions(): FilterOptionsResponse | null;
1690
+ get onEdit(): (params: EditParams, options?: ServerCallOptions) => Promise<EditResponse | void>;
1691
+ get filters(): Record<string, unknown>;
1692
+ get sort(): SortState;
1693
+ get sources(): string[] | undefined;
1694
+ get onSourceRemove(): ((params: SourceRemoveParams) => Promise<void>) | undefined;
1695
+ get onColumnDelete(): ((params: ColumnDeleteParams) => Promise<EditResponse | void>) | undefined;
1696
+ get onColumnEdit(): ((params: ColumnEditParams) => Promise<EditResponse | void>) | undefined;
1697
+ get hasExport(): boolean;
1698
+ private _exportAbortController;
1699
+ export(format: DataEditorFormat, allRows: boolean, rtl: boolean): Promise<void>;
1700
+ clear(): void;
1701
+ destroy(): void;
1702
+ }
1703
+
1704
+ /**
1705
+ * A post-resolution selection rectangle in stable coordinate space.
1706
+ * Produced by grid-layer resolvers from CellRange[] in grid-index space.
1707
+ * Consumed by DataStore operations and server sync.
1708
+ */
1709
+ type SelectionRect = {
1710
+ readonly fields: readonly string[];
1711
+ readonly rowIds: readonly TRowId[];
1712
+ };
1713
+
1714
+ /**
1715
+ * ServerEditBuilder — Stateless coordinate translator for server-delegated edits.
1716
+ *
1717
+ * Converts frontend coordinates (TRowId, column index, grid ranges) into
1718
+ * `EditParams` with `Region[]` that the server can interpret.
1719
+ *
1720
+ * Does NOT call `onEdit`. Only builds params.
1721
+ * DataStore calls the builder, then sends the result to the server.
1722
+ *
1723
+ * Responsibilities:
1724
+ * - TRowId → ServerRowId translation via primaryKey
1725
+ * - Grid index → column ID translation via columns array
1726
+ * - Filter/sort context attachment from ServerDataManager
1727
+ */
1728
+
1729
+ type ServerEditDeps<TRow extends DataEditorRow = DataEditorRow> = {
1730
+ getPrimaryKey: () => string;
1731
+ getRowById: (id: TRowId) => TRow | undefined;
1732
+ getColumnIds: () => string[];
1733
+ getFilters: () => Record<string, unknown>;
1734
+ getSort: () => SortState;
1735
+ getLockedColumns: () => ReadonlyMap<string, ColumnLockMode>;
1736
+ };
1737
+ declare class ServerEditBuilder<TRow extends DataEditorRow = DataEditorRow> {
1738
+ private readonly _deps;
1739
+ constructor(deps: ServerEditDeps<TRow>);
1740
+ resolveServerRowId(rowId: TRowId): ServerRowId | undefined;
1741
+ buildRegion(rowIds: TRowId[], columnIds: string[]): Region;
1742
+ /**
1743
+ * Collapse rowIds × columnIds into minimal Region[].
1744
+ * - All columns → omit column fields (row-only regions).
1745
+ * - Contiguous columns in schema order → single fromColumn/toColumn span.
1746
+ * - Non-contiguous → one region per contiguous column group.
1747
+ * Rows are expressed as fromRow/toRow using first/last of the provided array.
1748
+ */
1749
+ buildRegions(rowIds: TRowId[], columnIds: string[]): Region[];
1750
+ /**
1751
+ * Collapse columnIds into minimal column-only Region[] (all rows implied).
1752
+ * - All columns → `{ allSelected: true }`.
1753
+ * - Contiguous in schema order → single `{ fromColumn, toColumn }`.
1754
+ * - Non-contiguous → one region per contiguous group.
1755
+ */
1756
+ buildColumnRegions(columnIds: string[]): Region[];
1757
+ /**
1758
+ * Build minimal Region[] from multiple selection rectangles.
1759
+ * Each rect is collapsed independently, preserving disjoint selections.
1760
+ */
1761
+ buildRegionsFromRects(rects: SelectionRect[]): Region[];
1762
+ buildAllSelectedRegion(): Region;
1763
+ buildColumnRegion(columnId: string): Region;
1764
+ buildRowRegion(fromRowId: TRowId, toRowId: TRowId): Region;
1765
+ cellEdit(rowId: TRowId, field: string, value: unknown): EditParams;
1766
+ clear(target: Region[]): EditParams;
1767
+ paste(source: Region[], target: Region[], cut?: boolean): EditParams;
1768
+ pasteExternal(target: Region[], values: unknown[][]): EditParams;
1769
+ fill(source: Region[], target: Region[]): EditParams;
1770
+ transform(target: Region[], transform: TransformParams): EditParams;
1771
+ deleteRows(rowRanges: [TRowId, TRowId][]): EditParams;
1772
+ restoreRows(rowRanges: [TRowId, TRowId][]): EditParams;
1773
+ deleteAllRows(): EditParams;
1774
+ restoreAllRows(): EditParams;
1775
+ insertRow(anchorRowId: TRowId | undefined, position: InsertParams["position"], values: unknown[][], columnIds: string[]): EditParams;
1776
+ /**
1777
+ * Returns null when all columns are selected (caller decides representation).
1778
+ * Otherwise returns contiguous column spans as `{ fromColumn, toColumn }` regions.
1779
+ */
1780
+ private collapseColumns;
1781
+ private viewContext;
1782
+ }
1783
+
1562
1784
  /**
1563
1785
  * DirtyTracker — Change classification and revert detection for rows.
1564
1786
  *
@@ -1979,89 +2201,6 @@ type FillSpec = {
1979
2201
  rowIdToFillIndex: ReadonlyMap<TRowId, number>;
1980
2202
  };
1981
2203
 
1982
- type FormulaCellContext = {
1983
- value: unknown;
1984
- field: string;
1985
- rowId: TRowId;
1986
- getField: (field: string) => unknown;
1987
- /**
1988
- * Positional arguments for expression-compiled formulas.
1989
- * Populated by the expression evaluator when invoking multi-arg function
1990
- * calls. Single-arg formulas (UPPER, TRIM, etc.) still read from `value`.
1991
- * Undefined for all non-expression call sites — existing code is unaffected.
1992
- */
1993
- args?: readonly unknown[];
1994
- };
1995
- type FormulaParamType = "string" | "number" | "boolean" | "select";
1996
- type FormulaParam = {
1997
- name: string;
1998
- label: string;
1999
- type: FormulaParamType;
2000
- required?: boolean;
2001
- defaultValue?: unknown;
2002
- options?: Array<{
2003
- id: string;
2004
- text: string;
2005
- }>;
2006
- };
2007
- type ColumnInputKind = "single" | "multiple";
2008
- type ColumnInput = {
2009
- name: string;
2010
- label: string;
2011
- kind: ColumnInputKind;
2012
- required?: boolean;
2013
- };
2014
- type FormulaCategory = "text" | "number" | "logic" | "custom";
2015
- type FormulaArity = {
2016
- /** Minimum number of positional arguments. 0 means "callable with no args". */
2017
- min: number;
2018
- /** Maximum number of positional arguments. Use Number.POSITIVE_INFINITY for variadic. */
2019
- max: number;
2020
- };
2021
- type FormulaBase = {
2022
- name: string;
2023
- label: string;
2024
- category: FormulaCategory;
2025
- description?: string;
2026
- columns?: ColumnInput[];
2027
- params: FormulaParam[];
2028
- /**
2029
- * The call signature of this formula when invoked from the expression
2030
- * language. Required. For shortcut formulas (UPPER, TRIM, ...) use
2031
- * { min: 1, max: 1 }. For CLEAR use { min: 0, max: 0 }. For MERGE use
2032
- * { min: 2, max: Number.POSITIVE_INFINITY }.
2033
- */
2034
- arity: FormulaArity;
2035
- /** Parameter signature shown in autocomplete, without the function name. e.g. "(text, count)" */
2036
- syntax?: string;
2037
- /**
2038
- * Whether this formula may be invoked from the expression parser.
2039
- * Defaults to true when omitted. MERGE and SPLIT set this to false
2040
- * because they have dedicated modals that supply their non-expression
2041
- * params (column lists, separator, ...).
2042
- */
2043
- expressionCallable?: boolean;
2044
- };
2045
- type CellFormula = FormulaBase & {
2046
- kind: "cell";
2047
- compute: (ctx: FormulaCellContext, params: Record<string, unknown>) => unknown;
2048
- };
2049
- type RowFormula = FormulaBase & {
2050
- kind: "row";
2051
- targetFields: (params: Record<string, unknown>) => string[];
2052
- compute: (ctx: FormulaCellContext, params: Record<string, unknown>) => Record<string, unknown>;
2053
- };
2054
- type FormulaDefinition = CellFormula | RowFormula;
2055
-
2056
- declare class FormulaRegistry {
2057
- private readonly formulas;
2058
- register(formula: FormulaDefinition): void;
2059
- get(name: string): FormulaDefinition | undefined;
2060
- getAll(): FormulaDefinition[];
2061
- getByCategory(category: string): FormulaDefinition[];
2062
- getExpressionCallable(): FormulaDefinition[];
2063
- }
2064
-
2065
2204
  /**
2066
2205
  * Paste-level delta computations.
2067
2206
  *
@@ -2086,144 +2225,6 @@ type PasteSpec = {
2086
2225
  isCut: boolean;
2087
2226
  };
2088
2227
 
2089
- /**
2090
- * A single operation the LLM wants to apply to rows in the current filtered view.
2091
- *
2092
- * - `edit` — `fn` is `(r, ctx) => void`. Mutates `r` in place. Changed fields
2093
- * become column deltas. Rows with no changes are no-ops.
2094
- * - `delete` — `fn` is `(r, ctx) => boolean`. Truthy means "flag this row for
2095
- * deletion". Soft delete via `DeleteRowCommand`.
2096
- */
2097
- type ChatOp = {
2098
- action: "edit";
2099
- fn: string;
2100
- } | {
2101
- action: "delete";
2102
- fn: string;
2103
- };
2104
- /**
2105
- * A single chunk in the stream returned from `DataEditorChat.onMessage`.
2106
- *
2107
- * - `status` — progress message shown while processing (e.g. "Analyzing 500 rows...").
2108
- * - `message` — chat reply shown to the user.
2109
- * - `rows` — updated rows to apply to the grid. Matched by `primaryKey`.
2110
- * - `ops` — array of per-row operations (edits and/or deletes) to apply in order.
2111
- */
2112
- type ChatResponseChunk<TRow extends DataEditorRow = DataEditorRow> = {
2113
- type: "status";
2114
- content: string;
2115
- } | {
2116
- type: "message";
2117
- content: string;
2118
- } | {
2119
- type: "rows";
2120
- content: TRow[];
2121
- } | {
2122
- type: "ops";
2123
- content: ChatOp[];
2124
- };
2125
- /** Status of a row in the chat sample, relative to its origin snapshot. */
2126
- type ChatRowStatus = "new" | "edited" | "original";
2127
- /** A sample row handed to the chat callback, with its current status and validation errors. */
2128
- type ChatRow<TRow extends DataEditorRow = DataEditorRow> = {
2129
- /** Row data keyed by column ID. */
2130
- data: TRow;
2131
- /** Whether the row was newly added, edited, or is unchanged. */
2132
- status: ChatRowStatus;
2133
- /** Validation errors keyed by column ID. */
2134
- errors: Record<string, string[]>;
2135
- /** The source this row belongs to. */
2136
- source: string;
2137
- };
2138
- /** Aggregated error count across the current view, grouped by field and message. */
2139
- type ChatErrorSummary = {
2140
- /** Column ID where the error occurred. */
2141
- field: string;
2142
- /** The validation message. */
2143
- message: string;
2144
- /** How many rows hit this error. */
2145
- count: number;
2146
- /** A few example values that triggered the error. */
2147
- examples: string[];
2148
- };
2149
- /**
2150
- * Context about the current dataset, passed to `loadSuggestions` and extended
2151
- * into `ChatContext` for `onMessage`.
2152
- */
2153
- type ChatDataContext<TRow extends DataEditorRow = DataEditorRow> = {
2154
- /** Full column definitions. */
2155
- columns: DataEditorColumn[];
2156
- /** Row identifier field. */
2157
- primaryKey: keyof TRow;
2158
- /** Total rows in the dataset. */
2159
- totalRowCount: number;
2160
- /** Rows in the current filtered view. */
2161
- filteredRowCount: number;
2162
- /** Sample rows, with status and errors. Size controlled by `sampleSize`. */
2163
- sample: ChatRow<TRow>[];
2164
- /** Aggregated error counts by field and message. */
2165
- errorSummary: ChatErrorSummary[];
2166
- /** Access all rows. Use for full-dataset operations. */
2167
- getRows: () => ChatRow<TRow>[];
2168
- };
2169
- /**
2170
- * The full context passed to `DataEditorChat.onMessage` when the user sends
2171
- * a prompt. Includes the prompt itself and all dataset context.
2172
- */
2173
- type ChatContext<TRow extends DataEditorRow = DataEditorRow> = ChatDataContext<TRow> & {
2174
- /** The user's chat prompt. */
2175
- message: string;
2176
- };
2177
- /**
2178
- * Bring-your-own-AI chat configuration. When provided via the `chat` prop,
2179
- * the editor shows a chat panel alongside the grid. You own the AI
2180
- * integration; the SDK hands you dataset context and renders the streamed
2181
- * response.
2182
- *
2183
- * @example
2184
- * ```ts
2185
- * chat={{
2186
- * sampleSize: 50,
2187
- * onMessage: async function* (context) {
2188
- * yield { type: "status", content: "Thinking..." };
2189
- * const res = await fetch("/api/ai", {
2190
- * method: "POST",
2191
- * body: JSON.stringify({
2192
- * prompt: context.message,
2193
- * columns: context.columns,
2194
- * sample: context.sample.map(r => r.data),
2195
- * errors: context.errorSummary,
2196
- * }),
2197
- * }).then(r => r.json());
2198
- * yield { type: "rows", content: res.updatedRows };
2199
- * yield { type: "ops", content: res.ops };
2200
- * yield { type: "message", content: res.reply };
2201
- * },
2202
- * }}
2203
- * ```
2204
- */
2205
- type DataEditorChat<TRow extends DataEditorRow = DataEditorRow> = {
2206
- /**
2207
- * How many rows to include in the context sample. The SDK picks a
2208
- * representative slice including rows with errors. When omitted, the SDK decides.
2209
- */
2210
- sampleSize?: number;
2211
- /** Title shown above the suggestion list when the chat is empty. */
2212
- emptyTitle?: string;
2213
- /** How many suggestions to request from `loadSuggestions`. */
2214
- suggestionsCount?: number;
2215
- /** Optional prompt generator for the empty-state suggestion chips. */
2216
- loadSuggestions?: (context: ChatDataContext<TRow>) => Promise<string[]>;
2217
- /**
2218
- * Called when the user sends a message. Receives full dataset context.
2219
- * Returns an async iterable of response chunks — the SDK streams them
2220
- * into the UI.
2221
- */
2222
- onMessage: (context: ChatContext<TRow>) => AsyncIterable<ChatResponseChunk<TRow>>;
2223
- /** Called when the user cancels a pending request. Use to abort your API call. */
2224
- onCancel?: () => void;
2225
- };
2226
-
2227
2228
  type ApplyFormulaOptions = {
2228
2229
  /**
2229
2230
  * Column IDs to delete AFTER the formula has been applied.
@@ -2314,11 +2315,15 @@ declare class DataStore<TRow extends DataEditorRow = DataEditorRow> {
2314
2315
  private _dynamicColumns;
2315
2316
  private _effectiveColumns;
2316
2317
  private _lastColumnsInput;
2318
+ private _addedOptions;
2319
+ private withAddedOptions;
2320
+ private rebuildEffectiveColumns;
2317
2321
  setSchemaColumns(columns: DataEditorColumn[]): void;
2318
2322
  getDynamicColumns(): DataEditorColumn[];
2319
2323
  getEffectiveColumns: () => DataEditorColumn[];
2320
2324
  setDynamicColumns(fn: (prev: DataEditorColumn[]) => DataEditorColumn[]): void;
2321
2325
  addDynamicColumns(columns: DataEditorColumn[]): void;
2326
+ addColumnOptions(columnId: string, values: string[]): void;
2322
2327
  setColumns(columns: DataEditorColumn[]): void;
2323
2328
  subscribe: (listener: () => void) => (() => void);
2324
2329
  getSnapshot: () => DataStoreSnapshot;