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" ;
33import { MonacoEditor } from "solid-monaco" ;
44import 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
660const 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 ) ;
0 commit comments