Skip to content

fix: dark-mode input surfaces and stall-based install timeouts - #83

Merged
yujiezhang-ops merged 2 commits into
mainfrom
fix/dark-mode-input-and-install-stall-timeout
Aug 7, 2026
Merged

fix: dark-mode input surfaces and stall-based install timeouts#83
yujiezhang-ops merged 2 commits into
mainfrom
fix/dark-mode-input-and-install-stall-timeout

Conversation

@yujiezhang-ops

Copy link
Copy Markdown
Collaborator

Two independent bug fixes, one commit each.

Dark-mode text inputs (e5fa71a)

The API Key field stayed white in dark mode. Two causes, both needed fixing:

.secure-field and .search-field draw their own border, focus ring and background and hold the <input> as a bare text surface. The rule that themes every other field, .field-stack > input, is a child selector, so the wrapper in between stops it applying. Nothing else coloured these inputs, so the UA sheet's white default showed inside a dark box.

color-scheme also stayed at the :root default of light dark, following the desktop rather than the forced palette. It controls the parts of a control CSS cannot reach — a password field's masking dots, the caret, the autofill background — so forcing dark on a light desktop gave a dark field with a light caret.

Stall-based install timeouts (5f84f1a)

Installs and runtime downloads failed on slow links. The limits were elapsed time (180s per command, 10 min per download), which punishes a slow transfer for being slow while doing nothing about one that has actually died.

Both are now stall-based: no output from a command, or no bytes on a transfer, for the stall window ends it; anything still making progress runs as long as it needs. Old deadlines remain as one-hour backstops. http.Client.Timeout is replaced by dial and response-header timeouts, since that field bounds body reads too and was itself killing healthy transfers.

Stalls surface as ErrStalled, distinct from context.DeadlineExceeded and context.Canceled, so a dead network reads differently from the Task Center's stop button — and the captured stdout/stderr goes back with the error, since what the command said before going quiet is the only clue to where it stuck.

Two subtleties worth a reviewer's eye: liveness is recorded before boundedBuffer can discard, or a command past its 1 MB output cap would look stalled while healthy; and the watchdog polls a timestamp rather than arming a timer per read, which for a 50 MB download is hundreds of thousands of reads.

The frontend now sends timeout: 0 to mean "use the Go default" instead of repeating 180, which had been a second source of truth that disagreed silently once Go's default moved.

Testing

  • go test ./... — all packages pass
  • go test -race ./internal/{process,install,binding,desktopapp} — clean
  • go vet ./... — clean
  • pnpm run test — 30 files / 226 tests pass
  • pnpm run build (includes tsc --noEmit) — pass

New coverage: frontend/src/styles/input-surface.test.ts guards both wrappers and the color-scheme pairing; the Go side adds stall, cancellation-vs-stall, slow-but-alive, output-flood, and output-preserved-on-stall cases.

Dark mode was verified against computed styles in all three theme states (forced dark, forced light, follow-system) rather than by screenshot, since colour is what's under test.

🤖 Generated with Claude Code

yujiezhang-ops and others added 2 commits August 7, 2026 11:40
The API Key field stayed white in dark mode. Two independent causes:

`.secure-field` and `.search-field` draw their own border, focus ring and
background and hold the <input> as a bare text surface. The rule that themes
every other field, `.field-stack > input`, is a child selector, so the wrapper
in between stops it from applying. Nothing else coloured these inputs, leaving
the UA sheet's white default inside a dark box.

`color-scheme` also stayed at the `:root` default of `light dark`, so it
followed the desktop rather than the forced palette. That controls the parts of
a control the stylesheet cannot reach -- a password field's masking dots, the
caret, the autofill background -- so forcing dark on a light desktop produced a
dark field with a light caret, and the row still read as white.

Both wrappers now carry themed colour and background with the input inheriting
them, and both `.theme-dark` and `.theme-light` set `color-scheme`. Verified
against computed styles in all three theme states.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Installs and runtime downloads failed on slow links. The limits were elapsed
time -- 180s per command, 10min per download -- which punishes a slow transfer
for being slow while doing nothing about one that has genuinely died. An app
store does not give up on a slow download, it gives up on a stopped one.

Both limits are now stall-based: a command that produces no output, or a
transfer that receives no bytes, for the stall window is killed, while anything
still making progress runs as long as it needs. The old deadlines stay as
backstops at an hour, and `http.Client.Timeout` is dropped in favour of dial and
response-header timeouts, since that field bounds body reads too and was itself
killing healthy transfers.

Stalls surface as `ErrStalled`, distinct from `context.DeadlineExceeded` and
`context.Canceled`, so a dead network reads differently from the Task Center's
stop button. The captured stdout and stderr go back with it: whatever the
command said before going quiet is the only clue to where it stuck.

Liveness is recorded before `boundedBuffer` can discard, or a command that
passed its 1 MB output cap would be read as stalled while still healthy. The
watchdog polls a timestamp instead of arming a timer per read, which for a 50 MB
download is hundreds of thousands of reads.

The frontend now sends `timeout: 0` to mean "use the Go default" instead of
repeating 180, which had made it a second source of truth that disagreed
silently once Go's default moved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@yujiezhang-ops
yujiezhang-ops merged commit bc91057 into main Aug 7, 2026
4 checks passed
@yujiezhang-ops
yujiezhang-ops deleted the fix/dark-mode-input-and-install-stall-timeout branch August 7, 2026 09:52
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