juxscript 1.0.67 → 1.0.69

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/bin/cli.js CHANGED
@@ -6,362 +6,121 @@ import { fileURLToPath } from 'url';
6
6
 
7
7
  const __filename = fileURLToPath(import.meta.url);
8
8
  const __dirname = path.dirname(__filename);
9
-
10
9
  const command = process.argv[2];
11
10
 
12
- // ═══════════════════════════════════════════════════════════════════
13
- // CREATE COMMAND - Runs BEFORE dependencies are installed
14
- // ═══════════════════════════════════════════════════════════════════
11
+ function scaffoldProject(targetDir, packageRoot) {
12
+ const juxDir = path.join(targetDir, 'jux');
13
+ if (!fs.existsSync(juxDir)) fs.mkdirSync(juxDir, { recursive: true });
14
+ const presetDir = path.join(packageRoot, 'create');
15
+ let copied = 0;
16
+ if (fs.existsSync(presetDir)) {
17
+ fs.readdirSync(presetDir).forEach(file => {
18
+ if (file.startsWith('.')) return;
19
+ if (fs.statSync(path.join(presetDir, file)).isFile()) {
20
+ fs.copyFileSync(path.join(presetDir, file), path.join(juxDir, file));
21
+ copied++;
22
+ }
23
+ });
24
+ }
25
+ if (copied === 0 && !fs.existsSync(path.join(juxDir, 'index.jux'))) {
26
+ fs.writeFileSync(path.join(juxDir, 'index.jux'), `import { Element } from 'juxscript';\nElement('welcome', { tag: 'h1' }).text('Welcome to JUX!').render('app');\n`);
27
+ }
28
+ const configSrc = path.join(packageRoot, 'juxconfig.example.js');
29
+ const configDest = path.join(targetDir, 'juxconfig.js');
30
+ if (fs.existsSync(configSrc) && !fs.existsSync(configDest)) fs.copyFileSync(configSrc, configDest);
31
+ const gitIgnore = path.join(targetDir, '.gitignore');
32
+ if (!fs.existsSync(gitIgnore)) fs.writeFileSync(gitIgnore, `.jux-dist/\nnode_modules/\n.DS_Store\n.env\n*.log\n`);
33
+ const pkgPath = path.join(targetDir, 'package.json');
34
+ if (fs.existsSync(pkgPath)) {
35
+ try {
36
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
37
+ pkg.scripts = pkg.scripts || {};
38
+ let modified = false;
39
+ if (!pkg.scripts.dev) { pkg.scripts.dev = 'jux serve'; modified = true; }
40
+ if (!pkg.scripts.build) { pkg.scripts.build = 'jux build'; modified = true; }
41
+ if (modified) fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
42
+ } catch (e) { }
43
+ }
44
+ }
15
45
 
16
46
  if (command === 'create') {
17
- // ✅ FIX: Removed timestamp fallback from default name
18
47
  const projectName = process.argv[3] || 'my-jux-app';
19
-
20
48
  const projectPath = path.join(process.cwd(), projectName);
21
-
22
- console.log(`
23
- ╔═══════════════════════════════════════════════════════╗
24
- ║ ║
25
- ║ 🎨 Welcome to JUX ║
26
- ║ ║
27
- ║ Creating your new JUX project... ║
28
- ║ ║
29
- ╚═══════════════════════════════════════════════════════╝
30
- `);
31
-
32
- if (fs.existsSync(projectPath)) {
33
- console.error(`❌ Directory "${projectName}" already exists.`);
34
- console.error(` Please choose a different name or remove the existing directory.\n`);
35
- process.exit(1);
36
- }
37
-
49
+ if (fs.existsSync(projectPath)) { console.error(`❌ Directory "${projectName}" already exists.`); process.exit(1); }
38
50
  try {
39
51
  const { execSync } = await import('child_process');
40
-
41
- console.log(`📁 Creating directory: ${projectName}`);
42
52
  fs.mkdirSync(projectPath, { recursive: true });
43
53
  process.chdir(projectPath);
44
-
45
- console.log(`📦 Initializing package.json...`);
46
- const packageJson = {
47
- name: projectName,
48
- version: '0.1.0',
49
- type: 'module',
50
- scripts: {
51
- dev: 'jux serve',
52
- build: 'jux build'
53
- },
54
- dependencies: {
55
- juxscript: 'latest'
56
- }
57
- };
58
- fs.writeFileSync('package.json', JSON.stringify(packageJson, null, 2));
59
- console.log(` ✓ package.json created`);
60
-
61
- console.log(`\n📥 Installing juxscript...\n`);
62
- try {
63
- execSync('npm install', { stdio: 'inherit' });
64
- console.log(`\n ✓ Dependencies installed`);
65
- } catch (err) {
66
- console.error(`\n ⚠️ npm install failed, but continuing...`);
67
- }
68
-
69
- execSync('npx jux init', { stdio: 'inherit' });
70
-
71
- console.log(`\n📝 Creating .gitignore...`);
72
- fs.writeFileSync('.gitignore', `.jux-dist/\nnode_modules/\n.DS_Store\n.env\n*.log\n`);
73
- console.log(` ✓ .gitignore created`);
74
-
75
- console.log(`
76
- ╔═══════════════════════════════════════════════════════╗
77
- ║ ║
78
- ║ ✅ Project created successfully! ║
79
- ║ ║
80
- ╚═══════════════════════════════════════════════════════╝
81
-
82
- 📚 Resources:
83
- Documentation: [coming soon]
84
- GitHub: https://github.com/juxscript/jux
85
- Examples: https://github.com/juxscript/examples
86
-
87
- ⭐ If you find JUX useful, please star us on GitHub!
88
- 🔒 Security: Report issues to security@juxscript.com [placeholder]
89
-
90
- Say goodbye to markup </</>>>.
91
- Happy javascripting your frontend! 🎉
92
-
93
- Next steps:
94
- cd ${projectName}
95
- npm run dev
96
- `);
97
-
98
- } catch (err) {
99
- console.error(`\n❌ Project creation failed:`, err.message);
100
-
101
- if (fs.existsSync(projectPath)) {
102
- try {
103
- process.chdir('..');
104
- fs.rmSync(projectPath, { recursive: true, force: true });
105
- console.log(` ✓ Cleaned up failed project directory\n`);
106
- } catch (cleanupErr) {
107
- console.error(` ⚠️ Could not clean up directory\n`);
108
- }
109
- }
110
-
111
- process.exit(1);
112
- }
113
-
114
- process.exit(0); // ✅ Exit after create completes
54
+ const pkg = { name: projectName, version: '0.1.0', type: 'module', scripts: { dev: 'jux serve', build: 'jux build' }, dependencies: { juxscript: 'latest' } };
55
+ fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2));
56
+ const packageRoot = path.resolve(__dirname, '..');
57
+ scaffoldProject(projectPath, packageRoot);
58
+ try { execSync('npm install', { stdio: 'inherit' }); } catch (err) { }
59
+ console.log(`\n✅ Project created! Run: cd ${projectName} && npm run dev\n`);
60
+ } catch (err) { console.error(`\n❌ Failed:`, err.message); process.exit(1); }
61
+ process.exit(0);
115
62
  }
116
63
 
117
- // ═══════════════════════════════════════════════════════════════════
118
- // ALL OTHER COMMANDS - Require dependencies to be installed
119
- // ═══════════════════════════════════════════════════════════════════
120
-
121
- import {
122
- copyLibToOutput,
123
- copyProjectAssets,
124
- transpileProjectTypeScript,
125
- copyPresetsToOutput,
126
- bundleJuxFilesToRouter,
127
- generateIndexHtml
128
- } from '../machinery/compiler.js';
129
- import { verifyStaticBuild } from '../machinery/verifier.js'; // ✅ Import Verifier
130
- import { resolveConfig } from '../machinery/config.js'; // ✅ Import Config Resolver
64
+ import { copyLibToOutput, copyProjectAssets, transpileProjectTypeScript, copyPresetsToOutput, bundleJuxFilesToRouter, generateIndexHtml } from '../machinery/compiler.js';
65
+ import { verifyStaticBuild } from '../machinery/verifier.js';
66
+ import { resolveConfig } from '../machinery/config.js';
131
67
  import { start } from '../machinery/server.js';
132
- // ❌ REMOVED static import of config to prevent crash on incorrect path
133
68
 
134
- /**
135
- * Dynamically load and normalize juxconfig.js from the user's project root
136
- */
137
69
  async function loadUserConfig(projectRoot) {
138
70
  const configPath = path.join(projectRoot, 'juxconfig.js');
139
71
  let rawConfig = {};
140
-
141
- if (fs.existsSync(configPath)) {
142
- try {
143
- const module = await import(configPath);
144
- // Support export default defineConfig({...}) OR export const config = {...}
145
- rawConfig = module.default || module.config || {};
146
- } catch (err) {
147
- console.warn(`⚠️ Error loading juxconfig.js: ${err.message}. Using defaults.`);
148
- }
149
- }
150
-
151
- // ✅ Normalize DX sugar (short paths, missing extensions, aliases) into strict config
72
+ if (fs.existsSync(configPath)) { try { const module = await import(configPath); rawConfig = module.default || module.config || {}; } catch (err) { } }
152
73
  return resolveConfig(rawConfig, projectRoot);
153
74
  }
154
75
 
155
- // Recursively find .jux files
156
- function findJuxFiles(dir, fileList = []) {
157
- if (!fs.existsSync(dir)) return fileList;
158
- const files = fs.readdirSync(dir);
159
- files.forEach(file => {
160
- const filePath = path.join(dir, file);
161
- const stat = fs.statSync(filePath);
162
- if (stat.isDirectory()) {
163
- if (file !== 'node_modules' && file !== 'jux-dist' && file !== '.git' && file !== 'server') {
164
- findJuxFiles(filePath, fileList);
165
- }
166
- } else if (file.endsWith('.jux')) {
167
- fileList.push(filePath);
168
- }
169
- });
170
- return fileList;
171
- }
172
-
173
- // ═══════════════════════════════════════════════════════════════════
174
- // MAIN EXECUTION BLOCK (Async)
175
- // ═══════════════════════════════════════════════════════════════════
176
76
  (async () => {
177
- // 1. Resolve Configuration relative to CWD
178
77
  const projectRoot = process.cwd();
179
78
  const config = await loadUserConfig(projectRoot);
180
-
181
- // 2. Define PATHS Contract
182
79
  const PATHS = {
183
- packageRoot: path.resolve(__dirname, '..'), // node_modules/juxscript
80
+ packageRoot: path.resolve(__dirname, '..'),
184
81
  projectRoot: projectRoot,
185
-
186
- // Use config values
187
82
  get juxSource() { return path.join(this.projectRoot, config.directories?.source || 'jux'); },
188
83
  get juxLib() { return path.resolve(this.packageRoot, 'lib'); },
189
84
  get frontendDist() { return path.join(this.projectRoot, config.directories?.distribution || '.jux-dist'); }
190
85
  };
191
86
 
192
- console.log('📍 JUX Paths:');
193
- console.log(` Package: ${PATHS.packageRoot}`);
194
- console.log(` Project: ${PATHS.projectRoot}`);
195
- console.log(` Source: ${PATHS.juxSource}`);
196
- console.log(` Output: ${PATHS.frontendDist}`);
197
- console.log(` Lib: ${PATHS.juxLib}\n`);
198
-
199
- /**
200
- * Build the entire JUX project
201
- */
202
87
  async function buildProject(isServe = false, wsPort = 3001) {
203
- const buildStartTime = performance.now();
204
- console.log('++ Building JUX frontend...\n');
88
+ if (!fs.existsSync(PATHS.juxSource)) { console.error(`❌ Source not found: ${PATHS.juxSource}`); process.exit(1); }
89
+ if (fs.existsSync(PATHS.frontendDist)) fs.rmSync(PATHS.frontendDist, { recursive: true, force: true });
90
+ fs.mkdirSync(PATHS.frontendDist, { recursive: true });
205
91
 
206
- try {
207
- // Verify jux source directory exists
208
- if (!fs.existsSync(PATHS.juxSource)) {
209
- console.error(`❌ Source directory not found: ${PATHS.juxSource}`);
210
- console.error(` Please create a directory named '${config.directories?.source || 'jux'}'`);
211
- process.exit(1);
212
- }
213
-
214
- // Clean and create frontend dist
215
- if (fs.existsSync(PATHS.frontendDist)) {
216
- fs.rmSync(PATHS.frontendDist, { recursive: true, force: true });
217
- }
218
- fs.mkdirSync(PATHS.frontendDist, { recursive: true });
92
+ // ✅ Clean Log
93
+ process.stdout.write('🔨 Building JUX... ');
219
94
 
220
- // Copy dependencies
221
- const libStartTime = performance.now();
95
+ try {
222
96
  await copyLibToOutput(PATHS.juxLib, PATHS.frontendDist);
223
- const libTime = performance.now() - libStartTime;
224
- console.log(`⏱️ Lib copy time: ${libTime.toFixed(0)}ms\n`);
225
-
226
- const presetsStartTime = performance.now();
227
97
  await copyPresetsToOutput(PATHS.packageRoot, PATHS.frontendDist);
228
- const presetsTime = performance.now() - presetsStartTime;
229
- console.log(`⏱️ Presets copy time: ${presetsTime.toFixed(0)}ms\n`);
230
-
231
- const assetsStartTime = performance.now();
232
98
  await copyProjectAssets(PATHS.juxSource, PATHS.frontendDist);
233
- const assetsTime = performance.now() - assetsStartTime;
234
- console.log(`⏱️ Assets copy time: ${assetsTime.toFixed(0)}ms\n`);
235
-
236
- const tsStartTime = performance.now();
237
99
  await transpileProjectTypeScript(PATHS.juxSource, PATHS.frontendDist);
238
- const tsTime = performance.now() - tsStartTime;
239
- console.log(`⏱️ TypeScript transpile time: ${tsTime.toFixed(0)}ms\n`);
240
-
241
- // Bundle Routers
242
- const projectJuxFiles = findJuxFiles(PATHS.juxSource);
243
- console.log(`📝 Found ${projectJuxFiles.length} .jux file(s)\n`);
244
-
245
- if (projectJuxFiles.length === 0) {
246
- console.warn('⚠️ No .jux files found to bundle');
247
- process.exit(1);
248
- }
249
-
250
- const bundleStartTime = performance.now();
251
- const bundleResult = await bundleJuxFilesToRouter(PATHS.juxSource, PATHS.frontendDist, {
252
- routePrefix: '',
253
- config // Pass config to bundler
254
- });
255
- const bundleTime = performance.now() - bundleStartTime;
256
-
257
- const indexStartTime = performance.now();
258
- generateIndexHtml(PATHS.frontendDist, bundleResult, {
259
- isDev: isServe,
260
- wsPort: isServe ? wsPort : undefined
261
- });
262
- const indexTime = performance.now() - indexStartTime;
263
-
264
- // ✅ NEW: Post-Compilation Verification
265
- if (!verifyStaticBuild(PATHS.frontendDist)) {
266
- console.error('🛑 Critical Build Failure. Aborting.');
267
- process.exit(1);
268
- }
269
-
270
- const totalBuildTime = performance.now() - buildStartTime;
271
- console.log(`\n✅ Bundled → ${PATHS.frontendDist}/${bundleResult.mainJsFilename}\n`);
272
-
273
- // Build Summary Log
274
- console.log(`📊 Build Summary:`);
275
- console.log(` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
276
- console.log(` Library copy: ${libTime.toFixed(0)}ms`);
277
- console.log(` Router bundle: ${bundleTime.toFixed(0)}ms`);
278
- console.log(` Total build time: ${totalBuildTime.toFixed(0)}ms\n`);
279
-
280
- if (!isServe) {
281
- // ...existing code...
282
- }
100
+ const bundleResult = await bundleJuxFilesToRouter(PATHS.juxSource, PATHS.frontendDist, { routePrefix: '', config });
101
+ generateIndexHtml(PATHS.frontendDist, bundleResult, { isDev: isServe, wsPort: isServe ? wsPort : undefined });
102
+ if (!verifyStaticBuild(PATHS.frontendDist)) { console.error('🛑 Critical Build Failure.'); process.exit(1); }
283
103
 
104
+ const fileCount = fs.readdirSync(PATHS.juxSource).filter(f => f.endsWith('.jux')).length; // Approximate check
105
+ console.log(`✅ Done. (${fileCount} files, ${(fs.statSync(path.join(PATHS.frontendDist, 'main.js')).size / 1024).toFixed(1)}KB)`);
284
106
  } catch (err) {
285
- console.error(`❌ Build error:`, err.message);
286
- console.error(err.stack);
107
+ console.log('❌ Failed.');
108
+ console.error(err.message);
287
109
  process.exit(1);
288
110
  }
289
111
  }
290
112
 
291
- // ═══════════════════════════════════════════════════════════════
292
- // COMMAND DISPATCHER
293
- // ═══════════════════════════════════════════════════════════════
294
-
295
- if (command === 'init') {
296
- console.log('🎨 Initializing JUX project...\n');
297
- const juxDir = PATHS.juxSource;
298
- if (fs.existsSync(juxDir)) {
299
- console.error('❌ Source directory already exists');
300
- process.exit(1);
301
- }
302
-
303
- fs.mkdirSync(juxDir, { recursive: true }); // create it.
304
-
305
- let filesCopied = 0;
306
- // Copy default presets
307
- const defaultPresetSrc = path.join(PATHS.packageRoot, 'create');
308
- if (fs.existsSync(defaultPresetSrc)) {
309
- const entries = fs.readdirSync(defaultPresetSrc, { withFileTypes: true });
310
- for (const entry of entries) {
311
- if (entry.isFile()) {
312
- fs.copyFileSync(path.join(defaultPresetSrc, entry.name), path.join(juxDir, entry.name));
313
- filesCopied++;
314
- // console.log(`+ Created ${entry.name}`);
315
- }
316
- }
317
- }
318
-
319
- // ✅ ADD: Safe fallback if presets are missing so build doesn't fail
320
- if (filesCopied === 0) {
321
- console.warn('⚠️ Install pages not available. Creating default index.jux...');
322
- const indexContent = `import { jux } from 'juxscript';
323
-
324
- jux.hero('welcome', {
325
- title: 'Welcome to JUX',
326
- subtitle: 'Start building in jux/index.jux'
327
- }).render('#app');
328
- `;
329
- fs.writeFileSync(path.join(juxDir, 'index.jux'), indexContent);
330
- console.log('+ Created index.jux');
331
- }
332
-
333
- // Create juxconfig.js
334
- const configExampleSrc = path.join(PATHS.packageRoot, 'juxconfig.example.js');
335
- const configDest = path.join(PATHS.projectRoot, 'juxconfig.js');
336
- if (fs.existsSync(configExampleSrc) && !fs.existsSync(configDest)) {
337
- fs.copyFileSync(configExampleSrc, configDest);
338
- console.log('+ Created juxconfig.js');
339
- }
340
-
341
- console.log('\n✅ JUX project initialized!');
342
-
113
+ if (command === 'init' || command === 'install') {
114
+ scaffoldProject(PATHS.projectRoot, PATHS.packageRoot);
115
+ console.log('✅ Initialized. Next: npm run dev');
343
116
  } else if (command === 'build') {
344
117
  await buildProject(false);
345
- console.log(`✅ Build complete: ${PATHS.frontendDist}`);
346
-
347
118
  } else if (command === 'serve') {
348
119
  const httpPort = parseInt(process.argv[3]) || config.defaults?.httpPort || 3000;
349
120
  const wsPort = parseInt(process.argv[4]) || config.defaults?.wsPort || 3001;
350
-
351
121
  await buildProject(true, wsPort);
352
- await start(httpPort, wsPort, PATHS.frontendDist, config); // Pass config to server
353
-
122
+ await start(httpPort, wsPort, PATHS.frontendDist, config);
354
123
  } else {
355
- // ...existing code...
356
- // (Help text)
357
- console.log(`
358
- JUX CLI - A JavaScript UX authorship platform
359
- Usage:
360
- npx jux create [name]
361
- npx jux init
362
- npx jux build
363
- npx jux serve
364
- `);
124
+ console.log(`Usage: npx jux [create|init|build|serve]`);
365
125
  }
366
-
367
126
  })();
package/create/index.jux CHANGED
@@ -1,90 +1,79 @@
1
- import { jux, state } from 'juxscript';
2
- import { initializeGrid } from 'layout.jux';
3
-
4
- // Initialize the grid layout - this executes the rendering
5
- const grid = initializeGrid();
6
-
7
- // ═══════════════════════════════════════════════════════════════════
8
- // HEADER CONTENT
9
- // ═══════════════════════════════════════════════════════════════════
10
-
11
- jux.heading('logo-text')
12
- .level(1)
13
- .text('JUX')
14
- .render('#appheader-logo');
15
-
16
- // ═══════════════════════════════════════════════════════════════════
17
- // MAIN CONTENT
18
- // ═══════════════════════════════════════════════════════════════════
19
-
20
- jux.hero('welcome', {
21
- title: 'Welcome to JUX',
22
- subtitle: 'A JavaScript UX authorship platform'
23
- }).render('#appmain-content');
24
-
25
- jux.divider().render('#appmain-content');
26
-
27
- jux.heading('start-heading')
28
- .level(2)
29
- .text('Getting Started')
30
- .render('#appmain-content');
31
-
32
- jux.paragraph('start-intro')
33
- .text('Edit this file to build your app. Here are some quick tips:')
34
- .style('margin: 1rem 0;')
35
- .render('#appmain-content');
36
-
37
- jux.list('quick-tips', {
38
- items: [
39
- 'Run npx jux serve for dev mode with hot reload',
40
- 'Run npx jux build to compile for production',
41
- 'Serve .jux-dist/ from your backend',
42
- 'Check out the docs at juxscript.com/docs'
43
- ]
44
- }).render('#appmain-content');
45
-
46
- jux.divider().render('#appmain-content');
47
-
48
- jux.heading('example-heading')
49
- .level(3)
50
- .text('Quick Example')
51
- .render('#appmain-content');
52
-
53
- jux.code('example-code')
54
- .language('javascript')
55
- .code(`import { jux, state } from 'juxscript';
56
-
57
- const count = state(0);
58
-
59
- jux.button('increment')
60
- .label('Click me!')
61
- .bind('click', () => count.set(count.value + 1))
62
- .render('#app');
63
-
64
- jux.paragraph('counter')
65
- .sync('text', count, val => \`Count: \${val}\`)
66
- .render('#app');`)
67
- .render('#appmain-content');
68
-
69
- // Create a reactive counter demo
70
- const count = state(0);
71
-
72
- jux.button('increment')
73
- .label('Click me!')
74
- .bind('click', () => count.set(count.value + 1))
75
- .style('margin: 1rem 0;')
76
- .render('#appmain-content');
77
-
78
- jux.paragraph('counter')
79
- .sync('text', count, val => `Count: ${val}`)
80
- .style('font-size: 1.25rem; font-weight: 600; color: var(--color-brand);')
81
- .render('#appmain-content');
82
-
83
- // ═══════════════════════════════════════════════════════════════════
84
- // FOOTER CONTENT
85
- // ═══════════════════════════════════════════════════════════════════
86
-
87
- jux.paragraph('footer-text')
88
- .text('© 2026 JUX Authoring Framework')
89
- .style('color: var(--color-text-tertiary);')
90
- .render('#appfooter-content');
1
+ import { Element, List } from 'juxscript';
2
+ import { LandingLayout } from './layout.jux';
3
+
4
+ // 1. Initialize Layout
5
+ LandingLayout();
6
+
7
+ // 2. Header: Logo & Nav
8
+ Element('logo', { tag: 'h2' })
9
+ .text('JUX STUDIO')
10
+ .style('margin: 0; padding: 25px 40px; font-weight: 800; letter-spacing: -1px; display: flex; align-items: center; height: 100%; box-sizing: border-box;')
11
+ .render('landing-layout-0-0');
12
+
13
+ // 3. Hero Section
14
+ const hero = Element('hero-content', { tag: 'div' })
15
+ .style('display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100%; text-align: center; background: #f8f9fa;')
16
+ .render('landing-layout-1-0');
17
+
18
+ Element('hero-h1', { tag: 'h1' })
19
+ .text('Build Web Apps. Simply.')
20
+ .style('font-size: 4rem; margin: 0 0 20px 0; font-weight: 900; background: linear-gradient(45deg, #111, #555); -webkit-background-clip: text; -webkit-text-fill-color: transparent;')
21
+ .render('hero-content');
22
+
23
+ Element('hero-p', { tag: 'p' })
24
+ .text('No complex build steps. No magic. Just standard JavaScript components.')
25
+ .style('font-size: 1.5rem; color: #666; max-width: 600px; line-height: 1.5;')
26
+ .render('hero-content');
27
+
28
+ Element('cta-btn', { tag: 'button' })
29
+ .text('Get Started')
30
+ .style('margin-top: 30px; padding: 15px 40px; font-size: 1.2rem; background: #000; color: #fff; border: none; border-radius: 50px; cursor: pointer; transition: transform 0.2s;')
31
+ .render('hero-content');
32
+
33
+ // 4. Features Section (Nested Content)
34
+ const featuresWrapper = Element('features-wrapper', { tag: 'div' })
35
+ .style('max-width: 1000px; margin: 60px auto; display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 40px; padding: 0 20px;')
36
+ .render('landing-layout-2-0');
37
+
38
+ // Component Showcase within Landing Page
39
+ const listDemo = Element('list-demo-card', { tag: 'div' })
40
+ .style('background: #fff; padding: 30px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.05); border: 1px solid #eee;')
41
+ .render('features-wrapper');
42
+
43
+ Element('list-label', { tag: 'h3' }).text('Interactive Components').render('list-demo-card');
44
+ Element('list-desc', { tag: 'p' }).text('Try dragging or sorting this list:').style('color:#666; margin-bottom:20px;').render('list-demo-card');
45
+
46
+ List('feature-list', { items: ['Zero Config', 'Standard Web APIs', 'Hot Reloading', 'TypeScript Ready'] })
47
+ .listType('unordered')
48
+ .enableSort()
49
+ .enableMove()
50
+ .render('list-demo-card');
51
+
52
+ // Feature Cards
53
+ const cardData = [
54
+ { title: 'Reactive State', desc: 'Granular binding means high performance without a virtual DOM.' },
55
+ { title: 'Time Travel', desc: 'Built-in undo/redo capabilities for complex application state.' }
56
+ ];
57
+
58
+ cardData.forEach((f, i) => {
59
+ const card = Element(`card-${i}`, { tag: 'div' })
60
+ .style('background: #fff; padding: 30px; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.05); border: 1px solid #eee;')
61
+ .render('features-wrapper');
62
+
63
+ Element(`ct-${i}`, { tag: 'h3' }).text(f.title).render(`card-${i}`);
64
+ Element(`cd-${i}`, { tag: 'p' }).text(f.desc).style('color: #666; line-height: 1.6;').render(`card-${i}`);
65
+ });
66
+
67
+ // 5. Footer
68
+ const footer = Element('footer-content', { tag: 'div' })
69
+ .html('&copy; 2025 Jux Project. Open Source.')
70
+ .style('display:flex; align-items:center; justify-content:center; height:100%; background:#111; color:#666;')
71
+ .render('landing-layout-3-0');
72
+
73
+ // 6. Global Reset (injected via Element)
74
+ Element('reset-styles', { tag: 'style' })
75
+ .text(`
76
+ body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background: #fff; color: #111; }
77
+ button:hover { transform: scale(1.05); }
78
+ `)
79
+ .render('app');