diff options
| author | Gabriel Schneider <[email protected]> | 2026-10-01 11:11:25 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 12:24:31 -0300 |
| commit | 5874742494a293c5e285d2f1284c748e39d980f8 (patch) | |
| tree | e3fa445947f90e2e6262b3466430b901d2e62295 /docs | |
| parent | d3638abbb3d177ea08beed3b09cd9682b3732daf (diff) | |
| download | pardes-5874742494a293c5e285d2f1284c748e39d980f8.tar.gz pardes-5874742494a293c5e285d2f1284c748e39d980f8.zip | |
The docs become a website: a page per chapter, a landing page with recordings, one Typst bundle run
docs/site/site.typ writes every page in one bundle compile: the landing
page (the tagline, the recordings listed in media/clips.toml, links to
start from), a page per entry of `chapters` in style.typ, and the tutor.
Every page shares a tag-line header with the chapter words; #doc links
resolve to the right page and anchor; a chapter with four or more
sections gets an "On this page" list. Links are relative and pages flat,
so it reads from file:// or any static server. `zig build site` installs
it to <prefix>/share/doc/pardes/site and fails plainly without typst.
The look is 0x4200.cafe's lapis layout (framed panels, hard shadows, a
tag-line trail, Departure Mono headings over Crimson Pro) in orchard's
greens and amber, forestbones_light when the reader prefers light; both
fonts are OFL and shipped. In HTML a key is one pill with its + and ›,
and a mouse button is an inline SVG in currentColor. The tagline lives
once in style.typ for the sheet, the book and the site, and the single
page html.typ gives way to the site.
The landing leads with one plain sentence (columns of panes, click to
run or open, helix keys, scripts through files), then the tagline and a
line on where it runs; a hero image is the first ready clip's poster;
"Start: the guide" leads the links, "Hands on: the tutor" next, the rest
a plain row; the captions use no undefined words. docs/typ/install.typ
holds the install lines once: setup.typ has an Install section that
includes it, and the landing includes it too. Building says up front it
is for contributors and points at setup to install. In HTML the guide's
glossary is a closed <details> (#glossary, the same heading and table on
paper), each drawn mouse carries hidden text (left-click, hold left,
click middle) for screen readers, w3m and copying, and the footer takes
a source link and a licence from `project` in style.typ once decided.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/site/README | 24 | ||||
| -rw-r--r-- | docs/site/fonts/CrimsonPro-VF-italic.woff2 | bin | 0 -> 52448 bytes | |||
| -rw-r--r-- | docs/site/fonts/CrimsonPro-VF-normal.woff2 | bin | 0 -> 49660 bytes | |||
| -rw-r--r-- | docs/site/fonts/DepartureMono-Regular.woff2 | bin | 0 -> 22496 bytes | |||
| -rw-r--r-- | docs/site/fonts/LICENSE-CrimsonPro.txt | 93 | ||||
| -rw-r--r-- | docs/site/fonts/LICENSE-DepartureMono.txt | 93 | ||||
| -rw-r--r-- | docs/site/media/clips.toml | 24 | ||||
| -rw-r--r-- | docs/site/media/placeholder.svg | 1 | ||||
| -rw-r--r-- | docs/site/site.css | 142 | ||||
| -rw-r--r-- | docs/site/site.typ | 161 | ||||
| -rw-r--r-- | docs/typ/README | 8 | ||||
| -rw-r--r-- | docs/typ/book.typ | 4 | ||||
| -rw-r--r-- | docs/typ/building.typ | 3 | ||||
| -rw-r--r-- | docs/typ/cheatsheet-a4.typ | 4 | ||||
| -rw-r--r-- | docs/typ/guide.typ | 6 | ||||
| -rw-r--r-- | docs/typ/html.typ | 24 | ||||
| -rw-r--r-- | docs/typ/install.typ | 8 | ||||
| -rw-r--r-- | docs/typ/setup.typ | 3 | ||||
| -rw-r--r-- | docs/typ/style.typ | 63 |
19 files changed, 622 insertions, 39 deletions
diff --git a/docs/site/README b/docs/site/README new file mode 100644 index 00000000..76c2973f --- /dev/null +++ b/docs/site/README @@ -0,0 +1,24 @@ +docs/site: the pardes website, built from docs/typ + + site.typ the wrapper: every page, the shared tag-line header and + chapter nav, the landing page, and the files it ships. + Pages come from `chapters` in ../typ/style.typ, so a new + chapter is a new page; #doc links resolve across pages. + site.css the whole look (lapis layout from 0x4200.cafe in + pardes's orchard greens; forestbones_light when the + reader prefers light). + fonts/ Departure Mono and Crimson Pro, both SIL OFL, licences + alongside. + media/clips.toml the landing page's recordings, in order: name, caption, + ready. For a clip named N the site ships media/N.webm, + media/N.mp4 and media/N.png (the poster); until `ready` + is true it shows placeholder.svg instead. + +Build (from the repo root): + + typst compile --root . --format bundle --features bundle,html docs/site/site.typ OUT + zig build site -Dplatform=tty --prefix DIR # DIR/share/doc/pardes/site/ + +Every page sits at the top of OUT and every link is relative, so the site +works from file:// and from any static server. The only prose of the +site's own is the landing page; everything else is included from docs/typ. diff --git a/docs/site/fonts/CrimsonPro-VF-italic.woff2 b/docs/site/fonts/CrimsonPro-VF-italic.woff2 Binary files differnew file mode 100644 index 00000000..59923684 --- /dev/null +++ b/docs/site/fonts/CrimsonPro-VF-italic.woff2 diff --git a/docs/site/fonts/CrimsonPro-VF-normal.woff2 b/docs/site/fonts/CrimsonPro-VF-normal.woff2 Binary files differnew file mode 100644 index 00000000..7fd174a4 --- /dev/null +++ b/docs/site/fonts/CrimsonPro-VF-normal.woff2 diff --git a/docs/site/fonts/DepartureMono-Regular.woff2 b/docs/site/fonts/DepartureMono-Regular.woff2 Binary files differnew file mode 100644 index 00000000..b4a23dc1 --- /dev/null +++ b/docs/site/fonts/DepartureMono-Regular.woff2 diff --git a/docs/site/fonts/LICENSE-CrimsonPro.txt b/docs/site/fonts/LICENSE-CrimsonPro.txt new file mode 100644 index 00000000..6a7d97cd --- /dev/null +++ b/docs/site/fonts/LICENSE-CrimsonPro.txt @@ -0,0 +1,93 @@ +Copyright 2018 The Crimson Pro Project Authors (https://github.com/Fonthausen/CrimsonPro) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/docs/site/fonts/LICENSE-DepartureMono.txt b/docs/site/fonts/LICENSE-DepartureMono.txt new file mode 100644 index 00000000..de524761 --- /dev/null +++ b/docs/site/fonts/LICENSE-DepartureMono.txt @@ -0,0 +1,93 @@ +Copyright 2022–2024 Helena Zhang (helenazhang.com). + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/docs/site/media/clips.toml b/docs/site/media/clips.toml new file mode 100644 index 00000000..f927450f --- /dev/null +++ b/docs/site/media/clips.toml @@ -0,0 +1,24 @@ +# The landing page's recordings, in the order shown. For each clip the +# site ships media/<name>.webm and media/<name>.mp4, with media/<name>.png +# as the poster. While `ready` is false the clip shows placeholder.svg and +# no video, so the site builds before the recordings exist. + +[[clip]] +name = "look-and-execute" +caption = "Right-click opens what you point at; middle-click runs it." +ready = false + +[[clip]] +name = "terminals" +caption = "Terminal panes: type to the program, or move around its output." +ready = false + +[[clip]] +name = "scripting" +caption = "A script drives the session by reading and writing files." +ready = false + +[[clip]] +name = "diff-review" +caption = "Right-click a diff line to open that line of the file." +ready = false diff --git a/docs/site/media/placeholder.svg b/docs/site/media/placeholder.svg new file mode 100644 index 00000000..0b0efde0 --- /dev/null +++ b/docs/site/media/placeholder.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 400"><rect width="640" height="400" fill="#121c16"/><rect x="24" y="24" width="592" height="28" fill="#1d2824"/><rect x="24" y="24" width="28" height="28" fill="#add38a"/><rect x="24" y="64" width="592" height="312" fill="none" stroke="#486152" stroke-width="2"/><text x="320" y="230" fill="#b2c3b8" font-family="monospace" font-size="22" text-anchor="middle">recording to come</text></svg> diff --git a/docs/site/site.css b/docs/site/site.css new file mode 100644 index 00000000..bbb1140f --- /dev/null +++ b/docs/site/site.css @@ -0,0 +1,142 @@ +/* pardes docs site. The 0x4200.cafe lapis layout (framed panels, hard + shadows, a tag-line trail, pixel headings over a book hand) in pardes's + orchard greens; light mode follows forestbones_light. Fonts are OFL and + shipped in fonts/. */ + +@font-face { font-family:"Crimson Pro"; src:url("fonts/CrimsonPro-VF-normal.woff2") format("woff2"); font-weight:200 900; font-display:swap; } +@font-face { font-family:"Crimson Pro"; src:url("fonts/CrimsonPro-VF-italic.woff2") format("woff2"); font-weight:200 900; font-style:italic; font-display:swap; } +@font-face { font-family:"Departure Mono"; src:url("fonts/DepartureMono-Regular.woff2") format("woff2"); font-display:swap; size-adjust:72%; } + +/* orchard: field, panel, ink, leaf, amber, plum */ +:root { + --field:#0d1410; --deep:#070c09; --panel:#121c16; --tag:#1d2824; --tag-hi:#26362d; + --ink:#e5eee5; --dim:#b2c3b8; --leaf:#add38a; --leaf-hi:#b9f480; --amber:#f4c086; --plum:#d3b0d9; + --rule:#486152; --shadow:#000c; --stripe:#3c614a; + --hand:"Crimson Pro", Georgia, serif; --pixel:"Departure Mono", ui-monospace, monospace; + --mono:ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace; +} +/* forestbones_light: paper, butter tags, green ink */ +@media (prefers-color-scheme: light) { + :root { + --field:#f3ead2; --deep:#e9dcb8; --panel:#faf3e1; --tag:#f3e2aa; --tag-hi:#e8d48e; + --ink:#2f3b33; --dim:#56645b; --leaf:#3c6b56; --leaf-hi:#2c7a3e; --amber:#9a5810; --plum:#6e4774; + --rule:#b1a26f; --shadow:#4f5b6233; --stripe:#cdd9b0; + } +} + +* { box-sizing:border-box; } +html { background:var(--deep); color:var(--ink); scrollbar-color:var(--leaf) var(--deep); } +body { margin:0; min-height:100vh; background:var(--field) radial-gradient(color-mix(in srgb, var(--leaf) 14%, transparent) 1px, transparent 1.5px) 0 0/22px 22px; } +.shell { max-width:980px; margin:0 auto; padding:24px 16px 40px; display:grid; gap:26px; } + +/* --- the trail: a pane's tag line ------------------------------------------- */ +.trail { + display:flex; flex-wrap:wrap; align-items:baseline; gap:6px 12px; padding:9px 14px; + background:var(--tag); border:2px solid var(--leaf); box-shadow:5px 5px 0 var(--amber); + font:20px/1.3 var(--pixel); +} +.trail .grip { width:.8em; height:.8em; background:var(--leaf); align-self:center; } +.trail a { color:var(--leaf); text-decoration:none; } +.trail a:hover, .trail a[aria-current] { color:var(--amber); } +.trail .home { color:var(--plum); } +.trail .sep { color:var(--amber); } +.trail .here { color:var(--dim); } +.trail .words { display:flex; flex-wrap:wrap; gap:4px 14px; margin-left:auto; font-size:17px; } + +/* --- the folio --------------------------------------------------------------- */ +.panel { min-width:0; padding:14px; background:repeating-linear-gradient(45deg, var(--stripe) 0 2px, var(--panel) 2px 7px); border:2px solid var(--leaf); box-shadow:6px 6px 0 var(--shadow); } +.content { + background:var(--panel); padding:22px clamp(14px, 4vw, 40px) 30px; + box-shadow:inset 0 0 0 2px var(--leaf), inset 0 0 0 5px var(--panel), inset 0 0 0 6px var(--amber); + font:420 20px/1.55 var(--hand); overflow-wrap:break-word; +} +.content p, .content li { hyphens:auto; text-wrap:pretty; } +.content a { color:var(--amber); } +.content a:hover { color:var(--leaf-hi); } +.content strong { color:var(--leaf); font-weight:640; } +.content h2, .content h1 { font:clamp(30px, 5vw, 44px)/1.1 var(--pixel); color:var(--leaf); text-transform:uppercase; margin:.3em 0 .6em; text-shadow:3px 3px 0 var(--shadow); } +.content h3 { font:26px/1.2 var(--pixel); color:var(--amber); text-transform:uppercase; border-bottom:2px dotted var(--leaf); padding-bottom:4px; margin:1.6em 0 .6em; } +.content h3::before { content:"¶ "; color:var(--leaf); } +.content h4 { font:italic 600 23px/1.25 var(--hand); color:var(--leaf); margin:1.3em 0 .4em; } +.content h5 { font:600 20px var(--hand); color:var(--dim); margin:1em 0 .3em; } + +/* code: a window on the machine, titled in stripes */ +.content code, .content pre, .content kbd, .tag { font-family:var(--mono); } +.content code { font-size:.82em; overflow-wrap:break-word; hyphens:none; } +.content pre { + font-size:15px; line-height:1.45; padding:12px 14px; margin:1em 0; overflow-x:auto; + background:var(--deep); border:2px solid var(--leaf); border-top-width:8px; box-shadow:5px 5px 0 var(--shadow); +} +.content pre code { font-size:inherit; overflow-wrap:normal; } +@media (prefers-color-scheme: dark) { .content pre span { color:inherit !important; } } +.content pre.tutor { font-size:14px; } + +/* tables: rows on alternating tints, a pardes #pairs */ +.content table { width:100%; border-collapse:collapse; margin:1em 0; font-size:.92em; display:block; overflow-x:auto; } +.content tbody, .content thead { display:table; width:100%; } +.content td, .content th { padding:5px 9px; vertical-align:top; text-align:left; } +.content tr:nth-child(odd) td { background:color-mix(in srgb, var(--tag) 70%, transparent); } +.content th { font:17px var(--pixel); color:var(--leaf); } + +/* --- the semantic elements (docs/typ/style.typ) ------------------------------ */ +.key { display:inline-block; padding:0 .3em; border-radius:4px; background:color-mix(in srgb, var(--leaf) 20%, transparent); color:var(--ink); font-size:.82em; white-space:nowrap; line-height:1.4; } +.key .plus, .key .then { color:var(--leaf); opacity:.8; padding:0 .12em; } +.word { background:var(--tag); padding:0 .25em; color:var(--dim); } +.word code { font-size:.82em; color:var(--ink); } +.addr code { color:var(--plum); } +.file code { color:var(--leaf); } +.btn, .chord { display:inline-flex; align-items:center; gap:2px; vertical-align:-.38em; margin-block:-.3em; } +.mouse { width:1.15em; height:1.55em; } +.mouse .off { fill:none; stroke:var(--dim); stroke-width:8; } +.mouse .on { fill:var(--amber); stroke:var(--amber); stroke-width:8; } +.mouse .halo { fill:var(--panel); } +.mouse .wheel.off { fill:var(--panel); stroke-width:5; } +.mouse .wheel.on { stroke-width:5; } +.btn .shift { font-size:.9em; color:var(--amber); } +.chord .then { font-size:.9em; color:var(--dim); } +.tag { margin:.6em 0; padding:4px 8px; background:var(--tag); color:var(--dim); font-size:15px; line-height:1.5; } +.tag .grip { display:inline-block; width:.8em; height:.8em; background:var(--leaf); margin-right:.5em; vertical-align:-.05em; } +.tag .path { color:var(--plum); } +.tag code { font-size:inherit; } + +/* --- contents, landing, footer ---------------------------------------------- */ +.toc { margin:0 0 1.6em; border:2px solid var(--leaf); background:var(--deep); box-shadow:5px 5px 0 var(--amber); } +.toc-bar { padding:2px 10px; font:17px var(--pixel); color:var(--leaf); background:repeating-linear-gradient(0deg, var(--leaf) 0 2px, var(--tag) 2px 4px); } +.toc-bar span { background:var(--tag); padding:0 8px; } +.toc-bar, .toc ol { margin:0; } +.toc ol { padding:8px 14px 10px 2.4em; columns:2 14em; font-size:18px; } +.masthead { font:clamp(56px, 12vw, 104px)/1 var(--pixel) !important; text-shadow:5px 5px 0 var(--amber) !important; margin:.1em 0 .1em !important; } +.pitch { font-style:italic; font-size:22px; color:var(--dim); margin-top:0; } +.clips { display:grid; grid-template-columns:repeat(auto-fit, minmax(min(100%, 380px), 1fr)); gap:22px; margin:1.4em 0; } +.clip { margin:0; border:2px solid var(--leaf); background:var(--deep); box-shadow:5px 5px 0 var(--shadow); } +.clip video, .clip img { display:block; width:100%; height:auto; aspect-ratio:16/10; background:var(--deep); } +.clip figcaption { padding:8px 12px; font-size:18px; color:var(--dim); border-top:2px solid var(--leaf); } +.lead { font-size:23px; line-height:1.5; hyphens:manual; margin:.2em 0 .5em; } +.pitch { font-style:italic; font-size:19px; hyphens:manual; color:var(--dim); margin:0 0 .3em; } +.runs { font-size:18px; color:var(--dim); margin:0 0 1.2em; } +.hero { display:block; width:100%; height:auto; margin:0 0 1.4em; border:2px solid var(--leaf); box-shadow:6px 6px 0 var(--shadow); } +.start { display:flex; flex-wrap:wrap; gap:14px; margin:0 0 .8em; } +.start .go { display:inline-block; padding:8px 16px; font:20px var(--pixel); text-decoration:none; color:var(--leaf); background:var(--tag); border:2px solid var(--leaf); box-shadow:4px 4px 0 var(--amber); } +.start .go:first-child { font-size:24px; color:var(--panel); background:var(--leaf); } +.start .go span { font-size:.75em; opacity:.85; } +.start .go:hover { color:var(--amber); } +.rest { display:flex; flex-wrap:wrap; gap:4px 16px; font:17px var(--pixel); margin:0 0 1.2em; } +.rest a { color:var(--leaf); } +.content h2.install { font-size:30px; } +.glossary { margin:1.4em 0; } +.glossary summary { cursor:pointer; list-style-position:inside; } +.glossary summary h3, .glossary summary p { display:inline; margin:0; } +.glossary summary .count { font:20px var(--pixel); color:var(--dim); margin-left:.4em; } +.vh { position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); white-space:nowrap; } +.colophon { font:15px var(--pixel); color:var(--dim); text-align:center; } +.colophon a { color:var(--leaf); } +.colophon code { font:inherit; } + +@media (max-width:600px) { + .shell { padding:14px 10px 30px; gap:18px; } + .trail { font-size:17px; padding:8px 10px; } + .trail .words { margin-left:0; font-size:15px; } + .panel { padding:7px; } + .content { font-size:18px; } + .content pre { font-size:13px; } +} diff --git a/docs/site/site.typ b/docs/site/site.typ new file mode 100644 index 00000000..03d9a15d --- /dev/null +++ b/docs/site/site.typ @@ -0,0 +1,161 @@ +// The website: one Typst bundle run writes every page, the stylesheet, the +// fonts and the media. Pages come from `chapters` in ../typ/style.typ, so a +// new chapter is a new page; the only prose of its own is the landing page. +// typst compile --root . --format bundle --features bundle,html docs/site/site.typ OUT +// Every link is relative and every page sits at the top of OUT, so the site +// works from file:// and from any static server. +#import "../typ/style.typ": chapters, tutor-path, doc-links, tagline, project + +#let clips = toml("media/clips.toml").clip +#let pages = ( + (id: "index", title: "pardes"), + ..chapters.filter(c => c.file != none).map(c => (id: c.id, title: c.title, file: c.file)), + (id: "tutor", title: "The tutor"), +) +#let href(id) = id + ".html" + +// --- the files that are not pages -------------------------------------------- + +#asset("site.css", read("site.css", encoding: none)) +#for f in ("DepartureMono-Regular.woff2", "CrimsonPro-VF-normal.woff2", "CrimsonPro-VF-italic.woff2", + "LICENSE-DepartureMono.txt", "LICENSE-CrimsonPro.txt") { + asset("fonts/" + f, read("fonts/" + f, encoding: none)) +} +#asset("media/placeholder.svg", read("media/placeholder.svg", encoding: none)) +#for c in clips.filter(c => c.ready) { + for ext in ("webm", "mp4", "png") { + asset("media/" + c.name + "." + ext, read("media/" + c.name + "." + ext, encoding: none)) + } +} + +// --- the shell every page sits in --------------------------------------------- + +/// An HTML element: el("a", (href: "x.html"))[text], or el("li")[text]. +#let el(tag, ..args) = { + let p = args.pos() + html.elem(tag, attrs: if p.len() > 1 { p.first() } else { (:) }, p.last()) +} + +/// The running head: a tagline, the way a pane's tag reads, with the path +/// of this page and the chapters as its words. +#let trail(id, title) = el("header", (class: "trail"))[ + #el("span", (class: "grip", "aria-hidden": "true"))[] + #el("a", (class: "home", href: href("index")))[pardes] + #if id != "index" { + el("span", (class: "sep", "aria-hidden": "true"))[/] + el("span", (class: "here", "aria-current": "page"))[#title] + } + #el("nav", (class: "words", "aria-label": "Chapters"))[ + #for p in pages.slice(1) { + if p.id == id { el("a", (href: href(p.id), "aria-current": "page"))[#p.title] } + else { el("a", (href: href(p.id)))[#p.title] } + } + ] +] + +#let shell(id, title, body) = html.html(lang: "en", { + html.head({ + html.meta(charset: "utf-8") + el("meta", (name: "viewport", content: "width=device-width, initial-scale=1"))[] + el("meta", (name: "color-scheme", content: "dark light"))[] + html.title(if id == "index" { "pardes" } else { title + " · pardes" }) + el("link", (rel: "stylesheet", href: "site.css"))[] + }) + html.body({ + el("div", (class: "shell"))[ + #trail(id, title) + #el("main", (class: "panel", "data-page": id))[ + #el("article", (class: "content"))[#body] + ] + #el("footer", (class: "colophon"))[ + pardes · + #if project.source != none [#el("a", (href: project.source))[source] · ] + #if project.license != none [#project.license · ] + built from `docs/typ` with Typst · + #el("a", (href: "fonts/LICENSE-DepartureMono.txt"))[fonts] (OFL) + ] + ] + }) +}) + +/// The headings of this page only, as a contents list, when there are +/// enough of them to need one. +#let toc(id) = context { + let hs = query(selector(heading.where(level: 2)).after(label("start-" + id)).before(label("end-" + id))) + if hs.len() >= 4 { + el("nav", (class: "toc", "aria-label": "On this page"))[ + #el("div", (class: "toc-bar"))[#el("span")[On this page]] + #el("ol")[#for h in hs { el("li")[#link(h.location(), h.body)] }] + ] + } +} + +// --- the pages ------------------------------------------------------------------ + +#doc-links.update(true) + +#let ready = clips.filter(c => c.ready) +#let start = ( + (id: "guide", label: [Start: the guide], note: [about 15 min]), + (id: "tutor", label: [Hands on: the tutor], note: none), +) + +#document(href("index"), title: "pardes", shell("index", "pardes")[ + #el("h1", (class: "masthead"))[pardes] + #el("p", (class: "lead"))[pardes is an editor and terminal in columns of panes, each + a file, a terminal, a PDF or an image. Click any text to run it or open + it; the keys are helix's; scripts drive it through files.] + #el("p", (class: "pitch"))[#tagline.] + #el("p", (class: "runs"))[It runs in a terminal or in its own window (SDL), on Linux; + macOS and browser builds are in the tree.] + #if ready.len() > 0 { + el("img", (class: "hero", src: "media/" + ready.first().name + ".png", alt: ready.first().caption))[] + } + #el("div", (class: "start"))[ + #for s in start { + el("a", (class: "go", href: href(s.id)))[#s.label#if s.note != none [ #el("span")[(#s.note)]]] + } + ] + #el("p", (class: "rest"))[ + #for p in pages.slice(1).filter(p => p.id not in start.map(s => s.id)) { + el("a", (href: href(p.id)))[#p.title] + } + ] + #el("div", (class: "clips"))[ + #for c in clips { + el("figure", (class: "clip"))[ + #if c.ready { + el("video", (poster: "media/" + c.name + ".png", muted: "", loop: "", playsinline: "", + controls: "", preload: "none"))[ + #el("source", (src: "media/" + c.name + ".webm", type: "video/webm"))[] + #el("source", (src: "media/" + c.name + ".mp4", type: "video/mp4"))[] + ] + } else { + el("img", (src: "media/placeholder.svg", alt: "", width: "640", height: "400"))[] + } + #el("figcaption")[#c.caption] + ] + } + ] + #el("h2", (class: "install"))[Install] + #include "../typ/install.typ" +]) + +#for p in pages.slice(1, -1) { + document(href(p.id), title: p.title, shell(p.id, p.title)[ + #metadata(none) #label("start-" + p.id) + #heading(level: 1, p.title) #label("ch-" + p.id) + #toc(p.id) + #{ + set heading(offset: 1) + include "../typ/" + p.file + } + #metadata(none) #label("end-" + p.id) + ]) +} + +#document(href("tutor"), title: "The tutor", shell("tutor", "The tutor")[ + #heading(level: 1)[The tutor] <tutor> + The tutor as the editor shows it (#raw("Tutor"), or #raw("SPC h t")). + #el("pre", (class: "tutor"))[#read(tutor-path)] +]) diff --git a/docs/typ/README b/docs/typ/README index 048e5d01..522d99ff 100644 --- a/docs/typ/README +++ b/docs/typ/README @@ -10,8 +10,9 @@ docs/typ: the Typst documentation corpus cheatsheet-a4.typ output wrapper: the one-page A4 cheatsheet. book.typ output wrapper: an A4 book, title page, contents, every chapter, then the tutor (src/tutor.txt). - html.typ output wrapper: one HTML page, every chapter under - an anchor named by its id, then the tutor. + ../site/site.typ output wrapper: the website, a page per chapter plus + the landing page and the tutor, in one Typst bundle + run (see docs/site/README). Wrappers hold layout only, never facts. Chapters: `chapters` in style.typ lists them in reading order (guide, setup, @@ -27,7 +28,8 @@ Build (from the repo root): typst compile --ignore-system-fonts docs/typ/cheatsheet-a4.typ zig build cheatsheet -Dplatform=tty --prefix DIR # DIR/share/doc/pardes/cheatsheet.pdf typst compile --root . --ignore-system-fonts docs/typ/book.typ - typst compile --root . --features html --format html docs/typ/html.typ pardes.html + typst compile --root . --format bundle --features bundle,html docs/site/site.typ OUT + zig build site -Dplatform=tty --prefix DIR # DIR/share/doc/pardes/site/ (--root . because book and HTML read the tutor from src/tutor.txt.) The contract for content files diff --git a/docs/typ/book.typ b/docs/typ/book.typ index a89b52cc..7223ea1d 100644 --- a/docs/typ/book.typ +++ b/docs/typ/book.typ @@ -2,7 +2,7 @@ // content files named by `chapters` in style.typ; this is layout only. // typst compile --root . --ignore-system-fonts docs/typ/book.typ // (--root . because the tutor is read from src/tutor.txt.) -#import "style.typ": palette, mono, chapters, tutor-path, doc-links +#import "style.typ": palette, mono, chapters, tutor-path, doc-links, tagline #set document(title: "pardes") #doc-links.update(true) @@ -29,7 +29,7 @@ v(1fr) text(size: 34pt, weight: "bold", fill: palette.tag-name)[pardes] v(4mm) - text(size: 13pt, style: "italic", fill: palette.tag-fg)[acme's tags and three buttons, helix's keys, terminals as panes, the editor as a 9P filesystem] + text(size: 13pt, style: "italic", fill: palette.tag-fg, tagline) v(2fr) }) diff --git a/docs/typ/building.typ b/docs/typ/building.typ index a261a913..ab26e44c 100644 --- a/docs/typ/building.typ +++ b/docs/typ/building.typ @@ -3,6 +3,9 @@ // threads, detached sessions, the 9P engine, the listeners and mounts. #import "style.typ": key, keys, btn, chord, word, tag, addr, file, cmd, doc, pairs +For contributors: how pardes is built, tested and released, and how its +pieces fit. To install it, see #doc("setup", section: "install"). + = Platforms Zig *0.16.0* (`build.zig.zon` pins `minimum_zig_version`). Dependencies are diff --git a/docs/typ/cheatsheet-a4.typ b/docs/typ/cheatsheet-a4.typ index 6f22f723..7694382f 100644 --- a/docs/typ/cheatsheet-a4.typ +++ b/docs/typ/cheatsheet-a4.typ @@ -2,7 +2,7 @@ // cheatsheet.typ and the elements' look in style.typ; this is page, // type and headings only. // typst compile --ignore-system-fonts docs/typ/cheatsheet-a4.typ -#import "style.typ": palette, mono, density +#import "style.typ": palette, mono, density, tagline #set page(paper: "a4", fill: palette.paper, margin: (x: 9mm, y: 8mm), columns: 3) #set columns(gutter: 4.5mm) @@ -21,7 +21,7 @@ block(width: 100%, inset: (bottom: 2pt), stroke: (bottom: 0.6pt + palette.grip), { text(size: 15pt, weight: "bold", fill: palette.tag-name)[pardes] h(1fr) - text(size: 9pt, style: "italic", fill: palette.tag-fg)[acme's tags and three buttons, helix's keys, terminals as panes, the editor as a 9P filesystem] + text(size: 9pt, style: "italic", fill: palette.tag-fg, tagline) })) #show heading.where(level: 2): it => block( width: 100%, fill: palette.tag-bg, inset: (x: 2pt, y: 2pt), above: 0.75em, below: 0.4em, diff --git a/docs/typ/guide.typ b/docs/typ/guide.typ index a7320865..855fcde4 100644 --- a/docs/typ/guide.typ +++ b/docs/typ/guide.typ @@ -1,7 +1,7 @@ // The guide: pardes day to day, the path a newcomer reads once. Edge cases // live in the reference, the pager and the language servers in setup, the // keys on the cheatsheet. -#import "style.typ": key, keys, btn, chord, word, tag, addr, file, cmd, doc, pairs +#import "style.typ": key, keys, btn, chord, word, tag, addr, file, cmd, doc, pairs, glossary pardes is a screen of columns. Each column holds panes, and each pane is a tag (a line of words) over a body: a file, a terminal, a PDF or an image. @@ -9,9 +9,7 @@ Above the columns runs the workspace tag, and each column has a tag of its own. Any text anywhere can be clicked: #btn("B2") runs it, #btn("B3") looks at it. The keys are helix's, in modes. -= Words you'll see <words> - -#pairs( +#glossary("Words you'll see", <words>, [tag], [the line of words over a pane, a column or the screen; every word in it can be clicked], [the pane with the keyboard], [where your keys go; one pane at a time], [the session's directory], [where pardes started, or the directory `pardes DIR` named; workspace and column tags run there], diff --git a/docs/typ/html.typ b/docs/typ/html.typ deleted file mode 100644 index 5f9082cd..00000000 --- a/docs/typ/html.typ +++ /dev/null @@ -1,24 +0,0 @@ -// The HTML documentation: one page holding every topic in reading order, -// each under an anchor named by its id, then the tutor. -// typst compile --root . --features html --format html docs/typ/html.typ pardes.html -// One page because Typst 0.15's HTML export writes one document per run. -#import "style.typ": chapters, tutor-path, doc-links - -#set document(title: "pardes") -#doc-links.update(true) - -#html.elem("nav", { - for ch in chapters [- #link(label("ch-" + ch.id), ch.title)] - [- #link(<tutor>)[The tutor]] -}) - -#for ch in chapters [ - #heading(level: 1, ch.title) #label("ch-" + ch.id) - #if ch.file == none [_This chapter is not written yet._] else { - set heading(offset: 1) - include ch.file - } -] - -= The tutor <tutor> -#raw(read(tutor-path), block: true) diff --git a/docs/typ/install.typ b/docs/typ/install.typ new file mode 100644 index 00000000..9114f44c --- /dev/null +++ b/docs/typ/install.typ @@ -0,0 +1,8 @@ +// Installing pardes, in three lines: included by setup.typ and by the +// site's landing page, so it is written once. +#import "style.typ": cmd + +With Zig 0.16 (the build fetches everything else): +#cmd("zig build -Dplatform=tty --prefix ~/.local # pardes, in a terminal") +#cmd("zig build -Dplatform=gui --prefix ~/.local # pardes-gui, its own window") +Then run `pardes` or `pardes FILE`. diff --git a/docs/typ/setup.typ b/docs/typ/setup.typ index 45767757..1ed6447b 100644 --- a/docs/typ/setup.typ +++ b/docs/typ/setup.typ @@ -5,6 +5,9 @@ pardes runs as it is; a few lines elsewhere make it fit. += Install <install> +#include "install.typ" + = A 9P mount: 9ns <ninens> `9ns`, from cloud9 (`git.sr.ht/~gbrls/cloud9`, whose `zig build` builds it diff --git a/docs/typ/style.typ b/docs/typ/style.typ index cf55ae86..903a8273 100644 --- a/docs/typ/style.typ +++ b/docs/typ/style.typ @@ -56,9 +56,12 @@ (id: "scripting", title: "Scripting", file: "scripting.typ"), (id: "reference", title: "Reference", file: "reference.typ"), (id: "themes", title: "Themes", file: "themes.typ"), - (id: "building", title: "Building", file: "building.typ"), + (id: "building", title: "Building, for contributors", file: "building.typ"), ) +/// The one-line pitch: the A4 sheet's header and the site's landing page. +#let tagline = "acme's tags and three buttons, helix's keys, terminals as panes, the editor as a 9P filesystem" + /// The tutor, shown as it is in the editor (`Tutor`), from the repo root. #let tutor-path = "/src/tutor.txt" @@ -75,7 +78,9 @@ /// pill: a combination's parts joined by a muted +, a sequence's steps by a /// muted › (`Ctrl-w h` reads Ctrl + w › h). #let key(k) = context if html-out() { - html.elem("kbd", k.split(" ").map(t => html.elem("kbd", t)).join(" ")) + let sep(c, cls) = html.elem("span", attrs: (class: cls, "aria-hidden": "true"), c) + let press(t) = (if t.len() > 1 and t.contains("-") { t.split("-") } else { (t,) }).join(sep("+", "plus")) + html.elem("kbd", attrs: (class: "key"), k.split(" ").map(press).join(sep("›", "then"))) } else { let muted(c) = text(fill: palette.grip.transparentize(25%), size: 0.85em, c) let press(t) = (if t.len() > 1 and t.contains("-") { t.split("-") } else { (t,) }).join(muted[+]) @@ -91,6 +96,9 @@ #let keys(..ks) = context ks.pos().map(key).join(if html-out() [ ] else { h(0.25em) }) #let button-names = ("B1": "left button", "B2": "wheel", "B3": "right button") +#let click-names = ("B1": "left", "B2": "middle", "B3": "right") +/// Text that is read and copied but not shown, beside a drawn glyph. +#let hidden(t) = html.elem("span", attrs: (class: "vh"), t) /// A mouse, about cap height: left button, wheel, right button. The parts /// in `pressed` ("B1", "B2", "B3") are filled. @@ -116,10 +124,33 @@ }) } +/// The same mouse as inline SVG for HTML. Each part is classed `on` +/// (pressed) or `off`, and the stylesheet colours them: the pressed part +/// filled in the accent, the rest outlined and muted. +#let mouse-svg(pressed) = { + let cls(b) = if b in pressed { "on" } else { "off" } + let part(d, b) = html.elem("path", attrs: (class: cls(b), d: d)) + html.elem("svg", attrs: (class: "mouse", viewBox: "-6 -6 96 132", "aria-hidden": "true"), { + // taller buttons and a slimmer wheel than on paper, so a pressed + // button shows as a solid block at body size + part("M0 64H84V78A42 42 0 0 1 0 78Z", none) + part("M0 64V42A42 42 0 0 1 42 0V64Z", "B1") + part("M42 0A42 42 0 0 1 84 42V64H42Z", "B3") + // a pressed wheel is drawn wider, so it reads as pressed, not as a seam + let (hx, hw, wx, ww) = if "B2" in pressed { ("27", "30", "31", "22") } else { ("31", "22", "35", "14") } + html.elem("rect", attrs: (class: "halo", x: hx, y: "4", width: hw, height: "48", rx: "13")) + html.elem("rect", attrs: (class: "wheel " + cls("B2"), x: wx, y: "8", width: ww, height: "40", rx: "9")) + }) +} + /// A mouse button; `shift: true` for the button with Shift held. #let btn(b, shift: false) = context if html-out() { let label = (if shift { "shift " } else { "" }) + button-names.at(b) - html.elem("span", attrs: (class: "btn", aria-label: label, title: label), (if shift { "⇧" } else { "" }) + b) + html.elem("span", attrs: (class: "btn", title: label), { + if shift { html.elem("span", attrs: (class: "shift", "aria-hidden": "true"), "⇧") } + mouse-svg((b,)) + hidden((if shift { "Shift-" } else { "" }) + click-names.at(b) + "-click") + }) } else { if shift { text(size: 0.8em, fill: palette.col-grip)[⇧] } mouse((b,)) @@ -130,7 +161,13 @@ /// down, with arrows between. #let chord(..bs) = context if html-out() { let label = bs.pos().map(b => button-names.at(b)).join(" then ") - html.elem("span", attrs: (class: "chord", aria-label: label, title: label), bs.pos().join("-")) + let (held, ..clicks) = bs.pos() + let arrow = html.elem("span", attrs: (class: "then", "aria-hidden": "true"), "›") + let said = "hold " + click-names.at(held) + ", click " + clicks.map(c => click-names.at(c)).join(", then ") + html.elem("span", attrs: (class: "chord", title: label), { + ((mouse-svg((held,)),) + clicks.map(c => mouse-svg((held, c)))).join(arrow) + hidden(said) + }) } else { let (held, ..clicks) = bs.pos() let arrow = text(size: 0.75em, fill: palette.col-grip)[#h(0.5pt)›#h(0.5pt)] @@ -145,6 +182,7 @@ /// A whole tagline: its grip, the path, then its words. #let tag(words, path: none) = context if html-out() { html.elem("div", attrs: (class: "tag"), { + html.elem("span", attrs: (class: "grip", "aria-hidden": "true"))[] if path != none { span("path", path); [ ] } raw(words) }) @@ -215,3 +253,20 @@ } } } + +/// Where pardes lives and under what licence, for the site's footer (and +/// any output that wants them). none until decided. +#let project = (source: none, license: none) + +/// A glossary: like #pairs, under its own heading. On paper the heading +/// and the table; in HTML a <details> that starts closed, its summary the +/// heading and the count, so a reader who knows the words skips them. +#let glossary(title, lbl, ..cells) = context if html-out() { + html.elem("details", attrs: (class: "glossary"), { + html.elem("summary", [#heading(title) #lbl #html.elem("span", attrs: (class: "count"), "(" + str(calc.quo(cells.pos().len(), 2)) + ")")]) + pairs(..cells) + }) +} else { + [#heading(title) #lbl] + pairs(..cells) +} |
