@simon-says-build/demo-player 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.d.ts CHANGED
@@ -31,3 +31,13 @@ export const PLAYER_VERSION: string;
31
31
  export function reviewChangesOf(demo: unknown): { jobId: string; title: string }[];
32
32
  /** A release's demo (0.4.0): the session merge its review card releases, or null. */
33
33
  export function releaseOf(demo: unknown): { jobId: string; label: string; note: string } | null;
34
+
35
+ /**
36
+ * React Navigation (an app without expo-router): a path is the chain of route
37
+ * names to the focused screen, "/MainStack/Tabs/Dashboard". `nav` is the
38
+ * NavigationContainer's ref (or the container), read at call time.
39
+ */
40
+ export function navigationRoute(nav: unknown): string;
41
+ export function navigationRouter(nav: unknown): { push: (path: string) => boolean };
42
+ export function navigateArgs(path: string): [string] | [string, object] | null;
43
+ export function pathOfState(state: unknown): string;
package/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { DemoHost } from './src/DemoHost.js';
2
2
  export { demoConfig, findTarget, handlerFor, phoneSteps, scalePoint, playDemo, currentDemo, onDemo, reviewChangesOf, releaseOf, PLAYER_VERSION } from './src/core.js';
3
+ export { navigationRoute, navigationRouter, navigateArgs, pathOfState } from './src/navigation.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simon-says-build/demo-player",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "SimonSays mobile demo player: walks a preview's viewer through a job's change, inside the app. Renders nothing unless the bundle carries a demo.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/core.js CHANGED
@@ -10,7 +10,7 @@ import { bubbleBox } from './step-place.js';
10
10
  // What this player is, said when the app registers for "Ready to see"
11
11
  // (packages/ssb-overlay sends it; functions/in-app-notify.mjs clientOf). Kept
12
12
  // equal to package.json by test/core.test.mjs.
13
- export const PLAYER_VERSION = '0.4.0';
13
+ export const PLAYER_VERSION = '0.5.0';
14
14
 
15
15
  export const HOST = 5;
16
16
  export const TEXT = 6;
@@ -0,0 +1,52 @@
1
+ // REACT NAVIGATION, for an app without expo-router (2026-10-22, SuperSub's
2
+ // Center and Sub apps). A demo's `goto` step carries an in-app PATH, and the
3
+ // in-app overlay reports the screen a person is on as one. For an
4
+ // expo-router app that is its URL path. For a React Navigation app it is the
5
+ // chain of route NAMES from the root to the focused screen:
6
+ //
7
+ // /MainStack/Tabs/Dashboard
8
+ //
9
+ // navigationRoute() reads that off the navigation container, and
10
+ // navigationRouter() replays it with nested navigate() calls, so a path the
11
+ // overlay recorded is one the demo player can open. Both take the container
12
+ // (or a ref to it, read at call time). Pure apart from the container.
13
+
14
+ const containerOf = (nav) => (nav && 'current' in nav ? nav.current : nav);
15
+
16
+ /** The focused route's path, "/A/B/C", from a navigation state; "" when there is none. */
17
+ export function pathOfState(state) {
18
+ const names = [];
19
+ for (let s = state; s && Array.isArray(s.routes) && s.routes.length;) {
20
+ const r = s.routes[Number.isInteger(s.index) ? s.index : s.routes.length - 1];
21
+ if (!r?.name) break;
22
+ names.push(r.name);
23
+ s = r.state;
24
+ }
25
+ return names.length ? `/${names.map(encodeURIComponent).join('/')}` : '';
26
+ }
27
+
28
+ /** What navigate() is called with to open a path: [name, params], or null for a path that names nothing. */
29
+ export function navigateArgs(path) {
30
+ const names = String(path || '').split('?')[0].split('/').filter(Boolean).map((n) => { try { return decodeURIComponent(n); } catch { return n; } });
31
+ if (!names.length) return null;
32
+ let params;
33
+ for (let i = names.length - 1; i >= 1; i--) params = params ? { screen: names[i], params } : { screen: names[i] };
34
+ return params ? [names[0], params] : [names[0]];
35
+ }
36
+
37
+ /** The current screen's path on a React Navigation container ("" before it is ready). */
38
+ export function navigationRoute(nav) {
39
+ try { return pathOfState(containerOf(nav)?.getRootState?.()); } catch { return ''; }
40
+ }
41
+
42
+ /** A `router` for <DemoHost> on a React Navigation app: push(path) opens it. */
43
+ export function navigationRouter(nav) {
44
+ return {
45
+ push(path) {
46
+ const c = containerOf(nav);
47
+ const args = navigateArgs(path);
48
+ if (!c?.navigate || !args) return false;
49
+ try { c.navigate(...args); return true; } catch { return false; }
50
+ },
51
+ };
52
+ }