The fix review found the Games reload added to showPage() read `link`, which
the page declares later: opening Frame Control at #games, #library,
#sideloaded or #getgames threw at startup and skipped the rest of the setup.
And the iPhone app never watches the connection, so `link.s` stayed null and
titles still never reloaded there.
The reload now runs only when navigating to Games (the hashchange), where the
first load is already done, and doesn't depend on connection state; a failed
load shows its error in the list as Refresh did. tests/test_page_startup.py runs
the real showPage() at every page and section link with everything declared
later still uninitialised, and fails on the old line.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From the independent review of this PR:
- Phones lost the Refresh button, and nothing else reloaded sideloaded titles,
so one added or removed from another computer never showed. Opening Games
now reloads them.
- Send files... on Home uploaded with its progress bar in the hidden Tools
page, and phones have no Activity bar. Choosing files from Home now opens
the file panel on Tools.
- A session that finished while the page was open left its settings (1 / 0 / 0
after a test) in the form. The form goes back to its defaults when the
session ends. The test now covers that, and its mock uses the page's real
break default (20 minutes, not 30).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With one more button in the header the pill lost its route at a few widths
(970-1020, 1160-1180 and 1350-1410 px). The header's spacing now tightens below
1500 px, the shortcut hints go at 1200, and the compact header starts at 1030,
so the pill reads "Connected · Tailscale" in full at every width above that,
including the 1400 px default window.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Home answers "what's my headset doing, and what do I want to do now". A
Right now card says whether SteamVR is running and holds the volume and the
next things people do: cast to this screen, play a video or photo, show a
Mac window, send files, type on the Frame. With nothing captured, the
headset view is a short strip rather than a big black box; the keyboard and
trackpad fold to one line until turned on; VR performance folds away.
Screenshots and Family and comfort follow.
Tools is grouped: in the headset (Mac in the headset, the media player, the
panel switcher), then sending files, Linux apps, remote and power. Privacy
and updates, library artwork and the assistant move to a Settings page,
opened by a gear in the header (key 6), with About and licences.
Family and comfort no longer fills the form with a finished session's
settings, so it shows 30 / 20 / 30 rather than the last test's 1 / 0 / 0,
and its recent events carry their times.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 15:48:57 +10:00
12 changed files with 323 additions and 135 deletions
<buttonclass="action"id="castBtn"title="The live headset view, full screen, for people in the room">Cast to this screen</button>
<buttondata-go="media">Play a video or photo</button>
<buttondata-go="macview"id="nowMac"hidden>Show a Mac window</button>
<buttonid="nowSend">Send files…</button>
<buttondata-go="pad">Type on the Frame</button>
</div>
<pclass="hint">Casting shows everything the wearer sees, including anything private.</p>
</section>
</div>
<sectionclass="panel"id="vrTools">
<divclass="shelf-head"><h2>VR comfort and performance</h2><spanclass="spacer"></span><spanclass="sub"id="vrUpdated">Waiting for the Frame</span></div>
<divclass="stats"id="vrMetrics"></div>
<divclass="hint">Compositor FPS measures SteamVR output, not game FPS. Application FPS is unavailable when no app supplies timing. GPU time is render time, not GPU utilisation. Battery and temperature are in the battery menu at the top.</div>
<divclass="chips">
<buttonclass="small"id="hudStart">Open HUD in headset</button>
<buttonclass="small"id="shotsFolder"title="Open the SteamFrame folder in your Pictures">Show folder</button>
<buttonclass="action small"id="shotsSaveNew"disabled>Save new to this computer</button>
</div>
<divclass="hint">The HUD is ours and uses the Frame’s built-in tools. Place its panel with SteamVR’s Float in World or controller docking controls. It updates every two seconds until closed.</div>
<h3>Playspace and seated comfort</h3>
<pclass="sub"id="comfortNote">Tracking-origin controls are not available yet: reversible changes still need an idle-headset test. Use SteamVR’s recenter and room setup for now. A seated origin only affects apps that use seated tracking; it cannot force a game into seated mode.</p>
<divclass="hint">For motion comfort, choose snap-turn, teleport movement and a movement vignette in each game’s settings. SteamVR has no verified universal snap-turn or locomotion-vignette switch.</div>
<pclass="sub">None of Frame Control’s features needs these apps. Compatibility reports and account ownership are separate. Paid apps can only be installed here when already owned.</p>
<buttonclass="small"id="vrUtilitiesRefresh">Check ownership and reports</button>
<divclass="list"id="vrUtilities"><divclass="sub">Load this optional list to check the Frame account.</div></div>
<divclass="hint">Screenshots you take in the headset with Steam's screenshot shortcut. Click one to open it in the viewer; Save copies it to <code>~/Pictures/SteamFrame</code>.</div>
<buttonclass="small"id="shotsFolder"title="Open the SteamFrame folder in your Pictures">Show folder</button>
<buttonclass="action small"id="shotsSaveNew"disabled>Save new to this computer</button>
<detailsclass="panel fold"id="vrTools">
<summaryclass="shelf-head"><h2>VR performance</h2><spanclass="count">Frame timing, the HUD, playspace and optional software</span>
<spanclass="spacer"></span><spanclass="sub"id="vrUpdated">Waiting for the Frame</span></summary>
<divclass="stats"id="vrMetrics"></div>
<divclass="hint">Compositor FPS measures SteamVR output, not game FPS. Application FPS is unavailable when no app supplies timing. GPU time is render time, not GPU utilisation. Battery and temperature are in the battery menu at the top.</div>
<divclass="chips">
<buttonclass="small"id="hudStart">Open HUD in headset</button>
<divclass="hint">Screenshots you take in the headset with Steam's screenshot shortcut. Click one to open it in the viewer; Save copies it to <code>~/Pictures/SteamFrame</code>.</div>
</section>
<divclass="hint">The HUD is ours and uses the Frame’s built-in tools. Place its panel with SteamVR’s Float in World or controller docking controls. It updates every two seconds until closed.</div>
<h3>Playspace and seated comfort</h3>
<pclass="sub"id="comfortNote">Tracking-origin controls are not available yet: reversible changes still need an idle-headset test. Use SteamVR’s recenter and room setup for now. A seated origin only affects apps that use seated tracking; it cannot force a game into seated mode.</p>
<divclass="hint">For motion comfort, choose snap-turn, teleport movement and a movement vignette in each game’s settings. SteamVR has no verified universal snap-turn or locomotion-vignette switch.</div>
<pclass="sub">None of Frame Control’s features needs these apps. Compatibility reports and account ownership are separate. Paid apps can only be installed here when already owned.</p>
<buttonclass="small"id="vrUtilitiesRefresh">Check ownership and reports</button>
<divclass="list"id="vrUtilities"><divclass="sub">Load this optional list to check the Frame account.</div></div>
</details>
</details>
</div>
<divclass="page"data-page="games">
@@ -1030,7 +1071,65 @@
</div>
<divclass="page"data-page="tools">
<sectionclass="panel"><h2>Assistant and AI agents</h2><p>Use your own model endpoint, or review a proposed MCP action. Nothing is sent to a model until you opt in.</p><ahref="/assistant">Open assistant</a></section>
<divclass="shelf-head group-head"><h2>In the headset</h2><spanclass="count">Show your computer, films and photos, and panels</span></div>
<divclass="grid-3">
<sectionclass="panel"id="macview"hidden>
<divclass="shelf-head"><h2>Mac in the headset</h2><spanclass="count"id="mvCount"></span><spanclass="spacer"></span>
<pclass="sub">SteamGridDB supplies community artwork for sideloaded games. Without a key, Frame Control uses source images and creates the remaining art.</p>
<labelfor="steamGridKey">SteamGridDB API key</label>
<inputtype="password"id="steamGridKey"autocomplete="off"spellcheck="false"placeholder="Optional API key">
<buttonid="refreshAndroidArt"class="small"style="margin-top:12px">Refresh artwork for installed apps and titles</button>
</section>
<sectionclass="panel"id="power">
<divclass="shelf-head"><h2>Remote & power</h2></div>
<divclass="actions">
@@ -1120,45 +1180,17 @@
<divclass="hint desk-only">Sleep, Restart and Shut down open a terminal window for the Developer Mode password.</div>
<divclass="hint mobile-only">Sleep, Restart and Shut down ask for the Developer Mode password. SSH, SFTP, Steam Link and remote desktop open in the app that handles them.</div>
<pclass="sub">SteamGridDB supplies community artwork for sideloaded games. Without a key, Frame Control uses source images and creates the remaining art.</p>
<labelfor="steamGridKey">SteamGridDB API key</label>
<inputtype="password"id="steamGridKey"autocomplete="off"spellcheck="false"placeholder="Optional API key">
let devicesSeq = 0; // an older answer arriving late mustn't bring back the old selection
async function loadDevices() {
if (!link.live) return;
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.