juxscript 1.1.403 → 1.1.408

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 (154) hide show
  1. package/dist/components/button.d.ts +1 -0
  2. package/dist/components/button.d.ts.map +1 -1
  3. package/dist/components/button.js +37 -0
  4. package/dist/components/button.js.map +1 -1
  5. package/dist/components/c.d.ts +53 -0
  6. package/dist/components/c.d.ts.map +1 -0
  7. package/dist/components/c.js +127 -0
  8. package/dist/components/c.js.map +1 -0
  9. package/dist/components/charts/barChart.d.ts +119 -0
  10. package/dist/components/charts/barChart.d.ts.map +1 -0
  11. package/dist/components/charts/barChart.js +644 -0
  12. package/dist/components/charts/barChart.js.map +1 -0
  13. package/dist/components/charts/lineChart.d.ts +104 -0
  14. package/dist/components/charts/lineChart.d.ts.map +1 -0
  15. package/dist/components/charts/lineChart.js +466 -0
  16. package/dist/components/charts/lineChart.js.map +1 -0
  17. package/dist/components/charts/pieChart.d.ts +93 -0
  18. package/dist/components/charts/pieChart.d.ts.map +1 -0
  19. package/dist/components/charts/pieChart.js +397 -0
  20. package/dist/components/charts/pieChart.js.map +1 -0
  21. package/dist/components/checkbox.d.ts +2 -0
  22. package/dist/components/checkbox.d.ts.map +1 -1
  23. package/dist/components/checkbox.js +47 -0
  24. package/dist/components/checkbox.js.map +1 -1
  25. package/dist/components/flex.d.ts +91 -0
  26. package/dist/components/flex.d.ts.map +1 -0
  27. package/dist/components/flex.js +166 -0
  28. package/dist/components/flex.js.map +1 -0
  29. package/dist/components/g.d.ts +21 -0
  30. package/dist/components/g.d.ts.map +1 -0
  31. package/dist/components/g.js +52 -0
  32. package/dist/components/g.js.map +1 -0
  33. package/dist/components/input.d.ts +2 -0
  34. package/dist/components/input.d.ts.map +1 -1
  35. package/dist/components/input.js +21 -2
  36. package/dist/components/input.js.map +1 -1
  37. package/dist/components/jtable.d.ts +47 -0
  38. package/dist/components/jtable.d.ts.map +1 -0
  39. package/dist/components/jtable.js +307 -0
  40. package/dist/components/jtable.js.map +1 -0
  41. package/dist/components/link.d.ts +1 -0
  42. package/dist/components/link.d.ts.map +1 -1
  43. package/dist/components/link.js +17 -0
  44. package/dist/components/link.js.map +1 -1
  45. package/dist/components/list.d.ts +1 -0
  46. package/dist/components/list.d.ts.map +1 -1
  47. package/dist/components/list.js +18 -0
  48. package/dist/components/list.js.map +1 -1
  49. package/dist/components/menu.d.ts +108 -0
  50. package/dist/components/menu.d.ts.map +1 -0
  51. package/dist/components/menu.js +665 -0
  52. package/dist/components/menu.js.map +1 -0
  53. package/dist/components/nav.d.ts +1 -0
  54. package/dist/components/nav.d.ts.map +1 -1
  55. package/dist/components/nav.js +19 -0
  56. package/dist/components/nav.js.map +1 -1
  57. package/dist/components/radio.d.ts +1 -0
  58. package/dist/components/radio.d.ts.map +1 -1
  59. package/dist/components/radio.js +23 -0
  60. package/dist/components/radio.js.map +1 -1
  61. package/dist/components/routes.d.ts +17 -0
  62. package/dist/components/routes.d.ts.map +1 -1
  63. package/dist/components/routes.js +86 -0
  64. package/dist/components/routes.js.map +1 -1
  65. package/dist/components/select.d.ts +1 -0
  66. package/dist/components/select.d.ts.map +1 -1
  67. package/dist/components/select.js +17 -0
  68. package/dist/components/select.js.map +1 -1
  69. package/dist/components/table.d.ts +1 -0
  70. package/dist/components/table.d.ts.map +1 -1
  71. package/dist/components/table.js +20 -0
  72. package/dist/components/table.js.map +1 -1
  73. package/dist/components/tabs.d.ts +17 -1
  74. package/dist/components/tabs.d.ts.map +1 -1
  75. package/dist/components/tabs.js +50 -8
  76. package/dist/components/tabs.js.map +1 -1
  77. package/dist/components/tag.d.ts +1 -0
  78. package/dist/components/tag.d.ts.map +1 -1
  79. package/dist/components/tag.js +16 -0
  80. package/dist/components/tag.js.map +1 -1
  81. package/dist/components/widgets/calendar.d.ts +74 -0
  82. package/dist/components/widgets/calendar.d.ts.map +1 -0
  83. package/dist/components/widgets/calendar.js +308 -0
  84. package/dist/components/widgets/calendar.js.map +1 -0
  85. package/dist/components/widgets/canvas-ai.d.ts +12 -0
  86. package/dist/components/widgets/canvas-ai.d.ts.map +1 -0
  87. package/dist/components/widgets/canvas-ai.js +97 -0
  88. package/dist/components/widgets/canvas-ai.js.map +1 -0
  89. package/dist/components/widgets/canvas-compile.d.ts +36 -0
  90. package/dist/components/widgets/canvas-compile.d.ts.map +1 -0
  91. package/dist/components/widgets/canvas-compile.js +379 -0
  92. package/dist/components/widgets/canvas-compile.js.map +1 -0
  93. package/dist/components/widgets/canvas-persist.d.ts +11 -0
  94. package/dist/components/widgets/canvas-persist.d.ts.map +1 -0
  95. package/dist/components/widgets/canvas-persist.js +60 -0
  96. package/dist/components/widgets/canvas-persist.js.map +1 -0
  97. package/dist/components/widgets/canvas-registry.d.ts +42 -0
  98. package/dist/components/widgets/canvas-registry.d.ts.map +1 -0
  99. package/dist/components/widgets/canvas-registry.js +338 -0
  100. package/dist/components/widgets/canvas-registry.js.map +1 -0
  101. package/dist/components/widgets/canvas-styles.d.ts +2 -0
  102. package/dist/components/widgets/canvas-styles.d.ts.map +1 -0
  103. package/dist/components/widgets/canvas-styles.js +215 -0
  104. package/dist/components/widgets/canvas-styles.js.map +1 -0
  105. package/dist/components/widgets/canvas.d.ts +125 -0
  106. package/dist/components/widgets/canvas.d.ts.map +1 -0
  107. package/dist/components/widgets/canvas.js +1359 -0
  108. package/dist/components/widgets/canvas.js.map +1 -0
  109. package/dist/components/widgets/sidebar.d.ts +100 -0
  110. package/dist/components/widgets/sidebar.d.ts.map +1 -0
  111. package/dist/components/widgets/sidebar.js +434 -0
  112. package/dist/components/widgets/sidebar.js.map +1 -0
  113. package/dist/components/widgets/stepper.d.ts +87 -0
  114. package/dist/components/widgets/stepper.d.ts.map +1 -0
  115. package/dist/components/widgets/stepper.js +388 -0
  116. package/dist/components/widgets/stepper.js.map +1 -0
  117. package/dist/generated/jux-registry.d.ts +24 -0
  118. package/dist/generated/jux-registry.d.ts.map +1 -0
  119. package/dist/generated/jux-registry.js +90 -0
  120. package/dist/generated/jux-registry.js.map +1 -0
  121. package/dist/index.d.ts +41 -23
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js +39 -23
  124. package/dist/index.js.map +1 -1
  125. package/dist/state/pageState.d.ts +6 -0
  126. package/dist/state/pageState.d.ts.map +1 -1
  127. package/dist/state/pageState.js +24 -16
  128. package/dist/state/pageState.js.map +1 -1
  129. package/dist/styles/layout-regions-observer.d.ts +7 -0
  130. package/dist/styles/layout-regions-observer.d.ts.map +1 -0
  131. package/dist/styles/layout-regions-observer.js +52 -0
  132. package/dist/styles/layout-regions-observer.js.map +1 -0
  133. package/dist/utils/colors.d.ts +0 -3
  134. package/dist/utils/colors.d.ts.map +1 -1
  135. package/dist/utils/colors.js +20 -6
  136. package/dist/utils/colors.js.map +1 -1
  137. package/dist/utils/resolveContent.d.ts +11 -0
  138. package/dist/utils/resolveContent.d.ts.map +1 -0
  139. package/dist/utils/resolveContent.js +37 -0
  140. package/dist/utils/resolveContent.js.map +1 -0
  141. package/dist/utils/theme.d.ts +58 -0
  142. package/dist/utils/theme.d.ts.map +1 -0
  143. package/dist/utils/theme.js +172 -0
  144. package/dist/utils/theme.js.map +1 -0
  145. package/dist/widgets/canvas.d.ts +3 -94
  146. package/dist/widgets/canvas.d.ts.map +1 -1
  147. package/dist/widgets/canvas.js +3 -1007
  148. package/dist/widgets/canvas.js.map +1 -1
  149. package/juxconfig.example.js +19 -0
  150. package/machinery/compiler4.js +103 -9
  151. package/machinery/errors-client.js +171 -67
  152. package/machinery/jux-errors.js +218 -0
  153. package/machinery/serve.js +67 -0
  154. package/package.json +1 -1
@@ -1,1008 +1,4 @@
1
- import { pageState } from '../state/pageState.js';
2
- import generateId from '../utils/idgen.js';
3
- // ═══════════════════════════════════════════════════════════
4
- // DEFAULT INVENTORY
5
- // ═══════════════════════════════════════════════════════════
6
- const DEFAULT_INVENTORY = [
7
- { type: 'c', label: 'Container (c)', category: 'shapes' },
8
- { type: 'g', label: 'Group (g)', category: 'shapes' },
9
- { type: 'flex', label: 'Flex', category: 'shapes' },
10
- { type: 'flexRow', label: 'Flex Row', category: 'shapes' },
11
- { type: 'flexCol', label: 'Flex Col', category: 'shapes' },
12
- { type: 'sidebar', label: 'Sidebar', category: 'widgets' },
13
- { type: 'calendar', label: 'Calendar', category: 'widgets' },
14
- { type: 'stepper', label: 'Stepper', category: 'widgets' },
15
- { type: 'tabs', label: 'Tabs', category: 'widgets' },
16
- { type: 'button', label: 'Button', category: 'components' },
17
- { type: 'input', label: 'Input', category: 'components' },
18
- { type: 'select', label: 'Select', category: 'components' },
19
- { type: 'checkbox', label: 'Checkbox', category: 'components' },
20
- { type: 'radio', label: 'Radio', category: 'components' },
21
- { type: 'list', label: 'List', category: 'components' },
22
- { type: 'table', label: 'Table', category: 'components' },
23
- { type: 'nav', label: 'Nav', category: 'components' },
24
- { type: 'link', label: 'Link', category: 'components' },
25
- { type: 'tag', label: 'Tag', category: 'components' },
26
- { type: 'h1', label: 'H1', category: 'components' },
27
- { type: 'p', label: 'Paragraph', category: 'components' },
28
- { type: 'span', label: 'Span', category: 'components' },
29
- ];
30
- // ═══════════════════════════════════════════════════════════
31
- // DEFAULT PROPS PER TYPE
32
- // ═══════════════════════════════════════════════════════════
33
- function defaultProps(type) {
34
- switch (type) {
35
- case 'c': return { width: '100%', height: 'auto', padding: '16px' };
36
- case 'g': return { gap: '8px' };
37
- case 'flex': return { direction: 'row', gap: '8px' };
38
- case 'flexRow': return { gap: '8px' };
39
- case 'flexCol': return { gap: '8px' };
40
- case 'button': return { content: 'Click me', variant: 'default' };
41
- case 'input': return { label: 'Label', placeholder: 'Type...', value: '' };
42
- case 'select': return { label: 'Choose', placeholder: 'Select...' };
43
- case 'checkbox': return { label: 'Check me' };
44
- case 'radio': return { label: 'Options' };
45
- case 'list': return {};
46
- case 'table': return {};
47
- case 'nav': return {};
48
- case 'link': return { content: 'Link', href: '#' };
49
- case 'tag': return { content: 'Content' };
50
- case 'h1': return { content: 'Heading' };
51
- case 'p': return { content: 'Paragraph text' };
52
- case 'span': return { content: 'Inline text' };
53
- case 'sidebar': return { title: 'Nav', width: '260px' };
54
- case 'calendar': return {};
55
- case 'stepper': return {};
56
- case 'tabs': return {};
57
- default: return {};
58
- }
59
- }
60
- function defaultCode(type, id, props) {
61
- switch (type) {
62
- case 'c': return `c('${props.width || '100%'}', '${props.height || 'auto'}', '${props.padding || '16px'}')`;
63
- case 'g': return `g([])`;
64
- case 'flex': return `flex('${props.direction || 'row'}', '${props.gap || '8px'}')`;
65
- case 'flexRow': return `flexRow('${props.gap || '8px'}')`;
66
- case 'flexCol': return `flexCol('${props.gap || '8px'}')`;
67
- case 'button': return `jux.btn('${id}', { content: '${props.content || 'Click me'}' })`;
68
- case 'input': return `jux.input('${id}', { label: '${props.label || 'Label'}', placeholder: '${props.placeholder || 'Type...'}' })`;
69
- case 'select': return `jux.select('${id}', { label: '${props.label || 'Choose'}', placeholder: '${props.placeholder || 'Select...'}', options: [{ label: 'A', value: 'a' }] })`;
70
- case 'checkbox': return `jux.checkbox('${id}', { label: '${props.label || 'Check me'}' })`;
71
- case 'radio': return `jux.radio('${id}', { label: '${props.label || 'Options'}', options: [{ label: 'A', value: 'a' }, { label: 'B', value: 'b' }] })`;
72
- case 'link': return `jux.link('${id}', { content: '${props.content || 'Link'}', href: '${props.href || '#'}' })`;
73
- case 'h1': return `jux.h1('${id}', { content: '${props.content || 'Heading'}' })`;
74
- case 'p': return `jux.p('${id}', { content: '${props.content || 'Paragraph text'}' })`;
75
- case 'span': return `jux.span('${id}', { content: '${props.content || 'Inline text'}' })`;
76
- case 'tag': return `jux.div('${id}', { content: '${props.content || 'Content'}' })`;
77
- default: return `jux.${type}('${id}')`;
78
- }
79
- }
80
- let canvasStylesInjected = false;
81
- // ═══════════════════════════════════════════════════════════
82
- // LIVE PREVIEW RENDERER
83
- // ═══════════════════════════════════════════════════════════
84
- function renderPreview(item) {
85
- const wrapper = document.createElement('div');
86
- wrapper.className = 'jux-canvas-preview';
87
- switch (item.type) {
88
- case 'button': {
89
- const btn = document.createElement('button');
90
- btn.className = 'jux-canvas-preview-btn';
91
- btn.textContent = item.props.content || 'Click me';
92
- btn.addEventListener('click', (e) => e.stopPropagation());
93
- wrapper.appendChild(btn);
94
- break;
95
- }
96
- case 'input': {
97
- const group = document.createElement('div');
98
- group.className = 'jux-canvas-preview-field';
99
- if (item.props.label) {
100
- const lbl = document.createElement('label');
101
- lbl.className = 'jux-canvas-preview-label';
102
- lbl.textContent = item.props.label;
103
- group.appendChild(lbl);
104
- }
105
- const inp = document.createElement('input');
106
- inp.className = 'jux-canvas-preview-input';
107
- inp.placeholder = item.props.placeholder || '';
108
- inp.value = item.props.value || '';
109
- inp.addEventListener('click', (e) => e.stopPropagation());
110
- group.appendChild(inp);
111
- wrapper.appendChild(group);
112
- break;
113
- }
114
- case 'select': {
115
- const group = document.createElement('div');
116
- group.className = 'jux-canvas-preview-field';
117
- if (item.props.label) {
118
- const lbl = document.createElement('label');
119
- lbl.className = 'jux-canvas-preview-label';
120
- lbl.textContent = item.props.label;
121
- group.appendChild(lbl);
122
- }
123
- const sel = document.createElement('select');
124
- sel.className = 'jux-canvas-preview-select';
125
- const opt = document.createElement('option');
126
- opt.textContent = item.props.placeholder || 'Select...';
127
- opt.disabled = true;
128
- opt.selected = true;
129
- sel.appendChild(opt);
130
- sel.addEventListener('click', (e) => e.stopPropagation());
131
- group.appendChild(sel);
132
- wrapper.appendChild(group);
133
- break;
134
- }
135
- case 'checkbox': {
136
- const group = document.createElement('div');
137
- group.className = 'jux-canvas-preview-field jux-canvas-preview-inline';
138
- const cb = document.createElement('input');
139
- cb.type = 'checkbox';
140
- cb.className = 'jux-canvas-preview-checkbox';
141
- cb.addEventListener('click', (e) => e.stopPropagation());
142
- group.appendChild(cb);
143
- const lbl = document.createElement('label');
144
- lbl.className = 'jux-canvas-preview-label';
145
- lbl.textContent = item.props.label || 'Checkbox';
146
- group.appendChild(lbl);
147
- wrapper.appendChild(group);
148
- break;
149
- }
150
- case 'radio': {
151
- const group = document.createElement('div');
152
- group.className = 'jux-canvas-preview-field';
153
- if (item.props.label) {
154
- const lbl = document.createElement('label');
155
- lbl.className = 'jux-canvas-preview-label';
156
- lbl.textContent = item.props.label;
157
- group.appendChild(lbl);
158
- }
159
- const radioGroup = document.createElement('div');
160
- radioGroup.className = 'jux-canvas-preview-radio-group';
161
- ['Option A', 'Option B'].forEach((text, i) => {
162
- const row = document.createElement('label');
163
- row.className = 'jux-canvas-preview-radio-row';
164
- const r = document.createElement('input');
165
- r.type = 'radio';
166
- r.name = `preview-radio-${item.id}`;
167
- r.addEventListener('click', (e) => e.stopPropagation());
168
- row.appendChild(r);
169
- row.appendChild(document.createTextNode(text));
170
- radioGroup.appendChild(row);
171
- });
172
- group.appendChild(radioGroup);
173
- wrapper.appendChild(group);
174
- break;
175
- }
176
- case 'link': {
177
- const a = document.createElement('a');
178
- a.className = 'jux-canvas-preview-link';
179
- a.textContent = item.props.content || 'Link';
180
- a.href = 'javascript:void(0)';
181
- a.addEventListener('click', (e) => e.stopPropagation());
182
- wrapper.appendChild(a);
183
- break;
184
- }
185
- case 'h1': {
186
- const h = document.createElement('h3');
187
- h.className = 'jux-canvas-preview-heading';
188
- h.textContent = item.props.content || 'Heading';
189
- wrapper.appendChild(h);
190
- break;
191
- }
192
- case 'p': {
193
- const p = document.createElement('p');
194
- p.className = 'jux-canvas-preview-text';
195
- p.textContent = item.props.content || 'Paragraph text';
196
- wrapper.appendChild(p);
197
- break;
198
- }
199
- case 'span': {
200
- const sp = document.createElement('span');
201
- sp.className = 'jux-canvas-preview-text';
202
- sp.textContent = item.props.content || 'Inline text';
203
- wrapper.appendChild(sp);
204
- break;
205
- }
206
- case 'tag': {
207
- const d = document.createElement('div');
208
- d.className = 'jux-canvas-preview-tag';
209
- d.textContent = item.props.content || 'Content';
210
- wrapper.appendChild(d);
211
- break;
212
- }
213
- case 'nav': {
214
- const nav = document.createElement('nav');
215
- nav.className = 'jux-canvas-preview-nav';
216
- ['Home', 'About', 'Contact'].forEach(text => {
217
- const a = document.createElement('a');
218
- a.className = 'jux-canvas-preview-nav-item';
219
- a.textContent = text;
220
- a.href = 'javascript:void(0)';
221
- a.addEventListener('click', (e) => e.stopPropagation());
222
- nav.appendChild(a);
223
- });
224
- wrapper.appendChild(nav);
225
- break;
226
- }
227
- case 'list': {
228
- const ul = document.createElement('ul');
229
- ul.className = 'jux-canvas-preview-list';
230
- ['Item 1', 'Item 2', 'Item 3'].forEach(text => {
231
- const li = document.createElement('li');
232
- li.textContent = text;
233
- ul.appendChild(li);
234
- });
235
- wrapper.appendChild(ul);
236
- break;
237
- }
238
- case 'table': {
239
- const table = document.createElement('table');
240
- table.className = 'jux-canvas-preview-table';
241
- const thead = document.createElement('thead');
242
- const hr = document.createElement('tr');
243
- ['Name', 'Value'].forEach(h => { const th = document.createElement('th'); th.textContent = h; hr.appendChild(th); });
244
- thead.appendChild(hr);
245
- table.appendChild(thead);
246
- const tbody = document.createElement('tbody');
247
- [['Alpha', '1'], ['Beta', '2']].forEach(row => {
248
- const tr = document.createElement('tr');
249
- row.forEach(cell => { const td = document.createElement('td'); td.textContent = cell; tr.appendChild(td); });
250
- tbody.appendChild(tr);
251
- });
252
- table.appendChild(tbody);
253
- wrapper.appendChild(table);
254
- break;
255
- }
256
- // Container/layout shapes
257
- case 'c': {
258
- const d = document.createElement('div');
259
- d.className = 'jux-canvas-preview-container';
260
- d.style.cssText = `width:${item.props.width || '100%'};min-height:40px;padding:${item.props.padding || '16px'};`;
261
- d.textContent = 'Container';
262
- wrapper.appendChild(d);
263
- break;
264
- }
265
- case 'flexRow':
266
- case 'flex': {
267
- const d = document.createElement('div');
268
- d.className = 'jux-canvas-preview-flex';
269
- d.style.cssText = `display:flex;flex-direction:row;gap:${item.props.gap || '8px'};`;
270
- [1, 2, 3].forEach(n => {
271
- const child = document.createElement('div');
272
- child.className = 'jux-canvas-preview-flex-child';
273
- child.textContent = String(n);
274
- d.appendChild(child);
275
- });
276
- wrapper.appendChild(d);
277
- break;
278
- }
279
- case 'flexCol': {
280
- const d = document.createElement('div');
281
- d.className = 'jux-canvas-preview-flex';
282
- d.style.cssText = `display:flex;flex-direction:column;gap:${item.props.gap || '8px'};`;
283
- [1, 2].forEach(n => {
284
- const child = document.createElement('div');
285
- child.className = 'jux-canvas-preview-flex-child';
286
- child.textContent = String(n);
287
- d.appendChild(child);
288
- });
289
- wrapper.appendChild(d);
290
- break;
291
- }
292
- case 'g': {
293
- const d = document.createElement('div');
294
- d.className = 'jux-canvas-preview-group';
295
- d.textContent = 'Group';
296
- wrapper.appendChild(d);
297
- break;
298
- }
299
- // Widgets — placeholder previews
300
- case 'sidebar': {
301
- const d = document.createElement('div');
302
- d.className = 'jux-canvas-preview-widget';
303
- d.innerHTML = `<div style="font-weight:600;font-size:12px;margin-bottom:4px;">${item.props.title || 'Sidebar'}</div><div style="font-size:11px;color:#888;">• Home<br>• About<br>• Contact</div>`;
304
- wrapper.appendChild(d);
305
- break;
306
- }
307
- case 'calendar': {
308
- const d = document.createElement('div');
309
- d.className = 'jux-canvas-preview-widget';
310
- d.innerHTML = `<div style="font-weight:600;font-size:12px;margin-bottom:4px;">📅 Calendar</div><div style="font-size:10px;color:#888;font-family:monospace;">Mo Tu We Th Fr<br> 1 2 3 4 5<br> 6 7 8 9 10</div>`;
311
- wrapper.appendChild(d);
312
- break;
313
- }
314
- case 'stepper': {
315
- const d = document.createElement('div');
316
- d.className = 'jux-canvas-preview-widget';
317
- d.innerHTML = `<div style="display:flex;align-items:center;gap:4px;font-size:11px;"><span style="width:18px;height:18px;border-radius:50%;background:#3b82f6;color:white;display:flex;align-items:center;justify-content:center;font-size:10px;">1</span>→<span style="width:18px;height:18px;border-radius:50%;background:#e5e7eb;color:#666;display:flex;align-items:center;justify-content:center;font-size:10px;">2</span>→<span style="width:18px;height:18px;border-radius:50%;background:#e5e7eb;color:#666;display:flex;align-items:center;justify-content:center;font-size:10px;">3</span></div>`;
318
- wrapper.appendChild(d);
319
- break;
320
- }
321
- case 'tabs': {
322
- const d = document.createElement('div');
323
- d.className = 'jux-canvas-preview-widget';
324
- d.innerHTML = `<div style="display:flex;gap:0;font-size:11px;border-bottom:1px solid #e5e7eb;"><span style="padding:4px 10px;border-bottom:2px solid #3b82f6;font-weight:600;">Tab 1</span><span style="padding:4px 10px;color:#888;">Tab 2</span><span style="padding:4px 10px;color:#888;">Tab 3</span></div>`;
325
- wrapper.appendChild(d);
326
- break;
327
- }
328
- default: {
329
- const d = document.createElement('div');
330
- d.className = 'jux-canvas-preview-generic';
331
- d.textContent = item.label;
332
- wrapper.appendChild(d);
333
- }
334
- }
335
- return wrapper;
336
- }
337
- // ═══════════════════════════════════════════════════════════
338
- // CANVAS CLASS
339
- // ═══════════════════════════════════════════════════════════
340
- class Canvas {
341
- constructor(id, options = {}) {
342
- this._el = null;
343
- this._items = [];
344
- this._selectedId = null;
345
- this._activeTab = 'design';
346
- this._onChange = null;
347
- this._canvasEl = null;
348
- this._inventoryEl = null;
349
- this._propertiesEl = null;
350
- this._counter = 0;
351
- this._dragOverItemId = null;
352
- this.id = id || generateId('canvas');
353
- this._opts = {
354
- title: options.title || 'Canvas Builder',
355
- width: options.width || '100%',
356
- height: options.height || '100vh',
357
- inventoryWidth: options.inventoryWidth || '220px',
358
- propertiesWidth: options.propertiesWidth || '280px',
359
- ...options,
360
- };
361
- }
362
- // ═══════════════════════════════════════════════════════════
363
- // FLUENT API
364
- // ═══════════════════════════════════════════════════════════
365
- title(val) { this._opts.title = val; return this; }
366
- width(val) { this._opts.width = val; return this; }
367
- height(val) { this._opts.height = val; return this; }
368
- inventoryWidth(val) { this._opts.inventoryWidth = val; return this; }
369
- propertiesWidth(val) { this._opts.propertiesWidth = val; return this; }
370
- style(val) { this._opts.style = val; return this; }
371
- class(val) { this._opts.class = val; return this; }
372
- onChange(fn) {
373
- this._onChange = fn;
374
- return this;
375
- }
376
- // ═══════════════════════════════════════════════════════════
377
- // PAGESTATE COMPAT
378
- // ═══════════════════════════════════════════════════════════
379
- getValue() { return this._selectedId; }
380
- setValue(val) { this._selectItem(val); return this; }
381
- getElement() { return this._el; }
382
- getTitle() { return this._opts.title; }
383
- getWidth() { return this._opts.width; }
384
- getHeight() { return this._opts.height; }
385
- getInventoryWidth() { return this._opts.inventoryWidth; }
386
- getPropertiesWidth() { return this._opts.propertiesWidth; }
387
- getClass() { return this._opts.class || ''; }
388
- getStyle() { return this._opts.style || ''; }
389
- getItems() { return [...this._items]; }
390
- getSelectedItem() { return this._items.find(i => i.id === this._selectedId) || null; }
391
- setTitle(val) { return this.title(val); }
392
- setWidth(val) { return this.width(val); }
393
- setHeight(val) { return this.height(val); }
394
- setInventoryWidth(val) { return this.inventoryWidth(val); }
395
- setPropertiesWidth(val) { return this.propertiesWidth(val); }
396
- setClass(val) { return this.class(val); }
397
- setStyle(val) { return this.style(val); }
398
- get state() { return pageState[this.id]; }
399
- // ═══════════════════════════════════════════════════════════
400
- // ACTIONS
401
- // ═══════════════════════════════════════════════════════════
402
- addItemToCanvas(type, category, beforeId) {
403
- this._counter++;
404
- const itemId = `${type}-${this._counter}`;
405
- const entry = DEFAULT_INVENTORY.find(e => e.type === type);
406
- const props = defaultProps(type);
407
- const item = {
408
- id: itemId,
409
- type,
410
- category,
411
- label: entry?.label || type,
412
- code: defaultCode(type, itemId, props),
413
- props,
414
- };
415
- if (beforeId) {
416
- const idx = this._items.findIndex(i => i.id === beforeId);
417
- if (idx >= 0) {
418
- this._items.splice(idx, 0, item);
419
- }
420
- else {
421
- this._items.push(item);
422
- }
423
- }
424
- else {
425
- this._items.push(item);
426
- }
427
- this._renderCanvasItems();
428
- this._selectItem(itemId);
429
- if (this._onChange)
430
- this._onChange(item);
431
- return this;
432
- }
433
- removeItemFromCanvas(itemId) {
434
- this._items = this._items.filter(i => i.id !== itemId);
435
- if (this._selectedId === itemId) {
436
- this._selectedId = null;
437
- this._renderProperties();
438
- }
439
- this._renderCanvasItems();
440
- if (this._onChange)
441
- this._onChange(null);
442
- return this;
443
- }
444
- updateItemCode(itemId, code) {
445
- const item = this._items.find(i => i.id === itemId);
446
- if (item)
447
- item.code = code;
448
- return this;
449
- }
450
- updateItemProps(itemId, propName, value) {
451
- const item = this._items.find(i => i.id === itemId);
452
- if (item) {
453
- item.props[propName] = value;
454
- item.code = defaultCode(item.type, item.id, item.props);
455
- this._renderCanvasItems();
456
- // Re-select to update properties if needed
457
- if (this._selectedId === itemId) {
458
- this._renderProperties();
459
- }
460
- }
461
- return this;
462
- }
463
- moveItem(itemId, beforeId) {
464
- const idx = this._items.findIndex(i => i.id === itemId);
465
- if (idx < 0)
466
- return this;
467
- const [item] = this._items.splice(idx, 1);
468
- if (beforeId) {
469
- const targetIdx = this._items.findIndex(i => i.id === beforeId);
470
- if (targetIdx >= 0) {
471
- this._items.splice(targetIdx, 0, item);
472
- }
473
- else {
474
- this._items.push(item);
475
- }
476
- }
477
- else {
478
- this._items.push(item);
479
- }
480
- this._renderCanvasItems();
481
- return this;
482
- }
483
- // ═══════════════════════════════════════════════════════════
484
- // RENDER
485
- // ═══════════════════════════════════════════════════════════
486
- render(target) {
487
- this._injectStyles();
488
- this._el = this._buildDOM();
489
- let container = null;
490
- if (target && typeof target === 'object' && 'element' in target)
491
- container = target.element;
492
- else if (target instanceof HTMLElement)
493
- container = target;
494
- else if (typeof target === 'string')
495
- container = document.getElementById(target) || document.querySelector(target);
496
- else
497
- container = document.getElementById('app');
498
- if (container && this._el) {
499
- container.appendChild(this._el);
500
- }
501
- return this;
502
- }
503
- into(target) { return this.render(target); }
504
- destroy() {
505
- pageState.__unregister(this.id);
506
- this._el?.remove();
507
- }
508
- // ═══════════════════════════════════════════════════════════
509
- // INTERNAL — DOM
510
- // ═══════════════════════════════════════════════════════════
511
- _buildDOM() {
512
- const el = document.createElement('div');
513
- el.id = this.id;
514
- el.className = 'jux-canvas' + (this._opts.class ? ' ' + this._opts.class : '');
515
- el.style.cssText = `width:${this._opts.width};height:${this._opts.height};display:flex;${this._opts.style || ''}`;
516
- this._inventoryEl = this._buildInventoryPanel();
517
- el.appendChild(this._inventoryEl);
518
- const canvasWrapper = document.createElement('div');
519
- canvasWrapper.className = 'jux-canvas-center';
520
- canvasWrapper.style.cssText = `flex:1;display:flex;flex-direction:column;overflow:hidden;`;
521
- const header = document.createElement('div');
522
- header.className = 'jux-canvas-header';
523
- header.textContent = this._opts.title;
524
- canvasWrapper.appendChild(header);
525
- this._canvasEl = document.createElement('div');
526
- this._canvasEl.className = 'jux-canvas-area';
527
- this._canvasEl.setAttribute('data-empty', 'true');
528
- const emptyMsg = document.createElement('div');
529
- emptyMsg.className = 'jux-canvas-empty';
530
- emptyMsg.textContent = 'Drag components from the inventory or click to add';
531
- this._canvasEl.appendChild(emptyMsg);
532
- // Drop from inventory onto empty canvas
533
- this._canvasEl.addEventListener('dragover', (e) => {
534
- e.preventDefault();
535
- this._canvasEl.classList.add('jux-canvas-area--dragover');
536
- });
537
- this._canvasEl.addEventListener('dragleave', (e) => {
538
- // Only remove if leaving the canvas itself
539
- if (e.relatedTarget && this._canvasEl.contains(e.relatedTarget))
540
- return;
541
- this._canvasEl.classList.remove('jux-canvas-area--dragover');
542
- });
543
- this._canvasEl.addEventListener('drop', (e) => {
544
- e.preventDefault();
545
- this._canvasEl.classList.remove('jux-canvas-area--dragover');
546
- // New item from inventory
547
- const type = e.dataTransfer?.getData('text/plain');
548
- const category = e.dataTransfer?.getData('application/x-category');
549
- const moveId = e.dataTransfer?.getData('application/x-canvas-move');
550
- if (moveId) {
551
- this.moveItem(moveId, null);
552
- }
553
- else if (type && category) {
554
- this.addItemToCanvas(type, category);
555
- }
556
- });
557
- canvasWrapper.appendChild(this._canvasEl);
558
- el.appendChild(canvasWrapper);
559
- this._propertiesEl = this._buildPropertiesPanel();
560
- el.appendChild(this._propertiesEl);
561
- return el;
562
- }
563
- _buildInventoryPanel() {
564
- const panel = document.createElement('div');
565
- panel.className = 'jux-canvas-inventory';
566
- panel.style.width = this._opts.inventoryWidth;
567
- const panelTitle = document.createElement('div');
568
- panelTitle.className = 'jux-canvas-panel-title';
569
- panelTitle.textContent = 'Inventory';
570
- panel.appendChild(panelTitle);
571
- const categories = [
572
- { key: 'shapes', label: 'Shapes', icon: '◻' },
573
- { key: 'widgets', label: 'Widgets', icon: '⚙' },
574
- { key: 'components', label: 'Components', icon: '◈' },
575
- ];
576
- for (const cat of categories) {
577
- const section = document.createElement('div');
578
- section.className = 'jux-canvas-inv-section';
579
- const sectionLabel = document.createElement('div');
580
- sectionLabel.className = 'jux-canvas-inv-label';
581
- sectionLabel.textContent = `${cat.icon} ${cat.label.toUpperCase()}`;
582
- section.appendChild(sectionLabel);
583
- const entries = DEFAULT_INVENTORY.filter(e => e.category === cat.key);
584
- for (const entry of entries) {
585
- const item = document.createElement('div');
586
- item.className = 'jux-canvas-inv-item';
587
- item.textContent = entry.label;
588
- item.setAttribute('draggable', 'true');
589
- item.addEventListener('dragstart', (e) => {
590
- e.dataTransfer?.setData('text/plain', entry.type);
591
- e.dataTransfer?.setData('application/x-category', entry.category);
592
- item.classList.add('jux-canvas-inv-item--dragging');
593
- });
594
- item.addEventListener('dragend', () => {
595
- item.classList.remove('jux-canvas-inv-item--dragging');
596
- });
597
- item.addEventListener('click', () => {
598
- this.addItemToCanvas(entry.type, entry.category);
599
- });
600
- section.appendChild(item);
601
- }
602
- panel.appendChild(section);
603
- }
604
- return panel;
605
- }
606
- _buildPropertiesPanel() {
607
- const panel = document.createElement('div');
608
- panel.className = 'jux-canvas-properties';
609
- panel.style.width = this._opts.propertiesWidth;
610
- const panelTitle = document.createElement('div');
611
- panelTitle.className = 'jux-canvas-panel-title';
612
- panelTitle.textContent = 'Properties';
613
- panel.appendChild(panelTitle);
614
- const content = document.createElement('div');
615
- content.className = 'jux-canvas-props-content';
616
- content.innerHTML = '<div class="jux-canvas-props-empty">Select a component</div>';
617
- panel.appendChild(content);
618
- return panel;
619
- }
620
- // ── Canvas Items (flow-based) ──
621
- _renderCanvasItems() {
622
- if (!this._canvasEl)
623
- return;
624
- this._canvasEl.innerHTML = '';
625
- if (this._items.length === 0) {
626
- this._canvasEl.setAttribute('data-empty', 'true');
627
- const emptyMsg = document.createElement('div');
628
- emptyMsg.className = 'jux-canvas-empty';
629
- emptyMsg.textContent = 'Drag components from the inventory or click to add';
630
- this._canvasEl.appendChild(emptyMsg);
631
- return;
632
- }
633
- this._canvasEl.removeAttribute('data-empty');
634
- for (const item of this._items) {
635
- const el = document.createElement('div');
636
- el.className = 'jux-canvas-item' + (item.id === this._selectedId ? ' jux-canvas-item--selected' : '');
637
- el.setAttribute('data-item-id', item.id);
638
- el.setAttribute('draggable', 'true');
639
- // Category badge (small, top-left corner)
640
- const badge = document.createElement('span');
641
- badge.className = `jux-canvas-item-badge jux-canvas-item-badge--${item.category}`;
642
- badge.textContent = item.category === 'shapes' ? '◻' : item.category === 'widgets' ? '⚙' : '◈';
643
- el.appendChild(badge);
644
- // Remove button
645
- const removeBtn = document.createElement('span');
646
- removeBtn.className = 'jux-canvas-item-remove';
647
- removeBtn.textContent = '×';
648
- removeBtn.addEventListener('click', (e) => {
649
- e.stopPropagation();
650
- this.removeItemFromCanvas(item.id);
651
- });
652
- el.appendChild(removeBtn);
653
- // Live preview
654
- const preview = renderPreview(item);
655
- el.appendChild(preview);
656
- // Select on click
657
- el.addEventListener('click', () => this._selectItem(item.id));
658
- // Drag to reorder
659
- el.addEventListener('dragstart', (e) => {
660
- e.dataTransfer?.setData('application/x-canvas-move', item.id);
661
- e.dataTransfer.effectAllowed = 'move';
662
- el.classList.add('jux-canvas-item--dragging');
663
- });
664
- el.addEventListener('dragend', () => {
665
- el.classList.remove('jux-canvas-item--dragging');
666
- // Clear all drop indicators
667
- this._canvasEl?.querySelectorAll('.jux-canvas-item--drop-before').forEach(n => n.classList.remove('jux-canvas-item--drop-before'));
668
- });
669
- el.addEventListener('dragover', (e) => {
670
- e.preventDefault();
671
- e.stopPropagation();
672
- // Show drop indicator
673
- this._canvasEl?.querySelectorAll('.jux-canvas-item--drop-before').forEach(n => n.classList.remove('jux-canvas-item--drop-before'));
674
- el.classList.add('jux-canvas-item--drop-before');
675
- });
676
- el.addEventListener('dragleave', () => {
677
- el.classList.remove('jux-canvas-item--drop-before');
678
- });
679
- el.addEventListener('drop', (e) => {
680
- e.preventDefault();
681
- e.stopPropagation();
682
- el.classList.remove('jux-canvas-item--drop-before');
683
- const moveId = e.dataTransfer?.getData('application/x-canvas-move');
684
- const type = e.dataTransfer?.getData('text/plain');
685
- const category = e.dataTransfer?.getData('application/x-category');
686
- if (moveId && moveId !== item.id) {
687
- this.moveItem(moveId, item.id);
688
- }
689
- else if (type && category) {
690
- this.addItemToCanvas(type, category, item.id);
691
- }
692
- });
693
- this._canvasEl.appendChild(el);
694
- }
695
- }
696
- // ── Selection (preserves active tab) ──
697
- _selectItem(itemId) {
698
- const changed = this._selectedId !== itemId;
699
- this._selectedId = itemId;
700
- // Do NOT reset _activeTab — preserve whatever tab the user was on
701
- if (this._canvasEl) {
702
- this._canvasEl.querySelectorAll('.jux-canvas-item').forEach(el => {
703
- el.classList.toggle('jux-canvas-item--selected', el.getAttribute('data-item-id') === itemId);
704
- });
705
- }
706
- this._renderProperties();
707
- if (this._onChange) {
708
- this._onChange(this._items.find(i => i.id === itemId) || null);
709
- }
710
- }
711
- _renderProperties() {
712
- if (!this._propertiesEl)
713
- return;
714
- const content = this._propertiesEl.querySelector('.jux-canvas-props-content');
715
- if (!content)
716
- return;
717
- const item = this._items.find(i => i.id === this._selectedId);
718
- if (!item) {
719
- content.innerHTML = '<div class="jux-canvas-props-empty">Select a component</div>';
720
- return;
721
- }
722
- content.innerHTML = '';
723
- // Tab Bar
724
- const tabBar = document.createElement('div');
725
- tabBar.className = 'jux-canvas-tab-bar';
726
- const tabs = [
727
- { key: 'design', label: 'Design' },
728
- { key: 'code', label: 'Code' },
729
- { key: 'state', label: 'State' },
730
- ];
731
- for (const tab of tabs) {
732
- const btn = document.createElement('button');
733
- btn.className = 'jux-canvas-tab' + (tab.key === this._activeTab ? ' jux-canvas-tab--active' : '');
734
- btn.textContent = tab.label;
735
- btn.addEventListener('click', () => {
736
- this._activeTab = tab.key;
737
- this._renderProperties();
738
- });
739
- tabBar.appendChild(btn);
740
- }
741
- content.appendChild(tabBar);
742
- const tabContent = document.createElement('div');
743
- tabContent.className = 'jux-canvas-tab-content';
744
- if (this._activeTab === 'design') {
745
- this._renderDesignTab(tabContent, item);
746
- }
747
- else if (this._activeTab === 'code') {
748
- this._renderCodeTab(tabContent, item);
749
- }
750
- else {
751
- this._renderStateTab(tabContent, item);
752
- }
753
- content.appendChild(tabContent);
754
- }
755
- _renderDesignTab(container, item) {
756
- const header = document.createElement('div');
757
- header.className = 'jux-canvas-prop-header';
758
- header.innerHTML = `<strong>Component Code</strong>`;
759
- container.appendChild(header);
760
- // Show current code as editable single-line
761
- const codeInput = document.createElement('textarea');
762
- codeInput.className = 'jux-canvas-code-editor jux-canvas-code-editor--small';
763
- codeInput.value = item.code;
764
- codeInput.spellcheck = false;
765
- codeInput.rows = 2;
766
- codeInput.addEventListener('change', () => {
767
- this.updateItemCode(item.id, codeInput.value);
768
- });
769
- container.appendChild(codeInput);
770
- // Editable props
771
- const propsHeader = document.createElement('div');
772
- propsHeader.className = 'jux-canvas-prop-header';
773
- propsHeader.style.marginTop = '12px';
774
- propsHeader.innerHTML = `<strong>Properties</strong>`;
775
- container.appendChild(propsHeader);
776
- const propDefs = this._getPropertiesForType(item.type);
777
- for (const prop of propDefs) {
778
- const row = document.createElement('div');
779
- row.className = 'jux-canvas-prop-row';
780
- const label = document.createElement('label');
781
- label.className = 'jux-canvas-prop-label';
782
- label.textContent = prop.name;
783
- row.appendChild(label);
784
- const input = document.createElement('input');
785
- input.className = 'jux-canvas-prop-input';
786
- input.type = prop.inputType || 'text';
787
- input.value = item.props[prop.name] || prop.defaultValue || '';
788
- input.placeholder = prop.placeholder || '';
789
- input.addEventListener('change', () => {
790
- this.updateItemProps(item.id, prop.name, input.value);
791
- });
792
- row.appendChild(input);
793
- container.appendChild(row);
794
- }
795
- }
796
- _renderCodeTab(container, item) {
797
- const label = document.createElement('div');
798
- label.className = 'jux-canvas-code-label';
799
- label.textContent = 'Component Code';
800
- container.appendChild(label);
801
- const textarea = document.createElement('textarea');
802
- textarea.className = 'jux-canvas-code-editor';
803
- textarea.value = item.code;
804
- textarea.spellcheck = false;
805
- textarea.addEventListener('change', () => {
806
- this.updateItemCode(item.id, textarea.value);
807
- });
808
- container.appendChild(textarea);
809
- const previewLabel = document.createElement('div');
810
- previewLabel.className = 'jux-canvas-code-label';
811
- previewLabel.style.marginTop = '12px';
812
- previewLabel.textContent = 'Full Page Code';
813
- container.appendChild(previewLabel);
814
- const preview = document.createElement('pre');
815
- preview.className = 'jux-canvas-code-preview';
816
- preview.textContent = this._generateFullCode();
817
- container.appendChild(preview);
818
- }
819
- _renderStateTab(container, item) {
820
- const header = document.createElement('div');
821
- header.className = 'jux-canvas-prop-header';
822
- header.innerHTML = `<strong>PageState</strong><span class="jux-canvas-prop-id">pageState['${item.id}']</span>`;
823
- container.appendChild(header);
824
- const stateProps = [
825
- { prop: 'value', desc: 'Current value of the component' },
826
- { prop: 'content', desc: 'Text content' },
827
- { prop: 'visible', desc: 'Visibility toggle' },
828
- { prop: 'disabled', desc: 'Disabled state' },
829
- { prop: 'click', desc: 'Click event flag' },
830
- { prop: 'change', desc: 'Change event flag' },
831
- { prop: 'focus', desc: 'Focus event flag' },
832
- { prop: 'hover', desc: 'Hover state flag' },
833
- ];
834
- for (const sp of stateProps) {
835
- const row = document.createElement('div');
836
- row.className = 'jux-canvas-state-row';
837
- row.innerHTML = `<code>pageState['${item.id}'].${sp.prop}</code><span class="jux-canvas-state-desc">${sp.desc}</span>`;
838
- container.appendChild(row);
839
- }
840
- const exLabel = document.createElement('div');
841
- exLabel.className = 'jux-canvas-code-label';
842
- exLabel.style.marginTop = '12px';
843
- exLabel.textContent = 'Example Interaction';
844
- container.appendChild(exLabel);
845
- const example = document.createElement('pre');
846
- example.className = 'jux-canvas-code-preview';
847
- example.textContent = this._generateInteractionExample(item);
848
- container.appendChild(example);
849
- }
850
- _getPropertiesForType(type) {
851
- const common = [
852
- { name: 'class', placeholder: 'CSS class' },
853
- { name: 'style', placeholder: 'CSS inline style' },
854
- ];
855
- switch (type) {
856
- case 'button':
857
- return [{ name: 'content', placeholder: 'Button text' }, { name: 'variant', placeholder: 'default | outline | ghost' }, ...common];
858
- case 'input':
859
- return [{ name: 'label', placeholder: 'Field label' }, { name: 'placeholder', placeholder: 'Placeholder' }, { name: 'value', placeholder: 'Default value' }, ...common];
860
- case 'select':
861
- return [{ name: 'label', placeholder: 'Field label' }, { name: 'placeholder', placeholder: 'Placeholder' }, ...common];
862
- case 'checkbox':
863
- return [{ name: 'label', placeholder: 'Checkbox label' }, ...common];
864
- case 'radio':
865
- return [{ name: 'label', placeholder: 'Group label' }, ...common];
866
- case 'h1':
867
- case 'p':
868
- case 'span':
869
- case 'tag':
870
- return [{ name: 'content', placeholder: 'Text content' }, ...common];
871
- case 'link':
872
- return [{ name: 'content', placeholder: 'Link text' }, { name: 'href', placeholder: 'URL' }, ...common];
873
- case 'c':
874
- return [{ name: 'width', placeholder: '100%' }, { name: 'height', placeholder: 'auto' }, { name: 'padding', placeholder: '16px' }, ...common];
875
- case 'flex':
876
- case 'flexRow':
877
- case 'flexCol':
878
- return [{ name: 'gap', placeholder: '8px' }, ...common];
879
- case 'sidebar':
880
- return [{ name: 'title', placeholder: 'App Name' }, { name: 'width', placeholder: '260px' }, ...common];
881
- default:
882
- return common;
883
- }
884
- }
885
- _generateFullCode() {
886
- const lines = [
887
- "import { jux, pageState, c, g, flex } from 'juxscript';",
888
- '',
889
- ];
890
- for (const item of this._items) {
891
- lines.push(`const ${item.id.replace(/-/g, '_')} = ${item.code};`);
892
- }
893
- return lines.join('\n');
894
- }
895
- _generateInteractionExample(item) {
896
- const others = this._items.filter(i => i.id !== item.id);
897
- if (others.length === 0) {
898
- return `if (pageState['${item.id}'].click) {\n console.log('${item.label} clicked!');\n}`;
899
- }
900
- const other = others[0];
901
- return `if (pageState['${item.id}'].click) {\n pageState['${other.id}'].value = 'Updated!';\n}`;
902
- }
903
- // ═══════════════════════════════════════════════════════════
904
- // STYLES
905
- // ═══════════════════════════════════════════════════════════
906
- _injectStyles() {
907
- if (canvasStylesInjected)
908
- return;
909
- canvasStylesInjected = true;
910
- const s = document.createElement('style');
911
- s.id = 'jux-canvas-styles';
912
- s.textContent = `
913
- .jux-canvas{font-family:var(--font-sans,system-ui,sans-serif);background:hsl(var(--background,0 0% 100%));color:hsl(var(--foreground,222.2 84% 4.9%));border:1px solid hsl(var(--border,214.3 31.8% 91.4%));border-radius:var(--radius,8px);overflow:hidden}
914
- .jux-canvas-header{padding:10px 16px;font-weight:600;font-size:14px;border-bottom:1px solid hsl(var(--border,214.3 31.8% 91.4%));background:hsl(var(--card,0 0% 100%))}
915
- .jux-canvas-center{min-width:0}
916
- .jux-canvas-area{flex:1;position:relative;overflow:auto;background:hsl(var(--muted,210 40% 96.1%));background-image:radial-gradient(circle,hsl(var(--border,214.3 31.8% 91.4%)) 1px,transparent 1px);background-size:20px 20px;min-height:200px;transition:background .15s;display:flex;flex-direction:column;gap:8px;padding:16px;align-items:flex-start}
917
- .jux-canvas-area--dragover{background-color:hsl(var(--accent,210 40% 93%))}
918
- .jux-canvas-area[data-empty]{display:flex;align-items:center;justify-content:center}
919
- .jux-canvas-empty{color:hsl(var(--muted-foreground,215.4 16.3% 46.9%));font-size:13px;text-align:center;padding:40px}
920
- .jux-canvas-item{position:relative;display:block;background:hsl(var(--card,0 0% 100%));border:2px solid transparent;border-radius:var(--radius,8px);cursor:pointer;box-shadow:0 1px 3px rgba(0,0,0,.06);transition:box-shadow .15s,border-color .15s;user-select:none;min-width:120px;max-width:100%}
921
- .jux-canvas-item:hover{box-shadow:0 2px 8px rgba(0,0,0,.1);border-color:hsl(var(--border,214.3 31.8% 91.4%))}
922
- .jux-canvas-item--selected{border-color:hsl(var(--primary,222.2 47.4% 11.2%));box-shadow:0 0 0 3px hsl(var(--ring,222.2 84% 4.9%)/0.12)}
923
- .jux-canvas-item--dragging{opacity:.4}
924
- .jux-canvas-item--drop-before{border-top:3px solid hsl(var(--primary,222.2 47.4% 11.2%))!important}
925
- .jux-canvas-item-badge{position:absolute;top:4px;left:4px;width:16px;height:16px;border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:9px;z-index:1;pointer-events:none}
926
- .jux-canvas-item-badge--shapes{background:hsl(217 91% 93%);color:hsl(217 91% 40%)}
927
- .jux-canvas-item-badge--widgets{background:hsl(142 71% 90%);color:hsl(142 71% 35%)}
928
- .jux-canvas-item-badge--components{background:hsl(25 95% 91%);color:hsl(25 95% 40%)}
929
- .jux-canvas-item-remove{position:absolute;top:4px;right:4px;width:18px;height:18px;display:flex;align-items:center;justify-content:center;font-size:14px;color:hsl(var(--muted-foreground,215.4 16.3% 46.9%));cursor:pointer;border-radius:4px;z-index:1;opacity:0;transition:opacity .15s}
930
- .jux-canvas-item:hover .jux-canvas-item-remove{opacity:1}
931
- .jux-canvas-item-remove:hover{background:hsl(var(--destructive,0 84.2% 60.2%));color:white}
932
-
933
- /* Live preview wrapper */
934
- .jux-canvas-preview{padding:10px 12px;pointer-events:none}
935
- .jux-canvas-item--selected .jux-canvas-preview{pointer-events:auto}
936
-
937
- /* Preview component styles */
938
- .jux-canvas-preview-btn{padding:6px 16px;font-size:12px;font-weight:500;border:1px solid hsl(var(--border,214.3 31.8% 91.4%));border-radius:var(--radius,6px);background:hsl(var(--primary,222.2 47.4% 11.2%));color:hsl(var(--primary-foreground,210 40% 98%));cursor:pointer;font-family:inherit}
939
- .jux-canvas-preview-field{display:flex;flex-direction:column;gap:4px;min-width:160px}
940
- .jux-canvas-preview-inline{flex-direction:row;align-items:center;gap:6px}
941
- .jux-canvas-preview-label{font-size:11px;font-weight:500;color:hsl(var(--foreground,222.2 84% 4.9%))}
942
- .jux-canvas-preview-input,.jux-canvas-preview-select{padding:5px 8px;font-size:12px;border:1px solid hsl(var(--border,214.3 31.8% 91.4%));border-radius:var(--radius,6px);background:hsl(var(--background,0 0% 100%));font-family:inherit;color:hsl(var(--foreground,222.2 84% 4.9%))}
943
- .jux-canvas-preview-checkbox{width:14px;height:14px}
944
- .jux-canvas-preview-radio-group{display:flex;flex-direction:column;gap:3px}
945
- .jux-canvas-preview-radio-row{display:flex;align-items:center;gap:5px;font-size:11px;cursor:pointer}
946
- .jux-canvas-preview-link{font-size:12px;color:hsl(var(--primary,222.2 47.4% 11.2%));text-decoration:underline;cursor:pointer}
947
- .jux-canvas-preview-heading{font-size:16px;font-weight:700;margin:0}
948
- .jux-canvas-preview-text{font-size:12px;margin:0;color:hsl(var(--foreground,222.2 84% 4.9%))}
949
- .jux-canvas-preview-tag{font-size:12px;padding:4px 8px;background:hsl(var(--muted,210 40% 96.1%));border-radius:4px;display:inline-block}
950
- .jux-canvas-preview-nav{display:flex;flex-direction:column;gap:2px}
951
- .jux-canvas-preview-nav-item{font-size:11px;color:hsl(var(--foreground,222.2 84% 4.9%));text-decoration:none;padding:3px 8px;border-radius:4px}
952
- .jux-canvas-preview-nav-item:hover{background:hsl(var(--accent,210 40% 93%))}
953
- .jux-canvas-preview-list{margin:0;padding-left:16px;font-size:11px;list-style:disc}
954
- .jux-canvas-preview-list li{padding:1px 0}
955
- .jux-canvas-preview-table{font-size:11px;border-collapse:collapse;width:100%}
956
- .jux-canvas-preview-table th,.jux-canvas-preview-table td{padding:3px 8px;border:1px solid hsl(var(--border,214.3 31.8% 91.4%));text-align:left}
957
- .jux-canvas-preview-table th{background:hsl(var(--muted,210 40% 96.1%));font-weight:600}
958
- .jux-canvas-preview-container{border:1px dashed hsl(var(--border,214.3 31.8% 91.4%));border-radius:6px;display:flex;align-items:center;justify-content:center;color:hsl(var(--muted-foreground,215.4 16.3% 46.9%));font-size:11px}
959
- .jux-canvas-preview-flex{min-height:30px}
960
- .jux-canvas-preview-flex-child{padding:4px 12px;background:hsl(var(--muted,210 40% 96.1%));border:1px solid hsl(var(--border,214.3 31.8% 91.4%));border-radius:4px;font-size:11px;text-align:center}
961
- .jux-canvas-preview-group{border:1px dashed hsl(var(--border,214.3 31.8% 91.4%));padding:8px;border-radius:6px;font-size:11px;color:hsl(var(--muted-foreground,215.4 16.3% 46.9%));text-align:center}
962
- .jux-canvas-preview-widget{padding:6px;background:hsl(var(--muted,210 40% 96.1%));border-radius:6px;border:1px solid hsl(var(--border,214.3 31.8% 91.4%))}
963
- .jux-canvas-preview-generic{font-size:11px;color:hsl(var(--muted-foreground,215.4 16.3% 46.9%));padding:8px;text-align:center}
964
-
965
- /* Panels */
966
- .jux-canvas-inventory,.jux-canvas-properties{border-right:1px solid hsl(var(--border,214.3 31.8% 91.4%));background:hsl(var(--card,0 0% 100%));overflow-y:auto;flex-shrink:0}
967
- .jux-canvas-properties{border-right:none;border-left:1px solid hsl(var(--border,214.3 31.8% 91.4%))}
968
- .jux-canvas-panel-title{padding:10px 12px;font-weight:600;font-size:12px;text-transform:uppercase;letter-spacing:.05em;color:hsl(var(--muted-foreground,215.4 16.3% 46.9%));border-bottom:1px solid hsl(var(--border,214.3 31.8% 91.4%))}
969
- .jux-canvas-inv-section{padding:4px 0}
970
- .jux-canvas-inv-label{padding:8px 12px 4px;font-size:10px;font-weight:700;color:hsl(var(--muted-foreground,215.4 16.3% 46.9%));letter-spacing:.06em}
971
- .jux-canvas-inv-item{padding:5px 12px 5px 20px;font-size:12px;cursor:pointer;transition:background .12s;user-select:none;white-space:nowrap}
972
- .jux-canvas-inv-item:hover{background:hsl(var(--accent,210 40% 93%))}
973
- .jux-canvas-inv-item--dragging{opacity:.5}
974
- .jux-canvas-props-content{padding:0}
975
- .jux-canvas-props-empty{color:hsl(var(--muted-foreground,215.4 16.3% 46.9%));font-size:12px;padding:20px;text-align:center}
976
- .jux-canvas-tab-bar{display:flex;border-bottom:1px solid hsl(var(--border,214.3 31.8% 91.4%))}
977
- .jux-canvas-tab{flex:1;padding:7px 0;text-align:center;font-size:11px;font-weight:500;cursor:pointer;border:none;background:none;color:hsl(var(--muted-foreground,215.4 16.3% 46.9%));transition:color .12s,border-color .12s;border-bottom:2px solid transparent}
978
- .jux-canvas-tab:hover{color:hsl(var(--foreground,222.2 84% 4.9%))}
979
- .jux-canvas-tab--active{color:hsl(var(--foreground,222.2 84% 4.9%));border-bottom-color:hsl(var(--primary,222.2 47.4% 11.2%))}
980
- .jux-canvas-tab-content{padding:10px}
981
- .jux-canvas-prop-header{margin-bottom:8px;font-size:12px;display:flex;align-items:center;gap:8px}
982
- .jux-canvas-prop-id{font-size:10px;color:hsl(var(--muted-foreground,215.4 16.3% 46.9%));font-family:monospace;background:hsl(var(--muted,210 40% 96.1%));padding:1px 5px;border-radius:3px}
983
- .jux-canvas-prop-row{display:flex;align-items:center;gap:6px;margin-bottom:6px}
984
- .jux-canvas-prop-label{font-size:11px;font-weight:500;width:70px;flex-shrink:0;color:hsl(var(--muted-foreground,215.4 16.3% 46.9%))}
985
- .jux-canvas-prop-input{flex:1;padding:4px 6px;font-size:11px;border:1px solid hsl(var(--border,214.3 31.8% 91.4%));border-radius:4px;background:hsl(var(--background,0 0% 100%));color:hsl(var(--foreground,222.2 84% 4.9%));font-family:inherit}
986
- .jux-canvas-prop-input:focus{outline:none;border-color:hsl(var(--ring,222.2 84% 4.9%))}
987
- .jux-canvas-code-label{font-size:11px;font-weight:600;color:hsl(var(--muted-foreground,215.4 16.3% 46.9%));margin-bottom:4px}
988
- .jux-canvas-code-editor{width:100%;min-height:80px;padding:6px;font-size:11px;font-family:'Fira Code',monospace;border:1px solid hsl(var(--border,214.3 31.8% 91.4%));border-radius:4px;background:hsl(var(--muted,210 40% 96.1%));color:hsl(var(--foreground,222.2 84% 4.9%));resize:vertical;box-sizing:border-box}
989
- .jux-canvas-code-editor--small{min-height:40px}
990
- .jux-canvas-code-preview{font-size:10px;font-family:'Fira Code',monospace;background:hsl(var(--muted,210 40% 96.1%));padding:8px;border-radius:4px;overflow-x:auto;white-space:pre;margin:0;color:hsl(var(--foreground,222.2 84% 4.9%));border:1px solid hsl(var(--border,214.3 31.8% 91.4%))}
991
- .jux-canvas-state-row{padding:4px 0;font-size:11px;display:flex;flex-direction:column;gap:1px;border-bottom:1px solid hsl(var(--border,214.3 31.8% 91.4%)/0.5)}
992
- .jux-canvas-state-row code{font-family:'Fira Code',monospace;font-size:10px;color:hsl(var(--primary,222.2 47.4% 11.2%))}
993
- .jux-canvas-state-desc{font-size:10px;color:hsl(var(--muted-foreground,215.4 16.3% 46.9%))}
994
- `;
995
- document.head.appendChild(s);
996
- }
997
- }
998
- // ═══════════════════════════════════════════════════════════
999
- // FACTORY
1000
- // ═══════════════════════════════════════════════════════════
1001
- export function canvas(id, options = {}) {
1002
- const cv = new Canvas(id, options);
1003
- pageState.__register(cv);
1004
- return cv;
1005
- }
1006
- export { Canvas };
1007
- export default canvas;
1
+ // Re-export from canonical location
2
+ export { canvas, Canvas } from '../components/widgets/canvas.js';
3
+ export { canvas as default } from '../components/widgets/canvas.js';
1008
4
  //# sourceMappingURL=canvas.js.map