nasse-js 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 ADDED
@@ -0,0 +1,75 @@
1
+ # ⚡ Ozone JS
2
+
3
+ **The React Framework for Humans.**
4
+
5
+ Ozone JS es un framework de React moderno, intuitivo y extremadamente rápido, diseñado para ofrecer la potencia de Next.js con una fracción de la complejidad. Cero configuración, máximo rendimiento.
6
+
7
+ ## ✨ Características Principales
8
+
9
+ - 🚀 **Zero Config**: Empieza a desarrollar al instante. Todo está pre-configurado.
10
+ - 📁 **File-System Routing**: Tu estructura de carpetas en `src/app` define tus rutas automáticamente.
11
+ - 🌐 **Native SSR**: Soporte integrado para Server-Side Rendering mediante funciones `server()` exportadas.
12
+ - 🎨 **Premium UX**: Estética moderna por defecto, optimizada para desarrolladores y usuarios finales.
13
+ - 📦 **NPM Ready**: Andamiaje profesional listo para producción.
14
+
15
+ ## 🚀 Inicio Rápido
16
+
17
+ Para crear un nuevo proyecto de Ozone JS:
18
+
19
+ ```bash
20
+ npx create-ozone-app mi-proyecto
21
+ cd mi-proyecto
22
+ npm install
23
+ npm run dev
24
+ ```
25
+
26
+ ## 📂 Estructura del Proyecto
27
+
28
+ ```text
29
+ mi-proyecto/
30
+ ├── src/
31
+ │ └── app/
32
+ │ ├── _layout.tsx # Layout raíz (compartido)
33
+ │ └── index.tsx # Página de inicio (/)
34
+ ├── public/ # Assets estáticos (logos, icons)
35
+ ├── ozone.config.mjs # Configuración del framework
36
+ └── package.json
37
+ ```
38
+
39
+ ## 🛠️ Cómo funciona
40
+
41
+ ### 1. El Layout Compartido (`_layout.tsx`)
42
+ Define la cáscara HTML de tu aplicación. Es un componente de React estándar que recibe `children`.
43
+
44
+ ```tsx
45
+ export default function RootLayout({ children }) {
46
+ return (
47
+ <html lang="es">
48
+ <body>{children}</body>
49
+ </html>
50
+ );
51
+ }
52
+ ```
53
+
54
+ ### 2. Páginas y Data Fetching (`index.tsx`)
55
+ Puedes exportar una función `server` para cargar datos en el servidor antes de renderizar el componente.
56
+
57
+ ```tsx
58
+ export const server = async () => {
59
+ return { message: "Hola desde el servidor" };
60
+ };
61
+
62
+ export default function Home({ message }) {
63
+ return <h1>{message}</h1>;
64
+ }
65
+ ```
66
+
67
+ ## 💻 Comandos del CLI
68
+
69
+ - `ozone dev`: Inicia el servidor de desarrollo con HMR.
70
+ - `ozone build`: Prepara la aplicación para producción (Próximamente).
71
+ - `ozone start`: Levanta el servidor de producción.
72
+
73
+ ---
74
+
75
+ Diseñado con ❤️ para millones de desarrolladores. **Ozone JS** es el futuro de la web simple.
@@ -0,0 +1,335 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import chalk from 'chalk';
6
+ import inquirer from 'inquirer';
7
+ import { execSync } from 'child_process';
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ async function init() {
11
+ console.log(`\n ${chalk.bold.hex('#8b5cf6')('⚡')} ${chalk.bold('OZONE JS')} - The React Framework for Humans\n`);
12
+ const answers = await inquirer.prompt([
13
+ {
14
+ type: 'input',
15
+ name: 'projectName',
16
+ message: 'What is your project named?',
17
+ default: 'my-ozone-app',
18
+ validate: (input) => {
19
+ if (/^([A-Za-z\-\_\d])+$/.test(input))
20
+ return true;
21
+ return 'Project name may only include letters, numbers, underscores and dashes.';
22
+ }
23
+ },
24
+ {
25
+ type: 'confirm',
26
+ name: 'useTailwind',
27
+ message: 'Would you like to use Tailwind CSS?',
28
+ default: true
29
+ }
30
+ ]);
31
+ const targetDir = path.join(process.cwd(), answers.projectName);
32
+ if (fs.existsSync(targetDir)) {
33
+ console.error(chalk.red(`\nDirectory ${answers.projectName} already exists. Please choose another name.\n`));
34
+ process.exit(1);
35
+ }
36
+ console.log(`\nCreating a new Ozone app in ${chalk.green(targetDir)}.`);
37
+ fs.mkdirSync(targetDir, { recursive: true });
38
+ // Create standard folders
39
+ fs.mkdirSync(path.join(targetDir, 'app'), { recursive: true });
40
+ fs.mkdirSync(path.join(targetDir, 'public'), { recursive: true });
41
+ fs.mkdirSync(path.join(targetDir, 'components'), { recursive: true });
42
+ fs.mkdirSync(path.join(targetDir, 'assets'), { recursive: true });
43
+ // --- 1. GENERATE APP FILES ---
44
+ // app/index.tsx
45
+ fs.writeFileSync(path.join(targetDir, 'app', 'index.tsx'), `import React from 'react';
46
+
47
+ export const meta = {
48
+ title: "Welcome to Ozone JS",
49
+ description: "The React framework for humans."
50
+ };
51
+
52
+ export const server = async () => {
53
+ return {
54
+ message: "Server-side loaded data!"
55
+ };
56
+ };
57
+
58
+ export default function Home({ message }: { message: string }) {
59
+ return (
60
+ <div ${answers.useTailwind ? 'className="min-h-screen bg-gray-950 text-white flex flex-col items-center justify-center p-8"' : 'style={{ minHeight: "100vh", background: "#09090b", color: "white", padding: "2rem", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", fontFamily: "system-ui" }}'}>
61
+ <img src="/logo.svg" alt="Ozone Logo" ${answers.useTailwind ? 'className="w-24 h-24 mb-6"' : 'style={{ width: "96px", marginBottom: "1.5rem" }}'} />
62
+ <h1 ${answers.useTailwind ? 'className="text-4xl font-bold text-violet-500 mb-4"' : 'style={{ fontSize: "2.5rem", color: "#8b5cf6", margin: "0 0 1rem 0" }}'}>⚡ Ozone JS</h1>
63
+ <p ${answers.useTailwind ? 'className="text-gray-400 mb-8"' : 'style={{ color: "#a1a1aa", marginBottom: "2rem" }}'}>Welcome to your new app. Edit <code>app/index.tsx</code> to get started.</p>
64
+ <div ${answers.useTailwind ? 'className="p-4 bg-gray-900 rounded-lg border border-gray-800"' : 'style={{ padding: "1rem", background: "#18181b", borderRadius: "8px", border: "1px solid #27272a" }}'}>
65
+ <strong ${answers.useTailwind ? 'className="text-violet-400"' : 'style={{ color: "#a78bfa" }}'}>Data from server:</strong> {message}
66
+ </div>
67
+ </div>
68
+ );
69
+ }
70
+ `);
71
+ // app/_layout.tsx
72
+ const globalCssImport = answers.useTailwind ? `import '../assets/global.css';\n\n` : '';
73
+ fs.writeFileSync(path.join(targetDir, 'app', '_layout.tsx'), `import React from 'react';\n${globalCssImport}export default function RootLayout({ children }: { children: React.ReactNode }) {
74
+ return (
75
+ <html lang="en">
76
+ <head>
77
+ <meta charSet="utf-8" />
78
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
79
+ <link rel="icon" href="/favicon.ico" />
80
+ </head>
81
+ <body ${!answers.useTailwind ? 'style={{ margin: 0 }}' : ''}>
82
+ {children}
83
+ </body>
84
+ </html>
85
+ );
86
+ }
87
+ `);
88
+ // --- 2. GENERATE PUBLIC ASSETS ---
89
+ // public/favicon.ico (minimal base64 SVG or just an SVG masquerading as ICO for modern browsers)
90
+ fs.writeFileSync(path.join(targetDir, 'public', 'favicon.ico'), `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">⚡</text></svg>`);
91
+ // public/logo.svg
92
+ fs.writeFileSync(path.join(targetDir, 'public', 'logo.svg'), `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#8b5cf6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/></svg>`);
93
+ // public/globe.svg
94
+ fs.writeFileSync(path.join(targetDir, 'public', 'globe.svg'), `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/><path d="M2 12h20"/></svg>`);
95
+ // --- 3. GENERATE ROOT CONFIG FILES ---
96
+ // .gitignore (100% git compatible)
97
+ fs.writeFileSync(path.join(targetDir, '.gitignore'), `# See https://help.github.com/articles/ignoring-files/
98
+
99
+ # dependencies
100
+ /node_modules
101
+ /.pnp
102
+ .pnp.js
103
+
104
+ # testing
105
+ /coverage
106
+
107
+ # ozone
108
+ .ozone
109
+ /dist
110
+ /build
111
+
112
+ # misc
113
+ .DS_Store
114
+ *.pem
115
+
116
+ # debug
117
+ npm-debug.log*
118
+ yarn-debug.log*
119
+ yarn-error.log*
120
+
121
+ # local env files
122
+ .env.local
123
+ .env.development.local
124
+ .env.test.local
125
+ .env.production.local
126
+ `);
127
+ // .env.local
128
+ fs.writeFileSync(path.join(targetDir, '.env.local'), `# Local environment variables
129
+ OZONE_PUBLIC_API_URL=http://localhost:3000/api
130
+ `);
131
+ // AGENTS.md
132
+ fs.writeFileSync(path.join(targetDir, 'AGENTS.md'), `# Project Agents Guidelines
133
+
134
+ This document provides context for AI agents working on this Ozone JS project.
135
+
136
+ ## Stack
137
+ - Framework: Ozone JS (React 18+)
138
+ - Language: TypeScript
139
+ - Styling: ${answers.useTailwind ? 'Tailwind CSS' : 'Standard CSS'}
140
+
141
+ ## Rules
142
+ 1. Use \`app/index.tsx\` for the main page.
143
+ 2. Server-side logic goes into \`export const server\` within page components.
144
+ 3. Components belong in \`/components\`.
145
+ `);
146
+ // CLAUDE.md
147
+ fs.writeFileSync(path.join(targetDir, 'CLAUDE.md'), `# Claude Assistant Guidelines
148
+
149
+ When assisting with this Ozone JS project, please follow these rules:
150
+ - Prioritize React 18 functional components and hooks.
151
+ - Respect the file-based routing system in the \`app/\` directory.
152
+ - Use \`ozone.config.mjs\` for framework-specific settings.
153
+ `);
154
+ // jsconfig.json
155
+ fs.writeFileSync(path.join(targetDir, 'jsconfig.json'), JSON.stringify({
156
+ compilerOptions: {
157
+ baseUrl: ".",
158
+ paths: {
159
+ "@/*": ["./*"]
160
+ }
161
+ }
162
+ }, null, 2));
163
+ // eslint.config.mjs
164
+ fs.writeFileSync(path.join(targetDir, 'eslint.config.mjs'), `import js from "@eslint/js";
165
+ import reactPlugin from "eslint-plugin-react";
166
+
167
+ export default [
168
+ js.configs.recommended,
169
+ {
170
+ files: ["**/*.{js,jsx,mjs,cjs,ts,tsx}"],
171
+ plugins: {
172
+ react: reactPlugin,
173
+ },
174
+ rules: {
175
+ "react/react-in-jsx-scope": "off",
176
+ },
177
+ languageOptions: {
178
+ parserOptions: {
179
+ ecmaFeatures: {
180
+ jsx: true,
181
+ },
182
+ },
183
+ },
184
+ },
185
+ ];
186
+ `);
187
+ // ozone.config.mjs
188
+ fs.writeFileSync(path.join(targetDir, 'ozone.config.mjs'), `/** @type {import('ozone-js').OzoneConfig} */
189
+ const ozoneConfig = {
190
+ reactStrictMode: true,
191
+ // Add Ozone specific configurations here
192
+ };
193
+
194
+ export default ozoneConfig;
195
+ `);
196
+ // ozone-env.d.ts
197
+ fs.writeFileSync(path.join(targetDir, 'ozone-env.d.ts'), `/// <reference types="ozone-js" />
198
+ /// <reference types="react" />
199
+ /// <reference types="react-dom" />
200
+
201
+ // NOTE: This file should not be edited
202
+ `);
203
+ // README.md
204
+ fs.writeFileSync(path.join(targetDir, 'README.md'), `# ${answers.projectName}
205
+
206
+ This is a modern web application built with [Ozone JS](https://github.com/nasse-ozone).
207
+
208
+ ## Getting Started
209
+
210
+ First, run the development server:
211
+
212
+ \`\`\`bash
213
+ npm run dev
214
+ # or
215
+ yarn dev
216
+ # or
217
+ pnpm dev
218
+ \`\`\`
219
+
220
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
221
+
222
+ You can start editing the page by modifying \`app/index.tsx\`. The page auto-updates as you edit the file.
223
+ `);
224
+ // --- 4. OPTIONAL TAILWIND ---
225
+ if (answers.useTailwind) {
226
+ fs.writeFileSync(path.join(targetDir, 'tailwind.config.ts'), `import type { Config } from 'tailwindcss'
227
+
228
+ const config: Config = {
229
+ content: [
230
+ './app/**/*.{js,ts,jsx,tsx,mdx}',
231
+ './components/**/*.{js,ts,jsx,tsx,mdx}',
232
+ ],
233
+ theme: {
234
+ extend: {
235
+ colors: {
236
+ ozone: '#8b5cf6',
237
+ },
238
+ },
239
+ },
240
+ plugins: [],
241
+ }
242
+ export default config
243
+ `);
244
+ fs.writeFileSync(path.join(targetDir, 'postcss.config.mjs'), `export default {
245
+ plugins: {
246
+ tailwindcss: {},
247
+ autoprefixer: {},
248
+ },
249
+ };
250
+ `);
251
+ fs.writeFileSync(path.join(targetDir, 'assets', 'global.css'), `@tailwind base;
252
+ @tailwind components;
253
+ @tailwind utilities;
254
+
255
+ :root {
256
+ color-scheme: dark;
257
+ }
258
+ `);
259
+ }
260
+ // --- 5. PACKAGE.JSON & TSCONFIG ---
261
+ const dependencies = {
262
+ "react": "^18.2.0",
263
+ "react-dom": "^18.2.0"
264
+ };
265
+ const devDependencies = {
266
+ "@types/react": "^18.2.0",
267
+ "@types/react-dom": "^18.2.0",
268
+ "typescript": "^5.2.2",
269
+ "ozone-js": "file:.."
270
+ };
271
+ if (answers.useTailwind) {
272
+ devDependencies["tailwindcss"] = "^3.4.1";
273
+ devDependencies["postcss"] = "^8.4.35";
274
+ devDependencies["autoprefixer"] = "^10.4.17";
275
+ }
276
+ const pkgJson = {
277
+ name: answers.projectName,
278
+ version: "0.1.0",
279
+ private: true,
280
+ type: "module",
281
+ scripts: {
282
+ dev: "ozone dev",
283
+ build: "ozone build",
284
+ start: "ozone start"
285
+ },
286
+ dependencies,
287
+ devDependencies
288
+ };
289
+ fs.writeFileSync(path.join(targetDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
290
+ const tsconfig = {
291
+ "compilerOptions": {
292
+ "target": "ES2022",
293
+ "lib": ["DOM", "DOM.Iterable", "ES2022"],
294
+ "allowJs": true,
295
+ "skipLibCheck": true,
296
+ "strict": true,
297
+ "noEmit": true,
298
+ "esModuleInterop": true,
299
+ "module": "ESNext",
300
+ "moduleResolution": "bundler",
301
+ "resolveJsonModule": true,
302
+ "isolatedModules": true,
303
+ "jsx": "preserve",
304
+ "incremental": true,
305
+ "plugins": [
306
+ {
307
+ "name": "next"
308
+ }
309
+ ],
310
+ "paths": {
311
+ "@/*": ["./*"]
312
+ }
313
+ },
314
+ "include": ["ozone-env.d.ts", "**/*.ts", "**/*.tsx", ".ozone/types/**/*.ts"],
315
+ "exclude": ["node_modules"]
316
+ };
317
+ fs.writeFileSync(path.join(targetDir, 'tsconfig.json'), JSON.stringify(tsconfig, null, 2));
318
+ // --- 6. INITIALIZE GIT ---
319
+ try {
320
+ execSync('git init', { cwd: targetDir, stdio: 'ignore' });
321
+ execSync('git add .', { cwd: targetDir, stdio: 'ignore' });
322
+ // Don't commit yet to allow user to modify
323
+ }
324
+ catch (e) {
325
+ // Git might not be installed
326
+ }
327
+ console.log(`\n${chalk.green('Success!')} Created ${answers.projectName}.`);
328
+ console.log(`Inside that directory, you can run:\n`);
329
+ console.log(` ${chalk.cyan('npm install')}`);
330
+ console.log(` ${chalk.cyan('npm run dev')}\n`);
331
+ console.log(`Happy coding! ⚡\n`);
332
+ }
333
+ init().catch((e) => {
334
+ console.error(e);
335
+ });
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import chalk from 'chalk';
4
+ import path from 'path';
5
+ import { createServer } from 'vite';
6
+ import express from 'express';
7
+ const program = new Command();
8
+ program
9
+ .name('ozone')
10
+ .description('Ozone JS - The React Framework for Humans')
11
+ .version('1.0.0');
12
+ program
13
+ .command('dev')
14
+ .description('Start the development server')
15
+ .action(async () => {
16
+ console.log(`\n ${chalk.bold.hex('#8b5cf6')('⚡')} ${chalk.bold('OZONE JS')} v1.0.0\n`);
17
+ const app = express();
18
+ // Create Vite server in middleware mode
19
+ const vite = await createServer({
20
+ server: { middlewareMode: true },
21
+ appType: 'custom',
22
+ resolve: {
23
+ alias: {
24
+ '@': path.resolve(process.cwd(), './app')
25
+ }
26
+ }
27
+ });
28
+ // Use vite's connect instance as middleware
29
+ app.use(vite.middlewares);
30
+ app.use('*', async (req, res, next) => {
31
+ const url = req.originalUrl;
32
+ try {
33
+ // In a complete implementation, this would map the URL to the file system route,
34
+ // evaluate the 'server' export, inject it into the HTML, and SSR the component.
35
+ // For this minimal version, we will serve a basic HTML shell that loads client entry.
36
+ // This is a placeholder for the actual SSR logic.
37
+ const template = `<!DOCTYPE html>
38
+ <html lang="en">
39
+ <head>
40
+ <meta charset="UTF-8" />
41
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
42
+ <title>Ozone App</title>
43
+ <!-- In a real app we'd inject vite client and the entrypoint -->
44
+ </head>
45
+ <body>
46
+ <div id="root"></div>
47
+ <script type="module">
48
+ // Import the file system router and mount the app
49
+ console.log('Ozone JS client mounted!');
50
+ </script>
51
+ </body>
52
+ </html>`;
53
+ const html = await vite.transformIndexHtml(url, template);
54
+ res.status(200).set({ 'Content-Type': 'text/html' }).end(html);
55
+ }
56
+ catch (e) {
57
+ vite.ssrFixStacktrace(e);
58
+ next(e);
59
+ }
60
+ });
61
+ const port = 3000;
62
+ app.listen(port, () => {
63
+ console.log(` ${chalk.green('➜')} Local: ${chalk.cyan(`http://localhost:${port}/`)}`);
64
+ console.log(` ${chalk.green('➜')} Network: ${chalk.dim('use --host to expose')}\n`);
65
+ });
66
+ });
67
+ program
68
+ .command('build')
69
+ .description('Build the app for production')
70
+ .action(() => {
71
+ console.log(chalk.yellow('\nBuilding for production is not implemented in this minimal prototype.\n'));
72
+ });
73
+ program
74
+ .command('start')
75
+ .description('Start the production server')
76
+ .action(() => {
77
+ console.log(chalk.yellow('\nProduction server is not implemented in this minimal prototype.\n'));
78
+ });
79
+ program.parse(process.argv);
@@ -0,0 +1,9 @@
1
+ /** @type {import('@nasse-ozone/core').OzoneConfig} */
2
+ const ozoneConfig = {
3
+ reactStrictMode: true,
4
+ compiler: {
5
+ styledComponents: true,
6
+ },
7
+ };
8
+
9
+ export default ozoneConfig;
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "nasse-js",
3
+ "version": "1.0.0",
4
+ "description": "The React framework for humans",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-ozone-app": "./dist/cli/create.js",
8
+ "ozone": "./dist/cli/ozone.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "public",
13
+ "package.json",
14
+ "README.md",
15
+ "ozone.config.mjs"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc"
19
+ },
20
+ "dependencies": {
21
+ "chalk": "^5.3.0",
22
+ "commander": "^11.1.0",
23
+ "compression": "^1.8.1",
24
+ "express": "^4.18.2",
25
+ "inquirer": "^9.2.12",
26
+ "react": "^18.2.0",
27
+ "react-dom": "^18.2.0",
28
+ "react-router-dom": "^6.22.0",
29
+ "sirv": "^2.0.4",
30
+ "vite": "^5.1.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/compression": "^1.8.1",
34
+ "@types/express": "^4.17.21",
35
+ "@types/inquirer": "^9.0.9",
36
+ "@types/node": "^20.11.0",
37
+ "@types/react": "^18.2.0",
38
+ "@types/react-dom": "^18.2.0",
39
+ "typescript": "^5.3.0"
40
+ }
41
+ }
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">⚡</text></svg>
@@ -0,0 +1,3 @@
1
+ <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M18 2L8 18H14L11 30L24 13H18L21 2Z" fill="#8b5cf6"/>
3
+ </svg>
@@ -0,0 +1,5 @@
1
+ <svg width="100" height="100" viewBox="0 0 24 24" fill="none" stroke="#52525b" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">
2
+ <circle cx="12" cy="12" r="10"/>
3
+ <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>
4
+ <path d="M2 12h20"/>
5
+ </svg>
@@ -0,0 +1,16 @@
1
+ <svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="boltGradient" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#a78bfa" />
5
+ <stop offset="100%" stop-color="#8b5cf6" />
6
+ </linearGradient>
7
+ <filter id="glow">
8
+ <feGaussianBlur stdDeviation="2" result="coloredBlur"/>
9
+ <feMerge>
10
+ <feMergeNode in="coloredBlur"/>
11
+ <feMergeNode in="SourceGraphic"/>
12
+ </feMerge>
13
+ </filter>
14
+ </defs>
15
+ <path d="M55 5L25 55H45L35 95L75 40H55L65 5Z" fill="url(#boltGradient)" filter="url(#glow)"/>
16
+ </svg>