juxscript 1.0.24 → 1.0.26

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
@@ -247,18 +247,17 @@ async function buildProject(isServe = false) {
247
247
  console.log('+ Created jux/index.jux');
248
248
  }
249
249
 
250
- // Copy preset styles to jux/styles/
251
- const presetsSrc = path.join(PATHS.packageRoot, 'presets');
250
+ // Copy preset styles from presets/styles/ to jux/styles/
251
+ const presetsStylesSrc = path.join(PATHS.packageRoot, 'presets', 'styles');
252
252
  const stylesDest = path.join(juxDir, 'styles');
253
253
 
254
- if (fs.existsSync(presetsSrc)) {
254
+ if (fs.existsSync(presetsStylesSrc)) {
255
255
  fs.mkdirSync(stylesDest, { recursive: true });
256
256
 
257
- const presetFiles = fs.readdirSync(presetsSrc);
258
- presetFiles.forEach(file => {
259
- // Only copy CSS files, skip hey.jux
257
+ const styleFiles = fs.readdirSync(presetsStylesSrc);
258
+ styleFiles.forEach(file => {
260
259
  if (file.endsWith('.css')) {
261
- const srcFile = path.join(presetsSrc, file);
260
+ const srcFile = path.join(presetsStylesSrc, file);
262
261
  const destFile = path.join(stylesDest, file);
263
262
  fs.copyFileSync(srcFile, destFile);
264
263
  console.log(`+ Copied preset style: styles/${file}`);
@@ -312,17 +311,24 @@ node_modules/
312
311
  // ✅ Always serves router bundle
313
312
  await buildProject(true);
314
313
 
315
- const port = parseInt(process.argv[3]) || 3000;
316
- await start(port);
314
+ // Parse port arguments: npx jux serve [httpPort] [wsPort]
315
+ const httpPort = parseInt(process.argv[3]) || 3000;
316
+ const wsPort = parseInt(process.argv[4]) || 3001;
317
+
318
+ await start(httpPort, wsPort);
317
319
 
318
320
  } else {
319
321
  console.log(`
320
322
  JUX CLI - A JavaScript UX authorship platform
321
323
 
322
324
  Usage:
323
- npx jux init Initialize a new JUX project
324
- npx jux build Build router bundle to ./jux-dist/
325
- npx jux serve [port] Start dev server with hot reload (default: 3000)
325
+ npx jux init Initialize a new JUX project
326
+ npx jux build Build router bundle to ./jux-dist/
327
+ npx jux serve [http] [ws] Start dev server with hot reload
328
+
329
+ Arguments:
330
+ [http] HTTP server port (default: 3000)
331
+ [ws] WebSocket port (default: 3001)
326
332
 
327
333
  Project Structure:
328
334
  my-project/
@@ -345,9 +351,10 @@ Getting Started:
345
351
  4. Serve jux-dist/ from your backend
346
352
 
347
353
  Examples:
348
- npx jux build Build production bundle
349
- npx jux serve Start dev server on port 3000
350
- npx jux serve 8080 Start dev server on port 8080
354
+ npx jux build # Build production bundle
355
+ npx jux serve # Dev server (ports 3000/3001)
356
+ npx jux serve 8080 # HTTP on 8080, WS on 3001
357
+ npx jux serve 8080 8081 # HTTP on 8080, WS on 8081
351
358
  `);
352
359
  }
353
360
  })();
@@ -2059,5 +2059,5 @@
2059
2059
  }
2060
2060
  ],
2061
2061
  "version": "1.0.0",
2062
- "lastUpdated": "2026-01-28T04:47:53.565Z"
2062
+ "lastUpdated": "2026-01-28T14:47:56.941Z"
2063
2063
  }
@@ -580,8 +580,23 @@ ${routeTable}
580
580
  const app = document.getElementById('app');
581
581
 
582
582
  function render() {
583
- const path = location.pathname;
584
- const view = routes[path] || JuxNotFound;
583
+ let path = location.pathname;
584
+
585
+ // Try exact match first
586
+ let view = routes[path];
587
+
588
+ // If no match and path ends with /, try appending 'index'
589
+ if (!view && path.endsWith('/')) {
590
+ view = routes[path + 'index'] || routes[path.slice(0, -1) + '/index'];
591
+ }
592
+
593
+ // If still no match and path doesn't end with /, try appending '/index'
594
+ if (!view && !path.endsWith('/')) {
595
+ view = routes[path + '/index'];
596
+ }
597
+
598
+ // Fall back to 404
599
+ view = view || JuxNotFound;
585
600
 
586
601
  app.innerHTML = '';
587
602
  app.removeAttribute('data-jux-page');
@@ -638,10 +653,6 @@ export function generateIndexHtml(distDir, routes, mainJsFilename = 'main.js') {
638
653
  <title>Jux Application</title>
639
654
  </head>
640
655
  <body data-theme="">
641
- <!-- Navigation -->
642
- <nav style="padding: 20px; background: #f5f5f5; border-bottom: 2px solid #ddd;">
643
- ${navLinks}
644
- </nav>
645
656
  <!-- App container - router renders here -->
646
657
  <div id="app"></div>
647
658
  ${importMapScript}
@@ -9,7 +9,45 @@ import { WebSocketServer } from 'ws';
9
9
  const __filename = fileURLToPath(import.meta.url);
10
10
  const __dirname = path.dirname(__filename);
11
11
 
12
- async function serve(port = 3000, distDir = './jux-dist') {
12
+ /**
13
+ * Try to start a server on a port, with fallback to next port if busy
14
+ * Returns the actual port that was successfully allocated
15
+ */
16
+ async function tryPort(startPort, maxAttempts = 5, reservedPorts = []) {
17
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
18
+ const port = startPort + attempt;
19
+
20
+ // Skip if this port is already reserved
21
+ if (reservedPorts.includes(port)) {
22
+ continue;
23
+ }
24
+
25
+ try {
26
+ // Test if port is available
27
+ await new Promise((resolve, reject) => {
28
+ const testServer = http.createServer();
29
+ testServer.once('error', reject);
30
+ testServer.once('listening', () => {
31
+ testServer.close();
32
+ resolve(port);
33
+ });
34
+ testServer.listen(port);
35
+ });
36
+ return port;
37
+ } catch (err) {
38
+ if (err.code === 'EADDRINUSE') {
39
+ if (attempt < maxAttempts - 1) {
40
+ console.log(` Port ${port} in use, trying ${port + 1}...`);
41
+ }
42
+ continue;
43
+ }
44
+ throw err;
45
+ }
46
+ }
47
+ throw new Error(`Could not find available port after ${maxAttempts} attempts starting from ${startPort}`);
48
+ }
49
+
50
+ async function serve(httpPort = 3000, wsPort = 3001, distDir = './jux-dist') {
13
51
  const app = express();
14
52
  const absoluteDistDir = path.resolve(distDir);
15
53
  const projectRoot = path.resolve('.');
@@ -22,50 +60,46 @@ async function serve(port = 3000, distDir = './jux-dist') {
22
60
  process.exit(1);
23
61
  }
24
62
 
25
- // Strong cache prevention for main.js
63
+ // Strong cache prevention
26
64
  app.use((req, res, next) => {
27
65
  res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
28
66
  res.setHeader('Pragma', 'no-cache');
29
67
  res.setHeader('Expires', '0');
30
-
31
- // ✅ Add ETag prevention
32
68
  res.setHeader('ETag', 'W/"' + Date.now() + '"');
33
-
34
69
  next();
35
70
  });
36
71
 
37
- // Disable caching in dev mode
38
- app.use((req, res, next) => {
39
- res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
40
- res.setHeader('Pragma', 'no-cache');
41
- res.setHeader('Expires', '0');
42
- next();
43
- });
44
-
45
- // Serve static files (CSS, JS, images, etc.)
72
+ // Serve static files
46
73
  app.use(express.static(absoluteDistDir));
47
74
 
48
- // SPA fallback: only for requests that accept HTML (not static files)
75
+ // SPA fallback
49
76
  app.get('*', (req, res) => {
50
- // If request accepts HTML, serve index.html
51
77
  if (req.accepts('html')) {
52
78
  const indexPath = path.join(absoluteDistDir, 'index.html');
53
79
  if (fs.existsSync(indexPath)) {
54
80
  res.sendFile(indexPath);
55
81
  } else {
56
- res.status(404).send('index.html not found. Run `npx jux bundle` first.');
82
+ res.status(404).send('index.html not found. Run `npx jux build` first.');
57
83
  }
58
84
  } else {
59
- // For non-HTML requests (like .js, .css), return 404
60
85
  res.status(404).send('Not found');
61
86
  }
62
87
  });
63
88
 
89
+ // Find available ports sequentially to avoid collision
90
+ console.log('🔍 Finding available ports...');
91
+
92
+ // First, find HTTP port
93
+ const availableHttpPort = await tryPort(httpPort);
94
+
95
+ // Then find WS port, excluding the HTTP port we just allocated
96
+ const availableWsPort = await tryPort(wsPort, 5, [availableHttpPort]);
97
+
64
98
  // Create HTTP server
65
99
  const server = http.createServer(app);
66
100
 
67
101
  // WebSocket server for hot reload
68
- const wss = new WebSocketServer({ port: 3001 });
102
+ const wss = new WebSocketServer({ port: availableWsPort });
69
103
  const clients = [];
70
104
 
71
105
  wss.on('connection', (ws) => {
@@ -83,11 +117,11 @@ async function serve(port = 3000, distDir = './jux-dist') {
83
117
  });
84
118
  });
85
119
 
86
- console.log('🔌 WebSocket server running at ws://localhost:3001');
120
+ console.log(`🔌 WebSocket server running at ws://localhost:${availableWsPort}`);
87
121
 
88
122
  // Start HTTP server
89
- server.listen(port, () => {
90
- console.log(`🚀 JUX dev server running at http://localhost:${port}`);
123
+ server.listen(availableHttpPort, () => {
124
+ console.log(`🚀 JUX dev server running at http://localhost:${availableHttpPort}`);
91
125
  console.log(` Serving: ${absoluteDistDir}`);
92
126
  console.log(` Press Ctrl+C to stop\n`);
93
127
  });
@@ -110,9 +144,9 @@ async function serve(port = 3000, distDir = './jux-dist') {
110
144
  process.on('SIGINT', shutdown);
111
145
  process.on('SIGTERM', shutdown);
112
146
 
113
- return { server };
147
+ return { server, httpPort: availableHttpPort, wsPort: availableWsPort };
114
148
  }
115
149
 
116
- export async function start(port = 3000) {
117
- return serve(port, './jux-dist');
150
+ export async function start(httpPort = 3000, wsPort = 3001) {
151
+ return serve(httpPort, wsPort, './jux-dist');
118
152
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.0.24",
3
+ "version": "1.0.26",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "lib/jux.js",
@@ -0,0 +1,55 @@
1
+ import { jux } from 'juxscript';
2
+
3
+ // Import the layout styles
4
+ jux.include('styles/layout.css');
5
+
6
+ // ═══════════════════════════════════════════════════════════════════
7
+ // GRID LAYOUT - INITIALIZATION FUNCTION
8
+ // ═══════════════════════════════════════════════════════════════════
9
+ // Note: #app is automatically created by the Jux compiler
10
+
11
+ export function initializeGrid() {
12
+ // Header area
13
+ const appHeader = jux.container('appheader').render('#app');
14
+ const appHeaderContent = jux.container('appheader-content').render('#appheader');
15
+ const appHeaderLogo = jux.container('appheader-logo').render('#appheader-content');
16
+ const appHeaderNav = jux.container('appheader-nav').render('#appheader-content');
17
+ const appHeaderActions = jux.container('appheader-actions').render('#appheader-content');
18
+
19
+ // Left sidebar
20
+ const appAside = jux.container('appaside').render('#app');
21
+
22
+ // Main content area
23
+ const appMain = jux.container('appmain').render('#app');
24
+ const appMainContent = jux.container('appmain-content').render('#appmain');
25
+
26
+ // Right sidebar (optional - starts hidden)
27
+ const appSidebar = jux.container('appsidebar').render('#app');
28
+ const appSidebarHeader = jux.container('appsidebar-header').render('#appsidebar');
29
+ const appSidebarContent = jux.container('appsidebar-content').render('#appsidebar');
30
+ const appSidebarFooter = jux.container('appsidebar-footer').render('#appsidebar');
31
+
32
+ // Footer area
33
+ const appFooter = jux.container('appfooter').render('#app');
34
+ const appFooterContent = jux.container('appfooter-content').render('#appfooter');
35
+ const appFooterLegal = jux.container('appfooter-legal').render('#appfooter-content');
36
+
37
+ // Return references to all containers
38
+ return {
39
+ appHeader,
40
+ appHeaderContent,
41
+ appHeaderLogo,
42
+ appHeaderNav,
43
+ appHeaderActions,
44
+ appAside,
45
+ appMain,
46
+ appMainContent,
47
+ appSidebar,
48
+ appSidebarHeader,
49
+ appSidebarContent,
50
+ appSidebarFooter,
51
+ appFooter,
52
+ appFooterContent,
53
+ appFooterLegal
54
+ };
55
+ }
@@ -0,0 +1,90 @@
1
+ import { jux, state } from 'juxscript';
2
+ import { initializeGrid } from './grid.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.value++)
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.value++)
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 Platform')
89
+ .style('color: var(--color-text-tertiary);')
90
+ .render('#appfooter-content');
@@ -0,0 +1,280 @@
1
+ /* ============================================
2
+ APP CONTAINER - Main Layout Grid
3
+ ============================================ */
4
+ #app {
5
+ display: grid;
6
+ grid-template-columns: auto 1fr auto;
7
+ grid-template-rows: auto 1fr auto;
8
+ grid-template-areas:
9
+ "header header header"
10
+ "sidebar main aside"
11
+ "footer footer footer";
12
+ width: 100vw;
13
+ height: 100vh;
14
+ overflow: hidden;
15
+ }
16
+
17
+ /* ============================================
18
+ HEADER - Top Navigation Bar
19
+ ============================================ */
20
+ #appheader {
21
+ grid-area: header;
22
+ background: var(--color-surface-elevated);
23
+ border-bottom: var(--border-width) solid var(--color-border);
24
+ display: flex;
25
+ align-items: center;
26
+ padding: 0 var(--space-lg);
27
+ height: 48px;
28
+ z-index: 100;
29
+ transition: background-color var(--transition-base), border-color var(--transition-base);
30
+ }
31
+
32
+ #appheader-content {
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: space-between;
36
+ width: 100%;
37
+ gap: var(--space-xl);
38
+ }
39
+
40
+ #appheader-logo {
41
+ display: flex;
42
+ align-items: center;
43
+ gap: var(--space-md);
44
+ font-size: var(--font-size-lg);
45
+ font-weight: var(--font-weight-semibold);
46
+ color: var(--color-text-primary);
47
+ text-decoration: none;
48
+ padding: var(--space-sm) var(--space-md);
49
+ border-radius: var(--radius-md);
50
+ transition: background-color var(--transition-fast);
51
+ }
52
+
53
+ #appheader-logo:hover {
54
+ background: var(--color-surface-hover);
55
+ }
56
+
57
+ #appheader-nav {
58
+ flex: 1;
59
+ display: flex;
60
+ align-items: center;
61
+ gap: var(--space-sm);
62
+ }
63
+
64
+ #appheader-actions {
65
+ display: flex;
66
+ align-items: center;
67
+ gap: var(--space-md);
68
+ }
69
+
70
+ /* ============================================
71
+ LEFT SIDEBAR - Navigation
72
+ ============================================ */
73
+ .jux-sidebar,
74
+ #appaside {
75
+ grid-area: sidebar;
76
+ width: 260px;
77
+ background: var(--color-surface-base);
78
+ border-right: var(--border-width) solid var(--color-border);
79
+ display: flex;
80
+ flex-direction: column;
81
+ overflow: hidden;
82
+ transition: width var(--transition-base);
83
+ position: relative;
84
+ }
85
+
86
+ /* ============================================
87
+ MAIN CONTENT - Center Area
88
+ ============================================ */
89
+ #appmain {
90
+ grid-area: main;
91
+ background: var(--color-background);
92
+ overflow-y: auto;
93
+ overflow-x: hidden;
94
+ display: flex;
95
+ flex-direction: column;
96
+ position: relative;
97
+ }
98
+
99
+ #appmain-content {
100
+ flex: 1;
101
+ padding: var(--space-3xl) var(--space-2xl);
102
+ max-width: 1600px;
103
+ width: 100%;
104
+ margin: 0 auto;
105
+ }
106
+
107
+ /* Custom scrollbar for main content */
108
+ #appmain::-webkit-scrollbar {
109
+ width: 8px;
110
+ }
111
+
112
+ #appmain::-webkit-scrollbar-track {
113
+ background: var(--color-surface-base);
114
+ }
115
+
116
+ #appmain::-webkit-scrollbar-thumb {
117
+ background: var(--color-border);
118
+ border-radius: var(--radius-full);
119
+ }
120
+
121
+ #appmain::-webkit-scrollbar-thumb:hover {
122
+ background: var(--color-border-hover);
123
+ }
124
+
125
+ /* ============================================
126
+ RIGHT SIDEBAR - Optional Panel
127
+ ============================================ */
128
+ #appsidebar {
129
+ grid-area: aside;
130
+ width: 280px;
131
+ background: var(--color-surface-base);
132
+ border-left: var(--border-width) solid var(--color-border);
133
+ display: none;
134
+ flex-direction: column;
135
+ overflow: hidden;
136
+ transition: width var(--transition-base), background-color var(--transition-base);
137
+ }
138
+
139
+ #appsidebar.show {
140
+ display: flex;
141
+ }
142
+
143
+ #appsidebar.collapsed {
144
+ width: 0;
145
+ border: none;
146
+ }
147
+
148
+ #appsidebar-header {
149
+ padding: var(--space-lg);
150
+ border-bottom: var(--border-width) solid var(--color-border);
151
+ font-weight: var(--font-weight-semibold);
152
+ font-size: var(--font-size-sm);
153
+ color: var(--color-text-secondary);
154
+ text-transform: uppercase;
155
+ letter-spacing: 0.05em;
156
+ }
157
+
158
+ #appsidebar-content {
159
+ flex: 1;
160
+ overflow-y: auto;
161
+ overflow-x: hidden;
162
+ padding: var(--space-lg);
163
+ }
164
+
165
+ #appsidebar-content::-webkit-scrollbar {
166
+ width: 6px;
167
+ }
168
+
169
+ #appsidebar-content::-webkit-scrollbar-track {
170
+ background: transparent;
171
+ }
172
+
173
+ #appsidebar-content::-webkit-scrollbar-thumb {
174
+ background: var(--color-border);
175
+ border-radius: var(--radius-full);
176
+ }
177
+
178
+ #appsidebar-content::-webkit-scrollbar-thumb:hover {
179
+ background: var(--color-border-hover);
180
+ }
181
+
182
+ #appsidebar-footer {
183
+ padding: var(--space-lg);
184
+ border-top: var(--border-width) solid var(--color-border);
185
+ font-size: var(--font-size-xs);
186
+ color: var(--color-text-tertiary);
187
+ }
188
+
189
+ /* ============================================
190
+ FOOTER - Bottom Bar
191
+ ============================================ */
192
+ #appfooter {
193
+ grid-area: footer;
194
+ background: var(--color-surface-elevated);
195
+ border-top: var(--border-width) solid var(--color-border);
196
+ padding: var(--space-lg) var(--space-2xl);
197
+ display: flex;
198
+ align-items: center;
199
+ justify-content: space-between;
200
+ font-size: var(--font-size-sm);
201
+ color: var(--color-text-tertiary);
202
+ transition: background-color var(--transition-base), border-color var(--transition-base);
203
+ }
204
+
205
+ #appfooter-content {
206
+ display: flex;
207
+ align-items: center;
208
+ gap: var(--space-xl);
209
+ }
210
+
211
+ #appfooter-legal {
212
+ display: flex;
213
+ align-items: center;
214
+ gap: var(--space-lg);
215
+ }
216
+
217
+ #appfooter a {
218
+ color: var(--color-text-secondary);
219
+ text-decoration: none;
220
+ transition: color var(--transition-fast);
221
+ }
222
+
223
+ #appfooter a:hover {
224
+ color: var(--color-text-primary);
225
+ }
226
+
227
+ /* ============================================
228
+ RESPONSIVE GRID LAYOUTS
229
+ ============================================ */
230
+ @media (max-width: 1024px) {
231
+ #appsidebar {
232
+ display: none;
233
+ }
234
+
235
+ #app {
236
+ grid-template-columns: auto 1fr;
237
+ grid-template-areas:
238
+ "header header"
239
+ "sidebar main"
240
+ "footer footer";
241
+ }
242
+ }
243
+
244
+ @media (max-width: 768px) {
245
+ #app {
246
+ grid-template-columns: 1fr;
247
+ grid-template-areas:
248
+ "header"
249
+ "main"
250
+ "footer";
251
+ }
252
+
253
+ .jux-sidebar,
254
+ #appaside {
255
+ display: none;
256
+ position: fixed;
257
+ left: 0;
258
+ top: 0;
259
+ bottom: 0;
260
+ z-index: 1000;
261
+ width: 260px;
262
+ box-shadow: var(--shadow-xl);
263
+ transform: translateX(-100%);
264
+ transition: transform var(--transition-base);
265
+ }
266
+
267
+ .jux-sidebar.open,
268
+ #appaside.open {
269
+ display: flex;
270
+ transform: translateX(0);
271
+ }
272
+
273
+ #appmain-content {
274
+ padding: var(--space-2xl) var(--space-lg);
275
+ }
276
+
277
+ #appheader {
278
+ padding: 0 var(--space-md) 0 calc(var(--space-md) + 48px);
279
+ }
280
+ }
package/presets/hey.jux DELETED
@@ -1,60 +0,0 @@
1
- import { jux } from 'juxscript';
2
-
3
- // Welcome page with examples
4
- jux.hero('welcome', {
5
- title: 'Welcome to JUX',
6
- subtitle: 'A JavaScript UX authorship platform'
7
- }).render('#app');
8
-
9
- jux.divider().render('#app');
10
-
11
- jux.container('getting-started')
12
- .style('max-width: 800px; margin: 2rem auto; padding: 2rem;')
13
- .render('#app');
14
-
15
- jux.heading('start-heading')
16
- .level(2)
17
- .text('Getting Started')
18
- .render('#getting-started');
19
-
20
- jux.paragraph('start-intro')
21
- .text('Edit this file to build your app. Here are some quick tips:')
22
- .style('margin: 1rem 0;')
23
- .render('#getting-started');
24
-
25
- jux.list('quick-tips', {
26
- items: [
27
- 'Run npx jux serve for dev mode with hot reload',
28
- 'Run npx jux build to compile for production',
29
- 'Serve jux-dist/ from your backend',
30
- 'Check out the docs at juxscript.com/docs'
31
- ]
32
- }).render('#getting-started');
33
-
34
- jux.divider().render('#app');
35
-
36
- jux.container('example-container')
37
- .style('max-width: 800px; margin: 2rem auto; padding: 2rem; background: #f9fafb; border-radius: 8px;')
38
- .render('#app');
39
-
40
- jux.heading('example-heading')
41
- .level(3)
42
- .text('Quick Example')
43
- .render('#example-container');
44
-
45
- jux.code('example-code')
46
- .language('javascript')
47
- .code(`import { jux, state } from 'juxscript';
48
-
49
- // Create a reactive counter
50
- const count = state(0);
51
-
52
- jux.button('increment')
53
- .label('Click me!')
54
- .bind('click', () => count.value++)
55
- .render('#app');
56
-
57
- jux.paragraph('counter')
58
- .sync('text', count, val => \`Count: \${val}\`)
59
- .render('#app');`)
60
- .render('#example-container');