juxscript 1.0.8 → 1.0.10

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.
@@ -1,521 +0,0 @@
1
- /**
2
- * Notion Preset - Combined Layout + Theming
3
- * Light/Dark mode via [data-theme] attribute
4
- */
5
-
6
- /* ============================================
7
- BASE TOKENS (Theme-independent)
8
- ============================================ */
9
- :root {
10
- /* Typography */
11
- --font-family-base: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
12
- --font-family-mono: 'Courier New', monospace;
13
-
14
- --font-size-xs: 0.75rem;
15
- --font-size-sm: 0.875rem;
16
- --font-size-base: 1rem;
17
- --font-size-lg: 1.125rem;
18
- --font-size-xl: 1.25rem;
19
- --font-size-2xl: 1.5rem;
20
- --font-size-3xl: 2rem;
21
-
22
- --font-weight-normal: 400;
23
- --font-weight-medium: 500;
24
- --font-weight-semibold: 600;
25
- --font-weight-bold: 700;
26
-
27
- --line-height-tight: 1.25;
28
- --line-height-normal: 1.5;
29
- --line-height-relaxed: 1.75;
30
-
31
- /* Spacing */
32
- --space-xs: 0.25rem;
33
- --space-sm: 0.5rem;
34
- --space-md: 1rem;
35
- --space-lg: 1.5rem;
36
- --space-xl: 2rem;
37
- --space-2xl: 3rem;
38
- --space-3xl: 4rem;
39
-
40
- /* Borders */
41
- --radius-sm: 4px;
42
- --radius-md: 8px;
43
- --radius-lg: 12px;
44
- --radius-xl: 16px;
45
- --radius-full: 9999px;
46
-
47
- --border-width: 1px;
48
-
49
- /* Transitions */
50
- --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
51
- --transition-base: 200ms cubic-bezier(0.4, 0, 0.2, 1);
52
- --transition-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);
53
-
54
- /* Semantic colors (theme-independent) */
55
- --color-success: #10b981;
56
- --color-warning: #f59e0b;
57
- --color-danger: #ef4444;
58
- --color-info: #3b82f6;
59
- }
60
-
61
- /* ============================================
62
- LIGHT THEME (Default)
63
- ============================================ */
64
- :root,
65
- [data-theme="light"] {
66
- --color-brand: #0a9ca5;
67
- --color-brand-hover: #088892;
68
-
69
- --color-text-primary: #111827;
70
- --color-text-secondary: #6b7280;
71
- --color-text-tertiary: #9ca3af;
72
- --color-text-inverse: #ffffff;
73
-
74
- --color-surface-base: #f9fafb;
75
- --color-surface-elevated: #ffffff;
76
- --color-surface-hover: #f3f4f6;
77
- --color-background: #ffffff;
78
-
79
- --color-border: rgba(0, 0, 0, 0.06);
80
- --color-border-hover: rgba(0, 0, 0, 0.12);
81
-
82
- --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
83
- --shadow-md: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
84
- --shadow-lg: 0 4px 8px 0 rgba(0, 0, 0, 0.12);
85
- --shadow-xl: 0 8px 16px 0 rgba(0, 0, 0, 0.15);
86
- }
87
-
88
- /* ============================================
89
- DARK THEME
90
- ============================================ */
91
- [data-theme="dark"] {
92
- --color-brand: #14b8a6;
93
- --color-brand-hover: #0d9488;
94
-
95
- --color-text-primary: #f9fafb;
96
- --color-text-secondary: #d1d5db;
97
- --color-text-tertiary: #9ca3af;
98
- --color-text-inverse: #111827;
99
-
100
- --color-surface-base: #1f2937;
101
- --color-surface-elevated: #374151;
102
- --color-surface-hover: #4b5563;
103
- --color-background: #111827;
104
-
105
- --color-border: rgba(255, 255, 255, 0.08);
106
- --color-border-hover: rgba(255, 255, 255, 0.16);
107
-
108
- --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
109
- --shadow-md: 0 2px 4px 0 rgba(0, 0, 0, 0.4);
110
- --shadow-lg: 0 4px 8px 0 rgba(0, 0, 0, 0.5);
111
- --shadow-xl: 0 8px 16px 0 rgba(0, 0, 0, 0.6);
112
- }
113
-
114
- /* ============================================
115
- GLOBAL STYLES
116
- ============================================ */
117
- * {
118
- margin: 0;
119
- padding: 0;
120
- box-sizing: border-box;
121
- }
122
-
123
- body {
124
- font-family: var(--font-family-base);
125
- font-size: var(--font-size-base);
126
- line-height: var(--line-height-normal);
127
- color: var(--color-text-primary);
128
- background: var(--color-background);
129
- transition: background-color var(--transition-base), color var(--transition-base);
130
- }
131
-
132
- /* ============================================
133
- NOTION LAYOUT GRID
134
- ============================================ */
135
- #app {
136
- display: grid;
137
- grid-template-areas:
138
- "header header"
139
- "sidebar main"
140
- "footer footer";
141
- grid-template-columns: 350px 1fr;
142
- grid-template-rows: auto 1fr auto;
143
- min-height: 100vh;
144
- }
145
-
146
- /* ============================================
147
- HEADER
148
- ============================================ */
149
- #appheader {
150
- grid-area: header;
151
- position: sticky;
152
- top: 0;
153
- z-index: 100;
154
- background: var(--color-surface-elevated);
155
- border-bottom: var(--border-width) solid var(--color-border);
156
- display: flex;
157
- align-items: center;
158
- padding: var(--space-md) var(--space-lg);
159
- gap: var(--space-lg);
160
- transition: background-color var(--transition-base), border-color var(--transition-base);
161
- }
162
-
163
- #appheader-content {
164
- display: flex;
165
- align-items: center;
166
- width: 100%;
167
- gap: var(--space-lg);
168
- }
169
-
170
- #appheader-logo {
171
- flex-shrink: 0;
172
- }
173
-
174
- #appheader-nav {
175
- flex: 1;
176
- display: flex;
177
- gap: var(--space-md);
178
- }
179
-
180
- #appheader-actions {
181
- flex-shrink: 0;
182
- display: flex;
183
- gap: var(--space-sm);
184
- align-items: center;
185
- }
186
-
187
- /* Subheader - Hidden in notion layout */
188
- #appsubheader,
189
- #appsubheader-breadcrumbs,
190
- #appsubheader-tabs,
191
- #appsubheader-actions {
192
- display: none;
193
- }
194
-
195
- /* ============================================
196
- SIDEBAR
197
- ============================================ */
198
- #appsidebar {
199
- grid-area: sidebar;
200
- overflow-y: auto;
201
- background: var(--color-surface-base);
202
- border-right: var(--border-width) solid var(--color-border);
203
- transition: transform var(--transition-base),
204
- background-color var(--transition-base),
205
- border-color var(--transition-base);
206
- display: flex;
207
- flex-direction: column;
208
- }
209
-
210
- #appsidebar-header {
211
- padding: var(--space-md);
212
- border-bottom: var(--border-width) solid var(--color-border);
213
- flex-shrink: 0;
214
- }
215
-
216
- #appsidebar-content {
217
- flex: 1;
218
- overflow-y: auto;
219
- padding: var(--space-sm);
220
- }
221
-
222
- #appsidebar-footer {
223
- padding: var(--space-md);
224
- border-top: var(--border-width) solid var(--color-border);
225
- flex-shrink: 0;
226
- }
227
-
228
- /* ============================================
229
- MAIN
230
- ============================================ */
231
- #appmain {
232
- grid-area: main;
233
- overflow-y: auto;
234
- padding: var(--space-xl);
235
- background: var(--color-background);
236
- transition: background-color var(--transition-base);
237
- }
238
-
239
- #appmain-content {
240
- max-width: 100%;
241
- }
242
-
243
- /* ============================================
244
- ASIDE (Hidden in Notion layout)
245
- ============================================ */
246
- #appaside,
247
- #appaside-header,
248
- #appaside-content,
249
- #appaside-footer {
250
- display: none;
251
- }
252
-
253
- /* ============================================
254
- FOOTER
255
- ============================================ */
256
- #appfooter {
257
- grid-area: footer;
258
- background: var(--color-surface-elevated);
259
- border-top: var(--border-width) solid var(--color-border);
260
- padding: var(--space-lg) var(--space-xl);
261
- display: flex;
262
- justify-content: space-between;
263
- align-items: center;
264
- transition: background-color var(--transition-base), border-color var(--transition-base);
265
- }
266
-
267
- #appfooter-content {
268
- flex: 1;
269
- }
270
-
271
- #appfooter-legal {
272
- flex-shrink: 0;
273
- font-size: var(--font-size-sm);
274
- color: var(--color-text-secondary);
275
- }
276
-
277
- /* ============================================
278
- MODAL
279
- ============================================ */
280
- #appmodal {
281
- position: fixed;
282
- top: 0;
283
- left: 0;
284
- right: 0;
285
- bottom: 0;
286
- z-index: 2000;
287
- display: none;
288
- }
289
-
290
- #appmodal[aria-hidden="false"] {
291
- display: flex;
292
- align-items: center;
293
- justify-content: center;
294
- }
295
-
296
- #appmodal-backdrop {
297
- position: absolute;
298
- inset: 0;
299
- background: rgba(0, 0, 0, 0.6);
300
- backdrop-filter: blur(4px);
301
- }
302
-
303
- #appmodal-container {
304
- position: relative;
305
- background: var(--color-surface-elevated);
306
- border-radius: var(--radius-lg);
307
- box-shadow: var(--shadow-xl);
308
- max-width: 90vw;
309
- max-height: 90vh;
310
- display: flex;
311
- flex-direction: column;
312
- overflow: hidden;
313
- transition: background-color var(--transition-base);
314
- }
315
-
316
- #appmodal-header {
317
- padding: var(--space-lg);
318
- border-bottom: var(--border-width) solid var(--color-border);
319
- flex-shrink: 0;
320
- }
321
-
322
- #appmodal-content {
323
- flex: 1;
324
- overflow-y: auto;
325
- padding: var(--space-lg);
326
- }
327
-
328
- #appmodal-footer {
329
- padding: var(--space-lg);
330
- border-top: var(--border-width) solid var(--color-border);
331
- flex-shrink: 0;
332
- display: flex;
333
- gap: var(--space-sm);
334
- justify-content: flex-end;
335
- }
336
-
337
- /* ============================================
338
- THEME TOGGLE - CLEAN & MINIMAL
339
- ============================================ */
340
-
341
- .jux-theme-toggle {
342
- display: inline-flex;
343
- align-items: center;
344
- gap: var(--space-sm);
345
- }
346
-
347
- .jux-theme-toggle-button {
348
- /* Remove all borders and backgrounds */
349
- padding: var(--space-sm);
350
- background: transparent;
351
- border: none;
352
- border-radius: var(--radius-md);
353
- cursor: pointer;
354
- font-size: 1.5rem; /* Bigger icons */
355
- line-height: 1;
356
- transition: all var(--transition-fast);
357
- color: var(--color-text-secondary);
358
-
359
- /* Smooth icon transition */
360
- display: flex;
361
- align-items: center;
362
- justify-content: center;
363
- width: 2.5rem;
364
- height: 2.5rem;
365
- }
366
-
367
- .jux-theme-toggle-button:hover {
368
- background: var(--color-surface-hover);
369
- color: var(--color-text-primary);
370
- transform: scale(1.05);
371
- }
372
-
373
- .jux-theme-toggle-button:active {
374
- transform: scale(0.95);
375
- }
376
-
377
- /* Light mode icon styling */
378
- [data-theme="light"] .jux-theme-toggle-button {
379
- color: #f59e0b; /* Warm sun color */
380
- }
381
-
382
- [data-theme="light"] .jux-theme-toggle-button:hover {
383
- color: #d97706;
384
- }
385
-
386
- /* Dark mode icon styling */
387
- [data-theme="dark"] .jux-theme-toggle-button {
388
- color: #fbbf24; /* Bright moon color */
389
- }
390
-
391
- [data-theme="dark"] .jux-theme-toggle-button:hover {
392
- color: #fcd34d;
393
- }
394
-
395
- /* Dropdown variant (if needed) */
396
- .jux-theme-toggle-select {
397
- padding: var(--space-sm) var(--space-md);
398
- background: transparent;
399
- border: var(--border-width) solid var(--color-border);
400
- border-radius: var(--radius-md);
401
- cursor: pointer;
402
- font-size: var(--font-size-base);
403
- color: var(--color-text-primary);
404
- transition: all var(--transition-fast);
405
- }
406
-
407
- .jux-theme-toggle-select:hover {
408
- border-color: var(--color-border-hover);
409
- background: var(--color-surface-hover);
410
- }
411
-
412
- .jux-theme-toggle-select:focus {
413
- outline: none;
414
- border-color: var(--color-brand);
415
- }
416
-
417
- /* ============================================
418
- RESPONSIVE BREAKPOINTS
419
- ============================================ */
420
-
421
- /* Tablet (portrait) */
422
- @media (max-width: 1024px) {
423
- #app {
424
- grid-template-columns: 200px 1fr;
425
- }
426
-
427
- #appmain {
428
- padding: var(--space-lg);
429
- }
430
-
431
- #appheader {
432
- padding: var(--space-sm) var(--space-md);
433
- gap: var(--space-md);
434
- }
435
- }
436
-
437
- /* Mobile */
438
- @media (max-width: 768px) {
439
- #app {
440
- grid-template-areas:
441
- "header"
442
- "main"
443
- "footer";
444
- grid-template-columns: 1fr;
445
- }
446
-
447
- #appsidebar {
448
- position: fixed;
449
- top: 60px;
450
- left: 0;
451
- bottom: 0;
452
- width: 280px;
453
- max-width: 80vw;
454
- z-index: 99;
455
- transform: translateX(-100%);
456
- box-shadow: var(--shadow-xl);
457
- }
458
-
459
- #appsidebar.visible {
460
- transform: translateX(0);
461
- }
462
-
463
- #appmain {
464
- padding: var(--space-md);
465
- }
466
-
467
- #appheader {
468
- padding: var(--space-xs) var(--space-md);
469
- }
470
-
471
- #appfooter {
472
- padding: var(--space-md) var(--space-lg);
473
- flex-direction: column;
474
- gap: var(--space-sm);
475
- text-align: center;
476
- }
477
-
478
- .jux-theme-toggle-button {
479
- width: 2.25rem;
480
- height: 2.25rem;
481
- font-size: 1.25rem;
482
- }
483
- }
484
-
485
- /* Small mobile */
486
- @media (max-width: 480px) {
487
- #appsidebar {
488
- width: 100%;
489
- max-width: 100vw;
490
- }
491
-
492
- #appmain {
493
- padding: var(--space-sm);
494
- }
495
-
496
- #appfooter {
497
- padding: var(--space-sm) var(--space-md);
498
- font-size: var(--font-size-sm);
499
- }
500
-
501
- #appmodal-container {
502
- max-width: 95vw;
503
- max-height: 95vh;
504
- }
505
- }
506
-
507
- /* ============================================
508
- SMOOTH TRANSITIONS
509
- ============================================ */
510
- body,
511
- #app,
512
- #appheader,
513
- #appsidebar,
514
- #appmain,
515
- #appfooter,
516
- #appmodal-container,
517
- .jux-theme-toggle-button {
518
- transition: background-color var(--transition-base),
519
- border-color var(--transition-base),
520
- color var(--transition-base);
521
- }
@@ -1,27 +0,0 @@
1
- // muse: notion layout preset
2
- export function layout() {
3
- /* styles */
4
- jux.include('/examples/presets/notion.css');
5
-
6
- /* regions */
7
- jux.header('appheader').render("#app");
8
- jux.header('appsubheader').render("#app");
9
- jux.sidebar('appsidebar').render("#app");
10
- jux.main('appmain').render("#app");
11
- jux.sidebar('appaside').render("#app");
12
- jux.footer('appfooter').render("#app");
13
- jux.modal('appmodal').render("#app");
14
-
15
- /* theme toggle in header */
16
- jux.themeToggle('theme-toggle', {
17
- themes: [
18
- { id: 'light', label: 'Light', icon: '☀️' },
19
- { id: 'dark', label: 'Dark', icon: '🌙' }
20
- ],
21
- variant: 'button',
22
- showLabel: false
23
- }).render('#appheader');
24
- }
25
-
26
- // Auto-run on import
27
- layout();
@@ -1,128 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import { fileURLToPath } from 'url';
4
- import CleanCSS from 'clean-css';
5
- import { FileValidator } from '../validators/file-validator.js';
6
-
7
- const __filename = fileURLToPath(import.meta.url);
8
- const __dirname = path.dirname(__filename);
9
-
10
- /**
11
- * Generates CSS content from Jux configuration
12
- * Handles: global.css, themes, imports, styleImports, and styleInline blocks
13
- */
14
- export function generateCSS(juxConfig, layoutParsed = null) {
15
- const fileValidator = new FileValidator();
16
- let cssOutput = '';
17
-
18
- // 1. Add global.css (always first)
19
- const globalCssPath = path.join(__dirname, '../../lib/global.css');
20
- if (fs.existsSync(globalCssPath)) {
21
- cssOutput += `/* Global Styles */\n`;
22
- cssOutput += fs.readFileSync(globalCssPath, 'utf-8') + '\n\n';
23
- console.log(` ✓ Included global.css`);
24
- }
25
-
26
- // 2. Add theme CSS (layout theme first, then page theme if different)
27
- const themesToLoad = [];
28
-
29
- if (layoutParsed?.config?.theme) {
30
- themesToLoad.push({ theme: layoutParsed.config.theme, source: 'layout' });
31
- }
32
-
33
- if (juxConfig.theme && juxConfig.theme !== layoutParsed?.config?.theme) {
34
- themesToLoad.push({ theme: juxConfig.theme, source: 'page' });
35
- }
36
-
37
- for (const { theme, source } of themesToLoad) {
38
- const themePath = path.join(__dirname, '../../lib/themes', `${theme}.css`);
39
- if (fs.existsSync(themePath)) {
40
- cssOutput += `/* Theme: ${theme} (${source}) */\n`;
41
- cssOutput += fs.readFileSync(themePath, 'utf-8') + '\n\n';
42
- console.log(` ✓ Included theme: ${theme} (${source})`);
43
- } else {
44
- console.warn(` ⚠️ Theme not found: ${theme}`);
45
- }
46
- }
47
-
48
- // 3. Process @import directives (CSS files only from layout, then page)
49
- const allImports = [
50
- ...(layoutParsed?.config?.import || []),
51
- ...(juxConfig.import || [])
52
- ];
53
-
54
- if (allImports.length > 0) {
55
- const { categorized } = fileValidator.categorizeImports(allImports);
56
-
57
- // Only process CSS files
58
- for (const cssImport of categorized.css) {
59
- const resolvedPath = path.join(__dirname, '../../', cssImport);
60
- if (fs.existsSync(resolvedPath)) {
61
- cssOutput += `/* Import: ${cssImport} */\n`;
62
- cssOutput += fs.readFileSync(resolvedPath, 'utf-8') + '\n\n';
63
- console.log(` ✓ Included import: ${cssImport}`);
64
- } else {
65
- console.warn(` ⚠️ Import not found: ${cssImport} (resolved to ${resolvedPath})`);
66
- }
67
- }
68
-
69
- // Log skipped JS imports (will be handled in HTML)
70
- if (categorized.js.length > 0) {
71
- console.log(` ℹ️ Skipped JS imports (will be added to HTML): ${categorized.js.length} file(s)`);
72
- }
73
- }
74
-
75
- // 4. Process @style imports (CSS file references from layout, then page)
76
- const allStyleImports = [
77
- ...(layoutParsed?.config?.styleImports || []),
78
- ...(juxConfig.styleImports || [])
79
- ];
80
-
81
- for (const styleImport of allStyleImports) {
82
- // Handle URLs (CDN)
83
- if (styleImport.startsWith('http://') || styleImport.startsWith('https://')) {
84
- cssOutput += `/* External CSS: ${styleImport} */\n`;
85
- cssOutput += `@import url('${styleImport}');\n\n`;
86
- console.log(` ✓ Added CDN import: ${styleImport}`);
87
- } else {
88
- // Handle local files
89
- const resolvedPath = path.join(__dirname, '../../', styleImport);
90
- if (fs.existsSync(resolvedPath)) {
91
- cssOutput += `/* Style Import: ${styleImport} */\n`;
92
- cssOutput += fs.readFileSync(resolvedPath, 'utf-8') + '\n\n';
93
- console.log(` ✓ Included style import: ${styleImport}`);
94
- } else {
95
- console.warn(` ⚠️ Style import not found: ${styleImport} (resolved to ${resolvedPath})`);
96
- }
97
- }
98
- }
99
-
100
- // 5. Process inline @style blocks (layout first, then page)
101
- const allInlineStyles = [
102
- ...(layoutParsed?.config?.styleInline || []),
103
- ...(juxConfig.styleInline || [])
104
- ];
105
-
106
- if (allInlineStyles.length > 0) {
107
- cssOutput += `/* Inline Styles (${allInlineStyles.length} block(s)) */\n`;
108
-
109
- allInlineStyles.forEach((styleBlock, index) => {
110
- const source = index < (layoutParsed?.config?.styleInline?.length || 0) ? 'layout' : 'page';
111
-
112
- try {
113
- const validatedStyle = fileValidator.validateStyleContent(styleBlock, `inline block #${index + 1}`);
114
-
115
- if (!fileValidator.isEmptyStyle(validatedStyle)) {
116
- cssOutput += `/* Inline Block #${index + 1} (${source}) */\n`;
117
- cssOutput += validatedStyle + '\n\n';
118
- }
119
- } catch (error) {
120
- console.error(` ❌ ${error.message}`);
121
- }
122
- });
123
-
124
- console.log(` ✓ Included ${allInlineStyles.length} inline style block(s)`);
125
- }
126
-
127
- return cssOutput.trim();
128
- }