@marimo-team/frontend 0.19.3-dev3 → 0.19.3-dev32

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 (150) hide show
  1. package/dist/assets/{CellStatus-CJVmn-JU.js → CellStatus-CGsC_xni.js} +1 -1
  2. package/dist/assets/{ConnectedDataExplorerComponent-KlUs_Sz3.js → ConnectedDataExplorerComponent-Cr6-n9Em.js} +1 -1
  3. package/dist/assets/{ErrorBoundary-Drf1manw.js → ErrorBoundary-C7JBxSzd.js} +1 -1
  4. package/dist/assets/{ImperativeModal-q6QlC2aZ.js → ImperativeModal-DVhvP4lH.js} +1 -1
  5. package/dist/assets/{JsonOutput-BLd1jTNA.js → JsonOutput-CCSLhScI.js} +1 -1
  6. package/dist/assets/{LazyAnyLanguageCodeMirror-jpEDlD0M.js → LazyAnyLanguageCodeMirror-Cp2punaU.js} +2 -2
  7. package/dist/assets/{MarimoErrorOutput-Orp0blpZ.js → MarimoErrorOutput-CDtUiLzW.js} +1 -1
  8. package/dist/assets/{RenderHTML-BzGWfPTJ.js → RenderHTML-B1PnGgGU.js} +1 -1
  9. package/dist/assets/VisuallyHidden-B9t3FhTP.js +1 -0
  10. package/dist/assets/{add-cell-with-ai-Dx5UA23s.js → add-cell-with-ai-DG5ez5tQ.js} +1 -1
  11. package/dist/assets/{add-database-form-MNQmbAaT.js → add-database-form-BDC4sw5d.js} +1 -1
  12. package/dist/assets/{agent-panel-BS2q53WX.js → agent-panel-DvdoiTMc.js} +15 -15
  13. package/dist/assets/{ai-model-dropdown-DZ7evAGU.js → ai-model-dropdown-DSzuccWn.js} +1 -1
  14. package/dist/assets/{alert-dialog-k5KxevGr.js → alert-dialog-jcHA5geR.js} +1 -1
  15. package/dist/assets/{any-language-editor-DQu1Tt2N.js → any-language-editor-Cm83E7D_.js} +1 -1
  16. package/dist/assets/{app-config-button-CsABtw-A.js → app-config-button-DrOOXwCm.js} +1 -1
  17. package/dist/assets/button-B8cGZzP5.js +1 -0
  18. package/dist/assets/{cache-panel-C1So4Zu3.js → cache-panel-1FqnpB9y.js} +1 -1
  19. package/dist/assets/{cell-editor-D4tKLHiP.js → cell-editor-Bowbis7w.js} +13 -13
  20. package/dist/assets/{cell-link-BoUpLV2S.js → cell-link-DXYH8FLx.js} +1 -1
  21. package/dist/assets/{cells-D_SkyFDn.js → cells-4S-e0VcC.js} +46 -46
  22. package/dist/assets/{chat-components-6dWNbFjV.js → chat-components-Bs4IxADW.js} +1 -1
  23. package/dist/assets/{chat-display-Cfg38gMM.js → chat-display-CPq1j1yR.js} +1 -1
  24. package/dist/assets/{chat-panel-CofEklYU.js → chat-panel-dvZilXEF.js} +1 -1
  25. package/dist/assets/{client-BUus2uot.js → client-DdSTQs8P.js} +1 -1
  26. package/dist/assets/{column-preview-9nPwQyJZ.js → column-preview-DtHgotrc.js} +1 -1
  27. package/dist/assets/{command-Qt3ng7cb.js → command-NXYtUgbp.js} +1 -1
  28. package/dist/assets/{command-palette-ptrGtoIl.js → command-palette-85dl4RPg.js} +1 -1
  29. package/dist/assets/{common-DE3UmpZO.js → common-DTmoX7TD.js} +1 -1
  30. package/dist/assets/config-Ba3eeYri.js +1 -0
  31. package/dist/assets/context-DHfVoQfl.js +1 -0
  32. package/dist/assets/{copy-icon-B69c-352.js → copy-icon-jWsqdLn1.js} +1 -1
  33. package/dist/assets/{datasource-CR_zEq5o.js → datasource-CPVUAqzI.js} +1 -1
  34. package/dist/assets/{dependency-graph-panel-DQnoPpen.js → dependency-graph-panel-VYsYCQNm.js} +1 -1
  35. package/dist/assets/{dialog-DUEuLcT2.js → dialog-CF5DtF1E.js} +1 -1
  36. package/dist/assets/{dist-DOFFh6Ii.js → dist-Dg7UO_Vw.js} +1 -1
  37. package/dist/assets/{documentation-panel-C0769uWv.js → documentation-panel-CwAtyJSz.js} +1 -1
  38. package/dist/assets/{download-CITws1-y.js → download-BcXr1SQk.js} +1 -1
  39. package/dist/assets/edit-page-CCa1yDjZ.js +12 -0
  40. package/dist/assets/{error-banner-DU5Qb8a8.js → error-banner-DvT0IGDZ.js} +1 -1
  41. package/dist/assets/{error-panel-B-EvbVVc.js → error-panel-BbbZ1gDy.js} +1 -1
  42. package/dist/assets/{es-CEEVxHsX.js → es-B3NV9-gC.js} +1 -1
  43. package/dist/assets/{field-DDKGFzpC.js → field-Clr_fqUr.js} +1 -1
  44. package/dist/assets/{file-explorer-panel-BqkMxs-d.js → file-explorer-panel-JB9Lb6XZ.js} +1 -1
  45. package/dist/assets/{floating-outline-p0aHdM2W.js → floating-outline-BDIr9Bbt.js} +1 -1
  46. package/dist/assets/{focus-D18AHojc.js → focus-DAgLzXHM.js} +1 -1
  47. package/dist/assets/{form-DEraWoX5.js → form-DVkjQxq_.js} +1 -1
  48. package/dist/assets/{glide-data-editor-D_bRnWfy.js → glide-data-editor-Dv8ZW9dk.js} +1 -1
  49. package/dist/assets/{globals-D3SeIm1j.js → globals-BJD8iIVw.js} +1 -1
  50. package/dist/assets/{home-page-9rDRaLCP.js → home-page-5ew0gT8b.js} +1 -1
  51. package/dist/assets/house-CncUa_LL.js +1 -0
  52. package/dist/assets/index-Y7E_QQxO.js +43 -0
  53. package/dist/assets/index-__6MNWbe.css +2 -0
  54. package/dist/assets/input-B80Yt1uu.js +1 -0
  55. package/dist/assets/{kiosk-mode-DL9UBacr.js → kiosk-mode-Cev1VOAm.js} +1 -1
  56. package/dist/assets/{label-qwandMoh.js → label-CNZLffHW.js} +1 -1
  57. package/dist/assets/{layout-Bd6wzfjT.js → layout-CurIookF.js} +4 -4
  58. package/dist/assets/links-Bpd4gqTj.js +1 -0
  59. package/dist/assets/{logs-panel-C7VsvCHz.js → logs-panel-CLsE2AN1.js} +1 -1
  60. package/dist/assets/{markdown-renderer-C_xfLXiO.js → markdown-renderer-Cc0PiY-K.js} +1 -1
  61. package/dist/assets/{mode-BFwdGSZ7.js → mode-B3tNMXH2.js} +1 -1
  62. package/dist/assets/{multi-map-fjX9ImVF.js → multi-map-CQd4MZr5.js} +1 -1
  63. package/dist/assets/name-cell-input-lkGsSC32.js +1 -0
  64. package/dist/assets/{outline-panel-BlhMaZpZ.js → outline-panel-CR_3Njth.js} +1 -1
  65. package/dist/assets/{packages-panel-YNB3ay32.js → packages-panel-D2JfrhoE.js} +1 -1
  66. package/dist/assets/panels-CpuHPy5a.js +1 -0
  67. package/dist/assets/{process-output-4w8xDO0j.js → process-output-rKnBuRRW.js} +1 -1
  68. package/dist/assets/{readonly-python-code-DjP4vjBQ.js → readonly-python-code-D7ixCdRP.js} +1 -1
  69. package/dist/assets/{run-page-DRgXZvWr.js → run-page-UbtG3woR.js} +1 -1
  70. package/dist/assets/{scratchpad-panel-BYCWZWFJ.js → scratchpad-panel-Bvg9Sbf8.js} +1 -1
  71. package/dist/assets/{secrets-panel-CDWmmmBS.js → secrets-panel-BMY6PPth.js} +1 -1
  72. package/dist/assets/{select-D0g5GnIs.js → select-D9lTzMzP.js} +1 -1
  73. package/dist/assets/{session-panel-C0b1PCJc.js → session-panel-CE7qRaFP.js} +1 -1
  74. package/dist/assets/{slides-component-MkPkpql1.js → slides-component-Dp0Yv5b0.js} +1 -1
  75. package/dist/assets/{snippets-panel-CpjS6w9M.js → snippets-panel-CiNTJKw4.js} +1 -1
  76. package/dist/assets/state-CZqgb3sC.js +1 -0
  77. package/dist/assets/{state-kFdt1US2.js → state-lX2Tur1r.js} +1 -1
  78. package/dist/assets/{switch-C9iBa4rX.js → switch-utDdERLs.js} +1 -1
  79. package/dist/assets/{terminal-BvgBa6Ri.js → terminal-BWM0fOMh.js} +1 -1
  80. package/dist/assets/{textarea-D2hGxqGj.js → textarea-CuGxnSJT.js} +1 -1
  81. package/dist/assets/{tracing-CKdq5KMT.js → tracing-CfhQiEJB.js} +1 -1
  82. package/dist/assets/{tracing-panel-DXjACxb_.js → tracing-panel-BES8ykC_.js} +2 -2
  83. package/dist/assets/{types-VzFAm7Cv.js → types-BqIUl7zM.js} +1 -1
  84. package/dist/assets/{useAddCell-CVfRv5mq.js → useAddCell-B5Yugh3r.js} +1 -1
  85. package/dist/assets/useBoolean-DI-eW-xX.js +1 -0
  86. package/dist/assets/{useCellActionButton-Woqd9LSB.js → useCellActionButton-BNKqQ1-h.js} +1 -1
  87. package/dist/assets/{useDateFormatter-CV0QXb5P.js → useDateFormatter-DsANziQR.js} +1 -1
  88. package/dist/assets/{useDeleteCell-GHwc6J4l.js → useDeleteCell-ChzEFH8j.js} +1 -1
  89. package/dist/assets/{useDependencyPanelTab-B0ZsFCYF.js → useDependencyPanelTab-MCxvkAot.js} +1 -1
  90. package/dist/assets/useNotebookActions-BRPD06EX.js +1 -0
  91. package/dist/assets/{useNumberFormatter-D8ks3oPN.js → useNumberFormatter-FoXhpyAb.js} +1 -1
  92. package/dist/assets/usePress-DTwIUo40.js +7 -0
  93. package/dist/assets/{useRunCells-BZ_OFvV4.js → useRunCells-Dgv3VAFX.js} +1 -1
  94. package/dist/assets/{useSplitCell-BnJiHHep.js → useSplitCell-_rfoF197.js} +1 -1
  95. package/dist/assets/utilities.esm-C0AZu44u.js +3 -0
  96. package/dist/assets/{vega-component-DpAAiTdH.js → vega-component-BoLFf7jF.js} +1 -1
  97. package/dist/assets/{write-secret-modal-CLm48gMe.js → write-secret-modal-hOetwavI.js} +1 -1
  98. package/dist/index.html +54 -54
  99. package/package.json +5 -5
  100. package/src/__tests__/mount.test.ts +128 -0
  101. package/src/components/app-config/__tests__/get-dirty-values.test.ts +1 -1
  102. package/src/components/app-config/user-config-form.tsx +1 -1
  103. package/src/components/chat/acp/agent-panel.tsx +56 -43
  104. package/src/components/editor/KernelStartupErrorModal.tsx +101 -0
  105. package/src/components/editor/actions/name-cell-input.tsx +10 -4
  106. package/src/components/editor/chrome/types.ts +2 -4
  107. package/src/components/editor/chrome/wrapper/app-chrome.tsx +55 -58
  108. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +150 -96
  109. package/src/components/editor/notebook-cell.tsx +13 -13
  110. package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +27 -0
  111. package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +6 -0
  112. package/src/components/utils/lazy-mount.tsx +29 -8
  113. package/src/core/MarimoApp.tsx +2 -0
  114. package/src/core/cells/cells.ts +1 -4
  115. package/src/core/cells/scrollCellIntoView.ts +3 -2
  116. package/src/core/codemirror/cm.ts +2 -0
  117. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +123 -0
  118. package/src/core/codemirror/lsp/notebook-lsp.ts +44 -4
  119. package/src/core/codemirror/misc/__tests__/string-braces.test.ts +200 -0
  120. package/src/core/codemirror/misc/string-braces.ts +37 -0
  121. package/src/core/errors/state.ts +7 -1
  122. package/src/core/islands/main.ts +2 -0
  123. package/src/core/kernel/__tests__/handlers.test.ts +2 -2
  124. package/src/core/kernel/state.ts +1 -0
  125. package/src/core/network/__tests__/requests-lazy.test.ts +1 -1
  126. package/src/core/network/requests-lazy.ts +2 -2
  127. package/src/core/websocket/types.ts +1 -0
  128. package/src/core/websocket/useMarimoKernelConnection.tsx +18 -1
  129. package/src/css/app/Cell.css +6 -0
  130. package/src/css/globals.css +2 -0
  131. package/src/mount.tsx +6 -0
  132. package/src/plugins/impl/chat/chat-ui.tsx +16 -4
  133. package/src/utils/events.ts +1 -0
  134. package/dist/assets/VisuallyHidden-BodIky8L.js +0 -1
  135. package/dist/assets/button-DuYGqRtX.js +0 -1
  136. package/dist/assets/config-babG4OBR.js +0 -1
  137. package/dist/assets/context-BAYdLMF_.js +0 -1
  138. package/dist/assets/edit-page-pktOsK3x.js +0 -12
  139. package/dist/assets/globe-CY9im410.js +0 -1
  140. package/dist/assets/index-CGjwCay2.js +0 -43
  141. package/dist/assets/index-nL7dJutx.css +0 -2
  142. package/dist/assets/input-CaEtLL8p.js +0 -1
  143. package/dist/assets/links-ENMiP32L.js +0 -1
  144. package/dist/assets/name-cell-input-CsDYsdH3.js +0 -1
  145. package/dist/assets/panels-DfOJ05Sp.js +0 -1
  146. package/dist/assets/state-BTRRCWOH.js +0 -1
  147. package/dist/assets/useBoolean-5kuXz69O.js +0 -1
  148. package/dist/assets/useNotebookActions-DQW0wwU6.js +0 -1
  149. package/dist/assets/usePress-C2LPFxyv.js +0 -7
  150. package/dist/assets/utilities.esm-FYD46c3d.js +0 -3
@@ -9,6 +9,7 @@ import { invariant } from "@/utils/invariant";
9
9
  import { Logger } from "@/utils/Logger";
10
10
  import { LRUCache } from "@/utils/lru";
11
11
  import { Objects } from "@/utils/objects";
12
+ import { getPositionAtWordBounds } from "../completion/hints";
12
13
  import { topologicalCodesAtom } from "../copilot/getCodes";
13
14
  import {
14
15
  getEditorCodeAsPython,
@@ -22,6 +23,14 @@ import {
22
23
  } from "./types";
23
24
  import { getLSPDocument } from "./utils";
24
25
 
26
+ /**
27
+ * Check if a variable name is private (starts with underscore but not dunder).
28
+ * Private variables in marimo are cell-local and should not be renamed across cells.
29
+ */
30
+ function isPrivateVariable(name: string): boolean {
31
+ return name.startsWith("_") && !name.startsWith("__");
32
+ }
33
+
25
34
  class Snapshotter {
26
35
  private documentVersion = 0;
27
36
  private readonly getNotebookCode: () => {
@@ -433,15 +442,46 @@ export class NotebookLanguageServerClient implements ILanguageServerClient {
433
442
 
434
443
  // Update the code in the plugins manually
435
444
  const editors = this.getNotebookEditors();
436
- for (const [cellId, ev] of Objects.entries(editors)) {
437
- const newCode = editsToNewCode.get(cellId);
445
+
446
+ // Check if this is a private variable rename (should only affect current cell)
447
+ // Private variables in marimo are cell-local and should not be renamed across cells
448
+ const originEditor = editors[cellId];
449
+ let isPrivateRename = false;
450
+ if (originEditor) {
451
+ // Convert LSP position (line, character) to CodeMirror position
452
+ const line = originEditor.state.doc.line(params.position.line + 1);
453
+ const cmPosition = line.from + params.position.character;
454
+ const { startToken, endToken } = getPositionAtWordBounds(
455
+ originEditor.state.doc,
456
+ cmPosition,
457
+ );
458
+ const originalName = originEditor.state.doc.sliceString(
459
+ startToken,
460
+ endToken,
461
+ );
462
+ isPrivateRename = isPrivateVariable(originalName);
463
+ if (isPrivateRename) {
464
+ Logger.debug(
465
+ "[lsp] Private variable rename detected, limiting to current cell",
466
+ originalName,
467
+ );
468
+ }
469
+ }
470
+
471
+ for (const [currentCellId, ev] of Objects.entries(editors)) {
472
+ // For private variable renames, only update the originating cell
473
+ if (isPrivateRename && currentCellId !== cellId) {
474
+ continue;
475
+ }
476
+
477
+ const newCode = editsToNewCode.get(currentCellId);
438
478
  if (newCode == null) {
439
- Logger.warn("No new code for cell", cellId);
479
+ Logger.warn("No new code for cell", currentCellId);
440
480
  continue;
441
481
  }
442
482
 
443
483
  if (!ev) {
444
- Logger.warn("No view for plugin", cellId);
484
+ Logger.warn("No view for plugin", currentCellId);
445
485
  continue;
446
486
  }
447
487
 
@@ -0,0 +1,200 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { python } from "@codemirror/lang-python";
4
+ import { EditorState } from "@codemirror/state";
5
+ import { EditorView } from "@codemirror/view";
6
+ import { afterEach, describe, expect, it } from "vitest";
7
+ import { stringBraceInputHandler } from "../string-braces";
8
+
9
+ function createEditor(
10
+ initialContent: string,
11
+ cursorPosition: number,
12
+ ): EditorView {
13
+ const state = EditorState.create({
14
+ doc: initialContent,
15
+ selection: { anchor: cursorPosition },
16
+ extensions: [python()],
17
+ });
18
+
19
+ const view = new EditorView({
20
+ state,
21
+ parent: document.body,
22
+ });
23
+
24
+ return view;
25
+ }
26
+
27
+ describe("string brace auto-closing", () => {
28
+ let view: EditorView;
29
+
30
+ afterEach(() => {
31
+ if (view) {
32
+ view.destroy();
33
+ if (document.body.contains(view.dom)) {
34
+ view.dom.remove();
35
+ }
36
+ }
37
+ });
38
+
39
+ it("should auto-close braces in f-strings", () => {
40
+ view = createEditor('f"hello ', 8);
41
+ const result = stringBraceInputHandler(view, 8, 8, "{");
42
+
43
+ expect(result).toBe(true);
44
+ expect(view.state.doc.toString()).toBe('f"hello {}');
45
+ expect(view.state.selection.main.head).toBe(9);
46
+ });
47
+
48
+ it("should auto-close braces in regular double-quoted strings", () => {
49
+ view = createEditor('"hello ', 7);
50
+ const result = stringBraceInputHandler(view, 7, 7, "{");
51
+
52
+ expect(result).toBe(true);
53
+ expect(view.state.doc.toString()).toBe('"hello {}');
54
+ expect(view.state.selection.main.head).toBe(8);
55
+ });
56
+
57
+ it("should auto-close braces in rf-strings", () => {
58
+ view = createEditor('rf"hello ', 9);
59
+ const result = stringBraceInputHandler(view, 9, 9, "{");
60
+
61
+ expect(result).toBe(true);
62
+ expect(view.state.doc.toString()).toBe('rf"hello {}');
63
+ expect(view.state.selection.main.head).toBe(10);
64
+ });
65
+
66
+ it("should auto-close braces in fr-strings", () => {
67
+ view = createEditor('fr"hello ', 9);
68
+ const result = stringBraceInputHandler(view, 9, 9, "{");
69
+
70
+ expect(result).toBe(true);
71
+ expect(view.state.doc.toString()).toBe('fr"hello {}');
72
+ expect(view.state.selection.main.head).toBe(10);
73
+ });
74
+
75
+ it("should auto-close braces in single-quoted strings", () => {
76
+ view = createEditor("'hello ", 7);
77
+ const result = stringBraceInputHandler(view, 7, 7, "{");
78
+
79
+ expect(result).toBe(true);
80
+ expect(view.state.doc.toString()).toBe("'hello {}");
81
+ expect(view.state.selection.main.head).toBe(8);
82
+ });
83
+
84
+ it("should auto-close braces in uppercase F-strings", () => {
85
+ view = createEditor('F"hello ', 8);
86
+ const result = stringBraceInputHandler(view, 8, 8, "{");
87
+
88
+ expect(result).toBe(true);
89
+ expect(view.state.doc.toString()).toBe('F"hello {}');
90
+ expect(view.state.selection.main.head).toBe(9);
91
+ });
92
+
93
+ it("should auto-close braces in raw strings without f/t", () => {
94
+ view = createEditor('r"hello ', 8);
95
+ const result = stringBraceInputHandler(view, 8, 8, "{");
96
+
97
+ expect(result).toBe(true);
98
+ expect(view.state.doc.toString()).toBe('r"hello {}');
99
+ expect(view.state.selection.main.head).toBe(9);
100
+ });
101
+
102
+ // Handled by other CodeMirror handler(s)
103
+ it("should NOT auto-close braces outside strings", () => {
104
+ view = createEditor("x = ", 4);
105
+ const result = stringBraceInputHandler(view, 4, 4, "{");
106
+
107
+ expect(result).toBe(false);
108
+ expect(view.state.doc.toString()).toBe("x = ");
109
+ });
110
+
111
+ it("should NOT auto-close braces when typing other characters", () => {
112
+ view = createEditor('f"hello ', 8);
113
+ const result = stringBraceInputHandler(view, 8, 8, "a");
114
+
115
+ expect(result).toBe(false);
116
+ expect(view.state.doc.toString()).toBe('f"hello ');
117
+ });
118
+
119
+ it("should handle braces at the start of string", () => {
120
+ view = createEditor('f"', 2);
121
+ const result = stringBraceInputHandler(view, 2, 2, "{");
122
+
123
+ expect(result).toBe(true);
124
+ expect(view.state.doc.toString()).toBe('f"{}');
125
+ expect(view.state.selection.main.head).toBe(3);
126
+ });
127
+
128
+ it("should handle braces in the middle of string content", () => {
129
+ view = createEditor('f"hello world ', 14);
130
+ const result = stringBraceInputHandler(view, 14, 14, "{");
131
+
132
+ expect(result).toBe(true);
133
+ expect(view.state.doc.toString()).toBe('f"hello world {}');
134
+ expect(view.state.selection.main.head).toBe(15);
135
+ });
136
+
137
+ it("should handle multiple braces in string", () => {
138
+ view = createEditor('f"hello {} world', 16);
139
+ const result = stringBraceInputHandler(view, 16, 16, "{");
140
+
141
+ expect(result).toBe(true);
142
+ expect(view.state.doc.toString()).toBe('f"hello {} world{}');
143
+ expect(view.state.selection.main.head).toBe(17);
144
+ });
145
+
146
+ it("should handle empty string", () => {
147
+ view = createEditor('f""', 2);
148
+ const result = stringBraceInputHandler(view, 2, 2, "{");
149
+
150
+ expect(result).toBe(true);
151
+ expect(view.state.doc.toString()).toBe('f"{}"');
152
+ expect(view.state.selection.main.head).toBe(3);
153
+ });
154
+
155
+ it("should auto-close braces in triple-quoted strings", () => {
156
+ view = createEditor('"""hello ', 9);
157
+ const result = stringBraceInputHandler(view, 9, 9, "{");
158
+
159
+ expect(result).toBe(true);
160
+ expect(view.state.doc.toString()).toBe('"""hello {}');
161
+ expect(view.state.selection.main.head).toBe(10);
162
+ });
163
+
164
+ it("should auto-close braces in triple-quoted f-strings", () => {
165
+ view = createEditor('f"""hello ', 10);
166
+ const result = stringBraceInputHandler(view, 10, 10, "{");
167
+
168
+ expect(result).toBe(true);
169
+ expect(view.state.doc.toString()).toBe('f"""hello {}');
170
+ expect(view.state.selection.main.head).toBe(11);
171
+ });
172
+
173
+ it("should auto-close braces in triple single-quoted strings", () => {
174
+ view = createEditor("'''hello ", 9);
175
+ const result = stringBraceInputHandler(view, 9, 9, "{");
176
+
177
+ expect(result).toBe(true);
178
+ expect(view.state.doc.toString()).toBe("'''hello {}");
179
+ expect(view.state.selection.main.head).toBe(10);
180
+ });
181
+
182
+ it("should auto-close braces in triple single-quoted f-strings", () => {
183
+ view = createEditor("f'''hello ", 10);
184
+ const result = stringBraceInputHandler(view, 10, 10, "{");
185
+
186
+ expect(result).toBe(true);
187
+ expect(view.state.doc.toString()).toBe("f'''hello {}");
188
+ expect(view.state.selection.main.head).toBe(11);
189
+ });
190
+
191
+ it("should NOT auto-close braces when text is selected", () => {
192
+ view = createEditor('f"hello world"', 8);
193
+ // User has selected "world" (from position 8 to 13)
194
+ const result = stringBraceInputHandler(view, 8, 13, "{");
195
+
196
+ expect(result).toBe(false);
197
+ // Document should remain unchanged since we return false
198
+ expect(view.state.doc.toString()).toBe('f"hello world"');
199
+ });
200
+ });
@@ -0,0 +1,37 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { syntaxTree } from "@codemirror/language";
3
+ import type { Extension } from "@codemirror/state";
4
+ import { EditorView } from "@codemirror/view";
5
+
6
+ export function stringBraceInputHandler(
7
+ view: EditorView,
8
+ from: number,
9
+ to: number,
10
+ text: string,
11
+ ): boolean {
12
+ if (text !== "{") {
13
+ return false;
14
+ }
15
+
16
+ if (from !== to) {
17
+ return false;
18
+ }
19
+
20
+ const tree = syntaxTree(view.state);
21
+ const node = tree.resolveInner(from, -1);
22
+
23
+ if (!node?.type.name.includes("String")) {
24
+ return false;
25
+ }
26
+
27
+ view.dispatch({
28
+ changes: { from, to, insert: "{}" },
29
+ selection: { anchor: from + 1 },
30
+ userEvent: "input.type",
31
+ });
32
+ return true;
33
+ }
34
+
35
+ export function stringsAutoCloseBraces(): Extension {
36
+ return EditorView.inputHandler.of(stringBraceInputHandler);
37
+ }
@@ -1,11 +1,17 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import { useAtomValue } from "jotai";
3
+ import { atom, useAtomValue } from "jotai";
4
4
  import { createReducerAndAtoms } from "@/utils/createReducer";
5
5
  import type { Identified } from "@/utils/typed";
6
6
  import { generateUUID } from "@/utils/uuid";
7
7
  import type { Banner } from "../kernel/messages";
8
8
 
9
+ /**
10
+ * Atom for storing kernel startup error message.
11
+ * When set to a non-null value, shows a modal with the error details.
12
+ */
13
+ export const kernelStartupErrorAtom = atom<string | null>(null);
14
+
9
15
  interface BannerState {
10
16
  banners: Identified<Banner>[];
11
17
  }
@@ -192,6 +192,8 @@ export async function initialize() {
192
192
  return;
193
193
  case "cache-info":
194
194
  return;
195
+ case "kernel-startup-error":
196
+ return;
195
197
  default:
196
198
  logNever(msg.data);
197
199
  }
@@ -142,8 +142,8 @@ describe("buildCellData", () => {
142
142
  cell2: "y = 2",
143
143
  },
144
144
  last_execution_time: {
145
- cell1: 1234567890,
146
- cell2: 1234567891,
145
+ cell1: 1_234_567_890,
146
+ cell2: 1_234_567_891,
147
147
  },
148
148
  app_config: {
149
149
  width: "normal",
@@ -1,3 +1,4 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
1
2
  import { atom } from "jotai";
2
3
  import { waitFor } from "../state/jotai";
3
4
 
@@ -1,4 +1,4 @@
1
- /* Copyright 2024 Marimo. All rights reserved. */
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { beforeEach, describe, expect, it, vi } from "vitest";
4
4
  import type { RuntimeManager } from "../../runtime/runtime";
@@ -1,4 +1,4 @@
1
- /* Copyright 2024 Marimo. All rights reserved. */
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { NoKernelConnectedError } from "@/utils/errors";
4
4
  import { Logger } from "@/utils/Logger";
@@ -152,7 +152,7 @@ export function createLazyRequests(
152
152
  `Dropping request: ${key}, since not connected to a kernel.`,
153
153
  );
154
154
  // Silently drop the request
155
- return Promise.resolve();
155
+ return;
156
156
 
157
157
  case "throwError":
158
158
  throw new NoKernelConnectedError();
@@ -16,6 +16,7 @@ export const WebSocketClosedReason = {
16
16
  KERNEL_DISCONNECTED: "KERNEL_DISCONNECTED",
17
17
  ALREADY_RUNNING: "ALREADY_RUNNING",
18
18
  MALFORMED_QUERY: "MALFORMED_QUERY",
19
+ KERNEL_STARTUP_ERROR: "KERNEL_STARTUP_ERROR",
19
20
  } as const;
20
21
 
21
22
  export type WebSocketClosedReason =
@@ -40,7 +40,7 @@ import {
40
40
  } from "../datasets/request-registry";
41
41
  import { useDatasetsActions } from "../datasets/state";
42
42
  import { UI_ELEMENT_REGISTRY } from "../dom/uiregistry";
43
- import { useBannersActions } from "../errors/state";
43
+ import { kernelStartupErrorAtom, useBannersActions } from "../errors/state";
44
44
  import { FUNCTIONS_REGISTRY } from "../functions/FunctionRegistry";
45
45
  import {
46
46
  handleCellNotificationeration,
@@ -105,6 +105,7 @@ export function useMarimoKernelConnection(opts: {
105
105
  const setCapabilities = useSetAtom(capabilitiesAtom);
106
106
  const runtimeManager = useRuntimeManager();
107
107
  const setCacheInfo = useSetAtom(cacheInfoAtom);
108
+ const setKernelStartupError = useSetAtom(kernelStartupErrorAtom);
108
109
 
109
110
  const handleMessage = (e: MessageEvent<JsonString<NotificationPayload>>) => {
110
111
  const msg = jsonParseWithSpecialChar(e.data);
@@ -134,6 +135,11 @@ export function useMarimoKernelConnection(opts: {
134
135
  case "interrupted":
135
136
  return;
136
137
 
138
+ case "kernel-startup-error":
139
+ // Full error received via message before websocket close
140
+ setKernelStartupError(msg.data.error);
141
+ return;
142
+
137
143
  case "send-ui-element-message": {
138
144
  const modelId = msg.data.model_id;
139
145
  const uiElement = msg.data.ui_element;
@@ -413,6 +419,17 @@ export function useMarimoKernelConnection(opts: {
413
419
  return;
414
420
 
415
421
  default:
422
+ // Check for kernel startup error (full error already received via message)
423
+ if (e.reason === "MARIMO_KERNEL_STARTUP_ERROR") {
424
+ setConnection({
425
+ state: WebSocketState.CLOSED,
426
+ code: WebSocketClosedReason.KERNEL_STARTUP_ERROR,
427
+ reason: "Failed to start kernel sandbox",
428
+ });
429
+ ws.close(); // prevent reconnecting
430
+ return;
431
+ }
432
+
416
433
  // Session should be valid
417
434
  // - browser tab might have been closed or re-opened
418
435
  // - computer might have just woken from sleep
@@ -319,6 +319,12 @@
319
319
  }
320
320
  }
321
321
 
322
+ #cell-setup,
323
+ #cell-setup .cm-editor,
324
+ #cell-setup .cm-gutter {
325
+ background-color: var(--gray-2);
326
+ }
327
+
322
328
  #App.disconnected {
323
329
  /* Background determined by disconnected gradient/noise. */
324
330
 
@@ -184,8 +184,10 @@
184
184
  --shadow-2xl-solid: 10px 12px 0px 0px var(--base-shadow-darker), 0 0px 8px 0px hsl(0deg 0% 90% / 50%);
185
185
 
186
186
  /* Solid shadows with lighter shade color */
187
+
187
188
  /* biome-ignore format: definition needs to be oneline or breaks variants */
188
189
  --shadow-sm-solid-shade: 2px 2px 0px 0px var(--base-shadow), 0px 0px 2px 0px hsl(0deg 0% 50% / 20%);
190
+
189
191
  /* biome-ignore format: definition needs to be oneline or breaks variants */
190
192
  --shadow-md-solid-shade: 4px 4px 0px 0px var(--base-shadow), 0 0px 2px 0px hsl(0deg 0% 60% / 50%);
191
193
  }
package/src/mount.tsx CHANGED
@@ -29,6 +29,7 @@ import {
29
29
  import { MarimoApp, preloadPage } from "./core/MarimoApp";
30
30
  import { type AppMode, initialModeAtom, viewStateAtom } from "./core/mode";
31
31
  import { cleanupAuthQueryParams } from "./core/network/auth";
32
+ import { connectionAtom } from "./core/network/connection";
32
33
  import { requestClientAtom } from "./core/network/requests";
33
34
  import { resolveRequestClient } from "./core/network/resolve";
34
35
  import {
@@ -42,6 +43,7 @@ import { isStaticNotebook } from "./core/static/static-state";
42
43
  import { maybeRegisterVSCodeBindings } from "./core/vscode/vscode-bindings";
43
44
  import type { FileStore } from "./core/wasm/store";
44
45
  import { notebookFileStore } from "./core/wasm/store";
46
+ import { WebSocketState } from "./core/websocket/types";
45
47
  import { vegaLoader } from "./plugins/impl/vega/loader";
46
48
  import { initializePlugins } from "./plugins/plugins";
47
49
  import { ThemeProvider } from "./theme/ThemeProvider";
@@ -304,6 +306,10 @@ function initStore(options: unknown) {
304
306
  ...firstRuntimeConfig,
305
307
  serverToken: parsedOptions.data.serverToken,
306
308
  });
309
+ // If the remote runtime is not lazy, start it in CONNECTING
310
+ if (!firstRuntimeConfig.lazy && !isStaticNotebook()) {
311
+ store.set(connectionAtom, { state: WebSocketState.CONNECTING });
312
+ }
307
313
  } else {
308
314
  store.set(runtimeConfigAtom, {
309
315
  ...DEFAULT_RUNTIME_CONFIG,
@@ -75,12 +75,26 @@ interface Props extends PluginFunctions {
75
75
  export const Chatbot: React.FC<Props> = (props) => {
76
76
  const [input, setInput] = useState("");
77
77
  const [config, setConfig] = useState<ChatConfig>(props.config);
78
+ const [prevPropsConfig, setPrevPropsConfig] = useState<ChatConfig>(
79
+ props.config,
80
+ );
78
81
  const [files, setFiles] = useState<File[] | undefined>(undefined);
79
82
  const fileInputRef = useRef<HTMLInputElement>(null);
80
83
  const formRef = useRef<HTMLFormElement>(null);
81
84
  const codeMirrorInputRef = useRef<ReactCodeMirrorRef>(null);
82
85
  const scrollContainerRef = useRef<HTMLDivElement>(null);
83
86
 
87
+ const configChanged = Object.keys(props.config).some(
88
+ (key) =>
89
+ props.config[key as keyof ChatConfig] !==
90
+ prevPropsConfig[key as keyof ChatConfig],
91
+ );
92
+
93
+ if (configChanged) {
94
+ setConfig(props.config);
95
+ setPrevPropsConfig(props.config);
96
+ }
97
+
84
98
  // Use a ref to avoid stale closure in the fetch callback
85
99
  const configRef = useRef<ChatConfig>(config);
86
100
  configRef.current = config;
@@ -739,7 +753,6 @@ const ConfigPopup: React.FC<{
739
753
  config: ChatConfig;
740
754
  onChange: (newConfig: ChatConfig) => void;
741
755
  }> = ({ config, onChange }) => {
742
- const [localConfig, setLocalConfig] = useState<ChatConfig>(config);
743
756
  const [open, setOpen] = useState(false);
744
757
 
745
758
  const handleChange = (key: keyof ChatConfig, value: number | null) => {
@@ -754,8 +767,7 @@ const ConfigPopup: React.FC<{
754
767
  finalValue = clampedValue;
755
768
  }
756
769
 
757
- const newConfig = { ...localConfig, [key]: finalValue };
758
- setLocalConfig(newConfig);
770
+ const newConfig = { ...config, [key]: finalValue };
759
771
  onChange(newConfig);
760
772
  };
761
773
 
@@ -782,7 +794,7 @@ const ConfigPopup: React.FC<{
782
794
  <PopoverContent className="w-70 border">
783
795
  <div className="grid gap-3">
784
796
  <h4 className="font-bold leading-none">Configuration</h4>
785
- {Objects.entries(localConfig).map(([key, value]) => (
797
+ {Objects.entries(config).map(([key, value]) => (
786
798
  <div key={key} className="grid grid-cols-3 items-center gap-1">
787
799
  <Label
788
800
  htmlFor={key}
@@ -42,6 +42,7 @@ export const Events = {
42
42
  target.tagName === "INPUT" ||
43
43
  target.tagName === "TEXTAREA" ||
44
44
  target.tagName.startsWith("MARIMO") ||
45
+ target.isContentEditable ||
45
46
  Events.fromCodeMirror(e)
46
47
  );
47
48
  },
@@ -1 +0,0 @@
1
- import{s as be}from"./chunk-LvLJmgfZ.js";import{t as me}from"./react-BGmjiNul.js";import{L as ve,P as Ee,R as Te}from"./input-CaEtLL8p.js";import{A as w,C as ge,M as we,N as v,O as Re,b as J,c as Ce,j as S,k as X,l as ye,w as Se,z as R}from"./usePress-C2LPFxyv.js";import{t as ke}from"./context-BAYdLMF_.js";var f=be(me(),1),_e=(0,f.createContext)(null);(0,f.createContext)(null),(0,f.createContext)(null),(0,f.createContext)(null),(0,f.createContext)(null);var Le=(0,f.createContext)({}),xe=(0,f.createContext)(null);(0,f.createContext)(null);var Fe=class{get currentNode(){return this._currentNode}set currentNode(e){if(!X(this.root,e))throw Error("Cannot set currentNode to a node that is not contained by the root node.");let t=[],r=e,n=e;for(this._currentNode=e;r&&r!==this.root;)if(r.nodeType===Node.DOCUMENT_FRAGMENT_NODE){let i=r,l=this._doc.createTreeWalker(i,this.whatToShow,{acceptNode:this._acceptNode});t.push(l),l.currentNode=n,this._currentSetFor.add(l),r=n=i.host}else r=r.parentNode;let o=this._doc.createTreeWalker(this.root,this.whatToShow,{acceptNode:this._acceptNode});t.push(o),o.currentNode=n,this._currentSetFor.add(o),this._walkerStack=t}get doc(){return this._doc}firstChild(){let e=this.currentNode,t=this.nextNode();return X(e,t)?(t&&(this.currentNode=t),t):(this.currentNode=e,null)}lastChild(){let e=this._walkerStack[0].lastChild();return e&&(this.currentNode=e),e}nextNode(){var t;let e=this._walkerStack[0].nextNode();if(e){if(e.shadowRoot){let r;if(typeof this.filter=="function"?r=this.filter(e):(t=this.filter)!=null&&t.acceptNode&&(r=this.filter.acceptNode(e)),r===NodeFilter.FILTER_ACCEPT)return this.currentNode=e,e;let n=this.nextNode();return n&&(this.currentNode=n),n}return e&&(this.currentNode=e),e}else if(this._walkerStack.length>1){this._walkerStack.shift();let r=this.nextNode();return r&&(this.currentNode=r),r}else return null}previousNode(){var r;let e=this._walkerStack[0];if(e.currentNode===e.root){if(this._currentSetFor.has(e))if(this._currentSetFor.delete(e),this._walkerStack.length>1){this._walkerStack.shift();let n=this.previousNode();return n&&(this.currentNode=n),n}else return null;return null}let t=e.previousNode();if(t){if(t.shadowRoot){let n;if(typeof this.filter=="function"?n=this.filter(t):(r=this.filter)!=null&&r.acceptNode&&(n=this.filter.acceptNode(t)),n===NodeFilter.FILTER_ACCEPT)return t&&(this.currentNode=t),t;let o=this.lastChild();return o&&(this.currentNode=o),o}return t&&(this.currentNode=t),t}else if(this._walkerStack.length>1){this._walkerStack.shift();let n=this.previousNode();return n&&(this.currentNode=n),n}else return null}nextSibling(){return null}previousSibling(){return null}parentNode(){return null}constructor(e,t,r,n){this._walkerStack=[],this._currentSetFor=new Set,this._acceptNode=i=>{var l;if(i.nodeType===Node.ELEMENT_NODE){let s=i.shadowRoot;if(s){let u=this._doc.createTreeWalker(s,this.whatToShow,{acceptNode:this._acceptNode});return this._walkerStack.unshift(u),NodeFilter.FILTER_ACCEPT}else{if(typeof this.filter=="function")return this.filter(i);if((l=this.filter)!=null&&l.acceptNode)return this.filter.acceptNode(i);if(this.filter===null)return NodeFilter.FILTER_ACCEPT}}return NodeFilter.FILTER_SKIP},this._doc=e,this.root=t,this.filter=n??null,this.whatToShow=r??NodeFilter.SHOW_ALL,this._currentNode=t,this._walkerStack.unshift(e.createTreeWalker(t,r,this._acceptNode));let o=t.shadowRoot;if(o){let i=this._doc.createTreeWalker(o,this.whatToShow,{acceptNode:this._acceptNode});this._walkerStack.unshift(i)}}};function Me(e,t,r,n){return we()?new Fe(e,t,r,n):e.createTreeWalker(t,r,n)}function Ie(e,t){let r=(0,f.useRef)(!0),n=(0,f.useRef)(null);(0,f.useEffect)(()=>(r.current=!0,()=>{r.current=!1}),[]),(0,f.useEffect)(()=>{let o=n.current;r.current?r.current=!1:(!o||t.some((i,l)=>!Object.is(i,o[l])))&&e(),n.current=t},t)}function x(e,t){if(!e)return!1;let r=window.getComputedStyle(e),n=/(auto|scroll)/.test(r.overflow+r.overflowX+r.overflowY);return n&&t&&(n=e.scrollHeight!==e.clientHeight||e.scrollWidth!==e.clientWidth),n}function Pe(e,t){let r=e;for(x(r,t)&&(r=r.parentElement);r&&!x(r,t);)r=r.parentElement;return r||document.scrollingElement||document.documentElement}function Ae(e,t){let r=[];for(;e&&e!==document.documentElement;)x(e,t)&&r.push(e),e=e.parentElement;return r}function We(e){return ge()?e.metaKey:e.ctrlKey}var Ke=new Set(["checkbox","radio","range","color","file","image","button","submit","reset"]);function He(e){return e instanceof HTMLInputElement&&!Ke.has(e.type)||e instanceof HTMLTextAreaElement||e instanceof HTMLElement&&e.isContentEditable}var Oe=0,z=new Map;function De(e){let[t,r]=(0,f.useState)();return R(()=>{if(!e)return;let n=z.get(e);if(n)r(n.element.id);else{let o=`react-aria-description-${Oe++}`;r(o);let i=document.createElement("div");i.id=o,i.style.display="none",i.textContent=e,document.body.appendChild(i),n={refCount:0,element:i},z.set(e,n)}return n.refCount++,()=>{n&&--n.refCount===0&&(n.element.remove(),z.delete(e))}},[e]),{"aria-describedby":e?t:void 0}}function Y(e,t){let r=Q(e,t,"left"),n=Q(e,t,"top"),o=t.offsetWidth,i=t.offsetHeight,l=e.scrollLeft,s=e.scrollTop,{borderTopWidth:u,borderLeftWidth:c,scrollPaddingTop:a,scrollPaddingRight:p,scrollPaddingBottom:d,scrollPaddingLeft:N}=getComputedStyle(e),{scrollMarginTop:m,scrollMarginRight:L,scrollMarginBottom:k,scrollMarginLeft:ce}=getComputedStyle(t),se=l+parseInt(c,10),G=s+parseInt(u,10),M=se+e.clientWidth,I=G+e.clientHeight,P=parseInt(a,10)||0,A=parseInt(d,10)||0,W=parseInt(p,10)||0,K=parseInt(N,10)||0,ae=parseInt(m,10)||0,ue=parseInt(k,10)||0,de=parseInt(L,10)||0,H=r-(parseInt(ce,10)||0),O=r+o+de,D=n-ae,j=n+i+ue,fe=l+parseInt(c,10)+K,pe=M-W,he=s+parseInt(u,10)+P,Ne=I-A;(H>fe||O<pe)&&(H<=l+K?l=H-parseInt(c,10)-K:O>M-W&&(l+=O-M+W)),(D>he||j<Ne)&&(D<=G+P?s=D-parseInt(u,10)-P:j>I-A&&(s+=j-I+A)),e.scrollTo({left:l,top:s})}function Q(e,t,r){let n=r==="left"?"offsetLeft":"offsetTop",o=0;for(;t.offsetParent&&(o+=t[n],t.offsetParent!==e);){if(t.offsetParent.contains(e)){o-=e[n];break}t=t.offsetParent}return o}function je(e,t){if(e&&document.contains(e)){let l=document.scrollingElement||document.documentElement;if(window.getComputedStyle(l).overflow!=="hidden"&&!J()){var r;let{left:s,top:u}=e.getBoundingClientRect();e==null||(r=e.scrollIntoView)==null||r.call(e,{block:"nearest"});let{left:c,top:a}=e.getBoundingClientRect();if(Math.abs(s-c)>1||Math.abs(u-a)>1){var n,o,i;t==null||(o=t.containingElement)==null||(n=o.scrollIntoView)==null||n.call(o,{block:"center",inline:"center"}),(i=e.scrollIntoView)==null||i.call(e,{block:"nearest"})}}else{let s=Ae(e);for(let u of s)Y(u,e)}}}var B=new Map;function ze(e){let{locale:t}=ke(),r=t+(e?Object.entries(e).sort((o,i)=>o[0]<i[0]?-1:1).join():"");if(B.has(r))return B.get(r);let n=new Intl.Collator(t,e);return B.set(r,n),n}var Z=f.createContext(null),q="react-aria-focus-scope-restore",h=null;function Be(e){let{children:t,contain:r,restoreFocus:n,autoFocus:o}=e,i=(0,f.useRef)(null),l=(0,f.useRef)(null),s=(0,f.useRef)([]),{parentNode:u}=(0,f.useContext)(Z)||{},c=(0,f.useMemo)(()=>new $({scopeRef:s}),[s]);R(()=>{let d=u||b.root;if(b.getTreeNode(d.scopeRef)&&h&&!F(h,d.scopeRef)){let N=b.getTreeNode(h);N&&(d=N)}d.addChild(c),b.addNode(c)},[c,u]),R(()=>{let d=b.getTreeNode(s);d&&(d.contain=!!r)},[r]),R(()=>{var L;let d=(L=i.current)==null?void 0:L.nextSibling,N=[],m=k=>k.stopPropagation();for(;d&&d!==l.current;)N.push(d),d.addEventListener(q,m),d=d.nextSibling;return s.current=N,()=>{for(let k of N)k.removeEventListener(q,m)}},[t]),Je(s,n,r),Ve(s,r),Xe(s,n,r),Ge(s,o),(0,f.useEffect)(()=>{let d=w(v(s.current?s.current[0]:void 0)),N=null;if(g(d,s.current)){for(let m of b.traverse())m.scopeRef&&g(d,m.scopeRef.current)&&(N=m);N===b.getTreeNode(s)&&(h=N.scopeRef)}},[s]),R(()=>()=>{var N,m;let d=((m=(N=b.getTreeNode(s))==null?void 0:N.parent)==null?void 0:m.scopeRef)??null;(s===h||F(s,h))&&(!d||b.getTreeNode(d))&&(h=d),b.removeTreeNode(s)},[s]);let a=(0,f.useMemo)(()=>qe(s),[]),p=(0,f.useMemo)(()=>({focusManager:a,parentNode:c}),[c,a]);return f.createElement(Z.Provider,{value:p},f.createElement("span",{"data-focus-scope-start":!0,hidden:!0,ref:i}),t,f.createElement("span",{"data-focus-scope-end":!0,hidden:!0,ref:l}))}function qe(e){return{focusNext(t={}){let r=e.current,{from:n,tabbable:o,wrap:i,accept:l}=t,s=n||w(v(r[0]??void 0)),u=r[0].previousElementSibling,c=T(y(r),{tabbable:o,accept:l},r);c.currentNode=g(s,r)?s:u;let a=c.nextNode();return!a&&i&&(c.currentNode=u,a=c.nextNode()),a&&E(a,!0),a},focusPrevious(t={}){let r=e.current,{from:n,tabbable:o,wrap:i,accept:l}=t,s=n||w(v(r[0]??void 0)),u=r[r.length-1].nextElementSibling,c=T(y(r),{tabbable:o,accept:l},r);c.currentNode=g(s,r)?s:u;let a=c.previousNode();return!a&&i&&(c.currentNode=u,a=c.previousNode()),a&&E(a,!0),a},focusFirst(t={}){let r=e.current,{tabbable:n,accept:o}=t,i=T(y(r),{tabbable:n,accept:o},r);i.currentNode=r[0].previousElementSibling;let l=i.nextNode();return l&&E(l,!0),l},focusLast(t={}){let r=e.current,{tabbable:n,accept:o}=t,i=T(y(r),{tabbable:n,accept:o},r);i.currentNode=r[r.length-1].nextElementSibling;let l=i.previousNode();return l&&E(l,!0),l}}}function y(e){return e[0].parentElement}function _(e){let t=b.getTreeNode(h);for(;t&&t.scopeRef!==e;){if(t.contain)return!1;t=t.parent}return!0}function Ue(e){var r,n;if(e.checked)return!0;let t=[];return t=e.form?[...((n=(r=e.form)==null?void 0:r.elements)==null?void 0:n.namedItem(e.name))??[]]:[...v(e).querySelectorAll(`input[type="radio"][name="${CSS.escape(e.name)}"]`)].filter(o=>!o.form),t?!t.some(o=>o.checked):!1}function Ve(e,t){let r=(0,f.useRef)(void 0),n=(0,f.useRef)(void 0);R(()=>{let o=e.current;if(!t){n.current&&(n.current=(cancelAnimationFrame(n.current),void 0));return}let i=v(o?o[0]:void 0),l=c=>{if(c.key!=="Tab"||c.altKey||c.ctrlKey||c.metaKey||!_(e)||c.isComposing)return;let a=w(i),p=e.current;if(!p||!g(a,p))return;let d=T(y(p),{tabbable:!0},p);if(!a)return;d.currentNode=a;let N=c.shiftKey?d.previousNode():d.nextNode();N||(N=(d.currentNode=c.shiftKey?p[p.length-1].nextElementSibling:p[0].previousElementSibling,c.shiftKey?d.previousNode():d.nextNode())),c.preventDefault(),N&&E(N,!0)},s=c=>{(!h||F(h,e))&&g(S(c),e.current)?(h=e,r.current=S(c)):_(e)&&!C(S(c),e)?r.current?r.current.focus():h&&h.current&&U(h.current):_(e)&&(r.current=S(c))},u=c=>{n.current&&cancelAnimationFrame(n.current),n.current=requestAnimationFrame(()=>{let a=Te(),p=(a==="virtual"||a===null)&&Se()&&J(),d=w(i);if(!p&&d&&_(e)&&!C(d,e)){h=e;let m=S(c);if(m&&m.isConnected){var N;r.current=m,(N=r.current)==null||N.focus()}else h.current&&U(h.current)}})};return i.addEventListener("keydown",l,!1),i.addEventListener("focusin",s,!1),o==null||o.forEach(c=>c.addEventListener("focusin",s,!1)),o==null||o.forEach(c=>c.addEventListener("focusout",u,!1)),()=>{i.removeEventListener("keydown",l,!1),i.removeEventListener("focusin",s,!1),o==null||o.forEach(c=>c.removeEventListener("focusin",s,!1)),o==null||o.forEach(c=>c.removeEventListener("focusout",u,!1))}},[e,t]),R(()=>()=>{n.current&&cancelAnimationFrame(n.current)},[n])}function ee(e){return C(e)}function g(e,t){return!e||!t?!1:t.some(r=>r.contains(e))}function C(e,t=null){if(e instanceof Element&&e.closest("[data-react-aria-top-layer]"))return!0;for(let{scopeRef:r}of b.traverse(b.getTreeNode(t)))if(r&&g(e,r.current))return!0;return!1}function $e(e){return C(e,h)}function F(e,t){var n;let r=(n=b.getTreeNode(t))==null?void 0:n.parent;for(;r;){if(r.scopeRef===e)return!0;r=r.parent}return!1}function E(e,t=!1){if(e!=null&&!t)try{ve(e)}catch{}else if(e!=null)try{e.focus()}catch{}}function te(e,t=!0){let r=e[0].previousElementSibling,n=y(e),o=T(n,{tabbable:t},e);o.currentNode=r;let i=o.nextNode();return t&&!i&&(n=y(e),o=T(n,{tabbable:!1},e),o.currentNode=r,i=o.nextNode()),i}function U(e,t=!0){E(te(e,t))}function Ge(e,t){let r=f.useRef(t);(0,f.useEffect)(()=>{r.current&&(h=e,!g(w(v(e.current?e.current[0]:void 0)),h.current)&&e.current&&U(e.current)),r.current=!1},[e])}function Je(e,t,r){R(()=>{if(t||r)return;let n=e.current,o=v(n?n[0]:void 0),i=l=>{let s=S(l);g(s,e.current)?h=e:ee(s)||(h=null)};return o.addEventListener("focusin",i,!1),n==null||n.forEach(l=>l.addEventListener("focusin",i,!1)),()=>{o.removeEventListener("focusin",i,!1),n==null||n.forEach(l=>l.removeEventListener("focusin",i,!1))}},[e,t,r])}function re(e){let t=b.getTreeNode(h);for(;t&&t.scopeRef!==e;){if(t.nodeToRestore)return!1;t=t.parent}return(t==null?void 0:t.scopeRef)===e}function Xe(e,t,r){let n=(0,f.useRef)(typeof document<"u"?w(v(e.current?e.current[0]:void 0)):null);R(()=>{let o=e.current,i=v(o?o[0]:void 0);if(!t||r)return;let l=()=>{(!h||F(h,e))&&g(w(i),e.current)&&(h=e)};return i.addEventListener("focusin",l,!1),o==null||o.forEach(s=>s.addEventListener("focusin",l,!1)),()=>{i.removeEventListener("focusin",l,!1),o==null||o.forEach(s=>s.removeEventListener("focusin",l,!1))}},[e,r]),R(()=>{let o=v(e.current?e.current[0]:void 0);if(!t)return;let i=l=>{if(l.key!=="Tab"||l.altKey||l.ctrlKey||l.metaKey||!_(e)||l.isComposing)return;let s=o.activeElement;if(!C(s,e)||!re(e))return;let u=b.getTreeNode(e);if(!u)return;let c=u.nodeToRestore,a=T(o.body,{tabbable:!0});a.currentNode=s;let p=l.shiftKey?a.previousNode():a.nextNode();if((!c||!c.isConnected||c===o.body)&&(c=void 0,u.nodeToRestore=void 0),(!p||!C(p,e))&&c){a.currentNode=c;do p=l.shiftKey?a.previousNode():a.nextNode();while(C(p,e));l.preventDefault(),l.stopPropagation(),p?E(p,!0):ee(c)?E(c,!0):s.blur()}};return r||o.addEventListener("keydown",i,!0),()=>{r||o.removeEventListener("keydown",i,!0)}},[e,t,r]),R(()=>{let o=v(e.current?e.current[0]:void 0);if(!t)return;let i=b.getTreeNode(e);if(i)return i.nodeToRestore=n.current??void 0,()=>{let l=b.getTreeNode(e);if(!l)return;let s=l.nodeToRestore,u=w(o);if(t&&s&&(u&&C(u,e)||u===o.body&&re(e))){let c=b.clone();requestAnimationFrame(()=>{if(o.activeElement===o.body){let a=c.getTreeNode(e);for(;a;){if(a.nodeToRestore&&a.nodeToRestore.isConnected){ne(a.nodeToRestore);return}a=a.parent}for(a=c.getTreeNode(e);a;){if(a.scopeRef&&a.scopeRef.current&&b.getTreeNode(a.scopeRef)){ne(te(a.scopeRef.current,!0));return}a=a.parent}}})}}},[e,t])}function ne(e){e.dispatchEvent(new CustomEvent(q,{bubbles:!0,cancelable:!0}))&&E(e)}function T(e,t,r){let n=t!=null&&t.tabbable?ye:Ce,o=v((e==null?void 0:e.nodeType)===Node.ELEMENT_NODE?e:null),i=Me(o,e||o,NodeFilter.SHOW_ELEMENT,{acceptNode(l){var s;return t!=null&&((s=t.from)!=null&&s.contains(l))||t!=null&&t.tabbable&&l.tagName==="INPUT"&&l.getAttribute("type")==="radio"&&(!Ue(l)||i.currentNode.tagName==="INPUT"&&i.currentNode.type==="radio"&&i.currentNode.name===l.name)?NodeFilter.FILTER_REJECT:n(l)&&(!r||g(l,r))&&(!(t!=null&&t.accept)||t.accept(l))?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});return t!=null&&t.from&&(i.currentNode=t.from),i}function Ye(e,t={}){return{focusNext(r={}){let n=e.current;if(!n)return null;let{from:o,tabbable:i=t.tabbable,wrap:l=t.wrap,accept:s=t.accept}=r,u=o||w(v(n)),c=T(n,{tabbable:i,accept:s});n.contains(u)&&(c.currentNode=u);let a=c.nextNode();return!a&&l&&(c.currentNode=n,a=c.nextNode()),a&&E(a,!0),a},focusPrevious(r=t){let n=e.current;if(!n)return null;let{from:o,tabbable:i=t.tabbable,wrap:l=t.wrap,accept:s=t.accept}=r,u=o||w(v(n)),c=T(n,{tabbable:i,accept:s});if(n.contains(u))c.currentNode=u;else{let p=V(c);return p&&E(p,!0),p??null}let a=c.previousNode();if(!a&&l){c.currentNode=n;let p=V(c);if(!p)return null;a=p}return a&&E(a,!0),a??null},focusFirst(r=t){let n=e.current;if(!n)return null;let{tabbable:o=t.tabbable,accept:i=t.accept}=r,l=T(n,{tabbable:o,accept:i}).nextNode();return l&&E(l,!0),l},focusLast(r=t){let n=e.current;if(!n)return null;let{tabbable:o=t.tabbable,accept:i=t.accept}=r,l=V(T(n,{tabbable:o,accept:i}));return l&&E(l,!0),l??null}}}function V(e){let t,r;do r=e.lastChild(),r&&(t=r);while(r);return t}var Qe=class le{get size(){return this.fastMap.size}getTreeNode(t){return this.fastMap.get(t)}addTreeNode(t,r,n){let o=this.fastMap.get(r??null);if(!o)return;let i=new $({scopeRef:t});o.addChild(i),i.parent=o,this.fastMap.set(t,i),n&&(i.nodeToRestore=n)}addNode(t){this.fastMap.set(t.scopeRef,t)}removeTreeNode(t){if(t===null)return;let r=this.fastMap.get(t);if(!r)return;let n=r.parent;for(let i of this.traverse())i!==r&&r.nodeToRestore&&i.nodeToRestore&&r.scopeRef&&r.scopeRef.current&&g(i.nodeToRestore,r.scopeRef.current)&&(i.nodeToRestore=r.nodeToRestore);let o=r.children;n&&(n.removeChild(r),o.size>0&&o.forEach(i=>n&&n.addChild(i))),this.fastMap.delete(r.scopeRef)}*traverse(t=this.root){if(t.scopeRef!=null&&(yield t),t.children.size>0)for(let r of t.children)yield*this.traverse(r)}clone(){var r;let t=new le;for(let n of this.traverse())t.addTreeNode(n.scopeRef,((r=n.parent)==null?void 0:r.scopeRef)??null,n.nodeToRestore);return t}constructor(){this.fastMap=new Map,this.root=new $({scopeRef:null}),this.fastMap.set(null,this.root)}},$=class{addChild(e){this.children.add(e),e.parent=this}removeChild(e){this.children.delete(e),e.parent=void 0}constructor(e){this.children=new Set,this.contain=!1,this.scopeRef=e.scopeRef}},b=new Qe,oe={border:0,clip:"rect(0 0 0 0)",clipPath:"inset(50%)",height:"1px",margin:"-1px",overflow:"hidden",padding:0,position:"absolute",width:"1px",whiteSpace:"nowrap"};function ie(e={}){let{style:t,isFocusable:r}=e,[n,o]=(0,f.useState)(!1),{focusWithinProps:i}=Ee({isDisabled:!r,onFocusWithinChange:s=>o(s)}),l=(0,f.useMemo)(()=>n?t:t?{...oe,...t}:oe,[n]);return{visuallyHiddenProps:{...i,style:l}}}function Ze(e){let{children:t,elementType:r="div",isFocusable:n,style:o,...i}=e,{visuallyHiddenProps:l}=ie(e);return f.createElement(r,Re(i,l),t)}export{xe as _,T as a,Y as c,We as d,He as f,_e as g,Ie as h,Be as i,je as l,x as m,ie as n,Ye as o,Pe as p,$e as r,ze as s,Ze as t,De as u,Le as v};
@@ -1 +0,0 @@
1
- import{s as k}from"./chunk-LvLJmgfZ.js";import{t as R}from"./react-BGmjiNul.js";import{t as j}from"./compiler-runtime-DeeZ7FnK.js";import{l as I}from"./hotkeys-uKX61F1_.js";import{n as S,t as K}from"./useEventListener-COkmyg1v.js";import{t as O}from"./jsx-runtime-DN_bIXfG.js";import{n as A,t as d}from"./cn-C1rgT0yh.js";var a=k(R(),1),w=k(O(),1),D=Symbol.for("react.lazy"),h=a.use;function P(e){return typeof e=="object"&&!!e&&"then"in e}function N(e){return typeof e=="object"&&!!e&&"$$typeof"in e&&e.$$typeof===D&&"_payload"in e&&P(e._payload)}function C(e){let t=$(e),r=a.forwardRef((n,o)=>{let{children:i,...l}=n;N(i)&&typeof h=="function"&&(i=h(i._payload));let s=a.Children.toArray(i),u=s.find(H);if(u){let p=u.props.children,c=s.map(g=>g===u?a.Children.count(p)>1?a.Children.only(null):a.isValidElement(p)?p.props.children:null:g);return(0,w.jsx)(t,{...l,ref:o,children:a.isValidElement(p)?a.cloneElement(p,void 0,c):null})}return(0,w.jsx)(t,{...l,ref:o,children:i})});return r.displayName=`${e}.Slot`,r}var _=C("Slot");function $(e){let t=a.forwardRef((r,n)=>{let{children:o,...i}=r;if(N(o)&&typeof h=="function"&&(o=h(o._payload)),a.isValidElement(o)){let l=W(o),s=V(i,o.props);return o.type!==a.Fragment&&(s.ref=n?S(n,l):l),a.cloneElement(o,s)}return a.Children.count(o)>1?a.Children.only(null):null});return t.displayName=`${e}.SlotClone`,t}var z=Symbol("radix.slottable");function H(e){return a.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===z}function V(e,t){let r={...t};for(let n in t){let o=e[n],i=t[n];/^on[A-Z]/.test(n)?o&&i?r[n]=(...l)=>{let s=i(...l);return o(...l),s}:o&&(r[n]=o):n==="style"?r[n]={...o,...i}:n==="className"&&(r[n]=[o,i].filter(Boolean).join(" "))}return{...e,...r}}function W(e){var n,o;let t=(n=Object.getOwnPropertyDescriptor(e.props,"ref"))==null?void 0:n.get,r=t&&"isReactWarning"in t&&t.isReactWarning;return r?e.ref:(t=(o=Object.getOwnPropertyDescriptor(e,"ref"))==null?void 0:o.get,r=t&&"isReactWarning"in t&&t.isReactWarning,r?e.props.ref:e.props.ref||e.ref)}const E={stopPropagation:e=>t=>{t.stopPropagation(),e&&e(t)},onEnter:e=>t=>{t.key==="Enter"&&e&&e(t)},preventFocus:e=>{e.preventDefault()},fromInput:e=>{let t=e.target;return t.tagName==="INPUT"||t.tagName==="TEXTAREA"||t.tagName.startsWith("MARIMO")||E.fromCodeMirror(e)},fromCodeMirror:e=>e.target.closest(".cm-editor")!==null,shouldIgnoreKeyboardEvent(e){return e.target instanceof HTMLInputElement||e.target instanceof HTMLTextAreaElement||e.target instanceof HTMLSelectElement||e.target instanceof HTMLElement&&(e.target.isContentEditable||e.target.tagName==="BUTTON"||e.target.closest("[role='textbox']")!==null||e.target.closest("[contenteditable='true']")!==null||e.target.closest(".cm-editor")!==null)},hasModifier:e=>e.ctrlKey||e.metaKey||e.altKey||e.shiftKey,isMetaOrCtrl:e=>e.metaKey||e.ctrlKey};var L=j(),f="active:shadow-none",T=A(d("disabled:opacity-50 disabled:pointer-events-none","inline-flex items-center justify-center rounded-md text-sm font-medium focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-2 ring-offset-background"),{variants:{variant:{default:d("bg-primary text-primary-foreground hover:bg-primary/90 shadow-xs border border-primary",f),destructive:d("border shadow-xs","bg-(--red-9) hover:bg-(--red-10) dark:bg-(--red-6) dark:hover:bg-(--red-7)","text-(--red-1) dark:text-(--red-12)","border-(--red-11)",f),success:d("border shadow-xs","bg-(--grass-9) hover:bg-(--grass-10) dark:bg-(--grass-6) dark:hover:bg-(--grass-7)","text-(--grass-1) dark:text-(--grass-12)","border-(--grass-11)",f),warn:d("border shadow-xs","bg-(--yellow-9) hover:bg-(--yellow-10) dark:bg-(--yellow-6) dark:hover:bg-(--yellow-7)","text-(--yellow-12)","border-(--yellow-11)",f),action:d("bg-action text-action-foreground shadow-xs","hover:bg-action-hover border border-action",f),outline:d("border border-slate-300 shadow-xs","hover:bg-accent hover:text-accent-foreground","hover:border-primary","aria-selected:text-accent-foreground aria-selected:border-primary",f),secondary:d("bg-secondary text-secondary-foreground hover:bg-secondary/80","border border-input shadow-xs",f),text:d("opacity-80 hover:opacity-100","active:opacity-100"),ghost:d("border border-transparent","hover:bg-accent hover:text-accent-foreground hover:shadow-xs",f,"active:text-accent-foreground"),link:"underline-offset-4 hover:underline text-link",linkDestructive:"underline-offset-4 hover:underline text-destructive underline-destructive",outlineDestructive:"border border-destructive text-destructive hover:bg-destructive/10"},size:{default:"h-10 py-2 px-4",xs:"h-7 px-2 rounded-md text-xs",sm:"h-9 px-3 rounded-md",lg:"h-11 px-8 rounded-md",icon:"h-6 w-6 mb-0"},disabled:{true:"opacity-50 pointer-events-none"}},defaultVariants:{variant:"default",size:"sm"}}),M=a.forwardRef((e,t)=>{let r=(0,L.c)(7),{className:n,variant:o,size:i,asChild:l,keyboardShortcut:s,...u}=e,p=l===void 0?!1:l,c=a.useRef(null),g;r[0]===Symbol.for("react.memo_cache_sentinel")?(g=()=>c.current,r[0]=g):g=r[0],a.useImperativeHandle(t,g);let b;r[1]===s?b=r[2]:(b=y=>{s&&(E.shouldIgnoreKeyboardEvent(y)||I(s)(y)&&(y.preventDefault(),y.stopPropagation(),c!=null&&c.current&&!c.current.disabled&&c.current.click()))},r[1]=s,r[2]=b),K(document,"keydown",b);let v=p?_:"button",x=d(T({variant:o,size:i,className:n,disabled:u.disabled}),n),m;return r[3]!==v||r[4]!==u||r[5]!==x?(m=(0,w.jsx)(v,{className:x,ref:c,...u}),r[3]=v,r[4]=u,r[5]=x,r[6]=m):m=r[6],m});M.displayName="Button";export{C as a,_ as i,T as n,E as r,M as t};