@@ -5,34 +5,42 @@ import (
55 "go/scanner"
66)
77
8- func ClearOutput (outputUpdater func (any )) {
9- outputUpdater (func (items []any ) []any {
10- return []any {}
11- })
8+ const (
9+ errType = `err`
10+ textType = `text`
11+ typeKey = `type`
12+ contextKey = `context`
13+ )
14+
15+ // ClearOutput clears all output items from the output box.
16+ func ClearOutput (setOutput func (any )) {
17+ setOutput ([]any {})
1218}
1319
14- func WriteError (outputUpdater func (any ), err error ) {
15- outputUpdater (func (items []any ) []any {
20+ // OutputError appends an error item to the output box.
21+ func OutputError (setOutput func (any ), err error ) {
22+ setOutput (func (items []any ) []any {
1623 if list , ok := err .(scanner.ErrorList ); ok {
1724 for _ , entry := range list {
18- items = append (items , map [string ]any {`type` : `err` , `context` : entry .Error ()})
25+ items = append (items , map [string ]any {typeKey : errType , contextKey : entry .Error ()})
1926 }
2027 return items
2128 }
22- return append (items , map [string ]any {`type` : `err` , `context` : err .Error ()})
29+ return append (items , map [string ]any {typeKey : errType , contextKey : err .Error ()})
2330 })
2431}
2532
26- func WriteString (outputUpdater func (any ), s string ) {
27- outputUpdater (func (items []any ) []any {
33+ // OutputString appends a text item to the output box.
34+ func OutputString (setOutput func (any ), s string ) {
35+ setOutput (func (items []any ) []any {
2836 if maxItem := len (items ) - 1 ; maxItem >= 0 {
2937 lastItem := items [maxItem ].(map [string ]any )
30- if lastItem [`type` ] == `text` {
31- lastItem [`context` ] = lastItem [`context` ].(string ) + s
38+ if lastItem [typeKey ] == textType {
39+ lastItem [contextKey ] = lastItem [contextKey ].(string ) + s
3240 return items
3341 }
3442 }
35- return append (items , map [string ]any {`type` : `text` , `context` : s })
43+ return append (items , map [string ]any {typeKey : textType , contextKey : s })
3644 })
3745}
3846
@@ -45,25 +53,69 @@ func OutputBox(output []any) *Element {
4553
4654func outputBoxComponent (props Props ) * Element {
4755 output := As [[]any ](props , `output` )
56+ outputBoxRef := UseRef ()
57+
58+ UseEffect (func () {
59+ // If there are only errors, scroll to the top,
60+ // otherwise scroll to the bottom.
61+ outputBox := outputBoxRef .Current ()
62+ scrollTop := 0
63+ if hasNonErrors (output ) {
64+ scrollTop = outputBox .Get (`scrollHeight` ).Int ()
65+ }
66+ outputBox .Set (`scrollTop` , scrollTop )
67+ }, []any {output })
4868
4969 children := make ([]Node , 0 , len (output ))
50- for _ , item := range output {
70+ for i , item := range output {
5171 itemMap := item .(map [string ]any )
72+ children = append (children , outputLine (
73+ i , itemMap [typeKey ] == errType ,
74+ itemMap [contextKey ].(string ),
75+ ))
76+ }
77+
78+ return Div (Props {
79+ `id` : `output-box` ,
80+ `ref` : outputBoxRef ,
81+ }, children ... )
82+ }
5283
53- classType := `output-text`
54- if itemMap [`type` ] == `err` {
55- classType = `output-err`
84+ // hasNonErrors determines if any output is not an error,
85+ // otherwise the list is empty or only contains errors.
86+ func hasNonErrors (output []any ) bool {
87+ for _ , item := range output {
88+ if item .(map [string ]any )[typeKey ] != errType {
89+ return true
5690 }
57- id := fmt .Sprintf (`output-item-%d` , len (children ))
91+ }
92+ return false
93+ }
94+
95+ // outputLine creates a React element for a single output line.
96+ // The index is used to create a unique ID for the line so it should
97+ // be the line's position in the output list.
98+ func outputLine (index int , isError bool , content string ) * Element {
99+ return CreateElement (outputLineComponent , Props {
100+ `index` : index ,
101+ `isError` : isError ,
102+ `content` : content ,
103+ })
104+ }
105+
106+ func outputLineComponent (props Props ) * Element {
107+ index := int (As [float64 ](props , `index` ))
108+ isError := As [bool ](props , `isError` )
109+ content := As [string ](props , `content` )
58110
59- child := CreateElement (`pre` , Props {
60- `className` : classType ,
61- `id` : id ,
62- }, itemMap [`context` ])
63- children = append (children , child )
111+ classType := `output-text`
112+ if isError {
113+ classType = `output-err`
64114 }
115+ id := fmt .Sprintf (`output-item-%d` , index )
65116
66- return Div (Props {
67- `id` : `output-box` ,
68- }, children ... )
117+ return Pre (Props {
118+ `className` : classType ,
119+ `id` : id ,
120+ }, content )
69121}
0 commit comments