Add right-click sanitized SVG export for dashboard tiles
Introduce a dashboard workflow to export screenshot SVGs without external font URL references. Server changes: - Added screenshot query flags: sanitize_font_urls=1 and download=1. - Added SVG sanitization that strips @font-face src:url(...) for the vendored font path. - Added safe filename normalization for download responses. - Added Content-Disposition attachment support for downloadable screenshot exports. - Preserved existing cache behavior while computing ETags from the actual response variant. Dashboard changes: - Added tile contextmenu handler (right-click) to trigger sanitized SVG download per tile. - Download URL includes cache-busting timestamp to avoid stale browser downloads. Tests: - Added coverage for sanitized download response (attachment header + font URL removal). - Added coverage asserting dashboard HTML includes right-click sanitized download wiring. - Validated with make format && make check. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -229,6 +229,39 @@ func TestScreenshotCreatesSessionFromRequestedRoute(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestScreenshotSanitizedDownloadRemovesFontFaceURL(t *testing.T) {
|
||||
_, httpServer, _ := newServerForTests(t, false)
|
||||
resp, err := http.Get(httpServer.URL + "/screenshot.svg?route_key=shell&sanitize_font_urls=1&download=1")
|
||||
if err != nil {
|
||||
t.Fatalf("screenshot request error = %v", err)
|
||||
}
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d body=%q", resp.StatusCode, string(body))
|
||||
}
|
||||
if disposition := resp.Header.Get("Content-Disposition"); !strings.Contains(disposition, "attachment;") || !strings.Contains(disposition, "shell-screenshot.svg") {
|
||||
t.Fatalf("unexpected content disposition: %q", disposition)
|
||||
}
|
||||
if strings.Contains(string(body), `src:url("/static/fonts/FiraCodeNerdFont-Regular.ttf")`) {
|
||||
t.Fatalf("expected sanitized svg without font-face url")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDashboardIncludesContextMenuSanitizedDownload(t *testing.T) {
|
||||
_, httpServer, _ := newServerForTests(t, true)
|
||||
resp, err := http.Get(httpServer.URL + "/")
|
||||
if err != nil {
|
||||
t.Fatalf("dashboard request error = %v", err)
|
||||
}
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
text := string(body)
|
||||
if !strings.Contains(text, "contextmenu") || !strings.Contains(text, "sanitize_font_urls=1&download=1") {
|
||||
t.Fatalf("expected contextmenu sanitized download wiring in dashboard page")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootTerminalPageAndSparklineValidation(t *testing.T) {
|
||||
_, httpServer, _ := newServerForTests(t, false)
|
||||
resp, err := http.Get(httpServer.URL + "/?route_key=shell")
|
||||
|
||||
Reference in New Issue
Block a user