@marimo-team/frontend 0.19.3-dev4 → 0.19.3-dev42
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.
- package/dist/assets/{CellStatus-Ba6Af_Tb.js → CellStatus-b7Yo2X9j.js} +1 -1
- package/dist/assets/{ConnectedDataExplorerComponent-KlUs_Sz3.js → ConnectedDataExplorerComponent-Cr6-n9Em.js} +1 -1
- package/dist/assets/{ErrorBoundary-Drf1manw.js → ErrorBoundary-C7JBxSzd.js} +1 -1
- package/dist/assets/{ImperativeModal-q6QlC2aZ.js → ImperativeModal-DVhvP4lH.js} +1 -1
- package/dist/assets/{JsonOutput-4ruRfyOj.js → JsonOutput-C8Eo1zBR.js} +5 -5
- package/dist/assets/{LazyAnyLanguageCodeMirror-jpEDlD0M.js → LazyAnyLanguageCodeMirror-Cp2punaU.js} +2 -2
- package/dist/assets/{MarimoErrorOutput-DnjH3pD8.js → MarimoErrorOutput-CXBGzjO2.js} +2 -2
- package/dist/assets/{RenderHTML-DaJXe2U2.js → RenderHTML-SoetmcW2.js} +1 -1
- package/dist/assets/VisuallyHidden-B9t3FhTP.js +1 -0
- package/dist/assets/{add-cell-with-ai-Bsds_6SU.js → add-cell-with-ai-D2qS3Nos.js} +21 -21
- package/dist/assets/{add-database-form-CqIp3_WN.js → add-database-form-BBkiGMZ_.js} +2 -2
- package/dist/assets/agent-panel-BzV4XUTo.js +287 -0
- package/dist/assets/{ai-model-dropdown-LK8Wr5iu.js → ai-model-dropdown-CrMTCgo7.js} +1 -1
- package/dist/assets/{alert-dialog-k5KxevGr.js → alert-dialog-jcHA5geR.js} +1 -1
- package/dist/assets/{any-language-editor-DQu1Tt2N.js → any-language-editor-Cm83E7D_.js} +1 -1
- package/dist/assets/{app-config-button-BaVc4Y5z.js → app-config-button-9izWmQ0X.js} +1 -1
- package/dist/assets/button-B8cGZzP5.js +1 -0
- package/dist/assets/{cache-panel-C1So4Zu3.js → cache-panel-1FqnpB9y.js} +1 -1
- package/dist/assets/cell-editor-Do6lWWk9.js +23 -0
- package/dist/assets/cell-link-BP7_Ns0N.js +1 -0
- package/dist/assets/{cells-KYKWFk6C.js → cells-Cv9PtwL9.js} +49 -49
- package/dist/assets/{chat-components-O6DUIpBx.js → chat-components-Be6BPrbT.js} +1 -1
- package/dist/assets/{chat-display-DD3KokYi.js → chat-display-BRKfnhbm.js} +1 -1
- package/dist/assets/{chat-panel-D4DIcOM1.js → chat-panel-71zcilvi.js} +2 -2
- package/dist/assets/client-CGOlSEYr.js +4 -0
- package/dist/assets/{column-preview-EpCGr4Xp.js → column-preview-MC6VOHbd.js} +1 -1
- package/dist/assets/{command-Dqe0kvHp.js → command-n_oMaKjl.js} +1 -1
- package/dist/assets/{command-palette-DWacsFDk.js → command-palette-DfZNcw7W.js} +1 -1
- package/dist/assets/common-MUZIZluQ.js +1 -0
- package/dist/assets/config-DFDEcYvy.js +1 -0
- package/dist/assets/context-DHfVoQfl.js +1 -0
- package/dist/assets/{copy-icon-B69c-352.js → copy-icon-jWsqdLn1.js} +1 -1
- package/dist/assets/{datasource-JeWYnuIr.js → datasource-CEsMStKs.js} +2 -2
- package/dist/assets/{dependency-graph-panel-BJibnwCO.js → dependency-graph-panel-CNTGbfLZ.js} +4 -4
- package/dist/assets/{dialog-DUEuLcT2.js → dialog-CF5DtF1E.js} +1 -1
- package/dist/assets/{dist-DOFFh6Ii.js → dist-Dg7UO_Vw.js} +1 -1
- package/dist/assets/{documentation-panel-B2W3q2YB.js → documentation-panel-Cb9AHO2C.js} +1 -1
- package/dist/assets/{download-NfnO_JCs.js → download-24bI2vH0.js} +1 -1
- package/dist/assets/edit-page-DSuXLdcn.js +12 -0
- package/dist/assets/{error-banner-DU5Qb8a8.js → error-banner-DvT0IGDZ.js} +1 -1
- package/dist/assets/{error-panel-Bv-7GYgJ.js → error-panel-CpYH0GfR.js} +1 -1
- package/dist/assets/{es-KtEicG7U.js → es-BITbuY9w.js} +1 -1
- package/dist/assets/{field-DDKGFzpC.js → field-Clr_fqUr.js} +1 -1
- package/dist/assets/{file-explorer-panel-CToUezud.js → file-explorer-panel-CdA81LHh.js} +1 -1
- package/dist/assets/{floating-outline-Db40vhG8.js → floating-outline-BbJ4ldyu.js} +1 -1
- package/dist/assets/{focus-BCdX47jS.js → focus-D1y1tXyC.js} +1 -1
- package/dist/assets/{form-DwtJQd_Z.js → form-BAtvsPJL.js} +2 -2
- package/dist/assets/{glide-data-editor-D_bRnWfy.js → glide-data-editor-Dv8ZW9dk.js} +1 -1
- package/dist/assets/{globals-MS86g8oR.js → globals-C6OH39EA.js} +1 -1
- package/dist/assets/{home-page-BfVf41OG.js → home-page-B_YprqxM.js} +2 -2
- package/dist/assets/house-CncUa_LL.js +1 -0
- package/dist/assets/index-C30GhE0W.css +2 -0
- package/dist/assets/index-DoE3JZXY.js +43 -0
- package/dist/assets/input-B80Yt1uu.js +1 -0
- package/dist/assets/{kiosk-mode-CEhvsEr0.js → kiosk-mode-DfyjlR7p.js} +1 -1
- package/dist/assets/{label-qwandMoh.js → label-CNZLffHW.js} +1 -1
- package/dist/assets/{layout-Cvaok8Kj.js → layout-9uQoV-6h.js} +4 -4
- package/dist/assets/links-DbDrjRnm.js +1 -0
- package/dist/assets/{logs-panel-J2FKnKaj.js → logs-panel-svcirwjp.js} +1 -1
- package/dist/assets/{markdown-renderer-BlG9DgUG.js → markdown-renderer-DlVqlHOL.js} +2 -2
- package/dist/assets/mode-PeuS_Lp-.js +1 -0
- package/dist/assets/{multi-map-fjX9ImVF.js → multi-map-CQd4MZr5.js} +1 -1
- package/dist/assets/name-cell-input-YMoA0SQj.js +1 -0
- package/dist/assets/{outline-panel-Doj3GJrQ.js → outline-panel-RKJ5Mqrt.js} +1 -1
- package/dist/assets/{packages-panel-nqWXQzKf.js → packages-panel-BuiAGEBw.js} +1 -1
- package/dist/assets/panels-BKsZUDjc.js +1 -0
- package/dist/assets/{process-output-DiSW8Nbo.js → process-output-KJWsSvCT.js} +1 -1
- package/dist/assets/{readonly-python-code-CKY5LsMp.js → readonly-python-code-HPlG_YPX.js} +1 -1
- package/dist/assets/run-page-CBDzVDX3.js +1 -0
- package/dist/assets/scratchpad-panel-CarbQVYs.js +1 -0
- package/dist/assets/{secrets-panel-CDWmmmBS.js → secrets-panel-BMY6PPth.js} +1 -1
- package/dist/assets/{select-D0g5GnIs.js → select-D9lTzMzP.js} +1 -1
- package/dist/assets/{session-panel-CGFRSBw9.js → session-panel-Cv14Ehfm.js} +1 -1
- package/dist/assets/{slides-component-MkPkpql1.js → slides-component-Dp0Yv5b0.js} +1 -1
- package/dist/assets/{snippets-panel-ClHeSpc5.js → snippets-panel-OAdQXQ93.js} +1 -1
- package/dist/assets/state-DYG6kYly.js +1 -0
- package/dist/assets/state-xh6GqNrp.js +1 -0
- package/dist/assets/{switch-BmbGJWHc.js → switch-DPeh0R76.js} +1 -1
- package/dist/assets/{terminal-BvgBa6Ri.js → terminal-BbAhzgnR.js} +1 -1
- package/dist/assets/{textarea-WklymBeK.js → textarea-wbzgrXvB.js} +1 -1
- package/dist/assets/{tracing-D0WYhZdr.js → tracing-Bh3EJxAS.js} +1 -1
- package/dist/assets/{tracing-panel-CNxN58z7.js → tracing-panel-BzSQ7qvB.js} +2 -2
- package/dist/assets/{types-BrgXpvGt.js → types-B8Qb1FfB.js} +1 -1
- package/dist/assets/{useAddCell-a9qZ0_KE.js → useAddCell-DBGvrN8K.js} +1 -1
- package/dist/assets/{useBoolean-5kuXz69O.js → useBoolean-CyOFPk5r.js} +1 -1
- package/dist/assets/{useCellActionButton-9W_R41MM.js → useCellActionButton-BlS_HKk-.js} +1 -1
- package/dist/assets/{useDateFormatter-CV0QXb5P.js → useDateFormatter-DsANziQR.js} +1 -1
- package/dist/assets/useDeleteCell-BvQIJfpI.js +1 -0
- package/dist/assets/{useDependencyPanelTab-0reaqvvh.js → useDependencyPanelTab-BqEhbPr2.js} +1 -1
- package/dist/assets/useInterval-BGPIviJp.js +1 -0
- package/dist/assets/useNotebookActions-D1Woz3AV.js +1 -0
- package/dist/assets/{useNumberFormatter-D8ks3oPN.js → useNumberFormatter-FoXhpyAb.js} +1 -1
- package/dist/assets/usePress-DTwIUo40.js +7 -0
- package/dist/assets/useRunCells-B9Xr4tcH.js +1 -0
- package/dist/assets/useSplitCell-7xBW3b8-.js +1 -0
- package/dist/assets/utilities.esm-xahhGpny.js +3 -0
- package/dist/assets/{vega-component-DpAAiTdH.js → vega-component-dUiiVmIx.js} +1 -1
- package/dist/assets/{write-secret-modal-CLm48gMe.js → write-secret-modal-hOetwavI.js} +1 -1
- package/dist/index.html +54 -54
- package/package.json +5 -5
- package/src/__mocks__/requests.ts +1 -0
- package/src/__tests__/mount.test.ts +128 -0
- package/src/components/app-config/__tests__/get-dirty-values.test.ts +1 -1
- package/src/components/app-config/user-config-form.tsx +1 -1
- package/src/components/chat/acp/agent-panel.tsx +56 -43
- package/src/components/chat/chat-utils.ts +0 -19
- package/src/components/data-table/column-header.tsx +1 -1
- package/src/components/editor/KernelStartupErrorModal.tsx +101 -0
- package/src/components/editor/actions/name-cell-input.tsx +10 -4
- package/src/components/editor/ai/completion-handlers.tsx +1 -1
- package/src/components/editor/alerts/connecting-alert.tsx +33 -6
- package/src/components/editor/chrome/types.ts +2 -4
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +55 -58
- package/src/components/editor/chrome/wrapper/footer-items/runtime-settings.tsx +150 -96
- package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +27 -0
- package/src/components/editor/renderers/vertical-layout/useFocusFirstEditor.ts +6 -0
- package/src/components/utils/lazy-mount.tsx +29 -8
- package/src/core/MarimoApp.tsx +2 -0
- package/src/core/cells/cells.ts +2 -0
- package/src/core/cells/scrollCellIntoView.ts +3 -2
- package/src/core/codemirror/cm.ts +2 -0
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +123 -0
- package/src/core/codemirror/lsp/notebook-lsp.ts +44 -4
- package/src/core/codemirror/misc/__tests__/string-braces.test.ts +200 -0
- package/src/core/codemirror/misc/string-braces.ts +37 -0
- package/src/core/errors/state.ts +7 -1
- package/src/core/export/__tests__/hooks.test.ts +504 -0
- package/src/core/export/hooks.ts +93 -4
- package/src/core/islands/bridge.ts +1 -0
- package/src/core/islands/main.ts +2 -0
- package/src/core/kernel/__tests__/handlers.test.ts +2 -2
- package/src/core/kernel/state.ts +1 -0
- package/src/core/network/__tests__/requests-lazy.test.ts +1 -1
- package/src/core/network/__tests__/requests-network.test.ts +0 -18
- package/src/core/network/requests-lazy.ts +3 -2
- package/src/core/network/requests-network.ts +10 -7
- package/src/core/network/requests-static.ts +1 -0
- package/src/core/network/requests-toasting.tsx +1 -0
- package/src/core/network/types.ts +2 -0
- package/src/core/wasm/bridge.ts +1 -0
- package/src/core/websocket/types.ts +1 -0
- package/src/core/websocket/useMarimoKernelConnection.tsx +18 -1
- package/src/css/globals.css +2 -0
- package/src/hooks/__tests__/useInterval.test.tsx +104 -0
- package/src/hooks/useInterval.ts +32 -6
- package/src/mount.tsx +6 -0
- package/src/plugins/impl/chat/chat-ui.tsx +16 -4
- package/src/plugins/impl/chat/types.ts +5 -12
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +3 -1
- package/src/utils/events.ts +1 -0
- package/dist/assets/VisuallyHidden-BodIky8L.js +0 -1
- package/dist/assets/agent-panel-CaAPVPdJ.js +0 -287
- package/dist/assets/button-DuYGqRtX.js +0 -1
- package/dist/assets/cell-editor-OFm-OSAP.js +0 -23
- package/dist/assets/cell-link-CfLJRl3p.js +0 -1
- package/dist/assets/client-Cha_JfGC.js +0 -4
- package/dist/assets/common-A6YWtmpq.js +0 -1
- package/dist/assets/config-babG4OBR.js +0 -1
- package/dist/assets/context-BAYdLMF_.js +0 -1
- package/dist/assets/edit-page-nuU4FVXi.js +0 -12
- package/dist/assets/globe-CY9im410.js +0 -1
- package/dist/assets/index-BI88xbv4.js +0 -43
- package/dist/assets/index-Chgc_07S.css +0 -2
- package/dist/assets/input-CaEtLL8p.js +0 -1
- package/dist/assets/links-ENMiP32L.js +0 -1
- package/dist/assets/mode-CK5Oq-Jz.js +0 -1
- package/dist/assets/name-cell-input-D7axzd6k.js +0 -1
- package/dist/assets/panels-CdYbZBqo.js +0 -1
- package/dist/assets/run-page-GP8eGE39.js +0 -1
- package/dist/assets/scratchpad-panel-B1p8zqAE.js +0 -1
- package/dist/assets/state-BBgXjqJI.js +0 -1
- package/dist/assets/state-CP7_TGWl.js +0 -1
- package/dist/assets/useDeleteCell-5kJUaejE.js +0 -1
- package/dist/assets/useInterval-DpipYmgs.js +0 -1
- package/dist/assets/useNotebookActions-o341ZCMJ.js +0 -1
- package/dist/assets/usePress-C2LPFxyv.js +0 -7
- package/dist/assets/useRunCells-wXhl9zOP.js +0 -1
- package/dist/assets/useSplitCell-mmm5jxn2.js +0 -1
- package/dist/assets/utilities.esm-Ckt5kMF-.js +0 -3
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { connectionAtom } from "@/core/network/connection";
|
|
5
|
+
import { store } from "@/core/state/jotai";
|
|
6
|
+
import { WebSocketState } from "@/core/websocket/types";
|
|
7
|
+
import { mount, visibleForTesting } from "../mount";
|
|
8
|
+
|
|
9
|
+
// Mock React DOM
|
|
10
|
+
vi.mock("react-dom/client", () => ({
|
|
11
|
+
createRoot: vi.fn(() => ({
|
|
12
|
+
render: vi.fn(),
|
|
13
|
+
})),
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
// Mock static state
|
|
17
|
+
vi.mock("@/core/static/static-state", () => ({
|
|
18
|
+
isStaticNotebook: vi.fn(() => false),
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
// Mock other side-effect modules
|
|
22
|
+
vi.mock("@/core/vscode/vscode-bindings", () => ({
|
|
23
|
+
maybeRegisterVSCodeBindings: vi.fn(),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
vi.mock("@/plugins/plugins", () => ({
|
|
27
|
+
initializePlugins: vi.fn(),
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
vi.mock("@/core/network/auth", () => ({
|
|
31
|
+
cleanupAuthQueryParams: vi.fn(),
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
vi.mock("@/utils/vitals", () => ({
|
|
35
|
+
reportVitals: vi.fn(),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
// Mock preloadPage
|
|
39
|
+
vi.mock("@/core/MarimoApp", () => ({
|
|
40
|
+
MarimoApp: () => null,
|
|
41
|
+
preloadPage: vi.fn(),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
describe("mount", () => {
|
|
45
|
+
const mockElement = document.createElement("div");
|
|
46
|
+
|
|
47
|
+
beforeEach(() => {
|
|
48
|
+
visibleForTesting.reset();
|
|
49
|
+
// Reset connection atom to initial state
|
|
50
|
+
store.set(connectionAtom, { state: WebSocketState.NOT_STARTED });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
vi.clearAllMocks();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const baseOptions = {
|
|
58
|
+
filename: "test.py",
|
|
59
|
+
code: "",
|
|
60
|
+
version: "0.0.1",
|
|
61
|
+
mode: "edit" as const,
|
|
62
|
+
config: {},
|
|
63
|
+
configOverrides: {},
|
|
64
|
+
appConfig: {},
|
|
65
|
+
view: { showAppCode: true },
|
|
66
|
+
serverToken: "",
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
describe("connection state initialization", () => {
|
|
70
|
+
it("should set connection to CONNECTING when runtimeConfig has lazy=false", () => {
|
|
71
|
+
mount(
|
|
72
|
+
{
|
|
73
|
+
...baseOptions,
|
|
74
|
+
runtimeConfig: [{ url: "http://localhost:8080", lazy: false }],
|
|
75
|
+
},
|
|
76
|
+
mockElement,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const connection = store.get(connectionAtom);
|
|
80
|
+
expect(connection.state).toBe(WebSocketState.CONNECTING);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("should keep connection as NOT_STARTED when runtimeConfig has lazy=true", () => {
|
|
84
|
+
mount(
|
|
85
|
+
{
|
|
86
|
+
...baseOptions,
|
|
87
|
+
runtimeConfig: [{ url: "http://localhost:8080", lazy: true }],
|
|
88
|
+
},
|
|
89
|
+
mockElement,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const connection = store.get(connectionAtom);
|
|
93
|
+
expect(connection.state).toBe(WebSocketState.NOT_STARTED);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should keep connection as NOT_STARTED when no runtimeConfig is provided", () => {
|
|
97
|
+
mount(
|
|
98
|
+
{
|
|
99
|
+
...baseOptions,
|
|
100
|
+
runtimeConfig: [],
|
|
101
|
+
},
|
|
102
|
+
mockElement,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const connection = store.get(connectionAtom);
|
|
106
|
+
expect(connection.state).toBe(WebSocketState.NOT_STARTED);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should keep connection as NOT_STARTED for static notebooks even with lazy=false", async () => {
|
|
110
|
+
const { isStaticNotebook } = await import("@/core/static/static-state");
|
|
111
|
+
vi.mocked(isStaticNotebook).mockReturnValue(true);
|
|
112
|
+
|
|
113
|
+
// Reset mount state to allow another mount
|
|
114
|
+
visibleForTesting.reset();
|
|
115
|
+
|
|
116
|
+
mount(
|
|
117
|
+
{
|
|
118
|
+
...baseOptions,
|
|
119
|
+
runtimeConfig: [{ url: "http://localhost:8080", lazy: false }],
|
|
120
|
+
},
|
|
121
|
+
mockElement,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
const connection = store.get(connectionAtom);
|
|
125
|
+
expect(connection.state).toBe(WebSocketState.NOT_STARTED);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -69,7 +69,7 @@ export function getDirtyValues<T extends FieldValues>(
|
|
|
69
69
|
dirtyFields: Partial<Record<keyof T, unknown>>,
|
|
70
70
|
): Partial<T> {
|
|
71
71
|
const result: Partial<T> = {};
|
|
72
|
-
for (const key of Object.keys(dirtyFields) as
|
|
72
|
+
for (const key of Object.keys(dirtyFields) as (keyof T)[]) {
|
|
73
73
|
const dirty = dirtyFields[key];
|
|
74
74
|
if (dirty === true) {
|
|
75
75
|
result[key] = values[key];
|
|
@@ -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
|
|
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
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
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
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
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
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
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
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
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
|
-
|
|
822
|
-
|
|
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;
|
|
@@ -8,7 +8,6 @@ import type {
|
|
|
8
8
|
InvokeAiToolRequest,
|
|
9
9
|
InvokeAiToolResponse,
|
|
10
10
|
} from "@/core/network/types";
|
|
11
|
-
import type { ChatMessage } from "@/plugins/impl/chat/types";
|
|
12
11
|
import { blobToString } from "@/utils/fileToBase64";
|
|
13
12
|
import { Logger } from "@/utils/Logger";
|
|
14
13
|
import { getAICompletionBodyWithAttachments } from "../editor/ai/completion-utils";
|
|
@@ -68,7 +67,6 @@ function stringifyTextParts(parts: UIMessage["parts"]): string {
|
|
|
68
67
|
export async function buildCompletionRequestBody(
|
|
69
68
|
messages: UIMessage[],
|
|
70
69
|
): Promise<{
|
|
71
|
-
messages: ChatMessage[]; // Deprecated. TODO: Remove in the future
|
|
72
70
|
uiMessages: UIMessage[];
|
|
73
71
|
context?: (null | components["schemas"]["AiCompletionContext"]) | undefined;
|
|
74
72
|
includeOtherCode: string;
|
|
@@ -91,25 +89,8 @@ export async function buildCompletionRequestBody(
|
|
|
91
89
|
};
|
|
92
90
|
}
|
|
93
91
|
|
|
94
|
-
function toChatMessage(message: UIMessage, isLast: boolean): ChatMessage {
|
|
95
|
-
// Clone parts to avoid mutating the original message
|
|
96
|
-
const parts = [...message.parts];
|
|
97
|
-
if (isLast) {
|
|
98
|
-
parts.push(...completionBody.attachments);
|
|
99
|
-
}
|
|
100
|
-
return {
|
|
101
|
-
id: message.id,
|
|
102
|
-
role: message.role,
|
|
103
|
-
content: stringifyTextParts(message.parts), // This is no longer used in the backend
|
|
104
|
-
parts,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
92
|
return {
|
|
109
93
|
...completionBody.body,
|
|
110
|
-
messages: messages.map((m, idx) =>
|
|
111
|
-
toChatMessage(m, idx === messages.length - 1),
|
|
112
|
-
),
|
|
113
94
|
uiMessages: messages.map((m, idx) =>
|
|
114
95
|
addAttachmentsToMessage(m, idx === messages.length - 1),
|
|
115
96
|
),
|
|
@@ -593,7 +593,7 @@ const PopoverFilterByValues = <TData, TValue>({
|
|
|
593
593
|
<>
|
|
594
594
|
<Command className="text-sm outline-hidden" shouldFilter={false}>
|
|
595
595
|
<CommandInput
|
|
596
|
-
placeholder=
|
|
596
|
+
placeholder={`Search among the top ${data.length} values`}
|
|
597
597
|
autoFocus={true}
|
|
598
598
|
onValueChange={(value) => setQuery(value.trim())}
|
|
599
599
|
/>
|
|
@@ -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={
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
onKeyDown={(e) => {
|
|
97
|
+
// Prevent all key presses from triggering hotkeys
|
|
98
|
+
e.stopPropagation();
|
|
99
|
+
|
|
100
|
+
// On Enter, blur the input to commit the change
|
|
101
|
+
if (e.key === "Enter") {
|
|
102
|
+
if (e.target instanceof HTMLElement) {
|
|
103
|
+
e.target.blur();
|
|
104
|
+
}
|
|
99
105
|
}
|
|
100
|
-
}
|
|
106
|
+
}}
|
|
101
107
|
>
|
|
102
108
|
{value}
|
|
103
109
|
</span>
|
|
@@ -124,7 +124,7 @@ export const AcceptCompletionButton: React.FC<{
|
|
|
124
124
|
size={size}
|
|
125
125
|
disabled={isLoading}
|
|
126
126
|
onClick={handleAcceptAndRun}
|
|
127
|
-
className={`${baseClasses} rounded-l-none px-1.5 ${borderless && "border-0 border-l
|
|
127
|
+
className={`${baseClasses} rounded-l-none px-1.5 ${borderless && "border-0 border-l"} ${playButtonStyles}`}
|
|
128
128
|
>
|
|
129
129
|
<PlayIcon className="h-2.5 w-2.5" />
|
|
130
130
|
</Button>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useAtomValue } from "jotai";
|
|
4
4
|
import { LoadingEllipsis } from "@/components/icons/loading-ellipsis";
|
|
5
|
+
import { Spinner } from "@/components/icons/spinner";
|
|
5
6
|
import { Button } from "@/components/ui/button";
|
|
6
7
|
import { DelayMount } from "@/components/utils/delay-mount";
|
|
7
8
|
import {
|
|
@@ -10,24 +11,50 @@ import {
|
|
|
10
11
|
isNotStartedAtom,
|
|
11
12
|
} from "@/core/network/connection";
|
|
12
13
|
import { useConnectToRuntime } from "@/core/runtime/config";
|
|
14
|
+
import { Banner } from "@/plugins/impl/common/error-banner";
|
|
13
15
|
import { Tooltip } from "../../ui/tooltip";
|
|
14
16
|
import { FloatingAlert } from "./floating-alert";
|
|
15
17
|
|
|
16
18
|
const SHORT_DELAY_MS = 1000; // 1 second
|
|
19
|
+
const LONG_DELAY_MS = 5000; // 5 seconds
|
|
17
20
|
|
|
18
21
|
export const ConnectingAlert: React.FC = () => {
|
|
19
22
|
const isConnecting = useAtomValue(isConnectingAtom);
|
|
20
23
|
const isClosed = useAtomValue(isClosedAtom);
|
|
21
24
|
|
|
22
25
|
if (isConnecting) {
|
|
26
|
+
const subtleNotification = (
|
|
27
|
+
<Tooltip content="Connecting to a marimo runtime">
|
|
28
|
+
<div className="flex items-center">
|
|
29
|
+
<LoadingEllipsis size={5} className="text-yellow-500" />
|
|
30
|
+
</div>
|
|
31
|
+
</Tooltip>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const longNotification = (
|
|
35
|
+
<Banner
|
|
36
|
+
kind="info"
|
|
37
|
+
className="flex flex-col rounded py-2 px-4 animate-in slide-in-from-top w-fit"
|
|
38
|
+
>
|
|
39
|
+
<div className="flex flex-col gap-4 justify-between items-start text-muted-foreground text-base">
|
|
40
|
+
<div className="flex items-center gap-2">
|
|
41
|
+
<Spinner className="h-4 w-4" />
|
|
42
|
+
<p>Connecting to a marimo runtime ...</p>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</Banner>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// This waits for 1 second to show the subtle notification, then shows the long notification after 5 seconds.
|
|
23
49
|
return (
|
|
24
50
|
<DelayMount milliseconds={SHORT_DELAY_MS}>
|
|
25
|
-
<div className="
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
51
|
+
<div className="m-0 flex items-center min-h-[28px] fixed top-5 left-1/2 transform -translate-x-1/2 z-200">
|
|
52
|
+
<DelayMount
|
|
53
|
+
milliseconds={LONG_DELAY_MS}
|
|
54
|
+
fallback={subtleNotification}
|
|
55
|
+
>
|
|
56
|
+
{longNotification}
|
|
57
|
+
</DelayMount>
|
|
31
58
|
</div>
|
|
32
59
|
</DelayMount>
|
|
33
60
|
);
|
|
@@ -194,10 +194,8 @@ export function isPanelHidden(
|
|
|
194
194
|
if (panel.hidden) {
|
|
195
195
|
return true;
|
|
196
196
|
}
|
|
197
|
-
if (panel.requiredCapability) {
|
|
198
|
-
|
|
199
|
-
return true;
|
|
200
|
-
}
|
|
197
|
+
if (panel.requiredCapability && !capabilities[panel.requiredCapability]) {
|
|
198
|
+
return true;
|
|
201
199
|
}
|
|
202
200
|
return false;
|
|
203
201
|
}
|