Ticket UI polish: stale-fade, disconnected overlay, favicon, v0.7#10
Merged
Conversation
…ion tickets #7: .brand font-size 14px → 18px, .brand-version 11px → 14px, .conn dot 11px → 14px with its connected-state glow 3px → 4px. Pure CSS, no layout changes. #1: New staleClass(ticket, now) helper alongside typeClass/formatAge returns 'stale-1'..'stale-4' based on age in minutes (>=2, >=4, >=8, >=16). PermissionRequest tickets and working tickets always return '' so red attention and active processing never fade. Wired into the <li> class list. Four new CSS rules between .ticket.pressing and .ticket.working apply filter: saturate(N) at 0.75 / 0.5 / 0.25 / 0 — the whole ticket (bg, border, title text, accents) ages together in step jumps. Placed before .ticket.working so the pastel-green wins on the defensive co-apply case. The existing 5s `now` reactive tick already drives age-label re-renders, so tier rollovers ride that same render pass — zero new timers or allocations.
…appable tickets #10. Introduced an everConnected sticky flag (flips true on first SSE onopen, never resets) and an isDisconnected derived (!!clientToken && !connected && everConnected). The sticky flag prevents a false-positive disconnected flash on every page load and on every visibility-change wake, since `connected` starts false until onopen has had a chance to fire on the re-opened EventSource. When isDisconnected: a small uppercase "disconnected" banner renders below the header (role="status", aria-live="polite"), the ticket list <ul> gets a `disconnected` class that drops opacity to 0.5 and sets pointer-events: none so taps cannot queue /api/focus against a dead daemon, and the "You have zero tickets!" empty-state is suppressed (the banner alone tells the truth — the prior message implied live state). Mock mode is unaffected (connected = true short-circuits isDisconnected regardless of everConnected). focusing state still self-clears via the existing finally setTimeout, so an in-flight tap at the moment of disconnect doesn't strand the pressing visual.
Copied a placeholder mark (single-path SVG with prefers-color-scheme auto-invert) from PycharmProjects/webpage and dropped it into static/ as favicon.svg, favicon.ico, favicon-32.png, favicon-16.png. app.html previously declared only <link rel="apple-touch-icon"> pointing at the iOS home-screen icon, which is why browsers were auto-deriving a generic "E" tab icon from it. Added explicit <link rel="icon"> declarations (SVG-first, with ico + sized PNG fallbacks for older browsers) so the tab favicon is no longer derived from the iOS icon. The existing apple-touch-icon line is untouched, so the home-screen icon is unaffected.
In-memory lastTapped state (string | null, no persistence — lost on page reload) is set inside focusSession at the start of the tap, alongside the existing focusing assignment. The <li> gets class:tapped={lastTapped === ticket.session_id} so the most recently tapped ticket renders with a stronger box-shadow (0 8px 24px) and z-index: 1, reading visually as "lifted." Pure box-shadow + z-index — no transform — so the marker composes cleanly with the existing pressing (scale 0.985), working (scale 0.985 + pastel-green), and stale-N (filter: saturate(N)) state modifiers without conflict. Caveat documented inline: goes stale if the user switches sessions inside the terminal directly (no AppleScript polling); accepted for v0.
- Notch: semi-circle pushed 1px outward, no curve border. Reads as a cutout, not a disc. - Tapped: dropped the shadow; the most recently tapped ticket's title bolds to 700. - Tap release: focusing-state hold cut from 300ms to 80ms. - iOS tap-highlight overlay killed via -webkit-tap-highlight-color: transparent. - Disconnected: tickets fade to opacity 0 over 1.2s; centered "you are disconnected" overlay fades in over 900ms. - Header version bumped to v0.7. - Layout's orphan Svelte-logo favicon import removed so app.html's custom favicon links win.
AsteroidHunter
added a commit
that referenced
this pull request
May 21, 2026
* Bumped header sizes and added stale-fade tiers to idle Stop/Notification tickets #7: .brand font-size 14px → 18px, .brand-version 11px → 14px, .conn dot 11px → 14px with its connected-state glow 3px → 4px. Pure CSS, no layout changes. #1: New staleClass(ticket, now) helper alongside typeClass/formatAge returns 'stale-1'..'stale-4' based on age in minutes (>=2, >=4, >=8, >=16). PermissionRequest tickets and working tickets always return '' so red attention and active processing never fade. Wired into the <li> class list. Four new CSS rules between .ticket.pressing and .ticket.working apply filter: saturate(N) at 0.75 / 0.5 / 0.25 / 0 — the whole ticket (bg, border, title text, accents) ages together in step jumps. Placed before .ticket.working so the pastel-green wins on the defensive co-apply case. The existing 5s `now` reactive tick already drives age-label re-renders, so tier rollovers ride that same render pass — zero new timers or allocations. * Added a real disconnected state: "disconnected" banner + dimmed non-tappable tickets #10. Introduced an everConnected sticky flag (flips true on first SSE onopen, never resets) and an isDisconnected derived (!!clientToken && !connected && everConnected). The sticky flag prevents a false-positive disconnected flash on every page load and on every visibility-change wake, since `connected` starts false until onopen has had a chance to fire on the re-opened EventSource. When isDisconnected: a small uppercase "disconnected" banner renders below the header (role="status", aria-live="polite"), the ticket list <ul> gets a `disconnected` class that drops opacity to 0.5 and sets pointer-events: none so taps cannot queue /api/focus against a dead daemon, and the "You have zero tickets!" empty-state is suppressed (the banner alone tells the truth — the prior message implied live state). Mock mode is unaffected (connected = true short-circuits isDisconnected regardless of everConnected). focusing state still self-clears via the existing finally setTimeout, so an in-flight tap at the moment of disconnect doesn't strand the pressing visual. * Added a real browser-tab favicon (SVG + ico + 16/32 PNG fallbacks) Copied a placeholder mark (single-path SVG with prefers-color-scheme auto-invert) from PycharmProjects/webpage and dropped it into static/ as favicon.svg, favicon.ico, favicon-32.png, favicon-16.png. app.html previously declared only <link rel="apple-touch-icon"> pointing at the iOS home-screen icon, which is why browsers were auto-deriving a generic "E" tab icon from it. Added explicit <link rel="icon"> declarations (SVG-first, with ico + sized PNG fallbacks for older browsers) so the tab favicon is no longer derived from the iOS icon. The existing apple-touch-icon line is untouched, so the home-screen icon is unaffected. * Added a "tapped" raised-shadow marker on the last-tapped ticket In-memory lastTapped state (string | null, no persistence — lost on page reload) is set inside focusSession at the start of the tap, alongside the existing focusing assignment. The <li> gets class:tapped={lastTapped === ticket.session_id} so the most recently tapped ticket renders with a stronger box-shadow (0 8px 24px) and z-index: 1, reading visually as "lifted." Pure box-shadow + z-index — no transform — so the marker composes cleanly with the existing pressing (scale 0.985), working (scale 0.985 + pastel-green), and stale-N (filter: saturate(N)) state modifiers without conflict. Caveat documented inline: goes stale if the user switches sessions inside the terminal directly (no AppleScript polling); accepted for v0. * Ticket UI polish: cutout notch, centered disconnected overlay, v0.7 - Notch: semi-circle pushed 1px outward, no curve border. Reads as a cutout, not a disc. - Tapped: dropped the shadow; the most recently tapped ticket's title bolds to 700. - Tap release: focusing-state hold cut from 300ms to 80ms. - iOS tap-highlight overlay killed via -webkit-tap-highlight-color: transparent. - Disconnected: tickets fade to opacity 0 over 1.2s; centered "you are disconnected" overlay fades in over 900ms. - Header version bumped to v0.7. - Layout's orphan Svelte-logo favicon import removed so app.html's custom favicon links win. * Added uno + tour subcommand dispatch to bin/claudex.sh (Phases 2-4 of claudex-uno) Three new subcommands of claudex, dispatched by a `case "${1:-}"` after the existing tmux/claude/expediter presence checks: claudex (existing) tmux session with claude + expediter side-by-side claudex uno newbie onboarding: execs `expediter --steps "..."` with the four pipe-delimited steps (Scan QR / Open tab → tour sonnet / Open tab → tour haiku / watch your phone) claudex tour sonnet fresh tmux session running `claude --model sonnet "<v5 explainer prompt>"` claudex tour haiku fresh tmux session running `claude --model haiku "Write a haiku about a positive, collaborative human-AI future."` anything else prints usage + exit 1 The tour flow builds a unique session name `claudex-tour-<model>-$(date +%s)`, shell-escapes the prompt via printf '%q' for tmux's `/bin/sh -c`, runs `tmux new-session -d -s "$session" "claude --model $MODEL $QUOTED_PROMPT"`, then attaches via `tmux switch-client` (inside tmux) or `tmux attach-session` (outside) — mirroring the existing default-claudex pattern. The Sonnet explainer prompt is loaded into a shell variable via `IFS= read -r -d '' PROMPT <<'END_PROMPT' || true`, not via `PROMPT=$(cat <<'END_PROMPT' ... END_PROMPT)`, because macOS bash 3.2's command-substitution parser misparses apostrophes inside heredocs (even quoted-delimiter heredocs) as opening single-quote strings, breaking `bash -n` on a prompt that contains `You're`, `you'll`, `don't`, etc. read -d '' reads the heredoc directly into the variable without command-substitution wrapping and sidesteps the bug entirely. Validated `bash -n` clean and exercised the three error paths: `claudex bogus`, `claudex tour`, `claudex tour bogus` all exit 1 with the expected usage messages. * Changed default `claudex` from two tmux windows to side-by-side panes Previously created two windows (claude + expediter) with the user landing on claude and the QR hidden behind Ctrl-b n. New flow: one window split via `tmux split-window -h` into pane 0 (left, expediter + QR) and pane 1 (right, claude, focused on attach). QR visible from the moment the user attaches; no tmux navigation required. Header comment also updated to describe the new default and list the uno/tour subcommands introduced earlier in the claudex-uno plan. * Added `claudex uno` to install.sh closing's "Few ways to use expediter" list (Phase 5 of claudex-uno) Adjusted the three-command closing-cheat-sheet alignment to a 14-character description-column start (previously 12). All three lines now line up: expediter start the daemon and print the QR for linking your phone claudex open tmux with claude + expediter side-by-side claudex uno new to tmux or Claude Code? start here Surfaces the newbie onboarding path directly at install completion. Without this line, `claudex uno` was only discoverable via README. * Added --steps flag to bin/expediter.mjs alongside --title Pipe-delimited list of numbered steps that prints below the QR. Opt-in via `--steps "<s1>|<s2>|..."`; plain `expediter` never prints steps. Used by `claudex uno` to surface the four newbie-onboarding lines. Reapplied by hand rather than cherry-picking the original 0a783db, because that commit deleted the --title handler (it was authored before --title existed on main). Adds STEPS_IDX/STEPS_RAW alongside TITLE_IDX/TITLE_VALUE, appends a --steps section to --help, and prints the steps in printAccess after the optional --print-url block. * Claudex polish: tour prompts as .txt, pane-keep-alive, bolded install closing - claudex tour: moved the sonnet and haiku prompts out of inline heredocs into bin/uno_prompts/<model>.txt. The script resolves $EXPEDITER_HOME/bin/uno_prompts/<model>.txt and passes claude `Read <path> and respond per its contents.` so prompts are editable without touching shell quoting. - claudex (default): expediter pane wraps the command as `expediter; exec ${SHELL:-bash}` so the pane stays open after expediter exits (it exits immediately when the daemon is already up, otherwise the user only sees the claude pane). - install.sh closing: bolded the "Few ways to use expediter:" header and the three commands; indented commands 2 spaces; trailing blank line after the last command.
AsteroidHunter
added a commit
that referenced
this pull request
May 21, 2026
* Bumped header sizes and added stale-fade tiers to idle Stop/Notification tickets #7: .brand font-size 14px → 18px, .brand-version 11px → 14px, .conn dot 11px → 14px with its connected-state glow 3px → 4px. Pure CSS, no layout changes. #1: New staleClass(ticket, now) helper alongside typeClass/formatAge returns 'stale-1'..'stale-4' based on age in minutes (>=2, >=4, >=8, >=16). PermissionRequest tickets and working tickets always return '' so red attention and active processing never fade. Wired into the <li> class list. Four new CSS rules between .ticket.pressing and .ticket.working apply filter: saturate(N) at 0.75 / 0.5 / 0.25 / 0 — the whole ticket (bg, border, title text, accents) ages together in step jumps. Placed before .ticket.working so the pastel-green wins on the defensive co-apply case. The existing 5s `now` reactive tick already drives age-label re-renders, so tier rollovers ride that same render pass — zero new timers or allocations. * Added a real disconnected state: "disconnected" banner + dimmed non-tappable tickets #10. Introduced an everConnected sticky flag (flips true on first SSE onopen, never resets) and an isDisconnected derived (!!clientToken && !connected && everConnected). The sticky flag prevents a false-positive disconnected flash on every page load and on every visibility-change wake, since `connected` starts false until onopen has had a chance to fire on the re-opened EventSource. When isDisconnected: a small uppercase "disconnected" banner renders below the header (role="status", aria-live="polite"), the ticket list <ul> gets a `disconnected` class that drops opacity to 0.5 and sets pointer-events: none so taps cannot queue /api/focus against a dead daemon, and the "You have zero tickets!" empty-state is suppressed (the banner alone tells the truth — the prior message implied live state). Mock mode is unaffected (connected = true short-circuits isDisconnected regardless of everConnected). focusing state still self-clears via the existing finally setTimeout, so an in-flight tap at the moment of disconnect doesn't strand the pressing visual. * Added a real browser-tab favicon (SVG + ico + 16/32 PNG fallbacks) Copied a placeholder mark (single-path SVG with prefers-color-scheme auto-invert) from PycharmProjects/webpage and dropped it into static/ as favicon.svg, favicon.ico, favicon-32.png, favicon-16.png. app.html previously declared only <link rel="apple-touch-icon"> pointing at the iOS home-screen icon, which is why browsers were auto-deriving a generic "E" tab icon from it. Added explicit <link rel="icon"> declarations (SVG-first, with ico + sized PNG fallbacks for older browsers) so the tab favicon is no longer derived from the iOS icon. The existing apple-touch-icon line is untouched, so the home-screen icon is unaffected. * Added a "tapped" raised-shadow marker on the last-tapped ticket In-memory lastTapped state (string | null, no persistence — lost on page reload) is set inside focusSession at the start of the tap, alongside the existing focusing assignment. The <li> gets class:tapped={lastTapped === ticket.session_id} so the most recently tapped ticket renders with a stronger box-shadow (0 8px 24px) and z-index: 1, reading visually as "lifted." Pure box-shadow + z-index — no transform — so the marker composes cleanly with the existing pressing (scale 0.985), working (scale 0.985 + pastel-green), and stale-N (filter: saturate(N)) state modifiers without conflict. Caveat documented inline: goes stale if the user switches sessions inside the terminal directly (no AppleScript polling); accepted for v0. * Ticket UI polish: cutout notch, centered disconnected overlay, v0.7 - Notch: semi-circle pushed 1px outward, no curve border. Reads as a cutout, not a disc. - Tapped: dropped the shadow; the most recently tapped ticket's title bolds to 700. - Tap release: focusing-state hold cut from 300ms to 80ms. - iOS tap-highlight overlay killed via -webkit-tap-highlight-color: transparent. - Disconnected: tickets fade to opacity 0 over 1.2s; centered "you are disconnected" overlay fades in over 900ms. - Header version bumped to v0.7. - Layout's orphan Svelte-logo favicon import removed so app.html's custom favicon links win. * Added uno + tour subcommand dispatch to bin/claudex.sh (Phases 2-4 of claudex-uno) Three new subcommands of claudex, dispatched by a `case "${1:-}"` after the existing tmux/claude/expediter presence checks: claudex (existing) tmux session with claude + expediter side-by-side claudex uno newbie onboarding: execs `expediter --steps "..."` with the four pipe-delimited steps (Scan QR / Open tab → tour sonnet / Open tab → tour haiku / watch your phone) claudex tour sonnet fresh tmux session running `claude --model sonnet "<v5 explainer prompt>"` claudex tour haiku fresh tmux session running `claude --model haiku "Write a haiku about a positive, collaborative human-AI future."` anything else prints usage + exit 1 The tour flow builds a unique session name `claudex-tour-<model>-$(date +%s)`, shell-escapes the prompt via printf '%q' for tmux's `/bin/sh -c`, runs `tmux new-session -d -s "$session" "claude --model $MODEL $QUOTED_PROMPT"`, then attaches via `tmux switch-client` (inside tmux) or `tmux attach-session` (outside) — mirroring the existing default-claudex pattern. The Sonnet explainer prompt is loaded into a shell variable via `IFS= read -r -d '' PROMPT <<'END_PROMPT' || true`, not via `PROMPT=$(cat <<'END_PROMPT' ... END_PROMPT)`, because macOS bash 3.2's command-substitution parser misparses apostrophes inside heredocs (even quoted-delimiter heredocs) as opening single-quote strings, breaking `bash -n` on a prompt that contains `You're`, `you'll`, `don't`, etc. read -d '' reads the heredoc directly into the variable without command-substitution wrapping and sidesteps the bug entirely. Validated `bash -n` clean and exercised the three error paths: `claudex bogus`, `claudex tour`, `claudex tour bogus` all exit 1 with the expected usage messages. * Changed default `claudex` from two tmux windows to side-by-side panes Previously created two windows (claude + expediter) with the user landing on claude and the QR hidden behind Ctrl-b n. New flow: one window split via `tmux split-window -h` into pane 0 (left, expediter + QR) and pane 1 (right, claude, focused on attach). QR visible from the moment the user attaches; no tmux navigation required. Header comment also updated to describe the new default and list the uno/tour subcommands introduced earlier in the claudex-uno plan. * Added `claudex uno` to install.sh closing's "Few ways to use expediter" list (Phase 5 of claudex-uno) Adjusted the three-command closing-cheat-sheet alignment to a 14-character description-column start (previously 12). All three lines now line up: expediter start the daemon and print the QR for linking your phone claudex open tmux with claude + expediter side-by-side claudex uno new to tmux or Claude Code? start here Surfaces the newbie onboarding path directly at install completion. Without this line, `claudex uno` was only discoverable via README. * Added --steps flag to bin/expediter.mjs alongside --title Pipe-delimited list of numbered steps that prints below the QR. Opt-in via `--steps "<s1>|<s2>|..."`; plain `expediter` never prints steps. Used by `claudex uno` to surface the four newbie-onboarding lines. Reapplied by hand rather than cherry-picking the original 0a783db, because that commit deleted the --title handler (it was authored before --title existed on main). Adds STEPS_IDX/STEPS_RAW alongside TITLE_IDX/TITLE_VALUE, appends a --steps section to --help, and prints the steps in printAccess after the optional --print-url block. * Claudex polish: tour prompts as .txt, pane-keep-alive, bolded install closing - claudex tour: moved the sonnet and haiku prompts out of inline heredocs into bin/uno_prompts/<model>.txt. The script resolves $EXPEDITER_HOME/bin/uno_prompts/<model>.txt and passes claude `Read <path> and respond per its contents.` so prompts are editable without touching shell quoting. - claudex (default): expediter pane wraps the command as `expediter; exec ${SHELL:-bash}` so the pane stays open after expediter exits (it exits immediately when the daemon is already up, otherwise the user only sees the claude pane). - install.sh closing: bolded the "Few ways to use expediter:" header and the three commands; indented commands 2 spaces; trailing blank line after the last command. * Rewrote README for v0 public release Added pitch + Why section, restructured Install/Uninstall with details dropdowns, renamed Use to How to use, removed home-screen install and USB-hotspot details, replaced em-dashes with --, and softened the security "what this does not stop" paragraph. * README: claudex now side-by-side panes, added claudex uno mention Reflects PR #11. Fixed two stale spots that described claudex as opening two tmux windows, and added a brief `claudex uno` paragraph in the same conversational style as the existing claudex one.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
static/, linked fromapp.html. Removed the orphan Svelte-logo import from+layout.sveltethat was overriding it.Test plan
bun run checkcleanbun test162/162 passgit diff origin/main origin/premainis empty and grepwasFront/disconnected-overlay/v0.7show up in origin/main