Incompatible Changes
For complete changes, see the CHANGELOG.
0.23.0
- A Sound's
typeis now SoundBusId (SoundType | (string & {})) rather thanSoundType.SoundTypeis unchanged, still exported, and its three values still mean what they meant, so nothing that uses only those values needs to change — but aswitchover a sound'stypethat relied on it being an exhaustive union of three now needs a default arm Sound.voice(),Sound.bgm()andSound.sound()defaulttypeinstead of overwriting it.Sound.voice({src, type: "alice"})used to silently produce avoiceclip; it now produces one on thealicebus. Code that passed atypeto a factory and depended on it being ignored will now be honoured- A
Preferencecopies the defaults object it is handed rather than writing into it. TwoGames in one process no longer share one settings object, and moving a slider no longer rewritesGame.DefaultPreferencefor the rest of the process. A host that mutated the shared object on purpose must now go throughgame.preference
0.22.0
LiveGame.playSoundand a dialog line's voice start a clip at theSound's configured volume rather than at full volume.Sound.voice({src, volume: 0.4})played through either path used to sound at 1 and now sounds at 0.4. Callers that pass an explicit target are unaffectedSound.play()on abgm-typed clip no longer throwsStaticScriptWarning. It logs aconsole.warnand plays. A story that relied on the throw to fail loudly at chain-build time now runs
0.20.0
- The
Chained*type aliases —ChainedControl,ChainedPersistent, and their siblings — were removed. They were@internal, so they never appeared in the emitted declarations, and the public signatures that used them now name the chained type directly:Control.do()returnsProxied<Control, Chained<LogicAction.Actions>>,Persistent.set()returnsProxied<Persistent<T>, Chained<LogicAction.Actions>>. No signature changed shape and no runtime behaviour moved
0.19.1
StackFrameSnapshot.branchesis nowStackSnapshot[]rather than an array of frame arrays, so a branch arrives whole — with itsloopandtag— instead of being reduced to its frames.branches[i][0]becomesbranches[i].frames[0]. The type is experimental and read-only, which is why it was corrected rather than duplicated into a parallel field
0.17.1
- fastForward's
reasongained"stalled". Runtime behaviour for the existing values is unchanged, but the return type is wider: an exhaustiveswitchoverreasonneeds the extra arm to keep compiling. Hosts that ignore the result are unaffected - A fast-forward run can now end early on a step that cannot be skipped. Each suspended line is given
options.stepTimeoutms (default10000) to settle; a step that outlives it ends the run with"stalled"instead of continuing past it. Previously the run hung there and the promise settled neither way, so nothing that works today starts failing — but a host that treats any non-"menu"reason as success should now distinguish"stalled". RaisestepTimeoutfor a story that fast-forwards through long unskippable media
0.17.0
onPreloadComplete,oncePreloadComplete,whenPreloadComplete()andevent:preloaded.completenow fire before the game is entered — while a menu may still be on screen — rather than afternewGame()has mounted a scene. The entry scene is registered for preloading as soon as the story is loaded, which is the point of the release. Nothing fails to compile; the callback simply runs at a different moment- If you used it to gate a loading step, it now does its job better and needs no change
- If you used it to mean "the game has content on screen", switch to
onFirstSceneReady/whenFirstSceneReady(). Those are unchanged and still require a real mounted scene - See Preloading
0.16.0
The transition API was redesigned around two ideas: transition engines are instantiated with new and take a single options object, while the mask patterns they animate are built with the static factories on Mask and passed as the pattern option.
DissolveandFadeInno longer take positional argumentsnew Dissolve(1000, easing)→new Dissolve({duration: 1000, easing})new FadeIn(500, [x, y], easing)→new FadeIn({duration: 500, offset: [x, y], easing})— note the parameter rename fromstartPostooffset
SoftWipe,SoftIris, andBlindswere removed; use Reveal with the matching patternnew SoftWipe({duration, direction, feather})→new Reveal({duration, pattern: Mask.wipe({direction, feather})})new SoftIris({duration, center, feather})→new Reveal({duration, pattern: Mask.iris({center, feather})})new Blinds({duration, orientation, slats})→new Reveal({duration, pattern: Mask.blinds({orientation, slats})})
MaskTransitionwas removed; its hard-edged clips areRevealwith a zero-feather patternMaskTransition.circle({duration, center})→new Reveal({duration, pattern: Mask.iris({center, feather: 0})})MaskTransition.wipe({duration, direction})→new Reveal({duration, pattern: Mask.wipe({direction, feather: 0})})
ThroughColor's static factories were removed; it is now constructed withnewand takes its geometry as apatternThroughColor.fade({duration, color, hold})→new ThroughColor({duration, color, hold})ThroughColor.wipe({direction, feather, ...rest})→new ThroughColor({...rest, pattern: Mask.wipe({direction, feather})})ThroughColor.blinds({orientation, slats, ...rest})→new ThroughColor({...rest, pattern: Mask.blinds({orientation, slats})})ThroughColor.iris({center, feather, ...rest})→new ThroughColor({...rest, pattern: Mask.iris({center, feather}), inverted: true})— note theinverted: true: the classic iris-to-black closes rim-in, which is the pattern's inverted orientation
- Removed option types:
SoftWipeOptions,SoftIrisOptions,BlindsOptions,MaskTransitionCircleOptions,MaskTransitionWipeOptions,ThroughColorFadeOptions,ThroughColorWipeOptions,ThroughColorBlindsOptions,ThroughColorIrisOptions. New exported types:DissolveOptions,FadeInOptions,RevealOptions,ThroughColorOptions,ThroughColorUncover,MaskPattern, and the per-factory*PatternOptionstypes
0.13.0
SavedGame'sstoreis now typed as SerializedNamespaceData instead of StorableData. It always carried the tagged form; only the declaration was wrong. The save format itself did not change, so existing saves keep loading — but code that readsavedGame.game.storeby hand and relied on the old declaration will now see the tagged shape it was actually getting all along- Saves written by 0.12.x and earlier load correctly again, with one exception: a save produced by saving after loading on an affected version has its values tagged twice on disk, which cannot be told apart from a value the game legitimately stored. Such saves are not repaired
image.darken(darkness, duration)now animates overdurationinstead of applying instantly. Previously the animation only ran when an easing was passed as well, and the duration was silently dropped otherwise. Passdurationof0for the old jump-in-place behavior
0.9.0
JumpConfig.unloadScenewas removed
0.8.0
Displayable.scaleis changed. Now it takes two parametersscaleXandscaleYinstead of the previous singlescaleparameter
0.7.0
Routeris deprecated, useLayoutRouteras a more powerful routerPageis refactoredgame.config.skipKeyandgame.config.nextKeyare deprecated, usegame.keyMapinstead
0.6.0
- The game has completely transitioned from a single node currentAction to a StackModel. The model ensures that:
- Awaitable is handled in an explicit way
- Support for sub-stack model recursive calls
- Full support for serialization/deserialization
- Support for scenario operations to break the call stack
- Better branching/merging operations
- Less prone to state clutter when deserializing and undoing
- Is a complete solution for nested operations
game.config.skipIntervalis deprecated, useGamePreference.skipIntervalinstead
0.5.0
game.config.cpsis deprecated, useGamePreference.cpsinstead- Menu GameElementHistory.
selectedmay be null
0.4.0
game.config.elements.say.textIntervalis deprecated, usegame.config.elements.say.cpsinsteadgame.confighas been refactored, see GameConfig for more detailsgame.config.playeris deprecated, usegame.configinsteadgame.config.elementsis deprecated, usegame.configinsteadgame.config.elementStylesis deprecated, usegame.configinstead
0.3.0
- NarraLeaf-React now requires React 19 or later
- Image Config has changed:
- the type of
config.srcshould be a tag definition or a string - In tag-based image config,
config.srcas a resolver function is moved toconfig.src.resolve - Image can't be marked as wearable anymore, use
image.wearorimage.asWearableOfinstead
- the type of
- These methods of
Imagehas been changed:setAppearance,setTags,setSrc->charapplyTransform->transformwearis a new alias foraddWearableasWearableOfis a new alias forbindWearableinit,setPosition,dispose,copyare removedIImageTransitionis removed, useImageTransitioninsteadFadeis removed, use Dissolve instead, which takes the same duration and easing
- These methods of
Texthas been changed:applyTransform->transformapplyTransitionis removed, applying transitions are still in planningITextTransitionis removed, useTextTransitioninstead
- These methods of
Transformhas been changed:overwriteis removed- Transformer API is completely deprecated
- These methods/properties of
Scenehas been changed:activate,deactivateare removed, the game will manage the scene's lifecycle automaticallyapplyTransformis removed, usescene.background.transforminsteadinheritis removedrequestImagePreload->preloadImage
- These methods of
Soundhas been changed:- use
copyto create a new sound instance play,stopandsetVolumemethod can receive adurationparameterfadeis removed, usesetVolumeinstead
- use
- In displayable elements, the transform states are separated from the element states
- These changes are made to
Soundconfigsyncandtypeare removed- use
preloadto use Howler.js's preload feature - use
seekproperty to set the initial seek position
- Scene's config now can't specify the
invertYandinvertXproperties, use story configorigininstead Top,Center,Bottom,HBox, andVBoxare deprecated, usePageRouterAPI insteadITransitions are all deprecated, useTransitionAPI insteadFontSizeTransition->FontSizeBaseImageTransition->ImageTransitionBaseTextTransition->TextTransition
0.2.2
- SceneConfig
invertYis nowtrueby default
0.2.0
- Image constructor signature has changed. Now the first argument must be a config object.
0.1.2
game.config.player.widthandgame.config.player.heightcannot be string anymore