feat(highlight): preset overhaul, categories, stats, theme colours, per-host profiles

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
This commit is contained in:
Bill committed 2026-09-23 11:14:31 +08:00
1 parent 41b1cb1b25
commit e08e1cfec4
39 files changed
+3164 -89

No files matched your search

+5 -1
View File
@@ -34,6 +34,7 @@ interface StoredConnection {
askPassphraseAtConnect: boolean
keyPath?: string
keepaliveIntervalSec: number
highlightProfileId?: string
createdAt: number
lastConnectedAt?: number
password_enc?: string
@@ -52,7 +53,8 @@ const PUBLIC_KEYS = [
'askPasswordAtConnect',
'askPassphraseAtConnect',
'keyPath',
'keepaliveIntervalSec'
'keepaliveIntervalSec',
'highlightProfileId'
] as const satisfies readonly (keyof Omit<StoredConnection, 'password_enc' | 'keyContent_enc' | 'passphrase_enc'>)[]
function encrypt(plain: string): string {
@@ -96,6 +98,7 @@ function toPublic(stored: StoredConnection): SshConnection {
askPassphraseAtConnect: stored.askPassphraseAtConnect,
keyPath: stored.keyPath,
keepaliveIntervalSec: stored.keepaliveIntervalSec,
highlightProfileId: stored.highlightProfileId,
createdAt: stored.createdAt,
lastConnectedAt: stored.lastConnectedAt,
savedAuth: {
@@ -175,6 +178,7 @@ export class ConnectionsStore {
askPassphraseAtConnect: input.askPassphraseAtConnect,
keyPath: input.keyPath,
keepaliveIntervalSec: input.keepaliveIntervalSec,
highlightProfileId: input.highlightProfileId,
createdAt: Date.now()
}
list.push(stored)