@zentto/studio 0.6.0 → 0.6.1

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.
@@ -78,233 +78,233 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
78
78
  this.codeModalTitle = '';
79
79
  this.codeModalContent = '';
80
80
  }
81
- static { this.styles = [studioTokens, fieldBaseStyles, css `
82
- :host { display: block; font-family: var(--zs-font-family); }
83
-
84
- .wizard {
85
- max-width: 900px; margin: 0 auto;
86
- background: var(--zs-bg); border-radius: 12px;
87
- border: 1px solid var(--zs-border);
88
- overflow: hidden;
89
- }
90
-
91
- /* Steps bar */
92
- .wizard-steps {
93
- display: flex; background: var(--zs-bg-secondary);
94
- border-bottom: 1px solid var(--zs-border); padding: 0;
95
- overflow-x: auto;
96
- }
97
- .wizard-step {
98
- flex: 1; display: flex; align-items: center; gap: 6px;
99
- padding: 14px 12px; cursor: pointer;
100
- color: var(--zs-text-muted); font-size: 12px;
101
- border-bottom: 3px solid transparent;
102
- transition: all 150ms; min-width: 0; white-space: nowrap;
103
- }
104
- .wizard-step:hover { color: var(--zs-text-secondary); }
105
- .wizard-step--active {
106
- color: var(--zs-primary); border-bottom-color: var(--zs-primary);
107
- font-weight: 500;
108
- }
109
- .wizard-step--done { color: var(--zs-success); }
110
- .wizard-step-icon { font-size: 16px; }
111
- .wizard-step-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
112
-
113
- /* Body */
114
- .wizard-body { padding: 32px; min-height: 400px; }
115
- .wizard-title { font-size: 22px; font-weight: 600; margin: 0 0 4px; color: var(--zs-text); }
116
- .wizard-desc { font-size: 14px; color: var(--zs-text-secondary); margin: 0 0 24px; }
117
-
118
- /* Footer */
119
- .wizard-footer {
120
- display: flex; justify-content: space-between;
121
- padding: 16px 32px; border-top: 1px solid var(--zs-border);
122
- background: var(--zs-bg-secondary);
123
- }
124
-
125
- /* Template cards */
126
- .template-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
127
- .template-card {
128
- padding: 24px; border: 2px solid var(--zs-border);
129
- border-radius: 12px; cursor: pointer;
130
- transition: all 200ms; text-align: center;
131
- }
132
- .template-card:hover { border-color: var(--zs-primary); transform: translateY(-2px); box-shadow: 0 4px 12px var(--zs-shadow); }
133
- .template-card--selected { border-color: var(--zs-primary); background: var(--zs-primary-light); }
134
- .template-icon { font-size: 40px; margin-bottom: 12px; }
135
- .template-title { font-size: 16px; font-weight: 600; color: var(--zs-text); }
136
- .template-desc { font-size: 13px; color: var(--zs-text-secondary); margin-top: 4px; }
137
-
138
- /* Form groups */
139
- .form-group { margin-bottom: 20px; }
140
- .form-label { font-size: 13px; font-weight: 500; color: var(--zs-text); margin-bottom: 6px; display: block; }
141
- .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
142
- .form-row-3 { grid-template-columns: 1fr 1fr 1fr; }
143
- .form-row-4 { grid-template-columns: 1fr 1fr 1fr 1fr; }
144
-
145
- /* Color picker grid */
146
- .color-grid { display: flex; flex-wrap: wrap; gap: 8px; }
147
- .color-swatch {
148
- width: 36px; height: 36px; border-radius: 8px;
149
- cursor: pointer; border: 3px solid transparent;
150
- transition: all 150ms;
151
- }
152
- .color-swatch:hover { transform: scale(1.15); }
153
- .color-swatch--selected { border-color: var(--zs-text); box-shadow: 0 0 0 2px white, 0 0 0 4px var(--zs-text); }
154
-
155
- /* Sidebar style picker */
156
- .style-grid { display: flex; gap: 12px; }
157
- .style-option {
158
- flex: 1; padding: 16px; text-align: center;
159
- border: 2px solid var(--zs-border); border-radius: 8px;
160
- cursor: pointer; transition: all 150ms;
161
- }
162
- .style-option:hover { border-color: var(--zs-primary); }
163
- .style-option--selected { border-color: var(--zs-primary); background: var(--zs-primary-light); }
164
- .style-preview {
165
- width: 60px; height: 40px; border-radius: 4px;
166
- margin: 0 auto 8px; border: 1px solid var(--zs-border);
167
- }
168
-
169
- /* Nav editor */
170
- .nav-list { border: 1px solid var(--zs-border); border-radius: 8px; overflow: hidden; }
171
- .nav-item-row {
172
- display: flex; align-items: center; gap: 8px;
173
- padding: 10px 12px; border-bottom: 1px solid var(--zs-border);
174
- font-size: 14px;
175
- }
176
- .nav-item-row:last-child { border-bottom: none; }
177
- .nav-item-icon { font-size: 18px; cursor: pointer; }
178
- .nav-item-title { flex: 1; }
179
- .nav-item-actions { display: flex; gap: 4px; }
180
- .nav-item-btn {
181
- border: none; background: none; cursor: pointer;
182
- font-size: 14px; padding: 2px 6px; border-radius: 4px;
183
- color: var(--zs-text-muted); transition: all 100ms;
184
- }
185
- .nav-item-btn:hover { background: var(--zs-bg-hover); color: var(--zs-text); }
186
- .nav-item-btn--danger:hover { color: var(--zs-danger); }
187
- .add-btn {
188
- display: flex; align-items: center; gap: 6px;
189
- padding: 8px 16px; margin-top: 12px;
190
- border: 1px dashed var(--zs-border); border-radius: 8px;
191
- background: none; cursor: pointer;
192
- font-family: var(--zs-font-family); font-size: 13px;
193
- color: var(--zs-text-secondary); transition: all 150ms;
194
- }
195
- .add-btn:hover { border-color: var(--zs-primary); color: var(--zs-primary); }
196
-
197
- /* Icon picker */
198
- .icon-grid { display: flex; flex-wrap: wrap; gap: 4px; }
199
- .icon-btn {
200
- width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
201
- border: 1px solid transparent; border-radius: 6px;
202
- cursor: pointer; font-size: 18px; background: none;
203
- transition: all 100ms;
204
- }
205
- .icon-btn:hover { background: var(--zs-bg-hover); border-color: var(--zs-border); }
206
- .icon-btn--selected { background: var(--zs-primary-light); border-color: var(--zs-primary); }
207
-
208
- /* Preview */
209
- .preview-container {
210
- border: 1px solid var(--zs-border); border-radius: 8px;
211
- height: 500px; overflow: hidden;
212
- }
213
-
214
- /* Buttons */
215
- .btn {
216
- padding: 10px 24px; border-radius: var(--zs-radius);
217
- font-family: var(--zs-font-family); font-size: 14px;
218
- font-weight: 500; cursor: pointer; border: 1px solid transparent;
219
- transition: all 150ms;
220
- }
221
- .btn--primary { background: var(--zs-primary); color: white; }
222
- .btn--primary:hover { background: var(--zs-primary-hover); }
223
- .btn--secondary { background: var(--zs-bg); color: var(--zs-text); border-color: var(--zs-border); }
224
- .btn--secondary:hover { background: var(--zs-bg-hover); }
225
- .btn--success { background: var(--zs-success); color: white; }
226
- .btn--success:hover { opacity: 0.9; }
227
- .btn:disabled { opacity: 0.5; cursor: not-allowed; }
228
-
229
- /* Data source rows */
230
- .ds-card {
231
- border: 1px solid var(--zs-border); border-radius: 8px;
232
- padding: 16px; margin-bottom: 12px;
233
- background: var(--zs-bg);
234
- }
235
- .ds-card-header {
236
- display: flex; align-items: center; justify-content: space-between;
237
- margin-bottom: 12px;
238
- }
239
- .ds-card-title { font-size: 14px; font-weight: 500; color: var(--zs-text); }
240
- .ds-method-badge {
241
- display: inline-block; padding: 2px 8px; border-radius: 4px;
242
- font-size: 11px; font-weight: 600; color: white;
243
- }
244
- .ds-method-GET { background: #27ae60; }
245
- .ds-method-POST { background: #3498db; }
246
- .ds-method-PUT { background: #e67e22; }
247
- .ds-method-PATCH { background: #9b59b6; }
248
- .ds-method-DELETE { background: #e74c3c; }
249
-
250
- /* Theme preview card */
251
- .theme-preview-card {
252
- border: 1px solid var(--zs-border); border-radius: 12px;
253
- padding: 24px; margin-top: 20px;
254
- transition: all 200ms;
255
- }
256
- .theme-preview-header {
257
- font-size: 16px; font-weight: 600; margin-bottom: 8px;
258
- }
259
- .theme-preview-text {
260
- font-size: 13px; margin-bottom: 16px; opacity: 0.7;
261
- }
262
- .theme-preview-btn {
263
- display: inline-block; padding: 8px 20px;
264
- border: none; border-radius: 8px;
265
- color: white; font-size: 13px; font-weight: 500;
266
- }
267
- .theme-preview-input {
268
- display: inline-block; padding: 6px 12px;
269
- border: 1px solid; border-radius: 8px;
270
- font-size: 13px; margin-left: 8px; width: 150px;
271
- }
272
-
273
- /* Code modal */
274
- .code-modal-overlay {
275
- position: fixed; inset: 0; z-index: 9999;
276
- background: rgba(0,0,0,0.5); display: flex;
277
- align-items: center; justify-content: center;
278
- }
279
- .code-modal {
280
- background: var(--zs-bg); border-radius: 12px;
281
- width: 90%; max-width: 800px; max-height: 80vh;
282
- display: flex; flex-direction: column;
283
- box-shadow: 0 20px 60px rgba(0,0,0,0.3);
284
- }
285
- .code-modal-header {
286
- display: flex; align-items: center; justify-content: space-between;
287
- padding: 16px 20px; border-bottom: 1px solid var(--zs-border);
288
- }
289
- .code-modal-header h3 { margin: 0; font-size: 16px; }
290
- .code-modal-body {
291
- flex: 1; overflow: auto; padding: 0;
292
- }
293
- .code-modal-body pre {
294
- margin: 0; padding: 20px; font-size: 13px;
295
- font-family: 'Fira Code', 'Cascadia Code', monospace;
296
- line-height: 1.5; white-space: pre-wrap; word-break: break-word;
297
- background: var(--zs-bg-secondary); min-height: 200px;
298
- }
299
- .code-modal-footer {
300
- display: flex; gap: 8px; justify-content: flex-end;
301
- padding: 12px 20px; border-top: 1px solid var(--zs-border);
302
- }
303
-
304
- /* Export buttons row */
305
- .export-row {
306
- display: flex; gap: 8px; flex-wrap: wrap;
307
- }
81
+ static { this.styles = [studioTokens, fieldBaseStyles, css `
82
+ :host { display: block; font-family: var(--zs-font-family); }
83
+
84
+ .wizard {
85
+ max-width: 900px; margin: 0 auto;
86
+ background: var(--zs-bg); border-radius: 12px;
87
+ border: 1px solid var(--zs-border);
88
+ overflow: hidden;
89
+ }
90
+
91
+ /* Steps bar */
92
+ .wizard-steps {
93
+ display: flex; background: var(--zs-bg-secondary);
94
+ border-bottom: 1px solid var(--zs-border); padding: 0;
95
+ overflow-x: auto;
96
+ }
97
+ .wizard-step {
98
+ flex: 1; display: flex; align-items: center; gap: 6px;
99
+ padding: 14px 12px; cursor: pointer;
100
+ color: var(--zs-text-muted); font-size: 12px;
101
+ border-bottom: 3px solid transparent;
102
+ transition: all 150ms; min-width: 0; white-space: nowrap;
103
+ }
104
+ .wizard-step:hover { color: var(--zs-text-secondary); }
105
+ .wizard-step--active {
106
+ color: var(--zs-primary); border-bottom-color: var(--zs-primary);
107
+ font-weight: 500;
108
+ }
109
+ .wizard-step--done { color: var(--zs-success); }
110
+ .wizard-step-icon { font-size: 16px; }
111
+ .wizard-step-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
112
+
113
+ /* Body */
114
+ .wizard-body { padding: 32px; min-height: 400px; }
115
+ .wizard-title { font-size: 22px; font-weight: 600; margin: 0 0 4px; color: var(--zs-text); }
116
+ .wizard-desc { font-size: 14px; color: var(--zs-text-secondary); margin: 0 0 24px; }
117
+
118
+ /* Footer */
119
+ .wizard-footer {
120
+ display: flex; justify-content: space-between;
121
+ padding: 16px 32px; border-top: 1px solid var(--zs-border);
122
+ background: var(--zs-bg-secondary);
123
+ }
124
+
125
+ /* Template cards */
126
+ .template-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
127
+ .template-card {
128
+ padding: 24px; border: 2px solid var(--zs-border);
129
+ border-radius: 12px; cursor: pointer;
130
+ transition: all 200ms; text-align: center;
131
+ }
132
+ .template-card:hover { border-color: var(--zs-primary); transform: translateY(-2px); box-shadow: 0 4px 12px var(--zs-shadow); }
133
+ .template-card--selected { border-color: var(--zs-primary); background: var(--zs-primary-light); }
134
+ .template-icon { font-size: 40px; margin-bottom: 12px; }
135
+ .template-title { font-size: 16px; font-weight: 600; color: var(--zs-text); }
136
+ .template-desc { font-size: 13px; color: var(--zs-text-secondary); margin-top: 4px; }
137
+
138
+ /* Form groups */
139
+ .form-group { margin-bottom: 20px; }
140
+ .form-label { font-size: 13px; font-weight: 500; color: var(--zs-text); margin-bottom: 6px; display: block; }
141
+ .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
142
+ .form-row-3 { grid-template-columns: 1fr 1fr 1fr; }
143
+ .form-row-4 { grid-template-columns: 1fr 1fr 1fr 1fr; }
144
+
145
+ /* Color picker grid */
146
+ .color-grid { display: flex; flex-wrap: wrap; gap: 8px; }
147
+ .color-swatch {
148
+ width: 36px; height: 36px; border-radius: 8px;
149
+ cursor: pointer; border: 3px solid transparent;
150
+ transition: all 150ms;
151
+ }
152
+ .color-swatch:hover { transform: scale(1.15); }
153
+ .color-swatch--selected { border-color: var(--zs-text); box-shadow: 0 0 0 2px white, 0 0 0 4px var(--zs-text); }
154
+
155
+ /* Sidebar style picker */
156
+ .style-grid { display: flex; gap: 12px; }
157
+ .style-option {
158
+ flex: 1; padding: 16px; text-align: center;
159
+ border: 2px solid var(--zs-border); border-radius: 8px;
160
+ cursor: pointer; transition: all 150ms;
161
+ }
162
+ .style-option:hover { border-color: var(--zs-primary); }
163
+ .style-option--selected { border-color: var(--zs-primary); background: var(--zs-primary-light); }
164
+ .style-preview {
165
+ width: 60px; height: 40px; border-radius: 4px;
166
+ margin: 0 auto 8px; border: 1px solid var(--zs-border);
167
+ }
168
+
169
+ /* Nav editor */
170
+ .nav-list { border: 1px solid var(--zs-border); border-radius: 8px; overflow: hidden; }
171
+ .nav-item-row {
172
+ display: flex; align-items: center; gap: 8px;
173
+ padding: 10px 12px; border-bottom: 1px solid var(--zs-border);
174
+ font-size: 14px;
175
+ }
176
+ .nav-item-row:last-child { border-bottom: none; }
177
+ .nav-item-icon { font-size: 18px; cursor: pointer; }
178
+ .nav-item-title { flex: 1; }
179
+ .nav-item-actions { display: flex; gap: 4px; }
180
+ .nav-item-btn {
181
+ border: none; background: none; cursor: pointer;
182
+ font-size: 14px; padding: 2px 6px; border-radius: 4px;
183
+ color: var(--zs-text-muted); transition: all 100ms;
184
+ }
185
+ .nav-item-btn:hover { background: var(--zs-bg-hover); color: var(--zs-text); }
186
+ .nav-item-btn--danger:hover { color: var(--zs-danger); }
187
+ .add-btn {
188
+ display: flex; align-items: center; gap: 6px;
189
+ padding: 8px 16px; margin-top: 12px;
190
+ border: 1px dashed var(--zs-border); border-radius: 8px;
191
+ background: none; cursor: pointer;
192
+ font-family: var(--zs-font-family); font-size: 13px;
193
+ color: var(--zs-text-secondary); transition: all 150ms;
194
+ }
195
+ .add-btn:hover { border-color: var(--zs-primary); color: var(--zs-primary); }
196
+
197
+ /* Icon picker */
198
+ .icon-grid { display: flex; flex-wrap: wrap; gap: 4px; }
199
+ .icon-btn {
200
+ width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
201
+ border: 1px solid transparent; border-radius: 6px;
202
+ cursor: pointer; font-size: 18px; background: none;
203
+ transition: all 100ms;
204
+ }
205
+ .icon-btn:hover { background: var(--zs-bg-hover); border-color: var(--zs-border); }
206
+ .icon-btn--selected { background: var(--zs-primary-light); border-color: var(--zs-primary); }
207
+
208
+ /* Preview */
209
+ .preview-container {
210
+ border: 1px solid var(--zs-border); border-radius: 8px;
211
+ height: 500px; overflow: hidden;
212
+ }
213
+
214
+ /* Buttons */
215
+ .btn {
216
+ padding: 10px 24px; border-radius: var(--zs-radius);
217
+ font-family: var(--zs-font-family); font-size: 14px;
218
+ font-weight: 500; cursor: pointer; border: 1px solid transparent;
219
+ transition: all 150ms;
220
+ }
221
+ .btn--primary { background: var(--zs-primary); color: white; }
222
+ .btn--primary:hover { background: var(--zs-primary-hover); }
223
+ .btn--secondary { background: var(--zs-bg); color: var(--zs-text); border-color: var(--zs-border); }
224
+ .btn--secondary:hover { background: var(--zs-bg-hover); }
225
+ .btn--success { background: var(--zs-success); color: white; }
226
+ .btn--success:hover { opacity: 0.9; }
227
+ .btn:disabled { opacity: 0.5; cursor: not-allowed; }
228
+
229
+ /* Data source rows */
230
+ .ds-card {
231
+ border: 1px solid var(--zs-border); border-radius: 8px;
232
+ padding: 16px; margin-bottom: 12px;
233
+ background: var(--zs-bg);
234
+ }
235
+ .ds-card-header {
236
+ display: flex; align-items: center; justify-content: space-between;
237
+ margin-bottom: 12px;
238
+ }
239
+ .ds-card-title { font-size: 14px; font-weight: 500; color: var(--zs-text); }
240
+ .ds-method-badge {
241
+ display: inline-block; padding: 2px 8px; border-radius: 4px;
242
+ font-size: 11px; font-weight: 600; color: white;
243
+ }
244
+ .ds-method-GET { background: #27ae60; }
245
+ .ds-method-POST { background: #3498db; }
246
+ .ds-method-PUT { background: #e67e22; }
247
+ .ds-method-PATCH { background: #9b59b6; }
248
+ .ds-method-DELETE { background: #e74c3c; }
249
+
250
+ /* Theme preview card */
251
+ .theme-preview-card {
252
+ border: 1px solid var(--zs-border); border-radius: 12px;
253
+ padding: 24px; margin-top: 20px;
254
+ transition: all 200ms;
255
+ }
256
+ .theme-preview-header {
257
+ font-size: 16px; font-weight: 600; margin-bottom: 8px;
258
+ }
259
+ .theme-preview-text {
260
+ font-size: 13px; margin-bottom: 16px; opacity: 0.7;
261
+ }
262
+ .theme-preview-btn {
263
+ display: inline-block; padding: 8px 20px;
264
+ border: none; border-radius: 8px;
265
+ color: white; font-size: 13px; font-weight: 500;
266
+ }
267
+ .theme-preview-input {
268
+ display: inline-block; padding: 6px 12px;
269
+ border: 1px solid; border-radius: 8px;
270
+ font-size: 13px; margin-left: 8px; width: 150px;
271
+ }
272
+
273
+ /* Code modal */
274
+ .code-modal-overlay {
275
+ position: fixed; inset: 0; z-index: 9999;
276
+ background: rgba(0,0,0,0.5); display: flex;
277
+ align-items: center; justify-content: center;
278
+ }
279
+ .code-modal {
280
+ background: var(--zs-bg); border-radius: 12px;
281
+ width: 90%; max-width: 800px; max-height: 80vh;
282
+ display: flex; flex-direction: column;
283
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
284
+ }
285
+ .code-modal-header {
286
+ display: flex; align-items: center; justify-content: space-between;
287
+ padding: 16px 20px; border-bottom: 1px solid var(--zs-border);
288
+ }
289
+ .code-modal-header h3 { margin: 0; font-size: 16px; }
290
+ .code-modal-body {
291
+ flex: 1; overflow: auto; padding: 0;
292
+ }
293
+ .code-modal-body pre {
294
+ margin: 0; padding: 20px; font-size: 13px;
295
+ font-family: 'Fira Code', 'Cascadia Code', monospace;
296
+ line-height: 1.5; white-space: pre-wrap; word-break: break-word;
297
+ background: var(--zs-bg-secondary); min-height: 200px;
298
+ }
299
+ .code-modal-footer {
300
+ display: flex; gap: 8px; justify-content: flex-end;
301
+ padding: 12px 20px; border-top: 1px solid var(--zs-border);
302
+ }
303
+
304
+ /* Export buttons row */
305
+ .export-row {
306
+ display: flex; gap: 8px; flex-wrap: wrap;
307
+ }
308
308
  `]; }
309
309
  connectedCallback() {
310
310
  super.connectedCallback();
@@ -353,57 +353,57 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
353
353
  }
354
354
  // ─── Render ───────────────────────────────────────
355
355
  render() {
356
- return html `
357
- <div class="wizard">
358
- <div class="wizard-steps">
359
- ${STEPS.map((step, i) => html `
360
- <div class="wizard-step ${i === this.currentStep ? 'wizard-step--active' : i < this.currentStep ? 'wizard-step--done' : ''}"
356
+ return html `
357
+ <div class="wizard">
358
+ <div class="wizard-steps">
359
+ ${STEPS.map((step, i) => html `
360
+ <div class="wizard-step ${i === this.currentStep ? 'wizard-step--active' : i < this.currentStep ? 'wizard-step--done' : ''}"
361
361
  @click="${() => { if (i <= this.currentStep || this.config)
362
- this.currentStep = i; }}">
363
- <span class="wizard-step-icon">${i < this.currentStep ? '✓' : step.icon}</span>
364
- <span class="wizard-step-title">${step.title}</span>
365
- </div>
366
- `)}
367
- </div>
368
-
369
- <div class="wizard-body">
370
- <h2 class="wizard-title">${STEPS[this.currentStep].title}</h2>
371
- <p class="wizard-desc">${STEPS[this.currentStep].description}</p>
372
-
362
+ this.currentStep = i; }}">
363
+ <span class="wizard-step-icon">${i < this.currentStep ? '✓' : step.icon}</span>
364
+ <span class="wizard-step-title">${step.title}</span>
365
+ </div>
366
+ `)}
367
+ </div>
368
+
369
+ <div class="wizard-body">
370
+ <h2 class="wizard-title">${STEPS[this.currentStep].title}</h2>
371
+ <p class="wizard-desc">${STEPS[this.currentStep].description}</p>
372
+
373
373
  ${this.currentStep === 0 ? this.renderTemplateStep() :
374
374
  this.currentStep === 1 ? this.renderBrandingStep() :
375
375
  this.currentStep === 2 ? this.renderNavStep() :
376
376
  this.currentStep === 3 ? this.renderDataSourcesStep() :
377
377
  this.currentStep === 4 ? this.renderPagesStep() :
378
378
  this.currentStep === 5 ? this.renderThemeStep() :
379
- this.renderPreviewStep()}
380
- </div>
381
-
382
- <div class="wizard-footer">
383
- <button class="btn btn--secondary" ?disabled="${this.currentStep === 0}" @click="${this.prev}">Anterior</button>
379
+ this.renderPreviewStep()}
380
+ </div>
381
+
382
+ <div class="wizard-footer">
383
+ <button class="btn btn--secondary" ?disabled="${this.currentStep === 0}" @click="${this.prev}">Anterior</button>
384
384
  ${this.currentStep < STEPS.length - 1
385
385
  ? html `<button class="btn btn--primary" ?disabled="${!this.canNext()}" @click="${this.next}">Siguiente</button>`
386
- : html `<button class="btn btn--success" @click="${this.finish}">Crear Aplicacion</button>`}
387
- </div>
388
- </div>
389
-
390
- ${this.codeModalOpen ? this.renderCodeModal() : nothing}
386
+ : html `<button class="btn btn--success" @click="${this.finish}">Crear Aplicacion</button>`}
387
+ </div>
388
+ </div>
389
+
390
+ ${this.codeModalOpen ? this.renderCodeModal() : nothing}
391
391
  `;
392
392
  }
393
393
  // ─── Step 1: Template Selection ──────────────────
394
394
  renderTemplateStep() {
395
395
  const templates = listAppTemplates();
396
- return html `
397
- <div class="template-grid">
398
- ${templates.map(t => html `
399
- <div class="template-card ${this.selectedTemplate === t.id ? 'template-card--selected' : ''}"
400
- @click="${() => { this.selectedTemplate = t.id; }}">
401
- <div class="template-icon">${t.icon}</div>
402
- <div class="template-title">${t.title}</div>
403
- <div class="template-desc">${t.description}</div>
404
- </div>
405
- `)}
406
- </div>
396
+ return html `
397
+ <div class="template-grid">
398
+ ${templates.map(t => html `
399
+ <div class="template-card ${this.selectedTemplate === t.id ? 'template-card--selected' : ''}"
400
+ @click="${() => { this.selectedTemplate = t.id; }}">
401
+ <div class="template-icon">${t.icon}</div>
402
+ <div class="template-title">${t.title}</div>
403
+ <div class="template-desc">${t.description}</div>
404
+ </div>
405
+ `)}
406
+ </div>
407
407
  `;
408
408
  }
409
409
  // ─── Step 2: Branding ─────────────────────────────
@@ -411,92 +411,92 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
411
411
  if (!this.config)
412
412
  return nothing;
413
413
  const b = this.config.branding;
414
- return html `
415
- <div class="form-row">
416
- <div class="form-group">
417
- <label class="form-label">Nombre de la App</label>
418
- <input class="zs-input" .value="${b.title ?? ''}" @input="${(e) => { this.config.branding.title = e.target.value; this.requestUpdate(); }}" />
419
- </div>
420
- <div class="form-group">
421
- <label class="form-label">Subtitulo</label>
422
- <input class="zs-input" .value="${b.subtitle ?? ''}" @input="${(e) => { this.config.branding.subtitle = e.target.value; this.requestUpdate(); }}" />
423
- </div>
424
- </div>
425
-
426
- <div class="form-group">
427
- <label class="form-label">Color Principal</label>
428
- <div class="color-grid">
429
- ${COLOR_PALETTE.map(color => html `
430
- <div class="color-swatch ${b.primaryColor === color ? 'color-swatch--selected' : ''}"
431
- style="background: ${color}"
432
- @click="${() => { this.config.branding.primaryColor = color; this.requestUpdate(); }}"
433
- ></div>
434
- `)}
435
- </div>
436
- </div>
437
-
438
- <div class="form-group">
439
- <label class="form-label">Estilo del Sidebar</label>
440
- <div class="style-grid">
441
- ${SIDEBAR_STYLES.map(s => html `
442
- <div class="style-option ${b.sidebarStyle === s.value ? 'style-option--selected' : ''}"
443
- @click="${() => { this.config.branding.sidebarStyle = s.value; this.requestUpdate(); }}">
444
- <div class="style-preview" style="background: ${s.preview}"></div>
445
- <div style="font-size:13px;font-weight:500;">${s.label}</div>
446
- </div>
447
- `)}
448
- </div>
449
- </div>
450
-
451
- <div class="form-group">
452
- <label class="form-label">URL del Logo (opcional)</label>
453
- <input class="zs-input" .value="${b.logo ?? ''}" placeholder="https://..." @input="${(e) => { this.config.branding.logo = e.target.value; this.requestUpdate(); }}" />
454
- </div>
414
+ return html `
415
+ <div class="form-row">
416
+ <div class="form-group">
417
+ <label class="form-label">Nombre de la App</label>
418
+ <input class="zs-input" .value="${b.title ?? ''}" @input="${(e) => { this.config.branding.title = e.target.value; this.requestUpdate(); }}" />
419
+ </div>
420
+ <div class="form-group">
421
+ <label class="form-label">Subtitulo</label>
422
+ <input class="zs-input" .value="${b.subtitle ?? ''}" @input="${(e) => { this.config.branding.subtitle = e.target.value; this.requestUpdate(); }}" />
423
+ </div>
424
+ </div>
425
+
426
+ <div class="form-group">
427
+ <label class="form-label">Color Principal</label>
428
+ <div class="color-grid">
429
+ ${COLOR_PALETTE.map(color => html `
430
+ <div class="color-swatch ${b.primaryColor === color ? 'color-swatch--selected' : ''}"
431
+ style="background: ${color}"
432
+ @click="${() => { this.config.branding.primaryColor = color; this.requestUpdate(); }}"
433
+ ></div>
434
+ `)}
435
+ </div>
436
+ </div>
437
+
438
+ <div class="form-group">
439
+ <label class="form-label">Estilo del Sidebar</label>
440
+ <div class="style-grid">
441
+ ${SIDEBAR_STYLES.map(s => html `
442
+ <div class="style-option ${b.sidebarStyle === s.value ? 'style-option--selected' : ''}"
443
+ @click="${() => { this.config.branding.sidebarStyle = s.value; this.requestUpdate(); }}">
444
+ <div class="style-preview" style="background: ${s.preview}"></div>
445
+ <div style="font-size:13px;font-weight:500;">${s.label}</div>
446
+ </div>
447
+ `)}
448
+ </div>
449
+ </div>
450
+
451
+ <div class="form-group">
452
+ <label class="form-label">URL del Logo (opcional)</label>
453
+ <input class="zs-input" .value="${b.logo ?? ''}" placeholder="https://..." @input="${(e) => { this.config.branding.logo = e.target.value; this.requestUpdate(); }}" />
454
+ </div>
455
455
  `;
456
456
  }
457
457
  // ─── Step 3: Navigation Editor ────────────────────
458
458
  renderNavStep() {
459
459
  if (!this.config)
460
460
  return nothing;
461
- return html `
462
- <div class="nav-list">
463
- ${this.config.navigation.map((item, i) => html `
464
- <div class="nav-item-row">
465
- <span class="nav-item-icon" @click="${() => { this.editingNavIndex = i; this.showIconPicker = !this.showIconPicker; }}">${item.icon ?? (item.kind === 'header' ? '📌' : item.kind === 'divider' ? '—' : '📄')}</span>
461
+ return html `
462
+ <div class="nav-list">
463
+ ${this.config.navigation.map((item, i) => html `
464
+ <div class="nav-item-row">
465
+ <span class="nav-item-icon" @click="${() => { this.editingNavIndex = i; this.showIconPicker = !this.showIconPicker; }}">${item.icon ?? (item.kind === 'header' ? '📌' : item.kind === 'divider' ? '—' : '📄')}</span>
466
466
  ${item.kind === 'divider'
467
467
  ? html `<span class="nav-item-title" style="color:var(--zs-text-muted);font-style:italic;">Separador</span>`
468
- : html `<input class="zs-input" style="height:32px;font-size:13px;" .value="${item.title ?? ''}" @input="${(e) => { this.config.navigation[i].title = e.target.value; this.requestUpdate(); }}" />`}
469
- <div class="nav-item-actions">
470
- <button class="nav-item-btn" title="Subir" @click="${() => this.moveNav(i, -1)}">↑</button>
471
- <button class="nav-item-btn" title="Bajar" @click="${() => this.moveNav(i, 1)}">↓</button>
472
- <button class="nav-item-btn nav-item-btn--danger" title="Eliminar" @click="${() => { this.config.navigation.splice(i, 1); this.requestUpdate(); }}">✕</button>
473
- </div>
474
- </div>
475
- `)}
476
- </div>
477
-
478
- ${this.showIconPicker ? html `
479
- <div style="margin-top:12px;padding:12px;border:1px solid var(--zs-border);border-radius:8px;">
480
- <div style="font-size:12px;color:var(--zs-text-secondary);margin-bottom:8px;">Selecciona un icono:</div>
481
- <div class="icon-grid">
482
- ${ICON_PALETTE.map(icon => html `
468
+ : html `<input class="zs-input" style="height:32px;font-size:13px;" .value="${item.title ?? ''}" @input="${(e) => { this.config.navigation[i].title = e.target.value; this.requestUpdate(); }}" />`}
469
+ <div class="nav-item-actions">
470
+ <button class="nav-item-btn" title="Subir" @click="${() => this.moveNav(i, -1)}">↑</button>
471
+ <button class="nav-item-btn" title="Bajar" @click="${() => this.moveNav(i, 1)}">↓</button>
472
+ <button class="nav-item-btn nav-item-btn--danger" title="Eliminar" @click="${() => { this.config.navigation.splice(i, 1); this.requestUpdate(); }}">✕</button>
473
+ </div>
474
+ </div>
475
+ `)}
476
+ </div>
477
+
478
+ ${this.showIconPicker ? html `
479
+ <div style="margin-top:12px;padding:12px;border:1px solid var(--zs-border);border-radius:8px;">
480
+ <div style="font-size:12px;color:var(--zs-text-secondary);margin-bottom:8px;">Selecciona un icono:</div>
481
+ <div class="icon-grid">
482
+ ${ICON_PALETTE.map(icon => html `
483
483
  <button class="icon-btn" @click="${() => {
484
484
  if (this.editingNavIndex >= 0) {
485
485
  this.config.navigation[this.editingNavIndex].icon = icon;
486
486
  this.showIconPicker = false;
487
487
  this.requestUpdate();
488
488
  }
489
- }}">${icon}</button>
490
- `)}
491
- </div>
492
- </div>
493
- ` : ''}
494
-
495
- <div style="display:flex;gap:8px;">
496
- <button class="add-btn" @click="${() => this.addNavItem('page')}">➕ Pagina</button>
497
- <button class="add-btn" @click="${() => this.addNavItem('header')}">📌 Seccion</button>
498
- <button class="add-btn" @click="${() => this.addNavItem('divider')}">— Separador</button>
499
- </div>
489
+ }}">${icon}</button>
490
+ `)}
491
+ </div>
492
+ </div>
493
+ ` : ''}
494
+
495
+ <div style="display:flex;gap:8px;">
496
+ <button class="add-btn" @click="${() => this.addNavItem('page')}">➕ Pagina</button>
497
+ <button class="add-btn" @click="${() => this.addNavItem('header')}">📌 Seccion</button>
498
+ <button class="add-btn" @click="${() => this.addNavItem('divider')}">— Separador</button>
499
+ </div>
500
500
  `;
501
501
  }
502
502
  addNavItem(kind) {
@@ -531,77 +531,77 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
531
531
  if (!this.config)
532
532
  return nothing;
533
533
  const sources = this.ensureDataSources();
534
- return html `
535
- ${sources.length === 0 ? html `
536
- <div style="text-align:center;padding:40px 20px;color:var(--zs-text-muted);">
537
- <div style="font-size:40px;margin-bottom:12px;">🔗</div>
538
- <div style="font-size:14px;">No hay fuentes de datos configuradas.</div>
539
- <div style="font-size:13px;margin-top:4px;">Agrega APIs REST para conectar tus paginas con datos reales.</div>
540
- </div>
541
- ` : nothing}
542
-
543
- ${sources.map((ds, i) => html `
544
- <div class="ds-card">
545
- <div class="ds-card-header">
546
- <span class="ds-card-title">${ds.name || `Fuente ${i + 1}`}</span>
547
- <div style="display:flex;gap:8px;align-items:center;">
548
- <span class="ds-method-badge ds-method-${ds.method ?? 'GET'}">${ds.method ?? 'GET'}</span>
549
- <button class="nav-item-btn nav-item-btn--danger" title="Eliminar"
550
- @click="${() => { sources.splice(i, 1); this.requestUpdate(); }}">✕</button>
551
- </div>
552
- </div>
553
-
554
- <div class="form-row" style="margin-bottom:12px;">
555
- <div class="form-group" style="margin-bottom:0;">
556
- <label class="form-label">ID</label>
534
+ return html `
535
+ ${sources.length === 0 ? html `
536
+ <div style="text-align:center;padding:40px 20px;color:var(--zs-text-muted);">
537
+ <div style="font-size:40px;margin-bottom:12px;">🔗</div>
538
+ <div style="font-size:14px;">No hay fuentes de datos configuradas.</div>
539
+ <div style="font-size:13px;margin-top:4px;">Agrega APIs REST para conectar tus paginas con datos reales.</div>
540
+ </div>
541
+ ` : nothing}
542
+
543
+ ${sources.map((ds, i) => html `
544
+ <div class="ds-card">
545
+ <div class="ds-card-header">
546
+ <span class="ds-card-title">${ds.name || `Fuente ${i + 1}`}</span>
547
+ <div style="display:flex;gap:8px;align-items:center;">
548
+ <span class="ds-method-badge ds-method-${ds.method ?? 'GET'}">${ds.method ?? 'GET'}</span>
549
+ <button class="nav-item-btn nav-item-btn--danger" title="Eliminar"
550
+ @click="${() => { sources.splice(i, 1); this.requestUpdate(); }}">✕</button>
551
+ </div>
552
+ </div>
553
+
554
+ <div class="form-row" style="margin-bottom:12px;">
555
+ <div class="form-group" style="margin-bottom:0;">
556
+ <label class="form-label">ID</label>
557
557
  <input class="zs-input" .value="${ds.id}" @input="${(e) => {
558
558
  ds.id = e.target.value;
559
559
  this.requestUpdate();
560
- }}" placeholder="customers" />
561
- </div>
562
- <div class="form-group" style="margin-bottom:0;">
563
- <label class="form-label">Nombre</label>
560
+ }}" placeholder="customers" />
561
+ </div>
562
+ <div class="form-group" style="margin-bottom:0;">
563
+ <label class="form-label">Nombre</label>
564
564
  <input class="zs-input" .value="${ds.name}" @input="${(e) => {
565
565
  ds.name = e.target.value;
566
566
  this.requestUpdate();
567
- }}" placeholder="Clientes" />
568
- </div>
569
- </div>
570
-
571
- <div class="form-row" style="margin-bottom:12px;">
572
- <div class="form-group" style="margin-bottom:0;grid-column:span 1;">
573
- <label class="form-label">Metodo</label>
567
+ }}" placeholder="Clientes" />
568
+ </div>
569
+ </div>
570
+
571
+ <div class="form-row" style="margin-bottom:12px;">
572
+ <div class="form-group" style="margin-bottom:0;grid-column:span 1;">
573
+ <label class="form-label">Metodo</label>
574
574
  <select class="zs-input" .value="${ds.method ?? 'GET'}" @change="${(e) => {
575
575
  ds.method = e.target.value;
576
576
  this.requestUpdate();
577
- }}">
578
- ${HTTP_METHODS.map(m => html `<option value="${m}" ?selected="${ds.method === m}">${m}</option>`)}
579
- </select>
580
- </div>
581
- <div class="form-group" style="margin-bottom:0;">
582
- <label class="form-label">Intervalo de refresco (ms)</label>
583
- <input class="zs-input" type="number" .value="${String(ds.refreshInterval ?? '')}" placeholder="0 = sin polling"
577
+ }}">
578
+ ${HTTP_METHODS.map(m => html `<option value="${m}" ?selected="${ds.method === m}">${m}</option>`)}
579
+ </select>
580
+ </div>
581
+ <div class="form-group" style="margin-bottom:0;">
582
+ <label class="form-label">Intervalo de refresco (ms)</label>
583
+ <input class="zs-input" type="number" .value="${String(ds.refreshInterval ?? '')}" placeholder="0 = sin polling"
584
584
  @input="${(e) => {
585
585
  const v = parseInt(e.target.value);
586
586
  ds.refreshInterval = isNaN(v) || v <= 0 ? undefined : v;
587
587
  this.requestUpdate();
588
- }}" />
589
- </div>
590
- </div>
591
-
592
- <div class="form-group" style="margin-bottom:12px;">
593
- <label class="form-label">URL</label>
588
+ }}" />
589
+ </div>
590
+ </div>
591
+
592
+ <div class="form-group" style="margin-bottom:12px;">
593
+ <label class="form-label">URL</label>
594
594
  <input class="zs-input" .value="${ds.url ?? ''}" @input="${(e) => {
595
595
  ds.url = e.target.value;
596
596
  this.requestUpdate();
597
- }}" placeholder="https://api.ejemplo.com/v1/resource" />
598
- </div>
599
-
600
- <div class="form-group" style="margin-bottom:0;">
601
- <label class="form-label">Headers (JSON, opcional)</label>
602
- <textarea class="zs-input" rows="2" style="font-family:monospace;font-size:12px;resize:vertical;"
603
- .value="${ds.headers ? JSON.stringify(ds.headers, null, 2) : ''}"
604
- placeholder='{ "Authorization": "Bearer ..." }'
597
+ }}" placeholder="https://api.ejemplo.com/v1/resource" />
598
+ </div>
599
+
600
+ <div class="form-group" style="margin-bottom:0;">
601
+ <label class="form-label">Headers (JSON, opcional)</label>
602
+ <textarea class="zs-input" rows="2" style="font-family:monospace;font-size:12px;resize:vertical;"
603
+ .value="${ds.headers ? JSON.stringify(ds.headers, null, 2) : ''}"
604
+ placeholder='{ "Authorization": "Bearer ..." }'
605
605
  @change="${(e) => {
606
606
  const raw = e.target.value.trim();
607
607
  if (!raw) {
@@ -614,12 +614,12 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
614
614
  }
615
615
  catch { /* ignore invalid JSON */ }
616
616
  this.requestUpdate();
617
- }}"
618
- ></textarea>
619
- </div>
620
- </div>
621
- `)}
622
-
617
+ }}"
618
+ ></textarea>
619
+ </div>
620
+ </div>
621
+ `)}
622
+
623
623
  <button class="add-btn" @click="${() => {
624
624
  const id = `ds-${Date.now()}`;
625
625
  this.ensureDataSources().push({
@@ -631,44 +631,44 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
631
631
  autoFetch: true,
632
632
  });
633
633
  this.requestUpdate();
634
- }}">🔗 Nueva Fuente de Datos</button>
634
+ }}">🔗 Nueva Fuente de Datos</button>
635
635
  `;
636
636
  }
637
637
  // ─── Step 5: Pages ────────────────────────────────
638
638
  renderPagesStep() {
639
639
  if (!this.config)
640
640
  return nothing;
641
- return html `
642
- <div class="nav-list">
643
- ${this.config.pages.map((page, i) => html `
644
- <div class="nav-item-row">
645
- <span class="nav-item-icon">${this.getContentIcon(page.content)}</span>
646
- <div style="flex:1;">
647
- <input class="zs-input" style="height:30px;font-size:13px;margin-bottom:4px;" .value="${page.title}" @input="${(e) => { this.config.pages[i].title = e.target.value; this.requestUpdate(); }}" />
648
- <div style="display:flex;gap:8px;align-items:center;">
649
- <span style="font-size:11px;color:var(--zs-text-muted);">/${page.segment}</span>
650
- <select style="font-size:11px;padding:2px 6px;border:1px solid var(--zs-border);border-radius:4px;" .value="${page.content}" @change="${(e) => { this.config.pages[i].content = e.target.value; this.requestUpdate(); }}">
651
- <option value="empty">Vacia</option>
652
- <option value="cards">Cards/Dashboard</option>
653
- <option value="datagrid">Grid de Datos</option>
654
- <option value="schema">Formulario</option>
655
- <option value="chart">Graficos</option>
656
- <option value="html">HTML</option>
657
- <option value="iframe">iFrame</option>
658
- <option value="tabs">Tabs</option>
659
- <option value="custom">Custom</option>
660
- </select>
661
- </div>
662
- </div>
663
- <button class="nav-item-btn nav-item-btn--danger" @click="${() => { this.config.pages.splice(i, 1); this.requestUpdate(); }}">✕</button>
664
- </div>
665
- `)}
666
- </div>
641
+ return html `
642
+ <div class="nav-list">
643
+ ${this.config.pages.map((page, i) => html `
644
+ <div class="nav-item-row">
645
+ <span class="nav-item-icon">${this.getContentIcon(page.content)}</span>
646
+ <div style="flex:1;">
647
+ <input class="zs-input" style="height:30px;font-size:13px;margin-bottom:4px;" .value="${page.title}" @input="${(e) => { this.config.pages[i].title = e.target.value; this.requestUpdate(); }}" />
648
+ <div style="display:flex;gap:8px;align-items:center;">
649
+ <span style="font-size:11px;color:var(--zs-text-muted);">/${page.segment}</span>
650
+ <select style="font-size:11px;padding:2px 6px;border:1px solid var(--zs-border);border-radius:4px;" .value="${page.content}" @change="${(e) => { this.config.pages[i].content = e.target.value; this.requestUpdate(); }}">
651
+ <option value="empty">Vacia</option>
652
+ <option value="cards">Cards/Dashboard</option>
653
+ <option value="datagrid">Grid de Datos</option>
654
+ <option value="schema">Formulario</option>
655
+ <option value="chart">Graficos</option>
656
+ <option value="html">HTML</option>
657
+ <option value="iframe">iFrame</option>
658
+ <option value="tabs">Tabs</option>
659
+ <option value="custom">Custom</option>
660
+ </select>
661
+ </div>
662
+ </div>
663
+ <button class="nav-item-btn nav-item-btn--danger" @click="${() => { this.config.pages.splice(i, 1); this.requestUpdate(); }}">✕</button>
664
+ </div>
665
+ `)}
666
+ </div>
667
667
  <button class="add-btn" @click="${() => {
668
668
  const id = `page-${Date.now()}`;
669
669
  this.config.pages.push({ id, segment: id, title: 'Nueva Pagina', content: 'empty' });
670
670
  this.requestUpdate();
671
- }}">➕ Nueva Pagina</button>
671
+ }}">➕ Nueva Pagina</button>
672
672
  `;
673
673
  }
674
674
  getContentIcon(type) {
@@ -684,116 +684,116 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
684
684
  if (!this.config)
685
685
  return nothing;
686
686
  const theme = this.ensureTheme();
687
- return html `
688
- <!-- Theme mode -->
689
- <div class="form-group">
690
- <label class="form-label">Modo de Tema</label>
691
- <div class="style-grid">
692
- ${THEME_MODES.map(m => html `
693
- <div class="style-option ${theme.mode === m.value ? 'style-option--selected' : ''}"
694
- @click="${() => { theme.mode = m.value; this.requestUpdate(); }}">
695
- <div style="font-size:28px;margin-bottom:6px;">${m.icon}</div>
696
- <div style="font-size:13px;font-weight:500;">${m.label}</div>
697
- </div>
698
- `)}
699
- </div>
700
- </div>
701
-
702
- <!-- Primary color -->
703
- <div class="form-group">
704
- <label class="form-label">Color Primario (--zs-primary)</label>
705
- <div class="color-grid">
706
- ${COLOR_PALETTE.map(color => html `
707
- <div class="color-swatch ${theme.primaryColor === color ? 'color-swatch--selected' : ''}"
708
- style="background: ${color}"
709
- @click="${() => { theme.primaryColor = color; this.requestUpdate(); }}"
710
- ></div>
711
- `)}
712
- </div>
713
- </div>
714
-
715
- <!-- Font family -->
716
- <div class="form-row">
717
- <div class="form-group">
718
- <label class="form-label">Tipografia (--zs-font)</label>
687
+ return html `
688
+ <!-- Theme mode -->
689
+ <div class="form-group">
690
+ <label class="form-label">Modo de Tema</label>
691
+ <div class="style-grid">
692
+ ${THEME_MODES.map(m => html `
693
+ <div class="style-option ${theme.mode === m.value ? 'style-option--selected' : ''}"
694
+ @click="${() => { theme.mode = m.value; this.requestUpdate(); }}">
695
+ <div style="font-size:28px;margin-bottom:6px;">${m.icon}</div>
696
+ <div style="font-size:13px;font-weight:500;">${m.label}</div>
697
+ </div>
698
+ `)}
699
+ </div>
700
+ </div>
701
+
702
+ <!-- Primary color -->
703
+ <div class="form-group">
704
+ <label class="form-label">Color Primario (--zs-primary)</label>
705
+ <div class="color-grid">
706
+ ${COLOR_PALETTE.map(color => html `
707
+ <div class="color-swatch ${theme.primaryColor === color ? 'color-swatch--selected' : ''}"
708
+ style="background: ${color}"
709
+ @click="${() => { theme.primaryColor = color; this.requestUpdate(); }}"
710
+ ></div>
711
+ `)}
712
+ </div>
713
+ </div>
714
+
715
+ <!-- Font family -->
716
+ <div class="form-row">
717
+ <div class="form-group">
718
+ <label class="form-label">Tipografia (--zs-font)</label>
719
719
  <select class="zs-input" .value="${theme.fontFamily ?? 'Inter, sans-serif'}" @change="${(e) => {
720
720
  theme.fontFamily = e.target.value;
721
721
  this.requestUpdate();
722
- }}">
723
- ${FONT_OPTIONS.map(f => html `<option value="${f}" ?selected="${theme.fontFamily === f}">${f.split(',')[0]}</option>`)}
724
- </select>
725
- </div>
726
- <div class="form-group">
727
- <label class="form-label">Border Radius (--zs-radius)</label>
722
+ }}">
723
+ ${FONT_OPTIONS.map(f => html `<option value="${f}" ?selected="${theme.fontFamily === f}">${f.split(',')[0]}</option>`)}
724
+ </select>
725
+ </div>
726
+ <div class="form-group">
727
+ <label class="form-label">Border Radius (--zs-radius)</label>
728
728
  <select class="zs-input" .value="${String(theme.borderRadius ?? 8)}" @change="${(e) => {
729
729
  theme.borderRadius = parseInt(e.target.value);
730
730
  this.requestUpdate();
731
- }}">
732
- ${RADIUS_OPTIONS.map(r => html `<option value="${r.value}" ?selected="${theme.borderRadius === r.value}">${r.label}</option>`)}
733
- </select>
734
- </div>
735
- </div>
736
-
737
- <!-- Accent color -->
738
- <div class="form-group">
739
- <label class="form-label">Color de Acento (--zs-accent, opcional)</label>
740
- <div class="color-grid">
741
- ${COLOR_PALETTE.map(color => html `
742
- <div class="color-swatch ${theme.accentColor === color ? 'color-swatch--selected' : ''}"
743
- style="background: ${color}"
744
- @click="${() => { theme.accentColor = color; this.requestUpdate(); }}"
745
- ></div>
746
- `)}
747
- </div>
748
- </div>
749
-
750
- <!-- Font size & spacing -->
751
- <div class="form-row">
752
- <div class="form-group">
753
- <label class="form-label">Tamano de Fuente Base (px)</label>
731
+ }}">
732
+ ${RADIUS_OPTIONS.map(r => html `<option value="${r.value}" ?selected="${theme.borderRadius === r.value}">${r.label}</option>`)}
733
+ </select>
734
+ </div>
735
+ </div>
736
+
737
+ <!-- Accent color -->
738
+ <div class="form-group">
739
+ <label class="form-label">Color de Acento (--zs-accent, opcional)</label>
740
+ <div class="color-grid">
741
+ ${COLOR_PALETTE.map(color => html `
742
+ <div class="color-swatch ${theme.accentColor === color ? 'color-swatch--selected' : ''}"
743
+ style="background: ${color}"
744
+ @click="${() => { theme.accentColor = color; this.requestUpdate(); }}"
745
+ ></div>
746
+ `)}
747
+ </div>
748
+ </div>
749
+
750
+ <!-- Font size & spacing -->
751
+ <div class="form-row">
752
+ <div class="form-group">
753
+ <label class="form-label">Tamano de Fuente Base (px)</label>
754
754
  <input class="zs-input" type="number" min="10" max="22" .value="${String(theme.fontSize ?? 14)}" @input="${(e) => {
755
755
  theme.fontSize = parseInt(e.target.value) || 14;
756
756
  this.requestUpdate();
757
- }}" />
758
- </div>
759
- <div class="form-group">
760
- <label class="form-label">Espaciado Base (px)</label>
757
+ }}" />
758
+ </div>
759
+ <div class="form-group">
760
+ <label class="form-label">Espaciado Base (px)</label>
761
761
  <input class="zs-input" type="number" min="2" max="16" .value="${String(theme.spacing ?? 8)}" @input="${(e) => {
762
762
  theme.spacing = parseInt(e.target.value) || 8;
763
763
  this.requestUpdate();
764
- }}" />
765
- </div>
766
- </div>
767
-
768
- <!-- Mini preview -->
769
- <div class="theme-preview-card" style="
770
- background: ${theme.mode === 'dark' ? '#1e1e2d' : '#ffffff'};
771
- color: ${theme.mode === 'dark' ? '#e0e0e0' : '#333333'};
772
- font-family: ${theme.fontFamily ?? 'Inter, sans-serif'};
773
- font-size: ${theme.fontSize ?? 14}px;
774
- border-radius: ${theme.borderRadius ?? 8}px;
775
- ">
776
- <div class="theme-preview-header" style="color: ${theme.primaryColor ?? '#3498db'};">
777
- Vista previa del tema
778
- </div>
779
- <div class="theme-preview-text">
780
- Asi se vera tu aplicacion con estos ajustes de estilo.
781
- </div>
782
- <span class="theme-preview-btn" style="
783
- background: ${theme.primaryColor ?? '#3498db'};
784
- border-radius: ${theme.borderRadius ?? 8}px;
785
- ">Boton Primario</span>
786
- <input class="theme-preview-input" value="Campo de texto"
787
- style="
788
- border-color: ${theme.mode === 'dark' ? '#444' : '#ccc'};
789
- border-radius: ${theme.borderRadius ?? 8}px;
790
- background: ${theme.mode === 'dark' ? '#2a2a3d' : '#f9f9f9'};
791
- color: ${theme.mode === 'dark' ? '#e0e0e0' : '#333'};
792
- font-family: ${theme.fontFamily ?? 'Inter, sans-serif'};
793
- "
794
- readonly
795
- />
796
- </div>
764
+ }}" />
765
+ </div>
766
+ </div>
767
+
768
+ <!-- Mini preview -->
769
+ <div class="theme-preview-card" style="
770
+ background: ${theme.mode === 'dark' ? '#1e1e2d' : '#ffffff'};
771
+ color: ${theme.mode === 'dark' ? '#e0e0e0' : '#333333'};
772
+ font-family: ${theme.fontFamily ?? 'Inter, sans-serif'};
773
+ font-size: ${theme.fontSize ?? 14}px;
774
+ border-radius: ${theme.borderRadius ?? 8}px;
775
+ ">
776
+ <div class="theme-preview-header" style="color: ${theme.primaryColor ?? '#3498db'};">
777
+ Vista previa del tema
778
+ </div>
779
+ <div class="theme-preview-text">
780
+ Asi se vera tu aplicacion con estos ajustes de estilo.
781
+ </div>
782
+ <span class="theme-preview-btn" style="
783
+ background: ${theme.primaryColor ?? '#3498db'};
784
+ border-radius: ${theme.borderRadius ?? 8}px;
785
+ ">Boton Primario</span>
786
+ <input class="theme-preview-input" value="Campo de texto"
787
+ style="
788
+ border-color: ${theme.mode === 'dark' ? '#444' : '#ccc'};
789
+ border-radius: ${theme.borderRadius ?? 8}px;
790
+ background: ${theme.mode === 'dark' ? '#2a2a3d' : '#f9f9f9'};
791
+ color: ${theme.mode === 'dark' ? '#e0e0e0' : '#333'};
792
+ font-family: ${theme.fontFamily ?? 'Inter, sans-serif'};
793
+ "
794
+ readonly
795
+ />
796
+ </div>
797
797
  `;
798
798
  }
799
799
  // ─── Step 7: Preview ──────────────────────────────
@@ -802,34 +802,34 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
802
802
  return nothing;
803
803
  const navPageCount = this.config.navigation.filter(n => n.kind !== 'divider' && n.kind !== 'header').length;
804
804
  const dsCount = this.config.dataSources?.length ?? 0;
805
- return html `
806
- <div style="display:flex;gap:16px;margin-bottom:16px;align-items:center;flex-wrap:wrap;">
807
- <div style="flex:1;min-width:200px;">
808
- <div style="font-size:13px;color:var(--zs-text-secondary);margin-bottom:4px;">Resumen:</div>
809
- <div style="font-size:14px;">
810
- <strong>${this.config.branding.title}</strong> —
811
- ${this.config.pages.length} paginas,
812
- ${navPageCount} items en menu${dsCount > 0 ? `, ${dsCount} fuentes de datos` : ''}
813
- </div>
814
- </div>
815
- </div>
816
-
817
- <!-- Export buttons -->
818
- <div class="export-row" style="margin-bottom:16px;">
819
- <button class="btn btn--secondary" @click="${this.exportJson}">📋 Exportar JSON</button>
820
- <button class="btn btn--secondary" @click="${this.exportReactCode}">⚛️ Exportar Codigo React</button>
821
- <button class="btn btn--secondary" @click="${this.exportNextCode}">▲ Exportar Codigo Next.js</button>
805
+ return html `
806
+ <div style="display:flex;gap:16px;margin-bottom:16px;align-items:center;flex-wrap:wrap;">
807
+ <div style="flex:1;min-width:200px;">
808
+ <div style="font-size:13px;color:var(--zs-text-secondary);margin-bottom:4px;">Resumen:</div>
809
+ <div style="font-size:14px;">
810
+ <strong>${this.config.branding.title}</strong> —
811
+ ${this.config.pages.length} paginas,
812
+ ${navPageCount} items en menu${dsCount > 0 ? `, ${dsCount} fuentes de datos` : ''}
813
+ </div>
814
+ </div>
815
+ </div>
816
+
817
+ <!-- Export buttons -->
818
+ <div class="export-row" style="margin-bottom:16px;">
819
+ <button class="btn btn--secondary" @click="${this.exportJson}">📋 Exportar JSON</button>
820
+ <button class="btn btn--secondary" @click="${this.exportReactCode}">⚛️ Exportar Codigo React</button>
821
+ <button class="btn btn--secondary" @click="${this.exportNextCode}">▲ Exportar Codigo Next.js</button>
822
822
  <button class="btn btn--secondary" @click="${() => {
823
823
  const json = JSON.stringify(this.config, null, 2);
824
824
  navigator.clipboard.writeText(json).then(() => {
825
825
  alert('JSON copiado al portapapeles');
826
826
  });
827
- }}">📎 Copiar JSON</button>
828
- </div>
829
-
830
- <div class="preview-container">
831
- <zentto-studio-app .config="${this.config}"></zentto-studio-app>
832
- </div>
827
+ }}">📎 Copiar JSON</button>
828
+ </div>
829
+
830
+ <div class="preview-container">
831
+ <zentto-studio-app .config="${this.config}"></zentto-studio-app>
832
+ </div>
833
833
  `;
834
834
  }
835
835
  // ─── Export Handlers ──────────────────────────────
@@ -888,25 +888,25 @@ let ZsAppWizard = class ZsAppWizard extends LitElement {
888
888
  }
889
889
  // ─── Code Modal ───────────────────────────────────
890
890
  renderCodeModal() {
891
- return html `
891
+ return html `
892
892
  <div class="code-modal-overlay" @click="${(e) => {
893
893
  if (e.target === e.currentTarget)
894
894
  this.closeCodeModal();
895
- }}">
896
- <div class="code-modal">
897
- <div class="code-modal-header">
898
- <h3>${this.codeModalTitle}</h3>
899
- <button class="nav-item-btn" @click="${this.closeCodeModal}" style="font-size:18px;">✕</button>
900
- </div>
901
- <div class="code-modal-body">
902
- <pre>${this.codeModalContent}</pre>
903
- </div>
904
- <div class="code-modal-footer">
905
- <button class="btn btn--secondary" @click="${this.downloadCode}">💾 Descargar</button>
906
- <button class="btn btn--primary" @click="${this.copyCodeToClipboard}">📋 Copiar</button>
907
- </div>
908
- </div>
909
- </div>
895
+ }}">
896
+ <div class="code-modal">
897
+ <div class="code-modal-header">
898
+ <h3>${this.codeModalTitle}</h3>
899
+ <button class="nav-item-btn" @click="${this.closeCodeModal}" style="font-size:18px;">✕</button>
900
+ </div>
901
+ <div class="code-modal-body">
902
+ <pre>${this.codeModalContent}</pre>
903
+ </div>
904
+ <div class="code-modal-footer">
905
+ <button class="btn btn--secondary" @click="${this.downloadCode}">💾 Descargar</button>
906
+ <button class="btn btn--primary" @click="${this.copyCodeToClipboard}">📋 Copiar</button>
907
+ </div>
908
+ </div>
909
+ </div>
910
910
  `;
911
911
  }
912
912
  };