Skip to main content

max / alloy

Pick lockscreen as first marquee; stub alloy_lockscreen Resolve the table-vs-prose tension in MARQUEE-APPS.md in favor of lockscreen-first: smaller contained Wayland integration than the package GUI, exercises both fonts in their canonical roles, and shakes down alloy_ui before the package GUI puts real load on it. Package GUI moves to "v1 #2 — design-system reusability proof," starting once the lockscreen is past first-light. Scaffold crates/alloy_lockscreen/ as a stub: - Cargo crate compiles into a placeholder bin (alloy-lock). - README captures v1 scope (clock, date, password, multi-monitor), explicit non-goals (media, notifications, wallpaper, animation), surface layout, design-system call-outs, and the four open questions that still block real implementation (Wayland crate, PAM crate, process model, recovery path). No Wayland or PAM code yet — those land after the open-question calls in the README are made.
Co-Authored-By
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-06-26 19:32 UTC
Signed with PGP, not checked
Commit: 142b6314bc379a19c64b255a509b62611ecd7a7d
Parent: 0330be6
6 files changed, +97 insertions, -3 deletions
M Cargo.lock +7
@@ -29,6 +29,13 @@
29 29 "zerocopy",
30 30 ]
31 31
32 + [[package]]
33 + name = "alloy_lockscreen"
34 + version = "0.0.0"
35 + dependencies = [
36 + "alloy_ui",
37 + ]
38 +
32 39 [[package]]
33 40 name = "alloy_ui"
34 41 version = "0.0.0"
@@ -4,8 +4,8 @@
4 4
5 5 | Tier | App | Why it qualifies |
6 6 |---|---|---|
7 - | Concurrent with v0 | **Package GUI** | The pedagogical wedge product. Teaches the rpm-ostree layered / Flatpak / distrobox decision. This is the hardest thing on Silverblue and the original first-tool framing. |
8 - | v1 | **Lockscreen** | Small, high-visibility (every wake), security-sensitive (simplicity helps), ext-session-lock-v1 is a clean protocol, no production-quality Rust lockscreen exists today. Better first-egui-app than the package GUI because it's smaller and more visible. |
7 + | v1 (#1, alloy_ui shakedown) | **Lockscreen** | Small, high-visibility (every wake), security-sensitive (simplicity helps), ext-session-lock-v1 is a clean protocol, no production-quality Rust lockscreen exists today. First marquee app: smallest contained Wayland integration, exercises both fonts (Iosevka for the password field, Departure Mono for clock and date) in their canonical roles. |
8 + | v1 (#2, design-system reusability proof) | **Package GUI** | The pedagogical wedge product. Teaches the rpm-ostree layered / Flatpak / distrobox decision. This is the hardest thing on Silverblue and the original first-tool framing. Starts as soon as `alloy_ui` is shaken down by the lockscreen; ships into v0 stack alongside it. |
9 9 | v1 candidate | **Wallpaper picker** | Browse the curated impressionist collection ([STACK.md](STACK.md#wallpapers)) with painter / title / year / source metadata, preview, set-as-current. Small scope, distinct surface from the lockscreen, gives the collection a first-class home rather than treating it as filesystem chrome. Bumps to v1 if scoped tight. |
10 10 | v1 candidate | **TextEdit-equivalent** | Small native-feeling graphical text editor for ordinary users (audience: the same people the package GUI serves — opened a config file from the file manager). Intentionally scoped down: open/edit/save, multi-tab, find/replace, tree-sitter highlighting, real OS file dialogs. No project tree, no LSP, no debugger, no plugin system. Power users install Helix. ~2–3 month scope. Pairs with the non-reactive principle as a concrete proof point. |
11 11 | v2 | **Notification daemon** | High-visibility (every alert), bounded scope, real Rust ecosystem gap. More scope than the lockscreen — action invocation, persistent history, urgency, replace-id semantics, DND — but well-defined protocol (`org.freedesktop.Notifications` D-Bus + wlr-layer-shell). |
M docs/todo.md +1 -1
@@ -20,7 +20,7 @@
20 20 - [x] Set up the repo: git init, license, contribution model. → **GPLv3-or-later** (deliberate pick; egui is MIT/Apache so the choice was open). [LICENSE](../LICENSE), [CONTRIBUTING.md](../CONTRIBUTING.md).
21 21 - [ ] Branding assets: wordmark/logotype, repo social card, favicon. Deferred until first user-facing surface ships.
22 22 - [x] Scaffold the Alloy egui design-system crate. → `crates/alloy_ui/` with `tokens.toml` → `build.rs` → `src/generated/tokens.rs` pipeline, `apply_alloy_visuals`, starter primitives (`primary_button`, `section`, `field_row`, `SeverityTag`, `focus_ring`), and an `examples/showcase` binary. Widget set is intentionally minimal — grow it as marquee apps need it, not preemptively.
23 - - [ ] Pick the first concrete deliverable to build (lockscreen v1 or package GUI — both are listed as egui apps; sequencing matters).
23 + - [x] Pick the first concrete deliverable to build. → **Lockscreen v1.** Smaller contained Wayland integration than the package GUI, security-critical scope favors simplicity, exercises both fonts in their canonical roles (Iosevka for the password field, Departure Mono for clock/date readouts), and shakes down `alloy_ui` on a small surface before the package GUI puts real load on it. Package GUI starts as the design-system reusability proof once the lockscreen is past first-light. See [MARQUEE-APPS.md](MARQUEE-APPS.md).
24 24
25 25 ## v0 stack to package and document
26 26
@@ -1,0 +1,16 @@
1 + [package]
2 + name = "alloy_lockscreen"
3 + version = "0.0.0"
4 + description = "Alloy lockscreen: ext-session-lock-v1 + PAM, rendered in egui."
5 + edition.workspace = true
6 + rust-version.workspace = true
7 + license.workspace = true
8 + repository.workspace = true
9 + authors.workspace = true
10 +
11 + [[bin]]
12 + name = "alloy-lock"
13 + path = "src/main.rs"
14 +
15 + [dependencies]
16 + alloy_ui = { path = "../alloy_ui" }
@@ -1,0 +1,57 @@
1 + # alloy_lockscreen
2 +
3 + The Alloy lockscreen. First marquee app; first concrete consumer of `alloy_ui`. Binary name: `alloy-lock`.
4 +
5 + ## Status
6 +
7 + Stub. The crate compiles, depends on `alloy_ui`, and prints a placeholder. Wayland and PAM integration land after the open questions below are resolved.
8 +
9 + ## v1 scope
10 +
11 + Deliberately small. Everything not on this list is out of scope for v1.
12 +
13 + **In scope**
14 + - Lock the session via `ext-session-lock-v1`. Render a fullscreen surface per output.
15 + - Display the current time, the current date, and a single password field.
16 + - Authenticate via PAM (`pam` system service stack).
17 + - Honor system locale for time/date formatting.
18 + - Multi-monitor: render an identical lockscreen on every connected output. Capture the keyboard on whichever output the cursor is on at lock time; mirror keystrokes' visual feedback (dot count, error state) on the others.
19 + - Theme via `alloy_ui::apply_alloy_visuals` so the lockscreen tracks the system mode.
20 +
21 + **Out of scope for v1**
22 + - Media controls (play/pause, skip, volume).
23 + - Notification preview on the lock surface.
24 + - Wallpaper / image background. The v1 surface is the chrome ramp; the wallpaper picker app handles image curation separately.
25 + - Fingerprint, smartcard, or any non-PAM-password auth path.
26 + - Custom keymap handling beyond what the compositor passes through.
27 + - Animation. The lockscreen is a stable surface; the only motion is the cursor blink and the password-dot reveal as keys land. See [DESIGN-LANGUAGE.md](../../docs/DESIGN-LANGUAGE.md) on the non-reactive principle.
28 + - Session-switching, user-picker. Single-user assumption holds for v1.
29 +
30 + ## Surface layout
31 +
32 + Authored mockup pending. Working sketch:
33 +
34 + - Centered column on each output.
35 + - Top: the time, **Departure Mono** at `size-readout-xl`, `tracking-display-caps`. This is the lockscreen's reason to exist visually — the display font's canonical surface.
36 + - Beneath the time: the date, Departure Mono at `size-readout-sm`, uppercase.
37 + - Below the date, separated by `space-xl`: the password field, **Iosevka** at `size-body`, no visible label, focus ring per `alloy_ui::focus_ring`. Reveals dots as keys are typed, never the password text.
38 + - Below the password field: a single-line status area in Iosevka at `size-caption`. Empty on idle; carries the PAM error message after a failed attempt.
39 +
40 + No other chrome. No clock seconds. No battery indicator on the lockscreen — the lockscreen is a wake surface, not a status display.
41 +
42 + ## Design-system call-outs
43 +
44 + - This is the first place Departure Mono renders in a real Alloy surface. Verify the font registration in `alloy_ui::theme` before relying on it here, and document the fallback chain if Departure's unicode coverage misses anything in the user's locale-formatted date.
45 + - The focus ring is `border-strong`, never accent. Failed-auth state may *temporarily* color the status text in `accent-error`, per the accent-on-text rule in [TOKENS.md](../../docs/TOKENS.md#accent-on-surface-text-rule). The password field's border does **not** flip to accent.
46 + - The lockscreen runs at `surface` tier. The password field sits on `surface-raised`. No popovers, no overlays — there is nowhere for an overlay to go on a lockscreen.
47 +
48 + ## Open questions blocking implementation
49 +
50 + 1. **Wayland protocol crate.** `wayrs-client` (lighter, async-ergonomic, smaller dep tree, newer), `smithay-client-toolkit` (canonical, larger, more battle-tested), or raw `wayland-client` (control, more code). Decision criterion: what gives us a clean ext-session-lock-v1 binding with the smallest stable dep surface. To be resolved before any Wayland code lands.
51 + 2. **PAM crate.** `pam` (canonical bindings, may be stale), `pam-client` (newer, more ergonomic), or vendoring a thin FFI ourselves. PAM is a small enough API surface that vendoring is reasonable if no current crate is well-maintained.
52 + 3. **Process model.** Single-process binding all outputs, or one process per output. Single-process is simpler and matches how swaylock works; one-process-per-output gets us isolation if PAM blocks. Default: single-process.
53 + 4. **Recovery path.** If the lockscreen process crashes while holding the session lock, the compositor stays locked and the user is stranded. ext-session-lock-v1 has explicit semantics for this; document the chosen recovery path (compositor-side fallback, watchdog, intentional crash-to-greeter) before shipping.
54 +
55 + ## Non-goals as a marquee app
56 +
57 + The lockscreen is not where we prove the design system handles dense forms, scrolling lists, or popovers. Its scope is intentionally narrow: a clock, a date, a password field. The package GUI takes those broader proofs once `alloy_ui` is shaken down here.
@@ -1,0 +1,14 @@
1 + //! Alloy lockscreen — placeholder entry point.
2 + //!
3 + //! Real implementation is pending the Wayland protocol-crate decision
4 + //! (`wayrs-client` vs `smithay-client-toolkit` vs raw `wayland-client`) and the
5 + //! PAM integration. See `README.md` for scope, design call-outs, and the open
6 + //! questions blocking implementation.
7 +
8 + fn main() {
9 + eprintln!(
10 + "alloy-lock: not yet implemented. \
11 + See crates/alloy_lockscreen/README.md for the scoped v1 plan."
12 + );
13 + std::process::exit(1);
14 + }