@marimo-team/islands 0.23.6-dev15 → 0.23.6-dev17
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-Cyca6aKX.js → chat-ui-CtIeZD6j.js} +2 -2
- package/dist/{code-visibility-Ch6utETw.js → code-visibility-BQo0Byk4.js} +2 -2
- package/dist/{html-to-image-6VI69paz.js → html-to-image-uzALXlch.js} +2114 -2097
- package/dist/main.js +5 -5
- package/dist/{process-output-SkNR_Omd.js → process-output-CD5QiwBb.js} +1 -1
- package/dist/{reveal-component-CIvwF-9l.js → reveal-component-CULfHQbp.js} +2 -2
- package/package.json +1 -1
- package/src/plugins/core/RenderHTML.tsx +49 -3
- package/src/plugins/core/__test__/RenderHTML.test.ts +54 -0
package/dist/main.js
CHANGED
|
@@ -22,18 +22,18 @@ import { _ as Logger, c as Objects, g as cn, h as Events, i as NOT_SET, l as use
|
|
|
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-TGGAUEWp.js";
|
|
25
|
-
import { $ as useCellActions, At as DeferredRequestRegistry, B as safeExtractSetUIElementMessageBuffers, Bn as Braces, Bt as getDataTypeColor, C as AccordionContent, Cn as Root2$1, Ct as customPythonLanguageSupport, Dn as Table2, Dt as Paths, E as BorderAllIcon, En as Trash2, Et as PathBuilder, F as base64ToDataView, Fn as Eye, Ft as jotaiJsonStorage, Gt as convertStatsName, H as getMarimoExportContext, In as Database, J as getCellNames, Jt as useRequestClient, K as createActions, Kt as getRequestClient, L as dataViewToBase64, Ln as Columns2, Mn as Layers, Nn as Info, Nt as repl, On as PaintRoller, Pn as FileText, Q as reducer, Rt as PluralWords, S as Accordion, Sn as Item$1, St as Checkbox, T as AccordionTrigger, U as hasTrustedExportContext, V as renderHTML, Vt as require_client, W as hasRunAnyCellAtom, X as notebookOutline, Y as notebookAtom, Yt as isUninstantiated, Z as numColumnsAtom, _n as atomWithStorage, a as useLastFocusedCellId, an as parseInitialValue, bt as isInternalCellName, ct as kioskModeAtom, dn as OBJECT_ID_ATTR, dt as outputIsLoading, en as NotebookScopedLocalStorage, et as useCellIds, f as isOutputEmpty, fn as RANDOM_ID_ATTR, ft as outputIsStale, gn as atomWithReducer, i as useCellFocusActions, in as parseDataset, jn as LoaderCircle, jt as generateUUID, k as ChevronDownIcon, ln as UIElementId, mt as headingToIdentifier, nt as createCell, o as maybeAddAltairImport, ot as getInitialAppMode, p as useExpandedConsoleOutput, pn as jsonParseWithSpecialChar, pt as isErrorMime, qt as requestClientAtom, r as LazyAnyLanguageCodeMirror, rn as parseAttrValue, s as Spinner, sn as HTMLCellId, st as initialModeAtom, un as findCellId, vn as selectAtom, w as AccordionItem, wn as Trigger2, wt as MarkdownLanguageAdapter, xn as Content2, xt as normalizeName, yt as getValidName, zn as CircleAlert, zt as DATA_TYPE_ICON, __tla as __tla_0 } from "./html-to-image-
|
|
25
|
+
import { $ as useCellActions, At as DeferredRequestRegistry, B as safeExtractSetUIElementMessageBuffers, Bn as Braces, Bt as getDataTypeColor, C as AccordionContent, Cn as Root2$1, Ct as customPythonLanguageSupport, Dn as Table2, Dt as Paths, E as BorderAllIcon, En as Trash2, Et as PathBuilder, F as base64ToDataView, Fn as Eye, Ft as jotaiJsonStorage, Gt as convertStatsName, H as getMarimoExportContext, In as Database, J as getCellNames, Jt as useRequestClient, K as createActions, Kt as getRequestClient, L as dataViewToBase64, Ln as Columns2, Mn as Layers, Nn as Info, Nt as repl, On as PaintRoller, Pn as FileText, Q as reducer, Rt as PluralWords, S as Accordion, Sn as Item$1, St as Checkbox, T as AccordionTrigger, U as hasTrustedExportContext, V as renderHTML, Vt as require_client, W as hasRunAnyCellAtom, X as notebookOutline, Y as notebookAtom, Yt as isUninstantiated, Z as numColumnsAtom, _n as atomWithStorage, a as useLastFocusedCellId, an as parseInitialValue, bt as isInternalCellName, ct as kioskModeAtom, dn as OBJECT_ID_ATTR, dt as outputIsLoading, en as NotebookScopedLocalStorage, et as useCellIds, f as isOutputEmpty, fn as RANDOM_ID_ATTR, ft as outputIsStale, gn as atomWithReducer, i as useCellFocusActions, in as parseDataset, jn as LoaderCircle, jt as generateUUID, k as ChevronDownIcon, ln as UIElementId, mt as headingToIdentifier, nt as createCell, o as maybeAddAltairImport, ot as getInitialAppMode, p as useExpandedConsoleOutput, pn as jsonParseWithSpecialChar, pt as isErrorMime, qt as requestClientAtom, r as LazyAnyLanguageCodeMirror, rn as parseAttrValue, s as Spinner, sn as HTMLCellId, st as initialModeAtom, un as findCellId, vn as selectAtom, w as AccordionItem, wn as Trigger2, wt as MarkdownLanguageAdapter, xn as Content2, xt as normalizeName, yt as getValidName, zn as CircleAlert, zt as DATA_TYPE_ICON, __tla as __tla_0 } from "./html-to-image-uzALXlch.js";
|
|
26
26
|
import { __tla as __tla_1 } from "./chunk-5FQGJX7Z-CO1e63h_.js";
|
|
27
27
|
import { o as useSize, s as Root$3, u as createLucideIcon } from "./dist-ESg7xyoD.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-BKWq0wn2.js";
|
|
29
|
-
import { $ as getPageIndexForRow, A as contextAwarePanelType, At as GripHorizontal, B as TableHead, Bt as ChevronsDownUp, C as useInternalStateWithSync, Ct as RenderTextWithLinks, D as PANEL_TYPES, Dt as getStaticVirtualFiles, E as ContextAwarePanelItem, Et as EmotionCacheProvider, F as Fill, Ft as Download, G as inferFieldTypes, H as TableRow, Ht as ArrowDownWideNarrow, I as Provider$1, It as Code, J as ColumnChartSpecModel, K as renderCellValue, L as Table, Lt as ChevronsUpDown, M as SlotNames, Mt as EyeOff, N as slotsController, O as contextAwarePanelOpen, Ot as isStaticNotebook, P as Toggle, Pt as Ellipsis, Q as filtersToFilterGroup, R as TableBody, Rt as ChevronsRight, S as prettifyRowCount, St as useOverflowDetection, T as ComboboxItem, Tt as HtmlOutput, U as NAMELESS_COLUMN_PREFIX, V as TableHeader, Vt as ChevronLeft, W as generateColumns, X as useIntersectionObserver, Y as DelayMount, Z as usePrevious$1, _ as downloadBlob, _t as TabsTrigger, at as toFieldTypes, b as Filenames, bt as ChartLoadingState, c as Slide, ct as CommandEmpty, d as JsonOutput, dt as CommandList, et as loadTableAndRawData, f as OutputArea, ft as CommandSeparator, g as ADD_PRINTING_CLASS, gt as TabsList, h as InstallPackageButton, ht as TabsContent, it as TOO_MANY_ROWS, j as isCellAwareAtom, jt as Funnel, k as contextAwarePanelOwner, kt as TextWrap, l as RadioGroup, lt as CommandInput, m as DataTable, mt as Tabs, n as marimoVersionAtom, nt as INDEX_COLUMN_NAME, o as SLIDE_TYPE_OPTIONS_BY_VALUE, ot as getMimeValues, p as OutputRenderer, pt as Maps, q as ColumnChartContext, r as showCodeInRunModeAtom, rt as SELECT_COLUMN_ID, st as Command, t as useNotebookCodeAvailable, tt as loadTableData, u as RadioGroupItem, ut as CommandItem, v as downloadByURL, vt as ChartErrorState, w as Combobox, wt as Kbd, x as prettifyRowColumnCount, xt as LazyVegaEmbed, y as downloadHTMLAsImage, yt as ChartInfoState, z as TableCell, zt as ChevronsLeft, __tla as __tla_2 } from "./code-visibility-
|
|
29
|
+
import { $ as getPageIndexForRow, A as contextAwarePanelType, At as GripHorizontal, B as TableHead, Bt as ChevronsDownUp, C as useInternalStateWithSync, Ct as RenderTextWithLinks, D as PANEL_TYPES, Dt as getStaticVirtualFiles, E as ContextAwarePanelItem, Et as EmotionCacheProvider, F as Fill, Ft as Download, G as inferFieldTypes, H as TableRow, Ht as ArrowDownWideNarrow, I as Provider$1, It as Code, J as ColumnChartSpecModel, K as renderCellValue, L as Table, Lt as ChevronsUpDown, M as SlotNames, Mt as EyeOff, N as slotsController, O as contextAwarePanelOpen, Ot as isStaticNotebook, P as Toggle, Pt as Ellipsis, Q as filtersToFilterGroup, R as TableBody, Rt as ChevronsRight, S as prettifyRowCount, St as useOverflowDetection, T as ComboboxItem, Tt as HtmlOutput, U as NAMELESS_COLUMN_PREFIX, V as TableHeader, Vt as ChevronLeft, W as generateColumns, X as useIntersectionObserver, Y as DelayMount, Z as usePrevious$1, _ as downloadBlob, _t as TabsTrigger, at as toFieldTypes, b as Filenames, bt as ChartLoadingState, c as Slide, ct as CommandEmpty, d as JsonOutput, dt as CommandList, et as loadTableAndRawData, f as OutputArea, ft as CommandSeparator, g as ADD_PRINTING_CLASS, gt as TabsList, h as InstallPackageButton, ht as TabsContent, it as TOO_MANY_ROWS, j as isCellAwareAtom, jt as Funnel, k as contextAwarePanelOwner, kt as TextWrap, l as RadioGroup, lt as CommandInput, m as DataTable, mt as Tabs, n as marimoVersionAtom, nt as INDEX_COLUMN_NAME, o as SLIDE_TYPE_OPTIONS_BY_VALUE, ot as getMimeValues, p as OutputRenderer, pt as Maps, q as ColumnChartContext, r as showCodeInRunModeAtom, rt as SELECT_COLUMN_ID, st as Command, t as useNotebookCodeAvailable, tt as loadTableData, u as RadioGroupItem, ut as CommandItem, v as downloadByURL, vt as ChartErrorState, w as Combobox, wt as Kbd, x as prettifyRowColumnCount, xt as LazyVegaEmbed, y as downloadHTMLAsImage, yt as ChartInfoState, z as TableCell, zt as ChevronsLeft, __tla as __tla_2 } from "./code-visibility-BQo0Byk4.js";
|
|
30
30
|
import { c as Calendar, i as createReducerAndAtoms, n as useOnUnmount, o as ToggleLeft, t as useOnMount } from "./useLifecycle-smVfjLNI.js";
|
|
31
31
|
import { n as $fb18d541ea1ad717$export$ad991b66133851cf, r as $5a387cc49350e6db$export$722debc0e56fea39, t as $896ba0a80a8f4d36$export$85fd5fdf27bacc79 } from "./useDateFormatter-B3mCQMP3.js";
|
|
32
32
|
import { t as Check } from "./check-CFM2mVDr.js";
|
|
33
33
|
import { A as Trigger$1, C as $a916eb452884faea$export$b7a616150fdb9f44, D as $b5e257d569688ac6$export$535bd6ca7f90a273, E as $18f2051aff69b9bf$export$a54013f0d02a8f82, F as X, L as ChevronDown, M as usePrevious$2, N as useDirection, P as createCollection, S as logNever, T as $18f2051aff69b9bf$export$43bb16f9c6d9e3f7, a as SelectGroup, c as SelectSeparator, d as NativeSelect, f as selectStyles, i as SelectContent, j as clamp$2, k as Icon, l as SelectTrigger, n as capitalize, o as SelectItem, r as Select, s as SelectLabel, t as Strings, u as SelectValue, w as $488c6ddbf4ef74c2$export$cc77c4ff7e8673c5, x as assertNever } from "./strings-B_FOH6eV.js";
|
|
34
34
|
import { $ as $e5be200c675c3b3a$export$aca958c65c314e6c, A as $d2b4bc8c273e7be6$export$24d547caef80ccd1, At as $c87311424ea30a05$export$fedb369cb70207f1, B as $64fa3d84918910a7$export$c62b8e45d58ddad9, Bt as $431fbd86ca7dc216$export$f21a1ffae260145a, C as $a049562f99e7db0e$export$eb2fcfdbd7ba97d4, Ct as $8ae05eaa5c114e9c$export$7f54fc3180508a52, D as $ee014567cb39d3f0$export$ff05c3ac10437e03, Dt as $c87311424ea30a05$export$78551043582a6a98, E as $ee014567cb39d3f0$export$f551688fc98f2e09, Et as $c87311424ea30a05$export$6446a186d09e379e, F as $64fa3d84918910a7$export$2881499e37b75b9a, Ft as $d4ee10de306f2510$export$b4f377a2b6254582, H as $64fa3d84918910a7$export$ef03459518577ad4, Ht as $bdb11010cef70236$export$b4cc09c592e8fdb8, I as $64fa3d84918910a7$export$29f1550f4b0d4415, It as $d4ee10de306f2510$export$cd4e5573fbe2b576, J as $d2e8511e6f209edf$export$e908e06f4b8e3402, K as useDebounceControlledState, L as $64fa3d84918910a7$export$4d86445c2cf5e3, Lt as $d4ee10de306f2510$export$e58f029f0fbfdb29, M as $01b77f81d0f07f68$export$75b6ee27786ba447, Mt as $65484d02dcb7eb3e$export$457c3d6518dd4c6f, N as $01b77f81d0f07f68$export$b04be29aa201d4f5, Nt as $3ef42575df84b30b$export$9d1611c77c2fe928, O as $514c0188e459b4c0$export$5f1af8db9871e1d6, Ot as $c87311424ea30a05$export$9ac100e40613ea10, P as $f39a9eba43920ace$export$b5d7cc18bb8d2b59, Pt as $d4ee10de306f2510$export$4282f70798064fe0, Q as $e5be200c675c3b3a$export$a763b9476acd3eb, R as $64fa3d84918910a7$export$9d4c57ee4c6ffdd8, Rt as $f4e2df6bd15f8569$export$98658e8c59125e6a, S as $3985021b0ad6602f$export$f5b8910cec6cf069, St as $e9faafb641e167db$export$90fc3a17d93f704c, T as $d3e0e05bdfcf66bd$export$c24727297075ec6a, Tt as $313b98861ee5dd6c$export$d6875122194c7b44, U as $64fa3d84918910a7$export$fabf2dc03a41866e, Ut as $bdb11010cef70236$export$f680877a34711e37, V as $64fa3d84918910a7$export$df3a06d6289f983e, Vt as $ff5963eb1fccf552$export$e08e3b67e392101e, Wt as $f0a04ccd8dbdd83b$export$e5c5a5f917a5871c, X as $e93e671b31057976$export$b8473d3665f3a75a, Y as $2baaea4c71418dea$export$294aa081a6c6f55d, Z as $e5be200c675c3b3a$export$75ee7c75d68f5b0e, _t as $9446cca9a3875146$export$7d15b64cf5a3a4c4, a as NumberField, at as $6c7bd7858deea686$export$cd11ab140839f11d, b as DropdownMenuTrigger, bt as $b4b717babfbb907b$export$bebd5a1431fec25d, c as prettyNumber, ct as $6db58dc88e78b024$export$2f817fcdc4b89ae0, d as DropdownMenuContent, dt as $9ab94262bd0047c7$export$420e68273165f4ec, et as $e5be200c675c3b3a$export$dad6ae84456c676a, f as DropdownMenuGroup, fn as Circle, ft as $3ad3f6e1647bc98d$export$80f3e147d781571c, g as DropdownMenuSeparator, gt as $ae1eeba8b9eafd08$export$5165eccb35aaadb5, ht as _class_private_field_init, i as OnBlurredInput, it as $701a24aa0da5b062$export$ea18c227d4417cc3, j as $d2b4bc8c273e7be6$export$353f5b6fc5456de1, jt as $7215afc6de606d6b$export$de79e2c695e052f3, k as $514c0188e459b4c0$export$9afb8bc826b033ea, kt as $c87311424ea30a05$export$a11b0059900ceec8, l as prettyScientificNumber, lt as $5b160d28a433310d$export$c17fa47878dc55b6, m as DropdownMenuLabel, mt as $f6c31cce2adf654f$export$45712eceda6fad21, n as DebouncedNumberInput, nt as $319e236875307eab$export$a9b970dcc4ae71a9, ot as $fca6afa0e843324b$export$87b761675e8eaa10, p as DropdownMenuItem, pn as ChevronRight, pt as $507fabe10e71c6fb$export$630ff653c5ada6a9, q as useDebouncedCallback, r as Input, rt as $f7dceffc5ad7768b$export$4e328f61c538687f, st as $fca6afa0e843324b$export$f12b703ca79dfbb1, t as DebouncedInput, tt as $e5be200c675c3b3a$export$fc1a364ae1f3ff10, u as DropdownMenu, ut as $6179b936705e76d3$export$ae780daf29e6d456, vt as $458b0a5536c1a7cf$export$40bfa8c7b0832715, w as $a049562f99e7db0e$export$f9c6924e160136d1, wt as $df56164dff5785e2$export$4338b53315abf666, x as $3985021b0ad6602f$export$37fb8590cf2c088c, xt as $99facab73266f662$export$5add1d006293d136, yt as $b4b717babfbb907b$export$4c063cf1350e6fed, z as $64fa3d84918910a7$export$c245e6201fed2f75, zt as $431fbd86ca7dc216$export$b204af158042fbac } from "./input-Drx1pguW.js";
|
|
35
35
|
import { c as asRemoteURL, f as require_cuid2, g as CircleQuestionMark, h as isWasm, m as Deferred, u as appendQueryParams } from "./toDate-yqOcZ_tY.js";
|
|
36
|
-
import { a as MarimoIncomingMessageEvent, c as MarimoValueUpdateEvent, d as File, i as PythonIcon, l as createInputEvent, n as blobToString, o as MarimoValueInputEvent, r as filesToBase64, s as MarimoValueReadyEvent, t as processOutput, u as deserializeBlob } from "./process-output-
|
|
36
|
+
import { a as MarimoIncomingMessageEvent, c as MarimoValueUpdateEvent, d as File, i as PythonIcon, l as createInputEvent, n as blobToString, o as MarimoValueInputEvent, r as filesToBase64, s as MarimoValueReadyEvent, t as processOutput, u as deserializeBlob } from "./process-output-CD5QiwBb.js";
|
|
37
37
|
import { i as Pencil, n as Trash, r as Plus, t as BulkEdit } from "./types-DBtDeUKD.js";
|
|
38
38
|
import { t as require_react_dom } from "./react-dom-BWRJ_g_k.js";
|
|
39
39
|
import { t as require_jsx_runtime } from "./jsx-runtime-COBk7ree.js";
|
|
@@ -13655,7 +13655,7 @@ Defaulting to \`null\`.`;
|
|
|
13655
13655
|
};
|
|
13656
13656
|
}
|
|
13657
13657
|
};
|
|
13658
|
-
var LazyChatbot = import_react.lazy(() => import("./chat-ui-
|
|
13658
|
+
var LazyChatbot = import_react.lazy(() => import("./chat-ui-CtIeZD6j.js").then((e) => ({
|
|
13659
13659
|
default: e.Chatbot
|
|
13660
13660
|
}))), messageSchema = array(object({
|
|
13661
13661
|
id: string(),
|
|
@@ -44649,7 +44649,7 @@ ${c}
|
|
|
44649
44649
|
if (l && l !== "slide") return l;
|
|
44650
44650
|
if (c == null ? void 0 : c.has(e)) return "skip";
|
|
44651
44651
|
}
|
|
44652
|
-
var LazySlidesComponent = import_react.lazy(() => import("./reveal-component-
|
|
44652
|
+
var LazySlidesComponent = import_react.lazy(() => import("./reveal-component-CULfHQbp.js"));
|
|
44653
44653
|
const SlidesLayoutPlugin = {
|
|
44654
44654
|
type: "slides",
|
|
44655
44655
|
name: "Slides",
|
|
@@ -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 { it as parseHtmlContent, rt as ansiToPlainText } from "./html-to-image-
|
|
3
|
+
import { it as parseHtmlContent, rt as ansiToPlainText } from "./html-to-image-uzALXlch.js";
|
|
4
4
|
import { u as createLucideIcon } from "./dist-ESg7xyoD.js";
|
|
5
5
|
import { t as Strings } from "./strings-B_FOH6eV.js";
|
|
6
6
|
import { t as require_jsx_runtime } from "./jsx-runtime-COBk7ree.js";
|
|
@@ -6,9 +6,9 @@ import { s as __toESM } from "./chunk-BNovOVIE.js";
|
|
|
6
6
|
import { _ as Logger, g as cn, h as Events, l as useEventListener, t as Button } from "./button-CA5pI2YF.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 "./html-to-image-
|
|
9
|
+
import "./html-to-image-uzALXlch.js";
|
|
10
10
|
import "./chunk-5FQGJX7Z-CO1e63h_.js";
|
|
11
|
-
import { It as Code, Mt as EyeOff, Nt as Expand, a as DEFAULT_SLIDE_TYPE, c as Slide, i as DEFAULT_DECK_TRANSITION, s as SlideSidebar, t as useNotebookCodeAvailable } from "./code-visibility-
|
|
11
|
+
import { It as Code, Mt as EyeOff, Nt as Expand, a as DEFAULT_SLIDE_TYPE, c as Slide, i as DEFAULT_DECK_TRANSITION, s as SlideSidebar, t as useNotebookCodeAvailable } from "./code-visibility-BQo0Byk4.js";
|
|
12
12
|
import "./input-Drx1pguW.js";
|
|
13
13
|
import "./toDate-yqOcZ_tY.js";
|
|
14
14
|
import "./react-dom-BWRJ_g_k.js";
|
package/package.json
CHANGED
|
@@ -6,6 +6,7 @@ import parse, {
|
|
|
6
6
|
type HTMLReactParserOptions,
|
|
7
7
|
} from "html-react-parser";
|
|
8
8
|
import React, {
|
|
9
|
+
cloneElement,
|
|
9
10
|
isValidElement,
|
|
10
11
|
type JSX,
|
|
11
12
|
type ReactNode,
|
|
@@ -169,6 +170,35 @@ const addCopyButtonToCodehilite: TransformFn = (
|
|
|
169
170
|
}
|
|
170
171
|
};
|
|
171
172
|
|
|
173
|
+
// Decorator (not a match-and-replace transform): applies a src-based key
|
|
174
|
+
// to <img> elements so they remount on src change. Reusing an <img> across
|
|
175
|
+
// src changes can leave the previous image painted (e.g. when the new
|
|
176
|
+
// request is slow/blocked, served stale by a CDN, or fails CORS), so the
|
|
177
|
+
// user sees the old image even though the HTML source is up to date.
|
|
178
|
+
//
|
|
179
|
+
// Runs unconditionally after the match-and-replace transforms so it still
|
|
180
|
+
// applies when an <img> was already wrapped by, say, wrapTooltipTargets.
|
|
181
|
+
const keyImagesBySrc: TransformFn = (
|
|
182
|
+
reactNode: ReactNode,
|
|
183
|
+
domNode: DOMNode,
|
|
184
|
+
index: number,
|
|
185
|
+
): JSX.Element | undefined => {
|
|
186
|
+
if (!(domNode instanceof Element) || domNode.name !== "img") {
|
|
187
|
+
return undefined;
|
|
188
|
+
}
|
|
189
|
+
const src = domNode.attribs?.src;
|
|
190
|
+
if (!src || !isValidElement(reactNode)) {
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
// data: URIs are inline — no network fetch — so they can't go stale.
|
|
194
|
+
// Skip to avoid bloating the React key with a megabyte base64 payload.
|
|
195
|
+
// URI schemes are case-insensitive per RFC 3986.
|
|
196
|
+
if (/^data:/i.test(src)) {
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
return cloneElement(reactNode, { key: `${src}-${index}` });
|
|
200
|
+
};
|
|
201
|
+
|
|
172
202
|
// Wrap elements with data-marimo-doc attribute in a DocHoverTarget
|
|
173
203
|
const wrapDocHoverTargets: TransformFn = (
|
|
174
204
|
reactNode: ReactNode,
|
|
@@ -281,6 +311,8 @@ function parseHtml({
|
|
|
281
311
|
...additionalReplacements,
|
|
282
312
|
];
|
|
283
313
|
|
|
314
|
+
// Match-and-replace transforms: the first one that returns a value wins
|
|
315
|
+
// (short-circuits the rest).
|
|
284
316
|
const transformFunctions: TransformFn[] = [
|
|
285
317
|
addCopyButtonToCodehilite,
|
|
286
318
|
preserveQueryParamsInAnchorLinks,
|
|
@@ -290,6 +322,12 @@ function parseHtml({
|
|
|
290
322
|
removeWrappingHtmlTags,
|
|
291
323
|
];
|
|
292
324
|
|
|
325
|
+
// Decorators: run unconditionally on the result of the transform pipeline
|
|
326
|
+
// and may further wrap/clone it. Used for cross-cutting concerns that
|
|
327
|
+
// should apply regardless of which (if any) match-and-replace transform
|
|
328
|
+
// ran above.
|
|
329
|
+
const decoratorFunctions: TransformFn[] = [keyImagesBySrc];
|
|
330
|
+
|
|
293
331
|
return parse(html, {
|
|
294
332
|
replace: (domNode: DOMNode, index: number) => {
|
|
295
333
|
for (const renderFunction of renderFunctions) {
|
|
@@ -301,13 +339,21 @@ function parseHtml({
|
|
|
301
339
|
return domNode;
|
|
302
340
|
},
|
|
303
341
|
transform: (reactNode: ReactNode, domNode: DOMNode, index: number) => {
|
|
342
|
+
let result: ReactNode = reactNode as JSX.Element;
|
|
304
343
|
for (const transformFunction of transformFunctions) {
|
|
305
|
-
const transformed = transformFunction(
|
|
344
|
+
const transformed = transformFunction(result, domNode, index);
|
|
306
345
|
if (transformed) {
|
|
307
|
-
|
|
346
|
+
result = transformed;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
for (const decorate of decoratorFunctions) {
|
|
351
|
+
const decorated = decorate(result, domNode, index);
|
|
352
|
+
if (decorated) {
|
|
353
|
+
result = decorated;
|
|
308
354
|
}
|
|
309
355
|
}
|
|
310
|
-
return
|
|
356
|
+
return result as JSX.Element;
|
|
311
357
|
},
|
|
312
358
|
});
|
|
313
359
|
}
|
|
@@ -60,6 +60,60 @@ describe("parseHtml", () => {
|
|
|
60
60
|
`);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
+
test("img has key derived from src so React remounts on src change", () => {
|
|
64
|
+
const html = '<img src="https://cdn.example.com/a.png" alt="a">';
|
|
65
|
+
const result = parseHtml({ html }) as React.ReactElement;
|
|
66
|
+
expect(result.key).toBe("https://cdn.example.com/a.png-0");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("multiple imgs each get distinct keys", () => {
|
|
70
|
+
const html =
|
|
71
|
+
'<div><img src="https://cdn.example.com/a.png"><img src="https://cdn.example.com/b.png"></div>';
|
|
72
|
+
const result = parseHtml({ html }) as React.ReactElement<{
|
|
73
|
+
children: React.ReactElement[];
|
|
74
|
+
}>;
|
|
75
|
+
const children = result.props.children;
|
|
76
|
+
expect(children[0].key).toBe("https://cdn.example.com/a.png-0");
|
|
77
|
+
expect(children[1].key).toBe("https://cdn.example.com/b.png-1");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("img without src is left alone", () => {
|
|
81
|
+
const html = "<img>";
|
|
82
|
+
const result = parseHtml({ html }) as React.ReactElement;
|
|
83
|
+
expect(result.key).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("img with data: URI is not keyed (inline, no network fetch)", () => {
|
|
87
|
+
const longPayload = "A".repeat(10_000);
|
|
88
|
+
const html = `<img src="data:image/png;base64,${longPayload}">`;
|
|
89
|
+
const result = parseHtml({ html }) as React.ReactElement;
|
|
90
|
+
// No remount-on-src needed for inline images, so we leave the key
|
|
91
|
+
// unset rather than bloat it with the base64 payload.
|
|
92
|
+
expect(result.key).toBeNull();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("img with uppercase DATA: URI is also skipped (scheme is case-insensitive)", () => {
|
|
96
|
+
const html = `<img src="DATA:image/png;base64,${"A".repeat(100)}">`;
|
|
97
|
+
const result = parseHtml({ html }) as React.ReactElement;
|
|
98
|
+
expect(result.key).toBeNull();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("img wrapped by data-tooltip is still keyed by src", () => {
|
|
102
|
+
const html =
|
|
103
|
+
'<img src="https://cdn.example.com/a.png" data-tooltip="hi" alt="a">';
|
|
104
|
+
const result = parseHtml({ html }) as React.ReactElement;
|
|
105
|
+
// Outer Tooltip carries the src-based key so it remounts on src change,
|
|
106
|
+
// forcing the inner <img> to remount as well.
|
|
107
|
+
expect(result.key).toBe("https://cdn.example.com/a.png-0");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("img wrapped by data-marimo-doc is still keyed by src", () => {
|
|
111
|
+
const html =
|
|
112
|
+
'<img src="https://cdn.example.com/b.png" data-marimo-doc="foo.bar">';
|
|
113
|
+
const result = parseHtml({ html }) as React.ReactElement;
|
|
114
|
+
expect(result.key).toBe("https://cdn.example.com/b.png-0");
|
|
115
|
+
});
|
|
116
|
+
|
|
63
117
|
test("codehilite with copy button", () => {
|
|
64
118
|
const html =
|
|
65
119
|
'<div class="codehilite"><pre><code>console.log("Hello");</code></pre></div>';
|