HI7RAI commited on
Commit
b42b938
·
verified ·
1 Parent(s): 3d927db

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +510 -744
index.html CHANGED
@@ -1,760 +1,526 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
 
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>NEXUS FX OS | AI Sci-Fi Studio</title>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
- <!-- Libraries -->
9
- <script src="https://cdn.tailwindcss.com"></script>
10
- <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"></script>
11
- <script src="https://cdn.jsdelivr.net/npm/mathjs@11.8.0/lib/browser/math.min.js"></script>
12
- <script src="https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"></script>
13
- <script src="https://cdnjs.cloudflare.com/ajax/libs/pako@2.1.0/dist/pako.min.js"></script>
14
- <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.2/dist/gsap.min.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- <!-- Three.js Import Map -->
17
- <script type="importmap">
18
- {
19
- "imports": {
20
- "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
21
- "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
22
- }
23
- }
24
- </script>
25
-
26
- <style>
27
- :root {
28
- --bg-void: #030306;
29
- --bg-panel: #0a0a0f;
30
- --bg-card: #12121a;
31
- --border: #1a1a2e;
32
- --accent-cyan: #00f0ff;
33
- --accent-magenta: #ff00aa;
34
- --accent-lime: #00ff66;
35
- --accent-red: #ff2244;
36
- --text: #e8e8f0;
37
- --text-dim: #6a6a7a;
38
- }
39
-
40
- * { box-sizing: border-box; margin: 0; padding: 0; outline: none; }
41
-
42
- body {
43
- font-family: 'JetBrains Mono', monospace;
44
- background: var(--bg-void);
45
- color: var(--text);
46
- overflow: hidden;
47
- height: 100vh;
48
- background-image:
49
- radial-gradient(circle at 10% 20%, rgba(0, 240, 255, 0.1) 0%, transparent 20%),
50
- radial-gradient(circle at 90% 80%, rgba(255, 0, 170, 0.1) 0%, transparent 20%);
51
- }
52
-
53
- .font-display { font-family: 'Orbitron', sans-serif; }
54
- .glitch-text {
55
- position: relative;
56
- }
57
- .glitch-text::before, .glitch-text::after {
58
- content: attr(data-text);
59
- position: absolute;
60
- top: 0; left: 0; width: 100%; height: 100%;
61
- background: var(--bg-void);
62
- }
63
- .glitch-text::before {
64
- left: 2px; text-shadow: -1px 0 var(--accent-magenta);
65
- clip: rect(24px, 550px, 90px, 0); animation: glitch-anim-2 3s infinite linear alternate-reverse;
66
- }
67
- .glitch-text::after {
68
- left: -2px; text-shadow: -1px 0 var(--accent-cyan);
69
- clip: rect(85px, 550px, 140px, 0); animation: glitch-anim 2.5s infinite linear alternate-reverse;
70
- }
71
-
72
- @keyframes glitch-anim {
73
- 0% { clip: rect(14px, 9999px, 127px, 0); }
74
- 20% { clip: rect(80px, 9999px, 95px, 0); }
75
- 40% { clip: rect(10px, 9999px, 50px, 0); }
76
- 60% { clip: rect(60px, 9999px, 120px, 0); }
77
- 80% { clip: rect(20px, 9999px, 80px, 0); }
78
- 100% { clip: rect(100px, 9999px, 110px, 0); }
79
- }
80
- @keyframes glitch-anim-2 {
81
- 0% { clip: rect(65px, 9999px, 100px, 0); }
82
- 20% { clip: rect(10px, 9999px, 40px, 0); }
83
- 40% { clip: rect(90px, 9999px, 130px, 0); }
84
- 60% { clip: rect(30px, 9999px, 80px, 0); }
85
- 80% { clip: rect(70px, 9999px, 110px, 0); }
86
- 100% { clip: rect(50px, 9999px, 90px, 0); }
87
- }
88
-
89
- /* Scrollbars */
90
- ::-webkit-scrollbar { width: 6px; height: 6px; }
91
- ::-webkit-scrollbar-track { background: var(--bg-void); }
92
- ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
93
- ::-webkit-scrollbar-thumb:hover { background: var(--accent-cyan); }
94
-
95
- /* Custom Inputs */
96
- input[type=range] {
97
- -webkit-appearance: none; width: 100%; background: transparent;
98
- }
99
- input[type=range]::-webkit-slider-thumb {
100
- -webkit-appearance: none; height: 14px; width: 14px; border-radius: 50%;
101
- background: var(--text); cursor: pointer; margin-top: -5px;
102
- box-shadow: 0 0 5px var(--accent-cyan);
103
- }
104
- input[type=range]::-webkit-slider-runnable-track {
105
- width: 100%; height: 4px; cursor: pointer; background: var(--border); border-radius: 2px;
106
- }
107
-
108
- .glass-panel {
109
- background: rgba(10, 10, 15, 0.85);
110
- backdrop-filter: blur(10px);
111
- border: 1px solid rgba(255,255,255,0.08);
112
- box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5);
113
- }
114
-
115
- .neon-box {
116
- border: 1px solid var(--border);
117
- transition: all 0.2s;
118
- }
119
- .neon-box:hover { box-shadow: 0 0 15px rgba(0, 240, 255, 0.2); border-color: var(--accent-cyan); }
120
- .neon-box.active { box-shadow: 0 0 20px var(--accent-cyan); border-color: var(--accent-cyan); }
121
-
122
- .fx-card {
123
- background: rgba(18, 18, 26, 0.9);
124
- border-left: 3px solid var(--border);
125
- transition: all 0.3s;
126
- }
127
- .fx-card:hover { background: rgba(25, 25, 35, 0.95); }
128
- .fx-card.active { border-left-color: var(--accent-lime); background: rgba(0, 50, 30, 0.2); box-shadow: inset 0 0 20px rgba(0, 255, 100, 0.1); }
129
-
130
- /* 3D Canvas Container */
131
- #viewport-3d {
132
- background: radial-gradient(circle at center, #1a1a24 0%, #000000 100%);
133
- }
134
- </style>
135
- <link rel="preconnect" href="https://fonts.googleapis.com">
136
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
137
- <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=JetBrains+Mono:wght@300;400;500&display=swap" rel="stylesheet">
138
  </head>
 
139
  <body class="flex flex-col h-screen">
140
 
141
- <!-- HEADER -->
142
- <header class="h-16 bg-[#050508] border-b border-[#1a1a2e] flex items-center justify-between px-6 z-50 shadow-[0_5px_20px_rgba(0,0,0,0.8)]">
143
- <div class="flex items-center gap-4">
144
- <div class="w-10 h-10 rounded-lg bg-gradient-to-br from-cyan-500 to-magenta-600 flex items-center justify-center shadow-[0_0_15px_rgba(0,240,255,0.5)]">
145
- <i class="fa-solid fa-cube text-white text-xl"></i>
146
- </div>
147
- <div>
148
- <h1 class="font-display text-xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-magenta-400 glitch-text" data-text="NEXUS FX OS">
149
- NEXUS FX OS
150
- </h1>
151
- <div class="text-xs text-cyan-500/70 font-mono tracking-widest">INTELLIGENT MEDIA ENGINE // V.2.0.24</div>
152
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  </div>
154
-
155
- <div class="flex items-center gap-3">
156
- <button id="btn-randomize" class="hidden md:flex items-center gap-2 px-4 py-2 rounded bg-[#1a1a2e] hover:bg-[#2a2a4e] text-cyan-400 border border-[#1a1a2e] transition-all hover:border-cyan-500/50 hover:shadow-[0_0_10px_rgba(0,240,255,0.3)] font-mono text-xs uppercase tracking-wider">
157
- <i class="fa-solid fa-shuffle"></i> Randomize System
158
- </button>
159
-
160
- <button id="btn-export" class="flex items-center gap-2 px-4 py-2 rounded bg-gradient-to-r from-cyan-600 to-blue-700 hover:from-cyan-500 hover:to-blue-600 text-white shadow-[0_0_15px_rgba(0,240,255,0.4)] transition-all hover:shadow-[0_0_25px_rgba(0,240,255,0.6)] font-mono text-xs uppercase tracking-wider font-bold">
161
- <i class="fa-solid fa-download"></i> Export Project
162
- </button>
163
  </div>
164
- </header>
165
 
166
- <!-- MAIN LAYOUT -->
167
- <main class="flex-1 flex overflow-hidden">
 
 
 
 
 
 
 
 
 
 
 
 
 
168
 
169
- <!-- LEFT PANEL: FX LIBRARY -->
170
- <aside class="w-80 bg-[#0a0a0f] border-r border-[#1a1a2e] flex flex-col overflow-hidden">
171
- <div class="p-4 border-b border-[#1a1a2e] bg-[#050508]">
172
- <div class="flex items-center justify-between mb-3">
173
- <h2 class="font-display text-sm text-cyan-400 tracking-widest">FX LIBRARY <span class="text-[10px] bg-cyan-900/30 px-2 py-0.5 rounded ml-2">50+ MODULES</span></h2>
174
- <div class="relative">
175
- <i class="fa-solid fa-search absolute left-3 top-2.5 text-cyan-500/50 text-xs"></i>
176
- <input type="text" id="search-fx" placeholder="Search shaders..." class="w-full pl-8 pr-3 py-1.5 bg-[#12121a] border border-[#1a1a2e] rounded text-[10px] text-cyan-200 focus:border-cyan-500 focus:outline-none transition-colors">
177
- </div>
178
- </div>
179
- <div class="flex gap-2 overflow-x-auto pb-2">
180
- <span class="px-2 py-1 rounded text-[9px] bg-[#1a1a2e] text-cyan-300 border border-[#1a1a2e] cursor-pointer hover:bg-cyan-900/30 hover:border-cyan-500/50 transition">ALL</span>
181
- <span class="px-2 py-1 rounded text-[9px] bg-[#1a1a2e] text-purple-300 border border-[#1a1a2e] cursor-pointer hover:bg-purple-900/30 hover:border-purple-500/50 transition">COLOR</span>
182
- <span class="px-2 py-1 rounded text-[9px] bg-[#1a1a2e] text-red-300 border border-[#1a1a2e] cursor-pointer hover:bg-red-900/30 hover:border-red-500/50 transition">GLITCH</span>
183
- <span class="px-2 py-1 rounded text-[9px] bg-[#1a1a2e] text-green-300 border border-[#1a1a2e] cursor-pointer hover:bg-green-900/30 hover:border-green-500/50 transition">GEOMETRY</span>
184
- <span class="px-2 py-1 rounded text-[9px] bg-[#1a1a2e] text-yellow-300 border border-[#1a1a2e] cursor-pointer hover:bg-yellow-900/30 hover:border-yellow-500/50 transition">AUDIO</span>
185
- </div>
186
- </div>
187
-
188
- <div id="fx-list" class="flex-1 overflow-y-auto p-3 space-y-2 custom-scrollbar">
189
- <!-- FX Items Injected Here -->
190
- </div>
191
-
192
- <div class="p-4 border-t border-[#1a1a2e] bg-[#050508]">
193
- <h3 class="text-[10px] uppercase text-gray-500 mb-2 font-bold tracking-wider">Media Input</h3>
194
- <div class="grid grid-cols-2 gap-2">
195
- <label class="cursor-pointer flex flex-col items-center justify-center p-3 rounded border border-[#1a1a2e] hover:border-cyan-500/50 hover:bg-cyan-900/10 transition group">
196
- <i class="fa-solid fa-video text-cyan-500 mb-1 group-hover:text-cyan-300 transition"></i>
197
- <span class="text-[10px] text-cyan-200 group-hover:text-white">Load Video</span>
198
- <input type="file" id="inp-video" accept="video/*" class="hidden">
199
- </label>
200
- <label class="cursor-pointer flex flex-col items-center justify-center p-3 rounded border border-[#1a1a2e] hover:border-magenta-500/50 hover:bg-magenta-900/10 transition group">
201
- <i class="fa-solid fa-music text-magenta-500 mb-1 group-hover:text-magenta-300 transition"></i>
202
- <span class="text-[10px] text-cyan-200 group-hover:text-white">Load Audio</span>
203
- <input type="file" id="inp-audio" accept="audio/*" class="hidden">
204
- </label>
205
- </div>
206
- </div>
207
- </aside>
208
-
209
- <!-- CENTER: VIEWPORT -->
210
- <section class="flex-1 relative flex flex-col items-center justify-center bg-[#030306] overflow-hidden">
211
-
212
- <!-- 3D Canvas -->
213
- <div id="viewport-3d" class="absolute inset-0 w-full h-full z-0">
214
- <canvas id="glCanvas" class="w-full h-full object-contain"></canvas>
215
- </div>
216
-
217
- <!-- Overlay Info -->
218
- <div class="absolute top-4 left-4 z-10 flex flex-col gap-2 pointer-events-none">
219
- <div class="glass-panel px-3 py-2 rounded border-l-4 border-cyan-500">
220
- <div class="text-[10px] text-cyan-300 uppercase font-bold">Current Engine</div>
221
- <div class="text-xs font-mono text-white" id="engine-status">WebGL 2.0 // Three.js Core</div>
222
- </div>
223
- <div class="glass-panel px-3 py-2 rounded border-l-4 border-magenta-500 w-48">
224
- <div class="text-[10px] text-magenta-300 uppercase font-bold">Audio Reactivity</div>
225
- <div class="flex items-center gap-2 mt-1">
226
- <div class="flex-1 h-1 bg-gray-800 rounded overflow-hidden">
227
- <div id="audio-bar" class="h-full bg-magenta-500 w-0 transition-all duration-75"></div>
228
- </div>
229
- <span id="audio-val" class="text-[9px] font-mono text-magenta-300">0%</span>
230
- </div>
231
- </div>
232
- </div>
233
-
234
- <!-- Bottom Controls -->
235
- <div class="absolute bottom-6 z-20 flex items-center gap-4 bg-[#00000080] backdrop-blur-md px-6 py-3 rounded-full border border-[#1a1a2e] shadow-[0_0_30px_rgba(0,0,0,0.8)]">
236
- <button id="btn-play" class="w-10 h-10 rounded-full bg-white text-black hover:bg-cyan-400 transition flex items-center justify-center">
237
- <i class="fa-solid fa-play text-sm ml-0.5"></i>
238
- </button>
239
- <div class="flex flex-col">
240
- <span class="text-[9px] text-gray-400 uppercase tracking-widest">Time</span>
241
- <span id="time-display" class="font-mono text-sm text-cyan-400">00:00:00</span>
242
- </div>
243
- <button id="btn-screenshot" class="px-3 py-1 rounded text-[10px] text-gray-300 hover:text-white hover:bg-white/10 transition border border-transparent hover:border-white/20">
244
- <i class="fa-solid fa-camera mr-1"></i> Snapshot
245
- </button>
246
- </div>
247
- </section>
248
-
249
- <!-- RIGHT PANEL: STACK & SETTINGS -->
250
- <aside class="w-80 bg-[#0a0a0f] border-l border-[#1a1a2e] flex flex-col overflow-hidden z-10">
251
-
252
- <!-- Active Stack -->
253
- <div class="p-4 border-b border-[#1a1a2e] bg-[#050508]">
254
- <div class="flex items-center justify-between">
255
- <h2 class="font-display text-sm text-cyan-400 tracking-widest">EFFECT STACK</h2>
256
- <span id="stack-count" class="text-[10px] bg-cyan-900/30 text-cyan-300 px-2 py-0.5 rounded border border-cyan-900/50">0 / 12</span>
257
- </div>
258
- <div class="text-[9px] text-gray-500 mt-1">Drag to reorder • Click to adjust</div>
259
  </div>
 
 
 
 
 
 
 
 
 
 
260
 
261
- <div id="stack-list" class="flex-1 overflow-y-auto p-4 space-y-3 bg-[#08080c]">
262
- <!-- Active Effects go here -->
263
- <div class="text-center py-10 text-gray-600 text-sm border-2 border-dashed border-gray-800 rounded">
264
- <i class="fa-solid fa-layer-group text-2xl mb-2 opacity-50"></i>
265
- No effects active. Drag from library.
266
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
 
269
- <!-- Quick Settings -->
270
- <div class="p-4 border-t border-[#1a1a2e] bg-[#050508] space-y-4">
271
- <div>
272
- <div class="flex justify-between mb-1">
273
- <label class="text-[10px] text-cyan-400 uppercase font-bold tracking-wider">Global Resolution</label>
274
- <span id="res-val" class="text-[9px] text-gray-400 font-mono">1080p</span>
275
- </div>
276
- <input type="range" id="global-res" min="360" max="2160" step="72" value="1080" class="w-full">
277
- </div>
278
-
279
- <div>
280
- <div class="flex justify-between mb-1">
281
- <label class="text-[10px] text-magenta-400 uppercase font-bold tracking-wider">Frame Rate (FPS)</label>
282
- <span id="fps-val" class="text-[9px] text-gray-400 font-mono">60.000</span>
283
- </div>
284
- <input type="range" id="global-fps" min="15" max="240" step="0.1" value="60" class="w-full">
285
- </div>
286
-
287
- <div class="pt-2 border-t border-[#1a1a2e]">
288
- <h3 class="text-[10px] text-gray-500 uppercase font-bold mb-2">APK Converter</h3>
289
- <div class="flex items-center justify-between text-[9px] text-gray-400 mb-2 bg-[#000] p-2 rounded border border-[#1a1a2e]">
290
- <span>Web to Android Builder</span>
291
- <i class="fa-solid fa-mobile-screen text-cyan-500"></i>
292
- </div>
293
- <div class="grid grid-cols-2 gap-2">
294
- <input type="text" id="app-name" placeholder="App Name" class="bg-[#12121a] border border-[#1a1a2e] rounded px-2 py-1 text-[9px] text-cyan-200 focus:border-cyan-500">
295
- <input type="text" id="app-id" placeholder="Bundle ID" class="bg-[#12121a] border border-[#1a1a2e] rounded px-2 py-1 text-[9px] text-cyan-200 focus:border-cyan-500" value="com.nexus.app">
296
- </div>
297
- <button id="btn-build-apk" class="w-full mt-2 py-1.5 rounded bg-[#1a1a2e] hover:bg-[#2a2a4e] text-[10px] text-cyan-300 border border-[#1a1a2e] transition flex items-center justify-center gap-2">
298
- <i class="fa-solid fa-code-commit"></i> Generate Android Project
299
- </button>
300
- </div>
301
- </div>
302
- </aside>
303
- </main>
304
-
305
- <!-- SCRIPTS -->
306
- <script type="module">
307
- import * as THREE from 'three';
308
- import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
309
- import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
310
- import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
311
-
312
- // --- 1. FX LIBRARY DEFINITION ---
313
- const FX_LIBRARY = [
314
- { id: 'bw', name: 'Monochrome', category: 'Color', desc: 'Converts video to black and white with contrast control.', glsl: `
315
- uniform float amount;
316
- void main() {
317
- vec4 color = texture2D(tDiffuse, vUv);
318
- float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
319
- gl_FragColor = vec4(vec3(gray * amount), color.a);
320
- }`
321
- },
322
- { id: 'rgb_shift', name: 'Chromatic Aberration', category: 'Glitch', desc: 'Separates RGB channels for sci-fi distortion.', glsl: `
323
- uniform float amount;
324
- uniform float uAudio;
325
- void main() {
326
- float offset = amount * 0.05 * (1.0 + uAudio);
327
- vec2 rUv = vUv + vec2(offset, 0.0);
328
- vec2 gUv = vUv;
329
- vec2 bUv = vUv - vec2(offset, 0.0);
330
- vec4 r = texture2D(tDiffuse, rUv);
331
- vec4 g = texture2D(tDiffuse, gUv);
332
- vec4 b = texture2D(tDiffuse, bUv);
333
- gl_FragColor = vec4(r.r, g.g, b.b, 1.0);
334
- }`
335
- },
336
- { id: 'noise', name: 'Digital Noise', category: 'Glitch', desc: 'Static noise overlay with audio sensitivity.', glsl: `
337
- uniform float amount;
338
- uniform float time;
339
- float rand(vec2 co) { return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); }
340
- void main() {
341
- vec4 color = texture2D(tDiffuse, vUv);
342
- float diff = (rand(vUv + time) - 0.5) * amount;
343
- gl_FragColor = vec4(color.rgb + diff, color.a);
344
- }`
345
- },
346
- { id: 'scanlines', name: 'CRT Scanlines', category: 'Visual', desc: 'Retro television scanline effect.', glsl: `
347
- uniform float amount;
348
- uniform vec2 resolution;
349
- void main() {
350
- vec4 color = texture2D(tDiffuse, vUv);
351
- float scanline = sin(vUv.y * resolution.y * 0.5) * 0.1 * amount;
352
- gl_FragColor = vec4(color.rgb - scanline, color.a);
353
- }`
354
- },
355
- { id: 'vignette', name: 'Neon Vignette', category: 'Art', desc: 'Darken edges with neon color bleed.', glsl: `
356
- uniform float amount;
357
- uniform vec2 resolution;
358
- void main() {
359
- vec4 color = texture2D(tDiffuse, vUv);
360
- vec2 uv = vUv - 0.5;
361
- float dist = length(uv);
362
- float aspect = resolution.x / resolution.y;
363
- uv.x *= aspect;
364
- float curve = smoothstep(0.8, 0.1, length(uv));
365
- gl_FragColor = vec4(color.rgb * curve, color.a);
366
- }`
367
- },
368
- { id: 'shockwave', name: 'Sonic Shockwave', category: 'Audio', desc: 'Expands based on bass frequencies.', glsl: `
369
- uniform float amount;
370
- uniform float uAudio;
371
- uniform vec2 resolution;
372
- void main() {
373
- vec2 uv = vUv - 0.5;
374
- float aspect = resolution.x / resolution.y;
375
- uv.x *= aspect;
376
- float radius = length(uv);
377
- float angle = atan(uv.y, uv.x);
378
-
379
- // Distort based on audio
380
- float distortion = sin(angle * 10.0 + radius * 20.0 * uAudio + uAudio * 5.0) * amount * 0.1;
381
-
382
- vec2 newUv = vUv + distortion * vec2(cos(angle), sin(angle));
383
- gl_FragColor = texture2D(tDiffuse, newUv);
384
- }`
385
- },
386
- { id: 'pixelate', name: 'Mosaic Glitch', category: 'Glitch', desc: 'Blocky pixelation based on mid frequencies.', glsl: `
387
- uniform float amount;
388
- uniform vec2 resolution;
389
- void main() {
390
- float d = 1.0 / (amount * 100.0 + 10.0);
391
- vec2 coord = d * floor(vUv / d);
392
- gl_FragColor = texture2D(tDiffuse, coord);
393
- }`
394
- },
395
- { id: 'invert', name: 'Cyber Invert', category: 'Color', desc: 'High contrast negative effect.', glsl: `
396
- uniform float amount;
397
- void main() {
398
- vec4 color = texture2D(tDiffuse, vUv);
399
- gl_FragColor = vec4(mix(color.rgb, 1.0 - color.rgb, amount), color.a);
400
- }`
401
- }
402
- ];
403
-
404
- // --- 2. APP CORE ---
405
- const NexusOS = {
406
- scene: null,
407
- camera: null,
408
- renderer: null,
409
- composer: null,
410
- video: null,
411
- videoTex: null,
412
- audioCtx: null,
413
- analyser: null,
414
- dataArray: null,
415
- stack: [],
416
- maxEffects: 12,
417
- isPlaying: false,
418
- animationId: null,
419
-
420
- init: () => {
421
- NexusOS.setup3D();
422
- NexusOS.setupUI();
423
- NexusOS.setupAudio();
424
- NexusOS.animate();
425
- },
426
-
427
- setup3D: () => {
428
- const container = document.getElementById('viewport-3d');
429
- const width = container.clientWidth;
430
- const height = container.clientHeight;
431
-
432
- // Three.js Setup
433
- NexusOS.scene = new THREE.Scene();
434
- NexusOS.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
435
-
436
- NexusOS.renderer = new THREE.WebGLRenderer({
437
- canvas: document.getElementById('glCanvas'),
438
- antialias: true,
439
- preserveDrawingBuffer: true
440
- });
441
- NexusOS.renderer.setSize(width, height);
442
- NexusOS.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
443
-
444
- // Composer Setup
445
- NexusOS.composer = new EffectComposer(NexusOS.renderer);
446
- const renderPass = new RenderPass(NexusOS.scene, NexusOS.camera);
447
- NexusOS.composer.addPass(renderPass);
448
-
449
- // Base Plane for Video
450
- const geometry = new THREE.PlaneGeometry(2, 2);
451
- const material = new THREE.MeshBasicMaterial({ color: 0x000000 });
452
- const quad = new THREE.Mesh(geometry, material);
453
- NexusOS.scene.add(quad);
454
- NexusOS.quad = quad;
455
-
456
- // Resize Handler
457
- window.addEventListener('resize', () => {
458
- const w = container.clientWidth;
459
- const h = container.clientHeight;
460
- NexusOS.renderer.setSize(w, h);
461
- NexusOS.composer.setSize(w, h);
462
- NexusOS.stack.forEach(pass => {
463
- if(pass.uniforms.resolution) pass.uniforms.resolution.value.set(w, h);
464
- });
465
- });
466
- },
467
-
468
- setupUI: () => {
469
- const list = document.getElementById('fx-list');
470
-
471
- // Group by Category
472
- const categories = [...new Set(FX_LIBRARY.map(s => s.category))];
473
-
474
- categories.forEach(cat => {
475
- const header = document.createElement('div');
476
- header.className = "text-[9px] text-gray-500 uppercase font-bold mt-4 mb-2 pl-2 tracking-widest border-b border-[#1a1a2e]";
477
- header.innerText = cat;
478
- list.appendChild(header);
479
-
480
- FX_LIBRARY.filter(s => s.category === cat).forEach(shader => {
481
- const el = document.createElement('div');
482
- el.className = "fx-card p-3 rounded transition-all group cursor-pointer border-l-4 border-[#1a1a2e]";
483
- el.innerHTML = `
484
- <div class="flex justify-between items-start mb-1">
485
- <span class="font-bold text-cyan-300 text-sm group-hover:text-white transition">${shader.name}</span>
486
- <i class="fa-solid fa-plus-circle text-[10px] text-gray-500 group-hover:text-cyan-400 transition"></i>
487
- </div>
488
- <div class="text-[9px] text-gray-400 line-clamp-2">${shader.desc}</div>
489
- `;
490
-
491
- // Drag Start
492
- el.draggable = true;
493
- el.addEventListener('dragstart', (e) => {
494
- e.dataTransfer.setData('text/plain', shader.id);
495
- e.dataTransfer.setData('name', shader.name);
496
- e.dataTransfer.setData('glsl', shader.glsl);
497
- });
498
-
499
- list.appendChild(el);
500
- });
501
- });
502
-
503
- // Drop Zone (Stack List)
504
- const stackList = document.getElementById('stack-list');
505
- stackList.addEventListener('dragover', (e) => e.preventDefault());
506
- stackList.addEventListener('drop', (e) => {
507
- e.preventDefault();
508
- const id = e.dataTransfer.getData('text/plain');
509
- const name = e.dataTransfer.getData('name');
510
- const glsl = e.dataTransfer.getData('glsl');
511
-
512
- if (NexusOS.stack.length < NexusOS.maxEffects) {
513
- NexusOS.addEffect(id, name, glsl);
514
- } else {
515
- alert("Effect Stack Full (Max 12)");
516
- }
517
- });
518
-
519
- // Search
520
- document.getElementById('search-fx').addEventListener('input', (e) => {
521
- const term = e.target.value.toLowerCase();
522
- const cards = document.querySelectorAll('.fx-card');
523
- cards.forEach(card => {
524
- const text = card.innerText.toLowerCase();
525
- card.style.display = text.includes(term) ? 'block' : 'none';
526
- });
527
- });
528
-
529
- // Global Settings
530
- document.getElementById('global-res').addEventListener('input', (e) => {
531
- document.getElementById('res-val').innerText = e.target.value + 'p';
532
- // Logic to resize canvas would go here
533
- });
534
- document.getElementById('global-fps').addEventListener('input', (e) => {
535
- document.getElementById('fps-val').innerText = parseFloat(e.target.value).toFixed(3);
536
- });
537
-
538
- // Play/Pause
539
- document.getElementById('btn-play').addEventListener('click', () => {
540
- if (NexusOS.video.paused) {
541
- NexusOS.video.play();
542
- document.getElementById('audio-src').play();
543
- NexusOS.isPlaying = true;
544
- document.getElementById('btn-play').innerHTML = '<i class="fa-solid fa-pause text-sm ml-0.5"></i>';
545
- } else {
546
- NexusOS.video.pause();
547
- document.getElementById('audio-src').pause();
548
- NexusOS.isPlaying = false;
549
- document.getElementById('btn-play').innerHTML = '<i class="fa-solid fa-play text-sm ml-0.5"></i>';
550
- }
551
- });
552
-
553
- // Randomize
554
- document.getElementById('btn-randomize').addEventListener('click', () => {
555
- // Clear stack
556
- NexusOS.stack.forEach(() => NexusOS.removeEffect(NexusOS.stack[0].uid));
557
-
558
- // Add random
559
- const count = Math.floor(Math.random() * 5) + 2;
560
- for(let i=0; i<count; i++) {
561
- const rnd = FX_LIBRARY[Math.floor(Math.random() * FX_LIBRARY.length)];
562
- NexusOS.addEffect(rnd.id, rnd.name, rnd.glsl);
563
- }
564
- });
565
-
566
- // Export
567
- document.getElementById('btn-export').addEventListener('click', () => {
568
- alert("Project exported to local storage as JSON config.");
569
- const config = {
570
- effects: NexusOS.stack.map(p => ({ id: p.def.id, amount: p.uniforms.amount.value })),
571
- resolution: document.getElementById('global-res').value,
572
- fps: document.getElementById('global-fps').value
573
- };
574
- const blob = new Blob([JSON.stringify(config)], {type: 'application/json'});
575
- const a = document.createElement('a');
576
- a.href = URL.createObjectURL(blob);
577
- a.download = 'nexus_project_config.json';
578
- a.click();
579
- });
580
-
581
- // APK Builder
582
- document.getElementById('btn-build-apk').addEventListener('click', () => {
583
- NexusOS.buildAndroidProject();
584
- });
585
- },
586
-
587
- setupAudio: () => {
588
- NexusOS.video = document.getElementById('video-src');
589
- if(!NexusOS.video) {
590
- // Create placeholder if not loaded
591
- NexusOS.video = document.createElement('video');
592
- NexusOS.video.id = 'video-src';
593
- NexusOS.video.playsInline = true;
594
- NexusOS.video.loop = true;
595
- NexusOS.video.src = "https://media.w3.org/2010/05/sintel/trailer_hd.mp4"; // Fallback
596
- document.body.appendChild(NexusOS.video);
597
- }
598
-
599
- // Load Audio
600
- const audioEl = document.getElementById('audio-src');
601
- if(!audioEl) {
602
- const audio = document.createElement('audio');
603
- audio.id = 'audio-src';
604
- audio.crossOrigin = "anonymous";
605
- document.body.appendChild(audio);
606
- }
607
-
608
- // File Inputs
609
- document.getElementById('inp-video').addEventListener('change', (e) => {
610
- const file = e.target.files[0];
611
- if (file) {
612
- const url = URL.createObjectURL(file);
613
- NexusOS.video.src = url;
614
- NexusOS.video.load();
615
- NexusOS.video.play().then(() => {
616
- NexusOS.isPlaying = true;
617
- document.getElementById('btn-play').innerHTML = '<i class="fa-solid fa-pause text-sm ml-0.5"></i>';
618
- });
619
- }
620
- });
621
-
622
- document.getElementById('inp-audio').addEventListener('change', (e) => {
623
- const file = e.target.files[0];
624
- if (file) {
625
- const url = URL.createObjectURL(file);
626
- const audioEl = document.getElementById('audio-src');
627
- audioEl.src = url;
628
-
629
- if (!NexusOS.audioCtx) {
630
- NexusOS.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
631
- const src = NexusOS.audioCtx.createMediaElementSource(audioEl);
632
- NexusOS.analyser = NexusOS.audioCtx.createAnalyser();
633
- NexusOS.analyser.fftSize = 256;
634
- src.connect(NexusOS.analyser);
635
- NexusOS.analyser.connect(NexusOS.audioCtx.destination);
636
- NexusOS.dataArray = new Uint8Array(NexusOS.analyser.frequencyBinCount);
637
- }
638
- audioEl.play();
639
- }
640
- });
641
- },
642
-
643
- addEffect: (id, name, glslCode) => {
644
- const def = FX_LIBRARY.find(s => s.id === id);
645
- if (!def) return;
646
-
647
- const myUniforms = {
648
- "tDiffuse": { value: null },
649
- "amount": { value: 0.5 },
650
- "time": { value: 0.0 },
651
- "resolution": { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
652
- "uAudio": { value: 0.0 }
653
- };
654
-
655
- const myShader = {
656
- uniforms: myUniforms,
657
- vertexShader: `
658
- varying vec2 vUv;
659
- void main() {
660
- vUv = uv;
661
- gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
662
- }`,
663
- fragmentShader: glslCode
664
- };
665
-
666
- const pass = new ShaderPass(myShader);
667
- pass.uid = Date.now() + Math.random();
668
- pass.def = def;
669
-
670
- // Insert at top
671
- NexusOS.composer.insertPass(pass, 1);
672
- NexusOS.stack.unshift(pass);
673
- NexusOS.renderStackUI();
674
- },
675
-
676
- removeEffect: (uid) => {
677
- const idx = NexusOS.stack.findIndex(p => p.uid === uid);
678
- if (idx > -1) {
679
- NexusOS.composer.removePass(NexusOS.stack[idx]);
680
- NexusOS.stack.splice(idx, 1);
681
- NexusOS.renderStackUI();
682
- }
683
- },
684
-
685
- renderStackUI: () => {
686
- const container = document.getElementById('stack-list');
687
- document.getElementById('stack-count').innerText = `${NexusOS.stack.length} / ${NexusOS.maxEffects}`;
688
-
689
- if (NexusOS.stack.length === 0) {
690
- container.innerHTML = '<div class="text-center py-10 text-gray-600 text-sm border-2 border-dashed border-gray-800 rounded"><i class="fa-solid fa-layer-group text-2xl mb-2 opacity-50"></i>No effects active. Drag from library.</div>';
691
- return;
692
- }
693
-
694
- container.innerHTML = '';
695
- NexusOS.stack.forEach((pass, i) => {
696
- const el = document.createElement('div');
697
- el.className = "neon-box active p-3 rounded mb-2";
698
- el.innerHTML = `
699
- <div class="flex justify-between items-center mb-2">
700
- <div class="flex items-center gap-2">
701
- <span class="text-[9px] bg-cyan-900 text-cyan-300 px-1 rounded">${i+1}</span>
702
- <span class="text-sm font-bold text-cyan-300">${pass.def.name}</span>
703
- </div>
704
- <button class="text-red-500 hover:text-white transition" onclick="NexusOS.removeEffect(${pass.uid})">
705
- <i class="fa-solid fa-trash"></i>
706
- </button>
707
- </div>
708
- <div class="flex items-center gap-2">
709
- <span class="text-[9px] text-gray-500">INTENSITY</span>
710
- <input type="range" class="w-full h-1 bg-gray-800 rounded appearance-none cursor-pointer"
711
- min="0" max="1" step="0.01" value="${pass.uniforms.amount.value}"
712
- oninput="NexusOS.updateEffect(${pass.uid}, this.value)">
713
- </div>
714
- `;
715
- container.appendChild(el);
716
- });
717
- },
718
-
719
- updateEffect: (uid, val) => {
720
- const pass = NexusOS.stack.find(p => p.uid === uid);
721
- if (pass) pass.uniforms.amount.value = parseFloat(val);
722
- },
723
-
724
- buildAndroidProject: () => {
725
- const appName = document.getElementById('app-name').value || "NexusApp";
726
- const appId = document.getElementById('app-id').value || "com.nexus.app";
727
-
728
- if(!appId.includes('.')) {
729
- alert("Invalid Bundle ID format. Use com.company.app");
730
- return;
731
- }
732
-
733
- const zip = new JSZip();
734
- const assets = {};
735
-
736
- // Generate a simple HTML file for the app
737
- const htmlContent = `
738
- <!DOCTYPE html>
739
- <html>
740
- <head>
741
- <title>${appName}</title>
742
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
743
- <style>
744
- body { margin:0; background:#000; overflow:hidden; }
745
- canvas { display:block; width:100%; height:100%; }
746
- .anycoder { position:fixed; bottom:5px; right:5px; font-size:10px; color:#555; z-index:9999; }
747
- </style>
748
- </head>
749
- <body>
750
- <canvas id="glCanvas"></canvas>
751
- <div class="anycoder">Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">Nexus FX Forge</a></div>
752
- <script>
753
- // Simplified logic for WebView
754
- const canvas = document.getElementById('glCanvas');
755
- const gl = canvas.getContext('webgl');
756
- // ... (WebGL init code would go here, simplified for demo)
757
- canvas.width = window.innerWidth; canvas.height = window.innerHeight;
758
- <\/script>
759
- </body>
760
- </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
+
4
  <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>NEXUS FX OS | AI Sci-Fi Studio + VisionFX Pro</title>
8
+
9
+ <!-- Libraries -->
10
+ <script src="https://cdn.tailwindcss.com"></script>
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"></script>
12
+ <script src="https://cdn.jsdelivr.net/npm/mathjs@11.8.0/lib/browser/math.min.js"></script>
13
+ <script src="https://cdn.jsdelivr.net/npm/jszip@3.10.1/dist/jszip.min.js"></script>
14
+ <script src="https://cdn.jsdelivr.net/npm/gsap@3.12.2/dist/gsap.min.js"></script>
15
+
16
+ <!-- Three.js Import Map -->
17
+ <script type="importmap">
18
+ {
19
+ "imports": {
20
+ "three": "https://unpkg.com/three@0.160.0/build/three.module.js",
21
+ "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
22
+ }
23
+ }
24
+ </script>
25
+
26
+ <style>
27
+ :root {
28
+ --bg-void: #030306;
29
+ --bg-panel: #0a0a0f;
30
+ --bg-card: #12121a;
31
+ --border: #1a1a2e;
32
+ --accent-cyan: #00f0ff;
33
+ --accent-magenta: #ff00aa;
34
+ --accent-lime: #00ff66;
35
+ --accent-red: #ff2244;
36
+ --text: #e8e8f0;
37
+ --text-dim: #6a6a7a;
38
+ --math-bg: #000000;
39
+ }
40
+
41
+ * { box-sizing: border-box; margin: 0; padding: 0; outline: none; }
42
+
43
+ body {
44
+ font-family: 'JetBrains Mono', monospace;
45
+ background: var(--bg-void);
46
+ color: var(--text);
47
+ overflow: hidden;
48
+ height: 100vh;
49
+ background-image:
50
+ radial-gradient(circle at 10% 20%, rgba(0, 240, 255, 0.1) 0%, transparent 20%),
51
+ radial-gradient(circle at 90% 80%, rgba(255, 0, 170, 0.1) 0%, transparent 20%);
52
+ }
53
+
54
+ .font-display { font-family: 'Orbitron', sans-serif; }
55
+
56
+ /* Glitch Text Animation */
57
+ .glitch-text {
58
+ position: relative;
59
+ z-index: 1;
60
+ }
61
+ .glitch-text::before, .glitch-text::after {
62
+ content: attr(data-text);
63
+ position: absolute;
64
+ top: 0; left: 0; width: 100%; height: 100%;
65
+ background: var(--bg-void); z-index: -1;
66
+ }
67
+ .glitch-text::before {
68
+ left: 2px; text-shadow: -1px 0 var(--accent-magenta);
69
+ clip: rect(24px, 550px, 90px, 0);
70
+ animation: glitch-anim-2 3s infinite linear alternate-reverse;
71
+ }
72
+ .glitch-text::after {
73
+ left: -2px; text-shadow: -1px 0 var(--accent-cyan);
74
+ clip: rect(85px, 550px, 140px, 0);
75
+ animation: glitch-anim 2.5s infinite linear alternate-reverse;
76
+ }
77
+ @keyframes glitch-anim { /* ... (Same as original) ... */
78
+ 0% { clip: rect(14px, 9999px, 127px, 0); } 20% { clip: rect(80px, 9999px, 95px, 0); }
79
+ 40% { clip: rect(10px, 9999px, 50px, 0); } 60% { clip: rect(60px, 9999px, 120px, 0); }
80
+ 80% { clip: rect(20px, 9999px, 80px, 0); } 100% { clip: rect(100px, 9999px, 110px, 0); }
81
+ }
82
+ @keyframes glitch-anim-2 { /* ... (Same as original) ... */
83
+ 0% { clip: rect(65px, 9999px, 100px, 0); } 20% { clip: rect(10px, 9999px, 40px, 0); }
84
+ 40% { clip: rect(90px, 9999px, 130px, 0); } 60% { clip: rect(30px, 9999px, 80px, 0); }
85
+ 80% { clip: rect(70px, 9999px, 110px, 0); } 100% { clip: rect(50px, 9999px, 90px, 0); }
86
+ }
87
+
88
+ /* Scrollbars & Inputs */
89
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
90
+ ::-webkit-scrollbar-track { background: var(--bg-void); }
91
+ ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
92
+ ::-webkit-scrollbar-thumb:hover { background: var(--accent-cyan); }
93
 
94
+ input[type=range] {
95
+ -webkit-appearance: none; width: 100%; background: transparent;
96
+ cursor: grab; transition: all 0.2s;
97
+ }
98
+ input[type=range]:active { cursor: grabbing; }
99
+ input[type=range]::-webkit-slider-thumb {
100
+ -webkit-appearance: none; height: 14px; width: 14px; border-radius: 50%;
101
+ background: var(--text); cursor: pointer; margin-top: -5px;
102
+ box-shadow: 0 0 5px var(--accent-cyan);
103
+ }
104
+ input[type=range]::-webkit-slider-runnable-track {
105
+ width: 100%; height: 4px; cursor: pointer; background: var(--border); border-radius: 2px;
106
+ }
107
+
108
+ /* Glassmorphism & Neon */
109
+ .glass-panel {
110
+ background: rgba(10, 10, 15, 0.85); backdrop-filter: blur(10px);
111
+ border: 1px solid rgba(255, 255, 255, 0.08); box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.5);
112
+ }
113
+ .neon-box { border: 1px solid var(--border); transition: all 0.2s; }
114
+ .neon-box:hover { box-shadow: 0 0 15px rgba(0, 240, 255, 0.2); border-color: var(--accent-cyan); }
115
+ .neon-box.active { box-shadow: 0 0 20px var(--accent-cyan); border-color: var(--accent-cyan); }
116
 
117
+ .fx-card { background: rgba(18, 18, 26, 0.9); border-left: 3px solid var(--border); transition: all 0.3s; }
118
+ .fx-card:hover { background: rgba(25, 25, 35, 0.95); }
119
+
120
+ /* Math Engine UI */
121
+ .math-container { position: relative; }
122
+ .math-input {
123
+ width: 100%; background: var(--math-bg); border: 1px solid var(--border);
124
+ color: var(--accent-cyan); font-family: 'Courier New', monospace; padding: 8px;
125
+ min-height: 40px; resize: vertical; font-size: 14px; transition: border 0.3s;
126
+ }
127
+ .math-input:focus { border-color: var(--accent-lime); outline: none; }
128
+ .math-btn { background: #222; border: 1px solid #444; color: #aaa; cursor: pointer; padding: 4px 8px; font-size: 10px; }
129
+ .math-btn:hover { background: var(--accent-cyan); color: #000; }
130
+
131
+ .math-popup {
132
+ position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);
133
+ background: var(--bg-panel); border: 1px solid var(--accent-cyan); padding: 20px;
134
+ z-index: 5000; box-shadow: 0 0 50px rgba(0, 240, 255, 0.2); width: 90%; max-width: 500px;
135
+ display: none; animation: popIn 0.3s ease;
136
+ }
137
+ @keyframes popIn { from { opacity: 0; transform: translate(-50%, -45%); } to { opacity: 1; transform: translate(-50%, -50%); } }
138
+
139
+ /* Split View Controls */
140
+ .split-view-handle {
141
+ width: 10px; background: #000; cursor: col-resize; border-left: 1px solid var(--border); border-right: 1px solid var(--border);
142
+ }
143
+ .split-view-handle:hover { background: var(--accent-cyan); }
144
+
145
+ /* 3D Canvas Container */
146
+ #viewport-3d { background: radial-gradient(circle at center, #1a1a24 0%, #000000 100%); }
147
+
148
+ /* Mobile Responsive Tweaks */
149
+ @media (max-width: 768px) {
150
+ .split-view-container { flex-direction: column; }
151
+ .split-view-handle { width: 100%; height: 10px; cursor: row-resize; border-left: none; border-right: none; border-top: 1px solid var(--border); border-bottom: 1px solid var(--border); }
152
+ .panel-left, .panel-right { flex: 1; }
153
+ }
154
+ </style>
155
+ <link rel="preconnect" href="https://fonts.googleapis.com">
156
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
157
+ <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=JetBrains+Mono:wght@300;400;500&display=swap" rel="stylesheet">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  </head>
159
+
160
  <body class="flex flex-col h-screen">
161
 
162
+ <!-- HEADER -->
163
+ <header class="h-16 bg-[#050508] border-b border-[#1a1a2e] flex items-center justify-between px-6 z-50 shadow-[0_5px_20px_rgba(0,0,0,0.8)]">
164
+ <div class="flex items-center gap-4">
165
+ <div class="w-10 h-10 rounded-lg bg-gradient-to-br from-cyan-500 to-magenta-600 flex items-center justify-center shadow-[0_0_15px_rgba(0,240,255,0.5)]">
166
+ <i class="fa-solid fa-cube text-white text-xl"></i>
167
+ </div>
168
+ <div>
169
+ <h1 class="font-display text-xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-magenta-400 glitch-text" data-text="NEXUS FX OS">
170
+ NEXUS FX OS
171
+ </h1>
172
+ <div class="text-xs text-cyan-500/70 font-mono tracking-widest">INTELLIGENT MEDIA ENGINE // V.3.0.0</div>
173
+ </div>
174
+ </div>
175
+ <div class="flex items-center gap-3">
176
+ <button id="btn-randomize" class="hidden md:flex items-center gap-2 px-4 py-2 rounded bg-[#1a1a2e] hover:bg-[#2a2a4e] text-cyan-400 border border-[#1a1a2e] transition-all hover:border-cyan-500/50 hover:shadow-[0_0_10px_rgba(0,240,255,0.3)] font-mono text-xs uppercase tracking-wider">
177
+ <i class="fa-solid fa-shuffle"></i> Randomize
178
+ </button>
179
+ <button id="btn-export" class="flex items-center gap-2 px-4 py-2 rounded bg-gradient-to-r from-cyan-600 to-blue-700 hover:from-cyan-500 hover:to-blue-600 text-white shadow-[0_0_15px_rgba(0,240,255,0.4)] transition-all hover:shadow-[0_0_25px_rgba(0,240,255,0.6)] font-mono text-xs uppercase tracking-wider font-bold">
180
+ <i class="fa-solid fa-download"></i> Export
181
+ </button>
182
+ </div>
183
+ </header>
184
+
185
+ <!-- MAIN LAYOUT -->
186
+ <main class="flex-1 flex overflow-hidden split-view-container">
187
+
188
+ <!-- LEFT PANEL: FX LIBRARY & MATH ENGINE -->
189
+ <aside class="w-96 bg-[#0a0a0f] border-r border-[#1a1a2e] flex flex-col overflow-hidden relative">
190
+
191
+ <!-- FX Library Section -->
192
+ <div class="p-4 border-b border-[#1a1a2e] bg-[#050508]">
193
+ <div class="flex items-center justify-between mb-3">
194
+ <h2 class="font-display text-sm text-cyan-400 tracking-widest">FX LIBRARY <span class="text-[10px] bg-cyan-900/30 px-2 py-0.5 rounded ml-2">100+ MODULES</span></h2>
195
+ <div class="relative">
196
+ <i class="fa-solid fa-search absolute left-3 top-2.5 text-cyan-500/50 text-xs"></i>
197
+ <input type="text" id="search-fx" placeholder="Search shaders..." class="w-full pl-8 pr-3 py-1.5 bg-[#12121a] border border-[#1a1a2e] rounded text-[10px] text-cyan-200 focus:border-cyan-500 focus:outline-none transition-colors">
198
+ </div>
199
  </div>
200
+ <div class="flex gap-2 overflow-x-auto pb-2">
201
+ <span class="px-2 py-1 rounded text-[9px] bg-[#1a1a2e] text-cyan-300 border border-[#1a1a2e] cursor-pointer hover:bg-cyan-900/30 transition">ALL</span>
202
+ <span class="px-2 py-1 rounded text-[9px] bg-[#1a1a2e] text-purple-300 border border-[#1a1a2e] cursor-pointer hover:bg-purple-900/30 transition">COLOR</span>
203
+ <span class="px-2 py-1 rounded text-[9px] bg-[#1a1a2e] text-red-300 border border-[#1a1a2e] cursor-pointer hover:bg-red-900/30 transition">GLITCH</span>
204
+ <span class="px-2 py-1 rounded text-[9px] bg-[#1a1a2e] text-green-300 border border-[#1a1a2e] cursor-pointer hover:bg-green-900/30 transition">GEOMETRY</span>
205
+ <span class="px-2 py-1 rounded text-[9px] bg-[#1a1a2e] text-yellow-300 border border-[#1a1a2e] cursor-pointer hover:bg-yellow-900/30 transition">AUDIO</span>
 
 
 
206
  </div>
207
+ </div>
208
 
209
+ <div id="fx-list" class="flex-1 overflow-y-auto p-3 space-y-2 custom-scrollbar relative z-10">
210
+ <!-- FX Items Injected Here -->
211
+ <div class="text-center py-10 text-gray-600 text-sm">
212
+ <i class="fa-solid fa-list text-2xl mb-2 opacity-50"></i> Drag effects or use Math Engine below
213
+ </div>
214
+ </div>
215
+
216
+ <!-- Math Engine Section (Bottom of Left Panel) -->
217
+ <div class="p-4 border-t border-[#1a1a2e] bg-[#050508] z-20">
218
+ <div class="flex items-center justify-between mb-2">
219
+ <h3 class="font-display text-sm text-magenta-400 tracking-widest">MATH ENGINE // V.3</h3>
220
+ <button id="btn-spread" class="text-[10px] bg-magenta-900/20 text-magenta-300 px-2 py-0.5 rounded border border-magenta-900/50 hover:bg-magenta-900/50 transition flex items-center gap-1">
221
+ <i class="fa-solid fa-expand"></i> Split View
222
+ </button>
223
+ </div>
224
 
225
+ <div class="space-y-2">
226
+ <div class="flex items-center gap-2">
227
+ <button id="btn-start-stop" class="flex-1 py-2 rounded bg-[#1a1a2e] hover:bg-[#2a2a4e] text-xs text-cyan-300 border border-[#1a1a2e] transition flex items-center justify-center gap-2">
228
+ <i class="fa-solid fa-play" id="play-icon"></i> <span id="play-text">Start Flow</span>
229
+ </button>
230
+ <button id="btn-clear" class="px-2 py-2 rounded bg-red-900/20 text-red-400 hover:bg-red-900/40 transition">
231
+ <i class="fa-solid fa-trash"></i>
232
+ </button>
233
+ </div>
234
+
235
+ <div class="math-container">
236
+ <textarea id="math-input" class="math-input" placeholder="Enter Formula (e.g., sin(x) * cos(y) + 0.5 * time)..." spellcheck="false"></textarea>
237
+ <div class="absolute top-2 right-2 flex gap-1">
238
+ <button class="math-btn" id="btn-history"><i class="fa-solid fa-history"></i> H</button>
239
+ <button class="math-btn" id="btn-calc"><i class="fa-solid fa-calculator"></i> C</button>
240
+ <button class="math-btn" id="btn-apply"><i class="fa-solid fa-check"></i></button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  </div>
242
+ </div>
243
+
244
+ <div id="math-output" class="text-[9px] text-gray-400 font-mono min-h-[16px]">
245
+ Waiting for input...
246
+ </div>
247
+
248
+ <div id="generated-sliders" class="space-y-3 pt-2">
249
+ <!-- Dynamic Sliders appear here -->
250
+ </div>
251
+ </div>
252
 
253
+ <div class="mt-4 pt-3 border-t border-[#1a1a2e]">
254
+ <h4 class="text-[10px] text-gray-500 uppercase font-bold mb-2">Pattern Extractor</h4>
255
+ <textarea id="pattern-input" class="w-full h-24 bg-[#000] border border-[#1a1a2e] rounded p-2 text-[9px] text-cyan-200 font-mono mb-2" placeholder="Paste code to extract patterns (URLs, Functions, Comments)..."></textarea>
256
+ <div class="flex gap-2">
257
+ <select id="pattern-select" class="flex-1 bg-[#12121a] border border-[#1a1a2e] rounded p-1 text-[9px] text-cyan-200">
258
+ <option value="urls">Download Files (pdf, zip...)</option>
259
+ <option value="functions">Function Definitions</option>
260
+ <option value="comments">Comments (JS/Java)</option>
261
+ <option value="cyber">🧷 Cyber Patterns</option>
262
+ </select>
263
+ <button id="btn-extract" class="px-3 py-1 rounded bg-[#1a1a2e] hover:bg-[#2a2a4e] text-[9px] text-cyan-300 border border-[#1a1a2e]">Extract</button>
264
+ </div>
265
+ <div id="pattern-output" class="mt-2 text-[8px] text-gray-500 font-mono h-20 overflow-y-auto border border-[#1a1a2e] bg-black/50 p-2 rounded"></div>
266
+ </div>
267
+ </div>
268
+ </aside>
269
+
270
+ <!-- CENTER: VIEWPORT -->
271
+ <section class="flex-1 relative flex flex-col items-center justify-center bg-[#030306] overflow-hidden">
272
+
273
+ <!-- 3D Canvas -->
274
+ <div id="viewport-3d" class="absolute inset-0 w-full h-full z-0">
275
+ <canvas id="glCanvas" class="w-full h-full object-contain"></canvas>
276
+ </div>
277
+
278
+ <!-- Overlay Info -->
279
+ <div class="absolute top-4 left-4 z-10 flex flex-col gap-2 pointer-events-none">
280
+ <div class="glass-panel px-3 py-2 rounded border-l-4 border-cyan-500">
281
+ <div class="text-[10px] text-cyan-300 uppercase font-bold">Current Engine</div>
282
+ <div class="text-xs font-mono text-white" id="engine-status">WebGL 2.0 // Three.js Core</div>
283
+ </div>
284
+ <div class="glass-panel px-3 py-2 rounded border-l-4 border-magenta-500 w-48">
285
+ <div class="text-[10px] text-magenta-300 uppercase font-bold">Audio Reactivity</div>
286
+ <div class="flex items-center gap-2 mt-1">
287
+ <div class="flex-1 h-1 bg-gray-800 rounded overflow-hidden">
288
+ <div id="audio-bar" class="h-full bg-magenta-500 w-0 transition-all duration-75"></div>
289
  </div>
290
+ <span id="audio-val" class="text-[9px] font-mono text-magenta-300">0%</span>
291
+ </div>
292
+ </div>
293
+ </div>
294
+
295
+ <!-- Bottom Controls -->
296
+ <div class="absolute bottom-6 z-20 flex items-center gap-4 bg-[#00000080] backdrop-blur-md px-6 py-3 rounded-full border border-[#1a1a2e] shadow-[0_0_30px_rgba(0,0,0,0.8)]">
297
+ <button id="btn-play" class="w-10 h-10 rounded-full bg-white text-black hover:bg-cyan-400 transition flex items-center justify-center">
298
+ <i class="fa-solid fa-play text-sm ml-0.5"></i>
299
+ </button>
300
+ <div class="flex flex-col">
301
+ <span class="text-[9px] text-gray-400 uppercase tracking-widest">Time</span>
302
+ <span id="time-display" class="font-mono text-sm text-cyan-400">00:00:00</span>
303
+ </div>
304
+ <button id="btn-screenshot" class="px-3 py-1 rounded text-[10px] text-gray-300 hover:text-white hover:bg-white/10 transition border border-transparent hover:border-white/20">
305
+ <i class="fa-solid fa-camera mr-1"></i> Snapshot
306
+ </button>
307
+ </div>
308
+ </section>
309
+
310
+ <!-- RIGHT PANEL: STACK & SETTINGS -->
311
+ <aside class="w-80 bg-[#0a0a0f] border-l border-[#1a1a2e] flex flex-col overflow-hidden z-10">
312
+
313
+ <!-- Active Stack -->
314
+ <div class="p-4 border-b border-[#1a1a2e] bg-[#050508]">
315
+ <div class="flex items-center justify-between">
316
+ <h2 class="font-display text-sm text-cyan-400 tracking-widest">EFFECT STACK</h2>
317
+ <span id="stack-count" class="text-[10px] bg-cyan-900/30 text-cyan-300 px-2 py-0.5 rounded border border-cyan-900/50">0 / 12</span>
318
+ </div>
319
+ <div class="text-[9px] text-gray-500 mt-1">Drag to reorder • Click to adjust</div>
320
+ </div>
321
 
322
+ <div id="stack-list" class="flex-1 overflow-y-auto p-4 space-y-3 bg-[#08080c]">
323
+ <div class="text-center py-10 text-gray-600 text-sm border-2 border-dashed border-gray-800 rounded">
324
+ <i class="fa-solid fa-layer-group text-2xl mb-2 opacity-50"></i>
325
+ No effects active. Drag from library.
326
+ </div>
327
+ </div>
328
+
329
+ <!-- Quick Settings -->
330
+ <div class="p-4 border-t border-[#1a1a2e] bg-[#050508] space-y-4">
331
+ <div>
332
+ <div class="flex justify-between mb-1">
333
+ <label class="text-[10px] text-cyan-400 uppercase font-bold tracking-wider">Global Resolution</label>
334
+ <span id="res-val" class="text-[9px] text-gray-400 font-mono">1080p</span>
335
+ </div>
336
+ <input type="range" id="global-res" min="360" max="2160" step="72" value="1080" class="w-full">
337
+ </div>
338
+ <div>
339
+ <div class="flex justify-between mb-1">
340
+ <label class="text-[10px] text-magenta-400 uppercase font-bold tracking-wider">Frame Rate (FPS)</label>
341
+ <span id="fps-val" class="text-[9px] text-gray-400 font-mono">60.000</span>
342
+ </div>
343
+ <input type="range" id="global-fps" min="15" max="240" step="0.1" value="60" class="w-full">
344
+ </div>
345
+ <div class="pt-2 border-t border-[#1a1a2e]">
346
+ <h3 class="text-[10px] text-gray-500 uppercase font-bold mb-2">APK Converter</h3>
347
+ <div class="flex items-center justify-between text-[9px] text-gray-400 mb-2 bg-[#000] p-2 rounded border border-[#1a1a2e]">
348
+ <span>Web to Android Builder</span>
349
+ <i class="fa-solid fa-mobile-screen text-cyan-500"></i>
350
+ </div>
351
+ <div class="grid grid-cols-2 gap-2">
352
+ <input type="text" id="app-name" placeholder="App Name" class="bg-[#12121a] border border-[#1a1a2e] rounded px-2 py-1 text-[9px] text-cyan-200 focus:border-cyan-500">
353
+ <input type="text" id="app-id" placeholder="Bundle ID" class="bg-[#12121a] border border-[#1a1a2e] rounded px-2 py-1 text-[9px] text-cyan-200 focus:border-cyan-500" value="com.nexus.app">
354
+ </div>
355
+ <button id="btn-build-apk" class="w-full mt-2 py-1.5 rounded bg-[#1a1a2e] hover:bg-[#2a2a4e] text-[10px] text-cyan-300 border border-[#1a1a2e] transition flex items-center justify-center gap-2">
356
+ <i class="fa-solid fa-code-commit"></i> Generate Android Project
357
+ </button>
358
+ </div>
359
+ <div class="pt-2 border-t border-[#1a1a2e]">
360
+ <h3 class="text-[10px] text-gray-500 uppercase font-bold mb-2">Database Stats</h3>
361
+ <div class="grid grid-cols-2 gap-2 text-[9px] text-gray-400 font-mono">
362
+ <div class="bg-[#000] p-2 rounded border border-[#1a1a2e]">Presets: <span id="stat-presets" class="text-cyan-400">0</span></div>
363
+ <div class="bg-[#000] p-2 rounded border border-[#1a1a2e]">History: <span id="stat-history" class="text-magenta-400">0</span></div>
364
+ </div>
365
+ </div>
366
+ </div>
367
+ </aside>
368
+ </main>
369
+
370
+ <!-- MATH POPUP MODAL -->
371
+ <div id="math-popup" class="math-popup">
372
+ <div class="flex justify-between items-center mb-4 border-b border-gray-700 pb-2">
373
+ <h3 class="text-lg font-bold text-cyan-400">Math Correction Assistant</h3>
374
+ <button id="popup-close" class="text-gray-400 hover:text-white text-xl">&times;</button>
375
+ </div>
376
+ <p class="text-sm text-gray-300 mb-2">We detected a syntax issue in your formula. Here is the corrected version:</p>
377
+ <textarea id="popup-correction" class="w-full h-32 bg-[#000] border border-cyan-500/50 text-cyan-200 p-2 font-mono text-sm mb-4"></textarea>
378
+ <div class="flex justify-between items-center">
379
+ <span class="text-xs text-yellow-500">Auto-fix applied in 0.003s</span>
380
+ <button id="popup-apply" class="px-4 py-2 bg-cyan-600 hover:bg-cyan-500 text-white rounded text-sm font-bold transition">Accept & Apply</button>
381
+ </div>
382
+ </div>
383
+
384
+ <!-- SCRIPTS -->
385
+ <script type="module">
386
+ import * as THREE from 'three';
387
+ import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
388
+ import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
389
+ import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js';
390
+
391
+ // --- 1. FX LIBRARY DEFINITION (Merged & Expanded) ---
392
+ const FX_LIBRARY = [
393
+ { id: 'bw', name: 'Monochrome', category: 'Color', desc: 'Converts video to black and white with contrast control.', glsl: `uniform float amount; void main() { vec4 color = texture2D(tDiffuse, vUv); float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114)); gl_FragColor = vec4(vec3(gray * amount), color.a); }` },
394
+ { id: 'rgb_shift', name: 'Chromatic Aberration', category: 'Glitch', desc: 'Separates RGB channels for sci-fi distortion.', glsl: `uniform float amount; uniform float uAudio; void main() { float offset = amount * 0.05 * (1.0 + uAudio); vec2 rUv = vUv + vec2(offset, 0.0); vec2 gUv = vUv; vec2 bUv = vUv - vec2(offset, 0.0); vec4 r = texture2D(tDiffuse, rUv); vec4 g = texture2D(tDiffuse, gUv); vec4 b = texture2D(tDiffuse, bUv); gl_FragColor = vec4(r.r, g.g, b.b, 1.0); }` },
395
+ { id: 'noise', name: 'Digital Noise', category: 'Glitch', desc: 'Static noise overlay with audio sensitivity.', glsl: `uniform float amount; uniform float time; float rand(vec2 co) { return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); } void main() { vec4 color = texture2D(tDiffuse, vUv); float diff = (rand(vUv + time) - 0.5) * amount; gl_FragColor = vec4(color.rgb + diff, color.a); }` },
396
+ { id: 'scanlines', name: 'CRT Scanlines', category: 'Visual', desc: 'Retro television scanline effect.', glsl: `uniform float amount; uniform vec2 resolution; void main() { vec4 color = texture2D(tDiffuse, vUv); float scanline = sin(vUv.y * resolution.y * 0.5) * 0.1 * amount; gl_FragColor = vec4(color.rgb - scanline, color.a); }` },
397
+ { id: 'vignette', name: 'Neon Vignette', category: 'Art', desc: 'Darken edges with neon color bleed.', glsl: `uniform float amount; uniform vec2 resolution; void main() { vec4 color = texture2D(tDiffuse, vUv); vec2 uv = vUv - 0.5; float dist = length(uv); float aspect = resolution.x / resolution.y; uv.x *= aspect; float curve = smoothstep(0.8, 0.1, length(uv)); gl_FragColor = vec4(color.rgb * curve, color.a); }` },
398
+ { id: 'shockwave', name: 'Sonic Shockwave', category: 'Audio', desc: 'Expands based on bass frequencies.', glsl: `uniform float amount; uniform float uAudio; uniform vec2 resolution; void main() { vec2 uv = vUv - 0.5; float aspect = resolution.x / resolution.y; uv.x *= aspect; float radius = length(uv); float angle = atan(uv.y, uv.x); float distortion = sin(angle * 10.0 + radius * 20.0 * uAudio + uAudio * 5.0) * amount * 0.1; vec2 newUv = vUv + distortion * vec2(cos(angle), sin(angle)); gl_FragColor = texture2D(tDiffuse, newUv); }` },
399
+ { id: 'pixelate', name: 'Mosaic Glitch', category: 'Glitch', desc: 'Blocky pixelation based on mid frequencies.', glsl: `uniform float amount; uniform vec2 resolution; void main() { float d = 1.0 / (amount * 100.0 + 10.0); vec2 coord = d * floor(vUv / d); gl_FragColor = texture2D(tDiffuse, coord); }` },
400
+ { id: 'invert', name: 'Cyber Invert', category: 'Color', desc: 'High contrast negative effect.', glsl: `uniform float amount; void main() { vec4 color = texture2D(tDiffuse, vUv); gl_FragColor = vec4(mix(color.rgb, 1.0 - color.rgb, amount), color.a); }` },
401
+ // VisionFX Pro Additions
402
+ { id: 'toxic', name: 'Toxic Fallout', category: 'Color', desc: 'Green channel amplification with noise.', glsl: `uniform float amount; uniform float time; void main() { vec4 color = texture2D(tDiffuse, vUv); float noise = (fract(sin(dot(vUv, vec2(12.9898,78.233))) * 43758.5453) - 0.5) * amount * 0.2; gl_FragColor = vec4(color.r, color.g * (1.0 + amount), color.b, color.a) + noise; }` },
403
+ { id: 'luma_rush', name: 'Luma Rush', category: 'Logic', desc: 'Speed control based on brightness.', glsl: `uniform float amount; uniform float uAudio; void main() { vec4 color = texture2D(tDiffuse, vUv); float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114)); float speed = 1.0 + (gray * amount * 2.0) + uAudio; gl_FragColor = texture2D(tDiffuse, vUv * speed); }` }
404
+ ];
405
+
406
+ // --- 2. APP CORE (Nexus OS + Vision FX Integration) ---
407
+ const NexusOS = {
408
+ scene: null, camera: null, renderer: null, composer: null,
409
+ video: null, videoTex: null, audioCtx: null, analyser: null, dataArray: null,
410
+ stack: [], maxEffects: 12, isPlaying: false, animationId: null,
411
+ mathState: { isRunning: false, autoCorrect: true },
412
+ mathHistory: [],
413
+
414
+ init: () => {
415
+ NexusOS.setup3D();
416
+ NexusOS.setupUI();
417
+ NexusOS.setupAudio();
418
+ NexusOS.animate();
419
+ NexusOS.loadStats();
420
+ },
421
+
422
+ setup3D: () => {
423
+ const container = document.getElementById('viewport-3d');
424
+ const width = container.clientWidth;
425
+ const height = container.clientHeight;
426
+
427
+ NexusOS.scene = new THREE.Scene();
428
+ NexusOS.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
429
+
430
+ NexusOS.renderer = new THREE.WebGLRenderer({ canvas: document.getElementById('glCanvas'), antialias: true, preserveDrawingBuffer: true });
431
+ NexusOS.renderer.setSize(width, height);
432
+ NexusOS.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
433
+
434
+ NexusOS.composer = new EffectComposer(NexusOS.renderer);
435
+ const renderPass = new RenderPass(NexusOS.scene, NexusOS.camera);
436
+ NexusOS.composer.addPass(renderPass);
437
+
438
+ const geometry = new THREE.PlaneGeometry(2, 2);
439
+ const material = new THREE.MeshBasicMaterial({ color: 0x000000 });
440
+ const quad = new THREE.Mesh(geometry, material);
441
+ NexusOS.scene.add(quad);
442
+ NexusOS.quad = quad;
443
+
444
+ window.addEventListener('resize', () => {
445
+ const w = container.clientWidth; const h = container.clientHeight;
446
+ NexusOS.renderer.setSize(w, h); NexusOS.composer.setSize(w, h);
447
+ NexusOS.stack.forEach(pass => { if(pass.uniforms.resolution) pass.uniforms.resolution.value.set(w, h); });
448
+ });
449
+ },
450
+
451
+ setupUI: () => {
452
+ const list = document.getElementById('fx-list');
453
+ const categories = [...new Set(FX_LIBRARY.map(s => s.category))];
454
+
455
+ categories.forEach(cat => {
456
+ const header = document.createElement('div');
457
+ header.className = "text-[9px] text-gray-500 uppercase font-bold mt-4 mb-2 pl-2 tracking-widest border-b border-[#1a1a2e]";
458
+ header.innerText = cat; list.appendChild(header);
459
+
460
+ FX_LIBRARY.filter(s => s.category === cat).forEach(shader => {
461
+ const el = document.createElement('div');
462
+ el.className = "fx-card p-3 rounded transition-all group cursor-pointer border-l-4 border-[#1a1a2e]";
463
+ el.innerHTML = `
464
+ <div class="flex justify-between items-start mb-1">
465
+ <span class="font-bold text-cyan-300 text-sm group-hover:text-white transition">${shader.name}</span>
466
+ <i class="fa-solid fa-plus-circle text-[10px] text-gray-500 group-hover:text-cyan-400 transition"></i>
467
+ </div>
468
+ <div class="text-[9px] text-gray-400 line-clamp-2">${shader.desc}</div>
469
+ `;
470
+ el.draggable = true;
471
+ el.addEventListener('dragstart', (e) => {
472
+ e.dataTransfer.setData('text/plain', shader.id);
473
+ e.dataTransfer.setData('name', shader.name);
474
+ e.dataTransfer.setData('glsl', shader.glsl);
475
+ });
476
+ list.appendChild(el);
477
+ });
478
+ });
479
+
480
+ const stackList = document.getElementById('stack-list');
481
+ stackList.addEventListener('dragover', (e) => e.preventDefault());
482
+ stackList.addEventListener('drop', (e) => {
483
+ e.preventDefault();
484
+ const id = e.dataTransfer.getData('text/plain');
485
+ if (NexusOS.stack.length < NexusOS.maxEffects) NexusOS.addEffect(id, e.dataTransfer.getData('name'), e.dataTransfer.getData('glsl'));
486
+ else alert("Effect Stack Full (Max 12)");
487
+ });
488
+
489
+ document.getElementById('search-fx').addEventListener('input', (e) => {
490
+ const term = e.target.value.toLowerCase();
491
+ document.querySelectorAll('.fx-card').forEach(card => {
492
+ card.style.display = card.innerText.toLowerCase().includes(term) ? 'block' : 'none';
493
+ });
494
+ });
495
+
496
+ // Global Settings
497
+ document.getElementById('global-res').addEventListener('input', (e) => document.getElementById('res-val').innerText = e.target.value + 'p');
498
+ document.getElementById('global-fps').addEventListener('input', (e) => document.getElementById('fps-val').innerText = parseFloat(e.target.value).toFixed(3));
499
+
500
+ // Play/Pause
501
+ document.getElementById('btn-play').addEventListener('click', () => {
502
+ if (NexusOS.video.paused) {
503
+ NexusOS.video.play(); document.getElementById('audio-src').play();
504
+ NexusOS.isPlaying = true;
505
+ document.getElementById('btn-play').innerHTML = '<i class="fa-solid fa-pause text-sm ml-0.5"></i>';
506
+ } else {
507
+ NexusOS.video.pause(); document.getElementById('audio-src').pause();
508
+ NexusOS.isPlaying = false;
509
+ document.getElementById('btn-play').innerHTML = '<i class="fa-solid fa-play text-sm ml-0.5"></i>';
510
+ }
511
+ });
512
+
513
+ // Randomize
514
+ document.getElementById('btn-randomize').addEventListener('click', () => {
515
+ NexusOS.stack.forEach(() => NexusOS.removeEffect(NexusOS.stack[0].uid));
516
+ const count = Math.floor(Math.random() * 5) + 2;
517
+ for(let i=0; i<count; i++) {
518
+ const rnd = FX_LIBRARY[Math.floor(Math.random() * FX_LIBRARY.length)];
519
+ NexusOS.addEffect(rnd.id, rnd.name, rnd.glsl);
520
+ }
521
+ });
522
+
523
+ // Export
524
+ document.getElementById('btn-export').addEventListener('click', () => {
525
+ const config = { effects: NexusOS.stack.map(p => ({ id: p.def.id, amount: p.uniforms.amount.value })), resolution: document.getElementById('global-res').value };
526
+ const blob = new Blob([JSON.stringify(config)], {type: 'application/json'});