@marimo-team/islands 0.23.10-dev22 → 0.23.10-dev24

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 (40) hide show
  1. package/dist/{ConnectedDataExplorerComponent-DdeG-Hi-.js → ConnectedDataExplorerComponent-DmBropAy.js} +11 -11
  2. package/dist/{ErrorBoundary-rULOrC_p.js → ErrorBoundary-DpbaKVv7.js} +1 -1
  3. package/dist/{any-language-editor-CiES2a2h.js → any-language-editor-QDDrgvfh.js} +4 -4
  4. package/dist/{chat-ui-BTobdMRF.js → chat-ui-BrKSZ7Yu.js} +15 -15
  5. package/dist/{check-DTbrK0zt.js → check-BCaJeT-J.js} +1 -1
  6. package/dist/{code-visibility-PzFbkUPt.js → code-visibility-CVmFerQM.js} +18 -18
  7. package/dist/{copy-5jQ_kGE1.js → copy-UqRYxiOg.js} +1 -1
  8. package/dist/{dist-C1BYNeCR.js → dist-Dk6PV_d3.js} +10 -10
  9. package/dist/{error-banner-5bz0L9hS.js → error-banner-Cc0I3C9e.js} +1 -1
  10. package/dist/{esm-CCuYCd3R.js → esm-DzhtSSSq.js} +1 -1
  11. package/dist/{extends-CkydH1Q5.js → extends-C3j0Pbh9.js} +3 -3
  12. package/dist/{formats-DHxc-FdY.js → formats-C4wO47tk.js} +1 -1
  13. package/dist/{glide-data-editor-CRvL2R9l.js → glide-data-editor-Qhu8oCX-.js} +8 -8
  14. package/dist/{html-to-image-CjsdUYrb.js → html-to-image-D5fIgQg_.js} +13 -13
  15. package/dist/{input-DVkbXbIX.js → input-CMYy4hzj.js} +5 -5
  16. package/dist/{label-LWtdw5i8.js → label-CC4ytI1X.js} +1 -1
  17. package/dist/main.js +29 -29
  18. package/dist/{mermaid-lXOw5Py9.js → mermaid-zuLgJ8J8.js} +4 -4
  19. package/dist/{process-output-CI8a-CUx.js → process-output-B59yoBQx.js} +3 -3
  20. package/dist/{reveal-component-DEUT00rM.js → reveal-component-bghJ00sb.js} +561 -560
  21. package/dist/{spec-DMRQmLOc.js → spec-X7FwLJni.js} +4 -4
  22. package/dist/{strings-GCJA9n6d.js → strings-J57tzLr3.js} +22 -22
  23. package/dist/style.css +1 -1
  24. package/dist/{toDate-x-WRDCH7.js → toDate-d8RCRrRd.js} +2 -2
  25. package/dist/{tooltip-C5FYOpQc.js → tooltip-DpcyNkQ2.js} +2 -2
  26. package/dist/{types-CVvp1fKr.js → types-ChtMFmZ2.js} +1 -1
  27. package/dist/{useAsyncData-iRgKDT5s.js → useAsyncData-PonK__yh.js} +1 -1
  28. package/dist/{useDateFormatter-BRcO_TGJ.js → useDateFormatter-QB-3MpYr.js} +2 -2
  29. package/dist/{useDeepCompareMemoize-CkQ57VS2.js → useDeepCompareMemoize-D3NGWke6.js} +1 -1
  30. package/dist/{useLifecycle-BBO9PIph.js → useLifecycle-00mO3OSS.js} +2 -2
  31. package/dist/{useTheme-DHIrRQOe.js → useTheme-DEhDzATN.js} +1 -1
  32. package/dist/{vega-component-Dq-SH463.js → vega-component-9h1ACS78.js} +8 -8
  33. package/dist/{zod-CoBiJ5v4.js → zod-aLSua2NL.js} +24 -23
  34. package/package.json +1 -1
  35. package/src/components/editor/chrome/wrapper/app-chrome.tsx +97 -52
  36. package/src/components/editor/chrome/wrapper/lazy-panels.ts +91 -0
  37. package/src/components/editor/chrome/wrapper/sidebar.tsx +2 -0
  38. package/src/components/slides/reveal-component.tsx +4 -0
  39. package/src/components/ui/reorderable-list.tsx +13 -0
  40. package/src/utils/lazy.ts +6 -1
@@ -4,9 +4,9 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4
4
  import { t as __commonJSMin } from "./chunk-BNovOVIE.js";
5
5
  import { _ as Logger } from "./button-C5K9fIPF.js";
6
6
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
7
- import { u as createLucideIcon } from "./dist-C1BYNeCR.js";
7
+ import { u as createLucideIcon } from "./dist-Dk6PV_d3.js";
8
8
  import { r as KnownQueryParams } from "./constants-T20xxyNf.js";
9
- import { b as atom, d as store, m as isIslands, p as waitFor } from "./useTheme-DHIrRQOe.js";
9
+ import { b as atom, d as store, m as isIslands, p as waitFor } from "./useTheme-DEhDzATN.js";
10
10
  import { t as invariant } from "./invariant-wRzNXIsJ.js";
11
11
  var CircleQuestionMark = createLucideIcon("circle-question-mark", [
12
12
  ["circle", {
@@ -2,9 +2,9 @@ import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { f as createSlottable, g as cn, m as useComposedRefs } from "./button-C5K9fIPF.js";
3
3
  import { t as require_react } from "./react-DA-nE2FX.js";
4
4
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
5
- import { a as createPopperScope, i as Root2, n as Arrow, r as Content, s as Root, t as Anchor } from "./dist-C1BYNeCR.js";
5
+ import { a as createPopperScope, i as Root2, n as Arrow, r as Content, s as Root, t as Anchor } from "./dist-Dk6PV_d3.js";
6
6
  import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
7
- import { $ as StyleNamespace, X as withFullScreenAsRoot, Z as withSmartCollisionBoundary, _t as Primitive, dt as Presence, ft as useControllableState, gt as createContextScope, it as Portal, mt as composeEventHandlers, st as DismissableLayer, ut as useId } from "./zod-CoBiJ5v4.js";
7
+ import { Q as withSmartCollisionBoundary, Z as withFullScreenAsRoot, _t as createContextScope, at as Portal, ct as DismissableLayer, dt as useId, et as StyleNamespace, ft as Presence, ht as composeEventHandlers, pt as useControllableState, vt as Primitive } from "./zod-aLSua2NL.js";
8
8
  var import_react = /* @__PURE__ */ __toESM(require_react(), 1), import_jsx_runtime = /* @__PURE__ */ __toESM(require_jsx_runtime(), 1), [createTooltipContext, createTooltipScope] = createContextScope("Tooltip", [createPopperScope]), usePopperScope = createPopperScope(), PROVIDER_NAME = "TooltipProvider", DEFAULT_DELAY_DURATION = 700, TOOLTIP_OPEN = "tooltip.open", [TooltipProviderContextProvider, useTooltipProviderContext] = createTooltipContext(PROVIDER_NAME), TooltipProvider$1 = (n) => {
9
9
  let { __scopeTooltip: j, delayDuration: M = DEFAULT_DELAY_DURATION, skipDelayDuration: N = 300, disableHoverableContent: P = false, children: F } = n, I = import_react.useRef(true), L = import_react.useRef(false), R = import_react.useRef(0);
10
10
  return import_react.useEffect(() => {
@@ -1,4 +1,4 @@
1
- import { u as createLucideIcon } from "./dist-C1BYNeCR.js";
1
+ import { u as createLucideIcon } from "./dist-Dk6PV_d3.js";
2
2
  var Pencil = createLucideIcon("pencil", [["path", {
3
3
  d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
4
4
  key: "1a8usu"
@@ -1,7 +1,7 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-DA-nE2FX.js";
3
3
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
4
- import { T as useEvent_default } from "./useTheme-DHIrRQOe.js";
4
+ import { T as useEvent_default } from "./useTheme-DEhDzATN.js";
5
5
  import { t as invariant } from "./invariant-wRzNXIsJ.js";
6
6
  var import_compiler_runtime = require_compiler_runtime(), import_react = /* @__PURE__ */ __toESM(require_react(), 1), Result = {
7
7
  error(e, s) {
@@ -1,7 +1,7 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-DA-nE2FX.js";
3
- import { u as createLucideIcon } from "./dist-C1BYNeCR.js";
4
- import { E as $18f2051aff69b9bf$export$43bb16f9c6d9e3f7 } from "./strings-GCJA9n6d.js";
3
+ import { u as createLucideIcon } from "./dist-Dk6PV_d3.js";
4
+ import { E as $18f2051aff69b9bf$export$43bb16f9c6d9e3f7 } from "./strings-J57tzLr3.js";
5
5
  var ChartPie = createLucideIcon("chart-pie", [["path", {
6
6
  d: "M21 12c.552 0 1.005-.449.95-.998a10 10 0 0 0-8.953-8.951c-.55-.055-.998.398-.998.95v8a1 1 0 0 0 1 1z",
7
7
  key: "pzmjnu"
@@ -1,6 +1,6 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-DA-nE2FX.js";
3
- import { w as dequal } from "./useTheme-DHIrRQOe.js";
3
+ import { w as dequal } from "./useTheme-DEhDzATN.js";
4
4
  var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
5
5
  function useDeepCompareMemoize(e) {
6
6
  let i = import_react.useRef(e);
@@ -2,9 +2,9 @@ import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { _ as Logger, g as cn, r as cva } from "./button-C5K9fIPF.js";
3
3
  import { t as require_react } from "./react-DA-nE2FX.js";
4
4
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
5
- import { u as createLucideIcon } from "./dist-C1BYNeCR.js";
5
+ import { u as createLucideIcon } from "./dist-Dk6PV_d3.js";
6
6
  import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
7
- import { b as atom, v as useSetAtom } from "./useTheme-DHIrRQOe.js";
7
+ import { b as atom, v as useSetAtom } from "./useTheme-DEhDzATN.js";
8
8
  var Calendar = createLucideIcon("calendar", [
9
9
  ["path", {
10
10
  d: "M8 2v4",
@@ -2,7 +2,7 @@ import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { _ as Logger, a as OverridingHotkeyProvider, s as resolvePlatform } from "./button-C5K9fIPF.js";
3
3
  import { t as require_react } from "./react-DA-nE2FX.js";
4
4
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
5
- import { A as looseObject, B as union, I as record, N as number, P as object, R as string, T as boolean, b as _enum, w as array } from "./zod-CoBiJ5v4.js";
5
+ import { A as looseObject, B as union, I as record, N as number, P as object, R as string, T as boolean, b as _enum, w as array } from "./zod-aLSua2NL.js";
6
6
  import { t as merge_default } from "./merge-Be1CqGnU.js";
7
7
  var import_react = /* @__PURE__ */ __toESM(require_react()), useInsertionEffect = typeof window < "u" ? import_react.useInsertionEffect || import_react.useLayoutEffect : () => {
8
8
  };
@@ -2,23 +2,23 @@ import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { _ as Logger, c as Objects, g as cn, h as Events } from "./button-C5K9fIPF.js";
3
3
  import { t as require_react } from "./react-DA-nE2FX.js";
4
4
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
5
- import { c as asRemoteURL, v as CircleQuestionMark } from "./toDate-x-WRDCH7.js";
5
+ import { c as asRemoteURL, v as CircleQuestionMark } from "./toDate-d8RCRrRd.js";
6
6
  import "./react-dom-BTJzcVJ9.js";
7
7
  import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
8
- import "./zod-CoBiJ5v4.js";
9
- import { n as ErrorBanner } from "./error-banner-5bz0L9hS.js";
10
- import { t as Tooltip } from "./tooltip-C5FYOpQc.js";
8
+ import "./zod-aLSua2NL.js";
9
+ import { n as ErrorBanner } from "./error-banner-Cc0I3C9e.js";
10
+ import { t as Tooltip } from "./tooltip-DpcyNkQ2.js";
11
11
  import { i as debounce_default } from "./constants-T20xxyNf.js";
12
- import { T as useEvent_default, n as useTheme } from "./useTheme-DHIrRQOe.js";
12
+ import { T as useEvent_default, n as useTheme } from "./useTheme-DEhDzATN.js";
13
13
  import { s as uniq } from "./arrays-sEtDRoG4.js";
14
- import { a as isValid, i as AlertTitle, n as Alert, t as arrow } from "./formats-DHxc-FdY.js";
14
+ import { a as isValid, i as AlertTitle, n as Alert, t as arrow } from "./formats-C4wO47tk.js";
15
15
  import { n as formats } from "./vega-loader.browser-CZ-J8Py3.js";
16
16
  import { a as getContainerWidth, n as vegaLoadData, s as tooltipHandler } from "./loader-BWLPpjKK.js";
17
17
  import { t as j } from "./react-vega-B0sAlDTL.js";
18
18
  import "./defaultLocale-u-3osm0P.js";
19
19
  import "./defaultLocale-BoHTsDG6.js";
20
- import { t as useAsyncData } from "./useAsyncData-iRgKDT5s.js";
21
- import { t as useDeepCompareMemoize } from "./useDeepCompareMemoize-CkQ57VS2.js";
20
+ import { t as useAsyncData } from "./useAsyncData-PonK__yh.js";
21
+ import { t as useDeepCompareMemoize } from "./useDeepCompareMemoize-D3NGWke6.js";
22
22
  import { t as Semaphore } from "./semaphore-CNDGTzkX.js";
23
23
  var import_compiler_runtime = require_compiler_runtime(), import_react = /* @__PURE__ */ __toESM(require_react(), 1);
24
24
  function fixRelativeUrl(e) {
@@ -11758,7 +11758,7 @@ var external_exports = /* @__PURE__ */ __export({
11758
11758
  config(en_default());
11759
11759
  var zod_default = external_exports;
11760
11760
  export {
11761
- StyleNamespace as $,
11761
+ isInVscodeExtension as $,
11762
11762
  looseObject as A,
11763
11763
  union as B,
11764
11764
  any as C,
@@ -11775,59 +11775,60 @@ export {
11775
11775
  number$1 as N,
11776
11776
  lazy as O,
11777
11777
  object as P,
11778
- isInVscodeExtension as Q,
11778
+ withSmartCollisionBoundary as Q,
11779
11779
  string$1 as R,
11780
11780
  _null as S,
11781
11781
  boolean$1 as T,
11782
11782
  ZodError as U,
11783
11783
  unknown as V,
11784
11784
  toJSONSchema as W,
11785
- withFullScreenAsRoot as X,
11785
+ useFullScreenElement as X,
11786
11786
  MAX_HEIGHT_OFFSET as Y,
11787
- withSmartCollisionBoundary as Z,
11787
+ withFullScreenAsRoot as Z,
11788
11788
  ZodString as _,
11789
- Primitive as _t,
11789
+ createContextScope as _t,
11790
11790
  ZodIssueCode as a,
11791
- FocusScope as at,
11791
+ Portal as at,
11792
11792
  _enum as b,
11793
11793
  ZodBoolean as c,
11794
- Root as ct,
11794
+ DismissableLayer as ct,
11795
11795
  ZodDiscriminatedUnion as d,
11796
- Presence as dt,
11797
- hideOthers as et,
11796
+ useId as dt,
11797
+ StyleNamespace as et,
11798
11798
  ZodEnum as f,
11799
- useControllableState as ft,
11799
+ Presence as ft,
11800
11800
  ZodOptional as g,
11801
- createContextScope as gt,
11801
+ createContext2 as gt,
11802
11802
  ZodObject as h,
11803
- createContext2 as ht,
11803
+ composeEventHandlers as ht,
11804
11804
  string as i,
11805
- Portal as it,
11805
+ useFocusGuards as it,
11806
11806
  nan as j,
11807
11807
  literal as k,
11808
11808
  ZodDate as l,
11809
- useCallbackRef$1 as lt,
11809
+ Root as lt,
11810
11810
  ZodNumber as m,
11811
- composeEventHandlers as mt,
11811
+ useLayoutEffect2 as mt,
11812
11812
  date as n,
11813
- __awaiter as nt,
11813
+ Combination_default as nt,
11814
11814
  ZodAny as o,
11815
- Branch as ot,
11815
+ FocusScope as ot,
11816
11816
  ZodLiteral as p,
11817
- useLayoutEffect2 as pt,
11817
+ useControllableState as pt,
11818
11818
  $ZodError as q,
11819
11819
  number as r,
11820
- useFocusGuards as rt,
11820
+ __awaiter as rt,
11821
11821
  ZodArray as s,
11822
- DismissableLayer as st,
11822
+ Branch as st,
11823
11823
  zod_default as t,
11824
- Combination_default as tt,
11824
+ hideOthers as tt,
11825
11825
  ZodDefault as u,
11826
- useId as ut,
11826
+ useCallbackRef$1 as ut,
11827
11827
  ZodType as v,
11828
- dispatchDiscreteCustomEvent as vt,
11828
+ Primitive as vt,
11829
11829
  array as w,
11830
11830
  _instanceof as x,
11831
11831
  ZodUnion as y,
11832
+ dispatchDiscreteCustomEvent as yt,
11832
11833
  tuple as z
11833
11834
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.23.10-dev22",
3
+ "version": "0.23.10-dev24",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -34,6 +34,26 @@ import { ErrorBoundary } from "../../boundary/ErrorBoundary";
34
34
  import { raf2 } from "../../navigation/focus-utils";
35
35
  import { ContextAwarePanel } from "../panels/context-aware-panel/context-aware-panel";
36
36
  import { PanelSectionProvider } from "../panels/panel-context";
37
+ import { useTheme } from "@/theme/useTheme";
38
+ import {
39
+ LazyAgentPanel,
40
+ LazyCachePanel,
41
+ LazyChatPanel,
42
+ LazyDependencyGraphPanel,
43
+ LazyDocumentationPanel,
44
+ LazyErrorsPanel,
45
+ LazyFileExplorerPanel,
46
+ LazyLogsPanel,
47
+ LazyOutlinePanel,
48
+ LazyPackagesPanel,
49
+ LazyScratchpadPanel,
50
+ LazySecretsPanel,
51
+ LazySessionPanel,
52
+ LazySnippetsPanel,
53
+ LazyTerminal,
54
+ LazyTracingPanel,
55
+ PANEL_PRELOADERS,
56
+ } from "./lazy-panels";
37
57
  import { panelLayoutAtom, useChromeActions, useChromeState } from "../state";
38
58
  import {
39
59
  isPanelHidden,
@@ -50,33 +70,26 @@ import { useAiPanelTab } from "./useAiPanel";
50
70
  import { useDependencyPanelTab } from "./useDependencyPanelTab";
51
71
  import { handleDragging } from "./utils";
52
72
 
53
- const LazyTerminal = React.lazy(() => import("@/components/terminal/terminal"));
54
- const LazyChatPanel = React.lazy(() => import("@/components/chat/chat-panel"));
55
- const LazyAgentPanel = React.lazy(
56
- () => import("@/components/chat/acp/agent-panel"),
57
- );
58
- const LazyDependencyGraphPanel = React.lazy(
59
- () => import("@/components/editor/chrome/panels/dependency-graph-panel"),
60
- );
61
- const LazySessionPanel = React.lazy(() => import("../panels/session-panel"));
62
- const LazyDocumentationPanel = React.lazy(
63
- () => import("../panels/documentation-panel"),
64
- );
65
- const LazyErrorsPanel = React.lazy(() => import("../panels/error-panel"));
66
- const LazyFileExplorerPanel = React.lazy(
67
- () => import("../panels/file-explorer-panel"),
68
- );
69
- const LazyLogsPanel = React.lazy(() => import("../panels/logs-panel"));
70
- const LazyOutlinePanel = React.lazy(() => import("../panels/outline-panel"));
71
- const LazyPackagesPanel = React.lazy(() => import("../panels/packages-panel"));
72
- const LazyScratchpadPanel = React.lazy(
73
- () => import("../panels/scratchpad-panel"),
74
- );
75
- const LazySecretsPanel = React.lazy(() => import("../panels/secrets-panel"));
76
- const LazySnippetsPanel = React.lazy(() => import("../panels/snippets-panel"));
77
- const LazyTracingPanel = React.lazy(() => import("../panels/tracing-panel"));
78
- const LazyCachePanel = React.lazy(() => import("../panels/cache-panel"));
79
-
73
+ // Placeholder that matches the eventual xterm theme background so the
74
+ // transition into the loaded terminal is seamless rather than a blank flash.
75
+ const TerminalSkeleton: React.FC = () => {
76
+ const { theme } = useTheme();
77
+ const isDark = theme === "dark";
78
+ return (
79
+ <div
80
+ aria-label="Loading terminal"
81
+ role="status"
82
+ className="w-full h-full flex items-start p-3 font-mono text-xs select-none"
83
+ style={{
84
+ background: isDark ? "#0f172a" : "#ffffff",
85
+ color: isDark ? "#94a3b8" : "#64748b",
86
+ }}
87
+ >
88
+ <span className="opacity-70">Starting terminal</span>
89
+ <span className="ml-1 inline-block w-2 h-3.5 align-middle bg-current animate-pulse" />
90
+ </div>
91
+ );
92
+ };
80
93
  export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
81
94
  const {
82
95
  isSidebarOpen,
@@ -96,6 +109,33 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
96
109
  const capabilities = useAtomValue(capabilitiesAtom);
97
110
  const aiEnabled = useAtomValue(aiEnabledAtom);
98
111
 
112
+ // On mount, idle-preload whichever panels the user had open at last unload,
113
+ // so the first interaction with the sidebar/dev panel doesn't hit a cold
114
+ // chunk fetch. Runs once.
115
+ // oxlint-disable-next-line react-hooks/exhaustive-deps
116
+ useEffect(() => {
117
+ const preloadOpenPanels = () => {
118
+ if (isSidebarOpen && selectedPanel) {
119
+ PANEL_PRELOADERS[selectedPanel]?.();
120
+ }
121
+ if (isDeveloperPanelOpen && selectedDeveloperPanelTab) {
122
+ PANEL_PRELOADERS[selectedDeveloperPanelTab]?.();
123
+ }
124
+ };
125
+
126
+ const canIdle =
127
+ typeof window !== "undefined" &&
128
+ typeof window.requestIdleCallback === "function";
129
+ if (canIdle) {
130
+ const handle = window.requestIdleCallback(preloadOpenPanels, {
131
+ timeout: 2000,
132
+ });
133
+ return () => window.cancelIdleCallback(handle);
134
+ }
135
+ const handle = setTimeout(preloadOpenPanels, 300);
136
+ return () => clearTimeout(handle);
137
+ }, []);
138
+
99
139
  // Convert current developer panel items to PanelDescriptors
100
140
  // Filter out hidden panels (e.g., terminal when capability is not available)
101
141
  const devPanelItems = useMemo(() => {
@@ -256,32 +296,34 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
256
296
 
257
297
  const renderAiPanel = () => {
258
298
  if (agentsEnabled && aiPanelTab === "agents") {
259
- return <LazyAgentPanel />;
299
+ return <LazyAgentPanel.Component />;
260
300
  }
261
- return <LazyChatPanel />;
301
+ return <LazyChatPanel.Component />;
262
302
  };
263
303
 
264
304
  const SIDEBAR_PANELS: Record<PanelType, React.ReactNode> = {
265
- files: <LazyFileExplorerPanel />,
266
- variables: <LazySessionPanel />,
267
- dependencies: <LazyDependencyGraphPanel />,
268
- packages: <LazyPackagesPanel />,
269
- outline: <LazyOutlinePanel />,
270
- documentation: <LazyDocumentationPanel />,
271
- snippets: <LazySnippetsPanel />,
305
+ files: <LazyFileExplorerPanel.Component />,
306
+ variables: <LazySessionPanel.Component />,
307
+ dependencies: <LazyDependencyGraphPanel.Component />,
308
+ packages: <LazyPackagesPanel.Component />,
309
+ outline: <LazyOutlinePanel.Component />,
310
+ documentation: <LazyDocumentationPanel.Component />,
311
+ snippets: <LazySnippetsPanel.Component />,
272
312
  ai: renderAiPanel(),
273
- errors: <LazyErrorsPanel />,
274
- scratchpad: <LazyScratchpadPanel />,
275
- tracing: <LazyTracingPanel />,
276
- secrets: <LazySecretsPanel />,
277
- logs: <LazyLogsPanel />,
313
+ errors: <LazyErrorsPanel.Component />,
314
+ scratchpad: <LazyScratchpadPanel.Component />,
315
+ tracing: <LazyTracingPanel.Component />,
316
+ secrets: <LazySecretsPanel.Component />,
317
+ logs: <LazyLogsPanel.Component />,
278
318
  terminal: (
279
- <LazyTerminal
280
- visible={isSidebarOpen && selectedPanel === "terminal"}
281
- onClose={() => setIsSidebarOpen(false)}
282
- />
319
+ <Suspense fallback={<TerminalSkeleton />}>
320
+ <LazyTerminal.Component
321
+ visible={isSidebarOpen && selectedPanel === "terminal"}
322
+ onClose={() => setIsSidebarOpen(false)}
323
+ />
324
+ </Suspense>
283
325
  ),
284
- cache: <LazyCachePanel />,
326
+ cache: <LazyCachePanel.Component />,
285
327
  };
286
328
 
287
329
  const helpPaneBody = (
@@ -414,12 +456,14 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
414
456
  const DEVELOPER_PANELS: Record<PanelType, React.ReactNode> = {
415
457
  ...SIDEBAR_PANELS,
416
458
  terminal: (
417
- <LazyTerminal
418
- visible={
419
- isDeveloperPanelOpen && selectedDeveloperPanelTab === "terminal"
420
- }
421
- onClose={() => setIsDeveloperPanelOpen(false)}
422
- />
459
+ <Suspense fallback={<TerminalSkeleton />}>
460
+ <LazyTerminal.Component
461
+ visible={
462
+ isDeveloperPanelOpen && selectedDeveloperPanelTab === "terminal"
463
+ }
464
+ onClose={() => setIsDeveloperPanelOpen(false)}
465
+ />
466
+ </Suspense>
423
467
  ),
424
468
  };
425
469
 
@@ -474,6 +518,7 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
474
518
  className="flex flex-row gap-1"
475
519
  minItems={0}
476
520
  onAction={(panel) => openApplication(panel.type)}
521
+ onItemPreloadHint={(panel) => PANEL_PRELOADERS[panel.type]?.()}
477
522
  renderItem={(panel) => (
478
523
  <div
479
524
  className={cn(
@@ -0,0 +1,91 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { reactLazyWithPreload } from "@/utils/lazy";
4
+ import { Logger } from "@/utils/Logger";
5
+ import type { PanelType } from "../types";
6
+
7
+ // Preloading is best-effort: a chunk-load failure here should not surface as
8
+ // an unhandled rejection (the panel will retry the import when actually
9
+ // rendered, where Suspense/ErrorBoundary handle the failure).
10
+ const safePreload = (lazy: { preload: () => Promise<unknown> }) => (): void => {
11
+ void lazy.preload().catch((error) => {
12
+ Logger.debug("Failed to preload panel chunk", error);
13
+ });
14
+ };
15
+
16
+ // Centralized lazy panels. Using reactLazyWithPreload (instead of React.lazy)
17
+ // gives each panel a .preload() method so the chunk can be fetched on intent
18
+ // (hovering the sidebar icon or developer-panel tab) before the user clicks.
19
+
20
+ export const LazyTerminal = reactLazyWithPreload(
21
+ () => import("@/components/terminal/terminal"),
22
+ );
23
+ export const LazyChatPanel = reactLazyWithPreload(
24
+ () => import("@/components/chat/chat-panel"),
25
+ );
26
+ export const LazyAgentPanel = reactLazyWithPreload(
27
+ () => import("@/components/chat/acp/agent-panel"),
28
+ );
29
+ export const LazyDependencyGraphPanel = reactLazyWithPreload(
30
+ () => import("../panels/dependency-graph-panel"),
31
+ );
32
+ export const LazySessionPanel = reactLazyWithPreload(
33
+ () => import("../panels/session-panel"),
34
+ );
35
+ export const LazyDocumentationPanel = reactLazyWithPreload(
36
+ () => import("../panels/documentation-panel"),
37
+ );
38
+ export const LazyErrorsPanel = reactLazyWithPreload(
39
+ () => import("../panels/error-panel"),
40
+ );
41
+ export const LazyFileExplorerPanel = reactLazyWithPreload(
42
+ () => import("../panels/file-explorer-panel"),
43
+ );
44
+ export const LazyLogsPanel = reactLazyWithPreload(
45
+ () => import("../panels/logs-panel"),
46
+ );
47
+ export const LazyOutlinePanel = reactLazyWithPreload(
48
+ () => import("../panels/outline-panel"),
49
+ );
50
+ export const LazyPackagesPanel = reactLazyWithPreload(
51
+ () => import("../panels/packages-panel"),
52
+ );
53
+ export const LazyScratchpadPanel = reactLazyWithPreload(
54
+ () => import("../panels/scratchpad-panel"),
55
+ );
56
+ export const LazySecretsPanel = reactLazyWithPreload(
57
+ () => import("../panels/secrets-panel"),
58
+ );
59
+ export const LazySnippetsPanel = reactLazyWithPreload(
60
+ () => import("../panels/snippets-panel"),
61
+ );
62
+ export const LazyTracingPanel = reactLazyWithPreload(
63
+ () => import("../panels/tracing-panel"),
64
+ );
65
+ export const LazyCachePanel = reactLazyWithPreload(
66
+ () => import("../panels/cache-panel"),
67
+ );
68
+
69
+ // Preloader registry: hovering an icon/tab calls into this map to warm the
70
+ // corresponding chunk. Two panel types (chat and agents) share the "ai" slot,
71
+ // so we preload both.
72
+ export const PANEL_PRELOADERS: Record<PanelType, () => void> = {
73
+ files: safePreload(LazyFileExplorerPanel),
74
+ variables: safePreload(LazySessionPanel),
75
+ dependencies: safePreload(LazyDependencyGraphPanel),
76
+ packages: safePreload(LazyPackagesPanel),
77
+ outline: safePreload(LazyOutlinePanel),
78
+ documentation: safePreload(LazyDocumentationPanel),
79
+ snippets: safePreload(LazySnippetsPanel),
80
+ ai: () => {
81
+ safePreload(LazyChatPanel)();
82
+ safePreload(LazyAgentPanel)();
83
+ },
84
+ errors: safePreload(LazyErrorsPanel),
85
+ scratchpad: safePreload(LazyScratchpadPanel),
86
+ tracing: safePreload(LazyTracingPanel),
87
+ secrets: safePreload(LazySecretsPanel),
88
+ logs: safePreload(LazyLogsPanel),
89
+ terminal: safePreload(LazyTerminal),
90
+ cache: safePreload(LazyCachePanel),
91
+ };
@@ -22,6 +22,7 @@ import {
22
22
  PANELS,
23
23
  type PanelDescriptor,
24
24
  } from "../types";
25
+ import { PANEL_PRELOADERS } from "./lazy-panels";
25
26
 
26
27
  export const Sidebar: React.FC = () => {
27
28
  const { selectedPanel, selectedDeveloperPanelTab, isSidebarOpen } =
@@ -141,6 +142,7 @@ export const Sidebar: React.FC = () => {
141
142
  className="flex flex-col gap-0"
142
143
  minItems={0}
143
144
  onAction={(panel) => toggleApplication(panel.type)}
145
+ onItemPreloadHint={(panel) => PANEL_PRELOADERS[panel.type]?.()}
144
146
  renderItem={(panel) => (
145
147
  <SidebarItem
146
148
  tooltip={panel.tooltip}
@@ -14,6 +14,7 @@ import { Deck, Fragment, Slide, Stack } from "@revealjs/react";
14
14
  import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
15
15
  import { Slide as CellOutputSlide } from "@/components/slides/slide";
16
16
  import { Button } from "@/components/ui/button";
17
+ import { useFullScreenElement } from "@/components/ui/fullscreen";
17
18
  import { Tooltip } from "@/components/ui/tooltip";
18
19
  import type { CellId } from "@/core/cells/ids";
19
20
  import type { RuntimeCell } from "@/core/cells/types";
@@ -274,6 +275,8 @@ const RevealSlidesComponent = ({
274
275
  const containerRef = useRef<HTMLDivElement>(null);
275
276
  const deckRef = useRef<RevealApi | null>(null);
276
277
  const { width, height } = useSlideDimensions(containerRef);
278
+ const isFullscreen = useFullScreenElement() != null;
279
+
277
280
  // Skip the Notes plugin inside reveal's own speaker-view iframes so pressing
278
281
  // `S` there doesn't try to spawn another popup.
279
282
  const kioskMode = useAtomValue(kioskModeAtom);
@@ -607,6 +610,7 @@ const RevealSlidesComponent = ({
607
610
  <PanelResizeHandle
608
611
  className="mo-slides-notes-resize"
609
612
  hitAreaMargins={{ coarse: 12, fine: 4 }}
613
+ disabled={isFullscreen}
610
614
  />
611
615
  <Panel
612
616
  defaultSize={10}
@@ -57,6 +57,12 @@ export interface ReorderableListProps<T> {
57
57
  * Callback when an item is clicked
58
58
  */
59
59
  onAction?: (item: T) => void;
60
+ /**
61
+ * Fired when an item is hovered or focused. Useful for preloading the
62
+ * resource the item points to before it is activated. Attached to the
63
+ * focusable list item so it works for both pointer and keyboard users.
64
+ */
65
+ onItemPreloadHint?: (item: T) => void;
60
66
  /**
61
67
  * All available items that can be added to the list
62
68
  */
@@ -142,6 +148,7 @@ export const ReorderableList = <T extends object>({
142
148
  ariaLabel = "Reorderable list",
143
149
  className,
144
150
  crossListDrag,
151
+ onItemPreloadHint,
145
152
  }: ReorderableListProps<T>) => {
146
153
  const mimeType = crossListDrag
147
154
  ? getDragMimeType(crossListDrag.dragType)
@@ -294,6 +301,12 @@ export const ReorderableList = <T extends object>({
294
301
  key={getKey(item)}
295
302
  id={getKey(item)}
296
303
  className="active:cursor-grabbing data-[dragging]:opacity-60 outline-none"
304
+ onHoverStart={
305
+ onItemPreloadHint ? () => onItemPreloadHint(item) : undefined
306
+ }
307
+ onFocus={
308
+ onItemPreloadHint ? () => onItemPreloadHint(item) : undefined
309
+ }
297
310
  >
298
311
  {renderItem(item)}
299
312
  </ListBoxItem>
package/src/utils/lazy.ts CHANGED
@@ -3,7 +3,12 @@
3
3
  import React from "react";
4
4
 
5
5
  interface LazyComponentWithPreload<T> {
6
- preload: () => void;
6
+ /**
7
+ * Eagerly trigger the dynamic import. Returns the import promise so callers
8
+ * can await it or attach error handling; safe to call multiple times (the
9
+ * import is memoized).
10
+ */
11
+ preload: () => Promise<{ default: React.ComponentType<T> }>;
7
12
  Component: React.LazyExoticComponent<React.ComponentType<T>>;
8
13
  }
9
14