juxscript 1.1.401 → 1.1.403

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 (157) hide show
  1. package/dist/components/gateway.d.ts +8 -0
  2. package/dist/components/gateway.d.ts.map +1 -1
  3. package/dist/components/gateway.js +15 -0
  4. package/dist/components/gateway.js.map +1 -1
  5. package/dist/{components → containers}/c.d.ts.map +1 -1
  6. package/dist/{components → containers}/c.js.map +1 -1
  7. package/dist/containers/flex.d.ts +49 -0
  8. package/dist/containers/flex.d.ts.map +1 -0
  9. package/dist/containers/flex.js +122 -0
  10. package/dist/containers/flex.js.map +1 -0
  11. package/dist/{components → containers}/g.d.ts.map +1 -1
  12. package/dist/{components → containers}/g.js.map +1 -1
  13. package/dist/index.d.ts +18 -4
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +18 -4
  16. package/dist/index.js.map +1 -1
  17. package/dist/services/dbService.d.ts +65 -0
  18. package/dist/services/dbService.d.ts.map +1 -0
  19. package/dist/services/dbService.js +131 -0
  20. package/dist/services/dbService.js.map +1 -0
  21. package/dist/services/emailService.d.ts +71 -0
  22. package/dist/services/emailService.d.ts.map +1 -0
  23. package/dist/services/emailService.js +118 -0
  24. package/dist/services/emailService.js.map +1 -0
  25. package/dist/services/rdsService.d.ts +64 -0
  26. package/dist/services/rdsService.d.ts.map +1 -0
  27. package/dist/services/rdsService.js +130 -0
  28. package/dist/services/rdsService.js.map +1 -0
  29. package/dist/services/storageService.d.ts +84 -0
  30. package/dist/services/storageService.d.ts.map +1 -0
  31. package/dist/services/storageService.js +188 -0
  32. package/dist/services/storageService.js.map +1 -0
  33. package/dist/widgets/canvas.d.ts +95 -0
  34. package/dist/widgets/canvas.d.ts.map +1 -0
  35. package/dist/widgets/canvas.js +1008 -0
  36. package/dist/widgets/canvas.js.map +1 -0
  37. package/dist/widgets/stepper.d.ts +83 -0
  38. package/dist/widgets/stepper.d.ts.map +1 -0
  39. package/dist/widgets/stepper.js +387 -0
  40. package/dist/widgets/stepper.js.map +1 -0
  41. package/package.json +1 -1
  42. package/dist/components/barChart.d.ts +0 -119
  43. package/dist/components/barChart.d.ts.map +0 -1
  44. package/dist/components/barChart.js +0 -555
  45. package/dist/components/barChart.js.map +0 -1
  46. package/dist/components/container.d.ts +0 -58
  47. package/dist/components/container.d.ts.map +0 -1
  48. package/dist/components/container.js +0 -152
  49. package/dist/components/container.js.map +0 -1
  50. package/dist/components/grid.d.ts +0 -58
  51. package/dist/components/grid.d.ts.map +0 -1
  52. package/dist/components/grid.js +0 -128
  53. package/dist/components/grid.js.map +0 -1
  54. package/dist/components/lineChart.d.ts +0 -104
  55. package/dist/components/lineChart.d.ts.map +0 -1
  56. package/dist/components/lineChart.js +0 -466
  57. package/dist/components/lineChart.js.map +0 -1
  58. package/dist/components/pieChart.d.ts +0 -93
  59. package/dist/components/pieChart.d.ts.map +0 -1
  60. package/dist/components/pieChart.js +0 -397
  61. package/dist/components/pieChart.js.map +0 -1
  62. package/dist/components/stack.d.ts +0 -42
  63. package/dist/components/stack.d.ts.map +0 -1
  64. package/dist/components/stack.js +0 -109
  65. package/dist/components/stack.js.map +0 -1
  66. package/dist/devtools/devtools.d.ts +0 -3
  67. package/dist/devtools/devtools.d.ts.map +0 -1
  68. package/dist/devtools/devtools.js +0 -182
  69. package/dist/devtools/devtools.js.map +0 -1
  70. package/dist/primitives/button.d.ts +0 -53
  71. package/dist/primitives/button.d.ts.map +0 -1
  72. package/dist/primitives/button.js +0 -170
  73. package/dist/primitives/button.js.map +0 -1
  74. package/dist/primitives/c.d.ts +0 -53
  75. package/dist/primitives/c.d.ts.map +0 -1
  76. package/dist/primitives/c.js +0 -127
  77. package/dist/primitives/c.js.map +0 -1
  78. package/dist/primitives/checkbox.d.ts +0 -92
  79. package/dist/primitives/checkbox.d.ts.map +0 -1
  80. package/dist/primitives/checkbox.js +0 -217
  81. package/dist/primitives/checkbox.js.map +0 -1
  82. package/dist/primitives/data.d.ts +0 -58
  83. package/dist/primitives/data.d.ts.map +0 -1
  84. package/dist/primitives/data.js +0 -131
  85. package/dist/primitives/data.js.map +0 -1
  86. package/dist/primitives/grid.d.ts +0 -58
  87. package/dist/primitives/grid.d.ts.map +0 -1
  88. package/dist/primitives/grid.js +0 -128
  89. package/dist/primitives/grid.js.map +0 -1
  90. package/dist/primitives/include.d.ts +0 -86
  91. package/dist/primitives/include.d.ts.map +0 -1
  92. package/dist/primitives/include.js +0 -239
  93. package/dist/primitives/include.js.map +0 -1
  94. package/dist/primitives/indexDb.d.ts +0 -80
  95. package/dist/primitives/indexDb.d.ts.map +0 -1
  96. package/dist/primitives/indexDb.js +0 -253
  97. package/dist/primitives/indexDb.js.map +0 -1
  98. package/dist/primitives/input.d.ts +0 -88
  99. package/dist/primitives/input.d.ts.map +0 -1
  100. package/dist/primitives/input.js +0 -216
  101. package/dist/primitives/input.js.map +0 -1
  102. package/dist/primitives/link.d.ts +0 -51
  103. package/dist/primitives/link.d.ts.map +0 -1
  104. package/dist/primitives/link.js +0 -178
  105. package/dist/primitives/link.js.map +0 -1
  106. package/dist/primitives/list.d.ts +0 -66
  107. package/dist/primitives/list.d.ts.map +0 -1
  108. package/dist/primitives/list.js +0 -233
  109. package/dist/primitives/list.js.map +0 -1
  110. package/dist/primitives/nav.d.ts +0 -64
  111. package/dist/primitives/nav.d.ts.map +0 -1
  112. package/dist/primitives/nav.js +0 -236
  113. package/dist/primitives/nav.js.map +0 -1
  114. package/dist/primitives/radio.d.ts +0 -58
  115. package/dist/primitives/radio.d.ts.map +0 -1
  116. package/dist/primitives/radio.js +0 -135
  117. package/dist/primitives/radio.js.map +0 -1
  118. package/dist/primitives/routes.d.ts +0 -15
  119. package/dist/primitives/routes.d.ts.map +0 -1
  120. package/dist/primitives/routes.js +0 -34
  121. package/dist/primitives/routes.js.map +0 -1
  122. package/dist/primitives/select.d.ts +0 -67
  123. package/dist/primitives/select.d.ts.map +0 -1
  124. package/dist/primitives/select.js +0 -160
  125. package/dist/primitives/select.js.map +0 -1
  126. package/dist/primitives/style.d.ts +0 -27
  127. package/dist/primitives/style.d.ts.map +0 -1
  128. package/dist/primitives/style.js +0 -53
  129. package/dist/primitives/style.js.map +0 -1
  130. package/dist/primitives/table.d.ts +0 -83
  131. package/dist/primitives/table.d.ts.map +0 -1
  132. package/dist/primitives/table.js +0 -264
  133. package/dist/primitives/table.js.map +0 -1
  134. package/dist/primitives/tabs.d.ts +0 -75
  135. package/dist/primitives/tabs.d.ts.map +0 -1
  136. package/dist/primitives/tabs.js +0 -263
  137. package/dist/primitives/tabs.js.map +0 -1
  138. package/dist/primitives/tag.d.ts +0 -92
  139. package/dist/primitives/tag.d.ts.map +0 -1
  140. package/dist/primitives/tag.js +0 -151
  141. package/dist/primitives/tag.js.map +0 -1
  142. package/dist/services/db.d.ts +0 -44
  143. package/dist/services/db.d.ts.map +0 -1
  144. package/dist/services/db.js +0 -59
  145. package/dist/services/db.js.map +0 -1
  146. package/dist/services/email.d.ts +0 -50
  147. package/dist/services/email.d.ts.map +0 -1
  148. package/dist/services/email.js +0 -60
  149. package/dist/services/email.js.map +0 -1
  150. package/dist/services/s3.d.ts +0 -61
  151. package/dist/services/s3.d.ts.map +0 -1
  152. package/dist/services/s3.js +0 -79
  153. package/dist/services/s3.js.map +0 -1
  154. /package/dist/{components → containers}/c.d.ts +0 -0
  155. /package/dist/{components → containers}/c.js +0 -0
  156. /package/dist/{components → containers}/g.d.ts +0 -0
  157. /package/dist/{components → containers}/g.js +0 -0
@@ -0,0 +1,1008 @@
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;
1008
+ //# sourceMappingURL=canvas.js.map