solanapolis 1.0.0
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/README.md +518 -0
- package/bin/solanapolis.js +197 -0
- package/convex/_generated/api.d.ts +175 -0
- package/convex/_generated/api.js +23 -0
- package/convex/_generated/dataModel.d.ts +60 -0
- package/convex/_generated/server.d.ts +143 -0
- package/convex/_generated/server.js +93 -0
- package/convex/agent/conversation.ts +352 -0
- package/convex/agent/embeddingsCache.ts +110 -0
- package/convex/agent/memory.ts +450 -0
- package/convex/agent/schema.ts +53 -0
- package/convex/aiChat.ts +54 -0
- package/convex/aiTown/agent.ts +382 -0
- package/convex/aiTown/agentDescription.ts +27 -0
- package/convex/aiTown/agentInputs.ts +155 -0
- package/convex/aiTown/agentOperations.ts +178 -0
- package/convex/aiTown/conversation.ts +395 -0
- package/convex/aiTown/conversationMembership.ts +38 -0
- package/convex/aiTown/game.ts +371 -0
- package/convex/aiTown/ids.ts +32 -0
- package/convex/aiTown/inputHandler.ts +9 -0
- package/convex/aiTown/inputs.ts +25 -0
- package/convex/aiTown/insertInput.ts +20 -0
- package/convex/aiTown/location.ts +32 -0
- package/convex/aiTown/main.ts +154 -0
- package/convex/aiTown/movement.ts +189 -0
- package/convex/aiTown/player.ts +310 -0
- package/convex/aiTown/playerDescription.ts +35 -0
- package/convex/aiTown/schema.ts +79 -0
- package/convex/aiTown/world.ts +65 -0
- package/convex/aiTown/worldMap.ts +74 -0
- package/convex/chat.ts +79 -0
- package/convex/constants.ts +78 -0
- package/convex/convex.config.ts +6 -0
- package/convex/crons.ts +89 -0
- package/convex/engine/abstractGame.ts +199 -0
- package/convex/engine/historicalObject.ts +355 -0
- package/convex/engine/schema.ts +56 -0
- package/convex/http.ts +36 -0
- package/convex/init.ts +110 -0
- package/convex/messages.ts +53 -0
- package/convex/npcCarAgents.ts +415 -0
- package/convex/schema.ts +61 -0
- package/convex/streaming.ts +23 -0
- package/convex/testing.ts +202 -0
- package/convex/tsconfig.json +18 -0
- package/convex/util/FastIntegerCompression.ts +221 -0
- package/convex/util/assertNever.ts +4 -0
- package/convex/util/asyncMap.ts +20 -0
- package/convex/util/compression.ts +71 -0
- package/convex/util/geometry.ts +132 -0
- package/convex/util/isSimpleObject.ts +11 -0
- package/convex/util/llm.ts +724 -0
- package/convex/util/minheap.ts +38 -0
- package/convex/util/object.ts +22 -0
- package/convex/util/sleep.ts +3 -0
- package/convex/util/types.ts +33 -0
- package/convex/util/xxhash.ts +228 -0
- package/convex/world.ts +257 -0
- package/data/animations/campfire.json +45 -0
- package/data/animations/gentlesparkle.json +37 -0
- package/data/animations/gentlesplash.json +61 -0
- package/data/animations/gentlewaterfall.json +61 -0
- package/data/animations/windmill.json +78 -0
- package/data/characters.ts +121 -0
- package/data/convertMap.js +74 -0
- package/data/gentle.js +330 -0
- package/data/spritesheets/f1.ts +75 -0
- package/data/spritesheets/f2.ts +75 -0
- package/data/spritesheets/f3.ts +75 -0
- package/data/spritesheets/f4.ts +75 -0
- package/data/spritesheets/f5.ts +75 -0
- package/data/spritesheets/f6.ts +75 -0
- package/data/spritesheets/f7.ts +75 -0
- package/data/spritesheets/f8.ts +75 -0
- package/data/spritesheets/p1.ts +59 -0
- package/data/spritesheets/p2.ts +59 -0
- package/data/spritesheets/p3.ts +59 -0
- package/data/spritesheets/player.ts +59 -0
- package/data/spritesheets/types.ts +26 -0
- package/eslint.config.mjs +37 -0
- package/next.config.ts +7 -0
- package/package.json +85 -0
- package/postcss.config.mjs +7 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/helius-icon.svg +84 -0
- package/public/helius-logo.svg +85 -0
- package/public/next.svg +1 -0
- package/public/plane.glb +0 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/scripts/clear-city.ts +74 -0
- package/scripts/seed-wallets.ts +185 -0
- package/scripts/setup-webhook.ts +73 -0
- package/src/app/api/auth/callback/route.ts +6 -0
- package/src/app/api/auth/link-wallet/route.ts +6 -0
- package/src/app/api/auth/phantom/route.ts +6 -0
- package/src/app/api/broadcast-position/route.ts +59 -0
- package/src/app/api/leaderboard/route.ts +85 -0
- package/src/app/api/network-stats/route.ts +86 -0
- package/src/app/api/parcel-reward/route.ts +181 -0
- package/src/app/api/queue-status/route.ts +30 -0
- package/src/app/api/snapshots/route.ts +37 -0
- package/src/app/api/transactions/enhanced/route.ts +57 -0
- package/src/app/api/treasury/route.ts +83 -0
- package/src/app/api/wallet/[address]/balances/route.ts +124 -0
- package/src/app/api/wallet/[address]/identity/route.ts +32 -0
- package/src/app/api/wallet/[address]/route.ts +216 -0
- package/src/app/api/wallet/[address]/traded-tokens/route.ts +41 -0
- package/src/app/api/wallets/route.ts +68 -0
- package/src/app/api/webhooks/helius/route.ts +76 -0
- package/src/app/auth/callback/page.tsx +29 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +39 -0
- package/src/app/layout.tsx +43 -0
- package/src/app/page.tsx +16 -0
- package/src/components/AITownNPCs.tsx +206 -0
- package/src/components/ActivityFeed.tsx +189 -0
- package/src/components/AuthPanel.tsx +163 -0
- package/src/components/BeachScene.tsx +280 -0
- package/src/components/Building.tsx +138 -0
- package/src/components/CesiumFlight.tsx +1768 -0
- package/src/components/CesiumGlobe.tsx +616 -0
- package/src/components/CitizenCard.tsx +442 -0
- package/src/components/CitizenCardModal.tsx +153 -0
- package/src/components/CityGrid.tsx +313 -0
- package/src/components/CityLandmarks.tsx +427 -0
- package/src/components/CityScene.tsx +1289 -0
- package/src/components/CitySlotsBadge.tsx +68 -0
- package/src/components/CockpitHUD.tsx +460 -0
- package/src/components/ConvexWrapper.tsx +19 -0
- package/src/components/DubaiDistrict.tsx +630 -0
- package/src/components/FlightMiniMap.tsx +133 -0
- package/src/components/GameChat.tsx +383 -0
- package/src/components/GameHUD.tsx +393 -0
- package/src/components/Ground.tsx +14 -0
- package/src/components/HowItWorksModal.tsx +251 -0
- package/src/components/IngestionBanner.tsx +123 -0
- package/src/components/InstancedBuildings.tsx +316 -0
- package/src/components/InstancedCars.tsx +504 -0
- package/src/components/InstancedCityPlanes.tsx +259 -0
- package/src/components/InstancedHouses.tsx +246 -0
- package/src/components/InstancedLampPosts.tsx +201 -0
- package/src/components/InstancedResidentCars.tsx +357 -0
- package/src/components/InstancedRoadDashes.tsx +42 -0
- package/src/components/InstancedSkyscrapers.tsx +434 -0
- package/src/components/InstancedTrees.tsx +67 -0
- package/src/components/LeaderboardPanel.tsx +136 -0
- package/src/components/MultiplayerPlanes.tsx +128 -0
- package/src/components/NetworkStats.tsx +83 -0
- package/src/components/NewBuildingSpotlight.tsx +93 -0
- package/src/components/ParcelChallengeBanner.tsx +242 -0
- package/src/components/ParcelReward.tsx +191 -0
- package/src/components/Park.tsx +42 -0
- package/src/components/PhantomWrapper.tsx +22 -0
- package/src/components/PixelStreamViewer.tsx +335 -0
- package/src/components/PlaneMode.tsx +190 -0
- package/src/components/PlayerCar.tsx +211 -0
- package/src/components/PlayerPlane.tsx +255 -0
- package/src/components/ProjectileRenderer.tsx +249 -0
- package/src/components/QueueStatusBanner.tsx +86 -0
- package/src/components/RealPlayerTags.tsx +82 -0
- package/src/components/SceneLighting.tsx +382 -0
- package/src/components/SelectionBeam.tsx +59 -0
- package/src/components/SwapPanel.tsx +104 -0
- package/src/components/SwapParticles.tsx +237 -0
- package/src/components/TreasureGate.tsx +505 -0
- package/src/components/WalletPanel.tsx +421 -0
- package/src/components/WalletSearch.tsx +244 -0
- package/src/components/WelcomeOverlay.tsx +135 -0
- package/src/components/WindowTooltip.tsx +498 -0
- package/src/context/AuthContext.tsx +230 -0
- package/src/lib/bot-detection.ts +125 -0
- package/src/lib/building-math.ts +136 -0
- package/src/lib/building-shader.ts +253 -0
- package/src/lib/car-paths.ts +244 -0
- package/src/lib/car-system.ts +182 -0
- package/src/lib/city-constants.ts +29 -0
- package/src/lib/city-slots.ts +35 -0
- package/src/lib/city-zoning.ts +64 -0
- package/src/lib/collision-map.ts +147 -0
- package/src/lib/day-night.ts +252 -0
- package/src/lib/export-card.ts +28 -0
- package/src/lib/helius-webhook.ts +90 -0
- package/src/lib/helius.ts +74 -0
- package/src/lib/house-shader.ts +119 -0
- package/src/lib/mock-data.ts +56 -0
- package/src/lib/multiplayer-manager.ts +329 -0
- package/src/lib/plane-physics.ts +66 -0
- package/src/lib/player-car.ts +147 -0
- package/src/lib/player-plane.ts +200 -0
- package/src/lib/projectile-system.ts +272 -0
- package/src/lib/skyscraper-types.ts +52 -0
- package/src/lib/sound-engine.ts +464 -0
- package/src/lib/supabase-admin.ts +9 -0
- package/src/lib/supabase.ts +8 -0
- package/src/lib/swap-events.ts +70 -0
- package/src/middleware.ts +37 -0
- package/src/types/phantom.d.ts +16 -0
- package/src/types/wallet.ts +20 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useRef, useEffect, useState, useMemo } from "react";
|
|
4
|
+
import { useFrame, useThree } from "@react-three/fiber";
|
|
5
|
+
import { Stars, Sky } from "@react-three/drei";
|
|
6
|
+
import * as THREE from "three";
|
|
7
|
+
import { getDayNightState, CYCLE_DURATION } from "@/lib/day-night";
|
|
8
|
+
|
|
9
|
+
interface SceneLightingProps {
|
|
10
|
+
timeRef: React.MutableRefObject<number>;
|
|
11
|
+
autoModeRef: React.MutableRefObject<boolean>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Sub-component: updates Sky props at ~2fps (plenty for slow day/night cycle)
|
|
15
|
+
function SkyController({ timeRef }: { timeRef: React.MutableRefObject<number> }) {
|
|
16
|
+
const [params, setParams] = useState<{
|
|
17
|
+
sunPosition: [number, number, number];
|
|
18
|
+
turbidity: number;
|
|
19
|
+
rayleigh: number;
|
|
20
|
+
mieCoefficient: number;
|
|
21
|
+
mieDirectionalG: number;
|
|
22
|
+
}>({
|
|
23
|
+
sunPosition: [200, 60, 30],
|
|
24
|
+
turbidity: 4,
|
|
25
|
+
rayleigh: 2,
|
|
26
|
+
mieCoefficient: 0.005,
|
|
27
|
+
mieDirectionalG: 0.8,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const lastRef = useRef(0);
|
|
31
|
+
|
|
32
|
+
useFrame(() => {
|
|
33
|
+
const now = performance.now();
|
|
34
|
+
if (now - lastRef.current < 500) return; // 2fps sky updates
|
|
35
|
+
lastRef.current = now;
|
|
36
|
+
|
|
37
|
+
const s = getDayNightState(timeRef.current);
|
|
38
|
+
setParams({
|
|
39
|
+
sunPosition: [s.sunPosition.x, s.sunPosition.y, s.sunPosition.z],
|
|
40
|
+
turbidity: s.turbidity,
|
|
41
|
+
rayleigh: s.rayleigh,
|
|
42
|
+
mieCoefficient: s.mieCoefficient,
|
|
43
|
+
mieDirectionalG: s.mieDirectionalG,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<Sky
|
|
49
|
+
distance={4500}
|
|
50
|
+
sunPosition={params.sunPosition}
|
|
51
|
+
turbidity={params.turbidity}
|
|
52
|
+
rayleigh={params.rayleigh}
|
|
53
|
+
mieCoefficient={params.mieCoefficient}
|
|
54
|
+
mieDirectionalG={params.mieDirectionalG}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ── Shooting Stars — animated meteor streaks at night ──────────────────────────
|
|
60
|
+
const METEOR_COUNT = 3;
|
|
61
|
+
|
|
62
|
+
function ShootingStars({ timeRef }: { timeRef: React.MutableRefObject<number> }) {
|
|
63
|
+
const groupRef = useRef<THREE.Group>(null);
|
|
64
|
+
|
|
65
|
+
// Pre-create meteor line geometries
|
|
66
|
+
const meteors = useMemo(() => {
|
|
67
|
+
return Array.from({ length: METEOR_COUNT }, () => {
|
|
68
|
+
const geo = new THREE.BufferGeometry();
|
|
69
|
+
const positions = new Float32Array(6); // 2 points × 3
|
|
70
|
+
geo.setAttribute("position", new THREE.BufferAttribute(positions, 3));
|
|
71
|
+
const mat = new THREE.LineBasicMaterial({
|
|
72
|
+
color: new THREE.Color("#ffffff"),
|
|
73
|
+
transparent: true,
|
|
74
|
+
opacity: 0,
|
|
75
|
+
linewidth: 1,
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
geo,
|
|
79
|
+
mat,
|
|
80
|
+
// Lifecycle
|
|
81
|
+
startTime: Math.random() * 10,
|
|
82
|
+
duration: 1.5 + Math.random() * 2,
|
|
83
|
+
cooldown: 4 + Math.random() * 8,
|
|
84
|
+
elapsed: 0,
|
|
85
|
+
active: false,
|
|
86
|
+
// Trajectory
|
|
87
|
+
origin: new THREE.Vector3(),
|
|
88
|
+
direction: new THREE.Vector3(),
|
|
89
|
+
tailLength: 30 + Math.random() * 50,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
94
|
+
function resetMeteor(m: typeof meteors[0]) {
|
|
95
|
+
// Random position in upper hemisphere
|
|
96
|
+
const theta = Math.random() * Math.PI * 2;
|
|
97
|
+
const phi = Math.random() * 0.5 + 0.2; // above horizon
|
|
98
|
+
const r = 500 + Math.random() * 200;
|
|
99
|
+
m.origin.set(
|
|
100
|
+
Math.cos(theta) * Math.sin(phi) * r,
|
|
101
|
+
Math.cos(phi) * r,
|
|
102
|
+
Math.sin(theta) * Math.sin(phi) * r,
|
|
103
|
+
);
|
|
104
|
+
// Downward trajectory with random angle
|
|
105
|
+
m.direction.set(
|
|
106
|
+
(Math.random() - 0.5) * 0.6,
|
|
107
|
+
-0.8 - Math.random() * 0.2,
|
|
108
|
+
(Math.random() - 0.5) * 0.6,
|
|
109
|
+
).normalize();
|
|
110
|
+
m.tailLength = 30 + Math.random() * 60;
|
|
111
|
+
m.duration = 0.8 + Math.random() * 1.5;
|
|
112
|
+
m.cooldown = 3 + Math.random() * 12;
|
|
113
|
+
m.elapsed = 0;
|
|
114
|
+
m.active = true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
useFrame((_, delta) => {
|
|
118
|
+
const state = getDayNightState(timeRef.current);
|
|
119
|
+
const nightFactor = state.starsOpacity;
|
|
120
|
+
|
|
121
|
+
for (const m of meteors) {
|
|
122
|
+
if (!m.active) {
|
|
123
|
+
m.cooldown -= delta;
|
|
124
|
+
if (m.cooldown <= 0 && nightFactor > 0.3) {
|
|
125
|
+
resetMeteor(m);
|
|
126
|
+
}
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
m.elapsed += delta;
|
|
131
|
+
const t = m.elapsed / m.duration;
|
|
132
|
+
|
|
133
|
+
if (t >= 1) {
|
|
134
|
+
m.active = false;
|
|
135
|
+
m.mat.opacity = 0;
|
|
136
|
+
m.cooldown = 3 + Math.random() * 12;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Fade in/out: quick ramp up, slow fade
|
|
141
|
+
const fade = t < 0.15 ? t / 0.15 : 1 - Math.pow((t - 0.15) / 0.85, 2);
|
|
142
|
+
m.mat.opacity = fade * nightFactor * 0.85;
|
|
143
|
+
|
|
144
|
+
// Head position
|
|
145
|
+
const speed = 400;
|
|
146
|
+
const headX = m.origin.x + m.direction.x * speed * m.elapsed;
|
|
147
|
+
const headY = m.origin.y + m.direction.y * speed * m.elapsed;
|
|
148
|
+
const headZ = m.origin.z + m.direction.z * speed * m.elapsed;
|
|
149
|
+
|
|
150
|
+
// Tail position
|
|
151
|
+
const tailX = headX - m.direction.x * m.tailLength;
|
|
152
|
+
const tailY = headY - m.direction.y * m.tailLength;
|
|
153
|
+
const tailZ = headZ - m.direction.z * m.tailLength;
|
|
154
|
+
|
|
155
|
+
const positions = m.geo.attributes.position as THREE.BufferAttribute;
|
|
156
|
+
positions.setXYZ(0, tailX, tailY, tailZ);
|
|
157
|
+
positions.setXYZ(1, headX, headY, headZ);
|
|
158
|
+
positions.needsUpdate = true;
|
|
159
|
+
|
|
160
|
+
// Color: white-hot at head, blue-ish at tail based on speed
|
|
161
|
+
m.mat.color.setHSL(0.15, 0.1, 0.95);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Imperatively create THREE.Line objects (avoid JSX <line> SVG type clash)
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
const group = groupRef.current;
|
|
168
|
+
if (!group) return;
|
|
169
|
+
const lines: THREE.Line[] = [];
|
|
170
|
+
for (const m of meteors) {
|
|
171
|
+
const ln = new THREE.Line(m.geo, m.mat);
|
|
172
|
+
ln.frustumCulled = false;
|
|
173
|
+
group.add(ln);
|
|
174
|
+
lines.push(ln);
|
|
175
|
+
}
|
|
176
|
+
return () => {
|
|
177
|
+
for (const ln of lines) group.remove(ln);
|
|
178
|
+
};
|
|
179
|
+
}, [meteors]);
|
|
180
|
+
|
|
181
|
+
return <group ref={groupRef} />;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ── City Glow — ambient light haze over the city at night ─────────────────────
|
|
185
|
+
function CityGlow({ timeRef }: { timeRef: React.MutableRefObject<number> }) {
|
|
186
|
+
const lightRef = useRef<THREE.PointLight>(null);
|
|
187
|
+
const light2Ref = useRef<THREE.PointLight>(null);
|
|
188
|
+
|
|
189
|
+
useFrame(() => {
|
|
190
|
+
const state = getDayNightState(timeRef.current);
|
|
191
|
+
const nightFactor = state.starsOpacity;
|
|
192
|
+
|
|
193
|
+
// Warm city glow from below — visible at night
|
|
194
|
+
if (lightRef.current) {
|
|
195
|
+
lightRef.current.intensity = nightFactor * 1.8;
|
|
196
|
+
const pulse = Math.sin(performance.now() * 0.0008) * 0.1 + 0.9;
|
|
197
|
+
lightRef.current.intensity *= pulse;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Cool accent overhead — subtle purple city-reflection-on-clouds feel
|
|
201
|
+
if (light2Ref.current) {
|
|
202
|
+
light2Ref.current.intensity = nightFactor * 0.4;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<>
|
|
208
|
+
<pointLight
|
|
209
|
+
ref={lightRef}
|
|
210
|
+
position={[0, -10, 0]}
|
|
211
|
+
color="#ff8844"
|
|
212
|
+
intensity={0}
|
|
213
|
+
distance={800}
|
|
214
|
+
decay={1.5}
|
|
215
|
+
/>
|
|
216
|
+
<pointLight
|
|
217
|
+
ref={light2Ref}
|
|
218
|
+
position={[0, 250, 0]}
|
|
219
|
+
color="#6366f1"
|
|
220
|
+
intensity={0}
|
|
221
|
+
distance={1000}
|
|
222
|
+
decay={1.0}
|
|
223
|
+
/>
|
|
224
|
+
</>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export default function SceneLighting({ timeRef, autoModeRef }: SceneLightingProps) {
|
|
229
|
+
const { scene } = useThree();
|
|
230
|
+
const sceneRef = useRef(scene);
|
|
231
|
+
const fogRef = useRef<THREE.Fog | null>(null);
|
|
232
|
+
const ambientRef = useRef<THREE.AmbientLight>(null);
|
|
233
|
+
const sunRef = useRef<THREE.DirectionalLight>(null);
|
|
234
|
+
const hemiRef = useRef<THREE.HemisphereLight>(null);
|
|
235
|
+
const accentRef = useRef<THREE.PointLight>(null);
|
|
236
|
+
const moonRef = useRef<THREE.DirectionalLight>(null);
|
|
237
|
+
const starsGroupRef = useRef<THREE.Group>(null);
|
|
238
|
+
|
|
239
|
+
// Clear flat background — Sky component renders the sky sphere
|
|
240
|
+
useEffect(() => {
|
|
241
|
+
const sceneObj = sceneRef.current;
|
|
242
|
+
sceneObj.background = null;
|
|
243
|
+
|
|
244
|
+
const fog = new THREE.Fog("#0a0a14", 500, 1200);
|
|
245
|
+
sceneObj.fog = fog;
|
|
246
|
+
fogRef.current = fog;
|
|
247
|
+
|
|
248
|
+
return () => {
|
|
249
|
+
if (sceneObj.fog === fog) sceneObj.fog = null;
|
|
250
|
+
};
|
|
251
|
+
}, []);
|
|
252
|
+
|
|
253
|
+
useFrame((_, delta) => {
|
|
254
|
+
if (autoModeRef.current) {
|
|
255
|
+
timeRef.current = (timeRef.current + delta / CYCLE_DURATION) % 1;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const state = getDayNightState(timeRef.current);
|
|
259
|
+
|
|
260
|
+
// ── Fog ────────────────────────────────────────────────────────────────────
|
|
261
|
+
const fog = fogRef.current;
|
|
262
|
+
if (fog) {
|
|
263
|
+
fog.color.copy(state.fogColor);
|
|
264
|
+
fog.near = state.fogNear;
|
|
265
|
+
fog.far = state.fogFar;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ── Lights ────────────────────────────────────────────────────────────────
|
|
269
|
+
if (ambientRef.current) {
|
|
270
|
+
ambientRef.current.color.copy(state.ambientColor);
|
|
271
|
+
ambientRef.current.intensity = state.ambientIntensity;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (sunRef.current) {
|
|
275
|
+
sunRef.current.color.copy(state.sunColor);
|
|
276
|
+
sunRef.current.intensity = state.sunIntensity;
|
|
277
|
+
sunRef.current.position.copy(state.sunPosition);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (hemiRef.current) {
|
|
281
|
+
hemiRef.current.color.copy(state.hemiSkyColor);
|
|
282
|
+
hemiRef.current.groundColor.copy(state.hemiGroundColor);
|
|
283
|
+
hemiRef.current.intensity = state.hemiIntensity;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (accentRef.current) {
|
|
287
|
+
accentRef.current.color.copy(state.accentColor);
|
|
288
|
+
accentRef.current.intensity = state.accentIntensity;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Moon — subtle cool fill during night, invisible during day
|
|
292
|
+
if (moonRef.current) {
|
|
293
|
+
moonRef.current.intensity = state.starsOpacity * 0.35;
|
|
294
|
+
moonRef.current.position.set(
|
|
295
|
+
-state.sunPosition.x * 0.8,
|
|
296
|
+
Math.abs(state.sunPosition.y) * 0.4 + 80,
|
|
297
|
+
-state.sunPosition.z,
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ── Stars opacity ─────────────────────────────────────────────────────────
|
|
302
|
+
if (starsGroupRef.current) {
|
|
303
|
+
starsGroupRef.current.traverse((child) => {
|
|
304
|
+
if (child instanceof THREE.Points) {
|
|
305
|
+
const mat = child.material as THREE.PointsMaterial;
|
|
306
|
+
mat.transparent = true;
|
|
307
|
+
mat.opacity = state.starsOpacity;
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
return (
|
|
314
|
+
<>
|
|
315
|
+
{/* Physically-based Preetham atmospheric sky — slow-updated for performance */}
|
|
316
|
+
<SkyController timeRef={timeRef} />
|
|
317
|
+
|
|
318
|
+
{/* Primary sun — key light */}
|
|
319
|
+
<directionalLight
|
|
320
|
+
ref={sunRef}
|
|
321
|
+
position={[50, 100, 20]}
|
|
322
|
+
intensity={1.2}
|
|
323
|
+
color="#fffae8"
|
|
324
|
+
castShadow={false}
|
|
325
|
+
/>
|
|
326
|
+
|
|
327
|
+
{/* Ambient fill */}
|
|
328
|
+
<ambientLight ref={ambientRef} intensity={0.5} color="#dde8ff" />
|
|
329
|
+
|
|
330
|
+
{/* Hemisphere sky/ground fill */}
|
|
331
|
+
<hemisphereLight ref={hemiRef} args={["#80c4e8", "#6a4a28", 0.4]} />
|
|
332
|
+
|
|
333
|
+
{/* Atmospheric accent — indigo at night, warm at sunset */}
|
|
334
|
+
<pointLight
|
|
335
|
+
ref={accentRef}
|
|
336
|
+
position={[-30, 60, -30]}
|
|
337
|
+
intensity={0.6}
|
|
338
|
+
color="#6366f1"
|
|
339
|
+
distance={600}
|
|
340
|
+
decay={1.2}
|
|
341
|
+
/>
|
|
342
|
+
|
|
343
|
+
{/* Moon fill — cool blue at night */}
|
|
344
|
+
<directionalLight
|
|
345
|
+
ref={moonRef}
|
|
346
|
+
position={[-180, 120, -30]}
|
|
347
|
+
intensity={0}
|
|
348
|
+
color="#c8d8ff"
|
|
349
|
+
/>
|
|
350
|
+
|
|
351
|
+
{/* Stars — fade in at dusk, out at dawn */}
|
|
352
|
+
<group ref={starsGroupRef}>
|
|
353
|
+
{/* Primary star field — bright nearby stars */}
|
|
354
|
+
<Stars
|
|
355
|
+
radius={700}
|
|
356
|
+
depth={300}
|
|
357
|
+
count={6000}
|
|
358
|
+
factor={5.5}
|
|
359
|
+
saturation={0.35}
|
|
360
|
+
fade
|
|
361
|
+
speed={0.5}
|
|
362
|
+
/>
|
|
363
|
+
{/* Deep star backdrop — subtle dense star dust */}
|
|
364
|
+
<Stars
|
|
365
|
+
radius={1200}
|
|
366
|
+
depth={400}
|
|
367
|
+
count={3000}
|
|
368
|
+
factor={3}
|
|
369
|
+
saturation={0.15}
|
|
370
|
+
fade
|
|
371
|
+
speed={0.2}
|
|
372
|
+
/>
|
|
373
|
+
</group>
|
|
374
|
+
|
|
375
|
+
{/* Shooting stars — animated meteor streaks at night */}
|
|
376
|
+
<ShootingStars timeRef={timeRef} />
|
|
377
|
+
|
|
378
|
+
{/* City glow — warm haze from below at night */}
|
|
379
|
+
<CityGlow timeRef={timeRef} />
|
|
380
|
+
</>
|
|
381
|
+
);
|
|
382
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useRef } from "react";
|
|
4
|
+
import { useFrame } from "@react-three/fiber";
|
|
5
|
+
import * as THREE from "three";
|
|
6
|
+
|
|
7
|
+
interface SelectionBeamProps {
|
|
8
|
+
position: [number, number, number];
|
|
9
|
+
buildingHeight: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function SelectionBeam({ position, buildingHeight }: SelectionBeamProps) {
|
|
13
|
+
const beamRef = useRef<THREE.Mesh>(null);
|
|
14
|
+
const glowRef = useRef<THREE.PointLight>(null);
|
|
15
|
+
|
|
16
|
+
useFrame(({ clock }) => {
|
|
17
|
+
const t = clock.getElapsedTime();
|
|
18
|
+
|
|
19
|
+
// Pulsing beam opacity
|
|
20
|
+
if (beamRef.current) {
|
|
21
|
+
const mat = beamRef.current.material as THREE.MeshBasicMaterial;
|
|
22
|
+
mat.opacity = 0.25 + Math.sin(t * 2) * 0.1;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Pulsing glow light
|
|
26
|
+
if (glowRef.current) {
|
|
27
|
+
glowRef.current.intensity = 8 + Math.sin(t * 2) * 3;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const beamHeight = 80;
|
|
32
|
+
const beamY = buildingHeight + beamHeight / 2;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<group position={position}>
|
|
36
|
+
{/* Vertical beam */}
|
|
37
|
+
<mesh ref={beamRef} position={[0, beamY - position[1], 0]}>
|
|
38
|
+
<cylinderGeometry args={[0.15, 0.4, beamHeight, 8]} />
|
|
39
|
+
<meshBasicMaterial
|
|
40
|
+
color="#8b5cf6"
|
|
41
|
+
transparent
|
|
42
|
+
opacity={0.3}
|
|
43
|
+
side={THREE.DoubleSide}
|
|
44
|
+
depthWrite={false}
|
|
45
|
+
/>
|
|
46
|
+
</mesh>
|
|
47
|
+
|
|
48
|
+
{/* Point light glow at top of building */}
|
|
49
|
+
<pointLight
|
|
50
|
+
ref={glowRef}
|
|
51
|
+
position={[0, buildingHeight - position[1] + 2, 0]}
|
|
52
|
+
color="#8b5cf6"
|
|
53
|
+
intensity={8}
|
|
54
|
+
distance={30}
|
|
55
|
+
decay={2}
|
|
56
|
+
/>
|
|
57
|
+
</group>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
interface SwapInfo {
|
|
4
|
+
walletAddress: string;
|
|
5
|
+
signature: string;
|
|
6
|
+
tokenIn: string | null;
|
|
7
|
+
tokenOut: string | null;
|
|
8
|
+
amountSol: number | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface SwapPanelProps {
|
|
12
|
+
swap: SwapInfo | null;
|
|
13
|
+
onClose: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function shortenAddress(addr: string): string {
|
|
17
|
+
return `${addr.slice(0, 4)}...${addr.slice(-4)}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function formatAmount(n: number): string {
|
|
21
|
+
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(2)}M`;
|
|
22
|
+
if (n >= 1_000) return `${(n / 1_000).toFixed(2)}K`;
|
|
23
|
+
if (n >= 1) return n.toFixed(4);
|
|
24
|
+
if (n >= 0.0001) return n.toFixed(6);
|
|
25
|
+
return n.toExponential(2);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default function SwapPanel({ swap, onClose }: SwapPanelProps) {
|
|
29
|
+
if (!swap) return null;
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="w-full sm:w-80 bg-black/50 backdrop-blur-xl border border-white/[0.08] rounded-t-2xl sm:rounded-2xl p-5 text-white">
|
|
33
|
+
<div className="flex justify-between items-center mb-4">
|
|
34
|
+
<h3 className="text-xs font-semibold text-white/40 uppercase tracking-wider">
|
|
35
|
+
Swap
|
|
36
|
+
</h3>
|
|
37
|
+
<button
|
|
38
|
+
onClick={onClose}
|
|
39
|
+
className="w-6 h-6 flex items-center justify-center rounded-lg text-white/30 hover:text-white hover:bg-white/[0.06] transition-colors cursor-pointer"
|
|
40
|
+
>
|
|
41
|
+
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" stroke="currentColor" strokeWidth="1.5">
|
|
42
|
+
<path d="M1 1l8 8M9 1l-8 8" />
|
|
43
|
+
</svg>
|
|
44
|
+
</button>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
{/* Wallet address */}
|
|
48
|
+
<p className="font-mono text-sm text-purple-300/80 break-all mb-5">
|
|
49
|
+
{swap.walletAddress}
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
{/* Swap direction */}
|
|
53
|
+
<div className="mb-5 pb-4 border-b border-white/[0.06]">
|
|
54
|
+
<h4 className="text-xs font-semibold text-white/40 uppercase tracking-wider mb-3">
|
|
55
|
+
Direction
|
|
56
|
+
</h4>
|
|
57
|
+
<div className="flex items-center gap-3">
|
|
58
|
+
<div className="flex-1 min-w-0">
|
|
59
|
+
<div className="text-xs text-white/35 mb-1">Sold</div>
|
|
60
|
+
<div className="font-mono text-sm text-white/70 truncate">
|
|
61
|
+
{swap.tokenIn ? shortenAddress(swap.tokenIn) : "Unknown"}
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
<div className="text-white/25 text-lg shrink-0">→</div>
|
|
65
|
+
<div className="flex-1 min-w-0">
|
|
66
|
+
<div className="text-xs text-white/35 mb-1">Bought</div>
|
|
67
|
+
<div className="font-mono text-sm text-white/70 truncate">
|
|
68
|
+
{swap.tokenOut ? shortenAddress(swap.tokenOut) : "Unknown"}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
|
|
74
|
+
{/* Details */}
|
|
75
|
+
<div className="mb-5 pb-4 border-b border-white/[0.06] space-y-2.5 text-sm">
|
|
76
|
+
{swap.amountSol != null && (
|
|
77
|
+
<div className="flex justify-between">
|
|
78
|
+
<span className="text-white/45">Amount</span>
|
|
79
|
+
<span className="font-mono text-white/80">{formatAmount(swap.amountSol)} SOL</span>
|
|
80
|
+
</div>
|
|
81
|
+
)}
|
|
82
|
+
<div className="flex justify-between">
|
|
83
|
+
<span className="text-white/45">Signature</span>
|
|
84
|
+
<span className="font-mono text-white/60 truncate ml-4 max-w-[160px]">
|
|
85
|
+
{shortenAddress(swap.signature)}
|
|
86
|
+
</span>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
{/* Links */}
|
|
91
|
+
<div className="space-y-2">
|
|
92
|
+
<a
|
|
93
|
+
href={`https://orbmarkets.io/tx/${swap.signature}`}
|
|
94
|
+
target="_blank"
|
|
95
|
+
rel="noopener noreferrer"
|
|
96
|
+
className="flex items-center justify-between w-full px-3 py-2 bg-white/[0.04] hover:bg-white/[0.08] border border-white/[0.08] rounded-xl text-sm text-white/60 hover:text-white/80 transition-colors"
|
|
97
|
+
>
|
|
98
|
+
<span>View on Orb</span>
|
|
99
|
+
<span className="text-white/30">→</span>
|
|
100
|
+
</a>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
);
|
|
104
|
+
}
|