Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ between releases do not reach them. Remote-PC runtime bundles are versioned sepa
## [Unreleased]

### Added
- An update tells what it brings before it is installed. **Settings → Updates** shows
- An update tells what it brings before it is installed. **Settings → About** shows
**What's new** under the version on offer: the changelog of every release the update installs,
newest first, read from the release itself. The line under the header has a **What's new**
button that opens Settings there. The notes come from the version that is running, so they
Expand Down Expand Up @@ -63,6 +63,17 @@ between releases do not reach them. Remote-PC runtime bundles are versioned sepa
gives a program by itself, and with nothing held the notice had only a dash to show. Text
past the 1,024 characters it holds is now told as left out, where it only raised that count.
([#561](https://github.com/devswha/herdr-web-ui/pull/561))
- Settings shows one page at a time. A list of pages (Appearance, Chat, Terminal, Alerts, Voice
input, Subscription usage, Shortcuts, Phone & devices, Remote PCs, About) stays beside the open
page; on a phone the list comes first and a page opens from it. Each page groups its settings
in cards, one control at the end of each row, and a phone no longer scrolls through every
setting to reach the last. What moved: the terminal's font, wheel speed, input mode and
**Key bar** from Appearance to **Terminal**; Composer and Quick replies into **Chat**; Phone,
Install and Devices into **Phone & devices**; Updates and herdr into **About**. Colors and
Language are menus now, and Language lists English, 简体中文, 日本語, 한국어 in that order after
System. Every setting keeps its value. The Back button of a phone or a browser
steps out of Settings (the key bar editor, the page, then the dialog) instead of leaving the app.
([#562](https://github.com/devswha/herdr-web-ui/pull/562))
- The mic button shows in a desktop's chat without being turned on. **Settings → Voice input →
Microphone button** is now **Auto**, **On** or **Off**. Auto, the default, puts the mic in the
chat's message box on a desktop, and only where dictation can work: over HTTPS, with an OpenAI
Expand All @@ -73,7 +84,7 @@ between releases do not reach them. Remote-PC runtime bundles are versioned sepa
- On a phone, the sidebar's **Agents** list starts folded, leaving the drawer to the workspaces.
A tap on its heading opens it. ([#559](https://github.com/devswha/herdr-web-ui/pull/559))
- On a touch screen, the chip above the message box that takes Claude Code's suggested next
prompt is on again unless **Settings → Composer → Suggestion chip** turns it off.
prompt is on again unless **Settings → Chat → Suggestion chip** turns it off.
([#559](https://github.com/devswha/herdr-web-ui/pull/559))
- Every agent's chat uses one compact Send/Stop button on desktop and phones. Sending during
work schedules a next-turn message; its explicit ↑ Send now action delivers it immediately,
Expand Down
53 changes: 39 additions & 14 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -925,16 +925,40 @@ One set for both themes: the card is island black wherever it shows.
`.kbd` hints where a global shortcut exists.

### Settings dialog
- Appearance: Dark / Light / System, Comfortable / Compact, terminal font `10–22px`, terminal font
family.
- Composer: Enter sends. Chat: Show thinking, chat width (Narrow 820px / Default, following the
pane / Wide 1152px / Full, the pane less its gutters), chat font
size and family. Shortcuts: the complete
platform-resolved table.
- Appearance keeps Key bar as a compact description and Edit key bar button. That button opens
- One page at a time. From 641px the dialog is two columns at a fixed size (`--content-w` wide,
`40rem` tall, so turning a page never moves its edge): a `13rem` list of pages on `--bg`
(icon + name, the open one on `--bg-hover` with its glyph in `--accent`) and the open page
beside it under its own title and the close button. A phone shows one of the two: the sheet
(`92%` tall) opens on the list, drawn as one card of rows with a chevron each, and a page
replaces it under a Back to settings control. The dialog's accessible name stays **Settings**
on every page; the list is a vertical `tablist` with one Tab stop, and the arrows walk it
(beside an open page they turn the page too). Escape closes the dialog from any page.
- Each step into Settings is an entry of the browser's history (`lib/settingsHistory.ts`): the
dialog, the page a phone opens from its list, the key bar editor. The system Back button (and
an edge swipe) takes one step out and never leaves the app from inside Settings; the Back
control, the X, Escape and the scrim take the same entries off. Beside the list, turning pages
replaces the one entry. A reload steps out of the entries it finds; Forward reopens the page.
- Pages, in order: **Appearance** (theme, colors, density, language, sidebar rows), **Chat**
(panes open in, show thinking, chat width, chat font size and family; then **Composer**: Enter
sends, suggestion chip; then **Quick replies**), **Terminal** (font size and family, wheel
speed, input mode, Key bar), **Alerts**, **Voice input**, **Subscription usage**,
**Shortcuts** (the complete platform-resolved list), **Phone & devices** (the phone address,
Keep screen on, Install; then paired devices), **Remote PCs**, **About** (Updates, herdr,
the repository links). A button that points at Updates opens the dialog on About.
- Every page is built from the same parts (`components/SettingsControls.tsx`). A group is an
optional caption (`--fs-sm`, `--fw-semibold`, `--text-dim`, sentence case) over one card:
`--bg-elevated`, hairline `--border`, `--radius-lg`, its rows parted by hairlines and never
boxed on their own. A row is a label (`--fs-sm`, `--fw-medium`, `--text-strong`) with at most
one dim sentence under it (`--fs-xs`, no wider than `54ch`) and one control at the end of the
line. The controls are a switch, a segmented control for two to four short choices, a select
for five or more, a stepper (minus, value in `--font-mono`, plus, in one bordered well) and a
text field; inside a card they sit on `--bg-input`. On a phone a control that needs the line
(three or more segments, a text field, or a long sentence above it) goes under its label at
the card's width, its segments sharing it equally; the rest stay on the label's line.
- Terminal keeps Key bar as a compact description and Edit key bar button. That button opens
a detail screen in the same dialog with its own scroll body and a Back to settings control.
Back or Escape returns to the previous Settings scroll position and focuses Edit key bar;
closing the dialog resets it to Settings for the next open. The detail lists the terminal keys
Back or Escape returns to the Terminal page's scroll position and focuses Edit key bar;
closing the dialog resets it for the next open. The detail lists the terminal keys
in their display order. Every key can be
removed or moved with up/down buttons; those controls keep `--touch-target` hit areas on phones.
Add key offers the supported key catalog, and Ctrl/Alt/Shift toggles add or remove held-modifier
Expand All @@ -943,10 +967,11 @@ One set for both themes: the card is island black wherever it shows.
held modifiers. Restore defaults returns the original row. The keyboard mode control stays first.
- A font family is a text field saved when it is left, on Enter or when the dialog closes, not
per keystroke.
- Remote PCs follows Devices: an **Add PC** row (label, one-line description, button) opens the PC
- Remote PCs: an **Add PC** row (label, one-line description, button) opens the PC
setup dialog and closes Settings behind it; when that dialog closes, focus lands on the header's
workspace-list toggle. Under the row, once the server has answered, the bridge auto-update switch.
- Install reflects installed, promptable or browser-instructions state; About links the repository.
- Install (a row of the Phone group) reflects installed, promptable or browser-instructions state;
About links the repository.
- The running versions are always written, since the sidebar carries none. Updates opens with
**Running vX.Y.Z (commit)**: the server's version and commit, or the client's own build version
before the server answers and where it names neither. While the server runs another version than
Expand All @@ -955,8 +980,8 @@ One set for both themes: the card is island black wherever it shows.
**Running herdr X.Y.Z**; where herdr cannot be updated from here (Windows, an older server) the
section is that line alone, from the health check.
- Subscription usage: the on switch with one description, then (when on) Used / Remaining,
Weekly / Session and one hairline card of accounts (`.usage-accounts`, `--radius-md`): an
uppercase `--bg-elevated` header, then one 38px row per account
Weekly / Session and, under an **Accounts** caption, one card of accounts (`.usage-accounts`):
one `--touch-target` row per account
(mark, name, dim ellipsized email, then 28px move-up, move-down and eye controls in fixed columns;
a move that cannot happen keeps its column but is not shown). A hidden account's row fades and
its eye closes; it stays listed so it can be shown again.
Expand All @@ -968,7 +993,7 @@ One set for both themes: the card is island black wherever it shows.
- The mobile key bar is Esc, Tab, sticky Ctrl/Alt/Shift, Enter, arrows and `^C`;
it never steals typing focus. Modifiers remain highlighted until toggled off,
leaving the pane/lens or disconnecting; each exposes `aria-pressed`.
- That is the default row. Settings → Appearance → Key bar can add, remove and reorder every
- That is the default row. Settings → Terminal → Key bar can add, remove and reorder every
terminal key and register exact combinations. Removing a held modifier clears it immediately.
A deliberately empty row retains only the keyboard mode control on a touch screen.
- The mobile drawer slides over a scrim. Closed visibility removes its controls from the tab order.
Expand Down
8 changes: 4 additions & 4 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ git --version

| Method | When | Updates |
| --- | --- | --- |
| **A. herdr plugin** (default) | The user wants it to start with herdr | In-app: Settings → Updates |
| **A. herdr plugin** (default) | The user wants it to start with herdr | In-app: Settings → About |
| **B. Source checkout** | The user wants to develop it, or asks for a clone | In-app, while the checkout stays on a clean `main` |
| **C. One-line installer** | The user agrees to install what is missing (herdr, Bun, Node) and to let Tailscale serve the app | In-app, like A |

Expand Down Expand Up @@ -152,15 +152,15 @@ tailscale serve --bg --https=443 http://127.0.0.1:7317
Or run the one-line installer (method C) again, which installs nothing when the app is there, or
`bun scripts/plugin.ts phone` from a source checkout: it runs that command on the first
free HTTPS port when Tailscale runs and does not serve the app yet, says how to undo it, and prints the
address as a QR code. **Settings → Phone** in the app shows this step's state: the address that already works as a QR
address as a QR code. **Settings → Phone & devices** in the app shows this step's state: the address that already works as a QR
code, or the exact command still to run. Who gets in:

- The user's own Tailscale devices get in as the user: `tailscale serve` states the login, and the
server compares it with this PC's. Nothing to configure. Other people's logins are refused, and
tagged devices (no person's login) need pairing. If the user's own phone is asked to pair anyway,
`HERDR_WEB_TAILSCALE_SERVE_ONLY=1` lets it in without a code on a tailnet one login owns
([Access and safety](docs/guide.md#access-and-safety)).
- Any other device (someone else's, or a LAN or public address) is paired: **Settings → Devices**
- Any other device (someone else's, or a LAN or public address) is paired: **Settings → Phone & devices**
on the PC shows a six-digit code and a QR code; the device enters it once. On a headless PC with
no browser, `bun "$(ls -d ~/.config/herdr/plugins/github/devswha.herdr-web-ui-* | head -1)/scripts/plugin.ts" pair`
prints the code in the terminal. Do this with the user present; never read a code aloud into a log.
Expand Down Expand Up @@ -197,7 +197,7 @@ The [user guide](docs/guide.md#configuration) lists the rest.

## Update

- Settings → **Updates** → **Update and restart** when a new release (`vX.Y.Z`) is out. It works for
- Settings → **About** → **Update and restart** when a new release (`vX.Y.Z`) is out. It works for
both install methods. The new
version is built separately and the app restarts only if the build and health check pass.
- Plugin alternative: `herdr plugin install devswha/herdr-web-ui --yes` again. It replaces the
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ the details private until that release is out.

## Supported versions

Only the latest `vX.Y.Z` release gets fixes. Installs update to it from **Settings → Updates** (or
Only the latest `vX.Y.Z` release gets fixes. Installs update to it from **Settings → About** (or
by themselves with `HERDR_WEB_AUTO_UPDATE=1`). A fix in the remote-PC runtime ships as a new
`remote-vN` bundle together with the release that uses it.

Expand Down
1 change: 1 addition & 0 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ bun test # needs herdr installed; creates and removes its
bun run test:ui # browser regression against isolated test servers
bun scripts/sticky-modifiers-regression.ts # mobile held keys through real legacy/Kitty PTYs
bun scripts/key-bar-customization-demo-regression.ts # mobile key layout, saved combinations and migration on demo fixtures
bun scripts/settings-pages-demo-regression.ts # every Settings page on a 390px and a 320px phone, and Back stepping out of the dialog
bun scripts/chat-browser-qa.ts # chat lens end to end
bun scripts/output-browser-qa.ts # terminal output flow control end to end
bun scripts/math-browser-qa.ts # chat math: KaTeX loads with the first expression
Expand Down
10 changes: 5 additions & 5 deletions docs/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ The one-line installer runs this for you when Tailscale runs on the PC and does

Only devices in your tailnet can open that address. Your own devices get in without a code as it is: `tailscale serve` states your login. If your own phone is asked to pair anyway, `HERDR_WEB_TAILSCALE_SERVE_ONLY=1` lets it in without a code on a tailnet one login owns, and only when nothing else, such as a public reverse proxy or tunnel, reaches this port (see [Access and safety](#access-and-safety)). Devices that belong to someone else pair with a six-digit code.

**Settings → Phone** in the app does this step for you as far as it can: it shows the address Tailscale already serves for this PC as a QR code, or the exact command still to run, and the address it will give.
**Settings → Phone & devices** in the app does this step for you as far as it can: it shows the address Tailscale already serves for this PC as a QR code, or the exact command still to run, and the address it will give.

1. Open the address.
2. Install the app: in Safari, choose **Share → Add to Home Screen**; in Chrome, choose **Install app**.
Expand All @@ -250,13 +250,13 @@ On a phone:
protocols cannot distinguish every chord (for example Ctrl+I and Tab).
Draft text in the input line is sent unchanged. Modifiers clear
when leaving the pane or terminal view, or when the connection drops.
**Settings → Appearance → Key bar → Edit key bar** opens a separate screen to add, remove and
**Settings → Terminal → Key bar → Edit key bar** opens a separate screen to add, remove and
reorder every key, including modifier
buttons. Register a custom combination such as Ctrl+W, choose editing keys or F1–F12, or restore
the default row. Saved combinations use exactly their selected modifiers; ordinary keys use
the currently held modifiers. Removing a held modifier button clears it.
- Dragging the terminal scrolls the real herdr pane.
- **Settings → Phone → Keep screen on** keeps the screen awake while a terminal or chat
- **Settings → Phone & devices → Keep screen on** keeps the screen awake while a terminal or chat
pane is open. It is off by default, releases when the app is hidden, and resumes when
you return. It needs HTTPS or localhost and browser support; power-saving mode may refuse it.

Expand All @@ -282,7 +282,7 @@ More in [remote PCs](remote-pcs.md).
Anyone who can reach the server can type into your terminals, so what matters is who gets in. It listens on `127.0.0.1` by default, which means only this computer. From anywhere else, a request gets in in one of three ways:

- **It is you, says Tailscale.** `tailscale serve` states the requesting device's Tailscale login in a header it strips from anything incoming. A login that matches this PC's own gets in; another login is refused, and a tagged device (one with no person's login) needs pairing. If you set `HERDR_WEB_TAILSCALE_SERVE_ONLY=1` to say `tailscale serve` is the only way in, and `tailscale status` on this PC lists one login on every node it sees and no tagged node, a serve request that states no login is the owner as well: no one else is among those nodes to be mistaken for (a node an ACL hides from this PC is not seen, and cannot reach serve either), so your own phone opens the address and lands in the app with no code. Nothing to set up, unless this PC's own Tailscale node is tagged: it then has no login of its own, so every device pairs, yours included, or you name your login in `HERDR_WEB_TAILSCALE_OWNER`.
- **It is a paired device.** **Settings → Devices**, on the PC (or on a device already paired), shows a six-digit code that lives ten minutes and a QR code that carries it. On a headless PC, the `pair` command prints the same in its terminal (see [In a terminal](#in-a-terminal)); Devices also shows the pairing link as text, to send to the other device. The other device enters it once and keeps its own credential in an HttpOnly cookie; the list shows it, and **Revoke** immediately closes its terminal connections and roster stream and refuses subsequent requests.
- **It is a paired device.** **Settings → Phone & devices**, on the PC (or on a device already paired), shows a six-digit code that lives ten minutes and a QR code that carries it. On a headless PC, the `pair` command prints the same in its terminal (see [In a terminal](#in-a-terminal)); Devices also shows the pairing link as text, to send to the other device. The other device enters it once and keeps its own credential in an HttpOnly cookie; the list shows it, and **Revoke** immediately closes its terminal connections and roster stream and refuses subsequent requests.
- **It holds the token.** `HERDR_WEB_TOKEN`, for scripts and proxies, as a cookie after sign-in or as `Authorization: Bearer <token>`. When a token is set, everything else needs it, this computer and your own Tailscale login included: enter it once on each device, and that browser stays signed in for a year. A paired device still gets in without it.

| How you reach it | What gets you in |
Expand Down Expand Up @@ -398,7 +398,7 @@ Updates need a clean checkout: `main` for a source install, or herdr's plugin ch

### Updating herdr

herdr itself is updated from **Settings → herdr → Update herdr**. herdr refuses `herdr update` typed into one of its panes ("run `herdr update` outside herdr"), and every terminal in the app is a pane, so the server runs it instead, for the herdr on the PC the app runs on:
herdr itself is updated from **Settings → About → Update herdr**. herdr refuses `herdr update` typed into one of its panes ("run `herdr update` outside herdr"), and every terminal in the app is a pane, so the server runs it instead, for the herdr on the PC the app runs on:

- It installs the newest herdr and moves the running panes onto it with a live handoff. Panes and agents keep running, and open terminals reconnect.
- If a newer herdr was already installed from a shell, the running server is still the old one: Settings says so, and the button only moves the panes.
Expand Down
Loading
Loading