create-pixi-vn 1.1.0 → 1.1.2

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.
Files changed (80) hide show
  1. package/README.md +18 -6
  2. package/package.json +1 -1
  3. package/template-react-vite-muijoy/src/AppRoutes.tsx +15 -11
  4. package/template-react-vite-muijoy/src/Home.tsx +24 -9
  5. package/template-react-vite-muijoy/src/components/GameSaveSlot.tsx +2 -2
  6. package/template-react-vite-muijoy/src/components/NextButton.tsx +17 -21
  7. package/template-react-vite-muijoy/src/components/ReturnMainMenuButton.tsx +3 -2
  8. package/{template-react-vite-muijoy-ink-tauri/src/interceptors/RefreshEventInterceptor.tsx → template-react-vite-muijoy/src/hooks/useClosePageDetector.tsx} +11 -15
  9. package/template-react-vite-muijoy/src/hooks/useKeyDetector.tsx +16 -0
  10. package/template-react-vite-muijoy/src/{interceptors/OnKeyEventInterceptor.tsx → hooks/useKeyboardDetector.tsx} +4 -9
  11. package/template-react-vite-muijoy/src/hooks/useNetworkDetector.tsx +18 -0
  12. package/{template-react-vite-muijoy-ink/src/interceptors/SkipAutoInterceptor.tsx → template-react-vite-muijoy/src/hooks/useSkipAutoDetector.tsx} +2 -2
  13. package/template-react-vite-muijoy/src/labels/startLabel.ts +11 -2
  14. package/template-react-vite-muijoy/src/main.tsx +3 -3
  15. package/template-react-vite-muijoy/src/screens/HistoryScreen.tsx +9 -12
  16. package/template-react-vite-muijoy/src/screens/OfflineScreen.tsx +54 -0
  17. package/template-react-vite-muijoy/src/screens/QuickTools.tsx +2 -2
  18. package/template-react-vite-muijoy/src/screens/Settings.tsx +8 -12
  19. package/template-react-vite-muijoy/src/stores/useNetworkStore.ts +20 -0
  20. package/template-react-vite-muijoy-ink/src/AppRoutes.tsx +15 -11
  21. package/template-react-vite-muijoy-ink/src/Home.tsx +26 -11
  22. package/template-react-vite-muijoy-ink/src/components/GameSaveSlot.tsx +2 -2
  23. package/template-react-vite-muijoy-ink/src/components/NextButton.tsx +17 -21
  24. package/template-react-vite-muijoy-ink/src/components/ReturnMainMenuButton.tsx +3 -2
  25. package/{template-react-vite-muijoy-tauri/src/interceptors/RefreshEventInterceptor.tsx → template-react-vite-muijoy-ink/src/hooks/useClosePageDetector.tsx} +11 -15
  26. package/template-react-vite-muijoy-ink/src/{interceptors/InkInitialization.tsx → hooks/useInkInitialization.tsx} +1 -1
  27. package/template-react-vite-muijoy-ink/src/hooks/useKeyDetector.tsx +16 -0
  28. package/template-react-vite-muijoy-ink/src/{interceptors/OnKeyEventInterceptor.tsx → hooks/useKeyboardDetector.tsx} +4 -9
  29. package/template-react-vite-muijoy-ink/src/hooks/useNetworkDetector.tsx +18 -0
  30. package/{template-react-vite-muijoy-tauri/src/interceptors/SkipAutoInterceptor.tsx → template-react-vite-muijoy-ink/src/hooks/useSkipAutoDetector.tsx} +2 -2
  31. package/template-react-vite-muijoy-ink/src/ink/start.ink +5 -0
  32. package/template-react-vite-muijoy-ink/src/main.tsx +3 -3
  33. package/template-react-vite-muijoy-ink/src/screens/HistoryScreen.tsx +9 -12
  34. package/template-react-vite-muijoy-ink/src/screens/OfflineScreen.tsx +54 -0
  35. package/template-react-vite-muijoy-ink/src/screens/QuickTools.tsx +2 -2
  36. package/template-react-vite-muijoy-ink/src/screens/Settings.tsx +8 -12
  37. package/template-react-vite-muijoy-ink/src/stores/useNetworkStore.ts +20 -0
  38. package/template-react-vite-muijoy-ink-tauri/src/AppRoutes.tsx +15 -11
  39. package/template-react-vite-muijoy-ink-tauri/src/Home.tsx +26 -11
  40. package/template-react-vite-muijoy-ink-tauri/src/components/GameSaveSlot.tsx +2 -2
  41. package/template-react-vite-muijoy-ink-tauri/src/components/NextButton.tsx +17 -21
  42. package/template-react-vite-muijoy-ink-tauri/src/components/ReturnMainMenuButton.tsx +3 -2
  43. package/{template-react-vite-muijoy-ink/src/interceptors/RefreshEventInterceptor.tsx → template-react-vite-muijoy-ink-tauri/src/hooks/useClosePageDetector.tsx} +11 -15
  44. package/template-react-vite-muijoy-ink-tauri/src/{interceptors/InkInitialization.tsx → hooks/useInkInitialization.tsx} +1 -1
  45. package/template-react-vite-muijoy-ink-tauri/src/hooks/useKeyDetector.tsx +16 -0
  46. package/{template-react-vite-muijoy-tauri/src/interceptors/OnKeyEventInterceptor.tsx → template-react-vite-muijoy-ink-tauri/src/hooks/useKeyboardDetector.tsx} +4 -9
  47. package/template-react-vite-muijoy-ink-tauri/src/hooks/useNetworkDetector.tsx +18 -0
  48. package/template-react-vite-muijoy-ink-tauri/src/{interceptors/SkipAutoInterceptor.tsx → hooks/useSkipAutoDetector.tsx} +2 -2
  49. package/template-react-vite-muijoy-ink-tauri/src/ink/start.ink +5 -0
  50. package/template-react-vite-muijoy-ink-tauri/src/main.tsx +3 -3
  51. package/template-react-vite-muijoy-ink-tauri/src/screens/HistoryScreen.tsx +9 -12
  52. package/template-react-vite-muijoy-ink-tauri/src/screens/OfflineScreen.tsx +54 -0
  53. package/template-react-vite-muijoy-ink-tauri/src/screens/QuickTools.tsx +2 -2
  54. package/template-react-vite-muijoy-ink-tauri/src/screens/Settings.tsx +8 -12
  55. package/template-react-vite-muijoy-ink-tauri/src/stores/useNetworkStore.ts +20 -0
  56. package/template-react-vite-muijoy-tauri/src/AppRoutes.tsx +15 -11
  57. package/template-react-vite-muijoy-tauri/src/Home.tsx +24 -9
  58. package/template-react-vite-muijoy-tauri/src/components/GameSaveSlot.tsx +2 -2
  59. package/template-react-vite-muijoy-tauri/src/components/NextButton.tsx +17 -21
  60. package/template-react-vite-muijoy-tauri/src/components/ReturnMainMenuButton.tsx +3 -2
  61. package/{template-react-vite-muijoy/src/interceptors/RefreshEventInterceptor.tsx → template-react-vite-muijoy-tauri/src/hooks/useClosePageDetector.tsx} +11 -15
  62. package/template-react-vite-muijoy-tauri/src/hooks/useKeyDetector.tsx +16 -0
  63. package/{template-react-vite-muijoy-ink-tauri/src/interceptors/OnKeyEventInterceptor.tsx → template-react-vite-muijoy-tauri/src/hooks/useKeyboardDetector.tsx} +4 -9
  64. package/template-react-vite-muijoy-tauri/src/hooks/useNetworkDetector.tsx +18 -0
  65. package/{template-react-vite-muijoy/src/interceptors/SkipAutoInterceptor.tsx → template-react-vite-muijoy-tauri/src/hooks/useSkipAutoDetector.tsx} +2 -2
  66. package/template-react-vite-muijoy-tauri/src/labels/startLabel.ts +11 -2
  67. package/template-react-vite-muijoy-tauri/src/main.tsx +3 -3
  68. package/template-react-vite-muijoy-tauri/src/screens/HistoryScreen.tsx +9 -12
  69. package/template-react-vite-muijoy-tauri/src/screens/OfflineScreen.tsx +54 -0
  70. package/template-react-vite-muijoy-tauri/src/screens/QuickTools.tsx +2 -2
  71. package/template-react-vite-muijoy-tauri/src/screens/Settings.tsx +8 -12
  72. package/template-react-vite-muijoy-tauri/src/stores/useNetworkStore.ts +20 -0
  73. package/template-react-vite-muijoy/src/interceptors/GoBackEventInterceptor.tsx +0 -17
  74. package/template-react-vite-muijoy/src/utils/actions-utility.ts +0 -10
  75. package/template-react-vite-muijoy-ink/src/interceptors/GoBackEventInterceptor.tsx +0 -17
  76. package/template-react-vite-muijoy-ink/src/utils/actions-utility.ts +0 -10
  77. package/template-react-vite-muijoy-ink-tauri/src/interceptors/GoBackEventInterceptor.tsx +0 -17
  78. package/template-react-vite-muijoy-ink-tauri/src/utils/actions-utility.ts +0 -10
  79. package/template-react-vite-muijoy-tauri/src/interceptors/GoBackEventInterceptor.tsx +0 -17
  80. package/template-react-vite-muijoy-tauri/src/utils/actions-utility.ts +0 -10
package/README.md CHANGED
@@ -4,9 +4,14 @@ You can start using Pixi’VN by initializing a new project.
4
4
 
5
5
  ## Prerequisites
6
6
 
7
+ Before starting, you must have the following tools installed:
8
+
7
9
  * [Node.js](https://nodejs.org/) version 18 or higher.
8
10
  * Text Editor with TypeScript support.
9
- * [Visual Studio Code](https://code.visualstudio.com/) is recommended.
11
+ * [Visual Studio Code](https://code.visualstudio.com/)
12
+ * [Cursor](https://www.cursor.com/)
13
+ * (Recommended) [Git](https://git-scm.com/)
14
+ * Have a [GitHub account](https://github.com/)
10
15
 
11
16
  ## Project Initialization
12
17
 
@@ -17,19 +22,26 @@ To initialize a new project, you can use the following command:
17
22
  npm create pixi-vn@latest
18
23
 
19
24
  # yarn
20
- yarn create pixi-vn@latest
25
+ yarn create pixi-vn
21
26
 
22
27
  # pnpm
23
- pnpm create pixi-vn@latest
28
+ pnpm create pixi-vn
29
+
30
+ # bun
31
+ bun create pixi-vn
24
32
 
25
33
  # bun
26
- bun create pixi-vn@latest
34
+ deno init --npm pixi-vn
27
35
  ```
28
36
 
29
37
  The supported template presets are:
30
38
 
31
- * **[Basic Visual Novel - Web page (Vite + React + MUI joy)](https://github.com/DRincs-Productions/pixi-vn-react-template)**
32
- * **[Basic Visual Novel - Multi Device (Vite + React + MUI joy + Electron Forge)](https://github.com/DRincs-Productions/pixi-vn-react-template/tree/electron)**
39
+ **Visual Novel - React**:
40
+
41
+ * **[Visual Novel - React - Typescript - Web page](https://github.com/DRincs-Productions/pixi-vn-react-template)**
42
+ * **[Visual Novel - React - Typescript - Web page + Desktop + Mobile](https://github.com/DRincs-Productions/pixi-vn-react-template/tree/tauri)**
43
+ * **[Visual Novel - React - Ink + Typescript - Web page](https://github.com/DRincs-Productions/pixi-vn-react-template/tree/ink)**
44
+ * **[Visual Novel - React - Ink + Typescript - Web page + Desktop + Mobile](https://github.com/DRincs-Productions/pixi-vn-react-template/tree/ink-tauri)**
33
45
 
34
46
  ( More templates will be added in the future, see this [issue](https://github.com/DRincs-Productions/pixi-vn/issues/162) for more information )
35
47
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-pixi-vn",
3
3
  "description": "Create a new Pixi’VN project",
4
- "version": "1.1.0",
4
+ "version": "1.1.2",
5
5
  "type": "module",
6
6
  "license": "GPL-3.0",
7
7
  "author": "DRincs-Productions",
@@ -1,7 +1,7 @@
1
1
  import { Route, Routes } from 'react-router-dom';
2
2
  import NextButton from './components/NextButton';
3
3
  import { LOADING_ROUTE, MAIN_MENU_ROUTE, NARRATION_ROUTE } from './constans';
4
- import SkipAutoInterceptor from './interceptors/SkipAutoInterceptor';
4
+ import useSkipAutoDetector from './hooks/useSkipAutoDetector';
5
5
  import HistoryScreen from './screens/HistoryScreen';
6
6
  import LoadingScreen from './screens/LoadingScreen';
7
7
  import MainMenu from './screens/MainMenu';
@@ -14,17 +14,21 @@ export default function AppRoutes() {
14
14
  <Routes>
15
15
  <Route key={"main_menu"} path={MAIN_MENU_ROUTE} element={<MainMenu />} />
16
16
  <Route key={"loading"} path={LOADING_ROUTE} element={<LoadingScreen />} />
17
- <Route key={"narration"} path={NARRATION_ROUTE}
18
- element={<>
19
- <HistoryScreen />
20
- <QuickTools />
21
- <NarrationScreen />
22
- <SkipAutoInterceptor />
23
- <TextInput />
24
- <NextButton />
25
- </>}
26
- />
17
+ <Route key={"narration"} path={NARRATION_ROUTE} element={<NarrationElement />} />
27
18
  <Route path="*" element={<MainMenu />} />
28
19
  </Routes>
29
20
  )
30
21
  }
22
+
23
+ function NarrationElement() {
24
+ useSkipAutoDetector()
25
+ return (
26
+ <>
27
+ <HistoryScreen />
28
+ <QuickTools />
29
+ <NarrationScreen />
30
+ <TextInput />
31
+ <NextButton />
32
+ </>
33
+ )
34
+ }
@@ -1,24 +1,35 @@
1
1
  import { Box } from '@mui/joy';
2
2
  import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
3
3
  import Routes from './AppRoutes';
4
+ import useClosePageDetector from './hooks/useClosePageDetector';
5
+ import useKeyboardDetector from './hooks/useKeyboardDetector';
6
+ import useEventListener from './hooks/useKeyDetector';
7
+ import useNetworkDetector from './hooks/useNetworkDetector';
4
8
  import Imports from './Imports';
5
- import GoBackEventInterceptor from './interceptors/GoBackEventInterceptor';
6
- import OnKeyEventInterceptor from './interceptors/OnKeyEventInterceptor';
7
- import RefreshSaveEventInterceptor from './interceptors/RefreshEventInterceptor';
8
9
  import GameSaveScreen from './screens/GameSaveScreen';
9
10
  import SaveLoadAlert from './screens/modals/SaveLoadAlert';
11
+ import OfflineScreen from './screens/OfflineScreen';
10
12
  import Settings from './screens/Settings';
11
13
 
12
- export default function Home() {
14
+ function HomeChild() {
15
+ useKeyboardDetector()
16
+ useClosePageDetector()
17
+ useNetworkDetector()
18
+ // Prevent the user from going back to the previous page
19
+ useEventListener({
20
+ type: 'popstate',
21
+ listener: () => {
22
+ window.history.forward();
23
+ }
24
+ })
25
+
13
26
  return (
14
- <Imports>
27
+ <>
15
28
  <Routes />
16
29
  <Settings />
17
30
  <GameSaveScreen />
18
31
  <SaveLoadAlert />
19
- <OnKeyEventInterceptor />
20
- <GoBackEventInterceptor />
21
- <RefreshSaveEventInterceptor />
32
+ <OfflineScreen />
22
33
  <Box
23
34
  sx={{
24
35
  pointerEvents: "auto",
@@ -26,6 +37,10 @@ export default function Home() {
26
37
  >
27
38
  <ReactQueryDevtools initialIsOpen={false} />
28
39
  </Box>
29
- </Imports>
40
+ </>
30
41
  )
31
42
  }
43
+
44
+ export default function Home() {
45
+ return (<Imports><HomeChild /></Imports>)
46
+ }
@@ -120,7 +120,7 @@ export default function GameSaveSlot({ saveId, onDelete, onLoad, onOverwriteSave
120
120
  }}
121
121
  />
122
122
  </IconButton>
123
- <IconButton
123
+ {location.pathname !== MAIN_MENU_ROUTE && <IconButton
124
124
  onClick={() => onOverwriteSave(saveData)}
125
125
  >
126
126
  <SaveAsIcon
@@ -129,7 +129,7 @@ export default function GameSaveSlot({ saveId, onDelete, onLoad, onOverwriteSave
129
129
  color: useTheme().palette.neutral[300],
130
130
  }}
131
131
  />
132
- </IconButton>
132
+ </IconButton>}
133
133
  <IconButton
134
134
  onClick={() => {
135
135
  onLoad(saveData)
@@ -3,8 +3,9 @@ import { Button } from '@mui/joy';
3
3
  import { useQueryClient } from '@tanstack/react-query';
4
4
  import { motion } from "motion/react";
5
5
  import { useSnackbar } from 'notistack';
6
- import { useCallback, useEffect } from 'react';
6
+ import { useCallback } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
+ import useEventListener from '../hooks/useKeyDetector';
8
9
  import useInterfaceStore from '../stores/useInterfaceStore';
9
10
  import useSkipStore from '../stores/useSkipStore';
10
11
  import useStepStore from '../stores/useStepStore';
@@ -52,28 +53,23 @@ export default function NextButton() {
52
53
  }
53
54
  }, [tNarration, queryClient])
54
55
 
55
- const onkeypress = useCallback((event: KeyboardEvent) => {
56
- if ((event.code == 'Enter' || event.code == 'Space')) {
57
- setSkipEnabled(true)
56
+ useEventListener({
57
+ type: 'keypress',
58
+ listener: (event) => {
59
+ if ((event.code == 'Enter' || event.code == 'Space')) {
60
+ setSkipEnabled(true)
61
+ }
58
62
  }
59
- }, [])
60
-
61
- const onkeyup = useCallback((event: KeyboardEvent) => {
62
- if ((event.code == 'Enter' || event.code == 'Space')) {
63
- setSkipEnabled(false)
64
- nextOnClick()
63
+ })
64
+ useEventListener({
65
+ type: 'keyup',
66
+ listener: (event) => {
67
+ if ((event.code == 'Enter' || event.code == 'Space')) {
68
+ setSkipEnabled(false)
69
+ nextOnClick()
70
+ }
65
71
  }
66
- }, [nextOnClick])
67
-
68
- useEffect(() => {
69
- window.addEventListener("keypress", onkeypress);
70
- window.addEventListener("keyup", onkeyup);
71
-
72
- return () => {
73
- window.removeEventListener("keypress", onkeypress);
74
- window.removeEventListener("keyup", onkeyup);
75
- };
76
- }, [onkeypress, onkeyup]);
72
+ })
77
73
 
78
74
  return (
79
75
  <Button
@@ -1,3 +1,4 @@
1
+ import { clearAllGameDatas } from '@drincs/pixi-vn';
1
2
  import ExitToAppIcon from '@mui/icons-material/ExitToApp';
2
3
  import { Button, Stack, Typography } from "@mui/joy";
3
4
  import { useState } from 'react';
@@ -5,7 +6,6 @@ import { useTranslation } from 'react-i18next';
5
6
  import { useLocation } from 'react-router-dom';
6
7
  import ModalDialogCustom from '../components/ModalDialog';
7
8
  import useSettingsScreenStore from '../stores/useSettingsScreenStore';
8
- import { gameEnd } from '../utils/actions-utility';
9
9
  import { useMyNavigate } from '../utils/navigate-utility';
10
10
 
11
11
  export default function ReturnMainMenuButton() {
@@ -51,7 +51,8 @@ export default function ReturnMainMenuButton() {
51
51
  color='danger'
52
52
  variant="outlined"
53
53
  onClick={() => {
54
- gameEnd(navigate)
54
+ clearAllGameDatas()
55
+ navigate('/')
55
56
  setOpenSettings(false)
56
57
  setOpenDialog(false)
57
58
  }}
@@ -1,30 +1,26 @@
1
1
  import { useQueryClient } from '@tanstack/react-query';
2
- import { useCallback, useEffect } from 'react';
2
+ import { useEffect } from 'react';
3
3
  import { useLocation } from 'react-router-dom';
4
4
  import { LOADING_ROUTE, MAIN_MENU_ROUTE } from '../constans';
5
5
  import { INTERFACE_DATA_USE_QUEY_KEY } from '../use_query/useQueryInterface';
6
6
  import { useMyNavigate } from '../utils/navigate-utility';
7
7
  import { addRefreshSave, loadRefreshSave } from '../utils/save-utility';
8
+ import useEventListener from './useKeyDetector';
8
9
 
9
- export default function RefreshSaveEventInterceptor() {
10
+ export default function useClosePageDetector() {
10
11
  const navigate = useMyNavigate();
11
12
  const queryClient = useQueryClient()
12
13
  const location = useLocation();
13
14
 
14
- const beforeunload = useCallback(async () => {
15
- if (location.pathname === MAIN_MENU_ROUTE || location.pathname === LOADING_ROUTE) {
16
- return
15
+ useEventListener({
16
+ type: 'beforeunload',
17
+ listener: async () => {
18
+ if (location.pathname === MAIN_MENU_ROUTE || location.pathname === LOADING_ROUTE) {
19
+ return
20
+ }
21
+ await addRefreshSave()
17
22
  }
18
- await addRefreshSave()
19
- }, [location])
20
-
21
- useEffect(() => {
22
- window.addEventListener("beforeunload", beforeunload);
23
-
24
- return () => {
25
- window.removeEventListener("beforeunload", beforeunload);
26
- };
27
- }, [beforeunload]);
23
+ })
28
24
 
29
25
  useEffect(() => {
30
26
  loadRefreshSave(navigate).then(() => queryClient.invalidateQueries({ queryKey: [INTERFACE_DATA_USE_QUEY_KEY] }))
@@ -0,0 +1,16 @@
1
+ import { useEffect } from 'react';
2
+
3
+ export default function useEventListener<K extends keyof WindowEventMap>({ type, listener }: {
4
+ type: K
5
+ listener: (this: Window, ev: WindowEventMap[K]) => any
6
+ }) {
7
+ useEffect(() => {
8
+ window.addEventListener(type, listener);
9
+
10
+ return () => {
11
+ window.removeEventListener(type, listener);
12
+ };
13
+ }, [onkeydown]);
14
+
15
+ return null
16
+ }
@@ -1,6 +1,6 @@
1
1
  import { useQueryClient } from '@tanstack/react-query';
2
2
  import { useSnackbar } from 'notistack';
3
- import { useCallback, useEffect } from 'react';
3
+ import { useCallback } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { useLocation } from 'react-router-dom';
6
6
  import useGameSaveScreenStore from '../stores/useGameSaveScreenStore';
@@ -8,8 +8,9 @@ import useInterfaceStore from '../stores/useInterfaceStore';
8
8
  import useQueryLastSave, { LAST_SAVE_USE_QUEY_KEY } from '../use_query/useQueryLastSave';
9
9
  import { SAVES_USE_QUEY_KEY } from '../use_query/useQuerySaves';
10
10
  import { putSaveIntoIndexDB } from '../utils/save-utility';
11
+ import useEventListener from './useKeyDetector';
11
12
 
12
- export default function EventInterceptor() {
13
+ export default function useKeyboardDetector() {
13
14
  const hideInterface = useInterfaceStore((state) => state.hidden);
14
15
  const setHideInterface = useInterfaceStore((state) => state.setHidden);
15
16
  const setOpenLoadAlert = useGameSaveScreenStore((state) => (state.editLoadAlert))
@@ -61,13 +62,7 @@ export default function EventInterceptor() {
61
62
  }
62
63
  }, [location, hideInterface, lastSave, queryClient, t])
63
64
 
64
- useEffect(() => {
65
- window.addEventListener('keydown', onkeydown);
66
-
67
- return () => {
68
- window.removeEventListener('keydown', onkeydown);
69
- };
70
- }, [onkeydown]);
65
+ useEventListener({ type: 'keydown', listener: onkeydown })
71
66
 
72
67
  return null
73
68
  }
@@ -0,0 +1,18 @@
1
+ import { useEffect } from "react";
2
+ import useNetworkStore from "../stores/useNetworkStore";
3
+
4
+ export default function useNetworkDetector() {
5
+ const update = useNetworkStore((state) => (state.updateOnlineStatus))
6
+
7
+ useEffect(() => {
8
+ window.addEventListener("online", update);
9
+ window.addEventListener("offline", update);
10
+
11
+ return () => {
12
+ window.removeEventListener("online", update);
13
+ window.removeEventListener("offline", update);
14
+ };
15
+ }, []);
16
+
17
+ return null;
18
+ };
@@ -10,13 +10,13 @@ import useTypewriterStore from '../stores/useTypewriterStore';
10
10
  import { INTERFACE_DATA_USE_QUEY_KEY } from '../use_query/useQueryInterface';
11
11
  import { useMyNavigate } from '../utils/navigate-utility';
12
12
 
13
- export default function SkipAutoInterceptor() {
13
+ export default function useSkipAutoDetector() {
14
14
  const navigate = useMyNavigate();
15
15
  const { t: tNarration } = useTranslation(["narration"]);
16
16
  const skipEnabled = useSkipStore((state) => state.enabled)
17
17
  const autoEnabled = useAutoInfoStore((state) => state.enabled)
18
18
  const autoTime = useAutoInfoStore((state) => state.time)
19
- const typewriterInProgress = useTypewriterStore((state) => !state.inProgress)
19
+ const typewriterInProgress = useTypewriterStore((state) => state.inProgress)
20
20
  const [recheckSkip, setRecheckSkip] = useState<number>(0)
21
21
  const { enqueueSnackbar } = useSnackbar();
22
22
  const setNextStepLoading = useStepStore((state) => state.setLoading);
@@ -1,4 +1,4 @@
1
- import { ChoiceMenuOption, ChoiceMenuOptionClose, moveIn, narration, newLabel, showImage, showImageContainer } from "@drincs/pixi-vn";
1
+ import { Assets, ChoiceMenuOption, ChoiceMenuOptionClose, moveIn, narration, newLabel, showImage, showImageContainer } from "@drincs/pixi-vn";
2
2
  import { james, mc, sly, steph } from "../values/characters";
3
3
 
4
4
  const steph_fullname = "Stephanie";
@@ -329,7 +329,16 @@ const startLabel = newLabel("start", [
329
329
  new ChoiceMenuOptionClose("No, I want to stop here"),
330
330
  ]
331
331
  },
332
- ]);
332
+ ], {
333
+ onLoadingLabel: () => {
334
+ Assets.load([
335
+ "bg01-hallway",
336
+ "m01-body", "m01-eyes-grin", "m01-eyes-smile", "m01-eyes-wow", "m01-mouth-grin00", "m01-mouth-smile00", "m01-mouth-smile01",
337
+ "fm01-body", "fm01-eyes-smile", "fm01-eyes-upset", "fm01-mouth-serious00", "fm01-mouth-serious01", "fm01-mouth-smile00",
338
+ "fm02-body", "fm02-eyes-joy", "fm02-eyes-nervous", "fm02-eyes-wow", "fm02-mouth-nervous00", "fm02-mouth-smile00",
339
+ ])
340
+ }
341
+ });
333
342
  export default startLabel;
334
343
 
335
344
  const secondPart = newLabel("second_part", [
@@ -1,8 +1,7 @@
1
- import { canvas, narration } from '@drincs/pixi-vn'
1
+ import { canvas, clearAllGameDatas, narration } from '@drincs/pixi-vn'
2
2
  import { createRoot } from 'react-dom/client'
3
3
  import App from './App'
4
4
  import './index.css'
5
- import { gameEnd } from './utils/actions-utility'
6
5
  import './values/characters'
7
6
 
8
7
  // Canvas setup with PIXI
@@ -32,7 +31,8 @@ canvas.initialize(body, 1920, 1080, {
32
31
  })
33
32
 
34
33
  narration.onGameEnd = async ({ navigate }) => {
35
- gameEnd(navigate)
34
+ clearAllGameDatas()
35
+ navigate('/')
36
36
  }
37
37
 
38
38
  narration.onStepError = async (_error, { notify, t }) => {
@@ -3,12 +3,13 @@ import SearchRoundedIcon from "@mui/icons-material/SearchRounded";
3
3
  import { Box, Chip, Input, Stack, Theme, Typography } from "@mui/joy";
4
4
  import Avatar from '@mui/joy/Avatar';
5
5
  import { useMediaQuery } from '@mui/material';
6
- import React, { useCallback, useEffect, useState } from 'react';
6
+ import React, { useState } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import Markdown from 'react-markdown';
9
9
  import rehypeRaw from "rehype-raw";
10
10
  import remarkGfm from "remark-gfm";
11
11
  import ModalDialogCustom from '../components/ModalDialog';
12
+ import useEventListener from '../hooks/useKeyDetector';
12
13
  import useHistoryScreenStore from '../stores/useHistoryScreenStore';
13
14
  import { useQueryNarrativeHistory } from '../use_query/useQueryInterface';
14
15
 
@@ -90,18 +91,14 @@ export default function HistoryScreen() {
90
91
  const { t } = useTranslation(["ui"]);
91
92
  const smScreen = useMediaQuery((theme: Theme) => theme.breakpoints.down("md"));
92
93
 
93
- const onkeydown = useCallback((event: KeyboardEvent) => {
94
- if (event.code == 'KeyH' && event.altKey) {
95
- editOpen()
94
+ useEventListener({
95
+ type: 'keydown',
96
+ listener: (event) => {
97
+ if (event.code == 'KeyH' && event.altKey) {
98
+ editOpen()
99
+ }
96
100
  }
97
- }, [])
98
-
99
- useEffect(() => {
100
- window.addEventListener('keydown', onkeydown);
101
- return () => {
102
- window.removeEventListener('keydown', onkeydown);
103
- };
104
- }, [onkeydown]);
101
+ })
105
102
 
106
103
  return (
107
104
  <ModalDialogCustom
@@ -0,0 +1,54 @@
1
+ import SignalWifiBadIcon from '@mui/icons-material/SignalWifiBad';
2
+ import { Box } from '@mui/joy';
3
+ import Modal from '@mui/joy/Modal';
4
+ import { AnimatePresence, motion } from "motion/react";
5
+ import useNetworkStore from '../stores/useNetworkStore';
6
+
7
+ export default function OfflineScreen() {
8
+ const open = useNetworkStore((state) => !state.isOnline)
9
+
10
+ return (
11
+ <AnimatePresence>
12
+ <Modal
13
+ keepMounted
14
+ open={open}
15
+ component={motion.div}
16
+ variants={{
17
+ open: {
18
+ opacity: 1,
19
+ pointerEvents: "auto",
20
+ backdropFilter: "blur(8px)",
21
+ },
22
+ closed: {
23
+ opacity: 0,
24
+ pointerEvents: "none",
25
+ backdropFilter: "blur(0px)",
26
+ }
27
+ }}
28
+ initial={"closed"}
29
+ animate={open ? "open" : "closed"}
30
+ exit={"closed"}
31
+ transition={{
32
+ duration: 0.4,
33
+ }}
34
+ >
35
+ <Box
36
+ sx={{
37
+ position: "absolute",
38
+ left: "50%",
39
+ top: "50%",
40
+ transform: "translate(-50%, -50%)",
41
+ borderRadius: "20%",
42
+ }}
43
+ >
44
+ <SignalWifiBadIcon
45
+ color="error"
46
+ sx={{
47
+ fontSize: "1000%",
48
+ }}
49
+ />
50
+ </Box>
51
+ </Modal>
52
+ </AnimatePresence>
53
+ );
54
+ }
@@ -1,3 +1,4 @@
1
+ import { narration } from '@drincs/pixi-vn';
1
2
  import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
2
3
  import { IconButton, Stack, useTheme } from '@mui/joy';
3
4
  import { useQueryClient } from '@tanstack/react-query';
@@ -14,7 +15,6 @@ import useSkipStore from '../stores/useSkipStore';
14
15
  import { INTERFACE_DATA_USE_QUEY_KEY, useQueryCanGoBack } from '../use_query/useQueryInterface';
15
16
  import useQueryLastSave, { LAST_SAVE_USE_QUEY_KEY } from '../use_query/useQueryLastSave';
16
17
  import { SAVES_USE_QUEY_KEY } from '../use_query/useQuerySaves';
17
- import { goBack } from '../utils/actions-utility';
18
18
  import { useMyNavigate } from '../utils/navigate-utility';
19
19
  import { putSaveIntoIndexDB } from '../utils/save-utility';
20
20
 
@@ -68,7 +68,7 @@ export default function QuickTools() {
68
68
  transition={{ type: "tween" }}
69
69
  >
70
70
  <TextMenuButton
71
- onClick={() => goBack(navigate).then(() => queryClient.invalidateQueries({ queryKey: [INTERFACE_DATA_USE_QUEY_KEY] }))}
71
+ onClick={() => narration.goBack(navigate).then(() => queryClient.invalidateQueries({ queryKey: [INTERFACE_DATA_USE_QUEY_KEY] }))}
72
72
  disabled={!canGoBack}
73
73
  sx={{ pointerEvents: !hideInterface ? "auto" : "none" }}
74
74
  >
@@ -1,8 +1,8 @@
1
1
  import { Box, DialogContent, DialogTitle, Divider, Drawer, FormControl, ModalClose, RadioGroup, Sheet, Typography } from "@mui/joy";
2
2
  import { Theme, useMediaQuery } from '@mui/material';
3
- import { useCallback, useEffect } from 'react';
4
3
  import { useTranslation } from 'react-i18next';
5
4
  import ReturnMainMenuButton from '../components/ReturnMainMenuButton';
5
+ import useEventListener from "../hooks/useKeyDetector";
6
6
  import useSettingsScreenStore from '../stores/useSettingsScreenStore';
7
7
  import AutoSettingToggle from './settings/AutoSettingToggle';
8
8
  import DialoguesSettings from './settings/DialoguesSettings';
@@ -19,18 +19,14 @@ export default function Settings() {
19
19
  const { t } = useTranslation(["ui"]);
20
20
  const smScreen = useMediaQuery((theme: Theme) => theme.breakpoints.down('sm'));
21
21
 
22
- const onkeydown = useCallback((event: KeyboardEvent) => {
23
- if (event.code == 'Escape') {
24
- editOpen()
22
+ useEventListener({
23
+ type: 'keydown',
24
+ listener: (event) => {
25
+ if (event.code == 'Escape') {
26
+ editOpen()
27
+ }
25
28
  }
26
- }, []);
27
-
28
- useEffect(() => {
29
- window.addEventListener('keydown', onkeydown);
30
- return () => {
31
- window.removeEventListener('keydown', onkeydown);
32
- };
33
- }, [onkeydown]);
29
+ })
34
30
 
35
31
  return (
36
32
  <Drawer
@@ -0,0 +1,20 @@
1
+ import { create } from 'zustand';
2
+
3
+ type NetworkStoreType = {
4
+ /**
5
+ * Whether the user is online or not
6
+ */
7
+ isOnline: boolean,
8
+ /**
9
+ * Update the online status
10
+ */
11
+ updateOnlineStatus: () => void,
12
+ }
13
+
14
+ const useNetworkStore = create<NetworkStoreType>((set) => ({
15
+ isOnline: navigator.onLine,
16
+ updateOnlineStatus: () => {
17
+ set({ isOnline: navigator.onLine })
18
+ }
19
+ }))
20
+ export default useNetworkStore;
@@ -1,7 +1,7 @@
1
1
  import { Route, Routes } from 'react-router-dom';
2
2
  import NextButton from './components/NextButton';
3
3
  import { LOADING_ROUTE, MAIN_MENU_ROUTE, NARRATION_ROUTE } from './constans';
4
- import SkipAutoInterceptor from './interceptors/SkipAutoInterceptor';
4
+ import useSkipAutoDetector from './hooks/useSkipAutoDetector';
5
5
  import HistoryScreen from './screens/HistoryScreen';
6
6
  import LoadingScreen from './screens/LoadingScreen';
7
7
  import MainMenu from './screens/MainMenu';
@@ -14,17 +14,21 @@ export default function AppRoutes() {
14
14
  <Routes>
15
15
  <Route key={"main_menu"} path={MAIN_MENU_ROUTE} element={<MainMenu />} />
16
16
  <Route key={"loading"} path={LOADING_ROUTE} element={<LoadingScreen />} />
17
- <Route key={"narration"} path={NARRATION_ROUTE}
18
- element={<>
19
- <HistoryScreen />
20
- <QuickTools />
21
- <NarrationScreen />
22
- <SkipAutoInterceptor />
23
- <TextInput />
24
- <NextButton />
25
- </>}
26
- />
17
+ <Route key={"narration"} path={NARRATION_ROUTE} element={<NarrationElement />} />
27
18
  <Route path="*" element={<MainMenu />} />
28
19
  </Routes>
29
20
  )
30
21
  }
22
+
23
+ function NarrationElement() {
24
+ useSkipAutoDetector()
25
+ return (
26
+ <>
27
+ <HistoryScreen />
28
+ <QuickTools />
29
+ <NarrationScreen />
30
+ <TextInput />
31
+ <NextButton />
32
+ </>
33
+ )
34
+ }