My personal website, in gleam+lustre!
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add code block high lighting!


Signed-off-by: MLC Bloeiman <mar@strawmelonjuice.com>

+192 -11
+1
gleam.toml
··· 9 9 chilp = ">= 1.0.0 and < 2.0.0" 10 10 gleam_time = ">= 1.7.0 and < 2.0.0" 11 11 webls = ">= 1.6.1 and < 2.0.0" 12 + smalto = ">= 3.0.0 and < 4.0.0" 12 13 13 14 [dev-dependencies] 14 15 glentities = ">= 6.2.1 and < 7.0.0"
+2
manifest.toml
··· 42 42 { name = "polly", version = "3.1.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_erlang", "gleam_otp", "gleam_stdlib", "simplifile"], otp_app = "polly", source = "hex", outer_checksum = "51FB565D81FF6212FDF3306D44419601F2A7C4EDD1F00FC9DA5C376A00AED4FE" }, 43 43 { name = "rsvp", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_fetch", "gleam_http", "gleam_httpc", "gleam_javascript", "gleam_json", "gleam_stdlib", "lustre"], otp_app = "rsvp", source = "hex", outer_checksum = "40F9E0E662FF258E10C7041A9591261FE802D56625FB444B91510969644F7722" }, 44 44 { name = "simplifile", version = "2.4.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "7C18AFA4FED0B4CE1FA5B0B4BAC1FA1744427054EA993565F6F3F82E5453170D" }, 45 + { name = "smalto", version = "3.0.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib", "houdini"], otp_app = "smalto", source = "hex", outer_checksum = "13EA8935A9E60A770E326FB850F5A5B3612A62E0A6C62C3F850AD04C842E3769" }, 45 46 { name = "snag", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "snag", source = "hex", outer_checksum = "274F41D6C3ECF99F7686FDCE54183333E41D2C1CA5A3A673F9A8B2C7A4401077" }, 46 47 { name = "splitter", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "splitter", source = "hex", outer_checksum = "3DFD6B6C49E61EDAF6F7B27A42054A17CFF6CA2135FF553D0CB61C234D281DD0" }, 47 48 { name = "telemetry", version = "1.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "telemetry", source = "hex", outer_checksum = "2172E05A27531D3D31DD9782841065C50DD5C3C7699D95266B2EDD54C2DAFA1C" }, ··· 63 64 lustre_dev_tools = { version = ">= 2.3.4 and < 3.0.0" } 64 65 modem = { version = ">= 2.1.2 and < 3.0.0" } 65 66 simplifile = { version = ">= 2.3.2 and < 3.0.0" } 67 + smalto = { version = ">= 3.0.0 and < 4.0.0" } 66 68 webls = { version = ">= 1.6.1 and < 2.0.0" }
+189 -11
src/homepage/djotparse.gleam
··· 1 1 //// Stolen from my famous: 2 2 //// https://forge.strawmelonjuice.com/CynthiaWebsiteEngine/Mini/src/branch/2.0.0/cynthia_websites_mini_server/src/cynthia_websites_mini_server/utils/djotparse.gleam 3 3 4 + import gleam/io 4 5 import gleam/list 6 + import gleam/option.{type Option, None, Some} 5 7 import gleam/string 6 8 import jot 9 + import smalto 10 + import smalto/grammar.{type Grammar} 11 + import smalto/languages/csharp 12 + import smalto/languages/css 13 + import smalto/languages/gleam 14 + import smalto/languages/php 15 + import smalto/languages/rust 7 16 8 17 pub fn djot_to_html(djot djot: String) -> String { 9 18 djot 19 + |> preprocess_codeblocks 10 20 |> preprocess_tables 11 21 |> jot.to_html 12 - |> string.replace("<p", "<p class=\"mt-8\"") 22 + |> string.replace("<p>", "<p class=\"mt-8\">") 13 23 |> string.replace("<li", "<li class=\"mt-4\"") 14 24 |> string.replace("<h1", "<h2 class=\"text-3xl font-light mt-14\"") 15 25 |> string.replace("<h2", "<h3 class=\"text-2xl font-light mt-14\"") ··· 23 33 24 34 pub fn preprocess_tables(djot: String) -> String { 25 35 let lines = string.split(djot, on: "\n") 26 - process_lines(lines, [], False, False, False) 36 + process_table_lines(lines, [], False, False, False) 27 37 } 28 38 29 - fn process_lines( 39 + fn process_table_lines( 30 40 lines: List(String), 31 41 acc: List(String), 32 42 in_table: Bool, ··· 57 67 case is_header, in_table, is_table_line { 58 68 // Header is above 59 69 True, True, True -> { 60 - process_lines(rest, ["<tbody>", ..acc], True, True, True) 70 + process_table_lines(rest, ["<tbody>", ..acc], True, True, True) 61 71 } 62 72 // Table starts 63 73 _, False, True -> { 64 - process_lines( 74 + process_table_lines( 65 75 rest, 66 76 [ 67 77 "\n``` =html\n" 68 78 <> tstart 69 79 <> "\n" 70 - <> line_to_row(line, next_is_header), 80 + <> line_to_table_row(line, next_is_header), 71 81 ..acc 72 82 ], 73 83 True, ··· 78 88 79 89 // Table continues 80 90 False, True, True -> 81 - process_lines( 91 + process_table_lines( 82 92 rest, 83 - [line_to_row(line, next_is_header), ..acc], 93 + [line_to_table_row(line, next_is_header), ..acc], 84 94 True, 85 95 False, 86 96 tbody_opened, ··· 94 104 list.map(acc, string.replace(_, tstart, tstart <> "<tbody>")) 95 105 True -> acc 96 106 } 97 - process_lines( 107 + process_table_lines( 98 108 rest, 99 109 [line, "</tbody></table>\n```\n", ..acc], 100 110 False, ··· 105 115 106 116 // Normal text 107 117 _, False, False -> 108 - process_lines(rest, [line, ..acc], False, False, False) 118 + process_table_lines(rest, [line, ..acc], False, False, False) 109 119 } 110 120 } 111 121 } 112 122 } 113 123 114 - fn line_to_row(line: String, as_header: Bool) -> String { 124 + fn line_to_table_row(line: String, as_header: Bool) -> String { 115 125 let cells = 116 126 line 117 127 |> string.trim ··· 143 153 <> "</tr>" 144 154 } 145 155 } 156 + 157 + fn preprocess_codeblocks(djot: String) -> String { 158 + let lines = string.split(djot, on: "\n") 159 + process_codeblock_lines(lines, None, "") 160 + } 161 + 162 + fn process_codeblock_lines( 163 + ahead: List(String), 164 + collection: option.Option(#(Grammar, String)), 165 + processed: String, 166 + ) { 167 + case ahead { 168 + // End of the Djot 169 + [] -> { 170 + case collection { 171 + // Uh-oh! This shouldn't happen, but ending the code block here is fine. This Djot should probably be corrected later though! 172 + Some(#(grammar, collected)) -> { 173 + processed <> process_codeblock(collected, grammar) 174 + } 175 + None -> { 176 + processed 177 + } 178 + } 179 + } 180 + // A raw block! 181 + ["``` =" <> x, ..rest] -> { 182 + case collection { 183 + None -> 184 + process_codeblock_lines( 185 + rest, 186 + // Don't start collecting. 187 + None, 188 + // Just add it to the output, Jot will parse this as usual. 189 + processed <> "\n``` =" <> x, 190 + ) 191 + //... A raw block inside a code block! 192 + Some(#(grammar, collected)) -> 193 + process_codeblock_lines( 194 + rest, 195 + // Collect it! 196 + Some(#(grammar, collected <> "\n" <> "``` =" <> x)), 197 + // No need to add anything to the output. 198 + processed, 199 + ) 200 + } 201 + } 202 + ["```", ..rest] -> { 203 + // End of code block or undescribed code block 204 + case collection { 205 + // End of code block 206 + Some(#(grammar, collected)) -> { 207 + process_codeblock_lines( 208 + rest, 209 + // We stop collecting 210 + None, 211 + // And process the last collection into a HTML <code> block. 212 + processed <> process_codeblock(collected, grammar), 213 + ) 214 + } 215 + // Undescribed code block 216 + None -> { 217 + process_codeblock_lines( 218 + rest, 219 + // Don't start collecting. 220 + None, 221 + // Just add it to the output, Jot will parse this as usual. 222 + processed <> "\n```", 223 + ) 224 + } 225 + } 226 + } 227 + // Start of a code block! 228 + ["```" <> t, ..rest] -> { 229 + case collection { 230 + // Maybe this is an MD/Djot codeblock... We just collect this start as normal code 231 + Some(collected) -> { 232 + process_codeblock_lines( 233 + rest, 234 + Some(#(collected.0, collected.1 <> "\n```")), 235 + processed, 236 + ) 237 + } 238 + // Okay, it's definitely a start! 239 + None -> { 240 + case find_codeblock_grammar(t) { 241 + // We don't know this grammar :C can't collect grammar we don't know... treating it like an undescribed code block 242 + None -> 243 + process_codeblock_lines( 244 + rest, 245 + // Don't start collecting. 246 + None, 247 + // Just add it to the output, Jot will parse this as usual. 248 + processed <> "\n```", 249 + ) 250 + // We can start collecting! 251 + Some(grammar) -> { 252 + process_codeblock_lines( 253 + rest, 254 + // Start collecting. 255 + Some(#(grammar, "")), 256 + // No need to add anything to the output. 257 + processed, 258 + ) 259 + } 260 + } 261 + } 262 + } 263 + } 264 + // Either normal code or normal Djot! 265 + [text, ..rest] -> { 266 + case collection { 267 + None -> 268 + process_codeblock_lines( 269 + rest, 270 + // Don't start collecting. 271 + None, 272 + // Just add it to the output, Jot will parse this as usual. 273 + processed <> "\n" <> text, 274 + ) 275 + Some(#(grammar, collected)) -> 276 + process_codeblock_lines( 277 + rest, 278 + // Collect it! 279 + Some(#(grammar, collected <> "\n" <> text)), 280 + // No need to add anything to the output. 281 + processed, 282 + ) 283 + } 284 + } 285 + } 286 + } 287 + 288 + fn process_codeblock(collected: String, grammar: Grammar) -> String { 289 + "\n\n``` =html\n<pre class=\"bg-slate-500 rounded-sm text-primary p-1 m-0\"><code class=\"codeblock\">" 290 + <> "<span class=\"text-sm float-right\">" 291 + <> grammar.name 292 + |> string.capitalise() 293 + <> "</span>" 294 + <> smalto.to_html(collected, grammar) 295 + |> string.replace("smalto-function", "text-[#9ce7ff] p-0 m-0") 296 + |> string.replace("smalto-punctuation", "p-0 m-0") 297 + |> string.replace("smalto-keyword", "text-[#ffd5996] p-0 m-0") 298 + |> string.replace("smalto-string", "text-[#c8ffa7] p-0 m-0") 299 + |> string.replace("smalto-number", "text-[#c8ffa7] p-0 m-0") 300 + |> string.replace("smalto-comment", "italic text-[#d4d4d4] p-0 m-0") 301 + |> string.replace("smalto-operator", "text-[#ffaff3] p-0 m-0") 302 + |> string.replace("smalto-type", "text-[#ffddfa] p-0 m-0") 303 + |> string.replace("smalto-module", "text-[#ffddfa] p-0 m-0") 304 + |> string.replace("smalto-tag", "text-[#ff6b6b] p-0 m-0") 305 + |> string.replace("smalto-attribute", "text-[#ffd596] p-0 m-0") 306 + |> string.replace("smalto-selector", "text-[#9ce7ff] p-0 m-0") 307 + |> string.replace("smalto-property", "text-[#ffd596] p-0 m-0") 308 + <> "\n</code></pre>\n\n```" 309 + } 310 + 311 + fn find_codeblock_grammar(t: String) -> Option(Grammar) { 312 + case t { 313 + "cs" | "csharp" -> Some(csharp.grammar()) 314 + "rs" | "rust" -> Some(rust.grammar()) 315 + "css" -> Some(css.grammar()) 316 + "gleam" -> Some(gleam.grammar()) 317 + "php" -> Some(php.grammar()) 318 + f -> { 319 + io.println_error("No grammar defined for " <> f) 320 + None 321 + } 322 + } 323 + }