Changelog

Everything that's shipped

From the first proof of concept to today — newest first. Built in a nine-day sprint; versions were assigned to capability milestones afterwards.

v1.82.02026-07-18

Recordings belong to their owner

A recording is now visible only to its owner, their team admin, and the superadmin — a superadmin’s clips to nobody else. A per-clip share switch opens one recording to the owner’s teammates, and every path that carries footage (list, poster, stream, export, key-track) enforces the same rule. The tab itself grew ownership chips, the ★ keep star, per-row and bulk delete — deletes vanish instantly and finish in the background.

v1.81.02026-07-17

One bar to act on anything

The library’s action bar now serves both worlds: it installs or debugs a freshly dropped file just like a stored package (rename and delete politely grey out for files not yet in the library). And the DevTools debugging flow wears the same bar — Ladit runs the pipeline, Instalovat hops over to the install tab with your source preselected.

v1.80.72026-07-17

One install intent at a time

A staged upload and a selected library package could both look chosen at once. Now the tab holds a single intent: dropping a file clears the library selection, and picking from the library quietly discards the staged file.

v1.80.62026-07-17

The dropzone reaches DevTools

Uploading a package while adding the debug module now uses the same drag-and-drop zone as the install tab — drop a file under "NEBO", it becomes a selectable source with a save-to-library switch in the familiar style.

v1.80.52026-07-17

The off switch shows itself

A switched-off install option no longer melts into its background — the track got a proper grey in both themes.

v1.80.42026-07-17

Daintier switches

The install options' switches shrank to the mockup's petite size — no more oversized toggles crowding out their own labels.

v1.80.32026-07-17

Pixel-matched to the mockup

The install options got their explanations back — short, one line, never wrapping — with the exact proportions of the approved mockup. Third try's the charm.

v1.80.22026-07-17

The options bar slims down

The two install switches now sit in one tidy line each — shorter labels, smaller icons, explanations tucked into tooltips. Same choices, half the visual weight.

v1.80.12026-07-17

Options appear when they matter

The install tab rests as a single clean drop area. The moment you pick a package, it turns into a tidy card with the file, two clearly explained switches — launch right away, keep in the library — and one Install button. No more settings hanging around with nothing to apply to.

v1.80.02026-07-17

One library to rule both tabs

The install tab and the debugging flow now share a single package library — the same clean selectable rows everywhere, with one action bar: install, debug, rename, delete. Each package wears its owner's colour: green for yours, blue for your team's, amber for the superadmin's.

v1.79.82026-07-17

Debug uploads join the library

Uploading a package while adding the debug module now offers the same "save to library" switch the install tab has — the clean original is stored, so next time it's one click away on any TV.

v1.79.72026-07-17

One click less, one acronym less

Re-opening the debugging offer no longer routes through a card asking for the same click twice — it goes straight to picking the package. And the footnote under DevTools dropped a cryptic acronym nobody needed to know.

v1.79.62026-07-17

The popout hands over cleanly

Opening DevTools in a separate window now closes the embedded panel and says where to continue, so two inspectors never wrestle over one TV. The header learned to read the app's real name straight from the package, the add-debug flow openly says when a package already carries the debug module, and a redundant status line above the cards is gone.

v1.79.52026-07-17

DevTools tell you what you're debugging

The DevTools header now names the app actually running on the TV instead of an internal code name, a new ✕ button lets you disconnect the inspector (and reconnect with one click), and the widescreen layout lost its mystery gap — the camera stretches, the remote compacts, and the tools sit right where they should.

v1.79.42026-07-17

Debugging waits for the app to come back

Adding a debug module restarts the app on the TV — and the page now patiently waits for it to call home instead of giving up after the first look. The connect step keeps spinning until the app reports in; only after a good minute does it offer a retry. Plus clearer wording on the upload row and the package library sorted newest-first.

v1.79.32026-07-17

Certificates are nobody's business

The install tab stopped lecturing about signing certificates — that all happens quietly in the background. What stayed, front and centre, is the one note that matters: an installed app is temporary and uninstalls itself once you release the device.

v1.79.22026-07-17

Install moves up front

A small reshuffle on the device detail: the Instalace tab now sits right after Informace — installing apps became an everyday action for everyone, so it earned the spot before the debugging tools.

v1.79.12026-07-17

Library polish

Deleting a stored package now asks for confirmation, the rename field grew to fit longer names, and the DevTools debugging flow can take its package straight from the library — the same one the install tab uses, with the same sharing rules.

v1.79.02026-07-17

The library follows the org chart

Package sharing now mirrors how the team works: everyone gets the superadmin's packages, a team admin sees the whole team's, and a member sees their own plus what their admins share — a colleague's private uploads stay private. Packages can also be renamed in place, with the same common-sense rules about whose you may touch.

v1.78.12026-07-17

Members see the certs are ready

A quick follow-up: right after opening installs to everyone, members still saw "certificates not set up" on devices where the superadmin had set them — the readiness check itself was locked to superadmins. Now everyone sees whether a device is ready to install, while certificate details stay with the superadmin.

v1.78.02026-07-17

Everyone installs; the TV forgets when you leave

Installing an app onto a TV is no longer a superadmin privilege — anyone drops in a package and it's signed and installed automatically. The twist: apps installed by regular users and team admins are temporary. Release the device and the TV uninstalls them by itself, debug builds included. And a new package library keeps your uploads around — your own stay private, a team admin's are shared with the team, a superadmin's with everyone.

v1.77.12026-07-17

No stray divider for members

A tiny cosmetic fix on the device info card: for regular members, the last visible row no longer draws a divider line into the empty space where the admin-only rows would be.

v1.77.02026-07-17

Roles mean what they say

The three roles got honest names — Superadmin runs the infrastructure, a Team admin (Správce týmu) manages their own team, a Member (Člen týmu) uses its TVs. And the names now match the powers: a team admin's audit view covers their team instead of everyone, only a superadmin creates teams, and DevTools — which was never really restricted — is now openly available to every role, listing only the TVs you're allowed to see.

v1.76.12026-07-17

Amber everywhere it belongs

The amber marking now covers every superadmin-only block on the device detail — the management card and the emergency power card included, with the explaining legend under the last of them. And the emergency power restart is now truly superadmin-only: the server refuses anyone else, and nobody else sees the button.

v1.76.02026-07-17

Admin-only details wear amber

On a device's Informace tab, everything only privileged roles can see — the IP addresses and the live power meter — is now tinted amber with a small edge stripe, and a one-line legend explains the colour. The live power card itself became superadmin-only, so regular users simply don't see it at all.

v1.75.12026-07-16

Details in their right places

The keyboard button now glows green while it's driving the TV. The remote-layout switcher went back under the remote — with the layout jump fixed properly — so the volume row lines up with the top of the picture again. And the picture controls (straighten, quality) moved out of the tab row to sit right under the video, where they belong.

v1.75.02026-07-16

Type like a remote

Click the live picture and your keyboard becomes the TV remote: arrows steer, Enter confirms, Backspace goes back, and R, G, B, Y press the colour keys. A small badge shows when the keyboard is live (Esc turns it off), and every keystroke visibly presses the matching button in the on-screen remote.

v1.74.02026-07-16

A tidier TV remote

The remote pane got its walkthrough polish: launching apps is now a proper dropdown fed by what's actually installed on the TV, the text-and-apps drawer reads as one card with a clear name, the layout no longer jumps when switching remote layouts (the switcher moved on top), and the picture controls sit together neatly even on narrow screens.

v1.73.02026-07-16

The certificate remembers its TVs

A signing certificate covers a list of TVs, but the app never showed which — and regenerating it for a new TV started from a blank list, quietly dropping the old ones. Now the stored certificate card names every TV it covers, and "Replace" starts from that full list, so adding a TV really just adds it.

v1.72.12026-07-16

Signing helper, delivered

The app-signing wizard's first step asked you to run a helper script you had no way to obtain — running the command ended in "file not found". The helper now lives on the server and the command fetches it for you before running it. A stray dropdown arrow was also nudged into place.

v1.72.02026-07-16

A tidier device form

The device form grew up: network addresses now sit in one clean grid — a labelled row per component, matching fields and buttons, technical MAC addresses in a technical font, and the camera-calibration status shows the exact date and time. App signing got two clear tabs (generate automatically or upload by hand) with a step-by-step guide, and the camera-type menu only offers cameras we actually own.

v1.71.02026-07-16

Device health tucked into Settings

The device-health monitor moved out of everyone's way: it now lives in Settings as an admin tool. The one piece regular users care about stays in sight — an offline device now says right in the device list when it was last online.

v1.70.02026-07-16

Favourites first

Devices you've starred now sit at the top of the device list, whatever sorting you choose. Prefer the plain order? One tick in the sort menu turns the pinning off.

v1.69.22026-07-16

Filter menus close when you click away

An open filter or sort menu on the device list now closes on a click anywhere else on the page — before, clicking the header or footer left it hanging open.

v1.69.12026-07-16

Device-list polish: one sort button, tidy checkboxes, settled power chip

Sorting now lives in a single neat button — field and direction together in one menu. The filter checkboxes were redrawn to sit right in the light theme. And the little power readout on grid cards found its home: a small chip in the card's corner that stays put instead of floating over the neighbours.

v1.69.02026-07-16

Smarter device filters & instant returns

Device-list filters now select more than one value at a time — several teams, brands or states at once, each filter a neat checkbox menu. Grid cards say what they mean: brand and model each get their own line. And coming back to the list from a device is instant — the app remembers what it just showed you instead of reloading everything from scratch.

v1.68.12026-07-16

List tooling always visible

The device list got its tools back. A well-meant rule had been hiding the search box, the list/grid switch, sorting, filters and the column chips until a list reached about eight items — with two real devices that meant no tools at all. The rule is gone: the toolbar now always shows, on devices, recordings and monitoring history alike.

v1.68.02026-07-16

Login polish: the living mark + pre-auth theme & language

The sign-in screen's logo comes alive: every half minute the stack of screens advances like a conveyor — a new edge appears from afar, the bars ride down, and the front one unfolds into the next TV, nudging the old one away. And the two things you couldn't change before signing in — light/dark theme and language (CZ ⇄ EN) — now sit as subtle controls in the footer.

v1.67.42026-07-16

Install progress: real-time streaming

The install checklist now tracks the actual work in real time — the TV box streams each step (connect, push, install, launch…) the moment it finishes and it appears on screen right away, instead of the whole list arriving at the end and the app faking the progression.

v1.67.32026-07-16

Install progress: step by step

The install checklist now reveals each step one at a time as it finishes (rather than all at once), each step's full technical detail is available by clicking it open, and the working spinner is clearer.

v1.67.22026-07-16

Install progress: cleaner steps

More install-tab refinements from a live test: the signing step now visibly shows it's working, the step list is clean (no raw device logs — details show only when a step fails, and the "uninstall previous version" step is now named properly), and the drop area appears immediately while certificates are verified quietly in the background.

v1.67.12026-07-15

Install tab polish

Small refinements to the new install tab after a live test: the drop area sits tidily under the tabs (no big gap), picking a package clearly shows the progress starting, the step checklist looks nicer, and a device that already has certificates goes straight to the drop area instead of briefly flashing a "no certificates" message.

v1.67.02026-07-15

📥 Install onto the TV: drop-and-go

Putting an app on a TV is now one step. The device's "Install" tab is a single drop area: drop an app package and it's automatically signed, sent to the TV, installed and launched, with a live checklist of each step. The old manual certificate + signing screens are gone from here — certificate setup moved into the device's edit form, where it's set up once.

v1.66.02026-07-15

🌐 UX audit batch C: localization

Tidies up text that used to leak the app's internal wording into the Czech interface. The activity log now describes every action in plain language (saved a watch, signed an app, connected an app…) instead of showing a raw internal event name, and the monitoring screen shows its scheduler messages — like "the TV is in use, a live session wins" — in your chosen language.

v1.65.02026-07-15

🧭 UX audit follow-up (batches A & B)

Polish from a fresh whole-app UX/UI audit — a screen-by-screen designer's pass in light and dark. Batch A tightens consistency and brand: a redundant version stamp is gone from Settings, the auto-play preference matches its neighbours as an on/off segment, the device-health rows no longer say "Online" three times, recording key-counts show one consistent glyph + number, and the Teams password action reads as an action. Batch B adds a loading/shell layer: the top navigation bar is present the moment a screen opens (instead of appearing only after the data loads), and every section shows a loading state during that wait rather than a brief empty flash.

v1.64.02026-07-15

✨ A growing nav and a hero login lockup

Brand polish on the app shell. The top navigation bar is now larger while you're scrolled to the top — showing the full screenwhere wordmark — and smoothly shrinks to the compact bar as you scroll down. The sign-in screen becomes a hero: a big stacked logo (mark over a two-line screen / where wordmark) with a soft green brand glow behind it, in both light and dark.

v1.63.22026-07-15

🧅 Relay-only must also mean TCP-only

Follow-up to the relay fallback: on half-open networks the relay itself must be reached over TCP/TLS — a relay reached over UDP dies exactly like the direct path. The fallback now uses exclusively the TLS relay on port 443.

v1.63.12026-07-15

🕳️ Half-open networks: relay-only fallback

Some restrictive networks are half-open: connection checks pass but the video packets themselves are silently dropped, so the player looked connected while showing nothing. The player now detects that signature (connected twice in a row with zero media arriving) and re-dials exclusively through the TLS relay, which such networks do pass.

v1.63.02026-07-15

🚆 Camera works on 443-only networks (TURNS)

Live camera now reaches phones and laptops on restrictive networks — train, hotel and corporate wifi that block everything except HTTPS. The media path can relay over TLS on port 443 (indistinguishable from HTTPS to firewalls), and the web player now fetches its full relay configuration from the server before connecting. Built and TLS-verified live from a moving train on ČD's CDWIFI — the network that exposed the gap.

v1.62.42026-07-15

🎥 Camera-failed overlay: no more eternal spinner

When the camera stream can't come up at all (some public/corporate networks block the media path), the terminal "video failed" message no longer keeps the spinner spinning as if still trying — and the message text is properly centered. Waiting states (connecting, TV offline) keep their spinner.

v1.62.32026-07-14

↩️ Status pills back to the legible generic glyphs

The v1.62.2 brand mark family is reverted (same-day owner call): at pill size the generic check / play / person / lock glyphs say what a state means at a glance, five near-identical screen marks didn't. Legibility wins over brand consistency for functional icons; the offline alert mark stays.

v1.62.22026-07-14

🎨 Brand state marks on the set status pills

The set status pills (Volné / Ovládáte / Používá se / Nedostupné / Offline) now all carry the brand state-mark family — one "stack of screens" mark, the front screen's glyph tells the state, the pill's colour tells whose. Completes the state-icon rollout the offline badge started in v1.61.1.

v1.62.12026-07-14

🧪 Coverage push + agent socket hygiene

A test-coverage batch (the suite grows to 53 files / 1468 assertions) — the Samsung pairing state machine now runs against a mock TV in CI, including the stale-token self-heal. It also surfaced one real fix: a pairing attempt the TV refuses no longer leaves a half-open socket behind.

v1.62.02026-07-14

🚚 Infra identifier migration to screenwhere

The live identifiers deferred by the rename are migrated — nothing visible changes in the app. Filesystem paths on the site box and the cloud VPS now say screenwhere, the site agent runs as one templated screenwhere-agent@<set> service per set, and new installs pair with the TV as "Screenwhere Agent" (existing sets keep their stored pairing untouched).

v1.61.12026-07-14

🖼 Brand rollout: two-tone wordmark + state marks

The new logo family now shows up across the product: a two-tone wordmark (login, app footer, these docs), the brand alert state mark on offline badges, and the real mark + favicon on the docs site.

v1.61.02026-07-14

🏷 The product is now Screenwhere

The internal working code name is retired — the product is now called Screenwhere (screen + anywhere). Every user-visible surface is renamed: the app wordmark and titles, the burned-in recording watermark, the sign-in pages, MCP metadata and these docs. The same release ships the final logo — the "stack of screens" mark — as the app icon, nav mark and first favicon.

v1.60.32026-07-14

🩹 Agent self-heals a dead TV socket

When the TV dropped the remote-control connection (standby, or two agents sharing one TV), the box kept using the dead handle forever — keys silently went nowhere and the calibration card refused to open until the agent was restarted. A failed send now reconnects once and retries by itself.

v1.60.22026-07-14

🩹 Straightened view honours 4:3 cameras

The straighten-view renderer assumed every camera streams 16:9, so a 4:3 camera (like set-01's 5MP Reolink) had its straightened picture squeezed to 75 % width — calibration discs could never reach their target rings. The output now letterboxes to the TV panel's own 16:9 and the lens model uses the camera's real aspect; 16:9 cameras are unaffected.

v1.60.12026-07-14

🩹 Wait for the card's fullscreen before locking on

The faster live detection could lock onto a frame before the TV browser had gone fullscreen, so its toolbar shifted the card down and the calibration came out offset. It now waits for fullscreen to settle (and re-asserts it during the search) before detecting.

v1.60.02026-07-14

🎥 Detection runs off the live stream now

The "finding the markers" step used to sit for several seconds fetching slow snapshots from the camera. It now uses the live video stream instead — you watch the camera feed the whole time, and the colour discs are found within a frame or two of the card appearing. Much faster, and it always looks like something is happening.

v1.59.12026-07-14

⚡ Faster, calmer capture polling

The calibration "capturing…" status used to flicker a few times before detecting, because every retry re-asserted fullscreen and waited over a second. Now only the first try does that; the rest just re-snap quickly until the colour discs appear.

v1.59.02026-07-14

🎨 Calibration targets match the colour discs

The alignment targets in the rectified pane are now coloured circles with a centre dot — one per corner, matching the disc colours — instead of the old grey squares, and the draggable handles wear their corner's colour too. Each disc on the TV card also gained a precise white centre dot to aim the handle at.

v1.58.12026-07-14

🩹 Wait for the card before detecting

The frame could be captured before the TV finished drawing the colour card, so detection ran on a blank screen. It now retries the capture until the card is up before placing the handles.

v1.58.02026-07-14

🌈 New calibration card: colour discs that auto-detect

The calibration card now shows four big solid colour discs — red, green, blue, yellow, one per corner — instead of the old black squares that were hard to spot in a dim room. The app finds each by its colour, so the handles land on the markers automatically and it can't get the corners mixed up. Missing a colour (bad framing) just falls back to pre-placed handles you nudge.

v1.57.02026-07-14

🎥 Calibration live preview is a real stream now

The "live preview" toggle in the calibration editor used ~1 fps snapshots and looked choppy. It now dials a real video stream into the editor — one decoder, full frame rate, as smooth as the live detail — with the rectified result rendered from it every frame.

v1.56.02026-07-14

🎯 Handles always pre-placed; the background stream truly pauses

Marker detection is fragile in a dim room, so the four handles are now always pre-placed — from the previous calibration when there is one, otherwise on the aim guides — and you just nudge each onto its marker. And the live stream behind the calibration dialog now genuinely stops decoding (it was being revived by the camera self-heal loop), so dragging is smooth.

v1.55.02026-07-14

🎛 Calibration polish: tight sliders, smarter detection, live preview

Slider ranges now match real edits (lens tweaks live within ±0.1), the marker detection pre-places the handles reliably — it looks near the previous calibration first and automatically re-captures when the TV was still loading the card — and the left pane can switch to a ~1 fps live preview. Buttons that did nothing useful are gone: the TV returns automatically on save or close.

v1.54.02026-07-14

🎯 Calibration: one flow, correct mapping, both-way zoom

Opening the calibration now does everything itself: the card loads on the TV with a narrated progress line, a frame is captured, the markers are found automatically and you land straight in the editor. The handle mapping was fixed — handles now belong exactly on the marker centres and the result is the whole panel edge-to-edge, with a saved zoom (both directions) for that sliver of margin around the screen. The background live stream pauses while calibrating, so dragging stays smooth.

v1.53.02026-07-14

🖼 "Straighten picture" in the recordings player too

The same display-only straightening the live view got is now available when reviewing recordings — a toggle next to the speed buttons, shown when the device has a saved calibration. Recordings stay raw on disk; the current calibration is applied at playback, so clips recorded before a camera re-aim may not line up perfectly — that's why it's a toggle.

v1.52.32026-07-14

🩹 The straightening is truly permanent now

The straightened view held only within one session — coming back showed the raw picture again. The server had it stored all along; the app's device-list loaders copied an explicit list of fields and the straightening wasn't on it, so every fresh page load dropped it. Now it carries through: a saved calibration applies on every visit until you recalibrate or remove it.

v1.52.22026-07-13

🩹 The straightening survives the device-edit form

Saving the device-edit form right after calibrating brought the raw view back — the app's cached device list predated the calibration, so it "forgot" the fresh straightening until a reload. The calibration save now updates that cache too, so the straightened view sticks through any navigation.

v1.52.12026-07-13

🩹 Saving the calibration lands you at the result

"I saved — and now what?" The save now finishes the job: the TV returns to what it was showing, the live player's straighten toggle switches on for that device, and the dialog closes. Open the live detail and the stream is already straightened. Recordings stay raw by design.

v1.52.02026-07-13

🎯 Calibration: aim live, then one-click fine-calibrate

The calibration dialog now leads with the live picture: aim the camera so the on-TV markers overlay the dashed outlines, press "Dokalibrovat" — and the four marker centres are detected automatically, dropping you straight into the straighten editor with the handles pre-placed. One save stores both the checks calibration and the view straightening. Clicking the centres by hand remains only as a fallback when detection isn't confident.

v1.51.22026-07-13

🩹 The straighten editor now matches the approved mockup

The first shipped editor looked nothing like the concept the owner approved. It is now the mockup, faithfully: original view with four draggable green handles on the left, the straightened result with ghost marker outlines, guide lines, zoom, grid and an A/B toggle on the right. Dragging is smooth too — frames upload to the GPU once, and repaints batch into single animation frames.

v1.51.12026-07-13

🩹 Your own live session no longer blocks calibration

Opening the live screen made you the device's holder — and the calibration dialog then refused with "someone is using this device", even though that someone was you. Calibration now proceeds when the session holding the device is your own; a device held by another user still says no.

v1.51.02026-07-13

🖼 Straighten the camera view

A camera never hangs perfectly — the picture is tilted and the wide lens bows straight lines. The view can now be straightened in software: in the calibration dialog you drag the four marked card centres, dial the lens curvature until the markers settle into their ghost outlines, and save. The live player then offers a "Straighten picture" toggle that re-projects the stream on the GPU — no extra decoding, no added latency. Purely cosmetic: recordings stay raw and the automated checks keep their own calibration, so this can never break a watch.

  • Single-pass WebGL (inverse homography + Brown k1/k2 lens model); per-set params saved by admins (POST /app/set-view-calib, audited); the on-TV card gained thin grid lines to dial the lens against.
v1.50.02026-07-10

⏲ Idle sessions release themselves

A pocketed phone kept holding a TV — and kept the recording signal on — for 7.5 hours overnight, because a background tab's connection never dies on its own. The relay is now the authority: a session that shows no sign of life for 15 minutes gets its control auto-released and its watching stopped (which also ends the recording). While you're actually there — tapping, typing, or just watching the live picture with the screen on — the app quietly pings the relay, so a real session is never cut. Coming back after an idle stop resumes with a single tap.

  • Server-side reaper (IDLE_RELEASE_MS, 15 min default, audited); a lightweight activity ping (≤1/min, only while visible and interacting-or-decoding); a running scenario is never reaped; camera stage says "paused due to inactivity — tap to resume".
v1.49.32026-07-10

🩹 No more browser-default input boxes

Owner report from the phone: the alert-webhook URL field and the access-token name field looked alien — grey browser-default boxes with a light border. The app styled inputs per screen and had no global base, so a field outside any styled scope fell back to the browser's dark-mode default rendering. There's now a zero-specificity base style for all text inputs (surface background, themed border and focus ring, placeholder colour), so a bare input can never render browser-default again — and every screen-specific style still wins.

v1.49.22026-07-10

✨ One gold star everywhere + the add/edit-device screen got its review pass

Two long-deferred UX leftovers closed in one small batch. Every active ★ in the app — favourite devices on the dashboard and in the picker, permanently-kept recordings in Záznamy and on dashboard poster badges — now turns the same gold (previously half of them turned the brand green). And the standalone add/edit-device form finally got its dedicated walkthrough.

  • New --star colour token (slightly darker gold on light surfaces for readability); applied to favourite buttons, keep buttons and poster badges.
  • Add/edit device: "Smazat zařízení" is styled as the destructive action it is (a CSS class mismatch left it looking like a plain button); "Typ kamery" offers an explicit "(nezadáno)" option instead of rendering blank for devices with no stored camera type; team-access chips (and all pill tabs) gained a hover state; three hardcoded Czech labels in the set-detail editor are localized; filled MAC values no longer look like placeholder text.
v1.49.12026-07-10

🩹 A frozen camera picture now heals itself — "the remote does nothing" was a stale frame

Real-phone report: after camera fullscreen, the remote seemed dead. The full chain check (relay + Pi agent logs + a camera snap) proved every key press DID reach the TV — its menu cursor moved exactly as pressed. What actually broke was the picture: the fullscreen→remote transition made the phone throttle the video decoder mid-session, and since v1.48.3 the app (correctly) no longer tears down a connection whose data still flows — so the preview showed the last decoded frame, frozen, and the TV's reactions were invisible.

  • When data flows but nothing decodes for ~3 seconds while someone is actually watching (camera pane, PiP, or fullscreen), the app re-dials the stream — a fresh decoder starting at a keyframe (engine.kickStream(), rate-limited to once per 15 s). Hidden previews stay untouched — that throttling is fine and saves battery.
v1.49.02026-07-10

📌 The phone camera picture now floats — drag it to any corner

Owner pick after comparing variants on a real phone: the little camera preview on the remote tab no longer sits in the page flow — it floats over the remote. By default it tucks into the top-right corner under the sticky nav, and it stays put while you scroll a long remote, so the TV stays visible next to the bottom rows. Don't like the corner? Drag the box anywhere — it snaps to the nearest corner on release, and each set remembers its corner.

  • CSS position:fixed + a pointer-drag handler with snap-to-nearest-corner; the corner is persisted per set (tv-pip-corner:<set> in localStorage). ✕ / re-show chip / ⤢ jump-to-camera and the v1.48.4 real-element reparenting are unchanged; desktop ≥ 740 px untouched.
v1.48.42026-07-09

🩹 The phone camera picture-in-picture shows the real picture

Follow-up to v1.48.3: the little camera picture stopped blinking but stayed black. A second video element mirroring the same stream can't render on phones — the decoder is throttled while the only rendered sink is missing, and a sink attached mid-stream doesn't wake it up. So the PiP now borrows the real camera video element: it moves into the PiP box on the remote tab and moves back when you leave. One element, always visible somewhere — the picture never stops.

  • pipSync reparents #cam-video instead of mirroring the stream; DOM identity survives the move, so stream, stats and fullscreen keep working. Desktop is explicitly excluded via the same media query that hides the PiP, and the separate PiP video element is gone from the page.
v1.48.32026-07-09

🩹 The phone camera picture-in-picture no longer blinks in and out

On a real phone, the little camera picture on the remote tab kept flashing up and vanishing in an endless loop. The culprit was our own 7-second liveness watchdog: it only trusted decoded frames as proof of life, and mobile Chrome pauses decoding while the video element is hidden — so a perfectly healthy stream was torn down and rebuilt every ~7–8 seconds. Receiving data now counts as alive too.

  • framesReceived counts alongside framesDecoded in the engine's stream-liveness check, and the PiP video gets an explicit play() nudge when the stream attaches (mobile autoplay).
v1.48.22026-07-09

🍔 The mobile menu now drops down from the burger — not up from the bottom

Owner preference after the first real-phone session: tapping the burger now opens a classic dropdown menu anchored right under the header — where the eye goes after tapping — instead of a sheet sliding up from the bottom edge. Everything else is unchanged: it still closes when you pick a section or tap outside.

  • CSS-only re-anchor of the existing sheet element; no behavior change and no server restart needed.
v1.48.12026-07-09

🩹 The app survives a slashless URL — and an expired session lands on login

Two fixes straight from a real phone. Visiting …/app without the trailing slash used to render the app as raw unstyled HTML — the browser resolved every asset URL against the site root. /app and /docs now 308-redirect to the slashed form, query string preserved. And opening the app with a stale stored session now lands on the login screen instead of an empty dashboard with zero devices and no user.

  • The boot-time data loads now funnel 401s into the normal "session expired" path, and the app re-checks the session before showing a screen; a new static-serving test suite covers the redirects, caching headers, the SPA fallback and path traversal.
v1.48.02026-07-09

📜 Run history that goes all the way back — "Load older" in Monitoring

The Monitoring run history no longer stops at the newest 100 runs. A "Load older" button under the list fetches earlier runs 50 at a time, all the way back to the start of the retained ring. Pages are keyed by run id — not by offset — so rows don't shift when new runs land between clicks, and the verdict chips keep filtering everything that's loaded.

  • GET /app/scenario-runs gains a before=<runId> cursor and a more flag; it composes with scenarioId and limit, and a pruned cursor simply returns an empty page (the ring prunes oldest-first, so nothing older survives either).
v1.47.02026-07-09

⏱ Scenarios can assert memory and load time — from inside the app

A new perf: condition reads the running app's real performance numbers over the same debug channel el: uses — no camera involved. memoryMB asserts the live JS heap ("the app stays under 150 MB"), domReadyMs and loadMs assert how fast the page actually got ready. Works in check/waitUntil/if/repeat.until, so a watched scenario can fail the moment an app starts leaking or loading slowly.

  • check: { perf: memoryMB, max: 150 } · waitUntil: { perf: loadMs, max: 8000, timeout: 15000 } — editor autocomplete, validation, the VS Code schema and the inspector's "Otestovat teď" all know the new kind.
  • Metrics that don't exist yet (page still loading, no memory API) report as a soft "not yet" instead of an error, so launch-then-waitUntil sequences just work.
v1.46.02026-07-09

📱 See the TV while you press keys — camera PiP on the phone remote

The mobile pass is complete. On a phone the remote tab now docks a small live camera preview above the volume row — dismiss it with ✕ (per device; a dashed chip brings it back) or tap ⤢ to jump to the full camera. It mirrors the already-running stream, so it costs no extra bandwidth. Plus phone polish: a one-line sticky header on the device detail, two-line title clamps, settings controls that drop under their label, and a stacked install form.

v1.45.02026-07-09

📱 Comfortable on a phone: swipeable stats, fixed device rows, one tablet breakpoint

First build wave of the mobile/tablet pass. On phones the dashboard stats become a swipeable row with position dots (the 4th card used to be clipped and unreachable, the storage card collapsed into a jumble), device-list titles no longer paint under the status chip, the nav sheet closes after picking a section, and the review player stops scrolling sideways. Tablets get one deliberate threshold: at 740 px the camera-beside-remote layout and the horizontal nav flip together, so iPad mini portrait now gets the full arrangement.

v1.44.12026-07-09

🩹 Monitoring run history: trigger icons back to text size

Found during the mobile/tablet audit but broken at every screen size since v1.32: the run-history trigger glyphs (plán/ručně/kamera) shipped without a size rule, so the icons rendered ~135 px wide and every history row took half a screen. One CSS rule returns them to inline text size.

v1.44.02026-07-09

🎬 Scenarios can assert the video really plays + installs are clean reinstalls

An el: condition now reads the player itself: playing ("the video element truly renders — not paused, not ended, buffered enough") and minTime (currentTime at least N seconds) prove playback from inside the instrumented app, no camera needed. Separately, every install now uninstalls an already-present same-name package first, so installs always start clean.

  • waitUntil: { el: "video", playing: true, minTime: 5 } — usable in check/waitUntil/if/repeat.until; editor autocomplete, validation and the VS Code schema know the new fields.
  • Uninstall-first installs retire the "already installed under a different certificate" dialog — that error state can no longer happen; app data is wiped with the old install, giving scenarios a defined starting state.
v1.43.12026-07-09

🐛 The DevTools tab now loads its target list

Found while verifying the add-debug chain live: the DevTools pane fetched the target list without the auth header every other call sends, so it always got an empty list and showed "no debug module" even with a target registered. Now it authenticates like the rest.

v1.43.02026-07-09

🐛 Add-debug fixes: the debug target attaches, and already-installed apps are handled

Two bugs surfaced on the first live end-to-end run of "add debug to this app" on a real TV. The injected debug client now registers under a title the DevTools tab (and el:/picker) can actually select, and installing over an app that's already on the TV under a different certificate no longer dead-ends.

  • The inject sets window.ChiiTitle to the set's id (chii ignores the old data-name guess), so the target is selectable and el:/picker can reach it.
  • When the app is already installed under another cert (Tizen won't overwrite it → 118012), a confirm dialog offers to uninstall the original and install the debug build in its place — box-side, no operator step.
v1.42.02026-07-09

🎯 Pick elements visually in the scenario editor

No more hand-writing CSS selectors for el: conditions: the editor's new 🎯 Element button lists the running app's elements live — selector, label, focus state — and one click drops a "press DOWN until it has focus" block into the scenario.

  • Selectors are derived to survive focus moves: ids win, state-ish class names (focused, active…) are excluded.
  • Filter box, match counts and a focus chip make the right row easy to spot; a refresh re-reads the app.
  • Same read-only probe channel and gating as el: checks — apps without the debug module read out why instead of listing.
v1.41.02026-07-09

el: conditions — the app itself as the oracle

Scenarios can now assert what the running TV app's DOM shows — "press DOWN until the row named Movies has focus" — through the debug plane, no camera involved. A structured, read-only probe runs inside the instrumented app; the run log reads out what the app actually showed.

  • Selector + text + focused/visible, all asserted on the same element; works in every condition slot next to ocr and image checks.
  • An app without the debug module simply never passes the condition (with a clear read-out) — camera conditions remain the universal fallback.
  • Free-form js conditions stay refused by design: el: gives authors assertions, never code execution.
v1.40.02026-07-08

The tva CLI — scenarios live in git

Authored scenarios sync between the relay and local YAML files: pull them into a repo, edit them in VS Code (each file carries a $schema header, so autocomplete and validation just work), push them back — authenticated by a personal access token with the same author/admin rules as the app.

  • A brand-new local file creates a scenario on push and is renamed to the server-minted id, so it stays pushable.
  • Validator problems print per file — nothing invalid ever lands on the relay.
v1.39.02026-07-08

Schedule a watch straight from the editor

A scenario you just wrote can go under monitoring without hunting for it in another screen: the editor toolbar gains a ⏰ Watch button (admins) that saves your edits and opens Monitoring's new-watch dialog with the scenario already picked.

  • Same save-first rule as Run — the watch always schedules the stored document.
v1.38.12026-07-08

Stable-fullscreen calibration capture

The TV browser drops out of fullscreen after ~15–20 s, so the calibration card's geometry could differ between captures. Capture and save now re-assert fullscreen (one OK press) and use a fresh frame — the marked geometry and the stored camera reference are always the fullscreen card.

  • The card itself also retries fullscreen the moment it drops out (best effort).
v1.38.02026-07-08

Rectified checks + the standing camera watch

The calibration's two consumers land — the fallen-camera incident's fix is complete. Every check frame on a calibrated set is warped into a canonical panel space (regions and references live in TV coordinates and survive a camera re-aim), and every scheduled run first verifies the camera still sees the TV where calibration left it.

  • A bumped camera now FAILS the watch with the live frame as evidence and fires the alert webhook — never a silent PASS again.
  • The camera check anchors on the panel's surroundings, so it works whatever the TV happens to show; too little detail → "inconclusive", never a false alarm.
  • The condition inspector previews through the same rectified eye the checks use.
v1.37.12026-07-08

Calibration status refreshes after the overlay closes

Polish from the first real remote calibration (a set aimed from 1000 km away): the device-edit forms now re-fetch the "Calibrated" status the moment the calibration overlay closes, instead of showing the stale value until a reload.

v1.37.02026-07-08

Camera install calibration — aim every camera the same way

The fallen-camera incident's systematic fix begins: a guided calibration flow in the device-edit forms. The TV shows a full-screen test card (opened in its browser — no app install needed), the installer aims the camera against ghost marker outlines over a live view, clicks the four marker centers and saves.

  • Every camera then frames its TV the same way — check regions and reference frames become transferable between sets.
  • Each set stores its camera→screen homography and a "camera sees the TV" reference frame; the standing watch check hooks on next.
  • Admin-only, audited, and a set someone is actively using refuses the card — a live session always wins.
v1.36.12026-07-08

Scenario tags are now clickable filters

Tags in the scenario library used to be decoration — the search box quietly matched them, but nothing told you. Now clicking a tag chip filters the list (clicking it again clears), chips respond to hover and keyboard, and the search bar stays visible whenever the library has anything in it.

v1.36.02026-07-08

The condition inspector — tune checks against live camera evidence

Scenario checks stop being guesswork: open the inspector on any condition, see the live camera frame with the checked region outlined, drag the strictness knobs and hit "Test now" — PASS or FAIL with the measured numbers, before the scenario ever runs.

  • OCR checks: expected text, region and match strictness; image checks add content and position tolerance plus one-click capture of a new reference frame.
  • Everything writes back into the scenario code — the editor stays the source of truth.
  • The editor's syntax colours are finally readable in the dark theme (and still right in light).
v1.35.02026-07-08

A lighter TV detail and Settings that read like a page, not a pile

The remote's occasional tools (type text, launch an app) fold into one "Akce" block; expert tabs show only to the roles that use them, and the install/signing workflow finally gets the full screen width. Settings regroup into four titled sections.

  • Everyday controls stay put — the remote just lost a storey of rarely-used rows.
  • DevTools appears from admin up, Instalace only for superadmins — and opens wide, with the certificate and signing cards side by side.
  • Settings: Account & security · Preferences · Organization (admin) · System (super); device health moved next to the device list where it belongs.
v1.34.02026-07-08

One scenario library — origin is a badge, not a section

Scenarios from recordings and scenarios written in the editor now live in one list — each row wears a small badge saying where it came from, and a recorded one converts to a fully editable scenario in one click.

  • One search, sort and team filter for everything; "+ Nový scénář" sits in the header.
  • The convert action turns a recording's key-track into an editable document and drops you straight into the editor.
  • The dashboard's watch strip now speaks the same language as the Hlídání section.
v1.33.02026-07-08

Global navigation — every section one click away

The app's sections — Zařízení, Scénáře, Hlídání, Záznamy — now live in one persistent top bar with icon tabs, an active indicator and the familiar avatar, on every screen. No more bouncing off the dashboard to get anywhere.

  • Hover a section to get a one-breath explanation of what lives there; a red badge on Hlídání counts failing watches.
  • The dashboard puts your tasks first — failing watches and devices on top, charts folded into a collapsed "Provoz" section.
  • Deep screens (a TV's detail, the player, the editor) keep a contextual back-crumb under the bar; on phones the sections tuck into a bottom sheet.
v1.32.02026-07-08

A consistency pass — the UX audit's quick wins

A whole-app UX audit ran this week; this release ships its first batch: schedule times in your timezone, list toolbars that appear only when a list is big enough to need them, one time format, one version number, SVG icons everywhere — and the alert webhook is now set in Settings instead of on the server.

  • Nastavení → Alerty (webhook): device outages and failed watches POST to your URL — no server access needed; a restart-survival bug in stored settings was fixed along the way.
  • Watch schedules display and accept times in your wall clock — "denně 05:00" and "další 05:00" finally agree.
  • Search, sort and filter controls on short lists step aside until the list grows (~8 items); device and recording rows stop repeating the model name twice.
  • Old /app/picker links work again, back-links name where they go, and the settings page no longer claims to be version 0.1.0.
v1.31.02026-07-08

Screenshot checks — "the screen looks like this", position included

Scenarios can now assert against a stored reference frame, not just OCR text: capture what the screen should look like, and every run compares the live camera against it — content and position. A layout that slid 20 pixels down fails the check even when every word still matches.

  • Reference frames are captured straight off the live camera and stored with the scenario — they version and die with it.
  • Two knobs per check: content tolerance (how different the pixels may be) and offset tolerance (how far the region may move); the failure report includes where the content actually was.
  • A camera quality change doesn't break checks — frames are scale-normalized before comparing.
  • Also fixed: a tab reopened after your session expired now lands on the sign-in screen with an explanation, instead of a broken page.
v1.30.02026-07-08

Pick the app to launch — the TV tells you what's installed

The launch row and AI agents now ask the TV for its installed apps instead of you hand-typing Tizen ids: open the row, get a type-ahead list of names (Netflix, YouTube, Disney+…), pick, launch. Agents get the matching list_apps tool.

  • The app list is read straight off the TV and parsed with a format written against a real 52-app capture — ids with dashes and non-ASCII titles included.
  • The list is cached per TV and refreshed on reconnect; manual id entry keeps working even when the list can't load.
  • Close/restart was probed on the real hardware and is not offered by this TV's locked shell — launching stays the one verb, now with none of the typing.
v1.29.02026-07-07

Launch an app on the TV — from the app and from AI agents

The sibling of v1.28's text entry: start any installed app by its Tizen id in one step — a "Launch app" row on the live remote, and a matching launch_app MCP tool for AI agents. No more HOME-menu navigation to get an app on screen.

  • A collapsible row under the text entry: app id + launch (Enter works); a progress toast covers the multi-second round-trip and a failure shows the box's actual error — including how to fix developer mode.
  • A malformed app id never leaves the browser (the client mirrors the box's guard); the MCP tool validates before doing anything.
  • Rides the launch transport proven in v1.23 (the box's sdb link; the TV's developer mode must whitelist the box). Close/restart, deep links and an installed-app picker are next in the backlog.
v1.28.02026-07-07

Type text on the TV — from the app and from AI agents

No more arrow-key hunt-and-peck on on-screen keyboards. The live remote gains a text-entry row that types straight into whatever input field is focused on the TV, and AI agents get the matching send_text MCP tool — search boxes and login forms filled in one step.

  • A low-key "Type text" toggle under the remote expands into an input + send row; Enter sends, and the TV's confirmation drives a success/fail toast.
  • Sending replaces the field's content (an empty send clears it) — the hint under the field says so, and agents get the same semantics spelled out in the tool description.
  • Typed text may be a credential: it is never logged anywhere, and the field is cleared when you switch TVs.
  • Web + MCP only — the typing transport itself (Samsung SendInputString) shipped in v1.23 and is hardware-verified.
v1.27.02026-07-07

A monitoring screen — scenarios watch your TVs on a schedule

Scheduled scenarios now watch your apps, and a monitoring screen shows what happened: a card per watch with the last verdict and a colour-coded chip per TV, a filterable run history, and — for a failure — the camera frame from the exact moment a check failed. A watch can target several TVs or a whole team, running them in parallel or one at a time.

  • Multi-TV watches run in parallel (fast) or serial (one TV at a time, to dodge playback concurrency limits), with an optional cleanup scenario between TVs that deregisters a device to free its slot for the next.
  • Watch a whole team and new TVs join automatically; a busy TV is skipped so a live session always wins; a failing scheduled run alerts your webhook.
  • The dashboard surfaces failing watches with which TVs failed; every run keeps a full step-by-step record, and a failed run keeps the screenshot of what the screen actually showed.
v1.26.02026-07-07

Runs remember themselves — and can run on a schedule

Every scenario run is now recorded: each step with its timing, every check with its measured match, and the final verdict. Scenarios can also run unattended on a schedule — nightly at three, or every half hour — and if a scheduled run doesn't pass, an alert goes out to your webhook. A scheduled run never interrupts a person: if someone is using the TV, it steps aside and records that it did.

  • The run history keeps the last runs with full detail — what ran, when, on which TV, triggered by whom, and exactly why a failing run failed.
  • Schedules are daily-at or every-N-minutes, managed by admins; alerts reuse the same webhook channel as the device-offline notifications.
  • AI agents got the full loop too: over MCP they can list, read, write and run scenarios — and get back PASS or FAIL with what was expected versus what the screen actually said.
v1.25.02026-07-07

Scenarios can check the screen — and say PASS or FAIL

Until now a scenario replay was blind: it pressed the keys and a human watched the camera to judge the result. Scenarios can now assert what's actually on the TV — "the screen says Welcome" — through the real camera, with no changes to the app being tested. A run finally ends with a verdict: PASS, or FAIL with what the screen actually said.

  • Checks read the screen through the camera (OCR, Czech included) and match fuzzily — camera frames are noisy, so a tunable match threshold decides, and the editor shows the measured similarity for every attempt.
  • The building blocks compose: check with a timeout (polls until the screen settles), "press DOWN until the row appears", if/else branches on what's visible, and soft checks that record a miss without stopping the run.
  • A failed check stops the run and quotes what was expected versus what was read — straight in the editor's run console, on the exact line of code that failed.
v1.24.02026-07-07

A code editor for scenarios

Authored scenarios got their editor: a full code editor with syntax highlighting and autocomplete, opened straight from the Scenarios screen. One scenario, three interchangeable notations — YAML, JSONC and TypeScript — switch tabs and nothing is lost. Below the code sits a debug-style run console: pick a TV, run the scenario, and watch each step light up in the code with the exact wall-clock time it fired.

  • Run executes the saved document on one TV with per-step timings and a verdict; dry-run walks the steps without sending anything. Multi-TV runs stay in the library.
  • Record straight into the editor: drive the TV from its live view in another window and the presses stream in as steps with their real pauses. Drag a wait value right in the code to retime it.
  • The TypeScript notation is a typed literal — parsed, never executed — so an editor, a git repo and an AI agent can all safely read and write the same scenario.
v1.23.02026-07-07

Scenarios become editable documents

Until now a scenario was whatever you happened to record. It can now be an authored document: a readable list of steps — press these keys, wait, type text, launch an app, repeat a block — that is stored on the relay, validated against a published format, and run step by step onto any TV. Recordings convert into editable documents with one call, and the format already reserves room for on-screen checks (the upcoming camera verdicts).

  • The format is deliberately simple data, not code: steps and bounded loops, with a published schema — so editors (and AI agents) can safely read and write it, and VS Code autocompletes it out of the box.
  • Two new abilities arrive as steps and will surface in the app next: typing whole text into the TV's focused input (no more arrow-key hunt-and-peck) and launching an installed app by its id.
  • Anyone signed in can author scenarios; editing someone else's needs the author, an admin or a superadmin — and every save, run and delete is audited. Checks (like "the screen shows Welcome") store fine but politely refuse to run until the camera-checkpoint release.
v1.22.02026-07-06

Release everywhere + multi-window awareness

Have the same TV's live view open in three tabs and on your phone? The app now treats all of them as you: any window can drive the TV, the banner shows "you're using this in 4 windows", and Release means release — it frees the set from every window and session at once.

  • Releasing works by identity, not by the lucky tab that happened to grab control first — one click frees the active session and any background reservation.
  • After you release, open windows become viewers and never silently re-take the set on a reconnect; an explicit "Take control" button brings it back when you want it.
  • Strangers are rejected exactly as before — this only unifies your own windows; every action stays audited under your account.
v1.21.02026-07-06

Burned exports draw the actual buttons

A downloaded QA clip no longer labels presses with plain text — it draws the remote's buttons. The last few presses ride along the bottom of the picture as icon chips: the newest is big and glows in its category colour with an English label, older ones shrink and fade to the left, so a reviewer sees the sequence context, not just the last press.

  • Design was mockup-first: three overlay variants were prototyped and one approved before any build; the device header and watermark stay as before.
  • Nothing renders on the box — the button chips are pre-made images the box's ffmpeg composites in with per-press time windows, so a burn costs about the same as before.
  • Presses without a matching icon (older recordings, exotic keys) and very long tracks still burn as the plain-text labels; both styles can share one clip.
v1.20.02026-07-06

Scenario library + running on several TVs at once

Saved (★) recordings with key presses are now a first-class scenario library, and a scenario can replay on several TVs in parallel. The new Scénáře screen turns timestamps into readable test scripts — name them, tag them, search them — and runs one on any set of free TVs with a live progress row per TV.

  • A scenario is a ★-kept recording promoted to a script: owner, team admin or superadmin can give it a name and tags; the key-track itself never leaves the recording's box.
  • The runner multi-selects eligible TVs (online + free/yours; busy and offline are locked), fires the replay on each in parallel, and shows per-TV step progress with stop-one or stop-all.
  • Each run rides the existing per-target replay engine with the same control gating and audit — running on N TVs takes N sessions, released automatically when the run ends.
v1.19.02026-07-06

OAuth click-to-connect for MCP

AI clients can now connect with a click-to-connect login instead of pasting a token. Screenwhere is its own authorization server — you sign in with your existing local account (email, password, 2FA) and the connected app acts as you. OAuth is a front door onto your account, not a separate identity or a third-party login.

  • Standard OAuth 2.1 with PKCE: a short-lived access token that auto-refreshes, resolving to your account with your live role — so all the usual access rules and audit apply unchanged.
  • On the consent screen you choose read-only or read + control; a read-only connection can look but never drive the TV, enforced server-side.
  • Settings → Connected apps lists what you've connected and lets you disconnect any of them; "sign out everywhere" drops them too. This completes the MCP feature set.
v1.18.02026-07-06

MCP control tools — an agent can drive the TV

Builds on the read-only MCP server: an AI agent can now control a TV through MCP — press remote keys, switch power, and replay recorded scenarios — each action as the user who drives it, with the relay enforcing who may control and auditing every action.

  • Tools: send_keys (a sequence like "HOME RIGHT OK"), power (on / off / cycle via the smart plug), and play_scenario (replay a recording's key-track onto a TV).
  • Each action runs as the holder of the set — taken automatically on a free TV, or taken over by an admin; a regular user can't wrest control from someone else who's using it.
  • Nothing new is trusted: the relay stays the single authority, the server holds no standing credential, and every key-press, power switch and replay is audited as the real user.
v1.17.02026-07-06

Personal access tokens + MCP server (read-only)

Groundwork for letting an AI agent drive your TVs: mint a personal access token and point an MCP client at your sets to read state, health, recordings and stats — each agent acting as the user who drives it (pass-through identity), never more.

  • A PAT resolves to a user through the same unforgeable live-role model as a session — it can't out-rank or outlive the user, and it works on every access-controlled endpoint, always filtered to what that user may see.
  • Self-service in Settings → Access tokens: create (shown once), label, revoke. Minting requires a real web session, so a token can't mint more tokens; "sign out everywhere" also revokes them.
  • The MCP server is a stateless façade that forwards your token to the relay — it holds no standing credential; the relay stays the single access authority. Read-only tools first; control tools are next.
v1.16.02026-07-06

Add debugging to any TV app (chii-inject)

The DevTools tab can now turn a customer's app into a debuggable one. If the running app has no debug client, it offers to add one — the box injects our debug client into the app's start page, re-signs the package with the site's certificate, and reinstalls it on the TV, then the DevTools attach as normal. The injected client self-activates on every boot, so it's a one-time step.

  • Available to every user who can use the set, not just superadmin — the pipeline runs box-side with the set's stored certificates, so the user never handles cert material. Certificate details stay superadmin-only.
  • Source package = the box's archived last-installed package (kept per set, swept after ~5 days) or an upload; installing through the Instalace tab now archives the package for reuse.
  • Live pipeline in the DevTools tab — inject → repackage → sign → push → install → launch → connect — each step shown with its status, with a plain-language reason and retry on failure.
v1.15.32026-07-05

Plug polling rides out sleepy WiFi radios

A smart plug on weak WiFi can stall its first connection for seconds; the agent's flat 4-second timeout killed exactly those requests, so energy polling failed and the log filled with an identical line every 20 seconds. Plug calls now use a generous timeout plus one retry, and failure logging is rate-limited.

  • The energy meter is cumulative, so even sparse successful reads keep the consumption history correct.
  • The emergency power-cycle path gets the same hardening — the last-resort recovery must not fail just because the plug's radio was asleep.
  • Log spam → one line on first failure, a summary at most every 10 minutes, and a "recovered" line when the plug comes back.
v1.15.22026-07-05

Power gauge reads correctly in light mode

The live-power W callout and gauge on the Dashboard cards and device grid were tuned for dark mode — in light mode the callout was a dark blob and the gauge's level dot vanished on the white card. Both now adapt to the theme; dark mode is unchanged. Purely visual.

  • The callout is a theme-aware surface pill: white with a subtle bordered pointer on light, dark surface on dark — readable either way.
  • The gauge's level dot uses a new theme token: a readable green on light, near-white on dark.
v1.15.12026-07-05

Dashboard recordings show the key-press count

Each recording in the Dashboard's per-device "Záznamy" strip now shows how many buttons were pressed during it — a small keyboard glyph + count on the right of each row, matching the recordings grid. The number was already captured; this surfaces it on the Dashboard too.

v1.15.02026-07-05

Generate the signing certificate on the box — no Certificate Manager

Screenwhere now mints the Samsung author + distributor developer certificates itself, straight from Samsung's certificate authority — the last manual step in getting an app onto a TV. The only thing left for a person is a one-time Samsung account sign-in. Proven end-to-end on the real office TV.

  • A "Vygenerovat certifikát" panel in the set detail's Instalace tab (super-only): sign in via a tiny helper, pick the privilege level (Public / Partner / Platform) and the TV DUIDs (auto-read over sdb), and click Generate — the certs are created and stored encrypted on the box, ready for the existing sign + install flows.
  • The box builds the RSA keys + CSR (TV DUIDs in the CSR), has Samsung's CA sign it, and assembles the .p12 with the vendored Samsung CA chain. Private keys never leave the box.
  • Samsung locks the sign-in redirect to localhost, so a thin operator-side helper (tools/samsung-token-helper.py) captures the token and hands it over through a code-gated, single-use relay endpoint; the token is never persisted or logged.
  • Caveat: a distributor certificate only signs apps whose privileges fit its level — a Public cert can't sign a partner-privilege app. Full reference: Cert-gen.
v1.14.22026-07-04

Install staging file no longer piles up on the TV

The package we push to the TV to install it now reuses one stable filename that each install overwrites, so it never accumulates — instead of leaving a new file behind every time.

  • This TV's sdb shell is locked to app-management commands (no remote file delete), so a timed cleanup isn't possible; overwrite-reuse keeps the footprint to a single small file.
  • The box's own local copy was already deleted immediately after each install.
v1.14.12026-07-04

App signing: the TV actually accepts our .wgt now

Our signature was cryptographically valid but every Samsung TV rejected it. Fixed — and verified on the real office TV, including re-signing another company's app with our own certificate so it installs.

  • Root cause: Tizen Studio signs with Apache Santuario, which stores the signature in a specific layout (newlines between elements, base64 wrapped at 76 columns, a compact properties object, no XML declaration) and signs over exactly that. We emitted compact XML; the TV's validator only accepts Studio's layout.
  • Fix makes our output byte-for-byte identical to Studio's for the same content + certificate (RSA signing is deterministic), so it's guaranteed installable.
  • Proven end-to-end on a UE43NU7192: a foreign app stripped and re-signed with our certificate installs and launches; unsigned and tampered packages are correctly rejected.
v1.14.02026-07-04

Install onto the TV, straight from the app

The set detail's "Instalace" tab now installs a signed app onto the Samsung TV over the box — no operator laptop in the loop — instead of only signing it for a manual install.

  • The box pushes the signed .wgt to the TV over the vendored sdb, installs it, and optionally launches it, reporting each step (connect → push → install → launch).
  • A "TV DUID" read-out hands you the value to enrol in Samsung Certificate Manager when generating the distributor certificate.
  • Super-only and audited; runs entirely box-side (a multi-tenant-service requirement).
v1.13.12026-07-04

App signing: match Tizen Studio's algorithms

Aligned the signer to what current Tizen Studio emits — SHA-512 digests, RSA-SHA512, exclusive canonicalisation — validated against two real Studio-signed reference packages.

v1.13.02026-07-04

App signing (Tizen .wgt) — get our app onto the TV

A new app-sign plane: sign a Tizen install package with the site's own author + distributor certificates so a TV accepts it. Set detail → "Instalace" tab, super-only.

  • Pure-Node signer runs on the box; the certificates are stored encrypted at rest and never leave the site's network.
  • Relay endpoints are super-only and audited; private keys transit only in-flight, never persisted on the cloud.
v1.12.22026-07-03

Dashboard power reading moved left of the bar

The live-power watt reading on the Dashboard device cards now sits as a callout to the left of the gauge, matching the device grid.

v1.12.12026-07-03

Bigger, status-tinted TV thumbnail

The device list and Dashboard device rows get a larger TV thumbnail, tinted by status.

v1.12.02026-07-01

Left-edge live-power gauge on device cards

The device cards' power indicator is redesigned from a mini sparkline into a glowing left-edge gauge driven by live wattage.

v1.11.02026-07-01

Live-power sparkline on device cards

Device cards on the Dashboard and the "all devices" picker show a mini power trend from the smart plug's live draw.

v1.10.02026-07-01

Energy: consumption history, cost & live power

Screenwhere now tracks the smart plugs' energy itself — all local, nothing goes to the cloud — with a super-only consumption/cost panel and an on-demand live-power sparkline on the set detail.

v1.9.02026-07-01

Shelly plug goes live: emergency power-cycle

The "Restartovat napájení" feature — a hard power-cycle of the TV's wall socket via a Shelly Plug M Gen3 — verified on real hardware, with SHA-256 HTTP Digest auth.

v1.8.22026-07-01

Záznamy tab polish: author, durations, action icons

The set-detail Záznamy tab reads cleaner: your own recordings no longer repeat your name on every row, zero-second durations are trimmed, and the Přehrát / Scénář actions got proper icons with a unified hover.

  • Author is shown only when it's not you: your own clips drop the repeated name, a teammate's clip gets a colour-coded dot + name so it stands out. Applies to the Záznamy tab and the recordings overview.
  • Tidier durations: 10 min 0 s10 min (the trailing 0 s is dropped on a whole minute).
  • Action icons: a play triangle on "Přehrát", a film clapperboard on "Scénář"; on hover both scale up together and the clapperboard opens its arm with a smooth ease-out (reduced-motion respected).
  • The per-session recording pill on the camera stage is now top-aligned with the fullscreen button.
v1.8.12026-06-30

Loading skeletons on Zařízení + Záznamy

The device list and the recordings overview now show themed shimmer placeholders while they load, matching the Dashboard — so opening them reads as "loading" instead of looking frozen.

  • Zařízení (device picker): shimmer placeholder rows/tiles while the device list loads, instead of an empty gap on a cold open.
  • Záznamy (recordings overview): the same shimmer treatment replaces the bare "Načítání…" line; honours the list/grid toggle.
  • The skeleton primitives are now shared across the Dashboard, picker and recordings; reduced-motion is respected.
v1.8.02026-06-30

Per-session recording control + a faster Dashboard

Turn recording on/off per session on the live screen (keep or delete what was captured), and a Dashboard that loads with a skeleton, never flashes the device list, and re-opens instantly.

  • Per-session REC pill on the live camera stage (holder toggles, viewers read-only) on top of the set's recording master; a per-user default in Nastavení ("Nahrávat má sezení").
  • Stop-and-delete: turning off mid-session offers keep / delete this clip / keep recording.
  • Dashboard: shimmer skeleton on first load; no device-list flash on open (shows the dashboard immediately); instant re-entry via a stale-while-revalidate cache (chart selection preserved).
  • Camera on UDP-blocking networks: committed the MediaMTX public-IP WebRTC fix to the repo + added TURN-over-TCP so the stream relays over TCP where UDP is blocked (TURN-over-TLS :443 deferred).
v1.7.02026-06-30

Set-detail Záznamy tab UX (+ live-camera WebRTC fix)

The review reaches the live detail's recordings tab — it loads reliably, plays through the Review player, navigates consistently — plus a real live-camera fix.

  • Záznamy tab loads reliably (deep-link WS race fixed); "Přehrát" opens the Review player (camera stays live); in-app and browser Back both return to the detail.
  • Scénář target picker redesign (source device highlighted, "stay here"); humanized copy + clearer day separators.
  • Live-camera fix: MediaMTX advertised 127.0.0.1 as its WebRTC ICE candidate → media never reached the browser; bound the WebRTC UDP socket to the public IP. Clearer "network may be blocking the stream" failure message.
v1.6.02026-06-29

Set-detail UX review pass

The screen-by-screen review reaches the live set detail — a responsive remote, plane-editor fixes and a perf win.

  • The remote collapses to icon-only below 340px (CSS container) so labels never spill the camera-resized column; a tooltip on every key.
  • Perf — dropped the per-frame backdrop blur over the live video; plane-editor fixes (edit-handle clipping, backdrop-to-close, name only in edit mode).
v1.5.02026-06-29

Picker UX review pass

The review reaches the device picker — lots of affordances, plus standalone device editing.

  • Edit a device without connecting to it (standalone form, no live WS); release a held device from the picker (new presence/release).
  • A "Zobrazit" field selector, an ASC/DESC sort toggle, and cross-screen card unification (shared TV thumbnail + OS ribbon).
v1.4.02026-06-29

Dashboard UX review pass

The review reaches the Dashboard — lots of polish, plus two real bugs.

  • Critical fix — a $q helper recursed and crashed every set detail since 1.3.0; the recording "3274 min" duration bug fixed; ★ double-toggle fixed.
  • Dashboard polish (Czech plurals, a single version source, crisp chart labels, most-used redesign) + a colored live-egress utilization gauge.
v1.3.02026-06-29

DOM-heavy web typing

Finish the type-checking pass on the browser code — and the type checker earned its keep.

  • All web/js modules now @ts-check-green (CI-guarded).
  • The type checker surfaced + fixed 4 real latent bugs (Teams, DevTools, bulk-install, curSet) — all from a shadowed i18n t().
v1.2.02026-06-29

Deep relay split

Break the 1.9k-LOC relay.mjs into focused modules — same behavior, now safe to evolve.

  • relay.mjs 1874 → 92 lines: a thin bootstrap wiring 13 focused modules; shared state mutated in place (no reference rewrites).
  • Behavior-identical, verified per step (test suite + typecheck green). Deploy note: now 14 .mjs files — scp them together.
v1.1.02026-06-29

Quality pass: tests, security, types & CI

Prove it works, harden the public relay, and stop the bug class at the source — no rewrite.

  • One-command test suite (16 → 24 suites, 462 assertions); security hardening (6 findings fixed, F1–F6); dev-only tsc --checkJs types.
  • GitHub Actions CI on push + PR; leaf-module extraction. Architecture review verdict: keep the lean stack, no rewrite.
v1.0.02026-06-28

First documented release

Everything that was built is now written down — and versioned.

  • Restructured docs, this visual documentation site, the full changelog, and an in-app "What's new" link.
  • Retroactive version history + tags. No behavioural change — this release is about being understandable.
v0.12.02026-06-28

Hardening & polish batch

Manual MAC entry, burned-in QA video, a live bandwidth gauge, safer installs.

  • Editable MAC fields; burn-in video export; super-only live egress panel.
  • Idempotent provisioning; cache-control + ETag (no stale assets after a deploy).
v0.11.02026-06-27

Dashboard (Přehled)

A glanceable home with favourites, recent recordings and usage charts.

  • Default home + Phase-2 stats backend (real charts); clean English URLs; zero-token controller auth.
v0.10.02026-06-26

Bilingual (cs/en)

The whole app speaks Czech and English, switchable live with no reload.

v0.9.02026-06-26

Recording, replay & storage

Record a session, replay the exact key-presses, keep storage in check.

  • Local on-demand recording with a synchronised key-track; review player; "Scénář" replay onto a live TV.
  • Per-user storage quota + disk-reserve eviction (★-kept never touched).
v0.8.02026-06-25

Audit, health, power & on-demand camera

A trustworthy log, offline alerts, a remote power-cycle, and a camera that only streams when watched.

  • Structured audit log + usage stats; set-health + offline alerts; Shelly power-cycle.
  • On-demand camera push (viewer-gated + 2-min linger) — the fix for bandwidth at ~20 sets.
v0.7.02026-06-25

Authentication hardening

Real, tamper-proof logins with two-factor and trusted devices.

  • Server-minted session tokens (unforgeable role), scrypt passwords, TOTP 2FA for everyone, 30-day device-trust.
v0.6.02026-06-25

LAN discovery, MAC identity & smart power

Find devices on the network, follow them by MAC, one-button TV power.

  • "Find on network" scans; the registry keys by MAC; the agent self-heals a drifted camera IP.
v0.5.02026-06-24

Device registry write API

Add and edit devices from the app, with the box configured from the cloud.

v0.4.02026-06-23

Teams, accounts & takeover

Shared access with teams, real accounts, graceful hand-over.

  • Server-side team access + WHEP gate; passwords; takeover rework; the Raspberry Pi as-built.
v0.3.02026-06-22

The product UI

The app gets its real look and is wired to the live system.

  • The KAC Design System prototype, then the real web app wired to the live camera / control / debug.
v0.2.02026-06-20

Multiple sets & provisioning

Run many TVs from one app and one box per site.

  • Camera push folded into the agent; per-set tokens; a set registry + web picker; systemd per-set instances.
v0.1.02026-06-19

Proof of concept + one-set MVP

Control a TV on a foreign network, with live video and debugging, from anywhere.

  • The cloud control relay; one set with WHEP camera, a remote pad and a DevTools deep-link. Verified from a phone on cellular.