package engineeringdashboard import ( "encoding/json" "fmt" "os" "path/filepath" "strings" ) // ExportManifest describes what was embedded in an offline HTML. type ExportManifest struct { Included []string `json:"included"` Truncated []string `json:"truncated"` MetadataOnly []string `json:"metadata_only"` SensitiveWarn []string `json:"sensitive_warnings,omitempty"` } // ExportHTML writes a self-contained offline snapshot (mode 0600). func ExportHTML(snap Snapshot, outputPath string) error { css, err := readAsset("assets/app.css") if err != nil { return err } manifest := ExportManifest{} docs := make([]DocumentNode, 0, len(snap.Documents)) for _, d := range snap.Documents { cp := d if IsAssetsPath(cp.Path) { cp.Body = "" manifest.MetadataOnly = append(manifest.MetadataOnly, cp.Path) } else if cp.BodyTruncated { manifest.Truncated = append(manifest.Truncated, cp.Path) manifest.Included = append(manifest.Included, cp.Path) } else if cp.Body != "" { manifest.Included = append(manifest.Included, cp.Path) } else { manifest.MetadataOnly = append(manifest.MetadataOnly, cp.Path) } // strip any accidental absolute secret-looking paths from body display only — keep relative if strings.Contains(strings.ToLower(cp.Body), "/.secrets/") { manifest.SensitiveWarn = append(manifest.SensitiveWarn, cp.Path+": contains .secrets path mention") } docs = append(docs, cp) } exportSnap := snap exportSnap.Documents = docs payload, err := MarshalSnapshotJSON(exportSnap) if err != nil { return err } // HTML-safe JSON: break sequences safeJSON := strings.ReplaceAll(string(payload), "<", "\\u003c") safeJSON = strings.ReplaceAll(safeJSON, ">", "\\u003e") safeJSON = strings.ReplaceAll(safeJSON, "&", "\\u0026") safeJSON = strings.ReplaceAll(safeJSON, "