@oddsquad/vite-plugin-lit 0.2.3 → 0.4.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 (240) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/LICENSE +28 -0
  3. package/README.md +157 -0
  4. package/bin.mjs +154 -0
  5. package/client.d.ts +61 -0
  6. package/dist/client/assets/browser-agent-rpc-BXhoSh1z-CdtPhBL-.js +69 -0
  7. package/dist/client/assets/browser-agent-rpc-BXhoSh1z-CdtPhBL-.js.map +1 -0
  8. package/dist/client/assets/flow-DK8vmxvN.js +745 -0
  9. package/dist/client/assets/flow-DK8vmxvN.js.map +1 -0
  10. package/dist/client/assets/index-DVe6M4HV.js +10614 -0
  11. package/dist/client/assets/index-DVe6M4HV.js.map +1 -0
  12. package/{panel → dist/client}/index.html +1 -0
  13. package/index.d.ts.map +1 -1
  14. package/index.js +2 -2
  15. package/index.js.map +1 -1
  16. package/lib/color-scheme.d.ts.map +1 -1
  17. package/lib/color-scheme.js +17 -21
  18. package/lib/color-scheme.js.map +1 -1
  19. package/lib/devframe/definition.d.ts +45 -0
  20. package/lib/devframe/definition.d.ts.map +1 -0
  21. package/lib/devframe/definition.js +547 -0
  22. package/lib/devframe/definition.js.map +1 -0
  23. package/lib/devframe/icon.d.ts +8 -0
  24. package/lib/devframe/icon.d.ts.map +1 -0
  25. package/lib/devframe/icon.js +21 -0
  26. package/lib/devframe/icon.js.map +1 -0
  27. package/lib/devframe/paths.d.ts +12 -0
  28. package/lib/devframe/paths.d.ts.map +1 -0
  29. package/lib/devframe/paths.js +38 -0
  30. package/lib/devframe/paths.js.map +1 -0
  31. package/lib/devframe/protocol.d.ts +218 -0
  32. package/lib/devframe/protocol.d.ts.map +1 -0
  33. package/lib/devframe/protocol.js +77 -0
  34. package/lib/devframe/protocol.js.map +1 -0
  35. package/lib/devframe/source.d.ts +49 -0
  36. package/lib/devframe/source.d.ts.map +1 -0
  37. package/lib/devframe/source.js +23 -0
  38. package/lib/devframe/source.js.map +1 -0
  39. package/lib/devframe/vite.d.ts +116 -0
  40. package/lib/devframe/vite.d.ts.map +1 -0
  41. package/lib/devframe/vite.js +292 -0
  42. package/lib/devframe/vite.js.map +1 -0
  43. package/lib/http.d.ts +29 -0
  44. package/lib/http.d.ts.map +1 -0
  45. package/lib/http.js +35 -0
  46. package/lib/http.js.map +1 -0
  47. package/lib/icons.d.ts +2 -0
  48. package/lib/icons.d.ts.map +1 -1
  49. package/lib/icons.js +10 -4
  50. package/lib/icons.js.map +1 -1
  51. package/lib/options.d.ts +131 -0
  52. package/lib/options.d.ts.map +1 -0
  53. package/lib/options.js +128 -0
  54. package/lib/options.js.map +1 -0
  55. package/lib/plugin.d.ts +6 -137
  56. package/lib/plugin.d.ts.map +1 -1
  57. package/lib/plugin.js +280 -677
  58. package/lib/plugin.js.map +1 -1
  59. package/lib/plugins/css-literals.d.ts +15 -0
  60. package/lib/plugins/css-literals.d.ts.map +1 -0
  61. package/lib/plugins/css-literals.js +154 -0
  62. package/lib/plugins/css-literals.js.map +1 -0
  63. package/lib/plugins/css-queries.d.ts +20 -0
  64. package/lib/plugins/css-queries.d.ts.map +1 -0
  65. package/lib/plugins/css-queries.js +139 -0
  66. package/lib/plugins/css-queries.js.map +1 -0
  67. package/lib/plugins/open-in-editor.d.ts +16 -0
  68. package/lib/plugins/open-in-editor.d.ts.map +1 -0
  69. package/lib/plugins/open-in-editor.js +91 -0
  70. package/lib/plugins/open-in-editor.js.map +1 -0
  71. package/lib/plugins/shared.d.ts +13 -0
  72. package/lib/plugins/shared.d.ts.map +1 -0
  73. package/lib/plugins/shared.js +23 -0
  74. package/lib/plugins/shared.js.map +1 -0
  75. package/lib/plugins/timeline-virtual.d.ts +19 -0
  76. package/lib/plugins/timeline-virtual.d.ts.map +1 -0
  77. package/lib/plugins/timeline-virtual.js +53 -0
  78. package/lib/plugins/timeline-virtual.js.map +1 -0
  79. package/lib/runtime/css.d.ts +2 -2
  80. package/lib/runtime/css.d.ts.map +1 -1
  81. package/lib/runtime/css.js +41 -34
  82. package/lib/runtime/css.js.map +1 -1
  83. package/lib/runtime/edge-panel.d.ts.map +1 -1
  84. package/lib/runtime/edge-panel.js +82 -91
  85. package/lib/runtime/edge-panel.js.map +1 -1
  86. package/lib/runtime/fonts.d.ts.map +1 -1
  87. package/lib/runtime/fonts.js +1 -1
  88. package/lib/runtime/fonts.js.map +1 -1
  89. package/lib/runtime/indicator.d.ts.map +1 -1
  90. package/lib/runtime/indicator.js +71 -74
  91. package/lib/runtime/indicator.js.map +1 -1
  92. package/lib/runtime/inspector/collect.d.ts.map +1 -1
  93. package/lib/runtime/inspector/collect.js +83 -90
  94. package/lib/runtime/inspector/collect.js.map +1 -1
  95. package/lib/runtime/inspector/highlight.d.ts +16 -0
  96. package/lib/runtime/inspector/highlight.d.ts.map +1 -0
  97. package/lib/runtime/inspector/highlight.js +66 -0
  98. package/lib/runtime/inspector/highlight.js.map +1 -0
  99. package/lib/runtime/inspector/install.d.ts.map +1 -1
  100. package/lib/runtime/inspector/install.js +230 -205
  101. package/lib/runtime/inspector/install.js.map +1 -1
  102. package/lib/runtime/inspector/serialize.d.ts.map +1 -1
  103. package/lib/runtime/inspector/serialize.js +86 -107
  104. package/lib/runtime/inspector/serialize.js.map +1 -1
  105. package/lib/runtime/intern.d.ts.map +1 -1
  106. package/lib/runtime/intern.js +31 -29
  107. package/lib/runtime/intern.js.map +1 -1
  108. package/lib/runtime/overrides.d.ts.map +1 -1
  109. package/lib/runtime/overrides.js +14 -13
  110. package/lib/runtime/overrides.js.map +1 -1
  111. package/lib/runtime/patch.d.ts +8 -0
  112. package/lib/runtime/patch.d.ts.map +1 -1
  113. package/lib/runtime/patch.js +321 -282
  114. package/lib/runtime/patch.js.map +1 -1
  115. package/lib/runtime/source-meta.d.ts.map +1 -1
  116. package/lib/runtime/source-meta.js +1 -1
  117. package/lib/runtime/source-meta.js.map +1 -1
  118. package/lib/runtime/source-overlay/editors.d.ts.map +1 -1
  119. package/lib/runtime/source-overlay/editors.js +27 -27
  120. package/lib/runtime/source-overlay/editors.js.map +1 -1
  121. package/lib/runtime/source-overlay/mask-path.d.ts.map +1 -1
  122. package/lib/runtime/source-overlay/mask-path.js +15 -15
  123. package/lib/runtime/source-overlay/mask-path.js.map +1 -1
  124. package/lib/runtime/source-overlay/overlay-element.d.ts.map +1 -1
  125. package/lib/runtime/source-overlay/overlay-element.js +359 -355
  126. package/lib/runtime/source-overlay/overlay-element.js.map +1 -1
  127. package/lib/runtime/source-overlay/source-host.d.ts.map +1 -1
  128. package/lib/runtime/source-overlay/source-host.js +64 -71
  129. package/lib/runtime/source-overlay/source-host.js.map +1 -1
  130. package/lib/runtime/source-overlay/template.d.ts.map +1 -1
  131. package/lib/runtime/source-overlay/template.js +3 -3
  132. package/lib/runtime/source-overlay/template.js.map +1 -1
  133. package/lib/runtime/source-overlay.d.ts.map +1 -1
  134. package/lib/runtime/source-overlay.js +5 -2
  135. package/lib/runtime/source-overlay.js.map +1 -1
  136. package/lib/runtime/timeline/clock.d.ts.map +1 -1
  137. package/lib/runtime/timeline/clock.js +2 -2
  138. package/lib/runtime/timeline/clock.js.map +1 -1
  139. package/lib/runtime/timeline/flash.d.ts +39 -0
  140. package/lib/runtime/timeline/flash.d.ts.map +1 -0
  141. package/lib/runtime/timeline/flash.js +169 -0
  142. package/lib/runtime/timeline/flash.js.map +1 -0
  143. package/lib/runtime/timeline/identity.d.ts.map +1 -1
  144. package/lib/runtime/timeline/identity.js +27 -27
  145. package/lib/runtime/timeline/identity.js.map +1 -1
  146. package/lib/runtime/timeline/input.d.ts.map +1 -1
  147. package/lib/runtime/timeline/input.js +54 -67
  148. package/lib/runtime/timeline/input.js.map +1 -1
  149. package/lib/runtime/timeline/install.d.ts.map +1 -1
  150. package/lib/runtime/timeline/install.js +47 -33
  151. package/lib/runtime/timeline/install.js.map +1 -1
  152. package/lib/runtime/timeline/lifecycle.d.ts +8 -0
  153. package/lib/runtime/timeline/lifecycle.d.ts.map +1 -1
  154. package/lib/runtime/timeline/lifecycle.js +149 -133
  155. package/lib/runtime/timeline/lifecycle.js.map +1 -1
  156. package/lib/runtime/timeline/public-api.d.ts +2 -2
  157. package/lib/runtime/timeline/public-api.d.ts.map +1 -1
  158. package/lib/runtime/timeline/public-api.js +11 -11
  159. package/lib/runtime/timeline/public-api.js.map +1 -1
  160. package/lib/runtime/timeline/render.d.ts +15 -6
  161. package/lib/runtime/timeline/render.d.ts.map +1 -1
  162. package/lib/runtime/timeline/render.js +212 -78
  163. package/lib/runtime/timeline/render.js.map +1 -1
  164. package/lib/runtime/timeline/transport.d.ts.map +1 -1
  165. package/lib/runtime/timeline/transport.js +39 -43
  166. package/lib/runtime/timeline/transport.js.map +1 -1
  167. package/lib/segmented-tabs.d.ts +3 -1
  168. package/lib/segmented-tabs.d.ts.map +1 -1
  169. package/lib/segmented-tabs.js +136 -107
  170. package/lib/segmented-tabs.js.map +1 -1
  171. package/lib/snapshot.d.ts +28 -0
  172. package/lib/snapshot.d.ts.map +1 -0
  173. package/lib/snapshot.js +57 -0
  174. package/lib/snapshot.js.map +1 -0
  175. package/lib/source-meta.d.ts.map +1 -1
  176. package/lib/source-meta.js +176 -162
  177. package/lib/source-meta.js.map +1 -1
  178. package/lib/timeline/derive.d.ts +152 -0
  179. package/lib/timeline/derive.d.ts.map +1 -0
  180. package/lib/timeline/derive.js +242 -0
  181. package/lib/timeline/derive.js.map +1 -0
  182. package/lib/tokens.d.ts.map +1 -1
  183. package/lib/tokens.js +17 -18
  184. package/lib/tokens.js.map +1 -1
  185. package/lib/transform.d.ts.map +1 -1
  186. package/lib/transform.js +62 -57
  187. package/lib/transform.js.map +1 -1
  188. package/lib/types.d.ts.map +1 -1
  189. package/lib/types.js +1 -1
  190. package/lib/types.js.map +1 -1
  191. package/lib/wrap-table.d.ts.map +1 -1
  192. package/lib/wrap-table.js +19 -17
  193. package/lib/wrap-table.js.map +1 -1
  194. package/package.json +80 -13
  195. package/types/hmr-incompatibility.d.ts +59 -0
  196. package/types/hmr-incompatibility.d.ts.map +1 -0
  197. package/types/hmr-incompatibility.js +30 -0
  198. package/types/hmr-incompatibility.js.map +1 -0
  199. package/types/in-page.d.ts +49 -0
  200. package/types/in-page.d.ts.map +1 -0
  201. package/types/in-page.js +33 -0
  202. package/types/in-page.js.map +1 -0
  203. package/types/inspector.d.ts.map +1 -1
  204. package/types/inspector.js.map +1 -1
  205. package/types/snapshot.d.ts +44 -0
  206. package/types/snapshot.d.ts.map +1 -0
  207. package/types/snapshot.js +7 -0
  208. package/types/snapshot.js.map +1 -0
  209. package/types/timeline.d.ts +23 -1
  210. package/types/timeline.d.ts.map +1 -1
  211. package/types/timeline.js +2 -0
  212. package/types/timeline.js.map +1 -1
  213. package/lib/timeline-plugin.d.ts +0 -72
  214. package/lib/timeline-plugin.d.ts.map +0 -1
  215. package/lib/timeline-plugin.js +0 -376
  216. package/lib/timeline-plugin.js.map +0 -1
  217. package/panel/components-view.d.ts +0 -60
  218. package/panel/components-view.d.ts.map +0 -1
  219. package/panel/components-view.js +0 -587
  220. package/panel/components-view.js.map +0 -1
  221. package/panel/devtools-settings.d.ts +0 -41
  222. package/panel/devtools-settings.d.ts.map +0 -1
  223. package/panel/devtools-settings.js +0 -463
  224. package/panel/devtools-settings.js.map +0 -1
  225. package/panel/lit-devtools-panel.d.ts +0 -37
  226. package/panel/lit-devtools-panel.d.ts.map +0 -1
  227. package/panel/lit-devtools-panel.js +0 -185
  228. package/panel/lit-devtools-panel.js.map +0 -1
  229. package/panel/timeline-event-list.d.ts +0 -43
  230. package/panel/timeline-event-list.d.ts.map +0 -1
  231. package/panel/timeline-event-list.js +0 -416
  232. package/panel/timeline-event-list.js.map +0 -1
  233. package/panel/timeline-layers.d.ts +0 -25
  234. package/panel/timeline-layers.d.ts.map +0 -1
  235. package/panel/timeline-layers.js +0 -96
  236. package/panel/timeline-layers.js.map +0 -1
  237. package/panel/timeline-view.d.ts +0 -38
  238. package/panel/timeline-view.d.ts.map +0 -1
  239. package/panel/timeline-view.js +0 -234
  240. package/panel/timeline-view.js.map +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,112 @@
1
+ # Changelog
2
+
3
+ Notable changes per release. Versions before 0.3.0 predate this file; see the
4
+ git history for those.
5
+
6
+ ## 0.4.0 — 2026-09-29
7
+
8
+ A maintenance release. The timeline panel stays fast with a full buffer, the
9
+ live inspector stops walking the whole page, and several failures that used to
10
+ pass silently now say so. Releases are published from CI now.
11
+
12
+ ### Added
13
+
14
+ - **Verbose render layer.** lit-html's per-part debug events (template
15
+ instantiation, `set part`, each binding commit) go to a new "Lit Render
16
+ (verbose)" layer. It is off by default, because it fires once per binding on
17
+ every render. Values are summarized as strings (`string:"…"`, `node:<li>`,
18
+ `template`, `function:onClick`) and never passed through.
19
+ - **Releases from a version tag.** Pushing a `v*` tag runs the CI gate, checks
20
+ the tag against `package.json` and `CHANGELOG.md`, publishes to npm over
21
+ trusted publishing (OIDC), and creates the GitHub Release from the changelog
22
+ section. Prereleases go to the `next` dist-tag.
23
+
24
+ ### Changed
25
+
26
+ - **The timeline list renders only the visible rows**, through
27
+ `@lit-labs/virtualizer`. A 2500-event recording keeps about 75 rows in the
28
+ DOM instead of one per event. Filtering reruns only when the events or
29
+ filters change.
30
+ - **Live inspector mode no longer re-walks the page on every mutation.**
31
+ Batches with no custom element or shadow root in them are dropped, and
32
+ relevant batches walk only the subtrees they added.
33
+ - **The docs site was rewritten** in plain language, with a tutorial, task-sized
34
+ DevTools guides, a troubleshooting page, screenshots in both themes, and the
35
+ Lit Design System's colours and type.
36
+
37
+ ### Fixed
38
+
39
+ - The source overlay's default hotkey is Ctrl+Shift+S, as documented. The
40
+ runtime used to fall back to `e`, which also collided with the DevTools pick
41
+ command.
42
+ - The source overlay removes its HMR listeners when it disconnects. Moving the
43
+ element used to register the toggle twice, so the Vite DevTools overlay
44
+ command did nothing, and a removed overlay kept reacting.
45
+ - `urlSheet()` and `?css-sheet` warn when a stylesheet fails to load, and treat
46
+ non-ok responses as failures. A 404 used to hand Vite's fallback page to the
47
+ sheet as CSS, with a clean console. The sheet keeps its last good CSS.
48
+ - An unknown `LIT_PLUGIN_HMR_ON_INCOMPATIBLE` value logs a warning and falls
49
+ back to the default, instead of quietly acting as `reload`.
50
+ - The Lightning CSS pass over `css` literals finds them by parsing the module,
51
+ so `css`-tagged text inside a string, a comment, or another template literal
52
+ is no longer rewritten.
53
+
54
+ ## 0.3.0 — 2026-09-17
55
+
56
+ The DevTools half of the plugin was rebuilt on
57
+ [devframe](https://www.npmjs.com/package/devframe), and most of what follows
58
+ falls out of that: the panel is no longer tied to Vite DevTools, and the same
59
+ data it shows is reachable by a coding agent over MCP.
60
+
61
+ ### Added
62
+
63
+ - **The panel runs on devframe.** The bespoke transport is gone. The panel
64
+ works docked in Vite DevTools, in its own tab, or standalone from the CLI.
65
+ - **`lit-devtools` CLI.** `lit-devtools dev` serves the panel without a Vite
66
+ dev server of its own; `lit-devtools mcp` exposes the panel's data to an MCP
67
+ client over stdio, discovering a running dev server on its own.
68
+ - **MCP tools for agents** — list the component tree, read recent timeline
69
+ events, start and stop a recording, and read why a module fell back to a full
70
+ reload instead of a hot patch.
71
+ - **Updates tab.** The timeline reads as an explainer for a single update:
72
+ what changed, which components re-rendered, and what it cost.
73
+ - **Session export.** A recorded session can be written out as a static copy of
74
+ the panel — shareable, no dev server needed to open it.
75
+ - **Deep links.** Panel views are addressable, so a link opens the tab, the
76
+ selected element, or an exported snapshot at the right place.
77
+ - **Flash on update.** An opt-in fading outline over every element that
78
+ finishes an update, with an optional ramp colouring it by updates per second.
79
+ - **Ambient types for `virtual:lit-plugin/timeline`**, shipped in
80
+ `@oddsquad/vite-plugin-lit/client` alongside the CSS query declarations.
81
+ Following the custom-layers guide no longer means silencing `TS2307`.
82
+ - **Documentation site** at <https://oddcelot.github.io/vite-plugin-lit/>, and
83
+ a CI workflow that type checks, lints, tests and builds every push.
84
+
85
+ ### Changed
86
+
87
+ - Panel settings persist per developer through devframe's settings store
88
+ instead of per browser, so they follow you across browsers and survive
89
+ clearing site data. `localStorage` stays as the page's boot-time cache.
90
+ - Source links open through the shared open-in-editor service and resolve
91
+ against the Vite root.
92
+ - The docked panel draws its hover outline over a direct page channel rather
93
+ than routing through the dev server.
94
+ - Tooling moved to Vite+ (oxfmt, oxlint, tsgolint); dependencies updated to
95
+ TypeScript 7, Vite 8.3, es-module-lexer 3 and magic-string 1.
96
+ - The README is a front door now; the long-form guides live on the docs site.
97
+
98
+ ### Fixed
99
+
100
+ - **`virtual:lit-plugin/timeline` broke `vite build`.** Both hooks lived on the
101
+ serve-only plugin, so nothing claimed the specifier during a build and the
102
+ bundler failed on a module the developer never wrote. It now resolves in dev
103
+ and build alike, stubbing itself out in any build.
104
+ - The dev-server endpoints reject cross-site requests.
105
+ - Source metadata is injected in the component class's own scope, so it can no
106
+ longer reference a class that isn't in scope.
107
+ - The DevTools element watch survives a hot patch; `updated()` is resolved
108
+ lazily rather than captured once.
109
+ - Recording state is synced to a page runtime when it connects, so a reload
110
+ mid-recording keeps recording.
111
+ - `recent-events` works when called with no arguments, and the documented MCP
112
+ endpoint path matches the one the server serves.
package/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2017 Google LLC. All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # @oddsquad/vite-plugin-lit
2
+
3
+ A Vite plugin for Lit projects with true HMR, CSS delivery helpers for shadow
4
+ roots, and a DevTools timeline.
5
+
6
+ **[Documentation →](https://oddcelot.github.io/vite-plugin-lit/)**
7
+
8
+ Based on `@lit-labs/vite-plugin-lit` (formerly `@lit-labs/vite-hmr`) from the
9
+ [lit monorepo](https://github.com/lit/lit), which is kept checked out as a
10
+ read-only [submodule](./lit) here for reference and opt-in canary testing
11
+ against lit `main`.
12
+
13
+ ## Why
14
+
15
+ lit-html decides "is this the same template?" by **object identity** of the
16
+ `TemplateStringsArray`, not by content. Vite HMR re-executes an edited module,
17
+ so a one-character edit rebuilds the component's entire subtree and takes
18
+ focus, scroll, input state, and child `@state` with it.
19
+
20
+ This plugin fixes that with two cooperating mechanisms:
21
+
22
+ 1. **Template-strings interning** — the `html`/`svg`/`mathml`/`css` tags are
23
+ wrapped in dev so strings arrays are canonicalized by content, and only the
24
+ edited template rebuilds its part of the DOM.
25
+ 2. **In-place class patching** — the duplicate `customElements.define` from the
26
+ re-executed module patches the originally-registered class in place, then
27
+ restores reactive property values through the new accessors.
28
+
29
+ [How it works →](https://oddcelot.github.io/vite-plugin-lit/concepts/how-hmr-works/)
30
+
31
+ ## Usage
32
+
33
+ ```ts
34
+ // vite.config.ts
35
+ import {defineConfig} from 'vite';
36
+ import {litPlugin} from '@oddsquad/vite-plugin-lit';
37
+
38
+ export default defineConfig({
39
+ plugins: [litPlugin()],
40
+ });
41
+ ```
42
+
43
+ The HMR feature only applies to the dev server (`apply: 'serve'`); production
44
+ builds are untouched. The CSS queries (`?hmr-url`, `?css-sheet`) and Lightning
45
+ CSS literal processing apply in both dev and build.
46
+
47
+ ## Features
48
+
49
+ - **[HMR that keeps state](https://oddcelot.github.io/vite-plugin-lit/guides/hmr/)**
50
+ — what survives an edit, and what falls back to a reload.
51
+ - **[Stylesheets at scale](https://oddcelot.github.io/vite-plugin-lit/guides/stylesheets/)**
52
+ — one shared `CSSStyleSheet` for thousands of shadow roots, hot-swapped
53
+ without re-rendering, from a bare `?css-sheet` import.
54
+ - **[DevTools timeline](https://oddcelot.github.io/vite-plugin-lit/guides/devtools/)**
55
+ — a layered event recorder and live component inspector inside Vite DevTools.
56
+ - **[Source overlay](https://oddcelot.github.io/vite-plugin-lit/guides/source-overlay/)**
57
+ — click any element in the page to open its source in your editor.
58
+ - **[Agent access](#coding-agents-mcp)** — the same component tree and timeline
59
+ as MCP tools, for coding agents.
60
+ - **[Ecosystem support](https://oddcelot.github.io/vite-plugin-lit/guides/ecosystem/)**
61
+ — signals, context, tasks, and the virtualizer across a patch.
62
+
63
+ Reference:
64
+ [options](https://oddcelot.github.io/vite-plugin-lit/reference/options/) ·
65
+ [environment variables](https://oddcelot.github.io/vite-plugin-lit/reference/environment-variables/) ·
66
+ [import queries](https://oddcelot.github.io/vite-plugin-lit/reference/import-queries/) ·
67
+ [runtime API](https://oddcelot.github.io/vite-plugin-lit/reference/runtime-api/) ·
68
+ [CLI](https://oddcelot.github.io/vite-plugin-lit/reference/cli/) ·
69
+ [limitations](https://oddcelot.github.io/vite-plugin-lit/reference/limitations/) ·
70
+ [compatibility](https://oddcelot.github.io/vite-plugin-lit/reference/compatibility/) ·
71
+ [benchmarks](https://oddcelot.github.io/vite-plugin-lit/reference/benchmarks/) ·
72
+ [troubleshooting](https://oddcelot.github.io/vite-plugin-lit/guides/troubleshooting/)
73
+
74
+ ## Coding agents (MCP)
75
+
76
+ The DevTools panel's data is also exposed as MCP tools — `lit_list-components`,
77
+ `lit_component-details`, `lit_update-summary`, `lit_recent-events`,
78
+ `lit_get-meta`, `lit_hmr-incompatibilities`, `lit_set-recording` — so an agent
79
+ can read the live component tree and timeline instead of guessing from source.
80
+
81
+ These answer only while a Vite dev server with DevTools is **running**. There
82
+ is no stored data and nothing to go stale: with the server down an agent gets
83
+ a connection error, not an empty answer.
84
+
85
+ Point an MCP client at it either way:
86
+
87
+ ```sh
88
+ # Auto-discovers every running dev server on this machine.
89
+ claude mcp add lit-devtools -- npx -y @oddsquad/vite-plugin-lit mcp
90
+
91
+ # Or dial one server directly, if you'd rather pin the port.
92
+ claude mcp add lit-devtools -- npx -y mcp-remote http://localhost:5179/__devtools/__mcp
93
+ ```
94
+
95
+ The equivalent `claude_desktop_config.json` / `mcp.json` entry:
96
+
97
+ ```json
98
+ {
99
+ "mcpServers": {
100
+ "lit-devtools": {
101
+ "command": "npx",
102
+ "args": ["-y", "@oddsquad/vite-plugin-lit", "mcp"]
103
+ }
104
+ }
105
+ }
106
+ ```
107
+
108
+ The two differ in how the server is found, not in what it can do:
109
+
110
+ - **`... vite-plugin-lit mcp`** runs devframe's connector over stdio. The
111
+ plugin publishes each running dev server to devframe's instance registry,
112
+ and the connector lists them through two gateway tools
113
+ (`devframe_connect_list-instances`, `devframe_connect_call-tool`) — so one
114
+ entry covers every project you have running, with no port to keep in sync.
115
+ Being a shared connector, it surfaces every devframe on the machine, not
116
+ only this plugin's tools.
117
+ - **`mcp-remote <url>`** bridges stdio to one fixed URL and depends on no
118
+ discovery at all. Reach for it if registry discovery is unavailable — the
119
+ plugin logs a warning saying so at startup, naming the reason.
120
+
121
+ The CLI also has `lit-devtools dev`, a standalone devframe server with no page
122
+ attached. It exists to prove the panel definition runs without Vite (a
123
+ framework-neutrality harness, and the groundwork for non-Vite adapters); it
124
+ cannot show a real component tree, so it is not a way to inspect an app
125
+ without a dev server.
126
+
127
+ ## Playground
128
+
129
+ A manually inspectable fixture app (also the source for the e2e fixtures), and
130
+ self-contained enough to open on
131
+ [StackBlitz](https://stackblitz.com/github/oddcelot/vite-plugin-lit/tree/main/playground)
132
+ straight from the repo URL. Run it locally from the repo root:
133
+
134
+ ```sh
135
+ pnpm dev # builds the plugin, then serves http://localhost:5179
136
+ ```
137
+
138
+ ## Development
139
+
140
+ Requires Node 26 and pnpm 12 (`corepack enable` picks up the pinned version).
141
+ The published package itself runs on Node 20 or newer.
142
+
143
+ ```sh
144
+ pnpm install # workspace: root package + playground + docs
145
+ pnpm build # tsc + panel assets -> ./lib, ./panel, ./index.js
146
+ pnpm exec vp check # format, lint, type check
147
+ pnpm test # unit + e2e
148
+ pnpm docs:dev # the documentation site
149
+ ```
150
+
151
+ More in
152
+ [Development](https://oddcelot.github.io/vite-plugin-lit/contributing/development/)
153
+ and [Testing](https://oddcelot.github.io/vite-plugin-lit/contributing/testing/).
154
+
155
+ ## License
156
+
157
+ BSD-3-Clause. See [LICENSE](./LICENSE).
package/bin.mjs ADDED
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Google LLC
5
+ * SPDX-License-Identifier: BSD-3-Clause
6
+ */
7
+
8
+ /**
9
+ * `lit-devtools` — the package's command line.
10
+ *
11
+ * Built on a bare `cac()` instance rather than devframe's `createCac()`
12
+ * adapter, deliberately. Two things make the wrapper the wrong fit here:
13
+ *
14
+ * 1. Its `mcp` subcommand runs `createMcpServer(definition, ...)`, which
15
+ * executes the definition's `setup()` fresh and in-process. This
16
+ * package's definition reads a `TimelineSource`, and a CLI has no page
17
+ * to attach — so that server would answer every component query with an
18
+ * empty list, forever, while looking perfectly healthy. An MCP server
19
+ * that is confidently wrong is worse than no MCP server. The `mcp`
20
+ * command below proxies an *already-running* dev server instead.
21
+ * 2. It registers `dev` as cac's default command (`[...args]`), not a named
22
+ * one, and cac resolves commands first-match-wins — so its built-in
23
+ * subcommands cannot be cleanly overridden after the fact, only spliced
24
+ * out of `cli.commands`. Hand-rolling is less code than fighting that.
25
+ *
26
+ * `cac` and `@devframes/agentic` are optional peers, imported dynamically
27
+ * and only on the path that needs them, so installing this package without
28
+ * them stays supported.
29
+ *
30
+ * @see plans/roadmap/03-cli-and-stdio-mcp.md
31
+ */
32
+
33
+ import process from 'node:process';
34
+ import {createLitDevframe} from './lib/devframe/definition.js';
35
+ import {createNullSource} from './lib/devframe/source.js';
36
+ import {PACKAGE_VERSION} from './lib/devframe/paths.js';
37
+
38
+ /**
39
+ * Standalone `dev` default port. Deliberately *not* the playground's 5179
40
+ * (`playground/vite.config.ts`): running `pnpm run dev` and `lit-devtools
41
+ * dev` against the same checkout is a normal thing to do while working on
42
+ * this package, and two servers fighting over one port is a worse first
43
+ * experience than a port nobody has to think about.
44
+ */
45
+ const DEFAULT_DEV_PORT = 5180;
46
+
47
+ /** Fail with a readable message instead of a module-resolution stack trace. */
48
+ const requirePeer = async (specifier, hint) => {
49
+ try {
50
+ return await import(specifier);
51
+ } catch {
52
+ console.error(
53
+ `[lit-devtools] this command needs the optional peer "${hint}". ` +
54
+ `Install it and retry:\n\n npm install -D ${hint}\n`
55
+ );
56
+ process.exit(1);
57
+ }
58
+ };
59
+
60
+ const main = async () => {
61
+ const {default: cac} = await requirePeer('cac', 'cac');
62
+ const cli = cac('lit-devtools');
63
+
64
+ cli
65
+ .command('dev', 'Start a standalone devframe dev server (no page attached)')
66
+ .option('--port <port>', 'Port to listen on', {default: DEFAULT_DEV_PORT})
67
+ .option('--host <host>', 'Host to bind to', {default: 'localhost'})
68
+ .option('--open', 'Open the browser on start')
69
+ .action(async (flags) => {
70
+ const {createDevServer} = await import('devframe/adapters/dev');
71
+ // A standalone server has no Vite and no page, so the component tree
72
+ // is empty by construction. This command is a framework-neutrality
73
+ // harness for the definition, not a way to inspect a real app --
74
+ // for that, run your Vite dev server with DevTools enabled.
75
+ await createDevServer(
76
+ createLitDevframe({
77
+ source: createNullSource(),
78
+ version: PACKAGE_VERSION,
79
+ }),
80
+ {
81
+ host: flags.host,
82
+ port: Number(flags.port),
83
+ flags: {open: Boolean(flags.open)},
84
+ }
85
+ );
86
+ });
87
+
88
+ cli
89
+ .command('build', 'Explain how to export a static snapshot of a session')
90
+ .action(() => {
91
+ // Deliberately not implemented here. A snapshot worth attaching to an
92
+ // issue is a *recorded session*, and the session lives in the running
93
+ // dev server's memory -- a fresh CLI process has no page, no timeline
94
+ // and no component tree, so anything it could build would be an empty
95
+ // shell. The export therefore runs inside the dev server that holds
96
+ // the data; see plans/roadmap/08-static-snapshot.md.
97
+ console.error(
98
+ `[lit-devtools] A static snapshot is exported from a running ` +
99
+ `session, not from this CLI.\n` +
100
+ `Record what you want to report in the DevTools Lit panel, then ` +
101
+ `press "Export snapshot" in the Timeline tab. The dev server ` +
102
+ `writes a self-contained panel directory you can zip onto an issue.`
103
+ );
104
+ process.exit(1);
105
+ });
106
+
107
+ cli
108
+ .command('mcp', 'Start a stdio MCP server proxying running dev servers')
109
+ .option(
110
+ '--port <port>',
111
+ 'Also probe this port for an instance the registry does not list. ' +
112
+ 'Probes <origin>/__connection.json at the ROOT path only, so this ' +
113
+ 'finds a standalone "lit-devtools dev" server but NOT a ' +
114
+ 'Vite-hosted one (mounted under /__devtools/) -- for those, rely ' +
115
+ 'on registry discovery. Repeatable.'
116
+ )
117
+ .option(
118
+ '--token <token>',
119
+ 'Bearer token presented to each instance MCP route, for a dev server ' +
120
+ 'whose DevTools auth gate is enabled.'
121
+ )
122
+ .action(async (flags) => {
123
+ const {startConnectServer} = await requirePeer(
124
+ '@devframes/agentic/connect',
125
+ '@devframes/agentic'
126
+ );
127
+ // This is devframe's own generic connector: it discovers every
128
+ // devframe instance on the machine, not just this package's. Its two
129
+ // gateway tools (`devframe_connect_list-instances` /
130
+ // `devframe_connect_call-tool`) are how an agent reaches the `lit_*`
131
+ // tools, one hop in.
132
+ const ports = [flags.port]
133
+ .flat()
134
+ .filter((p) => p != null)
135
+ .map(Number)
136
+ .filter((p) => Number.isInteger(p) && p > 0);
137
+
138
+ await startConnectServer({
139
+ ...(ports.length > 0 ? {ports} : {}),
140
+ ...(flags.token ? {authToken: String(flags.token)} : {}),
141
+ });
142
+ });
143
+
144
+ cli.help();
145
+ cli.version(PACKAGE_VERSION);
146
+
147
+ cli.parse(process.argv, {run: false});
148
+ await cli.runMatchedCommand();
149
+ };
150
+
151
+ main().catch((error) => {
152
+ console.error(`[lit-devtools] ${error?.stack ?? error}`);
153
+ process.exit(1);
154
+ });
package/client.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ */
6
+
7
+ /**
8
+ * Ambient types for the import queries provided by the Lit Vite plugin.
9
+ *
10
+ * Reference via tsconfig (`"types": ["@oddsquad/vite-plugin-lit/client"]`) or
11
+ * `/// <reference types="@oddsquad/vite-plugin-lit/client" />`.
12
+ */
13
+
14
+ declare module '*.css?hmr-url' {
15
+ const href: string;
16
+ export default href;
17
+ }
18
+
19
+ declare module '*.css?css-sheet' {
20
+ const sheet: CSSStyleSheet;
21
+ export default sheet;
22
+ }
23
+
24
+ declare module 'virtual:lit-plugin/timeline' {
25
+ export interface TimelineLayer {
26
+ id: string;
27
+ label: string;
28
+ /** 0xRRGGBB */
29
+ color: number;
30
+ }
31
+
32
+ export interface TimelineEvent<TData = unknown> {
33
+ layerId: string;
34
+ /** Milliseconds since the current recording started. */
35
+ time: number;
36
+ data: TData;
37
+ title?: string;
38
+ subtitle?: string;
39
+ /** Pairs a start event with its matching end event in the same update group. */
40
+ groupId?: number | string;
41
+ logType?: 'default' | 'warning' | 'error';
42
+ /** Set by the plugin's built-in capture layers; not needed for custom events. */
43
+ meta?: {
44
+ elementId?: number;
45
+ tagName?: string;
46
+ source?: {file: string; line: number};
47
+ };
48
+ }
49
+
50
+ /**
51
+ * Emit a custom timeline event from app code. No-ops in production or
52
+ * when the `timeline` option is disabled.
53
+ */
54
+ export function addTimelineEvent(event: TimelineEvent): void;
55
+
56
+ /**
57
+ * Register a custom timeline layer and announce it to the panel.
58
+ * Idempotent — duplicate ids are ignored.
59
+ */
60
+ export function addTimelineLayer(layer: TimelineLayer): void;
61
+ }
@@ -0,0 +1,69 @@
1
+ import { n as onBrowserAgentToolsChanged, r as nanoid, t as listBrowserAgentTools } from "./index-DVe6M4HV.js";
2
+ //#region node_modules/.pnpm/devframe@1.0.0_@devframes+agentic@1.0.0_cac@7.0.0_srvx@1.0.5/node_modules/devframe/dist/browser-agent-rpc-BXhoSh1z.mjs
3
+ var CLIENT_ID_STORAGE_KEY = "devframe:client-id";
4
+ var memoryClientId;
5
+ /**
6
+ * This browser tab's stable client id: one nanoid per tab, persisted in
7
+ * `sessionStorage` so it survives page reloads and RPC reconnects. The node
8
+ * side uses it to tell connected tabs apart across reconnects (see #394).
9
+ *
10
+ * Tab duplication copies `sessionStorage`, so two tabs can briefly share an id
11
+ * until per-tab disambiguation lands with the wider tab-metadata work.
12
+ */
13
+ function resolveClientId(win = globalThis.window) {
14
+ try {
15
+ const storage = win?.sessionStorage;
16
+ if (storage) {
17
+ let id = storage.getItem(CLIENT_ID_STORAGE_KEY);
18
+ if (!id) {
19
+ id = nanoid();
20
+ storage.setItem(CLIENT_ID_STORAGE_KEY, id);
21
+ }
22
+ return id;
23
+ }
24
+ } catch {}
25
+ memoryClientId ??= nanoid();
26
+ return memoryClientId;
27
+ }
28
+ /** Mirror this document's browser-agent registry over its existing RPC connection. */
29
+ function setupBrowserAgentRpcBridge(rpc) {
30
+ rpc.client.register({
31
+ name: "devframe:agent:invoke-client-tool",
32
+ type: "action",
33
+ jsonSerializable: true,
34
+ handler: async (id, args) => {
35
+ const tool = listBrowserAgentTools().find((tool) => tool.id === id);
36
+ if (!tool) throw new Error(`[devframe/agent] browser tool "${id}" not found`);
37
+ return await tool.invoke(args);
38
+ }
39
+ });
40
+ let queued = false;
41
+ let disposed = false;
42
+ let lastSyncedCount = 0;
43
+ const sync = () => {
44
+ if (queued || disposed) return;
45
+ queued = true;
46
+ queueMicrotask(async () => {
47
+ queued = false;
48
+ if (disposed) return;
49
+ const manifests = listBrowserAgentTools().map(({ invoke: _, ...manifest }) => manifest);
50
+ if (manifests.length === 0 && lastSyncedCount === 0) return;
51
+ lastSyncedCount = manifests.length;
52
+ await rpc.callOptional("devframe:agent:sync-client-tools", resolveClientId(), manifests).catch(() => {});
53
+ });
54
+ };
55
+ const stopTools = onBrowserAgentToolsChanged(sync);
56
+ const stopConnection = rpc.events.on("connection:status", (status) => {
57
+ if (status === "connected") sync();
58
+ });
59
+ sync();
60
+ return () => {
61
+ disposed = true;
62
+ stopTools();
63
+ stopConnection();
64
+ };
65
+ }
66
+ //#endregion
67
+ export { setupBrowserAgentRpcBridge };
68
+
69
+ //# sourceMappingURL=browser-agent-rpc-BXhoSh1z-CdtPhBL-.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-agent-rpc-BXhoSh1z-CdtPhBL-.js","names":[],"sources":["../../../node_modules/.pnpm/devframe@1.0.0_@devframes+agentic@1.0.0_cac@7.0.0_srvx@1.0.5/node_modules/devframe/dist/browser-agent-rpc-BXhoSh1z.mjs"],"sourcesContent":["import { nanoid } from \"./utils/nanoid.mjs\";\nimport { n as onBrowserAgentToolsChanged, t as listBrowserAgentTools } from \"./browser-agent-Pu-FLk6I.mjs\";\n//#region src/client/client-id.ts\nconst CLIENT_ID_STORAGE_KEY = \"devframe:client-id\";\nlet memoryClientId;\n/**\n* This browser tab's stable client id: one nanoid per tab, persisted in\n* `sessionStorage` so it survives page reloads and RPC reconnects. The node\n* side uses it to tell connected tabs apart across reconnects (see #394).\n*\n* Tab duplication copies `sessionStorage`, so two tabs can briefly share an id\n* until per-tab disambiguation lands with the wider tab-metadata work.\n*/\nfunction resolveClientId(win = globalThis.window) {\n\ttry {\n\t\tconst storage = win?.sessionStorage;\n\t\tif (storage) {\n\t\t\tlet id = storage.getItem(CLIENT_ID_STORAGE_KEY);\n\t\t\tif (!id) {\n\t\t\t\tid = nanoid();\n\t\t\t\tstorage.setItem(CLIENT_ID_STORAGE_KEY, id);\n\t\t\t}\n\t\t\treturn id;\n\t\t}\n\t} catch {}\n\tmemoryClientId ??= nanoid();\n\treturn memoryClientId;\n}\n//#endregion\n//#region src/client/browser-agent-rpc.ts\n/** Mirror this document's browser-agent registry over its existing RPC connection. */\nfunction setupBrowserAgentRpcBridge(rpc) {\n\trpc.client.register({\n\t\tname: \"devframe:agent:invoke-client-tool\",\n\t\ttype: \"action\",\n\t\tjsonSerializable: true,\n\t\thandler: async (id, args) => {\n\t\t\tconst tool = listBrowserAgentTools().find((tool) => tool.id === id);\n\t\t\tif (!tool) throw new Error(`[devframe/agent] browser tool \"${id}\" not found`);\n\t\t\treturn await tool.invoke(args);\n\t\t}\n\t});\n\tlet queued = false;\n\tlet disposed = false;\n\tlet lastSyncedCount = 0;\n\tconst sync = () => {\n\t\tif (queued || disposed) return;\n\t\tqueued = true;\n\t\tqueueMicrotask(async () => {\n\t\t\tqueued = false;\n\t\t\tif (disposed) return;\n\t\t\tconst manifests = listBrowserAgentTools().map(({ invoke: _, ...manifest }) => manifest);\n\t\t\tif (manifests.length === 0 && lastSyncedCount === 0) return;\n\t\t\tlastSyncedCount = manifests.length;\n\t\t\tawait rpc.callOptional(\"devframe:agent:sync-client-tools\", resolveClientId(), manifests).catch(() => {});\n\t\t});\n\t};\n\tconst stopTools = onBrowserAgentToolsChanged(sync);\n\tconst stopConnection = rpc.events.on(\"connection:status\", (status) => {\n\t\tif (status === \"connected\") sync();\n\t});\n\tsync();\n\treturn () => {\n\t\tdisposed = true;\n\t\tstopTools();\n\t\tstopConnection();\n\t};\n}\n//#endregion\nexport { setupBrowserAgentRpcBridge };\n"],"x_google_ignoreList":[0],"mappings":";;AAGA,IAAM,wBAAwB;AAC9B,IAAI;;;;;;;;;AASJ,SAAS,gBAAgB,MAAM,WAAW,QAAQ;CACjD,IAAI;EACH,MAAM,UAAU,KAAK;EACrB,IAAI,SAAS;GACZ,IAAI,KAAK,QAAQ,QAAQ,qBAAqB;GAC9C,IAAI,CAAC,IAAI;IACR,KAAK,OAAO;IACZ,QAAQ,QAAQ,uBAAuB,EAAE;GAC1C;GACA,OAAO;EACR;CACD,QAAQ,CAAC;CACT,mBAAmB,OAAO;CAC1B,OAAO;AACR;;AAIA,SAAS,2BAA2B,KAAK;CACxC,IAAI,OAAO,SAAS;EACnB,MAAM;EACN,MAAM;EACN,kBAAkB;EAClB,SAAS,OAAO,IAAI,SAAS;GAC5B,MAAM,OAAO,sBAAsB,CAAC,CAAC,MAAM,SAAS,KAAK,OAAO,EAAE;GAClE,IAAI,CAAC,MAAM,MAAM,IAAI,MAAM,kCAAkC,GAAG,YAAY;GAC5E,OAAO,MAAM,KAAK,OAAO,IAAI;EAC9B;CACD,CAAC;CACD,IAAI,SAAS;CACb,IAAI,WAAW;CACf,IAAI,kBAAkB;CACtB,MAAM,aAAa;EAClB,IAAI,UAAU,UAAU;EACxB,SAAS;EACT,eAAe,YAAY;GAC1B,SAAS;GACT,IAAI,UAAU;GACd,MAAM,YAAY,sBAAsB,CAAC,CAAC,KAAK,EAAE,QAAQ,GAAG,GAAG,eAAe,QAAQ;GACtF,IAAI,UAAU,WAAW,KAAK,oBAAoB,GAAG;GACrD,kBAAkB,UAAU;GAC5B,MAAM,IAAI,aAAa,oCAAoC,gBAAgB,GAAG,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC;EACxG,CAAC;CACF;CACA,MAAM,YAAY,2BAA2B,IAAI;CACjD,MAAM,iBAAiB,IAAI,OAAO,GAAG,sBAAsB,WAAW;EACrE,IAAI,WAAW,aAAa,KAAK;CAClC,CAAC;CACD,KAAK;CACL,aAAa;EACZ,WAAW;EACX,UAAU;EACV,eAAe;CAChB;AACD"}