@marimo-team/islands 0.23.15-dev32 → 0.23.15-dev4
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/{ConnectedDataExplorerComponent-7p7rt9iw.js → ConnectedDataExplorerComponent-CU4fZJzG.js} +4 -4
- package/dist/{ErrorBoundary-B_CAG7_e.js → ErrorBoundary-DE6tzZf-.js} +1 -1
- package/dist/{ImageComparisonComponent-1i5IBhon.js → ImageComparisonComponent-CHrI72em.js} +129 -173
- package/dist/assets/__vite-browser-external-BQCLNwri.js +1 -0
- package/dist/assets/worker-DAWRHcPq.js +73 -0
- package/dist/{chat-ui-C0wkSwxB.js → chat-ui-DinOvbWu.js} +3111 -3111
- package/dist/{code-visibility-CEHWVGz1.js → code-visibility-Dk8cVOny.js} +864 -946
- package/dist/{debounce-BOD3DbfP.js → constants-T20xxyNf.js} +24 -1
- package/dist/{formats-BX3uDQbB.js → formats-Dzx4J_z1.js} +1 -1
- package/dist/{glide-data-editor-CwZz71BD.js → glide-data-editor-CjTu7ukN.js} +3 -3
- package/dist/{html-to-image-D8Q5SLSJ.js → html-to-image-_wGfk8V-.js} +2241 -2277
- package/dist/{input-BGPrFH3g.js → input-DtsN7xm-.js} +2 -2
- package/dist/main.js +1201 -1297
- package/dist/{mermaid-UdmxG2PZ.js → mermaid-BYqXy_NE.js} +2 -2
- package/dist/{process-output-C_A12w9J.js → process-output-Mh4UrjwM.js} +1 -1
- package/dist/{reveal-component-kLDhMoa2.js → reveal-component-B5DXLyO7.js} +605 -618
- package/dist/{spec-DSs9v0xx.js → spec-Cz-Bj1JI.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/{toDate-CKpRx4TS.js → toDate-CWNNlFEX.js} +4 -3
- package/dist/{useAsyncData-BSOyAbac.js → useAsyncData-KfHB8wQR.js} +1 -1
- package/dist/{useDeepCompareMemoize-BTLeWzuR.js → useDeepCompareMemoize-nJMtxhm4.js} +1 -1
- package/dist/{useLifecycle-C6wHjkhW.js → useLifecycle-DegSo0lV.js} +1 -1
- package/dist/{useTheme-Df_vGflw.js → useTheme-6eZ3GOTS.js} +27 -62
- package/dist/{vega-component-DNHEV0u0.js → vega-component-DzyyM9fc.js} +6 -6
- package/package.json +1 -1
- package/src/components/chat/__tests__/chat-utils.test.ts +1 -244
- package/src/components/chat/chat-panel.tsx +67 -196
- package/src/components/chat/chat-utils.ts +2 -111
- package/src/components/editor/SortableCell.tsx +2 -6
- package/src/components/editor/actions/pair-with-agent-modal.tsx +64 -20
- package/src/components/editor/ai/add-cell-with-ai.tsx +5 -14
- package/src/components/editor/ai/ai-completion-editor.tsx +1 -4
- package/src/components/editor/cell/cell-context-menu.tsx +0 -7
- package/src/components/editor/chrome/panels/packages-panel.tsx +1 -11
- package/src/components/editor/columns/cell-column.tsx +4 -34
- package/src/components/editor/columns/sortable-column.tsx +4 -24
- package/src/components/editor/navigation/__tests__/navigation.test.ts +0 -33
- package/src/components/editor/navigation/navigation.ts +1 -8
- package/src/components/editor/notebook-cell.tsx +106 -203
- package/src/components/editor/renderers/cell-array.tsx +13 -26
- package/src/components/editor/renderers/cells-renderer.tsx +2 -8
- package/src/components/editor/renderers/slides-layout/__tests__/plugin.test.ts +0 -29
- package/src/components/editor/renderers/slides-layout/types.ts +0 -4
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +19 -19
- package/src/components/slides/reveal-component.tsx +4 -37
- package/src/components/slides/slide-form.tsx +0 -72
- package/src/core/cells/__tests__/utils.test.ts +0 -59
- package/src/core/cells/utils.ts +0 -51
- package/src/core/codemirror/completion/__tests__/signature-hint.test.ts +3 -97
- package/src/core/codemirror/completion/signature-hint.ts +11 -78
- package/src/core/constants.ts +0 -6
- package/src/core/islands/__tests__/bridge.test.ts +46 -341
- package/src/core/islands/__tests__/parse.test.ts +0 -105
- package/src/core/islands/bootstrap.ts +3 -8
- package/src/core/islands/bridge.ts +23 -116
- package/src/core/islands/components/web-components.tsx +15 -76
- package/src/core/islands/constants.ts +0 -1
- package/src/core/islands/main.ts +3 -35
- package/src/core/islands/parse.ts +23 -70
- package/src/core/islands/worker/worker.tsx +57 -145
- package/src/core/wasm/worker/bootstrap.ts +20 -113
- package/src/css/app/Cell.css +0 -10
- package/src/hooks/useHotkey.ts +4 -29
- package/src/plugins/impl/anywidget/__tests__/host.test.ts +9 -6
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +61 -22
- package/src/plugins/impl/anywidget/model-proxy.ts +13 -0
- package/src/plugins/impl/anywidget/widget-binding.ts +34 -4
- package/src/plugins/impl/image-comparison/ImageComparisonComponent.tsx +2 -53
- package/src/plugins/impl/matplotlib/__tests__/matplotlib-renderer.test.ts +3 -71
- package/src/plugins/impl/matplotlib/matplotlib-renderer.ts +0 -1
- package/src/theme/useTheme.ts +1 -16
- package/dist/assets/__vite-browser-external-CjNAy01L.js +0 -1
- package/dist/assets/worker-B3XPCb6y.js +0 -98
- package/src/components/chat/__tests__/message-queue.test.tsx +0 -121
- package/src/components/editor/__tests__/output-persistence.test.tsx +0 -241
- package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +0 -153
- package/src/components/editor/actions/pair-with-agent-commands.ts +0 -109
- package/src/components/editor/columns/__tests__/cell-column.test.tsx +0 -105
- package/src/components/editor/renderers/__tests__/cells-renderer.test.tsx +0 -66
- package/src/core/islands/components/__tests__/web-components.test.tsx +0 -214
- package/src/core/islands/worker/__tests__/controller.test.ts +0 -173
- package/src/hooks/__tests__/useHotkey.test.tsx +0 -88
- package/src/plugins/impl/image-comparison/__tests__/ImageComparisonComponent.test.tsx +0 -71
- package/src/theme/__tests__/useTheme.test.ts +0 -68
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
-
type CSSProperties,
|
|
5
4
|
startTransition,
|
|
6
5
|
useEffect,
|
|
7
6
|
useMemo,
|
|
@@ -27,7 +26,6 @@ import { Logger } from "@/utils/Logger";
|
|
|
27
26
|
import "./slides.css";
|
|
28
27
|
import "./reveal-slides.css";
|
|
29
28
|
import type {
|
|
30
|
-
DeckVerticalAlign,
|
|
31
29
|
SlideConfig,
|
|
32
30
|
SlidesLayout,
|
|
33
31
|
SlideType,
|
|
@@ -41,7 +39,6 @@ import {
|
|
|
41
39
|
type ComposedSubslide,
|
|
42
40
|
} from "./compose-slides";
|
|
43
41
|
import {
|
|
44
|
-
DEFAULT_DECK_VERTICAL_ALIGN,
|
|
45
42
|
DEFAULT_DECK_TRANSITION,
|
|
46
43
|
DEFAULT_SLIDE_TYPE,
|
|
47
44
|
SlideSidebar,
|
|
@@ -295,38 +292,16 @@ export function useParkedPreview(options: {
|
|
|
295
292
|
};
|
|
296
293
|
}
|
|
297
294
|
|
|
298
|
-
/**
|
|
299
|
-
* Margin style that positions a slide's content vertically within the
|
|
300
|
-
* full-height slide. The content is a flex item, so the vertical margins decide
|
|
301
|
-
* where the free space lands: `auto` on both sides centers it, while pinning one
|
|
302
|
-
* side to `0` pushes content to the top or bottom. The horizontal `20px` keeps
|
|
303
|
-
* content off the slide edges regardless of alignment.
|
|
304
|
-
*/
|
|
305
|
-
function resolveSlideContentStyle(
|
|
306
|
-
verticalAlign: DeckVerticalAlign | undefined,
|
|
307
|
-
): CSSProperties {
|
|
308
|
-
switch (verticalAlign ?? DEFAULT_DECK_VERTICAL_ALIGN) {
|
|
309
|
-
case "top":
|
|
310
|
-
return { margin: "0 20px auto" };
|
|
311
|
-
case "bottom":
|
|
312
|
-
return { margin: "auto 20px 0" };
|
|
313
|
-
default:
|
|
314
|
-
return { margin: "auto 20px" };
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
295
|
const SubslideView = ({
|
|
319
296
|
subslide,
|
|
320
297
|
resolveShowCode,
|
|
321
298
|
isEditable,
|
|
322
299
|
slideConfigs,
|
|
323
|
-
contentStyle,
|
|
324
300
|
}: {
|
|
325
301
|
subslide: ComposedSubslide<RuntimeCell>;
|
|
326
302
|
resolveShowCode: (cellId: CellId) => boolean;
|
|
327
303
|
isEditable: boolean;
|
|
328
304
|
slideConfigs: ReadonlyMap<CellId, SlideConfig>;
|
|
329
|
-
contentStyle: CSSProperties;
|
|
330
305
|
}) => {
|
|
331
306
|
const { slideLevel, cumulativeByBlock } = buildSubslideNotes(
|
|
332
307
|
subslide,
|
|
@@ -346,7 +321,9 @@ const SubslideView = ({
|
|
|
346
321
|
? "mo-slide-content flex flex-col gap-3"
|
|
347
322
|
: "mo-slide-content"
|
|
348
323
|
}
|
|
349
|
-
style={
|
|
324
|
+
style={{
|
|
325
|
+
margin: "auto 20px",
|
|
326
|
+
}}
|
|
350
327
|
>
|
|
351
328
|
{subslide.blocks.map((block, i) => {
|
|
352
329
|
const rendered = block.cells.map((cell) => {
|
|
@@ -537,10 +514,6 @@ const RevealSlidesComponent = ({
|
|
|
537
514
|
);
|
|
538
515
|
|
|
539
516
|
const deckTransition = layout.deck?.transition ?? DEFAULT_DECK_TRANSITION;
|
|
540
|
-
const slideContentStyle = resolveSlideContentStyle(
|
|
541
|
-
layout.deck?.verticalAlign,
|
|
542
|
-
);
|
|
543
|
-
|
|
544
517
|
// Reveal's Notes plugin iframes the deck for the current/upcoming-slide
|
|
545
518
|
// previews. We load the same URL but as a read-only kiosk client with the
|
|
546
519
|
// app chrome hidden, which `<SlidesLayoutRenderer>` interprets the same as
|
|
@@ -563,10 +536,6 @@ const RevealSlidesComponent = ({
|
|
|
563
536
|
transition: deckTransition,
|
|
564
537
|
keyboardCondition: (event: KeyboardEvent) => !Events.fromInput(event),
|
|
565
538
|
url: kioskUrl,
|
|
566
|
-
// reveal.js auto-switches to its scroll view for mobile.
|
|
567
|
-
// We disable this mode because it rewrites the slide DOM that
|
|
568
|
-
// `@revealjs/react` owns, which crashes on `reveal.sync()` re-renders.
|
|
569
|
-
scrollActivationWidth: 0,
|
|
570
539
|
}),
|
|
571
540
|
[width, height, deckTransition, kioskUrl],
|
|
572
541
|
);
|
|
@@ -735,7 +704,6 @@ const RevealSlidesComponent = ({
|
|
|
735
704
|
resolveShowCode={resolveShowCode}
|
|
736
705
|
isEditable={isEditable}
|
|
737
706
|
slideConfigs={layout.cells}
|
|
738
|
-
contentStyle={slideContentStyle}
|
|
739
707
|
/>
|
|
740
708
|
);
|
|
741
709
|
}
|
|
@@ -749,7 +717,6 @@ const RevealSlidesComponent = ({
|
|
|
749
717
|
resolveShowCode={resolveShowCode}
|
|
750
718
|
isEditable={isEditable}
|
|
751
719
|
slideConfigs={layout.cells}
|
|
752
|
-
contentStyle={slideContentStyle}
|
|
753
720
|
/>
|
|
754
721
|
);
|
|
755
722
|
})}
|
|
@@ -776,7 +743,7 @@ const RevealSlidesComponent = ({
|
|
|
776
743
|
? "mo-slide-content flex flex-col gap-3"
|
|
777
744
|
: "mo-slide-content"
|
|
778
745
|
}
|
|
779
|
-
style={
|
|
746
|
+
style={{ margin: "auto 20px" }}
|
|
780
747
|
>
|
|
781
748
|
<ParkedPreviewContent
|
|
782
749
|
cell={parkedPreviewCell}
|
|
@@ -23,7 +23,6 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
|
23
23
|
import type { CellId } from "@/core/cells/ids";
|
|
24
24
|
import { cn } from "@/utils/cn";
|
|
25
25
|
import type {
|
|
26
|
-
DeckVerticalAlign,
|
|
27
26
|
DeckTransition,
|
|
28
27
|
SlidesLayout,
|
|
29
28
|
SlideType,
|
|
@@ -38,7 +37,6 @@ import { jotaiJsonStorage } from "@/utils/storage/jotai";
|
|
|
38
37
|
|
|
39
38
|
export const DEFAULT_SLIDE_TYPE: SlideType = "slide";
|
|
40
39
|
export const DEFAULT_DECK_TRANSITION: DeckTransition = "slide";
|
|
41
|
-
export const DEFAULT_DECK_VERTICAL_ALIGN: DeckVerticalAlign = "center";
|
|
42
40
|
const COLLAPSED_CONFIG_WIDTH = 36;
|
|
43
41
|
const slideConfigOpenAtom = atomWithStorage<boolean>(
|
|
44
42
|
"marimo:slides:config-open",
|
|
@@ -120,30 +118,6 @@ const DECK_TRANSITION_OPTIONS: DeckTransitionOption[] = [
|
|
|
120
118
|
{ value: "zoom", label: "Zoom", description: "Zoom into the next slide." },
|
|
121
119
|
];
|
|
122
120
|
|
|
123
|
-
interface DeckVerticalAlignOption {
|
|
124
|
-
value: DeckVerticalAlign;
|
|
125
|
-
label: string;
|
|
126
|
-
description: string;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const DECK_VERTICAL_ALIGN_OPTIONS: DeckVerticalAlignOption[] = [
|
|
130
|
-
{
|
|
131
|
-
value: "center",
|
|
132
|
-
label: "Center",
|
|
133
|
-
description: "Vertically center each slide's content.",
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
value: "top",
|
|
137
|
-
label: "Top",
|
|
138
|
-
description: "Align content to the top, like the cell view.",
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
value: "bottom",
|
|
142
|
-
label: "Bottom",
|
|
143
|
-
description: "Align content to the bottom of each slide.",
|
|
144
|
-
},
|
|
145
|
-
];
|
|
146
|
-
|
|
147
121
|
const SlidesForm = ({
|
|
148
122
|
layout,
|
|
149
123
|
setLayout,
|
|
@@ -326,12 +300,6 @@ const DeckConfigForm = ({
|
|
|
326
300
|
(opt) => opt.value === currentTransition,
|
|
327
301
|
)?.description;
|
|
328
302
|
|
|
329
|
-
const currentVerticalAlign: DeckVerticalAlign =
|
|
330
|
-
layout.deck?.verticalAlign ?? DEFAULT_DECK_VERTICAL_ALIGN;
|
|
331
|
-
const activeVerticalAlignDescription = DECK_VERTICAL_ALIGN_OPTIONS.find(
|
|
332
|
-
(opt) => opt.value === currentVerticalAlign,
|
|
333
|
-
)?.description;
|
|
334
|
-
|
|
335
303
|
const handleTransitionChange = (value: DeckTransition) => {
|
|
336
304
|
setLayout({
|
|
337
305
|
...layout,
|
|
@@ -339,13 +307,6 @@ const DeckConfigForm = ({
|
|
|
339
307
|
});
|
|
340
308
|
};
|
|
341
309
|
|
|
342
|
-
const handleVerticalAlignChange = (value: DeckVerticalAlign) => {
|
|
343
|
-
setLayout({
|
|
344
|
-
...layout,
|
|
345
|
-
deck: { ...layout.deck, verticalAlign: value },
|
|
346
|
-
});
|
|
347
|
-
};
|
|
348
|
-
|
|
349
310
|
return (
|
|
350
311
|
<div className="flex flex-col gap-3">
|
|
351
312
|
<div className="flex flex-col gap-1.5">
|
|
@@ -376,39 +337,6 @@ const DeckConfigForm = ({
|
|
|
376
337
|
<p className="text-xs text-foreground/70">{activeDescription}</p>
|
|
377
338
|
)}
|
|
378
339
|
</div>
|
|
379
|
-
<div className="flex flex-col gap-1.5">
|
|
380
|
-
<label
|
|
381
|
-
htmlFor="deck-vertical-align"
|
|
382
|
-
className="font-semibold text-sm text-foreground"
|
|
383
|
-
>
|
|
384
|
-
Vertical alignment
|
|
385
|
-
</label>
|
|
386
|
-
<Select
|
|
387
|
-
value={currentVerticalAlign}
|
|
388
|
-
onValueChange={(value) =>
|
|
389
|
-
handleVerticalAlignChange(value as DeckVerticalAlign)
|
|
390
|
-
}
|
|
391
|
-
>
|
|
392
|
-
<SelectTrigger
|
|
393
|
-
id="deck-vertical-align"
|
|
394
|
-
aria-label="Vertical alignment"
|
|
395
|
-
>
|
|
396
|
-
<SelectValue />
|
|
397
|
-
</SelectTrigger>
|
|
398
|
-
<SelectContent>
|
|
399
|
-
{DECK_VERTICAL_ALIGN_OPTIONS.map(({ value, label }) => (
|
|
400
|
-
<SelectItem key={value} value={value}>
|
|
401
|
-
{label}
|
|
402
|
-
</SelectItem>
|
|
403
|
-
))}
|
|
404
|
-
</SelectContent>
|
|
405
|
-
</Select>
|
|
406
|
-
{activeVerticalAlignDescription && (
|
|
407
|
-
<p className="text-xs text-foreground/70">
|
|
408
|
-
{activeVerticalAlignDescription}
|
|
409
|
-
</p>
|
|
410
|
-
)}
|
|
411
|
-
</div>
|
|
412
340
|
</div>
|
|
413
341
|
);
|
|
414
342
|
};
|
|
@@ -8,13 +8,11 @@ import {
|
|
|
8
8
|
} from "@/core/cells/cells";
|
|
9
9
|
import { CellId } from "@/core/cells/ids";
|
|
10
10
|
import type { CellData, CellRuntimeState } from "@/core/cells/types";
|
|
11
|
-
import type { OutputMessage } from "@/core/kernel/messages";
|
|
12
11
|
import { MultiColumn } from "@/utils/id-tree";
|
|
13
12
|
import {
|
|
14
13
|
disabledCellIds,
|
|
15
14
|
enabledCellIds,
|
|
16
15
|
isUninstantiated,
|
|
17
|
-
shouldHidePublishedCell,
|
|
18
16
|
staleCellIds,
|
|
19
17
|
} from "../utils";
|
|
20
18
|
|
|
@@ -428,60 +426,3 @@ describe("enabledCellIds", () => {
|
|
|
428
426
|
expect(result).toEqual([cellId2, cellId3]);
|
|
429
427
|
});
|
|
430
428
|
});
|
|
431
|
-
|
|
432
|
-
describe("shouldHidePublishedCell", () => {
|
|
433
|
-
const base = {
|
|
434
|
-
errored: false,
|
|
435
|
-
interrupted: false,
|
|
436
|
-
stopped: false,
|
|
437
|
-
output: null,
|
|
438
|
-
showErrorTracebacks: false,
|
|
439
|
-
};
|
|
440
|
-
const errorOutput = (traceback?: string): OutputMessage => ({
|
|
441
|
-
channel: "marimo-error",
|
|
442
|
-
mimetype: "application/vnd.marimo+error",
|
|
443
|
-
data: [
|
|
444
|
-
{ type: "exception", exception_type: "ValueError", msg: "!", traceback },
|
|
445
|
-
],
|
|
446
|
-
timestamp: 0,
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
it("does not hide a healthy cell", () => {
|
|
450
|
-
expect(shouldHidePublishedCell(base)).toBe(false);
|
|
451
|
-
});
|
|
452
|
-
|
|
453
|
-
it.each(["errored", "interrupted", "stopped"] as const)(
|
|
454
|
-
"hides a %s cell",
|
|
455
|
-
(key) => {
|
|
456
|
-
expect(shouldHidePublishedCell({ ...base, [key]: true })).toBe(true);
|
|
457
|
-
},
|
|
458
|
-
);
|
|
459
|
-
|
|
460
|
-
it("hides error outputs", () => {
|
|
461
|
-
expect(shouldHidePublishedCell({ ...base, output: errorOutput() })).toBe(
|
|
462
|
-
true,
|
|
463
|
-
);
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
it("shows error outputs with a traceback when show_tracebacks is enabled", () => {
|
|
467
|
-
expect(
|
|
468
|
-
shouldHidePublishedCell({
|
|
469
|
-
...base,
|
|
470
|
-
errored: true,
|
|
471
|
-
output: errorOutput("Traceback (most recent call last) ..."),
|
|
472
|
-
showErrorTracebacks: true,
|
|
473
|
-
}),
|
|
474
|
-
).toBe(false);
|
|
475
|
-
});
|
|
476
|
-
|
|
477
|
-
it("hides error outputs without a traceback even when show_tracebacks is enabled", () => {
|
|
478
|
-
expect(
|
|
479
|
-
shouldHidePublishedCell({
|
|
480
|
-
...base,
|
|
481
|
-
errored: true,
|
|
482
|
-
output: errorOutput(),
|
|
483
|
-
showErrorTracebacks: true,
|
|
484
|
-
}),
|
|
485
|
-
).toBe(true);
|
|
486
|
-
});
|
|
487
|
-
});
|
package/src/core/cells/utils.ts
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import type { EditorView } from "@codemirror/view";
|
|
4
4
|
import { Objects } from "@/utils/objects";
|
|
5
|
-
import type { MarimoError, OutputMessage } from "../kernel/messages";
|
|
6
|
-
import { isErrorMime } from "../mime";
|
|
7
5
|
import type { RuntimeState } from "../network/types";
|
|
8
6
|
import type { NotebookState } from "./cells";
|
|
9
7
|
import type { CellId } from "./ids";
|
|
@@ -198,52 +196,3 @@ export function cellStatusClasses({
|
|
|
198
196
|
stale: status === "disabled-transitively",
|
|
199
197
|
};
|
|
200
198
|
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Status classes for a published cell (read or present mode, output only).
|
|
204
|
-
*/
|
|
205
|
-
export function publishedCellClasses({
|
|
206
|
-
errored,
|
|
207
|
-
stopped,
|
|
208
|
-
}: {
|
|
209
|
-
errored: boolean;
|
|
210
|
-
stopped: boolean;
|
|
211
|
-
}) {
|
|
212
|
-
return {
|
|
213
|
-
published: true,
|
|
214
|
-
"has-error": errored,
|
|
215
|
-
stopped: stopped,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Whether a published cell (read or present mode) should be hidden.
|
|
221
|
-
*
|
|
222
|
-
* Errored, interrupted, and stopped cells (and error outputs) are hidden in
|
|
223
|
-
* published views, unless `show_tracebacks` is enabled and the output carries
|
|
224
|
-
* an exception traceback to display inline.
|
|
225
|
-
*/
|
|
226
|
-
export function shouldHidePublishedCell({
|
|
227
|
-
errored,
|
|
228
|
-
interrupted,
|
|
229
|
-
stopped,
|
|
230
|
-
output,
|
|
231
|
-
showErrorTracebacks,
|
|
232
|
-
}: {
|
|
233
|
-
errored: boolean;
|
|
234
|
-
interrupted: boolean;
|
|
235
|
-
stopped: boolean;
|
|
236
|
-
output: OutputMessage | null;
|
|
237
|
-
showErrorTracebacks: boolean;
|
|
238
|
-
}): boolean {
|
|
239
|
-
const outputIsError = isErrorMime(output?.mimetype);
|
|
240
|
-
const hasTraceback =
|
|
241
|
-
showErrorTracebacks &&
|
|
242
|
-
outputIsError &&
|
|
243
|
-
Array.isArray(output?.data) &&
|
|
244
|
-
output.data.some(
|
|
245
|
-
(e: MarimoError) =>
|
|
246
|
-
e.type === "exception" && "traceback" in e && e.traceback,
|
|
247
|
-
);
|
|
248
|
-
return (errored || interrupted || stopped || outputIsError) && !hasTraceback;
|
|
249
|
-
}
|
|
@@ -5,7 +5,6 @@ import { EditorView, type Tooltip } from "@codemirror/view";
|
|
|
5
5
|
import { describe, expect, it } from "vitest";
|
|
6
6
|
import {
|
|
7
7
|
asSignatureHint,
|
|
8
|
-
closeSignatureHint,
|
|
9
8
|
setSignatureHintEffect,
|
|
10
9
|
signatureHintField,
|
|
11
10
|
} from "../signature-hint";
|
|
@@ -52,79 +51,12 @@ describe("signatureHintField", () => {
|
|
|
52
51
|
expect(state.field(signatureHintField)).toBeNull();
|
|
53
52
|
});
|
|
54
53
|
|
|
55
|
-
it("keeps and re-anchors the tooltip across edits
|
|
54
|
+
it("keeps and re-anchors the tooltip across edits", () => {
|
|
56
55
|
let state = stateWithHint("plt.plot(", 9);
|
|
57
|
-
// Insert before the tooltip position
|
|
58
|
-
|
|
59
|
-
state = state.update({
|
|
60
|
-
changes: { from: 0, insert: "xy" },
|
|
61
|
-
selection: { anchor: 11 },
|
|
62
|
-
}).state;
|
|
56
|
+
// Insert before the tooltip position; it should shift to stay anchored.
|
|
57
|
+
state = state.update({ changes: { from: 0, insert: "xy" } }).state;
|
|
63
58
|
expect(state.field(signatureHintField)?.pos).toBe(11);
|
|
64
59
|
});
|
|
65
|
-
|
|
66
|
-
it("dismisses the tooltip when the closing paren is typed", () => {
|
|
67
|
-
let state = stateWithHint("plt.plot(", 9);
|
|
68
|
-
// Type the closing paren; the cursor is now outside the call.
|
|
69
|
-
state = state.update({
|
|
70
|
-
changes: { from: 9, insert: ")" },
|
|
71
|
-
selection: { anchor: 10 },
|
|
72
|
-
}).state;
|
|
73
|
-
expect(state.field(signatureHintField)).toBeNull();
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("dismisses the tooltip when the anchored call closes inside grouping parens", () => {
|
|
77
|
-
// Regression for the `(plt.plot())` case: the outer grouping paren must not
|
|
78
|
-
// keep the (now-closed) plt.plot hint alive.
|
|
79
|
-
let state = stateWithHint("(plt.plot(", 10);
|
|
80
|
-
// Close plt.plot's call; the outer `(` is still open but we've left the
|
|
81
|
-
// anchored call.
|
|
82
|
-
state = state.update({
|
|
83
|
-
changes: { from: 10, insert: ")" },
|
|
84
|
-
selection: { anchor: 11 },
|
|
85
|
-
}).state;
|
|
86
|
-
expect(state.field(signatureHintField)).toBeNull();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("keeps the tooltip while typing a nested call inside the anchored call", () => {
|
|
90
|
-
// Cursor inside the anchored call of `f(g(<cursor>`; opening/typing a nested
|
|
91
|
-
// call stays inside the anchored call, so the hint should remain.
|
|
92
|
-
let state = stateWithHint("f(g(", 4);
|
|
93
|
-
state = state.update({
|
|
94
|
-
changes: { from: 4, insert: "x(" },
|
|
95
|
-
selection: { anchor: 6 },
|
|
96
|
-
}).state;
|
|
97
|
-
expect(state.field(signatureHintField)?.pos).toBe(4);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it("keeps the tooltip when a nested call closes inside the anchored call", () => {
|
|
101
|
-
const anchor = "f(".length;
|
|
102
|
-
let state = stateWithHint("f(g(x", anchor);
|
|
103
|
-
state = state.update({
|
|
104
|
-
changes: { from: 5, insert: ")" },
|
|
105
|
-
selection: { anchor: 6 },
|
|
106
|
-
}).state;
|
|
107
|
-
expect(state.field(signatureHintField)?.pos).toBe(anchor);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it("dismisses the tooltip when the closing paren is typed in a large multi-line call", () => {
|
|
111
|
-
const anchor = "f(".length;
|
|
112
|
-
const prefix = `f(\n${" x,\n".repeat(25)}`;
|
|
113
|
-
let state = EditorState.create({
|
|
114
|
-
doc: prefix,
|
|
115
|
-
selection: { anchor: prefix.length },
|
|
116
|
-
extensions: [signatureHintField],
|
|
117
|
-
});
|
|
118
|
-
state = state.update({
|
|
119
|
-
effects: setSignatureHintEffect.of(fakeTooltip(anchor)),
|
|
120
|
-
}).state;
|
|
121
|
-
const head = prefix.length;
|
|
122
|
-
state = state.update({
|
|
123
|
-
changes: { from: head, insert: ")" },
|
|
124
|
-
selection: { anchor: head + 1 },
|
|
125
|
-
}).state;
|
|
126
|
-
expect(state.field(signatureHintField)).toBeNull();
|
|
127
|
-
});
|
|
128
60
|
});
|
|
129
61
|
|
|
130
62
|
describe("asSignatureHint", () => {
|
|
@@ -160,29 +92,3 @@ describe("asSignatureHint", () => {
|
|
|
160
92
|
expect(wrapped.above).toBe(true);
|
|
161
93
|
});
|
|
162
94
|
});
|
|
163
|
-
|
|
164
|
-
describe("closeSignatureHint", () => {
|
|
165
|
-
it("returns false when no hint is showing", () => {
|
|
166
|
-
const view = new EditorView({
|
|
167
|
-
state: EditorState.create({ extensions: [signatureHintField] }),
|
|
168
|
-
});
|
|
169
|
-
expect(closeSignatureHint(view)).toBe(false);
|
|
170
|
-
expect(view.state.field(signatureHintField)).toBeNull();
|
|
171
|
-
view.destroy();
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it("dismisses the hint and returns true when one is showing", () => {
|
|
175
|
-
const view = new EditorView({
|
|
176
|
-
state: EditorState.create({
|
|
177
|
-
doc: "plt.plot(",
|
|
178
|
-
extensions: [signatureHintField],
|
|
179
|
-
}),
|
|
180
|
-
});
|
|
181
|
-
view.dispatch({ effects: setSignatureHintEffect.of(fakeTooltip(9)) });
|
|
182
|
-
expect(view.state.field(signatureHintField)?.pos).toBe(9);
|
|
183
|
-
|
|
184
|
-
expect(closeSignatureHint(view)).toBe(true);
|
|
185
|
-
expect(view.state.field(signatureHintField)).toBeNull();
|
|
186
|
-
view.destroy();
|
|
187
|
-
});
|
|
188
|
-
});
|
|
@@ -1,61 +1,12 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
import type { EditorState } from "@codemirror/state";
|
|
3
2
|
import { StateEffect, StateField } from "@codemirror/state";
|
|
4
|
-
import {
|
|
3
|
+
import { showTooltip, type Tooltip } from "@codemirror/view";
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Effect to set (or clear, with `null`) the floating signature hint.
|
|
8
7
|
*/
|
|
9
8
|
export const setSignatureHintEffect = StateEffect.define<Tooltip | null>();
|
|
10
9
|
|
|
11
|
-
// Bound the scan so large cells stay cheap on every keystroke.
|
|
12
|
-
const MAX_LINES_BACK = 20;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Whether the cursor is still inside the anchored call (just inside its `(`).
|
|
16
|
-
*
|
|
17
|
-
* Anchor-relative paren scan, bounded to {@link MAX_LINES_BACK} lines.
|
|
18
|
-
* Good enough for hint dismissal — not a full parse (ignores strings/comments).
|
|
19
|
-
*/
|
|
20
|
-
function isCursorInsideAnchoredCall(options: {
|
|
21
|
-
state: EditorState;
|
|
22
|
-
anchor: number;
|
|
23
|
-
head: number;
|
|
24
|
-
}): boolean {
|
|
25
|
-
const { state, anchor, head } = options;
|
|
26
|
-
if (head < anchor) {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const headLine = state.doc.lineAt(head).number;
|
|
31
|
-
const anchorLine = state.doc.lineAt(anchor).number;
|
|
32
|
-
const startLine = Math.max(anchorLine, headLine - MAX_LINES_BACK + 1);
|
|
33
|
-
const from = Math.max(anchor, state.doc.line(startLine).from);
|
|
34
|
-
|
|
35
|
-
// If the anchor is outside the bounded window, assume its `(` is still open.
|
|
36
|
-
const assumedOpen = from > anchor;
|
|
37
|
-
let balance = assumedOpen ? 1 : 0;
|
|
38
|
-
const iter = state.doc.iterRange(from, head);
|
|
39
|
-
for (;;) {
|
|
40
|
-
const { value, done } = iter.next();
|
|
41
|
-
if (done) {
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
for (const char of value) {
|
|
45
|
-
if (char === "(") {
|
|
46
|
-
balance++;
|
|
47
|
-
} else if (char === ")") {
|
|
48
|
-
balance--;
|
|
49
|
-
const closed = assumedOpen ? balance <= 0 : balance < 0;
|
|
50
|
-
if (closed) {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
10
|
/**
|
|
60
11
|
* Wrap a tooltip so it renders like the completion popup's info box.
|
|
61
12
|
*
|
|
@@ -83,7 +34,13 @@ export function asSignatureHint(tooltip: Tooltip): Tooltip {
|
|
|
83
34
|
* Holds the floating "signature hint" shown after typing `(` or `,` inside a
|
|
84
35
|
* call on the non-LSP (Jedi) completion path.
|
|
85
36
|
*
|
|
86
|
-
* The LSP path has its own signature help; this fills the gap for users
|
|
37
|
+
* The LSP path has its own signature help; this fills the gap for users
|
|
38
|
+
* without a language server. The completion source (`pythonCompletionSource`)
|
|
39
|
+
* drives it: it dispatches `setSignatureHintEffect` with the tooltip when the
|
|
40
|
+
* backend returns a signature and with `null` otherwise. The hint is also
|
|
41
|
+
* cleared when the cursor moves via a selection-only change (e.g. clicking
|
|
42
|
+
* away or arrowing out of the call), and kept anchored across edits so it
|
|
43
|
+
* doesn't flicker while a fresh result is in flight.
|
|
87
44
|
*/
|
|
88
45
|
export const signatureHintField = StateField.define<Tooltip | null>({
|
|
89
46
|
create: () => null,
|
|
@@ -100,36 +57,12 @@ export const signatureHintField = StateField.define<Tooltip | null>({
|
|
|
100
57
|
if (tr.selection && !tr.docChanged) {
|
|
101
58
|
return null;
|
|
102
59
|
}
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
// flicker while a fresh result is in flight; the completion source refreshes
|
|
106
|
-
// or clears it as results arrive.
|
|
60
|
+
// Keep the hint anchored across edits; the completion source refreshes or
|
|
61
|
+
// clears it as new results arrive.
|
|
107
62
|
if (tr.docChanged) {
|
|
108
|
-
|
|
109
|
-
if (
|
|
110
|
-
!isCursorInsideAnchoredCall({
|
|
111
|
-
state: tr.state,
|
|
112
|
-
anchor,
|
|
113
|
-
head: tr.state.selection.main.head,
|
|
114
|
-
})
|
|
115
|
-
) {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
return { ...tooltip, pos: anchor };
|
|
63
|
+
return { ...tooltip, pos: tr.changes.mapPos(tooltip.pos) };
|
|
119
64
|
}
|
|
120
65
|
return tooltip;
|
|
121
66
|
},
|
|
122
67
|
provide: (field) => showTooltip.from(field),
|
|
123
68
|
});
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Dismiss the floating signature hint if one is showing.
|
|
127
|
-
* Returns `true` if a hint was dismissed.
|
|
128
|
-
*/
|
|
129
|
-
export function closeSignatureHint(view: EditorView): boolean {
|
|
130
|
-
if (view.state.field(signatureHintField, false)) {
|
|
131
|
-
view.dispatch({ effects: setSignatureHintEffect.of(null) });
|
|
132
|
-
return true;
|
|
133
|
-
}
|
|
134
|
-
return false;
|
|
135
|
-
}
|
package/src/core/constants.ts
CHANGED
|
@@ -50,12 +50,6 @@ export const KnownQueryParams = {
|
|
|
50
50
|
* If false, the chrome will be hidden.
|
|
51
51
|
*/
|
|
52
52
|
showChrome: "show-chrome",
|
|
53
|
-
/**
|
|
54
|
-
* Override the display theme: `light`, `dark`, or `system`.
|
|
55
|
-
* Takes precedence over the notebook's saved `display.theme`.
|
|
56
|
-
* Ignored for embedded islands, which infer the theme from their host page.
|
|
57
|
-
*/
|
|
58
|
-
theme: "theme",
|
|
59
53
|
};
|
|
60
54
|
|
|
61
55
|
/**
|