Skip to content

Commit e5aaa47

Browse files
committed
fix(ui): resolve editor race condition and default personality inheritance
**Problem:** 1. Personality/SystemPrompt editors showed empty fields on first open (race condition) 2. Changing default personality in preferences didn't affect new conversations 3. Mermaid diagrams render poorly for complex flows (hardcoded spacing) **Solution:** 1. Replaced .onAppear pattern with custom init() in editors: - PersonalityEditor: Initialize @State values directly from personality parameter - SystemPromptConfigurationEditor: Same pattern for consistency - Data available immediately when view appears, no async delay 2. ConversationSettings.init() now reads defaultPersonalityId from UserDefaults: - Falls back to Assistant UUID if not set - New conversations inherit user's default personality preference 3. Issue #3 investigated but not fixed in this commit: - All Mermaid renderers use hardcoded sizing (nodeWidth=180, spacing=100, etc.) - Recommendation: Switch to mermaid - Recommendation: Switch to mermaid - Recommendation: Switch to mermaid - Recommendation: Switchg:* - Recomm: - Recommendation: Switch to mermaid - Recommendation: Switch to mermaide view displays ✅ Default personality now inherited by new conversations
1 parent b0dd6e3 commit e5aaa47

3 files changed

Lines changed: 61 additions & 31 deletions

File tree

Sources/ConversationEngine/ConversationModel.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,22 @@ public struct ConversationSettings: Codable, Sendable {
148148
self.maxTokens = maxTokens
149149
self.contextWindowSize = contextWindowSize
150150
self.workspacePromptIds = workspacePromptIds
151-
self.selectedPersonalityId = selectedPersonalityId
151+
152+
/// Default to user's selected default personality if none specified
153+
/// This ensures new conversations inherit the personality preference
154+
if let providedPersonalityId = selectedPersonalityId {
155+
self.selectedPersonalityId = providedPersonalityId
156+
} else {
157+
// Get default personality from PersonalityManager
158+
// This is synchronous and safe as PersonalityManager is @MainActor
159+
if let defaultId = UUID(uuidString: UserDefaults.standard.string(forKey: "defaultPersonalityId") ?? "") {
160+
self.selectedPersonalityId = defaultId
161+
} else {
162+
// Fallback to Assistant (first default personality)
163+
self.selectedPersonalityId = UUID(uuidString: "00000000-0000-0000-0000-000000000001")
164+
}
165+
}
166+
152167
self.enableReasoning = enableReasoning
153168
self.enableTools = enableTools
154169
self.autoApprove = autoApprove

Sources/UserInterface/PersonalityManagementView.swift

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,30 @@ struct PersonalityEditor: View {
122122
let personalityManager: PersonalityManager
123123
@Binding var isPresented: Bool
124124

125-
@State private var name: String = ""
126-
@State private var description: String = ""
127-
@State private var selectedTraits: [PersonalityTraitCategory: PersonalityTrait] = [:]
128-
@State private var customInstructions: String = ""
125+
@State private var name: String
126+
@State private var description: String
127+
@State private var selectedTraits: [PersonalityTraitCategory: PersonalityTrait]
128+
@State private var customInstructions: String
129+
130+
/// Initialize with data immediately - no .onAppear race condition
131+
init(personality: Personality?, personalityManager: PersonalityManager, isPresented: Binding<Bool>) {
132+
self.personality = personality
133+
self.personalityManager = personalityManager
134+
self._isPresented = isPresented
135+
136+
// Initialize State values directly from personality (if editing) or defaults (if new)
137+
if let personality = personality {
138+
self._name = State(initialValue: personality.name)
139+
self._description = State(initialValue: personality.description)
140+
self._selectedTraits = State(initialValue: personality.selectedTraits)
141+
self._customInstructions = State(initialValue: personality.customInstructions)
142+
} else {
143+
self._name = State(initialValue: "")
144+
self._description = State(initialValue: "")
145+
self._selectedTraits = State(initialValue: [:])
146+
self._customInstructions = State(initialValue: "")
147+
}
148+
}
129149

130150
private var isEditing: Bool { personality != nil }
131151
private var title: String { isEditing ? "Edit Personality" : "New Personality" }
@@ -297,18 +317,6 @@ struct PersonalityEditor: View {
297317
}
298318
.frame(minWidth: 600, idealWidth: 800, maxWidth: 1000,
299319
minHeight: 500, idealHeight: 700, maxHeight: .infinity)
300-
.onAppear {
301-
setupInitialValues()
302-
}
303-
}
304-
305-
private func setupInitialValues() {
306-
if let personality = personality {
307-
name = personality.name
308-
description = personality.description
309-
selectedTraits = personality.selectedTraits
310-
customInstructions = personality.customInstructions
311-
}
312320
}
313321

314322
private func savePersonality() {

Sources/UserInterface/SystemPromptManagementView.swift

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,31 @@ struct SystemPromptConfigurationEditor: View {
9191
let promptManager: SystemPromptManager
9292
@Binding var isPresented: Bool
9393

94-
@State private var name: String = ""
95-
@State private var description: String = ""
96-
@State private var components: [SystemPromptComponent] = []
94+
@State private var name: String
95+
@State private var description: String
96+
@State private var components: [SystemPromptComponent]
9797
@State private var showingNewComponent = false
9898
@State private var showingComponentLibrary = false
9999
@State private var editingComponent: SystemPromptComponent?
100100

101+
/// Initialize with data immediately - no .onAppear race condition
102+
init(configuration: SystemPromptConfiguration?, promptManager: SystemPromptManager, isPresented: Binding<Bool>) {
103+
self.configuration = configuration
104+
self.promptManager = promptManager
105+
self._isPresented = isPresented
106+
107+
// Initialize State values directly from configuration (if editing) or defaults (if new)
108+
if let configuration = configuration {
109+
self._name = State(initialValue: configuration.name)
110+
self._description = State(initialValue: configuration.description)
111+
self._components = State(initialValue: configuration.components)
112+
} else {
113+
self._name = State(initialValue: "")
114+
self._description = State(initialValue: "")
115+
self._components = State(initialValue: [])
116+
}
117+
}
118+
101119
private var isEditing: Bool { configuration != nil }
102120
private var title: String { isEditing ? "Edit Template" : "New Template" }
103121

@@ -230,9 +248,6 @@ struct SystemPromptConfigurationEditor: View {
230248
}
231249
.frame(minWidth: 600, idealWidth: 800, maxWidth: 1000,
232250
minHeight: 500, idealHeight: 700, maxHeight: .infinity)
233-
.onAppear {
234-
setupInitialValues()
235-
}
236251
.sheet(isPresented: $showingNewComponent) {
237252
SystemPromptComponentEditor(
238253
component: nil,
@@ -270,14 +285,6 @@ struct SystemPromptConfigurationEditor: View {
270285
}
271286
}
272287

273-
private func setupInitialValues() {
274-
if let configuration = configuration {
275-
name = configuration.name
276-
description = configuration.description
277-
components = configuration.components
278-
}
279-
}
280-
281288
private func generatePreviewPrompt() -> String {
282289
return components
283290
.filter { $0.isEnabled }

0 commit comments

Comments
 (0)