@@ -1063,9 +1063,9 @@ private var headerBar: some View { DragHost { headerContent }.zIndex(2) }
10631063 if isDroppingIntoManual {
10641064 RoundedRectangle ( cornerRadius: 6 )
10651065 . fill ( Color . accentColor. opacity ( 0.08 ) )
1066+ RoundedRectangle ( cornerRadius: 6 )
1067+ . strokeBorder ( Color . accentColor, style: StrokeStyle ( lineWidth: 2 , dash: [ 6 , 4 ] ) )
10661068 }
1067- RoundedRectangle ( cornerRadius: 6 )
1068- . strokeBorder ( isDroppingIntoManual ? Color . accentColor : Color . gray. opacity ( 0.2 ) , style: StrokeStyle ( lineWidth: isDroppingIntoManual ? 2 : 1 , dash: isDroppingIntoManual ? [ 6 , 4 ] : [ ] ) )
10691069 }
10701070 )
10711071 . onDrop ( of: [ UTType . fileURL] , isTargeted: $isDroppingIntoManual) { providers in
@@ -1590,6 +1590,8 @@ private extension PanelView {
15901590 var highlightIDs : Set < UUID >
15911591 var activeID : UUID ?
15921592 var searchQuery : String ?
1593+ @State private var hoveringUser : Bool = false
1594+ @FocusState private var editFocused : Bool
15931595
15941596 var body : some View {
15951597 VStack ( alignment: . leading, spacing: 8 ) {
@@ -1600,12 +1602,59 @@ private extension PanelView {
16001602 . padding ( . horizontal, 8 )
16011603 Spacer ( )
16021604 if let u = item. user {
1603- Text ( TranscriptListView . previewText ( u. text) )
1604- . font ( . callout)
1605- . foregroundStyle ( . secondary)
1606- . lineLimit ( 2 )
1607- . multilineTextAlignment ( . trailing)
1608- . frame ( maxWidth: 420 , alignment: . trailing)
1605+ let isLatestUser = ( model. conversation. lastIndex ( where: { $0. role == . user } ) != nil ) && ( model. conversation. last ( where: { $0. role == . user } ) ? . id == u. id)
1606+ if model. editingLatestUser && isLatestUser {
1607+ // Inline editor for the latest user message
1608+ VStack ( alignment: . trailing, spacing: 6 ) {
1609+ // Keep provided style; enforce readable text color via NSView-backed editor
1610+ ManualTextView ( text: $model. editingLatestUserText, isFocused: $editFocused, fontSize: model. uiBodyPointSize, textColor: NSColor . white. withAlphaComponent ( 0.85 ) )
1611+ . background ( . ultraThinMaterial. opacity ( 0.2 ) )
1612+ . frame ( minHeight: 80 , maxHeight: 120 )
1613+ . overlay ( RoundedRectangle ( cornerRadius: 6 ) . stroke ( Color . gray. opacity ( 0.2 ) ) )
1614+ . frame ( maxWidth: 560 )
1615+ . onAppear { editFocused = true }
1616+ HStack ( spacing: 8 ) {
1617+ Text ( " ⌘↩: save & regenerate • ESC: cancel " ) . font ( . caption2) . foregroundStyle ( . secondary)
1618+ }
1619+ }
1620+ . padding ( 8 )
1621+ . frame ( maxWidth: 560 , alignment: . trailing)
1622+ . background (
1623+ Group {
1624+ // Cmd+Enter: commit and retry generation ONLY in this context
1625+ // This is intentionally scoped here to avoid clashing with the
1626+ // global manual input handler for Cmd+Enter.
1627+ Button ( " " ) { Task { await model. commitEditLatestUserAndRegenerate ( ) } }
1628+ . keyboardShortcut ( . return, modifiers: [ . command] )
1629+ . frame ( width: 0 , height: 0 )
1630+ . opacity ( 0 )
1631+ . allowsHitTesting ( false )
1632+ // ESC: cancel edit without persisting
1633+ Button ( " " ) { model. cancelEditLatestUser ( ) }
1634+ . keyboardShortcut ( . escape, modifiers: [ ] )
1635+ . frame ( width: 0 , height: 0 )
1636+ . opacity ( 0 )
1637+ . allowsHitTesting ( false )
1638+ }
1639+ )
1640+ } else {
1641+ HStack ( alignment: . firstTextBaseline, spacing: 6 ) {
1642+ Text ( TranscriptListView . previewText ( u. text) )
1643+ . font ( . callout)
1644+ . foregroundStyle ( . secondary)
1645+ . lineLimit ( 2 )
1646+ . multilineTextAlignment ( . trailing)
1647+ . frame ( maxWidth: 420 , alignment: . trailing)
1648+ . contentShape ( Rectangle ( ) )
1649+ . onHover { hovering in hoveringUser = hovering }
1650+ . onTapGesture { if isLatestUser { model. beginEditLatestUser ( ) } }
1651+ if isLatestUser {
1652+ Button ( action: { model. beginEditLatestUser ( ) } ) { Image ( systemName: " pencil " ) . font ( . caption) }
1653+ . buttonStyle ( . plain)
1654+ . opacity ( hoveringUser ? 1 : 0 )
1655+ }
1656+ }
1657+ }
16091658 }
16101659 }
16111660
@@ -2830,6 +2879,7 @@ private struct ManualTextView: NSViewRepresentable {
28302879 @Binding var text : String
28312880 var isFocused : FocusState < Bool > . Binding
28322881 var fontSize : Double = 13.0
2882+ var textColor : NSColor ? = nil
28332883
28342884 final class Coordinator : NSObject , NSTextViewDelegate {
28352885 var parent : ManualTextView
@@ -2856,6 +2906,7 @@ private struct ManualTextView: NSViewRepresentable {
28562906 tv. isRichText = false
28572907 tv. isContinuousSpellCheckingEnabled = false
28582908 tv. font = . systemFont( ofSize: CGFloat ( fontSize) )
2909+ if let c = textColor { tv. textColor = c; tv. insertionPointColor = c }
28592910 tv. string = text
28602911 tv. delegate = context. coordinator
28612912 tv. textContainerInset = NSSize ( width: 4 , height: 4 )
@@ -2874,6 +2925,7 @@ private struct ManualTextView: NSViewRepresentable {
28742925 tv. scrollRangeToVisible ( tv. selectedRange ( ) )
28752926 }
28762927 tv. font = . systemFont( ofSize: CGFloat ( fontSize) )
2928+ if let c = textColor { tv. textColor = c; tv. insertionPointColor = c }
28772929 if isFocused. wrappedValue, let window = nsView. window, window. firstResponder !== tv {
28782930 window. makeFirstResponder ( tv)
28792931 tv. scrollRangeToVisible ( tv. selectedRange ( ) )
0 commit comments