@marimo-team/islands 0.25.3-dev4 → 0.25.3-dev6

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 (90) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNSxdKyJ.js → ConnectedDataExplorerComponent-CNMliVzw.js} +1 -1
  2. package/dist/{ErrorBoundary-CuU5CuVr.js → ErrorBoundary-Ca-Sa492.js} +1 -1
  3. package/dist/{abnfDiagram-VCTEODGH-DQDjNXt3.js → abnfDiagram-VCTEODGH-D-Ez91gV.js} +2 -2
  4. package/dist/{any-language-editor-B9REGlD9.js → any-language-editor-DZ_YTVus.js} +1 -1
  5. package/dist/{architectureDiagram-5GKGNRK7-Nmls7Wxn.js → architectureDiagram-5GKGNRK7-CTpcNtO8.js} +1 -1
  6. package/dist/{chat-ui-BDpymeoe.js → chat-ui-0lKdOoQ3.js} +436 -437
  7. package/dist/{chunk-IMKFNOWR-CoIFEo-1.js → chunk-IMKFNOWR-DGxi-zeo.js} +1 -1
  8. package/dist/{chunk-SHT3W25Y-lHiGx3ZC.js → chunk-SHT3W25Y-CV9J3rAB.js} +1 -1
  9. package/dist/{chunk-SVP7TREG-BvsHIrY_.js → chunk-SVP7TREG-BL-9Fqgg.js} +1 -1
  10. package/dist/{chunk-TICWLB2K-BC8YIm_n.js → chunk-TICWLB2K-Cu_XMjDM.js} +1 -1
  11. package/dist/{classDiagram-ZZMXUADV-Bt98lkkv.js → classDiagram-ZZMXUADV-BXXV13Hq.js} +2 -2
  12. package/dist/{classDiagram-v2-VYDZK3BY-D9bfbcC3.js → classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js} +2 -2
  13. package/dist/{code-visibility-Lu0dkrFO.js → code-visibility-NGRjhbtA.js} +14 -14
  14. package/dist/{common-DiWGWDIM.js → common-B7_ZSg0P.js} +6426 -6256
  15. package/dist/{cynefinDiagram-5FMLGOSQ-CgWra7wt.js → cynefinDiagram-5FMLGOSQ-CH96rwba.js} +1 -1
  16. package/dist/{dagre-GXQ25YYZ-CFZ8-35c.js → dagre-GXQ25YYZ-BD3PmpFY.js} +1 -1
  17. package/dist/{data-grid-overlay-editor-ga7ytgbj.js → data-grid-overlay-editor-B_cmrkBm.js} +1 -1
  18. package/dist/{diagram-S7CK7UJ4-lCNQrW7d.js → diagram-S7CK7UJ4-BsSJJldB.js} +1 -1
  19. package/dist/{diagram-UQ7AKVKN-CQPQ6pP_.js → diagram-UQ7AKVKN-muQtNZc4.js} +1 -1
  20. package/dist/{diagram-VX7I27RA-Cf3b0llz.js → diagram-VX7I27RA-CdhuZAqR.js} +1 -1
  21. package/dist/{diagram-Z3DM3KII-Bt4QLrpn.js → diagram-Z3DM3KII-BD8mXjOj.js} +1 -1
  22. package/dist/{ebnfDiagram-PWID7BFC-D3NAyPFY.js → ebnfDiagram-PWID7BFC-BIN1sweE.js} +2 -2
  23. package/dist/{erDiagram-RLTQ6QDP-Tlu09_Nm.js → erDiagram-RLTQ6QDP-Ctp5cfoN.js} +1 -1
  24. package/dist/{error-banner-Da600cwz.js → error-banner-D-WCIiMt.js} +10 -5
  25. package/dist/{cells-C8iCYvLU.js → error-entries-DSvFWT_M.js} +2262 -2206
  26. package/dist/{feedback-button-DQA-pzxi.js → feedback-button-CTF2Nj9s.js} +115 -116
  27. package/dist/{flowDiagram-HODETNUW-CAYbpJmV.js → flowDiagram-HODETNUW-3EfLrlFb.js} +2 -2
  28. package/dist/{formats-BGh2thRc.js → formats-CeOmU1MU.js} +1 -1
  29. package/dist/{glide-data-editor-Bv75MH6g.js → glide-data-editor-BQHF9puC.js} +3 -3
  30. package/dist/{globals-EDQGFIvi.js → globals-Cj8iqMEp.js} +1 -1
  31. package/dist/{html-to-image-CZY1pIZH.js → html-to-image-B8s5SgGm.js} +23 -23
  32. package/dist/{infoDiagram-27XIBGKW-XrDxhr0x.js → infoDiagram-27XIBGKW-DipQoHxl.js} +1 -1
  33. package/dist/{ishikawaDiagram-5VMMS53U-CD1muzov.js → ishikawaDiagram-5VMMS53U-DgFMjSGz.js} +1 -1
  34. package/dist/{kanban-definition-UXKFOSKX-Dg9W142_.js → kanban-definition-UXKFOSKX-w1ACVjgl.js} +1 -1
  35. package/dist/main.js +164 -163
  36. package/dist/{mermaid-CiAXjLpA.js → mermaid-CHo49Yv-.js} +33 -33
  37. package/dist/{mindmap-definition-YA3MSWOX-DgCidgYJ.js → mindmap-definition-YA3MSWOX-CrT0KSfX.js} +1 -1
  38. package/dist/{number-overlay-editor-BCP3MxhC.js → number-overlay-editor-DBcnm0SY.js} +1 -1
  39. package/dist/{pegDiagram-XKGWAZYB-coOYYKB6.js → pegDiagram-XKGWAZYB-BqvywNJA.js} +2 -2
  40. package/dist/{pieDiagram-E7YTZNPT-BnXTc3bj.js → pieDiagram-E7YTZNPT-BfxJ_XCc.js} +1 -1
  41. package/dist/{process-output-e4jj9Ijt.js → process-output-CKRXyszb.js} +3 -3
  42. package/dist/{railroadDiagram-O6MQD6OU-B28-u83n.js → railroadDiagram-O6MQD6OU-8vFLVTPu.js} +2 -2
  43. package/dist/{requirementDiagram-BXWQKSXE-CdYCTMu0.js → requirementDiagram-BXWQKSXE-a_MxGvxe.js} +1 -1
  44. package/dist/{reveal-component-DO5BJB9l.js → reveal-component-DTBSuGkC.js} +4 -4
  45. package/dist/{sequenceDiagram-WJ2MYXX4-CM3aRAe_.js → sequenceDiagram-WJ2MYXX4-CXFMnxGh.js} +1 -1
  46. package/dist/{stateDiagram-D77RDMKH-DOZUMVj8.js → stateDiagram-D77RDMKH-DQeA_CwI.js} +2 -2
  47. package/dist/{stateDiagram-v2-MP3YSRHH-CISYxma6.js → stateDiagram-v2-MP3YSRHH-BxxQucm1.js} +2 -2
  48. package/dist/style.css +1 -1
  49. package/dist/{swimlanes-42K2YHIH-BkSppxp8.js → swimlanes-42K2YHIH-Do6L-cYg.js} +1 -1
  50. package/dist/{swimlanesDiagram-VR7AAH4N-c8G5pGxC.js → swimlanesDiagram-VR7AAH4N-DUzPvSVU.js} +2 -2
  51. package/dist/{timeline-definition-24CTP7MA-CaMQayqK.js → timeline-definition-24CTP7MA-D3c46WM5.js} +1 -1
  52. package/dist/{toDate-BNJf01u1.js → toDate-vIXljnsr.js} +39 -38
  53. package/dist/{vega-component-DG29TAB7.js → vega-component-DTG6zxaY.js} +3 -3
  54. package/dist/{vennDiagram-4TSXK5OY-CF8pfaK3.js → vennDiagram-4TSXK5OY-BSZD8JcZ.js} +1 -1
  55. package/dist/{wardleyDiagram-VM6X3IG4-dGPu39d-.js → wardleyDiagram-VM6X3IG4-48cUWkfA.js} +1 -1
  56. package/dist/{xychartDiagram-S5SC5T6Z-CP6-9q56.js → xychartDiagram-S5SC5T6Z-CswYgYeu.js} +1 -1
  57. package/package.json +1 -1
  58. package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  59. package/src/__tests__/mount.test.ts +8 -8
  60. package/src/components/editor/Output.tsx +9 -2
  61. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  62. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +186 -34
  63. package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
  64. package/src/components/editor/actions/pair-with-agent-modal.tsx +96 -36
  65. package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
  66. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +372 -0
  67. package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
  68. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
  69. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  70. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +548 -0
  71. package/src/components/editor/errors/send-error-report-button.tsx +69 -0
  72. package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
  73. package/src/components/editor/output/MarimoTracebackOutput.tsx +35 -20
  74. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
  75. package/src/components/editor/output/ansi-reduce.ts +6 -0
  76. package/src/core/attachments/__tests__/toast.test.tsx +58 -0
  77. package/src/core/attachments/state.ts +21 -0
  78. package/src/core/attachments/toast.ts +15 -0
  79. package/src/core/cells/__tests__/cells.test.ts +52 -0
  80. package/src/core/cells/__tests__/error-report.test.ts +139 -0
  81. package/src/core/cells/__tests__/session.test.ts +50 -1
  82. package/src/core/cells/error-report.ts +63 -0
  83. package/src/core/cells/session.ts +28 -1
  84. package/src/core/config/pair.ts +5 -3
  85. package/src/core/islands/__tests__/bootstrap.test.ts +26 -0
  86. package/src/core/islands/bootstrap.ts +1 -0
  87. package/src/core/websocket/__tests__/useMarimoKernelConnection.hook.test.tsx +193 -0
  88. package/src/core/websocket/useMarimoKernelConnection.tsx +9 -0
  89. package/src/mount.tsx +3 -3
  90. package/dist/error-entries-CzoYkIQZ.js +0 -56
@@ -0,0 +1,237 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { useAtomValue } from "jotai";
4
+ import {
5
+ BotIcon,
6
+ CheckIcon,
7
+ Loader2Icon,
8
+ UnplugIcon,
9
+ XIcon,
10
+ } from "lucide-react";
11
+ import { useCallback, useEffect, useRef, useState } from "react";
12
+ import { Button } from "@/components/ui/button";
13
+ import {
14
+ Popover,
15
+ PopoverContent,
16
+ PopoverTrigger,
17
+ } from "@/components/ui/popover";
18
+ import {
19
+ type Attachment,
20
+ agentAttachmentsAtom,
21
+ attachmentLabel,
22
+ } from "@/core/attachments/state";
23
+ import { agentToast } from "@/core/attachments/toast";
24
+ import { pairConfigAtom } from "@/core/config/pair";
25
+ import { stableSessionIdAtom } from "@/core/kernel/session";
26
+ import { API } from "@/core/network/api";
27
+ import { useTimeAgo } from "@/hooks/useFormatting";
28
+ import { FooterItem } from "../footer-item";
29
+
30
+ export function AttachmentStatus() {
31
+ const hostEnabled = useAtomValue(pairConfigAtom)?.hostEnabled;
32
+ const agents = useAtomValue(agentAttachmentsAtom);
33
+ const [open, setOpen] = useState(false);
34
+ const previous = useRef(new Set<string>());
35
+ const automatic = useRef(false);
36
+ const pointerInside = useRef(false);
37
+ const timeout = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
38
+
39
+ const stopTimer = useCallback(() => clearTimeout(timeout.current), []);
40
+ const startTimer = useCallback(() => {
41
+ stopTimer();
42
+ timeout.current = setTimeout(() => setOpen(false), 3000);
43
+ }, [stopTimer]);
44
+ const preserveEditorFocus = (event: Event) => {
45
+ if (automatic.current) {
46
+ event.preventDefault();
47
+ }
48
+ };
49
+
50
+ useEffect(() => {
51
+ if (!open) {
52
+ pointerInside.current = false;
53
+ }
54
+ const current = new Set(
55
+ hostEnabled ? agents.map((agent) => agent.identity ?? agent.id) : [],
56
+ );
57
+ const connected = [...current].some((id) => !previous.current.has(id));
58
+ for (const id of current) {
59
+ previous.current.add(id);
60
+ }
61
+ if (current.size === 0) {
62
+ stopTimer();
63
+ setOpen(false);
64
+ } else if (connected) {
65
+ if (open && !automatic.current) {
66
+ return;
67
+ }
68
+ automatic.current = true;
69
+ setOpen(true);
70
+ if (!pointerInside.current) {
71
+ startTimer();
72
+ }
73
+ }
74
+ }, [agents, hostEnabled, open, startTimer, stopTimer]);
75
+
76
+ useEffect(() => stopTimer, [stopTimer]);
77
+
78
+ if (!hostEnabled || agents.length === 0) {
79
+ return null;
80
+ }
81
+ const identities = new Set(agents.map((agent) => agent.identity ?? agent.id));
82
+ const label =
83
+ identities.size === 1
84
+ ? `${attachmentLabel(agents[0])} connected`
85
+ : `${identities.size} agents connected`;
86
+
87
+ return (
88
+ <Popover
89
+ open={open}
90
+ onOpenChange={(nextOpen) => {
91
+ automatic.current = false;
92
+ stopTimer();
93
+ setOpen(nextOpen);
94
+ }}
95
+ >
96
+ <PopoverTrigger asChild={true}>
97
+ <FooterItem
98
+ asChild={true}
99
+ tooltip={label}
100
+ selected={open}
101
+ data-testid="footer-attachment-status"
102
+ aria-label={label}
103
+ >
104
+ <button type="button">
105
+ <span className="flex items-center gap-1.5">
106
+ <BotIcon className="w-4 h-4" />
107
+ <span className="max-w-48 truncate">{label}</span>
108
+ </span>
109
+ </button>
110
+ </FooterItem>
111
+ </PopoverTrigger>
112
+ <PopoverContent
113
+ className="w-80 p-0 text-sm"
114
+ side="top"
115
+ align="start"
116
+ onOpenAutoFocus={preserveEditorFocus}
117
+ onCloseAutoFocus={preserveEditorFocus}
118
+ onPointerEnter={() => {
119
+ pointerInside.current = true;
120
+ stopTimer();
121
+ }}
122
+ onPointerLeave={() => {
123
+ pointerInside.current = false;
124
+ if (automatic.current) {
125
+ startTimer();
126
+ }
127
+ }}
128
+ onFocusCapture={() => {
129
+ automatic.current = false;
130
+ stopTimer();
131
+ }}
132
+ >
133
+ <h4 className="font-medium px-4 py-3 border-b">{label}</h4>
134
+ <div className="max-h-72 overflow-auto divide-y">
135
+ {agents.map((attachment) => (
136
+ <AttachmentStatusItem key={attachment.id} attachment={attachment} />
137
+ ))}
138
+ </div>
139
+ <p className="px-4 py-3 border-t text-xs text-muted-foreground">
140
+ Disconnect removes the notebook connection. The agent session keeps
141
+ running.
142
+ </p>
143
+ </PopoverContent>
144
+ </Popover>
145
+ );
146
+ }
147
+
148
+ function AttachmentStatusItem({ attachment }: { attachment: Attachment }) {
149
+ const stableSessionId = useAtomValue(stableSessionIdAtom);
150
+ const [state, setState] = useState<"idle" | "confirming" | "disconnecting">(
151
+ "idle",
152
+ );
153
+ const confirming = state !== "idle";
154
+ const disconnecting = state === "disconnecting";
155
+ const disconnectButton = useRef<HTMLButtonElement>(null);
156
+ const timeAgo = useTimeAgo();
157
+ const label = attachmentLabel(attachment);
158
+ const disconnectLabel = disconnecting
159
+ ? `Disconnecting ${label}`
160
+ : confirming
161
+ ? `Confirm disconnect ${label}`
162
+ : `Disconnect ${label}`;
163
+
164
+ const disconnect = async () => {
165
+ if (disconnecting || !stableSessionId) {
166
+ return;
167
+ }
168
+ setState("disconnecting");
169
+ try {
170
+ await API.post(
171
+ `/pair/notebooks/${encodeURIComponent(stableSessionId)}/attachments/detach`,
172
+ { attachment_id: attachment.id },
173
+ );
174
+ } catch {
175
+ agentToast({ variant: "danger", title: `Could not disconnect ${label}` });
176
+ } finally {
177
+ setState("idle");
178
+ }
179
+ };
180
+
181
+ return (
182
+ <div className="flex items-start gap-3 px-4 py-3">
183
+ <div className="min-w-0 flex-1">
184
+ <h5 className="font-medium">{label}</h5>
185
+ <p className="text-xs text-muted-foreground mt-1">
186
+ Connected {timeAgo(attachment.since)}
187
+ </p>
188
+ <p
189
+ className="text-xs font-mono text-muted-foreground truncate mt-1"
190
+ title={attachment.id}
191
+ >
192
+ {attachment.id}
193
+ </p>
194
+ </div>
195
+ <div className="flex shrink-0 items-center gap-1">
196
+ {confirming && (
197
+ <span className="text-xs text-muted-foreground">
198
+ {disconnecting ? "Disconnecting…" : "Disconnect?"}
199
+ </span>
200
+ )}
201
+ <Button
202
+ ref={disconnectButton}
203
+ size="icon"
204
+ variant="outlineDestructive"
205
+ disabled={disconnecting || !stableSessionId}
206
+ onClick={confirming ? disconnect : () => setState("confirming")}
207
+ aria-label={disconnectLabel}
208
+ title={disconnectLabel}
209
+ aria-busy={disconnecting}
210
+ >
211
+ {disconnecting ? (
212
+ <Loader2Icon className="size-3.5 animate-spin" aria-hidden={true} />
213
+ ) : confirming ? (
214
+ <CheckIcon className="size-3.5" aria-hidden={true} />
215
+ ) : (
216
+ <UnplugIcon className="size-3.5" aria-hidden={true} />
217
+ )}
218
+ </Button>
219
+ {confirming && (
220
+ <Button
221
+ size="icon"
222
+ variant="outline"
223
+ disabled={disconnecting}
224
+ onClick={() => {
225
+ setState("idle");
226
+ disconnectButton.current?.focus();
227
+ }}
228
+ aria-label={`Cancel disconnect ${label}`}
229
+ title={`Cancel disconnect ${label}`}
230
+ >
231
+ <XIcon className="size-3.5" aria-hidden={true} />
232
+ </Button>
233
+ )}
234
+ </div>
235
+ </div>
236
+ );
237
+ }
@@ -12,6 +12,7 @@ import { ShowInKioskMode } from "../../kiosk-mode";
12
12
  import { panelLayoutAtom, useChromeActions, useChromeState } from "../state";
13
13
  import { FooterItem } from "./footer-item";
14
14
  import { AIStatusIcon } from "./footer-items/ai-status";
15
+ import { AttachmentStatus } from "./footer-items/attachment-status";
15
16
  import {
16
17
  BackendConnectionStatus,
17
18
  connectionStatusAtom,
@@ -107,6 +108,7 @@ export const Footer: React.FC = () => {
107
108
 
108
109
  <div className="flex items-center shrink-0 min-w-0">
109
110
  <MachineStats />
111
+ <AttachmentStatus />
110
112
  <AIStatusIcon />
111
113
  <CopilotStatusIcon />
112
114
  <RTCStatus />