Pathakkunal commited on
Commit
96c754a
·
1 Parent(s): 77accfa

feat: transform JSON terminal output into styled HTML list hierarchy in UI

Browse files
Files changed (3) hide show
  1. code.html +55 -17
  2. dashboard.html +71 -19
  3. glassui.html +58 -19
code.html CHANGED
@@ -164,23 +164,7 @@ SKU: JK-2024-WTR"></textarea>
164
  </div>
165
  </div>
166
  <div class="flex-1 p-4 overflow-auto custom-scrollbar font-mono text-sm leading-6">
167
- <pre><code class="language-json"><span class="text-slate-500">1</span> <span class="text-yellow-500">{</span>
168
- <span class="text-slate-500">2</span> <span class="text-primary">"product_analysis"</span><span class="text-white">:</span> <span class="text-yellow-500">{</span>
169
- <span class="text-slate-500">3</span> <span class="text-primary">"title"</span><span class="text-white">:</span> <span class="text-sky-300">"Apex Terrain All-Weather Performance Jacket"</span><span class="text-white">,</span>
170
- <span class="text-slate-500">4</span> <span class="text-primary">"category"</span><span class="text-white">:</span> <span class="text-sky-300">"Outerwear / Men's / Technical Shells"</span><span class="text-white">,</span>
171
- <span class="text-slate-500">5</span> <span class="text-primary">"features"</span><span class="text-white">:</span> <span class="text-yellow-500">[</span>
172
- <span class="text-slate-500">6</span> <span class="text-sky-300">"Gore-Tex Pro Membrane"</span><span class="text-white">,</span>
173
- <span class="text-slate-500">7</span> <span class="text-sky-300">"Articulated Sleeves"</span><span class="text-white">,</span>
174
- <span class="text-slate-500">8</span> <span class="text-sky-300">"Helmet-Compatible Hood"</span>
175
- <span class="text-slate-500">9</span> <span class="text-yellow-500">]</span><span class="text-white">,</span>
176
- <span class="text-slate-500">10</span> <span class="text-primary">"seo_tags"</span><span class="text-white">:</span> <span class="text-yellow-500">[</span>
177
- <span class="text-slate-500">11</span> <span class="text-sky-300">"#hikinggear"</span><span class="text-white">,</span> <span class="text-sky-300">"#waterproof"</span><span class="text-white">,</span> <span class="text-sky-300">"#adventure"</span>
178
- <span class="text-slate-500">12</span> <span class="text-yellow-500">]</span><span class="text-white">,</span>
179
- <span class="text-slate-500">13</span> <span class="text-primary">"sentiment_score"</span><span class="text-white">:</span> <span class="text-purple-400">0.98</span><span class="text-white">,</span>
180
- <span class="text-slate-500">14</span> <span class="text-primary">"market_fit"</span><span class="text-white">:</span> <span class="text-sky-300">"High Demand"</span>
181
- <span class="text-slate-500">15</span> <span class="text-yellow-500">}</span><span class="text-white">,</span>
182
- <span class="text-slate-500">16</span> <span class="text-primary">"deployment_status"</span><span class="text-white">:</span> <span class="text-sky-300">"Ready"</span>
183
- <span class="text-slate-500">17</span> <span class="text-yellow-500">}</span></code></pre>
184
  </div>
185
  </div>
186
  </div>
@@ -195,6 +179,60 @@ SKU: JK-2024-WTR"></textarea>
195
  '100%': { left: '125%' },
196
  }
197
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  </script>
199
 
200
  </body></html>
 
164
  </div>
165
  </div>
166
  <div class="flex-1 p-4 overflow-auto custom-scrollbar font-mono text-sm leading-6">
167
+ <div id="jsonOutput" class="w-full"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  </div>
169
  </div>
170
  </div>
 
179
  '100%': { left: '125%' },
180
  }
181
  }
182
+
183
+ const INITIAL_DATA = {
184
+ "product_analysis": {
185
+ "title": "Apex Terrain All-Weather Performance Jacket",
186
+ "category": "Outerwear / Men's / Technical Shells",
187
+ "features": ["Gore-Tex Pro Membrane", "Articulated Sleeves", "Helmet-Compatible Hood"],
188
+ "seo_tags": ["#hikinggear", "#waterproof", "#adventure"],
189
+ "sentiment_score": 0.98,
190
+ "market_fit": "High Demand"
191
+ },
192
+ "deployment_status": "Ready"
193
+ };
194
+ window.__currentJSONData = INITIAL_DATA;
195
+
196
+ function jsonToHTMLList(data, isRoot = true) {
197
+ if (typeof data !== 'object' || data === null) {
198
+ return `<span class="text-slate-300 break-words">${data}</span>`;
199
+ }
200
+
201
+ const isArray = Array.isArray(data);
202
+ let html = `<ul class="space-y-2 ${isRoot ? '' : 'pl-4 lg:pl-6 border-l border-border-dark mt-2'}">`;
203
+
204
+ for (const key in data) {
205
+ if (data.hasOwnProperty(key)) {
206
+ const value = data[key];
207
+ const isValueObject = typeof value === 'object' && value !== null;
208
+
209
+ html += `<li class="relative">`;
210
+
211
+ if (!isRoot) {
212
+ html += `<span class="absolute -left-[20px] lg:-left-[28px] top-2.5 size-1.5 rounded-full bg-primary/50"></span>`;
213
+ }
214
+
215
+ if (!isArray) {
216
+ html += `<strong class="text-primary font-semibold mr-2">${key}:</strong>`;
217
+ }
218
+
219
+ if (isValueObject) {
220
+ html += `<div class="mt-1">${jsonToHTMLList(value, false)}</div>`;
221
+ } else {
222
+ if (isArray) {
223
+ html += `<span class="inline-block bg-primary/10 border border-primary/20 rounded-full px-2.5 py-0.5 text-xs text-sky-300 mt-1">${value}</span>`;
224
+ } else {
225
+ html += `<span class="text-slate-300 break-words">${value}</span>`;
226
+ }
227
+ }
228
+ html += `</li>`;
229
+ }
230
+ }
231
+ html += `</ul>`;
232
+ return html;
233
+ }
234
+
235
+ document.getElementById('jsonOutput').innerHTML = jsonToHTMLList(window.__currentJSONData);
236
  </script>
237
 
238
  </body></html>
dashboard.html CHANGED
@@ -258,23 +258,7 @@
258
  </div>
259
  </div>
260
  <div class="flex-1 p-4 lg:p-5 overflow-auto custom-scrollbar font-mono text-xs lg:text-sm leading-6 lg:leading-7">
261
- <pre><code class="language-json block" id="jsonOutput"><span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">01</span><span class="text-amber-400">{</span>
262
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">02</span> <span class="text-amber-600">"product_analysis"</span><span class="text-neutral-400">:</span> <span class="text-amber-400">{</span>
263
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">03</span> <span class="text-amber-600">"title"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Noir Elite Series Artisan Timepiece"</span><span class="text-neutral-400">,</span>
264
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">04</span> <span class="text-amber-600">"category"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Luxury / Accessories"</span><span class="text-neutral-400">,</span>
265
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">05</span> <span class="text-amber-600">"features"</span><span class="text-neutral-400">:</span> <span class="text-amber-400">[</span>
266
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">06</span> <span class="text-amber-200">"Obsidian Finish"</span><span class="text-neutral-400">,</span>
267
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">07</span> <span class="text-amber-200">"Golden Accents"</span><span class="text-neutral-400">,</span>
268
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">08</span> <span class="text-amber-200">"Smart Haptic Interface"</span>
269
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">09</span> <span class="text-amber-400">]</span><span class="text-neutral-400">,</span>
270
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">10</span> <span class="text-amber-600">"seo_tags"</span><span class="text-neutral-400">:</span> <span class="text-amber-400">[</span>
271
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">11</span> <span class="text-amber-200">"#luxurywear"</span><span class="text-neutral-400">,</span> <span class="text-amber-200">"#amberstyle"</span><span class="text-neutral-400">,</span> <span class="text-amber-200">"#premiumtech"</span>
272
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">12</span> <span class="text-amber-400">]</span><span class="text-neutral-400">,</span>
273
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">13</span> <span class="text-amber-600">"sentiment_score"</span><span class="text-neutral-400">:</span> <span class="text-amber-500">0.99</span><span class="text-neutral-400">,</span>
274
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">14</span> <span class="text-amber-600">"market_fit"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Exceptional"</span>
275
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">15</span> <span class="text-amber-400">}</span><span class="text-neutral-400">,</span>
276
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">16</span> <span class="text-amber-600">"deployment_status"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Authorized"</span>
277
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">17</span><span class="text-amber-400">}</span></code></pre>
278
  </div>
279
  </div>
280
  </div>
@@ -288,6 +272,57 @@
288
  tailwind.config.theme.extend.keyframes = {
289
  shine: { '0%': { left: '-100%' }, '100%': { left: '200%' } }
290
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  const dropZone = document.getElementById('dropZone');
292
  const fileInput = document.getElementById('fileInput');
293
  const startBtn = document.getElementById('startBtn');
@@ -373,7 +408,8 @@ startBtn.addEventListener('click', async (e) => {
373
  const response = await fetch('/generate-catalog', { method: 'POST', body: formData });
374
  if (!response.ok) throw new Error("Server Error " + response.status);
375
  const data = await response.json();
376
- jsonOutput.textContent = JSON.stringify(data, null, 2);
 
377
  isCatalogGenerated = true;
378
  } catch (error) {
379
  console.error("Agent Error:", error); alert("Pipeline failed: " + error.message);
@@ -382,11 +418,27 @@ startBtn.addEventListener('click', async (e) => {
382
  }
383
  });
384
  copyBtn.addEventListener('click', () => {
385
- navigator.clipboard.writeText(jsonOutput.innerText).then(() => {
386
  const originalIcon = copyIcon.innerText; copyIcon.innerText = 'check'; copyIcon.classList.add('text-green-400');
387
  setTimeout(() => { copyIcon.innerText = originalIcon; copyIcon.classList.remove('text-green-400'); }, 2000);
388
  });
389
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
390
  </script>
391
  </body>
392
  </html>
 
258
  </div>
259
  </div>
260
  <div class="flex-1 p-4 lg:p-5 overflow-auto custom-scrollbar font-mono text-xs lg:text-sm leading-6 lg:leading-7">
261
+ <div id="jsonOutput" class="w-full"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  </div>
263
  </div>
264
  </div>
 
272
  tailwind.config.theme.extend.keyframes = {
273
  shine: { '0%': { left: '-100%' }, '100%': { left: '200%' } }
274
  }
275
+ const INITIAL_DATA = {
276
+ "product_analysis": {
277
+ "title": "Noir Elite Series Artisan Timepiece",
278
+ "category": "Luxury / Accessories",
279
+ "features": ["Obsidian Finish", "Golden Accents", "Smart Haptic Interface"],
280
+ "seo_tags": ["#luxurywear", "#amberstyle", "#premiumtech"],
281
+ "sentiment_score": 0.99,
282
+ "market_fit": "Exceptional"
283
+ },
284
+ "deployment_status": "Authorized"
285
+ };
286
+ window.__currentJSONData = INITIAL_DATA;
287
+
288
+ function jsonToHTMLList(data, isRoot = true) {
289
+ if (typeof data !== 'object' || data === null) {
290
+ return `<span class="text-neutral-300 break-words">${data}</span>`;
291
+ }
292
+
293
+ const isArray = Array.isArray(data);
294
+ let html = `<ul class="space-y-2 ${isRoot ? '' : 'pl-4 lg:pl-6 border-l border-white/10 mt-2'}">`;
295
+
296
+ for (const key in data) {
297
+ if (data.hasOwnProperty(key)) {
298
+ const value = data[key];
299
+ const isValueObject = typeof value === 'object' && value !== null;
300
+
301
+ html += `<li class="relative">`;
302
+
303
+ if (!isRoot) {
304
+ html += `<span class="absolute -left-[20px] lg:-left-[28px] top-2.5 size-1.5 rounded-full bg-amber-500/50"></span>`;
305
+ }
306
+
307
+ if (!isArray) {
308
+ html += `<strong class="text-amber-400 font-semibold mr-2">${key}:</strong>`;
309
+ }
310
+
311
+ if (isValueObject) {
312
+ html += `<div class="mt-1">${jsonToHTMLList(value, false)}</div>`;
313
+ } else {
314
+ if (isArray) {
315
+ html += `<span class="inline-block bg-amber-500/10 border border-amber-500/20 rounded-full px-2.5 py-0.5 text-xs text-amber-200 mt-1">${value}</span>`;
316
+ } else {
317
+ html += `<span class="text-neutral-300 break-words">${value}</span>`;
318
+ }
319
+ }
320
+ html += `</li>`;
321
+ }
322
+ }
323
+ html += `</ul>`;
324
+ return html;
325
+ }
326
  const dropZone = document.getElementById('dropZone');
327
  const fileInput = document.getElementById('fileInput');
328
  const startBtn = document.getElementById('startBtn');
 
408
  const response = await fetch('/generate-catalog', { method: 'POST', body: formData });
409
  if (!response.ok) throw new Error("Server Error " + response.status);
410
  const data = await response.json();
411
+ window.__currentJSONData = data;
412
+ jsonOutput.innerHTML = jsonToHTMLList(data);
413
  isCatalogGenerated = true;
414
  } catch (error) {
415
  console.error("Agent Error:", error); alert("Pipeline failed: " + error.message);
 
418
  }
419
  });
420
  copyBtn.addEventListener('click', () => {
421
+ navigator.clipboard.writeText(JSON.stringify(window.__currentJSONData, null, 2)).then(() => {
422
  const originalIcon = copyIcon.innerText; copyIcon.innerText = 'check'; copyIcon.classList.add('text-green-400');
423
  setTimeout(() => { copyIcon.innerText = originalIcon; copyIcon.classList.remove('text-green-400'); }, 2000);
424
  });
425
  });
426
+
427
+ const downloadBtn = document.getElementById('downloadBtn');
428
+ if (downloadBtn) {
429
+ downloadBtn.addEventListener('click', () => {
430
+ const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(window.__currentJSONData, null, 2));
431
+ const downloadAnchorNode = document.createElement('a');
432
+ downloadAnchorNode.setAttribute("href", dataStr);
433
+ downloadAnchorNode.setAttribute("download", "stylesync_output.json");
434
+ document.body.appendChild(downloadAnchorNode);
435
+ downloadAnchorNode.click();
436
+ downloadAnchorNode.remove();
437
+ });
438
+ }
439
+
440
+ // Initialize placeholder data
441
+ jsonOutput.innerHTML = jsonToHTMLList(window.__currentJSONData);
442
  </script>
443
  </body>
444
  </html>
glassui.html CHANGED
@@ -254,23 +254,7 @@
254
  </div>
255
  </div>
256
  <div class="flex-1 p-4 lg:p-5 overflow-auto custom-scrollbar font-mono text-xs lg:text-sm leading-6 lg:leading-7">
257
- <pre><code class="language-json block" id="jsonOutput"><span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">01</span><span class="text-amber-400">{</span>
258
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">02</span> <span class="text-amber-600">"product_analysis"</span><span class="text-neutral-400">:</span> <span class="text-amber-400">{</span>
259
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">03</span> <span class="text-amber-600">"title"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Noir Elite Series Artisan Timepiece"</span><span class="text-neutral-400">,</span>
260
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">04</span> <span class="text-amber-600">"category"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Luxury / Accessories"</span><span class="text-neutral-400">,</span>
261
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">05</span> <span class="text-amber-600">"features"</span><span class="text-neutral-400">:</span> <span class="text-amber-400">[</span>
262
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">06</span> <span class="text-amber-200">"Obsidian Finish"</span><span class="text-neutral-400">,</span>
263
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">07</span> <span class="text-amber-200">"Golden Accents"</span><span class="text-neutral-400">,</span>
264
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">08</span> <span class="text-amber-200">"Smart Haptic Interface"</span>
265
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">09</span> <span class="text-amber-400">]</span><span class="text-neutral-400">,</span>
266
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">10</span> <span class="text-amber-600">"seo_tags"</span><span class="text-neutral-400">:</span> <span class="text-amber-400">[</span>
267
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">11</span> <span class="text-amber-200">"#luxurywear"</span><span class="text-neutral-400">,</span> <span class="text-amber-200">"#amberstyle"</span><span class="text-neutral-400">,</span> <span class="text-amber-200">"#premiumtech"</span>
268
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">12</span> <span class="text-amber-400">]</span><span class="text-neutral-400">,</span>
269
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">13</span> <span class="text-amber-600">"sentiment_score"</span><span class="text-neutral-400">:</span> <span class="text-amber-500">0.99</span><span class="text-neutral-400">,</span>
270
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">14</span> <span class="text-amber-600">"market_fit"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Exceptional"</span>
271
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">15</span> <span class="text-amber-400">}</span><span class="text-neutral-400">,</span>
272
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">16</span> <span class="text-amber-600">"deployment_status"</span><span class="text-neutral-400">:</span> <span class="text-amber-200">"Authorized"</span>
273
- <span class="text-neutral-700 select-none mr-3 lg:mr-4 border-r border-neutral-800 pr-2">17</span><span class="text-amber-400">}</span></code></pre>
274
  </div>
275
  </div>
276
  </div>
@@ -289,6 +273,58 @@
289
  '100%': { left: '200%' },
290
  }
291
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  const dropZone = document.getElementById('dropZone');
293
  const fileInput = document.getElementById('fileInput');
294
  const startBtn = document.getElementById('startBtn');
@@ -376,7 +412,7 @@
376
  }
377
 
378
  copyBtn.addEventListener('click', () => {
379
- const textToCopy = jsonOutput.innerText;
380
  navigator.clipboard.writeText(textToCopy).then(() => {
381
  const originalIcon = copyIcon.innerText;
382
  copyIcon.style.opacity = '0';
@@ -405,7 +441,7 @@
405
  });
406
  });
407
  downloadBtn.addEventListener('click', () => {
408
- const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(jsonOutput.innerText);
409
  const downloadAnchorNode = document.createElement('a');
410
  downloadAnchorNode.setAttribute("href", dataStr);
411
  downloadAnchorNode.setAttribute("download", "stylesync_output.json");
@@ -428,6 +464,9 @@
428
  startBtn.classList.add('animate-pulse-slow', 'animate-glow-pulse');
429
  }, 1500);
430
  });
 
 
 
431
  </script>
432
 
433
  </body></html>
 
254
  </div>
255
  </div>
256
  <div class="flex-1 p-4 lg:p-5 overflow-auto custom-scrollbar font-mono text-xs lg:text-sm leading-6 lg:leading-7">
257
+ <div id="jsonOutput" class="w-full"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
  </div>
259
  </div>
260
  </div>
 
273
  '100%': { left: '200%' },
274
  }
275
  }
276
+
277
+ const INITIAL_DATA = {
278
+ "product_analysis": {
279
+ "title": "Noir Elite Series Artisan Timepiece",
280
+ "category": "Luxury / Accessories",
281
+ "features": ["Obsidian Finish", "Golden Accents", "Smart Haptic Interface"],
282
+ "seo_tags": ["#luxurywear", "#amberstyle", "#premiumtech"],
283
+ "sentiment_score": 0.99,
284
+ "market_fit": "Exceptional"
285
+ },
286
+ "deployment_status": "Authorized"
287
+ };
288
+ window.__currentJSONData = INITIAL_DATA;
289
+
290
+ function jsonToHTMLList(data, isRoot = true) {
291
+ if (typeof data !== 'object' || data === null) {
292
+ return `<span class="text-neutral-300 break-words">${data}</span>`;
293
+ }
294
+
295
+ const isArray = Array.isArray(data);
296
+ let html = `<ul class="space-y-2 ${isRoot ? '' : 'pl-4 lg:pl-6 border-l border-white/10 mt-2'}">`;
297
+
298
+ for (const key in data) {
299
+ if (data.hasOwnProperty(key)) {
300
+ const value = data[key];
301
+ const isValueObject = typeof value === 'object' && value !== null;
302
+
303
+ html += `<li class="relative">`;
304
+
305
+ if (!isRoot) {
306
+ html += `<span class="absolute -left-[20px] lg:-left-[28px] top-2.5 size-1.5 rounded-full bg-amber-500/50"></span>`;
307
+ }
308
+
309
+ if (!isArray) {
310
+ html += `<strong class="text-amber-400 font-semibold mr-2">${key}:</strong>`;
311
+ }
312
+
313
+ if (isValueObject) {
314
+ html += `<div class="mt-1">${jsonToHTMLList(value, false)}</div>`;
315
+ } else {
316
+ if (isArray) {
317
+ html += `<span class="inline-block bg-amber-500/10 border border-amber-500/20 rounded-full px-2.5 py-0.5 text-xs text-amber-200 mt-1">${value}</span>`;
318
+ } else {
319
+ html += `<span class="text-neutral-300 break-words">${value}</span>`;
320
+ }
321
+ }
322
+ html += `</li>`;
323
+ }
324
+ }
325
+ html += `</ul>`;
326
+ return html;
327
+ }
328
  const dropZone = document.getElementById('dropZone');
329
  const fileInput = document.getElementById('fileInput');
330
  const startBtn = document.getElementById('startBtn');
 
412
  }
413
 
414
  copyBtn.addEventListener('click', () => {
415
+ const textToCopy = JSON.stringify(window.__currentJSONData, null, 2);
416
  navigator.clipboard.writeText(textToCopy).then(() => {
417
  const originalIcon = copyIcon.innerText;
418
  copyIcon.style.opacity = '0';
 
441
  });
442
  });
443
  downloadBtn.addEventListener('click', () => {
444
+ const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(window.__currentJSONData, null, 2));
445
  const downloadAnchorNode = document.createElement('a');
446
  downloadAnchorNode.setAttribute("href", dataStr);
447
  downloadAnchorNode.setAttribute("download", "stylesync_output.json");
 
464
  startBtn.classList.add('animate-pulse-slow', 'animate-glow-pulse');
465
  }, 1500);
466
  });
467
+
468
+ // Initialize placeholder data
469
+ jsonOutput.innerHTML = jsonToHTMLList(window.__currentJSONData);
470
  </script>
471
 
472
  </body></html>