@backbay/glia-three 0.2.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/package.json +43 -0
  2. package/src/environment/AuroraLayer/AuroraLayer.stories.tsx +43 -0
  3. package/src/environment/AuroraLayer/AuroraLayer.tsx +200 -0
  4. package/src/environment/AuroraLayer/index.ts +2 -0
  5. package/src/environment/AuroraLayer/types.ts +30 -0
  6. package/src/environment/EnvironmentLayer/EnvironmentLayer.stories.tsx +262 -0
  7. package/src/environment/EnvironmentLayer/EnvironmentLayer.tsx +105 -0
  8. package/src/environment/EnvironmentLayer/index.ts +4 -0
  9. package/src/environment/EnvironmentLayer/presets.ts +128 -0
  10. package/src/environment/FogLayer/FogLayer.stories.tsx +83 -0
  11. package/src/environment/FogLayer/FogLayer.tsx +113 -0
  12. package/src/environment/FogLayer/index.ts +2 -0
  13. package/src/environment/FogLayer/types.ts +45 -0
  14. package/src/environment/VolumetricLight/VolumetricLight.stories.tsx +55 -0
  15. package/src/environment/VolumetricLight/VolumetricLight.tsx +188 -0
  16. package/src/environment/VolumetricLight/index.ts +2 -0
  17. package/src/environment/VolumetricLight/types.ts +33 -0
  18. package/src/environment/WeatherLayer/WeatherLayer.stories.tsx +348 -0
  19. package/src/environment/WeatherLayer/WeatherLayer.tsx +266 -0
  20. package/src/environment/WeatherLayer/cinematicCanvas.tsx +809 -0
  21. package/src/environment/WeatherLayer/colors.ts +27 -0
  22. package/src/environment/WeatherLayer/index.ts +4 -0
  23. package/src/environment/WeatherLayer/leafPresets.ts +12 -0
  24. package/src/environment/WeatherLayer/particles.ts +227 -0
  25. package/src/environment/WeatherLayer/types.ts +140 -0
  26. package/src/environment/index.ts +17 -0
  27. package/src/environment/shared/index.ts +2 -0
  28. package/src/environment/shared/noise.ts +10 -0
  29. package/src/environment/shared/performance.ts +33 -0
  30. package/src/environment/shared/types.ts +26 -0
  31. package/src/index.ts +2 -0
  32. package/src/lib/utils.ts +6 -0
  33. package/src/lib/vision-types.ts +84 -0
  34. package/src/three/AgentConsole/AgentConsole.stories.tsx +397 -0
  35. package/src/three/AgentConsole/AgentConsole.tsx +195 -0
  36. package/src/three/AgentConsole/ConsoleChat.tsx +237 -0
  37. package/src/three/AgentConsole/FocusConstellation.tsx +297 -0
  38. package/src/three/AgentConsole/GlyphAvatar.stories.tsx +110 -0
  39. package/src/three/AgentConsole/GlyphAvatar.tsx +117 -0
  40. package/src/three/AgentConsole/QuickActions.tsx +111 -0
  41. package/src/three/AgentConsole/index.ts +41 -0
  42. package/src/three/AgentConsole/types.ts +241 -0
  43. package/src/three/AmbientField/AmbientField.stories.tsx +290 -0
  44. package/src/three/AmbientField/AmbientField.tsx +307 -0
  45. package/src/three/AmbientField/BackbayFieldBus.ts +326 -0
  46. package/src/three/AmbientField/FieldProvider.tsx +83 -0
  47. package/src/three/AmbientField/index.ts +37 -0
  48. package/src/three/AmbientField/shaders/constellation.ts +384 -0
  49. package/src/three/AmbientField/types.ts +174 -0
  50. package/src/three/AttackGraph/AttackGraph.stories.tsx +144 -0
  51. package/src/three/AttackGraph/AttackGraph.tsx +325 -0
  52. package/src/three/AttackGraph/index.ts +19 -0
  53. package/src/three/AttackGraph/types.ts +97 -0
  54. package/src/three/AuditTrail/AuditTrail.stories.tsx +567 -0
  55. package/src/three/AuditTrail/AuditTrail.tsx +644 -0
  56. package/src/three/AuditTrail/index.ts +33 -0
  57. package/src/three/AuditTrail/types.ts +192 -0
  58. package/src/three/CrystallineOrganism/Breadcrumb.tsx +61 -0
  59. package/src/three/CrystallineOrganism/CrystallineOrganism.stories.tsx +509 -0
  60. package/src/three/CrystallineOrganism/CrystallineOrganism.tsx +273 -0
  61. package/src/three/CrystallineOrganism/LatticeEdge.tsx +69 -0
  62. package/src/three/CrystallineOrganism/OrganismLattice.tsx +159 -0
  63. package/src/three/CrystallineOrganism/OrganismParticles.tsx +148 -0
  64. package/src/three/CrystallineOrganism/OrganismShell.tsx +277 -0
  65. package/src/three/CrystallineOrganism/constants.ts +161 -0
  66. package/src/three/CrystallineOrganism/index.ts +17 -0
  67. package/src/three/CrystallineOrganism/layouts/hexGrid.ts +85 -0
  68. package/src/three/CrystallineOrganism/layouts/index.ts +1 -0
  69. package/src/three/CrystallineOrganism/types.ts +167 -0
  70. package/src/three/CrystallineOrganism/useOrganismEmotion.ts +84 -0
  71. package/src/three/FirewallBarrier/FirewallBarrier.stories.tsx +167 -0
  72. package/src/three/FirewallBarrier/FirewallBarrier.tsx +259 -0
  73. package/src/three/FirewallBarrier/index.ts +14 -0
  74. package/src/three/FirewallBarrier/types.ts +52 -0
  75. package/src/three/Glyph/GlyphObject.stories.tsx +577 -0
  76. package/src/three/Glyph/GlyphObject.tsx +422 -0
  77. package/src/three/Glyph/index.ts +10 -0
  78. package/src/three/Glyph/types.ts +36 -0
  79. package/src/three/Glyph/useGlyphController.ts +231 -0
  80. package/src/three/Glyph/useGlyphEmotion.ts +70 -0
  81. package/src/three/Graph3D/Graph3D.stories.tsx +269 -0
  82. package/src/three/Graph3D/Graph3D.tsx +248 -0
  83. package/src/three/Graph3D/GraphEdge.tsx +79 -0
  84. package/src/three/Graph3D/GraphNode.tsx +239 -0
  85. package/src/three/Graph3D/types.ts +66 -0
  86. package/src/three/Graph3D/utils.ts +204 -0
  87. package/src/three/IntelFeed/IntelFeed.stories.tsx +168 -0
  88. package/src/three/IntelFeed/IntelFeed.tsx +284 -0
  89. package/src/three/IntelFeed/index.ts +14 -0
  90. package/src/three/IntelFeed/types.ts +56 -0
  91. package/src/three/MetricsGalaxy/MetricsGalaxy.tsx +484 -0
  92. package/src/three/MetricsGalaxy/index.ts +6 -0
  93. package/src/three/MetricsGalaxy/types.ts +26 -0
  94. package/src/three/NetworkTopology/NetworkTopology.stories.tsx +184 -0
  95. package/src/three/NetworkTopology/NetworkTopology.tsx +421 -0
  96. package/src/three/NetworkTopology/index.ts +34 -0
  97. package/src/three/NetworkTopology/types.ts +128 -0
  98. package/src/three/ParticleField/ParticleField.stories.tsx +162 -0
  99. package/src/three/ParticleField/ParticleField.tsx +81 -0
  100. package/src/three/ParticleField/index.ts +1 -0
  101. package/src/three/PermissionMatrix/PermissionMatrix.stories.tsx +475 -0
  102. package/src/three/PermissionMatrix/PermissionMatrix.tsx +380 -0
  103. package/src/three/PermissionMatrix/index.ts +15 -0
  104. package/src/three/PermissionMatrix/types.ts +54 -0
  105. package/src/three/QuantumField/ConstellationField.tsx +238 -0
  106. package/src/three/QuantumField/FieldBus.ts +349 -0
  107. package/src/three/QuantumField/FieldLayer.tsx +430 -0
  108. package/src/three/QuantumField/FieldProvider.tsx +460 -0
  109. package/src/three/QuantumField/PcbField.tsx +406 -0
  110. package/src/three/QuantumField/QuantumField.stories.tsx +1155 -0
  111. package/src/three/QuantumField/TrailRTT.ts +212 -0
  112. package/src/three/QuantumField/WaterField.tsx +226 -0
  113. package/src/three/QuantumField/WaterSimRTT.ts +283 -0
  114. package/src/three/QuantumField/domMapping.ts +185 -0
  115. package/src/three/QuantumField/index.ts +110 -0
  116. package/src/three/QuantumField/styles/index.ts +9 -0
  117. package/src/three/QuantumField/styles/styleA.ts +526 -0
  118. package/src/three/QuantumField/styles/styleB.ts +1210 -0
  119. package/src/three/QuantumField/styles/styleC.ts +266 -0
  120. package/src/three/QuantumField/themes.ts +211 -0
  121. package/src/three/QuantumField/types.ts +380 -0
  122. package/src/three/SOCCommandCenter/SOCCommandCenter.stories.tsx +591 -0
  123. package/src/three/SOCCommandCenter/SOCCommandCenter.tsx +248 -0
  124. package/src/three/SOCCommandCenter/index.ts +26 -0
  125. package/src/three/SOCCommandCenter/types.ts +201 -0
  126. package/src/three/SecurityDashboard/SecurityDashboard.stories.tsx +508 -0
  127. package/src/three/SecurityDashboard/SecurityDashboard.tsx +507 -0
  128. package/src/three/SecurityDashboard/index.ts +37 -0
  129. package/src/three/SecurityDashboard/types.ts +143 -0
  130. package/src/three/SecurityShield/SecurityShield.stories.tsx +257 -0
  131. package/src/three/SecurityShield/SecurityShield.tsx +502 -0
  132. package/src/three/SecurityShield/index.ts +25 -0
  133. package/src/three/SecurityShield/types.ts +64 -0
  134. package/src/three/Sentinel/AvatarMode.tsx +578 -0
  135. package/src/three/Sentinel/AvatarRenderer.tsx +199 -0
  136. package/src/three/Sentinel/CameraPip.tsx +127 -0
  137. package/src/three/Sentinel/CardinalItem.tsx +83 -0
  138. package/src/three/Sentinel/CardinalMenu.tsx +370 -0
  139. package/src/three/Sentinel/DockedMiniOrb.tsx +146 -0
  140. package/src/three/Sentinel/RadialSubmenu.tsx +273 -0
  141. package/src/three/Sentinel/SentinelConversation.tsx +802 -0
  142. package/src/three/Sentinel/SentinelOrb.tsx +316 -0
  143. package/src/three/Sentinel/SentinelOverlay.tsx +146 -0
  144. package/src/three/Sentinel/SentinelProvider.tsx +145 -0
  145. package/src/three/Sentinel/SentinelTether.tsx +182 -0
  146. package/src/three/Sentinel/SigilPlaceholder.tsx +176 -0
  147. package/src/three/Sentinel/VerticalSubmenu.tsx +150 -0
  148. package/src/three/Sentinel/index.ts +145 -0
  149. package/src/three/Sentinel/sentinelStore.ts +196 -0
  150. package/src/three/Sentinel/types.ts +403 -0
  151. package/src/three/Sentinel/useCameraPermission.ts +153 -0
  152. package/src/three/Sentinel/useThrowPhysics.ts +220 -0
  153. package/src/three/SpatialWorkspace/CyntraWorkspace.tsx +84 -0
  154. package/src/three/SpatialWorkspace/JobCluster.tsx +281 -0
  155. package/src/three/SpatialWorkspace/NodeGraph.tsx +236 -0
  156. package/src/three/SpatialWorkspace/ReceiptOrbit.tsx +368 -0
  157. package/src/three/SpatialWorkspace/SpatialWorkspace.stories.tsx +547 -0
  158. package/src/three/SpatialWorkspace/SpatialWorkspace.tsx +428 -0
  159. package/src/three/SpatialWorkspace/TrustRings.tsx +228 -0
  160. package/src/three/SpatialWorkspace/adapters.ts +353 -0
  161. package/src/three/SpatialWorkspace/index.ts +85 -0
  162. package/src/three/SpatialWorkspace/nexusAdapter.ts +182 -0
  163. package/src/three/SpatialWorkspace/types.ts +389 -0
  164. package/src/three/ThreatRadar/ThreatRadar.stories.tsx +451 -0
  165. package/src/three/ThreatRadar/ThreatRadar.tsx +542 -0
  166. package/src/three/ThreatRadar/index.ts +8 -0
  167. package/src/three/ThreatRadar/types.ts +90 -0
  168. package/src/three/ThreeErrorBoundary/ThreeErrorBoundary.tsx +235 -0
  169. package/src/three/ThreeErrorBoundary/index.ts +5 -0
  170. package/src/three/index.ts +56 -0
  171. package/tsconfig.json +20 -0
  172. package/tsup.config.ts +21 -0
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@backbay/glia-three",
3
+ "version": "0.2.0-alpha.2",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
12
+ "./three": { "types": "./dist/three/index.d.ts", "import": "./dist/three/index.js" },
13
+ "./environment": { "types": "./dist/environment/index.d.ts", "import": "./dist/environment/index.js" }
14
+ },
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "typecheck": "tsc --noEmit"
18
+ },
19
+ "dependencies": {
20
+ "@backbay/contract": "0.1.0-alpha.1",
21
+ "@backbay/glia-agent": "workspace:*",
22
+ "clsx": "^2.1.1",
23
+ "tailwind-merge": "^3.3.0"
24
+ },
25
+ "peerDependencies": {
26
+ "react": "^18 || ^19",
27
+ "react-dom": "^18 || ^19",
28
+ "three": ">=0.150",
29
+ "@react-three/fiber": "^9",
30
+ "@react-three/drei": "^10"
31
+ },
32
+ "optionalDependencies": {
33
+ "framer-motion": "^12",
34
+ "styled-components": "^6"
35
+ },
36
+ "devDependencies": {
37
+ "typescript": "^5.8.3",
38
+ "styled-components": "^6.3.8",
39
+ "tsup": "^8.5.1",
40
+ "@types/three": "^0.170.0",
41
+ "@types/react": "^19.0.0"
42
+ }
43
+ }
@@ -0,0 +1,43 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { AuroraLayer } from "./AuroraLayer";
3
+ import type { AuroraType } from "./types";
4
+
5
+ const meta: Meta<typeof AuroraLayer> = {
6
+ title: "Primitives/Environment/AuroraLayer",
7
+ component: AuroraLayer,
8
+ parameters: { layout: "fullscreen", backgrounds: { default: "dark" } },
9
+ tags: ["autodocs"],
10
+ argTypes: {
11
+ type: { control: "select", options: ["aurora", "nebula", "gradient", "stars", "clouds", "sunset", "storm", "cosmic", "heat", "underwater"] },
12
+ intensity: { control: { type: "range", min: 0, max: 1, step: 0.1 } },
13
+ speed: { control: { type: "range", min: 0, max: 2, step: 0.1 } },
14
+ },
15
+ decorators: [(Story) => (<div style={{ width: "100%", height: "500px", position: "relative", background: "#0a0a0f" }}><Story /></div>)],
16
+ };
17
+
18
+ export default meta;
19
+ type Story = StoryObj<typeof AuroraLayer>;
20
+
21
+ export const Aurora: Story = { args: { type: "aurora", intensity: 0.8 } };
22
+ export const Nebula: Story = { args: { type: "nebula", intensity: 0.7 } };
23
+ export const Gradient: Story = { args: { type: "gradient", intensity: 1 } };
24
+ export const Stars: Story = { args: { type: "stars", intensity: 0.9, complexity: 5 } };
25
+ export const Clouds: Story = { args: { type: "clouds", intensity: 0.6 } };
26
+ export const Sunset: Story = { args: { type: "sunset", intensity: 0.9 } };
27
+ export const Storm: Story = { args: { type: "storm", intensity: 0.8 } };
28
+ export const Cosmic: Story = { args: { type: "cosmic", intensity: 0.7 } };
29
+ export const Heat: Story = { args: { type: "heat", intensity: 0.6 } };
30
+ export const Underwater: Story = { args: { type: "underwater", intensity: 0.8 } };
31
+
32
+ export const AllAuroraTypes: Story = {
33
+ render: () => (
34
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", height: "100%", gap: 1 }}>
35
+ {(["aurora", "nebula", "gradient", "stars", "clouds", "sunset", "storm", "cosmic", "heat", "underwater"] as AuroraType[]).map((type) => (
36
+ <div key={type} style={{ position: "relative", background: "#0a0a0f", minHeight: "200px" }}>
37
+ <AuroraLayer type={type} intensity={0.7} />
38
+ <div style={{ position: "absolute", bottom: 8, left: 8, color: "#fff", fontSize: 10, fontFamily: "monospace", opacity: 0.7 }}>{type}</div>
39
+ </div>
40
+ ))}
41
+ </div>
42
+ ),
43
+ };
@@ -0,0 +1,200 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { useReducedMotion } from "framer-motion";
5
+ import { cn } from "../../lib/utils";
6
+ import { getPerformanceConfig } from "../shared/performance";
7
+ import { NOISE_DATA_URL } from "../shared/noise";
8
+ import type { AuroraLayerProps } from "./types";
9
+ import { AURORA_CONFIGS } from "./types";
10
+
11
+ export function AuroraLayer({
12
+ type,
13
+ colors: propColors,
14
+ intensity = 1,
15
+ speed = 1,
16
+ stylePreset = 'ui',
17
+ complexity: propComplexity,
18
+ direction = 'vertical',
19
+ blend = 'normal',
20
+ enabled = true,
21
+ className,
22
+ style,
23
+ }: AuroraLayerProps) {
24
+ const reducedMotion = useReducedMotion();
25
+ const perfConfig = getPerformanceConfig();
26
+ const [time, setTime] = React.useState(0);
27
+ const lightningRef = React.useRef(0);
28
+
29
+ const config = AURORA_CONFIGS[type];
30
+ const colors = propColors ?? config.colors;
31
+ const complexity = propComplexity ?? config.complexity;
32
+ const shouldAnimate = !reducedMotion && perfConfig.tier !== 'minimal';
33
+
34
+ React.useEffect(() => {
35
+ if (!shouldAnimate || !enabled) return;
36
+ lightningRef.current = 0;
37
+ let frame: number;
38
+ const animate = () => {
39
+ if (type === 'storm') {
40
+ // Occasional lightning with short decay (avoid per-frame random strobe).
41
+ const chance = stylePreset === 'cinematic' ? 0.003 : 0.007;
42
+ if (Math.random() < chance) lightningRef.current = 1;
43
+ lightningRef.current *= stylePreset === 'cinematic' ? 0.92 : 0.88;
44
+ if (lightningRef.current < 0.02) lightningRef.current = 0;
45
+ }
46
+
47
+ setTime(prev => prev + config.animationSpeed * speed);
48
+ frame = requestAnimationFrame(animate);
49
+ };
50
+ frame = requestAnimationFrame(animate);
51
+ return () => cancelAnimationFrame(frame);
52
+ }, [shouldAnimate, enabled, type, stylePreset, config.animationSpeed, speed]);
53
+
54
+ if (!enabled) return null;
55
+
56
+ const lightning = type === 'storm' ? lightningRef.current : 0;
57
+
58
+ const auroraStyle = React.useMemo((): React.CSSProperties => {
59
+ const opacity = intensity;
60
+ const c = colors.map(color => `${color}${Math.round(opacity * 200).toString(16).padStart(2, '0')}`);
61
+
62
+ switch (type) {
63
+ case 'aurora':
64
+ return {
65
+ background: `
66
+ linear-gradient(${direction === 'horizontal' ? '90deg' : '180deg'},
67
+ ${c[0]} ${Math.sin(time) * 20 + 10}%,
68
+ ${c[1]} ${50 + Math.cos(time * 1.3) * 20}%,
69
+ ${c[2]} ${90 + Math.sin(time * 0.7) * 10}%
70
+ )
71
+ `,
72
+ filter: `blur(${40 + Math.sin(time * 2) * 10}px)`,
73
+ mixBlendMode: blend as React.CSSProperties['mixBlendMode'],
74
+ };
75
+
76
+ case 'nebula':
77
+ return {
78
+ background: `
79
+ radial-gradient(ellipse at ${30 + Math.sin(time) * 20}% ${40 + Math.cos(time * 0.8) * 20}%, ${c[0]}, transparent 50%),
80
+ radial-gradient(ellipse at ${70 + Math.cos(time * 1.2) * 15}% ${60 + Math.sin(time * 0.9) * 15}%, ${c[1]}, transparent 40%),
81
+ radial-gradient(ellipse at ${50 + Math.sin(time * 0.7) * 25}% ${50 + Math.cos(time * 1.1) * 25}%, ${c[2]}, transparent 45%)
82
+ `,
83
+ mixBlendMode: blend as React.CSSProperties['mixBlendMode'],
84
+ };
85
+
86
+ case 'gradient':
87
+ return {
88
+ background: `linear-gradient(${direction === 'horizontal' ? '90deg' : direction === 'radial' ? '135deg' : '180deg'}, ${c.join(', ')})`,
89
+ mixBlendMode: blend as React.CSSProperties['mixBlendMode'],
90
+ };
91
+
92
+ case 'stars':
93
+ // Generate pseudo-random star positions based on complexity
94
+ const starGradients = Array.from({ length: Math.min(complexity * 10, 50) }, (_, i) => {
95
+ const x = ((i * 17 + Math.sin(i) * 30) % 100);
96
+ const y = ((i * 23 + Math.cos(i) * 40) % 100);
97
+ const twinkle = Math.sin(time * (2 + i * 0.1)) * 0.5 + 0.5;
98
+ const size = 1 + (i % 3);
99
+ 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)`;
100
+ });
101
+ return {
102
+ background: starGradients.join(', '),
103
+ mixBlendMode: blend as React.CSSProperties['mixBlendMode'],
104
+ };
105
+
106
+ case 'clouds':
107
+ return {
108
+ background: `
109
+ radial-gradient(ellipse 80% 50% at ${20 + Math.sin(time) * 10}% ${30 + Math.cos(time * 0.5) * 10}%, ${c[0]}, transparent),
110
+ radial-gradient(ellipse 60% 40% at ${60 + Math.cos(time * 0.7) * 15}% ${50 + Math.sin(time * 0.6) * 10}%, ${c[1]}, transparent),
111
+ radial-gradient(ellipse 70% 45% at ${40 + Math.sin(time * 0.8) * 12}% ${70 + Math.cos(time * 0.4) * 8}%, ${c[2]}, transparent)
112
+ `,
113
+ filter: 'blur(30px)',
114
+ mixBlendMode: blend as React.CSSProperties['mixBlendMode'],
115
+ };
116
+
117
+ case 'sunset':
118
+ return {
119
+ background: `linear-gradient(180deg,
120
+ ${c[2]} 0%,
121
+ ${c[1]} ${40 + Math.sin(time) * 5}%,
122
+ ${c[0]} ${70 + Math.cos(time * 0.5) * 5}%,
123
+ ${c[0]}00 100%
124
+ )`,
125
+ mixBlendMode: blend as React.CSSProperties['mixBlendMode'],
126
+ };
127
+
128
+ case 'storm':
129
+ return {
130
+ background: `
131
+ radial-gradient(ellipse 100% 60% at ${30 + Math.sin(time * 2) * 20}% ${20 + Math.cos(time) * 10}%, ${c[1]}, transparent),
132
+ radial-gradient(ellipse 80% 50% at ${70 + Math.cos(time * 1.5) * 25}% ${40 + Math.sin(time * 1.2) * 15}%, ${c[2]}, transparent),
133
+ linear-gradient(180deg, ${c[0]}, ${c[1]})
134
+ `,
135
+ filter: lightning
136
+ ? `brightness(${1 + lightning * (stylePreset === 'cinematic' ? 1.8 : 2.6)}) contrast(${1 + lightning * 0.35})`
137
+ : undefined,
138
+ mixBlendMode: blend as React.CSSProperties['mixBlendMode'],
139
+ };
140
+
141
+ case 'cosmic':
142
+ return {
143
+ background: `
144
+ radial-gradient(ellipse at center, ${c[3] ?? c[0]} 0%, transparent 50%),
145
+ radial-gradient(ellipse at ${30 + Math.sin(time * 0.5) * 20}% ${30 + Math.cos(time * 0.3) * 20}%, ${c[1]}, transparent 40%),
146
+ radial-gradient(ellipse at ${70 + Math.cos(time * 0.4) * 15}% ${70 + Math.sin(time * 0.6) * 15}%, ${c[2]}, transparent 35%),
147
+ linear-gradient(180deg, ${c[0]}, ${c[1]})
148
+ `,
149
+ mixBlendMode: blend as React.CSSProperties['mixBlendMode'],
150
+ };
151
+
152
+ case 'heat':
153
+ return {
154
+ background: `
155
+ radial-gradient(ellipse at 50% 100%, ${c[0]}, transparent 60%),
156
+ radial-gradient(ellipse at ${30 + Math.sin(time * 3) * 20}% 80%, ${c[1]}, transparent 40%),
157
+ radial-gradient(ellipse at ${70 + Math.cos(time * 2.5) * 20}% 90%, ${c[2]}, transparent 45%)
158
+ `,
159
+ filter: `blur(${10 + Math.sin(time * 5) * 5}px)`,
160
+ mixBlendMode: blend as React.CSSProperties['mixBlendMode'],
161
+ };
162
+
163
+ case 'underwater':
164
+ return {
165
+ background: `
166
+ linear-gradient(180deg, ${c[0]} 0%, ${c[1]} 50%, ${c[2]} 100%),
167
+ radial-gradient(ellipse at ${50 + Math.sin(time) * 30}% ${30 + Math.cos(time * 0.8) * 20}%, ${c[2]}40, transparent 40%)
168
+ `,
169
+ mixBlendMode: blend as React.CSSProperties['mixBlendMode'],
170
+ };
171
+
172
+ default:
173
+ return {};
174
+ }
175
+ }, [type, colors, intensity, complexity, direction, blend, time, lightning, stylePreset]);
176
+
177
+ return (
178
+ <div
179
+ className={cn("pointer-events-none absolute inset-0", className)}
180
+ style={{ ...auroraStyle, ...style }}
181
+ aria-hidden="true"
182
+ >
183
+ {stylePreset === 'cinematic' && (
184
+ <div
185
+ className="absolute inset-0"
186
+ style={{
187
+ backgroundImage: `url("${NOISE_DATA_URL}")`,
188
+ backgroundRepeat: 'repeat',
189
+ backgroundSize: `${200 + complexity * 16}px ${200 + complexity * 16}px`,
190
+ backgroundPosition: `${time * 120}px ${time * 80}px`,
191
+ opacity: Math.min(0.1, 0.04 + intensity * 0.03),
192
+ mixBlendMode: 'overlay',
193
+ filter: 'blur(0.4px) contrast(1.1)',
194
+ }}
195
+ aria-hidden="true"
196
+ />
197
+ )}
198
+ </div>
199
+ );
200
+ }
@@ -0,0 +1,2 @@
1
+ export { AuroraLayer } from './AuroraLayer';
2
+ export type { AuroraLayerProps, AuroraType } from './types';
@@ -0,0 +1,30 @@
1
+ import type { BaseLayerProps } from '../shared/types';
2
+
3
+ export type AuroraType = 'aurora' | 'nebula' | 'gradient' | 'stars' | 'clouds' | 'sunset' | 'storm' | 'cosmic' | 'heat' | 'underwater';
4
+
5
+ export interface AuroraLayerProps extends BaseLayerProps {
6
+ type: AuroraType;
7
+ colors?: string[];
8
+ complexity?: number;
9
+ direction?: 'horizontal' | 'vertical' | 'radial';
10
+ blend?: 'normal' | 'screen' | 'overlay' | 'multiply';
11
+ }
12
+
13
+ export interface AuroraConfig {
14
+ colors: string[];
15
+ animationSpeed: number;
16
+ complexity: number;
17
+ }
18
+
19
+ export const AURORA_CONFIGS: Record<AuroraType, AuroraConfig> = {
20
+ aurora: { colors: ['#00ff87', '#60efff', '#ff00aa'], animationSpeed: 0.008, complexity: 3 },
21
+ nebula: { colors: ['#1a0533', '#4b0082', '#ff006e'], animationSpeed: 0.005, complexity: 4 },
22
+ gradient: { colors: ['#667eea', '#764ba2'], animationSpeed: 0.003, complexity: 1 },
23
+ stars: { colors: ['#ffffff', '#fffacd', '#e6e6fa'], animationSpeed: 0.01, complexity: 5 },
24
+ clouds: { colors: ['#f0f0f0', '#d0d0d0', '#e8e8e8'], animationSpeed: 0.004, complexity: 3 },
25
+ sunset: { colors: ['#ff6b35', '#f7c59f', '#2e294e'], animationSpeed: 0.002, complexity: 2 },
26
+ storm: { colors: ['#1c1c1c', '#363636', '#4a4a4a'], animationSpeed: 0.015, complexity: 4 },
27
+ cosmic: { colors: ['#0d0221', '#0f084b', '#26408b', '#a663cc'], animationSpeed: 0.003, complexity: 5 },
28
+ heat: { colors: ['#ff4500', '#ff6347', '#ffa500'], animationSpeed: 0.02, complexity: 2 },
29
+ underwater: { colors: ['#006994', '#00a8cc', '#40e0d0'], animationSpeed: 0.006, complexity: 3 },
30
+ };
@@ -0,0 +1,262 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { EnvironmentLayer } from "./EnvironmentLayer";
3
+ import { ENVIRONMENT_PRESETS, type EnvironmentPreset } from "./presets";
4
+
5
+ const PRESET_BACKGROUNDS: Record<EnvironmentPreset, string> = {
6
+ "enchanted-forest": "linear-gradient(to bottom, #0a1f0a, #051005)",
7
+ "cyberpunk-city": "linear-gradient(to bottom, #0a001a, #1a0033)",
8
+ "deep-space": "#000005",
9
+ underwater: "linear-gradient(to bottom, #001a20, #002030)",
10
+ apocalypse: "linear-gradient(to bottom, #1a1008, #0a0804)",
11
+ "cozy-night": "linear-gradient(to bottom, #0a0a15, #050510)",
12
+ haunted: "linear-gradient(to bottom, #0a0a0f, #05050a)",
13
+ synthwave: "linear-gradient(to bottom, #0a001a, #1a0033)",
14
+ "zen-garden": "linear-gradient(to bottom, #1a1510, #0f0a08)",
15
+ volcanic: "linear-gradient(to bottom, #1a0a00, #0a0500)",
16
+ arctic: "linear-gradient(to bottom, #0a1520, #051015)",
17
+ noir: "#0a0a0a",
18
+ ethereal: "linear-gradient(to bottom, #1a0030, #0a0015)",
19
+ matrix: "#000a00",
20
+ rave: "#050005",
21
+ "alien-world": "linear-gradient(to bottom, #0a0020, #050010)",
22
+ };
23
+
24
+ const meta: Meta<typeof EnvironmentLayer> = {
25
+ title: "Primitives/Environment/EnvironmentLayer",
26
+ component: EnvironmentLayer,
27
+ parameters: {
28
+ layout: "fullscreen",
29
+ backgrounds: { default: "dark" },
30
+ },
31
+ tags: ["autodocs"],
32
+ argTypes: {
33
+ preset: {
34
+ control: "select",
35
+ options: Object.keys(ENVIRONMENT_PRESETS),
36
+ },
37
+ intensity: {
38
+ control: { type: "range", min: 0, max: 1, step: 0.1 },
39
+ },
40
+ stylePreset: {
41
+ control: "select",
42
+ options: ["ui", "cinematic"],
43
+ },
44
+ enabled: {
45
+ control: "boolean",
46
+ },
47
+ },
48
+ decorators: [
49
+ (Story, context) => {
50
+ const preset = (context.args.preset as EnvironmentPreset) || "enchanted-forest";
51
+ return (
52
+ <div
53
+ style={{
54
+ width: "100%",
55
+ height: "500px",
56
+ position: "relative",
57
+ background: PRESET_BACKGROUNDS[preset],
58
+ }}
59
+ >
60
+ <Story />
61
+ </div>
62
+ );
63
+ },
64
+ ],
65
+ };
66
+
67
+ export default meta;
68
+ type Story = StoryObj<typeof EnvironmentLayer>;
69
+
70
+ // Individual preset stories
71
+ export const EnchantedForest: Story = {
72
+ args: {
73
+ preset: "enchanted-forest",
74
+ intensity: 1,
75
+ },
76
+ };
77
+
78
+ export const CyberpunkCity: Story = {
79
+ args: {
80
+ preset: "cyberpunk-city",
81
+ intensity: 1,
82
+ },
83
+ };
84
+
85
+ export const DeepSpace: Story = {
86
+ args: {
87
+ preset: "deep-space",
88
+ intensity: 1,
89
+ },
90
+ };
91
+
92
+ export const Underwater: Story = {
93
+ args: {
94
+ preset: "underwater",
95
+ intensity: 1,
96
+ },
97
+ };
98
+
99
+ export const Apocalypse: Story = {
100
+ args: {
101
+ preset: "apocalypse",
102
+ intensity: 1,
103
+ },
104
+ };
105
+
106
+ export const CozyNight: Story = {
107
+ args: {
108
+ preset: "cozy-night",
109
+ intensity: 1,
110
+ },
111
+ };
112
+
113
+ export const Haunted: Story = {
114
+ args: {
115
+ preset: "haunted",
116
+ intensity: 1,
117
+ },
118
+ };
119
+
120
+ export const Synthwave: Story = {
121
+ args: {
122
+ preset: "synthwave",
123
+ intensity: 1,
124
+ },
125
+ };
126
+
127
+ export const ZenGarden: Story = {
128
+ args: {
129
+ preset: "zen-garden",
130
+ intensity: 1,
131
+ },
132
+ };
133
+
134
+ export const Volcanic: Story = {
135
+ args: {
136
+ preset: "volcanic",
137
+ intensity: 1,
138
+ },
139
+ };
140
+
141
+ export const Arctic: Story = {
142
+ args: {
143
+ preset: "arctic",
144
+ intensity: 1,
145
+ },
146
+ };
147
+
148
+ export const Noir: Story = {
149
+ args: {
150
+ preset: "noir",
151
+ intensity: 1,
152
+ },
153
+ };
154
+
155
+ export const Ethereal: Story = {
156
+ args: {
157
+ preset: "ethereal",
158
+ intensity: 1,
159
+ },
160
+ };
161
+
162
+ export const Matrix: Story = {
163
+ args: {
164
+ preset: "matrix",
165
+ intensity: 1,
166
+ },
167
+ };
168
+
169
+ export const Rave: Story = {
170
+ args: {
171
+ preset: "rave",
172
+ intensity: 1,
173
+ },
174
+ };
175
+
176
+ export const AlienWorld: Story = {
177
+ args: {
178
+ preset: "alien-world",
179
+ intensity: 1,
180
+ },
181
+ };
182
+
183
+ // Intensity variants
184
+ export const LowIntensity: Story = {
185
+ args: {
186
+ preset: "enchanted-forest",
187
+ intensity: 0.3,
188
+ },
189
+ };
190
+
191
+ export const MediumIntensity: Story = {
192
+ args: {
193
+ preset: "cyberpunk-city",
194
+ intensity: 0.6,
195
+ },
196
+ };
197
+
198
+ // Override examples
199
+ export const WithWeatherOverride: Story = {
200
+ args: {
201
+ preset: "enchanted-forest",
202
+ intensity: 1,
203
+ weatherOverride: {
204
+ type: "spores",
205
+ color: "#ff00ff",
206
+ },
207
+ },
208
+ };
209
+
210
+ export const WithLightOverride: Story = {
211
+ args: {
212
+ preset: "cyberpunk-city",
213
+ intensity: 1,
214
+ lightOverride: {
215
+ color: "#00ffff",
216
+ type: "laser",
217
+ },
218
+ },
219
+ };
220
+
221
+ // Gallery view of all presets
222
+ export const AllPresets: Story = {
223
+ render: () => (
224
+ <div style={{ display: "grid", gridTemplateColumns: "repeat(4, 1fr)", height: "100%", gap: 1 }}>
225
+ {(Object.keys(ENVIRONMENT_PRESETS) as EnvironmentPreset[]).map((preset) => (
226
+ <div
227
+ key={preset}
228
+ style={{
229
+ position: "relative",
230
+ background: PRESET_BACKGROUNDS[preset],
231
+ minHeight: "200px",
232
+ }}
233
+ >
234
+ <EnvironmentLayer preset={preset} intensity={0.8} />
235
+ <div
236
+ style={{
237
+ position: "absolute",
238
+ bottom: 8,
239
+ left: 8,
240
+ color: "#fff",
241
+ fontSize: 10,
242
+ fontFamily: "monospace",
243
+ textTransform: "uppercase",
244
+ letterSpacing: "0.05em",
245
+ opacity: 0.8,
246
+ textShadow: "0 1px 3px rgba(0,0,0,0.8)",
247
+ }}
248
+ >
249
+ {preset}
250
+ </div>
251
+ </div>
252
+ ))}
253
+ </div>
254
+ ),
255
+ decorators: [
256
+ (Story) => (
257
+ <div style={{ width: "100%", height: "800px" }}>
258
+ <Story />
259
+ </div>
260
+ ),
261
+ ],
262
+ };
@@ -0,0 +1,105 @@
1
+ "use client";
2
+
3
+ import { useReducedMotion } from "framer-motion";
4
+ import { cn } from "../../lib/utils";
5
+ import { getPerformanceConfig } from "../shared/performance";
6
+ import type { EnvironmentStylePreset } from "../shared/types";
7
+ import { WeatherLayer } from "../WeatherLayer";
8
+ import type { WeatherLayerProps } from "../WeatherLayer";
9
+ import { FogLayer } from "../FogLayer";
10
+ import type { FogLayerProps } from "../FogLayer";
11
+ import { VolumetricLight } from "../VolumetricLight";
12
+ import type { VolumetricLightProps } from "../VolumetricLight";
13
+ import { AuroraLayer } from "../AuroraLayer";
14
+ import type { AuroraLayerProps } from "../AuroraLayer";
15
+ import { ENVIRONMENT_PRESETS, type EnvironmentPreset } from "./presets";
16
+
17
+ export interface EnvironmentLayerProps {
18
+ preset: EnvironmentPreset;
19
+ enabled?: boolean;
20
+ intensity?: number;
21
+ stylePreset?: EnvironmentStylePreset;
22
+ className?: string;
23
+ style?: React.CSSProperties;
24
+ weatherOverride?: Partial<Omit<WeatherLayerProps, "enabled" | "className" | "style">>;
25
+ fogOverride?: Partial<Omit<FogLayerProps, "enabled" | "className" | "style">>;
26
+ lightOverride?: Partial<Omit<VolumetricLightProps, "enabled" | "className" | "style">>;
27
+ skyOverride?: Partial<Omit<AuroraLayerProps, "enabled" | "className" | "style">>;
28
+ }
29
+
30
+ export function EnvironmentLayer({
31
+ preset,
32
+ enabled = true,
33
+ intensity = 1,
34
+ stylePreset,
35
+ className,
36
+ style,
37
+ weatherOverride,
38
+ fogOverride,
39
+ lightOverride,
40
+ skyOverride,
41
+ }: EnvironmentLayerProps) {
42
+ const reducedMotion = useReducedMotion();
43
+ const perfConfig = getPerformanceConfig();
44
+ const resolvedStylePreset: EnvironmentStylePreset = stylePreset ?? (perfConfig.useShaders ? "cinematic" : "ui");
45
+
46
+ if (!enabled || reducedMotion || perfConfig.tier === "minimal") {
47
+ return null;
48
+ }
49
+
50
+ const config = ENVIRONMENT_PRESETS[preset];
51
+
52
+ if (!config) {
53
+ console.warn(
54
+ `EnvironmentLayer: Unknown preset "${preset}". Available presets: ${Object.keys(ENVIRONMENT_PRESETS).join(", ")}`
55
+ );
56
+ return null;
57
+ }
58
+
59
+ return (
60
+ <div
61
+ className={cn("pointer-events-none absolute inset-0 overflow-hidden", className)}
62
+ style={style}
63
+ aria-hidden="true"
64
+ >
65
+ {config.sky && (
66
+ <AuroraLayer
67
+ enabled={enabled}
68
+ {...config.sky}
69
+ stylePreset={resolvedStylePreset}
70
+ intensity={(config.sky.intensity ?? 1) * intensity}
71
+ {...skyOverride}
72
+ />
73
+ )}
74
+ {config.fog && (
75
+ <FogLayer
76
+ enabled={enabled}
77
+ {...config.fog}
78
+ stylePreset={resolvedStylePreset}
79
+ intensity={(config.fog.intensity ?? 1) * intensity}
80
+ {...fogOverride}
81
+ />
82
+ )}
83
+ {config.light && (
84
+ <VolumetricLight
85
+ enabled={enabled}
86
+ {...config.light}
87
+ stylePreset={resolvedStylePreset}
88
+ intensity={(config.light.intensity ?? 1) * intensity}
89
+ {...lightOverride}
90
+ />
91
+ )}
92
+ {config.weather && (
93
+ <WeatherLayer
94
+ enabled={enabled}
95
+ {...config.weather}
96
+ stylePreset={resolvedStylePreset}
97
+ intensity={(config.weather.intensity ?? 1) * intensity}
98
+ {...weatherOverride}
99
+ />
100
+ )}
101
+ </div>
102
+ );
103
+ }
104
+
105
+ EnvironmentLayer.displayName = "EnvironmentLayer";
@@ -0,0 +1,4 @@
1
+ export { EnvironmentLayer } from "./EnvironmentLayer";
2
+ export type { EnvironmentLayerProps } from "./EnvironmentLayer";
3
+ export { ENVIRONMENT_PRESETS } from "./presets";
4
+ export type { EnvironmentPreset, EnvironmentConfig } from "./presets";