mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-27 10:02:38 +01:00
Return through local Framey panel routes
This commit is contained in:
1 parent
efc55919b6
commit
39aa8d56fb
3 files changed
+62
-6
No files matched your search
@@ -76,7 +76,7 @@ none of these frontend controls has passed a worn-headset check yet.
|
||||
| `localStorage` | Synchronous, per-plugin cache backed by the Frame Control panel origin. Up to 64 keys, 64 KiB per value and 128 KiB total; `clear()` removes only this plugin's cache. Falls back to memory if panel storage is denied. | Parent/iframe round-trip and reopen in the local host test; real headset persistence remains unverified. |
|
||||
| `SteamClient.Storage.GetJSON` / `SetObject` | Uses the same bounded per-plugin cache; a missing key rejects with `Not found`. It cannot read Steam account or global storage. | Host contract tests and local Chromium settings views; headset persistence after reopen remains unverified. |
|
||||
| IndexedDB / localForage | IndexedDB is deliberately absent inside the opaque iframe so feature detection chooses the bounded localStorage driver. Plugins requiring IndexedDB directly remain unsupported. | HLTB official frontend in local Chromium: its previous `IDBFactory.open` SecurityError disappeared. No live HLTB data request was run. |
|
||||
| `routerHook.addRoute` / `removeRoute`, `Navigation.Navigate` / `NavigateBack`, `useParams` | Local routes inside this plugin panel, with named segments such as `/plugin/game/:appid/:view?`; `useParams()` reads the current segment values | Host unit test; VR navigation unverified |
|
||||
| `routerHook.addRoute` / `removeRoute`, `Navigation.Navigate` / `NavigateBack`, `useParams` | Local routes inside this plugin panel, with named segments such as `/plugin/game/:appid/:view?`; `useParams()` reads the current segment values. Back walks up to 32 local page transitions and skips routes removed since navigation. | Host unit test; VR navigation unverified |
|
||||
| `routerHook.addPatch`, global components | No-op; cannot patch Steam routes | Not applicable |
|
||||
| `findModule`, `findModuleChild`, `findModuleExport`, class lookup and module mapping | Searches a finite registry of Framey controls and CSS maps. Unknown Steam modules return `undefined` or an empty mapping. The optional UI-audio export cannot play sound. | Host contract tests; pinned TabMaster 2.15.1 QAM and documentation render in local Chromium. Steam library editing and worn VR remain unverified. |
|
||||
| `Navigation.CloseSideMenus` | Returns to this plugin's main panel | Pinned HLTB for Deck 2.0.12 shallow settings view in local host test. Live HLTB search remains unverified. |
|
||||
@@ -90,6 +90,9 @@ Frame Control store; only paths registered by that same plugin can navigate.
|
||||
Optional route segments must be at the end. Framey accepts local path segments
|
||||
only; query strings, external URLs, Steam library routes and duplicate parameter
|
||||
names are rejected. Removing an active route returns to the plugin panel.
|
||||
`Navigation.NavigateBack()` returns to the preceding local page, then the
|
||||
plugin root. `Navigation.CloseSideMenus()` returns directly to the root and
|
||||
clears that panel's history. Neither operation navigates Steam's own UI.
|
||||
Toasts stay inside the panel, so keep essential errors in the plugin UI too.
|
||||
Treat module lookup results as Framey-local exports. Do not depend on Steam
|
||||
webpack IDs, function-source signatures, or Steam-only CSS class names; a
|
||||
|
||||
@@ -464,11 +464,47 @@ assert.equal(window.DFL.useParams().view,'art');
|
||||
assert.equal(window.DFL.Navigation.Navigate('/plugin/game/123/art/extra'),false);
|
||||
assert.equal(window.DFL.Navigation.Navigate('/plugin/game/123?admin=1'),false);
|
||||
assert.equal(window.DFL.Navigation.Navigate('/plugin/game//art'),false);
|
||||
assert.equal(window.DFL.Navigation.NavigateBack(),true);
|
||||
assert.equal(window.DFL.useParams().appid,'123');
|
||||
assert.equal(window.DFL.useParams().view,undefined,'Back returns to the previous local page');
|
||||
assert.equal(window.DFL.Navigation.NavigateBack(),true);
|
||||
assert.equal(window.DFL.useParams().appid,undefined,'A second Back returns to the plugin root');
|
||||
assert.equal(window.DFL.Navigation.Navigate('/plugin/game/123'),true);
|
||||
assert.equal(window.DFL.Navigation.Navigate('/plugin/game/123'),true);
|
||||
assert.equal(window.DFL.Navigation.Navigate('/plugin/game/123/art'),true);
|
||||
assert.equal(window.DFL.Navigation.NavigateBack(),true);
|
||||
assert.equal(window.DFL.useParams().view,undefined,'Navigating to the same page did not add a history entry');
|
||||
assert.equal(window.DFL.Navigation.NavigateBack(),true);
|
||||
assert.equal(window.DFL.useParams().appid,undefined);
|
||||
assert.throws(()=>api.routerHook.addRoute('/plugin/:id/:id',GameRoute));
|
||||
assert.throws(()=>api.routerHook.addRoute('/plugin/:id?/art',GameRoute));
|
||||
api.routerHook.addRoute('/plugin/other',LocalRoute);
|
||||
assert.equal(window.DFL.Navigation.Navigate('/plugin/game/123'),true);
|
||||
assert.equal(window.DFL.Navigation.Navigate('/plugin/other'),true);
|
||||
removeGame();
|
||||
assert.equal(window.DFL.Navigation.NavigateBack(),true);
|
||||
assert.equal(nodes(window.__surface({plugin}),LocalRoute).length,0,
|
||||
'Back skips a route that the plugin unregistered');
|
||||
assert.equal(window.DFL.useParams().appid,undefined);
|
||||
assert.equal(nodes(window.__surface({plugin}),GameRoute).length,0);
|
||||
api.routerHook.removeRoute('/plugin/other');
|
||||
api.routerHook.addRoute('/plugin/first',LocalRoute);
|
||||
api.routerHook.addRoute('/plugin/second',LocalRoute);
|
||||
for(let index=0;index<40;index++)
|
||||
assert.equal(window.DFL.Navigation.Navigate(index%2 ? '/plugin/first' : '/plugin/second'),true);
|
||||
for(let index=0;index<32;index++)window.DFL.Navigation.NavigateBack();
|
||||
assert.equal(nodes(window.__surface({plugin}),LocalRoute).length,1);
|
||||
window.DFL.Navigation.NavigateBack();
|
||||
assert.equal(nodes(window.__surface({plugin}),LocalRoute).length,0,
|
||||
'local route history is bounded and falls back to the plugin root');
|
||||
window.DFL.Navigation.Navigate('/plugin/first');
|
||||
window.DFL.Navigation.Navigate('/plugin/second');
|
||||
window.DFL.Navigation.CloseSideMenus();
|
||||
window.DFL.Navigation.NavigateBack();
|
||||
assert.equal(nodes(window.__surface({plugin}),LocalRoute).length,0,
|
||||
'CloseSideMenus clears the local route history');
|
||||
api.routerHook.removeRoute('/plugin/first');
|
||||
api.routerHook.removeRoute('/plugin/second');
|
||||
|
||||
// Event subscription is acknowledged before a method that immediately emits.
|
||||
const onMessage=listeners.find(x=>x.type==='message').handler;
|
||||
|
||||
+22
-5
@@ -56,6 +56,7 @@
|
||||
const pending = new Map();
|
||||
const routes = new Map();
|
||||
const routeListeners = new Set();
|
||||
const routeHistory = [];
|
||||
let currentRoute = null;
|
||||
let currentParams = {};
|
||||
let nextId = 0;
|
||||
@@ -368,15 +369,31 @@
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function navigate(path) {
|
||||
if (path !== null && !localPath(path)) return false;
|
||||
const match = path === null ? null : activeRoute(path);
|
||||
if (path !== null && !match) return false;
|
||||
function showRoute(path, match) {
|
||||
currentRoute = path;
|
||||
currentParams = match?.params || {};
|
||||
for (const listener of routeListeners) listener(path);
|
||||
return true;
|
||||
}
|
||||
function navigate(path) {
|
||||
if (path !== null && !localPath(path)) return false;
|
||||
const match = path === null ? null : activeRoute(path);
|
||||
if (path !== null && !match) return false;
|
||||
if (path === null) routeHistory.length = 0;
|
||||
else if (path !== currentRoute) {
|
||||
routeHistory.push(currentRoute);
|
||||
if (routeHistory.length > 32) routeHistory.shift();
|
||||
}
|
||||
return showRoute(path, match);
|
||||
}
|
||||
function navigateBack() {
|
||||
while (routeHistory.length) {
|
||||
const previous = routeHistory.pop();
|
||||
const match = previous === null ? null : activeRoute(previous);
|
||||
if (previous === null || match) return showRoute(previous, match);
|
||||
}
|
||||
return navigate(null);
|
||||
}
|
||||
const routerHook = {
|
||||
addRoute(path, component) {
|
||||
if (!routePattern(path) ||
|
||||
@@ -491,7 +508,7 @@
|
||||
joinClassNames:(...names)=>names.filter(Boolean).join(' '),
|
||||
sleep:ms=>new Promise(resolve=>setTimeout(resolve,Math.max(0,Number(ms)||0))),
|
||||
staticClasses: {Title:'framey-title'},
|
||||
Navigation: {Navigate:navigate, NavigateBack:()=>navigate(null), CloseSideMenus:()=>navigate(null),
|
||||
Navigation: {Navigate:navigate, NavigateBack:navigateBack, CloseSideMenus:()=>navigate(null),
|
||||
NavigateToExternalWeb: url => { if (/^https:\/\//.test(url)) window.open(url, '_blank', 'noopener'); }},
|
||||
showModal, showContextMenu:showModal,
|
||||
// Steam app, library, and store patches have no target inside this panel.
|
||||
|
||||
Reference in new issue
Block a user