diff options
| author | Gabriel Schneider <[email protected]> | 2026-10-01 12:42:48 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 13:04:28 -0300 |
| commit | f6a511c61d7ef2ff680f70077b236559a595be69 (patch) | |
| tree | 496144bde72ee3d8cad0d84ab1ff110f65b0308d | |
| parent | 0892e3aa5cd46e43b0bb0ac10f47d2ee4f500409 (diff) | |
| download | pardes-f6a511c61d7ef2ff680f70077b236559a595be69.tar.gz pardes-f6a511c61d7ef2ff680f70077b236559a595be69.zip | |
Clicks in the site's clips stop, then click in place with a ring; the hero opens on all three panes; new clips show the PDF and image panes; the Themes gallery has a shot of every themeHEADmain
record_demos.py: a click glides to its target, stops, and presses and
releases with the pointer still (a ring and the button's name on the
press, a pulse after), pausing before anything moves. The hero's README,
main.c and output pane are all up from its first frame. pdf-navigation
and pdf-tint-fit page the Intel SDM vol. 2 (--pdf, read where it is):
PdfSections to CPUID, a search stepped with n, PdfTint's three looks,
PdfFit both ways and a page address. image-pane opens a Mandelbrot PNG
the script draws, then Petscii. `record_demos.py themes` shoots one
scene in each theme of src/themes, in the Themes order, into
docs/site/media/themes with themes.toml (ready).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
104 files changed, 716 insertions, 64 deletions
diff --git a/docs/site/media/captions.txt b/docs/site/media/captions.txt index 55f64d5b..73ad850c 100644 --- a/docs/site/media/captions.txt +++ b/docs/site/media/captions.txt @@ -1,4 +1,7 @@ -text-is-the-interface: Middle-click runs a word; right-click an error opens the file. -terminals-are-panes: A shell is a pane: Ctrl-b, then move over its output. -scripting-over-9p: The session is files: a shell script reads and writes its panes. -diff-review: git diff pages into a pane; right-click a change to open it. +text-is-the-interface: Middle-click runs a word; right-click an error opens its line. +terminals-are-panes: A terminal is a pane: Ctrl-b, then move over its output. +scripting-over-9p: A script drives the session through its virtual filesystem (9P). +diff-review: Right-click a diff line to open that line of the file. +pdf-navigation: A 2,515-page PDF: page, jump to CPUID, search, step hits. +pdf-tint-fit: PdfTint tints the page, PdfFit fits it, a page address jumps. +image-pane: Right-click a picture's name to open it; Petscii redraws it. diff --git a/docs/site/media/clips.toml b/docs/site/media/clips.toml index 9a83d681..3ce07fa3 100644 --- a/docs/site/media/clips.toml +++ b/docs/site/media/clips.toml @@ -23,3 +23,18 @@ ready = true name = "diff-review" caption = "Right-click a diff line to open that line of the file." ready = true + +[[clip]] +name = "pdf-navigation" +caption = "A 2,515-page PDF: page, jump to CPUID, search, step hits." +ready = true + +[[clip]] +name = "pdf-tint-fit" +caption = "PdfTint tints the page, PdfFit fits it, a page address jumps." +ready = true + +[[clip]] +name = "image-pane" +caption = "Right-click a picture's name to open it; Petscii redraws it." +ready = true diff --git a/docs/site/media/diff-review.mp4 b/docs/site/media/diff-review.mp4 Binary files differindex e3bb2f5b..73fa1a76 100644 --- a/docs/site/media/diff-review.mp4 +++ b/docs/site/media/diff-review.mp4 diff --git a/docs/site/media/diff-review.png b/docs/site/media/diff-review.png Binary files differindex fda184e3..76a7dc43 100644 --- a/docs/site/media/diff-review.png +++ b/docs/site/media/diff-review.png diff --git a/docs/site/media/diff-review.webm b/docs/site/media/diff-review.webm Binary files differindex ed201b13..da7756f9 100644 --- a/docs/site/media/diff-review.webm +++ b/docs/site/media/diff-review.webm diff --git a/docs/site/media/image-pane.mp4 b/docs/site/media/image-pane.mp4 Binary files differnew file mode 100644 index 00000000..de60b945 --- /dev/null +++ b/docs/site/media/image-pane.mp4 diff --git a/docs/site/media/image-pane.png b/docs/site/media/image-pane.png Binary files differnew file mode 100644 index 00000000..3f77e2ac --- /dev/null +++ b/docs/site/media/image-pane.png diff --git a/docs/site/media/image-pane.webm b/docs/site/media/image-pane.webm Binary files differnew file mode 100644 index 00000000..46de130b --- /dev/null +++ b/docs/site/media/image-pane.webm diff --git a/docs/site/media/pdf-navigation.mp4 b/docs/site/media/pdf-navigation.mp4 Binary files differnew file mode 100644 index 00000000..f0369f7b --- /dev/null +++ b/docs/site/media/pdf-navigation.mp4 diff --git a/docs/site/media/pdf-navigation.png b/docs/site/media/pdf-navigation.png Binary files differnew file mode 100644 index 00000000..79dd6453 --- /dev/null +++ b/docs/site/media/pdf-navigation.png diff --git a/docs/site/media/pdf-navigation.webm b/docs/site/media/pdf-navigation.webm Binary files differnew file mode 100644 index 00000000..17c8b1b3 --- /dev/null +++ b/docs/site/media/pdf-navigation.webm diff --git a/docs/site/media/pdf-tint-fit.mp4 b/docs/site/media/pdf-tint-fit.mp4 Binary files differnew file mode 100644 index 00000000..a1b4a5a6 --- /dev/null +++ b/docs/site/media/pdf-tint-fit.mp4 diff --git a/docs/site/media/pdf-tint-fit.png b/docs/site/media/pdf-tint-fit.png Binary files differnew file mode 100644 index 00000000..923ab143 --- /dev/null +++ b/docs/site/media/pdf-tint-fit.png diff --git a/docs/site/media/pdf-tint-fit.webm b/docs/site/media/pdf-tint-fit.webm Binary files differnew file mode 100644 index 00000000..e641101f --- /dev/null +++ b/docs/site/media/pdf-tint-fit.webm diff --git a/docs/site/media/scripting-over-9p.webm b/docs/site/media/scripting-over-9p.webm Binary files differindex b6f192b3..bf39a7e8 100644 --- a/docs/site/media/scripting-over-9p.webm +++ b/docs/site/media/scripting-over-9p.webm diff --git a/docs/site/media/terminals-are-panes.webm b/docs/site/media/terminals-are-panes.webm Binary files differindex 3f1ab09e..15ee73fe 100644 --- a/docs/site/media/terminals-are-panes.webm +++ b/docs/site/media/terminals-are-panes.webm diff --git a/docs/site/media/text-is-the-interface.mp4 b/docs/site/media/text-is-the-interface.mp4 Binary files differindex 19545731..253d1f39 100644 --- a/docs/site/media/text-is-the-interface.mp4 +++ b/docs/site/media/text-is-the-interface.mp4 diff --git a/docs/site/media/text-is-the-interface.png b/docs/site/media/text-is-the-interface.png Binary files differindex 81a73aee..b0d40b15 100644 --- a/docs/site/media/text-is-the-interface.png +++ b/docs/site/media/text-is-the-interface.png diff --git a/docs/site/media/text-is-the-interface.webm b/docs/site/media/text-is-the-interface.webm Binary files differindex 88c02d11..07cf62e6 100644 --- a/docs/site/media/text-is-the-interface.webm +++ b/docs/site/media/text-is-the-interface.webm diff --git a/docs/site/media/themes/acme.png b/docs/site/media/themes/acme.png Binary files differnew file mode 100644 index 00000000..da647db1 --- /dev/null +++ b/docs/site/media/themes/acme.png diff --git a/docs/site/media/themes/alucard.png b/docs/site/media/themes/alucard.png Binary files differnew file mode 100644 index 00000000..dab6ce80 --- /dev/null +++ b/docs/site/media/themes/alucard.png diff --git a/docs/site/media/themes/atelier.png b/docs/site/media/themes/atelier.png Binary files differnew file mode 100644 index 00000000..a8c36f2a --- /dev/null +++ b/docs/site/media/themes/atelier.png diff --git a/docs/site/media/themes/ayu_dark.png b/docs/site/media/themes/ayu_dark.png Binary files differnew file mode 100644 index 00000000..711ed8aa --- /dev/null +++ b/docs/site/media/themes/ayu_dark.png diff --git a/docs/site/media/themes/ayu_light.png b/docs/site/media/themes/ayu_light.png Binary files differnew file mode 100644 index 00000000..0ddcb9af --- /dev/null +++ b/docs/site/media/themes/ayu_light.png diff --git a/docs/site/media/themes/ayu_mirage.png b/docs/site/media/themes/ayu_mirage.png Binary files differnew file mode 100644 index 00000000..56676d1f --- /dev/null +++ b/docs/site/media/themes/ayu_mirage.png diff --git a/docs/site/media/themes/catppuccin_frappe.png b/docs/site/media/themes/catppuccin_frappe.png Binary files differnew file mode 100644 index 00000000..58c13dbe --- /dev/null +++ b/docs/site/media/themes/catppuccin_frappe.png diff --git a/docs/site/media/themes/catppuccin_latte.png b/docs/site/media/themes/catppuccin_latte.png Binary files differnew file mode 100644 index 00000000..27e58ce0 --- /dev/null +++ b/docs/site/media/themes/catppuccin_latte.png diff --git a/docs/site/media/themes/catppuccin_macchiato.png b/docs/site/media/themes/catppuccin_macchiato.png Binary files differnew file mode 100644 index 00000000..fb32c742 --- /dev/null +++ b/docs/site/media/themes/catppuccin_macchiato.png diff --git a/docs/site/media/themes/catppuccin_mocha.png b/docs/site/media/themes/catppuccin_mocha.png Binary files differnew file mode 100644 index 00000000..e16e4224 --- /dev/null +++ b/docs/site/media/themes/catppuccin_mocha.png diff --git a/docs/site/media/themes/clay.png b/docs/site/media/themes/clay.png Binary files differnew file mode 100644 index 00000000..117b731d --- /dev/null +++ b/docs/site/media/themes/clay.png diff --git a/docs/site/media/themes/dark.png b/docs/site/media/themes/dark.png Binary files differnew file mode 100644 index 00000000..6346759e --- /dev/null +++ b/docs/site/media/themes/dark.png diff --git a/docs/site/media/themes/dark_plus.png b/docs/site/media/themes/dark_plus.png Binary files differnew file mode 100644 index 00000000..6a750655 --- /dev/null +++ b/docs/site/media/themes/dark_plus.png diff --git a/docs/site/media/themes/daybreak.png b/docs/site/media/themes/daybreak.png Binary files differnew file mode 100644 index 00000000..ca3dcc5c --- /dev/null +++ b/docs/site/media/themes/daybreak.png diff --git a/docs/site/media/themes/doom_one.png b/docs/site/media/themes/doom_one.png Binary files differnew file mode 100644 index 00000000..95f13135 --- /dev/null +++ b/docs/site/media/themes/doom_one.png diff --git a/docs/site/media/themes/doom_one_light.png b/docs/site/media/themes/doom_one_light.png Binary files differnew file mode 100644 index 00000000..c8c4c69a --- /dev/null +++ b/docs/site/media/themes/doom_one_light.png diff --git a/docs/site/media/themes/dracula.png b/docs/site/media/themes/dracula.png Binary files differnew file mode 100644 index 00000000..64eea04a --- /dev/null +++ b/docs/site/media/themes/dracula.png diff --git a/docs/site/media/themes/dracula_soft.png b/docs/site/media/themes/dracula_soft.png Binary files differnew file mode 100644 index 00000000..9ef81583 --- /dev/null +++ b/docs/site/media/themes/dracula_soft.png diff --git a/docs/site/media/themes/duckbones.png b/docs/site/media/themes/duckbones.png Binary files differnew file mode 100644 index 00000000..9809ab36 --- /dev/null +++ b/docs/site/media/themes/duckbones.png diff --git a/docs/site/media/themes/dusk.png b/docs/site/media/themes/dusk.png Binary files differnew file mode 100644 index 00000000..0d95b59c --- /dev/null +++ b/docs/site/media/themes/dusk.png diff --git a/docs/site/media/themes/forestbones_dark.png b/docs/site/media/themes/forestbones_dark.png Binary files differnew file mode 100644 index 00000000..ea399605 --- /dev/null +++ b/docs/site/media/themes/forestbones_dark.png diff --git a/docs/site/media/themes/forestbones_light.png b/docs/site/media/themes/forestbones_light.png Binary files differnew file mode 100644 index 00000000..862858de --- /dev/null +++ b/docs/site/media/themes/forestbones_light.png diff --git a/docs/site/media/themes/forge.png b/docs/site/media/themes/forge.png Binary files differnew file mode 100644 index 00000000..bab91d48 --- /dev/null +++ b/docs/site/media/themes/forge.png diff --git a/docs/site/media/themes/forge_black.png b/docs/site/media/themes/forge_black.png Binary files differnew file mode 100644 index 00000000..e09320ed --- /dev/null +++ b/docs/site/media/themes/forge_black.png diff --git a/docs/site/media/themes/forge_soft.png b/docs/site/media/themes/forge_soft.png Binary files differnew file mode 100644 index 00000000..1b9fb2fb --- /dev/null +++ b/docs/site/media/themes/forge_soft.png diff --git a/docs/site/media/themes/harvest.png b/docs/site/media/themes/harvest.png Binary files differnew file mode 100644 index 00000000..b95e8c76 --- /dev/null +++ b/docs/site/media/themes/harvest.png diff --git a/docs/site/media/themes/helix.png b/docs/site/media/themes/helix.png Binary files differnew file mode 100644 index 00000000..6c353815 --- /dev/null +++ b/docs/site/media/themes/helix.png diff --git a/docs/site/media/themes/ink.png b/docs/site/media/themes/ink.png Binary files differnew file mode 100644 index 00000000..314610db --- /dev/null +++ b/docs/site/media/themes/ink.png diff --git a/docs/site/media/themes/kanagawabones.png b/docs/site/media/themes/kanagawabones.png Binary files differnew file mode 100644 index 00000000..c2c58ec9 --- /dev/null +++ b/docs/site/media/themes/kanagawabones.png diff --git a/docs/site/media/themes/lagoon.png b/docs/site/media/themes/lagoon.png Binary files differnew file mode 100644 index 00000000..bdd3dd49 --- /dev/null +++ b/docs/site/media/themes/lagoon.png diff --git a/docs/site/media/themes/lapis.png b/docs/site/media/themes/lapis.png Binary files differnew file mode 100644 index 00000000..2bc4024a --- /dev/null +++ b/docs/site/media/themes/lapis.png diff --git a/docs/site/media/themes/lapis_plain.png b/docs/site/media/themes/lapis_plain.png Binary files differnew file mode 100644 index 00000000..7315c2a0 --- /dev/null +++ b/docs/site/media/themes/lapis_plain.png diff --git a/docs/site/media/themes/monokai.png b/docs/site/media/themes/monokai.png Binary files differnew file mode 100644 index 00000000..4b4854b8 --- /dev/null +++ b/docs/site/media/themes/monokai.png diff --git a/docs/site/media/themes/monokai_pro.png b/docs/site/media/themes/monokai_pro.png Binary files differnew file mode 100644 index 00000000..e8036053 --- /dev/null +++ b/docs/site/media/themes/monokai_pro.png diff --git a/docs/site/media/themes/monokai_pro_classic.png b/docs/site/media/themes/monokai_pro_classic.png Binary files differnew file mode 100644 index 00000000..4e25319c --- /dev/null +++ b/docs/site/media/themes/monokai_pro_classic.png diff --git a/docs/site/media/themes/monokai_pro_light.png b/docs/site/media/themes/monokai_pro_light.png Binary files differnew file mode 100644 index 00000000..ee2f1e4c --- /dev/null +++ b/docs/site/media/themes/monokai_pro_light.png diff --git a/docs/site/media/themes/monokai_pro_light_sun.png b/docs/site/media/themes/monokai_pro_light_sun.png Binary files differnew file mode 100644 index 00000000..67e6e807 --- /dev/null +++ b/docs/site/media/themes/monokai_pro_light_sun.png diff --git a/docs/site/media/themes/monokai_pro_machine.png b/docs/site/media/themes/monokai_pro_machine.png Binary files differnew file mode 100644 index 00000000..3f683a68 --- /dev/null +++ b/docs/site/media/themes/monokai_pro_machine.png diff --git a/docs/site/media/themes/monokai_pro_octagon.png b/docs/site/media/themes/monokai_pro_octagon.png Binary files differnew file mode 100644 index 00000000..bc7a831e --- /dev/null +++ b/docs/site/media/themes/monokai_pro_octagon.png diff --git a/docs/site/media/themes/monokai_pro_ristretto.png b/docs/site/media/themes/monokai_pro_ristretto.png Binary files differnew file mode 100644 index 00000000..06429245 --- /dev/null +++ b/docs/site/media/themes/monokai_pro_ristretto.png diff --git a/docs/site/media/themes/monokai_pro_spectrum.png b/docs/site/media/themes/monokai_pro_spectrum.png Binary files differnew file mode 100644 index 00000000..c6e49f4c --- /dev/null +++ b/docs/site/media/themes/monokai_pro_spectrum.png diff --git a/docs/site/media/themes/neobones_dark.png b/docs/site/media/themes/neobones_dark.png Binary files differnew file mode 100644 index 00000000..026400de --- /dev/null +++ b/docs/site/media/themes/neobones_dark.png diff --git a/docs/site/media/themes/neobones_light.png b/docs/site/media/themes/neobones_light.png Binary files differnew file mode 100644 index 00000000..62d26b3a --- /dev/null +++ b/docs/site/media/themes/neobones_light.png diff --git a/docs/site/media/themes/nord.png b/docs/site/media/themes/nord.png Binary files differnew file mode 100644 index 00000000..d5c903b2 --- /dev/null +++ b/docs/site/media/themes/nord.png diff --git a/docs/site/media/themes/nordbones.png b/docs/site/media/themes/nordbones.png Binary files differnew file mode 100644 index 00000000..272ad19d --- /dev/null +++ b/docs/site/media/themes/nordbones.png diff --git a/docs/site/media/themes/orchard.png b/docs/site/media/themes/orchard.png Binary files differnew file mode 100644 index 00000000..623fde75 --- /dev/null +++ b/docs/site/media/themes/orchard.png diff --git a/docs/site/media/themes/orchard_black.png b/docs/site/media/themes/orchard_black.png Binary files differnew file mode 100644 index 00000000..02d78992 --- /dev/null +++ b/docs/site/media/themes/orchard_black.png diff --git a/docs/site/media/themes/paper.png b/docs/site/media/themes/paper.png Binary files differnew file mode 100644 index 00000000..59919d80 --- /dev/null +++ b/docs/site/media/themes/paper.png diff --git a/docs/site/media/themes/rose_pine.png b/docs/site/media/themes/rose_pine.png Binary files differnew file mode 100644 index 00000000..9dbf1d76 --- /dev/null +++ b/docs/site/media/themes/rose_pine.png diff --git a/docs/site/media/themes/rose_pine_dawn.png b/docs/site/media/themes/rose_pine_dawn.png Binary files differnew file mode 100644 index 00000000..8a062630 --- /dev/null +++ b/docs/site/media/themes/rose_pine_dawn.png diff --git a/docs/site/media/themes/rose_pine_moon.png b/docs/site/media/themes/rose_pine_moon.png Binary files differnew file mode 100644 index 00000000..ee93f871 --- /dev/null +++ b/docs/site/media/themes/rose_pine_moon.png diff --git a/docs/site/media/themes/rosebones_dark.png b/docs/site/media/themes/rosebones_dark.png Binary files differnew file mode 100644 index 00000000..19992fea --- /dev/null +++ b/docs/site/media/themes/rosebones_dark.png diff --git a/docs/site/media/themes/rosebones_light.png b/docs/site/media/themes/rosebones_light.png Binary files differnew file mode 100644 index 00000000..a879dc55 --- /dev/null +++ b/docs/site/media/themes/rosebones_light.png diff --git a/docs/site/media/themes/seoul256_dark_233.png b/docs/site/media/themes/seoul256_dark_233.png Binary files differnew file mode 100644 index 00000000..a757640b --- /dev/null +++ b/docs/site/media/themes/seoul256_dark_233.png diff --git a/docs/site/media/themes/seoul256_dark_234.png b/docs/site/media/themes/seoul256_dark_234.png Binary files differnew file mode 100644 index 00000000..a34b3f2a --- /dev/null +++ b/docs/site/media/themes/seoul256_dark_234.png diff --git a/docs/site/media/themes/seoul256_dark_235.png b/docs/site/media/themes/seoul256_dark_235.png Binary files differnew file mode 100644 index 00000000..d4c69938 --- /dev/null +++ b/docs/site/media/themes/seoul256_dark_235.png diff --git a/docs/site/media/themes/seoul256_dark_236.png b/docs/site/media/themes/seoul256_dark_236.png Binary files differnew file mode 100644 index 00000000..8f1c48ce --- /dev/null +++ b/docs/site/media/themes/seoul256_dark_236.png diff --git a/docs/site/media/themes/seoul256_dark_237.png b/docs/site/media/themes/seoul256_dark_237.png Binary files differnew file mode 100644 index 00000000..3816b258 --- /dev/null +++ b/docs/site/media/themes/seoul256_dark_237.png diff --git a/docs/site/media/themes/seoul256_dark_238.png b/docs/site/media/themes/seoul256_dark_238.png Binary files differnew file mode 100644 index 00000000..9553da5f --- /dev/null +++ b/docs/site/media/themes/seoul256_dark_238.png diff --git a/docs/site/media/themes/seoul256_dark_239.png b/docs/site/media/themes/seoul256_dark_239.png Binary files differnew file mode 100644 index 00000000..8109f756 --- /dev/null +++ b/docs/site/media/themes/seoul256_dark_239.png diff --git a/docs/site/media/themes/seoul256_light_252.png b/docs/site/media/themes/seoul256_light_252.png Binary files differnew file mode 100644 index 00000000..7f01a1ab --- /dev/null +++ b/docs/site/media/themes/seoul256_light_252.png diff --git a/docs/site/media/themes/seoul256_light_253.png b/docs/site/media/themes/seoul256_light_253.png Binary files differnew file mode 100644 index 00000000..5b9d2a43 --- /dev/null +++ b/docs/site/media/themes/seoul256_light_253.png diff --git a/docs/site/media/themes/seoul256_light_254.png b/docs/site/media/themes/seoul256_light_254.png Binary files differnew file mode 100644 index 00000000..125546f7 --- /dev/null +++ b/docs/site/media/themes/seoul256_light_254.png diff --git a/docs/site/media/themes/seoul256_light_255.png b/docs/site/media/themes/seoul256_light_255.png Binary files differnew file mode 100644 index 00000000..06b1e3fc --- /dev/null +++ b/docs/site/media/themes/seoul256_light_255.png diff --git a/docs/site/media/themes/seoul256_light_256.png b/docs/site/media/themes/seoul256_light_256.png Binary files differnew file mode 100644 index 00000000..ce4ee4da --- /dev/null +++ b/docs/site/media/themes/seoul256_light_256.png diff --git a/docs/site/media/themes/seoulbones_dark.png b/docs/site/media/themes/seoulbones_dark.png Binary files differnew file mode 100644 index 00000000..74ea8ebc --- /dev/null +++ b/docs/site/media/themes/seoulbones_dark.png diff --git a/docs/site/media/themes/seoulbones_light.png b/docs/site/media/themes/seoulbones_light.png Binary files differnew file mode 100644 index 00000000..7591721a --- /dev/null +++ b/docs/site/media/themes/seoulbones_light.png diff --git a/docs/site/media/themes/solarium.png b/docs/site/media/themes/solarium.png Binary files differnew file mode 100644 index 00000000..f24a111e --- /dev/null +++ b/docs/site/media/themes/solarium.png diff --git a/docs/site/media/themes/solarized_dark.png b/docs/site/media/themes/solarized_dark.png Binary files differnew file mode 100644 index 00000000..7df0ef41 --- /dev/null +++ b/docs/site/media/themes/solarized_dark.png diff --git a/docs/site/media/themes/solarized_light.png b/docs/site/media/themes/solarized_light.png Binary files differnew file mode 100644 index 00000000..40b88ef3 --- /dev/null +++ b/docs/site/media/themes/solarized_light.png diff --git a/docs/site/media/themes/spectrum.png b/docs/site/media/themes/spectrum.png Binary files differnew file mode 100644 index 00000000..111f0913 --- /dev/null +++ b/docs/site/media/themes/spectrum.png diff --git a/docs/site/media/themes/themes.toml b/docs/site/media/themes/themes.toml index 23df1e97..ed45b075 100644 --- a/docs/site/media/themes/themes.toml +++ b/docs/site/media/themes/themes.toml @@ -1,7 +1,6 @@ -# The Themes page gallery, in NextColor's order: media/themes/<file> for -# each. `ready = false` shows placeholder.svg under each name, so the site -# builds before the screenshots exist; set it true (or drop it) once they do. -ready = false +# The Themes page gallery, in the order the Themes builtin walks them: +# media/themes/<file> for each, made by test/record_demos.py themes. +ready = true [[theme]] name = "orchard" @@ -77,3 +76,343 @@ kind = "dark" name = "orchard_black" file = "orchard_black.png" kind = "dark" + +[[theme]] +name = "acme" +file = "acme.png" +kind = "light" + +[[theme]] +name = "lapis" +file = "lapis.png" +kind = "dark" + +[[theme]] +name = "lapis_plain" +file = "lapis_plain.png" +kind = "dark" + +[[theme]] +name = "dark_plus" +file = "dark_plus.png" +kind = "dark" + +[[theme]] +name = "solarized_dark" +file = "solarized_dark.png" +kind = "dark" + +[[theme]] +name = "solarized_light" +file = "solarized_light.png" +kind = "light" + +[[theme]] +name = "dracula" +file = "dracula.png" +kind = "dark" + +[[theme]] +name = "dracula_soft" +file = "dracula_soft.png" +kind = "dark" + +[[theme]] +name = "alucard" +file = "alucard.png" +kind = "light" + +[[theme]] +name = "monokai" +file = "monokai.png" +kind = "dark" + +[[theme]] +name = "monokai_pro" +file = "monokai_pro.png" +kind = "dark" + +[[theme]] +name = "monokai_pro_classic" +file = "monokai_pro_classic.png" +kind = "dark" + +[[theme]] +name = "monokai_pro_machine" +file = "monokai_pro_machine.png" +kind = "dark" + +[[theme]] +name = "monokai_pro_octagon" +file = "monokai_pro_octagon.png" +kind = "dark" + +[[theme]] +name = "monokai_pro_ristretto" +file = "monokai_pro_ristretto.png" +kind = "dark" + +[[theme]] +name = "monokai_pro_spectrum" +file = "monokai_pro_spectrum.png" +kind = "dark" + +[[theme]] +name = "monokai_pro_light" +file = "monokai_pro_light.png" +kind = "light" + +[[theme]] +name = "monokai_pro_light_sun" +file = "monokai_pro_light_sun.png" +kind = "light" + +[[theme]] +name = "tokyonight_night" +file = "tokyonight_night.png" +kind = "dark" + +[[theme]] +name = "tokyonight_storm" +file = "tokyonight_storm.png" +kind = "dark" + +[[theme]] +name = "tokyonight_moon" +file = "tokyonight_moon.png" +kind = "dark" + +[[theme]] +name = "tokyonight_day" +file = "tokyonight_day.png" +kind = "light" + +[[theme]] +name = "rose_pine" +file = "rose_pine.png" +kind = "dark" + +[[theme]] +name = "rose_pine_moon" +file = "rose_pine_moon.png" +kind = "dark" + +[[theme]] +name = "rose_pine_dawn" +file = "rose_pine_dawn.png" +kind = "light" + +[[theme]] +name = "catppuccin_latte" +file = "catppuccin_latte.png" +kind = "light" + +[[theme]] +name = "catppuccin_frappe" +file = "catppuccin_frappe.png" +kind = "dark" + +[[theme]] +name = "catppuccin_macchiato" +file = "catppuccin_macchiato.png" +kind = "dark" + +[[theme]] +name = "catppuccin_mocha" +file = "catppuccin_mocha.png" +kind = "dark" + +[[theme]] +name = "seoul256_dark_233" +file = "seoul256_dark_233.png" +kind = "dark" + +[[theme]] +name = "seoul256_dark_234" +file = "seoul256_dark_234.png" +kind = "dark" + +[[theme]] +name = "seoul256_dark_235" +file = "seoul256_dark_235.png" +kind = "dark" + +[[theme]] +name = "seoul256_dark_236" +file = "seoul256_dark_236.png" +kind = "dark" + +[[theme]] +name = "seoul256_dark_237" +file = "seoul256_dark_237.png" +kind = "dark" + +[[theme]] +name = "seoul256_dark_238" +file = "seoul256_dark_238.png" +kind = "dark" + +[[theme]] +name = "seoul256_dark_239" +file = "seoul256_dark_239.png" +kind = "dark" + +[[theme]] +name = "seoul256_light_252" +file = "seoul256_light_252.png" +kind = "light" + +[[theme]] +name = "seoul256_light_253" +file = "seoul256_light_253.png" +kind = "light" + +[[theme]] +name = "seoul256_light_254" +file = "seoul256_light_254.png" +kind = "light" + +[[theme]] +name = "seoul256_light_255" +file = "seoul256_light_255.png" +kind = "light" + +[[theme]] +name = "seoul256_light_256" +file = "seoul256_light_256.png" +kind = "light" + +[[theme]] +name = "zenbones_light" +file = "zenbones_light.png" +kind = "light" + +[[theme]] +name = "zenbones_dark" +file = "zenbones_dark.png" +kind = "dark" + +[[theme]] +name = "neobones_light" +file = "neobones_light.png" +kind = "light" + +[[theme]] +name = "neobones_dark" +file = "neobones_dark.png" +kind = "dark" + +[[theme]] +name = "vimbones" +file = "vimbones.png" +kind = "light" + +[[theme]] +name = "forestbones_light" +file = "forestbones_light.png" +kind = "light" + +[[theme]] +name = "forestbones_dark" +file = "forestbones_dark.png" +kind = "dark" + +[[theme]] +name = "nordbones" +file = "nordbones.png" +kind = "dark" + +[[theme]] +name = "rosebones_light" +file = "rosebones_light.png" +kind = "light" + +[[theme]] +name = "rosebones_dark" +file = "rosebones_dark.png" +kind = "dark" + +[[theme]] +name = "tokyobones_light" +file = "tokyobones_light.png" +kind = "light" + +[[theme]] +name = "tokyobones_dark" +file = "tokyobones_dark.png" +kind = "dark" + +[[theme]] +name = "seoulbones_light" +file = "seoulbones_light.png" +kind = "light" + +[[theme]] +name = "seoulbones_dark" +file = "seoulbones_dark.png" +kind = "dark" + +[[theme]] +name = "duckbones" +file = "duckbones.png" +kind = "dark" + +[[theme]] +name = "zenburned" +file = "zenburned.png" +kind = "dark" + +[[theme]] +name = "zenwritten_light" +file = "zenwritten_light.png" +kind = "light" + +[[theme]] +name = "zenwritten_dark" +file = "zenwritten_dark.png" +kind = "dark" + +[[theme]] +name = "kanagawabones" +file = "kanagawabones.png" +kind = "dark" + +[[theme]] +name = "nord" +file = "nord.png" +kind = "dark" + +[[theme]] +name = "ayu_dark" +file = "ayu_dark.png" +kind = "dark" + +[[theme]] +name = "ayu_mirage" +file = "ayu_mirage.png" +kind = "dark" + +[[theme]] +name = "ayu_light" +file = "ayu_light.png" +kind = "light" + +[[theme]] +name = "doom_one" +file = "doom_one.png" +kind = "dark" + +[[theme]] +name = "doom_one_light" +file = "doom_one_light.png" +kind = "light" + +[[theme]] +name = "helix" +file = "helix.png" +kind = "dark" + +[[theme]] +name = "dark" +file = "dark.png" +kind = "dark" diff --git a/docs/site/media/themes/tokyobones_dark.png b/docs/site/media/themes/tokyobones_dark.png Binary files differnew file mode 100644 index 00000000..f873726d --- /dev/null +++ b/docs/site/media/themes/tokyobones_dark.png diff --git a/docs/site/media/themes/tokyobones_light.png b/docs/site/media/themes/tokyobones_light.png Binary files differnew file mode 100644 index 00000000..211a30ff --- /dev/null +++ b/docs/site/media/themes/tokyobones_light.png diff --git a/docs/site/media/themes/tokyonight_day.png b/docs/site/media/themes/tokyonight_day.png Binary files differnew file mode 100644 index 00000000..cdbef103 --- /dev/null +++ b/docs/site/media/themes/tokyonight_day.png diff --git a/docs/site/media/themes/tokyonight_moon.png b/docs/site/media/themes/tokyonight_moon.png Binary files differnew file mode 100644 index 00000000..d35bf419 --- /dev/null +++ b/docs/site/media/themes/tokyonight_moon.png diff --git a/docs/site/media/themes/tokyonight_night.png b/docs/site/media/themes/tokyonight_night.png Binary files differnew file mode 100644 index 00000000..5497275b --- /dev/null +++ b/docs/site/media/themes/tokyonight_night.png diff --git a/docs/site/media/themes/tokyonight_storm.png b/docs/site/media/themes/tokyonight_storm.png Binary files differnew file mode 100644 index 00000000..5e62b9e2 --- /dev/null +++ b/docs/site/media/themes/tokyonight_storm.png diff --git a/docs/site/media/themes/vimbones.png b/docs/site/media/themes/vimbones.png Binary files differnew file mode 100644 index 00000000..134fd582 --- /dev/null +++ b/docs/site/media/themes/vimbones.png diff --git a/docs/site/media/themes/zenbones_dark.png b/docs/site/media/themes/zenbones_dark.png Binary files differnew file mode 100644 index 00000000..d7e8315c --- /dev/null +++ b/docs/site/media/themes/zenbones_dark.png diff --git a/docs/site/media/themes/zenbones_light.png b/docs/site/media/themes/zenbones_light.png Binary files differnew file mode 100644 index 00000000..22c2da48 --- /dev/null +++ b/docs/site/media/themes/zenbones_light.png diff --git a/docs/site/media/themes/zenburned.png b/docs/site/media/themes/zenburned.png Binary files differnew file mode 100644 index 00000000..3b97815b --- /dev/null +++ b/docs/site/media/themes/zenburned.png diff --git a/docs/site/media/themes/zenwritten_dark.png b/docs/site/media/themes/zenwritten_dark.png Binary files differnew file mode 100644 index 00000000..6421245f --- /dev/null +++ b/docs/site/media/themes/zenwritten_dark.png diff --git a/docs/site/media/themes/zenwritten_light.png b/docs/site/media/themes/zenwritten_light.png Binary files differnew file mode 100644 index 00000000..28f3a144 --- /dev/null +++ b/docs/site/media/themes/zenwritten_light.png diff --git a/test/record_demos.py b/test/record_demos.py index 655ec475..71bf1055 100644 --- a/test/record_demos.py +++ b/test/record_demos.py @@ -1,16 +1,23 @@ #!/usr/bin/env python3 -"""Record the documentation site's landing-page clips from a hidden window. +"""Record the documentation site's clips and theme shots from a hidden window. zig build -Dplatform=gui -Doptimize=ReleaseSafe --prefix <scratch dir> -python3 -B test/record_demos.py <scratch dir>/bin/pardes-gui [clip ...] +python3 -B test/record_demos.py <scratch dir>/bin/pardes-gui [clip ... | themes] Each clip drives a throwaway session (PARDES_TEST: an offscreen capture, the virtual clock) through its stdin's window-pixel mouse events, its keys and its 9P files, keeps the settled frame after every step, and lays the -frames out on a timeline with a drawn pointer. ffmpeg encodes the timeline -to docs/site/media/<clip>.webm and .mp4, with <clip>.png as its poster and -captions.txt beside them. Needs a Wayland display for the hidden window, -gcc and git for the demo project, and 9ns (cloud9) for the scripting clip. +frames out on a timeline with a drawn pointer: a click glides there, +stops, and presses and releases still, a ring and the button's name drawn. +ffmpeg encodes the timeline to docs/site/media/<clip>.webm and .mp4, with +<clip>.png as its poster and captions.txt beside them (clips.toml, which +the site reads, is kept by hand). `themes` instead writes +docs/site/media/themes/<theme>.png for each theme of src/themes, in the +Themes builtin's order, and themes.toml. + +Needs a Wayland display for the hidden window, gcc and git for the demo +project, 9ns (cloud9) for the scripting clip, Pillow and numpy, and the +Intel SDM vol. 2 for the PDF clips (--pdf; read where it is, never copied). Frames and the intermediate video go under --work (default ../.scratch/rec/work, beside the checkout). The demo project lives at @@ -241,6 +248,19 @@ class Session: cw, ch = w / self.cols, h / self.rows return int(start * cw + (col - start + offset) * TAG_W), int((row + .55) * ch) + def tag_word(self, serial, word): + """The pixel over the middle of `word` in pane `serial`'s tag, which + may run past the grid's columns (/screen cuts a tag at its width, + the window wraps it at its own): its rows hold (width - 3 cells) / + TAG_W characters each.""" + tag = self.tag(serial) + w, h = self.size() + cw, ch = w / self.cols, h / self.rows + per_row = int((w - 3 * cw) // TAG_W) + top = self.find(tag[:min(len(tag), self.cols - 4)], 0)[1] + at = tag.index(word) + len(word) // 2 + return int(3 * cw + (at % per_row + .5) * TAG_W), int((top + at // per_row + .55) * ch) + def size(self): with open(self.caps / 'latest.ppm', 'rb') as f: f.readline() @@ -280,37 +300,43 @@ class Session: # the timeline - def shot(self, seconds, poster=False): + def shot(self, seconds, poster=False, ring=None, ring_button=None): """Hold the settled frame for `seconds`, the pointer where it is. - `poster` marks the frame drawn as the clip's still.""" + `poster` marks the frame drawn as the clip's still; `ring` draws a + click there ('press' while held, 'pulse' just after).""" data = self.settle() path = self.frames / f'{len(self.shots):04d}.ppm' path.write_bytes(data) self.shots.append(dict(frame=path, secs=seconds, start=self.pointer, end=self.pointer, - button=self.button, badge=self.badge, poster=poster)) + button=self.button, badge=self.badge, poster=poster, ring=ring, + ring_button=ring_button or self.button)) def glide(self, x, y, seconds=.6): """Glide the drawn pointer to (x, y) over the frame on screen, then move the window's pointer there (dragging any button held).""" if self.shots and self.pointer is not None: last = self.shots[-1] - self.shots.append(dict(last, secs=seconds, start=self.pointer, end=(x, y), poster=False)) + self.shots.append(dict(last, secs=seconds, start=self.pointer, end=(x, y), poster=False, ring=None)) self.pointer = (x, y) self.mouse('motion', self.button or 0, x, y) - def sweep(self, button, start, end, hold=.5): - """Press `button` (SDL: 1 left, 2 middle, 3 right) at `start`, - drag it to `end` and release: the sweep acme's B2 and B3 take.""" - if self.pointer != start: - self.glide(*start) - self.mouse('down', button, *start) + def click(self, button, x, y, done=None, what='the click', rest=.4, hold=.35, after=1.0): + """Glide to (x, y) and stop; a beat later press `button` (SDL: 1 + left, 2 middle, 3 right) and release it with the pointer still, a + ring and the button's name drawn on the press; once `done()` says + the click took, hold the result `after` seconds, the ring pulsing + out, before anything moves again.""" + if self.pointer != (x, y): + self.glide(x, y) + self.shot(rest) + self.mouse('down', button, x, y) self.button = button - self.shot(.15) - if end != start: - self.glide(*end, seconds=.3) - self.shot(hold) - self.mouse('up', button, *self.pointer) + self.shot(hold, ring='press') + self.mouse('up', button, x, y) self.button = None + if done is not None: + self.wait(done, what) + self.shot(after, ring='pulse', ring_button=button) def press(self, keys, per_key=.4): """Send each (key bytes, badge) of `keys`, a frame a key with the @@ -360,21 +386,31 @@ class Painter: self.font = ImageFont.truetype(str(BADGE_FONT), 24) self.size = size - def paint(self, img, pointer, button, badge): + def paint(self, img, pointer, badge, ring=None, button=None, since=0.0): + """`img` with the badge, the pointer, and a click: `ring` 'press' + (a ring and the button's name) or 'pulse' (`since` seconds after the + release: the ring widening and fading, the name kept a moment).""" img = img.copy() w, h = self.size if badge: draw_badge(img, badge, (w - 16, h - 16), self.font, anchor='rb') if pointer is not None: x, y = pointer - if button: + if ring and button: label, colour = BUTTONS[button] d = ImageDraw.Draw(img, 'RGBA') - r = 16 - d.ellipse((x - r, y - r, x + r, y + r), outline=colour + (255,), width=4) - # The name sits below the pointer, kept inside the frame. - lx = min(x + 22, w - 16 - self.font.getlength(label)) - draw_badge(img, label, (lx, y + 40), self.font, fill=colour) + if ring == 'press': + r, alpha, named = 16, 255, True + else: + t = min(since / .45, 1) + r, alpha, named = 16 + 22 * t, int(255 * (1 - t)), since < .6 + if alpha > 0: + d.ellipse((x - r, y - r, x + r, y + r), outline=colour + (alpha,), width=4) + if named: + # The name sits below the pointer, kept inside the frame. + lx = min(x + 22, w - 16 - self.font.getlength(label)) + ly = y + 40 if y + 70 < h else y - 60 + draw_badge(img, label, (lx, ly), self.font, fill=colour) img.paste(self.sprite, (int(x) - 1, int(y) - 1), self.sprite) return img @@ -409,22 +445,23 @@ def compose(shots, out): n = round(clock * FPS) - emitted for i in range(n): t = ease((i + 1) / n) - frame = painter.paint(img, lerp(shot['start'], shot['end'], t), shot['button'], shot['badge']) + frame = painter.paint(img, lerp(shot['start'], shot['end'], t), shot['badge'], shot['ring'], + shot['ring_button'], i / FPS) ff.stdin.write(frame.tobytes()) if shot['poster'] and poster is None: - poster = painter.paint(img, shot['end'], shot['button'], None) + poster = painter.paint(img, shot['end'], None) emitted += n last = shots[-1] n = round(.7 * FPS) for i in range(n): t = ease((i + 1) / n) frame = Image.blend(loaded[1], first, t) - ff.stdin.write(painter.paint(frame, lerp(last['end'], shots[0]['start'], t), None, None).tobytes()) + ff.stdin.write(painter.paint(frame, lerp(last['end'], shots[0]['start'], t), None).tobytes()) emitted += n ff.stdin.close() if ff.wait() != 0: raise RuntimeError('ffmpeg (lossless) failed') - return emitted / FPS, poster or painter.paint(first, None, None, None) + return emitted / FPS, poster or painter.paint(first, None, None) def encode(lossless, name, poster, crf_webm=30, crf_mp4=20): @@ -447,20 +484,29 @@ def encode(lossless, name, poster, crf_webm=30, crf_mp4=20): # the clips -def text_is_the_interface(s): - """B2 on `make` in a tag runs it; B3 on the error opens the file there.""" +def hero_setup(s): + """README, main.c under it and an output pane under that, all on + screen from the first frame: the output pane is a finished `clear`, + which `make` will reuse.""" + s.write('/pane/1/look', f'{PROJECT}/src/main.c\n') + s.wait(lambda: len(s.panes()) == 2, 'main.c') + s.write('/pane/1/exec', 'clear\n') + s.wait(lambda: any(kind == 'cmd' and 'exit' in s.tag(n) for n, kind in s.panes().items()), 'the output pane') s.write('/pane/1/tag', ' make') + s.mouse('motion', 0, *s.cell_px(36, 9)) + + +def text_is_the_interface(s): + """B2 on `make` in a tag runs it; B3 on the error opens main.c there.""" + output = next(n for n, kind in s.panes().items() if kind == 'cmd') s.pointer = s.cell_px(36, 9) - s.shot(1.0) + s.shot(1.2) col, row = s.find('make', 0) - s.sweep(2, s.tag_px(col, row, offset=.15), s.tag_px(col + 3, row, offset=.85)) - s.wait(lambda: any('exit' in s.tag(n) for n, kind in s.panes().items() if kind == 'cmd'), 'make to finish') - s.shot(1.1) + s.click(2, *s.tag_px(col + 1, row), done=lambda: '(make) exit' in s.tag(output), what='make') col, row = s.find('src/main.c:12:5', 0) - s.sweep(3, s.cell_px(col, row), s.cell_px(col + 14, row)) - s.wait(lambda: len(s.panes()) == 3, 'main.c to open') - s.glide(*s.cell_px(40, 9), seconds=.5) - s.shot(2.6, poster=True) + s.click(3, *s.cell_px(col + 4, row), done=lambda: any(line.startswith(' 12 ') for line in s.screen()[6:12]), + what='main.c at line 12', after=2.8) + s.shots[-1]['poster'] = True def shell_setup(s): @@ -543,32 +589,273 @@ def diff_review(s): s.wait_text('+ printf') s.shot(1.4) col, row = s.find('+ printf', 0) - s.sweep(3, s.cell_px(col, row), s.cell_px(col, row)) - s.wait(lambda: len(s.panes()) == 3, 'main.c to open') - s.glide(*s.cell_px(42, 9), seconds=.5) - s.shot(2.6, poster=True) + s.click(3, *s.cell_px(col, row), done=lambda: len(s.panes()) == 3, what='main.c to open', after=2.6) + s.shots[-1]['poster'] = True + + +def mandelbrot(path, width=1200, height=800): + """The classic view of the Mandelbrot set, smoothly coloured.""" + import numpy as np + x = np.linspace(-2.35, 0.85, width) + y = np.linspace(-1.0667, 1.0667, height) + c = x[None, :] + 1j * y[:, None] + z = np.zeros_like(c) + count = np.zeros(c.shape) + alive = np.ones(c.shape, bool) + limit = 200 + for n in range(limit): + z[alive] = z[alive] ** 2 + c[alive] + escaped = alive & (np.abs(z) > 4) + # The smooth (normalised) iteration count, so the bands blend. + count[escaped] = n + 1 - np.log2(np.log2(np.abs(z[escaped]))) + alive &= ~escaped + t = np.sqrt(np.clip(count / 40, 0, 1)) + # A deep blue through gold to white, the inside black. + stops = np.array([[0, 7, 100], [32, 107, 203], [237, 255, 255], [255, 170, 0], [80, 2, 0]], float) + pos = np.linspace(0, 1, len(stops)) + rgb = np.stack([np.interp(t, pos, stops[:, k]) for k in range(3)], axis=-1) + rgb[alive] = 0 + Image.fromarray(rgb.astype(np.uint8)).save(path) + + +def image_setup(s): + """A lone terminal at the project, which holds a picture.""" + mandelbrot(PROJECT / 'mandelbrot.png') + shell_setup(s) + s.write('/pane/1/exec', 'Del\n') + s.wait(lambda: list(s.panes()) == [2], 'the README to close') + + +def image_pane(s): + """ls in a shell; B3 on the PNG's name opens it in a pane.""" + s.pointer = s.cell_px(40, 8) + s.shot(1.0) + s.type('ls', lambda typed: at_prompt(s, typed)()) + s.keys('\r') + s.wait(lambda: any('mandelbrot.png' in line for line in s.terminal_rows()), 'ls') + s.shot(.9) + col, row = s.find('mandelbrot.png', 0) + s.click(3, *s.cell_px(col + 5, row), done=lambda: len(s.panes()) == 2, what='the picture', after=2.2) + s.shots[-1]['poster'] = True + image = next(n for n, kind in s.panes().items() if kind == 'image') + # Petscii, a word typed into the picture's tag, redraws it as a C64's + # character art would. + s.write(f'/pane/{image}/tag', ' Petscii') + s.click(2, *s.tag_word(image, 'Petscii'), done=lambda: 'petscii:on' in s.tag(image), what='petscii', + after=2.6) + + +# The Intel 64 and IA-32 SDM, vol. 2 (2,515 pages), opened where it is. +PDF = [Path('/home/goblin/01-resources/intel-devref-ii.pdf')] + + +def pdf_args(): + return [PDF[0]] + + +def pdf_setup(s): + s.mouse('motion', 0, *s.cell_px(COLS - 2, ROWS - 1)) + + +def pdf_tag(s, word, pane=None): + """The pixel over `word` in the PDF pane's tag.""" + pane = pane or next(n for n, kind in s.panes().items() if kind == 'pdf') + return s.tag_word(pane, word) + + +def page_of(s, pane): + tag = s.tag(pane) + return int(tag[tag.index('[') + 1:tag.index('/', tag.index('['))]) + + +def pdf_navigation(s): + """Page through the Intel SDM, jump to CPUID from its sections, search + a word and step through the hits.""" + pdf = next(n for n, kind in s.panes().items() if kind == 'pdf') + s.pointer = s.cell_px(44, 12) + s.shot(1.2) + s.press([('\x06', 'Ctrl-f'), ('\x06', 'Ctrl-f Ctrl-f')], per_key=.7) + s.click(2, *pdf_tag(s, 'PdfSections'), done=lambda: len(s.panes()) == 2, what='the sections', after=.8) + listing = next(n for n in s.panes() if n != pdf) + top = s.find('+PdfSections', 0)[1] + s.write('/focus', f'{listing}\n') + s.badge = '200G' + for key in '200G': + s.keys(key) + s.wait(lambda: any('CPUID' in line for line in s.screen()[top:]), 'the CPUID row') + s.shot(1.0) + s.badge = None + # A row's file.pdf:PAGE:SECTION is the place to look at. + col, row = s.find('pdf:317:200', after=top + 1) + s.click(3, *s.cell_px(col + 4, row), done=lambda: page_of(s, pdf) == 317, what='page 317', after=1.2) + s.click(2, *s.tag_word(listing, 'Del'), done=lambda: list(s.panes()) == [pdf], what='the listing to close', + after=.8) + s.write('/focus', f'{pdf}\n') + s.badge = '/' + s.keys('/') + s.shot(.25) + # Its first hits are on the pages after CPUID's first. + s.type('apic id', lambda typed: True, per_key=.08) + s.badge = '/apic id Enter' + s.keys('\r') + s.wait(lambda: any(kind == 'text' for kind in s.panes().values()), 'the hits') + s.shot(1.0) + s.badge = None + hits = next(n for n, kind in s.panes().items() if kind == 'text') + # The hit list folds to its tag, and the page has the column again. + s.click(2, *s.tag_word(hits, 'Collapse'), done=lambda: 'collapsed' in s.read(f'/pane/{hits}/ctl'), + what='the hits to fold', after=.8) + s.write('/focus', f'{pdf}\n') + for k in range(1, 4): + s.badge = ' '.join(['n'] * k) + s.keys('n') + s.shot(.8) + s.badge = None + s.shots[-1]['secs'] = 1.6 + s.shots[-1]['poster'] = True + + +def pdf_tint_setup(s): + """CPUID's page, and two words of the user's in the tag: PdfFit and a + page address (MOV's).""" + s.write('/pane/1/look', f'{PDF[0]}:317\n') + s.wait(lambda: page_of(s, 1) == 317, 'page 317') + s.write('/pane/1/tag', f' PdfFit {PDF[0].name}:762') + pdf_setup(s) + + +def pdf_tint_fit(s): + """PdfTint through its three looks, PdfFit both ways, then a look at + a page address.""" + s.pointer = s.cell_px(44, 12) + s.shot(1.2) + for _ in range(3): + before = s.read('/pane/1/ctl').split()[-1] + s.click(2, *pdf_tag(s, 'PdfTint'), done=lambda: s.read('/pane/1/ctl').split()[-1] != before, + what='the tint', after=1.0) + for fit in ('height', 'width'): + s.click(2, *pdf_tag(s, 'PdfFit'), done=lambda: f'fit:{fit}' in s.read('/pane/1/ctl'), what='the fit', + after=1.3) + s.click(3, *pdf_tag(s, f'{PDF[0].name}:762'), done=lambda: page_of(s, 1) == 762, what='page 762', after=2.0) + s.shots[-1]['poster'] = True + + +THEMES = MEDIA / 'themes' +THEME_WIDTH = 640 +THEME_MAX_BYTES = 120_000 + + +def theme_scene(s): + """One column: a C file with its syntax colours and a selection, a + shell that ran make (gcc's colours), and git diff paged into +Pager.""" + env = dict(clean_env(), HOME=str(HOME), GIT_AUTHOR_NAME='demo', GIT_AUTHOR_EMAIL='[email protected]', + GIT_COMMITTER_NAME='demo', GIT_COMMITTER_EMAIL='[email protected]') + for args in (['init', '-q'], ['add', '.'], ['commit', '-qm', 'hello'], ['config', 'diff.context', '1']): + subprocess.run(['git', '-C', str(PROJECT), *args], check=True, env=env, capture_output=True) + main = PROJECT / 'src/main.c' + main.write_text(main.read_text().replace('i <= 4', 'i <= 5')) + s.write('/pane/1/exec', 'Tty\n') + s.wait(lambda: 2 in s.panes(), 'the terminal') + s.write('/pane/2/pty/data', f"cd {PROJECT}; PS1='$ '; clear; make; git diff\r") + s.wait(lambda: len(s.panes()) == 3, 'the pager') + s.wait(lambda: 'Error 1' in s.read('/pane/2/body'), 'make') + s.write('/pane/1/dot', '12\n') + # The pager shows its hunk, the end of the diff. + pager = next(n for n, kind in s.panes().items() if n not in (1, 2)) + s.write(f'/pane/{pager}/dot', '$\n') + + +def theme_order(s): + """The themes as the Themes builtin lists them, up to the imported + ones: pardes's own, the ports and the legacy pair (src/themes).""" + s.write('/ctl', 'Themes\n') + s.wait(lambda: any(s.read(f'/pane/{n}/name').strip().endswith('+Themes') for n in s.panes()), '+Themes') + pane = next(n for n in s.panes() if s.read(f'/pane/{n}/name').strip().endswith('+Themes')) + names = [] + for line in s.read(f'/pane/{pane}/body').splitlines(): + if line.startswith('# Imported'): + break + if line.startswith('Theme '): + names.append(line.split()[1]) + s.write(f'/pane/{pane}/ctl', 'Del\n') + s.wait(lambda: pane not in s.panes(), '+Themes to close') + return names + + +def save_theme_png(data, path): + """The capture at THEME_WIDTH, as small a PNG as reads: full colour if it + fits THEME_MAX_BYTES, else 256 colours. Answers light or dark, from the + colour most of the window is.""" + import io + img = Image.open(io.BytesIO(data)).convert('RGB') + ground = max(img.getcolors(1 << 24), key=lambda c: c[0])[1] + lum = sum(w * (v / 255) ** 2.2 for w, v in zip((.2126, .7152, .0722), ground)) + small = img.resize((THEME_WIDTH, round(img.height * THEME_WIDTH / img.width)), Image.LANCZOS) + small.save(path, optimize=True) + if path.stat().st_size > THEME_MAX_BYTES: + small.quantize(256, method=Image.Quantize.MEDIANCUT, dither=Image.Dither.NONE).save(path, optimize=True) + return 'light' if lum > .18 else 'dark' + + +def record_themes(binary, work): + """docs/site/media/themes/<theme>.png for every theme of src/themes, in + the Themes builtin's order, and themes.toml listing them.""" + make_project() + s = Session(binary, work / 'themes', 'rec-themes', [PROJECT / 'src/main.c'], rows=22) + try: + theme_scene(s) + names = theme_order(s) + THEMES.mkdir(parents=True, exist_ok=True) + rows = [] + for name in names: + s.write('/ctl', f'Theme {name}\n') + # A pointer move puts away the word's flash that a command + # leaves up until the pointer moves. + s.mouse('motion', 0, *s.cell_px(COLS - 2, 1)) + s.mouse('motion', 0, *s.cell_px(COLS - 3, 1)) + kind = save_theme_png(s.settle(), THEMES / f'{name}.png') + rows.append((name, kind)) + print(f'{name}.png {kind} {(THEMES / f"{name}.png").stat().st_size / 1e3:.0f} KB', flush=True) + finally: + s.close() + toml = ['# The Themes page gallery, in the order the Themes builtin walks them:', + '# media/themes/<file> for each, made by test/record_demos.py themes.', + 'ready = true', ''] + for name, kind in rows: + toml += ['[[theme]]', f'name = "{name}"', f'file = "{name}.png"', f'kind = "{kind}"', ''] + (THEMES / 'themes.toml').write_text('\n'.join(toml)) CLIPS = { 'text-is-the-interface': dict( - run=text_is_the_interface, args=[PROJECT / 'README'], - caption='Middle-click runs a word; right-click an error opens the file.'), + setup=hero_setup, run=text_is_the_interface, args=[PROJECT / 'README'], + caption='Middle-click runs a word; right-click an error opens its line.'), 'terminals-are-panes': dict( setup=shell_setup, run=terminals_are_panes, args=[PROJECT / 'src/main.c'], - caption='A shell is a pane: Ctrl-b, then move over its output.'), + caption='A terminal is a pane: Ctrl-b, then move over its output.'), 'scripting-over-9p': dict( setup=mounted_shell_setup, run=scripting_over_9p, args=[PROJECT / 'README'], - caption='The session is files: a shell script reads and writes its panes.'), + caption='A script drives the session through its virtual filesystem (9P).'), 'diff-review': dict( setup=diff_setup, run=diff_review, args=[PROJECT / 'README'], - caption='git diff pages into a pane; right-click a change to open it.'), + caption='Right-click a diff line to open that line of the file.'), + 'pdf-navigation': dict( + setup=pdf_setup, run=pdf_navigation, args=pdf_args, + caption='A 2,515-page PDF: page, jump to CPUID, search, step hits.'), + 'pdf-tint-fit': dict( + setup=pdf_tint_setup, run=pdf_tint_fit, args=pdf_args, + caption='PdfTint tints the page, PdfFit fits it, a page address jumps.'), + 'image-pane': dict( + setup=image_setup, run=image_pane, args=[PROJECT / 'README'], + caption="Right-click a picture's name to open it; Petscii redraws it."), } def record(binary, work, name): clip = CLIPS[name] make_project() - s = Session(binary, work / name, 'rec-' + name, clip['args'], init=clip.get('init', '')) + args = clip['args']() if callable(clip['args']) else clip['args'] + s = Session(binary, work / name, 'rec-' + name, args, init=clip.get('init', '')) try: if 'setup' in clip: clip['setup'](s) @@ -586,19 +873,26 @@ def record(binary, work, name): def main(): parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument('binary', type=Path, help='a pardes-gui built with -Dplatform=gui') - parser.add_argument('clips', nargs='*', help='which clips (default: all): ' + ', '.join(CLIPS)) + parser.add_argument('clips', nargs='*', + help='which clips (default: all), or themes for the theme gallery: ' + ', '.join(CLIPS)) parser.add_argument('--work', type=Path, default=REPO.parent / '.scratch/rec/work', help='frames and intermediates (large; default %(default)s)') parser.add_argument('--theme', default=THEME[0], help='a theme name, or default (default %(default)s)') + parser.add_argument('--pdf', type=Path, default=PDF[0], + help='the PDF the pdf clips page through, opened read-only (default %(default)s)') args = parser.parse_args() THEME[0] = args.theme + PDF[0] = args.pdf.resolve() names = args.clips or list(CLIPS) for name in names: - if name not in CLIPS: + if name not in CLIPS and name != 'themes': parser.error(f'no clip {name!r}: ' + ', '.join(CLIPS)) try: for name in names: - record(args.binary, args.work.resolve(), name) + if name == 'themes': + record_themes(args.binary, args.work.resolve()) + else: + record(args.binary, args.work.resolve(), name) finally: shutil.rmtree(PROJECT, ignore_errors=True) shutil.rmtree(HOME, ignore_errors=True) @@ -606,7 +900,8 @@ def main(): lines = dict(line.split(': ', 1) for line in captions.read_text().splitlines() if ': ' in line) \ if captions.exists() else {} for name in names: - lines[name] = CLIPS[name]['caption'] + if name in CLIPS: + lines[name] = CLIPS[name]['caption'] captions.write_text(''.join(f'{name}: {lines[name]}\n' for name in CLIPS if name in lines)) |
