deio-scroll 1.0.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/LICENSE +21 -0
- package/README.md +21 -0
- package/dist/api/server.d.ts +3 -0
- package/dist/api/server.d.ts.map +1 -0
- package/dist/api/server.js +393 -0
- package/dist/api/server.js.map +1 -0
- package/dist/artifacts/store.d.ts +24 -0
- package/dist/artifacts/store.d.ts.map +1 -0
- package/dist/artifacts/store.js +43 -0
- package/dist/artifacts/store.js.map +1 -0
- package/dist/browser/componentInteraction.d.ts +24 -0
- package/dist/browser/componentInteraction.d.ts.map +1 -0
- package/dist/browser/componentInteraction.js +328 -0
- package/dist/browser/componentInteraction.js.map +1 -0
- package/dist/browser/composition.d.ts +42 -0
- package/dist/browser/composition.d.ts.map +1 -0
- package/dist/browser/composition.js +132 -0
- package/dist/browser/composition.js.map +1 -0
- package/dist/browser/cookies.d.ts +3 -0
- package/dist/browser/cookies.d.ts.map +1 -0
- package/dist/browser/cookies.js +20 -0
- package/dist/browser/cookies.js.map +1 -0
- package/dist/browser/curves.d.ts +4 -0
- package/dist/browser/curves.d.ts.map +1 -0
- package/dist/browser/curves.js +35 -0
- package/dist/browser/curves.js.map +1 -0
- package/dist/browser/detectScrollMode.d.ts +5 -0
- package/dist/browser/detectScrollMode.d.ts.map +1 -0
- package/dist/browser/detectScrollMode.js +61 -0
- package/dist/browser/detectScrollMode.js.map +1 -0
- package/dist/browser/directionGuardrails.d.ts +22 -0
- package/dist/browser/directionGuardrails.d.ts.map +1 -0
- package/dist/browser/directionGuardrails.js +99 -0
- package/dist/browser/directionGuardrails.js.map +1 -0
- package/dist/browser/goto.d.ts +9 -0
- package/dist/browser/goto.d.ts.map +1 -0
- package/dist/browser/goto.js +62 -0
- package/dist/browser/goto.js.map +1 -0
- package/dist/browser/hydrate.d.ts +8 -0
- package/dist/browser/hydrate.d.ts.map +1 -0
- package/dist/browser/hydrate.js +73 -0
- package/dist/browser/hydrate.js.map +1 -0
- package/dist/browser/mediaClock.d.ts +9 -0
- package/dist/browser/mediaClock.d.ts.map +1 -0
- package/dist/browser/mediaClock.js +86 -0
- package/dist/browser/mediaClock.js.map +1 -0
- package/dist/browser/motion.d.ts +21 -0
- package/dist/browser/motion.d.ts.map +1 -0
- package/dist/browser/motion.js +42 -0
- package/dist/browser/motion.js.map +1 -0
- package/dist/browser/networkPolicy.d.ts +11 -0
- package/dist/browser/networkPolicy.d.ts.map +1 -0
- package/dist/browser/networkPolicy.js +119 -0
- package/dist/browser/networkPolicy.js.map +1 -0
- package/dist/browser/prime.d.ts +3 -0
- package/dist/browser/prime.d.ts.map +1 -0
- package/dist/browser/prime.js +13 -0
- package/dist/browser/prime.js.map +1 -0
- package/dist/browser/sanitize.d.ts +3 -0
- package/dist/browser/sanitize.d.ts.map +1 -0
- package/dist/browser/sanitize.js +37 -0
- package/dist/browser/sanitize.js.map +1 -0
- package/dist/browser/scroll.d.ts +29 -0
- package/dist/browser/scroll.d.ts.map +1 -0
- package/dist/browser/scroll.js +420 -0
- package/dist/browser/scroll.js.map +1 -0
- package/dist/browser/scrollEasing.d.ts +5 -0
- package/dist/browser/scrollEasing.d.ts.map +1 -0
- package/dist/browser/scrollEasing.js +40 -0
- package/dist/browser/scrollEasing.js.map +1 -0
- package/dist/browser/scrollReadiness.d.ts +25 -0
- package/dist/browser/scrollReadiness.d.ts.map +1 -0
- package/dist/browser/scrollReadiness.js +105 -0
- package/dist/browser/scrollReadiness.js.map +1 -0
- package/dist/browser/urlGuard.d.ts +4 -0
- package/dist/browser/urlGuard.d.ts.map +1 -0
- package/dist/browser/urlGuard.js +22 -0
- package/dist/browser/urlGuard.js.map +1 -0
- package/dist/browser/virtualScroll.d.ts +19 -0
- package/dist/browser/virtualScroll.d.ts.map +1 -0
- package/dist/browser/virtualScroll.js +50 -0
- package/dist/browser/virtualScroll.js.map +1 -0
- package/dist/capture/detectMargins.d.ts +6 -0
- package/dist/capture/detectMargins.d.ts.map +1 -0
- package/dist/capture/detectMargins.js +145 -0
- package/dist/capture/detectMargins.js.map +1 -0
- package/dist/capture/frameRecorder.d.ts +29 -0
- package/dist/capture/frameRecorder.d.ts.map +1 -0
- package/dist/capture/frameRecorder.js +74 -0
- package/dist/capture/frameRecorder.js.map +1 -0
- package/dist/capture/stitchFrames.d.ts +9 -0
- package/dist/capture/stitchFrames.d.ts.map +1 -0
- package/dist/capture/stitchFrames.js +62 -0
- package/dist/capture/stitchFrames.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +329 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/browserLaunch.d.ts +13 -0
- package/dist/config/browserLaunch.d.ts.map +1 -0
- package/dist/config/browserLaunch.js +63 -0
- package/dist/config/browserLaunch.js.map +1 -0
- package/dist/config/recordingProfile.d.ts +10 -0
- package/dist/config/recordingProfile.d.ts.map +1 -0
- package/dist/config/recordingProfile.js +25 -0
- package/dist/config/recordingProfile.js.map +1 -0
- package/dist/config/runtimeMode.d.ts +2 -0
- package/dist/config/runtimeMode.d.ts.map +1 -0
- package/dist/config/runtimeMode.js +4 -0
- package/dist/config/runtimeMode.js.map +1 -0
- package/dist/config/virtualScroll.d.ts +8 -0
- package/dist/config/virtualScroll.d.ts.map +1 -0
- package/dist/config/virtualScroll.js +19 -0
- package/dist/config/virtualScroll.js.map +1 -0
- package/dist/contracts.d.ts +157 -0
- package/dist/contracts.d.ts.map +1 -0
- package/dist/contracts.js +136 -0
- package/dist/contracts.js.map +1 -0
- package/dist/editor/compileVideo.d.ts +21 -0
- package/dist/editor/compileVideo.d.ts.map +1 -0
- package/dist/editor/compileVideo.js +104 -0
- package/dist/editor/compileVideo.js.map +1 -0
- package/dist/editor/composeComparison.d.ts +30 -0
- package/dist/editor/composeComparison.d.ts.map +1 -0
- package/dist/editor/composeComparison.js +315 -0
- package/dist/editor/composeComparison.js.map +1 -0
- package/dist/editor/composeResponsiveness.d.ts +19 -0
- package/dist/editor/composeResponsiveness.d.ts.map +1 -0
- package/dist/editor/composeResponsiveness.js +198 -0
- package/dist/editor/composeResponsiveness.js.map +1 -0
- package/dist/editor/frameVideo.d.ts +11 -0
- package/dist/editor/frameVideo.d.ts.map +1 -0
- package/dist/editor/frameVideo.js +181 -0
- package/dist/editor/frameVideo.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/inspection/inspectWebsite.d.ts +21 -0
- package/dist/inspection/inspectWebsite.d.ts.map +1 -0
- package/dist/inspection/inspectWebsite.js +412 -0
- package/dist/inspection/inspectWebsite.js.map +1 -0
- package/dist/jobs/manager.d.ts +123 -0
- package/dist/jobs/manager.d.ts.map +1 -0
- package/dist/jobs/manager.js +378 -0
- package/dist/jobs/manager.js.map +1 -0
- package/dist/jobs/store.d.ts +150 -0
- package/dist/jobs/store.d.ts.map +1 -0
- package/dist/jobs/store.js +446 -0
- package/dist/jobs/store.js.map +1 -0
- package/dist/jobs/thumbnail.d.ts +2 -0
- package/dist/jobs/thumbnail.d.ts.map +1 -0
- package/dist/jobs/thumbnail.js +36 -0
- package/dist/jobs/thumbnail.js.map +1 -0
- package/dist/pipeline/processRecordingRequest.d.ts +4 -0
- package/dist/pipeline/processRecordingRequest.d.ts.map +1 -0
- package/dist/pipeline/processRecordingRequest.js +17 -0
- package/dist/pipeline/processRecordingRequest.js.map +1 -0
- package/dist/pipeline/recordComparison.d.ts +4 -0
- package/dist/pipeline/recordComparison.d.ts.map +1 -0
- package/dist/pipeline/recordComparison.js +99 -0
- package/dist/pipeline/recordComparison.js.map +1 -0
- package/dist/pipeline/recordResponsiveness.d.ts +4 -0
- package/dist/pipeline/recordResponsiveness.d.ts.map +1 -0
- package/dist/pipeline/recordResponsiveness.js +122 -0
- package/dist/pipeline/recordResponsiveness.js.map +1 -0
- package/dist/pipeline/recordWebsite.d.ts +11 -0
- package/dist/pipeline/recordWebsite.d.ts.map +1 -0
- package/dist/pipeline/recordWebsite.js +446 -0
- package/dist/pipeline/recordWebsite.js.map +1 -0
- package/dist/pipeline/styleRecording.d.ts +12 -0
- package/dist/pipeline/styleRecording.d.ts.map +1 -0
- package/dist/pipeline/styleRecording.js +59 -0
- package/dist/pipeline/styleRecording.js.map +1 -0
- package/dist/transcode/ffmpeg.d.ts +4 -0
- package/dist/transcode/ffmpeg.d.ts.map +1 -0
- package/dist/transcode/ffmpeg.js +62 -0
- package/dist/transcode/ffmpeg.js.map +1 -0
- package/dist/transcode/probe.d.ts +7 -0
- package/dist/transcode/probe.d.ts.map +1 -0
- package/dist/transcode/probe.js +91 -0
- package/dist/transcode/probe.js.map +1 -0
- package/dist/transcode/quality.d.ts +8 -0
- package/dist/transcode/quality.d.ts.map +1 -0
- package/dist/transcode/quality.js +13 -0
- package/dist/transcode/quality.js.map +1 -0
- package/dist/types.d.ts +302 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/worker.d.ts +3 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +22 -0
- package/dist/worker.js.map +1 -0
- package/package.json +63 -0
- package/public/assets/index-C63t4P1L.css +1 -0
- package/public/assets/index-Sfgf7dSH.js +9 -0
- package/public/background_presets/gray_noise_gradient.png +0 -0
- package/public/background_presets/paper_blue.png +0 -0
- package/public/background_presets/red_blocks_gradient.png +0 -0
- package/public/deio-scroll-mark.svg +12 -0
- package/public/favicon.svg +1 -0
- package/public/icons.svg +24 -0
- package/public/index.html +16 -0
- package/public/scrollizard-mark.png +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ken-rl
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# deio
|
|
2
|
+
|
|
3
|
+
Create polished smooth-scroll recordings of live websites, from a local web app or the command line.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx deio-scroll setup
|
|
7
|
+
npx deio-scroll
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
Record directly:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx deio-scroll example.com --curve linear --speed 1
|
|
14
|
+
npx deio-scroll https://example.com --quality high --viewport 1920x1080
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Deio requires Node.js 20+ and ffmpeg. `npx deio-scroll setup` installs Playwright Chromium and checks whether ffmpeg is available.
|
|
18
|
+
|
|
19
|
+
Run `npx deio-scroll --help` for all commands and options.
|
|
20
|
+
|
|
21
|
+
Source and full documentation: https://github.com/ken-rl/website_recorder
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":";AACA,OAAO,eAAe,CAAC"}
|
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import http from "node:http";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { restyleRecording } from "../pipeline/styleRecording.js";
|
|
8
|
+
import { inspectWebsite } from "../inspection/inspectWebsite.js";
|
|
9
|
+
import { RecordingJobManager } from "../jobs/manager.js";
|
|
10
|
+
import { assertSafeTargetUrl } from "../browser/networkPolicy.js";
|
|
11
|
+
import { parseInspectRequest, parseRecordRequest, parseStyleRequest } from "../contracts.js";
|
|
12
|
+
import { embeddedWorkerEnabled } from "../config/runtimeMode.js";
|
|
13
|
+
const PORT = Number(process.env.PORT ?? 3847);
|
|
14
|
+
const HOST = process.env.HOST ?? "127.0.0.1";
|
|
15
|
+
const OUTPUT_DIR = path.resolve(process.env.OUTPUT_DIR ?? "./outputs");
|
|
16
|
+
const PUBLIC_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../public");
|
|
17
|
+
// Local startup is self-contained. Hosted deployments can set EMBEDDED_WORKER=0
|
|
18
|
+
// and run src/worker.ts independently against the same durable job database.
|
|
19
|
+
const hasEmbeddedWorker = embeddedWorkerEnabled();
|
|
20
|
+
const jobs = new RecordingJobManager(OUTPUT_DIR, {
|
|
21
|
+
processJobs: hasEmbeddedWorker,
|
|
22
|
+
recoverRunning: hasEmbeddedWorker ? "requeue" : "interrupt",
|
|
23
|
+
});
|
|
24
|
+
await jobs.initialize();
|
|
25
|
+
const server = http.createServer((req, res) => {
|
|
26
|
+
void handleRequest(req, res).catch((error) => {
|
|
27
|
+
console.error("Unhandled API request error", error);
|
|
28
|
+
if (res.writableEnded)
|
|
29
|
+
return;
|
|
30
|
+
if (!res.headersSent) {
|
|
31
|
+
sendJson(res, 500, { ok: false, error: errorMessage(error) });
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
res.end();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
server.on("error", (error) => {
|
|
39
|
+
const message = error.code === "EADDRINUSE"
|
|
40
|
+
? `Deio Scroll cannot start: ${HOST}:${PORT} is already in use. Stop the existing dev server or choose another PORT.`
|
|
41
|
+
: `Deio Scroll server error: ${error.message}`;
|
|
42
|
+
console.error(message);
|
|
43
|
+
process.exitCode = 1;
|
|
44
|
+
void jobs.shutdown();
|
|
45
|
+
});
|
|
46
|
+
async function handleRequest(req, res) {
|
|
47
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
|
|
48
|
+
if (req.method === "GET" && url.pathname === "/health") {
|
|
49
|
+
return sendJson(res, 200, {
|
|
50
|
+
ok: true,
|
|
51
|
+
workerMode: hasEmbeddedWorker ? "embedded" : "external",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (req.method === "POST" && url.pathname === "/api/inspect") {
|
|
55
|
+
try {
|
|
56
|
+
const body = parseInspectRequest(await readJsonBody(req));
|
|
57
|
+
await assertSafeTargetUrl(body.targetUrl);
|
|
58
|
+
const inspection = await inspectWebsite(body);
|
|
59
|
+
return sendJson(res, 200, { ok: true, inspection });
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const message = error instanceof Error ? error.message : "Inspection failed";
|
|
63
|
+
return sendJson(res, 400, { ok: false, error: message });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (req.method === "GET" && url.pathname === "/api/proxy") {
|
|
67
|
+
const target = url.searchParams.get("url");
|
|
68
|
+
if (!target) {
|
|
69
|
+
res.writeHead(400);
|
|
70
|
+
return res.end("Missing url parameter");
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
await assertSafeTargetUrl(target);
|
|
74
|
+
const response = await fetch(target);
|
|
75
|
+
const html = await response.text();
|
|
76
|
+
const targetUrlObj = new URL(target);
|
|
77
|
+
const baseUrl = targetUrlObj.origin + targetUrlObj.pathname;
|
|
78
|
+
let rewritten = html;
|
|
79
|
+
const baseTag = `<base href="${baseUrl}">`;
|
|
80
|
+
if (/<head[^>]*>/i.test(rewritten)) {
|
|
81
|
+
rewritten = rewritten.replace(/<head[^>]*>/i, (m) => `${m}\n${baseTag}`);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
rewritten = baseTag + rewritten;
|
|
85
|
+
}
|
|
86
|
+
res.writeHead(200, {
|
|
87
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
88
|
+
"Access-Control-Allow-Origin": "*",
|
|
89
|
+
"X-Frame-Options": "ALLOWALL",
|
|
90
|
+
"Content-Security-Policy": "default-src * 'unsafe-inline' 'unsafe-eval' data: blob:; frame-ancestors *",
|
|
91
|
+
});
|
|
92
|
+
return res.end(rewritten);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
res.writeHead(500);
|
|
96
|
+
return res.end(error instanceof Error ? error.message : "Proxy failed");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (req.method === "POST" && url.pathname === "/api/jobs") {
|
|
100
|
+
try {
|
|
101
|
+
const body = parseRecordRequest(await readJsonBody(req));
|
|
102
|
+
await Promise.all([
|
|
103
|
+
assertSafeTargetUrl(body.targetUrl),
|
|
104
|
+
body.comparison ? assertSafeTargetUrl(body.comparison.targetUrl) : Promise.resolve(),
|
|
105
|
+
]);
|
|
106
|
+
const job = await jobs.create(body);
|
|
107
|
+
return sendJson(res, 202, jobLinks(job.jobId));
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
const message = error instanceof Error ? error.message : "Could not create job";
|
|
111
|
+
return sendJson(res, 400, { ok: false, error: message });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (req.method === "GET" && url.pathname === "/api/jobs") {
|
|
115
|
+
return sendJson(res, 200, { ok: true, jobs: await jobs.list() });
|
|
116
|
+
}
|
|
117
|
+
const jobRoute = url.pathname.match(/^\/api\/jobs\/([^/]+)$/);
|
|
118
|
+
if (req.method === "GET" && jobRoute) {
|
|
119
|
+
try {
|
|
120
|
+
return sendJson(res, 200, { ok: true, job: await jobs.get(jobRoute[1]) });
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
return sendJson(res, 404, { ok: false, error: errorMessage(error) });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const eventsRoute = url.pathname.match(/^\/api\/jobs\/([^/]+)\/events$/);
|
|
127
|
+
if (req.method === "GET" && eventsRoute) {
|
|
128
|
+
try {
|
|
129
|
+
const jobId = eventsRoute[1];
|
|
130
|
+
const current = await jobs.get(jobId);
|
|
131
|
+
res.writeHead(200, {
|
|
132
|
+
"content-type": "text/event-stream; charset=utf-8",
|
|
133
|
+
"cache-control": "no-cache, no-transform",
|
|
134
|
+
connection: "keep-alive",
|
|
135
|
+
"x-accel-buffering": "no",
|
|
136
|
+
});
|
|
137
|
+
res.flushHeaders();
|
|
138
|
+
res.write("retry: 1500\n\n");
|
|
139
|
+
const write = (job) => {
|
|
140
|
+
if (!res.writableEnded && !res.destroyed) {
|
|
141
|
+
res.write(`event: job\ndata: ${JSON.stringify(job)}\n\n`);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
write(current);
|
|
145
|
+
const unsubscribe = jobs.subscribe(jobId, write);
|
|
146
|
+
const heartbeat = setInterval(() => {
|
|
147
|
+
if (!res.writableEnded && !res.destroyed)
|
|
148
|
+
res.write(": keepalive\n\n");
|
|
149
|
+
}, 10_000);
|
|
150
|
+
let cleanedUp = false;
|
|
151
|
+
const cleanup = () => {
|
|
152
|
+
if (cleanedUp)
|
|
153
|
+
return;
|
|
154
|
+
cleanedUp = true;
|
|
155
|
+
clearInterval(heartbeat);
|
|
156
|
+
unsubscribe();
|
|
157
|
+
};
|
|
158
|
+
req.on("close", cleanup);
|
|
159
|
+
res.on("close", cleanup);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
return sendJson(res, 404, { ok: false, error: errorMessage(error) });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const cancelRoute = url.pathname.match(/^\/api\/jobs\/([^/]+)\/cancel$/);
|
|
167
|
+
if (req.method === "POST" && cancelRoute) {
|
|
168
|
+
try {
|
|
169
|
+
return sendJson(res, 200, { ok: true, job: await jobs.cancel(cancelRoute[1]) });
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
return sendJson(res, 409, { ok: false, error: errorMessage(error) });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
const retryRoute = url.pathname.match(/^\/api\/jobs\/([^/]+)\/retry$/);
|
|
176
|
+
if (req.method === "POST" && retryRoute) {
|
|
177
|
+
try {
|
|
178
|
+
const job = await jobs.retry(retryRoute[1]);
|
|
179
|
+
return sendJson(res, 202, jobLinks(job.jobId));
|
|
180
|
+
}
|
|
181
|
+
catch (error) {
|
|
182
|
+
return sendJson(res, 409, { ok: false, error: errorMessage(error) });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (req.method === "DELETE" && jobRoute) {
|
|
186
|
+
try {
|
|
187
|
+
await jobs.remove(jobRoute[1]);
|
|
188
|
+
return sendJson(res, 200, { ok: true });
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
return sendJson(res, 409, { ok: false, error: errorMessage(error) });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (req.method === "GET" && url.pathname === "/api/upcoming") {
|
|
195
|
+
return sendJson(res, 200, {
|
|
196
|
+
ok: true,
|
|
197
|
+
upcomingFeatures: [
|
|
198
|
+
{
|
|
199
|
+
id: "react-component",
|
|
200
|
+
title: "Embeddable React Component (<WebRecorder />)",
|
|
201
|
+
description: "A reusable React component for developer dashboards and documentation with built-in progress feedback.",
|
|
202
|
+
status: "planned",
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: "deterministic-caching",
|
|
206
|
+
title: "Deterministic Server Caching",
|
|
207
|
+
description: "Instant video loading on matching configurations by checking pre-recorded caches indexed by SHA-256 configuration hashes.",
|
|
208
|
+
status: "planned",
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
id: "audio-overlay",
|
|
212
|
+
title: "Audio & Speech Hydration",
|
|
213
|
+
description: "Synthesize background soundtracks or text-to-speech audio overlays synchronized with the viewport scroll animation.",
|
|
214
|
+
status: "planned",
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
if (req.method === "GET" &&
|
|
220
|
+
(url.pathname === "/" ||
|
|
221
|
+
url.pathname === "/upcoming" ||
|
|
222
|
+
url.pathname === "/library" ||
|
|
223
|
+
url.pathname === "/compare")) {
|
|
224
|
+
return serveFile(res, path.join(PUBLIC_DIR, "index.html"), "text/html; charset=utf-8");
|
|
225
|
+
}
|
|
226
|
+
if (req.method === "GET" && url.pathname.startsWith("/outputs/")) {
|
|
227
|
+
const relativePath = url.pathname.slice("/outputs/".length);
|
|
228
|
+
const filePath = path.resolve(OUTPUT_DIR, relativePath);
|
|
229
|
+
if (!filePath.startsWith(OUTPUT_DIR + path.sep)) {
|
|
230
|
+
return sendJson(res, 403, { ok: false, error: "Forbidden" });
|
|
231
|
+
}
|
|
232
|
+
return serveFile(res, filePath, contentTypeFor(filePath));
|
|
233
|
+
}
|
|
234
|
+
// Serve general static files from PUBLIC_DIR (e.g. built JS/CSS files)
|
|
235
|
+
if (req.method === "GET" && url.pathname !== "/") {
|
|
236
|
+
const relativePath = url.pathname.startsWith("/")
|
|
237
|
+
? url.pathname.slice(1)
|
|
238
|
+
: url.pathname;
|
|
239
|
+
const filePath = path.resolve(PUBLIC_DIR, relativePath);
|
|
240
|
+
if (filePath.startsWith(PUBLIC_DIR + path.sep)) {
|
|
241
|
+
try {
|
|
242
|
+
await fs.access(filePath);
|
|
243
|
+
return serveFile(res, filePath, contentTypeFor(filePath));
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// Fall through to other routes or 404
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (req.method === "POST" && url.pathname === "/record") {
|
|
251
|
+
try {
|
|
252
|
+
const body = parseRecordRequest(await readJsonBody(req));
|
|
253
|
+
await Promise.all([
|
|
254
|
+
assertSafeTargetUrl(body.targetUrl),
|
|
255
|
+
body.comparison ? assertSafeTargetUrl(body.comparison.targetUrl) : Promise.resolve(),
|
|
256
|
+
]);
|
|
257
|
+
const queued = await jobs.create(body);
|
|
258
|
+
const job = await jobs.waitForCompletion(queued.jobId);
|
|
259
|
+
if (job.status !== "completed" || !job.result) {
|
|
260
|
+
throw new Error(job.error?.message ?? `Recording ${job.status}`);
|
|
261
|
+
}
|
|
262
|
+
const result = job.result;
|
|
263
|
+
return sendJson(res, 200, {
|
|
264
|
+
ok: true,
|
|
265
|
+
jobId: job.jobId,
|
|
266
|
+
videoUrl: result.videoUrl,
|
|
267
|
+
sourceVideoUrl: result.sourceVideoUrl,
|
|
268
|
+
mp4Path: path.join(OUTPUT_DIR, job.jobId, "output.mp4"),
|
|
269
|
+
durationMs: result.durationMs,
|
|
270
|
+
renderTimeMs: result.renderTimeMs,
|
|
271
|
+
viewport: result.viewport,
|
|
272
|
+
scrollStrategy: result.scrollStrategy,
|
|
273
|
+
motionPlan: result.motionPlan,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
catch (error) {
|
|
277
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
278
|
+
return sendJson(res, 400, { ok: false, error: message });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (req.method === "POST" && url.pathname === "/style") {
|
|
282
|
+
try {
|
|
283
|
+
const body = parseStyleRequest(await readJsonBody(req));
|
|
284
|
+
const result = await restyleRecording(body, OUTPUT_DIR);
|
|
285
|
+
const existing = await jobs.get(body.jobId).catch(() => null);
|
|
286
|
+
if (existing) {
|
|
287
|
+
const request = existing.request
|
|
288
|
+
? { ...existing.request, backgroundPreset: body.backgroundPreset, addShadow: body.addShadow, roundedCorners: body.roundedCorners }
|
|
289
|
+
: undefined;
|
|
290
|
+
await jobs.refreshResult(body.jobId, request);
|
|
291
|
+
}
|
|
292
|
+
return sendJson(res, 200, { ok: true, ...result });
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
296
|
+
return sendJson(res, 400, { ok: false, error: message });
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
sendJson(res, 404, { ok: false, error: "Not found" });
|
|
300
|
+
}
|
|
301
|
+
server.listen(PORT, HOST, () => {
|
|
302
|
+
console.log(`Deio Scroll listening on http://${HOST}:${PORT}`);
|
|
303
|
+
console.log(`output directory: ${OUTPUT_DIR}`);
|
|
304
|
+
console.log(hasEmbeddedWorker
|
|
305
|
+
? "capture worker: embedded"
|
|
306
|
+
: "capture worker: external (start pnpm dev:worker)");
|
|
307
|
+
});
|
|
308
|
+
const shutdown = () => {
|
|
309
|
+
server.close(() => {
|
|
310
|
+
void jobs.shutdown().finally(() => process.exit(0));
|
|
311
|
+
});
|
|
312
|
+
// SSE clients are intentionally long-lived and would otherwise keep close() pending.
|
|
313
|
+
server.closeAllConnections();
|
|
314
|
+
};
|
|
315
|
+
process.once("SIGINT", shutdown);
|
|
316
|
+
process.once("SIGTERM", shutdown);
|
|
317
|
+
function sendJson(res, status, payload) {
|
|
318
|
+
res.writeHead(status, {
|
|
319
|
+
"content-type": "application/json",
|
|
320
|
+
"cache-control": "no-store",
|
|
321
|
+
});
|
|
322
|
+
res.end(JSON.stringify(payload, null, 2));
|
|
323
|
+
}
|
|
324
|
+
async function serveFile(res, filePath, contentType) {
|
|
325
|
+
try {
|
|
326
|
+
const data = await fs.readFile(filePath);
|
|
327
|
+
res.writeHead(200, { "content-type": contentType });
|
|
328
|
+
res.end(data);
|
|
329
|
+
}
|
|
330
|
+
catch {
|
|
331
|
+
sendJson(res, 404, { ok: false, error: "Not found" });
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
function contentTypeFor(filePath) {
|
|
335
|
+
if (filePath.endsWith(".mp4"))
|
|
336
|
+
return "video/mp4";
|
|
337
|
+
if (filePath.endsWith(".html"))
|
|
338
|
+
return "text/html; charset=utf-8";
|
|
339
|
+
if (filePath.endsWith(".css"))
|
|
340
|
+
return "text/css; charset=utf-8";
|
|
341
|
+
if (filePath.endsWith(".js"))
|
|
342
|
+
return "text/javascript; charset=utf-8";
|
|
343
|
+
if (filePath.endsWith(".svg"))
|
|
344
|
+
return "image/svg+xml";
|
|
345
|
+
if (filePath.endsWith(".png"))
|
|
346
|
+
return "image/png";
|
|
347
|
+
if (filePath.endsWith(".jpg") || filePath.endsWith(".jpeg"))
|
|
348
|
+
return "image/jpeg";
|
|
349
|
+
if (filePath.endsWith(".ico"))
|
|
350
|
+
return "image/x-icon";
|
|
351
|
+
return "application/octet-stream";
|
|
352
|
+
}
|
|
353
|
+
function readJsonBody(req) {
|
|
354
|
+
return new Promise((resolve, reject) => {
|
|
355
|
+
const chunks = [];
|
|
356
|
+
let size = 0;
|
|
357
|
+
let tooLarge = false;
|
|
358
|
+
req.on("data", (chunk) => {
|
|
359
|
+
size += chunk.length;
|
|
360
|
+
if (size > 2_097_152) {
|
|
361
|
+
tooLarge = true;
|
|
362
|
+
chunks.length = 0;
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (!tooLarge)
|
|
366
|
+
chunks.push(chunk);
|
|
367
|
+
});
|
|
368
|
+
req.on("end", () => {
|
|
369
|
+
if (tooLarge)
|
|
370
|
+
return reject(new Error("JSON body cannot exceed 2 MB"));
|
|
371
|
+
try {
|
|
372
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
373
|
+
resolve(JSON.parse(raw));
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
reject(new Error("Invalid JSON body"));
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
req.on("error", reject);
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
function jobLinks(jobId) {
|
|
383
|
+
return {
|
|
384
|
+
ok: true,
|
|
385
|
+
jobId,
|
|
386
|
+
statusUrl: `/api/jobs/${jobId}`,
|
|
387
|
+
eventsUrl: `/api/jobs/${jobId}/events`,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function errorMessage(error) {
|
|
391
|
+
return error instanceof Error ? error.message : "Unknown error";
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type ArtifactKind = "output" | "source" | "thumbnail";
|
|
2
|
+
export interface StoredArtifact {
|
|
3
|
+
kind: ArtifactKind;
|
|
4
|
+
path: string;
|
|
5
|
+
sizeBytes: number;
|
|
6
|
+
createdAt: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ArtifactStore {
|
|
9
|
+
jobDirectory(jobId: string): string;
|
|
10
|
+
pathFor(jobId: string, kind: ArtifactKind): string;
|
|
11
|
+
inspect(jobId: string, kind: ArtifactKind): Promise<StoredArtifact | null>;
|
|
12
|
+
delete(jobId: string, kind: ArtifactKind): Promise<void>;
|
|
13
|
+
deleteJob(jobId: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare class LocalArtifactStore implements ArtifactStore {
|
|
16
|
+
readonly root: string;
|
|
17
|
+
constructor(root: string);
|
|
18
|
+
jobDirectory(jobId: string): string;
|
|
19
|
+
pathFor(jobId: string, kind: ArtifactKind): string;
|
|
20
|
+
inspect(jobId: string, kind: ArtifactKind): Promise<StoredArtifact | null>;
|
|
21
|
+
delete(jobId: string, kind: ArtifactKind): Promise<void>;
|
|
22
|
+
deleteJob(jobId: string): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/artifacts/store.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACpC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAAC;IACnD,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IAC3E,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC;AAQD,qBAAa,kBAAmB,YAAW,aAAa;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM;gBAAZ,IAAI,EAAE,MAAM;IAEjC,YAAY,CAAC,KAAK,EAAE,MAAM;IAK1B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY;IAInC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAY1E,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY;IAIxC,SAAS,CAAC,KAAK,EAAE,MAAM;CAG9B"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const FILENAMES = {
|
|
4
|
+
output: "output.mp4",
|
|
5
|
+
source: "source.mp4",
|
|
6
|
+
thumbnail: "thumbnail.jpg",
|
|
7
|
+
};
|
|
8
|
+
export class LocalArtifactStore {
|
|
9
|
+
root;
|
|
10
|
+
constructor(root) {
|
|
11
|
+
this.root = root;
|
|
12
|
+
}
|
|
13
|
+
jobDirectory(jobId) {
|
|
14
|
+
validateArtifactJobId(jobId);
|
|
15
|
+
return path.join(this.root, jobId);
|
|
16
|
+
}
|
|
17
|
+
pathFor(jobId, kind) {
|
|
18
|
+
return path.join(this.jobDirectory(jobId), FILENAMES[kind]);
|
|
19
|
+
}
|
|
20
|
+
async inspect(jobId, kind) {
|
|
21
|
+
const artifactPath = this.pathFor(jobId, kind);
|
|
22
|
+
const details = await fs.stat(artifactPath).catch(() => null);
|
|
23
|
+
if (!details?.isFile())
|
|
24
|
+
return null;
|
|
25
|
+
return {
|
|
26
|
+
kind,
|
|
27
|
+
path: artifactPath,
|
|
28
|
+
sizeBytes: details.size,
|
|
29
|
+
createdAt: details.birthtime.toISOString(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async delete(jobId, kind) {
|
|
33
|
+
await fs.rm(this.pathFor(jobId, kind), { force: true });
|
|
34
|
+
}
|
|
35
|
+
async deleteJob(jobId) {
|
|
36
|
+
await fs.rm(this.jobDirectory(jobId), { recursive: true, force: true });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function validateArtifactJobId(jobId) {
|
|
40
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(jobId))
|
|
41
|
+
throw new Error("Invalid jobId");
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/artifacts/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAmB7B,MAAM,SAAS,GAAiC;IAC9C,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,YAAY;IACpB,SAAS,EAAE,eAAe;CAC3B,CAAC;AAEF,MAAM,OAAO,kBAAkB;IACR;IAArB,YAAqB,IAAY;QAAZ,SAAI,GAAJ,IAAI,CAAQ;IAAG,CAAC;IAErC,YAAY,CAAC,KAAa;QACxB,qBAAqB,CAAC,KAAK,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrC,CAAC;IAED,OAAO,CAAC,KAAa,EAAE,IAAkB;QACvC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9D,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,KAAa,EAAE,IAAkB;QAC7C,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC/C,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAC9D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE;YAAE,OAAO,IAAI,CAAC;QACpC,OAAO;YACL,IAAI;YACJ,IAAI,EAAE,YAAY;YAClB,SAAS,EAAE,OAAO,CAAC,IAAI;YACvB,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,WAAW,EAAE;SAC3C,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAa,EAAE,IAAkB;QAC5C,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,KAAa;QAC3B,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1E,CAAC;CACF;AAED,SAAS,qBAAqB,CAAC,KAAa;IAC1C,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Page } from "playwright";
|
|
2
|
+
import type { ComponentInteraction, MotionBeat } from "../types.js";
|
|
3
|
+
export declare class ComponentInteractionAnimator {
|
|
4
|
+
private readonly page;
|
|
5
|
+
private activeBeat;
|
|
6
|
+
private activeSelector;
|
|
7
|
+
constructor(page: Page);
|
|
8
|
+
render(options: {
|
|
9
|
+
beatIndex: number;
|
|
10
|
+
selector: string;
|
|
11
|
+
interaction: ComponentInteraction;
|
|
12
|
+
progress: number;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
reset(): Promise<void>;
|
|
15
|
+
private prepare;
|
|
16
|
+
private activateOnce;
|
|
17
|
+
}
|
|
18
|
+
export declare function preflightComponentInteractions(page: Page, beats: MotionBeat[]): Promise<{
|
|
19
|
+
selector: string;
|
|
20
|
+
label: string;
|
|
21
|
+
recovered: boolean;
|
|
22
|
+
}[]>;
|
|
23
|
+
export declare function installInteractionNavigationGuards(page: Page): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=componentInteraction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"componentInteraction.d.ts","sourceRoot":"","sources":["../../src/browser/componentInteraction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AASpE,qBAAa,4BAA4B;IAI3B,OAAO,CAAC,QAAQ,CAAC,IAAI;IAHjC,OAAO,CAAC,UAAU,CAAM;IACxB,OAAO,CAAC,cAAc,CAAM;gBAEC,IAAI,EAAE,IAAI;IAEjC,MAAM,CAAC,OAAO,EAAE;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,oBAAoB,CAAC;QAClC,QAAQ,EAAE,MAAM,CAAC;KAClB;IAoEK,KAAK;YAqBG,OAAO;YA+DP,YAAY;CAkB3B;AAED,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,UAAU,EAAE;cAsFE,MAAM;WAAS,MAAM;eAAa,OAAO;KAvD/D;AAED,wBAAsB,kCAAkC,CAAC,IAAI,EAAE,IAAI,iBAmClE"}
|