@marimo-team/islands 0.23.5-dev1 → 0.23.5-dev11
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/{slide-form-CYU9AOO4.js → code-visibility-Bk5JSPxu.js} +849 -784
- package/dist/main.js +1039 -1073
- package/dist/{reveal-component-DK-5_Ei4.js → reveal-component-Cg99UrL3.js} +575 -527
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/data-table/export-actions.tsx +15 -1
- package/src/components/editor/file-tree/file-explorer.tsx +38 -8
- package/src/components/editor/file-tree/file-operations.tsx +19 -2
- package/src/components/editor/notebook-cell.tsx +33 -23
- package/src/components/editor/renderers/slides-layout/slides-layout.tsx +3 -5
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +2 -17
- package/src/components/slides/reveal-component.tsx +159 -47
- package/src/components/slides/reveal-slides.css +8 -0
- package/src/components/slides/slide-cell-view.tsx +182 -0
- package/src/components/slides/slide-form.tsx +2 -0
- package/src/core/cells/utils.ts +45 -0
- package/src/core/islands/stubs/slide-cell-view.tsx +30 -0
- package/src/core/meta/__tests__/code-visibility.test.tsx +141 -0
- package/src/core/meta/code-visibility.ts +48 -0
package/package.json
CHANGED
|
@@ -151,7 +151,21 @@ export const ExportMenu: React.FC<ExportActionProps> = (props) => {
|
|
|
151
151
|
}
|
|
152
152
|
const rawName = (result.filename ?? "").trim();
|
|
153
153
|
const baseName = Filenames.withoutExtension(rawName) || "download";
|
|
154
|
-
|
|
154
|
+
const downloadName = `${baseName}.${format}`;
|
|
155
|
+
// Append ?download=1 so the server returns Content-Disposition: attachment.
|
|
156
|
+
// This forces a save even when <a download> is ignored — e.g., inside
|
|
157
|
+
// sandboxed iframes that lack `allow-downloads`. Skip for data: URLs
|
|
158
|
+
// (used in pyodide/wasm) since query params would corrupt the payload.
|
|
159
|
+
let downloadUrl = result.url;
|
|
160
|
+
if (!downloadUrl.startsWith("data:")) {
|
|
161
|
+
const separator = downloadUrl.includes("?") ? "&" : "?";
|
|
162
|
+
const params = new URLSearchParams({
|
|
163
|
+
download: "1",
|
|
164
|
+
filename: downloadName,
|
|
165
|
+
});
|
|
166
|
+
downloadUrl = `${downloadUrl}${separator}${params.toString()}`;
|
|
167
|
+
}
|
|
168
|
+
downloadByURL(downloadUrl, downloadName);
|
|
155
169
|
};
|
|
156
170
|
|
|
157
171
|
const handleClipboardCopy = async (
|
|
@@ -523,7 +523,10 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileInfo>) => {
|
|
|
523
523
|
iconClassName="w-5 h-5"
|
|
524
524
|
>
|
|
525
525
|
{!node.data.isDirectory && (
|
|
526
|
-
<DropdownMenuItem
|
|
526
|
+
<DropdownMenuItem
|
|
527
|
+
onSelect={() => node.select()}
|
|
528
|
+
data-testid="file-explorer-open-file-menu-item"
|
|
529
|
+
>
|
|
527
530
|
<ViewIcon className={MENU_ITEM_ICON_CLASS} />
|
|
528
531
|
Open file
|
|
529
532
|
</DropdownMenuItem>
|
|
@@ -533,6 +536,7 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileInfo>) => {
|
|
|
533
536
|
onSelect={() => {
|
|
534
537
|
openFile({ path: node.data.path });
|
|
535
538
|
}}
|
|
539
|
+
data-testid="file-explorer-open-external-menu-item"
|
|
536
540
|
>
|
|
537
541
|
<ExternalLinkIcon className={MENU_ITEM_ICON_CLASS} />
|
|
538
542
|
Open file in external editor
|
|
@@ -540,28 +544,44 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileInfo>) => {
|
|
|
540
544
|
)}
|
|
541
545
|
{node.data.isDirectory && (
|
|
542
546
|
<>
|
|
543
|
-
<DropdownMenuItem
|
|
547
|
+
<DropdownMenuItem
|
|
548
|
+
onSelect={() => handleCreateNotebook()}
|
|
549
|
+
data-testid="file-explorer-create-notebook-menu-item"
|
|
550
|
+
>
|
|
544
551
|
<MarimoPlusIcon className={MENU_ITEM_ICON_CLASS} />
|
|
545
552
|
Create notebook
|
|
546
553
|
</DropdownMenuItem>
|
|
547
|
-
<DropdownMenuItem
|
|
554
|
+
<DropdownMenuItem
|
|
555
|
+
onSelect={() => handleCreateFile()}
|
|
556
|
+
data-testid="file-explorer-create-file-menu-item"
|
|
557
|
+
>
|
|
548
558
|
<FilePlus2Icon className={MENU_ITEM_ICON_CLASS} />
|
|
549
559
|
Create file
|
|
550
560
|
</DropdownMenuItem>
|
|
551
|
-
<DropdownMenuItem
|
|
561
|
+
<DropdownMenuItem
|
|
562
|
+
onSelect={() => handleCreateFolder()}
|
|
563
|
+
data-testid="file-explorer-create-folder-menu-item"
|
|
564
|
+
>
|
|
552
565
|
<FolderPlusIcon className={MENU_ITEM_ICON_CLASS} />
|
|
553
566
|
Create folder
|
|
554
567
|
</DropdownMenuItem>
|
|
555
568
|
<DropdownMenuSeparator />
|
|
556
569
|
</>
|
|
557
570
|
)}
|
|
558
|
-
<RenameMenuItem
|
|
559
|
-
|
|
571
|
+
<RenameMenuItem
|
|
572
|
+
onSelect={() => node.edit()}
|
|
573
|
+
testId="file-explorer-rename-menu-item"
|
|
574
|
+
/>
|
|
575
|
+
<DuplicateMenuItem
|
|
576
|
+
onSelect={handleDuplicate}
|
|
577
|
+
testId="file-explorer-duplicate-menu-item"
|
|
578
|
+
/>
|
|
560
579
|
<DropdownMenuItem
|
|
561
580
|
onSelect={async () => {
|
|
562
581
|
await copyToClipboard(node.data.path);
|
|
563
582
|
toast({ title: "Copied to clipboard" });
|
|
564
583
|
}}
|
|
584
|
+
data-testid="file-explorer-copy-path-menu-item"
|
|
565
585
|
>
|
|
566
586
|
<ListTreeIcon className={MENU_ITEM_ICON_CLASS} />
|
|
567
587
|
Copy path
|
|
@@ -574,6 +594,7 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileInfo>) => {
|
|
|
574
594
|
);
|
|
575
595
|
toast({ title: "Copied to clipboard" });
|
|
576
596
|
}}
|
|
597
|
+
data-testid="file-explorer-copy-relative-path-menu-item"
|
|
577
598
|
>
|
|
578
599
|
<ListTreeIcon className={MENU_ITEM_ICON_CLASS} />
|
|
579
600
|
Copy relative path
|
|
@@ -586,6 +607,7 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileInfo>) => {
|
|
|
586
607
|
const pythonCode = PYTHON_CODE_FOR_FILE_TYPE[fileType](path);
|
|
587
608
|
handleInsertCode(pythonCode);
|
|
588
609
|
}}
|
|
610
|
+
data-testid="file-explorer-insert-snippet-menu-item"
|
|
589
611
|
>
|
|
590
612
|
<BetweenHorizontalStartIcon className={MENU_ITEM_ICON_CLASS} />
|
|
591
613
|
Insert snippet for reading file
|
|
@@ -601,6 +623,7 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileInfo>) => {
|
|
|
601
623
|
const pythonCode = PYTHON_CODE_FOR_FILE_TYPE[fileType](path);
|
|
602
624
|
await copyToClipboard(pythonCode);
|
|
603
625
|
}}
|
|
626
|
+
data-testid="file-explorer-copy-snippet-menu-item"
|
|
604
627
|
>
|
|
605
628
|
<BracesIcon className={MENU_ITEM_ICON_CLASS} />
|
|
606
629
|
Copy snippet for reading file
|
|
@@ -608,7 +631,10 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileInfo>) => {
|
|
|
608
631
|
{node.data.isMarimoFile && !isWasm() && (
|
|
609
632
|
<>
|
|
610
633
|
<DropdownMenuSeparator />
|
|
611
|
-
<DropdownMenuItem
|
|
634
|
+
<DropdownMenuItem
|
|
635
|
+
onSelect={handleOpenMarimoFile}
|
|
636
|
+
data-testid="file-explorer-open-notebook-menu-item"
|
|
637
|
+
>
|
|
612
638
|
<PlaySquareIcon className={MENU_ITEM_ICON_CLASS} />
|
|
613
639
|
Open notebook
|
|
614
640
|
</DropdownMenuItem>
|
|
@@ -637,6 +663,7 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileInfo>) => {
|
|
|
637
663
|
);
|
|
638
664
|
}
|
|
639
665
|
}}
|
|
666
|
+
data-testid="file-explorer-download-menu-item"
|
|
640
667
|
>
|
|
641
668
|
<DownloadIcon className={MENU_ITEM_ICON_CLASS} />
|
|
642
669
|
Download
|
|
@@ -644,7 +671,10 @@ const Node = ({ node, style, dragHandle }: NodeRendererProps<FileInfo>) => {
|
|
|
644
671
|
<DropdownMenuSeparator />
|
|
645
672
|
</>
|
|
646
673
|
)}
|
|
647
|
-
<DeleteMenuItem
|
|
674
|
+
<DeleteMenuItem
|
|
675
|
+
onSelect={handleDeleteFile}
|
|
676
|
+
testId="file-explorer-delete-menu-item"
|
|
677
|
+
/>
|
|
648
678
|
</FileActionsDropdown>
|
|
649
679
|
</span>
|
|
650
680
|
</div>
|
|
@@ -219,12 +219,19 @@ export const RenameMenuItem = ({
|
|
|
219
219
|
onSelect,
|
|
220
220
|
disabled,
|
|
221
221
|
title,
|
|
222
|
+
testId,
|
|
222
223
|
}: {
|
|
223
224
|
onSelect: (evt: Event) => void;
|
|
224
225
|
disabled?: boolean;
|
|
225
226
|
title?: string;
|
|
227
|
+
testId?: string;
|
|
226
228
|
}) => (
|
|
227
|
-
<DropdownMenuItem
|
|
229
|
+
<DropdownMenuItem
|
|
230
|
+
onSelect={onSelect}
|
|
231
|
+
disabled={disabled}
|
|
232
|
+
title={title}
|
|
233
|
+
data-testid={testId}
|
|
234
|
+
>
|
|
228
235
|
<Edit3Icon className={MENU_ITEM_ICON_CLASS} />
|
|
229
236
|
Rename
|
|
230
237
|
</DropdownMenuItem>
|
|
@@ -234,12 +241,19 @@ export const DuplicateMenuItem = ({
|
|
|
234
241
|
onSelect,
|
|
235
242
|
disabled,
|
|
236
243
|
title,
|
|
244
|
+
testId,
|
|
237
245
|
}: {
|
|
238
246
|
onSelect: (evt: Event) => void;
|
|
239
247
|
disabled?: boolean;
|
|
240
248
|
title?: string;
|
|
249
|
+
testId?: string;
|
|
241
250
|
}) => (
|
|
242
|
-
<DropdownMenuItem
|
|
251
|
+
<DropdownMenuItem
|
|
252
|
+
onSelect={onSelect}
|
|
253
|
+
disabled={disabled}
|
|
254
|
+
title={title}
|
|
255
|
+
data-testid={testId}
|
|
256
|
+
>
|
|
243
257
|
<CopyIcon className={MENU_ITEM_ICON_CLASS} />
|
|
244
258
|
Duplicate
|
|
245
259
|
</DropdownMenuItem>
|
|
@@ -249,16 +263,19 @@ export const DeleteMenuItem = ({
|
|
|
249
263
|
onSelect,
|
|
250
264
|
disabled,
|
|
251
265
|
title,
|
|
266
|
+
testId,
|
|
252
267
|
}: {
|
|
253
268
|
onSelect: (evt: Event) => void;
|
|
254
269
|
disabled?: boolean;
|
|
255
270
|
title?: string;
|
|
271
|
+
testId?: string;
|
|
256
272
|
}) => (
|
|
257
273
|
<DropdownMenuItem
|
|
258
274
|
onSelect={onSelect}
|
|
259
275
|
variant="danger"
|
|
260
276
|
disabled={disabled}
|
|
261
277
|
title={title}
|
|
278
|
+
data-testid={testId}
|
|
262
279
|
>
|
|
263
280
|
<Trash2Icon className={MENU_ITEM_ICON_CLASS} />
|
|
264
281
|
Delete
|
|
@@ -48,7 +48,11 @@ import {
|
|
|
48
48
|
useCellRuntime,
|
|
49
49
|
} from "../../core/cells/cells";
|
|
50
50
|
import { type CellId, SETUP_CELL_ID } from "../../core/cells/ids";
|
|
51
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
cellNeedsRun,
|
|
53
|
+
cellStatusClasses,
|
|
54
|
+
isUninstantiated,
|
|
55
|
+
} from "../../core/cells/utils";
|
|
52
56
|
import type { UserConfig } from "../../core/config/config-schema";
|
|
53
57
|
import { isAppInteractionDisabled } from "../../core/websocket/connection-utils";
|
|
54
58
|
import { useCellRenderCount } from "../../hooks/useCellRenderCount";
|
|
@@ -390,9 +394,6 @@ const EditableCellComponent = ({
|
|
|
390
394
|
|
|
391
395
|
const [languageAdapter, setLanguageAdapter] = useState<LanguageAdapterType>();
|
|
392
396
|
|
|
393
|
-
const disabledOrAncestorDisabled =
|
|
394
|
-
cellData.config.disabled || cellRuntime.status === "disabled-transitively";
|
|
395
|
-
|
|
396
397
|
const uninstantiated = isUninstantiated({
|
|
397
398
|
executionTime: cellRuntime.runElapsedTimeMs ?? cellData.lastExecutionTime,
|
|
398
399
|
status: cellRuntime.status,
|
|
@@ -401,10 +402,13 @@ const EditableCellComponent = ({
|
|
|
401
402
|
stopped: cellRuntime.stopped,
|
|
402
403
|
});
|
|
403
404
|
|
|
404
|
-
const needsRun =
|
|
405
|
-
cellData.edited
|
|
406
|
-
cellRuntime.interrupted
|
|
407
|
-
|
|
405
|
+
const needsRun = cellNeedsRun({
|
|
406
|
+
edited: cellData.edited,
|
|
407
|
+
interrupted: cellRuntime.interrupted,
|
|
408
|
+
staleInputs: cellRuntime.staleInputs,
|
|
409
|
+
disabled: cellData.config.disabled,
|
|
410
|
+
status: cellRuntime.status,
|
|
411
|
+
});
|
|
408
412
|
|
|
409
413
|
const loading = outputIsLoading(cellRuntime.status);
|
|
410
414
|
|
|
@@ -532,11 +536,13 @@ const EditableCellComponent = ({
|
|
|
532
536
|
|
|
533
537
|
const className = clsx("marimo-cell", "hover-actions-parent z-10", {
|
|
534
538
|
interactive: true,
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
539
|
+
...cellStatusClasses({
|
|
540
|
+
needsRun,
|
|
541
|
+
errored: cellRuntime.errored,
|
|
542
|
+
stopped: cellRuntime.stopped,
|
|
543
|
+
disabled: cellData.config.disabled,
|
|
544
|
+
status: cellRuntime.status,
|
|
545
|
+
}),
|
|
540
546
|
borderless:
|
|
541
547
|
isMarkdownCodeHidden && hasOutput && !navigationProps["data-selected"],
|
|
542
548
|
});
|
|
@@ -979,9 +985,6 @@ const SetupCellComponent = ({
|
|
|
979
985
|
const setAiCompletionCell = useSetAtom(aiCompletionCellAtom);
|
|
980
986
|
const runCell = useRunCell(cellId);
|
|
981
987
|
|
|
982
|
-
const disabledOrAncestorDisabled =
|
|
983
|
-
cellData.config.disabled || cellRuntime.status === "disabled-transitively";
|
|
984
|
-
|
|
985
988
|
const uninstantiated = isUninstantiated({
|
|
986
989
|
executionTime: cellRuntime.runElapsedTimeMs ?? cellData.lastExecutionTime,
|
|
987
990
|
status: cellRuntime.status,
|
|
@@ -990,10 +993,13 @@ const SetupCellComponent = ({
|
|
|
990
993
|
stopped: cellRuntime.stopped,
|
|
991
994
|
});
|
|
992
995
|
|
|
993
|
-
const needsRun =
|
|
994
|
-
cellData.edited
|
|
995
|
-
cellRuntime.interrupted
|
|
996
|
-
|
|
996
|
+
const needsRun = cellNeedsRun({
|
|
997
|
+
edited: cellData.edited,
|
|
998
|
+
interrupted: cellRuntime.interrupted,
|
|
999
|
+
staleInputs: cellRuntime.staleInputs,
|
|
1000
|
+
disabled: cellData.config.disabled,
|
|
1001
|
+
status: cellRuntime.status,
|
|
1002
|
+
});
|
|
997
1003
|
const loading =
|
|
998
1004
|
cellRuntime.status === "running" || cellRuntime.status === "queued";
|
|
999
1005
|
|
|
@@ -1034,9 +1040,13 @@ const SetupCellComponent = ({
|
|
|
1034
1040
|
|
|
1035
1041
|
const className = clsx("marimo-cell", "hover-actions-parent z-10", {
|
|
1036
1042
|
interactive: true,
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1043
|
+
...cellStatusClasses({
|
|
1044
|
+
needsRun,
|
|
1045
|
+
errored: cellRuntime.errored,
|
|
1046
|
+
stopped: cellRuntime.stopped,
|
|
1047
|
+
disabled: cellData.config.disabled,
|
|
1048
|
+
status: cellRuntime.status,
|
|
1049
|
+
}),
|
|
1040
1050
|
});
|
|
1041
1051
|
|
|
1042
1052
|
const handleRefactorWithAI: OnRefactorWithAI = useEvent(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
import React, { useMemo,
|
|
2
|
+
import React, { useMemo, useState } from "react";
|
|
3
3
|
import { useAtomValue } from "jotai";
|
|
4
4
|
import { numColumnsAtom } from "@/core/cells/cells";
|
|
5
5
|
import type { CellId } from "@/core/cells/ids";
|
|
@@ -8,7 +8,6 @@ import type { SlidesLayout } from "./types";
|
|
|
8
8
|
import { computeSlideCellsInfo } from "./compute-slide-cells";
|
|
9
9
|
import { SlidesMinimap } from "@/components/slides/minimap";
|
|
10
10
|
import useEvent from "react-use-event-hook";
|
|
11
|
-
import type { RevealApi } from "reveal.js";
|
|
12
11
|
|
|
13
12
|
type Props = ICellRendererProps<SlidesLayout>;
|
|
14
13
|
|
|
@@ -26,7 +25,6 @@ export const SlidesLayoutRenderer: React.FC<Props> = ({
|
|
|
26
25
|
const numColumns = useAtomValue(numColumnsAtom);
|
|
27
26
|
const isMultiColumn = numColumns > 1;
|
|
28
27
|
const [activeCellId, setActiveCellId] = useState<CellId | null>(null);
|
|
29
|
-
const deckRef = useRef<RevealApi | null>(null);
|
|
30
28
|
|
|
31
29
|
const { cellsWithOutput, skippedIds, slideTypes, startCellIndex } = useMemo(
|
|
32
30
|
() => computeSlideCellsInfo(cells, layout),
|
|
@@ -53,9 +51,9 @@ export const SlidesLayoutRenderer: React.FC<Props> = ({
|
|
|
53
51
|
setLayout={setLayout}
|
|
54
52
|
activeIndex={resolvedIndex}
|
|
55
53
|
onSlideChange={handleSlideChange}
|
|
56
|
-
|
|
57
|
-
configWidth={250}
|
|
54
|
+
configWidth={300}
|
|
58
55
|
mode={mode}
|
|
56
|
+
isEditable={mode !== "read"}
|
|
59
57
|
/>
|
|
60
58
|
);
|
|
61
59
|
|
|
@@ -34,6 +34,7 @@ import { useResolvedMarimoConfig } from "@/core/config/config";
|
|
|
34
34
|
import { CSSClasses, KnownQueryParams } from "@/core/constants";
|
|
35
35
|
import type { MarimoError, OutputMessage } from "@/core/kernel/messages";
|
|
36
36
|
import { kernelStateAtom } from "@/core/kernel/state";
|
|
37
|
+
import { useNotebookCodeAvailable } from "@/core/meta/code-visibility";
|
|
37
38
|
import { showCodeInRunModeAtom } from "@/core/meta/state";
|
|
38
39
|
import { isErrorMime } from "@/core/mime";
|
|
39
40
|
import { type AppMode, kioskModeAtom } from "@/core/mode";
|
|
@@ -83,23 +84,7 @@ const VerticalLayoutRenderer: React.FC<VerticalLayoutProps> = ({
|
|
|
83
84
|
: showCodeByQueryParam === "true";
|
|
84
85
|
});
|
|
85
86
|
|
|
86
|
-
const
|
|
87
|
-
const cellsHaveCode = cells.some((cell) => Boolean(cell.code));
|
|
88
|
-
|
|
89
|
-
if (kioskMode) {
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Only show code if in read mode and there is at least one cell with code
|
|
94
|
-
|
|
95
|
-
// If it is a static-notebook or wasm-read-only-notebook, code is always included,
|
|
96
|
-
// but it can be turned it off via a query parameter (include-code=false)
|
|
97
|
-
|
|
98
|
-
const includeCode = urlParams.get(KnownQueryParams.includeCode);
|
|
99
|
-
return mode === "read" && includeCode !== "false" && cellsHaveCode;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const canShowCode = evaluateCanShowCode();
|
|
87
|
+
const canShowCode = useNotebookCodeAvailable(cells);
|
|
103
88
|
|
|
104
89
|
const renderCell = (cell: CellRuntimeState & CellData) => {
|
|
105
90
|
return (
|