Embed widget: Image option scales a camera stream to the tile

This commit is contained in:
2026-10-11 20:23:37 +02:00
parent 79e3908a76
commit d7c6e27a2a
3 changed files with 28 additions and 0 deletions
+4
View File
@@ -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://<camera>/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
@@ -582,6 +582,16 @@ export function WidgetPanel({
onCheckedChange={(trusted) => set({ trusted })}
/>
</div>
<div className="flex items-center justify-between gap-2 text-sm">
<FieldLabel help="Draws the address as a picture scaled to the tile, for a camera stream or snapshot rather than a page.">
Image
</FieldLabel>
<Switch
checked={cfg.image === true}
aria-label="Image"
onCheckedChange={(image) => set({ image })}
/>
</div>
</>
) : widget.type === "markdown" ? (
<div className="grid gap-1.5">
@@ -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 <p className="text-muted-foreground">Give it an address.</p>
if (config(widget).image === true) {
return (
<img
src={url}
alt={widget.title || url}
referrerPolicy="no-referrer"
className="h-full w-full rounded-md object-contain"
/>
)
}
if (trusted && onOrigin(url, window.location.origin)) {
return (
<p className="text-muted-foreground" data-testid="embed-refused">