feat(trace): add WriteHTML + Step.HTMLAvailable

Per-step HTML lives in <run>/html/step-NNNNN.html so trace.jsonl stays
line-greppable on apps with hundreds-of-KB DOMs.
This commit is contained in:
pj committed 2026-04-27 18:40:05 +07:00
1 parent fa0c82fb40
commit b6a6aeff5f
2 files changed
+94 -10

No files matched your search

+35 -10
View File
@@ -13,16 +13,17 @@ import (
)
type Step struct {
Index int `json:"step"`
Timestamp time.Time `json:"timestamp"`
Screen string `json:"screen,omitempty"`
Snapshots map[string]json.RawMessage `json:"snapshots,omitempty"`
Action *Action `json:"action,omitempty"`
Exceptions []Exception `json:"exceptions,omitempty"`
Violations []string `json:"violations,omitempty"`
Hierarchy *hierarchy.Tree `json:"hierarchy,omitempty"`
Residuals map[string]json.RawMessage `json:"residuals,omitempty"`
Metrics *Metrics `json:"metrics,omitempty"`
Index int `json:"step"`
Timestamp time.Time `json:"timestamp"`
Screen string `json:"screen,omitempty"`
Snapshots map[string]json.RawMessage `json:"snapshots,omitempty"`
Action *Action `json:"action,omitempty"`
Exceptions []Exception `json:"exceptions,omitempty"`
Violations []string `json:"violations,omitempty"`
Hierarchy *hierarchy.Tree `json:"hierarchy,omitempty"`
Residuals map[string]json.RawMessage `json:"residuals,omitempty"`
Metrics *Metrics `json:"metrics,omitempty"`
HTMLAvailable bool `json:"html_available,omitempty"`
}
type Metrics struct {
@@ -143,6 +144,30 @@ func (w *Writer) writePNG(name string, png []byte) error {
return os.WriteFile(filepath.Join(directory, name), png, 0o644)
}
// WriteHTML stores the page HTML for `stepIndex` under `<dir>/html/`. Empty
// payloads are skipped so the directory only exists when there is real
// content to render in the inspect UI.
func (w *Writer) WriteHTML(stepIndex int, html []byte) error {
return w.writeHTML(fmt.Sprintf("step-%05d.html", stepIndex), html)
}
// WriteHTMLAfter writes the post-action HTML, paired with the post-action
// screenshot.
func (w *Writer) WriteHTMLAfter(stepIndex int, html []byte) error {
return w.writeHTML(fmt.Sprintf("step-%05d-after.html", stepIndex), html)
}
func (w *Writer) writeHTML(name string, html []byte) error {
if len(html) == 0 {
return nil
}
directory := filepath.Join(w.directory, "html")
if err := os.MkdirAll(directory, 0o755); err != nil {
return fmt.Errorf("mkdir html: %w", err)
}
return os.WriteFile(filepath.Join(directory, name), html, 0o644)
}
func (w *Writer) Close() error {
w.mutex.Lock()
defer w.mutex.Unlock()