lightview 1.8.2 → 2.0.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.
Files changed (262) hide show
  1. package/.codacy/cli.sh +149 -0
  2. package/.codacy/codacy.yaml +15 -0
  3. package/.github/instructions/codacy.instructions.md +72 -0
  4. package/.wranglerignore +21 -0
  5. package/README.md +1330 -19
  6. package/_headers +4 -0
  7. package/build.js +70 -0
  8. package/components/actions/button.js +151 -0
  9. package/components/actions/dropdown.js +120 -0
  10. package/components/actions/modal.js +146 -0
  11. package/components/actions/swap.js +118 -0
  12. package/components/daisyui.js +288 -0
  13. package/components/data-display/accordion.js +128 -0
  14. package/components/data-display/alert.js +112 -0
  15. package/components/data-display/avatar.js +170 -0
  16. package/components/data-display/badge.js +82 -0
  17. package/components/data-display/card.js +151 -0
  18. package/components/data-display/carousel.js +94 -0
  19. package/components/data-display/chart.js +220 -0
  20. package/components/data-display/chat.js +128 -0
  21. package/components/data-display/collapse.js +103 -0
  22. package/components/data-display/countdown.js +69 -0
  23. package/components/data-display/diff.js +111 -0
  24. package/components/data-display/kbd.js +65 -0
  25. package/components/data-display/loading.js +75 -0
  26. package/components/data-display/progress.js +79 -0
  27. package/components/data-display/radial-progress.js +88 -0
  28. package/components/data-display/skeleton.js +66 -0
  29. package/components/data-display/stats.js +159 -0
  30. package/components/data-display/table.js +146 -0
  31. package/components/data-display/timeline.js +146 -0
  32. package/components/data-display/toast.js +72 -0
  33. package/components/data-display/tooltip.js +74 -0
  34. package/components/data-input/checkbox.js +253 -0
  35. package/components/data-input/file-input.js +224 -0
  36. package/components/data-input/input.js +264 -0
  37. package/components/data-input/radio.js +338 -0
  38. package/components/data-input/range.js +204 -0
  39. package/components/data-input/rating.js +219 -0
  40. package/components/data-input/select.js +287 -0
  41. package/components/data-input/textarea.js +287 -0
  42. package/components/data-input/toggle.js +201 -0
  43. package/components/index.js +137 -0
  44. package/components/layout/divider.js +72 -0
  45. package/components/layout/drawer.js +142 -0
  46. package/components/layout/footer.js +100 -0
  47. package/components/layout/hero.js +109 -0
  48. package/components/layout/indicator.js +90 -0
  49. package/components/layout/join.js +78 -0
  50. package/components/layout/navbar.js +110 -0
  51. package/components/navigation/breadcrumbs.js +91 -0
  52. package/components/navigation/dock.js +103 -0
  53. package/components/navigation/menu.js +126 -0
  54. package/components/navigation/pagination.js +105 -0
  55. package/components/navigation/steps.js +89 -0
  56. package/components/navigation/tabs.css +177 -0
  57. package/components/navigation/tabs.js +123 -0
  58. package/components/theme/theme-switch.css +65 -0
  59. package/components/theme/theme-switch.js +177 -0
  60. package/docs/about.html +164 -0
  61. package/docs/api/computed.html +184 -0
  62. package/docs/api/effects.html +173 -0
  63. package/docs/api/elements.html +180 -0
  64. package/docs/api/enhance.html +225 -0
  65. package/docs/api/hypermedia.html +165 -0
  66. package/docs/api/index.html +178 -0
  67. package/docs/api/nav.html +18 -0
  68. package/docs/api/signals.html +136 -0
  69. package/docs/api/state.html +217 -0
  70. package/docs/assets/images/logo-favicon.svg +42 -0
  71. package/docs/assets/images/logo-static.svg +40 -0
  72. package/docs/assets/images/logo.svg +66 -0
  73. package/docs/assets/js/examplify.js +395 -0
  74. package/docs/assets/styles/site.css +1102 -0
  75. package/docs/assets/styles/themes.css +236 -0
  76. package/docs/components/accordion.html +439 -0
  77. package/docs/components/alert.html +528 -0
  78. package/docs/components/avatar.html +586 -0
  79. package/docs/components/badge.html +531 -0
  80. package/docs/components/breadcrumbs.html +278 -0
  81. package/docs/components/button.html +579 -0
  82. package/docs/components/card.html +561 -0
  83. package/docs/components/carousel.html +286 -0
  84. package/docs/components/chart-area.html +702 -0
  85. package/docs/components/chart-bar.html +782 -0
  86. package/docs/components/chart-column.html +735 -0
  87. package/docs/components/chart-line.html +794 -0
  88. package/docs/components/chart-pie.html +823 -0
  89. package/docs/components/chart.html +610 -15
  90. package/docs/components/chat.html +547 -0
  91. package/docs/components/checkbox.html +641 -0
  92. package/docs/components/collapse.html +536 -0
  93. package/docs/components/component-nav.html +53 -0
  94. package/docs/components/countdown.html +470 -0
  95. package/docs/components/diff.html +245 -0
  96. package/docs/components/divider.html +240 -0
  97. package/docs/components/dock.html +277 -0
  98. package/docs/components/drawer.html +515 -0
  99. package/docs/components/dropdown.html +479 -0
  100. package/docs/components/file-input.html +591 -0
  101. package/docs/components/footer.html +301 -0
  102. package/docs/components/gallery.html +504 -0
  103. package/docs/components/hero.html +264 -0
  104. package/docs/components/index.css +840 -0
  105. package/docs/components/index.html +735 -0
  106. package/docs/components/indicator.html +342 -0
  107. package/docs/components/input.html +644 -0
  108. package/docs/components/join.html +285 -0
  109. package/docs/components/kbd.html +322 -0
  110. package/docs/components/loading.html +521 -0
  111. package/docs/components/menu.html +461 -0
  112. package/docs/components/modal.html +639 -0
  113. package/docs/components/navbar.html +321 -0
  114. package/docs/components/pagination.html +279 -0
  115. package/docs/components/progress.html +514 -0
  116. package/docs/components/radial-progress.html +434 -0
  117. package/docs/components/radio.html +655 -0
  118. package/docs/components/range.html +611 -0
  119. package/docs/components/rating.html +642 -0
  120. package/docs/components/select.html +696 -0
  121. package/docs/components/sidebar-setup.js +93 -0
  122. package/docs/components/skeleton.html +447 -0
  123. package/docs/components/spinner.html +68 -0
  124. package/docs/components/stats.html +486 -0
  125. package/docs/components/steps.html +356 -0
  126. package/docs/components/swap.html +517 -0
  127. package/docs/components/switch.html +68 -0
  128. package/docs/components/table.html +668 -0
  129. package/docs/components/tabs.html +506 -0
  130. package/docs/components/text-input.html +68 -0
  131. package/docs/components/textarea.html +603 -0
  132. package/docs/components/timeline.html +485 -42
  133. package/docs/components/toast.html +474 -0
  134. package/docs/components/toggle.html +564 -0
  135. package/docs/components/tooltip.html +423 -0
  136. package/docs/examples/getting-started-example.html +40 -0
  137. package/docs/examples/index.html +93 -0
  138. package/docs/getting-started/index.html +739 -0
  139. package/docs/getting-started/reviews.html +23 -0
  140. package/docs/getting-started/reviews.odom +108 -0
  141. package/docs/getting-started/reviews.vdom +84 -0
  142. package/docs/index.html +132 -42
  143. package/docs/playground.html +416 -0
  144. package/docs/router.html +285 -0
  145. package/docs/styles/index.html +190 -0
  146. package/functions/_middleware.js +32 -0
  147. package/index.html +309 -0
  148. package/lightview-router.js +364 -0
  149. package/lightview-x.js +1577 -0
  150. package/lightview.js +659 -1200
  151. package/middleware/locale.js +25 -0
  152. package/middleware/markdown.js +44 -0
  153. package/middleware/notFound.js +37 -0
  154. package/package.json +27 -41
  155. package/watch.js +92 -0
  156. package/wrangler.toml +12 -0
  157. package/.idea/lightview.iml +0 -12
  158. package/.idea/modules.xml +0 -8
  159. package/.idea/vcs.xml +0 -6
  160. package/LICENSE +0 -21
  161. package/codepen-no-tabs-embed.css +0 -2
  162. package/docs/CNAME +0 -1
  163. package/docs/api.html +0 -674
  164. package/docs/blank.html +0 -10
  165. package/docs/comparedto.html +0 -89
  166. package/docs/components/chart-repl.html +0 -69
  167. package/docs/components/components.js +0 -113
  168. package/docs/components/contents.html +0 -17
  169. package/docs/components/gantt-repl.html +0 -61
  170. package/docs/components/gantt.html +0 -42
  171. package/docs/components/gauge-repl.html +0 -66
  172. package/docs/components/gauge.html +0 -20
  173. package/docs/components/orgchart-repl.html +0 -64
  174. package/docs/components/orgchart.html +0 -41
  175. package/docs/components/repl-as-src.html +0 -17
  176. package/docs/components/repl-repl.html +0 -95
  177. package/docs/components/repl.html +0 -527
  178. package/docs/components/timeline-repl.html +0 -72
  179. package/docs/components.html +0 -14
  180. package/docs/css/highlightjs.min.css +0 -9
  181. package/docs/css/tutorial.css +0 -35
  182. package/docs/examples/anchor.html +0 -11
  183. package/docs/examples/chart.html +0 -34
  184. package/docs/examples/counter.html +0 -26
  185. package/docs/examples/counter.test.mjs +0 -47
  186. package/docs/examples/counter2.html +0 -26
  187. package/docs/examples/directives.html +0 -79
  188. package/docs/examples/foreign.html +0 -50
  189. package/docs/examples/forgeinform.html +0 -98
  190. package/docs/examples/form.html +0 -61
  191. package/docs/examples/gauge.html +0 -18
  192. package/docs/examples/invalid-template-literals.html +0 -44
  193. package/docs/examples/medium/remote.html +0 -60
  194. package/docs/examples/message.html +0 -18
  195. package/docs/examples/nested.html +0 -11
  196. package/docs/examples/object-bound-form.html +0 -34
  197. package/docs/examples/remote-server.js +0 -51
  198. package/docs/examples/remote.html +0 -34
  199. package/docs/examples/remote.json +0 -1
  200. package/docs/examples/scratch.html +0 -69
  201. package/docs/examples/sensors/index.html +0 -44
  202. package/docs/examples/sensors/sensor-server.js +0 -30
  203. package/docs/examples/shared.html +0 -41
  204. package/docs/examples/template.html +0 -33
  205. package/docs/examples/timeline.html +0 -21
  206. package/docs/examples/todo.html +0 -40
  207. package/docs/examples/top.html +0 -10
  208. package/docs/examples/types.html +0 -94
  209. package/docs/examples/xor.html +0 -62
  210. package/docs/examples.html +0 -25
  211. package/docs/javascript/codejar.min.js +0 -8
  212. package/docs/javascript/highlightjs.min.js +0 -1173
  213. package/docs/javascript/isomorphic-git.js +0 -9
  214. package/docs/javascript/json5.min.js +0 -1
  215. package/docs/javascript/lightning-fs.js +0 -1
  216. package/docs/javascript/lightview.js +0 -1285
  217. package/docs/javascript/marked.min.js +0 -6
  218. package/docs/javascript/peerjs.min.js +0 -70
  219. package/docs/javascript/turndown.js +0 -973
  220. package/docs/javascript/types.js +0 -606
  221. package/docs/javascript/utils.js +0 -45
  222. package/docs/lightview.html +0 -63
  223. package/docs/old_index.html +0 -965
  224. package/docs/old_index.md +0 -1132
  225. package/docs/slidein.html +0 -51
  226. package/docs/tutorial/0-getting-started.html +0 -67
  227. package/docs/tutorial/1-intro-to-variables.html +0 -103
  228. package/docs/tutorial/10-template-components.html +0 -80
  229. package/docs/tutorial/11-linked-components.html +0 -76
  230. package/docs/tutorial/12-imported-components.html +0 -67
  231. package/docs/tutorial/13-input-binding.html +0 -94
  232. package/docs/tutorial/14-automatic-variable-creation.html +0 -74
  233. package/docs/tutorial/15-form-binding.html +0 -110
  234. package/docs/tutorial/16-if-directive.html +0 -60
  235. package/docs/tutorial/17-loop-directives.html +0 -83
  236. package/docs/tutorial/18-sanitizing-and-escaping-input.html +0 -79
  237. package/docs/tutorial/2-imported-and-exported-variables.html +0 -80
  238. package/docs/tutorial/3-data-types.html +0 -89
  239. package/docs/tutorial/4-extended-data-types.html +0 -83
  240. package/docs/tutorial/5-extended-functional-types.html +0 -96
  241. package/docs/tutorial/5.1-extended-functional-types.html +0 -79
  242. package/docs/tutorial/5.2-extended-functional-types.html +0 -70
  243. package/docs/tutorial/6-conventional-javascript.html +0 -75
  244. package/docs/tutorial/7-monitoring-with-observers.html +0 -107
  245. package/docs/tutorial/8-event-listeners.html +0 -65
  246. package/docs/tutorial/9-intro-to-components.html +0 -91
  247. package/docs/tutorial/contents.html +0 -32
  248. package/docs/tutorial/my-component.html +0 -29
  249. package/docs/tutorial/remote-value.json +0 -4
  250. package/docs/websiterepl.html +0 -46
  251. package/jest-puppeteer.config.js +0 -5
  252. package/jest.config.json +0 -12
  253. package/lightview.min.js +0 -1
  254. package/lightview_good.js +0 -1267
  255. package/lightview_optimized.js +0 -1274
  256. package/repl_hold.html +0 -320
  257. package/test/basic.html +0 -104
  258. package/test/basic.test.mjs +0 -315
  259. package/test/extended.html +0 -29
  260. package/test/extended.test.mjs +0 -448
  261. package/types.js +0 -607
  262. package/unsplash.key +0 -1
@@ -0,0 +1,739 @@
1
+ <!-- SEO-friendly SPA Shim -->
2
+ <script src="/lightview-router.js"></script>
3
+ <script>
4
+ if (window.LightviewRouter) {
5
+ LightviewRouter.base('/index.html');
6
+ }
7
+ </script>
8
+
9
+
10
+
11
+ <div class="section">
12
+ <div class="section-content" style="max-width: 1200px;">
13
+ <h1>Getting Started</h1>
14
+
15
+ <!-- Step Navigation -->
16
+ <div class="tutorial-steps" style="display: flex; gap: 0.5rem; margin-bottom: 2rem; flex-wrap: wrap;">
17
+ <button class="btn btn-primary step-btn active" onclick="switchStep(1)">1. Basics</button>
18
+ <button class="btn btn-secondary step-btn" onclick="switchStep(2)">2. Signals</button>
19
+ <button class="btn btn-secondary step-btn" onclick="switchStep(3)">3. State</button>
20
+ <button class="btn btn-secondary step-btn" onclick="switchStep(4)">4. Hypermedia</button>
21
+ <button class="btn btn-secondary step-btn" onclick="switchStep(5)">5. Components</button>
22
+ </div>
23
+
24
+ <!-- Tutorial Layout: Preview on top, Code & Concepts below -->
25
+ <div class="tutorial-wrapper"
26
+ style="border: 1px solid var(--site-border); border-radius: var(--site-radius-lg); overflow: hidden; background: var(--site-surface);">
27
+
28
+ <!-- Preview Area -->
29
+ <div id="tutorial-preview"
30
+ style="min-height: 350px; background: var(--site-bg); border-bottom: 1px solid var(--site-border);">
31
+ <!-- Iframes injected here -->
32
+ </div>
33
+
34
+ <!-- Split: Code | Resizer | Concepts -->
35
+ <div id="tutorial-split" style="display: flex; min-height: 400px;">
36
+
37
+ <!-- Code Column -->
38
+ <div id="code-column" class="tutorial-column"
39
+ style="flex: 1; min-width: 200px; display: flex; flex-direction: column; border-right: none;">
40
+ <div
41
+ style="padding: 0.5rem 1rem; background: var(--site-bg-alt); border-bottom: 1px solid var(--site-border);">
42
+ <span
43
+ style="font-weight: 600; color: var(--site-text-secondary); font-size: 0.875rem;">CODE</span>
44
+ </div>
45
+ <!-- Code blocks injected here -->
46
+ </div>
47
+
48
+ <!-- Resizer Handle -->
49
+ <div id="tutorial-resizer"
50
+ style="width: 6px; background: var(--site-border); cursor: col-resize; flex-shrink: 0; transition: background 0.2s;">
51
+ </div>
52
+
53
+ <!-- Concepts Column -->
54
+ <div id="concepts-column" class="tutorial-column"
55
+ style="flex: 1; min-width: 200px; display: flex; flex-direction: column;">
56
+ <div
57
+ style="padding: 0.5rem 1rem; background: var(--site-bg-alt); border-bottom: 1px solid var(--site-border);">
58
+ <span
59
+ style="font-weight: 600; color: var(--site-text-secondary); font-size: 0.875rem;">CONCEPTS</span>
60
+ </div>
61
+ <div id="tutorial-concepts" style="padding: 1.5rem; overflow-y: auto; flex: 1;">
62
+ <!-- Concept blocks injected here -->
63
+ </div>
64
+ </div>
65
+ </div>
66
+ </div>
67
+
68
+ </div>
69
+ </div>
70
+
71
+ <style>
72
+ .step-btn.active {
73
+ background: var(--site-primary) !important;
74
+ color: white !important;
75
+ border-color: var(--site-primary) !important;
76
+ }
77
+
78
+ #tutorial-resizer:hover {
79
+ background: var(--site-primary);
80
+ }
81
+
82
+ #tutorial-resizer.dragging {
83
+ background: var(--site-primary);
84
+ }
85
+
86
+ /* Step content visibility */
87
+ .step-content {
88
+ display: none;
89
+ height: 100%;
90
+ }
91
+
92
+ .step-content.active {
93
+ display: block;
94
+ }
95
+
96
+ /* Code column specific layout for active step */
97
+ #code-column .step-content.active {
98
+ display: flex;
99
+ flex-direction: column;
100
+ flex: 1;
101
+ overflow: auto;
102
+ }
103
+
104
+ /* Ensure pre tags fill space */
105
+ #code-column pre {
106
+ flex: 1;
107
+ margin: 0;
108
+ outline: none;
109
+ }
110
+
111
+ @media (max-width: 900px) {
112
+ #tutorial-split {
113
+ flex-direction: column !important;
114
+ }
115
+
116
+ #tutorial-resizer {
117
+ display: none;
118
+ }
119
+
120
+ #code-column,
121
+ #concepts-column {
122
+ min-width: 100% !important;
123
+ }
124
+ }
125
+ </style>
126
+
127
+ <script>
128
+ (function () {
129
+ const previewContainer = document.getElementById('tutorial-preview');
130
+ const codeContainer = document.getElementById('code-column');
131
+ const conceptsContainer = document.getElementById('tutorial-concepts');
132
+ const stepBtns = document.querySelectorAll('.step-btn');
133
+
134
+ // Tutorial data for each step
135
+ const tutorialData = {
136
+ 1: {
137
+ options: {
138
+ autoRun: true,
139
+ },
140
+ code: `// STEP 1: BASICS - Pure UI with Tagged Functions
141
+ const { tags, $ } = Lightview;
142
+ const { div, h1, p, style } = tags;
143
+
144
+ // 1. Build UI using Tagged Functions
145
+ // These functions return standard DOM elements
146
+ const App = div({ class: 'hero' },
147
+ h1('Welcome to Lightview'),
148
+ p('Lightview is a tiny library for building modern web interfaces.')
149
+ );
150
+
151
+ // 2. The $ Function for Selections & Content
152
+ // Inject the App into the #app container
153
+ $('#app').content(App);
154
+
155
+ // 3. Injecting Styles
156
+ // Use $ with a location to inject styles at the end of the app
157
+ $('#app').content(style({}, \`
158
+ .hero {
159
+ padding: 2rem;
160
+ background: #f8fafc;
161
+ border-radius: 12px;
162
+ border: 1px solid #e2e8f0;
163
+ font-family: system-ui, sans-serif;
164
+ }
165
+ h1 { color: #1e293b; margin: 0 0 1rem; font-size: 2rem; }
166
+ p { color: #64748b; font-size: 1.1rem; line-height: 1.5; }
167
+ \`), 'beforeend');`,
168
+ concepts: `
169
+ <h3 style="margin-top: 0; color: var(--site-primary);">Step 1: Basic Content Creation</h3>
170
+ <p>Welcome! Let's start with the basics of building UI without reactivity or components.</p>
171
+
172
+ <h4>Key Concepts:</h4>
173
+ <ul style="padding-left: 1.25rem; color: var(--site-text-secondary);">
174
+ <li style="margin-bottom: 0.75rem;"><code>tags</code> — Every HTML tag is available as a function. <code>div(...)</code> returns a real <code>HTMLDivElement</code>.</li>
175
+ <li style="margin-bottom: 0.75rem;"><code>$(selector)</code> — A powerful utility for selecting elements and manipulating them.</li>
176
+ <li style="margin-bottom: 0.75rem;"><code>.content(node, location)</code> — Replaces or appends content. Locations like <code>'beforeend'</code> allow injecting styles or scripts dynamicly.</li>
177
+ </ul>
178
+ <p>This approach results in standard, lightweight DOM elements with no overhead.</p>
179
+ `
180
+ },
181
+ 2: {
182
+ options: {
183
+ autoRun: true,
184
+ },
185
+ code: `// STEP 2: SIGNALS - The foundation of reactivity
186
+ const { signal, tags, $ } = Lightview;
187
+ const { div, h3, p, button, img, style } = tags;
188
+
189
+ // Create a reactive signal for the button state
190
+ const added = signal(false);
191
+
192
+ // Build the UI using Lightview's tag functions
193
+ const App = div({ class: 'product-card' },
194
+ img({
195
+ src: 'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=500&auto=format&fit=crop',
196
+ class: 'product-image'
197
+ }),
198
+ h3({ class: 'product-title' }, 'Red Nike Sneakers'),
199
+
200
+ // Reactive button - text updates when signal changes
201
+ div({ class: 'product-footer' },
202
+ button({
203
+ class: 'add-btn',
204
+ onclick: () => added.value = !added.value // Toggle state
205
+ }, () => added.value ? '✓ Added' : 'Add to Cart')
206
+ )
207
+ );
208
+
209
+ $('#app').content(App);
210
+
211
+ // ---------------------------------------------------------------
212
+ // STYLES
213
+ // ---------------------------------------------------------------
214
+ $('#app').content(style({},
215
+ \`.product-card { width: 280px; font-family: system-ui, sans-serif; }
216
+ .product-image { width: 100%; border-radius: 8px; margin-bottom: 1rem; }
217
+ .product-title { margin: 0 0 0.25rem; font-size: 1.1rem; }
218
+ .product-footer { display: flex; justify-content: flex-end; }
219
+ .add-btn { background: #3b82f6; color: white; border: none; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-weight: 500; }
220
+ .add-btn:hover { background: #2563eb; }\`), 'beforeend');`,
221
+ concepts: `
222
+ <h3 style="margin-top: 0; color: var(--site-primary);">Step 2: Reactive Signals</h3>
223
+ <p>In this step, we use <strong>Signals</strong> to handle data that changes over time.</p>
224
+
225
+ <h4>Key Concepts:</h4>
226
+ <ul style="padding-left: 1.25rem; color: var(--site-text-secondary);">
227
+ <li style="margin-bottom: 0.75rem;"><code>signal(value)</code> — Creates a reactive container. Changing <code>added.value</code> automatically updates the UI.</li>
228
+ <li style="margin-bottom: 0.75rem;"><code>tags</code> — Build HTML elements with pure JavaScript functions like <code>div()</code>, <code>button()</code>.</li>
229
+ <li style="margin-bottom: 0.75rem;"><code>Inside Tags</code> — Pass a function as a child (e.g., <code>() => added.value ? ...</code>) to create a reactive text node.</li>
230
+ </ul>
231
+ <p><strong>Try it:</strong> Click the "Add to Cart" button to see it toggle!</p>
232
+ `
233
+ },
234
+ 3: {
235
+ options: {
236
+ allowSameOrigin: true,
237
+ autoRun: true,
238
+ },
239
+ code: `// STEP 3: STATE + VDOM SYNTAX
240
+ const { tags, $ } = Lightview;
241
+ const { div, style } = tags;
242
+ const { state } = LightviewX;
243
+
244
+ // 1. Deep Reactivity with Optional Persistence
245
+ const session = state({
246
+ cart: [
247
+ { id: 3, name: 'Green Reeboks', price: 60 }
248
+ ],
249
+ items: [
250
+ { id: 1, name: 'Red Nike Sneakers', price: 99 },
251
+ { id: 2, name: 'Blue Adidas', price: 85 },
252
+ { id: 3, name: 'Green Reeboks', price: 60 }
253
+ ]
254
+ }, { name: 'shopping-session', storage: sessionStorage });
255
+
256
+ const addToCart = (item) => session.cart.push(item);
257
+ const clearCart = () => {
258
+ session.cart.length = 0; // Clear reactive array
259
+ sessionStorage.removeItem('shopping-session'); // Delete session variable
260
+ };
261
+
262
+ // 2. vDOM Syntax: { tag, attributes, children }
263
+ const App = div({ class: 'shop-container' }, [
264
+ // Cart Badge
265
+ { tag: 'div', attributes: { class: 'cart-badge' }, children: [
266
+ { tag: 'span', attributes: {}, children: [
267
+ () => '🛒 Cart: ' + session.cart.length + ' items'
268
+ ]},
269
+ { tag: 'button', attributes: {
270
+ class: 'clear-btn',
271
+ onclick: clearCart
272
+ }, children: ['Clear Cart'] }
273
+ ]},
274
+
275
+ // Product List
276
+ { tag: 'div', attributes: { class: 'product-list' }, children:
277
+ session.items.map(item => ({
278
+ tag: 'div', attributes: { class: 'product-row' }, children: [
279
+ { tag: 'span', children: [item.name + ' ($' + item.price + ')'] },
280
+ { tag: 'button', attributes: {
281
+ class: 'add-btn',
282
+ onclick: () => addToCart(item)
283
+ }, children: ['Add'] }
284
+ ]
285
+ }))
286
+ },
287
+
288
+ // Cart Contents
289
+ { tag: 'h3', attributes: { class: 'cart-title' }, children: ['Cart:'] },
290
+ { tag: 'ul', attributes: { class: 'cart-list' }, children: [
291
+ () => session.cart.length === 0
292
+ ? { tag: 'li', attributes: { class: 'empty' }, children: ['Empty'] }
293
+ : session.cart.map((item, i) => ({
294
+ tag: 'li', attributes: {}, children: [
295
+ item.name + ' ',
296
+ { tag: 'button', attributes: {
297
+ class: 'remove-btn',
298
+ onclick: () => session.cart.splice(i, 1)
299
+ }, children: ['×'] }
300
+ ]
301
+ }))
302
+ ]},
303
+
304
+ // Styles
305
+ { tag: 'style', attributes: {}, children: [
306
+ \`.shop-container { width: 320px; font-family: system-ui, sans-serif; }
307
+ .cart-badge { padding: 0.75rem; background: #f3f4f6; border-radius: 8px; margin-bottom: 1rem; display: flex; justify-content: space-between; align-items: center; }
308
+ .clear-btn { font-size: 0.7rem; padding: 4px 8px; background: #94a3b8; color: white; border: none; border-radius: 4px; cursor: pointer; }
309
+ .product-list { display: flex; flex-direction: column; gap: 0.5rem; }
310
+ .product-row { display: flex; justify-content: space-between; padding: 0.75rem; border: 1px solid #e5e7eb; border-radius: 6px; }
311
+ .add-btn { background: #3b82f6; color: white; border: none; padding: 0.25rem 0.75rem; border-radius: 4px; cursor: pointer; }
312
+ .cart-title { font-size: 1rem; margin-top: 1.5rem; }
313
+ .cart-list { padding-left: 1.25rem; margin: 0.5rem 0; }
314
+ .cart-list li { margin-bottom: 0.25rem; }
315
+ .cart-list .empty { color: #9ca3af; }
316
+ .remove-btn { font-size: 0.875rem; color: #ef4444; border: none; background: none; cursor: pointer; margin-left: 0.5rem; }\`
317
+ ]}
318
+ ]);
319
+
320
+ $('#app').content(App);`,
321
+ concepts: `
322
+ <h3 style="margin-top: 0; color: var(--site-primary);">Step 3: State & vDOM Syntax</h3>
323
+ <p>This step introduces <strong>State</strong> and the <strong>vDOM</strong> object syntax.</p>
324
+
325
+ <h4>Key Concepts:</h4>
326
+ <ul style="padding-left: 1.25rem; color: var(--site-text-secondary);">
327
+ <li style="margin-bottom: 0.75rem;"><strong>vDOM Syntax</strong> — Elements are plain objects: <code>{ tag: 'div', attributes: {}, children: [] }</code>. This structure is excellent for programmatic generation, serialization (JSON), or building custom components.</li>
328
+ <li style="margin-bottom: 0.75rem;"><strong>Deep Reactivity</strong> — Use <code>LightviewX.state()</code> for complex objects and arrays. Arrays methods like <code>.push()</code> and <code>.splice()</code> trigger updates automatically.</li>
329
+ <li style="margin-bottom: 0.75rem;"><strong>State Persistence</strong> — Pass <code>{ name, storage }</code> to <code>state()</code> to automatically persist data to <code>sessionStorage</code> or <code>localStorage</code>.</li>
330
+ <li style="margin-bottom: 0.75rem;"><strong>Interoperability</strong> — You can pass valid vDOM objects as children to standard <code>tags</code> functions directly.</li>
331
+ </ul>
332
+ <p><strong>Try it:</strong> Add items to the cart and re-run the code to see persistence!</p>
333
+ `
334
+ },
335
+ 4: {
336
+ options: {
337
+ allowSameOrigin: true,
338
+ autoRun: true
339
+ },
340
+ code: `// STEP 4: HYPERMEDIA + OBJECT DOM SYNTAX (requires lightview-x)
341
+ // Choose a format to load: HTML, VDOM, or Object DOM
342
+
343
+ // Object DOM syntax integration is handled by lightview-x
344
+ const { signal, element, $ } = Lightview;
345
+
346
+ // Track which format was loaded and the review count
347
+ const currentFormat = signal('none', 'currentFormat');
348
+ const reviewCount = signal(0, 'reviewCount');
349
+
350
+ // Helper to get source code signal
351
+ const sourceCode = signal('Select a format...', 'sourceCode');
352
+
353
+ const loadFormat = async (format, file) => {
354
+ currentFormat.value = format;
355
+ reviewCount.value = 3;
356
+ // Fetch source code for display
357
+ try {
358
+ const text = await fetch(file).then(r => r.text());
359
+ sourceCode.value = text;
360
+ } catch (e) {
361
+ sourceCode.value = 'Error loading source';
362
+ }
363
+ };
364
+
365
+ // Build UI using Object DOM syntax - compact JSON format
366
+ const App = element('div', { class: 'reviews-container' }, [
367
+ // Header
368
+ {
369
+ div: {
370
+ class: 'reviews-header', children: [
371
+ { h3: { class: 'reviews-title', children: ['Customer Reviews'] } },
372
+ {
373
+ p: {
374
+ class: 'reviews-subtitle', children: [
375
+ 'Choose a format to load reviews from external files:'
376
+ ]
377
+ }
378
+ },
379
+
380
+ // Format selector buttons
381
+ {
382
+ div: {
383
+ class: 'format-buttons', children: [
384
+ {
385
+ button: {
386
+ class: 'format-btn html',
387
+ href: './reviews.html',
388
+ target: '#reviews-box',
389
+ onclick: () => loadFormat('HTML', './reviews.html'),
390
+ children: ['📄 HTML']
391
+ }
392
+ },
393
+ {
394
+ button: {
395
+ class: 'format-btn vdom',
396
+ href: './reviews.vdom',
397
+ target: '#reviews-box',
398
+ onclick: () => loadFormat('VDOM', './reviews.vdom'),
399
+ children: ['🔷 VDOM']
400
+ }
401
+ },
402
+ {
403
+ button: {
404
+ class: 'format-btn odom',
405
+ href: './reviews.odom',
406
+ target: '#reviews-box',
407
+ onclick: () => loadFormat('ODOM', './reviews.odom'),
408
+ children: ['🔶 Object DOM']
409
+ }
410
+ }
411
+ ]
412
+ }
413
+ }
414
+ ]
415
+ }
416
+ },
417
+
418
+ // Split layout: Rendered UI | Source Code
419
+ {
420
+ div: {
421
+ class: 'split-view', children: [
422
+ // Left: Rendered Content
423
+ {
424
+ div: {
425
+ class: 'render-pane', children: [
426
+ { p: { class: 'pane-label', children: ['Rendered Output:'] } },
427
+ { div: { id: 'reviews-box', src: '' } },
428
+ {
429
+ p: {
430
+ class: 'status', children: [
431
+ () => currentFormat.value === 'none'
432
+ ? 'Click a button above to load...'
433
+ : 'Loaded ' + reviewCount.value + ' reviews from ' + currentFormat.value
434
+ ]
435
+ }
436
+ }
437
+ ]
438
+ }
439
+ },
440
+
441
+ // Right: Source Code
442
+ {
443
+ div: {
444
+ class: 'source-pane', children: [
445
+ { p: { class: 'pane-label', children: ['Source Code:'] } },
446
+ {
447
+ pre: {
448
+ class: 'source-code', children: [
449
+ () => sourceCode.value
450
+ ]
451
+ }
452
+ }
453
+ ]
454
+ }
455
+ }
456
+ ]
457
+ }
458
+ },
459
+
460
+ // Styles
461
+ { style: { children: [
462
+ \`.reviews-container { width: 100%; box-sizing: border-box; font-family: system-ui, sans-serif; }
463
+ .reviews-header { margin-bottom: 1rem; }
464
+ .reviews-title { margin: 0 0 0.5rem; }
465
+ .reviews-subtitle { color: #6b7280; margin: 0 0 0.75rem; font-size: 0.9em; }
466
+ .format-buttons { display: flex; gap: 0.5rem; margin-bottom: 1rem; }
467
+ .format-btn { flex: 1; padding: 0.5rem; cursor: pointer; border: none; border-radius: 6px; font-weight: 500; font-size: 0.9em; }
468
+ .format-btn.html { background: #dbeafe; color: #1e40af; }
469
+ .format-btn.vdom { background: #ede9fe; color: #6d28d9; }
470
+ .format-btn.odom { background: #ffedd5; color: #c2410c; }
471
+ .split-view { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; width: 100%; box-sizing: border-box; }
472
+ .render-pane, .source-pane { border: 1px solid #e5e7eb; border-radius: 8px; padding: 1rem; background: #fff; min-width: 0; }
473
+ .pane-label { font-size: 0.75rem; font-weight: bold; color: #9ca3af; text-transform: uppercase; margin: 0 0 0.5rem; }
474
+ .source-code { margin: 0; font-family: monospace; font-size: 0.75rem; background: #f8f9fa; padding: 0.5rem; border-radius: 4px; overflow-x: auto; white-space: pre-wrap; color: #374151; height: 90%; overflow-y: auto; }
475
+ .status { color: #6366f1; font-weight: 500; margin: 1rem 0 0; font-size: 0.9em; }\`
476
+ ]}}
477
+ ]);
478
+
479
+ $('#app').content(App);`,
480
+ concepts: `
481
+ <h3 style="margin-top: 0; color: var(--site-primary);">Step 4: Hypermedia + Multiple Formats</h3>
482
+ <p>Lightview's <strong>hypermedia</strong> feature allows your application to load partial content dynamically from various file formats.</p>
483
+
484
+ <h4>Key Concepts:</h4>
485
+ <ul style="padding-left: 1.25rem; color: var(--site-text-secondary);">
486
+ <li style="margin-bottom: 0.75rem;"><code>href</code> on any element — Clicking it will fetch content and place it into the target.</li>
487
+ <li style="margin-bottom: 0.75rem;"><code>src</code> — Defines the source URL to fetch content from.</li>
488
+ <li style="margin-bottom: 0.75rem;"><strong>Template Literals</strong> — Loaded files (HTML, VDOM, ODOM) can contain <code>\${...}</code> syntax. These are evaluated reactively and can access named <code>signals</code> or <code>state</code> using <code>signal.get('name').value</code>. Note the code <code>Displaying &dollar;{signal.get('reviewCount').value} reviews in &dollar;{signal.get('currentFormat').value} format.</code> in the examples.</li>
489
+ </ul>
490
+
491
+ <hr style="margin: 1.5rem 0; border: none; border-top: 1px solid var(--site-border);">
492
+
493
+ <h4>Format Comparison:</h4>
494
+ <div style="font-size: 0.875rem;">
495
+ <div style="margin-bottom: 1rem;">
496
+ <strong style="color: #1e40af;">📄 HTML</strong>
497
+ <ul style="margin: 0.25rem 0 0; padding-left: 1.25rem; color: var(--site-text-secondary);">
498
+ <li><strong>Pros:</strong> Standard, easy to read, works with existing backend templates.</li>
499
+ <li><strong>Cons:</strong> Verbose, no type safety, harder to manipulate programmatically.</li>
500
+ </ul>
501
+ </div>
502
+
503
+ <div style="margin-bottom: 1rem;">
504
+ <strong style="color: #6d28d9;">🔷 VDOM JSON</strong>
505
+ <ul style="margin: 0.25rem 0 0; padding-left: 1.25rem; color: var(--site-text-secondary);">
506
+ <li><strong>Pros:</strong> Explicit structure, easy to generate from servers, lightweight.</li>
507
+ <li><strong>Cons:</strong> Verbose JSON structure (repeated "tag", "attributes" keys).</li>
508
+ </ul>
509
+ </div>
510
+
511
+ <div>
512
+ <strong style="color: #c2410c;">🔶 Object DOM JSON</strong>
513
+ <ul style="margin: 0.25rem 0 0; padding-left: 1.25rem; color: var(--site-text-secondary);">
514
+ <li><strong>Pros:</strong> Extremely compact, human-readable JSON, expressive.</li>
515
+ <li><strong>Cons:</strong> Non-standard structure, requires understanding the syntax mapping.</li>
516
+ </ul>
517
+ </div>
518
+ </div>
519
+ `
520
+ },
521
+ 5: {
522
+ options: {
523
+ autoRun: true
524
+ },
525
+ code: `// STEP 5: COMPONENTS, COMPOSITION & COMPUTED
526
+ // Import the real components from the component library
527
+ import Button from '../../components/actions/button.js';
528
+ import Badge from '../../components/data-display/badge.js';
529
+ import '../../components/data-display/card.js';
530
+
531
+ const { tags, signal, computed, $ } = Lightview;
532
+ const { div, h3, span, Card, style, img } = tags;
533
+
534
+ // Logic for the product card
535
+ const price = 99;
536
+ const quantity = signal(1); // Reactive signal
537
+ const total = computed(() => price * quantity.value); // Computed derived value
538
+
539
+ const styles = \`
540
+ .product-card { width: 500px; font-family: system-ui, sans-serif; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); background: white; border-radius: 1rem; overflow: hidden; }
541
+ .product-body { padding: 1.25rem; }
542
+ .product-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 1rem; }
543
+ .card-title { margin: 0; font-size: 1.1rem; font-weight: bold; }
544
+ .product-controls { display: flex; justify-content: space-between; align-items: center; padding-top: 1rem; border-top: 1px solid #f3f4f6; }
545
+ .total-price { font-size: 1.25rem; font-weight: 800; color: #111; }
546
+ .quantity-controls { display: flex; align-items: center; gap: 0.5rem; }
547
+ .quantity-value { width: 1.5rem; text-align: center; font-weight: 500; }
548
+ \`;
549
+
550
+ // 5. Styles for Shadow DOM
551
+ // Registering a named stylesheet allows it to be used inside isolated component styles
552
+ LightviewX.registerStyleSheet('product-styles', styles);
553
+
554
+ const App = Card({
555
+ class: 'product-card',
556
+ styleSheets: ['product-styles'] // Passes the registered sheet into the shadow root
557
+ },
558
+
559
+ img({
560
+ src: 'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=500&auto=format&fit=crop',
561
+ style: 'object-fit: cover;',
562
+ alt: 'Red Nike Sneakers'
563
+ }),
564
+
565
+ Card.Body({ class: 'product-body' },
566
+ div({ class: 'product-header' },
567
+ h3({ class: 'card-title' }, 'Red Nike Sneakers'),
568
+ Badge({ color: 'success', variant: 'outline' }, 'In Stock')
569
+ ),
570
+
571
+ // Quantity & Price Controls
572
+ div({ class: 'product-controls' },
573
+ span({ class: 'total-price' }, () => '$' + total.value),
574
+ div({ class: 'quantity-controls' },
575
+ Button({ size: 'sm', class: 'btn-circle', onclick: () => quantity.value = Math.max(1, quantity.value - 1) }, '-'),
576
+ span({ class: 'quantity-value' }, () => quantity.value),
577
+ Button({ size: 'sm', class: 'btn-circle', onclick: () => quantity.value++ }, '+')
578
+ )
579
+ )
580
+ )
581
+ );
582
+
583
+ $('#app').content(App);`,
584
+ concepts: `<h3 style="margin-top: 0; color: var(--site-primary);">Step 5: Components & Isolated Styling</h3>
585
+ <p>Use the <strong>Lightview Component Library</strong> for polished UI elements that use Shadow DOM for isolation.</p>
586
+
587
+ <h4>Key Concepts:</h4>
588
+ <ul style="padding-left: 1.25rem; color: var(--site-text-secondary);">
589
+ <li style="margin-bottom: 0.75rem;"><strong>Components</strong> — Reusable blocks like <code>Card</code>, <code>Button</code>, and <code>Badge</code>.</li>
590
+ <li style="margin-bottom: 0.75rem;"><strong>Auto-Registration</strong> — Components auto-register with <code>tags</code> for easy destructuring.</li>
591
+ <li style="margin-bottom: 0.75rem;"><strong>Shadow DOM Isolation</strong> — Components isolate their styles (including DaisyUI/Tailwind) to avoid global CSS pollution.</li>
592
+ <li style="margin-bottom: 0.75rem;"><strong>Named StyleSheets</strong> — Use <code>LightviewX.registerStyleSheet(name, css)</code> to define styles that can be safely passed into a component's <code>styleSheets</code> property.</li>
593
+ <li style="margin-bottom: 0.75rem;"><strong>Computed</strong> — <code>computed(() => ...)</code> creates derived values that reactively update based on other signals.</li>
594
+ </ul>
595
+ <p><strong>Next:</strong> Explore the <a href="/docs/components">Components</a> library!</p>`
596
+ }
597
+ };
598
+
599
+
600
+
601
+ // Initialize Steps
602
+ Object.keys(tutorialData).forEach(step => {
603
+ const data = tutorialData[step];
604
+
605
+ // 1. Create Preview Container
606
+ const previewEl = document.createElement('div');
607
+ previewEl.id = `preview-${step}`;
608
+ previewEl.className = 'step-content';
609
+ previewContainer.appendChild(previewEl);
610
+
611
+ // 2. Create Code Container
612
+ const codeWrapper = document.createElement('div');
613
+ codeWrapper.id = `code-container-${step}`;
614
+ codeWrapper.className = 'step-content';
615
+
616
+ const preEl = document.createElement('pre');
617
+ preEl.style.flex = '1';
618
+ preEl.style.margin = '0';
619
+ preEl.style.overflow = 'auto';
620
+
621
+ const codeEl = document.createElement('code');
622
+ codeEl.id = `code-${step}`;
623
+ codeEl.contentEditable = "true";
624
+ codeEl.style.display = 'block';
625
+ codeEl.style.padding = '1rem';
626
+ codeEl.style.minHeight = '100%';
627
+ codeEl.style.outline = 'none';
628
+ codeEl.textContent = data.code;
629
+
630
+ preEl.appendChild(codeEl);
631
+ codeWrapper.appendChild(preEl);
632
+ codeContainer.appendChild(codeWrapper);
633
+
634
+ // 3. Create Concepts Container
635
+ const conceptsEl = document.createElement('div');
636
+ conceptsEl.id = `concepts-${step}`;
637
+ conceptsEl.className = 'step-content';
638
+ conceptsEl.innerHTML = data.concepts;
639
+ conceptsContainer.appendChild(conceptsEl);
640
+
641
+ // 4. Initialize Examplify
642
+ // Check if step uses modules (looking for import statement)
643
+ const isModule = data.code.includes('import ');
644
+
645
+ const exResult = examplify(codeEl, {
646
+ scripts: ['../../../lightview.js', '../../../lightview-x.js'],
647
+ html: '<div id="app" style="padding: 1rem;"></div>',
648
+ location: 'afterEnd',
649
+ type: isModule ? 'module' : 'text/javascript',
650
+ ...data.options || {}
651
+ });
652
+
653
+
654
+
655
+ // Move iframe to preview container
656
+ if (exResult && exResult.iframe) {
657
+ previewEl.appendChild(exResult.iframe);
658
+ exResult.iframe.style.width = '100%';
659
+ exResult.iframe.style.border = 'none';
660
+ exResult.iframe.style.height = '100%';
661
+ }
662
+ });
663
+
664
+ // Step switching logic
665
+ window.switchStep = function (step) {
666
+ // Update buttons
667
+ stepBtns.forEach((btn, i) => {
668
+ const btnStep = i + 1;
669
+ if (btnStep === step) {
670
+ btn.classList.add('active', 'btn-primary');
671
+ btn.classList.remove('btn-secondary');
672
+ } else {
673
+ btn.classList.remove('active', 'btn-primary');
674
+ btn.classList.add('btn-secondary');
675
+ }
676
+ });
677
+
678
+ // Toggle Content
679
+ ['preview', 'code-container', 'concepts'].forEach(prefix => {
680
+ document.querySelectorAll(`[id ^= "${prefix}-"]`).forEach(el => {
681
+ if (el.id === `${prefix}-${step}`) {
682
+ el.classList.add('active');
683
+ } else {
684
+ el.classList.remove('active');
685
+ }
686
+ });
687
+ });
688
+
689
+ // Trigger onLoad for the step if it exists (e.g. fetching files)
690
+ const data = tutorialData[step];
691
+ if (data && data.onLoad) data.onLoad();
692
+
693
+
694
+ };
695
+
696
+ // ---- Resizable columns ----
697
+ const resizer = document.getElementById('tutorial-resizer');
698
+ const splitContainer = document.getElementById('tutorial-split');
699
+ let isResizing = false;
700
+
701
+ resizer.addEventListener('mousedown', (e) => {
702
+ isResizing = true;
703
+ resizer.classList.add('dragging');
704
+ document.body.style.cursor = 'col-resize';
705
+ document.body.style.userSelect = 'none';
706
+ e.preventDefault();
707
+ });
708
+
709
+ document.addEventListener('mousemove', (e) => {
710
+ if (!isResizing) return;
711
+
712
+ const containerRect = splitContainer.getBoundingClientRect();
713
+ // const containerWidth = containerRect.width - 6;
714
+ const offsetX = e.clientX - containerRect.left;
715
+
716
+ // Calculate percentages with min/max constraints
717
+ let codePercent = (offsetX / containerRect.width) * 100;
718
+ codePercent = Math.max(20, Math.min(80, codePercent));
719
+
720
+ codeContainer.style.flex = 'none';
721
+ codeContainer.style.width = codePercent + '%';
722
+ conceptsContainer.style.flex = 'none';
723
+ conceptsContainer.style.width = (100 - codePercent - 1) + '%';
724
+ });
725
+
726
+ document.addEventListener('mouseup', () => {
727
+ if (isResizing) {
728
+ isResizing = false;
729
+ resizer.classList.remove('dragging');
730
+ document.body.style.cursor = '';
731
+ document.body.style.userSelect = '';
732
+ }
733
+ });
734
+
735
+ // Initial load
736
+ switchStep(1);
737
+
738
+ })();
739
+ </script>