clou-lang 0.2.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 +128 -0
- package/ai/clou-ai-prompt.md +239 -0
- package/bin/clou.js +281 -0
- package/examples/calculator.clou +41 -0
- package/examples/hello-terminal.clou +22 -0
- package/examples/hello.clou +37 -0
- package/examples/hello.html +220 -0
- package/examples/multipage/about.html +319 -0
- package/examples/multipage/contact.html +308 -0
- package/examples/multipage/index.html +322 -0
- package/examples/multipage/shared.clou +19 -0
- package/examples/multipage/site.clou +102 -0
- package/examples/portfolio.clou +51 -0
- package/examples/portfolio.html +217 -0
- package/examples/quiz.clou +90 -0
- package/examples/showcase.clou +136 -0
- package/examples/showcase.html +410 -0
- package/examples/startup.clou +153 -0
- package/examples/startup.html +469 -0
- package/examples/themes-demo.clou +117 -0
- package/examples/themes-demo.html +429 -0
- package/package.json +48 -0
- package/playground/clou-browser.js +2576 -0
- package/playground/index.html +682 -0
- package/src/bundle-browser.js +62 -0
- package/src/compiler.js +761 -0
- package/src/devserver.js +154 -0
- package/src/index.js +87 -0
- package/src/lexer.js +456 -0
- package/src/parser.js +879 -0
- package/src/terminal-parser.js +358 -0
- package/src/terminal-runtime.js +310 -0
- package/src/themes.js +469 -0
|
@@ -0,0 +1,682 @@
|
|
|
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>Clou Playground - Build Websites Live</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
|
+
|
|
10
|
+
:root {
|
|
11
|
+
--bg: #0f0f23;
|
|
12
|
+
--bg2: #1a1a35;
|
|
13
|
+
--bg3: #252545;
|
|
14
|
+
--text: #e0e0e0;
|
|
15
|
+
--accent: #6C63FF;
|
|
16
|
+
--accent2: #00e5ff;
|
|
17
|
+
--border: #2a2a4a;
|
|
18
|
+
--green: #00ff88;
|
|
19
|
+
--red: #ff6b6b;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
body {
|
|
23
|
+
background: var(--bg);
|
|
24
|
+
color: var(--text);
|
|
25
|
+
font-family: 'Segoe UI', system-ui, sans-serif;
|
|
26
|
+
height: 100vh;
|
|
27
|
+
overflow: hidden;
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-direction: column;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* ── Header ── */
|
|
33
|
+
.header {
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: space-between;
|
|
37
|
+
padding: 10px 20px;
|
|
38
|
+
background: var(--bg2);
|
|
39
|
+
border-bottom: 1px solid var(--border);
|
|
40
|
+
height: 52px;
|
|
41
|
+
flex-shrink: 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.logo {
|
|
45
|
+
font-size: 1.3em;
|
|
46
|
+
font-weight: 700;
|
|
47
|
+
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
|
48
|
+
-webkit-background-clip: text;
|
|
49
|
+
-webkit-text-fill-color: transparent;
|
|
50
|
+
background-clip: text;
|
|
51
|
+
letter-spacing: -0.5px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.logo span {
|
|
55
|
+
-webkit-text-fill-color: #888;
|
|
56
|
+
font-weight: 400;
|
|
57
|
+
font-size: 0.75em;
|
|
58
|
+
margin-left: 8px;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.header-actions {
|
|
62
|
+
display: flex;
|
|
63
|
+
gap: 8px;
|
|
64
|
+
align-items: center;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.theme-select {
|
|
68
|
+
padding: 6px 12px;
|
|
69
|
+
background: var(--bg3);
|
|
70
|
+
border: 1px solid var(--border);
|
|
71
|
+
border-radius: 6px;
|
|
72
|
+
color: var(--text);
|
|
73
|
+
font-size: 13px;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
outline: none;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.theme-select:focus { border-color: var(--accent); }
|
|
79
|
+
|
|
80
|
+
.btn {
|
|
81
|
+
padding: 6px 14px;
|
|
82
|
+
border: 1px solid var(--border);
|
|
83
|
+
border-radius: 6px;
|
|
84
|
+
background: var(--bg3);
|
|
85
|
+
color: var(--text);
|
|
86
|
+
font-size: 13px;
|
|
87
|
+
cursor: pointer;
|
|
88
|
+
transition: all 0.15s;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.btn:hover { background: var(--accent); color: white; border-color: var(--accent); }
|
|
92
|
+
.btn-accent { background: var(--accent); color: white; border-color: var(--accent); }
|
|
93
|
+
.btn-accent:hover { background: #5a52d5; }
|
|
94
|
+
|
|
95
|
+
/* ── Examples Bar ── */
|
|
96
|
+
.examples-bar {
|
|
97
|
+
display: flex;
|
|
98
|
+
gap: 6px;
|
|
99
|
+
padding: 8px 20px;
|
|
100
|
+
background: var(--bg);
|
|
101
|
+
border-bottom: 1px solid var(--border);
|
|
102
|
+
overflow-x: auto;
|
|
103
|
+
flex-shrink: 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.example-btn {
|
|
107
|
+
padding: 4px 12px;
|
|
108
|
+
border: 1px solid var(--border);
|
|
109
|
+
border-radius: 20px;
|
|
110
|
+
background: transparent;
|
|
111
|
+
color: #888;
|
|
112
|
+
font-size: 12px;
|
|
113
|
+
cursor: pointer;
|
|
114
|
+
white-space: nowrap;
|
|
115
|
+
transition: all 0.15s;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.example-btn:hover { color: var(--accent); border-color: var(--accent); }
|
|
119
|
+
.example-btn.active { color: var(--accent); border-color: var(--accent); background: rgba(108, 99, 255, 0.1); }
|
|
120
|
+
|
|
121
|
+
/* ── Main Split ── */
|
|
122
|
+
.main {
|
|
123
|
+
display: flex;
|
|
124
|
+
flex: 1;
|
|
125
|
+
overflow: hidden;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* ── Editor Panel ── */
|
|
129
|
+
.editor-panel {
|
|
130
|
+
width: 45%;
|
|
131
|
+
display: flex;
|
|
132
|
+
flex-direction: column;
|
|
133
|
+
border-right: 1px solid var(--border);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.panel-header {
|
|
137
|
+
padding: 8px 16px;
|
|
138
|
+
font-size: 12px;
|
|
139
|
+
font-weight: 600;
|
|
140
|
+
text-transform: uppercase;
|
|
141
|
+
letter-spacing: 1px;
|
|
142
|
+
color: #666;
|
|
143
|
+
background: var(--bg2);
|
|
144
|
+
border-bottom: 1px solid var(--border);
|
|
145
|
+
display: flex;
|
|
146
|
+
justify-content: space-between;
|
|
147
|
+
align-items: center;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.status {
|
|
151
|
+
font-size: 11px;
|
|
152
|
+
font-weight: 400;
|
|
153
|
+
text-transform: none;
|
|
154
|
+
letter-spacing: 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.status-ok { color: var(--green); }
|
|
158
|
+
.status-err { color: var(--red); }
|
|
159
|
+
|
|
160
|
+
#editor {
|
|
161
|
+
flex: 1;
|
|
162
|
+
width: 100%;
|
|
163
|
+
background: var(--bg);
|
|
164
|
+
color: #e0e0e0;
|
|
165
|
+
border: none;
|
|
166
|
+
outline: none;
|
|
167
|
+
padding: 16px;
|
|
168
|
+
font-family: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', 'Consolas', monospace;
|
|
169
|
+
font-size: 14px;
|
|
170
|
+
line-height: 1.6;
|
|
171
|
+
resize: none;
|
|
172
|
+
tab-size: 4;
|
|
173
|
+
-moz-tab-size: 4;
|
|
174
|
+
white-space: pre;
|
|
175
|
+
overflow: auto;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
#editor::placeholder { color: #444; }
|
|
179
|
+
|
|
180
|
+
/* ── Preview Panel ── */
|
|
181
|
+
.preview-panel {
|
|
182
|
+
flex: 1;
|
|
183
|
+
display: flex;
|
|
184
|
+
flex-direction: column;
|
|
185
|
+
background: white;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
#preview {
|
|
189
|
+
flex: 1;
|
|
190
|
+
border: none;
|
|
191
|
+
width: 100%;
|
|
192
|
+
height: 100%;
|
|
193
|
+
background: white;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* ── Error Toast ── */
|
|
197
|
+
.error-bar {
|
|
198
|
+
display: none;
|
|
199
|
+
padding: 8px 16px;
|
|
200
|
+
background: rgba(255, 107, 107, 0.1);
|
|
201
|
+
border-top: 1px solid rgba(255, 107, 107, 0.3);
|
|
202
|
+
color: var(--red);
|
|
203
|
+
font-family: monospace;
|
|
204
|
+
font-size: 13px;
|
|
205
|
+
max-height: 80px;
|
|
206
|
+
overflow-y: auto;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.error-bar.visible { display: block; }
|
|
210
|
+
|
|
211
|
+
/* ── Resize Handle ── */
|
|
212
|
+
.resize-handle {
|
|
213
|
+
width: 4px;
|
|
214
|
+
cursor: col-resize;
|
|
215
|
+
background: var(--border);
|
|
216
|
+
transition: background 0.15s;
|
|
217
|
+
}
|
|
218
|
+
.resize-handle:hover { background: var(--accent); }
|
|
219
|
+
|
|
220
|
+
/* ── Responsive ── */
|
|
221
|
+
@media (max-width: 768px) {
|
|
222
|
+
.main { flex-direction: column; }
|
|
223
|
+
.editor-panel { width: 100%; height: 50%; border-right: none; border-bottom: 1px solid var(--border); }
|
|
224
|
+
.resize-handle { display: none; }
|
|
225
|
+
}
|
|
226
|
+
</style>
|
|
227
|
+
</head>
|
|
228
|
+
<body>
|
|
229
|
+
|
|
230
|
+
<div class="header">
|
|
231
|
+
<div class="logo">Clou Playground <span>v0.2</span></div>
|
|
232
|
+
<div class="header-actions">
|
|
233
|
+
<select class="theme-select" id="themeSelect">
|
|
234
|
+
<option value="">No Theme</option>
|
|
235
|
+
</select>
|
|
236
|
+
<button class="btn" id="copyBtn">Copy HTML</button>
|
|
237
|
+
<button class="btn btn-accent" id="downloadBtn">Download</button>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<div class="examples-bar" id="examplesBar"></div>
|
|
242
|
+
|
|
243
|
+
<div class="main">
|
|
244
|
+
<div class="editor-panel" id="editorPanel">
|
|
245
|
+
<div class="panel-header">
|
|
246
|
+
<span>Editor — .clou</span>
|
|
247
|
+
<span class="status" id="status"></span>
|
|
248
|
+
</div>
|
|
249
|
+
<textarea id="editor" spellcheck="false" placeholder="Write your Clou code here..."></textarea>
|
|
250
|
+
<div class="error-bar" id="errorBar"></div>
|
|
251
|
+
</div>
|
|
252
|
+
<div class="resize-handle" id="resizeHandle"></div>
|
|
253
|
+
<div class="preview-panel">
|
|
254
|
+
<div class="panel-header">Preview</div>
|
|
255
|
+
<iframe id="preview" sandbox="allow-scripts allow-modals"></iframe>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<script src="clou-browser.js"></script>
|
|
260
|
+
<script>
|
|
261
|
+
(function() {
|
|
262
|
+
const editor = document.getElementById('editor');
|
|
263
|
+
const preview = document.getElementById('preview');
|
|
264
|
+
const status = document.getElementById('status');
|
|
265
|
+
const errorBar = document.getElementById('errorBar');
|
|
266
|
+
const themeSelect = document.getElementById('themeSelect');
|
|
267
|
+
const copyBtn = document.getElementById('copyBtn');
|
|
268
|
+
const downloadBtn = document.getElementById('downloadBtn');
|
|
269
|
+
const examplesBar = document.getElementById('examplesBar');
|
|
270
|
+
|
|
271
|
+
let lastHtml = '';
|
|
272
|
+
let compileTimer = null;
|
|
273
|
+
|
|
274
|
+
// ── Populate Themes ──
|
|
275
|
+
Clou.getThemeNames().forEach(name => {
|
|
276
|
+
const opt = document.createElement('option');
|
|
277
|
+
opt.value = name;
|
|
278
|
+
opt.textContent = name.charAt(0).toUpperCase() + name.slice(1);
|
|
279
|
+
themeSelect.appendChild(opt);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
// ── Examples ──
|
|
283
|
+
const examples = {
|
|
284
|
+
'Hello World': `page "My First Website":
|
|
285
|
+
title "Hello Clou!"
|
|
286
|
+
|
|
287
|
+
box:
|
|
288
|
+
heading "Welcome!"
|
|
289
|
+
text "This is my first website made with Clou."
|
|
290
|
+
text "Edit this code on the left to see changes!"
|
|
291
|
+
|
|
292
|
+
button "Click me":
|
|
293
|
+
show message "Hello from Clou!"
|
|
294
|
+
|
|
295
|
+
style:
|
|
296
|
+
background color #f0f4f8
|
|
297
|
+
text color #333
|
|
298
|
+
button color #4A90D9
|
|
299
|
+
center`,
|
|
300
|
+
|
|
301
|
+
'Landing Page': `set brand to "SpaceApp"
|
|
302
|
+
|
|
303
|
+
template feature(emoji, title, desc):
|
|
304
|
+
card:
|
|
305
|
+
icon "{emoji}"
|
|
306
|
+
heading "{title}"
|
|
307
|
+
text "{desc}"
|
|
308
|
+
animate slide
|
|
309
|
+
|
|
310
|
+
page "{brand} - Launch Your Ideas":
|
|
311
|
+
theme "midnight"
|
|
312
|
+
|
|
313
|
+
navbar "{brand}":
|
|
314
|
+
link "Features" to "#features"
|
|
315
|
+
link "Pricing" to "#pricing"
|
|
316
|
+
link "Start" to "#cta"
|
|
317
|
+
|
|
318
|
+
section "hero":
|
|
319
|
+
center
|
|
320
|
+
space 30
|
|
321
|
+
heading "Launch Your Ideas Into Space":
|
|
322
|
+
huge
|
|
323
|
+
animate fade
|
|
324
|
+
text "The fastest way to build and ship your next project.":
|
|
325
|
+
big
|
|
326
|
+
space 20
|
|
327
|
+
row:
|
|
328
|
+
center
|
|
329
|
+
button "Get Started":
|
|
330
|
+
show message "Welcome to {brand}!"
|
|
331
|
+
button "Learn More":
|
|
332
|
+
go to "#features"
|
|
333
|
+
space 30
|
|
334
|
+
|
|
335
|
+
section "features":
|
|
336
|
+
center
|
|
337
|
+
heading "Why {brand}?":
|
|
338
|
+
huge
|
|
339
|
+
space 20
|
|
340
|
+
grid:
|
|
341
|
+
use feature("⚡", "Blazing Fast", "Built for speed from the ground up.")
|
|
342
|
+
use feature("🔒", "Secure", "Enterprise-grade security by default.")
|
|
343
|
+
use feature("🎨", "Beautiful", "Stunning UI without the effort.")
|
|
344
|
+
|
|
345
|
+
section "cta":
|
|
346
|
+
center
|
|
347
|
+
card:
|
|
348
|
+
gradient #6C63FF to #00e5ff
|
|
349
|
+
padding 60
|
|
350
|
+
center
|
|
351
|
+
heading "Ready to Launch?":
|
|
352
|
+
huge
|
|
353
|
+
animate bounce
|
|
354
|
+
text "Start building for free today."
|
|
355
|
+
space 20
|
|
356
|
+
button "Start Now":
|
|
357
|
+
show message "Let's go!"`,
|
|
358
|
+
|
|
359
|
+
'Portfolio': `set name to "Alex"
|
|
360
|
+
|
|
361
|
+
page "{name}'s Portfolio":
|
|
362
|
+
theme "glass"
|
|
363
|
+
|
|
364
|
+
navbar "{name}":
|
|
365
|
+
link "Work" to "#work"
|
|
366
|
+
link "About" to "#about"
|
|
367
|
+
link "Contact" to "#contact"
|
|
368
|
+
|
|
369
|
+
section "hero":
|
|
370
|
+
center
|
|
371
|
+
space 40
|
|
372
|
+
heading "Hi, I'm {name}":
|
|
373
|
+
huge
|
|
374
|
+
animate fade
|
|
375
|
+
text "Designer & Developer":
|
|
376
|
+
big
|
|
377
|
+
space 40
|
|
378
|
+
|
|
379
|
+
section "work":
|
|
380
|
+
center
|
|
381
|
+
heading "My Work":
|
|
382
|
+
huge
|
|
383
|
+
space 20
|
|
384
|
+
grid:
|
|
385
|
+
card:
|
|
386
|
+
heading "Project Alpha"
|
|
387
|
+
text "A mobile app for fitness tracking"
|
|
388
|
+
animate slide
|
|
389
|
+
card:
|
|
390
|
+
heading "Project Beta"
|
|
391
|
+
text "An e-commerce website redesign"
|
|
392
|
+
animate slide
|
|
393
|
+
card:
|
|
394
|
+
heading "Project Gamma"
|
|
395
|
+
text "A dashboard for data analytics"
|
|
396
|
+
animate slide
|
|
397
|
+
|
|
398
|
+
section "contact":
|
|
399
|
+
center
|
|
400
|
+
card:
|
|
401
|
+
center
|
|
402
|
+
padding 40
|
|
403
|
+
heading "Get In Touch"
|
|
404
|
+
space 10
|
|
405
|
+
input "Your email"
|
|
406
|
+
input "Your message"
|
|
407
|
+
space 10
|
|
408
|
+
button "Send":
|
|
409
|
+
show message "Thanks! I'll get back to you soon."
|
|
410
|
+
|
|
411
|
+
footer:
|
|
412
|
+
text "© 2026 {name}. Built with Clou."`,
|
|
413
|
+
|
|
414
|
+
'Neon Theme': `page "Neon Vibes":
|
|
415
|
+
theme "neon"
|
|
416
|
+
|
|
417
|
+
navbar "NEON":
|
|
418
|
+
link "Home" to "#"
|
|
419
|
+
link "About" to "#"
|
|
420
|
+
link "Contact" to "#"
|
|
421
|
+
|
|
422
|
+
section "hero":
|
|
423
|
+
center
|
|
424
|
+
space 30
|
|
425
|
+
heading "Welcome to the Future":
|
|
426
|
+
huge
|
|
427
|
+
animate fade
|
|
428
|
+
text "Cyberpunk aesthetics. One line of code.":
|
|
429
|
+
big
|
|
430
|
+
space 20
|
|
431
|
+
button "Enter the Grid":
|
|
432
|
+
show message "Welcome, hacker."
|
|
433
|
+
space 30
|
|
434
|
+
|
|
435
|
+
grid:
|
|
436
|
+
card:
|
|
437
|
+
icon "🟢"
|
|
438
|
+
heading "Neon Glow"
|
|
439
|
+
text "Electric green on pure black."
|
|
440
|
+
animate slide
|
|
441
|
+
card:
|
|
442
|
+
icon "⚡"
|
|
443
|
+
heading "Gradient Text"
|
|
444
|
+
text "Headers shimmer with color."
|
|
445
|
+
animate slide
|
|
446
|
+
card:
|
|
447
|
+
icon "🌃"
|
|
448
|
+
heading "Dark Mode"
|
|
449
|
+
text "Easy on the eyes, hard to forget."
|
|
450
|
+
animate slide
|
|
451
|
+
|
|
452
|
+
footer:
|
|
453
|
+
text "Powered by Clou"`,
|
|
454
|
+
|
|
455
|
+
'Candy Theme': `page "Sweet Shop":
|
|
456
|
+
theme "candy"
|
|
457
|
+
|
|
458
|
+
navbar "Sweet Shop":
|
|
459
|
+
link "Menu" to "#menu"
|
|
460
|
+
link "About" to "#about"
|
|
461
|
+
link "Order" to "#order"
|
|
462
|
+
|
|
463
|
+
section "hero":
|
|
464
|
+
center
|
|
465
|
+
space 30
|
|
466
|
+
icon "🍬"
|
|
467
|
+
heading "Life is Sweet":
|
|
468
|
+
huge
|
|
469
|
+
animate bounce
|
|
470
|
+
text "The cutest little candy shop on the internet.":
|
|
471
|
+
big
|
|
472
|
+
space 30
|
|
473
|
+
|
|
474
|
+
section "menu":
|
|
475
|
+
center
|
|
476
|
+
heading "Our Treats":
|
|
477
|
+
huge
|
|
478
|
+
space 20
|
|
479
|
+
grid:
|
|
480
|
+
card:
|
|
481
|
+
icon "🍩"
|
|
482
|
+
heading "Donuts"
|
|
483
|
+
text "Freshly glazed, always amazing."
|
|
484
|
+
animate slide
|
|
485
|
+
card:
|
|
486
|
+
icon "🧁"
|
|
487
|
+
heading "Cupcakes"
|
|
488
|
+
text "Fluffy clouds of sweetness."
|
|
489
|
+
animate slide
|
|
490
|
+
card:
|
|
491
|
+
icon "🍪"
|
|
492
|
+
heading "Cookies"
|
|
493
|
+
text "Crunchy on the outside, soft inside."
|
|
494
|
+
animate slide
|
|
495
|
+
|
|
496
|
+
section "order":
|
|
497
|
+
center
|
|
498
|
+
card:
|
|
499
|
+
center
|
|
500
|
+
padding 40
|
|
501
|
+
heading "Place an Order"
|
|
502
|
+
space 10
|
|
503
|
+
input "Your name"
|
|
504
|
+
input "What would you like?"
|
|
505
|
+
space 10
|
|
506
|
+
button "Order Now":
|
|
507
|
+
show message "Order placed! 🍬"
|
|
508
|
+
|
|
509
|
+
footer:
|
|
510
|
+
text "Sweet Shop © 2026 — Made with Clou"`,
|
|
511
|
+
|
|
512
|
+
'Minimal': `page "Minimal":
|
|
513
|
+
theme "minimal"
|
|
514
|
+
|
|
515
|
+
section "main":
|
|
516
|
+
space 40
|
|
517
|
+
heading "Less is more.":
|
|
518
|
+
huge
|
|
519
|
+
animate fade
|
|
520
|
+
space 10
|
|
521
|
+
text "A clean canvas for your ideas. No noise. No distractions."
|
|
522
|
+
text "Just content that matters."
|
|
523
|
+
space 30
|
|
524
|
+
line
|
|
525
|
+
space 30
|
|
526
|
+
heading "Features"
|
|
527
|
+
space 10
|
|
528
|
+
list:
|
|
529
|
+
text "Clean typography"
|
|
530
|
+
text "Focused layout"
|
|
531
|
+
text "Black and white"
|
|
532
|
+
text "Zero clutter"
|
|
533
|
+
space 30
|
|
534
|
+
line
|
|
535
|
+
space 30
|
|
536
|
+
button "Get Started":
|
|
537
|
+
show message "Keep it simple."
|
|
538
|
+
space 40
|
|
539
|
+
|
|
540
|
+
footer:
|
|
541
|
+
text "Built with Clou — the simplest way to build websites."`,
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
// Create example buttons
|
|
545
|
+
Object.keys(examples).forEach((name, i) => {
|
|
546
|
+
const btn = document.createElement('button');
|
|
547
|
+
btn.className = 'example-btn' + (i === 0 ? ' active' : '');
|
|
548
|
+
btn.textContent = name;
|
|
549
|
+
btn.onclick = () => {
|
|
550
|
+
editor.value = examples[name];
|
|
551
|
+
document.querySelectorAll('.example-btn').forEach(b => b.classList.remove('active'));
|
|
552
|
+
btn.classList.add('active');
|
|
553
|
+
// Auto-detect theme
|
|
554
|
+
const themeMatch = examples[name].match(/theme\s+"(\w+)"/);
|
|
555
|
+
themeSelect.value = themeMatch ? themeMatch[1] : '';
|
|
556
|
+
compileAndRender();
|
|
557
|
+
};
|
|
558
|
+
examplesBar.appendChild(btn);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
// ── Compile & Render ──
|
|
562
|
+
function compileAndRender() {
|
|
563
|
+
let source = editor.value;
|
|
564
|
+
|
|
565
|
+
// If theme selected and not already in code, inject it
|
|
566
|
+
const selectedTheme = themeSelect.value;
|
|
567
|
+
if (selectedTheme && !source.includes('theme "')) {
|
|
568
|
+
// Find page line and inject theme after it
|
|
569
|
+
const lines = source.split('\n');
|
|
570
|
+
let injected = false;
|
|
571
|
+
for (let i = 0; i < lines.length; i++) {
|
|
572
|
+
if (lines[i].trim().startsWith('page ')) {
|
|
573
|
+
// Find next indented line
|
|
574
|
+
const indent = lines[i + 1] ? lines[i + 1].match(/^(\s*)/)[1] : ' ';
|
|
575
|
+
lines.splice(i + 1, 0, indent + 'theme "' + selectedTheme + '"');
|
|
576
|
+
injected = true;
|
|
577
|
+
break;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (!injected) {
|
|
581
|
+
source = 'theme "' + selectedTheme + '"\n' + source;
|
|
582
|
+
} else {
|
|
583
|
+
source = lines.join('\n');
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
try {
|
|
588
|
+
const result = Clou.buildClou(source);
|
|
589
|
+
lastHtml = result.html;
|
|
590
|
+
|
|
591
|
+
// Write to iframe
|
|
592
|
+
const doc = preview.contentDocument || preview.contentWindow.document;
|
|
593
|
+
doc.open();
|
|
594
|
+
doc.write(lastHtml);
|
|
595
|
+
doc.close();
|
|
596
|
+
|
|
597
|
+
status.textContent = 'Compiled';
|
|
598
|
+
status.className = 'status status-ok';
|
|
599
|
+
errorBar.classList.remove('visible');
|
|
600
|
+
} catch (err) {
|
|
601
|
+
status.textContent = 'Error';
|
|
602
|
+
status.className = 'status status-err';
|
|
603
|
+
errorBar.textContent = err.message;
|
|
604
|
+
errorBar.classList.add('visible');
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// ── Auto-compile on input ──
|
|
609
|
+
editor.addEventListener('input', () => {
|
|
610
|
+
clearTimeout(compileTimer);
|
|
611
|
+
compileTimer = setTimeout(compileAndRender, 200);
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
// ── Tab key support ──
|
|
615
|
+
editor.addEventListener('keydown', (e) => {
|
|
616
|
+
if (e.key === 'Tab') {
|
|
617
|
+
e.preventDefault();
|
|
618
|
+
const start = editor.selectionStart;
|
|
619
|
+
const end = editor.selectionEnd;
|
|
620
|
+
editor.value = editor.value.substring(0, start) + ' ' + editor.value.substring(end);
|
|
621
|
+
editor.selectionStart = editor.selectionEnd = start + 4;
|
|
622
|
+
compileAndRender();
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
|
|
626
|
+
// ── Theme select ──
|
|
627
|
+
themeSelect.addEventListener('change', compileAndRender);
|
|
628
|
+
|
|
629
|
+
// ── Copy HTML ──
|
|
630
|
+
copyBtn.addEventListener('click', () => {
|
|
631
|
+
if (lastHtml) {
|
|
632
|
+
navigator.clipboard.writeText(lastHtml).then(() => {
|
|
633
|
+
copyBtn.textContent = 'Copied!';
|
|
634
|
+
setTimeout(() => copyBtn.textContent = 'Copy HTML', 1500);
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
// ── Download HTML ──
|
|
640
|
+
downloadBtn.addEventListener('click', () => {
|
|
641
|
+
if (!lastHtml) return;
|
|
642
|
+
const blob = new Blob([lastHtml], { type: 'text/html' });
|
|
643
|
+
const url = URL.createObjectURL(blob);
|
|
644
|
+
const a = document.createElement('a');
|
|
645
|
+
a.href = url;
|
|
646
|
+
a.download = 'clou-website.html';
|
|
647
|
+
a.click();
|
|
648
|
+
URL.revokeObjectURL(url);
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
// ── Resize Handle ──
|
|
652
|
+
const resizeHandle = document.getElementById('resizeHandle');
|
|
653
|
+
const editorPanel = document.getElementById('editorPanel');
|
|
654
|
+
let isResizing = false;
|
|
655
|
+
|
|
656
|
+
resizeHandle.addEventListener('mousedown', (e) => {
|
|
657
|
+
isResizing = true;
|
|
658
|
+
document.body.style.cursor = 'col-resize';
|
|
659
|
+
document.body.style.userSelect = 'none';
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
document.addEventListener('mousemove', (e) => {
|
|
663
|
+
if (!isResizing) return;
|
|
664
|
+
const percent = (e.clientX / window.innerWidth) * 100;
|
|
665
|
+
if (percent > 20 && percent < 80) {
|
|
666
|
+
editorPanel.style.width = percent + '%';
|
|
667
|
+
}
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
document.addEventListener('mouseup', () => {
|
|
671
|
+
isResizing = false;
|
|
672
|
+
document.body.style.cursor = '';
|
|
673
|
+
document.body.style.userSelect = '';
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
// ── Load first example ──
|
|
677
|
+
editor.value = examples['Hello World'];
|
|
678
|
+
compileAndRender();
|
|
679
|
+
})();
|
|
680
|
+
</script>
|
|
681
|
+
</body>
|
|
682
|
+
</html>
|