@backbay/glia-three 0.2.0-alpha.3 → 0.2.0-alpha.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/chunk-HFSDW5IV.js +1861 -0
- package/dist/chunk-HFSDW5IV.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-T7Z4PIZ7.js +17606 -0
- package/dist/chunk-T7Z4PIZ7.js.map +1 -0
- package/dist/environment/index.d.ts +116 -0
- package/dist/environment/index.js +26 -0
- package/dist/environment/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +274 -0
- package/dist/index.js.map +1 -0
- package/dist/three/index.d.ts +3402 -0
- package/dist/three/index.js +252 -0
- package/dist/three/index.js.map +1 -0
- package/package.json +4 -1
- package/src/environment/AuroraLayer/AuroraLayer.stories.tsx +0 -43
- package/src/environment/AuroraLayer/AuroraLayer.tsx +0 -200
- package/src/environment/AuroraLayer/index.ts +0 -2
- package/src/environment/AuroraLayer/types.ts +0 -30
- package/src/environment/EnvironmentLayer/EnvironmentLayer.stories.tsx +0 -262
- package/src/environment/EnvironmentLayer/EnvironmentLayer.tsx +0 -105
- package/src/environment/EnvironmentLayer/index.ts +0 -4
- package/src/environment/EnvironmentLayer/presets.ts +0 -128
- package/src/environment/FogLayer/FogLayer.stories.tsx +0 -83
- package/src/environment/FogLayer/FogLayer.tsx +0 -113
- package/src/environment/FogLayer/index.ts +0 -2
- package/src/environment/FogLayer/types.ts +0 -45
- package/src/environment/VolumetricLight/VolumetricLight.stories.tsx +0 -55
- package/src/environment/VolumetricLight/VolumetricLight.tsx +0 -188
- package/src/environment/VolumetricLight/index.ts +0 -2
- package/src/environment/VolumetricLight/types.ts +0 -33
- package/src/environment/WeatherLayer/WeatherLayer.stories.tsx +0 -348
- package/src/environment/WeatherLayer/WeatherLayer.tsx +0 -266
- package/src/environment/WeatherLayer/cinematicCanvas.tsx +0 -809
- package/src/environment/WeatherLayer/colors.ts +0 -27
- package/src/environment/WeatherLayer/index.ts +0 -4
- package/src/environment/WeatherLayer/leafPresets.ts +0 -12
- package/src/environment/WeatherLayer/particles.ts +0 -227
- package/src/environment/WeatherLayer/types.ts +0 -140
- package/src/environment/index.ts +0 -17
- package/src/environment/shared/index.ts +0 -2
- package/src/environment/shared/noise.ts +0 -10
- package/src/environment/shared/performance.ts +0 -33
- package/src/environment/shared/types.ts +0 -26
- package/src/index.ts +0 -2
- package/src/lib/utils.ts +0 -6
- package/src/lib/vision-types.ts +0 -84
- package/src/three/AgentConsole/AgentConsole.stories.tsx +0 -397
- package/src/three/AgentConsole/AgentConsole.tsx +0 -195
- package/src/three/AgentConsole/ConsoleChat.tsx +0 -237
- package/src/three/AgentConsole/FocusConstellation.tsx +0 -297
- package/src/three/AgentConsole/GlyphAvatar.stories.tsx +0 -110
- package/src/three/AgentConsole/GlyphAvatar.tsx +0 -117
- package/src/three/AgentConsole/QuickActions.tsx +0 -111
- package/src/three/AgentConsole/index.ts +0 -41
- package/src/three/AgentConsole/types.ts +0 -241
- package/src/three/AmbientField/AmbientField.stories.tsx +0 -290
- package/src/three/AmbientField/AmbientField.tsx +0 -307
- package/src/three/AmbientField/BackbayFieldBus.ts +0 -326
- package/src/three/AmbientField/FieldProvider.tsx +0 -83
- package/src/three/AmbientField/index.ts +0 -37
- package/src/three/AmbientField/shaders/constellation.ts +0 -384
- package/src/three/AmbientField/types.ts +0 -174
- package/src/three/AttackGraph/AttackGraph.stories.tsx +0 -144
- package/src/three/AttackGraph/AttackGraph.tsx +0 -325
- package/src/three/AttackGraph/index.ts +0 -19
- package/src/three/AttackGraph/types.ts +0 -97
- package/src/three/AuditTrail/AuditTrail.stories.tsx +0 -567
- package/src/three/AuditTrail/AuditTrail.tsx +0 -644
- package/src/three/AuditTrail/index.ts +0 -33
- package/src/three/AuditTrail/types.ts +0 -192
- package/src/three/CrystallineOrganism/Breadcrumb.tsx +0 -61
- package/src/three/CrystallineOrganism/CrystallineOrganism.stories.tsx +0 -509
- package/src/three/CrystallineOrganism/CrystallineOrganism.tsx +0 -273
- package/src/three/CrystallineOrganism/LatticeEdge.tsx +0 -69
- package/src/three/CrystallineOrganism/OrganismLattice.tsx +0 -159
- package/src/three/CrystallineOrganism/OrganismParticles.tsx +0 -148
- package/src/three/CrystallineOrganism/OrganismShell.tsx +0 -277
- package/src/three/CrystallineOrganism/constants.ts +0 -161
- package/src/three/CrystallineOrganism/index.ts +0 -17
- package/src/three/CrystallineOrganism/layouts/hexGrid.ts +0 -85
- package/src/three/CrystallineOrganism/layouts/index.ts +0 -1
- package/src/three/CrystallineOrganism/types.ts +0 -167
- package/src/three/CrystallineOrganism/useOrganismEmotion.ts +0 -84
- package/src/three/FirewallBarrier/FirewallBarrier.stories.tsx +0 -167
- package/src/three/FirewallBarrier/FirewallBarrier.tsx +0 -259
- package/src/three/FirewallBarrier/index.ts +0 -14
- package/src/three/FirewallBarrier/types.ts +0 -52
- package/src/three/Glyph/GlyphObject.stories.tsx +0 -577
- package/src/three/Glyph/GlyphObject.tsx +0 -422
- package/src/three/Glyph/index.ts +0 -10
- package/src/three/Glyph/types.ts +0 -36
- package/src/three/Glyph/useGlyphController.ts +0 -231
- package/src/three/Glyph/useGlyphEmotion.ts +0 -70
- package/src/three/Graph3D/Graph3D.stories.tsx +0 -269
- package/src/three/Graph3D/Graph3D.tsx +0 -248
- package/src/three/Graph3D/GraphEdge.tsx +0 -79
- package/src/three/Graph3D/GraphNode.tsx +0 -239
- package/src/three/Graph3D/types.ts +0 -66
- package/src/three/Graph3D/utils.ts +0 -204
- package/src/three/IntelFeed/IntelFeed.stories.tsx +0 -168
- package/src/three/IntelFeed/IntelFeed.tsx +0 -284
- package/src/three/IntelFeed/index.ts +0 -14
- package/src/three/IntelFeed/types.ts +0 -56
- package/src/three/MetricsGalaxy/MetricsGalaxy.tsx +0 -484
- package/src/three/MetricsGalaxy/index.ts +0 -6
- package/src/three/MetricsGalaxy/types.ts +0 -26
- package/src/three/NetworkTopology/NetworkTopology.stories.tsx +0 -184
- package/src/three/NetworkTopology/NetworkTopology.tsx +0 -421
- package/src/three/NetworkTopology/index.ts +0 -34
- package/src/three/NetworkTopology/types.ts +0 -128
- package/src/three/ParticleField/ParticleField.stories.tsx +0 -162
- package/src/three/ParticleField/ParticleField.tsx +0 -81
- package/src/three/ParticleField/index.ts +0 -1
- package/src/three/PermissionMatrix/PermissionMatrix.stories.tsx +0 -475
- package/src/three/PermissionMatrix/PermissionMatrix.tsx +0 -380
- package/src/three/PermissionMatrix/index.ts +0 -15
- package/src/three/PermissionMatrix/types.ts +0 -54
- package/src/three/QuantumField/ConstellationField.tsx +0 -238
- package/src/three/QuantumField/FieldBus.ts +0 -349
- package/src/three/QuantumField/FieldLayer.tsx +0 -430
- package/src/three/QuantumField/FieldProvider.tsx +0 -460
- package/src/three/QuantumField/PcbField.tsx +0 -406
- package/src/three/QuantumField/QuantumField.stories.tsx +0 -1155
- package/src/three/QuantumField/TrailRTT.ts +0 -212
- package/src/three/QuantumField/WaterField.tsx +0 -226
- package/src/three/QuantumField/WaterSimRTT.ts +0 -283
- package/src/three/QuantumField/domMapping.ts +0 -185
- package/src/three/QuantumField/index.ts +0 -110
- package/src/three/QuantumField/styles/index.ts +0 -9
- package/src/three/QuantumField/styles/styleA.ts +0 -526
- package/src/three/QuantumField/styles/styleB.ts +0 -1210
- package/src/three/QuantumField/styles/styleC.ts +0 -266
- package/src/three/QuantumField/themes.ts +0 -211
- package/src/three/QuantumField/types.ts +0 -380
- package/src/three/SOCCommandCenter/SOCCommandCenter.stories.tsx +0 -591
- package/src/three/SOCCommandCenter/SOCCommandCenter.tsx +0 -248
- package/src/three/SOCCommandCenter/index.ts +0 -26
- package/src/three/SOCCommandCenter/types.ts +0 -201
- package/src/three/SecurityDashboard/SecurityDashboard.stories.tsx +0 -508
- package/src/three/SecurityDashboard/SecurityDashboard.tsx +0 -507
- package/src/three/SecurityDashboard/index.ts +0 -37
- package/src/three/SecurityDashboard/types.ts +0 -143
- package/src/three/SecurityShield/SecurityShield.stories.tsx +0 -257
- package/src/three/SecurityShield/SecurityShield.tsx +0 -502
- package/src/three/SecurityShield/index.ts +0 -25
- package/src/three/SecurityShield/types.ts +0 -64
- package/src/three/Sentinel/AvatarMode.tsx +0 -578
- package/src/three/Sentinel/AvatarRenderer.tsx +0 -199
- package/src/three/Sentinel/CameraPip.tsx +0 -127
- package/src/three/Sentinel/CardinalItem.tsx +0 -83
- package/src/three/Sentinel/CardinalMenu.tsx +0 -370
- package/src/three/Sentinel/DockedMiniOrb.tsx +0 -146
- package/src/three/Sentinel/RadialSubmenu.tsx +0 -273
- package/src/three/Sentinel/SentinelConversation.tsx +0 -802
- package/src/three/Sentinel/SentinelOrb.tsx +0 -316
- package/src/three/Sentinel/SentinelOverlay.tsx +0 -146
- package/src/three/Sentinel/SentinelProvider.tsx +0 -145
- package/src/three/Sentinel/SentinelTether.tsx +0 -182
- package/src/three/Sentinel/SigilPlaceholder.tsx +0 -176
- package/src/three/Sentinel/VerticalSubmenu.tsx +0 -150
- package/src/three/Sentinel/index.ts +0 -145
- package/src/three/Sentinel/sentinelStore.ts +0 -196
- package/src/three/Sentinel/types.ts +0 -403
- package/src/three/Sentinel/useCameraPermission.ts +0 -153
- package/src/three/Sentinel/useThrowPhysics.ts +0 -220
- package/src/three/SpatialWorkspace/CyntraWorkspace.tsx +0 -84
- package/src/three/SpatialWorkspace/JobCluster.tsx +0 -281
- package/src/three/SpatialWorkspace/NodeGraph.tsx +0 -236
- package/src/three/SpatialWorkspace/ReceiptOrbit.tsx +0 -368
- package/src/three/SpatialWorkspace/SpatialWorkspace.stories.tsx +0 -547
- package/src/three/SpatialWorkspace/SpatialWorkspace.tsx +0 -428
- package/src/three/SpatialWorkspace/TrustRings.tsx +0 -228
- package/src/three/SpatialWorkspace/adapters.ts +0 -353
- package/src/three/SpatialWorkspace/index.ts +0 -85
- package/src/three/SpatialWorkspace/nexusAdapter.ts +0 -182
- package/src/three/SpatialWorkspace/types.ts +0 -389
- package/src/three/ThreatRadar/ThreatRadar.stories.tsx +0 -451
- package/src/three/ThreatRadar/ThreatRadar.tsx +0 -542
- package/src/three/ThreatRadar/index.ts +0 -8
- package/src/three/ThreatRadar/types.ts +0 -90
- package/src/three/ThreeErrorBoundary/ThreeErrorBoundary.tsx +0 -235
- package/src/three/ThreeErrorBoundary/index.ts +0 -5
- package/src/three/index.ts +0 -56
- package/tsconfig.json +0 -20
- package/tsup.config.ts +0 -21
|
@@ -0,0 +1,1861 @@
|
|
|
1
|
+
// src/environment/WeatherLayer/WeatherLayer.tsx
|
|
2
|
+
import * as React2 from "react";
|
|
3
|
+
import { useReducedMotion } from "framer-motion";
|
|
4
|
+
|
|
5
|
+
// src/lib/utils.ts
|
|
6
|
+
import { clsx } from "clsx";
|
|
7
|
+
import { twMerge } from "tailwind-merge";
|
|
8
|
+
function cn(...inputs) {
|
|
9
|
+
return twMerge(clsx(inputs));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// src/environment/shared/types.ts
|
|
13
|
+
var PERFORMANCE_PRESETS = {
|
|
14
|
+
high: { tier: "high", maxParticles: 2e3, useShaders: true, targetFPS: 60 },
|
|
15
|
+
medium: { tier: "medium", maxParticles: 800, useShaders: true, targetFPS: 60 },
|
|
16
|
+
low: { tier: "low", maxParticles: 300, useShaders: false, targetFPS: 30 },
|
|
17
|
+
minimal: { tier: "minimal", maxParticles: 0, useShaders: false, targetFPS: 0 }
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// src/environment/shared/performance.ts
|
|
21
|
+
function detectPerformanceTier() {
|
|
22
|
+
if (typeof window === "undefined") return "medium";
|
|
23
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
24
|
+
return "minimal";
|
|
25
|
+
}
|
|
26
|
+
const nav = navigator;
|
|
27
|
+
if (nav.deviceMemory && nav.deviceMemory < 4) {
|
|
28
|
+
return "low";
|
|
29
|
+
}
|
|
30
|
+
if (navigator.hardwareConcurrency && navigator.hardwareConcurrency < 4) {
|
|
31
|
+
return "low";
|
|
32
|
+
}
|
|
33
|
+
const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
34
|
+
if (isMobile) return "low";
|
|
35
|
+
return "medium";
|
|
36
|
+
}
|
|
37
|
+
function getPerformanceConfig(tier) {
|
|
38
|
+
const detected = tier ?? detectPerformanceTier();
|
|
39
|
+
return PERFORMANCE_PRESETS[detected];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/environment/WeatherLayer/types.ts
|
|
43
|
+
var WEATHER_CONFIGS = {
|
|
44
|
+
rain: {
|
|
45
|
+
baseCount: 200,
|
|
46
|
+
sizeRange: [1, 3],
|
|
47
|
+
opacityRange: [0.4, 0.7],
|
|
48
|
+
speedRange: [15, 25],
|
|
49
|
+
motionStyle: "fall",
|
|
50
|
+
shape: "streak",
|
|
51
|
+
colors: ["#a0c4ff", "#bde0fe"]
|
|
52
|
+
},
|
|
53
|
+
snow: {
|
|
54
|
+
baseCount: 150,
|
|
55
|
+
sizeRange: [2, 6],
|
|
56
|
+
opacityRange: [0.6, 0.9],
|
|
57
|
+
speedRange: [1, 3],
|
|
58
|
+
motionStyle: "drift",
|
|
59
|
+
shape: "circle",
|
|
60
|
+
colors: ["#ffffff", "#e8f4ff"]
|
|
61
|
+
},
|
|
62
|
+
dust: {
|
|
63
|
+
baseCount: 80,
|
|
64
|
+
sizeRange: [1, 3],
|
|
65
|
+
opacityRange: [0.2, 0.4],
|
|
66
|
+
speedRange: [0.5, 2],
|
|
67
|
+
motionStyle: "drift",
|
|
68
|
+
shape: "circle",
|
|
69
|
+
colors: ["#d4a574", "#c9b896"]
|
|
70
|
+
},
|
|
71
|
+
leaves: {
|
|
72
|
+
baseCount: 30,
|
|
73
|
+
sizeRange: [6, 12],
|
|
74
|
+
opacityRange: [0.7, 1],
|
|
75
|
+
speedRange: [2, 5],
|
|
76
|
+
motionStyle: "drift",
|
|
77
|
+
shape: "leaf",
|
|
78
|
+
colors: ["#d4a373", "#e07b39", "#bc6c25"]
|
|
79
|
+
},
|
|
80
|
+
embers: {
|
|
81
|
+
baseCount: 60,
|
|
82
|
+
sizeRange: [2, 5],
|
|
83
|
+
opacityRange: [0.5, 0.8],
|
|
84
|
+
speedRange: [2, 5],
|
|
85
|
+
motionStyle: "rise",
|
|
86
|
+
shape: "glow",
|
|
87
|
+
colors: ["#ff6b35", "#f7931e", "#ffcc00"]
|
|
88
|
+
},
|
|
89
|
+
fireflies: {
|
|
90
|
+
baseCount: 40,
|
|
91
|
+
sizeRange: [3, 6],
|
|
92
|
+
opacityRange: [0.3, 1],
|
|
93
|
+
speedRange: [0.3, 1],
|
|
94
|
+
motionStyle: "drift",
|
|
95
|
+
shape: "glow",
|
|
96
|
+
colors: ["#90ee90", "#adff2f", "#7fff00"]
|
|
97
|
+
},
|
|
98
|
+
ash: {
|
|
99
|
+
baseCount: 100,
|
|
100
|
+
sizeRange: [2, 5],
|
|
101
|
+
opacityRange: [0.3, 0.6],
|
|
102
|
+
speedRange: [1, 3],
|
|
103
|
+
motionStyle: "fall",
|
|
104
|
+
shape: "circle",
|
|
105
|
+
colors: ["#4a4a4a", "#6b6b6b", "#8b8b8b"]
|
|
106
|
+
},
|
|
107
|
+
sakura: {
|
|
108
|
+
baseCount: 50,
|
|
109
|
+
sizeRange: [6, 12],
|
|
110
|
+
opacityRange: [0.6, 0.9],
|
|
111
|
+
speedRange: [1, 3],
|
|
112
|
+
motionStyle: "drift",
|
|
113
|
+
shape: "petal",
|
|
114
|
+
colors: ["#ffb7c5", "#ff69b4", "#ffc0cb"]
|
|
115
|
+
},
|
|
116
|
+
sparks: {
|
|
117
|
+
baseCount: 40,
|
|
118
|
+
sizeRange: [1, 3],
|
|
119
|
+
opacityRange: [0.7, 1],
|
|
120
|
+
speedRange: [8, 15],
|
|
121
|
+
motionStyle: "erratic",
|
|
122
|
+
shape: "glow",
|
|
123
|
+
colors: ["#ffd700", "#ffffff", "#fffacd"]
|
|
124
|
+
},
|
|
125
|
+
spores: {
|
|
126
|
+
baseCount: 60,
|
|
127
|
+
sizeRange: [4, 8],
|
|
128
|
+
opacityRange: [0.4, 0.7],
|
|
129
|
+
speedRange: [0.5, 1.5],
|
|
130
|
+
motionStyle: "rise",
|
|
131
|
+
shape: "glow",
|
|
132
|
+
colors: ["#9370db", "#8a2be2", "#da70d6"]
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// src/environment/WeatherLayer/particles.ts
|
|
137
|
+
function smoothstep(edge0, edge1, x) {
|
|
138
|
+
const t = Math.max(0, Math.min(1, (x - edge0) / (edge1 - edge0)));
|
|
139
|
+
return t * t * (3 - 2 * t);
|
|
140
|
+
}
|
|
141
|
+
function getSpawnPosition(bounds, velocity) {
|
|
142
|
+
const margin = 40;
|
|
143
|
+
if (Math.abs(velocity.x) > Math.abs(velocity.y)) {
|
|
144
|
+
return {
|
|
145
|
+
x: velocity.x > 0 ? -margin : bounds.width + margin,
|
|
146
|
+
y: Math.random() * bounds.height
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
x: Math.random() * bounds.width,
|
|
151
|
+
y: velocity.y > 0 ? -margin : bounds.height + margin
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function getLifeRangeMs(type) {
|
|
155
|
+
switch (type) {
|
|
156
|
+
case "rain":
|
|
157
|
+
return [900, 2200];
|
|
158
|
+
case "snow":
|
|
159
|
+
return [3200, 7200];
|
|
160
|
+
case "dust":
|
|
161
|
+
return [5e3, 11e3];
|
|
162
|
+
case "leaves":
|
|
163
|
+
return [6e3, 14e3];
|
|
164
|
+
case "embers":
|
|
165
|
+
return [2600, 6500];
|
|
166
|
+
case "fireflies":
|
|
167
|
+
return [4500, 9e3];
|
|
168
|
+
case "ash":
|
|
169
|
+
return [4500, 1e4];
|
|
170
|
+
case "sakura":
|
|
171
|
+
return [6500, 15e3];
|
|
172
|
+
case "sparks":
|
|
173
|
+
return [450, 1200];
|
|
174
|
+
case "spores":
|
|
175
|
+
return [5e3, 12e3];
|
|
176
|
+
default:
|
|
177
|
+
return [3e3, 8e3];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function createParticle(id, type, config, wind, bounds, stylePreset = "ui", colorsOverride) {
|
|
181
|
+
const [minSize, maxSize] = config.sizeRange;
|
|
182
|
+
const [minOpacity, maxOpacity] = config.opacityRange;
|
|
183
|
+
const [minSpeed, maxSpeed] = config.speedRange;
|
|
184
|
+
const speed = minSpeed + Math.random() * (maxSpeed - minSpeed);
|
|
185
|
+
let vx = wind.x * speed;
|
|
186
|
+
let vy = wind.y * speed;
|
|
187
|
+
const depth = stylePreset === "cinematic" ? Math.sqrt(Math.random()) : 0;
|
|
188
|
+
const sizeScale = stylePreset === "cinematic" ? 0.55 + (1 - depth) * 0.45 : 1;
|
|
189
|
+
const speedScale = stylePreset === "cinematic" ? 0.6 + (1 - depth) * 0.4 : 1;
|
|
190
|
+
const opacityScale = stylePreset === "cinematic" ? 0.45 + (1 - depth) * 0.55 : 1;
|
|
191
|
+
switch (config.motionStyle) {
|
|
192
|
+
case "fall":
|
|
193
|
+
vy = speed;
|
|
194
|
+
vx = wind.x * 2 + (Math.random() - 0.5) * 0.5;
|
|
195
|
+
break;
|
|
196
|
+
case "rise":
|
|
197
|
+
vy = -speed;
|
|
198
|
+
vx = wind.x + (Math.random() - 0.5) * 2;
|
|
199
|
+
break;
|
|
200
|
+
case "drift": {
|
|
201
|
+
const allowUpdraft = type === "dust" || type === "fireflies";
|
|
202
|
+
const verticalSign = allowUpdraft ? Math.random() > 0.5 ? 1 : -1 : 1;
|
|
203
|
+
const driftFallScale = type === "snow" ? 0.35 : type === "leaves" ? 0.55 : type === "sakura" ? 0.45 : 0.3;
|
|
204
|
+
vy = speed * driftFallScale * verticalSign;
|
|
205
|
+
vx = wind.x + (Math.random() - 0.5) * 2;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
case "erratic":
|
|
209
|
+
vy = (Math.random() - 0.3) * speed;
|
|
210
|
+
vx = (Math.random() - 0.5) * speed * 2;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
vx *= speedScale;
|
|
214
|
+
vy *= speedScale;
|
|
215
|
+
const [minLife, maxLife] = getLifeRangeMs(type);
|
|
216
|
+
const maxLifeMs = minLife + Math.random() * (maxLife - minLife);
|
|
217
|
+
const palette = colorsOverride && colorsOverride.length > 0 ? colorsOverride : config.colors;
|
|
218
|
+
const color = palette.length > 0 ? palette[Math.floor(Math.random() * palette.length)] : "#ffffff";
|
|
219
|
+
const baseOpacity = (minOpacity + Math.random() * (maxOpacity - minOpacity)) * opacityScale;
|
|
220
|
+
const baseSize = (minSize + Math.random() * (maxSize - minSize)) * sizeScale;
|
|
221
|
+
const twinkleAmountBase = type === "fireflies" ? 0.55 : type === "sparks" ? 0.35 : type === "embers" ? 0.18 : type === "spores" ? 0.12 : 0;
|
|
222
|
+
const twinkleAmount = stylePreset === "cinematic" ? twinkleAmountBase * 0.65 : twinkleAmountBase;
|
|
223
|
+
const twinkleSpeed = type === "sparks" ? 0.03 : type === "fireflies" ? 8e-3 : type === "embers" ? 0.01 : type === "spores" ? 7e-3 : 0;
|
|
224
|
+
const driftPhase = Math.random() * Math.PI * 2;
|
|
225
|
+
const twinklePhase = Math.random() * Math.PI * 2;
|
|
226
|
+
const velocity = { x: vx, y: vy };
|
|
227
|
+
const spawn = getSpawnPosition(bounds, velocity);
|
|
228
|
+
const rotation = config.shape === "streak" ? Math.atan2(velocity.y, velocity.x) * 180 / Math.PI - 90 : Math.random() * 360;
|
|
229
|
+
const rotationSpeed = config.shape === "leaf" || config.shape === "petal" ? (stylePreset === "cinematic" ? 0.03 : 0.06) * (Math.random() > 0.5 ? 1 : -1) : 0;
|
|
230
|
+
return {
|
|
231
|
+
id,
|
|
232
|
+
x: spawn.x,
|
|
233
|
+
y: spawn.y,
|
|
234
|
+
size: baseSize,
|
|
235
|
+
opacity: baseOpacity,
|
|
236
|
+
rotation,
|
|
237
|
+
rotationSpeed,
|
|
238
|
+
velocity,
|
|
239
|
+
life: 0,
|
|
240
|
+
maxLife: maxLifeMs,
|
|
241
|
+
color,
|
|
242
|
+
baseOpacity,
|
|
243
|
+
depth,
|
|
244
|
+
driftPhase,
|
|
245
|
+
twinklePhase,
|
|
246
|
+
twinkleSpeed,
|
|
247
|
+
twinkleAmount
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function updateParticle(particle, config, deltaTime) {
|
|
251
|
+
const newLife = particle.life + deltaTime;
|
|
252
|
+
const lifeRatio = newLife / particle.maxLife;
|
|
253
|
+
const fadeIn = smoothstep(0, 0.08, lifeRatio);
|
|
254
|
+
const fadeOut = 1 - smoothstep(0.82, 1, lifeRatio);
|
|
255
|
+
let opacity = particle.baseOpacity * fadeIn * fadeOut;
|
|
256
|
+
if (particle.twinkleAmount > 0 && particle.twinkleSpeed > 0) {
|
|
257
|
+
const tw = Math.sin(newLife * particle.twinkleSpeed + particle.twinklePhase);
|
|
258
|
+
opacity *= Math.max(0.15, 1 + tw * particle.twinkleAmount * 0.5);
|
|
259
|
+
}
|
|
260
|
+
let vx = particle.velocity.x;
|
|
261
|
+
let vy = particle.velocity.y;
|
|
262
|
+
if (config.motionStyle === "drift") {
|
|
263
|
+
vx += Math.sin(newLife * 2e-3 + particle.driftPhase) * 0.18;
|
|
264
|
+
vy += Math.cos(newLife * 16e-4 + particle.driftPhase) * 0.07;
|
|
265
|
+
} else if (config.motionStyle === "erratic") {
|
|
266
|
+
vx += Math.sin(newLife * 0.01 + particle.driftPhase) * 0.25;
|
|
267
|
+
vy += Math.cos(newLife * 0.012 + particle.driftPhase) * 0.18;
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
...particle,
|
|
271
|
+
x: particle.x + vx * deltaTime * 0.1,
|
|
272
|
+
y: particle.y + vy * deltaTime * 0.1,
|
|
273
|
+
rotation: particle.rotation + particle.rotationSpeed * deltaTime,
|
|
274
|
+
life: newLife,
|
|
275
|
+
opacity
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function getParticleCount(type, intensity, maxParticles, stylePreset = "ui") {
|
|
279
|
+
const config = WEATHER_CONFIGS[type];
|
|
280
|
+
const styleScale = stylePreset === "cinematic" ? type === "snow" ? 1.8 : type === "leaves" ? 1.4 : 0.7 : 1;
|
|
281
|
+
const desired = Math.floor(config.baseCount * intensity * styleScale);
|
|
282
|
+
return Math.min(desired, maxParticles);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/environment/WeatherLayer/cinematicCanvas.tsx
|
|
286
|
+
import * as React from "react";
|
|
287
|
+
|
|
288
|
+
// src/environment/WeatherLayer/leafPresets.ts
|
|
289
|
+
var AUTUMN_LEAF_COLOR_PRESETS = {
|
|
290
|
+
"early-autumn": ["#6b8f3f", "#a9b84a", "#d8c35a", "#e5b04a", "#c97a2c"],
|
|
291
|
+
"peak-autumn": ["#b63a2b", "#d86b2c", "#e5a93a", "#c56b2b", "#7a3e1b"],
|
|
292
|
+
"late-autumn": ["#5a3d2b", "#7a563e", "#9a6f4b", "#b68a5e", "#8a6b4f"],
|
|
293
|
+
golden: ["#f2d27a", "#e9c15c", "#d8a93b", "#b9872a", "#e8b04a"],
|
|
294
|
+
rust: ["#a34a1f", "#c2602d", "#d6782f", "#e07b39", "#bc6c25"],
|
|
295
|
+
"maple-red": ["#7a1f1f", "#a02b2b", "#c0302b", "#d44a2c", "#e36a3a"],
|
|
296
|
+
muted: ["#d4a373", "#c08a5a", "#a66a3f", "#8c5a36", "#e3b07a"]
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
// src/environment/WeatherLayer/colors.ts
|
|
300
|
+
function resolveWeatherColors({
|
|
301
|
+
type,
|
|
302
|
+
configColors,
|
|
303
|
+
colors,
|
|
304
|
+
leafColorPreset
|
|
305
|
+
}) {
|
|
306
|
+
if (colors && colors.length > 0) return colors;
|
|
307
|
+
if (type === "leaves" && leafColorPreset) {
|
|
308
|
+
return AUTUMN_LEAF_COLOR_PRESETS[leafColorPreset] ?? configColors;
|
|
309
|
+
}
|
|
310
|
+
return configColors;
|
|
311
|
+
}
|
|
312
|
+
function colorsKey(colors) {
|
|
313
|
+
if (!colors || colors.length === 0) return "";
|
|
314
|
+
return colors.join("|");
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// src/environment/WeatherLayer/cinematicCanvas.tsx
|
|
318
|
+
import { jsx } from "react/jsx-runtime";
|
|
319
|
+
function clamp(min, value, max) {
|
|
320
|
+
return Math.max(min, Math.min(max, value));
|
|
321
|
+
}
|
|
322
|
+
function parseHexColor(color) {
|
|
323
|
+
const hex = color.trim();
|
|
324
|
+
if (!hex.startsWith("#")) return null;
|
|
325
|
+
const raw = hex.slice(1);
|
|
326
|
+
if (raw.length === 3) {
|
|
327
|
+
const r = Number.parseInt(raw[0] + raw[0], 16);
|
|
328
|
+
const g = Number.parseInt(raw[1] + raw[1], 16);
|
|
329
|
+
const b = Number.parseInt(raw[2] + raw[2], 16);
|
|
330
|
+
return { r, g, b };
|
|
331
|
+
}
|
|
332
|
+
if (raw.length === 6) {
|
|
333
|
+
const r = Number.parseInt(raw.slice(0, 2), 16);
|
|
334
|
+
const g = Number.parseInt(raw.slice(2, 4), 16);
|
|
335
|
+
const b = Number.parseInt(raw.slice(4, 6), 16);
|
|
336
|
+
return { r, g, b };
|
|
337
|
+
}
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
function mix(a, b, t) {
|
|
341
|
+
const tt = clamp(0, t, 1);
|
|
342
|
+
return {
|
|
343
|
+
r: Math.round(a.r + (b.r - a.r) * tt),
|
|
344
|
+
g: Math.round(a.g + (b.g - a.g) * tt),
|
|
345
|
+
b: Math.round(a.b + (b.b - a.b) * tt)
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
function rgba(c, a) {
|
|
349
|
+
return `rgba(${c.r}, ${c.g}, ${c.b}, ${clamp(0, a, 1)})`;
|
|
350
|
+
}
|
|
351
|
+
function getSeededNoise(t, phase) {
|
|
352
|
+
return Math.sin(t * 7e-4 + phase) * 0.6 + Math.sin(t * 13e-4 + phase * 1.7) * 0.3 + Math.sin(t * 21e-4 + phase * 2.3) * 0.1;
|
|
353
|
+
}
|
|
354
|
+
var SNOW_SPRITE_VARIANTS = 4;
|
|
355
|
+
var LEAF_SPRITE_VARIANTS = 11;
|
|
356
|
+
function makeSnowSprite(color, variant) {
|
|
357
|
+
const size = 64;
|
|
358
|
+
const canvas = document.createElement("canvas");
|
|
359
|
+
canvas.width = size;
|
|
360
|
+
canvas.height = size;
|
|
361
|
+
const ctx = canvas.getContext("2d");
|
|
362
|
+
if (!ctx) return canvas;
|
|
363
|
+
ctx.clearRect(0, 0, size, size);
|
|
364
|
+
const v = (variant % SNOW_SPRITE_VARIANTS + SNOW_SPRITE_VARIANTS) % SNOW_SPRITE_VARIANTS;
|
|
365
|
+
const cx = size / 2;
|
|
366
|
+
const cy = size / 2;
|
|
367
|
+
const r = size * 0.46;
|
|
368
|
+
const highlightX = cx - r * (0.16 + v * 0.04);
|
|
369
|
+
const highlightY = cy - r * (0.18 - v * 0.03);
|
|
370
|
+
const softness = v === 3 ? 0.62 : v === 2 ? 0.56 : v === 1 ? 0.5 : 0.46;
|
|
371
|
+
const g = ctx.createRadialGradient(cx - r * 0.1, cy - r * 0.1, 0, cx, cy, r);
|
|
372
|
+
g.addColorStop(0, "rgba(255,255,255,0.95)");
|
|
373
|
+
g.addColorStop(0.45, `rgba(255,255,255,${0.38 + v * 0.02})`);
|
|
374
|
+
g.addColorStop(1, "rgba(255,255,255,0)");
|
|
375
|
+
ctx.fillStyle = g;
|
|
376
|
+
ctx.beginPath();
|
|
377
|
+
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
|
378
|
+
ctx.fill();
|
|
379
|
+
const h = ctx.createRadialGradient(highlightX, highlightY, 0, highlightX, highlightY, r * softness);
|
|
380
|
+
h.addColorStop(0, `rgba(255,255,255,${0.26 + v * 0.05})`);
|
|
381
|
+
h.addColorStop(1, "rgba(255,255,255,0)");
|
|
382
|
+
ctx.fillStyle = h;
|
|
383
|
+
ctx.beginPath();
|
|
384
|
+
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
|
385
|
+
ctx.fill();
|
|
386
|
+
ctx.globalCompositeOperation = "source-in";
|
|
387
|
+
ctx.fillStyle = color;
|
|
388
|
+
ctx.fillRect(0, 0, size, size);
|
|
389
|
+
ctx.globalCompositeOperation = "source-over";
|
|
390
|
+
const s = ctx.createRadialGradient(highlightX, highlightY, 0, highlightX, highlightY, r * 0.34);
|
|
391
|
+
s.addColorStop(0, `rgba(255,255,255,${0.18 + v * 0.04})`);
|
|
392
|
+
s.addColorStop(1, "rgba(255,255,255,0)");
|
|
393
|
+
ctx.fillStyle = s;
|
|
394
|
+
ctx.beginPath();
|
|
395
|
+
ctx.arc(cx, cy, r, 0, Math.PI * 2);
|
|
396
|
+
ctx.fill();
|
|
397
|
+
return canvas;
|
|
398
|
+
}
|
|
399
|
+
function makeLeafSprite(color, variant) {
|
|
400
|
+
const size = 160;
|
|
401
|
+
const canvas = document.createElement("canvas");
|
|
402
|
+
canvas.width = size;
|
|
403
|
+
canvas.height = size;
|
|
404
|
+
const ctx = canvas.getContext("2d");
|
|
405
|
+
if (!ctx) return canvas;
|
|
406
|
+
ctx.clearRect(0, 0, size, size);
|
|
407
|
+
const base = parseHexColor(color) ?? { r: 180, g: 120, b: 70 };
|
|
408
|
+
const dark = mix(base, { r: 0, g: 0, b: 0 }, 0.35);
|
|
409
|
+
const light = mix(base, { r: 255, g: 255, b: 255 }, 0.25);
|
|
410
|
+
const rim = mix(base, { r: 0, g: 0, b: 0 }, 0.2);
|
|
411
|
+
const cx = size / 2;
|
|
412
|
+
const cy = size / 2 - 4;
|
|
413
|
+
const shape = (variant % LEAF_SPRITE_VARIANTS + LEAF_SPRITE_VARIANTS) % LEAF_SPRITE_VARIANTS;
|
|
414
|
+
const drawRadialPath = (radii, rx, ry) => {
|
|
415
|
+
const pts = radii.map((r, i) => {
|
|
416
|
+
const a = -Math.PI / 2 + i / radii.length * Math.PI * 2;
|
|
417
|
+
return {
|
|
418
|
+
x: cx + Math.cos(a) * rx * r,
|
|
419
|
+
y: cy + Math.sin(a) * ry * r
|
|
420
|
+
};
|
|
421
|
+
});
|
|
422
|
+
const mid = (a, b) => ({
|
|
423
|
+
x: (a.x + b.x) / 2,
|
|
424
|
+
y: (a.y + b.y) / 2
|
|
425
|
+
});
|
|
426
|
+
ctx.beginPath();
|
|
427
|
+
const start = mid(pts[pts.length - 1], pts[0]);
|
|
428
|
+
ctx.moveTo(start.x, start.y);
|
|
429
|
+
for (let i = 0; i < pts.length; i++) {
|
|
430
|
+
const p0 = pts[i];
|
|
431
|
+
const p1 = pts[(i + 1) % pts.length];
|
|
432
|
+
const m = mid(p0, p1);
|
|
433
|
+
ctx.quadraticCurveTo(p0.x, p0.y, m.x, m.y);
|
|
434
|
+
}
|
|
435
|
+
ctx.closePath();
|
|
436
|
+
};
|
|
437
|
+
let w = 56;
|
|
438
|
+
let h = 78;
|
|
439
|
+
let skew = 0;
|
|
440
|
+
let veinStyle = "midrib";
|
|
441
|
+
let carveNotch = false;
|
|
442
|
+
let drawKind = "ovate";
|
|
443
|
+
let radialRadii = null;
|
|
444
|
+
switch (shape) {
|
|
445
|
+
case 0:
|
|
446
|
+
w = 52;
|
|
447
|
+
h = 78;
|
|
448
|
+
skew = -0.04;
|
|
449
|
+
drawKind = "ovate";
|
|
450
|
+
break;
|
|
451
|
+
case 1:
|
|
452
|
+
w = 66;
|
|
453
|
+
h = 74;
|
|
454
|
+
drawKind = "radial";
|
|
455
|
+
radialRadii = [1, 0.58, 0.96, 0.52, 0.92, 0.6, 0.86, 0.62, 0.96, 0.58];
|
|
456
|
+
break;
|
|
457
|
+
case 2:
|
|
458
|
+
w = 60;
|
|
459
|
+
h = 80;
|
|
460
|
+
drawKind = "radial";
|
|
461
|
+
radialRadii = [1, 0.82, 0.95, 0.84, 0.92, 0.86, 0.9, 0.86, 0.92, 0.84, 0.95, 0.82];
|
|
462
|
+
break;
|
|
463
|
+
case 3:
|
|
464
|
+
w = 74;
|
|
465
|
+
h = 70;
|
|
466
|
+
veinStyle = "fan";
|
|
467
|
+
carveNotch = true;
|
|
468
|
+
drawKind = "ginkgo";
|
|
469
|
+
break;
|
|
470
|
+
case 4:
|
|
471
|
+
w = 60;
|
|
472
|
+
h = 78;
|
|
473
|
+
drawKind = "heart";
|
|
474
|
+
break;
|
|
475
|
+
case 5:
|
|
476
|
+
w = 34;
|
|
477
|
+
h = 80;
|
|
478
|
+
skew = 0.08;
|
|
479
|
+
drawKind = "willow";
|
|
480
|
+
break;
|
|
481
|
+
case 6:
|
|
482
|
+
w = 54;
|
|
483
|
+
h = 76;
|
|
484
|
+
drawKind = "deltoid";
|
|
485
|
+
break;
|
|
486
|
+
case 7:
|
|
487
|
+
w = 74;
|
|
488
|
+
h = 78;
|
|
489
|
+
drawKind = "radial";
|
|
490
|
+
radialRadii = [1, 0.74, 0.96, 0.72, 0.92, 0.72, 0.96, 0.74, 1, 0.74];
|
|
491
|
+
break;
|
|
492
|
+
case 8:
|
|
493
|
+
w = 50;
|
|
494
|
+
h = 74;
|
|
495
|
+
skew = 0.02;
|
|
496
|
+
drawKind = "ovate";
|
|
497
|
+
break;
|
|
498
|
+
case 9:
|
|
499
|
+
w = 66;
|
|
500
|
+
h = 74;
|
|
501
|
+
drawKind = "heart";
|
|
502
|
+
break;
|
|
503
|
+
case 10:
|
|
504
|
+
w = 56;
|
|
505
|
+
h = 80;
|
|
506
|
+
drawKind = "radial";
|
|
507
|
+
radialRadii = [1, 0.92, 0.99, 0.91, 0.98, 0.9, 0.97, 0.9, 0.98, 0.91, 0.99, 0.92];
|
|
508
|
+
break;
|
|
509
|
+
default:
|
|
510
|
+
w = 52;
|
|
511
|
+
h = 78;
|
|
512
|
+
skew = -0.04;
|
|
513
|
+
drawKind = "ovate";
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
const drawLeafPath = () => {
|
|
517
|
+
switch (drawKind) {
|
|
518
|
+
case "ovate":
|
|
519
|
+
ctx.beginPath();
|
|
520
|
+
ctx.moveTo(cx, cy - h);
|
|
521
|
+
ctx.bezierCurveTo(cx + w, cy - h * 0.55, cx + w * (1 - skew), cy + h * 0.55, cx, cy + h);
|
|
522
|
+
ctx.bezierCurveTo(cx - w * (1 + skew), cy + h * 0.55, cx - w, cy - h * 0.55, cx, cy - h);
|
|
523
|
+
ctx.closePath();
|
|
524
|
+
return;
|
|
525
|
+
case "radial":
|
|
526
|
+
drawRadialPath(radialRadii, w, h);
|
|
527
|
+
return;
|
|
528
|
+
case "ginkgo":
|
|
529
|
+
ctx.beginPath();
|
|
530
|
+
ctx.moveTo(cx, cy + h * 0.78);
|
|
531
|
+
ctx.bezierCurveTo(
|
|
532
|
+
cx + w * 0.55,
|
|
533
|
+
cy + h * 0.55,
|
|
534
|
+
cx + w * 1.05,
|
|
535
|
+
cy + h * 0.1,
|
|
536
|
+
cx + w * 0.92,
|
|
537
|
+
cy - h * 0.12
|
|
538
|
+
);
|
|
539
|
+
ctx.quadraticCurveTo(cx + w * 0.5, cy - h * 1.02, cx, cy - h * 0.88);
|
|
540
|
+
ctx.quadraticCurveTo(cx - w * 0.5, cy - h * 1.02, cx - w * 0.92, cy - h * 0.12);
|
|
541
|
+
ctx.bezierCurveTo(
|
|
542
|
+
cx - w * 1.05,
|
|
543
|
+
cy + h * 0.1,
|
|
544
|
+
cx - w * 0.55,
|
|
545
|
+
cy + h * 0.55,
|
|
546
|
+
cx,
|
|
547
|
+
cy + h * 0.78
|
|
548
|
+
);
|
|
549
|
+
ctx.closePath();
|
|
550
|
+
return;
|
|
551
|
+
case "heart":
|
|
552
|
+
ctx.beginPath();
|
|
553
|
+
ctx.moveTo(cx, cy + h);
|
|
554
|
+
ctx.bezierCurveTo(cx + w * 0.95, cy + h * 0.45, cx + w, cy + h * 0.05, cx + w * 0.62, cy - h * 0.38);
|
|
555
|
+
ctx.bezierCurveTo(cx + w * 0.25, cy - h * 0.78, cx, cy - h * 0.62, cx, cy - h * 0.48);
|
|
556
|
+
ctx.bezierCurveTo(cx, cy - h * 0.62, cx - w * 0.25, cy - h * 0.78, cx - w * 0.62, cy - h * 0.38);
|
|
557
|
+
ctx.bezierCurveTo(cx - w, cy + h * 0.05, cx - w * 0.95, cy + h * 0.45, cx, cy + h);
|
|
558
|
+
ctx.closePath();
|
|
559
|
+
return;
|
|
560
|
+
case "deltoid":
|
|
561
|
+
ctx.beginPath();
|
|
562
|
+
ctx.moveTo(cx, cy - h);
|
|
563
|
+
ctx.quadraticCurveTo(cx + w * 1.05, cy - h * 0.15, cx + w * 0.36, cy + h);
|
|
564
|
+
ctx.quadraticCurveTo(cx, cy + h * 0.78, cx - w * 0.36, cy + h);
|
|
565
|
+
ctx.quadraticCurveTo(cx - w * 1.05, cy - h * 0.15, cx, cy - h);
|
|
566
|
+
ctx.closePath();
|
|
567
|
+
return;
|
|
568
|
+
case "willow":
|
|
569
|
+
ctx.beginPath();
|
|
570
|
+
ctx.moveTo(cx, cy - h);
|
|
571
|
+
ctx.bezierCurveTo(cx + w, cy - h * 0.35, cx + w * (1 - skew), cy + h * 0.55, cx, cy + h);
|
|
572
|
+
ctx.bezierCurveTo(cx - w * (1 + skew), cy + h * 0.55, cx - w, cy - h * 0.35, cx, cy - h);
|
|
573
|
+
ctx.closePath();
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
ctx.save();
|
|
578
|
+
ctx.translate(2, 4);
|
|
579
|
+
drawLeafPath();
|
|
580
|
+
ctx.fillStyle = "rgba(0,0,0,0.22)";
|
|
581
|
+
ctx.fill();
|
|
582
|
+
ctx.restore();
|
|
583
|
+
drawLeafPath();
|
|
584
|
+
const grad = ctx.createLinearGradient(cx - w, cy - h, cx + w, cy + h);
|
|
585
|
+
grad.addColorStop(0, rgba(dark, 0.95));
|
|
586
|
+
grad.addColorStop(0.45, rgba(base, 0.95));
|
|
587
|
+
grad.addColorStop(1, rgba(light, 0.95));
|
|
588
|
+
ctx.fillStyle = grad;
|
|
589
|
+
ctx.fill();
|
|
590
|
+
drawLeafPath();
|
|
591
|
+
ctx.strokeStyle = rgba(rim, 0.55);
|
|
592
|
+
ctx.lineWidth = 2;
|
|
593
|
+
ctx.stroke();
|
|
594
|
+
ctx.save();
|
|
595
|
+
ctx.strokeStyle = rgba(dark, 0.5);
|
|
596
|
+
ctx.lineWidth = 3;
|
|
597
|
+
ctx.lineCap = "round";
|
|
598
|
+
ctx.beginPath();
|
|
599
|
+
ctx.moveTo(cx, cy + h * 0.95);
|
|
600
|
+
ctx.quadraticCurveTo(cx + w * 0.18, cy + h * 1.08, cx + w * 0.1, cy + h * 1.22);
|
|
601
|
+
ctx.stroke();
|
|
602
|
+
ctx.restore();
|
|
603
|
+
ctx.save();
|
|
604
|
+
drawLeafPath();
|
|
605
|
+
ctx.clip();
|
|
606
|
+
ctx.fillStyle = rgba(mix(dark, { r: 0, g: 0, b: 0 }, 0.1), 0.18);
|
|
607
|
+
for (let i = 0; i < 14; i++) {
|
|
608
|
+
const px = cx + (Math.random() - 0.5) * w * 1.5;
|
|
609
|
+
const py = cy + (Math.random() - 0.25) * h * 1.4;
|
|
610
|
+
const pr = 1.2 + Math.random() * 3.8;
|
|
611
|
+
ctx.globalAlpha = 0.06 + Math.random() * 0.08;
|
|
612
|
+
ctx.beginPath();
|
|
613
|
+
ctx.arc(px, py, pr, 0, Math.PI * 2);
|
|
614
|
+
ctx.fill();
|
|
615
|
+
}
|
|
616
|
+
ctx.globalAlpha = 0.45;
|
|
617
|
+
ctx.strokeStyle = rgba(mix(base, { r: 255, g: 255, b: 255 }, 0.6), 0.55);
|
|
618
|
+
ctx.lineWidth = 1.15;
|
|
619
|
+
if (veinStyle === "fan") {
|
|
620
|
+
const y0 = cy + h * 0.55;
|
|
621
|
+
for (let i = -7; i <= 7; i++) {
|
|
622
|
+
const t = i / 7;
|
|
623
|
+
const x1 = cx + t * w * 0.82;
|
|
624
|
+
const y1 = cy - h * 0.86 + Math.abs(t) * h * 0.18;
|
|
625
|
+
ctx.globalAlpha = 0.1 + (1 - Math.abs(t)) * 0.12;
|
|
626
|
+
ctx.beginPath();
|
|
627
|
+
ctx.moveTo(cx, y0);
|
|
628
|
+
ctx.quadraticCurveTo(cx + t * w * 0.25, cy - h * 0.1, x1, y1);
|
|
629
|
+
ctx.stroke();
|
|
630
|
+
}
|
|
631
|
+
} else {
|
|
632
|
+
ctx.globalAlpha = 0.34;
|
|
633
|
+
ctx.beginPath();
|
|
634
|
+
ctx.moveTo(cx, cy + h * 0.9);
|
|
635
|
+
ctx.quadraticCurveTo(cx + w * 0.05, cy, cx, cy - h * 0.9);
|
|
636
|
+
ctx.stroke();
|
|
637
|
+
const veinCount = shape === 5 ? 4 : 6;
|
|
638
|
+
for (let i = 0; i < veinCount; i++) {
|
|
639
|
+
const t = (i + 1) / (veinCount + 1);
|
|
640
|
+
const y = cy + h * 0.78 - t * h * 1.35;
|
|
641
|
+
const veinW = w * (0.12 + t * 0.6);
|
|
642
|
+
ctx.globalAlpha = 0.12;
|
|
643
|
+
ctx.beginPath();
|
|
644
|
+
ctx.moveTo(cx, y);
|
|
645
|
+
ctx.quadraticCurveTo(cx + veinW, y + h * 0.08, cx + veinW * 0.9, y + h * 0.22);
|
|
646
|
+
ctx.stroke();
|
|
647
|
+
ctx.beginPath();
|
|
648
|
+
ctx.moveTo(cx, y);
|
|
649
|
+
ctx.quadraticCurveTo(cx - veinW, y + h * 0.08, cx - veinW * 0.9, y + h * 0.22);
|
|
650
|
+
ctx.stroke();
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
ctx.restore();
|
|
654
|
+
if (carveNotch) {
|
|
655
|
+
ctx.save();
|
|
656
|
+
ctx.globalCompositeOperation = "destination-out";
|
|
657
|
+
ctx.beginPath();
|
|
658
|
+
ctx.arc(cx, cy - h * 0.78, w * 0.12, 0, Math.PI * 2);
|
|
659
|
+
ctx.fill();
|
|
660
|
+
ctx.restore();
|
|
661
|
+
}
|
|
662
|
+
return canvas;
|
|
663
|
+
}
|
|
664
|
+
function getSprite(cache, key, build) {
|
|
665
|
+
const existing = cache.get(key);
|
|
666
|
+
if (existing) return existing;
|
|
667
|
+
const created = build();
|
|
668
|
+
cache.set(key, created);
|
|
669
|
+
return created;
|
|
670
|
+
}
|
|
671
|
+
function createSnowParticles(count, config, bounds, options) {
|
|
672
|
+
const particles = [];
|
|
673
|
+
const palette = options?.colorsOverride && options.colorsOverride.length > 0 ? options.colorsOverride : config.colors;
|
|
674
|
+
for (let i = 0; i < count; i++) {
|
|
675
|
+
const z = Math.pow(Math.random(), 0.55);
|
|
676
|
+
let size = 1.1 + (1 - z) * 4.4 + Math.random() * 0.8;
|
|
677
|
+
const speed = 16 + (1 - z) * 68 + Math.random() * 12;
|
|
678
|
+
const driftAmp = 6 + (1 - z) * 24 + Math.random() * 10;
|
|
679
|
+
const driftFreq = 0.7 + Math.random() * 1.6;
|
|
680
|
+
const alpha = 0.24 + (1 - z) * 0.56 + Math.random() * 0.1;
|
|
681
|
+
const spriteIndex = Math.floor(Math.random() * SNOW_SPRITE_VARIANTS);
|
|
682
|
+
if (z < 0.22 && Math.random() < 0.06) {
|
|
683
|
+
size *= 1.8;
|
|
684
|
+
}
|
|
685
|
+
const spriteColor = options?.colorOverride ?? (palette.length > 0 ? palette[Math.floor(Math.random() * palette.length)] : "#ffffff");
|
|
686
|
+
particles.push({
|
|
687
|
+
x: Math.random() * bounds.width,
|
|
688
|
+
y: Math.random() * bounds.height,
|
|
689
|
+
z,
|
|
690
|
+
speed,
|
|
691
|
+
driftAmp,
|
|
692
|
+
driftFreq,
|
|
693
|
+
phase: Math.random() * Math.PI * 2,
|
|
694
|
+
spriteColor,
|
|
695
|
+
spriteIndex,
|
|
696
|
+
alpha,
|
|
697
|
+
size
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
return particles;
|
|
701
|
+
}
|
|
702
|
+
function createLeafParticles(count, config, bounds, options) {
|
|
703
|
+
const particles = [];
|
|
704
|
+
const palette = options?.colorsOverride && options.colorsOverride.length > 0 ? options.colorsOverride : config.colors;
|
|
705
|
+
for (let i = 0; i < count; i++) {
|
|
706
|
+
const z = Math.pow(Math.random(), 0.6);
|
|
707
|
+
const size = 6.5 + (1 - z) * 13.5 + Math.random() * 2.2;
|
|
708
|
+
const speed = 18 + (1 - z) * 48 + Math.random() * 9;
|
|
709
|
+
const alpha = 0.55 + (1 - z) * 0.35;
|
|
710
|
+
const rotation = Math.random() * Math.PI * 2;
|
|
711
|
+
const rotationSpeed = (Math.random() * 0.9 + 0.4) * (Math.random() > 0.5 ? 1 : -1);
|
|
712
|
+
const flutterSpeed = 1.2 + Math.random() * 1.6;
|
|
713
|
+
const windScale = 0.4 + Math.random() * 0.9;
|
|
714
|
+
const spriteColor = options?.colorOverride ?? (palette.length > 0 ? palette[Math.floor(Math.random() * palette.length)] : "#d4a373");
|
|
715
|
+
const spriteIndex = Math.floor(Math.random() * LEAF_SPRITE_VARIANTS);
|
|
716
|
+
particles.push({
|
|
717
|
+
x: Math.random() * bounds.width,
|
|
718
|
+
y: Math.random() * bounds.height,
|
|
719
|
+
z,
|
|
720
|
+
speed,
|
|
721
|
+
windScale,
|
|
722
|
+
phase: Math.random() * Math.PI * 2,
|
|
723
|
+
rotation,
|
|
724
|
+
rotationSpeed,
|
|
725
|
+
flutterPhase: Math.random() * Math.PI * 2,
|
|
726
|
+
flutterSpeed,
|
|
727
|
+
spriteColor,
|
|
728
|
+
spriteIndex,
|
|
729
|
+
alpha,
|
|
730
|
+
size
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
return particles;
|
|
734
|
+
}
|
|
735
|
+
function updateSnow(particles, dtSec, tMs, bounds, wind) {
|
|
736
|
+
const margin = 50;
|
|
737
|
+
for (const p of particles) {
|
|
738
|
+
const turb = getSeededNoise(tMs, p.phase);
|
|
739
|
+
const vx = wind.x * (20 + (1 - p.z) * 35) + turb * p.driftAmp * 0.9;
|
|
740
|
+
const vy = p.speed + wind.y * 10 + turb * 8;
|
|
741
|
+
p.x += vx * dtSec;
|
|
742
|
+
p.y += vy * dtSec;
|
|
743
|
+
p.x += Math.sin(tMs * 1e-3 * p.driftFreq + p.phase) * p.driftAmp * dtSec * 1.2;
|
|
744
|
+
if (p.y > bounds.height + margin) {
|
|
745
|
+
p.y = -margin - Math.random() * 40;
|
|
746
|
+
p.x = Math.random() * bounds.width;
|
|
747
|
+
}
|
|
748
|
+
if (p.x < -margin) p.x = bounds.width + margin;
|
|
749
|
+
if (p.x > bounds.width + margin) p.x = -margin;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
function updateLeaves(particles, dtSec, tMs, bounds, wind) {
|
|
753
|
+
const margin = 80;
|
|
754
|
+
for (const p of particles) {
|
|
755
|
+
const turb = getSeededNoise(tMs, p.phase);
|
|
756
|
+
const sway = Math.sin(tMs * 12e-4 + p.phase) * (14 + p.z * 18);
|
|
757
|
+
const vx = wind.x * (28 + (1 - p.z) * 40) * p.windScale + turb * 22 + sway;
|
|
758
|
+
const vy = p.speed + wind.y * 10 + Math.cos(tMs * 1e-3 + p.phase) * 10;
|
|
759
|
+
p.x += vx * dtSec;
|
|
760
|
+
p.y += vy * dtSec;
|
|
761
|
+
p.rotation += (p.rotationSpeed + turb * 0.6) * dtSec;
|
|
762
|
+
if (p.y > bounds.height + margin) {
|
|
763
|
+
p.y = -margin - Math.random() * 80;
|
|
764
|
+
p.x = Math.random() * bounds.width;
|
|
765
|
+
}
|
|
766
|
+
if (p.x < -margin) p.x = bounds.width + margin;
|
|
767
|
+
if (p.x > bounds.width + margin) p.x = -margin;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
function drawSnow(ctx, particles, opacity, spriteCache) {
|
|
771
|
+
for (const p of particles) {
|
|
772
|
+
const sprite = getSprite(
|
|
773
|
+
spriteCache,
|
|
774
|
+
`snow:${p.spriteColor}:${p.spriteIndex}`,
|
|
775
|
+
() => makeSnowSprite(p.spriteColor, p.spriteIndex)
|
|
776
|
+
);
|
|
777
|
+
const alpha = clamp(0, p.alpha * opacity, 1);
|
|
778
|
+
ctx.globalAlpha = alpha;
|
|
779
|
+
const s = p.size;
|
|
780
|
+
const baseX = p.x - s / 2;
|
|
781
|
+
const baseY = p.y - s / 2;
|
|
782
|
+
ctx.drawImage(sprite, baseX, baseY, s, s);
|
|
783
|
+
if (p.z < 0.2 && s > 6) {
|
|
784
|
+
ctx.globalAlpha = alpha * 0.14;
|
|
785
|
+
ctx.drawImage(sprite, baseX - s * 0.25, baseY - s * 0.25, s * 1.5, s * 1.5);
|
|
786
|
+
}
|
|
787
|
+
if (p.z < 0.25 && s > 3.2) {
|
|
788
|
+
ctx.globalAlpha = alpha * 0.28;
|
|
789
|
+
ctx.drawImage(sprite, baseX, baseY - s * 0.35, s, s);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
function drawLeaves(ctx, particles, opacity, tMs, config, spriteCache) {
|
|
794
|
+
for (const p of particles) {
|
|
795
|
+
const leafColor = p.spriteColor || config.colors[0] || "#d4a373";
|
|
796
|
+
const key = `leaf:${leafColor}:${p.spriteIndex}`;
|
|
797
|
+
const sprite = getSprite(spriteCache, key, () => makeLeafSprite(leafColor, p.spriteIndex));
|
|
798
|
+
const alpha = clamp(0, p.alpha * opacity, 1);
|
|
799
|
+
const flutter = Math.sin(tMs * 1e-3 * p.flutterSpeed + p.flutterPhase);
|
|
800
|
+
const tilt = flutter * 0.35;
|
|
801
|
+
const scale = p.size / 52 * (0.78 + (1 - p.z) * 0.34);
|
|
802
|
+
const squash = 0.75 + Math.abs(flutter) * 0.25;
|
|
803
|
+
ctx.save();
|
|
804
|
+
ctx.globalAlpha = alpha;
|
|
805
|
+
ctx.translate(p.x, p.y);
|
|
806
|
+
ctx.rotate(p.rotation + tilt);
|
|
807
|
+
ctx.scale(scale, scale * squash);
|
|
808
|
+
ctx.drawImage(sprite, -sprite.width / 2, -sprite.height / 2);
|
|
809
|
+
ctx.restore();
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
function getCinematicCount(type, intensity, maxParticles) {
|
|
813
|
+
return getParticleCount(type, intensity, maxParticles, "cinematic");
|
|
814
|
+
}
|
|
815
|
+
function WeatherLayerCinematicCanvas({
|
|
816
|
+
type,
|
|
817
|
+
intensity = 0.5,
|
|
818
|
+
stylePreset = "cinematic",
|
|
819
|
+
wind = { x: 0, y: 0 },
|
|
820
|
+
color,
|
|
821
|
+
colors,
|
|
822
|
+
leafColorPreset,
|
|
823
|
+
opacity = 1,
|
|
824
|
+
blur = false,
|
|
825
|
+
maxParticles: propMaxParticles,
|
|
826
|
+
enabled = true,
|
|
827
|
+
className,
|
|
828
|
+
style
|
|
829
|
+
}) {
|
|
830
|
+
const perfConfig = getPerformanceConfig();
|
|
831
|
+
const maxParticles = propMaxParticles ?? perfConfig.maxParticles;
|
|
832
|
+
const colorsKeyProp = colorsKey(colors);
|
|
833
|
+
const config = WEATHER_CONFIGS[type];
|
|
834
|
+
const particleColors = React.useMemo(
|
|
835
|
+
() => resolveWeatherColors({ type, colors, leafColorPreset, configColors: config.colors }),
|
|
836
|
+
[type, leafColorPreset, colorsKeyProp, config]
|
|
837
|
+
);
|
|
838
|
+
const containerRef = React.useRef(null);
|
|
839
|
+
const canvasRef = React.useRef(null);
|
|
840
|
+
const frameRef = React.useRef(0);
|
|
841
|
+
const lastRef = React.useRef(0);
|
|
842
|
+
const boundsRef = React.useRef({ width: 0, height: 0 });
|
|
843
|
+
const snowRef = React.useRef([]);
|
|
844
|
+
const leafRef = React.useRef([]);
|
|
845
|
+
const spriteCacheRef = React.useRef(/* @__PURE__ */ new Map());
|
|
846
|
+
React.useEffect(() => {
|
|
847
|
+
if (stylePreset !== "cinematic" || !enabled) return;
|
|
848
|
+
const container = containerRef.current;
|
|
849
|
+
const canvas = canvasRef.current;
|
|
850
|
+
if (!container || !canvas) return;
|
|
851
|
+
const ctx = canvas.getContext("2d", { alpha: true });
|
|
852
|
+
if (!ctx) return;
|
|
853
|
+
lastRef.current = 0;
|
|
854
|
+
const dpr = clamp(1, window.devicePixelRatio ?? 1, 2);
|
|
855
|
+
const resize = () => {
|
|
856
|
+
const width = Math.max(1, Math.floor(container.clientWidth));
|
|
857
|
+
const height = Math.max(1, Math.floor(container.clientHeight));
|
|
858
|
+
boundsRef.current = { width, height };
|
|
859
|
+
canvas.width = Math.floor(width * dpr);
|
|
860
|
+
canvas.height = Math.floor(height * dpr);
|
|
861
|
+
canvas.style.width = `${width}px`;
|
|
862
|
+
canvas.style.height = `${height}px`;
|
|
863
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
864
|
+
};
|
|
865
|
+
resize();
|
|
866
|
+
let ro = null;
|
|
867
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
868
|
+
ro = new ResizeObserver(resize);
|
|
869
|
+
ro.observe(container);
|
|
870
|
+
} else {
|
|
871
|
+
window.addEventListener("resize", resize);
|
|
872
|
+
}
|
|
873
|
+
const bounds = boundsRef.current;
|
|
874
|
+
const count = getCinematicCount(type, intensity, maxParticles);
|
|
875
|
+
if (type === "snow") {
|
|
876
|
+
snowRef.current = createSnowParticles(count, config, bounds, { colorsOverride: particleColors, colorOverride: color });
|
|
877
|
+
leafRef.current = [];
|
|
878
|
+
} else if (type === "leaves") {
|
|
879
|
+
leafRef.current = createLeafParticles(count, config, bounds, { colorsOverride: particleColors, colorOverride: color });
|
|
880
|
+
snowRef.current = [];
|
|
881
|
+
} else {
|
|
882
|
+
snowRef.current = [];
|
|
883
|
+
leafRef.current = [];
|
|
884
|
+
}
|
|
885
|
+
const animate = (tMs) => {
|
|
886
|
+
if (!lastRef.current) lastRef.current = tMs;
|
|
887
|
+
const dtMs = Math.min(tMs - lastRef.current, 40);
|
|
888
|
+
lastRef.current = tMs;
|
|
889
|
+
const dtSec = dtMs / 1e3;
|
|
890
|
+
const b = boundsRef.current;
|
|
891
|
+
if (type === "snow") {
|
|
892
|
+
updateSnow(snowRef.current, dtSec, tMs, b, wind);
|
|
893
|
+
} else if (type === "leaves") {
|
|
894
|
+
updateLeaves(leafRef.current, dtSec, tMs, b, wind);
|
|
895
|
+
}
|
|
896
|
+
ctx.clearRect(0, 0, b.width, b.height);
|
|
897
|
+
ctx.globalCompositeOperation = "source-over";
|
|
898
|
+
if (type === "snow") {
|
|
899
|
+
drawSnow(ctx, snowRef.current, opacity, spriteCacheRef.current);
|
|
900
|
+
} else if (type === "leaves") {
|
|
901
|
+
drawLeaves(ctx, leafRef.current, opacity, tMs, WEATHER_CONFIGS[type], spriteCacheRef.current);
|
|
902
|
+
}
|
|
903
|
+
frameRef.current = requestAnimationFrame(animate);
|
|
904
|
+
};
|
|
905
|
+
frameRef.current = requestAnimationFrame(animate);
|
|
906
|
+
return () => {
|
|
907
|
+
if (ro) ro.disconnect();
|
|
908
|
+
else window.removeEventListener("resize", resize);
|
|
909
|
+
if (frameRef.current) cancelAnimationFrame(frameRef.current);
|
|
910
|
+
};
|
|
911
|
+
}, [type, intensity, wind.x, wind.y, color, opacity, maxParticles, enabled, stylePreset, leafColorPreset, colorsKeyProp]);
|
|
912
|
+
return /* @__PURE__ */ jsx(
|
|
913
|
+
"div",
|
|
914
|
+
{
|
|
915
|
+
ref: containerRef,
|
|
916
|
+
className: cn("pointer-events-none absolute inset-0 overflow-hidden", className),
|
|
917
|
+
style: {
|
|
918
|
+
opacity: 1,
|
|
919
|
+
filter: blur ? "blur(0.8px)" : void 0,
|
|
920
|
+
...style
|
|
921
|
+
},
|
|
922
|
+
"aria-hidden": "true",
|
|
923
|
+
children: /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "absolute inset-0", "aria-hidden": "true" })
|
|
924
|
+
}
|
|
925
|
+
);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// src/environment/WeatherLayer/WeatherLayer.tsx
|
|
929
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
930
|
+
var ParticleRenderer = React2.memo(function ParticleRenderer2({
|
|
931
|
+
particle,
|
|
932
|
+
config,
|
|
933
|
+
colorOverride
|
|
934
|
+
}) {
|
|
935
|
+
const translate = `translate3d(${particle.x}px, ${particle.y}px, 0)`;
|
|
936
|
+
const rotate = `rotate(${particle.rotation}deg)`;
|
|
937
|
+
const particleColor = colorOverride ?? particle.color;
|
|
938
|
+
const baseStyle = {
|
|
939
|
+
position: "absolute",
|
|
940
|
+
left: 0,
|
|
941
|
+
top: 0,
|
|
942
|
+
opacity: particle.opacity,
|
|
943
|
+
transform: `${translate} ${rotate}`,
|
|
944
|
+
willChange: "transform, opacity",
|
|
945
|
+
pointerEvents: "none"
|
|
946
|
+
};
|
|
947
|
+
switch (config.shape) {
|
|
948
|
+
case "streak":
|
|
949
|
+
return /* @__PURE__ */ jsx2(
|
|
950
|
+
"div",
|
|
951
|
+
{
|
|
952
|
+
style: {
|
|
953
|
+
...baseStyle,
|
|
954
|
+
width: 1,
|
|
955
|
+
height: particle.size * 8,
|
|
956
|
+
background: `linear-gradient(to bottom, transparent, ${particleColor}, transparent)`
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
);
|
|
960
|
+
case "glow":
|
|
961
|
+
return /* @__PURE__ */ jsx2(
|
|
962
|
+
"div",
|
|
963
|
+
{
|
|
964
|
+
style: {
|
|
965
|
+
...baseStyle,
|
|
966
|
+
width: particle.size,
|
|
967
|
+
height: particle.size,
|
|
968
|
+
borderRadius: "50%",
|
|
969
|
+
backgroundColor: particleColor,
|
|
970
|
+
boxShadow: `0 0 ${particle.size * 2}px ${particleColor}`
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
);
|
|
974
|
+
case "leaf":
|
|
975
|
+
return /* @__PURE__ */ jsx2(
|
|
976
|
+
"div",
|
|
977
|
+
{
|
|
978
|
+
style: {
|
|
979
|
+
...baseStyle,
|
|
980
|
+
width: particle.size,
|
|
981
|
+
height: particle.size * 0.6,
|
|
982
|
+
borderRadius: "50% 0 50% 0",
|
|
983
|
+
backgroundColor: particleColor
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
);
|
|
987
|
+
case "petal":
|
|
988
|
+
return /* @__PURE__ */ jsx2(
|
|
989
|
+
"div",
|
|
990
|
+
{
|
|
991
|
+
style: {
|
|
992
|
+
...baseStyle,
|
|
993
|
+
width: particle.size,
|
|
994
|
+
height: particle.size * 0.7,
|
|
995
|
+
borderRadius: "50% 50% 50% 50%",
|
|
996
|
+
backgroundColor: particleColor,
|
|
997
|
+
transform: `${translate} ${rotate} scale(1, 0.6)`
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
);
|
|
1001
|
+
default:
|
|
1002
|
+
return /* @__PURE__ */ jsx2(
|
|
1003
|
+
"div",
|
|
1004
|
+
{
|
|
1005
|
+
style: {
|
|
1006
|
+
...baseStyle,
|
|
1007
|
+
width: particle.size,
|
|
1008
|
+
height: particle.size,
|
|
1009
|
+
borderRadius: "50%",
|
|
1010
|
+
backgroundColor: particleColor
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
});
|
|
1016
|
+
function WeatherLayer({
|
|
1017
|
+
type,
|
|
1018
|
+
intensity = 0.5,
|
|
1019
|
+
stylePreset = "ui",
|
|
1020
|
+
wind = { x: 0, y: 0 },
|
|
1021
|
+
color,
|
|
1022
|
+
colors,
|
|
1023
|
+
leafColorPreset,
|
|
1024
|
+
opacity = 1,
|
|
1025
|
+
blur = false,
|
|
1026
|
+
maxParticles: propMaxParticles,
|
|
1027
|
+
enabled = true,
|
|
1028
|
+
className,
|
|
1029
|
+
style
|
|
1030
|
+
}) {
|
|
1031
|
+
const reducedMotion = useReducedMotion();
|
|
1032
|
+
const containerRef = React2.useRef(null);
|
|
1033
|
+
const [particles, setParticles] = React2.useState([]);
|
|
1034
|
+
const frameRef = React2.useRef(0);
|
|
1035
|
+
const lastTimeRef = React2.useRef(0);
|
|
1036
|
+
const particleIdRef = React2.useRef(0);
|
|
1037
|
+
const config = WEATHER_CONFIGS[type];
|
|
1038
|
+
const perfConfig = getPerformanceConfig();
|
|
1039
|
+
const maxParticles = propMaxParticles ?? perfConfig.maxParticles;
|
|
1040
|
+
const colorsKeyProp = colorsKey(colors);
|
|
1041
|
+
const particleColors = React2.useMemo(
|
|
1042
|
+
() => resolveWeatherColors({ type, colors, leafColorPreset, configColors: config.colors }),
|
|
1043
|
+
[type, leafColorPreset, colorsKeyProp]
|
|
1044
|
+
);
|
|
1045
|
+
const shouldRender = enabled && !reducedMotion && perfConfig.tier !== "minimal";
|
|
1046
|
+
const useCinematicCanvas = shouldRender && stylePreset === "cinematic" && perfConfig.useShaders && (type === "snow" || type === "leaves");
|
|
1047
|
+
if (useCinematicCanvas) {
|
|
1048
|
+
return /* @__PURE__ */ jsx2(
|
|
1049
|
+
WeatherLayerCinematicCanvas,
|
|
1050
|
+
{
|
|
1051
|
+
type,
|
|
1052
|
+
intensity,
|
|
1053
|
+
stylePreset,
|
|
1054
|
+
wind,
|
|
1055
|
+
color,
|
|
1056
|
+
colors,
|
|
1057
|
+
leafColorPreset,
|
|
1058
|
+
opacity,
|
|
1059
|
+
blur,
|
|
1060
|
+
maxParticles,
|
|
1061
|
+
enabled,
|
|
1062
|
+
className,
|
|
1063
|
+
style
|
|
1064
|
+
}
|
|
1065
|
+
);
|
|
1066
|
+
}
|
|
1067
|
+
React2.useEffect(() => {
|
|
1068
|
+
if (!shouldRender || !containerRef.current) {
|
|
1069
|
+
setParticles([]);
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
lastTimeRef.current = 0;
|
|
1073
|
+
const container = containerRef.current;
|
|
1074
|
+
const bounds = {
|
|
1075
|
+
width: container.offsetWidth,
|
|
1076
|
+
height: container.offsetHeight
|
|
1077
|
+
};
|
|
1078
|
+
const particleCount = getParticleCount(type, intensity, maxParticles, stylePreset);
|
|
1079
|
+
const initialParticles = [];
|
|
1080
|
+
for (let i = 0; i < particleCount; i++) {
|
|
1081
|
+
let p = createParticle(particleIdRef.current++, type, config, wind, bounds, stylePreset, particleColors);
|
|
1082
|
+
p.y = Math.random() * bounds.height;
|
|
1083
|
+
p.x = Math.random() * bounds.width;
|
|
1084
|
+
p.life = Math.random() * p.maxLife * 0.7;
|
|
1085
|
+
p = updateParticle(p, config, 0);
|
|
1086
|
+
initialParticles.push(p);
|
|
1087
|
+
}
|
|
1088
|
+
setParticles(initialParticles);
|
|
1089
|
+
const animate = (time) => {
|
|
1090
|
+
if (!lastTimeRef.current) lastTimeRef.current = time;
|
|
1091
|
+
const deltaTime = Math.min(time - lastTimeRef.current, 50);
|
|
1092
|
+
lastTimeRef.current = time;
|
|
1093
|
+
setParticles((prev) => {
|
|
1094
|
+
const updated = [];
|
|
1095
|
+
const currentBounds = {
|
|
1096
|
+
width: container.offsetWidth,
|
|
1097
|
+
height: container.offsetHeight
|
|
1098
|
+
};
|
|
1099
|
+
for (const p of prev) {
|
|
1100
|
+
const newP = updateParticle(p, config, deltaTime);
|
|
1101
|
+
if (newP.life < newP.maxLife && newP.y > -50 && newP.y < currentBounds.height + 50 && newP.x > -50 && newP.x < currentBounds.width + 50) {
|
|
1102
|
+
updated.push(newP);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
const targetCount = getParticleCount(type, intensity, maxParticles, stylePreset);
|
|
1106
|
+
while (updated.length < targetCount) {
|
|
1107
|
+
updated.push(
|
|
1108
|
+
createParticle(particleIdRef.current++, type, config, wind, currentBounds, stylePreset, particleColors)
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
return updated;
|
|
1112
|
+
});
|
|
1113
|
+
frameRef.current = requestAnimationFrame(animate);
|
|
1114
|
+
};
|
|
1115
|
+
frameRef.current = requestAnimationFrame(animate);
|
|
1116
|
+
return () => {
|
|
1117
|
+
if (frameRef.current) {
|
|
1118
|
+
cancelAnimationFrame(frameRef.current);
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
}, [shouldRender, type, intensity, stylePreset, wind.x, wind.y, maxParticles, config, leafColorPreset, colorsKeyProp]);
|
|
1122
|
+
if (!shouldRender) {
|
|
1123
|
+
return null;
|
|
1124
|
+
}
|
|
1125
|
+
return /* @__PURE__ */ jsx2(
|
|
1126
|
+
"div",
|
|
1127
|
+
{
|
|
1128
|
+
ref: containerRef,
|
|
1129
|
+
className: cn(
|
|
1130
|
+
"pointer-events-none absolute inset-0 overflow-hidden",
|
|
1131
|
+
className
|
|
1132
|
+
),
|
|
1133
|
+
style: {
|
|
1134
|
+
opacity,
|
|
1135
|
+
filter: blur ? "blur(1px)" : void 0,
|
|
1136
|
+
...style
|
|
1137
|
+
},
|
|
1138
|
+
"aria-hidden": "true",
|
|
1139
|
+
children: particles.map((p) => /* @__PURE__ */ jsx2(
|
|
1140
|
+
ParticleRenderer,
|
|
1141
|
+
{
|
|
1142
|
+
particle: p,
|
|
1143
|
+
config,
|
|
1144
|
+
colorOverride: color
|
|
1145
|
+
},
|
|
1146
|
+
p.id
|
|
1147
|
+
))
|
|
1148
|
+
}
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// src/environment/FogLayer/FogLayer.tsx
|
|
1153
|
+
import * as React3 from "react";
|
|
1154
|
+
import { useReducedMotion as useReducedMotion2 } from "framer-motion";
|
|
1155
|
+
|
|
1156
|
+
// src/environment/shared/noise.ts
|
|
1157
|
+
var NOISE_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
|
|
1158
|
+
<filter id="n">
|
|
1159
|
+
<feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="3" seed="2" stitchTiles="stitch" />
|
|
1160
|
+
<feColorMatrix type="saturate" values="0" />
|
|
1161
|
+
</filter>
|
|
1162
|
+
<rect width="200" height="200" filter="url(#n)" opacity="0.55" />
|
|
1163
|
+
</svg>`;
|
|
1164
|
+
var NOISE_DATA_URL = `data:image/svg+xml,${encodeURIComponent(NOISE_SVG)}`;
|
|
1165
|
+
|
|
1166
|
+
// src/environment/FogLayer/types.ts
|
|
1167
|
+
var FOG_CONFIGS = {
|
|
1168
|
+
depth: {
|
|
1169
|
+
defaultColor: "#0a0a0f",
|
|
1170
|
+
defaultDensity: 0.5,
|
|
1171
|
+
animationSpeed: 0,
|
|
1172
|
+
gradientStops: 2
|
|
1173
|
+
},
|
|
1174
|
+
ground: {
|
|
1175
|
+
defaultColor: "#ffffff",
|
|
1176
|
+
defaultDensity: 0.7,
|
|
1177
|
+
animationSpeed: 0.02,
|
|
1178
|
+
gradientStops: 3
|
|
1179
|
+
},
|
|
1180
|
+
volumetric: {
|
|
1181
|
+
defaultColor: "#e8e8ff",
|
|
1182
|
+
defaultDensity: 0.4,
|
|
1183
|
+
animationSpeed: 0.01,
|
|
1184
|
+
gradientStops: 4
|
|
1185
|
+
},
|
|
1186
|
+
mist: {
|
|
1187
|
+
defaultColor: "#f0f0ff",
|
|
1188
|
+
defaultDensity: 0.3,
|
|
1189
|
+
animationSpeed: 0.015,
|
|
1190
|
+
gradientStops: 5
|
|
1191
|
+
}
|
|
1192
|
+
};
|
|
1193
|
+
|
|
1194
|
+
// src/environment/FogLayer/FogLayer.tsx
|
|
1195
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
1196
|
+
function FogLayer({
|
|
1197
|
+
type,
|
|
1198
|
+
density: propDensity,
|
|
1199
|
+
color: propColor,
|
|
1200
|
+
height = 0.4,
|
|
1201
|
+
animated = true,
|
|
1202
|
+
intensity = 1,
|
|
1203
|
+
stylePreset = "ui",
|
|
1204
|
+
enabled = true,
|
|
1205
|
+
className,
|
|
1206
|
+
style
|
|
1207
|
+
}) {
|
|
1208
|
+
const reducedMotion = useReducedMotion2();
|
|
1209
|
+
const perfConfig = getPerformanceConfig();
|
|
1210
|
+
const [offset, setOffset] = React3.useState(0);
|
|
1211
|
+
const config = FOG_CONFIGS[type];
|
|
1212
|
+
const density = propDensity ?? config.defaultDensity;
|
|
1213
|
+
const color = propColor ?? config.defaultColor;
|
|
1214
|
+
const shouldAnimate = animated && !reducedMotion && perfConfig.tier !== "minimal";
|
|
1215
|
+
React3.useEffect(() => {
|
|
1216
|
+
if (!shouldAnimate || !enabled || config.animationSpeed === 0) return;
|
|
1217
|
+
let frame;
|
|
1218
|
+
const animate = () => {
|
|
1219
|
+
setOffset((prev) => (prev + config.animationSpeed) % 100);
|
|
1220
|
+
frame = requestAnimationFrame(animate);
|
|
1221
|
+
};
|
|
1222
|
+
frame = requestAnimationFrame(animate);
|
|
1223
|
+
return () => cancelAnimationFrame(frame);
|
|
1224
|
+
}, [shouldAnimate, enabled, config.animationSpeed]);
|
|
1225
|
+
if (!enabled) return null;
|
|
1226
|
+
const gradientStyle = React3.useMemo(() => {
|
|
1227
|
+
const opacity = density * intensity;
|
|
1228
|
+
switch (type) {
|
|
1229
|
+
case "depth":
|
|
1230
|
+
return {
|
|
1231
|
+
background: `linear-gradient(to top, ${color}${Math.round(opacity * 255).toString(16).padStart(2, "0")}, transparent)`
|
|
1232
|
+
};
|
|
1233
|
+
case "ground":
|
|
1234
|
+
return {
|
|
1235
|
+
background: `linear-gradient(to top,
|
|
1236
|
+
${color}${Math.round(opacity * 255).toString(16).padStart(2, "0")} 0%,
|
|
1237
|
+
${color}${Math.round(opacity * 0.5 * 255).toString(16).padStart(2, "0")} ${height * 50}%,
|
|
1238
|
+
transparent ${height * 100}%)`
|
|
1239
|
+
};
|
|
1240
|
+
case "volumetric":
|
|
1241
|
+
return {
|
|
1242
|
+
background: `
|
|
1243
|
+
radial-gradient(ellipse 120% 60% at 50% ${100 - offset}%, ${color}${Math.round(opacity * 0.6 * 255).toString(16).padStart(2, "0")}, transparent),
|
|
1244
|
+
radial-gradient(ellipse 100% 40% at ${30 + offset * 0.2}% 80%, ${color}${Math.round(opacity * 0.4 * 255).toString(16).padStart(2, "0")}, transparent),
|
|
1245
|
+
radial-gradient(ellipse 80% 50% at ${70 - offset * 0.15}% 90%, ${color}${Math.round(opacity * 0.5 * 255).toString(16).padStart(2, "0")}, transparent)
|
|
1246
|
+
`
|
|
1247
|
+
};
|
|
1248
|
+
case "mist":
|
|
1249
|
+
return {
|
|
1250
|
+
background: `
|
|
1251
|
+
radial-gradient(ellipse 150% 80% at ${40 + Math.sin(offset * 0.1) * 20}% ${60 + Math.cos(offset * 0.08) * 10}%, ${color}${Math.round(opacity * 0.3 * 255).toString(16).padStart(2, "0")}, transparent),
|
|
1252
|
+
radial-gradient(ellipse 120% 60% at ${60 + Math.cos(offset * 0.12) * 15}% ${40 + Math.sin(offset * 0.09) * 15}%, ${color}${Math.round(opacity * 0.25 * 255).toString(16).padStart(2, "0")}, transparent),
|
|
1253
|
+
linear-gradient(to top, ${color}${Math.round(opacity * 0.15 * 255).toString(16).padStart(2, "0")}, transparent 50%)
|
|
1254
|
+
`
|
|
1255
|
+
};
|
|
1256
|
+
default:
|
|
1257
|
+
return {};
|
|
1258
|
+
}
|
|
1259
|
+
}, [type, color, density, intensity, height, offset]);
|
|
1260
|
+
return /* @__PURE__ */ jsx3(
|
|
1261
|
+
"div",
|
|
1262
|
+
{
|
|
1263
|
+
className: cn(
|
|
1264
|
+
"pointer-events-none absolute inset-0",
|
|
1265
|
+
className
|
|
1266
|
+
),
|
|
1267
|
+
style: {
|
|
1268
|
+
...gradientStyle,
|
|
1269
|
+
...style
|
|
1270
|
+
},
|
|
1271
|
+
"aria-hidden": "true",
|
|
1272
|
+
children: stylePreset === "cinematic" && /* @__PURE__ */ jsx3(
|
|
1273
|
+
"div",
|
|
1274
|
+
{
|
|
1275
|
+
className: "absolute inset-0",
|
|
1276
|
+
style: {
|
|
1277
|
+
backgroundImage: `url("${NOISE_DATA_URL}")`,
|
|
1278
|
+
backgroundRepeat: "repeat",
|
|
1279
|
+
backgroundSize: `${260 - Math.round(density * 80)}px ${260 - Math.round(density * 80)}px`,
|
|
1280
|
+
backgroundPosition: `${offset * 30}px ${offset * 20}px`,
|
|
1281
|
+
opacity: Math.min(0.18, 0.04 + density * intensity * 0.12),
|
|
1282
|
+
mixBlendMode: "soft-light",
|
|
1283
|
+
filter: "blur(0.6px) contrast(1.2)"
|
|
1284
|
+
},
|
|
1285
|
+
"aria-hidden": "true"
|
|
1286
|
+
}
|
|
1287
|
+
)
|
|
1288
|
+
}
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
// src/environment/VolumetricLight/VolumetricLight.tsx
|
|
1293
|
+
import * as React4 from "react";
|
|
1294
|
+
import { useReducedMotion as useReducedMotion3 } from "framer-motion";
|
|
1295
|
+
|
|
1296
|
+
// src/environment/VolumetricLight/types.ts
|
|
1297
|
+
var LIGHT_CONFIGS = {
|
|
1298
|
+
godrays: { defaultColor: "#fffbe6", defaultIntensity: 0.6, animated: true, blendMode: "screen" },
|
|
1299
|
+
shaft: { defaultColor: "#ffffff", defaultIntensity: 0.5, animated: false, blendMode: "screen" },
|
|
1300
|
+
bloom: { defaultColor: "#ffffff", defaultIntensity: 0.4, animated: false, blendMode: "screen" },
|
|
1301
|
+
flare: { defaultColor: "#ffe4b5", defaultIntensity: 0.7, animated: true, blendMode: "screen" },
|
|
1302
|
+
caustics: { defaultColor: "#00d4ff", defaultIntensity: 0.4, animated: true, blendMode: "overlay" },
|
|
1303
|
+
scanner: { defaultColor: "#00ff00", defaultIntensity: 0.6, animated: true, blendMode: "screen" },
|
|
1304
|
+
neon: { defaultColor: "#ff00ff", defaultIntensity: 0.8, animated: true, blendMode: "screen" },
|
|
1305
|
+
spotlight: { defaultColor: "#ffffff", defaultIntensity: 0.7, animated: false, blendMode: "screen" },
|
|
1306
|
+
rim: { defaultColor: "#00f0ff", defaultIntensity: 0.5, animated: false, blendMode: "screen" },
|
|
1307
|
+
laser: { defaultColor: "#ff0000", defaultIntensity: 0.9, animated: true, blendMode: "screen" }
|
|
1308
|
+
};
|
|
1309
|
+
|
|
1310
|
+
// src/environment/VolumetricLight/VolumetricLight.tsx
|
|
1311
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1312
|
+
function VolumetricLight({
|
|
1313
|
+
type,
|
|
1314
|
+
source = { x: 0.5, y: 0 },
|
|
1315
|
+
color: propColor,
|
|
1316
|
+
intensity = 0.5,
|
|
1317
|
+
decay = 0.5,
|
|
1318
|
+
angle = 0,
|
|
1319
|
+
width = 0.3,
|
|
1320
|
+
animated: propAnimated,
|
|
1321
|
+
stylePreset = "ui",
|
|
1322
|
+
enabled = true,
|
|
1323
|
+
className,
|
|
1324
|
+
style
|
|
1325
|
+
}) {
|
|
1326
|
+
const reducedMotion = useReducedMotion3();
|
|
1327
|
+
const perfConfig = getPerformanceConfig();
|
|
1328
|
+
const [time, setTime] = React4.useState(0);
|
|
1329
|
+
const config = LIGHT_CONFIGS[type];
|
|
1330
|
+
const color = propColor ?? config.defaultColor;
|
|
1331
|
+
const shouldAnimate = (propAnimated ?? config.animated) && !reducedMotion && perfConfig.tier !== "minimal";
|
|
1332
|
+
React4.useEffect(() => {
|
|
1333
|
+
if (!shouldAnimate || !enabled) return;
|
|
1334
|
+
let frame;
|
|
1335
|
+
const animate = () => {
|
|
1336
|
+
setTime((prev) => prev + 0.016);
|
|
1337
|
+
frame = requestAnimationFrame(animate);
|
|
1338
|
+
};
|
|
1339
|
+
frame = requestAnimationFrame(animate);
|
|
1340
|
+
return () => cancelAnimationFrame(frame);
|
|
1341
|
+
}, [shouldAnimate, enabled]);
|
|
1342
|
+
if (!enabled) return null;
|
|
1343
|
+
const lightStyle = React4.useMemo(() => {
|
|
1344
|
+
const opacity = intensity * config.defaultIntensity;
|
|
1345
|
+
const d = Math.max(0, Math.min(1, decay));
|
|
1346
|
+
switch (type) {
|
|
1347
|
+
case "godrays":
|
|
1348
|
+
return {
|
|
1349
|
+
background: `conic-gradient(from ${angle}deg at ${source.x * 100}% ${source.y * 100}%,
|
|
1350
|
+
${color}${Math.round(opacity * 0.4 * 255).toString(16).padStart(2, "0")} 0deg,
|
|
1351
|
+
transparent 15deg,
|
|
1352
|
+
${color}${Math.round(opacity * 0.3 * 255).toString(16).padStart(2, "0")} 30deg,
|
|
1353
|
+
transparent 45deg,
|
|
1354
|
+
${color}${Math.round(opacity * 0.35 * 255).toString(16).padStart(2, "0")} 60deg,
|
|
1355
|
+
transparent 75deg,
|
|
1356
|
+
${color}${Math.round(opacity * 0.25 * 255).toString(16).padStart(2, "0")} 90deg,
|
|
1357
|
+
transparent 105deg
|
|
1358
|
+
)`,
|
|
1359
|
+
mixBlendMode: config.blendMode
|
|
1360
|
+
};
|
|
1361
|
+
case "shaft":
|
|
1362
|
+
return {
|
|
1363
|
+
background: `linear-gradient(${angle}deg,
|
|
1364
|
+
transparent ${(source.x - width / 2) * 100}%,
|
|
1365
|
+
${color}${Math.round(opacity * 0.5 * 255).toString(16).padStart(2, "0")} ${source.x * 100}%,
|
|
1366
|
+
transparent ${(source.x + width / 2) * 100}%
|
|
1367
|
+
)`,
|
|
1368
|
+
mixBlendMode: config.blendMode
|
|
1369
|
+
};
|
|
1370
|
+
case "bloom": {
|
|
1371
|
+
const bloomInner = 18 + d * 22;
|
|
1372
|
+
const bloomMid = 42 + d * 24;
|
|
1373
|
+
return {
|
|
1374
|
+
background: `radial-gradient(circle at ${source.x * 100}% ${source.y * 100}%,
|
|
1375
|
+
${color}${Math.round(opacity * 0.6 * 255).toString(16).padStart(2, "0")},
|
|
1376
|
+
${color}${Math.round(opacity * 0.3 * 255).toString(16).padStart(2, "0")} ${bloomInner}%,
|
|
1377
|
+
transparent ${bloomMid}%
|
|
1378
|
+
)`,
|
|
1379
|
+
mixBlendMode: config.blendMode,
|
|
1380
|
+
filter: "blur(20px)"
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
case "flare":
|
|
1384
|
+
const flareOffset = Math.sin(time * 2) * 5;
|
|
1385
|
+
return {
|
|
1386
|
+
background: `
|
|
1387
|
+
radial-gradient(ellipse 20% 5% at ${source.x * 100}% ${source.y * 100 + flareOffset}%, ${color}${Math.round(opacity * 0.8 * 255).toString(16).padStart(2, "0")}, transparent),
|
|
1388
|
+
radial-gradient(ellipse 5% 20% at ${source.x * 100}% ${source.y * 100}%, ${color}${Math.round(opacity * 0.6 * 255).toString(16).padStart(2, "0")}, transparent),
|
|
1389
|
+
radial-gradient(circle at ${source.x * 100}% ${source.y * 100}%, ${color}${Math.round(opacity * 0.4 * 255).toString(16).padStart(2, "0")}, transparent 20%)
|
|
1390
|
+
`,
|
|
1391
|
+
mixBlendMode: config.blendMode
|
|
1392
|
+
};
|
|
1393
|
+
case "caustics":
|
|
1394
|
+
const causticPhase = time * 0.5;
|
|
1395
|
+
return {
|
|
1396
|
+
background: `
|
|
1397
|
+
radial-gradient(ellipse at ${50 + Math.sin(causticPhase) * 20}% ${50 + Math.cos(causticPhase * 1.3) * 20}%, ${color}${Math.round(opacity * 0.3 * 255).toString(16).padStart(2, "0")}, transparent 40%),
|
|
1398
|
+
radial-gradient(ellipse at ${50 + Math.cos(causticPhase * 0.7) * 25}% ${50 + Math.sin(causticPhase * 1.1) * 25}%, ${color}${Math.round(opacity * 0.25 * 255).toString(16).padStart(2, "0")}, transparent 35%)
|
|
1399
|
+
`,
|
|
1400
|
+
mixBlendMode: config.blendMode
|
|
1401
|
+
};
|
|
1402
|
+
case "scanner":
|
|
1403
|
+
const scanAngle = time * 60 % 360;
|
|
1404
|
+
return {
|
|
1405
|
+
background: `conic-gradient(from ${scanAngle}deg at ${source.x * 100}% ${source.y * 100}%,
|
|
1406
|
+
${color}${Math.round(opacity * 0.6 * 255).toString(16).padStart(2, "0")} 0deg,
|
|
1407
|
+
transparent 30deg,
|
|
1408
|
+
transparent 360deg
|
|
1409
|
+
)`,
|
|
1410
|
+
mixBlendMode: config.blendMode
|
|
1411
|
+
};
|
|
1412
|
+
case "neon":
|
|
1413
|
+
const flickerIntensity = 0.9 + Math.sin(time * 20) * 0.1;
|
|
1414
|
+
return {
|
|
1415
|
+
background: `linear-gradient(${angle}deg, transparent 40%, ${color}${Math.round(opacity * flickerIntensity * 0.8 * 255).toString(16).padStart(2, "0")} 50%, transparent 60%)`,
|
|
1416
|
+
boxShadow: `0 0 30px ${color}${Math.round(opacity * flickerIntensity * 0.5 * 255).toString(16).padStart(2, "0")}, 0 0 60px ${color}${Math.round(opacity * flickerIntensity * 0.3 * 255).toString(16).padStart(2, "0")}`,
|
|
1417
|
+
mixBlendMode: config.blendMode
|
|
1418
|
+
};
|
|
1419
|
+
case "spotlight": {
|
|
1420
|
+
const spotMid = 40 + d * 35;
|
|
1421
|
+
return {
|
|
1422
|
+
background: `radial-gradient(ellipse ${width * 100}% ${width * 150}% at ${source.x * 100}% ${source.y * 100}%,
|
|
1423
|
+
${color}${Math.round(opacity * 0.7 * 255).toString(16).padStart(2, "0")},
|
|
1424
|
+
${color}${Math.round(opacity * 0.3 * 255).toString(16).padStart(2, "0")} ${spotMid}%,
|
|
1425
|
+
transparent 100%
|
|
1426
|
+
)`,
|
|
1427
|
+
mixBlendMode: config.blendMode
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
case "rim":
|
|
1431
|
+
return {
|
|
1432
|
+
boxShadow: `inset 0 0 ${60 * intensity}px ${color}${Math.round(opacity * 0.6 * 255).toString(16).padStart(2, "0")}`,
|
|
1433
|
+
mixBlendMode: config.blendMode
|
|
1434
|
+
};
|
|
1435
|
+
case "laser":
|
|
1436
|
+
const laserOffset = Math.sin(time * 3) * 10;
|
|
1437
|
+
return {
|
|
1438
|
+
background: `linear-gradient(${angle + laserOffset}deg,
|
|
1439
|
+
transparent 49%,
|
|
1440
|
+
${color}${Math.round(opacity * 255).toString(16).padStart(2, "0")} 49.5%,
|
|
1441
|
+
${color}${Math.round(opacity * 255).toString(16).padStart(2, "0")} 50.5%,
|
|
1442
|
+
transparent 51%
|
|
1443
|
+
)`,
|
|
1444
|
+
mixBlendMode: config.blendMode
|
|
1445
|
+
};
|
|
1446
|
+
default:
|
|
1447
|
+
return {};
|
|
1448
|
+
}
|
|
1449
|
+
}, [type, source, color, intensity, decay, angle, width, time, config]);
|
|
1450
|
+
return /* @__PURE__ */ jsx4(
|
|
1451
|
+
"div",
|
|
1452
|
+
{
|
|
1453
|
+
className: cn("pointer-events-none absolute inset-0", className),
|
|
1454
|
+
style: { ...lightStyle, ...style },
|
|
1455
|
+
"aria-hidden": "true",
|
|
1456
|
+
children: stylePreset === "cinematic" && /* @__PURE__ */ jsx4(
|
|
1457
|
+
"div",
|
|
1458
|
+
{
|
|
1459
|
+
className: "absolute inset-0",
|
|
1460
|
+
style: {
|
|
1461
|
+
backgroundImage: `url("${NOISE_DATA_URL}")`,
|
|
1462
|
+
backgroundRepeat: "repeat",
|
|
1463
|
+
backgroundSize: "240px 240px",
|
|
1464
|
+
backgroundPosition: `${time * 160}px ${time * 110}px`,
|
|
1465
|
+
opacity: Math.min(0.12, 0.03 + intensity * 0.06),
|
|
1466
|
+
mixBlendMode: "overlay",
|
|
1467
|
+
filter: "blur(0.4px) contrast(1.25)"
|
|
1468
|
+
},
|
|
1469
|
+
"aria-hidden": "true"
|
|
1470
|
+
}
|
|
1471
|
+
)
|
|
1472
|
+
}
|
|
1473
|
+
);
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// src/environment/AuroraLayer/AuroraLayer.tsx
|
|
1477
|
+
import * as React5 from "react";
|
|
1478
|
+
import { useReducedMotion as useReducedMotion4 } from "framer-motion";
|
|
1479
|
+
|
|
1480
|
+
// src/environment/AuroraLayer/types.ts
|
|
1481
|
+
var AURORA_CONFIGS = {
|
|
1482
|
+
aurora: { colors: ["#00ff87", "#60efff", "#ff00aa"], animationSpeed: 8e-3, complexity: 3 },
|
|
1483
|
+
nebula: { colors: ["#1a0533", "#4b0082", "#ff006e"], animationSpeed: 5e-3, complexity: 4 },
|
|
1484
|
+
gradient: { colors: ["#667eea", "#764ba2"], animationSpeed: 3e-3, complexity: 1 },
|
|
1485
|
+
stars: { colors: ["#ffffff", "#fffacd", "#e6e6fa"], animationSpeed: 0.01, complexity: 5 },
|
|
1486
|
+
clouds: { colors: ["#f0f0f0", "#d0d0d0", "#e8e8e8"], animationSpeed: 4e-3, complexity: 3 },
|
|
1487
|
+
sunset: { colors: ["#ff6b35", "#f7c59f", "#2e294e"], animationSpeed: 2e-3, complexity: 2 },
|
|
1488
|
+
storm: { colors: ["#1c1c1c", "#363636", "#4a4a4a"], animationSpeed: 0.015, complexity: 4 },
|
|
1489
|
+
cosmic: { colors: ["#0d0221", "#0f084b", "#26408b", "#a663cc"], animationSpeed: 3e-3, complexity: 5 },
|
|
1490
|
+
heat: { colors: ["#ff4500", "#ff6347", "#ffa500"], animationSpeed: 0.02, complexity: 2 },
|
|
1491
|
+
underwater: { colors: ["#006994", "#00a8cc", "#40e0d0"], animationSpeed: 6e-3, complexity: 3 }
|
|
1492
|
+
};
|
|
1493
|
+
|
|
1494
|
+
// src/environment/AuroraLayer/AuroraLayer.tsx
|
|
1495
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
1496
|
+
function AuroraLayer({
|
|
1497
|
+
type,
|
|
1498
|
+
colors: propColors,
|
|
1499
|
+
intensity = 1,
|
|
1500
|
+
speed = 1,
|
|
1501
|
+
stylePreset = "ui",
|
|
1502
|
+
complexity: propComplexity,
|
|
1503
|
+
direction = "vertical",
|
|
1504
|
+
blend = "normal",
|
|
1505
|
+
enabled = true,
|
|
1506
|
+
className,
|
|
1507
|
+
style
|
|
1508
|
+
}) {
|
|
1509
|
+
const reducedMotion = useReducedMotion4();
|
|
1510
|
+
const perfConfig = getPerformanceConfig();
|
|
1511
|
+
const [time, setTime] = React5.useState(0);
|
|
1512
|
+
const lightningRef = React5.useRef(0);
|
|
1513
|
+
const config = AURORA_CONFIGS[type];
|
|
1514
|
+
const colors = propColors ?? config.colors;
|
|
1515
|
+
const complexity = propComplexity ?? config.complexity;
|
|
1516
|
+
const shouldAnimate = !reducedMotion && perfConfig.tier !== "minimal";
|
|
1517
|
+
React5.useEffect(() => {
|
|
1518
|
+
if (!shouldAnimate || !enabled) return;
|
|
1519
|
+
lightningRef.current = 0;
|
|
1520
|
+
let frame;
|
|
1521
|
+
const animate = () => {
|
|
1522
|
+
if (type === "storm") {
|
|
1523
|
+
const chance = stylePreset === "cinematic" ? 3e-3 : 7e-3;
|
|
1524
|
+
if (Math.random() < chance) lightningRef.current = 1;
|
|
1525
|
+
lightningRef.current *= stylePreset === "cinematic" ? 0.92 : 0.88;
|
|
1526
|
+
if (lightningRef.current < 0.02) lightningRef.current = 0;
|
|
1527
|
+
}
|
|
1528
|
+
setTime((prev) => prev + config.animationSpeed * speed);
|
|
1529
|
+
frame = requestAnimationFrame(animate);
|
|
1530
|
+
};
|
|
1531
|
+
frame = requestAnimationFrame(animate);
|
|
1532
|
+
return () => cancelAnimationFrame(frame);
|
|
1533
|
+
}, [shouldAnimate, enabled, type, stylePreset, config.animationSpeed, speed]);
|
|
1534
|
+
if (!enabled) return null;
|
|
1535
|
+
const lightning = type === "storm" ? lightningRef.current : 0;
|
|
1536
|
+
const auroraStyle = React5.useMemo(() => {
|
|
1537
|
+
const opacity = intensity;
|
|
1538
|
+
const c = colors.map((color) => `${color}${Math.round(opacity * 200).toString(16).padStart(2, "0")}`);
|
|
1539
|
+
switch (type) {
|
|
1540
|
+
case "aurora":
|
|
1541
|
+
return {
|
|
1542
|
+
background: `
|
|
1543
|
+
linear-gradient(${direction === "horizontal" ? "90deg" : "180deg"},
|
|
1544
|
+
${c[0]} ${Math.sin(time) * 20 + 10}%,
|
|
1545
|
+
${c[1]} ${50 + Math.cos(time * 1.3) * 20}%,
|
|
1546
|
+
${c[2]} ${90 + Math.sin(time * 0.7) * 10}%
|
|
1547
|
+
)
|
|
1548
|
+
`,
|
|
1549
|
+
filter: `blur(${40 + Math.sin(time * 2) * 10}px)`,
|
|
1550
|
+
mixBlendMode: blend
|
|
1551
|
+
};
|
|
1552
|
+
case "nebula":
|
|
1553
|
+
return {
|
|
1554
|
+
background: `
|
|
1555
|
+
radial-gradient(ellipse at ${30 + Math.sin(time) * 20}% ${40 + Math.cos(time * 0.8) * 20}%, ${c[0]}, transparent 50%),
|
|
1556
|
+
radial-gradient(ellipse at ${70 + Math.cos(time * 1.2) * 15}% ${60 + Math.sin(time * 0.9) * 15}%, ${c[1]}, transparent 40%),
|
|
1557
|
+
radial-gradient(ellipse at ${50 + Math.sin(time * 0.7) * 25}% ${50 + Math.cos(time * 1.1) * 25}%, ${c[2]}, transparent 45%)
|
|
1558
|
+
`,
|
|
1559
|
+
mixBlendMode: blend
|
|
1560
|
+
};
|
|
1561
|
+
case "gradient":
|
|
1562
|
+
return {
|
|
1563
|
+
background: `linear-gradient(${direction === "horizontal" ? "90deg" : direction === "radial" ? "135deg" : "180deg"}, ${c.join(", ")})`,
|
|
1564
|
+
mixBlendMode: blend
|
|
1565
|
+
};
|
|
1566
|
+
case "stars":
|
|
1567
|
+
const starGradients = Array.from({ length: Math.min(complexity * 10, 50) }, (_, i) => {
|
|
1568
|
+
const x = (i * 17 + Math.sin(i) * 30) % 100;
|
|
1569
|
+
const y = (i * 23 + Math.cos(i) * 40) % 100;
|
|
1570
|
+
const twinkle = Math.sin(time * (2 + i * 0.1)) * 0.5 + 0.5;
|
|
1571
|
+
const size = 1 + i % 3;
|
|
1572
|
+
return `radial-gradient(circle ${size}px at ${x}% ${y}%, ${colors[i % colors.length]}${Math.round(twinkle * opacity * 255).toString(16).padStart(2, "0")}, transparent ${size}px)`;
|
|
1573
|
+
});
|
|
1574
|
+
return {
|
|
1575
|
+
background: starGradients.join(", "),
|
|
1576
|
+
mixBlendMode: blend
|
|
1577
|
+
};
|
|
1578
|
+
case "clouds":
|
|
1579
|
+
return {
|
|
1580
|
+
background: `
|
|
1581
|
+
radial-gradient(ellipse 80% 50% at ${20 + Math.sin(time) * 10}% ${30 + Math.cos(time * 0.5) * 10}%, ${c[0]}, transparent),
|
|
1582
|
+
radial-gradient(ellipse 60% 40% at ${60 + Math.cos(time * 0.7) * 15}% ${50 + Math.sin(time * 0.6) * 10}%, ${c[1]}, transparent),
|
|
1583
|
+
radial-gradient(ellipse 70% 45% at ${40 + Math.sin(time * 0.8) * 12}% ${70 + Math.cos(time * 0.4) * 8}%, ${c[2]}, transparent)
|
|
1584
|
+
`,
|
|
1585
|
+
filter: "blur(30px)",
|
|
1586
|
+
mixBlendMode: blend
|
|
1587
|
+
};
|
|
1588
|
+
case "sunset":
|
|
1589
|
+
return {
|
|
1590
|
+
background: `linear-gradient(180deg,
|
|
1591
|
+
${c[2]} 0%,
|
|
1592
|
+
${c[1]} ${40 + Math.sin(time) * 5}%,
|
|
1593
|
+
${c[0]} ${70 + Math.cos(time * 0.5) * 5}%,
|
|
1594
|
+
${c[0]}00 100%
|
|
1595
|
+
)`,
|
|
1596
|
+
mixBlendMode: blend
|
|
1597
|
+
};
|
|
1598
|
+
case "storm":
|
|
1599
|
+
return {
|
|
1600
|
+
background: `
|
|
1601
|
+
radial-gradient(ellipse 100% 60% at ${30 + Math.sin(time * 2) * 20}% ${20 + Math.cos(time) * 10}%, ${c[1]}, transparent),
|
|
1602
|
+
radial-gradient(ellipse 80% 50% at ${70 + Math.cos(time * 1.5) * 25}% ${40 + Math.sin(time * 1.2) * 15}%, ${c[2]}, transparent),
|
|
1603
|
+
linear-gradient(180deg, ${c[0]}, ${c[1]})
|
|
1604
|
+
`,
|
|
1605
|
+
filter: lightning ? `brightness(${1 + lightning * (stylePreset === "cinematic" ? 1.8 : 2.6)}) contrast(${1 + lightning * 0.35})` : void 0,
|
|
1606
|
+
mixBlendMode: blend
|
|
1607
|
+
};
|
|
1608
|
+
case "cosmic":
|
|
1609
|
+
return {
|
|
1610
|
+
background: `
|
|
1611
|
+
radial-gradient(ellipse at center, ${c[3] ?? c[0]} 0%, transparent 50%),
|
|
1612
|
+
radial-gradient(ellipse at ${30 + Math.sin(time * 0.5) * 20}% ${30 + Math.cos(time * 0.3) * 20}%, ${c[1]}, transparent 40%),
|
|
1613
|
+
radial-gradient(ellipse at ${70 + Math.cos(time * 0.4) * 15}% ${70 + Math.sin(time * 0.6) * 15}%, ${c[2]}, transparent 35%),
|
|
1614
|
+
linear-gradient(180deg, ${c[0]}, ${c[1]})
|
|
1615
|
+
`,
|
|
1616
|
+
mixBlendMode: blend
|
|
1617
|
+
};
|
|
1618
|
+
case "heat":
|
|
1619
|
+
return {
|
|
1620
|
+
background: `
|
|
1621
|
+
radial-gradient(ellipse at 50% 100%, ${c[0]}, transparent 60%),
|
|
1622
|
+
radial-gradient(ellipse at ${30 + Math.sin(time * 3) * 20}% 80%, ${c[1]}, transparent 40%),
|
|
1623
|
+
radial-gradient(ellipse at ${70 + Math.cos(time * 2.5) * 20}% 90%, ${c[2]}, transparent 45%)
|
|
1624
|
+
`,
|
|
1625
|
+
filter: `blur(${10 + Math.sin(time * 5) * 5}px)`,
|
|
1626
|
+
mixBlendMode: blend
|
|
1627
|
+
};
|
|
1628
|
+
case "underwater":
|
|
1629
|
+
return {
|
|
1630
|
+
background: `
|
|
1631
|
+
linear-gradient(180deg, ${c[0]} 0%, ${c[1]} 50%, ${c[2]} 100%),
|
|
1632
|
+
radial-gradient(ellipse at ${50 + Math.sin(time) * 30}% ${30 + Math.cos(time * 0.8) * 20}%, ${c[2]}40, transparent 40%)
|
|
1633
|
+
`,
|
|
1634
|
+
mixBlendMode: blend
|
|
1635
|
+
};
|
|
1636
|
+
default:
|
|
1637
|
+
return {};
|
|
1638
|
+
}
|
|
1639
|
+
}, [type, colors, intensity, complexity, direction, blend, time, lightning, stylePreset]);
|
|
1640
|
+
return /* @__PURE__ */ jsx5(
|
|
1641
|
+
"div",
|
|
1642
|
+
{
|
|
1643
|
+
className: cn("pointer-events-none absolute inset-0", className),
|
|
1644
|
+
style: { ...auroraStyle, ...style },
|
|
1645
|
+
"aria-hidden": "true",
|
|
1646
|
+
children: stylePreset === "cinematic" && /* @__PURE__ */ jsx5(
|
|
1647
|
+
"div",
|
|
1648
|
+
{
|
|
1649
|
+
className: "absolute inset-0",
|
|
1650
|
+
style: {
|
|
1651
|
+
backgroundImage: `url("${NOISE_DATA_URL}")`,
|
|
1652
|
+
backgroundRepeat: "repeat",
|
|
1653
|
+
backgroundSize: `${200 + complexity * 16}px ${200 + complexity * 16}px`,
|
|
1654
|
+
backgroundPosition: `${time * 120}px ${time * 80}px`,
|
|
1655
|
+
opacity: Math.min(0.1, 0.04 + intensity * 0.03),
|
|
1656
|
+
mixBlendMode: "overlay",
|
|
1657
|
+
filter: "blur(0.4px) contrast(1.1)"
|
|
1658
|
+
},
|
|
1659
|
+
"aria-hidden": "true"
|
|
1660
|
+
}
|
|
1661
|
+
)
|
|
1662
|
+
}
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
// src/environment/EnvironmentLayer/EnvironmentLayer.tsx
|
|
1667
|
+
import { useReducedMotion as useReducedMotion5 } from "framer-motion";
|
|
1668
|
+
|
|
1669
|
+
// src/environment/EnvironmentLayer/presets.ts
|
|
1670
|
+
var ENVIRONMENT_PRESETS = {
|
|
1671
|
+
"enchanted-forest": {
|
|
1672
|
+
weather: { type: "fireflies", intensity: 0.7 },
|
|
1673
|
+
fog: { type: "ground", intensity: 0.5, color: "#1a3a2a" },
|
|
1674
|
+
light: { type: "godrays", intensity: 0.6, color: "#d4f0c4" },
|
|
1675
|
+
sky: { type: "aurora", intensity: 0.4, colors: ["#00ff87", "#004d2f", "#001a0f"] }
|
|
1676
|
+
},
|
|
1677
|
+
"cyberpunk-city": {
|
|
1678
|
+
weather: { type: "rain", intensity: 0.8, wind: { x: -0.3, y: 0 } },
|
|
1679
|
+
fog: { type: "volumetric", intensity: 0.6, color: "#1a0033" },
|
|
1680
|
+
light: { type: "neon", intensity: 0.8, color: "#ff00ff", angle: 90 },
|
|
1681
|
+
sky: { type: "gradient", intensity: 0.7, colors: ["#0a001a", "#1a0033", "#330066"] }
|
|
1682
|
+
},
|
|
1683
|
+
"deep-space": {
|
|
1684
|
+
weather: { type: "spores", intensity: 0.3, color: "#ffffff" },
|
|
1685
|
+
fog: void 0,
|
|
1686
|
+
light: void 0,
|
|
1687
|
+
sky: { type: "nebula", intensity: 0.8, colors: ["#0d0221", "#1a0533", "#4b0082", "#9400d3"] }
|
|
1688
|
+
},
|
|
1689
|
+
underwater: {
|
|
1690
|
+
weather: { type: "spores", intensity: 0.4, color: "#40e0d0" },
|
|
1691
|
+
fog: { type: "mist", intensity: 0.5, color: "#006994" },
|
|
1692
|
+
light: { type: "caustics", intensity: 0.7, color: "#00d4ff" },
|
|
1693
|
+
sky: { type: "underwater", intensity: 0.9 }
|
|
1694
|
+
},
|
|
1695
|
+
apocalypse: {
|
|
1696
|
+
weather: { type: "ash", intensity: 0.8 },
|
|
1697
|
+
fog: { type: "volumetric", intensity: 0.7, color: "#2a1a0a" },
|
|
1698
|
+
light: void 0,
|
|
1699
|
+
sky: { type: "storm", intensity: 0.9, colors: ["#1c1c1c", "#363636", "#4a3020"] }
|
|
1700
|
+
},
|
|
1701
|
+
"cozy-night": {
|
|
1702
|
+
weather: { type: "fireflies", intensity: 0.4 },
|
|
1703
|
+
fog: { type: "mist", intensity: 0.3, color: "#0a0a20" },
|
|
1704
|
+
light: { type: "bloom", intensity: 0.5, color: "#fff5e6", source: { x: 0.5, y: 0.3 } },
|
|
1705
|
+
sky: { type: "stars", intensity: 0.6, colors: ["#ffffff", "#fffacd", "#e6e6fa"] }
|
|
1706
|
+
},
|
|
1707
|
+
haunted: {
|
|
1708
|
+
weather: { type: "embers", intensity: 0.3, color: "#4a90a4" },
|
|
1709
|
+
fog: { type: "ground", intensity: 0.8, color: "#1a1a2e" },
|
|
1710
|
+
light: { type: "godrays", intensity: 0.4, color: "#4a6fa5" },
|
|
1711
|
+
sky: { type: "storm", intensity: 0.7, colors: ["#0a0a0f", "#1a1a2e", "#2a2a4e"] }
|
|
1712
|
+
},
|
|
1713
|
+
synthwave: {
|
|
1714
|
+
weather: { type: "sparks", intensity: 0.4, color: "#ff00ff" },
|
|
1715
|
+
fog: void 0,
|
|
1716
|
+
light: { type: "scanner", intensity: 0.6, color: "#00ffff", source: { x: 0.5, y: 1 } },
|
|
1717
|
+
sky: { type: "gradient", intensity: 0.9, colors: ["#ff00ff", "#ff6ec7", "#0a001a"] }
|
|
1718
|
+
},
|
|
1719
|
+
"zen-garden": {
|
|
1720
|
+
weather: { type: "sakura", intensity: 0.5, wind: { x: 0.2, y: 0 } },
|
|
1721
|
+
fog: { type: "mist", intensity: 0.4, color: "#f0f0f0" },
|
|
1722
|
+
light: { type: "godrays", intensity: 0.5, color: "#ffe4b5" },
|
|
1723
|
+
sky: { type: "sunset", intensity: 0.7, colors: ["#ff6b35", "#f7c59f", "#2e294e"] }
|
|
1724
|
+
},
|
|
1725
|
+
volcanic: {
|
|
1726
|
+
weather: { type: "embers", intensity: 0.8 },
|
|
1727
|
+
fog: { type: "volumetric", intensity: 0.6, color: "#1a0a00" },
|
|
1728
|
+
light: void 0,
|
|
1729
|
+
sky: { type: "heat", intensity: 0.9, colors: ["#ff4500", "#ff6347", "#1a0500"] }
|
|
1730
|
+
},
|
|
1731
|
+
arctic: {
|
|
1732
|
+
weather: { type: "snow", intensity: 0.7, wind: { x: 0.2, y: 0 } },
|
|
1733
|
+
fog: { type: "mist", intensity: 0.5, color: "#e8f4ff" },
|
|
1734
|
+
light: { type: "rim", intensity: 0.4, color: "#00f0ff" },
|
|
1735
|
+
sky: { type: "aurora", intensity: 0.6, colors: ["#00ff87", "#60efff", "#0a1a2f"] }
|
|
1736
|
+
},
|
|
1737
|
+
noir: {
|
|
1738
|
+
weather: { type: "rain", intensity: 0.6, wind: { x: -0.2, y: 0 } },
|
|
1739
|
+
fog: { type: "depth", intensity: 0.7, color: "#0a0a0f" },
|
|
1740
|
+
light: { type: "spotlight", intensity: 0.8, color: "#ffffff", source: { x: 0.3, y: 0 }, width: 0.4 },
|
|
1741
|
+
sky: { type: "clouds", intensity: 0.5, colors: ["#1a1a1a", "#2a2a2a", "#0a0a0a"] }
|
|
1742
|
+
},
|
|
1743
|
+
ethereal: {
|
|
1744
|
+
weather: { type: "spores", intensity: 0.5, color: "#da70d6" },
|
|
1745
|
+
fog: void 0,
|
|
1746
|
+
light: { type: "bloom", intensity: 0.6, color: "#e6e6fa", source: { x: 0.5, y: 0.5 } },
|
|
1747
|
+
sky: { type: "nebula", intensity: 0.7, colors: ["#4b0082", "#8a2be2", "#da70d6"] }
|
|
1748
|
+
},
|
|
1749
|
+
matrix: {
|
|
1750
|
+
weather: { type: "rain", intensity: 0.9, color: "#00ff00", wind: { x: 0, y: 0 } },
|
|
1751
|
+
fog: void 0,
|
|
1752
|
+
light: { type: "scanner", intensity: 0.4, color: "#00ff00", source: { x: 0.5, y: 0 } },
|
|
1753
|
+
sky: { type: "gradient", intensity: 0.8, colors: ["#001a00", "#003300", "#000a00"] }
|
|
1754
|
+
},
|
|
1755
|
+
rave: {
|
|
1756
|
+
weather: { type: "sparks", intensity: 0.6 },
|
|
1757
|
+
fog: void 0,
|
|
1758
|
+
light: { type: "laser", intensity: 0.8, color: "#ff0000", angle: 45 },
|
|
1759
|
+
sky: { type: "cosmic", intensity: 0.7, colors: ["#0d0221", "#ff00ff", "#00ffff", "#ff00ff"] }
|
|
1760
|
+
},
|
|
1761
|
+
"alien-world": {
|
|
1762
|
+
weather: { type: "spores", intensity: 0.6, color: "#7fff00" },
|
|
1763
|
+
fog: { type: "volumetric", intensity: 0.5, color: "#1a0033" },
|
|
1764
|
+
light: { type: "caustics", intensity: 0.5, color: "#7fff00" },
|
|
1765
|
+
sky: { type: "nebula", intensity: 0.8, colors: ["#1a0033", "#4b0082", "#7fff00"] }
|
|
1766
|
+
}
|
|
1767
|
+
};
|
|
1768
|
+
|
|
1769
|
+
// src/environment/EnvironmentLayer/EnvironmentLayer.tsx
|
|
1770
|
+
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
1771
|
+
function EnvironmentLayer({
|
|
1772
|
+
preset,
|
|
1773
|
+
enabled = true,
|
|
1774
|
+
intensity = 1,
|
|
1775
|
+
stylePreset,
|
|
1776
|
+
className,
|
|
1777
|
+
style,
|
|
1778
|
+
weatherOverride,
|
|
1779
|
+
fogOverride,
|
|
1780
|
+
lightOverride,
|
|
1781
|
+
skyOverride
|
|
1782
|
+
}) {
|
|
1783
|
+
const reducedMotion = useReducedMotion5();
|
|
1784
|
+
const perfConfig = getPerformanceConfig();
|
|
1785
|
+
const resolvedStylePreset = stylePreset ?? (perfConfig.useShaders ? "cinematic" : "ui");
|
|
1786
|
+
if (!enabled || reducedMotion || perfConfig.tier === "minimal") {
|
|
1787
|
+
return null;
|
|
1788
|
+
}
|
|
1789
|
+
const config = ENVIRONMENT_PRESETS[preset];
|
|
1790
|
+
if (!config) {
|
|
1791
|
+
console.warn(
|
|
1792
|
+
`EnvironmentLayer: Unknown preset "${preset}". Available presets: ${Object.keys(ENVIRONMENT_PRESETS).join(", ")}`
|
|
1793
|
+
);
|
|
1794
|
+
return null;
|
|
1795
|
+
}
|
|
1796
|
+
return /* @__PURE__ */ jsxs(
|
|
1797
|
+
"div",
|
|
1798
|
+
{
|
|
1799
|
+
className: cn("pointer-events-none absolute inset-0 overflow-hidden", className),
|
|
1800
|
+
style,
|
|
1801
|
+
"aria-hidden": "true",
|
|
1802
|
+
children: [
|
|
1803
|
+
config.sky && /* @__PURE__ */ jsx6(
|
|
1804
|
+
AuroraLayer,
|
|
1805
|
+
{
|
|
1806
|
+
enabled,
|
|
1807
|
+
...config.sky,
|
|
1808
|
+
stylePreset: resolvedStylePreset,
|
|
1809
|
+
intensity: (config.sky.intensity ?? 1) * intensity,
|
|
1810
|
+
...skyOverride
|
|
1811
|
+
}
|
|
1812
|
+
),
|
|
1813
|
+
config.fog && /* @__PURE__ */ jsx6(
|
|
1814
|
+
FogLayer,
|
|
1815
|
+
{
|
|
1816
|
+
enabled,
|
|
1817
|
+
...config.fog,
|
|
1818
|
+
stylePreset: resolvedStylePreset,
|
|
1819
|
+
intensity: (config.fog.intensity ?? 1) * intensity,
|
|
1820
|
+
...fogOverride
|
|
1821
|
+
}
|
|
1822
|
+
),
|
|
1823
|
+
config.light && /* @__PURE__ */ jsx6(
|
|
1824
|
+
VolumetricLight,
|
|
1825
|
+
{
|
|
1826
|
+
enabled,
|
|
1827
|
+
...config.light,
|
|
1828
|
+
stylePreset: resolvedStylePreset,
|
|
1829
|
+
intensity: (config.light.intensity ?? 1) * intensity,
|
|
1830
|
+
...lightOverride
|
|
1831
|
+
}
|
|
1832
|
+
),
|
|
1833
|
+
config.weather && /* @__PURE__ */ jsx6(
|
|
1834
|
+
WeatherLayer,
|
|
1835
|
+
{
|
|
1836
|
+
enabled,
|
|
1837
|
+
...config.weather,
|
|
1838
|
+
stylePreset: resolvedStylePreset,
|
|
1839
|
+
intensity: (config.weather.intensity ?? 1) * intensity,
|
|
1840
|
+
...weatherOverride
|
|
1841
|
+
}
|
|
1842
|
+
)
|
|
1843
|
+
]
|
|
1844
|
+
}
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
EnvironmentLayer.displayName = "EnvironmentLayer";
|
|
1848
|
+
|
|
1849
|
+
export {
|
|
1850
|
+
PERFORMANCE_PRESETS,
|
|
1851
|
+
detectPerformanceTier,
|
|
1852
|
+
getPerformanceConfig,
|
|
1853
|
+
AUTUMN_LEAF_COLOR_PRESETS,
|
|
1854
|
+
WeatherLayer,
|
|
1855
|
+
FogLayer,
|
|
1856
|
+
VolumetricLight,
|
|
1857
|
+
AuroraLayer,
|
|
1858
|
+
ENVIRONMENT_PRESETS,
|
|
1859
|
+
EnvironmentLayer
|
|
1860
|
+
};
|
|
1861
|
+
//# sourceMappingURL=chunk-HFSDW5IV.js.map
|