juxscript 1.0.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.
Files changed (61) hide show
  1. package/README.md +292 -0
  2. package/bin/cli.js +149 -0
  3. package/lib/adapters/base-adapter.js +35 -0
  4. package/lib/adapters/index.js +33 -0
  5. package/lib/adapters/mysql-adapter.js +65 -0
  6. package/lib/adapters/postgres-adapter.js +70 -0
  7. package/lib/adapters/sqlite-adapter.js +56 -0
  8. package/lib/components/app.ts +124 -0
  9. package/lib/components/button.ts +136 -0
  10. package/lib/components/card.ts +205 -0
  11. package/lib/components/chart.ts +125 -0
  12. package/lib/components/code.ts +242 -0
  13. package/lib/components/container.ts +282 -0
  14. package/lib/components/data.ts +105 -0
  15. package/lib/components/docs-data.json +1211 -0
  16. package/lib/components/error-handler.ts +285 -0
  17. package/lib/components/footer.ts +146 -0
  18. package/lib/components/header.ts +167 -0
  19. package/lib/components/hero.ts +170 -0
  20. package/lib/components/import.ts +430 -0
  21. package/lib/components/input.ts +175 -0
  22. package/lib/components/layout.ts +113 -0
  23. package/lib/components/list.ts +392 -0
  24. package/lib/components/main.ts +111 -0
  25. package/lib/components/menu.ts +170 -0
  26. package/lib/components/modal.ts +216 -0
  27. package/lib/components/nav.ts +136 -0
  28. package/lib/components/node.ts +200 -0
  29. package/lib/components/reactivity.js +104 -0
  30. package/lib/components/script.ts +152 -0
  31. package/lib/components/sidebar.ts +168 -0
  32. package/lib/components/style.ts +129 -0
  33. package/lib/components/table.ts +279 -0
  34. package/lib/components/tabs.ts +191 -0
  35. package/lib/components/theme.ts +97 -0
  36. package/lib/components/view.ts +174 -0
  37. package/lib/jux.ts +203 -0
  38. package/lib/layouts/default.css +260 -0
  39. package/lib/layouts/default.jux +8 -0
  40. package/lib/layouts/figma.css +334 -0
  41. package/lib/layouts/figma.jux +0 -0
  42. package/lib/layouts/notion.css +258 -0
  43. package/lib/styles/base-theme.css +186 -0
  44. package/lib/styles/dark-theme.css +144 -0
  45. package/lib/styles/global.css +1131 -0
  46. package/lib/styles/light-theme.css +144 -0
  47. package/lib/styles/tokens/dark.css +86 -0
  48. package/lib/styles/tokens/light.css +86 -0
  49. package/lib/themes/dark.css +86 -0
  50. package/lib/themes/light.css +86 -0
  51. package/lib/utils/path-resolver.js +23 -0
  52. package/machinery/compiler.js +262 -0
  53. package/machinery/doc-generator.js +160 -0
  54. package/machinery/generators/css.js +128 -0
  55. package/machinery/generators/html.js +108 -0
  56. package/machinery/imports.js +155 -0
  57. package/machinery/server.js +185 -0
  58. package/machinery/validators/file-validator.js +123 -0
  59. package/machinery/watcher.js +148 -0
  60. package/package.json +58 -0
  61. package/types/globals.d.ts +16 -0
@@ -0,0 +1,285 @@
1
+ /**
2
+ * ErrorHandler - Global error handler for JUX components
3
+ * Displays errors visually in the DOM for easy debugging
4
+ */
5
+
6
+ export interface ComponentError {
7
+ component: string;
8
+ method: string;
9
+ message: string;
10
+ stack?: string;
11
+ timestamp: Date;
12
+ context?: any;
13
+ }
14
+
15
+ export class ErrorHandler {
16
+ private static errors: ComponentError[] = [];
17
+ private static errorContainer: HTMLDivElement | null = null;
18
+ private static enabled: boolean = true;
19
+
20
+ /**
21
+ * Initialize error handler
22
+ */
23
+ static init() {
24
+ if (typeof document === 'undefined') return;
25
+
26
+ // Create error container
27
+ this.errorContainer = document.createElement('div');
28
+ this.errorContainer.id = 'jux-error-container';
29
+ this.errorContainer.style.cssText = `
30
+ position: fixed;
31
+ bottom: 20px;
32
+ right: 20px;
33
+ max-width: 500px;
34
+ max-height: 600px;
35
+ overflow-y: auto;
36
+ z-index: 999999;
37
+ font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
38
+ font-size: 12px;
39
+ `;
40
+
41
+ document.body.appendChild(this.errorContainer);
42
+
43
+ // Listen for unhandled errors
44
+ window.addEventListener('error', (event) => {
45
+ this.captureError({
46
+ component: 'Global',
47
+ method: 'window.onerror',
48
+ message: event.message,
49
+ stack: event.error?.stack,
50
+ timestamp: new Date(),
51
+ context: { filename: event.filename, lineno: event.lineno, colno: event.colno }
52
+ });
53
+ });
54
+
55
+ // Listen for unhandled promise rejections
56
+ window.addEventListener('unhandledrejection', (event) => {
57
+ this.captureError({
58
+ component: 'Global',
59
+ method: 'unhandledrejection',
60
+ message: event.reason?.message || String(event.reason),
61
+ stack: event.reason?.stack,
62
+ timestamp: new Date(),
63
+ context: { promise: event.promise }
64
+ });
65
+ });
66
+ }
67
+
68
+ /**
69
+ * Capture and display an error
70
+ */
71
+ static captureError(error: ComponentError) {
72
+ if (!this.enabled) return;
73
+
74
+ this.errors.push(error);
75
+ this.renderError(error);
76
+ console.error(`[JUX Error] ${error.component}.${error.method}:`, error.message);
77
+ }
78
+
79
+ /**
80
+ * Render an error card in the DOM
81
+ */
82
+ private static renderError(error: ComponentError) {
83
+ if (!this.errorContainer) return;
84
+
85
+ const errorCard = document.createElement('div');
86
+ errorCard.style.cssText = `
87
+ background: linear-gradient(135deg, #1a1a1a 0%, #2a1a1a 100%);
88
+ border-left: 4px solid #ff4757;
89
+ border-radius: 8px;
90
+ padding: 16px;
91
+ margin-bottom: 12px;
92
+ box-shadow: 0 4px 12px rgba(255, 71, 87, 0.3);
93
+ animation: slideIn 0.3s ease-out;
94
+ `;
95
+
96
+ const time = error.timestamp.toLocaleTimeString();
97
+
98
+ errorCard.innerHTML = `
99
+ <style>
100
+ @keyframes slideIn {
101
+ from {
102
+ transform: translateX(100%);
103
+ opacity: 0;
104
+ }
105
+ to {
106
+ transform: translateX(0);
107
+ opacity: 1;
108
+ }
109
+ }
110
+ .jux-error-header {
111
+ display: flex;
112
+ justify-content: space-between;
113
+ align-items: center;
114
+ margin-bottom: 8px;
115
+ }
116
+ .jux-error-title {
117
+ color: #ff4757;
118
+ font-weight: bold;
119
+ font-size: 13px;
120
+ }
121
+ .jux-error-time {
122
+ color: #666;
123
+ font-size: 11px;
124
+ }
125
+ .jux-error-location {
126
+ color: #ffa502;
127
+ font-size: 12px;
128
+ margin-bottom: 8px;
129
+ }
130
+ .jux-error-message {
131
+ color: #e0e0e0;
132
+ margin-bottom: 8px;
133
+ line-height: 1.4;
134
+ }
135
+ .jux-error-stack {
136
+ background: #0a0a0a;
137
+ padding: 8px;
138
+ border-radius: 4px;
139
+ color: #888;
140
+ font-size: 10px;
141
+ max-height: 150px;
142
+ overflow-y: auto;
143
+ margin-bottom: 8px;
144
+ }
145
+ .jux-error-context {
146
+ background: #0a0a0a;
147
+ padding: 8px;
148
+ border-radius: 4px;
149
+ color: #888;
150
+ font-size: 10px;
151
+ }
152
+ .jux-error-close {
153
+ background: #ff4757;
154
+ color: white;
155
+ border: none;
156
+ border-radius: 4px;
157
+ padding: 4px 12px;
158
+ cursor: pointer;
159
+ font-size: 11px;
160
+ float: right;
161
+ }
162
+ .jux-error-close:hover {
163
+ background: #ff6b7a;
164
+ }
165
+ </style>
166
+ <div class="jux-error-header">
167
+ <div class="jux-error-title">⚠️ Component Error</div>
168
+ <div class="jux-error-time">${time}</div>
169
+ </div>
170
+ <div class="jux-error-location">
171
+ ${error.component}.${error.method}()
172
+ </div>
173
+ <div class="jux-error-message">
174
+ ${this.escapeHtml(error.message)}
175
+ </div>
176
+ ${error.stack ? `
177
+ <details>
178
+ <summary style="color: #888; cursor: pointer; margin-bottom: 8px;">Stack Trace</summary>
179
+ <div class="jux-error-stack">${this.escapeHtml(error.stack)}</div>
180
+ </details>
181
+ ` : ''}
182
+ ${error.context ? `
183
+ <details>
184
+ <summary style="color: #888; cursor: pointer; margin-bottom: 8px;">Context</summary>
185
+ <div class="jux-error-context">${this.escapeHtml(JSON.stringify(error.context, null, 2))}</div>
186
+ </details>
187
+ ` : ''}
188
+ <button class="jux-error-close">Dismiss</button>
189
+ `;
190
+
191
+ // Add close button handler
192
+ const closeBtn = errorCard.querySelector('.jux-error-close');
193
+ closeBtn?.addEventListener('click', () => {
194
+ errorCard.style.animation = 'slideOut 0.3s ease-out';
195
+ setTimeout(() => errorCard.remove(), 300);
196
+ });
197
+
198
+ this.errorContainer.appendChild(errorCard);
199
+ }
200
+
201
+ /**
202
+ * Escape HTML to prevent XSS
203
+ */
204
+ private static escapeHtml(str: string): string {
205
+ const div = document.createElement('div');
206
+ div.textContent = str;
207
+ return div.innerHTML;
208
+ }
209
+
210
+ /**
211
+ * Clear all errors
212
+ */
213
+ static clear() {
214
+ this.errors = [];
215
+ if (this.errorContainer) {
216
+ this.errorContainer.innerHTML = '';
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Enable/disable error handler
222
+ */
223
+ static setEnabled(enabled: boolean) {
224
+ this.enabled = enabled;
225
+ }
226
+
227
+ /**
228
+ * Get all captured errors
229
+ */
230
+ static getErrors(): ComponentError[] {
231
+ return [...this.errors];
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Utility function to wrap component methods with error handling
237
+ */
238
+ export function withErrorHandling<T extends (...args: any[]) => any>(
239
+ component: string,
240
+ method: string,
241
+ fn: T,
242
+ context?: any
243
+ ): T {
244
+ return ((...args: any[]) => {
245
+ try {
246
+ const result = fn(...args);
247
+
248
+ // Handle async functions
249
+ if (result instanceof Promise) {
250
+ return result.catch((error) => {
251
+ ErrorHandler.captureError({
252
+ component,
253
+ method,
254
+ message: error.message || String(error),
255
+ stack: error.stack,
256
+ timestamp: new Date(),
257
+ context
258
+ });
259
+ throw error;
260
+ });
261
+ }
262
+
263
+ return result;
264
+ } catch (error: any) {
265
+ ErrorHandler.captureError({
266
+ component,
267
+ method,
268
+ message: error.message || String(error),
269
+ stack: error.stack,
270
+ timestamp: new Date(),
271
+ context
272
+ });
273
+ throw error;
274
+ }
275
+ }) as T;
276
+ }
277
+
278
+ // Initialize on import
279
+ if (typeof document !== 'undefined') {
280
+ if (document.readyState === 'loading') {
281
+ document.addEventListener('DOMContentLoaded', () => ErrorHandler.init());
282
+ } else {
283
+ ErrorHandler.init();
284
+ }
285
+ }
@@ -0,0 +1,146 @@
1
+ import { Reactive, getOrCreateContainer } from './reactivity.js';
2
+
3
+ /**
4
+ * Footer component options
5
+ */
6
+ export interface FooterOptions {
7
+ content?: string;
8
+ copyright?: string;
9
+ links?: Array<{ label: string; href: string }>;
10
+ }
11
+
12
+ /**
13
+ * Footer component state
14
+ */
15
+ type FooterState = {
16
+ content: string;
17
+ copyright: string;
18
+ links: Array<{ label: string; href: string }>;
19
+ };
20
+
21
+ /**
22
+ * Footer component
23
+ *
24
+ * Usage:
25
+ * const footer = jux.footer('myFooter', {
26
+ * copyright: '© 2025 My Company',
27
+ * links: [
28
+ * { label: 'Privacy', href: '/privacy' },
29
+ * { label: 'Terms', href: '/terms' }
30
+ * ]
31
+ * });
32
+ * footer.render('#appfooter');
33
+ */
34
+ export class Footer extends Reactive {
35
+ state!: FooterState;
36
+ container: HTMLElement | null = null;
37
+
38
+ constructor(componentId: string, options: FooterOptions = {}) {
39
+ super();
40
+ this._setComponentId(componentId);
41
+
42
+ this.state = this._createReactiveState({
43
+ content: options.content ?? '',
44
+ copyright: options.copyright ?? '',
45
+ links: options.links ?? []
46
+ }) as FooterState;
47
+ }
48
+
49
+ /* -------------------------
50
+ * Fluent API
51
+ * ------------------------- */
52
+
53
+ content(value: string): this {
54
+ this.state.content = value;
55
+ return this;
56
+ }
57
+
58
+ copyright(value: string): this {
59
+ this.state.copyright = value;
60
+ return this;
61
+ }
62
+
63
+ links(value: Array<{ label: string; href: string }>): this {
64
+ this.state.links = value;
65
+ return this;
66
+ }
67
+
68
+ /* -------------------------
69
+ * Render
70
+ * ------------------------- */
71
+
72
+ render(targetId?: string): this {
73
+ let container: HTMLElement;
74
+
75
+ if (targetId) {
76
+ const target = document.querySelector(targetId);
77
+ if (!target || !(target instanceof HTMLElement)) {
78
+ throw new Error(`Footer: Target element "${targetId}" not found`);
79
+ }
80
+ container = target;
81
+ } else {
82
+ container = getOrCreateContainer(this._componentId) as HTMLElement;
83
+ }
84
+
85
+ this.container = container;
86
+ const { content, copyright, links } = this.state;
87
+
88
+ const footer = document.createElement('footer');
89
+ footer.className = 'jux-footer';
90
+ footer.id = this._componentId;
91
+
92
+ if (content) {
93
+ const contentEl = document.createElement('div');
94
+ contentEl.className = 'jux-footer-content';
95
+ contentEl.textContent = content;
96
+ footer.appendChild(contentEl);
97
+ }
98
+
99
+ if (links.length > 0) {
100
+ const linksEl = document.createElement('div');
101
+ linksEl.className = 'jux-footer-links';
102
+
103
+ links.forEach(link => {
104
+ const linkEl = document.createElement('a');
105
+ linkEl.className = 'jux-footer-link';
106
+ linkEl.href = link.href;
107
+ linkEl.textContent = link.label;
108
+ linksEl.appendChild(linkEl);
109
+ });
110
+
111
+ footer.appendChild(linksEl);
112
+ }
113
+
114
+ if (copyright) {
115
+ const copyrightEl = document.createElement('div');
116
+ copyrightEl.className = 'jux-footer-copyright';
117
+ copyrightEl.textContent = copyright;
118
+ footer.appendChild(copyrightEl);
119
+ }
120
+
121
+ container.appendChild(footer);
122
+ return this;
123
+ }
124
+
125
+ /**
126
+ * Render to another Jux component's container
127
+ */
128
+ renderTo(juxComponent: any): this {
129
+ if (!juxComponent || typeof juxComponent !== 'object') {
130
+ throw new Error('Footer.renderTo: Invalid component - not an object');
131
+ }
132
+
133
+ if (!juxComponent._componentId || typeof juxComponent._componentId !== 'string') {
134
+ throw new Error('Footer.renderTo: Invalid component - missing _componentId (not a Jux component)');
135
+ }
136
+
137
+ return this.render(`#${juxComponent._componentId}`);
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Factory helper
143
+ */
144
+ export function footer(componentId: string, options: FooterOptions = {}): Footer {
145
+ return new Footer(componentId, options);
146
+ }
@@ -0,0 +1,167 @@
1
+ import { Reactive, getOrCreateContainer } from './reactivity.js';
2
+
3
+ /**
4
+ * Header component options
5
+ */
6
+ export interface HeaderOptions {
7
+ title?: string;
8
+ logo?: string;
9
+ navigation?: Array<{ label: string; href: string }>;
10
+ sticky?: boolean;
11
+ }
12
+
13
+ /**
14
+ * Header component state
15
+ */
16
+ type HeaderState = {
17
+ title: string;
18
+ logo: string;
19
+ navigation: Array<{ label: string; href: string }>;
20
+ sticky: boolean;
21
+ };
22
+
23
+ /**
24
+ * Header component
25
+ *
26
+ * Usage:
27
+ * const header = jux.header('myHeader', {
28
+ * title: 'My App',
29
+ * navigation: [
30
+ * { label: 'Home', href: '/' },
31
+ * { label: 'About', href: '/about' }
32
+ * ]
33
+ * });
34
+ * header.render('#appheader');
35
+ */
36
+ export class Header extends Reactive {
37
+ state!: HeaderState;
38
+ container: HTMLElement | null = null;
39
+
40
+ constructor(componentId: string, options: HeaderOptions = {}) {
41
+ super();
42
+ this._setComponentId(componentId);
43
+
44
+ this.state = this._createReactiveState({
45
+ title: options.title ?? '',
46
+ logo: options.logo ?? '',
47
+ navigation: options.navigation ?? [],
48
+ sticky: options.sticky ?? true
49
+ }) as HeaderState;
50
+ }
51
+
52
+ /* -------------------------
53
+ * Fluent API
54
+ * ------------------------- */
55
+
56
+ title(value: string): this {
57
+ this.state.title = value;
58
+ return this;
59
+ }
60
+
61
+ logo(value: string): this {
62
+ this.state.logo = value;
63
+ return this;
64
+ }
65
+
66
+ navigation(value: Array<{ label: string; href: string }>): this {
67
+ this.state.navigation = value;
68
+ return this;
69
+ }
70
+
71
+ sticky(value: boolean): this {
72
+ this.state.sticky = value;
73
+ return this;
74
+ }
75
+
76
+ /* -------------------------
77
+ * Render
78
+ * ------------------------- */
79
+
80
+ render(targetId?: string): this {
81
+ let container: HTMLElement;
82
+
83
+ if (targetId) {
84
+ const target = document.querySelector(targetId);
85
+ if (!target || !(target instanceof HTMLElement)) {
86
+ throw new Error(`Header: Target element "${targetId}" not found`);
87
+ }
88
+ container = target;
89
+ } else {
90
+ container = getOrCreateContainer(this._componentId) as HTMLElement;
91
+ }
92
+
93
+ this.container = container;
94
+ const { title, logo, navigation, sticky } = this.state;
95
+
96
+ const header = document.createElement('header');
97
+ header.className = 'jux-header';
98
+ header.id = this._componentId;
99
+
100
+ if (sticky) {
101
+ header.classList.add('jux-header-sticky');
102
+ }
103
+
104
+ // Logo section
105
+ if (logo || title) {
106
+ const logoSection = document.createElement('div');
107
+ logoSection.className = 'jux-header-logo';
108
+
109
+ if (logo) {
110
+ const logoImg = document.createElement('img');
111
+ logoImg.src = logo;
112
+ logoImg.alt = title || 'Logo';
113
+ logoSection.appendChild(logoImg);
114
+ }
115
+
116
+ if (title) {
117
+ const titleEl = document.createElement('span');
118
+ titleEl.className = 'jux-header-title';
119
+ titleEl.textContent = title;
120
+ logoSection.appendChild(titleEl);
121
+ }
122
+
123
+ header.appendChild(logoSection);
124
+ }
125
+
126
+ // Navigation
127
+ if (navigation.length > 0) {
128
+ const nav = document.createElement('nav');
129
+ nav.className = 'jux-header-nav';
130
+
131
+ navigation.forEach(item => {
132
+ const link = document.createElement('a');
133
+ link.className = 'jux-header-nav-item';
134
+ link.href = item.href;
135
+ link.textContent = item.label;
136
+ nav.appendChild(link);
137
+ });
138
+
139
+ header.appendChild(nav);
140
+ }
141
+
142
+ container.appendChild(header);
143
+ return this;
144
+ }
145
+
146
+ /**
147
+ * Render to another Jux component's container
148
+ */
149
+ renderTo(juxComponent: any): this {
150
+ if (!juxComponent || typeof juxComponent !== 'object') {
151
+ throw new Error('Header.renderTo: Invalid component - not an object');
152
+ }
153
+
154
+ if (!juxComponent._componentId || typeof juxComponent._componentId !== 'string') {
155
+ throw new Error('Header.renderTo: Invalid component - missing _componentId (not a Jux component)');
156
+ }
157
+
158
+ return this.render(`#${juxComponent._componentId}`);
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Factory helper
164
+ */
165
+ export function header(componentId: string, options: HeaderOptions = {}): Header {
166
+ return new Header(componentId, options);
167
+ }