@marimo-team/frontend 0.25.2-dev97 → 0.25.2-dev99

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/index.html CHANGED
@@ -66,7 +66,7 @@
66
66
  <marimo-server-token data-token="{{ server_token }}" hidden></marimo-server-token>
67
67
  <!-- /TODO -->
68
68
  <title>{{ title }}</title>
69
- <script type="module" crossorigin src="./assets/index-B5XWyg6n.js"></script>
69
+ <script type="module" crossorigin src="./assets/index-B3pNgH5o.js"></script>
70
70
  <link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-B0Z9INg1.js">
71
71
  <link rel="modulepreload" crossorigin href="./assets/react-CUdNIagt.js">
72
72
  <link rel="modulepreload" crossorigin href="./assets/compiler-runtime-BQ9S8nyo.js">
@@ -241,7 +241,7 @@
241
241
  <link rel="stylesheet" crossorigin href="./assets/cells-0PFKL3SH.css">
242
242
  <link rel="stylesheet" crossorigin href="./assets/markdown-renderer-DjIHoF9Z.css">
243
243
  <link rel="stylesheet" crossorigin href="./assets/JsonOutput-B0GmlMnR.css">
244
- <link rel="stylesheet" crossorigin href="./assets/index-Xxay17E9.css">
244
+ <link rel="stylesheet" crossorigin href="./assets/index-CXX6l_22.css">
245
245
  </head>
246
246
  <body>
247
247
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/frontend",
3
- "version": "0.25.2-dev97",
3
+ "version": "0.25.2-dev99",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -182,7 +182,8 @@
182
182
  "build:islands": "cross-env VITE_MARIMO_ISLANDS=true vite --config islands/vite.config.mts build",
183
183
  "preview:islands": "cross-env VITE_MARIMO_VERSION='0.4.6' VITE_MARIMO_ISLANDS=true vite --config islands/vite.config.mts build",
184
184
  "storybook": "storybook dev -p 6006",
185
- "build-storybook": "storybook build"
185
+ "build-storybook": "storybook build",
186
+ "sync-katex-css": "node --experimental-strip-types scripts/sync-katex-css.ts"
186
187
  },
187
188
  "peerDependencies": {
188
189
  "react": "^17 || ^18 || ^19",
@@ -0,0 +1,166 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { readFile } from "node:fs/promises";
4
+ import postcss from "postcss";
5
+ import { beforeAll, describe, expect, it } from "vitest";
6
+ import {
7
+ checkKatexCss,
8
+ generateKatexCss,
9
+ readUpstreamCss,
10
+ stylesheetPath,
11
+ } from "../../scripts/katex-css";
12
+
13
+ let upstream: string;
14
+ let generated: string;
15
+
16
+ beforeAll(async () => {
17
+ upstream = await readUpstreamCss();
18
+ generated = await generateKatexCss(upstream);
19
+ });
20
+
21
+ describe("copied KaTeX stylesheet", () => {
22
+ it("matches the installed package with only documented marimo overrides", async () => {
23
+ await expect(
24
+ checkKatexCss(upstream, await readFile(stylesheetPath, "utf8")),
25
+ ).resolves.toBeUndefined();
26
+ });
27
+
28
+ it.each(["added", "removed", "changed"])(
29
+ "detects %s upstream rule without a version change",
30
+ async (change) => {
31
+ const root = postcss.parse(upstream);
32
+ if (change === "added") {
33
+ root.append(
34
+ postcss.parse(
35
+ ".katex .reflectbox { display: inline-block; transform: scaleX(-1); }",
36
+ ),
37
+ );
38
+ } else {
39
+ root.walkRules(".katex .mspace", (rule) => {
40
+ if (change === "removed") {
41
+ rule.remove();
42
+ } else {
43
+ rule.append(postcss.decl({ prop: "color", value: "red" }));
44
+ }
45
+ });
46
+ }
47
+ await expect(checkKatexCss(root.toString(), generated)).rejects.toThrow(
48
+ "pnpm sync-katex-css",
49
+ );
50
+ const regenerated = await generateKatexCss(root.toString());
51
+ await checkKatexCss(root.toString(), regenerated);
52
+ },
53
+ );
54
+
55
+ it.each(["added", "removed", "changed"])(
56
+ "detects %s local rule",
57
+ async (change) => {
58
+ const root = postcss.parse(generated);
59
+ if (change === "added") {
60
+ root.append(postcss.parse(".katex .unexpected { display: none; }"));
61
+ } else {
62
+ root.walkRules(".katex .mspace", (rule) => {
63
+ if (change === "removed") {
64
+ rule.remove();
65
+ } else {
66
+ rule.append(postcss.decl({ prop: "color", value: "red" }));
67
+ }
68
+ });
69
+ }
70
+ await expect(checkKatexCss(upstream, root.toString())).rejects.toThrow(
71
+ "pnpm sync-katex-css",
72
+ );
73
+ },
74
+ );
75
+
76
+ it("preserves math sizing, tag alignment, and equation counters without bundling font faces", () => {
77
+ const root = postcss.parse(generated);
78
+ const declarations = (selector: string) => {
79
+ const values: Record<string, string> = {};
80
+ root.walkRules(selector, (rule) => {
81
+ rule.walkDecls((decl) => {
82
+ values[decl.prop] = decl.value;
83
+ });
84
+ });
85
+ return values;
86
+ };
87
+ expect(declarations(".katex")).toEqual({
88
+ "font-family": '"KaTeX_Main", "Times New Roman", serif',
89
+ "font-size": "110%",
90
+ "font-style": "normal",
91
+ "line-height": "inherit",
92
+ position: "relative",
93
+ "text-indent": "0",
94
+ "text-rendering": "auto",
95
+ });
96
+ expect(
97
+ declarations(".katex-display > .katex > .katex-html > .katex-tag"),
98
+ ).toEqual({ position: "absolute", right: "0", "text-align": "right" });
99
+ expect(declarations("body,\n.katex-display")).toEqual({
100
+ "counter-reset": "katexEqnNo mmlEqnNo",
101
+ });
102
+ expect(declarations(".katex .angl")["border-right"]).toBe("0.049em solid");
103
+ const fontFaces: postcss.AtRule[] = [];
104
+ root.walkAtRules("font-face", (rule) => {
105
+ fontFaces.push(rule);
106
+ });
107
+ expect(fontFaces).toEqual([]);
108
+ });
109
+
110
+ it.each(["root", "media"])(
111
+ "preserves SVG rule order and %s scope when upstream adds a competing rule",
112
+ async (scope) => {
113
+ const root = postcss.parse(upstream);
114
+ const svgRules: postcss.Rule[] = [];
115
+ root.walkRules(".katex svg", (rule) => {
116
+ svgRules.push(rule);
117
+ });
118
+ const secondSvg = svgRules[1];
119
+ secondSvg.before(postcss.parse(".katex :is(svg) { stroke-width: 3; }"));
120
+ if (scope === "media") {
121
+ const media = postcss.atRule({ name: "media", params: "print" });
122
+ secondSvg.before(media);
123
+ media.append(secondSvg);
124
+ }
125
+
126
+ const svgCascade = (css: string) => {
127
+ const rules: {
128
+ selector: string;
129
+ scope: string;
130
+ strokeWidth: string | undefined;
131
+ }[] = [];
132
+ postcss.parse(css).walkRules((rule) => {
133
+ if (
134
+ rule.selector !== ".katex svg" &&
135
+ rule.selector !== ".katex :is(svg)"
136
+ ) {
137
+ return;
138
+ }
139
+ let strokeWidth: string | undefined;
140
+ rule.walkDecls("stroke-width", (decl) => {
141
+ strokeWidth = decl.value;
142
+ });
143
+ const parent = rule.parent;
144
+ rules.push({
145
+ selector: rule.selector,
146
+ scope:
147
+ parent?.type === "atrule"
148
+ ? `${parent.name} ${parent.params}`
149
+ : "root",
150
+ strokeWidth,
151
+ });
152
+ });
153
+ return rules;
154
+ };
155
+ const regenerated = await generateKatexCss(root.toString());
156
+ expect(svgCascade(regenerated)).toEqual(svgCascade(root.toString()));
157
+ await checkKatexCss(root.toString(), regenerated);
158
+ },
159
+ );
160
+
161
+ it("requires review when an overridden upstream declaration changes", async () => {
162
+ await expect(
163
+ generateKatexCss(upstream.replace("line-height:1.2", "line-height:1.3")),
164
+ ).rejects.toThrow("review overrides");
165
+ });
166
+ });
@@ -1,23 +1,20 @@
1
1
  /*
2
- * Adapted from https://cdn.jsdelivr.net/npm/katex@0.18.5/dist/katex.min.css
2
+ * Generated from the installed katex/dist/katex.min.css.
3
+ * Run `pnpm sync-katex-css` from the repository root or frontend to regenerate.
4
+ * Do not edit this file directly; update scripts/katex-css.ts for local overrides.
3
5
  *
4
- * KaTeX's class names change between versions (0.18 prefixed most of its
5
- * internal classes with `katex-`), so this file must be regenerated whenever
6
- * the `katex` package is bumped. Steps:
7
- *
8
- * 1. Copy the upstream katex.min.css for the installed version.
9
- * 2. Delete every `@font-face` block. The fonts are declared in katex-fonts.css.
10
- * 3. Keep the upstream `em` values. KaTeX sizes scripts, rules, and spacing
11
- * relative to the surrounding math, which can differ from the root size.
12
- * 4. Run oxfmt to expand the file, then stylelint with --fix. Merge the two
13
- * `.katex svg` rules stylelint flags as duplicates.
14
- * 5. Re-apply the marimo edits below:
15
- * - `.katex`: `font-size: 110%`, `line-height: inherit` and
16
- * `font-style: normal` in place of the upstream `font` shorthand, so math
17
- * follows the surrounding text size and stays upright in blockquotes.
18
- * - `.katex-tag` inside `.katex-display`: `text-align: right`.
19
- * - `counter-reset` on `.katex-display` as well as `body`, so equation
20
- * numbers restart for each display block.
6
+ * Documented marimo overrides:
7
+ * - Remove @font-face: font declarations live in katex-fonts.css.
8
+ * - Preserve em units so scripts, rules, and spacing scale with surrounding math.
9
+ * - Replace .katex's font shorthand with its font-family, font-size: 110%,
10
+ * font-style: normal, and line-height: inherit to follow surrounding text size
11
+ * and keep math upright in blockquotes.
12
+ * - Right-align display equation tags.
13
+ * - Reset equation counters on each display block as well as body.
14
+ * - Preserve duplicate SVG rules in their upstream order and scope, with a
15
+ * targeted stylelint exemption so their cascade remains intact.
16
+ * Formatting and syntax normalization use oxfmt and stylelint; adjacent exact
17
+ * duplicate declarations left by vendor-prefix normalization are removed.
21
18
  */
22
19
 
23
20
  /* stylelint-disable unit-allowed-list -- KaTeX sizes are relative to the math. */
@@ -1002,6 +999,17 @@
1002
999
  .katex svg {
1003
1000
  fill: currentcolor;
1004
1001
  stroke: currentcolor;
1002
+ display: block;
1003
+ height: inherit;
1004
+ position: absolute;
1005
+ width: 100%;
1006
+ }
1007
+
1008
+ .katex svg path {
1009
+ stroke: none;
1010
+ }
1011
+ /* stylelint-disable no-duplicate-selectors -- Preserve the upstream SVG cascade. */
1012
+ .katex svg {
1005
1013
  fill-rule: nonzero;
1006
1014
  fill-opacity: 1;
1007
1015
  stroke-width: 1;
@@ -1011,16 +1019,8 @@
1011
1019
  stroke-dasharray: none;
1012
1020
  stroke-dashoffset: 0;
1013
1021
  stroke-opacity: 1;
1014
- display: block;
1015
- height: inherit;
1016
- position: absolute;
1017
- width: 100%;
1018
1022
  }
1019
-
1020
- .katex svg path {
1021
- stroke: none;
1022
- }
1023
-
1023
+ /* stylelint-enable no-duplicate-selectors */
1024
1024
  .katex img {
1025
1025
  border-style: none;
1026
1026
  max-height: none;