Initial import

This commit is contained in:
2025-10-10 10:05:13 +02:00
parent 3effc1597b
commit b96b6e7f8f
164 changed files with 5473 additions and 0 deletions

132
recorder/asciicast.go Normal file
View File

@@ -0,0 +1,132 @@
package recorder
import (
"encoding/json"
"fmt"
"io"
"strconv"
"sync"
"time"
)
type asciiCastRecorder struct {
wc io.WriteCloser
mu sync.Mutex
header asciiCastHeader
last time.Time
closed bool
}
type asciiCastHeader struct {
Version int `json:"version"`
Term asciiCastTerm `json:"term"`
Timestamp int64 `json:"timestamp"`
Title string `json:"title,omitempty"`
Env map[string]string `json:"env,omitempty"`
}
type asciiCastTerm struct {
Columns int `json:"cols"`
Rows int `json:"rows"`
Type string `json:"type"`
}
type asciiCastDuration float64
func (d asciiCastDuration) MarshalJSON() ([]byte, error) {
return []byte(strconv.FormatFloat(float64(d), 'f', 6, 64)), nil
}
type asciiCastFrame struct {
Delay float64
Data []byte
}
func (f asciiCastFrame) MarshalJSON() ([]byte, error) {
s, _ := json.Marshal(string(f.Data))
return []byte(fmt.Sprintf(`[%.6f, %s]`, f.Delay, s)), nil
}
func newAsciiCastRecorder(wc io.WriteCloser, info Info) (*asciiCastRecorder, error) {
now := time.Now()
if err := json.NewEncoder(wc).Encode(asciiCastHeader{
Version: 3,
Term: asciiCastTerm{
Columns: info.Columns,
Rows: info.Rows,
Type: info.TerminalType,
},
Timestamp: now.Unix(),
Title: info.Title,
}); err != nil {
return nil, err
}
if _, err := io.WriteString(wc, "\n"); err != nil {
return nil, err
}
return &asciiCastRecorder{
wc: wc,
last: now,
}, nil
}
func (r *asciiCastRecorder) Close() error {
return r.wc.Close()
}
func (r *asciiCastRecorder) writeFrame(kind rune, p []byte) (int, error) {
r.mu.Lock()
defer r.mu.Unlock()
if r.closed {
return 0, io.ErrClosedPipe
}
var (
now = time.Now()
delay = now.Sub(r.last)
)
v, _ := json.Marshal(string(p))
_, err := io.WriteString(r.wc, "["+strconv.FormatFloat(delay.Seconds(), 'f', 6, 64)+", \""+string(kind)+"\", "+string(v)+"]\n")
r.last = now
return len(p), err
}
type asciiCastWriter struct {
r *asciiCastRecorder
kind rune
}
func (w *asciiCastWriter) Close() error {
return w.r.Close()
}
func (w *asciiCastWriter) Write(p []byte) (int, error) {
return w.r.writeFrame(w.kind, p)
}
func (r *asciiCastRecorder) Reads() io.WriteCloser {
return &asciiCastWriter{r, 'o'}
}
func (r *asciiCastRecorder) Writes() io.WriteCloser {
return &asciiCastWriter{r, 'i'}
}
func (r *asciiCastRecorder) Resize(columns, rows int) {
var (
now = time.Now()
delay = now.Sub(r.last)
)
r.mu.Lock()
_, _ = fmt.Fprintf(r.wc, "[%.6f, \"r\", \"%dx%d\"]\n", delay.Seconds(), columns, rows)
r.last = now
r.mu.Unlock()
}
var (
_ Recorder = (*asciiCastRecorder)(nil)
_ Resizer = (*asciiCastRecorder)(nil)
)