@marimo-team/frontend 0.19.3-dev8 → 0.19.4-dev0

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 (179) hide show
  1. package/dist/assets/{CellStatus-BwPGnX3z.js → CellStatus--kUu6N2K.js} +1 -1
  2. package/dist/assets/{ConnectedDataExplorerComponent-KlUs_Sz3.js → ConnectedDataExplorerComponent-BKJwCHu7.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--AuyEErr.js → JsonOutput-BSGE-MRo.js} +5 -5
  6. package/dist/assets/{LazyAnyLanguageCodeMirror-jpEDlD0M.js → LazyAnyLanguageCodeMirror-Cp2punaU.js} +2 -2
  7. package/dist/assets/{MarimoErrorOutput-BZjY8e2w.js → MarimoErrorOutput-CX0SCJOZ.js} +2 -2
  8. package/dist/assets/{RenderHTML-BTLaM20B.js → RenderHTML-Do_PVqRy.js} +1 -1
  9. package/dist/assets/VisuallyHidden-B9t3FhTP.js +1 -0
  10. package/dist/assets/{add-cell-with-ai-BWWVs9qV.js → add-cell-with-ai-manh7kBT.js} +21 -21
  11. package/dist/assets/{add-database-form-Bw_YRH1r.js → add-database-form-CgkV0MRs.js} +2 -2
  12. package/dist/assets/agent-panel-D-OmT-rw.js +287 -0
  13. package/dist/assets/{ai-model-dropdown-BrUOgnWS.js → ai-model-dropdown-DzyBY5VA.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-B8CXELx0.js → app-config-button-DC3alCuB.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-RHFZmO74.js +23 -0
  20. package/dist/assets/cell-link-Dqj_nfXA.js +1 -0
  21. package/dist/assets/{cells-DU3EySUd.js → cells-BNQUQiDS.js} +49 -49
  22. package/dist/assets/{chat-components-Bc9j9ls4.js → chat-components-CWiXtKu6.js} +1 -1
  23. package/dist/assets/{chat-display-BrTi6c8V.js → chat-display-CGnOamQG.js} +1 -1
  24. package/dist/assets/{chat-panel-8Dym5Gv3.js → chat-panel-Dh1M55c9.js} +2 -2
  25. package/dist/assets/client-CDjmJmVw.js +4 -0
  26. package/dist/assets/{column-preview-Ck6B_-sQ.js → column-preview-CKxT2s-S.js} +1 -1
  27. package/dist/assets/{command-B_minI8b.js → command-YPFTinLj.js} +1 -1
  28. package/dist/assets/{command-palette-BT3u6JBB.js → command-palette-7fVEhKGc.js} +1 -1
  29. package/dist/assets/common-DJkPpBxC.js +1 -0
  30. package/dist/assets/config-D6nhy4FA.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-DCvPlnaJ.js → datasource-DerBLc6V.js} +2 -2
  34. package/dist/assets/{dependency-graph-panel-C9jYZ6pA.js → dependency-graph-panel-Vd-OsVLa.js} +4 -4
  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-AsatrTfg.js → documentation-panel-xG2-zpwg.js} +1 -1
  38. package/dist/assets/{download-PR1bF3g_.js → download-B6EJS7Ar.js} +1 -1
  39. package/dist/assets/edit-page-7Hkti2j_.js +12 -0
  40. package/dist/assets/{error-banner-DU5Qb8a8.js → error-banner-DvT0IGDZ.js} +1 -1
  41. package/dist/assets/{error-panel-D_wVKV6I.js → error-panel-BxBpZYvt.js} +1 -1
  42. package/dist/assets/{es-CEE_7T0w.js → es-BoHEdemq.js} +1 -1
  43. package/dist/assets/{field-DDKGFzpC.js → field-Clr_fqUr.js} +1 -1
  44. package/dist/assets/{file-explorer-panel-DltK8JVp.js → file-explorer-panel-C9K0vIPl.js} +1 -1
  45. package/dist/assets/{floating-outline-BfdazXWm.js → floating-outline-DCrTuu2G.js} +1 -1
  46. package/dist/assets/{focus-CtlWIiQr.js → focus-DM53w5BH.js} +1 -1
  47. package/dist/assets/{form-Cy5TkLzh.js → form-BcKfhfZc.js} +2 -2
  48. package/dist/assets/{glide-data-editor-D_bRnWfy.js → glide-data-editor-CRb9AiCG.js} +1 -1
  49. package/dist/assets/{globals-BSLm1nlz.js → globals-Bf30kOQF.js} +1 -1
  50. package/dist/assets/{home-page-CEnaUutq.js → home-page-BRyNf7fl.js} +2 -2
  51. package/dist/assets/index-CBMqMxiq.js +43 -0
  52. package/dist/assets/index-DDc_1b-N.css +2 -0
  53. package/dist/assets/input-B80Yt1uu.js +1 -0
  54. package/dist/assets/{kiosk-mode-BnTZR6mM.js → kiosk-mode-P-NYHJID.js} +1 -1
  55. package/dist/assets/{label-qwandMoh.js → label-CNZLffHW.js} +1 -1
  56. package/dist/assets/{layout-BTiWDrbh.js → layout-DT91GUei.js} +4 -4
  57. package/dist/assets/links-D529u6GQ.js +1 -0
  58. package/dist/assets/{logs-panel-Cnp9tO_1.js → logs-panel-C2dfrRig.js} +1 -1
  59. package/dist/assets/{markdown-renderer-BSrbBHwX.js → markdown-renderer-BPnVa0ym.js} +2 -2
  60. package/dist/assets/{mermaid-BPkO79lo.js → mermaid--ZwxKP7u.js} +1 -1
  61. package/dist/assets/mode-Dq8MKjNR.js +1 -0
  62. package/dist/assets/{multi-map-fjX9ImVF.js → multi-map-CQd4MZr5.js} +1 -1
  63. package/dist/assets/name-cell-input-BaEPC7ON.js +1 -0
  64. package/dist/assets/{outline-panel-DCfj1bI-.js → outline-panel-Cca864H0.js} +1 -1
  65. package/dist/assets/{packages-panel-BiEckVdM.js → packages-panel-Cy_KAYmq.js} +1 -1
  66. package/dist/assets/{panels-BXRys72u.js → panels-BzlLZfye.js} +1 -1
  67. package/dist/assets/{process-output-wGlHkL-Q.js → process-output-Dn1rOp26.js} +1 -1
  68. package/dist/assets/{readonly-python-code-xbh7G2Y2.js → readonly-python-code-CXeF74Iq.js} +1 -1
  69. package/dist/assets/{renderShortcut-D0Pei-OA.js → renderShortcut-eU5Hsfml.js} +1 -1
  70. package/dist/assets/{run-page-BCwJRhCq.js → run-page-CM_n6pXD.js} +1 -1
  71. package/dist/assets/scratchpad-panel-XCkVY3Hp.js +1 -0
  72. package/dist/assets/{secrets-panel-CDWmmmBS.js → secrets-panel-BMY6PPth.js} +1 -1
  73. package/dist/assets/{select-D0g5GnIs.js → select-D9lTzMzP.js} +1 -1
  74. package/dist/assets/{session-panel-DuQl_oQp.js → session-panel-BDt6Y_mU.js} +1 -1
  75. package/dist/assets/{slides-component-MkPkpql1.js → slides-component-Dp0Yv5b0.js} +1 -1
  76. package/dist/assets/{snippets-panel-R_ql6HGu.js → snippets-panel-K-JKJQBf.js} +1 -1
  77. package/dist/assets/state-DWRZTH2y.js +1 -0
  78. package/dist/assets/state-JzO-Ni5T.js +1 -0
  79. package/dist/assets/{switch-CWzL-0WF.js → switch-RowEjq0T.js} +1 -1
  80. package/dist/assets/{terminal-BWM0fOMh.js → terminal-BhbNfCNw.js} +1 -1
  81. package/dist/assets/{textarea-CfvBt_Xm.js → textarea-Di1KKcL4.js} +1 -1
  82. package/dist/assets/{tracing-Kscqc1t3.js → tracing-nvbrZdpf.js} +1 -1
  83. package/dist/assets/{tracing-panel-BEzOflWc.js → tracing-panel-CTXJaO-A.js} +2 -2
  84. package/dist/assets/{types-DhuSHMNQ.js → types-CT2U5Ljy.js} +1 -1
  85. package/dist/assets/{useAddCell-C9lbOVO1.js → useAddCell-COb93CUl.js} +1 -1
  86. package/dist/assets/{useBoolean-B-A0dyIW.js → useBoolean-B_S7yTZz.js} +1 -1
  87. package/dist/assets/{useCellActionButton-fsh9MTAX.js → useCellActionButton-D5Zt1dDz.js} +1 -1
  88. package/dist/assets/{useDateFormatter-CV0QXb5P.js → useDateFormatter-DsANziQR.js} +1 -1
  89. package/dist/assets/useDeleteCell-DHF_xvAh.js +1 -0
  90. package/dist/assets/{useDependencyPanelTab-CngFbla0.js → useDependencyPanelTab-D59iW_MD.js} +1 -1
  91. package/dist/assets/useInterval-BGPIviJp.js +1 -0
  92. package/dist/assets/{useNotebookActions-D01w160c.js → useNotebookActions-DEl-rH-3.js} +1 -1
  93. package/dist/assets/{useNumberFormatter-D8ks3oPN.js → useNumberFormatter-FoXhpyAb.js} +1 -1
  94. package/dist/assets/usePress-DTwIUo40.js +7 -0
  95. package/dist/assets/useRunCells-CKEmgeKM.js +1 -0
  96. package/dist/assets/useSplitCell-D9YiO-z5.js +1 -0
  97. package/dist/assets/{useTheme-DfP1CWaW.js → useTheme-CNj0G_ol.js} +1 -1
  98. package/dist/assets/utilities.esm-DG4qccZc.js +3 -0
  99. package/dist/assets/utils-pfqq9IdB.js +1 -0
  100. package/dist/assets/{vega-component-B8ghmMYW.js → vega-component-C1voDf5W.js} +1 -1
  101. package/dist/assets/{write-secret-modal-CLm48gMe.js → write-secret-modal-hOetwavI.js} +1 -1
  102. package/dist/index.html +56 -56
  103. package/package.json +5 -5
  104. package/src/__mocks__/requests.ts +1 -0
  105. package/src/__tests__/mount.test.ts +128 -0
  106. package/src/components/app-config/__tests__/get-dirty-values.test.ts +1 -1
  107. package/src/components/app-config/ai-config.tsx +328 -28
  108. package/src/components/app-config/user-config-form.tsx +10 -3
  109. package/src/components/chat/acp/agent-panel.tsx +56 -43
  110. package/src/components/chat/chat-utils.ts +0 -19
  111. package/src/components/data-table/column-header.tsx +1 -1
  112. package/src/components/editor/KernelStartupErrorModal.tsx +2 -2
  113. package/src/components/editor/actions/name-cell-input.tsx +10 -4
  114. package/src/components/editor/ai/completion-handlers.tsx +1 -1
  115. package/src/components/editor/alerts/connecting-alert.tsx +33 -6
  116. package/src/components/editor/chrome/types.ts +2 -4
  117. package/src/components/editor/chrome/wrapper/app-chrome.tsx +55 -58
  118. package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +150 -96
  119. package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +27 -0
  120. package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +6 -0
  121. package/src/components/utils/lazy-mount.tsx +29 -8
  122. package/src/core/ai/ids/ids.ts +12 -4
  123. package/src/core/cells/cells.ts +2 -0
  124. package/src/core/cells/scrollCellIntoView.ts +3 -2
  125. package/src/core/codemirror/cm.ts +2 -0
  126. package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +123 -0
  127. package/src/core/codemirror/lsp/notebook-lsp.ts +44 -4
  128. package/src/core/codemirror/misc/__tests__/string-braces.test.ts +200 -0
  129. package/src/core/codemirror/misc/string-braces.ts +37 -0
  130. package/src/core/config/__tests__/config-schema.test.ts +36 -0
  131. package/src/core/config/config-schema.ts +1 -0
  132. package/src/core/export/__tests__/hooks.test.ts +504 -0
  133. package/src/core/export/hooks.ts +93 -4
  134. package/src/core/islands/bridge.ts +1 -0
  135. package/src/core/kernel/__tests__/handlers.test.ts +2 -2
  136. package/src/core/kernel/state.ts +1 -0
  137. package/src/core/network/__tests__/requests-lazy.test.ts +1 -1
  138. package/src/core/network/__tests__/requests-network.test.ts +0 -18
  139. package/src/core/network/requests-lazy.ts +3 -2
  140. package/src/core/network/requests-network.ts +10 -7
  141. package/src/core/network/requests-static.ts +1 -0
  142. package/src/core/network/requests-toasting.tsx +1 -0
  143. package/src/core/network/types.ts +2 -0
  144. package/src/core/wasm/bridge.ts +1 -0
  145. package/src/css/globals.css +2 -0
  146. package/src/hooks/__tests__/useInterval.test.tsx +104 -0
  147. package/src/hooks/useInterval.ts +32 -6
  148. package/src/mount.tsx +6 -0
  149. package/src/plugins/impl/chat/ChatPlugin.tsx +2 -4
  150. package/src/plugins/impl/chat/chat-ui.tsx +62 -191
  151. package/src/plugins/impl/chat/types.ts +5 -12
  152. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +3 -1
  153. package/src/utils/events.ts +1 -0
  154. package/dist/assets/VisuallyHidden-BodIky8L.js +0 -1
  155. package/dist/assets/agent-panel-Bm-vW8YL.js +0 -287
  156. package/dist/assets/button-DuYGqRtX.js +0 -1
  157. package/dist/assets/cell-editor-Cdtc1m3g.js +0 -23
  158. package/dist/assets/cell-link-CFAPzUg5.js +0 -1
  159. package/dist/assets/client-DfkWorYM.js +0 -4
  160. package/dist/assets/common-jorbwXZC.js +0 -1
  161. package/dist/assets/config-Ba3eeYri.js +0 -1
  162. package/dist/assets/context-BAYdLMF_.js +0 -1
  163. package/dist/assets/edit-page-B1Ed6RKp.js +0 -12
  164. package/dist/assets/index-DNg7_e7t.js +0 -43
  165. package/dist/assets/index-__6MNWbe.css +0 -2
  166. package/dist/assets/input-CaEtLL8p.js +0 -1
  167. package/dist/assets/links-Bpd4gqTj.js +0 -1
  168. package/dist/assets/mode-yhfN-4ye.js +0 -1
  169. package/dist/assets/name-cell-input-CmuWqgFR.js +0 -1
  170. package/dist/assets/scratchpad-panel-C6PpCYtK.js +0 -1
  171. package/dist/assets/state-DEHWsmkM.js +0 -1
  172. package/dist/assets/state-DXAf-ejz.js +0 -1
  173. package/dist/assets/useDeleteCell-ByImoTpm.js +0 -1
  174. package/dist/assets/useInterval-DpipYmgs.js +0 -1
  175. package/dist/assets/usePress-C2LPFxyv.js +0 -7
  176. package/dist/assets/useRunCells-CmnSPQtM.js +0 -1
  177. package/dist/assets/useSplitCell-BTH64tve.js +0 -1
  178. package/dist/assets/utilities.esm-CMQs6YPp.js +0 -3
  179. package/dist/assets/utils-CJJIceVn.js +0 -1
@@ -593,7 +593,7 @@ const PopoverFilterByValues = <TData, TValue>({
593
593
  <>
594
594
  <Command className="text-sm outline-hidden" shouldFilter={false}>
595
595
  <CommandInput
596
- placeholder="Search"
596
+ placeholder={`Search among the top ${data.length} values`}
597
597
  autoFocus={true}
598
598
  onValueChange={(value) => setQuery(value.trim())}
599
599
  />
@@ -65,7 +65,7 @@ export const KernelStartupErrorModal: React.FC = () => {
65
65
  manager can't install your notebook's dependencies.
66
66
  </AlertDialogDescription>
67
67
  </AlertDialogHeader>
68
- <div className="my-4">
68
+ <div className="my-4 overflow-hidden">
69
69
  <div className="flex items-center justify-between mb-2">
70
70
  <span className="text-sm font-medium text-muted-foreground">
71
71
  Error Details
@@ -80,7 +80,7 @@ export const KernelStartupErrorModal: React.FC = () => {
80
80
  Copy
81
81
  </Button>
82
82
  </div>
83
- <pre className="bg-muted p-4 rounded-md text-sm font-mono overflow-auto max-h-80 whitespace-pre-wrap break-words">
83
+ <pre className="bg-muted p-4 rounded-md text-sm font-mono overflow-auto max-h-80">
84
84
  {error}
85
85
  </pre>
86
86
  </div>
@@ -93,11 +93,17 @@ export const NameCellContentEditable: React.FC<{
93
93
  onChange={inputProps.onChange}
94
94
  onBlur={inputProps.onBlur}
95
95
  onFocus={inputProps.onFocus}
96
- onKeyDown={Events.onEnter((e) => {
97
- if (e.target instanceof HTMLElement) {
98
- e.target.blur();
96
+ onKeyDown={(e) => {
97
+ // Prevent all key presses from triggering hotkeys
98
+ e.stopPropagation();
99
+
100
+ // On Enter, blur the input to commit the change
101
+ if (e.key === "Enter") {
102
+ if (e.target instanceof HTMLElement) {
103
+ e.target.blur();
104
+ }
99
105
  }
100
- })}
106
+ }}
101
107
  >
102
108
  {value}
103
109
  </span>
@@ -124,7 +124,7 @@ export const AcceptCompletionButton: React.FC<{
124
124
  size={size}
125
125
  disabled={isLoading}
126
126
  onClick={handleAcceptAndRun}
127
- className={`${baseClasses} rounded-l-none px-1.5 ${borderless && "border-0 border-l-1"} ${playButtonStyles}`}
127
+ className={`${baseClasses} rounded-l-none px-1.5 ${borderless && "border-0 border-l"} ${playButtonStyles}`}
128
128
  >
129
129
  <PlayIcon className="h-2.5 w-2.5" />
130
130
  </Button>
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useAtomValue } from "jotai";
4
4
  import { LoadingEllipsis } from "@/components/icons/loading-ellipsis";
5
+ import { Spinner } from "@/components/icons/spinner";
5
6
  import { Button } from "@/components/ui/button";
6
7
  import { DelayMount } from "@/components/utils/delay-mount";
7
8
  import {
@@ -10,24 +11,50 @@ import {
10
11
  isNotStartedAtom,
11
12
  } from "@/core/network/connection";
12
13
  import { useConnectToRuntime } from "@/core/runtime/config";
14
+ import { Banner } from "@/plugins/impl/common/error-banner";
13
15
  import { Tooltip } from "../../ui/tooltip";
14
16
  import { FloatingAlert } from "./floating-alert";
15
17
 
16
18
  const SHORT_DELAY_MS = 1000; // 1 second
19
+ const LONG_DELAY_MS = 5000; // 5 seconds
17
20
 
18
21
  export const ConnectingAlert: React.FC = () => {
19
22
  const isConnecting = useAtomValue(isConnectingAtom);
20
23
  const isClosed = useAtomValue(isClosedAtom);
21
24
 
22
25
  if (isConnecting) {
26
+ const subtleNotification = (
27
+ <Tooltip content="Connecting to a marimo runtime">
28
+ <div className="flex items-center">
29
+ <LoadingEllipsis size={5} className="text-yellow-500" />
30
+ </div>
31
+ </Tooltip>
32
+ );
33
+
34
+ const longNotification = (
35
+ <Banner
36
+ kind="info"
37
+ className="flex flex-col rounded py-2 px-4 animate-in slide-in-from-top w-fit"
38
+ >
39
+ <div className="flex flex-col gap-4 justify-between items-start text-muted-foreground text-base">
40
+ <div className="flex items-center gap-2">
41
+ <Spinner className="h-4 w-4" />
42
+ <p>Connecting to a marimo runtime ...</p>
43
+ </div>
44
+ </div>
45
+ </Banner>
46
+ );
47
+
48
+ // This waits for 1 second to show the subtle notification, then shows the long notification after 5 seconds.
23
49
  return (
24
50
  <DelayMount milliseconds={SHORT_DELAY_MS}>
25
- <div className="absolute top-4 m-0 flex items-center min-h-[28px] left-1/2 transform -translate-x-1/2 z-200 ">
26
- <Tooltip content="Connecting to a marimo runtime">
27
- <div className="flex items-center">
28
- <LoadingEllipsis size={5} className="text-yellow-500" />
29
- </div>
30
- </Tooltip>
51
+ <div className="m-0 flex items-center min-h-[28px] fixed top-5 left-1/2 transform -translate-x-1/2 z-200">
52
+ <DelayMount
53
+ milliseconds={LONG_DELAY_MS}
54
+ fallback={subtleNotification}
55
+ >
56
+ {longNotification}
57
+ </DelayMount>
31
58
  </div>
32
59
  </DelayMount>
33
60
  );
@@ -194,10 +194,8 @@ export function isPanelHidden(
194
194
  if (panel.hidden) {
195
195
  return true;
196
196
  }
197
- if (panel.requiredCapability) {
198
- if (!capabilities[panel.requiredCapability]) {
199
- return true;
200
- }
197
+ if (panel.requiredCapability && !capabilities[panel.requiredCapability]) {
198
+ return true;
201
199
  }
202
200
  return false;
203
201
  }
@@ -20,7 +20,7 @@ import { XIcon } from "lucide-react";
20
20
  import { Button } from "@/components/ui/button";
21
21
  import { ReorderableList } from "@/components/ui/reorderable-list";
22
22
  import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
23
- import { LazyMount } from "@/components/utils/lazy-mount";
23
+ import { LazyActivity } from "@/components/utils/lazy-mount";
24
24
  import { cellErrorCount } from "@/core/cells/cells";
25
25
  import { capabilitiesAtom } from "@/core/config/capabilities";
26
26
  import { getFeatureFlag } from "@/core/config/feature-flag";
@@ -34,6 +34,7 @@ import {
34
34
  PANEL_MAP,
35
35
  PANELS,
36
36
  type PanelDescriptor,
37
+ type PanelType,
37
38
  } from "../types";
38
39
  import { BackendConnectionStatus } from "./footer-items/backend-status";
39
40
  import { PanelsWrapper } from "./panels";
@@ -253,6 +254,29 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
253
254
  return <LazyChatPanel />;
254
255
  };
255
256
 
257
+ const SIDEBAR_PANELS: Record<PanelType, React.ReactNode> = {
258
+ files: <LazyFileExplorerPanel />,
259
+ variables: <LazySessionPanel />,
260
+ dependencies: <LazyDependencyGraphPanel />,
261
+ packages: <LazyPackagesPanel />,
262
+ outline: <LazyOutlinePanel />,
263
+ documentation: <LazyDocumentationPanel />,
264
+ snippets: <LazySnippetsPanel />,
265
+ ai: renderAiPanel(),
266
+ errors: <LazyErrorsPanel />,
267
+ scratchpad: <LazyScratchpadPanel />,
268
+ tracing: <LazyTracingPanel />,
269
+ secrets: <LazySecretsPanel />,
270
+ logs: <LazyLogsPanel />,
271
+ terminal: (
272
+ <LazyTerminal
273
+ visible={isSidebarOpen && selectedPanel === "terminal"}
274
+ onClose={() => setIsSidebarOpen(false)}
275
+ />
276
+ ),
277
+ cache: <LazyCachePanel />,
278
+ };
279
+
256
280
  const helpPaneBody = (
257
281
  <ErrorBoundary>
258
282
  <PanelSectionProvider value="sidebar">
@@ -328,26 +352,14 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
328
352
  </div>
329
353
  <Suspense>
330
354
  <TooltipProvider>
331
- {selectedPanel === "files" && <LazyFileExplorerPanel />}
332
- {selectedPanel === "variables" && <LazySessionPanel />}
333
- {selectedPanel === "dependencies" && <LazyDependencyGraphPanel />}
334
- {selectedPanel === "packages" && <LazyPackagesPanel />}
335
- {selectedPanel === "outline" && <LazyOutlinePanel />}
336
- {selectedPanel === "documentation" && <LazyDocumentationPanel />}
337
- {selectedPanel === "snippets" && <LazySnippetsPanel />}
338
- {selectedPanel === "ai" && renderAiPanel()}
339
- {selectedPanel === "errors" && <LazyErrorsPanel />}
340
- {selectedPanel === "scratchpad" && <LazyScratchpadPanel />}
341
- {selectedPanel === "tracing" && <LazyTracingPanel />}
342
- {selectedPanel === "secrets" && <LazySecretsPanel />}
343
- {selectedPanel === "logs" && <LazyLogsPanel />}
344
- {selectedPanel === "terminal" && (
345
- <LazyTerminal
346
- visible={isSidebarOpen}
347
- onClose={() => setIsSidebarOpen(false)}
348
- />
349
- )}
350
- {selectedPanel === "cache" && <LazyCachePanel />}
355
+ {Object.entries(SIDEBAR_PANELS).map(([key, Panel]) => (
356
+ <LazyActivity
357
+ key={key}
358
+ mode={selectedPanel === key ? "visible" : "hidden"}
359
+ >
360
+ {Panel}
361
+ </LazyActivity>
362
+ ))}
351
363
  </TooltipProvider>
352
364
  </Suspense>
353
365
  </div>
@@ -388,6 +400,18 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
388
400
  </Panel>
389
401
  );
390
402
 
403
+ const DEVELOPER_PANELS: Record<PanelType, React.ReactNode> = {
404
+ ...SIDEBAR_PANELS,
405
+ terminal: (
406
+ <LazyTerminal
407
+ visible={
408
+ isDeveloperPanelOpen && selectedDeveloperPanelTab === "terminal"
409
+ }
410
+ onClose={() => setIsDeveloperPanelOpen(false)}
411
+ />
412
+ ),
413
+ };
414
+
391
415
  const bottomPanel = (
392
416
  <Panel
393
417
  ref={developerPanelRef}
@@ -475,43 +499,16 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
475
499
  <Suspense fallback={<div />}>
476
500
  <PanelSectionProvider value="developer-panel">
477
501
  <div className="flex-1 overflow-hidden">
478
- {selectedDeveloperPanelTab === "files" && (
479
- <LazyFileExplorerPanel />
480
- )}
481
- {selectedDeveloperPanelTab === "variables" && (
482
- <LazySessionPanel />
483
- )}
484
- {selectedDeveloperPanelTab === "dependencies" && (
485
- <LazyDependencyGraphPanel />
486
- )}
487
- {selectedDeveloperPanelTab === "packages" && (
488
- <LazyPackagesPanel />
489
- )}
490
- {selectedDeveloperPanelTab === "outline" && <LazyOutlinePanel />}
491
- {selectedDeveloperPanelTab === "documentation" && (
492
- <LazyDocumentationPanel />
493
- )}
494
- {selectedDeveloperPanelTab === "snippets" && (
495
- <LazySnippetsPanel />
496
- )}
497
- {selectedDeveloperPanelTab === "ai" && renderAiPanel()}
498
- {selectedDeveloperPanelTab === "errors" && <LazyErrorsPanel />}
499
- {selectedDeveloperPanelTab === "scratchpad" && (
500
- <LazyScratchpadPanel />
501
- )}
502
- {selectedDeveloperPanelTab === "tracing" && <LazyTracingPanel />}
503
- {selectedDeveloperPanelTab === "secrets" && <LazySecretsPanel />}
504
- {selectedDeveloperPanelTab === "logs" && <LazyLogsPanel />}
505
- {/* LazyMount needed for Terminal to avoid spurious connection */}
506
- {selectedDeveloperPanelTab === "terminal" && (
507
- <LazyMount isOpen={isDeveloperPanelOpen}>
508
- <LazyTerminal
509
- visible={isDeveloperPanelOpen}
510
- onClose={() => setIsDeveloperPanelOpen(false)}
511
- />
512
- </LazyMount>
513
- )}
514
- {selectedDeveloperPanelTab === "cache" && <LazyCachePanel />}
502
+ {Object.entries(DEVELOPER_PANELS).map(([key, Panel]) => (
503
+ <LazyActivity
504
+ key={key}
505
+ mode={
506
+ selectedDeveloperPanelTab === key ? "visible" : "hidden"
507
+ }
508
+ >
509
+ {Panel}
510
+ </LazyActivity>
511
+ ))}
515
512
  </div>
516
513
  </PanelSectionProvider>
517
514
  </Suspense>
@@ -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
  );
@@ -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);