Skip to content

feat: hostname-aware window title#82

Merged
Ark0N merged 1 commit into
Ark0N:masterfrom
aakhter:feat/hostname-aware-title
May 12, 2026
Merged

feat: hostname-aware window title#82
Ark0N merged 1 commit into
Ark0N:masterfrom
aakhter:feat/hostname-aware-title

Conversation

@aakhter
Copy link
Copy Markdown
Contributor

@aakhter aakhter commented May 12, 2026

Summary

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, so you don't end up
typing into the wrong session.

Example:

Before After
Codeman codeman:laptop-aakhter
Codeman codeman:nas02

Implementation

  • src/cli.ts — new --title-hostname <hostname> flag overrides the detected hostname (handy when os.hostname() returns something noisy, or when you just want a cosmetic name).
  • src/web/server.tsWebServer 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. The <title> substitution is HTML-escaped via a small escapeHtmlText() helper so a hostname like <script> can't break out.
  • 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 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 shows codeman:<your-hostname>.
  • Same with --title-hostname custom-name — tab title shows codeman:custom-name.
  • Trigger an idle/notification flash (background the tab while a session emits an event) — verify the flashing title alternates between (N) codeman:<hostname> and codeman:<hostname>, not the old Codeman literal.
  • HTML escape: --title-hostname '<script>' → page title in the rendered HTML shows codeman:&lt;script&gt;, no DOM injection.

Note on the test file diff size

test/file-link-click.test.ts shows ~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 the browserAvailable = ...startsWith('codeman:') line.

🤖 Generated with Claude Code

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) <[email protected]>
@Ark0N
Copy link
Copy Markdown
Owner

Ark0N commented May 12, 2026

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. --title-hostname override is a nice escape hatch for noisy os.hostname() outputs.

@Ark0N Ark0N merged commit b7e94e7 into Ark0N:master May 12, 2026
1 check passed
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) <[email protected]>
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) <[email protected]>
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.

2 participants