lightview 2.3.8 → 2.4.4

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 (38) hide show
  1. package/.gemini/CODE_ANALYSIS_AND_IMPROVEMENT_PLAN.md +56 -0
  2. package/AI-GUIDANCE.md +259 -0
  3. package/README.md +35 -0
  4. package/components/data-display/diff.js +36 -4
  5. package/docs/api/hypermedia.html +75 -5
  6. package/docs/api/index.html +3 -3
  7. package/docs/api/nav.html +0 -16
  8. package/docs/articles/html-vs-json-partials.md +102 -0
  9. package/docs/articles/lightview-vs-htmx.md +610 -0
  10. package/docs/benchmarks/tagged-fragment.js +36 -0
  11. package/docs/components/chart.html +157 -210
  12. package/docs/components/component-nav.html +1 -1
  13. package/docs/components/diff.html +33 -21
  14. package/docs/components/gallery.html +107 -4
  15. package/docs/components/index.css +18 -3
  16. package/docs/components/index.html +20 -9
  17. package/docs/dom-benchmark.html +644 -0
  18. package/docs/getting-started/index.html +2 -2
  19. package/docs/hypermedia/index.html +391 -0
  20. package/docs/hypermedia/nav.html +17 -0
  21. package/docs/index.html +128 -18
  22. package/index.html +59 -10
  23. package/lightview-all.js +223 -67
  24. package/lightview-cdom.js +1 -2
  25. package/lightview-x.js +144 -13
  26. package/lightview.js +85 -277
  27. package/package.json +2 -2
  28. package/src/lightview-cdom.js +1 -5
  29. package/src/lightview-x.js +158 -27
  30. package/src/lightview.js +94 -60
  31. package/docs/articles/calculator-no-javascript-hackernoon.md +0 -283
  32. package/docs/articles/calculator-no-javascript.md +0 -290
  33. package/docs/articles/part1-reference.md +0 -236
  34. package/lightview.js.bak +0 -1
  35. package/test-xpath.html +0 -63
  36. package/test_error.txt +0 -0
  37. package/test_output.txt +0 -0
  38. package/test_output_full.txt +0 -0
@@ -0,0 +1,644 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-theme="light">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>DOM Update Performance Benchmark | Lightview</title>
8
+ <link href="https://cdn.jsdelivr.net/npm/daisyui@4.12.23/dist/full.min.css" rel="stylesheet" type="text/css" />
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+ <link rel="preconnect" href="https://fonts.googleapis.com">
11
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
+ <link
13
+ href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Outfit:wght@400;600;700&display=swap"
14
+ rel="stylesheet">
15
+ <style>
16
+ :root {
17
+ --site-primary: #8338ec;
18
+ --site-secondary: #3a86ff;
19
+ --site-accent: #ff006e;
20
+ }
21
+
22
+ body {
23
+ font-family: 'Inter', sans-serif;
24
+ background: #f8fafc;
25
+ }
26
+
27
+ h1,
28
+ h2,
29
+ h3 {
30
+ font-family: 'Outfit', sans-serif;
31
+ }
32
+
33
+ .btn-benchmark {
34
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
35
+ }
36
+
37
+ .btn-benchmark:hover {
38
+ transform: translateY(-2px);
39
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
40
+ }
41
+
42
+ .result-card {
43
+ background: white;
44
+ border-radius: 1rem;
45
+ padding: 1.5rem;
46
+ border: 1px solid #e2e8f0;
47
+ transition: all 0.3s ease;
48
+ }
49
+
50
+ .result-card.active {
51
+ border-color: var(--site-primary);
52
+ box-shadow: 0 4px 6px -1px rgba(131, 56, 236, 0.1);
53
+ }
54
+
55
+ #benchmark-target {
56
+ max-height: 400px;
57
+ overflow: auto;
58
+ border: 1px solid #e2e8f0;
59
+ border-radius: 0.5rem;
60
+ background: white;
61
+ }
62
+
63
+ .progress-bar-custom {
64
+ height: 8px;
65
+ border-radius: 4px;
66
+ background: #e2e8f0;
67
+ overflow: hidden;
68
+ margin-top: 0.5rem;
69
+ }
70
+
71
+ .progress-fill {
72
+ height: 100%;
73
+ background: var(--site-primary);
74
+ width: 0%;
75
+ transition: width 0.5s ease;
76
+ }
77
+
78
+ /* Benchmark Target Styles */
79
+ .benchmark-container {
80
+ padding: 1rem;
81
+ }
82
+
83
+ .items-grid {
84
+ display: grid;
85
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
86
+ gap: 1rem;
87
+ margin-top: 1rem;
88
+ }
89
+
90
+ .item-card {
91
+ background: #fff;
92
+ border: 1px solid #e2e8f0;
93
+ border-radius: 0.5rem;
94
+ padding: 0.75rem;
95
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
96
+ }
97
+
98
+ .item-title {
99
+ font-weight: bold;
100
+ font-size: 0.9rem;
101
+ color: var(--site-primary);
102
+ margin-bottom: 0.25rem;
103
+ }
104
+
105
+ .item-description {
106
+ font-size: 0.8rem;
107
+ color: #64748b;
108
+ line-height: 1.2;
109
+ }
110
+
111
+ .item-footer {
112
+ margin-top: 0.5rem;
113
+ font-size: 0.7rem;
114
+ color: #94a3b8;
115
+ border-top: 1px solid #f1f5f9;
116
+ padding-top: 0.25rem;
117
+ }
118
+ </style>
119
+ </head>
120
+
121
+ <body class="p-4 md:p-8">
122
+ <div class="max-w-6xl mx-auto">
123
+ <header class="mb-12 text-center">
124
+ <h1
125
+ class="text-4xl md:text-5xl font-extrabold mb-4 bg-clip-text text-transparent bg-gradient-to-r from-purple-600 to-blue-600">
126
+ DOM Update Benchmark
127
+ </h1>
128
+ <p class="text-xl text-slate-600 max-w-2xl mx-auto">
129
+ Comparing different strategies for updating DOM fragments.
130
+ </p>
131
+ </header>
132
+
133
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-12">
134
+ <!-- Controls -->
135
+ <div class="lg:col-span-1 space-y-4">
136
+ <div class="card bg-white shadow-sm border border-slate-200">
137
+ <div class="card-body">
138
+ <h2 class="card-title text-slate-800">Configuration</h2>
139
+ <div class="form-control w-full">
140
+ <label class="label">
141
+ <span class="label-text text-xs font-bold uppercase text-slate-500">Number of
142
+ Rows</span>
143
+ </label>
144
+ <input type="number" id="row-count" value="1000" class="input input-bordered w-full h-10" />
145
+ </div>
146
+ <div class="form-control w-full mt-2">
147
+ <label class="label">
148
+ <span class="label-text text-xs font-bold uppercase text-slate-500">Number of
149
+ Cycles</span>
150
+ </label>
151
+ <input type="number" id="cycle-count" value="10" class="input input-bordered w-full h-10" />
152
+ </div>
153
+ <div class="mt-6 flex flex-col gap-3">
154
+ <button onclick="runBenchmark('innerHTML')"
155
+ class="btn btn-outline btn-benchmark border-slate-300">Run innerHTML</button>
156
+ <button onclick="runBenchmark('DOMParser')"
157
+ class="btn btn-outline btn-benchmark border-slate-300">Run DOMParser</button>
158
+ <button onclick="runBenchmark('RawVDOM')"
159
+ class="btn btn-outline btn-benchmark border-slate-300">Run Raw vDOM</button>
160
+ <button onclick="runBenchmark('RawoDOM')"
161
+ class="btn btn-outline btn-benchmark border-slate-300">Run Raw oDOM</button>
162
+ <button onclick="runBenchmark('vDOM')"
163
+ class="btn btn-outline btn-benchmark border-slate-300">Run Lightview vDOM</button>
164
+ <button onclick="runBenchmark('TaggedScript')"
165
+ class="btn btn-outline btn-benchmark border-slate-300">Run Lightview Tagged
166
+ Script</button>
167
+ <div class="divider"></div>
168
+ <button onclick="runAll()" class="btn btn-primary text-white">Run All Benchmarks</button>
169
+ <button onclick="clearTarget()" class="btn btn-ghost">Clear Target</button>
170
+ </div>
171
+ </div>
172
+ </div>
173
+ </div>
174
+
175
+ <!-- Results -->
176
+ <div class="lg:col-span-2 space-y-4">
177
+ <div id="results-container" class="grid grid-cols-1 sm:grid-cols-2 gap-4">
178
+ <!-- Template for result cards -->
179
+ <div class="result-card" id="res-innerHTML">
180
+ <div class="flex justify-between items-start mb-2">
181
+ <h3 class="font-bold text-slate-500 uppercase text-xs tracking-widest">innerHTML</h3>
182
+ <div class="flex flex-col items-end gap-1">
183
+ <div class="badge badge-outline badge-xs opacity-50"><span class="size">--</span> KB
184
+ </div>
185
+ <div class="badge badge-accent badge-xs text-white opacity-70"><span
186
+ class="gzip-size">--</span> KB gzip</div>
187
+ <div class="badge badge-ghost badge-sm opacity-50">ms</div>
188
+ </div>
189
+ </div>
190
+ <div class="text-3xl font-black text-slate-800 mb-4"><span class="time">--</span></div>
191
+
192
+ <div class="grid grid-cols-4 gap-2 text-[10px] uppercase font-bold text-slate-400 mb-3">
193
+ <div>Min <span class="min block text-slate-600">--</span></div>
194
+ <div>Avg <span class="avg block text-slate-600">--</span></div>
195
+ <div>Max <span class="max block text-slate-600">--</span></div>
196
+ <div class="text-right">±SD <span class="std block text-slate-600">--</span></div>
197
+ </div>
198
+
199
+ <div class="progress-bar-custom">
200
+ <div class="progress-fill" id="fill-innerHTML"></div>
201
+ </div>
202
+ </div>
203
+
204
+ <div class="result-card" id="res-DOMParser">
205
+ <div class="flex justify-between items-start mb-2">
206
+ <h3 class="font-bold text-slate-500 uppercase text-xs tracking-widest">DOMParser</h3>
207
+ <div class="flex flex-col items-end gap-1">
208
+ <div class="badge badge-outline badge-xs opacity-50"><span class="size">--</span> KB
209
+ </div>
210
+ <div class="badge badge-accent badge-xs text-white opacity-70"><span
211
+ class="gzip-size">--</span> KB gzip</div>
212
+ <div class="badge badge-ghost badge-sm opacity-50">ms</div>
213
+ </div>
214
+ </div>
215
+ <div class="text-3xl font-black text-slate-800 mb-4"><span class="time">--</span></div>
216
+
217
+ <div class="grid grid-cols-4 gap-2 text-[10px] uppercase font-bold text-slate-400 mb-3">
218
+ <div>Min <span class="min block text-slate-600">--</span></div>
219
+ <div>Avg <span class="avg block text-slate-600">--</span></div>
220
+ <div>Max <span class="max block text-slate-600">--</span></div>
221
+ <div class="text-right">±SD <span class="std block text-slate-600">--</span></div>
222
+ </div>
223
+
224
+ <div class="progress-bar-custom">
225
+ <div class="progress-fill" id="fill-DOMParser"></div>
226
+ </div>
227
+ </div>
228
+
229
+ <div class="result-card" id="res-RawVDOM">
230
+ <div class="flex justify-between items-start mb-2">
231
+ <h3 class="font-bold text-slate-500 uppercase text-xs tracking-widest">Raw vDOM (No Lib)
232
+ </h3>
233
+ <div class="flex flex-col items-end gap-1">
234
+ <div class="badge badge-outline badge-xs opacity-50"><span class="size">--</span> KB
235
+ </div>
236
+ <div class="badge badge-accent badge-xs text-white opacity-70"><span
237
+ class="gzip-size">--</span> KB gzip</div>
238
+ <div class="badge badge-ghost badge-sm opacity-50">ms</div>
239
+ </div>
240
+ </div>
241
+ <div class="text-3xl font-black text-slate-800 mb-4"><span class="time">--</span></div>
242
+
243
+ <div class="grid grid-cols-4 gap-2 text-[10px] uppercase font-bold text-slate-400 mb-3">
244
+ <div>Min <span class="min block text-slate-600">--</span></div>
245
+ <div>Avg <span class="avg block text-slate-600">--</span></div>
246
+ <div>Max <span class="max block text-slate-600">--</span></div>
247
+ <div class="text-right">±SD <span class="std block text-slate-600">--</span></div>
248
+ </div>
249
+
250
+ <div class="progress-bar-custom">
251
+ <div class="progress-fill" id="fill-RawVDOM"></div>
252
+ </div>
253
+ </div>
254
+
255
+ <div class="result-card" id="res-RawoDOM">
256
+ <div class="flex justify-between items-start mb-2">
257
+ <h3 class="font-bold text-slate-500 uppercase text-xs tracking-widest">Raw oDOM (No Lib)
258
+ </h3>
259
+ <div class="flex flex-col items-end gap-1">
260
+ <div class="badge badge-outline badge-xs opacity-50"><span class="size">--</span> KB
261
+ </div>
262
+ <div class="badge badge-accent badge-xs text-white opacity-70"><span
263
+ class="gzip-size">--</span> KB gzip</div>
264
+ <div class="badge badge-ghost badge-sm opacity-50">ms</div>
265
+ </div>
266
+ </div>
267
+ <div class="text-3xl font-black text-slate-800 mb-4"><span class="time">--</span></div>
268
+
269
+ <div class="grid grid-cols-4 gap-2 text-[10px] uppercase font-bold text-slate-400 mb-3">
270
+ <div>Min <span class="min block text-slate-600">--</span></div>
271
+ <div>Avg <span class="avg block text-slate-600">--</span></div>
272
+ <div>Max <span class="max block text-slate-600">--</span></div>
273
+ <div class="text-right">±SD <span class="std block text-slate-600">--</span></div>
274
+ </div>
275
+
276
+ <div class="progress-bar-custom">
277
+ <div class="progress-fill" id="fill-RawoDOM"></div>
278
+ </div>
279
+ </div>
280
+
281
+ <div class="result-card" id="res-vDOM">
282
+ <div class="flex justify-between items-start mb-2">
283
+ <h3 class="font-bold text-slate-500 uppercase text-xs tracking-widest">Lightview vDOM</h3>
284
+ <div class="flex flex-col items-end gap-1">
285
+ <div class="badge badge-outline badge-xs opacity-50"><span class="size">--</span> KB
286
+ </div>
287
+ <div class="badge badge-accent badge-xs text-white opacity-70"><span
288
+ class="gzip-size">--</span> KB gzip</div>
289
+ <div class="badge badge-ghost badge-sm opacity-50">ms</div>
290
+ </div>
291
+ </div>
292
+ <div class="text-3xl font-black text-slate-800 mb-4"><span class="time">--</span></div>
293
+
294
+ <div class="grid grid-cols-4 gap-2 text-[10px] uppercase font-bold text-slate-400 mb-3">
295
+ <div>Min <span class="min block text-slate-600">--</span></div>
296
+ <div>Avg <span class="avg block text-slate-600">--</span></div>
297
+ <div>Max <span class="max block text-slate-600">--</span></div>
298
+ <div class="text-right">±SD <span class="std block text-slate-600">--</span></div>
299
+ </div>
300
+
301
+ <div class="progress-bar-custom">
302
+ <div class="progress-fill" id="fill-vDOM"></div>
303
+ </div>
304
+ </div>
305
+
306
+ <div class="result-card" id="res-TaggedScript">
307
+ <div class="flex justify-between items-start mb-2">
308
+ <h3 class="font-bold text-slate-500 uppercase text-xs tracking-widest">Lightview Tagged
309
+ Script</h3>
310
+ <div class="flex flex-col items-end gap-1">
311
+ <div class="badge badge-outline badge-xs opacity-50"><span class="size">--</span> KB
312
+ </div>
313
+ <div class="badge badge-accent badge-xs text-white opacity-70"><span
314
+ class="gzip-size">--</span> KB gzip</div>
315
+ <div class="badge badge-ghost badge-sm opacity-50">ms</div>
316
+ </div>
317
+ </div>
318
+ <div class="text-3xl font-black text-slate-800 mb-4"><span class="time">--</span></div>
319
+
320
+ <div class="grid grid-cols-4 gap-2 text-[10px] uppercase font-bold text-slate-400 mb-3">
321
+ <div>Min <span class="min block text-slate-600">--</span></div>
322
+ <div>Avg <span class="avg block text-slate-600">--</span></div>
323
+ <div>Max <span class="max block text-slate-600">--</span></div>
324
+ <div class="text-right">±SD <span class="std block text-slate-600">--</span></div>
325
+ </div>
326
+
327
+ <div class="progress-bar-custom">
328
+ <div class="progress-fill" id="fill-TaggedScript"></div>
329
+ </div>
330
+ </div>
331
+ </div>
332
+
333
+ <div class="card bg-white shadow-sm border border-slate-200">
334
+ <div class="card-body">
335
+ <h2 class="card-title text-slate-800">Benchmark Target</h2>
336
+ <p class="text-sm text-slate-500 mb-2">Live DOM update area:</p>
337
+ <div id="benchmark-target">
338
+ <div class="p-8 text-center text-slate-400 italic">Target is empty. Run a benchmark to
339
+ populate.</div>
340
+ </div>
341
+ </div>
342
+ </div>
343
+ </div>
344
+ </div>
345
+ </div>
346
+
347
+ <!-- Core Libraries -->
348
+ <script src="/lightview.js"></script>
349
+ <script src="/lightview-x.js"></script>
350
+
351
+ <script>
352
+ function generateHTML(count) {
353
+ let html = '<section class="benchmark-container"><header><h2>Benchmark Results</h2></header><div class="items-grid">';
354
+ for (let i = 0; i < count; i++) {
355
+ html += `<article class="item-card"><header><h3 class="item-title">Item ${i}</h3></header><div class="item-content"><p class="item-description">Detailed description for item ${i} in the benchmark list.</p></div><footer class="item-footer"><span>Metadata for item ${i}</span></footer></article>`;
356
+ }
357
+ html += '</div></section>';
358
+ return html;
359
+ }
360
+
361
+ function generateVDOM(count) {
362
+ const items = [];
363
+ for (let i = 0; i < count; i++) {
364
+ items.push({
365
+ tag: 'article',
366
+ attributes: { class: 'item-card' },
367
+ children: [
368
+ { tag: 'header', children: [{ tag: 'h3', attributes: { class: 'item-title' }, children: [`Item ${i}`] }] },
369
+ { tag: 'div', attributes: { class: 'item-content' }, children: [{ tag: 'p', attributes: { class: 'item-description' }, children: [`Detailed description for item ${i} in the benchmark list.`] }] },
370
+ { tag: 'footer', attributes: { class: 'item-footer' }, children: [{ tag: 'span', children: [`Metadata for item ${i}`] }] }
371
+ ]
372
+ });
373
+ }
374
+ return {
375
+ tag: 'section',
376
+ attributes: { class: 'benchmark-container' },
377
+ children: [
378
+ { tag: 'header', children: [{ tag: 'h2', children: ['Benchmark Results'] }] },
379
+ { tag: 'div', attributes: { class: 'items-grid' }, children: items }
380
+ ]
381
+ };
382
+ }
383
+
384
+ // Dedicated helper to convert vDOM to DOM without Lightview
385
+ function vdomToDOM(vnode) {
386
+ if (typeof vnode !== 'object' || vnode === null) {
387
+ return document.createTextNode(vnode);
388
+ }
389
+
390
+ const el = document.createElement(vnode.tag);
391
+
392
+ const attrs = vnode.attributes;
393
+ if (attrs) {
394
+ for (const key in attrs) {
395
+ if (key === 'class') el.className = attrs[key];
396
+ else el.setAttribute(key, attrs[key]);
397
+ }
398
+ }
399
+
400
+ const children = vnode.children;
401
+ if (children) {
402
+ el.append(...children.map(vdomToDOM));
403
+ }
404
+
405
+ return el;
406
+ }
407
+
408
+ function generateODOM(count) {
409
+ const items = [];
410
+ for (let i = 0; i < count; i++) {
411
+ items.push({
412
+ article: {
413
+ class: 'item-card',
414
+ children: [
415
+ { header: { h3: { class: 'item-title', children: [`Item ${i}`] } } },
416
+ { div: { class: 'item-content', p: { class: 'item-description', children: [`Detailed description for item ${i} in the benchmark list.`] } } },
417
+ { footer: { class: 'item-footer', span: `Metadata for item ${i}` } }
418
+ ]
419
+ }
420
+ });
421
+ }
422
+ return {
423
+ section: {
424
+ class: 'benchmark-container',
425
+ children: [
426
+ { header: { h2: 'Benchmark Results' } },
427
+ { div: { class: 'items-grid', children: items } }
428
+ ]
429
+ }
430
+ };
431
+ }
432
+
433
+ function odomToDOM(onode) {
434
+ if (typeof onode !== 'object' || onode === null) {
435
+ return document.createTextNode(onode);
436
+ }
437
+
438
+ const tag = Object.keys(onode)[0];
439
+ const el = document.createElement(tag);
440
+ const content = onode[tag];
441
+
442
+ if (typeof content === 'object' && content !== null && !Array.isArray(content)) {
443
+ for (const key in content) {
444
+ if (key === 'children') {
445
+ el.append(...content.children.map(odomToDOM));
446
+ } else if (key === 'class') {
447
+ el.className = content[key];
448
+ } else if (typeof content[key] === 'object' && !Array.isArray(content[key])) {
449
+ // Nested shorthand tag
450
+ const child = {};
451
+ child[key] = content[key];
452
+ el.append(odomToDOM(child));
453
+ } else if (Array.isArray(content[key])) {
454
+ // Property with array value (e.g., children shorthand)
455
+ el.append(...content[key].map(odomToDOM));
456
+ } else {
457
+ el.setAttribute(key, content[key]);
458
+ }
459
+ }
460
+ } else {
461
+ if (Array.isArray(content)) {
462
+ el.append(...content.map(odomToDOM));
463
+ } else {
464
+ el.append(odomToDOM(content));
465
+ }
466
+ }
467
+
468
+ return el;
469
+ }
470
+
471
+ async function getGzipSize(str) {
472
+ try {
473
+ const stream = new Blob([str]).stream();
474
+ const compressedStream = stream.pipeThrough(new CompressionStream('gzip'));
475
+ const chunks = [];
476
+ for await (const chunk of compressedStream) {
477
+ chunks.push(chunk);
478
+ }
479
+ const blob = new Blob(chunks);
480
+ return blob.size;
481
+ } catch (e) {
482
+ console.error("CompressionStream not supported", e);
483
+ return 0;
484
+ }
485
+ }
486
+
487
+ const target = document.getElementById('benchmark-target');
488
+
489
+ function calculateStats(times) {
490
+ const min = Math.min(...times);
491
+ const max = Math.max(...times);
492
+ const avg = times.reduce((a, b) => a + b, 0) / times.length;
493
+ const std = Math.sqrt(times.map(x => Math.pow(x - avg, 2)).reduce((a, b) => a + b, 0) / times.length);
494
+ return { min, max, avg, std };
495
+ }
496
+
497
+ function updateUIResult(id, stats, size, gzipSize) {
498
+ const card = document.getElementById(`res-${id}`);
499
+ card.querySelector('.time').innerText = stats.avg.toFixed(2);
500
+ card.querySelector('.min').innerText = stats.min.toFixed(2);
501
+ card.querySelector('.avg').innerText = stats.avg.toFixed(2);
502
+ card.querySelector('.max').innerText = stats.max.toFixed(2);
503
+ card.querySelector('.std').innerText = stats.std.toFixed(2);
504
+ if (size !== undefined) {
505
+ card.querySelector('.size').innerText = (size / 1024).toFixed(2);
506
+ }
507
+ if (gzipSize !== undefined) {
508
+ card.querySelector('.gzip-size').innerText = (gzipSize / 1024).toFixed(2);
509
+ }
510
+
511
+ card.classList.add('active');
512
+
513
+ const fill = document.getElementById(`fill-${id}`);
514
+ const percentage = Math.min(100, (stats.avg / 200) * 100);
515
+ fill.style.width = `${percentage}%`;
516
+ }
517
+
518
+ function clearTarget() {
519
+ target.replaceChildren();
520
+ target.innerHTML = '<div class="p-8 text-center text-slate-400 italic">Target is empty.</div>';
521
+ ['innerHTML', 'DOMParser', 'vDOM', 'RawVDOM', 'RawoDOM', 'TaggedScript'].forEach(id => {
522
+ const card = document.getElementById(`res-${id}`);
523
+ card.classList.remove('active');
524
+ card.querySelector('.time').innerText = '--';
525
+ card.querySelector('.min').innerText = '--';
526
+ card.querySelector('.avg').innerText = '--';
527
+ card.querySelector('.max').innerText = '--';
528
+ card.querySelector('.std').innerText = '--';
529
+ card.querySelector('.size').innerText = '--';
530
+ card.querySelector('.gzip-size').innerText = '--';
531
+ const fillEl = document.getElementById(`fill-${id}`);
532
+ if (fillEl) fillEl.style.width = '0%';
533
+ });
534
+ }
535
+
536
+ async function runCycle(type, data, count) {
537
+ // 2. Settle phase (allow GC to run and memory pressure to stabilize)
538
+ if (window.gc) window.gc();
539
+ // Give 100ms for browser housekeeping between cycles
540
+ await new Promise(r => setTimeout(r, 100));
541
+
542
+ let time = 0;
543
+ if (type === 'innerHTML') {
544
+ const start = performance.now();
545
+ target.innerHTML = data;
546
+ time = performance.now() - start;
547
+ } else if (type === 'DOMParser') {
548
+ const parser = new DOMParser();
549
+ const start = performance.now();
550
+ const doc = parser.parseFromString(data, 'text/html');
551
+ target.replaceChildren(...doc.body.childNodes);
552
+ time = performance.now() - start;
553
+ } else if (type === 'vDOM') {
554
+ const start = performance.now();
555
+ const json = JSON.parse(data);
556
+ const { domEl } = Lightview.element(json.tag, json.attributes, json.children);
557
+ target.replaceChildren(domEl);
558
+ time = performance.now() - start;
559
+ } else if (type === 'RawVDOM') {
560
+ const start = performance.now();
561
+ const json = JSON.parse(data);
562
+ const domEl = vdomToDOM(json);
563
+ target.replaceChildren(domEl);
564
+ time = performance.now() - start;
565
+ } else if (type === 'RawoDOM') {
566
+ const start = performance.now();
567
+ const json = JSON.parse(data);
568
+ const domEl = odomToDOM(json);
569
+ target.replaceChildren(domEl);
570
+ time = performance.now() - start;
571
+ } else if (type === 'TaggedScript') {
572
+ const existing = document.getElementById('tagged-script-tag');
573
+ if (existing) existing.remove();
574
+
575
+ await new Promise(resolve => {
576
+ window.__resolveTaggedBenchmark = resolve;
577
+ const script = document.createElement('script');
578
+ script.id = 'tagged-script-tag';
579
+ script.src = `./benchmarks/tagged-fragment.js?count=${count}&t=${Date.now()}`;
580
+ document.body.appendChild(script);
581
+ });
582
+ time = window.__taggedBenchmarkTime;
583
+ }
584
+ return time;
585
+ }
586
+
587
+ async function runBenchmark(type, preGeneratedData = null) {
588
+ const count = parseInt(document.getElementById('row-count').value);
589
+ const cycles = parseInt(document.getElementById('cycle-count').value) || 1;
590
+ const times = [];
591
+
592
+ let data = preGeneratedData;
593
+
594
+ // 1. Preparation phase (outside of timing)
595
+ if (!data && type !== 'TaggedScript') {
596
+ if (type === 'innerHTML' || type === 'DOMParser') data = generateHTML(count);
597
+ if (type === 'vDOM' || type === 'RawVDOM') data = generateVDOM(count);
598
+ if (type === 'RawoDOM') data = generateODOM(count);
599
+ }
600
+
601
+ console.log(`Starting ${cycles} cycles for ${type}...`);
602
+ const stringData = type === 'TaggedScript' ? '' : (typeof data === 'string' ? data : JSON.stringify(data));
603
+ const size = stringData.length;
604
+ const gzipSize = size > 0 ? await getGzipSize(stringData) : 0;
605
+
606
+ for (let i = 0; i < cycles; i++) {
607
+ const time = await runCycle(type, stringData, count);
608
+ times.push(time);
609
+ }
610
+
611
+ const stats = calculateStats(times);
612
+ updateUIResult(type, stats, size, gzipSize);
613
+ return stats;
614
+ }
615
+
616
+ async function runAll() {
617
+ clearTarget();
618
+ const count = parseInt(document.getElementById('row-count').value);
619
+
620
+ console.log("Pre-generating data for Run All...");
621
+ const htmlData = generateHTML(count);
622
+ const vdomData = generateVDOM(count);
623
+ const odomData = generateODOM(count);
624
+
625
+ const configs = [
626
+ { id: 'innerHTML', data: htmlData },
627
+ { id: 'DOMParser', data: htmlData },
628
+ { id: 'RawVDOM', data: vdomData },
629
+ { id: 'RawoDOM', data: odomData },
630
+ { id: 'vDOM', data: vdomData },
631
+ { id: 'TaggedScript', data: null }
632
+ ];
633
+
634
+ for (const config of configs) {
635
+ await runBenchmark(config.id, config.data);
636
+ // Pause between benchmarks to ensure clean state
637
+ await new Promise(r => setTimeout(r, 500));
638
+ }
639
+ console.log("Run All completed.");
640
+ }
641
+ </script>
642
+ </body>
643
+
644
+ </html>
@@ -141,7 +141,7 @@ const { div, h1, p, style } = tags;
141
141
  // These functions return Lightview elements (access raw DOM via .domEl)
142
142
  const App = div({ class: 'hero' },
143
143
  h1('Welcome to Lightview'),
144
- p('Lightview is a library for building modern web interfaces.')
144
+ p('Lightview is a library for humans and LLMs to build modern web interfaces.')
145
145
  );
146
146
 
147
147
  // 2. The $ Function for Selections & Content
@@ -477,7 +477,7 @@ $('#app').content(App);`,
477
477
  $('#app').content(App);`,
478
478
  concepts: `
479
479
  <h3 style="margin-top: 0; color: var(--site-primary);">Step 4: Hypermedia + Multiple Formats</h3>
480
- <p>Lightview's <strong>hypermedia</strong> feature allows your application to load partial content dynamically from various file formats.</p>
480
+ <p>Lightview's <a href="/docs/hypermedia/">hypermedia</a> feature allows your application to load partial content dynamically from various file formats.</p>
481
481
 
482
482
  <h4>Key Concepts:</h4>
483
483
  <ul style="padding-left: 1.25rem; color: var(--site-text-secondary);">