Skip to content

Commit 8de7b87

Browse files
committed
feat: add saveCanvasToText primitive
1 parent 95ab6c6 commit 8de7b87

3 files changed

Lines changed: 49 additions & 0 deletions

File tree

internal/canvas/canvas.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ type Canvas struct {
8787
isFilling bool
8888
IsLooping bool
8989
save bool
90+
saveText bool
9091
autoResize bool
9192
disabled bool
9293
}
@@ -180,6 +181,10 @@ func (c *Canvas) render() {
180181
c.exportCanvasToPNG(content)
181182
c.save = false
182183
}
184+
if c.saveText {
185+
c.exportCanvasToText(content)
186+
c.saveText = false
187+
}
183188
if c.frames != nil {
184189
c.recordFrame(content)
185190
}

internal/canvas/canvas_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package canvas
33
import (
44
"fmt"
55
"math"
6+
"os"
7+
"path/filepath"
8+
"strings"
69
"testing"
710
"time"
811
)
@@ -303,3 +306,26 @@ func TestBalancedPushDoesNotWarn(t *testing.T) {
303306
t.Errorf("debugBuffer = %v, want empty", c.debugBuffer)
304307
}
305308
}
309+
310+
// The exported text must contain the drawn characters but none of the
311+
// ANSI color escapes used to render them.
312+
func TestSaveCanvasToText(t *testing.T) {
313+
c := mockCanvas(5, 5)
314+
c.Rect(0, 0, 3, 3)
315+
316+
filename := filepath.Join(t.TempDir(), "out.txt")
317+
c.saveText = true
318+
c.saveFilename = filename
319+
c.render()
320+
321+
got, err := os.ReadFile(filename)
322+
if err != nil {
323+
t.Fatalf("can't read exported text file: %v", err)
324+
}
325+
if strings.Contains(string(got), "\x1b") {
326+
t.Errorf("exported text contains an ANSI escape: %q", got)
327+
}
328+
if !strings.Contains(string(got), c.strokeText) {
329+
t.Errorf("exported text = %q, want it to contain the drawn stroke %q", got, c.strokeText)
330+
}
331+
}

internal/canvas/capture.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import (
99
"image/png"
1010
"math"
1111
"os"
12+
"strings"
1213

1314
"github.com/charmbracelet/log"
15+
"github.com/charmbracelet/x/ansi"
1416

1517
ansitoimage "github.com/xaviergodart/go-ansi-to-image"
1618
)
@@ -22,6 +24,14 @@ func (c *Canvas) SaveCanvasToPNG(filename string) {
2224
c.Redraw()
2325
}
2426

27+
// SaveCanvasToText exports the canvas to a plain text file, dropping
28+
// all color information.
29+
func (c *Canvas) SaveCanvasToText(filename string) {
30+
c.saveText = true
31+
c.saveFilename = filename
32+
c.Redraw()
33+
}
34+
2535
// SaveCanvasToGIF exports the canvas to an animated gif
2636
// for a given duration (in seconds).
2737
func (c *Canvas) SaveCanvasToGIF(filename string, duration int) {
@@ -133,6 +143,14 @@ func (c *Canvas) exportCanvasToPNG(frame string) {
133143
}
134144
}
135145

146+
func (c *Canvas) exportCanvasToText(frame string) {
147+
fmt.Fprintln(c.output, "Saving text...")
148+
text := strings.ReplaceAll(ansi.Strip(frame), "\r\n", "\n")
149+
if err := os.WriteFile(c.saveFilename, []byte(text), 0o644); err != nil {
150+
c.captureFailed(fmt.Errorf("can't create text file: %w", err))
151+
}
152+
}
153+
136154
func gifDelay(fps int) int {
137155
return max(int(math.Round(100/float64(clamp(fps, minFPS, maxFPS)))), 1)
138156
}

0 commit comments

Comments
 (0)