@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
@@ -0,0 +1,27 @@
1
+ import { AUTUMN_LEAF_COLOR_PRESETS } from "./leafPresets";
2
+ import type { AutumnLeafColorPreset } from "./leafPresets";
3
+ import type { WeatherType } from "./types";
4
+
5
+ export function resolveWeatherColors({
6
+ type,
7
+ configColors,
8
+ colors,
9
+ leafColorPreset,
10
+ }: {
11
+ type: WeatherType;
12
+ configColors: readonly string[];
13
+ colors?: readonly string[];
14
+ leafColorPreset?: AutumnLeafColorPreset;
15
+ }): readonly string[] {
16
+ if (colors && colors.length > 0) return colors;
17
+ if (type === "leaves" && leafColorPreset) {
18
+ return AUTUMN_LEAF_COLOR_PRESETS[leafColorPreset] ?? configColors;
19
+ }
20
+ return configColors;
21
+ }
22
+
23
+ export function colorsKey(colors: readonly string[] | undefined) {
24
+ if (!colors || colors.length === 0) return "";
25
+ return colors.join("|");
26
+ }
27
+
@@ -0,0 +1,4 @@
1
+ export { WeatherLayer } from './WeatherLayer';
2
+ export type { WeatherLayerProps, WeatherType } from './types';
3
+ export { AUTUMN_LEAF_COLOR_PRESETS } from "./leafPresets";
4
+ export type { AutumnLeafColorPreset } from "./leafPresets";
@@ -0,0 +1,12 @@
1
+ export const AUTUMN_LEAF_COLOR_PRESETS = {
2
+ "early-autumn": ["#6b8f3f", "#a9b84a", "#d8c35a", "#e5b04a", "#c97a2c"],
3
+ "peak-autumn": ["#b63a2b", "#d86b2c", "#e5a93a", "#c56b2b", "#7a3e1b"],
4
+ "late-autumn": ["#5a3d2b", "#7a563e", "#9a6f4b", "#b68a5e", "#8a6b4f"],
5
+ golden: ["#f2d27a", "#e9c15c", "#d8a93b", "#b9872a", "#e8b04a"],
6
+ rust: ["#a34a1f", "#c2602d", "#d6782f", "#e07b39", "#bc6c25"],
7
+ "maple-red": ["#7a1f1f", "#a02b2b", "#c0302b", "#d44a2c", "#e36a3a"],
8
+ muted: ["#d4a373", "#c08a5a", "#a66a3f", "#8c5a36", "#e3b07a"],
9
+ } as const;
10
+
11
+ export type AutumnLeafColorPreset = keyof typeof AUTUMN_LEAF_COLOR_PRESETS;
12
+
@@ -0,0 +1,227 @@
1
+ import type { Particle, WeatherConfig, WeatherType } from './types';
2
+ import { WEATHER_CONFIGS } from './types';
3
+ import type { EnvironmentStylePreset } from '../shared/types';
4
+
5
+ function smoothstep(edge0: number, edge1: number, x: number) {
6
+ const t = Math.max(0, Math.min(1, (x - edge0) / (edge1 - edge0)));
7
+ return t * t * (3 - 2 * t);
8
+ }
9
+
10
+ function getSpawnPosition(
11
+ bounds: { width: number; height: number },
12
+ velocity: { x: number; y: number }
13
+ ) {
14
+ const margin = 40;
15
+ if (Math.abs(velocity.x) > Math.abs(velocity.y)) {
16
+ return {
17
+ x: velocity.x > 0 ? -margin : bounds.width + margin,
18
+ y: Math.random() * bounds.height,
19
+ };
20
+ }
21
+
22
+ return {
23
+ x: Math.random() * bounds.width,
24
+ y: velocity.y > 0 ? -margin : bounds.height + margin,
25
+ };
26
+ }
27
+
28
+ function getLifeRangeMs(type: WeatherType): [number, number] {
29
+ switch (type) {
30
+ case 'rain':
31
+ return [900, 2200];
32
+ case 'snow':
33
+ return [3200, 7200];
34
+ case 'dust':
35
+ return [5000, 11000];
36
+ case 'leaves':
37
+ return [6000, 14000];
38
+ case 'embers':
39
+ return [2600, 6500];
40
+ case 'fireflies':
41
+ return [4500, 9000];
42
+ case 'ash':
43
+ return [4500, 10000];
44
+ case 'sakura':
45
+ return [6500, 15000];
46
+ case 'sparks':
47
+ return [450, 1200];
48
+ case 'spores':
49
+ return [5000, 12000];
50
+ default:
51
+ return [3000, 8000];
52
+ }
53
+ }
54
+
55
+ export function createParticle(
56
+ id: number,
57
+ type: WeatherType,
58
+ config: WeatherConfig,
59
+ wind: { x: number; y: number },
60
+ bounds: { width: number; height: number },
61
+ stylePreset: EnvironmentStylePreset = 'ui',
62
+ colorsOverride?: readonly string[]
63
+ ): Particle {
64
+ const [minSize, maxSize] = config.sizeRange;
65
+ const [minOpacity, maxOpacity] = config.opacityRange;
66
+ const [minSpeed, maxSpeed] = config.speedRange;
67
+
68
+ const speed = minSpeed + Math.random() * (maxSpeed - minSpeed);
69
+ let vx = wind.x * speed;
70
+ let vy = wind.y * speed;
71
+
72
+ const depth = stylePreset === 'cinematic' ? Math.sqrt(Math.random()) : 0;
73
+ const sizeScale = stylePreset === 'cinematic' ? 0.55 + (1 - depth) * 0.45 : 1;
74
+ const speedScale = stylePreset === 'cinematic' ? 0.6 + (1 - depth) * 0.4 : 1;
75
+ const opacityScale = stylePreset === 'cinematic' ? 0.45 + (1 - depth) * 0.55 : 1;
76
+
77
+ switch (config.motionStyle) {
78
+ case 'fall':
79
+ vy = speed;
80
+ vx = wind.x * 2 + (Math.random() - 0.5) * 0.5;
81
+ break;
82
+ case 'rise':
83
+ vy = -speed;
84
+ vx = wind.x + (Math.random() - 0.5) * 2;
85
+ break;
86
+ case 'drift': {
87
+ // Some particles should always "fall" even when drifting (snow, leaves, petals).
88
+ const allowUpdraft = type === 'dust' || type === 'fireflies';
89
+ const verticalSign = allowUpdraft ? (Math.random() > 0.5 ? 1 : -1) : 1;
90
+
91
+ const driftFallScale =
92
+ type === 'snow' ? 0.35 :
93
+ type === 'leaves' ? 0.55 :
94
+ type === 'sakura' ? 0.45 :
95
+ 0.3;
96
+
97
+ vy = speed * driftFallScale * verticalSign;
98
+ vx = wind.x + (Math.random() - 0.5) * 2;
99
+ break;
100
+ }
101
+ case 'erratic':
102
+ vy = (Math.random() - 0.3) * speed;
103
+ vx = (Math.random() - 0.5) * speed * 2;
104
+ break;
105
+ }
106
+
107
+ vx *= speedScale;
108
+ vy *= speedScale;
109
+
110
+ const [minLife, maxLife] = getLifeRangeMs(type);
111
+ const maxLifeMs = minLife + Math.random() * (maxLife - minLife);
112
+ const palette = colorsOverride && colorsOverride.length > 0 ? colorsOverride : config.colors;
113
+ const color = palette.length > 0 ? palette[Math.floor(Math.random() * palette.length)] : '#ffffff';
114
+ const baseOpacity = (minOpacity + Math.random() * (maxOpacity - minOpacity)) * opacityScale;
115
+ const baseSize = (minSize + Math.random() * (maxSize - minSize)) * sizeScale;
116
+
117
+ // Only some weather types should visibly twinkle.
118
+ const twinkleAmountBase =
119
+ type === 'fireflies' ? 0.55 :
120
+ type === 'sparks' ? 0.35 :
121
+ type === 'embers' ? 0.18 :
122
+ type === 'spores' ? 0.12 :
123
+ 0;
124
+
125
+ const twinkleAmount = stylePreset === 'cinematic' ? twinkleAmountBase * 0.65 : twinkleAmountBase;
126
+ const twinkleSpeed =
127
+ type === 'sparks' ? 0.03 :
128
+ type === 'fireflies' ? 0.008 :
129
+ type === 'embers' ? 0.01 :
130
+ type === 'spores' ? 0.007 :
131
+ 0;
132
+
133
+ const driftPhase = Math.random() * Math.PI * 2;
134
+ const twinklePhase = Math.random() * Math.PI * 2;
135
+
136
+ const velocity = { x: vx, y: vy };
137
+ const spawn = getSpawnPosition(bounds, velocity);
138
+
139
+ const rotation =
140
+ config.shape === 'streak'
141
+ ? (Math.atan2(velocity.y, velocity.x) * 180) / Math.PI - 90
142
+ : Math.random() * 360;
143
+
144
+ const rotationSpeed =
145
+ config.shape === 'leaf' || config.shape === 'petal'
146
+ ? (stylePreset === 'cinematic' ? 0.03 : 0.06) * (Math.random() > 0.5 ? 1 : -1)
147
+ : 0;
148
+
149
+ return {
150
+ id,
151
+ x: spawn.x,
152
+ y: spawn.y,
153
+ size: baseSize,
154
+ opacity: baseOpacity,
155
+ rotation,
156
+ rotationSpeed,
157
+ velocity,
158
+ life: 0,
159
+ maxLife: maxLifeMs,
160
+ color,
161
+ baseOpacity,
162
+ depth,
163
+ driftPhase,
164
+ twinklePhase,
165
+ twinkleSpeed,
166
+ twinkleAmount,
167
+ };
168
+ }
169
+
170
+ export function updateParticle(
171
+ particle: Particle,
172
+ config: WeatherConfig,
173
+ deltaTime: number
174
+ ): Particle {
175
+ const newLife = particle.life + deltaTime;
176
+ const lifeRatio = newLife / particle.maxLife;
177
+
178
+ // Fade in/out
179
+ const fadeIn = smoothstep(0, 0.08, lifeRatio);
180
+ const fadeOut = 1 - smoothstep(0.82, 1, lifeRatio);
181
+ let opacity = particle.baseOpacity * fadeIn * fadeOut;
182
+
183
+ if (particle.twinkleAmount > 0 && particle.twinkleSpeed > 0) {
184
+ const tw = Math.sin(newLife * particle.twinkleSpeed + particle.twinklePhase);
185
+ opacity *= Math.max(0.15, 1 + tw * particle.twinkleAmount * 0.5);
186
+ }
187
+
188
+ // Add some drift variation
189
+ let vx = particle.velocity.x;
190
+ let vy = particle.velocity.y;
191
+
192
+ if (config.motionStyle === 'drift') {
193
+ vx += Math.sin(newLife * 0.002 + particle.driftPhase) * 0.18;
194
+ vy += Math.cos(newLife * 0.0016 + particle.driftPhase) * 0.07;
195
+ } else if (config.motionStyle === 'erratic') {
196
+ vx += Math.sin(newLife * 0.01 + particle.driftPhase) * 0.25;
197
+ vy += Math.cos(newLife * 0.012 + particle.driftPhase) * 0.18;
198
+ }
199
+
200
+ return {
201
+ ...particle,
202
+ x: particle.x + vx * deltaTime * 0.1,
203
+ y: particle.y + vy * deltaTime * 0.1,
204
+ rotation: particle.rotation + particle.rotationSpeed * deltaTime,
205
+ life: newLife,
206
+ opacity,
207
+ };
208
+ }
209
+
210
+ export function getParticleCount(
211
+ type: WeatherType,
212
+ intensity: number,
213
+ maxParticles: number,
214
+ stylePreset: EnvironmentStylePreset = 'ui'
215
+ ): number {
216
+ const config = WEATHER_CONFIGS[type];
217
+ const styleScale =
218
+ stylePreset === 'cinematic'
219
+ ? type === 'snow'
220
+ ? 1.8
221
+ : type === 'leaves'
222
+ ? 1.4
223
+ : 0.7
224
+ : 1;
225
+ const desired = Math.floor(config.baseCount * intensity * styleScale);
226
+ return Math.min(desired, maxParticles);
227
+ }
@@ -0,0 +1,140 @@
1
+ import type { BaseLayerProps } from '../shared/types';
2
+ import type { AutumnLeafColorPreset } from './leafPresets';
3
+
4
+ export type WeatherType =
5
+ | 'rain' | 'snow' | 'dust' | 'leaves' | 'embers'
6
+ | 'fireflies' | 'ash' | 'sakura' | 'sparks' | 'spores';
7
+
8
+ export interface WeatherLayerProps extends BaseLayerProps {
9
+ type: WeatherType;
10
+ wind?: { x: number; y: number };
11
+ color?: string;
12
+ colors?: readonly string[];
13
+ leafColorPreset?: AutumnLeafColorPreset;
14
+ opacity?: number;
15
+ blur?: boolean;
16
+ maxParticles?: number;
17
+ }
18
+
19
+ export interface Particle {
20
+ id: number;
21
+ x: number;
22
+ y: number;
23
+ size: number;
24
+ opacity: number;
25
+ rotation: number;
26
+ rotationSpeed: number;
27
+ velocity: { x: number; y: number };
28
+ life: number;
29
+ maxLife: number;
30
+ color: string;
31
+ baseOpacity: number;
32
+ depth: number; // 0 (near) → 1 (far)
33
+ driftPhase: number;
34
+ twinklePhase: number;
35
+ twinkleSpeed: number;
36
+ twinkleAmount: number;
37
+ }
38
+
39
+ export interface WeatherConfig {
40
+ baseCount: number;
41
+ sizeRange: [number, number];
42
+ opacityRange: [number, number];
43
+ speedRange: [number, number];
44
+ motionStyle: 'fall' | 'rise' | 'drift' | 'erratic';
45
+ shape: 'circle' | 'streak' | 'leaf' | 'petal' | 'glow';
46
+ colors: string[];
47
+ }
48
+
49
+ export const WEATHER_CONFIGS: Record<WeatherType, WeatherConfig> = {
50
+ rain: {
51
+ baseCount: 200,
52
+ sizeRange: [1, 3],
53
+ opacityRange: [0.4, 0.7],
54
+ speedRange: [15, 25],
55
+ motionStyle: 'fall',
56
+ shape: 'streak',
57
+ colors: ['#a0c4ff', '#bde0fe'],
58
+ },
59
+ snow: {
60
+ baseCount: 150,
61
+ sizeRange: [2, 6],
62
+ opacityRange: [0.6, 0.9],
63
+ speedRange: [1, 3],
64
+ motionStyle: 'drift',
65
+ shape: 'circle',
66
+ colors: ['#ffffff', '#e8f4ff'],
67
+ },
68
+ dust: {
69
+ baseCount: 80,
70
+ sizeRange: [1, 3],
71
+ opacityRange: [0.2, 0.4],
72
+ speedRange: [0.5, 2],
73
+ motionStyle: 'drift',
74
+ shape: 'circle',
75
+ colors: ['#d4a574', '#c9b896'],
76
+ },
77
+ leaves: {
78
+ baseCount: 30,
79
+ sizeRange: [6, 12],
80
+ opacityRange: [0.7, 1.0],
81
+ speedRange: [2, 5],
82
+ motionStyle: 'drift',
83
+ shape: 'leaf',
84
+ colors: ['#d4a373', '#e07b39', '#bc6c25'],
85
+ },
86
+ embers: {
87
+ baseCount: 60,
88
+ sizeRange: [2, 5],
89
+ opacityRange: [0.5, 0.8],
90
+ speedRange: [2, 5],
91
+ motionStyle: 'rise',
92
+ shape: 'glow',
93
+ colors: ['#ff6b35', '#f7931e', '#ffcc00'],
94
+ },
95
+ fireflies: {
96
+ baseCount: 40,
97
+ sizeRange: [3, 6],
98
+ opacityRange: [0.3, 1.0],
99
+ speedRange: [0.3, 1],
100
+ motionStyle: 'drift',
101
+ shape: 'glow',
102
+ colors: ['#90ee90', '#adff2f', '#7fff00'],
103
+ },
104
+ ash: {
105
+ baseCount: 100,
106
+ sizeRange: [2, 5],
107
+ opacityRange: [0.3, 0.6],
108
+ speedRange: [1, 3],
109
+ motionStyle: 'fall',
110
+ shape: 'circle',
111
+ colors: ['#4a4a4a', '#6b6b6b', '#8b8b8b'],
112
+ },
113
+ sakura: {
114
+ baseCount: 50,
115
+ sizeRange: [6, 12],
116
+ opacityRange: [0.6, 0.9],
117
+ speedRange: [1, 3],
118
+ motionStyle: 'drift',
119
+ shape: 'petal',
120
+ colors: ['#ffb7c5', '#ff69b4', '#ffc0cb'],
121
+ },
122
+ sparks: {
123
+ baseCount: 40,
124
+ sizeRange: [1, 3],
125
+ opacityRange: [0.7, 1.0],
126
+ speedRange: [8, 15],
127
+ motionStyle: 'erratic',
128
+ shape: 'glow',
129
+ colors: ['#ffd700', '#ffffff', '#fffacd'],
130
+ },
131
+ spores: {
132
+ baseCount: 60,
133
+ sizeRange: [4, 8],
134
+ opacityRange: [0.4, 0.7],
135
+ speedRange: [0.5, 1.5],
136
+ motionStyle: 'rise',
137
+ shape: 'glow',
138
+ colors: ['#9370db', '#8a2be2', '#da70d6'],
139
+ },
140
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Environment primitives
3
+ *
4
+ * Atmospheric and environmental effect components:
5
+ * - WeatherLayer: Particle-based weather effects (rain, snow, dust, etc.)
6
+ * - FogLayer: Fog and mist effects
7
+ * - VolumetricLight: Light shafts, godrays, and glow effects
8
+ * - AuroraLayer: Sky and aurora effects
9
+ * - EnvironmentLayer: Convenience wrapper combining all layers with presets
10
+ */
11
+
12
+ export * from "./WeatherLayer";
13
+ export * from "./FogLayer";
14
+ export * from "./VolumetricLight";
15
+ export * from "./AuroraLayer";
16
+ export * from "./EnvironmentLayer";
17
+ export * from "./shared";
@@ -0,0 +1,2 @@
1
+ export * from './types';
2
+ export * from './performance';
@@ -0,0 +1,10 @@
1
+ const NOISE_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
2
+ <filter id="n">
3
+ <feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="3" seed="2" stitchTiles="stitch" />
4
+ <feColorMatrix type="saturate" values="0" />
5
+ </filter>
6
+ <rect width="200" height="200" filter="url(#n)" opacity="0.55" />
7
+ </svg>`;
8
+
9
+ export const NOISE_DATA_URL = `data:image/svg+xml,${encodeURIComponent(NOISE_SVG)}`;
10
+
@@ -0,0 +1,33 @@
1
+ import { PERFORMANCE_PRESETS, type PerformanceTier, type PerformanceConfig } from './types';
2
+
3
+ export function detectPerformanceTier(): PerformanceTier {
4
+ if (typeof window === 'undefined') return 'medium';
5
+
6
+ // Check reduced motion first
7
+ if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
8
+ return 'minimal';
9
+ }
10
+
11
+ // Check device memory (Chrome only)
12
+ const nav = navigator as Navigator & { deviceMemory?: number };
13
+ if (nav.deviceMemory && nav.deviceMemory < 4) {
14
+ return 'low';
15
+ }
16
+
17
+ // Check hardware concurrency
18
+ if (navigator.hardwareConcurrency && navigator.hardwareConcurrency < 4) {
19
+ return 'low';
20
+ }
21
+
22
+ // Check if mobile
23
+ const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
24
+ if (isMobile) return 'low';
25
+
26
+ // Default to medium
27
+ return 'medium';
28
+ }
29
+
30
+ export function getPerformanceConfig(tier?: PerformanceTier): PerformanceConfig {
31
+ const detected = tier ?? detectPerformanceTier();
32
+ return PERFORMANCE_PRESETS[detected];
33
+ }
@@ -0,0 +1,26 @@
1
+ export type PerformanceTier = 'high' | 'medium' | 'low' | 'minimal';
2
+
3
+ export type EnvironmentStylePreset = 'cinematic' | 'ui';
4
+
5
+ export interface PerformanceConfig {
6
+ tier: PerformanceTier;
7
+ maxParticles: number;
8
+ useShaders: boolean;
9
+ targetFPS: number;
10
+ }
11
+
12
+ export const PERFORMANCE_PRESETS: Record<PerformanceTier, PerformanceConfig> = {
13
+ high: { tier: 'high', maxParticles: 2000, useShaders: true, targetFPS: 60 },
14
+ medium: { tier: 'medium', maxParticles: 800, useShaders: true, targetFPS: 60 },
15
+ low: { tier: 'low', maxParticles: 300, useShaders: false, targetFPS: 30 },
16
+ minimal: { tier: 'minimal', maxParticles: 0, useShaders: false, targetFPS: 0 },
17
+ };
18
+
19
+ export interface BaseLayerProps {
20
+ intensity?: number;
21
+ speed?: number;
22
+ stylePreset?: EnvironmentStylePreset;
23
+ enabled?: boolean;
24
+ className?: string;
25
+ style?: React.CSSProperties;
26
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './three/index.js';
2
+ export * from './environment/index.js';
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Vision types - copied from @backbay/glia/vision to avoid circular dependency.
3
+ * Canonical source: packages/glia/src/vision/types.ts
4
+ */
5
+
6
+ export interface VisionSize {
7
+ width: number;
8
+ height: number;
9
+ }
10
+
11
+ export interface VisionCameraPose {
12
+ position: [number, number, number];
13
+ target: [number, number, number];
14
+ up?: [number, number, number];
15
+ fov?: number;
16
+ projectionMatrix?: number[];
17
+ }
18
+
19
+ export interface VisionChannel<T = unknown> {
20
+ id: string;
21
+ kind: string;
22
+ data: T;
23
+ width?: number;
24
+ height?: number;
25
+ meta?: Record<string, unknown>;
26
+ }
27
+
28
+ export interface VisionTopologyNode {
29
+ id: string;
30
+ type: string;
31
+ label?: string;
32
+ position?: [number, number, number];
33
+ radius?: number;
34
+ bounds?: {
35
+ min: [number, number, number];
36
+ max: [number, number, number];
37
+ };
38
+ color?: string;
39
+ meta?: Record<string, unknown>;
40
+ }
41
+
42
+ export interface VisionTopologyEdge {
43
+ id?: string;
44
+ from: string;
45
+ to: string;
46
+ type?: string;
47
+ color?: string;
48
+ weight?: number;
49
+ meta?: Record<string, unknown>;
50
+ }
51
+
52
+ export interface VisionTopology {
53
+ id?: string;
54
+ source?: string;
55
+ updatedAt: number;
56
+ nodes: VisionTopologyNode[];
57
+ edges?: VisionTopologyEdge[];
58
+ meta?: Record<string, unknown>;
59
+ }
60
+
61
+ export interface VisionFrame {
62
+ id?: string;
63
+ source: string;
64
+ capturedAt: number;
65
+ size?: VisionSize;
66
+ camera?: VisionCameraPose;
67
+ channels?: VisionChannel[];
68
+ topology?: VisionTopology;
69
+ meta?: Record<string, unknown>;
70
+ }
71
+
72
+ export interface VisionCaptureOptions {
73
+ size?: VisionSize;
74
+ includeTopology?: boolean;
75
+ includeCamera?: boolean;
76
+ signal?: AbortSignal | null;
77
+ }
78
+
79
+ export interface VisionAdapter {
80
+ id: string;
81
+ label?: string;
82
+ capture(options?: VisionCaptureOptions): Promise<VisionFrame>;
83
+ getTopology?: () => VisionTopology | null;
84
+ }