@velox0/cerver 0.4.2 → 0.5.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 +37 -10
- package/lib/assets/embed.js +33 -8
- package/lib/codegen/dispatch_gen.js +4 -1
- package/lib/codegen/generator.js +90 -55
- package/lib/codegen/route_table.js +2 -2
- package/lib/commands/build.js +31 -8
- package/lib/commands/dev.js +9 -2
- package/lib/commands/new.js +667 -258
- package/lib/commands/run.js +8 -1
- package/lib/compiler/compile.js +9 -4
- package/lib/config.js +20 -1
- package/lib/parser/discover.js +6 -6
- package/package.json +2 -2
- package/runtime/cerver.h +10 -0
- package/runtime/http_writer.c +134 -27
- package/runtime/router.c +194 -10
- package/runtime/server.c +40 -6
- package/runtime/static.c +80 -64
- package/runtime/tests/runtime_tests.c +142 -13
- package/.github/workflows/ci.yml +0 -35
- package/.github/workflows/publish.yml +0 -50
- package/test/run.js +0 -355
package/lib/commands/new.js
CHANGED
|
@@ -17,7 +17,7 @@ function newProject(name) {
|
|
|
17
17
|
console.log(`\n Creating cerver project: ${name}\n`);
|
|
18
18
|
|
|
19
19
|
// Create directory structure
|
|
20
|
-
const dirs = ["", "
|
|
20
|
+
const dirs = ["", "routes", "public", "public/about", "dist"];
|
|
21
21
|
|
|
22
22
|
for (const dir of dirs) {
|
|
23
23
|
fs.mkdirSync(path.join(projectDir, dir), { recursive: true });
|
|
@@ -35,7 +35,7 @@ function newProject(name) {
|
|
|
35
35
|
// Default route
|
|
36
36
|
fs.copyFileSync(
|
|
37
37
|
path.join(templatesDir, "index.route.js"),
|
|
38
|
-
path.join(projectDir, "
|
|
38
|
+
path.join(projectDir, "routes", "index.js"),
|
|
39
39
|
);
|
|
40
40
|
|
|
41
41
|
// Default public/index.html
|
|
@@ -48,264 +48,13 @@ function newProject(name) {
|
|
|
48
48
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
49
49
|
<title>${name}</title>
|
|
50
50
|
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
|
51
|
-
<link rel="
|
|
52
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
53
|
-
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Unbounded:wght@500;700&display=swap" rel="stylesheet">
|
|
54
|
-
<style>
|
|
55
|
-
:root {
|
|
56
|
-
--ink: #121316;
|
|
57
|
-
--muted: #4a4f57;
|
|
58
|
-
--paper: #f7f4f1;
|
|
59
|
-
--glass: rgba(255, 255, 255, 0.7);
|
|
60
|
-
--edge: rgba(255, 255, 255, 0.6);
|
|
61
|
-
--pink: #ff7abf;
|
|
62
|
-
--peach: #ffb380;
|
|
63
|
-
--mint: #7de3c9;
|
|
64
|
-
--blue: #6aa9ff;
|
|
65
|
-
--shadow: rgba(18, 19, 22, 0.18);
|
|
66
|
-
}
|
|
67
|
-
* {
|
|
68
|
-
box-sizing: border-box;
|
|
69
|
-
}
|
|
70
|
-
body {
|
|
71
|
-
margin: 0;
|
|
72
|
-
font-family: "Space Grotesk", "Segoe UI", sans-serif;
|
|
73
|
-
color: var(--ink);
|
|
74
|
-
min-height: 100vh;
|
|
75
|
-
display: grid;
|
|
76
|
-
place-items: center;
|
|
77
|
-
background-color: var(--paper);
|
|
78
|
-
background-image:
|
|
79
|
-
radial-gradient(circle at 15% 10%, rgba(255, 122, 191, 0.35), transparent 45%),
|
|
80
|
-
radial-gradient(circle at 85% 12%, rgba(255, 179, 128, 0.4), transparent 50%),
|
|
81
|
-
radial-gradient(circle at 82% 82%, rgba(122, 214, 255, 0.35), transparent 55%),
|
|
82
|
-
radial-gradient(circle at 20% 80%, rgba(125, 227, 201, 0.35), transparent 55%),
|
|
83
|
-
linear-gradient(120deg, #f7f4f1 0%, #f2f7ff 100%);
|
|
84
|
-
}
|
|
85
|
-
body::before,
|
|
86
|
-
body::after {
|
|
87
|
-
content: "";
|
|
88
|
-
position: fixed;
|
|
89
|
-
inset: -20% -10%;
|
|
90
|
-
pointer-events: none;
|
|
91
|
-
}
|
|
92
|
-
body::before {
|
|
93
|
-
background:
|
|
94
|
-
conic-gradient(from 200deg at 50% 50%, rgba(255, 122, 191, 0.08), rgba(122, 214, 255, 0.08), rgba(125, 227, 201, 0.08), rgba(255, 179, 128, 0.08));
|
|
95
|
-
filter: blur(60px);
|
|
96
|
-
opacity: 0.6;
|
|
97
|
-
}
|
|
98
|
-
body::after {
|
|
99
|
-
background-image: radial-gradient(circle, rgba(18, 19, 22, 0.04) 1px, transparent 1px);
|
|
100
|
-
background-size: 24px 24px;
|
|
101
|
-
opacity: 0.6;
|
|
102
|
-
}
|
|
103
|
-
.stage {
|
|
104
|
-
width: min(1100px, 92vw);
|
|
105
|
-
padding: 4rem 0;
|
|
106
|
-
}
|
|
107
|
-
.frame {
|
|
108
|
-
position: relative;
|
|
109
|
-
display: grid;
|
|
110
|
-
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
111
|
-
gap: 2.5rem;
|
|
112
|
-
align-items: center;
|
|
113
|
-
padding: clamp(2.5rem, 4vw, 4rem);
|
|
114
|
-
border-radius: 32px;
|
|
115
|
-
background: var(--glass);
|
|
116
|
-
border: 1px solid var(--edge);
|
|
117
|
-
box-shadow:
|
|
118
|
-
0 40px 90px -40px var(--shadow),
|
|
119
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.7);
|
|
120
|
-
backdrop-filter: blur(18px);
|
|
121
|
-
-webkit-backdrop-filter: blur(18px);
|
|
122
|
-
animation: rise 0.8s cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
123
|
-
}
|
|
124
|
-
h1 {
|
|
125
|
-
margin: 0.6rem 0 1rem;
|
|
126
|
-
font-family: "Unbounded", "Space Grotesk", sans-serif;
|
|
127
|
-
font-size: clamp(2.2rem, 4vw, 4.4rem);
|
|
128
|
-
letter-spacing: 0.08em;
|
|
129
|
-
text-transform: uppercase;
|
|
130
|
-
}
|
|
131
|
-
p {
|
|
132
|
-
margin: 0 0 1.5rem;
|
|
133
|
-
font-size: 1.1rem;
|
|
134
|
-
line-height: 1.7;
|
|
135
|
-
color: var(--muted);
|
|
136
|
-
max-width: 32rem;
|
|
137
|
-
}
|
|
138
|
-
.steps {
|
|
139
|
-
margin: 0 0 1.8rem;
|
|
140
|
-
}
|
|
141
|
-
.steps-box {
|
|
142
|
-
padding: 1rem 1.2rem;
|
|
143
|
-
border-radius: 18px;
|
|
144
|
-
background: rgba(255, 255, 255, 0.35);
|
|
145
|
-
border: 1px solid rgba(18, 19, 22, 0.08);
|
|
146
|
-
backdrop-filter: blur(8px);
|
|
147
|
-
-webkit-backdrop-filter: blur(8px);
|
|
148
|
-
}
|
|
149
|
-
.steps-title {
|
|
150
|
-
margin: 0 0 0.7rem;
|
|
151
|
-
font-size: 0.75rem;
|
|
152
|
-
text-transform: uppercase;
|
|
153
|
-
letter-spacing: 0.18em;
|
|
154
|
-
color: var(--muted);
|
|
155
|
-
}
|
|
156
|
-
.steps-list {
|
|
157
|
-
margin: 0;
|
|
158
|
-
padding-left: 1.1rem;
|
|
159
|
-
display: grid;
|
|
160
|
-
gap: 0.55rem;
|
|
161
|
-
color: var(--muted);
|
|
162
|
-
}
|
|
163
|
-
.steps-list li::marker {
|
|
164
|
-
color: var(--muted);
|
|
165
|
-
}
|
|
166
|
-
.steps-list strong {
|
|
167
|
-
color: #2c3138;
|
|
168
|
-
font-weight: 600;
|
|
169
|
-
}
|
|
170
|
-
.meta {
|
|
171
|
-
display: inline-flex;
|
|
172
|
-
align-items: center;
|
|
173
|
-
gap: 0.6rem;
|
|
174
|
-
font-weight: 600;
|
|
175
|
-
color: #2c3138;
|
|
176
|
-
}
|
|
177
|
-
.dot {
|
|
178
|
-
width: 10px;
|
|
179
|
-
height: 10px;
|
|
180
|
-
border-radius: 50%;
|
|
181
|
-
background: linear-gradient(135deg, var(--pink), var(--blue));
|
|
182
|
-
box-shadow: 0 0 12px rgba(122, 214, 255, 0.8);
|
|
183
|
-
}
|
|
184
|
-
.footer {
|
|
185
|
-
margin-top: 1.8rem;
|
|
186
|
-
text-align: center;
|
|
187
|
-
font-size: 0.9rem;
|
|
188
|
-
color: var(--muted);
|
|
189
|
-
}
|
|
190
|
-
.footer a {
|
|
191
|
-
color: inherit;
|
|
192
|
-
text-decoration: none;
|
|
193
|
-
font-weight: 600;
|
|
194
|
-
}
|
|
195
|
-
.footer a:hover {
|
|
196
|
-
text-decoration: underline;
|
|
197
|
-
}
|
|
198
|
-
.art {
|
|
199
|
-
position: relative;
|
|
200
|
-
display: grid;
|
|
201
|
-
place-items: center;
|
|
202
|
-
min-height: 280px;
|
|
203
|
-
}
|
|
204
|
-
.art::before {
|
|
205
|
-
content: "";
|
|
206
|
-
position: absolute;
|
|
207
|
-
width: min(320px, 70vw);
|
|
208
|
-
aspect-ratio: 1;
|
|
209
|
-
border-radius: 28%;
|
|
210
|
-
background: linear-gradient(140deg, rgba(255, 122, 191, 0.25), rgba(122, 214, 255, 0.2), rgba(125, 227, 201, 0.25));
|
|
211
|
-
filter: blur(10px);
|
|
212
|
-
transform: rotate(18deg);
|
|
213
|
-
}
|
|
214
|
-
.art img {
|
|
215
|
-
width: min(300px, 65vw);
|
|
216
|
-
height: auto;
|
|
217
|
-
border-radius: 18%;
|
|
218
|
-
filter: drop-shadow(0 25px 40px rgba(18, 19, 22, 0.25));
|
|
219
|
-
animation: float 6s ease-in-out infinite;
|
|
220
|
-
}
|
|
221
|
-
@keyframes float {
|
|
222
|
-
0%, 100% { transform: translateY(0px) rotate(-2deg); }
|
|
223
|
-
50% { transform: translateY(-12px) rotate(2deg); }
|
|
224
|
-
}
|
|
225
|
-
@keyframes rise {
|
|
226
|
-
from { opacity: 0; transform: translateY(18px) scale(0.98); }
|
|
227
|
-
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
228
|
-
}
|
|
229
|
-
@media (max-width: 760px) {
|
|
230
|
-
.stage {
|
|
231
|
-
padding: 2.5rem 0;
|
|
232
|
-
}
|
|
233
|
-
.frame {
|
|
234
|
-
padding: 2.2rem;
|
|
235
|
-
}
|
|
236
|
-
h1 {
|
|
237
|
-
letter-spacing: 0.05em;
|
|
238
|
-
}
|
|
239
|
-
.art {
|
|
240
|
-
order: -1;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
@media (prefers-color-scheme: dark) {
|
|
244
|
-
:root {
|
|
245
|
-
--ink: #f7f8fb;
|
|
246
|
-
--muted: #c2c8d0;
|
|
247
|
-
--paper: #0b0e13;
|
|
248
|
-
--glass: rgba(15, 19, 28, 0.78);
|
|
249
|
-
--edge: rgba(255, 255, 255, 0.12);
|
|
250
|
-
--shadow: rgba(0, 0, 0, 0.65);
|
|
251
|
-
}
|
|
252
|
-
body {
|
|
253
|
-
background-image:
|
|
254
|
-
radial-gradient(circle at 15% 10%, rgba(255, 122, 191, 0.45), transparent 45%),
|
|
255
|
-
radial-gradient(circle at 85% 12%, rgba(255, 179, 128, 0.45), transparent 50%),
|
|
256
|
-
radial-gradient(circle at 82% 82%, rgba(106, 169, 255, 0.45), transparent 55%),
|
|
257
|
-
radial-gradient(circle at 20% 80%, rgba(125, 227, 201, 0.4), transparent 55%),
|
|
258
|
-
linear-gradient(120deg, #0b0e13 0%, #121826 100%);
|
|
259
|
-
}
|
|
260
|
-
body::before {
|
|
261
|
-
opacity: 0.95;
|
|
262
|
-
filter: blur(90px);
|
|
263
|
-
}
|
|
264
|
-
body::after {
|
|
265
|
-
opacity: 0.2;
|
|
266
|
-
}
|
|
267
|
-
.frame {
|
|
268
|
-
box-shadow:
|
|
269
|
-
0 50px 120px -40px rgba(0, 0, 0, 0.75),
|
|
270
|
-
0 0 120px rgba(106, 169, 255, 0.25),
|
|
271
|
-
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
|
272
|
-
}
|
|
273
|
-
.steps-box {
|
|
274
|
-
background: rgba(6, 8, 14, 0.45);
|
|
275
|
-
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
276
|
-
}
|
|
277
|
-
.steps-list strong {
|
|
278
|
-
color: #eef1f6;
|
|
279
|
-
}
|
|
280
|
-
.meta {
|
|
281
|
-
color: #e6e9ef;
|
|
282
|
-
}
|
|
283
|
-
.dot {
|
|
284
|
-
box-shadow: 0 0 18px rgba(255, 122, 191, 0.9);
|
|
285
|
-
}
|
|
286
|
-
.art::before {
|
|
287
|
-
filter: blur(18px);
|
|
288
|
-
}
|
|
289
|
-
.art img {
|
|
290
|
-
filter: drop-shadow(0 30px 60px rgba(106, 169, 255, 0.45));
|
|
291
|
-
}
|
|
292
|
-
.footer {
|
|
293
|
-
color: #c2c8d0;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
@media (prefers-reduced-motion: reduce) {
|
|
297
|
-
.frame,
|
|
298
|
-
.art img {
|
|
299
|
-
animation: none;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
</style>
|
|
51
|
+
<link rel="stylesheet" href="/style.css">
|
|
303
52
|
</head>
|
|
304
53
|
<body>
|
|
305
54
|
<main class="stage">
|
|
306
55
|
<section class="frame">
|
|
307
56
|
<div class="copy">
|
|
308
|
-
<h1>${name}</h1>
|
|
57
|
+
<h1 class="home-title">${name}</h1>
|
|
309
58
|
<p>Native-speed web apps with a glassy sheen. Your new cerver project is wired, built, and ready to ship.</p>
|
|
310
59
|
<div class="steps">
|
|
311
60
|
<div class="steps-box">
|
|
@@ -313,6 +62,7 @@ function newProject(name) {
|
|
|
313
62
|
<ul class="steps-list">
|
|
314
63
|
<li>Edit <strong>public/index.html</strong> to change this page.</li>
|
|
315
64
|
<li>Config lives in <strong>cerver.config.js</strong> at the project root.</li>
|
|
65
|
+
<li>Learn more <strong><a href="/about">about cerver</a></strong>.</li>
|
|
316
66
|
</ul>
|
|
317
67
|
</div>
|
|
318
68
|
</div>
|
|
@@ -325,7 +75,7 @@ function newProject(name) {
|
|
|
325
75
|
<img src="/cerver.png" alt="cerver logo">
|
|
326
76
|
</div>
|
|
327
77
|
</section>
|
|
328
|
-
<footer class="footer">
|
|
78
|
+
<footer class="footer-home">
|
|
329
79
|
<a href="https://github.com/velox0/cerver" target="_blank" rel="noreferrer">GitHub: github.com/velox0/cerver</a>
|
|
330
80
|
</footer>
|
|
331
81
|
</main>
|
|
@@ -334,7 +84,519 @@ function newProject(name) {
|
|
|
334
84
|
`,
|
|
335
85
|
);
|
|
336
86
|
|
|
337
|
-
//
|
|
87
|
+
// Default public/style.css
|
|
88
|
+
fs.writeFileSync(
|
|
89
|
+
path.join(projectDir, "public", "style.css"),
|
|
90
|
+
`@import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Unbounded:wght@500;700&family=JetBrains+Mono:wght@400;500&display=swap');
|
|
91
|
+
|
|
92
|
+
:root {
|
|
93
|
+
--ink: #121316;
|
|
94
|
+
--muted: #4a4f57;
|
|
95
|
+
--paper: #f7f4f1;
|
|
96
|
+
--glass: rgba(255, 255, 255, 0.75);
|
|
97
|
+
--edge: rgba(255, 255, 255, 0.6);
|
|
98
|
+
--pink: #ff7abf;
|
|
99
|
+
--peach: #ffb380;
|
|
100
|
+
--mint: #7de3c9;
|
|
101
|
+
--blue: #6aa9ff;
|
|
102
|
+
--shadow: rgba(18, 19, 22, 0.12);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@media (prefers-color-scheme: dark) {
|
|
106
|
+
:root {
|
|
107
|
+
--ink: #f7f8fb;
|
|
108
|
+
--muted: #c2c8d0;
|
|
109
|
+
--paper: #0b0e13;
|
|
110
|
+
--glass: rgba(15, 19, 28, 0.82);
|
|
111
|
+
--edge: rgba(255, 255, 255, 0.08);
|
|
112
|
+
--shadow: rgba(0, 0, 0, 0.5);
|
|
113
|
+
--blue: #58a6ff;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
* {
|
|
118
|
+
box-sizing: border-box;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
body {
|
|
122
|
+
margin: 0;
|
|
123
|
+
font-family: "Space Grotesk", "Segoe UI", sans-serif;
|
|
124
|
+
color: var(--ink);
|
|
125
|
+
height: 100vh;
|
|
126
|
+
overflow: hidden;
|
|
127
|
+
display: grid;
|
|
128
|
+
place-items: center;
|
|
129
|
+
background-color: var(--paper);
|
|
130
|
+
background-image:
|
|
131
|
+
radial-gradient(circle at 15% 10%, rgba(255, 122, 191, 0.25), transparent 45%),
|
|
132
|
+
radial-gradient(circle at 85% 12%, rgba(255, 179, 128, 0.3), transparent 50%),
|
|
133
|
+
radial-gradient(circle at 82% 82%, rgba(122, 214, 255, 0.25), transparent 55%),
|
|
134
|
+
radial-gradient(circle at 20% 80%, rgba(125, 227, 201, 0.35), transparent 55%),
|
|
135
|
+
linear-gradient(120deg, #f7f4f1 0%, #f2f7ff 100%);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@media (prefers-color-scheme: dark) {
|
|
139
|
+
body {
|
|
140
|
+
background-image:
|
|
141
|
+
radial-gradient(circle at 15% 10%, rgba(255, 122, 191, 0.45), transparent 45%),
|
|
142
|
+
radial-gradient(circle at 85% 12%, rgba(255, 179, 128, 0.45), transparent 50%),
|
|
143
|
+
radial-gradient(circle at 82% 82%, rgba(106, 169, 255, 0.45), transparent 55%),
|
|
144
|
+
radial-gradient(circle at 20% 80%, rgba(125, 227, 201, 0.4), transparent 55%),
|
|
145
|
+
linear-gradient(120deg, #0b0e13 0%, #121826 100%);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
body::before,
|
|
150
|
+
body::after {
|
|
151
|
+
content: "";
|
|
152
|
+
position: fixed;
|
|
153
|
+
inset: -20% -10%;
|
|
154
|
+
pointer-events: none;
|
|
155
|
+
z-index: -1;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
body::before {
|
|
159
|
+
background:
|
|
160
|
+
conic-gradient(from 200deg at 50% 50%, rgba(255, 122, 191, 0.08), rgba(122, 214, 255, 0.08), rgba(125, 227, 201, 0.08), rgba(255, 179, 128, 0.08));
|
|
161
|
+
filter: blur(60px);
|
|
162
|
+
opacity: 0.6;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
body::after {
|
|
166
|
+
background-image: radial-gradient(circle, rgba(18, 19, 22, 0.04) 1px, transparent 1px);
|
|
167
|
+
background-size: 24px 24px;
|
|
168
|
+
opacity: 0.6;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@media (prefers-color-scheme: dark) {
|
|
172
|
+
body::before {
|
|
173
|
+
opacity: 0.95;
|
|
174
|
+
filter: blur(90px);
|
|
175
|
+
}
|
|
176
|
+
body::after {
|
|
177
|
+
background-image: radial-gradient(circle, rgba(255, 255, 255, 0.02) 1px, transparent 1px);
|
|
178
|
+
opacity: 0.2;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* Visited / Non-visited links: Pink / Green hue */
|
|
183
|
+
a, a:visited {
|
|
184
|
+
color: #ff57a0;
|
|
185
|
+
text-decoration: none;
|
|
186
|
+
font-weight: 600;
|
|
187
|
+
transition: color 0.2s ease, border-color 0.2s ease;
|
|
188
|
+
border-bottom: 1px solid rgba(255, 87, 160, 0.2);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
a:hover, a:active {
|
|
192
|
+
color: #10b981;
|
|
193
|
+
border-bottom-color: rgba(16, 185, 129, 0.6);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@media (prefers-color-scheme: dark) {
|
|
197
|
+
a, a:visited {
|
|
198
|
+
color: #ff7abf;
|
|
199
|
+
border-bottom-color: rgba(255, 122, 191, 0.2);
|
|
200
|
+
}
|
|
201
|
+
a:hover, a:active {
|
|
202
|
+
color: #7de3c9;
|
|
203
|
+
border-bottom-color: rgba(125, 227, 201, 0.6);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Home template specific styles */
|
|
208
|
+
.stage {
|
|
209
|
+
width: min(1100px, 92vw);
|
|
210
|
+
padding: 4rem 0;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.frame {
|
|
214
|
+
position: relative;
|
|
215
|
+
display: grid;
|
|
216
|
+
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
|
217
|
+
gap: 2.5rem;
|
|
218
|
+
align-items: center;
|
|
219
|
+
padding: clamp(2.5rem, 4vw, 4rem);
|
|
220
|
+
border-radius: 32px;
|
|
221
|
+
background: var(--glass);
|
|
222
|
+
border: 1px solid var(--edge);
|
|
223
|
+
box-shadow:
|
|
224
|
+
0 40px 90px -40px var(--shadow),
|
|
225
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.7);
|
|
226
|
+
backdrop-filter: blur(18px);
|
|
227
|
+
-webkit-backdrop-filter: blur(18px);
|
|
228
|
+
animation: rise 0.8s cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
@media (prefers-color-scheme: dark) {
|
|
232
|
+
.frame {
|
|
233
|
+
box-shadow:
|
|
234
|
+
0 50px 120px -40px rgba(0, 0, 0, 0.75),
|
|
235
|
+
0 0 120px rgba(106, 169, 255, 0.25),
|
|
236
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
h1.home-title {
|
|
241
|
+
margin: 0.6rem 0 1rem;
|
|
242
|
+
font-family: "Unbounded", "Space Grotesk", sans-serif;
|
|
243
|
+
font-size: clamp(2.2rem, 4vw, 4.4rem);
|
|
244
|
+
letter-spacing: 0.08em;
|
|
245
|
+
text-transform: uppercase;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.steps {
|
|
249
|
+
margin: 0 0 1.8rem;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.steps-box {
|
|
253
|
+
padding: 1rem 1.2rem;
|
|
254
|
+
border-radius: 18px;
|
|
255
|
+
background: rgba(255, 255, 255, 0.35);
|
|
256
|
+
border: 1px solid rgba(18, 19, 22, 0.08);
|
|
257
|
+
backdrop-filter: blur(8px);
|
|
258
|
+
-webkit-backdrop-filter: blur(8px);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@media (prefers-color-scheme: dark) {
|
|
262
|
+
.steps-box {
|
|
263
|
+
background: rgba(6, 8, 14, 0.45);
|
|
264
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.steps-title {
|
|
269
|
+
margin: 0 0 0.7rem;
|
|
270
|
+
font-size: 0.75rem;
|
|
271
|
+
text-transform: uppercase;
|
|
272
|
+
letter-spacing: 0.18em;
|
|
273
|
+
color: var(--muted);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.steps-list {
|
|
277
|
+
margin: 0;
|
|
278
|
+
padding-left: 1.1rem;
|
|
279
|
+
display: grid;
|
|
280
|
+
gap: 0.55rem;
|
|
281
|
+
color: var(--muted);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
.steps-list li::marker {
|
|
285
|
+
color: var(--muted);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.steps-list strong {
|
|
289
|
+
color: var(--ink);
|
|
290
|
+
font-weight: 600;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.meta {
|
|
294
|
+
display: inline-flex;
|
|
295
|
+
align-items: center;
|
|
296
|
+
gap: 0.6rem;
|
|
297
|
+
font-weight: 600;
|
|
298
|
+
color: var(--ink);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.dot {
|
|
302
|
+
width: 10px;
|
|
303
|
+
height: 10px;
|
|
304
|
+
border-radius: 50%;
|
|
305
|
+
background: linear-gradient(135deg, var(--pink), var(--blue));
|
|
306
|
+
box-shadow: 0 0 12px rgba(122, 214, 255, 0.8);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
@media (prefers-color-scheme: dark) {
|
|
310
|
+
.dot {
|
|
311
|
+
box-shadow: 0 0 18px rgba(255, 122, 191, 0.9);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.footer-home {
|
|
316
|
+
margin-top: 1.8rem;
|
|
317
|
+
text-align: center;
|
|
318
|
+
font-size: 0.9rem;
|
|
319
|
+
color: var(--muted);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.footer-home a {
|
|
323
|
+
color: inherit;
|
|
324
|
+
text-decoration: none;
|
|
325
|
+
font-weight: 600;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.footer-home a:hover {
|
|
329
|
+
text-decoration: underline;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.art {
|
|
333
|
+
position: relative;
|
|
334
|
+
display: grid;
|
|
335
|
+
place-items: center;
|
|
336
|
+
min-height: 280px;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.art::before {
|
|
340
|
+
content: "";
|
|
341
|
+
position: absolute;
|
|
342
|
+
width: min(320px, 70vw);
|
|
343
|
+
aspect-ratio: 1;
|
|
344
|
+
border-radius: 28%;
|
|
345
|
+
background: linear-gradient(140deg, rgba(255, 122, 191, 0.25), rgba(122, 214, 255, 0.2), rgba(125, 227, 201, 0.25));
|
|
346
|
+
filter: blur(10px);
|
|
347
|
+
transform: rotate(18deg);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
@media (prefers-color-scheme: dark) {
|
|
351
|
+
.art::before {
|
|
352
|
+
filter: blur(18px);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.art img {
|
|
357
|
+
width: min(300px, 65vw);
|
|
358
|
+
height: auto;
|
|
359
|
+
border-radius: 18%;
|
|
360
|
+
filter: drop-shadow(0 25px 40px rgba(18, 19, 22, 0.25));
|
|
361
|
+
animation: float 6s ease-in-out infinite;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
@media (prefers-color-scheme: dark) {
|
|
365
|
+
.art img {
|
|
366
|
+
filter: drop-shadow(0 30px 60px rgba(106, 169, 255, 0.45));
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
@keyframes float {
|
|
371
|
+
0%, 100% { transform: translateY(0px) rotate(-2deg); }
|
|
372
|
+
50% { transform: translateY(-12px) rotate(2deg); }
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
@keyframes rise {
|
|
376
|
+
from { opacity: 0; transform: translateY(18px) scale(0.98); }
|
|
377
|
+
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
@media (max-width: 760px) {
|
|
381
|
+
.stage {
|
|
382
|
+
padding: 2.5rem 0;
|
|
383
|
+
}
|
|
384
|
+
.frame {
|
|
385
|
+
padding: 2.2rem;
|
|
386
|
+
}
|
|
387
|
+
h1.home-title {
|
|
388
|
+
letter-spacing: 0.05em;
|
|
389
|
+
}
|
|
390
|
+
.art {
|
|
391
|
+
order: -1;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
@media (prefers-reduced-motion: reduce) {
|
|
396
|
+
.frame,
|
|
397
|
+
.art img {
|
|
398
|
+
animation: none;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/* Docs template specific styles */
|
|
403
|
+
.container {
|
|
404
|
+
width: min(840px, 95vw);
|
|
405
|
+
max-height: 85vh;
|
|
406
|
+
display: flex;
|
|
407
|
+
flex-direction: column;
|
|
408
|
+
padding: 2.5rem clamp(1.5rem, 5vw, 3.5rem);
|
|
409
|
+
border-radius: 24px;
|
|
410
|
+
background: var(--glass);
|
|
411
|
+
border: 1px solid var(--edge);
|
|
412
|
+
box-shadow: 0 30px 70px -30px var(--shadow);
|
|
413
|
+
backdrop-filter: blur(20px);
|
|
414
|
+
-webkit-backdrop-filter: blur(20px);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.docs-content {
|
|
418
|
+
overflow-y: auto;
|
|
419
|
+
flex-grow: 1;
|
|
420
|
+
padding-right: 1.5rem;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.docs-content::-webkit-scrollbar {
|
|
424
|
+
width: 8px;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
.docs-content::-webkit-scrollbar-track {
|
|
428
|
+
background: transparent;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.docs-content::-webkit-scrollbar-thumb {
|
|
432
|
+
background: rgba(18, 19, 22, 0.1);
|
|
433
|
+
border-radius: 4px;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.docs-content::-webkit-scrollbar-thumb:hover {
|
|
437
|
+
background: rgba(18, 19, 22, 0.2);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
@media (prefers-color-scheme: dark) {
|
|
441
|
+
.docs-content::-webkit-scrollbar-thumb {
|
|
442
|
+
background: rgba(255, 255, 255, 0.15);
|
|
443
|
+
}
|
|
444
|
+
.docs-content::-webkit-scrollbar-thumb:hover {
|
|
445
|
+
background: rgba(255, 255, 255, 0.25);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.back-link {
|
|
450
|
+
display: inline-flex;
|
|
451
|
+
align-items: center;
|
|
452
|
+
gap: 0.5rem;
|
|
453
|
+
margin-bottom: 2rem;
|
|
454
|
+
color: var(--muted);
|
|
455
|
+
text-decoration: none;
|
|
456
|
+
font-weight: 600;
|
|
457
|
+
font-size: 0.95rem;
|
|
458
|
+
transition: color 0.2s ease;
|
|
459
|
+
flex-shrink: 0;
|
|
460
|
+
border-bottom: none !important;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.back-link:hover {
|
|
464
|
+
color: var(--ink);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
.markdown-body h1 {
|
|
468
|
+
font-family: "Unbounded", "Space Grotesk", sans-serif;
|
|
469
|
+
font-size: clamp(1.8rem, 3.5vw, 2.8rem);
|
|
470
|
+
margin-top: 0;
|
|
471
|
+
margin-bottom: 1.5rem;
|
|
472
|
+
letter-spacing: -0.02em;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.markdown-body h2 {
|
|
476
|
+
font-family: "Unbounded", "Space Grotesk", sans-serif;
|
|
477
|
+
font-size: clamp(1.3rem, 2.5vw, 1.8rem);
|
|
478
|
+
margin-top: 2.5rem;
|
|
479
|
+
margin-bottom: 1rem;
|
|
480
|
+
border-bottom: 2px solid rgba(18, 19, 22, 0.08);
|
|
481
|
+
padding-bottom: 0.4rem;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
@media (prefers-color-scheme: dark) {
|
|
485
|
+
.markdown-body h2 {
|
|
486
|
+
border-bottom-color: rgba(255, 255, 255, 0.1);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.markdown-body h3 {
|
|
491
|
+
font-size: 1.25rem;
|
|
492
|
+
margin-top: 1.8rem;
|
|
493
|
+
margin-bottom: 0.8rem;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.markdown-body p,
|
|
497
|
+
.markdown-body li {
|
|
498
|
+
font-size: 1.05rem;
|
|
499
|
+
line-height: 1.7;
|
|
500
|
+
color: var(--muted);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
.markdown-body p {
|
|
504
|
+
margin: 0 0 1.2rem;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.markdown-body ul,
|
|
508
|
+
.markdown-body ol {
|
|
509
|
+
margin: 0 0 1.5rem;
|
|
510
|
+
padding-left: 1.5rem;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.markdown-body li {
|
|
514
|
+
margin-bottom: 0.6rem;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.markdown-body code {
|
|
518
|
+
font-family: "JetBrains Mono", monospace;
|
|
519
|
+
background: rgba(0, 0, 0, 0.05);
|
|
520
|
+
padding: 0.2rem 0.4rem;
|
|
521
|
+
border-radius: 6px;
|
|
522
|
+
font-size: 0.9rem;
|
|
523
|
+
color: #d13d7a;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
@media (prefers-color-scheme: dark) {
|
|
527
|
+
.markdown-body code {
|
|
528
|
+
background: rgba(255, 255, 255, 0.08);
|
|
529
|
+
color: #ff7abf;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.markdown-body pre {
|
|
534
|
+
background: #181a1f;
|
|
535
|
+
padding: 1.2rem;
|
|
536
|
+
border-radius: 12px;
|
|
537
|
+
overflow-x: auto;
|
|
538
|
+
margin: 1.5rem 0;
|
|
539
|
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.markdown-body pre code {
|
|
543
|
+
background: transparent;
|
|
544
|
+
color: #abb2bf;
|
|
545
|
+
padding: 0;
|
|
546
|
+
font-size: 0.9rem;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.markdown-body table {
|
|
550
|
+
width: 100%;
|
|
551
|
+
border-collapse: collapse;
|
|
552
|
+
margin: 1.5rem 0;
|
|
553
|
+
font-size: 0.95rem;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.markdown-body th,
|
|
557
|
+
.markdown-body td {
|
|
558
|
+
padding: 0.8rem 1rem;
|
|
559
|
+
text-align: left;
|
|
560
|
+
border-bottom: 1px solid rgba(18, 19, 22, 0.08);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
@media (prefers-color-scheme: dark) {
|
|
564
|
+
.markdown-body th,
|
|
565
|
+
.markdown-body td {
|
|
566
|
+
border-bottom-color: rgba(255, 255, 255, 0.1);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
.markdown-body th {
|
|
571
|
+
font-weight: 600;
|
|
572
|
+
background: rgba(0, 0, 0, 0.02);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
@media (prefers-color-scheme: dark) {
|
|
576
|
+
.markdown-body th {
|
|
577
|
+
background: rgba(255, 255, 255, 0.04);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.markdown-body tr:nth-child(even) {
|
|
582
|
+
background: rgba(0, 0, 0, 0.01);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
@media (prefers-color-scheme: dark) {
|
|
586
|
+
.markdown-body tr:nth-child(even) {
|
|
587
|
+
background: rgba(255, 255, 255, 0.02);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.footer-docs {
|
|
592
|
+
text-align: center;
|
|
593
|
+
font-size: 0.9rem;
|
|
594
|
+
color: var(--muted);
|
|
595
|
+
}
|
|
596
|
+
`,
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
// Copy static assets
|
|
338
600
|
fs.copyFileSync(
|
|
339
601
|
path.join(templatesDir, "cerver.png"),
|
|
340
602
|
path.join(projectDir, "public", "cerver.png"),
|
|
@@ -362,9 +624,54 @@ function newProject(name) {
|
|
|
362
624
|
) + "\n",
|
|
363
625
|
);
|
|
364
626
|
|
|
627
|
+
// Read README.md from the cerver project root
|
|
628
|
+
const readmePath = path.join(__dirname, "..", "..", "README.md");
|
|
629
|
+
let readmeContent = "";
|
|
630
|
+
if (fs.existsSync(readmePath)) {
|
|
631
|
+
readmeContent = fs.readFileSync(readmePath, "utf8");
|
|
632
|
+
} else {
|
|
633
|
+
readmeContent = `# Cerver
|
|
634
|
+
|
|
635
|
+
Cerver is a lightweight compile-time web framework.
|
|
636
|
+
`;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const parsedReadme = parseMarkdown(readmeContent);
|
|
640
|
+
|
|
641
|
+
// Generate public/about/about.html
|
|
642
|
+
fs.writeFileSync(
|
|
643
|
+
path.join(projectDir, "public", "about", "about.html"),
|
|
644
|
+
`<!doctype html>
|
|
645
|
+
<html lang="en">
|
|
646
|
+
<head>
|
|
647
|
+
<meta charset="utf-8">
|
|
648
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
649
|
+
<title>About ${name}</title>
|
|
650
|
+
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
|
651
|
+
<link rel="stylesheet" href="/style.css">
|
|
652
|
+
</head>
|
|
653
|
+
<body>
|
|
654
|
+
<div class="container">
|
|
655
|
+
<a href="/" class="back-link">← Back to Home</a>
|
|
656
|
+
<div class="docs-content">
|
|
657
|
+
<article class="markdown-body">
|
|
658
|
+
${parsedReadme}
|
|
659
|
+
</article>
|
|
660
|
+
</div>
|
|
661
|
+
<footer class="footer-docs" style="margin-top: 1.5rem; flex-shrink: 0;">
|
|
662
|
+
Powered by cerver
|
|
663
|
+
</footer>
|
|
664
|
+
</div>
|
|
665
|
+
</body>
|
|
666
|
+
</html>
|
|
667
|
+
`,
|
|
668
|
+
);
|
|
669
|
+
|
|
365
670
|
console.log(" Created:");
|
|
366
|
-
console.log("
|
|
671
|
+
console.log(" routes/index.js");
|
|
367
672
|
console.log(" public/index.html");
|
|
673
|
+
console.log(" public/style.css");
|
|
674
|
+
console.log(" public/about/about.html");
|
|
368
675
|
console.log(" public/cerver.png");
|
|
369
676
|
console.log(" public/favicon.ico");
|
|
370
677
|
console.log(" cerver.config.js");
|
|
@@ -377,4 +684,106 @@ function newProject(name) {
|
|
|
377
684
|
console.log("");
|
|
378
685
|
}
|
|
379
686
|
|
|
687
|
+
function parseMarkdown(md) {
|
|
688
|
+
const lines = md.split("\n");
|
|
689
|
+
const htmlLines = [];
|
|
690
|
+
let inList = false;
|
|
691
|
+
let inCode = false;
|
|
692
|
+
let codeBlock = [];
|
|
693
|
+
|
|
694
|
+
for (let i = 0; i < lines.length; i++) {
|
|
695
|
+
const line = lines[i];
|
|
696
|
+
|
|
697
|
+
if (line.startsWith("```")) {
|
|
698
|
+
if (inCode) {
|
|
699
|
+
inCode = false;
|
|
700
|
+
htmlLines.push(`<pre><code>${codeBlock.join("\n")}</code></pre>`);
|
|
701
|
+
codeBlock = [];
|
|
702
|
+
} else {
|
|
703
|
+
inCode = true;
|
|
704
|
+
}
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (inCode) {
|
|
709
|
+
codeBlock.push(line.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"));
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
if (line.trim().startsWith("- ")) {
|
|
714
|
+
if (!inList) {
|
|
715
|
+
htmlLines.push("<ul>");
|
|
716
|
+
inList = true;
|
|
717
|
+
}
|
|
718
|
+
const itemText = line.trim().slice(2);
|
|
719
|
+
htmlLines.push(` <li>${inlineFormatting(itemText)}</li>`);
|
|
720
|
+
continue;
|
|
721
|
+
} else if (line.trim().startsWith("|") || line.trim() === "") {
|
|
722
|
+
if (inList) {
|
|
723
|
+
htmlLines.push("</ul>");
|
|
724
|
+
inList = false;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
if (line.startsWith("# ")) {
|
|
729
|
+
htmlLines.push(`<h1>${inlineFormatting(line.slice(2))}</h1>`);
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
if (line.startsWith("## ")) {
|
|
733
|
+
htmlLines.push(`<h2>${inlineFormatting(line.slice(3))}</h2>`);
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
if (line.startsWith("### ")) {
|
|
737
|
+
htmlLines.push(`<h3>${inlineFormatting(line.slice(4))}</h3>`);
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
if (line.trim().startsWith("|")) {
|
|
742
|
+
if (line.includes("---")) {
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
const cols = line.split("|").map(c => c.trim()).filter((c, idx, arr) => idx > 0 && idx < arr.length - 1);
|
|
746
|
+
htmlLines.push(`<tr>${cols.map(c => `<td>${inlineFormatting(c)}</td>`).join("")}</tr>`);
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
if (line.trim() !== "") {
|
|
751
|
+
htmlLines.push(`<p>${inlineFormatting(line)}</p>`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
if (inList) {
|
|
756
|
+
htmlLines.push("</ul>");
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
let joined = htmlLines.join("\n");
|
|
760
|
+
joined = joined.replace(/(<tr>.*?<\/tr>)+/gs, "<table>\n$&\n</table>");
|
|
761
|
+
|
|
762
|
+
return joined;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function inlineFormatting(text) {
|
|
766
|
+
let escaped = text
|
|
767
|
+
.replace(/&/g, "&")
|
|
768
|
+
.replace(/</g, "<")
|
|
769
|
+
.replace(/>/g, ">");
|
|
770
|
+
|
|
771
|
+
// Map raw Cerver Logo html tag from templates/cerver.png to /cerver.png
|
|
772
|
+
escaped = escaped.replace(/<img src="templates\/cerver\.png" alt="Cerver Logo" width="200px" align="right" \/>/g, '<img src="/cerver.png" alt="Cerver Logo" width="100" style="float: right; margin-left: 1.5rem; border-radius: 12px; max-width: 100%; height: auto;" />');
|
|
773
|
+
|
|
774
|
+
// Handle Markdown images: 
|
|
775
|
+
escaped = escaped.replace(/!\[([^\]]*?)\]\(([^)]+?)\)/g, '<img src="$2" alt="$1" style="height: 20px; vertical-align: middle; display: inline-block; margin: 2px;" />');
|
|
776
|
+
|
|
777
|
+
// Handle Markdown links: [text](url)
|
|
778
|
+
escaped = escaped.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
|
|
779
|
+
|
|
780
|
+
// Handle bold: **text**
|
|
781
|
+
escaped = escaped.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
782
|
+
|
|
783
|
+
// Handle inline code: `code`
|
|
784
|
+
escaped = escaped.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
785
|
+
|
|
786
|
+
return escaped;
|
|
787
|
+
}
|
|
788
|
+
|
|
380
789
|
module.exports = { newProject };
|