Skip to content

feat(web-replay): add RDP session replay web component stack#1216

Open
Karthik S Kashyap (karthikkashyap98) wants to merge 3 commits intoDevolutions:masterfrom
karthikkashyap98:feat/web-replay
Open

feat(web-replay): add RDP session replay web component stack#1216
Karthik S Kashyap (karthikkashyap98) wants to merge 3 commits intoDevolutions:masterfrom
karthikkashyap98:feat/web-replay

Conversation

@karthikkashyap98
Copy link
Copy Markdown

RDP Replay Player

Demo

Hello! 👋 I'm adding replay support for our use case at Cloudflare.

This is structured as 3 commits to keep the logical separation clear. If it would be easier to review as three stacked PRs (WASM crate, web component, demo app), I'm happy to split it up! Let me know what works best.

What this adds

Browser-based playback of recorded RDP sessions. Does not include a recording mechanism, instead the player accepts PDUs through a ReplayDataSource interface, and it's up to the consumer to provide the data.

Commit 1: WASM decoding engine (ironrdp-web-replay + iron-replay-player-wasm)

New Rust crate compiled to WASM via wasm-pack. Reuses ironrdp-session FastPath and ironrdp-graphics to decode recorded PDUs and render them to a canvas in the browser.

  • PDU Buffer (buffer.rs): Holds recorded PDUs with their timestamps in FIFO order, consumed by the replay engine during rendering.
  • Replay Engine (replay.rs): Main entry point that the web component from commit 2 uses. Handles rendering up to a specific point in time, seeking, canvas painting with cursor compositing, and state reset for backward seeks.
  • PDU Routing (process.rs): Dispatches raw bytes through server FastPath (graphics/pointers), client FastPath (mouse input), and X224 (session control).
  • TS Adapter (web-client/iron-replay-player-wasm/): Wraps the wasm-pack output into a clean ES module, following the same pattern as iron-remote-desktop-rdp.

Tests: Integration tests in a new ironrdp-testsuite-replay crate covering PDU processing, seek behavior, and rendering.

Commit 2: Web component (iron-replay-player)

Svelte 5 custom element that provides the playback UI. Follows the same module injection pattern as iron-remote-desktop, receives its WASM backend and data source as props at runtime, knows nothing about RDP.

  • Web Component (src/iron-replay-player.svelte): <iron-replay-player> custom element.
  • Data Source Interface (src/interfaces/ReplayDataSource.ts): ReplayDataSource is passed as a prop. Allows the replay player to work with any data source (HTTP, local file, IndexedDB, WebSocket) as long as it follows the open/fetch/close contract.
  • Playback Engine (src/services/replay-store.svelte.ts): requestAnimationFrame render loop that calls renderTill() on the WASM engine from commit 1 each frame. Handles playback controls (play/pause/seek/speed) and checks buffer health, fetching more data from the ReplayDataSource if needed.
  • UI: Seek bar with drag to seek, speed selector, keyboard shortcuts (Space, arrows, j/k/l), and overlays for loading/buffering/ended states.
  • Unit Tests (tests/replay-store.svelte.test.ts): Test the store state machine using fake timers and mocks.
  • Browser Tests (tests/browser/): Smoke tests that render the full component in Chromium and verify UI interactions (pointer events, keyboard shortcuts, overlay clicks) trigger the right store functions.

Playwright setup: Browser tests require npx playwright install chromium after npm install.

Commit 3: Demo app (iron-svelte-replay-client)

SvelteKit app showing how a consumer wires the WASM backend, adapter, and web component together with real data sources. This is a reference implementation, a production consumer would implement ReplayDataSource against their own format and transport.

  • HTTP Range Data Source (src/lib/HttpRangeDataSource.ts): ReplayDataSource impl that works with byte-range requests, fetching only the bytes needed for the current playback window. Primary demo of the replay engine.
  • Local File Data Source (src/lib/LocalFileDataSource.ts): ReplayDataSource impl for drag-and-drop playback. Reads the full file into memory, serves PDUs as zero-copy subviews.
  • Recording Format (src/lib/recordingFormat.ts): Custom binary format (header + index table + PDU data) explained in detail in the component README (web-client/iron-replay-player/README.md).
  • Dev Server (scripts/replay-server.mjs): Serves recordings locally with HTTP Range and CORS support.

Build integration

New cargo xtask web subcommands, following the existing install/build/check pattern:

  • install-replay: runs npm install for the adapter, component, and demo app
  • build-replay: compiles the WASM crate, patches the generated JS for Vite, builds all three packages
  • check-replay: dev-mode build, type checking, linting, and unit tests for all packages
  • run-replay: starts the SvelteKit dev server

Render loop

sequenceDiagram
    participant rAF as requestAnimationFrame
    participant Store as replay-store
    participant DS as DataSource
    participant WASM as WASM Replay
    participant Canvas as Canvas
    participant UI as SeekBar / Controls
    loop Every Frame (~16ms)
        rAF->>Store: tick(timestamp)

        alt Buffer low
            Store->>DS: fetch(fromMs, toMs, signal)
            DS-->>Store: ReplayPdu[]
            Store->>WASM: pushPdu() for each PDU
        end

        Store->>Store: elapsed += delta x speed
        Store->>WASM: renderTill(elapsed)
        WASM->>WASM: decode buffered PDUs -> framebuffer
        WASM->>Canvas: putImageData + cursor composite
        WASM-->>Store: RenderResult

        Store->>UI: update elapsed
        Store->>rAF: requestAnimationFrame(tick)
    end
Loading

Test plan

  • cargo xtask check fmt -v
  • cargo xtask check lints -v
  • cargo xtask check tests -v (includes replay integration tests)
  • cargo xtask web build-replay
  • cargo xtask web check-replay (type checking, linting, unit tests)
  • cd web-client/iron-replay-player && npm run test:browser (browser tests, Playwright)
  • cd web-client/iron-replay-player && npm run check (svelte-check)

cargo xtask web check-replay runs linting, type checking, and unit tests for all three packages.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a full “web replay” stack to IronRDP: a new Rust→WASM replay engine, a Svelte custom-element player UI that consumes a ReplayDataSource, and a SvelteKit demo app showing HTTP Range + local-file playback. It also integrates new cargo xtask web *-replay commands to build/check/run the replay packages.

Changes:

  • Add ironrdp-web-replay (Rust/WASM) replay engine + ironrdp-testsuite-replay integration tests.
  • Add <iron-replay-player> Svelte 5 custom element (store, UI, unit + browser tests).
  • Add iron-svelte-replay-client demo app + extend xtask web with replay build/install/check/run commands.

Reviewed changes

Copilot reviewed 85 out of 93 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
xtask/src/web.rs Adds replay build/install/check/run tasks and a shared wasm-pack JS patcher for Vite.
xtask/src/main.rs Wires new replay web actions into the xtask dispatcher.
xtask/src/cli.rs Adds CLI help + argument parsing for web *-replay commands.
web-client/iron-svelte-replay-client/vite.config.ts New demo app Vite config (SvelteKit + WASM/top-level-await plugins).
web-client/iron-svelte-replay-client/tsconfig.json New demo app TS config.
web-client/iron-svelte-replay-client/svelte.config.js New demo app SvelteKit adapter config.
web-client/iron-svelte-replay-client/static/favicon.svg Demo app favicon asset.
web-client/iron-svelte-replay-client/src/routes/http/+page.svelte HTTP Range demo route wiring <iron-replay-player> with HttpRangeDataSource.
web-client/iron-svelte-replay-client/src/routes/file/+page.svelte Local file demo route wiring <iron-replay-player> with LocalFileDataSource.
web-client/iron-svelte-replay-client/src/routes/+page.svelte Demo home page (source selection).
web-client/iron-svelte-replay-client/src/routes/+layout.svelte Demo layout with simple nav/back link.
web-client/iron-svelte-replay-client/src/lib/recordingFormat.ts Recording format constants + searchByTime utility.
web-client/iron-svelte-replay-client/src/lib/initPlayer.ts Demo helper to init WASM backend and set element properties.
web-client/iron-svelte-replay-client/src/lib/fetchRecording.ts HTTP Range fetch + header/index parsing utilities.
web-client/iron-svelte-replay-client/src/lib/ReplayDataSource.types.ts Demo-local structural typing for ReplayDataSource compatibility.
web-client/iron-svelte-replay-client/src/lib/LocalFileDataSource.ts Demo local-file ReplayDataSource implementation.
web-client/iron-svelte-replay-client/src/lib/HttpRangeDataSource.ts Demo HTTP Range ReplayDataSource implementation.
web-client/iron-svelte-replay-client/src/app.html Loads the built <iron-replay-player> bundle into the demo app.
web-client/iron-svelte-replay-client/src/app.d.ts SvelteKit app type stub.
web-client/iron-svelte-replay-client/scripts/replay-server.mjs Dev-only Range-enabled HTTP server for sample recordings.
web-client/iron-svelte-replay-client/pre-build.js Pre-build orchestrator to build/copy component + WASM adapter artifacts into static/.
web-client/iron-svelte-replay-client/package.json Demo app scripts and dev tooling dependencies.
web-client/iron-svelte-replay-client/eslint.config.mjs Demo app ESLint flat config.
web-client/iron-svelte-replay-client/README.md Demo app usage/docs and recording file format spec.
web-client/iron-svelte-replay-client/.prettierrc.yaml Demo app Prettier config.
web-client/iron-svelte-replay-client/.prettierignore Demo app Prettier ignore list.
web-client/iron-svelte-replay-client/.npmrc Demo app npm engine strict setting.
web-client/iron-svelte-replay-client/.gitignore Demo app gitignore (build + generated static artifacts).
web-client/iron-replay-player/vitest.config.ts Unit-test Vitest config for the replay player package.
web-client/iron-replay-player/vitest.browser.config.ts Browser-test Vitest config (Playwright/Chromium).
web-client/iron-replay-player/vite.config.ts Library build config for the <iron-replay-player> bundle.
web-client/iron-replay-player/tsconfig.node.json TS project reference for Vite config typing.
web-client/iron-replay-player/tsconfig.json Replay player TS config (Svelte + tests).
web-client/iron-replay-player/tests/helpers/mock-wasm-replay.ts WASM backend mock for unit/browser tests.
web-client/iron-replay-player/tests/helpers/mock-data-source.ts Deferred-promise mock ReplayDataSource for tests.
web-client/iron-replay-player/tests/format-time.test.ts Unit tests for formatTime().
web-client/iron-replay-player/tests/browser/speed-selector.browser.test.ts Browser tests for speed selector behavior.
web-client/iron-replay-player/tests/browser/setup.ts Browser test mount helpers + ready-event capture.
web-client/iron-replay-player/tests/browser/seek.browser.test.ts Browser tests for seekbar pointer + keyboard seek behavior.
web-client/iron-replay-player/tests/browser/playback-controls.browser.test.ts Browser tests for play/pause/reset/overlay interactions.
web-client/iron-replay-player/tests/browser/overlays.browser.test.ts Browser tests for loading/buffering/action/ended overlays.
web-client/iron-replay-player/tests/README.md Test strategy + patterns documentation.
web-client/iron-replay-player/svelte.config.js Enables Svelte custom element compilation.
web-client/iron-replay-player/src/ui/format-time.ts Time formatting utility used by UI.
web-client/iron-replay-player/src/ui/SeekBar.svelte Seekbar UI component (pointer-driven slider).
web-client/iron-replay-player/src/ui/PlaybackControls.svelte Playback controls UI (play/pause/reset/speed/fullscreen).
web-client/iron-replay-player/src/services/replay-store.svelte.ts Core store/state machine (buffering, seek, rAF loop, errors).
web-client/iron-replay-player/src/main.ts Library entry point exporting CE + public TS types.
web-client/iron-replay-player/src/iron-replay-player.svelte <iron-replay-player> custom element implementation + styling.
web-client/iron-replay-player/src/interfaces/ReplayModule.ts TS interface for injected WASM backend module + instance API.
web-client/iron-replay-player/src/interfaces/ReplayDataSource.ts TS interface for consumer-provided replay data sources + errors.
web-client/iron-replay-player/src/interfaces/PlayerApi.ts Public imperative API shape emitted via ready event.
web-client/iron-replay-player/src/interfaces/PlaybackState.ts Store playback state type.
web-client/iron-replay-player/src/interfaces/LoadState.ts Store load state type.
web-client/iron-replay-player/package.json Replay player package scripts + dev deps.
web-client/iron-replay-player/eslint.config.mjs Replay player ESLint flat config.
web-client/iron-replay-player/README.md Replay player usage docs (module injection, datasource contract, API).
web-client/iron-replay-player/.prettierrc.yaml Replay player Prettier config.
web-client/iron-replay-player/.prettierignore Replay player Prettier ignore list.
web-client/iron-replay-player/.npmrc Replay player npm engine strict setting.
web-client/iron-replay-player/.gitignore Replay player gitignore.
web-client/iron-replay-player-wasm/vite.config.ts Adapter library build config (ES module + dts generation).
web-client/iron-replay-player-wasm/tsconfig.node.json TS project reference for Vite config typing.
web-client/iron-replay-player-wasm/tsconfig.json WASM adapter TS config.
web-client/iron-replay-player-wasm/src/main.ts Adapter module exporting init() + ReplayBackend.
web-client/iron-replay-player-wasm/pre-build.js Adapter prebuild script invoking cargo xtask web build-replay.
web-client/iron-replay-player-wasm/package.json WASM adapter scripts + dev deps.
web-client/iron-replay-player-wasm/eslint.config.mjs WASM adapter ESLint flat config.
web-client/iron-replay-player-wasm/.prettierrc.yaml WASM adapter Prettier config.
web-client/iron-replay-player-wasm/.prettierignore WASM adapter Prettier ignore list.
web-client/iron-replay-player-wasm/.npmrc WASM adapter npm engine strict setting.
web-client/iron-replay-player-wasm/.gitignore WASM adapter gitignore.
crates/ironrdp-web-replay/src/replay.rs WASM-facing Replay engine (canvas blit + cursor compositing + config).
crates/ironrdp-web-replay/src/process.rs Replay PDU routing/processing (FastPath/X224, seek suppression, results).
crates/ironrdp-web-replay/src/lib.rs Crate module wiring + exported WASM-facing types.
crates/ironrdp-web-replay/src/buffer.rs Timestamped FIFO PDU buffer for replay processing.
crates/ironrdp-web-replay/README.md Crate documentation (API, build, tests, limitations).
crates/ironrdp-web-replay/Cargo.toml New crate manifest + wasm/web dependencies.
crates/ironrdp-web-replay/.gitignore Ignores wasm-pack outputs and targets.
crates/ironrdp-testsuite-replay/tests/web_replay/process.rs Integration tests for replay processing/seek behavior/results.
crates/ironrdp-testsuite-replay/tests/web_replay/mod.rs PDU buffer integration tests.
crates/ironrdp-testsuite-replay/tests/main.rs Test harness entry for replay testsuite.
crates/ironrdp-testsuite-replay/test_data/pdu/web_replay/x224_server_demand_active.bin Captured binary PDU fixture used by tests.
crates/ironrdp-testsuite-replay/src/lib.rs Testsuite crate root.
crates/ironrdp-testsuite-replay/Cargo.toml New replay testsuite crate manifest.
Cargo.lock Adds lock entries for new crates and dependencies.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +54 to +59
const firstEntry = this.indexTable[startIdx];
const lastEntry = this.indexTable[endIdx - 1];
// Safe: RDP recording byte offsets are well under Number.MAX_SAFE_INTEGER.
const startByte = Number(firstEntry.byteOffset);
const endByte = Number(lastEntry.byteOffset) + lastEntry.pduLength - 1;

Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This converts uint64 byte offsets to number without validating they’re within Number.MAX_SAFE_INTEGER. If a recording’s offsets exceed that, the computed Range request boundaries and subarray slicing will be incorrect. Consider keeping offsets as bigint (and computing ranges with bigint) or assert safe-integer bounds and fail fast with a clear error.

Copilot uses AI. Check for mistakes.
Comment on lines +177 to +188
// Sync cursor state from processor (clone to release the borrow).
let pointer_state = {
#[expect(clippy::unwrap_used, reason = "processor is Some per is_none() early return")]
self.processor.as_ref().unwrap().current_pointer_state().clone()
};
match &pointer_state {
crate::process::PointerState::Bitmap(pointer) => {
self.pointer_hotspot_x = pointer.hotspot_x;
self.pointer_hotspot_y = pointer.hotspot_y;
self.cursor_canvas = Self::build_cursor_canvas(pointer);
self.pointer_hidden = false;
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor bitmap compositing rebuilds an OffscreenCanvas on every render_till() call whenever the pointer state is Bitmap, even if the pointer hasn’t changed. This is likely a large per-frame allocation/CPU cost. Consider caching the last DecodedPointer identity (e.g., store the Arc<DecodedPointer> and use Arc::ptr_eq to detect changes) and only rebuilding cursor_canvas when a new pointer bitmap update is observed.

Copilot uses AI. Check for mistakes.
Comment thread crates/ironrdp-web-replay/README.md Outdated
Comment on lines +51 to +58
`ironrdp-testsuite-core`, following the same pattern as `ironrdp-session`.

```sh
# Run all web-replay tests
cargo test -p ironrdp-testsuite-core -- web_replay

# Run a specific test
cargo test -p ironrdp-testsuite-core -- web_replay::pdu_buffer_clear_empties_buffer
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

README references ironrdp-testsuite-core and shows cargo test -p ironrdp-testsuite-core -- web_replay ..., but the tests added in this PR live under the new ironrdp-testsuite-replay crate. Update the crate name and example commands so they match the actual test location.

Suggested change
`ironrdp-testsuite-core`, following the same pattern as `ironrdp-session`.
```sh
# Run all web-replay tests
cargo test -p ironrdp-testsuite-core -- web_replay
# Run a specific test
cargo test -p ironrdp-testsuite-core -- web_replay::pdu_buffer_clear_empties_buffer
`ironrdp-testsuite-replay`, following the same pattern as `ironrdp-session`.
```sh
# Run all web-replay tests
cargo test -p ironrdp-testsuite-replay -- web_replay
# Run a specific test
cargo test -p ironrdp-testsuite-replay -- web_replay::pdu_buffer_clear_empties_buffer

Copilot uses AI. Check for mistakes.
Comment on lines +10 to +12
"check": "svelte-check --tsconfig ./tsconfig.json",
"check:dist": "tsc ./dist/index.d.ts --noEmit",
"test": "vitest run",
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check:dist runs tsc ./dist/index.d.ts --noEmit, but this package’s Vite config explicitly omits type generation (no dts plugin), so dist/index.d.ts won’t exist after vite build. Either remove/rename this script, or add a type-generation step that actually produces dist/index.d.ts.

Copilot uses AI. Check for mistakes.
Comment thread xtask/src/web.rs
Comment on lines +26 to +31
fn patch_vite_wasm_url(js_path: &std::path::Path, wasm_filename: &str) -> anyhow::Result<()> {
let content = fs::read_to_string(js_path)?;
let content = format!("import wasmUrl from './{wasm_filename}?url';\n\n{content}");
let content = content.replace(&format!("new URL('{wasm_filename}', import.meta.url)"), "wasmUrl");
fs::write(js_path, content)?;
Ok(())
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

patch_vite_wasm_url() is not idempotent: every invocation prepends a new import wasmUrl ... line, so running cargo xtask web build* twice will produce duplicate imports and a JS parse error. Consider detecting an existing patch (e.g., check for the import line / a marker comment) before prepending, or rewrite the file in a way that’s safe to apply multiple times.

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +45
const indexStart = HEADER_SIZE;
const indexView = new DataView(this.buffer, indexStart, INDEX_ROW_SIZE * this.totalPdus);
this.indexTable = [];
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new DataView(this.buffer, indexStart, INDEX_ROW_SIZE * this.totalPdus) will throw a RangeError if the file is truncated (too small for the declared index table). Consider adding an explicit length check (buffer.byteLength >= HEADER_SIZE + INDEX_ROW_SIZE*totalPdus) and throwing a clearer error message before constructing the DataView.

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +53
const pduLength = indexView.getUint32(offset + 4, false);
const byteOffset = Number(indexView.getBigUint64(offset + 8, false));
if (byteOffset + pduLength > this.buffer.byteLength) {
throw new Error(`PDU ${i} extends beyond file boundary (offset ${byteOffset}, length ${pduLength}, file size ${this.buffer.byteLength})`);
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

byteOffset is stored as a uint64 in the recording format but is converted to Number here. For sufficiently large recordings this can lose precision and cause incorrect slicing / boundary checks. Either store byteOffset as bigint and convert safely at use-sites, or assert byteOffset <= Number.MAX_SAFE_INTEGER and throw a clear error when it’s not.

Copilot uses AI. Check for mistakes.
Comment on lines +186 to +195
async function initialiseRecording(source: ReplayDataSource): Promise<void> {
openAbort = resetAbort(openAbort);
const { signal } = openAbort; // capture before any await

dataSource = source;
loadState = { status: 'loading' };
duration = 0;
totalPdus = 0;
fetchedUntilMs = 0;
recordingMetadata = null;
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

initialiseRecording() overwrites dataSource without closing the previous one and without resetting playback state / stopping the rAF loop. If load() is called while playing, tick() can start calling fetch() before the new open() resolves, and the old data source may leak resources. Close the previous data source, stop playback (paused=true, waiting=false, seeking=false, ended=false), reset elapsed/lastTimestamp, and abort any in-flight prefetch/seek before assigning the new source.

Copilot uses AI. Check for mistakes.
Comment on lines +552 to +554
:global(.__irp-seekbar.interactive:hover .__irp-seekbar-track) {
height: 6px;
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSS selectors use .__irp-seekbar.interactive:hover ..., but the component sets the class name as __irp-interactive (see class:__irp-interactive). As written, the hover height change will never apply. Update the selector to match the actual class name.

Copilot uses AI. Check for mistakes.
Comment on lines +589 to +592
:global(.__irp-seekbar.interactive:hover .__irp-seekbar-head) {
width: 16px;
height: 16px;
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as above: the selector .__irp-seekbar.interactive:hover .__irp-seekbar-head won’t match because the class name is __irp-interactive. Update the selector so the hover head size change actually applies.

Copilot uses AI. Check for mistakes.
Add the Rust WASM replay engine (ironrdp-web-replay) that decodes RDP
session recordings (FastPath PDUs) into a canvas framebuffer, and the
TypeScript adapter library (iron-replay-player-wasm) that bridges the
wasm-pack output to a clean init()/ReplayBackend ES module API.

Includes trimmed xtask commands (build-replay, install-replay,
check-replay) for building and checking the WASM + adapter layer.

The web component and demo app will be added in subsequent stacked PRs.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: I think it’s not worth having a new crate just for replay, instead you may add them to ironrdp-testsuite-extra (rebase on master before doing that, I made some changes to it)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's fair!
I'm gonna refactor this. Thanks!

Add the <iron-replay-player> custom element that provides RDP session
replay playback with adaptive buffering, seeking, and variable-speed
controls. Accepts a ReplayDataSource (consumer-provided) and
ReplayModule (WASM backend from the adapter layer) via props.

Expands xtask build-replay/install-replay/check-replay to also build,
install, and check the web component package.

The demo app will be added in the next stacked PR.
Add the SvelteKit demo application that wires together the WASM engine,
TS adapter, and web component. Ships two reference ReplayDataSource
implementations: HttpRangeDataSource for streaming via HTTP Range
requests, and LocalFileDataSource for drag-and-drop file testing.

Completes the xtask build integration by expanding build-replay,
install-replay, and check-replay to cover all three packages, and
adding the run-replay command to start the dev server.

This is the final PR in the 3-branch stacked chain.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants