Skip to main content

max / makenotwork

Declare this site's Chrome, and describe the shortcuts overlay e0c0d991, decided 2026-08-30. This server declared no Chrome at all: the only one in the tree was Chrome::new(), the empty default, used twice in src/quasi/embeds.rs for embeds that deliberately have no shell. Paid with the /pricing port rather than after it. Every screen converted from here on inherits the shortcut with no further work, which is the reason to do it now instead of accumulating a site-wide gap to close under launch pressure. It is also what gives Outcome::Over somewhere to land: a renderer emits the overlay container for an app that declares chrome, and an app that declares none gets a swap that does nothing. quasi wires the key and does not draw the listing. binding_html emits a hidden button whose action targets the overlay container and that is all of it, and Binding::group names "the heading a listing shows it beneath" -- which says plainly that the listing is a screen the app describes. So the plumbing is quasi's and the screen is ours. Its own mount, not a route inside the pricing nest. An overlay reachable from every screen is not one screen's route, and the binding's address is absolute in the emitted markup, so registering it under /pricing would have given the site-wide shortcut a pricing-shaped address that moved the first time a second screen converted. One binding, because a Binding carries an Action and Escape, Cmd+S and Cmd+K perform no route. Those three are keyboard.js's and are spelled once beside the declaration rather than in a template; they move into chrome() on the day the description layer can say what they do. The described key is read off the Chrome, which is the point Binding::label exists to make. AND THE HALF THE BRIEF DID NOT ASK FOR, because it would have shipped broken: keyboard.js binds `?` too and loads on described and undescribed pages alike, both being served from one shell, so `?` would have opened two overlays. The JS handler now stands down where the page carries data-chrome. The described page wins because its listing is derived from the bindings and the other is a hand-written copy; both go when the last template converts.
Author: Max Johnson <me@maxj.phd> · 2026-08-30 23:36 UTC
Signed with PGP, not checked
Commit: 09c9d0b1d1465d2b6ad036a824c35bf3f529be17
Parent: afd71c0
7 files changed, +202 insertions, -12 deletions
@@ -10759,6 +10759,14 @@
10759 10759 "pkg-config",
10760 10760 ]
10761 10761
10762 + [[patch.unused]]
10763 + name = "synckit-client"
10764 + version = "0.10.0"
10765 +
10766 + [[patch.unused]]
10767 + name = "synckit-config"
10768 + version = "0.2.0"
10769 +
10762 10770 [[patch.unused]]
10763 10771 name = "quasi-immediate"
10764 10772 version = "0.91.1"
@@ -10786,11 +10794,3 @@
10786 10794 [[patch.unused]]
10787 10795 name = "painhours"
10788 10796 version = "0.1.0"
10789 -
10790 - [[patch.unused]]
10791 - name = "synckit-client"
10792 - version = "0.10.0"
10793 -
10794 - [[patch.unused]]
10795 - name = "synckit-config"
10796 - version = "0.2.0"
@@ -46,7 +46,6 @@
46 46 {% if crate::changelog::is_published() %}<a href="/changelog">Changelog</a>{% endif %}
47 47 <a href="mailto:info@makenot.work">Contact</a>
48 48 <a href="/health" title="Service status and uptime">Status</a>
49 - <a href="#" data-prevent data-action="toggleShortcutsHelp" title="Keyboard shortcuts (?)">Shortcuts</a>
50 49 </div>
51 50 <p>&copy; 2026 Make Creative, LLC</p>
52 51 </footer>
@@ -60,6 +60,7 @@
60 60 pub mod rich_field;
61 61 pub mod schedule_field;
62 62 pub mod settings_tabs;
63 + pub mod shortcuts;
63 64 pub mod ssh_keys;
64 65 pub mod upload_field;
65 66 pub mod user_analytics;
@@ -361,7 +362,25 @@
361 362 /// fixture, and both readings are about screens that hold a session. A public
362 363 /// screen registers no mutating route, so the probe has nothing to skip.
363 364 pub fn public_mounts(app: &AppState) -> Vec<(&'static str, axum::Router)> {
364 - vec![(pricing::PATH, pricing_mount(app))]
365 + vec![
366 + (pricing::PATH, pricing_mount(app)),
367 + (shortcuts::PATH, shortcuts_mount()),
368 + ]
369 + }
370 +
371 + /// The keyboard-shortcuts overlay, on the same shell every described screen
372 + /// gets so the listing looks like the site it is drawn over.
373 + ///
374 + /// Its own mount rather than a route inside the pricing nest: an overlay
375 + /// reachable from every screen is not one screen's route, and the binding's
376 + /// address is absolute in the emitted markup (`e0c0d991`).
377 + fn shortcuts_mount() -> axum::Router {
378 + quasi_axum::Adapter::new(
379 + shortcuts::router(),
380 + std::sync::Arc::new(()),
381 + std::sync::Arc::new(pricing::renderer()),
382 + )
383 + .into_router()
365 384 }
366 385
367 386 /// The fee calculator's own nest: the page and the recompute it answers.
@@ -671,7 +671,17 @@
671 671 pub fn renderer() -> Webview {
672 672 let shell = crate::shell::described()
673 673 .with_body_first(crate::shell::skip_link(PAGE))
674 - .with_body_last(crate::shell::body_last());
674 + .with_body_last(crate::shell::body_last())
675 + // What this site offers from everywhere, which today is one key
676 + // (`e0c0d991`). On the shell rather than on a screen because that is
677 + // the whole claim chrome makes: an affordance reachable from every
678 + // screen is not a fact about any one of them, and every screen
679 + // converted after this inherits it with no further work.
680 + //
681 + // It is also what gives `Outcome::Over` somewhere to land -- a renderer
682 + // emits the overlay container for an app that declares chrome, and an
683 + // app that declares none gets a swap that does nothing.
684 + .with_chrome(crate::quasi::shortcuts::chrome());
675 685 // The `Shell::head` append that stood here is gone as of quasi 0.80
676 686 // (quasicoherent `a0e16839`). It wrote the plain `<meta name="description">`
677 687 // by hand because `Screen::summarised` reached `og:description` and
@@ -420,3 +420,47 @@
420 420 "the pressed screens and the mountable ones have diverged"
421 421 );
422 422 }
423 +
424 + /// `e0c0d991`. The shortcut is declared once on the shell, so every described
425 + /// screen offers it and none of them has to remember to.
426 + #[tokio::test]
427 + async fn a_described_screen_offers_the_shortcuts_key() {
428 + let mut h = viewing().await;
429 + let html = h.client.get("/pricing").await.text;
430 +
431 + // quasi's half: a hidden button per binding, firing on the key, aimed at
432 + // the overlay container. The renderer wires it; nothing here draws a list.
433 + assert!(
434 + html.contains("data-chrome"),
435 + "the chrome is emitted: {html:.400}"
436 + );
437 + assert!(
438 + html.contains("Keyboard shortcuts"),
439 + "the binding is labelled: {html:.400}"
440 + );
441 + assert!(
442 + html.contains("/shortcuts"),
443 + "and it reaches the listing: {html:.400}"
444 + );
445 +
446 + // And the affordance it replaces is gone.
447 + assert!(
448 + !html.contains("toggleShortcutsHelp"),
449 + "the data-action link was retired: {html:.400}"
450 + );
451 + }
452 +
453 + /// The listing is ours, and it answers as something drawn over the page rather
454 + /// than as a page of its own.
455 + #[tokio::test]
456 + async fn the_shortcuts_listing_names_every_key_this_site_binds() {
457 + let mut h = viewing().await;
458 + let html = h.client.htmx_get("/shortcuts").await.text;
459 +
460 + // The described binding, read off the Chrome rather than written twice.
461 + assert!(html.contains('?'), "the described key: {html:.400}");
462 + // And the three the host still owns.
463 + for key in ["Cmd+K", "Esc", "Cmd+S"] {
464 + assert!(html.contains(key), "{key} is listed: {html:.400}");
465 + }
466 + }
@@ -38,6 +38,23 @@
38 38 document.body.appendChild(overlay);
39 39 }
40 40
41 + /**
42 + * Whether this page declares its shortcuts rather than hard-coding them.
43 + *
44 + * `e0c0d991`. A described document carries `quasi-webview`'s chrome: one hidden
45 + * button per binding, marked `data-chrome`, and `?` is bound to the described
46 + * listing. This file loads on described and undescribed pages alike -- both are
47 + * served from one shell -- so without the check both handlers fire on `?` and
48 + * the reader gets two overlays.
49 + *
50 + * The described page wins, because its listing is derived from the bindings and
51 + * this one is a hand-written copy. When the last template converts, this
52 + * function and the overlay below it go with it.
53 + */
54 + function describedChrome(): boolean {
55 + return document.querySelector('[data-chrome]') !== null;
56 + }
57 +
41 58 /** Install global keyboard shortcuts + the nav toggle. Ported from mnw.js:504. */
42 59 export function initKeyboard(): void {
43 60 document.addEventListener('keydown', (e) => {
@@ -60,7 +77,7 @@
60 77 searchInput.select();
61 78 }
62 79 }
63 - if (e.key === '?' && !inInput && !e.metaKey && !e.ctrlKey) {
80 + if (e.key === '?' && !inInput && !e.metaKey && !e.ctrlKey && !describedChrome()) {
64 81 e.preventDefault();
65 82 toggleShortcutsHelp();
66 83 }
@@ -1,0 +1,101 @@
1 + //! The keyboard shortcuts this site offers, and the overlay that lists them.
2 + //!
3 + //! `e0c0d991`, decided 2026-08-30. This server declared no `Chrome` at all --
4 + //! the only one in the tree was `Chrome::new()`, the empty default, used twice
5 + //! in [`super::embeds`] for embeds that deliberately have no shell.
6 + //!
7 + //! Paid with the `/pricing` port rather than after it. Every screen converted
8 + //! from here on inherits the shortcut with no further work, which is the whole
9 + //! reason to do it now instead of accumulating a site-wide gap to close under
10 + //! launch pressure.
11 + //!
12 + //! # What quasi does, and what this owns
13 + //!
14 + //! quasi wires the key and does not draw the listing.
15 + //! `quasi-webview`'s `binding_html` emits a hidden button whose action targets
16 + //! the overlay container, and that is all of it; `Binding::group` names "the
17 + //! heading a listing shows it beneath", which says plainly that the listing is
18 + //! a screen the app describes. So the plumbing is quasi's and the screen is
19 + //! ours. There is no renderer-drawn help overlay to go looking for.
20 + //!
21 + //! # Its own mount, not a route inside the pricing nest
22 + //!
23 + //! An overlay reachable from every screen is not one screen's route. The
24 + //! binding's address is absolute in the emitted markup, so registering it under
25 + //! `/pricing` would give the site-wide shortcut a pricing-shaped address and
26 + //! move it the first time a second screen converted.
27 +
28 + use quasi_router::screen::{Cell, Cells, Column};
29 + use quasi_router::{
30 + Action, Chrome, Node, Outcome, RegionKind, Request, Response, RouteError, Router, Screen, Slot,
31 + };
32 +
33 + /// Where the listing answers.
34 + pub const PATH: &str = "/shortcuts";
35 +
36 + /// The key that opens it.
37 + const OPENS: &str = "?";
38 +
39 + /// What this site binds.
40 + ///
41 + /// One binding, and the listing below says why the other three are not here:
42 + /// a `Binding` carries an [`Action`], and Escape, Cmd+S and Cmd+K perform no
43 + /// route. They are `static/dist/core/keyboard.js`'s, and they move into this
44 + /// function on the day the description layer can say what they do.
45 + ///
46 + /// Hung on the shell rather than built per screen, so every described screen
47 + /// this server serves offers it and none of them has to remember to.
48 + #[must_use]
49 + pub fn chrome() -> Chrome {
50 + Chrome::new().bind(OPENS, "Keyboard shortcuts", Action::get(PATH))
51 + }
52 +
53 + /// One key and what it does, for the listing.
54 + ///
55 + /// The described binding is read off [`chrome`] rather than written again here,
56 + /// which is the point `Binding::label` exists to make: a help overlay that
57 + /// lists the bindings is otherwise a second, hand-written copy that drifts.
58 + /// The host keys have no `Binding` to read, so they are spelled -- once, beside
59 + /// the declaration, rather than in a template.
60 + const HOST_KEYS: &[(&str, &str)] = &[
61 + ("Cmd+K", "Search"),
62 + ("Esc", "Close modal or overlay"),
63 + ("Cmd+S", "Save the current form"),
64 + ];
65 +
66 + /// The listing, drawn over whatever the reader was looking at.
67 + ///
68 + /// A table because that is what it is: two columns, one row per key. The
69 + /// shipped overlay was a hand-written `<table>` inside a string of markup in
70 + /// `keyboard.js`, and this is the same thing said once.
71 + pub fn screen(_state: &(), _request: Request) -> Result<Response, RouteError> {
72 + let mut rows: Vec<Cells> = chrome()
73 + .bindings
74 + .iter()
75 + .map(|binding| Cells::new([Cell::new(&binding.key), Cell::new(&binding.label)]))
76 + .collect();
77 + rows.extend(
78 + HOST_KEYS
79 + .iter()
80 + .map(|(key, what)| Cells::new([Cell::new(*key), Cell::new(*what)])),
81 + );
82 +
83 + Ok(Outcome::Over(
84 + Screen::list_detail("Keyboard shortcuts", false).with(
85 + Slot::new("shortcuts", RegionKind::Modal)
86 + .label("Keyboard shortcuts")
87 + .with(Node::Table {
88 + columns: vec![Column::new("Key"), Column::new("Does")],
89 + rows,
90 + more: None,
91 + }),
92 + ),
93 + )
94 + .into())
95 + }
96 +
97 + /// The listing's own nest.
98 + #[must_use]
99 + pub fn router() -> Router<()> {
100 + Router::<()>::new().get("/", screen)
101 + }