feat: add recent session switchers
This commit is contained in:
@@ -11,6 +11,7 @@ import UniformTypeIdentifiers
|
||||
struct ClawdisChatComposer: View {
|
||||
@Bindable var viewModel: ClawdisChatViewModel
|
||||
let style: ClawdisChatView.Style
|
||||
let showsSessionSwitcher: Bool
|
||||
|
||||
#if !os(macOS)
|
||||
@State private var pickerItems: [PhotosPickerItem] = []
|
||||
@@ -23,6 +24,9 @@ struct ClawdisChatComposer: View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
if self.showsToolbar {
|
||||
HStack(spacing: 6) {
|
||||
if self.showsSessionSwitcher {
|
||||
self.sessionPicker
|
||||
}
|
||||
self.thinkingPicker
|
||||
Spacer()
|
||||
self.refreshButton
|
||||
@@ -91,6 +95,26 @@ struct ClawdisChatComposer: View {
|
||||
.frame(maxWidth: 140, alignment: .leading)
|
||||
}
|
||||
|
||||
private var sessionPicker: some View {
|
||||
Picker(
|
||||
"Session",
|
||||
selection: Binding(
|
||||
get: { self.viewModel.sessionKey },
|
||||
set: { next in self.viewModel.switchSession(to: next) }))
|
||||
{
|
||||
ForEach(self.viewModel.sessionChoices, id: \.key) { session in
|
||||
Text(session.key)
|
||||
.font(.system(.caption, design: .monospaced))
|
||||
.tag(session.key)
|
||||
}
|
||||
}
|
||||
.labelsHidden()
|
||||
.pickerStyle(.menu)
|
||||
.controlSize(.small)
|
||||
.frame(maxWidth: 160, alignment: .leading)
|
||||
.help("Session")
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
private var attachmentPicker: some View {
|
||||
#if os(macOS)
|
||||
|
||||
@@ -58,7 +58,10 @@ public struct ClawdisChatView: View {
|
||||
VStack(spacing: Layout.stackSpacing) {
|
||||
self.messageList
|
||||
.padding(.horizontal, Layout.outerPaddingHorizontal)
|
||||
ClawdisChatComposer(viewModel: self.viewModel, style: self.style)
|
||||
ClawdisChatComposer(
|
||||
viewModel: self.viewModel,
|
||||
style: self.style,
|
||||
showsSessionSwitcher: self.showsSessionSwitcher)
|
||||
.padding(.horizontal, Layout.composerPaddingHorizontal)
|
||||
}
|
||||
.padding(.vertical, Layout.outerPaddingVertical)
|
||||
|
||||
@@ -99,6 +99,42 @@ public final class ClawdisChatViewModel {
|
||||
Task { await self.performSwitchSession(to: sessionKey) }
|
||||
}
|
||||
|
||||
public var sessionChoices: [ClawdisChatSessionEntry] {
|
||||
let now = Date().timeIntervalSince1970 * 1000
|
||||
let cutoff = now - (24 * 60 * 60 * 1000)
|
||||
let sorted = self.sessions.sorted { ($0.updatedAt ?? 0) > ($1.updatedAt ?? 0) }
|
||||
var seen = Set<String>()
|
||||
var recent: [ClawdisChatSessionEntry] = []
|
||||
for entry in sorted {
|
||||
guard !seen.contains(entry.key) else { continue }
|
||||
seen.insert(entry.key)
|
||||
guard (entry.updatedAt ?? 0) >= cutoff else { continue }
|
||||
recent.append(entry)
|
||||
}
|
||||
|
||||
let mainKey = "main"
|
||||
var result: [ClawdisChatSessionEntry] = []
|
||||
var included = Set<String>()
|
||||
if let main = sorted.first(where: { $0.key == mainKey }) {
|
||||
result.append(main)
|
||||
included.insert(mainKey)
|
||||
} else if self.sessionKey == mainKey {
|
||||
result.append(self.placeholderSession(key: mainKey))
|
||||
included.insert(mainKey)
|
||||
}
|
||||
|
||||
for entry in recent where !included.contains(entry.key) {
|
||||
result.append(entry)
|
||||
included.insert(entry.key)
|
||||
}
|
||||
|
||||
if !included.contains(self.sessionKey) {
|
||||
result.append(self.placeholderSession(key: self.sessionKey))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
public func addAttachments(urls: [URL]) {
|
||||
Task { await self.loadAttachments(urls: urls) }
|
||||
}
|
||||
@@ -301,6 +337,23 @@ public final class ClawdisChatViewModel {
|
||||
await self.bootstrap()
|
||||
}
|
||||
|
||||
private func placeholderSession(key: String) -> ClawdisChatSessionEntry {
|
||||
ClawdisChatSessionEntry(
|
||||
key: key,
|
||||
kind: nil,
|
||||
updatedAt: nil,
|
||||
sessionId: nil,
|
||||
systemSent: nil,
|
||||
abortedLastRun: nil,
|
||||
thinkingLevel: nil,
|
||||
verboseLevel: nil,
|
||||
inputTokens: nil,
|
||||
outputTokens: nil,
|
||||
totalTokens: nil,
|
||||
model: nil,
|
||||
contextTokens: nil)
|
||||
}
|
||||
|
||||
private func handleTransportEvent(_ evt: ClawdisChatTransportEvent) {
|
||||
switch evt {
|
||||
case let .health(ok):
|
||||
|
||||
Reference in New Issue
Block a user