@oddsquad/vite-plugin-lit 0.2.3 → 0.3.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 (210) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +28 -0
  3. package/README.md +153 -0
  4. package/bin.mjs +154 -0
  5. package/client.d.ts +61 -0
  6. package/dist/client/assets/browser-agent-rpc-BXhoSh1z-BvWe4sTJ.js +69 -0
  7. package/dist/client/assets/browser-agent-rpc-BXhoSh1z-BvWe4sTJ.js.map +1 -0
  8. package/dist/client/assets/index-CFrVgNfS.js +9740 -0
  9. package/dist/client/assets/index-CFrVgNfS.js.map +1 -0
  10. package/{panel → dist/client}/index.html +1 -0
  11. package/index.d.ts.map +1 -1
  12. package/index.js +2 -2
  13. package/index.js.map +1 -1
  14. package/lib/color-scheme.d.ts.map +1 -1
  15. package/lib/color-scheme.js +17 -21
  16. package/lib/color-scheme.js.map +1 -1
  17. package/lib/devframe/definition.d.ts +45 -0
  18. package/lib/devframe/definition.d.ts.map +1 -0
  19. package/lib/devframe/definition.js +547 -0
  20. package/lib/devframe/definition.js.map +1 -0
  21. package/lib/devframe/icon.d.ts +8 -0
  22. package/lib/devframe/icon.d.ts.map +1 -0
  23. package/lib/devframe/icon.js +21 -0
  24. package/lib/devframe/icon.js.map +1 -0
  25. package/lib/devframe/paths.d.ts +12 -0
  26. package/lib/devframe/paths.d.ts.map +1 -0
  27. package/lib/devframe/paths.js +38 -0
  28. package/lib/devframe/paths.js.map +1 -0
  29. package/lib/devframe/protocol.d.ts +218 -0
  30. package/lib/devframe/protocol.d.ts.map +1 -0
  31. package/lib/devframe/protocol.js +76 -0
  32. package/lib/devframe/protocol.js.map +1 -0
  33. package/lib/devframe/source.d.ts +49 -0
  34. package/lib/devframe/source.d.ts.map +1 -0
  35. package/lib/devframe/source.js +23 -0
  36. package/lib/devframe/source.js.map +1 -0
  37. package/lib/devframe/vite.d.ts +116 -0
  38. package/lib/devframe/vite.d.ts.map +1 -0
  39. package/lib/devframe/vite.js +291 -0
  40. package/lib/devframe/vite.js.map +1 -0
  41. package/lib/http.d.ts +29 -0
  42. package/lib/http.d.ts.map +1 -0
  43. package/lib/http.js +35 -0
  44. package/lib/http.js.map +1 -0
  45. package/lib/icons.d.ts +2 -0
  46. package/lib/icons.d.ts.map +1 -1
  47. package/lib/icons.js +10 -4
  48. package/lib/icons.js.map +1 -1
  49. package/lib/plugin.d.ts +15 -4
  50. package/lib/plugin.d.ts.map +1 -1
  51. package/lib/plugin.js +640 -574
  52. package/lib/plugin.js.map +1 -1
  53. package/lib/runtime/css.d.ts +2 -2
  54. package/lib/runtime/css.d.ts.map +1 -1
  55. package/lib/runtime/css.js +34 -34
  56. package/lib/runtime/css.js.map +1 -1
  57. package/lib/runtime/edge-panel.d.ts.map +1 -1
  58. package/lib/runtime/edge-panel.js +82 -91
  59. package/lib/runtime/edge-panel.js.map +1 -1
  60. package/lib/runtime/fonts.d.ts.map +1 -1
  61. package/lib/runtime/fonts.js +1 -1
  62. package/lib/runtime/fonts.js.map +1 -1
  63. package/lib/runtime/indicator.d.ts.map +1 -1
  64. package/lib/runtime/indicator.js +71 -74
  65. package/lib/runtime/indicator.js.map +1 -1
  66. package/lib/runtime/inspector/collect.d.ts.map +1 -1
  67. package/lib/runtime/inspector/collect.js +83 -90
  68. package/lib/runtime/inspector/collect.js.map +1 -1
  69. package/lib/runtime/inspector/highlight.d.ts +16 -0
  70. package/lib/runtime/inspector/highlight.d.ts.map +1 -0
  71. package/lib/runtime/inspector/highlight.js +66 -0
  72. package/lib/runtime/inspector/highlight.js.map +1 -0
  73. package/lib/runtime/inspector/install.d.ts.map +1 -1
  74. package/lib/runtime/inspector/install.js +180 -205
  75. package/lib/runtime/inspector/install.js.map +1 -1
  76. package/lib/runtime/inspector/serialize.d.ts.map +1 -1
  77. package/lib/runtime/inspector/serialize.js +86 -107
  78. package/lib/runtime/inspector/serialize.js.map +1 -1
  79. package/lib/runtime/intern.d.ts.map +1 -1
  80. package/lib/runtime/intern.js +31 -29
  81. package/lib/runtime/intern.js.map +1 -1
  82. package/lib/runtime/overrides.d.ts.map +1 -1
  83. package/lib/runtime/overrides.js +14 -13
  84. package/lib/runtime/overrides.js.map +1 -1
  85. package/lib/runtime/patch.d.ts +8 -0
  86. package/lib/runtime/patch.d.ts.map +1 -1
  87. package/lib/runtime/patch.js +321 -282
  88. package/lib/runtime/patch.js.map +1 -1
  89. package/lib/runtime/source-meta.d.ts.map +1 -1
  90. package/lib/runtime/source-meta.js +1 -1
  91. package/lib/runtime/source-meta.js.map +1 -1
  92. package/lib/runtime/source-overlay/editors.d.ts.map +1 -1
  93. package/lib/runtime/source-overlay/editors.js +27 -27
  94. package/lib/runtime/source-overlay/editors.js.map +1 -1
  95. package/lib/runtime/source-overlay/mask-path.d.ts.map +1 -1
  96. package/lib/runtime/source-overlay/mask-path.js +15 -15
  97. package/lib/runtime/source-overlay/mask-path.js.map +1 -1
  98. package/lib/runtime/source-overlay/overlay-element.d.ts.map +1 -1
  99. package/lib/runtime/source-overlay/overlay-element.js +347 -356
  100. package/lib/runtime/source-overlay/overlay-element.js.map +1 -1
  101. package/lib/runtime/source-overlay/source-host.d.ts.map +1 -1
  102. package/lib/runtime/source-overlay/source-host.js +64 -71
  103. package/lib/runtime/source-overlay/source-host.js.map +1 -1
  104. package/lib/runtime/source-overlay/template.d.ts.map +1 -1
  105. package/lib/runtime/source-overlay/template.js +3 -3
  106. package/lib/runtime/source-overlay/template.js.map +1 -1
  107. package/lib/runtime/source-overlay.d.ts.map +1 -1
  108. package/lib/runtime/source-overlay.js +5 -2
  109. package/lib/runtime/source-overlay.js.map +1 -1
  110. package/lib/runtime/timeline/clock.d.ts.map +1 -1
  111. package/lib/runtime/timeline/clock.js +2 -2
  112. package/lib/runtime/timeline/clock.js.map +1 -1
  113. package/lib/runtime/timeline/flash.d.ts +39 -0
  114. package/lib/runtime/timeline/flash.d.ts.map +1 -0
  115. package/lib/runtime/timeline/flash.js +169 -0
  116. package/lib/runtime/timeline/flash.js.map +1 -0
  117. package/lib/runtime/timeline/identity.d.ts.map +1 -1
  118. package/lib/runtime/timeline/identity.js +27 -27
  119. package/lib/runtime/timeline/identity.js.map +1 -1
  120. package/lib/runtime/timeline/input.d.ts.map +1 -1
  121. package/lib/runtime/timeline/input.js +54 -67
  122. package/lib/runtime/timeline/input.js.map +1 -1
  123. package/lib/runtime/timeline/install.d.ts.map +1 -1
  124. package/lib/runtime/timeline/install.js +38 -29
  125. package/lib/runtime/timeline/install.js.map +1 -1
  126. package/lib/runtime/timeline/lifecycle.d.ts +8 -0
  127. package/lib/runtime/timeline/lifecycle.d.ts.map +1 -1
  128. package/lib/runtime/timeline/lifecycle.js +149 -133
  129. package/lib/runtime/timeline/lifecycle.js.map +1 -1
  130. package/lib/runtime/timeline/public-api.d.ts.map +1 -1
  131. package/lib/runtime/timeline/public-api.js +9 -9
  132. package/lib/runtime/timeline/public-api.js.map +1 -1
  133. package/lib/runtime/timeline/render.d.ts.map +1 -1
  134. package/lib/runtime/timeline/render.js +70 -75
  135. package/lib/runtime/timeline/render.js.map +1 -1
  136. package/lib/runtime/timeline/transport.d.ts.map +1 -1
  137. package/lib/runtime/timeline/transport.js +39 -43
  138. package/lib/runtime/timeline/transport.js.map +1 -1
  139. package/lib/segmented-tabs.d.ts +3 -1
  140. package/lib/segmented-tabs.d.ts.map +1 -1
  141. package/lib/segmented-tabs.js +136 -107
  142. package/lib/segmented-tabs.js.map +1 -1
  143. package/lib/snapshot.d.ts +28 -0
  144. package/lib/snapshot.d.ts.map +1 -0
  145. package/lib/snapshot.js +57 -0
  146. package/lib/snapshot.js.map +1 -0
  147. package/lib/source-meta.d.ts.map +1 -1
  148. package/lib/source-meta.js +176 -162
  149. package/lib/source-meta.js.map +1 -1
  150. package/lib/timeline/derive.d.ts +152 -0
  151. package/lib/timeline/derive.d.ts.map +1 -0
  152. package/lib/timeline/derive.js +242 -0
  153. package/lib/timeline/derive.js.map +1 -0
  154. package/lib/timeline-plugin.d.ts.map +1 -1
  155. package/lib/timeline-plugin.js +304 -321
  156. package/lib/timeline-plugin.js.map +1 -1
  157. package/lib/tokens.d.ts.map +1 -1
  158. package/lib/tokens.js +17 -18
  159. package/lib/tokens.js.map +1 -1
  160. package/lib/transform.d.ts.map +1 -1
  161. package/lib/transform.js +62 -57
  162. package/lib/transform.js.map +1 -1
  163. package/lib/types.d.ts.map +1 -1
  164. package/lib/types.js +1 -1
  165. package/lib/types.js.map +1 -1
  166. package/lib/wrap-table.d.ts.map +1 -1
  167. package/lib/wrap-table.js +19 -17
  168. package/lib/wrap-table.js.map +1 -1
  169. package/package.json +77 -13
  170. package/types/hmr-incompatibility.d.ts +59 -0
  171. package/types/hmr-incompatibility.d.ts.map +1 -0
  172. package/types/hmr-incompatibility.js +30 -0
  173. package/types/hmr-incompatibility.js.map +1 -0
  174. package/types/in-page.d.ts +49 -0
  175. package/types/in-page.d.ts.map +1 -0
  176. package/types/in-page.js +33 -0
  177. package/types/in-page.js.map +1 -0
  178. package/types/inspector.d.ts.map +1 -1
  179. package/types/inspector.js.map +1 -1
  180. package/types/snapshot.d.ts +44 -0
  181. package/types/snapshot.d.ts.map +1 -0
  182. package/types/snapshot.js +7 -0
  183. package/types/snapshot.js.map +1 -0
  184. package/types/timeline.d.ts +11 -0
  185. package/types/timeline.d.ts.map +1 -1
  186. package/types/timeline.js.map +1 -1
  187. package/panel/components-view.d.ts +0 -60
  188. package/panel/components-view.d.ts.map +0 -1
  189. package/panel/components-view.js +0 -587
  190. package/panel/components-view.js.map +0 -1
  191. package/panel/devtools-settings.d.ts +0 -41
  192. package/panel/devtools-settings.d.ts.map +0 -1
  193. package/panel/devtools-settings.js +0 -463
  194. package/panel/devtools-settings.js.map +0 -1
  195. package/panel/lit-devtools-panel.d.ts +0 -37
  196. package/panel/lit-devtools-panel.d.ts.map +0 -1
  197. package/panel/lit-devtools-panel.js +0 -185
  198. package/panel/lit-devtools-panel.js.map +0 -1
  199. package/panel/timeline-event-list.d.ts +0 -43
  200. package/panel/timeline-event-list.d.ts.map +0 -1
  201. package/panel/timeline-event-list.js +0 -416
  202. package/panel/timeline-event-list.js.map +0 -1
  203. package/panel/timeline-layers.d.ts +0 -25
  204. package/panel/timeline-layers.d.ts.map +0 -1
  205. package/panel/timeline-layers.js +0 -96
  206. package/panel/timeline-layers.js.map +0 -1
  207. package/panel/timeline-view.d.ts +0 -38
  208. package/panel/timeline-view.d.ts.map +0 -1
  209. package/panel/timeline-view.js +0 -234
  210. package/panel/timeline-view.js.map +0 -1
package/CHANGELOG.md ADDED
@@ -0,0 +1,64 @@
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.3.0 — 2026-09-17
7
+
8
+ The DevTools half of the plugin was rebuilt on
9
+ [devframe](https://www.npmjs.com/package/devframe), and most of what follows
10
+ falls out of that: the panel is no longer tied to Vite DevTools, and the same
11
+ data it shows is reachable by a coding agent over MCP.
12
+
13
+ ### Added
14
+
15
+ - **The panel runs on devframe.** The bespoke transport is gone. The panel
16
+ works docked in Vite DevTools, in its own tab, or standalone from the CLI.
17
+ - **`lit-devtools` CLI.** `lit-devtools dev` serves the panel without a Vite
18
+ dev server of its own; `lit-devtools mcp` exposes the panel's data to an MCP
19
+ client over stdio, discovering a running dev server on its own.
20
+ - **MCP tools for agents** — list the component tree, read recent timeline
21
+ events, start and stop a recording, and read why a module fell back to a full
22
+ reload instead of a hot patch.
23
+ - **Updates tab.** The timeline reads as an explainer for a single update:
24
+ what changed, which components re-rendered, and what it cost.
25
+ - **Session export.** A recorded session can be written out as a static copy of
26
+ the panel — shareable, no dev server needed to open it.
27
+ - **Deep links.** Panel views are addressable, so a link opens the tab, the
28
+ selected element, or an exported snapshot at the right place.
29
+ - **Flash on update.** An opt-in fading outline over every element that
30
+ finishes an update, with an optional ramp colouring it by updates per second.
31
+ - **Ambient types for `virtual:lit-plugin/timeline`**, shipped in
32
+ `@oddsquad/vite-plugin-lit/client` alongside the CSS query declarations.
33
+ Following the custom-layers guide no longer means silencing `TS2307`.
34
+ - **Documentation site** at <https://oddcelot.github.io/vite-plugin-lit/>, and
35
+ a CI workflow that type checks, lints, tests and builds every push.
36
+
37
+ ### Changed
38
+
39
+ - Panel settings persist per developer through devframe's settings store
40
+ instead of per browser, so they follow you across browsers and survive
41
+ clearing site data. `localStorage` stays as the page's boot-time cache.
42
+ - Source links open through the shared open-in-editor service and resolve
43
+ against the Vite root.
44
+ - The docked panel draws its hover outline over a direct page channel rather
45
+ than routing through the dev server.
46
+ - Tooling moved to Vite+ (oxfmt, oxlint, tsgolint); dependencies updated to
47
+ TypeScript 7, Vite 8.3, es-module-lexer 3 and magic-string 1.
48
+ - The README is a front door now; the long-form guides live on the docs site.
49
+
50
+ ### Fixed
51
+
52
+ - **`virtual:lit-plugin/timeline` broke `vite build`.** Both hooks lived on the
53
+ serve-only plugin, so nothing claimed the specifier during a build and the
54
+ bundler failed on a module the developer never wrote. It now resolves in dev
55
+ and build alike, stubbing itself out in any build.
56
+ - The dev-server endpoints reject cross-site requests.
57
+ - Source metadata is injected in the component class's own scope, so it can no
58
+ longer reference a class that isn't in scope.
59
+ - The DevTools element watch survives a hot patch; `updated()` is resolved
60
+ lazily rather than captured once.
61
+ - Recording state is synced to a page runtime when it connects, so a reload
62
+ mid-recording keeps recording.
63
+ - `recent-events` works when called with no arguments, and the documented MCP
64
+ 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,153 @@
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/getting-started/how-it-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-timeline/)**
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
+ [limitations](https://oddcelot.github.io/vite-plugin-lit/reference/limitations/) ·
69
+ [benchmarks](https://oddcelot.github.io/vite-plugin-lit/reference/benchmarks/)
70
+
71
+ ## Coding agents (MCP)
72
+
73
+ The DevTools panel's data is also exposed as MCP tools — `lit_list-components`,
74
+ `lit_component-details`, `lit_recent-events`, `lit_get-meta`,
75
+ `lit_hmr-incompatibilities`, `lit_set-recording` — so an agent can read the
76
+ live component tree and timeline instead of guessing from source.
77
+
78
+ These answer only while a Vite dev server with DevTools is **running**. There
79
+ is no stored data and nothing to go stale: with the server down an agent gets
80
+ a connection error, not an empty answer.
81
+
82
+ Point an MCP client at it either way:
83
+
84
+ ```sh
85
+ # Auto-discovers every running dev server on this machine.
86
+ claude mcp add lit-devtools -- npx -y @oddsquad/vite-plugin-lit mcp
87
+
88
+ # Or dial one server directly, if you'd rather pin the port.
89
+ claude mcp add lit-devtools -- npx -y mcp-remote http://localhost:5179/__devtools/__mcp
90
+ ```
91
+
92
+ The equivalent `claude_desktop_config.json` / `mcp.json` entry:
93
+
94
+ ```json
95
+ {
96
+ "mcpServers": {
97
+ "lit-devtools": {
98
+ "command": "npx",
99
+ "args": ["-y", "@oddsquad/vite-plugin-lit", "mcp"]
100
+ }
101
+ }
102
+ }
103
+ ```
104
+
105
+ The two differ in how the server is found, not in what it can do:
106
+
107
+ - **`... vite-plugin-lit mcp`** runs devframe's connector over stdio. The
108
+ plugin publishes each running dev server to devframe's instance registry,
109
+ and the connector lists them through two gateway tools
110
+ (`devframe_connect_list-instances`, `devframe_connect_call-tool`) — so one
111
+ entry covers every project you have running, with no port to keep in sync.
112
+ Being a shared connector, it surfaces every devframe on the machine, not
113
+ only this plugin's tools.
114
+ - **`mcp-remote <url>`** bridges stdio to one fixed URL and depends on no
115
+ discovery at all. Reach for it if registry discovery is unavailable — the
116
+ plugin logs a warning saying so at startup, naming the reason.
117
+
118
+ The CLI also has `lit-devtools dev`, a standalone devframe server with no page
119
+ attached. It exists to prove the panel definition runs without Vite (a
120
+ framework-neutrality harness, and the groundwork for non-Vite adapters); it
121
+ cannot show a real component tree, so it is not a way to inspect an app
122
+ without a dev server.
123
+
124
+ ## Playground
125
+
126
+ A manually inspectable fixture app (also the source for the e2e fixtures), and
127
+ self-contained enough to open on
128
+ [StackBlitz](https://stackblitz.com/github/oddcelot/vite-plugin-lit/tree/main/playground)
129
+ straight from the repo URL. Run it locally from the repo root:
130
+
131
+ ```sh
132
+ pnpm dev # builds the plugin, then serves http://localhost:5179
133
+ ```
134
+
135
+ ## Development
136
+
137
+ Requires Node 26 and pnpm 12 (`corepack enable` picks up the pinned version).
138
+
139
+ ```sh
140
+ pnpm install # workspace: root package + playground + docs
141
+ pnpm build # tsc + panel assets -> ./lib, ./panel, ./index.js
142
+ pnpm exec vp check # format, lint, type check
143
+ pnpm test # unit + e2e
144
+ pnpm docs:dev # the documentation site
145
+ ```
146
+
147
+ More in
148
+ [Development](https://oddcelot.github.io/vite-plugin-lit/contributing/development/)
149
+ and [Testing](https://oddcelot.github.io/vite-plugin-lit/contributing/testing/).
150
+
151
+ ## License
152
+
153
+ 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-CFrVgNfS.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-BvWe4sTJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-agent-rpc-BXhoSh1z-BvWe4sTJ.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"}