Repository navigation
feat(sessions): chat session sidebar, run cues and custom sound files - #1
Conversation
…pstream 2.2.0 In-leaf resizable session list: Pinned/Archived sections, search, sort, new/open/rename/delete, pin/archive (running-guard), resize + width persist, reveals >=600px. Plugin-data-backed flags with rename/delete lifecycle. Rebased onto current upstream (was 2.1.0); resolved dataStore import + Chat.svelte layout conflicts (dropped upstream-removed messageNavHotkeys). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
afa40f7 to
5e57f30
Compare
|
Re-ran the full gate battery against this branch. Baseline is the PR's own base (
One note for whoever builds the test vault, unrelated to this branch: Not smoke-tested in a live vault — the gates are the evidence, and the resize point is a code-level finding. Best regards, 🤖 Written with Claude Code |
…rmove The divider assigned data.sessionSidebarWidth on every pointermove, and the store setter saves on assignment, so a single drag produced a burst of whole-file data.json writes - the write pattern behind the 2026-09-27 corruption incident. Rendering and persistence are now separate: a drag previews into component-local state and commits once on pointerup; a held arrow key is coalesced through a 250ms quiet period; destroy() flushes so a chosen width is not lost on unmount. Covered by test/components/sessionResizePersistence.test.ts - a gesture persists exactly once, and the value still survives a reload.
Each row now shows what happened to its last turn instead of only a spinner for running sessions. Running, done, failed, cancelled and interrupted each carry a distinct icon AND a text label, so colour is never the only signal. idle is the honest unknown, not a synonym for done: the registry only holds sessions that have been opened, so an unopened row has no live state to read and renders nothing rather than implying an outcome. A pair left streaming by a mid-run close reports interrupted - it is neither done nor failed, and guessing would put a false claim on a status surface. Terminal state comes from the last message pair's assistant state, which the session already holds reactively, so a row updates as a run settles.
…rip ends the drag Integrates the two parallel resize fixes: the commit-on-pointerup width model with its four tests, plus pointer capture and pointercancel handling. Without capture a pointerup landing outside the 5px strip, or over an iframe, is lost and the drag sticks to the cursor.
One cue per settled run, fired from ChatSession's finally so it happens once however many panes have the thread open. The outcome is read from the state the catch block has already normalised, so a post-success bookkeeping failure still reports success rather than a false failure. Cancelled runs stay silent. Sound and notification are independent toggles, both defaulting on. The settings read is guarded: the finally can execute with no plugin initialised (headless, or the unit tests for this path) and a cue must never break the bookkeeping of a run that has already settled.
findPairAcrossRebuild prefers stableKey, but sendMessage builds the optimistic pair without one, so after the settle rebuild the lookup matched nothing and a successful run emitted no cue. Read the settled state from the pair the run holds and treat the lookup as a preference. A regression test drives the rebuild seam, and both new tests go red without this fix.
… status icon on navigation Two follow-ups reported after the live smoke test (2026-10-01). 1. The Windows notification said "Chat run finished" without saying which chat. The settle cue now carries the session name and uses it as the notification title. Notice, the permission-less floor, composes the name in. The name is mirrored onto the thread's session flags from the loaded thread title, so a backgrounded settle can name a chat that is not open. 2. The per-row status icon vanished when navigating between sessions. A live session knows a row's outcome, but the registry keeps only MAX_PARKED_SESSIONS idle sessions and evicts the rest, so the in-memory state is gone and statusFor fell back to idle. The settle handler now persists the outcome on the thread's flags and rows read it back via deriveRowStatus. Live state wins when present, the persisted outcome is the fallback, and idle keeps its honest meaning. Tests: the new assertions were shown red with the fix reverted and green with it restored. svelte-check 0/0. Full suite 147/148 files, 2028/2029 pass, the one failure being the pre-existing bootstrapDefaultSkills CRLF test, untouched here. Production build exit 0.
A settlement cue could only be the built-in 880/320 Hz tone. Users who want their own sound had no way to supply one. Add optional success/failure sound-file paths, read from disk at play time and falling back to the built-in tone when the file is missing or undecodable. Desktop-only: the fields are hidden on mobile, and the reader returns null where there is no filesystem. The decision stays pure (cueForOutcome) so it is testable without an AudioContext; only emission touches the filesystem. Decoded buffers and in-flight decodes are cached per path so a settle does not re-read the same file.
CI caught eight files the formatter would have rewritten — the local check had been run with the wrong binary (npx resolved `biome@0.3.3`, not the repo's `@biomejs/biome@1.9.4`), so it reported a false pass. Formatting only; no behaviour change.
What
The whole session-sidebar stack in one PR. Consolidates what were three stacked PRs — #1 (desktop sidebar), #2 (mobile sheet + touch actions) and #4 (custom sound files). #2 and #4 are closed; every commit is in this branch, so the diff is the sum of the three.
runSuccessSoundPath/runFailureSoundPathtake absolute OS paths, decoded at play time, falling back to the built-in 880/320 Hz tone when missing or undecodable.How I tested it
Real vault, Obsidian 1.13.7, desktop (Windows):
Not tested: the mobile sheet on a real device. Everything above is desktop.
AI assistance: drafted by an AI agent (Smart Second Brain); reviewed and tested live by me.
Checklist
check/format/lintpass locally — via the npm equivalents (npx biome format,npx biome lintexit 0 across./src ./test ./integration;svelte-check0/0 on both configs).bunis not available on the machine this was prepared on.test— one failure,test/skills/shippedSkillHistory.test.ts, pre-existing and Windows-clone-only (core.autocrlf=true, no.gitattributes); it fails identically with these changes reverted and CI checks out LF.manifest.jsonis unchangedNotes
CONTRIBUTING asks for PRs against
main— this one is, and the earlier stacked bases are gone with the closed PRs.