# Migration guide (/faq/migration)



## pixi-vn [#pixi-vn]

### Migration from v1.9.3 to v1.9.4 — canvas transitions/effects namespaces, top-level `tickers` [#migration-from-v193-to-v194--canvas-transitionseffects-namespaces-top-level-tickers]

Version **1.9.4 is not released yet**. The changes below describe the upcoming release.

#### Canvas transitions and effects namespaces [#canvas-transitions-and-effects-namespaces]

Canvas transition helpers and canvas effects are now grouped under discoverable namespaces:
`transitions` contains `showWithDissolve`, `showWithFade`, `moveIn`, `moveOut`, `zoomIn`, `zoomOut`,
`pushIn`, `pushOut`, `removeWithDissolve`, and `removeWithFade`; `effects` contains `shakeEffect`.

The previous flat exports remain available in v1.9.4 for compatibility, but are deprecated. Update
imports and calls when migrating:

```ts
import { moveIn, shakeEffect, zoomIn } from "@drincs/pixi-vn"; // [!code --]
import { effects, transitions } from "@drincs/pixi-vn"; // [!code ++]

await zoomIn("alice", "alice"); // [!code --]
await transitions.zoomIn("alice", "alice"); // [!code ++]

await moveIn("alice", "alice"); // [!code --]
await transitions.moveIn("alice", "alice"); // [!code ++]

shakeEffect("alice"); // [!code --]
effects.shakeEffect("alice"); // [!code ++]
```

The transition and effect behavior and their options are unchanged. This release reorganizes the
canvas helpers to make them easier to discover; it does not remove the old functions. The same
namespaces are available from `@drincs/pixi-vn/canvas`.

#### `canvas.tickers` → top-level `tickers` [#canvastickers--top-level-tickers]

Ticker management is no longer part of the canvas manager: it's its own `tickers` singleton,
exported from `@drincs/pixi-vn` next to `canvas`, `sound`, etc. `canvas.tickers` still works in
v1.9.4 — it just forwards to `tickers` — but it's deprecated. The methods and their arguments are
unchanged; only the receiver changes:

```ts
import { canvas } from "@drincs/pixi-vn"; // [!code --]
import { tickers } from "@drincs/pixi-vn"; // [!code ++]

canvas.tickers.add("alien", new RotateTicker({ speed: 0.2 })); // [!code --]
tickers.add("alien", new RotateTicker({ speed: 0.2 })); // [!code ++]

canvas.tickers.pause({ canvasAlias: "alien" }); // [!code --]
tickers.pause({ canvasAlias: "alien" }); // [!code ++]

await canvas.tickers.forceCompletion(id); // [!code --]
await tickers.forceCompletion(id); // [!code ++]
```

The same applies to every other member (`addSequence`, `find`, `remove`, `removeAll`, `resume`,
`isPaused`, `unlinkComponent`, `transfer`, `completeOnStepEnd`, `onComplete`, `currentTickers`,
`currentTickersSteps`). The type `CanvasTickersInterface` is likewise a deprecated alias of the new
`TickersInterface`:

```ts
import type { CanvasTickersInterface } from "@drincs/pixi-vn"; // [!code --]
import type { TickersInterface } from "@drincs/pixi-vn"; // [!code ++]
```

`TickerBase`, `tickerDecorator`, `RegisteredTickers`, and the `Ticker*` types are still exported
from `@drincs/pixi-vn` exactly as before. Saves are unaffected: tickers are still serialized as part
of the canvas state.

Only code that reached into canvas internals breaks: the ticker bookkeeping on
`CanvasManagerStatic` (`_currentTickers`, `_currentTickersSequence`, `_currentTickersTimeouts`,
`_tickersToCompleteOnStepEnd`, `currentTickersWithoutCreatedBySteps`, `currentTickersSequence`,
`generateTickerId`, `addTickerTimeoutInfo`, `removeTickerTimeout*`) moved to the
`TickersManagerStatic` namespace, where the two former getters are now functions:

```ts
CanvasManagerStatic._currentTickers; // [!code --]
TickersManagerStatic._currentTickers; // [!code ++]

CanvasManagerStatic.currentTickersSequence; // [!code --]
TickersManagerStatic.currentTickersSequence(); // [!code ++]
```

Prefer the public `tickers.currentTickers` / `tickers.currentTickersSteps` over either.

### Migration from v1.8.x to v1.9.0 [#migration-from-v18x-to-v190]

In this release, several APIs that had been marked as deprecated for a while (with a documented
replacement already available) have been removed.

`canvas.addTickersSequence` no longer accepts `Repeat`/`Pause` steps — only `Ticker` instances. If you used them to loop or pause between steps, chain `Ticker`s by duration instead.

```ts
canvas.addTickersSequence("alien", [
    new RotateTicker({ speed: 0.1, clockwise: true }, 2),
    Pause(1), // [!code --]
    new RotateTicker({ speed: 0.2, clockwise: false }, 2),
    Repeat, // [!code --]
]);
```

`forceCompleteBeforeNext` has been removed from the transition helpers (`showWithDissolve`, `moveIn`, `moveOut`, `zoomIn`, `zoomOut`, `pushIn`, `pushOut`, ...) and from ticker options. Use `completeOnContinue` instead, it is no longer just an alias for it.

```ts
await showWithDissolve("alice", "alice", {
    forceCompleteBeforeNext: true, // [!code --]
    completeOnContinue: true, // [!code ++]
});
```

The deprecated `image` field on `AssetMemory` has been removed; use `alias`/`url` instead.

The `navigate` overload of `Game.restoreGameState` has been removed. Configure navigation once via `Game.onNavigate` (or `Game.init({ navigate })`) instead of passing it to every call.

```ts
Game.restoreGameState(data, navigateTo); // [!code --]
Game.restoreGameState(data); // [!code ++]
```

The singular `Game.onError` handler, deprecated since v1.6.0, has been removed. Use `Game.addOnError`/`Game.removeOnError`.

```ts
Game.onError((type, error, { notify, uiTransition }) => { // [!code --]
    notify(uiTransition("allert_error_occurred"), { variant: "error" }); // [!code --]
    console.error(`Error occurred: ${type}`, error); // [!code --]
}); // [!code --]
Game.addOnError((error, { notify, uiTransition }) => { // [!code ++]
    notify(uiTransition("allert_error_occurred"), { variant: "error" }); // [!code ++]
    console.error(`Error occurred`, error); // [!code ++]
}); // [!code ++]
```

#### Manager APIs reorganized into namespaces [#manager-apis-reorganized-into-namespaces]

The `canvas`, `sound`, `storage`, and `narration` managers used to expose dozens of unrelated methods directly on the manager (ticker methods, layer methods, and HTML-layer methods all mixed together on `canvas`, for example). This release groups related operations into namespaced sub-APIs — `canvas.tickers`/`.layers`/`.htmlLayers`, `sound.channels`/`.unsaved`, `storage.temp`/`.flags`, `narration.labels`/`.queries`/`.input`/`.choices` — so each manager reads as a short, discoverable surface instead of a flat list, and related options/overloads live next to each other. See <DynamicLink href="/start/canvas-tickers">Tickers</DynamicLink>, <DynamicLink href="/start/canvas">Canvas</DynamicLink>, <DynamicLink href="/start/sound">Sound</DynamicLink>, <DynamicLink href="/start/flags">Flags</DynamicLink>, <DynamicLink href="/start/temp-storage">Temporary storage</DynamicLink>, and <DynamicLink href="/start/labels-flow">Labels flow</DynamicLink> for the current shape of each namespace.

Almost every old flat method still works and just forwards to its namespaced replacement — it's marked `@deprecated` but not removed, so migrate at your own pace. Two members are an exception: their **type** changed, so they break immediately rather than just warning.

`sound.channels` was `AudioChannelInterface[]`; it is now an object, and the array moved to `.values`:

```ts
sound.channels.map((c) => c.alias); // [!code --]
sound.channels.values.map((c) => c.alias); // [!code ++]
```

`narration.choices` (getter) was `StoredIndexedChoiceInterface[] | undefined`; it now returns a namespace object, and the array moved to `.list`. The *setter*, `narration.choices = [...]`, is unchanged — it's a shortcut for `.list`:

```ts
const choices = narration.choices; // [!code --]
const choices = narration.choices.list; // [!code ++]
```

Every other rename below is a non-breaking, forward-compatible deprecation.

`canvas`'s ticker methods moved to `canvas.tickers` (since v1.9.4 that is itself a deprecated alias of the top-level `tickers` — if you are migrating past v1.9.4, go straight to `tickers.*`, see the v1.9.3 → v1.9.4 section above):

```ts
canvas.transferTickers("old", "new"); // [!code --]
canvas.tickers.transfer("old", "new"); // [!code ++]

canvas.currentTickers; // [!code --]
canvas.tickers.currentTickers; // [!code ++]

canvas.currentTickersSteps; // [!code --]
canvas.tickers.currentTickersSteps; // [!code ++]

canvas.findTicker("t1"); // [!code --]
canvas.tickers.find("t1"); // [!code ++]

canvas.addTicker("alien", new RotateTicker({ speed: 0.2 })); // [!code --]
canvas.tickers.add("alien", new RotateTicker({ speed: 0.2 })); // [!code ++]

canvas.addTickersSequence("alien", steps); // [!code --]
canvas.tickers.addSequence("alien", steps); // [!code ++]

canvas.unlinkComponentFromTicker("alien", RotateTicker); // [!code --]
canvas.tickers.unlinkComponent("alien", RotateTicker); // [!code ++]

canvas.removeAllTickers(); // [!code --]
canvas.tickers.removeAll(); // [!code ++]

canvas.removeTicker("t1"); // [!code --]
canvas.tickers.remove("t1"); // [!code ++]

canvas.pauseTicker({ canvasAlias: "alien" }); // [!code --]
canvas.tickers.pause({ canvasAlias: "alien" }); // [!code ++]

canvas.resumeTicker({ canvasAlias: "alien" }); // [!code --]
canvas.tickers.resume({ canvasAlias: "alien" }); // [!code ++]

canvas.isTickerPaused("alien"); // [!code --]
canvas.tickers.isPaused("alien"); // [!code ++]

canvas.completeTickerOnStepEnd({ id: "t1" }); // [!code --]
canvas.tickers.completeOnStepEnd({ id: "t1" }); // [!code ++]

canvas.forceCompletionOfTicker("t1"); // [!code --]
canvas.tickers.forceCompletion("t1"); // [!code ++]

canvas.onTickerComplete("t1", options); // [!code --]
canvas.tickers.onComplete("t1", options); // [!code ++]
```

`canvas`'s layer methods moved to `canvas.layers`:

```ts
canvas.gameLayer; // [!code --]
canvas.layers.gameLayer; // [!code ++]

canvas.addLayer("ui", uiLayer); // [!code --]
canvas.layers.add("ui", uiLayer); // [!code ++]

canvas.getLayer("ui"); // [!code --]
canvas.layers.get("ui"); // [!code ++]

canvas.removeLayer("ui"); // [!code --]
canvas.layers.remove("ui"); // [!code ++]
```

`canvas`'s HTML-layer methods moved to `canvas.htmlLayers`:

```ts
canvas.addHtmlLayer("ui", root); // [!code --]
canvas.htmlLayers.add("ui", root); // [!code ++]

canvas.getHtmlLayer("ui"); // [!code --]
canvas.htmlLayers.get("ui"); // [!code ++]

canvas.removeHtmlLayer("ui"); // [!code --]
canvas.htmlLayers.remove("ui"); // [!code ++]
```

`sound`'s transient/unsaved playback methods moved to `sound.unsaved`:

```ts
sound.playTransient("ui-click"); // [!code --]
sound.unsaved.playTransient("ui-click"); // [!code ++]

sound.pauseUnsavedAll(); // [!code --]
sound.unsaved.pauseAll(); // [!code ++]

sound.resumeUnsavedAll(); // [!code --]
sound.unsaved.resumeAll(); // [!code ++]

sound.stopTransientAll(); // [!code --]
sound.unsaved.stopTransientAll(); // [!code ++]
```

`sound`'s channel creation/lookup methods moved to `sound.channels` (alongside the `.values` change above):

```ts
sound.addChannel("music"); // [!code --]
sound.channels.add("music"); // [!code ++]

sound.findChannel("music"); // [!code --]
sound.channels.find("music"); // [!code ++]
```

`storage`'s temporary-variable methods moved to `storage.temp` (a new `storage.temp.deadlines` map is exposed alongside them):

```ts
storage.setTempVariable("key", "value"); // [!code --]
storage.temp.set("key", "value"); // [!code ++]

storage.removeTempVariable("key"); // [!code --]
storage.temp.remove("key"); // [!code ++]
```

`storage`'s flag methods moved to `storage.flags`:

```ts
storage.setFlag("introSeen", true); // [!code --]
storage.flags.set("introSeen", true); // [!code ++]

storage.getFlag("introSeen"); // [!code --]
storage.flags.get("introSeen"); // [!code ++]
```

`narration`'s label-stack members moved to `narration.labels`:

```ts
narration.openedLabels; // [!code --]
narration.labels.opened; // [!code ++]

narration.currentLabel; // [!code --]
narration.labels.current; // [!code ++]

narration.closeCurrentLabel(); // [!code --]
narration.labels.closeCurrent(); // [!code ++]

narration.closeAllLabels(); // [!code --]
narration.labels.closeAll(); // [!code ++]
```

`narration`'s read-only queries moved to `narration.queries`:

```ts
narration.isLabelAlreadyCompleted(label); // [!code --]
narration.queries.isLabelAlreadyCompleted(label); // [!code ++]

narration.alreadyCurrentStepMadeChoices; // [!code --]
narration.queries.alreadyCurrentStepMadeChoices; // [!code ++]

narration.isCurrentStepAlreadyOpened; // [!code --]
narration.queries.isCurrentStepAlreadyOpened; // [!code ++]

narration.getTimesLabelOpened(label); // [!code --]
narration.queries.timesLabelOpened(label); // [!code ++]

narration.getTimesChoiceMade(index); // [!code --]
narration.queries.timesChoiceMade(index); // [!code ++]
```

`narration`'s input-request members moved to `narration.input`:

```ts
narration.inputValue; // [!code --]
narration.input.value; // [!code ++]

narration.isRequiredInput; // [!code --]
narration.input.isRequired; // [!code ++]

narration.inputType; // [!code --]
narration.input.type; // [!code ++]

narration.requestInput({ type: "number" }); // [!code --]
narration.input.request({ type: "number" }); // [!code ++]

narration.removeInputRequest(); // [!code --]
narration.input.removeRequest(); // [!code ++]
```

`narration`'s choice selection moved to `narration.choices` (alongside the `.list` change above):

```ts
narration.selectChoice(choices[0], {}); // [!code --]
narration.choices.select(choices[0], {}); // [!code ++]
```

### Migration from v1.7.x to v1.8.0 [#migration-from-v17x-to-v180]

In this version, it has been decided to migrate from `@pixi/sound` to `Tone.js` as the audio system. This decision was made to improve performance, stability, and flexibility of the audio system, as well as to resolve some known issues with `@pixi/sound`.

To migrate to the new audio system, it is necessary to change how the volume of a `media` is modified.

```ts
import { sound } from "@drincs/pixi-vn";

const bgmMedia = sound.find("bgm_cheerful");

if (bgmMedia) bgmMedia.volume = 90; // [!code --]
if (bgmMedia) bgmMedia.volume.value = 90; // [!code ++]
```

Additionally, it will no longer be possible to modify audio assets directly.

```ts
sound.backgroundLoadBundle(AUDIO_BUNDLE_NAME).then(() => {
    // [!code --]
    sound.edit("bgm_cheerful", { volume: 90 }); // [!code --]
}); // [!code --]
```

### Migration from v1.6.x to v1.7.0 [#migration-from-v16x-to-v170]

This version has been designed to improve integration with the template.

The first change introduced is that HTML layouts now use `userSelect: "none"` by default (so text is not selectable). This was done to prevent issues with text selection during UI interactions. If you want to make text selectable, you can simply override this property with `userSelect: "auto"`.

```ts
const htmlLayer = canvas.addHtmlLayer("ui", root, {
    userSelect: "auto", // [!code ++]
});
```

Additionally, some features have been deprecated to centralize navigation management into a single function. You should now use `Game.onNavigate` to handle navigation between UI screens, instead of passing a navigation function to every function that requires one (for example `Game.restoreGameState`).

```ts
Game.onNavigate((path) => {
    // [!code ++]
    navigateTo(path); // [!code ++]
}); // [!code ++]

Game.restoreGameState(data, navigateTo); // [!code --]
Game.restoreGameState(data); // [!code ++]
```

Finally, the ability to implement a custom UI storage has been added, allowing automatic rerendering when game variables change. To do this, you need to use the `storage.setStorageHandler` function to set a handler that updates the state of your custom UI storage whenever a game variable is modified.

```ts
import { Store } from "@tanstack/store";
// Create a TanStack store that mirrors the game storage variables
const gameStore = new Store<Record<string, unknown>>({});

storage.setStorageHandler({
    onSetVariable: (key, value) => {
        gameStore.setState((state) => ({ ...state, [key]: value }));
    },
    onRemoveVariable: (key) => {
        gameStore.setState((state) => {
            const next = { ...state };
            delete next[key];
            return next;
        });
    },
    onClearOldTempVariable: (key) => {
        gameStore.setState((state) => {
            const next = { ...state };
            delete next[key];
            return next;
        });
    },
});
```

### Migration from v1.5.x to v1.6.0 [#migration-from-v15x-to-v160]

In this version, the audio system has been completely redesigned, as the previous implementation was very limiting. In this new version, the `channel` component has been introduced, and the system has been divided into four elements, each responsible for a specific role, unlike before where they could be used in multiple ways, leading to instability (`sound` (manager), `channels`, `media`, sound `assets`).

```ts
import { sound } from '@drincs/pixi-vn'

sound.add('bird', 'resources/bird.mp3'); // [!code --]
sound.play('bird', {
    loop: true,
});

let s = sound.add('bird', 'resources/bird.mp3'); // [!code --]
s.play({ // [!code --]
sound.play('bird', { // [!code ++]
    loop: true,
});

let s = sound.add('bird', { // [!code --]
    url: 'resources/bird.mp3', // [!code --]
    loop: true, // [!code --]
}); // [!code --]
s.play(); // [!code --]
sound.play('bird', { // [!code ++]
    loop: true, // [!code ++]
}); // [!code ++]

s.play(); // [!code --]
sound.play('bird'); // [!code ++]
s.pause(); // [!code --]
sound.pause('bird'); // [!code ++]
s.resume(); // [!code --]
sound.resume('bird'); // [!code ++]
s.stop(); // [!code --]
sound.stop('bird'); // [!code ++]
```

Also, to have a cleaner start to the game, `Game.start` has been added. `Game.start` has the same functionality as `narration.call`, but in the future if I need to add some processes before starting the narration, I can do it without breaking changes. For now, it is just an alias for `narration.call`, but it is recommended to use `Game.start` instead of `narration.call` to start the game.

```ts
Game.clear(); // [!code --]
narration.call("start", gameProps); // [!code --]
Game.start("start", gameProps); // [!code ++]
```

The storage keyword system has also been improved by adopting a common naming convention using `:` as a separator, and by adding the ability to use prefixes to better identify keys (for example `character:alice:friendship`). This change was made to improve the cache management of storage and to make it easier to identify the type of data stored. (This change does not affect the API and will not cause any issues with existing saves).

Finally, error handling has been improved by adding the ability to manage errors through specific handlers.

```ts title="main.ts"
import { Game, drawCanvasErrorHandler } from '@drincs/pixi-vn'

Game.addOnError(drawCanvasErrorHandler()); // [!code ++]

Game.onError((type, error, { notify, uiTransition }) => { // [!code --]
    notify(uiTransition("allert_error_occurred"), { variant: "error" }); // [!code --]
    console.error(`Error occurred: ${type}`, error); // [!code --]
Game.addOnError((error, { notify, uiTransition }) => { // [!code ++]
    notify && uiTransition && notify(uiTransition("allert_error_occurred"), { variant: "error" }); // [!code ++]
    console.error(`Error occurred`, error); // [!code ++]
});
```

### Migration from v1.4.x to v1.5.0 [#migration-from-v14x-to-v150]

In this update, the `pixi.js` dependency has been marked as **external** for non-CDN builds (for example, when using Vite.js or the official templates).

When using a CDN, the version of `pixi.js` bundled with the library will continue to be used. This ensures compatibility, prevents known issues, and preserves the overall integrity of the library.

**Why is `pixi.js` now external?** `pixi.js` does not support multiple instances. Making it external allows developers to safely integrate third-party libraries that are designed to work with `pixi.js`, without running into instance conflicts.

In v1.5.4, the canvas resizing logic has also been changed. If you used an official template, it's recommended to modify the CSS as follows:

```css title="src/index.css"
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin "tailwindcss-motion";

html, /* [!code ++] */
body {
    /* [!code ++] */
    background-color: #242424; /* [!code ++] */
    height: 100%; /* [!code ++] */
} /* [!code ++] */

body {
    margin: 0;
    min-height: 100vh; /* [!code ++] */
    display: flex;
    overflow: hidden;
}
```

### Migration from v1.3.x to v1.4.0 [#migration-from-v13x-to-v140]

This document describes the changes that need to be made to your code to migrate from v1.3.x to v1.4.0.

In this release, some functions for managing "navigation of the narrative" have been modified to allow for the easy implementation of rollback and rollforward. Specifically, it is now possible to execute multiple back and continue requests synchronously, and the system will internally queue them and calculate the delta to execute only the necessary processes. You can read more about this in the <DynamicLink href="/start/labels-flow#rollback-rollforward">Rollback and Rollforward</DynamicLink> section.

For this reason it was necessary to modify the `stepHistory.back()` function and the `Game.init()` function. `stepHistory.back()` now requires a path navigation function rather than a `StepLabelProps` function. You can now define the path navigation function in `Game.init()`.

```ts
stepHistory.back((_path) => {
    // [!code --]
    // TODO: navigate in the url path // [!code --]
    // READ THIS: https://pixi-vn.com/start/interface.html#navigate-switch-between-ui-screens // [!code --]
}); // [!code --]
const gameProps = {}; // [!code ++]
stepHistory.back(gameProps); // [!code ++]
```

```ts
Game.init(body, {
    height: 1080,
    width: 1920,
    backgroundColor: "#303030",
    navigate: (_path) => {
        // [!code ++]
        // TODO: navigate in the url path // [!code ++]
    }, // [!code ++]
}).then(() => {
    // ...
});
```

Some long-deprecated features have been removed in this release, including: `stepHistory.goBack`, `narration.goNext`, `PIXI`, `canvas.canvasWidth`, `canvas.canvasHeight`, `canvas.onEndOfTicker`, `FadeAlphaTicker`, `MoveTicker`, `RotateTicker`, `ZoomTicker`, `narration.canContinue`, `narration.callLabel`, `narration.jumpLabel`, `narration.choiceMenuOptions`, `storage.startingStorage`, `storage.setVariable`, `storage.getVariable` and `storage.removeVariable`.

### Migration from v1.2.x to v1.3.0 [#migration-from-v12x-to-v130]

This document describes the changes that need to be made to your code to migrate from v1.2.x to v1.3.0.

In this release, the canvas component animations have been revolutionized. The core animation function is now <DynamicLink href="/start/canvas-motion">`canvas.animate`</DynamicLink>, a function that combines PixiJS tickers and [`motion`](https://motion.dev/docs/animate). So the provided tickers preset (`FadeAlphaTicker`, `MoveTicker`, `RotateTicker`, `ZoomTicker`) has been deprecated.

### Migration from v0.10.x to v1.0 [#migration-from-v010x-to-v10]

This document describes the changes that need to be made to your code to migrate from v0.10.x to v1.0.

Being version 1.0.0 it is the first complete release of the library. It is a breaking change and the API has changed significantly. The fundamental change of this release was to split the entire engine into independent modules. This allows for a more modular and flexible design, making it easier to use the engine in different contexts. The size of packages has been reduced significantly (150 MB to 50 MB) and the engine is now more efficient and faster.

The `Game` namespace object has been introduced for the which contains all the functionality that exploits multiple modules. This is the main entry point for the engine and is used to access all the functions of the engine.

The `stepHistory` module has been introduced to manage the history of the game. Previously the entire management of the game's history was handled by `narration`.

```ts
clearAllGameDatas(); // [!code --]
Game.clear(); // [!code ++]
```

```ts
canvas // [!code --]
    .initialize(body, { // [!code --]
        height: 1080, // [!code --]
        width: 1920, // [!code --]
        backgroundColor: "#303030", // [!code --]
    }) // [!code --]
    .then(() => { // [!code --]
        // Pixi.JS UI Layer // [!code --]
        canvas.addLayer(CANVAS_UI_LAYER_NAME, new Container()); // [!code --]
Game.init(body, { // [!code ++]
    height: 1080, // [!code ++]
    width: 1920, // [!code ++]
    backgroundColor: "#303030", // [!code ++]
}).then(() => { // [!code ++]
    // Pixi.JS UI Layer // [!code ++]
    canvas.addLayer(CANVAS_UI_LAYER_NAME, new Container()); // [!code ++]
```

```ts
getSaveData(); // [!code --]
Game.exportGameState(); // [!code ++]
```

```ts
getSaveJson(); // [!code --]
JSON.stringify(Game.exportGameState()); // [!code ++]
```

```ts
loadSaveData(data, navigate); // [!code --]
Game.restoreGameState(data, navigate); // [!code ++]
```

```ts
loadSaveJson(data, navigate); // [!code --]
Game.restoreGameState(JSON.parse(dataString) as GameState, navigate); // [!code ++]
```

```ts
loadSaveJson(data, navigate); // [!code --]
Game.restoreGameState(JSON.parse(dataString) as GameState, navigate); // [!code ++]
```

```ts
jsonToSaveData(json); // [!code --]
JSON.parse(json); // [!code ++]
```

```ts
narration.canGoBack; // [!code --]
stepHistory.canGoBack; // [!code ++]
```

```ts
narration.back(); // [!code --]
stepHistory.back(); // [!code ++]
```

```ts
narration.narrativeHistory; // [!code --]
stepHistory.narrativeHistory; // [!code ++]
```

## nqtr [#nqtr]

### Migration from v0.7.x to v0.8.0 (nqtr) [#migration-from-v07x-to-v080-nqtr]

<Callout title="ink" type="error">
  This version does not support save files created with previous versions if temporary `commitment` were used (i.e., if they were added using `routine.add`).

  To make the saves compatible, you need to modify the save file (remember that it is a JSON) by changing the `___nqtr-temporary_commitment___` variable from an array of strings (a list of `commitment` IDs) to an object with keys corresponding to the `commitment` IDs and values corresponding to an object containing `id` and `roomId` (the latter is required to identify the link between the `commitment` and the `room`).

  ```json title="save.json"
  {
      // ...
      "___nqtr-temporary_commitment___": [
          // [!code --]
          "alice_sleep", // [!code --]
          "alice_eat" // [!code --]
      ], // [!code --]
      "___nqtr-temporary_commitment___": {
          // [!code ++]
          "alice_sleep": {
              // [!code ++]
              "id": "alice_sleep", // [!code ++]
              "roomId": "alice_room" // [!code ++]
          }, // [!code ++]
          "alice_eat": {
              // [!code ++]
              "id": "alice_eat", // [!code ++]
              "roomId": "alice_room" // [!code ++]
          } // [!code ++]
      } // [!code ++]
      // ...
  }
  ```
</Callout>

This document describes the changes that need to be made to your code to migrate from v0.7.x to v0.8.0.

In this release, the way to link a `commitment` to a `room` has been changed. You can now define its routine directly inside the `room`. Therefore, instead of passing the `room` in the `commitment` constructor, you can define the routine directly in the `room` constructor.

This change was made to simplify the management of routines and to make the relationship between `commitment` and `room` clearer.

```ts title="models/nqtr/Commitment.ts"
export default class Commitment extends CommitmentStoredClass implements CommitmentInterface {
    constructor(
        id: string,
        characters: CharacterInterface | CharacterInterface[],
        room: RoomInterface, // [!code --]
        props: {
            name?: string;
            image?: PixiUIParam<Commitment>;
            background?: PixiUIParam<Commitment>;
            icon?: ReactUIParam<Commitment>;
            onRun?: OnRunEvent<CommitmentInterface>;
            disabled?: boolean | (() => boolean);
            hidden?: boolean | (() => boolean);
        } & CommitmentStoredClassProps,
    ) {
        characters = Array.isArray(characters) ? characters : [characters];
        super(id, characters, room, props.onRun, props); // [!code --]
        super(id, characters, props.onRun, props); // [!code ++]
        this.name = props.name || "";
        this._image = props.image;
        this._background = props.background;
        this._icon = props.icon;
        this._defaultDisabled = props.disabled || false;
        this._defaultHidden = props.hidden || false;
    }
    // ...
    override get isActive(): boolean { // [!code --]
    override isActive(options?: ActiveScheduling): boolean { // [!code ++]
        if (this.hidden) {
            return false;
        }
        return super.isActive; // [!code --]
        return super.isActive(options); // [!code ++]
    }
}
```

```ts title="models/nqtr/Room.ts"
export default class Room extends RoomStoredClass implements RoomInterface {
    constructor(
        id: string,
        location: LocationInterface,
        props: {
            name: string;
            disabled?: boolean | (() => boolean);
            hidden?: boolean | (() => boolean);
            background: PixiUIParam<Room>;
            activities?: ActivityInterface[];
            routine?: CommitmentInterface[]; // [!code ++]
            isEntrance?: boolean;
        },
    ) {
        super(id, location, props.activities); // [!code --]
        super(id, location, {
            // [!code ++]
            activities: props.activities || [], // [!code ++]
            routine: props.routine || [], // [!code ++]
        }); // [!code ++]
        this.name = props.name;
        this._defaultDisabled = props.disabled || false;
        this._defaultHidden = props.hidden || false;
        this._background = props.background;
        this.isEntrance = props.isEntrance || false;
    }
    // ...
}
```

```ts title="models/nqtr/Activity.ts"
export default class Activity extends ActivityStoredClass implements ActivityInterface {
    // ...
    override get isActive(): boolean { // [!code --]
    override isActive(options?: ActiveScheduling): boolean { // [!code ++]
        if (this.hidden) {
            return false;
        }
        return super.isActive; // [!code --]
        return super.isActive(options); // [!code ++]
    }
}
```

```tsx title="values/routine.tsx"
const aliceSleep = new Commitment("alice_sleep", alice, aliceRoom, { // [!code --]
const aliceSleep = new Commitment("alice_sleep", alice, { // [!code ++]
    priority: 1,
    timeSlot: {
        from: 20,
        to: 10,
    },
    // ...
});

// ...

export const fixedRoutine = [aliceSleep, ...]; // [!code --]

RegisteredCommitments.add(fixedRoutine); // [!code --]
RegisteredCommitments.add([aliceSleep, ...]);  // [!code ++]
```

```ts title="utils/nqtr-utility.ts"
export function initializeNQTR() {
    // ...
    routine.fixedRoutine = fixedRoutine; // [!code --]
}
```

```tsx title="values/rooms.tsx"
export const aliceRoom = new Room("alice_room", mcHome, {
    name: "Alice room",
    background: new TimeSlotsImage({
        morning: "location_aliceroom-0",
        afternoon: "location_aliceroom-1",
        evening: "location_aliceroom-2",
        night: "location_aliceroom-3",
    }),
    routine: [aliceSleep], // [!code ++]
});
```

```ts
routine.add(aliceSleep); // [!code --]
aliceRoom.addCommitment(aliceSleep); // [!code ++]
```

## pixi-vn-ink [#pixi-vn-ink]

### Migration from v1.0.x to v1.1.0 (ink) [#migration-from-v10x-to-v110-ink]

In this release, the base <DynamicLink href="/ink/hashtag">hashtag commands</DynamicLink> (the default `#` commands used to interact with the canvas, such as `# show`, `# moveIn`, etc.) are no longer registered automatically. You now need to call [`addBaseHashtagCommands`](/jsdoc/pixi-vn-ink/index/functions/addBaseHashtagCommands) yourself before they can be used in your ink scripts.

```ts title="content/ink/hashtag-commands.ts"
import { addBaseHashtagCommands } from "@drincs/pixi-vn-ink"; // [!code ++]

addBaseHashtagCommands(); // [!code ++]
```
