@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
@@ -677,7 +677,7 @@ const AgentPanel: React.FC = () => {
677
677
  ? getAgentWebSocketUrl(selectedTab.agentId)
678
678
  : NO_WS_SET;
679
679
  const { sendUpdateFile, sendFileDetails } = useRequestClient();
680
- const isCreatingNewSession = useRef(false);
680
+ const creatingOrResumingSession = useRef(false);
681
681
 
682
682
  const acpClient = useAcpClient({
683
683
  wsUrl,
@@ -716,6 +716,7 @@ const AgentPanel: React.FC = () => {
716
716
  sessionMode,
717
717
  activeSessionId,
718
718
  agent,
719
+ clearNotifications,
719
720
  } = acpClient;
720
721
 
721
722
  useEffect(() => {
@@ -757,40 +758,42 @@ const AgentPanel: React.FC = () => {
757
758
  return;
758
759
  }
759
760
 
760
- // If there is an active session, we should stop it
761
- if (activeSessionId) {
762
- setActiveSessionId(null);
763
- await agent.cancel({ sessionId: activeSessionId }).catch((error) => {
764
- logger.error("Failed to cancel active session", { error });
765
- });
766
- }
761
+ creatingOrResumingSession.current = true;
762
+
763
+ try {
764
+ // If there is an active session, we should stop it
765
+ if (activeSessionId) {
766
+ await agent.cancel({ sessionId: activeSessionId }).catch((error) => {
767
+ logger.error("Failed to cancel active session", { error });
768
+ });
769
+ clearNotifications(activeSessionId);
770
+ setActiveSessionId(null);
771
+ }
767
772
 
768
- // Get the selected model from the current session state
769
- const currentModel = selectedTab?.selectedModel ?? null;
770
- logger.debug("Creating new agent session", { model: currentModel });
771
- isCreatingNewSession.current = true;
772
- const newSession = await agent
773
- .newSession({
773
+ // Get the selected model from the current session state
774
+ const currentModel = selectedTab?.selectedModel ?? null;
775
+ logger.debug("Creating new agent session", { model: currentModel });
776
+ const newSession = await agent.newSession({
774
777
  cwd: getCwd(),
775
778
  mcpServers: [],
776
779
  _meta: currentModel ? { model: currentModel } : undefined,
777
- })
778
- .finally(() => {
779
- isCreatingNewSession.current = false;
780
780
  });
781
781
 
782
- // Capture models from the response
783
- if (newSession.models) {
784
- logger.debug("Session models received", { models: newSession.models });
785
- setSessionModels(newSession.models);
786
- }
782
+ // Capture models from the response
783
+ if (newSession.models) {
784
+ logger.debug("Session models received", { models: newSession.models });
785
+ setSessionModels(newSession.models);
786
+ }
787
787
 
788
- setSessionState((prev) =>
789
- updateSessionExternalAgentSessionId(
790
- prev,
791
- newSession.sessionId as ExternalAgentSessionId,
792
- ),
793
- );
788
+ setSessionState((prev) =>
789
+ updateSessionExternalAgentSessionId(
790
+ prev,
791
+ newSession.sessionId as ExternalAgentSessionId,
792
+ ),
793
+ );
794
+ } finally {
795
+ creatingOrResumingSession.current = false;
796
+ }
794
797
  });
795
798
 
796
799
  const handleResumeSession = useEvent(
@@ -804,23 +807,28 @@ const AgentPanel: React.FC = () => {
804
807
  if (!agent.loadSession) {
805
808
  throw new Error("Agent does not support loading sessions");
806
809
  }
807
- const loadedSession = await agent.loadSession({
808
- sessionId: previousSessionId,
809
- cwd: getCwd(),
810
- mcpServers: [],
811
- });
812
-
813
- // Capture models from the response if available
814
- if (loadedSession?.models) {
815
- logger.debug("Session models received", {
816
- models: loadedSession.models,
810
+ creatingOrResumingSession.current = true;
811
+ try {
812
+ const loadedSession = await agent.loadSession({
813
+ sessionId: previousSessionId,
814
+ cwd: getCwd(),
815
+ mcpServers: [],
817
816
  });
818
- setSessionModels(loadedSession.models);
819
- }
820
817
 
821
- setSessionState((prev) =>
822
- updateSessionExternalAgentSessionId(prev, previousSessionId),
823
- );
818
+ // Capture models from the response if available
819
+ if (loadedSession?.models) {
820
+ logger.debug("Session models received", {
821
+ models: loadedSession.models,
822
+ });
823
+ setSessionModels(loadedSession.models);
824
+ }
825
+
826
+ setSessionState((prev) =>
827
+ updateSessionExternalAgentSessionId(prev, previousSessionId),
828
+ );
829
+ } finally {
830
+ creatingOrResumingSession.current = false;
831
+ }
824
832
  },
825
833
  );
826
834
 
@@ -838,6 +846,11 @@ const AgentPanel: React.FC = () => {
838
846
  return;
839
847
  }
840
848
 
849
+ // Prevent race conditions
850
+ if (creatingOrResumingSession.current) {
851
+ return;
852
+ }
853
+
841
854
  // If there is an available session, resume it, otherwise create a new one
842
855
  const createOrResumeSession = async () => {
843
856
  const availableSession = tabLastActiveSessionId ?? activeSessionId;
@@ -0,0 +1,101 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { useAtom } from "jotai";
4
+ import { CopyIcon, HomeIcon, XCircleIcon } from "lucide-react";
5
+ import { kernelStartupErrorAtom } from "@/core/errors/state";
6
+ import {
7
+ AlertDialog,
8
+ AlertDialogAction,
9
+ AlertDialogContent,
10
+ AlertDialogDescription,
11
+ AlertDialogFooter,
12
+ AlertDialogHeader,
13
+ AlertDialogTitle,
14
+ } from "../ui/alert-dialog";
15
+ import { Button } from "../ui/button";
16
+ import { toast } from "../ui/use-toast";
17
+
18
+ /**
19
+ * Modal that displays kernel startup errors.
20
+ * Shows when the kernel fails to start in sandbox mode,
21
+ * displaying the stderr output so users can diagnose the issue.
22
+ */
23
+ export const KernelStartupErrorModal: React.FC = () => {
24
+ const [error, setError] = useAtom(kernelStartupErrorAtom);
25
+
26
+ if (error === null) {
27
+ return null;
28
+ }
29
+
30
+ const handleCopy = async () => {
31
+ try {
32
+ await navigator.clipboard.writeText(error);
33
+ toast({
34
+ title: "Copied to clipboard",
35
+ description: "Error details have been copied to your clipboard.",
36
+ });
37
+ } catch {
38
+ toast({
39
+ title: "Failed to copy",
40
+ description: "Could not copy to clipboard.",
41
+ variant: "danger",
42
+ });
43
+ }
44
+ };
45
+
46
+ const handleClose = () => {
47
+ setError(null);
48
+ };
49
+
50
+ const handleReturnHome = () => {
51
+ const withoutSearch = document.baseURI.split("?")[0];
52
+ window.open(withoutSearch, "_self");
53
+ };
54
+
55
+ return (
56
+ <AlertDialog open={true} onOpenChange={(open) => !open && handleClose()}>
57
+ <AlertDialogContent className="max-w-2xl">
58
+ <AlertDialogHeader>
59
+ <AlertDialogTitle className="flex items-center gap-2 text-destructive">
60
+ <XCircleIcon className="h-5 w-5" />
61
+ Kernel Startup Failed
62
+ </AlertDialogTitle>
63
+ <AlertDialogDescription>
64
+ The kernel failed to start. This usually happens when the package
65
+ manager can't install your notebook's dependencies.
66
+ </AlertDialogDescription>
67
+ </AlertDialogHeader>
68
+ <div className="my-4">
69
+ <div className="flex items-center justify-between mb-2">
70
+ <span className="text-sm font-medium text-muted-foreground">
71
+ Error Details
72
+ </span>
73
+ <Button
74
+ variant="outline"
75
+ size="xs"
76
+ onClick={handleCopy}
77
+ className="flex items-center gap-1"
78
+ >
79
+ <CopyIcon className="h-3 w-3" />
80
+ Copy
81
+ </Button>
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">
84
+ {error}
85
+ </pre>
86
+ </div>
87
+ <AlertDialogFooter>
88
+ <Button
89
+ variant="outline"
90
+ onClick={handleReturnHome}
91
+ className="flex items-center gap-2"
92
+ >
93
+ <HomeIcon className="h-4 w-4" />
94
+ Return to Home
95
+ </Button>
96
+ <AlertDialogAction onClick={handleClose}>Dismiss</AlertDialogAction>
97
+ </AlertDialogFooter>
98
+ </AlertDialogContent>
99
+ </AlertDialog>
100
+ );
101
+ };
@@ -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>
@@ -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>