Skip to content

State and settings

State follows the reader through the story. It’s saved in bookmarks, and it rewinds and replays with the reader.

Settings aren’t tied to the reader’s place in the story. They aren’t saved in bookmarks, and they don’t change when the reader steps backward or forward. Use them for things like preferences, unlocked galleries, and achievements.

Scripts loaded from your project.json’s main script share one state object and one settings object. Screenplay expressions can read that state too.

Each plugin has its own private state and settings. To share data with other plugins, a plugin provides selectors.

State and settings are plain objects. Both start out as an empty object, {}, unless you set starting values with defineInitialState or defineInitialSettings.

getState returns the current state.

getState<T>(): T
Example
const trust = getState().trust;

setState replaces the current state.

setState<T>(newState: T | ((currentState: T) => T)): T

Pass the new state, or a function that takes the current state and returns the new one. Use the function form when the new state depends on the current state.

setState replaces the whole object. To change only part of it, use updateState.

Example
setState({
trust: 1,
achievements: ["first-blood"],
});
setState((current) => ({
...current,
trust: current.trust + 1,
}));

updateState merges a value into the current state.

updateState<T>(newState: Partial<T> | ((currentState: T) => Partial<T>)): T

Pass the values to merge, or a function that takes the current state and returns them.

updateState only merges the top level. Each top-level key you pass replaces the existing value, so spread nested objects to keep their other values.

Example
updateState({ trust: 8 });
updateState((current) => ({ trust: current.trust + 1 }));

getSettings returns the current settings.

getSettings<T>(): T
Example
const { volume } = getSettings();

setSettings replaces the current settings.

setSettings<T>(newSettings: T | ((currentSettings: T) => T)): T

Pass the new settings, or a function that takes the current settings and returns the new ones. Use the function form when the new settings depend on the current settings.

Example
setSettings({
volume: 0.8,
unlocked: { "prologue-gallery": true },
});
setSettings((current) => ({
...current,
volume: 0.8,
}));

updateSettings merges a value into the current settings.

updateSettings<T>(
newSettings: Partial<T> | ((currentSettings: T) => Partial<T>),
): T

Pass the values to merge, or a function that takes the current settings and returns them.

updateSettings only merges the top level. Each top-level key you pass replaces the existing value, so spread nested objects to keep their other values.

Example
updateSettings({ volume: 0.8 });
updateSettings((current) => ({
unlocked: { ...current.unlocked, "act-1-gallery": true },
}));

defineInitialState sets the state your plugin has when the reader starts a new story.

defineInitialState<T>(initialState: T): void

If you don’t call defineInitialState, your state starts as {}. If you call it twice, Inkweaver throws an error.

When Inkweaver loads saved state, anything missing from the top level comes from your initial state. That only goes one level deep, so to add a value inside a nested object, use a migration.

Example
defineInitialState({ reputation: 0 });

defineStateMigration adds a migration that upgrades state saved by an older version of your plugin.

defineStateMigration<T>(version: string, migrate: StateMigration<T>): void
type StateMigration<T> = (oldState: unknown) => T;
ParameterValue
versionThe version that introduced the change, like "2.0.0". It must be valid semver.
migrateA function that takes state saved by the previous version and returns it in this version’s shape.

When Inkweaver loads state, it runs the migrations for every version after the one that saved the state, oldest first. That’s whatever version your plugin.json said at the time, or project.json for your novel’s own scripts.

If you define the same version twice, Inkweaver throws an error. If the state was saved by a newer version of your plugin than the one that’s running, Inkweaver throws an error and doesn’t load it.

Example
// Before v2, `reputation` was called `trust`.
defineStateMigration("2.0.0", (oldState) => {
const { trust, ...rest } = oldState;
return { ...rest, reputation: trust };
});

defineInitialSettings sets your plugin’s starting settings.

defineInitialSettings<T>(initialSettings: T): void

It works the same way defineInitialState does.

Example
defineInitialSettings({
masterVolume: 1,
musicVolume: 1,
voiceVolume: 1,
});

defineSettingsMigration adds a migration that upgrades settings saved by an older version of your plugin.

defineSettingsMigration<T>(
version: string,
migrate: SettingsMigration<T>,
): void
type SettingsMigration<T> = (oldSettings: unknown) => T;

It works the same way defineStateMigration does, except that Inkweaver loads settings only once, at startup. So if the settings were saved by a newer version of your plugin, the novel doesn’t start.

Example
// V2 splits volume into three channels.
defineSettingsMigration("2.0.0", (oldSettings) => {
const { volume, ...rest } = oldSettings;
return {
...rest,
masterVolume: volume,
musicVolume: volume,
voiceVolume: volume,
};
});