Always download SVG screenshots
The dashboard now always downloads SVG on right-click even when PNG thumbnails are enabled, while keeping SVG as the default mode in the docs and tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -13,7 +13,7 @@ https://github.com/user-attachments/assets/62c52183-83a3-4fb5-97b1-ed001de4f53a
|
|||||||
- Typeahead find for quickly finding and launching sessions with minimal friction
|
- Typeahead find for quickly finding and launching sessions with minimal friction
|
||||||
- Web terminal with reconnect support
|
- Web terminal with reconnect support
|
||||||
- Ghostty WebAssembly terminal engine for fast rendering from [`ghostty-web`](https://github.com/rcarmo/ghostty-web)
|
- Ghostty WebAssembly terminal engine for fast rendering from [`ghostty-web`](https://github.com/rcarmo/ghostty-web)
|
||||||
- Session dashboard with live SVG screenshots from [`go-te`](https://github.com/rcarmo/go-te)
|
- Session dashboard with live SVG (or optional PNG) screenshots from [`go-te`](https://github.com/rcarmo/go-te)
|
||||||
- Docker watch mode (`webterm-command` / `webterm-theme` labels)
|
- Docker watch mode (`webterm-command` / `webterm-theme` labels)
|
||||||
- Docker compose manifest ingestion
|
- Docker compose manifest ingestion
|
||||||
- CPU sparkline tiles for compose services
|
- CPU sparkline tiles for compose services
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ webterm/server.go (LocalServer)
|
|||||||
├── docker_exec_session.go (Docker exec-backed sessions)
|
├── docker_exec_session.go (Docker exec-backed sessions)
|
||||||
├── docker_watcher.go (container add/remove discovery)
|
├── docker_watcher.go (container add/remove discovery)
|
||||||
├── docker_stats.go (CPU sampling + sparkline data)
|
├── docker_stats.go (CPU sampling + sparkline data)
|
||||||
└── svg_exporter.go (terminal snapshot -> SVG)
|
├── svg_exporter.go (terminal snapshot -> SVG)
|
||||||
|
└── png_exporter.go (terminal snapshot -> PNG via coverage blending)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Packages
|
## Packages
|
||||||
@@ -24,6 +25,7 @@ webterm/server.go (LocalServer)
|
|||||||
- `cmd/webterm`: CLI entrypoint
|
- `cmd/webterm`: CLI entrypoint
|
||||||
- `webterm`: server/runtime/domain logic
|
- `webterm`: server/runtime/domain logic
|
||||||
- `internal/terminalstate`: Go terminal emulator wrapper (`go-te`) used for screenshots
|
- `internal/terminalstate`: Go terminal emulator wrapper (`go-te`) used for screenshots
|
||||||
|
- `webterm/coverage_table.go`: coverage map for approximate PNG rendering
|
||||||
|
|
||||||
## Runtime data flow
|
## Runtime data flow
|
||||||
|
|
||||||
@@ -33,7 +35,7 @@ webterm/server.go (LocalServer)
|
|||||||
- live WS stream (`stdout`)
|
- live WS stream (`stdout`)
|
||||||
- replay buffer (reconnect support)
|
- replay buffer (reconnect support)
|
||||||
- terminal-state tracker (`go-te`) for screenshots
|
- terminal-state tracker (`go-te`) for screenshots
|
||||||
4. Dashboard pulls `/screenshot.svg` and listens on `/events` for activity.
|
4. Dashboard pulls `/screenshot.svg` (default) or `/screenshot.png` when `WEBTERM_SCREENSHOT_MODE=png`, and listens on `/events` for activity.
|
||||||
|
|
||||||
## Static assets
|
## Static assets
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -1087,12 +1087,11 @@ func (s *LocalServer) handleRoot(w http.ResponseWriter, r *http.Request) {
|
|||||||
dockerWatchJS = "true"
|
dockerWatchJS = "true"
|
||||||
}
|
}
|
||||||
screenshotEndpoint := "/screenshot.svg"
|
screenshotEndpoint := "/screenshot.svg"
|
||||||
|
screenshotDownloadEndpoint := "/screenshot.svg"
|
||||||
screenshotDownloadQuery := "sanitize_font_urls=1&download=1"
|
screenshotDownloadQuery := "sanitize_font_urls=1&download=1"
|
||||||
screenshotDownloadExt := "svg"
|
screenshotDownloadExt := "svg"
|
||||||
if s.screenshotMode == "png" {
|
if s.screenshotMode == "png" {
|
||||||
screenshotEndpoint = "/screenshot.png"
|
screenshotEndpoint = "/screenshot.png"
|
||||||
screenshotDownloadQuery = "download=1"
|
|
||||||
screenshotDownloadExt = "png"
|
|
||||||
}
|
}
|
||||||
html := fmt.Sprintf(`<!DOCTYPE html>
|
html := fmt.Sprintf(`<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
@@ -1151,6 +1150,7 @@ func (s *LocalServer) handleRoot(w http.ResponseWriter, r *http.Request) {
|
|||||||
const composeMode = %s;
|
const composeMode = %s;
|
||||||
const dockerWatchMode = %s;
|
const dockerWatchMode = %s;
|
||||||
const screenshotEndpoint = %q;
|
const screenshotEndpoint = %q;
|
||||||
|
const screenshotDownloadEndpoint = %q;
|
||||||
const screenshotDownloadQuery = %q;
|
const screenshotDownloadQuery = %q;
|
||||||
const screenshotDownloadExt = %q;
|
const screenshotDownloadExt = %q;
|
||||||
let cardsBySlug = {};
|
let cardsBySlug = {};
|
||||||
@@ -1172,7 +1172,7 @@ func (s *LocalServer) handleRoot(w http.ResponseWriter, r *http.Request) {
|
|||||||
function downloadSanitizedScreenshot(slug) {
|
function downloadSanitizedScreenshot(slug) {
|
||||||
if (!slug) return;
|
if (!slug) return;
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = screenshotEndpoint + '?route_key=' + encodeURIComponent(slug) + '&' + screenshotDownloadQuery + '&_t=' + Date.now();
|
link.href = screenshotDownloadEndpoint + '?route_key=' + encodeURIComponent(slug) + '&' + screenshotDownloadQuery + '&_t=' + Date.now();
|
||||||
link.download = slug + '-screenshot.' + screenshotDownloadExt;
|
link.download = slug + '-screenshot.' + screenshotDownloadExt;
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
@@ -1605,7 +1605,7 @@ func (s *LocalServer) handleRoot(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`, string(tilesJSON), composeModeJS, dockerWatchJS, screenshotEndpoint, screenshotDownloadQuery, screenshotDownloadExt)
|
</html>`, string(tilesJSON), composeModeJS, dockerWatchJS, screenshotEndpoint, screenshotDownloadEndpoint, screenshotDownloadQuery, screenshotDownloadExt)
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
_, _ = io.WriteString(w, html)
|
_, _ = io.WriteString(w, html)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -552,6 +552,9 @@ func TestDashboardUsesPNGWhenEnabled(t *testing.T) {
|
|||||||
if !strings.Contains(text, "screenshot.png") {
|
if !strings.Contains(text, "screenshot.png") {
|
||||||
t.Fatalf("expected dashboard to request png screenshots when enabled")
|
t.Fatalf("expected dashboard to request png screenshots when enabled")
|
||||||
}
|
}
|
||||||
|
if !strings.Contains(text, "sanitize_font_urls=1&download=1") || !strings.Contains(text, "screenshot.svg") {
|
||||||
|
t.Fatalf("expected contextmenu downloads to use svg screenshots")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRootTerminalPageAndSparklineValidation(t *testing.T) {
|
func TestRootTerminalPageAndSparklineValidation(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user