diff --git a/docs/interface/dashboards.md b/docs/interface/dashboards.md index ffaec0b..6660a78 100644 --- a/docs/interface/dashboards.md +++ b/docs/interface/dashboards.md @@ -161,6 +161,10 @@ this app's own address, or the hub's when the app is reached through one, shows a notice instead: on that origin the page could reach into the app and read your session. +**Image** draws the address as a picture scaled to fit the tile, rather than +as a page. Use it for a camera's MJPEG stream (`http:///stream`) or a +snapshot URL, which a page frame would show at the camera's native size. + ## Dashboard settings Most of what a dashboard carries is a widget: a tile bound to a message. A diff --git a/frontend/src/components/Dashboard/panels.tsx b/frontend/src/components/Dashboard/panels.tsx index 1bfc144..3afe437 100644 --- a/frontend/src/components/Dashboard/panels.tsx +++ b/frontend/src/components/Dashboard/panels.tsx @@ -582,6 +582,16 @@ export function WidgetPanel({ onCheckedChange={(trusted) => set({ trusted })} /> +
+ + Image + + set({ image })} + /> +
) : widget.type === "markdown" ? (
diff --git a/frontend/src/components/Dashboard/widgets.tsx b/frontend/src/components/Dashboard/widgets.tsx index 09f41be..4968329 100644 --- a/frontend/src/components/Dashboard/widgets.tsx +++ b/frontend/src/components/Dashboard/widgets.tsx @@ -488,11 +488,25 @@ export function onOrigin(url: string, origin: string): boolean { * its own. **Trusted** keeps it in its own origin, which most interactive * sites need to start (the demo's weather map renders an unstyled list * without it), and is refused for this app's own origin. + * + * **Image** draws the address as a picture scaled to the tile instead: a + * camera's MJPEG stream or snapshot, which a frame would show at its native + * size. An image runs nothing, so it needs no sandbox. */ function EmbedWidget({ widget }: WidgetProps) { const url = embedUrl(widget) const trusted = config(widget).trusted === true if (!url) return

Give it an address.

+ if (config(widget).image === true) { + return ( + {widget.title + ) + } if (trusted && onOrigin(url, window.location.origin)) { return (