feat: hostname-aware window title#82
Merged
Merged
Conversation
Set the browser tab title to codeman:${hostname} instead of the bare
"Codeman" literal. Useful for users running multiple Codeman instances
across hosts (laptop, dev box, NAS) — the OS hostname disambiguates
which tab points at which backend.
Implementation:
- src/cli.ts: new --title-hostname <hostname> flag overrides the
detected hostname (handy for cosmetic naming or when os.hostname()
returns something noisy).
- src/web/server.ts: WebServer now accepts an optional titleHostname
constructor arg (defaults to os.hostname()), composes
windowTitle = codeman:${titleHostname}, and serves / and
/index.html by templating that title into the cached index.html
template (with HTML escaping of the title text).
- src/web/public/notification-manager.js: title-flash logic now uses
this.originalTitle instead of the hardcoded "Codeman" literal, so
the tab flash respects the per-host title.
- scripts/browser-comparison.mjs + test/file-link-click.test.ts:
expectations updated from === "Codeman" to a startsWith("codeman:")
predicate so they pass regardless of host.
The new index.html templating is intentionally narrow — it only
substitutes the <title> tag and continues to serve everything else
from the static template. No JS-side title injection, so it works
without JavaScript and shows the correct title from the very first
paint.
Note: test/file-link-click.test.ts shows ~49 prettier-reformat lines
that are not part of the feature — they are pre-existing prettier
debt that the pre-commit hook required me to clear. The single
behavioral change is the browserAvailable line.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Owner
|
Thanks — this is genuinely useful for me too (I bounce between several Codeman hosts). The implementation is exactly the right shape: server-side HTML templating so the correct title paints on first byte (no JS-side flicker), narrow HTML-escape scoped to title element content, and the notification-manager change so the flashing title also respects the per-host name. |
Ark0N
added a commit
that referenced
this pull request
May 12, 2026
Closes the Web Push gap left by #82: in-page Notification API and tab title flash both showed `codeman:<host>` after that PR, but OS-level notifications dispatched via the service worker — the surface that matters most when the tab is closed and the user is reading their system notification center across multiple Codeman instances — still hardcoded the literal "Codeman" prefix. Service workers run in an isolated context with no access to document.title or any in-page state, so the hostname has to ride along in the push payload itself. Server (server.ts:sendPushNotifications): emit `hostTitle: this.windowTitle` in the JSON payload alongside the existing `title` (event-specific text like "Permission Required"). The two stay separate so the SW can compose them — the server knows the host, the SW knows the OS context. Service worker (sw.js): compose `${hostTitle}: ${title}` when both present, mirroring the in-page Notification format from notification-manager.js. Fall back to `title || hostTitle || 'Codeman'` so older servers (which omit hostTitle) keep working — the field is purely additive on the wire. Tests (test/push-payload-host-title.test.ts): mock the `web-push` module via vi.hoisted(), instantiate WebServer without binding a port, stub the push store with one fake subscription, and verify the JSON payload shipped to webpush.sendNotification carries the right hostTitle for both --title-hostname overrides and the os.hostname() default. Also mirrors the SW's title-composition logic in a small helper so any future change to the format breaks the test instead of being caught only by users running multiple Codeman instances. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ark0N
added a commit
that referenced
this pull request
May 12, 2026
Backfill the two regression gaps flagged on master after the recent
hostname-title and tmux-flicker fixes shipped without server-side
assertions.
* test/server-index-title.test.ts (8 tests) — exercises WebServer's
index.html templating path: default os.hostname(), --title-hostname
override, HTML-escape against `<script>`-style breakout, ampersand
non-double-encoding, exact-once substitution, and byte-identical
template-tail invariance.
* test/tmux-window-size-query.test.ts (15 tests) — mocks
child_process.execFileSync and walks the helper through the
browser-resize-between-attaches happy path, query-then-die race,
zero/negative/empty/non-numeric output, plus argv-form/timeout
assertions to lock down the no-shell-interpolation guarantee.
* src/session.ts — extracts the inline 14-line tmux size query into
a named `queryTmuxWindowSize()` export so the test surface is a
pure function. Behavior unchanged.
* src/web/public/notification-manager.js — Browser Notification API
(layer 3) now uses `${this.originalTitle}: ${title}` so OS-level
desktop pop-ups carry the same `codeman:<host>` prefix that the
tab title and Web Push payloads already do, finishing the
hostname plumb-through started in #82.
* CLAUDE.md, README.md — document the dual-CLI env-prefix discipline
(CLAUDE_CODE_* vs OPENCODE_*), expand the xterm-zerolag-input
duplication gotcha to mention the published-package side-effect,
and note that the hostname prefix now applies uniformly to tab
title, tab-flash, and OS notifications.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Set the browser tab title to
codeman:${hostname}instead of the bareCodemanliteral.Useful for users running multiple Codeman instances across hosts (laptop, dev box, NAS) —
the OS hostname disambiguates which tab points at which backend, so you don't end up
typing into the wrong session.
Example:
Codemancodeman:laptop-aakhterCodemancodeman:nas02Implementation
src/cli.ts— new--title-hostname <hostname>flag overrides the detected hostname (handy whenos.hostname()returns something noisy, or when you just want a cosmetic name).src/web/server.ts—WebServernow accepts an optionaltitleHostnameconstructor arg (defaults toos.hostname()), composeswindowTitle = codeman:${titleHostname}, and serves/and/index.htmlby templating that title into the cachedindex.htmltemplate. The<title>substitution is HTML-escaped via a smallescapeHtmlText()helper so a hostname like<script>can't break out.src/web/public/notification-manager.js— title-flash logic now usesthis.originalTitleinstead of the hardcodedCodemanliteral, so the tab flash respects the per-host title.scripts/browser-comparison.mjs+test/file-link-click.test.ts— expectations updated from=== 'Codeman'to astartsWith('codeman:')predicate so they pass regardless of host.The index.html templating is intentionally narrow — it only substitutes the
<title>tag and continues to serve everything else from the static template. No JS-side title injection, so the correct title shows from the very first paint and works without JavaScript.Test plan
npm run build && node dist/cli.js web -p 3000— open in browser, tab title showscodeman:<your-hostname>.--title-hostname custom-name— tab title showscodeman:custom-name.(N) codeman:<hostname>andcodeman:<hostname>, not the oldCodemanliteral.--title-hostname '<script>'→ page title in the rendered HTML showscodeman:<script>, no DOM injection.Note on the test file diff size
test/file-link-click.test.tsshows ~49 prettier-reformat lines that aren't part of the feature — they're pre-existing prettier debt the local pre-commit hook required me to clear. The only behavioral change in that file is thebrowserAvailable = ...startsWith('codeman:')line.🤖 Generated with Claude Code