@marimo-team/frontend 0.22.1-dev2 → 0.22.1-dev21

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 (72) hide show
  1. package/dist/assets/{ConnectedDataExplorerComponent-BhOfvmd2.js → ConnectedDataExplorerComponent-Bgd8MpO7.js} +1 -1
  2. package/dist/assets/{JsonOutput-DK6QFpxq.js → JsonOutput-CYRewW_n.js} +10 -10
  3. package/dist/assets/{add-cell-with-ai-C9CXMDL-.js → add-cell-with-ai-CJL5NwHh.js} +24 -24
  4. package/dist/assets/{add-connection-dialog-uUw1BwGd.js → add-connection-dialog-_oI2JBU2.js} +1 -1
  5. package/dist/assets/{agent-panel-CKXYOD10.js → agent-panel-frCoZBMf.js} +1 -1
  6. package/dist/assets/ai-model-dropdown-CMj49xMg.js +5 -0
  7. package/dist/assets/{app-config-button-B4_Fzop6.js → app-config-button-DCVf5Azv.js} +1 -1
  8. package/dist/assets/{cell-editor-DsXpgc4r.js → cell-editor-nJeqD5Xq.js} +1 -1
  9. package/dist/assets/{chat-display-CRKEYeok.js → chat-display-DnlV-Vjp.js} +1 -1
  10. package/dist/assets/chat-panel-BrH9Bycd.js +3 -0
  11. package/dist/assets/{chat-ui-CQB2SbzK.js → chat-ui-gEEIyfNV.js} +1 -1
  12. package/dist/assets/{column-preview-CWOsM3UD.js → column-preview-PiH54hAW.js} +1 -1
  13. package/dist/assets/{command-palette-BXM1GCVc.js → command-palette-CrC7ohqO.js} +1 -1
  14. package/dist/assets/{edit-page-BXQVe56n.js → edit-page-idaINdSX.js} +7 -7
  15. package/dist/assets/{file-explorer-panel-QFaPxpp8.js → file-explorer-panel-sS4HTA69.js} +1 -1
  16. package/dist/assets/{form-CoRP5wCe.js → form-B-BLUzr3.js} +1 -1
  17. package/dist/assets/{formats-C_TavbEL.js → formats-N7VZhahg.js} +1 -1
  18. package/dist/assets/{home-page-GHJ3-S70.js → home-page-Lm5CjxXF.js} +1 -1
  19. package/dist/assets/{hooks-CfqzfU-0.js → hooks-ChIFqb9W.js} +1 -1
  20. package/dist/assets/index-CCazW2UV.css +2 -0
  21. package/dist/assets/index-eeNpwqOH.js +35 -0
  22. package/dist/assets/{layout-FSEZfgnG.js → layout-BlYB96ph.js} +1 -1
  23. package/dist/assets/{markdown-renderer-wLZT8no0.js → markdown-renderer-CYb9pckL.js} +1 -1
  24. package/dist/assets/{packages-panel-CwbHm9uC.js → packages-panel-C7RTPIUf.js} +1 -1
  25. package/dist/assets/{panels-BhaV1xQQ.js → panels-qPMPLlzN.js} +1 -1
  26. package/dist/assets/{run-page-BtSGYy1m.js → run-page-Cqmrl9SW.js} +1 -1
  27. package/dist/assets/{scratchpad-panel-riBRfLg4.js → scratchpad-panel-Ddw1KV8R.js} +1 -1
  28. package/dist/assets/{session-panel-B2ux2cYO.js → session-panel-DTp3QX73.js} +1 -1
  29. package/dist/assets/{state-LRco7VGF.js → state-Nag9RDED.js} +1 -1
  30. package/dist/assets/{useNotebookActions-C2iPqhjB.js → useNotebookActions-CG6L_-9T.js} +1 -1
  31. package/dist/assets/{utils-BDlGlVyF.js → utils-DWFl4LEP.js} +3 -3
  32. package/dist/assets/{vega-component-DiFt6ZG4.js → vega-component-BAuy1PqH.js} +1 -1
  33. package/dist/index.html +9 -9
  34. package/package.json +1 -1
  35. package/src/__tests__/branded.ts +6 -0
  36. package/src/components/chat/chat-panel.tsx +2 -1
  37. package/src/components/data-table/TableBottomBar.tsx +12 -1
  38. package/src/components/data-table/TableTopBar.tsx +31 -35
  39. package/src/components/data-table/charts/charts.tsx +40 -11
  40. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +1 -1
  41. package/src/components/data-table/data-table.tsx +6 -1
  42. package/src/components/data-table/loading-table.tsx +4 -1
  43. package/src/components/data-table/range-focus/cell-selection-stats.tsx +3 -1
  44. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +1 -1
  45. package/src/components/data-table/table-explorer-panel/table-explorer-panel.tsx +2 -2
  46. package/src/components/editor/ai/add-cell-with-ai.tsx +2 -1
  47. package/src/components/editor/chrome/panels/context-aware-panel/context-aware-panel.tsx +1 -1
  48. package/src/components/editor/chrome/wrapper/footer-items/lsp-status.tsx +2 -1
  49. package/src/core/cells/__tests__/apply-transaction.test.ts +12 -11
  50. package/src/core/cells/document-changes.ts +9 -9
  51. package/src/core/codemirror/copilot/__tests__/transport.test.ts +128 -2
  52. package/src/core/codemirror/copilot/client.ts +9 -2
  53. package/src/core/codemirror/copilot/language-server.ts +11 -0
  54. package/src/core/codemirror/copilot/transport.ts +32 -6
  55. package/src/core/islands/__tests__/bridge.test.ts +20 -10
  56. package/src/core/network/__tests__/requests-lazy.test.ts +30 -14
  57. package/src/core/websocket/useMarimoKernelConnection.tsx +5 -11
  58. package/src/core/websocket/useWebSocket.tsx +3 -1
  59. package/src/css/app/Cell.css +22 -1
  60. package/src/css/table.css +17 -0
  61. package/src/plugins/impl/DataTablePlugin.tsx +1 -0
  62. package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +2 -11
  63. package/src/plugins/impl/vega/__tests__/utils.test.ts +68 -0
  64. package/src/plugins/impl/vega/utils.ts +14 -5
  65. package/src/plugins/impl/vega/vega.css +2 -1
  66. package/src/utils/json/base64.ts +2 -5
  67. package/src/utils/time.ts +4 -2
  68. package/src/utils/typed.ts +2 -2
  69. package/dist/assets/ai-model-dropdown-Ck6NNlZH.js +0 -5
  70. package/dist/assets/chat-panel-CYkuZYzq.js +0 -3
  71. package/dist/assets/index-BFY3jw7I.css +0 -2
  72. package/dist/assets/index-e_bkIEdq.js +0 -35
@@ -4,6 +4,7 @@
4
4
  import type { RowSelectionState, Table } from "@tanstack/react-table";
5
5
  import { useLocale } from "react-aria";
6
6
  import type { GetRowIds } from "@/plugins/impl/DataTablePlugin";
7
+ import { cn } from "@/utils/cn";
7
8
  import { Events } from "@/utils/events";
8
9
  import { prettyNumber } from "@/utils/numbers";
9
10
  import { Button } from "../ui/button";
@@ -21,6 +22,8 @@ interface TableBottomBarProps<TData> {
21
22
  getRowIds?: GetRowIds;
22
23
  showPageSizeSelector?: boolean;
23
24
  tableLoading?: boolean;
25
+ part?: string;
26
+ className?: string;
24
27
  }
25
28
 
26
29
  export const TableBottomBar = <TData,>({
@@ -32,6 +35,8 @@ export const TableBottomBar = <TData,>({
32
35
  getRowIds,
33
36
  showPageSizeSelector,
34
37
  tableLoading,
38
+ part,
39
+ className,
35
40
  }: TableBottomBarProps<TData>) => {
36
41
  const { locale } = useLocale();
37
42
  const handleSelectAllRows = (value: boolean) => {
@@ -143,7 +148,13 @@ export const TableBottomBar = <TData,>({
143
148
  };
144
149
 
145
150
  return (
146
- <div className="flex lg:grid lg:grid-cols-[1fr_auto_1fr] items-center shrink-0 pt-1">
151
+ <div
152
+ part={part}
153
+ className={cn(
154
+ "flex lg:grid lg:grid-cols-[1fr_auto_1fr] items-center shrink-0",
155
+ className,
156
+ )}
157
+ >
147
158
  <div className="flex flex-col text-sm text-muted-foreground px-2 shrink-0">
148
159
  <div className="flex items-center gap-1">{renderTotal()}</div>
149
160
  <CellSelectionStats table={table} className="lg:hidden" />
@@ -69,43 +69,39 @@ export const TableTopBar: React.FC<TableTopBarProps> = ({
69
69
  }
70
70
 
71
71
  return (
72
- <div className="flex items-center h-10 px-2 border-b gap-3">
73
- {/* always-visible search */}
72
+ <div className="flex items-center h-10 px-2 border-b gap-2">
74
73
  {onSearchQueryChange && enableSearch && (
75
- <div className="flex items-center">
76
- <div className="flex items-center gap-1 rounded-full border px-2 w-56">
77
- <SearchIcon className="w-4 h-4 text-muted-foreground shrink-0" />
78
- <input
79
- ref={inputRef}
80
- type="text"
81
- className="h-6 border-none bg-transparent focus:outline-hidden text-sm w-full min-w-0"
82
- value={internalValue}
83
- onKeyDown={(e) => {
84
- if (e.key === "Escape") {
85
- setInternalValue("");
86
- inputRef.current?.blur();
87
- }
88
- }}
89
- onChange={(e) => setInternalValue(e.target.value)}
90
- placeholder="Search..."
91
- />
92
- {reloading && <Spinner size="small" />}
93
- {internalValue && (
94
- <Button
95
- variant="text"
96
- size="xs"
97
- className="h-5 w-5 p-0 shrink-0"
98
- onClick={() => setInternalValue("")}
99
- >
100
- <XIcon className="w-3 h-3 text-muted-foreground" />
101
- </Button>
102
- )}
103
- </div>
74
+ <div className="flex flex-1 items-center gap-1 px-2 rounded-sm focus-within:ring-1 focus-within:ring-border transition-shadow">
75
+ <SearchIcon className="w-4 h-4 text-muted-foreground shrink-0" />
76
+ <input
77
+ ref={inputRef}
78
+ type="text"
79
+ className="h-6 border-none bg-transparent focus:outline-hidden text-sm w-full min-w-0"
80
+ value={internalValue}
81
+ onKeyDown={(e) => {
82
+ if (e.key === "Escape") {
83
+ setInternalValue("");
84
+ inputRef.current?.blur();
85
+ }
86
+ }}
87
+ onChange={(e) => setInternalValue(e.target.value)}
88
+ placeholder="Search..."
89
+ />
90
+ {reloading && <Spinner size="small" />}
91
+ {internalValue && (
92
+ <Button
93
+ variant="text"
94
+ size="xs"
95
+ className="h-5 w-5 p-0 shrink-0"
96
+ onClick={() => setInternalValue("")}
97
+ >
98
+ <XIcon className="w-3 h-3 text-muted-foreground" />
99
+ </Button>
100
+ )}
104
101
  </div>
105
102
  )}
106
103
 
107
- {/* actions grouped together */}
108
- <div className="flex items-center gap-1 shrink-0">
104
+ <div className="flex items-center shrink-0">
109
105
  {showChartBuilder && (
110
106
  <Button
111
107
  variant="text"
@@ -117,7 +113,7 @@ export const TableTopBar: React.FC<TableTopBarProps> = ({
117
113
  onClick={toggleDisplayHeader}
118
114
  >
119
115
  <ChartSplineIcon className="w-3.5 h-3.5" />
120
- Chart Builder
116
+ Visualize
121
117
  </Button>
122
118
  )}
123
119
  {showTableExplorer && togglePanel && (
@@ -131,7 +127,7 @@ export const TableTopBar: React.FC<TableTopBarProps> = ({
131
127
  onClick={() => togglePanel(PANEL_TYPES.ROW_VIEWER)}
132
128
  >
133
129
  <PanelRightIcon className="w-3.5 h-3.5" />
134
- Table Explorer
130
+ Explorer
135
131
  </Button>
136
132
  )}
137
133
  {downloadAs && <ExportMenu downloadAs={downloadAs} />}
@@ -13,7 +13,7 @@ import {
13
13
  XIcon,
14
14
  } from "lucide-react";
15
15
  import type { JSX } from "react";
16
- import React, { useMemo, useState } from "react";
16
+ import React, { useMemo, useRef, useState } from "react";
17
17
  import { type UseFormReturn, useForm } from "react-hook-form";
18
18
  import useResizeObserver from "use-resize-observer";
19
19
  import { PythonIcon } from "@/components/editor/cell/code/icons";
@@ -61,6 +61,7 @@ export interface TablePanelProps {
61
61
  totalRows: number | TooManyRows;
62
62
  columns: number;
63
63
  displayHeader: boolean;
64
+ onCloseChartBuilder?: () => void;
64
65
  getDataUrl?: GetDataUrl;
65
66
  fieldTypes?: FieldTypesWithExternalType | null;
66
67
  }
@@ -74,12 +75,33 @@ export const TablePanel: React.FC<TablePanelProps> = ({
74
75
  getDataUrl,
75
76
  fieldTypes,
76
77
  displayHeader,
78
+ onCloseChartBuilder,
77
79
  }) => {
78
80
  const [tabsMap, saveTabsMap] = useAtom(tabsStorageAtom);
79
81
  const tabs = cellId ? (tabsMap.get(cellId) ?? []) : [];
80
82
 
81
- const [tabNum, setTabNum] = useState(0);
82
83
  const [selectedTab, setSelectedTab] = useState(DEFAULT_TAB_NAME);
84
+ const [tabCounter, setTabCounter] = useState(tabs.length);
85
+ const prevDisplayHeader = useRef(displayHeader);
86
+
87
+ // Auto-create a default chart tab when chart builder opens with no tabs
88
+ if (
89
+ displayHeader &&
90
+ !prevDisplayHeader.current &&
91
+ tabs.length === 0 &&
92
+ cellId
93
+ ) {
94
+ prevDisplayHeader.current = displayHeader;
95
+ const tabName = getChartTabName(0, NEW_CHART_TYPE);
96
+ const newTabs = new Map(tabsMap);
97
+ newTabs.set(cellId, [
98
+ { tabName, chartType: NEW_CHART_TYPE, config: getChartDefaults() },
99
+ ]);
100
+ saveTabsMap(newTabs);
101
+ setTabCounter(1);
102
+ setSelectedTab(tabName);
103
+ }
104
+ prevDisplayHeader.current = displayHeader;
83
105
 
84
106
  if (!displayHeader || (tabs.length === 0 && !displayHeader)) {
85
107
  return dataTable;
@@ -89,7 +111,7 @@ export const TablePanel: React.FC<TablePanelProps> = ({
89
111
  if (!cellId) {
90
112
  return;
91
113
  }
92
- const tabName = getChartTabName(tabNum, NEW_CHART_TYPE);
114
+ const tabName = getChartTabName(tabCounter, NEW_CHART_TYPE);
93
115
 
94
116
  const newTabs = new Map(tabsMap);
95
117
  newTabs.set(cellId, [
@@ -102,7 +124,7 @@ export const TablePanel: React.FC<TablePanelProps> = ({
102
124
  ]);
103
125
 
104
126
  saveTabsMap(newTabs);
105
- setTabNum(tabNum + 1);
127
+ setTabCounter(tabCounter + 1);
106
128
  setSelectedTab(tabName);
107
129
  };
108
130
 
@@ -110,14 +132,21 @@ export const TablePanel: React.FC<TablePanelProps> = ({
110
132
  if (!cellId) {
111
133
  return;
112
134
  }
135
+ const deletedIndex = tabs.findIndex((tab) => tab.tabName === tabName);
136
+ const remaining = tabs.filter((tab) => tab.tabName !== tabName);
113
137
  const newTabs = new Map(tabsMap);
114
- newTabs.set(
115
- cellId,
116
- tabs.filter((tab) => tab.tabName !== tabName),
117
- );
138
+ newTabs.set(cellId, remaining);
118
139
  saveTabsMap(newTabs);
119
- setSelectedTab(DEFAULT_TAB_NAME);
120
- setTabNum(tabNum - 1);
140
+
141
+ if (remaining.length === 0) {
142
+ onCloseChartBuilder?.();
143
+ } else if (tabName === selectedTab) {
144
+ if (deletedIndex < remaining.length) {
145
+ setSelectedTab(remaining[deletedIndex].tabName);
146
+ } else {
147
+ setSelectedTab(remaining[remaining.length - 1].tabName);
148
+ }
149
+ }
121
150
  };
122
151
 
123
152
  const saveTabChart = ({
@@ -178,7 +207,7 @@ export const TablePanel: React.FC<TablePanelProps> = ({
178
207
 
179
208
  return (
180
209
  <Tabs value={selectedTab} className="-mt-1">
181
- <TabsList>
210
+ <TabsList part="table-tabs">
182
211
  <TabsTrigger
183
212
  className="text-xs"
184
213
  value={DEFAULT_TAB_NAME}
@@ -72,7 +72,7 @@ export const ColumnExplorerPanel = ({
72
72
  });
73
73
 
74
74
  return (
75
- <div className="mt-5 mb-3">
75
+ <div className="mb-3">
76
76
  <span className="text-xs font-semibold ml-2 flex">
77
77
  {prettifyRowColumnCount(totalRows, totalColumns, locale)}
78
78
  <CopyClipboardIcon
@@ -283,7 +283,10 @@ const DataTableInternal = <TData,>({
283
283
  <div className={cn(wrapperClassName, "flex flex-col space-y-1")}>
284
284
  <FilterPills filters={filters} table={table} />
285
285
  <CellSelectionProvider>
286
- <div className={cn(className || "rounded-md border overflow-hidden")}>
286
+ <div
287
+ part="table-wrapper"
288
+ className={cn(className || "rounded-md border overflow-hidden")}
289
+ >
287
290
  <TableTopBar
288
291
  enableSearch={enableSearch}
289
292
  searchQuery={searchQuery}
@@ -313,6 +316,8 @@ const DataTableInternal = <TData,>({
313
316
  </Table>
314
317
  </div>
315
318
  <TableBottomBar
319
+ part="table-footer"
320
+ className="border-t border-border pt-1.5 pb-0.5"
316
321
  totalColumns={totalColumns}
317
322
  pagination={pagination}
318
323
  selection={selection}
@@ -24,7 +24,10 @@ export const LoadingTable = ({
24
24
 
25
25
  return (
26
26
  <div className={cn(wrapperClassName, "flex flex-col space-y-2")}>
27
- <div className={cn(className || "rounded-md border")}>
27
+ <div
28
+ part="table-wrapper"
29
+ className={cn(className || "rounded-md border")}
30
+ >
28
31
  <Table>
29
32
  <TableHeader>
30
33
  {Array.from({ length: 1 }).map((_, i) => (
@@ -34,7 +34,9 @@ export const CellSelectionStats = <TData,>({
34
34
  if (dataCellCount < 2) {
35
35
  return (
36
36
  <Tooltip content="Select multiple cells to see stats">
37
- <span className={cn("text-sm text-muted-foreground italic", className)}>
37
+ <span
38
+ className={cn("text-xs text-muted-foreground/80 italic", className)}
39
+ >
38
40
  No selection
39
41
  </span>
40
42
  </Tooltip>
@@ -247,7 +247,7 @@ export const RowViewerPanel: React.FC<RowViewerPanelProps> = ({
247
247
 
248
248
  return (
249
249
  <div
250
- className="flex flex-col gap-3 mt-4 focus:outline-hidden"
250
+ className="flex flex-col gap-3 focus:outline-hidden"
251
251
  ref={panelRef}
252
252
  tabIndex={-1}
253
253
  onKeyDown={handleKeyDown}
@@ -42,7 +42,7 @@ export interface TableExplorerPanelProps {
42
42
  }
43
43
 
44
44
  const tabTriggerClassName =
45
- "text-xs uppercase tracking-wide font-semibold cursor-pointer transition-colors";
45
+ "text-[13px] uppercase tracking-wide font-semibold cursor-pointer transition-colors";
46
46
  const activeClassName = "text-primary";
47
47
  const inactiveClassName = "hover:text-foreground";
48
48
 
@@ -110,7 +110,7 @@ export const TableExplorerPanel: React.FC<TableExplorerPanelProps> = ({
110
110
  <Tabs
111
111
  value={resolvedTab}
112
112
  onValueChange={(value) => onTabChange(value as PanelType)}
113
- className="h-full flex flex-col min-w-[350px]"
113
+ className="flex flex-col min-w-[350px]"
114
114
  >
115
115
  <Fill name={SlotNames.CONTEXT_AWARE_PANEL_HEADER}>
116
116
  <div className="flex items-center gap-1">
@@ -122,7 +122,7 @@ export const AddCellWithAI: React.FC<{
122
122
  transport: new StreamingChunkTransport(
123
123
  {
124
124
  api: runtimeManager.getAiURL("completion").toString(),
125
- headers: runtimeManager.headers(),
125
+ headers: () => runtimeManager.headers(),
126
126
  prepareSendMessagesRequest: async (options) => {
127
127
  const completionBody = await buildCompletionRequestBody(
128
128
  options.messages,
@@ -136,6 +136,7 @@ export const AddCellWithAI: React.FC<{
136
136
  };
137
137
 
138
138
  return {
139
+ api: runtimeManager.getAiURL("completion").toString(),
139
140
  body: body,
140
141
  };
141
142
  },
@@ -101,7 +101,7 @@ export const ContextAwarePanel: React.FC = () => {
101
101
  const renderBody = () => {
102
102
  return (
103
103
  <div className="pb-7 mb-4 h-full overflow-auto">
104
- <div className="p-3 border-b flex justify-between items-center">
104
+ <div className="px-3 py-2 border-b flex justify-between items-center">
105
105
  {renderModeToggle()}
106
106
  <Slot name={SlotNames.CONTEXT_AWARE_PANEL_HEADER} />
107
107
  <Button
@@ -13,6 +13,7 @@ import type {
13
13
  LspRestartRequest,
14
14
  LspRestartResponse,
15
15
  } from "@/core/network/types";
16
+ import { isWasm } from "@/core/wasm/utils";
16
17
  import { isAppConnected } from "@/core/websocket/connection-utils";
17
18
  import { useAsyncData } from "@/hooks/useAsyncData";
18
19
  import { useInterval } from "@/hooks/useInterval";
@@ -26,7 +27,7 @@ export const LspStatus: React.FC = () => {
26
27
  const setLspHealth = useSetAtom(lspHealthAtom);
27
28
 
28
29
  const { isFetching, data, refetch } = useAsyncData(async () => {
29
- if (!isAppConnected(connection)) {
30
+ if (isWasm() || !isAppConnected(connection)) {
30
31
  return null;
31
32
  }
32
33
 
@@ -18,6 +18,7 @@ import { python } from "@codemirror/lang-python";
18
18
  import { EditorState } from "@codemirror/state";
19
19
  import { EditorView } from "@codemirror/view";
20
20
  import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
21
+ import { cellId } from "@/__tests__/branded";
21
22
  import type { CellHandle } from "@/components/editor/notebook-cell";
22
23
  import { adaptiveLanguageConfiguration } from "@/core/codemirror/language/extension";
23
24
  import { OverridingHotkeyProvider } from "@/core/hotkeys/hotkeys";
@@ -121,7 +122,7 @@ describe("applyTransactionChanges edge cases", () => {
121
122
  apply([
122
123
  {
123
124
  type: "create-cell",
124
- cellId: "new-cell",
125
+ cellId: cellId("new-cell"),
125
126
  code: "configured",
126
127
  name: "",
127
128
  config: { hide_code: true, disabled: true, column: 1 },
@@ -141,12 +142,12 @@ describe("applyTransactionChanges edge cases", () => {
141
142
  apply([
142
143
  {
143
144
  type: "create-cell",
144
- cellId: "new-cell",
145
+ cellId: cellId("new-cell"),
145
146
  code: "new",
146
147
  name: "",
147
148
  config: {},
148
149
  },
149
- { type: "move-cell", cellId: "new-cell", before: a },
150
+ { type: "move-cell", cellId: cellId("new-cell"), before: a },
150
151
  ]);
151
152
  expect(pretty(state)).toMatchInlineSnapshot(`
152
153
  "
@@ -162,12 +163,12 @@ describe("applyTransactionChanges edge cases", () => {
162
163
  apply([
163
164
  {
164
165
  type: "create-cell",
165
- cellId: "new-cell",
166
+ cellId: cellId("new-cell"),
166
167
  code: "initial",
167
168
  name: "",
168
169
  config: {},
169
170
  },
170
- { type: "set-code", cellId: "new-cell", code: "updated" },
171
+ { type: "set-code", cellId: cellId("new-cell"), code: "updated" },
171
172
  ]);
172
173
  expect(pretty(state)).toMatchInlineSnapshot(`
173
174
  "
@@ -182,12 +183,12 @@ describe("applyTransactionChanges edge cases", () => {
182
183
  apply([
183
184
  {
184
185
  type: "create-cell",
185
- cellId: "ephemeral",
186
+ cellId: cellId("ephemeral"),
186
187
  code: "tmp",
187
188
  name: "",
188
189
  config: {},
189
190
  },
190
- { type: "delete-cell", cellId: "ephemeral" },
191
+ { type: "delete-cell", cellId: cellId("ephemeral") },
191
192
  ]);
192
193
  expect(pretty(state)).toMatchInlineSnapshot(`
193
194
  "
@@ -228,7 +229,7 @@ describe("applyTransactionChanges edge cases", () => {
228
229
  it("move-cell with missing after anchor falls back to end", () => {
229
230
  setup("a", "b");
230
231
  const [a] = state.cellIds.inOrderIds;
231
- apply([{ type: "move-cell", cellId: a, after: "nonexistent" as CellId }]);
232
+ apply([{ type: "move-cell", cellId: a, after: cellId("nonexistent") }]);
232
233
  expect(pretty(state)).toMatchInlineSnapshot(`
233
234
  "
234
235
  1: 'b'
@@ -240,7 +241,7 @@ describe("applyTransactionChanges edge cases", () => {
240
241
  it("move-cell with missing before anchor falls back to start", () => {
241
242
  setup("a", "b");
242
243
  const [, b] = state.cellIds.inOrderIds;
243
- apply([{ type: "move-cell", cellId: b, before: "nonexistent" as CellId }]);
244
+ apply([{ type: "move-cell", cellId: b, before: cellId("nonexistent") }]);
244
245
  expect(pretty(state)).toMatchInlineSnapshot(`
245
246
  "
246
247
  1: 'b'
@@ -254,8 +255,8 @@ describe("applyTransactionChanges edge cases", () => {
254
255
  apply([
255
256
  {
256
257
  type: "move-cell",
257
- cellId: "nonexistent" as CellId,
258
- after: "0" as CellId,
258
+ cellId: cellId("nonexistent"),
259
+ after: cellId("0"),
259
260
  },
260
261
  ]);
261
262
  expect(pretty(state)).toMatchInlineSnapshot(`
@@ -425,7 +425,7 @@ export function fromDocumentChanges(
425
425
  cellId,
426
426
  before,
427
427
  code: change.code,
428
- newCellId: change.cellId as CellId,
428
+ newCellId: change.cellId,
429
429
  autoFocus: false,
430
430
  hideCode: change.config?.hide_code ?? false,
431
431
  },
@@ -433,14 +433,14 @@ export function fromDocumentChanges(
433
433
  if (change.name) {
434
434
  actions.push({
435
435
  type: "updateCellName",
436
- payload: { cellId: change.cellId as CellId, name: change.name },
436
+ payload: { cellId: change.cellId, name: change.name },
437
437
  });
438
438
  }
439
439
  if (change.config?.disabled != null || change.config?.column != null) {
440
440
  actions.push({
441
441
  type: "updateCellConfig",
442
442
  payload: {
443
- cellId: change.cellId as CellId,
443
+ cellId: change.cellId,
444
444
  config: {
445
445
  ...(change.config.disabled != null && {
446
446
  disabled: change.config.disabled,
@@ -460,7 +460,7 @@ export function fromDocumentChanges(
460
460
  case "delete-cell":
461
461
  actions.push({
462
462
  type: "deleteCell",
463
- payload: { cellId: change.cellId as CellId },
463
+ payload: { cellId: change.cellId },
464
464
  });
465
465
  break;
466
466
 
@@ -471,7 +471,7 @@ export function fromDocumentChanges(
471
471
  // missing. No-ops if the cell itself doesn't exist.
472
472
  case "move-cell": {
473
473
  const ids = [...getCurrentCellIds()];
474
- const cellId = change.cellId as CellId;
474
+ const cellId = change.cellId;
475
475
  const idx = ids.indexOf(cellId);
476
476
  if (idx < 0) {
477
477
  break;
@@ -507,7 +507,7 @@ export function fromDocumentChanges(
507
507
  case "reorder-cells":
508
508
  actions.push({
509
509
  type: "setCellIds",
510
- payload: { cellIds: change.cellIds as CellId[] },
510
+ payload: { cellIds: change.cellIds },
511
511
  });
512
512
  break;
513
513
 
@@ -518,7 +518,7 @@ export function fromDocumentChanges(
518
518
  actions.push({
519
519
  type: "setCellCodes",
520
520
  payload: {
521
- ids: [change.cellId as CellId],
521
+ ids: [change.cellId],
522
522
  codes: [change.code],
523
523
  codeIsStale: true,
524
524
  },
@@ -530,7 +530,7 @@ export function fromDocumentChanges(
530
530
  case "set-name":
531
531
  actions.push({
532
532
  type: "updateCellName",
533
- payload: { cellId: change.cellId as CellId, name: change.name },
533
+ payload: { cellId: change.cellId, name: change.name },
534
534
  });
535
535
  break;
536
536
 
@@ -542,7 +542,7 @@ export function fromDocumentChanges(
542
542
  actions.push({
543
543
  type: "updateCellConfig",
544
544
  payload: {
545
- cellId: change.cellId as CellId,
545
+ cellId: change.cellId,
546
546
  config: {
547
547
  ...(change.hideCode != null && { hide_code: change.hideCode }),
548
548
  ...(change.disabled != null && { disabled: change.disabled }),