@marimo-team/islands 0.23.17-dev10 → 0.23.17-dev13
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/{chat-ui-DsZJj75A.js → chat-ui-DH3hwWoI.js} +2 -2
- package/dist/{common-CL5RzjRX.js → common-D8DGPf0N.js} +2 -2
- package/dist/{html-to-image-CZ1kLKkq.js → html-to-image-CxlSazqu.js} +2095 -2088
- package/dist/main.js +5 -5
- package/dist/{process-output-bVfbcx5_.js → process-output-B24cDGzV.js} +1 -1
- package/dist/{reveal-component-o0NFIMKL.js → reveal-component-UfXDaVe-.js} +2 -2
- package/package.json +1 -1
- package/src/__mocks__/requests.ts +5 -0
- package/src/components/editor/actions/export-dialog/__tests__/export-dialog.test.tsx +35 -0
- package/src/components/editor/actions/export-dialog/__tests__/state.test.ts +61 -0
- package/src/components/editor/actions/export-dialog/format-notice.tsx +28 -0
- package/src/components/editor/actions/export-dialog/state.ts +23 -5
- package/src/components/editor/actions/export-dialog/use-export-dialog.ts +3 -13
- package/src/components/editor/chrome/panels/outline/__tests__/useActiveOutline.test.ts +26 -0
- package/src/core/__tests__/mode.test.ts +85 -0
- package/src/core/dom/outline.ts +25 -2
- package/src/core/mode.ts +11 -9
- package/src/core/network/__tests__/requests-lazy.test.ts +1 -0
package/dist/main.js
CHANGED
|
@@ -22,17 +22,17 @@ import { _ as Functions, c as useEventListener, f as composeRefs, g as Logger, h
|
|
|
22
22
|
import { t as require_react } from "./react-DA-nE2FX.js";
|
|
23
23
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
24
24
|
import { n as Copy, r as toast, t as copyToClipboard } from "./copy-DNq8q3zw.js";
|
|
25
|
-
import { $ as notebookOutline, $t as publishedCellClasses, A as ChevronDownIcon, An as Root2$1, At as DeferredRequestRegistry, Bn as Info, C as Accordion, Cn as atomWithReducer, Ct as MarkdownLanguageAdapter, D as BorderAllIcon, E as AccordionTrigger, Et as Paths, Fn as PaintRoller, G as hasRunAnyCellAtom, Gn as Columns2, H as renderHTML, Hn as Eye, Ht as DATA_TYPE_ICON, I as base64ToDataView, Jn as Braces, Jt as convertStatsName, K as cellIdsAtom, Lt as repl, Nn as Trash2, On as Content2, Pn as Table2, Pt as jotaiJsonStorage, Q as notebookAtom, Qt as isUninstantiated, R as dataViewToBase64, Rn as LoaderCircle, Rt as SQLParser, St as isRecord, T as AccordionItem, Tn as selectAtom, Tt as PathBuilder, U as getMarimoExportContext, V as safeExtractSetUIElementMessageBuffers, Vn as FileText, Vt as PluralWords, W as hasTrustedExportContext, Wn as Database, Wt as require_client, X as hasOnlyOneCellAtom, Xt as requestClientAtom, Y as getCellNames, Yt as getRequestClient, Z as initialNotebookState, Zt as useRequestClient, _n as UIElementId, _t as isInternalCellName, a as useCellFocusActions, at as createCell, bn as RANDOM_ID_ATTR, bt as customPythonLanguageSupport, cn as NotebookScopedLocalStorage, dn as parseAttrValue, dt as headingToIdentifier, en as shouldHidePublishedCell, et as numColumnsAtom, f as isOutputEmpty, fn as parseDataset, gt as getValidName, hn as HTMLCellId, i as LazyAnyLanguageCodeMirror, jn as Trigger2, jt as generateUUID, kn as Item$1, lt as outputIsLoading, n as Spinner, nt as useCellActions, o as useLastFocusedCellId, p as useExpandedConsoleOutput, pn as parseInitialValue, q as createActions, qn as CircleAlert, rt as useCellIds, s as maybeAddAltairImport, tt as reducer, ut as outputIsStale, vn as findCellId, vt as normalizeName, w as AccordionContent, wn as atomWithStorage, xn as jsonParseWithSpecialChar, xt as hasFunctionProperty, yn as OBJECT_ID_ATTR, yt as Checkbox, zn as Layers, zt as MarkdownParser, __tla as __tla_0 } from "./html-to-image-
|
|
25
|
+
import { $ as notebookOutline, $t as publishedCellClasses, A as ChevronDownIcon, An as Root2$1, At as DeferredRequestRegistry, Bn as Info, C as Accordion, Cn as atomWithReducer, Ct as MarkdownLanguageAdapter, D as BorderAllIcon, E as AccordionTrigger, Et as Paths, Fn as PaintRoller, G as hasRunAnyCellAtom, Gn as Columns2, H as renderHTML, Hn as Eye, Ht as DATA_TYPE_ICON, I as base64ToDataView, Jn as Braces, Jt as convertStatsName, K as cellIdsAtom, Lt as repl, Nn as Trash2, On as Content2, Pn as Table2, Pt as jotaiJsonStorage, Q as notebookAtom, Qt as isUninstantiated, R as dataViewToBase64, Rn as LoaderCircle, Rt as SQLParser, St as isRecord, T as AccordionItem, Tn as selectAtom, Tt as PathBuilder, U as getMarimoExportContext, V as safeExtractSetUIElementMessageBuffers, Vn as FileText, Vt as PluralWords, W as hasTrustedExportContext, Wn as Database, Wt as require_client, X as hasOnlyOneCellAtom, Xt as requestClientAtom, Y as getCellNames, Yt as getRequestClient, Z as initialNotebookState, Zt as useRequestClient, _n as UIElementId, _t as isInternalCellName, a as useCellFocusActions, at as createCell, bn as RANDOM_ID_ATTR, bt as customPythonLanguageSupport, cn as NotebookScopedLocalStorage, dn as parseAttrValue, dt as headingToIdentifier, en as shouldHidePublishedCell, et as numColumnsAtom, f as isOutputEmpty, fn as parseDataset, gt as getValidName, hn as HTMLCellId, i as LazyAnyLanguageCodeMirror, jn as Trigger2, jt as generateUUID, kn as Item$1, lt as outputIsLoading, n as Spinner, nt as useCellActions, o as useLastFocusedCellId, p as useExpandedConsoleOutput, pn as parseInitialValue, q as createActions, qn as CircleAlert, rt as useCellIds, s as maybeAddAltairImport, tt as reducer, ut as outputIsStale, vn as findCellId, vt as normalizeName, w as AccordionContent, wn as atomWithStorage, xn as jsonParseWithSpecialChar, xt as hasFunctionProperty, yn as OBJECT_ID_ATTR, yt as Checkbox, zn as Layers, zt as MarkdownParser, __tla as __tla_0 } from "./html-to-image-CxlSazqu.js";
|
|
26
26
|
import { __tla as __tla_1 } from "./chunk-5FQGJX7Z-B9WoXK1R.js";
|
|
27
27
|
import { o as useSize, s as Root$1, u as createLucideIcon } from "./dist-DIlxLFXG.js";
|
|
28
28
|
import { A as SquareFunction, C as DEFAULT_COLOR_SCHEME, D as SCALE_TYPE_DESCRIPTIONS, E as EMPTY_VALUE$1, O as TIME_UNIT_DESCRIPTIONS, S as DEFAULT_AGGREGATION, T as DEFAULT_TIME_UNIT, _ as AGGREGATION_TYPE_DESCRIPTIONS, a as AGGREGATION_FNS$1, b as COLOR_SCHEMES, c as COLOR_BY_FIELDS, d as NONE_VALUE, f as SELECTABLE_DATA_TYPES, g as TIME_UNITS, h as STRING_AGGREGATION_FNS, i as convertDataTypeToSelectable, j as ChartColumn, k as escapeFieldName, l as COMBINED_TIME_UNITS, m as SORT_TYPES, n as createSpecWithoutData, o as BIN_AGGREGATION, p as SINGLE_TIME_UNITS, r as isFieldSet, s as CHART_TYPES, t as augmentSpecWithData, u as ChartType, v as AGGREGATION_TYPE_ICON, w as DEFAULT_MAX_BINS_FACET, x as COUNT_FIELD, y as CHART_TYPE_ICON } from "./spec-CQKL_D3i.js";
|
|
29
|
-
import { $ as CommandSeparator, $t as Kbd, A as ErrorState, At as getPageIndexForRow, B as ContextMenuTrigger, Bt as dateToLocalISODate, C as downloadHTMLAsImage, Ct as inferFieldTypes, D as ColumnName, Dt as DelayMount, E as downloadSizeLimitAtom, Et as ColumnChartSpecModel, F as getUserColumnVisibilityCounts, Ft as TOO_MANY_ROWS, G as DateRangePicker, Gt as TabsList, H as useSelectList, Ht as dateToLocalISOTime, I as ContextMenu, It as toFieldTypes, J as Command, Jt as ChartInfoState, K as Combobox, Kt as TabsTrigger, L as ContextMenuContent, Lt as getMimeValues, M as prettifyRowColumnCount, Mt as loadTableData, N as prettifyRowCount, Nt as INDEX_COLUMN_NAME, O as ColumnPreviewContainer, Ot as useIntersectionObserver, P as getColumnCountForDisplay, Pt as SELECT_COLUMN_ID, Q as CommandList, Qt as RenderTextWithLinks, R as ContextMenuItem, Rt as isNullishFilter, S as downloadExportedFile, St as generateColumns, T as Filenames, Tt as ColumnChartContext, U as CompactChipRow, Ut as Tabs, V as useInternalStateWithSync, Vt as dateToLocalISODateTime, W as DatePicker, Wt as TabsContent, X as CommandInput, Xt as LazyVegaEmbed, Y as CommandEmpty, Yt as ChartLoadingState, Z as CommandItem, Zt as useOverflowDetection, _ as DataTable, _n as ChevronLeft, _t as TableCell, an as TextWrap, at as contextAwarePanelType, b as downloadBlob, bt as TableRow, c as SLIDE_TYPE_OPTIONS_BY_VALUE, cn as EyeOff, ct as slotsController, d as Switch, dn as Download, en as HtmlOutput, et as smartMatch, f as RadioGroup, fn as Code, g as OutputRenderer, gn as ChevronsDownUp, gt as TableBody, h as OutputArea, hn as ChevronsLeft, ht as Table, i as showCodeInRunModeAtom, in as $fae977aafc393c5c$export$6b862160d295c8e, it as contextAwarePanelOwner, j as LoadingState, jt as loadTableAndRawData, k as EmptyState, kt as usePrevious$1, lt as Toggle, m as JsonOutput, mn as ChevronsRight, mt as Provider$1, n as useNotebookCodeAvailable, nn as safeLocale, nt as PANEL_TYPES, on as GripHorizontal, ot as isCellAwareAtom, p as RadioGroupItem, pn as ChevronsUpDown, pt as Fill, q as ComboboxItem, qt as ChartErrorState, r as marimoVersionAtom, rn as $fae977aafc393c5c$export$588937bcd60ade55, rt as contextAwarePanelOpen, sn as Funnel, st as SlotNames, t as cellDomProps, tn as EmotionCacheProvider, tt as ContextAwarePanelItem, u as Slide, un as Ellipsis, v as InstallPackageButton, vn as ArrowDownWideNarrow, vt as TableHead, w as Progress, wt as renderCellValue, x as downloadByURL, xt as NAMELESS_COLUMN_PREFIX, y as ADD_PRINTING_CLASS, yt as TableHeader, z as ContextMenuSeparator, zt as Maps, __tla as __tla_2 } from "./common-
|
|
29
|
+
import { $ as CommandSeparator, $t as Kbd, A as ErrorState, At as getPageIndexForRow, B as ContextMenuTrigger, Bt as dateToLocalISODate, C as downloadHTMLAsImage, Ct as inferFieldTypes, D as ColumnName, Dt as DelayMount, E as downloadSizeLimitAtom, Et as ColumnChartSpecModel, F as getUserColumnVisibilityCounts, Ft as TOO_MANY_ROWS, G as DateRangePicker, Gt as TabsList, H as useSelectList, Ht as dateToLocalISOTime, I as ContextMenu, It as toFieldTypes, J as Command, Jt as ChartInfoState, K as Combobox, Kt as TabsTrigger, L as ContextMenuContent, Lt as getMimeValues, M as prettifyRowColumnCount, Mt as loadTableData, N as prettifyRowCount, Nt as INDEX_COLUMN_NAME, O as ColumnPreviewContainer, Ot as useIntersectionObserver, P as getColumnCountForDisplay, Pt as SELECT_COLUMN_ID, Q as CommandList, Qt as RenderTextWithLinks, R as ContextMenuItem, Rt as isNullishFilter, S as downloadExportedFile, St as generateColumns, T as Filenames, Tt as ColumnChartContext, U as CompactChipRow, Ut as Tabs, V as useInternalStateWithSync, Vt as dateToLocalISODateTime, W as DatePicker, Wt as TabsContent, X as CommandInput, Xt as LazyVegaEmbed, Y as CommandEmpty, Yt as ChartLoadingState, Z as CommandItem, Zt as useOverflowDetection, _ as DataTable, _n as ChevronLeft, _t as TableCell, an as TextWrap, at as contextAwarePanelType, b as downloadBlob, bt as TableRow, c as SLIDE_TYPE_OPTIONS_BY_VALUE, cn as EyeOff, ct as slotsController, d as Switch, dn as Download, en as HtmlOutput, et as smartMatch, f as RadioGroup, fn as Code, g as OutputRenderer, gn as ChevronsDownUp, gt as TableBody, h as OutputArea, hn as ChevronsLeft, ht as Table, i as showCodeInRunModeAtom, in as $fae977aafc393c5c$export$6b862160d295c8e, it as contextAwarePanelOwner, j as LoadingState, jt as loadTableAndRawData, k as EmptyState, kt as usePrevious$1, lt as Toggle, m as JsonOutput, mn as ChevronsRight, mt as Provider$1, n as useNotebookCodeAvailable, nn as safeLocale, nt as PANEL_TYPES, on as GripHorizontal, ot as isCellAwareAtom, p as RadioGroupItem, pn as ChevronsUpDown, pt as Fill, q as ComboboxItem, qt as ChartErrorState, r as marimoVersionAtom, rn as $fae977aafc393c5c$export$588937bcd60ade55, rt as contextAwarePanelOpen, sn as Funnel, st as SlotNames, t as cellDomProps, tn as EmotionCacheProvider, tt as ContextAwarePanelItem, u as Slide, un as Ellipsis, v as InstallPackageButton, vn as ArrowDownWideNarrow, vt as TableHead, w as Progress, wt as renderCellValue, x as downloadByURL, xt as NAMELESS_COLUMN_PREFIX, y as ADD_PRINTING_CLASS, yt as TableHeader, z as ContextMenuSeparator, zt as Maps, __tla as __tla_2 } from "./common-D8DGPf0N.js";
|
|
30
30
|
import { c as Calendar, i as createReducerAndAtoms, n as useOnUnmount, o as ToggleLeft, t as useOnMount } from "./useLifecycle-CMJGqBaY.js";
|
|
31
31
|
import { t as Check } from "./check-DP7U0H0G.js";
|
|
32
32
|
import { A as Icon, C as logNever, D as $18f2051aff69b9bf$export$a54013f0d02a8f82, E as $18f2051aff69b9bf$export$43bb16f9c6d9e3f7, F as createCollection, I as X, M as clamp$2, N as usePrevious$2, P as useDirection, R as ChevronDown, S as assertNever, a as SelectGroup, c as SelectSeparator, d as NativeSelect, i as SelectContent, j as Trigger$1, l as SelectTrigger, n as capitalize, o as SelectItem, r as Select, s as SelectLabel, t as Strings, u as SelectValue, w as $a916eb452884faea$export$b7a616150fdb9f44 } from "./strings-DIy9NAmg.js";
|
|
33
33
|
import { B as $64fa3d84918910a7$export$4d86445c2cf5e3, C as DropdownMenuTrigger, Ft as $65484d02dcb7eb3e$export$457c3d6518dd4c6f, It as $3ef42575df84b30b$export$9d1611c77c2fe928, W as $64fa3d84918910a7$export$df3a06d6289f983e, Wt as $ff5963eb1fccf552$export$e08e3b67e392101e, X as useDebouncedCallback, Y as useDebounceControlledState, a as NumberField, d as roundToFractionDigits, f as DropdownMenu, g as DropdownMenuLabel, gn as ChevronRight, h as DropdownMenuItem, hn as Circle, i as OnBlurredInput, l as prettyNumber, m as DropdownMenuGroup, n as DebouncedNumberInput, o as maxFractionDigitsForSteps, ot as $f7dceffc5ad7768b$export$4e328f61c538687f, p as DropdownMenuContent, pt as $6179b936705e76d3$export$ae780daf29e6d456, r as Input, st as $701a24aa0da5b062$export$ea18c227d4417cc3, t as DebouncedInput, u as prettyScientificNumber, v as DropdownMenuSeparator, xt as $458b0a5536c1a7cf$export$40bfa8c7b0832715, y as DropdownMenuShortcut, z as $64fa3d84918910a7$export$29f1550f4b0d4415 } from "./input-C5K4VBvB.js";
|
|
34
34
|
import { S as isWasm, _ as initialModeAtom, b as viewStateAtom, c as asRemoteURL, d as isStaticNotebook, f as appendQueryParams, g as getInitialAppMode, m as require_cuid2, u as getStaticVirtualFiles, v as kioskModeAtom, w as CircleQuestionMark, x as Deferred } from "./toDate-CqhBWjvO.js";
|
|
35
|
-
import { a as MarimoIncomingMessageEvent, c as MarimoValueUpdateEvent, f as Square, i as PythonIcon, l as createInputEvent, n as blobToString, o as MarimoValueInputEvent, p as File, r as filesToBase64, s as MarimoValueReadyEvent, t as processOutput, u as deserializeBlob } from "./process-output-
|
|
35
|
+
import { a as MarimoIncomingMessageEvent, c as MarimoValueUpdateEvent, f as Square, i as PythonIcon, l as createInputEvent, n as blobToString, o as MarimoValueInputEvent, p as File, r as filesToBase64, s as MarimoValueReadyEvent, t as processOutput, u as deserializeBlob } from "./process-output-B24cDGzV.js";
|
|
36
36
|
import { n as Trash, r as Pencil, t as BulkEdit } from "./types-DicH9Oi0.js";
|
|
37
37
|
import { n as require_prop_types, r as Plus, t as ErrorBoundary } from "./ErrorBoundary-DhLqwSWA.js";
|
|
38
38
|
import { t as require_react_dom } from "./react-dom-BTJzcVJ9.js";
|
|
@@ -9017,7 +9017,7 @@ let __tla = Promise.all([
|
|
|
9017
9017
|
};
|
|
9018
9018
|
}
|
|
9019
9019
|
};
|
|
9020
|
-
var LazyChatbot = import_react.lazy(() => import("./chat-ui-
|
|
9020
|
+
var LazyChatbot = import_react.lazy(() => import("./chat-ui-DH3hwWoI.js").then((e) => ({
|
|
9021
9021
|
default: e.Chatbot
|
|
9022
9022
|
}))), messageSchema = array(object({
|
|
9023
9023
|
id: string(),
|
|
@@ -38218,7 +38218,7 @@ ${c}
|
|
|
38218
38218
|
function _temp2$2(e) {
|
|
38219
38219
|
e.target === e.currentTarget && e.key === "Enter" && (e.preventDefault(), e.stopPropagation(), e.currentTarget.click());
|
|
38220
38220
|
}
|
|
38221
|
-
var LazySlidesComponent = import_react.lazy(() => import("./reveal-component-
|
|
38221
|
+
var LazySlidesComponent = import_react.lazy(() => import("./reveal-component-UfXDaVe-.js"));
|
|
38222
38222
|
const SlidesLayoutRenderer = ({ layout: e, setLayout: r, cells: c, mode: l }) => {
|
|
38223
38223
|
var _a5;
|
|
38224
38224
|
let u = useAtomValue(kioskModeAtom), d = l === "read" || u, f = useAtomValue(numColumnsAtom) > 1, [p, m] = (0, import_react.useState)(null), { slideCells: h, skippedIds: g, noOutputIds: _, slideTypes: v, startCellIndex: y } = (0, import_react.useMemo)(() => computeSlideCellsInfo(c, e), [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { s as __toESM } from "./chunk-BNovOVIE.js";
|
|
2
2
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
3
|
-
import { F as base64ToDataURL, ot as ansiToPlainText, st as parseHtmlContent } from "./html-to-image-
|
|
3
|
+
import { F as base64ToDataURL, ot as ansiToPlainText, st as parseHtmlContent } from "./html-to-image-CxlSazqu.js";
|
|
4
4
|
import { u as createLucideIcon } from "./dist-DIlxLFXG.js";
|
|
5
5
|
import { t as Strings } from "./strings-DIy9NAmg.js";
|
|
6
6
|
import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
|
|
@@ -6,10 +6,10 @@ import { s as __toESM } from "./chunk-BNovOVIE.js";
|
|
|
6
6
|
import { c as useEventListener, g as Logger, h as cn, m as Events, t as Button } from "./button-BSm8IXRU.js";
|
|
7
7
|
import { t as require_react } from "./react-DA-nE2FX.js";
|
|
8
8
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
9
|
-
import { ut as outputIsStale } from "./html-to-image-
|
|
9
|
+
import { ut as outputIsStale } from "./html-to-image-CxlSazqu.js";
|
|
10
10
|
import "./chunk-5FQGJX7Z-B9WoXK1R.js";
|
|
11
11
|
import { u as createLucideIcon } from "./dist-DIlxLFXG.js";
|
|
12
|
-
import { a as DEFAULT_DECK_TRANSITION, cn as EyeOff, dt as PanelGroup, fn as Code, ft as PanelResizeHandle, l as SlideSidebar, ln as Expand, n as useNotebookCodeAvailable, o as DEFAULT_DECK_VERTICAL_ALIGN, s as DEFAULT_SLIDE_TYPE, t as cellDomProps, u as Slide, ut as Panel } from "./common-
|
|
12
|
+
import { a as DEFAULT_DECK_TRANSITION, cn as EyeOff, dt as PanelGroup, fn as Code, ft as PanelResizeHandle, l as SlideSidebar, ln as Expand, n as useNotebookCodeAvailable, o as DEFAULT_DECK_VERTICAL_ALIGN, s as DEFAULT_SLIDE_TYPE, t as cellDomProps, u as Slide, ut as Panel } from "./common-D8DGPf0N.js";
|
|
13
13
|
import { R as ChevronDown } from "./strings-DIy9NAmg.js";
|
|
14
14
|
import { X as useDebouncedCallback } from "./input-C5K4VBvB.js";
|
|
15
15
|
import { v as kioskModeAtom } from "./toDate-CqhBWjvO.js";
|
package/package.json
CHANGED
|
@@ -72,26 +72,31 @@ export const MockRequestClient = {
|
|
|
72
72
|
format: "html",
|
|
73
73
|
dependenciesAvailable: true,
|
|
74
74
|
missingPackages: [],
|
|
75
|
+
missingSetup: [],
|
|
75
76
|
},
|
|
76
77
|
{
|
|
77
78
|
format: "markdown",
|
|
78
79
|
dependenciesAvailable: true,
|
|
79
80
|
missingPackages: [],
|
|
81
|
+
missingSetup: [],
|
|
80
82
|
},
|
|
81
83
|
{
|
|
82
84
|
format: "ipynb",
|
|
83
85
|
dependenciesAvailable: true,
|
|
84
86
|
missingPackages: [],
|
|
87
|
+
missingSetup: [],
|
|
85
88
|
},
|
|
86
89
|
{
|
|
87
90
|
format: "pdf",
|
|
88
91
|
dependenciesAvailable: true,
|
|
89
92
|
missingPackages: [],
|
|
93
|
+
missingSetup: [],
|
|
90
94
|
},
|
|
91
95
|
{
|
|
92
96
|
format: "script",
|
|
93
97
|
dependenciesAvailable: true,
|
|
94
98
|
missingPackages: [],
|
|
99
|
+
missingSetup: [],
|
|
95
100
|
},
|
|
96
101
|
],
|
|
97
102
|
}),
|
|
@@ -259,6 +259,7 @@ describe("ExportDialog", () => {
|
|
|
259
259
|
format: "pdf",
|
|
260
260
|
dependenciesAvailable: false,
|
|
261
261
|
missingPackages: ["nbconvert[webpdf]"],
|
|
262
|
+
missingSetup: [],
|
|
262
263
|
},
|
|
263
264
|
],
|
|
264
265
|
}),
|
|
@@ -275,6 +276,40 @@ describe("ExportDialog", () => {
|
|
|
275
276
|
expect(screen.getByTestId("export-format-pdf")).toBeVisible();
|
|
276
277
|
});
|
|
277
278
|
|
|
279
|
+
it("explains missing Playwright Chromium setup", async () => {
|
|
280
|
+
store.set(
|
|
281
|
+
requestClientAtom,
|
|
282
|
+
MockRequestClient.create({
|
|
283
|
+
getExportAvailability: vi.fn().mockResolvedValue({
|
|
284
|
+
source: "server",
|
|
285
|
+
formats: [
|
|
286
|
+
{
|
|
287
|
+
format: "pdf",
|
|
288
|
+
dependenciesAvailable: false,
|
|
289
|
+
missingPackages: [],
|
|
290
|
+
missingSetup: [
|
|
291
|
+
{
|
|
292
|
+
name: "playwright-chromium",
|
|
293
|
+
command: "uv run playwright install chromium",
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
}),
|
|
299
|
+
}),
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
renderDialog("pdf");
|
|
303
|
+
|
|
304
|
+
expect(
|
|
305
|
+
await screen.findByText("PDF export requires Playwright Chromium."),
|
|
306
|
+
).toBeVisible();
|
|
307
|
+
expect(
|
|
308
|
+
screen.getByText("uv run playwright install chromium"),
|
|
309
|
+
).toBeVisible();
|
|
310
|
+
expect(screen.getByTestId("export-submit")).toBeDisabled();
|
|
311
|
+
});
|
|
312
|
+
|
|
278
313
|
it("announces requirement checks as status updates", () => {
|
|
279
314
|
store.set(
|
|
280
315
|
requestClientAtom,
|
|
@@ -19,11 +19,13 @@ const AVAILABLE: ExportAvailabilityResponse = {
|
|
|
19
19
|
format: "ipynb",
|
|
20
20
|
dependenciesAvailable: true,
|
|
21
21
|
missingPackages: [],
|
|
22
|
+
missingSetup: [],
|
|
22
23
|
},
|
|
23
24
|
{
|
|
24
25
|
format: "pdf",
|
|
25
26
|
dependenciesAvailable: true,
|
|
26
27
|
missingPackages: [],
|
|
28
|
+
missingSetup: [],
|
|
27
29
|
},
|
|
28
30
|
],
|
|
29
31
|
};
|
|
@@ -188,6 +190,7 @@ describe("getExportFormatStatus", () => {
|
|
|
188
190
|
format: "pdf",
|
|
189
191
|
dependenciesAvailable: false,
|
|
190
192
|
missingPackages: ["nbconvert[webpdf]"],
|
|
193
|
+
missingSetup: [],
|
|
191
194
|
},
|
|
192
195
|
],
|
|
193
196
|
},
|
|
@@ -202,6 +205,64 @@ describe("getExportFormatStatus", () => {
|
|
|
202
205
|
});
|
|
203
206
|
});
|
|
204
207
|
|
|
208
|
+
it("reports missing server setup", () => {
|
|
209
|
+
expect(
|
|
210
|
+
status("pdf", {
|
|
211
|
+
availability: {
|
|
212
|
+
status: "success",
|
|
213
|
+
data: {
|
|
214
|
+
source: "server",
|
|
215
|
+
formats: [
|
|
216
|
+
{
|
|
217
|
+
format: "pdf",
|
|
218
|
+
dependenciesAvailable: false,
|
|
219
|
+
missingPackages: [],
|
|
220
|
+
missingSetup: [
|
|
221
|
+
{
|
|
222
|
+
name: "playwright-chromium",
|
|
223
|
+
command: "uv run playwright install chromium",
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
}),
|
|
231
|
+
).toEqual({
|
|
232
|
+
available: false,
|
|
233
|
+
reason: {
|
|
234
|
+
type: "missing-setup",
|
|
235
|
+
requirements: [
|
|
236
|
+
{
|
|
237
|
+
name: "playwright-chromium",
|
|
238
|
+
command: "uv run playwright install chromium",
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("fails open when an unavailable format has no requirements", () => {
|
|
246
|
+
expect(
|
|
247
|
+
status("pdf", {
|
|
248
|
+
availability: {
|
|
249
|
+
status: "success",
|
|
250
|
+
data: {
|
|
251
|
+
source: "server",
|
|
252
|
+
formats: [
|
|
253
|
+
{
|
|
254
|
+
format: "pdf",
|
|
255
|
+
dependenciesAvailable: false,
|
|
256
|
+
missingPackages: [],
|
|
257
|
+
missingSetup: [],
|
|
258
|
+
},
|
|
259
|
+
],
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
}),
|
|
263
|
+
).toEqual({ available: true, availabilityCheckFailed: true });
|
|
264
|
+
});
|
|
265
|
+
|
|
205
266
|
it.each([
|
|
206
267
|
["html", true, undefined],
|
|
207
268
|
["markdown", true, undefined],
|
|
@@ -10,8 +10,18 @@ import type {
|
|
|
10
10
|
ExportBlockReason,
|
|
11
11
|
ExportFormat,
|
|
12
12
|
ExportFormatStatus,
|
|
13
|
+
ExportSetupRequirement,
|
|
13
14
|
} from "./state";
|
|
14
15
|
|
|
16
|
+
const SETUP_REQUIREMENTS: Record<
|
|
17
|
+
ExportSetupRequirement["name"],
|
|
18
|
+
{ description: string }
|
|
19
|
+
> = {
|
|
20
|
+
"playwright-chromium": {
|
|
21
|
+
description: "PDF export requires Playwright Chromium.",
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
|
|
15
25
|
export function FormatStatusIcon({ status }: { status: ExportFormatStatus }) {
|
|
16
26
|
if (status.available && !status.availabilityCheckFailed) {
|
|
17
27
|
return null;
|
|
@@ -124,6 +134,24 @@ function ReasonNotice({
|
|
|
124
134
|
where marimo is running to use this export.
|
|
125
135
|
</Notice>
|
|
126
136
|
);
|
|
137
|
+
case "missing-setup":
|
|
138
|
+
return (
|
|
139
|
+
<Notice>
|
|
140
|
+
<span className="block min-w-0">
|
|
141
|
+
{reason.requirements.map((requirement) => {
|
|
142
|
+
const details = SETUP_REQUIREMENTS[requirement.name];
|
|
143
|
+
return (
|
|
144
|
+
<span className="block" key={requirement.name}>
|
|
145
|
+
{details.description}
|
|
146
|
+
<code className="mt-1 block break-all font-mono">
|
|
147
|
+
{requirement.command}
|
|
148
|
+
</code>
|
|
149
|
+
</span>
|
|
150
|
+
);
|
|
151
|
+
})}
|
|
152
|
+
</span>
|
|
153
|
+
</Notice>
|
|
154
|
+
);
|
|
127
155
|
case "wasm-runtime":
|
|
128
156
|
return (
|
|
129
157
|
<Notice>
|
|
@@ -15,6 +15,8 @@ export const EXPORT_FORMATS = [
|
|
|
15
15
|
] as const;
|
|
16
16
|
|
|
17
17
|
export type ExportFormat = (typeof EXPORT_FORMATS)[number];
|
|
18
|
+
export type ExportSetupRequirement =
|
|
19
|
+
ExportAvailabilityResponse["formats"][number]["missingSetup"][number];
|
|
18
20
|
|
|
19
21
|
export const MARKDOWN_FLAVORS = ["pymdown", "qmd", "mystmd", "mdx"] as const;
|
|
20
22
|
export const IPYNB_SORT_MODES = ["topological", "top-down"] as const;
|
|
@@ -185,6 +187,7 @@ export type ExportBlockReason =
|
|
|
185
187
|
| { type: "checking-requirements" }
|
|
186
188
|
| { type: "notebook-must-be-named" }
|
|
187
189
|
| { type: "missing-packages"; packages: string[] }
|
|
190
|
+
| { type: "missing-setup"; requirements: ExportSetupRequirement[] }
|
|
188
191
|
| { type: "wasm-runtime" };
|
|
189
192
|
|
|
190
193
|
export interface ExportFormatStatus {
|
|
@@ -308,12 +311,27 @@ export function getExportFormatStatus({
|
|
|
308
311
|
return { available: true, availabilityCheckFailed: true };
|
|
309
312
|
}
|
|
310
313
|
if (!serverStatus.dependenciesAvailable) {
|
|
314
|
+
if (serverStatus.missingPackages.length > 0) {
|
|
315
|
+
return {
|
|
316
|
+
available: false,
|
|
317
|
+
reason: {
|
|
318
|
+
type: "missing-packages",
|
|
319
|
+
packages: serverStatus.missingPackages,
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
if (serverStatus.missingSetup.length > 0) {
|
|
324
|
+
return {
|
|
325
|
+
available: false,
|
|
326
|
+
reason: {
|
|
327
|
+
type: "missing-setup",
|
|
328
|
+
requirements: serverStatus.missingSetup,
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
}
|
|
311
332
|
return {
|
|
312
|
-
available:
|
|
313
|
-
|
|
314
|
-
type: "missing-packages",
|
|
315
|
-
packages: serverStatus.missingPackages,
|
|
316
|
-
},
|
|
333
|
+
available: true,
|
|
334
|
+
availabilityCheckFailed: true,
|
|
317
335
|
};
|
|
318
336
|
}
|
|
319
337
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import { useAtom
|
|
3
|
+
import { useAtom } from "jotai";
|
|
4
4
|
import { useEffect, useRef, useState } from "react";
|
|
5
5
|
import { toast } from "@/components/ui/use-toast";
|
|
6
6
|
import {
|
|
7
7
|
updateCellOutputsWithScreenshots,
|
|
8
8
|
useEnrichCellOutputs,
|
|
9
9
|
} from "@/core/export/hooks";
|
|
10
|
-
import { runDuringPresentMode
|
|
10
|
+
import { runDuringPresentMode } from "@/core/mode";
|
|
11
11
|
import { useRequestClient } from "@/core/network/requests";
|
|
12
12
|
import type { ExportAvailabilityResponse } from "@/core/network/types";
|
|
13
13
|
import { useFilename } from "@/core/saving/filename";
|
|
@@ -278,8 +278,6 @@ function useExportDialogAction({
|
|
|
278
278
|
}) {
|
|
279
279
|
const requests = useRequestClient();
|
|
280
280
|
const takeScreenshots = useEnrichCellOutputs();
|
|
281
|
-
const viewState = useAtomValue(viewStateAtom);
|
|
282
|
-
const setViewState = useSetAtom(viewStateAtom);
|
|
283
281
|
const [isExporting, setIsExporting] = useState(false);
|
|
284
282
|
const dialogRef = useRef<HTMLDivElement>(null);
|
|
285
283
|
const mountedRef = useRef(true);
|
|
@@ -303,15 +301,7 @@ function useExportDialogAction({
|
|
|
303
301
|
const capturePNG = async () => {
|
|
304
302
|
const capture = () =>
|
|
305
303
|
captureCurrentAppView(dialogRef.current?.parentElement ?? null);
|
|
306
|
-
|
|
307
|
-
await capture();
|
|
308
|
-
return;
|
|
309
|
-
}
|
|
310
|
-
try {
|
|
311
|
-
await runDuringPresentMode(capture);
|
|
312
|
-
} finally {
|
|
313
|
-
setViewState(viewState);
|
|
314
|
-
}
|
|
304
|
+
await runDuringPresentMode(capture);
|
|
315
305
|
};
|
|
316
306
|
|
|
317
307
|
const submit = async () => {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { parseOutline } from "@/core/dom/outline";
|
|
4
|
+
import { findOutlineElements } from "../useActiveOutline";
|
|
5
|
+
|
|
6
|
+
describe("findOutlineElements", () => {
|
|
7
|
+
it.each([
|
|
8
|
+
"Design from the portfolio",
|
|
9
|
+
'Decorative Art of "Spanish California"',
|
|
10
|
+
`The painter's "Study"`,
|
|
11
|
+
])("finds an id-less heading named %s", (name) => {
|
|
12
|
+
const html = `<h3>${name}</h3>`;
|
|
13
|
+
document.body.innerHTML = html;
|
|
14
|
+
|
|
15
|
+
const outline = parseOutline({
|
|
16
|
+
mimetype: "text/html",
|
|
17
|
+
timestamp: 0,
|
|
18
|
+
channel: "output",
|
|
19
|
+
data: html,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
expect(
|
|
23
|
+
findOutlineElements(outline?.items ?? []).map(([element]) => element),
|
|
24
|
+
).toEqual([document.querySelector("h3")]);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { CellId } from "@/core/cells/ids";
|
|
5
|
+
import { runDuringPresentMode, viewStateAtom } from "@/core/mode";
|
|
6
|
+
import { store } from "@/core/state/jotai";
|
|
7
|
+
|
|
8
|
+
const requestAnimationFrameMock = vi.fn((callback: FrameRequestCallback) => {
|
|
9
|
+
callback(0);
|
|
10
|
+
return 0;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
async function runAfterRender(fn: () => void | Promise<void>): Promise<void> {
|
|
14
|
+
const result = runDuringPresentMode(fn);
|
|
15
|
+
await vi.runAllTimersAsync();
|
|
16
|
+
return result;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("runDuringPresentMode", () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
vi.useFakeTimers();
|
|
22
|
+
vi.stubGlobal("requestAnimationFrame", requestAnimationFrameMock);
|
|
23
|
+
requestAnimationFrameMock.mockClear();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
vi.useRealTimers();
|
|
28
|
+
vi.unstubAllGlobals();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("runs in present mode and restores the captured view state", async () => {
|
|
32
|
+
const state = { mode: "edit" as const, cellAnchor: CellId.create() };
|
|
33
|
+
store.set(viewStateAtom, state);
|
|
34
|
+
|
|
35
|
+
await runAfterRender(() => {
|
|
36
|
+
expect(store.get(viewStateAtom)).toEqual({
|
|
37
|
+
...state,
|
|
38
|
+
mode: "present",
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(store.get(viewStateAtom)).toEqual(state);
|
|
43
|
+
expect(requestAnimationFrameMock).toHaveBeenCalledTimes(2);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("restores the captured view state when the callback rejects", async () => {
|
|
47
|
+
const state = { mode: "edit" as const, cellAnchor: CellId.create() };
|
|
48
|
+
const error = new Error("capture failed");
|
|
49
|
+
store.set(viewStateAtom, state);
|
|
50
|
+
|
|
51
|
+
const result = runDuringPresentMode(() => Promise.reject(error));
|
|
52
|
+
const rejection = expect(result).rejects.toBe(error);
|
|
53
|
+
await vi.runAllTimersAsync();
|
|
54
|
+
await rejection;
|
|
55
|
+
|
|
56
|
+
expect(store.get(viewStateAtom)).toEqual(state);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("runs directly when already in present mode", async () => {
|
|
60
|
+
const state = { mode: "present" as const, cellAnchor: CellId.create() };
|
|
61
|
+
store.set(viewStateAtom, state);
|
|
62
|
+
|
|
63
|
+
await runDuringPresentMode(() => {
|
|
64
|
+
expect(store.get(viewStateAtom)).toEqual(state);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(store.get(viewStateAtom)).toEqual(state);
|
|
68
|
+
expect(requestAnimationFrameMock).not.toHaveBeenCalled();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it.each(["read", "home", "gallery"] as const)(
|
|
72
|
+
"runs directly without changing %s mode",
|
|
73
|
+
async (mode) => {
|
|
74
|
+
const state = { mode, cellAnchor: CellId.create() };
|
|
75
|
+
store.set(viewStateAtom, state);
|
|
76
|
+
|
|
77
|
+
await runDuringPresentMode(() => {
|
|
78
|
+
expect(store.get(viewStateAtom)).toEqual(state);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
expect(store.get(viewStateAtom)).toEqual(state);
|
|
82
|
+
expect(requestAnimationFrameMock).not.toHaveBeenCalled();
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
});
|
package/src/core/dom/outline.ts
CHANGED
|
@@ -63,13 +63,36 @@ function getOutline(html: string): Outline | null {
|
|
|
63
63
|
return { items };
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
function toXPathStringLiteral(value: string): string {
|
|
67
|
+
if (!value.includes('"')) {
|
|
68
|
+
return `"${value}"`;
|
|
69
|
+
}
|
|
70
|
+
if (!value.includes("'")) {
|
|
71
|
+
return `'${value}'`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const segments = value.split('"');
|
|
75
|
+
const literals: string[] = [];
|
|
76
|
+
for (const [index, segment] of segments.entries()) {
|
|
77
|
+
if (segment) {
|
|
78
|
+
literals.push(`"${segment}"`);
|
|
79
|
+
}
|
|
80
|
+
if (index < segments.length - 1) {
|
|
81
|
+
literals.push(`'"'`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return `concat(${literals.join(", ")})`;
|
|
85
|
+
}
|
|
86
|
+
|
|
66
87
|
export function headingToIdentifier(heading: Element): OutlineItem["by"] {
|
|
67
88
|
const id = heading.id;
|
|
68
89
|
if (id) {
|
|
69
90
|
return { id };
|
|
70
91
|
}
|
|
71
|
-
const name = heading.textContent;
|
|
72
|
-
return {
|
|
92
|
+
const name = heading.textContent ?? "";
|
|
93
|
+
return {
|
|
94
|
+
path: `//${heading.tagName}[contains(., ${toXPathStringLiteral(name)})]`,
|
|
95
|
+
};
|
|
73
96
|
}
|
|
74
97
|
|
|
75
98
|
export function mergeOutlines(outlines: (Outline | null)[]): Outline {
|
package/src/core/mode.ts
CHANGED
|
@@ -55,20 +55,22 @@ export async function runDuringPresentMode(
|
|
|
55
55
|
fn: () => void | Promise<void>,
|
|
56
56
|
): Promise<void> {
|
|
57
57
|
const state = store.get(viewStateAtom);
|
|
58
|
-
if (state.mode
|
|
58
|
+
if (state.mode !== "edit") {
|
|
59
59
|
await fn();
|
|
60
60
|
return;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
store.set(viewStateAtom, { ...state, mode: "present" });
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
try {
|
|
65
|
+
// Wait 100ms to allow the page to render
|
|
66
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
67
|
+
// Wait 2 frames
|
|
68
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
69
|
+
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
70
|
+
await fn();
|
|
71
|
+
} finally {
|
|
72
|
+
store.set(viewStateAtom, state);
|
|
73
|
+
}
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
export const viewStateAtom = atom<ViewState>({
|