@marimo-team/islands 0.23.9-dev8 → 0.23.10-dev0
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-OzrfMM5L.js → ConnectedDataExplorerComponent-CyV83R2m.js} +4 -4
- package/dist/assets/__vite-browser-external-Ci2ZQfXU.js +1 -0
- package/dist/assets/{worker-CpBbwbQo.js → worker-ip3AI_sN.js} +2 -2
- package/dist/{chat-ui-BDI3FMI8.js → chat-ui-ChD4VvCo.js} +3060 -3033
- package/dist/{code-visibility-SqsoLwxQ.js → code-visibility-CjGICDxg.js} +1368 -1204
- package/dist/{formats-DQ5qjo_Q.js → formats-DHxc-FdY.js} +1 -1
- package/dist/{glide-data-editor-DqRY9naW.js → glide-data-editor-BOmK9ETQ.js} +2 -2
- package/dist/{html-to-image-CiSinpSR.js → html-to-image-BHv7CEU_.js} +2145 -2153
- package/dist/{input-CZD2z6X2.js → input-_2sjvfne.js} +1 -1
- package/dist/main.js +680 -705
- package/dist/{mermaid-IU93XzmY.js → mermaid-lXOw5Py9.js} +2 -2
- package/dist/{process-output-5qJjMRKh.js → process-output-BvySRgli.js} +33 -25
- package/dist/{reveal-component-v4zHgynl.js → reveal-component-DVWED--8.js} +312 -291
- package/dist/{spec-a6DaqW__.js → spec-B96zNUEA.js} +1 -1
- package/dist/style.css +1 -1
- package/dist/{toDate-ZVVIBmdk.js → toDate-x-WRDCH7.js} +1 -1
- package/dist/{useAsyncData-C008zUPi.js → useAsyncData-iRgKDT5s.js} +1 -1
- package/dist/{useDeepCompareMemoize-BrA3_n61.js → useDeepCompareMemoize-CkQ57VS2.js} +1 -1
- package/dist/{useLifecycle-BNaoJ5a4.js → useLifecycle-BBO9PIph.js} +1 -1
- package/dist/{useTheme-7O0YWlE5.js → useTheme-DHIrRQOe.js} +34 -21
- package/dist/{vega-component-DJNmOdUj.js → vega-component-Dq-SH463.js} +5 -5
- package/package.json +1 -1
- package/src/components/ai/__tests__/ai-utils.test.ts +43 -38
- package/src/components/ai/ai-model-dropdown.tsx +2 -2
- package/src/components/app-config/ai-config.tsx +147 -16
- package/src/components/app-config/user-config-form.tsx +37 -1
- package/src/components/chat/__tests__/chat-utils.test.ts +269 -0
- package/src/components/chat/chat-panel.tsx +38 -5
- package/src/components/chat/chat-utils.ts +14 -58
- package/src/components/data-table/TableBottomBar.tsx +5 -8
- package/src/components/data-table/__tests__/column-explorer.test.tsx +128 -0
- package/src/components/data-table/__tests__/header-items.test.tsx +220 -10
- package/src/components/data-table/column-explorer-panel/column-explorer.tsx +95 -29
- package/src/components/data-table/column-header.tsx +17 -12
- package/src/components/data-table/data-table.tsx +4 -0
- package/src/components/data-table/export-actions.tsx +19 -12
- package/src/components/data-table/header-items.tsx +40 -16
- package/src/components/data-table/hooks/use-column-visibility.ts +14 -0
- package/src/components/data-table/schemas.ts +2 -2
- package/src/components/data-table/table-explorer-panel/table-explorer-panel.tsx +16 -6
- package/src/components/databases/display.tsx +2 -0
- package/src/components/datasources/__tests__/utils.test.ts +82 -0
- package/src/components/datasources/utils.ts +16 -15
- package/src/components/editor/Disconnected.tsx +1 -60
- package/src/components/editor/__tests__/viewer-banner.test.tsx +89 -0
- package/src/components/editor/actions/pair-with-agent-modal.tsx +1 -0
- package/src/components/editor/actions/useCellActionButton.tsx +3 -3
- package/src/components/editor/actions/useNotebookActions.tsx +5 -2
- package/src/components/editor/cell/code/cell-editor.tsx +25 -5
- package/src/components/editor/chrome/types.ts +13 -6
- package/src/components/editor/chrome/wrapper/app-chrome.tsx +6 -4
- package/src/components/editor/chrome/wrapper/footer-items/ai-status.tsx +10 -1
- package/src/components/editor/chrome/wrapper/sidebar.tsx +7 -5
- package/src/components/editor/errors/auto-fix.tsx +3 -3
- package/src/components/editor/header/__tests__/status.test.tsx +0 -15
- package/src/components/editor/header/app-header.tsx +1 -4
- package/src/components/editor/header/status.tsx +4 -13
- package/src/components/editor/navigation/__tests__/navigation.test.ts +15 -0
- package/src/components/editor/navigation/navigation.ts +5 -0
- package/src/components/editor/output/MarimoErrorOutput.tsx +103 -25
- package/src/components/editor/output/MarimoTracebackOutput.tsx +28 -39
- package/src/components/editor/renderers/cell-array.tsx +27 -24
- package/src/components/editor/renderers/slides-layout/__tests__/compute-slide-cells.test.ts +30 -17
- package/src/components/editor/renderers/slides-layout/compute-slide-cells.ts +17 -8
- package/src/components/editor/renderers/slides-layout/slides-layout.tsx +10 -12
- package/src/components/editor/viewer-banner.tsx +82 -0
- package/src/components/slides/minimap.tsx +45 -9
- package/src/components/slides/reveal-component.tsx +82 -37
- package/src/components/slides/slide-cell-view.tsx +12 -1
- package/src/components/slides/slide-form.tsx +11 -3
- package/src/components/static-html/static-banner.tsx +28 -22
- package/src/core/ai/__tests__/model-registry.test.ts +72 -60
- package/src/core/ai/model-registry.ts +33 -28
- package/src/core/cells/__tests__/actions.test.ts +48 -0
- package/src/core/cells/actions.ts +5 -6
- package/src/core/codemirror/__tests__/setup.test.ts +29 -0
- package/src/core/codemirror/cells/traceback-decorations.ts +1 -1
- package/src/core/codemirror/cm.ts +50 -3
- package/src/core/codemirror/completion/hints.ts +4 -1
- package/src/core/codemirror/format.ts +1 -0
- package/src/core/codemirror/keymaps/vim.ts +63 -0
- package/src/core/codemirror/language/languages/sql/sql.ts +1 -0
- package/src/core/codemirror/language/languages/sql/utils.ts +2 -0
- package/src/core/config/__tests__/config-schema.test.ts +4 -0
- package/src/core/config/config-schema.ts +4 -0
- package/src/core/config/config.ts +16 -0
- package/src/core/edit-app.tsx +3 -0
- package/src/core/islands/bootstrap.ts +2 -0
- package/src/core/kernel/__tests__/handlers.test.ts +5 -0
- package/src/core/websocket/__tests__/useMarimoKernelConnection.test.ts +0 -13
- package/src/core/websocket/types.ts +0 -6
- package/src/core/websocket/useMarimoKernelConnection.tsx +3 -12
- package/src/css/app/Cell.css +0 -1
- package/src/plugins/impl/DataTablePlugin.tsx +48 -22
- package/src/plugins/impl/chat/ChatPlugin.tsx +7 -1
- package/src/plugins/impl/chat/__tests__/chat-ui.test.ts +278 -0
- package/src/plugins/impl/chat/chat-ui.tsx +106 -59
- package/src/plugins/impl/chat/types.ts +5 -0
- package/src/utils/__tests__/json-parser.test.ts +1 -69
- package/src/utils/json/json-parser.ts +0 -30
- package/dist/assets/__vite-browser-external-CAdMKBac.js +0 -1
|
@@ -66,6 +66,7 @@ interface SlideThumbnailCardProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
|
66
66
|
isActiveDragSource?: boolean;
|
|
67
67
|
isOverlay?: boolean;
|
|
68
68
|
isVisible?: boolean;
|
|
69
|
+
isNoOutput?: boolean;
|
|
69
70
|
slideType?: SlideType;
|
|
70
71
|
ref?: React.Ref<HTMLDivElement>;
|
|
71
72
|
}
|
|
@@ -77,6 +78,7 @@ interface SlideThumbnailRowProps extends React.ButtonHTMLAttributes<HTMLButtonEl
|
|
|
77
78
|
dropIndicator?: DropPosition | null;
|
|
78
79
|
isActiveDragSource?: boolean;
|
|
79
80
|
isVisible?: boolean;
|
|
81
|
+
isNoOutput?: boolean;
|
|
80
82
|
slideType?: SlideType;
|
|
81
83
|
ref?: React.Ref<HTMLButtonElement>;
|
|
82
84
|
}
|
|
@@ -86,15 +88,23 @@ interface SlidesMinimapProps {
|
|
|
86
88
|
thumbnailWidth: number;
|
|
87
89
|
canReorder: boolean;
|
|
88
90
|
activeCellId: CellId | null;
|
|
89
|
-
// Set of cell ids that
|
|
91
|
+
// Set of cell ids that should be visually treated like skipped entries.
|
|
90
92
|
skippedIds?: ReadonlySet<CellId>;
|
|
93
|
+
// Set of cell ids that currently have no rendered output.
|
|
94
|
+
noOutputIds?: ReadonlySet<CellId>;
|
|
91
95
|
slideTypes?: ReadonlyMap<CellId, SlideType>;
|
|
92
96
|
onSlideClick: (index: number) => void;
|
|
93
97
|
}
|
|
94
98
|
|
|
99
|
+
interface ThumbnailVisual {
|
|
100
|
+
label: string;
|
|
101
|
+
description: string;
|
|
102
|
+
Icon?: LucideIcon;
|
|
103
|
+
}
|
|
104
|
+
|
|
95
105
|
function getSlideTypeVisual(
|
|
96
106
|
slideType: SlideType | undefined,
|
|
97
|
-
):
|
|
107
|
+
): ThumbnailVisual | null {
|
|
98
108
|
if (!slideType || slideType === "slide") {
|
|
99
109
|
return null;
|
|
100
110
|
}
|
|
@@ -102,8 +112,13 @@ function getSlideTypeVisual(
|
|
|
102
112
|
return { label, description, Icon };
|
|
103
113
|
}
|
|
104
114
|
|
|
115
|
+
const NO_OUTPUT_VISUAL: ThumbnailVisual = {
|
|
116
|
+
label: "No output",
|
|
117
|
+
description: "Hidden because this cell has no output.",
|
|
118
|
+
};
|
|
119
|
+
|
|
105
120
|
const SLIDE_ASPECT_RATIO = 16 / 9;
|
|
106
|
-
const SLIDE_BASE_WIDTH =
|
|
121
|
+
const SLIDE_BASE_WIDTH = 520;
|
|
107
122
|
|
|
108
123
|
function computeThumbnailDimensions(width: number): ThumbnailDimensions {
|
|
109
124
|
return {
|
|
@@ -196,6 +211,7 @@ export const SlidesMinimap = ({
|
|
|
196
211
|
canReorder,
|
|
197
212
|
activeCellId,
|
|
198
213
|
skippedIds,
|
|
214
|
+
noOutputIds,
|
|
199
215
|
slideTypes,
|
|
200
216
|
onSlideClick,
|
|
201
217
|
}: SlidesMinimapProps) => {
|
|
@@ -288,6 +304,7 @@ export const SlidesMinimap = ({
|
|
|
288
304
|
dimensions={dimensions}
|
|
289
305
|
isActiveSlide={cell.id === activeCellId}
|
|
290
306
|
isVisible={visibleIds.has(cell.id)}
|
|
307
|
+
isNoOutput={noOutputIds?.has(cell.id)}
|
|
291
308
|
slideType={resolveSlideType({
|
|
292
309
|
cellId: cell.id,
|
|
293
310
|
slideTypes,
|
|
@@ -325,6 +342,7 @@ export const SlidesMinimap = ({
|
|
|
325
342
|
isActive={activeId === cell.id}
|
|
326
343
|
isActiveSlide={cell.id === activeCellId}
|
|
327
344
|
isVisible={visibleIds.has(cell.id)}
|
|
345
|
+
isNoOutput={noOutputIds?.has(cell.id)}
|
|
328
346
|
slideType={resolveSlideType({
|
|
329
347
|
cellId: cell.id,
|
|
330
348
|
slideTypes,
|
|
@@ -347,6 +365,7 @@ export const SlidesMinimap = ({
|
|
|
347
365
|
dimensions={dimensions}
|
|
348
366
|
isOverlay={true}
|
|
349
367
|
isActiveDragSource={true}
|
|
368
|
+
isNoOutput={noOutputIds?.has(activeCell.id)}
|
|
350
369
|
/>
|
|
351
370
|
)}
|
|
352
371
|
</DragOverlay>
|
|
@@ -378,6 +397,7 @@ interface SortableSlideThumbnailProps {
|
|
|
378
397
|
isActive: boolean;
|
|
379
398
|
isActiveSlide?: boolean;
|
|
380
399
|
isVisible?: boolean;
|
|
400
|
+
isNoOutput?: boolean;
|
|
381
401
|
slideType?: SlideType;
|
|
382
402
|
onClick?: () => void;
|
|
383
403
|
}
|
|
@@ -389,6 +409,7 @@ const SortableSlideThumbnail = ({
|
|
|
389
409
|
isActive,
|
|
390
410
|
isActiveSlide,
|
|
391
411
|
isVisible,
|
|
412
|
+
isNoOutput,
|
|
392
413
|
slideType,
|
|
393
414
|
onClick,
|
|
394
415
|
}: SortableSlideThumbnailProps) => {
|
|
@@ -405,6 +426,7 @@ const SortableSlideThumbnail = ({
|
|
|
405
426
|
isActiveDragSource={isActive}
|
|
406
427
|
isActiveSlide={isActiveSlide}
|
|
407
428
|
isVisible={isVisible}
|
|
429
|
+
isNoOutput={isNoOutput}
|
|
408
430
|
slideType={slideType}
|
|
409
431
|
onClick={onClick}
|
|
410
432
|
{...attributes}
|
|
@@ -422,6 +444,7 @@ const SlideThumbnailRow = ({
|
|
|
422
444
|
isActiveSlide = false,
|
|
423
445
|
isActiveDragSource = false,
|
|
424
446
|
isVisible,
|
|
447
|
+
isNoOutput,
|
|
425
448
|
slideType,
|
|
426
449
|
onClick,
|
|
427
450
|
ref,
|
|
@@ -462,6 +485,7 @@ const SlideThumbnailRow = ({
|
|
|
462
485
|
isActiveSlide={isActiveSlide}
|
|
463
486
|
isActiveDragSource={isActiveDragSource}
|
|
464
487
|
isVisible={isVisible}
|
|
488
|
+
isNoOutput={isNoOutput}
|
|
465
489
|
slideType={slideType}
|
|
466
490
|
/>
|
|
467
491
|
</button>
|
|
@@ -477,13 +501,14 @@ const SlideThumbnailCard = ({
|
|
|
477
501
|
isActiveDragSource = false,
|
|
478
502
|
isOverlay = false,
|
|
479
503
|
isVisible = false,
|
|
504
|
+
isNoOutput = false,
|
|
480
505
|
slideType,
|
|
481
506
|
ref,
|
|
482
507
|
...props
|
|
483
508
|
}: SlideThumbnailCardProps) => {
|
|
484
509
|
const { width, height, scale } = dimensions;
|
|
485
|
-
const visual = getSlideTypeVisual(slideType);
|
|
486
|
-
const isSkipped = slideType === "skip";
|
|
510
|
+
const visual = isNoOutput ? NO_OUTPUT_VISUAL : getSlideTypeVisual(slideType);
|
|
511
|
+
const isSkipped = isNoOutput || slideType === "skip";
|
|
487
512
|
|
|
488
513
|
const outerStyle: React.CSSProperties = {
|
|
489
514
|
width,
|
|
@@ -525,16 +550,20 @@ const SlideThumbnailCard = ({
|
|
|
525
550
|
height: height / scale,
|
|
526
551
|
}}
|
|
527
552
|
>
|
|
528
|
-
|
|
553
|
+
{isNoOutput ? (
|
|
554
|
+
<MiniCodePreview code={cell.code} />
|
|
555
|
+
) : (
|
|
556
|
+
<Slide cellId={cell.id} status={cell.status} output={cell.output} />
|
|
557
|
+
)}
|
|
529
558
|
</div>
|
|
530
559
|
)}
|
|
531
560
|
{isSkipped && (
|
|
532
561
|
<div
|
|
533
|
-
className="absolute inset-0 bg-muted/
|
|
562
|
+
className="absolute inset-0 bg-muted/50 pointer-events-none"
|
|
534
563
|
aria-hidden={true}
|
|
535
564
|
/>
|
|
536
565
|
)}
|
|
537
|
-
{visual && (
|
|
566
|
+
{visual?.Icon && (
|
|
538
567
|
<Tooltip
|
|
539
568
|
content={
|
|
540
569
|
<span className="text-xs opacity-80">{visual.description}</span>
|
|
@@ -550,7 +579,6 @@ const SlideThumbnailCard = ({
|
|
|
550
579
|
aria-label={visual.label}
|
|
551
580
|
>
|
|
552
581
|
<visual.Icon className="h-3.5 w-3.5" />
|
|
553
|
-
{/* <span>{visual.label}</span> */}
|
|
554
582
|
</span>
|
|
555
583
|
</Tooltip>
|
|
556
584
|
)}
|
|
@@ -558,6 +586,14 @@ const SlideThumbnailCard = ({
|
|
|
558
586
|
);
|
|
559
587
|
};
|
|
560
588
|
|
|
589
|
+
const MiniCodePreview = ({ code }: { code: string }) => {
|
|
590
|
+
return (
|
|
591
|
+
<pre className="my-auto w-full overflow-hidden whitespace-pre-wrap wrap-break-word text-lg">
|
|
592
|
+
{code}
|
|
593
|
+
</pre>
|
|
594
|
+
);
|
|
595
|
+
};
|
|
596
|
+
|
|
561
597
|
function projectDropTarget(
|
|
562
598
|
event: DragMoveEvent | DragOverEvent,
|
|
563
599
|
): ProjectedDropTarget | null {
|
|
@@ -221,26 +221,54 @@ const SubslideView = ({
|
|
|
221
221
|
);
|
|
222
222
|
};
|
|
223
223
|
|
|
224
|
+
const ParkedPreviewContent = ({
|
|
225
|
+
cell,
|
|
226
|
+
isNoOutputPreview,
|
|
227
|
+
isEditable,
|
|
228
|
+
codeShown,
|
|
229
|
+
}: {
|
|
230
|
+
cell: RuntimeCell;
|
|
231
|
+
isNoOutputPreview: boolean;
|
|
232
|
+
isEditable: boolean;
|
|
233
|
+
codeShown: boolean;
|
|
234
|
+
}) => {
|
|
235
|
+
if (isNoOutputPreview && isEditable) {
|
|
236
|
+
return <SlideCellView cell={cell} />;
|
|
237
|
+
}
|
|
238
|
+
if (isNoOutputPreview && codeShown) {
|
|
239
|
+
return <SlideCellReadOnlyView cell={cell} />;
|
|
240
|
+
}
|
|
241
|
+
return (
|
|
242
|
+
<CellOutputSlide
|
|
243
|
+
cellId={cell.id}
|
|
244
|
+
status={cell.status}
|
|
245
|
+
output={cell.output}
|
|
246
|
+
/>
|
|
247
|
+
);
|
|
248
|
+
};
|
|
249
|
+
|
|
224
250
|
// There is an upstream react bug in dev mode (https://github.com/facebook/react/issues/34840)
|
|
225
251
|
// Uncaught SecurityError: Failed to read a named property '$$typeof' from 'Window'
|
|
226
252
|
// Happens with cells containing iframes / external content
|
|
227
253
|
const RevealSlidesComponent = ({
|
|
228
|
-
|
|
254
|
+
slideCells,
|
|
229
255
|
layout,
|
|
230
256
|
setLayout,
|
|
257
|
+
noOutputIds,
|
|
231
258
|
activeIndex,
|
|
232
259
|
onSlideChange,
|
|
233
260
|
mode,
|
|
234
|
-
configWidth
|
|
261
|
+
configWidth, // px
|
|
235
262
|
isEditable = false,
|
|
236
263
|
}: {
|
|
237
|
-
|
|
264
|
+
slideCells: RuntimeCell[];
|
|
238
265
|
layout: SlidesLayout;
|
|
239
266
|
setLayout: (layout: SlidesLayout) => void;
|
|
267
|
+
noOutputIds: ReadonlySet<CellId>;
|
|
240
268
|
activeIndex?: number;
|
|
241
269
|
onSlideChange?: (index: number) => void;
|
|
242
270
|
mode: AppMode;
|
|
243
|
-
configWidth
|
|
271
|
+
configWidth: number;
|
|
244
272
|
isEditable?: boolean;
|
|
245
273
|
}) => {
|
|
246
274
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
@@ -255,42 +283,46 @@ const RevealSlidesComponent = ({
|
|
|
255
283
|
);
|
|
256
284
|
|
|
257
285
|
const [showCode, setShowCode] = useState(false);
|
|
258
|
-
const codeAvailable = useNotebookCodeAvailable(
|
|
286
|
+
const codeAvailable = useNotebookCodeAvailable(slideCells);
|
|
259
287
|
const codeToggleEnabled = !isIslands() && codeAvailable;
|
|
260
288
|
const codeShown = codeToggleEnabled && showCode;
|
|
261
289
|
|
|
262
|
-
const activeCell =
|
|
263
|
-
activeIndex != null ? cellsWithOutput[activeIndex] : undefined;
|
|
290
|
+
const activeCell = activeIndex != null ? slideCells[activeIndex] : undefined;
|
|
264
291
|
// Fall back to the first cell while the deck settles on an initial slide.
|
|
265
292
|
// Still `undefined` when the deck is empty (handled below).
|
|
266
|
-
const activeConfigCell = activeCell ??
|
|
293
|
+
const activeConfigCell = activeCell ?? slideCells.at(0);
|
|
267
294
|
|
|
268
295
|
const composition = useMemo(
|
|
269
296
|
() =>
|
|
270
297
|
composeSlides({
|
|
271
|
-
cells:
|
|
298
|
+
cells: slideCells,
|
|
272
299
|
getType: (cell) =>
|
|
273
|
-
|
|
300
|
+
noOutputIds.has(cell.id)
|
|
301
|
+
? "skip"
|
|
302
|
+
: (layout.cells.get(cell.id)?.type ?? DEFAULT_SLIDE_TYPE),
|
|
274
303
|
}),
|
|
275
|
-
[
|
|
304
|
+
[slideCells, noOutputIds, layout.cells],
|
|
276
305
|
);
|
|
277
306
|
|
|
278
|
-
//
|
|
279
|
-
// minimap we render a preview over the deck and park reveal on
|
|
280
|
-
// real slide; keyboard nav while parked is handled below.
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
307
|
+
// Skipped and output-less cells aren't part of the composed deck. When one is
|
|
308
|
+
// selected in the minimap we render a preview over the deck and park reveal on
|
|
309
|
+
// a neighboring real slide; keyboard nav while parked is handled below.
|
|
310
|
+
const activeCellSlideType = activeCell
|
|
311
|
+
? layout.cells.get(activeCell.id)?.type
|
|
312
|
+
: undefined;
|
|
313
|
+
const isNoOutputPreview =
|
|
314
|
+
activeCell != null && noOutputIds.has(activeCell.id);
|
|
315
|
+
const isParkedPreview = activeCellSlideType === "skip" || isNoOutputPreview;
|
|
316
|
+
const parkedPreviewCell = isParkedPreview ? activeCell : null;
|
|
285
317
|
|
|
286
318
|
const { cellToTarget, targetToCellIndex } = useMemo(
|
|
287
319
|
() =>
|
|
288
320
|
buildSlideIndices({
|
|
289
321
|
composition,
|
|
290
|
-
cells:
|
|
322
|
+
cells: slideCells,
|
|
291
323
|
getId: (c) => c.id,
|
|
292
324
|
}),
|
|
293
|
-
[composition,
|
|
325
|
+
[composition, slideCells],
|
|
294
326
|
);
|
|
295
327
|
|
|
296
328
|
const deckTransition = layout.deck?.transition ?? DEFAULT_DECK_TRANSITION;
|
|
@@ -322,7 +354,7 @@ const RevealSlidesComponent = ({
|
|
|
322
354
|
const navigateDeckToActiveCell = useEvent((deck: RevealApi) => {
|
|
323
355
|
const target = resolveDeckNavigationTarget({
|
|
324
356
|
activeIndex,
|
|
325
|
-
cells:
|
|
357
|
+
cells: slideCells,
|
|
326
358
|
cellToTarget,
|
|
327
359
|
getId: (cell) => cell.id,
|
|
328
360
|
});
|
|
@@ -340,7 +372,7 @@ const RevealSlidesComponent = ({
|
|
|
340
372
|
return;
|
|
341
373
|
}
|
|
342
374
|
navigateDeckToActiveCell(deck);
|
|
343
|
-
}, [activeIndex, cellToTarget,
|
|
375
|
+
}, [activeIndex, cellToTarget, slideCells, navigateDeckToActiveCell]);
|
|
344
376
|
|
|
345
377
|
// Toggling code (re)mounts a CodeMirror editor on the active slide. Defer
|
|
346
378
|
// the state update so the button/keypress paints first and the heavier mount
|
|
@@ -380,12 +412,12 @@ const RevealSlidesComponent = ({
|
|
|
380
412
|
return { h: target.h, v: target.v };
|
|
381
413
|
}, [activeCell, cellToTarget]);
|
|
382
414
|
|
|
383
|
-
// Forward the deck's current cell to the parent, except while a
|
|
415
|
+
// Forward the deck's current cell to the parent, except while a parked
|
|
384
416
|
// preview is parked: every reveal.js event during that window is an echo
|
|
385
417
|
// of the programmatic park (possibly with transient indices), so ignoring
|
|
386
|
-
// them keeps `activeCellId` pinned on the
|
|
418
|
+
// them keeps `activeCellId` pinned on the minimap cell.
|
|
387
419
|
const reportCurrentCell = useEvent(() => {
|
|
388
|
-
if (
|
|
420
|
+
if (parkedPreviewCell != null) {
|
|
389
421
|
return;
|
|
390
422
|
}
|
|
391
423
|
const deck = deckRef.current;
|
|
@@ -401,10 +433,10 @@ const RevealSlidesComponent = ({
|
|
|
401
433
|
}
|
|
402
434
|
});
|
|
403
435
|
|
|
404
|
-
// While parked on a
|
|
436
|
+
// While parked on a preview, step through minimap order instead of
|
|
405
437
|
// letting reveal.js advance from the parked slide the user can't see.
|
|
406
438
|
const handleParkedNavKey = useEvent((event: KeyboardEvent) => {
|
|
407
|
-
if (!
|
|
439
|
+
if (!parkedPreviewCell || activeIndex == null) {
|
|
408
440
|
return;
|
|
409
441
|
}
|
|
410
442
|
if (Events.fromInput(event)) {
|
|
@@ -418,7 +450,7 @@ const RevealSlidesComponent = ({
|
|
|
418
450
|
event.preventDefault();
|
|
419
451
|
event.stopPropagation();
|
|
420
452
|
const nextIndex = activeIndex + direction;
|
|
421
|
-
if (nextIndex < 0 || nextIndex >=
|
|
453
|
+
if (nextIndex < 0 || nextIndex >= slideCells.length) {
|
|
422
454
|
return;
|
|
423
455
|
}
|
|
424
456
|
onSlideChange?.(nextIndex);
|
|
@@ -431,6 +463,10 @@ const RevealSlidesComponent = ({
|
|
|
431
463
|
|
|
432
464
|
useEventListener(document, "keydown", handleParkedNavKey, { capture: true });
|
|
433
465
|
|
|
466
|
+
const parkedPreviewLabel = isNoOutputPreview
|
|
467
|
+
? "Hidden as there is no output"
|
|
468
|
+
: "Skipped in presentation";
|
|
469
|
+
|
|
434
470
|
const slideArea = (
|
|
435
471
|
<div
|
|
436
472
|
ref={containerRef}
|
|
@@ -439,7 +475,7 @@ const RevealSlidesComponent = ({
|
|
|
439
475
|
<div className="group relative" style={{ width, height }}>
|
|
440
476
|
<Deck
|
|
441
477
|
deckRef={deckRef}
|
|
442
|
-
className="aspect-video w-full overflow-hidden border rounded bg-background mo-slides-theme prose-slides"
|
|
478
|
+
className="aspect-video w-full overflow-hidden border rounded bg-background mo-slides-theme prose-slides focus:outline-none focus-visible:outline-none"
|
|
443
479
|
config={revealConfig}
|
|
444
480
|
onReady={handleDeckReady}
|
|
445
481
|
onSlideChange={handleSlideChange}
|
|
@@ -480,21 +516,30 @@ const RevealSlidesComponent = ({
|
|
|
480
516
|
);
|
|
481
517
|
})}
|
|
482
518
|
</Deck>
|
|
483
|
-
{
|
|
519
|
+
{parkedPreviewCell && (
|
|
484
520
|
<div
|
|
521
|
+
key={parkedPreviewCell.id}
|
|
485
522
|
className="absolute inset-0 z-10 border rounded bg-background flex flex-col overflow-hidden"
|
|
486
|
-
aria-label=
|
|
523
|
+
aria-label={parkedPreviewLabel}
|
|
487
524
|
>
|
|
488
525
|
<div className="flex items-center gap-1.5 px-3 py-1.5 text-xs text-muted-foreground border-b bg-muted/40">
|
|
489
526
|
<EyeOffIcon className="h-3.5 w-3.5" />
|
|
490
|
-
<span>
|
|
527
|
+
<span>{parkedPreviewLabel}</span>
|
|
491
528
|
</div>
|
|
492
529
|
<div className="flex-1 overflow-auto flex">
|
|
493
|
-
<div
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
530
|
+
<div
|
|
531
|
+
className={
|
|
532
|
+
isNoOutputPreview && (isEditable || codeShown)
|
|
533
|
+
? "mo-slide-content flex flex-col gap-3"
|
|
534
|
+
: "mo-slide-content"
|
|
535
|
+
}
|
|
536
|
+
style={{ margin: "auto 20px" }}
|
|
537
|
+
>
|
|
538
|
+
<ParkedPreviewContent
|
|
539
|
+
cell={parkedPreviewCell}
|
|
540
|
+
isNoOutputPreview={isNoOutputPreview}
|
|
541
|
+
isEditable={isEditable}
|
|
542
|
+
codeShown={codeShown}
|
|
498
543
|
/>
|
|
499
544
|
</div>
|
|
500
545
|
</div>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { useMemo, useRef, useState } from "react";
|
|
4
4
|
import type { EditorView } from "@codemirror/view";
|
|
5
5
|
import { useAtomValue } from "jotai";
|
|
6
|
+
import { cellDomProps } from "@/components/editor/common";
|
|
6
7
|
import { CellEditor } from "@/components/editor/cell/code/cell-editor";
|
|
7
8
|
import { CellStatusComponent } from "@/components/editor/cell/CellStatus";
|
|
8
9
|
import { RunButton } from "@/components/editor/cell/RunButton";
|
|
@@ -111,7 +112,10 @@ export const SlideCellView = ({ cell }: { cell: RuntimeCell }) => {
|
|
|
111
112
|
);
|
|
112
113
|
|
|
113
114
|
const editor = (
|
|
114
|
-
<div
|
|
115
|
+
<div
|
|
116
|
+
className={editorWrapperClassName}
|
|
117
|
+
{...cellDomProps(cell.id, cell.name)}
|
|
118
|
+
>
|
|
115
119
|
<CellEditor
|
|
116
120
|
theme={theme}
|
|
117
121
|
showPlaceholder={false}
|
|
@@ -134,7 +138,14 @@ export const SlideCellView = ({ cell }: { cell: RuntimeCell }) => {
|
|
|
134
138
|
languageAdapter={languageAdapter}
|
|
135
139
|
setLanguageAdapter={setLanguageAdapter}
|
|
136
140
|
showLanguageToggles={false}
|
|
141
|
+
// The reveal.js transforms in slides view break the inline AI
|
|
142
|
+
// tooltip's fixed positioning, so disable it here.
|
|
143
|
+
inlineAiTooltip={false}
|
|
137
144
|
outputArea={cellOutputPosition}
|
|
145
|
+
// Parent tooltips (completions, hover, signature help) to the Reveal
|
|
146
|
+
// viewport so they stay visible when presenting fullscreen; `#App` sits
|
|
147
|
+
// outside the fullscreened subtree and would never paint.
|
|
148
|
+
tooltipParentSelector=".reveal-viewport"
|
|
138
149
|
/>
|
|
139
150
|
{toolbar}
|
|
140
151
|
</div>
|
|
@@ -26,15 +26,23 @@ import type {
|
|
|
26
26
|
SlidesLayout,
|
|
27
27
|
SlideType,
|
|
28
28
|
} from "../editor/renderers/slides-layout/types";
|
|
29
|
-
import {
|
|
29
|
+
import { useAtom } from "jotai";
|
|
30
|
+
import { atomWithStorage } from "jotai/utils";
|
|
30
31
|
import { Tooltip } from "../ui/tooltip";
|
|
31
32
|
import { Button } from "../ui/button";
|
|
32
33
|
import { Kbd } from "../ui/kbd";
|
|
33
34
|
import type { RuntimeCell } from "@/core/cells/types";
|
|
35
|
+
import { jotaiJsonStorage } from "@/utils/storage/jotai";
|
|
34
36
|
|
|
35
37
|
export const DEFAULT_SLIDE_TYPE: SlideType = "slide";
|
|
36
38
|
export const DEFAULT_DECK_TRANSITION: DeckTransition = "slide";
|
|
37
39
|
const COLLAPSED_CONFIG_WIDTH = 36;
|
|
40
|
+
const slideConfigOpenAtom = atomWithStorage<boolean>(
|
|
41
|
+
"marimo:slides:config-open",
|
|
42
|
+
true,
|
|
43
|
+
jotaiJsonStorage,
|
|
44
|
+
{ getOnInit: true },
|
|
45
|
+
);
|
|
38
46
|
|
|
39
47
|
export interface SlideTypeOption {
|
|
40
48
|
value: SlideType;
|
|
@@ -322,7 +330,7 @@ export const SlideSidebar = ({
|
|
|
322
330
|
setLayout: (layout: SlidesLayout) => void;
|
|
323
331
|
activeConfigCell?: RuntimeCell;
|
|
324
332
|
}) => {
|
|
325
|
-
const [isConfigOpen, setIsConfigOpen] =
|
|
333
|
+
const [isConfigOpen, setIsConfigOpen] = useAtom(slideConfigOpenAtom);
|
|
326
334
|
|
|
327
335
|
return (
|
|
328
336
|
<aside
|
|
@@ -350,7 +358,7 @@ export const SlideSidebar = ({
|
|
|
350
358
|
variant="ghost"
|
|
351
359
|
size="icon"
|
|
352
360
|
className="h-7 w-7 text-muted-foreground hover:text-foreground"
|
|
353
|
-
onClick={() => setIsConfigOpen(!
|
|
361
|
+
onClick={() => setIsConfigOpen((open) => !open)}
|
|
354
362
|
aria-expanded={isConfigOpen}
|
|
355
363
|
aria-controls="slide-config-panel"
|
|
356
364
|
>
|
|
@@ -6,6 +6,7 @@ import { useAtomValue } from "jotai";
|
|
|
6
6
|
import { CopyIcon, DownloadIcon } from "lucide-react";
|
|
7
7
|
import type React from "react";
|
|
8
8
|
import { Constants } from "@/core/constants";
|
|
9
|
+
import { useResolvedMarimoConfig } from "@/core/config/config";
|
|
9
10
|
import { codeAtom } from "@/core/saving/file-state";
|
|
10
11
|
import { useFilename } from "@/core/saving/filename";
|
|
11
12
|
import { isStaticNotebook } from "@/core/static/static-state";
|
|
@@ -66,6 +67,9 @@ const StaticBannerDialog = ({ code }: { code: string }) => {
|
|
|
66
67
|
filename = filename.slice(lastSlash + 1);
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
const [resolvedConfig] = useResolvedMarimoConfig();
|
|
71
|
+
const molabEnabled = resolvedConfig.sharing?.molab !== false;
|
|
72
|
+
|
|
69
73
|
const href = window.location.href;
|
|
70
74
|
const molabLink = createShareableLink({
|
|
71
75
|
code,
|
|
@@ -118,28 +122,30 @@ const StaticBannerDialog = ({ code }: { code: string }) => {
|
|
|
118
122
|
</div>
|
|
119
123
|
)}
|
|
120
124
|
|
|
121
|
-
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
125
|
+
{molabEnabled && (
|
|
126
|
+
<div className="pt-3 border-t flex gap-2 items-center">
|
|
127
|
+
<Button
|
|
128
|
+
asChild={true}
|
|
129
|
+
variant="outline"
|
|
130
|
+
size="xs"
|
|
131
|
+
className="shrink-0"
|
|
132
|
+
>
|
|
133
|
+
<a href={molabLink} target="_blank" rel="noopener noreferrer">
|
|
134
|
+
<MarimoPlusIcon
|
|
135
|
+
size={12}
|
|
136
|
+
strokeWidth={1.5}
|
|
137
|
+
className="mr-1.5 mt-px text-(--grass-11)"
|
|
138
|
+
/>
|
|
139
|
+
Open in molab
|
|
140
|
+
</a>
|
|
141
|
+
</Button>
|
|
142
|
+
<p className="text-sm text-(--sky-12)">
|
|
143
|
+
Run this notebook in{" "}
|
|
144
|
+
<span className="font-semibold">molab</span>, marimo's
|
|
145
|
+
cloud-hosted notebook platform.
|
|
146
|
+
</p>
|
|
147
|
+
</div>
|
|
148
|
+
)}
|
|
143
149
|
</DialogDescription>
|
|
144
150
|
</DialogHeader>
|
|
145
151
|
<div className="flex gap-3 pt-2">
|