Skip to content

Claudex uno + tour, side-by-side panes, --steps flag#11

Merged
AsteroidHunter merged 10 commits into
mainfrom
premain
May 21, 2026
Merged

Claudex uno + tour, side-by-side panes, --steps flag#11
AsteroidHunter merged 10 commits into
mainfrom
premain

Conversation

@AsteroidHunter
Copy link
Copy Markdown
Owner

Summary

  • claudex (no args) now opens tmux with side-by-side panes — expediter (QR) on the left, claude on the right. Expediter pane stays open after the QR prints via exec ${SHELL:-bash}.
  • claudex uno — newbie onboarding: starts the daemon + prints the QR + four numbered onboarding steps below it.
  • claudex tour sonnet / claudex tour haiku — fresh tmux session with the respective model. The prompts live in bin/uno_prompts/<model>.txt and the script passes claude "Read <path> and respond per its contents." so prompts can be edited without touching shell quoting.
  • expediter --steps "<s1>|<s2>|..." — pipe-delimited numbered list printed below the QR (opt-in; used by claudex uno). --title flag preserved alongside.
  • Install closing message: bolded header + 3 indented bolded commands (expediter, claudex, claudex uno) with trailing blank line.

Test plan

  • bun run check clean
  • bun test 162/162 pass
  • bash -n syntax check on claudex.sh + install.sh
  • Manual: claudex opens side-by-side panes, expediter pane stays open after QR
  • After merge: git diff origin/main origin/premain empty; token grep for --steps, --title, uno_prompts, split-window, exec ${SHELL, Few ways in origin/main

…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.
… 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.
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.
…r" 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.
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.
… 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 AsteroidHunter merged commit 743567f into main May 21, 2026
1 check passed
AsteroidHunter added a commit that referenced this pull request May 21, 2026
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.
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant