boilercraft 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/LICENSE +21 -0
- package/README.md +171 -0
- package/bin/boilercraft.js +110 -0
- package/cli/app.js +596 -0
- package/cli/ui.js +356 -0
- package/docs/STACK_ENGINE.md +93 -0
- package/docs/assets/cli-create.svg +52 -0
- package/docs/assets/cli-menu.svg +47 -0
- package/docs/stacks/dotnet-core.md +69 -0
- package/docs/stacks/laravel-vue.md +46 -0
- package/docs/stacks/nextjs.md +58 -0
- package/docs/stacks/node-express.md +58 -0
- package/docs/stacks/raw-php.md +130 -0
- package/main.js +82 -0
- package/package.json +86 -0
- package/public/index.html +721 -0
- package/public/logo.png +17 -0
- package/public/logo.svg +17 -0
- package/server.js +84 -0
- package/services/componentGenerator.js +195 -0
- package/services/engine/scaffoldEngine.js +517 -0
- package/services/engine/toolInstaller.js +232 -0
- package/services/engine/versionCatalog.js +115 -0
- package/services/generate.js +46 -0
- package/services/projectGenerator.js +686 -0
- package/services/readmeGenerator.js +221 -0
- package/services/themeGenerator.js +150 -0
- package/services/versionResolver.js +176 -0
- package/stacks/dotnet-core/overlays/api/Controllers/ProductsController.cs +33 -0
- package/stacks/dotnet-core/overlays/api/Program.cs +33 -0
- package/stacks/dotnet-core/overlays/api/wwwroot/index.html +38 -0
- package/stacks/dotnet-core/overlays/api-auth/Controllers/AuthController.cs +87 -0
- package/stacks/dotnet-core/overlays/common/Data/ApplicationDbContext.cs +25 -0
- package/stacks/dotnet-core/overlays/common/Models/Product.cs +19 -0
- package/stacks/dotnet-core/overlays/mvc/Controllers/ProductsController.cs +84 -0
- package/stacks/dotnet-core/overlays/mvc/Views/Home/Index.cshtml +23 -0
- package/stacks/dotnet-core/overlays/mvc/Views/Products/Create.cshtml +16 -0
- package/stacks/dotnet-core/overlays/mvc/Views/Products/Delete.cshtml +16 -0
- package/stacks/dotnet-core/overlays/mvc/Views/Products/Edit.cshtml +17 -0
- package/stacks/dotnet-core/overlays/mvc/Views/Products/Index.cshtml +46 -0
- package/stacks/dotnet-core/overlays/mvc/Views/Products/_Form.cshtml +19 -0
- package/stacks/dotnet-core/overlays/mvc-auth/Controllers/AccountController.cs +98 -0
- package/stacks/dotnet-core/overlays/mvc-auth/Models/AppUser.cs +48 -0
- package/stacks/dotnet-core/overlays/mvc-auth/Views/Account/Login.cshtml +35 -0
- package/stacks/dotnet-core/overlays/mvc-auth/Views/Account/Register.cshtml +41 -0
- package/stacks/dotnet-core/overlays/mvc-auth/Views/Shared/_AuthNav.cshtml +22 -0
- package/stacks/dotnet-core/snippets/packages-auth.xml +4 -0
- package/stacks/dotnet-core/snippets/packages-common.xml +5 -0
- package/stacks/dotnet-core/snippets/packages-ef.xml +4 -0
- package/stacks/dotnet-core/snippets/packages-mssql.xml +4 -0
- package/stacks/dotnet-core/snippets/packages-mysql.xml +4 -0
- package/stacks/dotnet-core/snippets/packages-swagger.xml +4 -0
- package/stacks/dotnet-core/stack.json +426 -0
- package/stacks/laravel/overlays/auth/app/Http/Controllers/Api/AuthController.php +67 -0
- package/stacks/laravel/overlays/common/app/Http/Controllers/BoilerCraftController.php +14 -0
- package/stacks/laravel/overlays/common/resources/views/boilercraft.blade.php +36 -0
- package/stacks/laravel/overlays/inertia-common/resources/views/app.blade.php +15 -0
- package/stacks/laravel/overlays/inertia-react/resources/js/Pages/Welcome.jsx +18 -0
- package/stacks/laravel/overlays/inertia-react/resources/js/app.jsx +13 -0
- package/stacks/laravel/overlays/inertia-vue/resources/js/Pages/Welcome.vue +23 -0
- package/stacks/laravel/overlays/inertia-vue/resources/js/app.js +13 -0
- package/stacks/laravel/snippets/api-routes.php +9 -0
- package/stacks/laravel/snippets/web-routes-inertia.php +7 -0
- package/stacks/laravel/snippets/web-routes.php +4 -0
- package/stacks/laravel/stack.json +477 -0
- package/stacks/nextjs/overlays/auth/src/lib/auth.js +36 -0
- package/stacks/nextjs/overlays/auth-app/src/app/api/auth/login/route.js +11 -0
- package/stacks/nextjs/overlays/auth-app/src/app/api/auth/register/route.js +14 -0
- package/stacks/nextjs/overlays/auth-pages/src/pages/api/auth/login.js +14 -0
- package/stacks/nextjs/overlays/auth-pages/src/pages/api/auth/register.js +17 -0
- package/stacks/nextjs/overlays/common/.env.example +3 -0
- package/stacks/nextjs/overlays/common/.env.local +3 -0
- package/stacks/nextjs/overlays/db/mongodb/src/lib/db.js +12 -0
- package/stacks/nextjs/overlays/db/mssql/src/lib/db.js +17 -0
- package/stacks/nextjs/overlays/db/mysql/src/lib/db.js +13 -0
- package/stacks/nextjs/overlays/router-app/src/app/api/health/route.js +10 -0
- package/stacks/nextjs/overlays/router-app/src/app/layout.js +16 -0
- package/stacks/nextjs/overlays/router-app/src/app/page.js +19 -0
- package/stacks/nextjs/overlays/router-pages/src/pages/_app.js +7 -0
- package/stacks/nextjs/overlays/router-pages/src/pages/_document.js +13 -0
- package/stacks/nextjs/overlays/router-pages/src/pages/api/health.js +8 -0
- package/stacks/nextjs/overlays/router-pages/src/pages/index.js +27 -0
- package/stacks/nextjs/stack.json +257 -0
- package/stacks/node-express/overlays/auth/src/controllers/authController.js +53 -0
- package/stacks/node-express/overlays/auth/src/middlewares/auth.js +36 -0
- package/stacks/node-express/overlays/auth/src/routes/authRoutes.js +16 -0
- package/stacks/node-express/overlays/common/.env.example +5 -0
- package/stacks/node-express/overlays/common/public/index.html +31 -0
- package/stacks/node-express/overlays/common/src/server.js +32 -0
- package/stacks/node-express/overlays/db/mongodb/src/db.js +13 -0
- package/stacks/node-express/overlays/db/mssql/src/db.js +22 -0
- package/stacks/node-express/overlays/db/mysql/src/db.js +22 -0
- package/stacks/node-express/stack.json +165 -0
- package/templates/dotnet-bootstrap-mssql/Controllers/AuthController.cs +74 -0
- package/templates/dotnet-bootstrap-mssql/Controllers/ProductsController.cs +33 -0
- package/templates/dotnet-bootstrap-mssql/Data/ApplicationDbContext.cs +24 -0
- package/templates/dotnet-bootstrap-mssql/Models/Product.cs +10 -0
- package/templates/dotnet-core/Controllers/AuthController.cs +74 -0
- package/templates/laravel-full/app/Http/Controllers/AuthController.php +79 -0
- package/templates/laravel-full/app/Http/Controllers/HomeController.php +22 -0
- package/templates/laravel-full/app/Models/User.php +49 -0
- package/templates/laravel-full/artisan +22 -0
- package/templates/laravel-full/bootstrap/app.php +21 -0
- package/templates/laravel-full/config/database.php +54 -0
- package/templates/laravel-full/database/migrations/2024_01_01_000001_create_users_table.php +27 -0
- package/templates/laravel-full/public/index.php +17 -0
- package/templates/laravel-full/resources/css/app.css +3 -0
- package/templates/laravel-full/resources/js/Pages/Home.vue +76 -0
- package/templates/laravel-full/resources/js/app.js +21 -0
- package/templates/laravel-full/resources/js/bootstrap.js +3 -0
- package/templates/laravel-full/resources/views/app.blade.php +15 -0
- package/templates/laravel-full/routes/api.php +8 -0
- package/templates/laravel-full/routes/console.php +7 -0
- package/templates/laravel-full/routes/web.php +24 -0
- package/templates/laravel-full/tailwind.config.js +12 -0
- package/templates/laravel-full/vite.config.js +20 -0
- package/templates/laravel-vue/app/Http/Controllers/Auth/AuthenticatedSessionController.php +43 -0
- package/templates/laravel-vue-mysql/app/Http/Controllers/HomeController.php +25 -0
- package/templates/laravel-vue-mysql/resources/js/Pages/Home.vue +70 -0
- package/templates/nextjs/src/app/api/auth/login/route.js +28 -0
- package/templates/nextjs-mongodb/src/app/api/health/route.js +17 -0
- package/templates/nextjs-mongodb/src/lib/mongodb.js +18 -0
- package/templates/nextjs-mssql/src/app/api/health/route.js +24 -0
- package/templates/nextjs-mssql/src/lib/mssql.js +38 -0
- package/templates/node-express/src/controllers/authController.js +53 -0
- package/templates/node-express/src/middlewares/auth.js +36 -0
- package/templates/node-express/src/routes/authRoutes.js +16 -0
- package/templates/node-mongodb/src/controllers/userController.js +30 -0
- package/templates/node-mongodb/src/middlewares/errorHandler.js +8 -0
- package/templates/node-mongodb/src/models/User.js +23 -0
- package/templates/node-mongodb/src/routes/api.js +9 -0
- package/templates/raw-php-mysql/app/Controllers/AuthController.php +73 -0
- package/templates/raw-php-mysql/app/Controllers/HomeController.php +65 -0
- package/templates/raw-php-mysql/app/Models/User.php +60 -0
- package/templates/raw-php-mysql/app/Views/404.php +15 -0
- package/templates/raw-php-mysql/app/Views/auth/login.php +66 -0
- package/templates/raw-php-mysql/app/Views/auth/register.php +71 -0
- package/templates/raw-php-mysql/app/Views/dashboard.php +50 -0
- package/templates/raw-php-mysql/app/Views/home.php +83 -0
- package/templates/raw-php-mysql/config/Auth.php +62 -0
- package/templates/raw-php-mysql/config/Database.php +100 -0
- package/templates/raw-php-mysql/config/Env.php +30 -0
- package/templates/raw-php-mysql/config/Middleware.php +53 -0
- package/templates/raw-php-mysql/config/QueryBuilder.php +109 -0
- package/templates/raw-php-mysql/config/Router.php +33 -0
- package/templates/raw-php-mysql/public/index.php +57 -0
|
@@ -0,0 +1,721 @@
|
|
|
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>BoilerCraft Studio</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
10
|
+
<style>
|
|
11
|
+
:root {
|
|
12
|
+
--primary: #0284c7;
|
|
13
|
+
--primary-hover: #0369a1;
|
|
14
|
+
--bg: #f8fafc;
|
|
15
|
+
--surface: #ffffff;
|
|
16
|
+
--border: #e2e8f0;
|
|
17
|
+
--border-focus: #0284c7;
|
|
18
|
+
--text: #0f172a;
|
|
19
|
+
--text-muted: #64748b;
|
|
20
|
+
--radius: 8px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
* { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Plus Jakarta Sans', sans-serif; }
|
|
24
|
+
body { background-color: var(--bg); color: var(--text); min-height: 100vh; }
|
|
25
|
+
|
|
26
|
+
header {
|
|
27
|
+
background: var(--surface); border-bottom: 1px solid var(--border);
|
|
28
|
+
position: sticky; top: 0; z-index: 50; padding: 0.85rem 2rem;
|
|
29
|
+
display: flex; justify-content: space-between; align-items: center;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.brand { display: flex; align-items: center; gap: 10px; }
|
|
33
|
+
.brand img { width: 34px; height: 34px; border-radius: 6px; }
|
|
34
|
+
.brand h1 { font-size: 1.15rem; font-weight: 800; color: var(--text); }
|
|
35
|
+
.badge-clean {
|
|
36
|
+
font-size: 0.7rem; font-weight: 700; padding: 2px 7px; border-radius: 4px;
|
|
37
|
+
background: #f1f5f9; color: var(--text-muted); border: 1px solid var(--border);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.header-actions { display: flex; align-items: center; gap: 16px; }
|
|
41
|
+
.header-actions a { color: var(--text-muted); text-decoration: none; font-size: 0.85rem; font-weight: 600; }
|
|
42
|
+
.header-actions a:hover { color: var(--primary); }
|
|
43
|
+
|
|
44
|
+
.app-layout {
|
|
45
|
+
max-width: 1400px; margin: 0 auto; padding: 1.75rem 2rem;
|
|
46
|
+
display: grid; grid-template-columns: 1fr 410px; gap: 1.75rem;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.step-card {
|
|
50
|
+
background: var(--surface); border: 1px solid var(--border);
|
|
51
|
+
border-radius: var(--radius); padding: 1.5rem; margin-bottom: 1.25rem;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.step-header { display: flex; align-items: center; gap: 8px; margin-bottom: 1.25rem; }
|
|
55
|
+
.step-num {
|
|
56
|
+
width: 24px; height: 24px; border-radius: 5px; background: var(--primary); color: white;
|
|
57
|
+
display: flex; align-items: center; justify-content: center; font-size: 0.75rem; font-weight: 700;
|
|
58
|
+
}
|
|
59
|
+
.step-header h2 { font-size: 1.05rem; font-weight: 700; color: var(--text); }
|
|
60
|
+
|
|
61
|
+
.input-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
|
|
62
|
+
.form-group { display: flex; flex-direction: column; gap: 5px; }
|
|
63
|
+
.form-group.full { grid-column: span 2; }
|
|
64
|
+
|
|
65
|
+
label { font-size: 0.8rem; font-weight: 600; color: var(--text-muted); }
|
|
66
|
+
input[type="text"], input[type="number"], select {
|
|
67
|
+
background: #ffffff; border: 1px solid var(--border); color: var(--text);
|
|
68
|
+
padding: 9px 12px; border-radius: 6px; font-size: 0.9rem; outline: none;
|
|
69
|
+
}
|
|
70
|
+
input:focus, select:focus { border-color: var(--border-focus); }
|
|
71
|
+
|
|
72
|
+
.folder-picker-group { display: flex; gap: 8px; }
|
|
73
|
+
.folder-picker-group input { flex: 1; }
|
|
74
|
+
.btn-browse {
|
|
75
|
+
background: #f1f5f9; border: 1px solid var(--border); color: var(--text);
|
|
76
|
+
padding: 9px 16px; border-radius: 6px; font-size: 0.85rem; font-weight: 700;
|
|
77
|
+
cursor: pointer; transition: background 0.15s; white-space: nowrap;
|
|
78
|
+
}
|
|
79
|
+
.btn-browse:hover { background: #e2e8f0; }
|
|
80
|
+
|
|
81
|
+
.matrix-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; }
|
|
82
|
+
.choice-card {
|
|
83
|
+
background: #ffffff; border: 1px solid var(--border); border-radius: 7px;
|
|
84
|
+
padding: 12px 14px; cursor: pointer; transition: all 0.15s;
|
|
85
|
+
}
|
|
86
|
+
.choice-card:hover { border-color: #cbd5e1; background: #f8fafc; }
|
|
87
|
+
.choice-card.selected { border-color: var(--primary); background: #f0f9ff; }
|
|
88
|
+
.choice-title { font-weight: 700; font-size: 0.9rem; margin-bottom: 3px; color: var(--text); }
|
|
89
|
+
.choice-desc { font-size: 0.75rem; color: var(--text-muted); line-height: 1.4; }
|
|
90
|
+
|
|
91
|
+
/* Version / Framework Dropdowns Bar */
|
|
92
|
+
.framework-controls {
|
|
93
|
+
display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px;
|
|
94
|
+
background: #f8fafc; border: 1px solid var(--border); border-radius: 8px; padding: 12px; margin-top: 14px;
|
|
95
|
+
}
|
|
96
|
+
.engine-note { grid-column: 1 / -1; font-size: 0.75rem; color: var(--text-muted); line-height: 1.5; }
|
|
97
|
+
.engine-note.warn { color: #b45309; }
|
|
98
|
+
.choice-card.unsupported { opacity: 0.45; }
|
|
99
|
+
|
|
100
|
+
.checkbox-group { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
|
|
101
|
+
.checkbox-tile {
|
|
102
|
+
display: flex; align-items: center; gap: 10px; background: #ffffff;
|
|
103
|
+
border: 1px solid var(--border); padding: 10px 14px; border-radius: 6px; cursor: pointer;
|
|
104
|
+
}
|
|
105
|
+
.checkbox-tile:hover { background: #f8fafc; }
|
|
106
|
+
.checkbox-tile input[type="checkbox"] { width: 17px; height: 17px; accent-color: var(--primary); }
|
|
107
|
+
|
|
108
|
+
.preview-pane { position: sticky; top: 75px; height: fit-content; }
|
|
109
|
+
.preview-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.5rem; }
|
|
110
|
+
|
|
111
|
+
.summary-list { list-style: none; display: flex; flex-direction: column; gap: 10px; font-size: 0.85rem; margin-bottom: 1.5rem; }
|
|
112
|
+
.summary-list li { display: flex; justify-content: space-between; border-bottom: 1px solid #f1f5f9; padding-bottom: 6px; }
|
|
113
|
+
.summary-list span:first-child { color: var(--text-muted); }
|
|
114
|
+
.summary-list span:last-child { font-weight: 700; color: var(--text); }
|
|
115
|
+
|
|
116
|
+
.console-box {
|
|
117
|
+
background: #0f172a; border-radius: 6px; padding: 12px;
|
|
118
|
+
font-family: 'JetBrains Mono', monospace; font-size: 0.75rem; color: #38bdf8;
|
|
119
|
+
height: 130px; overflow-y: auto; margin-bottom: 1.25rem;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.btn-generate {
|
|
123
|
+
width: 100%; background: var(--primary); color: white;
|
|
124
|
+
border: none; padding: 13px 20px; border-radius: 6px; font-size: 0.95rem;
|
|
125
|
+
font-weight: 700; cursor: pointer; transition: background 0.15s;
|
|
126
|
+
}
|
|
127
|
+
.btn-generate:hover { background: var(--primary-hover); }
|
|
128
|
+
.btn-generate:disabled { opacity: 0.6; cursor: not-allowed; }
|
|
129
|
+
</style>
|
|
130
|
+
</head>
|
|
131
|
+
<body>
|
|
132
|
+
|
|
133
|
+
<header>
|
|
134
|
+
<div class="brand">
|
|
135
|
+
<img src="/logo.svg" alt="BoilerCraft Logo">
|
|
136
|
+
<div>
|
|
137
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
138
|
+
<h1>BoilerCraft Studio</h1>
|
|
139
|
+
<span class="badge-clean">v1.0.0</span>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="header-actions">
|
|
144
|
+
<a href="https://github.com/itrabbi24/BoilerCraft" target="_blank">GitHub Repository ↗</a>
|
|
145
|
+
<a href="https://github.com/itrabbi24" target="_blank" style="color: var(--primary); font-weight: 700;">ARG RABBI</a>
|
|
146
|
+
</div>
|
|
147
|
+
</header>
|
|
148
|
+
|
|
149
|
+
<div class="app-layout">
|
|
150
|
+
|
|
151
|
+
<div class="config-flow">
|
|
152
|
+
|
|
153
|
+
<!-- STEP 1: Output Location & Information -->
|
|
154
|
+
<section class="step-card">
|
|
155
|
+
<div class="step-header">
|
|
156
|
+
<div class="step-num">1</div>
|
|
157
|
+
<h2>Output Location & Project Details</h2>
|
|
158
|
+
</div>
|
|
159
|
+
<div class="input-grid">
|
|
160
|
+
<div class="form-group full">
|
|
161
|
+
<label for="outputFolder">Output Directory (Where to create project)</label>
|
|
162
|
+
<div class="folder-picker-group">
|
|
163
|
+
<input type="text" id="outputFolder" placeholder="C:\Users\Desktop\test or leave empty for default generated/ folder" oninput="updateSummary()">
|
|
164
|
+
<button type="button" class="btn-browse" onclick="browseFolder()">📁 Choose Folder</button>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<div class="form-group">
|
|
169
|
+
<label for="projectName">Project Name (Folder Slug)</label>
|
|
170
|
+
<input type="text" id="projectName" value="my-platform" oninput="updateSummary()">
|
|
171
|
+
</div>
|
|
172
|
+
<div class="form-group">
|
|
173
|
+
<label for="appTitle">Display App Title</label>
|
|
174
|
+
<input type="text" id="appTitle" value="Enterprise Platform" oninput="updateSummary()">
|
|
175
|
+
</div>
|
|
176
|
+
<div class="form-group full">
|
|
177
|
+
<label for="appDesc">Description</label>
|
|
178
|
+
<input type="text" id="appDesc" value="Enterprise application generated with BoilerCraft Studio" oninput="updateSummary()">
|
|
179
|
+
</div>
|
|
180
|
+
<div class="form-group">
|
|
181
|
+
<label for="author">Author / Maintainer</label>
|
|
182
|
+
<input type="text" id="author" value="ARG RABBI" oninput="updateSummary()">
|
|
183
|
+
</div>
|
|
184
|
+
<div class="form-group">
|
|
185
|
+
<label for="serverPort">Server Port</label>
|
|
186
|
+
<input type="number" id="serverPort" value="3000" oninput="updateSummary()">
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</section>
|
|
190
|
+
|
|
191
|
+
<!-- STEP 2: Backend Architecture & Specific Versions -->
|
|
192
|
+
<section class="step-card">
|
|
193
|
+
<div class="step-header">
|
|
194
|
+
<div class="step-num">2</div>
|
|
195
|
+
<h2>Backend Architecture & Versioning</h2>
|
|
196
|
+
</div>
|
|
197
|
+
<div class="matrix-grid" id="backendOptions">
|
|
198
|
+
<div class="choice-card selected" onclick="selectBackend('laravel', this)">
|
|
199
|
+
<div class="choice-title">🔴 Laravel</div>
|
|
200
|
+
<div class="choice-desc">Artisan Engine, Vue/Blade, Eloquent ORM, Full Scaffolding.</div>
|
|
201
|
+
</div>
|
|
202
|
+
<div class="choice-card" onclick="selectBackend('nextjs', this)">
|
|
203
|
+
<div class="choice-title">🚀 Next.js</div>
|
|
204
|
+
<div class="choice-desc">App Router, React Server Components, Server Actions.</div>
|
|
205
|
+
</div>
|
|
206
|
+
<div class="choice-card" onclick="selectBackend('node-express', this)">
|
|
207
|
+
<div class="choice-title">⚡ Node.js + Express</div>
|
|
208
|
+
<div class="choice-desc">Clean Architecture, JWT Middleware, Error Handlers.</div>
|
|
209
|
+
</div>
|
|
210
|
+
<div class="choice-card" onclick="selectBackend('dotnet-core', this)">
|
|
211
|
+
<div class="choice-title">🟣 .NET Core</div>
|
|
212
|
+
<div class="choice-desc">C# Web API, Entity Framework Core, Swagger OpenAPI.</div>
|
|
213
|
+
</div>
|
|
214
|
+
<div class="choice-card" onclick="selectBackend('raw-php', this)">
|
|
215
|
+
<div class="choice-title">📦 Raw PHP (MVC)</div>
|
|
216
|
+
<div class="choice-desc">Modern MVC, PDO Multi-Driver, QueryBuilder, AJAX.</div>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<!-- Dynamic Version Controls (Shown dynamically for active backend) -->
|
|
221
|
+
<div class="framework-controls" id="versionControlBox">
|
|
222
|
+
<div class="form-group" id="laravelVersionGroup">
|
|
223
|
+
<label for="laravelVersion">Laravel Framework Release</label>
|
|
224
|
+
<!-- Options are loaded live from /api/stacks (npm / Packagist / .NET release index),
|
|
225
|
+
so new framework releases appear without editing this file.
|
|
226
|
+
Old hardcoded list kept for reference:
|
|
227
|
+
<option value="^11.0" selected>Laravel 11.x (Latest Modern LTS)</option>
|
|
228
|
+
<option value="^10.0">Laravel 10.x (Classic LTS)</option>
|
|
229
|
+
-->
|
|
230
|
+
<select id="laravelVersion" onchange="updateSummary()">
|
|
231
|
+
<option value="">Loading versions…</option>
|
|
232
|
+
</select>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<div class="form-group" id="laravelFrontendGroup">
|
|
236
|
+
<label for="laravelFrontend">Laravel Frontend</label>
|
|
237
|
+
<select id="laravelFrontend" onchange="updateSummary()">
|
|
238
|
+
<option value="blade" selected>Blade (classic)</option>
|
|
239
|
+
<option value="vue">Vue 3 (Inertia)</option>
|
|
240
|
+
<option value="react">React (Inertia)</option>
|
|
241
|
+
</select>
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
<div class="form-group" id="nodeVersionGroup">
|
|
245
|
+
<label for="nodeVersion">Node.js Express Release</label>
|
|
246
|
+
<!-- Loaded live from /api/stacks. Old hardcoded list:
|
|
247
|
+
<option value="^5.0.0" selected>Express 5.x (Modern Next-Gen)</option>
|
|
248
|
+
<option value="^4.21.0">Express 4.x (Classic Stable)</option>
|
|
249
|
+
-->
|
|
250
|
+
<select id="nodeVersion" onchange="updateSummary()">
|
|
251
|
+
<option value="">Loading versions…</option>
|
|
252
|
+
</select>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<div class="form-group" id="nextVersionGroup">
|
|
256
|
+
<label for="nextVersion">Next.js Engine Version</label>
|
|
257
|
+
<!-- Loaded live from /api/stacks. Old hardcoded list:
|
|
258
|
+
<option value="^15.0.0">Next.js 15.x (Turbopack + React 19)</option>
|
|
259
|
+
<option value="^14.2.0" selected>Next.js 14.x (Stable App Router)</option>
|
|
260
|
+
-->
|
|
261
|
+
<select id="nextVersion" onchange="updateSummary()">
|
|
262
|
+
<option value="">Loading versions…</option>
|
|
263
|
+
</select>
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
<div class="form-group" id="nextRouterGroup">
|
|
267
|
+
<label for="nextRouter">Next.js Router</label>
|
|
268
|
+
<select id="nextRouter" onchange="updateSummary()">
|
|
269
|
+
<option value="app" selected>App Router (recommended)</option>
|
|
270
|
+
<option value="pages">Pages Router (classic)</option>
|
|
271
|
+
</select>
|
|
272
|
+
</div>
|
|
273
|
+
|
|
274
|
+
<div class="form-group" id="dotnetVersionGroup">
|
|
275
|
+
<label for="dotnetVersion">.NET Target Framework</label>
|
|
276
|
+
<!-- Loaded live from /api/stacks. Old hardcoded list:
|
|
277
|
+
<option value="net8.0" selected>.NET 8.0 (Long Term Support)</option>
|
|
278
|
+
<option value="net9.0">.NET 9.0 (Standard Term Support)</option>
|
|
279
|
+
-->
|
|
280
|
+
<select id="dotnetVersion" onchange="updateSummary()">
|
|
281
|
+
<option value="">Loading versions…</option>
|
|
282
|
+
</select>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<div class="form-group" id="dotnetTemplateGroup">
|
|
286
|
+
<label for="dotnetTemplate">.NET Project Type</label>
|
|
287
|
+
<select id="dotnetTemplate" onchange="updateSummary()">
|
|
288
|
+
<option value="mvc" selected>MVC (Model · View · Controller)</option>
|
|
289
|
+
<option value="api">Web API (JSON + Swagger)</option>
|
|
290
|
+
</select>
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<div class="engine-note" id="engineNote"></div>
|
|
294
|
+
</div>
|
|
295
|
+
</section>
|
|
296
|
+
|
|
297
|
+
<!-- STEP 3: Authentication & Security -->
|
|
298
|
+
<section class="step-card">
|
|
299
|
+
<div class="step-header">
|
|
300
|
+
<div class="step-num">3</div>
|
|
301
|
+
<h2>Authentication & Security Modules</h2>
|
|
302
|
+
</div>
|
|
303
|
+
<div class="checkbox-group">
|
|
304
|
+
<label class="checkbox-tile">
|
|
305
|
+
<input type="checkbox" id="authEnabled" checked onchange="updateSummary()">
|
|
306
|
+
<div>
|
|
307
|
+
<div style="font-weight: 700; font-size: 0.9rem;">🔐 Enable Authentication (Login & Register)</div>
|
|
308
|
+
<div style="font-size: 0.75rem; color: var(--text-muted);">Include password hashing, sessions/JWT, & controllers</div>
|
|
309
|
+
</div>
|
|
310
|
+
</label>
|
|
311
|
+
<label class="checkbox-tile">
|
|
312
|
+
<input type="checkbox" id="roleAuthEnabled" checked onchange="updateSummary()">
|
|
313
|
+
<div>
|
|
314
|
+
<div style="font-weight: 700; font-size: 0.9rem;">🛡️ Role-Based Authorization Guard</div>
|
|
315
|
+
<div style="font-size: 0.75rem; color: var(--text-muted);">Admin/User permissions & route guard middlewares</div>
|
|
316
|
+
</div>
|
|
317
|
+
</label>
|
|
318
|
+
</div>
|
|
319
|
+
</section>
|
|
320
|
+
|
|
321
|
+
<!-- STEP 4: Database Engine -->
|
|
322
|
+
<section class="step-card">
|
|
323
|
+
<div class="step-header">
|
|
324
|
+
<div class="step-num">4</div>
|
|
325
|
+
<h2>Database Engine</h2>
|
|
326
|
+
</div>
|
|
327
|
+
<div class="matrix-grid" id="dbOptions">
|
|
328
|
+
<div class="choice-card selected" onclick="selectDb('mysql', this)">
|
|
329
|
+
<div class="choice-title">MySQL / MariaDB</div>
|
|
330
|
+
<div class="choice-desc">Relational DB with connection pooling & migrations.</div>
|
|
331
|
+
</div>
|
|
332
|
+
<div class="choice-card" onclick="selectDb('mssql', this)">
|
|
333
|
+
<div class="choice-title">Microsoft SQL Server</div>
|
|
334
|
+
<div class="choice-desc">Enterprise SQL Server pool / EF Core connector.</div>
|
|
335
|
+
</div>
|
|
336
|
+
<div class="choice-card" onclick="selectDb('mongodb', this)">
|
|
337
|
+
<div class="choice-title">MongoDB</div>
|
|
338
|
+
<div class="choice-desc">NoSQL document store with Mongoose ODM.</div>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
</section>
|
|
342
|
+
|
|
343
|
+
<!-- STEP 5: Styling & UI Components -->
|
|
344
|
+
<section class="step-card">
|
|
345
|
+
<div class="step-header">
|
|
346
|
+
<div class="step-num">5</div>
|
|
347
|
+
<h2>Styling Engine & Essential UI Components</h2>
|
|
348
|
+
</div>
|
|
349
|
+
|
|
350
|
+
<label style="margin-bottom: 8px; display: block;">Styling Framework:</label>
|
|
351
|
+
<div class="matrix-grid mb-4" id="styleOptions" style="margin-bottom: 1.25rem;">
|
|
352
|
+
<div class="choice-card selected" onclick="selectStyling('tailwind', this)">
|
|
353
|
+
<div class="choice-title">Tailwind CSS</div>
|
|
354
|
+
<div class="choice-desc">Utility-first modern CSS framework.</div>
|
|
355
|
+
</div>
|
|
356
|
+
<div class="choice-card" onclick="selectStyling('bootstrap', this)">
|
|
357
|
+
<div class="choice-title">Bootstrap 5</div>
|
|
358
|
+
<div class="choice-desc">Responsive components and grid.</div>
|
|
359
|
+
</div>
|
|
360
|
+
<div class="choice-card" onclick="selectStyling('vanilla', this)">
|
|
361
|
+
<div class="choice-title">Vanilla CSS</div>
|
|
362
|
+
<div class="choice-desc">Zero-dependency pure design tokens.</div>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
|
|
366
|
+
<label style="margin-bottom: 8px; display: block;">Inject UI Components:</label>
|
|
367
|
+
<div class="checkbox-group">
|
|
368
|
+
<label class="checkbox-tile">
|
|
369
|
+
<input type="checkbox" id="compSkeleton" checked onchange="updateSummary()">
|
|
370
|
+
<div>
|
|
371
|
+
<div style="font-weight: 600; font-size: 0.85rem;">Skeleton Loader</div>
|
|
372
|
+
<div style="font-size: 0.75rem; color: var(--text-muted);">Pulse placeholder cards</div>
|
|
373
|
+
</div>
|
|
374
|
+
</label>
|
|
375
|
+
<label class="checkbox-tile">
|
|
376
|
+
<input type="checkbox" id="compSpinner" checked onchange="updateSummary()">
|
|
377
|
+
<div>
|
|
378
|
+
<div style="font-weight: 600; font-size: 0.85rem;">Loader Spinner</div>
|
|
379
|
+
<div style="font-size: 0.75rem; color: var(--text-muted);">Rotating state indicator</div>
|
|
380
|
+
</div>
|
|
381
|
+
</label>
|
|
382
|
+
<label class="checkbox-tile">
|
|
383
|
+
<input type="checkbox" id="compModal" checked onchange="updateSummary()">
|
|
384
|
+
<div>
|
|
385
|
+
<div style="font-weight: 600; font-size: 0.85rem;">Action Modal</div>
|
|
386
|
+
<div style="font-size: 0.75rem; color: var(--text-muted);">Accessible dialog component</div>
|
|
387
|
+
</div>
|
|
388
|
+
</label>
|
|
389
|
+
<label class="checkbox-tile">
|
|
390
|
+
<input type="checkbox" id="compOffcanvas" checked onchange="updateSummary()">
|
|
391
|
+
<div>
|
|
392
|
+
<div style="font-weight: 600; font-size: 0.85rem;">Offcanvas Drawer</div>
|
|
393
|
+
<div style="font-size: 0.75rem; color: var(--text-muted);">Sliding responsive drawer</div>
|
|
394
|
+
</div>
|
|
395
|
+
</label>
|
|
396
|
+
<label class="checkbox-tile">
|
|
397
|
+
<input type="checkbox" id="compSwal" checked onchange="updateSummary()">
|
|
398
|
+
<div>
|
|
399
|
+
<div style="font-weight: 600; font-size: 0.85rem;">SweetAlert2 (Swal.fire)</div>
|
|
400
|
+
<div style="font-size: 0.75rem; color: var(--text-muted);">Animated confirmation popups</div>
|
|
401
|
+
</div>
|
|
402
|
+
</label>
|
|
403
|
+
<label class="checkbox-tile">
|
|
404
|
+
<input type="checkbox" id="compToastr" checked onchange="updateSummary()">
|
|
405
|
+
<div>
|
|
406
|
+
<div style="font-weight: 600; font-size: 0.85rem;">Toastr / Sonner</div>
|
|
407
|
+
<div style="font-size: 0.75rem; color: var(--text-muted);">Non-blocking toast alerts</div>
|
|
408
|
+
</div>
|
|
409
|
+
</label>
|
|
410
|
+
</div>
|
|
411
|
+
</section>
|
|
412
|
+
|
|
413
|
+
</div>
|
|
414
|
+
|
|
415
|
+
<!-- Right Summary Sidebar -->
|
|
416
|
+
<aside class="preview-pane">
|
|
417
|
+
<div class="preview-card">
|
|
418
|
+
<h3 style="font-size: 1.05rem; font-weight: 700; margin-bottom: 1rem;">Project Configuration</h3>
|
|
419
|
+
|
|
420
|
+
<ul class="summary-list">
|
|
421
|
+
<li>
|
|
422
|
+
<span>Destination Path</span>
|
|
423
|
+
<span id="sumPath" style="word-break: break-all; text-align: right; max-width: 210px; color: var(--primary);">[Default: generated/]</span>
|
|
424
|
+
</li>
|
|
425
|
+
<li>
|
|
426
|
+
<span>Target Folder</span>
|
|
427
|
+
<span id="sumSlug">my-platform</span>
|
|
428
|
+
</li>
|
|
429
|
+
<li>
|
|
430
|
+
<span>Backend & Version</span>
|
|
431
|
+
<span id="sumStack">LARAVEL (^11.0)</span>
|
|
432
|
+
</li>
|
|
433
|
+
<li>
|
|
434
|
+
<span>Authentication</span>
|
|
435
|
+
<span id="sumAuth" style="color: #10b981;">Active (Auth + Roles)</span>
|
|
436
|
+
</li>
|
|
437
|
+
<li>
|
|
438
|
+
<span>Database</span>
|
|
439
|
+
<span id="sumDb">MYSQL</span>
|
|
440
|
+
</li>
|
|
441
|
+
<li>
|
|
442
|
+
<span>Styling</span>
|
|
443
|
+
<span id="sumStyle">TAILWIND</span>
|
|
444
|
+
</li>
|
|
445
|
+
<li>
|
|
446
|
+
<span>Port</span>
|
|
447
|
+
<span id="sumPort">3000</span>
|
|
448
|
+
</li>
|
|
449
|
+
</ul>
|
|
450
|
+
|
|
451
|
+
<div class="console-box" id="consoleLogs">
|
|
452
|
+
[Ready] BoilerCraft Studio initialized.<br>
|
|
453
|
+
Select options and click Generate Project...
|
|
454
|
+
</div>
|
|
455
|
+
|
|
456
|
+
<button class="btn-generate" id="btnGen" onclick="generateProjectAction()">
|
|
457
|
+
<span>Generate Project</span>
|
|
458
|
+
</button>
|
|
459
|
+
</div>
|
|
460
|
+
</aside>
|
|
461
|
+
|
|
462
|
+
</div>
|
|
463
|
+
|
|
464
|
+
<script>
|
|
465
|
+
let ipcRenderer = null;
|
|
466
|
+
try {
|
|
467
|
+
if (window.require) {
|
|
468
|
+
const electron = window.require('electron');
|
|
469
|
+
ipcRenderer = electron.ipcRenderer;
|
|
470
|
+
}
|
|
471
|
+
} catch (e) {}
|
|
472
|
+
|
|
473
|
+
const state = {
|
|
474
|
+
backend: 'laravel',
|
|
475
|
+
database: 'mysql',
|
|
476
|
+
styling: 'tailwind'
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
async function browseFolder() {
|
|
480
|
+
if (ipcRenderer) {
|
|
481
|
+
const selected = await ipcRenderer.invoke('select-directory');
|
|
482
|
+
if (selected) {
|
|
483
|
+
document.getElementById('outputFolder').value = selected;
|
|
484
|
+
updateSummary();
|
|
485
|
+
}
|
|
486
|
+
} else {
|
|
487
|
+
const manual = prompt('Enter destination folder path (e.g. C:\\Users\\Desktop\\test):');
|
|
488
|
+
if (manual) {
|
|
489
|
+
document.getElementById('outputFolder').value = manual;
|
|
490
|
+
updateSummary();
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function selectBackend(b, el) {
|
|
496
|
+
state.backend = b;
|
|
497
|
+
highlight(el, '#backendOptions');
|
|
498
|
+
syncVersionDropdownVisibility();
|
|
499
|
+
updateSummary();
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function syncVersionDropdownVisibility() {
|
|
503
|
+
const laravelGroup = document.getElementById('laravelVersionGroup');
|
|
504
|
+
const nodeGroup = document.getElementById('nodeVersionGroup');
|
|
505
|
+
const nextGroup = document.getElementById('nextVersionGroup');
|
|
506
|
+
const dotnetGroup = document.getElementById('dotnetVersionGroup');
|
|
507
|
+
|
|
508
|
+
laravelGroup.style.display = (state.backend === 'laravel') ? 'flex' : 'none';
|
|
509
|
+
document.getElementById('laravelFrontendGroup').style.display = (state.backend === 'laravel') ? 'flex' : 'none';
|
|
510
|
+
nextGroup.style.display = (state.backend === 'nextjs') ? 'flex' : 'none';
|
|
511
|
+
document.getElementById('nextRouterGroup').style.display = (state.backend === 'nextjs') ? 'flex' : 'none';
|
|
512
|
+
nodeGroup.style.display = (state.backend === 'node-express') ? 'flex' : 'none';
|
|
513
|
+
dotnetGroup.style.display = (state.backend === 'dotnet-core') ? 'flex' : 'none';
|
|
514
|
+
document.getElementById('dotnetTemplateGroup').style.display = (state.backend === 'dotnet-core') ? 'flex' : 'none';
|
|
515
|
+
renderEngineNote();
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// ---- Live stack catalog (versions + toolchain status) ----
|
|
519
|
+
const VERSION_SELECTS = {
|
|
520
|
+
'laravel': 'laravelVersion',
|
|
521
|
+
'nextjs': 'nextVersion',
|
|
522
|
+
'node-express': 'nodeVersion',
|
|
523
|
+
'dotnet-core': 'dotnetVersion'
|
|
524
|
+
};
|
|
525
|
+
let stackCatalog = {};
|
|
526
|
+
|
|
527
|
+
async function loadStacks() {
|
|
528
|
+
try {
|
|
529
|
+
const res = await fetch('/api/stacks');
|
|
530
|
+
const { stacks } = await res.json();
|
|
531
|
+
for (const stack of stacks) {
|
|
532
|
+
stackCatalog[stack.id] = stack;
|
|
533
|
+
const select = document.getElementById(VERSION_SELECTS[stack.id]);
|
|
534
|
+
if (!select) continue;
|
|
535
|
+
select.innerHTML = '';
|
|
536
|
+
for (const v of stack.versions) {
|
|
537
|
+
const tags = [];
|
|
538
|
+
if (v.recommended) tags.push('Recommended');
|
|
539
|
+
if (v.lts) tags.push('LTS');
|
|
540
|
+
if (!v.tested) tags.push('Untested');
|
|
541
|
+
const opt = document.createElement('option');
|
|
542
|
+
opt.value = String(v.major);
|
|
543
|
+
opt.textContent = `${stack.name} ${v.major}.x — latest ${v.latest}${tags.length ? ' (' + tags.join(', ') + ')' : ''}`;
|
|
544
|
+
if (v.recommended) opt.selected = true;
|
|
545
|
+
select.appendChild(opt);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
} catch (err) {
|
|
549
|
+
logToConsole(`Could not load live versions: ${err.message}`);
|
|
550
|
+
}
|
|
551
|
+
renderEngineNote();
|
|
552
|
+
updateSummary();
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function renderEngineNote() {
|
|
556
|
+
const note = document.getElementById('engineNote');
|
|
557
|
+
const stack = stackCatalog[state.backend];
|
|
558
|
+
|
|
559
|
+
document.querySelectorAll('#dbOptions .choice-card').forEach(card => {
|
|
560
|
+
const db = (card.getAttribute('onclick').match(/selectDb\('([^']+)'/) || [])[1];
|
|
561
|
+
const unsupported = stack && !stack.databases.includes(db);
|
|
562
|
+
card.classList.toggle('unsupported', !!unsupported);
|
|
563
|
+
card.title = unsupported ? `${stack.name} does not support this database yet` : '';
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
if (!stack) {
|
|
567
|
+
note.className = 'engine-note';
|
|
568
|
+
note.textContent = state.backend === 'raw-php' ? 'Raw PHP is generated from built-in templates.' : '';
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (stack.requirements.ok) {
|
|
572
|
+
note.className = 'engine-note';
|
|
573
|
+
note.textContent = `Base project is created with the official ${stack.name} tooling, so it always matches the selected version exactly.`;
|
|
574
|
+
} else {
|
|
575
|
+
note.className = 'engine-note warn';
|
|
576
|
+
note.textContent = `Missing on this machine: ${stack.requirements.missing.map(m => m.name + (m.install ? ' → ' + m.install : '')).join(' · ')}. ` +
|
|
577
|
+
`Generation will fall back to offline templates until these are installed.`;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function selectDb(d, el) {
|
|
582
|
+
state.database = d;
|
|
583
|
+
highlight(el, '#dbOptions');
|
|
584
|
+
updateSummary();
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function selectStyling(st, el) {
|
|
588
|
+
state.styling = st;
|
|
589
|
+
highlight(el, '#styleOptions');
|
|
590
|
+
updateSummary();
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function highlight(selectedEl, parentSelector) {
|
|
594
|
+
document.querySelectorAll(parentSelector + ' .choice-card').forEach(c => c.classList.remove('selected'));
|
|
595
|
+
selectedEl.classList.add('selected');
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function updateSummary() {
|
|
599
|
+
const name = document.getElementById('projectName').value.trim() || 'my-project';
|
|
600
|
+
const port = document.getElementById('serverPort').value || '3000';
|
|
601
|
+
const outDir = document.getElementById('outputFolder').value.trim();
|
|
602
|
+
|
|
603
|
+
document.getElementById('sumSlug').innerText = name;
|
|
604
|
+
document.getElementById('sumPort').innerText = port;
|
|
605
|
+
document.getElementById('sumDb').innerText = state.database.toUpperCase();
|
|
606
|
+
document.getElementById('sumStyle').innerText = state.styling.toUpperCase();
|
|
607
|
+
document.getElementById('sumPath').innerText = outDir || '[Default: generated/]';
|
|
608
|
+
|
|
609
|
+
// Version display
|
|
610
|
+
let verText = '';
|
|
611
|
+
if (state.backend === 'laravel') verText = document.getElementById('laravelVersion').value;
|
|
612
|
+
else if (state.backend === 'nextjs') verText = `${document.getElementById('nextVersion').value}, ${document.getElementById('nextRouter').value} router`;
|
|
613
|
+
else if (state.backend === 'node-express') verText = document.getElementById('nodeVersion').value;
|
|
614
|
+
else if (state.backend === 'dotnet-core') verText = document.getElementById('dotnetVersion').value;
|
|
615
|
+
document.getElementById('sumStack').innerText = `${state.backend.toUpperCase()} (${verText || 'latest'})`;
|
|
616
|
+
|
|
617
|
+
// Auth display
|
|
618
|
+
const auth = document.getElementById('authEnabled').checked;
|
|
619
|
+
const roles = document.getElementById('roleAuthEnabled').checked;
|
|
620
|
+
if (auth && roles) document.getElementById('sumAuth').innerText = 'Active (Auth + Roles)';
|
|
621
|
+
else if (auth) document.getElementById('sumAuth').innerText = 'Auth Only';
|
|
622
|
+
else document.getElementById('sumAuth').innerText = 'Disabled';
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
function logToConsole(text) {
|
|
626
|
+
const b = document.getElementById('consoleLogs');
|
|
627
|
+
// textContent, not innerHTML: log lines include raw CLI/error output.
|
|
628
|
+
b.appendChild(document.createElement('br'));
|
|
629
|
+
b.appendChild(document.createTextNode(`> ${text}`));
|
|
630
|
+
b.scrollTop = b.scrollHeight;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
async function generateProjectAction() {
|
|
634
|
+
const btn = document.getElementById('btnGen');
|
|
635
|
+
btn.disabled = true;
|
|
636
|
+
btn.innerText = 'Generating Project...';
|
|
637
|
+
logToConsole(`Assembling: ${state.backend} + ${state.database} + ${state.styling}...`);
|
|
638
|
+
|
|
639
|
+
const payload = {
|
|
640
|
+
projectName: document.getElementById('projectName').value,
|
|
641
|
+
outputDir: document.getElementById('outputFolder').value.trim() || null,
|
|
642
|
+
appTitle: document.getElementById('appTitle').value,
|
|
643
|
+
description: document.getElementById('appDesc').value,
|
|
644
|
+
author: document.getElementById('author').value,
|
|
645
|
+
port: parseInt(document.getElementById('serverPort').value),
|
|
646
|
+
backend: state.backend,
|
|
647
|
+
stack: state.backend,
|
|
648
|
+
database: state.database,
|
|
649
|
+
styling: state.styling,
|
|
650
|
+
// Keyed by stack id; values are major versions ("12", "16", …).
|
|
651
|
+
versions: {
|
|
652
|
+
'laravel': document.getElementById('laravelVersion').value,
|
|
653
|
+
'node-express': document.getElementById('nodeVersion').value,
|
|
654
|
+
'nextjs': document.getElementById('nextVersion').value,
|
|
655
|
+
'dotnet-core': document.getElementById('dotnetVersion').value
|
|
656
|
+
},
|
|
657
|
+
// Stack-specific options (see "options" in stacks/<id>/stack.json).
|
|
658
|
+
options: state.backend === 'nextjs' ? { router: document.getElementById('nextRouter').value }
|
|
659
|
+
: state.backend === 'laravel' ? { frontend: document.getElementById('laravelFrontend').value }
|
|
660
|
+
: state.backend === 'dotnet-core' ? { template: document.getElementById('dotnetTemplate').value }
|
|
661
|
+
: {},
|
|
662
|
+
auth: {
|
|
663
|
+
enabled: document.getElementById('authEnabled').checked,
|
|
664
|
+
roles: document.getElementById('roleAuthEnabled').checked
|
|
665
|
+
},
|
|
666
|
+
components: {
|
|
667
|
+
skeleton: document.getElementById('compSkeleton').checked,
|
|
668
|
+
spinner: document.getElementById('compSpinner').checked,
|
|
669
|
+
modal: document.getElementById('compModal').checked,
|
|
670
|
+
offcanvas: document.getElementById('compOffcanvas').checked,
|
|
671
|
+
swalfire: document.getElementById('compSwal').checked,
|
|
672
|
+
toastr: document.getElementById('compToastr').checked
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
try {
|
|
677
|
+
// Streamed NDJSON: log lines while official installers run, then the result.
|
|
678
|
+
const response = await fetch('/api/generate?stream=1', {
|
|
679
|
+
method: 'POST',
|
|
680
|
+
headers: { 'Content-Type': 'application/json' },
|
|
681
|
+
body: JSON.stringify(payload)
|
|
682
|
+
});
|
|
683
|
+
const reader = response.body.getReader();
|
|
684
|
+
const decoder = new TextDecoder();
|
|
685
|
+
let buffer = '';
|
|
686
|
+
let result = { success: false, error: 'No response from generator' };
|
|
687
|
+
while (true) {
|
|
688
|
+
const { done, value } = await reader.read();
|
|
689
|
+
if (done) break;
|
|
690
|
+
buffer += decoder.decode(value, { stream: true });
|
|
691
|
+
let nl;
|
|
692
|
+
while ((nl = buffer.indexOf('\n')) !== -1) {
|
|
693
|
+
const line = buffer.slice(0, nl).trim();
|
|
694
|
+
buffer = buffer.slice(nl + 1);
|
|
695
|
+
if (!line) continue;
|
|
696
|
+
const msg = JSON.parse(line);
|
|
697
|
+
if (msg.type === 'log') logToConsole(msg.message);
|
|
698
|
+
else if (msg.type === 'result') result = msg;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
if (result.success) {
|
|
702
|
+
(result.warnings || []).forEach(w => logToConsole(`WARNING: ${w}`));
|
|
703
|
+
logToConsole(`SUCCESS! Created at: ${result.path}`);
|
|
704
|
+
alert(`🎉 Project '${result.projectName}' created successfully!\n\nFolder Path: ${result.path}`);
|
|
705
|
+
} else {
|
|
706
|
+
logToConsole(`ERROR: ${result.error}`);
|
|
707
|
+
}
|
|
708
|
+
} catch (err) {
|
|
709
|
+
logToConsole(`Error: ${err.message}`);
|
|
710
|
+
} finally {
|
|
711
|
+
btn.disabled = false;
|
|
712
|
+
btn.innerText = 'Generate Project';
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
syncVersionDropdownVisibility();
|
|
717
|
+
updateSummary();
|
|
718
|
+
loadStacks();
|
|
719
|
+
</script>
|
|
720
|
+
</body>
|
|
721
|
+
</html>
|