@idosgames/app-shell 0.1.0 → 0.1.1
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/dist/index.cjs +1 -1
- package/dist/index.d.cts +51 -5
- package/dist/index.d.ts +51 -5
- package/dist/index.js +1 -1
- package/package.json +64 -64
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var client=require('react-dom/client'),react$1=require('@idosgames/react'),react=require('react'),jsxRuntime=require('react/jsx-runtime');function S(){let
|
|
1
|
+
'use strict';var client=require('react-dom/client'),react$1=require('@idosgames/react'),react=require('react'),core=require('@idosgames/core'),jsxRuntime=require('react/jsx-runtime');function S(){let s=new Map;return {emit(o,t){let n=s.get(o);if(n)for(let r of [...n])r(t);},on(o,t){let n=t,r=s.get(o);return r||(r=new Set,s.set(o,r)),r.add(n),()=>{r.delete(n),r.size===0&&s.delete(o);}}}}var z={position:"absolute",inset:"0"};function h(s){return {request(o){let t=document.createElement("div");return t.dataset.surface=o,Object.assign(t.style,z),t.style.pointerEvents=o==="fullbleed-canvas"?"auto":"none",s.appendChild(t),{host:t,release(){t.remove();}}}}}function b(s,o){return s.map(t=>{let n={module:t,scenes:[],panels:[],routes:[]},r={client:o.client,titleId:o.client.titleID,events:o.events,surface:o.surface,registerScene:l=>n.scenes.push(l),registerPanel:l=>n.panels.push(l),registerRoute:l=>n.routes.push(l)};return t.setup(r),n})}var f=class{constructor(o){this.container=o;}container;hosts=new Map;active=new Set;ensureMounted(o){let t=this.hosts.get(o);return t||(t=document.createElement("div"),Object.assign(t.style,{position:"absolute",inset:"0"}),this.container.appendChild(t),this.hosts.set(o,t),o.mount({host:t})),t}setActive(o){let t=new Set(o);for(let n of this.active){if(t.has(n))continue;n.suspend?.();let r=this.hosts.get(n);r&&(r.style.display="none");}for(let n of t){if(this.active.has(n))continue;let r=this.ensureMounted(n);r.style.display="block",n.activate?.();}this.active=t;}destroy(){for(let[o,t]of this.hosts)o.destroy(),t.remove();this.hosts.clear(),this.active.clear();}};function E({registrations:s}){let o=react.useRef(null),t=react.useRef(null),n=react.useMemo(()=>s.flatMap(e=>e.routes.map(i=>({id:i.id,label:i.label,icon:i.icon,order:i.order??0,moduleId:e.module.id}))).sort((e,i)=>e.order-i.order),[s]),[r,l]=react.useState(()=>n[0]?.id??null);react.useEffect(()=>{r!==null&&n.some(e=>e.id===r)||l(n[0]?.id??null);},[n,r]);let d=react.useMemo(()=>n.find(e=>e.id===r)?.moduleId??null,[n,r]);react.useEffect(()=>{let e=o.current;if(!e)return;let i=t.current??=new f(e),m=s.find(g=>g.module.id===d)?.scenes??[];i.setActive(m);},[s,d]),react.useEffect(()=>()=>{t.current?.destroy(),t.current=null;},[]);let u=react.useMemo(()=>s.flatMap(e=>e.panels.filter(i=>i.activeOnly===false||e.module.id===d).map(i=>{let m=i.component;return {key:`${e.module.id}:${i.id}`,slot:i.slot,order:i.order??0,moduleId:e.module.id,component:jsxRuntime.jsx(m,{})}})).sort((e,i)=>e.order-i.order),[s,d]),c=e=>u.filter(i=>i.slot===e),p=s.length>0;return jsxRuntime.jsxs("div",{style:N,children:[jsxRuntime.jsx("div",{ref:o,style:T}),jsxRuntime.jsx("div",{style:O,children:c("overlay").map(e=>jsxRuntime.jsx("div",{style:y,children:e.component},e.key))}),jsxRuntime.jsx("div",{style:W,children:c("hud").map(e=>jsxRuntime.jsx("div",{style:y,children:e.component},e.key))}),jsxRuntime.jsx("div",{style:j,children:c("sidebar").map(e=>jsxRuntime.jsx("div",{style:y,children:e.component},e.key))}),n.length>1&&jsxRuntime.jsx("nav",{style:U,children:n.map(e=>jsxRuntime.jsxs("button",{type:"button",onClick:()=>l(e.id),style:e.id===r?$:I,children:[e.icon?`${e.icon} `:"",e.label]},e.id))}),c("modal").length>0&&jsxRuntime.jsx("div",{style:F,children:c("modal").map(e=>jsxRuntime.jsx("div",{style:y,children:e.component},e.key))}),!p&&jsxRuntime.jsx(G,{})]})}function G(){return jsxRuntime.jsxs("div",{style:K,children:[jsxRuntime.jsx("div",{style:{fontSize:40,marginBottom:12},children:"\u{1F9E9}"}),jsxRuntime.jsx("div",{style:{fontWeight:700,fontSize:18,marginBottom:6},children:"No modules installed"}),jsxRuntime.jsxs("div",{style:{opacity:.7,maxWidth:380,lineHeight:1.5},children:["Add a module under ",jsxRuntime.jsx("code",{children:"src/modules/"})," and register it in"," ",jsxRuntime.jsx("code",{children:"src/modules.ts"}),". The host shell will pick it up here."]})]})}var N={position:"absolute",inset:0,overflow:"hidden",color:"#fff",fontFamily:"system-ui, sans-serif"},T={position:"absolute",inset:0,zIndex:0},O={position:"absolute",inset:0,zIndex:1,pointerEvents:"none"},W={position:"absolute",top:0,left:0,right:0,zIndex:2,display:"flex",gap:12,padding:16,pointerEvents:"none"},j={position:"absolute",top:0,right:0,bottom:0,width:340,maxWidth:"40vw",zIndex:2,display:"flex",flexDirection:"column",gap:12,padding:16,overflowY:"auto",pointerEvents:"none"},U={position:"absolute",bottom:16,left:"50%",transform:"translateX(-50%)",zIndex:3,display:"flex",gap:8,padding:6,borderRadius:14,background:"rgba(20,18,40,0.72)",backdropFilter:"blur(6px)",pointerEvents:"auto"},I={border:"none",borderRadius:10,padding:"8px 16px",fontWeight:700,fontSize:14,color:"#cfc9ee",background:"transparent",cursor:"pointer"},$={...I,color:"#fff",background:"#6c5ce7"},F={position:"absolute",inset:0,zIndex:4,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(0,0,0,0.5)",pointerEvents:"none"},y={pointerEvents:"auto"},K={position:"absolute",inset:0,zIndex:1,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",textAlign:"center",padding:24};function M(s){let{client:o,modules:t,events:n,surface:r,renderLogin:l}=s,[d,u]=react.useState(true),[c,p]=react.useState(null),e=()=>{p(b(t,{client:o,events:n,surface:r}));},i=react.useRef(false);return react.useEffect(()=>{if(!i.current){if(i.current=true,o.auth.lastAuthType===core.AuthType.None){u(false);return}(async()=>((await o.auth.autoLogin()).ok&&e(),u(false)))();}},[o]),react.useEffect(()=>{let m=()=>p(null),g=o.on("auth:unauthorized",m),w=o.on("auth:loggedOut",m);return ()=>{g(),w();}},[o]),c?jsxRuntime.jsx(E,{registrations:c}):d?null:l({client:o,onAuthenticated:e})}function P({client:s,onAuthenticated:o}){let[t,n]=react.useState(false),[r,l]=react.useState(null),d=async()=>{n(true),l(null);let u=await s.auth.loginWithDeviceID();if(u.ok){o();return}l(u.error),n(false);};return jsxRuntime.jsx("div",{style:{position:"absolute",inset:0,display:"grid",placeItems:"center",gap:"12px",color:"#e8e6f3",font:"14px system-ui, sans-serif"},children:jsxRuntime.jsxs("div",{style:{display:"grid",gap:"12px",justifyItems:"center"},children:[jsxRuntime.jsx("button",{type:"button",onClick:()=>{d();},disabled:t,style:{padding:"10px 20px",borderRadius:"8px",border:"1px solid #4c4470",background:t?"#2a2545":"#3b3363",color:"inherit",cursor:t?"default":"pointer",font:"inherit"},children:t?"Signing in\u2026":"Play as guest"}),r&&jsxRuntime.jsx("span",{style:{color:"#ff9b9b"},children:r})]})})}function _(s){let{container:o,client:t,modules:n,renderLogin:r}=s;o.style.position="relative";let l=document.createElement("div");Object.assign(l.style,{position:"absolute",inset:"0",zIndex:"0",pointerEvents:"none"});let d=document.createElement("div");Object.assign(d.style,{position:"absolute",inset:"0",zIndex:"1"}),o.append(l,d);let u=S(),c=h(l),p=client.createRoot(d);return p.render(jsxRuntime.jsx(react$1.IDosGamesProvider,{client:t,children:jsxRuntime.jsx(react$1.StatusProvider,{children:jsxRuntime.jsx(M,{client:t,modules:n,events:u,surface:c,renderLogin:r??P})})})),{unmount(){p.unmount(),l.remove(),d.remove();}}}exports.AuthGate=M;exports.DefaultLoginScreen=P;exports.HostShell=E;exports.SceneStage=f;exports.createSharedEventBus=S;exports.createSurfaceAllocator=h;exports.mountHost=_;exports.runModules=b;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,13 +1,44 @@
|
|
|
1
|
-
import { IDosGamesClient, Module,
|
|
1
|
+
import { IDosGamesClient, Module, SharedEventBus, SurfaceAllocator, EngineScene, UiPanel, RouteEntry } from '@idosgames/module-sdk';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
+
/** What a login screen receives from the host. */
|
|
5
|
+
interface LoginScreenProps {
|
|
6
|
+
/** The (not yet authenticated) shared client. Call any `client.auth.login*` method on it. */
|
|
7
|
+
client: IDosGamesClient;
|
|
8
|
+
/** Call after a login succeeds to enter the game. */
|
|
9
|
+
onAuthenticated(): void;
|
|
10
|
+
}
|
|
11
|
+
type RenderLogin = (props: LoginScreenProps) => ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* The Login ↔ Main gate — the analog of the Unity SDK's two scenes.
|
|
14
|
+
*
|
|
15
|
+
* It lives in the host runtime rather than in a project or a feature module, on purpose:
|
|
16
|
+
*
|
|
17
|
+
* - Modules receive an ALREADY-AUTHENTICATED client (see `ModuleContext`), and are installed only
|
|
18
|
+
* once that is true. A login module would be a cycle — you would need modules running to render
|
|
19
|
+
* the screen that lets you get a session.
|
|
20
|
+
* - Getting this wrong is expensive and silent (modules mounted before auth, a dead session never
|
|
21
|
+
* re-gated), so the state machine is not something each project should re-implement. What a game
|
|
22
|
+
* genuinely wants to own is how the login screen LOOKS — that is the `renderLogin` prop.
|
|
23
|
+
*/
|
|
24
|
+
declare function AuthGate(props: {
|
|
25
|
+
client: IDosGamesClient;
|
|
26
|
+
modules: readonly Module[];
|
|
27
|
+
events: SharedEventBus;
|
|
28
|
+
surface: SurfaceAllocator;
|
|
29
|
+
renderLogin: RenderLogin;
|
|
30
|
+
}): ReactNode;
|
|
31
|
+
|
|
4
32
|
interface HostHandle {
|
|
5
33
|
unmount(): void;
|
|
6
34
|
}
|
|
7
35
|
/**
|
|
8
|
-
* Mounts the host
|
|
9
|
-
*
|
|
10
|
-
*
|
|
36
|
+
* Mounts the host into `container`: the Login ↔ Main gate, and behind it the module shell, all
|
|
37
|
+
* driven by one shared SDK client.
|
|
38
|
+
*
|
|
39
|
+
* The client is handed over NOT yet authenticated — the host signs in. Callers must not log in
|
|
40
|
+
* first: the gate replays the previous session itself (`autoLogin`) and shows the login screen when
|
|
41
|
+
* there is nothing to replay, which is also what makes "switch account" and a dead session work.
|
|
11
42
|
*
|
|
12
43
|
* DOM layout inside `container`:
|
|
13
44
|
* container (position: relative)
|
|
@@ -18,8 +49,23 @@ declare function mountHost(options: {
|
|
|
18
49
|
container: HTMLElement;
|
|
19
50
|
client: IDosGamesClient;
|
|
20
51
|
modules: readonly Module[];
|
|
52
|
+
/**
|
|
53
|
+
* The Login scene's content. Optional so a project that deleted its screen still boots with a
|
|
54
|
+
* usable one; games are expected to pass their own (see the host template's LoginScreen).
|
|
55
|
+
*/
|
|
56
|
+
renderLogin?: RenderLogin;
|
|
21
57
|
}): HostHandle;
|
|
22
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Last-resort login screen, used only when the host was mounted without a `renderLogin`.
|
|
61
|
+
*
|
|
62
|
+
* Deliberately plain and guest-only: a game's real login screen belongs in the project (branding,
|
|
63
|
+
* which providers it offers, art), and lives in the host template as `src/LoginScreen.tsx`. This
|
|
64
|
+
* one exists so a project that deleted or broke its screen still boots into something a player can
|
|
65
|
+
* get past, instead of a blank page.
|
|
66
|
+
*/
|
|
67
|
+
declare function DefaultLoginScreen({ client, onAuthenticated, }: LoginScreenProps): ReactNode;
|
|
68
|
+
|
|
23
69
|
interface ModuleRegistration {
|
|
24
70
|
module: Module;
|
|
25
71
|
scenes: EngineScene[];
|
|
@@ -58,4 +104,4 @@ declare function createSharedEventBus(): SharedEventBus;
|
|
|
58
104
|
/** Creates an allocator whose surfaces are appended to `layer`. */
|
|
59
105
|
declare function createSurfaceAllocator(layer: HTMLElement): SurfaceAllocator;
|
|
60
106
|
|
|
61
|
-
export { type HostHandle, HostShell, type ModuleRegistration, SceneStage, type SharedServices, createSharedEventBus, createSurfaceAllocator, mountHost, runModules };
|
|
107
|
+
export { AuthGate, DefaultLoginScreen, type HostHandle, HostShell, type LoginScreenProps, type ModuleRegistration, type RenderLogin, SceneStage, type SharedServices, createSharedEventBus, createSurfaceAllocator, mountHost, runModules };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,44 @@
|
|
|
1
|
-
import { IDosGamesClient, Module,
|
|
1
|
+
import { IDosGamesClient, Module, SharedEventBus, SurfaceAllocator, EngineScene, UiPanel, RouteEntry } from '@idosgames/module-sdk';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
+
/** What a login screen receives from the host. */
|
|
5
|
+
interface LoginScreenProps {
|
|
6
|
+
/** The (not yet authenticated) shared client. Call any `client.auth.login*` method on it. */
|
|
7
|
+
client: IDosGamesClient;
|
|
8
|
+
/** Call after a login succeeds to enter the game. */
|
|
9
|
+
onAuthenticated(): void;
|
|
10
|
+
}
|
|
11
|
+
type RenderLogin = (props: LoginScreenProps) => ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* The Login ↔ Main gate — the analog of the Unity SDK's two scenes.
|
|
14
|
+
*
|
|
15
|
+
* It lives in the host runtime rather than in a project or a feature module, on purpose:
|
|
16
|
+
*
|
|
17
|
+
* - Modules receive an ALREADY-AUTHENTICATED client (see `ModuleContext`), and are installed only
|
|
18
|
+
* once that is true. A login module would be a cycle — you would need modules running to render
|
|
19
|
+
* the screen that lets you get a session.
|
|
20
|
+
* - Getting this wrong is expensive and silent (modules mounted before auth, a dead session never
|
|
21
|
+
* re-gated), so the state machine is not something each project should re-implement. What a game
|
|
22
|
+
* genuinely wants to own is how the login screen LOOKS — that is the `renderLogin` prop.
|
|
23
|
+
*/
|
|
24
|
+
declare function AuthGate(props: {
|
|
25
|
+
client: IDosGamesClient;
|
|
26
|
+
modules: readonly Module[];
|
|
27
|
+
events: SharedEventBus;
|
|
28
|
+
surface: SurfaceAllocator;
|
|
29
|
+
renderLogin: RenderLogin;
|
|
30
|
+
}): ReactNode;
|
|
31
|
+
|
|
4
32
|
interface HostHandle {
|
|
5
33
|
unmount(): void;
|
|
6
34
|
}
|
|
7
35
|
/**
|
|
8
|
-
* Mounts the host
|
|
9
|
-
*
|
|
10
|
-
*
|
|
36
|
+
* Mounts the host into `container`: the Login ↔ Main gate, and behind it the module shell, all
|
|
37
|
+
* driven by one shared SDK client.
|
|
38
|
+
*
|
|
39
|
+
* The client is handed over NOT yet authenticated — the host signs in. Callers must not log in
|
|
40
|
+
* first: the gate replays the previous session itself (`autoLogin`) and shows the login screen when
|
|
41
|
+
* there is nothing to replay, which is also what makes "switch account" and a dead session work.
|
|
11
42
|
*
|
|
12
43
|
* DOM layout inside `container`:
|
|
13
44
|
* container (position: relative)
|
|
@@ -18,8 +49,23 @@ declare function mountHost(options: {
|
|
|
18
49
|
container: HTMLElement;
|
|
19
50
|
client: IDosGamesClient;
|
|
20
51
|
modules: readonly Module[];
|
|
52
|
+
/**
|
|
53
|
+
* The Login scene's content. Optional so a project that deleted its screen still boots with a
|
|
54
|
+
* usable one; games are expected to pass their own (see the host template's LoginScreen).
|
|
55
|
+
*/
|
|
56
|
+
renderLogin?: RenderLogin;
|
|
21
57
|
}): HostHandle;
|
|
22
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Last-resort login screen, used only when the host was mounted without a `renderLogin`.
|
|
61
|
+
*
|
|
62
|
+
* Deliberately plain and guest-only: a game's real login screen belongs in the project (branding,
|
|
63
|
+
* which providers it offers, art), and lives in the host template as `src/LoginScreen.tsx`. This
|
|
64
|
+
* one exists so a project that deleted or broke its screen still boots into something a player can
|
|
65
|
+
* get past, instead of a blank page.
|
|
66
|
+
*/
|
|
67
|
+
declare function DefaultLoginScreen({ client, onAuthenticated, }: LoginScreenProps): ReactNode;
|
|
68
|
+
|
|
23
69
|
interface ModuleRegistration {
|
|
24
70
|
module: Module;
|
|
25
71
|
scenes: EngineScene[];
|
|
@@ -58,4 +104,4 @@ declare function createSharedEventBus(): SharedEventBus;
|
|
|
58
104
|
/** Creates an allocator whose surfaces are appended to `layer`. */
|
|
59
105
|
declare function createSurfaceAllocator(layer: HTMLElement): SurfaceAllocator;
|
|
60
106
|
|
|
61
|
-
export { type HostHandle, HostShell, type ModuleRegistration, SceneStage, type SharedServices, createSharedEventBus, createSurfaceAllocator, mountHost, runModules };
|
|
107
|
+
export { AuthGate, DefaultLoginScreen, type HostHandle, HostShell, type LoginScreenProps, type ModuleRegistration, type RenderLogin, SceneStage, type SharedServices, createSharedEventBus, createSurfaceAllocator, mountHost, runModules };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import {createRoot}from'react-dom/client';import {IDosGamesProvider,StatusProvider}from'@idosgames/react';import {useRef,useMemo,useState,useEffect}from'react';import {jsx,jsxs}from'react/jsx-runtime';function S(){let
|
|
1
|
+
import {createRoot}from'react-dom/client';import {IDosGamesProvider,StatusProvider}from'@idosgames/react';import {useRef,useMemo,useState,useEffect}from'react';import {AuthType}from'@idosgames/core';import {jsx,jsxs}from'react/jsx-runtime';function S(){let s=new Map;return {emit(o,t){let n=s.get(o);if(n)for(let r of [...n])r(t);},on(o,t){let n=t,r=s.get(o);return r||(r=new Set,s.set(o,r)),r.add(n),()=>{r.delete(n),r.size===0&&s.delete(o);}}}}var z={position:"absolute",inset:"0"};function h(s){return {request(o){let t=document.createElement("div");return t.dataset.surface=o,Object.assign(t.style,z),t.style.pointerEvents=o==="fullbleed-canvas"?"auto":"none",s.appendChild(t),{host:t,release(){t.remove();}}}}}function b(s,o){return s.map(t=>{let n={module:t,scenes:[],panels:[],routes:[]},r={client:o.client,titleId:o.client.titleID,events:o.events,surface:o.surface,registerScene:l=>n.scenes.push(l),registerPanel:l=>n.panels.push(l),registerRoute:l=>n.routes.push(l)};return t.setup(r),n})}var f=class{constructor(o){this.container=o;}container;hosts=new Map;active=new Set;ensureMounted(o){let t=this.hosts.get(o);return t||(t=document.createElement("div"),Object.assign(t.style,{position:"absolute",inset:"0"}),this.container.appendChild(t),this.hosts.set(o,t),o.mount({host:t})),t}setActive(o){let t=new Set(o);for(let n of this.active){if(t.has(n))continue;n.suspend?.();let r=this.hosts.get(n);r&&(r.style.display="none");}for(let n of t){if(this.active.has(n))continue;let r=this.ensureMounted(n);r.style.display="block",n.activate?.();}this.active=t;}destroy(){for(let[o,t]of this.hosts)o.destroy(),t.remove();this.hosts.clear(),this.active.clear();}};function E({registrations:s}){let o=useRef(null),t=useRef(null),n=useMemo(()=>s.flatMap(e=>e.routes.map(i=>({id:i.id,label:i.label,icon:i.icon,order:i.order??0,moduleId:e.module.id}))).sort((e,i)=>e.order-i.order),[s]),[r,l]=useState(()=>n[0]?.id??null);useEffect(()=>{r!==null&&n.some(e=>e.id===r)||l(n[0]?.id??null);},[n,r]);let d=useMemo(()=>n.find(e=>e.id===r)?.moduleId??null,[n,r]);useEffect(()=>{let e=o.current;if(!e)return;let i=t.current??=new f(e),m=s.find(g=>g.module.id===d)?.scenes??[];i.setActive(m);},[s,d]),useEffect(()=>()=>{t.current?.destroy(),t.current=null;},[]);let u=useMemo(()=>s.flatMap(e=>e.panels.filter(i=>i.activeOnly===false||e.module.id===d).map(i=>{let m=i.component;return {key:`${e.module.id}:${i.id}`,slot:i.slot,order:i.order??0,moduleId:e.module.id,component:jsx(m,{})}})).sort((e,i)=>e.order-i.order),[s,d]),c=e=>u.filter(i=>i.slot===e),p=s.length>0;return jsxs("div",{style:N,children:[jsx("div",{ref:o,style:T}),jsx("div",{style:O,children:c("overlay").map(e=>jsx("div",{style:y,children:e.component},e.key))}),jsx("div",{style:W,children:c("hud").map(e=>jsx("div",{style:y,children:e.component},e.key))}),jsx("div",{style:j,children:c("sidebar").map(e=>jsx("div",{style:y,children:e.component},e.key))}),n.length>1&&jsx("nav",{style:U,children:n.map(e=>jsxs("button",{type:"button",onClick:()=>l(e.id),style:e.id===r?$:I,children:[e.icon?`${e.icon} `:"",e.label]},e.id))}),c("modal").length>0&&jsx("div",{style:F,children:c("modal").map(e=>jsx("div",{style:y,children:e.component},e.key))}),!p&&jsx(G,{})]})}function G(){return jsxs("div",{style:K,children:[jsx("div",{style:{fontSize:40,marginBottom:12},children:"\u{1F9E9}"}),jsx("div",{style:{fontWeight:700,fontSize:18,marginBottom:6},children:"No modules installed"}),jsxs("div",{style:{opacity:.7,maxWidth:380,lineHeight:1.5},children:["Add a module under ",jsx("code",{children:"src/modules/"})," and register it in"," ",jsx("code",{children:"src/modules.ts"}),". The host shell will pick it up here."]})]})}var N={position:"absolute",inset:0,overflow:"hidden",color:"#fff",fontFamily:"system-ui, sans-serif"},T={position:"absolute",inset:0,zIndex:0},O={position:"absolute",inset:0,zIndex:1,pointerEvents:"none"},W={position:"absolute",top:0,left:0,right:0,zIndex:2,display:"flex",gap:12,padding:16,pointerEvents:"none"},j={position:"absolute",top:0,right:0,bottom:0,width:340,maxWidth:"40vw",zIndex:2,display:"flex",flexDirection:"column",gap:12,padding:16,overflowY:"auto",pointerEvents:"none"},U={position:"absolute",bottom:16,left:"50%",transform:"translateX(-50%)",zIndex:3,display:"flex",gap:8,padding:6,borderRadius:14,background:"rgba(20,18,40,0.72)",backdropFilter:"blur(6px)",pointerEvents:"auto"},I={border:"none",borderRadius:10,padding:"8px 16px",fontWeight:700,fontSize:14,color:"#cfc9ee",background:"transparent",cursor:"pointer"},$={...I,color:"#fff",background:"#6c5ce7"},F={position:"absolute",inset:0,zIndex:4,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(0,0,0,0.5)",pointerEvents:"none"},y={pointerEvents:"auto"},K={position:"absolute",inset:0,zIndex:1,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",textAlign:"center",padding:24};function M(s){let{client:o,modules:t,events:n,surface:r,renderLogin:l}=s,[d,u]=useState(true),[c,p]=useState(null),e=()=>{p(b(t,{client:o,events:n,surface:r}));},i=useRef(false);return useEffect(()=>{if(!i.current){if(i.current=true,o.auth.lastAuthType===AuthType.None){u(false);return}(async()=>((await o.auth.autoLogin()).ok&&e(),u(false)))();}},[o]),useEffect(()=>{let m=()=>p(null),g=o.on("auth:unauthorized",m),w=o.on("auth:loggedOut",m);return ()=>{g(),w();}},[o]),c?jsx(E,{registrations:c}):d?null:l({client:o,onAuthenticated:e})}function P({client:s,onAuthenticated:o}){let[t,n]=useState(false),[r,l]=useState(null),d=async()=>{n(true),l(null);let u=await s.auth.loginWithDeviceID();if(u.ok){o();return}l(u.error),n(false);};return jsx("div",{style:{position:"absolute",inset:0,display:"grid",placeItems:"center",gap:"12px",color:"#e8e6f3",font:"14px system-ui, sans-serif"},children:jsxs("div",{style:{display:"grid",gap:"12px",justifyItems:"center"},children:[jsx("button",{type:"button",onClick:()=>{d();},disabled:t,style:{padding:"10px 20px",borderRadius:"8px",border:"1px solid #4c4470",background:t?"#2a2545":"#3b3363",color:"inherit",cursor:t?"default":"pointer",font:"inherit"},children:t?"Signing in\u2026":"Play as guest"}),r&&jsx("span",{style:{color:"#ff9b9b"},children:r})]})})}function _(s){let{container:o,client:t,modules:n,renderLogin:r}=s;o.style.position="relative";let l=document.createElement("div");Object.assign(l.style,{position:"absolute",inset:"0",zIndex:"0",pointerEvents:"none"});let d=document.createElement("div");Object.assign(d.style,{position:"absolute",inset:"0",zIndex:"1"}),o.append(l,d);let u=S(),c=h(l),p=createRoot(d);return p.render(jsx(IDosGamesProvider,{client:t,children:jsx(StatusProvider,{children:jsx(M,{client:t,modules:n,events:u,surface:c,renderLogin:r??P})})})),{unmount(){p.unmount(),l.remove(),d.remove();}}}export{M as AuthGate,P as DefaultLoginScreen,E as HostShell,f as SceneStage,S as createSharedEventBus,h as createSurfaceAllocator,_ as mountHost,b as runModules};
|
package/package.json
CHANGED
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@idosgames/app-shell",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "The host runtime for composable modules: module registry, shared event bus, surface allocator, scene stage, and the React Mode Router shell. A project seeds one host shell and plugs N modules into it.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"author": "iDos Games",
|
|
8
|
-
"homepage": "https://github.com/iDos-Games/iDosGamesSDK_TS#readme",
|
|
9
|
-
"repository": {
|
|
10
|
-
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/iDos-Games/iDosGamesSDK_TS.git",
|
|
12
|
-
"directory": "packages/app-shell"
|
|
13
|
-
},
|
|
14
|
-
"bugs": {
|
|
15
|
-
"url": "https://github.com/iDos-Games/iDosGamesSDK_TS/issues"
|
|
16
|
-
},
|
|
17
|
-
"keywords": [
|
|
18
|
-
"idosgames",
|
|
19
|
-
"sdk",
|
|
20
|
-
"host",
|
|
21
|
-
"shell",
|
|
22
|
-
"modules",
|
|
23
|
-
"plugin"
|
|
24
|
-
],
|
|
25
|
-
"publishConfig": {
|
|
26
|
-
"access": "public"
|
|
27
|
-
},
|
|
28
|
-
"sideEffects": false,
|
|
29
|
-
"main": "./dist/index.cjs",
|
|
30
|
-
"module": "./dist/index.js",
|
|
31
|
-
"types": "./dist/index.d.ts",
|
|
32
|
-
"exports": {
|
|
33
|
-
".": {
|
|
34
|
-
"types": "./dist/index.d.ts",
|
|
35
|
-
"import": "./dist/index.js",
|
|
36
|
-
"require": "./dist/index.cjs"
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
"files": [
|
|
40
|
-
"dist",
|
|
41
|
-
"README.md",
|
|
42
|
-
"LICENSE"
|
|
43
|
-
],
|
|
44
|
-
"scripts": {
|
|
45
|
-
"build": "tsup",
|
|
46
|
-
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
47
|
-
},
|
|
48
|
-
"//": "react/react-dom are consumer-provided peers. @idosgames/* are real ranges pinned in step with those packages.",
|
|
49
|
-
"dependencies": {
|
|
50
|
-
"@idosgames/core": "^0.1.1",
|
|
51
|
-
"@idosgames/module-sdk": "^0.1.0",
|
|
52
|
-
"@idosgames/react": "^0.1.0"
|
|
53
|
-
},
|
|
54
|
-
"peerDependencies": {
|
|
55
|
-
"react": "^18.3.1 || ^19.0.0",
|
|
56
|
-
"react-dom": "^18.3.1 || ^19.0.0"
|
|
57
|
-
},
|
|
58
|
-
"devDependencies": {
|
|
59
|
-
"@types/react": "^19.2.17",
|
|
60
|
-
"@types/react-dom": "^19.2.3",
|
|
61
|
-
"react": "^19.2.7",
|
|
62
|
-
"react-dom": "^19.2.7"
|
|
63
|
-
}
|
|
64
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@idosgames/app-shell",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "The host runtime for composable modules: module registry, shared event bus, surface allocator, scene stage, and the React Mode Router shell. A project seeds one host shell and plugs N modules into it.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "iDos Games",
|
|
8
|
+
"homepage": "https://github.com/iDos-Games/iDosGamesSDK_TS#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/iDos-Games/iDosGamesSDK_TS.git",
|
|
12
|
+
"directory": "packages/app-shell"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/iDos-Games/iDosGamesSDK_TS/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"idosgames",
|
|
19
|
+
"sdk",
|
|
20
|
+
"host",
|
|
21
|
+
"shell",
|
|
22
|
+
"modules",
|
|
23
|
+
"plugin"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"main": "./dist/index.cjs",
|
|
30
|
+
"module": "./dist/index.js",
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"exports": {
|
|
33
|
+
".": {
|
|
34
|
+
"types": "./dist/index.d.ts",
|
|
35
|
+
"import": "./dist/index.js",
|
|
36
|
+
"require": "./dist/index.cjs"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsup",
|
|
46
|
+
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
47
|
+
},
|
|
48
|
+
"//": "react/react-dom are consumer-provided peers. @idosgames/* are real ranges pinned in step with those packages.",
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@idosgames/core": "^0.1.1",
|
|
51
|
+
"@idosgames/module-sdk": "^0.1.0",
|
|
52
|
+
"@idosgames/react": "^0.1.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"react": "^18.3.1 || ^19.0.0",
|
|
56
|
+
"react-dom": "^18.3.1 || ^19.0.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/react": "^19.2.17",
|
|
60
|
+
"@types/react-dom": "^19.2.3",
|
|
61
|
+
"react": "^19.2.7",
|
|
62
|
+
"react-dom": "^19.2.7"
|
|
63
|
+
}
|
|
64
|
+
}
|