@hkdigital/lib-sveltekit 0.1.62 → 0.1.65

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 (209) hide show
  1. package/README.md +135 -135
  2. package/dist/assets/autospuiten/car-paint-picker.js +41 -41
  3. package/dist/assets/autospuiten/labels.js +7 -7
  4. package/dist/classes/data/IterableTree.js +243 -243
  5. package/dist/classes/data/Selector.js +190 -190
  6. package/dist/classes/data/index.js +2 -2
  7. package/dist/classes/index.js +4 -4
  8. package/dist/classes/promise/HkPromise.js +377 -377
  9. package/dist/classes/promise/index.js +1 -1
  10. package/dist/classes/stores/SubscribersCount.js +107 -107
  11. package/dist/classes/stores/index.js +1 -1
  12. package/dist/classes/streams/LogTransformStream.js +19 -19
  13. package/dist/classes/streams/ServerEventsStore.js +110 -110
  14. package/dist/classes/streams/TimeStampSource.js +26 -26
  15. package/dist/classes/streams/index.js +3 -3
  16. package/dist/classes/svelte/audio/AudioLoader.svelte.js +58 -58
  17. package/dist/classes/svelte/audio/AudioScene.svelte.js +295 -295
  18. package/dist/classes/svelte/audio/mocks.js +35 -35
  19. package/dist/classes/svelte/finite-state-machine/FiniteStateMachine.svelte.js +133 -133
  20. package/dist/classes/svelte/finite-state-machine/index.js +1 -1
  21. package/dist/classes/svelte/image/ImageLoader.svelte.js +47 -47
  22. package/dist/classes/svelte/image/ImageScene.svelte.js +253 -253
  23. package/dist/classes/svelte/image/ImageVariantsLoader.svelte.js +152 -152
  24. package/dist/classes/svelte/image/index.js +4 -4
  25. package/dist/classes/svelte/image/mocks.js +35 -35
  26. package/dist/classes/svelte/image/typedef.js +8 -8
  27. package/dist/classes/svelte/index.js +14 -14
  28. package/dist/classes/svelte/loading-state-machine/LoadingStateMachine.svelte.js +109 -109
  29. package/dist/classes/svelte/loading-state-machine/constants.js +16 -16
  30. package/dist/classes/svelte/loading-state-machine/index.js +3 -3
  31. package/dist/classes/svelte/network-loader/NetworkLoader.svelte.js +331 -331
  32. package/dist/classes/svelte/network-loader/constants.js +3 -3
  33. package/dist/classes/svelte/network-loader/index.js +3 -3
  34. package/dist/classes/svelte/network-loader/mocks.js +30 -30
  35. package/dist/classes/svelte/network-loader/typedef.js +8 -8
  36. package/dist/components/area/HkArea.svelte +49 -49
  37. package/dist/components/area/HkGridArea.svelte +77 -77
  38. package/dist/components/area/index.js +2 -2
  39. package/dist/components/buttons/button/Button.svelte +82 -82
  40. package/dist/components/buttons/button-icon-steeze/SteezeIconButton.svelte +30 -30
  41. package/dist/components/buttons/button-text/TextButton.svelte +21 -21
  42. package/dist/components/buttons/index.js +3 -3
  43. package/dist/components/debug/debug-panel-design-scaling/DebugPanelDesignScaling.svelte +146 -146
  44. package/dist/components/debug/index.js +1 -1
  45. package/dist/components/hkdev/blocks/TextBlock.svelte +46 -46
  46. package/dist/components/hkdev/buttons/CheckButton.svelte +62 -62
  47. package/dist/components/icons/HkIcon.svelte +86 -86
  48. package/dist/components/icons/HkTabIcon.svelte +116 -116
  49. package/dist/components/icons/SteezeIcon.svelte +97 -97
  50. package/dist/components/icons/index.js +6 -6
  51. package/dist/components/icons/typedef.js +16 -16
  52. package/dist/components/index.js +2 -2
  53. package/dist/components/inputs/index.js +1 -1
  54. package/dist/components/inputs/text-input/TestTextInput.svelte__ +102 -102
  55. package/dist/components/inputs/text-input/TextInput.svelte +223 -223
  56. package/dist/components/inputs/text-input/TextInput.svelte___ +83 -83
  57. package/dist/components/inputs/text-input/assets/IconInvalid.svelte +14 -14
  58. package/dist/components/inputs/text-input/assets/IconValid.svelte +12 -12
  59. package/dist/components/layout/grid-layers/GridLayers.svelte +167 -167
  60. package/dist/components/layout/index.js +1 -1
  61. package/dist/components/panels/index.js +1 -1
  62. package/dist/components/panels/panel/Panel.svelte +43 -43
  63. package/dist/components/rows/index.js +3 -3
  64. package/dist/components/rows/panel-grid-row/PanelGridRow.svelte +104 -104
  65. package/dist/components/rows/panel-row-2/PanelRow2.svelte +40 -40
  66. package/dist/components/tab-bar/HkTabBar.state.svelte.js +149 -149
  67. package/dist/components/tab-bar/HkTabBar.svelte +74 -74
  68. package/dist/components/tab-bar/HkTabBarSelector.state.svelte.js +93 -93
  69. package/dist/components/tab-bar/HkTabBarSelector.svelte +49 -49
  70. package/dist/components/tab-bar/index.js +17 -17
  71. package/dist/components/tab-bar/typedef.js +8 -8
  72. package/dist/config/imagetools-config.js +189 -189
  73. package/dist/config/imagetools.d.ts +71 -71
  74. package/dist/config/typedef.js +8 -8
  75. package/dist/constants/bases.js +13 -13
  76. package/dist/constants/errors/api.js +9 -9
  77. package/dist/constants/errors/generic.js +5 -5
  78. package/dist/constants/errors/index.js +3 -3
  79. package/dist/constants/errors/jwt.js +5 -5
  80. package/dist/constants/http/headers.js +6 -6
  81. package/dist/constants/http/index.js +2 -2
  82. package/dist/constants/http/methods.js +2 -2
  83. package/dist/constants/index.js +3 -3
  84. package/dist/constants/mime/application.js +5 -5
  85. package/dist/constants/mime/audio.js +13 -13
  86. package/dist/constants/mime/image.js +3 -3
  87. package/dist/constants/mime/index.js +4 -4
  88. package/dist/constants/mime/text.js +2 -2
  89. package/dist/constants/regexp/index.js +31 -31
  90. package/dist/constants/regexp/inspiratie.js__ +95 -95
  91. package/dist/constants/regexp/text.js +49 -49
  92. package/dist/constants/regexp/user.js +32 -32
  93. package/dist/constants/regexp/web.js +3 -3
  94. package/dist/constants/state-labels/input-states.js +11 -11
  95. package/dist/constants/state-labels/submit-states.js +4 -4
  96. package/dist/constants/time.js +28 -28
  97. package/dist/css/utilities.css +43 -43
  98. package/dist/design/design-config.js +73 -73
  99. package/dist/design/tailwind-theme-extend.js +158 -158
  100. package/dist/schemas/index.js +1 -1
  101. package/dist/schemas/validate-url.js +180 -180
  102. package/dist/server/index.js +1 -1
  103. package/dist/server/logger.js +94 -94
  104. package/dist/states/index.js +1 -1
  105. package/dist/states/navigation.svelte.js +55 -55
  106. package/dist/stores/index.js +1 -1
  107. package/dist/stores/theme.js +80 -80
  108. package/dist/themes/hkdev/components/blocks/text-block.css +41 -41
  109. package/dist/themes/hkdev/components/boxes/game-box.css +12 -12
  110. package/dist/themes/hkdev/components/buttons/button-icon-steeze.css +22 -22
  111. package/dist/themes/hkdev/components/buttons/button-text.css +32 -32
  112. package/dist/themes/hkdev/components/buttons/button.css +142 -142
  113. package/dist/themes/hkdev/components/buttons/skip-button.css +6 -6
  114. package/dist/themes/hkdev/components/icons/icon-steeze.css +22 -22
  115. package/dist/themes/hkdev/components/inputs/text-input.css +104 -104
  116. package/dist/themes/hkdev/components/panels/panel.css +27 -27
  117. package/dist/themes/hkdev/components/rows/panel-grid-row.css +6 -6
  118. package/dist/themes/hkdev/components/rows/panel-row-2.css +7 -7
  119. package/dist/themes/hkdev/components.css +47 -47
  120. package/dist/themes/hkdev/debug.css +1 -1
  121. package/dist/themes/hkdev/global/layout.css +39 -39
  122. package/dist/themes/hkdev/global/on-colors.css +53 -53
  123. package/dist/themes/hkdev/globals.css +11 -11
  124. package/dist/themes/hkdev/responsive.css +12 -12
  125. package/dist/themes/hkdev/theme-ext.js +15 -15
  126. package/dist/themes/hkdev/theme.js +235 -235
  127. package/dist/themes/index.js +1 -1
  128. package/dist/util/array/index.js +455 -455
  129. package/dist/util/bases/base58.js +262 -262
  130. package/dist/util/bases/index.js +1 -1
  131. package/dist/util/compare/index.js +247 -247
  132. package/dist/util/css/css-vars.js +83 -83
  133. package/dist/util/css/index.js +1 -1
  134. package/dist/util/design-system/components/states.js +22 -22
  135. package/dist/util/design-system/css/clamp.js +66 -66
  136. package/dist/util/design-system/css/root-design-vars.js +100 -100
  137. package/dist/util/design-system/index.js +5 -5
  138. package/dist/util/design-system/layout/scaling.js +228 -228
  139. package/dist/util/design-system/skeleton.js +208 -208
  140. package/dist/util/design-system/tailwind.js +288 -288
  141. package/dist/util/expect/arrays.js +47 -47
  142. package/dist/util/expect/index.js +259 -259
  143. package/dist/util/expect/primitives.js +55 -55
  144. package/dist/util/expect/url.js +60 -60
  145. package/dist/util/function/index.js +218 -218
  146. package/dist/util/http/errors.js +97 -97
  147. package/dist/util/http/headers.js +45 -45
  148. package/dist/util/http/http-request.js +294 -294
  149. package/dist/util/http/index.js +22 -22
  150. package/dist/util/http/json-request.js +143 -143
  151. package/dist/util/http/mocks.js +65 -65
  152. package/dist/util/http/response.js +241 -241
  153. package/dist/util/http/test-data__/content-length-test-hkdigital-small.V4HfZyBQ.avif +0 -0
  154. package/dist/util/http/url.js +52 -52
  155. package/dist/util/image/index.js +86 -86
  156. package/dist/util/index.js +2 -2
  157. package/dist/util/is/index.js +140 -140
  158. package/dist/util/iterate/index.js +234 -234
  159. package/dist/util/object/index.js +1361 -1361
  160. package/dist/util/singleton/index.js +97 -97
  161. package/dist/util/string/array-path.js +75 -75
  162. package/dist/util/string/convert.js +54 -54
  163. package/dist/util/string/fs.js +226 -226
  164. package/dist/util/string/index.js +5 -5
  165. package/dist/util/string/interpolate.js +61 -61
  166. package/dist/util/string/pad.js +10 -10
  167. package/dist/util/svelte/index.js +4 -4
  168. package/dist/util/svelte/loading/loading-tracker.svelte.js +108 -108
  169. package/dist/util/svelte/observe/index.js +49 -49
  170. package/dist/util/svelte/state-context/index.js +83 -83
  171. package/dist/util/svelte/wait/index.js +38 -38
  172. package/dist/util/sveltekit/index.js +1 -1
  173. package/dist/util/sveltekit/route-folders/index.js +101 -101
  174. package/dist/util/time/index.js +323 -323
  175. package/dist/util/unique/index.js +249 -249
  176. package/dist/valibot/date.js__ +10 -10
  177. package/dist/valibot/index.js +9 -9
  178. package/dist/valibot/url.js +95 -95
  179. package/dist/valibot/user.js +23 -23
  180. package/dist/widgets/button-group/ButtonGroup.svelte +82 -94
  181. package/dist/widgets/button-group/ButtonGroup.svelte.d.ts +0 -2
  182. package/dist/widgets/button-group/typedef.js +10 -10
  183. package/dist/widgets/compare-left-right/CompareLeftRight.svelte +179 -179
  184. package/dist/widgets/compare-left-right/index.js +1 -1
  185. package/dist/widgets/game-box/GameBox.svelte +579 -186
  186. package/dist/widgets/game-box/GameBox.svelte.d.ts +64 -4
  187. package/dist/widgets/game-box/gamebox.util.js +83 -83
  188. package/dist/widgets/hk-app-layout/HkAppLayout.state.svelte.js +25 -25
  189. package/dist/widgets/hk-app-layout/HkAppLayout.svelte +251 -251
  190. package/dist/widgets/image-box/ImageBox.svelte +212 -212
  191. package/dist/widgets/image-box/index.js +5 -5
  192. package/dist/widgets/image-box/typedef.js +32 -32
  193. package/dist/widgets/index.js +23 -23
  194. package/dist/widgets/presenter/(broken) Presenter.state.svelte.js__ +613 -0
  195. package/dist/widgets/presenter/ImageSlide.svelte +64 -64
  196. package/dist/widgets/presenter/Presenter.state.svelte.js +636 -636
  197. package/dist/widgets/presenter/Presenter.svelte +140 -140
  198. package/dist/widgets/presenter/Presenter.svelte__ +125 -0
  199. package/dist/widgets/presenter/constants.js +7 -7
  200. package/dist/widgets/presenter/index.js +10 -10
  201. package/dist/widgets/presenter/typedef.js +106 -106
  202. package/dist/widgets/presenter/util.js +210 -210
  203. package/dist/widgets/virtual-viewport/VirtualViewport.svelte +196 -196
  204. package/dist/zod/all.js +33 -33
  205. package/dist/zod/generic.js +11 -11
  206. package/dist/zod/javascript.js +32 -32
  207. package/dist/zod/user.js +16 -16
  208. package/dist/zod/web.js +52 -52
  209. package/package.json +102 -102
@@ -1,186 +1,579 @@
1
- <script>
2
- import { onMount } from 'svelte';
3
- import {
4
- getGameWidthOnLandscape,
5
- getGameWidthOnPortrait
6
- } from './gamebox.util.js';
7
- import { enableContainerScaling } from '../../util/design-system/index.js';
8
-
9
- /**
10
- * @type {{
11
- * base?: string,
12
- * bg?: string,
13
- * classes?: string,
14
- * style?: string,
15
- * aspectOnLandscape?: number,
16
- * aspectOnPortrait?: number,
17
- * enableScaling?: boolean,
18
- * designLandscape?: {width: number, height: number},
19
- * designPortrait?: {width: number, height: number},
20
- * clamping?: {
21
- * ui: {min: number, max: number},
22
- * textBase: {min: number, max: number},
23
- * textHeading: {min: number, max: number},
24
- * textUi: {min: number, max: number}
25
- * },
26
- * snippetLandscape?: import('svelte').Snippet,
27
- * snippetPortrait?: import('svelte').Snippet,
28
- * [attr: string]: any
29
- * }}
30
- */
31
- const {
32
- // > Style
33
- base = '',
34
- bg = '',
35
- classes = '',
36
- style = '',
37
-
38
- // > Functional properties
39
- aspectOnLandscape,
40
- aspectOnPortrait,
41
-
42
- marginLeft = 0,
43
- marginRight = 0,
44
-
45
- marginTop = 0,
46
- marginBottom = 0,
47
-
48
- // > Scaling options
49
- enableScaling = false,
50
- designLandscape = { width: 1920, height: 1080 },
51
- designPortrait = { width: 1080, height: 1920 },
52
- clamping = {
53
- ui: { min: 0.3, max: 2 },
54
- textBase: { min: 0.75, max: 1.5 },
55
- textHeading: { min: 0.75, max: 2.25 },
56
- textUi: { min: 0.5, max: 1.25 }
57
- },
58
-
59
- // > Snippets
60
- snippetLandscape,
61
- snippetPortrait
62
- } = $props();
63
-
64
- // > Game dimensions and state
65
- let windowWidth = $state();
66
- let windowHeight = $state();
67
- let gameWidth = $state();
68
- let gameHeight = $state();
69
- let isLandscape = $derived(windowWidth > windowHeight);
70
-
71
- // Game container reference
72
- let gameContainer = $state();
73
-
74
- // Update game dimensions based on window size and orientation
75
- $effect(() => {
76
- if (!windowWidth || !windowHeight) return;
77
-
78
- const availWidth = windowWidth - marginLeft - marginRight;
79
- const availHeight = windowHeight - marginTop - marginBottom;
80
-
81
- // console.debug('GameBox margins:', {
82
- // marginLeft,
83
- // marginRight,
84
- // marginTop,
85
- // marginBottom
86
- // });
87
-
88
- let gameAspect;
89
-
90
- if (availWidth > availHeight) {
91
- gameWidth = getGameWidthOnLandscape({
92
- windowWidth: availWidth,
93
- windowHeight: availHeight,
94
- aspectOnLandscape
95
- });
96
- gameAspect = aspectOnLandscape;
97
- } else {
98
- gameWidth = getGameWidthOnPortrait({
99
- windowWidth: availWidth,
100
- windowHeight: availHeight,
101
- aspectOnPortrait
102
- });
103
- gameAspect = aspectOnPortrait;
104
- }
105
-
106
- if (gameAspect) {
107
- gameHeight = gameWidth / gameAspect;
108
- } else {
109
- gameHeight = availHeight;
110
- }
111
- });
112
-
113
- // Set up scaling if enabled, with orientation awareness
114
- $effect(() => {
115
- if (!enableScaling || !gameContainer || !gameWidth || !gameHeight) {
116
- return () => {}; // No-op cleanup if scaling not enabled or required elements missing
117
- }
118
-
119
- // Select the appropriate design based on orientation
120
- const activeDesign = isLandscape ? designLandscape : designPortrait;
121
-
122
- // console.debug(
123
- // `GameBox scaling [${isLandscape ? 'landscape' : 'portrait'}]:`,
124
- // `game: ${gameWidth}x${gameHeight}`,
125
- // `design: ${activeDesign.width}x${activeDesign.height}`
126
- // );
127
-
128
- // Apply scaling with the current design based on orientation
129
- return enableContainerScaling({
130
- container: gameContainer,
131
- design: activeDesign,
132
- clamping,
133
- getDimensions: () => ({
134
- width: gameWidth,
135
- height: gameHeight
136
- })
137
- });
138
- });
139
-
140
- onMount(() => {
141
- const gameBoxNoScroll = 'game-box-no-scroll';
142
- const html = document.documentElement;
143
- html.classList.add(gameBoxNoScroll);
144
-
145
- return () => {
146
- html.classList.remove(gameBoxNoScroll);
147
- };
148
- });
149
- </script>
150
-
151
- <svelte:window bind:innerWidth={windowWidth} bind:innerHeight={windowHeight} />
152
-
153
- {#if gameHeight}
154
- <div
155
- data-component="game-box"
156
- data-orientation={isLandscape ? 'landscape' : 'portrait'}
157
- bind:this={gameContainer}
158
- class="{base} {bg} {classes}"
159
- style:width="{gameWidth}px"
160
- style:height="{gameHeight}px"
161
- style:--game-width={gameWidth}
162
- style:--game-height={gameHeight}
163
- style:margin-left="{marginLeft}px"
164
- style:margin-right="{marginRight}px"
165
- style:margin-top="{marginTop}px"
166
- style:margin-bottom="{marginBottom}px"
167
- {style}
168
- >
169
- {#if isLandscape}
170
- {@render snippetLandscape()}
171
- {:else}
172
- {@render snippetPortrait()}
173
- {/if}
174
- </div>
175
- {/if}
176
-
177
- <style>
178
- :global(html.game-box-no-scroll) {
179
- overflow: clip;
180
- scrollbar-width: none; /* Firefox */
181
- -ms-overflow-style: none; /* IE and Edge */
182
- }
183
- :global(html.game-box-no-scroll::-webkit-scrollbar) {
184
- display: none;
185
- }
186
- </style>
1
+ <script>
2
+ import { onMount } from 'svelte';
3
+
4
+ import {
5
+ getGameWidthOnLandscape,
6
+ getGameWidthOnPortrait
7
+ } from './gamebox.util.js';
8
+
9
+ import { enableContainerScaling } from '../../util/design-system/index.js';
10
+
11
+ /**
12
+ * @typedef {{
13
+ * isMobile:boolean,
14
+ * device:string,
15
+ * isFullscreen:boolean,
16
+ * isDevMode:boolean,
17
+ * requestDevmode:function,
18
+ * requestFullscreen:function,
19
+ * gameWidth: number,
20
+ * gameHeight: number
21
+ * }} SnippetParams
22
+ */
23
+
24
+ /**
25
+ * @typedef {import('svelte').Snippet<[SnippetParams]>} GameBoxSnippet
26
+ */
27
+
28
+ /**
29
+ * @type {{
30
+ * base?: string,
31
+ * bg?: string,
32
+ * classes?: string,
33
+ * style?: string,
34
+ * aspectOnLandscape?: number,
35
+ * aspectOnPortrait?: number,
36
+ * marginLeft?: number,
37
+ * marginRight?: number,
38
+ * marginTop?: number,
39
+ * marginBottom?: number,
40
+ * center?: boolean,
41
+ * enableScaling?: boolean,
42
+ * designLandscape?: {width: number, height: number},
43
+ * designPortrait?: {width: number, height: number},
44
+ * clamping?: {
45
+ * ui: {min: number, max: number},
46
+ * textBase: {min: number, max: number},
47
+ * textHeading: {min: number, max: number},
48
+ * textUi: {min: number, max: number}
49
+ * },
50
+ * snippetLandscape?:GameBoxSnippet,
51
+ * snippetPortrait?: GameBoxSnippet,
52
+ * snippetRequireFullscreen?: GameBoxSnippet,
53
+ * snippetInstallOnHomeScreen?: GameBoxSnippet,
54
+ * [attr: string]: any
55
+ * }}
56
+ */
57
+ const {
58
+ // > Style
59
+ base = '',
60
+ bg = '',
61
+ classes = '',
62
+ style = '',
63
+
64
+ // > Functional properties
65
+ aspectOnLandscape,
66
+ aspectOnPortrait,
67
+
68
+ marginLeft = 0,
69
+ marginRight = 0,
70
+
71
+ marginTop = 0,
72
+ marginBottom = 0,
73
+
74
+ center,
75
+
76
+ // > Scaling options
77
+ enableScaling = false,
78
+ designLandscape = { width: 1920, height: 1080 },
79
+ designPortrait = { width: 1920, height: 1080 },
80
+ clamping = {
81
+ ui: { min: 0.3, max: 2 },
82
+ textBase: { min: 0.75, max: 1.5 },
83
+ textHeading: { min: 0.75, max: 2.25 },
84
+ textUi: { min: 0.5, max: 1.25 }
85
+ },
86
+
87
+ // > Snippets
88
+ snippetLandscape,
89
+ snippetPortrait,
90
+ snippetRequireFullscreen,
91
+ snippetInstallOnHomeScreen
92
+ } = $props();
93
+
94
+ // > Game dimensions and state
95
+ let windowWidth = $state();
96
+ let windowHeight = $state();
97
+
98
+ let gameWidth = $state();
99
+ let gameHeight = $state();
100
+
101
+ let isAppleMobile = getIsAppleMobile();
102
+
103
+ let isPwa = $state(false);
104
+
105
+ let device = $state();
106
+
107
+ let iosWindowWidth = $state();
108
+ let iosWindowHeight = $state();
109
+
110
+ function getIsLandscape() {
111
+ if (isPwa && isAppleMobile) {
112
+ return iosWindowWidth > iosWindowHeight;
113
+ } else {
114
+ return windowWidth > windowHeight;
115
+ }
116
+ }
117
+
118
+ let isLandscape = $state();
119
+
120
+ // $derived.by(getIsLandscape);
121
+
122
+ $effect(() => {
123
+ isLandscape = getIsLandscape();
124
+ });
125
+
126
+ // Game container reference
127
+ let gameContainer = $state();
128
+
129
+ // Update game dimensions based on window size and orientation
130
+ $effect(() => {
131
+ const width = iosWindowWidth ?? windowWidth;
132
+ const height = iosWindowHeight ?? windowHeight;
133
+
134
+ if (!width || !height) return;
135
+
136
+ const availWidth = width - marginLeft - marginRight;
137
+ const availHeight = height - marginTop - marginBottom;
138
+
139
+ // console.debug('GameBox margins:', {
140
+ // marginLeft,
141
+ // marginRight,
142
+ // marginTop,
143
+ // marginBottom
144
+ // });
145
+
146
+ let gameAspect;
147
+
148
+ if (availWidth > availHeight) {
149
+ gameWidth = getGameWidthOnLandscape({
150
+ windowWidth: availWidth,
151
+ windowHeight: availHeight,
152
+ aspectOnLandscape
153
+ });
154
+ gameAspect = aspectOnLandscape;
155
+ } else {
156
+ gameWidth = getGameWidthOnPortrait({
157
+ windowWidth: availWidth,
158
+ windowHeight: availHeight,
159
+ aspectOnPortrait
160
+ });
161
+ gameAspect = aspectOnPortrait;
162
+ }
163
+
164
+ if (gameAspect) {
165
+ gameHeight = gameWidth / gameAspect;
166
+ } else {
167
+ gameHeight = availHeight;
168
+ }
169
+ });
170
+
171
+ // Set up scaling if enabled, with orientation awareness
172
+ $effect(() => {
173
+ if (!enableScaling || !gameContainer || !gameWidth || !gameHeight) {
174
+ return () => {}; // No-op cleanup if scaling not enabled or required elements missing
175
+ }
176
+
177
+ // Select the appropriate design based on orientation
178
+ const activeDesign = isLandscape ? designLandscape : designPortrait;
179
+
180
+ // console.debug(
181
+ // `GameBox scaling [${isLandscape ? 'landscape' : 'portrait'}]:`,
182
+ // `game: ${gameWidth}x${gameHeight}`,
183
+ // `design: ${activeDesign.width}x${activeDesign.height}`
184
+ // );
185
+
186
+ // Apply scaling with the current design based on orientation
187
+ return enableContainerScaling({
188
+ container: gameContainer,
189
+ design: activeDesign,
190
+ clamping,
191
+ getDimensions: () => ({
192
+ width: gameWidth,
193
+ height: gameHeight
194
+ })
195
+ });
196
+ });
197
+
198
+ let show = $state(false);
199
+
200
+ let isMobile = $state(false);
201
+
202
+ let isDevMode = $state(false);
203
+
204
+ // Check: always true for home app?
205
+ let isFullscreen = $state(false);
206
+
207
+ let supportsFullscreen = $state(false);
208
+
209
+ onMount(() => {
210
+ supportsFullscreen = document.fullscreenEnabled;
211
+
212
+ isMobile = getIsMobile();
213
+
214
+ device = whatDevice();
215
+
216
+ // Run before show
217
+ isFullscreen = !!document.fullscreenElement;
218
+
219
+ isPwa = window.matchMedia(
220
+ '(display-mode: fullscreen) or (display-mode: standalone)'
221
+ ).matches;
222
+
223
+ isLandscape = getIsLandscape();
224
+
225
+ show = true;
226
+
227
+ function updateIosWidthHeight() {
228
+ // const isPwa = window.matchMedia(
229
+ // '(display-mode: fullscreen) or (display-mode: standalone)'
230
+ // ).matches;
231
+
232
+ if (isPwa && isAppleMobile) {
233
+ const angle = screen.orientation.angle;
234
+
235
+ if (angle === 90 || angle === 270) {
236
+ iosWindowWidth = screen.height;
237
+ iosWindowHeight = screen.width;
238
+ } else {
239
+ iosWindowWidth = screen.width;
240
+ iosWindowHeight = screen.height;
241
+ }
242
+ // console.debug( { iosWindowWidth, iosWindowHeight } );
243
+ }
244
+ }
245
+
246
+ updateIosWidthHeight();
247
+
248
+ function updateOrientation(event) {
249
+ // console.debug('updateOrientation');
250
+ const type = event.target.type;
251
+ const angle = event.target.angle;
252
+
253
+ // isPwa = window.matchMedia(
254
+ // '(display-mode: fullscreen) or (display-mode: standalone)'
255
+ // ).matches;
256
+
257
+ updateIosWidthHeight();
258
+
259
+ console.debug(
260
+ `ScreenOrientation change: ${type}, ${angle} degrees.`,
261
+ isPwa,
262
+ windowWidth,
263
+ windowHeight,
264
+ screen.width,
265
+ screen.height,
266
+ iosWindowWidth,
267
+ iosWindowHeight
268
+ );
269
+
270
+ // if( angle
271
+ }
272
+
273
+ $effect(() => {
274
+ screen.orientation.addEventListener('change', updateOrientation);
275
+
276
+ return () => {
277
+ screen.orientation.removeEventListener('change', updateOrientation);
278
+ };
279
+ });
280
+
281
+ //
282
+ });
283
+
284
+ onMount(() => {
285
+ const gameBoxNoScroll = 'game-box-no-scroll';
286
+ const html = document.documentElement;
287
+ html.classList.add(gameBoxNoScroll);
288
+
289
+ return () => {
290
+ html.classList.remove(gameBoxNoScroll);
291
+ };
292
+ });
293
+
294
+ function getIsAppleMobile() {
295
+ return /iPhone|iPod/.test(navigator.userAgent);
296
+ }
297
+
298
+ function whatDevice() {
299
+ if (isAppleMobile) {
300
+ return 'IOS';
301
+ } else if (/Android/.test(navigator.userAgent)) {
302
+ return 'Android';
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Returns true if a device is a mobile phone (or similar)
308
+ */
309
+ function getIsMobile() {
310
+ // @ts-ignore
311
+ if (navigator?.userAgentData?.mobile !== undefined) {
312
+ // Supports for mobile flag
313
+ // @ts-ignore
314
+ return navigator.userAgentData.mobile;
315
+ } else if (isAppleMobile) {
316
+ return true;
317
+ } else if (/Android/.test(navigator.userAgent)) {
318
+ return true;
319
+ }
320
+
321
+ return false;
322
+ }
323
+
324
+ /**
325
+ * Returns true if the window is in full screen
326
+ * - Checks if CSS thinks we're in fullscreen mode
327
+ * - Checks if there is a fullscreen element (for safari)
328
+ */
329
+ function getIsFullscreen() {
330
+ if (
331
+ window.matchMedia(
332
+ '(display-mode: fullscreen) or (display-mode: standalone)'
333
+ ).matches
334
+ ) {
335
+ return true;
336
+ } else if (document.fullscreenElement) {
337
+ // Safari
338
+ return true;
339
+ }
340
+
341
+ return false;
342
+ }
343
+
344
+ async function requestFullscreen() {
345
+ console.debug('Request full screen');
346
+ show = false;
347
+
348
+ await document.documentElement.requestFullscreen();
349
+ isFullscreen = true;
350
+
351
+ setTimeout(() => {
352
+ show = true;
353
+ }, 1000);
354
+ }
355
+
356
+ // async function exitFullscreen() {
357
+ // console.debug('Exit full screen');
358
+ // show = false;
359
+
360
+ // await document.exitFullscreen();
361
+ // isFullscreen = false;
362
+
363
+ // setTimeout( () => { show = true; }, 1000 );
364
+ // }
365
+
366
+ $effect(() => {
367
+ // Update isFullscreen if window width or height changes
368
+
369
+ windowWidth;
370
+ windowHeight;
371
+
372
+ isFullscreen = getIsFullscreen();
373
+
374
+ // if( !isFullscreen )
375
+ // {
376
+ // show = false;
377
+ // setTimeout( () => { show = true; }, 1000 );
378
+ // }
379
+
380
+ // console.debug('isFullscreen', isFullscreen);
381
+ });
382
+
383
+ isDevMode = false;
384
+
385
+ function requestDevmode() {
386
+ isDevMode = true;
387
+ console.debug(isDevMode);
388
+ }
389
+
390
+ $effect(() => {
391
+ if (location.hostname === 'localhost') {
392
+ isDevMode = true;
393
+ }
394
+ });
395
+
396
+ $effect(() => {
397
+ if (isFullscreen) {
398
+ const url = new URL(window.location.href);
399
+ url.searchParams.set('preset', 'cinema');
400
+ window.history.pushState({}, '', url);
401
+ }
402
+ });
403
+ </script>
404
+
405
+ <svelte:window bind:innerWidth={windowWidth} bind:innerHeight={windowHeight} />
406
+
407
+ {#if gameHeight}
408
+ <div class:center>
409
+ <div
410
+ data-component="game-box"
411
+ data-orientation={isLandscape ? 'landscape' : 'portrait'}
412
+ bind:this={gameContainer}
413
+ class="{base} {bg} {classes}"
414
+ class:isMobile
415
+ style:width="{gameWidth}px"
416
+ style:height="{gameHeight}px"
417
+ style:--game-width={gameWidth}
418
+ style:--game-height={gameHeight}
419
+ style:margin-left="{marginLeft}px"
420
+ style:margin-right="{marginRight}px"
421
+ style:margin-top="{marginTop}px"
422
+ style:margin-bottom="{marginBottom}px"
423
+ {style}
424
+ >
425
+ {#if show}
426
+ {#if isLandscape}
427
+ <!-- Landscape -->
428
+ {#if snippetRequireFullscreen}
429
+ <!-- Require fullscreen -->
430
+ {#if isFullscreen && !isDevMode}
431
+ {@render snippetLandscape({
432
+ isMobile,
433
+ device,
434
+ isFullscreen,
435
+ isDevMode,
436
+ requestDevmode,
437
+ requestFullscreen,
438
+ gameWidth,
439
+ gameHeight
440
+ })}
441
+ {:else if supportsFullscreen && !isDevMode}
442
+ <!-- Require fullscreen (on landscape) -->
443
+ {@render snippetRequireFullscreen({
444
+ isMobile,
445
+ device,
446
+ isFullscreen,
447
+ isDevMode,
448
+ requestDevmode,
449
+ requestFullscreen,
450
+ gameWidth,
451
+ gameHeight
452
+ })}
453
+ {:else if isMobile && snippetInstallOnHomeScreen && !isDevMode}
454
+ <!-- Require install on home screen on mobile -->
455
+ {@render snippetInstallOnHomeScreen({
456
+ isMobile,
457
+ device,
458
+ isFullscreen,
459
+ isDevMode,
460
+ requestDevmode,
461
+ requestFullscreen,
462
+ gameWidth,
463
+ gameHeight
464
+ })}
465
+ {:else}
466
+ {@render snippetLandscape({
467
+ isMobile,
468
+ device,
469
+ isFullscreen,
470
+ isDevMode,
471
+ requestDevmode,
472
+ requestFullscreen,
473
+ gameWidth,
474
+ gameHeight
475
+ })}
476
+ {/if}
477
+ {:else}
478
+ <!-- Do not require fullscreen -->
479
+ <!-- *we do not try install home app -->
480
+ {@render snippetLandscape({
481
+ isMobile,
482
+ device,
483
+ isFullscreen,
484
+ isDevMode,
485
+ requestDevmode,
486
+ requestFullscreen,
487
+ gameWidth,
488
+ gameHeight
489
+ })}
490
+ {/if}
491
+ {:else}
492
+ <!-- Portrait -->
493
+ {#if snippetRequireFullscreen}
494
+ <!-- Require fullscreen -->
495
+ {#if isFullscreen && !isDevMode}
496
+ {@render snippetPortrait({
497
+ isMobile,
498
+ device,
499
+ isFullscreen,
500
+ isDevMode,
501
+ requestDevmode,
502
+ requestFullscreen,
503
+ gameWidth,
504
+ gameHeight
505
+ })}
506
+ {:else if supportsFullscreen && !isDevMode}
507
+ <!-- Require fullscreen (on landscape) -->
508
+ {@render snippetRequireFullscreen({
509
+ isMobile,
510
+ device,
511
+ isFullscreen,
512
+ isDevMode,
513
+ requestDevmode,
514
+ requestFullscreen,
515
+ gameWidth,
516
+ gameHeight
517
+ })}
518
+ {:else if isMobile && snippetInstallOnHomeScreen && !isDevMode}
519
+ <!-- Require install on home screen on mobile -->
520
+ {@render snippetInstallOnHomeScreen({
521
+ isMobile,
522
+ device,
523
+ isFullscreen,
524
+ isDevMode,
525
+ requestDevmode,
526
+ requestFullscreen,
527
+ gameWidth,
528
+ gameHeight
529
+ })}
530
+ {:else}
531
+ {@render snippetPortrait({
532
+ isMobile,
533
+ device,
534
+ isFullscreen,
535
+ isDevMode,
536
+ requestDevmode,
537
+ requestFullscreen,
538
+ gameWidth,
539
+ gameHeight
540
+ })}
541
+ {/if}
542
+ {:else}
543
+ <!-- Do not require fullscreen -->
544
+ <!-- *we do not try install home app -->
545
+ {@render snippetPortrait({
546
+ isMobile,
547
+ device,
548
+ isFullscreen,
549
+ isDevMode,
550
+ requestDevmode,
551
+ requestFullscreen,
552
+ gameWidth,
553
+ gameHeight
554
+ })}
555
+ {/if}
556
+ {/if}
557
+ {/if}
558
+ </div>
559
+ </div>
560
+ {/if}
561
+
562
+ <style>
563
+ .center {
564
+ height: 100lvh;
565
+ display: grid;
566
+ justify-items: center;
567
+ align-items: center;
568
+ /* border: solid 1px red;*/
569
+ }
570
+
571
+ :global(html.game-box-no-scroll) {
572
+ overflow: clip;
573
+ scrollbar-width: none; /* Firefox */
574
+ -ms-overflow-style: none; /* IE and Edge */
575
+ }
576
+ :global(html.game-box-no-scroll::-webkit-scrollbar) {
577
+ display: none;
578
+ }
579
+ </style>