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,174 @@
1
+ import { Reactive, getOrCreateContainer } from './reactivity.js';
2
+
3
+ /**
4
+ * View component options
5
+ */
6
+ export interface ViewOptions {
7
+ title?: string;
8
+ description?: string;
9
+ children?: any[];
10
+ }
11
+
12
+ /**
13
+ * View component state
14
+ */
15
+ type ViewState = {
16
+ title: string;
17
+ description: string;
18
+ children: any[];
19
+ };
20
+
21
+ /**
22
+ * View component - container for organizing page content
23
+ *
24
+ * Usage:
25
+ * const view = jux.view('myView', {
26
+ * title: 'Dashboard',
27
+ * description: 'Main dashboard view'
28
+ * });
29
+ * view.add(component1);
30
+ * await view.render();
31
+ */
32
+ export class View extends Reactive {
33
+ state!: ViewState;
34
+ container: HTMLElement | null = null;
35
+
36
+ constructor(componentId: string, options: ViewOptions = {}) {
37
+ super();
38
+ this._setComponentId(componentId);
39
+
40
+ this.state = this._createReactiveState({
41
+ title: options.title ?? '',
42
+ description: options.description ?? '',
43
+ children: Array.isArray(options.children) ? [...options.children] : [],
44
+ }) as ViewState;
45
+ }
46
+
47
+ /* -------------------------
48
+ * Fluent API
49
+ * ------------------------- */
50
+
51
+ title(value: string): this {
52
+ this.state.title = value;
53
+ return this;
54
+ }
55
+
56
+ description(value: string): this {
57
+ this.state.description = value;
58
+ return this;
59
+ }
60
+
61
+ add(component: any | any[]): this {
62
+ if (Array.isArray(component)) {
63
+ this.state.children.push(...component);
64
+ } else {
65
+ this.state.children.push(component);
66
+ }
67
+ return this;
68
+ }
69
+
70
+ /* -------------------------
71
+ * Render
72
+ * ------------------------- */
73
+
74
+ async render(targetId?: string): Promise<this> {
75
+ let container: HTMLElement;
76
+
77
+ if (targetId) {
78
+ const target = document.querySelector(targetId);
79
+ if (!target || !(target instanceof HTMLElement)) {
80
+ throw new Error(`View: Target element "${targetId}" not found`);
81
+ }
82
+ container = target;
83
+ } else {
84
+ container = getOrCreateContainer(this._componentId) as HTMLElement;
85
+ }
86
+
87
+ this.container = container;
88
+ const { title, description, children } = this.state;
89
+
90
+ const view = document.createElement('div');
91
+ view.className = 'jux-view';
92
+ view.id = this._componentId;
93
+
94
+ // View header
95
+ if (title || description) {
96
+ const header = document.createElement('div');
97
+ header.className = 'jux-view-header';
98
+
99
+ if (title) {
100
+ const titleEl = document.createElement('h1');
101
+ titleEl.className = 'jux-view-title';
102
+ titleEl.textContent = title;
103
+ header.appendChild(titleEl);
104
+ }
105
+
106
+ if (description) {
107
+ const descEl = document.createElement('p');
108
+ descEl.className = 'jux-view-description';
109
+ descEl.textContent = description;
110
+ header.appendChild(descEl);
111
+ }
112
+
113
+ view.appendChild(header);
114
+ }
115
+
116
+ // View content
117
+ const content = document.createElement('div');
118
+ content.className = 'jux-view-content';
119
+
120
+ // Render children
121
+ for (let i = 0; i < children.length; i++) {
122
+ const child = children[i];
123
+ if (!child) continue;
124
+
125
+ let childId = (child as any)._componentId ?? `view-child-${i}`;
126
+
127
+ const childWrapper = document.createElement('div');
128
+ childWrapper.className = 'jux-view-item';
129
+ childWrapper.id = childId;
130
+ content.appendChild(childWrapper);
131
+
132
+ // Event binding - render children
133
+ if (typeof (child as any).render === 'function') {
134
+ try {
135
+ const result = (child as any).render(childWrapper);
136
+ if (result && typeof (result as any).then === 'function') {
137
+ await result;
138
+ }
139
+ } catch (err) {
140
+ console.error(`View: Error rendering child ${i}:`, err);
141
+ childWrapper.innerHTML = `<div style="color: #ff6b6b; padding: 1rem;">Error: ${(err as Error).message}</div>`;
142
+ }
143
+ }
144
+ }
145
+
146
+ view.appendChild(content);
147
+ container.appendChild(view);
148
+
149
+ this.emit('rendered');
150
+ return this;
151
+ }
152
+
153
+ /**
154
+ * Render to another Jux component's container
155
+ */
156
+ async renderTo(juxComponent: any): Promise<this> {
157
+ if (!juxComponent || typeof juxComponent !== 'object') {
158
+ throw new Error('View.renderTo: Invalid component - not an object');
159
+ }
160
+
161
+ if (!juxComponent._componentId || typeof juxComponent._componentId !== 'string') {
162
+ throw new Error('View.renderTo: Invalid component - missing _componentId (not a Jux component)');
163
+ }
164
+
165
+ return this.render(`#${juxComponent._componentId}`);
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Factory helper
171
+ */
172
+ export function view(componentId: string, options: ViewOptions = {}): View {
173
+ return new View(componentId, options);
174
+ }
package/lib/jux.ts ADDED
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Jux Runtime - Shared across all pages
3
+ * Imports all components and provides the jux global object
4
+ */
5
+
6
+ import { Reactive } from './components/reactivity.js';
7
+ import { Data, data } from './components/data.js';
8
+ import { table, Table, type TableOptions, type TableColumn } from './components/table.js';
9
+ import { hero, Hero, type HeroOptions } from './components/hero.js';
10
+ import { card, Card, type CardOptions, type CardAction } from './components/card.js';
11
+ import { button, Button, type ButtonOptions } from './components/button.js';
12
+ import { header, Header, type HeaderOptions } from './components/header.js';
13
+ import { footer, Footer, type FooterOptions } from './components/footer.js';
14
+ import { main, Main, type MainOptions } from './components/main.js';
15
+ import { sidebar, Sidebar, type SidebarOptions } from './components/sidebar.js';
16
+ import { container, Container, type ContainerOptions } from './components/container.js';
17
+ import { modal, Modal, type ModalOptions } from './components/modal.js';
18
+ import { tabs, Tabs, type TabsOptions, type Tab } from './components/tabs.js';
19
+ import { list, List, type ListOptions, type ListItem } from './components/list.js';
20
+ import { menu, Menu, type MenuOptions, type MenuItem } from './components/menu.js';
21
+ import { nav, Nav, type NavOptions, type NavItem } from './components/nav.js';
22
+ import { chart, Chart, type ChartOptions } from './components/chart.js';
23
+ import { view, View, type ViewOptions } from './components/view.js';
24
+ import { code, Code, type CodeOptions } from './components/code.js';
25
+ import { input, Input, type InputOptions } from './components/input.js';
26
+ import { node, Node, type NodeOptions } from './components/node.js';
27
+ import { App } from './components/app.js';
28
+ import { Style } from './components/style.js';
29
+ import { Script } from './components/script.js';
30
+ import { Layout } from './components/layout.js';
31
+ import { ErrorHandler } from './components/error-handler.js';
32
+ import { theme, Theme } from './components/theme.js';
33
+
34
+ /* -------------------------
35
+ * Type Exports
36
+ * ------------------------- */
37
+
38
+ export type {
39
+ HeroOptions,
40
+ ButtonOptions,
41
+ ContainerOptions,
42
+ ListOptions,
43
+ ListItem,
44
+ HeaderOptions,
45
+ FooterOptions,
46
+ MainOptions,
47
+ SidebarOptions,
48
+ ModalOptions,
49
+ TabsOptions,
50
+ Tab,
51
+ MenuOptions,
52
+ MenuItem,
53
+ NavOptions,
54
+ NavItem,
55
+ ViewOptions,
56
+ TableOptions,
57
+ TableColumn,
58
+ ChartOptions,
59
+ CodeOptions,
60
+ InputOptions,
61
+ NodeOptions,
62
+ CardOptions,
63
+ CardAction
64
+ };
65
+
66
+ /* -------------------------
67
+ * Class Exports
68
+ * ------------------------- */
69
+
70
+ export {
71
+ Reactive,
72
+ Data,
73
+ Hero,
74
+ Card,
75
+ Button,
76
+ Container,
77
+ List,
78
+ Table,
79
+ Header,
80
+ Footer,
81
+ Main,
82
+ Sidebar,
83
+ Modal,
84
+ Tabs,
85
+ Menu,
86
+ Nav,
87
+ Chart,
88
+ Code,
89
+ Input,
90
+ View,
91
+ Node,
92
+ App,
93
+ Style,
94
+ Script,
95
+ Layout,
96
+ Theme
97
+ };
98
+
99
+ /* -------------------------
100
+ * Jux Singleton
101
+ * ------------------------- */
102
+
103
+ class Jux {
104
+ apiUrl: string = '/api/query';
105
+
106
+ // Utility methods
107
+ param(name: string): string | null {
108
+ if (typeof window !== 'undefined' && (window as any).juxContext?.[name] !== undefined) {
109
+ return (window as any).juxContext[name];
110
+ }
111
+
112
+ if (typeof window !== 'undefined') {
113
+ const urlParams = new URLSearchParams(window.location.search);
114
+ return urlParams.get(name);
115
+ }
116
+
117
+ return null;
118
+ }
119
+
120
+ // Component methods
121
+ data = data;
122
+ table = table;
123
+ hero = hero;
124
+ card = card;
125
+ button = button;
126
+ header = header;
127
+ footer = footer;
128
+ main = main;
129
+ sidebar = sidebar;
130
+ container = container;
131
+ modal = modal;
132
+ tabs = tabs;
133
+ list = list;
134
+ menu = menu;
135
+ code = code;
136
+ nav = nav;
137
+ input = input;
138
+ node = node;
139
+ view = view;
140
+ chart = chart;
141
+ theme = theme;
142
+
143
+ // App-level configuration methods
144
+ app(): App {
145
+ return new App();
146
+ }
147
+
148
+ style(contentOrUrl: string): Style {
149
+ return new Style(contentOrUrl);
150
+ }
151
+
152
+ script(contentOrSrc: string): Script {
153
+ return new Script(contentOrSrc);
154
+ }
155
+
156
+ layout(juxFile: string): Layout {
157
+ return new Layout(juxFile);
158
+ }
159
+ }
160
+
161
+ const jux = new Jux();
162
+
163
+ /* -------------------------
164
+ * Exports
165
+ *
166
+ * ⚠️ IMPORTANT: When adding a new component:
167
+ * 1. Import it at the top
168
+ * 2. Add to Jux class as a method
169
+ * 3. Export as $ alias below
170
+ * 4. Update machinery/compiler → COMPONENT_ALIASES array
171
+ * ------------------------- */
172
+
173
+ export {
174
+ jux,
175
+ // $ aliases - just re-export the factory functions
176
+ table as $table,
177
+ hero as $hero,
178
+ card as $card,
179
+ button as $button,
180
+ header as $header,
181
+ footer as $footer,
182
+ main as $main,
183
+ sidebar as $sidebar,
184
+ container as $container,
185
+ modal as $modal,
186
+ tabs as $tabs,
187
+ list as $list,
188
+ menu as $menu,
189
+ code as $code,
190
+ nav as $nav,
191
+ input as $input,
192
+ node as $node,
193
+ view as $view,
194
+ chart as $chart,
195
+ data as $data,
196
+ App as $app,
197
+ Style as $style,
198
+ Script as $script,
199
+ Layout as $layout,
200
+ theme as $theme,
201
+
202
+ ErrorHandler
203
+ };
@@ -0,0 +1,260 @@
1
+ /* Default Layout Grid (Notion-style) */
2
+
3
+ #app {
4
+
5
+ display: grid;
6
+ grid-template-areas:
7
+ "header header"
8
+ "sidebar main"
9
+ "footer footer";
10
+ grid-template-columns: 250px 1fr;
11
+ grid-template-rows: auto 1fr auto;
12
+ min-height: 100vh;
13
+ }
14
+
15
+ /* Header */
16
+ #appheader {
17
+ grid-area: header;
18
+ position: sticky;
19
+ top: 0;
20
+ z-index: 100;
21
+ background: var(--color-surface-elevated);
22
+ border-bottom: var(--border-width) solid var(--color-border);
23
+ display: flex;
24
+ align-items: center;
25
+ padding: var(--space-md) var(--space-lg);
26
+ gap: var(--space-lg);
27
+ }
28
+
29
+ #appheader-logo {
30
+ flex-shrink: 0;
31
+ }
32
+
33
+ #appheader-nav {
34
+ flex: 1;
35
+ display: flex;
36
+ gap: var(--space-md);
37
+ }
38
+
39
+ #appheader-actions {
40
+ flex-shrink: 0;
41
+ display: flex;
42
+ gap: var(--space-sm);
43
+ }
44
+
45
+ /* Subheader - Hidden in default layout */
46
+ #appsubheader {
47
+ display: none;
48
+ }
49
+
50
+ #appsubheader-breadcrumbs,
51
+ #appsubheader-tabs,
52
+ #appsubheader-actions {
53
+ display: none;
54
+ }
55
+
56
+ /* Sidebar */
57
+ #appsidebar {
58
+ grid-area: sidebar;
59
+ overflow-y: auto;
60
+ background: var(--color-surface-base);
61
+ border-right: var(--border-width) solid var(--color-border);
62
+ transition: transform var(--transition-base);
63
+ display: flex;
64
+ flex-direction: column;
65
+ }
66
+
67
+ #appsidebar-header {
68
+ padding: var(--space-md);
69
+ border-bottom: var(--border-width) solid var(--color-border);
70
+ flex-shrink: 0;
71
+ }
72
+
73
+ #appsidebar-content {
74
+ flex: 1;
75
+ overflow-y: auto;
76
+ padding: var(--space-sm);
77
+ }
78
+
79
+ #appsidebar-footer {
80
+ padding: var(--space-md);
81
+ border-top: var(--border-width) solid var(--color-border);
82
+ flex-shrink: 0;
83
+ }
84
+
85
+ /* Main */
86
+ #appmain {
87
+ grid-area: main;
88
+ overflow-y: auto;
89
+ padding: var(--space-xl);
90
+ background: var(--color-background);
91
+ }
92
+
93
+ /* Aside - Hidden in default layout */
94
+ #appaside {
95
+ display: none;
96
+ }
97
+
98
+ #appaside-header,
99
+ #appaside-content,
100
+ #appaside-footer {
101
+ display: none;
102
+ }
103
+
104
+ /* Footer */
105
+ #appfooter {
106
+ grid-area: footer;
107
+ background: var(--color-surface-elevated);
108
+ border-top: var(--border-width) solid var(--color-border);
109
+ padding: var(--space-lg) var(--space-xl);
110
+ display: flex;
111
+ justify-content: space-between;
112
+ align-items: center;
113
+ }
114
+
115
+ #appfooter-content {
116
+ flex: 1;
117
+ }
118
+
119
+ #appfooter-legal {
120
+ flex-shrink: 0;
121
+ font-size: var(--font-size-sm);
122
+ color: var(--color-text-secondary);
123
+ }
124
+
125
+ /* Modal */
126
+ #appmodal {
127
+ position: fixed;
128
+ top: 0;
129
+ left: 0;
130
+ right: 0;
131
+ bottom: 0;
132
+ z-index: 2000;
133
+ display: none;
134
+ }
135
+
136
+ #appmodal[aria-hidden="false"] {
137
+ display: flex;
138
+ align-items: center;
139
+ justify-content: center;
140
+ }
141
+
142
+ #appmodal-backdrop {
143
+ position: absolute;
144
+ inset: 0;
145
+ background: rgba(0, 0, 0, 0.6);
146
+ backdrop-filter: blur(4px);
147
+ }
148
+
149
+ #appmodal-container {
150
+ position: relative;
151
+ background: var(--color-surface-elevated);
152
+ border-radius: var(--radius-lg);
153
+ box-shadow: var(--shadow-2xl);
154
+ max-width: 90vw;
155
+ max-height: 90vh;
156
+ display: flex;
157
+ flex-direction: column;
158
+ overflow: hidden;
159
+ }
160
+
161
+ #appmodal-header {
162
+ padding: var(--space-lg);
163
+ border-bottom: var(--border-width) solid var(--color-border);
164
+ flex-shrink: 0;
165
+ }
166
+
167
+ #appmodal-content {
168
+ flex: 1;
169
+ overflow-y: auto;
170
+ padding: var(--space-lg);
171
+ }
172
+
173
+ #appmodal-footer {
174
+ padding: var(--space-lg);
175
+ border-top: var(--border-width) solid var(--color-border);
176
+ flex-shrink: 0;
177
+ display: flex;
178
+ gap: var(--space-sm);
179
+ justify-content: flex-end;
180
+ }
181
+
182
+ /* Tablet (portrait) */
183
+ @media (max-width: 1024px) {
184
+ #app {
185
+ grid-template-columns: 200px 1fr;
186
+ }
187
+
188
+ #appmain {
189
+ padding: var(--space-lg);
190
+ }
191
+
192
+ #appheader {
193
+ padding: var(--space-sm) var(--space-md);
194
+ gap: var(--space-md);
195
+ }
196
+ }
197
+
198
+ /* Mobile */
199
+ @media (max-width: 768px) {
200
+ #app {
201
+ grid-template-areas:
202
+ "header"
203
+ "main"
204
+ "footer";
205
+ grid-template-columns: 1fr;
206
+ }
207
+
208
+ #appsidebar {
209
+ position: fixed;
210
+ top: 60px;
211
+ left: 0;
212
+ bottom: 0;
213
+ width: 280px;
214
+ max-width: 80vw;
215
+ z-index: 99;
216
+ transform: translateX(-100%);
217
+ box-shadow: var(--shadow-xl);
218
+ }
219
+
220
+ #appsidebar.visible {
221
+ transform: translateX(0);
222
+ }
223
+
224
+ #appmain {
225
+ padding: var(--space-md);
226
+ }
227
+
228
+ #appheader {
229
+ padding: var(--space-xs) var(--space-md);
230
+ }
231
+
232
+ #appfooter {
233
+ padding: var(--space-md) var(--space-lg);
234
+ flex-direction: column;
235
+ gap: var(--space-sm);
236
+ text-align: center;
237
+ }
238
+ }
239
+
240
+ /* Small mobile */
241
+ @media (max-width: 480px) {
242
+ #appsidebar {
243
+ width: 100%;
244
+ max-width: 100vw;
245
+ }
246
+
247
+ #appmain {
248
+ padding: var(--space-sm);
249
+ }
250
+
251
+ #appfooter {
252
+ padding: var(--space-sm) var(--space-md);
253
+ font-size: var(--font-size-sm);
254
+ }
255
+
256
+ #appmodal-container {
257
+ max-width: 95vw;
258
+ max-height: 95vh;
259
+ }
260
+ }
@@ -0,0 +1,8 @@
1
+ //empty layout file
2
+ import { $hero } from '/lib/jux.js';
3
+
4
+ $hero('layout-hero', {
5
+ title: '...Default Layout.... ',
6
+ subtitle: 'This is the default layout provided by JUX.',
7
+ description: 'You can customize this layout by creating your own layout files.'
8
+ }).render();