@oddsquad/vite-plugin-lit 0.3.0 → 0.5.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 (124) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/README.md +26 -11
  3. package/bin.mjs +16 -12
  4. package/dist/client/assets/{browser-agent-rpc-BXhoSh1z-BvWe4sTJ.js → browser-agent-rpc-BXhoSh1z-CRHFLjFo.js} +2 -2
  5. package/dist/client/assets/{browser-agent-rpc-BXhoSh1z-BvWe4sTJ.js.map → browser-agent-rpc-BXhoSh1z-CRHFLjFo.js.map} +1 -1
  6. package/dist/client/assets/flow-DK8vmxvN.js +745 -0
  7. package/dist/client/assets/flow-DK8vmxvN.js.map +1 -0
  8. package/dist/client/assets/{index-CFrVgNfS.js → index-Gn4Hk_9s.js} +2749 -793
  9. package/dist/client/assets/index-Gn4Hk_9s.js.map +1 -0
  10. package/dist/client/index.html +11 -1
  11. package/lib/devframe/definition.d.ts +7 -0
  12. package/lib/devframe/definition.d.ts.map +1 -1
  13. package/lib/devframe/definition.js +56 -11
  14. package/lib/devframe/definition.js.map +1 -1
  15. package/lib/devframe/launch-editor.d.ts +20 -0
  16. package/lib/devframe/launch-editor.d.ts.map +1 -0
  17. package/lib/devframe/launch-editor.js +40 -0
  18. package/lib/devframe/launch-editor.js.map +1 -0
  19. package/lib/devframe/protocol.d.ts +31 -4
  20. package/lib/devframe/protocol.d.ts.map +1 -1
  21. package/lib/devframe/protocol.js +22 -3
  22. package/lib/devframe/protocol.js.map +1 -1
  23. package/lib/devframe/rpc-source.d.ts +44 -0
  24. package/lib/devframe/rpc-source.d.ts.map +1 -0
  25. package/lib/devframe/rpc-source.js +150 -0
  26. package/lib/devframe/rpc-source.js.map +1 -0
  27. package/lib/devframe/source.d.ts +12 -0
  28. package/lib/devframe/source.d.ts.map +1 -1
  29. package/lib/devframe/source.js +12 -0
  30. package/lib/devframe/source.js.map +1 -1
  31. package/lib/devframe/vite.d.ts +2 -0
  32. package/lib/devframe/vite.d.ts.map +1 -1
  33. package/lib/devframe/vite.js +3 -8
  34. package/lib/devframe/vite.js.map +1 -1
  35. package/lib/options.d.ts +136 -0
  36. package/lib/options.d.ts.map +1 -0
  37. package/lib/options.js +175 -0
  38. package/lib/options.js.map +1 -0
  39. package/lib/override-baselines.d.ts +17 -0
  40. package/lib/override-baselines.d.ts.map +1 -0
  41. package/lib/override-baselines.js +25 -0
  42. package/lib/override-baselines.js.map +1 -0
  43. package/lib/plugin.d.ts +6 -148
  44. package/lib/plugin.d.ts.map +1 -1
  45. package/lib/plugin.js +22 -477
  46. package/lib/plugin.js.map +1 -1
  47. package/lib/plugins/css-literals.d.ts +15 -0
  48. package/lib/plugins/css-literals.d.ts.map +1 -0
  49. package/lib/plugins/css-literals.js +154 -0
  50. package/lib/plugins/css-literals.js.map +1 -0
  51. package/lib/plugins/css-queries.d.ts +20 -0
  52. package/lib/plugins/css-queries.d.ts.map +1 -0
  53. package/lib/plugins/css-queries.js +139 -0
  54. package/lib/plugins/css-queries.js.map +1 -0
  55. package/lib/plugins/open-in-editor.d.ts +16 -0
  56. package/lib/plugins/open-in-editor.d.ts.map +1 -0
  57. package/lib/plugins/open-in-editor.js +96 -0
  58. package/lib/plugins/open-in-editor.js.map +1 -0
  59. package/lib/plugins/shared.d.ts +13 -0
  60. package/lib/plugins/shared.d.ts.map +1 -0
  61. package/lib/plugins/shared.js +23 -0
  62. package/lib/plugins/shared.js.map +1 -0
  63. package/lib/plugins/timeline-virtual.d.ts +19 -0
  64. package/lib/plugins/timeline-virtual.d.ts.map +1 -0
  65. package/lib/plugins/timeline-virtual.js +53 -0
  66. package/lib/plugins/timeline-virtual.js.map +1 -0
  67. package/lib/runtime/css.d.ts.map +1 -1
  68. package/lib/runtime/css.js +9 -2
  69. package/lib/runtime/css.js.map +1 -1
  70. package/lib/runtime/indicator.js.map +1 -1
  71. package/lib/runtime/inspector/install.js +65 -13
  72. package/lib/runtime/inspector/install.js.map +1 -1
  73. package/lib/runtime/inspector/serialize.d.ts.map +1 -1
  74. package/lib/runtime/inspector/serialize.js +20 -0
  75. package/lib/runtime/inspector/serialize.js.map +1 -1
  76. package/lib/runtime/overrides.d.ts +4 -10
  77. package/lib/runtime/overrides.d.ts.map +1 -1
  78. package/lib/runtime/overrides.js +5 -3
  79. package/lib/runtime/overrides.js.map +1 -1
  80. package/lib/runtime/page-channel.d.ts +52 -0
  81. package/lib/runtime/page-channel.d.ts.map +1 -0
  82. package/lib/runtime/page-channel.js +67 -0
  83. package/lib/runtime/page-channel.js.map +1 -0
  84. package/lib/runtime/patch.d.ts.map +1 -1
  85. package/lib/runtime/patch.js +2 -1
  86. package/lib/runtime/patch.js.map +1 -1
  87. package/lib/runtime/rpc-transport.d.ts +46 -0
  88. package/lib/runtime/rpc-transport.d.ts.map +1 -0
  89. package/lib/runtime/rpc-transport.js +82 -0
  90. package/lib/runtime/rpc-transport.js.map +1 -0
  91. package/lib/runtime/source-overlay/overlay-element.d.ts.map +1 -1
  92. package/lib/runtime/source-overlay/overlay-element.js +30 -7
  93. package/lib/runtime/source-overlay/overlay-element.js.map +1 -1
  94. package/lib/runtime/timeline/install.js +25 -9
  95. package/lib/runtime/timeline/install.js.map +1 -1
  96. package/lib/runtime/timeline/public-api.d.ts +2 -2
  97. package/lib/runtime/timeline/public-api.d.ts.map +1 -1
  98. package/lib/runtime/timeline/public-api.js +13 -5
  99. package/lib/runtime/timeline/public-api.js.map +1 -1
  100. package/lib/runtime/timeline/render.d.ts +15 -6
  101. package/lib/runtime/timeline/render.d.ts.map +1 -1
  102. package/lib/runtime/timeline/render.js +150 -11
  103. package/lib/runtime/timeline/render.js.map +1 -1
  104. package/lib/runtime/timeline/transport.d.ts +6 -9
  105. package/lib/runtime/timeline/transport.d.ts.map +1 -1
  106. package/lib/runtime/timeline/transport.js.map +1 -1
  107. package/lib/timeline/filter.d.ts +38 -0
  108. package/lib/timeline/filter.d.ts.map +1 -0
  109. package/lib/timeline/filter.js +44 -0
  110. package/lib/timeline/filter.js.map +1 -0
  111. package/lib/timeline/tracks.d.ts +113 -0
  112. package/lib/timeline/tracks.d.ts.map +1 -0
  113. package/lib/timeline/tracks.js +168 -0
  114. package/lib/timeline/tracks.js.map +1 -0
  115. package/package.json +9 -1
  116. package/types/timeline.d.ts +50 -1
  117. package/types/timeline.d.ts.map +1 -1
  118. package/types/timeline.js +4 -0
  119. package/types/timeline.js.map +1 -1
  120. package/dist/client/assets/index-CFrVgNfS.js.map +0 -1
  121. package/lib/timeline-plugin.d.ts +0 -72
  122. package/lib/timeline-plugin.d.ts.map +0 -1
  123. package/lib/timeline-plugin.js +0 -359
  124. package/lib/timeline-plugin.js.map +0 -1
@@ -1044,103 +1044,12 @@ var setColorSchemePreference = (pref) => {
1044
1044
  localStorage.setItem(COLOR_SCHEME_LS_KEY, pref);
1045
1045
  } catch {}
1046
1046
  applyColorScheme(pref);
1047
- };
1048
- //#endregion
1049
- //#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorate.js
1050
- function __decorate(decorators, target, key, desc) {
1051
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1052
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1053
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1054
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1055
- }
1056
- //#endregion
1057
- //#region src/panel/timeline-layers.ts
1058
- /**
1059
- * @license
1060
- * Copyright 2026 Google LLC
1061
- * SPDX-License-Identifier: BSD-3-Clause
1062
- */
1063
- var TimelineLayers = class TimelineLayers extends i$2 {
1064
- constructor(..._args) {
1065
- super(..._args);
1066
- this.layers = [];
1067
- }
1068
- static {
1069
- this.styles = [tokens, i$5`
1070
- :host {
1071
- display: flex;
1072
- gap: var(--lit-devtools-space-2);
1073
- padding: var(--lit-devtools-space-3) var(--lit-devtools-space-5);
1074
- border-bottom: 1px solid var(--lit-devtools-border);
1075
- background: var(--lit-devtools-surface-low);
1076
- flex-shrink: 0;
1077
- flex-wrap: wrap;
1078
- }
1079
- button {
1080
- display: flex;
1081
- align-items: center;
1082
- gap: var(--lit-devtools-space-2);
1083
- padding: var(--lit-devtools-space-2) var(--lit-devtools-space-4);
1084
- border-radius: var(--lit-devtools-radius-pill);
1085
- background: var(--lit-devtools-surface-elevated);
1086
- border: 1px solid transparent;
1087
- color: var(--lit-devtools-text-secondary);
1088
- font-size: var(--lit-devtools-text-2xs);
1089
- cursor: pointer;
1090
- user-select: none;
1091
- transition: opacity var(--lit-devtools-dur-fast)
1092
- var(--lit-devtools-ease-standard);
1093
- }
1094
- button.on {
1095
- color: var(--lit-devtools-text);
1096
- }
1097
- button:hover {
1098
- background: var(--lit-devtools-surface-hover);
1099
- }
1100
- .dot {
1101
- width: 8px;
1102
- height: 8px;
1103
- border-radius: 50%;
1104
- flex-shrink: 0;
1105
- opacity: 0.4;
1106
- }
1107
- button.on .dot {
1108
- opacity: 1;
1109
- }
1110
- `];
1111
- }
1112
- _toggle(id) {
1113
- this.dispatchEvent(new CustomEvent("layer-toggle", {
1114
- detail: { id },
1115
- bubbles: true,
1116
- composed: true
1117
- }));
1118
- }
1119
- render() {
1120
- return b`${this.layers.map((l) => b`
1121
- <button
1122
- class=${l.enabled ? "on" : ""}
1123
- title=${l.enabled ? `Hide ${l.label}` : `Show ${l.label}`}
1124
- @click=${() => this._toggle(l.id)}
1125
- >
1126
- <span
1127
- class="dot"
1128
- style=${"background:#" + l.color.toString(16).padStart(6, "0")}
1129
- ></span>
1130
- ${l.label}
1131
- </button>
1132
- `)}`;
1133
- }
1134
- };
1135
- __decorate([n$3({ type: Array })], TimelineLayers.prototype, "layers", void 0);
1136
- TimelineLayers = __decorate([t$2("timeline-layers")], TimelineLayers);
1137
- //#endregion
1138
- //#region node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/directive-helpers.js
1139
- /**
1140
- * @license
1141
- * Copyright 2020 Google LLC
1142
- * SPDX-License-Identifier: BSD-3-Clause
1143
- */ var { I: t$1 } = j$1, i$1 = (o) => o, r$1 = (o) => void 0 === o.strings, s$1 = () => document.createComment(""), v = (o, n, e) => {
1047
+ }, { I: t$1 } = j$1, i$1 = (o) => o, r$1 = (o) => void 0 === o.strings, s$1 = () => document.createComment(""), v = (o, n, e) => {
1048
+ /**
1049
+ * @license
1050
+ * Copyright 2020 Google LLC
1051
+ * SPDX-License-Identifier: BSD-3-Clause
1052
+ */
1144
1053
  const l = o._$AA.parentNode, d = void 0 === n ? o._$AB : n._$AA;
1145
1054
  if (void 0 === e) e = new t$1(l.insertBefore(s$1(), d), l.insertBefore(s$1(), d), o, o.options);
1146
1055
  else {
@@ -1289,67 +1198,6 @@ var n = e$1(class extends f {
1289
1198
  }
1290
1199
  });
1291
1200
  //#endregion
1292
- //#region node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/directives/repeat.js
1293
- /**
1294
- * @license
1295
- * Copyright 2017 Google LLC
1296
- * SPDX-License-Identifier: BSD-3-Clause
1297
- */
1298
- var u = (e, s, t) => {
1299
- const r = /* @__PURE__ */ new Map();
1300
- for (let l = s; l <= t; l++) r.set(e[l], l);
1301
- return r;
1302
- };
1303
- var c = e$1(class extends i {
1304
- constructor(e) {
1305
- if (super(e), e.type !== t.CHILD) throw Error("repeat() can only be used in text expressions");
1306
- }
1307
- dt(e, s, t) {
1308
- let r;
1309
- void 0 === t ? t = s : void 0 !== s && (r = s);
1310
- const l = [], o = [];
1311
- let i = 0;
1312
- for (const s of e) l[i] = r ? r(s, i) : i, o[i] = t(s, i), i++;
1313
- return {
1314
- values: o,
1315
- keys: l
1316
- };
1317
- }
1318
- render(e, s, t) {
1319
- return this.dt(e, s, t).values;
1320
- }
1321
- update(s, [t, r, c]) {
1322
- const d = M(s), { values: p$3, keys: a } = this.dt(t, r, c);
1323
- if (!Array.isArray(d)) return this.ut = a, p$3;
1324
- const h = this.ut ??= [], v$2 = [];
1325
- let m, y, x = 0, j = d.length - 1, k = 0, w = p$3.length - 1;
1326
- for (; x <= j && k <= w;) if (null === d[x]) x++;
1327
- else if (null === d[j]) j--;
1328
- else if (h[x] === a[k]) v$2[k] = u$1(d[x], p$3[k]), x++, k++;
1329
- else if (h[j] === a[w]) v$2[w] = u$1(d[j], p$3[w]), j--, w--;
1330
- else if (h[x] === a[w]) v$2[w] = u$1(d[x], p$3[w]), v(s, v$2[w + 1], d[x]), x++, w--;
1331
- else if (h[j] === a[k]) v$2[k] = u$1(d[j], p$3[k]), v(s, d[x], d[j]), j--, k++;
1332
- else if (void 0 === m && (m = u(a, k, w), y = u(h, x, j)), m.has(h[x])) if (m.has(h[j])) {
1333
- const e = y.get(a[k]), t = void 0 !== e ? d[e] : null;
1334
- if (null === t) {
1335
- const e = v(s, d[x]);
1336
- u$1(e, p$3[k]), v$2[k] = e;
1337
- } else v$2[k] = u$1(t, p$3[k]), v(s, d[x], t), d[e] = null;
1338
- k++;
1339
- } else h$2(d[j]), j--;
1340
- else h$2(d[x]), x++;
1341
- for (; k <= w;) {
1342
- const e = v(s, v$2[w + 1]);
1343
- u$1(e, p$3[k]), v$2[k++] = e;
1344
- }
1345
- for (; x <= j;) {
1346
- const e = d[x++];
1347
- null !== e && h$2(e);
1348
- }
1349
- return this.ut = a, p(s, v$2), E;
1350
- }
1351
- });
1352
- //#endregion
1353
1201
  //#region src/lib/timeline/derive.ts
1354
1202
  /**
1355
1203
  * @license
@@ -1560,105 +1408,1353 @@ var attributeInput = (cycles, events, windowMs = 200) => {
1560
1408
  });
1561
1409
  };
1562
1410
  //#endregion
1563
- //#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/utils/colors.mjs
1564
- function makeColor(open, close) {
1565
- const o = `\x1B[${open}m`;
1566
- const c = `\x1B[${close}m`;
1567
- return ((arg, ...values) => {
1568
- if (Array.isArray(arg) && "raw" in arg) {
1569
- const strings = arg;
1570
- let out = "";
1571
- for (let i = 0; i < strings.length; i++) {
1572
- out += strings[i];
1573
- if (i < values.length) out += String(values[i]);
1574
- }
1575
- return `${o}${out}${c}`;
1576
- }
1577
- return `${o}${String(arg)}${c}`;
1578
- });
1579
- }
1580
- var colors = {
1581
- blue: makeColor(34, 39),
1582
- cyan: makeColor(36, 39),
1583
- gray: makeColor(90, 39),
1584
- green: makeColor(32, 39),
1585
- red: makeColor(31, 39),
1586
- yellow: makeColor(33, 39),
1587
- bold: makeColor(1, 22),
1588
- dim: makeColor(2, 22),
1589
- reset: makeColor(0, 0),
1590
- underline: makeColor(4, 24)
1591
- };
1592
- //#endregion
1593
- //#region node_modules/.pnpm/nostics@1.2.0/node_modules/nostics/dist/index.mjs
1411
+ //#region src/lib/timeline/filter.ts
1594
1412
  /**
1595
- * Transforms a value or a function that returns a value to a value.
1596
- *
1597
- * @param valFn either a value or a function that returns a value
1598
- * @param args arguments to pass to the function if `valFn` is a function
1599
- *
1600
- * @internal
1413
+ * @license
1414
+ * Copyright 2026 Google LLC
1415
+ * SPDX-License-Identifier: BSD-3-Clause
1601
1416
  */
1602
- function toValueWithArgs(valFn, ...args) {
1603
- return typeof valFn === "function" ? valFn(...args) : valFn;
1604
- }
1605
- var captureStackTrace = Error.captureStackTrace;
1606
- var Diagnostic = class Diagnostic extends Error {
1607
- name;
1608
- /**
1609
- * The diagnostic code, e.g. `MATH_E001`.
1610
- * Also appears as the `name` property.
1611
- */
1612
- code;
1613
- /**
1614
- * URL to extended documentation for this diagnostic code.
1615
- * Auto-generated from {@link DefineDiagnosticsOptions.docsBase}.
1616
- */
1617
- docs;
1618
- /**
1619
- * Optional actionable instructions on how to resolve the problem.
1620
- */
1621
- fix;
1622
- /**
1623
- * Locations in user code that contributed to this diagnostic, in
1624
- * `file:line:column` format. Relevant when the stack trace doesn't reflect
1625
- * the user's source (e.g. compilers, bundlers), otherwise redundant with the
1626
- * stack and should be omitted.
1627
- */
1628
- sources;
1629
- /**
1630
- * Alias for {@link Error.message}: the reason this diagnostic was raised.
1631
- */
1632
- get why() {
1633
- return this.message;
1634
- }
1635
- /**
1636
- * @param init structured initializer; `why` is required
1637
- * @param captureFrom V8 stack-cutoff frame. Defaults to {@link Diagnostic}
1638
- * so the top of the trace is the `new Diagnostic(...)` call site.
1639
- * `defineDiagnostics` passes its action method to strip its own frames too.
1640
- * Ignored on engines without `Error.captureStackTrace`.
1641
- */
1642
- constructor(init, captureFrom = Diagnostic) {
1643
- super(init.why, { cause: init.cause });
1644
- this.code = this.name = init.code;
1645
- this.fix = init.fix;
1646
- this.docs = init.docs;
1647
- this.sources = init.sources;
1648
- captureStackTrace?.(this, captureFrom);
1649
- }
1650
- /**
1651
- * Converts the diagnostic into a serializable structured object.
1652
- */
1653
- toJSON() {
1417
+ /**
1418
+ * Compiles the regex box. An empty box matches everything. A pattern that
1419
+ * does not compile (the user is mid-typing `foo(`) disables the filter rather
1420
+ * than hiding every row, and is reported as `invalid` so the input can say so.
1421
+ */
1422
+ var compileRegex = (source) => {
1423
+ if (source === "") return {
1424
+ re: null,
1425
+ invalid: false
1426
+ };
1427
+ try {
1654
1428
  return {
1655
- name: this.name,
1656
- why: this.why,
1657
- fix: this.fix,
1658
- docs: this.docs,
1659
- sources: this.sources,
1660
- cause: this.cause,
1661
- stack: this.stack
1429
+ re: new RegExp(source, "i"),
1430
+ invalid: false
1431
+ };
1432
+ } catch {
1433
+ return {
1434
+ re: null,
1435
+ invalid: true
1436
+ };
1437
+ }
1438
+ };
1439
+ /** Text the regex filter searches: element tag, name, subtitle and the
1440
+ * changed property keys (the reason is worth searching for by name). */
1441
+ var spanHaystack = (span) => `${span.meta?.tagName ?? ""} ${span.name} ${span.subtitle ?? ""} ${(span.changed ?? []).join(" ")}`;
1442
+ /**
1443
+ * Applies the element and regex filters. `re` comes from {@link compileRegex}
1444
+ * so a caller filtering on every keystroke compiles once, not per span.
1445
+ */
1446
+ var filterSpans = (spans, elementId, re) => spans.filter((span) => (elementId === null || span.meta?.elementId === elementId) && (re === null || re.test(spanHaystack(span))));
1447
+ /** Distinct elements (by stable id) seen across the recorded events. */
1448
+ var listElements = (events) => {
1449
+ const seen = /* @__PURE__ */ new Map();
1450
+ for (const ev of events) {
1451
+ const id = ev.meta?.elementId;
1452
+ if (id != null && !seen.has(id)) seen.set(id, ev.meta?.tagName ?? "unknown");
1453
+ }
1454
+ return [...seen].map(([id, tag]) => ({
1455
+ id,
1456
+ tag
1457
+ }));
1458
+ };
1459
+ //#endregion
1460
+ //#region \0@oxc-project+runtime@0.149.0/helpers/esm/decorate.js
1461
+ function __decorate(decorators, target, key, desc) {
1462
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1463
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1464
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1465
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1466
+ }
1467
+ //#endregion
1468
+ //#region src/lib/segmented-tabs.ts
1469
+ /**
1470
+ * @license
1471
+ * Copyright 2026 Google LLC
1472
+ * SPDX-License-Identifier: BSD-3-Clause
1473
+ */
1474
+ var SegmentedTabs = class SegmentedTabs extends i$2 {
1475
+ static {
1476
+ this.styles = [tokens, i$5`
1477
+ :host {
1478
+ display: flex;
1479
+ gap: var(--lit-devtools-space-1);
1480
+ align-self: stretch;
1481
+ }
1482
+ button {
1483
+ appearance: none;
1484
+ display: flex;
1485
+ align-items: center;
1486
+ gap: var(--lit-devtools-space-2);
1487
+ border: 0;
1488
+ background: none;
1489
+ font: inherit;
1490
+ color: var(--lit-devtools-text-muted);
1491
+ padding: 0 var(--lit-devtools-space-5);
1492
+ cursor: pointer;
1493
+ border-bottom: 2px solid transparent;
1494
+ white-space: nowrap;
1495
+ transition:
1496
+ color var(--lit-devtools-dur-fast) var(--lit-devtools-ease-standard),
1497
+ border-color var(--lit-devtools-dur-fast)
1498
+ var(--lit-devtools-ease-standard),
1499
+ background var(--lit-devtools-dur-fast)
1500
+ var(--lit-devtools-ease-standard);
1501
+ }
1502
+ button:hover {
1503
+ color: var(--lit-devtools-text);
1504
+ background: var(--lit-devtools-surface-hover);
1505
+ }
1506
+ button.active {
1507
+ color: var(--lit-devtools-accent);
1508
+ border-bottom-color: var(--lit-devtools-accent);
1509
+ }
1510
+ button:focus-visible {
1511
+ outline: 2px solid var(--lit-devtools-accent-ring);
1512
+ outline-offset: -2px;
1513
+ }
1514
+ button svg {
1515
+ width: 18px;
1516
+ height: 18px;
1517
+ flex-shrink: 0;
1518
+ }
1519
+ .badge {
1520
+ flex-shrink: 0;
1521
+ min-width: 16px;
1522
+ padding: 0 var(--lit-devtools-space-1);
1523
+ border-radius: var(--lit-devtools-radius-pill);
1524
+ background: var(--lit-devtools-error-soft);
1525
+ color: var(--lit-devtools-error);
1526
+ font-size: var(--lit-devtools-text-xs);
1527
+ line-height: 16px;
1528
+ text-align: center;
1529
+ }
1530
+ :host([size='sm']) button {
1531
+ font-size: var(--lit-devtools-text-xs);
1532
+ padding: 0 var(--lit-devtools-space-4);
1533
+ }
1534
+ :host([size='md']) button,
1535
+ :host(:not([size])) button {
1536
+ font-size: var(--lit-devtools-text-sm);
1537
+ padding: 0 var(--lit-devtools-space-5);
1538
+ }
1539
+ `];
1540
+ }
1541
+ constructor() {
1542
+ super();
1543
+ this.items = [];
1544
+ this.value = "";
1545
+ this.size = "md";
1546
+ }
1547
+ _select(id) {
1548
+ this.value = id;
1549
+ this.dispatchEvent(new CustomEvent("change", {
1550
+ detail: { value: id },
1551
+ bubbles: true,
1552
+ composed: true
1553
+ }));
1554
+ }
1555
+ render() {
1556
+ return b`
1557
+ ${this.items.map((item) => b`
1558
+ <button
1559
+ role="tab"
1560
+ aria-selected=${item.id === this.value}
1561
+ class=${item.id === this.value ? "active" : ""}
1562
+ @click=${() => this._select(item.id)}
1563
+ >
1564
+ ${item.icon ? b`<svg
1565
+ viewBox="0 0 256 256"
1566
+ fill="currentColor"
1567
+ aria-hidden="true"
1568
+ >
1569
+ <path d=${item.icon} />
1570
+ </svg>` : A}
1571
+ ${item.label}
1572
+ ${item.badge ? b`<span class="badge">${item.badge}</span>` : A}
1573
+ </button>
1574
+ `)}
1575
+ `;
1576
+ }
1577
+ };
1578
+ __decorate([n$3({ type: Array })], SegmentedTabs.prototype, "items", void 0);
1579
+ __decorate([n$3()], SegmentedTabs.prototype, "value", void 0);
1580
+ __decorate([n$3()], SegmentedTabs.prototype, "size", void 0);
1581
+ SegmentedTabs = __decorate([t$2("segmented-tabs")], SegmentedTabs);
1582
+ //#endregion
1583
+ //#region src/panel/timeline-layers.ts
1584
+ /**
1585
+ * @license
1586
+ * Copyright 2026 Google LLC
1587
+ * SPDX-License-Identifier: BSD-3-Clause
1588
+ */
1589
+ /** A layer's `0xRRGGBB` colour as a CSS hex string. */
1590
+ var hexColor = (color) => "#" + color.toString(16).padStart(6, "0");
1591
+ /** CSS colour of layer `id`, grey for a layer `layers` does not know. */
1592
+ var layerColor = (layers, id) => {
1593
+ const layer = layers.find((l) => l.id === id);
1594
+ return layer ? hexColor(layer.color) : "#888";
1595
+ };
1596
+ var TimelineLayers = class TimelineLayers extends i$2 {
1597
+ constructor(..._args) {
1598
+ super(..._args);
1599
+ this.layers = [];
1600
+ this.caption = "";
1601
+ }
1602
+ static {
1603
+ this.styles = [tokens, i$5`
1604
+ :host {
1605
+ display: flex;
1606
+ gap: var(--lit-devtools-space-2);
1607
+ padding: var(--lit-devtools-space-3) var(--lit-devtools-space-5);
1608
+ border-bottom: 1px solid var(--lit-devtools-border);
1609
+ background: var(--lit-devtools-surface-low);
1610
+ flex-shrink: 0;
1611
+ flex-wrap: wrap;
1612
+ }
1613
+ button {
1614
+ display: flex;
1615
+ align-items: center;
1616
+ gap: var(--lit-devtools-space-2);
1617
+ padding: var(--lit-devtools-space-2) var(--lit-devtools-space-4);
1618
+ border-radius: var(--lit-devtools-radius-pill);
1619
+ background: var(--lit-devtools-surface-elevated);
1620
+ border: 1px solid transparent;
1621
+ color: var(--lit-devtools-text-secondary);
1622
+ font-size: var(--lit-devtools-text-2xs);
1623
+ cursor: pointer;
1624
+ user-select: none;
1625
+ transition: opacity var(--lit-devtools-dur-fast)
1626
+ var(--lit-devtools-ease-standard);
1627
+ }
1628
+ button.on {
1629
+ color: var(--lit-devtools-text);
1630
+ }
1631
+ button:hover {
1632
+ background: var(--lit-devtools-surface-hover);
1633
+ }
1634
+ .caption {
1635
+ align-self: center;
1636
+ color: var(--lit-devtools-text-muted);
1637
+ font-size: var(--lit-devtools-text-2xs);
1638
+ margin-right: var(--lit-devtools-space-2);
1639
+ }
1640
+ .dot {
1641
+ width: 8px;
1642
+ height: 8px;
1643
+ border-radius: 50%;
1644
+ flex-shrink: 0;
1645
+ opacity: 0.4;
1646
+ }
1647
+ button.on .dot {
1648
+ opacity: 1;
1649
+ }
1650
+ `];
1651
+ }
1652
+ _toggle(id) {
1653
+ this.dispatchEvent(new CustomEvent("layer-toggle", {
1654
+ detail: { id },
1655
+ bubbles: true,
1656
+ composed: true
1657
+ }));
1658
+ }
1659
+ render() {
1660
+ return b`${this.caption ? b`<span class="caption">${this.caption}</span>` : A}${this.layers.map((l) => b`
1661
+ <button
1662
+ class=${l.enabled ? "on" : ""}
1663
+ title=${l.enabled ? `Hide ${l.label}` : `Show ${l.label}`}
1664
+ @click=${() => this._toggle(l.id)}
1665
+ >
1666
+ <span class="dot" style=${"background:" + hexColor(l.color)}></span>
1667
+ ${l.label}
1668
+ </button>
1669
+ `)}`;
1670
+ }
1671
+ };
1672
+ __decorate([n$3({ type: Array })], TimelineLayers.prototype, "layers", void 0);
1673
+ __decorate([n$3()], TimelineLayers.prototype, "caption", void 0);
1674
+ TimelineLayers = __decorate([t$2("timeline-layers")], TimelineLayers);
1675
+ //#endregion
1676
+ //#region node_modules/.pnpm/lit-html@3.3.3/node_modules/lit-html/directives/repeat.js
1677
+ /**
1678
+ * @license
1679
+ * Copyright 2017 Google LLC
1680
+ * SPDX-License-Identifier: BSD-3-Clause
1681
+ */
1682
+ var u = (e, s, t) => {
1683
+ const r = /* @__PURE__ */ new Map();
1684
+ for (let l = s; l <= t; l++) r.set(e[l], l);
1685
+ return r;
1686
+ };
1687
+ var c = e$1(class extends i {
1688
+ constructor(e) {
1689
+ if (super(e), e.type !== t.CHILD) throw Error("repeat() can only be used in text expressions");
1690
+ }
1691
+ dt(e, s, t) {
1692
+ let r;
1693
+ void 0 === t ? t = s : void 0 !== s && (r = s);
1694
+ const l = [], o = [];
1695
+ let i = 0;
1696
+ for (const s of e) l[i] = r ? r(s, i) : i, o[i] = t(s, i), i++;
1697
+ return {
1698
+ values: o,
1699
+ keys: l
1700
+ };
1701
+ }
1702
+ render(e, s, t) {
1703
+ return this.dt(e, s, t).values;
1704
+ }
1705
+ update(s, [t, r, c]) {
1706
+ const d = M(s), { values: p$3, keys: a } = this.dt(t, r, c);
1707
+ if (!Array.isArray(d)) return this.ut = a, p$3;
1708
+ const h = this.ut ??= [], v$2 = [];
1709
+ let m, y, x = 0, j = d.length - 1, k = 0, w = p$3.length - 1;
1710
+ for (; x <= j && k <= w;) if (null === d[x]) x++;
1711
+ else if (null === d[j]) j--;
1712
+ else if (h[x] === a[k]) v$2[k] = u$1(d[x], p$3[k]), x++, k++;
1713
+ else if (h[j] === a[w]) v$2[w] = u$1(d[j], p$3[w]), j--, w--;
1714
+ else if (h[x] === a[w]) v$2[w] = u$1(d[x], p$3[w]), v(s, v$2[w + 1], d[x]), x++, w--;
1715
+ else if (h[j] === a[k]) v$2[k] = u$1(d[j], p$3[k]), v(s, d[x], d[j]), j--, k++;
1716
+ else if (void 0 === m && (m = u(a, k, w), y = u(h, x, j)), m.has(h[x])) if (m.has(h[j])) {
1717
+ const e = y.get(a[k]), t = void 0 !== e ? d[e] : null;
1718
+ if (null === t) {
1719
+ const e = v(s, d[x]);
1720
+ u$1(e, p$3[k]), v$2[k] = e;
1721
+ } else v$2[k] = u$1(t, p$3[k]), v(s, d[x], t), d[e] = null;
1722
+ k++;
1723
+ } else h$2(d[j]), j--;
1724
+ else h$2(d[x]), x++;
1725
+ for (; k <= w;) {
1726
+ const e = v(s, v$2[w + 1]);
1727
+ u$1(e, p$3[k]), v$2[k++] = e;
1728
+ }
1729
+ for (; x <= j;) {
1730
+ const e = d[x++];
1731
+ null !== e && h$2(e);
1732
+ }
1733
+ return this.ut = a, p(s, v$2), E;
1734
+ }
1735
+ });
1736
+ //#endregion
1737
+ //#region node_modules/.pnpm/@lit-labs+virtualizer@2.1.1/node_modules/@lit-labs/virtualizer/events.js
1738
+ /**
1739
+ * @license
1740
+ * Copyright 2021 Google LLC
1741
+ * SPDX-License-Identifier: BSD-3-Clause
1742
+ */
1743
+ var RangeChangedEvent = class RangeChangedEvent extends Event {
1744
+ constructor(range) {
1745
+ super(RangeChangedEvent.eventName, { bubbles: false });
1746
+ this.first = range.first;
1747
+ this.last = range.last;
1748
+ }
1749
+ };
1750
+ RangeChangedEvent.eventName = "rangeChanged";
1751
+ var VisibilityChangedEvent = class VisibilityChangedEvent extends Event {
1752
+ constructor(range) {
1753
+ super(VisibilityChangedEvent.eventName, { bubbles: false });
1754
+ this.first = range.first;
1755
+ this.last = range.last;
1756
+ }
1757
+ };
1758
+ VisibilityChangedEvent.eventName = "visibilityChanged";
1759
+ var UnpinnedEvent = class UnpinnedEvent extends Event {
1760
+ constructor() {
1761
+ super(UnpinnedEvent.eventName, { bubbles: false });
1762
+ }
1763
+ };
1764
+ UnpinnedEvent.eventName = "unpinned";
1765
+ //#endregion
1766
+ //#region node_modules/.pnpm/@lit-labs+virtualizer@2.1.1/node_modules/@lit-labs/virtualizer/ScrollerController.js
1767
+ /**
1768
+ * @license
1769
+ * Copyright 2021 Google LLC
1770
+ * SPDX-License-Identifier: BSD-3-Clause
1771
+ */
1772
+ var ScrollerShim = class {
1773
+ constructor(element) {
1774
+ this._element = null;
1775
+ const node = element ?? window;
1776
+ this._node = node;
1777
+ if (element) this._element = element;
1778
+ }
1779
+ get element() {
1780
+ return this._element || document.scrollingElement || document.documentElement;
1781
+ }
1782
+ get scrollTop() {
1783
+ return this.element.scrollTop || window.scrollY;
1784
+ }
1785
+ get scrollLeft() {
1786
+ return this.element.scrollLeft || window.scrollX;
1787
+ }
1788
+ get scrollHeight() {
1789
+ return this.element.scrollHeight;
1790
+ }
1791
+ get scrollWidth() {
1792
+ return this.element.scrollWidth;
1793
+ }
1794
+ get viewportHeight() {
1795
+ return this._element ? this._element.getBoundingClientRect().height : window.innerHeight;
1796
+ }
1797
+ get viewportWidth() {
1798
+ return this._element ? this._element.getBoundingClientRect().width : window.innerWidth;
1799
+ }
1800
+ get maxScrollTop() {
1801
+ return this.scrollHeight - this.viewportHeight;
1802
+ }
1803
+ get maxScrollLeft() {
1804
+ return this.scrollWidth - this.viewportWidth;
1805
+ }
1806
+ };
1807
+ var ScrollerController = class extends ScrollerShim {
1808
+ constructor(client, element) {
1809
+ super(element);
1810
+ this._clients = /* @__PURE__ */ new Set();
1811
+ this._retarget = null;
1812
+ this._end = null;
1813
+ this.__destination = null;
1814
+ this.correctingScrollError = false;
1815
+ this._checkForArrival = this._checkForArrival.bind(this);
1816
+ this._updateManagedScrollTo = this._updateManagedScrollTo.bind(this);
1817
+ this.scrollTo = this.scrollTo.bind(this);
1818
+ this.scrollBy = this.scrollBy.bind(this);
1819
+ const node = this._node;
1820
+ this._originalScrollTo = node.scrollTo;
1821
+ this._originalScrollBy = node.scrollBy;
1822
+ this._originalScroll = node.scroll;
1823
+ this._attach(client);
1824
+ }
1825
+ get _destination() {
1826
+ return this.__destination;
1827
+ }
1828
+ get scrolling() {
1829
+ return this._destination !== null;
1830
+ }
1831
+ scrollTo(p1, p2) {
1832
+ const options = typeof p1 === "number" && typeof p2 === "number" ? {
1833
+ left: p1,
1834
+ top: p2
1835
+ } : p1;
1836
+ this._scrollTo(options);
1837
+ }
1838
+ scrollBy(p1, p2) {
1839
+ const options = typeof p1 === "number" && typeof p2 === "number" ? {
1840
+ left: p1,
1841
+ top: p2
1842
+ } : p1;
1843
+ if (options.top !== void 0) options.top += this.scrollTop;
1844
+ if (options.left !== void 0) options.left += this.scrollLeft;
1845
+ this._scrollTo(options);
1846
+ }
1847
+ _nativeScrollTo(options) {
1848
+ this._originalScrollTo.bind(this._element || window)(options);
1849
+ }
1850
+ _scrollTo(options, retarget = null, end = null) {
1851
+ if (this._end !== null) this._end();
1852
+ if (options.behavior === "smooth") {
1853
+ this._setDestination(options);
1854
+ this._retarget = retarget;
1855
+ this._end = end;
1856
+ } else this._resetScrollState();
1857
+ this._nativeScrollTo(options);
1858
+ }
1859
+ _setDestination(options) {
1860
+ let { top, left } = options;
1861
+ top = top === void 0 ? void 0 : Math.max(0, Math.min(top, this.maxScrollTop));
1862
+ left = left === void 0 ? void 0 : Math.max(0, Math.min(left, this.maxScrollLeft));
1863
+ if (this._destination !== null && left === this._destination.left && top === this._destination.top) return false;
1864
+ this.__destination = {
1865
+ top,
1866
+ left,
1867
+ behavior: "smooth"
1868
+ };
1869
+ return true;
1870
+ }
1871
+ _resetScrollState() {
1872
+ this.__destination = null;
1873
+ this._retarget = null;
1874
+ this._end = null;
1875
+ }
1876
+ _updateManagedScrollTo(coordinates) {
1877
+ if (this._destination) {
1878
+ if (this._setDestination(coordinates)) this._nativeScrollTo(this._destination);
1879
+ }
1880
+ }
1881
+ managedScrollTo(options, retarget, end) {
1882
+ this._scrollTo(options, retarget, end);
1883
+ return this._updateManagedScrollTo;
1884
+ }
1885
+ correctScrollError(coordinates) {
1886
+ this.correctingScrollError = true;
1887
+ requestAnimationFrame(() => requestAnimationFrame(() => this.correctingScrollError = false));
1888
+ this._nativeScrollTo(coordinates);
1889
+ if (this._retarget) this._setDestination(this._retarget());
1890
+ if (this._destination) this._nativeScrollTo(this._destination);
1891
+ }
1892
+ _checkForArrival() {
1893
+ if (this._destination !== null) {
1894
+ const { scrollTop, scrollLeft } = this;
1895
+ let { top, left } = this._destination;
1896
+ top = Math.min(top || 0, this.maxScrollTop);
1897
+ left = Math.min(left || 0, this.maxScrollLeft);
1898
+ const topDiff = Math.abs(top - scrollTop);
1899
+ const leftDiff = Math.abs(left - scrollLeft);
1900
+ if (topDiff < 1 && leftDiff < 1) {
1901
+ if (this._end) this._end();
1902
+ this._resetScrollState();
1903
+ }
1904
+ }
1905
+ }
1906
+ detach(client) {
1907
+ this._clients.delete(client);
1908
+ /**
1909
+ * If there aren't any more clients, then return the node's default
1910
+ * scrolling methods
1911
+ */
1912
+ if (this._clients.size === 0) {
1913
+ this._node.scrollTo = this._originalScrollTo;
1914
+ this._node.scrollBy = this._originalScrollBy;
1915
+ this._node.scroll = this._originalScroll;
1916
+ this._node.removeEventListener("scroll", this._checkForArrival);
1917
+ }
1918
+ return null;
1919
+ }
1920
+ _attach(client) {
1921
+ this._clients.add(client);
1922
+ /**
1923
+ * The node should only have the methods shimmed when adding the first
1924
+ * client – otherwise it's redundant
1925
+ */
1926
+ if (this._clients.size === 1) {
1927
+ this._node.scrollTo = this.scrollTo;
1928
+ this._node.scrollBy = this.scrollBy;
1929
+ this._node.scroll = this.scrollTo;
1930
+ this._node.addEventListener("scroll", this._checkForArrival);
1931
+ }
1932
+ }
1933
+ };
1934
+ //#endregion
1935
+ //#region \0vite/preload-helper.js
1936
+ var scriptRel = "modulepreload";
1937
+ var assetsURL = function(dep, importerUrl) {
1938
+ return new URL(dep, importerUrl).href;
1939
+ };
1940
+ var seen = {};
1941
+ var __vitePreload = function preload(baseModule, deps, importerUrl) {
1942
+ let promise = Promise.resolve();
1943
+ if (deps && deps.length > 0) {
1944
+ const links = document.getElementsByTagName("link");
1945
+ const cspNonceMeta = document.querySelector("meta[property=csp-nonce]");
1946
+ const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute("nonce");
1947
+ function allSettled(promises) {
1948
+ return Promise.all(promises.map((p) => Promise.resolve(p).then((value) => ({
1949
+ status: "fulfilled",
1950
+ value
1951
+ }), (reason) => ({
1952
+ status: "rejected",
1953
+ reason
1954
+ }))));
1955
+ }
1956
+ function importMetaResolve(specifier) {
1957
+ if (import.meta.resolve) return import.meta.resolve(specifier);
1958
+ return new URL(
1959
+ specifier,
1960
+ /** #__KEEP__ */
1961
+ import.meta.url
1962
+ ).href;
1963
+ }
1964
+ promise = allSettled(deps.map((dep) => {
1965
+ dep = assetsURL(dep, importerUrl);
1966
+ dep = importMetaResolve(dep);
1967
+ if (dep in seen) return;
1968
+ seen[dep] = true;
1969
+ const isCss = dep.endsWith(".css");
1970
+ for (let i = links.length - 1; i >= 0; i--) {
1971
+ const link = links[i];
1972
+ if (link.href === dep && (!isCss || link.rel === "stylesheet")) return;
1973
+ }
1974
+ const link = document.createElement("link");
1975
+ link.rel = isCss ? "stylesheet" : scriptRel;
1976
+ if (!isCss) link.as = "script";
1977
+ link.crossOrigin = "";
1978
+ link.href = dep;
1979
+ if (cspNonce) link.setAttribute("nonce", cspNonce);
1980
+ document.head.appendChild(link);
1981
+ if (isCss) return new Promise((res, rej) => {
1982
+ link.addEventListener("load", res);
1983
+ link.addEventListener("error", () => rej(/* @__PURE__ */ new Error(`Unable to preload CSS for ${dep}`)));
1984
+ });
1985
+ }).filter((p) => p !== void 0));
1986
+ }
1987
+ function handlePreloadError(err) {
1988
+ const e = new Event("vite:preloadError", { cancelable: true });
1989
+ e.payload = err;
1990
+ window.dispatchEvent(e);
1991
+ if (!e.defaultPrevented) throw err;
1992
+ }
1993
+ return promise.then((res) => {
1994
+ for (const item of res || []) {
1995
+ if (item.status !== "rejected") continue;
1996
+ handlePreloadError(item.reason);
1997
+ }
1998
+ return baseModule().catch(handlePreloadError);
1999
+ });
2000
+ };
2001
+ //#endregion
2002
+ //#region node_modules/.pnpm/@lit-labs+virtualizer@2.1.1/node_modules/@lit-labs/virtualizer/Virtualizer.js
2003
+ /**
2004
+ * @license
2005
+ * Copyright 2021 Google LLC
2006
+ * SPDX-License-Identifier: BSD-3-Clause
2007
+ */
2008
+ var _ResizeObserver = typeof window !== "undefined" ? window.ResizeObserver : void 0;
2009
+ var virtualizerRef = Symbol("virtualizerRef");
2010
+ var SIZER_ATTRIBUTE = "virtualizer-sizer";
2011
+ var DefaultLayoutConstructor;
2012
+ /**
2013
+ * Provides virtual scrolling boilerplate.
2014
+ *
2015
+ * Extensions of this class must set hostElement and layout.
2016
+ *
2017
+ * Extensions of this class must also override VirtualRepeater's DOM
2018
+ * manipulation methods.
2019
+ */
2020
+ var Virtualizer = class {
2021
+ constructor(config) {
2022
+ this._benchmarkStart = null;
2023
+ this._layout = null;
2024
+ this._clippingAncestors = [];
2025
+ /**
2026
+ * Layout provides these values, we set them on _render().
2027
+ * TODO @straversi: Can we find an XOR type, usable for the key here?
2028
+ */
2029
+ this._scrollSize = null;
2030
+ /**
2031
+ * Difference between scroll target's current and required scroll offsets.
2032
+ * Provided by layout.
2033
+ */
2034
+ this._scrollError = null;
2035
+ /**
2036
+ * A list of the positions (top, left) of the children in the current range.
2037
+ */
2038
+ this._childrenPos = null;
2039
+ this._childMeasurements = null;
2040
+ this._toBeMeasured = /* @__PURE__ */ new Map();
2041
+ this._rangeChanged = true;
2042
+ this._itemsChanged = true;
2043
+ this._visibilityChanged = true;
2044
+ this._scrollerController = null;
2045
+ this._isScroller = false;
2046
+ this._sizer = null;
2047
+ /**
2048
+ * Resize observer attached to hostElement.
2049
+ */
2050
+ this._hostElementRO = null;
2051
+ /**
2052
+ * Resize observer attached to children.
2053
+ */
2054
+ this._childrenRO = null;
2055
+ this._mutationObserver = null;
2056
+ this._scrollEventListeners = [];
2057
+ this._scrollEventListenerOptions = { passive: true };
2058
+ this._loadListener = this._childLoaded.bind(this);
2059
+ /**
2060
+ * Index of element to scroll into view, plus scroll
2061
+ * behavior options, as imperatively specified via
2062
+ * `element(index).scrollIntoView()`
2063
+ */
2064
+ this._scrollIntoViewTarget = null;
2065
+ this._updateScrollIntoViewCoordinates = null;
2066
+ /**
2067
+ * Items to render. Set by items.
2068
+ */
2069
+ this._items = [];
2070
+ /**
2071
+ * Index of the first child in the range, not necessarily the first visible child.
2072
+ * TODO @straversi: Consider renaming these.
2073
+ */
2074
+ this._first = -1;
2075
+ /**
2076
+ * Index of the last child in the range.
2077
+ */
2078
+ this._last = -1;
2079
+ /**
2080
+ * Index of the first item intersecting the viewport.
2081
+ */
2082
+ this._firstVisible = -1;
2083
+ /**
2084
+ * Index of the last item intersecting the viewport.
2085
+ */
2086
+ this._lastVisible = -1;
2087
+ this._scheduled = /* @__PURE__ */ new WeakSet();
2088
+ /**
2089
+ * Invoked at the end of each render cycle: children in the range are
2090
+ * measured, and their dimensions passed to this callback. Use it to layout
2091
+ * children as needed.
2092
+ */
2093
+ this._measureCallback = null;
2094
+ this._measureChildOverride = null;
2095
+ /**
2096
+ * State for `layoutComplete` promise
2097
+ */
2098
+ this._layoutCompletePromise = null;
2099
+ this._layoutCompleteResolver = null;
2100
+ this._layoutCompleteRejecter = null;
2101
+ this._pendingLayoutComplete = null;
2102
+ /**
2103
+ * Layout initialization is async because we dynamically load
2104
+ * the default layout if none is specified. This state is to track
2105
+ * whether init is complete.
2106
+ */
2107
+ this._layoutInitialized = null;
2108
+ /**
2109
+ * Track connection state to guard against errors / unnecessary work
2110
+ */
2111
+ this._connected = false;
2112
+ if (!config) throw new Error("Virtualizer constructor requires a configuration object");
2113
+ if (config.hostElement) this._init(config);
2114
+ else throw new Error("Virtualizer configuration requires the \"hostElement\" property");
2115
+ }
2116
+ set items(items) {
2117
+ if (Array.isArray(items) && items !== this._items) {
2118
+ this._itemsChanged = true;
2119
+ this._items = items;
2120
+ this._schedule(this._updateLayout);
2121
+ }
2122
+ }
2123
+ _init(config) {
2124
+ this._isScroller = !!config.scroller;
2125
+ this._initHostElement(config);
2126
+ const layoutConfig = config.layout || {};
2127
+ this._layoutInitialized = this._initLayout(layoutConfig);
2128
+ }
2129
+ _initObservers() {
2130
+ this._mutationObserver = new MutationObserver(this._finishDOMUpdate.bind(this));
2131
+ this._hostElementRO = new _ResizeObserver(() => this._hostElementSizeChanged());
2132
+ this._childrenRO = new _ResizeObserver(this._childrenSizeChanged.bind(this));
2133
+ }
2134
+ _initHostElement(config) {
2135
+ const hostElement = this._hostElement = config.hostElement;
2136
+ this._applyVirtualizerStyles();
2137
+ hostElement[virtualizerRef] = this;
2138
+ }
2139
+ connected() {
2140
+ this._initObservers();
2141
+ const includeSelf = this._isScroller;
2142
+ this._clippingAncestors = getClippingAncestors(this._hostElement, includeSelf);
2143
+ this._scrollerController = new ScrollerController(this, this._clippingAncestors[0]);
2144
+ this._schedule(this._updateLayout);
2145
+ this._observeAndListen();
2146
+ this._connected = true;
2147
+ }
2148
+ _observeAndListen() {
2149
+ this._mutationObserver.observe(this._hostElement, { childList: true });
2150
+ this._hostElementRO.observe(this._hostElement);
2151
+ this._scrollEventListeners.push(window);
2152
+ window.addEventListener("scroll", this, this._scrollEventListenerOptions);
2153
+ this._clippingAncestors.forEach((ancestor) => {
2154
+ ancestor.addEventListener("scroll", this, this._scrollEventListenerOptions);
2155
+ this._scrollEventListeners.push(ancestor);
2156
+ this._hostElementRO.observe(ancestor);
2157
+ });
2158
+ this._hostElementRO.observe(this._scrollerController.element);
2159
+ this._children.forEach((child) => this._childrenRO.observe(child));
2160
+ this._scrollEventListeners.forEach((target) => target.addEventListener("scroll", this, this._scrollEventListenerOptions));
2161
+ }
2162
+ disconnected() {
2163
+ this._scrollEventListeners.forEach((target) => target.removeEventListener("scroll", this, this._scrollEventListenerOptions));
2164
+ this._scrollEventListeners = [];
2165
+ this._clippingAncestors = [];
2166
+ this._scrollerController?.detach(this);
2167
+ this._scrollerController = null;
2168
+ this._mutationObserver?.disconnect();
2169
+ this._mutationObserver = null;
2170
+ this._hostElementRO?.disconnect();
2171
+ this._hostElementRO = null;
2172
+ this._childrenRO?.disconnect();
2173
+ this._childrenRO = null;
2174
+ this._rejectLayoutCompletePromise("disconnected");
2175
+ this._connected = false;
2176
+ }
2177
+ _applyVirtualizerStyles() {
2178
+ const style = this._hostElement.style;
2179
+ style.display = style.display || "block";
2180
+ style.position = style.position || "relative";
2181
+ style.contain = style.contain || "size layout";
2182
+ if (this._isScroller) {
2183
+ style.overflow = style.overflow || "auto";
2184
+ style.minHeight = style.minHeight || "150px";
2185
+ }
2186
+ }
2187
+ _getSizer() {
2188
+ const hostElement = this._hostElement;
2189
+ if (!this._sizer) {
2190
+ let sizer = hostElement.querySelector(`[${SIZER_ATTRIBUTE}]`);
2191
+ if (!sizer) {
2192
+ sizer = document.createElement("div");
2193
+ sizer.setAttribute(SIZER_ATTRIBUTE, "");
2194
+ hostElement.appendChild(sizer);
2195
+ }
2196
+ Object.assign(sizer.style, {
2197
+ position: "absolute",
2198
+ margin: "-2px 0 0 0",
2199
+ padding: 0,
2200
+ visibility: "hidden",
2201
+ fontSize: "2px"
2202
+ });
2203
+ sizer.textContent = "&nbsp;";
2204
+ sizer.setAttribute(SIZER_ATTRIBUTE, "");
2205
+ this._sizer = sizer;
2206
+ }
2207
+ return this._sizer;
2208
+ }
2209
+ async updateLayoutConfig(layoutConfig) {
2210
+ await this._layoutInitialized;
2211
+ const Ctor = layoutConfig.type || DefaultLayoutConstructor;
2212
+ if (typeof Ctor === "function" && this._layout instanceof Ctor) {
2213
+ const config = { ...layoutConfig };
2214
+ delete config.type;
2215
+ this._layout.config = config;
2216
+ return true;
2217
+ }
2218
+ return false;
2219
+ }
2220
+ async _initLayout(layoutConfig) {
2221
+ let config;
2222
+ let Ctor;
2223
+ if (typeof layoutConfig.type === "function") {
2224
+ Ctor = layoutConfig.type;
2225
+ const copy = { ...layoutConfig };
2226
+ delete copy.type;
2227
+ config = copy;
2228
+ } else config = layoutConfig;
2229
+ if (Ctor === void 0) DefaultLayoutConstructor = Ctor = (await __vitePreload(async () => {
2230
+ const { FlowLayout } = await import("./flow-DK8vmxvN.js");
2231
+ return { FlowLayout };
2232
+ }, [], import.meta.url)).FlowLayout;
2233
+ this._layout = new Ctor((message) => this._handleLayoutMessage(message), config);
2234
+ if (this._layout.measureChildren && typeof this._layout.updateItemSizes === "function") {
2235
+ if (typeof this._layout.measureChildren === "function") this._measureChildOverride = this._layout.measureChildren;
2236
+ this._measureCallback = this._layout.updateItemSizes.bind(this._layout);
2237
+ }
2238
+ if (this._layout.listenForChildLoadEvents) this._hostElement.addEventListener("load", this._loadListener, true);
2239
+ this._schedule(this._updateLayout);
2240
+ }
2241
+ startBenchmarking() {
2242
+ if (this._benchmarkStart === null) this._benchmarkStart = window.performance.now();
2243
+ }
2244
+ stopBenchmarking() {
2245
+ if (this._benchmarkStart !== null) {
2246
+ const now = window.performance.now();
2247
+ const timeElapsed = now - this._benchmarkStart;
2248
+ const virtualizationTime = performance.getEntriesByName("uv-virtualizing", "measure").filter((e) => e.startTime >= this._benchmarkStart && e.startTime < now).reduce((t, m) => t + m.duration, 0);
2249
+ this._benchmarkStart = null;
2250
+ return {
2251
+ timeElapsed,
2252
+ virtualizationTime
2253
+ };
2254
+ }
2255
+ return null;
2256
+ }
2257
+ _measureChildren() {
2258
+ const mm = {};
2259
+ const children = this._children;
2260
+ const fn = this._measureChildOverride || this._measureChild;
2261
+ for (let i = 0; i < children.length; i++) {
2262
+ const child = children[i];
2263
+ const idx = this._first + i;
2264
+ if (this._itemsChanged || this._toBeMeasured.has(child)) mm[idx] = fn.call(this, child, this._items[idx]);
2265
+ }
2266
+ this._childMeasurements = mm;
2267
+ this._schedule(this._updateLayout);
2268
+ this._toBeMeasured.clear();
2269
+ }
2270
+ /**
2271
+ * Returns the width, height, and margins of the given child.
2272
+ */
2273
+ _measureChild(element) {
2274
+ const { width, height } = element.getBoundingClientRect();
2275
+ return Object.assign({
2276
+ width,
2277
+ height
2278
+ }, getMargins(element));
2279
+ }
2280
+ async _schedule(method) {
2281
+ if (!this._scheduled.has(method)) {
2282
+ this._scheduled.add(method);
2283
+ await Promise.resolve();
2284
+ this._scheduled.delete(method);
2285
+ method.call(this);
2286
+ }
2287
+ }
2288
+ async _updateDOM(state) {
2289
+ this._scrollSize = state.scrollSize;
2290
+ this._adjustRange(state.range);
2291
+ this._childrenPos = state.childPositions;
2292
+ this._scrollError = state.scrollError || null;
2293
+ const { _rangeChanged, _itemsChanged } = this;
2294
+ if (this._visibilityChanged) {
2295
+ this._notifyVisibility();
2296
+ this._visibilityChanged = false;
2297
+ }
2298
+ if (_rangeChanged || _itemsChanged) {
2299
+ this._notifyRange();
2300
+ this._rangeChanged = false;
2301
+ }
2302
+ this._finishDOMUpdate();
2303
+ }
2304
+ _finishDOMUpdate() {
2305
+ if (this._connected) {
2306
+ this._children.forEach((child) => this._childrenRO.observe(child));
2307
+ this._checkScrollIntoViewTarget(this._childrenPos);
2308
+ this._positionChildren(this._childrenPos);
2309
+ this._sizeHostElement(this._scrollSize);
2310
+ this._correctScrollError();
2311
+ if (this._benchmarkStart && "mark" in window.performance) window.performance.mark("uv-end");
2312
+ }
2313
+ }
2314
+ _updateLayout() {
2315
+ if (this._layout && this._connected) {
2316
+ this._layout.items = this._items;
2317
+ this._updateView();
2318
+ if (this._childMeasurements !== null) {
2319
+ if (this._measureCallback) this._measureCallback(this._childMeasurements);
2320
+ this._childMeasurements = null;
2321
+ }
2322
+ this._layout.reflowIfNeeded();
2323
+ if (this._benchmarkStart && "mark" in window.performance) window.performance.mark("uv-end");
2324
+ }
2325
+ }
2326
+ _handleScrollEvent() {
2327
+ if (this._benchmarkStart && "mark" in window.performance) {
2328
+ try {
2329
+ window.performance.measure("uv-virtualizing", "uv-start", "uv-end");
2330
+ } catch (e) {
2331
+ console.warn("Error measuring performance data: ", e);
2332
+ }
2333
+ window.performance.mark("uv-start");
2334
+ }
2335
+ if (this._scrollerController.correctingScrollError === false) this._layout?.unpin();
2336
+ this._schedule(this._updateLayout);
2337
+ }
2338
+ handleEvent(event) {
2339
+ switch (event.type) {
2340
+ case "scroll":
2341
+ if (event.currentTarget === window || this._clippingAncestors.includes(event.currentTarget)) this._handleScrollEvent();
2342
+ break;
2343
+ default: console.warn("event not handled", event);
2344
+ }
2345
+ }
2346
+ _handleLayoutMessage(message) {
2347
+ if (message.type === "stateChanged") this._updateDOM(message);
2348
+ else if (message.type === "visibilityChanged") {
2349
+ this._firstVisible = message.firstVisible;
2350
+ this._lastVisible = message.lastVisible;
2351
+ this._notifyVisibility();
2352
+ } else if (message.type === "unpinned") this._hostElement.dispatchEvent(new UnpinnedEvent());
2353
+ }
2354
+ get _children() {
2355
+ const arr = [];
2356
+ let next = this._hostElement.firstElementChild;
2357
+ while (next) {
2358
+ if (!next.hasAttribute(SIZER_ATTRIBUTE)) arr.push(next);
2359
+ next = next.nextElementSibling;
2360
+ }
2361
+ return arr;
2362
+ }
2363
+ _updateView() {
2364
+ const hostElement = this._hostElement;
2365
+ const scrollingElement = this._scrollerController?.element;
2366
+ const layout = this._layout;
2367
+ if (hostElement && scrollingElement && layout) {
2368
+ let top, left, bottom, right;
2369
+ const hostElementBounds = hostElement.getBoundingClientRect();
2370
+ top = 0;
2371
+ left = 0;
2372
+ bottom = window.innerHeight;
2373
+ right = window.innerWidth;
2374
+ const ancestorBounds = this._clippingAncestors.map((ancestor) => ancestor.getBoundingClientRect());
2375
+ ancestorBounds.unshift(hostElementBounds);
2376
+ for (const bounds of ancestorBounds) {
2377
+ top = Math.max(top, bounds.top);
2378
+ left = Math.max(left, bounds.left);
2379
+ bottom = Math.min(bottom, bounds.bottom);
2380
+ right = Math.min(right, bounds.right);
2381
+ }
2382
+ const scrollingElementBounds = scrollingElement.getBoundingClientRect();
2383
+ const offsetWithinScroller = {
2384
+ left: hostElementBounds.left - scrollingElementBounds.left,
2385
+ top: hostElementBounds.top - scrollingElementBounds.top
2386
+ };
2387
+ const totalScrollSize = {
2388
+ width: scrollingElement.scrollWidth,
2389
+ height: scrollingElement.scrollHeight
2390
+ };
2391
+ const scrollTop = top - hostElementBounds.top + hostElement.scrollTop;
2392
+ const scrollLeft = left - hostElementBounds.left + hostElement.scrollLeft;
2393
+ const height = Math.max(0, bottom - top);
2394
+ layout.viewportSize = {
2395
+ width: Math.max(0, right - left),
2396
+ height
2397
+ };
2398
+ layout.viewportScroll = {
2399
+ top: scrollTop,
2400
+ left: scrollLeft
2401
+ };
2402
+ layout.totalScrollSize = totalScrollSize;
2403
+ layout.offsetWithinScroller = offsetWithinScroller;
2404
+ }
2405
+ }
2406
+ /**
2407
+ * Styles the host element so that its size reflects the
2408
+ * total size of all items.
2409
+ */
2410
+ _sizeHostElement(size) {
2411
+ const max = 82e5;
2412
+ const h = size && size.width !== null ? Math.min(max, size.width) : 0;
2413
+ const v = size && size.height !== null ? Math.min(max, size.height) : 0;
2414
+ if (this._isScroller) this._getSizer().style.transform = `translate(${h}px, ${v}px)`;
2415
+ else {
2416
+ const style = this._hostElement.style;
2417
+ style.minWidth = h ? `${h}px` : "100%";
2418
+ style.minHeight = v ? `${v}px` : "100%";
2419
+ }
2420
+ }
2421
+ /**
2422
+ * Sets the top and left transform style of the children from the values in
2423
+ * pos.
2424
+ */
2425
+ _positionChildren(pos) {
2426
+ if (pos) pos.forEach(({ top, left, width, height, xOffset, yOffset }, index) => {
2427
+ const child = this._children[index - this._first];
2428
+ if (child) {
2429
+ child.style.position = "absolute";
2430
+ child.style.boxSizing = "border-box";
2431
+ child.style.transform = `translate(${left}px, ${top}px)`;
2432
+ if (width !== void 0) child.style.width = width + "px";
2433
+ if (height !== void 0) child.style.height = height + "px";
2434
+ child.style.left = xOffset === void 0 ? null : xOffset + "px";
2435
+ child.style.top = yOffset === void 0 ? null : yOffset + "px";
2436
+ }
2437
+ });
2438
+ }
2439
+ async _adjustRange(range) {
2440
+ const { _first, _last, _firstVisible, _lastVisible } = this;
2441
+ this._first = range.first;
2442
+ this._last = range.last;
2443
+ this._firstVisible = range.firstVisible;
2444
+ this._lastVisible = range.lastVisible;
2445
+ this._rangeChanged = this._rangeChanged || this._first !== _first || this._last !== _last;
2446
+ this._visibilityChanged = this._visibilityChanged || this._firstVisible !== _firstVisible || this._lastVisible !== _lastVisible;
2447
+ }
2448
+ _correctScrollError() {
2449
+ if (this._scrollError) {
2450
+ const { scrollTop, scrollLeft } = this._scrollerController;
2451
+ const { top, left } = this._scrollError;
2452
+ this._scrollError = null;
2453
+ this._scrollerController.correctScrollError({
2454
+ top: scrollTop - top,
2455
+ left: scrollLeft - left
2456
+ });
2457
+ }
2458
+ }
2459
+ element(index) {
2460
+ if (index === Infinity) index = this._items.length - 1;
2461
+ return this._items?.[index] === void 0 ? void 0 : { scrollIntoView: (options = {}) => this._scrollElementIntoView({
2462
+ ...options,
2463
+ index
2464
+ }) };
2465
+ }
2466
+ _scrollElementIntoView(options) {
2467
+ if (options.index >= this._first && options.index <= this._last) this._children[options.index - this._first].scrollIntoView(options);
2468
+ else {
2469
+ options.index = Math.min(options.index, this._items.length - 1);
2470
+ if (options.behavior === "smooth") {
2471
+ const coordinates = this._layout.getScrollIntoViewCoordinates(options);
2472
+ const { behavior } = options;
2473
+ this._updateScrollIntoViewCoordinates = this._scrollerController.managedScrollTo(Object.assign(coordinates, { behavior }), () => this._layout.getScrollIntoViewCoordinates(options), () => this._scrollIntoViewTarget = null);
2474
+ this._scrollIntoViewTarget = options;
2475
+ } else this._layout.pin = options;
2476
+ }
2477
+ }
2478
+ /**
2479
+ * If we are smoothly scrolling to an element and the target element
2480
+ * is in the DOM, we update our target coordinates as needed
2481
+ */
2482
+ _checkScrollIntoViewTarget(pos) {
2483
+ const { index } = this._scrollIntoViewTarget || {};
2484
+ if (index && pos?.has(index)) this._updateScrollIntoViewCoordinates(this._layout.getScrollIntoViewCoordinates(this._scrollIntoViewTarget));
2485
+ }
2486
+ /**
2487
+ * Emits a rangechange event with the current first, last, firstVisible, and
2488
+ * lastVisible.
2489
+ */
2490
+ _notifyRange() {
2491
+ this._hostElement.dispatchEvent(new RangeChangedEvent({
2492
+ first: this._first,
2493
+ last: this._last
2494
+ }));
2495
+ }
2496
+ _notifyVisibility() {
2497
+ this._hostElement.dispatchEvent(new VisibilityChangedEvent({
2498
+ first: this._firstVisible,
2499
+ last: this._lastVisible
2500
+ }));
2501
+ }
2502
+ get layoutComplete() {
2503
+ if (!this._layoutCompletePromise) this._layoutCompletePromise = new Promise((resolve, reject) => {
2504
+ this._layoutCompleteResolver = resolve;
2505
+ this._layoutCompleteRejecter = reject;
2506
+ });
2507
+ return this._layoutCompletePromise;
2508
+ }
2509
+ _rejectLayoutCompletePromise(reason) {
2510
+ if (this._layoutCompleteRejecter !== null) this._layoutCompleteRejecter(reason);
2511
+ this._resetLayoutCompleteState();
2512
+ }
2513
+ _scheduleLayoutComplete() {
2514
+ if (this._layoutCompletePromise && this._pendingLayoutComplete === null) this._pendingLayoutComplete = requestAnimationFrame(() => requestAnimationFrame(() => this._resolveLayoutCompletePromise()));
2515
+ }
2516
+ _resolveLayoutCompletePromise() {
2517
+ if (this._layoutCompleteResolver !== null) this._layoutCompleteResolver();
2518
+ this._resetLayoutCompleteState();
2519
+ }
2520
+ _resetLayoutCompleteState() {
2521
+ this._layoutCompletePromise = null;
2522
+ this._layoutCompleteResolver = null;
2523
+ this._layoutCompleteRejecter = null;
2524
+ this._pendingLayoutComplete = null;
2525
+ }
2526
+ /**
2527
+ * Render and update the view at the next opportunity with the given
2528
+ * hostElement size.
2529
+ */
2530
+ _hostElementSizeChanged() {
2531
+ this._schedule(this._updateLayout);
2532
+ }
2533
+ _childLoaded() {}
2534
+ _childrenSizeChanged(changes) {
2535
+ if (this._layout?.measureChildren) {
2536
+ for (const change of changes) this._toBeMeasured.set(change.target, change.contentRect);
2537
+ this._measureChildren();
2538
+ }
2539
+ this._scheduleLayoutComplete();
2540
+ this._itemsChanged = false;
2541
+ this._rangeChanged = false;
2542
+ }
2543
+ };
2544
+ function getMargins(el) {
2545
+ const style = window.getComputedStyle(el);
2546
+ return {
2547
+ marginTop: getMarginValue(style.marginTop),
2548
+ marginRight: getMarginValue(style.marginRight),
2549
+ marginBottom: getMarginValue(style.marginBottom),
2550
+ marginLeft: getMarginValue(style.marginLeft)
2551
+ };
2552
+ }
2553
+ function getMarginValue(value) {
2554
+ const float = value ? parseFloat(value) : NaN;
2555
+ return Number.isNaN(float) ? 0 : float;
2556
+ }
2557
+ function getParentElement(el) {
2558
+ if (el.assignedSlot !== null) return el.assignedSlot;
2559
+ if (el.parentElement !== null) return el.parentElement;
2560
+ const parentNode = el.parentNode;
2561
+ if (parentNode && parentNode.nodeType === Node.DOCUMENT_FRAGMENT_NODE) return parentNode.host || null;
2562
+ return null;
2563
+ }
2564
+ function getElementAncestors(el, includeSelf = false) {
2565
+ const ancestors = [];
2566
+ let parent = includeSelf ? el : getParentElement(el);
2567
+ while (parent !== null) {
2568
+ ancestors.push(parent);
2569
+ parent = getParentElement(parent);
2570
+ }
2571
+ return ancestors;
2572
+ }
2573
+ function getClippingAncestors(el, includeSelf = false) {
2574
+ let foundFixed = false;
2575
+ return getElementAncestors(el, includeSelf).filter((a) => {
2576
+ if (foundFixed) return false;
2577
+ const style = getComputedStyle(a);
2578
+ foundFixed = style.position === "fixed";
2579
+ return style.overflow !== "visible";
2580
+ });
2581
+ }
2582
+ //#endregion
2583
+ //#region node_modules/.pnpm/@lit-labs+virtualizer@2.1.1/node_modules/@lit-labs/virtualizer/virtualize.js
2584
+ /**
2585
+ * @license
2586
+ * Copyright 2021 Google LLC
2587
+ * SPDX-License-Identifier: BSD-3-Clause
2588
+ */
2589
+ var defaultKeyFunction = (item) => item;
2590
+ var defaultRenderItem = (item, idx) => b`${idx}: ${JSON.stringify(item, null, 2)}`;
2591
+ var VirtualizeDirective = class extends f {
2592
+ constructor(part) {
2593
+ super(part);
2594
+ this._virtualizer = null;
2595
+ this._first = 0;
2596
+ this._last = -1;
2597
+ this._renderItem = (item, idx) => defaultRenderItem(item, idx + this._first);
2598
+ this._keyFunction = (item, idx) => defaultKeyFunction(item, idx + this._first);
2599
+ this._items = [];
2600
+ if (part.type !== t.CHILD) throw new Error("The virtualize directive can only be used in child expressions");
2601
+ }
2602
+ render(config) {
2603
+ if (config) this._setFunctions(config);
2604
+ const itemsToRender = [];
2605
+ if (this._first >= 0 && this._last >= this._first) for (let i = this._first; i <= this._last; i++) itemsToRender.push(this._items[i]);
2606
+ return c(itemsToRender, this._keyFunction, this._renderItem);
2607
+ }
2608
+ update(part, [config]) {
2609
+ this._setFunctions(config);
2610
+ const itemsChanged = this._items !== config.items;
2611
+ this._items = config.items || [];
2612
+ if (this._virtualizer) this._updateVirtualizerConfig(part, config);
2613
+ else this._initialize(part, config);
2614
+ return itemsChanged ? E : this.render();
2615
+ }
2616
+ async _updateVirtualizerConfig(part, config) {
2617
+ if (!await this._virtualizer.updateLayoutConfig(config.layout || {})) {
2618
+ const hostElement = part.parentNode;
2619
+ this._makeVirtualizer(hostElement, config);
2620
+ }
2621
+ this._virtualizer.items = this._items;
2622
+ }
2623
+ _setFunctions(config) {
2624
+ const { renderItem, keyFunction } = config;
2625
+ if (renderItem) this._renderItem = (item, idx) => renderItem(item, idx + this._first);
2626
+ if (keyFunction) this._keyFunction = (item, idx) => keyFunction(item, idx + this._first);
2627
+ }
2628
+ _makeVirtualizer(hostElement, config) {
2629
+ if (this._virtualizer) this._virtualizer.disconnected();
2630
+ const { layout, scroller, items } = config;
2631
+ this._virtualizer = new Virtualizer({
2632
+ hostElement,
2633
+ layout,
2634
+ scroller
2635
+ });
2636
+ this._virtualizer.items = items;
2637
+ this._virtualizer.connected();
2638
+ }
2639
+ _initialize(part, config) {
2640
+ const hostElement = part.parentNode;
2641
+ if (hostElement && hostElement.nodeType === 1) {
2642
+ hostElement.addEventListener("rangeChanged", (e) => {
2643
+ this._first = e.first;
2644
+ this._last = e.last;
2645
+ this.setValue(this.render());
2646
+ });
2647
+ this._makeVirtualizer(hostElement, config);
2648
+ }
2649
+ }
2650
+ disconnected() {
2651
+ this._virtualizer?.disconnected();
2652
+ }
2653
+ reconnected() {
2654
+ this._virtualizer?.connected();
2655
+ }
2656
+ };
2657
+ var virtualize = e$1(VirtualizeDirective);
2658
+ //#endregion
2659
+ //#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/utils/colors.mjs
2660
+ function makeColor(open, close) {
2661
+ const o = `\x1B[${open}m`;
2662
+ const c = `\x1B[${close}m`;
2663
+ return ((arg, ...values) => {
2664
+ if (Array.isArray(arg) && "raw" in arg) {
2665
+ const strings = arg;
2666
+ let out = "";
2667
+ for (let i = 0; i < strings.length; i++) {
2668
+ out += strings[i];
2669
+ if (i < values.length) out += String(values[i]);
2670
+ }
2671
+ return `${o}${out}${c}`;
2672
+ }
2673
+ return `${o}${String(arg)}${c}`;
2674
+ });
2675
+ }
2676
+ var colors = {
2677
+ blue: makeColor(34, 39),
2678
+ cyan: makeColor(36, 39),
2679
+ gray: makeColor(90, 39),
2680
+ green: makeColor(32, 39),
2681
+ red: makeColor(31, 39),
2682
+ yellow: makeColor(33, 39),
2683
+ bold: makeColor(1, 22),
2684
+ dim: makeColor(2, 22),
2685
+ reset: makeColor(0, 0),
2686
+ underline: makeColor(4, 24)
2687
+ };
2688
+ //#endregion
2689
+ //#region node_modules/.pnpm/nostics@1.2.0/node_modules/nostics/dist/index.mjs
2690
+ /**
2691
+ * Transforms a value or a function that returns a value to a value.
2692
+ *
2693
+ * @param valFn either a value or a function that returns a value
2694
+ * @param args arguments to pass to the function if `valFn` is a function
2695
+ *
2696
+ * @internal
2697
+ */
2698
+ function toValueWithArgs(valFn, ...args) {
2699
+ return typeof valFn === "function" ? valFn(...args) : valFn;
2700
+ }
2701
+ var captureStackTrace = Error.captureStackTrace;
2702
+ var Diagnostic = class Diagnostic extends Error {
2703
+ name;
2704
+ /**
2705
+ * The diagnostic code, e.g. `MATH_E001`.
2706
+ * Also appears as the `name` property.
2707
+ */
2708
+ code;
2709
+ /**
2710
+ * URL to extended documentation for this diagnostic code.
2711
+ * Auto-generated from {@link DefineDiagnosticsOptions.docsBase}.
2712
+ */
2713
+ docs;
2714
+ /**
2715
+ * Optional actionable instructions on how to resolve the problem.
2716
+ */
2717
+ fix;
2718
+ /**
2719
+ * Locations in user code that contributed to this diagnostic, in
2720
+ * `file:line:column` format. Relevant when the stack trace doesn't reflect
2721
+ * the user's source (e.g. compilers, bundlers), otherwise redundant with the
2722
+ * stack and should be omitted.
2723
+ */
2724
+ sources;
2725
+ /**
2726
+ * Alias for {@link Error.message}: the reason this diagnostic was raised.
2727
+ */
2728
+ get why() {
2729
+ return this.message;
2730
+ }
2731
+ /**
2732
+ * @param init structured initializer; `why` is required
2733
+ * @param captureFrom V8 stack-cutoff frame. Defaults to {@link Diagnostic}
2734
+ * so the top of the trace is the `new Diagnostic(...)` call site.
2735
+ * `defineDiagnostics` passes its action method to strip its own frames too.
2736
+ * Ignored on engines without `Error.captureStackTrace`.
2737
+ */
2738
+ constructor(init, captureFrom = Diagnostic) {
2739
+ super(init.why, { cause: init.cause });
2740
+ this.code = this.name = init.code;
2741
+ this.fix = init.fix;
2742
+ this.docs = init.docs;
2743
+ this.sources = init.sources;
2744
+ captureStackTrace?.(this, captureFrom);
2745
+ }
2746
+ /**
2747
+ * Converts the diagnostic into a serializable structured object.
2748
+ */
2749
+ toJSON() {
2750
+ return {
2751
+ name: this.name,
2752
+ why: this.why,
2753
+ fix: this.fix,
2754
+ docs: this.docs,
2755
+ sources: this.sources,
2756
+ cause: this.cause,
2757
+ stack: this.stack
1662
2758
  };
1663
2759
  }
1664
2760
  };
@@ -4013,73 +5109,6 @@ function toAgentToolName(id) {
4013
5109
  return id.replace(/[^\w-]+/g, "_").slice(0, MAX_TOOL_NAME_LENGTH);
4014
5110
  }
4015
5111
  //#endregion
4016
- //#region \0vite/preload-helper.js
4017
- var scriptRel = "modulepreload";
4018
- var assetsURL = function(dep, importerUrl) {
4019
- return new URL(dep, importerUrl).href;
4020
- };
4021
- var seen = {};
4022
- var __vitePreload = function preload(baseModule, deps, importerUrl) {
4023
- let promise = Promise.resolve();
4024
- if (deps && deps.length > 0) {
4025
- const links = document.getElementsByTagName("link");
4026
- const cspNonceMeta = document.querySelector("meta[property=csp-nonce]");
4027
- const cspNonce = cspNonceMeta?.nonce || cspNonceMeta?.getAttribute("nonce");
4028
- function allSettled(promises) {
4029
- return Promise.all(promises.map((p) => Promise.resolve(p).then((value) => ({
4030
- status: "fulfilled",
4031
- value
4032
- }), (reason) => ({
4033
- status: "rejected",
4034
- reason
4035
- }))));
4036
- }
4037
- function importMetaResolve(specifier) {
4038
- if (import.meta.resolve) return import.meta.resolve(specifier);
4039
- return new URL(
4040
- specifier,
4041
- /** #__KEEP__ */
4042
- import.meta.url
4043
- ).href;
4044
- }
4045
- promise = allSettled(deps.map((dep) => {
4046
- dep = assetsURL(dep, importerUrl);
4047
- dep = importMetaResolve(dep);
4048
- if (dep in seen) return;
4049
- seen[dep] = true;
4050
- const isCss = dep.endsWith(".css");
4051
- for (let i = links.length - 1; i >= 0; i--) {
4052
- const link = links[i];
4053
- if (link.href === dep && (!isCss || link.rel === "stylesheet")) return;
4054
- }
4055
- const link = document.createElement("link");
4056
- link.rel = isCss ? "stylesheet" : scriptRel;
4057
- if (!isCss) link.as = "script";
4058
- link.crossOrigin = "";
4059
- link.href = dep;
4060
- if (cspNonce) link.setAttribute("nonce", cspNonce);
4061
- document.head.appendChild(link);
4062
- if (isCss) return new Promise((res, rej) => {
4063
- link.addEventListener("load", res);
4064
- link.addEventListener("error", () => rej(/* @__PURE__ */ new Error(`Unable to preload CSS for ${dep}`)));
4065
- });
4066
- }).filter((p) => p !== void 0));
4067
- }
4068
- function handlePreloadError(err) {
4069
- const e = new Event("vite:preloadError", { cancelable: true });
4070
- e.payload = err;
4071
- window.dispatchEvent(e);
4072
- if (!e.defaultPrevented) throw err;
4073
- }
4074
- return promise.then((res) => {
4075
- for (const item of res || []) {
4076
- if (item.status !== "rejected") continue;
4077
- handlePreloadError(item.reason);
4078
- }
4079
- return baseModule().catch(handlePreloadError);
4080
- });
4081
- };
4082
- //#endregion
4083
5112
  //#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/client/index.mjs
4084
5113
  /** Devframe runtime routes and static output conventions. */
4085
5114
  var DEVFRAME_CONNECTION_META_FILENAME = "__connection.json";
@@ -5980,7 +7009,7 @@ async function getDevframeRpcClient(options = {}) {
5980
7009
  bootstrapAuthSettled = true;
5981
7010
  });
5982
7011
  if (connectionMeta.mcp) __vitePreload(async () => {
5983
- const { setupBrowserAgentRpcBridge } = await import("./browser-agent-rpc-BXhoSh1z-BvWe4sTJ.js");
7012
+ const { setupBrowserAgentRpcBridge } = await import("./browser-agent-rpc-BXhoSh1z-CRHFLjFo.js");
5984
7013
  return { setupBrowserAgentRpcBridge };
5985
7014
  }, [], import.meta.url).then(({ setupBrowserAgentRpcBridge }) => {
5986
7015
  if (!closed) disposeBrowserAgentBridge = setupBrowserAgentRpcBridge(rpc);
@@ -6026,6 +7055,8 @@ var SOURCE_OVERLAY_EDITORS = [
6026
7055
  ];
6027
7056
  /** localStorage key holding the {@link SettingsOverride}. */
6028
7057
  var SETTINGS_OVERRIDE_LS_KEY = "lit-devtools-overrides";
7058
+ /** localStorage key holding the {@link OverrideBaselines}. */
7059
+ var OVERRIDE_BASELINES_LS_KEY = "lit-devtools-override-baselines";
6029
7060
  //#endregion
6030
7061
  //#region src/lib/devframe/protocol.ts
6031
7062
  /**
@@ -6042,6 +7073,7 @@ var SESSION_STATE_KEY = "session";
6042
7073
  var LAYER_FLAGS = {
6043
7074
  "lit-lifecycle": "litLifecycleEnabled",
6044
7075
  "lit-render": "litRenderEnabled",
7076
+ "lit-render-verbose": "litRenderVerboseEnabled",
6045
7077
  mouse: "mouseEventEnabled",
6046
7078
  keyboard: "keyboardEventEnabled"
6047
7079
  };
@@ -6160,12 +7192,139 @@ var openInEditor = async (file, line) => {
6160
7192
  } catch (err) {
6161
7193
  console.warn("[lit-devtools] open-source failed, falling back to the endpoint", err);
6162
7194
  }
6163
- try {
6164
- await openViaEndpoint(file, line);
6165
- } catch (err) {
6166
- console.warn("[lit-devtools] open-in-editor failed", err);
7195
+ try {
7196
+ await openViaEndpoint(file, line);
7197
+ } catch (err) {
7198
+ console.warn("[lit-devtools] open-in-editor failed", err);
7199
+ }
7200
+ };
7201
+ //#endregion
7202
+ //#region src/panel/timeline-span-detail.ts
7203
+ /**
7204
+ * @license
7205
+ * Copyright 2026 Google LLC
7206
+ * SPDX-License-Identifier: BSD-3-Clause
7207
+ */
7208
+ var TimelineSpanDetail = class TimelineSpanDetail extends i$2 {
7209
+ constructor(..._args) {
7210
+ super(..._args);
7211
+ this.filterable = false;
7212
+ }
7213
+ static {
7214
+ this.styles = [tokens, i$5`
7215
+ :host {
7216
+ display: block;
7217
+ border-top: 1px solid var(--lit-devtools-border);
7218
+ background: var(--lit-devtools-surface-low);
7219
+ padding: var(--lit-devtools-space-5) var(--lit-devtools-space-5);
7220
+ font-size: var(--lit-devtools-text-2xs);
7221
+ font-family: var(--lit-devtools-font-mono);
7222
+ color: var(--lit-devtools-text-secondary);
7223
+ flex-shrink: 0;
7224
+ max-height: 130px;
7225
+ overflow-y: auto;
7226
+ }
7227
+ table {
7228
+ border-collapse: collapse;
7229
+ width: 100%;
7230
+ }
7231
+ td {
7232
+ padding: var(--lit-devtools-space-1) var(--lit-devtools-space-4)
7233
+ var(--lit-devtools-space-1) 0;
7234
+ vertical-align: top;
7235
+ }
7236
+ .key {
7237
+ color: var(--lit-devtools-text-muted);
7238
+ white-space: nowrap;
7239
+ }
7240
+ .val {
7241
+ color: var(--lit-devtools-text);
7242
+ word-break: break-all;
7243
+ }
7244
+ a {
7245
+ color: var(--lit-devtools-accent);
7246
+ text-decoration: none;
7247
+ cursor: pointer;
7248
+ }
7249
+ a:hover {
7250
+ text-decoration: underline;
7251
+ }
7252
+ .filter-link {
7253
+ margin-left: var(--lit-devtools-space-4);
7254
+ font-size: var(--lit-devtools-text-2xs);
7255
+ }
7256
+ `];
7257
+ }
7258
+ _emit(type, id) {
7259
+ this.dispatchEvent(new CustomEvent(type, {
7260
+ detail: { id },
7261
+ bubbles: true,
7262
+ composed: true
7263
+ }));
7264
+ }
7265
+ render() {
7266
+ const row = this.span;
7267
+ if (!row) return A;
7268
+ const { meta } = row;
7269
+ const src = meta?.source;
7270
+ const data = row.events[0]?.data;
7271
+ const id = meta?.elementId;
7272
+ return b`
7273
+ <table>
7274
+ <tr>
7275
+ <td class="key">layer</td>
7276
+ <td class="val">${row.layerId}</td>
7277
+ </tr>
7278
+ <tr>
7279
+ <td class="key">time</td>
7280
+ <td class="val">${row.start.toFixed(3)} ms</td>
7281
+ </tr>
7282
+ ${row.duration !== void 0 ? b`<tr>
7283
+ <td class="key">duration</td>
7284
+ <td class="val">${row.duration.toFixed(3)} ms</td>
7285
+ </tr>` : A}
7286
+ ${row.changed?.length ? b`<tr>
7287
+ <td class="key">changed</td>
7288
+ <td class="val">${row.changed.join(", ")}</td>
7289
+ </tr>` : A}
7290
+ ${meta?.tagName ? b`<tr>
7291
+ <td class="key">element</td>
7292
+ <td class="val">
7293
+ &lt;${meta.tagName}&gt; #${id}
7294
+ ${id != null && this.filterable ? b`<a
7295
+ class="filter-link"
7296
+ @click=${() => this._emit("element-filter", id)}
7297
+ >filter</a
7298
+ >` : A}${id != null ? b`<a
7299
+ class="filter-link"
7300
+ title="Open this element in the Components tab"
7301
+ @click=${() => this._emit("inspect-element", id)}
7302
+ >inspect</a
7303
+ >` : A}
7304
+ </td>
7305
+ </tr>` : A}
7306
+ ${src ? b`<tr>
7307
+ <td class="key">source</td>
7308
+ <td class="val">
7309
+ <a
7310
+ class="src-link"
7311
+ title="Open this file in your editor"
7312
+ @click=${() => openInEditor(src.file, src.line)}
7313
+ >${src.file}:${src.line}</a
7314
+ >
7315
+ </td>
7316
+ </tr>` : A}
7317
+ ${data != null ? b`<tr>
7318
+ <td class="key">data</td>
7319
+ <td class="val">${JSON.stringify(data)}</td>
7320
+ </tr>` : A}
7321
+ </table>
7322
+ `;
6167
7323
  }
6168
7324
  };
7325
+ __decorate([n$3({ attribute: false })], TimelineSpanDetail.prototype, "span", void 0);
7326
+ __decorate([n$3({ type: Boolean })], TimelineSpanDetail.prototype, "filterable", void 0);
7327
+ TimelineSpanDetail = __decorate([t$2("timeline-span-detail")], TimelineSpanDetail);
6169
7328
  //#endregion
6170
7329
  //#region src/panel/timeline-event-list.ts
6171
7330
  /**
@@ -6173,6 +7332,9 @@ var openInEditor = async (file, line) => {
6173
7332
  * Copyright 2026 Google LLC
6174
7333
  * SPDX-License-Identifier: BSD-3-Clause
6175
7334
  */
7335
+ var RAW_KEY_PREFIX = "raw:";
7336
+ /** Whether `key` names a Raw-mode row rather than a collapsed span. */
7337
+ var isRawKey = (key) => key.startsWith(RAW_KEY_PREFIX);
6176
7338
  /**
6177
7339
  * Adapts one raw event to the row shape for the Raw toggle. Deliberately
6178
7340
  * drops `groupId`: pairing is what the collapsed mode is for, and without it
@@ -6180,7 +7342,7 @@ var openInEditor = async (file, line) => {
6180
7342
  */
6181
7343
  var rawRow = (event, index) => ({
6182
7344
  layerId: event.layerId,
6183
- key: `raw:${index}`,
7345
+ key: `${RAW_KEY_PREFIX}${index}`,
6184
7346
  name: event.title ?? event.layerId,
6185
7347
  start: event.time,
6186
7348
  subtitle: event.subtitle,
@@ -6198,14 +7360,15 @@ var TimelineEventList = class TimelineEventList extends i$2 {
6198
7360
  constructor(..._args) {
6199
7361
  super(..._args);
6200
7362
  this.events = [];
7363
+ this.spans = [];
6201
7364
  this.layers = [];
6202
- this._selectedKey = null;
6203
- this._elementFilter = null;
6204
- this._regex = "";
7365
+ this.selectedKey = null;
7366
+ this.elementFilter = null;
7367
+ this.regex = "";
6205
7368
  this._raw = false;
6206
7369
  this._scrollRef = e();
6207
- this._elementsCache = [];
6208
7370
  this._rowsCache = [];
7371
+ this._visibleCache = [];
6209
7372
  }
6210
7373
  static {
6211
7374
  this.styles = [tokens, i$5`
@@ -6225,31 +7388,6 @@ var TimelineEventList = class TimelineEventList extends i$2 {
6225
7388
  color: var(--lit-devtools-text-muted);
6226
7389
  flex-shrink: 0;
6227
7390
  }
6228
- .filterbar select {
6229
- background: var(--lit-devtools-surface-elevated);
6230
- color: var(--lit-devtools-text);
6231
- border: 1px solid var(--lit-devtools-border-strong);
6232
- border-radius: var(--lit-devtools-radius-sm);
6233
- padding: var(--lit-devtools-space-1) var(--lit-devtools-space-3);
6234
- font-size: var(--lit-devtools-text-2xs);
6235
- font-family: var(--lit-devtools-font-mono);
6236
- }
6237
- .filterbar input.regex {
6238
- background: var(--lit-devtools-surface-elevated);
6239
- color: var(--lit-devtools-text);
6240
- border: 1px solid var(--lit-devtools-border-strong);
6241
- border-radius: var(--lit-devtools-radius-sm);
6242
- padding: var(--lit-devtools-space-1) var(--lit-devtools-space-3);
6243
- font-size: var(--lit-devtools-text-2xs);
6244
- font-family: var(--lit-devtools-font-mono);
6245
- min-width: 140px;
6246
- }
6247
- .filterbar input.regex::placeholder {
6248
- color: var(--lit-devtools-text-muted);
6249
- }
6250
- .filterbar input.regex.invalid {
6251
- border-color: var(--lit-devtools-error);
6252
- }
6253
7391
  .filterbar button {
6254
7392
  background: var(--lit-devtools-surface-elevated);
6255
7393
  color: var(--lit-devtools-text-muted);
@@ -6288,6 +7426,9 @@ var TimelineEventList = class TimelineEventList extends i$2 {
6288
7426
  color: var(--lit-devtools-text-muted);
6289
7427
  }
6290
7428
  .row {
7429
+ /* The virtualizer positions rows absolutely; stretch them back. */
7430
+ box-sizing: border-box;
7431
+ width: 100%;
6291
7432
  display: flex;
6292
7433
  align-items: baseline;
6293
7434
  gap: var(--lit-devtools-space-4);
@@ -6303,314 +7444,728 @@ var TimelineEventList = class TimelineEventList extends i$2 {
6303
7444
  .row.selected {
6304
7445
  background: var(--lit-devtools-surface-active);
6305
7446
  }
6306
- .time {
6307
- color: var(--lit-devtools-text-muted);
6308
- flex-shrink: 0;
6309
- width: 56px;
6310
- text-align: right;
7447
+ .time {
7448
+ color: var(--lit-devtools-text-muted);
7449
+ flex-shrink: 0;
7450
+ width: 56px;
7451
+ text-align: right;
7452
+ }
7453
+ .dot {
7454
+ width: 8px;
7455
+ height: 8px;
7456
+ border-radius: 50%;
7457
+ flex-shrink: 0;
7458
+ margin-top: 2px;
7459
+ }
7460
+ .title {
7461
+ flex: 1;
7462
+ color: var(--lit-devtools-text);
7463
+ overflow: hidden;
7464
+ text-overflow: ellipsis;
7465
+ white-space: nowrap;
7466
+ }
7467
+ .subtitle {
7468
+ color: var(--lit-devtools-text-muted);
7469
+ flex-shrink: 0;
7470
+ max-width: 200px;
7471
+ overflow: hidden;
7472
+ text-overflow: ellipsis;
7473
+ white-space: nowrap;
7474
+ }
7475
+ .changed {
7476
+ color: var(--lit-devtools-accent);
7477
+ flex-shrink: 0;
7478
+ max-width: 220px;
7479
+ overflow: hidden;
7480
+ text-overflow: ellipsis;
7481
+ white-space: nowrap;
7482
+ }
7483
+ .dur {
7484
+ color: var(--lit-devtools-text-secondary);
7485
+ flex-shrink: 0;
7486
+ width: 62px;
7487
+ text-align: right;
7488
+ }
7489
+ .dur.open {
7490
+ color: var(--lit-devtools-text-muted);
7491
+ }
7492
+ `];
7493
+ }
7494
+ willUpdate(changed) {
7495
+ if (changed.has("events") || changed.has("spans") || changed.has("_raw")) this._rowsCache = this._raw ? this.events.map(rawRow) : this.spans;
7496
+ if (changed.has("events") || changed.has("spans") || changed.has("_raw") || changed.has("layers") || changed.has("elementFilter") || changed.has("regex")) this._visibleCache = filterSpans(this._rowsCache.filter((row) => this._layerOn(row)), this.elementFilter, compileRegex(this.regex).re);
7497
+ }
7498
+ updated(changed) {
7499
+ if (changed.has("events")) {
7500
+ const el = this._scrollRef.value;
7501
+ if (el) el.scrollTop = el.scrollHeight;
7502
+ }
7503
+ }
7504
+ _select(key) {
7505
+ this.dispatchEvent(new CustomEvent("span-select", {
7506
+ detail: { key },
7507
+ bubbles: true,
7508
+ composed: true
7509
+ }));
7510
+ }
7511
+ /**
7512
+ * Scrolls the selected row into view, or to the newest row when nothing is
7513
+ * selected. `timeline-view` calls this when switching back from Tracks: the
7514
+ * list was hidden while the selection moved, so its own auto-scroll could
7515
+ * not follow.
7516
+ */
7517
+ reveal() {
7518
+ const el = this._scrollRef.value;
7519
+ if (!el) return;
7520
+ const index = this.selectedKey === null ? -1 : this._visibleCache.findIndex((row) => row.key === this.selectedKey);
7521
+ if (index === -1) {
7522
+ el.scrollTop = el.scrollHeight;
7523
+ return;
7524
+ }
7525
+ const attempt = (left) => {
7526
+ try {
7527
+ el[virtualizerRef]?.element(index)?.scrollIntoView({ block: "center" });
7528
+ } catch {
7529
+ if (left > 0) requestAnimationFrame(() => attempt(left - 1));
7530
+ }
7531
+ };
7532
+ attempt(30);
7533
+ }
7534
+ _layerOn(row) {
7535
+ const l = this.layers.find((l) => l.id === row.layerId);
7536
+ return !l || l.enabled;
7537
+ }
7538
+ render() {
7539
+ const visible = this._visibleCache;
7540
+ const selected = this.selectedKey === null ? void 0 : this._rowsCache.find((row) => row.key === this.selectedKey);
7541
+ return b`
7542
+ ${this.events.length > 0 ? b`
7543
+ <div class="filterbar">
7544
+ <button
7545
+ class=${this._raw ? "on" : ""}
7546
+ title="Show one row per recorded event instead of collapsing start/end pairs"
7547
+ @click=${() => {
7548
+ this._raw = !this._raw;
7549
+ this._select(null);
7550
+ }}
7551
+ >
7552
+ Raw
7553
+ </button>
7554
+ <span class="count"
7555
+ >${visible.length} / ${this._rowsCache.length}</span
7556
+ >
7557
+ </div>
7558
+ ` : A}
7559
+ ${visible.length === 0 ? b`
7560
+ <div class="scroll">
7561
+ <div class="empty">
7562
+ <span>No events recorded.</span>
7563
+ <span class="hint"
7564
+ >Press Record then interact with the page.</span
7565
+ >
7566
+ </div>
7567
+ </div>
7568
+ ` : b`
7569
+ <div class="scroll" ${n(this._scrollRef)}>
7570
+ ${virtualize({
7571
+ scroller: true,
7572
+ items: visible,
7573
+ keyFunction: (row, i) => row?.key ?? i,
7574
+ renderItem: (row) => row ? this._renderRow(row) : b``
7575
+ })}
7576
+ </div>
7577
+ `}
7578
+ ${selected ? b`<timeline-span-detail
7579
+ filterable
7580
+ .span=${selected}
7581
+ ></timeline-span-detail>` : A}
7582
+ `;
7583
+ }
7584
+ /** One row of the virtualized list (`virtualize`'s `renderItem`). */
7585
+ _renderRow(row) {
7586
+ return b`
7587
+ <div
7588
+ class="row ${this.selectedKey === row.key ? "selected" : ""}"
7589
+ @click=${() => this._select(row.key)}
7590
+ >
7591
+ <span class="time">${row.start.toFixed(1)}ms</span>
7592
+ <span
7593
+ class="dot"
7594
+ style=${"background:" + layerColor(this.layers, row.layerId)}
7595
+ ></span>
7596
+ <span class="title">${row.name}</span>
7597
+ ${row.changed?.length ? b`<span class="changed">${row.changed.join(", ")}</span>` : A}
7598
+ <span class="subtitle">${row.subtitle ?? A}</span>
7599
+ <span class="dur ${row.duration === void 0 ? "open" : ""}"
7600
+ >${renderDuration(row)}</span
7601
+ >
7602
+ </div>
7603
+ `;
7604
+ }
7605
+ };
7606
+ __decorate([n$3({ type: Array })], TimelineEventList.prototype, "events", void 0);
7607
+ __decorate([n$3({ type: Array })], TimelineEventList.prototype, "spans", void 0);
7608
+ __decorate([n$3({ type: Array })], TimelineEventList.prototype, "layers", void 0);
7609
+ __decorate([n$3({ attribute: false })], TimelineEventList.prototype, "selectedKey", void 0);
7610
+ __decorate([n$3({ attribute: false })], TimelineEventList.prototype, "elementFilter", void 0);
7611
+ __decorate([n$3({ attribute: false })], TimelineEventList.prototype, "regex", void 0);
7612
+ __decorate([r$2()], TimelineEventList.prototype, "_raw", void 0);
7613
+ TimelineEventList = __decorate([t$2("timeline-event-list")], TimelineEventList);
7614
+ //#endregion
7615
+ //#region src/lib/timeline/tracks.ts
7616
+ /**
7617
+ * @license
7618
+ * Copyright 2026 Google LLC
7619
+ * SPDX-License-Identifier: BSD-3-Clause
7620
+ */
7621
+ /**
7622
+ * Where a span stops occupying its row. A point event occupies only its own
7623
+ * instant; a span whose end has not arrived (still running, or its end fell
7624
+ * out of the buffer) occupies the rest of the recording, because that is
7625
+ * where it is drawn — up to the live edge.
7626
+ */
7627
+ var occupiedEnd = (span) => {
7628
+ if (span.end !== void 0) return span.end;
7629
+ return span.groupId === void 0 ? span.start : Infinity;
7630
+ };
7631
+ /**
7632
+ * Groups spans into one track per layer, in `layers` order, then any layer
7633
+ * the spans mention that `layers` does not (a custom layer announced after the
7634
+ * last `get-meta`), in first-seen order. A layer with no spans still gets an
7635
+ * empty track, so a lane does not appear and vanish as events arrive.
7636
+ *
7637
+ * Within a track, spans are packed greedily: each goes into the first row
7638
+ * whose last span has ended by the time it starts. Lit's lifecycle phases
7639
+ * nest, so one update tick lands as `performUpdate` on row 0 and
7640
+ * `willUpdate` / `update` / `updated` side by side on row 1 — the flame shape
7641
+ * falls out of the packing without modelling it.
7642
+ */
7643
+ var buildTracks = (spans, layers) => {
7644
+ const byLayer = /* @__PURE__ */ new Map();
7645
+ for (const layer of layers) byLayer.set(layer.id, []);
7646
+ for (const span of spans) {
7647
+ let list = byLayer.get(span.layerId);
7648
+ if (list === void 0) {
7649
+ list = [];
7650
+ byLayer.set(span.layerId, list);
7651
+ }
7652
+ list.push(span);
7653
+ }
7654
+ return [...byLayer].map(([layerId, list]) => ({
7655
+ layerId,
7656
+ rows: packRows(list)
7657
+ }));
7658
+ };
7659
+ var packRows = (spans) => {
7660
+ const sorted = spans.every((s, i) => i === 0 || spans[i - 1].start <= s.start) ? spans : [...spans].sort((a, b) => a.start - b.start);
7661
+ const rows = [];
7662
+ const rowEnds = [];
7663
+ for (const span of sorted) {
7664
+ let row = rowEnds.findIndex((end) => end <= span.start);
7665
+ if (row === -1) {
7666
+ row = rows.length;
7667
+ rows.push([]);
7668
+ rowEnds.push(-Infinity);
7669
+ }
7670
+ rows[row].push(span);
7671
+ rowEnds[row] = occupiedEnd(span);
7672
+ }
7673
+ return rows;
7674
+ };
7675
+ /**
7676
+ * The recording's time bounds over `spans`: the earliest start, and the
7677
+ * latest instant anything is known to have happened (an end, or the start of
7678
+ * a span with none). The origin is the earliest start rather than zero
7679
+ * because the panel's buffer is capped and rolls its oldest events off; a
7680
+ * long recording would otherwise open on an empty stretch.
7681
+ */
7682
+ var timeBounds = (spans) => {
7683
+ if (spans.length === 0) return {
7684
+ origin: 0,
7685
+ extent: 0
7686
+ };
7687
+ let origin = Infinity;
7688
+ let last = -Infinity;
7689
+ for (const span of spans) {
7690
+ origin = Math.min(origin, span.start);
7691
+ last = Math.max(last, span.end ?? span.start);
7692
+ }
7693
+ return {
7694
+ origin,
7695
+ extent: last - origin
7696
+ };
7697
+ };
7698
+ /** Deepest zoom, in multiples of "fit": about a microsecond per pixel on a
7699
+ * second-long recording, past which there is nothing left to separate. */
7700
+ var MAX_ZOOM = 1e5;
7701
+ /** A recording with a single instant in it still needs a nonzero axis. */
7702
+ var MIN_EXTENT_MS = 1;
7703
+ /**
7704
+ * Builds the scale for a plot `width` pixels wide showing a recording that
7705
+ * begins at `originMs` and runs for `extentMs`.
7706
+ *
7707
+ * `zoom` is a multiple of "fit" (1 shows the whole recording); `pan` is how
7708
+ * many milliseconds past the origin the left edge sits. Both are clamped, so
7709
+ * callers can apply a raw wheel or drag delta and read back where it landed.
7710
+ */
7711
+ var timeScale = (originMs, extentMs, width, zoom = 1, pan = 0) => {
7712
+ const extent = Math.max(extentMs, MIN_EXTENT_MS);
7713
+ const z = clampZoom(zoom);
7714
+ const visibleMs = extent / z;
7715
+ const p = clampPan(pan, extent, z);
7716
+ const pxPerMs = Math.max(width, 1) / visibleMs;
7717
+ const start = originMs + p;
7718
+ return {
7719
+ pxPerMs,
7720
+ start,
7721
+ end: start + visibleMs,
7722
+ toX: (ms) => (ms - start) * pxPerMs,
7723
+ toMs: (x) => start + x / pxPerMs
7724
+ };
7725
+ };
7726
+ var clampZoom = (zoom) => Math.min(Math.max(Number.isNaN(zoom) ? 1 : zoom, 1), MAX_ZOOM);
7727
+ /** Keeps the visible window inside the recording. */
7728
+ var clampPan = (pan, extentMs, zoom) => {
7729
+ const extent = Math.max(extentMs, MIN_EXTENT_MS);
7730
+ const max = extent - extent / clampZoom(zoom);
7731
+ return Math.min(Math.max(Number.isNaN(pan) ? 0 : pan, 0), max);
7732
+ };
7733
+ /** The pan that puts the live edge (the end of the recording) at the right
7734
+ * of the plot, for following a recording in progress. */
7735
+ var livePan = (extentMs, zoom) => clampPan(Infinity, extentMs, zoom);
7736
+ /**
7737
+ * Zooms by `factor` while keeping the millisecond under pixel `x` where it
7738
+ * is, which is what a wheel over a timeline is expected to do.
7739
+ */
7740
+ var zoomAt = (originMs, extentMs, width, zoom, pan, x, factor) => {
7741
+ const anchor = timeScale(originMs, extentMs, width, zoom, pan).toMs(x);
7742
+ const nextZoom = clampZoom(clampZoom(zoom) * factor);
7743
+ const after = timeScale(originMs, extentMs, width, nextZoom, 0);
7744
+ return {
7745
+ zoom: nextZoom,
7746
+ pan: clampPan(anchor - originMs - x / after.pxPerMs, extentMs, nextZoom)
7747
+ };
7748
+ };
7749
+ /**
7750
+ * Index range `[from, to)` of the spans in `row` that intersect the window
7751
+ * `[start, end]`, found by binary search so a zoomed-in lane renders a
7752
+ * handful of marks rather than walking thousands.
7753
+ *
7754
+ * Only the span just before the first one starting inside the window can
7755
+ * reach into it, because a row's spans never overlap.
7756
+ */
7757
+ var visibleRange = (row, start, end) => {
7758
+ const firstAtOrAfter = (t, inclusive) => {
7759
+ let lo = 0;
7760
+ let hi = row.length;
7761
+ while (lo < hi) {
7762
+ const mid = lo + hi >> 1;
7763
+ const s = row[mid].start;
7764
+ if (inclusive ? s < t : s <= t) lo = mid + 1;
7765
+ else hi = mid;
7766
+ }
7767
+ return lo;
7768
+ };
7769
+ let from = firstAtOrAfter(start, true);
7770
+ if (from > 0 && occupiedEnd(row[from - 1]) >= start) from--;
7771
+ const to = firstAtOrAfter(end, false);
7772
+ return [from, Math.max(from, to)];
7773
+ };
7774
+ /**
7775
+ * A round tick interval (1, 2 or 5 times a power of ten, in milliseconds) at
7776
+ * least `minMs` long, for axis labels that land on readable numbers.
7777
+ */
7778
+ var niceStep = (minMs) => {
7779
+ if (!(minMs > 0) || !Number.isFinite(minMs)) return 1;
7780
+ const pow = 10 ** Math.floor(Math.log10(minMs));
7781
+ for (const m of [
7782
+ 1,
7783
+ 2,
7784
+ 5,
7785
+ 10
7786
+ ]) if (m * pow >= minMs) return m * pow;
7787
+ return 10 * pow;
7788
+ };
7789
+ //#endregion
7790
+ //#region src/panel/timeline-tracks.ts
7791
+ /**
7792
+ * @license
7793
+ * Copyright 2026 Google LLC
7794
+ * SPDX-License-Identifier: BSD-3-Clause
7795
+ */
7796
+ /** Height of one packed row inside a lane. */
7797
+ var ROW_PX = 14;
7798
+ /** Marks narrower than this draw as a fixed-width tick, so sub-millisecond
7799
+ * Lit phases stay visible at any zoom. */
7800
+ var MIN_MARK_PX = 2;
7801
+ /** Rough spacing between axis labels. */
7802
+ var TICK_PX = 90;
7803
+ /** A pointer that moves less than this between down and up was a click. */
7804
+ var DRAG_SLOP_PX = 3;
7805
+ var formatAxis = (ms, step) => step >= 1e3 ? `${(ms / 1e3).toFixed(step >= 1e4 ? 0 : 1)}s` : `${ms.toFixed(step >= 1 ? 0 : step >= .1 ? 1 : 2)}ms`;
7806
+ var describe = (span) => {
7807
+ const parts = [span.name];
7808
+ if (span.subtitle) parts.push(span.subtitle);
7809
+ parts.push(`@ ${span.start.toFixed(2)}ms`);
7810
+ if (span.duration !== void 0) parts.push(`${span.duration.toFixed(3)}ms`);
7811
+ else if (span.groupId !== void 0) parts.push("still open");
7812
+ return parts.join(" · ");
7813
+ };
7814
+ var TimelineTracks = class TimelineTracks extends i$2 {
7815
+ constructor(..._args) {
7816
+ super(..._args);
7817
+ this.spans = [];
7818
+ this.layers = [];
7819
+ this.selectedKey = null;
7820
+ this.visibleTracks = [];
7821
+ this.recording = false;
7822
+ this._zoom = 1;
7823
+ this._pan = 0;
7824
+ this._follow = true;
7825
+ this._width = 0;
7826
+ this._ticksRef = e();
7827
+ this._resize = null;
7828
+ this._observed = null;
7829
+ this._tracks = [];
7830
+ this._visibleSpans = [];
7831
+ this._bounds = {
7832
+ origin: 0,
7833
+ extent: 0
7834
+ };
7835
+ this._drag = null;
7836
+ this._suppressClick = false;
7837
+ }
7838
+ static {
7839
+ this.styles = [tokens, i$5`
7840
+ :host {
7841
+ display: flex;
7842
+ flex-direction: column;
7843
+ flex: 1;
7844
+ overflow: hidden;
7845
+ font-size: var(--lit-devtools-text-2xs);
7846
+ font-family: var(--lit-devtools-font-mono);
7847
+ --gutter: 120px;
7848
+ }
7849
+ .axis,
7850
+ .lane {
7851
+ display: flex;
6311
7852
  }
6312
- .dot {
6313
- width: 8px;
6314
- height: 8px;
6315
- border-radius: 50%;
7853
+ .axis {
6316
7854
  flex-shrink: 0;
6317
- margin-top: 2px;
7855
+ height: 20px;
7856
+ border-bottom: 1px solid var(--lit-devtools-border);
7857
+ color: var(--lit-devtools-text-muted);
6318
7858
  }
6319
- .title {
6320
- flex: 1;
6321
- color: var(--lit-devtools-text);
7859
+ .gutter {
7860
+ flex: 0 0 var(--gutter);
7861
+ box-sizing: border-box;
7862
+ padding: 0 var(--lit-devtools-space-4);
7863
+ display: flex;
7864
+ align-items: center;
7865
+ gap: var(--lit-devtools-space-2);
6322
7866
  overflow: hidden;
6323
- text-overflow: ellipsis;
6324
7867
  white-space: nowrap;
7868
+ border-right: 1px solid var(--lit-devtools-border);
6325
7869
  }
6326
- .subtitle {
6327
- color: var(--lit-devtools-text-muted);
6328
- flex-shrink: 0;
6329
- max-width: 200px;
7870
+ .ticks {
7871
+ position: relative;
7872
+ flex: 1;
6330
7873
  overflow: hidden;
6331
- text-overflow: ellipsis;
6332
- white-space: nowrap;
6333
7874
  }
6334
- .changed {
6335
- color: var(--lit-devtools-accent);
6336
- flex-shrink: 0;
6337
- max-width: 220px;
6338
- overflow: hidden;
6339
- text-overflow: ellipsis;
7875
+ .tick-label {
7876
+ position: absolute;
7877
+ top: 3px;
7878
+ padding-left: var(--lit-devtools-space-2);
7879
+ border-left: 1px solid var(--lit-devtools-border-strong);
6340
7880
  white-space: nowrap;
6341
7881
  }
6342
- .dur {
6343
- color: var(--lit-devtools-text-secondary);
6344
- flex-shrink: 0;
6345
- width: 62px;
6346
- text-align: right;
7882
+ .lanes {
7883
+ flex: 1;
7884
+ overflow-y: auto;
7885
+ cursor: grab;
7886
+ user-select: none;
7887
+ touch-action: none;
6347
7888
  }
6348
- .dur.open {
6349
- color: var(--lit-devtools-text-muted);
7889
+ .lanes.dragging {
7890
+ cursor: grabbing;
6350
7891
  }
6351
- .detail {
6352
- border-top: 1px solid var(--lit-devtools-border);
6353
- background: var(--lit-devtools-surface-low);
6354
- padding: var(--lit-devtools-space-5) var(--lit-devtools-space-5);
6355
- font-size: var(--lit-devtools-text-2xs);
6356
- font-family: var(--lit-devtools-font-mono);
7892
+ .lane {
7893
+ border-bottom: 1px solid var(--lit-devtools-border);
7894
+ }
7895
+ .lane .gutter {
6357
7896
  color: var(--lit-devtools-text-secondary);
7897
+ cursor: default;
7898
+ }
7899
+ .dot {
7900
+ width: 8px;
7901
+ height: 8px;
7902
+ border-radius: 50%;
6358
7903
  flex-shrink: 0;
6359
- max-height: 130px;
6360
- overflow-y: auto;
6361
7904
  }
6362
- table {
6363
- border-collapse: collapse;
6364
- width: 100%;
7905
+ .plot {
7906
+ flex: 1;
7907
+ min-width: 0;
7908
+ max-height: calc(8 * ${ROW_PX}px);
7909
+ overflow-x: hidden;
7910
+ overflow-y: auto;
6365
7911
  }
6366
- td {
6367
- padding: var(--lit-devtools-space-1) var(--lit-devtools-space-4)
6368
- var(--lit-devtools-space-1) 0;
6369
- vertical-align: top;
7912
+ .rows {
7913
+ position: relative;
6370
7914
  }
6371
- .key {
6372
- color: var(--lit-devtools-text-muted);
7915
+ .mark {
7916
+ position: absolute;
7917
+ box-sizing: border-box;
7918
+ height: ${11}px;
7919
+ margin-top: 1px;
7920
+ border-radius: 2px;
7921
+ opacity: 0.85;
7922
+ overflow: hidden;
6373
7923
  white-space: nowrap;
7924
+ color: var(--lit-devtools-surface-low);
7925
+ font-size: 9px;
7926
+ line-height: ${11}px;
7927
+ padding: 0 2px;
7928
+ cursor: pointer;
6374
7929
  }
6375
- .val {
6376
- color: var(--lit-devtools-text);
6377
- word-break: break-all;
7930
+ .mark.tick {
7931
+ padding: 0;
6378
7932
  }
6379
- a {
6380
- color: var(--lit-devtools-accent);
6381
- text-decoration: none;
7933
+ .mark.open {
7934
+ opacity: 0.45;
6382
7935
  }
6383
- a:hover {
6384
- text-decoration: underline;
7936
+ .mark:hover {
7937
+ opacity: 1;
6385
7938
  }
6386
- .src-link {
6387
- cursor: pointer;
7939
+ .mark.selected {
7940
+ opacity: 1;
7941
+ outline: 2px solid var(--lit-devtools-text);
7942
+ outline-offset: 0;
7943
+ z-index: 1;
6388
7944
  }
6389
- .filter-link {
6390
- cursor: pointer;
6391
- margin-left: var(--lit-devtools-space-4);
6392
- font-size: var(--lit-devtools-text-2xs);
7945
+ .mark:focus-visible {
7946
+ outline: 2px solid var(--lit-devtools-accent-ring);
7947
+ z-index: 1;
7948
+ }
7949
+ .empty {
7950
+ display: flex;
7951
+ flex-direction: column;
7952
+ align-items: center;
7953
+ justify-content: center;
7954
+ flex: 1;
7955
+ gap: var(--lit-devtools-space-4);
7956
+ color: var(--lit-devtools-text-muted);
7957
+ font-family: var(--lit-devtools-font-sans);
7958
+ font-size: var(--lit-devtools-text-xs);
6393
7959
  }
6394
7960
  `];
6395
7961
  }
6396
- willUpdate(changed) {
6397
- if (changed.has("events") || changed.has("_raw")) {
6398
- this._rowsCache = this._raw ? this.events.map(rawRow) : toSpans(this.events);
6399
- if (this._selectedKey !== null && !this._rowsCache.some((row) => row.key === this._selectedKey)) this._selectedKey = null;
6400
- }
6401
- if (changed.has("events")) {
6402
- this._elementsCache = this._computeElements();
6403
- if (this._elementFilter !== null && !this.events.some((ev) => ev.meta?.elementId === this._elementFilter)) this._elementFilter = null;
6404
- }
6405
- }
6406
- updated(changed) {
6407
- if (changed.has("events")) {
6408
- const el = this._scrollRef.value;
6409
- if (el) el.scrollTop = el.scrollHeight;
6410
- }
6411
- }
6412
- _colorOf(layerId) {
6413
- const l = this.layers.find((l) => l.id === layerId);
6414
- if (!l) return "#888";
6415
- return "#" + l.color.toString(16).padStart(6, "0");
6416
- }
6417
- _isVisible(row) {
6418
- const l = this.layers.find((l) => l.id === row.layerId);
6419
- if (l && !l.enabled) return false;
6420
- if (this._elementFilter !== null && row.meta?.elementId !== this._elementFilter) return false;
6421
- return true;
7962
+ disconnectedCallback() {
7963
+ super.disconnectedCallback();
7964
+ this._resize?.disconnect();
7965
+ this._resize = null;
7966
+ this._observed = null;
7967
+ }
7968
+ updated() {
7969
+ const el = this._ticksRef.value ?? null;
7970
+ if (el === this._observed) return;
7971
+ this._resize?.disconnect();
7972
+ this._observed = el;
7973
+ if (!el) return;
7974
+ this._resize ??= new ResizeObserver(() => {
7975
+ this._width = this._observed?.clientWidth ?? 0;
7976
+ });
7977
+ this._resize.observe(el);
6422
7978
  }
6423
- /** Distinct elements (by stable id) seen across the recorded events. */
6424
- _computeElements() {
6425
- const seen = /* @__PURE__ */ new Map();
6426
- for (const ev of this.events) {
6427
- const id = ev.meta?.elementId;
6428
- if (id != null && !seen.has(id)) seen.set(id, ev.meta?.tagName ?? "unknown");
7979
+ willUpdate(changed) {
7980
+ if (changed.has("spans") || changed.has("layers") || changed.has("visibleTracks")) {
7981
+ const shown = new Set(this.visibleTracks);
7982
+ this._visibleSpans = this.spans.filter((s) => shown.has(s.layerId));
7983
+ this._tracks = buildTracks(this._visibleSpans, this.layers).filter((t) => shown.has(t.layerId));
7984
+ this._bounds = timeBounds(this._visibleSpans);
7985
+ }
7986
+ }
7987
+ _scale() {
7988
+ const { origin, extent } = this._bounds;
7989
+ const pan = this._follow && this.recording ? livePan(extent, this._zoom) : this._pan;
7990
+ return timeScale(origin, extent, this._width, this._zoom, pan);
7991
+ }
7992
+ /** Commit a zoom/pan, and keep following only if it still ends at the live
7993
+ * edge. */
7994
+ _setView(zoom, pan) {
7995
+ const { extent } = this._bounds;
7996
+ this._zoom = clampZoom(zoom);
7997
+ this._pan = clampPan(pan, extent, this._zoom);
7998
+ this._follow = this._pan >= livePan(extent, this._zoom) - 1e-9;
7999
+ }
8000
+ _currentPan() {
8001
+ return this._scale().start - this._bounds.origin;
8002
+ }
8003
+ _onWheel(e) {
8004
+ if (this._width === 0 || this._visibleSpans.length === 0) return;
8005
+ const plot = this._ticksRef.value;
8006
+ if (!plot) return;
8007
+ const x = e.clientX - plot.getBoundingClientRect().left;
8008
+ if (x < 0) return;
8009
+ e.preventDefault();
8010
+ const { origin, extent } = this._bounds;
8011
+ const pan = this._currentPan();
8012
+ if (Math.abs(e.deltaX) > Math.abs(e.deltaY) || e.shiftKey) {
8013
+ const delta = e.shiftKey ? e.deltaY : e.deltaX;
8014
+ const scale = this._scale();
8015
+ this._setView(this._zoom, pan + delta / scale.pxPerMs);
8016
+ return;
6429
8017
  }
6430
- return [...seen].map(([id, tag]) => ({
6431
- id,
6432
- tag
6433
- }));
8018
+ const factor = Math.exp(-e.deltaY * .002);
8019
+ const next = zoomAt(origin, extent, this._width, this._zoom, pan, x, factor);
8020
+ this._setView(next.zoom, next.pan);
6434
8021
  }
6435
- _onFilterChange(e) {
6436
- const v = e.target.value;
6437
- this._elementFilter = v === "" ? null : Number(v);
8022
+ _onPointerDown(e) {
8023
+ if (e.button !== 0) return;
8024
+ this._drag = {
8025
+ x: e.clientX,
8026
+ pan: this._currentPan(),
8027
+ moved: false
8028
+ };
6438
8029
  }
6439
- /** Ask the panel shell to open the Components tab on this element. */
6440
- _inspect(id) {
6441
- this.dispatchEvent(new CustomEvent("inspect-element", {
6442
- detail: { id },
8030
+ _onPointerMove(e) {
8031
+ const drag = this._drag;
8032
+ if (!drag) return;
8033
+ const dx = e.clientX - drag.x;
8034
+ if (!drag.moved && Math.abs(dx) < DRAG_SLOP_PX) return;
8035
+ if (!drag.moved) {
8036
+ drag.moved = true;
8037
+ e.currentTarget.setPointerCapture(e.pointerId);
8038
+ this.requestUpdate();
8039
+ }
8040
+ this._setView(this._zoom, drag.pan - dx / this._scale().pxPerMs);
8041
+ }
8042
+ _onPointerUp() {
8043
+ if (this._drag?.moved) {
8044
+ this._suppressClick = true;
8045
+ setTimeout(() => this._suppressClick = false);
8046
+ this.requestUpdate();
8047
+ }
8048
+ this._drag = null;
8049
+ }
8050
+ _refit() {
8051
+ this._zoom = 1;
8052
+ this._pan = 0;
8053
+ this._follow = true;
8054
+ }
8055
+ _select(key) {
8056
+ if (this._suppressClick) return;
8057
+ this.dispatchEvent(new CustomEvent("span-select", {
8058
+ detail: { key },
6443
8059
  bubbles: true,
6444
8060
  composed: true
6445
8061
  }));
6446
8062
  }
6447
- _onRegexInput(e) {
6448
- this._regex = e.target.value;
6449
- }
6450
- /** Text searched by the regex filter — element tag, name, subtitle and the
6451
- * changed property keys (the reason is worth searching for by name). */
6452
- _haystack(row) {
6453
- return `${row.meta?.tagName ?? ""} ${row.name} ${row.subtitle ?? ""} ${(row.changed ?? []).join(" ")}`;
6454
- }
6455
8063
  render() {
6456
- const elements = this._elementsCache;
6457
- let re = null;
6458
- let regexInvalid = false;
6459
- if (this._regex !== "") try {
6460
- re = new RegExp(this._regex, "i");
6461
- } catch {
6462
- regexInvalid = true;
6463
- }
6464
- const visible = this._rowsCache.filter((row) => this._isVisible(row) && (re === null || re.test(this._haystack(row))));
6465
- const selected = this._selectedKey === null ? void 0 : this._rowsCache.find((row) => row.key === this._selectedKey);
8064
+ const selected = this.selectedSpan;
8065
+ const detail = selected ? b`<timeline-span-detail
8066
+ filterable
8067
+ .span=${selected}
8068
+ ></timeline-span-detail>` : A;
8069
+ if (this._tracks.length === 0) return b`<div class="empty">
8070
+ ${this.visibleTracks.length === 0 ? "No tracks shown. Pick some above." : "No events recorded."}
8071
+ </div>
8072
+ ${detail}`;
8073
+ const scale = this._scale();
6466
8074
  return b`
6467
- ${this.events.length > 0 ? b`
6468
- <div class="filterbar">
6469
- ${elements.length > 0 ? b`
6470
- <span>Element:</span>
6471
- <select @change=${this._onFilterChange}>
6472
- <option
6473
- value=""
6474
- ?selected=${this._elementFilter === null}
6475
- >
6476
- All elements
6477
- </option>
6478
- ${elements.map((el) => b`
6479
- <option
6480
- value=${el.id}
6481
- ?selected=${this._elementFilter === el.id}
6482
- >
6483
- &lt;${el.tag}&gt; #${el.id}
6484
- </option>
6485
- `)}
6486
- </select>
6487
- ` : A}
6488
- <input
6489
- class="regex ${regexInvalid ? "invalid" : ""}"
6490
- type="text"
6491
- spellcheck="false"
6492
- placeholder="filter regex…"
6493
- title="Case-insensitive regex matched against element tag, title and subtitle"
6494
- .value=${this._regex}
6495
- @input=${this._onRegexInput}
6496
- />
6497
- <button
6498
- class=${this._raw ? "on" : ""}
6499
- title="Show one row per recorded event instead of collapsing start/end pairs"
6500
- @click=${() => {
6501
- this._raw = !this._raw;
6502
- }}
6503
- >
6504
- Raw
6505
- </button>
6506
- <span class="count"
6507
- >${visible.length} / ${this._rowsCache.length}</span
6508
- >
6509
- </div>
6510
- ` : A}
6511
- <div class="scroll" ${n(this._scrollRef)}>
6512
- ${visible.length === 0 ? b`
6513
- <div class="empty">
6514
- <span>No events recorded.</span>
6515
- <span class="hint"
6516
- >Press Record then interact with the page.</span
6517
- >
6518
- </div>
6519
- ` : c(visible, (row) => row.key, (row) => b`
6520
- <div
6521
- class="row ${this._selectedKey === row.key ? "selected" : ""}"
6522
- @click=${() => {
6523
- this._selectedKey = row.key;
6524
- }}
6525
- >
6526
- <span class="time">${row.start.toFixed(1)}ms</span>
6527
- <span
6528
- class="dot"
6529
- style=${"background:" + this._colorOf(row.layerId)}
6530
- ></span>
6531
- <span class="title">${row.name}</span>
6532
- ${row.changed?.length ? b`<span class="changed"
6533
- >${row.changed.join(", ")}</span
6534
- >` : A}
6535
- <span class="subtitle">${row.subtitle ?? A}</span>
6536
- <span
6537
- class="dur ${row.duration === void 0 ? "open" : ""}"
6538
- >${renderDuration(row)}</span
6539
- >
6540
- </div>
6541
- `)}
8075
+ <div class="axis">
8076
+ <div class="gutter"></div>
8077
+ <div class="ticks" ${n(this._ticksRef)}>
8078
+ ${this._width > 0 ? this._renderTicks(scale) : A}
8079
+ </div>
8080
+ </div>
8081
+ <div
8082
+ class="lanes ${this._drag?.moved ? "dragging" : ""}"
8083
+ title="Wheel to zoom, drag to pan, double-click to fit"
8084
+ @wheel=${this._onWheel}
8085
+ @pointerdown=${this._onPointerDown}
8086
+ @pointermove=${this._onPointerMove}
8087
+ @pointerup=${this._onPointerUp}
8088
+ @pointercancel=${this._onPointerUp}
8089
+ @dblclick=${this._refit}
8090
+ >
8091
+ ${this._tracks.map((track) => this._renderLane(track, scale))}
6542
8092
  </div>
6543
- ${selected ? this._renderDetail(selected) : A}
8093
+ ${detail}
6544
8094
  `;
6545
8095
  }
6546
- _renderDetail(row) {
6547
- const { meta } = row;
6548
- const src = meta?.source;
6549
- const data = row.events[0]?.data;
8096
+ _renderTicks(scale) {
8097
+ const step = niceStep(TICK_PX / scale.pxPerMs);
8098
+ const labels = [];
8099
+ for (let t = Math.ceil(scale.start / step) * step; t <= scale.end && labels.length < 200; t += step) labels.push(b`<span class="tick-label" style="left:${scale.toX(t)}px"
8100
+ >${formatAxis(t, step)}</span
8101
+ >`);
8102
+ return labels;
8103
+ }
8104
+ _renderLane(track, scale) {
8105
+ const layer = this.layers.find((l) => l.id === track.layerId);
8106
+ const color = layerColor(this.layers, track.layerId);
8107
+ const rows = Math.max(track.rows.length, 1);
6550
8108
  return b`
6551
- <div class="detail">
6552
- <table>
6553
- <tr>
6554
- <td class="key">layer</td>
6555
- <td class="val">${row.layerId}</td>
6556
- </tr>
6557
- <tr>
6558
- <td class="key">time</td>
6559
- <td class="val">${row.start.toFixed(3)} ms</td>
6560
- </tr>
6561
- ${row.duration !== void 0 ? b`<tr>
6562
- <td class="key">duration</td>
6563
- <td class="val">${row.duration.toFixed(3)} ms</td>
6564
- </tr>` : A}
6565
- ${row.changed?.length ? b`<tr>
6566
- <td class="key">changed</td>
6567
- <td class="val">${row.changed.join(", ")}</td>
6568
- </tr>` : A}
6569
- ${meta?.tagName ? b`<tr>
6570
- <td class="key">element</td>
6571
- <td class="val">
6572
- &lt;${meta.tagName}&gt; #${meta.elementId}
6573
- ${meta.elementId != null ? b`<a
6574
- class="filter-link"
6575
- @click=${() => {
6576
- this._elementFilter = meta.elementId;
6577
- }}
6578
- >filter</a
6579
- ><a
6580
- class="filter-link"
6581
- title="Open this element in the Components tab"
6582
- @click=${() => this._inspect(meta.elementId)}
6583
- >inspect</a
6584
- >` : A}
6585
- </td>
6586
- </tr>` : A}
6587
- ${src ? b`<tr>
6588
- <td class="key">source</td>
6589
- <td class="val">
6590
- <a
6591
- class="src-link"
6592
- title="Open this file in your editor"
6593
- @click=${() => openInEditor(src.file, src.line)}
6594
- >${src.file}:${src.line}</a
6595
- >
6596
- </td>
6597
- </tr>` : A}
6598
- ${data != null ? b`<tr>
6599
- <td class="key">data</td>
6600
- <td class="val">${JSON.stringify(data)}</td>
6601
- </tr>` : A}
6602
- </table>
8109
+ <div class="lane">
8110
+ <div class="gutter" title=${layer?.label ?? track.layerId}>
8111
+ <span class="dot" style="background:${color}"></span>
8112
+ ${layer?.label ?? track.layerId}
8113
+ </div>
8114
+ <div class="plot">
8115
+ <div class="rows" style="height:${rows * ROW_PX}px">
8116
+ ${this._width > 0 ? track.rows.map((row, i) => this._renderRow(row, i, scale, color)) : A}
8117
+ </div>
8118
+ </div>
6603
8119
  </div>
6604
8120
  `;
6605
8121
  }
8122
+ _renderRow(row, rowIndex, scale, color) {
8123
+ const [from, to] = visibleRange(row, scale.start, scale.end);
8124
+ const liveEdge = this._bounds.origin + this._bounds.extent;
8125
+ const marks = [];
8126
+ let lastTickPx = -Infinity;
8127
+ for (let i = from; i < to; i++) {
8128
+ const span = row[i];
8129
+ const open = span.end === void 0 && span.groupId !== void 0;
8130
+ const x0 = scale.toX(span.start);
8131
+ const x1 = scale.toX(span.end ?? (open ? liveEdge : span.start));
8132
+ const isTick = x1 - x0 < MIN_MARK_PX;
8133
+ const selected = span.key === this.selectedKey;
8134
+ if (isTick && !selected && Math.round(x0) === lastTickPx) continue;
8135
+ if (isTick) lastTickPx = Math.round(x0);
8136
+ const left = Math.max(x0, -1);
8137
+ const width = isTick ? MIN_MARK_PX : Math.min(x1, this._width + 1) - left;
8138
+ marks.push(b`<div
8139
+ class="mark ${isTick ? "tick" : ""} ${open ? "open" : ""} ${selected ? "selected" : ""}"
8140
+ role="button"
8141
+ tabindex="0"
8142
+ title=${describe(span)}
8143
+ style="left:${left}px;width:${width}px;top:${rowIndex * ROW_PX}px;background:${color}"
8144
+ @click=${() => this._select(span.key)}
8145
+ @keydown=${(e) => {
8146
+ if (e.key === "Enter" || e.key === " ") {
8147
+ e.preventDefault();
8148
+ this._select(span.key);
8149
+ }
8150
+ }}
8151
+ >
8152
+ ${isTick || width < 40 ? A : span.name}
8153
+ </div>`);
8154
+ }
8155
+ return marks;
8156
+ }
6606
8157
  };
6607
- __decorate([n$3({ type: Array })], TimelineEventList.prototype, "events", void 0);
6608
- __decorate([n$3({ type: Array })], TimelineEventList.prototype, "layers", void 0);
6609
- __decorate([r$2()], TimelineEventList.prototype, "_selectedKey", void 0);
6610
- __decorate([r$2()], TimelineEventList.prototype, "_elementFilter", void 0);
6611
- __decorate([r$2()], TimelineEventList.prototype, "_regex", void 0);
6612
- __decorate([r$2()], TimelineEventList.prototype, "_raw", void 0);
6613
- TimelineEventList = __decorate([t$2("timeline-event-list")], TimelineEventList);
8158
+ __decorate([n$3({ type: Array })], TimelineTracks.prototype, "spans", void 0);
8159
+ __decorate([n$3({ type: Array })], TimelineTracks.prototype, "layers", void 0);
8160
+ __decorate([n$3({ attribute: false })], TimelineTracks.prototype, "selectedKey", void 0);
8161
+ __decorate([n$3({ attribute: false })], TimelineTracks.prototype, "selectedSpan", void 0);
8162
+ __decorate([n$3({ type: Array })], TimelineTracks.prototype, "visibleTracks", void 0);
8163
+ __decorate([n$3({ type: Boolean })], TimelineTracks.prototype, "recording", void 0);
8164
+ __decorate([r$2()], TimelineTracks.prototype, "_zoom", void 0);
8165
+ __decorate([r$2()], TimelineTracks.prototype, "_pan", void 0);
8166
+ __decorate([r$2()], TimelineTracks.prototype, "_follow", void 0);
8167
+ __decorate([r$2()], TimelineTracks.prototype, "_width", void 0);
8168
+ TimelineTracks = __decorate([t$2("timeline-tracks")], TimelineTracks);
6614
8169
  //#endregion
6615
8170
  //#region src/panel/timeline-store.ts
6616
8171
  /**
@@ -6640,6 +8195,47 @@ TimelineEventList = __decorate([t$2("timeline-event-list")], TimelineEventList);
6640
8195
  * recent events; older ones scroll off.
6641
8196
  */
6642
8197
  var MAX_EVENTS = 5e3;
8198
+ /**
8199
+ * sessionStorage key for the id of the newest event Clear (or a fresh
8200
+ * recording) threw away. A reloaded panel is refilled from the node's
8201
+ * `timeline-history`, which Clear does not touch (agents still read the same
8202
+ * buffer through `recent-events`), so without this a reload would resurrect
8203
+ * everything the developer cleared. sessionStorage, not a module variable:
8204
+ * the point is to survive the reload. Ids are `${epoch}-${seq}` with a seq
8205
+ * that never resets across Clear (see `devframe/definition.ts`).
8206
+ */
8207
+ var CLEARED_THROUGH_KEY = "lit-devtools:timeline-cleared-through";
8208
+ var parseEventId = (id) => {
8209
+ if (id === void 0) return void 0;
8210
+ const cut = id.lastIndexOf("-");
8211
+ const seq = Number(id.slice(cut + 1));
8212
+ return cut > 0 && Number.isInteger(seq) ? {
8213
+ epoch: id.slice(0, cut),
8214
+ seq
8215
+ } : void 0;
8216
+ };
8217
+ var readClearedThrough = () => {
8218
+ try {
8219
+ return parseEventId(sessionStorage.getItem(CLEARED_THROUGH_KEY) ?? void 0);
8220
+ } catch {
8221
+ return;
8222
+ }
8223
+ };
8224
+ var rememberClearedThrough = (id) => {
8225
+ try {
8226
+ sessionStorage.setItem(CLEARED_THROUGH_KEY, id);
8227
+ } catch {}
8228
+ };
8229
+ /** Drops events at or before the cleared-through mark. A mark from another
8230
+ * epoch belongs to an earlier dev server and says nothing about these ids. */
8231
+ var withoutCleared = (batch) => {
8232
+ const mark = readClearedThrough();
8233
+ if (mark === void 0) return batch;
8234
+ return batch.filter((event) => {
8235
+ const id = parseEventId(event.id);
8236
+ return id === void 0 || id.epoch !== mark.epoch || id.seq > mark.seq;
8237
+ });
8238
+ };
6643
8239
  var events = [];
6644
8240
  var error = null;
6645
8241
  var started = false;
@@ -6654,6 +8250,8 @@ var getTimelineEvents = () => events;
6654
8250
  var getTimelineError = () => error;
6655
8251
  var clearTimelineEvents = () => {
6656
8252
  if (events.length === 0) return;
8253
+ const newest = events[events.length - 1].id;
8254
+ if (newest !== void 0) rememberClearedThrough(newest);
6657
8255
  events = [];
6658
8256
  notify$1();
6659
8257
  };
@@ -6679,8 +8277,16 @@ var start = async () => {
6679
8277
  return;
6680
8278
  }
6681
8279
  const reader = rpc.rpc.streaming.subscribe(meta.stream.channel, meta.stream.id, { highWaterMark: 4096 });
8280
+ const seeded = withoutCleared(await rpc.rpc.call("timeline-history").catch(() => [])).slice(-5e3);
8281
+ const seededIds = new Set(seeded.map((event) => event.id));
8282
+ if (seeded.length > 0) {
8283
+ events = seeded;
8284
+ notify$1();
8285
+ }
6682
8286
  for await (const batch of reader) {
6683
- const next = [...events, ...batch];
8287
+ const fresh = withoutCleared(batch).filter((event) => event.id === void 0 || !seededIds.has(event.id));
8288
+ if (fresh.length === 0) continue;
8289
+ const next = [...events, ...fresh];
6684
8290
  events = next.length > MAX_EVENTS ? next.slice(-5e3) : next;
6685
8291
  notify$1();
6686
8292
  }
@@ -6696,15 +8302,63 @@ var start = async () => {
6696
8302
  * Copyright 2026 Google LLC
6697
8303
  * SPDX-License-Identifier: BSD-3-Clause
6698
8304
  */
8305
+ /** localStorage key remembering List vs Tracks. */
8306
+ var MODE_LS_KEY = "lit-devtools-timeline-mode";
8307
+ /** localStorage key remembering which tracks the user hid. Stored as the
8308
+ * hidden set, not the shown one, so a layer seen for the first time (a
8309
+ * custom layer, a new built-in) gets a track by default. */
8310
+ var HIDDEN_TRACKS_LS_KEY = "lit-devtools-timeline-hidden-tracks";
8311
+ var MODE_TABS = [{
8312
+ id: "list",
8313
+ label: "List"
8314
+ }, {
8315
+ id: "tracks",
8316
+ label: "Tracks"
8317
+ }];
8318
+ var readMode = () => {
8319
+ try {
8320
+ return localStorage.getItem(MODE_LS_KEY) === "tracks" ? "tracks" : "list";
8321
+ } catch {
8322
+ return "list";
8323
+ }
8324
+ };
8325
+ var readHiddenTracks = () => {
8326
+ try {
8327
+ const parsed = JSON.parse(localStorage.getItem(HIDDEN_TRACKS_LS_KEY) ?? "[]");
8328
+ return new Set(Array.isArray(parsed) ? parsed.filter((id) => typeof id === "string") : []);
8329
+ } catch {
8330
+ return /* @__PURE__ */ new Set();
8331
+ }
8332
+ };
8333
+ var store = (key, value) => {
8334
+ try {
8335
+ localStorage.setItem(key, value);
8336
+ } catch {}
8337
+ };
6699
8338
  var TimelineView = class TimelineView extends i$2 {
6700
8339
  constructor(..._args) {
6701
8340
  super(..._args);
6702
8341
  this._recording = false;
8342
+ this._sessionSeen = false;
6703
8343
  this._exporting = false;
6704
8344
  this._exportNote = null;
6705
8345
  this._events = [];
6706
8346
  this._layers = [];
6707
8347
  this._error = null;
8348
+ this._mode = readMode();
8349
+ this._hiddenTracks = readHiddenTracks();
8350
+ this._selectedKey = null;
8351
+ this._elementFilter = null;
8352
+ this._regex = "";
8353
+ this._spans = [];
8354
+ this._spansOf = null;
8355
+ this._elements = [];
8356
+ this._filteredSpans = [];
8357
+ this._pendingEventId = null;
8358
+ this._revealSelection = false;
8359
+ this._pendingSettled = false;
8360
+ this._listRef = e();
8361
+ this._visibleTracks = [];
6708
8362
  this._baseLayers = [];
6709
8363
  this._rpc = null;
6710
8364
  this._sessionOff = null;
@@ -6749,20 +8403,57 @@ var TimelineView = class TimelineView extends i$2 {
6749
8403
  }
6750
8404
  .export-note {
6751
8405
  color: var(--lit-devtools-text-muted);
6752
- font-size: var(--lit-devtools-text-2xs);
6753
- overflow: hidden;
6754
- text-overflow: ellipsis;
6755
- white-space: nowrap;
8406
+ font-size: var(--lit-devtools-text-2xs);
8407
+ overflow: hidden;
8408
+ text-overflow: ellipsis;
8409
+ white-space: nowrap;
8410
+ }
8411
+ .record.active {
8412
+ border-color: var(--lit-devtools-error);
8413
+ background: var(--lit-devtools-error-soft);
8414
+ color: var(--lit-devtools-error);
8415
+ }
8416
+ .toolbar segmented-tabs {
8417
+ align-self: stretch;
8418
+ margin: calc(-1 * var(--lit-devtools-space-3)) 0;
8419
+ }
8420
+ .filterbar {
8421
+ display: flex;
8422
+ align-items: center;
8423
+ gap: var(--lit-devtools-space-3);
8424
+ padding: var(--lit-devtools-space-2) var(--lit-devtools-space-5);
8425
+ border-bottom: 1px solid var(--lit-devtools-border);
8426
+ font-size: var(--lit-devtools-text-2xs);
8427
+ color: var(--lit-devtools-text-muted);
8428
+ flex-shrink: 0;
8429
+ }
8430
+ .filterbar select,
8431
+ .filterbar input.regex {
8432
+ background: var(--lit-devtools-surface-elevated);
8433
+ color: var(--lit-devtools-text);
8434
+ border: 1px solid var(--lit-devtools-border-strong);
8435
+ border-radius: var(--lit-devtools-radius-sm);
8436
+ padding: var(--lit-devtools-space-1) var(--lit-devtools-space-3);
8437
+ font-size: var(--lit-devtools-text-2xs);
8438
+ font-family: var(--lit-devtools-font-mono);
8439
+ }
8440
+ .filterbar input.regex {
8441
+ min-width: 140px;
8442
+ }
8443
+ .filterbar input.regex::placeholder {
8444
+ color: var(--lit-devtools-text-muted);
6756
8445
  }
6757
- .record.active {
8446
+ .filterbar input.regex.invalid {
6758
8447
  border-color: var(--lit-devtools-error);
6759
- background: var(--lit-devtools-error-soft);
6760
- color: var(--lit-devtools-error);
6761
8448
  }
6762
- timeline-event-list {
8449
+ timeline-event-list,
8450
+ timeline-tracks {
6763
8451
  flex: 1;
6764
8452
  overflow: hidden;
6765
8453
  }
8454
+ [hidden] {
8455
+ display: none !important;
8456
+ }
6766
8457
  .error {
6767
8458
  padding: var(--lit-devtools-space-5);
6768
8459
  color: var(--lit-devtools-text-secondary);
@@ -6785,6 +8476,60 @@ var TimelineView = class TimelineView extends i$2 {
6785
8476
  this._storeOff?.();
6786
8477
  this._storeOff = null;
6787
8478
  }
8479
+ willUpdate(changed) {
8480
+ if (changed.has("_layers") || changed.has("_hiddenTracks")) this._visibleTracks = this._layers.filter((l) => l.enabled && !this._hiddenTracks.has(l.id)).map((l) => l.id);
8481
+ if (this._events !== this._spansOf) {
8482
+ this._spansOf = this._events;
8483
+ this._spans = toSpans(this._events);
8484
+ this._elements = listElements(this._events);
8485
+ if (this._elementFilter !== null && !this._elements.some((el) => el.id === this._elementFilter)) this._elementFilter = null;
8486
+ const key = this._selectedKey;
8487
+ if (key !== null && !isRawKey(key) && !this._spans.some((s) => s.key === key)) this._selectedKey = null;
8488
+ this._applyPendingEvent();
8489
+ }
8490
+ if (changed.has("_events") || changed.has("_elementFilter") || changed.has("_regex")) this._filteredSpans = filterSpans(this._spans, this._elementFilter, compileRegex(this._regex).re);
8491
+ }
8492
+ /**
8493
+ * Select the span a deep link named. Its start event is the natural target
8494
+ * (`span.events[0]`), but any of the span's events matches, so a link to
8495
+ * either half of a pair lands on the same row.
8496
+ *
8497
+ * An id the buffer does not hold — evicted, or from another session — ends
8498
+ * with no selection and no error, once there are events to have looked in.
8499
+ */
8500
+ selectEvent(id) {
8501
+ this._pendingEventId = id;
8502
+ this._applyPendingEvent();
8503
+ this.requestUpdate();
8504
+ }
8505
+ _applyPendingEvent() {
8506
+ const id = this._pendingEventId;
8507
+ if (id === null || this._spans.length === 0) return;
8508
+ const span = this._spans.find((s) => s.events.some((e) => e.id === id));
8509
+ this._pendingEventId = null;
8510
+ this._pendingSettled = true;
8511
+ this._selectedKey = span?.key ?? null;
8512
+ if (span !== void 0) this._revealSelection = true;
8513
+ }
8514
+ /** Id of the selected span's start event, for the shell's URL sync. While a
8515
+ * linked id is still waiting on the store it is reported as-is, so the
8516
+ * address bar does not drop it before the events land. */
8517
+ get selectedEventId() {
8518
+ if (this._pendingEventId !== null) return this._pendingEventId;
8519
+ const key = this._selectedKey;
8520
+ if (key === null) return null;
8521
+ return this._spans.find((s) => s.key === key)?.events[0]?.id ?? null;
8522
+ }
8523
+ updated(changed) {
8524
+ if (this._revealSelection) {
8525
+ this._revealSelection = false;
8526
+ this.updateComplete.then(() => this._listRef.value?.updateComplete).then(() => this._listRef.value?.reveal());
8527
+ }
8528
+ if (changed.has("_selectedKey") || this._pendingSettled) {
8529
+ this._pendingSettled = false;
8530
+ this.dispatchEvent(new CustomEvent("selection-change"));
8531
+ }
8532
+ }
6788
8533
  _readStore() {
6789
8534
  if (!this._active) return;
6790
8535
  this._events = getTimelineEvents();
@@ -6816,7 +8561,8 @@ var TimelineView = class TimelineView extends i$2 {
6816
8561
  * to the recording flag and merged layer list. */
6817
8562
  _applySession(state) {
6818
8563
  const { recordingState } = state.layers;
6819
- if (recordingState && !this._recording) clearTimelineEvents();
8564
+ if (this._sessionSeen && recordingState && !this._recording) clearTimelineEvents();
8565
+ this._sessionSeen = true;
6820
8566
  this._recording = recordingState;
6821
8567
  this._layers = this._mergeLayers(state.layers, state.customLayers);
6822
8568
  }
@@ -6871,10 +8617,46 @@ var TimelineView = class TimelineView extends i$2 {
6871
8617
  console.warn("[lit-devtools] toggle-layer failed", err);
6872
8618
  });
6873
8619
  }
8620
+ _onModeChange(e) {
8621
+ e.stopPropagation();
8622
+ this._mode = e.detail.value === "tracks" ? "tracks" : "list";
8623
+ store(MODE_LS_KEY, this._mode);
8624
+ if (this._mode === "list") this.updateComplete.then(() => this._listRef.value?.reveal());
8625
+ }
8626
+ _onTrackToggle(e) {
8627
+ e.stopPropagation();
8628
+ const hidden = new Set(this._hiddenTracks);
8629
+ if (!hidden.delete(e.detail.id)) hidden.add(e.detail.id);
8630
+ this._hiddenTracks = hidden;
8631
+ store(HIDDEN_TRACKS_LS_KEY, JSON.stringify([...hidden]));
8632
+ }
8633
+ _onElementSelect(e) {
8634
+ const v = e.target.value;
8635
+ this._elementFilter = v === "" ? null : Number(v);
8636
+ }
8637
+ _onRegexInput(e) {
8638
+ this._regex = e.target.value;
8639
+ }
8640
+ /** The detail pane's **filter** link. */
8641
+ _onElementFilter(e) {
8642
+ this._elementFilter = e.detail.id;
8643
+ }
8644
+ _onSpanSelect(e) {
8645
+ this._pendingEventId = null;
8646
+ this._selectedKey = e.detail.key;
8647
+ }
6874
8648
  render() {
6875
8649
  if (this._error !== null) return b`<div class="error">${this._error}</div>`;
8650
+ const tracks = this._mode === "tracks";
8651
+ const captured = this._layers.filter((l) => l.enabled);
6876
8652
  return b`
6877
8653
  <div class="toolbar">
8654
+ <segmented-tabs
8655
+ size="sm"
8656
+ .items=${MODE_TABS}
8657
+ .value=${this._mode}
8658
+ @change=${this._onModeChange}
8659
+ ></segmented-tabs>
6878
8660
  ${this._exportNote === null ? A : b`<span class="export-note">${this._exportNote}</span>`}
6879
8661
  <span class="spacer"></span>
6880
8662
  <button
@@ -6896,10 +8678,67 @@ var TimelineView = class TimelineView extends i$2 {
6896
8678
  .layers=${this._layers}
6897
8679
  @layer-toggle=${this._onLayerToggle}
6898
8680
  ></timeline-layers>
8681
+ ${tracks ? b`<timeline-layers
8682
+ caption="Tracks"
8683
+ .layers=${captured.map((l) => ({
8684
+ ...l,
8685
+ enabled: !this._hiddenTracks.has(l.id)
8686
+ }))}
8687
+ @layer-toggle=${this._onTrackToggle}
8688
+ ></timeline-layers>` : A}
8689
+ ${this._events.length > 0 ? b`<div class="filterbar">
8690
+ ${this._elements.length > 0 ? b`
8691
+ <span>Element:</span>
8692
+ <select @change=${this._onElementSelect}>
8693
+ <option
8694
+ value=""
8695
+ ?selected=${this._elementFilter === null}
8696
+ >
8697
+ All elements
8698
+ </option>
8699
+ ${this._elements.map((el) => b`
8700
+ <option
8701
+ value=${el.id}
8702
+ ?selected=${this._elementFilter === el.id}
8703
+ >
8704
+ &lt;${el.tag}&gt; #${el.id}
8705
+ </option>
8706
+ `)}
8707
+ </select>
8708
+ ` : A}
8709
+ <input
8710
+ class="regex ${compileRegex(this._regex).invalid ? "invalid" : ""}"
8711
+ type="text"
8712
+ spellcheck="false"
8713
+ placeholder="filter regex…"
8714
+ title="Case-insensitive regex matched against element tag, title and subtitle"
8715
+ .value=${this._regex}
8716
+ @input=${this._onRegexInput}
8717
+ />
8718
+ </div>` : A}
6899
8719
  <timeline-event-list
8720
+ ${n(this._listRef)}
8721
+ ?hidden=${tracks}
6900
8722
  .events=${this._events}
8723
+ .spans=${this._spans}
6901
8724
  .layers=${this._layers}
8725
+ .selectedKey=${this._selectedKey}
8726
+ .elementFilter=${this._elementFilter}
8727
+ .regex=${this._regex}
8728
+ @span-select=${this._onSpanSelect}
8729
+ @element-filter=${this._onElementFilter}
6902
8730
  ></timeline-event-list>
8731
+ <timeline-tracks
8732
+ ?hidden=${!tracks}
8733
+ .spans=${this._filteredSpans}
8734
+ .layers=${this._layers}
8735
+ .visibleTracks=${this._visibleTracks}
8736
+ .selectedKey=${this._selectedKey}
8737
+ .selectedSpan=${this._selectedKey === null ? void 0 : this._spans.find((s) => s.key === this._selectedKey)}
8738
+ ?recording=${this._recording}
8739
+ @span-select=${this._onSpanSelect}
8740
+ @element-filter=${this._onElementFilter}
8741
+ ></timeline-tracks>
6903
8742
  `;
6904
8743
  }
6905
8744
  };
@@ -6909,6 +8748,11 @@ __decorate([r$2()], TimelineView.prototype, "_exportNote", void 0);
6909
8748
  __decorate([r$2()], TimelineView.prototype, "_events", void 0);
6910
8749
  __decorate([r$2()], TimelineView.prototype, "_layers", void 0);
6911
8750
  __decorate([r$2()], TimelineView.prototype, "_error", void 0);
8751
+ __decorate([r$2()], TimelineView.prototype, "_mode", void 0);
8752
+ __decorate([r$2()], TimelineView.prototype, "_hiddenTracks", void 0);
8753
+ __decorate([r$2()], TimelineView.prototype, "_selectedKey", void 0);
8754
+ __decorate([r$2()], TimelineView.prototype, "_elementFilter", void 0);
8755
+ __decorate([r$2()], TimelineView.prototype, "_regex", void 0);
6912
8756
  TimelineView = __decorate([t$2("timeline-view")], TimelineView);
6913
8757
  //#endregion
6914
8758
  //#region src/types/hmr-incompatibility.ts
@@ -7723,6 +9567,29 @@ var writeLocal = (override) => {
7723
9567
  else localStorage.setItem(SETTINGS_OVERRIDE_LS_KEY, JSON.stringify(override));
7724
9568
  } catch {}
7725
9569
  };
9570
+ /**
9571
+ * The config values the current overrides were made against; `{}` when unset
9572
+ * or unparsable. Deliberately separate from the override itself, so the
9573
+ * runtime's apply and the live push never carry it.
9574
+ */
9575
+ var readBaselines = () => {
9576
+ try {
9577
+ const raw = localStorage.getItem(OVERRIDE_BASELINES_LS_KEY);
9578
+ if (raw !== null) return JSON.parse(raw);
9579
+ } catch {}
9580
+ return {};
9581
+ };
9582
+ var writeLocalBaselines = (baselines) => {
9583
+ try {
9584
+ if (baselines === null || Object.keys(baselines).length === 0) localStorage.removeItem(OVERRIDE_BASELINES_LS_KEY);
9585
+ else localStorage.setItem(OVERRIDE_BASELINES_LS_KEY, JSON.stringify(baselines));
9586
+ } catch {}
9587
+ };
9588
+ /** Write the durable baselines (or delete them when empty), best-effort. */
9589
+ var persistBaselines = (baselines) => {
9590
+ const empty = baselines === void 0 || Object.keys(baselines).length === 0;
9591
+ litRpc().then((rpc) => empty ? rpc.settings.global.delete("overrideBaselines") : rpc.settings.global.set("overrideBaselines", baselines)).catch(() => {});
9592
+ };
7726
9593
  /** Send an override to the app runtime over RPC, best-effort. */
7727
9594
  var pushLive = (override) => {
7728
9595
  litRpc().then((rpc) => rpc.rpc.call("set-settings-override", override)).catch(() => {});
@@ -7742,10 +9609,18 @@ var onOverrideChange = (cb) => {
7742
9609
  listeners.delete(cb);
7743
9610
  };
7744
9611
  };
7745
- /** A change made in this panel: all three copies, then listeners. */
7746
- var commitOverride = (next) => {
9612
+ /**
9613
+ * A change made in this panel: all three copies, then listeners. `baselines`,
9614
+ * when given, replaces the recorded baselines (local and durable; never sent
9615
+ * to the page).
9616
+ */
9617
+ var commitOverride = (next, baselines) => {
7747
9618
  writeLocal(next);
7748
9619
  persist(next);
9620
+ if (baselines !== void 0) {
9621
+ writeLocalBaselines(baselines);
9622
+ persistBaselines(baselines);
9623
+ }
7749
9624
  pushLive(next);
7750
9625
  notify(next);
7751
9626
  };
@@ -7760,8 +9635,9 @@ var patchOverride = (patch) => {
7760
9635
  * A value that arrived *from* the durable store: mirror it locally and to the
7761
9636
  * page, but don't write it back — that would be a pointless round trip.
7762
9637
  */
7763
- var adoptOverride = (next) => {
9638
+ var adoptOverride = (next, baselines) => {
7764
9639
  writeLocal(next);
9640
+ if (baselines !== void 0) writeLocalBaselines(baselines);
7765
9641
  pushLive(next);
7766
9642
  notify(next);
7767
9643
  };
@@ -7773,9 +9649,46 @@ var adoptOverride = (next) => {
7773
9649
  var resetOverride = (envValues) => {
7774
9650
  writeLocal(null);
7775
9651
  persist(void 0);
9652
+ writeLocalBaselines(null);
9653
+ persistBaselines(void 0);
7776
9654
  if (envValues !== void 0) pushLive(envValues);
7777
9655
  notify({});
7778
9656
  };
9657
+ /**
9658
+ * Drop one key from the override, leaving the rest in place. `baseline` is
9659
+ * that key's resolved config value; it goes to the live runtime alongside the
9660
+ * remaining override (a missing field would leave the current live value in
9661
+ * place) but isn't stored, so the key reads as un-overridden again.
9662
+ */
9663
+ var dropOverrideKey = (key, baseline) => {
9664
+ const next = { ...readOverride() };
9665
+ delete next[key];
9666
+ const empty = Object.keys(next).length === 0;
9667
+ writeLocal(empty ? null : next);
9668
+ persist(empty ? void 0 : next);
9669
+ const baselines = readBaselines();
9670
+ delete baselines[key];
9671
+ writeLocalBaselines(baselines);
9672
+ persistBaselines(baselines);
9673
+ pushLive({
9674
+ ...next,
9675
+ [key]: baseline
9676
+ });
9677
+ notify(next);
9678
+ };
9679
+ /**
9680
+ * Re-stamp one key's baseline to the config value it has now ("Keep"), so the
9681
+ * changed-config hint goes away while the override stays.
9682
+ */
9683
+ var keepBaseline = (key, current) => {
9684
+ const next = {
9685
+ ...readBaselines(),
9686
+ [key]: current
9687
+ };
9688
+ writeLocalBaselines(next);
9689
+ persistBaselines(next);
9690
+ notify(readOverride());
9691
+ };
7779
9692
  //#endregion
7780
9693
  //#region src/panel/components-view.ts
7781
9694
  /**
@@ -8833,6 +10746,8 @@ var DEEP_LINK_TABS = [
8833
10746
  "settings"
8834
10747
  ];
8835
10748
  var isTab = (value) => typeof value === "string" && DEEP_LINK_TABS.includes(value);
10749
+ /** True when a link asks for anything at all. */
10750
+ var hasLink = (link) => link.tab !== void 0 || link.componentId !== void 0 || link.eventId !== void 0;
8836
10751
  /** Parse a `DeepLink` out of hash params, ignoring anything unrecognised. */
8837
10752
  var fromParams = (params) => {
8838
10753
  const link = {};
@@ -8840,6 +10755,8 @@ var fromParams = (params) => {
8840
10755
  if (isTab(tab)) link.tab = tab;
8841
10756
  const id = Number(params.get("component"));
8842
10757
  if (params.get("component") !== null && Number.isInteger(id)) link.componentId = id;
10758
+ const eventId = params.get("event");
10759
+ if (eventId !== null && eventId !== "") link.eventId = eventId;
8843
10760
  return link;
8844
10761
  };
8845
10762
  /** Read the link encoded in the current URL hash, if any. */
@@ -8859,6 +10776,8 @@ var writeHashLink = (link) => {
8859
10776
  else params.set("tab", link.tab);
8860
10777
  if (link.componentId === void 0) params.delete("component");
8861
10778
  else params.set("component", String(link.componentId));
10779
+ if (link.eventId === void 0) params.delete("event");
10780
+ else params.set("event", link.eventId);
8862
10781
  const next = params.toString();
8863
10782
  if (next === location.hash.replace(/^#/, "")) return;
8864
10783
  history.replaceState(history.state, "", next === "" ? location.pathname : `#${next}`);
@@ -8873,7 +10792,7 @@ var writeHashLink = (link) => {
8873
10792
  */
8874
10793
  var onDeepLink = (apply) => {
8875
10794
  const hashLink = readHashLink();
8876
- if (hashLink.tab !== void 0 || hashLink.componentId !== void 0) apply(hashLink);
10795
+ if (hasLink(hashLink)) apply(hashLink);
8877
10796
  window.addEventListener("hashchange", () => apply(readHashLink()));
8878
10797
  (async () => {
8879
10798
  try {
@@ -8888,7 +10807,9 @@ var onDeepLink = (apply) => {
8888
10807
  if (isTab(tab)) link.tab = tab;
8889
10808
  const id = params["componentId"];
8890
10809
  if (typeof id === "number" && Number.isInteger(id)) link.componentId = id;
8891
- if (link.tab !== void 0 || link.componentId !== void 0) apply(link);
10810
+ const eventId = params["eventId"];
10811
+ if (typeof eventId === "string" && eventId !== "") link.eventId = eventId;
10812
+ if (hasLink(link)) apply(link);
8892
10813
  };
8893
10814
  handle(state.value());
8894
10815
  state.on("updated", handle);
@@ -8896,6 +10817,27 @@ var onDeepLink = (apply) => {
8896
10817
  })();
8897
10818
  };
8898
10819
  //#endregion
10820
+ //#region src/lib/override-baselines.ts
10821
+ /**
10822
+ * @license
10823
+ * Copyright 2026 Google LLC
10824
+ * SPDX-License-Identifier: BSD-3-Clause
10825
+ */
10826
+ /**
10827
+ * Whether the config value an override was made against has since changed.
10828
+ *
10829
+ * `recorded` is `undefined` for a legacy override that predates baseline
10830
+ * tracking, which never counts as a change (no false positives). Values are
10831
+ * compared structurally. The source-overlay editor reports `'custom'` for an
10832
+ * `EditorConfig` object, which can't be compared meaningfully, so any side
10833
+ * being `'custom'` skips the check.
10834
+ */
10835
+ var baselineChanged = (key, recorded, current) => {
10836
+ if (recorded === void 0 || current === void 0) return false;
10837
+ if (key === "sourceOverlayEditor" && (recorded === "custom" || current === "custom")) return false;
10838
+ return JSON.stringify(recorded) !== JSON.stringify(current);
10839
+ };
10840
+ //#endregion
8899
10841
  //#region src/panel/devtools-settings.ts
8900
10842
  /**
8901
10843
  * @license
@@ -8907,6 +10849,7 @@ var DevtoolsSettings = class DevtoolsSettings extends i$2 {
8907
10849
  super(..._args);
8908
10850
  this._settings = null;
8909
10851
  this._override = {};
10852
+ this._recorded = {};
8910
10853
  this._loaded = false;
8911
10854
  this._colorScheme = "auto";
8912
10855
  this._unsubscribeOverride = null;
@@ -9006,6 +10949,52 @@ var DevtoolsSettings = class DevtoolsSettings extends i$2 {
9006
10949
  margin-left: var(--lit-devtools-space-3);
9007
10950
  vertical-align: middle;
9008
10951
  }
10952
+ .src {
10953
+ margin-left: var(--lit-devtools-space-3);
10954
+ vertical-align: middle;
10955
+ }
10956
+ .row-reset {
10957
+ appearance: none;
10958
+ border: 1px solid var(--lit-devtools-border-strong);
10959
+ background: var(--lit-devtools-surface-elevated);
10960
+ color: var(--lit-devtools-text-muted);
10961
+ border-radius: var(--lit-devtools-radius-sm);
10962
+ font: inherit;
10963
+ font-size: var(--lit-devtools-text-2xs);
10964
+ line-height: 1;
10965
+ padding: 1px var(--lit-devtools-space-2);
10966
+ margin-left: var(--lit-devtools-space-3);
10967
+ vertical-align: middle;
10968
+ cursor: pointer;
10969
+ }
10970
+ .row-reset:hover {
10971
+ background: var(--lit-devtools-surface-hover);
10972
+ color: var(--lit-devtools-text);
10973
+ }
10974
+ .nudge {
10975
+ display: flex;
10976
+ align-items: center;
10977
+ flex-wrap: wrap;
10978
+ gap: var(--lit-devtools-space-3);
10979
+ margin-top: var(--lit-devtools-space-2);
10980
+ color: var(--lit-devtools-warning);
10981
+ font-family: inherit;
10982
+ font-size: var(--lit-devtools-text-2xs);
10983
+ }
10984
+ .nudge button {
10985
+ appearance: none;
10986
+ border: 1px solid var(--lit-devtools-border-strong);
10987
+ background: var(--lit-devtools-surface-elevated);
10988
+ color: var(--lit-devtools-text);
10989
+ border-radius: var(--lit-devtools-radius-sm);
10990
+ font: inherit;
10991
+ line-height: 1;
10992
+ padding: 1px var(--lit-devtools-space-3);
10993
+ cursor: pointer;
10994
+ }
10995
+ .nudge button:hover {
10996
+ background: var(--lit-devtools-surface-hover);
10997
+ }
9009
10998
  label.toggle {
9010
10999
  display: inline-flex;
9011
11000
  align-items: center;
@@ -9046,8 +11035,10 @@ var DevtoolsSettings = class DevtoolsSettings extends i$2 {
9046
11035
  connectedCallback() {
9047
11036
  super.connectedCallback();
9048
11037
  this._override = readOverride();
11038
+ this._recorded = readBaselines();
9049
11039
  this._unsubscribeOverride = onOverrideChange((o) => {
9050
11040
  this._override = o;
11041
+ this._recorded = readBaselines();
9051
11042
  });
9052
11043
  this._colorScheme = readColorSchemePreference();
9053
11044
  this._fetch();
@@ -9087,16 +11078,16 @@ var DevtoolsSettings = class DevtoolsSettings extends i$2 {
9087
11078
  }
9088
11079
  /**
9089
11080
  * Apply a settings snapshot. Both branches no-op when the value already
9090
- * matches, which is what stops `_adopt` -> `_push` -> store write ->
11081
+ * matches, which is what stops `_adopt` -> `commitOverride` -> store write ->
9091
11082
  * `onChange` -> `_adopt` from looping.
9092
11083
  */
9093
11084
  _adopt(all) {
9094
- const { appearance, override } = all;
11085
+ const { appearance, override, overrideBaselines } = all;
9095
11086
  if (appearance !== void 0 && appearance !== this._colorScheme) {
9096
11087
  this._colorScheme = appearance;
9097
11088
  setColorSchemePreference(appearance);
9098
11089
  }
9099
- if (override !== void 0 && JSON.stringify(override) !== JSON.stringify(this._override)) adoptOverride(override);
11090
+ if (override !== void 0 && (JSON.stringify(override) !== JSON.stringify(this._override) || JSON.stringify(overrideBaselines ?? {}) !== JSON.stringify(readBaselines()))) adoptOverride(override, overrideBaselines ?? {});
9100
11091
  }
9101
11092
  /** Write one preference through to the durable store, best-effort. */
9102
11093
  _persist(key, value) {
@@ -9117,43 +11108,91 @@ var DevtoolsSettings = class DevtoolsSettings extends i$2 {
9117
11108
  this._loaded = true;
9118
11109
  }
9119
11110
  }
9120
- /** Persist the merged override and push it to the app runtime. */
9121
- _push(override) {
9122
- commitOverride(override);
9123
- }
9124
11111
  _set(key, value) {
9125
- this._push({
11112
+ const s = this._settings;
11113
+ const baselines = readBaselines();
11114
+ if (s !== null && key in this._baselines(s)) baselines[key] = this._baselines(s)[key];
11115
+ commitOverride({
9126
11116
  ...this._override,
9127
11117
  [key]: value
9128
- });
11118
+ }, baselines);
9129
11119
  }
9130
- /** Clear overrides and revert the runtime to the resolved env values. */
9131
- _reset() {
9132
- const s = this._settings;
9133
- resetOverride(s === null ? void 0 : {
11120
+ /** The resolved config value of every overridable setting. */
11121
+ _baselines(s) {
11122
+ return {
9134
11123
  hmrReconnect: s.hmr.reconnect,
9135
11124
  hmrOnIncompatible: s.hmr.onIncompatible,
9136
11125
  hmrIndicatorVisible: s.hmr.indicatorEnabled,
9137
11126
  hmrIndicatorCount: s.hmr.indicatorCount,
9138
- sourceOverlayEditor: s.sourceOverlay.editor,
11127
+ sourceOverlayEditor: s.sourceOverlay.editor
11128
+ };
11129
+ }
11130
+ /** Clear overrides and revert the runtime to the resolved env values. */
11131
+ _reset() {
11132
+ const s = this._settings;
11133
+ resetOverride(s === null ? void 0 : {
11134
+ ...this._baselines(s),
9139
11135
  flashUpdates: false,
9140
11136
  flashUpdatesRamp: false
9141
11137
  });
9142
11138
  }
11139
+ /** Drop one overridden setting, reverting it (live) to its baseline. */
11140
+ _resetKey(key) {
11141
+ const s = this._settings;
11142
+ if (s === null) return;
11143
+ dropOverrideKey(key, this._baselines(s)[key]);
11144
+ }
9143
11145
  _pill(on) {
9144
11146
  return b`<span class="pill ${on ? "on" : ""}"
9145
11147
  >${on ? "enabled" : "disabled"}</span
9146
11148
  >`;
9147
11149
  }
9148
- _ovr(overridden) {
9149
- return overridden ? b`<span class="ovr">(overridden)</span>` : A;
11150
+ /**
11151
+ * Origin badge behind a value, from the server-side provenance:
11152
+ * "(env)", "(option)" or "(default)". Nothing when the server sent none.
11153
+ */
11154
+ _source(key) {
11155
+ const src = this._settings?.sources?.[key];
11156
+ return src ? b`<span class="env src">(${src})</span>` : A;
11157
+ }
11158
+ /**
11159
+ * Badge for an overridable row. Without an override it's the plain origin
11160
+ * badge; with one it's "(overridden)", the baseline it replaced
11161
+ * ("env: Zed") and a reset for just this row. `baseline` is the config
11162
+ * value as the row displays it, `fmt` renders a recorded raw value the same
11163
+ * way. When the config value moved since the override was made, a second
11164
+ * line says so and offers Reset or Keep.
11165
+ */
11166
+ _ovrSource(key, baseline, fmt = String) {
11167
+ if (this._override[key] === void 0) return this._source(key);
11168
+ const src = this._settings?.sources?.[key] ?? "config";
11169
+ const s = this._settings;
11170
+ const current = s === null ? void 0 : this._baselines(s)[key];
11171
+ return b`<span class="ovr">(overridden)</span>
11172
+ <span class="env">${src}: ${baseline}</span>
11173
+ <button
11174
+ class="row-reset"
11175
+ title="Reset to the ${src} value"
11176
+ aria-label="Reset to the ${src} value"
11177
+ @click=${() => this._resetKey(key)}
11178
+ >
11179
+ ×
11180
+ </button>
11181
+ ${baselineChanged(key, this._recorded[key], current) ? b`<div class="nudge" data-nudge=${key}>
11182
+ <span
11183
+ >Config changed since you overrode this: was
11184
+ ${fmt(this._recorded[key])}, now ${baseline}</span
11185
+ >
11186
+ <button @click=${() => this._resetKey(key)}>Reset</button>
11187
+ <button @click=${() => keepBaseline(key, current)}>Keep</button>
11188
+ </div>` : A}`;
9150
11189
  }
9151
- _readonlyRow(key, value, env) {
11190
+ _readonlyRow(key, value, env, source) {
9152
11191
  return b`
9153
11192
  <tr>
9154
11193
  <td class="key">${key}</td>
9155
11194
  <td class="val">
9156
- ${String(value)}
11195
+ ${String(value)} ${source ? this._source(source) : A}
9157
11196
  ${env ? b`<span class="env">${env}</span>` : A}
9158
11197
  </td>
9159
11198
  </tr>
@@ -9209,7 +11248,7 @@ var DevtoolsSettings = class DevtoolsSettings extends i$2 {
9209
11248
  />
9210
11249
  ${reconnect ? "on" : "off"}
9211
11250
  </label>
9212
- ${this._ovr(o.hmrReconnect !== void 0)}
11251
+ ${this._ovrSource("hmrReconnect", s.hmr.reconnect ? "on" : "off", (v) => v ? "on" : "off")}
9213
11252
  </td>
9214
11253
  </tr>
9215
11254
  <tr>
@@ -9225,7 +11264,7 @@ var DevtoolsSettings = class DevtoolsSettings extends i$2 {
9225
11264
  warn
9226
11265
  </option>
9227
11266
  </select>
9228
- ${this._ovr(o.hmrOnIncompatible !== void 0)}
11267
+ ${this._ovrSource("hmrOnIncompatible", s.hmr.onIncompatible)}
9229
11268
  </td>
9230
11269
  </tr>
9231
11270
  <tr class=${s.hmr.indicatorEnabled ? "" : "row-disabled"}>
@@ -9240,7 +11279,7 @@ var DevtoolsSettings = class DevtoolsSettings extends i$2 {
9240
11279
  />
9241
11280
  ${s.hmr.indicatorEnabled ? indicatorVisible ? "shown" : "hidden" : "off (config)"}
9242
11281
  </label>
9243
- ${this._ovr(o.hmrIndicatorVisible !== void 0)}
11282
+ ${this._ovrSource("hmrIndicatorVisible", s.hmr.indicatorEnabled ? "shown" : "off (config)", (v) => v ? "shown" : "off (config)")}
9244
11283
  </td>
9245
11284
  </tr>
9246
11285
  <tr class=${s.hmr.indicatorEnabled ? "" : "row-disabled"}>
@@ -9255,10 +11294,41 @@ var DevtoolsSettings = class DevtoolsSettings extends i$2 {
9255
11294
  />
9256
11295
  ${indicatorCount ? "shown" : "hidden"}
9257
11296
  </label>
9258
- ${this._ovr(o.hmrIndicatorCount !== void 0)}
11297
+ ${this._ovrSource("hmrIndicatorCount", s.hmr.indicatorCount ? "shown" : "hidden", (v) => v ? "shown" : "hidden")}
9259
11298
  </td>
9260
11299
  </tr>
9261
11300
  </table>
11301
+ `;
11302
+ }
11303
+ /**
11304
+ * The editor select. A custom `EditorConfig` object (reported as
11305
+ * `'custom'`) isn't one of the built-in choices, so the select is disabled
11306
+ * and just shows "Custom".
11307
+ */
11308
+ _renderEditorRow(s) {
11309
+ const baseline = s.sourceOverlay.editor;
11310
+ const custom = baseline === "custom";
11311
+ const current = this._override.sourceOverlayEditor ?? baseline;
11312
+ const editorLabel = (value) => SOURCE_OVERLAY_EDITORS.find((ed) => ed.value === value)?.label ?? (value === "custom" ? "Custom" : value);
11313
+ const label = editorLabel(baseline);
11314
+ return b`
11315
+ <tr>
11316
+ <td class="key">editor</td>
11317
+ <td class="val">
11318
+ <select
11319
+ ?disabled=${custom}
11320
+ @change=${(e) => this._set("sourceOverlayEditor", e.target.value)}
11321
+ >
11322
+ ${custom ? b`<option value="custom" selected>Custom</option>` : SOURCE_OVERLAY_EDITORS.map((ed) => b`<option
11323
+ value=${ed.value}
11324
+ ?selected=${ed.value === current}
11325
+ >
11326
+ ${ed.label}
11327
+ </option>`)}
11328
+ </select>
11329
+ ${this._ovrSource("sourceOverlayEditor", label, (v) => editorLabel(String(v)))}
11330
+ </td>
11331
+ </tr>
9262
11332
  `;
9263
11333
  }
9264
11334
  /**
@@ -9329,24 +11399,9 @@ var DevtoolsSettings = class DevtoolsSettings extends i$2 {
9329
11399
  <section>
9330
11400
  <h3>Source Overlay ${this._pill(s.sourceOverlay.enabled)}</h3>
9331
11401
  ${s.sourceOverlay.enabled ? b`<table>
9332
- ${this._readonlyRow("hotkey", `Ctrl+Shift+${s.sourceOverlay.key.toUpperCase()}`, "LIT_PLUGIN_SOURCE_OVERLAY_KEY")}
9333
- <tr>
9334
- <td class="key">editor</td>
9335
- <td class="val">
9336
- <select
9337
- @change=${(e) => this._set("sourceOverlayEditor", e.target.value)}
9338
- >
9339
- ${SOURCE_OVERLAY_EDITORS.map((ed) => b`<option
9340
- value=${ed.value}
9341
- ?selected=${ed.value === (this._override.sourceOverlayEditor ?? s.sourceOverlay.editor)}
9342
- >
9343
- ${ed.label}
9344
- </option>`)}
9345
- </select>
9346
- ${this._ovr(this._override.sourceOverlayEditor !== void 0)}
9347
- </td>
9348
- </tr>
9349
- ${this._readonlyRow("throttle (ms)", s.sourceOverlay.throttleMs, "LIT_PLUGIN_SOURCE_OVERLAY_THROTTLE_MS")}
11402
+ ${this._readonlyRow("hotkey", `Ctrl+Shift+${s.sourceOverlay.key.toUpperCase()}`, "LIT_PLUGIN_SOURCE_OVERLAY_KEY", "sourceOverlayKey")}
11403
+ ${this._renderEditorRow(s)}
11404
+ ${this._readonlyRow("throttle (ms)", s.sourceOverlay.throttleMs, "LIT_PLUGIN_SOURCE_OVERLAY_THROTTLE_MS", "sourceOverlayThrottleMs")}
9350
11405
  </table>` : b`<p class="empty">
9351
11406
  Enable with <code>sourceOverlay: true</code> or
9352
11407
  <code>LIT_PLUGIN_SOURCE_OVERLAY=true</code>.
@@ -9373,125 +11428,11 @@ var DevtoolsSettings = class DevtoolsSettings extends i$2 {
9373
11428
  };
9374
11429
  __decorate([r$2()], DevtoolsSettings.prototype, "_settings", void 0);
9375
11430
  __decorate([r$2()], DevtoolsSettings.prototype, "_override", void 0);
11431
+ __decorate([r$2()], DevtoolsSettings.prototype, "_recorded", void 0);
9376
11432
  __decorate([r$2()], DevtoolsSettings.prototype, "_loaded", void 0);
9377
11433
  __decorate([r$2()], DevtoolsSettings.prototype, "_colorScheme", void 0);
9378
11434
  DevtoolsSettings = __decorate([t$2("devtools-settings")], DevtoolsSettings);
9379
11435
  //#endregion
9380
- //#region src/lib/segmented-tabs.ts
9381
- /**
9382
- * @license
9383
- * Copyright 2026 Google LLC
9384
- * SPDX-License-Identifier: BSD-3-Clause
9385
- */
9386
- var SegmentedTabs = class SegmentedTabs extends i$2 {
9387
- static {
9388
- this.styles = [tokens, i$5`
9389
- :host {
9390
- display: flex;
9391
- gap: var(--lit-devtools-space-1);
9392
- align-self: stretch;
9393
- }
9394
- button {
9395
- appearance: none;
9396
- display: flex;
9397
- align-items: center;
9398
- gap: var(--lit-devtools-space-2);
9399
- border: 0;
9400
- background: none;
9401
- font: inherit;
9402
- color: var(--lit-devtools-text-muted);
9403
- padding: 0 var(--lit-devtools-space-5);
9404
- cursor: pointer;
9405
- border-bottom: 2px solid transparent;
9406
- white-space: nowrap;
9407
- transition:
9408
- color var(--lit-devtools-dur-fast) var(--lit-devtools-ease-standard),
9409
- border-color var(--lit-devtools-dur-fast)
9410
- var(--lit-devtools-ease-standard),
9411
- background var(--lit-devtools-dur-fast)
9412
- var(--lit-devtools-ease-standard);
9413
- }
9414
- button:hover {
9415
- color: var(--lit-devtools-text);
9416
- background: var(--lit-devtools-surface-hover);
9417
- }
9418
- button.active {
9419
- color: var(--lit-devtools-accent);
9420
- border-bottom-color: var(--lit-devtools-accent);
9421
- }
9422
- button:focus-visible {
9423
- outline: 2px solid var(--lit-devtools-accent-ring);
9424
- outline-offset: -2px;
9425
- }
9426
- button svg {
9427
- width: 18px;
9428
- height: 18px;
9429
- flex-shrink: 0;
9430
- }
9431
- .badge {
9432
- flex-shrink: 0;
9433
- min-width: 16px;
9434
- padding: 0 var(--lit-devtools-space-1);
9435
- border-radius: var(--lit-devtools-radius-pill);
9436
- background: var(--lit-devtools-error-soft);
9437
- color: var(--lit-devtools-error);
9438
- font-size: var(--lit-devtools-text-xs);
9439
- line-height: 16px;
9440
- text-align: center;
9441
- }
9442
- :host([size='sm']) button {
9443
- font-size: var(--lit-devtools-text-xs);
9444
- padding: 0 var(--lit-devtools-space-4);
9445
- }
9446
- :host([size='md']) button,
9447
- :host(:not([size])) button {
9448
- font-size: var(--lit-devtools-text-sm);
9449
- padding: 0 var(--lit-devtools-space-5);
9450
- }
9451
- `];
9452
- }
9453
- constructor() {
9454
- super();
9455
- this.items = [];
9456
- this.value = "";
9457
- this.size = "md";
9458
- }
9459
- _select(id) {
9460
- this.value = id;
9461
- this.dispatchEvent(new CustomEvent("change", {
9462
- detail: { value: id },
9463
- bubbles: true,
9464
- composed: true
9465
- }));
9466
- }
9467
- render() {
9468
- return b`
9469
- ${this.items.map((item) => b`
9470
- <button
9471
- role="tab"
9472
- aria-selected=${item.id === this.value}
9473
- class=${item.id === this.value ? "active" : ""}
9474
- @click=${() => this._select(item.id)}
9475
- >
9476
- ${item.icon ? b`<svg
9477
- viewBox="0 0 256 256"
9478
- fill="currentColor"
9479
- aria-hidden="true"
9480
- >
9481
- <path d=${item.icon} />
9482
- </svg>` : A}
9483
- ${item.label}
9484
- ${item.badge ? b`<span class="badge">${item.badge}</span>` : A}
9485
- </button>
9486
- `)}
9487
- `;
9488
- }
9489
- };
9490
- __decorate([n$3({ type: Array })], SegmentedTabs.prototype, "items", void 0);
9491
- __decorate([n$3()], SegmentedTabs.prototype, "value", void 0);
9492
- __decorate([n$3()], SegmentedTabs.prototype, "size", void 0);
9493
- SegmentedTabs = __decorate([t$2("segmented-tabs")], SegmentedTabs);
9494
- //#endregion
9495
11436
  //#region src/lib/icons.ts
9496
11437
  /**
9497
11438
  * @license
@@ -9636,6 +11577,15 @@ var LitDevtoolsPanel = class LitDevtoolsPanel extends i$2 {
9636
11577
  firstUpdated() {
9637
11578
  onDeepLink((link) => {
9638
11579
  if (link.tab !== void 0) this._tab = link.tab;
11580
+ if (link.componentId === void 0 && link.eventId !== void 0) {
11581
+ this._tab = "timeline";
11582
+ const eventId = link.eventId;
11583
+ this.updateComplete.then(() => {
11584
+ this._timelineView?.selectEvent(eventId);
11585
+ this._syncHash();
11586
+ });
11587
+ return;
11588
+ }
9639
11589
  if (link.componentId === void 0) {
9640
11590
  this._syncHash();
9641
11591
  return;
@@ -9658,9 +11608,11 @@ var LitDevtoolsPanel = class LitDevtoolsPanel extends i$2 {
9658
11608
  _syncHash() {
9659
11609
  if (window.top !== window.self) return;
9660
11610
  const selectedId = this._tab === "updates" ? this._updatesView?.selectedId : this._componentsView?.selectedId;
11611
+ const eventId = this._tab === "timeline" ? this._timelineView?.selectedEventId : null;
9661
11612
  writeHashLink({
9662
11613
  tab: this._tab,
9663
- ...selectedId === null || selectedId === void 0 ? {} : { componentId: selectedId }
11614
+ ...selectedId === null || selectedId === void 0 ? {} : { componentId: selectedId },
11615
+ ...eventId === null || eventId === void 0 ? {} : { eventId }
9664
11616
  });
9665
11617
  }
9666
11618
  /**
@@ -9702,7 +11654,10 @@ var LitDevtoolsPanel = class LitDevtoolsPanel extends i$2 {
9702
11654
  ></segmented-tabs>
9703
11655
  </header>
9704
11656
  <div class="view" @inspect-element=${this._onInspectElement}>
9705
- <timeline-view ?hidden=${this._tab !== "timeline"}></timeline-view>
11657
+ <timeline-view
11658
+ ?hidden=${this._tab !== "timeline"}
11659
+ @selection-change=${this._syncHash}
11660
+ ></timeline-view>
9706
11661
  <!-- Kept mounted (like the timeline) so an overlay inspect-pick can
9707
11662
  arrive and switch us here even while another tab is in front. -->
9708
11663
  <components-view
@@ -9726,6 +11681,7 @@ __decorate([r$2()], LitDevtoolsPanel.prototype, "_tab", void 0);
9726
11681
  __decorate([r$2()], LitDevtoolsPanel.prototype, "_hmrCount", void 0);
9727
11682
  __decorate([e$3("components-view")], LitDevtoolsPanel.prototype, "_componentsView", void 0);
9728
11683
  __decorate([e$3("updates-view")], LitDevtoolsPanel.prototype, "_updatesView", void 0);
11684
+ __decorate([e$3("timeline-view")], LitDevtoolsPanel.prototype, "_timelineView", void 0);
9729
11685
  LitDevtoolsPanel = __decorate([t$2("lit-devtools-panel")], LitDevtoolsPanel);
9730
11686
  //#endregion
9731
11687
  //#region src/panel/main.ts
@@ -9737,4 +11693,4 @@ LitDevtoolsPanel = __decorate([t$2("lit-devtools-panel")], LitDevtoolsPanel);
9737
11693
  //#endregion
9738
11694
  export { onBrowserAgentToolsChanged as n, nanoid as r, listBrowserAgentTools as t };
9739
11695
 
9740
- //# sourceMappingURL=index-CFrVgNfS.js.map
11696
+ //# sourceMappingURL=index-Gn4Hk_9s.js.map