P1:
- settingsStore: back up an unparseable settings.json to .bak before
falling back to defaults, so the next mutation can no longer silently
wipe custom themes/highlight rules
- ptyDispatcher: fan out per-session data/exit handlers (Set instead of
a single slot) so SSH split panes stop stealing each other's stream
- FilePanel: monotonic refresh token keeps stale listings from painting
over a newer navigation; upload finish no longer yanks the panel back
- settings.css: active settings-tab label derives from --chrome-fg so it
stays visible on the shipped light themes
P2 (main/renderer):
- paste guard: a paste ending in a newline always confirms
- Workspace: closing an SSH pane no longer seeds the local cwd with a
remote path
- tray: skip close-dialog continuation on a destroyed window
- commands: close zombie 'in-progress' session logs at hydrate
- zmodem: clear the stale offer timer before arming a new one
- connectionsStore: coerce/validate renderer input before persisting
- sftp: OperationError marker class keeps translated errors out of the
transport-retry classifier
- CommandsPanel: surface save failures inside the dialog
- ConnectionSidebar: drop a tautological tooltip condition
P2 (i18n/tooling/tests):
- localize the 16 ANSI color labels and the highlight sample text
(21 new keys across zh-CN/zh-TW/en/ja)
- sync-changelog: keep ### subheadings, normalize CRLF notes
- release.cjs: GitHub release reuse-by-tag (idempotent re-runs); fail
loudly on a failed Gitea asset listing
- commands-store test: absent historyEnabled now truly tests absence
Rules & engine:
- 22 presets (was 11): split status into okstate/warnstate/badstate, add delop,
createop, danger, secret, level, exitcode, percent, http; status words are
case-insensitive and cover the ✓ ✔ ✅ ✗ ✘ ✖ ❌ ⚠ symbol set
- value bands: the first number in a match picks the colour
(percent: <20% red / 20-50% yellow / 50-80% light green / >=80% green)
- optional per-rule `caseInsensitive`, `category`, `bands`; load-time
`refreshBuiltinRules` upgrades untouched built-in patterns in place
Settings page:
- three-way master switch `highlightMode` (all / basic / off); `basic` runs only
the five safety+status rules and `off` empties the rule set rather than
bypassing HighlightStream (which would drop the held tail)
- category column + grouping (`highlightGroupByCategory`), per-rule hit/duration
stats (`highlightStats`, opt-in sink, snapshot once a second), theme-following
colours (`highlightThemeColors`, hue-bucket mapping onto the ANSI palette),
import/export JSON envelope, live preview through the real engine
- named rule subsets bound per host (`highlightPerHost` + `highlightProfiles`
+ `SshConnection.highlightProfileId`); empty ruleIds = every rule
Tests: new tests/hl-rules.mjs (word boundaries, case flag, negative words,
bands, import/export, preview, basic mode, categories, stats, theme colours,
profiles) + profile round-trip in tests/settings-store.mjs
- upload: per-chunk buffer (ssh2 re-reads the overflow tail after the ACK;
a reused buffer silently corrupted every file >= ~254KB)
- close the cached SFTP channel on eviction, attach an 'error' handler,
close the download handle, time out execQuiet, fail partial deletes
- per-session StringDecoder for the ssh data plane (CJK mojibake), real
exit codes, safe replay truncation, zmodem abort/counter/timer fixes
- sysinfo: idempotent poll end, error routing, per-poll watchdog, proc(5)
CPU total; expand cd ~/$HOME/%USERPROFILE% paths; log sanitizer fixes
- security: will-navigate guard, central IPC sender check, scheme
allowlist for openExternal, single-instance else branch, layout id and
log-name whitelists, custom theme sanitizing, atomic JSON writes with
EPERM retry in store.writeJson
- updater: per-attempt feed choice, quitAndInstall relaunch, dev guard,
update-state getter