Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Wisp/Models/Local/SpriteChat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ final class SpriteChat {
customName ?? "Chat \(chatNumber)"
}

var worktreeBranchLabel: String {
worktreeBranch.map { "'\($0)'" } ?? "same worktree"
}

init(
spriteName: String,
chatNumber: Int,
Expand Down
11 changes: 6 additions & 5 deletions Wisp/ViewModels/SpriteChatListViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,21 @@ final class SpriteChatListViewModel {
}

@discardableResult
func createChat(modelContext: ModelContext) -> SpriteChat {
func createChat(inheritingWorktreeFrom source: SpriteChat? = nil, modelContext: ModelContext) -> SpriteChat {
let maxNumber = chats.map(\.chatNumber).max() ?? 0

let workingDirectory = UserDefaults.standard.string(forKey: "workingDirectory_\(spriteName)") ?? "/home/sprite/project"

let workingDirectory = source?.workingDirectory
?? UserDefaults.standard.string(forKey: "workingDirectory_\(spriteName)")
?? "/home/sprite/project"
let chat = SpriteChat(
spriteName: spriteName,
chatNumber: maxNumber + 1,
workingDirectory: workingDirectory,
spriteCreatedAt: spriteCreatedAt
)
chat.worktreePath = source?.worktreePath
chat.worktreeBranch = source?.worktreeBranch
modelContext.insert(chat)
try? modelContext.save()

chats.insert(chat, at: 0)
activeChatId = chat.id
logger.info("Created chat \(chat.chatNumber) for \(self.spriteName)")
Expand Down
33 changes: 28 additions & 5 deletions Wisp/Views/SpriteDetail/Chat/ChatSwitcherSheet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,34 @@ struct ChatSwitcherSheet: View {
Button("Done") { dismiss() }
}
ToolbarItem(placement: .primaryAction) {
Button {
viewModel.createChat(modelContext: modelContext)
dismiss()
} label: {
Image(systemName: "plus")
if let activeChat = viewModel.activeChat, activeChat.worktreePath != nil {
let branchLabel = activeChat.worktreeBranchLabel
Menu {
Button {
viewModel.createChat(inheritingWorktreeFrom: activeChat, modelContext: modelContext)
dismiss()
} label: {
Label("New Chat in \(branchLabel)", systemImage: "arrow.branch")
}
Button {
viewModel.createChat(modelContext: modelContext)
dismiss()
} label: {
Label("New Chat", systemImage: "square.and.pencil")
}
} label: {
Image(systemName: "plus")
} primaryAction: {
viewModel.createChat(modelContext: modelContext)
dismiss()
}
} else {
Button {
viewModel.createChat(modelContext: modelContext)
dismiss()
} label: {
Image(systemName: "plus")
}
}
}
}
Expand Down
48 changes: 41 additions & 7 deletions Wisp/Views/SpriteDetail/SpriteDetailView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,39 @@ struct SpriteDetailView: View {
)
}

@ViewBuilder
private var newChatButton: some View {
if let activeChat = chatListViewModel.activeChat, activeChat.worktreePath != nil {
let branchLabel = activeChat.worktreeBranchLabel
Menu {
Button {
let chat = chatListViewModel.createChat(inheritingWorktreeFrom: activeChat, modelContext: modelContext)
switchToChat(chat)
} label: {
Label("New Chat in \(branchLabel)", systemImage: "arrow.branch")
}
Button {
let chat = chatListViewModel.createChat(modelContext: modelContext)
switchToChat(chat)
} label: {
Label("New Chat", systemImage: "square.and.pencil")
}
} label: {
Image(systemName: "square.and.pencil")
} primaryAction: {
let chat = chatListViewModel.createChat(modelContext: modelContext)
switchToChat(chat)
}
} else {
Button {
let chat = chatListViewModel.createChat(modelContext: modelContext)
switchToChat(chat)
} label: {
Image(systemName: "square.and.pencil")
}
}
}

private var regularLayout: some View {
HStack(spacing: 0) {
SpriteNavigationPanel(
Expand All @@ -64,7 +97,13 @@ struct SpriteDetailView: View {
let chat = chatListViewModel.createChat(modelContext: modelContext)
selectedTab = .chat
switchToChat(chat)
}
},
onCreateChatInWorktree: chatListViewModel.activeChat?.worktreePath != nil ? {
guard let activeChat = chatListViewModel.activeChat else { return }
let chat = chatListViewModel.createChat(inheritingWorktreeFrom: activeChat, modelContext: modelContext)
selectedTab = .chat
switchToChat(chat)
} : nil
)
.frame(width: 260)

Expand Down Expand Up @@ -190,12 +229,7 @@ struct SpriteDetailView: View {
}
}

Button {
let chat = chatListViewModel.createChat(modelContext: modelContext)
switchToChat(chat)
} label: {
Image(systemName: "square.and.pencil")
}
newChatButton
}
}
} else if selectedTab == .overview {
Expand Down
25 changes: 21 additions & 4 deletions Wisp/Views/SpriteDetail/SpriteNavigationPanel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ struct SpriteNavigationPanel: View {
@Binding var selection: SpriteNavSelection?
let chatListViewModel: SpriteChatListViewModel
let onCreateChat: () -> Void
var onCreateChatInWorktree: (() -> Void)? = nil
@Environment(SpritesAPIClient.self) private var apiClient
@Environment(ChatSessionManager.self) private var chatSessionManager
@Environment(\.modelContext) private var modelContext
Expand Down Expand Up @@ -46,11 +47,27 @@ struct SpriteNavigationPanel: View {
.tag(SpriteNavSelection.chat(chat.id))
}
}
Button(action: onCreateChat) {
Label("New Chat", systemImage: "square.and.pencil")
if let onCreateChatInWorktree {
let branchLabel = chatListViewModel.activeChat?.worktreeBranchLabel ?? "same worktree"
Menu {
Button(action: onCreateChatInWorktree) {
Label("New Chat in \(branchLabel)", systemImage: "arrow.branch")
}
Button(action: onCreateChat) {
Label("New Chat", systemImage: "square.and.pencil")
}
} label: {
Label("New Chat", systemImage: "square.and.pencil")
} primaryAction: onCreateChat
.foregroundStyle(.secondary)
.buttonStyle(.borderless)
} else {
Button(action: onCreateChat) {
Label("New Chat", systemImage: "square.and.pencil")
}
.foregroundStyle(.secondary)
.buttonStyle(.borderless)
}
.foregroundStyle(.secondary)
.buttonStyle(.borderless)
}
}
.listStyle(.sidebar)
Expand Down
Loading