Commit Graph

406 Commits

Author SHA1 Message Date
GitHub Copilot 3d4dab2359 Finalize Go-only migration, runtime hardening, and CI/container optimization
This commit consolidates the full repository transition to a Go-first codebase and captures the follow-up performance/reliability work completed in the same stream.

Highlights:
- Remove Python implementation and test suites (, , , ) and retire Python-specific docs/instructions.
- Move and standardize static web assets under , updating Bun/TypeScript build paths and server static resolution logic.
- Rewrite developer workflow to Makefile-first Go targets (vet/test/race/coverage/fuzz/build) and align repository guidance/docs accordingly.
- Update Docker and CI/CD for leaner artifacts:
  - switch to Alpine-based multi-stage build with stripped Go binary
  - install only minimal runtime deps (, )
  - tighten Docker build context via
  - ensure workflows build/publish the  target.
- Improve runtime correctness/latency and reduce duplication:
  - explicit WebSocket outbound frame typing (text vs binary) instead of payload-byte heuristics
  - SSE activity fan-out outside global lock and safer subscriber lifecycle
  - shared session output/snapshot helpers to reduce duplicated logic
  - restart-safe channel lifecycle for Docker watcher/stats start-stop-start flows
  - faster screenshot cold-start path (poll-until-ready within timeout vs fixed sleep).
- Add/expand regression coverage for the above lifecycle and helper paths.

Validation run:
- bun run build
Bundled 3 modules in 10ms

  terminal.js  0.68 MB  (entry point) (Bun typecheck + bundle)
- cd go && go vet ./...
cd go && go test ./...
ok  	github.com/rcarmo/webterm-go-port/cmd/webterm	(cached)
ok  	github.com/rcarmo/webterm-go-port/internal/terminalstate	(cached)
ok  	github.com/rcarmo/webterm-go-port/webterm	(cached)
cd go && go test ./webterm -coverprofile=coverage.out && go tool cover -func=coverage.out
ok  	github.com/rcarmo/webterm-go-port/webterm	(cached)	coverage: 81.0% of statements
github.com/rcarmo/webterm-go-port/webterm/cli.go:14:			RunCLI				51.6%
github.com/rcarmo/webterm-go-port/webterm/config.go:25:			DefaultConfig			100.0%
github.com/rcarmo/webterm-go-port/webterm/config.go:29:			LoadLandingYAML			82.6%
github.com/rcarmo/webterm-go-port/webterm/config.go:70:			LoadComposeManifest		76.9%
github.com/rcarmo/webterm-go-port/webterm/config.go:114:		extractLabel			92.3%
github.com/rcarmo/webterm-go-port/webterm/config.go:138:		asString			80.0%
github.com/rcarmo/webterm-go-port/webterm/constants.go:27:		EnvBool				50.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:30:	Read				100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:56:	NewDockerExecSession		100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:72:	Open				90.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:99:	Start				85.7%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:119:	readLoop			83.3%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:143:	handleOutput			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:153:	createExec			75.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:183:	startExecSocket			60.7%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:219:	resizeExec			83.3%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:237:	Close				90.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:250:	Wait				100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:257:	SetTerminalSize			81.8%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:274:	ForceRedraw			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:281:	SendBytes			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:294:	SendMeta			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:298:	IsRunning			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:304:	GetReplayBuffer			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:308:	GetScreenSnapshot		100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_exec_session.go:316:	UpdateConnector			80.0%
github.com/rcarmo/webterm-go-port/webterm/docker_http.go:23:		DockerSocketPath		100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_http.go:37:		newUnixHTTPClient		100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_http.go:47:		sharedUnixClient		91.7%
github.com/rcarmo/webterm-go-port/webterm/docker_http.go:64:		unixJSONRequest			84.2%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:33:		NewDockerStatsCollector		100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:47:		Available			72.7%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:64:		Start				80.0%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:78:		Stop				75.0%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:90:		AddService			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:101:		RemoveService			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:115:		GetCPUHistory			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:124:		pollLoop			95.2%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:160:		discoverContainers		65.4%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:198:		pollContainer			77.8%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:223:		calculateCPUPercent		69.2%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:260:		RenderSparklineSVG		89.3%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:299:		max				100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:306:		toAnyMap			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:323:		toStringMap			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:331:		toAnySlice			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:340:		toStringSlice			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:351:		toUint				91.7%
github.com/rcarmo/webterm-go-port/webterm/docker_stats.go:376:		toInt				85.7%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:34:		NewDockerWatcher		100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:54:		hasWebtermLabel			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:60:		isAutoLabel			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:67:		getContainerCommand		80.0%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:76:		getContainerTheme		100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:81:		getContainerName		42.9%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:93:		containerToSlug			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:98:		addContainer			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:119:	removeContainer			100.0%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:143:	listLabeledContainers		94.1%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:168:	handleEvent			80.0%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:202:	watchEvents			85.7%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:239:	ScanExisting			60.0%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:249:	Start				83.3%
github.com/rcarmo/webterm-go-port/webterm/docker_watcher.go:265:	Stop				81.8%
github.com/rcarmo/webterm-go-port/webterm/identity.go:12:		GenerateID			94.1%
github.com/rcarmo/webterm-go-port/webterm/normalize.go:13:		FilterDASequences		83.3%
github.com/rcarmo/webterm-go-port/webterm/replay.go:14:			NewReplayBuffer			66.7%
github.com/rcarmo/webterm-go-port/webterm/replay.go:21:			Add				100.0%
github.com/rcarmo/webterm-go-port/webterm/replay.go:43:			Bytes				100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:95:			OnData				100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go💯		OnBinary			100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:105:		OnMeta				0.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:107:		OnClose				100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:112:		NewLocalServer			100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:163:		findStaticPath			62.5%
github.com/rcarmo/webterm-go-port/webterm/server.go:184:		markRouteActivity		100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:207:		enqueueWSFrame			77.8%
github.com/rcarmo/webterm-go-port/webterm/server.go:233:		stopWSClient			100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:246:		wsSender			80.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:256:		createTerminalSession		66.7%
github.com/rcarmo/webterm-go-port/webterm/server.go:278:		clampInt			60.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:288:		parseResizePayload		88.9%
github.com/rcarmo/webterm-go-port/webterm/server.go:303:		handleWebSocket			81.1%
github.com/rcarmo/webterm-go-port/webterm/server.go:425:		chooseRouteForScreenshot	50.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:440:		screenshotTTL			66.7%
github.com/rcarmo/webterm-go-port/webterm/server.go:457:		handleScreenshot		55.7%
github.com/rcarmo/webterm-go-port/webterm/server.go:541:		handleCPUSparkline		94.4%
github.com/rcarmo/webterm-go-port/webterm/server.go:566:		handleEvents			76.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:602:		toIntFromQuery			100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:609:		dashboardTiles			81.8%
github.com/rcarmo/webterm-go-port/webterm/server.go:631:		handleTiles			100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:636:		getWSURL			65.2%
github.com/rcarmo/webterm-go-port/webterm/server.go:671:		handleRoot			56.8%
github.com/rcarmo/webterm-go-port/webterm/server.go:732:		htmlEscape			100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:736:		htmlAttrEscape			100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:740:		handleHealth			100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:744:		setupDockerFeatures		40.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:791:		shutdown			62.5%
github.com/rcarmo/webterm-go-port/webterm/server.go:814:		Handler				100.0%
github.com/rcarmo/webterm-go-port/webterm/server.go:829:		Run				77.8%
github.com/rcarmo/webterm-go-port/webterm/session.go:31:		OnData				0.0%
github.com/rcarmo/webterm-go-port/webterm/session.go:32:		OnBinary			0.0%
github.com/rcarmo/webterm-go-port/webterm/session.go:33:		OnMeta				0.0%
github.com/rcarmo/webterm-go-port/webterm/session.go:34:		OnClose				0.0%
github.com/rcarmo/webterm-go-port/webterm/session.go:36:		dispatchSessionOutput		100.0%
github.com/rcarmo/webterm-go-port/webterm/session.go:47:		snapshotFromTracker		100.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:20:	NewSessionManager		100.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:34:	SetSessionFactory		100.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:40:	defaultSessionFactory		87.5%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:57:	splitCommand			75.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:65:	shlexSplit			100.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:69:	AddApp				87.5%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:88:	RemoveApp			87.5%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:101:	Apps				100.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:107:	AppBySlug			100.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:114:	GetDefaultApp			80.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:123:	NewSession			50.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:167:	OnSessionEnd			100.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:176:	CloseAll			100.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:189:	CloseSession			87.5%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:201:	GetSession			100.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:207:	GetSessionByRouteKey		100.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:217:	GetSessionIDByRouteKey		100.0%
github.com/rcarmo/webterm-go-port/webterm/session_manager.go:223:	GetFirstRunningSession		85.7%
github.com/rcarmo/webterm-go-port/webterm/shellsplit.go:5:		shlexSplitImpl			100.0%
github.com/rcarmo/webterm-go-port/webterm/slugify.go:13:		Slugify				100.0%
github.com/rcarmo/webterm-go-port/webterm/svg_exporter.go:35:		RenderTerminalSVG		92.6%
github.com/rcarmo/webterm-go-port/webterm/svg_exporter.go:113:		colorToHex			87.5%
github.com/rcarmo/webterm-go-port/webterm/svg_exporter.go:139:		isHex				100.0%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:37:	NewTerminalSession		100.0%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:49:	Open				86.7%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:90:	Start				85.7%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:110:	readLoop			100.0%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:132:	handleOutput			100.0%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:142:	Close				100.0%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:160:	Wait				100.0%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:167:	SetTerminalSize			80.0%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:190:	ForceRedraw			100.0%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:198:	SendBytes			88.9%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:211:	SendMeta			100.0%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:215:	IsRunning			100.0%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:221:	GetReplayBuffer			100.0%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:225:	GetScreenSnapshot		100.0%
github.com/rcarmo/webterm-go-port/webterm/terminal_session.go:233:	UpdateConnector			80.0%
github.com/rcarmo/webterm-go-port/webterm/twoway.go:14:			NewTwoWayMap			100.0%
github.com/rcarmo/webterm-go-port/webterm/twoway.go:21:			Set				88.9%
github.com/rcarmo/webterm-go-port/webterm/twoway.go:36:			DeleteKey			100.0%
github.com/rcarmo/webterm-go-port/webterm/twoway.go:45:			Get				100.0%
github.com/rcarmo/webterm-go-port/webterm/twoway.go:52:			GetKey				100.0%
github.com/rcarmo/webterm-go-port/webterm/twoway.go:59:			Keys				100.0%
github.com/rcarmo/webterm-go-port/webterm/twoway.go:70:			UnsafeForward			100.0%
total:									(statements)			81.0%
- cd go && go test -race ./...
ok  	github.com/rcarmo/webterm-go-port/cmd/webterm	(cached)
ok  	github.com/rcarmo/webterm-go-port/internal/terminalstate	(cached)
ok  	github.com/rcarmo/webterm-go-port/webterm	(cached)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-14 18:03:28 +00:00
GitHub Copilot 065de286fb refactor: standardize Go layout with internal terminalstate
Audit findings: cmd/webterm and webterm package were acceptable, but terminalstate
was an internal implementation detail exposed as a public package.

Changes:
- Move go/terminalstate -> go/internal/terminalstate
- Update imports to github.com/rcarmo/webterm-go-port/internal/terminalstate
- Keep package name terminalstate unchanged

Resulting package layout:
- github.com/rcarmo/webterm-go-port/cmd/webterm
- github.com/rcarmo/webterm-go-port/internal/terminalstate
- github.com/rcarmo/webterm-go-port/webterm

Validation:
- go test ./...
- go test -race ./...
- go coverage check remains 80.9%
- make check

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-14 17:13:49 +00:00
GitHub Copilot f597f8f99d test: expand Go fuzzing and raise coverage above 80%
- Add comprehensive runtime tests for session, docker watcher/stats/http, and server helpers
- Add new fuzz targets: FuzzToIntFromQuery and FuzzHTMLHelpers
- Add cmd/webterm main entrypoint test
- Expand helper conversion and color-mode coverage tests

Validation:
- go test ./...
- go test -race ./...
- All 15 fuzz targets pass with short fuzz runs
- go test ./... -coverpkg=./... => 80.9% total statement coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-14 17:06:52 +00:00
GitHub Copilot 5aa9eb9426 chore: incorporate relevant agentbox skel files
Add relevant skeleton files from rcarmo/agentbox/skel for this repo:
- .github/workflows/ci.yml (Makefile checks + conditional Go tests)
- .github/instructions/{00-project-detection,docker-image,frontend-bun,go,python}.instructions.md

Instruction files were adapted where needed to match current workflow names
and repository layout (Go module under go/, Docker workflow filename).

Validated with make check and go test ./... .

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-14 16:44:44 +00:00
GitHub Copilot 674e62d983 refactor: upgrade go-te to v0.1.0, remove C1 normalization
go-te v0.1.0 fixes the ByteStream C1 bug (Latin-1 fallback for
invalid UTF-8 bytes), so NormalizeC1Controls is no longer needed.

Removed:
- NormalizeC1Controls function and its tests/fuzz target
- utf8Buffer field from TerminalSession and DockerExecSession
- C1BUG.md (fix shipped upstream)

The handleOutput pipeline now does: FilterDA → replay → tracker → connector
(was: FilterDA → NormalizeC1 → replay → tracker → connector)

All tests pass with -race. 13 fuzz targets remain.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-14 16:40:28 +00:00
GitHub Copilot 307a8cc312 docs: add go-te C1 ByteStream bug report (C1BUG.md)
ByteStream.Feed() in UTF-8 mode permanently stalls on raw C1
control bytes (0x80-0x9F). DecodeRune returns RuneError, the loop
breaks, and the bad byte stays in the buffer forever — blocking
all subsequent input and leaking memory.

Stream.handleGround() has correct C1 handlers but they are
unreachable through ByteStream. Includes reproduction case and
suggested fix (Latin-1 fallback for invalid UTF-8 bytes).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-14 16:32:50 +00:00
GitHub Copilot 52a96915f0 test: add 14 fuzz tests across Go packages
Fuzz targets covering all input-processing functions:
- terminalstate: FuzzTrackerFeed, FuzzTrackerFeedIncremental
- normalize: FuzzNormalizeC1Controls, FuzzFilterDASequences
- slugify: FuzzSlugify (with unit tests)
- identity: FuzzGenerateID (with unit tests)
- replay: FuzzReplayBuffer, FuzzReplayBufferRapid
- svg_exporter: FuzzColorToHex, FuzzIsHex, FuzzRenderTerminalSVG
- shellsplit: FuzzShlexSplit (with unit test)
- twoway: FuzzTwoWayMap (with unit test)
- config: FuzzExtractLabel

All 14 targets validated with -fuzztime=3s, no panics found.
All unit tests pass with -race.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-14 16:25:55 +00:00
GitHub Copilot fd6c1c4e0d feat: complete Go port with go-te terminal emulator
Full Go implementation under go/ replacing Python pyte with go-te:
- HTTP server with WebSocket protocol, SSE, screenshot SVG rendering
- PTY terminal sessions and Docker exec sessions
- Docker watcher (label-based container discovery + event stream)
- CPU stats collection with sparkline SVG rendering
- Session manager with TwoWayMap routing and replay buffers
- C1 normalization, DA filtering, identity generation, theme palettes

Audit fixes for 9 concurrency/correctness issues:
- HTTP transport leak: shared client pool for Docker socket calls
- WebSocket concurrent writes: all writes routed through send channel
- Closed channel panic: atomic.Bool guard on enqueueWSData
- GetFirstRunningSession: use UnsafeForward under SessionManager lock
- NewSession TOCTOU: re-check routeKey after re-acquiring lock
- waitErr data race: protect with mutex in both session types
- Replay buffer fragmentation: copy to new slice on eviction
- go-te dirty tracking: check screen.Dirty before incrementing counter
- Identity modulo bias: rejection sampling for uniform distribution

All Go tests pass (including -race). Python baseline unchanged (397 tests).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-14 16:18:23 +00:00
GitHub Copilot 7cb3799c3b Bump version to 1.2.18 2026-02-11 09:21:48 +00:00
GitHub Copilot 57a93a7cc6 Fix stdin stalls and test warnings 2026-02-11 09:21:29 +00:00
GitHub Copilot 3eeba0c1ec typo 2026-02-09 16:36:26 +00:00
GitHub Copilot b412d96653 Bump version to 1.2.17 2026-02-06 21:26:48 +00:00
GitHub Copilot 10ffb9d8cc docs: consolidate pyte patches documentation
Rename ink-clear-fix.md → pyte-patches.md and rewrite as a
comprehensive reference for all pyte monkeypatches:
- Patch 1: CSI S/T (SU/SD scroll) — primary ghost content fix
- Patch 2: Alternate screen buffer (DECSET 1049)
- Patch 3: Ink partial clear expansion (secondary defense)

Update README.md Known Issues to reflect the actual fix approach.
Update ARCHITECTURE.md to document alt_screen.py and pyte patching.
2026-02-06 21:24:52 +00:00
GitHub Copilot d538cae2fa Bump version to 1.2.16 2026-02-06 21:22:00 +00:00
GitHub Copilot 207eddc922 fix: implement CSI S/T (scroll up/down) to fix ghost content in screenshots
pyte 0.8.2 does not handle CSI S (SU — Scroll Up) or CSI T (SD —
Scroll Down). When TERM=xterm-256color, tmux sends CSI n S for bulk
scrolling instead of DECSTBM+index. pyte silently ignored these
sequences, leaving old content in the screen buffer — visible as
ghost content in SVG screenshots.

Fix: monkeypatch pyte's CSI dispatch tables to map S→scroll_up and
T→scroll_down, and implement both methods on AltScreen with proper
scroll-region (DECSTBM) support.

Adds 6 tests for SU/SD functionality.
2026-02-06 21:21:57 +00:00
GitHub Copilot c98cee4cd6 Bump version to 1.2.15 2026-02-06 20:01:52 +00:00
GitHub Copilot d74c2726bf docs: add Ink partial clear fix to README known issues and notes 2026-02-06 20:01:09 +00:00
GitHub Copilot 3ba8c7f352 fix: expand Ink partial clears to prevent ghost content in screenshots
Ink (React CLI framework) clears its output using repeated EL2+CUU1
sequences, one per previously-drawn line. When /clear resets Ink's
internal line counter, the next frame only erases a few lines instead
of the full previous output. In a real terminal the old content is in
scrollback and invisible, but pyte's fixed-size screen retains it,
producing ghost content (e.g. duplicated prompts) in SVG screenshots.

Added AltScreen.expand_clear_sequences() which detects runs of 3+
EL2+CUU1 pairs that don't reach row 0 and extends them to erase all
lines up to the top of the screen. Both DockerExecSession and
TerminalSession call this before feeding data to pyte.

Also made on_session_end() idempotent (contextlib.suppress KeyError)
to prevent a race when close_session() and natural session exit both
call it.

Added docs/ink-clear-fix.md with root cause analysis, byte-level
explanation, and reproduction script.
2026-02-06 20:00:09 +00:00
GitHub Copilot b87a698438 fix: properly clean up sessions when Docker containers are destroyed
- close_session() now calls on_session_end() to remove the session from
  sessions dict and routes, preventing zombie entries that persist after
  the container is gone
- _remove_container() now closes the active session before removing the
  app from apps_by_slug/apps, so session cleanup can still reference the
  app during teardown
- Updated and added tests to verify session tracking cleanup
2026-02-06 17:27:49 +00:00
GitHub Copilot 8513283eae Known issues 2026-02-04 21:49:09 +00:00
GitHub Copilot 70c0870bcc Bump version to 1.2.14 2026-02-04 07:14:20 +00:00
GitHub Copilot f5c2a80644 Fix tmux alt-screen handling
- handle DECSET ?47 as an alternate screen mode so tmux clear redraws don't overlay stale content in screenshots

- keep AltScreen mode checks aligned with 47/1047/1048/1049 variants used by full-screen TUIs

- document the screenshot debugging workflow in .github/skills/screenshot-debugging/SKILL.md for repeatable escape-sequence analysis
2026-02-04 07:13:26 +00:00
GitHub Copilot 4a4ab40917 Bump version to 1.2.13 2026-02-03 20:30:11 +00:00
GitHub Copilot 786cc4c80e Rebuild assets for mobile keybar updates 2026-02-03 20:21:00 +00:00
GitHub Copilot fa2255a06e Bump version to 1.2.12 2026-02-03 19:40:53 +00:00
GitHub Copilot 027c8931ed Add Alt/Fn modifiers to mobile keybar 2026-02-03 19:37:29 +00:00
GitHub Copilot 51984fd5d1 Bump version to 1.2.11 2026-02-01 09:46:47 +00:00
GitHub Copilot d644c3160e note 2026-02-01 09:46:44 +00:00
GitHub Copilot 381d844068 Make C1 handling UTF-8 safe 2026-02-01 09:45:23 +00:00
GitHub Copilot fadde71aa5 Teach helper Shift/Ctrl punctuation 2026-02-01 09:15:03 +00:00
GitHub Copilot 6e856f22b4 Let helper Ctrl guide physical letters 2026-02-01 09:10:31 +00:00
GitHub Copilot 5b52820b2c Bump version to 1.2.10 2026-02-01 08:58:37 +00:00
GitHub Copilot b71822b2be Prioritize Ctrl+Shift arrow sequences 2026-02-01 08:57:51 +00:00
GitHub Copilot 4c53c1509c Bump version to 1.2.9 2026-02-01 01:37:29 +00:00
GitHub Copilot 7849e53edd Fix iPad modifier handling for mobile keybar 2026-02-01 01:36:35 +00:00
GitHub Copilot e99ac51495 Bump version to 1.2.8 2026-01-31 19:24:05 +00:00
GitHub Copilot 704527c16c Invert selection colors for readability 2026-01-31 19:23:56 +00:00
GitHub Copilot dc60f78e59 Bump version to 1.2.7 2026-01-31 18:51:30 +00:00
GitHub Copilot 56574698ad Listen to the abyss: tame C1 control phantoms for clear screens 2026-01-31 18:50:58 +00:00
GitHub Copilot 64ee95b0f9 Bump version to 1.2.6 2026-01-31 16:50:02 +00:00
GitHub Copilot f12c9901d7 Handle alternate screen modes 2026-01-31 16:49:54 +00:00
GitHub Copilot 6d613f008d Rebuild terminal bundle 2026-01-31 16:28:33 +00:00
GitHub Copilot a530afce92 Improve selection contrast 2026-01-31 16:27:39 +00:00
GitHub Copilot 8408e7b679 Bump version to 1.2.5 2026-01-31 11:42:30 +00:00
GitHub Copilot 575eb236b2 Centralize env handling for screenshots 2026-01-31 11:42:12 +00:00
GitHub Copilot 38e7c0e489 Add alt screen buffer support 2026-01-31 11:37:34 +00:00
GitHub Copilot aba3460c40 Bump version to 1.2.4 2026-01-31 10:15:42 +00:00
GitHub Copilot 26e77a010b Document {container} placeholder in README 2026-01-31 10:15:05 +00:00
GitHub Copilot 9c5e3781c7 Support {container} placeholder in WEBTERM_DOCKER_AUTO_COMMAND
Allows per-container customization of the auto command. For example:
  WEBTERM_DOCKER_AUTO_COMMAND='tmux new-session -ADs {container}'

This creates a tmux session named after the container instead of using
a fixed session name for all containers.
2026-01-31 10:14:39 +00:00
GitHub Copilot 7556539b6f Bump version to 1.2.3 2026-01-31 08:41:06 +00:00