@marimo-team/islands 0.23.10-dev43 → 0.23.10-dev44
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-CqtCMdc-.js → chat-ui-BxwraqS-.js} +3 -3
- package/dist/{chunk-5FQGJX7Z-BNjes6Yx.js → chunk-5FQGJX7Z-BbqSm5gU.js} +1227 -1222
- package/dist/{code-block-37QAKDTI-m92Yc8pv.js → code-block-37QAKDTI-O6IH6wAi.js} +1 -1
- package/dist/{code-visibility-BpCUlq4u.js → code-visibility-CzYIDGEy.js} +2 -2
- package/dist/{html-to-image-BviMxrkm.js → html-to-image-DXSdAI_Y.js} +2129 -2113
- package/dist/main.js +6 -6
- package/dist/{mermaid-4DMBBIKO-DIdL224_.js → mermaid-4DMBBIKO-Eg3D4FVz.js} +1 -1
- package/dist/{process-output-Bq3VMBsg.js → process-output-uozPQhBl.js} +1 -1
- package/dist/{reveal-component-x3disQC3.js → reveal-component-DERGOPiW.js} +3 -3
- package/package.json +2 -1
- package/src/components/chat/chat-display.tsx +2 -2
- package/src/components/markdown/__tests__/markdown-renderer.test.tsx +43 -0
- package/src/components/markdown/markdown-renderer.tsx +24 -1
package/dist/main.js
CHANGED
|
@@ -22,17 +22,17 @@ 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-UqRYxiOg.js";
|
|
25
|
-
import { $ as reducer, B as safeExtractSetUIElementMessageBuffers, Bn as CircleAlert, Bt as DATA_TYPE_ICON, C as AccordionContent, Cn as Root2$1, Ct as Checkbox, Dn as Table2, Dt as PathBuilder, E as BorderAllIcon, En as Trash2, F as base64ToDataView, Fn as Eye, H as getMarimoExportContext, Ht as require_client, It as jotaiJsonStorage, J as getCellNames, Jt as requestClientAtom, K as createActions, Kt as convertStatsName, L as dataViewToBase64, Ln as Database, Mn as Layers, Mt as generateUUID, Nn as Info, On as PaintRoller, Ot as Paths, Pn as FileText, Pt as repl, Q as numColumnsAtom, Rn as Columns2, S as Accordion, Sn as Item$1, St as normalizeName, T as AccordionTrigger, Tt as MarkdownLanguageAdapter, U as hasTrustedExportContext, V as renderHTML, Vn as Braces, W as hasRunAnyCellAtom, X as notebookAtom, Xt as isUninstantiated, Y as hasOnlyOneCellAtom, Yt as useRequestClient, Z as notebookOutline, _n as atomWithStorage, a as useCellFocusActions, an as parseDataset, bt as getValidName, cn as HTMLCellId, ct as initialModeAtom, dn as findCellId, et as useCellActions, f as isOutputEmpty, fn as OBJECT_ID_ATTR, ft as outputIsLoading, gn as atomWithReducer, ht as headingToIdentifier, i as LazyAnyLanguageCodeMirror, in as parseAttrValue, jn as LoaderCircle, jt as DeferredRequestRegistry, k as ChevronDownIcon, lt as kioskModeAtom, mn as jsonParseWithSpecialChar, mt as isErrorMime, n as Spinner, o as useLastFocusedCellId, on as parseInitialValue, p as useExpandedConsoleOutput, pn as RANDOM_ID_ATTR, pt as outputIsStale, qt as getRequestClient, rt as createCell, s as maybeAddAltairImport, st as getInitialAppMode, tn as NotebookScopedLocalStorage, tt as useCellIds, un as UIElementId, vn as selectAtom, w as AccordionItem, wn as Trigger2, wt as customPythonLanguageSupport, xn as Content2, xt as isInternalCellName, zt as PluralWords, __tla as __tla_0 } from "./html-to-image-
|
|
26
|
-
import { __tla as __tla_1 } from "./chunk-5FQGJX7Z-
|
|
25
|
+
import { $ as reducer, B as safeExtractSetUIElementMessageBuffers, Bn as CircleAlert, Bt as DATA_TYPE_ICON, C as AccordionContent, Cn as Root2$1, Ct as Checkbox, Dn as Table2, Dt as PathBuilder, E as BorderAllIcon, En as Trash2, F as base64ToDataView, Fn as Eye, H as getMarimoExportContext, Ht as require_client, It as jotaiJsonStorage, J as getCellNames, Jt as requestClientAtom, K as createActions, Kt as convertStatsName, L as dataViewToBase64, Ln as Database, Mn as Layers, Mt as generateUUID, Nn as Info, On as PaintRoller, Ot as Paths, Pn as FileText, Pt as repl, Q as numColumnsAtom, Rn as Columns2, S as Accordion, Sn as Item$1, St as normalizeName, T as AccordionTrigger, Tt as MarkdownLanguageAdapter, U as hasTrustedExportContext, V as renderHTML, Vn as Braces, W as hasRunAnyCellAtom, X as notebookAtom, Xt as isUninstantiated, Y as hasOnlyOneCellAtom, Yt as useRequestClient, Z as notebookOutline, _n as atomWithStorage, a as useCellFocusActions, an as parseDataset, bt as getValidName, cn as HTMLCellId, ct as initialModeAtom, dn as findCellId, et as useCellActions, f as isOutputEmpty, fn as OBJECT_ID_ATTR, ft as outputIsLoading, gn as atomWithReducer, ht as headingToIdentifier, i as LazyAnyLanguageCodeMirror, in as parseAttrValue, jn as LoaderCircle, jt as DeferredRequestRegistry, k as ChevronDownIcon, lt as kioskModeAtom, mn as jsonParseWithSpecialChar, mt as isErrorMime, n as Spinner, o as useLastFocusedCellId, on as parseInitialValue, p as useExpandedConsoleOutput, pn as RANDOM_ID_ATTR, pt as outputIsStale, qt as getRequestClient, rt as createCell, s as maybeAddAltairImport, st as getInitialAppMode, tn as NotebookScopedLocalStorage, tt as useCellIds, un as UIElementId, vn as selectAtom, w as AccordionItem, wn as Trigger2, wt as customPythonLanguageSupport, xn as Content2, xt as isInternalCellName, zt as PluralWords, __tla as __tla_0 } from "./html-to-image-DXSdAI_Y.js";
|
|
26
|
+
import { __tla as __tla_1 } from "./chunk-5FQGJX7Z-BbqSm5gU.js";
|
|
27
27
|
import { o as useSize, s as Root$1, u as createLucideIcon } from "./dist-Dk6PV_d3.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-X7FwLJni.js";
|
|
29
|
-
import { $ as contextAwarePanelOpen, $t as $fae977aafc393c5c$export$6b862160d295c8e, A as prettifyRowCount, At as SELECT_COLUMN_ID, B as DatePicker, Bt as TabsContent, C as downloadSizeLimitAtom, Ct as DelayMount, D as ErrorState, Dt as loadTableAndRawData, E as EmptyState, Et as getPageIndexForRow, F as ContextMenuSeparator, Ft as Maps, G as CommandEmpty, Gt as ChartLoadingState, H as Combobox, Ht as TabsTrigger, I as ContextMenuTrigger, It as dateToLocalISODate, J as CommandList, Jt as RenderTextWithLinks, K as CommandInput, Kt as LazyVegaEmbed, L as useInternalStateWithSync, Lt as dateToLocalISODateTime, M as ContextMenu, Mt as toFieldTypes, N as ContextMenuContent, Nt as getMimeValues, O as LoadingState, Ot as loadTableData, P as ContextMenuItem, Pt as isNullishFilter, Q as PANEL_TYPES, Qt as $fae977aafc393c5c$export$588937bcd60ade55, R as useSelectList, Rt as dateToLocalISOTime, S as Filenames, St as ColumnChartSpecModel, T as ColumnPreviewContainer, Tt as usePrevious$1, U as ComboboxItem, Ut as ChartErrorState, V as DateRangePicker, Vt as TabsList, W as Command, Wt as ChartInfoState, X as smartMatch, Xt as HtmlOutput, Y as CommandSeparator, Yt as Kbd, Z as ContextAwarePanelItem, Zt as EmotionCacheProvider, _ as ADD_PRINTING_CLASS, _t as NAMELESS_COLUMN_PREFIX, an as Ellipsis, at as Toggle, b as downloadHTMLAsImage, bt as renderCellValue, c as Slide, cn as ChevronsUpDown, d as RadioGroupItem, dn as ChevronsDownUp, dt as Table, en as TextWrap, et as contextAwarePanelOwner, f as JsonOutput, fn as ChevronLeft, ft as TableBody, g as InstallPackageButton, gt as TableRow, h as DataTable, ht as TableHeader, it as slotsController, j as getColumnCountForDisplay, jt as TOO_MANY_ROWS, k as prettifyRowColumnCount, kt as INDEX_COLUMN_NAME, l as Switch, ln as ChevronsRight, lt as Fill, m as OutputRenderer, mt as TableHead, n as marimoVersionAtom, nn as Funnel, nt as isCellAwareAtom, o as SLIDE_TYPE_OPTIONS_BY_VALUE, on as Download, p as OutputArea, pn as ArrowDownWideNarrow, pt as TableCell, q as CommandItem, qt as useOverflowDetection, r as showCodeInRunModeAtom, rn as EyeOff, rt as SlotNames, sn as Code, t as useNotebookCodeAvailable, tn as GripHorizontal, tt as contextAwarePanelType, u as RadioGroup, un as ChevronsLeft, ut as Provider$1, v as downloadBlob, vt as generateColumns, w as ColumnName, wt as useIntersectionObserver, x as Progress, xt as ColumnChartContext, y as downloadByURL, yt as inferFieldTypes, z as CompactChipRow, zt as Tabs, __tla as __tla_2 } from "./code-visibility-
|
|
29
|
+
import { $ as contextAwarePanelOpen, $t as $fae977aafc393c5c$export$6b862160d295c8e, A as prettifyRowCount, At as SELECT_COLUMN_ID, B as DatePicker, Bt as TabsContent, C as downloadSizeLimitAtom, Ct as DelayMount, D as ErrorState, Dt as loadTableAndRawData, E as EmptyState, Et as getPageIndexForRow, F as ContextMenuSeparator, Ft as Maps, G as CommandEmpty, Gt as ChartLoadingState, H as Combobox, Ht as TabsTrigger, I as ContextMenuTrigger, It as dateToLocalISODate, J as CommandList, Jt as RenderTextWithLinks, K as CommandInput, Kt as LazyVegaEmbed, L as useInternalStateWithSync, Lt as dateToLocalISODateTime, M as ContextMenu, Mt as toFieldTypes, N as ContextMenuContent, Nt as getMimeValues, O as LoadingState, Ot as loadTableData, P as ContextMenuItem, Pt as isNullishFilter, Q as PANEL_TYPES, Qt as $fae977aafc393c5c$export$588937bcd60ade55, R as useSelectList, Rt as dateToLocalISOTime, S as Filenames, St as ColumnChartSpecModel, T as ColumnPreviewContainer, Tt as usePrevious$1, U as ComboboxItem, Ut as ChartErrorState, V as DateRangePicker, Vt as TabsList, W as Command, Wt as ChartInfoState, X as smartMatch, Xt as HtmlOutput, Y as CommandSeparator, Yt as Kbd, Z as ContextAwarePanelItem, Zt as EmotionCacheProvider, _ as ADD_PRINTING_CLASS, _t as NAMELESS_COLUMN_PREFIX, an as Ellipsis, at as Toggle, b as downloadHTMLAsImage, bt as renderCellValue, c as Slide, cn as ChevronsUpDown, d as RadioGroupItem, dn as ChevronsDownUp, dt as Table, en as TextWrap, et as contextAwarePanelOwner, f as JsonOutput, fn as ChevronLeft, ft as TableBody, g as InstallPackageButton, gt as TableRow, h as DataTable, ht as TableHeader, it as slotsController, j as getColumnCountForDisplay, jt as TOO_MANY_ROWS, k as prettifyRowColumnCount, kt as INDEX_COLUMN_NAME, l as Switch, ln as ChevronsRight, lt as Fill, m as OutputRenderer, mt as TableHead, n as marimoVersionAtom, nn as Funnel, nt as isCellAwareAtom, o as SLIDE_TYPE_OPTIONS_BY_VALUE, on as Download, p as OutputArea, pn as ArrowDownWideNarrow, pt as TableCell, q as CommandItem, qt as useOverflowDetection, r as showCodeInRunModeAtom, rn as EyeOff, rt as SlotNames, sn as Code, t as useNotebookCodeAvailable, tn as GripHorizontal, tt as contextAwarePanelType, u as RadioGroup, un as ChevronsLeft, ut as Provider$1, v as downloadBlob, vt as generateColumns, w as ColumnName, wt as useIntersectionObserver, x as Progress, xt as ColumnChartContext, y as downloadByURL, yt as inferFieldTypes, z as CompactChipRow, zt as Tabs, __tla as __tla_2 } from "./code-visibility-CzYIDGEy.js";
|
|
30
30
|
import { c as Calendar, i as createReducerAndAtoms, n as useOnUnmount, o as ToggleLeft, t as useOnMount } from "./useLifecycle-00mO3OSS.js";
|
|
31
31
|
import { t as Check } from "./check-BCaJeT-J.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-J57tzLr3.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-OdWHkobi.js";
|
|
34
34
|
import { _ as isWasm, c as asRemoteURL, d as isStaticNotebook, f as appendQueryParams, g as Deferred, m as require_cuid2, u as getStaticVirtualFiles, v as CircleQuestionMark } from "./toDate-d8RCRrRd.js";
|
|
35
|
-
import { a as MarimoIncomingMessageEvent, c as MarimoValueUpdateEvent, d as Square, f 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-
|
|
35
|
+
import { a as MarimoIncomingMessageEvent, c as MarimoValueUpdateEvent, d as Square, f 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-uozPQhBl.js";
|
|
36
36
|
import { n as Trash, r as Pencil, t as BulkEdit } from "./types-ChtMFmZ2.js";
|
|
37
37
|
import { n as require_prop_types, r as Plus, t as ErrorBoundary } from "./ErrorBoundary-DpbaKVv7.js";
|
|
38
38
|
import { t as require_react_dom } from "./react-dom-BTJzcVJ9.js";
|
|
@@ -8558,7 +8558,7 @@ let __tla = Promise.all([
|
|
|
8558
8558
|
};
|
|
8559
8559
|
}
|
|
8560
8560
|
};
|
|
8561
|
-
var LazyChatbot = import_react.lazy(() => import("./chat-ui-
|
|
8561
|
+
var LazyChatbot = import_react.lazy(() => import("./chat-ui-BxwraqS-.js").then((e) => ({
|
|
8562
8562
|
default: e.Chatbot
|
|
8563
8563
|
}))), messageSchema = array(object({
|
|
8564
8564
|
id: string(),
|
|
@@ -36187,7 +36187,7 @@ ${c}
|
|
|
36187
36187
|
function _temp2$2(e) {
|
|
36188
36188
|
e.target === e.currentTarget && e.key === "Enter" && (e.preventDefault(), e.stopPropagation(), e.currentTarget.click());
|
|
36189
36189
|
}
|
|
36190
|
-
var LazySlidesComponent = import_react.lazy(() => import("./reveal-component-
|
|
36190
|
+
var LazySlidesComponent = import_react.lazy(() => import("./reveal-component-DERGOPiW.js"));
|
|
36191
36191
|
const SlidesLayoutRenderer = ({ layout: e, setLayout: r, cells: c, mode: l }) => {
|
|
36192
36192
|
var _a3;
|
|
36193
36193
|
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 { at as parseHtmlContent, it as ansiToPlainText } from "./html-to-image-
|
|
3
|
+
import { at as parseHtmlContent, it as ansiToPlainText } from "./html-to-image-DXSdAI_Y.js";
|
|
4
4
|
import { u as createLucideIcon } from "./dist-Dk6PV_d3.js";
|
|
5
5
|
import { t as Strings } from "./strings-J57tzLr3.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 { _ as Logger, g as cn, h as Events, l as useEventListener, t as Button } from "./button-C5K9fIPF.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 { lt as kioskModeAtom } from "./html-to-image-
|
|
10
|
-
import "./chunk-5FQGJX7Z-
|
|
9
|
+
import { lt as kioskModeAtom } from "./html-to-image-DXSdAI_Y.js";
|
|
10
|
+
import "./chunk-5FQGJX7Z-BbqSm5gU.js";
|
|
11
11
|
import { u as createLucideIcon } from "./dist-Dk6PV_d3.js";
|
|
12
|
-
import { a as DEFAULT_SLIDE_TYPE, c as Slide, ct as PanelResizeHandle, i as DEFAULT_DECK_TRANSITION, in as Expand, ot as Panel, rn as EyeOff, s as SlideSidebar, sn as Code, st as PanelGroup, t as useNotebookCodeAvailable } from "./code-visibility-
|
|
12
|
+
import { a as DEFAULT_SLIDE_TYPE, c as Slide, ct as PanelResizeHandle, i as DEFAULT_DECK_TRANSITION, in as Expand, ot as Panel, rn as EyeOff, s as SlideSidebar, sn as Code, st as PanelGroup, t as useNotebookCodeAvailable } from "./code-visibility-CzYIDGEy.js";
|
|
13
13
|
import { X as useDebouncedCallback } from "./input-OdWHkobi.js";
|
|
14
14
|
import "./toDate-d8RCRrRd.js";
|
|
15
15
|
import "./react-dom-BTJzcVJ9.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marimo-team/islands",
|
|
3
|
-
"version": "0.23.10-
|
|
3
|
+
"version": "0.23.10-dev44",
|
|
4
4
|
"main": "dist/main.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -141,6 +141,7 @@
|
|
|
141
141
|
"react-vega": "^8.0.0",
|
|
142
142
|
"react-virtuoso": "^4.18.1",
|
|
143
143
|
"reactflow": "^11.11.4",
|
|
144
|
+
"rehype-sanitize": "^6.0.0",
|
|
144
145
|
"remark-gfm": "^4.0.1",
|
|
145
146
|
"reveal.js": "^6.0.0",
|
|
146
147
|
"rpc-anywhere": "^1.7.0",
|
|
@@ -60,8 +60,8 @@ export const renderUIMessage = ({
|
|
|
60
60
|
|
|
61
61
|
switch (part.type) {
|
|
62
62
|
case "text":
|
|
63
|
-
// Streamdown
|
|
64
|
-
//
|
|
63
|
+
// Streamdown strips marimo elements, so render them with our own HTML
|
|
64
|
+
// renderer. Other markdown (incl. mo.md HTML) goes through Streamdown.
|
|
65
65
|
if (part.text.includes("<marimo-")) {
|
|
66
66
|
return (
|
|
67
67
|
<React.Fragment key={index}>
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { MarkdownRenderer } from "../markdown-renderer";
|
|
5
|
+
|
|
6
|
+
describe("MarkdownRenderer", () => {
|
|
7
|
+
// Regression test for https://github.com/marimo-team/marimo/issues/9847
|
|
8
|
+
it("preserves class names on raw HTML (e.g. marimo admonitions)", async () => {
|
|
9
|
+
const { container } = render(
|
|
10
|
+
<MarkdownRenderer
|
|
11
|
+
content={
|
|
12
|
+
'<div class="admonition error">' +
|
|
13
|
+
'<p class="admonition-title">Error</p>' +
|
|
14
|
+
"<p>Nope!</p>" +
|
|
15
|
+
"</div>"
|
|
16
|
+
}
|
|
17
|
+
/>,
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
await waitFor(() => {
|
|
21
|
+
expect(screen.getByText("Nope!")).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const admonition = container.querySelector(".admonition.error");
|
|
25
|
+
expect(admonition).toBeInTheDocument();
|
|
26
|
+
expect(container.querySelector(".admonition-title")).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("strips unsafe tags while keeping class names", async () => {
|
|
30
|
+
const { container } = render(
|
|
31
|
+
<MarkdownRenderer
|
|
32
|
+
content={'<div class="safe"><script>alert(1)</script>hello</div>'}
|
|
33
|
+
/>,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
await waitFor(() => {
|
|
37
|
+
expect(screen.getByText("hello")).toBeInTheDocument();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(container.querySelector(".safe")).toBeInTheDocument();
|
|
41
|
+
expect(container.querySelector("script")).not.toBeInTheDocument();
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -4,7 +4,12 @@ import { EditorView } from "@codemirror/view";
|
|
|
4
4
|
import { useAtomValue } from "jotai";
|
|
5
5
|
import { BetweenHorizontalStartIcon } from "lucide-react";
|
|
6
6
|
import { memo, Suspense, useState } from "react";
|
|
7
|
-
import
|
|
7
|
+
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
|
|
8
|
+
import {
|
|
9
|
+
defaultRehypePlugins,
|
|
10
|
+
Streamdown,
|
|
11
|
+
type StreamdownProps,
|
|
12
|
+
} from "streamdown";
|
|
8
13
|
import { Button, type ButtonProps } from "@/components/ui/button";
|
|
9
14
|
import { maybeAddMarimoImport } from "@/core/cells/add-missing-import";
|
|
10
15
|
import { useCellActions } from "@/core/cells/cells";
|
|
@@ -148,6 +153,23 @@ const CopyButton: React.FC<ButtonProps> = ({ onClick, ...props }) => {
|
|
|
148
153
|
);
|
|
149
154
|
};
|
|
150
155
|
|
|
156
|
+
// Allow `className` on every element; the default GitHub schema strips it,
|
|
157
|
+
// which drops the styling on marimo HTML like `mo.md(...)` admonitions.
|
|
158
|
+
const sanitizeSchema = {
|
|
159
|
+
...defaultSchema,
|
|
160
|
+
attributes: {
|
|
161
|
+
...defaultSchema.attributes,
|
|
162
|
+
"*": [...(defaultSchema.attributes?.["*"] ?? []), "className"],
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Keep Streamdown's other rehype plugins (raw, harden) so scripts and unsafe
|
|
167
|
+
// URLs are still sanitized.
|
|
168
|
+
const REHYPE_PLUGINS: StreamdownProps["rehypePlugins"] = Object.values({
|
|
169
|
+
...defaultRehypePlugins,
|
|
170
|
+
sanitize: [rehypeSanitize, sanitizeSchema],
|
|
171
|
+
});
|
|
172
|
+
|
|
151
173
|
type Components = StreamdownProps["components"];
|
|
152
174
|
|
|
153
175
|
const COMPONENTS: Components = {
|
|
@@ -187,6 +209,7 @@ export const MarkdownRenderer = memo(({ content }: { content: string }) => {
|
|
|
187
209
|
<Streamdown
|
|
188
210
|
components={COMPONENTS}
|
|
189
211
|
plugins={plugins}
|
|
212
|
+
rehypePlugins={REHYPE_PLUGINS}
|
|
190
213
|
className="mo-markdown-renderer"
|
|
191
214
|
>
|
|
192
215
|
{content}
|