Commit Graph
24 Commits
Author SHA1 Message Date
Rounak DattaandClaude Opus 5 ba843bb272 fix(mobile): keep a long prompt visible instead of hiding it behind the keyboard
Typing a prompt long enough to wrap ran the text off the bottom of the screen: the
tail — the part being typed, where the cursor is — sat behind the on-screen
keyboard, so the user was typing blind. Two independent causes.

**The overlay had no bottom bound.** On touch devices keystrokes are buffered in
the local-echo overlay and do not reach the PTY until Enter, so the CLI never
learns the prompt is long and nothing scrolls or reflows to make room. Meanwhile
the renderer lays its wrapped lines out straight DOWNWARD from the prompt row
(`top = promptRow * cellH`, each line at `i * cellH`) with nothing clamping it to
the visible rows — and with the keyboard up there are only a handful of those.

The block now grows UPWARD once it would pass the last visible row: it is lifted
so its final line lands ON that row. Every line div is opaque, so it covers
transcript above rather than vanishing under the keyboard below — the same thing a
real terminal does when a composer expands. A prompt taller than the whole
viewport keeps its TAIL, for the same reason the fix exists: the end is what the
user is looking at. `startCol` indents only the line that starts at the prompt
marker, so it is dropped along with that line when only the tail fits, and the
cursor follows the last VISIBLE line.

`rows` joins the render key: the layout depends on it, so a keyboard opening —
which changes rows without changing the text — must not be skipped as a redundant
render.

**`_shrinkPaddingToFit()` was reclaiming the bars' own space.** On phones the
toolbar and accessory bar are `position: fixed`, so they occupy no layout space
and `main`'s padding-bottom is the ONLY thing reserving room for them. Shrinking
it by the full sub-row slack pulled the terminal's bottom edge down underneath
them, and the row the following re-fit gained was painted behind them — clipping
the last line of a long prompt. The shrink now has a floor: the MEASURED height of
the currently-visible fixed bars, so genuine over-reservation of the hard-coded
84px is still reclaimed while a device that needs those pixels keeps them. The
floor is `Math.min(currentPadding, measured)`, so it can only ever prevent a
shrink, never cause a grow that would resize the terminal as a side effect.

Overlay behaviour lives in `packages/xterm-zerolag-input/` (single-source; the
vendor bundles are generated), so the fix is in the package with the row count
passed in as an optional `totalRows` — absent, the layout is exactly as before.

Tests: 7 cases in the package's `overlay-renderer.test.ts` (upward lift, tail
retention, indent drop, cursor on the last visible line, and the unclamped
fallbacks) and 7 in a new `test/mobile-keyboard-bottom-padding.test.ts` (reclaim,
floor, partial reclaim, no-grow, hidden bars, CJK strip, whole-row slack). 5 and 4
of them respectively fail without the fix. Package suite 238 pass, including the
codex byte-identity and replay tests.

Verified on Android + Chrome against a live instance: a ~460-character prompt
wrapping ~12 rows stays on screen while typing and arrives at the PTY intact.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:14:52 +00:00
Codeman maintainer 210da991d5 Merge christianhaberl's session-sidebar branch, ported to current master
Brings in https://github.com/christianhaberl/Codeman/pull/4 (three commits,
authorship preserved) and adapts it across the 211 commits master gained
since the branch was cut:

- App Settings control re-authored for the set-* surface (PR #278): a
  set-row in Layout -> Tabs, replacing the old settings-item markup the
  branch targeted. i18n description synced.
- Lineage arcs (PR #291, post-branch) are SKIPPED in sidebar layout:
  computeLineagePath()'s U-bridge geometry hangs from the horizontal
  strip's bottom edge and has no meaning against a vertical list. The
  lineage strip-scroll listener now also redraws subagent/ultracode
  connectors while the sidebar scrolls vertically.
- The desktop home tab rail (post-branch) defers to the sidebar: both dock
  the session list flush left, and the rail would render z-ordered under it.
- Active-row reveal unified into _scrollActiveTabIntoView() (#257 landed on
  master after the branch): sidebar mode branches to scrollIntoView
  block:'nearest', and _fullRenderSessionTabs() restores scrollTop alongside
  the #257 scrollLeft restore so ambient rebuilds cannot yank a mid-scroll
  sidebar back to the top.
- Mobile active-tab hoisting the branch guarded against no longer exists on
  master (removed by #257); kept master's order-stable render.

Verified: typecheck, lint, format:check, check:frontend-syntax,
check:public-assets, PostCSS parse of both merged stylesheets, the 26 new
jsdom tests, the structural guard suites, and the headless-Chromium harness
(scripts/verify-session-sidebar.mts) green across all seven layout states
at 1600/1000/393px against current master.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 18:28:15 +02:00
Codeman maintainer 9a0e665f72 fix(terminal): preserve scroll intent across keyboard resize, surface history truncation
Closes #259, closes #258. Both bottom out in the same gap: nothing tracked
whether the user was following live output or reading history.

#259 — the keyboard path forced the terminal to the bottom unconditionally
(onKeyboardShow/onKeyboardHide passed scrollToBottom:true, applied with no
check), so opening the keyboard while scrolled up yanked the user down. The
settle cycle now captures intent on its FIRST event, before any fit() has
reflowed the buffer, and returns to that anchor when the user was reading.
A later capture would read an already-moved viewportY, which is why the
capture point matters. The param is renamed restoreScroll to match.

Separately, flushPendingWrites gated viewport preservation on
_hasRecentUserScrollUp(), a 1500ms decay window, so a user who scrolled up and
then actually READ for longer lost protection mid-read. Being scrolled up IS
the intent however long ago it was expressed, so it now keys off position.
The recency window stays as a race guard on the sticky scroll-to-bottom.

The full-history repull already held the user's place and is unchanged.

#258 — truncation was reported by a grey line written INTO the terminal
("earlier output truncated"), which scrolls away with the output it describes,
cannot be acted on, and said the same thing whether the rest was one click away
or gone forever. The server set one `truncated` boolean at two sites meaning
opposite things, and the client discarded fullSize and source entirely.

The route now reports truncationReason ('tail' = intentional partial replay,
the rest is retained; 'capped' = the byte ceiling dropped it) plus
retainedBytes, and 'capped' is not downgraded by a later tail cut. The client
renders a dismissible banner outside terminal output with three honest states:
recoverable (offers Load full history), at-ceiling, and exhausted. The Load
button forces past the scroll cooldown but NOT past _replayWouldShrinkBuffer,
which still refuses a downgrade for repaint-mode panes.

The banner is an overlay, not a flex child: FitAddon derives rows/cols from the
terminal parent's computed height, so occupying real layout space would SIGWINCH
the CLI on every truncation-state change.

Verified in a real browser on the 7 skins: banner text and button clear 4.5:1
contrast on all of them, and terminal height is byte-identical with the banner
shown. The first cut used --bg-elevated and --accent-muted, which do not exist,
so light skins rendered a hardcoded dark bar under dark text; it now uses only
tokens every skin redefines.

test/terminal-scroll-intent.test.ts lives outside test/mobile/ deliberately —
that suite is excluded from test:ci, so a guard placed there is invisible to CI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 00:00:59 +02:00
Codeman maintainer 1f164bc8d2 fix(mobile): only arm the viewport settle on a real keyboard transition
A visualViewport resize event without a pending show/hide transition now
only pushes a pending settle back (_deferViewportSettle) instead of arming
fit + PTY-resize work of its own. Keyboard detection can miss a
fine-grained OS animation entirely (each step under 150px, with the
baseline chasing the animation down), while MobileDetection's own listener
still shrinks --app-height, so the per-event settle fitted xterm against a
mid-animation container with no keyboard CSS compensation and resized the
PTY to transient dims. The resulting SIGWINCH thrash (58 -> 10 -> 50 rows)
duplicated prompts and left tmux dot filler in the transcript on keyboard
close. Reproduced with a faked visualViewport driving the real handler;
master is unaffected because it never resized the PTY from this path.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-08 12:06:08 +02:00
lior 66abe6c70a fix(mobile): coalesce keyboard viewport settling 2026-08-08 08:13:58 +03:00
Claudia[bot] c01edcbbb8 feat(web): optional collapsible left session sidebar
The header tab strip stops working past roughly a dozen sessions: it wraps
into two or three rows, eats vertical space and still cannot be scanned.
This adds a vertical session list in a left <aside> as an ALTERNATIVE
layout — a filter box, a live count, and a 44px collapsed rail that keeps
the ambient signal (status dot, task badge) visible.

The strip is not removed. Settings -> Display -> Tab Bar -> Session List
Layout switches between them and the default stays 'header', so existing
users see no change until they opt in.

Structure: one #sessionTabs element, two mount points. applySessionListLayout()
re-parents the SAME node between #sessionTabsHost and #sessionSidebarList,
which is why there is no second renderer and no duplicated wiring — app.$()
caches getElementById results and never invalidates them, so a moved node
keeps every existing consumer (settings-ui, webview-tabs, the generated
gesture bundle, the mobile tests) working untouched.

Notable integration points:
- Below 1024px the sidebar is an off-canvas drawer overlaying the terminal;
  closed it gets inert + aria-hidden so it cannot be tabbed into, and touch
  swipes over it no longer switch sessions.
- Subagent and ultracode windows anchor to the right edge of a sidebar row
  instead of its bottom, connector curves follow.
- Alt+B toggles; the chord is gated out of the PTY so xterm cannot also
  write ESC b into a live session.
- Collapse state lives in its own localStorage key (the settings blob is
  rebuilt from DOM controls on every save) and falls back to in-memory
  intent where storage throws.

Verified: frontend syntax + public asset checks, tsc, eslint, 26 new jsdom
tests, and a headless-Chromium harness (scripts/verify-session-sidebar.mts)
that renders a synthetic 25-session fleet in both layouts at 1600/1000/393px
and asserts mount point, widths, inert/aria state and row count.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 16:42:21 +02:00
codeman-local 51cb3a7205 fix(mobile): preserve settings across foldable postures 2026-07-20 21:22:28 +08:00
Teigen 566c65c3c9 fix(iPad): accessory bar styling, positioning, and paste dialog
Move keyboard accessory bar and paste dialog CSS from mobile.css
(gated behind max-width: 1023px) to styles.css (always loaded).
iPad landscape (≥1024px) was getting unstyled white buttons.

- Add position:fixed via .touch-device class for accessory bar
- Fix dismiss button: gray-blue → blue, matching phone styling
- JS: position accessory bar above keyboard on iPad via direct bottom
- JS: position CJK above accessory bar (bottom: keyboardHeight + 44)
- Clear accessory bar bottom in resetLayout()
2026-06-17 23:40:04 +08:00
Teigen cd7d8c7329 fix(mobile): split CJK keyboard positioning by device size
Phones use translateY(-keyboardOffset) — CSS bottom is relative to layout
viewport and keyboardOffset reliably lifts it above the keyboard (iOS
doesn't auto-scroll the visual viewport for the CJK textarea on phones).

iPad uses direct bottom positioning from keyboard height — translateY
broke because iOS auto-scrolls the visual viewport when the CJK textarea
receives focus, making keyboardOffset approach 0.
2026-06-17 23:40:04 +08:00
Teigen c55af9ec39 fix(iPad): CJK input positioning, paste dialog, and voice dictation duplication
Three iPad-specific issues fixed:

1. CJK input hidden behind keyboard: updateLayoutForKeyboard() gate changed
   from screen-size to touch-device detection. On iPad, CJK textarea (always
   position:fixed) gets bottom offset computed from keyboard HEIGHT directly
   instead of keyboardOffset (which depends on visualViewport.offsetTop that
   iOS adjusts when the CJK textarea receives focus). Toolbar/accessory bar
   transforms remain phone-only (they're normal-flow on iPad).

2. Paste dialog invisible on iPad: paste overlay CSS was inside
   @media (max-width: 430px) phone breakpoint — iPad (≥768px) had no styling.
   Extracted to universal section alongside keyboard accessory bar styles.

3. Voice dictation character duplication (Doubao/third-party IME):
   iOS voice dictation does NOT fire composition events (WebKit Bug 261764).
   Text arrives as bare input events; refinement is a delete→reinsert cycle.
   Rewrote CJK input handler with two-tier debounce:
   - Keyboard typing (no delete/replacement events): 150ms debounce
   - Dictation mode (deleteContentBackward or insertReplacementText detected):
     1500ms debounce, persists 3s to cover multi-word dictation
   - Composition path (compositionend): immediate flush, unchanged
   - Keydown singles/Enter/Esc/Ctrl: immediate, unchanged
   Also: keep cjkActive=true on blur while CJK is visible (prevents xterm
   from processing duplicate input when iOS dictation UI steals focus);
   keydown single-char sends tracked via timestamp to suppress the echo
   input event that third-party IMEs fire despite preventDefault.
2026-06-17 23:40:04 +08:00
Claude (Codeman maintainer) c7e8ff616f fix(tabs): re-evaluate auto-wrap on resize and on every full tab rebuild
Review polish on the desktop tab auto-wrap:

- Auto-wrap is purely width-driven, but updateTabOverflowMode() was only called at the
  tail of _renderSessionTabsImmediate (SSE content renders). Window resize — the primary
  trigger for tabs crossing the one-row overflow threshold — never re-evaluated it, so
  narrowing/widening the window left the wrap state stale until an unrelated status event
  fired a render. Call it from the debounced window-resize handler (no-op on
  mobile/tablet, where the method bails).

- Move the re-evaluation into _fullRenderSessionTabs() as well, so the incremental
  branch's two early `_fullRenderSessionTabs(); return;` paths (badge add/remove, which
  change tab width) and the manual two-rows toggle (applyTabWrapSettings → _fullRender…)
  re-evaluate too. The latter also fixes a transient where enabling manual two-rows while
  auto-wrap was on left both classes set (clipping folder tabs to 96px) until the next
  render.

- Add boundary cases to the policy test: exact fit and the +1 sub-pixel tolerance (no
  wrap), 2px over (wrap), and a single overflowing tab (no wrap).

Verified: tab-overflow test passes; tsc, check:frontend-syntax, check:public-assets,
prettier all clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 22:38:19 +02:00
arkonandClaude Fable 5 68310619a7 feat(session,mobile): auto-resume on usage limit + mobile view fixes
Auto-resume on usage limit ("token pause" control, opt-in checkbox at the
top of the Respawn tab, off by default):
- usage-limit-patterns.ts (new, pure): detects all Claude Code limit
  messages (1.0.x-2.1.x eras incl. "5-hour limit reached - resets 8pm",
  "You've hit your limit - resets 1:40pm (TZ)", weekly date forms, raw
  "usage limit reached|<epoch>") and parses the reset time. Conservative:
  no parseable future reset time, no action.
- SessionAutoOps: arms a timer at reset+2min, sends Esc (dismisses the
  rate-limit dialog) + "continue"; dedups footer redraws, retries every
  5min on stale times, cancels when Claude starts working, persists and
  re-arms across Codeman restarts (SessionState.autoResumeEnabled/At).
- Respawn guard: cycles are blocked while limit-paused so /clear cannot
  wipe the paused conversation (respawnBlocked reason 'usage_limit').
- POST /api/sessions/:id/auto-resume; SSE session:limitPauseScheduled/
  limitResume/limitResumeCancelled; toasts + status line in the modal.
- Respawn tab tidied: single-row prompt fields, merged behavior row.

Mobile fixes (0.9.8 regressions, user-reported):
- Resize arbitration is now activity-based: a desktop sizing claim only
  blocks phone resizes while the desktop typed within 90s
  (Session.DESKTOP_CLAIM_IDLE_MS). Idle desktop -> phone takes the pane;
  next desktop keystroke re-asserts the desktop layout server-side
  (noteDesktopActivity via ws-routes input). Phones re-send dims every
  30s (visible tab only, skipped while the keyboard is open) so attaching
  under a hot claim self-corrects. Fixes the desktop-width-stream-in-
  narrow-xterm soup (mid-word wraps, tmux dot fill, Ink overdraw).
- Cross-device reflows (takeover/re-assert) emit a debounced needsRefresh
  so all clients reload the buffer instead of stacking ghost Ink frames.
- Keyboard accessory/toolbar lift restored: measure keyboardOffset
  against window.innerHeight (layout viewport), not the shrunken .app -
  on iOS the offset computed to 0, leaving both bars hidden behind the
  OS keyboard with a dead gap above.
- Removed the mobile header utility ("three dots") toggle entirely;
  the headerRight tray stays collapsed on small viewports.

Tests: usage-limit-patterns (36), session-auto-resume (21), resize
arbitration (+6), session routes (+4), respawn guard (+2); MockSession
auto-resume/sizing stubs; mobile tabs test updated for toggle removal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 20:41:34 +02:00
arkonandClaude Opus 4.8 e77df131b8 fix(mobile): address review blockers on the touch-device change set
Review follow-ups on PR #111 (rebased onto master post-#112/#113):

Resize arbitration redesigned (review blocker 2): the previous
'cols < _ptyCols' guard froze a mobile-only session's PTY at the spawn
default — narrow phones rendered clipped and could never re-fit. The
guard now uses connection-scoped desktop sizing claims instead:
ws-routes registers a claim on a desktop-typed resize and releases it
on socket close (or when the same connection later reports a small
viewport), and Session.resize() ignores mobile/tablet resizes only
while at least one desktop connection holds a claim. A phone alone
fully controls its size (shrink, rows-only shrink, re-grow); a phone
glancing at a desktop-driven session can no longer reflow it.
mobile-handlers' keyboard open/close resize now declares its viewport
type so it participates in arbitration. Tests rewritten to cover
mobile-only shrink/rows-only/re-grow, claim/release lifecycle, multi-
claim behavior, and untyped legacy resizes; ws-routes test covers the
claim lifecycle over a real socket.

Solo/detached header restored (review blocker 3): index.html had
removed #soloSessionTitle and #soloRedockBtn, which _applySoloMode
still references — every detached window hit a null deref. Both are
back alongside the new mobile utility toggle.

Desktop leak fixed (review should-fix): .mobile-header-utility-toggle
had no rule outside the <=768px media queries, so the raw button
rendered on desktop. styles.css now hides it by default; the mobile/
tablet queries re-enable it.

Visual-regression baselines reverted to master (review should-fix):
the 18 contributor-machine PNGs are environment-specific (8 of the
behavioral tests already report environment-sensitive failures across
machines); re-baseline deliberately on the canonical machine instead.
The 24 behavioral keyboard/layout/tabs tests are kept as-is.

AGENTS.md trimmed to a pointer at CLAUDE.md (review should-fix) to
avoid drift between duplicated guidance.

Also dropped a dead getAttachmentHistoryForPersist stub (codex-branch
residue — no such method exists in src/).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 04:07:26 +02:00
Aamer AkhterandSaqeb Akhter 02fa3f30f5 fix(mobile): terminal and layout fixes for touch devices
Mobile-focused fixes for the web UI: keyboard-accessory layout and
overlap, native input visibility above the keyboard, CJK input handling,
terminal touch scrolling, tab-menu tap targets, mic-recording glow
containment, and mobile resize/keyboard-state handling on tab switch,
plus mobile visual-regression test coverage and snapshots.

Co-Authored-By: Saqeb Akhter <saqeb.akhter@gmail.com>
2026-06-10 03:47:26 +02:00
arkonandClaude Opus 4.6 497ca4891a fix: restore mobile terminal scrollback — use JS scrollLines() instead of broken native scroll
xterm.js DOM renderer doesn't populate .xterm-viewport's scroll area (the div
is empty, scrollHeight === clientHeight), so native CSS scrolling via
touch-action:pan-y and overflow-y:scroll had nothing to scroll. Desktop worked
only because the wheel handler called terminal.scrollLines() directly.

- Replace split mobile/desktop touch handlers with unified JS-driven handler
  that converts touch deltas to terminal.scrollLines() calls (with pixel
  accumulation for slow swipes and momentum scrolling)
- Change touch-action from pan-y to none on terminal elements so browser
  doesn't fight the JS handler
- Remove now-unnecessary xterm-viewport position/overflow/z-index overrides
  and iOS -webkit-overflow-scrolling rules
- Fix _shrinkPaddingToFit() arithmetic (was adding gap instead of subtracting)
- Minor: add route-helpers.ts to CLAUDE.md, fix sse-events.ts comment count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 09:38:18 +01:00
arkonandClaude Opus 4.6 34c3d8f5ff fix: tighten mobile keyboard layout — eliminate dead space and toolbar overlap
- Remove redundant 50px CSS padding on terminal-container when keyboard visible
- Reduce JS paddingBottom constant from +94 to +84 (exact toolbar + accessory)
- Add _shrinkPaddingToFit() to eliminate terminal row quantization gap
- Add CSS padding-bottom on .main for fixed toolbar clearance (keyboard hidden)
- Match iOS Safari toolbar offset (100vh - --app-height) in .main padding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 12:35:08 +01:00
arkonandClaude Opus 4.6 2491471ba5 fix: prevent mobile page scroll when typing with keyboard open
iOS Safari scrolls the document to bring xterm's hidden textarea into
view when the user types, pushing the entire UI off-screen. Fix with:
- CSS position:fixed on .app when keyboard is visible
- window.scroll listener to reset scroll position as safety net
- scroll reset in onKeyboardShow before and after fit/resize

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 11:47:18 +01:00
arkonandClaude Opus 4.6 0c4aac8029 chore: version packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 10:05:16 +01:00
arkonandClaude Opus 4.6 e96baf9f66 chore: version packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 01:42:16 +01:00
arkonandClaude Opus 4.6 192690911f refactor: extract app.js into 6 domain modules with deferred init
Split the monolithic app.js (~12.5K lines) into 6 focused mixin modules
that extend CodemanApp.prototype via Object.assign:

- terminal-ui.js — terminal setup, rendering pipeline, controls
- respawn-ui.js — respawn banner, countdown, presets, run summary
- ralph-panel.js — Ralph state panel, fix_plan, plan versioning
- settings-ui.js — app settings, visibility, web push, tunnel/QR, help
- panels-ui.js — subagent panel, teams, insights, file browser, log viewer
- session-ui.js — quick start, session options, case settings

Fix deferred script init ordering: wrap CodemanApp instantiation in
DOMContentLoaded so all defer'd mixin modules execute their
Object.assign before the constructor runs. Without this, init() calls
methods like applyHeaderVisibilitySettings() that don't exist yet.

Guard missing cleanupWizardDragging() call in subagent-windows.js.
Update build.mjs to minify/hash all new modules. Update CLAUDE.md
with new frontend architecture and load order.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 21:24:22 +01:00
Skúli Arnlaugsson 2e4e646c06 fix: toolbar pushed off-screen on iPad Safari with tabs
On iPad Safari with the tab bar visible, `100vh` extends behind the
browser chrome, pushing the fixed-position toolbar out of view.

- Add `viewport-fit=cover` to viewport meta tag
- Use `100dvh` with `100vh` fallback for body/.app height
- Set `--app-height` CSS variable from `visualViewport.height` via JS
- Offset fixed toolbar on iOS Safari using the layout/visual viewport delta
2026-03-08 23:54:15 +00:00
Ark0NandClaude Opus 4.6 2ee9ad72e8 refactor: SSE event handlers, LLM context optimization, @fileoverview docs (#29)
* refactor: extract SSE event handlers into named class methods

Replace ~80 inline addListener closures in connectSSE() with a
declarative _SSE_HANDLER_MAP array that drives registration in a
single loop. Each handler is now a named _on* method on CodemanApp,
making them individually addressable for LLM navigation.

Add SSE_EVENTS constant object in constants.js to eliminate magic
event-type strings scattered across the frontend.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: fix inaccuracies in CLAUDE.md

- Fix types barrel path: src/types.ts → src/types/index.ts
- Update app.js line count: ~12K → ~11.5K
- Correct route handler counts (113 → 111, per-group fixes)
- Add code style, ESM gotcha, env vars, route test, lifecycle log docs
- Add Node 22 CI note, test teardown timeout, port range

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: add mobile screenshots and QR auth security writeup to README

Add 3 mobile screenshots (landing, idle, active) and expand the
mobile section with QR auth security design details and a
touch-optimized interface subsection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: bundle xterm-zerolag-input as vendor IIFE and add pre-commit hook

Build and postinstall now bundle the local xterm-zerolag-input package
as an IIFE at vendor/xterm-zerolag-input.js with global LocalEchoOverlay
shim. Add git pre-commit hook that runs prettier --check on staged .ts
files to catch format issues before CI.

Also bump constants.js and app.js cache-bust versions to 0.3.0 and add
tunnel upload URL display row in settings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: add cloudflared install support and interactive launch menu

- Add optional cloudflared dependency detection and installation
  across 6 distro families (macOS, Debian, Fedora, Arch, Alpine, SUSE)
- Add tunnel systemd service setup helper
- Replace post-install instructions with interactive launch menu
  (run now / systemd service / skip)
- Uninstall now cleans up both codeman-web and codeman-tunnel services

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: gitignore readme-preview.mjs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: WIP — SSE event constants, @fileoverview docs, CLAUDE.md compression

- Migrate broadcast() string literals → SseEvent.* typed constants
- Add @fileoverview with cross-domain references to all 13 type domain files
- Add @fileoverview to frontend JS modules (constants, mobile, voice, etc.)
- Add section dividers to route files for LLM scanability
- Compress CLAUDE.md: flat file list → domain table, fix counts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: optimize codebase for LLM context window efficiency

CLAUDE.md: 456 → 309 lines (32% reduction)
- Merge Commands into compact table, remove redundant bash block
- Convert Security section to dense table format
- Merge Performance + Resource Limits, Debugging + Troubleshooting
- Compress Tunnel, Memory Leak, Scripts, Screenshots sections
- Remove Key Patterns that duplicate @fileoverview in source files

Backend @fileoverview enhancements (10 priority files):
- session.ts: key methods, events, cross-domain refs
- respawn-controller.ts: state machine, idle detection layers
- ralph-tracker.ts: exports, circuit breaker, events
- ralph-loop.ts: lifecycle, persistence, events
- subagent-watcher.ts: watched patterns, teammate detection
- server.ts: coordination list, port interfaces
- state-store.ts: dual-file persistence, migration
- session-manager.ts: lifecycle methods, mutex guard
- hooks-config.ts: hook events list, categories
- sse-events.ts: category breakdown (~90 events, 17 categories)

Frontend app.js: add 6 section dividers, update @fileoverview line refs

Fix: escape glob `*/` in JSDoc that broke ESLint parser

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR #29 review bugs

- server.ts: replace hardcoded 'session:needsRefresh' with SseEvent constant
- install.sh: fix Alpine cloudflared install for non-root (download to tmpfile first)
- install.sh: replace Arch pacman (AUR-only) with direct binary download
- index.html: bump all 8 remaining cache-bust versions from v0.2.9 to v0.3.0
- mobile-handlers.js: fix @dependency annotation (keyboard-accessory.js, not constants.js)
- types/push.ts: fix layer number (4, not 5)
- subagent-watcher.ts: fix watched pattern path to include {session} segment
- constants.js: fix SSE_EVENTS count in @fileoverview (~73, not ~65)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 22:40:18 +01:00
arkonandClaude Opus 4.6 d094d9ec50 fix: code cleanup — path traversal, test leaks, dead code, consistency
- Add path traversal protection to GET /api/cases/:name and fix-plan
- Use safePathSchema for LinkCaseSchema.path
- Fix QR auth test timer leak (afterAll → afterEach) and env var try/finally
- Remove dead terminal size check after Zod validation in resize route
- Remove no-op sampleCount guard in adaptive timing
- Replace hardcoded values with constants in notification-manager and subagent-windows
- Add Zod validation to POST /api/auth/revoke
- Use _apiPut instead of raw fetch in subagent-windows
- Add SwipeHandler.cleanup() for consistency with other mobile handlers
- Move NiceConfig/ProcessStats from types/plan.ts to types/common.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 17:26:31 +01:00
arkonandClaude Opus 4.6 f44f0a912f refactor: split app.js into focused frontend modules (phase 5)
Extract 8 modules from the 15K-line app.js monolith:
- constants.js: shared constants, timing, escapeHtml(), extractSyncSegments()
- mobile-handlers.js: MobileDetection, KeyboardHandler, SwipeHandler
- voice-input.js: DeepgramProvider, VoiceInput
- notification-manager.js: NotificationManager class
- keyboard-accessory.js: KeyboardAccessoryBar, FocusTrap
- api-client.js: _api(), _apiJson(), _apiPost(), _apiDelete() helpers
- subagent-windows.js: 13 subagent window methods (open, close, drag, lines)
- vendor/xterm-zerolag-input.js: IIFE build replacing inlined copy

app.js reduced from ~15,200 to ~11,500 lines. All modules use global
scope with <script defer> ordering. Prototype extensions use
Object.assign(CodemanApp.prototype, {...}) pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 04:57:37 +01:00