@oddsquad/vite-plugin-lit 0.4.0 → 0.6.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.
- package/CHANGELOG.md +85 -0
- package/README.md +16 -5
- package/bin.mjs +16 -12
- package/dist/client/assets/{browser-agent-rpc-BXhoSh1z-CdtPhBL-.js → browser-agent-rpc-BXhoSh1z-DXAVFObe.js} +2 -2
- package/dist/client/assets/{browser-agent-rpc-BXhoSh1z-CdtPhBL-.js.map → browser-agent-rpc-BXhoSh1z-DXAVFObe.js.map} +1 -1
- package/dist/client/assets/{index-DVe6M4HV.js → index-DVr5EnyB.js} +1859 -751
- package/dist/client/assets/index-DVr5EnyB.js.map +1 -0
- package/dist/client/index.html +11 -1
- package/lib/devframe/definition.d.ts +7 -0
- package/lib/devframe/definition.d.ts.map +1 -1
- package/lib/devframe/definition.js +56 -11
- package/lib/devframe/definition.js.map +1 -1
- package/lib/devframe/launch-editor.d.ts +20 -0
- package/lib/devframe/launch-editor.d.ts.map +1 -0
- package/lib/devframe/launch-editor.js +40 -0
- package/lib/devframe/launch-editor.js.map +1 -0
- package/lib/devframe/protocol.d.ts +31 -4
- package/lib/devframe/protocol.d.ts.map +1 -1
- package/lib/devframe/protocol.js +21 -3
- package/lib/devframe/protocol.js.map +1 -1
- package/lib/devframe/rpc-source.d.ts +44 -0
- package/lib/devframe/rpc-source.d.ts.map +1 -0
- package/lib/devframe/rpc-source.js +150 -0
- package/lib/devframe/rpc-source.js.map +1 -0
- package/lib/devframe/source.d.ts +12 -0
- package/lib/devframe/source.d.ts.map +1 -1
- package/lib/devframe/source.js +12 -0
- package/lib/devframe/source.js.map +1 -1
- package/lib/devframe/vite.d.ts +2 -0
- package/lib/devframe/vite.d.ts.map +1 -1
- package/lib/devframe/vite.js +3 -9
- package/lib/devframe/vite.js.map +1 -1
- package/lib/options.d.ts +6 -1
- package/lib/options.d.ts.map +1 -1
- package/lib/options.js +65 -18
- package/lib/options.js.map +1 -1
- package/lib/override-baselines.d.ts +17 -0
- package/lib/override-baselines.d.ts.map +1 -0
- package/lib/override-baselines.js +25 -0
- package/lib/override-baselines.js.map +1 -0
- package/lib/plugin.d.ts.map +1 -1
- package/lib/plugin.js +8 -0
- package/lib/plugin.js.map +1 -1
- package/lib/plugins/open-in-editor.d.ts.map +1 -1
- package/lib/plugins/open-in-editor.js +6 -1
- package/lib/plugins/open-in-editor.js.map +1 -1
- package/lib/runtime/indicator.js.map +1 -1
- package/lib/runtime/inspector/install.js +4 -2
- package/lib/runtime/inspector/install.js.map +1 -1
- package/lib/runtime/inspector/serialize.d.ts.map +1 -1
- package/lib/runtime/inspector/serialize.js +20 -0
- package/lib/runtime/inspector/serialize.js.map +1 -1
- package/lib/runtime/overrides.d.ts +4 -10
- package/lib/runtime/overrides.d.ts.map +1 -1
- package/lib/runtime/overrides.js +5 -3
- package/lib/runtime/overrides.js.map +1 -1
- package/lib/runtime/page-channel.d.ts +52 -0
- package/lib/runtime/page-channel.d.ts.map +1 -0
- package/lib/runtime/page-channel.js +67 -0
- package/lib/runtime/page-channel.js.map +1 -0
- package/lib/runtime/patch.d.ts.map +1 -1
- package/lib/runtime/patch.js +2 -1
- package/lib/runtime/patch.js.map +1 -1
- package/lib/runtime/rpc-transport.d.ts +46 -0
- package/lib/runtime/rpc-transport.d.ts.map +1 -0
- package/lib/runtime/rpc-transport.js +82 -0
- package/lib/runtime/rpc-transport.js.map +1 -0
- package/lib/runtime/source-overlay/overlay-element.d.ts.map +1 -1
- package/lib/runtime/source-overlay/overlay-element.js +13 -3
- package/lib/runtime/source-overlay/overlay-element.js.map +1 -1
- package/lib/runtime/timeline/chrome-tracks.d.ts +22 -0
- package/lib/runtime/timeline/chrome-tracks.d.ts.map +1 -0
- package/lib/runtime/timeline/chrome-tracks.js +74 -0
- package/lib/runtime/timeline/chrome-tracks.js.map +1 -0
- package/lib/runtime/timeline/clock.d.ts +6 -0
- package/lib/runtime/timeline/clock.d.ts.map +1 -1
- package/lib/runtime/timeline/clock.js +6 -0
- package/lib/runtime/timeline/clock.js.map +1 -1
- package/lib/runtime/timeline/install.js +42 -13
- package/lib/runtime/timeline/install.js.map +1 -1
- package/lib/runtime/timeline/public-api.d.ts.map +1 -1
- package/lib/runtime/timeline/public-api.js +11 -3
- package/lib/runtime/timeline/public-api.js.map +1 -1
- package/lib/runtime/timeline/transport.d.ts +6 -9
- package/lib/runtime/timeline/transport.d.ts.map +1 -1
- package/lib/runtime/timeline/transport.js.map +1 -1
- package/lib/timeline/filter.d.ts +38 -0
- package/lib/timeline/filter.d.ts.map +1 -0
- package/lib/timeline/filter.js +44 -0
- package/lib/timeline/filter.js.map +1 -0
- package/lib/timeline/tracks.d.ts +113 -0
- package/lib/timeline/tracks.d.ts.map +1 -0
- package/lib/timeline/tracks.js +168 -0
- package/lib/timeline/tracks.js.map +1 -0
- package/package.json +6 -1
- package/types/timeline.d.ts +44 -0
- package/types/timeline.d.ts.map +1 -1
- package/types/timeline.js +2 -0
- package/types/timeline.js.map +1 -1
- package/dist/client/assets/index-DVe6M4HV.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/lib/runtime/timeline/install.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../../src/lib/runtime/timeline/install.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * Timeline runtime install entry point.\n *\n * Injected by the plugin into the host page via `transformIndexHtml` when\n * `timeline: true` is set. Sets up all capture layers and wires them to the\n * transport.\n *\n * Recording starts off; the panel iframe toggles it via the Vite HMR channel\n * (Phase 0/1) or devframe shared state (Phase 2+). Layers capture while the\n * panel is recording OR the \"chrome performance tracks\" preference is on; the\n * panel transport only receives events while recording, the Chrome tracks sink\n * only while the preference is on.\n */\n\nimport {emit, setHotClient} from './transport.js';\nimport {resetClock} from './clock.js';\nimport {createChromeTracksSink} from './chrome-tracks.js';\nimport {installLifecycleLayer, setUpdateHook} from './lifecycle.js';\nimport {installRenderLayer, setRenderDebugEnabled} from './render.js';\nimport {installMouseLayer, installKeyboardLayer} from './input.js';\nimport {flashUpdate, setFlashEnabled, setFlashRamp} from './flash.js';\nimport {subscribeOverride} from '../overrides.js';\nimport {pageChannel} from '../page-channel.js';\nimport type {ViteHotLike} from '../page-channel.js';\nimport type {\n TimelineEvent,\n TimelineLayersState,\n} from '../../../types/timeline.js';\n\n// ---------------------------------------------------------------------------\n// Recording state — written by the panel toggle, read by all capture layers.\n// ---------------------------------------------------------------------------\n\nconst state: TimelineLayersState = {\n recordingState: false,\n litLifecycleEnabled: true,\n litRenderEnabled: true,\n litRenderVerboseEnabled: false,\n mouseEventEnabled: false,\n keyboardEventEnabled: false,\n};\n\n// Mirror to Chrome DevTools' Performance panel; a page-side preference driven\n// by the settings override, independent of panel recording.\nlet chromeTracks = false;\nconst sink = createChromeTracksSink();\n\n// Layers run when either consumer wants events.\nconst capturing = (): boolean => state.recordingState || chromeTracks;\nconst out = (e: TimelineEvent): void => {\n if (state.recordingState) emit(e);\n if (chromeTracks) sink.push(e);\n};\nconst lifecycleEnabled = (): boolean => state.litLifecycleEnabled;\nconst renderEnabled = (): boolean => state.litRenderEnabled;\nconst renderVerboseEnabled = (): boolean => state.litRenderVerboseEnabled;\nconst mouseEnabled = (): boolean => state.mouseEventEnabled;\nconst keyboardEnabled = (): boolean => state.keyboardEventEnabled;\n\n// Drive Lit's debug event flag from capturing × (render-layer-enabled OR\n// verbose-layer-enabled) so lit-html only pays the per-render CustomEvent\n// dispatch cost while we're actually capturing one of the two render layers.\nconst syncRenderDebug = (): void => {\n setRenderDebugEnabled(\n capturing() && (state.litRenderEnabled || state.litRenderVerboseEnabled)\n );\n};\n\n// ---------------------------------------------------------------------------\n// Capture layer installation\n// ---------------------------------------------------------------------------\n\ninstallLifecycleLayer(out, capturing, lifecycleEnabled);\ninstallRenderLayer(out, capturing, renderEnabled, renderVerboseEnabled);\ninstallMouseLayer(out, capturing, mouseEnabled);\ninstallKeyboardLayer(out, capturing, keyboardEnabled);\n\n// Flash-on-update rides the lifecycle wrappers but not the recording gate:\n// it's a page-side visual the panel toggles as a preference, so it follows the\n// settings override (read at boot, pushed live) rather than the layer state.\nsetUpdateHook(flashUpdate);\n\n// ---------------------------------------------------------------------------\n// Page channel wiring\n// ---------------------------------------------------------------------------\n\n// `import.meta.hot` exists in Vite dev builds; the tsconfig lib set doesn't\n// include it, so we access it through the un-typed global shape.\nconst hot = (import.meta as {hot?: ViteHotLike}).hot;\n\nsubscribeOverride(hot, (o) => {\n setFlashEnabled(o.flashUpdates ?? false);\n setFlashRamp(o.flashUpdatesRamp ?? false);\n const next = o.chromeTracks ?? false;\n if (next !== chromeTracks) {\n chromeTracks = next;\n if (!next) sink.reset();\n syncRenderDebug();\n }\n});\n\nif (hot !== undefined) {\n pageChannel.useViteHot(hot);\n setHotClient(pageChannel);\n\n // Panel → app: toggle recording and per-layer flags.\n pageChannel.on('lit:timeline:recording-changed', (data) => {\n const next = (data as {recording: boolean}).recording;\n // Re-zero the timeline clock on the rising edge so event times read as\n // \"ms since recording started\" rather than since page load.\n if (next && !state.recordingState) resetClock();\n state.recordingState = next;\n syncRenderDebug();\n });\n\n pageChannel.on('lit:timeline:layers-changed', (data) => {\n Object.assign(state, data as Partial<TimelineLayersState>);\n syncRenderDebug();\n });\n\n // Announce readiness so the panel can detect the runtime.\n pageChannel.send('lit:timeline:runtime-ready', {});\n // A carrier attached later starts with no idea a runtime exists.\n pageChannel.onAttach(() =>\n pageChannel.send('lit:timeline:runtime-ready', {})\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../../../src/lib/runtime/timeline/public-api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../../../src/lib/runtime/timeline/public-api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,OAAO,KAAK,EAAC,aAAa,EAAE,aAAa,EAAC,MAAM,4BAA4B,CAAC;AAE7E,YAAY,EAAC,aAAa,EAAE,aAAa,EAAC,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,UAAW,aAAa,KAAG,IAEvD,CAAC;AAYF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,UAAW,aAAa,KAAG,IAMvD,CAAC"}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* This module shares the same `transport.ts` instance as `install.ts`, so
|
|
12
12
|
* events emitted here flow through the same batched HMR channel to the panel.
|
|
13
13
|
*/
|
|
14
|
+
import {pageChannel} from '../page-channel.js';
|
|
14
15
|
import {emit, setHotClientCallback} from './transport.js';
|
|
15
16
|
/**
|
|
16
17
|
* Emit a custom timeline event from app code.
|
|
@@ -26,6 +27,14 @@ import {emit, setHotClientCallback} from './transport.js';
|
|
|
26
27
|
export const addTimelineEvent = (event) => {
|
|
27
28
|
emit(event);
|
|
28
29
|
};
|
|
30
|
+
const announcedLayers = [];
|
|
31
|
+
// A carrier attached later (the dev server's own RPC link, say) never saw the
|
|
32
|
+
// layers registered before it, so they are announced to it again.
|
|
33
|
+
pageChannel.onAttach(() => {
|
|
34
|
+
for (const layer of announcedLayers) {
|
|
35
|
+
pageChannel.send('lit:timeline:custom-layer', {layer});
|
|
36
|
+
}
|
|
37
|
+
});
|
|
29
38
|
/**
|
|
30
39
|
* Register a custom timeline layer and announce it to the panel.
|
|
31
40
|
*
|
|
@@ -35,9 +44,8 @@ export const addTimelineEvent = (event) => {
|
|
|
35
44
|
*/
|
|
36
45
|
export const addTimelineLayer = (layer) => {
|
|
37
46
|
const send = () => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
});
|
|
47
|
+
announcedLayers.push(layer);
|
|
48
|
+
pageChannel.send('lit:timeline:custom-layer', {layer});
|
|
41
49
|
};
|
|
42
50
|
setHotClientCallback(send);
|
|
43
51
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../../src/lib/runtime/timeline/public-api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AAEH,OAAO,EAAC,IAAI,EAAE,oBAAoB,EAAC,MAAM,gBAAgB,CAAC;AAK1D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAoB,EAAQ,EAAE;IAC7D,IAAI,CAAC,KAAK,CAAC,CAAC;AACd,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"public-api.js","sourceRoot":"","sources":["../../../src/lib/runtime/timeline/public-api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AAEH,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAC,IAAI,EAAE,oBAAoB,EAAC,MAAM,gBAAgB,CAAC;AAK1D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAoB,EAAQ,EAAE;IAC7D,IAAI,CAAC,KAAK,CAAC,CAAC;AACd,CAAC,CAAC;AAEF,MAAM,eAAe,GAAoB,EAAE,CAAC;AAE5C,8EAA8E;AAC9E,kEAAkE;AAClE,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE;IACxB,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;QACpC,WAAW,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAC,KAAK,EAAC,CAAC,CAAC;IACzD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAoB,EAAQ,EAAE;IAC7D,MAAM,IAAI,GAAG,GAAS,EAAE;QACtB,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5B,WAAW,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAC,KAAK,EAAC,CAAC,CAAC;IACzD,CAAC,CAAC;IACF,oBAAoB,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * Public timeline API — re-exported by the `virtual:lit-plugin/timeline`\n * virtual module so app code and other plugins can contribute events and\n * custom layers without reaching into plugin internals.\n *\n * This module shares the same `transport.ts` instance as `install.ts`, so\n * events emitted here flow through the same batched HMR channel to the panel.\n */\n\nimport {pageChannel} from '../page-channel.js';\nimport {emit, setHotClientCallback} from './transport.js';\nimport type {TimelineEvent, TimelineLayer} from '../../../types/timeline.js';\n\nexport type {TimelineEvent, TimelineLayer};\n\n/**\n * Emit a custom timeline event from app code.\n *\n * The event is forwarded to the Timeline panel when recording is active.\n * Use a custom `layerId` registered with `addTimelineLayer`, or one of the\n * built-in layer ids (`'lit-lifecycle'`, `'lit-render'`,\n * `'lit-render-verbose'`, `'mouse'`, `'keyboard'`).\n *\n * No-ops in production (the HMR channel is absent; events are dropped in the\n * transport queue).\n */\nexport const addTimelineEvent = (event: TimelineEvent): void => {\n emit(event);\n};\n\nconst announcedLayers: TimelineLayer[] = [];\n\n// A carrier attached later (the dev server's own RPC link, say) never saw the\n// layers registered before it, so they are announced to it again.\npageChannel.onAttach(() => {\n for (const layer of announcedLayers) {\n pageChannel.send('lit:timeline:custom-layer', {layer});\n }\n});\n\n/**\n * Register a custom timeline layer and announce it to the panel.\n *\n * The panel adds the layer to its toggle strip after the built-in layers.\n * The registration message is sent once on first call; duplicate ids are\n * ignored.\n */\nexport const addTimelineLayer = (layer: TimelineLayer): void => {\n const send = (): void => {\n announcedLayers.push(layer);\n pageChannel.send('lit:timeline:custom-layer', {layer});\n };\n setHotClientCallback(send);\n};\n"]}
|
|
@@ -6,21 +6,18 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* Buffered transport for timeline events.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Phase 2 migration: replace `sendViaHmr` with `sendViaRpc` when the
|
|
14
|
-
* devframe RPC client is available (injected by the panel's clientScript).
|
|
9
|
+
* Forwards events over the page channel (`../page-channel.ts`), which is
|
|
10
|
+
* Vite's HMR WebSocket by default and can be pointed at a standalone
|
|
11
|
+
* devframe server instead. Which carrier is in use is not this module's
|
|
12
|
+
* concern.
|
|
15
13
|
*
|
|
16
14
|
* Batching: we coalesce events with `queueMicrotask` so a burst of
|
|
17
15
|
* commit/set-part events from a single render doesn't hammer the channel
|
|
18
16
|
* with one message per event.
|
|
19
17
|
*/
|
|
20
18
|
import type { TimelineEvent } from '../../../types/timeline.js';
|
|
21
|
-
type
|
|
22
|
-
|
|
23
|
-
};
|
|
19
|
+
import type { PageTransport } from '../page-channel.js';
|
|
20
|
+
type HotClient = Pick<PageTransport, 'send'>;
|
|
24
21
|
/**
|
|
25
22
|
* Register the HMR hot client.
|
|
26
23
|
* Called by install.ts after import.meta.hot check.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../src/lib/runtime/timeline/transport.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../../src/lib/runtime/timeline/transport.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,4BAA4B,CAAC;AAC9D,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAC;AAEtD,KAAK,SAAS,GAAG,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;AAmB7C;;;;GAIG;AACH,eAAO,MAAM,YAAY,QAAS,SAAS,KAAG,IAe7C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,OAAQ,MAAM,IAAI,KAAG,IAMrD,CAAC;AAaF;;;;GAIG;AACH,eAAO,MAAM,IAAI,UAAW,aAAa,KAAG,IAc3C,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../src/lib/runtime/timeline/transport.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../../src/lib/runtime/timeline/transport.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAoBH,MAAM,KAAK,GAAoB,EAAE,CAAC;AAClC,IAAI,cAAc,GAAG,KAAK,CAAC;AAC3B,IAAI,SAAS,GAAqB,IAAI,CAAC;AAEvC;;;;;;GAMG;AACH,MAAM,WAAW,GAAG,IAAI,CAAC;AAEzB,kFAAkF;AAClF,oEAAoE;AACpE,MAAM,gBAAgB,GAAsB,EAAE,CAAC;AAE/C;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,GAAc,EAAQ,EAAE;IACnD,SAAS,GAAG,GAAG,CAAC;IAChB,6DAA6D;IAC7D,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;QACxC,cAAc,GAAG,IAAI,CAAC;QACtB,cAAc,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;IACD,iEAAiE;IACjE,KAAK,MAAM,EAAE,IAAI,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,IAAI,CAAC;YACH,EAAE,EAAE,CAAC;QACP,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;AACH,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,EAAc,EAAQ,EAAE;IAC3D,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,cAAc,CAAC,EAAE,CAAC,CAAC;IACrB,CAAC;SAAM,CAAC;QACN,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,KAAK,GAAG,GAAS,EAAE;IACvB,cAAc,GAAG,KAAK,CAAC;IACvB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO;IACrD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC;QACH,SAAS,CAAC,IAAI,CAAC,yBAAyB,EAAE,EAAC,MAAM,EAAE,KAAK,EAAC,CAAC,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,kEAAkE;IACpE,CAAC;AACH,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,KAAoB,EAAQ,EAAE;IACjD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,uEAAuE;QACvE,wEAAwE;QACxE,IAAI,KAAK,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC;YAC/B,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC;QAC9C,CAAC;QACD,OAAO;IACT,CAAC;IACD,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,cAAc,GAAG,IAAI,CAAC;QACtB,cAAc,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;AACH,CAAC,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * Buffered transport for timeline events.\n *\n * Forwards events over the page channel (`../page-channel.ts`), which is\n * Vite's HMR WebSocket by default and can be pointed at a standalone\n * devframe server instead. Which carrier is in use is not this module's\n * concern.\n *\n * Batching: we coalesce events with `queueMicrotask` so a burst of\n * commit/set-part events from a single render doesn't hammer the channel\n * with one message per event.\n */\n\nimport type {TimelineEvent} from '../../../types/timeline.js';\nimport type {PageTransport} from '../page-channel.js';\n\ntype HotClient = Pick<PageTransport, 'send'>;\n\nconst queue: TimelineEvent[] = [];\nlet flushScheduled = false;\nlet hotClient: HotClient | null = null;\n\n/**\n * Cap on events retained while no hot client is connected. Without a client\n * `flush()` cannot drain the queue, so an uncapped buffer would grow for the\n * whole session (in production, or before `install.ts` wires the client).\n * We keep only the most recent events; the retained tail flushes if a client\n * later connects.\n */\nconst MAX_PENDING = 1000;\n\n// Callbacks registered before the hot client was available (e.g. addTimelineLayer\n// called at module init time before install.ts has set the client).\nconst pendingCallbacks: Array<() => void> = [];\n\n/**\n * Register the HMR hot client.\n * Called by install.ts after import.meta.hot check.\n * Flushes any queued events and fires pending one-shot callbacks.\n */\nexport const setHotClient = (hot: HotClient): void => {\n hotClient = hot;\n // Drain any events that arrived before the client was wired.\n if (queue.length > 0 && !flushScheduled) {\n flushScheduled = true;\n queueMicrotask(flush);\n }\n // Fire one-shot callbacks (e.g. addTimelineLayer announcements).\n for (const cb of pendingCallbacks.splice(0)) {\n try {\n cb();\n } catch {\n // ignore\n }\n }\n};\n\n/**\n * Register a callback to be invoked once the HMR client is available.\n * If the client is already set the callback is fired on the next microtask.\n */\nexport const setHotClientCallback = (cb: () => void): void => {\n if (hotClient !== null) {\n queueMicrotask(cb);\n } else {\n pendingCallbacks.push(cb);\n }\n};\n\nconst flush = (): void => {\n flushScheduled = false;\n if (queue.length === 0 || hotClient === null) return;\n const batch = queue.splice(0);\n try {\n hotClient.send('lit:timeline:push-event', {events: batch});\n } catch {\n // HMR channel may be temporarily unavailable; events are dropped.\n }\n};\n\n/**\n * Enqueue a timeline event for forwarding to the Vite server.\n * Batches via microtask so a single synchronous render burst\n * doesn't send dozens of individual messages.\n */\nexport const emit = (event: TimelineEvent): void => {\n queue.push(event);\n if (hotClient === null) {\n // No consumer yet: bound the buffer instead of scheduling a flush that\n // would only no-op. `setHotClient` drains the retained tail on connect.\n if (queue.length > MAX_PENDING) {\n queue.splice(0, queue.length - MAX_PENDING);\n }\n return;\n }\n if (!flushScheduled) {\n flushScheduled = true;\n queueMicrotask(flush);\n }\n};\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* The Timeline's element and regex filters, as pure functions.
|
|
8
|
+
*
|
|
9
|
+
* Both presentations (the list and the tracks) answer "show me only this
|
|
10
|
+
* element" or "only things matching /update/". The filter state therefore
|
|
11
|
+
* lives in `timeline-view` and each presentation applies it here, so the two
|
|
12
|
+
* cannot drift apart on what a match means.
|
|
13
|
+
*/
|
|
14
|
+
import type { TimelineEvent } from '../../types/timeline.js';
|
|
15
|
+
import type { TimelineSpan } from './derive.js';
|
|
16
|
+
/**
|
|
17
|
+
* Compiles the regex box. An empty box matches everything. A pattern that
|
|
18
|
+
* does not compile (the user is mid-typing `foo(`) disables the filter rather
|
|
19
|
+
* than hiding every row, and is reported as `invalid` so the input can say so.
|
|
20
|
+
*/
|
|
21
|
+
export declare const compileRegex: (source: string) => {
|
|
22
|
+
re: RegExp | null;
|
|
23
|
+
invalid: boolean;
|
|
24
|
+
};
|
|
25
|
+
/** Text the regex filter searches: element tag, name, subtitle and the
|
|
26
|
+
* changed property keys (the reason is worth searching for by name). */
|
|
27
|
+
export declare const spanHaystack: (span: TimelineSpan) => string;
|
|
28
|
+
/**
|
|
29
|
+
* Applies the element and regex filters. `re` comes from {@link compileRegex}
|
|
30
|
+
* so a caller filtering on every keystroke compiles once, not per span.
|
|
31
|
+
*/
|
|
32
|
+
export declare const filterSpans: (spans: readonly TimelineSpan[], elementId: number | null, re: RegExp | null) => TimelineSpan[];
|
|
33
|
+
/** Distinct elements (by stable id) seen across the recorded events. */
|
|
34
|
+
export declare const listElements: (events: readonly TimelineEvent[]) => Array<{
|
|
35
|
+
id: number;
|
|
36
|
+
tag: string;
|
|
37
|
+
}>;
|
|
38
|
+
//# sourceMappingURL=filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/lib/timeline/filter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,YAAY,WACf,MAAM,KACb;IAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAOtC,CAAC;AAEF;yEACyE;AACzE,eAAO,MAAM,YAAY,SAAU,YAAY,KAAG,MAGnC,CAAC;AAEhB;;;GAGG;AACH,eAAO,MAAM,WAAW,UACf,SAAS,YAAY,EAAE,aACnB,MAAM,GAAG,IAAI,MACpB,MAAM,GAAG,IAAI,KAChB,YAAY,EAKZ,CAAC;AAEJ,wEAAwE;AACxE,eAAO,MAAM,YAAY,WACf,SAAS,aAAa,EAAE,KAC/B,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,CASjC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Compiles the regex box. An empty box matches everything. A pattern that
|
|
8
|
+
* does not compile (the user is mid-typing `foo(`) disables the filter rather
|
|
9
|
+
* than hiding every row, and is reported as `invalid` so the input can say so.
|
|
10
|
+
*/
|
|
11
|
+
export const compileRegex = (source) => {
|
|
12
|
+
if (source === '') return {re: null, invalid: false};
|
|
13
|
+
try {
|
|
14
|
+
return {re: new RegExp(source, 'i'), invalid: false};
|
|
15
|
+
} catch {
|
|
16
|
+
return {re: null, invalid: true};
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
/** Text the regex filter searches: element tag, name, subtitle and the
|
|
20
|
+
* changed property keys (the reason is worth searching for by name). */
|
|
21
|
+
export const spanHaystack = (span) =>
|
|
22
|
+
`${span.meta?.tagName ?? ''} ${span.name} ${span.subtitle ?? ''} ${(span.changed ?? []).join(' ')}`;
|
|
23
|
+
/**
|
|
24
|
+
* Applies the element and regex filters. `re` comes from {@link compileRegex}
|
|
25
|
+
* so a caller filtering on every keystroke compiles once, not per span.
|
|
26
|
+
*/
|
|
27
|
+
export const filterSpans = (spans, elementId, re) =>
|
|
28
|
+
spans.filter(
|
|
29
|
+
(span) =>
|
|
30
|
+
(elementId === null || span.meta?.elementId === elementId) &&
|
|
31
|
+
(re === null || re.test(spanHaystack(span)))
|
|
32
|
+
);
|
|
33
|
+
/** Distinct elements (by stable id) seen across the recorded events. */
|
|
34
|
+
export const listElements = (events) => {
|
|
35
|
+
const seen = new Map();
|
|
36
|
+
for (const ev of events) {
|
|
37
|
+
const id = ev.meta?.elementId;
|
|
38
|
+
if (id != null && !seen.has(id)) {
|
|
39
|
+
seen.set(id, ev.meta?.tagName ?? 'unknown');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return [...seen].map(([id, tag]) => ({id, tag}));
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/lib/timeline/filter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,MAAc,EACyB,EAAE;IACzC,IAAI,MAAM,KAAK,EAAE;QAAE,OAAO,EAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAC,CAAC;IACrD,IAAI,CAAC;QACH,OAAO,EAAC,EAAE,EAAE,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,KAAK,EAAC,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAC,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAC,CAAC;IACnC,CAAC;AACH,CAAC,CAAC;AAEF;yEACyE;AACzE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,IAAkB,EAAU,EAAE,CACzD,GAAG,IAAI,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,IAAI,CACjE,IAAI,CAAC,OAAO,IAAI,EAAE,CACnB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AAEhB;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,KAA8B,EAC9B,SAAwB,EACxB,EAAiB,EACD,EAAE,CAClB,KAAK,CAAC,MAAM,CACV,CAAC,IAAI,EAAE,EAAE,CACP,CAAC,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,KAAK,SAAS,CAAC;IAC1D,CAAC,EAAE,KAAK,IAAI,IAAI,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAC/C,CAAC;AAEJ,wEAAwE;AACxE,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,MAAgC,EACE,EAAE;IACpC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE,CAAC;QACxB,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC;QAC9B,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,OAAO,IAAI,SAAS,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,EAAC,EAAE,EAAE,GAAG,EAAC,CAAC,CAAC,CAAC;AACnD,CAAC,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * The Timeline's element and regex filters, as pure functions.\n *\n * Both presentations (the list and the tracks) answer \"show me only this\n * element\" or \"only things matching /update/\". The filter state therefore\n * lives in `timeline-view` and each presentation applies it here, so the two\n * cannot drift apart on what a match means.\n */\n\nimport type {TimelineEvent} from '../../types/timeline.js';\nimport type {TimelineSpan} from './derive.js';\n\n/**\n * Compiles the regex box. An empty box matches everything. A pattern that\n * does not compile (the user is mid-typing `foo(`) disables the filter rather\n * than hiding every row, and is reported as `invalid` so the input can say so.\n */\nexport const compileRegex = (\n source: string\n): {re: RegExp | null; invalid: boolean} => {\n if (source === '') return {re: null, invalid: false};\n try {\n return {re: new RegExp(source, 'i'), invalid: false};\n } catch {\n return {re: null, invalid: true};\n }\n};\n\n/** Text the regex filter searches: element tag, name, subtitle and the\n * changed property keys (the reason is worth searching for by name). */\nexport const spanHaystack = (span: TimelineSpan): string =>\n `${span.meta?.tagName ?? ''} ${span.name} ${span.subtitle ?? ''} ${(\n span.changed ?? []\n ).join(' ')}`;\n\n/**\n * Applies the element and regex filters. `re` comes from {@link compileRegex}\n * so a caller filtering on every keystroke compiles once, not per span.\n */\nexport const filterSpans = (\n spans: readonly TimelineSpan[],\n elementId: number | null,\n re: RegExp | null\n): TimelineSpan[] =>\n spans.filter(\n (span) =>\n (elementId === null || span.meta?.elementId === elementId) &&\n (re === null || re.test(spanHaystack(span)))\n );\n\n/** Distinct elements (by stable id) seen across the recorded events. */\nexport const listElements = (\n events: readonly TimelineEvent[]\n): Array<{id: number; tag: string}> => {\n const seen = new Map<number, string>();\n for (const ev of events) {\n const id = ev.meta?.elementId;\n if (id != null && !seen.has(id)) {\n seen.set(id, ev.meta?.tagName ?? 'unknown');\n }\n }\n return [...seen].map(([id, tag]) => ({id, tag}));\n};\n"]}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Layout for the Timeline's track view: one horizontal lane per layer on a
|
|
8
|
+
* shared time axis.
|
|
9
|
+
*
|
|
10
|
+
* The list answers "what happened, in what order, to whom". It cannot show
|
|
11
|
+
* concurrency or rhythm — which layers fire together, how a click lines up
|
|
12
|
+
* with the update tick it caused, how long a tick is next to its neighbours.
|
|
13
|
+
* This module turns the same {@link TimelineSpan}s into lanes and rows, and
|
|
14
|
+
* owns the arithmetic between milliseconds and pixels, so the element that
|
|
15
|
+
* draws them (`panel/timeline-tracks.ts`) is only DOM.
|
|
16
|
+
*
|
|
17
|
+
* Pure and dependency-free, like `derive.ts`, so it is unit-tested without a
|
|
18
|
+
* browser.
|
|
19
|
+
*/
|
|
20
|
+
import type { TimelineSpan } from './derive.js';
|
|
21
|
+
/**
|
|
22
|
+
* Spans in one row of a lane, sorted by start and never overlapping, so
|
|
23
|
+
* {@link visibleRange} can binary-search them.
|
|
24
|
+
*/
|
|
25
|
+
export type TrackRow = TimelineSpan[];
|
|
26
|
+
/** One lane: every span of one layer, packed into non-overlapping rows. */
|
|
27
|
+
export interface Track {
|
|
28
|
+
layerId: string;
|
|
29
|
+
rows: TrackRow[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Where a span stops occupying its row. A point event occupies only its own
|
|
33
|
+
* instant; a span whose end has not arrived (still running, or its end fell
|
|
34
|
+
* out of the buffer) occupies the rest of the recording, because that is
|
|
35
|
+
* where it is drawn — up to the live edge.
|
|
36
|
+
*/
|
|
37
|
+
export declare const occupiedEnd: (span: TimelineSpan) => number;
|
|
38
|
+
/**
|
|
39
|
+
* Groups spans into one track per layer, in `layers` order, then any layer
|
|
40
|
+
* the spans mention that `layers` does not (a custom layer announced after the
|
|
41
|
+
* last `get-meta`), in first-seen order. A layer with no spans still gets an
|
|
42
|
+
* empty track, so a lane does not appear and vanish as events arrive.
|
|
43
|
+
*
|
|
44
|
+
* Within a track, spans are packed greedily: each goes into the first row
|
|
45
|
+
* whose last span has ended by the time it starts. Lit's lifecycle phases
|
|
46
|
+
* nest, so one update tick lands as `performUpdate` on row 0 and
|
|
47
|
+
* `willUpdate` / `update` / `updated` side by side on row 1 — the flame shape
|
|
48
|
+
* falls out of the packing without modelling it.
|
|
49
|
+
*/
|
|
50
|
+
export declare const buildTracks: (spans: readonly TimelineSpan[], layers: readonly {
|
|
51
|
+
id: string;
|
|
52
|
+
}[]) => Track[];
|
|
53
|
+
/**
|
|
54
|
+
* The recording's time bounds over `spans`: the earliest start, and the
|
|
55
|
+
* latest instant anything is known to have happened (an end, or the start of
|
|
56
|
+
* a span with none). The origin is the earliest start rather than zero
|
|
57
|
+
* because the panel's buffer is capped and rolls its oldest events off; a
|
|
58
|
+
* long recording would otherwise open on an empty stretch.
|
|
59
|
+
*/
|
|
60
|
+
export declare const timeBounds: (spans: readonly TimelineSpan[]) => {
|
|
61
|
+
origin: number;
|
|
62
|
+
extent: number;
|
|
63
|
+
};
|
|
64
|
+
/** Deepest zoom, in multiples of "fit": about a microsecond per pixel on a
|
|
65
|
+
* second-long recording, past which there is nothing left to separate. */
|
|
66
|
+
export declare const MAX_ZOOM = 100000;
|
|
67
|
+
/** Mapping between recording time and pixels for the current zoom and pan. */
|
|
68
|
+
export interface TimeScale {
|
|
69
|
+
pxPerMs: number;
|
|
70
|
+
/** First and last millisecond inside the plot. */
|
|
71
|
+
start: number;
|
|
72
|
+
end: number;
|
|
73
|
+
toX(ms: number): number;
|
|
74
|
+
toMs(x: number): number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Builds the scale for a plot `width` pixels wide showing a recording that
|
|
78
|
+
* begins at `originMs` and runs for `extentMs`.
|
|
79
|
+
*
|
|
80
|
+
* `zoom` is a multiple of "fit" (1 shows the whole recording); `pan` is how
|
|
81
|
+
* many milliseconds past the origin the left edge sits. Both are clamped, so
|
|
82
|
+
* callers can apply a raw wheel or drag delta and read back where it landed.
|
|
83
|
+
*/
|
|
84
|
+
export declare const timeScale: (originMs: number, extentMs: number, width: number, zoom?: number, pan?: number) => TimeScale;
|
|
85
|
+
export declare const clampZoom: (zoom: number) => number;
|
|
86
|
+
/** Keeps the visible window inside the recording. */
|
|
87
|
+
export declare const clampPan: (pan: number, extentMs: number, zoom: number) => number;
|
|
88
|
+
/** The pan that puts the live edge (the end of the recording) at the right
|
|
89
|
+
* of the plot, for following a recording in progress. */
|
|
90
|
+
export declare const livePan: (extentMs: number, zoom: number) => number;
|
|
91
|
+
/**
|
|
92
|
+
* Zooms by `factor` while keeping the millisecond under pixel `x` where it
|
|
93
|
+
* is, which is what a wheel over a timeline is expected to do.
|
|
94
|
+
*/
|
|
95
|
+
export declare const zoomAt: (originMs: number, extentMs: number, width: number, zoom: number, pan: number, x: number, factor: number) => {
|
|
96
|
+
zoom: number;
|
|
97
|
+
pan: number;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Index range `[from, to)` of the spans in `row` that intersect the window
|
|
101
|
+
* `[start, end]`, found by binary search so a zoomed-in lane renders a
|
|
102
|
+
* handful of marks rather than walking thousands.
|
|
103
|
+
*
|
|
104
|
+
* Only the span just before the first one starting inside the window can
|
|
105
|
+
* reach into it, because a row's spans never overlap.
|
|
106
|
+
*/
|
|
107
|
+
export declare const visibleRange: (row: TrackRow, start: number, end: number) => [number, number];
|
|
108
|
+
/**
|
|
109
|
+
* A round tick interval (1, 2 or 5 times a power of ten, in milliseconds) at
|
|
110
|
+
* least `minMs` long, for axis labels that land on readable numbers.
|
|
111
|
+
*/
|
|
112
|
+
export declare const niceStep: (minMs: number) => number;
|
|
113
|
+
//# sourceMappingURL=tracks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracks.d.ts","sourceRoot":"","sources":["../../src/lib/timeline/tracks.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAE9C;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,YAAY,EAAE,CAAC;AAEtC,2EAA2E;AAC3E,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,QAAQ,EAAE,CAAC;CAClB;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,SAAU,YAAY,KAAG,MAGhD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,UACf,SAAS,YAAY,EAAE,UACtB,SAAS;IAAC,EAAE,EAAE,MAAM,CAAA;CAAC,EAAE,KAC9B,KAAK,EAeP,CAAC;AAwBF;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,UACd,SAAS,YAAY,EAAE,KAC7B;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CASjC,CAAC;AAEF;2EAC2E;AAC3E,eAAO,MAAM,QAAQ,SAAU,CAAC;AAKhC,8EAA8E;AAC9E,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,aACV,MAAM,YACN,MAAM,SACT,MAAM,kCAGZ,SAcF,CAAC;AAEF,eAAO,MAAM,SAAS,SAAU,MAAM,KAAG,MACuB,CAAC;AAEjE,qDAAqD;AACrD,eAAO,MAAM,QAAQ,QACd,MAAM,YACD,MAAM,QACV,MAAM,KACX,MAIF,CAAC;AAEF;0DAC0D;AAC1D,eAAO,MAAM,OAAO,aAAc,MAAM,QAAQ,MAAM,KAAG,MACrB,CAAC;AAErC;;;GAGG;AACH,eAAO,MAAM,MAAM,aACP,MAAM,YACN,MAAM,SACT,MAAM,QACP,MAAM,OACP,MAAM,KACR,MAAM,UACD,MAAM,KACb;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAO5B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,QAClB,QAAQ,SACN,MAAM,OACR,MAAM,KACV,CAAC,MAAM,EAAE,MAAM,CAgBjB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,UAAW,MAAM,KAAG,MAOxC,CAAC"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: BSD-3-Clause
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Where a span stops occupying its row. A point event occupies only its own
|
|
8
|
+
* instant; a span whose end has not arrived (still running, or its end fell
|
|
9
|
+
* out of the buffer) occupies the rest of the recording, because that is
|
|
10
|
+
* where it is drawn — up to the live edge.
|
|
11
|
+
*/
|
|
12
|
+
export const occupiedEnd = (span) => {
|
|
13
|
+
if (span.end !== undefined) return span.end;
|
|
14
|
+
return span.groupId === undefined ? span.start : Infinity;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Groups spans into one track per layer, in `layers` order, then any layer
|
|
18
|
+
* the spans mention that `layers` does not (a custom layer announced after the
|
|
19
|
+
* last `get-meta`), in first-seen order. A layer with no spans still gets an
|
|
20
|
+
* empty track, so a lane does not appear and vanish as events arrive.
|
|
21
|
+
*
|
|
22
|
+
* Within a track, spans are packed greedily: each goes into the first row
|
|
23
|
+
* whose last span has ended by the time it starts. Lit's lifecycle phases
|
|
24
|
+
* nest, so one update tick lands as `performUpdate` on row 0 and
|
|
25
|
+
* `willUpdate` / `update` / `updated` side by side on row 1 — the flame shape
|
|
26
|
+
* falls out of the packing without modelling it.
|
|
27
|
+
*/
|
|
28
|
+
export const buildTracks = (spans, layers) => {
|
|
29
|
+
const byLayer = new Map();
|
|
30
|
+
for (const layer of layers) byLayer.set(layer.id, []);
|
|
31
|
+
for (const span of spans) {
|
|
32
|
+
let list = byLayer.get(span.layerId);
|
|
33
|
+
if (list === undefined) {
|
|
34
|
+
list = [];
|
|
35
|
+
byLayer.set(span.layerId, list);
|
|
36
|
+
}
|
|
37
|
+
list.push(span);
|
|
38
|
+
}
|
|
39
|
+
return [...byLayer].map(([layerId, list]) => ({
|
|
40
|
+
layerId,
|
|
41
|
+
rows: packRows(list),
|
|
42
|
+
}));
|
|
43
|
+
};
|
|
44
|
+
const packRows = (spans) => {
|
|
45
|
+
// `toSpans` already sorts by start; re-sort only if a caller did not.
|
|
46
|
+
const sorted = spans.every((s, i) => i === 0 || spans[i - 1].start <= s.start)
|
|
47
|
+
? spans
|
|
48
|
+
: [...spans].sort((a, b) => a.start - b.start);
|
|
49
|
+
const rows = [];
|
|
50
|
+
const rowEnds = [];
|
|
51
|
+
for (const span of sorted) {
|
|
52
|
+
let row = rowEnds.findIndex((end) => end <= span.start);
|
|
53
|
+
if (row === -1) {
|
|
54
|
+
row = rows.length;
|
|
55
|
+
rows.push([]);
|
|
56
|
+
rowEnds.push(-Infinity);
|
|
57
|
+
}
|
|
58
|
+
rows[row].push(span);
|
|
59
|
+
rowEnds[row] = occupiedEnd(span);
|
|
60
|
+
}
|
|
61
|
+
return rows;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* The recording's time bounds over `spans`: the earliest start, and the
|
|
65
|
+
* latest instant anything is known to have happened (an end, or the start of
|
|
66
|
+
* a span with none). The origin is the earliest start rather than zero
|
|
67
|
+
* because the panel's buffer is capped and rolls its oldest events off; a
|
|
68
|
+
* long recording would otherwise open on an empty stretch.
|
|
69
|
+
*/
|
|
70
|
+
export const timeBounds = (spans) => {
|
|
71
|
+
if (spans.length === 0) return {origin: 0, extent: 0};
|
|
72
|
+
let origin = Infinity;
|
|
73
|
+
let last = -Infinity;
|
|
74
|
+
for (const span of spans) {
|
|
75
|
+
origin = Math.min(origin, span.start);
|
|
76
|
+
last = Math.max(last, span.end ?? span.start);
|
|
77
|
+
}
|
|
78
|
+
return {origin, extent: last - origin};
|
|
79
|
+
};
|
|
80
|
+
/** Deepest zoom, in multiples of "fit": about a microsecond per pixel on a
|
|
81
|
+
* second-long recording, past which there is nothing left to separate. */
|
|
82
|
+
export const MAX_ZOOM = 100_000;
|
|
83
|
+
/** A recording with a single instant in it still needs a nonzero axis. */
|
|
84
|
+
const MIN_EXTENT_MS = 1;
|
|
85
|
+
/**
|
|
86
|
+
* Builds the scale for a plot `width` pixels wide showing a recording that
|
|
87
|
+
* begins at `originMs` and runs for `extentMs`.
|
|
88
|
+
*
|
|
89
|
+
* `zoom` is a multiple of "fit" (1 shows the whole recording); `pan` is how
|
|
90
|
+
* many milliseconds past the origin the left edge sits. Both are clamped, so
|
|
91
|
+
* callers can apply a raw wheel or drag delta and read back where it landed.
|
|
92
|
+
*/
|
|
93
|
+
export const timeScale = (originMs, extentMs, width, zoom = 1, pan = 0) => {
|
|
94
|
+
const extent = Math.max(extentMs, MIN_EXTENT_MS);
|
|
95
|
+
const z = clampZoom(zoom);
|
|
96
|
+
const visibleMs = extent / z;
|
|
97
|
+
const p = clampPan(pan, extent, z);
|
|
98
|
+
const pxPerMs = Math.max(width, 1) / visibleMs;
|
|
99
|
+
const start = originMs + p;
|
|
100
|
+
return {
|
|
101
|
+
pxPerMs,
|
|
102
|
+
start,
|
|
103
|
+
end: start + visibleMs,
|
|
104
|
+
toX: (ms) => (ms - start) * pxPerMs,
|
|
105
|
+
toMs: (x) => start + x / pxPerMs,
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
export const clampZoom = (zoom) =>
|
|
109
|
+
Math.min(Math.max(Number.isNaN(zoom) ? 1 : zoom, 1), MAX_ZOOM);
|
|
110
|
+
/** Keeps the visible window inside the recording. */
|
|
111
|
+
export const clampPan = (pan, extentMs, zoom) => {
|
|
112
|
+
const extent = Math.max(extentMs, MIN_EXTENT_MS);
|
|
113
|
+
const max = extent - extent / clampZoom(zoom);
|
|
114
|
+
return Math.min(Math.max(Number.isNaN(pan) ? 0 : pan, 0), max);
|
|
115
|
+
};
|
|
116
|
+
/** The pan that puts the live edge (the end of the recording) at the right
|
|
117
|
+
* of the plot, for following a recording in progress. */
|
|
118
|
+
export const livePan = (extentMs, zoom) => clampPan(Infinity, extentMs, zoom);
|
|
119
|
+
/**
|
|
120
|
+
* Zooms by `factor` while keeping the millisecond under pixel `x` where it
|
|
121
|
+
* is, which is what a wheel over a timeline is expected to do.
|
|
122
|
+
*/
|
|
123
|
+
export const zoomAt = (originMs, extentMs, width, zoom, pan, x, factor) => {
|
|
124
|
+
const before = timeScale(originMs, extentMs, width, zoom, pan);
|
|
125
|
+
const anchor = before.toMs(x);
|
|
126
|
+
const nextZoom = clampZoom(clampZoom(zoom) * factor);
|
|
127
|
+
const after = timeScale(originMs, extentMs, width, nextZoom, 0);
|
|
128
|
+
const nextPan = anchor - originMs - x / after.pxPerMs;
|
|
129
|
+
return {zoom: nextZoom, pan: clampPan(nextPan, extentMs, nextZoom)};
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Index range `[from, to)` of the spans in `row` that intersect the window
|
|
133
|
+
* `[start, end]`, found by binary search so a zoomed-in lane renders a
|
|
134
|
+
* handful of marks rather than walking thousands.
|
|
135
|
+
*
|
|
136
|
+
* Only the span just before the first one starting inside the window can
|
|
137
|
+
* reach into it, because a row's spans never overlap.
|
|
138
|
+
*/
|
|
139
|
+
export const visibleRange = (row, start, end) => {
|
|
140
|
+
const firstAtOrAfter = (t, inclusive) => {
|
|
141
|
+
let lo = 0;
|
|
142
|
+
let hi = row.length;
|
|
143
|
+
while (lo < hi) {
|
|
144
|
+
const mid = (lo + hi) >> 1;
|
|
145
|
+
const s = row[mid].start;
|
|
146
|
+
if (inclusive ? s < t : s <= t) lo = mid + 1;
|
|
147
|
+
else hi = mid;
|
|
148
|
+
}
|
|
149
|
+
return lo;
|
|
150
|
+
};
|
|
151
|
+
let from = firstAtOrAfter(start, true);
|
|
152
|
+
if (from > 0 && occupiedEnd(row[from - 1]) >= start) from--;
|
|
153
|
+
const to = firstAtOrAfter(end, false);
|
|
154
|
+
return [from, Math.max(from, to)];
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* A round tick interval (1, 2 or 5 times a power of ten, in milliseconds) at
|
|
158
|
+
* least `minMs` long, for axis labels that land on readable numbers.
|
|
159
|
+
*/
|
|
160
|
+
export const niceStep = (minMs) => {
|
|
161
|
+
if (!(minMs > 0) || !Number.isFinite(minMs)) return 1;
|
|
162
|
+
const pow = 10 ** Math.floor(Math.log10(minMs));
|
|
163
|
+
for (const m of [1, 2, 5, 10]) {
|
|
164
|
+
if (m * pow >= minMs) return m * pow;
|
|
165
|
+
}
|
|
166
|
+
return 10 * pow;
|
|
167
|
+
};
|
|
168
|
+
//# sourceMappingURL=tracks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracks.js","sourceRoot":"","sources":["../../src/lib/timeline/tracks.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: BSD-3-Clause\n */\n\n/**\n * Layout for the Timeline's track view: one horizontal lane per layer on a\n * shared time axis.\n *\n * The list answers \"what happened, in what order, to whom\". It cannot show\n * concurrency or rhythm — which layers fire together, how a click lines up\n * with the update tick it caused, how long a tick is next to its neighbours.\n * This module turns the same {@link TimelineSpan}s into lanes and rows, and\n * owns the arithmetic between milliseconds and pixels, so the element that\n * draws them (`panel/timeline-tracks.ts`) is only DOM.\n *\n * Pure and dependency-free, like `derive.ts`, so it is unit-tested without a\n * browser.\n */\n\nimport type {TimelineSpan} from './derive.js';\n\n/**\n * Spans in one row of a lane, sorted by start and never overlapping, so\n * {@link visibleRange} can binary-search them.\n */\nexport type TrackRow = TimelineSpan[];\n\n/** One lane: every span of one layer, packed into non-overlapping rows. */\nexport interface Track {\n layerId: string;\n rows: TrackRow[];\n}\n\n/**\n * Where a span stops occupying its row. A point event occupies only its own\n * instant; a span whose end has not arrived (still running, or its end fell\n * out of the buffer) occupies the rest of the recording, because that is\n * where it is drawn — up to the live edge.\n */\nexport const occupiedEnd = (span: TimelineSpan): number => {\n if (span.end !== undefined) return span.end;\n return span.groupId === undefined ? span.start : Infinity;\n};\n\n/**\n * Groups spans into one track per layer, in `layers` order, then any layer\n * the spans mention that `layers` does not (a custom layer announced after the\n * last `get-meta`), in first-seen order. A layer with no spans still gets an\n * empty track, so a lane does not appear and vanish as events arrive.\n *\n * Within a track, spans are packed greedily: each goes into the first row\n * whose last span has ended by the time it starts. Lit's lifecycle phases\n * nest, so one update tick lands as `performUpdate` on row 0 and\n * `willUpdate` / `update` / `updated` side by side on row 1 — the flame shape\n * falls out of the packing without modelling it.\n */\nexport const buildTracks = (\n spans: readonly TimelineSpan[],\n layers: readonly {id: string}[]\n): Track[] => {\n const byLayer = new Map<string, TimelineSpan[]>();\n for (const layer of layers) byLayer.set(layer.id, []);\n for (const span of spans) {\n let list = byLayer.get(span.layerId);\n if (list === undefined) {\n list = [];\n byLayer.set(span.layerId, list);\n }\n list.push(span);\n }\n return [...byLayer].map(([layerId, list]) => ({\n layerId,\n rows: packRows(list),\n }));\n};\n\nconst packRows = (spans: TimelineSpan[]): TrackRow[] => {\n // `toSpans` already sorts by start; re-sort only if a caller did not.\n const sorted = spans.every(\n (s, i) => i === 0 || spans[i - 1]!.start <= s.start\n )\n ? spans\n : [...spans].sort((a, b) => a.start - b.start);\n const rows: TrackRow[] = [];\n const rowEnds: number[] = [];\n for (const span of sorted) {\n let row = rowEnds.findIndex((end) => end <= span.start);\n if (row === -1) {\n row = rows.length;\n rows.push([]);\n rowEnds.push(-Infinity);\n }\n rows[row]!.push(span);\n rowEnds[row] = occupiedEnd(span);\n }\n return rows;\n};\n\n/**\n * The recording's time bounds over `spans`: the earliest start, and the\n * latest instant anything is known to have happened (an end, or the start of\n * a span with none). The origin is the earliest start rather than zero\n * because the panel's buffer is capped and rolls its oldest events off; a\n * long recording would otherwise open on an empty stretch.\n */\nexport const timeBounds = (\n spans: readonly TimelineSpan[]\n): {origin: number; extent: number} => {\n if (spans.length === 0) return {origin: 0, extent: 0};\n let origin = Infinity;\n let last = -Infinity;\n for (const span of spans) {\n origin = Math.min(origin, span.start);\n last = Math.max(last, span.end ?? span.start);\n }\n return {origin, extent: last - origin};\n};\n\n/** Deepest zoom, in multiples of \"fit\": about a microsecond per pixel on a\n * second-long recording, past which there is nothing left to separate. */\nexport const MAX_ZOOM = 100_000;\n\n/** A recording with a single instant in it still needs a nonzero axis. */\nconst MIN_EXTENT_MS = 1;\n\n/** Mapping between recording time and pixels for the current zoom and pan. */\nexport interface TimeScale {\n pxPerMs: number;\n /** First and last millisecond inside the plot. */\n start: number;\n end: number;\n toX(ms: number): number;\n toMs(x: number): number;\n}\n\n/**\n * Builds the scale for a plot `width` pixels wide showing a recording that\n * begins at `originMs` and runs for `extentMs`.\n *\n * `zoom` is a multiple of \"fit\" (1 shows the whole recording); `pan` is how\n * many milliseconds past the origin the left edge sits. Both are clamped, so\n * callers can apply a raw wheel or drag delta and read back where it landed.\n */\nexport const timeScale = (\n originMs: number,\n extentMs: number,\n width: number,\n zoom = 1,\n pan = 0\n): TimeScale => {\n const extent = Math.max(extentMs, MIN_EXTENT_MS);\n const z = clampZoom(zoom);\n const visibleMs = extent / z;\n const p = clampPan(pan, extent, z);\n const pxPerMs = Math.max(width, 1) / visibleMs;\n const start = originMs + p;\n return {\n pxPerMs,\n start,\n end: start + visibleMs,\n toX: (ms) => (ms - start) * pxPerMs,\n toMs: (x) => start + x / pxPerMs,\n };\n};\n\nexport const clampZoom = (zoom: number): number =>\n Math.min(Math.max(Number.isNaN(zoom) ? 1 : zoom, 1), MAX_ZOOM);\n\n/** Keeps the visible window inside the recording. */\nexport const clampPan = (\n pan: number,\n extentMs: number,\n zoom: number\n): number => {\n const extent = Math.max(extentMs, MIN_EXTENT_MS);\n const max = extent - extent / clampZoom(zoom);\n return Math.min(Math.max(Number.isNaN(pan) ? 0 : pan, 0), max);\n};\n\n/** The pan that puts the live edge (the end of the recording) at the right\n * of the plot, for following a recording in progress. */\nexport const livePan = (extentMs: number, zoom: number): number =>\n clampPan(Infinity, extentMs, zoom);\n\n/**\n * Zooms by `factor` while keeping the millisecond under pixel `x` where it\n * is, which is what a wheel over a timeline is expected to do.\n */\nexport const zoomAt = (\n originMs: number,\n extentMs: number,\n width: number,\n zoom: number,\n pan: number,\n x: number,\n factor: number\n): {zoom: number; pan: number} => {\n const before = timeScale(originMs, extentMs, width, zoom, pan);\n const anchor = before.toMs(x);\n const nextZoom = clampZoom(clampZoom(zoom) * factor);\n const after = timeScale(originMs, extentMs, width, nextZoom, 0);\n const nextPan = anchor - originMs - x / after.pxPerMs;\n return {zoom: nextZoom, pan: clampPan(nextPan, extentMs, nextZoom)};\n};\n\n/**\n * Index range `[from, to)` of the spans in `row` that intersect the window\n * `[start, end]`, found by binary search so a zoomed-in lane renders a\n * handful of marks rather than walking thousands.\n *\n * Only the span just before the first one starting inside the window can\n * reach into it, because a row's spans never overlap.\n */\nexport const visibleRange = (\n row: TrackRow,\n start: number,\n end: number\n): [number, number] => {\n const firstAtOrAfter = (t: number, inclusive: boolean) => {\n let lo = 0;\n let hi = row.length;\n while (lo < hi) {\n const mid = (lo + hi) >> 1;\n const s = row[mid]!.start;\n if (inclusive ? s < t : s <= t) lo = mid + 1;\n else hi = mid;\n }\n return lo;\n };\n let from = firstAtOrAfter(start, true);\n if (from > 0 && occupiedEnd(row[from - 1]!) >= start) from--;\n const to = firstAtOrAfter(end, false);\n return [from, Math.max(from, to)];\n};\n\n/**\n * A round tick interval (1, 2 or 5 times a power of ten, in milliseconds) at\n * least `minMs` long, for axis labels that land on readable numbers.\n */\nexport const niceStep = (minMs: number): number => {\n if (!(minMs > 0) || !Number.isFinite(minMs)) return 1;\n const pow = 10 ** Math.floor(Math.log10(minMs));\n for (const m of [1, 2, 5, 10]) {\n if (m * pow >= minMs) return m * pow;\n }\n return 10 * pow;\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oddsquad/vite-plugin-lit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Vite plugin providing true HMR for Lit components, plus CSS helpers and a DevTools panel",
|
|
5
5
|
"license": "BSD-3-Clause",
|
|
6
6
|
"author": "Oddsquad (based on @lit-labs/vite-hmr by Google LLC)",
|
|
@@ -28,6 +28,10 @@
|
|
|
28
28
|
"types": "./lib/runtime/css.d.ts",
|
|
29
29
|
"default": "./lib/runtime/css.js"
|
|
30
30
|
},
|
|
31
|
+
"./connect.js": {
|
|
32
|
+
"types": "./lib/runtime/rpc-transport.d.ts",
|
|
33
|
+
"default": "./lib/runtime/rpc-transport.js"
|
|
34
|
+
},
|
|
31
35
|
"./client": {
|
|
32
36
|
"types": "./client.d.ts"
|
|
33
37
|
}
|
|
@@ -81,6 +85,7 @@
|
|
|
81
85
|
"lightningcss": "^1.33.0",
|
|
82
86
|
"lit": "^3.3.3",
|
|
83
87
|
"playwright-core": "^1.63.0",
|
|
88
|
+
"temporal-polyfill": "^1.0.5",
|
|
84
89
|
"typescript": "^7.0.2",
|
|
85
90
|
"vite": "npm:@voidzero-dev/vite-plus-core@0.3.2",
|
|
86
91
|
"vite-plus": "0.3.2"
|