Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1d3e040
feat(typst): add @prosemark/typst with typst.ts math widgets
cursoragent Apr 12, 2026
9075634
demo: use @prosemark/typst with Vite-bundled WASM
cursoragent Apr 12, 2026
914c357
feat(typst): ship WASM in dist/wasm and default to import.meta.url
cursoragent Apr 12, 2026
2623e71
refactor(typst): default WASM URLs via typst-ts-* imports (?url)
cursoragent Apr 12, 2026
fda74c7
fix(typst): strip SVG selection overlays breaking inline math
cursoragent May 30, 2026
38892c8
fix(typst): use display inline on SVG fragments for math layout
cursoragent May 30, 2026
ccbfaa4
fix(typst): set display inline on SVG nodes in JS, support fragments
cursoragent May 30, 2026
32160c8
fix(typst): export widget class; switch debug harness to typst
cursoragent May 30, 2026
bd77cba
docs: add /demo/typst page for @prosemark/typst testing
cursoragent May 30, 2026
de20f23
docs: add @components import alias for src/components
cursoragent May 30, 2026
a5754b3
fix(typst): default WASM URLs to jsDelivr, avoid bundling 28MB file
cursoragent May 30, 2026
f49fb05
fix(docs): use global scope for shared demo styles
cursoragent May 30, 2026
59a7b9d
fix(typst): rewrite SVG ink fills to currentColor
cursoragent May 30, 2026
d7dd9e1
docs: focus typst demo on math; link from main demo
cursoragent May 30, 2026
a8c023e
Align inline Typst math with text baseline via SVG offset
cursoragent May 30, 2026
08aa2f2
Move Typst demo intro into typstInitDoc.md
cursoragent May 30, 2026
98db29b
Fix typst baseline helpers for noUncheckedIndexedAccess
cursoragent May 30, 2026
f1d9aa3
Expand inline typst SVG viewBox to ink bounds
cursoragent May 30, 2026
bfd11c1
Scale display typst math SVGs to em-based height
cursoragent May 30, 2026
a87c0fe
Fix MathJax inline layout vs Starlight; reduce typst math scale
cursoragent May 30, 2026
d57cd3b
Inject editor ink color into typst math documents
cursoragent May 31, 2026
9bcda8a
Fix typst math ink color race and stroke fallback
cursoragent May 31, 2026
7723eb4
Rename ink debug attr to data-typst-ink-fill
cursoragent May 31, 2026
0d40108
Update typst-math-ink-color changeset
cursoragent May 31, 2026
6437d74
Fix typst math errors to match LaTeX extension styling
cursoragent May 31, 2026
aff6f2b
Ensure typst math errors render visibly like LaTeX
cursoragent May 31, 2026
d08b13e
Show typst math error messages inline like LaTeX extension
cursoragent May 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-prosemark-typst.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@prosemark/typst': minor
---

Add **`@prosemark/typst`**: CodeMirror widgets that render dollar-delimited **`Math`** nodes with [typst.ts](https://github.com/Myriad-Dreamin/typst.ts) (WASM compiler/renderer), plus delimiter/formula syntax theme and an LRU SVG cache. Default WASM URLs load from **jsDelivr** (pinned typst.ts version). Re-export math markdown syntax as **`typstMath*`** for consumers who only install this package.
5 changes: 5 additions & 0 deletions .changeset/latex-inline-math-starlight-css.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@prosemark/latex': patch
---

Fix inline MathJax SVG layout when doc themes (e.g. Starlight) set `.sl-markdown-content svg { display: block; height: auto }` by overriding display and height on inline math widgets.
5 changes: 5 additions & 0 deletions .changeset/typst-inline-math-baseline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@prosemark/typst': patch
---

Align inline Typst math with surrounding text by removing extra typst y-inset, using `vertical-align: baseline` on widgets, and applying a per-SVG em offset derived from typst glyph transforms (with ink-bbox fallback for fractions). Expand typst viewBoxes to ink bounds so subscripts and fractions do not clip or overlap the next line. Scale typst SVG height from viewBox pt using a 12pt-per-em ratio (typst body size). Scale display math the same way.
5 changes: 5 additions & 0 deletions .changeset/typst-inline-math-layout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@prosemark/typst': patch
---

Fix inline and block math layout by setting `display: inline` on typst SVG output (including multiple sibling fragments) via inline styles and widget CSS, so fragments do not stack as block-level boxes. Rewrite typst `#000` fills to `currentColor` so math inherits editor text color.
5 changes: 5 additions & 0 deletions .changeset/typst-jsdelivr-wasm-default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@prosemark/typst': patch
---

Default Typst WASM URLs to jsDelivr (pinned version) instead of bundler `?url` imports, so static hosts (e.g. Cloudflare Pages) are not required to ship the ~28 MB compiler WASM. Export `jsdelivrTypstWasmUrls` and `TYPST_TS_VERSION`; override with `compilerWasmUrl` / `rendererWasmUrl` to self-host or bundle.
5 changes: 5 additions & 0 deletions .changeset/typst-math-error-reporting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@prosemark/typst': patch
---

Show typst math render errors inline as readable messages (matching the LaTeX extension), not the raw formula source.
5 changes: 5 additions & 0 deletions .changeset/typst-math-ink-color.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@prosemark/typst': patch
---

Inject the editor foreground color into typst math documents (`#show math` / `#show math.equation: set text(fill: …)`), resolve color at widget render time (not the default black field value), and fall back to rewriting black SVG fill/stroke to `currentColor`. Expose `data-typst-ink-fill` and `data-typst-widget-version` on widget roots for deploy/cache verification.
2 changes: 1 addition & 1 deletion apps/debug-vsc-extn-in-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@codemirror/view": "^6.39.4",
"@lezer/markdown": "^1.6.1",
"@prosemark/core": "workspace:*",
"@prosemark/latex": "workspace:*",
"@prosemark/typst": "workspace:*",
"@prosemark/paste-rich-text": "workspace:*",
"@prosemark/render-html": "workspace:*",
"@prosemark/spellcheck-frontend": "workspace:*",
Expand Down
6 changes: 3 additions & 3 deletions apps/debug-vsc-extn-in-web/src/initDoc.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ function recieveMessage() {
}
```

## LaTeX math (layout check)
## Typst math (layout check)

Inline $a^2 + b^2 = c^2$ and a display block:
Inline $e^(i pi) + 1 = 0$ and a display block:

$$
\sum_{n=1}^{\infty} \frac{1}{n^2} = \frac{\pi^2}{6}
sum_(n=1)^oo 1/n^2 = pi^2/6
$$
23 changes: 15 additions & 8 deletions apps/debug-vsc-extn-in-web/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import {
renderHtmlMarkdownSyntaxExtensions,
} from '@prosemark/render-html';
import {
latexMarkdownEditorExtensions,
latexMarkdownSyntaxTheme,
} from '@prosemark/latex';
typstMarkdownEditorExtensions,
typstMarkdownSyntaxTheme,
} from '@prosemark/typst';
import { GFM } from '@lezer/markdown';
import {
pastePlainTextExtension,
Expand All @@ -41,7 +41,10 @@ if (!(logOutput instanceof HTMLPreElement)) {
const log = (message: string) => {
const stamp = new Date().toISOString().slice(11, 23);
const line = `[${stamp}] ${message}`;
logOutput.textContent = `${line}\n${logOutput.textContent ?? ''}`.slice(0, 12000);
logOutput.textContent = `${line}\n${logOutput.textContent ?? ''}`.slice(
0,
12000,
);
console.log(line);
};

Expand All @@ -64,8 +67,8 @@ const editor = new EditorView({
}),
prosemarkBasicSetup(),
prosemarkLightThemeSetup(),
...latexMarkdownSyntaxTheme,
...latexMarkdownEditorExtensions(),
...typstMarkdownSyntaxTheme,
...typstMarkdownEditorExtensions(),
htmlBlockExtension,
pasteRichTextExtension(),
pastePlainTextExtension(),
Expand Down Expand Up @@ -224,15 +227,19 @@ requireButton('clear-log').addEventListener('click', () => {
window.addEventListener('error', (event) => {
log(
`window.error: ${event.message || 'Unknown error'}${
event.error instanceof Error ? ` :: ${event.error.stack ?? event.error.message}` : ''
event.error instanceof Error
? ` :: ${event.error.stack ?? event.error.message}`
: ''
}`,
);
});

window.addEventListener('unhandledrejection', (event) => {
log(
`unhandledrejection: ${
event.reason instanceof Error ? event.reason.stack ?? event.reason.message : String(event.reason)
event.reason instanceof Error
? (event.reason.stack ?? event.reason.message)
: String(event.reason)
}`,
);
});
Expand Down
2 changes: 1 addition & 1 deletion apps/demo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"@lezer-unofficial/printer": "^1.0.1",
"@lezer/markdown": "^1.5.1",
"@prosemark/core": "workspace:*",
"@prosemark/latex": "workspace:*",
"@prosemark/typst": "workspace:*",
"@prosemark/paste-rich-text": "workspace:*",
"@prosemark/render-html": "workspace:*",
"@prosemark/spellcheck-frontend": "workspace:*",
Expand Down
6 changes: 3 additions & 3 deletions apps/demo/src/initDoc.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,14 @@ export default function MyComponent() {
Some centered text
</div>

### LaTeX math
### Typst math

Inline: Euler's identity $e^{i\pi} + 1 = 0$.
Inline: Euler's identity $e^(i pi) + 1 = 0$ (Typst math syntax inside `$…$`).

Display:

$$
\int_{-\infty}^{\infty} e^{-x^2}\,dx = \sqrt{\pi}
integral_(-infinity)^infinity e^(-x^2) dif x = sqrt(pi)
$$

## Credits
Expand Down
10 changes: 5 additions & 5 deletions apps/demo/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import {
renderHtmlMarkdownSyntaxExtensions,
} from '@prosemark/render-html';
import {
latexMarkdownEditorExtensions,
latexMarkdownSyntaxTheme,
} from '@prosemark/latex';
typstMarkdownEditorExtensions,
typstMarkdownSyntaxTheme,
} from '@prosemark/typst';
import { indentWithTab } from '@codemirror/commands';
import { GFM } from '@lezer/markdown';
import { indentUnit, syntaxTree } from '@codemirror/language';
Expand Down Expand Up @@ -47,8 +47,8 @@ const editor = new EditorView({
}),
prosemarkBasicSetup(),
prosemarkLightThemeSetup(),
...latexMarkdownSyntaxTheme,
...latexMarkdownEditorExtensions(),
...typstMarkdownSyntaxTheme,
...typstMarkdownEditorExtensions(),
htmlBlockExtension,
pasteRichTextExtension(),
pastePlainTextExtension(),
Expand Down
7 changes: 7 additions & 0 deletions apps/docs/astro.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
import { fileURLToPath } from 'node:url';
import { createStarlightTypeDocPlugin } from 'starlight-typedoc';

const buildTypeDocEntry = (
Expand Down Expand Up @@ -47,6 +48,7 @@ const spellcheckFrontend = buildTypeDocEntry(
'../../packages/spellcheck-frontend',
);
const latex = buildTypeDocEntry('@prosemark/latex', '../../packages/latex');
const typst = buildTypeDocEntry('@prosemark/typst', '../../packages/typst');

// https://astro.build/config
export default defineConfig({
Expand All @@ -57,6 +59,9 @@ export default defineConfig({
},
resolve: {
alias: {
'@components': fileURLToPath(
new URL('./src/components', import.meta.url),
),
buffer: 'buffer',
},
},
Expand Down Expand Up @@ -88,6 +93,7 @@ export default defineConfig({
pasteRichText.sidebarGroup,
spellcheckFrontend.sidebarGroup,
latex.sidebarGroup,
typst.sidebarGroup,
],
},
],
Expand All @@ -97,6 +103,7 @@ export default defineConfig({
pasteRichText.plugin,
spellcheckFrontend.plugin,
latex.plugin,
typst.plugin,
],

editLink: {
Expand Down
1 change: 1 addition & 0 deletions apps/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@lezer/markdown": "^1.5.1",
"@prosemark/core": "workspace:*",
"@prosemark/latex": "workspace:*",
"@prosemark/typst": "workspace:*",
"@prosemark/paste-rich-text": "workspace:*",
"@prosemark/render-html": "workspace:*",
"@prosemark/spellcheck-frontend": "workspace:*",
Expand Down
104 changes: 2 additions & 102 deletions apps/docs/src/components/ProseMarkDemo.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---

import ProseMarkDemoSharedStyles from './ProseMarkDemoSharedStyles.astro';
---

<div id="app">
Expand Down Expand Up @@ -71,104 +71,4 @@
});
</script>

<style>
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;

font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;

--pm-header-mark-color: oklch(82.8% 0.111 230.318);
--pm-link-color: oklch(58.8% 0.158 241.966);
--pm-muted-color: oklch(37.2% 0.044 257.287);
--pm-code-background-color: oklch(92.9% 0.013 255.508);
--pm-code-font:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
--pm-code-btn-background-color: oklch(86.9% 0.022 252.894);
--pm-code-btn-hover-background-color: oklch(70.4% 0.04 256.788);
--pm-blockquote-vertical-line-background-color: oklch(70.4% 0.04 256.788);

--pm-syntax-link: oklch(62.75% 0.188 259.38);
--pm-syntax-keyword: oklch(58.13% 0.248 297.57);
--pm-syntax-atom: oklch(51.29% 0.219 260.63);
--pm-syntax-literal: oklch(57.38% 0.111 170.31);
--pm-syntax-string: oklch(54.86% 0.184 25.53);
--pm-syntax-regexp: oklch(65.88% 0.184 43.8);
--pm-syntax-definition-variable: oklch(45.32% 0.171 260.3);
--pm-syntax-local-variable: oklch(64.13% 0.09 184.42);
--pm-syntax-type-namespace: oklch(49.1% 0.091 165.52);
--pm-syntax-class-name: oklch(64.42% 0.11 168.83);
--pm-syntax-special-variable-macro: oklch(52.58% 0.212 282.71);
--pm-syntax-definition-property: oklch(42.1% 0.142 260.08);
--pm-syntax-comment: oklch(62.79% 0.022 252.89);
--pm-syntax-invalid: oklch(64.62% 0.203 29.2);
--pm-cursor-color: black;

--pm-spellcheck-tooltip-background: oklch(100% 0 0);
--pm-spellcheck-tooltip-border: oklch(82% 0.005 264);
--pm-spellcheck-tooltip-text: oklch(30% 0.02 264);
--pm-spellcheck-tooltip-error: oklch(55% 0.22 29);
--pm-spellcheck-tooltip-hover: oklch(96% 0.005 264);
--pm-spellcheck-tooltip-actions-border: oklch(82% 0.005 264);
--pm-spellcheck-tooltip-font-size: 0.9rem;
}

:root[data-theme='dark'] {
--pm-header-mark-color: oklch(44.3% 0.11 240.79);
--pm-link-color: oklch(58.8% 0.158 241.966);
--pm-muted-color: oklch(55.4% 0.046 257.417);
--pm-code-background-color: oklch(27.9% 0.041 260.031);
--pm-code-font:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
--pm-code-btn-background-color: oklch(37.2% 0.044 257.287);
--pm-code-btn-hover-background-color: oklch(44.6% 0.043 257.281);
--pm-blockquote-vertical-line-background-color: oklch(44.6% 0.043 257.281);

--pm-syntax-link: oklch(73.24% 0.17 258.63);
--pm-syntax-keyword: oklch(70.05% 0.217 296.83);
--pm-syntax-atom: oklch(65.69% 0.2 259.93);
--pm-syntax-literal: oklch(71.27% 0.101 169.93);
--pm-syntax-string: oklch(68.53% 0.164 25.1);
--pm-syntax-regexp: oklch(76.88% 0.16 43.42);
--pm-syntax-definition-variable: oklch(61.42% 0.158 259.6);
--pm-syntax-local-variable: oklch(75.88% 0.082 184.11);
--pm-syntax-type-namespace: oklch(64.51% 0.083 165.19);
--pm-syntax-class-name: oklch(76.14% 0.1 168.52);
--pm-syntax-special-variable-macro: oklch(66.67% 0.193 282.06);
--pm-syntax-definition-property: oklch(58.92% 0.132 259.4);
--pm-syntax-comment: oklch(74.9% 0.02 252.89);
--pm-syntax-invalid: oklch(75.93% 0.182 28.91);
--pm-cursor-color: white;

--pm-spellcheck-tooltip-background: oklch(25% 0.02 264);
--pm-spellcheck-tooltip-border: oklch(40% 0.02 264);
--pm-spellcheck-tooltip-text: oklch(75% 0.02 264);
--pm-spellcheck-tooltip-error: oklch(65% 0.22 29);
--pm-spellcheck-tooltip-hover: oklch(35% 0.02 264);
--pm-spellcheck-tooltip-actions-border: oklch(40% 0.02 264);
}

#app {
width: 800px;
margin: 0 auto;
padding: 2rem;
}

#codemirror-container {
min-height: 100px;
width: 100%;
/* Inter + tailwindcss font-sans*/
--font:
Inter, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
* {
margin-top: 0px;
}
}
</style>
<ProseMarkDemoSharedStyles />
Loading