authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-09-21 14:03:37-07:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-09-21 14:03:37-07:00
logf0f7c6e0da14e600e621576558c77cc999160801
treef4ac3312eeb2fc8267807b37c89d52ad82c62cba
parentdaf7e3a223445a0020d0ffa52a54f26bf4374126
signature Commit is signed but in an unrecognized format.

chore: separate manual's html and css


8 files changed, 595 insertions(+), 592 deletions(-)

README.md+2-3
......@@ -1,8 +1,7 @@
11# Ix
22
3Ix (***I***nferable E***x***pression) is a high-level, general purpose
4programming language. It features a mix of Rust, Zig, and JavaScript's great
5points.
3Ix (`I`nferable E`x`pression) is a high-level, general purpose programming
4language. It features a mix of Rust, Zig, and JavaScript's great points.
65
76For a guide on how to use Ix, see the manual included in the binary
87distribution. Additionally, the latest commit of the manual is hosted online at
build.zig+1
......@@ -465,6 +465,7 @@ pub fn build(b: *Build) !void {
465465 }
466466 manual_run.addFileArg(b.path("doc/language-manual.md"));
467467 manual_run.addFileArg(b.path("src/manual/template.html"));
468 manual_run.addFileArg(b.path("src/manual/style.css"));
468469 const manual_out = manual_run.addOutputFileArg("language-reference.html");
469470 const install_manual = b.addInstallFile(manual_out, "doc/README.html");
470471
doc/language-manual.md+1-1
......@@ -1,6 +1,6 @@
11# Ix Programming Language
22
3Ix (***I***nferable E***x***pression) is a high-level programming language for
3Ix (**I**nferable E**x**pression) is a high-level programming language for
44developing web applications and games.
55
66Key goals of this project are:
dprint.jsonc+1
......@@ -2,6 +2,7 @@
22 "plugins": [
33 // sort-lines: start
44 "https://plugins.dprint.dev/exec-0.5.0.json@8d9972eee71fa1590e04873540421f3eda7674d0f1aae3d7c788615e7b7413d0",
5 "https://plugins.dprint.dev/g-plane/malva-v0.10.1.wasm",
56 "https://plugins.dprint.dev/g-plane/markup_fmt-v0.12.0.wasm",
67 "https://plugins.dprint.dev/json-0.19.3.wasm",
78 "https://plugins.dprint.dev/markdown-0.17.2.wasm",
src/manual/NaiveHtmlMinifier.zig+10-1
......@@ -46,7 +46,16 @@ fn processHtml(m: *NaiveHtmlMinifier) !void {
4646 ' ', '\t', '\n', '\r' => unreachable, // did not skip whitespace
4747
4848 '<' => switch (m.input[1]) {
49 '!' => try m.moveTokenWith(">"),
49 '!' => {
50 if (m.input[2] == '-' and m.input[3] == '-') {
51 // <!-- comment -->
52 m.input = m.input[(mem.indexOfPos(u8, m.input, 4, "-->") orelse @panic("no comment end")) + 3 ..];
53 return;
54 } else {
55 // <!DOCTYPE ...>
56 try m.moveTokenWith(">");
57 }
58 },
5059 '/' => try m.moveTokenWith(">"),
5160 else => {
5261 const first_tok = getToken(m, ws);
src/manual/generate_manual.zig+27-14
......@@ -16,10 +16,8 @@ comptime {
1616 assert(@import("builtin").mode == .Debug);
1717}
1818
19const cli_name = "wlang";
20const build_cli = cli_name ++ " build";
21const test_cli = cli_name ++ " test";
22const run_cli = cli_name ++ " run";
19const minify_html = true;
20const minify_css = false;
2321
2422pub fn main() !void {
2523 comptime std.debug.assert(@import("builtin").mode == .Debug);
......@@ -37,6 +35,7 @@ pub fn main() !void {
3735 const runtime_path = args.next().?;
3836 const markdown_src = try std.fs.cwd().readFileAlloc(gpa, args.next().?, std.math.maxInt(usize));
3937 const template_src = try std.fs.cwd().readFileAlloc(gpa, args.next().?, std.math.maxInt(usize));
38 const style_src_raw = try std.fs.cwd().readFileAlloc(gpa, args.next().?, std.math.maxInt(usize));
4039 const out_path = args.next().?;
4140
4241 assert(args.next() == null);
......@@ -44,8 +43,6 @@ pub fn main() !void {
4443 var contents = ArrayList(u8).init(gpa);
4544 var header: HeaderWriter = .{};
4645
47 const minify = false;
48
4946 try processMarkdown(
5047 gpa,
5148 progress,
......@@ -58,16 +55,23 @@ pub fn main() !void {
5855
5956 try header.end(gpa);
6057
61 const minified_template = if (minify)
58 const minified_template = if (minify_html)
6259 try NaiveHtmlMinifier.minify(template_src, gpa)
6360 else
6461 template_src;
6562
63 const style_src = try std.fmt.allocPrint(gpa, "<style>{s}</style>", .{style_src_raw});
64 const minified_style = if (minify_css)
65 try NaiveHtmlMinifier.minify(style_src, gpa)
66 else
67 style_src;
68
6669 const applied_template = try replaceInTemplate(gpa, minified_template, .{
6770 .contents = contents.items,
6871 .navigation = header.data.items,
6972 .nav_h1 = "Ix Language",
7073 .repository = "https://github.com/paperdave/wl",
74 .style = minified_style,
7175 });
7276
7377 const out_file = try std.fs.createFileAbsolute(out_path, .{});
......@@ -103,6 +107,7 @@ const InlineType = enum {
103107 bold,
104108 code,
105109 link,
110 comment,
106111};
107112
108113const MarkdownTokenizer = struct {
......@@ -396,7 +401,7 @@ fn processMarkdown(
396401 }
397402
398403 var example: CodeExample = .{
399 .filename = "example.wl",
404 .filename = "example.ix",
400405 .source = .{ .raw = code.items },
401406 };
402407
......@@ -444,8 +449,8 @@ fn processMarkdown(
444449 example.terminal = try mem.concat(gpa, u8, &.{
445450 "$ ",
446451 switch (tag) {
447 .code_run => run_cli,
448 .code_build => build_cli,
452 .code_run => "ix run",
453 .code_build => "ix build",
449454 else => unreachable,
450455 },
451456 " ",
......@@ -592,9 +597,9 @@ fn processMarkdown(
592597 try para.appendSlice(t.advanceLine());
593598
594599 var next_line = t.advanceLine();
595 while (t.str.len > 0 and next_line.len > 0) {
600 while (t.str.len > 0 and next_line.len > 0 and next_line[0] == '>') {
596601 try para.append(' ');
597 try para.appendSlice(next_line);
602 try para.appendSlice(next_line[1..]);
598603 next_line = t.advanceLine();
599604 }
600605
......@@ -609,7 +614,7 @@ fn processMarkdown(
609614pub fn nextInlineToken(src: []const u8) ?struct { InlineType, usize } {
610615 var pos: usize = 0;
611616 while (true) : (pos += 1) {
612 pos = mem.indexOfAnyPos(u8, src, pos, "*`[") orelse return null;
617 pos = mem.indexOfAnyPos(u8, src, pos, "*`[<") orelse return null;
613618
614619 switch (src[pos]) {
615620 '*' => switch (src[pos + 1]) {
......@@ -618,6 +623,10 @@ pub fn nextInlineToken(src: []const u8) ?struct { InlineType, usize } {
618623 },
619624 '`' => return .{ .code, pos },
620625 '[' => return .{ .link, pos },
626 '<' => if (src[pos + 1] == '!' and src[pos + 2] == '-' and src[pos + 3] == '-')
627 return .{ .comment, pos }
628 else
629 continue,
621630 else => unreachable,
622631 }
623632 }
......@@ -666,6 +675,10 @@ fn processInlineMarkdown(gpa: Allocator, src: []const u8, writer: anytype) !void
666675 try writer.writeAll("</a>");
667676 remain = remain[href_end + 1 ..];
668677 },
678 .comment => {
679 const end = mem.indexOfPos(u8, remain, 4, "-->") orelse @panic("missing comment end");
680 remain = remain[1 .. end + "-->".len];
681 },
669682 }
670683 }
671684 try writer.writeAll(remain);
......@@ -676,7 +689,7 @@ fn processInlineMarkdown(gpa: Allocator, src: []const u8, writer: anytype) !void
676689}
677690
678691// TODO: Change this to use Tree-sitter, which would also make it very simple
679// to highlight JavaScript code in addition to the language.
692// to highlight JavaScript code in addition to Ix source code
680693fn writeTokenizedCode(gpa: Allocator, code: []const u8, writer: anytype) !void {
681694 const code_z = try gpa.dupeZ(u8, code);
682695 defer gpa.free(code_z);
src/manual/style.css created+551
......@@ -0,0 +1,551 @@
1:root {
2 /* Catppuccin Latte (light) & Mocha (dark) */
3 --mantle: light-dark(#e6e9ef, #181825);
4 --crust: light-dark(#dce0e8, #11111b);
5 --base: light-dark(#eff1f5, #1e1e2e);
6 --surface-0: light-dark(#ccd0da, #313244);
7 --surface-1: light-dark(#bcc0cc, #45475a);
8 --surface-2: light-dark(#acb0be, #585b70);
9 --overlay-0: light-dark(#9ca0b0, #6c7086);
10 --overlay-1: light-dark(#8c8fa1, #7f849c);
11 --overlay-2: light-dark(#7c7f93, #9399b2);
12 --subtext-0: light-dark(#6c6f85, #a6adc8);
13 --subtext-1: light-dark(#5c5f77, #bac2de);
14 --text: light-dark(#4c4f69, #cdd6f4);
15 --accent: light-dark(#179299, #94e2d5);
16 --link: light-dark(#179299, #94e2d5);
17
18 --font-text:
19 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
20 Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
21 --font-code:
22 ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
23 "Courier New", monospace;
24}
25
26* {
27 margin: 0;
28 padding: 0;
29 box-sizing: border-box;
30}
31
32body {
33 background-color: var(--base);
34 color: var(--text);
35 font-family: var(--font-text);
36 color-scheme: light dark;
37 display: flex;
38}
39
40main {
41 width: 100%;
42 max-width: 80ch;
43 padding: 1rem;
44 padding-top: 2rem;
45 margin: auto;
46 line-height: 1.5;
47}
48
49h1, h2, h3, h4, h5, h6, p, ul, pre, blockquote, table {
50 margin-bottom: 1rem;
51}
52
53h2, h3, h4, h5, h6 {
54 padding-top: 0.8rem;
55 margin-top: -0.8rem;
56}
57
58ul {
59 padding-left: 2rem;
60}
61
62li {
63 margin-bottom: 0.25rem;
64}
65
66blockquote {
67 background-color: var(--surface-0);
68 border-left: 4px solid var(--overlay-2);
69 padding-left: 16px;
70 padding-top: 8px;
71 padding-bottom: 8px;
72}
73
74a {
75 color: inherit;
76}
77
78table, th, td {
79 border: 1px solid #9ca0b0;
80 border-collapse: collapse;
81}
82
83th, td {
84 padding: 0.5rem;
85}
86
87.ansi-1 {
88 font-weight: bold;
89}
90
91.ansi-31 {
92 color: light-dark(#d20f39, #f38ba8);
93}
94
95.ansi-32 {
96 color: light-dark(#40a02b, #a6e3a1);
97}
98
99.ansi-33 {
100 color: light-dark(#de8d1c, #f9e2af);
101}
102
103.ansi-34 {
104 color: light-dark(#1e66f5, #89b4fa);
105}
106
107.ansi-35 {
108 color: light-dark(#ea76cb, #f5c2e7);
109}
110
111.ansi-36 {
112 color: light-dark(#179299, #94e2d5);
113}
114
115.ansi-2 {
116 color: light-dark(#bcc0cc, #8087a2);
117}
118
119.kw {
120 color: light-dark(#8839ef, #cba6f7);
121 font-weight: bold;
122}
123
124.tok-string {
125 color: light-dark(#40a02b, #a6e3a1);
126}
127
128.tok-builtin_id {
129 color: light-dark(#d20f39, #f38ba8);
130 font-weight: bold;
131}
132
133.tok-number_int, .tok-number_float {
134 color: light-dark(#fe640b, #fab387);
135}
136
137.tok-semi,
138.tok-end_paren,
139.tok-start_paren,
140.tok-equal,
141.tok-colon,
142.tok-start_brace,
143.tok-end_brace,
144.tok-dot {
145 color: light-dark(#7c7f93, #9399b2);
146}
147
148aside {
149 position: sticky;
150 top: 0;
151 left: 0;
152 height: 100dvh;
153 height: 100vh;
154}
155
156#sidebar-content {
157 display: flex;
158 flex-direction: column;
159 background-color: var(--mantle);
160 height: 100%;
161 padding: 1rem;
162 border-radius: 0 1rem 1rem 0;
163 overflow-y: auto;
164 width: 40ch;
165}
166
167#toc, #toc ul, #links {
168 list-style: none;
169}
170
171nav header {
172 font-weight: bold;
173 font-size: 1.5rem;
174}
175
176#toc {
177 padding: 0;
178}
179
180#toc ul {
181 margin: 0rem;
182 padding-left: 1rem;
183 margin-bottom: 0;
184}
185
186#toc li {
187 margin: 0;
188}
189
190#toc ul li {
191 border-left: 1px solid var(--surface-2);
192 padding-left: 0.5rem;
193}
194
195#toc ul li:hover {
196 border-left: 1px solid var(--overlay-2);
197}
198
199#toc .number {
200 color: var(--accent);
201 font-weight: bold;
202}
203
204aside a {
205 position: relative;
206 /* anchor point for 'aside a::before' */
207 display: block;
208 text-decoration: none;
209 color: var(--text);
210 padding: 0.45rem 0.75rem;
211 border-radius: 0.25rem;
212}
213
214/* this invisible hitbox lets you click in the left gutter */
215aside a::before {
216 content: " ";
217 position: absolute;
218 display: block;
219 right: 100%;
220 top: 0;
221 bottom: 0;
222 width: 100vw;
223}
224
225aside a:hover {
226 background-color: var(--surface-0);
227}
228
229#links {
230 list-style: none;
231 padding: 0;
232 margin-top: auto;
233}
234
235#links li {
236 margin: 0;
237}
238
239#hidden-menu-checkbox, [name="tabs"] {
240 display: block;
241 width: 1px;
242 height: 1px;
243 position: fixed;
244 top: -3px;
245 left: -3px;
246}
247
248#menu-btn, #clickable-background {
249 display: none;
250}
251
252main a {
253 color: var(--link);
254}
255
256main a:active {
257 background-color: var(--accent);
258 color: var(--base);
259 border-radius: 0.25rem;
260 text-decoration: none;
261}
262
263.external::after {
264 background-color: var(--link);
265 content: "";
266 display: inline-flex;
267 height: 10px;
268 margin-left: 4px;
269 mask-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTYgMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTcuNSAxLjVWMEgxNnY4LjVoLTEuNVYyLjZMNyAxMC4xIDYgOWw3LjUtNy42aC02Wk0xMyAxNmMuNiAwIDEtLjQgMS0xdi01aC0xLjV2NC41aC0xMXYtMTFINlYySDFhMSAxIDAgMCAwLTEgMXYxMmMwIC42LjQgMSAxIDFoMTJaIiBmaWxsPSJyZWQiLz48L3N2Zz4=");
270 mask-size: cover;
271 width: 10px;
272}
273.external:active::after {
274 background-color: var(--base);
275}
276
277.heading {
278 color: inherit;
279 text-decoration: none;
280}
281
282.heading:hover, .heading:focus-visible {
283 text-decoration: underline;
284 position: relative;
285}
286
287.heading:hover::before, .heading:focus-visible::before {
288 content: " #";
289 color: var(--subtext-0);
290 position: absolute;
291 right: calc(100% + 0.5rem);
292 top: -0.125em;
293}
294
295/* Tablet/small screen layout */
296@media screen and (max-width: 1000px) {
297 html {
298 scroll-padding-top: 5rem;
299 }
300
301 main {
302 padding-top: 5rem;
303 }
304
305 #hidden-menu-checkbox:focus-visible + aside #menu-btn {
306 outline: 2px solid var(--text);
307 }
308
309 aside {
310 position: fixed;
311 left: -100%;
312 transition: left 500ms, bottom 300ms;
313 z-index: 9;
314 padding-top: 3rem;
315 }
316
317 aside a {
318 padding: 0.5rem 0.75rem;
319 }
320
321 #top-part {
322 position: fixed;
323 inset: 0;
324 height: fit-content;
325 background-color: var(--mantle);
326 padding: 0.5rem;
327 box-shadow: 0 4px 4px rgba(0, 0, 0, 0.3);
328 z-index: 9;
329 }
330
331 #menu-btn {
332 display: grid;
333 padding: 0.5rem;
334 width: fit-content;
335 cursor: pointer;
336 border-radius: 0.25rem;
337 user-select: none;
338 }
339
340 #close {
341 display: none;
342 }
343
344 #chevron {
345 display: none;
346 }
347
348 #menu-btn:hover {
349 background-color: var(--surface-0);
350 }
351
352 #hidden-menu-checkbox:checked ~ aside {
353 left: 0;
354 transition: left 300ms, bottom 300ms;
355 }
356
357 #hidden-menu-checkbox:checked ~ aside #clickable-background {
358 display: block;
359 position: fixed;
360 width: 100%;
361 height: 100%;
362 inset: 0;
363 background-color: rgba(0, 0, 0, 0.5);
364 z-index: -1;
365 }
366
367 #hidden-menu-checkbox:checked ~ aside #menu-btn .open {
368 display: none;
369 }
370
371 #hidden-menu-checkbox:checked ~ aside #menu-btn .close {
372 display: block;
373 }
374}
375
376/* Mobile layout */
377@media screen and (max-width: 600px) {
378 main {
379 padding: 3rem 1rem;
380 }
381
382 aside {
383 position: fixed;
384 top: initial;
385 left: 0;
386 bottom: calc(-100% + 6rem);
387 width: 100%;
388 display: flex;
389 flex-direction: column;
390 }
391
392 aside a {
393 padding: 0.8rem 0.75rem;
394 }
395
396 aside a:hover {
397 background-color: var(--surface-1);
398 }
399
400 #sidebar-content {
401 border-radius: 0px;
402 padding: 1rem;
403 width: 100%;
404 background-color: var(--surface-0);
405 }
406
407 #top-part {
408 position: initial;
409 padding: 0;
410 border-radius: 1.5rem 1.5rem 0 0;
411 box-shadow: 0 -4px 5px rgba(0, 0, 0, 0.15);
412 border-bottom: var(--overlay-2) 1px solid;
413 background-color: var(--surface-0);
414 }
415
416 #open, #close {
417 display: none !important;
418 }
419
420 #chevron {
421 display: block;
422 transition: transform 300ms;
423 }
424
425 #menu-btn {
426 display: flex;
427 justify-content: center;
428 width: 100%;
429 padding: 1.5rem 0;
430 border-radius: 1.5rem 1.5rem 0 0;
431 }
432
433 #hidden-menu-checkbox:checked ~ aside {
434 bottom: 0;
435 }
436
437 #hidden-menu-checkbox:checked ~ aside #chevron {
438 transform: rotateX(180deg);
439 }
440
441 #hidden-menu-checkbox:checked ~ aside #top-part {
442 box-shadow: none;
443 }
444}
445
446pre > code {
447 border: none;
448 padding: 0;
449}
450
451pre, code {
452 border: 0;
453 background-color: transparent;
454 font-family: var(--font-code);
455 font-size: 0.9rem;
456}
457
458.file {
459 border: 1px solid var(--surface-1);
460 background-color: var(--crust);
461 border-radius: 0.5rem;
462 overflow: hidden;
463 margin-bottom: 0.5rem;
464}
465
466.file figcaption {
467 padding: 0.25rem 0.5rem;
468 font-size: 0.75rem;
469 border-bottom: 1px solid var(--surface-1);
470 /* background-color: var(--crust); */
471 background-color: var(--accent);
472 color: var(--base);
473}
474
475.file pre {
476 margin: 0;
477 padding: 0.75rem 0.5rem;
478 overflow-y: scroll;
479 color: var(--text);
480 position: relative;
481}
482
483.file pre.terminal {
484 background-color: #24273a;
485 color: #cdd6f4;
486 border-radius: 0 0 0.5rem 0.5rem;
487}
488
489.file [role="tablist"] {
490 display: flex;
491 border-top: 1px solid var(--surface-1);
492 border-bottom: 1px solid var(--surface-1);
493 background-color: var(--surface-0);
494}
495
496.file [role="tab"] {
497 padding: 0.25rem 0.75rem;
498 cursor: pointer;
499 /* flex: 1; */
500 border-top: 2px solid transparent;
501 text-align: center;
502 font-size: 0.9rem;
503}
504
505.file .bottom {
506 display: none;
507}
508
509[name="tabs"][value="output"]:checked ~ main .file .tabs .output {
510 display: block;
511}
512
513[name="tabs"][value="output"]:checked
514 ~ main
515 .file
516 .tabs
517 [role="tab"][for="output"] {
518 font-weight: bold;
519 background-color: var(--base);
520 border-top: 2px solid var(--accent);
521}
522
523[name="tabs"][value="js"]:checked ~ main .file .tabs .js {
524 display: block;
525}
526
527[name="tabs"][value="js"]:checked ~ main .file .tabs [role="tab"][for="js"] {
528 font-weight: bold;
529 border-top: 2px solid var(--accent);
530 background-color: var(--base);
531}
532
533.file .copy-button {
534 position: absolute;
535 top: 0.25rem;
536 right: 0.25rem;
537 padding: 0.25rem;
538 border: 1px solid var(--surface-1);
539 border-radius: 0.25rem;
540 background-color: var(--base);
541 cursor: pointer;
542 display: grid;
543}
544
545.file .copy-button:hover {
546 background-color: var(--crust);
547}
548
549.file .copy-button .hidden {
550 display: none;
551}
src/manual/template.html+2-573
......@@ -3,581 +3,10 @@
33 <head>
44 <meta charset="UTF-8" />
55 <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6 <meta name="robots" content="noindex">
67 <title>Ix Language Manual</title>
7 <style>
8 :root {
9 /* Catppuccin Latte (light) & Mocha (dark) */
10 --mantle: light-dark(#e6e9ef, #181825);
11 --crust: light-dark(#dce0e8, #11111b);
12 --base: light-dark(#eff1f5, #1e1e2e);
13 --surface-0: light-dark(#ccd0da, #313244);
14 --surface-1: light-dark(#bcc0cc, #45475a);
15 --surface-2: light-dark(#acb0be, #585b70);
16 --overlay-0: light-dark(#9ca0b0, #6c7086);
17 --overlay-1: light-dark(#8c8fa1, #7f849c);
18 --overlay-2: light-dark(#7c7f93, #9399b2);
19 --subtext-0: light-dark(#6c6f85, #a6adc8);
20 --subtext-1: light-dark(#5c5f77, #bac2de);
21 --text: light-dark(#4c4f69, #cdd6f4);
22 --accent: light-dark(#179299, #94e2d5);
23 --link: light-dark(#179299, #94e2d5);
24 }
25
26 * {
27 margin: 0;
28 padding: 0;
29 box-sizing: border-box;
30 }
31
32 body {
33 background-color: var(--base);
34 color: var(--text);
35 font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
36 Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue",
37 sans-serif;
38 color-scheme: light dark;
39 display: flex;
40 }
41
42 main {
43 width: 100%;
44 max-width: 80ch;
45 padding: 1rem;
46 padding-top: 2rem;
47 margin: auto;
48 line-height: 1.5;
49 }
50
51 h1,
52 h2,
53 h3,
54 h4,
55 h5,
56 h6,
57 p,
58 ul,
59 pre,
60 blockquote,
61 table {
62 margin-bottom: 1rem;
63 }
64
65 h2,
66 h3,
67 h4,
68 h5,
69 h6 {
70 padding-top: 0.8rem;
71 margin-top: -0.8rem;
72 }
73
74 ul {
75 padding-left: 2rem;
76 }
77
78 li {
79 margin-bottom: 0.25rem;
80 }
81
82 blockquote {
83 background-color: var(--surface-0);
84 border-left: 4px solid var(--overlay-2);
85 padding-left: 16px;
86 padding-top: 8px;
87 padding-bottom: 8px;
88 }
89
90 a {
91 color: inherit;
92 }
93
94 table,
95 th,
96 td {
97 border: 1px solid #9ca0b0;
98 border-collapse: collapse;
99 }
100
101 th,
102 td {
103 padding: 0.5rem;
104 }
105
106 .ansi-1 {
107 font-weight: bold;
108 }
109
110 .ansi-31 {
111 color: light-dark(#d20f39, #f38ba8);
112 }
113
114 .ansi-32 {
115 color: light-dark(#40a02b, #a6e3a1);
116 }
117
118 .ansi-33 {
119 color: light-dark(#de8d1c, #f9e2af);
120 }
121
122 .ansi-34 {
123 color: light-dark(#1e66f5, #89b4fa);
124 }
125
126 .ansi-35 {
127 color: light-dark(#ea76cb, #f5c2e7);
128 }
129
130 .ansi-36 {
131 color: light-dark(#179299, #94e2d5);
132 }
133
134 .ansi-2 {
135 color: light-dark(#bcc0cc, #8087a2);
136 }
137
138 .kw {
139 color: light-dark(#8839ef, #cba6f7);
140 font-weight: bold;
141 }
142
143 .tok-string {
144 color: light-dark(#40a02b, #a6e3a1);
145 }
146
147 .tok-builtin_id {
148 color: light-dark(#d20f39, #f38ba8);
149 font-weight: bold;
150 }
151
152 .tok-number_int,
153 .tok-number_float {
154 color: light-dark(#fe640b, #fab387);
155 }
156
157 .tok-semi,
158 .tok-end_paren,
159 .tok-start_paren,
160 .tok-equal,
161 .tok-colon,
162 .tok-start_brace,
163 .tok-end_brace,
164 .tok-dot {
165 color: light-dark(#7c7f93, #9399b2);
166 }
167
168 aside {
169 position: sticky;
170 top: 0;
171 left: 0;
172 height: 100dvh;
173 height: 100vh;
174 }
175
176 #sidebar-content {
177 display: flex;
178 flex-direction: column;
179 background-color: var(--mantle);
180 height: 100%;
181 padding: 1rem;
182 border-radius: 0 1rem 1rem 0;
183 overflow-y: auto;
184 width: 40ch;
185 }
186
187 #toc,
188 #toc ul,
189 #links {
190 list-style: none;
191 }
192
193 nav header {
194 font-weight: bold;
195 font-size: 1.5rem;
196 }
197
198 #toc {
199 padding: 0;
200 }
201
202 #toc ul {
203 margin: 0rem;
204 padding-left: 1rem;
205 margin-bottom: 0;
206 }
207
208 #toc li {
209 margin: 0;
210 }
211
212 #toc ul li {
213 border-left: 1px solid var(--surface-2);
214 padding-left: 0.5rem;
215 }
216
217 #toc ul li:hover {
218 border-left: 1px solid var(--overlay-2);
219 }
220
221 #toc .number {
222 color: var(--accent);
223 font-weight: bold;
224 }
225
226 aside a {
227 position: relative;
228 /* anchor point for 'aside a::before' */
229 display: block;
230 text-decoration: none;
231 color: var(--text);
232 padding: 0.45rem 0.75rem;
233 border-radius: 0.25rem;
234 }
235
236 /* this invisible hitbox lets you click in the left gutter */
237 aside a::before {
238 content: " ";
239 position: absolute;
240 display: block;
241 right: 100%;
242 top: 0;
243 bottom: 0;
244 width: 100vw;
245 }
246
247 aside a:hover {
248 background-color: var(--surface-0);
249 }
250
251 #links {
252 list-style: none;
253 padding: 0;
254 margin-top: auto;
255 }
256
257 #links li {
258 margin: 0;
259 }
260
261 #hidden-menu-checkbox,
262 [name="tabs"] {
263 display: block;
264 width: 1px;
265 height: 1px;
266 position: fixed;
267 top: -3px;
268 left: -3px;
269 }
270
271 #menu-btn,
272 #clickable-background {
273 display: none;
274 }
275
276 main a {
277 color: var(--link);
278 }
279
280 main a:active {
281 background-color: var(--accent);
282 color: var(--base);
283 border-radius: 0.25rem;
284 text-decoration: none;
285 }
286
287 .external::after {
288 background-color: var(--link);
289 content: "";
290 display: inline-flex;
291 height: 10px;
292 margin-left: 4px;
293 mask-image: url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMTYgMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTcuNSAxLjVWMEgxNnY4LjVoLTEuNVYyLjZMNyAxMC4xIDYgOWw3LjUtNy42aC02Wk0xMyAxNmMuNiAwIDEtLjQgMS0xdi01aC0xLjV2NC41aC0xMXYtMTFINlYySDFhMSAxIDAgMCAwLTEgMXYxMmMwIC42LjQgMSAxIDFoMTJaIiBmaWxsPSJyZWQiLz48L3N2Zz4=");
294 mask-size: cover;
295 width: 10px;
296 }
297 .external:active::after {
298 background-color: var(--base);
299 }
300
301 .heading {
302 color: inherit;
303 text-decoration: none;
304 }
305
306 .heading:hover,
307 .heading:focus-visible {
308 text-decoration: underline;
309 position: relative;
310 }
311
312 .heading:hover::before,
313 .heading:focus-visible::before {
314 content: " #";
315 color: var(--subtext-0);
316 position: absolute;
317 right: calc(100% + 0.5rem);
318 top: -0.125em;
319 }
320
321 /* Tablet/small screen layout */
322 @media screen and (max-width: 1000px) {
323 html {
324 scroll-padding-top: 5rem;
325 }
326
327 main {
328 padding-top: 5rem;
329 }
330
331 #hidden-menu-checkbox:focus-visible+aside #menu-btn {
332 outline: 2px solid var(--text);
333 }
334
335 aside {
336 position: fixed;
337 left: -100%;
338 transition: left 500ms, bottom 300ms;
339 z-index: 9;
340 padding-top: 3rem;
341 }
342
343 aside a {
344 padding: 0.5rem 0.75rem;
345 }
346
347 #top-part {
348 position: fixed;
349 inset: 0;
350 height: fit-content;
351 background-color: var(--mantle);
352 padding: 0.5rem;
353 box-shadow: 0 4px 4px rgba(0, 0, 0, 0.3);
354 z-index: 9;
355 }
356
357 #menu-btn {
358 display: grid;
359 padding: 0.5rem;
360 width: fit-content;
361 cursor: pointer;
362 border-radius: 0.25rem;
363 user-select: none;
364 }
365
366
367 #close {
368 display: none;
369 }
370
371 #chevron {
372 display: none;
373 }
374
375 #menu-btn:hover {
376 background-color: var(--surface-0);
377 }
378
379 #hidden-menu-checkbox:checked~aside {
380 left: 0;
381 transition: left 300ms, bottom 300ms;
382 }
383
384 #hidden-menu-checkbox:checked~aside #clickable-background {
385 display: block;
386 position: fixed;
387 width: 100%;
388 height: 100%;
389 inset: 0;
390 background-color: rgba(0, 0, 0, 0.5);
391 z-index: -1;
392 }
393
394 #hidden-menu-checkbox:checked~aside #menu-btn .open {
395 display: none;
396 }
397
398 #hidden-menu-checkbox:checked~aside #menu-btn .close {
399 display: block;
400 }
401 }
402
403 /* Mobile layout */
404 @media screen and (max-width: 600px) {
405 main {
406 padding: 3rem 1rem;
407 }
408
409 aside {
410 position: fixed;
411 top: initial;
412 left: 0;
413 bottom: calc(-100% + 6rem);
414 width: 100%;
415 display: flex;
416 flex-direction: column;
417 }
418
419 aside a {
420 padding: 0.8rem 0.75rem;
421 }
422
423 aside a:hover {
424 background-color: var(--surface-1);
425 }
426
427 #sidebar-content {
428 border-radius: 0px;
429 padding: 1rem;
430 width: 100%;
431 background-color: var(--surface-0);
432 }
433
434 #top-part {
435 position: initial;
436 padding: 0;
437 border-radius: 1.5rem 1.5rem 0 0;
438 box-shadow: 0 -4px 5px rgba(0, 0, 0, 0.15);
439 border-bottom: var(--overlay-2) 1px solid;
440 background-color: var(--surface-0);
441 }
442
443 #open,
444 #close {
445 display: none !important;
446 }
447
448 #chevron {
449 display: block;
450 transition: transform 300ms;
451 }
452
453 #menu-btn {
454 display: flex;
455 justify-content: center;
456 width: 100%;
457 padding: 1.5rem 0;
458 border-radius: 1.5rem 1.5rem 0 0;
459 }
460
461 #hidden-menu-checkbox:checked~aside {
462 bottom: 0;
463 }
464
465 #hidden-menu-checkbox:checked~aside #chevron {
466 transform: rotateX(180deg);
467 }
468
469 #hidden-menu-checkbox:checked~aside #top-part {
470 box-shadow: none;
471 }
472 }
473
474 pre>code {
475 border: none;
476 padding: 0;
477 }
478
479 pre,
480 code {
481 border: 0;
482 background-color: transparent;
483 font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
484 "Liberation Mono", "Courier New", monospace;
485 font-size: 0.9rem;
486 }
487
488 .file {
489 border: 1px solid var(--surface-1);
490 background-color: var(--crust);
491 border-radius: 0.5rem;
492 overflow: hidden;
493 margin-bottom: 0.5rem;
494 }
495
496 .file figcaption {
497 padding: 0.25rem 0.5rem;
498 font-size: 0.75rem;
499 border-bottom: 1px solid var(--surface-1);
500 /* background-color: var(--crust); */
501 background-color: var(--accent);
502 color: var(--base);
503 }
504
505 .file pre {
506 margin: 0;
507 padding: 0.75rem 0.5rem;
508 overflow-y: scroll;
509 color: var(--text);
510 position: relative;
511 }
512
513 .file pre.terminal {
514 background-color: #24273a;
515 color: #cdd6f4;
516 border-radius: 0 0 0.5rem 0.5rem;
517 }
518
519 .file [role="tablist"] {
520 display: flex;
521 border-top: 1px solid var(--surface-1);
522 border-bottom: 1px solid var(--surface-1);
523 background-color: var(--surface-0);
524 }
525
526 .file [role="tab"] {
527 padding: 0.25rem 0.75rem;
528 cursor: pointer;
529 /* flex: 1; */
530 border-top: 2px solid transparent;
531 text-align: center;
532 font-size: 0.9rem;
533 }
534
535 .file .bottom {
536 display: none;
537 }
538
539 [name="tabs"][value="output"]:checked~main .file .tabs .output {
540 display: block;
541 }
542
543 [name="tabs"][value="output"]:checked~main .file .tabs [role="tab"][for="output"] {
544 font-weight: bold;
545 background-color: var(--base);
546 border-top: 2px solid var(--accent);
547 }
548
549 [name="tabs"][value="js"]:checked~main .file .tabs .js {
550 display: block;
551 }
552
553 [name="tabs"][value="js"]:checked~main .file .tabs [role="tab"][for="js"] {
554 font-weight: bold;
555 border-top: 2px solid var(--accent);
556 background-color: var(--base);
557 }
558
559 .file .copy-button {
560 position: absolute;
561 top: 0.25rem;
562 right: 0.25rem;
563 padding: 0.25rem;
564 border: 1px solid var(--surface-1);
565 border-radius: 0.25rem;
566 background-color: var(--base);
567 cursor: pointer;
568 display: grid;
569 }
570
571 .file .copy-button:hover {
572 background-color: var(--crust);
573 }
574
575 .file .copy-button .hidden {
576 display: none;
577 }
578 </style>
8 {style}
5799 </head>
580
58110 <body>
58211 <input type="checkbox" id="hidden-menu-checkbox" />
58312 <input name="tabs" type="radio" id="output" value="output" checked="true" />