Something went wrong. Try again.
small gleam coding and persistent agent daemon with a detachable cli and a canvas ui forked from okami.mom/albedo
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121package tui
import ( "fmt" "strconv" "strings"
"github.com/charmbracelet/x/ansi")
// chipRows is the height of the strip of cards above the composer: the// thumbnail area and a border above and below it.const chipRows = thumbRows + 2
// chip is one card of the strip: a title naming its marker, up to thumbRows// lines of content, and a caption.type chip struct { title, caption string content []string}
// chips are the attachments the composer still marks, in reading order.func (m ChatModel) chips() []chip { var chips []chip seen := make(map[string]bool) for _, match := range anyMarker.FindAllString(m.TextArea.Value(), -1) { if seen[match] { continue } seen[match] = true if found := imageMarker.FindStringSubmatch(match); found != nil { n, _ := strconv.Atoi(found[1]) if image, ok := m.Images.byNumber[n]; ok { chips = append(chips, m.imageChip(n, image)) } } else if found := pasteMarker.FindStringSubmatch(match); found != nil { n, _ := strconv.Atoi(found[1]) if text, ok := m.Pastes.byNumber[n]; ok { chips = append(chips, m.pasteChip(n, text)) } } } return chips}
func (m ChatModel) imageChip(n int, image pastedImage) chip { content := []string{"", DefaultStyles.Faint.Render("image")} if image.thumb != nil { content = placeholderRows(*image.thumb) } return chip{ title: "image #" + strconv.Itoa(n), caption: fmt.Sprintf("%d×%d", image.Width, image.Height), content: content, }}
func (m ChatModel) pasteChip(n int, text string) chip { lines := strings.Split(text, "\n") caption := "+" + strconv.Itoa(len(lines)) + " lines" if len(lines) <= collapseLines { caption = strconv.Itoa(len([]rune(text))) + " chars" } content := make([]string, 0, thumbRows) for _, line := range lines[:min(len(lines), thumbRows)] { line = ansi.Truncate(strings.ReplaceAll(line, "\t", " "), thumbCols, "") // padded to the full width, so the card keeps the lines left-aligned content = append(content, DefaultStyles.Faint.Render(line+strings.Repeat(" ", thumbCols-ansi.StringWidth(line)))) } return chip{title: "paste #" + strconv.Itoa(n), caption: caption, content: content}}
// chipStrip lays the chips out side by side within width, chipRows rows// high, or returns nil when there are none. Cards that do not fit are// counted at the end instead.func (m ChatModel) chipStrip(width int) []string { chips := m.chips() if len(chips) == 0 { return nil } const cardWidth = thumbCols + 2 shown := min(len(chips), (width+1)/(cardWidth+1)) if shown < len(chips) && shown*(cardWidth+1)+len("+99") > width { shown-- // room for the count of the rest } rows := make([]string, chipRows) for i, c := range chips[:max(0, shown)] { for r, line := range m.card(c) { rows[r] += strings.Repeat(" ", min(i, 1)) + line } } if hidden := len(chips) - max(0, shown); hidden > 0 { rows[chipRows/2] += DefaultStyles.Faint.Render(" +" + strconv.Itoa(hidden)) } return rows}
// card draws c in a rounded border thumbCols+2 cells wide, its content// centred, its title on the top edge and its caption on the bottom one.func (m ChatModel) card(c chip) []string { edge := func(left, label, right string) string { label = ansi.Truncate(label, thumbCols-2, "…") fill := thumbCols - ansi.StringWidth(label) - 2 return DefaultStyles.Decor.Render(left+strings.Repeat("─", fill-fill/2)+" ") + DefaultStyles.Muted.Render(label) + DefaultStyles.Decor.Render(" "+strings.Repeat("─", fill/2)+right) } side := DefaultStyles.Decor.Render("│") rows := []string{edge("╭", c.title, "╮")} top := (thumbRows - len(c.content)) / 2 for r := range thumbRows { line := "" if r >= top && r-top < len(c.content) { line = c.content[r-top] } pad := thumbCols - ansi.StringWidth(line) rows = append(rows, side+strings.Repeat(" ", pad/2)+line+strings.Repeat(" ", pad-pad/2)+side) } return append(rows, edge("╰", c.caption, "╯"))}