@almadar/ui 5.76.2 → 5.76.4
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/avl/index.cjs +368 -3
- package/dist/avl/index.js +368 -3
- package/dist/components/game/3d/index.cjs +7047 -0
- package/dist/components/game/3d/index.css +503 -0
- package/dist/components/game/3d/index.d.ts +13 -0
- package/dist/components/game/3d/index.js +6971 -0
- package/dist/components/game/3d/patterns.d.ts +20 -0
- package/dist/components/index.cjs +63 -3
- package/dist/components/index.js +63 -3
- package/dist/providers/index.cjs +368 -3
- package/dist/providers/index.js +368 -3
- package/dist/runtime/index.cjs +368 -3
- package/dist/runtime/index.js +368 -3
- package/package.json +1 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curated list of the PUBLIC 3D (Three.js) render-ui pattern components.
|
|
3
|
+
*
|
|
4
|
+
* The 3D family is a code-split optional surface (published via the
|
|
5
|
+
* `@almadar/ui/components/{organisms,molecules}/game/three` subpath, never in
|
|
6
|
+
* the main barrel — that would pull @react-three/fiber into every app). This
|
|
7
|
+
* file is the source of truth for WHICH 3D components are render-ui patterns:
|
|
8
|
+
* `pattern-sync` discovers `patterns.ts` files by walking the tree and admits
|
|
9
|
+
* exactly the names listed here, so the 3D scene internals re-exported alongside
|
|
10
|
+
* them (Scene3D, Camera3D, Lighting3D, ModelLoader, Canvas3D, …) do NOT leak in
|
|
11
|
+
* as factory patterns. Move this file anywhere under `components/` and the
|
|
12
|
+
* scanner still finds it.
|
|
13
|
+
*
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
export { GameCanvas3D } from './molecules/GameCanvas3D';
|
|
17
|
+
export { GameBoard3D } from './organisms/GameBoard3D';
|
|
18
|
+
export { GameCanvas3DBattleTemplate } from './templates/GameCanvas3DBattleTemplate';
|
|
19
|
+
export { GameCanvas3DCastleTemplate } from './templates/GameCanvas3DCastleTemplate';
|
|
20
|
+
export { GameCanvas3DWorldMapTemplate } from './templates/GameCanvas3DWorldMapTemplate';
|
|
@@ -52823,9 +52823,33 @@ var init_ToastSlot = __esm({
|
|
|
52823
52823
|
exports.ToastSlot.displayName = "ToastSlot";
|
|
52824
52824
|
}
|
|
52825
52825
|
});
|
|
52826
|
-
|
|
52827
|
-
|
|
52828
|
-
|
|
52826
|
+
function lazyThree(name, loader) {
|
|
52827
|
+
const Lazy = React80__namespace.default.lazy(
|
|
52828
|
+
() => loader().then((m) => {
|
|
52829
|
+
const Resolved = m[name];
|
|
52830
|
+
if (!Resolved) {
|
|
52831
|
+
throw new Error(
|
|
52832
|
+
`[@almadar/ui] 3D component "${name}" was not found in the three subpath bundle.`
|
|
52833
|
+
);
|
|
52834
|
+
}
|
|
52835
|
+
return { default: Resolved };
|
|
52836
|
+
})
|
|
52837
|
+
);
|
|
52838
|
+
function ThreeWrapper(props) {
|
|
52839
|
+
return React80__namespace.default.createElement(
|
|
52840
|
+
ThreeBoundary,
|
|
52841
|
+
{ name },
|
|
52842
|
+
React80__namespace.default.createElement(
|
|
52843
|
+
React80__namespace.default.Suspense,
|
|
52844
|
+
{ fallback: null },
|
|
52845
|
+
React80__namespace.default.createElement(Lazy, props)
|
|
52846
|
+
)
|
|
52847
|
+
);
|
|
52848
|
+
}
|
|
52849
|
+
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
52850
|
+
return ThreeWrapper;
|
|
52851
|
+
}
|
|
52852
|
+
var ThreeBoundary, GameBoard3D, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
52829
52853
|
var init_component_registry_generated = __esm({
|
|
52830
52854
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
52831
52855
|
init_AboutPageTemplate();
|
|
@@ -52936,9 +52960,11 @@ var init_component_registry_generated = __esm({
|
|
|
52936
52960
|
init_Form();
|
|
52937
52961
|
init_FormField();
|
|
52938
52962
|
init_FormSectionHeader();
|
|
52963
|
+
init_GameAudioProvider();
|
|
52939
52964
|
init_GameAudioToggle();
|
|
52940
52965
|
init_GameCard();
|
|
52941
52966
|
init_GameHud();
|
|
52967
|
+
init_GameIcon();
|
|
52942
52968
|
init_GameMenu();
|
|
52943
52969
|
init_GameShell();
|
|
52944
52970
|
init_GameTemplate();
|
|
@@ -53123,6 +53149,33 @@ var init_component_registry_generated = __esm({
|
|
|
53123
53149
|
init_WizardProgress();
|
|
53124
53150
|
init_WorldMapBoard();
|
|
53125
53151
|
init_WorldMapTemplate();
|
|
53152
|
+
ThreeBoundary = class extends React80__namespace.default.Component {
|
|
53153
|
+
constructor() {
|
|
53154
|
+
super(...arguments);
|
|
53155
|
+
__publicField(this, "state", { failed: false });
|
|
53156
|
+
}
|
|
53157
|
+
static getDerivedStateFromError() {
|
|
53158
|
+
return { failed: true };
|
|
53159
|
+
}
|
|
53160
|
+
render() {
|
|
53161
|
+
if (this.state.failed) {
|
|
53162
|
+
return React80__namespace.default.createElement(
|
|
53163
|
+
"div",
|
|
53164
|
+
{
|
|
53165
|
+
"data-testid": "three-unavailable",
|
|
53166
|
+
style: { padding: 16, fontSize: 13, lineHeight: 1.5, opacity: 0.7 }
|
|
53167
|
+
},
|
|
53168
|
+
`3D pattern "${this.props.name}" requires three.js. Install the optional peers three + @react-three/fiber + @react-three/drei (matching the host React major) to render it.`
|
|
53169
|
+
);
|
|
53170
|
+
}
|
|
53171
|
+
return this.props.children;
|
|
53172
|
+
}
|
|
53173
|
+
};
|
|
53174
|
+
GameBoard3D = lazyThree("GameBoard3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
53175
|
+
GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
53176
|
+
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
53177
|
+
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
53178
|
+
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
53126
53179
|
COMPONENT_REGISTRY = {
|
|
53127
53180
|
"AboutPageTemplate": exports.AboutPageTemplate,
|
|
53128
53181
|
"Accordion": exports.Accordion,
|
|
@@ -53238,9 +53291,16 @@ var init_component_registry_generated = __esm({
|
|
|
53238
53291
|
"Form": exports.Form,
|
|
53239
53292
|
"FormField": exports.FormField,
|
|
53240
53293
|
"FormSectionHeader": exports.FormSectionHeader,
|
|
53294
|
+
"GameAudioProvider": GameAudioProvider,
|
|
53241
53295
|
"GameAudioToggle": GameAudioToggle,
|
|
53296
|
+
"GameBoard3D": GameBoard3D,
|
|
53297
|
+
"GameCanvas3D": GameCanvas3D,
|
|
53298
|
+
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
53299
|
+
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
53300
|
+
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
53242
53301
|
"GameCard": GameCard,
|
|
53243
53302
|
"GameHud": GameHud,
|
|
53303
|
+
"GameIcon": GameIcon,
|
|
53244
53304
|
"GameMenu": GameMenu,
|
|
53245
53305
|
"GameShell": exports.GameShell,
|
|
53246
53306
|
"GameTemplate": exports.GameTemplate,
|
package/dist/components/index.js
CHANGED
|
@@ -52777,9 +52777,33 @@ var init_ToastSlot = __esm({
|
|
|
52777
52777
|
ToastSlot.displayName = "ToastSlot";
|
|
52778
52778
|
}
|
|
52779
52779
|
});
|
|
52780
|
-
|
|
52781
|
-
|
|
52782
|
-
|
|
52780
|
+
function lazyThree(name, loader) {
|
|
52781
|
+
const Lazy = React80__default.lazy(
|
|
52782
|
+
() => loader().then((m) => {
|
|
52783
|
+
const Resolved = m[name];
|
|
52784
|
+
if (!Resolved) {
|
|
52785
|
+
throw new Error(
|
|
52786
|
+
`[@almadar/ui] 3D component "${name}" was not found in the three subpath bundle.`
|
|
52787
|
+
);
|
|
52788
|
+
}
|
|
52789
|
+
return { default: Resolved };
|
|
52790
|
+
})
|
|
52791
|
+
);
|
|
52792
|
+
function ThreeWrapper(props) {
|
|
52793
|
+
return React80__default.createElement(
|
|
52794
|
+
ThreeBoundary,
|
|
52795
|
+
{ name },
|
|
52796
|
+
React80__default.createElement(
|
|
52797
|
+
React80__default.Suspense,
|
|
52798
|
+
{ fallback: null },
|
|
52799
|
+
React80__default.createElement(Lazy, props)
|
|
52800
|
+
)
|
|
52801
|
+
);
|
|
52802
|
+
}
|
|
52803
|
+
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
52804
|
+
return ThreeWrapper;
|
|
52805
|
+
}
|
|
52806
|
+
var ThreeBoundary, GameBoard3D, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
52783
52807
|
var init_component_registry_generated = __esm({
|
|
52784
52808
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
52785
52809
|
init_AboutPageTemplate();
|
|
@@ -52890,9 +52914,11 @@ var init_component_registry_generated = __esm({
|
|
|
52890
52914
|
init_Form();
|
|
52891
52915
|
init_FormField();
|
|
52892
52916
|
init_FormSectionHeader();
|
|
52917
|
+
init_GameAudioProvider();
|
|
52893
52918
|
init_GameAudioToggle();
|
|
52894
52919
|
init_GameCard();
|
|
52895
52920
|
init_GameHud();
|
|
52921
|
+
init_GameIcon();
|
|
52896
52922
|
init_GameMenu();
|
|
52897
52923
|
init_GameShell();
|
|
52898
52924
|
init_GameTemplate();
|
|
@@ -53077,6 +53103,33 @@ var init_component_registry_generated = __esm({
|
|
|
53077
53103
|
init_WizardProgress();
|
|
53078
53104
|
init_WorldMapBoard();
|
|
53079
53105
|
init_WorldMapTemplate();
|
|
53106
|
+
ThreeBoundary = class extends React80__default.Component {
|
|
53107
|
+
constructor() {
|
|
53108
|
+
super(...arguments);
|
|
53109
|
+
__publicField(this, "state", { failed: false });
|
|
53110
|
+
}
|
|
53111
|
+
static getDerivedStateFromError() {
|
|
53112
|
+
return { failed: true };
|
|
53113
|
+
}
|
|
53114
|
+
render() {
|
|
53115
|
+
if (this.state.failed) {
|
|
53116
|
+
return React80__default.createElement(
|
|
53117
|
+
"div",
|
|
53118
|
+
{
|
|
53119
|
+
"data-testid": "three-unavailable",
|
|
53120
|
+
style: { padding: 16, fontSize: 13, lineHeight: 1.5, opacity: 0.7 }
|
|
53121
|
+
},
|
|
53122
|
+
`3D pattern "${this.props.name}" requires three.js. Install the optional peers three + @react-three/fiber + @react-three/drei (matching the host React major) to render it.`
|
|
53123
|
+
);
|
|
53124
|
+
}
|
|
53125
|
+
return this.props.children;
|
|
53126
|
+
}
|
|
53127
|
+
};
|
|
53128
|
+
GameBoard3D = lazyThree("GameBoard3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
53129
|
+
GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
53130
|
+
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
53131
|
+
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
53132
|
+
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
53080
53133
|
COMPONENT_REGISTRY = {
|
|
53081
53134
|
"AboutPageTemplate": AboutPageTemplate,
|
|
53082
53135
|
"Accordion": Accordion,
|
|
@@ -53192,9 +53245,16 @@ var init_component_registry_generated = __esm({
|
|
|
53192
53245
|
"Form": Form,
|
|
53193
53246
|
"FormField": FormField,
|
|
53194
53247
|
"FormSectionHeader": FormSectionHeader,
|
|
53248
|
+
"GameAudioProvider": GameAudioProvider,
|
|
53195
53249
|
"GameAudioToggle": GameAudioToggle,
|
|
53250
|
+
"GameBoard3D": GameBoard3D,
|
|
53251
|
+
"GameCanvas3D": GameCanvas3D,
|
|
53252
|
+
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
53253
|
+
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
53254
|
+
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
53196
53255
|
"GameCard": GameCard,
|
|
53197
53256
|
"GameHud": GameHud,
|
|
53257
|
+
"GameIcon": GameIcon,
|
|
53198
53258
|
"GameMenu": GameMenu,
|
|
53199
53259
|
"GameShell": GameShell,
|
|
53200
53260
|
"GameTemplate": GameTemplate,
|
package/dist/providers/index.cjs
CHANGED
|
@@ -12145,14 +12145,318 @@ var init_useCanvasEffects = __esm({
|
|
|
12145
12145
|
"use client";
|
|
12146
12146
|
}
|
|
12147
12147
|
});
|
|
12148
|
+
function pickPath(entry) {
|
|
12149
|
+
if (Array.isArray(entry.path)) {
|
|
12150
|
+
return entry.path[Math.floor(Math.random() * entry.path.length)];
|
|
12151
|
+
}
|
|
12152
|
+
return entry.path;
|
|
12153
|
+
}
|
|
12154
|
+
function useGameAudio({
|
|
12155
|
+
manifest,
|
|
12156
|
+
baseUrl = "",
|
|
12157
|
+
initialMuted = false,
|
|
12158
|
+
initialVolume = 1
|
|
12159
|
+
}) {
|
|
12160
|
+
const [muted, setMutedState] = React107.useState(initialMuted);
|
|
12161
|
+
const [masterVolume, setMasterVolumeState] = React107.useState(initialVolume);
|
|
12162
|
+
const mutedRef = React107.useRef(muted);
|
|
12163
|
+
const volumeRef = React107.useRef(masterVolume);
|
|
12164
|
+
const manifestRef = React107.useRef(manifest);
|
|
12165
|
+
mutedRef.current = muted;
|
|
12166
|
+
volumeRef.current = masterVolume;
|
|
12167
|
+
manifestRef.current = manifest;
|
|
12168
|
+
const poolsRef = React107.useRef(/* @__PURE__ */ new Map());
|
|
12169
|
+
const getOrCreateElement = React107.useCallback((key) => {
|
|
12170
|
+
const entry = manifestRef.current[key];
|
|
12171
|
+
if (!entry) return null;
|
|
12172
|
+
let pool = poolsRef.current.get(key);
|
|
12173
|
+
if (!pool) {
|
|
12174
|
+
pool = [];
|
|
12175
|
+
poolsRef.current.set(key, pool);
|
|
12176
|
+
}
|
|
12177
|
+
const maxSize = entry.poolSize ?? 1;
|
|
12178
|
+
for (const audio of pool) {
|
|
12179
|
+
if (audio.paused && (audio.ended || audio.currentTime === 0)) {
|
|
12180
|
+
return audio;
|
|
12181
|
+
}
|
|
12182
|
+
}
|
|
12183
|
+
if (pool.length < maxSize) {
|
|
12184
|
+
const src = baseUrl + pickPath(entry);
|
|
12185
|
+
const audio = new Audio(src);
|
|
12186
|
+
audio.loop = entry.loop ?? false;
|
|
12187
|
+
pool.push(audio);
|
|
12188
|
+
return audio;
|
|
12189
|
+
}
|
|
12190
|
+
if (!entry.loop) {
|
|
12191
|
+
let oldest = pool[0];
|
|
12192
|
+
for (const audio of pool) {
|
|
12193
|
+
if (audio.currentTime > oldest.currentTime) {
|
|
12194
|
+
oldest = audio;
|
|
12195
|
+
}
|
|
12196
|
+
}
|
|
12197
|
+
oldest.pause();
|
|
12198
|
+
oldest.currentTime = 0;
|
|
12199
|
+
return oldest;
|
|
12200
|
+
}
|
|
12201
|
+
return null;
|
|
12202
|
+
}, [baseUrl]);
|
|
12203
|
+
const play = React107.useCallback((key) => {
|
|
12204
|
+
if (mutedRef.current) return;
|
|
12205
|
+
const entry = manifestRef.current[key];
|
|
12206
|
+
if (!entry) return;
|
|
12207
|
+
const audio = getOrCreateElement(key);
|
|
12208
|
+
if (!audio) return;
|
|
12209
|
+
audio.volume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
12210
|
+
if (!entry.loop) {
|
|
12211
|
+
audio.currentTime = 0;
|
|
12212
|
+
}
|
|
12213
|
+
const promise = audio.play();
|
|
12214
|
+
if (promise) {
|
|
12215
|
+
promise.catch(() => {
|
|
12216
|
+
});
|
|
12217
|
+
}
|
|
12218
|
+
}, [getOrCreateElement]);
|
|
12219
|
+
const stop = React107.useCallback((key) => {
|
|
12220
|
+
const pool = poolsRef.current.get(key);
|
|
12221
|
+
if (!pool) return;
|
|
12222
|
+
for (const audio of pool) {
|
|
12223
|
+
audio.pause();
|
|
12224
|
+
audio.currentTime = 0;
|
|
12225
|
+
}
|
|
12226
|
+
}, []);
|
|
12227
|
+
const currentMusicKeyRef = React107.useRef(null);
|
|
12228
|
+
const currentMusicElRef = React107.useRef(null);
|
|
12229
|
+
const musicFadeRef = React107.useRef(null);
|
|
12230
|
+
const pendingMusicKeyRef = React107.useRef(null);
|
|
12231
|
+
const clearMusicFade = React107.useCallback(() => {
|
|
12232
|
+
if (musicFadeRef.current) {
|
|
12233
|
+
clearInterval(musicFadeRef.current);
|
|
12234
|
+
musicFadeRef.current = null;
|
|
12235
|
+
}
|
|
12236
|
+
}, []);
|
|
12237
|
+
const playMusic = React107.useCallback((key) => {
|
|
12238
|
+
if (key === currentMusicKeyRef.current) return;
|
|
12239
|
+
pendingMusicKeyRef.current = key;
|
|
12240
|
+
const entry = manifestRef.current[key];
|
|
12241
|
+
if (!entry) return;
|
|
12242
|
+
const fadeDurationMs = entry.crossfadeDurationMs ?? 1500;
|
|
12243
|
+
const stepMs = 50;
|
|
12244
|
+
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
12245
|
+
const targetVolume = Math.min(1, (entry.volume ?? 1) * volumeRef.current);
|
|
12246
|
+
clearMusicFade();
|
|
12247
|
+
const src = baseUrl + (Array.isArray(entry.path) ? entry.path[0] : entry.path);
|
|
12248
|
+
const incoming = new Audio(src);
|
|
12249
|
+
incoming.loop = true;
|
|
12250
|
+
incoming.volume = 0;
|
|
12251
|
+
const outgoing = currentMusicElRef.current;
|
|
12252
|
+
const outgoingStartVol = outgoing?.volume ?? 0;
|
|
12253
|
+
currentMusicKeyRef.current = key;
|
|
12254
|
+
currentMusicElRef.current = incoming;
|
|
12255
|
+
if (!mutedRef.current) {
|
|
12256
|
+
incoming.play().catch(() => {
|
|
12257
|
+
currentMusicKeyRef.current = null;
|
|
12258
|
+
currentMusicElRef.current = outgoing;
|
|
12259
|
+
});
|
|
12260
|
+
}
|
|
12261
|
+
let step = 0;
|
|
12262
|
+
musicFadeRef.current = setInterval(() => {
|
|
12263
|
+
step++;
|
|
12264
|
+
const progress = Math.min(step / totalSteps, 1);
|
|
12265
|
+
incoming.volume = Math.min(1, targetVolume * progress);
|
|
12266
|
+
if (outgoing) {
|
|
12267
|
+
outgoing.volume = Math.max(0, outgoingStartVol * (1 - progress));
|
|
12268
|
+
}
|
|
12269
|
+
if (progress >= 1) {
|
|
12270
|
+
clearMusicFade();
|
|
12271
|
+
if (outgoing) {
|
|
12272
|
+
outgoing.pause();
|
|
12273
|
+
outgoing.src = "";
|
|
12274
|
+
}
|
|
12275
|
+
}
|
|
12276
|
+
}, stepMs);
|
|
12277
|
+
}, [baseUrl, clearMusicFade]);
|
|
12278
|
+
const stopMusic = React107.useCallback((fadeDurationMs = 1e3) => {
|
|
12279
|
+
const outgoing = currentMusicElRef.current;
|
|
12280
|
+
if (!outgoing) return;
|
|
12281
|
+
currentMusicKeyRef.current = null;
|
|
12282
|
+
currentMusicElRef.current = null;
|
|
12283
|
+
pendingMusicKeyRef.current = null;
|
|
12284
|
+
clearMusicFade();
|
|
12285
|
+
const startVolume = outgoing.volume;
|
|
12286
|
+
const stepMs = 50;
|
|
12287
|
+
const totalSteps = Math.max(1, fadeDurationMs / stepMs);
|
|
12288
|
+
let step = 0;
|
|
12289
|
+
musicFadeRef.current = setInterval(() => {
|
|
12290
|
+
step++;
|
|
12291
|
+
const progress = step / totalSteps;
|
|
12292
|
+
outgoing.volume = Math.max(0, startVolume * (1 - progress));
|
|
12293
|
+
if (progress >= 1) {
|
|
12294
|
+
clearMusicFade();
|
|
12295
|
+
outgoing.pause();
|
|
12296
|
+
outgoing.src = "";
|
|
12297
|
+
}
|
|
12298
|
+
}, stepMs);
|
|
12299
|
+
}, [clearMusicFade]);
|
|
12300
|
+
const stopAll = React107.useCallback(() => {
|
|
12301
|
+
for (const pool of poolsRef.current.values()) {
|
|
12302
|
+
for (const audio of pool) {
|
|
12303
|
+
audio.pause();
|
|
12304
|
+
audio.currentTime = 0;
|
|
12305
|
+
}
|
|
12306
|
+
}
|
|
12307
|
+
stopMusic(0);
|
|
12308
|
+
}, [stopMusic]);
|
|
12309
|
+
const setMuted = React107.useCallback((value) => {
|
|
12310
|
+
setMutedState(value);
|
|
12311
|
+
if (value) {
|
|
12312
|
+
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12313
|
+
if (manifestRef.current[key]?.loop) {
|
|
12314
|
+
for (const audio of pool) {
|
|
12315
|
+
if (!audio.paused) audio.pause();
|
|
12316
|
+
}
|
|
12317
|
+
}
|
|
12318
|
+
}
|
|
12319
|
+
currentMusicElRef.current?.pause();
|
|
12320
|
+
} else {
|
|
12321
|
+
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12322
|
+
const entry = manifestRef.current[key];
|
|
12323
|
+
if (entry?.loop && entry?.autostart) {
|
|
12324
|
+
for (const audio of pool) {
|
|
12325
|
+
if (audio.paused) audio.play().catch(() => {
|
|
12326
|
+
});
|
|
12327
|
+
}
|
|
12328
|
+
}
|
|
12329
|
+
}
|
|
12330
|
+
const musicEl = currentMusicElRef.current;
|
|
12331
|
+
if (musicEl) {
|
|
12332
|
+
musicEl.play().catch(() => {
|
|
12333
|
+
});
|
|
12334
|
+
}
|
|
12335
|
+
}
|
|
12336
|
+
}, []);
|
|
12337
|
+
const setMasterVolume = React107.useCallback((volume) => {
|
|
12338
|
+
const clamped = Math.max(0, Math.min(1, volume));
|
|
12339
|
+
setMasterVolumeState(clamped);
|
|
12340
|
+
for (const [key, pool] of poolsRef.current.entries()) {
|
|
12341
|
+
const entryVol = manifestRef.current[key]?.volume ?? 1;
|
|
12342
|
+
for (const audio of pool) {
|
|
12343
|
+
audio.volume = Math.min(1, entryVol * clamped);
|
|
12344
|
+
}
|
|
12345
|
+
}
|
|
12346
|
+
if (!musicFadeRef.current && currentMusicElRef.current) {
|
|
12347
|
+
const key = currentMusicKeyRef.current;
|
|
12348
|
+
const entryVol = key ? manifestRef.current[key]?.volume ?? 1 : 1;
|
|
12349
|
+
currentMusicElRef.current.volume = Math.min(1, entryVol * clamped);
|
|
12350
|
+
}
|
|
12351
|
+
}, []);
|
|
12352
|
+
const unlockedRef = React107.useRef(false);
|
|
12353
|
+
React107.useEffect(() => {
|
|
12354
|
+
const autoKeys = Object.keys(manifest).filter((k) => manifest[k].autostart);
|
|
12355
|
+
const hasPendingMusic = () => pendingMusicKeyRef.current !== null;
|
|
12356
|
+
const hasAutoStart = autoKeys.length > 0;
|
|
12357
|
+
if (!hasAutoStart && !hasPendingMusic()) return;
|
|
12358
|
+
const unlock = () => {
|
|
12359
|
+
if (unlockedRef.current) return;
|
|
12360
|
+
unlockedRef.current = true;
|
|
12361
|
+
if (!mutedRef.current) {
|
|
12362
|
+
for (const key of autoKeys) {
|
|
12363
|
+
play(key);
|
|
12364
|
+
}
|
|
12365
|
+
const pending = pendingMusicKeyRef.current;
|
|
12366
|
+
if (pending && pending !== currentMusicKeyRef.current) {
|
|
12367
|
+
playMusic(pending);
|
|
12368
|
+
}
|
|
12369
|
+
}
|
|
12370
|
+
};
|
|
12371
|
+
document.addEventListener("click", unlock, { once: true });
|
|
12372
|
+
document.addEventListener("keydown", unlock, { once: true });
|
|
12373
|
+
document.addEventListener("touchstart", unlock, { once: true });
|
|
12374
|
+
return () => {
|
|
12375
|
+
document.removeEventListener("click", unlock);
|
|
12376
|
+
document.removeEventListener("keydown", unlock);
|
|
12377
|
+
document.removeEventListener("touchstart", unlock);
|
|
12378
|
+
};
|
|
12379
|
+
}, [manifest, play, playMusic]);
|
|
12380
|
+
React107.useEffect(() => {
|
|
12381
|
+
return () => {
|
|
12382
|
+
clearMusicFade();
|
|
12383
|
+
for (const pool of poolsRef.current.values()) {
|
|
12384
|
+
for (const audio of pool) {
|
|
12385
|
+
audio.pause();
|
|
12386
|
+
audio.src = "";
|
|
12387
|
+
}
|
|
12388
|
+
}
|
|
12389
|
+
poolsRef.current.clear();
|
|
12390
|
+
if (currentMusicElRef.current) {
|
|
12391
|
+
currentMusicElRef.current.pause();
|
|
12392
|
+
currentMusicElRef.current.src = "";
|
|
12393
|
+
currentMusicElRef.current = null;
|
|
12394
|
+
}
|
|
12395
|
+
};
|
|
12396
|
+
}, [clearMusicFade]);
|
|
12397
|
+
return {
|
|
12398
|
+
play,
|
|
12399
|
+
stop,
|
|
12400
|
+
stopAll,
|
|
12401
|
+
playMusic,
|
|
12402
|
+
stopMusic,
|
|
12403
|
+
muted,
|
|
12404
|
+
setMuted,
|
|
12405
|
+
masterVolume,
|
|
12406
|
+
setMasterVolume
|
|
12407
|
+
};
|
|
12408
|
+
}
|
|
12148
12409
|
var init_useGameAudio = __esm({
|
|
12149
12410
|
"components/game/shared/hooks/useGameAudio.ts"() {
|
|
12150
12411
|
"use client";
|
|
12412
|
+
useGameAudio.displayName = "useGameAudio";
|
|
12151
12413
|
}
|
|
12152
12414
|
});
|
|
12153
12415
|
function useGameAudioContextOptional() {
|
|
12154
12416
|
return React107.useContext(GameAudioContext);
|
|
12155
12417
|
}
|
|
12418
|
+
function GameAudioProvider({
|
|
12419
|
+
manifest,
|
|
12420
|
+
baseUrl = "",
|
|
12421
|
+
children,
|
|
12422
|
+
initialMuted = false
|
|
12423
|
+
}) {
|
|
12424
|
+
const eventBus = useEventBus();
|
|
12425
|
+
const { play, stop, stopAll, playMusic, stopMusic, muted, setMuted, masterVolume, setMasterVolume } = useGameAudio({ manifest, baseUrl, initialMuted });
|
|
12426
|
+
React107.useEffect(() => {
|
|
12427
|
+
const unsubPlay = eventBus.on("UI:PLAY_SOUND", (event) => {
|
|
12428
|
+
const key = event.payload?.key;
|
|
12429
|
+
if (key) play(key);
|
|
12430
|
+
});
|
|
12431
|
+
const unsubStop = eventBus.on("UI:STOP_SOUND", (event) => {
|
|
12432
|
+
const key = event.payload?.key;
|
|
12433
|
+
if (key) {
|
|
12434
|
+
stop(key);
|
|
12435
|
+
} else {
|
|
12436
|
+
stopAll();
|
|
12437
|
+
}
|
|
12438
|
+
});
|
|
12439
|
+
const unsubChangeMusic = eventBus.on("UI:CHANGE_MUSIC", (event) => {
|
|
12440
|
+
const key = event.payload?.key;
|
|
12441
|
+
if (key) {
|
|
12442
|
+
playMusic(key);
|
|
12443
|
+
} else {
|
|
12444
|
+
stopMusic();
|
|
12445
|
+
}
|
|
12446
|
+
});
|
|
12447
|
+
const unsubStopMusic = eventBus.on("UI:STOP_MUSIC", () => {
|
|
12448
|
+
stopMusic();
|
|
12449
|
+
});
|
|
12450
|
+
return () => {
|
|
12451
|
+
unsubPlay();
|
|
12452
|
+
unsubStop();
|
|
12453
|
+
unsubChangeMusic();
|
|
12454
|
+
unsubStopMusic();
|
|
12455
|
+
};
|
|
12456
|
+
}, [eventBus, play, stop, stopAll, playMusic, stopMusic]);
|
|
12457
|
+
const value = { muted, setMuted, masterVolume, setMasterVolume, play, playMusic, stopMusic };
|
|
12458
|
+
return /* @__PURE__ */ jsxRuntime.jsx(GameAudioContext.Provider, { value, children });
|
|
12459
|
+
}
|
|
12156
12460
|
var GameAudioContext;
|
|
12157
12461
|
var init_GameAudioProvider = __esm({
|
|
12158
12462
|
"components/game/shared/providers/GameAudioProvider.tsx"() {
|
|
@@ -12161,6 +12465,7 @@ var init_GameAudioProvider = __esm({
|
|
|
12161
12465
|
init_useGameAudio();
|
|
12162
12466
|
GameAudioContext = React107.createContext(null);
|
|
12163
12467
|
GameAudioContext.displayName = "GameAudioContext";
|
|
12468
|
+
GameAudioProvider.displayName = "GameAudioProvider";
|
|
12164
12469
|
}
|
|
12165
12470
|
});
|
|
12166
12471
|
function GameAudioToggle({
|
|
@@ -50801,9 +51106,33 @@ var init_ToastSlot = __esm({
|
|
|
50801
51106
|
ToastSlot.displayName = "ToastSlot";
|
|
50802
51107
|
}
|
|
50803
51108
|
});
|
|
50804
|
-
|
|
50805
|
-
|
|
50806
|
-
|
|
51109
|
+
function lazyThree(name, loader) {
|
|
51110
|
+
const Lazy = React107__namespace.default.lazy(
|
|
51111
|
+
() => loader().then((m) => {
|
|
51112
|
+
const Resolved = m[name];
|
|
51113
|
+
if (!Resolved) {
|
|
51114
|
+
throw new Error(
|
|
51115
|
+
`[@almadar/ui] 3D component "${name}" was not found in the three subpath bundle.`
|
|
51116
|
+
);
|
|
51117
|
+
}
|
|
51118
|
+
return { default: Resolved };
|
|
51119
|
+
})
|
|
51120
|
+
);
|
|
51121
|
+
function ThreeWrapper(props) {
|
|
51122
|
+
return React107__namespace.default.createElement(
|
|
51123
|
+
ThreeBoundary,
|
|
51124
|
+
{ name },
|
|
51125
|
+
React107__namespace.default.createElement(
|
|
51126
|
+
React107__namespace.default.Suspense,
|
|
51127
|
+
{ fallback: null },
|
|
51128
|
+
React107__namespace.default.createElement(Lazy, props)
|
|
51129
|
+
)
|
|
51130
|
+
);
|
|
51131
|
+
}
|
|
51132
|
+
ThreeWrapper.displayName = `Lazy(${name})`;
|
|
51133
|
+
return ThreeWrapper;
|
|
51134
|
+
}
|
|
51135
|
+
var ThreeBoundary, GameBoard3D, GameCanvas3D, GameCanvas3DBattleTemplate, GameCanvas3DCastleTemplate, GameCanvas3DWorldMapTemplate, COMPONENT_REGISTRY;
|
|
50807
51136
|
var init_component_registry_generated = __esm({
|
|
50808
51137
|
"components/core/organisms/component-registry.generated.ts"() {
|
|
50809
51138
|
init_AboutPageTemplate();
|
|
@@ -50914,9 +51243,11 @@ var init_component_registry_generated = __esm({
|
|
|
50914
51243
|
init_Form();
|
|
50915
51244
|
init_FormField();
|
|
50916
51245
|
init_FormSectionHeader();
|
|
51246
|
+
init_GameAudioProvider();
|
|
50917
51247
|
init_GameAudioToggle();
|
|
50918
51248
|
init_GameCard();
|
|
50919
51249
|
init_GameHud();
|
|
51250
|
+
init_GameIcon();
|
|
50920
51251
|
init_GameMenu();
|
|
50921
51252
|
init_GameShell();
|
|
50922
51253
|
init_GameTemplate();
|
|
@@ -51101,6 +51432,33 @@ var init_component_registry_generated = __esm({
|
|
|
51101
51432
|
init_WizardProgress();
|
|
51102
51433
|
init_WorldMapBoard();
|
|
51103
51434
|
init_WorldMapTemplate();
|
|
51435
|
+
ThreeBoundary = class extends React107__namespace.default.Component {
|
|
51436
|
+
constructor() {
|
|
51437
|
+
super(...arguments);
|
|
51438
|
+
__publicField(this, "state", { failed: false });
|
|
51439
|
+
}
|
|
51440
|
+
static getDerivedStateFromError() {
|
|
51441
|
+
return { failed: true };
|
|
51442
|
+
}
|
|
51443
|
+
render() {
|
|
51444
|
+
if (this.state.failed) {
|
|
51445
|
+
return React107__namespace.default.createElement(
|
|
51446
|
+
"div",
|
|
51447
|
+
{
|
|
51448
|
+
"data-testid": "three-unavailable",
|
|
51449
|
+
style: { padding: 16, fontSize: 13, lineHeight: 1.5, opacity: 0.7 }
|
|
51450
|
+
},
|
|
51451
|
+
`3D pattern "${this.props.name}" requires three.js. Install the optional peers three + @react-three/fiber + @react-three/drei (matching the host React major) to render it.`
|
|
51452
|
+
);
|
|
51453
|
+
}
|
|
51454
|
+
return this.props.children;
|
|
51455
|
+
}
|
|
51456
|
+
};
|
|
51457
|
+
GameBoard3D = lazyThree("GameBoard3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
51458
|
+
GameCanvas3D = lazyThree("GameCanvas3D", () => import('@almadar/ui/components/molecules/game/three'));
|
|
51459
|
+
GameCanvas3DBattleTemplate = lazyThree("GameCanvas3DBattleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
51460
|
+
GameCanvas3DCastleTemplate = lazyThree("GameCanvas3DCastleTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
51461
|
+
GameCanvas3DWorldMapTemplate = lazyThree("GameCanvas3DWorldMapTemplate", () => import('@almadar/ui/components/molecules/game/three'));
|
|
51104
51462
|
COMPONENT_REGISTRY = {
|
|
51105
51463
|
"AboutPageTemplate": AboutPageTemplate,
|
|
51106
51464
|
"Accordion": Accordion,
|
|
@@ -51216,9 +51574,16 @@ var init_component_registry_generated = __esm({
|
|
|
51216
51574
|
"Form": Form,
|
|
51217
51575
|
"FormField": FormField,
|
|
51218
51576
|
"FormSectionHeader": FormSectionHeader,
|
|
51577
|
+
"GameAudioProvider": GameAudioProvider,
|
|
51219
51578
|
"GameAudioToggle": GameAudioToggle,
|
|
51579
|
+
"GameBoard3D": GameBoard3D,
|
|
51580
|
+
"GameCanvas3D": GameCanvas3D,
|
|
51581
|
+
"GameCanvas3DBattleTemplate": GameCanvas3DBattleTemplate,
|
|
51582
|
+
"GameCanvas3DCastleTemplate": GameCanvas3DCastleTemplate,
|
|
51583
|
+
"GameCanvas3DWorldMapTemplate": GameCanvas3DWorldMapTemplate,
|
|
51220
51584
|
"GameCard": GameCard,
|
|
51221
51585
|
"GameHud": GameHud,
|
|
51586
|
+
"GameIcon": GameIcon,
|
|
51222
51587
|
"GameMenu": GameMenu,
|
|
51223
51588
|
"GameShell": GameShell,
|
|
51224
51589
|
"GameTemplate": GameTemplate,
|