Skip to content

Commit 2648c53

Browse files
committed
feat: add haxiom branding
1 parent c1878b2 commit 2648c53

File tree

6 files changed

+180
-28
lines changed

6 files changed

+180
-28
lines changed

.github/workflows/biome-check.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
- name: Run Clippy
2121
run: cd markdown-renderer && cargo clippy -- -D warnings
2222
- name: Run Tests
23-
run: cd markdown-renderer && cargo test
23+
run: cd markdown-renderer && cargo test --features sanitize
2424
biome:
2525
name: Run BiomeJS
2626
runs-on: ubuntu-latest

example/src/App.tsx

Lines changed: 163 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,61 @@
1-
import { type Component, createSignal, onCleanup, onMount } from "solid-js";
2-
import { MarkdownRenderer } from "solid-markdown-wasm";
1+
import { type Component, createSignal, onCleanup, onMount, For } from "solid-js";
2+
import { MarkdownRenderer, type Themes } from "solid-markdown-wasm";
33
import { MonacoEditor } from "solid-monaco";
44
import initialMarkdown from "../src/assets/markdown_preview.md?raw";
5+
import haxiomLogo from "../src/assets/haxiom.svg";
6+
7+
// All available themes from the Rust lib.rs (matches the Themes type)
8+
const CODE_THEMES: Themes[] = [
9+
"1337",
10+
"OneHalfDark",
11+
"OneHalfLight",
12+
"Tomorrow",
13+
"agola-dark",
14+
"ascetic-white",
15+
"axar",
16+
"ayu-dark",
17+
"ayu-light",
18+
"ayu-mirage",
19+
"base16-atelierdune-light",
20+
"base16-ocean-dark",
21+
"base16-ocean-light",
22+
"bbedit",
23+
"boron",
24+
"charcoal",
25+
"cheerfully-light",
26+
"classic-modified",
27+
"demain",
28+
"dimmed-fluid",
29+
"dracula",
30+
"gray-matter-dark",
31+
"green",
32+
"gruvbox-dark",
33+
"gruvbox-light",
34+
"idle",
35+
"inspired-github",
36+
"ir-white",
37+
"kronuz",
38+
"material-dark",
39+
"material-light",
40+
"monokai",
41+
"nord",
42+
"nyx-bold",
43+
"one-dark",
44+
"railsbase16-green-screen-dark",
45+
"solarized-dark",
46+
"solarized-light",
47+
"subway-madrid",
48+
"subway-moscow",
49+
"two-dark",
50+
"visual-studio-dark",
51+
"zenburn",
52+
];
53+
54+
const EDITOR_THEMES = [
55+
{ value: "vs", label: "Light" },
56+
{ value: "vs-dark", label: "Dark" },
57+
{ value: "hc-black", label: "High Contrast" },
58+
] as const;
559

660
const LoadingFallback = () => (
761
<div class="flex justify-center items-center h-full">
@@ -15,6 +69,12 @@ const App: Component = () => {
1569
const [isDarkMode, setIsDarkMode] = createSignal(
1670
window.matchMedia("(prefers-color-scheme: dark)").matches,
1771
);
72+
const [codeTheme, setCodeTheme] = createSignal<Themes>(
73+
window.matchMedia("(prefers-color-scheme: dark)").matches ? "ayu-dark" : "ayu-light"
74+
);
75+
const [editorTheme, setEditorTheme] = createSignal<string>(
76+
window.matchMedia("(prefers-color-scheme: dark)").matches ? "vs-dark" : "vs"
77+
);
1878
let timeoutId: number | NodeJS.Timeout | undefined;
1979

2080
const debouncedSetMarkdown = (value: string) => {
@@ -49,6 +109,8 @@ const App: Component = () => {
49109
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
50110
const handleChange = (e: MediaQueryListEvent) => {
51111
setIsDarkMode(e.matches);
112+
setCodeTheme(e.matches ? "ayu-dark" : "ayu-light");
113+
setEditorTheme(e.matches ? "vs-dark" : "vs");
52114
};
53115
mediaQuery.addEventListener("change", handleChange);
54116

@@ -67,37 +129,115 @@ const App: Component = () => {
67129
const editorOptions = () => ({
68130
fontFamily: "'Iosevka', monospace",
69131
fontSize: 22,
70-
theme: isDarkMode() ? "vs-dark" : "vs-light",
132+
theme: editorTheme(),
71133
});
72134

135+
const selectClass = () =>
136+
`px-3 py-1.5 rounded border text-sm font-medium cursor-pointer ${
137+
isDarkMode()
138+
? "bg-gray-700 border-gray-600 text-gray-200 hover:bg-gray-600"
139+
: "bg-white border-gray-300 text-gray-700 hover:bg-gray-50"
140+
}`;
141+
73142
return (
74143
<div
75-
class="flex w-screen h-screen"
144+
class="flex flex-col w-screen h-screen"
76145
classList={{ "bg-[#1e1e1e]": isDarkMode(), "bg-white": !isDarkMode() }}
77146
>
78-
<div class="w-1/2 flex flex-col m-4">
79-
<MonacoEditor
80-
language="markdown"
81-
options={editorOptions()}
82-
value={markdown()}
83-
onChange={(val, _ev) => {
84-
handleInput(val);
85-
}}
86-
/>
87-
</div>
147+
{/* Toolbar */}
88148
<div
89-
class="w-1/2 flex flex-col"
90-
classList={{ "bg-[#0d1117]": isDarkMode(), "bg-white": !isDarkMode() }}
149+
class="flex items-center justify-between px-6 py-3 border-b"
150+
classList={{
151+
"bg-black border-gray-700": isDarkMode(),
152+
"bg-gray-100 border-gray-200": !isDarkMode(),
153+
}}
91154
>
92-
<div class="m-0 h-full shadow-sm overflow-y-auto p-4 px-6">
93-
<MarkdownRenderer
94-
markdown={debouncedMarkdown()}
95-
theme={isDarkMode() ? "ayu-dark" : "ayu-light"}
96-
class="markdown-body"
97-
fallback={<LoadingFallback />}
98-
onLoaded={() => console.log("WASM Loaded")}
155+
{/* Left side - Logo and project name */}
156+
<div class="flex items-center gap-3">
157+
<img src={haxiomLogo} alt="Haxiom" class="h-6 w-6" classList={{ "invert": isDarkMode() }} />
158+
<span class="font-semibold" classList={{ "text-white": isDarkMode(), "text-gray-900": !isDarkMode() }}>
159+
Haxiom
160+
</span>
161+
<span classList={{ "text-gray-500": isDarkMode(), "text-gray-400": !isDarkMode() }}>/</span>
162+
<span classList={{ "text-gray-400": isDarkMode(), "text-gray-500": !isDarkMode() }}>solid-markdown-wasm</span>
163+
</div>
164+
165+
{/* Right side - Theme selectors and Try Haxiom */}
166+
<div class="flex items-center gap-6">
167+
<div class="flex items-center gap-2">
168+
{/* biome-ignore lint/a11y/noLabelWithoutControl: <explanation> */}
169+
<label
170+
class="text-sm font-medium"
171+
classList={{ "text-gray-300": isDarkMode(), "text-gray-700": !isDarkMode() }}
172+
>
173+
Editor Theme:
174+
</label>
175+
<select
176+
class={selectClass()}
177+
value={editorTheme()}
178+
onChange={(e) => setEditorTheme(e.currentTarget.value)}
179+
>
180+
<For each={EDITOR_THEMES}>
181+
{(theme) => <option value={theme.value}>{theme.label}</option>}
182+
</For>
183+
</select>
184+
</div>
185+
186+
<div class="flex items-center gap-2">
187+
{/* biome-ignore lint/a11y/noLabelWithoutControl: <explanation> */}
188+
<label
189+
class="text-sm font-medium"
190+
classList={{ "text-gray-300": isDarkMode(), "text-gray-700": !isDarkMode() }}
191+
>
192+
Code Block Theme:
193+
</label>
194+
<select
195+
class={selectClass()}
196+
value={codeTheme()}
197+
onChange={(e) => setCodeTheme(e.currentTarget.value as Themes)}
198+
>
199+
<For each={CODE_THEMES}>{(theme) => <option value={theme}>{theme}</option>}</For>
200+
</select>
201+
</div>
202+
{/* Try Haxiom link */}
203+
<a
204+
href="https://haxiom.io"
205+
target="_blank"
206+
rel="noopener noreferrer"
207+
class="text-sm font-medium px-3 py-1.5 rounded transition-colors text-black hover:opacity-80"
208+
style={{ "background-color": "#6fffe9" }}
209+
>
210+
Try Haxiom
211+
</a>
212+
</div>
213+
</div>
214+
215+
{/* Main content */}
216+
<div class="flex flex-1 overflow-hidden">
217+
<div class="w-1/2 flex flex-col m-4">
218+
<MonacoEditor
219+
language="markdown"
220+
options={editorOptions()}
221+
value={markdown()}
222+
onChange={(val, _ev) => {
223+
handleInput(val);
224+
}}
99225
/>
100226
</div>
227+
<div
228+
class="w-1/2 flex flex-col"
229+
classList={{ "bg-[#0d1117]": isDarkMode(), "bg-white": !isDarkMode() }}
230+
>
231+
<div class="m-0 h-full shadow-sm overflow-y-auto p-4 px-6">
232+
<MarkdownRenderer
233+
markdown={debouncedMarkdown()}
234+
theme={codeTheme()}
235+
class="markdown-body"
236+
fallback={<LoadingFallback />}
237+
onLoaded={() => console.log("WASM Loaded")}
238+
/>
239+
</div>
240+
</div>
101241
</div>
102242
</div>
103243
);

example/src/assets/haxiom.svg

Lines changed: 1 addition & 0 deletions
Loading

markdown-renderer/src/syntect_plugin.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@ impl HighlightStrategy for Cached {
9999
}
100100

101101
LRU.with_borrow_mut(|lru| {
102-
let key = Box::<str>::from(code);
102+
// Include theme and syntax name in cache key to avoid returning
103+
// cached results from different themes or syntaxes
104+
let theme_str = theme.map(|s| s.as_str()).unwrap_or("");
105+
let key = Box::<str>::from(format!("{}:{}:{}", theme_str, syntax.name, code));
103106

104107
if let Some(html) = lru.get(&key) {
105108
return Ok(html.clone());

src/components/MarkdownRenderer.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
} from "solid-js";
1212
import { render } from "solid-js/web";
1313

14+
export type { Themes } from "markdown-renderer";
15+
1416
// Icon size constant
1517
const ICON_SIZE = 16;
1618

@@ -97,7 +99,6 @@ export interface MarkdownRendererProps {
9799
}
98100

99101
export const MarkdownRenderer: Component<MarkdownRendererProps> = (props) => {
100-
const { theme = "one-dark" } = props;
101102
const [renderedHtml, setRenderedHtml] = createSignal("");
102103
const [loadingWasm, setLoadingWasm] = createSignal(true);
103104
const [error, setError] = createSignal<string | null>(null);
@@ -308,9 +309,15 @@ export const MarkdownRenderer: Component<MarkdownRendererProps> = (props) => {
308309
});
309310

310311
createEffect(() => {
311-
if (!loadingWasm()) {
312+
// Track reactive dependencies at top level
313+
const markdown = props.markdown;
314+
const theme = props.theme ?? "one-dark";
315+
const isLoading = loadingWasm();
316+
317+
if (!isLoading) {
312318
const startTime = performance.now();
313-
const result = render_md(props.markdown, theme);
319+
console.log("[MarkdownRenderer] Rendering with theme:", theme);
320+
const result = render_md(markdown, theme);
314321

315322
// Extract iframes and replace with placeholders
316323
const { html, iframes } = extractIframes(result);

src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export {
22
MarkdownRenderer,
33
type MarkdownRendererProps,
4+
type Themes,
45
} from "./MarkdownRenderer";

0 commit comments

Comments
 (0)