@oddsquad/vite-plugin-lit 0.0.3 → 0.0.5

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 (175) hide show
  1. package/index.d.ts +3 -2
  2. package/index.d.ts.map +1 -1
  3. package/index.js +1 -1
  4. package/index.js.map +1 -1
  5. package/lib/color-scheme.d.ts +30 -0
  6. package/lib/color-scheme.d.ts.map +1 -0
  7. package/lib/color-scheme.js +41 -0
  8. package/lib/color-scheme.js.map +1 -0
  9. package/lib/icons.d.ts +18 -0
  10. package/lib/icons.d.ts.map +1 -0
  11. package/lib/icons.js +23 -0
  12. package/lib/icons.js.map +1 -0
  13. package/lib/plugin.d.ts +71 -6
  14. package/lib/plugin.d.ts.map +1 -1
  15. package/lib/plugin.js +522 -21
  16. package/lib/plugin.js.map +1 -1
  17. package/lib/runtime/css.d.ts +94 -0
  18. package/lib/runtime/css.d.ts.map +1 -0
  19. package/lib/runtime/css.js +112 -0
  20. package/lib/runtime/css.js.map +1 -0
  21. package/lib/runtime/edge-panel.d.ts +18 -0
  22. package/lib/runtime/edge-panel.d.ts.map +1 -0
  23. package/lib/runtime/edge-panel.js +130 -0
  24. package/lib/runtime/edge-panel.js.map +1 -0
  25. package/lib/runtime/fonts.d.ts +18 -0
  26. package/lib/runtime/fonts.d.ts.map +1 -0
  27. package/lib/runtime/fonts.js +18 -0
  28. package/lib/runtime/fonts.js.map +1 -0
  29. package/lib/runtime/indicator.d.ts +7 -0
  30. package/lib/runtime/indicator.d.ts.map +1 -0
  31. package/lib/runtime/indicator.js +141 -0
  32. package/lib/runtime/indicator.js.map +1 -0
  33. package/lib/runtime/inspector/collect.d.ts +17 -0
  34. package/lib/runtime/inspector/collect.d.ts.map +1 -0
  35. package/lib/runtime/inspector/collect.js +132 -0
  36. package/lib/runtime/inspector/collect.js.map +1 -0
  37. package/lib/runtime/inspector/install.d.ts +7 -0
  38. package/lib/runtime/inspector/install.d.ts.map +1 -0
  39. package/lib/runtime/inspector/install.js +204 -0
  40. package/lib/runtime/inspector/install.js.map +1 -0
  41. package/lib/runtime/inspector/serialize.d.ts +10 -0
  42. package/lib/runtime/inspector/serialize.d.ts.map +1 -0
  43. package/lib/runtime/inspector/serialize.js +133 -0
  44. package/lib/runtime/inspector/serialize.js.map +1 -0
  45. package/lib/runtime/intern.d.ts.map +1 -1
  46. package/lib/runtime/intern.js +1 -1
  47. package/lib/runtime/intern.js.map +1 -1
  48. package/lib/runtime/overrides.d.ts +25 -0
  49. package/lib/runtime/overrides.d.ts.map +1 -0
  50. package/lib/runtime/overrides.js +33 -0
  51. package/lib/runtime/overrides.js.map +1 -0
  52. package/lib/runtime/patch.d.ts +6 -15
  53. package/lib/runtime/patch.d.ts.map +1 -1
  54. package/lib/runtime/patch.js +81 -28
  55. package/lib/runtime/patch.js.map +1 -1
  56. package/lib/runtime/source-meta.d.ts +16 -0
  57. package/lib/runtime/source-meta.d.ts.map +1 -0
  58. package/lib/runtime/source-meta.js +8 -0
  59. package/lib/runtime/source-meta.js.map +1 -0
  60. package/lib/runtime/source-overlay/editors.d.ts +9 -0
  61. package/lib/runtime/source-overlay/editors.d.ts.map +1 -0
  62. package/lib/runtime/source-overlay/editors.js +40 -0
  63. package/lib/runtime/source-overlay/editors.js.map +1 -0
  64. package/lib/runtime/source-overlay/mask-path.d.ts +7 -0
  65. package/lib/runtime/source-overlay/mask-path.d.ts.map +1 -0
  66. package/lib/runtime/source-overlay/mask-path.js +26 -0
  67. package/lib/runtime/source-overlay/mask-path.js.map +1 -0
  68. package/lib/runtime/source-overlay/overlay-element.d.ts +18 -0
  69. package/lib/runtime/source-overlay/overlay-element.d.ts.map +1 -0
  70. package/lib/runtime/source-overlay/overlay-element.js +381 -0
  71. package/lib/runtime/source-overlay/overlay-element.js.map +1 -0
  72. package/lib/runtime/source-overlay/source-host.d.ts +14 -0
  73. package/lib/runtime/source-overlay/source-host.d.ts.map +1 -0
  74. package/lib/runtime/source-overlay/source-host.js +94 -0
  75. package/lib/runtime/source-overlay/source-host.js.map +1 -0
  76. package/lib/runtime/source-overlay/template.d.ts +7 -0
  77. package/lib/runtime/source-overlay/template.d.ts.map +1 -0
  78. package/lib/runtime/source-overlay/template.js +125 -0
  79. package/lib/runtime/source-overlay/template.js.map +1 -0
  80. package/lib/runtime/source-overlay.d.ts +7 -0
  81. package/lib/runtime/source-overlay.d.ts.map +1 -0
  82. package/lib/runtime/source-overlay.js +11 -0
  83. package/lib/runtime/source-overlay.js.map +1 -0
  84. package/lib/runtime/timeline/clock.d.ts +10 -0
  85. package/lib/runtime/timeline/clock.d.ts.map +1 -0
  86. package/lib/runtime/timeline/clock.js +22 -0
  87. package/lib/runtime/timeline/clock.js.map +1 -0
  88. package/lib/runtime/timeline/identity.d.ts +37 -0
  89. package/lib/runtime/timeline/identity.d.ts.map +1 -0
  90. package/lib/runtime/timeline/identity.js +73 -0
  91. package/lib/runtime/timeline/identity.js.map +1 -0
  92. package/lib/runtime/timeline/input.d.ts +16 -0
  93. package/lib/runtime/timeline/input.d.ts.map +1 -0
  94. package/lib/runtime/timeline/input.js +87 -0
  95. package/lib/runtime/timeline/input.js.map +1 -0
  96. package/lib/runtime/timeline/install.d.ts +7 -0
  97. package/lib/runtime/timeline/install.d.ts.map +1 -0
  98. package/lib/runtime/timeline/install.js +69 -0
  99. package/lib/runtime/timeline/install.js.map +1 -0
  100. package/lib/runtime/timeline/lifecycle.d.ts +27 -0
  101. package/lib/runtime/timeline/lifecycle.d.ts.map +1 -0
  102. package/lib/runtime/timeline/lifecycle.js +225 -0
  103. package/lib/runtime/timeline/lifecycle.js.map +1 -0
  104. package/lib/runtime/timeline/public-api.d.ts +28 -0
  105. package/lib/runtime/timeline/public-api.d.ts.map +1 -0
  106. package/lib/runtime/timeline/public-api.js +44 -0
  107. package/lib/runtime/timeline/public-api.js.map +1 -0
  108. package/lib/runtime/timeline/render.d.ts +39 -0
  109. package/lib/runtime/timeline/render.d.ts.map +1 -0
  110. package/lib/runtime/timeline/render.js +109 -0
  111. package/lib/runtime/timeline/render.js.map +1 -0
  112. package/lib/runtime/timeline/transport.d.ts +42 -0
  113. package/lib/runtime/timeline/transport.d.ts.map +1 -0
  114. package/lib/runtime/timeline/transport.js +86 -0
  115. package/lib/runtime/timeline/transport.js.map +1 -0
  116. package/lib/segmented-tabs.d.ts +33 -0
  117. package/lib/segmented-tabs.d.ts.map +1 -0
  118. package/lib/segmented-tabs.js +129 -0
  119. package/lib/segmented-tabs.js.map +1 -0
  120. package/lib/source-meta.d.ts +26 -0
  121. package/lib/source-meta.d.ts.map +1 -0
  122. package/lib/source-meta.js +129 -0
  123. package/lib/source-meta.js.map +1 -0
  124. package/lib/timeline-plugin.d.ts +72 -0
  125. package/lib/timeline-plugin.d.ts.map +1 -0
  126. package/lib/timeline-plugin.js +376 -0
  127. package/lib/timeline-plugin.js.map +1 -0
  128. package/lib/tokens.d.ts +38 -0
  129. package/lib/tokens.d.ts.map +1 -0
  130. package/lib/tokens.js +263 -0
  131. package/lib/tokens.js.map +1 -0
  132. package/lib/transform.d.ts +5 -5
  133. package/lib/transform.d.ts.map +1 -1
  134. package/lib/transform.js +3 -3
  135. package/lib/transform.js.map +1 -1
  136. package/lib/types.d.ts +34 -0
  137. package/lib/types.d.ts.map +1 -0
  138. package/lib/types.js +7 -0
  139. package/lib/types.js.map +1 -0
  140. package/lib/wrap-table.d.ts.map +1 -1
  141. package/lib/wrap-table.js.map +1 -1
  142. package/package.json +18 -4
  143. package/panel/components-view.d.ts +60 -0
  144. package/panel/components-view.d.ts.map +1 -0
  145. package/panel/components-view.js +581 -0
  146. package/panel/components-view.js.map +1 -0
  147. package/panel/devtools-settings.d.ts +41 -0
  148. package/panel/devtools-settings.d.ts.map +1 -0
  149. package/panel/devtools-settings.js +463 -0
  150. package/panel/devtools-settings.js.map +1 -0
  151. package/panel/index.html +29 -0
  152. package/panel/lit-devtools-panel.d.ts +37 -0
  153. package/panel/lit-devtools-panel.d.ts.map +1 -0
  154. package/panel/lit-devtools-panel.js +183 -0
  155. package/panel/lit-devtools-panel.js.map +1 -0
  156. package/panel/timeline-event-list.d.ts +43 -0
  157. package/panel/timeline-event-list.d.ts.map +1 -0
  158. package/panel/timeline-event-list.js +416 -0
  159. package/panel/timeline-event-list.js.map +1 -0
  160. package/panel/timeline-layers.d.ts +25 -0
  161. package/panel/timeline-layers.d.ts.map +1 -0
  162. package/panel/timeline-layers.js +96 -0
  163. package/panel/timeline-layers.js.map +1 -0
  164. package/panel/timeline-view.d.ts +38 -0
  165. package/panel/timeline-view.d.ts.map +1 -0
  166. package/panel/timeline-view.js +234 -0
  167. package/panel/timeline-view.js.map +1 -0
  168. package/types/inspector.d.ts +130 -0
  169. package/types/inspector.d.ts.map +1 -0
  170. package/types/inspector.js +16 -0
  171. package/types/inspector.js.map +1 -0
  172. package/types/timeline.d.ts +98 -0
  173. package/types/timeline.d.ts.map +1 -0
  174. package/types/timeline.js +39 -0
  175. package/types/timeline.js.map +1 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ */
6
+ /**
7
+ * Timeline runtime install entry point.
8
+ *
9
+ * Injected by the plugin into the host page via `transformIndexHtml` when
10
+ * `timeline: true` is set. Sets up all capture layers and wires them to the
11
+ * transport.
12
+ *
13
+ * Recording starts off; the panel iframe toggles it via the Vite HMR channel
14
+ * (Phase 0/1) or devframe shared state (Phase 2+).
15
+ */
16
+ import { emit, setHotClient } from './transport.js';
17
+ import { resetClock } from './clock.js';
18
+ import { installLifecycleLayer } from './lifecycle.js';
19
+ import { installRenderLayer, setRenderDebugEnabled } from './render.js';
20
+ import { installMouseLayer, installKeyboardLayer } from './input.js';
21
+ // ---------------------------------------------------------------------------
22
+ // Recording state — written by the panel toggle, read by all capture layers.
23
+ // ---------------------------------------------------------------------------
24
+ const state = {
25
+ recordingState: false,
26
+ litLifecycleEnabled: true,
27
+ litRenderEnabled: true,
28
+ mouseEventEnabled: false,
29
+ keyboardEventEnabled: false,
30
+ };
31
+ const recording = () => state.recordingState;
32
+ const lifecycleEnabled = () => state.litLifecycleEnabled;
33
+ const renderEnabled = () => state.litRenderEnabled;
34
+ const mouseEnabled = () => state.mouseEventEnabled;
35
+ const keyboardEnabled = () => state.keyboardEventEnabled;
36
+ // Drive Lit's debug event flag from recording × render-layer-enabled so
37
+ // lit-html only pays the per-render CustomEvent dispatch cost while we're
38
+ // actually capturing the render layer.
39
+ const syncRenderDebug = () => {
40
+ setRenderDebugEnabled(state.recordingState && state.litRenderEnabled);
41
+ };
42
+ // ---------------------------------------------------------------------------
43
+ // Capture layer installation
44
+ // ---------------------------------------------------------------------------
45
+ installLifecycleLayer(emit, recording, lifecycleEnabled);
46
+ installRenderLayer(emit, recording, renderEnabled);
47
+ installMouseLayer(emit, recording, mouseEnabled);
48
+ installKeyboardLayer(emit, recording, keyboardEnabled);
49
+ const hot = import.meta.hot;
50
+ if (hot !== undefined) {
51
+ setHotClient(hot);
52
+ // Panel → app: toggle recording and per-layer flags.
53
+ hot.on('lit:timeline:recording-changed', (data) => {
54
+ const next = data.recording;
55
+ // Re-zero the timeline clock on the rising edge so event times read as
56
+ // "ms since recording started" rather than since page load.
57
+ if (next && !state.recordingState)
58
+ resetClock();
59
+ state.recordingState = next;
60
+ syncRenderDebug();
61
+ });
62
+ hot.on('lit:timeline:layers-changed', (data) => {
63
+ Object.assign(state, data);
64
+ syncRenderDebug();
65
+ });
66
+ // Announce readiness so the panel can detect the runtime.
67
+ hot.send('lit:timeline:runtime-ready', {});
68
+ }
69
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../../../../packages/labs/vite-plugin-lit/src/lib/runtime/timeline/install.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;GASG;AAEH,OAAO,EAAC,IAAI,EAAE,YAAY,EAAC,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAC,UAAU,EAAC,MAAM,YAAY,CAAC;AACtC,OAAO,EAAC,qBAAqB,EAAC,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAC,kBAAkB,EAAE,qBAAqB,EAAC,MAAM,aAAa,CAAC;AACtE,OAAO,EAAC,iBAAiB,EAAE,oBAAoB,EAAC,MAAM,YAAY,CAAC;AAGnE,8EAA8E;AAC9E,6EAA6E;AAC7E,8EAA8E;AAE9E,MAAM,KAAK,GAAwB;IACjC,cAAc,EAAE,KAAK;IACrB,mBAAmB,EAAE,IAAI;IACzB,gBAAgB,EAAE,IAAI;IACtB,iBAAiB,EAAE,KAAK;IACxB,oBAAoB,EAAE,KAAK;CAC5B,CAAC;AAEF,MAAM,SAAS,GAAG,GAAY,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC;AACtD,MAAM,gBAAgB,GAAG,GAAY,EAAE,CAAC,KAAK,CAAC,mBAAmB,CAAC;AAClE,MAAM,aAAa,GAAG,GAAY,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC;AAC5D,MAAM,YAAY,GAAG,GAAY,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC;AAC5D,MAAM,eAAe,GAAG,GAAY,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC;AAElE,wEAAwE;AACxE,0EAA0E;AAC1E,uCAAuC;AACvC,MAAM,eAAe,GAAG,GAAS,EAAE;IACjC,qBAAqB,CAAC,KAAK,CAAC,cAAc,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;AACxE,CAAC,CAAC;AAEF,8EAA8E;AAC9E,6BAA6B;AAC7B,8EAA8E;AAE9E,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC;AACzD,kBAAkB,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AACnD,iBAAiB,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;AACjD,oBAAoB,CAAC,IAAI,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC;AAYvD,MAAM,GAAG,GAAI,MAAM,CAAC,IAAwB,CAAC,GAAG,CAAC;AAEjD,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;IACtB,YAAY,CAAC,GAAG,CAAC,CAAC;IAElB,qDAAqD;IACrD,GAAG,CAAC,EAAE,CAAC,gCAAgC,EAAE,CAAC,IAAI,EAAE,EAAE;QAChD,MAAM,IAAI,GAAI,IAA6B,CAAC,SAAS,CAAC;QACtD,uEAAuE;QACvE,4DAA4D;QAC5D,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,cAAc;YAAE,UAAU,EAAE,CAAC;QAChD,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;QAC5B,eAAe,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,EAAE,CAAC,6BAA6B,EAAE,CAAC,IAAI,EAAE,EAAE;QAC7C,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,IAAoC,CAAC,CAAC;QAC3D,eAAe,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,0DAA0D;IAC1D,GAAG,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;AAC7C,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * Timeline runtime install entry point.\n *\n * Injected by the plugin into the host page via `transformIndexHtml` when\n * `timeline: true` is set. Sets up all capture layers and wires them to the\n * transport.\n *\n * Recording starts off; the panel iframe toggles it via the Vite HMR channel\n * (Phase 0/1) or devframe shared state (Phase 2+).\n */\n\nimport {emit, setHotClient} from './transport.js';\nimport {resetClock} from './clock.js';\nimport {installLifecycleLayer} from './lifecycle.js';\nimport {installRenderLayer, setRenderDebugEnabled} from './render.js';\nimport {installMouseLayer, installKeyboardLayer} from './input.js';\nimport type {TimelineLayersState} from '../../../types/timeline.js';\n\n// ---------------------------------------------------------------------------\n// Recording state — written by the panel toggle, read by all capture layers.\n// ---------------------------------------------------------------------------\n\nconst state: TimelineLayersState = {\n recordingState: false,\n litLifecycleEnabled: true,\n litRenderEnabled: true,\n mouseEventEnabled: false,\n keyboardEventEnabled: false,\n};\n\nconst recording = (): boolean => state.recordingState;\nconst lifecycleEnabled = (): boolean => state.litLifecycleEnabled;\nconst renderEnabled = (): boolean => state.litRenderEnabled;\nconst mouseEnabled = (): boolean => state.mouseEventEnabled;\nconst keyboardEnabled = (): boolean => state.keyboardEventEnabled;\n\n// Drive Lit's debug event flag from recording × render-layer-enabled so\n// lit-html only pays the per-render CustomEvent dispatch cost while we're\n// actually capturing the render layer.\nconst syncRenderDebug = (): void => {\n setRenderDebugEnabled(state.recordingState && state.litRenderEnabled);\n};\n\n// ---------------------------------------------------------------------------\n// Capture layer installation\n// ---------------------------------------------------------------------------\n\ninstallLifecycleLayer(emit, recording, lifecycleEnabled);\ninstallRenderLayer(emit, recording, renderEnabled);\ninstallMouseLayer(emit, recording, mouseEnabled);\ninstallKeyboardLayer(emit, recording, keyboardEnabled);\n\n// ---------------------------------------------------------------------------\n// HMR channel wiring (Phase 0/1 transport)\n// ---------------------------------------------------------------------------\n\n// `import.meta.hot` exists in Vite dev builds; the tsconfig lib set doesn't\n// include it, so we access it through the un-typed global shape.\ntype ViteHot = {\n send: (event: string, data: unknown) => void;\n on: (event: string, handler: (data: unknown) => void) => void;\n};\nconst hot = (import.meta as {hot?: ViteHot}).hot;\n\nif (hot !== undefined) {\n setHotClient(hot);\n\n // Panel → app: toggle recording and per-layer flags.\n hot.on('lit:timeline:recording-changed', (data) => {\n const next = (data as {recording: boolean}).recording;\n // Re-zero the timeline clock on the rising edge so event times read as\n // \"ms since recording started\" rather than since page load.\n if (next && !state.recordingState) resetClock();\n state.recordingState = next;\n syncRenderDebug();\n });\n\n hot.on('lit:timeline:layers-changed', (data) => {\n Object.assign(state, data as Partial<TimelineLayersState>);\n syncRenderDebug();\n });\n\n // Announce readiness so the panel can detect the runtime.\n hot.send('lit:timeline:runtime-ready', {});\n}\n"]}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ */
6
+ import type { TimelineEvent } from '../../../types/timeline.js';
7
+ type EmitFn = (event: TimelineEvent) => void;
8
+ type RecordingFn = () => boolean;
9
+ type LayerEnabledFn = () => boolean;
10
+ /**
11
+ * Install lifecycle instrumentation for *every* Lit element on the page.
12
+ *
13
+ * The phases live on the shared ReactiveElement/LitElement base prototypes, so
14
+ * wrapping them there instruments all components at once — not just the first
15
+ * one registered (the old behavior, which also silently no-op'd when Vite
16
+ * served the production Lit build, since that omits the `reactiveElementVersions`
17
+ * sentinel the resolver gated on).
18
+ *
19
+ * A component that overrides `willUpdate`/`updated`/`firstUpdated` *without*
20
+ * calling `super` shadows the base no-op and won't report that phase — an
21
+ * accepted limitation; the common case and `super`-calling overrides report.
22
+ *
23
+ * Idempotent — wrapping is brand-guarded per prototype+method.
24
+ */
25
+ export declare const installLifecycleLayer: (emit: EmitFn, recording: RecordingFn, enabled: LayerEnabledFn) => void;
26
+ export {};
27
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../packages/labs/vite-plugin-lit/src/lib/runtime/timeline/lifecycle.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAoBH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AAE9D,KAAK,MAAM,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;AAC7C,KAAK,WAAW,GAAG,MAAM,OAAO,CAAC;AACjC,KAAK,cAAc,GAAG,MAAM,OAAO,CAAC;AA4KpC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,qBAAqB,GAChC,MAAM,MAAM,EACZ,WAAW,WAAW,EACtB,SAAS,cAAc,KACtB,IAyBF,CAAC"}
@@ -0,0 +1,225 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ */
6
+ /**
7
+ * ReactiveElement prototype instrumentation for the lifecycle timeline layer.
8
+ *
9
+ * Wraps the lifecycle methods listed in PHASES on the prototype of the
10
+ * ReactiveElement class loaded by the app — not by importing lit directly,
11
+ * to avoid multi-copy issues. The proto is resolved from the already-defined
12
+ * custom element registry (same mechanism as the HMR patch module).
13
+ *
14
+ * Each wrapped method emits a paired start/end TimelineEvent so the panel
15
+ * can render duration bars. performUpdate brackets the whole update tick and
16
+ * bumps a per-instance tick counter that becomes the groupId shared by all
17
+ * phase events within that tick.
18
+ *
19
+ * Gated by the recording flag so overhead is near-zero when idle.
20
+ */
21
+ import { idOf, sourceOf, changedKeys } from './identity.js';
22
+ import { now } from './clock.js';
23
+ /** Phases to instrument with start/end pairs. */
24
+ const UPDATE_PHASES = [
25
+ 'performUpdate',
26
+ 'willUpdate',
27
+ 'update',
28
+ 'updated',
29
+ 'firstUpdated',
30
+ ];
31
+ /** Point-in-time lifecycle events (no end bracket). */
32
+ const POINT_PHASES = ['connectedCallback', 'disconnectedCallback'];
33
+ const BRAND = Symbol.for('@lit-labs/vite-plugin-lit#timeline-lifecycle');
34
+ /** Per-instance update tick counter (bumped inside performUpdate wrapper). */
35
+ const ticks = new WeakMap();
36
+ const tickOf = (el) => ticks.get(el) ?? 0;
37
+ /** Returns true if `proto[name]` is already our wrapper (idempotent install). */
38
+ const isWrapped = (proto, name) => {
39
+ const fn = proto[name];
40
+ return (typeof fn === 'function' && fn[BRAND] === true);
41
+ };
42
+ /**
43
+ * Wrap `proto[name]` with a start/end pair emitting to `emit`.
44
+ * `isPoint` methods only emit a single event (no end bracket).
45
+ */
46
+ const wrap = (proto, name, isPoint, emit, recording, enabled) => {
47
+ if (isWrapped(proto, name))
48
+ return;
49
+ const orig = proto[name];
50
+ const wrapper = function (...args) {
51
+ if (!recording() || !enabled()) {
52
+ return orig?.apply(this, args);
53
+ }
54
+ // Bump the per-instance tick at the *start* of each performUpdate, before
55
+ // computing the groupId, so this whole update cycle — performUpdate and the
56
+ // willUpdate/update/updated phases it nests — shares one groupId, distinct
57
+ // from the previous cycle's. (Reading the tick before bumping put
58
+ // performUpdate in a different group from its own phases and let adjacent
59
+ // cycles collide on the same tick.)
60
+ if (name === 'performUpdate') {
61
+ ticks.set(this, tickOf(this) + 1);
62
+ }
63
+ const elementId = idOf(this);
64
+ const tagName = this.localName ?? 'unknown';
65
+ const source = sourceOf(this);
66
+ const tick = tickOf(this);
67
+ const groupId = `${elementId}:${tick}`;
68
+ const time = now();
69
+ const changed = changedKeys(args[0]);
70
+ if (!isPoint) {
71
+ emit({
72
+ layerId: 'lit-lifecycle',
73
+ time,
74
+ groupId,
75
+ title: name + ':start',
76
+ subtitle: tagName,
77
+ data: { phase: name, changed },
78
+ meta: { elementId, tagName, source },
79
+ });
80
+ }
81
+ let result;
82
+ try {
83
+ result = orig?.apply(this, args);
84
+ }
85
+ finally {
86
+ if (!isPoint) {
87
+ emit({
88
+ layerId: 'lit-lifecycle',
89
+ time: now(),
90
+ groupId,
91
+ title: name + ':end',
92
+ subtitle: tagName,
93
+ data: { phase: name },
94
+ meta: { elementId, tagName, source },
95
+ });
96
+ }
97
+ else {
98
+ emit({
99
+ layerId: 'lit-lifecycle',
100
+ time,
101
+ title: name,
102
+ subtitle: tagName,
103
+ data: { phase: name },
104
+ meta: { elementId, tagName, source },
105
+ });
106
+ }
107
+ }
108
+ return result;
109
+ };
110
+ wrapper[BRAND] = true;
111
+ try {
112
+ Object.defineProperty(proto, name, {
113
+ value: wrapper,
114
+ writable: true,
115
+ configurable: true,
116
+ });
117
+ }
118
+ catch {
119
+ // Non-configurable slot; instrumentation degrades gracefully.
120
+ }
121
+ };
122
+ /**
123
+ * Finds the prototype in `proto`'s chain that *owns* `name` as an own property,
124
+ * or null if none does.
125
+ */
126
+ const ownerOf = (proto, name) => {
127
+ let p = proto;
128
+ while (p !== null && !Object.prototype.hasOwnProperty.call(p, name)) {
129
+ p = Object.getPrototypeOf(p);
130
+ }
131
+ return p;
132
+ };
133
+ /**
134
+ * Returns any defined Lit element's prototype (duck-typed by `performUpdate`),
135
+ * or null if none can be found yet. One sample is enough: its chain leads to
136
+ * the shared ReactiveElement/LitElement base prototypes every component
137
+ * inherits.
138
+ *
139
+ * Tries the non-standard `_registry` map first, then falls back to sampling a
140
+ * live upgraded element from the DOM — `_registry` isn't present in every
141
+ * engine (e.g. some Chrome builds), and without this fallback an app whose
142
+ * components were all defined before the runtime installed gets no lifecycle
143
+ * instrumentation at all.
144
+ */
145
+ const findLitElementProto = () => {
146
+ const registry = customElements._registry;
147
+ if (registry !== undefined) {
148
+ for (const ctor of Object.values(registry)) {
149
+ const proto = ctor?.prototype;
150
+ if (proto != null && 'performUpdate' in proto)
151
+ return proto;
152
+ }
153
+ }
154
+ if (typeof document !== 'undefined') {
155
+ for (const el of document.querySelectorAll('*')) {
156
+ const proto = Object.getPrototypeOf(el);
157
+ if (proto != null && 'performUpdate' in proto)
158
+ return proto;
159
+ }
160
+ }
161
+ return null;
162
+ };
163
+ let installed = false;
164
+ /**
165
+ * Install lifecycle instrumentation for *every* Lit element on the page.
166
+ *
167
+ * The phases live on the shared ReactiveElement/LitElement base prototypes, so
168
+ * wrapping them there instruments all components at once — not just the first
169
+ * one registered (the old behavior, which also silently no-op'd when Vite
170
+ * served the production Lit build, since that omits the `reactiveElementVersions`
171
+ * sentinel the resolver gated on).
172
+ *
173
+ * A component that overrides `willUpdate`/`updated`/`firstUpdated` *without*
174
+ * calling `super` shadows the base no-op and won't report that phase — an
175
+ * accepted limitation; the common case and `super`-calling overrides report.
176
+ *
177
+ * Idempotent — wrapping is brand-guarded per prototype+method.
178
+ */
179
+ export const installLifecycleLayer = (emit, recording, enabled) => {
180
+ if (installed)
181
+ return;
182
+ installed = true;
183
+ // Instrument from a sample already on the page (covers components defined
184
+ // before the runtime installed — the common case, since the runtime is
185
+ // injected after the app's modules).
186
+ const proto = findLitElementProto();
187
+ if (proto !== null) {
188
+ patchBases(proto, emit, recording, enabled);
189
+ }
190
+ // Also instrument on future defines — both for components registered later
191
+ // and for the case where none were discoverable above. Kept installed for
192
+ // the page's lifetime; `patchBases` is idempotent (wrapping is brand-guarded
193
+ // per prototype+method), so re-running once the shared bases are wrapped is a
194
+ // cheap no-op.
195
+ const origDefine = customElements.define.bind(customElements);
196
+ customElements.define = (name, ctor, options) => {
197
+ origDefine(name, ctor, options);
198
+ const p = ctor?.prototype;
199
+ if (p != null && 'performUpdate' in p) {
200
+ patchBases(p, emit, recording, enabled);
201
+ }
202
+ };
203
+ };
204
+ /**
205
+ * Wraps the lifecycle phases on the shared base prototypes reachable from
206
+ * `sample`. `performUpdate` is never overridden by app code, so walking up from
207
+ * any Lit element lands on `ReactiveElement.prototype` — which owns the update/
208
+ * connect lifecycle, shared by all components. `update` is wrapped on its
209
+ * effective owner instead (`LitElement.prototype` overrides it to drive
210
+ * `render()`), so the timed call is the one that actually runs.
211
+ */
212
+ const patchBases = (sample, emit, recording, enabled) => {
213
+ const reProto = ownerOf(sample, 'performUpdate');
214
+ if (reProto === null)
215
+ return;
216
+ for (const name of UPDATE_PHASES) {
217
+ const owner = name === 'update' ? ownerOf(sample, 'update') : reProto;
218
+ if (owner !== null)
219
+ wrap(owner, name, false, emit, recording, enabled);
220
+ }
221
+ for (const name of POINT_PHASES) {
222
+ wrap(reProto, name, true, emit, recording, enabled);
223
+ }
224
+ };
225
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../../../../packages/labs/vite-plugin-lit/src/lib/runtime/timeline/lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * ReactiveElement prototype instrumentation for the lifecycle timeline layer.\n *\n * Wraps the lifecycle methods listed in PHASES on the prototype of the\n * ReactiveElement class loaded by the app — not by importing lit directly,\n * to avoid multi-copy issues. The proto is resolved from the already-defined\n * custom element registry (same mechanism as the HMR patch module).\n *\n * Each wrapped method emits a paired start/end TimelineEvent so the panel\n * can render duration bars. performUpdate brackets the whole update tick and\n * bumps a per-instance tick counter that becomes the groupId shared by all\n * phase events within that tick.\n *\n * Gated by the recording flag so overhead is near-zero when idle.\n */\n\nimport {idOf, sourceOf, changedKeys} from './identity.js';\nimport {now} from './clock.js';\nimport type {TimelineEvent} from '../../../types/timeline.js';\n\ntype EmitFn = (event: TimelineEvent) => void;\ntype RecordingFn = () => boolean;\ntype LayerEnabledFn = () => boolean;\n\n/** Phases to instrument with start/end pairs. */\nconst UPDATE_PHASES = [\n 'performUpdate',\n 'willUpdate',\n 'update',\n 'updated',\n 'firstUpdated',\n] as const;\n\n/** Point-in-time lifecycle events (no end bracket). */\nconst POINT_PHASES = ['connectedCallback', 'disconnectedCallback'] as const;\n\nconst BRAND = Symbol.for('@lit-labs/vite-plugin-lit#timeline-lifecycle');\n\n/** Per-instance update tick counter (bumped inside performUpdate wrapper). */\nconst ticks = new WeakMap<object, number>();\n\nconst tickOf = (el: object): number => ticks.get(el) ?? 0;\n\ntype AnyFn = (...args: unknown[]) => unknown;\ntype Proto = Record<string | symbol, AnyFn | undefined>;\n\n/** Returns true if `proto[name]` is already our wrapper (idempotent install). */\nconst isWrapped = (proto: Proto, name: string): boolean => {\n const fn = proto[name];\n return (\n typeof fn === 'function' && (fn as AnyFn & {[BRAND]?: true})[BRAND] === true\n );\n};\n\n/**\n * Wrap `proto[name]` with a start/end pair emitting to `emit`.\n * `isPoint` methods only emit a single event (no end bracket).\n */\nconst wrap = (\n proto: Proto,\n name: string,\n isPoint: boolean,\n emit: EmitFn,\n recording: RecordingFn,\n enabled: LayerEnabledFn\n): void => {\n if (isWrapped(proto, name)) return;\n const orig = proto[name];\n\n const wrapper: AnyFn & {[BRAND]?: true} = function (\n this: object,\n ...args: unknown[]\n ) {\n if (!recording() || !enabled()) {\n return orig?.apply(this, args);\n }\n\n // Bump the per-instance tick at the *start* of each performUpdate, before\n // computing the groupId, so this whole update cycle — performUpdate and the\n // willUpdate/update/updated phases it nests — shares one groupId, distinct\n // from the previous cycle's. (Reading the tick before bumping put\n // performUpdate in a different group from its own phases and let adjacent\n // cycles collide on the same tick.)\n if (name === 'performUpdate') {\n ticks.set(this, tickOf(this) + 1);\n }\n\n const elementId = idOf(this);\n const tagName = (this as Element).localName ?? 'unknown';\n const source = sourceOf(this);\n const tick = tickOf(this);\n const groupId = `${elementId}:${tick}`;\n const time = now();\n const changed = changedKeys(args[0]);\n\n if (!isPoint) {\n emit({\n layerId: 'lit-lifecycle',\n time,\n groupId,\n title: name + ':start',\n subtitle: tagName,\n data: {phase: name, changed},\n meta: {elementId, tagName, source},\n });\n }\n\n let result: unknown;\n try {\n result = orig?.apply(this, args);\n } finally {\n if (!isPoint) {\n emit({\n layerId: 'lit-lifecycle',\n time: now(),\n groupId,\n title: name + ':end',\n subtitle: tagName,\n data: {phase: name},\n meta: {elementId, tagName, source},\n });\n } else {\n emit({\n layerId: 'lit-lifecycle',\n time,\n title: name,\n subtitle: tagName,\n data: {phase: name},\n meta: {elementId, tagName, source},\n });\n }\n }\n return result;\n };\n wrapper[BRAND] = true;\n\n try {\n Object.defineProperty(proto, name, {\n value: wrapper,\n writable: true,\n configurable: true,\n });\n } catch {\n // Non-configurable slot; instrumentation degrades gracefully.\n }\n};\n\n/**\n * Finds the prototype in `proto`'s chain that *owns* `name` as an own property,\n * or null if none does.\n */\nconst ownerOf = (proto: Proto, name: string): Proto | null => {\n let p: Proto | null = proto;\n while (p !== null && !Object.prototype.hasOwnProperty.call(p, name)) {\n p = Object.getPrototypeOf(p) as Proto | null;\n }\n return p;\n};\n\n/**\n * Returns any defined Lit element's prototype (duck-typed by `performUpdate`),\n * or null if none can be found yet. One sample is enough: its chain leads to\n * the shared ReactiveElement/LitElement base prototypes every component\n * inherits.\n *\n * Tries the non-standard `_registry` map first, then falls back to sampling a\n * live upgraded element from the DOM — `_registry` isn't present in every\n * engine (e.g. some Chrome builds), and without this fallback an app whose\n * components were all defined before the runtime installed gets no lifecycle\n * instrumentation at all.\n */\nconst findLitElementProto = (): Proto | null => {\n const registry = (\n customElements as unknown as {\n _registry?: Record<string, CustomElementConstructor>;\n }\n )._registry;\n if (registry !== undefined) {\n for (const ctor of Object.values(registry)) {\n const proto = ctor?.prototype;\n if (proto != null && 'performUpdate' in proto) return proto as Proto;\n }\n }\n if (typeof document !== 'undefined') {\n for (const el of document.querySelectorAll('*')) {\n const proto = Object.getPrototypeOf(el) as Proto | null;\n if (proto != null && 'performUpdate' in proto) return proto;\n }\n }\n return null;\n};\n\nlet installed = false;\n\n/**\n * Install lifecycle instrumentation for *every* Lit element on the page.\n *\n * The phases live on the shared ReactiveElement/LitElement base prototypes, so\n * wrapping them there instruments all components at once — not just the first\n * one registered (the old behavior, which also silently no-op'd when Vite\n * served the production Lit build, since that omits the `reactiveElementVersions`\n * sentinel the resolver gated on).\n *\n * A component that overrides `willUpdate`/`updated`/`firstUpdated` *without*\n * calling `super` shadows the base no-op and won't report that phase — an\n * accepted limitation; the common case and `super`-calling overrides report.\n *\n * Idempotent — wrapping is brand-guarded per prototype+method.\n */\nexport const installLifecycleLayer = (\n emit: EmitFn,\n recording: RecordingFn,\n enabled: LayerEnabledFn\n): void => {\n if (installed) return;\n installed = true;\n\n // Instrument from a sample already on the page (covers components defined\n // before the runtime installed — the common case, since the runtime is\n // injected after the app's modules).\n const proto = findLitElementProto();\n if (proto !== null) {\n patchBases(proto, emit, recording, enabled);\n }\n\n // Also instrument on future defines — both for components registered later\n // and for the case where none were discoverable above. Kept installed for\n // the page's lifetime; `patchBases` is idempotent (wrapping is brand-guarded\n // per prototype+method), so re-running once the shared bases are wrapped is a\n // cheap no-op.\n const origDefine = customElements.define.bind(customElements);\n customElements.define = (name, ctor, options) => {\n origDefine(name, ctor, options);\n const p = ctor?.prototype as Proto | null;\n if (p != null && 'performUpdate' in p) {\n patchBases(p, emit, recording, enabled);\n }\n };\n};\n\n/**\n * Wraps the lifecycle phases on the shared base prototypes reachable from\n * `sample`. `performUpdate` is never overridden by app code, so walking up from\n * any Lit element lands on `ReactiveElement.prototype` — which owns the update/\n * connect lifecycle, shared by all components. `update` is wrapped on its\n * effective owner instead (`LitElement.prototype` overrides it to drive\n * `render()`), so the timed call is the one that actually runs.\n */\nconst patchBases = (\n sample: Proto,\n emit: EmitFn,\n recording: RecordingFn,\n enabled: LayerEnabledFn\n): void => {\n const reProto = ownerOf(sample, 'performUpdate');\n if (reProto === null) return;\n for (const name of UPDATE_PHASES) {\n const owner = name === 'update' ? ownerOf(sample, 'update') : reProto;\n if (owner !== null) wrap(owner, name, false, emit, recording, enabled);\n }\n for (const name of POINT_PHASES) {\n wrap(reProto, name, true, emit, recording, enabled);\n }\n};\n"]}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ */
6
+ import type { TimelineEvent, TimelineLayer } from '../../../types/timeline.js';
7
+ export type { TimelineEvent, TimelineLayer };
8
+ /**
9
+ * Emit a custom timeline event from app code.
10
+ *
11
+ * The event is forwarded to the Timeline panel when recording is active.
12
+ * Use a custom `layerId` registered with `addTimelineLayer`, or one of the
13
+ * built-in layer ids (`'lit-lifecycle'`, `'lit-render'`, `'mouse'`,
14
+ * `'keyboard'`).
15
+ *
16
+ * No-ops in production (the HMR channel is absent; events are dropped in the
17
+ * transport queue).
18
+ */
19
+ export declare const addTimelineEvent: (event: TimelineEvent) => void;
20
+ /**
21
+ * Register a custom timeline layer and announce it to the panel.
22
+ *
23
+ * The panel adds the layer to its toggle strip after the built-in layers.
24
+ * The registration message is sent once on first call; duplicate ids are
25
+ * ignored.
26
+ */
27
+ export declare const addTimelineLayer: (layer: TimelineLayer) => void;
28
+ //# sourceMappingURL=public-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../../../../../packages/labs/vite-plugin-lit/src/lib/runtime/timeline/public-api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,OAAO,KAAK,EAAC,aAAa,EAAE,aAAa,EAAC,MAAM,4BAA4B,CAAC;AAE7E,YAAY,EAAC,aAAa,EAAE,aAAa,EAAC,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,aAAa,KAAG,IAEvD,CAAC;AAIF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,aAAa,KAAG,IAOvD,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ */
6
+ /**
7
+ * Public timeline API — re-exported by the `virtual:lit-plugin/timeline`
8
+ * virtual module so app code and other plugins can contribute events and
9
+ * custom layers without reaching into plugin internals.
10
+ *
11
+ * This module shares the same `transport.ts` instance as `install.ts`, so
12
+ * events emitted here flow through the same batched HMR channel to the panel.
13
+ */
14
+ import { emit, setHotClientCallback } from './transport.js';
15
+ /**
16
+ * Emit a custom timeline event from app code.
17
+ *
18
+ * The event is forwarded to the Timeline panel when recording is active.
19
+ * Use a custom `layerId` registered with `addTimelineLayer`, or one of the
20
+ * built-in layer ids (`'lit-lifecycle'`, `'lit-render'`, `'mouse'`,
21
+ * `'keyboard'`).
22
+ *
23
+ * No-ops in production (the HMR channel is absent; events are dropped in the
24
+ * transport queue).
25
+ */
26
+ export const addTimelineEvent = (event) => {
27
+ emit(event);
28
+ };
29
+ /**
30
+ * Register a custom timeline layer and announce it to the panel.
31
+ *
32
+ * The panel adds the layer to its toggle strip after the built-in layers.
33
+ * The registration message is sent once on first call; duplicate ids are
34
+ * ignored.
35
+ */
36
+ export const addTimelineLayer = (layer) => {
37
+ const send = () => {
38
+ import.meta.hot?.send('lit:timeline:custom-layer', {
39
+ layer,
40
+ });
41
+ };
42
+ setHotClientCallback(send);
43
+ };
44
+ //# sourceMappingURL=public-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../../../../packages/labs/vite-plugin-lit/src/lib/runtime/timeline/public-api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AAEH,OAAO,EAAC,IAAI,EAAE,oBAAoB,EAAC,MAAM,gBAAgB,CAAC;AAK1D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAoB,EAAQ,EAAE;IAC7D,IAAI,CAAC,KAAK,CAAC,CAAC;AACd,CAAC,CAAC;AAIF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAoB,EAAQ,EAAE;IAC7D,MAAM,IAAI,GAAG,GAAS,EAAE;QACrB,MAAM,CAAC,IAAwB,CAAC,GAAG,EAAE,IAAI,CAAC,2BAA2B,EAAE;YACtE,KAAK;SACN,CAAC,CAAC;IACL,CAAC,CAAC;IACF,oBAAoB,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * Public timeline API — re-exported by the `virtual:lit-plugin/timeline`\n * virtual module so app code and other plugins can contribute events and\n * custom layers without reaching into plugin internals.\n *\n * This module shares the same `transport.ts` instance as `install.ts`, so\n * events emitted here flow through the same batched HMR channel to the panel.\n */\n\nimport {emit, setHotClientCallback} from './transport.js';\nimport type {TimelineEvent, TimelineLayer} from '../../../types/timeline.js';\n\nexport type {TimelineEvent, TimelineLayer};\n\n/**\n * Emit a custom timeline event from app code.\n *\n * The event is forwarded to the Timeline panel when recording is active.\n * Use a custom `layerId` registered with `addTimelineLayer`, or one of the\n * built-in layer ids (`'lit-lifecycle'`, `'lit-render'`, `'mouse'`,\n * `'keyboard'`).\n *\n * No-ops in production (the HMR channel is absent; events are dropped in the\n * transport queue).\n */\nexport const addTimelineEvent = (event: TimelineEvent): void => {\n emit(event);\n};\n\ntype ViteHot = {send: (event: string, data: unknown) => void};\n\n/**\n * Register a custom timeline layer and announce it to the panel.\n *\n * The panel adds the layer to its toggle strip after the built-in layers.\n * The registration message is sent once on first call; duplicate ids are\n * ignored.\n */\nexport const addTimelineLayer = (layer: TimelineLayer): void => {\n const send = (): void => {\n (import.meta as {hot?: ViteHot}).hot?.send('lit:timeline:custom-layer', {\n layer,\n });\n };\n setHotClientCallback(send);\n};\n"]}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ */
6
+ /**
7
+ * lit-render timeline layer — listens to the built-in lit-debug CustomEvents
8
+ * instead of patching prototypes, so it's zero-cost when the Lit debug flag
9
+ * is off and perfectly accurate for the render layer.
10
+ *
11
+ * `globalThis.emitLitDebugLogEvents = true` gates the events, and lit-html
12
+ * dispatches a CustomEvent per render whenever it's set — a real per-render
13
+ * cost. So the caller drives the flag via {@link setRenderDebugEnabled} from
14
+ * `recording × layer-enabled` rather than leaving it on for the whole session.
15
+ * The events are tagged `*Unstable` in the Lit source; we tolerate missing
16
+ * `kind` values gracefully.
17
+ *
18
+ * begin render / end render share a numeric `id` → we use it as groupId so
19
+ * the panel can show a duration bar for each render call.
20
+ */
21
+ import type { TimelineEvent } from '../../../types/timeline.js';
22
+ type EmitFn = (event: TimelineEvent) => void;
23
+ type RecordingFn = () => boolean;
24
+ type LayerEnabledFn = () => boolean;
25
+ /**
26
+ * Install the lit-debug render layer.
27
+ * Idempotent — calling again when already installed is a no-op.
28
+ */
29
+ export declare const installRenderLayer: (emit: EmitFn, recording: RecordingFn, layerEnabled: LayerEnabledFn) => void;
30
+ /**
31
+ * Toggle Lit's debug event system. Enabling makes lit-html dispatch a
32
+ * CustomEvent on every render (dev-only; no-op in prod builds), so the caller
33
+ * keeps it off unless the render layer is actively recording.
34
+ */
35
+ export declare const setRenderDebugEnabled: (enabled: boolean) => void;
36
+ /** Remove the listener and clear the Lit debug flag. */
37
+ export declare const uninstallRenderLayer: () => void;
38
+ export {};
39
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../../../packages/labs/vite-plugin-lit/src/lib/runtime/timeline/render.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AAI9D,KAAK,MAAM,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;AAC7C,KAAK,WAAW,GAAG,MAAM,OAAO,CAAC;AACjC,KAAK,cAAc,GAAG,MAAM,OAAO,CAAC;AAmGpC;;;GAGG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,MAAM,EACZ,WAAW,WAAW,EACtB,cAAc,cAAc,KAC3B,IASF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,SAAS,OAAO,KAAG,IAGxD,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,oBAAoB,QAAO,IAIvC,CAAC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: BSD-3-Clause
5
+ */
6
+ import { idOf, sourceOf } from './identity.js';
7
+ import { now } from './clock.js';
8
+ /**
9
+ * Builds the element-identity `meta` for a render event from the render
10
+ * `host` (the LitElement whose `render()` produced these debug events).
11
+ * Uses the same id/source maps as the lifecycle layer so an element keeps one
12
+ * stable id across both layers. Returns undefined for host-less renders
13
+ * (e.g. a bare lit-html `render()` call not driven by a LitElement).
14
+ */
15
+ const hostMeta = (host) => {
16
+ if (host === null || typeof host !== 'object')
17
+ return undefined;
18
+ return {
19
+ elementId: idOf(host),
20
+ tagName: host.localName ?? 'unknown',
21
+ source: sourceOf(host),
22
+ };
23
+ };
24
+ let removeListener = null;
25
+ const onLitDebug = (e, emit, recording, layerEnabled) => {
26
+ if (!recording() || !layerEnabled())
27
+ return;
28
+ const detail = e.detail;
29
+ if (!detail?.kind)
30
+ return;
31
+ const time = now();
32
+ const { kind, id } = detail;
33
+ switch (kind) {
34
+ case 'begin render': {
35
+ const meta = hostMeta(detail.options?.host);
36
+ emit({
37
+ layerId: 'lit-render',
38
+ time,
39
+ groupId: id,
40
+ title: 'render:start',
41
+ subtitle: meta?.tagName,
42
+ data: { kind, id },
43
+ meta,
44
+ });
45
+ break;
46
+ }
47
+ case 'end render': {
48
+ const meta = hostMeta(detail.options?.host);
49
+ emit({
50
+ layerId: 'lit-render',
51
+ time,
52
+ groupId: id,
53
+ title: 'render:end',
54
+ subtitle: meta?.tagName,
55
+ data: { kind, id },
56
+ meta,
57
+ });
58
+ break;
59
+ }
60
+ // `template prep` fires once per *unique* template, the first time it's
61
+ // compiled — low volume, useful as a "new template" marker.
62
+ case 'template prep':
63
+ emit({
64
+ layerId: 'lit-render',
65
+ time,
66
+ title: kind,
67
+ data: { kind, id },
68
+ });
69
+ break;
70
+ // `template updating` / `template instantiated` / `…and updated` fire once
71
+ // per template-bound ChildPart on *every* render — extremely high volume
72
+ // (a ticking clock or animation floods the layer). The begin/end render
73
+ // pair above already captures each render as a grouped duration, so these
74
+ // add noise without signal. Skip them; re-expose behind a "verbose" toggle
75
+ // if per-part detail is ever needed. Same rationale for commit * / set part.
76
+ default:
77
+ break;
78
+ }
79
+ };
80
+ /**
81
+ * Install the lit-debug render layer.
82
+ * Idempotent — calling again when already installed is a no-op.
83
+ */
84
+ export const installRenderLayer = (emit, recording, layerEnabled) => {
85
+ if (removeListener !== null)
86
+ return;
87
+ // The listener is cheap and always attached; the per-render cost lives in the
88
+ // `emitLitDebugLogEvents` flag, which the caller toggles via
89
+ // `setRenderDebugEnabled` only while actively capturing.
90
+ const handler = (e) => onLitDebug(e, emit, recording, layerEnabled);
91
+ window.addEventListener('lit-debug', handler);
92
+ removeListener = () => window.removeEventListener('lit-debug', handler);
93
+ };
94
+ /**
95
+ * Toggle Lit's debug event system. Enabling makes lit-html dispatch a
96
+ * CustomEvent on every render (dev-only; no-op in prod builds), so the caller
97
+ * keeps it off unless the render layer is actively recording.
98
+ */
99
+ export const setRenderDebugEnabled = (enabled) => {
100
+ globalThis.emitLitDebugLogEvents =
101
+ enabled;
102
+ };
103
+ /** Remove the listener and clear the Lit debug flag. */
104
+ export const uninstallRenderLayer = () => {
105
+ removeListener?.();
106
+ removeListener = null;
107
+ setRenderDebugEnabled(false);
108
+ };
109
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../../../../packages/labs/vite-plugin-lit/src/lib/runtime/timeline/render.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * lit-render timeline layer — listens to the built-in lit-debug CustomEvents\n * instead of patching prototypes, so it's zero-cost when the Lit debug flag\n * is off and perfectly accurate for the render layer.\n *\n * `globalThis.emitLitDebugLogEvents = true` gates the events, and lit-html\n * dispatches a CustomEvent per render whenever it's set — a real per-render\n * cost. So the caller drives the flag via {@link setRenderDebugEnabled} from\n * `recording × layer-enabled` rather than leaving it on for the whole session.\n * The events are tagged `*Unstable` in the Lit source; we tolerate missing\n * `kind` values gracefully.\n *\n * begin render / end render share a numeric `id` → we use it as groupId so\n * the panel can show a duration bar for each render call.\n */\n\nimport type {TimelineEvent} from '../../../types/timeline.js';\nimport {idOf, sourceOf} from './identity.js';\nimport {now} from './clock.js';\n\ntype EmitFn = (event: TimelineEvent) => void;\ntype RecordingFn = () => boolean;\ntype LayerEnabledFn = () => boolean;\n\n/** Minimal shape of the lit-debug event detail we care about. */\ninterface LitDebugDetail {\n kind: string;\n id?: number;\n template?: unknown;\n instance?: unknown;\n /** Render options passed to lit-html's `render()`; `host` is the element. */\n options?: {host?: unknown};\n}\n\n/**\n * Builds the element-identity `meta` for a render event from the render\n * `host` (the LitElement whose `render()` produced these debug events).\n * Uses the same id/source maps as the lifecycle layer so an element keeps one\n * stable id across both layers. Returns undefined for host-less renders\n * (e.g. a bare lit-html `render()` call not driven by a LitElement).\n */\nconst hostMeta = (\n host: unknown\n): NonNullable<TimelineEvent['meta']> | undefined => {\n if (host === null || typeof host !== 'object') return undefined;\n return {\n elementId: idOf(host),\n tagName: (host as Element).localName ?? 'unknown',\n source: sourceOf(host),\n };\n};\n\ntype LitDebugEvent = CustomEvent<LitDebugDetail>;\n\nlet removeListener: (() => void) | null = null;\n\nconst onLitDebug = (\n e: Event,\n emit: EmitFn,\n recording: RecordingFn,\n layerEnabled: LayerEnabledFn\n): void => {\n if (!recording() || !layerEnabled()) return;\n\n const detail = (e as LitDebugEvent).detail;\n if (!detail?.kind) return;\n\n const time = now();\n const {kind, id} = detail;\n\n switch (kind) {\n case 'begin render': {\n const meta = hostMeta(detail.options?.host);\n emit({\n layerId: 'lit-render',\n time,\n groupId: id,\n title: 'render:start',\n subtitle: meta?.tagName,\n data: {kind, id},\n meta,\n });\n break;\n }\n\n case 'end render': {\n const meta = hostMeta(detail.options?.host);\n emit({\n layerId: 'lit-render',\n time,\n groupId: id,\n title: 'render:end',\n subtitle: meta?.tagName,\n data: {kind, id},\n meta,\n });\n break;\n }\n\n // `template prep` fires once per *unique* template, the first time it's\n // compiled — low volume, useful as a \"new template\" marker.\n case 'template prep':\n emit({\n layerId: 'lit-render',\n time,\n title: kind,\n data: {kind, id},\n });\n break;\n\n // `template updating` / `template instantiated` / `…and updated` fire once\n // per template-bound ChildPart on *every* render — extremely high volume\n // (a ticking clock or animation floods the layer). The begin/end render\n // pair above already captures each render as a grouped duration, so these\n // add noise without signal. Skip them; re-expose behind a \"verbose\" toggle\n // if per-part detail is ever needed. Same rationale for commit * / set part.\n default:\n break;\n }\n};\n\n/**\n * Install the lit-debug render layer.\n * Idempotent — calling again when already installed is a no-op.\n */\nexport const installRenderLayer = (\n emit: EmitFn,\n recording: RecordingFn,\n layerEnabled: LayerEnabledFn\n): void => {\n if (removeListener !== null) return;\n\n // The listener is cheap and always attached; the per-render cost lives in the\n // `emitLitDebugLogEvents` flag, which the caller toggles via\n // `setRenderDebugEnabled` only while actively capturing.\n const handler = (e: Event) => onLitDebug(e, emit, recording, layerEnabled);\n window.addEventListener('lit-debug', handler);\n removeListener = () => window.removeEventListener('lit-debug', handler);\n};\n\n/**\n * Toggle Lit's debug event system. Enabling makes lit-html dispatch a\n * CustomEvent on every render (dev-only; no-op in prod builds), so the caller\n * keeps it off unless the render layer is actively recording.\n */\nexport const setRenderDebugEnabled = (enabled: boolean): void => {\n (globalThis as {emitLitDebugLogEvents?: boolean}).emitLitDebugLogEvents =\n enabled;\n};\n\n/** Remove the listener and clear the Lit debug flag. */\nexport const uninstallRenderLayer = (): void => {\n removeListener?.();\n removeListener = null;\n setRenderDebugEnabled(false);\n};\n"]}