@ogygia/devtools 0.0.0 → 0.11.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 (310) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +24 -1
  3. package/dist/client.d.ts +3 -0
  4. package/dist/client.js +24 -0
  5. package/dist/devtools/BoundaryOverlay.svelte +396 -0
  6. package/dist/devtools/BoundaryOverlay.svelte.d.ts +19 -0
  7. package/dist/devtools/Devtools.svelte +663 -0
  8. package/dist/devtools/Devtools.svelte.d.ts +15 -0
  9. package/dist/devtools/HubTab.svelte +206 -0
  10. package/dist/devtools/HubTab.svelte.d.ts +11 -0
  11. package/dist/devtools/HydrationTab.svelte +237 -0
  12. package/dist/devtools/HydrationTab.svelte.d.ts +13 -0
  13. package/dist/devtools/IslandDetail.svelte +615 -0
  14. package/dist/devtools/IslandDetail.svelte.d.ts +15 -0
  15. package/dist/devtools/LedgerTab.svelte +287 -0
  16. package/dist/devtools/LedgerTab.svelte.d.ts +11 -0
  17. package/dist/devtools/LensTab.svelte +290 -0
  18. package/dist/devtools/LensTab.svelte.d.ts +19 -0
  19. package/dist/devtools/NavTab.svelte +229 -0
  20. package/dist/devtools/NavTab.svelte.d.ts +11 -0
  21. package/dist/devtools/PageTab.svelte +1122 -0
  22. package/dist/devtools/PageTab.svelte.d.ts +15 -0
  23. package/dist/devtools/ProfilerTab.svelte +755 -0
  24. package/dist/devtools/ProfilerTab.svelte.d.ts +15 -0
  25. package/dist/devtools/PropsTree.svelte +155 -0
  26. package/dist/devtools/PropsTree.svelte.d.ts +15 -0
  27. package/dist/devtools/SessionTab.svelte +796 -0
  28. package/dist/devtools/SessionTab.svelte.d.ts +11 -0
  29. package/dist/devtools/TimelineTab.svelte +267 -0
  30. package/dist/devtools/TimelineTab.svelte.d.ts +11 -0
  31. package/dist/devtools/WireTab.svelte +149 -0
  32. package/dist/devtools/WireTab.svelte.d.ts +11 -0
  33. package/dist/devtools/bytes.d.ts +70 -0
  34. package/dist/devtools/bytes.js +82 -0
  35. package/dist/devtools/cache-headers.d.ts +20 -0
  36. package/dist/devtools/cache-headers.js +81 -0
  37. package/dist/devtools/highlight.d.ts +7 -0
  38. package/dist/devtools/highlight.js +83 -0
  39. package/dist/devtools/hydration.d.ts +48 -0
  40. package/dist/devtools/hydration.js +93 -0
  41. package/dist/devtools/index.d.ts +3 -0
  42. package/dist/devtools/index.js +3 -0
  43. package/dist/devtools/ledger-dom.d.ts +7 -0
  44. package/dist/devtools/ledger-dom.js +151 -0
  45. package/dist/devtools/leftovers.d.ts +77 -0
  46. package/dist/devtools/leftovers.js +319 -0
  47. package/dist/devtools/names.d.ts +1 -0
  48. package/dist/devtools/names.js +34 -0
  49. package/dist/devtools/page.d.ts +96 -0
  50. package/dist/devtools/page.js +957 -0
  51. package/dist/devtools/profile-anchors.d.ts +22 -0
  52. package/dist/devtools/profile-anchors.js +135 -0
  53. package/dist/devtools/profile-store.d.ts +175 -0
  54. package/dist/devtools/profile-store.js +239 -0
  55. package/dist/devtools/regions.d.ts +77 -0
  56. package/dist/devtools/regions.js +132 -0
  57. package/dist/devtools/session.d.ts +31 -0
  58. package/dist/devtools/session.js +455 -0
  59. package/dist/devtools/since-load.d.ts +50 -0
  60. package/dist/devtools/since-load.js +89 -0
  61. package/dist/devtools/source-lines.d.ts +24 -0
  62. package/dist/devtools/source-lines.js +106 -0
  63. package/dist/devtools/styles.d.ts +52 -0
  64. package/dist/devtools/styles.js +248 -0
  65. package/dist/devtools/testing-engine.d.ts +83 -0
  66. package/dist/devtools/testing-engine.js +220 -0
  67. package/dist/devtools/ui-app.d.ts +5 -0
  68. package/dist/devtools/ui-app.js +69 -0
  69. package/dist/devtools/ui.d.ts +50 -0
  70. package/dist/devtools/ui.js +175 -0
  71. package/dist/profiler/alloc.d.ts +75 -0
  72. package/dist/profiler/alloc.js +76 -0
  73. package/dist/profiler/analyze.d.ts +437 -0
  74. package/dist/profiler/analyze.js +1941 -0
  75. package/dist/profiler/app-path.d.ts +7 -0
  76. package/dist/profiler/app-path.js +26 -0
  77. package/dist/profiler/async-io.d.ts +58 -0
  78. package/dist/profiler/async-io.js +187 -0
  79. package/dist/profiler/beacon-standalone.d.ts +4 -0
  80. package/dist/profiler/beacon-standalone.js +409 -0
  81. package/dist/profiler/browser-findings.d.ts +25 -0
  82. package/dist/profiler/browser-findings.js +260 -0
  83. package/dist/profiler/byte-strip.d.ts +58 -0
  84. package/dist/profiler/byte-strip.js +228 -0
  85. package/dist/profiler/client-files.d.ts +16 -0
  86. package/dist/profiler/client-files.js +60 -0
  87. package/dist/profiler/client-windows.d.ts +49 -0
  88. package/dist/profiler/client-windows.js +95 -0
  89. package/dist/profiler/client.d.ts +40 -0
  90. package/dist/profiler/client.js +82 -0
  91. package/dist/profiler/compare.d.ts +308 -0
  92. package/dist/profiler/compare.js +733 -0
  93. package/dist/profiler/compress.d.ts +10 -0
  94. package/dist/profiler/compress.js +36 -0
  95. package/dist/profiler/contention.d.ts +60 -0
  96. package/dist/profiler/contention.js +86 -0
  97. package/dist/profiler/crypto.d.ts +19 -0
  98. package/dist/profiler/crypto.js +101 -0
  99. package/dist/profiler/doc-diff.d.ts +47 -0
  100. package/dist/profiler/doc-diff.js +214 -0
  101. package/dist/profiler/dom-diff.d.ts +43 -0
  102. package/dist/profiler/dom-diff.js +121 -0
  103. package/dist/profiler/drill.d.ts +89 -0
  104. package/dist/profiler/drill.js +553 -0
  105. package/dist/profiler/embedded-maps.d.ts +37 -0
  106. package/dist/profiler/embedded-maps.js +135 -0
  107. package/dist/profiler/env.d.ts +6 -0
  108. package/dist/profiler/env.js +10 -0
  109. package/dist/profiler/forecast.d.ts +76 -0
  110. package/dist/profiler/forecast.js +225 -0
  111. package/dist/profiler/frames.d.ts +11 -0
  112. package/dist/profiler/frames.js +42 -0
  113. package/dist/profiler/gc.d.ts +144 -0
  114. package/dist/profiler/gc.js +271 -0
  115. package/dist/profiler/hole-slots.d.ts +31 -0
  116. package/dist/profiler/hole-slots.js +90 -0
  117. package/dist/profiler/index.d.ts +288 -0
  118. package/dist/profiler/index.js +4921 -0
  119. package/dist/profiler/insights.d.ts +96 -0
  120. package/dist/profiler/insights.js +239 -0
  121. package/dist/profiler/ledger.d.ts +121 -0
  122. package/dist/profiler/ledger.js +440 -0
  123. package/dist/profiler/lineage.d.ts +81 -0
  124. package/dist/profiler/lineage.js +359 -0
  125. package/dist/profiler/module-map.d.ts +12 -0
  126. package/dist/profiler/module-map.js +94 -0
  127. package/dist/profiler/net.d.ts +169 -0
  128. package/dist/profiler/net.js +586 -0
  129. package/dist/profiler/on-demand.d.ts +11 -0
  130. package/dist/profiler/on-demand.js +15 -0
  131. package/dist/profiler/own-requests.d.ts +9 -0
  132. package/dist/profiler/own-requests.js +17 -0
  133. package/dist/profiler/patterns.d.ts +476 -0
  134. package/dist/profiler/patterns.js +2203 -0
  135. package/dist/profiler/profiler-router.d.ts +245 -0
  136. package/dist/profiler/profiler-router.js +113 -0
  137. package/dist/profiler/refresh-stub.d.ts +2 -0
  138. package/dist/profiler/refresh-stub.js +37 -0
  139. package/dist/profiler/report.d.ts +1404 -0
  140. package/dist/profiler/report.js +2638 -0
  141. package/dist/profiler/river.d.ts +65 -0
  142. package/dist/profiler/river.js +298 -0
  143. package/dist/profiler/route-files.d.ts +22 -0
  144. package/dist/profiler/route-files.js +55 -0
  145. package/dist/profiler/route-imports.d.ts +14 -0
  146. package/dist/profiler/route-imports.js +83 -0
  147. package/dist/profiler/saving.d.ts +22 -0
  148. package/dist/profiler/saving.js +42 -0
  149. package/dist/profiler/score.d.ts +109 -0
  150. package/dist/profiler/score.js +261 -0
  151. package/dist/profiler/session-cookie.d.ts +28 -0
  152. package/dist/profiler/session-cookie.js +45 -0
  153. package/dist/profiler/sink.d.ts +81 -0
  154. package/dist/profiler/sink.js +124 -0
  155. package/dist/profiler/site-fixes.d.ts +25 -0
  156. package/dist/profiler/site-fixes.js +62 -0
  157. package/dist/profiler/source-scan.d.ts +210 -0
  158. package/dist/profiler/source-scan.js +1724 -0
  159. package/dist/profiler/span.d.ts +90 -0
  160. package/dist/profiler/span.js +109 -0
  161. package/dist/profiler/stacks.d.ts +43 -0
  162. package/dist/profiler/stacks.js +107 -0
  163. package/dist/profiler/standalone.d.ts +27 -0
  164. package/dist/profiler/standalone.js +98 -0
  165. package/dist/profiler/steps.d.ts +39 -0
  166. package/dist/profiler/steps.js +74 -0
  167. package/dist/profiler/storage/index.d.ts +87 -0
  168. package/dist/profiler/storage/index.js +76 -0
  169. package/dist/profiler/storage/postgres.d.ts +12 -0
  170. package/dist/profiler/storage/postgres.js +94 -0
  171. package/dist/profiler/storage/redis.d.ts +19 -0
  172. package/dist/profiler/storage/redis.js +148 -0
  173. package/dist/profiler/storage/sqlite.d.ts +2 -0
  174. package/dist/profiler/storage/sqlite.js +85 -0
  175. package/dist/profiler/storage/visits.d.ts +4 -0
  176. package/dist/profiler/storage/visits.js +6 -0
  177. package/dist/profiler/stream-tail.d.ts +26 -0
  178. package/dist/profiler/stream-tail.js +74 -0
  179. package/dist/profiler/timeline.d.ts +279 -0
  180. package/dist/profiler/timeline.js +690 -0
  181. package/dist/profiler/ui/AllocStrip.svelte +146 -0
  182. package/dist/profiler/ui/AllocStrip.svelte.d.ts +17 -0
  183. package/dist/profiler/ui/Awaits.svelte +363 -0
  184. package/dist/profiler/ui/Awaits.svelte.d.ts +16 -0
  185. package/dist/profiler/ui/ByteStrip.svelte +182 -0
  186. package/dist/profiler/ui/ByteStrip.svelte.d.ts +18 -0
  187. package/dist/profiler/ui/Compare.svelte +24 -0
  188. package/dist/profiler/ui/Compare.svelte.d.ts +16 -0
  189. package/dist/profiler/ui/CompareBody.svelte +429 -0
  190. package/dist/profiler/ui/CompareBody.svelte.d.ts +8 -0
  191. package/dist/profiler/ui/ComponentsTable.svelte +265 -0
  192. package/dist/profiler/ui/ComponentsTable.svelte.d.ts +20 -0
  193. package/dist/profiler/ui/Dashboard.svelte +492 -0
  194. package/dist/profiler/ui/Dashboard.svelte.d.ts +31 -0
  195. package/dist/profiler/ui/DataRiver.svelte +206 -0
  196. package/dist/profiler/ui/DataRiver.svelte.d.ts +13 -0
  197. package/dist/profiler/ui/DomTravel.svelte +207 -0
  198. package/dist/profiler/ui/DomTravel.svelte.d.ts +9 -0
  199. package/dist/profiler/ui/DownloadHtml.svelte +52 -0
  200. package/dist/profiler/ui/DownloadHtml.svelte.d.ts +10 -0
  201. package/dist/profiler/ui/Drill.svelte +276 -0
  202. package/dist/profiler/ui/Drill.svelte.d.ts +12 -0
  203. package/dist/profiler/ui/ExportButton.svelte +59 -0
  204. package/dist/profiler/ui/ExportButton.svelte.d.ts +8 -0
  205. package/dist/profiler/ui/Flame.svelte +222 -0
  206. package/dist/profiler/ui/Flame.svelte.d.ts +12 -0
  207. package/dist/profiler/ui/FrameDetails.svelte +308 -0
  208. package/dist/profiler/ui/FrameDetails.svelte.d.ts +16 -0
  209. package/dist/profiler/ui/FunctionsTable.svelte +156 -0
  210. package/dist/profiler/ui/FunctionsTable.svelte.d.ts +17 -0
  211. package/dist/profiler/ui/IslandHeatmap.svelte +151 -0
  212. package/dist/profiler/ui/IslandHeatmap.svelte.d.ts +16 -0
  213. package/dist/profiler/ui/IslandsTable.svelte +448 -0
  214. package/dist/profiler/ui/IslandsTable.svelte.d.ts +16 -0
  215. package/dist/profiler/ui/KeepLocal.svelte +46 -0
  216. package/dist/profiler/ui/KeepLocal.svelte.d.ts +8 -0
  217. package/dist/profiler/ui/Ledger.svelte +196 -0
  218. package/dist/profiler/ui/Ledger.svelte.d.ts +18 -0
  219. package/dist/profiler/ui/LocalCompare.svelte +48 -0
  220. package/dist/profiler/ui/LocalCompare.svelte.d.ts +8 -0
  221. package/dist/profiler/ui/LocalReports.svelte +68 -0
  222. package/dist/profiler/ui/LocalReports.svelte.d.ts +7 -0
  223. package/dist/profiler/ui/Login.svelte +13 -0
  224. package/dist/profiler/ui/Login.svelte.d.ts +12 -0
  225. package/dist/profiler/ui/LoginForm.svelte +42 -0
  226. package/dist/profiler/ui/LoginForm.svelte.d.ts +7 -0
  227. package/dist/profiler/ui/Message.svelte +10 -0
  228. package/dist/profiler/ui/Message.svelte.d.ts +12 -0
  229. package/dist/profiler/ui/OnThisPage.svelte +137 -0
  230. package/dist/profiler/ui/OnThisPage.svelte.d.ts +3 -0
  231. package/dist/profiler/ui/OneClock.svelte +297 -0
  232. package/dist/profiler/ui/OneClock.svelte.d.ts +27 -0
  233. package/dist/profiler/ui/Panel.svelte +10 -0
  234. package/dist/profiler/ui/Panel.svelte.d.ts +16 -0
  235. package/dist/profiler/ui/Paths.svelte +269 -0
  236. package/dist/profiler/ui/Paths.svelte.d.ts +15 -0
  237. package/dist/profiler/ui/Patterns.svelte +364 -0
  238. package/dist/profiler/ui/Patterns.svelte.d.ts +15 -0
  239. package/dist/profiler/ui/PermalinkGate.svelte +143 -0
  240. package/dist/profiler/ui/PermalinkGate.svelte.d.ts +10 -0
  241. package/dist/profiler/ui/RenderSteps.svelte +218 -0
  242. package/dist/profiler/ui/RenderSteps.svelte.d.ts +13 -0
  243. package/dist/profiler/ui/Report.svelte +28 -0
  244. package/dist/profiler/ui/Report.svelte.d.ts +20 -0
  245. package/dist/profiler/ui/ReportBody.svelte +2592 -0
  246. package/dist/profiler/ui/ReportBody.svelte.d.ts +21 -0
  247. package/dist/profiler/ui/Run.svelte +17 -0
  248. package/dist/profiler/ui/Run.svelte.d.ts +14 -0
  249. package/dist/profiler/ui/RunView.svelte +146 -0
  250. package/dist/profiler/ui/RunView.svelte.d.ts +10 -0
  251. package/dist/profiler/ui/ScoreCard.svelte +197 -0
  252. package/dist/profiler/ui/ScoreCard.svelte.d.ts +7 -0
  253. package/dist/profiler/ui/Scrub.svelte +293 -0
  254. package/dist/profiler/ui/Scrub.svelte.d.ts +18 -0
  255. package/dist/profiler/ui/SeedExplainer.svelte +193 -0
  256. package/dist/profiler/ui/SeedExplainer.svelte.d.ts +16 -0
  257. package/dist/profiler/ui/ShareLink.svelte +148 -0
  258. package/dist/profiler/ui/ShareLink.svelte.d.ts +8 -0
  259. package/dist/profiler/ui/Shell.svelte +50 -0
  260. package/dist/profiler/ui/Shell.svelte.d.ts +27 -0
  261. package/dist/profiler/ui/Sidebar.svelte +312 -0
  262. package/dist/profiler/ui/Sidebar.svelte.d.ts +7 -0
  263. package/dist/profiler/ui/Site.svelte +20 -0
  264. package/dist/profiler/ui/Site.svelte.d.ts +23 -0
  265. package/dist/profiler/ui/SiteView.svelte +385 -0
  266. package/dist/profiler/ui/SiteView.svelte.d.ts +27 -0
  267. package/dist/profiler/ui/ThemeSwitch.svelte +78 -0
  268. package/dist/profiler/ui/ThemeSwitch.svelte.d.ts +3 -0
  269. package/dist/profiler/ui/Timeline.svelte +672 -0
  270. package/dist/profiler/ui/Timeline.svelte.d.ts +22 -0
  271. package/dist/profiler/ui/Treemap.svelte +278 -0
  272. package/dist/profiler/ui/Treemap.svelte.d.ts +20 -0
  273. package/dist/profiler/ui/Upload.svelte +19 -0
  274. package/dist/profiler/ui/Upload.svelte.d.ts +10 -0
  275. package/dist/profiler/ui/UploadForm.svelte +59 -0
  276. package/dist/profiler/ui/UploadForm.svelte.d.ts +6 -0
  277. package/dist/profiler/ui/WaitingTable.svelte +55 -0
  278. package/dist/profiler/ui/WaitingTable.svelte.d.ts +14 -0
  279. package/dist/profiler/ui/Waterfall.svelte +371 -0
  280. package/dist/profiler/ui/Waterfall.svelte.d.ts +7 -0
  281. package/dist/profiler/ui/WhatIf.svelte +87 -0
  282. package/dist/profiler/ui/WhatIf.svelte.d.ts +18 -0
  283. package/dist/profiler/ui/fake-progress.svelte.d.ts +15 -0
  284. package/dist/profiler/ui/fake-progress.svelte.js +38 -0
  285. package/dist/profiler/ui/format.d.ts +20 -0
  286. package/dist/profiler/ui/format.js +71 -0
  287. package/dist/profiler/ui/permalink.d.ts +9 -0
  288. package/dist/profiler/ui/permalink.js +108 -0
  289. package/dist/profiler/ui/press.d.ts +5 -0
  290. package/dist/profiler/ui/press.js +19 -0
  291. package/dist/profiler/ui/profiler.css +650 -0
  292. package/dist/profiler/ui/report-data.d.ts +189 -0
  293. package/dist/profiler/ui/report-data.js +393 -0
  294. package/dist/profiler/ui/report-request.d.ts +6 -0
  295. package/dist/profiler/ui/report-request.js +50 -0
  296. package/dist/profiler/ui/row-anchor.svelte.d.ts +12 -0
  297. package/dist/profiler/ui/row-anchor.svelte.js +44 -0
  298. package/dist/profiler/ui/sort.svelte.d.ts +14 -0
  299. package/dist/profiler/ui/sort.svelte.js +37 -0
  300. package/dist/profiler/ui/store.d.ts +45 -0
  301. package/dist/profiler/ui/store.js +92 -0
  302. package/dist/profiler/visit.d.ts +389 -0
  303. package/dist/profiler/visit.js +1002 -0
  304. package/dist/report.d.ts +5 -0
  305. package/dist/report.js +4 -0
  306. package/dist/server.d.ts +11 -0
  307. package/dist/server.js +38 -0
  308. package/dist/vite.d.ts +12 -0
  309. package/dist/vite.js +78 -0
  310. package/package.json +105 -6
@@ -0,0 +1,663 @@
1
+ <!-- Every devtools component injects its own CSS: the dock lives in a shadow root, and Svelte puts an
2
+ injected component's <style> in the shadow root it mounts into. An app's build extracts component CSS
3
+ to a file by default (Kit's `css: 'external'`), which the page links into its own <head>, where the
4
+ shadow boundary keeps it from the dock — a built dock opened unstyled. -->
5
+ <svelte:options css="injected" />
6
+
7
+ <script>
8
+ /**
9
+ * The devtools ROOT app — one component we `mount()` once (from ui.ts, behind the gate). It owns the
10
+ * launcher, a draggable + resizable tabbed window, and the boundary overlay; each tab is a child that
11
+ * reads the event bus. All UI state lives here as runes; children get `tick` (a refresh pulse while
12
+ * the window is open) + `overlay` (bindable, from the Lens tab).
13
+ *
14
+ * Reactivity rule for this whole component: **no `$effect`.** Everything runs at the moment a value
15
+ * changes or a callback fires — neodrag's get/set option pairs persist layout on drag/resize end, the
16
+ * open/close path owns the refresh timer, and the one global keydown is registered in `onMount`.
17
+ *
18
+ * Drag/resize is @neodrag/svelte (a runtime dependency of ogygia, so the bare import resolves for any
19
+ * consumer). The window drags only by its header and may sit partly off-screen; everything lives inside
20
+ * a fixed, clipped, click-through root so an off-screen panel can never make the host page scroll.
21
+ */
22
+ import LensTab from './LensTab.svelte';
23
+ import LedgerTab from './LedgerTab.svelte';
24
+ import TimelineTab from './TimelineTab.svelte';
25
+ import WireTab from './WireTab.svelte';
26
+ import HubTab from './HubTab.svelte';
27
+ import NavTab from './NavTab.svelte';
28
+ import ProfilerTab from './ProfilerTab.svelte';
29
+ import PageTab from './PageTab.svelte';
30
+ import SessionTab from './SessionTab.svelte';
31
+ import HydrationTab from './HydrationTab.svelte';
32
+ import { onMount } from 'svelte';
33
+ import BoundaryOverlay from './BoundaryOverlay.svelte';
34
+ import IslandDetail from './IslandDetail.svelte';
35
+ import { to_trace } from '@ogygia/core/events';
36
+ import { Draggable } from '@neodrag/svelte';
37
+ import { Resizable, RESIZE_EDGES } from '@neodrag/svelte/resize';
38
+
39
+ // `csrTrue` = mounted by the standalone boot on a Kit-hydrated (csr=true) page. The ogygia runtime
40
+ // never ran there, so there's no event bus and no islands to inspect — the window shows a notice
41
+ // pointing at a csr=false page instead of the (empty) instrument tabs.
42
+ // `startOpen` = the launcher's click loaded this dock (a build loads it only on open): open at once.
43
+ // `hide` (a build only): turn devtools off in this browser — the launcher and dock go, nothing is
44
+ // measured from the next load (insights/switch.ts); `?og-devtools=on` brings it back.
45
+ /** @type {{ csrTrue?: boolean, startOpen?: boolean, hide?: () => void }} */
46
+ let { csrTrue = false, startOpen = false, hide } = $props();
47
+ const LAZY = typeof __OGYGIA_DEVTOOLS_LAZY__ !== 'undefined' ? __OGYGIA_DEVTOOLS_LAZY__ : false;
48
+
49
+ const TABS = [
50
+ { id: 'lens', label: 'Lens' },
51
+ { id: 'page', label: 'Page' },
52
+ { id: 'record', label: 'Record' },
53
+ { id: 'hydration', label: 'Hydration' },
54
+ { id: 'bytes', label: 'Bytes' },
55
+ { id: 'wire', label: 'Wire' },
56
+ { id: 'hub', label: 'Hub' },
57
+ { id: 'nav', label: 'Nav' },
58
+ { id: 'timeline', label: 'Timeline' },
59
+ { id: 'profiler', label: 'Profiler' }
60
+ ];
61
+
62
+ // ── persisted layout (localStorage; written from callbacks, never an effect) ─────────────────────
63
+ const LS_KEY = 'ogygia:devtools:layout:v4';
64
+ function load_layout() {
65
+ try {
66
+ return JSON.parse(localStorage.getItem(LS_KEY) || '{}') || {};
67
+ } catch {
68
+ return {};
69
+ }
70
+ }
71
+ const saved = load_layout();
72
+ // The window is CSS-anchored top-left; neodrag's translate (`winPos`) places it. On the very first
73
+ // open with nothing saved we drop it bottom-right, above the launcher (`place_near_launcher`).
74
+ let launchPos = $state(saved.launch ?? { x: 0, y: 0 });
75
+ // Two SEPARATE offsets: `winPos` is the drag translate, `winResizeOff` is the translate a w/n resize
76
+ // induces to pin the far edge. neodrag SUMS both into the node transform, so they must be distinct —
77
+ // binding one state to both doubled it and threw the window off-screen.
78
+ let winPos = $state(saved.win ?? { x: 0, y: 0 });
79
+ let winResizeOff = $state(saved.rz ?? { x: 0, y: 0 });
80
+ let winSize = $state(saved.size ?? { width: 600, height: 680 });
81
+ const was_open = saved.open ?? false;
82
+ let open = $state(was_open);
83
+ // OPEN AS THE PAGE LOADS (remembered from the last page): when it began to show — the Page tab says
84
+ // so when the page's first paint follows it closely, since a browser may count the dock's own text
85
+ // as the page's first paint (Safari: its largest paint too)
86
+ if (was_open && typeof performance !== 'undefined')
87
+ globalThis[Symbol.for('ogygia.dock-open-at')] ??= performance.now();
88
+ let placed = saved.win != null;
89
+ function save_layout() {
90
+ try {
91
+ localStorage.setItem(
92
+ LS_KEY,
93
+ JSON.stringify({ open, launch: launchPos, win: winPos, rz: winResizeOff, size: winSize })
94
+ );
95
+ } catch {
96
+ /* private mode / quota — layout just won't persist */
97
+ }
98
+ }
99
+
100
+ // ── neodrag: launcher drags whole; window drags by header, resizes by its edges ─────────────────
101
+ const launchDrag = new Draggable({
102
+ // Keep a grabbable sliver on-screen but otherwise let it roam.
103
+ bounds: { target: 'viewport', padding: -80 },
104
+ get position() {
105
+ return launchPos;
106
+ },
107
+ set position(p) {
108
+ launchPos = p;
109
+ },
110
+ onDragEnd: (e) => {
111
+ launchPos = e.offset;
112
+ save_layout();
113
+ }
114
+ });
115
+ // Drag + resize COMPOSE through neodrag's shared transform: both bind the same `winPos` offset, so a
116
+ // `w`/`n` resize's far-edge-pin translate and the drag translate never fight. The resize core also owns
117
+ // applying width/height — we never set them ourselves. Negative bounds padding lets it roam anywhere,
118
+ // partly off-screen (the clip root stops any page scroll); a sliver always stays grabbable.
119
+ const winDrag = new Draggable({
120
+ bounds: { target: 'viewport', padding: -240 },
121
+ get position() {
122
+ return winPos;
123
+ },
124
+ set position(p) {
125
+ winPos = p;
126
+ },
127
+ onDragEnd: () => save_layout()
128
+ });
129
+ const winResize = new Resizable({
130
+ minWidth: 300,
131
+ minHeight: 200,
132
+ get size() {
133
+ return winSize;
134
+ },
135
+ set size(s) {
136
+ winSize = s;
137
+ },
138
+ get position() {
139
+ return winResizeOff;
140
+ },
141
+ set position(p) {
142
+ winResizeOff = p;
143
+ },
144
+ onResizeEnd: () => save_layout()
145
+ });
146
+ // Stable attachment/handle objects — computed once so re-renders don't re-run the attachments.
147
+ const winHandle = winDrag.handle();
148
+ const winCancel = winDrag.cancel();
149
+ // All eight edges — the shared-`winPos` composition above makes w/n resizes behave.
150
+ const resizeHandles = RESIZE_EDGES.map((edge) => ({ edge, props: winResize.handle(edge) }));
151
+
152
+ // ── open/close owns the live-refresh timer (no effect) ──────────────────────────────────────────
153
+ let tab = $state('lens');
154
+ let tick = $state(0);
155
+ // Explicit hover state for the launcher (pointerenter/leave — a `:hover` descendant selector proved
156
+ // flaky against neodrag's inline transform). `up` = fully revealed; otherwise it peeks below the fold.
157
+ let launchHover = $state(false);
158
+ let tickTimer = /** @type {ReturnType<typeof setInterval> | null} */ (null);
159
+ function refresh_on() {
160
+ if (!tickTimer) tickTimer = setInterval(() => tick++, 700);
161
+ }
162
+ function refresh_off() {
163
+ if (tickTimer) {
164
+ clearInterval(tickTimer);
165
+ tickTimer = null;
166
+ }
167
+ }
168
+ function place_near_launcher() {
169
+ winPos = {
170
+ x: Math.max(8, innerWidth - winSize.width - 24),
171
+ y: Math.max(8, innerHeight - winSize.height - 88)
172
+ };
173
+ }
174
+ function set_open(v) {
175
+ open = v;
176
+ if (open) {
177
+ if (!placed) {
178
+ place_near_launcher();
179
+ placed = true;
180
+ }
181
+ refresh_on();
182
+ } else {
183
+ refresh_off();
184
+ }
185
+ save_layout();
186
+ }
187
+ function toggle_open() {
188
+ set_open(!open);
189
+ }
190
+
191
+ // ── selection / picking (cross-highlight with the overlay + Lens tab) ───────────────────────────
192
+ let overlay = $state(false);
193
+ let focus = $state(/** @type {Element | null} */ (null));
194
+ let selected = $state(/** @type {Element | null} */ (null));
195
+ let picking = $state(false);
196
+ // Picking an island on the page ends pick mode AND turns off the overlay the picker switched on — you
197
+ // wanted THAT island, not the boxes. Done here at the assignment, not via an effect.
198
+ function select_region(el) {
199
+ selected = el;
200
+ picking = false;
201
+ overlay = false;
202
+ }
203
+
204
+ let copied = $state('');
205
+ async function copy_trace() {
206
+ const text = JSON.stringify(to_trace());
207
+ try {
208
+ // (no clipboard over plain http off localhost, a phone on the LAN; or the permission is refused)
209
+ if (!navigator.clipboard) throw new Error('no clipboard');
210
+ await navigator.clipboard.writeText(text);
211
+ copied = 'copied ✓';
212
+ } catch {
213
+ // the button used to do nothing here: save the trace as a file instead
214
+ const url = URL.createObjectURL(new Blob([text], { type: 'application/json' }));
215
+ const a = document.createElement('a');
216
+ a.href = url;
217
+ a.download = `ogygia-trace-${Date.now()}.json`;
218
+ a.click();
219
+ setTimeout(() => URL.revokeObjectURL(url), 1000);
220
+ copied = 'saved as a file';
221
+ }
222
+ setTimeout(() => (copied = ''), 1600);
223
+ }
224
+
225
+ // One-time setup: Alt+O toggle, the compiler's devtools metadata, and (re)starting the refresh timer
226
+ // if we restored an open window. Returns the teardown — this is lifecycle, not reactive state.
227
+ onMount(() => {
228
+ const onkey = (e) => {
229
+ if (e.altKey && (e.key === 'o' || e.key === 'O')) {
230
+ e.preventDefault();
231
+ toggle_open();
232
+ }
233
+ };
234
+ window.addEventListener('keydown', onkey);
235
+
236
+ // `names` (island id → component name) and `bytes` (island id → transitive dev-graph size) label
237
+ // the tabs with real names/costs. Best-effort — the endpoint only exists on the dev server (a
238
+ // build carries its names in the dock's own module: ui-app.ts).
239
+ if (!LAZY)
240
+ fetch('/__ogygia_devtools_meta')
241
+ .then((r) => (r.ok ? r.json() : null))
242
+ .then((meta) => {
243
+ if (!meta || typeof meta !== 'object') return;
244
+ if (meta.names) window.__ogygia_region_names = meta.names;
245
+ if (meta.bytes) window.__ogygia_region_bytes = meta.bytes;
246
+ tick++;
247
+ })
248
+ .catch(() => {});
249
+
250
+ if (startOpen && !open) set_open(true);
251
+ else if (open) refresh_on();
252
+ return () => {
253
+ window.removeEventListener('keydown', onkey);
254
+ refresh_off();
255
+ };
256
+ });
257
+ </script>
258
+
259
+ <BoundaryOverlay {overlay} bind:focus {selected} bind:picking onpick={select_region} />
260
+
261
+ <!-- Fixed, clipped, click-through root: absolute children (window/launcher) can be dragged partly
262
+ off-screen and are clipped at the viewport edge, so the host page never gains a scrollbar. -->
263
+ <div class="og-root">
264
+ {#if open}
265
+ <section
266
+ class="win"
267
+ class:busy={winResize.isResizing || winDrag.isDragging}
268
+ data-og-win
269
+ {...winDrag.attach}
270
+ {...winResize.attach}
271
+ >
272
+ <!-- (a plain header: the tabs inside are the tablist; the title is the dock's heading) -->
273
+ <header class="hd" {...winHandle}>
274
+ <!-- Title bar = the drag zone. Only the two buttons on it are cancel zones, so most of the row
275
+ (grip + title + spacer) is a generous grab area with a grab cursor. -->
276
+ <div class="bar">
277
+ <span class="grip" aria-hidden="true"></span>
278
+ <h2 class="ttl">ogygia devtools</h2>
279
+ {#if !csrTrue}
280
+ <button
281
+ class="trace"
282
+ {...winCancel}
283
+ title="copy an event trace to the clipboard (saved as a file where the clipboard is not allowed)"
284
+ onclick={copy_trace}
285
+ >
286
+ {copied || 'trace'}
287
+ </button>
288
+ {:else}
289
+ <span class="mode">csr=true</span>
290
+ {/if}
291
+ {#if hide}
292
+ <button
293
+ class="trace hide"
294
+ data-og-hide
295
+ {...winCancel}
296
+ title="turn devtools off in this browser (?og-devtools=on brings it back)"
297
+ onclick={hide}>hide</button
298
+ >
299
+ {/if}
300
+ <button class="x" {...winCancel} title="close" onclick={() => set_open(false)}>✕</button>
301
+ </div>
302
+ {#if !csrTrue}
303
+ <div class="tabs" role="tablist" aria-label="devtools instruments">
304
+ {#each TABS as t}
305
+ <button
306
+ class="tab"
307
+ {...winCancel}
308
+ role="tab"
309
+ aria-selected={tab === t.id}
310
+ aria-controls="og-dt-panel"
311
+ data-og-tab={t.id}
312
+ class:on={tab === t.id}
313
+ onclick={() => (tab = t.id)}
314
+ >
315
+ {t.label}
316
+ </button>
317
+ {/each}
318
+ </div>
319
+ {/if}
320
+ </header>
321
+
322
+ <!-- the panel scrolls: focusable so a keyboard can scroll it, labelled by the open tab -->
323
+ <div
324
+ class="body"
325
+ id="og-dt-panel"
326
+ role="tabpanel"
327
+ tabindex="0"
328
+ aria-label={TABS.find((t) => t.id === tab)?.label ?? 'devtools'}
329
+ >
330
+ {#if csrTrue}
331
+ <!-- a Kit-hydrated page: no islands, but the browser's side of it still tells a lot -->
332
+ <div class="notice" data-og-csr-notice>
333
+ <p class="h">This page runs on <code>csr=true</code>.</p>
334
+ <p>
335
+ SvelteKit hydrates the whole page here, so ogygia's runtime never boots: there are no
336
+ islands, no wire and no byte ledger. What the browser saw is below. Open a <code
337
+ >csr=false</code
338
+ > page for the island tools.
339
+ </p>
340
+ </div>
341
+ <PageTab {tick} bind:focus bind:selected />
342
+ {:else if selected}
343
+ <IslandDetail el={selected} {tick} onclose={() => (selected = null)} />
344
+ {:else if tab === 'lens'}
345
+ <LensTab {tick} bind:overlay bind:focus bind:selected bind:picking />
346
+ {:else if tab === 'page'}
347
+ <PageTab {tick} bind:focus bind:selected />
348
+ {:else if tab === 'record'}
349
+ <SessionTab {tick} />
350
+ {:else if tab === 'hydration'}
351
+ <HydrationTab {tick} bind:selected />
352
+ {:else if tab === 'bytes'}
353
+ <LedgerTab {tick} />
354
+ {:else if tab === 'wire'}
355
+ <WireTab {tick} />
356
+ {:else if tab === 'hub'}
357
+ <HubTab {tick} />
358
+ {:else if tab === 'nav'}
359
+ <NavTab {tick} />
360
+ {:else if tab === 'timeline'}
361
+ <TimelineTab {tick} />
362
+ {:else if tab === 'profiler'}
363
+ <ProfilerTab {tick} bind:focus bind:selected />
364
+ {/if}
365
+ </div>
366
+
367
+ {#each resizeHandles as h (h.edge)}
368
+ <div class="rh rh-{h.edge}" {...h.props}></div>
369
+ {/each}
370
+ </section>
371
+ {/if}
372
+
373
+ <div
374
+ class="launch-wrap"
375
+ {...launchDrag.attach}
376
+ onpointerenter={() => (launchHover = true)}
377
+ onpointerleave={() => (launchHover = false)}
378
+ >
379
+ <button
380
+ class="launch"
381
+ data-og-panel-toggle
382
+ class:on={open}
383
+ class:up={open || launchHover}
384
+ title="ogygia devtools (Alt+O)"
385
+ onclick={toggle_open}
386
+ >
387
+ <span class="dot"></span>og devtools
388
+ </button>
389
+ </div>
390
+ </div>
391
+
392
+ <style>
393
+ .og-root {
394
+ position: fixed;
395
+ inset: 0;
396
+ overflow: hidden;
397
+ pointer-events: none;
398
+ /* Max int32 — the launcher + window must beat anything. (The shadow HOST also carries max
399
+ z-index in the page context — see ui.ts; in here we only order against our own siblings:
400
+ the boundary overlay sits below at 2147483000 so the window paints over the lens.) */
401
+ z-index: 2147483647;
402
+ font:
403
+ 11px/1.5 ui-monospace,
404
+ SFMono-Regular,
405
+ Menlo,
406
+ monospace;
407
+ }
408
+
409
+ /* ── window ───────────────────────────────────────────────────────────── */
410
+ .win {
411
+ position: absolute;
412
+ left: 0;
413
+ top: 0;
414
+ /* Pre-attach fallback size; neodrag's resize core applies the live/persisted size on mount. */
415
+ width: 600px;
416
+ height: 680px;
417
+ pointer-events: auto;
418
+ display: flex;
419
+ flex-direction: column;
420
+ background: #0b1220;
421
+ color: #e2e8f0;
422
+ border: 1px solid rgba(148, 163, 184, 0.3);
423
+ border-radius: 12px;
424
+ box-shadow: 0 18px 48px rgba(0, 0, 0, 0.5);
425
+ overflow: hidden;
426
+ }
427
+ /* While dragging or resizing, kill text selection so a gesture never highlights the panel's contents. */
428
+ .win.busy,
429
+ .win.busy * {
430
+ user-select: none;
431
+ -webkit-user-select: none;
432
+ }
433
+ .hd {
434
+ display: flex;
435
+ flex-direction: column;
436
+ gap: 8px;
437
+ padding: 8px 10px;
438
+ cursor: grab;
439
+ user-select: none;
440
+ border-bottom: 1px solid rgba(148, 163, 184, 0.18);
441
+ background: #0d1526;
442
+ }
443
+ .hd:active {
444
+ cursor: grabbing;
445
+ }
446
+ /* Title bar — the drag zone. */
447
+ .bar {
448
+ display: flex;
449
+ align-items: center;
450
+ gap: 8px;
451
+ }
452
+ .grip {
453
+ width: 16px;
454
+ height: 12px;
455
+ flex: none;
456
+ background-image: radial-gradient(circle, #475569 1.1px, transparent 1.3px);
457
+ background-size: 5px 5px;
458
+ background-position: 1px 1px;
459
+ opacity: 0.75;
460
+ }
461
+ .tabs {
462
+ display: flex;
463
+ flex-wrap: wrap;
464
+ gap: 4px;
465
+ }
466
+ .ttl {
467
+ /* (a heading for assistive tech; it looks like the title it was) */
468
+ margin: 0 8px 0 0;
469
+ font-size: inherit;
470
+ font-weight: 700;
471
+ color: #5eead4;
472
+ letter-spacing: 0.02em;
473
+ }
474
+ .body:focus-visible {
475
+ outline: 2px solid rgba(94, 234, 212, 0.6);
476
+ outline-offset: -2px;
477
+ }
478
+ .tab {
479
+ padding: 4px 10px;
480
+ border-radius: 7px;
481
+ border: 1px solid transparent;
482
+ color: #94a3b8;
483
+ cursor: pointer;
484
+ background: none;
485
+ font: inherit;
486
+ }
487
+ .tab.on {
488
+ color: #e2e8f0;
489
+ background: rgba(148, 163, 184, 0.14);
490
+ border-color: rgba(148, 163, 184, 0.25);
491
+ }
492
+ .trace {
493
+ margin-left: auto;
494
+ padding: 3px 9px;
495
+ border-radius: 7px;
496
+ border: 1px solid rgba(148, 163, 184, 0.25);
497
+ background: #0d1526;
498
+ color: #94a3b8;
499
+ font: inherit;
500
+ font-size: 11px;
501
+ cursor: pointer;
502
+ }
503
+ .trace:hover {
504
+ color: #e2e8f0;
505
+ }
506
+ /* beside trace (which takes the bar's free space), not a second push to the right */
507
+ .hide {
508
+ margin-left: 6px;
509
+ }
510
+ .x {
511
+ color: #94a3b8;
512
+ cursor: pointer;
513
+ background: none;
514
+ border: none;
515
+ font: inherit;
516
+ font-size: 14px;
517
+ padding: 0 4px;
518
+ }
519
+ .body {
520
+ flex: 1;
521
+ overflow: auto;
522
+ padding: 12px 14px;
523
+ }
524
+ /* csr=true header pill — takes the `margin-left:auto` slot the trace button usually holds. */
525
+ .mode {
526
+ margin-left: auto;
527
+ padding: 3px 9px;
528
+ border-radius: 7px;
529
+ border: 1px solid rgba(251, 191, 36, 0.35);
530
+ background: rgba(251, 191, 36, 0.1);
531
+ color: #fbbf24;
532
+ font-size: 11px;
533
+ letter-spacing: 0.02em;
534
+ }
535
+ .notice {
536
+ display: flex;
537
+ flex-direction: column;
538
+ gap: 10px;
539
+ max-width: 46ch;
540
+ color: #cbd5e1;
541
+ line-height: 1.6;
542
+ }
543
+ .notice .h {
544
+ color: #e2e8f0;
545
+ font-weight: 600;
546
+ }
547
+ .notice code {
548
+ padding: 1px 5px;
549
+ border-radius: 5px;
550
+ background: rgba(148, 163, 184, 0.16);
551
+ color: #5eead4;
552
+ }
553
+
554
+ /* ── resize handles (neodrag marks them by data-attr; we place + size them) ── */
555
+ .rh {
556
+ position: absolute;
557
+ z-index: 3;
558
+ }
559
+ .rh-n {
560
+ top: -3px;
561
+ left: 10px;
562
+ right: 10px;
563
+ height: 7px;
564
+ cursor: ns-resize;
565
+ }
566
+ .rh-s {
567
+ bottom: -3px;
568
+ left: 10px;
569
+ right: 10px;
570
+ height: 7px;
571
+ cursor: ns-resize;
572
+ }
573
+ .rh-e {
574
+ right: -3px;
575
+ top: 10px;
576
+ bottom: 10px;
577
+ width: 7px;
578
+ cursor: ew-resize;
579
+ }
580
+ .rh-w {
581
+ left: -3px;
582
+ top: 10px;
583
+ bottom: 10px;
584
+ width: 7px;
585
+ cursor: ew-resize;
586
+ }
587
+ .rh-ne {
588
+ top: -4px;
589
+ right: -4px;
590
+ width: 14px;
591
+ height: 14px;
592
+ cursor: nesw-resize;
593
+ }
594
+ .rh-nw {
595
+ top: -4px;
596
+ left: -4px;
597
+ width: 14px;
598
+ height: 14px;
599
+ cursor: nwse-resize;
600
+ }
601
+ .rh-se {
602
+ bottom: -4px;
603
+ right: -4px;
604
+ width: 14px;
605
+ height: 14px;
606
+ cursor: nwse-resize;
607
+ }
608
+ .rh-sw {
609
+ bottom: -4px;
610
+ left: -4px;
611
+ width: 14px;
612
+ height: 14px;
613
+ cursor: nesw-resize;
614
+ }
615
+
616
+ /* ── launcher: anchored to the bottom, peeks up half-hidden + faded, hover reveals ── */
617
+ .launch-wrap {
618
+ position: absolute;
619
+ right: 24px;
620
+ bottom: 0;
621
+ pointer-events: auto;
622
+ }
623
+ .launch {
624
+ display: flex;
625
+ align-items: center;
626
+ gap: 7px;
627
+ padding: 9px 14px;
628
+ border-radius: 999px;
629
+ border: 1px solid rgba(148, 163, 184, 0.35);
630
+ background: #0b1220;
631
+ color: #e2e8f0;
632
+ font:
633
+ 600 12px/1 ui-monospace,
634
+ SFMono-Regular,
635
+ Menlo,
636
+ monospace;
637
+ cursor: pointer;
638
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
639
+ /* Peeks up below the fold + dimmed until revealed (open or hovered). */
640
+ transform: translateY(44%);
641
+ opacity: 0.55;
642
+ transition:
643
+ transform 0.18s ease,
644
+ opacity 0.18s ease,
645
+ background 0.15s ease;
646
+ }
647
+ .launch.up {
648
+ transform: translateY(-8px);
649
+ opacity: 1;
650
+ }
651
+ .launch.on {
652
+ background: #14b8a6;
653
+ color: #022;
654
+ border-color: #0d9488;
655
+ }
656
+ .launch .dot {
657
+ width: 8px;
658
+ height: 8px;
659
+ border-radius: 50%;
660
+ background: currentColor;
661
+ opacity: 0.85;
662
+ }
663
+ </style>
@@ -0,0 +1,15 @@
1
+ export default Devtools;
2
+ type Devtools = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const Devtools: import("svelte").Component<{
7
+ csrTrue?: boolean;
8
+ startOpen?: boolean;
9
+ hide?: () => void;
10
+ }, {}, "">;
11
+ type $$ComponentProps = {
12
+ csrTrue?: boolean;
13
+ startOpen?: boolean;
14
+ hide?: () => void;
15
+ };