@novely/core 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -88,6 +88,49 @@ type AudioHandle = {
88
88
  pause: () => void;
89
89
  play: () => void;
90
90
  };
91
+ type Context = {
92
+ id: string;
93
+ get root(): HTMLElement;
94
+ set root(value: HTMLElement);
95
+ character: (character: string) => CharacterHandle;
96
+ background: (background: string | BackgroundImage) => void;
97
+ dialog: (content: string, name: string, character: string | undefined, emotion: string | undefined, resolve: () => void) => void;
98
+ choices: (question: string, choices: [name: string, active: boolean][], resolve: (selected: number) => void) => void;
99
+ input: (question: string, onInput: (meta: ActionInputOnInputMeta<Lang, State>) => void, setup: ActionInputSetup, resolve: () => void) => void;
100
+ clear: (keep: Set<keyof DefaultActionProxy>, keepCharacters: Set<string>, keepAudio: {
101
+ music: Set<string>;
102
+ sounds: Set<string>;
103
+ }, resolve: () => void) => void;
104
+ custom: (fn: CustomHandler<Lang, State>, push: () => void) => Thenable<void>;
105
+ clearCustom: (fn: CustomHandler<Lang, State>) => void;
106
+ text: (str: string, resolve: () => void) => void;
107
+ vibrate: (pattern: VibratePattern) => void;
108
+ audio: {
109
+ voice: (source: string) => void;
110
+ voiceStop: () => void;
111
+ music: (source: string, method: 'music' | 'sound', loop?: boolean) => AudioHandle;
112
+ /**
113
+ * Stop all sounds
114
+ */
115
+ clear: () => void;
116
+ /**
117
+ * Destroy
118
+ */
119
+ destroy: () => void;
120
+ /**
121
+ * Initialize audio service, attach events, etc
122
+ */
123
+ start: () => void;
124
+ };
125
+ meta: {
126
+ get restoring(): boolean;
127
+ set restoring(value: boolean);
128
+ get preview(): boolean;
129
+ set preview(value: boolean);
130
+ get goingBack(): boolean;
131
+ set goingBack(value: boolean);
132
+ };
133
+ };
91
134
  type Renderer = {
92
135
  misc: {
93
136
  /**
@@ -112,11 +155,11 @@ type Renderer = {
112
155
  /**
113
156
  * Shows the screen
114
157
  */
115
- showScreen(name: NovelyScreen | 'loading'): void;
158
+ showScreen(name: NovelyScreen): void;
116
159
  /**
117
160
  * Returns current screen
118
161
  */
119
- getScreen(): NovelyScreen | 'loading' | (string & Record<never, never>);
162
+ getScreen(): NovelyScreen | (string & Record<never, never>);
120
163
  /**
121
164
  * Shows prompt to exit
122
165
  */
@@ -131,80 +174,35 @@ type Renderer = {
131
174
  unmount(): void;
132
175
  };
133
176
  };
134
- getContext: (context: string) => {
135
- id: string;
136
- get root(): HTMLElement;
137
- set root(value: HTMLElement);
138
- character: (character: string) => CharacterHandle;
139
- background: (background: string | BackgroundImage) => void;
140
- dialog: (content: string, name: string, character: string | undefined, emotion: string | undefined, resolve: () => void) => void;
141
- choices: (question: string, choices: [name: string, actions: ValidAction[], active?: boolean][], resolve: (selected: number) => void) => void;
142
- input: (question: string, onInput: (meta: ActionInputOnInputMeta<string, State>) => void, setup: ActionInputSetup, resolve: () => void) => void;
143
- clear: (keep: Set<keyof DefaultActionProxy>, keepCharacters: Set<string>, keepAudio: {
144
- music: Set<string>;
145
- sounds: Set<string>;
146
- }, resolve: () => void) => void;
147
- custom: (fn: Parameters<DefaultActionProxy['custom']>[0], push: () => void) => Thenable<void>;
148
- clearCustom: (fn: Parameters<DefaultActionProxy['custom']>[0]) => void;
149
- text: (str: string, resolve: () => void) => void;
150
- vibrate: (pattern: VibratePattern) => void;
151
- audio: {
152
- voice: (source: string) => void;
153
- voiceStop: () => void;
154
- music: (source: string, method: 'music' | 'sound', loop?: boolean) => AudioHandle;
155
- /**
156
- * Stop all sounds
157
- */
158
- clear: () => void;
159
- /**
160
- * Destroy
161
- */
162
- destroy: () => void;
163
- /**
164
- * Initialize audio service, attach events, etc
165
- */
166
- start: () => void;
167
- };
168
- meta: {
169
- get restoring(): boolean;
170
- set restoring(value: boolean);
171
- get preview(): boolean;
172
- set preview(value: boolean);
173
- get goingBack(): boolean;
174
- set goingBack(value: boolean);
175
- };
176
- store: unknown;
177
- setStore: unknown;
178
- };
177
+ getContext: (context: string) => Context;
179
178
  removeContext: (context: string) => void;
180
179
  };
181
- type Context = ReturnType<Renderer['getContext']>;
182
180
  type RendererInit = {
183
- characters: Record<string, Character>;
184
- set: (save: Save) => Promise<void>;
185
- restore: (save?: Save) => Promise<void>;
186
- save: (override?: boolean, type?: Save[2][1]) => void;
181
+ characters: Record<string, Character<Lang>>;
182
+ set: (save: Save<State>) => Promise<void>;
183
+ restore: (save?: Save<State>) => Promise<void>;
184
+ save: (override?: boolean, type?: Save<State>[2][1]) => void;
187
185
  newGame: () => void;
188
186
  exit: (force?: boolean) => void;
189
187
  back: () => Promise<void>;
190
- languages: string[];
188
+ languages: Lang[];
191
189
  /**
192
190
  * Translation function
193
191
  */
194
- t: (key: BaseTranslationStrings, lang: string) => string;
192
+ t: (key: BaseTranslationStrings, lang: Lang) => string;
195
193
  /**
196
194
  * Store that tracks data updates
197
195
  */
198
- $: Stored<StorageData>;
196
+ storageData: Stored<StorageData<Lang, Data>>;
199
197
  /**
200
198
  * Store that used to communicate between renderer and core
201
199
  */
202
- $$: Stored<CoreData>;
200
+ coreData: Stored<CoreData>;
203
201
  /**
204
202
  * There is different context, and the main one which is used for game
205
203
  */
206
204
  mainContextKey: string;
207
- preview: (save: Save, name: string) => Promise<void>;
205
+ preview: (save: Save<State>, name: string) => Promise<void>;
208
206
  removeContext: (name: string) => void;
209
207
  getStateFunction: (context: string) => StateFunction<State>;
210
208
  };
@@ -212,6 +210,7 @@ type RendererInit = {
212
210
  declare const getLanguage: (languages: string[]) => string;
213
211
 
214
212
  type Thenable<T> = T | Promise<T>;
213
+ type NoInfer<T> = [T][T extends any ? 0 : never];
215
214
  type PathItem = [null, number | string] | ['jump', string] | ['choice', number] | ['choice:exit'] | ['condition', string] | ['condition:exit'] | ['exit'] | ['block', string] | ['block:exit'];
216
215
  type Path = PathItem[];
217
216
  type State = Record<string, any>;
@@ -225,7 +224,7 @@ type Save<S extends State = State> = [
225
224
  meta: SaveMeta
226
225
  ];
227
226
  type Lang = string;
228
- type TypewriterSpeed = 'Slow' | 'Medium' | 'Fast' | 'Auto' | (string & Record<never, never>);
227
+ type TypewriterSpeed = 'Slow' | 'Medium' | 'Fast' | 'Auto';
229
228
  type SoundVolume = number;
230
229
  type StorageMeta<L extends string = string> = [
231
230
  lang: L,
@@ -246,7 +245,7 @@ type Stack = {
246
245
  push(value: Save): void;
247
246
  clear(): void;
248
247
  };
249
- type NovelyScreen = 'mainmenu' | 'game' | 'saves' | 'settings';
248
+ type NovelyScreen = 'mainmenu' | 'game' | 'saves' | 'settings' | 'loading';
250
249
  /**
251
250
  * @see https://pendletonjones.com/deep-partial
252
251
  */
@@ -269,7 +268,7 @@ type TranslationDescription = {
269
268
  plural?: Record<string, Pluralization>;
270
269
  actions?: TranslationActions;
271
270
  };
272
- interface NovelyInit<Languages extends string, Characters extends Record<string, Character<Languages>>, StateScheme extends State, DataScheme extends Data> {
271
+ interface NovelyInit<$Language extends Lang, Characters extends Record<string, Character<NoInfer<$Language>>>, StateScheme extends State, DataScheme extends Data> {
273
272
  /**
274
273
  * An object containing the characters in the game.
275
274
  * @example
@@ -301,7 +300,7 @@ interface NovelyInit<Languages extends string, Characters extends Record<string,
301
300
  /**
302
301
  * A function that returns a Renderer object used to display the game's content
303
302
  */
304
- renderer: (characters: RendererInit) => Renderer;
303
+ renderer: (initializationData: RendererInit) => Renderer;
305
304
  /**
306
305
  * An optional property that specifies the initial screen to display when the game starts
307
306
  */
@@ -328,7 +327,7 @@ interface NovelyInit<Languages extends string, Characters extends Record<string,
328
327
  * })
329
328
  * ```
330
329
  */
331
- translation: Record<Languages, TranslationDescription>;
330
+ translation: Record<$Language, TranslationDescription>;
332
331
  /**
333
332
  * Initial state value
334
333
  *
@@ -375,7 +374,7 @@ interface NovelyInit<Languages extends string, Characters extends Record<string,
375
374
  * })
376
375
  * ```
377
376
  */
378
- getLanguage?: (languages: string[], original: typeof getLanguage) => string;
377
+ getLanguage?: (languages: NoInfer<$Language>[], original: typeof getLanguage) => $Language | (string & Record<never, never>);
379
378
  /**
380
379
  * Ignores saved language, and uses `getLanguage` to get it on every engine start
381
380
  * @default false
@@ -631,4 +630,4 @@ declare const novely: <Languages extends string, Characters extends Record<strin
631
630
  destroy(): void;
632
631
  };
633
632
 
634
- export { type ActionProxy, type AllowedContent, type AudioHandle, type BaseTranslationStrings, type Character, type CharacterHandle, type Context, type CoreData, type CustomHandler, type CustomHandlerFunctionGetFn, type CustomHandlerFunctionParameters, type CustomHandlerGetResult, type CustomHandlerGetResultDataFunction, type DefaultActionProxy, EN, type Emotions, type FunctionableValue, type GetActionParameters, JP, KK, type Lang, type NovelyInit, type NovelyScreen, type Path, type PluralType, type Pluralization, RU, type Renderer, type RendererInit, type Save, type Stack, type StackHolder, type Storage, type StorageData, type StorageMeta, type Stored, type Story, type TextContent, type Thenable, type TranslationActions, type TypewriterSpeed, type ValidAction, localStorageStorage, novely };
633
+ export { type ActionInputOnInputMeta, type ActionInputSetup, type ActionProxy, type AllowedContent, type AudioHandle, type BackgroundImage, type BaseTranslationStrings, type Character, type CharacterHandle, type Context, type CoreData, type CustomHandler, type CustomHandlerFunctionGetFn, type CustomHandlerFunctionParameters, type CustomHandlerGetResult, type CustomHandlerGetResultDataFunction, type Data, type DeepPartial, type DefaultActionProxy, EN, type Emotions, type FunctionableValue, type GetActionParameters, JP, KK, type Lang, type NovelyInit, type NovelyScreen, type Path, type PluralType, type Pluralization, RU, type Renderer, type RendererInit, type Save, type Stack, type StackHolder, type State, type StateFunction, type Storage, type StorageData, type StorageMeta, type Stored, type Story, type TextContent, type Thenable, type TranslationActions, type TypewriterSpeed, type ValidAction, localStorageStorage, novely };
@@ -953,7 +953,7 @@ var Novely = (() => {
953
953
  data: klona(defaultData),
954
954
  meta: [getLanguageWithoutParameters(), DEFAULT_TYPEWRITER_SPEED, 1, 1, 1]
955
955
  };
956
- const $ = store(initialData);
956
+ const storageData = store(initialData);
957
957
  const coreData = store({
958
958
  dataLoaded: false
959
959
  });
@@ -975,10 +975,10 @@ var Novely = (() => {
975
975
  const throttledOnStorageDataChange = throttle(onStorageDataChange, throttleTimeout);
976
976
  const throttledEmergencyOnStorageDataChange = throttle(() => {
977
977
  if (saveOnUnload === true || saveOnUnload === "prod" && !DEV) {
978
- onStorageDataChange($.get());
978
+ onStorageDataChange(storageData.get());
979
979
  }
980
980
  }, 10);
981
- $.subscribe(throttledOnStorageDataChange);
981
+ storageData.subscribe(throttledOnStorageDataChange);
982
982
  const getStoredData = async () => {
983
983
  let stored = await storage.get();
984
984
  for (const migration of migrations) {
@@ -995,7 +995,7 @@ var Novely = (() => {
995
995
  stored.data = defaultData;
996
996
  }
997
997
  dataLoaded.resolve();
998
- $.set(stored);
998
+ storageData.set(stored);
999
999
  };
1000
1000
  storageDelay.then(getStoredData);
1001
1001
  const initial = getDefaultSave(klona(defaultState));
@@ -1013,7 +1013,7 @@ var Novely = (() => {
1013
1013
  return;
1014
1014
  const stack = useStack(MAIN_CONTEXT_KEY);
1015
1015
  const current = klona(stack.value);
1016
- $.update((prev) => {
1016
+ storageData.update((prev) => {
1017
1017
  const isLatest = findLastIndex(prev.saves, (value) => times.has(value[2][0])) === prev.saves.length - 1;
1018
1018
  current[2][0] = intime(Date.now());
1019
1019
  current[2][1] = type;
@@ -1035,7 +1035,7 @@ var Novely = (() => {
1035
1035
  return;
1036
1036
  const save2 = getDefaultSave(klona(defaultState));
1037
1037
  if (autosaves) {
1038
- $.update((prev) => {
1038
+ storageData.update((prev) => {
1039
1039
  return prev.saves.push(save2), prev;
1040
1040
  });
1041
1041
  }
@@ -1050,10 +1050,10 @@ var Novely = (() => {
1050
1050
  const restore = async (save2) => {
1051
1051
  if (!coreData.get().dataLoaded)
1052
1052
  return;
1053
- let latest = save2 || $.get().saves.at(-1);
1053
+ let latest = save2 || storageData.get().saves.at(-1);
1054
1054
  if (!latest) {
1055
1055
  latest = klona(initial);
1056
- $.update((prev) => {
1056
+ storageData.update((prev) => {
1057
1057
  prev.saves.push(latest);
1058
1058
  return prev;
1059
1059
  });
@@ -1137,7 +1137,7 @@ var Novely = (() => {
1137
1137
  ctx.audio.destroy();
1138
1138
  const [time, type] = current[2];
1139
1139
  if (type === "auto" && interacted <= 1 && times.has(time)) {
1140
- $.update((prev) => {
1140
+ storageData.update((prev) => {
1141
1141
  prev.saves = prev.saves.filter((save2) => save2 !== current);
1142
1142
  return prev;
1143
1143
  });
@@ -1208,8 +1208,8 @@ var Novely = (() => {
1208
1208
  removeContext,
1209
1209
  getStateFunction,
1210
1210
  languages,
1211
- $,
1212
- $$: coreData
1211
+ storageData,
1212
+ coreData
1213
1213
  });
1214
1214
  const useStack = createUseStackFunction(renderer);
1215
1215
  useStack(MAIN_CONTEXT_KEY).push(initial);
@@ -1303,7 +1303,7 @@ var Novely = (() => {
1303
1303
  const name = (() => {
1304
1304
  const c = character;
1305
1305
  const cs = characters;
1306
- const [lang] = $.get().meta;
1306
+ const [lang] = storageData.get().meta;
1307
1307
  if (c && c in cs) {
1308
1308
  const block = cs[c].name;
1309
1309
  if (typeof block === "string") {
@@ -1335,7 +1335,7 @@ var Novely = (() => {
1335
1335
  function({ ctx, push }, [fn]) {
1336
1336
  const { restoring, goingBack, preview: preview2 } = ctx.meta;
1337
1337
  const result = fn({
1338
- lang: $.get().meta[0],
1338
+ lang: storageData.get().meta[0],
1339
1339
  goingBack,
1340
1340
  restoring,
1341
1341
  preview: preview2,
@@ -1354,13 +1354,13 @@ var Novely = (() => {
1354
1354
  }
1355
1355
  const transformedChoices = choices.map(([content, action2, visible]) => {
1356
1356
  const shown = !visible || visible({
1357
- lang: $.get().meta[0],
1357
+ lang: storageData.get().meta[0],
1358
1358
  state: getStateAtCtx(ctx)
1359
1359
  });
1360
1360
  if (DEV && action2.length === 0 && !shown) {
1361
1361
  console.warn(`Choice children should not be empty, either add content there or make item not selectable`);
1362
1362
  }
1363
- return [templateReplace(content, data2), action2, shown];
1363
+ return [templateReplace(content, data2), shown];
1364
1364
  });
1365
1365
  if (DEV && transformedChoices.length === 0) {
1366
1366
  throw new Error(`Running choice without variants to choose from, look at how to use Choice action properly [https://novely.pages.dev/guide/actions/choice#usage]`);
@@ -1567,7 +1567,7 @@ var Novely = (() => {
1567
1567
  const {
1568
1568
  data: data2,
1569
1569
  meta: [lang]
1570
- } = $.get();
1570
+ } = storageData.get();
1571
1571
  const obj = values || data2;
1572
1572
  const cnt = isFunction(content) ? content(obj) : typeof content === "string" ? content : content[lang];
1573
1573
  const str2 = flattenAllowedContent(
@@ -1585,11 +1585,11 @@ var Novely = (() => {
1585
1585
  );
1586
1586
  };
1587
1587
  const data = (value) => {
1588
- const _data = $.get().data;
1588
+ const _data = storageData.get().data;
1589
1589
  if (!value)
1590
1590
  return _data;
1591
1591
  const val = isFunction(value) ? value(_data) : deepmerge(_data, value);
1592
- $.update((prev) => {
1592
+ storageData.update((prev) => {
1593
1593
  prev.data = val;
1594
1594
  return prev;
1595
1595
  });