@updog/data-editor 0.1.41 → 0.1.42

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 (3) hide show
  1. package/index.d.ts +480 -464
  2. package/index.js +3038 -2880
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -498,7 +498,14 @@ declare var export_default = {
498
498
  selectValuePlaceholder: "Select value",
499
499
  showMatched: "Show matched",
500
500
  allMatched: "All values are matched",
501
- unmatchedWarning: "Unmatched values won't be imported",
501
+ unmatchedWarning:
502
+ "Unmatched values won't be imported. Match this value to keep the data. You can edit values after importing.",
503
+ createOption: "Create option",
504
+ createOptionTitle: "Create option",
505
+ createOptionNameLabel: "Option name",
506
+ createOptionNamePlaceholder: "Enter option name",
507
+ createOptionNameTaken: "This option already exists",
508
+ createOptionSubmit: "Create",
502
509
  },
503
510
  primaryKey: {
504
511
  title: "Select primary key",
@@ -720,6 +727,11 @@ type SelectEditorCell = {
720
727
  type: "select";
721
728
  /** The list of options shown in the dropdown. Each string is both the stored value and the display label. */
722
729
  options: string[];
730
+ /**
731
+ * Let users add options via "Create option" (matching step + grid); never
732
+ * created implicitly. Defaults to true; false for a strict closed enum.
733
+ */
734
+ enableCustomValue?: boolean;
723
735
  };
724
736
  /** Number input cell with locale-aware formatting. */
725
737
  type NumberEditorCell = {
@@ -1246,78 +1258,6 @@ declare class ChunkedProcessor<T> {
1246
1258
  cancel(): void;
1247
1259
  }
1248
1260
 
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
1261
  /**
1322
1262
  * Internal contract: the surface a Scale client exposes to the SDK's data
1323
1263
  * layer. Implemented by `ScaleClient`. Not part of the public SDK API —
@@ -1340,185 +1280,143 @@ type ScaleClientApi<TRow extends DataEditorRow = DataEditorRow, TFilters = Recor
1340
1280
  scrollSensitivity?: number;
1341
1281
  };
1342
1282
 
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
1283
  /**
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.
1284
+ * A single operation the LLM wants to apply to rows in the current filtered view.
1285
+ *
1286
+ * - `edit` `fn` is `(r, ctx) => void`. Mutates `r` in place. Changed fields
1287
+ * become column deltas. Rows with no changes are no-ops.
1288
+ * - `delete` — `fn` is `(r, ctx) => boolean`. Truthy means "flag this row for
1289
+ * deletion". Soft delete via `DeleteRowCommand`.
1447
1290
  */
1448
- type SelectionRect = {
1449
- readonly fields: readonly string[];
1450
- readonly rowIds: readonly TRowId[];
1291
+ type ChatOp = {
1292
+ action: "edit";
1293
+ fn: string;
1294
+ } | {
1295
+ action: "delete";
1296
+ fn: string;
1451
1297
  };
1452
-
1453
1298
  /**
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.
1299
+ * A single chunk in the stream returned from `DataEditorChat.onMessage`.
1461
1300
  *
1462
- * Responsibilities:
1463
- * - TRowId ServerRowId translation via primaryKey
1464
- * - Grid index column ID translation via columns array
1465
- * - Filter/sort context attachment from ServerDataManager
1301
+ * - `status` — progress message shown while processing (e.g. "Analyzing 500 rows...").
1302
+ * - `message` chat reply shown to the user.
1303
+ * - `rows` updated rows to apply to the grid. Matched by `primaryKey`.
1304
+ * - `ops` — array of per-row operations (edits and/or deletes) to apply in order.
1466
1305
  */
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>;
1306
+ type ChatResponseChunk<TRow extends DataEditorRow = DataEditorRow> = {
1307
+ type: "status";
1308
+ content: string;
1309
+ } | {
1310
+ type: "message";
1311
+ content: string;
1312
+ } | {
1313
+ type: "rows";
1314
+ content: TRow[];
1315
+ } | {
1316
+ type: "ops";
1317
+ content: ChatOp[];
1475
1318
  };
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;
1319
+ /** Status of a row in the chat sample, relative to its origin snapshot. */
1320
+ type ChatRowStatus = "new" | "edited" | "original";
1321
+ /** A sample row handed to the chat callback, with its current status and validation errors. */
1322
+ type ChatRow<TRow extends DataEditorRow = DataEditorRow> = {
1323
+ /** Row data keyed by column ID. */
1324
+ data: TRow;
1325
+ /** Whether the row was newly added, edited, or is unchanged. */
1326
+ status: ChatRowStatus;
1327
+ /** Validation errors keyed by column ID. */
1328
+ errors: Record<string, string[]>;
1329
+ /** The source this row belongs to. */
1330
+ source: string;
1331
+ };
1332
+ /** Aggregated error count across the current view, grouped by field and message. */
1333
+ type ChatErrorSummary = {
1334
+ /** Column ID where the error occurred. */
1335
+ field: string;
1336
+ /** The validation message. */
1337
+ message: string;
1338
+ /** How many rows hit this error. */
1339
+ count: number;
1340
+ /** A few example values that triggered the error. */
1341
+ examples: string[];
1342
+ };
1343
+ /**
1344
+ * Context about the current dataset, passed to `loadSuggestions` and extended
1345
+ * into `ChatContext` for `onMessage`.
1346
+ */
1347
+ type ChatDataContext<TRow extends DataEditorRow = DataEditorRow> = {
1348
+ /** Full column definitions. */
1349
+ columns: DataEditorColumn[];
1350
+ /** Row identifier field. */
1351
+ primaryKey: keyof TRow;
1352
+ /** Total rows in the dataset. */
1353
+ totalRowCount: number;
1354
+ /** Rows in the current filtered view. */
1355
+ filteredRowCount: number;
1356
+ /** Sample rows, with status and errors. Size controlled by `sampleSize`. */
1357
+ sample: ChatRow<TRow>[];
1358
+ /** Aggregated error counts by field and message. */
1359
+ errorSummary: ChatErrorSummary[];
1360
+ /** Access all rows. Use for full-dataset operations. */
1361
+ getRows: () => ChatRow<TRow>[];
1362
+ };
1363
+ /**
1364
+ * The full context passed to `DataEditorChat.onMessage` when the user sends
1365
+ * a prompt. Includes the prompt itself and all dataset context.
1366
+ */
1367
+ type ChatContext<TRow extends DataEditorRow = DataEditorRow> = ChatDataContext<TRow> & {
1368
+ /** The user's chat prompt. */
1369
+ message: string;
1370
+ };
1371
+ /**
1372
+ * Bring-your-own-AI chat configuration. When provided via the `chat` prop,
1373
+ * the editor shows a chat panel alongside the grid. You own the AI
1374
+ * integration; the SDK hands you dataset context and renders the streamed
1375
+ * response.
1376
+ *
1377
+ * @example
1378
+ * ```ts
1379
+ * chat={{
1380
+ * sampleSize: 50,
1381
+ * onMessage: async function* (context) {
1382
+ * yield { type: "status", content: "Thinking..." };
1383
+ * const res = await fetch("/api/ai", {
1384
+ * method: "POST",
1385
+ * body: JSON.stringify({
1386
+ * prompt: context.message,
1387
+ * columns: context.columns,
1388
+ * sample: context.sample.map(r => r.data),
1389
+ * errors: context.errorSummary,
1390
+ * }),
1391
+ * }).then(r => r.json());
1392
+ * yield { type: "rows", content: res.updatedRows };
1393
+ * yield { type: "ops", content: res.ops };
1394
+ * yield { type: "message", content: res.reply };
1395
+ * },
1396
+ * }}
1397
+ * ```
1398
+ */
1399
+ type DataEditorChat<TRow extends DataEditorRow = DataEditorRow> = {
1515
1400
  /**
1516
- * Returns null when all columns are selected (caller decides representation).
1517
- * Otherwise returns contiguous column spans as `{ fromColumn, toColumn }` regions.
1401
+ * How many rows to include in the context sample. The SDK picks a
1402
+ * representative slice including rows with errors. When omitted, the SDK decides.
1518
1403
  */
1519
- private collapseColumns;
1520
- private viewContext;
1521
- }
1404
+ sampleSize?: number;
1405
+ /** Title shown above the suggestion list when the chat is empty. */
1406
+ emptyTitle?: string;
1407
+ /** How many suggestions to request from `loadSuggestions`. */
1408
+ suggestionsCount?: number;
1409
+ /** Optional prompt generator for the empty-state suggestion chips. */
1410
+ loadSuggestions?: (context: ChatDataContext<TRow>) => Promise<string[]>;
1411
+ /**
1412
+ * Called when the user sends a message. Receives full dataset context.
1413
+ * Returns an async iterable of response chunks — the SDK streams them
1414
+ * into the UI.
1415
+ */
1416
+ onMessage: (context: ChatContext<TRow>) => AsyncIterable<ChatResponseChunk<TRow>>;
1417
+ /** Called when the user cancels a pending request. Use to abort your API call. */
1418
+ onCancel?: () => void;
1419
+ };
1522
1420
 
1523
1421
  /**
1524
1422
  * Categories of internal errors surfaced through the `onError` callback.
@@ -1559,6 +1457,341 @@ declare class ErrorHandler {
1559
1457
  handleError(error: UpdogError): void;
1560
1458
  }
1561
1459
 
1460
+ type FormulaCellContext = {
1461
+ value: unknown;
1462
+ field: string;
1463
+ rowId: TRowId;
1464
+ getField: (field: string) => unknown;
1465
+ /**
1466
+ * Positional arguments for expression-compiled formulas.
1467
+ * Populated by the expression evaluator when invoking multi-arg function
1468
+ * calls. Single-arg formulas (UPPER, TRIM, etc.) still read from `value`.
1469
+ * Undefined for all non-expression call sites — existing code is unaffected.
1470
+ */
1471
+ args?: readonly unknown[];
1472
+ };
1473
+ type FormulaParamType = "string" | "number" | "boolean" | "select";
1474
+ type FormulaParam = {
1475
+ name: string;
1476
+ label: string;
1477
+ type: FormulaParamType;
1478
+ required?: boolean;
1479
+ defaultValue?: unknown;
1480
+ options?: Array<{
1481
+ id: string;
1482
+ text: string;
1483
+ }>;
1484
+ };
1485
+ type ColumnInputKind = "single" | "multiple";
1486
+ type ColumnInput = {
1487
+ name: string;
1488
+ label: string;
1489
+ kind: ColumnInputKind;
1490
+ required?: boolean;
1491
+ };
1492
+ type FormulaCategory = "text" | "number" | "logic" | "custom";
1493
+ type FormulaArity = {
1494
+ /** Minimum number of positional arguments. 0 means "callable with no args". */
1495
+ min: number;
1496
+ /** Maximum number of positional arguments. Use Number.POSITIVE_INFINITY for variadic. */
1497
+ max: number;
1498
+ };
1499
+ type FormulaBase = {
1500
+ name: string;
1501
+ label: string;
1502
+ category: FormulaCategory;
1503
+ description?: string;
1504
+ columns?: ColumnInput[];
1505
+ params: FormulaParam[];
1506
+ /**
1507
+ * The call signature of this formula when invoked from the expression
1508
+ * language. Required. For shortcut formulas (UPPER, TRIM, ...) use
1509
+ * { min: 1, max: 1 }. For CLEAR use { min: 0, max: 0 }. For MERGE use
1510
+ * { min: 2, max: Number.POSITIVE_INFINITY }.
1511
+ */
1512
+ arity: FormulaArity;
1513
+ /** Parameter signature shown in autocomplete, without the function name. e.g. "(text, count)" */
1514
+ syntax?: string;
1515
+ /**
1516
+ * Whether this formula may be invoked from the expression parser.
1517
+ * Defaults to true when omitted. MERGE and SPLIT set this to false
1518
+ * because they have dedicated modals that supply their non-expression
1519
+ * params (column lists, separator, ...).
1520
+ */
1521
+ expressionCallable?: boolean;
1522
+ };
1523
+ type CellFormula = FormulaBase & {
1524
+ kind: "cell";
1525
+ compute: (ctx: FormulaCellContext, params: Record<string, unknown>) => unknown;
1526
+ };
1527
+ type RowFormula = FormulaBase & {
1528
+ kind: "row";
1529
+ targetFields: (params: Record<string, unknown>) => string[];
1530
+ compute: (ctx: FormulaCellContext, params: Record<string, unknown>) => Record<string, unknown>;
1531
+ };
1532
+ type FormulaDefinition = CellFormula | RowFormula;
1533
+
1534
+ declare class FormulaRegistry {
1535
+ private readonly formulas;
1536
+ register(formula: FormulaDefinition): void;
1537
+ get(name: string): FormulaDefinition | undefined;
1538
+ getAll(): FormulaDefinition[];
1539
+ getByCategory(category: string): FormulaDefinition[];
1540
+ getExpressionCallable(): FormulaDefinition[];
1541
+ }
1542
+
1543
+ type RegisterSourceOptions = {
1544
+ name: string;
1545
+ id?: DataSourceId;
1546
+ isDeletable?: boolean;
1547
+ isInitialData?: boolean;
1548
+ };
1549
+ type MergeEntry<TRow> = {
1550
+ row: TRow;
1551
+ sourceId: DataSourceId;
1552
+ isNew: boolean;
1553
+ isEdited: boolean;
1554
+ };
1555
+ type RemovalPlan<TRow> = {
1556
+ rowsToDelete: Set<TRowId>;
1557
+ rowsToRestore: Array<{
1558
+ rowId: TRowId;
1559
+ row: TRow;
1560
+ originalSourceId: DataSourceId;
1561
+ isNew: boolean;
1562
+ isEdited: boolean;
1563
+ }>;
1564
+ };
1565
+ type ExtendedRemovalPlan<TRow> = RemovalPlan<TRow> & {
1566
+ sourceId: DataSourceId;
1567
+ repairedEntries: Array<{
1568
+ sourceId: DataSourceId;
1569
+ rowId: TRowId;
1570
+ before: MergeEntry<TRow>;
1571
+ after: MergeEntry<TRow> | null;
1572
+ }>;
1573
+ };
1574
+ declare class SourceManager<TRow extends DataEditorRow = DataEditorRow> {
1575
+ private readonly _defaultSourceId;
1576
+ private readonly overrides;
1577
+ private readonly sources;
1578
+ private readonly mergedRows;
1579
+ getSourceId(rowId: TRowId): DataSourceId;
1580
+ setSourceId(rowId: TRowId, sourceId: DataSourceId): void;
1581
+ deleteSourceId(rowId: TRowId): void;
1582
+ getOverrides(): ReadonlyMap<TRowId, DataSourceId>;
1583
+ register(options: RegisterSourceOptions): DataSourceId;
1584
+ /**
1585
+ * Re-insert a source using a full captured state, preserving
1586
+ * isVisible, isLoading, rowCount, etc. Used by SourceLifecycle.restore.
1587
+ * If the source already exists, overwrites its state.
1588
+ */
1589
+ restoreState(state: DataSourceState): void;
1590
+ has(sourceId: DataSourceId): boolean;
1591
+ get(sourceId: DataSourceId): DataSourceState | undefined;
1592
+ delete(sourceId: DataSourceId): void;
1593
+ setLoading(sourceId: DataSourceId, isLoading: boolean): void;
1594
+ finalizeAllSources(): void;
1595
+ values(): IterableIterator<DataSourceState>;
1596
+ getHiddenSourceIds(): Set<DataSourceId>;
1597
+ saveMergeSnapshot(sourceId: DataSourceId, rowId: TRowId, existingRow: TRow, previousSourceId: DataSourceId, isNew: boolean, isEdited: boolean): void;
1598
+ /**
1599
+ * Public so commands can re-install merge entries during undo of a remove.
1600
+ */
1601
+ restoreMergeEntry(sourceId: DataSourceId, rowId: TRowId, entry: MergeEntry<TRow>): void;
1602
+ /**
1603
+ * Pure — computes the full removal plan without mutating any state.
1604
+ * Callers run applyRemovalPlan(plan) to commit.
1605
+ */
1606
+ planRemoval(sourceId: DataSourceId): ExtendedRemovalPlan<TRow> | null;
1607
+ /**
1608
+ * Mutates internal state per plan produced by planRemoval.
1609
+ */
1610
+ applyRemovalPlan(plan: ExtendedRemovalPlan<TRow>): void;
1611
+ clear(): void;
1612
+ private getUniqueName;
1613
+ }
1614
+
1615
+ type ServerDataManagerDeps<TRow extends DataEditorRow = DataEditorRow> = {
1616
+ clear(): void;
1617
+ setLoading(isLoading: boolean): void;
1618
+ registerSource(options: RegisterSourceOptions): DataSourceId;
1619
+ setSourceLoading(sourceId: DataSourceId, isLoading: boolean): void;
1620
+ getLocalRowCount(): number;
1621
+ replaceServerRows(sourceId: DataSourceId, rows: ServerRow<TRow>[], offset: number, counts?: ServerQueryCounts): void;
1622
+ appendServerRows(rows: ServerRow<TRow>[], counts?: ServerQueryCounts): void;
1623
+ prependServerRows(rows: ServerRow<TRow>[], offset: number, counts?: ServerQueryCounts): void;
1624
+ applyServerRowMeta(rows: ServerRow<TRow>[], counts?: ServerQueryCounts): void;
1625
+ };
1626
+ type FetchDirection = "forward" | "backward" | "jump";
1627
+ declare class ServerDataManager<TRow extends DataEditorRow = DataEditorRow> {
1628
+ private _offset;
1629
+ private _totalCount;
1630
+ private _isFetching;
1631
+ private readonly _pageSize;
1632
+ private readonly _maxBufferRows;
1633
+ private _filters;
1634
+ private _sources;
1635
+ private _sort;
1636
+ private _filterOptions;
1637
+ private _filterOptionsFetched;
1638
+ private _abortController;
1639
+ private _syncAbort;
1640
+ private _lastVisibleStart;
1641
+ private _debouncedFetch;
1642
+ private readonly _config;
1643
+ private readonly _dataStoreRef;
1644
+ private readonly _sourceLabel;
1645
+ private _onChanged;
1646
+ constructor(config: ScaleClientApi<TRow>, dataStoreRef: ServerDataManagerDeps<TRow>, sourceLabel: string);
1647
+ get offset(): number;
1648
+ get totalCount(): number | null;
1649
+ get isFetching(): boolean;
1650
+ get pageSize(): number;
1651
+ get maxBufferRows(): number;
1652
+ setOnChanged(callback: () => void): void;
1653
+ setOffset(offset: number): void;
1654
+ setTotalCount(count: number): void;
1655
+ private setFetching;
1656
+ getExcess(currentCount: number, newCount: number): number;
1657
+ shouldFetch(visibleStart: number, visibleEnd: number, loadedCount: number): FetchDirection | null;
1658
+ /**
1659
+ * Full reload — abort in-flight, clear store, fetch first page.
1660
+ * Called on initial load, search/filter/sort changes, and resetFilters.
1661
+ */
1662
+ reload(): void;
1663
+ /**
1664
+ * Scroll-driven pagination with velocity-based debouncing.
1665
+ * Called from CanvasGrid on every scroll event.
1666
+ */
1667
+ handleScroll(visibleStart: number, visibleEnd: number): void;
1668
+ /**
1669
+ * Fetch a single page based on scroll position and current window state.
1670
+ */
1671
+ private fetchPage;
1672
+ /**
1673
+ * Re-query the current viewport and replace row data, metadata, and counts.
1674
+ * Called after successful edits and find-and-replace mutations.
1675
+ * Each call aborts the previous in-flight sync.
1676
+ */
1677
+ syncCurrentView(): void;
1678
+ /**
1679
+ * Merge filter keys into server filter state and reload.
1680
+ * Called by DataStore.setFilters() in server mode and by filter components.
1681
+ */
1682
+ setFilters(filters: Partial<Filters>): void;
1683
+ /**
1684
+ * Set sort state and reload.
1685
+ */
1686
+ setSort(sort: SortState): void;
1687
+ /**
1688
+ * Restrict query to visible sources and reload.
1689
+ * Pass `undefined` to include all sources.
1690
+ */
1691
+ setSources(sources: string[] | undefined): void;
1692
+ /**
1693
+ * Clear all filters and sort, then reload.
1694
+ */
1695
+ resetFilters(): void;
1696
+ /**
1697
+ * One-time fetch of filter option dictionaries for sidebar filter controls.
1698
+ */
1699
+ fetchFilterOptions(): void;
1700
+ getFilterOptions(): FilterOptionsResponse | null;
1701
+ get onEdit(): (params: EditParams, options?: ServerCallOptions) => Promise<EditResponse | void>;
1702
+ get filters(): Record<string, unknown>;
1703
+ get sort(): SortState;
1704
+ get sources(): string[] | undefined;
1705
+ get onSourceRemove(): ((params: SourceRemoveParams) => Promise<void>) | undefined;
1706
+ get onColumnDelete(): ((params: ColumnDeleteParams) => Promise<EditResponse | void>) | undefined;
1707
+ get onColumnEdit(): ((params: ColumnEditParams) => Promise<EditResponse | void>) | undefined;
1708
+ get hasExport(): boolean;
1709
+ private _exportAbortController;
1710
+ export(format: DataEditorFormat, allRows: boolean, rtl: boolean): Promise<void>;
1711
+ clear(): void;
1712
+ destroy(): void;
1713
+ }
1714
+
1715
+ /**
1716
+ * A post-resolution selection rectangle in stable coordinate space.
1717
+ * Produced by grid-layer resolvers from CellRange[] in grid-index space.
1718
+ * Consumed by DataStore operations and server sync.
1719
+ */
1720
+ type SelectionRect = {
1721
+ readonly fields: readonly string[];
1722
+ readonly rowIds: readonly TRowId[];
1723
+ };
1724
+
1725
+ /**
1726
+ * ServerEditBuilder — Stateless coordinate translator for server-delegated edits.
1727
+ *
1728
+ * Converts frontend coordinates (TRowId, column index, grid ranges) into
1729
+ * `EditParams` with `Region[]` that the server can interpret.
1730
+ *
1731
+ * Does NOT call `onEdit`. Only builds params.
1732
+ * DataStore calls the builder, then sends the result to the server.
1733
+ *
1734
+ * Responsibilities:
1735
+ * - TRowId → ServerRowId translation via primaryKey
1736
+ * - Grid index → column ID translation via columns array
1737
+ * - Filter/sort context attachment from ServerDataManager
1738
+ */
1739
+
1740
+ type ServerEditDeps<TRow extends DataEditorRow = DataEditorRow> = {
1741
+ getPrimaryKey: () => string;
1742
+ getRowById: (id: TRowId) => TRow | undefined;
1743
+ getColumnIds: () => string[];
1744
+ getFilters: () => Record<string, unknown>;
1745
+ getSort: () => SortState;
1746
+ getLockedColumns: () => ReadonlyMap<string, ColumnLockMode>;
1747
+ };
1748
+ declare class ServerEditBuilder<TRow extends DataEditorRow = DataEditorRow> {
1749
+ private readonly _deps;
1750
+ constructor(deps: ServerEditDeps<TRow>);
1751
+ resolveServerRowId(rowId: TRowId): ServerRowId | undefined;
1752
+ buildRegion(rowIds: TRowId[], columnIds: string[]): Region;
1753
+ /**
1754
+ * Collapse rowIds × columnIds into minimal Region[].
1755
+ * - All columns → omit column fields (row-only regions).
1756
+ * - Contiguous columns in schema order → single fromColumn/toColumn span.
1757
+ * - Non-contiguous → one region per contiguous column group.
1758
+ * Rows are expressed as fromRow/toRow using first/last of the provided array.
1759
+ */
1760
+ buildRegions(rowIds: TRowId[], columnIds: string[]): Region[];
1761
+ /**
1762
+ * Collapse columnIds into minimal column-only Region[] (all rows implied).
1763
+ * - All columns → `{ allSelected: true }`.
1764
+ * - Contiguous in schema order → single `{ fromColumn, toColumn }`.
1765
+ * - Non-contiguous → one region per contiguous group.
1766
+ */
1767
+ buildColumnRegions(columnIds: string[]): Region[];
1768
+ /**
1769
+ * Build minimal Region[] from multiple selection rectangles.
1770
+ * Each rect is collapsed independently, preserving disjoint selections.
1771
+ */
1772
+ buildRegionsFromRects(rects: SelectionRect[]): Region[];
1773
+ buildAllSelectedRegion(): Region;
1774
+ buildColumnRegion(columnId: string): Region;
1775
+ buildRowRegion(fromRowId: TRowId, toRowId: TRowId): Region;
1776
+ cellEdit(rowId: TRowId, field: string, value: unknown): EditParams;
1777
+ clear(target: Region[]): EditParams;
1778
+ paste(source: Region[], target: Region[], cut?: boolean): EditParams;
1779
+ pasteExternal(target: Region[], values: unknown[][]): EditParams;
1780
+ fill(source: Region[], target: Region[]): EditParams;
1781
+ transform(target: Region[], transform: TransformParams): EditParams;
1782
+ deleteRows(rowRanges: [TRowId, TRowId][]): EditParams;
1783
+ restoreRows(rowRanges: [TRowId, TRowId][]): EditParams;
1784
+ deleteAllRows(): EditParams;
1785
+ restoreAllRows(): EditParams;
1786
+ insertRow(anchorRowId: TRowId | undefined, position: InsertParams["position"], values: unknown[][], columnIds: string[]): EditParams;
1787
+ /**
1788
+ * Returns null when all columns are selected (caller decides representation).
1789
+ * Otherwise returns contiguous column spans as `{ fromColumn, toColumn }` regions.
1790
+ */
1791
+ private collapseColumns;
1792
+ private viewContext;
1793
+ }
1794
+
1562
1795
  /**
1563
1796
  * DirtyTracker — Change classification and revert detection for rows.
1564
1797
  *
@@ -1979,89 +2212,6 @@ type FillSpec = {
1979
2212
  rowIdToFillIndex: ReadonlyMap<TRowId, number>;
1980
2213
  };
1981
2214
 
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
2215
  /**
2066
2216
  * Paste-level delta computations.
2067
2217
  *
@@ -2086,144 +2236,6 @@ type PasteSpec = {
2086
2236
  isCut: boolean;
2087
2237
  };
2088
2238
 
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
2239
  type ApplyFormulaOptions = {
2228
2240
  /**
2229
2241
  * Column IDs to delete AFTER the formula has been applied.
@@ -2314,11 +2326,15 @@ declare class DataStore<TRow extends DataEditorRow = DataEditorRow> {
2314
2326
  private _dynamicColumns;
2315
2327
  private _effectiveColumns;
2316
2328
  private _lastColumnsInput;
2329
+ private _addedOptions;
2330
+ private withAddedOptions;
2331
+ private rebuildEffectiveColumns;
2317
2332
  setSchemaColumns(columns: DataEditorColumn[]): void;
2318
2333
  getDynamicColumns(): DataEditorColumn[];
2319
2334
  getEffectiveColumns: () => DataEditorColumn[];
2320
2335
  setDynamicColumns(fn: (prev: DataEditorColumn[]) => DataEditorColumn[]): void;
2321
2336
  addDynamicColumns(columns: DataEditorColumn[]): void;
2337
+ addColumnOptions(columnId: string, values: string[]): void;
2322
2338
  setColumns(columns: DataEditorColumn[]): void;
2323
2339
  subscribe: (listener: () => void) => (() => void);
2324
2340
  getSnapshot: () => DataStoreSnapshot;