update.go RawHistoryFolder
1package html
2
3import (
4 "encoding/json"
5 "fmt"
6
7 "github.com/bnema/vev-vt/core"
8)
9
10// UpdateSchemaVersion identifies the browser update contract.
11const UpdateSchemaVersion uint16 = 2
12
13// CursorStyle is a DECSCUSR cursor shape value from 0 through 6.
14type CursorStyle uint8
15
16// Cursor is the absolute cursor state associated with an update. StyleSet
17// distinguishes an unset style from an explicit DECSCUSR style zero.
18type Cursor struct {
19 Row int `json:"row"`
20 Column int `json:"column"`
21 Visible bool `json:"visible"`
22 Style CursorStyle `json:"style"`
23 StyleSet bool `json:"styleSet"`
24}
25
26// ColorKind identifies how a terminal color is resolved.
27type ColorKind uint8
28
29const (
30 ColorDefault ColorKind = iota
31 ColorIndexed
32 ColorRGB
33)
34
35// RGB is a browser-facing RGB color.
36type RGB struct {
37 R uint8 `json:"r"`
38 G uint8 `json:"g"`
39 B uint8 `json:"b"`
40}
41
42// Color is a validated terminal color token.
43type Color struct {
44 Kind ColorKind
45 Index uint8
46 RGB RGB
47}
48
49func (color Color) MarshalJSON() ([]byte, error) {
50 switch color.Kind {
51 case ColorDefault:
52 return []byte(`{"kind":0}`), nil
53 case ColorIndexed:
54 return json.Marshal(struct {
55 Kind ColorKind `json:"kind"`
56 Index uint8 `json:"index"`
57 }{Kind: color.Kind, Index: color.Index})
58 case ColorRGB:
59 return json.Marshal(struct {
60 Kind ColorKind `json:"kind"`
61 RGB RGB `json:"rgb"`
62 }{Kind: color.Kind, RGB: color.RGB})
63 default:
64 return nil, fmt.Errorf("html: invalid color kind %d", color.Kind)
65 }
66}
67
68// Style is the canonical, browser-facing subset of core.Style.
69type Style struct {
70 Bold bool `json:"bold,omitempty"`
71 Italic bool `json:"italic,omitempty"`
72 Inverse bool `json:"inverse,omitempty"`
73 Dim bool `json:"dim,omitempty"`
74 Blink bool `json:"blink,omitempty"`
75 Strikethrough bool `json:"strikethrough,omitempty"`
76 Underline bool `json:"underline,omitempty"`
77 UnderlineStyle core.UnderlineStyle `json:"underlineStyle,omitempty"`
78 Foreground Color `json:"foreground"`
79 Background Color `json:"background"`
80 UnderlineColor Color `json:"underlineColor"`
81}
82
83// CellUpdate occupies Width terminal columns beginning at Column. It is
84// either a text run of Width printable ASCII characters (blank cells are
85// spaces) sharing one style, or one non-ASCII character of width 1 or 2.
86// Wide-cell continuation markers are represented by the preceding cell's
87// width. Text values of one update may share backing storage, so retaining
88// one keeps that update's text alive.
89type CellUpdate struct {
90 Column int `json:"column"`
91 Width int `json:"width"`
92 Text string `json:"text"`
93 Style int `json:"style"`
94}
95
96// RowUpdate replaces one complete logical terminal row.
97type RowUpdate struct {
98 Row int `json:"row"`
99 Cells []CellUpdate `json:"cells"`
100}
101
102// Update is an immutable-by-contract browser update. Snapshot updates contain
103// every logical row; incremental updates contain complete changed rows.
104type Update struct {
105 SchemaVersion uint16 `json:"schemaVersion"`
106 Width int `json:"width"`
107 Height int `json:"height"`
108 Snapshot bool `json:"snapshot"`
109 Rows []RowUpdate `json:"rows"`
110 Styles []Style `json:"styles"`
111 Cursor Cursor `json:"cursor"`
112}
113
114func cloneUpdate(src Update) Update {
115 dst := src
116 dst.Styles = make([]Style, len(src.Styles))
117 copy(dst.Styles, src.Styles)
118 dst.Rows = make([]RowUpdate, len(src.Rows))
119 for i := range src.Rows {
120 dst.Rows[i] = src.Rows[i]
121 dst.Rows[i].Cells = append([]CellUpdate(nil), src.Rows[i].Cells...)
122 }
123 return dst
124}