mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 10:36:35 +00:00
Fix session rename not updating across views
After rename: - Update selectedSession so detail header refreshes immediately - Update sessionPreviews so previewFor() returns the new title - Dashboard now observes HermesFileWatcher and reloads on DB changes - Chat session menu reloads via file watcher (persists across nav) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import SwiftUI
|
|||||||
|
|
||||||
struct ChatView: View {
|
struct ChatView: View {
|
||||||
@Environment(ChatViewModel.self) private var viewModel
|
@Environment(ChatViewModel.self) private var viewModel
|
||||||
|
@Environment(HermesFileWatcher.self) private var fileWatcher
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
@@ -11,6 +12,9 @@ struct ChatView: View {
|
|||||||
}
|
}
|
||||||
.navigationTitle("Chat")
|
.navigationTitle("Chat")
|
||||||
.task { await viewModel.loadRecentSessions() }
|
.task { await viewModel.loadRecentSessions() }
|
||||||
|
.onChange(of: fileWatcher.lastChangeDate) {
|
||||||
|
Task { await viewModel.loadRecentSessions() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var toolbar: some View {
|
private var toolbar: some View {
|
||||||
@@ -36,6 +40,10 @@ struct ChatView: View {
|
|||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
if viewModel.hasActiveProcess {
|
||||||
|
voiceControls
|
||||||
|
}
|
||||||
|
|
||||||
if !viewModel.hermesBinaryExists {
|
if !viewModel.hermesBinaryExists {
|
||||||
Label("Hermes binary not found", systemImage: "exclamationmark.triangle")
|
Label("Hermes binary not found", systemImage: "exclamationmark.triangle")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
@@ -80,6 +88,41 @@ struct ChatView: View {
|
|||||||
.padding(.vertical, 6)
|
.padding(.vertical, 6)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var voiceControls: some View {
|
||||||
|
HStack(spacing: 8) {
|
||||||
|
Button {
|
||||||
|
viewModel.toggleVoice()
|
||||||
|
} label: {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Image(systemName: viewModel.voiceEnabled ? "mic.fill" : "mic.slash")
|
||||||
|
.foregroundStyle(viewModel.voiceEnabled ? .green : .secondary)
|
||||||
|
Text(viewModel.voiceEnabled ? "Voice On" : "Voice Off")
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(viewModel.voiceEnabled ? .primary : .secondary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.help("Toggle voice mode (/voice)")
|
||||||
|
|
||||||
|
if viewModel.voiceEnabled {
|
||||||
|
Button {
|
||||||
|
viewModel.pushToTalk()
|
||||||
|
} label: {
|
||||||
|
HStack(spacing: 4) {
|
||||||
|
Image(systemName: viewModel.isRecording ? "waveform.circle.fill" : "waveform.circle")
|
||||||
|
.foregroundStyle(viewModel.isRecording ? .red : .accentColor)
|
||||||
|
.symbolEffect(.pulse, isActive: viewModel.isRecording)
|
||||||
|
Text(viewModel.isRecording ? "Recording..." : "Push to Talk")
|
||||||
|
.font(.caption)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.buttonStyle(.plain)
|
||||||
|
.help("Push to talk (Ctrl+B)")
|
||||||
|
.keyboardShortcut("b", modifiers: .control)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var terminalArea: some View {
|
private var terminalArea: some View {
|
||||||
if let terminal = viewModel.terminalView {
|
if let terminal = viewModel.terminalView {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import SwiftUI
|
|||||||
struct DashboardView: View {
|
struct DashboardView: View {
|
||||||
@State private var viewModel = DashboardViewModel()
|
@State private var viewModel = DashboardViewModel()
|
||||||
@Environment(AppCoordinator.self) private var coordinator
|
@Environment(AppCoordinator.self) private var coordinator
|
||||||
|
@Environment(HermesFileWatcher.self) private var fileWatcher
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
@@ -16,6 +17,9 @@ struct DashboardView: View {
|
|||||||
}
|
}
|
||||||
.navigationTitle("Dashboard")
|
.navigationTitle("Dashboard")
|
||||||
.task { await viewModel.load() }
|
.task { await viewModel.load() }
|
||||||
|
.onChange(of: fileWatcher.lastChangeDate) {
|
||||||
|
Task { await viewModel.load() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var statusSection: some View {
|
private var statusSection: some View {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ final class SessionsViewModel {
|
|||||||
let result = runHermes(["sessions", "rename", sessionId, title])
|
let result = runHermes(["sessions", "rename", sessionId, title])
|
||||||
if result.exitCode == 0 {
|
if result.exitCode == 0 {
|
||||||
if let idx = sessions.firstIndex(where: { $0.id == sessionId }) {
|
if let idx = sessions.firstIndex(where: { $0.id == sessionId }) {
|
||||||
sessions[idx] = HermesSession(
|
let updated = HermesSession(
|
||||||
id: sessions[idx].id, source: sessions[idx].source,
|
id: sessions[idx].id, source: sessions[idx].source,
|
||||||
userId: sessions[idx].userId, model: sessions[idx].model,
|
userId: sessions[idx].userId, model: sessions[idx].model,
|
||||||
title: title, parentSessionId: sessions[idx].parentSessionId,
|
title: title, parentSessionId: sessions[idx].parentSessionId,
|
||||||
@@ -94,8 +94,13 @@ final class SessionsViewModel {
|
|||||||
cacheWriteTokens: sessions[idx].cacheWriteTokens,
|
cacheWriteTokens: sessions[idx].cacheWriteTokens,
|
||||||
estimatedCostUSD: sessions[idx].estimatedCostUSD
|
estimatedCostUSD: sessions[idx].estimatedCostUSD
|
||||||
)
|
)
|
||||||
|
sessions[idx] = updated
|
||||||
|
if selectedSession?.id == sessionId {
|
||||||
|
selectedSession = updated
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sessionPreviews[sessionId] = title
|
||||||
|
}
|
||||||
showRenameSheet = false
|
showRenameSheet = false
|
||||||
renameSessionId = nil
|
renameSessionId = nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user