@marimo-team/islands 0.25.3-dev250 → 0.25.3-dev251

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 (147) hide show
  1. package/dist/{ConnectedDataExplorerComponent-BBihxHrr.js → ConnectedDataExplorerComponent-DGOAQFtf.js} +11 -11
  2. package/dist/{ErrorBoundary-Ks8L_DFj.js → ErrorBoundary-CZ19wGWT.js} +7 -7
  3. package/dist/{abnfDiagram-VCTEODGH-DBcJVxYP.js → abnfDiagram-VCTEODGH-DzrZZXRD.js} +2 -2
  4. package/dist/{any-language-editor-jGaR0EAo.js → any-language-editor-Be7dA2VK.js} +3 -3
  5. package/dist/{architectureDiagram-5GKGNRK7-BcHZXF4S.js → architectureDiagram-5GKGNRK7-iF_snyFL.js} +1 -1
  6. package/dist/{button-B6RjDKJx.js → button-0-RqY1DW.js} +2 -1
  7. package/dist/{capabilities-CkVzWJEX.js → capabilities-B6yy_CBN.js} +1 -1
  8. package/dist/{chat-ui-DgaVxFrc.js → chat-ui-zbfp80GW.js} +1547 -1482
  9. package/dist/{check-Z_vtI3OL.js → check-CSvxZk5I.js} +1 -1
  10. package/dist/{chunk-IMKFNOWR-CTZlwBAA.js → chunk-IMKFNOWR-B7xytD4B.js} +1 -1
  11. package/dist/{chunk-SHT3W25Y-53g_RZHm.js → chunk-SHT3W25Y-I8ZFP5L1.js} +1 -1
  12. package/dist/{chunk-SVP7TREG-P6abNGV_.js → chunk-SVP7TREG-Di-uAPdT.js} +1 -1
  13. package/dist/{chunk-TICWLB2K-BTJJqkyF.js → chunk-TICWLB2K-BFpUy0ud.js} +1 -1
  14. package/dist/{classDiagram-ZZMXUADV-BUGqOrTm.js → classDiagram-ZZMXUADV-Cqm4JGpx.js} +2 -2
  15. package/dist/{classDiagram-v2-VYDZK3BY-CXpQvO-s.js → classDiagram-v2-VYDZK3BY-DFHRJpN3.js} +2 -2
  16. package/dist/{code-visibility-CCCqFZYh.js → code-visibility-D2D_sL1T.js} +4 -4
  17. package/dist/{common-JXDK9IjO.js → common-DSIM5DHJ.js} +262 -262
  18. package/dist/{config-DbjzjwxX.js → config-mwEjAmvP.js} +2 -2
  19. package/dist/{copy-icon-CG57cxwb.js → copy-icon-DD8bdw4L.js} +3 -3
  20. package/dist/{createReducer-DePYJ0F3.js → createReducer-Bx81cw8_.js} +1 -1
  21. package/dist/{cynefinDiagram-5FMLGOSQ-DOcnl8wW.js → cynefinDiagram-5FMLGOSQ-cuGOeTBC.js} +1 -1
  22. package/dist/{dagre-GXQ25YYZ-CuHR9QfG.js → dagre-GXQ25YYZ-DJ76aC4k.js} +1 -1
  23. package/dist/{data-grid-overlay-editor-QAJSeJpH.js → data-grid-overlay-editor-ByfOIO6H.js} +1 -1
  24. package/dist/{diagram-S7CK7UJ4-BPiG4kso.js → diagram-S7CK7UJ4-DLLc0sw7.js} +1 -1
  25. package/dist/{diagram-UQ7AKVKN-_piBOvOC.js → diagram-UQ7AKVKN-Tz_d6qmk.js} +1 -1
  26. package/dist/{diagram-VX7I27RA-DCzVeJ8M.js → diagram-VX7I27RA-D7SzPrDN.js} +1 -1
  27. package/dist/{diagram-Z3DM3KII-D64CF1jQ.js → diagram-Z3DM3KII-qr37XV58.js} +1 -1
  28. package/dist/{dialog-DsZ49mmU.js → dialog-BHYPX87J.js} +3 -3
  29. package/dist/{dropdown-menu-BRiE7Vnp.js → dropdown-menu-Blu7V6Mk.js} +6 -6
  30. package/dist/{ebnfDiagram-PWID7BFC-BapQPML-.js → ebnfDiagram-PWID7BFC-BKOQb4J0.js} +2 -2
  31. package/dist/{erDiagram-RLTQ6QDP-CNQvC2_Q.js → erDiagram-RLTQ6QDP-CM15Vq68.js} +1 -1
  32. package/dist/{error-banner-IsonnvA0.js → error-banner-DbRFOd2O.js} +2 -2
  33. package/dist/{error-entries-CRPfxMqm.js → error-entries-BuIE3Ius.js} +9 -9
  34. package/dist/{feedback-button-Bg7zGcid.js → feedback-button-VT03EJ1h.js} +7 -7
  35. package/dist/{flowDiagram-HODETNUW-BBt6S6oe.js → flowDiagram-HODETNUW-Dt3N5yFT.js} +2 -2
  36. package/dist/{formats-BK4VefeF.js → formats-gGospKWJ.js} +2 -2
  37. package/dist/{glide-data-editor-BuFe7QfV.js → glide-data-editor-C94G7kD7.js} +10 -10
  38. package/dist/{glide-utils-CQddGpa-.js → glide-utils-qbkRZpEy.js} +3 -3
  39. package/dist/{globals-8EK96XwF.js → globals-D7JZ5hgJ.js} +2 -2
  40. package/dist/{html-to-image-Gx4q3ZoD.js → html-to-image-BXrAd5q8.js} +460 -455
  41. package/dist/{infoDiagram-27XIBGKW-Hd8Ifz82.js → infoDiagram-27XIBGKW-C7axJMCl.js} +1 -1
  42. package/dist/{ishikawaDiagram-5VMMS53U-CP7hnHWx.js → ishikawaDiagram-5VMMS53U-CdC9Eur7.js} +1 -1
  43. package/dist/{kanban-definition-UXKFOSKX-CX91sa9b.js → kanban-definition-UXKFOSKX-BHnMIgD7.js} +1 -1
  44. package/dist/{loader-BisjOLiW.js → loader-BVEtQ4F3.js} +1 -1
  45. package/dist/main.js +236 -236
  46. package/dist/{mermaid-Djb3Ni9t.js → mermaid-DFYN_PXd.js} +35 -35
  47. package/dist/{mindmap-definition-YA3MSWOX-DgCNo07O.js → mindmap-definition-YA3MSWOX-BagHoCGU.js} +1 -1
  48. package/dist/{number-overlay-editor-BDJF1c8h.js → number-overlay-editor-CdcscCHp.js} +1 -1
  49. package/dist/{pegDiagram-XKGWAZYB-SGJhnIKo.js → pegDiagram-XKGWAZYB-8IIZczip.js} +2 -2
  50. package/dist/{pieDiagram-E7YTZNPT-1stXIWCQ.js → pieDiagram-E7YTZNPT-CE6dKBIr.js} +1 -1
  51. package/dist/{process-output-D2ISIyIs.js → process-output-D2ZjxrHe.js} +4 -4
  52. package/dist/{railroadDiagram-O6MQD6OU-DY1mojzB.js → railroadDiagram-O6MQD6OU-CkexPq3b.js} +2 -2
  53. package/dist/{requirementDiagram-BXWQKSXE-gHzhUp0D.js → requirementDiagram-BXWQKSXE-B0-OkuAe.js} +1 -1
  54. package/dist/{reveal-component-B_hNJfj-.js → reveal-component-Cr9euMM2.js} +8 -8
  55. package/dist/{select-BSxlITBo.js → select-CcidPqlM.js} +5 -5
  56. package/dist/{sequenceDiagram-WJ2MYXX4-rjPtD9fB.js → sequenceDiagram-WJ2MYXX4-Dxysg8DX.js} +1 -1
  57. package/dist/{spec-Dq8RNplp.js → spec-ZwfZcgsm.js} +4 -4
  58. package/dist/{stateDiagram-D77RDMKH-DMNxjUxV.js → stateDiagram-D77RDMKH-DTEN3-OJ.js} +2 -2
  59. package/dist/{stateDiagram-v2-MP3YSRHH-CumAaqGo.js → stateDiagram-v2-MP3YSRHH-vktk3Xc5.js} +2 -2
  60. package/dist/{strings-BGW-fXVJ.js → strings-BYJNyKmg.js} +3 -3
  61. package/dist/style.css +1 -1
  62. package/dist/{swimlanes-42K2YHIH-BU14_Tej.js → swimlanes-42K2YHIH-Cjveh9OM.js} +1 -1
  63. package/dist/{swimlanesDiagram-VR7AAH4N-CieAnPqJ.js → swimlanesDiagram-VR7AAH4N-Bdr7Ev_2.js} +2 -2
  64. package/dist/{swiper-component-Dir1o8ml.js → swiper-component-Dvj-goaD.js} +2 -2
  65. package/dist/{timeline-definition-24CTP7MA-CrVj4E1K.js → timeline-definition-24CTP7MA-KIuDcefn.js} +1 -1
  66. package/dist/{toast-BWIPi3aF.js → toast-CkB5Jq9K.js} +4 -4
  67. package/dist/{tooltip-C3UnvsRe.js → tooltip-Bzl4k00l.js} +2 -2
  68. package/dist/{use-restore-focus-BWO0z1lo.js → use-restore-focus-CmflVLWJ.js} +1 -1
  69. package/dist/{useDateFormatter-ChB4eyim.js → useDateFormatter-CYo5fm8s.js} +2 -2
  70. package/dist/{useIframeCapabilities-cMNQ80CK.js → useIframeCapabilities-BIolGsF1.js} +1 -1
  71. package/dist/{useLifecycle-BquH9ET-.js → useLifecycle-GNgvNwI-.js} +2 -2
  72. package/dist/{useTheme-DgOLVISn.js → useTheme-BZ2GGLgm.js} +141 -139
  73. package/dist/{vega-component-Bwg_Kuh-.js → vega-component-DOtem7Hf.js} +7 -7
  74. package/dist/{vennDiagram-4TSXK5OY-ZLxQ8yhm.js → vennDiagram-4TSXK5OY-DrIOUHfb.js} +1 -1
  75. package/dist/{wardleyDiagram-VM6X3IG4-D88pcLY1.js → wardleyDiagram-VM6X3IG4-CJNjf4OH.js} +1 -1
  76. package/dist/{xychartDiagram-S5SC5T6Z-_13d4g8g.js → xychartDiagram-S5SC5T6Z-DEshFho-.js} +1 -1
  77. package/package.json +1 -1
  78. package/src/__tests__/fixtures/pair-config.ts +1 -1
  79. package/src/components/app-config/__tests__/state.test.tsx +50 -0
  80. package/src/components/app-config/ai-config.tsx +1 -1
  81. package/src/components/chat/__tests__/capabilities-popover.test.tsx +0 -1
  82. package/src/components/chat/__tests__/chat-display.test.tsx +125 -0
  83. package/src/components/chat/__tests__/chat-markdown.test.tsx +43 -0
  84. package/src/components/chat/__tests__/chat-utils.test.ts +51 -1
  85. package/src/components/chat/acp/__tests__/agent-panel-initialization.test.tsx +1 -1
  86. package/src/components/chat/acp/__tests__/agent-panel.test.tsx +22 -17
  87. package/src/components/chat/acp/agent-panel.tsx +4 -3
  88. package/src/components/chat/acp/blocks.tsx +10 -10
  89. package/src/components/chat/acp/common.tsx +4 -5
  90. package/src/components/chat/acp/model-selector.tsx +1 -1
  91. package/src/components/chat/capabilities-popover.tsx +1 -6
  92. package/src/components/chat/chat-components.tsx +7 -4
  93. package/src/components/chat/chat-display.tsx +51 -6
  94. package/src/components/chat/chat-history-popover.tsx +6 -1
  95. package/src/components/chat/chat-markdown.tsx +15 -0
  96. package/src/components/chat/chat-panel.tsx +105 -119
  97. package/src/components/chat/chat-typography.css +108 -0
  98. package/src/components/chat/chat-utils.ts +8 -3
  99. package/src/components/chat/notebook-access-selector.tsx +1 -1
  100. package/src/components/chat/reasoning-accordion.tsx +3 -3
  101. package/src/components/chat/tool-call/tool-activity-group.tsx +49 -0
  102. package/src/components/chat/tool-call/tool-history-row.tsx +3 -30
  103. package/src/components/chat/tool-call/tool-result.tsx +1 -1
  104. package/src/components/chat/use-chat-generation.ts +19 -0
  105. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +18 -37
  106. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +5 -8
  107. package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +5 -19
  108. package/src/components/editor/actions/pair-with-agent-commands.ts +0 -1
  109. package/src/components/editor/actions/pair-with-agent-modal.tsx +7 -11
  110. package/src/components/editor/actions/pair-with-agent.tsx +3 -31
  111. package/src/components/editor/actions/useCellActionButton.tsx +1 -1
  112. package/src/components/editor/ai/ai-completion-editor.tsx +1 -1
  113. package/src/components/editor/ai/prompt-input.tsx +189 -0
  114. package/src/components/editor/cell/StagedAICell.tsx +3 -3
  115. package/src/components/editor/cell/code/cell-editor.tsx +14 -1
  116. package/src/components/editor/chrome/panels/panel-header.tsx +13 -0
  117. package/src/components/editor/chrome/types.ts +1 -1
  118. package/src/components/editor/chrome/wrapper/__tests__/pending-ai-cells.test.tsx +53 -45
  119. package/src/components/editor/chrome/wrapper/__tests__/useAskAiAboutCell.test.tsx +58 -0
  120. package/src/components/editor/chrome/wrapper/app-chrome.tsx +89 -72
  121. package/src/components/editor/chrome/wrapper/pending-ai-cells.tsx +5 -8
  122. package/src/components/editor/chrome/wrapper/useAskAiAboutCell.ts +36 -0
  123. package/src/components/editor/renderers/__tests__/add-cell-buttons.test.tsx +18 -0
  124. package/src/components/editor/renderers/cell-array.tsx +10 -67
  125. package/src/components/markdown/markdown-renderer.tsx +35 -20
  126. package/src/components/mcp/mcp-status-indicator.tsx +18 -14
  127. package/src/core/ai/__tests__/staged-cells.test.ts +1 -467
  128. package/src/core/ai/context/context.ts +2 -0
  129. package/src/core/ai/context/providers/__tests__/cell.test.ts +49 -0
  130. package/src/core/ai/context/providers/cell.ts +54 -0
  131. package/src/core/ai/staged-cells.ts +4 -280
  132. package/src/core/ai/state.ts +3 -0
  133. package/src/core/codemirror/__tests__/setup.test.ts +32 -0
  134. package/src/core/codemirror/cells/state.ts +1 -0
  135. package/src/core/codemirror/cm.ts +5 -1
  136. package/src/core/codemirror/config/types.ts +1 -1
  137. package/src/core/codemirror/language/languages/python.ts +3 -3
  138. package/src/core/codemirror/placeholder/__tests__/extensions.test.ts +16 -0
  139. package/src/core/codemirror/placeholder/extensions.ts +57 -19
  140. package/src/core/hotkeys/hotkeys.ts +2 -1
  141. package/src/core/notebook/document/__tests__/codemirror.test.ts +1 -0
  142. package/src/plugins/impl/chat/__tests__/chat-prompts.test.tsx +1 -1
  143. package/src/plugins/impl/chat/chat-ui.tsx +1 -1
  144. package/src/components/editor/ai/__tests__/staged-cell-submission.test.ts +0 -106
  145. package/src/components/editor/ai/add-cell-with-ai.tsx +0 -506
  146. package/src/components/editor/ai/staged-cell-submission.ts +0 -46
  147. package/src/components/editor/cell/__tests__/StagedAICell.test.tsx +0 -66
@@ -4,6 +4,7 @@ import React, {
4
4
  Suspense,
5
5
  useEffect,
6
6
  useMemo,
7
+ useState,
7
8
  } from "react";
8
9
  import {
9
10
  type ImperativePanelHandle,
@@ -33,6 +34,7 @@ import { cn } from "@/utils/cn";
33
34
  import { ErrorBoundary } from "../../boundary/ErrorBoundary";
34
35
  import { raf2 } from "../../navigation/focus-utils";
35
36
  import { ContextAwarePanel } from "../panels/context-aware-panel/context-aware-panel";
37
+ import { PanelHeaderActionsContext } from "../panels/panel-header";
36
38
  import { PanelSectionProvider } from "../panels/panel-context";
37
39
  import { RuntimeStatusToggle } from "../panels/runtime-status-toggle";
38
40
  import { useTheme } from "@/theme/useTheme";
@@ -327,99 +329,114 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
327
329
  cache: <LazyCachePanel.Component />,
328
330
  };
329
331
 
332
+ const [headerActionsTarget, setHeaderActionsTarget] =
333
+ useState<HTMLDivElement | null>(null);
334
+
330
335
  const helpPaneBody = (
331
336
  <ErrorBoundary>
332
337
  <PanelSectionProvider value="sidebar">
333
- <div className="flex flex-col h-full flex-1 overflow-hidden mr-[-4px]">
334
- <div className="p-3 border-b flex justify-between items-center">
335
- {selectedPanel === "dependencies" ? (
336
- <div className="flex items-center justify-between flex-1">
337
- <span className="text-sm text-(--slate-11) uppercase tracking-wide font-semibold">
338
- Dependencies
339
- </span>
338
+ <PanelHeaderActionsContext.Provider
339
+ value={
340
+ selectedPanel === "ai" && (!agentsEnabled || aiPanelTab === "chat")
341
+ ? headerActionsTarget
342
+ : null
343
+ }
344
+ >
345
+ <div className="@container flex flex-col h-full flex-1 overflow-hidden mr-[-4px]">
346
+ <div className="p-3 border-b flex shrink-0 flex-wrap justify-between items-center gap-y-1">
347
+ {selectedPanel === "dependencies" ? (
348
+ <div className="flex items-center justify-between flex-1">
349
+ <span className="text-sm text-(--slate-11) uppercase tracking-wide font-semibold">
350
+ Dependencies
351
+ </span>
352
+ <Tabs
353
+ value={dependencyPanelTab}
354
+ onValueChange={(value) => {
355
+ if (value === "minimap" || value === "graph") {
356
+ setDependencyPanelTab(value);
357
+ }
358
+ }}
359
+ >
360
+ <TabsList>
361
+ <TabsTrigger
362
+ value="minimap"
363
+ className="py-0.5 text-xs uppercase tracking-wide font-bold"
364
+ >
365
+ Minimap
366
+ </TabsTrigger>
367
+ <TabsTrigger
368
+ value="graph"
369
+ className="py-0.5 text-xs uppercase tracking-wide font-bold"
370
+ >
371
+ Graph
372
+ </TabsTrigger>
373
+ </TabsList>
374
+ </Tabs>
375
+ </div>
376
+ ) : selectedPanel === "ai" && agentsEnabled ? (
340
377
  <Tabs
341
- value={dependencyPanelTab}
378
+ value={aiPanelTab}
342
379
  onValueChange={(value) => {
343
- if (value === "minimap" || value === "graph") {
344
- setDependencyPanelTab(value);
380
+ if (value === "chat" || value === "agents") {
381
+ setAiPanelTab(value);
345
382
  }
346
383
  }}
347
384
  >
348
385
  <TabsList>
349
386
  <TabsTrigger
350
- value="minimap"
387
+ value="chat"
351
388
  className="py-0.5 text-xs uppercase tracking-wide font-bold"
352
389
  >
353
- Minimap
390
+ Chat
354
391
  </TabsTrigger>
355
392
  <TabsTrigger
356
- value="graph"
393
+ value="agents"
357
394
  className="py-0.5 text-xs uppercase tracking-wide font-bold"
358
395
  >
359
- Graph
396
+ Agents
360
397
  </TabsTrigger>
361
398
  </TabsList>
362
399
  </Tabs>
363
- </div>
364
- ) : selectedPanel === "ai" && agentsEnabled ? (
365
- <Tabs
366
- value={aiPanelTab}
367
- onValueChange={(value) => {
368
- if (value === "chat" || value === "agents") {
369
- setAiPanelTab(value);
370
- }
371
- }}
400
+ ) : (
401
+ <span className="text-sm text-(--slate-11) uppercase tracking-wide font-semibold flex-1">
402
+ {selectedPanel === "ai" ? "Chat" : selectedPanel}
403
+ </span>
404
+ )}
405
+ <div
406
+ ref={setHeaderActionsTarget}
407
+ className="sidebar-panel-actions ml-auto min-w-0"
408
+ />
409
+ {selectedPanel === "packages" && (
410
+ <RuntimeStatusToggle section="sidebar" />
411
+ )}
412
+ <Button
413
+ data-testid="close-helper-pane"
414
+ className="m-0"
415
+ size="xs"
416
+ variant="text"
417
+ onClick={() => setIsSidebarOpen(false)}
372
418
  >
373
- <TabsList>
374
- <TabsTrigger
375
- value="chat"
376
- className="py-0.5 text-xs uppercase tracking-wide font-bold"
377
- >
378
- Chat
379
- </TabsTrigger>
380
- <TabsTrigger
381
- value="agents"
382
- className="py-0.5 text-xs uppercase tracking-wide font-bold"
419
+ <XIcon className="w-4 h-4" />
420
+ </Button>
421
+ </div>
422
+ <Suspense>
423
+ <TooltipProvider>
424
+ {Object.entries(SIDEBAR_PANELS).map(([key, Panel]) => (
425
+ <LazyActivity
426
+ key={key}
427
+ mode={
428
+ isSidebarOpen && selectedPanel === key
429
+ ? "visible"
430
+ : "hidden"
431
+ }
383
432
  >
384
- Agents
385
- </TabsTrigger>
386
- </TabsList>
387
- </Tabs>
388
- ) : (
389
- <span className="text-sm text-(--slate-11) uppercase tracking-wide font-semibold flex-1">
390
- {selectedPanel}
391
- </span>
392
- )}
393
- {selectedPanel === "packages" && (
394
- <RuntimeStatusToggle section="sidebar" />
395
- )}
396
- <Button
397
- data-testid="close-helper-pane"
398
- className="m-0"
399
- size="xs"
400
- variant="text"
401
- onClick={() => setIsSidebarOpen(false)}
402
- >
403
- <XIcon className="w-4 h-4" />
404
- </Button>
433
+ {Panel}
434
+ </LazyActivity>
435
+ ))}
436
+ </TooltipProvider>
437
+ </Suspense>
405
438
  </div>
406
- <Suspense>
407
- <TooltipProvider>
408
- {Object.entries(SIDEBAR_PANELS).map(([key, Panel]) => (
409
- <LazyActivity
410
- key={key}
411
- mode={
412
- isSidebarOpen && selectedPanel === key
413
- ? "visible"
414
- : "hidden"
415
- }
416
- >
417
- {Panel}
418
- </LazyActivity>
419
- ))}
420
- </TooltipProvider>
421
- </Suspense>
422
- </div>
439
+ </PanelHeaderActionsContext.Provider>
423
440
  </PanelSectionProvider>
424
441
  </ErrorBoundary>
425
442
  );
@@ -562,7 +579,7 @@ export const AppChrome: React.FC<PropsWithChildren> = ({ children }) => {
562
579
  {/* Panel content */}
563
580
  <Suspense fallback={<div />}>
564
581
  <PanelSectionProvider value="developer-panel">
565
- <div className="flex-1 overflow-hidden">
582
+ <div className="flex flex-col flex-1 min-h-0 overflow-hidden">
566
583
  {Object.entries(DEVELOPER_PANELS).map(([key, Panel]) => (
567
584
  <LazyActivity
568
585
  key={key}
@@ -4,11 +4,8 @@ import { useAtomValue, useStore } from "jotai";
4
4
  import { ChevronDown, ChevronUp, SparklesIcon } from "lucide-react";
5
5
  import { useState } from "react";
6
6
  import { Button } from "@/components/ui/button";
7
- import {
8
- stagedAICellsAtom,
9
- stagedGenerationInProgressAtom,
10
- useStagedCells,
11
- } from "@/core/ai/staged-cells";
7
+ import { stagedAICellsAtom, useStagedCells } from "@/core/ai/staged-cells";
8
+ import { chatGenerationInProgressAtom } from "@/core/ai/state";
12
9
  import type { CellId } from "@/core/cells/ids";
13
10
  import { getNextIndex } from "@/utils/arrays";
14
11
  import { cn } from "@/utils/cn";
@@ -24,7 +21,7 @@ export const PendingAICells: React.FC = () => {
24
21
  const [currentIndex, setCurrentIndex] = useState<number | null>(null);
25
22
 
26
23
  const stagedAiCells = useAtomValue(stagedAICellsAtom);
27
- const generationInProgress = useAtomValue(stagedGenerationInProgressAtom);
24
+ const isGenerating = useAtomValue(chatGenerationInProgressAtom);
28
25
  const listStagedCells = [...stagedAiCells.keys()];
29
26
  const store = useStore();
30
27
  const { deleteStagedCell, removeStagedCell } = useStagedCells(store);
@@ -108,7 +105,7 @@ export const PendingAICells: React.FC = () => {
108
105
  <AcceptCompletionButton
109
106
  multipleCompletions={true}
110
107
  onAccept={acceptAllCompletions}
111
- isLoading={generationInProgress}
108
+ isLoading={isGenerating}
112
109
  label="Keep all"
113
110
  size="xs"
114
111
  buttonStyles="h-6.5"
@@ -118,7 +115,7 @@ export const PendingAICells: React.FC = () => {
118
115
  <RejectCompletionButton
119
116
  multipleCompletions={true}
120
117
  onDecline={rejectAllCompletions}
121
- disabled={generationInProgress}
118
+ disabled={isGenerating}
122
119
  size="xs"
123
120
  label="Discard all"
124
121
  className="h-6.5"
@@ -0,0 +1,36 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { useSetAtom, useStore } from "jotai";
4
+ import useEvent from "react-use-event-hook";
5
+ import { useAIConfigActions } from "@/core/ai/config";
6
+ import { CellContextProvider } from "@/core/ai/context/providers/cell";
7
+ import { pendingAiPromptAtom } from "@/core/ai/state";
8
+ import type { CellId } from "@/core/cells/ids";
9
+ import { aiAtom } from "@/core/config/config";
10
+ import { Logger } from "@/utils/Logger";
11
+ import { useChromeActions } from "../state";
12
+ import { aiPanelTabAtom } from "./useAiPanel";
13
+
14
+ export function useAskAiAboutCell() {
15
+ const store = useStore();
16
+ const { openApplication } = useChromeActions();
17
+ const setAiPanelTab = useSetAtom(aiPanelTabAtom);
18
+ const { saveModeChange } = useAIConfigActions();
19
+ const setPendingPrompt = useSetAtom(pendingAiPromptAtom);
20
+
21
+ return useEvent(async (cellId: CellId) => {
22
+ const mode = store.get(aiAtom)?.mode;
23
+ if (mode !== "agent" && mode !== "code_mode") {
24
+ await saveModeChange("agent").catch(() =>
25
+ Logger.error("Failed to save mode change"),
26
+ );
27
+ }
28
+ setPendingPrompt({
29
+ prompt: `@${new CellContextProvider(store).asURI(cellId)} `,
30
+ submit: false,
31
+ append: true,
32
+ });
33
+ setAiPanelTab("chat");
34
+ openApplication("ai");
35
+ });
36
+ }
@@ -0,0 +1,18 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { render, screen } from "@testing-library/react";
4
+ import { createStore, Provider } from "jotai";
5
+ import { expect, it } from "vitest";
6
+ import type { CellColumnId } from "@/core/cells/layout";
7
+ import { AddCellButtons } from "../cell-array";
8
+
9
+ it("offers only cell languages in the add-cell tray", () => {
10
+ render(
11
+ <Provider store={createStore()}>
12
+ <AddCellButtons columnId={"column-1" as CellColumnId} />
13
+ </Provider>,
14
+ );
15
+ expect(
16
+ screen.getAllByRole("button").map((button) => button.textContent),
17
+ ).toEqual(["Python", "Markdown", "SQL"]);
18
+ });
@@ -6,27 +6,18 @@ import {
6
6
  verticalListSortingStrategy,
7
7
  } from "@dnd-kit/sortable";
8
8
  import { useAtomValue } from "jotai";
9
- import {
10
- DatabaseIcon,
11
- SparklesIcon,
12
- SquareCodeIcon,
13
- SquareMIcon,
14
- } from "lucide-react";
9
+ import { DatabaseIcon, SquareCodeIcon, SquareMIcon } from "lucide-react";
15
10
  import { useEffect } from "react";
16
- import { useOpenSettingsToTab } from "@/components/app-config/state";
17
11
  import { StartupLogsAlert } from "@/components/editor/alerts/startup-logs-alert";
18
12
  import { Cell } from "@/components/editor/notebook-cell";
19
13
  import { PackageAlert } from "@/components/editor/package-alert";
20
14
  import { SortableCellsProvider } from "@/components/sort/SortableCellsProvider";
21
15
  import { Button } from "@/components/ui/button";
22
- import { Tooltip } from "@/components/ui/tooltip";
23
16
  import { maybeAddMarimoImport } from "@/core/cells/add-missing-import";
24
17
  import { SETUP_CELL_ID } from "@/core/cells/ids";
25
18
  import { LanguageAdapters } from "@/core/codemirror/language/LanguageAdapters";
26
19
  import { MARKDOWN_INITIAL_HIDE_CODE } from "@/core/codemirror/language/languages/markdown";
27
- import { aiEnabledAtom, aiFeaturesEnabledAtom } from "@/core/config/config";
28
20
  import { canInteractWithAppAtom } from "@/core/network/connection";
29
- import { useBoolean } from "@/hooks/useBoolean";
30
21
  import { cn } from "@/utils/cn";
31
22
  import { Functions } from "@/utils/functions";
32
23
  import { type CellColumnId, findColumn, useLayout } from "@/core/cells/layout";
@@ -38,7 +29,6 @@ import type { AppConfig, UserConfig } from "../../../core/config/config-schema";
38
29
  import type { AppMode } from "../../../core/mode";
39
30
  import { useHotkey } from "../../../hooks/useHotkey";
40
31
  import { type Theme, useTheme } from "../../../theme/useTheme";
41
- import { AddCellWithAI } from "../ai/add-cell-with-ai";
42
32
  import { NotStartedConnectionAlert } from "../alerts/connecting-alert";
43
33
  import { FloatingOutline } from "../chrome/panels/outline/floating-outline";
44
34
  import { useChromeActions } from "../chrome/state";
@@ -259,29 +249,26 @@ const CellColumn: React.FC<{
259
249
  );
260
250
  };
261
251
 
262
- const AddCellButtons: React.FC<{
252
+ export const AddCellButtons: React.FC<{
263
253
  columnId: CellColumnId;
264
254
  className?: string;
265
255
  }> = ({ columnId, className }) => {
266
256
  const { createCell } = useCellActions();
267
- const [isAiButtonOpen, isAiButtonOpenActions] = useBoolean(false);
268
- const aiEnabled = useAtomValue(aiEnabledAtom);
269
- const aiFeaturesEnabled = useAtomValue(aiFeaturesEnabledAtom);
270
257
  const canInteractWithApp = useAtomValue(canInteractWithAppAtom);
271
- const { handleClick } = useOpenSettingsToTab();
272
258
 
273
259
  const buttonClass = cn(
274
260
  "mb-0 rounded-none sm:px-4 md:px-5 lg:px-8 tracking-wide no-wrap whitespace-nowrap",
275
261
  "font-semibold opacity-70 hover:opacity-90 uppercase text-xs",
276
262
  );
277
263
 
278
- const renderBody = () => {
279
- if (aiEnabled && isAiButtonOpen) {
280
- return <AddCellWithAI onClose={isAiButtonOpenActions.toggle} />;
281
- }
282
-
283
- return (
284
- <>
264
+ return (
265
+ <div className="flex justify-center mt-4 pt-6 pb-32 group gap-4 w-full print:hidden">
266
+ <div
267
+ className={cn(
268
+ "border border-border rounded transition-all duration-200 overflow-hidden divide-x divide-border flex w-fit shadow-sm-solid-shade",
269
+ className,
270
+ )}
271
+ >
285
272
  <Button
286
273
  className={buttonClass}
287
274
  variant="text"
@@ -327,50 +314,6 @@ const AddCellButtons: React.FC<{
327
314
  <DatabaseIcon className="mr-2 size-4 shrink-0" />
328
315
  SQL
329
316
  </Button>
330
- {aiEnabled && (
331
- <Tooltip
332
- content={
333
- aiFeaturesEnabled ? null : (
334
- <span>AI provider not found or Edit model not selected</span>
335
- )
336
- }
337
- delayDuration={100}
338
- asChild={false}
339
- >
340
- <Button
341
- className={buttonClass}
342
- variant="text"
343
- size="sm"
344
- disabled={!canInteractWithApp}
345
- onClick={
346
- aiFeaturesEnabled
347
- ? isAiButtonOpenActions.toggle
348
- : () => handleClick("ai", "ai-providers")
349
- }
350
- >
351
- <SparklesIcon className="mr-2 size-4 shrink-0" />
352
- Generate with AI
353
- </Button>
354
- </Tooltip>
355
- )}
356
- </>
357
- );
358
- };
359
-
360
- return (
361
- <div className="flex justify-center mt-4 pt-6 pb-32 group gap-4 w-full print:hidden">
362
- <div
363
- className={cn(
364
- "border border-border rounded transition-all duration-200 overflow-hidden divide-x divide-border flex",
365
- !isAiButtonOpen && "w-fit shadow-sm-solid-shade",
366
- isAiButtonOpen &&
367
- "w-full max-w-4xl shadow-md-solid-shade shadow-(color:--blue-3)",
368
- className,
369
- // Always show the AI input when it's open
370
- isAiButtonOpen && "opacity-100",
371
- )}
372
- >
373
- {renderBody()}
374
317
  </div>
375
318
  </div>
376
319
  );
@@ -2,8 +2,7 @@
2
2
 
3
3
  import { EditorView } from "@codemirror/view";
4
4
  import { useAtomValue } from "jotai";
5
- import { BetweenHorizontalStartIcon } from "lucide-react";
6
- import { memo, Suspense, useState } from "react";
5
+ import { createContext, memo, Suspense, useContext, useState } from "react";
7
6
  import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
8
7
  import {
9
8
  defaultRehypePlugins,
@@ -25,6 +24,8 @@ import "./markdown-renderer.css";
25
24
 
26
25
  const extensions = [EditorView.lineWrapping];
27
26
 
27
+ const NotebookInsertionContext = createContext(true);
28
+
28
29
  interface CodeBlockProps {
29
30
  code: string;
30
31
  language: string | undefined;
@@ -91,13 +92,13 @@ const InsertCodeBlockButton = ({ code, language }: CodeBlockProps) => {
91
92
  return (
92
93
  <Button size="xs" variant="outline" onClick={handleInsertCode}>
93
94
  Add to Notebook
94
- <BetweenHorizontalStartIcon className="ml-2 h-4 w-4" />
95
95
  </Button>
96
96
  );
97
97
  };
98
98
 
99
99
  const CodeBlock = ({ code, language }: CodeBlockProps) => {
100
100
  const { theme } = useTheme();
101
+ const allowNotebookInsertion = useContext(NotebookInsertionContext);
101
102
  const [value, setValue] = useState(code);
102
103
 
103
104
  if (value !== code) {
@@ -122,11 +123,13 @@ const CodeBlock = ({ code, language }: CodeBlockProps) => {
122
123
  onChange={setValue}
123
124
  />
124
125
  </Suspense>
125
- <div className="flex justify-end mt-2 space-x-2">
126
+ <div className="flex justify-end mt-3 gap-2">
126
127
  <CopyButton size="xs" variant="outline" onClick={handleCopyCode}>
127
128
  Copy
128
129
  </CopyButton>
129
- <InsertCodeBlockButton code={value} language={language} />
130
+ {allowNotebookInsertion && (
131
+ <InsertCodeBlockButton code={value} language={language} />
132
+ )}
130
133
  </div>
131
134
  </div>
132
135
  );
@@ -174,8 +177,10 @@ const COMPONENTS: Components = {
174
177
  if (language && typeof children === "string") {
175
178
  const code = children.trim();
176
179
  return (
177
- <div>
178
- <div className="text-xs text-muted-foreground pl-1">{language}</div>
180
+ <div className="my-4">
181
+ <div className="mb-1 text-xs text-muted-foreground pl-1">
182
+ {language}
183
+ </div>
179
184
  <CodeBlock code={code} language={language} />
180
185
  </div>
181
186
  );
@@ -199,17 +204,27 @@ const useMathPlugin = () => {
199
204
  return plugins;
200
205
  };
201
206
 
202
- export const MarkdownRenderer = memo(({ content }: { content: string }) => {
203
- const plugins = useMathPlugin();
204
- return (
205
- <Streamdown
206
- components={COMPONENTS}
207
- plugins={plugins}
208
- rehypePlugins={REHYPE_PLUGINS}
209
- className="mo-markdown-renderer"
210
- >
211
- {content}
212
- </Streamdown>
213
- );
214
- });
207
+ export const MarkdownRenderer = memo(
208
+ ({
209
+ content,
210
+ allowNotebookInsertion = true,
211
+ }: {
212
+ content: string;
213
+ allowNotebookInsertion?: boolean;
214
+ }) => {
215
+ const plugins = useMathPlugin();
216
+ return (
217
+ <NotebookInsertionContext.Provider value={allowNotebookInsertion}>
218
+ <Streamdown
219
+ components={COMPONENTS}
220
+ plugins={plugins}
221
+ rehypePlugins={REHYPE_PLUGINS}
222
+ className="mo-markdown-renderer"
223
+ >
224
+ {content}
225
+ </Streamdown>
226
+ </NotebookInsertionContext.Provider>
227
+ );
228
+ },
229
+ );
215
230
  MarkdownRenderer.displayName = "MarkdownRenderer";
@@ -11,7 +11,7 @@ import { useMCPStatus } from "./hooks";
11
11
 
12
12
  /**
13
13
  * MCP Status indicator component
14
- * Shows a small icon with status color and a popover with detailed information
14
+ * Shows connection details and highlights states that need attention
15
15
  */
16
16
  export const MCPStatusIndicator: React.FC = () => {
17
17
  const { data: status, refetch, isFetching } = useMCPStatus();
@@ -41,13 +41,17 @@ export const MCPStatusIndicator: React.FC = () => {
41
41
  <Popover>
42
42
  <Tooltip content="MCP Status">
43
43
  <PopoverTrigger asChild={true}>
44
- <Button aria-label="MCP status" variant="text" size="icon">
44
+ <Button
45
+ className="chat-control"
46
+ aria-label="MCP status"
47
+ variant="text"
48
+ size="icon"
49
+ >
45
50
  <PlugIcon
46
51
  className={cn(
47
- "h-4 w-4",
48
- status?.status === "ok" && "text-green-500",
49
- status?.status === "partial" && "text-yellow-500",
50
- status?.status === "error" && hasServers && "text-red-500",
52
+ "h-4 w-4 text-muted-foreground",
53
+ status?.status === "partial" && "text-(--amber-11)",
54
+ status?.status === "error" && hasServers && "text-destructive",
51
55
  )}
52
56
  />
53
57
  </Button>
@@ -79,7 +83,7 @@ export const MCPStatusIndicator: React.FC = () => {
79
83
  </div>
80
84
  )}
81
85
  {status.error && (
82
- <div className="text-xs text-red-500 bg-red-50 dark:bg-red-950/20 p-2 rounded">
86
+ <div className="text-xs text-destructive bg-destructive/10 p-2 rounded">
83
87
  {status.error}
84
88
  </div>
85
89
  )}
@@ -129,13 +133,13 @@ export const McpStatusText: React.FC<{
129
133
  <span
130
134
  className={cn(
131
135
  "text-xs font-medium",
132
- status === "ok" && "text-green-500",
133
- status === "partial" && "text-yellow-500",
134
- status === "error" && "text-red-500",
135
- status === "failed" && "text-red-500",
136
- status === "disconnected" && "text-gray-500",
137
- status === "pending" && "text-yellow-500",
138
- status === "connected" && "text-green-500",
136
+ status === "ok" && "text-muted-foreground",
137
+ status === "partial" && "text-(--amber-11)",
138
+ status === "error" && "text-destructive",
139
+ status === "failed" && "text-destructive",
140
+ status === "disconnected" && "text-muted-foreground",
141
+ status === "pending" && "text-(--amber-11)",
142
+ status === "connected" && "text-muted-foreground",
139
143
  )}
140
144
  >
141
145
  {status}