max / alloy_tui
- Co-Authored-By
- Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6 files changed,
+97 insertions,
-3 deletions
| @@ -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). | |
| @@ -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 | + | } |