okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
3922552
main
1// Markdown table bounds and transcript copy selection live in the terminal 2// rendering path: the daemon e2e sees only committed text, never rendered 3// rows, and no harness can drive a mouse selection, so these regressions are 4// invisible outside the TUI. 5package tui 6 7import ( 8"strings" 9"testing" 10 11"albedo/cli/internal/daemon" 12"github.com/charmbracelet/x/ansi" 13) 14 15const tableTranscript = "| Tool | Logged median | Warm in-kernel benchmark |\n" + 16"|:---|---:|---:|\n" + 17"| `files.read` | 3 ms | 0.07 ms |\n" + 18"| `files.paths` | too few isolated calls | 12.7 ms |" 19 20// Tables render inside the transcript's width: every cell that fits survives, 21// a narrow table truncates its columns rather than its rows or its syntax, 22// and escaped pipes and fenced literal tables are never parsed as tables. 23func TestMarkdownTablesRenderWithinTheirBounds (t * testing.T ) { 24for _ ,tc := range []struct { 25name string 26text string 27absent string 28want []string 29width int 30 }{ 31 {name :"keeps every column it fits" ,text :tableTranscript ,width :80 , 32want : []string {"Tool" ,"files.read" ,"files.paths" ,"12.7 ms" },absent :"|:---" }, 33 {name :"narrow truncates columns, not rows or the syntax" ,text :tableTranscript ,width :38 , 34want : []string {"Tool" ,"files.read" ,"files.path" ,"12.7 ms" },absent :"|:---" }, 35 {name :"escaped pipes and fenced rows stay literal" , 36text :"Intro\n\n| Name | Value |\n| --- | --- |\n| a\\|b | `x\\|y` |\n\n" + 37"```text\n| literal | row |\n| --- | --- |\n```\nOutro" , 38width :72 , 39want : []string {"Intro" ,"a|b" ,"x|y" ,"| literal | row |" ,"| --- | --- |" ,"Outro" },absent :"" }, 40 } { 41got := ansi .Strip (RenderMarkdownAnsi (tc .text ,tc .width )) 42for _ ,value := range tc .want { 43if ! strings .Contains (got ,value ) { 44t .Fatalf ("%s: width %d lost %q:\n%s" ,tc .name ,tc .width ,value ,got ) 45 } 46 } 47if tc .absent != "" && strings .Contains (got ,tc .absent ) { 48t .Fatalf ("%s: width %d exposed %q:\n%s" ,tc .name ,tc .width ,tc .absent ,got ) 49 } 50for row := range strings .SplitSeq (got ,"\n" ) { 51if w := ansi .StringWidth (row );w > tc .width { 52t .Fatalf ("%s: width %d produced a %d-cell row: %q" ,tc .name ,tc .width ,w ,row ) 53 } 54 } 55 } 56} 57 58func TestSelectionSkipsTheRail (t * testing.T ) { 59lines := []string {"│ you" ,"│ can you list the tools" ,"" ,"│ albedo" } 60sel := Selection {Anchor :Point {Row :0 ,Col :0 },Head :Point {Row :3 ,Col :8 },Gutter :railWidth } 61if got ,want := SelectedText (lines ,sel ),"you\ncan you list the tools\n\nalbedo" ;got != want { 62t .Fatalf ("copied %q, want %q" ,got ,want ) 63 } 64for _ ,row := range HighlightSelection (lines ,sel ) { 65if strings .HasPrefix (row ,"\x1b[7m" ) { 66t .Fatalf ("highlight covers the rail: %q" ,row ) 67 } 68 } 69} 70 71// A copy is the text: no padding out to the viewport's width or height, no 72// chrome, no code block frame or quote bars, and wrapped rows joined back 73// into their lines. 74func TestSelectionCopiesOnlyText (t * testing.T ) { 75m := newTestChatModel (t ,& daemon.Session {ID :"s" }) 76m .SetSize (60 ,70 ) 77m .appendSettledEntry (HistoryEntry {Kind :EntryUser ,Text :"why does the thing break when i do the other thing, it is really annoying" }) 78m .appendSettledEntry (HistoryEntry {Kind :EntryTool ,ToolName :"bash" }) 79m .appendSettledEntry (HistoryEntry {Kind :EntryAssistant ,Text :"a paragraph that is long enough that it will **definitely** wrap around the width.\n" + 80"hard break\n\nsee https://example.com/a/really/long/path/without/any/spaces/in/it/at/all\n\n" + 81"- an item that also goes on for a while so it wraps onto a second row\n\n" + 82"```go\nfmt.Println(\"a line of code that is longer than the width of the chat\")\n```\n\n" + 83"> quoted text that goes on long enough to wrap around the width\n> > twice\n\n" + 84"done." }) 85m .appendSettledEntry (HistoryEntry {Kind :EntryTurnEnd ,Mood :moodDone ,ElapsedMs :5000 ,Tools :1 }) 86m .refreshViewportContent () 87lines := strings .Split (m .Viewport .View (),"\n" ) 88all := Selection {Anchor :Point {Row :0 ,Col :0 },Head :Point {Row :len (lines )- 1 ,Col :60 },Gutter :railWidth } 89want := "why does the thing break when i do the other thing, it is really annoying\n\n" + 90"a paragraph that is long enough that it will definitely wrap around the width.\n" + 91"hard break\n\nsee https://example.com/a/really/long/path/without/any/spaces/in/it/at/all\n\n" + 92"• an item that also goes on for a while so it wraps onto a second row\n\n" + 93"fmt.Println(\"a line of code that is longer than the width of the chat\")\n\n" + 94"quoted text that goes on long enough to wrap around the width\n\ntwice\n\n" + 95"done." 96if got := SelectedText (lines ,all );got != want { 97t .Fatalf ("copied %q, want %q" ,got ,want ) 98 } 99// chrome copies when it is all you selected 100for i ,line := range lines { 101if strings .Contains (ansi .Strip (line ),"bash" ) { 102row := Selection {Anchor :Point {Row :i ,Col :0 },Head :Point {Row :i ,Col :60 },Gutter :railWidth } 103if got := SelectedText (lines ,row );got != "ran bash" { 104t .Fatalf ("copied the tool row as %q" ,got ) 105 } 106 } 107 } 108}