abdulahad086 commited on
Commit
3cc6306
·
verified ·
1 Parent(s): 3a37e7a

Upload folder using huggingface_hub

Browse files
frontend/ElectricBorder.css ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .electric-border {
2
+ --electric-border-color: #34eb8f;
3
+ --electric-light-color: #34eb8f;
4
+ position: relative;
5
+
6
+ border-radius: inherit;
7
+ overflow: visible;
8
+ isolation: isolate;
9
+ }
10
+
11
+ /* Modern CSS Support Check */
12
+ @supports (color: oklch(from red l c h)) {
13
+ .electric-border {
14
+ --electric-light-color: oklch(from var(--electric-border-color) l c h);
15
+ }
16
+ }
17
+
18
+ .eb-canvas-container {
19
+ position: absolute;
20
+ top: 50%;
21
+ left: 50%;
22
+ transform: translate(-50%, -50%);
23
+ pointer-events: none;
24
+ z-index: 2;
25
+ }
26
+
27
+ .eb-canvas {
28
+ display: block;
29
+ }
30
+
31
+ .eb-layers {
32
+ position: absolute;
33
+ inset: 0;
34
+ border-radius: inherit;
35
+ pointer-events: none;
36
+ z-index: 0;
37
+ }
38
+
39
+ .eb-glow-1,
40
+ .eb-glow-2,
41
+ .eb-background-glow {
42
+ position: absolute;
43
+ inset: 0;
44
+ border-radius: inherit;
45
+ pointer-events: none;
46
+ box-sizing: border-box;
47
+ }
48
+
49
+ .eb-glow-1 {
50
+ border: 2px solid var(--electric-light-color);
51
+ opacity: 0.6;
52
+ filter: blur(1px);
53
+ }
54
+
55
+ .eb-glow-2 {
56
+ border: 2px solid var(--electric-light-color);
57
+ filter: blur(4px);
58
+ }
59
+
60
+ .eb-background-glow {
61
+ z-index: -1;
62
+ transform: scale(1.1);
63
+ filter: blur(32px);
64
+ opacity: 0.2;
65
+ background: linear-gradient(-30deg, var(--electric-light-color), transparent, var(--electric-border-color));
66
+ }
67
+
68
+ /* Ensure content stays on top */
69
+ .container {
70
+ position: relative;
71
+ z-index: 5;
72
+ }
frontend/ElectricBorder.js ADDED
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class ElectricBorder {
2
+ constructor(element, options = {}) {
3
+ if (!element) return console.error('ElectricBorder: Element not found');
4
+ console.log('ElectricBorder: Initializing with options:', options);
5
+ this.container = element;
6
+
7
+ this.options = {
8
+ color: options.color || '#34eb8f',
9
+ speed: options.speed || 1,
10
+ chaos: options.chaos || 0.12,
11
+ borderRadius: options.borderRadius || 24,
12
+ ...options
13
+ };
14
+
15
+ this.setupCanvas();
16
+ this.setupStyles();
17
+ this.time = 0;
18
+ this.lastFrameTime = 0;
19
+
20
+ this.resizeObserver = new ResizeObserver(() => this.updateSize());
21
+ this.resizeObserver.observe(this.container);
22
+
23
+ this.updateSize();
24
+ this.startAnimation();
25
+ }
26
+
27
+ setupCanvas() {
28
+ this.canvasContainer = document.createElement('div');
29
+ this.canvasContainer.className = 'eb-canvas-container';
30
+
31
+ this.canvas = document.createElement('canvas');
32
+ this.canvas.className = 'eb-canvas';
33
+ this.ctx = this.canvas.getContext('2d');
34
+
35
+ this.canvasContainer.appendChild(this.canvas);
36
+ this.container.prepend(this.canvasContainer);
37
+
38
+ this.glowLayers = document.createElement('div');
39
+ this.glowLayers.className = 'eb-layers';
40
+ this.glowLayers.innerHTML = `
41
+ <div class="eb-glow-1"></div>
42
+ <div class="eb-glow-2"></div>
43
+ <div class="eb-background-glow"></div>
44
+ `;
45
+ this.container.prepend(this.glowLayers);
46
+ }
47
+
48
+ setupStyles() {
49
+ this.container.classList.add('electric-border');
50
+ this.container.style.setProperty('--electric-border-color', this.options.color);
51
+ this.container.style.borderRadius = `${this.options.borderRadius}px`;
52
+ }
53
+
54
+ updateSize() {
55
+ const borderOffset = 60;
56
+ const rect = this.container.getBoundingClientRect();
57
+ this.width = rect.width + borderOffset * 2;
58
+ this.height = rect.height + borderOffset * 2;
59
+
60
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
61
+ this.canvas.width = this.width * dpr;
62
+ this.canvas.height = this.height * dpr;
63
+ this.canvas.style.width = `${this.width}px`;
64
+ this.canvas.style.height = `${this.height}px`;
65
+ this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
66
+ }
67
+
68
+ random(x) {
69
+ return (Math.sin(x * 12.9898) * 43758.5453) % 1;
70
+ }
71
+
72
+ noise2D(x, y) {
73
+ const i = Math.floor(x);
74
+ const j = Math.floor(y);
75
+ const fx = x - i;
76
+ const fy = y - j;
77
+
78
+ const a = this.random(i + j * 57);
79
+ const b = this.random(i + 1 + j * 57);
80
+ const c = this.random(i + (j + 1) * 57);
81
+ const d = this.random(i + 1 + (j + 1) * 57);
82
+
83
+ const ux = fx * fx * (3.0 - 2.0 * fx);
84
+ const uy = fy * fy * (3.0 - 2.0 * fy);
85
+
86
+ return a * (1 - ux) * (1 - uy) + b * ux * (1 - uy) + c * (1 - ux) * uy + d * ux * uy;
87
+ }
88
+
89
+ octavedNoise(x, octaves, lacunarity, gain, baseAmplitude, baseFrequency, time, seed, baseFlatness) {
90
+ let y = 0;
91
+ let amplitude = baseAmplitude;
92
+ let frequency = baseFrequency;
93
+
94
+ for (let i = 0; i < octaves; i++) {
95
+ let octaveAmplitude = amplitude;
96
+ if (i === 0) octaveAmplitude *= baseFlatness;
97
+ y += octaveAmplitude * this.noise2D(frequency * x + seed * 100, time * frequency * 0.3);
98
+ frequency *= lacunarity;
99
+ amplitude *= gain;
100
+ }
101
+ return y;
102
+ }
103
+
104
+ getCornerPoint(centerX, centerY, radius, startAngle, arcLength, progress) {
105
+ const angle = startAngle + progress * arcLength;
106
+ return {
107
+ x: centerX + radius * Math.cos(angle),
108
+ y: centerY + radius * Math.sin(angle)
109
+ };
110
+ }
111
+
112
+ getRoundedRectPoint(t, left, top, width, height, radius) {
113
+ const straightWidth = width - 2 * radius;
114
+ const straightHeight = height - 2 * radius;
115
+ const cornerArc = (Math.PI * radius) / 2;
116
+ const totalPerimeter = 2 * straightWidth + 2 * straightHeight + 4 * cornerArc;
117
+ const distance = t * totalPerimeter;
118
+
119
+ let accumulated = 0;
120
+
121
+ if (distance <= accumulated + straightWidth) {
122
+ return { x: left + radius + ((distance - accumulated) / straightWidth) * straightWidth, y: top };
123
+ }
124
+ accumulated += straightWidth;
125
+
126
+ if (distance <= accumulated + cornerArc) {
127
+ return this.getCornerPoint(left + width - radius, top + radius, radius, -Math.PI / 2, Math.PI / 2, (distance - accumulated) / cornerArc);
128
+ }
129
+ accumulated += cornerArc;
130
+
131
+ if (distance <= accumulated + straightHeight) {
132
+ return { x: left + width, y: top + radius + ((distance - accumulated) / straightHeight) * straightHeight };
133
+ }
134
+ accumulated += straightHeight;
135
+
136
+ if (distance <= accumulated + cornerArc) {
137
+ return this.getCornerPoint(left + width - radius, top + height - radius, radius, 0, Math.PI / 2, (distance - accumulated) / cornerArc);
138
+ }
139
+ accumulated += cornerArc;
140
+
141
+ if (distance <= accumulated + straightWidth) {
142
+ return { x: left + width - radius - ((distance - accumulated) / straightWidth) * straightWidth, y: top + height };
143
+ }
144
+ accumulated += straightWidth;
145
+
146
+ if (distance <= accumulated + cornerArc) {
147
+ return this.getCornerPoint(left + radius, top + height - radius, radius, Math.PI / 2, Math.PI / 2, (distance - accumulated) / cornerArc);
148
+ }
149
+ accumulated += cornerArc;
150
+
151
+ if (distance <= accumulated + straightHeight) {
152
+ return { x: left, y: top + height - radius - ((distance - accumulated) / straightHeight) * straightHeight };
153
+ }
154
+ accumulated += straightHeight;
155
+
156
+ return this.getCornerPoint(left + radius, top + radius, radius, Math.PI, Math.PI / 2, (distance - accumulated) / cornerArc);
157
+ }
158
+
159
+ draw(currentTime) {
160
+ if (!this.lastFrameTime) this.lastFrameTime = currentTime;
161
+ const deltaTime = (currentTime - this.lastFrameTime) / 1000;
162
+ this.time += deltaTime * this.options.speed;
163
+ this.lastFrameTime = currentTime;
164
+
165
+ const ctx = this.ctx;
166
+ const dpr = Math.min(window.devicePixelRatio || 1, 2);
167
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
168
+ ctx.clearRect(0, 0, this.width, this.height);
169
+
170
+ ctx.strokeStyle = this.options.color;
171
+ ctx.lineWidth = 1;
172
+ ctx.lineCap = 'round';
173
+ ctx.lineJoin = 'round';
174
+
175
+ const displacement = 60;
176
+ const borderOffset = 60;
177
+ const left = borderOffset;
178
+ const top = borderOffset;
179
+ const borderWidth = this.width - 2 * borderOffset;
180
+ const borderHeight = this.height - 2 * borderOffset;
181
+ const radius = Math.min(this.options.borderRadius, Math.min(borderWidth, borderHeight) / 2);
182
+
183
+ const approximatePerimeter = 2 * (borderWidth + borderHeight) + 2 * Math.PI * radius;
184
+ const sampleCount = Math.floor(approximatePerimeter / 2);
185
+
186
+ ctx.beginPath();
187
+ for (let i = 0; i <= sampleCount; i++) {
188
+ const progress = i / sampleCount;
189
+ const point = this.getRoundedRectPoint(progress, left, top, borderWidth, borderHeight, radius);
190
+
191
+ const xNoise = this.octavedNoise(progress * 8, 10, 1.6, 0.7, this.options.chaos, 10, this.time, 0, 0);
192
+ const yNoise = this.octavedNoise(progress * 8, 10, 1.6, 0.7, this.options.chaos, 10, this.time, 1, 0);
193
+
194
+ const dx = point.x + xNoise * displacement;
195
+ const dy = point.y + yNoise * displacement;
196
+
197
+ if (i === 0) ctx.moveTo(dx, dy);
198
+ else ctx.lineTo(dx, dy);
199
+ }
200
+ ctx.closePath();
201
+ ctx.stroke();
202
+
203
+ this.animationId = requestAnimationFrame((t) => this.draw(t));
204
+ }
205
+
206
+ startAnimation() {
207
+ this.animationId = requestAnimationFrame((t) => this.draw(t));
208
+ }
209
+
210
+ destroy() {
211
+ cancelAnimationFrame(this.animationId);
212
+ this.resizeObserver.disconnect();
213
+ }
214
+ }
frontend/Lightning.css ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .lightning-container {
2
+ width: 100%;
3
+ height: 100%;
4
+ position: fixed;
5
+ top: 0;
6
+ left: 0;
7
+ z-index: -1;
8
+ pointer-events: none;
9
+ opacity: 0.4;
10
+ /* Adjust opacity as needed for background */
11
+ }
frontend/Lightning.js ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class Lightning {
2
+ constructor(canvas, options = {}) {
3
+ this.canvas = canvas;
4
+ this.options = {
5
+ hue: options.hue || 230,
6
+ xOffset: options.xOffset || 0,
7
+ speed: options.speed || 1,
8
+ intensity: options.intensity || 1,
9
+ size: options.size || 1,
10
+ ...options
11
+ };
12
+
13
+ const gl = canvas.getContext('webgl');
14
+ if (!gl) {
15
+ console.error('WebGL not supported');
16
+ return;
17
+ }
18
+ this.gl = gl;
19
+
20
+ this.init();
21
+ this.resizeCanvas();
22
+ window.addEventListener('resize', () => this.resizeCanvas());
23
+ this.startTime = performance.now();
24
+ this.render();
25
+ }
26
+
27
+ init() {
28
+ const gl = this.gl;
29
+
30
+ const vertexShaderSource = `
31
+ attribute vec2 aPosition;
32
+ void main() {
33
+ gl_Position = vec4(aPosition, 0.0, 1.0);
34
+ }
35
+ `;
36
+
37
+ const fragmentShaderSource = `
38
+ precision mediump float;
39
+ uniform vec2 iResolution;
40
+ uniform float iTime;
41
+ uniform float uHue;
42
+ uniform float uXOffset;
43
+ uniform float uSpeed;
44
+ uniform float uIntensity;
45
+ uniform float uSize;
46
+
47
+ #define OCTAVE_COUNT 10
48
+
49
+ vec3 hsv2rgb(vec3 c) {
50
+ vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0,4.0,2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
51
+ return c.z * mix(vec3(1.0), rgb, c.y);
52
+ }
53
+
54
+ float hash11(float p) {
55
+ p = fract(p * .1031);
56
+ p *= p + 33.33;
57
+ p *= p + p;
58
+ return fract(p);
59
+ }
60
+
61
+ float hash12(vec2 p) {
62
+ vec3 p3 = fract(vec3(p.xyx) * .1031);
63
+ p3 += dot(p3, p3.yzx + 33.33);
64
+ return fract((p3.x + p3.y) * p3.z);
65
+ }
66
+
67
+ mat2 rotate2d(float theta) {
68
+ float c = cos(theta);
69
+ float s = sin(theta);
70
+ return mat2(c, -s, s, c);
71
+ }
72
+
73
+ float noise(vec2 p) {
74
+ vec2 ip = floor(p);
75
+ vec2 fp = fract(p);
76
+ float a = hash12(ip);
77
+ float b = hash12(ip + vec2(1.0, 0.0));
78
+ float c = hash12(ip + vec2(0.0, 1.0));
79
+ float d = hash12(ip + vec2(1.0, 1.0));
80
+
81
+ vec2 t = smoothstep(0.0, 1.0, fp);
82
+ return mix(mix(a, b, t.x), mix(c, d, t.x), t.y);
83
+ }
84
+
85
+ float fbm(vec2 p) {
86
+ float value = 0.0;
87
+ float amplitude = 0.5;
88
+ for (int i = 0; i < OCTAVE_COUNT; ++i) {
89
+ value += amplitude * noise(p);
90
+ p *= rotate2d(0.45);
91
+ p *= 2.0;
92
+ amplitude *= 0.5;
93
+ }
94
+ return value;
95
+ }
96
+
97
+ void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
98
+ vec2 uv = fragCoord / iResolution.xy;
99
+ uv = 2.0 * uv - 1.0;
100
+ uv.x *= iResolution.x / iResolution.y;
101
+ uv.x += uXOffset;
102
+
103
+ uv += 2.0 * fbm(uv * uSize + 0.8 * iTime * uSpeed) - 1.0;
104
+
105
+ float dist = abs(uv.x);
106
+ vec3 baseColor = hsv2rgb(vec3(uHue / 360.0, 0.7, 0.8));
107
+ vec3 col = baseColor * pow(mix(0.0, 0.07, hash11(iTime * uSpeed)) / dist, 1.0) * uIntensity;
108
+ col = pow(col, vec3(1.0));
109
+ fragColor = vec4(col, 1.0);
110
+ }
111
+
112
+ void main() {
113
+ mainImage(gl_FragColor, gl_FragCoord.xy);
114
+ }
115
+ `;
116
+
117
+ const compileShader = (source, type) => {
118
+ const shader = gl.createShader(type);
119
+ gl.shaderSource(shader, source);
120
+ gl.compileShader(shader);
121
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
122
+ console.error('Shader compile error:', gl.getShaderInfoLog(shader));
123
+ gl.deleteShader(shader);
124
+ return null;
125
+ }
126
+ return shader;
127
+ };
128
+
129
+ const vertexShader = compileShader(vertexShaderSource, gl.VERTEX_SHADER);
130
+ const fragmentShader = compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER);
131
+
132
+ const program = gl.createProgram();
133
+ gl.attachShader(program, vertexShader);
134
+ gl.attachShader(program, fragmentShader);
135
+ gl.linkProgram(program);
136
+ gl.useProgram(program);
137
+ this.program = program;
138
+
139
+ const vertices = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);
140
+ const vertexBuffer = gl.createBuffer();
141
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
142
+ gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
143
+
144
+ const aPosition = gl.getAttribLocation(program, 'aPosition');
145
+ gl.enableVertexAttribArray(aPosition);
146
+ gl.vertexAttribPointer(aPosition, 2, gl.FLOAT, false, 0, 0);
147
+
148
+ this.locations = {
149
+ iResolution: gl.getUniformLocation(program, 'iResolution'),
150
+ iTime: gl.getUniformLocation(program, 'iTime'),
151
+ uHue: gl.getUniformLocation(program, 'uHue'),
152
+ uXOffset: gl.getUniformLocation(program, 'uXOffset'),
153
+ uSpeed: gl.getUniformLocation(program, 'uSpeed'),
154
+ uIntensity: gl.getUniformLocation(program, 'uIntensity'),
155
+ uSize: gl.getUniformLocation(program, 'uSize')
156
+ };
157
+ }
158
+
159
+ resizeCanvas() {
160
+ const canvas = this.canvas;
161
+ canvas.width = canvas.clientWidth;
162
+ canvas.height = canvas.clientHeight;
163
+ this.gl.viewport(0, 0, canvas.width, canvas.height);
164
+ }
165
+
166
+ render() {
167
+ const gl = this.gl;
168
+ const canvas = this.canvas;
169
+ const currentTime = performance.now();
170
+
171
+ gl.uniform2f(this.locations.iResolution, canvas.width, canvas.height);
172
+ gl.uniform1f(this.locations.iTime, (currentTime - this.startTime) / 1000.0);
173
+ gl.uniform1f(this.locations.uHue, this.options.hue);
174
+ gl.uniform1f(this.locations.uXOffset, this.options.xOffset);
175
+ gl.uniform1f(this.locations.uSpeed, this.options.speed);
176
+ gl.uniform1f(this.locations.uIntensity, this.options.intensity);
177
+ gl.uniform1f(this.locations.uSize, this.options.size);
178
+
179
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
180
+ requestAnimationFrame(() => this.render());
181
+ }
182
+ }
frontend/index.html ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>AI RAG System</title>
8
+ <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600&display=swap" rel="stylesheet">
9
+ <link rel="stylesheet" href="ElectricBorder.css">
10
+ <link rel="stylesheet" href="Lightning.css">
11
+ <style>
12
+ :root {
13
+ --primary: #34eb8f;
14
+ --secondary: #2196f3;
15
+ --bg: #050505;
16
+ --card-bg: rgba(255, 255, 255, 0.03);
17
+ --text: #ffffff;
18
+ --error: #ff4444;
19
+ }
20
+
21
+ body {
22
+ font-family: 'Outfit', sans-serif;
23
+ background-color: var(--bg);
24
+ color: var(--text);
25
+ margin: 0;
26
+ display: flex;
27
+ justify-content: center;
28
+ align-items: center;
29
+ min-height: 100vh;
30
+ overflow-x: hidden;
31
+ padding: 20px;
32
+ }
33
+
34
+ /* Lightning Canvas fix */
35
+ canvas.lightning-container {
36
+ position: fixed;
37
+ top: 0;
38
+ left: 0;
39
+ width: 100vw;
40
+ height: 100vh;
41
+ z-index: -1;
42
+ pointer-events: none;
43
+ }
44
+
45
+ .container {
46
+ width: 90%;
47
+ max-width: 700px;
48
+ background: rgba(10, 10, 10, 0.7);
49
+ backdrop-filter: blur(25px);
50
+ padding: 2.5rem;
51
+ border-radius: 24px;
52
+ border: 1px solid rgba(255, 255, 255, 0.08);
53
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.82);
54
+ position: relative;
55
+ z-index: 10;
56
+ }
57
+
58
+ h1 {
59
+ text-align: center;
60
+ color: var(--primary);
61
+ font-weight: 600;
62
+ font-size: 2.2rem;
63
+ margin-bottom: 2rem;
64
+ letter-spacing: -0.5px;
65
+ text-shadow: 0 0 20px rgba(52, 235, 143, 0.4);
66
+ }
67
+
68
+ .section {
69
+ margin-bottom: 2rem;
70
+ padding: 1.5rem;
71
+ background: rgba(0, 0, 0, 0.4);
72
+ border-radius: 16px;
73
+ border: 1px solid rgba(255, 255, 255, 0.04);
74
+ }
75
+
76
+ .section-title {
77
+ font-size: 0.9rem;
78
+ color: #aaa;
79
+ margin-bottom: 1rem;
80
+ text-transform: uppercase;
81
+ letter-spacing: 1.5px;
82
+ font-weight: 600;
83
+ }
84
+
85
+ .input-group {
86
+ display: flex;
87
+ gap: 12px;
88
+ }
89
+
90
+ input[type="text"],
91
+ input[type="file"] {
92
+ flex: 1;
93
+ background: rgba(255, 255, 255, 0.06);
94
+ border: 1px solid rgba(255, 255, 255, 0.1);
95
+ padding: 14px 20px;
96
+ border-radius: 12px;
97
+ color: white;
98
+ outline: none;
99
+ transition: all 0.3s ease;
100
+ font-size: 1rem;
101
+ }
102
+
103
+ input:focus {
104
+ border-color: var(--primary);
105
+ background: rgba(255, 255, 255, 0.1);
106
+ box-shadow: 0 0 15px rgba(52, 235, 143, 0.15);
107
+ }
108
+
109
+ button {
110
+ padding: 12px 28px;
111
+ border-radius: 12px;
112
+ font-weight: 600;
113
+ cursor: pointer;
114
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
115
+ border: none;
116
+ display: flex;
117
+ align-items: center;
118
+ justify-content: center;
119
+ font-size: 1rem;
120
+ }
121
+
122
+ .btn-query {
123
+ background: var(--primary);
124
+ color: #000;
125
+ }
126
+
127
+ .btn-upload {
128
+ background: rgba(255, 255, 255, 0.05);
129
+ color: #fff;
130
+ border: 1px solid rgba(255, 255, 255, 0.15);
131
+ }
132
+
133
+ button:hover {
134
+ transform: translateY(-3px);
135
+ filter: brightness(1.2);
136
+ box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
137
+ }
138
+
139
+ #response {
140
+ background: rgba(0, 0, 0, 0.6);
141
+ padding: 1.5rem;
142
+ border-radius: 14px;
143
+ min-height: 100px;
144
+ line-height: 1.8;
145
+ font-size: 1.05rem;
146
+ border: 1px solid rgba(255, 255, 255, 0.05);
147
+ margin-top: 1rem;
148
+ max-height: 400px;
149
+ overflow-y: auto;
150
+ white-space: pre-wrap;
151
+ }
152
+
153
+ .loader {
154
+ display: none;
155
+ width: 18px;
156
+ height: 18px;
157
+ border: 3px solid rgba(255, 255, 255, .3);
158
+ border-radius: 50%;
159
+ border-top-color: #fff;
160
+ animation: spin 0.8s linear infinite;
161
+ margin-right: 12px;
162
+ }
163
+
164
+ @keyframes spin {
165
+ to {
166
+ transform: rotate(360deg);
167
+ }
168
+ }
169
+
170
+ .status {
171
+ padding: 8px 12px;
172
+ border-radius: 8px;
173
+ display: inline-block;
174
+ }
175
+
176
+ .error {
177
+ color: var(--error);
178
+ border-left: 3px solid var(--error);
179
+ background: rgba(255, 68, 68, 0.05);
180
+ padding-left: 15px;
181
+ }
182
+
183
+ .success {
184
+ color: var(--primary);
185
+ }
186
+ </style>
187
+ </head>
188
+
189
+ <body>
190
+ <!-- Lightning Background -->
191
+ <canvas id="lightningCanvas" class="lightning-container"></canvas>
192
+
193
+ <div id="mainWrapper" style="display: inline-block; position: relative; border-radius: 24px;">
194
+ <div class="container" id="appContainer">
195
+ <h1>AI RAG Assistant</h1>
196
+
197
+ <div class="section">
198
+ <div class="section-title">1. Upload Documents</div>
199
+ <div class="input-group">
200
+ <input type="file" id="pdfFile" accept=".pdf,.txt">
201
+ <button class="btn-upload" onclick="upload()">
202
+ <div id="uploadLoader" class="loader"></div>
203
+ Upload
204
+ </button>
205
+ </div>
206
+ <div style="margin-top: 15px; display: flex; align-items: center; gap: 10px; font-size: 0.9rem;">
207
+ <span style="color: #666;">No file ready?</span>
208
+ <button onclick="simulateUpload()"
209
+ style="background: rgba(52, 235, 143, 0.1); padding: 6px 14px; border: 1px solid var(--primary); color: var(--primary); font-size: 0.85rem; border-radius: 8px;">
210
+ Try with Sample Data
211
+ </button>
212
+ </div>
213
+ <div id="uploadStatus" class="status" style="margin-top: 15px; font-size: 0.95rem;"></div>
214
+ </div>
215
+
216
+ <div class="section">
217
+ <div class="section-title">2. Ask Questions</div>
218
+ <div class="input-group">
219
+ <input type="text" id="question" placeholder="Ask something about your docs...">
220
+ <button class="btn-query" onclick="ask()">
221
+ <div id="askLoader" class="loader"></div>
222
+ Ask
223
+ </button>
224
+ </div>
225
+ <div id="response">Waiting for query...</div>
226
+ </div>
227
+
228
+ <div style="text-align: center; opacity: 0.4; font-size: 0.75rem; margin-top: 10px;">
229
+ Powered by HuggingFace (Free Embeddings) & Groq AI
230
+ </div>
231
+ </div>
232
+ </div>
233
+
234
+ <script src="ElectricBorder.js"></script>
235
+ <script src="Lightning.js"></script>
236
+ <script>
237
+ // Initialize Effects
238
+ window.addEventListener('load', () => {
239
+ // Neon Border
240
+ new ElectricBorder(document.getElementById('mainWrapper'), {
241
+ color: '#34eb8f',
242
+ speed: 1.2,
243
+ chaos: 0.15,
244
+ borderRadius: 24
245
+ });
246
+
247
+ // Lightning Background
248
+ new Lightning(document.getElementById('lightningCanvas'), {
249
+ hue: 150, // Matches the green theme (120-150 range)
250
+ xOffset: 0,
251
+ speed: 0.8,
252
+ intensity: 1.2,
253
+ size: 0.8
254
+ });
255
+ });
256
+
257
+ async function simulateUpload() {
258
+ const statusDiv = document.getElementById('uploadStatus');
259
+ statusDiv.innerText = "Simulating sample upload...";
260
+ statusDiv.className = 'status';
261
+
262
+ try {
263
+ const res = await fetch('/download-sample');
264
+ const text = await res.text();
265
+
266
+ const file = new File([text], 'sample_data.txt', { type: 'text/plain' });
267
+ const formData = new FormData();
268
+ formData.append('file', file);
269
+
270
+ const response = await fetch('/upload', {
271
+ method: 'POST',
272
+ body: formData
273
+ });
274
+ const data = await response.json();
275
+
276
+ if (response.ok) {
277
+ statusDiv.className = 'status success';
278
+ statusDiv.innerText = "✅ Sample Data Loaded! Ask your questions.";
279
+ } else {
280
+ statusDiv.className = 'status error';
281
+ statusDiv.innerText = "Error: " + (data.detail || "Upload failed");
282
+ }
283
+ } catch (e) {
284
+ statusDiv.className = 'status error';
285
+ statusDiv.innerText = "Simulation failed: " + e.message;
286
+ }
287
+ }
288
+
289
+ async function upload() {
290
+ const fileInput = document.getElementById('pdfFile');
291
+ const statusDiv = document.getElementById('uploadStatus');
292
+ const loader = document.getElementById('uploadLoader');
293
+
294
+ if (!fileInput.files[0]) {
295
+ statusDiv.className = 'status error';
296
+ statusDiv.innerText = "Please select a file first.";
297
+ return;
298
+ }
299
+
300
+ const formData = new FormData();
301
+ formData.append('file', fileInput.files[0]);
302
+
303
+ loader.style.display = 'block';
304
+ statusDiv.innerText = "Uploading & indexing...";
305
+
306
+ try {
307
+ const response = await fetch('/upload', {
308
+ method: 'POST',
309
+ body: formData
310
+ });
311
+ const data = await response.json();
312
+
313
+ if (response.ok) {
314
+ statusDiv.className = 'status success';
315
+ statusDiv.innerText = "✅ " + (data.message || "Upload successful!");
316
+ } else {
317
+ statusDiv.className = 'status error';
318
+ statusDiv.innerText = "Error: " + (data.detail || data.message || "Upload failed");
319
+ }
320
+ } catch (e) {
321
+ statusDiv.className = 'status error';
322
+ statusDiv.innerText = "Connection failed: " + e.message;
323
+ } finally {
324
+ loader.style.display = 'none';
325
+ }
326
+ }
327
+
328
+ async function ask() {
329
+ const qInput = document.getElementById('question');
330
+ const question = qInput.value;
331
+ const resDiv = document.getElementById('response');
332
+ const loader = document.getElementById('askLoader');
333
+
334
+ if (!question) return;
335
+
336
+ loader.style.display = 'block';
337
+ resDiv.className = '';
338
+ resDiv.innerText = "Searching documents and thinking...";
339
+
340
+ try {
341
+ const response = await fetch('/ask', {
342
+ method: 'POST',
343
+ headers: { 'Content-Type': 'application/json' },
344
+ body: JSON.stringify({ question: question })
345
+ });
346
+ const data = await response.json();
347
+
348
+ if (response.ok) {
349
+ resDiv.innerText = data.answer;
350
+ } else {
351
+ resDiv.className = 'error';
352
+ resDiv.innerText = "Server Error: " + (data.detail || "Internal Server Error");
353
+ }
354
+ } catch (e) {
355
+ resDiv.className = 'error';
356
+ resDiv.innerText = "Error: Could not connect to the server.";
357
+ } finally {
358
+ loader.style.display = 'none';
359
+ }
360
+ }
361
+ </script>
362
+ </body>
363
+
364
+ </html>