@marimo-team/frontend 0.19.3-dev4 → 0.19.3-dev41
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-Csh7GHT3.js} +8 -8
- package/dist/assets/{add-database-form-CqIp3_WN.js → add-database-form-BBkiGMZ_.js} +2 -2
- package/dist/assets/agent-panel-C5C18dfm.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-gOJZyTG_.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-BwP6HOKA.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-KoYkiOm1.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-Bq3Xa2YC.js +43 -0
- package/dist/assets/index-__6MNWbe.css +2 -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-BcE_ovEU.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/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/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/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,200 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { python } from "@codemirror/lang-python";
|
|
4
|
+
import { EditorState } from "@codemirror/state";
|
|
5
|
+
import { EditorView } from "@codemirror/view";
|
|
6
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
7
|
+
import { stringBraceInputHandler } from "../string-braces";
|
|
8
|
+
|
|
9
|
+
function createEditor(
|
|
10
|
+
initialContent: string,
|
|
11
|
+
cursorPosition: number,
|
|
12
|
+
): EditorView {
|
|
13
|
+
const state = EditorState.create({
|
|
14
|
+
doc: initialContent,
|
|
15
|
+
selection: { anchor: cursorPosition },
|
|
16
|
+
extensions: [python()],
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const view = new EditorView({
|
|
20
|
+
state,
|
|
21
|
+
parent: document.body,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
return view;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe("string brace auto-closing", () => {
|
|
28
|
+
let view: EditorView;
|
|
29
|
+
|
|
30
|
+
afterEach(() => {
|
|
31
|
+
if (view) {
|
|
32
|
+
view.destroy();
|
|
33
|
+
if (document.body.contains(view.dom)) {
|
|
34
|
+
view.dom.remove();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should auto-close braces in f-strings", () => {
|
|
40
|
+
view = createEditor('f"hello ', 8);
|
|
41
|
+
const result = stringBraceInputHandler(view, 8, 8, "{");
|
|
42
|
+
|
|
43
|
+
expect(result).toBe(true);
|
|
44
|
+
expect(view.state.doc.toString()).toBe('f"hello {}');
|
|
45
|
+
expect(view.state.selection.main.head).toBe(9);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("should auto-close braces in regular double-quoted strings", () => {
|
|
49
|
+
view = createEditor('"hello ', 7);
|
|
50
|
+
const result = stringBraceInputHandler(view, 7, 7, "{");
|
|
51
|
+
|
|
52
|
+
expect(result).toBe(true);
|
|
53
|
+
expect(view.state.doc.toString()).toBe('"hello {}');
|
|
54
|
+
expect(view.state.selection.main.head).toBe(8);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should auto-close braces in rf-strings", () => {
|
|
58
|
+
view = createEditor('rf"hello ', 9);
|
|
59
|
+
const result = stringBraceInputHandler(view, 9, 9, "{");
|
|
60
|
+
|
|
61
|
+
expect(result).toBe(true);
|
|
62
|
+
expect(view.state.doc.toString()).toBe('rf"hello {}');
|
|
63
|
+
expect(view.state.selection.main.head).toBe(10);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("should auto-close braces in fr-strings", () => {
|
|
67
|
+
view = createEditor('fr"hello ', 9);
|
|
68
|
+
const result = stringBraceInputHandler(view, 9, 9, "{");
|
|
69
|
+
|
|
70
|
+
expect(result).toBe(true);
|
|
71
|
+
expect(view.state.doc.toString()).toBe('fr"hello {}');
|
|
72
|
+
expect(view.state.selection.main.head).toBe(10);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("should auto-close braces in single-quoted strings", () => {
|
|
76
|
+
view = createEditor("'hello ", 7);
|
|
77
|
+
const result = stringBraceInputHandler(view, 7, 7, "{");
|
|
78
|
+
|
|
79
|
+
expect(result).toBe(true);
|
|
80
|
+
expect(view.state.doc.toString()).toBe("'hello {}");
|
|
81
|
+
expect(view.state.selection.main.head).toBe(8);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("should auto-close braces in uppercase F-strings", () => {
|
|
85
|
+
view = createEditor('F"hello ', 8);
|
|
86
|
+
const result = stringBraceInputHandler(view, 8, 8, "{");
|
|
87
|
+
|
|
88
|
+
expect(result).toBe(true);
|
|
89
|
+
expect(view.state.doc.toString()).toBe('F"hello {}');
|
|
90
|
+
expect(view.state.selection.main.head).toBe(9);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("should auto-close braces in raw strings without f/t", () => {
|
|
94
|
+
view = createEditor('r"hello ', 8);
|
|
95
|
+
const result = stringBraceInputHandler(view, 8, 8, "{");
|
|
96
|
+
|
|
97
|
+
expect(result).toBe(true);
|
|
98
|
+
expect(view.state.doc.toString()).toBe('r"hello {}');
|
|
99
|
+
expect(view.state.selection.main.head).toBe(9);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Handled by other CodeMirror handler(s)
|
|
103
|
+
it("should NOT auto-close braces outside strings", () => {
|
|
104
|
+
view = createEditor("x = ", 4);
|
|
105
|
+
const result = stringBraceInputHandler(view, 4, 4, "{");
|
|
106
|
+
|
|
107
|
+
expect(result).toBe(false);
|
|
108
|
+
expect(view.state.doc.toString()).toBe("x = ");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("should NOT auto-close braces when typing other characters", () => {
|
|
112
|
+
view = createEditor('f"hello ', 8);
|
|
113
|
+
const result = stringBraceInputHandler(view, 8, 8, "a");
|
|
114
|
+
|
|
115
|
+
expect(result).toBe(false);
|
|
116
|
+
expect(view.state.doc.toString()).toBe('f"hello ');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should handle braces at the start of string", () => {
|
|
120
|
+
view = createEditor('f"', 2);
|
|
121
|
+
const result = stringBraceInputHandler(view, 2, 2, "{");
|
|
122
|
+
|
|
123
|
+
expect(result).toBe(true);
|
|
124
|
+
expect(view.state.doc.toString()).toBe('f"{}');
|
|
125
|
+
expect(view.state.selection.main.head).toBe(3);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should handle braces in the middle of string content", () => {
|
|
129
|
+
view = createEditor('f"hello world ', 14);
|
|
130
|
+
const result = stringBraceInputHandler(view, 14, 14, "{");
|
|
131
|
+
|
|
132
|
+
expect(result).toBe(true);
|
|
133
|
+
expect(view.state.doc.toString()).toBe('f"hello world {}');
|
|
134
|
+
expect(view.state.selection.main.head).toBe(15);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("should handle multiple braces in string", () => {
|
|
138
|
+
view = createEditor('f"hello {} world', 16);
|
|
139
|
+
const result = stringBraceInputHandler(view, 16, 16, "{");
|
|
140
|
+
|
|
141
|
+
expect(result).toBe(true);
|
|
142
|
+
expect(view.state.doc.toString()).toBe('f"hello {} world{}');
|
|
143
|
+
expect(view.state.selection.main.head).toBe(17);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("should handle empty string", () => {
|
|
147
|
+
view = createEditor('f""', 2);
|
|
148
|
+
const result = stringBraceInputHandler(view, 2, 2, "{");
|
|
149
|
+
|
|
150
|
+
expect(result).toBe(true);
|
|
151
|
+
expect(view.state.doc.toString()).toBe('f"{}"');
|
|
152
|
+
expect(view.state.selection.main.head).toBe(3);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("should auto-close braces in triple-quoted strings", () => {
|
|
156
|
+
view = createEditor('"""hello ', 9);
|
|
157
|
+
const result = stringBraceInputHandler(view, 9, 9, "{");
|
|
158
|
+
|
|
159
|
+
expect(result).toBe(true);
|
|
160
|
+
expect(view.state.doc.toString()).toBe('"""hello {}');
|
|
161
|
+
expect(view.state.selection.main.head).toBe(10);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should auto-close braces in triple-quoted f-strings", () => {
|
|
165
|
+
view = createEditor('f"""hello ', 10);
|
|
166
|
+
const result = stringBraceInputHandler(view, 10, 10, "{");
|
|
167
|
+
|
|
168
|
+
expect(result).toBe(true);
|
|
169
|
+
expect(view.state.doc.toString()).toBe('f"""hello {}');
|
|
170
|
+
expect(view.state.selection.main.head).toBe(11);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("should auto-close braces in triple single-quoted strings", () => {
|
|
174
|
+
view = createEditor("'''hello ", 9);
|
|
175
|
+
const result = stringBraceInputHandler(view, 9, 9, "{");
|
|
176
|
+
|
|
177
|
+
expect(result).toBe(true);
|
|
178
|
+
expect(view.state.doc.toString()).toBe("'''hello {}");
|
|
179
|
+
expect(view.state.selection.main.head).toBe(10);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("should auto-close braces in triple single-quoted f-strings", () => {
|
|
183
|
+
view = createEditor("f'''hello ", 10);
|
|
184
|
+
const result = stringBraceInputHandler(view, 10, 10, "{");
|
|
185
|
+
|
|
186
|
+
expect(result).toBe(true);
|
|
187
|
+
expect(view.state.doc.toString()).toBe("f'''hello {}");
|
|
188
|
+
expect(view.state.selection.main.head).toBe(11);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("should NOT auto-close braces when text is selected", () => {
|
|
192
|
+
view = createEditor('f"hello world"', 8);
|
|
193
|
+
// User has selected "world" (from position 8 to 13)
|
|
194
|
+
const result = stringBraceInputHandler(view, 8, 13, "{");
|
|
195
|
+
|
|
196
|
+
expect(result).toBe(false);
|
|
197
|
+
// Document should remain unchanged since we return false
|
|
198
|
+
expect(view.state.doc.toString()).toBe('f"hello world"');
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { syntaxTree } from "@codemirror/language";
|
|
3
|
+
import type { Extension } from "@codemirror/state";
|
|
4
|
+
import { EditorView } from "@codemirror/view";
|
|
5
|
+
|
|
6
|
+
export function stringBraceInputHandler(
|
|
7
|
+
view: EditorView,
|
|
8
|
+
from: number,
|
|
9
|
+
to: number,
|
|
10
|
+
text: string,
|
|
11
|
+
): boolean {
|
|
12
|
+
if (text !== "{") {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (from !== to) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const tree = syntaxTree(view.state);
|
|
21
|
+
const node = tree.resolveInner(from, -1);
|
|
22
|
+
|
|
23
|
+
if (!node?.type.name.includes("String")) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
view.dispatch({
|
|
28
|
+
changes: { from, to, insert: "{}" },
|
|
29
|
+
selection: { anchor: from + 1 },
|
|
30
|
+
userEvent: "input.type",
|
|
31
|
+
});
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function stringsAutoCloseBraces(): Extension {
|
|
36
|
+
return EditorView.inputHandler.of(stringBraceInputHandler);
|
|
37
|
+
}
|
package/src/core/errors/state.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import { useAtomValue } from "jotai";
|
|
3
|
+
import { atom, useAtomValue } from "jotai";
|
|
4
4
|
import { createReducerAndAtoms } from "@/utils/createReducer";
|
|
5
5
|
import type { Identified } from "@/utils/typed";
|
|
6
6
|
import { generateUUID } from "@/utils/uuid";
|
|
7
7
|
import type { Banner } from "../kernel/messages";
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Atom for storing kernel startup error message.
|
|
11
|
+
* When set to a non-null value, shows a modal with the error details.
|
|
12
|
+
*/
|
|
13
|
+
export const kernelStartupErrorAtom = atom<string | null>(null);
|
|
14
|
+
|
|
9
15
|
interface BannerState {
|
|
10
16
|
banners: Identified<Banner>[];
|
|
11
17
|
}
|