feat: newest message first, Raycast-style conversation sidebar
Three changes from comparing against Raycast's AI chat.
1. The transcript now reads newest message first, so an answer sits above
the question it responds to. The search bar is pinned to the top of
the window, so the most recent thing belongs directly under it rather
than below the fold. buildThreadMarkdown regroups messages back into
exchanges, so the horizontal rule separates one exchange from the next
instead of dividing every single message.
2. The sidebar is titles only, grouped into recency sections (Today,
Yesterday, This week, Older). Raycast does this, and it fixes rows
eliding down to "Hello l..." -- a subtitle and a timestamp on a 38px
row left the title almost no width. The check icon is what marks the
open conversation now.
3. The status bar names the open conversation, which is the nearest thing
Vicinae has to the header Raycast puts above its transcript.
Also removes dead code found on the way: ThreadMessage.preview was no
longer read by anything once the rows became conversations. Rather than
delete the plumbing outright, the activity line it carried ("Searching
the web") moved into the transcript placeholder and the search bar, so
tool activity is still visible -- it just is not a per-row preview any
more.
Verified on screen: answer above question, one rule per exchange, the
sidebar showing a single "Today" section with a title-only row, and the
status bar naming the conversation. typecheck, eslint, 113 tests and
vici build all pass.
This commit is contained in:
@@ -145,7 +145,9 @@ A few decisions worth knowing about before you change things:
|
||||
|
||||
**Return adapts to the search bar.** With text in it, `↩` sends. Empty — where sending would be a no-op anyway — `↩` opens the highlighted conversation. Every row carries that conversation's transcript as its detail, so moving the selection previews it.
|
||||
|
||||
**Conversations are ordered most-recently-updated first.** Vicinae resets list selection to the first row on every model change (`GenericListView.selectFirstOnReset`), and streaming changes the model on every flush. Ordering by recency keeps the conversation being chatted in at row zero, which is where the selection lands.
|
||||
**Conversations are ordered most-recently-updated first, and the sidebar shows titles only, grouped by recency.** Vicinae resets list selection to the first row on every model change (`GenericListView.selectFirstOnReset`), and streaming changes the model on every flush. Ordering by recency keeps the conversation being chatted in at row zero, which is where the selection lands. Raycast's sidebar works the same way, and grouping by day buys the recency cue that a timestamp on every row was trying to convey — while leaving the title room to actually be readable.
|
||||
|
||||
**The transcript reads newest message first.** The search bar sits at the top of the window, so the most recent message — an answer, once one has been written — is the first line under it rather than below the fold. Within a single exchange that means the answer appears above the question it responds to. `buildThread` produces that order, and `buildThreadMarkdown` regroups messages back into exchanges so the horizontal rule separates one exchange from the next instead of dividing every message.
|
||||
|
||||
**The transcript is text-only.** Speaker labels are plain `You` / `Lam`, with the question dimmed as a blockquote and the answer as body text. Icons are not possible there: `MdImage` renders images block-level, centred, and capped at 200px tall, and every candidate source was tried — `icon://person`, a raw icon slug, and a `file://` asset path — and each fell back to rendering its alt text as a caption.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user