107 Commits
Author SHA1 Message Date
spoopyghosty0 818814c693 Install links: manifest version + update check at start
A manifest may name its build's version (top-level "version" or
"frameport": {"version"}). FramePort keeps it with the game's link record
and, at start (at most every 6 h), fetches remembered web manifests again;
a newer version marks the game link_update. The Library bar and the game
page offer to download and install it (one confirmation, same download path
as a first install, a name the user gave is kept). CLI: frameport
link-updates [--install]. Docs: INSTALL_BUTTON.md.
2026-10-17 01:12:06 -04:00
spoopyghosty0 d0dbbe32e5 Save backups for Quest games (discussion #352)
Agent v94: _backup_saves / _restore_saves stream a tar.gz of a game's internal
and external Android storage over the SSH exec channel (caches, IL2CPP and
shader caches, FrameBridge files, videos/OBBs and files over 64 MiB left out;
manifest with sha256 last). File work runs in podman unshare, holds the
launch lock and is refused while the game runs. Restore treats the archive as
untrusted (confined paths, no links/devices, size limits, hashes checked,
staged first), keeps the replaced saves as saves-before-restore.tar.gz (undo)
and puts them back if swapping fails.

PC: install/saves.py (save-backups/<pkg>/, keep 10 by default), game menu
section Saves (back up, restore..., open backups folder), Frame page
"Back up all saves", back up before deleting saves on uninstall, Settings ->
Data keep count, CLI frameport saves backup|list|restore|undo.
2026-10-16 11:47:07 -04:00
spoopyghosty0 6101314ce1 Library folders list + scans relink moved games (discussion #351)
Settings -> Library folders lists the folders Rescan scans (scan.roots: status,
games from each, remove, Add folder...), CLI frameport folders / rescan.
A scan that finds a library game elsewhere whose own files are gone and that
is the same build points the entry at the new place (title, tags, art, user
recipe, builds and installs stay); another version is taken only by Scan a
folder... (as Update with a newer APK/version), Rescan reports it; a second
copy leaves the existing one. A full scan no longer overwrites a user recipe.
2026-10-16 10:37:11 -04:00
spoopyghosty0 960da4469b Announcements: a one-time pop-up in the app
Source of truth: announcements/current.md on main (front matter: id, title,
date, optional image/button/expires; Markdown body), read from raw GitHub every
6 h like the catalog, so a push reaches users without a release. Ships
switched off (active: false).

Shown only to people who used FramePort before the announcement's date, never
on a first start; any close (X, click outside, Escape) dismisses that id for
good, and a new announcement = a new id. Centred over the content area, right
of the sidebar. announcements/README.md explains the file;
ui_smoke.py --announcement renders it.
2026-10-16 09:23:46 -04:00
spoopyghosty0 39e1762e53 Game menu: Update with a newer version… for Linux apps and PC VR games (#161)
Follow-up to "Update with a newer APK…": Linux apps take their new AppImage/archive or folder (a file or a folder
picker, like the app was added), PC VR games their new game folder. pipeline.replace_linux_app / replace_rift_game
re-run the add path (add_linux_app(replace=) / add_rift_game(replace=)): the entry keeps its package (so the Steam
shortcut), name, artwork, tags, switches and the user's recipe; a folder keeps the program chosen before if it's still
there, else FramePort picks again (and asks, for ambiguous PC VR games). A name that looks like another app's is
allowed with a heads-up.

The entry gets a new source_id, recorded in the deployment's recipe at install (no agent change):
components.source_replaced shows the Frame copy (and a PC install, pc_outdated) as outdated until then. Such an
install also re-sends same-size files changed after the last install (installer.changed_since), since uploads
otherwise skip files the Frame has with the same size. The old PC VR folder is left out of later scans
(replaced_dirs). CLI: frameport new-version <game> <path> (APKs too). Docs: INSTALL.md.
2026-10-15 20:10:50 -04:00
spoopyghosty0 3d260dccb1 OpenSSH uploads log in like the Frame connection; SSH key files in Settings (#242)
A Frame that only authorizes the user's own key refused OpenSSH, which
offered FramePort's keys alone (IdentitiesOnly), so every upload fell back
to Paramiko. The connection now records which login worked (Frame.auth):
after a login with this PC's own keys/agent OpenSSH also offers private
copies of ~/.ssh/id_* (OpenSSH skips its defaults once -i is given) and the
agent; a refused strict login is retried that way once. FramePort doesn't
add its key to the Frame for this.

New: Settings > Installing > SSH keys (library ssh.extra_keys, CLI
frameport frame ssh-keys): extra private key files for the connection and
OpenSSH, checked with Paramiko (passphrase keys: use the agent). OpenSSH
runs with -v and a refused login's error lists the keys offered and its
warnings. whoami's SID is read from bytes (non-ASCII user names).
Checked with Windows ssh.exe/sftp.exe against the Frame.
2026-10-15 19:58:58 -04:00
spoopyghosty0 a5a86e591d eval_heuristics: adapter/device picks a catalog recipe inherits aren't errors in the dated score 2026-10-15 19:07:45 -04:00
spoopyghosty0 7cd78cfa26 eval_heuristics: date-aware score (patches newer than the entry's verified date are catalog gaps, not errors); fit report dates by verified date 2026-10-15 19:07:44 -04:00
spoopyghosty0 0599e55831 eval_heuristics: catalog truth goes through the 32-bit bridge / 2D-app transforms like suggest(); floors from the full snapshot 2026-10-15 19:07:39 -04:00
spoopyghosty0 2e83970ad5 Add catalog-only heuristics scoring and new patch fit reports 2026-10-15 19:07:38 -04:00
spoopyghosty0 33a778c195 scripts: heuristics_snapshot.py saves dump analyses as JSON for heuristics evals 2026-10-15 19:07:37 -04:00
spoopyghosty0 aeabb3df5a Uploads: stream small-file tar batches over OpenSSH too, Paramiko fallback (Discussion #242)
Only big files (>=8 MiB) used OpenSSH; games made mostly of small files stayed on Paramiko and were slow on a fast
link. native_upload.put_tar streams the same tar batch through system ssh with the paired key and pinned host key;
on any failure the install falls back to Paramiko's put_tar and logs it, and doesn't retry OpenSSH for later batches.
The job log now names the transport for each part. upload_bench gains app-small / paramiko-small comparisons.
2026-10-15 00:46:11 -04:00
spoopyghosty0 22413e21e9 Showcase: the fake Monitor history gets exact 1 s steps (smoothed numbers differed between renders on slow runners) 2026-10-14 20:47:31 -04:00
spoopyghosty0 3fcb901f8c Prefer OpenSSH for bulk uploads with resumable Paramiko fallback 2026-10-14 10:28:55 -04:00
spoopyghosty0 faea99e837 Skip unnecessary game update work and add bounded parallel uploads 2026-10-14 10:09:24 -04:00
Lucas-Mathieu 697e96c35f Add experimental shared foveation and Batman video/loading fixes 2026-10-13 01:50:29 +02:00
spoopyghosty0 1c02ebe1d3 Lint: line lengths, unused import; scripts/upload_bench.py (upload speed by method, discussion #242) 2026-10-12 18:15:09 -04:00
spoopyghosty0 cf18b30be9 Uninstall dialog: the delete-saves checkbox label wraps (it ran past the dialog; also the delete-files-on-this-PC, share-recipe and remove-FramePort checkboxes), games uninstalled from the Frame page show their name; Monitor: the agent's own workers aren't a running game (uninstalling showed the game as now playing while Steam restarted; agent v80) 2026-10-11 14:33:47 -04:00
spoopyghosty0 9149eaecd7 Patch reference: scripts/patch_docs.py generates docs/PATCHES.md (every patch and game setting, anchors per id) and the site's id map; --check test, catalog PRs regenerate it 2026-10-11 00:58:51 -04:00
spoopyghosty0 9016982394 Showcase: the pretend setup server has open_asks/prune like the real one (the install tutorial failed to record); test that it offers everything the GUI uses 2026-10-10 23:25:14 -04:00
spoopyghosty0 a5820e68d7 BlazeRush works (GitHub #57, from Klownicle's guide; headset check pending): frame.blazerush (1.0.349 only, fingerprint + words checked: avatar wait NOP, avatar renderer RET, shader cache cache/br_shader_v1), device.config_sync (agent v78: saved resolution in user_config.xml from the runtime eye size x scale at finalize, settings changes and before each start), catalog recipe with the GL shim fixes and scale 1.7, triage vrapi-fov-before-tracking / gl-integer-attribs 2026-10-10 23:18:42 -04:00
spoopyghosty0 9781c2bb6b Showcase: sharper README teaser (1280 px, 20 fps, WebP drawing preset from quality 95, up to 8 MB) and tour/install videos (CRF from 17, up to 20 MB); the teaser showed compression artifacts 2026-10-10 22:32:47 -04:00
spoopyghosty0 8accd6221a Install links: the homepage's example button is a demo link (frameport.app/demo/cool-game.json, published on the site). FramePort recognises every form of it without any network request and answers with a demo install question for "Cool Game"; Install tosses its cover out of the logo's portal (easter eggs off: "Nice, it works!"), nothing is downloaded or added. The landing page marks it as a demo link; open-link says it's the demo and stops 2026-10-10 21:47:57 -04:00
spoopyghosty0 35462324bb Docs: shorter, consistent copy
README is a short overview that links to the install guide; repeated
sections are said once; Share working recipe everywhere; the setup
script names the real buttons; a shorter notice, the same as the site's.
2026-10-10 16:34:19 -04:00
spoopyghosty0 2c1149815d App: shorter, consistent interface text
Dialogs, help texts and tooltips cut to what the user does or gets, one
name per action (Start setup, Uninstall FramePort, Update art on Frame,
Rebuild only), plain launch-test results (C.test_result), plain summaries
for every Game setting. The setup line now points at frameport.app.
2026-10-10 16:34:17 -04:00
spoopyghosty0 7e95c50f39 Game page: open the last launch test's log
The Steam Frame (or This PC) card gets a log button when the game's last launch test ran there: it opens the
saved log (last_test.log_path) in the launch log viewer. The showcase's demo library writes a short made-up log for
each passed test so the button shows in the docs pictures.
2026-10-10 14:44:54 -04:00
spoopyghosty0 60d8ab021b Install walkthrough: the setup line and Allow, in the video and on the site
- Install tutorial video (docs/showcase/videos/install.yaml, re-recorded by record_video.py): Start setup, a card
  for the setup line on the Frame (with the Developer Mode route in its note), the Frame asking and Allow clicked,
  a card for what the Frame then does by itself. Showcase: the fake pairing server takes asks; hook `frame_asks`.
- Site: the Install section is a six-step walkthrough (download, Start setup, the line or Developer Mode on the
  Frame, Allow, it sets itself up, add games and play), each step with a small live scene: the download unpacks,
  Start setup shows the line, Konsole types it and finds FramePort, Allow moves on, the setup ticks off, a game
  installs. The other ways link to /setup.
2026-10-10 10:16:13 -04:00
spoopyghosty0 351712172c Setup URL: the Frame finds FramePort, you allow it, the usual setup runs
One line for every Frame and PC, served by the project page: curl -sL spoopyghosty0.github.io/frameport/s | bash.
- bootstrap/setup.sh finds FramePort (stdlib mDNS on 5353 for _frameport-pair._tcp; else the USB cable's address and
  a /24 scan of /ping), asks with /hello, shows 4 digits, waits for Allow, then runs exactly the typed line's
  `curl -fsS <pc>/<code> | bash`: bootstrap.sh is unchanged.
- PairingServer (no second server): /ping, /hello (max 3 open asks), /wait (long poll; code only once allowed),
  decide(); announces itself over zeroconf while running (name, port, two words for this PC's key; never the code).
- Connect page: "Start setup" shows the static line, incoming asks as Allow / Deny cards (a toast elsewhere);
  "Use the setup command" keeps the address + code line; USB setup unchanged.
- The app key is created under a lock (the announcement and the page asked for it at once on a first start).
- Tests: approval/deny/limits/timeouts, the code never in the announcement, setup.sh end to end against a real server
  with a stub bootstrap (allowed and denied), digits match. ui_smoke --setup-url drives the page.
Checked on the dev Frame with a stub bootstrap: found by mDNS and by the scan fallback, allowed, code handed over.
2026-10-10 00:23:06 -04:00
spoopyghosty0 82e2a40791 Project page round 2: app demo, livelier hero, comparison, sharper videos
- "Meta Quest" only as a description (headline about the Steam Frame), trademark line in the footer.
- Hero cards: generated covers in the style of the app's placeholder art (hue from the title), the same app icon on
  both sides of the portal, a one-line APK name, stage ticker and progress, Play ripple, sparks; titles sized so
  words never break. Click the portal for the next game; the scene leans toward the pointer.
- "The app": a clickable, simplified FramePort window (Library, game page with patch switches, Steam Frame, Files,
  Live view, Type on Frame, Monitor with live sparklines) and a guided tour from a fresh start: connect the Frame,
  scan a folder, install, play. The showcase videos play in a lightbox.
- Compared: FramePort, FrameDrop and Valve's own tools, from their own pages (checked 2026-10-09); the same table in
  the README.
- record_video.py --size WxH (e.g. 2560x1440): films at that many device pixels; postprod reads the size at call
  time and draws cards at the 1920 layout scaled up. Default output unchanged.
2026-10-09 21:26:37 -04:00
spoopyghosty0 27694243f2 Screenshots: copy an image to the clipboard; holiday showers; perfect pacing stays
- Screenshots tab: Copy image (a button on the card under the pointer, the right-click menu and the viewer) puts
  the full-size screenshot on this PC's clipboard (core/clipboard.py: Windows/WSL PowerShell bitmap + file,
  macOS osascript, Linux wl-copy/xclip; Flet's set_image in web mode)
- Clicking a holiday badge showers the window: New Year confetti, winter snow, Valentine hearts, digits of pi,
  clovers, hopping eggs, bouncing pumpkins (moved like the confetti: one page update per phase)
- Perfect pacing after 2.5 minutes on target; the Perfect pacing pill and line stay until the rate slips
- Full charge: only the ring pulses (with a glow behind it), the percentage stays put
- Upload hop a little slower
2026-10-09 15:59:02 -04:00
spoopyghosty0 2480d203ec Easter eggs: portal toss, more holidays, perfect pacing, self-search, hello, full charge, upload hop, on air
- Seven logo clicks: the portal glows and rings, a cover is tossed out and falls across the window on a random arc
- Holiday badges dance on hover; new: Valentine's, Pi Day, St Patrick's, Easter weekend, April 1 (upside-down
  logo until hovered), New Year's party popper
- Monitor: a minute within 0.5 fps of target turns the fps chart into the portal gradient
- Library search "frameport": the logo spins and says "That's me!"
- Type on Frame: typing "hello" makes a headset wave (keys still go to the Frame)
- The battery ring sparkles on reaching 100 % on the charger
- Uploads over five minutes: the transit cover hops now and then
- Live view tab: a blinking ON AIR sign while streaming
- Showcase hooks perfect_pacing, battery, long_upload, press_keys
2026-10-09 14:18:08 -04:00
spoopyghosty0 c4983b6e18 Easter eggs; the Settings source link sits on the left
- ui/easter.py: seven quick clicks on the sidebar logo send a game's cover out of its portal for a somersault over
  the wordmark; the Monitor's frame rate has a mood tooltip; holiday badges on the logo (a pumpkin Oct 24-31, a
  snowflake Dec 20 - Jan 2); install milestones (confetti in the portal colours and a message at the 1st, 10th and
  100th different game on the Frame). Milestones show once ever: they count different games (updates and
  reinstalls don't), are stored as shown before they're shown, and the first use seeds from the games already on a
  Frame (no "first game" after an app update). Off with setting ui.easter_eggs; Reduce motion keeps the words only.
- C.kv puts control values on the left; C.link (no indent) for the About page's source link.
- Showcase: the demo library has the easter eggs off; steps hooks easter_eggs / fps_dip and a clicks action;
  record_video.py takes a storyboard file path (demos outside docs/).
2026-10-09 13:39:42 -04:00
spoopyghosty0 13736d2524 Showcase: placeholders in the setup command; Monitor charts grow live on film
- The install tutorial (app screen and card) shows curl -fsS <your-PC-address>:8765/<one-time-code> | bash, so
  nobody takes the example for their own command; the card says FramePort fills them in.
- Videos: opening the Monitor tab no longer replays two minutes of samples (its charts raced to catch up); the
  charts start there and grow live, as in the app. The pretend frame rate holds ~72 fps with a short, shallow dip
  now and then (it sat at 55-61 fps for seconds). Both videos re-recorded.
2026-10-09 11:20:13 -04:00
spoopyghosty0 a069c79c73 Showcase: any number of videos (docs/showcase/videos/<name>.yaml), plus an install tutorial
- record_video.py replaces record_tour.py: one storyboard per video with its output, a start state (demo or
  fresh profile, Frame connected or not, start page, live or frozen stream), setup steps before filming, filmed
  scenes and instruction cards (eyebrow, heading, numbered steps, a command, a note) for what happens outside
  FramePort. --changed-since records the videos whose storyboard (or the recorder) changed; several videos record
  in separate processes.
- videos/install.yaml: download, first start, the welcome screen's tool download, the setup command, the command
  on the Frame (card), connected, scanning a folder, installing and playing a game (81 s).
- demo_home profile "fresh": FramePort's first start (empty library, welcome screen, nothing on the Frame); its
  games wait for the pretend folder scan. Fakes for a first start: no Frame discovery on the real network, the setup
  command shows the docs' example address, a pretend tool download, steps hooks first_run and pairing_done.
- CI records changed (or named) videos; releases attach every docs/media/frameport-<name>.mp4 as
  FramePort-<name>.mp4. INSTALL.md and README link the tutorial; SHOWCASE.md documents the storyboard format.
- Cards are opened by their title in both videos (a card's middle is its Install button).
2026-10-09 10:37:22 -04:00
spoopyghosty0 0a2dbc3140 Library: a clear divider under the On your Frame shelf; showcase: in-game screenshots, verified renders
- Library: an "All games" label and a 2 px rule (blue→orange in Portal themes) between the shelf and the grid,
  hidden with the shelf.
- The pretend Frame's screenshots are the games' own in-game store screenshots (never cover art), installed games
  first; the running demo game is BONELAB (it has screenshots, so Take screenshot adds a real one).
- render_docs: a shot whose steps fail is retried in a fresh window and never written (a window that didn't connect
  had produced an empty Library); every shot is rendered twice in fresh windows and kept only when both agree (a
  misdrawn glyph, "7.θ W", slipped through once); changes from 0.04 % of the pixels count (the sidebar card
  showing another game is 0.12 %, run-to-run noise under 0.007 %). --fast renders once.
- record_tour: SHOWCASE_DEBUG=1 prints each step's start time. Docs images and the tour re-rendered.
2026-10-09 08:44:25 -04:00
spoopyghosty0 f7108c0ca4 Fix: a page change could die when a background thread set a control property mid-update
Flet packs a patch by iterating each control's _values dict; Prop.__set__ adds/removes keys there without a lock.
A property set on a background thread (the live Frame card's sample, job progress, the library loader) while the
event loop packed a patch raised "dictionary changed size during iteration" and the update was lost (seen in CI:
a sidebar click left the old page showing). serialize_flet_updates() now makes property writes take the same
re-entrant lock as patches (app.FLET_LOCK). Reproduced with Flet's own encoder: 16 failures in 4 s before, none
after; test_property_writes_wait_for_a_patch_being_packed. The showcase server and ui_smoke serialize like the app.
Tour re-recorded with the fix (clean, no fallback clicks).
2026-10-09 08:04:55 -04:00
spoopyghosty0 222633e462 Showcase: nav step (sidebar click checked against the page that shows; falls back to opening the route when a click's handler died in a Flet update race seen on CI); CI renders with Python 3.12 2026-10-09 01:08:16 -04:00
spoopyghosty0 f64c93b338 Showcase: every shot waits for its own page before the picture (CI caught Monitor still showing the warm-up page); settling needs 4 identical frames 2026-10-09 00:55:07 -04:00
spoopyghosty0 e35170d4f9 Showcase: CI workflow, release asset, docs; screenshots and the tour rendered from the demo library
.github/workflows/showcase.yml renders the docs screenshots on UI pushes to main (and the tour when its storyboard or
scripts change, or on Run workflow), keeps every render as an artifact and opens/updates the PR showcase/update
when a picture visibly changed. Releases attach the tour as FramePort-tour.mp4. docs/SHOWCASE.md explains running
it, the step language and the CI flow; README opens with the teaser (link to the full MP4). Renders refuse
placeholder art (--allow-missing-art). tests/test_showcase.py: demo library, privacy scan, image threshold,
manifests (every docs image has a shot), step validation, element picking, pointer paths, ffmpeg graphs.
scripts/demo_record.py is replaced by the tour.
2026-10-09 00:37:39 -04:00
spoopyghosty0 80a5c0b6d6 Showcase: scripted demo tour (docs/showcase/tour.yaml -> record_tour.py)
One window on the demo library, driven by name like a person would (drawn pointer, eased curved paths, a ring on
each click), filmed with Chrome's screencast (1080p, constant 30 fps from the frame timestamps), then captioned
(lower thirds in the app's colours), given title/end cards and crossfaded by postprod.py (ffmpeg only). Writes
docs/media/frameport-tour.mp4 (+ poster, best quality within 12 MB) and the README teaser docs/images/tour-teaser.webp.
Installs in the tour run a scripted job through the real queue; --draft for quick storyboard checks, --names lists
the clickable names. Element finding prefers the innermost match (a list's text includes its rows').
2026-10-09 00:22:19 -04:00
spoopyghosty0 63f0125954 Showcase: demo library + docs screenshot renderer
scripts/showcase/: the fakes split out of ui_smoke (the pretend Frame now reads the demo library's state, fixed
dates, a Take screenshot fake, a frozen monitor stream for stills), web.py (the GUI in web mode driven by name
through Flutter's semantics tree, drawn cursor, eased pointer paths, settle detection, one browser per window),
steps.py (the step language shared by screenshots and the tour), demo_home.py (21 catalog games from
docs/showcase/demo-library.yaml + demo-analyses.json, art and store details fetched once and cached) and
render_docs.py (docs/showcase/shots.yaml -> docs/images, rewritten only on a visible change; --check for CI).
2026-10-09 00:00:13 -04:00
spoopyghosty0 6782ace43d Live view: status and actions on one row, Play in/Quality/address on a second (one row squeezed the status into single words); a bottom gap keeps the in-window player off the window edge; while it plays in the browser or mpv the stage says so with Open in browser; ui_smoke renders the streaming bar with a local test stream 2026-10-08 21:48:11 -04:00
spoopyghosty0 9b3c3b330a Cover buttons: play arrow centred (Material's glyph is already optically offset), smaller (60 px cards, 48 px shelf); ui_smoke shoots the Install/Update cover buttons 2026-10-08 20:22:46 -04:00
spoopyghosty0 46d8c5c379 demo_record: cards clip (hover, Play, launch rings, game page Play/Install); ui_smoke's pretend Frame can launch games 2026-10-08 20:15:12 -04:00
spoopyghosty0 76542b53e5 Library cards: hover lift, art zoom and scrim; frosted Play/Install button that fills on hover; launch animation (portal rings + Starting on Frame…); filled Frame glyph on solid buttons
C.CoverButton (grid + shelf), library.card_hover/hover_motion/scrim, game page Play shows Starting on … with a spinner; glyphs.solid + frame-solid.svg for primary buttons; reduce motion skips lift/zoom/rings. ui_smoke: card hover, button hover and launch frames.
2026-10-08 20:05:15 -04:00
spoopyghosty0 4a511ca6e3 Merge origin/main into ui-refresh
Main's microSD drives (agent v63), Linux app icons (agent v64), OBB detection, re-analysis and catalog entries,
on top of the UI refresh. Our monitor modules ({"modules": [...]}) move to agent v65 (MIN_AGENT_MODULES = 65).
Main's new UI uses the refreshed components: "Move to…" is in the game page's sectioned menu (MenuState.movable),
the .obb question and the Move dialog use C.dialog, the Frame page's drive picker C.dropdown, line-style icons.
2026-10-08 17:01:24 -04:00
spoopyghosty0 35da9c569a Updates show a changelog: every version since yours in the update dialog, What's new after updating, Settings changelog
updates.fetch_changelog (GitHub releases list, cached 6 h, offline-safe), whats_new() strips the release footer, changelog_for/recent_history/pending_news (setting update.last_seen_version); update dialog lists each version (newest open), Library bar after an update, Settings -> Updates What's new...; ui_smoke --update uses fake changelog data.
2026-10-08 16:56:10 -04:00
spoopyghosty0 b20591540b scripts/demo_record.py: record short clips of the real GUI's animations (transit, live card, navigation, selection, themes)
- One Playwright context per clip (record_video), web mode with ui_smoke's fake Frame; MP4 (x264) + poster JPG via ffmpeg, blank start trimmed
- ui_smoke: install_fakes()/attach_fake_frame() factored out so the recorder reuses them (behaviour unchanged)
2026-10-08 15:40:40 -04:00
spoopyghosty0 676b025c5b ui_smoke: steps wait for their screenshot and mouse actions (names always match what they show)
- driver/shooter hand-off: the driver runs a step, signals ready, then waits (240 s max) for the shooter's screenshot + mouse actions before the next step; fixed STEP_SECONDS and the compensating sleeps (library-typed/-cleared, --hover, --gestures, --links) removed
- new step library-shelf-hover (the shelf's first card hovered: its Play button); hover positions per step
- mouse actions that save their own picture (menus) skip the duplicate -mouse shot; the -mouse shot waits the paint delay (typed search showed half-faded results)
- --links keeps the 1440x900 Add games click; --gestures positions updated for the shelf, fake screenshots keep the same day layout at any hour
2026-10-08 00:10:09 -04:00
spoopyghosty0 fac72d17c8 Setup checklist with inline fixes, gentle motion (Reduce motion setting), Settings index
- C.checklist/C.Check replace status_row: Frame 'Ready to play' (Install Lepton, Install Proton, Test, Restart the Frame when kernel keys run high), Settings Tools (Install per missing tool) and This PC
- Views fade in (150 ms AnimatedSwitcher, same-route redraws swap in place); the wordmark portal pulses once on connect; Settings > Appearance 'Reduce motion' (ui.reduce_motion) turns both off
- Settings: fixed index column (Appearance first, Remove FramePort last) that scrolls to sections; ui_smoke step settings-index
- Tests: checklist items, section order, reduce-motion default
2026-10-07 23:49:39 -04:00