lightview 1.8.2 → 2.0.0

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 (264) hide show
  1. package/.agent/workflows/daisyui-component-migration.md +155 -0
  2. package/.codacy/cli.sh +149 -0
  3. package/.codacy/codacy.yaml +15 -0
  4. package/.github/instructions/codacy.instructions.md +72 -0
  5. package/.wranglerignore +21 -0
  6. package/README.md +1330 -19
  7. package/_headers +4 -0
  8. package/build.js +70 -0
  9. package/components/actions/button.js +151 -0
  10. package/components/actions/dropdown.js +120 -0
  11. package/components/actions/modal.js +146 -0
  12. package/components/actions/swap.js +118 -0
  13. package/components/daisyui.js +288 -0
  14. package/components/data-display/accordion.js +128 -0
  15. package/components/data-display/alert.js +112 -0
  16. package/components/data-display/avatar.js +170 -0
  17. package/components/data-display/badge.js +82 -0
  18. package/components/data-display/card.js +151 -0
  19. package/components/data-display/carousel.js +94 -0
  20. package/components/data-display/chart.js +220 -0
  21. package/components/data-display/chat.js +128 -0
  22. package/components/data-display/collapse.js +103 -0
  23. package/components/data-display/countdown.js +69 -0
  24. package/components/data-display/diff.js +111 -0
  25. package/components/data-display/kbd.js +65 -0
  26. package/components/data-display/loading.js +75 -0
  27. package/components/data-display/progress.js +79 -0
  28. package/components/data-display/radial-progress.js +88 -0
  29. package/components/data-display/skeleton.js +66 -0
  30. package/components/data-display/stats.js +159 -0
  31. package/components/data-display/table.js +146 -0
  32. package/components/data-display/timeline.js +146 -0
  33. package/components/data-display/toast.js +72 -0
  34. package/components/data-display/tooltip.js +74 -0
  35. package/components/data-input/checkbox.js +253 -0
  36. package/components/data-input/file-input.js +224 -0
  37. package/components/data-input/input.js +264 -0
  38. package/components/data-input/radio.js +338 -0
  39. package/components/data-input/range.js +204 -0
  40. package/components/data-input/rating.js +219 -0
  41. package/components/data-input/select.js +287 -0
  42. package/components/data-input/textarea.js +287 -0
  43. package/components/data-input/toggle.js +201 -0
  44. package/components/index.js +137 -0
  45. package/components/layout/divider.js +72 -0
  46. package/components/layout/drawer.js +142 -0
  47. package/components/layout/footer.js +100 -0
  48. package/components/layout/hero.js +109 -0
  49. package/components/layout/indicator.js +90 -0
  50. package/components/layout/join.js +78 -0
  51. package/components/layout/navbar.js +110 -0
  52. package/components/navigation/breadcrumbs.js +91 -0
  53. package/components/navigation/dock.js +103 -0
  54. package/components/navigation/menu.js +126 -0
  55. package/components/navigation/pagination.js +105 -0
  56. package/components/navigation/steps.js +89 -0
  57. package/components/navigation/tabs.css +177 -0
  58. package/components/navigation/tabs.js +123 -0
  59. package/components/theme/theme-switch.css +65 -0
  60. package/components/theme/theme-switch.js +177 -0
  61. package/docs/about.html +164 -0
  62. package/docs/api/computed.html +184 -0
  63. package/docs/api/effects.html +173 -0
  64. package/docs/api/elements.html +180 -0
  65. package/docs/api/enhance.html +225 -0
  66. package/docs/api/hypermedia.html +165 -0
  67. package/docs/api/index.html +178 -0
  68. package/docs/api/nav.html +18 -0
  69. package/docs/api/signals.html +136 -0
  70. package/docs/api/state.html +217 -0
  71. package/docs/assets/images/logo-favicon.svg +42 -0
  72. package/docs/assets/images/logo-static.svg +40 -0
  73. package/docs/assets/images/logo.svg +66 -0
  74. package/docs/assets/js/examplify.js +395 -0
  75. package/docs/assets/styles/site.css +1102 -0
  76. package/docs/assets/styles/themes.css +236 -0
  77. package/docs/components/accordion.html +439 -0
  78. package/docs/components/alert.html +528 -0
  79. package/docs/components/avatar.html +586 -0
  80. package/docs/components/badge.html +531 -0
  81. package/docs/components/breadcrumbs.html +278 -0
  82. package/docs/components/button.html +579 -0
  83. package/docs/components/card.html +561 -0
  84. package/docs/components/carousel.html +286 -0
  85. package/docs/components/chart-area.html +702 -0
  86. package/docs/components/chart-bar.html +782 -0
  87. package/docs/components/chart-column.html +735 -0
  88. package/docs/components/chart-line.html +794 -0
  89. package/docs/components/chart-pie.html +823 -0
  90. package/docs/components/chart.html +610 -15
  91. package/docs/components/chat.html +547 -0
  92. package/docs/components/checkbox.html +641 -0
  93. package/docs/components/collapse.html +536 -0
  94. package/docs/components/component-nav.html +53 -0
  95. package/docs/components/countdown.html +470 -0
  96. package/docs/components/diff.html +245 -0
  97. package/docs/components/divider.html +240 -0
  98. package/docs/components/dock.html +277 -0
  99. package/docs/components/drawer.html +515 -0
  100. package/docs/components/dropdown.html +479 -0
  101. package/docs/components/file-input.html +591 -0
  102. package/docs/components/footer.html +301 -0
  103. package/docs/components/gallery.html +504 -0
  104. package/docs/components/hero.html +264 -0
  105. package/docs/components/index.css +840 -0
  106. package/docs/components/index.html +735 -0
  107. package/docs/components/indicator.html +342 -0
  108. package/docs/components/input.html +644 -0
  109. package/docs/components/join.html +285 -0
  110. package/docs/components/kbd.html +322 -0
  111. package/docs/components/loading.html +521 -0
  112. package/docs/components/menu.html +461 -0
  113. package/docs/components/modal.html +639 -0
  114. package/docs/components/navbar.html +321 -0
  115. package/docs/components/pagination.html +279 -0
  116. package/docs/components/progress.html +514 -0
  117. package/docs/components/radial-progress.html +434 -0
  118. package/docs/components/radio.html +655 -0
  119. package/docs/components/range.html +611 -0
  120. package/docs/components/rating.html +642 -0
  121. package/docs/components/select.html +696 -0
  122. package/docs/components/sidebar-setup.js +93 -0
  123. package/docs/components/skeleton.html +447 -0
  124. package/docs/components/spinner.html +68 -0
  125. package/docs/components/stats.html +486 -0
  126. package/docs/components/steps.html +356 -0
  127. package/docs/components/swap.html +517 -0
  128. package/docs/components/switch.html +68 -0
  129. package/docs/components/table.html +668 -0
  130. package/docs/components/tabs.html +506 -0
  131. package/docs/components/text-input.html +68 -0
  132. package/docs/components/textarea.html +603 -0
  133. package/docs/components/timeline.html +485 -42
  134. package/docs/components/toast.html +474 -0
  135. package/docs/components/toggle.html +564 -0
  136. package/docs/components/tooltip.html +423 -0
  137. package/docs/examples/getting-started-example.html +40 -0
  138. package/docs/examples/index.html +93 -0
  139. package/docs/getting-started/index.html +739 -0
  140. package/docs/getting-started/reviews.html +23 -0
  141. package/docs/getting-started/reviews.odom +108 -0
  142. package/docs/getting-started/reviews.vdom +84 -0
  143. package/docs/index.html +132 -42
  144. package/docs/playground.html +416 -0
  145. package/docs/router.html +285 -0
  146. package/docs/styles/index.html +190 -0
  147. package/functions/_middleware.js +32 -0
  148. package/index.html +309 -0
  149. package/lightview-router.js +364 -0
  150. package/lightview-x.js +1577 -0
  151. package/lightview.js +659 -1200
  152. package/lightview.js.backup +793 -0
  153. package/middleware/locale.js +25 -0
  154. package/middleware/markdown.js +44 -0
  155. package/middleware/notFound.js +37 -0
  156. package/package.json +27 -41
  157. package/watch.js +92 -0
  158. package/wrangler.toml +12 -0
  159. package/.idea/lightview.iml +0 -12
  160. package/.idea/modules.xml +0 -8
  161. package/.idea/vcs.xml +0 -6
  162. package/LICENSE +0 -21
  163. package/codepen-no-tabs-embed.css +0 -2
  164. package/docs/CNAME +0 -1
  165. package/docs/api.html +0 -674
  166. package/docs/blank.html +0 -10
  167. package/docs/comparedto.html +0 -89
  168. package/docs/components/chart-repl.html +0 -69
  169. package/docs/components/components.js +0 -113
  170. package/docs/components/contents.html +0 -17
  171. package/docs/components/gantt-repl.html +0 -61
  172. package/docs/components/gantt.html +0 -42
  173. package/docs/components/gauge-repl.html +0 -66
  174. package/docs/components/gauge.html +0 -20
  175. package/docs/components/orgchart-repl.html +0 -64
  176. package/docs/components/orgchart.html +0 -41
  177. package/docs/components/repl-as-src.html +0 -17
  178. package/docs/components/repl-repl.html +0 -95
  179. package/docs/components/repl.html +0 -527
  180. package/docs/components/timeline-repl.html +0 -72
  181. package/docs/components.html +0 -14
  182. package/docs/css/highlightjs.min.css +0 -9
  183. package/docs/css/tutorial.css +0 -35
  184. package/docs/examples/anchor.html +0 -11
  185. package/docs/examples/chart.html +0 -34
  186. package/docs/examples/counter.html +0 -26
  187. package/docs/examples/counter.test.mjs +0 -47
  188. package/docs/examples/counter2.html +0 -26
  189. package/docs/examples/directives.html +0 -79
  190. package/docs/examples/foreign.html +0 -50
  191. package/docs/examples/forgeinform.html +0 -98
  192. package/docs/examples/form.html +0 -61
  193. package/docs/examples/gauge.html +0 -18
  194. package/docs/examples/invalid-template-literals.html +0 -44
  195. package/docs/examples/medium/remote.html +0 -60
  196. package/docs/examples/message.html +0 -18
  197. package/docs/examples/nested.html +0 -11
  198. package/docs/examples/object-bound-form.html +0 -34
  199. package/docs/examples/remote-server.js +0 -51
  200. package/docs/examples/remote.html +0 -34
  201. package/docs/examples/remote.json +0 -1
  202. package/docs/examples/scratch.html +0 -69
  203. package/docs/examples/sensors/index.html +0 -44
  204. package/docs/examples/sensors/sensor-server.js +0 -30
  205. package/docs/examples/shared.html +0 -41
  206. package/docs/examples/template.html +0 -33
  207. package/docs/examples/timeline.html +0 -21
  208. package/docs/examples/todo.html +0 -40
  209. package/docs/examples/top.html +0 -10
  210. package/docs/examples/types.html +0 -94
  211. package/docs/examples/xor.html +0 -62
  212. package/docs/examples.html +0 -25
  213. package/docs/javascript/codejar.min.js +0 -8
  214. package/docs/javascript/highlightjs.min.js +0 -1173
  215. package/docs/javascript/isomorphic-git.js +0 -9
  216. package/docs/javascript/json5.min.js +0 -1
  217. package/docs/javascript/lightning-fs.js +0 -1
  218. package/docs/javascript/lightview.js +0 -1285
  219. package/docs/javascript/marked.min.js +0 -6
  220. package/docs/javascript/peerjs.min.js +0 -70
  221. package/docs/javascript/turndown.js +0 -973
  222. package/docs/javascript/types.js +0 -606
  223. package/docs/javascript/utils.js +0 -45
  224. package/docs/lightview.html +0 -63
  225. package/docs/old_index.html +0 -965
  226. package/docs/old_index.md +0 -1132
  227. package/docs/slidein.html +0 -51
  228. package/docs/tutorial/0-getting-started.html +0 -67
  229. package/docs/tutorial/1-intro-to-variables.html +0 -103
  230. package/docs/tutorial/10-template-components.html +0 -80
  231. package/docs/tutorial/11-linked-components.html +0 -76
  232. package/docs/tutorial/12-imported-components.html +0 -67
  233. package/docs/tutorial/13-input-binding.html +0 -94
  234. package/docs/tutorial/14-automatic-variable-creation.html +0 -74
  235. package/docs/tutorial/15-form-binding.html +0 -110
  236. package/docs/tutorial/16-if-directive.html +0 -60
  237. package/docs/tutorial/17-loop-directives.html +0 -83
  238. package/docs/tutorial/18-sanitizing-and-escaping-input.html +0 -79
  239. package/docs/tutorial/2-imported-and-exported-variables.html +0 -80
  240. package/docs/tutorial/3-data-types.html +0 -89
  241. package/docs/tutorial/4-extended-data-types.html +0 -83
  242. package/docs/tutorial/5-extended-functional-types.html +0 -96
  243. package/docs/tutorial/5.1-extended-functional-types.html +0 -79
  244. package/docs/tutorial/5.2-extended-functional-types.html +0 -70
  245. package/docs/tutorial/6-conventional-javascript.html +0 -75
  246. package/docs/tutorial/7-monitoring-with-observers.html +0 -107
  247. package/docs/tutorial/8-event-listeners.html +0 -65
  248. package/docs/tutorial/9-intro-to-components.html +0 -91
  249. package/docs/tutorial/contents.html +0 -32
  250. package/docs/tutorial/my-component.html +0 -29
  251. package/docs/tutorial/remote-value.json +0 -4
  252. package/docs/websiterepl.html +0 -46
  253. package/jest-puppeteer.config.js +0 -5
  254. package/jest.config.json +0 -12
  255. package/lightview.min.js +0 -1
  256. package/lightview_good.js +0 -1267
  257. package/lightview_optimized.js +0 -1274
  258. package/repl_hold.html +0 -320
  259. package/test/basic.html +0 -104
  260. package/test/basic.test.mjs +0 -315
  261. package/test/extended.html +0 -29
  262. package/test/extended.test.mjs +0 -448
  263. package/types.js +0 -607
  264. package/unsplash.key +0 -1
@@ -0,0 +1,395 @@
1
+ // Unique ID counter for iframe identification
2
+ var examplifyIdCounter = window.examplifyIdCounter || 0;
3
+ window.examplifyIdCounter = examplifyIdCounter;
4
+
5
+ function examplify(target, options = {}) {
6
+ const { scripts, styles, modules, html, at, location = 'beforeBegin', type, height, minHeight = 100, maxHeight = Infinity, allowSameOrigin = false, language = 'js', autoRun = false } = options;
7
+ const originalContent = target.textContent;
8
+ const autoResize = !height; // Auto-resize if no explicit height is provided
9
+ const iframeId = `examplify-${++examplifyIdCounter}`;
10
+
11
+ // State
12
+ let isRunning = false;
13
+
14
+ // 2. Create controls above the target
15
+ const controls = document.createElement('div');
16
+ const editable = target.getAttribute('contenteditable') == 'true';
17
+ controls.className = 'examplify-controls';
18
+
19
+ // Controls HTML
20
+ controls.innerHTML = `
21
+ <button class="examplify-btn examplify-run" title="Run">
22
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
23
+ <path d="M8 5v14l11-7z"/>
24
+ </svg>
25
+ <span>Run</span>
26
+ </button>
27
+ <button class="examplify-btn examplify-copy" title="Copy">
28
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
29
+ <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
30
+ <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
31
+ </svg>
32
+ <span>Copy</span>
33
+ </button>
34
+ ${editable ? `<button class="examplify-btn examplify-reset" title="Reset" style="display: none;">
35
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
36
+ <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/>
37
+ <path d="M3 3v5h5"/>
38
+ </svg>
39
+ <span>Reset</span>
40
+ </button>` : ''}
41
+ `;
42
+
43
+ // 3. Create iframe after target (initially empty)
44
+ let iframe = document.createElement('iframe');
45
+ iframe.className = 'examplify-iframe';
46
+ iframe.style.opacity = '1';
47
+ iframe.style.background = '#f9fafb'; // Light gray placeholder
48
+ iframe.style.border = '1px solid #e5e7eb';
49
+ iframe.style.transition = 'opacity 0.2s ease-in, height 0.2s ease-out';
50
+ iframe.sandbox = `${allowSameOrigin ? 'allow-same-origin ' : ''}allow-scripts`;
51
+
52
+ if (height) iframe.style.height = height;
53
+ if (minHeight) iframe.style.minHeight = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;
54
+ if (maxHeight && maxHeight !== Infinity) iframe.style.maxHeight = typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight;
55
+
56
+ // Set initial placeholder height if not specified, to show it exists
57
+ if (!height && !iframe.style.minHeight) {
58
+ iframe.style.height = '100px';
59
+ }
60
+
61
+ // Insert elements
62
+ const insertionPoint = (at || target);
63
+ insertionPoint.insertAdjacentElement(location, iframe);
64
+
65
+ if (target.parentElement && target.parentElement.tagName === 'PRE') {
66
+ target.parentElement.insertAdjacentElement('beforebegin', controls);
67
+ target.parentElement.classList.add('examplify-parent');
68
+ target.style.outline = 'none';
69
+ target.style.border = 'none';
70
+ } else {
71
+ target.insertAdjacentElement('beforebegin', controls);
72
+ }
73
+
74
+ // Styles
75
+ // We add styles only if not present, to avoid duplication if called multiple times
76
+ if (!document.getElementById('examplify-styles')) {
77
+ const style = document.createElement('style');
78
+ style.id = 'examplify-styles';
79
+ style.textContent = `
80
+ .examplify-controls {
81
+ display: flex;
82
+ gap: 0.5rem;
83
+ margin-bottom: 0.5rem;
84
+ }
85
+ .examplify-btn {
86
+ display: inline-flex;
87
+ align-items: center;
88
+ gap: 0.25rem;
89
+ padding: 0.25rem 0.5rem;
90
+ font-size: 0.75rem;
91
+ border: 1px solid #e5e7eb;
92
+ border-radius: 4px;
93
+ background: #f9fafb;
94
+ cursor: pointer;
95
+ transition: all 0.2s;
96
+ color: #374151;
97
+ }
98
+ .examplify-btn:hover {
99
+ background: #f3f4f6;
100
+ border-color: #d1d5db;
101
+ }
102
+ .examplify-iframe {
103
+ width: 100%;
104
+ border: 1px solid #e5e7eb;
105
+ border-radius: 6px;
106
+ margin-top: 0.5rem;
107
+ background: white;
108
+ }
109
+ .examplify-parent {
110
+ transition: outline 0.2s;
111
+ border-radius: 4px;
112
+ }
113
+ .examplify-parent:hover {
114
+ outline: 1px solid #d1d5db;
115
+ }
116
+ `;
117
+ document.head.appendChild(style);
118
+ }
119
+
120
+ // Helper: Placeholder Content
121
+ function getPlaceholderContent() {
122
+ return `<!DOCTYPE html>
123
+ <html>
124
+ <head>
125
+ <style>
126
+ body {
127
+ margin:0; padding:0; height:100vh;
128
+ display:flex; align-items:center; justify-content:center;
129
+ background:#f9fafb; color:#6b7280;
130
+ cursor:pointer; user-select:none;
131
+ font-family:system-ui,-apple-system,sans-serif;
132
+ transition: color 0.2s;
133
+ }
134
+ body:hover { color: #374151; }
135
+ .content { display:flex; align-items:center; gap:0.5rem; font-size:0.875rem; font-weight: 500; }
136
+ </style>
137
+ </head>
138
+ <body onclick="parent.postMessage({type:'examplify-run-click', id:'${iframeId}'}, '*')">
139
+ <div class="content">
140
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
141
+ <path d="M8 5v14l11-7z"/>
142
+ </svg>
143
+ <span>Click to run</span>
144
+ </div>
145
+ </body>
146
+ </html>`;
147
+ }
148
+
149
+ // Helper: Generate Iframe Content
150
+ function getIframeContent(codeContent) {
151
+ const currentTheme = document.documentElement.getAttribute('data-theme');
152
+ const themeAttr = currentTheme ? ` data-theme="${currentTheme}"` : '';
153
+ const autoResizeScript = autoResize ? `
154
+ <script>
155
+ const frameId = '${iframeId}';
156
+ function sendHeight() {
157
+ const height = Math.max(
158
+ document.body.scrollHeight,
159
+ document.body.offsetHeight,
160
+ document.documentElement.scrollHeight,
161
+ document.documentElement.offsetHeight
162
+ );
163
+ parent.postMessage({ type: 'examplify-resize', id: frameId, height: height }, '*');
164
+ }
165
+
166
+ window.addEventListener('load', () => {
167
+ sendHeight();
168
+ setTimeout(sendHeight, 300);
169
+ setTimeout(sendHeight, 1000);
170
+ });
171
+
172
+ if (typeof ResizeObserver !== 'undefined') {
173
+ const resizeObserver = new ResizeObserver(() => sendHeight());
174
+ resizeObserver.observe(document.body);
175
+ }
176
+
177
+ const mutationObserver = new MutationObserver(() => {
178
+ setTimeout(sendHeight, 50);
179
+ });
180
+ mutationObserver.observe(document.body, { childList: true, subtree: true, attributes: true });
181
+ </script>
182
+ ` : '';
183
+
184
+ return `<!DOCTYPE html>
185
+ <html${themeAttr}>
186
+ <head>
187
+ <style>
188
+ /* Hide body until stylesheets are loaded to prevent FOUC */
189
+ body {
190
+ font-family: system-ui, -apple-system, sans-serif;
191
+ padding: 1rem;
192
+ margin: 0;
193
+ opacity: 0;
194
+ transition: opacity 0.15s ease-in;
195
+ }
196
+ body.styles-ready { opacity: 1; }
197
+ </style>
198
+ ${styles ? styles.map(href => `<link rel="stylesheet" href="${href}">`).join('\n') : ''}
199
+ <script>
200
+ // Synchronously create the stylesheet-ready promise before any modules execute
201
+ window.__stylesheetsReady = (function() {
202
+ return new Promise(resolve => {
203
+ // Use requestAnimationFrame to ensure DOM is ready for querying
204
+ requestAnimationFrame(() => {
205
+ const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]'));
206
+ if (links.length === 0) {
207
+ resolve();
208
+ return;
209
+ }
210
+
211
+ let loaded = 0;
212
+ const checkDone = () => {
213
+ loaded++;
214
+ if (loaded >= links.length) resolve();
215
+ };
216
+
217
+ links.forEach(link => {
218
+ if (link.sheet) {
219
+ checkDone();
220
+ } else {
221
+ link.addEventListener('load', checkDone);
222
+ link.addEventListener('error', checkDone);
223
+ }
224
+ });
225
+
226
+ // Fallback timeout
227
+ setTimeout(resolve, 2000);
228
+ });
229
+ });
230
+ })();
231
+ <\/script>
232
+ ${modules ? modules.map(src => `<script type="module" src="${src}"></script>`).join('\n') : ''}
233
+ ${scripts ? scripts.map(src => `<script src="${src}"></script>`).join('\n') : ''}
234
+ <script type="module">
235
+ // Wait for stylesheets before initializing Lightview components
236
+ await window.__stylesheetsReady;
237
+ if (window.LightviewX) {
238
+ await window.LightviewX.initComponents({ shadowDefault: true });
239
+ }
240
+ </script>
241
+ </head>
242
+ <body>
243
+ ${language === 'html' ? codeContent : (html ? html : '<div id="example"></div>')}
244
+ ${language === 'html' ? '' : `<script ${type ? `type="${type}"` : ''}>
245
+ const render = (content) => {
246
+ const target = document.querySelector('#example');
247
+ target.innerHTML = '';
248
+ if (typeof content === 'string') {
249
+ target.innerHTML = content;
250
+ } else if (content && content.domEl) {
251
+ target.insertAdjacentElement('afterbegin', content.domEl);
252
+ } else if (content instanceof Node) {
253
+ target.insertAdjacentElement('afterbegin', content);
254
+ }
255
+ };
256
+ ${type === 'module' ? codeContent : `
257
+ // Wait for stylesheets before running example code
258
+ window.__stylesheetsReady.then(async () => {
259
+ ${codeContent}
260
+ });
261
+ `}
262
+ </script>`}
263
+ ${autoResizeScript}
264
+ <script>
265
+ // Reveal body and signal ready only after stylesheets are loaded
266
+ window.__stylesheetsReady.then(() => {
267
+ document.body.classList.add('styles-ready');
268
+ setTimeout(() => {
269
+ parent.postMessage({ type: 'examplify-ready', id: '${iframeId}' }, '*');
270
+ }, 50);
271
+ });
272
+
273
+ // Listen for theme changes from parent
274
+ window.addEventListener('message', (event) => {
275
+ if (event.data && event.data.type === 'theme-change' && event.data.theme) {
276
+ document.documentElement.setAttribute('data-theme', event.data.theme);
277
+ if (window.LightviewX && typeof window.LightviewX.setTheme === 'function') {
278
+ window.LightviewX.setTheme(event.data.theme);
279
+ }
280
+ }
281
+ });
282
+ </script>
283
+ </body>
284
+ </html>`;
285
+ }
286
+
287
+ // Helper: Run
288
+ function run() {
289
+ const content = getIframeContent(target.textContent);
290
+ iframe.style.background = '#fff';
291
+ iframe.srcdoc = content;
292
+ isRunning = true;
293
+ }
294
+
295
+ // Initialize: auto-run or show placeholder
296
+ if (autoRun) {
297
+ run();
298
+ } else {
299
+ iframe.srcdoc = getPlaceholderContent();
300
+ }
301
+
302
+ // Event Listeners
303
+ const runBtn = controls.querySelector('.examplify-run');
304
+ runBtn.addEventListener('click', run);
305
+
306
+ const copyBtn = controls.querySelector('.examplify-copy');
307
+ copyBtn.addEventListener('click', () => {
308
+ navigator.clipboard.writeText(target.textContent);
309
+
310
+ // Feedback
311
+ const originalHtml = copyBtn.innerHTML;
312
+ copyBtn.innerHTML = `
313
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
314
+ <polyline points="20 6 9 17 4 12"></polyline>
315
+ </svg>
316
+ <span>Copied!</span>
317
+ `;
318
+ setTimeout(() => copyBtn.innerHTML = originalHtml, 2000);
319
+ });
320
+
321
+ if (editable) {
322
+ const resetBtn = controls.querySelector('.examplify-reset');
323
+
324
+ // Reset action
325
+ resetBtn.addEventListener('click', () => {
326
+ target.textContent = originalContent;
327
+ resetBtn.style.display = 'none';
328
+ // Force reset even if running to reflect text change if any,
329
+ // but actually we want to reset to original content AND re-run
330
+ // OR go back to placeholder?
331
+ // Usually reset means "restore code to initial state".
332
+ // If the user modified code, we reset text.
333
+ // If they want to run the original code, they should click run?
334
+ // Or should we auto-run? Use Case: "Reset" usually implies "Fix my broken code".
335
+ // So re-running the original code makes sense.
336
+ if (isRunning) run();
337
+ });
338
+
339
+ // Watch for changes
340
+ target.addEventListener('input', () => {
341
+ if (target.textContent !== originalContent) {
342
+ resetBtn.style.display = 'inline-flex';
343
+ } else {
344
+ resetBtn.style.display = 'none';
345
+ }
346
+ });
347
+ }
348
+
349
+ // Global Message Listener (for resizing and run click)
350
+ window.addEventListener('message', (event) => {
351
+ if (!event.data || event.data.id !== iframeId) return;
352
+
353
+ if (event.data.type === 'examplify-resize' && autoResize) {
354
+ const h = event.data.height + 2; // buffer
355
+ iframe.style.height = Math.max(minHeight, Math.min(h, maxHeight)) + 'px';
356
+ }
357
+
358
+ if (event.data.type === 'examplify-run-click') {
359
+ run();
360
+ }
361
+ });
362
+
363
+ return { controls, iframe, target, run };
364
+ }
365
+
366
+ // Global Theme Observer (Host side)
367
+ if (typeof document !== 'undefined') {
368
+ let themeObserver = null;
369
+ const initThemeObserver = () => {
370
+ if (themeObserver) return;
371
+
372
+ themeObserver = new MutationObserver((mutations) => {
373
+ mutations.forEach((mutation) => {
374
+ if (mutation.attributeName === 'data-theme') {
375
+ const newTheme = document.documentElement.getAttribute('data-theme');
376
+ // Broadcast to all examplify iframes
377
+ document.querySelectorAll('.examplify-iframe').forEach(iframe => {
378
+ if (iframe.contentWindow) {
379
+ iframe.contentWindow.postMessage({ type: 'theme-change', theme: newTheme }, '*');
380
+ }
381
+ });
382
+ }
383
+ });
384
+ });
385
+
386
+ themeObserver.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
387
+ };
388
+
389
+ // Initialize when DOM is ready
390
+ if (document.readyState === 'loading') {
391
+ document.addEventListener('DOMContentLoaded', initThemeObserver);
392
+ } else {
393
+ initThemeObserver();
394
+ }
395
+ }