okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
673fd0d
main
1// Streaming Markdown must render every partial reply exactly as its settled 2// form: the daemon e2e sees committed transcript text only, never the live 3// view, so it cannot sample the streaming boundaries this sweep does. 4package tui 5 6import ( 7"strings" 8"testing" 9) 10 11var markdownSamples = []string { 12"shown as source:\n\n```md\n[x]: https://example.com\n<!-- note -->\n```\n\nsee [x] and <pre>\n\nafter" , 13`fixed in ` + "`" + `25424ba` + "`" + ` "keep row backgrounds across lip gloss v2 resets". lipgloss v2 closes styled text with the short reset ` + "`" + `\x1b[m` + "`" + `, where v1 used ` + "`" + `\x1b[0m` + "`" + `. 14 15i also measured cpu over 45 seconds while you were testing: 16 17| pid | binary | cpu | 18|---|---|---| 19| 84082 | new (` + "`" + `cli/bin/albedo` + "`" + `, this session) | ~4% | 20| 58609 | old nix one | ~13.5% | 21 22don't read too much into the new binary's number.` , 23`**what port-chat did:** 24- **paste:** ` + "`" + `chat.go` + "`" + ` never had a special case for paste. in v1, pasted text arrived as a key event. 25- **mouse:** it's one ` + "`" + `case tea.MouseMsg:` + "`" + ` with an inner switch. 26 27**things i'll change in my own pass:** 28- remove a porting-only comment. 29- run ` + "`" + `go mod tidy` + "`" + `. 30 31once port-forms reports back i'll do the full review.` , 32`the migration is big but mostly mechanical. the main ones i expect: 33- ` + "`" + `tea.KeyMsg` + "`" + ` with ` + "`" + `.Type` + "`" + `/` + "`" + `.Runes` + "`" + ` becomes ` + "`" + `tea.KeyPressMsg` + "`" + `. 34- mouse messages get split up. 35 361. use a static cursor there too. 372. let the terminal blink its own cursor. 38 39` + "`" + `` + "`" + `` + "`" + `sh 40ps -o %cpu= -p $(pgrep -n albedo) 41` + "`" + `` + "`" + `` + "`" + ` 42 43run it a few times. 44 45### heading three 46text under heading 47 48> a quote 49> continues 50 51after quote 52 53indented code 54 55final *line* here 56` , 57`- item 58 59continued inside item 60 61- second item 62 63next para 64 651. one 66 67nested para 68 692. two 70 71~~~ 72tilde fence 73 74still fence 75~~~ 76 77done` , 78`intro 79 80` + "`" + `` + "`" + `` + "`" + `go 81func a() { 82 83return 84} 85` + "`" + `` + "`" + `` + "`" + ` 86## after code heading 87para 88 89--- 90 91tail` , 92`the roots are 93 94$$ 95x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} 96 97\text{for } a \neq 0 98$$ 99and inline $e^{i\pi} + 1 = 0$, but $5 and $HOME/$USER stay text. 100 101- a sum \(\sum_{i=1}^{n} i\) in a list 102 103\[ 104\sum_{i=1}^{n} i = \frac{n(n+1)}{2} 105\] 106 107> **bold $\alpha$** in a quote 108 109done` , 110} 111 112// Block by block, every prefix renders exactly as the whole prefix does, and 113// no block boundary falls inside a code fence: a split fence renders the same 114// text but re-renders the whole tail on every streaming chunk. 115func TestMarkdownRendersTheSameBlockByBlock (t * testing.T ) { 116for _ ,width := range []int {40 ,98 } { 117for i ,sample := range markdownSamples { 118blocks := markdownBlocks (sample ) 119if len (blocks )< 2 { 120t .Fatalf ("sample %d is one block, so it tests nothing" ,i ) 121 } 122fence := "" 123for _ ,block := range blocks { 124if fence != "" { 125t .Fatalf ("sample %d splits a code fence between blocks" ,i ) 126 } 127for line := range strings .SplitSeq (block ,"\n" ) { 128fence = fenceAfter (fence ,line ) 129 } 130 } 131for end := 1 ;end <= len (sample );end += 7 { 132text := sample [:end ] 133if got ,want := RenderMarkdownAnsi (text ,width ),strings .Trim (renderMarkdown (text ,width )," \n" );got != want { 134t .Fatalf ("sample %d at %d, width %d:\n got %q\nwant %q" ,i ,end ,width ,got ,want ) 135 } 136 } 137 } 138 } 139}