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

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 (148) 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-D2zgBMLw.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/components/app-config/__tests__/get-dirty-values.test.ts +1 -1
  101. package/src/components/app-config/user-config-form.tsx +1 -1
  102. package/src/components/chat/acp/agent-panel.tsx +56 -43
  103. package/src/components/editor/KernelStartupErrorModal.tsx +101 -0
  104. package/src/components/editor/actions/name-cell-input.tsx +10 -4
  105. package/src/components/editor/chrome/types.ts +2 -4
  106. package/src/components/editor/chrome/wrapper/app-chrome.tsx +55 -58
  107. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +150 -96
  108. package/src/components/editor/notebook-cell.tsx +13 -13
  109. package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +27 -0
  110. package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +6 -0
  111. package/src/components/utils/lazy-mount.tsx +29 -8
  112. package/src/core/MarimoApp.tsx +2 -0
  113. package/src/core/cells/cells.ts +1 -4
  114. package/src/core/cells/scrollCellIntoView.ts +3 -2
  115. package/src/core/codemirror/cm.ts +2 -0
  116. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +123 -0
  117. package/src/core/codemirror/lsp/notebook-lsp.ts +44 -4
  118. package/src/core/codemirror/misc/__tests__/string-braces.test.ts +200 -0
  119. package/src/core/codemirror/misc/string-braces.ts +37 -0
  120. package/src/core/errors/state.ts +7 -1
  121. package/src/core/islands/main.ts +2 -0
  122. package/src/core/kernel/__tests__/handlers.test.ts +2 -2
  123. package/src/core/kernel/state.ts +1 -0
  124. package/src/core/network/__tests__/requests-lazy.test.ts +1 -1
  125. package/src/core/network/requests-lazy.ts +2 -2
  126. package/src/core/websocket/types.ts +1 -0
  127. package/src/core/websocket/useMarimoKernelConnection.tsx +18 -1
  128. package/src/css/app/Cell.css +6 -0
  129. package/src/css/globals.css +2 -0
  130. package/src/plugins/impl/chat/chat-ui.tsx +16 -4
  131. package/src/utils/events.ts +1 -0
  132. package/dist/assets/VisuallyHidden-BodIky8L.js +0 -1
  133. package/dist/assets/button-DuYGqRtX.js +0 -1
  134. package/dist/assets/config-babG4OBR.js +0 -1
  135. package/dist/assets/context-BAYdLMF_.js +0 -1
  136. package/dist/assets/edit-page-pktOsK3x.js +0 -12
  137. package/dist/assets/globe-CY9im410.js +0 -1
  138. package/dist/assets/index-CGjwCay2.js +0 -43
  139. package/dist/assets/index-nL7dJutx.css +0 -2
  140. package/dist/assets/input-CaEtLL8p.js +0 -1
  141. package/dist/assets/links-ENMiP32L.js +0 -1
  142. package/dist/assets/name-cell-input-CsDYsdH3.js +0 -1
  143. package/dist/assets/panels-DfOJ05Sp.js +0 -1
  144. package/dist/assets/state-BTRRCWOH.js +0 -1
  145. package/dist/assets/useBoolean-5kuXz69O.js +0 -1
  146. package/dist/assets/useNotebookActions-DQW0wwU6.js +0 -1
  147. package/dist/assets/usePress-C2LPFxyv.js +0 -7
  148. package/dist/assets/utilities.esm-FYD46c3d.js +0 -3
@@ -2,6 +2,8 @@
2
2
 
3
3
  import {
4
4
  ChevronDownIcon,
5
+ ExternalLinkIcon,
6
+ InfoIcon,
5
7
  PowerOffIcon,
6
8
  ZapIcon,
7
9
  ZapOffIcon,
@@ -15,7 +17,9 @@ import {
15
17
  DropdownMenuSeparator,
16
18
  DropdownMenuTrigger,
17
19
  } from "@/components/ui/dropdown-menu";
20
+ import { ExternalLink } from "@/components/ui/links";
18
21
  import { Switch } from "@/components/ui/switch";
22
+ import { Tooltip, TooltipProvider } from "@/components/ui/tooltip";
19
23
  import { useResolvedMarimoConfig } from "@/core/config/config";
20
24
  import { useRequestClient } from "@/core/network/requests";
21
25
  import { isWasm } from "@/core/wasm/utils";
@@ -99,117 +103,167 @@ export const RuntimeSettings: React.FC<RuntimeSettingsProps> = ({
99
103
  </FooterItem>
100
104
  </DropdownMenuTrigger>
101
105
  <DropdownMenuContent align="start" className="w-64">
102
- <DropdownMenuLabel>Runtime reactivity</DropdownMenuLabel>
106
+ <DropdownMenuLabel>
107
+ <div className="flex items-center justify-between w-full">
108
+ <span>Runtime reactivity</span>
109
+ <ExternalLink href="https://links.marimo.app/runtime-configuration">
110
+ <span className="text-xs font-normal flex items-center gap-1">
111
+ Docs
112
+ <ExternalLinkIcon className="w-3 h-3" />
113
+ </span>
114
+ </ExternalLink>
115
+ </div>
116
+ </DropdownMenuLabel>
103
117
  <DropdownMenuSeparator />
104
118
 
105
- {/* On startup toggle */}
106
- <DisableIfOverridden name="runtime.auto_instantiate">
107
- <div className="flex items-center justify-between px-2 py-2">
108
- <div className="flex items-center space-x-2">
109
- {config.runtime.auto_instantiate ? (
110
- <ZapIcon size={14} className="text-amber-500" />
111
- ) : (
112
- <ZapOffIcon size={14} className="text-muted-foreground" />
113
- )}
114
- <div>
115
- <div className="text-sm font-medium">On startup</div>
116
- <div className="text-xs text-muted-foreground">
117
- {config.runtime.auto_instantiate ? "autorun" : "lazy"}
119
+ <TooltipProvider>
120
+ {/* On startup toggle */}
121
+ <DisableIfOverridden name="runtime.auto_instantiate">
122
+ <div className="flex items-center justify-between px-2 py-2">
123
+ <div className="flex items-center space-x-2">
124
+ {config.runtime.auto_instantiate ? (
125
+ <ZapIcon size={14} className="text-amber-500" />
126
+ ) : (
127
+ <ZapOffIcon size={14} className="text-muted-foreground" />
128
+ )}
129
+ <div>
130
+ <div className="text-sm font-medium flex items-center gap-1">
131
+ On startup
132
+ <Tooltip
133
+ content={
134
+ <div className="max-w-[200px]">
135
+ Whether to automatically run the notebook on startup
136
+ </div>
137
+ }
138
+ >
139
+ <InfoIcon className="w-3 h-3" />
140
+ </Tooltip>
141
+ </div>
142
+ <div className="text-xs text-muted-foreground">
143
+ {config.runtime.auto_instantiate ? "autorun" : "lazy"}
144
+ </div>
118
145
  </div>
119
146
  </div>
147
+ <Switch
148
+ checked={config.runtime.auto_instantiate}
149
+ onCheckedChange={handleStartupToggle}
150
+ size="sm"
151
+ />
120
152
  </div>
121
- <Switch
122
- checked={config.runtime.auto_instantiate}
123
- onCheckedChange={handleStartupToggle}
124
- size="sm"
125
- />
126
- </div>
127
- </DisableIfOverridden>
153
+ </DisableIfOverridden>
128
154
 
129
- <DropdownMenuSeparator />
155
+ <DropdownMenuSeparator />
130
156
 
131
- {/* On cell change toggle */}
132
- <DisableIfOverridden name="runtime.on_cell_change">
133
- <div className="flex items-center justify-between px-2 py-2">
134
- <div className="flex items-center space-x-2">
135
- {config.runtime.on_cell_change === "autorun" ? (
136
- <ZapIcon size={14} className="text-amber-500" />
137
- ) : (
138
- <ZapOffIcon size={14} className="text-muted-foreground" />
139
- )}
140
- <div>
141
- <div className="text-sm font-medium">On cell change</div>
142
- <div className="text-xs text-muted-foreground">
143
- {config.runtime.on_cell_change}
157
+ {/* On cell change toggle */}
158
+ <DisableIfOverridden name="runtime.on_cell_change">
159
+ <div className="flex items-center justify-between px-2 py-2">
160
+ <div className="flex items-center space-x-2">
161
+ {config.runtime.on_cell_change === "autorun" ? (
162
+ <ZapIcon size={14} className="text-amber-500" />
163
+ ) : (
164
+ <ZapOffIcon size={14} className="text-muted-foreground" />
165
+ )}
166
+ <div>
167
+ <div className="text-sm font-medium flex items-center gap-1">
168
+ On cell change
169
+ <Tooltip
170
+ content={
171
+ <div className="max-w-[300px]">
172
+ Whether to automatically run dependent cells after
173
+ running a cell
174
+ </div>
175
+ }
176
+ >
177
+ <InfoIcon className="w-3 h-3" />
178
+ </Tooltip>
179
+ </div>
180
+ <div className="text-xs text-muted-foreground">
181
+ {config.runtime.on_cell_change}
182
+ </div>
144
183
  </div>
145
184
  </div>
185
+ <Switch
186
+ checked={config.runtime.on_cell_change === "autorun"}
187
+ onCheckedChange={handleCellChangeToggle}
188
+ size="sm"
189
+ />
146
190
  </div>
147
- <Switch
148
- checked={config.runtime.on_cell_change === "autorun"}
149
- onCheckedChange={handleCellChangeToggle}
150
- size="sm"
151
- />
152
- </div>
153
- </DisableIfOverridden>
191
+ </DisableIfOverridden>
154
192
 
155
- {!isWasm() && (
156
- <>
157
- <DropdownMenuSeparator />
193
+ {!isWasm() && (
194
+ <>
195
+ <DropdownMenuSeparator />
158
196
 
159
- {/* On module change dropdown */}
160
- <DisableIfOverridden name="runtime.auto_reload">
161
- <div className="px-2 py-1">
162
- <div className="flex items-center space-x-2 mb-2">
163
- {config.runtime.auto_reload === "off" && (
164
- <PowerOffIcon size={14} className="text-muted-foreground" />
165
- )}
166
- {config.runtime.auto_reload === "lazy" && (
167
- <ZapOffIcon size={14} className="text-muted-foreground" />
168
- )}
169
- {config.runtime.auto_reload === "autorun" && (
170
- <ZapIcon size={14} className="text-amber-500" />
171
- )}
172
- <div>
173
- <div className="text-sm font-medium">On module change</div>
174
- <div className="text-xs text-muted-foreground">
175
- {config.runtime.auto_reload}
197
+ {/* On module change dropdown */}
198
+ <DisableIfOverridden name="runtime.auto_reload">
199
+ <div className="px-2 py-1">
200
+ <div className="flex items-center space-x-2 mb-2">
201
+ {config.runtime.auto_reload === "off" && (
202
+ <PowerOffIcon
203
+ size={14}
204
+ className="text-muted-foreground"
205
+ />
206
+ )}
207
+ {config.runtime.auto_reload === "lazy" && (
208
+ <ZapOffIcon size={14} className="text-muted-foreground" />
209
+ )}
210
+ {config.runtime.auto_reload === "autorun" && (
211
+ <ZapIcon size={14} className="text-amber-500" />
212
+ )}
213
+ <div>
214
+ <div className="text-sm font-medium flex items-center gap-1">
215
+ On module change
216
+ <Tooltip
217
+ content={
218
+ <div className="max-w-[300px]">
219
+ Whether to run affected cells, mark them as stale,
220
+ or do nothing when an external module is updated
221
+ </div>
222
+ }
223
+ >
224
+ <InfoIcon className="w-3 h-3" />
225
+ </Tooltip>
226
+ </div>
227
+ <div className="text-xs text-muted-foreground">
228
+ {config.runtime.auto_reload}
229
+ </div>
176
230
  </div>
177
231
  </div>
232
+ <div className="space-y-1">
233
+ {["off", "lazy", "autorun"].map((option) => (
234
+ <button
235
+ key={option}
236
+ onClick={() =>
237
+ handleModuleReloadChange(
238
+ option as "off" | "lazy" | "autorun",
239
+ )
240
+ }
241
+ className={cn(
242
+ "w-full flex items-center px-2 py-1 text-sm rounded hover:bg-accent",
243
+ option === config.runtime.auto_reload && "bg-accent",
244
+ )}
245
+ >
246
+ {option === "off" && (
247
+ <PowerOffIcon size={12} className="mr-2" />
248
+ )}
249
+ {option === "lazy" && (
250
+ <ZapOffIcon size={12} className="mr-2" />
251
+ )}
252
+ {option === "autorun" && (
253
+ <ZapIcon size={12} className="mr-2" />
254
+ )}
255
+ <span className="capitalize">{option}</span>
256
+ {option === config.runtime.auto_reload && (
257
+ <span className="ml-auto">✓</span>
258
+ )}
259
+ </button>
260
+ ))}
261
+ </div>
178
262
  </div>
179
- <div className="space-y-1">
180
- {["off", "lazy", "autorun"].map((option) => (
181
- <button
182
- key={option}
183
- onClick={() =>
184
- handleModuleReloadChange(
185
- option as "off" | "lazy" | "autorun",
186
- )
187
- }
188
- className={cn(
189
- "w-full flex items-center px-2 py-1 text-sm rounded hover:bg-accent",
190
- option === config.runtime.auto_reload && "bg-accent",
191
- )}
192
- >
193
- {option === "off" && (
194
- <PowerOffIcon size={12} className="mr-2" />
195
- )}
196
- {option === "lazy" && (
197
- <ZapOffIcon size={12} className="mr-2" />
198
- )}
199
- {option === "autorun" && (
200
- <ZapIcon size={12} className="mr-2" />
201
- )}
202
- <span className="capitalize">{option}</span>
203
- {option === config.runtime.auto_reload && (
204
- <span className="ml-auto">✓</span>
205
- )}
206
- </button>
207
- ))}
208
- </div>
209
- </div>
210
- </DisableIfOverridden>
211
- </>
212
- )}
263
+ </DisableIfOverridden>
264
+ </>
265
+ )}
266
+ </TooltipProvider>
213
267
  </DropdownMenuContent>
214
268
  </DropdownMenu>
215
269
  );
@@ -585,7 +585,7 @@ const EditableCellComponent = ({
585
585
  className={cn(
586
586
  className,
587
587
  navigationProps.className,
588
- "focus:ring-1 focus:ring-(--slate-7) focus:ring-offset-2",
588
+ "focus:ring-1 focus:ring-(--slate-8) focus:ring-offset-2",
589
589
  )}
590
590
  ref={cellContainerRef}
591
591
  {...cellDomProps(cellId, cellData.name)}
@@ -1032,16 +1032,12 @@ const SetupCellComponent = ({
1032
1032
  const hasConsoleOutput = cellRuntime.consoleOutputs.length > 0;
1033
1033
  const isErrorOutput = isErrorMime(cellRuntime.output?.mimetype);
1034
1034
 
1035
- const className = clsx(
1036
- "marimo-cell",
1037
- "hover-actions-parent z-10 border shadow-sm",
1038
- "border-(--blue-5)! rounded-sm!",
1039
- {
1040
- "needs-run": needsRun,
1041
- "has-error": cellRuntime.errored,
1042
- stopped: cellRuntime.stopped,
1043
- },
1044
- );
1035
+ const className = clsx("marimo-cell", "hover-actions-parent z-10", {
1036
+ interactive: true,
1037
+ "needs-run": needsRun,
1038
+ "has-error": cellRuntime.errored,
1039
+ stopped: cellRuntime.stopped,
1040
+ });
1045
1041
 
1046
1042
  const handleRefactorWithAI: OnRefactorWithAI = useEvent(
1047
1043
  (opts: { prompt: string; triggerImmediately: boolean }) => {
@@ -1076,7 +1072,7 @@ const SetupCellComponent = ({
1076
1072
  {...mergeProps(navigationProps, {
1077
1073
  className: cn(
1078
1074
  className,
1079
- "focus:ring-1 focus:ring-(--blue-7) focus:ring-offset-0",
1075
+ "focus:ring-1 focus:ring-(--slate-8) focus:ring-offset-2",
1080
1076
  ),
1081
1077
  onBlur: closeCompletionHandler,
1082
1078
  onKeyDown: resumeCompletionHandler,
@@ -1086,7 +1082,11 @@ const SetupCellComponent = ({
1086
1082
  tabIndex={-1}
1087
1083
  data-setup-cell={true}
1088
1084
  >
1089
- <div className={cn("tray")} data-hidden={!isCellCodeShown}>
1085
+ <div
1086
+ className={cn("tray")}
1087
+ data-has-output-above={false}
1088
+ data-hidden={!isCellCodeShown}
1089
+ >
1090
1090
  <StagedAICellBackground
1091
1091
  cellId={cellId}
1092
1092
  className="mo-ai-setup-cell"
@@ -12,6 +12,8 @@ describe("useFocusFirstEditor", () => {
12
12
  cb(0);
13
13
  return 0;
14
14
  });
15
+ // Mock document.hasFocus() to return true so focus logic runs
16
+ vi.spyOn(document, "hasFocus").mockReturnValue(true);
15
17
  });
16
18
 
17
19
  afterEach(() => {
@@ -120,4 +122,29 @@ describe("useFocusFirstEditor", () => {
120
122
  writable: true,
121
123
  });
122
124
  });
125
+
126
+ it("should not focus when document does not have focus", () => {
127
+ // Mock document.hasFocus() to return false (e.g., when embedded in iframe)
128
+ vi.spyOn(document, "hasFocus").mockReturnValue(false);
129
+
130
+ const mockEditor = { focus: vi.fn() };
131
+ vi.spyOn(cellsModule, "getNotebook").mockReturnValue({
132
+ cellIds: { iterateTopLevelIds: ["cell-1"] },
133
+ cellData: {
134
+ "cell-1": { config: { hide_code: false } },
135
+ },
136
+ cellHandles: {
137
+ "cell-1": { current: { editorView: mockEditor } },
138
+ },
139
+ } as unknown as cellsModule.NotebookState);
140
+
141
+ renderHook(() => useFocusFirstEditor());
142
+
143
+ act(() => {
144
+ vi.advanceTimersByTime(100);
145
+ });
146
+
147
+ // Focus should NOT be called when document doesn't have focus
148
+ expect(mockEditor.focus).not.toHaveBeenCalled();
149
+ });
123
150
  });
@@ -19,6 +19,12 @@ export function useFocusFirstEditor() {
19
19
  const timeout = setTimeout(() => {
20
20
  // Let the DOM render
21
21
  requestAnimationFrame(() => {
22
+ // Skip auto-focus if the document doesn't have focus to avoid
23
+ // stealing focus from outside (e.g., when embedded in an iframe)
24
+ if (!document.hasFocus()) {
25
+ return;
26
+ }
27
+
22
28
  // Check if the URL contains a scrollTo parameter
23
29
  const hash = window.location.hash;
24
30
  const cellName = extractCellNameFromHash(hash);
@@ -1,5 +1,9 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- import React, { type PropsWithChildren } from "react";
2
+ import React, {
3
+ Activity,
4
+ type ActivityProps,
5
+ type PropsWithChildren,
6
+ } from "react";
3
7
 
4
8
  interface Props {
5
9
  isOpen: boolean;
@@ -12,13 +16,30 @@ export const LazyMount: React.FC<PropsWithChildren<Props>> = ({
12
16
  isOpen,
13
17
  children,
14
18
  }) => {
15
- const [isMounted, setIsMounted] = React.useState(false);
19
+ const [hasMountedBefore, setHasMountedBefore] = React.useState(false);
16
20
 
17
- React.useEffect(() => {
18
- if (isOpen && !isMounted) {
19
- setIsMounted(true);
20
- }
21
- }, [isOpen, isMounted]);
21
+ if (isOpen && !hasMountedBefore) {
22
+ setHasMountedBefore(true);
23
+ }
22
24
 
23
- return isMounted ? children : null;
25
+ return hasMountedBefore || isOpen ? children : null;
26
+ };
27
+
28
+ /**
29
+ * Wraps a component in an Activity component. It is not mounted until it is open for the first time.
30
+ */
31
+ export const LazyActivity: React.FC<PropsWithChildren<ActivityProps>> = (
32
+ props,
33
+ ) => {
34
+ const [hasMountedBefore, setHasMountedBefore] = React.useState(false);
35
+
36
+ if (props.mode === "visible" && !hasMountedBefore) {
37
+ setHasMountedBefore(true);
38
+ }
39
+
40
+ if (hasMountedBefore) {
41
+ return <Activity {...props} />;
42
+ }
43
+
44
+ return null;
24
45
  };
@@ -12,6 +12,7 @@ import { getInitialAppMode } from "@/core/mode";
12
12
  import { CssVariables } from "@/theme/ThemeProvider";
13
13
  import { reactLazyWithPreload } from "@/utils/lazy";
14
14
  import { ErrorBoundary } from "../components/editor/boundary/ErrorBoundary";
15
+ import { KernelStartupErrorModal } from "../components/editor/KernelStartupErrorModal";
15
16
  import { ModalProvider } from "../components/modal/ImperativeModal";
16
17
  import { Toaster } from "../components/ui/toaster";
17
18
  import { TooltipProvider } from "../components/ui/tooltip";
@@ -91,6 +92,7 @@ const Providers = memo(({ children }: PropsWithChildren) => {
91
92
  {children}
92
93
  <Toaster />
93
94
  <TailwindIndicator />
95
+ <KernelStartupErrorModal />
94
96
  </ModalProvider>
95
97
  </LocaleProvider>
96
98
  </SlotzProvider>
@@ -1325,10 +1325,7 @@ const {
1325
1325
  };
1326
1326
  },
1327
1327
  addSetupCellIfDoesntExist: (state, action: { code?: string }) => {
1328
- let { code } = action;
1329
- if (code == null) {
1330
- code = "# Initialization code that runs before all other cells";
1331
- }
1328
+ const { code } = action;
1332
1329
 
1333
1330
  if (state.cellIds.setupCellExists()) {
1334
1331
  // Just focus on the existing setup cell
@@ -53,8 +53,9 @@ export function focusAndScrollCellIntoView({
53
53
  Logger.warn("scrollCellIntoView: editor not found", cellId);
54
54
  return;
55
55
  }
56
- // If already focused, do nothing.
57
- if (editor.hasFocus) {
56
+ // Skip auto-focus if already focused, or if the document doesn't have
57
+ // focus to avoid stealing focus from outside (e.g., when embedded in an iframe)
58
+ if (editor.hasFocus || !document.hasFocus()) {
58
59
  return;
59
60
  }
60
61
 
@@ -64,6 +64,7 @@ import { getCurrentLanguageAdapter } from "./language/commands";
64
64
  import { adaptiveLanguageConfiguration } from "./language/extension";
65
65
  import { dndBundle } from "./misc/dnd";
66
66
  import { pasteBundle } from "./misc/paste";
67
+ import { stringsAutoCloseBraces } from "./misc/string-braces";
67
68
  import { reactiveReferencesBundle } from "./reactive-references/extension";
68
69
  import { darkTheme } from "./theme/dark";
69
70
  import { lightTheme } from "./theme/light";
@@ -203,6 +204,7 @@ export const basicBundle = (opts: CodeMirrorSetupOpts): Extension[] => {
203
204
  hintTooltip(lspConfig),
204
205
  copilotBundle(completionConfig),
205
206
  foldGutter(),
207
+ stringsAutoCloseBraces(),
206
208
  closeBrackets(),
207
209
  completionKeymap(),
208
210
  // to avoid clash with charDeleteBackward keymap
@@ -756,6 +756,129 @@ describe("NotebookLanguageServerClient", () => {
756
756
  // Verify that the import cell was not changed
757
757
  expect(mockView1.state.doc.toString()).toBe("import marimo as mo");
758
758
  });
759
+
760
+ it("should only rename private variables in the current cell (issue #7810)", async () => {
761
+ const props = {
762
+ workspaceFolders: null,
763
+ capabilities: {
764
+ textDocument: {
765
+ rename: {
766
+ prepareSupport: true,
767
+ },
768
+ },
769
+ },
770
+ languageId: "python",
771
+ transport: {
772
+ sendData: vi.fn(),
773
+ subscribe: vi.fn(),
774
+ connect: vi.fn(),
775
+ transportRequestManager: {
776
+ send: vi.fn(),
777
+ },
778
+ } as any,
779
+ };
780
+
781
+ // Setup editor views - both cells have a private variable _x
782
+ const cell1Code = "_x = 1\nprint(_x)";
783
+ const cell2Code = "_x = 2\nprint(_x)";
784
+
785
+ const mockView1 = new EditorView({
786
+ doc: cell1Code,
787
+ extensions: [
788
+ languageAdapterState.init(() => new PythonLanguageAdapter()),
789
+ languageMetadataField.init(() => ({})),
790
+ languageServerWithClient({
791
+ client: mockClient as unknown as LanguageServerClient,
792
+ documentUri: CellDocumentUri.of(Cells.cell1),
793
+ ...props,
794
+ }),
795
+ ],
796
+ });
797
+
798
+ const mockView2 = new EditorView({
799
+ doc: cell2Code,
800
+ extensions: [
801
+ languageAdapterState.init(() => new PythonLanguageAdapter()),
802
+ languageMetadataField.init(() => ({})),
803
+ languageServerWithClient({
804
+ client: mockClient as unknown as LanguageServerClient,
805
+ documentUri: CellDocumentUri.of(Cells.cell2),
806
+ ...props,
807
+ }),
808
+ ],
809
+ });
810
+
811
+ (notebookClient as any).getNotebookEditors = () => ({
812
+ [Cells.cell1]: mockView1,
813
+ [Cells.cell2]: mockView2,
814
+ });
815
+
816
+ // Update the mock to return the correct codes
817
+ vi.spyOn(store, "get").mockImplementation((atom) => {
818
+ if (atom === topologicalCodesAtom) {
819
+ return {
820
+ cellIds: [Cells.cell1, Cells.cell2],
821
+ codes: {
822
+ [Cells.cell1]: cell1Code,
823
+ [Cells.cell2]: cell2Code,
824
+ },
825
+ };
826
+ }
827
+ return undefined;
828
+ });
829
+
830
+ // Setup rename params - renaming _x in cell1
831
+ const renameParams: LSP.RenameParams = {
832
+ textDocument: { uri: CellDocumentUri.of(Cells.cell1) },
833
+ position: { line: 0, character: 0 }, // position of '_x'
834
+ newName: "_y",
835
+ };
836
+
837
+ // Open a document first to set up the lens
838
+ await notebookClient.textDocumentDidOpen({
839
+ textDocument: {
840
+ uri: CellDocumentUri.of(Cells.cell1),
841
+ languageId: "python",
842
+ version: 1,
843
+ text: cell1Code,
844
+ },
845
+ });
846
+
847
+ // Mock the response from the language server
848
+ // The LSP server would rename _x in BOTH cells (since it sees the merged doc)
849
+ const mockRenameResponse: LSP.WorkspaceEdit = {
850
+ documentChanges: [
851
+ {
852
+ textDocument: {
853
+ uri: "file:///__marimo_notebook__.py",
854
+ version: 1,
855
+ },
856
+ edits: [
857
+ {
858
+ range: {
859
+ start: { line: 0, character: 0 },
860
+ end: { line: 3, character: 10 },
861
+ },
862
+ // LSP renames _x to _y in both cells
863
+ newText: "_y = 1\nprint(_y)\n_y = 2\nprint(_y)",
864
+ },
865
+ ],
866
+ },
867
+ ],
868
+ };
869
+
870
+ mockClient.textDocumentRename = vi
871
+ .fn()
872
+ .mockResolvedValue(mockRenameResponse);
873
+
874
+ // Call rename
875
+ await notebookClient.textDocumentRename(renameParams);
876
+
877
+ // The fix: only cell1 should be renamed, cell2 should remain unchanged
878
+ // because private variables are cell-local in marimo
879
+ expect(mockView1.state.doc.toString()).toBe("_y = 1\nprint(_y)");
880
+ expect(mockView2.state.doc.toString()).toBe("_x = 2\nprint(_x)");
881
+ });
759
882
  });
760
883
 
761
884
  describe("diagnostics handling", () => {