diff --git a/releases/v2.6.0/RELEASE_NOTES.md b/releases/v2.6.0/RELEASE_NOTES.md index 39ca0d1..4587de0 100644 --- a/releases/v2.6.0/RELEASE_NOTES.md +++ b/releases/v2.6.0/RELEASE_NOTES.md @@ -106,6 +106,19 @@ The foundation of every gated surface above: ### Bug fixes +#### Chat composer + transcript (post-merge round) + +- **Typing lag in the chat composer (#67)** — `RichChatInputBar.updateMenuState()` ran on every keystroke and unconditionally wrote both `showMenu` and `selectedIndex`, tripping SwiftUI's "action tried to update multiple times per frame" warning and stalling input. Composer now coalesces writes to deltas, short-circuits when not in slash mode (the common case), and watches `commands.count` instead of re-allocating `commands.map(\.id)` per keystroke. +- **Chat font-size slider had no visible effect (#68)** — `RichChatView` only set `\.dynamicTypeSize`, but `ScarfFont` tokens are fixed-point (`Font.system(size: 14, …)`) so dynamic type didn't reach bubble text, reasoning, tool chips, code blocks, or markdown headings. New `\.chatFontScale` env value plumbed through `RichMessageBubble`, `MarkdownContentView`, and `CodeBlockView`; `ChatFontScale.{body, caption, captionStrong, caption2, mono, monoSmall, codeBlock, codeInline}(_:)` helpers mirror the ScarfFont base sizes so 100% is byte-for-byte identical to today's UI. +- **Placeholder ghosting on first keystroke (#65)** — `TextEditor`'s NSTextView surfaces a typed glyph one frame before the SwiftUI binding propagates, so the bare `if text.isEmpty` overlay rendered the translucent placeholder text on top of the just-typed character. Pinned an opaque background behind the placeholder rect and switched the conditional to `.opacity(...)` so the view tree stays stable per keystroke. +- **Draft text leaked between conversations (#62)** — composer `@State` survived session switches because the surrounding view tree was structurally identical. Bound `RichChatInputBar`'s identity to `richChat.sessionId` so SwiftUI rebuilds the view (and its `@State`) on session change. Stable fallback string for the "no session selected" window — `UUID()` would have minted a new id per body re-eval and trashed the composer mid-typing. +- **Sent message rendered blank after navigating away (#63)** — when a user sent a prompt and immediately resumed a different session before Hermes flushed the row to state.db, `resumeSession`'s `reset()` cleared `messages` and `loadSessionHistory` then read an as-yet-empty DB. New per-session pending-user-messages cache survives `reset()` and re-injects still-pending entries on load; entries clear themselves as soon as a matching DB row catches up. +- **No completion notification (#64)** — sending a long prompt and switching to other work required polling the chat to know when the response landed. New `ChatNotificationService` fires a local `UNUserNotificationCenter` banner on prompt completion when Scarf isn't the foreground app. Settings → Display → Feedback → "Notify when Hermes finishes" toggle, default on. +- **Per-message TTS playback (#66)** — small speaker glyph in each settled assistant bubble's metadata footer; uses `AVSpeechSynthesizer` with the user's macOS Spoken Content default voice, picks up offline. Markdown control characters stripped before speech. The deeper Settings → Voice provider integration (Edge / ElevenLabs / OpenAI / NeuTTS / Piper) is queued as a v2.7 follow-up. +- **ACP control-message timeout under gateway concurrency (#61)** — bumped 30s → 60s. State.db lock contention on a healthy host clears in seconds, but the previous 30s watchdog tripped under realistic gateway+ACP concurrency (Discord sync / skill registration / cron scheduling holding write locks during ACP `initialize` / `session/new` / `session/load`). 60s gives lock resolution headroom while still surfacing genuinely broken transports. + +#### Pre-merge + - **Test target compile** — `M5FeatureVMTests.ScriptedTransport` had drifted off the `ServerTransport` protocol after `cachedSnapshotPath` landed in v2.5.2; added the missing stub. `M0dViewModelsTests` got the `ConnectionStatusViewModel.Status.degraded` argument-name update. `CredentialPoolsGatingTests` got the missing `import ScarfCore`. The full `swift test` suite now runs (and passes — 215 tests across 17 suites). - **iOS package compile** — `RemoteBackupService.zipDirectory` and `RemoteRestoreService.unzipArchive` used `Foundation.Process` unconditionally, breaking the iOS build entirely (Process is unavailable on the iOS SDK). Wrapped in `#if !os(iOS)` with iOS stubs that throw — backup/restore is Mac-only by design.