@marimo-team/islands 0.15.5 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CBeIYi8p.js → ConnectedDataExplorerComponent-DyqLQGPc.js} +1567 -1544
  2. package/dist/{ImageComparisonComponent-Bk0a0xBq.js → ImageComparisonComponent-CQDGJfUA.js} +1 -1
  3. package/dist/{_baseUniq-utU5_Vu-.js → _baseUniq-B2Nna6Kt.js} +1 -1
  4. package/dist/{any-language-editor-PrUUh2lr.js → any-language-editor-D-wq0tOG.js} +1 -1
  5. package/dist/{architectureDiagram-W76B3OCA-D-vOp0UU.js → architectureDiagram-W76B3OCA-C6tdnMBf.js} +4 -4
  6. package/dist/assets/{worker-BcG8m3h5.js → worker-B0C57BK8.js} +40 -38
  7. package/dist/{blockDiagram-QIGZ2CNN-IG-z8q8A.js → blockDiagram-QIGZ2CNN-IagL8LCN.js} +5 -5
  8. package/dist/{c4Diagram-FPNF74CW-5AEXIX3t.js → c4Diagram-FPNF74CW-D3_lIWUP.js} +2 -2
  9. package/dist/{channel-ECVsTGGL.js → channel-DCJI_DKk.js} +1 -1
  10. package/dist/{chunk-4BX2VUAB-DfJcd9e-.js → chunk-4BX2VUAB-B2DrODwN.js} +1 -1
  11. package/dist/{chunk-55IACEB6-BwT8MejR.js → chunk-55IACEB6-BUWDsQ-t.js} +1 -1
  12. package/dist/{chunk-FMBD7UC4-DW7uxNR6.js → chunk-FMBD7UC4-BExPNFv1.js} +1 -1
  13. package/dist/{chunk-K7UQS3LO-BGn2ZPDQ.js → chunk-K7UQS3LO-Cixi-Yko.js} +4 -4
  14. package/dist/{chunk-QN33PNHL-BcIbOumv.js → chunk-QN33PNHL-B83MtvER.js} +1 -1
  15. package/dist/{chunk-QZHKN3VN-CMSnhk6x.js → chunk-QZHKN3VN-CXvbu85X.js} +1 -1
  16. package/dist/{chunk-TVAH2DTR-CZF2JRya.js → chunk-TVAH2DTR-CpiumCHg.js} +3 -3
  17. package/dist/{chunk-TZMSLE5B-BHzN_BY6.js → chunk-TZMSLE5B-DIzaZjcI.js} +1 -1
  18. package/dist/{classDiagram-v2-RKCZMP56-2H7MseyB.js → classDiagram-KNZD7YFC-DyN5HPdk.js} +2 -2
  19. package/dist/{classDiagram-KNZD7YFC-2H7MseyB.js → classDiagram-v2-RKCZMP56-DyN5HPdk.js} +2 -2
  20. package/dist/{clone-DKQcSK7N.js → clone-DrJYap2i.js} +1 -1
  21. package/dist/{cose-bilkent-S5V4N54A-CgvKFxTr.js → cose-bilkent-S5V4N54A-D39b4WrQ.js} +2 -2
  22. package/dist/{dagre-5GWH7T2D-VNFIipzt.js → dagre-5GWH7T2D-BLjRxDpS.js} +6 -6
  23. package/dist/{data-grid-overlay-editor-XdqkKCVx.js → data-grid-overlay-editor-DTALqerV.js} +2 -2
  24. package/dist/{diagram-N5W7TBWH-D1s8h-eH.js → diagram-N5W7TBWH-MM8AIKGR.js} +5 -5
  25. package/dist/{diagram-QEK2KX5R-DOa-AstT.js → diagram-QEK2KX5R-BZGarWuJ.js} +3 -3
  26. package/dist/{diagram-S2PKOQOG-CFZ-Y2zi.js → diagram-S2PKOQOG-CnPinN9Q.js} +3 -3
  27. package/dist/{dockerfile-zE-2DWBS.js → dockerfile-U8DnCJ4X.js} +1 -1
  28. package/dist/{erDiagram-AWTI2OKA-WxUYJfbS.js → erDiagram-AWTI2OKA-CvDVbxOO.js} +4 -4
  29. package/dist/{flowDiagram-PVAE7QVJ-dDZH2O1W.js → flowDiagram-PVAE7QVJ-C2uuBTZS.js} +5 -5
  30. package/dist/{ganttDiagram-OWAHRB6G-D3CCqPQq.js → ganttDiagram-OWAHRB6G-BEff10RF.js} +4 -4
  31. package/dist/{gitGraphDiagram-NY62KEGX-BHFylEwc.js → gitGraphDiagram-NY62KEGX-wggu0kb2.js} +4 -4
  32. package/dist/{glide-data-editor-D0aJSGV_.js → glide-data-editor-Bqh5_dzJ.js} +3 -3
  33. package/dist/{graph-BPGEu6c8.js → graph-DKpp_wzf.js} +3 -3
  34. package/dist/{index-HtOEKQ3O.js → index-4XruEJkp.js} +1 -1
  35. package/dist/{index-eDB61tLS.js → index-DW0BCGJE.js} +1 -1
  36. package/dist/{index-DotQhzoN.js → index-DdfF_cLK.js} +1 -1
  37. package/dist/{index-Bx2b23rX.js → index-DzJ_YPCG.js} +3 -3
  38. package/dist/{infoDiagram-STP46IZ2-DWhhqGPi.js → infoDiagram-STP46IZ2-DF7KW-Op.js} +2 -2
  39. package/dist/{journeyDiagram-BIP6EPQ6-CU8FpryL.js → journeyDiagram-BIP6EPQ6-B_jmhmqd.js} +3 -3
  40. package/dist/{kanban-definition-6OIFK2YF-CWhF_a4g.js → kanban-definition-6OIFK2YF-B-M9FTyw.js} +2 -2
  41. package/dist/{layout-DGonEvAZ.js → layout-C4oVYZZD.js} +4 -4
  42. package/dist/{linear-Cww2a6nQ.js → linear-C-HCGr0T.js} +1 -1
  43. package/dist/{main-Bc0LY9fB.js → main-B9x2-9f2.js} +93798 -93495
  44. package/dist/main.js +1 -1
  45. package/dist/{mermaid-DpJuOhRr.js → mermaid-BE4cM3Qs.js} +30 -30
  46. package/dist/{min-CFQjsG4L.js → min-DTpHJ698.js} +2 -2
  47. package/dist/{mindmap-definition-Q6HEUPPD-K513Ef1t.js → mindmap-definition-Q6HEUPPD-Cpd-hO1E.js} +3 -3
  48. package/dist/{number-overlay-editor-DuSchUfE.js → number-overlay-editor-CvURA2Ud.js} +2 -2
  49. package/dist/{pieDiagram-ADFJNKIX-DAIIUJJO.js → pieDiagram-ADFJNKIX-D9f_f6fn.js} +3 -3
  50. package/dist/{quadrantDiagram-LMRXKWRM-yuf-j7Os.js → quadrantDiagram-LMRXKWRM-DgllE7xw.js} +2 -2
  51. package/dist/{react-plotly-B378DZ9U.js → react-plotly-BU-JRJSi.js} +1 -1
  52. package/dist/{requirementDiagram-4UW4RH46-BBWvEl6q.js → requirementDiagram-4UW4RH46-Dk_G8eUb.js} +3 -3
  53. package/dist/{sankeyDiagram-GR3RE2ED-B_TwV-dS.js → sankeyDiagram-GR3RE2ED-BhLIhDc1.js} +1 -1
  54. package/dist/{sequenceDiagram-C3RYC4MD-BVC6lltp.js → sequenceDiagram-C3RYC4MD-DHoZdMFJ.js} +3 -3
  55. package/dist/{slides-component-CPX3S0Y9.js → slides-component-DXAgdf7K.js} +2 -2
  56. package/dist/{stateDiagram-KXAO66HF-BCU1tYTD.js → stateDiagram-KXAO66HF-C1Ie-7Xf.js} +4 -4
  57. package/dist/{stateDiagram-v2-UMBNRL4Z-BdvN6wTu.js → stateDiagram-v2-UMBNRL4Z--CRuIHtM.js} +2 -2
  58. package/dist/style.css +1 -1
  59. package/dist/{time-CSIip6fV.js → time-yQjlGPwa.js} +2 -2
  60. package/dist/{timeline-definition-XQNQX7LJ-CCxCPNQI.js → timeline-definition-XQNQX7LJ-D_PjxB1B.js} +1 -1
  61. package/dist/{treemap-75Q7IDZK-Du6v0BzD.js → treemap-75Q7IDZK--NYqQjUZ.js} +134 -134
  62. package/dist/{vega-component-Da93sTnp.js → vega-component-CCUOMM5K.js} +2 -2
  63. package/dist/{xychartDiagram-6GGTOJPD-Oq6xaZKR.js → xychartDiagram-6GGTOJPD-WLKsEnzs.js} +2 -2
  64. package/package.json +10 -5
  65. package/src/__tests__/mocks.ts +43 -0
  66. package/src/components/app-config/user-config-form.tsx +78 -1
  67. package/src/components/chat/acp/__tests__/__snapshots__/prompt.test.ts.snap +116 -65
  68. package/src/components/chat/acp/__tests__/atoms.test.ts +1 -1
  69. package/src/components/chat/acp/__tests__/context-utils.test.ts +222 -0
  70. package/src/components/chat/acp/__tests__/prompt.test.ts +1 -1
  71. package/src/components/chat/acp/__tests__/state.test.ts +38 -42
  72. package/src/components/chat/acp/agent-docs.tsx +33 -6
  73. package/src/components/chat/acp/agent-panel.css +0 -18
  74. package/src/components/chat/acp/agent-panel.tsx +394 -72
  75. package/src/components/chat/acp/agent-selector.tsx +7 -1
  76. package/src/components/chat/acp/blocks.tsx +40 -10
  77. package/src/components/chat/acp/common.tsx +10 -2
  78. package/src/components/chat/acp/context-utils.ts +127 -0
  79. package/src/components/chat/acp/prompt.ts +96 -53
  80. package/src/components/chat/acp/state.ts +1 -1
  81. package/src/components/chat/acp/types.ts +8 -0
  82. package/src/components/chat/chat-panel.tsx +28 -89
  83. package/src/components/chat/chat-utils.ts +127 -1
  84. package/src/components/chat/markdown-renderer.css +39 -0
  85. package/src/components/chat/markdown-renderer.tsx +12 -47
  86. package/src/components/chat/tool-call-accordion.tsx +148 -26
  87. package/src/components/data-table/SearchBar.tsx +8 -7
  88. package/src/components/data-table/__tests__/column_formatting.test.ts +50 -35
  89. package/src/components/data-table/__tests__/data-table.test.tsx +39 -1
  90. package/src/components/data-table/cell-hover-template/feature.ts +14 -0
  91. package/src/components/data-table/cell-hover-template/types.ts +11 -0
  92. package/src/components/data-table/charts/components/form-fields.tsx +41 -37
  93. package/src/components/data-table/charts/forms/common-chart.tsx +2 -2
  94. package/src/components/data-table/column-explorer-panel/column-explorer.tsx +5 -2
  95. package/src/components/data-table/column-formatting/feature.ts +62 -29
  96. package/src/components/data-table/column-formatting/types.ts +1 -0
  97. package/src/components/data-table/column-header.tsx +3 -1
  98. package/src/components/data-table/column-summary/chart-spec-model.tsx +24 -7
  99. package/src/components/data-table/column-summary/column-summary.tsx +18 -9
  100. package/src/components/data-table/columns.tsx +42 -18
  101. package/src/components/data-table/data-table.tsx +10 -2
  102. package/src/components/data-table/date-popover.tsx +85 -75
  103. package/src/components/data-table/filter-pills.tsx +14 -9
  104. package/src/components/data-table/header-items.tsx +5 -1
  105. package/src/components/data-table/pagination.tsx +20 -13
  106. package/src/components/data-table/renderers.tsx +28 -0
  107. package/src/components/data-table/row-viewer-panel/row-viewer.tsx +10 -8
  108. package/src/components/datasources/column-preview.tsx +6 -2
  109. package/src/components/datasources/datasources.tsx +8 -12
  110. package/src/components/editor/Cell.tsx +6 -0
  111. package/src/components/editor/actions/name-cell-input.tsx +6 -1
  112. package/src/components/editor/actions/useCellActionButton.tsx +3 -1
  113. package/src/components/editor/ai/__tests__/completion-utils.test.ts +178 -1
  114. package/src/components/editor/ai/add-cell-with-ai.tsx +68 -66
  115. package/src/components/editor/ai/ai-completion-editor.tsx +29 -26
  116. package/src/components/editor/ai/completion-handlers.tsx +44 -6
  117. package/src/components/editor/ai/completion-utils.ts +92 -0
  118. package/src/components/editor/ai/transport/chat-transport.tsx +39 -0
  119. package/src/components/editor/cell/CellStatus.tsx +23 -20
  120. package/src/components/editor/cell/CreateCellButton.tsx +3 -4
  121. package/src/components/editor/cell/StagedAICell.tsx +51 -0
  122. package/src/components/editor/cell/cell-actions.tsx +2 -1
  123. package/src/components/editor/cell/code/language-toggle.tsx +3 -4
  124. package/src/components/editor/chrome/wrapper/footer-items/machine-stats.tsx +39 -28
  125. package/src/components/editor/controls/notebook-menu-dropdown.tsx +4 -2
  126. package/src/components/editor/file-tree/requesting-tree.tsx +14 -8
  127. package/src/components/editor/renderers/CellArray.tsx +3 -4
  128. package/src/components/editor/renderers/slides-layout/slides-layout.tsx +3 -3
  129. package/src/components/editor/renderers/slides-layout/types.ts +1 -0
  130. package/src/components/pages/home-page.tsx +4 -1
  131. package/src/components/slides/slides-component.tsx +1 -1
  132. package/src/components/slides/slides.css +6 -0
  133. package/src/components/terminal/__tests__/state.test.ts +207 -0
  134. package/src/components/terminal/hooks.ts +41 -0
  135. package/src/components/terminal/state.ts +75 -0
  136. package/src/components/terminal/terminal.tsx +334 -13
  137. package/src/components/terminal/theme.tsx +57 -0
  138. package/src/components/tracing/tracing-spec.ts +5 -4
  139. package/src/components/ui/range-slider.tsx +4 -2
  140. package/src/components/ui/slider.tsx +3 -1
  141. package/src/components/variables/variables-table.tsx +3 -0
  142. package/src/core/MarimoApp.tsx +9 -6
  143. package/src/core/ai/__tests__/staged-cells.test.ts +356 -0
  144. package/src/core/ai/context/__tests__/registry.test.ts +6 -4
  145. package/src/core/ai/context/providers/cell-output.ts +3 -2
  146. package/src/core/ai/context/providers/error.ts +3 -1
  147. package/src/core/ai/context/providers/file.ts +7 -2
  148. package/src/core/ai/context/providers/tables.ts +3 -2
  149. package/src/core/ai/context/providers/variable.ts +6 -4
  150. package/src/core/ai/staged-cells.ts +241 -0
  151. package/src/core/cells/__tests__/add-missing-import.test.ts +67 -22
  152. package/src/core/cells/add-missing-import.ts +24 -7
  153. package/src/core/cells/cells.ts +27 -28
  154. package/src/core/cells/logs.ts +1 -1
  155. package/src/core/codemirror/find-replace/search-highlight.ts +3 -1
  156. package/src/core/codemirror/language/LanguageAdapters.ts +9 -3
  157. package/src/core/codemirror/lsp/federated-lsp.ts +1 -1
  158. package/src/core/codemirror/lsp/notebook-lsp.ts +8 -2
  159. package/src/core/codemirror/readonly/__tests__/extension.test.ts +1 -1
  160. package/src/core/codemirror/rtc/loro/awareness.ts +52 -17
  161. package/src/core/codemirror/rtc/loro/sync.ts +12 -4
  162. package/src/core/config/config-schema.ts +1 -0
  163. package/src/core/config/config.ts +4 -0
  164. package/src/core/hotkeys/hotkeys.ts +8 -4
  165. package/src/core/i18n/__tests__/locale-provider.test.tsx +176 -0
  166. package/src/core/i18n/locale-provider.tsx +35 -0
  167. package/src/core/i18n/with-locale.tsx +12 -0
  168. package/src/core/islands/components/web-components.tsx +13 -10
  169. package/src/core/islands/main.ts +2 -2
  170. package/src/core/kernel/RuntimeState.ts +4 -1
  171. package/src/core/kernel/messages.ts +8 -12
  172. package/src/core/network/DeferredRequestRegistry.ts +16 -4
  173. package/src/core/runtime/runtime.ts +5 -4
  174. package/src/core/saving/__tests__/filename.test.ts +37 -0
  175. package/src/core/static/__tests__/download-html.test.ts +43 -1
  176. package/src/core/wasm/bridge.ts +5 -1
  177. package/src/core/wasm/store.ts +4 -1
  178. package/src/core/wasm/worker/message-buffer.ts +3 -2
  179. package/src/core/websocket/types.ts +22 -16
  180. package/src/core/websocket/useMarimoWebSocket.tsx +2 -2
  181. package/src/css/app/Cell.css +11 -0
  182. package/src/hooks/useFormatting.ts +97 -0
  183. package/src/hooks/useTimer.ts +8 -5
  184. package/src/plugins/core/RenderHTML.tsx +36 -2
  185. package/src/plugins/core/__test__/RenderHTML.test.ts +72 -0
  186. package/src/plugins/core/registerReactComponent.tsx +44 -10
  187. package/src/plugins/impl/DataTablePlugin.tsx +4 -0
  188. package/src/plugins/impl/FileBrowserPlugin.tsx +8 -2
  189. package/src/plugins/impl/RangeSliderPlugin.tsx +5 -3
  190. package/src/plugins/impl/SliderPlugin.tsx +3 -1
  191. package/src/plugins/impl/anywidget/model.ts +16 -5
  192. package/src/plugins/impl/data-editor/types.ts +7 -5
  193. package/src/plugins/impl/data-explorer/components/column-summary.tsx +20 -13
  194. package/src/plugins/impl/panel/utils.ts +6 -4
  195. package/src/plugins/layout/OutlinePlugin.tsx +69 -0
  196. package/src/plugins/layout/StatPlugin.tsx +4 -1
  197. package/src/plugins/plugins.ts +2 -0
  198. package/src/stories/cell.stories.tsx +1 -1
  199. package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
  200. package/src/utils/__tests__/cell-urls.test.ts +29 -0
  201. package/src/utils/__tests__/dates.test.ts +45 -24
  202. package/src/utils/__tests__/filenames.test.ts +18 -0
  203. package/src/utils/__tests__/numbers.test.ts +42 -30
  204. package/src/utils/__tests__/once.test.ts +187 -0
  205. package/src/utils/__tests__/path.test.ts +38 -0
  206. package/src/utils/__tests__/urls.test.ts +56 -1
  207. package/src/utils/dates.ts +15 -10
  208. package/src/utils/edit-distance.ts +8 -6
  209. package/src/utils/errors.ts +9 -0
  210. package/src/utils/id-tree.tsx +21 -10
  211. package/src/utils/localStorage.ts +13 -4
  212. package/src/utils/numbers.ts +11 -11
  213. package/src/utils/once.ts +32 -0
  214. package/src/utils/paths.ts +4 -1
  215. package/src/utils/pluralize.ts +12 -5
  216. package/src/utils/python-poet/poet.ts +30 -15
  217. package/src/utils/time.ts +5 -1
@@ -1,18 +1,35 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
3
  import { createStore } from "jotai";
4
- import { describe, expect, it, vi } from "vitest";
4
+ import { beforeEach, describe, expect, it, vi } from "vitest";
5
+ import { MockNotebook } from "@/__mocks__/notebook";
6
+ import { MockRequestClient } from "@/__mocks__/requests";
7
+ import { store } from "@/core/state/jotai";
5
8
  import { variablesAtom } from "@/core/variables/state";
6
9
  import type { Variables } from "@/core/variables/types";
7
- import { CollapsibleTree, MultiColumn } from "@/utils/id-tree";
8
- import { maybeAddMissingImport } from "../add-missing-import";
9
- import { type NotebookState, notebookAtom } from "../cells";
10
+ import {
11
+ maybeAddAltairImport,
12
+ maybeAddMarimoImport,
13
+ maybeAddMissingImport,
14
+ } from "../add-missing-import";
15
+ import { notebookAtom } from "../cells";
10
16
  import type { CellId } from "../ids";
11
- import type { CellData } from "../types";
12
17
 
13
- const CELL_IDS = new MultiColumn([CollapsibleTree.from(["1" as CellId])]);
18
+ // Mock the getRequestClient function
19
+ const mockRequestClient = MockRequestClient.create();
20
+ vi.mock("@/core/network/requests", () => ({
21
+ getRequestClient: () => mockRequestClient,
22
+ }));
23
+
24
+ const Cell1 = "1" as CellId;
25
+ const Cell2 = "2" as CellId;
14
26
 
15
27
  describe("maybeAddMissingImport", () => {
28
+ beforeEach(() => {
29
+ store.set(variablesAtom, {} as Variables);
30
+ store.set(notebookAtom, MockNotebook.notebookState({ cellData: {} }));
31
+ });
32
+
16
33
  it("should not add an import if the variable is already in the variables state", () => {
17
34
  const appStore = createStore();
18
35
  appStore.set(variablesAtom, { mo: {} } as Variables);
@@ -36,14 +53,14 @@ describe("maybeAddMissingImport", () => {
36
53
  (code) => {
37
54
  const appStore = createStore();
38
55
  appStore.set(variablesAtom, {} as Variables);
39
- appStore.set(notebookAtom, {
40
- cellData: {
41
- ["1" as CellId]: {
42
- code: code,
43
- } as CellData,
44
- },
45
- cellIds: CELL_IDS,
46
- } as NotebookState);
56
+ appStore.set(
57
+ notebookAtom,
58
+ MockNotebook.notebookState({
59
+ cellData: {
60
+ [Cell1]: { code: code },
61
+ },
62
+ }),
63
+ );
47
64
  const onAddImport = vi.fn();
48
65
  maybeAddMissingImport({
49
66
  moduleName: "marimo",
@@ -58,14 +75,14 @@ describe("maybeAddMissingImport", () => {
58
75
  it("should add an import if the variable is not in the variables state and the import statement does not exist in the notebook", () => {
59
76
  const appStore = createStore();
60
77
  appStore.set(variablesAtom, {} as Variables);
61
- appStore.set(notebookAtom, {
62
- cellData: {
63
- ["1" as CellId]: {
64
- code: "mo.md('hello')",
65
- } as CellData,
66
- },
67
- cellIds: CELL_IDS,
68
- } as NotebookState);
78
+ appStore.set(
79
+ notebookAtom,
80
+ MockNotebook.notebookState({
81
+ cellData: {
82
+ [Cell1]: { code: "mo.md('hello')" },
83
+ },
84
+ }),
85
+ );
69
86
  const onAddImport = vi.fn();
70
87
  maybeAddMissingImport({
71
88
  moduleName: "marimo",
@@ -75,4 +92,32 @@ describe("maybeAddMissingImport", () => {
75
92
  });
76
93
  expect(onAddImport).toHaveBeenCalledWith("import marimo as mo");
77
94
  });
95
+
96
+ it("should not create a new cell if import already exists due to skipIfCodeExists", () => {
97
+ const addImports = [maybeAddMarimoImport, maybeAddAltairImport];
98
+ for (const addImport of addImports) {
99
+ store.set(variablesAtom, {} as Variables);
100
+ store.set(
101
+ notebookAtom,
102
+ MockNotebook.notebookState({
103
+ cellData: {
104
+ [Cell1]: { code: "import marimo as mo" },
105
+ [Cell2]: { code: "import altair as alt" },
106
+ },
107
+ }),
108
+ );
109
+
110
+ const createNewCell = vi.fn();
111
+ const result = addImport({
112
+ autoInstantiate: false,
113
+ createNewCell,
114
+ fromCellId: Cell1,
115
+ before: false,
116
+ });
117
+
118
+ // Should not create a new cell since the import already exists
119
+ expect(createNewCell).not.toHaveBeenCalled();
120
+ expect(result).toBeNull();
121
+ }
122
+ });
78
123
  });
@@ -47,27 +47,40 @@ export function maybeAddMissingImport({
47
47
  return true;
48
48
  }
49
49
 
50
+ /**
51
+ * Adds a marimo import to the notebook if not already present.
52
+ * @param autoInstantiate Whether to automatically run the cell.
53
+ * @param createNewCell The function to create a new cell.
54
+ * @param fromCellId The cell to add the import to.
55
+ * @param before Whether to add the import before or after the cell.
56
+ *
57
+ * Returns the ID of the new cell if added, otherwise null.
58
+ */
50
59
  export function maybeAddMarimoImport({
51
60
  autoInstantiate,
52
61
  createNewCell,
53
62
  fromCellId,
63
+ before,
54
64
  }: {
55
65
  autoInstantiate: boolean;
56
66
  createNewCell: CellActions["createNewCell"];
57
67
  fromCellId?: CellId | null;
58
- }): boolean {
68
+ before?: boolean;
69
+ }): CellId | null {
59
70
  const client = getRequestClient();
60
- return maybeAddMissingImport({
71
+ let newCellId: CellId | null = null;
72
+ const added = maybeAddMissingImport({
61
73
  moduleName: "marimo",
62
74
  variableName: "mo",
63
75
  onAddImport: (importStatement) => {
64
- const newCellId = CellId.create();
76
+ newCellId = CellId.create();
65
77
  createNewCell({
66
78
  cellId: fromCellId ?? "__end__",
67
- before: false,
79
+ before: before ?? false,
68
80
  code: importStatement,
69
81
  lastCodeRun: autoInstantiate ? importStatement : undefined,
70
82
  newCellId: newCellId,
83
+ skipIfCodeExists: true,
71
84
  autoFocus: false,
72
85
  });
73
86
  if (autoInstantiate) {
@@ -78,6 +91,7 @@ export function maybeAddMarimoImport({
78
91
  }
79
92
  },
80
93
  });
94
+ return added ? newCellId : null;
81
95
  }
82
96
 
83
97
  export function maybeAddAltairImport({
@@ -88,19 +102,21 @@ export function maybeAddAltairImport({
88
102
  autoInstantiate: boolean;
89
103
  createNewCell: CellActions["createNewCell"];
90
104
  fromCellId?: CellId | null;
91
- }): boolean {
105
+ }): CellId | null {
92
106
  const client = getRequestClient();
93
- return maybeAddMissingImport({
107
+ let newCellId: CellId | null = null;
108
+ const added = maybeAddMissingImport({
94
109
  moduleName: "altair",
95
110
  variableName: "alt",
96
111
  onAddImport: (importStatement) => {
97
- const newCellId = CellId.create();
112
+ newCellId = CellId.create();
98
113
  createNewCell({
99
114
  cellId: fromCellId ?? "__end__",
100
115
  before: false,
101
116
  code: importStatement,
102
117
  lastCodeRun: autoInstantiate ? importStatement : undefined,
103
118
  newCellId: newCellId,
119
+ skipIfCodeExists: true,
104
120
  autoFocus: false,
105
121
  });
106
122
  if (autoInstantiate) {
@@ -111,4 +127,5 @@ export function maybeAddAltairImport({
111
127
  }
112
128
  },
113
129
  });
130
+ return added ? newCellId : null;
114
131
  }
@@ -145,6 +145,31 @@ export function initialNotebookState(): NotebookState {
145
145
  });
146
146
  }
147
147
 
148
+ export interface CreateNewCellAction {
149
+ /** The target cell ID to create a new cell relative to. Can be:
150
+ * - A CellId string for an existing cell
151
+ * - "__end__" to append at the end of the first column
152
+ * - {type: "__end__", columnId} to append at the end of a specific column
153
+ */
154
+ cellId: CellId | "__end__" | { type: "__end__"; columnId: CellColumnId };
155
+ /** Whether to insert before (true) or after (false) the target cell */
156
+ before: boolean;
157
+ /** Initial code content for the new cell */
158
+ code?: string;
159
+ /** The last executed code for the new cell */
160
+ lastCodeRun?: string;
161
+ /** Timestamp of the last execution */
162
+ lastExecutionTime?: number;
163
+ /** Optional custom ID for the new cell. Auto-generated if not provided */
164
+ newCellId?: CellId;
165
+ /** Whether to focus the new cell after creation */
166
+ autoFocus?: boolean;
167
+ /** If true, skip creation if code already exists */
168
+ skipIfCodeExists?: boolean;
169
+ /** Hide the code in the new cell. This will be initially shown until the cell is blurred for the first time. */
170
+ hideCode?: boolean;
171
+ }
172
+
148
173
  /**
149
174
  * Actions and reducer for the notebook state.
150
175
  */
@@ -154,33 +179,7 @@ const {
154
179
  useActions,
155
180
  valueAtom: notebookAtom,
156
181
  } = createReducerAndAtoms(initialNotebookState, {
157
- createNewCell: (
158
- state,
159
- action: {
160
- /** The target cell ID to create a new cell relative to. Can be:
161
- * - A CellId string for an existing cell
162
- * - "__end__" to append at the end of the first column
163
- * - {type: "__end__", columnId} to append at the end of a specific column
164
- */
165
- cellId: CellId | "__end__" | { type: "__end__"; columnId: CellColumnId };
166
- /** Whether to insert before (true) or after (false) the target cell */
167
- before: boolean;
168
- /** Initial code content for the new cell */
169
- code?: string;
170
- /** The last executed code for the new cell */
171
- lastCodeRun?: string;
172
- /** Timestamp of the last execution */
173
- lastExecutionTime?: number;
174
- /** Optional custom ID for the new cell. Auto-generated if not provided */
175
- newCellId?: CellId;
176
- /** Whether to focus the new cell after creation */
177
- autoFocus?: boolean;
178
- /** If true, skip creation if code already exists */
179
- skipIfCodeExists?: boolean;
180
- /** Hide the code in the new cell. This will be initially shown until the cell is blurred for the first time. */
181
- hideCode?: boolean;
182
- },
183
- ) => {
182
+ createNewCell: (state, action: CreateNewCellAction) => {
184
183
  const {
185
184
  cellId,
186
185
  before,
@@ -1623,7 +1622,7 @@ const cellDataAtom = atomFamily((cellId: CellId) =>
1623
1622
  const cellRuntimeAtom = atomFamily((cellId: CellId) =>
1624
1623
  atom((get) => get(notebookAtom).cellRuntime[cellId]),
1625
1624
  );
1626
- const cellHandleAtom = atomFamily((cellId: CellId) =>
1625
+ export const cellHandleAtom = atomFamily((cellId: CellId) =>
1627
1626
  atom((get) => get(notebookAtom).cellHandles[cellId]),
1628
1627
  );
1629
1628
  /**
@@ -102,7 +102,7 @@ export function formatLogTimestamp(timestamp: number): string {
102
102
  try {
103
103
  // parse from UTC
104
104
  const date = fromUnixTime(timestamp);
105
- return date.toLocaleTimeString("en-US", {
105
+ return date.toLocaleTimeString(undefined, {
106
106
  hour12: true,
107
107
  hour: "numeric",
108
108
  minute: "numeric",
@@ -91,8 +91,10 @@ const HighlightMargin = 250;
91
91
  export const searchHighlighter = ViewPlugin.fromClass(
92
92
  class {
93
93
  decorations: DecorationSet;
94
+ readonly view: EditorView;
94
95
 
95
- constructor(readonly view: EditorView) {
96
+ constructor(view: EditorView) {
97
+ this.view = view;
96
98
  this.decorations = this.highlight(view.state.field(searchState));
97
99
  }
98
100
 
@@ -1,20 +1,26 @@
1
1
  /* Copyright 2024 Marimo. All rights reserved. */
2
2
 
3
+ import { once } from "@/utils/once";
3
4
  import { MarkdownLanguageAdapter } from "./languages/markdown";
4
5
  import { PythonLanguageAdapter } from "./languages/python";
5
6
  import { SQLLanguageAdapter } from "./languages/sql/sql";
6
7
  import type { LanguageAdapter, LanguageAdapterType } from "./types";
7
8
 
9
+ // Create cached instances
10
+ const createPythonAdapter = once(() => new PythonLanguageAdapter());
11
+ const createMarkdownAdapter = once(() => new MarkdownLanguageAdapter());
12
+ const createSqlAdapter = once(() => new SQLLanguageAdapter());
13
+
8
14
  export const LanguageAdapters: Record<LanguageAdapterType, LanguageAdapter> = {
9
15
  // Getters to prevent circular dependencies
10
16
  get python() {
11
- return new PythonLanguageAdapter();
17
+ return createPythonAdapter();
12
18
  },
13
19
  get markdown() {
14
- return new MarkdownLanguageAdapter();
20
+ return createMarkdownAdapter();
15
21
  },
16
22
  get sql() {
17
- return new SQLLanguageAdapter();
23
+ return createSqlAdapter();
18
24
  },
19
25
  };
20
26
 
@@ -4,7 +4,7 @@ import type { LanguageServerPlugin } from "@marimo-team/codemirror-languageserve
4
4
  import type * as LSP from "vscode-languageserver-protocol";
5
5
  import { Objects } from "@/utils/objects";
6
6
  import type { ILanguageServerClient } from "./types";
7
- import { getLSPDocument } from "./utils.ts";
7
+ import { getLSPDocument } from "./utils";
8
8
 
9
9
  function removeFalseyValues<T extends object>(obj: T): T {
10
10
  return Objects.filter(obj, (value) => value !== false && value !== null) as T;
@@ -18,13 +18,19 @@ import { getLSPDocument } from "./utils";
18
18
 
19
19
  class Snapshotter {
20
20
  private documentVersion = 0;
21
+ private readonly getNotebookCode: () => {
22
+ cellIds: CellId[];
23
+ codes: Record<CellId, string>;
24
+ };
21
25
 
22
26
  constructor(
23
- private readonly getNotebookCode: () => {
27
+ getNotebookCode: () => {
24
28
  cellIds: CellId[];
25
29
  codes: Record<CellId, string>;
26
30
  },
27
- ) {}
31
+ ) {
32
+ this.getNotebookCode = getNotebookCode;
33
+ }
28
34
 
29
35
  /**
30
36
  * Map from the global document version to the cell id and version.
@@ -9,7 +9,7 @@ import { connectionAtom } from "../../../network/connection";
9
9
  import { dynamicReadonly, isEditorReadonly } from "../extension";
10
10
 
11
11
  function makeStoreWithConnection(
12
- state: WebSocketState.CONNECTING | WebSocketState.OPEN,
12
+ state: typeof WebSocketState.CONNECTING | typeof WebSocketState.OPEN,
13
13
  ) {
14
14
  const store = createStore();
15
15
  store.set(connectionAtom, { state });
@@ -202,13 +202,25 @@ export const createSelectionLayer = (): Extension =>
202
202
  * Renders a blinking cursor to indicate the cursor of another user.
203
203
  */
204
204
  export class RemoteCursorMarker implements LayerMarker {
205
+ private left: number;
206
+ private top: number;
207
+ private height: number;
208
+ private name: string;
209
+ private colorClassName: string;
210
+
205
211
  constructor(
206
- private left: number,
207
- private top: number,
208
- private height: number,
209
- private name: string,
210
- private colorClassName: string,
211
- ) {}
212
+ left: number,
213
+ top: number,
214
+ height: number,
215
+ name: string,
216
+ colorClassName: string,
217
+ ) {
218
+ this.left = left;
219
+ this.top = top;
220
+ this.height = height;
221
+ this.name = name;
222
+ this.colorClassName = colorClassName;
223
+ }
212
224
 
213
225
  draw(): HTMLElement {
214
226
  const elt = document.createElement("div");
@@ -353,16 +365,30 @@ export interface CursorPosition {
353
365
 
354
366
  export class AwarenessPlugin implements PluginValue {
355
367
  sub: Subscription;
368
+ public view: EditorView;
369
+ public doc: LoroDoc;
370
+ public user: UserState;
371
+ public awareness: Awareness<AwarenessState>;
372
+ private getTextFromDoc: (doc: LoroDoc) => LoroText;
373
+ private scopeId: ScopeId;
374
+ private getUserId?: () => Uid;
356
375
 
357
376
  constructor(
358
- public view: EditorView,
359
- public doc: LoroDoc,
360
- public user: UserState,
361
- public awareness: Awareness<AwarenessState>,
362
- private getTextFromDoc: (doc: LoroDoc) => LoroText,
363
- private scopeId: ScopeId,
364
- private getUserId?: () => Uid,
377
+ view: EditorView,
378
+ doc: LoroDoc,
379
+ user: UserState,
380
+ awareness: Awareness<AwarenessState>,
381
+ getTextFromDoc: (doc: LoroDoc) => LoroText,
382
+ scopeId: ScopeId,
383
+ getUserId?: () => Uid,
365
384
  ) {
385
+ this.view = view;
386
+ this.doc = doc;
387
+ this.user = user;
388
+ this.awareness = awareness;
389
+ this.getTextFromDoc = getTextFromDoc;
390
+ this.scopeId = scopeId;
391
+ this.getUserId = getUserId;
366
392
  this.sub = this.doc.subscribe((e) => {
367
393
  if (e.by === "local") {
368
394
  // update remote cursor position
@@ -435,12 +461,21 @@ export class AwarenessPlugin implements PluginValue {
435
461
  }
436
462
  export class RemoteAwarenessPlugin implements PluginValue {
437
463
  _awarenessListener?: AwarenessListener;
464
+ public view: EditorView;
465
+ public doc: LoroDoc;
466
+ public awareness: Awareness<AwarenessState>;
467
+ private scopeId: ScopeId;
468
+
438
469
  constructor(
439
- public view: EditorView,
440
- public doc: LoroDoc,
441
- public awareness: Awareness<AwarenessState>,
442
- private scopeId: ScopeId,
470
+ view: EditorView,
471
+ doc: LoroDoc,
472
+ awareness: Awareness<AwarenessState>,
473
+ scopeId: ScopeId,
443
474
  ) {
475
+ this.view = view;
476
+ this.doc = doc;
477
+ this.awareness = awareness;
478
+ this.scopeId = scopeId;
444
479
  const listener: AwarenessListener = async (arg, origin) => {
445
480
  if (origin === "local") {
446
481
  return;
@@ -40,13 +40,21 @@ export const loroSyncAnnotation = Annotation.define();
40
40
  export class LoroSyncPluginValue implements PluginValue {
41
41
  sub?: Subscription;
42
42
  private isInitDispatch = false;
43
+ private view: EditorView;
44
+ private doc: LoroDoc;
45
+ private docPath: string[];
46
+ private getTextFromDoc: (doc: LoroDoc) => LoroText;
43
47
 
44
48
  constructor(
45
- private view: EditorView,
46
- private doc: LoroDoc,
47
- private docPath: string[],
48
- private getTextFromDoc: (doc: LoroDoc) => LoroText,
49
+ view: EditorView,
50
+ doc: LoroDoc,
51
+ docPath: string[],
52
+ getTextFromDoc: (doc: LoroDoc) => LoroText,
49
53
  ) {
54
+ this.view = view;
55
+ this.doc = doc;
56
+ this.docPath = docPath;
57
+ this.getTextFromDoc = getTextFromDoc;
50
58
  this.sub = doc.subscribe(this.onRemoteUpdate);
51
59
  Promise.resolve().then(() => {
52
60
  this.isInitDispatch = true;
@@ -144,6 +144,7 @@ export const UserConfigSchema = z
144
144
  }
145
145
  return width;
146
146
  }),
147
+ locale: z.string().nullable().optional(),
147
148
  reference_highlighting: z.boolean().default(false),
148
149
  })
149
150
  .passthrough()
@@ -82,6 +82,10 @@ export const editorFontSizeAtom = atom<number>((get) => {
82
82
  return get(resolvedMarimoConfigAtom).display.code_editor_font_size;
83
83
  });
84
84
 
85
+ export const localeAtom = atom<string | null | undefined>((get) => {
86
+ return get(resolvedMarimoConfigAtom).display.locale;
87
+ });
88
+
85
89
  export function isAiEnabled(config: UserConfig) {
86
90
  return (
87
91
  Boolean(config.ai?.models?.chat_model) ||
@@ -452,10 +452,13 @@ export class HotkeyProvider implements IHotkeyProvider {
452
452
  return new HotkeyProvider(DEFAULT_HOT_KEY, { platform });
453
453
  }
454
454
 
455
+ private hotkeys: Record<HotkeyAction, Hotkey>;
456
+
455
457
  constructor(
456
- private hotkeys: Record<HotkeyAction, Hotkey>,
458
+ hotkeys: Record<HotkeyAction, Hotkey>,
457
459
  options: HotkeyProviderOptions = {},
458
460
  ) {
461
+ this.hotkeys = hotkeys;
459
462
  this.platform = options.platform ?? resolvePlatform();
460
463
  this.mod = this.platform === "mac" ? "Cmd" : "Ctrl";
461
464
  }
@@ -503,13 +506,14 @@ export class HotkeyProvider implements IHotkeyProvider {
503
506
  }
504
507
 
505
508
  export class OverridingHotkeyProvider extends HotkeyProvider {
509
+ private readonly overrides: Partial<Record<HotkeyAction, string | undefined>>;
510
+
506
511
  constructor(
507
- private readonly overrides: Partial<
508
- Record<HotkeyAction, string | undefined>
509
- >,
512
+ overrides: Partial<Record<HotkeyAction, string | undefined>>,
510
513
  options: HotkeyProviderOptions = {},
511
514
  ) {
512
515
  super(DEFAULT_HOT_KEY, options);
516
+ this.overrides = overrides;
513
517
  }
514
518
 
515
519
  override getHotkey(action: HotkeyAction): ResolvedHotkey {