@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/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-BviMxrkm.js";
26
- import { __tla as __tla_1 } from "./chunk-5FQGJX7Z-BNjes6Yx.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-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-BpCUlq4u.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-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-Bq3VMBsg.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-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-CqtCMdc-.js").then((e) => ({
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-x3disQC3.js"));
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,5 +1,5 @@
1
1
  import "./react-DA-nE2FX.js";
2
- import { a as tt } from "./chunk-5FQGJX7Z-BNjes6Yx.js";
2
+ import { o as tt } from "./chunk-5FQGJX7Z-BbqSm5gU.js";
3
3
  import "./jsx-runtime-DebpN0FN.js";
4
4
  export {
5
5
  tt as Mermaid
@@ -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-BviMxrkm.js";
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-BviMxrkm.js";
10
- import "./chunk-5FQGJX7Z-BNjes6Yx.js";
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-BpCUlq4u.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-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-dev43",
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 sanitizes the HTML which strips out marimo elements
64
- // So instead, we render the HTML with our custom renderer.
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 { Streamdown, type StreamdownProps } from "streamdown";
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}