From f6a511c61d7ef2ff680f70077b236559a595be69 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 1 Oct 2026 12:42:48 -0300 Subject: 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 theme 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 --- docs/site/media/captions.txt | 11 +- docs/site/media/clips.toml | 15 + docs/site/media/diff-review.mp4 | Bin 139657 -> 133945 bytes docs/site/media/diff-review.png | Bin 84748 -> 92094 bytes docs/site/media/diff-review.webm | Bin 217009 -> 231829 bytes docs/site/media/image-pane.mp4 | Bin 0 -> 182755 bytes docs/site/media/image-pane.png | Bin 0 -> 189612 bytes docs/site/media/image-pane.webm | Bin 0 -> 223710 bytes docs/site/media/pdf-navigation.mp4 | Bin 0 -> 732978 bytes docs/site/media/pdf-navigation.png | Bin 0 -> 161942 bytes docs/site/media/pdf-navigation.webm | Bin 0 -> 809445 bytes docs/site/media/pdf-tint-fit.mp4 | Bin 0 -> 672000 bytes docs/site/media/pdf-tint-fit.png | Bin 0 -> 154718 bytes docs/site/media/pdf-tint-fit.webm | Bin 0 -> 687970 bytes docs/site/media/scripting-over-9p.webm | Bin 230045 -> 230045 bytes docs/site/media/terminals-are-panes.webm | Bin 132696 -> 132696 bytes docs/site/media/text-is-the-interface.mp4 | Bin 244272 -> 208679 bytes docs/site/media/text-is-the-interface.png | Bin 84804 -> 84827 bytes docs/site/media/text-is-the-interface.webm | Bin 330331 -> 279200 bytes docs/site/media/themes/acme.png | Bin 0 -> 56068 bytes docs/site/media/themes/alucard.png | Bin 0 -> 56543 bytes docs/site/media/themes/atelier.png | Bin 0 -> 54117 bytes docs/site/media/themes/ayu_dark.png | Bin 0 -> 53936 bytes docs/site/media/themes/ayu_light.png | Bin 0 -> 51539 bytes docs/site/media/themes/ayu_mirage.png | Bin 0 -> 53282 bytes docs/site/media/themes/catppuccin_frappe.png | Bin 0 -> 54195 bytes docs/site/media/themes/catppuccin_latte.png | Bin 0 -> 53833 bytes docs/site/media/themes/catppuccin_macchiato.png | Bin 0 -> 54572 bytes docs/site/media/themes/catppuccin_mocha.png | Bin 0 -> 55309 bytes docs/site/media/themes/clay.png | Bin 0 -> 54402 bytes docs/site/media/themes/dark.png | Bin 0 -> 51087 bytes docs/site/media/themes/dark_plus.png | Bin 0 -> 53097 bytes docs/site/media/themes/daybreak.png | Bin 0 -> 50612 bytes docs/site/media/themes/doom_one.png | Bin 0 -> 53266 bytes docs/site/media/themes/doom_one_light.png | Bin 0 -> 53006 bytes docs/site/media/themes/dracula.png | Bin 0 -> 54472 bytes docs/site/media/themes/dracula_soft.png | Bin 0 -> 54126 bytes docs/site/media/themes/duckbones.png | Bin 0 -> 55159 bytes docs/site/media/themes/dusk.png | Bin 0 -> 53826 bytes docs/site/media/themes/forestbones_dark.png | Bin 0 -> 55144 bytes docs/site/media/themes/forestbones_light.png | Bin 0 -> 53618 bytes docs/site/media/themes/forge.png | Bin 0 -> 53735 bytes docs/site/media/themes/forge_black.png | Bin 0 -> 49798 bytes docs/site/media/themes/forge_soft.png | Bin 0 -> 53106 bytes docs/site/media/themes/harvest.png | Bin 0 -> 55290 bytes docs/site/media/themes/helix.png | Bin 0 -> 52253 bytes docs/site/media/themes/ink.png | Bin 0 -> 50438 bytes docs/site/media/themes/kanagawabones.png | Bin 0 -> 55944 bytes docs/site/media/themes/lagoon.png | Bin 0 -> 51345 bytes docs/site/media/themes/lapis.png | Bin 0 -> 58311 bytes docs/site/media/themes/lapis_plain.png | Bin 0 -> 54111 bytes docs/site/media/themes/monokai.png | Bin 0 -> 55500 bytes docs/site/media/themes/monokai_pro.png | Bin 0 -> 52437 bytes docs/site/media/themes/monokai_pro_classic.png | Bin 0 -> 52583 bytes docs/site/media/themes/monokai_pro_light.png | Bin 0 -> 54494 bytes docs/site/media/themes/monokai_pro_light_sun.png | Bin 0 -> 55294 bytes docs/site/media/themes/monokai_pro_machine.png | Bin 0 -> 53048 bytes docs/site/media/themes/monokai_pro_octagon.png | Bin 0 -> 53951 bytes docs/site/media/themes/monokai_pro_ristretto.png | Bin 0 -> 52794 bytes docs/site/media/themes/monokai_pro_spectrum.png | Bin 0 -> 53833 bytes docs/site/media/themes/neobones_dark.png | Bin 0 -> 54808 bytes docs/site/media/themes/neobones_light.png | Bin 0 -> 54854 bytes docs/site/media/themes/nord.png | Bin 0 -> 53617 bytes docs/site/media/themes/nordbones.png | Bin 0 -> 55741 bytes docs/site/media/themes/orchard.png | Bin 0 -> 54859 bytes docs/site/media/themes/orchard_black.png | Bin 0 -> 50762 bytes docs/site/media/themes/paper.png | Bin 0 -> 55203 bytes docs/site/media/themes/rose_pine.png | Bin 0 -> 55110 bytes docs/site/media/themes/rose_pine_dawn.png | Bin 0 -> 56381 bytes docs/site/media/themes/rose_pine_moon.png | Bin 0 -> 53748 bytes docs/site/media/themes/rosebones_dark.png | Bin 0 -> 55441 bytes docs/site/media/themes/rosebones_light.png | Bin 0 -> 53670 bytes docs/site/media/themes/seoul256_dark_233.png | Bin 0 -> 54107 bytes docs/site/media/themes/seoul256_dark_234.png | Bin 0 -> 53423 bytes docs/site/media/themes/seoul256_dark_235.png | Bin 0 -> 53351 bytes docs/site/media/themes/seoul256_dark_236.png | Bin 0 -> 52348 bytes docs/site/media/themes/seoul256_dark_237.png | Bin 0 -> 51101 bytes docs/site/media/themes/seoul256_dark_238.png | Bin 0 -> 51346 bytes docs/site/media/themes/seoul256_dark_239.png | Bin 0 -> 50757 bytes docs/site/media/themes/seoul256_light_252.png | Bin 0 -> 49932 bytes docs/site/media/themes/seoul256_light_253.png | Bin 0 -> 50861 bytes docs/site/media/themes/seoul256_light_254.png | Bin 0 -> 51138 bytes docs/site/media/themes/seoul256_light_255.png | Bin 0 -> 51590 bytes docs/site/media/themes/seoul256_light_256.png | Bin 0 -> 48496 bytes docs/site/media/themes/seoulbones_dark.png | Bin 0 -> 53482 bytes docs/site/media/themes/seoulbones_light.png | Bin 0 -> 53299 bytes docs/site/media/themes/solarium.png | Bin 0 -> 53372 bytes docs/site/media/themes/solarized_dark.png | Bin 0 -> 52667 bytes docs/site/media/themes/solarized_light.png | Bin 0 -> 53850 bytes docs/site/media/themes/spectrum.png | Bin 0 -> 53539 bytes docs/site/media/themes/themes.toml | 347 ++++++++++++++++++- docs/site/media/themes/tokyobones_dark.png | Bin 0 -> 56620 bytes docs/site/media/themes/tokyobones_light.png | Bin 0 -> 55257 bytes docs/site/media/themes/tokyonight_day.png | Bin 0 -> 51489 bytes docs/site/media/themes/tokyonight_moon.png | Bin 0 -> 53770 bytes docs/site/media/themes/tokyonight_night.png | Bin 0 -> 54672 bytes docs/site/media/themes/tokyonight_storm.png | Bin 0 -> 53382 bytes docs/site/media/themes/vimbones.png | Bin 0 -> 55104 bytes docs/site/media/themes/zenbones_dark.png | Bin 0 -> 56055 bytes docs/site/media/themes/zenbones_light.png | Bin 0 -> 54982 bytes docs/site/media/themes/zenburned.png | Bin 0 -> 55739 bytes docs/site/media/themes/zenwritten_dark.png | Bin 0 -> 54856 bytes docs/site/media/themes/zenwritten_light.png | Bin 0 -> 55139 bytes test/record_demos.py | 407 +++++++++++++++++++---- 104 files changed, 716 insertions(+), 64 deletions(-) create mode 100644 docs/site/media/image-pane.mp4 create mode 100644 docs/site/media/image-pane.png create mode 100644 docs/site/media/image-pane.webm create mode 100644 docs/site/media/pdf-navigation.mp4 create mode 100644 docs/site/media/pdf-navigation.png create mode 100644 docs/site/media/pdf-navigation.webm create mode 100644 docs/site/media/pdf-tint-fit.mp4 create mode 100644 docs/site/media/pdf-tint-fit.png create mode 100644 docs/site/media/pdf-tint-fit.webm create mode 100644 docs/site/media/themes/acme.png create mode 100644 docs/site/media/themes/alucard.png create mode 100644 docs/site/media/themes/atelier.png create mode 100644 docs/site/media/themes/ayu_dark.png create mode 100644 docs/site/media/themes/ayu_light.png create mode 100644 docs/site/media/themes/ayu_mirage.png create mode 100644 docs/site/media/themes/catppuccin_frappe.png create mode 100644 docs/site/media/themes/catppuccin_latte.png create mode 100644 docs/site/media/themes/catppuccin_macchiato.png create mode 100644 docs/site/media/themes/catppuccin_mocha.png create mode 100644 docs/site/media/themes/clay.png create mode 100644 docs/site/media/themes/dark.png create mode 100644 docs/site/media/themes/dark_plus.png create mode 100644 docs/site/media/themes/daybreak.png create mode 100644 docs/site/media/themes/doom_one.png create mode 100644 docs/site/media/themes/doom_one_light.png create mode 100644 docs/site/media/themes/dracula.png create mode 100644 docs/site/media/themes/dracula_soft.png create mode 100644 docs/site/media/themes/duckbones.png create mode 100644 docs/site/media/themes/dusk.png create mode 100644 docs/site/media/themes/forestbones_dark.png create mode 100644 docs/site/media/themes/forestbones_light.png create mode 100644 docs/site/media/themes/forge.png create mode 100644 docs/site/media/themes/forge_black.png create mode 100644 docs/site/media/themes/forge_soft.png create mode 100644 docs/site/media/themes/harvest.png create mode 100644 docs/site/media/themes/helix.png create mode 100644 docs/site/media/themes/ink.png create mode 100644 docs/site/media/themes/kanagawabones.png create mode 100644 docs/site/media/themes/lagoon.png create mode 100644 docs/site/media/themes/lapis.png create mode 100644 docs/site/media/themes/lapis_plain.png create mode 100644 docs/site/media/themes/monokai.png create mode 100644 docs/site/media/themes/monokai_pro.png create mode 100644 docs/site/media/themes/monokai_pro_classic.png create mode 100644 docs/site/media/themes/monokai_pro_light.png create mode 100644 docs/site/media/themes/monokai_pro_light_sun.png create mode 100644 docs/site/media/themes/monokai_pro_machine.png create mode 100644 docs/site/media/themes/monokai_pro_octagon.png create mode 100644 docs/site/media/themes/monokai_pro_ristretto.png create mode 100644 docs/site/media/themes/monokai_pro_spectrum.png create mode 100644 docs/site/media/themes/neobones_dark.png create mode 100644 docs/site/media/themes/neobones_light.png create mode 100644 docs/site/media/themes/nord.png create mode 100644 docs/site/media/themes/nordbones.png create mode 100644 docs/site/media/themes/orchard.png create mode 100644 docs/site/media/themes/orchard_black.png create mode 100644 docs/site/media/themes/paper.png create mode 100644 docs/site/media/themes/rose_pine.png create mode 100644 docs/site/media/themes/rose_pine_dawn.png create mode 100644 docs/site/media/themes/rose_pine_moon.png create mode 100644 docs/site/media/themes/rosebones_dark.png create mode 100644 docs/site/media/themes/rosebones_light.png create mode 100644 docs/site/media/themes/seoul256_dark_233.png create mode 100644 docs/site/media/themes/seoul256_dark_234.png create mode 100644 docs/site/media/themes/seoul256_dark_235.png create mode 100644 docs/site/media/themes/seoul256_dark_236.png create mode 100644 docs/site/media/themes/seoul256_dark_237.png create mode 100644 docs/site/media/themes/seoul256_dark_238.png create mode 100644 docs/site/media/themes/seoul256_dark_239.png create mode 100644 docs/site/media/themes/seoul256_light_252.png create mode 100644 docs/site/media/themes/seoul256_light_253.png create mode 100644 docs/site/media/themes/seoul256_light_254.png create mode 100644 docs/site/media/themes/seoul256_light_255.png create mode 100644 docs/site/media/themes/seoul256_light_256.png create mode 100644 docs/site/media/themes/seoulbones_dark.png create mode 100644 docs/site/media/themes/seoulbones_light.png create mode 100644 docs/site/media/themes/solarium.png create mode 100644 docs/site/media/themes/solarized_dark.png create mode 100644 docs/site/media/themes/solarized_light.png create mode 100644 docs/site/media/themes/spectrum.png create mode 100644 docs/site/media/themes/tokyobones_dark.png create mode 100644 docs/site/media/themes/tokyobones_light.png create mode 100644 docs/site/media/themes/tokyonight_day.png create mode 100644 docs/site/media/themes/tokyonight_moon.png create mode 100644 docs/site/media/themes/tokyonight_night.png create mode 100644 docs/site/media/themes/tokyonight_storm.png create mode 100644 docs/site/media/themes/vimbones.png create mode 100644 docs/site/media/themes/zenbones_dark.png create mode 100644 docs/site/media/themes/zenbones_light.png create mode 100644 docs/site/media/themes/zenburned.png create mode 100644 docs/site/media/themes/zenwritten_dark.png create mode 100644 docs/site/media/themes/zenwritten_light.png 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 index e3bb2f5b..73fa1a76 100644 Binary files a/docs/site/media/diff-review.mp4 and b/docs/site/media/diff-review.mp4 differ diff --git a/docs/site/media/diff-review.png b/docs/site/media/diff-review.png index fda184e3..76a7dc43 100644 Binary files a/docs/site/media/diff-review.png and b/docs/site/media/diff-review.png differ diff --git a/docs/site/media/diff-review.webm b/docs/site/media/diff-review.webm index ed201b13..da7756f9 100644 Binary files a/docs/site/media/diff-review.webm and b/docs/site/media/diff-review.webm differ diff --git a/docs/site/media/image-pane.mp4 b/docs/site/media/image-pane.mp4 new file mode 100644 index 00000000..de60b945 Binary files /dev/null and b/docs/site/media/image-pane.mp4 differ diff --git a/docs/site/media/image-pane.png b/docs/site/media/image-pane.png new file mode 100644 index 00000000..3f77e2ac Binary files /dev/null and b/docs/site/media/image-pane.png differ diff --git a/docs/site/media/image-pane.webm b/docs/site/media/image-pane.webm new file mode 100644 index 00000000..46de130b Binary files /dev/null and b/docs/site/media/image-pane.webm differ diff --git a/docs/site/media/pdf-navigation.mp4 b/docs/site/media/pdf-navigation.mp4 new file mode 100644 index 00000000..f0369f7b Binary files /dev/null and b/docs/site/media/pdf-navigation.mp4 differ diff --git a/docs/site/media/pdf-navigation.png b/docs/site/media/pdf-navigation.png new file mode 100644 index 00000000..79dd6453 Binary files /dev/null and b/docs/site/media/pdf-navigation.png differ diff --git a/docs/site/media/pdf-navigation.webm b/docs/site/media/pdf-navigation.webm new file mode 100644 index 00000000..17c8b1b3 Binary files /dev/null and b/docs/site/media/pdf-navigation.webm differ diff --git a/docs/site/media/pdf-tint-fit.mp4 b/docs/site/media/pdf-tint-fit.mp4 new file mode 100644 index 00000000..a1b4a5a6 Binary files /dev/null and b/docs/site/media/pdf-tint-fit.mp4 differ diff --git a/docs/site/media/pdf-tint-fit.png b/docs/site/media/pdf-tint-fit.png new file mode 100644 index 00000000..923ab143 Binary files /dev/null and b/docs/site/media/pdf-tint-fit.png differ diff --git a/docs/site/media/pdf-tint-fit.webm b/docs/site/media/pdf-tint-fit.webm new file mode 100644 index 00000000..e641101f Binary files /dev/null and b/docs/site/media/pdf-tint-fit.webm differ diff --git a/docs/site/media/scripting-over-9p.webm b/docs/site/media/scripting-over-9p.webm index b6f192b3..bf39a7e8 100644 Binary files a/docs/site/media/scripting-over-9p.webm and b/docs/site/media/scripting-over-9p.webm differ diff --git a/docs/site/media/terminals-are-panes.webm b/docs/site/media/terminals-are-panes.webm index 3f1ab09e..15ee73fe 100644 Binary files a/docs/site/media/terminals-are-panes.webm and b/docs/site/media/terminals-are-panes.webm differ diff --git a/docs/site/media/text-is-the-interface.mp4 b/docs/site/media/text-is-the-interface.mp4 index 19545731..253d1f39 100644 Binary files a/docs/site/media/text-is-the-interface.mp4 and b/docs/site/media/text-is-the-interface.mp4 differ diff --git a/docs/site/media/text-is-the-interface.png b/docs/site/media/text-is-the-interface.png index 81a73aee..b0d40b15 100644 Binary files a/docs/site/media/text-is-the-interface.png and b/docs/site/media/text-is-the-interface.png differ diff --git a/docs/site/media/text-is-the-interface.webm b/docs/site/media/text-is-the-interface.webm index 88c02d11..07cf62e6 100644 Binary files a/docs/site/media/text-is-the-interface.webm and b/docs/site/media/text-is-the-interface.webm differ diff --git a/docs/site/media/themes/acme.png b/docs/site/media/themes/acme.png new file mode 100644 index 00000000..da647db1 Binary files /dev/null and b/docs/site/media/themes/acme.png differ diff --git a/docs/site/media/themes/alucard.png b/docs/site/media/themes/alucard.png new file mode 100644 index 00000000..dab6ce80 Binary files /dev/null and b/docs/site/media/themes/alucard.png differ diff --git a/docs/site/media/themes/atelier.png b/docs/site/media/themes/atelier.png new file mode 100644 index 00000000..a8c36f2a Binary files /dev/null and b/docs/site/media/themes/atelier.png differ diff --git a/docs/site/media/themes/ayu_dark.png b/docs/site/media/themes/ayu_dark.png new file mode 100644 index 00000000..711ed8aa Binary files /dev/null and b/docs/site/media/themes/ayu_dark.png differ diff --git a/docs/site/media/themes/ayu_light.png b/docs/site/media/themes/ayu_light.png new file mode 100644 index 00000000..0ddcb9af Binary files /dev/null and b/docs/site/media/themes/ayu_light.png differ diff --git a/docs/site/media/themes/ayu_mirage.png b/docs/site/media/themes/ayu_mirage.png new file mode 100644 index 00000000..56676d1f Binary files /dev/null and b/docs/site/media/themes/ayu_mirage.png differ diff --git a/docs/site/media/themes/catppuccin_frappe.png b/docs/site/media/themes/catppuccin_frappe.png new file mode 100644 index 00000000..58c13dbe Binary files /dev/null and b/docs/site/media/themes/catppuccin_frappe.png differ diff --git a/docs/site/media/themes/catppuccin_latte.png b/docs/site/media/themes/catppuccin_latte.png new file mode 100644 index 00000000..27e58ce0 Binary files /dev/null and b/docs/site/media/themes/catppuccin_latte.png differ diff --git a/docs/site/media/themes/catppuccin_macchiato.png b/docs/site/media/themes/catppuccin_macchiato.png new file mode 100644 index 00000000..fb32c742 Binary files /dev/null and b/docs/site/media/themes/catppuccin_macchiato.png differ diff --git a/docs/site/media/themes/catppuccin_mocha.png b/docs/site/media/themes/catppuccin_mocha.png new file mode 100644 index 00000000..e16e4224 Binary files /dev/null and b/docs/site/media/themes/catppuccin_mocha.png differ diff --git a/docs/site/media/themes/clay.png b/docs/site/media/themes/clay.png new file mode 100644 index 00000000..117b731d Binary files /dev/null and b/docs/site/media/themes/clay.png differ diff --git a/docs/site/media/themes/dark.png b/docs/site/media/themes/dark.png new file mode 100644 index 00000000..6346759e Binary files /dev/null and b/docs/site/media/themes/dark.png differ diff --git a/docs/site/media/themes/dark_plus.png b/docs/site/media/themes/dark_plus.png new file mode 100644 index 00000000..6a750655 Binary files /dev/null and b/docs/site/media/themes/dark_plus.png differ diff --git a/docs/site/media/themes/daybreak.png b/docs/site/media/themes/daybreak.png new file mode 100644 index 00000000..ca3dcc5c Binary files /dev/null and b/docs/site/media/themes/daybreak.png differ diff --git a/docs/site/media/themes/doom_one.png b/docs/site/media/themes/doom_one.png new file mode 100644 index 00000000..95f13135 Binary files /dev/null and b/docs/site/media/themes/doom_one.png differ diff --git a/docs/site/media/themes/doom_one_light.png b/docs/site/media/themes/doom_one_light.png new file mode 100644 index 00000000..c8c4c69a Binary files /dev/null and b/docs/site/media/themes/doom_one_light.png differ diff --git a/docs/site/media/themes/dracula.png b/docs/site/media/themes/dracula.png new file mode 100644 index 00000000..64eea04a Binary files /dev/null and b/docs/site/media/themes/dracula.png differ diff --git a/docs/site/media/themes/dracula_soft.png b/docs/site/media/themes/dracula_soft.png new file mode 100644 index 00000000..9ef81583 Binary files /dev/null and b/docs/site/media/themes/dracula_soft.png differ diff --git a/docs/site/media/themes/duckbones.png b/docs/site/media/themes/duckbones.png new file mode 100644 index 00000000..9809ab36 Binary files /dev/null and b/docs/site/media/themes/duckbones.png differ diff --git a/docs/site/media/themes/dusk.png b/docs/site/media/themes/dusk.png new file mode 100644 index 00000000..0d95b59c Binary files /dev/null and b/docs/site/media/themes/dusk.png differ diff --git a/docs/site/media/themes/forestbones_dark.png b/docs/site/media/themes/forestbones_dark.png new file mode 100644 index 00000000..ea399605 Binary files /dev/null and b/docs/site/media/themes/forestbones_dark.png differ diff --git a/docs/site/media/themes/forestbones_light.png b/docs/site/media/themes/forestbones_light.png new file mode 100644 index 00000000..862858de Binary files /dev/null and b/docs/site/media/themes/forestbones_light.png differ diff --git a/docs/site/media/themes/forge.png b/docs/site/media/themes/forge.png new file mode 100644 index 00000000..bab91d48 Binary files /dev/null and b/docs/site/media/themes/forge.png differ diff --git a/docs/site/media/themes/forge_black.png b/docs/site/media/themes/forge_black.png new file mode 100644 index 00000000..e09320ed Binary files /dev/null and b/docs/site/media/themes/forge_black.png differ diff --git a/docs/site/media/themes/forge_soft.png b/docs/site/media/themes/forge_soft.png new file mode 100644 index 00000000..1b9fb2fb Binary files /dev/null and b/docs/site/media/themes/forge_soft.png differ diff --git a/docs/site/media/themes/harvest.png b/docs/site/media/themes/harvest.png new file mode 100644 index 00000000..b95e8c76 Binary files /dev/null and b/docs/site/media/themes/harvest.png differ diff --git a/docs/site/media/themes/helix.png b/docs/site/media/themes/helix.png new file mode 100644 index 00000000..6c353815 Binary files /dev/null and b/docs/site/media/themes/helix.png differ diff --git a/docs/site/media/themes/ink.png b/docs/site/media/themes/ink.png new file mode 100644 index 00000000..314610db Binary files /dev/null and b/docs/site/media/themes/ink.png differ diff --git a/docs/site/media/themes/kanagawabones.png b/docs/site/media/themes/kanagawabones.png new file mode 100644 index 00000000..c2c58ec9 Binary files /dev/null and b/docs/site/media/themes/kanagawabones.png differ diff --git a/docs/site/media/themes/lagoon.png b/docs/site/media/themes/lagoon.png new file mode 100644 index 00000000..bdd3dd49 Binary files /dev/null and b/docs/site/media/themes/lagoon.png differ diff --git a/docs/site/media/themes/lapis.png b/docs/site/media/themes/lapis.png new file mode 100644 index 00000000..2bc4024a Binary files /dev/null and b/docs/site/media/themes/lapis.png differ diff --git a/docs/site/media/themes/lapis_plain.png b/docs/site/media/themes/lapis_plain.png new file mode 100644 index 00000000..7315c2a0 Binary files /dev/null and b/docs/site/media/themes/lapis_plain.png differ diff --git a/docs/site/media/themes/monokai.png b/docs/site/media/themes/monokai.png new file mode 100644 index 00000000..4b4854b8 Binary files /dev/null and b/docs/site/media/themes/monokai.png differ diff --git a/docs/site/media/themes/monokai_pro.png b/docs/site/media/themes/monokai_pro.png new file mode 100644 index 00000000..e8036053 Binary files /dev/null and b/docs/site/media/themes/monokai_pro.png differ diff --git a/docs/site/media/themes/monokai_pro_classic.png b/docs/site/media/themes/monokai_pro_classic.png new file mode 100644 index 00000000..4e25319c Binary files /dev/null and b/docs/site/media/themes/monokai_pro_classic.png differ diff --git a/docs/site/media/themes/monokai_pro_light.png b/docs/site/media/themes/monokai_pro_light.png new file mode 100644 index 00000000..ee2f1e4c Binary files /dev/null and b/docs/site/media/themes/monokai_pro_light.png differ diff --git a/docs/site/media/themes/monokai_pro_light_sun.png b/docs/site/media/themes/monokai_pro_light_sun.png new file mode 100644 index 00000000..67e6e807 Binary files /dev/null and b/docs/site/media/themes/monokai_pro_light_sun.png differ diff --git a/docs/site/media/themes/monokai_pro_machine.png b/docs/site/media/themes/monokai_pro_machine.png new file mode 100644 index 00000000..3f683a68 Binary files /dev/null and b/docs/site/media/themes/monokai_pro_machine.png differ diff --git a/docs/site/media/themes/monokai_pro_octagon.png b/docs/site/media/themes/monokai_pro_octagon.png new file mode 100644 index 00000000..bc7a831e Binary files /dev/null and b/docs/site/media/themes/monokai_pro_octagon.png differ diff --git a/docs/site/media/themes/monokai_pro_ristretto.png b/docs/site/media/themes/monokai_pro_ristretto.png new file mode 100644 index 00000000..06429245 Binary files /dev/null and b/docs/site/media/themes/monokai_pro_ristretto.png differ diff --git a/docs/site/media/themes/monokai_pro_spectrum.png b/docs/site/media/themes/monokai_pro_spectrum.png new file mode 100644 index 00000000..c6e49f4c Binary files /dev/null and b/docs/site/media/themes/monokai_pro_spectrum.png differ diff --git a/docs/site/media/themes/neobones_dark.png b/docs/site/media/themes/neobones_dark.png new file mode 100644 index 00000000..026400de Binary files /dev/null and b/docs/site/media/themes/neobones_dark.png differ diff --git a/docs/site/media/themes/neobones_light.png b/docs/site/media/themes/neobones_light.png new file mode 100644 index 00000000..62d26b3a Binary files /dev/null and b/docs/site/media/themes/neobones_light.png differ diff --git a/docs/site/media/themes/nord.png b/docs/site/media/themes/nord.png new file mode 100644 index 00000000..d5c903b2 Binary files /dev/null and b/docs/site/media/themes/nord.png differ diff --git a/docs/site/media/themes/nordbones.png b/docs/site/media/themes/nordbones.png new file mode 100644 index 00000000..272ad19d Binary files /dev/null and b/docs/site/media/themes/nordbones.png differ diff --git a/docs/site/media/themes/orchard.png b/docs/site/media/themes/orchard.png new file mode 100644 index 00000000..623fde75 Binary files /dev/null and b/docs/site/media/themes/orchard.png differ diff --git a/docs/site/media/themes/orchard_black.png b/docs/site/media/themes/orchard_black.png new file mode 100644 index 00000000..02d78992 Binary files /dev/null and b/docs/site/media/themes/orchard_black.png differ diff --git a/docs/site/media/themes/paper.png b/docs/site/media/themes/paper.png new file mode 100644 index 00000000..59919d80 Binary files /dev/null and b/docs/site/media/themes/paper.png differ diff --git a/docs/site/media/themes/rose_pine.png b/docs/site/media/themes/rose_pine.png new file mode 100644 index 00000000..9dbf1d76 Binary files /dev/null and b/docs/site/media/themes/rose_pine.png differ diff --git a/docs/site/media/themes/rose_pine_dawn.png b/docs/site/media/themes/rose_pine_dawn.png new file mode 100644 index 00000000..8a062630 Binary files /dev/null and b/docs/site/media/themes/rose_pine_dawn.png differ diff --git a/docs/site/media/themes/rose_pine_moon.png b/docs/site/media/themes/rose_pine_moon.png new file mode 100644 index 00000000..ee93f871 Binary files /dev/null and b/docs/site/media/themes/rose_pine_moon.png differ diff --git a/docs/site/media/themes/rosebones_dark.png b/docs/site/media/themes/rosebones_dark.png new file mode 100644 index 00000000..19992fea Binary files /dev/null and b/docs/site/media/themes/rosebones_dark.png differ diff --git a/docs/site/media/themes/rosebones_light.png b/docs/site/media/themes/rosebones_light.png new file mode 100644 index 00000000..a879dc55 Binary files /dev/null and b/docs/site/media/themes/rosebones_light.png differ diff --git a/docs/site/media/themes/seoul256_dark_233.png b/docs/site/media/themes/seoul256_dark_233.png new file mode 100644 index 00000000..a757640b Binary files /dev/null and b/docs/site/media/themes/seoul256_dark_233.png differ diff --git a/docs/site/media/themes/seoul256_dark_234.png b/docs/site/media/themes/seoul256_dark_234.png new file mode 100644 index 00000000..a34b3f2a Binary files /dev/null and b/docs/site/media/themes/seoul256_dark_234.png differ diff --git a/docs/site/media/themes/seoul256_dark_235.png b/docs/site/media/themes/seoul256_dark_235.png new file mode 100644 index 00000000..d4c69938 Binary files /dev/null and b/docs/site/media/themes/seoul256_dark_235.png differ diff --git a/docs/site/media/themes/seoul256_dark_236.png b/docs/site/media/themes/seoul256_dark_236.png new file mode 100644 index 00000000..8f1c48ce Binary files /dev/null and b/docs/site/media/themes/seoul256_dark_236.png differ diff --git a/docs/site/media/themes/seoul256_dark_237.png b/docs/site/media/themes/seoul256_dark_237.png new file mode 100644 index 00000000..3816b258 Binary files /dev/null and b/docs/site/media/themes/seoul256_dark_237.png differ diff --git a/docs/site/media/themes/seoul256_dark_238.png b/docs/site/media/themes/seoul256_dark_238.png new file mode 100644 index 00000000..9553da5f Binary files /dev/null and b/docs/site/media/themes/seoul256_dark_238.png differ diff --git a/docs/site/media/themes/seoul256_dark_239.png b/docs/site/media/themes/seoul256_dark_239.png new file mode 100644 index 00000000..8109f756 Binary files /dev/null and b/docs/site/media/themes/seoul256_dark_239.png differ diff --git a/docs/site/media/themes/seoul256_light_252.png b/docs/site/media/themes/seoul256_light_252.png new file mode 100644 index 00000000..7f01a1ab Binary files /dev/null and b/docs/site/media/themes/seoul256_light_252.png differ diff --git a/docs/site/media/themes/seoul256_light_253.png b/docs/site/media/themes/seoul256_light_253.png new file mode 100644 index 00000000..5b9d2a43 Binary files /dev/null and b/docs/site/media/themes/seoul256_light_253.png differ diff --git a/docs/site/media/themes/seoul256_light_254.png b/docs/site/media/themes/seoul256_light_254.png new file mode 100644 index 00000000..125546f7 Binary files /dev/null and b/docs/site/media/themes/seoul256_light_254.png differ diff --git a/docs/site/media/themes/seoul256_light_255.png b/docs/site/media/themes/seoul256_light_255.png new file mode 100644 index 00000000..06b1e3fc Binary files /dev/null and b/docs/site/media/themes/seoul256_light_255.png differ diff --git a/docs/site/media/themes/seoul256_light_256.png b/docs/site/media/themes/seoul256_light_256.png new file mode 100644 index 00000000..ce4ee4da Binary files /dev/null and b/docs/site/media/themes/seoul256_light_256.png differ diff --git a/docs/site/media/themes/seoulbones_dark.png b/docs/site/media/themes/seoulbones_dark.png new file mode 100644 index 00000000..74ea8ebc Binary files /dev/null and b/docs/site/media/themes/seoulbones_dark.png differ diff --git a/docs/site/media/themes/seoulbones_light.png b/docs/site/media/themes/seoulbones_light.png new file mode 100644 index 00000000..7591721a Binary files /dev/null and b/docs/site/media/themes/seoulbones_light.png differ diff --git a/docs/site/media/themes/solarium.png b/docs/site/media/themes/solarium.png new file mode 100644 index 00000000..f24a111e Binary files /dev/null and b/docs/site/media/themes/solarium.png differ diff --git a/docs/site/media/themes/solarized_dark.png b/docs/site/media/themes/solarized_dark.png new file mode 100644 index 00000000..7df0ef41 Binary files /dev/null and b/docs/site/media/themes/solarized_dark.png differ diff --git a/docs/site/media/themes/solarized_light.png b/docs/site/media/themes/solarized_light.png new file mode 100644 index 00000000..40b88ef3 Binary files /dev/null and b/docs/site/media/themes/solarized_light.png differ diff --git a/docs/site/media/themes/spectrum.png b/docs/site/media/themes/spectrum.png new file mode 100644 index 00000000..111f0913 Binary files /dev/null and b/docs/site/media/themes/spectrum.png differ 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/ 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/ 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 new file mode 100644 index 00000000..f873726d Binary files /dev/null and b/docs/site/media/themes/tokyobones_dark.png differ diff --git a/docs/site/media/themes/tokyobones_light.png b/docs/site/media/themes/tokyobones_light.png new file mode 100644 index 00000000..211a30ff Binary files /dev/null and b/docs/site/media/themes/tokyobones_light.png differ diff --git a/docs/site/media/themes/tokyonight_day.png b/docs/site/media/themes/tokyonight_day.png new file mode 100644 index 00000000..cdbef103 Binary files /dev/null and b/docs/site/media/themes/tokyonight_day.png differ diff --git a/docs/site/media/themes/tokyonight_moon.png b/docs/site/media/themes/tokyonight_moon.png new file mode 100644 index 00000000..d35bf419 Binary files /dev/null and b/docs/site/media/themes/tokyonight_moon.png differ diff --git a/docs/site/media/themes/tokyonight_night.png b/docs/site/media/themes/tokyonight_night.png new file mode 100644 index 00000000..5497275b Binary files /dev/null and b/docs/site/media/themes/tokyonight_night.png differ diff --git a/docs/site/media/themes/tokyonight_storm.png b/docs/site/media/themes/tokyonight_storm.png new file mode 100644 index 00000000..5e62b9e2 Binary files /dev/null and b/docs/site/media/themes/tokyonight_storm.png differ diff --git a/docs/site/media/themes/vimbones.png b/docs/site/media/themes/vimbones.png new file mode 100644 index 00000000..134fd582 Binary files /dev/null and b/docs/site/media/themes/vimbones.png differ diff --git a/docs/site/media/themes/zenbones_dark.png b/docs/site/media/themes/zenbones_dark.png new file mode 100644 index 00000000..d7e8315c Binary files /dev/null and b/docs/site/media/themes/zenbones_dark.png differ diff --git a/docs/site/media/themes/zenbones_light.png b/docs/site/media/themes/zenbones_light.png new file mode 100644 index 00000000..22c2da48 Binary files /dev/null and b/docs/site/media/themes/zenbones_light.png differ diff --git a/docs/site/media/themes/zenburned.png b/docs/site/media/themes/zenburned.png new file mode 100644 index 00000000..3b97815b Binary files /dev/null and b/docs/site/media/themes/zenburned.png differ diff --git a/docs/site/media/themes/zenwritten_dark.png b/docs/site/media/themes/zenwritten_dark.png new file mode 100644 index 00000000..6421245f Binary files /dev/null and b/docs/site/media/themes/zenwritten_dark.png differ diff --git a/docs/site/media/themes/zenwritten_light.png b/docs/site/media/themes/zenwritten_light.png new file mode 100644 index 00000000..28f3a144 Binary files /dev/null and b/docs/site/media/themes/zenwritten_light.png differ 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 -python3 -B test/record_demos.py /bin/pardes-gui [clip ...] +python3 -B test/record_demos.py /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/.webm and .mp4, with .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/.webm and .mp4, with +.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/.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='demo@example.com', + GIT_COMMITTER_NAME='demo', GIT_COMMITTER_EMAIL='demo@example.com') + 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/.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/ 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)) -- cgit v1.3