@marimo-team/frontend 0.21.2-dev37 → 0.21.2-dev40

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 (141) hide show
  1. package/dist/assets/{CellStatus-DYdGvia_.js → CellStatus-IHJk0CT8.js} +1 -1
  2. package/dist/assets/{JsonOutput-CN6rPeEV.js → JsonOutput-BIbkaOuk.js} +9 -9
  3. package/dist/assets/{MarimoErrorOutput-BhfAlCn4.js → MarimoErrorOutput-hYOXA8Ml.js} +5 -5
  4. package/dist/assets/{RenderHTML-BS4HOKgA.js → RenderHTML-DduaBOBv.js} +1 -1
  5. package/dist/assets/{add-cell-with-ai-pEba6oGB.js → add-cell-with-ai-Cbktc1B0.js} +9 -9
  6. package/dist/assets/{add-connection-dialog-9odisycO.js → add-connection-dialog-BewAq3li.js} +23 -23
  7. package/dist/assets/{agent-panel-BSEF-vxf.js → agent-panel-CtAqa-P9.js} +5 -5
  8. package/dist/assets/{ai-model-dropdown-CBQHQst1.js → ai-model-dropdown-DgMZ5T_L.js} +3 -3
  9. package/dist/assets/{app-config-button-XC0eJuj1.js → app-config-button-BLGBmcfT.js} +1 -1
  10. package/dist/assets/{azure-Cl-hv4CT.js → azure-BeB26UeM.js} +1 -1
  11. package/dist/assets/{cell-editor-CtnuCOtA.js → cell-editor-Ti3F6axu.js} +12 -13
  12. package/dist/assets/{cell-link-D8ZUYcpP.js → cell-link-BEnixmCb.js} +1 -1
  13. package/dist/assets/{cells-DZyIQGyJ.js → cells-DqKJsMUm.js} +79 -78
  14. package/dist/assets/{chat-display-CvRHSFG6.js → chat-display-B0hV2K6L.js} +1 -1
  15. package/dist/assets/{chat-panel-BAGhr_FT.js → chat-panel-CNGIGWIs.js} +2 -2
  16. package/dist/assets/{column-preview-BuB6sz5V.js → column-preview-CVqBaB8R.js} +1 -1
  17. package/dist/assets/{command-DxWyVVPx.js → command-DpusMohg.js} +1 -1
  18. package/dist/assets/{command-palette-BjbqAF2r.js → command-palette-BfuIslSf.js} +1 -1
  19. package/dist/assets/{common-DJx7lN8H.js → common-CYWtZYKm.js} +1 -1
  20. package/dist/assets/{components-ENebof0d.js → components-C4dDqD7U.js} +1 -1
  21. package/dist/assets/{components-DSj55gbq.js → components-DZQ7ndNS.js} +1 -1
  22. package/dist/assets/{context-aware-panel-C4GJI0g0.js → context-aware-panel-DEVrXLoa.js} +3 -3
  23. package/dist/assets/{datasource-Be25L0nm.js → datasource-Bo8w993m.js} +2 -2
  24. package/dist/assets/{dependency-graph-panel-DwIVdeIJ.js → dependency-graph-panel-u9oPy09H.js} +4 -4
  25. package/dist/assets/{documentation-panel-78VOc9_O.js → documentation-panel-BQRmNRb_.js} +1 -1
  26. package/dist/assets/{download-BlNsDzA2.js → download-DtyAt8iO.js} +1 -1
  27. package/dist/assets/{edit-page-vExGqsee.js → edit-page-Cqvk7zWH.js} +7 -7
  28. package/dist/assets/{error-panel-D8Yl4tpB.js → error-panel-DclemF57.js} +1 -1
  29. package/dist/assets/{file-explorer-panel-CbIYi1SW.js → file-explorer-panel-D706RRJ-.js} +3 -3
  30. package/dist/assets/{file-icons-zgA9qh-d.js → file-icons-D4HcGlr_.js} +1 -1
  31. package/dist/assets/{floating-outline-vyq-JnTK.js → floating-outline-B86gVj23.js} +1 -1
  32. package/dist/assets/{focus-Cnr-VB8o.js → focus-B85PAbhR.js} +1 -1
  33. package/dist/assets/{form-LO1BJ8WB.js → form--K3o18rQ.js} +2 -2
  34. package/dist/assets/{globals-3fE4PBWj.js → globals-C_t1YYpc.js} +1 -1
  35. package/dist/assets/{home-page-DFcaf-s1.js → home-page-C5LO1JXE.js} +1 -1
  36. package/dist/assets/{hooks-NxoY-x2K.js → hooks-W-qT5c6l.js} +1 -1
  37. package/dist/assets/{html-to-image-DDWA96Ex.js → html-to-image-CoMmxXBd.js} +1 -1
  38. package/dist/assets/{index-OH-GH0bF.js → index-BSzIstPK.js} +14 -14
  39. package/dist/assets/{kiosk-mode-D4FbpWVp.js → kiosk-mode-zv0zIGqw.js} +1 -1
  40. package/dist/assets/{layout-qNHT912R.js → layout-C_JVKYrz.js} +3 -3
  41. package/dist/assets/{logs-panel-14Cp2UUn.js → logs-panel-DowtOCA5.js} +1 -1
  42. package/dist/assets/{markdown-renderer-CH1ffrUG.js → markdown-renderer-CNaYgTOF.js} +1 -1
  43. package/dist/assets/{mode-OBdgd6Zz.js → mode-Bn_XF6AW.js} +1 -1
  44. package/dist/assets/{name-cell-input-BmkGdvnh.js → name-cell-input-CVt9kv6F.js} +1 -1
  45. package/dist/assets/{outline-panel-Bn1SN1eu.js → outline-panel-ClI23gcy.js} +1 -1
  46. package/dist/assets/{packages-panel-Cyb4BUmM.js → packages-panel-BW2m-sNe.js} +1 -1
  47. package/dist/assets/{panels-DfEUpHpA.js → panels-B2DAaT1E.js} +1 -1
  48. package/dist/assets/{process-output-CC4VhdkC.js → process-output-BewZuQwI.js} +1 -1
  49. package/dist/assets/{readonly-python-code-Cq9EKL61.js → readonly-python-code-BgTK_w0H.js} +1 -1
  50. package/dist/assets/{run-page-Df9bi2J7.js → run-page-DMpRvHgf.js} +1 -1
  51. package/dist/assets/{scratchpad-panel-vKbONRfT.js → scratchpad-panel-ClDi2sdV.js} +1 -1
  52. package/dist/assets/{session-panel-DPoC2CoL.js → session-panel-C-RbWN-G.js} +1 -1
  53. package/dist/assets/{snippets-panel-D7W6PE7E.js → snippets-panel-DCdeI2hB.js} +1 -1
  54. package/dist/assets/{state-Br7baqTT.js → state-CJ2HIW-e.js} +1 -1
  55. package/dist/assets/{switch-Cds8TleW.js → switch-O_7alg7G.js} +1 -1
  56. package/dist/assets/tracing-BxcUfhUv.js +1 -0
  57. package/dist/assets/{tracing-panel-BpKr5n4H.js → tracing-panel-DK4YfdoZ.js} +2 -2
  58. package/dist/assets/{useAddCell-XcxY_cZw.js → useAddCell-BpBkEpP6.js} +1 -1
  59. package/dist/assets/{useBoolean-0C18DvVA.js → useBoolean-Cb23qnhy.js} +1 -1
  60. package/dist/assets/{useCellActionButton-DFNSBzoc.js → useCellActionButton-fJ6SGX-Z.js} +1 -1
  61. package/dist/assets/{useDeleteCell-DLdqnODD.js → useDeleteCell-B4nKRnTM.js} +1 -1
  62. package/dist/assets/{useDependencyPanelTab-_E0t8nfy.js → useDependencyPanelTab-BUjZxWnG.js} +1 -1
  63. package/dist/assets/{useNotebookActions-DlbYUPWD.js → useNotebookActions-CooxVonM.js} +1 -1
  64. package/dist/assets/{useRunCells-YR_s_yuq.js → useRunCells-D5wCIJdU.js} +1 -1
  65. package/dist/assets/{useSplitCell-BNkvoKCq.js → useSplitCell-D8kqiCTS.js} +1 -1
  66. package/dist/index.html +32 -32
  67. package/package.json +1 -1
  68. package/src/__mocks__/notebook.ts +9 -9
  69. package/src/__tests__/branded.ts +20 -0
  70. package/src/components/data-table/charts/__tests__/storage.test.ts +7 -7
  71. package/src/components/editor/__tests__/data-attributes.test.tsx +8 -8
  72. package/src/components/editor/ai/__tests__/completion-utils.test.ts +15 -15
  73. package/src/components/editor/navigation/__tests__/clipboard.test.ts +2 -2
  74. package/src/components/editor/navigation/__tests__/selection.test.ts +7 -6
  75. package/src/components/editor/navigation/__tests__/state.test.ts +8 -7
  76. package/src/components/editor/output/MarimoErrorOutput.tsx +7 -7
  77. package/src/components/editor/output/__tests__/traceback.test.tsx +4 -4
  78. package/src/components/editor/output/console/__tests__/ConsoleOutput.test.tsx +4 -4
  79. package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +8 -1
  80. package/src/components/storage/storage-inspector.tsx +1 -2
  81. package/src/components/tracing/tracing.tsx +3 -1
  82. package/src/core/ai/__tests__/staged-cells.test.ts +9 -8
  83. package/src/core/ai/context/providers/__tests__/cell-output.test.ts +31 -31
  84. package/src/core/ai/context/providers/__tests__/datasource.test.ts +3 -3
  85. package/src/core/ai/context/providers/__tests__/tables.test.ts +3 -2
  86. package/src/core/ai/context/providers/__tests__/variable.test.ts +84 -63
  87. package/src/core/ai/tools/__tests__/edit-notebook-tool.test.ts +10 -9
  88. package/src/core/ai/tools/__tests__/run-cells-tool.test.ts +6 -6
  89. package/src/core/ai/tools/edit-notebook-tool.ts +3 -3
  90. package/src/core/cells/__tests__/add-missing-import.test.ts +3 -3
  91. package/src/core/cells/__tests__/cells.test.ts +192 -135
  92. package/src/core/cells/__tests__/focus.test.ts +5 -4
  93. package/src/core/cells/__tests__/logs.test.ts +13 -12
  94. package/src/core/cells/__tests__/pending-delete-service.test.tsx +3 -3
  95. package/src/core/cells/__tests__/runs.test.ts +22 -21
  96. package/src/core/cells/__tests__/scrollCellIntoView.test.ts +8 -7
  97. package/src/core/cells/__tests__/session.test.ts +23 -22
  98. package/src/core/cells/cells.ts +1 -1
  99. package/src/core/cells/ids.ts +5 -5
  100. package/src/core/cells/logs.ts +2 -2
  101. package/src/core/cells/runs.ts +6 -8
  102. package/src/core/codemirror/__tests__/format.test.ts +34 -36
  103. package/src/core/codemirror/__tests__/setup.test.ts +2 -2
  104. package/src/core/codemirror/cells/__tests__/extensions.test.ts +114 -0
  105. package/src/core/codemirror/cells/__tests__/traceback-decorations.test.ts +33 -32
  106. package/src/core/codemirror/cells/extensions.ts +66 -23
  107. package/src/core/codemirror/copilot/__tests__/getCodes.test.ts +12 -13
  108. package/src/core/codemirror/language/__tests__/utils.test.ts +3 -3
  109. package/src/core/codemirror/language/embedded/__tests__/embedded-python.test.ts +7 -8
  110. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +4 -3
  111. package/src/core/codemirror/reactive-references/__tests__/analyzer.test.ts +7 -6
  112. package/src/core/codemirror/reactive-references/analyzer.ts +2 -2
  113. package/src/core/codemirror/rtc/loro/__tests__/sync.test.ts +52 -0
  114. package/src/core/codemirror/rtc/loro/sync.ts +1 -0
  115. package/src/core/datasets/__tests__/data-source.test.ts +5 -6
  116. package/src/core/datasets/state.ts +1 -1
  117. package/src/core/errors/__tests__/errors.test.ts +2 -1
  118. package/src/core/export/__tests__/hooks.test.ts +37 -36
  119. package/src/core/islands/main.ts +2 -7
  120. package/src/core/kernel/__tests__/handlers.test.ts +5 -4
  121. package/src/core/kernel/handlers.ts +7 -4
  122. package/src/core/network/DeferredRequestRegistry.ts +2 -2
  123. package/src/core/network/__tests__/CachingRequestRegistry.test.ts +9 -10
  124. package/src/core/network/__tests__/DeferredRequestRegistry.test.ts +4 -6
  125. package/src/core/static/__tests__/virtual-file-tracker.test.ts +8 -8
  126. package/src/core/static/virtual-file-tracker.ts +1 -1
  127. package/src/core/storage/__tests__/state.test.ts +31 -21
  128. package/src/core/storage/state.ts +1 -1
  129. package/src/core/variables/__tests__/state.test.ts +6 -6
  130. package/src/core/variables/types.ts +2 -2
  131. package/src/core/wasm/__tests__/state.test.ts +8 -8
  132. package/src/core/websocket/useMarimoKernelConnection.tsx +12 -15
  133. package/src/plugins/impl/anywidget/model.ts +1 -2
  134. package/src/stories/cell.stories.tsx +8 -8
  135. package/src/stories/layout/vertical/one-column.stories.tsx +9 -8
  136. package/src/stories/log-viewer.stories.tsx +8 -8
  137. package/src/stories/variables.stories.tsx +2 -2
  138. package/src/utils/__tests__/download.test.tsx +19 -18
  139. package/src/utils/json/base64.ts +3 -3
  140. package/src/utils/traceback.ts +5 -3
  141. package/dist/assets/tracing-DFGiZoMP.js +0 -1
@@ -2,9 +2,15 @@
2
2
 
3
3
  import { closeCompletion, completionStatus } from "@codemirror/autocomplete";
4
4
  import { type Extension, Prec } from "@codemirror/state";
5
- import { EditorView, type KeyBinding, keymap } from "@codemirror/view";
5
+ import {
6
+ EditorView,
7
+ type KeyBinding,
8
+ keymap,
9
+ type ViewUpdate,
10
+ } from "@codemirror/view";
6
11
  import { createTracebackInfoAtom } from "@/core/cells/cells";
7
12
  import { type CellId, HTMLCellId, SCRATCH_CELL_ID } from "@/core/cells/ids";
13
+ import { loroSyncAnnotation } from "@/core/codemirror/rtc/loro/sync";
8
14
  import type { KeymapConfig } from "@/core/config/config-schema";
9
15
  import type { HotkeyProvider } from "@/core/hotkeys/hotkeys";
10
16
  import { duplicateWithCtrlModifier } from "@/core/hotkeys/shortcuts";
@@ -330,6 +336,53 @@ function cellCodeEditing(hotkeys: HotkeyProvider): Extension[] {
330
336
  return [onChangePlugin, formatKeymapExtension(hotkeys)];
331
337
  }
332
338
 
339
+ const MARKDOWN_AUTORUN_USER_EVENTS = ["input", "delete", "undo", "redo"];
340
+
341
+ function shouldAutorunMarkdownUpdate({
342
+ docChanged,
343
+ transactions,
344
+ predicate = () => true,
345
+ hasFocus = false,
346
+ }: Pick<ViewUpdate, "docChanged" | "transactions"> & {
347
+ predicate?: () => boolean;
348
+ hasFocus?: boolean;
349
+ }): boolean {
350
+ // If the doc didn't change, ignore.
351
+ if (!docChanged) {
352
+ return false;
353
+ }
354
+
355
+ // The caller decides when markdown autorun is allowed, e.g. not for
356
+ // f-strings where rerunning on every keystroke is usually incorrect.
357
+ if (!predicate()) {
358
+ return false;
359
+ }
360
+
361
+ // This happens on mount when we start in markdown mode.
362
+ // Ignore formatting changes so language switches don't trigger autorun.
363
+ const isFormattingChange = transactions.some((tr) =>
364
+ tr.effects.some((effect) => effect.is(formattingChangeEffect)),
365
+ );
366
+ if (isFormattingChange) {
367
+ return false;
368
+ }
369
+
370
+ return transactions.some((tr) => {
371
+ // Ignore RTC sync changes to avoid duplicate runs from remote edits.
372
+ if (tr.annotation(loroSyncAnnotation) !== undefined) {
373
+ return false;
374
+ }
375
+
376
+ // Prefer explicit local edit transactions, but keep a focused fallback for
377
+ // local rewrite paths like split-cell, which can update markdown content
378
+ // without annotating a user event.
379
+ return (
380
+ MARKDOWN_AUTORUN_USER_EVENTS.some((kind) => tr.isUserEvent(kind)) ||
381
+ hasFocus
382
+ );
383
+ });
384
+ }
385
+
333
386
  /**
334
387
  * Extension for auto-running markdown cells
335
388
  */
@@ -339,30 +392,16 @@ export function markdownAutoRunExtension({
339
392
  predicate: () => boolean;
340
393
  }): Extension {
341
394
  return EditorView.updateListener.of((update) => {
342
- // If the doc didn't change, ignore
343
- if (!update.docChanged) {
344
- return;
345
- }
346
-
347
- // If not focused, ignore
348
- // This can cause multiple runs when in RTC mode
349
- if (!update.view.hasFocus) {
350
- return;
351
- }
352
-
353
- if (!predicate()) {
395
+ if (
396
+ !shouldAutorunMarkdownUpdate({
397
+ docChanged: update.docChanged,
398
+ transactions: update.transactions,
399
+ predicate,
400
+ hasFocus: update.view.hasFocus,
401
+ })
402
+ ) {
354
403
  return;
355
404
  }
356
-
357
- // This happens on mount when we start in markdown mode
358
- const isFormattingChange = update.transactions.some((tr) =>
359
- tr.effects.some((effect) => effect.is(formattingChangeEffect)),
360
- );
361
- if (isFormattingChange) {
362
- // Ignore formatting changes
363
- return;
364
- }
365
-
366
405
  const actions = update.view.state.facet(cellActionsState);
367
406
  actions.onRun();
368
407
  });
@@ -388,3 +427,7 @@ export function cellBundle({
388
427
  ),
389
428
  ];
390
429
  }
430
+
431
+ export const exportedForTesting = {
432
+ shouldAutorunMarkdownUpdate,
433
+ };
@@ -3,28 +3,27 @@
3
3
  import { EditorState } from "@codemirror/state";
4
4
  import { EditorView } from "@codemirror/view";
5
5
  import { describe, expect, it } from "vitest";
6
+ import { cellId, variableName } from "@/__tests__/branded";
6
7
  import { initialNotebookState, notebookAtom } from "@/core/cells/cells";
7
- import type { CellId } from "@/core/cells/ids";
8
8
  import { OverridingHotkeyProvider } from "@/core/hotkeys/hotkeys";
9
9
  import { store } from "@/core/state/jotai";
10
10
  import { variablesAtom } from "@/core/variables/state";
11
- import type { VariableName, Variables } from "@/core/variables/types";
12
11
  import { MultiColumn } from "@/utils/id-tree";
13
12
  import { cellConfigExtension } from "../../config/extension";
14
13
  import { adaptiveLanguageConfiguration } from "../../language/extension";
15
14
  import { getCodes, getTopologicalCellIds } from "../getCodes";
16
15
 
17
16
  const Cells = {
18
- cell1: "cell1" as CellId,
19
- cell2: "cell2" as CellId,
20
- cell3: "cell3" as CellId,
21
- cell4: "cell4" as CellId,
17
+ cell1: cellId("cell1"),
18
+ cell2: cellId("cell2"),
19
+ cell3: cellId("cell3"),
20
+ cell4: cellId("cell4"),
22
21
  };
23
22
 
24
23
  const Variables = {
25
- var1: "var1" as VariableName,
26
- var2: "var2" as VariableName,
27
- var3: "var3" as VariableName,
24
+ var1: variableName("var1"),
25
+ var2: variableName("var2"),
26
+ var3: variableName("var3"),
28
27
  };
29
28
 
30
29
  function createMockEditorView(code: string) {
@@ -33,7 +32,7 @@ function createMockEditorView(code: string) {
33
32
  doc: code,
34
33
  extensions: [
35
34
  adaptiveLanguageConfiguration({
36
- cellId: "cell1" as CellId,
35
+ cellId: cellId("cell1"),
37
36
  completionConfig: {
38
37
  copilot: false,
39
38
  activate_on_typing: true,
@@ -45,7 +44,7 @@ function createMockEditorView(code: string) {
45
44
  lspConfig: {},
46
45
  }),
47
46
  cellConfigExtension({
48
- cellId: "cell1" as CellId,
47
+ cellId: cellId("cell1"),
49
48
  completionConfig: {
50
49
  copilot: false,
51
50
  activate_on_typing: true,
@@ -71,7 +70,7 @@ describe("getTopologicalCellIds", () => {
71
70
  it("should return topologically sorted cell IDs", () => {
72
71
  // Setup mock data
73
72
  const cellIds = [Cells.cell1, Cells.cell2, Cells.cell3, Cells.cell4];
74
- const variables: Variables = {
73
+ const variables = {
75
74
  [Variables.var1]: {
76
75
  name: Variables.var1,
77
76
  declaredBy: [Cells.cell1],
@@ -125,7 +124,7 @@ describe("getTopologicalCellIds", () => {
125
124
 
126
125
  it("should put new cells (with no dependencies) at the end", () => {
127
126
  const cellIds = [Cells.cell1, Cells.cell2, Cells.cell3, Cells.cell4];
128
- const variables: Variables = {
127
+ const variables = {
129
128
  [Variables.var1]: {
130
129
  name: Variables.var1,
131
130
  declaredBy: [Cells.cell2],
@@ -3,7 +3,7 @@
3
3
  import { EditorState } from "@codemirror/state";
4
4
  import { EditorView } from "@codemirror/view";
5
5
  import { describe, expect, it } from "vitest";
6
- import type { CellId } from "@/core/cells/ids";
6
+ import { cellId } from "@/__tests__/branded";
7
7
  import { OverridingHotkeyProvider } from "@/core/hotkeys/hotkeys";
8
8
  import { cellConfigExtension } from "../../config/extension";
9
9
  import { adaptiveLanguageConfiguration, switchLanguage } from "../extension";
@@ -26,12 +26,12 @@ function createEditor(doc: string) {
26
26
  codeium_api_key: null,
27
27
  },
28
28
  hotkeys: new OverridingHotkeyProvider({}),
29
- cellId: "cell1" as CellId,
29
+ cellId: cellId("cell1"),
30
30
  placeholderType: "marimo-import",
31
31
  lspConfig: {},
32
32
  }),
33
33
  cellConfigExtension({
34
- cellId: "cell1" as CellId,
34
+ cellId: cellId("cell1"),
35
35
  completionConfig: {
36
36
  activate_on_typing: true,
37
37
  signature_hint_on_typing: false,
@@ -5,10 +5,9 @@ import { python } from "@codemirror/lang-python";
5
5
  import { EditorState } from "@codemirror/state";
6
6
  import type { InlineContext } from "@lezer/markdown";
7
7
  import { beforeEach, describe, expect, it } from "vitest";
8
- import type { CellId } from "@/core/cells/ids";
8
+ import { cellId, variableName } from "@/__tests__/branded";
9
9
  import { store } from "@/core/state/jotai";
10
10
  import { variablesAtom } from "@/core/variables/state";
11
- import type { VariableName, Variables } from "@/core/variables/types";
12
11
  import { parsePython, variableCompletionSource } from "../embedded-python";
13
12
 
14
13
  const IS_ACTIVE = () => true;
@@ -51,16 +50,16 @@ describe("parsePython", () => {
51
50
 
52
51
  describe("variableCompletionSource", () => {
53
52
  beforeEach(() => {
54
- const mockCellId = "cell-1" as CellId;
55
- const mockVariables: Variables = {
56
- ["var1" as VariableName]: {
57
- name: "var1" as VariableName,
53
+ const mockCellId = cellId("cell-1");
54
+ const mockVariables = {
55
+ [variableName("var1")]: {
56
+ name: variableName("var1"),
58
57
  dataType: "int",
59
58
  declaredBy: [mockCellId],
60
59
  usedBy: [mockCellId],
61
60
  },
62
- ["var2" as VariableName]: {
63
- name: "var2" as VariableName,
61
+ [variableName("var2")]: {
62
+ name: variableName("var2"),
64
63
  dataType: "str",
65
64
  declaredBy: [mockCellId],
66
65
  usedBy: [mockCellId],
@@ -8,6 +8,7 @@ import {
8
8
  } from "@marimo-team/codemirror-languageserver";
9
9
  import { beforeEach, describe, expect, it, type Mocked, vi } from "vitest";
10
10
  import * as LSP from "vscode-languageserver-protocol";
11
+ import { cellId } from "@/__tests__/branded";
11
12
  import type { CellId } from "@/core/cells/ids";
12
13
  import { store } from "@/core/state/jotai";
13
14
  import { topologicalCodesAtom } from "../../copilot/getCodes";
@@ -19,9 +20,9 @@ import { NotebookLanguageServerClient } from "../notebook-lsp";
19
20
  import { CellDocumentUri, type ILanguageServerClient } from "../types";
20
21
 
21
22
  const Cells = {
22
- cell1: "cell1" as CellId,
23
- cell2: "cell2" as CellId,
24
- cell3: "cell3" as CellId,
23
+ cell1: cellId("cell1"),
24
+ cell2: cellId("cell2"),
25
+ cell3: cellId("cell3"),
25
26
  };
26
27
 
27
28
  describe("createNotebookLens", () => {
@@ -3,8 +3,8 @@
3
3
  import { python } from "@codemirror/lang-python";
4
4
  import { EditorState } from "@codemirror/state";
5
5
  import { describe, expect, test } from "vitest";
6
- import type { CellId } from "@/core/cells/ids";
7
- import type { VariableName, Variables } from "@/core/variables/types";
6
+ import { cellId, variableName } from "@/__tests__/branded";
7
+ import type { Variables } from "@/core/variables/types";
8
8
  import { findReactiveVariables, type ReactiveVariableRange } from "../analyzer";
9
9
 
10
10
  describe("findReactiveVariables - Lexical Scoping", () => {
@@ -1199,16 +1199,17 @@ class Foo:
1199
1199
  function runHighlight(variableNames: string[], code: string): string {
1200
1200
  const variables: Variables = {};
1201
1201
  for (const name of variableNames) {
1202
- variables[name as VariableName] = {
1203
- name: name as VariableName,
1204
- declaredBy: ["other-cell" as CellId],
1202
+ const varName = variableName(name);
1203
+ variables[varName] = {
1204
+ name: varName,
1205
+ declaredBy: [cellId("other-cell")],
1205
1206
  usedBy: [],
1206
1207
  value: "test-value",
1207
1208
  dataType: "str",
1208
1209
  };
1209
1210
  }
1210
1211
  const ranges = findReactiveVariables({
1211
- cellId: "current-cell" as CellId,
1212
+ cellId: cellId("current-cell"),
1212
1213
  state: EditorState.create({
1213
1214
  doc: code,
1214
1215
  extensions: [python()],
@@ -3,7 +3,7 @@
3
3
  import { syntaxTree } from "@codemirror/language";
4
4
  import type { EditorState } from "@codemirror/state";
5
5
  import type { SyntaxNode, Tree, TreeCursor } from "@lezer/common";
6
- import type { CellId } from "@/core/cells/ids";
6
+ import { type CellId, SETUP_CELL_ID } from "@/core/cells/ids";
7
7
  import type { VariableName, Variables } from "@/core/variables/types";
8
8
 
9
9
  export interface ReactiveVariableRange {
@@ -52,7 +52,7 @@ export function findReactiveVariables(options: {
52
52
  const variable = options.variables[name as VariableName];
53
53
  return (
54
54
  variable.dataType !== "module" &&
55
- !variable.declaredBy.includes("setup" as CellId) &&
55
+ !variable.declaredBy.includes(SETUP_CELL_ID) &&
56
56
  !variable.declaredBy.includes(options.cellId)
57
57
  );
58
58
  }),
@@ -0,0 +1,52 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { EditorState } from "@codemirror/state";
4
+ import type { EditorView } from "@codemirror/view";
5
+ import { LoroDoc, LoroText } from "loro-crdt";
6
+ import { describe, expect, it, vi } from "vitest";
7
+ import { LoroSyncPluginValue, loroSyncAnnotation } from "../sync";
8
+
9
+ describe("LoroSyncPluginValue", () => {
10
+ it("annotates the initial reconciliation dispatch as RTC sync", async () => {
11
+ const dispatch = vi.fn();
12
+ const view = {
13
+ state: EditorState.create({ doc: "local" }),
14
+ dispatch,
15
+ } as unknown as EditorView;
16
+
17
+ const doc = new LoroDoc();
18
+ const text = doc
19
+ .getMap("codes")
20
+ .getOrCreateContainer("cell-1", new LoroText());
21
+ text.insert(0, "remote");
22
+
23
+ const plugin = new LoroSyncPluginValue(
24
+ view,
25
+ doc,
26
+ ["codes", "cell-1"],
27
+ () => text,
28
+ );
29
+
30
+ await Promise.resolve();
31
+
32
+ expect(dispatch).toHaveBeenCalledTimes(1);
33
+ expect(dispatch).toHaveBeenCalledWith(
34
+ expect.objectContaining({
35
+ changes: [
36
+ {
37
+ from: 0,
38
+ to: view.state.doc.length,
39
+ insert: "remote",
40
+ },
41
+ ],
42
+ annotations: [
43
+ expect.objectContaining({
44
+ type: loroSyncAnnotation,
45
+ }),
46
+ ],
47
+ }),
48
+ );
49
+
50
+ plugin.destroy();
51
+ });
52
+ });
@@ -72,6 +72,7 @@ export class LoroSyncPluginValue implements PluginValue {
72
72
  insert: text.toString(),
73
73
  },
74
74
  ],
75
+ annotations: [loroSyncAnnotation.of(this)],
75
76
  });
76
77
  });
77
78
  }
@@ -1,5 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import { beforeEach, describe, expect, it } from "vitest";
3
+ import { variableName } from "@/__tests__/branded";
3
4
  import type { DataTable } from "@/core/kernel/messages";
4
5
  import type { VariableName } from "@/core/variables/types";
5
6
  import {
@@ -182,7 +183,7 @@ describe("filtering data sources", () => {
182
183
  });
183
184
 
184
185
  it("keeps matching variables and internal engines", () => {
185
- const filtered = filterDataSources(["conn1" as unknown as VariableName]);
186
+ const filtered = filterDataSources([variableName("conn1")]);
186
187
  expect(filtered.connectionsMap.size).toBe(defaultConnSize + 1);
187
188
  expect(filtered.connectionsMap.has("conn1" as ConnectionName)).toBe(true);
188
189
  for (const engine of INTERNAL_SQL_ENGINES) {
@@ -191,16 +192,14 @@ describe("filtering data sources", () => {
191
192
  });
192
193
 
193
194
  it("filters out non-matching variables", () => {
194
- const filtered = filterDataSources([
195
- "non_existent" as unknown as VariableName,
196
- ]);
195
+ const filtered = filterDataSources([variableName("non_existent")]);
197
196
  expect(filtered.connectionsMap.size).toBe(defaultConnSize);
198
197
  });
199
198
 
200
199
  it("handles mix of matching and non-matching variables", () => {
201
200
  const filtered = filterDataSources([
202
- "conn1" as unknown as VariableName,
203
- "non_existent" as unknown as VariableName,
201
+ variableName("conn1"),
202
+ variableName("non_existent"),
204
203
  ]);
205
204
  expect(filtered.connectionsMap.size).toBe(defaultConnSize + 1);
206
205
  expect(filtered.connectionsMap.has("conn1" as ConnectionName)).toBe(true);
@@ -83,7 +83,7 @@ const {
83
83
  if (!table.variable_name) {
84
84
  return true;
85
85
  }
86
- return names.has(table.variable_name as VariableName);
86
+ return names.has(table.variable_name);
87
87
  });
88
88
  return { ...state, tables };
89
89
  },
@@ -1,5 +1,6 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
  import { describe, expect, it } from "vitest";
3
+ import { cellId } from "@/__tests__/branded";
3
4
  import type { MarimoError } from "@/core/kernel/messages";
4
5
  import { getAutoFixes, getImportCode } from "../errors";
5
6
 
@@ -25,7 +26,7 @@ describe("getAutoFixes", () => {
25
26
  const error: MarimoError = {
26
27
  type: "multiple-defs",
27
28
  name: "foo",
28
- cells: ["foo"],
29
+ cells: [cellId("foo")],
29
30
  };
30
31
 
31
32
  const fixes = getAutoFixes(error, opts);