Skip to content

Commit e39905d

Browse files
committed
simpler in-place rename from the sidebar item
1 parent a33dead commit e39905d

3 files changed

Lines changed: 70 additions & 214 deletions

File tree

Flitro/Editor/ContextDetailsView.swift

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -268,15 +268,6 @@ struct ContextDetailsView: View {
268268
}
269269
}
270270
.toolbar {
271-
// Invisible, flexible item claims the center (principal) space
272-
ToolbarItem(placement: .principal) {
273-
Color.clear.frame(maxWidth: .infinity)
274-
}
275-
// Title anchored on the very left (navigation area), allowed to expand
276-
ToolbarItem(placement: .navigation) {
277-
ContextTitleView(context: context, contextManager: contextManager)
278-
.frame(maxWidth: .infinity, alignment: .leading)
279-
}
280271
// Trailing actions stay on the right
281272
ToolbarItemGroup(placement: .primaryAction) {
282273
// Add dropdown menu for adding items (unchanged)
@@ -293,10 +284,7 @@ struct ContextDetailsView: View {
293284
ContextButton(context: context, contextManager: contextManager)
294285
}
295286
}
296-
.navigationTitle("")
297-
.onChange(of: context.id) { _, _ in
298-
// Title editing state now managed by ContextTitleView
299-
}
287+
.navigationTitle(context.name)
300288
}
301289

302290
private func onOpenAction(for item: ContextItem, contextIdx: Int) -> (() -> Void)? {

Flitro/Editor/ContextTitleView.swift

Lines changed: 0 additions & 182 deletions
This file was deleted.

Flitro/Sidebar/ContextCardView.swift

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ struct ContextCardView: View {
1313
@State private var iconRotation: Double = 0
1414
@State private var cardScale: CGFloat = 1.0
1515
@EnvironmentObject private var contextManager: ContextManager
16+
@State private var isRenaming = false
17+
@State private var draftName: String = ""
18+
@FocusState private var renameFieldFocused: Bool
1619

1720
private var itemCountText: String {
1821
let total = context.items.count
@@ -24,7 +27,7 @@ struct ContextCardView: View {
2427
}
2528

2629
var body: some View {
27-
HStack(alignment: .center, spacing: 12) {
30+
HStack(alignment: .center, spacing: 8) {
2831
ContextIconView(
2932
context: context,
3033
size: 32,
@@ -33,28 +36,51 @@ struct ContextCardView: View {
3336
)
3437
.scaleEffect(cardScale)
3538
VStack(alignment: .leading, spacing: 2) {
36-
Text(context.name)
37-
.font(.system(size: 16, weight: isSelected ? .semibold : .regular))
38-
.lineLimit(2)
39-
.truncationMode(.tail)
40-
.multilineTextAlignment(.leading)
41-
.frame(maxWidth: .infinity, alignment: .leading)
42-
.fixedSize(horizontal: false, vertical: true)
43-
.minimumScaleFactor(0.6)
39+
if isRenaming {
40+
TextField("Rename Context", text: $draftName)
41+
.textFieldStyle(.plain)
42+
.focused($renameFieldFocused)
43+
.onSubmit { commitRename() }
44+
.onExitCommand { cancelRename() }
45+
.onChange(of: renameFieldFocused) { _, focused in
46+
if !focused { commitRename() }
47+
}
48+
.padding(.vertical, 6)
49+
.padding(.horizontal, 8)
50+
.background(
51+
RoundedRectangle(cornerRadius: 8)
52+
.fill(Color(NSColor.textBackgroundColor))
53+
.overlay(
54+
RoundedRectangle(cornerRadius: 8).stroke(Color.gray.opacity(0.15))
55+
)
56+
)
57+
.frame(maxWidth: .infinity, alignment: .leading)
58+
.fixedSize(horizontal: false, vertical: true)
59+
.layoutPriority(2)
60+
} else {
61+
Text(context.name)
62+
.font(.system(size: 16, weight: isSelected ? .semibold : .regular))
63+
.lineLimit(2)
64+
.truncationMode(.tail)
65+
.multilineTextAlignment(.leading)
66+
.frame(maxWidth: .infinity, alignment: .leading)
67+
.fixedSize(horizontal: false, vertical: true)
68+
.minimumScaleFactor(0.6)
69+
}
4470
}
45-
Spacer()
46-
VStack(alignment: .trailing, spacing: 6) {
71+
.frame(maxWidth: .infinity)
72+
if !isRenaming {
4773
Text(itemCountText)
4874
.font(.footnote)
75+
// // Dot indicator at top right
76+
VStack(alignment: .trailing, spacing: 0) {
77+
Circle()
78+
.fill(isActive ? Color("ActiveContextColor") : Color.clear)
79+
.frame(width: 8, height: 8)
80+
Spacer()
81+
}
82+
.frame(height: 32) // Adjust to match row/icon height
4983
}
50-
// // Dot indicator at top right
51-
VStack(alignment: .trailing, spacing: 0) {
52-
Circle()
53-
.fill(isActive ? Color("ActiveContextColor") : Color.clear)
54-
.frame(width: 8, height: 8)
55-
Spacer()
56-
}
57-
.frame(height: 32) // Adjust to match row/icon height
5884
}
5985
.padding(8)
6086
.frame(maxWidth: .infinity, alignment: .leading)
@@ -68,6 +94,10 @@ struct ContextCardView: View {
6894
contextManager.closeContext(contextID: context.id)
6995
}
7096
.disabled(!contextManager.isActive(contextID: context.id))
97+
Button("Rename") {
98+
draftName = context.name
99+
isRenaming = true
100+
}
71101
Button("Change Icon...") {
72102
showIconSelector = true
73103
}
@@ -117,11 +147,31 @@ struct ContextCardView: View {
117147
cardScale = 1.0
118148
}
119149
}
150+
.onChange(of: isRenaming) { _, newValue in
151+
if newValue {
152+
// ensure field gets focus when entering rename mode
153+
DispatchQueue.main.async { renameFieldFocused = true }
154+
}
155+
}
120156
.contentShape(Rectangle())
121157
.tag(context.id as UUID?)
122158
.help(analyticsTooltip(for: context.id))
123159
}
124160

161+
private func commitRename() {
162+
let trimmed = draftName.trimmingCharacters(in: .whitespacesAndNewlines)
163+
if !trimmed.isEmpty && trimmed != context.name {
164+
context.name = trimmed
165+
contextManager.saveContexts()
166+
}
167+
isRenaming = false
168+
}
169+
170+
private func cancelRename() {
171+
draftName = context.name
172+
isRenaming = false
173+
}
174+
125175
private func analyticsTooltip(for contextID: UUID) -> String {
126176
let analyticsManager = contextManager.analyticsManager
127177
let openCount = analyticsManager.getOpenCount(for: contextID)

0 commit comments

Comments
 (0)