narraleaf-react 0.19.0 → 0.19.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/game/nlcore/action/stackModel.d.ts +8 -2
- package/dist/main.js +1 -1
- package/package.json +114 -114
|
@@ -64,7 +64,13 @@ export type StackModelRawData = {
|
|
|
64
64
|
/**
|
|
65
65
|
* One frame of a read-only {@link StackModel.snapshot} — an action currently on the execution
|
|
66
66
|
* stack. A frame that is a concurrent group ({@link Control.all}/{@link Control.any}) also lists
|
|
67
|
-
* its branches
|
|
67
|
+
* its branches, each a whole {@link StackSnapshot}.
|
|
68
|
+
*
|
|
69
|
+
* `branches` used to be `StackFrameSnapshot[][]` — only each branch's `frames`. That silently threw
|
|
70
|
+
* away everything a nested stack knows about ITSELF: a `Control.repeat` runs as a nested StackModel,
|
|
71
|
+
* so its `loop` counter lived on a snapshot whose frames were kept and whose `loop` and `tag` were
|
|
72
|
+
* dropped on the way out. The counter was therefore unreachable from `getStackSnapshot()` no matter
|
|
73
|
+
* how a caller asked, which is exactly what a debug view most wants to show.
|
|
68
74
|
*
|
|
69
75
|
* **Experimental / read-only.** For tooling (Studio's call-stack view). The exact shape is not a
|
|
70
76
|
* stable contract and may change; do not serialize it or drive game logic from it.
|
|
@@ -73,7 +79,7 @@ export type StackFrameSnapshot = {
|
|
|
73
79
|
actionId: string | null;
|
|
74
80
|
actionType: string | null;
|
|
75
81
|
branchWaitType?: StackModelWaiting["type"];
|
|
76
|
-
branches?:
|
|
82
|
+
branches?: StackSnapshot[];
|
|
77
83
|
};
|
|
78
84
|
/**
|
|
79
85
|
* Read-only view of a StackModel's execution stack. `frames` are ordered top-first (the frame
|
package/dist/main.js
CHANGED
|
@@ -32,7 +32,7 @@ NarraLeaf cannot find the element with the id from the saved game
|
|
|
32
32
|
This may be caused by the damage of the saved game file or the change of the story file`);return t.get(s)})]}))}};pe.EventTypes={"event:state.end":"event:state.end","event:state.player.skip":"event:state.player.skip","event:state.player.lineEnd":"event:state.player.lineEnd","event:state.dialog.change":"event:state.dialog.change","event:state.player.requestFlush":"event:state.player.requestFlush","event:state.player.stageClick":"event:state.player.stageClick","event:state.scene.mount":"event:state.scene.mount","event:state.scene.unmount":"event:state.scene.unmount","event.state.onExpose":"event.state.onExpose","event:state.onRender":"event:state.onRender","event:state:flushPreloadedScenes":"event:state:flushPreloadedScenes","event:state.nvl.enter":"event:state.nvl.enter","event:state.nvl.exit":"event:state.nvl.exit","event:state.nvl.dialogComplete":"event:state.nvl.dialogComplete","event:state.nvl.dialogAppend":"event:state.nvl.dialogAppend","event:state.nvl.visibilityChange":"event:state.nvl.visibilityChange","event:state.nvl.change":"event:state.nvl.change"};var L=pe;var ls=32767,Za=1e3,We=class o{constructor(e,t=void 0){this.liveGame=e;this.__tag=void 0;this.waitingAction=null;this.loopConfig=null;this.loopBodyActions=[];this.loopCondition=null;this.loopStartTime=0;this.loopDebugCheckpoint=0;this.__tag=t,this.stack=new pi().addPushValidator(n=>{let i=this.stack.peek();if(n===i)throw new De("StackModel: Unexpected self-push in stack.");if(o.isCalledActionResult(i)){if(i.wait&&o.isStackModelsAwaiting(i.wait.type,i.wait.stackModels))throw new De("StackModel: Unexpected waiting action in stack. (is calledActionResult: true, wait: true)")}else if(S.isAwaitable(i)&&!i.isSettled())throw new De("StackModel: Unexpected unsettled Awaitable in stack.");if(!o.isCalledActionResult(n)&&!S.isAwaitable(n))throw new De("StackModel: Unexpected non-CalledActionResult or Awaitable in stack.");return!0})}static isStackModel(e){return e instanceof o}static createStackModel(e,t,n){let i=new o(e);return i.deserialize(t,n),i}static createCountLoop(e,t,n){let i=new o(e,"loop:count");return i.initLoop({type:"count",counter:0,limit:t,bodyActionIds:n.map(r=>r.getId()),broken:!1},n,null),i}static createConditionLoop(e,t,n,i){let r=new o(e,"loop:condition");return r.initLoop({type:"condition",counter:0,conditionActionId:n,bodyActionIds:i.map(s=>s.getId()),broken:!1},i,t),r}static isCalledActionResult(e){return!!e&&!this.isStackModel(e)&&!S.isAwaitable(e)&&"type"in e}static fromAction(e){return{type:e.type,node:e.contentNode}}static executeStackModelGroup(e,t){return e==="any"?S.any(...t.map(n=>n.execute())):S.all(...t.map(n=>n.execute()))}static isStackModelsAwaiting(e,t){if(t.length===0)throw new Error("StackModel: StackModels are empty.");return e==="any"?t.every(n=>!n.isEmpty()):t.some(n=>!n.isEmpty())}initLoop(e,t,n){this.loopConfig=e,this.loopBodyActions=t,this.loopCondition=n,this.loopStartTime=Date.now(),this.loopDebugCheckpoint=0,this.shouldContinueLoop(this.liveGame.getGameStateForce())&&this.pushLoopBody()}shouldContinueLoop(e){return!this.loopConfig||this.loopConfig.broken?!1:this.loopConfig.type==="count"?this.loopConfig.counter<(this.loopConfig.limit??0):this.loopCondition?this.loopCondition.evaluate({gameState:e}).value:!1}pushLoopBody(){if(this.loopBodyActions.length!==0)for(let e=this.loopBodyActions.length-1;e>=0;e--)this.stack.push(o.fromAction(this.loopBodyActions[e]))}onIterationComplete(){!this.loopConfig||this.loopConfig.broken||(this.loopConfig.counter++,this.liveGame.game.config.app.debug&&this.checkInfiniteLoop(),this.shouldContinueLoop(this.liveGame.getGameStateForce())&&this.pushLoopBody())}checkInfiniteLoop(){if(!this.loopConfig)return;let e=this.loopConfig.counter;if(e>0&&e%ls===0){let t=Date.now()-this.loopStartTime;if(e-this.loopDebugCheckpoint>=ls&&t<Za){let i=new I(`[NarraLeaf] Potential infinite loop detected!
|
|
33
33
|
Loop has executed ${e} iterations in ${t}ms.
|
|
34
34
|
This is likely a bug in your game script. Check your loop conditions.
|
|
35
|
-
Loop type: ${this.loopConfig.type}, broken: ${this.loopConfig.broken}`);throw this.liveGame.getGameStateForce().logger.error("StackModel",i.message),i}this.loopDebugCheckpoint=e,this.loopStartTime=Date.now()}}breakLoop(){if(!this.loopConfig)throw new I("Cannot break: StackModel is not a loop");this.loopConfig.broken=!0,this.stack.clear()}isLoop(){return this.loopConfig!==null}rollNext(){if(this.stack.isEmpty())return null;let e=this.stack.peek();if(S.isAwaitable(e)&&!e.isSettled())return e;if(o.isCalledActionResult(e)&&e.wait){let n=e.wait.stackModels;if(!n.length)throw new Error("StackModel: Waiting action contains empty stackModels.");if(o.isStackModelsAwaiting(e.wait.type,n))return n.forEach(i=>i.rollNext()),e}this.waitingAction=null;let t=this.stack.pop();if(S.isAwaitable(t)){if(t.isFailed())throw t.error;let n=t.result;if(n)return this.stack.push(n),this.liveGame.getGameStateForce().logger.debug("next action (resolved awaitable)",n.node?.action),n}else return this.waitingAction=t,this.executeActions(t);return null}execute(){let e=null,t=!1,n=new S().registerSkipController(new V(()=>{t=!0,e?.abort()}));return(async()=>{let r=0;for(;!t;){if(r++>this.liveGame.game.config.maxStackModelLoop)throw new Error("StackModel: Suspiciously long waiting loop.");if(this.stack.isEmpty()){if(this.loopConfig&&!this.loopConfig.broken&&(this.onIterationComplete(),!this.stack.isEmpty()))continue;t=!0;break}let s=this.rollNext();if(s){if(S.isAwaitable(s)){if(s.isFailed())throw s.error;if(s.isSettled())continue;if(e=s,await new Promise(a=>{s.onSettled(a)}),s.isFailed())throw s.error;if(s.isAborted()){t=!0;break}}else if(o.isCalledActionResult(s))if(s.wait)e=o.executeStackModelGroup(s.wait.type,s.wait.stackModels),await e;else continue}}})().then(()=>n.resolve()).catch(r=>n.fail(r)),n}abortStackTop(){if(this.stack.isEmpty())return null;let e=this.stack.peek();return e&&S.isAwaitable(e)?(this.stack.pop().abort(),this.waitingAction=null):o.isCalledActionResult(e)&&e.wait&&(e.wait.stackModels.forEach(t=>t.abortStackTop()),this.waitingAction=null),this.waitingAction}getWaitingAwaitable(){if(this.stack.isEmpty())return null;let e=this.stack.peek();return e&&S.isAwaitable(e)&&!e.isSettled()?e:null}getTopSync(){if(this.stack.isEmpty())return null;let e=!1;for(let t=this.stack.size()-1;t>=0;t--){let n=this.stack.get(t);if(n){if(o.isCalledActionResult(n))return n;if(e)throw new De("StackModel: Unexpected non-CalledActionResult in stack.")}else return null;e=!0}return null}peekTopActionId(){for(let e=this.stack.size()-1;e>=0;e--){let t=this.stack.get(e);if(o.isCalledActionResult(t))return t.node?.action?.getId()??null}return null}peekExecutingActionId(){if(this.stack.isEmpty())return null;let e=this.stack.peek();return e&&o.isCalledActionResult(e)?e.node?.action?.getId()??null:null}snapshot(){let e=[];for(let n=this.stack.size()-1;n>=0;n--){let i=this.stack.get(n);if(!o.isCalledActionResult(i))continue;let r={actionId:i.node?.action?.getId()??null,actionType:i.node?.action?.type??null};i.wait?.stackModels&&(r.branchWaitType=i.wait.type,r.branches=i.wait.stackModels.map(s=>s.snapshot()
|
|
35
|
+
Loop type: ${this.loopConfig.type}, broken: ${this.loopConfig.broken}`);throw this.liveGame.getGameStateForce().logger.error("StackModel",i.message),i}this.loopDebugCheckpoint=e,this.loopStartTime=Date.now()}}breakLoop(){if(!this.loopConfig)throw new I("Cannot break: StackModel is not a loop");this.loopConfig.broken=!0,this.stack.clear()}isLoop(){return this.loopConfig!==null}rollNext(){if(this.stack.isEmpty())return null;let e=this.stack.peek();if(S.isAwaitable(e)&&!e.isSettled())return e;if(o.isCalledActionResult(e)&&e.wait){let n=e.wait.stackModels;if(!n.length)throw new Error("StackModel: Waiting action contains empty stackModels.");if(o.isStackModelsAwaiting(e.wait.type,n))return n.forEach(i=>i.rollNext()),e}this.waitingAction=null;let t=this.stack.pop();if(S.isAwaitable(t)){if(t.isFailed())throw t.error;let n=t.result;if(n)return this.stack.push(n),this.liveGame.getGameStateForce().logger.debug("next action (resolved awaitable)",n.node?.action),n}else return this.waitingAction=t,this.executeActions(t);return null}execute(){let e=null,t=!1,n=new S().registerSkipController(new V(()=>{t=!0,e?.abort()}));return(async()=>{let r=0;for(;!t;){if(r++>this.liveGame.game.config.maxStackModelLoop)throw new Error("StackModel: Suspiciously long waiting loop.");if(this.stack.isEmpty()){if(this.loopConfig&&!this.loopConfig.broken&&(this.onIterationComplete(),!this.stack.isEmpty()))continue;t=!0;break}let s=this.rollNext();if(s){if(S.isAwaitable(s)){if(s.isFailed())throw s.error;if(s.isSettled())continue;if(e=s,await new Promise(a=>{s.onSettled(a)}),s.isFailed())throw s.error;if(s.isAborted()){t=!0;break}}else if(o.isCalledActionResult(s))if(s.wait)e=o.executeStackModelGroup(s.wait.type,s.wait.stackModels),await e;else continue}}})().then(()=>n.resolve()).catch(r=>n.fail(r)),n}abortStackTop(){if(this.stack.isEmpty())return null;let e=this.stack.peek();return e&&S.isAwaitable(e)?(this.stack.pop().abort(),this.waitingAction=null):o.isCalledActionResult(e)&&e.wait&&(e.wait.stackModels.forEach(t=>t.abortStackTop()),this.waitingAction=null),this.waitingAction}getWaitingAwaitable(){if(this.stack.isEmpty())return null;let e=this.stack.peek();return e&&S.isAwaitable(e)&&!e.isSettled()?e:null}getTopSync(){if(this.stack.isEmpty())return null;let e=!1;for(let t=this.stack.size()-1;t>=0;t--){let n=this.stack.get(t);if(n){if(o.isCalledActionResult(n))return n;if(e)throw new De("StackModel: Unexpected non-CalledActionResult in stack.")}else return null;e=!0}return null}peekTopActionId(){for(let e=this.stack.size()-1;e>=0;e--){let t=this.stack.get(e);if(o.isCalledActionResult(t))return t.node?.action?.getId()??null}return null}peekExecutingActionId(){if(this.stack.isEmpty())return null;let e=this.stack.peek();return e&&o.isCalledActionResult(e)?e.node?.action?.getId()??null:null}snapshot(){let e=[];for(let n=this.stack.size()-1;n>=0;n--){let i=this.stack.get(n);if(!o.isCalledActionResult(i))continue;let r={actionId:i.node?.action?.getId()??null,actionType:i.node?.action?.type??null};i.wait?.stackModels&&(r.branchWaitType=i.wait.type,r.branches=i.wait.stackModels.map(s=>s.snapshot())),e.push(r)}let t={frames:e};return this.__tag&&(t.tag=this.__tag),this.loopConfig&&(t.loop={type:this.loopConfig.type,counter:this.loopConfig.counter,limit:this.loopConfig.limit,broken:this.loopConfig.broken}),t}executeActions(e){if(!e.node?.action)return null;let t=this.liveGame.executeAction(this.liveGame.getGameStateForce(),e.node.action,{stackModel:this}),n=i=>i?S.isAwaitable(i)?(this.liveGame.getGameStateForce().logger.debug("next action (executed awaitable)",i),this.stack.push(i),i):i.node?.action?(this.liveGame.getGameStateForce().logger.debug("next action (executed)",i),this.stack.push(i),i):null:null;if(Array.isArray(t)){let i=null;for(let r of t){let s=n(r);s&&(i=s)}return i}else{let i=n(t);if(i)return i}return null}isWaiting(){let e=this.stack.peek();return e?S.isAwaitable(e)?!e.isSettled():o.isCalledActionResult(e)&&e.wait?o.isStackModelsAwaiting(e.wait.type,e.wait.stackModels):!1:!1}serialize(e=!0){let t=r=>{if(o.isCalledActionResult(r)){let s=r.node?.action?.getId()??null,a=r.node?.action?.type??null;return r.wait?.stackModels?{type:"link",actionType:a,action:s,stacks:r.wait.stackModels.map(c=>c.serialize()),stackWaitType:r.wait.type}:{type:"action",actionType:a,action:s}}return null},n=this.stack.map(t).filter(function(r){return r!==null});if(e&&this.waitingAction){let r=t(this.waitingAction);r&&n.push(r)}let i={items:n};return this.loopConfig&&(i.loop={type:this.loopConfig.type,counter:this.loopConfig.counter,limit:this.loopConfig.limit,conditionActionId:this.loopConfig.conditionActionId,bodyActionIds:this.loopConfig.bodyActionIds,broken:this.loopConfig.broken}),i}reset(){this.stack.forEach(e=>{o.isCalledActionResult(e)?e.wait?.stackModels.forEach(t=>t.reset()):S.isAwaitable(e)&&e.abort()}),this.waitingAction&&this.waitingAction.wait?.stackModels.forEach(e=>e.reset()),this.waitingAction=null,this.stack.clear(),this.loopConfig=null,this.loopBodyActions=[],this.loopCondition=null,this.loopStartTime=0,this.loopDebugCheckpoint=0}deserialize(e,t){this.reset();let n=e.items;for(let i of n)if(i.type==="action"){if(!i.action)continue;let{actionType:r,action:s}=i,a=t.get(s);if(!a)throw new Error(`Action not found: ${s}`);this.stack.push({type:r,node:a.contentNode,wait:null})}else if(i.type==="link"){let{actionType:r,action:s,stacks:a,stackWaitType:c}=i;if(c==null)throw new Error(`Missing stackWaitType for link action: ${s}`);this.stack.push({type:r,node:s?t.get(s)?.contentNode??null:null,wait:{type:c,stackModels:a.map(l=>o.createStackModel(this.liveGame,l,t))}})}if(e.loop){let i=e.loop,r=[];for(let a of i.bodyActionIds){let c=t.get(a);if(!c)throw new Error(`Loop body action not found: ${a}`);r.push(c)}let s=null;if(i.type==="condition"&&i.conditionActionId){let a=t.get(i.conditionActionId);if(a){let c=a.contentNode.getContent();Array.isArray(c)&&c.length>=2&&z.isLambda(c[1])&&(s=c[1])}}this.loopConfig={type:i.type,counter:i.counter,limit:i.limit,conditionActionId:i.conditionActionId,bodyActionIds:i.bodyActionIds,broken:i.broken},this.loopBodyActions=r,this.loopCondition=s,this.loopStartTime=Date.now(),this.loopDebugCheckpoint=i.counter}return this}isEmpty(){return this.stack.isEmpty()}push(...e){return this.stack.push(...e),this}};var He=class He{constructor(e){this.events=new X;this.story=null;this.gameLock=new ln;this.currentSavedGame=null;this.gameState=void 0;this.stackModel=null;this.asyncStackModels=new Set;this.lastDialog=null;this.mapCache=null;this._currentActionId=null;this.game=e,this._storable=new In,this.initNamespaces()}initNamespaces(){return this._storable.clear().addNamespace(new St(He.GameSpacesKey.game,He.DefaultNamespaces.game)),this.story&&this.story.initPersistent(this._storable),this}getStorable(){return this._storable}get storable(){return this._storable}loadStory(e){return this.story=e.constructStory(),this}serialize(){this.assertGameState();let e=this.story;if(!e)throw new Error("No story loaded");if(!this.currentSavedGame||!this.stackModel)throw new Error("Failed when trying to serialize the game: The game has not started");return{name:this.currentSavedGame.name,meta:{version:_r,created:this.currentSavedGame.meta.created,updated:Date.now(),id:this.currentSavedGame.meta.id,lastSentence:this.lastDialog?.sentence||null,lastSpeaker:this.lastDialog?.speaker||null,storyHash:e.hash()},game:{...this.serializeGameState(),history:this.gameState.gameHistory.serialize()}}}serializeGameState(){this.assertGameState();let e=this.gameState,t=this.story;if(!t)throw new Error("No story loaded");let n=this._storable.toData(),i=e.toData(),r=t.getAllElementStates(),s=this.stackModel.serialize(),a=Array.from(this.asyncStackModels).map(c=>c.serialize());return{store:n,stage:i,elementStates:r,stackModel:s,asyncStackModels:a,services:t.serializeServices()}}captureGameState(){try{return this.serializeGameState()}catch(e){return this.gameState?.logger.warn("LiveGame.captureGameState",e),null}}deserialize(e){if(!e)throw new Error("No saved game provided when trying to deserialize game state");this.assertGameState();let t=this.gameState,n=this.story;if(!n)throw new Error("No story loaded");this.game.hooks.trigger("beforeRestore",[]),t.rollLock.lock(),this.reset(),t.stage.forceRemount();let{game:{store:i,stage:r,elementStates:s,services:a,stackModel:c,asyncStackModels:l}}=e,[u,d]=this.constructMaps();this.initNamespaces(),this._storable.load(i),s.forEach(({id:p,data:h})=>{t.logger.debug("restore element",p);let m=d.get(p);if(!m)throw new Error("Element not found, id: "+p+`
|
|
36
36
|
NarraLeaf cannot find the element with the id from the saved game`);m.reset(),m.fromData(h)}),this.currentSavedGame=e,t.loadData(r,d),this.stackModel.deserialize(c,u),l.forEach(p=>{let h=We.createStackModel(this,p,u);this.asyncStackModels.add(h),t.timelines.attachTimeline(this.executeAsyncStackModel(h))}),n.deserializeServices(a),t.gameHistory.load(e.game.history??[],u),this.game.hooks.trigger("afterRestore",[]),t.events.once(L.EventTypes["event:state.onRender"],()=>{t.schedule(()=>{t.rollLock.unlock(),t.stage.next()},0)}),t.stage.forceUpdate()}getHistory(){return this.assertGameState(),this.gameState.gameHistory.getHistory()}undo(e){if(this.assertGameState(),!this.gameState.actionHistory.ableToUndo(this.gameState.gameHistory)){this.gameState.logger.warn("LiveGame.undo","No action to undo");return}let t=this.gameLock.register().lock();this.stackModel.abortStackTop();let n=e?this.gameState.actionHistory.undoUntil(e):this.gameState.actionHistory.undo(this.gameState.gameHistory);if(n){let[i]=this.constructMaps(),{rootStackSnapshot:r,stackModel:s}=n;n.action.type===cn.say&&this.gameState.isNvlMode()&&this.gameState.suppressNextNvlTyping(),this.stackModel.deserialize(r,i),s===this.stackModel&&this.stackModel.push(We.fromAction(n.action)),this.gameLock.off(t.unlock()),this.gameState.logger.info("LiveGame.undo","Undo until",e,"action",n),this.gameState.stage.forceUpdate(),this.gameState.stage.next(),this.gameState.schedule(()=>{this.gameState&&this.gameState.forceAnimation()},0)}else this.gameState.logger.warn("LiveGame.undo","No action found"),this.gameLock.off(t.unlock())}restoreToHistory(e){this.assertGameState();let t=this.gameState.gameHistory.getByToken(e);if(!t)return this.gameState.logger.warn("LiveGame.restoreToHistory","No history entry for token",e),!1;if(!t.snapshot)return this.gameState.logger.warn("LiveGame.restoreToHistory","History entry has no restore snapshot",e),!1;let n=this.gameState.gameHistory.serializeUntil(e),i={name:this.currentSavedGame?.name??"",meta:this.currentSavedGame?.meta??this.getNewSavedGame().meta,game:{...t.snapshot,history:n}};return this.deserialize(i),!0}dispose(){this.events.clear(),this.gameState?.dispose()}notify(e,t=3e3){this.assertGameState();let n=this.gameState.idManager.generateId(),i=this.gameState.notificationMgr.consume({id:n,message:e,duration:t}),r=S.toPromiseForce(i);return{cancel:()=>{i.abort()},promise:r}}playSound(e){this.assertGameState();let t=e instanceof URL?e.toString():e,n=typeof t=="string"?new Se({src:t}):t;return this.gameState.audioManager.playSoundToken(n)}skipDialog(){this.assertGameState(),this.gameState.events.emit(L.EventTypes["event:state.player.skip"],!0)}async fastForward(e={}){this.assertGameState();let t=this.gameState,n=e.until??"menu",i=typeof n=="object"?n.actionId:null,r=n==="menu"||i!==null,s=e.maxSteps??t.game.config.maxStackModelLoop,a=e.stepTimeout??He.FastForwardStepTimeout,c=i!==null?{reachedTarget:!1}:{},l=t.audioManager.getGlobalVolume();t.audioManager.setGlobalVolume(0),t.setFastForwarding(!0);try{let u=0;for(;u++<s;){if(i!==null&&this.stackModel.peekExecutingActionId()===i)return{reason:"action",reachedTarget:!0};if(r&&t.hasActiveMenu())return{reason:"menu",...c};if(this.stackModel.isEmpty())return{reason:"end",...c};let d=this.stackModel.getWaitingAwaitable();if(d){if(!await He.settleSuspendedStep(t,d,a))return{reason:"stalled",...c}}else t.stage.next(),await Promise.resolve()}return{reason:"maxSteps",...c}}finally{t.setFastForwarding(!1),t.audioManager.setGlobalVolume(l)}}static settleSuspendedStep(e,t,n){return new Promise(i=>{let r=!1,s=null,a,c=d=>{r||(r=!0,s!==null&&(clearTimeout(s),s=null),a?.cancel?.(),i(d))};if(a=t.onSettled(()=>c(!0)),r){a?.cancel?.();return}let l=Date.now()+n,u=()=>{if(!r&&(e.events.emit(L.EventTypes["event:state.player.skip"],!0),!r)){if(Date.now()>=l){c(!1);return}s=setTimeout(u,He.FastForwardSkipInterval)}};u()})}assertScreenshot(){this.assertGameState(),this.assertPlayerElement()}capturePng(){return this.assertScreenshot(),this.gameState.htmlToImage.toPng(this.gameState.mainContentNode,this.getScreenshotOptions())}captureJpeg(){return this.assertScreenshot(),this.gameState.htmlToImage.toJpeg(this.gameState.mainContentNode,this.getScreenshotOptions())}captureSvg(){return this.assertScreenshot(),this.gameState.htmlToImage.toSvg(this.gameState.mainContentNode,this.getScreenshotOptions())}capturePngBlob(){return this.assertScreenshot(),this.assertGameState(),this.assertPlayerElement(),this.gameState.htmlToImage.toBlob(this.gameState.mainContentNode,this.getScreenshotOptions())}onCharacterPrompt(e){return this.events.on(He.EventTypes["event:character.prompt"],e)}onMenuChoose(e){return this.events.on(He.EventTypes["event:menu.choose"],e)}onCurrentActionChange(e){return this.events.on(He.EventTypes["event:action.current"],e)}getCurrentActionId(){return this._currentActionId}getStackSnapshot(){return this.stackModel?{root:this.stackModel.snapshot(),async:Array.from(this.asyncStackModels).map(e=>e.snapshot())}:{root:{frames:[]},async:[]}}newGame(){this.assertGameState();let e=this.gameState,t=e.logger.group("LiveGame (newGame)",!0);this.reset(),this.initNamespaces();let n=this.getNewSavedGame();n.name="NewGame-"+Date.now(),this.currentSavedGame=n;let i=this.story?.entryScene?.getSceneRoot();i?this.stackModel.push(We.fromAction(i)):e.logger.warn("No scene root found");let r=this.story?.getAllElementMap(this.story,this.story?.entryScene?.getSceneRoot()||[]);return r?r.forEach(s=>{e.logger.debug("reset element",s),s.reset()}):e.logger.warn("No elements found"),e.stage.forceUpdate(),e.stage.next(),t.end(),this}waitForRouterExit(){let e=null;return{promise:new Promise(t=>{e=this.game.router.onceExitComplete(()=>{t()})}),cancel:()=>{e&&e.cancel()}}}waitForPageMount(){let e=null;return{promise:new Promise(t=>{e=this.game.router.oncePageMount(()=>{t()})}),cancel:()=>{e&&e.cancel()}}}requestFullScreen(e){this.assertGameState();let t="LiveGame.requestFullScreen";try{let n=this.gameState.playerCurrent;if(!n){this.gameState.logger.warn(t,"No player element found");return}if(n.requestFullscreen)return n.requestFullscreen(e);this.gameState.logger.warn(t,"Fullscreen is not supported")}catch(n){this.gameState.logger.error(t,n)}}exitFullScreen(){this.assertGameState();let e="LiveGame.exitFullScreen";try{if(document.exitFullscreen)return document.exitFullscreen();this.gameState.logger.warn(e,"Fullscreen is not supported")}catch(t){this.gameState.logger.error(e,t)}}constructMaps(){let e=this.story;if(!e)throw new Error("No story loaded");if(this.mapCache)return this.mapCache;let t=new Map,n=new Map;return e.forEachChild(e,e.entryScene?.getSceneRoot()||[],i=>{t.set(i.getId(),i),n.set(i.callee.getId(),i.callee)},{allowFutureScene:!0}),this.mapCache=[t,n],this.mapCache}getScreenshotOptions(){return{quality:this.game.config.screenshotQuality}}onPlayerEvent(e,t,n){this.assertPlayerElement();let i=this.gameState.playerCurrent;return i?(i.addEventListener(e,t,n),{cancel:()=>i.removeEventListener(e,t,n)}):(this.gameState.logger.warn("LiveGame.onEvent","No player element found"),{cancel:()=>{}})}onWindowEvent(e,t,n){return window.addEventListener(e,t,n),{cancel:()=>window.removeEventListener(e,t,n)}}reset(){this.assertGameState();let e=this.gameState;this.resetStackModels(),this.stackModel.reset(),this.currentSavedGame=null,this.lastDialog=null,e.forceReset()}next(){this.assertGameState();let e=this.gameState;if(this.gameLock.isLocked())return this.gameLock;if(!this.story)throw new Error("No story loaded");return this.stackModel.isEmpty()?(e.logger.weakWarn("Game Actions","Action stack is empty"),this.currentSavedGame?e.events.emit("event:state.end"):this.currentSavedGame=null,null):this.stackModel.rollNext()}setLastDialog(e,t){this.lastDialog={sentence:e,speaker:t}}requestAsyncStackModel(e){this.assertGameState();let t=new We(this);return this.asyncStackModels.add(t),t.push(...e),t}executeAsyncStackModel(e){this.assertGameState();let t=e.execute();return t.onFailed(n=>{this.gameState.logger.error("Async StackModel",n)}),t.onSettled(()=>{this.asyncStackModels.delete(e)}),t}createStackModel(e){let t=new We(this);return t.push(...e),t}resetStackModels(){this.asyncStackModels.forEach(e=>e.reset()),this.asyncStackModels.clear()}isPlaying(){return this.stackModel&&!this.stackModel.isEmpty()}executeAction(e,t,n){if(!this.stackModel)throw new Error("Stack model is not initialized");this._currentActionId=t.getId(),this.events.hasListeners(He.EventTypes["event:action.current"])&&this.events.emit(He.EventTypes["event:action.current"],{actionId:t.getId(),actionType:t.type});let i=t.executeAction(e,n);return S.isAwaitable(i)?i:i||null}setGameState(e){if(e&&this.gameState)throw new De("GameState already set");return this.gameState=e,e&&!this.stackModel&&(this.stackModel=new We(this,"$root")),this}getGameState(){return this.gameState}getGameStateForce(){if(!this.gameState)throw new De("GameState not set");return this.gameState}getAllPredictableActions(e,t,n){let i=t?.contentNode||null,r=[],s=[],a=new Set;for(;(i||s.length)&&!(n&&r.length>=n);){if(i||(i=s.pop().contentNode),[vt].some(c=>i?.action&&i.action instanceof c)){i=null;continue}if(i.action&&i.action.is(re,q.jumpTo)){let[c]=i.action.contentNode.getContent(),l=e.getScene(c);if(!l)throw i.action._sceneNotFoundError(i.action.getSceneName(c));if(a.has(l)){i=null;continue}a.add(l),i=l.getSceneRoot()?.contentNode||null;continue}else if(i.action&&i.action.is(ee,_.do)){let[c]=i.action.contentNode.getContent();i.getChild()?.action&&s.push(i.getChild().action),i=c[0]?.contentNode||null}i?.action&&r.push(i.action),i=i?.getChild()||null}return r}clearMainStack(){if(!this.stackModel)throw new De("No stack model found");return this.stackModel.reset(),this}getStackModelForce(){if(!this.stackModel)throw new De("No stack model found");return this.stackModel}getNewSavedGame(){return{name:"",meta:{created:Date.now(),updated:Date.now(),id:jr(),lastSentence:null,lastSpeaker:null,storyHash:this.story?.hash()||""},game:{store:{},stage:{scenes:[],audio:{sounds:[],groups:[]},videos:[]},elementStates:[],services:{},stackModel:{items:[]},asyncStackModels:[]}}}assertGameState(){if(!this.gameState)throw new I("No game state found, make sure you call this method in effect hooks or event handlers")}assertPlayerElement(){if(this.assertGameState(),!this.gameState.playerCurrent)throw new I("Player Element Not Mounted")}};He.DefaultNamespaces={game:{}},He.GameSpacesKey={game:"game"},He.EventTypes={"event:character.prompt":"event:character.prompt","event:menu.choose":"event:menu.choose","event:action.current":"event:action.current"},He.FastForwardStepTimeout=1e4,He.FastForwardSkipInterval=16;var bt=He;var Un=class Un{constructor(e){this.settings=e;this.events=new X;this.events.setMaxListeners(64)}setPreference(e,t){this.settings[e]=t,this.events.emit(Un.EventTypes["event:game.preference.change"],e,t)}getPreference(e){return this.settings[e]}getPreferences(){return this.settings}onPreferenceChange(e,t){return this.events.on(Un.EventTypes["event:game.preference.change"],(n,i)=>{typeof e=="string"?e===n&&t&&t(i):e(n,i)})}importPreferences(e){for(let t in e)Object.prototype.hasOwnProperty.call(e,t)&&this.setPreference(t,e[t])}exportPreferences(){let e={};for(let t in this.settings)Object.prototype.hasOwnProperty.call(this.settings,t)&&(e[t]=this.settings[t]);return e}togglePreference(e){if(typeof this.settings[e]!="boolean")throw new Error(`Preference ${e} is not a boolean`);this.setPreference(e,!this.getPreference(e))}};Un.EventTypes={"event:game.preference.change":"event:game.preference.change"};var On=Un;import"client-only";import Qs,{useEffect as Ml,useRef as tr}from"react";import"client-only";import us,{useContext as ps,useState as _a}from"react";var Io=us.createContext(null);function ds({children:o,game:e}){"use client";let t=new ne({}),[n]=_a(e||t);return us.createElement(Io,{value:n},o)}function M(){let o=ps(Io);if(!o)throw new Error("useGame must be used within a GameProvider");return o}function ms(){return ps(Io)}import ec,{createContext as tc,useContext as fs}from"react";var hs="/",Gi=class Gi{constructor(e,t=Gi.rootPath){this.events=new X;this.currentPath="";this.currentQuery={};this.history=[];this.historyIndex=-1;this.mountedPaths=new Set;this.defaultHandlerPaths=new Set;this.unmountingPaths=new Set;this.updateSyncHooks=new Set;this.transitioning=!1;this.game=e;let{path:n,query:i}=this.parseUrl(t);this.currentPath=n,this.currentQuery=i,this.history.push(t),this.historyIndex=0}getCurrentPath(){return this.currentPath}getPathname(){return this.currentPath}getCurrentQuery(){return{...this.currentQuery}}getQueryParams(){return this.getCurrentQuery()}getCurrentUrl(){return this.buildUrl(this.currentPath,this.currentQuery)}parseUrl(e){let[t,n]=e.split("?"),i={};if(n){let r=n.split("&");for(let s of r){let[a,c]=s.split("=");a&&(i[decodeURIComponent(a)]=c?decodeURIComponent(c):"")}}return{path:t||"/",query:i}}buildUrl(e,t){if(Object.keys(t).length===0)return e;let n=Object.entries(t).map(([i,r])=>`${encodeURIComponent(i)}=${encodeURIComponent(r)}`).join("&");return`${e}?${n}`}getQueryParam(e){return this.currentQuery[e]}setQueryParam(e,t){return this.currentQuery[e]=t,this.updateHistory(),this.emitOnChange(),this}setQueryParams(e){return Object.assign(this.currentQuery,e),this.updateHistory(),this.emitOnChange(),this}removeQueryParam(e){return delete this.currentQuery[e],this.updateHistory(),this.emitOnChange(),this}clearQueryParams(){return this.currentQuery={},this.updateHistory(),this.emitOnChange(),this}hasQueryParam(e){return e in this.currentQuery}getQueryParamKeys(){return Object.keys(this.currentQuery)}getQueryParamCount(){return Object.keys(this.currentQuery).length}getHistory(){return[...this.history]}getHistoryIndex(){return this.historyIndex}canGoBack(){return this.historyIndex>0}canGoForward(){return this.historyIndex<this.history.length-1}navigate(e,t){let{path:n,query:i}=this.parseUrl(e),r=this.resolvePath(n),s={...i,...t};if(this.currentPath===r)return this.currentQuery=s,this.historyIndex>=0&&(this.history[this.historyIndex]=this.buildUrl(r,s)),this.emitOnChange(),this;this.historyIndex<this.history.length-1&&(this.history.length=this.historyIndex+1);let a=this.buildUrl(r,s);return this.history.push(a),this.history.length>this.game.config.maxRouterHistory&&(this.history.shift(),this.historyIndex--),this.historyIndex++,this.currentPath=r,this.currentQuery=s,this.emitOnChange(),this.requestPageTransition(),this}back(){if(this.canGoBack()){this.historyIndex--;let{path:e,query:t}=this.parseUrl(this.history[this.historyIndex]);this.currentPath=e,this.currentQuery=t,this.emitOnChange(),this.requestPageTransition()}return this}forward(){if(this.canGoForward()){this.historyIndex++;let{path:e,query:t}=this.parseUrl(this.history[this.historyIndex]);this.currentPath=e,this.currentQuery=t,this.emitOnChange(),this.requestPageTransition()}return this}replace(e,t){let{path:n,query:i}=this.parseUrl(e),r=this.resolvePath(n),s={...i,...t};return this.currentPath=r,this.currentQuery=s,this.historyIndex>=0?this.history[this.historyIndex]=this.buildUrl(r,s):(this.history.push(this.buildUrl(r,s)),this.historyIndex=0),this.emitOnChange(),this}clear(){return this.currentPath="",this.currentQuery={},this.history=[],this.historyIndex=-1,this.emitOnChange(),this.requestPageTransition(),this}cleanHistory(){return this.history=this.currentPath?[this.buildUrl(this.currentPath,this.currentQuery)]:[],this.historyIndex=this.currentPath?0:-1,this}parsePath(e){return e.split("/").filter(t=>t.length>0)}buildPath(e){return"/"+e.join("/")}getParentPath(e){let t=this.parsePath(e);return t.length<=1?"":this.buildPath(t.slice(0,-1))}matchPath(e,t){let n=this.parsePath(e),i=this.parsePath(t);if(n.length<i.length)return!1;for(let r=0;r<i.length;r++){let s=n[r],a=i[r];if(a!=="*"){if(a.startsWith(":"))continue;if(s!==a)return!1}}return!0}exactMatch(e,t){let n=this.parsePath(e),i=this.parsePath(t);if(n.length!==i.length)return!1;for(let r=0;r<i.length;r++){let s=n[r],a=i[r];if(a!=="*"){if(a.startsWith(":"))continue;if(s!==a)return!1}}return!0}extractParams(e,t){let n={},i=this.parsePath(e),r=this.parsePath(t);if(i.length!==r.length)return n;for(let s=0;s<r.length;s++){let a=r[s];if(a.startsWith(":")){let c=a.slice(1);n[c]=i[s]}}return n}onExitComplete(e){return this.events.on("event:router.onExitComplete",e)}onceExitComplete(e){return this.events.once("event:router.onExitComplete",e)}onPageMount(e){return this.events.on("event:router.onPageMount",e)}oncePageMount(e){return this.events.once("event:router.onPageMount",e)}onUpdate(e){return this.updateSyncHooks.add(e),{cancel:()=>{this.updateSyncHooks.delete(e)}}}emitUpdateSync(){this.updateSyncHooks.forEach(e=>e())}mount(e){if(this.mountedPaths.has(e))throw new I(`Path ${e} is already mounted. This may be caused by multiple capture segments in the same path.`);return this.mountedPaths.add(e),{cancel:()=>{this.unmount(e)}}}unmount(e){this.mountedPaths.delete(e)}mountDefaultHandler(e){if(this.defaultHandlerPaths.has(e))throw new I(`Default handler path ${e} is already mounted.`);return this.defaultHandlerPaths.add(e),{cancel:()=>{this.unmountDefaultHandler(e)}}}unmountDefaultHandler(e){this.defaultHandlerPaths.delete(e)}emitOnPageMount(){this.events.emit("event:router.onPageMount")}onRootExitComplete(e){return this.events.on("event:router.onExitComplete",e)}isActive(){return this.currentPath!==""}onChange(e){return this.events.on("event:router.onChange",e)}emitOnChange(){this.events.emit("event:router.onChange")}resolvePath(e){let t=e.split("?")[0];if(t.startsWith("/"))return t;if(t==="")return this.currentPath;let n=this.parsePath(this.currentPath),i=t.split("/"),r=[];r.push(...n);for(let s=0;s<i.length;s++){let a=i[s];a===""||a==="."||(a===".."?r.length>0&&r.pop():r.push(a))}return this.buildPath(r)}normalizePath(e){let t=e.replace(/\/\/+/g,"/").replace(/\/$/,"");return t===""?"/":(t.startsWith("/"),t)}joinPath(e,...t){let n=this.normalizePath(e),s=[n.startsWith("/")?n:"/"+n,...t.filter(a=>a.length>0)].join("/");return this.normalizePath(s)}updateHistory(){this.historyIndex>=0&&(this.history[this.historyIndex]=this.buildUrl(this.currentPath,this.currentQuery))}requestPageTransition(){let e=()=>{this.transitioning=!0,this.emitUpdateSync(),this.emitOnChange()},t=()=>{this.events.emit("event:router.onTransitionEnd"),this.transitioning=!1,this.emitUpdateSync(),this.emitOnChange(),this.events.emit("event:router.onExitComplete")};if(e(),this.isPathsUnmounting()){let n=this.events.on("event:router.onPathUnmount",()=>{this.isPathsUnmounting()||(n.cancel(),Qe(()=>{t()}))})}else Qe(()=>{t()})}registerUnmountingPath(e){this.unmountingPaths.add(e)}isPathsUnmounting(){return this.unmountingPaths.size>0}unregisterUnmountingPath(e){this.unmountingPaths.delete(e),this.events.emit("event:router.onPathUnmount")}isTransitioning(){return this.transitioning}createToken(e){return Symbol(e)}};Gi.rootPath=hs;var jt=Gi,Ro=tc(null);function gs({children:o}){let e=M();return ec.createElement(Ro,{value:{router:e.router}},o)}function xt(){if(!fs(Ro))throw new Error("useRouter must be used within a RouterProvider");return fs(Ro).router}import vs from"react";import cc from"clsx";import nc,{createContext as ic,useContext as oc,useLayoutEffect as rc,useReducer as sc,useState as ac}from"react";var Mt=class Mt{constructor(){this.state={width:0,height:0,minWidth:800,minHeight:450,paused:!1,scale:0};this.events=new X().setMaxListeners(1/0);this.lockers=[];this.updater=null}update(e,t,n){this.state.width=e,this.state.height=t,this.state.scale=n,this.events.emit(Mt.EventTypes["event:aspectRatio.update"],e,t)}updateMin(e,t){this.state.minWidth=e,this.state.minHeight=t}lock(){let e=Symbol();return this.lockers.push(e),e}unlock(e){if(e&&!this.lockers.includes(e))throw new Error("Locker not found");return this.lockers=this.lockers.filter(t=>t!==e),this.triggerUpdate(),null}isLocked(){return!!this.lockers.length}getStyle(){return{width:`${this.state.width}px`,height:`${this.state.height}px`}}setUpdate(e){this.updater=e}pause(){this.state.paused=!0,this.events.emit(Mt.EventTypes["event:aspectRatio.pause"])}resume(){this.state.paused=!1,this.events.emit(Mt.EventTypes["event:aspectRatio.resume"])}onUpdate(e){return this.events.on(Mt.EventTypes["event:aspectRatio.update"],e).cancel}requestUpdate(){this.events.emit(Mt.EventTypes["event:aspectRatio.requestUpdate"])}onRequestedUpdate(e){return this.events.on(Mt.EventTypes["event:aspectRatio.requestUpdate"],e).cancel}triggerUpdate(){this.updater&&this.updater()}};Mt.EventTypes={"event:aspectRatio.update":"event:aspectRatio.update","event:aspectRatio.pause":"event:aspectRatio.pause","event:aspectRatio.resume":"event:aspectRatio.resume","event:aspectRatio.requestUpdate":"event:aspectRatio.requestUpdate"};var Mo=Mt,No=ic(null);function ys({children:o}){"use client";let[e]=ac(()=>new Mo);return nc.createElement(No,{value:{ratio:e}},o)}function Te(){let o=oc(No),[,e]=sc(n=>n+1,0);if(!No||!o)throw new Error("useRatio must be used within a RatioProvider");let{ratio:t}=o;return rc(()=>(e(),t.onUpdate(()=>{e()})),[t]),o}function At({children:o,className:e,style:t,ref:n,...i}){let{ratio:r}=Te(),s=r.getStyle();return vs.createElement("div",{className:cc("inset-0",e),style:{width:"100%",height:"100%",minWidth:`${r.state.minWidth}px`,minHeight:`${r.state.minHeight}px`},...i},vs.createElement("div",{style:{...s,position:"relative",...t||{}},...i||{},ref:n},o))}import lc,{useEffect as uc}from"react";function dt(o){let e=M(),[t,n]=lc.useState(e.preference.getPreference(o)),i=r=>{e.preference.setPreference(o,r),n(r)};return uc(()=>e.preference.onPreferenceChange(o,n).cancel,[o,e.preference,n]),[t,i]}import Go from"react";import Hi from"react";import Ss from"clsx";function Fi({children:o,className:e,style:t,...n}){let{ratio:i}=Te(),r=M();return Hi.createElement(At,{className:Ss("absolute pointer-events-none w-full h-full"),style:{transform:`scale(${i.state.scale})`,transformOrigin:"left top",width:r.config.width,height:r.config.height,pointerEvents:"none"},"data-element-type":"full",...n},Hi.createElement("div",{className:"absolute inset-0 w-full h-full"},Hi.createElement("div",{className:"inset-0 w-full h-full"},Hi.createElement("div",{className:Ss("pointer-events-auto-rest",e),style:t},o))))}function pc({children:o,className:e,style:t}){return Go.createElement(Go.Fragment,null,Go.createElement(Fi,{style:t,className:e,"data-element-type":"stage","data-code-source":"Stage.tsx"},o))}import Ts from"clsx";import bs from"react";function Vi({className:o,children:e,...t}){let{ratio:n}=Te();return bs.createElement("div",{style:{transform:`scale(${n.state.scale})`,transformOrigin:"left top"},className:Ts("w-full h-full")},bs.createElement("div",{className:Ts("z-20",o),...t},e))}import ct,{useState as Cs}from"react";import{motion as dc}from"motion/react";function Ui({children:o,border:e="solid",color:t="red",tag:n,borderWidth:i=1,as:r="div",ref:s,...a}){let c=M(),[l,u]=Cs(!1);if(!c.config.app.inspector)return ct.createElement(r,{...a,ref:s},o);let d={...a,onMouseEnter:()=>u(!0),onMouseLeave:()=>u(!1),style:{...a.style||{},outline:`${i}px ${e} ${t}`,zIndex:l?1e3:"auto"}};return ct.createElement(r,{...d,ref:s},n&&l&&ct.createElement("span",{className:"absolute top-0 left-0 bg-white text-black border-2 border-black text-sm"},n),o)}function mc({border:o="solid",color:e="red",tag:t,borderWidth:n=1,as:i="img",...r}){let s=M(),[a,c]=Cs(!1);if(!s.config.app.inspector)return ct.createElement(i,{...r});let l={...r,onMouseEnter:()=>c(!0),onMouseLeave:()=>c(!1),style:{...r.style||{},outline:`${o} ${n}px ${e}`,zIndex:a?1e3:"auto"}};return ct.createElement("div",null,ct.createElement(i,{...l}),t&&a&&ct.createElement("span",{className:"absolute top-0 left-0 bg-white text-black border-2 border-black"},t))}function fc(o){return ct.createElement(Ui,{...o,as:"div"})}function hc(o){return ct.createElement(Ui,{...o,as:"span"})}function gc(o){return ct.createElement(mc,{...o,as:"img"})}function yc(o){return ct.createElement(Ui,{...o,as:"button"})}function vc(o){return ct.createElement(Ui,{...o,as:dc.div,ref:o.ref,layout:o.layout})}var Sc={Div:fc,Span:hc,Button:yc,Img:gc,mDiv:vc},nn=Sc;import $i from"clsx";import Fe,{useEffect as xn,useRef as Ps,useState as Ho}from"react";import{useEffect as Tc,useState as bc}from"react";function de(o){let[e,t]=bc(0);Tc(()=>{n()},o??[]);function n(){t(i=>i+1)}return[n,e]}import xs from"react";var on=xs.createContext(null);function mt(){let o=xs.useContext(on);if(!o)throw new Error("useDialogContext must be used within a DialogContext");return o}import Oi,{useCallback as Cc,useLayoutEffect as Kn,useMemo as As,useRef as ws}from"react";import{useIsPresent as xc}from"motion/react";var ft=class ft{constructor(e){this.events=new X;this._forceSkipped=!1;this._idle=!1;this._active=!0;this.config=e,this._state="pending",this.autoForwardScheduler=new ai,this._count=0}get state(){return this._state}get deps(){return[this._count]}isIdle(){return this._idle}setIdle(e){this._idle=e}isActive(){return this._active}setActive(e){return this._active=e,e||this.cancelAutoForward(),this}requestComplete(){this._active&&(this.state==="ended"?this.safeEmit(ft.Events.complete):this.safeEmit(ft.Events.requestComplete))}forceSkip(){this._active&&(this.state==="ended"?this.emitComplete():(this._forceSkipped=!0,this.safeEmit(ft.Events.forceSkip)))}dispatchComplete(){if(this.state==="ended"){this.config.gameState.logger.weakWarn("DialogState","Dialog is already ended. Cannot dispatch complete.");return}if(!this.events.hasListeners(ft.Events.complete)){this.config.gameState.logger.weakWarn("DialogState","No listener for complete event. Cannot dispatch complete.");return}let e=this.config.gameState.game.preference;return this._state="ended",this.config.gameState.completeAdvDialogTyping(this.config.action.id),e.getPreference(ne.Preferences.autoForward)&&this.scheduleAutoForward(),this.emitComplete(),this}emitComplete(){return this.safeEmit(ft.Events.complete),this.emitFlush(),this}isEnded(){return this.state==="ended"}setPause(e){this.isEnded()||(e?this._state="paused":this._state="pending")}isForceSkipped(){return this._forceSkipped}tryScheduleAutoForward(){this.isEnded()&&this.scheduleAutoForward()}cancelAutoForward(){this.autoForwardScheduler.cancelTask()}emitFlush(){return this._count++,this.events.emit(ft.Events.onFlush),this}onFlush(e){return this.events.on(ft.Events.onFlush,e)}safeEmit(e,...t){return this.events.emit(e,...t)===0&&this.config.gameState.logger.weakWarn("DialogState",`Failed to emit event: ${e}. Target Component is not mounted.`),this}scheduleAutoForward(){let e=this.config.gameState.game.preference;!this._active||!e.getPreference(ne.Preferences.autoForward)||this.state!=="ended"||this.autoForwardScheduler.cancelTask().scheduleTask(()=>{this.events.emit(ft.Events.simulateClick)},this.config.gameState.game.config.autoForwardDelay/e.getPreference(ne.Preferences.gameSpeed))}};ft.Events={requestComplete:"event:dialog.requestComplete",complete:"event:dialog.complete",forceSkip:"event:dialog.forceSkip",onFlush:"event:dialog.onFlush",simulateClick:"event:dialog.simulateClick"};var Ee=ft;function Wn({action:o,onFinished:e,useTypeEffect:t=!0,gameState:n,active:i=!0}){let r=xc(),s=i&&r,a=ws(!1),c=ws(o),l=c.current!==o,u=As(()=>o.sentence?.evaluate(Ae.getCtx({gameState:n})),[o.sentence,n]),d=As(()=>new Ee({useTypeEffect:t,action:o,evaluatedWords:u||[],gameState:n,suppressInitialAnimation:l}),[o,n,t,u]),p=n.game.config.dialog,h=Cc(m=>{!s||a.current||(a.current=!0,e?.(m))},[s,e]);return Kn(()=>{a.current=!1},[d]),Kn(()=>{c.current=o},[o]),Kn(()=>(d.setActive(s),()=>{d.setActive(!1)}),[d,s]),Kn(()=>(n.logger.debug("NarraLeaf-React: Say","dialogState",d),d.events.on(Ee.Events.complete,m=>{n.logger.log("NarraLeaf-React: Say","Complete",d.isIdle()),s&&(d.isIdle()||m?h(!1):d.setIdle(!0))}).cancel),[d,h,n,s]),Kn(()=>n.events.on(L.EventTypes["event:state.player.skip"],m=>{s&&(m?(d.setIdle(!0),d.forceSkip()):d.isIdle()?h(!0):d.forceSkip())}).cancel,[d,h,n,s]),Oi.createElement(Oi.Fragment,null,Oi.createElement(on,{value:d},Oi.createElement(p,null)))}import{useEffect as Ki,useState as kc}from"react";import{useEffect as Ac,useState as wc}from"react";function rn(o){let e=M(),[t,n]=wc(e.keyMap.getKeyBinding(o)),i=r=>{e.keyMap.setKeyBinding(o,r),n(r)};return Ac(()=>e.keyMap.onKeyBindingChange(o,n).cancel,[o,e.keyMap,n]),[t,i]}var Cn=(t=>(t.skipAction="skipAction",t.nextAction="nextAction",t))(Cn||{});function Wi({entry:o,gameState:e,words:t,isActive:n,useTypeEffect:i}){let r=M(),[s]=rn("nextAction"),[a]=kc(()=>{let c=o.firedTextEvents??(o.firedTextEvents=new Set);return new Ee({useTypeEffect:i,action:{sentence:o.sentence,character:o.character,words:t,id:o.id},evaluatedWords:t,gameState:e,firedTextEvents:c})});return Ki(()=>{if(!n)return;let c=()=>{e.requestNvlAdvance(o.id)==="typing"&&a.requestComplete()},l=()=>{e.requestNvlSkip(o.id)==="typing"&&a.forceSkip()},u=e.events.on(L.EventTypes["event:state.player.stageClick"],c),d=e.events.on(L.EventTypes["event:state.player.skip"],l);return()=>{u.cancel(),d.cancel()}},[a,o.id,e,n]),Ki(()=>{if(!n)return;let c=u=>{if(u.repeat||!r.keyMap.match("nextAction",u.key))return;e.requestNvlAdvance(o.id)==="typing"&&a.requestComplete()};if(r.config.useWindowListener){let u=r.getLiveGame().onWindowEvent("keydown",c);return()=>{u.cancel()}}let l=r.getLiveGame().onPlayerEvent("keydown",c);return()=>{l.cancel()}},[a,o.id,r,e,n,s]),Ki(()=>{let c=a.events.on(Ee.Events.complete,l=>{n&&(e.completeNvlTyping(o.id),!a.isIdle()&&!l&&a.setIdle(!0))});return()=>{c.cancel()}},[a,o.id,e,n]),Ki(()=>{let c=a.events.on(Ee.Events.simulateClick,()=>{if(!n)return;e.requestNvlAdvance(o.id)==="typing"&&a.requestComplete()});return()=>{c.cancel()}},[a,n]),a}function Ec(o,e){let{expression:t,sound:n}=o.config;if(t){let{image:i,appearance:r}=t;i._setAppearanceSync(r),e.stage.update();let s=e.getExposedState(i);s&&(R.isLayeredSrc(i)?s.flush():s.updateStyleSync())}n&&e.audioManager.play(n,{end:n.state.volume,duration:0})}function zi(o,e,t){e.has(o)||(e.add(o),Ec(o,t))}function ks(o,e,t){let n=[];for(let i of o)if(i.isTextEvent()){let r=i.text;e.has(r)||n.push(r),zi(r,e,t)}return n}function*Vo(o){let e=[...o];for(let t=0;t<e.length;t++){let n=e[t];if(Ne.isPause(n.text)){yield Ne.from(n.text);continue}if(Ze.isTextEvent(n.text)){yield n.text;continue}for(let i=0;i<n.text.length;i++){let r=n.text[i];r===`
|
|
37
37
|
`?yield`
|
|
38
38
|
`:yield{text:r,config:n.config,tag:t,tag2:i,cps:n.config.cps}}}}function Fo(o){let e=Vo(o),t=[];for(let n of e)Ne.isPause(n)||Ze.isTextEvent(n)||t.push(n);return t}function Ds(o,e){o(t=>{let n=t[t.length-1];return n&&n!==`
|
package/package.json
CHANGED
|
@@ -1,114 +1,114 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "narraleaf-react",
|
|
3
|
-
"version": "0.19.
|
|
4
|
-
"description": "A React visual novel player framework",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./dist/main.js",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/main.js",
|
|
12
|
-
"require": "./dist/main.js"
|
|
13
|
-
},
|
|
14
|
-
"./built-in": {
|
|
15
|
-
"types": "./dist/built-in.d.ts",
|
|
16
|
-
"import": "./dist/built-in.js",
|
|
17
|
-
"require": "./dist/built-in.js"
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"scripts": {
|
|
21
|
-
"prebuild": "rimraf dist",
|
|
22
|
-
"build": "cross-env NODE_ENV=production node esbuild.config.js",
|
|
23
|
-
"build:dev": "rimraf dist && cross-env NODE_ENV=development node esbuild.config.js && tsc --emitDeclarationOnly && tsc-alias -p tsconfig.json && node project/postbuild.js",
|
|
24
|
-
"postbuild": "tsc --emitDeclarationOnly && tsc-alias -p tsconfig.json",
|
|
25
|
-
"lint": "eslint \"./src/**/*.{ts,tsx,js,jsx}\"",
|
|
26
|
-
"lint:fix": "eslint --fix \"./src/**/*.{ts,tsx,js,jsx}\"",
|
|
27
|
-
"prepublishOnly": "npm run lint && rimraf dist && cross-env NODE_ENV=production node esbuild.config.js && tsc --emitDeclarationOnly && tsc-alias -p tsconfig.json",
|
|
28
|
-
"typedoc": "typedoc src/index.ts --out doc",
|
|
29
|
-
"prepare": "husky",
|
|
30
|
-
"test": "vitest"
|
|
31
|
-
},
|
|
32
|
-
"devDependencies": {
|
|
33
|
-
"@eslint/eslintrc": "^3.1.0",
|
|
34
|
-
"@eslint/js": "^9.10.0",
|
|
35
|
-
"@tailwindcss/postcss": "^4.1.5",
|
|
36
|
-
"@tailwindcss/postcss7-compat": "^2.2.17",
|
|
37
|
-
"@types/fs-extra": "^11.0.4",
|
|
38
|
-
"@types/howler": "^2.2.11",
|
|
39
|
-
"@types/lodash": "^4.17.7",
|
|
40
|
-
"@types/node": "^22.15.3",
|
|
41
|
-
"@types/react": ">=19",
|
|
42
|
-
"@types/react-dom": ">=19",
|
|
43
|
-
"@typescript-eslint/eslint-plugin": "^8.5.0",
|
|
44
|
-
"@typescript-eslint/parser": "^8.5.0",
|
|
45
|
-
"autoprefixer": "^10.4.21",
|
|
46
|
-
"cross-env": "^7.0.3",
|
|
47
|
-
"cssnano": "^7.0.6",
|
|
48
|
-
"esbuild": "^0.25.3",
|
|
49
|
-
"esbuild-css-modules-plugin": "^3.1.4",
|
|
50
|
-
"esbuild-plugin-alias": "^0.2.1",
|
|
51
|
-
"esbuild-plugin-postcss2": "^0.1.2",
|
|
52
|
-
"esbuild-postcss": "^0.0.4",
|
|
53
|
-
"eslint": "^9.10.0",
|
|
54
|
-
"eslint-plugin-react": "^7.35.2",
|
|
55
|
-
"fs-extra": "^11.3.0",
|
|
56
|
-
"globals": "^15.9.0",
|
|
57
|
-
"husky": "^9.1.6",
|
|
58
|
-
"motion": ">=11 <12",
|
|
59
|
-
"postcss": "^8.5.3",
|
|
60
|
-
"postcss-cli": "^11.0.1",
|
|
61
|
-
"postcss-import": "^16.1.0",
|
|
62
|
-
"postcss-loader": "^8.1.1",
|
|
63
|
-
"postcss-preset-env": "^10.1.6",
|
|
64
|
-
"react": "^19.0.0",
|
|
65
|
-
"react-dom": "^19.0.0",
|
|
66
|
-
"rimraf": "^6.0.1",
|
|
67
|
-
"tailwindcss": "^4.1.5",
|
|
68
|
-
"tsc-alias": "^1.8.10",
|
|
69
|
-
"typedoc": "^0.26.7",
|
|
70
|
-
"typescript": "5.7.2",
|
|
71
|
-
"vitest": "^3.2.4"
|
|
72
|
-
},
|
|
73
|
-
"peerDependencies": {
|
|
74
|
-
"@emotion/is-prop-valid": "*",
|
|
75
|
-
"motion": ">=11 <12",
|
|
76
|
-
"react": ">=19",
|
|
77
|
-
"react-dom": ">=19"
|
|
78
|
-
},
|
|
79
|
-
"peerDependenciesMeta": {
|
|
80
|
-
"react": {
|
|
81
|
-
"optional": false
|
|
82
|
-
},
|
|
83
|
-
"react-dom": {
|
|
84
|
-
"optional": false
|
|
85
|
-
},
|
|
86
|
-
"motion": {
|
|
87
|
-
"optional": false
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
"files": [
|
|
91
|
-
"dist",
|
|
92
|
-
"!dist/**/*.test.d.ts",
|
|
93
|
-
"!dist/**/*.map"
|
|
94
|
-
],
|
|
95
|
-
"keywords": [
|
|
96
|
-
"react",
|
|
97
|
-
"visual-novel",
|
|
98
|
-
"typescript",
|
|
99
|
-
"component",
|
|
100
|
-
"narraleaf",
|
|
101
|
-
"nvl"
|
|
102
|
-
],
|
|
103
|
-
"author": "helloyork@icloud.com",
|
|
104
|
-
"license": "MPL-2.0",
|
|
105
|
-
"dependencies": {
|
|
106
|
-
"@narraleaf/sound": "0.1.0",
|
|
107
|
-
"client-only": "^0.0.1",
|
|
108
|
-
"clsx": "^2.1.1",
|
|
109
|
-
"howler": "^2.2.4",
|
|
110
|
-
"html-to-image": "^1.11.11",
|
|
111
|
-
"prop-types": "^15.8.1"
|
|
112
|
-
},
|
|
113
|
-
"packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
|
|
114
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "narraleaf-react",
|
|
3
|
+
"version": "0.19.1",
|
|
4
|
+
"description": "A React visual novel player framework",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/main.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/main.js",
|
|
12
|
+
"require": "./dist/main.js"
|
|
13
|
+
},
|
|
14
|
+
"./built-in": {
|
|
15
|
+
"types": "./dist/built-in.d.ts",
|
|
16
|
+
"import": "./dist/built-in.js",
|
|
17
|
+
"require": "./dist/built-in.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"prebuild": "rimraf dist",
|
|
22
|
+
"build": "cross-env NODE_ENV=production node esbuild.config.js",
|
|
23
|
+
"build:dev": "rimraf dist && cross-env NODE_ENV=development node esbuild.config.js && tsc --emitDeclarationOnly && tsc-alias -p tsconfig.json && node project/postbuild.js",
|
|
24
|
+
"postbuild": "tsc --emitDeclarationOnly && tsc-alias -p tsconfig.json",
|
|
25
|
+
"lint": "eslint \"./src/**/*.{ts,tsx,js,jsx}\"",
|
|
26
|
+
"lint:fix": "eslint --fix \"./src/**/*.{ts,tsx,js,jsx}\"",
|
|
27
|
+
"prepublishOnly": "npm run lint && rimraf dist && cross-env NODE_ENV=production node esbuild.config.js && tsc --emitDeclarationOnly && tsc-alias -p tsconfig.json",
|
|
28
|
+
"typedoc": "typedoc src/index.ts --out doc",
|
|
29
|
+
"prepare": "husky",
|
|
30
|
+
"test": "vitest"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@eslint/eslintrc": "^3.1.0",
|
|
34
|
+
"@eslint/js": "^9.10.0",
|
|
35
|
+
"@tailwindcss/postcss": "^4.1.5",
|
|
36
|
+
"@tailwindcss/postcss7-compat": "^2.2.17",
|
|
37
|
+
"@types/fs-extra": "^11.0.4",
|
|
38
|
+
"@types/howler": "^2.2.11",
|
|
39
|
+
"@types/lodash": "^4.17.7",
|
|
40
|
+
"@types/node": "^22.15.3",
|
|
41
|
+
"@types/react": ">=19",
|
|
42
|
+
"@types/react-dom": ">=19",
|
|
43
|
+
"@typescript-eslint/eslint-plugin": "^8.5.0",
|
|
44
|
+
"@typescript-eslint/parser": "^8.5.0",
|
|
45
|
+
"autoprefixer": "^10.4.21",
|
|
46
|
+
"cross-env": "^7.0.3",
|
|
47
|
+
"cssnano": "^7.0.6",
|
|
48
|
+
"esbuild": "^0.25.3",
|
|
49
|
+
"esbuild-css-modules-plugin": "^3.1.4",
|
|
50
|
+
"esbuild-plugin-alias": "^0.2.1",
|
|
51
|
+
"esbuild-plugin-postcss2": "^0.1.2",
|
|
52
|
+
"esbuild-postcss": "^0.0.4",
|
|
53
|
+
"eslint": "^9.10.0",
|
|
54
|
+
"eslint-plugin-react": "^7.35.2",
|
|
55
|
+
"fs-extra": "^11.3.0",
|
|
56
|
+
"globals": "^15.9.0",
|
|
57
|
+
"husky": "^9.1.6",
|
|
58
|
+
"motion": ">=11 <12",
|
|
59
|
+
"postcss": "^8.5.3",
|
|
60
|
+
"postcss-cli": "^11.0.1",
|
|
61
|
+
"postcss-import": "^16.1.0",
|
|
62
|
+
"postcss-loader": "^8.1.1",
|
|
63
|
+
"postcss-preset-env": "^10.1.6",
|
|
64
|
+
"react": "^19.0.0",
|
|
65
|
+
"react-dom": "^19.0.0",
|
|
66
|
+
"rimraf": "^6.0.1",
|
|
67
|
+
"tailwindcss": "^4.1.5",
|
|
68
|
+
"tsc-alias": "^1.8.10",
|
|
69
|
+
"typedoc": "^0.26.7",
|
|
70
|
+
"typescript": "5.7.2",
|
|
71
|
+
"vitest": "^3.2.4"
|
|
72
|
+
},
|
|
73
|
+
"peerDependencies": {
|
|
74
|
+
"@emotion/is-prop-valid": "*",
|
|
75
|
+
"motion": ">=11 <12",
|
|
76
|
+
"react": ">=19",
|
|
77
|
+
"react-dom": ">=19"
|
|
78
|
+
},
|
|
79
|
+
"peerDependenciesMeta": {
|
|
80
|
+
"react": {
|
|
81
|
+
"optional": false
|
|
82
|
+
},
|
|
83
|
+
"react-dom": {
|
|
84
|
+
"optional": false
|
|
85
|
+
},
|
|
86
|
+
"motion": {
|
|
87
|
+
"optional": false
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"files": [
|
|
91
|
+
"dist",
|
|
92
|
+
"!dist/**/*.test.d.ts",
|
|
93
|
+
"!dist/**/*.map"
|
|
94
|
+
],
|
|
95
|
+
"keywords": [
|
|
96
|
+
"react",
|
|
97
|
+
"visual-novel",
|
|
98
|
+
"typescript",
|
|
99
|
+
"component",
|
|
100
|
+
"narraleaf",
|
|
101
|
+
"nvl"
|
|
102
|
+
],
|
|
103
|
+
"author": "helloyork@icloud.com",
|
|
104
|
+
"license": "MPL-2.0",
|
|
105
|
+
"dependencies": {
|
|
106
|
+
"@narraleaf/sound": "0.1.0",
|
|
107
|
+
"client-only": "^0.0.1",
|
|
108
|
+
"clsx": "^2.1.1",
|
|
109
|
+
"howler": "^2.2.4",
|
|
110
|
+
"html-to-image": "^1.11.11",
|
|
111
|
+
"prop-types": "^15.8.1"
|
|
112
|
+
},
|
|
113
|
+
"packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
|
|
114
|
+
}
|