juxscript 1.1.71 → 1.1.73

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.
@@ -0,0 +1,402 @@
1
+ /* ═══════════════════════════════════════════════════════════════════
2
+ * MODAL BASE STYLES
3
+ * ═══════════════════════════════════════════════════════════════════ */
4
+
5
+ /* Modal Overlay - Base positioning */
6
+ .jux-modal-overlay {
7
+ position: fixed;
8
+ inset: 0;
9
+ z-index: 50;
10
+ background-color: rgba(0, 0, 0, 0.8);
11
+ backdrop-filter: blur(2px);
12
+ display: flex;
13
+ align-items: center;
14
+ justify-content: center;
15
+ }
16
+
17
+ /* Modal Container */
18
+ .jux-modal {
19
+ position: relative;
20
+ background-color: #ffffff;
21
+ border: 1px solid #e4e4e7;
22
+ border-radius: 0.5rem;
23
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
24
+ width: 100%;
25
+ max-width: 32rem;
26
+ z-index: 51;
27
+ display: flex;
28
+ flex-direction: column;
29
+ gap: 1rem;
30
+ padding: 1.5rem;
31
+ }
32
+
33
+ .jux-modal-small { max-width: 24rem; }
34
+ .jux-modal-medium { max-width: 32rem; }
35
+ .jux-modal-large { max-width: 48rem; }
36
+
37
+ /* ═══════════════════════════════════════════════════════════════════
38
+ * POSITION VARIANTS
39
+ * ═══════════════════════════════════════════════════════════════════ */
40
+
41
+ /* Center (default) */
42
+ .jux-modal-position-center {
43
+ align-items: center;
44
+ justify-content: center;
45
+ }
46
+
47
+ /* Top position */
48
+ .jux-modal-position-top {
49
+ align-items: flex-start;
50
+ justify-content: center;
51
+ padding-top: 2rem;
52
+ }
53
+
54
+ .jux-modal-position-top .jux-modal {
55
+ animation: modalSlideDown 0.3s ease-out;
56
+ }
57
+
58
+ @keyframes modalSlideDown {
59
+ from {
60
+ transform: translateY(-20px);
61
+ opacity: 0;
62
+ }
63
+ to {
64
+ transform: translateY(0);
65
+ opacity: 1;
66
+ }
67
+ }
68
+
69
+ /* Bottom position (bottom sheet) */
70
+ .jux-modal-position-bottom {
71
+ align-items: flex-end;
72
+ justify-content: center;
73
+ padding: 0;
74
+ }
75
+
76
+ .jux-modal-position-bottom .jux-modal {
77
+ border-radius: 0.75rem 0.75rem 0 0;
78
+ max-height: 90vh;
79
+ overflow-y: auto;
80
+ width: 100%;
81
+ max-width: 100%;
82
+ animation: modalSlideUp 0.3s ease-out;
83
+ }
84
+
85
+ @keyframes modalSlideUp {
86
+ from {
87
+ transform: translateY(100%);
88
+ opacity: 0;
89
+ }
90
+ to {
91
+ transform: translateY(0);
92
+ opacity: 1;
93
+ }
94
+ }
95
+
96
+ /* Fullscreen position */
97
+ .jux-modal-position-fullscreen {
98
+ align-items: stretch;
99
+ justify-content: stretch;
100
+ padding: 0;
101
+ }
102
+
103
+ .jux-modal-position-fullscreen .jux-modal {
104
+ max-width: 100%;
105
+ width: 100%;
106
+ height: 100%;
107
+ border-radius: 0;
108
+ margin: 0;
109
+ max-height: 100vh;
110
+ animation: modalFadeIn 0.2s ease-out;
111
+ }
112
+
113
+ @keyframes modalFadeIn {
114
+ from {
115
+ opacity: 0;
116
+ }
117
+ to {
118
+ opacity: 1;
119
+ }
120
+ }
121
+
122
+ /* ═══════════════════════════════════════════════════════════════════
123
+ * POPOVER VARIANTS (Tooltip-style)
124
+ * ═══════════════════════════════════════════════════════════════════ */
125
+
126
+ .jux-modal-position-popover-top,
127
+ .jux-modal-position-popover-bottom,
128
+ .jux-modal-position-popover-left,
129
+ .jux-modal-position-popover-right {
130
+ background-color: rgba(0, 0, 0, 0.2);
131
+ backdrop-filter: none;
132
+ }
133
+
134
+ .jux-modal-position-popover-top .jux-modal,
135
+ .jux-modal-position-popover-bottom .jux-modal,
136
+ .jux-modal-position-popover-left .jux-modal,
137
+ .jux-modal-position-popover-right .jux-modal {
138
+ max-width: 20rem;
139
+ padding: 1rem;
140
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.2), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
141
+ border-radius: 0.5rem;
142
+ }
143
+
144
+ /* Popover Top */
145
+ .jux-modal-position-popover-top {
146
+ align-items: flex-start;
147
+ justify-content: center;
148
+ padding-top: 1rem;
149
+ }
150
+
151
+ .jux-modal-position-popover-top .jux-modal {
152
+ animation: popoverSlideDown 0.2s ease-out;
153
+ }
154
+
155
+ @keyframes popoverSlideDown {
156
+ from {
157
+ transform: translateY(-10px);
158
+ opacity: 0;
159
+ }
160
+ to {
161
+ transform: translateY(0);
162
+ opacity: 1;
163
+ }
164
+ }
165
+
166
+ /* Popover Bottom */
167
+ .jux-modal-position-popover-bottom {
168
+ align-items: flex-end;
169
+ justify-content: center;
170
+ padding-bottom: 1rem;
171
+ }
172
+
173
+ .jux-modal-position-popover-bottom .jux-modal {
174
+ animation: popoverSlideUp 0.2s ease-out;
175
+ }
176
+
177
+ @keyframes popoverSlideUp {
178
+ from {
179
+ transform: translateY(10px);
180
+ opacity: 0;
181
+ }
182
+ to {
183
+ transform: translateY(0);
184
+ opacity: 1;
185
+ }
186
+ }
187
+
188
+ /* Popover Left */
189
+ .jux-modal-position-popover-left {
190
+ align-items: center;
191
+ justify-content: flex-start;
192
+ padding-left: 1rem;
193
+ }
194
+
195
+ .jux-modal-position-popover-left .jux-modal {
196
+ animation: popoverSlideRight 0.2s ease-out;
197
+ }
198
+
199
+ @keyframes popoverSlideRight {
200
+ from {
201
+ transform: translateX(-10px);
202
+ opacity: 0;
203
+ }
204
+ to {
205
+ transform: translateX(0);
206
+ opacity: 1;
207
+ }
208
+ }
209
+
210
+ /* Popover Right */
211
+ .jux-modal-position-popover-right {
212
+ align-items: center;
213
+ justify-content: flex-end;
214
+ padding-right: 1rem;
215
+ }
216
+
217
+ .jux-modal-position-popover-right .jux-modal {
218
+ animation: popoverSlideLeft 0.2s ease-out;
219
+ }
220
+
221
+ @keyframes popoverSlideLeft {
222
+ from {
223
+ transform: translateX(10px);
224
+ opacity: 0;
225
+ }
226
+ to {
227
+ transform: translateX(0);
228
+ opacity: 1;
229
+ }
230
+ }
231
+
232
+ /* Smaller close button for popovers */
233
+ .jux-modal-position-popover-top .jux-modal-close,
234
+ .jux-modal-position-popover-bottom .jux-modal-close,
235
+ .jux-modal-position-popover-left .jux-modal-close,
236
+ .jux-modal-position-popover-right .jux-modal-close {
237
+ width: 1.25rem;
238
+ height: 1.25rem;
239
+ font-size: 1rem;
240
+ top: 0.5rem;
241
+ right: 0.5rem;
242
+ }
243
+
244
+ /* ═══════════════════════════════════════════════════════════════════
245
+ * MODAL CONTENT SECTIONS
246
+ * ═══════════════════════════════════════════════════════════════════ */
247
+
248
+ /* Header */
249
+ .jux-modal-header {
250
+ display: flex;
251
+ align-items: flex-start;
252
+ gap: 0.75rem;
253
+ padding: 0;
254
+ text-align: left;
255
+ }
256
+
257
+ .jux-modal-header-icon {
258
+ display: flex;
259
+ align-items: center;
260
+ justify-content: center;
261
+ flex-shrink: 0;
262
+ color: #09090b;
263
+ margin-top: 0.125rem;
264
+ }
265
+
266
+ .jux-modal-header-title {
267
+ flex: 1;
268
+ font-size: 1.125rem;
269
+ font-weight: 600;
270
+ line-height: 1.2;
271
+ color: #09090b;
272
+ }
273
+
274
+ /* Body / Content */
275
+ .jux-modal-content {
276
+ padding: 0;
277
+ font-size: 0.875rem;
278
+ color: #71717a;
279
+ line-height: 1.5;
280
+ display: flex;
281
+ flex-direction: column;
282
+ gap: 1rem;
283
+ }
284
+
285
+ /* Footer with Actions */
286
+ .jux-modal-footer {
287
+ padding: 0;
288
+ padding-top: 0.5rem;
289
+ display: flex;
290
+ flex-direction: row;
291
+ justify-content: flex-end;
292
+ gap: 0.5rem;
293
+ flex-wrap: wrap;
294
+ }
295
+
296
+ /* Modal Action Buttons */
297
+ .jux-modal-action {
298
+ display: inline-flex;
299
+ align-items: center;
300
+ justify-content: center;
301
+ white-space: nowrap;
302
+ border-radius: 0.375rem;
303
+ font-size: 0.875rem;
304
+ font-weight: 500;
305
+ height: 2.5rem;
306
+ padding: 0 1rem;
307
+ cursor: pointer;
308
+ transition: all 0.15s ease;
309
+ border: 1px solid transparent;
310
+ }
311
+
312
+ .jux-modal-action-ghost {
313
+ background-color: transparent;
314
+ color: #09090b;
315
+ border: none;
316
+ }
317
+ .jux-modal-action-ghost:hover {
318
+ background-color: #f4f4f5;
319
+ color: #18181b;
320
+ }
321
+
322
+ .jux-modal-action-secondary {
323
+ border: 1px solid #e4e4e7;
324
+ background-color: #ffffff;
325
+ color: #09090b;
326
+ }
327
+ .jux-modal-action-secondary:hover {
328
+ background-color: #f4f4f5;
329
+ color: #18181b;
330
+ }
331
+
332
+ .jux-modal-action-primary {
333
+ background-color: #18181b;
334
+ color: #fafafa;
335
+ border: none;
336
+ }
337
+ .jux-modal-action-primary:hover {
338
+ background-color: #27272a;
339
+ }
340
+
341
+ .jux-modal-action-destructive {
342
+ background-color: #ef4444;
343
+ color: #ffffff;
344
+ border: none;
345
+ }
346
+ .jux-modal-action-destructive:hover {
347
+ background-color: #dc2626;
348
+ }
349
+
350
+ /* Close Button (X) */
351
+ .jux-modal-close {
352
+ position: absolute;
353
+ top: 1rem;
354
+ right: 1rem;
355
+ border-radius: 0.125rem;
356
+ opacity: 0.7;
357
+ transition: opacity 0.15s ease;
358
+ background: transparent;
359
+ border: none;
360
+ cursor: pointer;
361
+ color: #71717a;
362
+ padding: 0.25rem;
363
+ width: 1.5rem;
364
+ height: 1.5rem;
365
+ display: flex;
366
+ align-items: center;
367
+ justify-content: center;
368
+ font-size: 1.25rem;
369
+ line-height: 1;
370
+ }
371
+
372
+ .jux-modal-close:hover {
373
+ opacity: 1;
374
+ color: #09090b;
375
+ }
376
+
377
+ .jux-modal-close:focus-visible {
378
+ outline: 2px solid #18181b;
379
+ outline-offset: 2px;
380
+ }
381
+
382
+ /* ═══════════════════════════════════════════════════════════════════
383
+ * RESPONSIVE ADJUSTMENTS
384
+ * ═══════════════════════════════════════════════════════════════════ */
385
+
386
+ @media (max-width: 640px) {
387
+ .jux-modal-position-top,
388
+ .jux-modal-position-center {
389
+ padding: 1rem;
390
+ }
391
+
392
+ .jux-modal-position-bottom .jux-modal {
393
+ max-height: 95vh;
394
+ }
395
+
396
+ .jux-modal-position-popover-top .jux-modal,
397
+ .jux-modal-position-popover-bottom .jux-modal,
398
+ .jux-modal-position-popover-left .jux-modal,
399
+ .jux-modal-position-popover-right .jux-modal {
400
+ max-width: calc(100vw - 2rem);
401
+ }
402
+ }
@@ -23,7 +23,8 @@ try {
23
23
  // ═══════════════════════════════════════════════════════════════
24
24
  const directories = {
25
25
  source: rawConfig.directories?.source || './jux',
26
- distribution: rawConfig.directories?.distribution || './.jux-dist'
26
+ distribution: rawConfig.directories?.distribution || './.jux-dist',
27
+ public: rawConfig.directories?.public || './public' // ✅ Configurable public folder
27
28
  };
28
29
 
29
30
  const defaults = {
@@ -35,7 +36,8 @@ const defaults = {
35
36
  // Resolve absolute paths
36
37
  const paths = {
37
38
  source: path.resolve(PROJECT_ROOT, directories.source),
38
- distribution: path.resolve(PROJECT_ROOT, directories.distribution)
39
+ distribution: path.resolve(PROJECT_ROOT, directories.distribution),
40
+ public: path.resolve(PROJECT_ROOT, directories.public) // ✅ Use configured path
39
41
  };
40
42
 
41
43
  // ═══════════════════════════════════════════════════════════════
@@ -52,6 +54,13 @@ if (fs.existsSync(paths.source)) {
52
54
  process.exit(1);
53
55
  }
54
56
 
57
+ // ✅ Show public folder status
58
+ if (fs.existsSync(paths.public)) {
59
+ console.log(` ✓ public: ${paths.public}`);
60
+ } else {
61
+ console.log(` ℹ public: ${paths.public} (optional, not found)`);
62
+ }
63
+
55
64
  // ═══════════════════════════════════════════════════════════════
56
65
  // RUN BUILD
57
66
  // ═══════════════════════════════════════════════════════════════
@@ -60,8 +69,9 @@ console.log(`\n`);
60
69
  const compiler = new JuxCompiler({
61
70
  srcDir: paths.source,
62
71
  distDir: paths.distribution,
72
+ publicDir: directories.public, // ✅ Pass configured public directory name
63
73
  defaults,
64
- paths
74
+ paths // ✅ Pass resolved paths object (includes paths.public)
65
75
  });
66
76
 
67
77
  compiler.build()
@@ -13,6 +13,7 @@ export class JuxCompiler {
13
13
  this.config = config;
14
14
  this.srcDir = config.srcDir || './jux';
15
15
  this.distDir = config.distDir || './.jux-dist';
16
+ this.publicDir = config.publicDir || './public'; // ✅ Configurable public path
16
17
  this.defaults = config.defaults || {};
17
18
  this.paths = config.paths || {};
18
19
  this._juxscriptExports = null;
@@ -430,6 +431,9 @@ navigate(location.pathname);
430
431
  }
431
432
  fs.mkdirSync(this.distDir, { recursive: true });
432
433
 
434
+ // ✅ Copy public folder if exists
435
+ this.copyPublicFolder();
436
+
433
437
  const { views, dataModules, sharedModules } = this.scanFiles();
434
438
  console.log(`📁 Found ${views.length} views, ${sharedModules.length} shared, ${dataModules.length} data`);
435
439
 
@@ -497,4 +501,83 @@ navigate(location.pathname);
497
501
  console.log(`\n✅ Build Complete!\n`);
498
502
  return { success: true, errors: [], warnings: validation.warnings };
499
503
  }
504
+
505
+ /**
506
+ * Copy public folder contents to dist
507
+ */
508
+ copyPublicFolder() {
509
+ // ✅ Use configured public path or resolve from paths object
510
+ const publicSrc = this.paths.public
511
+ ? this.paths.public
512
+ : path.resolve(process.cwd(), this.publicDir);
513
+
514
+ if (!fs.existsSync(publicSrc)) {
515
+ return; // No public folder, skip
516
+ }
517
+
518
+ console.log('📦 Copying public assets...');
519
+
520
+ try {
521
+ this._copyDirRecursive(publicSrc, this.distDir, 0);
522
+ console.log('✅ Public assets copied');
523
+ } catch (err) {
524
+ console.warn('⚠️ Error copying public folder:', err.message);
525
+ }
526
+ }
527
+
528
+ /**
529
+ * Recursively copy directory contents
530
+ */
531
+ _copyDirRecursive(src, dest, depth = 0) {
532
+ const entries = fs.readdirSync(src, { withFileTypes: true });
533
+
534
+ entries.forEach(entry => {
535
+ // Skip hidden files and directories
536
+ if (entry.name.startsWith('.')) return;
537
+
538
+ const srcPath = path.join(src, entry.name);
539
+ const destPath = path.join(dest, entry.name);
540
+
541
+ if (entry.isDirectory()) {
542
+ // Create directory and recurse
543
+ if (!fs.existsSync(destPath)) {
544
+ fs.mkdirSync(destPath, { recursive: true });
545
+ }
546
+ this._copyDirRecursive(srcPath, destPath, depth + 1);
547
+ } else {
548
+ // Copy file
549
+ fs.copyFileSync(srcPath, destPath);
550
+
551
+ // Log files at root level only
552
+ if (depth === 0) {
553
+ const ext = path.extname(entry.name);
554
+ const icon = this._getFileIcon(ext);
555
+ console.log(` ${icon} ${entry.name}`);
556
+ }
557
+ }
558
+ });
559
+ }
560
+
561
+ /**
562
+ * Get icon for file type
563
+ */
564
+ _getFileIcon(ext) {
565
+ const icons = {
566
+ '.html': '📄',
567
+ '.css': '🎨',
568
+ '.js': '📜',
569
+ '.json': '📋',
570
+ '.png': '🖼️',
571
+ '.jpg': '🖼️',
572
+ '.jpeg': '🖼️',
573
+ '.gif': '🖼️',
574
+ '.svg': '🎨',
575
+ '.ico': '🔖',
576
+ '.woff': '🔤',
577
+ '.woff2': '🔤',
578
+ '.ttf': '🔤',
579
+ '.eot': '🔤'
580
+ };
581
+ return icons[ext.toLowerCase()] || '📦';
582
+ }
500
583
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juxscript",
3
- "version": "1.1.71",
3
+ "version": "1.1.73",
4
4
  "type": "module",
5
5
  "description": "A JavaScript UX authorship platform",
6
6
  "main": "index.js",