This prompt created the UI. It was run in the same conversation as the one that created shepherdd, and was also provided to Claude Opus 4.5 via VSCode Agent mode.

It was produced by ChatGPT after some back-and-forth [in this conversation](https://chatgpt.com/share/6952b1fb-35e4-800b-abca-5f212f9d77e5).

The output was committed as e2013eb6949298ce1f1c32e24f7a2a1785718154.

-----

Now implement the UI that integrates with `shepherdd` as described below.

---

# Linux / Wayland Launcher UI Specification

## 1. Purpose and scope

The Linux/Wayland launcher UI is the **primary user-facing shell** for a kiosk-style, child-friendly computing environment backed by `shepherdd`.

Its responsibilities are **presentation and input only**:

* display what `shepherdd` allows *right now*
* request launches and stops
* display authoritative state (time remaining, warnings)
* never enforce policy on its own

The launcher **must not**:

* track time limits independently
* decide availability rules
* attempt to supervise or kill applications

If the launcher crashes or disconnects, `shepherdd` continues enforcement.

---

## 2. High-level architecture

The Linux UI is split into **two cooperating Wayland clients**:

1. **Launcher UI** (main grid)
2. **HUD / Overlay UI** (always visible)

Both are ordinary Wayland clients and communicate **only** with `shepherdd` over local IPC.

```
┌───────────────────────────────────────────┐
│ Sway (kiosk-configured compositor)        │
│                                           │
│ ┌───────────────┐   ┌──────────────────┐ │
│ │ HUD Overlay   │   │ Running App      │ │
│ │ (layer-shell) │   │ (fullscreen)     │ │
│ └───────────────┘   └──────────────────┘ │
│                                           │
│ ┌───────────────────────────────────────┐ │
│ │ Launcher UI (grid, home screen)        │ │
│ └───────────────────────────────────────┘ │
└───────────────────────────────────────────┘
```

---

## 3. Compositor assumptions (Sway)

### 3.1 Required compositor features

The launcher assumes a Wayland compositor that supports:

* **wlr-layer-shell** (for always-on HUD)
* fullscreen surfaces
* IPC or rules to:

  * force fullscreen
  * prevent workspace switching
  * disable escape keybindings

Sway is the reference implementation.

### 3.2 Kiosk constraints to enforce in Sway config

The system must:

* expose **no user-visible workspaces**
* disable:

  * Alt+Tab
  * Mod+key bindings
  * exit / reload bindings
* force launched applications fullscreen
* ensure layer-shell overlay is always topmost

The launcher UI **must not rely on Sway configuration details**, but it must assume these constraints hold.

---

## 4. Process model

### 4.1 Launcher UI process

* GTK4 Wayland application
* normal Wayland surface
* visible only when:

  * no session is running
  * or user explicitly returns to “home”

### 4.2 HUD / Overlay process

* GTK4 Wayland application using **layer-shell**
* always visible, regardless of running app
* anchored to a screen edge (typically top)

### 4.3 Privilege

* runs as the **same user** as the compositor
* does **not** require elevated privileges
* all privileged actions go through `shepherdd`

---

## 5. IPC contract with `shepherdd`

### 5.1 Connection

* Unix domain socket (path configurable)
* persistent connection
* reconnect logic required

### 5.2 Commands issued by the launcher UI

* `ListEntries`
* `Launch { entry_id }`
* `StopCurrent`
* `GetState`
* `SubscribeEvents`

### 5.3 Events consumed by launcher & HUD

* `StateChanged`
* `SessionStarted`
* `WarningIssued`
* `SessionExpired`
* `SessionEnded`
* `PolicyReloaded`

The UI **must treat daemon state as authoritative**.

---

## 6. Launcher UI (main grid)

### 6.1 Visual layout

* Fullscreen window
* Grid of large, touch-friendly icons
* Each tile represents one `EntryView` from `shepherdd`

Each tile must show:

* icon
* label
* enabled / disabled state
* disabled reason (optional text or icon)

### 6.2 Availability handling

For each entry:

* If disabled:

  * tile is visually muted
  * tapping does nothing
  * optional “why” tooltip (e.g. “Not available until 3pm”)
* If enabled:

  * tapping sends `Launch(entry_id)`
  * UI transitions to “launching” state

### 6.3 Launch feedback

When a launch is requested:

* grid input is disabled
* show:

  * “Starting…” indicator
* wait for:

  * `SessionStarted` → transition away
  * or error → restore grid

### 6.4 Return to home

The launcher UI must be able to re-appear when:

* session ends
* user presses “close” in HUD (if allowed)
* daemon requests shell visibility

---

## 7. HUD / Overlay UI (always visible)

### 7.1 Placement and layer

* Wayland **layer-shell**
* layer: `overlay`
* exclusive zone: minimal (or none)
* must not be obscured by fullscreen apps

### 7.2 Required elements

The HUD must display, at minimum:

1. **Time remaining**

   * authoritative countdown from daemon
   * format: `MM:SS` or `H:MM:SS`
   * visually emphasized when below warning thresholds

2. **Battery level**

   * via UPower (local system API)
   * percent + icon
   * daemon does not own battery state

3. **Volume**

   * current output volume
   * mute state
   * controls may:

     * send `SetVolume` via daemon
     * or adjust locally (implementation choice)

4. **Close / End session button**

   * sends `StopCurrent`
   * availability depends on daemon state
   * may be hidden or disabled for certain entries

5. **Power button**

   * opens a small modal:

     * Suspend
     * Shutdown
     * Restart
   * actions are forwarded to daemon (daemon decides if allowed)

### 7.3 Warning presentation

On `WarningIssued`:

* show banner or modal in HUD
* severity reflected visually
* optional sound cue
* must not block rendering of underlying app

On `SessionExpired`:

* show “Time’s up” overlay
* remain visible until session ends and home returns

### 7.4 Failure handling

If daemon disconnects:

* HUD must:

  * show “Disconnected” indicator
  * attempt reconnect
  * **not** fabricate time remaining

---

## 8. Interaction between launcher and HUD

* Launcher and HUD **do not communicate directly**
* Both reflect the same daemon state
* HUD remains visible while launcher is hidden
* Launcher must ignore input while a session is running

---

## 9. Input and accessibility considerations

### 9.1 Input

* Touch-friendly sizing
* Mouse and keyboard supported
* No reliance on keyboard shortcuts for core actions

### 9.2 Accessibility

* High contrast mode recommended
* Large fonts
* Minimal text; icon-first design
* Avoid hover-only affordances

---

## 10. Error states and recovery

### 10.1 Daemon unavailable at startup

* Show blocking error screen:

  * “System not ready”
  * retry button
* Do not allow launching without daemon

### 10.2 App fails to launch

* Daemon emits failure
* Launcher returns to grid
* Optional error message

### 10.3 Session ends unexpectedly

* HUD updates immediately
* Launcher reappears automatically

---

## 11. Non-functional requirements

### 11.1 Robustness

* UI must tolerate daemon restarts
* UI must tolerate compositor restarts (best effort)

### 11.2 Determinism

* Countdown is driven only by daemon events/state
* UI must never infer remaining time independently

### 11.3 Maintainability

* No daemon logic embedded in UI
* All policy logic treated as opaque

---

## 12. Implementation constraints and recommendations

* Toolkit: **GTK4**
* Language: **Rust (gtk-rs)** preferred
* Layer-shell: `gtk4-layer-shell` (or equivalent)
* IPC client: reuse `shepherd-api` and `shepherd-ipc` types

---

## 13. Explicit non-goals

* Embedding third-party apps inside the launcher
* Replacing the compositor
* Managing system accounts or users
* Performing enforcement locally in UI

---

## 14. Success criteria

The launcher UI is considered correct if:

* It can fully drive the system using only daemon IPC.
* It remains usable while arbitrary fullscreen apps (VMs, emulators) run underneath.
* Time warnings and expiry are always visible and accurate.
* Replacing the launcher UI with another client would not weaken enforcement.
