summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--packages/tui/internal/components/textarea/textarea.go195
1 files changed, 187 insertions, 8 deletions
diff --git a/packages/tui/internal/components/textarea/textarea.go b/packages/tui/internal/components/textarea/textarea.go
index 60e72e09a..a56f2b788 100644
--- a/packages/tui/internal/components/textarea/textarea.go
+++ b/packages/tui/internal/components/textarea/textarea.go
@@ -2045,6 +2045,109 @@ func itemWidth(item any) int {
return 0
}
+// forceWrapAttachment splits an attachment's display text across multiple lines
+func forceWrapAttachment(att *attachment.Attachment, width int) [][]any {
+ if width <= 0 {
+ return [][]any{{att}}
+ }
+
+ display := att.Display
+ displayRunes := []rune(display)
+
+ if len(displayRunes) <= width {
+ return [][]any{{att}}
+ }
+
+ var lines [][]any
+ start := 0
+
+ for start < len(displayRunes) {
+ // Calculate how many runes fit in this line
+ end := start + width
+ if end > len(displayRunes) {
+ end = len(displayRunes)
+ }
+
+ // Create a wrapped attachment for this segment
+ wrappedAtt := &attachment.Attachment{
+ ID: att.ID,
+ Type: att.Type,
+ Display: string(displayRunes[start:end]),
+ URL: att.URL,
+ Filename: att.Filename,
+ MediaType: att.MediaType,
+ Source: att.Source,
+ }
+
+ lines = append(lines, []any{wrappedAtt})
+ start = end
+ }
+
+ return lines
+}
+
+// forceWrapWord splits a word that's too long to fit within the given width
+func forceWrapWord(word []any, width int) [][]any {
+ if width <= 0 || len(word) == 0 {
+ return [][]any{word}
+ }
+
+ var lines [][]any
+ currentLine := []any{}
+ currentWidth := 0
+
+ for _, item := range word {
+ if att, ok := item.(*attachment.Attachment); ok {
+ // Handle attachment that might be too wide
+ attWidth := uniseg.StringWidth(att.Display)
+
+ // If the attachment display is too wide, split it
+ if attWidth > width {
+ // Finish current line if it has content
+ if len(currentLine) > 0 {
+ lines = append(lines, currentLine)
+ currentLine = []any{}
+ currentWidth = 0
+ }
+
+ // Split the attachment display across multiple lines
+ wrappedAttachment := forceWrapAttachment(att, width)
+ lines = append(lines, wrappedAttachment...)
+ continue
+ }
+
+ // If adding this attachment would exceed the width, start a new line
+ if currentWidth+attWidth > width && len(currentLine) > 0 {
+ lines = append(lines, currentLine)
+ currentLine = []any{}
+ currentWidth = 0
+ }
+
+ currentLine = append(currentLine, item)
+ currentWidth += attWidth
+ } else if r, ok := item.(rune); ok {
+ itemWidth := rw.RuneWidth(r)
+
+ // If adding this rune would exceed the width, start a new line
+ if currentWidth+itemWidth > width && len(currentLine) > 0 {
+ lines = append(lines, currentLine)
+ currentLine = []any{}
+ currentWidth = 0
+ }
+
+ currentLine = append(currentLine, item)
+ currentWidth += itemWidth
+ }
+ }
+
+ // Add the last line if it has content
+ if len(currentLine) > 0 {
+ lines = append(lines, currentLine)
+ }
+
+ return lines
+}
+
func wrapInterfaces(content []any, width int) [][]any {
if width <= 0 {
return [][]any{content}
@@ -2076,11 +2179,49 @@ func wrapInterfaces(content []any, width int) [][]any {
if !inSpaces {
// End of a word
if lineW > 0 && lineW+wordW > width {
- lines = append(lines, word)
- lineW = wordW
+ // If the word itself is too long to fit on a line, force-wrap it
+ if wordW > width {
+ wrappedLines := forceWrapWord(word, width)
+ lines = append(lines, wrappedLines...)
+ // Calculate width of the last wrapped line
+ lastLine := wrappedLines[len(wrappedLines)-1]
+ lineW = 0
+ for _, item := range lastLine {
+ if r, ok := item.(rune); ok {
+ lineW += rw.RuneWidth(r)
+ } else if att, ok := item.(*attachment.Attachment); ok {
+ lineW += uniseg.StringWidth(att.Display)
+ }
+ }
+ } else {
+ lines = append(lines, word)
+ lineW = wordW
+ }
} else {
- lines[len(lines)-1] = append(lines[len(lines)-1], word...)
- lineW += wordW
+ // Check if the word needs to be force-wrapped even when it fits on the current line
+ if wordW > width {
+ currentLine := lines[len(lines)-1]
+ wrappedWord := forceWrapWord(word, width-lineW)
+ if len(wrappedWord) > 0 {
+ lines[len(lines)-1] = append(currentLine, wrappedWord[0]...)
+ for i := 1; i < len(wrappedWord); i++ {
+ lines = append(lines, wrappedWord[i])
+ }
+ // Calculate width of the last wrapped line
+ lastLine := wrappedWord[len(wrappedWord)-1]
+ lineW = 0
+ for _, item := range lastLine {
+ if r, ok := item.(rune); ok {
+ lineW += rw.RuneWidth(r)
+ } else if att, ok := item.(*attachment.Attachment); ok {
+ lineW += uniseg.StringWidth(att.Display)
+ }
+ }
+ }
+ } else {
+ lines[len(lines)-1] = append(lines[len(lines)-1], word...)
+ lineW += wordW
+ }
}
word = nil
wordW = 0
@@ -2110,11 +2251,49 @@ func wrapInterfaces(content []any, width int) [][]any {
// Handle any remaining word/spaces at the end of the content.
if wordW > 0 {
if lineW > 0 && lineW+wordW > width {
- lines = append(lines, word)
- lineW = wordW
+ // If the word itself is too long to fit on a line, force-wrap it
+ if wordW > width {
+ wrappedLines := forceWrapWord(word, width)
+ lines = append(lines, wrappedLines...)
+ // Calculate width of the last wrapped line
+ lastLine := wrappedLines[len(wrappedLines)-1]
+ lineW = 0
+ for _, item := range lastLine {
+ if r, ok := item.(rune); ok {
+ lineW += rw.RuneWidth(r)
+ } else if att, ok := item.(*attachment.Attachment); ok {
+ lineW += uniseg.StringWidth(att.Display)
+ }
+ }
+ } else {
+ lines = append(lines, word)
+ lineW = wordW
+ }
} else {
- lines[len(lines)-1] = append(lines[len(lines)-1], word...)
- lineW += wordW
+ // Check if the word needs to be force-wrapped even when it fits on the current line
+ if wordW > width {
+ currentLine := lines[len(lines)-1]
+ wrappedWord := forceWrapWord(word, width-lineW)
+ if len(wrappedWord) > 0 {
+ lines[len(lines)-1] = append(currentLine, wrappedWord[0]...)
+ for i := 1; i < len(wrappedWord); i++ {
+ lines = append(lines, wrappedWord[i])
+ }
+ // Calculate width of the last wrapped line
+ lastLine := wrappedWord[len(wrappedWord)-1]
+ lineW = 0
+ for _, item := range lastLine {
+ if r, ok := item.(rune); ok {
+ lineW += rw.RuneWidth(r)
+ } else if att, ok := item.(*attachment.Attachment); ok {
+ lineW += uniseg.StringWidth(att.Display)
+ }
+ }
+ }
+ } else {
+ lines[len(lines)-1] = append(lines[len(lines)-1], word...)
+ lineW += wordW
+ }
}
}
if spaceW > 0 {