davaux 0.8.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 (333) hide show
  1. package/BASELINE.md +169 -0
  2. package/CLAUDE.md +518 -0
  3. package/LICENSE +21 -0
  4. package/README.md +36 -0
  5. package/ROADMAP.md +198 -0
  6. package/build.mjs +101 -0
  7. package/client/control.ts +247 -0
  8. package/client/hydrate.ts +37 -0
  9. package/client/index.ts +19 -0
  10. package/client/jsx-runtime.ts +209 -0
  11. package/client/resource.ts +122 -0
  12. package/client/signal.ts +211 -0
  13. package/client/store.ts +110 -0
  14. package/client/useHead.ts +63 -0
  15. package/dist/build/config.d.ts +3 -0
  16. package/dist/build/config.d.ts.map +1 -0
  17. package/dist/build/config.js +38 -0
  18. package/dist/build/config.js.map +7 -0
  19. package/dist/build/index.d.ts +2 -0
  20. package/dist/build/index.d.ts.map +1 -0
  21. package/dist/build/index.js +13 -0
  22. package/dist/build/index.js.map +7 -0
  23. package/dist/build/plugins.d.ts +7 -0
  24. package/dist/build/plugins.d.ts.map +1 -0
  25. package/dist/build/plugins.js +85 -0
  26. package/dist/build/plugins.js.map +7 -0
  27. package/dist/cli.d.ts +2 -0
  28. package/dist/cli.d.ts.map +1 -0
  29. package/dist/cli.js +427 -0
  30. package/dist/cli.js.map +7 -0
  31. package/dist/client/control.d.ts +49 -0
  32. package/dist/client/control.d.ts.map +1 -0
  33. package/dist/client/control.js +154 -0
  34. package/dist/client/control.js.map +7 -0
  35. package/dist/client/hydrate.d.ts +7 -0
  36. package/dist/client/hydrate.d.ts.map +1 -0
  37. package/dist/client/hydrate.js +23 -0
  38. package/dist/client/hydrate.js.map +7 -0
  39. package/dist/client/index.d.ts +12 -0
  40. package/dist/client/index.d.ts.map +1 -0
  41. package/dist/client/index.js +32 -0
  42. package/dist/client/index.js.map +7 -0
  43. package/dist/client/jsx-runtime.d.ts +40 -0
  44. package/dist/client/jsx-runtime.d.ts.map +1 -0
  45. package/dist/client/jsx-runtime.js +139 -0
  46. package/dist/client/jsx-runtime.js.map +7 -0
  47. package/dist/client/resource.d.ts +31 -0
  48. package/dist/client/resource.d.ts.map +1 -0
  49. package/dist/client/resource.js +64 -0
  50. package/dist/client/resource.js.map +7 -0
  51. package/dist/client/signal.d.ts +90 -0
  52. package/dist/client/signal.d.ts.map +1 -0
  53. package/dist/client/signal.js +115 -0
  54. package/dist/client/signal.js.map +7 -0
  55. package/dist/client/store.d.ts +26 -0
  56. package/dist/client/store.d.ts.map +1 -0
  57. package/dist/client/store.js +63 -0
  58. package/dist/client/store.js.map +7 -0
  59. package/dist/client/useHead.d.ts +28 -0
  60. package/dist/client/useHead.d.ts.map +1 -0
  61. package/dist/client/useHead.js +33 -0
  62. package/dist/client/useHead.js.map +7 -0
  63. package/dist/config.d.ts +182 -0
  64. package/dist/config.d.ts.map +1 -0
  65. package/dist/config.js +21 -0
  66. package/dist/config.js.map +7 -0
  67. package/dist/create-multisite.d.ts +2 -0
  68. package/dist/create-multisite.d.ts.map +1 -0
  69. package/dist/create-multisite.js +291 -0
  70. package/dist/create-multisite.js.map +7 -0
  71. package/dist/create.d.ts +2 -0
  72. package/dist/create.d.ts.map +1 -0
  73. package/dist/create.js +179 -0
  74. package/dist/create.js.map +7 -0
  75. package/dist/dev/blueprints.d.ts +11 -0
  76. package/dist/dev/blueprints.d.ts.map +1 -0
  77. package/dist/dev/blueprints.js +65 -0
  78. package/dist/dev/blueprints.js.map +7 -0
  79. package/dist/dev/components.d.ts +19 -0
  80. package/dist/dev/components.d.ts.map +1 -0
  81. package/dist/dev/components.js +87 -0
  82. package/dist/dev/components.js.map +7 -0
  83. package/dist/dev/insert.d.ts +11 -0
  84. package/dist/dev/insert.d.ts.map +1 -0
  85. package/dist/dev/insert.js +160 -0
  86. package/dist/dev/insert.js.map +7 -0
  87. package/dist/dev/remove.d.ts +53 -0
  88. package/dist/dev/remove.d.ts.map +1 -0
  89. package/dist/dev/remove.js +518 -0
  90. package/dist/dev/remove.js.map +7 -0
  91. package/dist/dev/watch.d.ts +26 -0
  92. package/dist/dev/watch.d.ts.map +1 -0
  93. package/dist/dev/watch.js +2905 -0
  94. package/dist/dev/watch.js.map +7 -0
  95. package/dist/errors.d.ts +6 -0
  96. package/dist/errors.d.ts.map +1 -0
  97. package/dist/errors.js +63 -0
  98. package/dist/errors.js.map +7 -0
  99. package/dist/generate.d.ts +2 -0
  100. package/dist/generate.d.ts.map +1 -0
  101. package/dist/generate.js +191 -0
  102. package/dist/generate.js.map +7 -0
  103. package/dist/index.d.ts +9 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +57 -0
  106. package/dist/index.js.map +7 -0
  107. package/dist/island.d.ts +24 -0
  108. package/dist/island.d.ts.map +1 -0
  109. package/dist/island.js +15 -0
  110. package/dist/island.js.map +7 -0
  111. package/dist/jsx-runtime.d.ts +406 -0
  112. package/dist/jsx-runtime.d.ts.map +1 -0
  113. package/dist/jsx-runtime.js +90 -0
  114. package/dist/jsx-runtime.js.map +7 -0
  115. package/dist/link.d.ts +27 -0
  116. package/dist/link.d.ts.map +1 -0
  117. package/dist/link.js +29 -0
  118. package/dist/link.js.map +7 -0
  119. package/dist/oml/fragment.d.ts +16 -0
  120. package/dist/oml/fragment.d.ts.map +1 -0
  121. package/dist/oml/fragment.js +26 -0
  122. package/dist/oml/fragment.js.map +7 -0
  123. package/dist/oml/index.d.ts +11 -0
  124. package/dist/oml/index.d.ts.map +1 -0
  125. package/dist/oml/index.js +21 -0
  126. package/dist/oml/index.js.map +7 -0
  127. package/dist/oml/jsx-runtime.d.ts +34 -0
  128. package/dist/oml/jsx-runtime.d.ts.map +1 -0
  129. package/dist/oml/jsx-runtime.js +59 -0
  130. package/dist/oml/jsx-runtime.js.map +7 -0
  131. package/dist/oml/jsx.d.ts +14 -0
  132. package/dist/oml/jsx.d.ts.map +1 -0
  133. package/dist/oml/jsx.js +96 -0
  134. package/dist/oml/jsx.js.map +7 -0
  135. package/dist/oml/page.d.ts +7 -0
  136. package/dist/oml/page.d.ts.map +1 -0
  137. package/dist/oml/page.js +6 -0
  138. package/dist/oml/page.js.map +7 -0
  139. package/dist/oml/render.d.ts +13 -0
  140. package/dist/oml/render.d.ts.map +1 -0
  141. package/dist/oml/render.js +117 -0
  142. package/dist/oml/render.js.map +7 -0
  143. package/dist/oml/types.d.ts +79 -0
  144. package/dist/oml/types.d.ts.map +1 -0
  145. package/dist/oml/types.js +64 -0
  146. package/dist/oml/types.js.map +7 -0
  147. package/dist/router/handler.d.ts +53 -0
  148. package/dist/router/handler.d.ts.map +1 -0
  149. package/dist/router/handler.js +342 -0
  150. package/dist/router/handler.js.map +7 -0
  151. package/dist/router/matcher.d.ts +21 -0
  152. package/dist/router/matcher.d.ts.map +1 -0
  153. package/dist/router/matcher.js +28 -0
  154. package/dist/router/matcher.js.map +7 -0
  155. package/dist/router/scanner.d.ts +17 -0
  156. package/dist/router/scanner.d.ts.map +1 -0
  157. package/dist/router/scanner.js +197 -0
  158. package/dist/router/scanner.js.map +7 -0
  159. package/dist/server/index.d.ts +23 -0
  160. package/dist/server/index.d.ts.map +1 -0
  161. package/dist/server/index.js +29 -0
  162. package/dist/server/index.js.map +7 -0
  163. package/dist/signal.d.ts +15 -0
  164. package/dist/signal.d.ts.map +1 -0
  165. package/dist/signal.js +29 -0
  166. package/dist/signal.js.map +7 -0
  167. package/dist/ssg.d.ts +45 -0
  168. package/dist/ssg.d.ts.map +1 -0
  169. package/dist/ssg.js +175 -0
  170. package/dist/ssg.js.map +7 -0
  171. package/dist/test/actions.test.d.ts +2 -0
  172. package/dist/test/actions.test.d.ts.map +1 -0
  173. package/dist/test/body-limits.test.d.ts +2 -0
  174. package/dist/test/body-limits.test.d.ts.map +1 -0
  175. package/dist/test/errors.test.d.ts +2 -0
  176. package/dist/test/errors.test.d.ts.map +1 -0
  177. package/dist/test/fixtures/routes/[id].page.d.ts +4 -0
  178. package/dist/test/fixtures/routes/[id].page.d.ts.map +1 -0
  179. package/dist/test/fixtures/routes/_error.d.ts +3 -0
  180. package/dist/test/fixtures/routes/_error.d.ts.map +1 -0
  181. package/dist/test/fixtures/routes/_global.d.ts +3 -0
  182. package/dist/test/fixtures/routes/_global.d.ts.map +1 -0
  183. package/dist/test/fixtures/routes/_layout-template.d.ts +3 -0
  184. package/dist/test/fixtures/routes/_layout-template.d.ts.map +1 -0
  185. package/dist/test/fixtures/routes/_layout.d.ts +3 -0
  186. package/dist/test/fixtures/routes/_layout.d.ts.map +1 -0
  187. package/dist/test/fixtures/routes/_layout_scripts.d.ts +3 -0
  188. package/dist/test/fixtures/routes/_layout_scripts.d.ts.map +1 -0
  189. package/dist/test/fixtures/routes/_middleware.d.ts +3 -0
  190. package/dist/test/fixtures/routes/_middleware.d.ts.map +1 -0
  191. package/dist/test/fixtures/routes/_redirect301_mw.d.ts +3 -0
  192. package/dist/test/fixtures/routes/_redirect301_mw.d.ts.map +1 -0
  193. package/dist/test/fixtures/routes/_redirect_mw.d.ts +3 -0
  194. package/dist/test/fixtures/routes/_redirect_mw.d.ts.map +1 -0
  195. package/dist/test/fixtures/routes/about.page.d.ts +3 -0
  196. package/dist/test/fixtures/routes/about.page.d.ts.map +1 -0
  197. package/dist/test/fixtures/routes/action.page.d.ts +6 -0
  198. package/dist/test/fixtures/routes/action.page.d.ts.map +1 -0
  199. package/dist/test/fixtures/routes/api/form-all.post.d.ts +3 -0
  200. package/dist/test/fixtures/routes/api/form-all.post.d.ts.map +1 -0
  201. package/dist/test/fixtures/routes/api/form-limited.post.d.ts +6 -0
  202. package/dist/test/fixtures/routes/api/form-limited.post.d.ts.map +1 -0
  203. package/dist/test/fixtures/routes/api/response-obj.get.d.ts +3 -0
  204. package/dist/test/fixtures/routes/api/response-obj.get.d.ts.map +1 -0
  205. package/dist/test/fixtures/routes/api/upload.post.d.ts +12 -0
  206. package/dist/test/fixtures/routes/api/upload.post.d.ts.map +1 -0
  207. package/dist/test/fixtures/routes/api/users.get.d.ts +6 -0
  208. package/dist/test/fixtures/routes/api/users.get.d.ts.map +1 -0
  209. package/dist/test/fixtures/routes/api/xml.get.d.ts +3 -0
  210. package/dist/test/fixtures/routes/api/xml.get.d.ts.map +1 -0
  211. package/dist/test/fixtures/routes/auth/_middleware.d.ts +3 -0
  212. package/dist/test/fixtures/routes/auth/_middleware.d.ts.map +1 -0
  213. package/dist/test/fixtures/routes/auth/protected.page.d.ts +3 -0
  214. package/dist/test/fixtures/routes/auth/protected.page.d.ts.map +1 -0
  215. package/dist/test/fixtures/routes/index.page.d.ts +3 -0
  216. package/dist/test/fixtures/routes/index.page.d.ts.map +1 -0
  217. package/dist/test/fixtures/routes/oml.page.d.ts +3 -0
  218. package/dist/test/fixtures/routes/oml.page.d.ts.map +1 -0
  219. package/dist/test/fixtures/routes/redirect.page.d.ts +3 -0
  220. package/dist/test/fixtures/routes/redirect.page.d.ts.map +1 -0
  221. package/dist/test/fixtures/routes/ssg/[slug].page.d.ts +5 -0
  222. package/dist/test/fixtures/routes/ssg/[slug].page.d.ts.map +1 -0
  223. package/dist/test/fixtures/routes/ssg/server.page.d.ts +4 -0
  224. package/dist/test/fixtures/routes/ssg/server.page.d.ts.map +1 -0
  225. package/dist/test/fixtures/routes/state.page.d.ts +3 -0
  226. package/dist/test/fixtures/routes/state.page.d.ts.map +1 -0
  227. package/dist/test/fixtures/routes/throw.page.d.ts +3 -0
  228. package/dist/test/fixtures/routes/throw.page.d.ts.map +1 -0
  229. package/dist/test/fixtures/routes/wiki/[...slug].page.d.ts +3 -0
  230. package/dist/test/fixtures/routes/wiki/[...slug].page.d.ts.map +1 -0
  231. package/dist/test/helpers.d.ts +37 -0
  232. package/dist/test/helpers.d.ts.map +1 -0
  233. package/dist/test/layouts.test.d.ts +2 -0
  234. package/dist/test/layouts.test.d.ts.map +1 -0
  235. package/dist/test/middleware.test.d.ts +2 -0
  236. package/dist/test/middleware.test.d.ts.map +1 -0
  237. package/dist/test/multipart.test.d.ts +2 -0
  238. package/dist/test/multipart.test.d.ts.map +1 -0
  239. package/dist/test/oml-routing.test.d.ts +2 -0
  240. package/dist/test/oml-routing.test.d.ts.map +1 -0
  241. package/dist/test/oml.test.d.ts +2 -0
  242. package/dist/test/oml.test.d.ts.map +1 -0
  243. package/dist/test/redirects.test.d.ts +2 -0
  244. package/dist/test/redirects.test.d.ts.map +1 -0
  245. package/dist/test/routing.test.d.ts +2 -0
  246. package/dist/test/routing.test.d.ts.map +1 -0
  247. package/dist/test/ssg.test.d.ts +2 -0
  248. package/dist/test/ssg.test.d.ts.map +1 -0
  249. package/dist/test/web-response.test.d.ts +2 -0
  250. package/dist/test/web-response.test.d.ts.map +1 -0
  251. package/dist/types.d.ts +314 -0
  252. package/dist/types.d.ts.map +1 -0
  253. package/dist/types.js +292 -0
  254. package/dist/types.js.map +7 -0
  255. package/package.json +103 -0
  256. package/pka.config.json +32 -0
  257. package/src/build/config.ts +42 -0
  258. package/src/build/index.ts +6 -0
  259. package/src/build/plugins.ts +118 -0
  260. package/src/cli.ts +502 -0
  261. package/src/config.ts +197 -0
  262. package/src/create-multisite.ts +310 -0
  263. package/src/create.ts +194 -0
  264. package/src/dev/blueprints.ts +75 -0
  265. package/src/dev/components.ts +108 -0
  266. package/src/dev/insert.ts +221 -0
  267. package/src/dev/remove.ts +677 -0
  268. package/src/dev/watch.ts +3098 -0
  269. package/src/env.d.ts +5 -0
  270. package/src/errors.ts +64 -0
  271. package/src/generate.ts +228 -0
  272. package/src/index.ts +67 -0
  273. package/src/island.ts +47 -0
  274. package/src/jsx-runtime.d.ts +408 -0
  275. package/src/jsx-runtime.d.ts.map +1 -0
  276. package/src/jsx-runtime.ts +536 -0
  277. package/src/link.ts +49 -0
  278. package/src/oml/fragment.ts +54 -0
  279. package/src/oml/index.ts +21 -0
  280. package/src/oml/jsx-runtime.ts +121 -0
  281. package/src/oml/jsx.ts +151 -0
  282. package/src/oml/page.ts +13 -0
  283. package/src/oml/render.ts +181 -0
  284. package/src/oml/types.ts +159 -0
  285. package/src/router/handler.ts +515 -0
  286. package/src/router/matcher.ts +52 -0
  287. package/src/router/scanner.ts +272 -0
  288. package/src/server/index.ts +49 -0
  289. package/src/signal.ts +39 -0
  290. package/src/ssg.ts +253 -0
  291. package/src/test/actions.test.ts +40 -0
  292. package/src/test/body-limits.test.ts +83 -0
  293. package/src/test/errors.test.ts +53 -0
  294. package/src/test/fixtures/routes/[id].page.ts +3 -0
  295. package/src/test/fixtures/routes/_error.ts +6 -0
  296. package/src/test/fixtures/routes/_global.ts +8 -0
  297. package/src/test/fixtures/routes/_layout-template.ts +7 -0
  298. package/src/test/fixtures/routes/_layout.ts +7 -0
  299. package/src/test/fixtures/routes/_layout_scripts.ts +8 -0
  300. package/src/test/fixtures/routes/_middleware.ts +8 -0
  301. package/src/test/fixtures/routes/_redirect301_mw.ts +5 -0
  302. package/src/test/fixtures/routes/_redirect_mw.ts +5 -0
  303. package/src/test/fixtures/routes/about.page.ts +6 -0
  304. package/src/test/fixtures/routes/action.page.ts +11 -0
  305. package/src/test/fixtures/routes/api/form-all.post.ts +5 -0
  306. package/src/test/fixtures/routes/api/form-limited.post.ts +6 -0
  307. package/src/test/fixtures/routes/api/response-obj.get.ts +17 -0
  308. package/src/test/fixtures/routes/api/upload.post.ts +14 -0
  309. package/src/test/fixtures/routes/api/users.get.ts +3 -0
  310. package/src/test/fixtures/routes/api/xml.get.ts +5 -0
  311. package/src/test/fixtures/routes/auth/_middleware.ts +11 -0
  312. package/src/test/fixtures/routes/auth/protected.page.ts +3 -0
  313. package/src/test/fixtures/routes/index.page.ts +3 -0
  314. package/src/test/fixtures/routes/oml.page.ts +7 -0
  315. package/src/test/fixtures/routes/redirect.page.ts +3 -0
  316. package/src/test/fixtures/routes/ssg/[slug].page.ts +8 -0
  317. package/src/test/fixtures/routes/ssg/server.page.ts +5 -0
  318. package/src/test/fixtures/routes/state.page.ts +4 -0
  319. package/src/test/fixtures/routes/throw.page.ts +5 -0
  320. package/src/test/fixtures/routes/wiki/[...slug].page.ts +3 -0
  321. package/src/test/helpers.ts +132 -0
  322. package/src/test/layouts.test.ts +76 -0
  323. package/src/test/middleware.test.ts +69 -0
  324. package/src/test/multipart.test.ts +91 -0
  325. package/src/test/oml-routing.test.ts +59 -0
  326. package/src/test/oml.test.ts +429 -0
  327. package/src/test/redirects.test.ts +32 -0
  328. package/src/test/routing.test.ts +118 -0
  329. package/src/test/ssg.test.ts +273 -0
  330. package/src/test/web-response.test.ts +33 -0
  331. package/src/types.ts +670 -0
  332. package/tsconfig.client.json +17 -0
  333. package/tsconfig.json +20 -0
@@ -0,0 +1,2905 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createReadStream, existsSync, watch as fsWatch, readFileSync, statSync, writeFileSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import { extname, join, relative } from "node:path";
5
+ import { context } from "esbuild";
6
+ import { cssCollectorPlugin, generateIslandsEntry, islandServerPlugin } from "../build/plugins.js";
7
+ import {
8
+ collectEsbuildPlugins,
9
+ collectScannerSuffixes,
10
+ pathsToAlias
11
+ } from "../config.js";
12
+ import { formatBuildErrors } from "../errors.js";
13
+ import { buildApp } from "../router/handler.js";
14
+ import { scanIslands, scanRoutes } from "../router/scanner.js";
15
+ import { startServer } from "../server/index.js";
16
+ import { scanBlueprints } from "./blueprints.js";
17
+ import { saveBlueprint, scanComponents, updateBlueprint } from "./components.js";
18
+ import { insertAfterElement, insertJsx } from "./insert.js";
19
+ import { getComponentFragment, getElementFragment, moveNode, removeElement, removeJsx, replaceComponentFragment, replaceElementAttrs, replaceElementFragment, replaceJsx, replaceTextContent } from "./remove.js";
20
+ const MIME = {
21
+ ".js": "application/javascript",
22
+ ".mjs": "application/javascript",
23
+ ".css": "text/css",
24
+ ".html": "text/html",
25
+ ".json": "application/json",
26
+ ".png": "image/png",
27
+ ".jpg": "image/jpeg",
28
+ ".jpeg": "image/jpeg",
29
+ ".gif": "image/gif",
30
+ ".svg": "image/svg+xml",
31
+ ".ico": "image/x-icon",
32
+ ".woff": "font/woff",
33
+ ".woff2": "font/woff2"
34
+ };
35
+ let _partialUpdatesScript = null;
36
+ function getPartialUpdatesScript() {
37
+ if (_partialUpdatesScript !== null) return _partialUpdatesScript;
38
+ try {
39
+ const r = createRequire(import.meta.url);
40
+ const templateForCjs = r.resolve("template-for-polyfill");
41
+ const templateForJs = readFileSync(
42
+ templateForCjs.replace(/template-for-polyfill\.cjs$/, "template-for-polyfill.js"),
43
+ "utf-8"
44
+ );
45
+ const htmlSettersMain = r.resolve("html-setters-polyfill");
46
+ const htmlSettersJs = readFileSync(
47
+ htmlSettersMain.replace(/index\.js$/, "index.min.js"),
48
+ "utf-8"
49
+ );
50
+ _partialUpdatesScript = `${templateForJs}
51
+ ${htmlSettersJs}`;
52
+ } catch {
53
+ _partialUpdatesScript = "/* partial-updates polyfills unavailable */";
54
+ }
55
+ return _partialUpdatesScript;
56
+ }
57
+ function serveStatic(publicDir) {
58
+ if (!existsSync(publicDir)) return null;
59
+ return (req, res) => {
60
+ const filePath = join(publicDir, new URL(req.url ?? "/", "http://x").pathname);
61
+ if (!filePath.startsWith(`${publicDir}/`) && filePath !== publicDir) return false;
62
+ if (!existsSync(filePath) || !statSync(filePath).isFile()) return false;
63
+ const mime = MIME[extname(filePath)] ?? "application/octet-stream";
64
+ res.writeHead(200, { "Content-Type": mime });
65
+ createReadStream(filePath).pipe(res);
66
+ return true;
67
+ };
68
+ }
69
+ const sseClients = /* @__PURE__ */ new Set();
70
+ let serverReady = false;
71
+ let reloadTimer;
72
+ function sendToClients(data) {
73
+ for (const client of sseClients) {
74
+ try {
75
+ client.write(`data: ${data}
76
+
77
+ `);
78
+ } catch {
79
+ sseClients.delete(client);
80
+ }
81
+ }
82
+ }
83
+ function scheduleReload() {
84
+ if (!serverReady) return;
85
+ if (reloadTimer) clearTimeout(reloadTimer);
86
+ reloadTimer = setTimeout(() => {
87
+ reloadTimer = void 0;
88
+ sendToClients("reload");
89
+ }, 50);
90
+ }
91
+ const SHARED_WORKER_SCRIPT = `var ports=[];var source=null;
92
+ self.onconnect=function(ev){
93
+ var port=ev.ports[0];port.start();ports.push(port);
94
+ if(!source){
95
+ source=new EventSource('/_davaux/livereload');
96
+ source.onmessage=function(e){
97
+ ports=ports.filter(function(p){try{p.postMessage(e.data);return true}catch(_){return false}});
98
+ };
99
+ }
100
+ };`;
101
+ const LIVERELOAD_SCRIPT = `;(function(){
102
+ var overlay=null
103
+ function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')}
104
+ function show(errors){
105
+ if(!overlay){
106
+ overlay=document.createElement('div')
107
+ overlay.style.cssText='position:fixed;inset:0;z-index:99999;overflow:auto;background:#0d0d0d;color:#e8e8e8;font:13px/1.6 monospace;padding:2rem;box-sizing:border-box'
108
+ document.body.appendChild(overlay)
109
+ }
110
+ overlay.innerHTML='<p style="color:#ff5555;font-size:1.1em;margin:0 0 1.5rem 0"><b>[davaux] Build Error</b></p>'+
111
+ errors.map(function(e){
112
+ var loc=e.file?(e.file+(e.line?':'+e.line+':'+e.column:'')):'unknown'
113
+ return '<div style="margin-bottom:1.25rem;border:1px solid #ff3333;border-radius:6px;padding:1rem">'+
114
+ '<div style="color:#888;font-size:0.9em;margin-bottom:0.4rem">'+esc(loc)+'</div>'+
115
+ '<div style="color:#ff6b6b">'+esc(e.text)+'</div>'+
116
+ (e.lineText?'<pre style="margin:0.75rem 0 0;padding:0.5rem;background:#1a1a1a;border-radius:4px;overflow:auto;color:#aaa">'+esc(e.lineText)+'</pre>':'')+
117
+ '</div>'
118
+ }).join('')
119
+ }
120
+ function handle(data){
121
+ if(data==='reload'){location.reload()}
122
+ else if(data.slice(0,6)==='error:'){show(JSON.parse(data.slice(6)))}
123
+ }
124
+ if(typeof SharedWorker!=='undefined'){
125
+ var w=new SharedWorker('/_davaux/livereload-worker.js')
126
+ w.port.onmessage=function(ev){handle(ev.data)}
127
+ w.port.start()
128
+ } else {
129
+ new EventSource('/_davaux/livereload').onmessage=function(ev){handle(ev.data)}
130
+ }
131
+ })()`;
132
+ const INSPECTOR_SCRIPT = `;(function(){
133
+ if(window!==window.top)return
134
+ var KEY='_dv_ins'
135
+ var isOpen=sessionStorage.getItem(KEY)==='1'
136
+ var host=document.createElement('div')
137
+ var shadow=host.attachShadow({mode:'open'})
138
+ document.body.appendChild(host)
139
+ var st=document.createElement('style')
140
+ st.textContent=':host{all:initial;display:block;position:fixed;bottom:12px;right:12px;z-index:2147483647;font:13px/1.5 ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace}:host(.open){inset:0}*{box-sizing:border-box}.badge{display:block;padding:4px 10px;background:#0f0f1a;color:#7cc5f0;border:1px solid #2a2a4a;border-radius:20px;cursor:pointer;font:700 11px/1 inherit;letter-spacing:.05em;box-shadow:0 2px 8px rgba(0,0,0,.4);white-space:nowrap}.badge:hover{background:#1a1a2e}.edbtn{display:block;margin-top:6px;padding:4px 10px;background:#1a2a1a;color:#7eca9c;border:1px solid #2a4a2a;border-radius:20px;font:700 11px/1 inherit;letter-spacing:.05em;text-decoration:none;text-align:center;box-shadow:0 2px 8px rgba(0,0,0,.4);white-space:nowrap}.edbtn:hover{background:#243a24}:host(.open) .badge,:host(.open) .edbtn{display:none}.panel{display:none;position:absolute;inset:0;background:#0f0f1a;flex-direction:column;overflow:hidden}:host(.open) .panel{display:flex}.hd{display:flex;align-items:center;gap:8px;padding:8px 12px;background:#080816;border-bottom:1px solid #2a2a4a;flex-shrink:0}.title{color:#7cc5f0;font-weight:700;font-size:11px;letter-spacing:.1em;flex:1}.chip{font-size:10px;font-weight:700;padding:2px 7px;border-radius:10px;letter-spacing:.05em;display:none}.chip.oml{display:inline;background:#1a2a1a;color:#7eca9c;border:1px solid #2a4a2a}.chip.dom{display:inline;background:#0f0f1a;color:#aaa;border:1px solid #333}.xb{background:none;border:none;color:#555;cursor:pointer;font:inherit;padding:0;line-height:1}.xb:hover{color:#e8e8e8}.bd{overflow:auto;flex:1;padding:6px 0}.row{display:flex;align-items:baseline;padding:1px 8px;white-space:nowrap;border-radius:3px;color:#e8e8e8;cursor:default;-webkit-user-select:none;user-select:none}.row.c:hover{background:#1a1a2e;cursor:pointer}.tog{width:14px;font-size:9px;color:#666;flex-shrink:0;text-align:center}.el{color:#7cc5f0}.co{color:#7eca9c}.fr{color:#666}.tx{color:#888;font-style:italic}.pk{color:#b89eff}.pv{color:#f1c27d}.empty{padding:12px;color:#555;font-style:italic;text-align:center}'
141
+ shadow.appendChild(st)
142
+ ;(function(){var p=(window.__DVX__&&window.__DVX__.pos)||'bottom-right';if(p!=='bottom-right'){var ps=document.createElement('style');var css=':host:not(.open){';css+=p.indexOf('top')>-1?'top:12px;bottom:auto;':'bottom:12px;top:auto;';css+=p.indexOf('left')>-1?'left:12px;right:auto;':'right:12px;left:auto;';css+='}';ps.textContent=css;shadow.appendChild(ps)}})()
143
+ var badge=document.createElement('button')
144
+ badge.className='badge'
145
+ badge.textContent=(window.__DVX__&&window.__DVX__.label)||'Inspector'
146
+ badge.title=((window.__DVX__&&window.__DVX__.label)||'Inspector')+' (Ctrl+Shift+O)'
147
+ shadow.appendChild(badge)
148
+ var edbtn=document.createElement('a')
149
+ edbtn.className='edbtn'
150
+ edbtn.textContent='Edit'
151
+ edbtn.title='Visual Editor (Ctrl+Shift+E)'
152
+ edbtn.href='/_davaux/editor?page='+encodeURIComponent(location.pathname+location.search)
153
+ shadow.appendChild(edbtn)
154
+ var panel=document.createElement('div')
155
+ panel.className='panel'
156
+ var hd=document.createElement('div')
157
+ hd.className='hd'
158
+ var ttl=document.createElement('span')
159
+ ttl.className='title'
160
+ ttl.textContent='INSPECTOR'
161
+ var chip=document.createElement('span')
162
+ chip.className='chip'
163
+ var xb=document.createElement('button')
164
+ xb.className='xb'
165
+ xb.textContent='\u2715'
166
+ xb.title='Close'
167
+ hd.appendChild(ttl)
168
+ hd.appendChild(chip)
169
+ hd.appendChild(xb)
170
+ var bd=document.createElement('div')
171
+ bd.className='bd'
172
+ panel.appendChild(hd)
173
+ panel.appendChild(bd)
174
+ shadow.appendChild(panel)
175
+ function setOpen(v){
176
+ isOpen=!!v
177
+ sessionStorage.setItem(KEY,v?'1':'0')
178
+ host.classList.toggle('open',!!v)
179
+ if(v)load()
180
+ }
181
+ badge.onclick=function(){setOpen(!isOpen)}
182
+ xb.onclick=function(){setOpen(false)}
183
+ document.addEventListener('keydown',function(e){
184
+ if(e.ctrlKey&&e.shiftKey&&(e.key==='O'||e.key==='o')){e.preventDefault();setOpen(!isOpen)}
185
+ if(e.ctrlKey&&e.shiftKey&&(e.key==='E'||e.key==='e')){e.preventDefault();window.location.href='/_davaux/editor?page='+encodeURIComponent(location.pathname+location.search)}
186
+ })
187
+ if(isOpen)setOpen(true)
188
+ function setMode(mode){
189
+ badge.textContent=mode
190
+ chip.textContent=mode
191
+ chip.className='chip '+mode.toLowerCase()
192
+ }
193
+ function load(){
194
+ bd.innerHTML='<div class="empty">Loading\u2026</div>'
195
+ fetch('/_davaux/inspector?url='+encodeURIComponent(location.pathname+location.search))
196
+ .then(function(r){return r.json()})
197
+ .then(function(d){
198
+ bd.innerHTML=''
199
+ if(d.error){
200
+ setMode('DOM')
201
+ var bodySnap=domSnap(document.body)
202
+ if(bodySnap)bd.appendChild(tree(bodySnap,0))
203
+ return
204
+ }
205
+ setMode('OML')
206
+ bd.appendChild(tree(d.node,0))
207
+ })
208
+ .catch(function(){bd.innerHTML='<div class="empty">No tree available</div>'})
209
+ }
210
+ function domSnap(el){
211
+ if(el===host)return null
212
+ if(el.nodeType===3){var v=(el.textContent||'').trim();return v?{type:'#text',value:v}:null}
213
+ if(el.nodeType!==1)return null
214
+ var tag=el.tagName.toLowerCase()
215
+ var p={};for(var i=0;i<el.attributes.length;i++){var a=el.attributes[i];p[a.name]=a.value}
216
+ var cs=[];for(var j=0;j<el.childNodes.length;j++){var c=domSnap(el.childNodes[j]);if(c!==null)cs.push(c)}
217
+ return{type:'element',tag:tag,id:undefined,props:p,children:cs}
218
+ }
219
+ function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')}
220
+ function propHtml(p){
221
+ if(!p)return''
222
+ var ks=Object.keys(p).filter(function(k){return!/^on[A-Z]/.test(k)&&k!=='children'&&k!=='dangerouslySetInnerHTML'})
223
+ var out=ks.slice(0,3).map(function(k){
224
+ var v=p[k],s=typeof v==='string'?'"'+(v.length>18?v.slice(0,18)+'\u2026':esc(v))+'"':esc(String(v))
225
+ return' <span class="pk">'+esc(k)+'</span>=<span class="pv">'+s+'</span>'
226
+ }).join('')
227
+ if(ks.length>3)out+=' <span style="color:#555">+'+(ks.length-3)+'</span>'
228
+ return out
229
+ }
230
+ function tree(node,depth){
231
+ var w=document.createElement('div')
232
+ var pad=(depth*14+8)+'px'
233
+ if(node===null){
234
+ var r=document.createElement('div')
235
+ r.className='row'
236
+ r.style.paddingLeft=pad
237
+ r.innerHTML='<span class="tog"> </span><span class="fr">null</span>'
238
+ w.appendChild(r)
239
+ return w
240
+ }
241
+ if(node.type==='#text'){
242
+ var v=String(node.value||'')
243
+ var r=document.createElement('div')
244
+ r.className='row'
245
+ r.style.paddingLeft=pad
246
+ r.innerHTML='<span class="tog"> </span><span class="tx">"'+(v.length>60?esc(v.slice(0,60))+'\u2026':esc(v))+'"</span>'
247
+ w.appendChild(r)
248
+ return w
249
+ }
250
+ if(node.type==='#raw'){
251
+ var v=String(node.value||'')
252
+ var r=document.createElement('div')
253
+ r.className='row'
254
+ r.style.paddingLeft=pad
255
+ r.innerHTML='<span class="tog"> </span><span class="fr">[raw html] </span><span class="tx">'+(v.length>60?esc(v.slice(0,60))+'\u2026':esc(v))+'</span>'
256
+ w.appendChild(r)
257
+ return w
258
+ }
259
+ var cs=node.type==='#component'
260
+ ?(node.output&&node.output.type!=='#raw'?[node.output]:(node.children||[]).filter(function(c){return c!==null}))
261
+ :(node.children||[]).filter(function(c){return c!==null})
262
+ var hk=cs.length>0
263
+ var closed=false
264
+ var row=document.createElement('div')
265
+ row.className='row'+(hk?' c':'')
266
+ row.style.paddingLeft=pad
267
+ var tog=document.createElement('span')
268
+ tog.className='tog'
269
+ tog.textContent=hk?'\u25BC':' '
270
+ var lbl=document.createElement('span')
271
+ if(node.type==='element'){
272
+ lbl.innerHTML='<span class="el">&lt;'+esc(node.tag)+'</span>'+propHtml(node.props)+'<span class="el">&gt;</span>'
273
+ }else if(node.type==='#component'){
274
+ lbl.innerHTML='<span class="co">\u25C8 '+esc(node.name)+'</span>'+propHtml(node.props)
275
+ }else{
276
+ lbl.innerHTML='<span class="fr">&lt;&gt;</span>'
277
+ }
278
+ row.appendChild(tog)
279
+ row.appendChild(lbl)
280
+ w.appendChild(row)
281
+ if(hk){
282
+ var kc=document.createElement('div')
283
+ cs.forEach(function(c){kc.appendChild(tree(c,depth+1))})
284
+ w.appendChild(kc)
285
+ row.onclick=function(){
286
+ closed=!closed
287
+ kc.style.display=closed?'none':''
288
+ tog.textContent=closed?'\u25B6':'\u25BC'
289
+ }
290
+ }
291
+ return w
292
+ }
293
+ })()`;
294
+ const EDITOR_HTML = `<!DOCTYPE html>
295
+ <html lang="en">
296
+ <head>
297
+ <meta charset="utf-8">
298
+ <title>Davaux Visual Editor</title>
299
+ <script type="importmap">
300
+ {
301
+ "imports": {
302
+ "https://esm.sh/@codemirror/state@^6.6.0?target=es2022": "https://esm.sh/@codemirror/state@6.6.0/es2022/state.mjs",
303
+ "https://esm.sh/@codemirror/state@^6.0.0?target=es2022": "https://esm.sh/@codemirror/state@6.6.0/es2022/state.mjs",
304
+ "https://esm.sh/@codemirror/view@^6.27.0?target=es2022": "https://esm.sh/@codemirror/view@6.43.0/es2022/view.mjs",
305
+ "https://esm.sh/@codemirror/view@^6.23.0?target=es2022": "https://esm.sh/@codemirror/view@6.43.0/es2022/view.mjs",
306
+ "https://esm.sh/@codemirror/language@^6.0.0?target=es2022": "https://esm.sh/@codemirror/language@6.12.3/es2022/language.mjs",
307
+ "https://esm.sh/@codemirror/language@^6.3.0?target=es2022": "https://esm.sh/@codemirror/language@6.12.3/es2022/language.mjs",
308
+ "https://esm.sh/@codemirror/autocomplete@^6.7.1?target=es2022": "https://esm.sh/@codemirror/autocomplete@6.20.2/es2022/autocomplete.mjs",
309
+ "https://esm.sh/@codemirror/lang-html@^6.0.0?target=es2022": "https://esm.sh/@codemirror/lang-html@6.4.11/es2022/lang-html.mjs",
310
+ "https://esm.sh/@lezer/common@^1.1.0?target=es2022": "https://esm.sh/@lezer/common@1.5.2/es2022/common.mjs",
311
+ "https://esm.sh/@lezer/common@^1.5.0?target=es2022": "https://esm.sh/@lezer/common@1.5.2/es2022/common.mjs",
312
+ "https://esm.sh/@lezer/highlight@^1.0.0?target=es2022": "https://esm.sh/@lezer/highlight@1.2.3/es2022/highlight.mjs",
313
+ "https://esm.sh/crelt@^1.0.6?target=es2022": "https://esm.sh/crelt@1.0.6/es2022/crelt.mjs"
314
+ }
315
+ }
316
+ </script>
317
+ <style>
318
+ :root{
319
+ --dvx-bg:#0d0d1a;--dvx-surf:#0f0f1a;--dvx-hd:#080816;--dvx-inp:#1a1a2e;
320
+ --dvx-bdr:#2a2a4a;--dvx-bdr-hi:#4a4a8a;
321
+ --dvx-txt:#e8e8e8;--dvx-txt-dim:#aaa;--dvx-txt-faint:#666;--dvx-txt-ghost:#555;
322
+ --dvx-row-hover:#1a1a2e;--dvx-row-sel:#1a2a3a;
323
+ }
324
+ [data-dvx-theme="light"]{
325
+ --dvx-bg:#f8f9fa;--dvx-surf:#ffffff;--dvx-hd:#e9ecef;--dvx-inp:#f1f3f5;
326
+ --dvx-bdr:#ced4da;--dvx-bdr-hi:#4c6ef5;
327
+ --dvx-txt:#212529;--dvx-txt-dim:#343a40;--dvx-txt-faint:#495057;--dvx-txt-ghost:#6c757d;
328
+ --dvx-row-hover:#f1f3f5;--dvx-row-sel:#dbe4ff;
329
+ }
330
+ *{box-sizing:border-box;margin:0;padding:0}
331
+ body{display:flex;flex-direction:column;height:100vh;background:var(--dvx-bg);color:var(--dvx-txt);font:13px/1.5 ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace}
332
+ header{display:flex;align-items:center;gap:12px;padding:8px 16px;background:var(--dvx-hd);border-bottom:1px solid var(--dvx-bdr);flex-shrink:0}
333
+ .logo{color:#7cc5f0;font-weight:700;font-size:11px;letter-spacing:.1em}
334
+ .page-bar{display:flex;align-items:center;gap:8px;flex:1}
335
+ .page-bar span{color:var(--dvx-txt-faint);font-size:11px}
336
+ .pg-wrap{position:relative;display:inline-flex;align-items:center}
337
+ .page-input{background:var(--dvx-inp);border:1px solid var(--dvx-bdr);color:var(--dvx-txt);padding:3px 22px 3px 8px;border-radius:4px;font:inherit;width:240px}
338
+ .page-input:focus{outline:none;border-color:var(--dvx-bdr-hi)}
339
+ .pg-clear{position:absolute;right:6px;background:none;border:none;color:var(--dvx-txt-ghost);cursor:pointer;font:inherit;font-size:13px;line-height:1;padding:0;display:none}
340
+ .pg-clear:hover{color:var(--dvx-txt-dim)}
341
+ .hbtn{background:#1a2a3a;border:1px solid #2a4a6a;color:#7cc5f0;padding:3px 10px;border-radius:4px;cursor:pointer;font:inherit;text-decoration:none;display:inline-block}
342
+ .hbtn:hover{background:#243a4a}
343
+ .main{display:flex;flex:1;overflow:hidden}
344
+ .palette{width:260px;flex-shrink:0;background:var(--dvx-surf);border-right:1px solid var(--dvx-bdr);display:flex;flex-direction:column;overflow:hidden}
345
+ .section-hd{color:var(--dvx-txt-faint);font-size:10px;font-weight:700;letter-spacing:.1em;padding:4px 4px 8px;flex-shrink:0}
346
+ .bp-card{background:var(--dvx-inp);border:1px solid var(--dvx-bdr);border-radius:6px;margin-bottom:6px;cursor:pointer;overflow:hidden;flex-shrink:0}
347
+ .bp-card:hover{border-color:var(--dvx-bdr-hi)}
348
+ .bp-card.sel{border-color:#7cc5f0}
349
+ .bp-prev{padding:8px;min-height:36px;background:var(--dvx-surf);overflow:hidden;max-height:72px;font-size:12px}
350
+ .bp-lbl{padding:5px 8px;font-size:11px;font-weight:700;color:#7eca9c;border-top:1px solid var(--dvx-bdr)}
351
+ .empty{color:var(--dvx-txt-ghost);font-style:italic;padding:8px 4px;font-size:11px}
352
+ .center{flex:1;background:var(--dvx-bg);display:flex;flex-direction:column;overflow:hidden}
353
+ .ctoolbar{display:flex;align-items:center;gap:4px;padding:4px 8px;background:var(--dvx-hd);border-bottom:1px solid var(--dvx-bdr);flex-shrink:0}
354
+ .tool-btn{background:none;border:1px solid transparent;color:var(--dvx-txt-ghost);cursor:pointer;font:700 10px/1 inherit;letter-spacing:.05em;padding:4px 10px;border-radius:4px}
355
+ .tool-btn:hover{color:var(--dvx-txt-dim);border-color:var(--dvx-bdr)}
356
+ .tool-btn.active{background:var(--dvx-inp);color:#7cc5f0;border-color:var(--dvx-bdr-hi)}
357
+ .center iframe{flex:1;width:100%;border:none;background:#fff}
358
+ .details{width:260px;flex-shrink:0;background:var(--dvx-surf);border-left:1px solid var(--dvx-bdr);overflow-y:auto;padding:12px;display:flex;flex-direction:column;gap:0}
359
+ .d-title{color:#7cc5f0;font-weight:700;font-size:13px;margin-bottom:12px}
360
+ .d-lbl{color:var(--dvx-txt-faint);font-size:10px;font-weight:700;letter-spacing:.1em;margin:12px 0 6px}
361
+ .prop-row{margin-bottom:4px;font-size:12px}
362
+ .pn{color:#b89eff}
363
+ .pt{color:#888}
364
+ .pr{color:#ff6b6b;font-size:10px}
365
+ .jsx-block{background:var(--dvx-inp);border:1px solid var(--dvx-bdr);border-radius:4px;padding:8px;white-space:pre;overflow-x:auto;font-size:11px;color:var(--dvx-txt);margin-bottom:8px}
366
+ .cbtn{background:#1a3a1a;border:1px solid #2a6a2a;color:#7eca9c;padding:4px 12px;border-radius:4px;cursor:pointer;font:inherit;width:100%}
367
+ .cbtn:hover{background:#243a24}
368
+ .cbtn.ok{background:#1a4a1a;color:#5ef05e}
369
+ .ibtn{background:#1a2a3a;border-color:#2a4a6a;color:#7cc5f0;margin-top:4px}
370
+ .ibtn:hover{background:#243a4a}
371
+ .ins-st{font-size:10px;margin-top:5px;min-height:14px;color:#888;word-break:break-all}
372
+ .ins-st.ok{color:#7eca9c}
373
+ .ins-st.warn{color:#f5a623}
374
+ .ins-st.err{color:#ff8888}
375
+ .no-sel{color:var(--dvx-txt-ghost);font-style:italic;padding:8px 0;font-size:12px}
376
+ .src-ed{width:100%;font:12px/1.6 ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;background:var(--dvx-hd);border:1px solid var(--dvx-bdr);color:var(--dvx-txt);padding:12px;tab-size:2;white-space:pre;resize:none}
377
+ .src-ed:focus{outline:none;border-color:var(--dvx-bdr-hi)}
378
+ .src-file-lbl{font-size:10px;color:var(--dvx-txt-ghost);word-break:break-all;padding:0 2px}
379
+ .src-modal{position:fixed;inset:0;z-index:1000;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center}
380
+ .src-modal-box{display:flex;flex-direction:column;width:80vw;height:82vh;background:var(--dvx-surf);border:1px solid var(--dvx-bdr);border-radius:8px;overflow:hidden;box-shadow:0 8px 40px rgba(0,0,0,.4)}
381
+ .src-modal-hd{display:flex;align-items:center;gap:8px;padding:8px 12px;background:var(--dvx-hd);border-bottom:1px solid var(--dvx-bdr);flex-shrink:0}
382
+ .src-modal-hd .cbtn{width:auto;flex-shrink:0}
383
+ .src-modal-hd #src-modal-title{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;word-break:normal}
384
+ .src-editor-area{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}
385
+ .src-modal-ta{flex:1;border-radius:0;border:none;border-top:1px solid var(--dvx-bdr);padding:12px 16px;min-height:0;background:var(--dvx-surf);color:var(--dvx-txt)}
386
+ #src-cm-host{flex:1;min-height:0;overflow:hidden;display:none;border-top:1px solid var(--dvx-bdr)}
387
+ #src-cm-host .cm-editor{height:100%!important;background:var(--dvx-bg)}
388
+ #src-cm-host .cm-scroller{overflow:auto!important}
389
+ .src-modal-ft{display:flex;align-items:center;gap:8px;padding:4px 8px;background:var(--dvx-hd);border-top:1px solid var(--dvx-bdr);flex-shrink:0}
390
+ .src-modal-ft .ins-st{flex:1;margin:0}
391
+ .xbtn{background:none;border:none;color:var(--dvx-txt-ghost);cursor:pointer;font:inherit;font-size:14px;line-height:1;padding:0 2px}.xbtn:hover{color:var(--dvx-txt)}
392
+ .pi-row{margin-bottom:6px;display:flex;flex-direction:column;gap:2px}
393
+ .pi-lbl{font-size:11px}.pi-lbl .pn{color:#b89eff}.pi-lbl .pt{color:var(--dvx-txt-faint)}.pi-lbl .pr{color:#ff6b6b}
394
+ .pi{background:var(--dvx-inp);border:1px solid var(--dvx-bdr);color:var(--dvx-txt);padding:3px 6px;border-radius:3px;font:inherit;font-size:11px;width:100%}
395
+ .pi:focus{outline:none;border-color:var(--dvx-bdr-hi)}
396
+ .pi-cb{accent-color:#7cc5f0;width:14px;height:14px;cursor:pointer}
397
+ .el-chip{display:inline-block;background:#1a2a3a;border:1px solid #2a4a6a;border-radius:4px;padding:2px 7px;font-size:11px;color:#7cc5f0;margin-bottom:8px}
398
+ .comp-chip{display:inline-block;background:#1a2a1a;border:1px solid #2a4a2a;border-radius:4px;padding:2px 7px;font-size:11px;color:#7eca9c;margin-bottom:4px}
399
+ .det-tabs{display:flex;border-bottom:1px solid var(--dvx-bdr);flex-shrink:0;margin-bottom:4px}
400
+ .dtab{background:none;border:none;border-bottom:2px solid transparent;color:var(--dvx-txt-faint);cursor:pointer;font:700 10px/1 inherit;letter-spacing:.1em;padding:7px 12px;flex:1}
401
+ .dtab.active{color:#7cc5f0;border-bottom-color:#7cc5f0}
402
+ .dtab:hover:not(.active){color:var(--dvx-txt-dim)}
403
+ .cv-row{margin-bottom:10px}
404
+ .cv-name{color:#b89eff;font-size:10px;margin-bottom:3px;word-break:break-all}
405
+ .cv-color-row{display:flex;gap:4px;align-items:center}
406
+ .cv-color{width:32px;height:24px;padding:1px 2px;border:1px solid var(--dvx-bdr);border-radius:3px;background:var(--dvx-inp);cursor:pointer;flex-shrink:0}
407
+ .cv-text{background:var(--dvx-inp);border:1px solid var(--dvx-bdr);color:var(--dvx-txt);padding:3px 6px;border-radius:3px;font:inherit;font-size:11px;width:100%;min-width:0}
408
+ .cv-text:focus{outline:none;border-color:var(--dvx-bdr-hi)}
409
+ .cv-color-row .cv-text{flex:1;width:auto}
410
+ .pal-tabs{display:flex;border-bottom:1px solid var(--dvx-bdr);flex-shrink:0}
411
+ .ptab{background:none;border:none;border-bottom:2px solid transparent;color:var(--dvx-txt-faint);cursor:pointer;font:700 10px/1 inherit;letter-spacing:.1em;padding:7px 8px;flex:1}
412
+ .ptab.active{color:#7cc5f0;border-bottom-color:#7cc5f0}
413
+ .ptab:hover:not(.active){color:var(--dvx-txt-dim)}
414
+ .pal-panel{display:none;flex:1;overflow-y:auto;padding:8px;flex-direction:column}
415
+ .pal-panel.active{display:flex}
416
+ .conv-hd{display:flex;gap:4px;margin-bottom:8px;align-items:center;flex-shrink:0}
417
+ .conv-dir{background:var(--dvx-inp);border:1px solid var(--dvx-bdr);color:var(--dvx-txt);padding:3px 6px;border-radius:3px;font:inherit;font-size:11px;flex:1;min-width:0}
418
+ .conv-dir:focus{outline:none;border-color:var(--dvx-bdr-hi)}
419
+ .conv-go{background:#1a2a3a;border:1px solid #2a4a6a;color:#7cc5f0;padding:3px 8px;border-radius:3px;cursor:pointer;font:inherit;font-size:11px;flex-shrink:0}
420
+ .conv-go:hover{background:#243a4a}
421
+ .conv-item{background:var(--dvx-inp);border:1px solid var(--dvx-bdr);border-radius:4px;padding:6px 8px;margin-bottom:4px;flex-shrink:0}
422
+ .conv-name{color:#7eca9c;font-weight:700;font-size:11px;margin-bottom:2px}
423
+ .conv-meta{color:var(--dvx-txt-ghost);font-size:10px;margin-bottom:4px;word-break:break-all}
424
+ .conv-btn{background:#1a3a1a;border:1px solid #2a5a2a;color:#7eca9c;padding:2px 8px;border-radius:3px;cursor:pointer;font:inherit;font-size:10px;letter-spacing:.05em}
425
+ .conv-btn:hover:not([disabled]){background:#243a24}
426
+ .conv-btn.done{background:#1a4a1a;color:#5ef05e;border-color:#2a6a2a;cursor:default}
427
+ .conv-btn.err{background:#3a1a1a;color:#ff8888;border-color:#5a2a2a}
428
+ .conv-acts{display:flex;gap:4px;align-items:center}
429
+ .conv-exists{font-size:10px;color:#5ef05e;flex:1}
430
+ .conv-upd{background:#1a2a3a;border:1px solid #2a4a6a;color:#7cc5f0}
431
+ .conv-upd:hover:not([disabled]){background:#243a4a}
432
+ .ebtn{background:#1a2a3a;border:1px solid #2a4a6a;color:#7cc5f0}
433
+ .ebtn:hover{background:#243a4a}
434
+ .ep-row{display:flex;gap:3px;align-items:center;margin-bottom:3px}
435
+ .ep-name{width:68px;flex-shrink:0}
436
+ .ep-type{width:64px;flex-shrink:0;background:var(--dvx-inp);border:1px solid var(--dvx-bdr);color:var(--dvx-txt);padding:3px 4px;border-radius:3px;font:inherit;font-size:11px}
437
+ .ep-def{flex:1;min-width:0}
438
+ .ep-req-lbl{display:flex;align-items:center;gap:2px;font-size:10px;color:var(--dvx-txt-faint);flex-shrink:0;cursor:pointer}
439
+ .ep-rm{background:none;border:none;color:var(--dvx-txt-ghost);cursor:pointer;font:inherit;font-size:11px;padding:0 2px;flex-shrink:0;line-height:1}
440
+ .ep-rm:hover{color:#ff8888}
441
+ .tr-row{display:flex;align-items:center;gap:3px;padding:2px 4px;border-radius:3px;cursor:pointer;user-select:none;min-width:0}
442
+ .tr-row:hover{background:var(--dvx-row-hover)}
443
+ .tr-row.tr-sel{background:var(--dvx-row-sel)}
444
+ .tr-tog{width:12px;flex-shrink:0;text-align:center;font-size:9px;color:var(--dvx-txt-ghost);cursor:pointer;padding-top:1px}
445
+ .tr-tog:hover{color:var(--dvx-txt-dim)}
446
+ .tr-lbl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-size:11px}
447
+ .tr-tag{color:#7cc5f0}
448
+ .tr-comp{color:#7eca9c}
449
+ .tr-frag{color:var(--dvx-txt-ghost)}
450
+ .tr-txt{color:var(--dvx-txt-ghost);font-style:italic;font-size:10px}
451
+ </style>
452
+ </head>
453
+ <body>
454
+ <header>
455
+ <div class="logo">&#9826; DAVAUX EDITOR</div>
456
+ <div class="page-bar">
457
+ <span>Page:</span>
458
+ <div class="pg-wrap">
459
+ <input id="pg" class="page-input" value="/" list="route-list" autocomplete="off" />
460
+ <button id="pg-clear" class="pg-clear" type="button">&#x2715;</button>
461
+ </div>
462
+ <datalist id="route-list"></datalist>
463
+ <button class="hbtn" onclick="goPage()">Go</button>
464
+ </div>
465
+ <a id="back" href="/" class="hbtn">&#8592; Back to page</a>
466
+ </header>
467
+ <div class="main">
468
+ <div class="palette">
469
+ <div class="pal-tabs">
470
+ <button class="ptab active" data-pal="bps" onclick="switchPal('bps')">BLUEPRINTS</button>
471
+ <button class="ptab" data-pal="conv" onclick="switchPal('conv')">CONVERT</button>
472
+ <button class="ptab" data-pal="tree" onclick="switchPal('tree')">TREE</button>
473
+ </div>
474
+ <div id="pal-bps" class="pal-panel active">
475
+ <div id="bplist"><div class="empty">Loading&#8230;</div></div>
476
+ </div>
477
+ <div id="pal-tree" class="pal-panel">
478
+ <div id="tree-root"><div class="empty">Load a page to see the tree.</div></div>
479
+ </div>
480
+ <div id="pal-conv" class="pal-panel">
481
+ <div class="conv-hd">
482
+ <input id="conv-dir" class="conv-dir" value="src/components" title="Component folder (relative to project root)" />
483
+ <button class="conv-go" onclick="loadComponents()">Scan</button>
484
+ </div>
485
+ <div id="conv-list"><div class="empty">Click Scan to load components.</div></div>
486
+ </div>
487
+ </div>
488
+ <div class="center">
489
+ <div class="ctoolbar">
490
+ <button id="sel-toggle" class="tool-btn" title="Inspect elements (I)" onclick="toggleInspect()">\u2316 Inspect</button>
491
+ <button id="edit-src-btn" class="tool-btn" style="display:none" onclick="openPageSrcModal()">&#9998; Edit Source</button>
492
+ <div id="layout-btns"></div>
493
+ </div>
494
+ <iframe id="frame" src="/"></iframe>
495
+ <div id="src-modal" class="src-modal" style="display:none" onclick="if(event.target===this)closeSrcModal()">
496
+ <div class="src-modal-box">
497
+ <div class="src-modal-hd">
498
+ <span id="src-modal-title" class="src-file-lbl" style="flex:1;font-size:11px;color:#aaa"></span>
499
+ <button class="cbtn" id="src-save-btn" style="padding:3px 12px">Save</button>
500
+ <button class="xbtn" onclick="closeSrcModal()" title="Close (Esc)">\u2715</button>
501
+ </div>
502
+ <div class="src-editor-area">
503
+ <textarea class="src-ed src-modal-ta" id="src-ta" spellcheck="false"></textarea>
504
+ <div id="src-cm-host"></div>
505
+ </div>
506
+ <div class="src-modal-ft">
507
+ <div id="src-st" class="ins-st"></div>
508
+ <button id="src-hl-btn" class="tool-btn" style="font-size:10px;padding:3px 8px" onclick="toggleHighlight()">\u2726 Highlight</button>
509
+ </div>
510
+ </div>
511
+ </div>
512
+ </div>
513
+ <div class="details">
514
+ <div class="det-tabs">
515
+ <button class="dtab active" data-mode="comp" onclick="switchMode('comp')">PROPS</button>
516
+ <button class="dtab" data-mode="styles" onclick="switchMode('styles')">STYLES</button>
517
+ </div>
518
+ <div id="det"><div class="no-sel">Select a component to see details.</div></div>
519
+ </div>
520
+ </div>
521
+ <script>
522
+ ;(function(){
523
+ var NL=String.fromCharCode(10)
524
+ var bps=[]
525
+ var sel=-1
526
+ var overrides={}
527
+ var omlTree=null
528
+ var pageMode='oml'
529
+ var pageFilePath=null
530
+ var pageFileType=null
531
+ var pageSourceContent=''
532
+ var cmEditor=null
533
+ var cmModules=null
534
+ var srcHighlight=localStorage.getItem('dvx-src-hl')==='1'
535
+ var themeObserver=null
536
+ var insertTarget=null
537
+ var pageLayouts=[]
538
+ var pageOwnFilePath=null
539
+ var srcModalMode='file'
540
+ var srcFragmentComp=null
541
+ var srcFragmentTag=null
542
+ var srcFragmentIdx=0
543
+ var prevHovered=null
544
+ var selected=null
545
+ var selectedTreeRow=null
546
+ var cssVarMap={}
547
+ var styleEdits={}
548
+ var detMode='comp'
549
+ var frame=document.getElementById('frame')
550
+ var params=new URLSearchParams(location.search)
551
+ var pg=params.get('page')||'/'
552
+ document.getElementById('pg').value=pg
553
+ setFrame(pg)
554
+ document.getElementById('back').href=pg
555
+ var pgInp=document.getElementById('pg')
556
+ var pgClear=document.getElementById('pg-clear')
557
+ function syncPgClear(){pgClear.style.display=pgInp.value?'block':'none'}
558
+ pgInp.addEventListener('keydown',function(e){if(e.key==='Enter')goPage()})
559
+ pgInp.addEventListener('input',syncPgClear)
560
+ pgClear.addEventListener('click',function(){pgInp.value='';syncPgClear();pgInp.focus()})
561
+ syncPgClear()
562
+
563
+ function setFrame(path){
564
+ var sep=path.indexOf('?')>=0?'&':'?'
565
+ frame.src=path+sep+'_editor=1'
566
+ }
567
+ function goPage(){
568
+ pg=document.getElementById('pg').value.trim()||'/'
569
+ setFrame(pg)
570
+ document.getElementById('back').href=pg
571
+ history.replaceState(null,'','/_davaux/editor?page='+encodeURIComponent(pg))
572
+ }
573
+ window.goPage=goPage
574
+
575
+ // \u2500\u2500 Click-to-select bridge \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
576
+ // \u2500\u2500 Inspect mode \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
577
+ var selectMode=false
578
+ function setSelectMode(v){
579
+ selectMode=!!v
580
+ var btn=document.getElementById('sel-toggle')
581
+ if(btn)btn.classList.toggle('active',selectMode)
582
+ if(!selectMode&&prevHovered){prevHovered.style.outline='';prevHovered=null}
583
+ if(!selectMode&&selected){try{selected.style.outline=''}catch(_){}selected=null}
584
+ try{frame.contentDocument.body.style.cursor=selectMode?'crosshair':''}catch(_){}
585
+ }
586
+ window.setSelectMode=setSelectMode
587
+ window.toggleInspect=function(){setSelectMode(!selectMode)}
588
+ window.openSrcModal=openSrcModal
589
+ window.openLayoutModal=openLayoutModal
590
+ window.openPageSrcModal=openPageSrcModal
591
+ window.closeSrcModal=closeSrcModal
592
+ window.toggleHighlight=toggleHighlight
593
+ window.openFragmentModal=openFragmentModal
594
+ window.openElementFragmentModal=openElementFragmentModal
595
+ document.addEventListener('keydown',function(e){
596
+ if(e.key==='Escape'){var m=document.getElementById('src-modal');if(m&&m.style.display!=='none'){closeSrcModal();return}}
597
+ if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA'||e.target.tagName==='SELECT'||e.target.isContentEditable)return
598
+ if(e.key==='i'||e.key==='I'){e.preventDefault();setSelectMode(!selectMode)}
599
+ })
600
+
601
+ frame.addEventListener('load',function(){
602
+ try{
603
+ var doc=frame.contentDocument
604
+ if(!doc||!doc.body)return
605
+ // Fetch OML tree for this page
606
+ var fp=frame.contentWindow.location
607
+ var rawSearch=fp.search.replace(/[?&]_editor=1/,'')
608
+ var treeUrl=fp.pathname+(rawSearch||'')
609
+ omlTree=null
610
+ ocState=null
611
+ oeState=null
612
+ selected=null
613
+ insertTarget=null
614
+ styleEdits={}
615
+ var det=document.getElementById('det')
616
+ if(det)det.innerHTML='<div class="no-sel">Select a component to see details.</div>'
617
+ fetch('/_davaux/inspector?url='+encodeURIComponent(treeUrl))
618
+ .then(function(r){return r.json()})
619
+ .then(function(d){
620
+ pageFileType=d.fileType||'tsx'
621
+ pageFilePath=d.filePath||null
622
+ pageOwnFilePath=d.filePath||null
623
+ pageLayouts=d.layouts||[]
624
+ omlTree=d.node||null
625
+ pageMode=(pageFileType==='mdx'||pageFileType==='md')?'source':'oml'
626
+ renderLayoutButtons()
627
+ if(pageMode==='source'){
628
+ enterSourceMode()
629
+ }else{
630
+ var esb=document.getElementById('edit-src-btn')
631
+ if(esb)esb.style.display=pageFilePath?'':'none'
632
+ pageSourceContent=''
633
+ if(pageFilePath){
634
+ fetch('/_davaux/get-source?file='+encodeURIComponent(pageFilePath))
635
+ .then(function(r){return r.json()})
636
+ .then(function(d2){pageSourceContent=d2.content||''})
637
+ .catch(function(){})
638
+ }
639
+ closeSrcModal()
640
+ renderTreePanel()
641
+ }
642
+ })
643
+ .catch(function(){renderTreePanel()})
644
+ scanCssVars(doc)
645
+ detectPageTheme(doc)
646
+ if(themeObserver)themeObserver.disconnect()
647
+ themeObserver=new MutationObserver(function(){detectPageTheme(doc)})
648
+ themeObserver.observe(doc.documentElement,{attributes:true,attributeFilter:['data-mantine-color-scheme','data-color-scheme','data-theme']})
649
+ if(detMode==='styles')renderStylesPanel()
650
+ if(selectMode)doc.body.style.cursor='crosshair'
651
+ // Hover \u2014 blue outline (inspect mode only)
652
+ doc.addEventListener('mouseover',function(e){
653
+ if(!selectMode)return
654
+ // Restore selection outline on previously hovered element (if it was selected)
655
+ if(prevHovered){
656
+ prevHovered.style.outline=prevHovered===selected?'2px solid #7cc5f0':''
657
+ prevHovered.style.outlineOffset=prevHovered===selected?'-1px':''
658
+ }
659
+ if(e.target===doc.body||e.target===doc.documentElement){prevHovered=null;return}
660
+ prevHovered=e.target
661
+ e.target.style.outline='2px solid rgba(124,197,240,0.7)'
662
+ e.target.style.outlineOffset='-1px'
663
+ })
664
+ doc.addEventListener('mouseout',function(){
665
+ if(!selectMode)return
666
+ if(prevHovered){
667
+ // Restore selection outline if this element is still selected
668
+ prevHovered.style.outline=prevHovered===selected?'2px solid #7cc5f0':''
669
+ prevHovered.style.outlineOffset=prevHovered===selected?'-1px':''
670
+ prevHovered=null
671
+ }
672
+ })
673
+ // Click \u2014 toggle selection and show details (inspect mode only)
674
+ doc.addEventListener('click',function(e){
675
+ if(!selectMode)return
676
+ e.preventDefault()
677
+ e.stopPropagation()
678
+ var target=e.target
679
+ // Clear previous selection outline
680
+ if(selected){selected.style.outline='';selected.style.outlineOffset=''}
681
+ if(selected===target){
682
+ // Same element clicked again \u2014 deselect
683
+ selected=null
684
+ ocState=null
685
+ oeState=null
686
+ var det=document.getElementById('det')
687
+ if(det)det.innerHTML='<div class="no-sel">Select a component to see details.</div>'
688
+ return
689
+ }
690
+ // Select the new element
691
+ selected=target
692
+ selected.style.outline='2px solid #7cc5f0'
693
+ selected.style.outlineOffset='-1px'
694
+ showElemDet(target)
695
+ },true)
696
+ }catch(_){}
697
+ })
698
+
699
+ function showElemDet(el){
700
+ detMode='comp'
701
+ document.querySelectorAll('.dtab').forEach(function(t){t.classList.toggle('active',t.dataset.mode==='comp')})
702
+ sel=-1
703
+ ocState=null
704
+ oeState=null
705
+ styleEdits={}
706
+ document.querySelectorAll('.bp-card').forEach(function(c){c.classList.remove('sel')})
707
+ var tag=el.tagName?el.tagName.toLowerCase():'?'
708
+ var cls=el.getAttribute?el.getAttribute('class')||'':''
709
+ var txt=(el.textContent||'').trim().slice(0,80)
710
+ var det=document.getElementById('det')
711
+ // Islands use data-island; library components use data-oml-comp injected at render time.
712
+ // Walk up from the clicked element to find the nearest marker.
713
+ var islandName=null
714
+ var omlCompEl=null
715
+ var n=el
716
+ while(n){
717
+ var di=n.getAttribute&&n.getAttribute('data-island');if(di){islandName=di;break}
718
+ var dc=n.getAttribute&&n.getAttribute('data-oml-comp');if(dc){omlCompEl=n;break}
719
+ n=n.parentElement
720
+ }
721
+ var match=islandName&&omlTree?findByName(omlTree,islandName)
722
+ :omlCompEl&&omlTree?findByOmlComp(omlTree,omlCompEl.getAttribute('data-oml-comp'),parseInt(omlCompEl.getAttribute('data-oml-inst')||'0',10))
723
+ :omlTree?findInTree(omlTree,tag,txt,cls):null
724
+ if(match&&match.type==='#component'){
725
+ var compIdx=countNameBefore(omlTree,match,match.name)
726
+ var rawProps=Object.assign({},match.props||{})
727
+ var chi=compChildrenInfo(match)
728
+ ocState={name:match.name,props:rawProps,instanceIndex:compIdx,childrenText:chi.childrenText,hasChildren:chi.hasChildren}
729
+ insertTarget={type:'comp',name:match.name,instanceIndex:compIdx}
730
+ renderOcPanel(det)
731
+ syncTreeRow(match)
732
+ return
733
+ }
734
+ if(match&&match.type==='element'){
735
+ var elemText=null
736
+ if(match.children&&match.children.length>0){
737
+ var allTxt=true
738
+ var accum=''
739
+ for(var ci=0;ci<match.children.length;ci++){
740
+ var ch=match.children[ci]
741
+ if(ch&&ch.type==='#text'){accum+=ch.value||''}
742
+ else{allTxt=false;break}
743
+ }
744
+ if(allTxt&&accum.trim())elemText=accum
745
+ }
746
+ var idx=countTagBefore(omlTree,match,match.tag)
747
+ oeState={tag:match.tag,props:Object.assign({},match.props||{}),instanceIndex:idx,textContent:elemText}
748
+ insertTarget={type:'elem',tag:match.tag,instanceIndex:idx}
749
+ renderOePanel(det)
750
+ syncTreeRow(match)
751
+ return
752
+ }
753
+ syncTreeRow(null)
754
+ var h='<span class="el-chip">&lt;'+esc(tag)+'&gt;</span>'
755
+ if(cls)h+='<div class="prop-row"><span class="pn">class</span><span class="pt"> "'+esc(cls)+'"</span></div>'
756
+ if(txt)h+='<div class="d-lbl">TEXT</div><div style="color:#888;font-size:11px;word-break:break-all;line-height:1.4">'+esc(txt)+'</div>'
757
+ det.innerHTML=h
758
+ }
759
+ function compChildrenInfo(node){
760
+ var kids=node.children||[]
761
+ if(kids.length===0)return{childrenText:null,hasChildren:false}
762
+ if(kids.length===1&&kids[0]&&kids[0].type==='#text')return{childrenText:kids[0].value||null,hasChildren:true}
763
+ return{childrenText:null,hasChildren:true}
764
+ }
765
+ function isOmlVal(v){
766
+ if(!v||typeof v!=='object')return false
767
+ if(Array.isArray(v))return v.some(isOmlVal)
768
+ var t=v.type
769
+ return t==='element'||t==='#component'||t==='#fragment'||t==='#text'||t==='#raw'
770
+ }
771
+ function renderOcPanel(det){
772
+ if(!ocState)return
773
+ var entries=Object.entries(ocState.props).filter(function(e){return typeof e[1]!=='function'&&!isOmlVal(e[1])})
774
+ var readonlyEntries=Object.entries(ocState.props).filter(function(e){return isOmlVal(e[1])})
775
+ // Find matching blueprint to surface optional props not currently in the JSX
776
+ var bp=bps.find(function(b){return b.name===ocState.name})
777
+ var bpSchema=bp?bp.props||{}:{}
778
+ var missing=Object.keys(bpSchema).filter(function(k){return!(k in ocState.props)})
779
+ var h='<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">'
780
+ h+='<span class="comp-chip" style="margin:0">&#9826; '+esc(ocState.name)+'</span>'
781
+ h+='<button class="cbtn mv-up" style="padding:2px 6px;font-size:12px;width:auto" title="Move up">\u2191</button>'
782
+ h+='<button class="cbtn mv-dn" style="padding:2px 6px;font-size:12px;width:auto" title="Move down">\u2193</button>'
783
+ h+='<button class="cbtn rem-btn" style="background:#2a1a1a;border-color:#4a2a2a;color:#ff8888;padding:2px 8px;font-size:10px;width:auto;margin-left:auto" data-comp="'+esc(ocState.name)+'" data-idx="'+ocState.instanceIndex+'">Remove</button>'
784
+ h+='</div>'
785
+ h+='<div id="rem-st" class="ins-st"></div>'
786
+ if(entries.length||readonlyEntries.length){
787
+ h+='<div class="d-lbl">PROPS</div>'
788
+ entries.forEach(function(e){
789
+ var k=e[0],v=e[1]
790
+ h+='<div class="pi-row"><label class="pi-lbl"><span class="pn">'+esc(k)+'</span>'
791
+ h+='<span class="pt"> '+esc(typeof v)+'</span></label>'
792
+ if(typeof v==='boolean'){
793
+ h+='<input type="checkbox" class="pi pi-cb oc-inp" data-prop="'+esc(k)+'" data-type="boolean"'+(v?' checked':'')+'>'
794
+ }else if(typeof v==='number'){
795
+ h+='<input type="number" class="pi oc-inp" data-prop="'+esc(k)+'" data-type="number" value="'+esc(String(v))+'">'
796
+ }else if(typeof v==='object'&&v!==null){
797
+ h+='<input type="text" class="pi oc-inp" data-prop="'+esc(k)+'" data-type="json" value="'+esc(JSON.stringify(v))+'">'
798
+ }else{
799
+ h+='<input type="text" class="pi oc-inp" data-prop="'+esc(k)+'" data-type="string" value="'+esc(String(v!==null&&v!==undefined?v:''))+'">'
800
+ }
801
+ h+='</div>'
802
+ })
803
+ readonlyEntries.forEach(function(e){
804
+ var k=e[0]
805
+ h+='<div class="pi-row"><label class="pi-lbl"><span class="pn">'+esc(k)+'</span><span class="pt"> jsx</span></label>'
806
+ h+='<input type="text" class="pi" value="[JSX]" readonly style="opacity:0.4;cursor:default"></div>'
807
+ })
808
+ }else{
809
+ h+='<div class="no-sel" style="font-size:11px">No props</div>'
810
+ }
811
+ if(ocState.childrenText!==null&&ocState.childrenText!==undefined){
812
+ h+='<div class="d-lbl" style="margin-top:8px">CONTENT</div>'
813
+ h+='<textarea class="pi oc-txt" rows="2" style="width:100%;resize:vertical;font-family:monospace;font-size:11px">'+esc(ocState.childrenText)+'</textarea>'
814
+ }else if(ocState.hasChildren){
815
+ h+='<div class="d-lbl" style="margin-top:8px">CHILDREN</div>'
816
+ h+='<button class="cbtn ibtn" style="font-size:10px;padding:3px 8px;width:auto" onclick="openFragmentModal()">&#9998; Edit Children</button>'
817
+ }
818
+ // Blueprint optional props not yet in JSX
819
+ if(missing.length){
820
+ h+='<div class="d-lbl" style="margin-top:8px">OPTIONAL</div>'
821
+ missing.forEach(function(k){
822
+ var schema=bpSchema[k]
823
+ h+='<div class="pi-row"><span class="pn" style="opacity:0.5;font-size:11px;flex:1">'+esc(k)+'</span>'
824
+ h+='<span class="pt" style="opacity:0.4;font-size:10px;flex-shrink:0;margin-right:4px">'+esc(schema.type||'string')+'</span>'
825
+ h+='<button class="conv-btn oc-add-bp" data-prop="'+esc(k)+'" data-type="'+(schema.type||'string')+'" style="font-size:10px;padding:2px 6px">+ Add</button>'
826
+ h+='</div>'
827
+ })
828
+ }
829
+ // Manual add prop
830
+ h+='<button class="cbtn oc-add-manual" style="margin-top:6px;font-size:10px;background:#1a1a2e;color:#666">+ Add prop</button>'
831
+ h+='<div style="display:flex;gap:4px;margin-top:8px"><button class="cbtn" id="oc-save-btn">Save changes</button></div>'
832
+ h+='<div id="oc-st" class="ins-st"></div>'
833
+ det.innerHTML=h
834
+ det.querySelectorAll('.oc-inp').forEach(function(inp){
835
+ inp.addEventListener('input',function(){
836
+ var p=this.dataset.prop,t=this.dataset.type
837
+ if(t==='boolean')ocState.props[p]=this.checked
838
+ else if(t==='number')ocState.props[p]=Number(this.value)
839
+ else if(t==='json'){try{ocState.props[p]=JSON.parse(this.value)}catch(_){}}
840
+ else ocState.props[p]=this.value
841
+ })
842
+ })
843
+ var ocTxt=det.querySelector('.oc-txt')
844
+ if(ocTxt)ocTxt.addEventListener('input',function(){ocState.childrenText=this.value})
845
+ det.querySelectorAll('.oc-add-bp').forEach(function(btn){
846
+ btn.addEventListener('click',function(){
847
+ var k=this.dataset.prop,t=this.dataset.type||'string'
848
+ ocState.props[k]=defVal(t)
849
+ renderOcPanel(document.getElementById('det'))
850
+ })
851
+ })
852
+ var addManual=det.querySelector('.oc-add-manual')
853
+ if(addManual)addManual.addEventListener('click',function(){
854
+ var row=document.createElement('div')
855
+ row.className='pi-row'
856
+ row.style.gap='4px'
857
+ row.innerHTML='<input type="text" class="pi" placeholder="name" style="flex:1"><input type="text" class="pi" placeholder="value" style="flex:1"><button class="conv-btn" style="padding:2px 6px;font-size:10px;flex-shrink:0">OK</button>'
858
+ var saveArea=det.querySelector('#oc-save-btn')
859
+ if(saveArea&&saveArea.parentElement)saveArea.parentElement.before(row)
860
+ row.querySelector('input').focus()
861
+ row.querySelector('button').addEventListener('click',function(){
862
+ var ins=row.querySelectorAll('input')
863
+ var name=ins[0].value.trim(),val=ins[1].value
864
+ if(!name)return
865
+ ocState.props[name]=val
866
+ renderOcPanel(document.getElementById('det'))
867
+ })
868
+ })
869
+ var rb=det.querySelector('.rem-btn')
870
+ if(rb)rb.addEventListener('click',function(){remComp(this.getAttribute('data-comp'),parseInt(this.getAttribute('data-idx')||'0',10))})
871
+ var sb=det.querySelector('#oc-save-btn')
872
+ if(sb)sb.addEventListener('click',saveOmlComp)
873
+ var mvup=det.querySelector('.mv-up')
874
+ if(mvup)mvup.addEventListener('click',function(){movePanelNode(true,ocState.name,ocState.instanceIndex,'up')})
875
+ var mvdn=det.querySelector('.mv-dn')
876
+ if(mvdn)mvdn.addEventListener('click',function(){movePanelNode(true,ocState.name,ocState.instanceIndex,'down')})
877
+ }
878
+ function buildJsxFromObj(name,props){
879
+ var entries=Object.entries(props).filter(function(e){return typeof e[1]!=='function'})
880
+ if(!entries.length)return'<'+name+' />'
881
+ var lines=entries.map(function(e){
882
+ var k=e[0],v=e[1]
883
+ if(typeof v==='string')return' '+k+'='+JSON.stringify(v)
884
+ if(typeof v==='boolean')return' '+k+'={'+(v?'true':'false')+'}'
885
+ if(typeof v==='number')return' '+k+'={'+v+'}'
886
+ return' '+k+'={'+JSON.stringify(v)+'}'
887
+ })
888
+ return'<'+name+NL+lines.join(NL)+NL+'/>'
889
+ }
890
+ function saveOmlComp(){
891
+ if(!ocState)return
892
+ var st=document.getElementById('oc-st')
893
+ if(st){st.textContent='Saving...';st.className='ins-st'}
894
+ fetch('/_davaux/update-comp-props',{
895
+ method:'POST',headers:{'Content-Type':'application/json'},
896
+ body:JSON.stringify({page:pg.split('?')[0],component:ocState.name,newProps:ocState.props,instanceIndex:ocState.instanceIndex,childrenText:ocState.childrenText})
897
+ })
898
+ .then(function(r){return r.json()})
899
+ .then(function(res){
900
+ var s=document.getElementById('oc-st');if(!s)return
901
+ if(res.replaced){
902
+ s.textContent='Saved to '+res.file;s.className='ins-st ok'
903
+ var b=document.getElementById('oc-save-btn')
904
+ if(b){b.textContent='Saved!';b.classList.add('ok');setTimeout(function(){b.textContent='Save changes';b.classList.remove('ok')},2000)}
905
+ }else{s.textContent=res.error;s.className='ins-st err'}
906
+ })
907
+ .catch(function(){var s=document.getElementById('oc-st');if(s){s.textContent='Request failed';s.className='ins-st err'}})
908
+ }
909
+ window.saveOmlComp=saveOmlComp
910
+
911
+ function countTagBefore(root,target,tag){
912
+ var state={n:0,found:false}
913
+ walkTagCount(root,target,tag,state)
914
+ return state.found?state.n:0
915
+ }
916
+ function walkTagCount(node,target,tag,state){
917
+ if(state.found)return
918
+ if(node===target){state.found=true;return}
919
+ if(node.type==='element'){
920
+ if(node.tag===tag)state.n++
921
+ var kids=node.children||[]
922
+ for(var i=0;i<kids.length;i++)walkTagCount(kids[i],target,tag,state)
923
+ }else if(node.type==='#fragment'){
924
+ var kids=node.children||[]
925
+ for(var i=0;i<kids.length;i++)walkTagCount(kids[i],target,tag,state)
926
+ }else if(node.type==='#component'){
927
+ var sub=(node.output&&node.output.type!=='#raw')?[node.output]:(node.children||[])
928
+ for(var i=0;i<sub.length;i++)walkTagCount(sub[i],target,tag,state)
929
+ }
930
+ }
931
+ function countNameBefore(root,target,name){
932
+ var state={n:0,found:false}
933
+ walkNameCount(root,target,name,state)
934
+ return state.found?state.n:0
935
+ }
936
+ function walkNameCount(node,target,name,state){
937
+ if(!node||state.found)return
938
+ if(node===target){state.found=true;return}
939
+ if(node.type==='#component'){
940
+ if(node.name===name)state.n++
941
+ var sub=(node.output&&node.output.type!=='#raw')?[node.output]:(node.children||[])
942
+ for(var i=0;i<sub.length;i++)walkNameCount(sub[i],target,name,state)
943
+ }else if(node.type==='element'||node.type==='#fragment'){
944
+ var kids=node.children||[]
945
+ for(var i=0;i<kids.length;i++)walkNameCount(kids[i],target,name,state)
946
+ }
947
+ }
948
+ function renderOePanel(det){
949
+ if(!oeState)return
950
+ var entries=Object.entries(oeState.props).filter(function(e){return typeof e[1]!=='function'})
951
+ var h='<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">'
952
+ h+='<span class="el-chip" style="margin:0">&lt;'+esc(oeState.tag)+'&gt;</span>'
953
+ h+='<button class="cbtn mv-up" style="padding:2px 6px;font-size:12px;width:auto" title="Move up">\u2191</button>'
954
+ h+='<button class="cbtn mv-dn" style="padding:2px 6px;font-size:12px;width:auto" title="Move down">\u2193</button>'
955
+ h+='<button class="cbtn oe-rem-btn" style="background:#2a1a1a;border-color:#4a2a2a;color:#ff8888;padding:2px 8px;font-size:10px;width:auto;margin-left:auto">Remove</button>'
956
+ h+='</div>'
957
+ if(entries.length){
958
+ h+='<div class="d-lbl">ATTRIBUTES</div>'
959
+ entries.forEach(function(e){
960
+ var k=e[0],v=e[1]
961
+ h+='<div class="pi-row"><label class="pi-lbl"><span class="pn">'+esc(k)+'</span>'
962
+ if(typeof v==='boolean'){
963
+ h+='<span class="pt"> bool</span></label>'
964
+ h+='<input type="checkbox" class="pi pi-cb oe-inp" data-attr="'+esc(k)+'" data-type="boolean"'+(v?' checked':'')+'>'
965
+ }else if(typeof v==='object'&&v!==null){
966
+ h+='<span class="pt"> object</span></label>'
967
+ h+='<input type="text" class="pi oe-inp" data-attr="'+esc(k)+'" data-type="json" value="'+esc(JSON.stringify(v))+'">'
968
+ }else{
969
+ h+='<span class="pt"> string</span></label>'
970
+ h+='<input type="text" class="pi oe-inp" data-attr="'+esc(k)+'" data-type="string" value="'+esc(String(v!==null&&v!==undefined?v:''))+'">'
971
+ }
972
+ h+='</div>'
973
+ })
974
+ }else{
975
+ h+='<div class="no-sel" style="font-size:11px">No attributes</div>'
976
+ }
977
+ if(oeState.textContent!==null&&oeState.textContent!==undefined){
978
+ h+='<div class="d-lbl" style="margin-top:8px">TEXT</div>'
979
+ h+='<textarea class="pi oe-txt" rows="2" style="width:100%;resize:vertical;font-family:monospace;font-size:11px">'+esc(oeState.textContent)+'</textarea>'
980
+ }else{
981
+ h+='<div class="d-lbl" style="margin-top:8px">CHILDREN</div>'
982
+ h+='<button class="cbtn ibtn" style="font-size:10px;padding:3px 8px;width:auto" onclick="openElementFragmentModal()">&#9998; Edit Children</button>'
983
+ }
984
+ h+='<button class="cbtn oe-add-manual" style="margin-top:6px;font-size:10px;background:#1a1a2e;color:#666">+ Add attr</button>'
985
+ h+='<div style="display:flex;gap:4px;margin-top:8px"><button class="cbtn" id="oe-save-btn">Save changes</button></div>'
986
+ h+='<div id="oe-st" class="ins-st"></div>'
987
+ h+='<div style="margin-top:8px">'
988
+ h+='<button class="cbtn oe-add-sib-btn" style="font-size:10px;background:#1a2a1a;border-color:#2a4a2a;color:#88cc88;width:auto;padding:2px 8px">+ Add sibling</button>'
989
+ h+='<div id="oe-sib-form" style="display:none;margin-top:6px">'
990
+ h+='<div class="pi-row"><label class="pi-lbl"><span class="pn">tag</span></label>'
991
+ h+='<select class="pi" id="sib-tag"><option>div</option><option>p</option><option>span</option><option>h1</option><option>h2</option><option>h3</option><option>section</option><option>article</option><option>button</option><option>a</option><option>ul</option><option>li</option></select>'
992
+ h+='</div>'
993
+ h+='<div class="pi-row"><label class="pi-lbl"><span class="pn">class</span></label><input type="text" class="pi" id="sib-cls" placeholder="optional"></div>'
994
+ h+='<div class="pi-row"><label class="pi-lbl"><span class="pn">text</span></label><input type="text" class="pi" id="sib-txt" placeholder="optional"></div>'
995
+ h+='<button class="cbtn" id="sib-create-btn" style="margin-top:4px">Create</button>'
996
+ h+='</div></div>'
997
+ det.innerHTML=h
998
+ det.querySelectorAll('.oe-inp').forEach(function(inp){
999
+ inp.addEventListener('input',function(){
1000
+ var a=this.dataset.attr,t=this.dataset.type
1001
+ if(t==='boolean')oeState.props[a]=this.checked
1002
+ else if(t==='json'){try{oeState.props[a]=JSON.parse(this.value)}catch(_){}}
1003
+ else oeState.props[a]=this.value
1004
+ })
1005
+ })
1006
+ var txtArea=det.querySelector('.oe-txt')
1007
+ if(txtArea)txtArea.addEventListener('input',function(){oeState.textContent=this.value})
1008
+ var addManual=det.querySelector('.oe-add-manual')
1009
+ if(addManual)addManual.addEventListener('click',function(){
1010
+ var row=document.createElement('div')
1011
+ row.className='pi-row'
1012
+ row.style.gap='4px'
1013
+ row.innerHTML='<input type="text" class="pi" placeholder="attr" style="flex:1"><input type="text" class="pi" placeholder="value" style="flex:1"><button class="conv-btn" style="padding:2px 6px;font-size:10px;flex-shrink:0">OK</button>'
1014
+ var saveArea=det.querySelector('#oe-save-btn')
1015
+ if(saveArea&&saveArea.parentElement)saveArea.parentElement.before(row)
1016
+ row.querySelector('input').focus()
1017
+ row.querySelector('button').addEventListener('click',function(){
1018
+ var ins=row.querySelectorAll('input')
1019
+ var name=ins[0].value.trim(),val=ins[1].value
1020
+ if(!name)return
1021
+ oeState.props[name]=val
1022
+ renderOePanel(document.getElementById('det'))
1023
+ })
1024
+ })
1025
+ var sb=det.querySelector('#oe-save-btn')
1026
+ if(sb)sb.addEventListener('click',saveOmlElem)
1027
+ var mvup=det.querySelector('.mv-up')
1028
+ if(mvup)mvup.addEventListener('click',function(){movePanelNode(false,oeState.tag,oeState.instanceIndex,'up')})
1029
+ var mvdn=det.querySelector('.mv-dn')
1030
+ if(mvdn)mvdn.addEventListener('click',function(){movePanelNode(false,oeState.tag,oeState.instanceIndex,'down')})
1031
+ var remBtn=det.querySelector('.oe-rem-btn')
1032
+ if(remBtn)remBtn.addEventListener('click',removeOmlElem)
1033
+ var addSibBtn=det.querySelector('.oe-add-sib-btn')
1034
+ if(addSibBtn)addSibBtn.addEventListener('click',function(){
1035
+ var form=document.getElementById('oe-sib-form')
1036
+ if(form)form.style.display=form.style.display==='none'?'block':'none'
1037
+ })
1038
+ var createSibBtn=det.querySelector('#sib-create-btn')
1039
+ if(createSibBtn)createSibBtn.addEventListener('click',insertSiblingElem)
1040
+ }
1041
+ function insertSiblingElem(){
1042
+ if(!oeState)return
1043
+ var tagEl=document.getElementById('sib-tag')
1044
+ var clsEl=document.getElementById('sib-cls')
1045
+ var txtEl=document.getElementById('sib-txt')
1046
+ var tag=tagEl?tagEl['value']:'div'
1047
+ var cls=clsEl?clsEl['value'].trim():''
1048
+ var txt=txtEl?txtEl['value'].trim():''
1049
+ var attrs=cls?' class="'+cls+'"':''
1050
+ var jsx=txt?'<'+tag+attrs+'>'+txt+'</'+tag+'>':'<'+tag+attrs+' />'
1051
+ var st=document.getElementById('oe-st')
1052
+ if(st){st.textContent='Creating...';st.className='ins-st'}
1053
+ fetch('/_davaux/insert-after',{
1054
+ method:'POST',headers:{'Content-Type':'application/json'},
1055
+ body:JSON.stringify({page:pg.split('?')[0],tag:oeState.tag,instanceIndex:oeState.instanceIndex,newJsx:jsx})
1056
+ })
1057
+ .then(function(r){return r.json()})
1058
+ .then(function(res){
1059
+ var s=document.getElementById('oe-st');if(!s)return
1060
+ if(res.inserted){
1061
+ s.textContent='Inserted into '+res.file;s.className='ins-st ok'
1062
+ var cb=document.getElementById('sib-create-btn')
1063
+ if(cb){cb['disabled']=true;cb.textContent='Done'}
1064
+ }else{s.textContent=res.error;s.className='ins-st err'}
1065
+ })
1066
+ .catch(function(){var s=document.getElementById('oe-st');if(s){s.textContent='Request failed';s.className='ins-st err'}})
1067
+ }
1068
+ function removeOmlElem(){
1069
+ if(!oeState)return
1070
+ var st=document.getElementById('oe-st')
1071
+ if(st){st.textContent='Removing...';st.className='ins-st'}
1072
+ fetch('/_davaux/remove',{
1073
+ method:'POST',headers:{'Content-Type':'application/json'},
1074
+ body:JSON.stringify({page:pg.split('?')[0],tag:oeState.tag,instanceIndex:oeState.instanceIndex})
1075
+ })
1076
+ .then(function(r){return r.json()})
1077
+ .then(function(res){
1078
+ var s=document.getElementById('oe-st');if(!s)return
1079
+ if(res.removed){
1080
+ s.textContent='Removed from '+res.file;s.className='ins-st ok'
1081
+ var rb=document.querySelector('.oe-rem-btn')
1082
+ if(rb){rb.disabled=true;rb.textContent='Removed'}
1083
+ }else{s.textContent=res.error;s.className='ins-st err'}
1084
+ })
1085
+ .catch(function(){var s=document.getElementById('oe-st');if(s){s.textContent='Request failed';s.className='ins-st err'}})
1086
+ }
1087
+ function saveOmlElem(){
1088
+ if(!oeState)return
1089
+ var st=document.getElementById('oe-st')
1090
+ if(st){st.textContent='Saving...';st.className='ins-st'}
1091
+ fetch('/_davaux/update-element',{
1092
+ method:'POST',headers:{'Content-Type':'application/json'},
1093
+ body:JSON.stringify({page:pg.split('?')[0],tag:oeState.tag,newProps:oeState.props,instanceIndex:oeState.instanceIndex,textContent:oeState.textContent})
1094
+ })
1095
+ .then(function(r){return r.json()})
1096
+ .then(function(res){
1097
+ var s=document.getElementById('oe-st');if(!s)return
1098
+ if(res.replaced){
1099
+ s.textContent='Saved to '+res.file;s.className='ins-st ok'
1100
+ var b=document.getElementById('oe-save-btn')
1101
+ if(b){b.textContent='Saved!';b.classList.add('ok');setTimeout(function(){b.textContent='Save changes';b.classList.remove('ok')},2000)}
1102
+ }else{s.textContent=res.error;s.className='ins-st err'}
1103
+ })
1104
+ .catch(function(){var s=document.getElementById('oe-st');if(s){s.textContent='Request failed';s.className='ins-st err'}})
1105
+ }
1106
+ window.saveOmlElem=saveOmlElem
1107
+
1108
+ function movePanelNode(isComp,name,instanceIndex,dir){
1109
+ var stId=isComp?'oc-st':'oe-st'
1110
+ var st=document.getElementById(stId)
1111
+ if(st){st.textContent='Moving...';st.className='ins-st'}
1112
+ fetch('/_davaux/move',{
1113
+ method:'POST',headers:{'Content-Type':'application/json'},
1114
+ body:JSON.stringify({page:pg.split('?')[0],name:name,isComponent:isComp,instanceIndex:instanceIndex,direction:dir})
1115
+ })
1116
+ .then(function(r){return r.json()})
1117
+ .then(function(res){
1118
+ var s=document.getElementById(stId);if(!s)return
1119
+ if(res.moved){s.textContent='';s.className='ins-st'}
1120
+ else{s.textContent=res.error;s.className='ins-st err'}
1121
+ })
1122
+ .catch(function(){var s=document.getElementById(stId);if(s){s.textContent='Request failed';s.className='ins-st err'}})
1123
+ }
1124
+ window.movePanelNode=movePanelNode
1125
+
1126
+ function findByName(node,name){
1127
+ if(!node)return null
1128
+ if(node.type==='#component'&&node.name===name)return node
1129
+ var kids
1130
+ if(node.type==='element'||node.type==='#fragment'){kids=node.children||[]}
1131
+ else if(node.type==='#component'){kids=(node.output&&node.output.type!=='#raw')?[node.output]:(node.children||[])}
1132
+ else{kids=[]}
1133
+ for(var i=0;i<kids.length;i++){var f=findByName(kids[i],name);if(f)return f}
1134
+ return null
1135
+ }
1136
+ function findByOmlComp(root,name,inst){
1137
+ var state={n:0,found:null}
1138
+ walkFindOmlComp(root,name,inst,state)
1139
+ return state.found
1140
+ }
1141
+ function walkFindOmlComp(node,name,inst,state){
1142
+ if(!node||state.found)return
1143
+ if(node.type==='#component'){
1144
+ if(node.name===name){
1145
+ if(state.n===inst){state.found=node;return}
1146
+ state.n++
1147
+ }
1148
+ var sub=(node.output&&node.output.type!=='#raw')?[node.output]:(node.children||[])
1149
+ for(var i=0;i<sub.length;i++)walkFindOmlComp(sub[i],name,inst,state)
1150
+ }else if(node.type==='element'||node.type==='#fragment'){
1151
+ var kids=node.children||[]
1152
+ for(var i=0;i<kids.length;i++)walkFindOmlComp(kids[i],name,inst,state)
1153
+ }
1154
+ }
1155
+ function nodeMatchesCriteria(node,tag,txt,cls){
1156
+ if(node.tag!==tag)return false
1157
+ var nc=(node.props&&node.props.class)||''
1158
+ // Class must match when provided \u2014 prevents div.grid from matching div.card.
1159
+ if(cls&&nc!==cls)return false
1160
+ // Text is always checked as a tiebreaker \u2014 prevents the first div.card from
1161
+ // matching when clicking a different div.card that shares the same class.
1162
+ if(txt){var nt=extractText(node).trim();if(nt.slice(0,30)!==txt.slice(0,30))return false}
1163
+ return true
1164
+ }
1165
+ function findInTree(node,tag,txt,cls){
1166
+ if(!node)return null
1167
+ // Elements: check children FIRST so we always return the deepest (most specific) match.
1168
+ if(node.type==='element'){
1169
+ var ekids=node.children||[]
1170
+ for(var i=0;i<ekids.length;i++){var ef=findInTree(ekids[i],tag,txt,cls);if(ef)return ef}
1171
+ if(nodeMatchesCriteria(node,tag,txt,cls))return node
1172
+ return null
1173
+ }
1174
+ if(node.type==='#component'){
1175
+ // For #raw-returning components, search in JSX children (the hierarchy the user wrote)
1176
+ if(node.output&&node.output.type==='#raw'){
1177
+ var jkids=node.children||[]
1178
+ for(var i=0;i<jkids.length;i++){var jf=findInTree(jkids[i],tag,txt,cls);if(jf)return jf}
1179
+ return null
1180
+ }
1181
+ if(node.output){
1182
+ var r=node.output
1183
+ if(r.type==='element'&&nodeMatchesCriteria(r,tag,txt,cls))return node
1184
+ var fc=findInTree(r,tag,txt,cls)
1185
+ if(fc)return node
1186
+ }
1187
+ return null
1188
+ }
1189
+ if(node.type==='#fragment'){
1190
+ var fkids=node.children||[]
1191
+ for(var i=0;i<fkids.length;i++){var ff=findInTree(fkids[i],tag,txt,cls);if(ff)return ff}
1192
+ return null
1193
+ }
1194
+ return null
1195
+ }
1196
+ function extractText(node){
1197
+ if(!node)return''
1198
+ if(node.type==='#text')return node.value
1199
+ if(node.type==='element'||node.type==='#fragment')return(node.children||[]).map(extractText).join('')
1200
+ if(node.type==='#component'){
1201
+ if(node.output&&node.output.type!=='#raw')return extractText(node.output)
1202
+ return(node.children||[]).map(extractText).join('')
1203
+ }
1204
+ return''
1205
+ }
1206
+
1207
+ // \u2500\u2500 Tree Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1208
+ function renderLayoutButtons(){
1209
+ var container=document.getElementById('layout-btns')
1210
+ if(!container)return
1211
+ container.innerHTML=''
1212
+ if(!pageLayouts.length)return
1213
+ pageLayouts.forEach(function(l){
1214
+ var btn=document.createElement('button')
1215
+ btn.className='tool-btn'
1216
+ btn.title=l.filePath
1217
+ var parts=l.filePath.replace(/\\\\/g,'/').split('/')
1218
+ var label=pageLayouts.length===1?'Layout':(parts.length>2?parts.slice(-2).join('/'):l.filePath)
1219
+ btn.textContent='\u2670 '+label
1220
+ btn.onclick=function(){openLayoutModal(l.filePath)}
1221
+ container.appendChild(btn)
1222
+ })
1223
+ }
1224
+ function openLayoutModal(filePath){
1225
+ srcModalMode='file'
1226
+ pageFilePath=filePath
1227
+ fetch('/_davaux/get-source?file='+encodeURIComponent(filePath))
1228
+ .then(function(r){return r.json()})
1229
+ .then(function(d){pageSourceContent=d.content||'';openSrcModal('file')})
1230
+ .catch(function(){})
1231
+ }
1232
+ function openPageSrcModal(){
1233
+ if(!pageOwnFilePath)return
1234
+ pageFilePath=pageOwnFilePath
1235
+ srcModalMode='file'
1236
+ fetch('/_davaux/get-source?file='+encodeURIComponent(pageOwnFilePath))
1237
+ .then(function(r){return r.json()})
1238
+ .then(function(d){pageSourceContent=d.content||'';openSrcModal('file')})
1239
+ .catch(function(){})
1240
+ }
1241
+ function enterSourceMode(){
1242
+ var root=document.getElementById('tree-root')
1243
+ if(root)root.innerHTML='<div class="empty">'+esc(pageFileType.toUpperCase())+' page</div>'
1244
+ var det=document.getElementById('det')
1245
+ if(det)det.innerHTML='<div class="no-sel">Click <b>Edit Source</b> in the toolbar to edit this page.</div>'
1246
+ var btn=document.getElementById('edit-src-btn')
1247
+ if(btn)btn.style.display=''
1248
+ if(!pageFilePath)return
1249
+ fetch('/_davaux/get-source?file='+encodeURIComponent(pageFilePath))
1250
+ .then(function(r){return r.json()})
1251
+ .then(function(d){pageSourceContent=d.content||''})
1252
+ .catch(function(){})
1253
+ }
1254
+ function openSrcModal(mode){
1255
+ if(mode)srcModalMode=mode
1256
+ var modal=document.getElementById('src-modal')
1257
+ var title=document.getElementById('src-modal-title')
1258
+ var ta=document.getElementById('src-ta')
1259
+ var saveBtn=document.getElementById('src-save-btn')
1260
+ var st=document.getElementById('src-st')
1261
+ var hlBtn=document.getElementById('src-hl-btn')
1262
+ if(!modal||!ta)return
1263
+ if(title)title.textContent=srcModalMode==='fragment'?('<'+srcFragmentComp+'> children'):srcModalMode==='element-fragment'?('<'+srcFragmentTag+'> children'):(pageFilePath||'')
1264
+ if(st)st.textContent=''
1265
+ if(saveBtn){saveBtn.disabled=false;saveBtn.onclick=saveSource}
1266
+ if(hlBtn)hlBtn.classList.toggle('active',srcHighlight)
1267
+ if(cmEditor&&cmEditor._dvxMode!==srcModalMode){cmEditor.destroy();cmEditor=null}
1268
+ if(cmEditor){
1269
+ try{cmEditor.dispatch({changes:{from:0,to:cmEditor.state.doc.length,insert:pageSourceContent}})}
1270
+ catch(_){cmEditor.destroy();cmEditor=null}
1271
+ }
1272
+ if(!cmEditor){
1273
+ ta.value=pageSourceContent
1274
+ ta.onkeydown=function(e){
1275
+ if(e.key==='Tab'){e.preventDefault();var s=this.selectionStart,en=this.selectionEnd;this.value=this.value.substring(0,s)+' '+this.value.substring(en);this.selectionStart=this.selectionEnd=s+2;pageSourceContent=this.value}
1276
+ if(e.key==='Escape'){closeSrcModal()}
1277
+ }
1278
+ ta.oninput=function(){pageSourceContent=this.value}
1279
+ if(srcHighlight)enableHighlight()
1280
+ }
1281
+ modal.style.display='flex'
1282
+ if(!cmEditor)ta.focus()
1283
+ }
1284
+ function closeSrcModal(){
1285
+ var modal=document.getElementById('src-modal')
1286
+ if(modal)modal.style.display='none'
1287
+ }
1288
+ function saveSource(){
1289
+ var btn=document.getElementById('src-save-btn'),st=document.getElementById('src-st')
1290
+ if(btn)btn.disabled=true
1291
+ if(st)st.textContent='Saving\u2026'
1292
+ var body=srcModalMode==='fragment'
1293
+ ?JSON.stringify({file:pageFilePath,component:srcFragmentComp,instanceIndex:srcFragmentIdx,content:pageSourceContent})
1294
+ :srcModalMode==='element-fragment'
1295
+ ?JSON.stringify({file:pageFilePath,tag:srcFragmentTag,instanceIndex:srcFragmentIdx,content:pageSourceContent})
1296
+ :JSON.stringify({file:pageFilePath,content:pageSourceContent})
1297
+ var url=srcModalMode==='fragment'?'/_davaux/update-fragment':srcModalMode==='element-fragment'?'/_davaux/update-element-fragment':'/_davaux/update-source'
1298
+ fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body:body})
1299
+ .then(function(r){return r.json()})
1300
+ .then(function(){if(st){st.textContent='Saved';setTimeout(function(){st.textContent=''},2000)}if(btn)btn.disabled=false})
1301
+ .catch(function(){if(st)st.textContent='Error saving';if(btn)btn.disabled=false})
1302
+ }
1303
+ async function enableHighlight(){
1304
+ var hlBtn=document.getElementById('src-hl-btn')
1305
+ var ta=document.getElementById('src-ta')
1306
+ var host=document.getElementById('src-cm-host')
1307
+ if(!ta||!host)return
1308
+ if(hlBtn){hlBtn.disabled=true;hlBtn.textContent='\u27F3 Loading\u2026'}
1309
+ if(!cmModules){
1310
+ try{
1311
+ var [view,cmds,lang,langMd,langJs,thm]=await Promise.all([
1312
+ import('https://esm.sh/@codemirror/view@6'),
1313
+ import('https://esm.sh/@codemirror/commands@6'),
1314
+ import('https://esm.sh/@codemirror/language@6'),
1315
+ import('https://esm.sh/@codemirror/lang-markdown@6'),
1316
+ import('https://esm.sh/@codemirror/lang-javascript@6'),
1317
+ import('https://esm.sh/@codemirror/theme-one-dark@6'),
1318
+ ])
1319
+ cmModules={EditorView:view.EditorView,lineNumbers:view.lineNumbers,keymap:view.keymap,drawSelection:view.drawSelection,highlightActiveLine:view.highlightActiveLine,history:cmds.history,defaultKeymap:cmds.defaultKeymap,historyKeymap:cmds.historyKeymap,syntaxHighlighting:lang.syntaxHighlighting,defaultHighlightStyle:lang.defaultHighlightStyle,indentOnInput:lang.indentOnInput,LanguageDescription:lang.LanguageDescription,markdown:langMd.markdown,markdownLanguage:langMd.markdownLanguage,javascript:langJs.javascript,oneDark:thm.oneDark}
1320
+ }catch(e){
1321
+ console.error('CodeMirror load failed',e)
1322
+ srcHighlight=false
1323
+ localStorage.setItem('dvx-src-hl','0')
1324
+ if(hlBtn){hlBtn.disabled=false;hlBtn.textContent='\u2726 Highlight';hlBtn.classList.remove('active')}
1325
+ return
1326
+ }
1327
+ }
1328
+ var isDark=document.documentElement.dataset.dvxTheme!=='light'
1329
+ var isFragment=srcModalMode==='fragment'||srcModalMode==='element-fragment'
1330
+ var isTsx=!isFragment&&pageFilePath&&/\\.tsx?$/.test(pageFilePath)
1331
+ var m=cmModules
1332
+ var LD=m.LanguageDescription
1333
+ var activeLang=(isFragment||isTsx)
1334
+ ?m.javascript({jsx:true,typescript:true})
1335
+ :m.markdown({base:m.markdownLanguage,codeLanguages:[
1336
+ LD.of({name:'JavaScript',alias:['js','javascript'],extensions:['js'],support:m.javascript()}),
1337
+ LD.of({name:'TypeScript',alias:['ts','typescript'],extensions:['ts'],support:m.javascript({typescript:true})}),
1338
+ LD.of({name:'JSX',alias:['jsx'],extensions:['jsx'],support:m.javascript({jsx:true})}),
1339
+ LD.of({name:'TSX',alias:['tsx'],extensions:['tsx'],support:m.javascript({jsx:true,typescript:true})}),
1340
+ ]})
1341
+ cmEditor=new m.EditorView({
1342
+ doc:pageSourceContent,
1343
+ extensions:[
1344
+ m.lineNumbers(),
1345
+ m.history(),
1346
+ m.keymap.of([...m.defaultKeymap,...m.historyKeymap]),
1347
+ m.drawSelection(),
1348
+ m.highlightActiveLine(),
1349
+ m.indentOnInput(),
1350
+ m.syntaxHighlighting(m.defaultHighlightStyle),
1351
+ ...(isDark?[m.oneDark]:[]),
1352
+ activeLang,
1353
+ m.EditorView.updateListener.of(function(upd){if(upd.docChanged)pageSourceContent=upd.state.doc.toString()}),
1354
+ m.EditorView.domEventHandlers({keydown:function(e){if(e.key==='Escape'){closeSrcModal();return true}}}),
1355
+ ],
1356
+ parent:host
1357
+ })
1358
+ cmEditor._dvxDark=isDark
1359
+ cmEditor._dvxMode=srcModalMode
1360
+ ta.style.display='none'
1361
+ host.style.display='flex'
1362
+ host.style.flexDirection='column'
1363
+ if(hlBtn){hlBtn.disabled=false;hlBtn.textContent='\u2726 Highlight';hlBtn.classList.add('active')}
1364
+ }
1365
+ function openFragmentModal(){
1366
+ if(!ocState||!pageOwnFilePath)return
1367
+ pageFilePath=pageOwnFilePath
1368
+ srcFragmentComp=ocState.name
1369
+ srcFragmentIdx=ocState.instanceIndex
1370
+ srcModalMode='fragment'
1371
+ var det=document.getElementById('det')
1372
+ var st=document.getElementById('src-st')
1373
+ if(st)st.textContent=''
1374
+ fetch('/_davaux/get-fragment?file='+encodeURIComponent(pageOwnFilePath)+'&component='+encodeURIComponent(srcFragmentComp)+'&instanceIndex='+srcFragmentIdx)
1375
+ .then(function(r){return r.json()})
1376
+ .then(function(d){
1377
+ if(!d.found){if(det)det.innerHTML='<div class="no-sel" style="color:#ff8888">'+esc(d.error)+'</div>';return}
1378
+ pageSourceContent=d.content||''
1379
+ openSrcModal('fragment')
1380
+ })
1381
+ .catch(function(){if(det)det.innerHTML='<div class="no-sel" style="color:#ff8888">Could not load fragment.</div>'})
1382
+ }
1383
+ function openElementFragmentModal(){
1384
+ if(!oeState||!pageOwnFilePath)return
1385
+ pageFilePath=pageOwnFilePath
1386
+ srcFragmentTag=oeState.tag
1387
+ srcFragmentIdx=oeState.instanceIndex
1388
+ srcModalMode='element-fragment'
1389
+ var det=document.getElementById('det')
1390
+ fetch('/_davaux/get-element-fragment?file='+encodeURIComponent(pageOwnFilePath)+'&tag='+encodeURIComponent(srcFragmentTag)+'&instanceIndex='+srcFragmentIdx)
1391
+ .then(function(r){return r.json()})
1392
+ .then(function(d){
1393
+ if(!d.found){if(det)det.innerHTML='<div class="no-sel" style="color:#ff8888">'+esc(d.error)+'</div>';return}
1394
+ pageSourceContent=d.content||''
1395
+ openSrcModal('element-fragment')
1396
+ })
1397
+ .catch(function(){if(det)det.innerHTML='<div class="no-sel" style="color:#ff8888">Could not load fragment.</div>'})
1398
+ }
1399
+ function disableHighlight(){
1400
+ var ta=document.getElementById('src-ta')
1401
+ var host=document.getElementById('src-cm-host')
1402
+ if(cmEditor){pageSourceContent=cmEditor.state.doc.toString();cmEditor.destroy();cmEditor=null}
1403
+ if(ta){ta.value=pageSourceContent;ta.style.display='';ta.focus()}
1404
+ if(host){host.style.display='none';host.innerHTML=''}
1405
+ var hlBtn=document.getElementById('src-hl-btn')
1406
+ if(hlBtn){hlBtn.classList.remove('active');hlBtn.textContent='\u2726 Highlight'}
1407
+ }
1408
+ function toggleHighlight(){
1409
+ srcHighlight=!srcHighlight
1410
+ localStorage.setItem('dvx-src-hl',srcHighlight?'1':'0')
1411
+ if(srcHighlight)enableHighlight()
1412
+ else disableHighlight()
1413
+ }
1414
+ function renderTreePanel(){
1415
+ var root=document.getElementById('tree-root')
1416
+ if(!root)return
1417
+ if(!omlTree){root.innerHTML='<div class="empty">Load a page to see the tree.</div>';return}
1418
+ root.innerHTML=''
1419
+ var dom=buildTreeNode(omlTree,0,true)
1420
+ if(dom)root.appendChild(dom)
1421
+ else root.innerHTML='<div class="empty">Empty page.</div>'
1422
+ }
1423
+
1424
+ function buildTreeNode(node,depth,isRoot){
1425
+ if(!node)return null
1426
+ if(node.type==='#raw')return null
1427
+ if(node.type==='#text'){
1428
+ var v=String(node.value||'').trim()
1429
+ if(!v)return null
1430
+ var w=document.createElement('div')
1431
+ var row=document.createElement('div')
1432
+ row.className='tr-row'
1433
+ row.style.paddingLeft=(depth*12+4)+'px'
1434
+ row.innerHTML='<span class="tr-tog"> </span><span class="tr-lbl"><span class="tr-txt">"'+esc(v.length>28?v.slice(0,28)+'\u2026':v)+'"</span></span>'
1435
+ w.appendChild(row)
1436
+ return w
1437
+ }
1438
+ var cs
1439
+ if(node.type==='#fragment'){
1440
+ cs=(node.children||[]).filter(Boolean)
1441
+ if(isRoot||depth===0){
1442
+ var w=document.createElement('div')
1443
+ cs.forEach(function(c){var ch=buildTreeNode(c,depth,false);if(ch)w.appendChild(ch)})
1444
+ return w
1445
+ }
1446
+ }else if(node.type==='#component'){
1447
+ // If return is #raw (library component), show JSX children hierarchy instead
1448
+ if(node.output&&node.output.type!=='#raw'){cs=[node.output]}
1449
+ else{cs=(node.children||[]).filter(Boolean)}
1450
+ }else{
1451
+ var allCs=(node.children||[]).filter(Boolean)
1452
+ var hasElemKids=allCs.some(function(c){return c.type!=='#text'&&c.type!=='#raw'})
1453
+ cs=hasElemKids?allCs.filter(function(c){return c.type!=='#text'&&c.type!=='#raw'}):[]
1454
+ }
1455
+ var hk=cs&&cs.length>0
1456
+ var collapsed=false
1457
+ var w=document.createElement('div')
1458
+ var row=document.createElement('div')
1459
+ row.className='tr-row'
1460
+ row.style.paddingLeft=(depth*12+4)+'px'
1461
+ var togSpan=document.createElement('span')
1462
+ togSpan.className='tr-tog'
1463
+ togSpan.textContent=hk?'\u25BC':' '
1464
+ var lblSpan=document.createElement('span')
1465
+ lblSpan.className='tr-lbl'
1466
+ if(node.type==='element'){
1467
+ var cls=(node.props&&node.props.class)||''
1468
+ var fc=cls?'.'+cls.split(' ')[0]:''
1469
+ lblSpan.innerHTML='<span class="tr-tag">&lt;'+esc(node.tag+fc)+'&gt;</span>'
1470
+ row._omlNode=node
1471
+ row.addEventListener('click',function(e){if(e.target===togSpan)return;selectFromTree(node)})
1472
+ }else if(node.type==='#component'){
1473
+ lblSpan.innerHTML='<span class="tr-comp">&#9826; '+esc(node.name)+'</span>'
1474
+ row._omlNode=node
1475
+ row.addEventListener('click',function(e){if(e.target===togSpan)return;selectFromTree(node)})
1476
+ }else{
1477
+ lblSpan.innerHTML='<span class="tr-frag">&lt;&gt;</span>'
1478
+ row.addEventListener('click',function(){if(!hk)return;collapsed=!collapsed;kidsDiv.style.display=collapsed?'none':'';togSpan.textContent=collapsed?'\u25B6':'\u25BC'})
1479
+ }
1480
+ row.appendChild(togSpan)
1481
+ row.appendChild(lblSpan)
1482
+ w.appendChild(row)
1483
+ if(hk){
1484
+ var kidsDiv=document.createElement('div')
1485
+ cs.forEach(function(c){var ch=buildTreeNode(c,depth+1,false);if(ch)kidsDiv.appendChild(ch)})
1486
+ w.appendChild(kidsDiv)
1487
+ togSpan.addEventListener('click',function(e){
1488
+ e.stopPropagation()
1489
+ collapsed=!collapsed
1490
+ kidsDiv.style.display=collapsed?'none':''
1491
+ togSpan.textContent=collapsed?'\u25B6':'\u25BC'
1492
+ })
1493
+ }
1494
+ return w
1495
+ }
1496
+
1497
+ function selectFromTree(node){
1498
+ if(!node)return
1499
+ var det=document.getElementById('det')
1500
+ if(!det)return
1501
+ if(selected){try{selected.style.outline='';selected.style.outlineOffset=''}catch(_){}selected=null}
1502
+ detMode='comp'
1503
+ document.querySelectorAll('.dtab').forEach(function(t){t.classList.toggle('active',t.dataset.mode==='comp')})
1504
+ sel=-1
1505
+ document.querySelectorAll('.bp-card').forEach(function(c){c.classList.remove('sel')})
1506
+ if(node.type==='#component'){
1507
+ var treeCompIdx=countNameBefore(omlTree,node,node.name)
1508
+ var treeRawProps=Object.assign({},node.props||{})
1509
+ var chi=compChildrenInfo(node)
1510
+ ocState={name:node.name,props:treeRawProps,instanceIndex:treeCompIdx,childrenText:chi.childrenText,hasChildren:chi.hasChildren}
1511
+ oeState=null
1512
+ renderOcPanel(det)
1513
+ }else if(node.type==='element'){
1514
+ var idx=countTagBefore(omlTree,node,node.tag)
1515
+ var elemText=null
1516
+ var allCs=(node.children||[]).filter(Boolean)
1517
+ var onlyTxt=allCs.length>0&&allCs.every(function(c){return c.type==='#text'})
1518
+ if(onlyTxt){var acc='';allCs.forEach(function(c){acc+=c.value||''});if(acc.trim())elemText=acc}
1519
+ oeState={tag:node.tag,props:Object.assign({},node.props||{}),instanceIndex:idx,textContent:elemText}
1520
+ ocState=null
1521
+ renderOePanel(det)
1522
+ }
1523
+ try{
1524
+ var domEl=findDomElForNode(node)
1525
+ if(domEl){
1526
+ selected=domEl
1527
+ domEl.style.outline='2px solid #7cc5f0'
1528
+ domEl.style.outlineOffset='-1px'
1529
+ domEl.scrollIntoView({behavior:'smooth',block:'nearest'})
1530
+ }
1531
+ }catch(_){}
1532
+ syncTreeRow(node)
1533
+ }
1534
+
1535
+ function findDomElForNode(node){
1536
+ if(!node)return null
1537
+ var doc=frame.contentDocument
1538
+ if(!doc)return null
1539
+ if(node.type==='#component'){
1540
+ var islandEl=doc.querySelector('[data-island="'+node.name+'"]')
1541
+ if(islandEl)return islandEl
1542
+ var compInst=countNameBefore(omlTree,node,node.name)
1543
+ var compEls=doc.querySelectorAll('[data-oml-comp="'+node.name+'"]')
1544
+ return compEls[compInst]||null
1545
+ }
1546
+ if(node.type==='element'){
1547
+ var tag=node.tag
1548
+ var cls=(node.props&&node.props.class)||''
1549
+ var txt=extractText(node).trim().slice(0,30)
1550
+ var els=Array.from(doc.querySelectorAll(tag))
1551
+ for(var i=0;i<els.length;i++){
1552
+ var el=els[i]
1553
+ var elCls=el.getAttribute?el.getAttribute('class')||'':''
1554
+ var elTxt=(el.textContent||'').trim().slice(0,30)
1555
+ if((!cls||elCls===cls)&&(!txt||elTxt===txt))return el
1556
+ }
1557
+ }
1558
+ return null
1559
+ }
1560
+
1561
+ function syncTreeRow(node){
1562
+ if(selectedTreeRow){selectedTreeRow.classList.remove('tr-sel');selectedTreeRow=null}
1563
+ if(!node)return
1564
+ var root=document.getElementById('tree-root')
1565
+ if(!root)return
1566
+ var rows=root.querySelectorAll('.tr-row')
1567
+ for(var i=0;i<rows.length;i++){
1568
+ if(rows[i]._omlNode===node){
1569
+ selectedTreeRow=rows[i]
1570
+ selectedTreeRow.classList.add('tr-sel')
1571
+ try{selectedTreeRow.scrollIntoView({block:'nearest'})}catch(_){}
1572
+ break
1573
+ }
1574
+ }
1575
+ }
1576
+
1577
+ // \u2500\u2500 Route picker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1578
+ fetch('/_davaux/routes').then(function(r){return r.json()}).then(function(routes){
1579
+ var dl=document.getElementById('route-list')
1580
+ routes.forEach(function(r){
1581
+ var opt=document.createElement('option')
1582
+ opt.value=r.urlPattern
1583
+ dl.appendChild(opt)
1584
+ })
1585
+ }).catch(function(){})
1586
+
1587
+ // \u2500\u2500 Blueprint palette \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1588
+ function refreshBps(){
1589
+ fetch('/_davaux/blueprints').then(function(r){return r.json()}).then(function(data){
1590
+ bps=data
1591
+ var list=document.getElementById('bplist')
1592
+ if(!bps.length){list.innerHTML='<div class="empty">No blueprints found.<br>Add .oml.json files to<br>src/blueprints/</div>';return}
1593
+ list.innerHTML=''
1594
+ bps.forEach(function(bp,i){
1595
+ var card=document.createElement('div')
1596
+ card.className='bp-card'
1597
+ if(bp.previewHtml){var pv=document.createElement('div');pv.className='bp-prev';pv.innerHTML=bp.previewHtml;card.appendChild(pv)}
1598
+ var lbl=document.createElement('div');lbl.className='bp-lbl';lbl.textContent=bp.name;card.appendChild(lbl)
1599
+ card.onclick=function(){pick(i)}
1600
+ list.appendChild(card)
1601
+ })
1602
+ }).catch(function(){document.getElementById('bplist').innerHTML='<div class="empty">Failed to load.</div>'})
1603
+ }
1604
+ refreshBps()
1605
+
1606
+ // \u2500\u2500 Blueprint editor \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1607
+ var editBp=null
1608
+ var ocState=null
1609
+ var oeState=null
1610
+ function startEdit(i){
1611
+ var bp=bps[i]
1612
+ editBp={idx:i,id:bp.id,name:bp.name,rows:Object.entries(bp.props||{}).map(function(e){
1613
+ var s=e[1];return{name:e[0],type:s.type||'string',required:!!s.required,def:s.default!==undefined?String(s.default):''}
1614
+ })}
1615
+ renderEditPanel()
1616
+ }
1617
+ window.startEdit=startEdit
1618
+ function renderEditPanel(){
1619
+ if(!editBp)return
1620
+ var det=document.getElementById('det')
1621
+ det.innerHTML=
1622
+ '<div style="display:flex;align-items:center;gap:6px;margin-bottom:10px">'
1623
+ +'<div id="ep-title" class="d-title" style="margin:0;flex:1">'+esc(editBp.name)+'</div>'
1624
+ +'</div>'
1625
+ +'<div class="d-lbl">NAME</div>'
1626
+ +'<input id="ep-bpname" class="pi" style="margin-bottom:8px" value="'+esc(editBp.name)+'">'
1627
+ +'<div class="d-lbl">PROPS</div>'
1628
+ +'<div id="ep-list"></div>'
1629
+ +'<button class="cbtn" style="margin-bottom:8px" onclick="epAdd()">+ Add prop</button>'
1630
+ +'<div style="display:flex;gap:4px;margin-bottom:4px">'
1631
+ +'<button class="cbtn ebtn" onclick="epSave()">Save</button>'
1632
+ +'<button class="cbtn" style="background:#1a1a2e;flex:1" onclick="epCancel()">Cancel</button>'
1633
+ +'</div>'
1634
+ +'<div id="ep-st" class="ins-st"></div>'
1635
+ renderEpRows()
1636
+ document.getElementById('ep-bpname').addEventListener('input',function(){
1637
+ editBp.name=this.value
1638
+ var t=document.getElementById('ep-title');if(t)t.textContent=this.value
1639
+ })
1640
+ }
1641
+ function renderEpRows(){
1642
+ var list=document.getElementById('ep-list');if(!list)return
1643
+ list.innerHTML=''
1644
+ editBp.rows.forEach(function(row,idx){
1645
+ var d=document.createElement('div');d.className='ep-row'
1646
+ var ni=document.createElement('input');ni.type='text';ni.className='pi ep-name';ni.value=row.name;ni.placeholder='name'
1647
+ ni.addEventListener('input',function(){editBp.rows[idx].name=this.value})
1648
+ var ts=document.createElement('select');ts.className='ep-type'
1649
+ ;['string','number','boolean','function','node','array'].forEach(function(t){
1650
+ var o=document.createElement('option');o.value=t;o.textContent=t;if(t===row.type)o.selected=true;ts.appendChild(o)
1651
+ })
1652
+ ts.addEventListener('change',function(){editBp.rows[idx].type=this.value})
1653
+ var rl=document.createElement('label');rl.className='ep-req-lbl'
1654
+ var rc=document.createElement('input');rc.type='checkbox';rc.className='pi-cb';rc.checked=row.required
1655
+ rc.addEventListener('change',function(){editBp.rows[idx].required=this.checked})
1656
+ rl.appendChild(rc);rl.appendChild(Object.assign(document.createElement('span'),{textContent:'req'}))
1657
+ var di=document.createElement('input');di.type='text';di.className='pi ep-def';di.value=row.def;di.placeholder='default'
1658
+ di.addEventListener('input',function(){editBp.rows[idx].def=this.value})
1659
+ var rm=document.createElement('button');rm.className='ep-rm';rm.innerHTML='\u2715';rm.title='Remove'
1660
+ rm.addEventListener('click',function(){editBp.rows.splice(idx,1);renderEpRows()})
1661
+ d.appendChild(ni);d.appendChild(ts);d.appendChild(rl);d.appendChild(di);d.appendChild(rm)
1662
+ list.appendChild(d)
1663
+ })
1664
+ }
1665
+ window.epAdd=function(){
1666
+ editBp.rows.push({name:'',type:'string',required:false,def:''})
1667
+ renderEpRows()
1668
+ var list=document.getElementById('ep-list')
1669
+ if(list){var ins=list.querySelectorAll('.ep-name');if(ins.length)ins[ins.length-1].focus()}
1670
+ }
1671
+ window.epCancel=function(){
1672
+ editBp=null
1673
+ if(sel>=0)renderBpDet(bps[sel])
1674
+ else document.getElementById('det').innerHTML='<div class="no-sel">Select a component to see details.</div>'
1675
+ }
1676
+ window.epSave=function(){
1677
+ var st=document.getElementById('ep-st')
1678
+ if(st){st.textContent='Saving...';st.className='ins-st'}
1679
+ var props={}
1680
+ editBp.rows.forEach(function(row){
1681
+ var n=row.name.trim();if(!n)return
1682
+ var s={type:row.type,required:row.required}
1683
+ if(row.def!==''){
1684
+ if(row.type==='boolean')s.default=row.def==='true'
1685
+ else if(row.type==='number')s.default=Number(row.def)
1686
+ else s.default=row.def
1687
+ }
1688
+ props[n]=s
1689
+ })
1690
+ fetch('/_davaux/update-blueprint',{
1691
+ method:'POST',headers:{'Content-Type':'application/json'},
1692
+ body:JSON.stringify({id:editBp.id,name:editBp.name,props:props})
1693
+ })
1694
+ .then(function(r){return r.json()})
1695
+ .then(function(res){
1696
+ if(res.saved){
1697
+ editBp=null;sel=-1
1698
+ document.getElementById('det').innerHTML='<div class="no-sel">Blueprint saved.</div>'
1699
+ refreshBps()
1700
+ }else{if(st){st.textContent=res.error||'Save failed';st.className='ins-st err'}}
1701
+ })
1702
+ .catch(function(){if(st){st.textContent='Request failed';st.className='ins-st err'}})
1703
+ }
1704
+
1705
+ // \u2500\u2500 Prop editing \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1706
+ function pick(i){
1707
+ detMode='comp'
1708
+ document.querySelectorAll('.dtab').forEach(function(t){t.classList.toggle('active',t.dataset.mode==='comp')})
1709
+ sel=i
1710
+ overrides={}
1711
+ ocState=null
1712
+ document.querySelectorAll('.bp-card').forEach(function(c,j){c.classList.toggle('sel',j===i)})
1713
+ renderBpDet(bps[i])
1714
+ }
1715
+
1716
+ function renderBpDet(bp){
1717
+ var pe=Object.entries(bp.props||{})
1718
+ var h='<div style="display:flex;align-items:center;gap:6px;margin-bottom:12px"><div class="d-title" style="margin:0;flex:1">'+esc(bp.name)+'</div><button class="cbtn ebtn" style="padding:2px 8px;font-size:10px;width:auto" onclick="startEdit('+sel+')">Edit</button></div>'
1719
+ if(pe.length){
1720
+ h+='<div class="d-lbl">PROPS</div>'
1721
+ pe.forEach(function(e){
1722
+ var name=e[0],schema=e[1]
1723
+ var def=schema.default!==undefined?schema.default:defVal(schema.type)
1724
+ h+='<div class="pi-row"><label class="pi-lbl"><span class="pn">'+esc(name)+'</span>'
1725
+ +'<span class="pt"> '+esc(schema.type)+'</span>'
1726
+ +(schema.required?'<span class="pr"> *</span>':'')+'</label>'
1727
+ if(schema.type==='boolean'){
1728
+ h+='<input type="checkbox" class="pi pi-cb" data-prop="'+esc(name)+'" data-type="boolean"'+(def?' checked':'')+'>'
1729
+ }else if(schema.type==='number'){
1730
+ h+='<input type="number" class="pi" data-prop="'+esc(name)+'" data-type="number" value="'+esc(String(def||0))+'">'
1731
+ }else{
1732
+ h+='<input type="text" class="pi" data-prop="'+esc(name)+'" data-type="string" value="'+esc(String(def||''))+'">'
1733
+ }
1734
+ h+='</div>'
1735
+ })
1736
+ }
1737
+ h+='<div class="d-lbl">JSX</div>'
1738
+ h+='<div class="jsx-block" id="jsx-out">'+esc(buildJsx(bp))+'</div>'
1739
+ h+='<button class="cbtn" id="cpbtn" onclick="cp()">Copy JSX</button>'
1740
+ var insLabel=insertTarget?('Insert after &lt;'+(insertTarget.name||insertTarget.tag)+'&gt;'):'Insert into page'
1741
+ h+='<button class="cbtn ibtn" id="ibtn" onclick="ins()">'+insLabel+'</button>'
1742
+ h+='<div id="ins-st" class="ins-st"></div>'
1743
+ var det=document.getElementById('det')
1744
+ det.innerHTML=h
1745
+ det.querySelectorAll('.pi').forEach(function(inp){
1746
+ inp.addEventListener('input',function(){
1747
+ var prop=this.dataset.prop,type=this.dataset.type
1748
+ overrides[prop]=type==='boolean'?this.checked:type==='number'?Number(this.value):this.value
1749
+ var out=document.getElementById('jsx-out')
1750
+ if(out)out.textContent=buildJsx(bps[sel])
1751
+ })
1752
+ })
1753
+ }
1754
+
1755
+ function buildJsx(bp){
1756
+ var pe=Object.entries(bp.props||{})
1757
+ if(!pe.length)return'<'+bp.name+' />'
1758
+ var lines=pe.map(function(e){
1759
+ var name=e[0],schema=e[1]
1760
+ var val=overrides[name]!==undefined?overrides[name]:(schema.default!==undefined?schema.default:defVal(schema.type))
1761
+ return' '+name+'='+fmtVal(schema.type,val)
1762
+ })
1763
+ return'<'+bp.name+NL+lines.join(NL)+NL+'/>'
1764
+ }
1765
+ function fmtVal(type,val){
1766
+ if(type==='string')return JSON.stringify(String(val))
1767
+ if(type==='boolean')return'{'+(val?'true':'false')+'}'
1768
+ if(type==='number')return'{'+Number(val)+'}'
1769
+ return'{'+JSON.stringify(val)+'}'
1770
+ }
1771
+ function defVal(type){
1772
+ return{string:'example',number:0,boolean:false,function:'() => {}',node:'content',array:[]}[type]??'example'
1773
+ }
1774
+
1775
+ function switchMode(m){
1776
+ detMode=m
1777
+ document.querySelectorAll('.dtab').forEach(function(t){t.classList.toggle('active',t.dataset.mode===m)})
1778
+ if(m==='styles'){renderStylesPanel()}
1779
+ else{
1780
+ if(sel>=0)renderBpDet(bps[sel])
1781
+ else document.getElementById('det').innerHTML='<div class="no-sel">Select a component to see details.</div>'
1782
+ }
1783
+ }
1784
+ function detectPageTheme(doc){
1785
+ try{
1786
+ var forced=window.__DVX_EDITOR__&&window.__DVX_EDITOR__.theme
1787
+ if(forced==='dark'||forced==='light'){document.documentElement.dataset.dvxTheme=forced;return}
1788
+ var html=doc&&doc.documentElement
1789
+ if(!html)return
1790
+ var scheme=html.dataset.mantineColorScheme||html.dataset.colorScheme||html.dataset.theme||''
1791
+ var dark=scheme==='dark'||(!scheme&&window.matchMedia&&window.matchMedia('(prefers-color-scheme: dark)').matches)
1792
+ document.documentElement.dataset.dvxTheme=dark?'dark':'light'
1793
+ // Swap CodeMirror theme if active
1794
+ if(cmEditor&&cmModules){
1795
+ var wasDark=cmEditor._dvxDark
1796
+ var nowDark=dark
1797
+ if(wasDark!==nowDark){
1798
+ disableHighlight()
1799
+ if(srcHighlight)enableHighlight()
1800
+ }
1801
+ }
1802
+ }catch(_){}
1803
+ }
1804
+ function scanCssVars(doc){
1805
+ var vars={}
1806
+ try{
1807
+ for(var i=0;i<doc.styleSheets.length;i++){
1808
+ var sheet=doc.styleSheets[i]
1809
+ try{
1810
+ var rules=sheet.cssRules
1811
+ for(var j=0;j<rules.length;j++){
1812
+ var rule=rules[j]
1813
+ if(rule.selectorText===':root'||rule.selectorText==='html'){
1814
+ var style=rule.style
1815
+ for(var k=0;k<style.length;k++){
1816
+ var prop=style[k]
1817
+ if(prop.charAt(0)==='-'&&prop.charAt(1)==='-'){
1818
+ var cv=style.getPropertyValue(prop).trim()
1819
+ vars[prop]={value:cv,type:detectVarType(cv)}
1820
+ }
1821
+ }
1822
+ }
1823
+ }
1824
+ }catch(_){}
1825
+ }
1826
+ }catch(_){}
1827
+ cssVarMap=vars
1828
+ }
1829
+ function detectVarType(val){
1830
+ var v=val.trim()
1831
+ if(/^#[0-9a-fA-F]{3,8}$/.test(v))return'color'
1832
+ if(v.indexOf('rgb')===0||v.indexOf('hsl')===0)return'color-fn'
1833
+ if(/^-?[0-9]+([.][0-9]+)?(px|rem|em|%|vw|vh|ch|ex|pt|fr)$/.test(v))return'size'
1834
+ if(/^-?[0-9]+([.][0-9]+)?$/.test(v))return'number'
1835
+ return'text'
1836
+ }
1837
+ var STYLE_GROUPS=[
1838
+ {label:'TYPOGRAPHY',props:['color','font-size','font-weight','line-height','text-align']},
1839
+ {label:'SPACING',props:['padding','margin','gap']},
1840
+ {label:'BACKGROUND',props:['background-color']},
1841
+ {label:'BORDER',props:['border-radius','border-width','border-color']},
1842
+ {label:'LAYOUT',props:['display','width','height','max-width']},
1843
+ ]
1844
+ function isVisibleColor(v){var t=v.trim();return(/^#/.test(t)||/^rgb/.test(t)||/^hsl/.test(t))&&t!=='rgba(0, 0, 0, 0)'&&t!=='transparent'}
1845
+ function renderStylePropRow(prop,computed,current){
1846
+ var showPicker=isVisibleColor(computed||'')&&/color|background/.test(prop)
1847
+ var pick=current&&isVisibleColor(current)?current:isVisibleColor(computed||'')?computed:''
1848
+ var h='<div class="cv-row">'
1849
+ h+='<div class="cv-name">'+esc(prop)+'</div>'
1850
+ if(showPicker){
1851
+ h+='<div class="cv-color-row">'
1852
+ h+='<input type="color" class="sty-inp cv-color" data-prop="'+esc(prop)+'" data-role="cp" value="'+esc(pick)+'">'
1853
+ h+='<input type="text" class="sty-inp cv-text" data-prop="'+esc(prop)+'" data-role="tx" placeholder="'+esc((computed||'').slice(0,24))+'" value="'+esc(current)+'">'
1854
+ h+='</div>'
1855
+ }else{
1856
+ h+='<input type="text" class="sty-inp cv-text" data-prop="'+esc(prop)+'" placeholder="'+esc((computed||'').slice(0,28))+'" value="'+esc(current)+'">'
1857
+ }
1858
+ h+='</div>'
1859
+ return h
1860
+ }
1861
+ function renderElementStylesSection(cs){
1862
+ var h='<div class="d-lbl">ELEMENT STYLES</div>'
1863
+ STYLE_GROUPS.forEach(function(g){
1864
+ var rows=g.props.map(function(p){
1865
+ var computed=''
1866
+ try{computed=cs.getPropertyValue(p)||''}catch(_){}
1867
+ var current=styleEdits[p]!==undefined?styleEdits[p]:(selected.style.getPropertyValue(p)||'')
1868
+ return renderStylePropRow(p,computed,current)
1869
+ }).join('')
1870
+ h+='<div class="d-lbl" style="font-size:9px;margin:8px 0 4px">'+g.label+'</div>'+rows
1871
+ })
1872
+ h+='<div style="display:flex;gap:4px;margin-top:8px">'
1873
+ h+='<button class="cbtn" id="style-save-btn" onclick="saveInlineStyles()">Apply Styles</button>'
1874
+ h+='</div>'
1875
+ h+='<div id="style-st" class="ins-st"></div>'
1876
+ h+='<div class="d-lbl" style="margin-top:16px">CSS VARIABLES</div>'
1877
+ return h
1878
+ }
1879
+ function renderStylesPanel(){
1880
+ var det=document.getElementById('det')
1881
+ var h=''
1882
+ if(selected){
1883
+ try{
1884
+ var cs=frame.contentDocument.defaultView.getComputedStyle(selected)
1885
+ h+=renderElementStylesSection(cs)
1886
+ }catch(_){}
1887
+ }
1888
+ var names=Object.keys(cssVarMap)
1889
+ if(!names.length&&!h){det.innerHTML='<div class="no-sel">Select an element to inspect its styles,<br>or add CSS custom properties on :root.</div>';return}
1890
+ if(names.length){
1891
+ if(!selected)h+='<div class="d-lbl">CSS VARIABLES</div>'
1892
+ var colors=names.filter(function(n){return cssVarMap[n].type==='color'})
1893
+ var colorFns=names.filter(function(n){return cssVarMap[n].type==='color-fn'})
1894
+ var sizes=names.filter(function(n){return cssVarMap[n].type==='size'||cssVarMap[n].type==='number'})
1895
+ var texts=names.filter(function(n){return cssVarMap[n].type==='text'})
1896
+ if(colors.length+colorFns.length){h+='<div class="d-lbl" style="font-size:9px;margin:8px 0 4px">COLORS</div>';colors.concat(colorFns).forEach(function(n){h+=renderVarRow(n)})}
1897
+ if(sizes.length){h+='<div class="d-lbl" style="font-size:9px;margin:8px 0 4px">SIZES</div>';sizes.forEach(function(n){h+=renderVarRow(n)})}
1898
+ if(texts.length){h+='<div class="d-lbl" style="font-size:9px;margin:8px 0 4px">OTHER</div>';texts.forEach(function(n){h+=renderVarRow(n)})}
1899
+ h+='<button class="cbtn" id="css-cpbtn" onclick="ccss()" style="margin-top:12px">Copy CSS</button>'
1900
+ }
1901
+ det.innerHTML=h
1902
+ // Wire CSS var inputs
1903
+ det.querySelectorAll('.cv-inp').forEach(function(inp){
1904
+ inp.addEventListener('input',function(){
1905
+ var name=this.dataset.var
1906
+ var val=this.value
1907
+ cssVarMap[name].value=val
1908
+ var companion=det.querySelector('[data-var="'+name+'"][data-role="'+(this.dataset.role==='cp'?'tx':'cp')+'"]')
1909
+ if(companion)companion.value=val
1910
+ try{frame.contentDocument.documentElement.style.setProperty(name,val)}catch(_){}
1911
+ })
1912
+ })
1913
+ // Wire element style inputs \u2014 live preview
1914
+ det.querySelectorAll('.sty-inp').forEach(function(inp){
1915
+ inp.addEventListener('input',function(){
1916
+ var prop=this.dataset.prop
1917
+ var val=this.value.trim()
1918
+ styleEdits[prop]=val
1919
+ if(this.dataset.role){
1920
+ var companion=det.querySelector('[data-prop="'+prop+'"][data-role="'+(this.dataset.role==='cp'?'tx':'cp')+'"]')
1921
+ if(companion)companion.value=val
1922
+ }
1923
+ try{if(val)selected.style.setProperty(prop,val);else selected.style.removeProperty(prop)}catch(_){}
1924
+ })
1925
+ })
1926
+ }
1927
+ function saveInlineStyles(){
1928
+ if(!oeState&&!ocState)return
1929
+ var det=document.getElementById('det')
1930
+ var st=document.getElementById('style-st')
1931
+ if(st){st.textContent='Saving...';st.className='ins-st'}
1932
+ // Build style string from edits
1933
+ var styleObj={}
1934
+ var existing=(oeState&&oeState.props&&oeState.props.style)||(ocState&&ocState.props&&ocState.props.style)||''
1935
+ if(typeof existing==='string'){existing.split(';').forEach(function(p){var i=p.indexOf(':');if(i>-1){var k=p.slice(0,i).trim(),v=p.slice(i+1).trim();if(k)styleObj[k]=v}})}
1936
+ det.querySelectorAll('.sty-inp[data-role="tx"],.sty-inp:not([data-role])').forEach(function(inp){
1937
+ var prop=inp.dataset.prop,val=inp.value.trim()
1938
+ if(val)styleObj[prop]=val;else delete styleObj[prop]
1939
+ })
1940
+ var styleStr=Object.entries(styleObj).map(function(e){return e[0]+': '+e[1]}).join('; ')
1941
+ var url,body
1942
+ if(oeState){
1943
+ var np=Object.assign({},oeState.props);if(styleStr)np.style=styleStr;else delete np.style
1944
+ url='/_davaux/update-element'
1945
+ body=JSON.stringify({page:pg.split('?')[0],tag:oeState.tag,newProps:np,instanceIndex:oeState.instanceIndex,textContent:oeState.textContent})
1946
+ }else{
1947
+ var np=Object.assign({},ocState.props);if(styleStr)np.style=styleStr;else delete np.style
1948
+ url='/_davaux/update-comp-props'
1949
+ body=JSON.stringify({page:pg.split('?')[0],component:ocState.name,newProps:np,instanceIndex:ocState.instanceIndex,childrenText:ocState.childrenText})
1950
+ }
1951
+ fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body:body})
1952
+ .then(function(r){return r.json()})
1953
+ .then(function(res){
1954
+ var ok=res.replaced||res.inserted
1955
+ if(st){st.textContent=ok?'Saved':(res.error||'Error');st.className='ins-st '+(ok?'ok':'err');setTimeout(function(){st.textContent=''},2000)}
1956
+ })
1957
+ .catch(function(){if(st){st.textContent='Error';st.className='ins-st err'}})
1958
+ }
1959
+ window.saveInlineStyles=saveInlineStyles
1960
+ function renderVarRow(name){
1961
+ var v=cssVarMap[name]
1962
+ var h='<div class="cv-row"><div class="cv-name">'+esc(name)+'</div>'
1963
+ if(v.type==='color'){
1964
+ h+='<div class="cv-color-row">'
1965
+ h+='<input type="color" class="cv-inp cv-color" data-var="'+esc(name)+'" data-role="cp" value="'+esc(v.value)+'">'
1966
+ h+='<input type="text" class="cv-inp cv-text" data-var="'+esc(name)+'" data-role="tx" value="'+esc(v.value)+'">'
1967
+ h+='</div>'
1968
+ }else{
1969
+ h+='<input type="text" class="cv-inp cv-text" data-var="'+esc(name)+'" value="'+esc(v.value)+'">'
1970
+ }
1971
+ h+='</div>'
1972
+ return h
1973
+ }
1974
+ window.switchMode=switchMode
1975
+
1976
+ // \u2500\u2500 Palette tabs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1977
+ function switchPal(m){
1978
+ document.querySelectorAll('.ptab').forEach(function(t){t.classList.toggle('active',t.dataset.pal===m)})
1979
+ document.querySelectorAll('.pal-panel').forEach(function(p){p.classList.toggle('active',p.id==='pal-'+m)})
1980
+ }
1981
+ window.switchPal=switchPal
1982
+
1983
+ // \u2500\u2500 Convert panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1984
+ var comps=[]
1985
+ function loadComponents(){
1986
+ var dir=document.getElementById('conv-dir').value.trim()||'src/components'
1987
+ var cl=document.getElementById('conv-list')
1988
+ cl.innerHTML='<div class="empty">Scanning\u2026</div>'
1989
+ fetch('/_davaux/components?dir='+encodeURIComponent(dir))
1990
+ .then(function(r){return r.json()})
1991
+ .then(function(data){comps=data;renderConvList()})
1992
+ .catch(function(){cl.innerHTML='<div class="empty">Failed to scan.</div>'})
1993
+ }
1994
+ window.loadComponents=loadComponents
1995
+ function renderConvList(){
1996
+ var cl=document.getElementById('conv-list')
1997
+ if(!comps.length){cl.innerHTML='<div class="empty">No components found.<br>Create .tsx files starting with<br>an uppercase letter.</div>';return}
1998
+ cl.innerHTML=''
1999
+ comps.forEach(function(c){
2000
+ var div=document.createElement('div')
2001
+ div.className='conv-item'
2002
+ var nc=Object.keys(c.props||{}).length
2003
+ var nd=document.createElement('div');nd.className='conv-name';nd.textContent=c.name
2004
+ var md=document.createElement('div');md.className='conv-meta';md.textContent=c.file+(nc?' \xB7 '+nc+' prop'+(nc!==1?'s':''):'')
2005
+ var acts=document.createElement('div');acts.className='conv-acts'
2006
+ if(c.hasBlueprintAlready){
2007
+ var ex=document.createElement('span');ex.className='conv-exists';ex.textContent='\u2713 exists'
2008
+ var upd=document.createElement('button');upd.className='conv-btn conv-upd';upd.innerHTML='\u21BB Update'
2009
+ upd.onclick=(function(comp,b){return function(){saveBp(comp,b,true)}})(c,upd)
2010
+ acts.appendChild(ex);acts.appendChild(upd)
2011
+ }else{
2012
+ var btn=document.createElement('button');btn.className='conv-btn';btn.innerHTML='\u2192 Blueprint'
2013
+ btn.onclick=(function(comp,b){return function(){saveBp(comp,b,false)}})(c,btn)
2014
+ acts.appendChild(btn)
2015
+ }
2016
+ div.appendChild(nd);div.appendChild(md);div.appendChild(acts)
2017
+ cl.appendChild(div)
2018
+ })
2019
+ }
2020
+ function saveBp(comp,btn,isUpdate){
2021
+ btn.disabled=true;btn.textContent='Saving...'
2022
+ fetch('/_davaux/save-blueprint',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:comp.name,props:comp.props,importPath:comp.importPath})})
2023
+ .then(function(r){return r.json()})
2024
+ .then(function(res){
2025
+ if(res.saved){
2026
+ if(isUpdate){
2027
+ btn.innerHTML='\u2713 Updated'
2028
+ setTimeout(function(){btn.innerHTML='\u21BB Update';btn.disabled=false},1500)
2029
+ }else{
2030
+ comps.forEach(function(c){if(c.name===comp.name)c.hasBlueprintAlready=true})
2031
+ renderConvList()
2032
+ refreshBps()
2033
+ }
2034
+ }else{btn.innerHTML='Error';btn.className='conv-btn conv-upd err';btn.disabled=false}
2035
+ })
2036
+ .catch(function(){btn.innerHTML='Error';btn.className='conv-btn conv-upd err';btn.disabled=false})
2037
+ }
2038
+
2039
+ function buildCss(){
2040
+ var lines=Object.keys(cssVarMap).map(function(n){return' '+n+': '+cssVarMap[n].value+';'})
2041
+ return':root {'+NL+lines.join(NL)+NL+'}'
2042
+ }
2043
+ window.ccss=function(){
2044
+ navigator.clipboard.writeText(buildCss()).then(function(){
2045
+ var b=document.getElementById('css-cpbtn')
2046
+ if(!b)return
2047
+ b.textContent='Copied!'
2048
+ b.classList.add('ok')
2049
+ setTimeout(function(){b.textContent='Copy CSS';b.classList.remove('ok')},1500)
2050
+ })
2051
+ }
2052
+ window.cp=function(){
2053
+ if(sel<0)return
2054
+ navigator.clipboard.writeText(buildJsx(bps[sel])).then(function(){
2055
+ var b=document.getElementById('cpbtn')
2056
+ if(!b)return
2057
+ b.textContent='Copied!'
2058
+ b.classList.add('ok')
2059
+ setTimeout(function(){b.textContent='Copy JSX';b.classList.remove('ok')},1500)
2060
+ })
2061
+ }
2062
+ window.ins=function(){
2063
+ if(sel<0)return
2064
+ var bp=bps[sel]
2065
+ var st=document.getElementById('ins-st')
2066
+ if(st){st.textContent='Inserting...';st.className='ins-st'}
2067
+ var url,body
2068
+ if(insertTarget){
2069
+ url='/_davaux/insert-after'
2070
+ body=JSON.stringify({page:pg.split('?')[0],tag:insertTarget.name||insertTarget.tag,instanceIndex:insertTarget.instanceIndex,newJsx:buildJsx(bp),imports:bp.imports||{}})
2071
+ }else{
2072
+ url='/_davaux/insert'
2073
+ body=JSON.stringify({page:pg.split('?')[0],jsx:buildJsx(bp),imports:bp.imports||{}})
2074
+ }
2075
+ fetch(url,{
2076
+ method:'POST',
2077
+ headers:{'Content-Type':'application/json'},
2078
+ body:body
2079
+ })
2080
+ .then(function(r){return r.json()})
2081
+ .then(function(res){
2082
+ var st2=document.getElementById('ins-st')
2083
+ if(!st2)return
2084
+ if(res.inserted){
2085
+ st2.textContent=res.warning?res.warning:'Inserted into '+res.file
2086
+ st2.className='ins-st '+(res.warning?'warn':'ok')
2087
+ var b=document.getElementById('ibtn')
2088
+ if(b){b.textContent='Inserted!';b.classList.add('ok');setTimeout(function(){b.textContent='Insert into page';b.classList.remove('ok')},2000)}
2089
+ }else{
2090
+ st2.textContent=res.error
2091
+ st2.className='ins-st err'
2092
+ }
2093
+ })
2094
+ .catch(function(e){
2095
+ var st3=document.getElementById('ins-st')
2096
+ if(st3){st3.textContent='Request failed';st3.className='ins-st err'}
2097
+ })
2098
+ }
2099
+ window.remComp=function(name,instanceIndex){
2100
+ if(!name)return
2101
+ var st=document.getElementById('rem-st')
2102
+ if(st){st.textContent='Removing...';st.className='ins-st'}
2103
+ fetch('/_davaux/remove',{
2104
+ method:'POST',headers:{'Content-Type':'application/json'},
2105
+ body:JSON.stringify({page:pg.split('?')[0],component:name,instanceIndex:instanceIndex??0})
2106
+ })
2107
+ .then(function(r){return r.json()})
2108
+ .then(function(res){
2109
+ var s=document.getElementById('rem-st');if(!s)return
2110
+ if(res.removed){
2111
+ s.textContent='Removed from '+res.file
2112
+ s.className='ins-st ok'
2113
+ var rb=document.querySelector('.rem-btn')
2114
+ if(rb){rb.disabled=true;rb.textContent='Removed'}
2115
+ }else{
2116
+ s.textContent=res.error
2117
+ s.className='ins-st err'
2118
+ }
2119
+ })
2120
+ .catch(function(){var s=document.getElementById('rem-st');if(s){s.textContent='Request failed';s.className='ins-st err'}})
2121
+ }
2122
+ function esc(s){return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')}
2123
+ })()
2124
+ </script>
2125
+ </body>
2126
+ </html>`;
2127
+ const reloadPlugin = {
2128
+ name: "davaux-reload",
2129
+ setup(build) {
2130
+ build.onEnd((result) => {
2131
+ if (result.errors.length > 0) {
2132
+ console.error(`
2133
+ [davaux] Build error:
2134
+ ${formatBuildErrors(result.errors)}`);
2135
+ if (serverReady) {
2136
+ const payload = result.errors.map((e) => ({
2137
+ text: e.text,
2138
+ file: e.location?.file,
2139
+ line: e.location?.line,
2140
+ column: e.location?.column,
2141
+ lineText: e.location?.lineText?.trim()
2142
+ }));
2143
+ sendToClients(`error:${JSON.stringify(payload)}`);
2144
+ }
2145
+ return;
2146
+ }
2147
+ scheduleReload();
2148
+ });
2149
+ }
2150
+ };
2151
+ async function startDev(options) {
2152
+ const {
2153
+ cwd,
2154
+ port = 3e3,
2155
+ hostname = "localhost",
2156
+ routesDir = join(cwd, "src", "routes"),
2157
+ publicDir = join(cwd, "public"),
2158
+ islandsDir = join(cwd, "src", "islands"),
2159
+ clientEntry = join(cwd, "src", "client.ts"),
2160
+ paths,
2161
+ plugins: davauxPlugins = [],
2162
+ external: userExternal = [],
2163
+ middlewareSrc,
2164
+ editor: editorConfig
2165
+ } = options;
2166
+ const editorEnabled = editorConfig?.enabled === true;
2167
+ const serverExternal = ["node:*", "davaux", ...userExternal];
2168
+ const userAlias = pathsToAlias(paths ?? {});
2169
+ const extraPlugins = collectEsbuildPlugins(davauxPlugins);
2170
+ const scannerSuffixes = collectScannerSuffixes(davauxPlugins);
2171
+ const dauxDir = join(cwd, ".davaux");
2172
+ const outDir = join(dauxDir, "routes");
2173
+ const clientOutFile = join(dauxDir, "client.js");
2174
+ const islandsOutFile = join(dauxDir, "islands.js");
2175
+ const stylesOutFile = join(dauxDir, "styles.css");
2176
+ const hasClient = existsSync(clientEntry);
2177
+ function toCompiledPath(src) {
2178
+ return join(outDir, relative(routesDir, src).replace(/\.(tsx?|jsx?|mdx?)$/, ".js"));
2179
+ }
2180
+ function toCompiledDir(srcDir) {
2181
+ return join(outDir, relative(routesDir, srcDir));
2182
+ }
2183
+ function collectSourceFiles(scan2) {
2184
+ return [
2185
+ ...scan2.routes.map((r) => r.filePath),
2186
+ ...scan2.layouts.map((l) => l.filePath),
2187
+ ...scan2.middlewares.map((m) => m.filePath),
2188
+ ...scan2.errorPage ? [scan2.errorPage] : []
2189
+ ];
2190
+ }
2191
+ function makeApp(scan2, islandFiles) {
2192
+ const injectedScripts = [];
2193
+ if (islandFiles.length > 0) injectedScripts.push("/_davaux/islands.js");
2194
+ if (hasClient) injectedScripts.push("/_davaux/client.js");
2195
+ const appMiddlewarePath = middlewareSrc ? join(dauxDir, "middleware.js") : void 0;
2196
+ return buildApp(
2197
+ {
2198
+ routes: scan2.routes.map((r) => ({ ...r, filePath: toCompiledPath(r.filePath) })),
2199
+ layouts: scan2.layouts.map((l) => ({
2200
+ filePath: toCompiledPath(l.filePath),
2201
+ dirPath: toCompiledDir(l.dirPath)
2202
+ })),
2203
+ middlewares: scan2.middlewares.map((m) => ({
2204
+ filePath: toCompiledPath(m.filePath),
2205
+ dirPath: toCompiledDir(m.dirPath)
2206
+ })),
2207
+ errorPage: scan2.errorPage ? toCompiledPath(scan2.errorPage) : void 0
2208
+ },
2209
+ true,
2210
+ injectedScripts,
2211
+ ["/_davaux/styles.css"],
2212
+ appMiddlewarePath,
2213
+ "",
2214
+ editorEnabled
2215
+ );
2216
+ }
2217
+ let scan = await scanRoutes(routesDir, scannerSuffixes);
2218
+ let islands = await scanIslands(islandsDir);
2219
+ let sourceFiles = collectSourceFiles(scan);
2220
+ if (sourceFiles.length === 0) {
2221
+ console.warn("[davaux] No routes found in", routesDir);
2222
+ }
2223
+ let ctx = await context({
2224
+ entryPoints: sourceFiles,
2225
+ outdir: outDir,
2226
+ outbase: routesDir,
2227
+ format: "esm",
2228
+ platform: "node",
2229
+ target: "node22",
2230
+ bundle: true,
2231
+ external: serverExternal,
2232
+ jsx: "automatic",
2233
+ jsxImportSource: "davaux/oml",
2234
+ sourcemap: "inline",
2235
+ alias: { ...userAlias, "davaux/client": "davaux/signal" },
2236
+ plugins: [
2237
+ reloadPlugin,
2238
+ islandServerPlugin(islandsDir),
2239
+ cssCollectorPlugin(dauxDir, stylesOutFile),
2240
+ ...extraPlugins
2241
+ ]
2242
+ });
2243
+ await ctx.rebuild();
2244
+ let islandsCtx;
2245
+ if (islands.length > 0) {
2246
+ islandsCtx = await context({
2247
+ stdin: {
2248
+ contents: generateIslandsEntry(islands),
2249
+ loader: "ts",
2250
+ resolveDir: cwd
2251
+ },
2252
+ outfile: islandsOutFile,
2253
+ format: "esm",
2254
+ platform: "browser",
2255
+ target: "es2022",
2256
+ bundle: true,
2257
+ jsx: "automatic",
2258
+ jsxImportSource: "davaux/client",
2259
+ sourcemap: "inline",
2260
+ alias: userAlias,
2261
+ tsconfigRaw: JSON.stringify({
2262
+ compilerOptions: { jsx: "react-jsx", jsxImportSource: "davaux/client" }
2263
+ }),
2264
+ plugins: [reloadPlugin, cssCollectorPlugin(dauxDir, stylesOutFile), ...extraPlugins]
2265
+ });
2266
+ await islandsCtx.rebuild();
2267
+ }
2268
+ let clientCtx;
2269
+ if (hasClient) {
2270
+ clientCtx = await context({
2271
+ entryPoints: [clientEntry],
2272
+ outfile: clientOutFile,
2273
+ format: "esm",
2274
+ platform: "browser",
2275
+ target: "es2022",
2276
+ bundle: true,
2277
+ jsx: "automatic",
2278
+ jsxImportSource: "davaux/client",
2279
+ sourcemap: "inline",
2280
+ alias: userAlias,
2281
+ tsconfigRaw: JSON.stringify({
2282
+ compilerOptions: { jsx: "react-jsx", jsxImportSource: "davaux/client" }
2283
+ }),
2284
+ plugins: [reloadPlugin, ...extraPlugins]
2285
+ });
2286
+ await clientCtx.rebuild();
2287
+ }
2288
+ const middlewareOutFile = join(dauxDir, "middleware.js");
2289
+ let middlewareCtx;
2290
+ if (middlewareSrc) {
2291
+ middlewareCtx = await context({
2292
+ entryPoints: [middlewareSrc],
2293
+ outfile: middlewareOutFile,
2294
+ format: "esm",
2295
+ platform: "node",
2296
+ target: "node22",
2297
+ bundle: true,
2298
+ external: serverExternal,
2299
+ jsx: "automatic",
2300
+ jsxImportSource: "davaux",
2301
+ sourcemap: "inline",
2302
+ alias: userAlias,
2303
+ plugins: [reloadPlugin, ...extraPlugins]
2304
+ });
2305
+ await middlewareCtx.rebuild();
2306
+ }
2307
+ let app = makeApp(scan, islands);
2308
+ const staticHandler = serveStatic(publicDir);
2309
+ startServer(() => app, {
2310
+ port,
2311
+ hostname,
2312
+ onRequest: async (req, res) => {
2313
+ const path = req.url?.split("?")[0];
2314
+ if (path === "/_davaux/livereload") {
2315
+ res.writeHead(200, {
2316
+ "Content-Type": "text/event-stream",
2317
+ "Cache-Control": "no-cache",
2318
+ Connection: "keep-alive"
2319
+ });
2320
+ res.write("\n");
2321
+ sseClients.add(res);
2322
+ req.on("close", () => sseClients.delete(res));
2323
+ return Promise.resolve(true);
2324
+ }
2325
+ if (path === "/_davaux/livereload.js") {
2326
+ res.writeHead(200, { "Content-Type": "application/javascript" });
2327
+ res.end(LIVERELOAD_SCRIPT);
2328
+ return Promise.resolve(true);
2329
+ }
2330
+ if (path === "/_davaux/livereload-worker.js") {
2331
+ res.writeHead(200, { "Content-Type": "application/javascript" });
2332
+ res.end(SHARED_WORKER_SCRIPT);
2333
+ return Promise.resolve(true);
2334
+ }
2335
+ if (path === "/_davaux/partial-updates.js") {
2336
+ res.writeHead(200, { "Content-Type": "application/javascript" });
2337
+ res.end(getPartialUpdatesScript());
2338
+ return Promise.resolve(true);
2339
+ }
2340
+ if (path?.startsWith("/_davaux/") && !editorEnabled) {
2341
+ const alwaysOn = [
2342
+ "/_davaux/livereload",
2343
+ "/_davaux/livereload.js",
2344
+ "/_davaux/livereload-worker.js",
2345
+ "/_davaux/partial-updates.js",
2346
+ "/_davaux/styles.css",
2347
+ "/_davaux/islands.js",
2348
+ "/_davaux/client.js"
2349
+ ];
2350
+ if (!alwaysOn.includes(path)) {
2351
+ res.writeHead(404);
2352
+ res.end();
2353
+ return Promise.resolve(true);
2354
+ }
2355
+ }
2356
+ if (path === "/_davaux/inspector.js") {
2357
+ res.writeHead(200, {
2358
+ "Content-Type": "application/javascript",
2359
+ "Cache-Control": "no-store"
2360
+ });
2361
+ const dvx = JSON.stringify({
2362
+ pos: editorConfig?.badge?.position ?? "bottom-right",
2363
+ label: editorConfig?.badge?.label ?? "Inspector"
2364
+ });
2365
+ res.end(`window.__DVX__=${dvx};
2366
+ ${INSPECTOR_SCRIPT}`);
2367
+ return Promise.resolve(true);
2368
+ }
2369
+ if (path === "/_davaux/inspector") {
2370
+ const url = new URL(req.url ?? "/", "http://x").searchParams.get("url") ?? "/";
2371
+ const node = app.devOmlStore?.get(url);
2372
+ const pageUrl = url.split("?")[0];
2373
+ const route = scan.routes.find((r) => r.urlPattern === pageUrl && r.type === "page");
2374
+ const filePath = route?.filePath ? relative(cwd, route.filePath) : null;
2375
+ const ext = route?.filePath ? extname(route.filePath) : "";
2376
+ const fileType = ext === ".mdx" ? "mdx" : ext === ".md" ? "md" : "tsx";
2377
+ const layouts = route?.filePath ? scan.layouts.filter((l) => {
2378
+ const d = route.filePath.replace(/[\\/][^\\/]+$/, "");
2379
+ return d === l.dirPath || d.startsWith(`${l.dirPath}/`);
2380
+ }).sort((a, b) => b.dirPath.length - a.dirPath.length).map((l) => ({ filePath: relative(cwd, l.filePath) })) : [];
2381
+ res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store" });
2382
+ res.end(
2383
+ node !== void 0 ? JSON.stringify({ node, fileType, filePath, layouts }) : JSON.stringify({ error: "No OML tree for this URL. Load the page first.", fileType, filePath, layouts })
2384
+ );
2385
+ return Promise.resolve(true);
2386
+ }
2387
+ if (path === "/_davaux/editor") {
2388
+ res.writeHead(200, {
2389
+ "Content-Type": "text/html; charset=utf-8",
2390
+ "Cache-Control": "no-store"
2391
+ });
2392
+ const dvxEditor = JSON.stringify({ theme: editorConfig?.theme ?? "auto" });
2393
+ let cssContent = editorConfig?.css ?? "";
2394
+ if (editorConfig?.cssFile) {
2395
+ try {
2396
+ cssContent += readFileSync(join(cwd, editorConfig.cssFile), "utf-8");
2397
+ } catch {
2398
+ }
2399
+ }
2400
+ const extras = `<script>window.__DVX_EDITOR__=${dvxEditor}</script>${cssContent ? `<style>${cssContent}</style>` : ""}`;
2401
+ res.end(EDITOR_HTML.replace("</head>", `${extras}</head>`));
2402
+ return Promise.resolve(true);
2403
+ }
2404
+ if (path === "/_davaux/blueprints") {
2405
+ try {
2406
+ const entries = scanBlueprints(cwd);
2407
+ res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store" });
2408
+ res.end(JSON.stringify(entries));
2409
+ } catch (err) {
2410
+ console.error("[davaux] Failed to scan blueprints:", err);
2411
+ res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store" });
2412
+ res.end("[]");
2413
+ }
2414
+ return Promise.resolve(true);
2415
+ }
2416
+ if (path === "/_davaux/get-source" && req.method === "GET") {
2417
+ const file = new URL(req.url ?? "/", "http://x").searchParams.get("file") ?? "";
2418
+ const absPath = join(cwd, file);
2419
+ if (!file || !absPath.startsWith(`${cwd}/`)) {
2420
+ res.writeHead(403, { "Content-Type": "application/json" });
2421
+ res.end(JSON.stringify({ error: "Forbidden" }));
2422
+ return Promise.resolve(true);
2423
+ }
2424
+ try {
2425
+ const content = readFileSync(absPath, "utf-8");
2426
+ res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store" });
2427
+ res.end(JSON.stringify({ content }));
2428
+ } catch {
2429
+ res.writeHead(404, { "Content-Type": "application/json" });
2430
+ res.end(JSON.stringify({ error: "File not found" }));
2431
+ }
2432
+ return Promise.resolve(true);
2433
+ }
2434
+ if (path === "/_davaux/update-source" && req.method === "POST") {
2435
+ const body = await new Promise((resolve) => {
2436
+ const chunks = [];
2437
+ req.on("data", (c) => chunks.push(c));
2438
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2439
+ req.on("error", () => resolve("{}"));
2440
+ });
2441
+ const { file, content } = JSON.parse(body);
2442
+ const absPath = join(cwd, file);
2443
+ if (!file || !absPath.startsWith(`${cwd}/`)) {
2444
+ res.writeHead(403, { "Content-Type": "application/json" });
2445
+ res.end(JSON.stringify({ error: "Forbidden" }));
2446
+ return Promise.resolve(true);
2447
+ }
2448
+ writeFileSync(absPath, content, "utf-8");
2449
+ res.writeHead(200, { "Content-Type": "application/json" });
2450
+ res.end(JSON.stringify({ ok: true }));
2451
+ return Promise.resolve(true);
2452
+ }
2453
+ if (path === "/_davaux/get-fragment" && req.method === "GET") {
2454
+ const params = new URL(req.url ?? "/", "http://x").searchParams;
2455
+ const file = params.get("file") ?? "";
2456
+ const component = params.get("component") ?? "";
2457
+ const instanceIndex = parseInt(params.get("instanceIndex") ?? "0", 10);
2458
+ const absPath = join(cwd, file);
2459
+ if (!file || !absPath.startsWith(`${cwd}/`)) {
2460
+ res.writeHead(403, { "Content-Type": "application/json" });
2461
+ res.end(JSON.stringify({ error: "Forbidden" }));
2462
+ return Promise.resolve(true);
2463
+ }
2464
+ const result = getComponentFragment(absPath, component, instanceIndex);
2465
+ res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store" });
2466
+ res.end(JSON.stringify(result));
2467
+ return Promise.resolve(true);
2468
+ }
2469
+ if (path === "/_davaux/update-fragment" && req.method === "POST") {
2470
+ const body = await new Promise((resolve) => {
2471
+ const chunks = [];
2472
+ req.on("data", (c) => chunks.push(c));
2473
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2474
+ req.on("error", () => resolve("{}"));
2475
+ });
2476
+ const { file, component, instanceIndex, content } = JSON.parse(body);
2477
+ const absPath = join(cwd, file);
2478
+ if (!file || !absPath.startsWith(`${cwd}/`)) {
2479
+ res.writeHead(403, { "Content-Type": "application/json" });
2480
+ res.end(JSON.stringify({ error: "Forbidden" }));
2481
+ return Promise.resolve(true);
2482
+ }
2483
+ const result = replaceComponentFragment(absPath, component, instanceIndex, content);
2484
+ res.writeHead(200, { "Content-Type": "application/json" });
2485
+ res.end(JSON.stringify(result));
2486
+ return Promise.resolve(true);
2487
+ }
2488
+ if (path === "/_davaux/get-element-fragment" && req.method === "GET") {
2489
+ const params = new URL(req.url ?? "/", "http://x").searchParams;
2490
+ const file = params.get("file") ?? "";
2491
+ const tag = params.get("tag") ?? "";
2492
+ const instanceIndex = parseInt(params.get("instanceIndex") ?? "0", 10);
2493
+ const absPath = join(cwd, file);
2494
+ if (!file || !absPath.startsWith(`${cwd}/`)) {
2495
+ res.writeHead(403, { "Content-Type": "application/json" });
2496
+ res.end(JSON.stringify({ error: "Forbidden" }));
2497
+ return Promise.resolve(true);
2498
+ }
2499
+ const result = getElementFragment(absPath, tag, instanceIndex);
2500
+ res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store" });
2501
+ res.end(JSON.stringify(result));
2502
+ return Promise.resolve(true);
2503
+ }
2504
+ if (path === "/_davaux/update-element-fragment" && req.method === "POST") {
2505
+ const body = await new Promise((resolve) => {
2506
+ const chunks = [];
2507
+ req.on("data", (c) => chunks.push(c));
2508
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2509
+ req.on("error", () => resolve("{}"));
2510
+ });
2511
+ const { file, tag, instanceIndex, content } = JSON.parse(body);
2512
+ const absPath = join(cwd, file);
2513
+ if (!file || !absPath.startsWith(`${cwd}/`)) {
2514
+ res.writeHead(403, { "Content-Type": "application/json" });
2515
+ res.end(JSON.stringify({ error: "Forbidden" }));
2516
+ return Promise.resolve(true);
2517
+ }
2518
+ const result = replaceElementFragment(absPath, tag, instanceIndex, content);
2519
+ res.writeHead(200, { "Content-Type": "application/json" });
2520
+ res.end(JSON.stringify(result));
2521
+ return Promise.resolve(true);
2522
+ }
2523
+ if (path === "/_davaux/routes") {
2524
+ const pageRoutes = scan.routes.filter((r) => r.type === "page").map((r) => ({ urlPattern: r.urlPattern })).sort((a, b) => a.urlPattern.localeCompare(b.urlPattern));
2525
+ res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store" });
2526
+ res.end(JSON.stringify(pageRoutes));
2527
+ return Promise.resolve(true);
2528
+ }
2529
+ if (path === "/_davaux/insert" && req.method === "POST") {
2530
+ const body = await new Promise((resolve) => {
2531
+ const chunks = [];
2532
+ req.on("data", (c) => chunks.push(c));
2533
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2534
+ req.on("error", () => resolve("{}"));
2535
+ });
2536
+ const { page, jsx, imports } = JSON.parse(body);
2537
+ const pageUrl = (page ?? "/").split("?")[0];
2538
+ const route = scan.routes.find((r) => r.urlPattern === pageUrl && r.type === "page");
2539
+ if (!route) {
2540
+ res.writeHead(404, { "Content-Type": "application/json" });
2541
+ res.end(JSON.stringify({ inserted: false, error: `No page route found for: ${pageUrl}` }));
2542
+ return Promise.resolve(true);
2543
+ }
2544
+ const result = insertJsx(route.filePath, jsx, imports ?? {});
2545
+ const display = result.inserted ? { ...result, file: relative(cwd, result.file) } : result;
2546
+ res.writeHead(200, { "Content-Type": "application/json" });
2547
+ res.end(JSON.stringify(display));
2548
+ return Promise.resolve(true);
2549
+ }
2550
+ if (path === "/_davaux/insert-after" && req.method === "POST") {
2551
+ const body = await new Promise((resolve) => {
2552
+ const chunks = [];
2553
+ req.on("data", (c) => chunks.push(c));
2554
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2555
+ req.on("error", () => resolve("{}"));
2556
+ });
2557
+ const { page, tag, instanceIndex, newJsx, imports } = JSON.parse(body);
2558
+ const pageUrl = (page ?? "/").split("?")[0];
2559
+ const route = scan.routes.find((r) => r.urlPattern === pageUrl && r.type === "page");
2560
+ if (!route) {
2561
+ res.writeHead(404, { "Content-Type": "application/json" });
2562
+ res.end(JSON.stringify({ inserted: false, error: `No page route found for: ${pageUrl}` }));
2563
+ return Promise.resolve(true);
2564
+ }
2565
+ const result = insertAfterElement(route.filePath, tag, instanceIndex ?? 0, newJsx, imports ?? {});
2566
+ const display = result.inserted ? { ...result, file: relative(cwd, result.file) } : result;
2567
+ res.writeHead(200, { "Content-Type": "application/json" });
2568
+ res.end(JSON.stringify(display));
2569
+ return Promise.resolve(true);
2570
+ }
2571
+ if (path === "/_davaux/components") {
2572
+ const qs = new URL(req.url ?? "/", "http://x").searchParams;
2573
+ const dir = qs.get("dir") || "src/components";
2574
+ const absDir = dir.startsWith("/") ? dir : join(cwd, dir);
2575
+ const blueprintsDir = join(cwd, "src", "blueprints");
2576
+ try {
2577
+ const entries = scanComponents(absDir, cwd, blueprintsDir);
2578
+ res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store" });
2579
+ res.end(JSON.stringify(entries));
2580
+ } catch (err) {
2581
+ console.error("[davaux] Failed to scan components:", err);
2582
+ res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store" });
2583
+ res.end("[]");
2584
+ }
2585
+ return Promise.resolve(true);
2586
+ }
2587
+ if (path === "/_davaux/save-blueprint" && req.method === "POST") {
2588
+ const body = await new Promise((resolve) => {
2589
+ const chunks = [];
2590
+ req.on("data", (c) => chunks.push(c));
2591
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2592
+ req.on("error", () => resolve("{}"));
2593
+ });
2594
+ const { name, props, importPath } = JSON.parse(body);
2595
+ const blueprintsDir = join(cwd, "src", "blueprints");
2596
+ const imports = importPath ? { [name]: importPath } : {};
2597
+ const result = saveBlueprint(name, props, imports, blueprintsDir);
2598
+ res.writeHead(200, { "Content-Type": "application/json" });
2599
+ res.end(JSON.stringify(result));
2600
+ return Promise.resolve(true);
2601
+ }
2602
+ if (path === "/_davaux/update-blueprint" && req.method === "POST") {
2603
+ const body = await new Promise((resolve) => {
2604
+ const chunks = [];
2605
+ req.on("data", (c) => chunks.push(c));
2606
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2607
+ req.on("error", () => resolve("{}"));
2608
+ });
2609
+ const { id, name, props } = JSON.parse(body);
2610
+ const blueprintsDir = join(cwd, "src", "blueprints");
2611
+ const result = updateBlueprint(id, name, props, blueprintsDir);
2612
+ res.writeHead(200, { "Content-Type": "application/json" });
2613
+ res.end(JSON.stringify(result));
2614
+ return Promise.resolve(true);
2615
+ }
2616
+ if (path === "/_davaux/remove" && req.method === "POST") {
2617
+ const body = await new Promise((resolve) => {
2618
+ const chunks = [];
2619
+ req.on("data", (c) => chunks.push(c));
2620
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2621
+ req.on("error", () => resolve("{}"));
2622
+ });
2623
+ const { page, component, tag, instanceIndex } = JSON.parse(body);
2624
+ const pageUrl = (page ?? "/").split("?")[0];
2625
+ const route = scan.routes.find((r) => r.urlPattern === pageUrl && r.type === "page");
2626
+ if (!route) {
2627
+ res.writeHead(404, { "Content-Type": "application/json" });
2628
+ res.end(JSON.stringify({ removed: false, error: `No page route found for: ${pageUrl}` }));
2629
+ return Promise.resolve(true);
2630
+ }
2631
+ const result = tag !== void 0 ? removeElement(route.filePath, tag, instanceIndex ?? 0) : removeJsx(route.filePath, component ?? "", instanceIndex);
2632
+ const display = result.removed ? { ...result, file: relative(cwd, result.file) } : result;
2633
+ res.writeHead(200, { "Content-Type": "application/json" });
2634
+ res.end(JSON.stringify(display));
2635
+ return Promise.resolve(true);
2636
+ }
2637
+ if (path === "/_davaux/update-component" && req.method === "POST") {
2638
+ const body = await new Promise((resolve) => {
2639
+ const chunks = [];
2640
+ req.on("data", (c) => chunks.push(c));
2641
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2642
+ req.on("error", () => resolve("{}"));
2643
+ });
2644
+ const { page, component, jsx, instanceIndex } = JSON.parse(body);
2645
+ const pageUrl = (page ?? "/").split("?")[0];
2646
+ const route = scan.routes.find((r) => r.urlPattern === pageUrl && r.type === "page");
2647
+ if (!route) {
2648
+ res.writeHead(404, { "Content-Type": "application/json" });
2649
+ res.end(JSON.stringify({ replaced: false, error: `No page route found for: ${pageUrl}` }));
2650
+ return Promise.resolve(true);
2651
+ }
2652
+ const result = replaceJsx(route.filePath, component, jsx, instanceIndex ?? 0);
2653
+ const display = result.replaced ? { ...result, file: relative(cwd, result.file) } : result;
2654
+ res.writeHead(200, { "Content-Type": "application/json" });
2655
+ res.end(JSON.stringify(display));
2656
+ return Promise.resolve(true);
2657
+ }
2658
+ if (path === "/_davaux/update-element" && req.method === "POST") {
2659
+ const body = await new Promise((resolve) => {
2660
+ const chunks = [];
2661
+ req.on("data", (c) => chunks.push(c));
2662
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2663
+ req.on("error", () => resolve("{}"));
2664
+ });
2665
+ const { page, tag, newProps, instanceIndex, textContent } = JSON.parse(body);
2666
+ if (!page || !tag) {
2667
+ res.writeHead(400, { "Content-Type": "application/json" });
2668
+ res.end(JSON.stringify({ replaced: false, error: "Missing page or tag" }));
2669
+ return Promise.resolve(true);
2670
+ }
2671
+ const pageUrl = (page ?? "/").split("?")[0];
2672
+ const route = scan.routes.find((r) => r.urlPattern === pageUrl && r.type === "page");
2673
+ if (!route) {
2674
+ res.writeHead(404, { "Content-Type": "application/json" });
2675
+ res.end(JSON.stringify({ replaced: false, error: `No page route found for: ${pageUrl}` }));
2676
+ return Promise.resolve(true);
2677
+ }
2678
+ const attrsResult = replaceElementAttrs(route.filePath, tag, newProps, instanceIndex ?? 0);
2679
+ if (!attrsResult.replaced) {
2680
+ const display2 = attrsResult;
2681
+ res.writeHead(200, { "Content-Type": "application/json" });
2682
+ res.end(JSON.stringify(display2));
2683
+ return Promise.resolve(true);
2684
+ }
2685
+ if (textContent != null) {
2686
+ const textResult = replaceTextContent(route.filePath, tag, textContent, instanceIndex ?? 0);
2687
+ if (!textResult.replaced) {
2688
+ res.writeHead(200, { "Content-Type": "application/json" });
2689
+ res.end(JSON.stringify(textResult));
2690
+ return Promise.resolve(true);
2691
+ }
2692
+ }
2693
+ const display = { ...attrsResult, file: relative(cwd, attrsResult.file) };
2694
+ res.writeHead(200, { "Content-Type": "application/json" });
2695
+ res.end(JSON.stringify(display));
2696
+ return Promise.resolve(true);
2697
+ }
2698
+ if (path === "/_davaux/update-comp-props" && req.method === "POST") {
2699
+ const body = await new Promise((resolve) => {
2700
+ const chunks = [];
2701
+ req.on("data", (c) => chunks.push(c));
2702
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2703
+ req.on("error", () => resolve("{}"));
2704
+ });
2705
+ const { page, component, newProps, instanceIndex, childrenText } = JSON.parse(body);
2706
+ const pageUrl = (page ?? "/").split("?")[0];
2707
+ const route = scan.routes.find((r) => r.urlPattern === pageUrl && r.type === "page");
2708
+ if (!route) {
2709
+ res.writeHead(404, { "Content-Type": "application/json" });
2710
+ res.end(JSON.stringify({ replaced: false, error: `No page route found for: ${pageUrl}` }));
2711
+ return Promise.resolve(true);
2712
+ }
2713
+ const attrsResult = replaceElementAttrs(route.filePath, component, newProps ?? {}, instanceIndex ?? 0);
2714
+ if (!attrsResult.replaced) {
2715
+ res.writeHead(200, { "Content-Type": "application/json" });
2716
+ res.end(JSON.stringify(attrsResult));
2717
+ return Promise.resolve(true);
2718
+ }
2719
+ if (childrenText != null) {
2720
+ replaceTextContent(route.filePath, component, childrenText, instanceIndex ?? 0);
2721
+ }
2722
+ const display = { ...attrsResult, file: relative(cwd, attrsResult.file) };
2723
+ res.writeHead(200, { "Content-Type": "application/json" });
2724
+ res.end(JSON.stringify(display));
2725
+ return Promise.resolve(true);
2726
+ }
2727
+ if (path === "/_davaux/move" && req.method === "POST") {
2728
+ const body = await new Promise((resolve) => {
2729
+ const chunks = [];
2730
+ req.on("data", (c) => chunks.push(c));
2731
+ req.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
2732
+ req.on("error", () => resolve("{}"));
2733
+ });
2734
+ const { page, name, isComponent, instanceIndex, direction } = JSON.parse(body);
2735
+ if (!page || !name || !direction) {
2736
+ res.writeHead(400, { "Content-Type": "application/json" });
2737
+ res.end(JSON.stringify({ moved: false, error: "Missing required fields" }));
2738
+ return Promise.resolve(true);
2739
+ }
2740
+ const pageUrl = (page ?? "/").split("?")[0];
2741
+ const route = scan.routes.find((r) => r.urlPattern === pageUrl && r.type === "page");
2742
+ if (!route) {
2743
+ res.writeHead(404, { "Content-Type": "application/json" });
2744
+ res.end(JSON.stringify({ moved: false, error: `No page route found for: ${pageUrl}` }));
2745
+ return Promise.resolve(true);
2746
+ }
2747
+ const result = moveNode(route.filePath, name, isComponent ?? false, instanceIndex ?? 0, direction);
2748
+ const display = result.moved ? { ...result, file: relative(cwd, result.file) } : result;
2749
+ res.writeHead(200, { "Content-Type": "application/json" });
2750
+ res.end(JSON.stringify(display));
2751
+ return Promise.resolve(true);
2752
+ }
2753
+ if (path === "/_davaux/styles.css") {
2754
+ res.writeHead(200, { "Content-Type": "text/css" });
2755
+ if (existsSync(stylesOutFile)) {
2756
+ createReadStream(stylesOutFile).pipe(res);
2757
+ } else {
2758
+ res.end();
2759
+ }
2760
+ return Promise.resolve(true);
2761
+ }
2762
+ if (path === "/_davaux/islands.js") {
2763
+ res.writeHead(200, { "Content-Type": "application/javascript" });
2764
+ createReadStream(islandsOutFile).pipe(res);
2765
+ return Promise.resolve(true);
2766
+ }
2767
+ if (hasClient && path === "/_davaux/client.js") {
2768
+ res.writeHead(200, { "Content-Type": "application/javascript" });
2769
+ createReadStream(clientOutFile).pipe(res);
2770
+ return Promise.resolve(true);
2771
+ }
2772
+ if (staticHandler) return Promise.resolve(staticHandler(req, res));
2773
+ return Promise.resolve(false);
2774
+ }
2775
+ });
2776
+ serverReady = true;
2777
+ const CONFIG_NAMES = ["davaux.config.ts", "davaux.config.js", "davaux.config.mjs"];
2778
+ const activeConfigFile = CONFIG_NAMES.map((f) => join(cwd, f)).find(existsSync);
2779
+ if (activeConfigFile) {
2780
+ let configReloadTimer;
2781
+ fsWatch(activeConfigFile, () => {
2782
+ if (configReloadTimer) clearTimeout(configReloadTimer);
2783
+ configReloadTimer = setTimeout(async () => {
2784
+ configReloadTimer = void 0;
2785
+ try {
2786
+ app = makeApp(scan, islands);
2787
+ console.log("[davaux] Config reloaded");
2788
+ scheduleReload();
2789
+ } catch (err) {
2790
+ console.error("[davaux] Failed to reload config:", err);
2791
+ }
2792
+ }, 300);
2793
+ });
2794
+ }
2795
+ await ctx.watch();
2796
+ if (islandsCtx) await islandsCtx.watch();
2797
+ if (clientCtx) await clientCtx.watch();
2798
+ if (middlewareCtx) await middlewareCtx.watch();
2799
+ console.log(" Watching for changes...");
2800
+ let isRebuilding = false;
2801
+ let rebuildTimer;
2802
+ async function handleStructuralChange() {
2803
+ const newScan = await scanRoutes(routesDir, scannerSuffixes);
2804
+ const newIslands = await scanIslands(islandsDir);
2805
+ const newSourceFiles = collectSourceFiles(newScan);
2806
+ const same = newSourceFiles.length === sourceFiles.length && newSourceFiles.every((f) => sourceFiles.includes(f)) && newIslands.length === islands.length && newIslands.every((i) => islands.some((j) => j.filePath === i.filePath));
2807
+ if (same) return;
2808
+ sourceFiles = newSourceFiles;
2809
+ scan = newScan;
2810
+ islands = newIslands;
2811
+ ctx = await context({
2812
+ entryPoints: sourceFiles,
2813
+ outdir: outDir,
2814
+ outbase: routesDir,
2815
+ format: "esm",
2816
+ platform: "node",
2817
+ target: "node22",
2818
+ bundle: true,
2819
+ external: serverExternal,
2820
+ jsx: "automatic",
2821
+ jsxImportSource: "davaux/oml",
2822
+ sourcemap: "inline",
2823
+ alias: { ...userAlias, "davaux/client": "davaux/signal" },
2824
+ plugins: [
2825
+ reloadPlugin,
2826
+ islandServerPlugin(islandsDir),
2827
+ cssCollectorPlugin(dauxDir, stylesOutFile),
2828
+ ...extraPlugins
2829
+ ]
2830
+ });
2831
+ await ctx.rebuild();
2832
+ if (islands.length > 0) {
2833
+ islandsCtx = await context({
2834
+ stdin: {
2835
+ contents: generateIslandsEntry(islands),
2836
+ loader: "ts",
2837
+ resolveDir: cwd
2838
+ },
2839
+ outfile: islandsOutFile,
2840
+ format: "esm",
2841
+ platform: "browser",
2842
+ target: "es2022",
2843
+ bundle: true,
2844
+ jsx: "automatic",
2845
+ jsxImportSource: "davaux/client",
2846
+ sourcemap: "inline",
2847
+ alias: userAlias,
2848
+ tsconfigRaw: JSON.stringify({
2849
+ compilerOptions: { jsx: "react-jsx", jsxImportSource: "davaux/client" }
2850
+ }),
2851
+ plugins: [reloadPlugin, cssCollectorPlugin(dauxDir, stylesOutFile), ...extraPlugins]
2852
+ });
2853
+ await islandsCtx.rebuild();
2854
+ } else {
2855
+ islandsCtx = void 0;
2856
+ }
2857
+ app = makeApp(scan, islands);
2858
+ await ctx.watch();
2859
+ if (islandsCtx) await islandsCtx.watch();
2860
+ scheduleReload();
2861
+ console.log("[davaux] Routes updated");
2862
+ }
2863
+ function onStructuralChange() {
2864
+ ctx.dispose().catch(() => {
2865
+ });
2866
+ islandsCtx?.dispose().catch(() => {
2867
+ });
2868
+ if (rebuildTimer) clearTimeout(rebuildTimer);
2869
+ rebuildTimer = setTimeout(() => {
2870
+ if (isRebuilding) return;
2871
+ isRebuilding = true;
2872
+ handleStructuralChange().catch((err) => console.error("[davaux] Route rebuild failed:", err)).finally(() => {
2873
+ isRebuilding = false;
2874
+ });
2875
+ }, 200);
2876
+ }
2877
+ fsWatch(routesDir, { recursive: true }, (event, filename) => {
2878
+ if (event !== "rename" || !filename) return;
2879
+ if (!/\.(tsx?|jsx?|mdx?)$/.test(filename)) return;
2880
+ onStructuralChange();
2881
+ });
2882
+ if (existsSync(islandsDir)) {
2883
+ fsWatch(islandsDir, { recursive: true }, (event, filename) => {
2884
+ if (event !== "rename" || !filename) return;
2885
+ if (!/\.(tsx?|jsx?|mdx?)$/.test(filename)) return;
2886
+ onStructuralChange();
2887
+ });
2888
+ }
2889
+ startTypeChecker(cwd);
2890
+ }
2891
+ function startTypeChecker(cwd) {
2892
+ const tscBin = join(cwd, "node_modules", ".bin", "tsc");
2893
+ if (!existsSync(tscBin) || !existsSync(join(cwd, "tsconfig.json"))) return;
2894
+ const proc = spawn(tscBin, ["--noEmit", "--watch", "--preserveWatchOutput"], {
2895
+ cwd,
2896
+ stdio: ["ignore", "inherit", "inherit"]
2897
+ });
2898
+ proc.on("error", () => {
2899
+ });
2900
+ process.on("exit", () => proc.kill());
2901
+ }
2902
+ export {
2903
+ startDev
2904
+ };
2905
+ //# sourceMappingURL=watch.js.map