@@ -15,6 +15,17 @@ struct QuickPostView: View {
1515 _viewModel = StateObject ( wrappedValue: QuickPostViewModel . shared)
1616 }
1717
18+ private var textAreaHeight : CGFloat {
19+ let font = NSFont ( name: " Menlo " , size: 14.0 ) ?? NSFont . systemFont ( ofSize: 14.0 )
20+ let lineHeight = ceil ( font. ascender - font. descender + font. leading) + 4 // 4 = lineSpacing
21+ let insets : CGFloat = 8 // textContainerInset.height * 2
22+ let padding : CGFloat = 20 // SwiftUI top + bottom padding
23+ let minHeight = lineHeight * 5 + insets + padding
24+ let maxHeight = lineHeight * 12 + insets + padding
25+ let needed = viewModel. textContentHeight + padding
26+ return max ( minHeight, min ( maxHeight, needed) )
27+ }
28+
1829 var body : some View {
1930 VStack ( spacing: 0 ) {
2031 // Upper: Avatar | Text Entry
@@ -41,7 +52,7 @@ struct QuickPostView: View {
4152 . padding ( . bottom, 10 )
4253 . padding ( . leading, 0 )
4354 . padding ( . trailing, 10 )
44- . frame ( height: 160 )
55+ . frame ( height: textAreaHeight )
4556 . onChange ( of: viewModel. content) { newValue in
4657 handleAutocomplete ( oldValue: previousContent, newValue: newValue)
4758 previousContent = newValue
@@ -104,8 +115,12 @@ struct QuickPostView: View {
104115 }
105116 Spacer ( )
106117 Button ( role: . cancel) {
107- viewModel. cleanup ( )
108- dismiss ( )
118+ if viewModel. hasContent {
119+ viewModel. showDiscardAlert = true
120+ } else {
121+ viewModel. cleanup ( )
122+ dismiss ( )
123+ }
109124 } label: {
110125 Text ( " Cancel " )
111126 . frame ( minWidth: 50 )
@@ -133,16 +148,25 @@ struct QuickPostView: View {
133148 } . padding ( 10 )
134149 . background ( Color ( NSColor . windowBackgroundColor) )
135150 } . frame ( width: 500 , height: sheetHeight ( ) )
151+ . alert ( " Discard Post? " , isPresented: $viewModel. showDiscardAlert) {
152+ Button ( " Discard " , role: . destructive) {
153+ viewModel. cleanup ( )
154+ dismiss ( )
155+ }
156+ Button ( " Keep Editing " , role: . cancel) { }
157+ } message: {
158+ Text ( " Your post will be lost if you discard it. " )
159+ }
136160 }
137161
138162 private func sheetHeight( ) -> CGFloat {
139163 if viewModel. fileURLs. count > 0 {
140164 if let _ = viewModel. audioURL {
141- return 310 + 25
165+ return textAreaHeight + 175
142166 }
143- return 310
167+ return textAreaHeight + 150
144168 }
145- return 200
169+ return textAreaHeight + 40
146170 }
147171
148172 private func handleAutocomplete( oldValue: String , newValue: String ) {
@@ -161,8 +185,9 @@ struct QuickPostView: View {
161185 let previousLine = lines [ lines. count - 2 ]
162186 let trimmedPrevious = previousLine. trimmingCharacters ( in: . whitespaces)
163187
164- // Check if previous line is an empty list item (including todo lists)
165- if trimmedPrevious == " * " || trimmedPrevious == " - " || trimmedPrevious == " - [ ] " || trimmedPrevious == " - [x] " {
188+ // Check if previous line is an empty list item (including todo lists and numbered lists)
189+ let isEmptyNumberedItem = trimmedPrevious. range ( of: #"^\d+\.$"# , options: . regularExpression) != nil
190+ if trimmedPrevious == " * " || trimmedPrevious == " - " || trimmedPrevious == " - [ ] " || trimmedPrevious == " - [x] " || isEmptyNumberedItem {
166191 // Remove the empty list marker from previous line
167192 var updatedLines = lines
168193 updatedLines [ lines. count - 2 ] = " "
@@ -189,6 +214,15 @@ struct QuickPostView: View {
189214 viewModel. content = newValue + " - "
190215 }
191216 }
217+ // Check if previous line starts with a numbered list (e.g., "1. ")
218+ else if let match = trimmedPrevious. range ( of: #"^(\d+)\. "# , options: . regularExpression) {
219+ if newValue. hasSuffix ( " \n " ) {
220+ let numberStr = trimmedPrevious [ match] . dropLast ( 2 ) // drop ". "
221+ if let number = Int ( numberStr) {
222+ viewModel. content = newValue + " \( number + 1 ) . "
223+ }
224+ }
225+ }
192226 }
193227
194228 @ViewBuilder
@@ -432,6 +466,15 @@ struct QuickPostTextView: NSViewRepresentable {
432466 return
433467 }
434468 parent. text = textView. string
469+ updateContentHeight ( textView)
470+ }
471+
472+ private func updateContentHeight( _ textView: NSTextView ) {
473+ guard let layoutManager = textView. layoutManager,
474+ let textContainer = textView. textContainer else { return }
475+ layoutManager. ensureLayout ( for: textContainer)
476+ let usedRect = layoutManager. usedRect ( for: textContainer)
477+ parent. viewModel. textContentHeight = usedRect. height + textView. textContainerInset. height * 2
435478 }
436479
437480 func textDidEndEditing( _ notification: Notification ) {
@@ -542,6 +585,15 @@ final class QuickPostTextEditorContainer: NSView {
542585 textView. string = text
543586 let end = ( text as NSString ) . length
544587 textView. setSelectedRange ( NSRange ( location: end, length: 0 ) )
588+ updateContentHeight ( )
589+ }
590+
591+ private func updateContentHeight( ) {
592+ guard let layoutManager = textView. layoutManager,
593+ let textContainer = textView. textContainer else { return }
594+ layoutManager. ensureLayout ( for: textContainer)
595+ let usedRect = layoutManager. usedRect ( for: textContainer)
596+ viewModel. textContentHeight = usedRect. height + textView. textContainerInset. height * 2
545597 }
546598}
547599
0 commit comments