multiplane 0.0.0-stage → 0.3.1
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 +202 -0
- package/README.en.md +324 -0
- package/README.md +323 -2
- package/THIRD_PARTY_NOTICES.md +30 -0
- package/bin/multiplane +98 -0
- package/examples/alpha.html +18 -0
- package/examples/animation.html +17 -0
- package/examples/assets/OFL.txt +93 -0
- package/examples/assets/Roboto-Variable.ttf +0 -0
- package/examples/assets/card.svg +6 -0
- package/examples/assets/readme-card-0.svg +1 -0
- package/examples/assets/readme-card-1.svg +1 -0
- package/examples/assets/readme-card-2.svg +1 -0
- package/examples/assets/readme-detail.svg +1 -0
- package/examples/readme-cards.html +23 -0
- package/examples/readme-cut.html +13 -0
- package/examples/readme-depth.html +15 -0
- package/examples/readme-wheel.html +14 -0
- package/examples/three-cards.html +17 -0
- package/examples/typography.html +17 -0
- package/licenses/GPL-3.0.txt +232 -0
- package/licenses/LGPL-3.0.txt +304 -0
- package/licenses/acorn-LICENSE +21 -0
- package/licenses/colour-LICENSE.md +82 -0
- package/licenses/detect-libc-LICENSE +201 -0
- package/licenses/entities-LICENSE +11 -0
- package/licenses/nanoid-LICENSE +20 -0
- package/licenses/native/README.ijg +258 -0
- package/licenses/native/aom-LICENSE +27 -0
- package/licenses/native/aom-PATENTS +108 -0
- package/licenses/native/archive-COPYING +65 -0
- package/licenses/native/cairo-COPYING +33 -0
- package/licenses/native/cairo-COPYING-MPL-1.1 +470 -0
- package/licenses/native/cgif-LICENSE +21 -0
- package/licenses/native/exif-COPYING +504 -0
- package/licenses/native/expat-COPYING +21 -0
- package/licenses/native/ffi-LICENSE +21 -0
- package/licenses/native/fontconfig-COPYING +200 -0
- package/licenses/native/freetype-FTL.TXT +169 -0
- package/licenses/native/fribidi-COPYING +501 -0
- package/licenses/native/glib-COPYING +175 -0
- package/licenses/native/harfbuzz-COPYING +42 -0
- package/licenses/native/heif-COPYING +871 -0
- package/licenses/native/highway-LICENSE +371 -0
- package/licenses/native/imagequant-COPYRIGHT +36 -0
- package/licenses/native/lcms-COPYING +22 -0
- package/licenses/native/mozjpeg-LICENSE.md +135 -0
- package/licenses/native/nsgif-COPYING +21 -0
- package/licenses/native/pango-COPYING +482 -0
- package/licenses/native/pixman-COPYING +42 -0
- package/licenses/native/png-LICENSE +134 -0
- package/licenses/native/proxy-libintl-COPYING +482 -0
- package/licenses/native/rsvg-COPYING.LIB +502 -0
- package/licenses/native/sources.json +240 -0
- package/licenses/native/tiff-LICENSE.md +49 -0
- package/licenses/native/uhdr-LICENSE +230 -0
- package/licenses/native/vips-COPYING +502 -0
- package/licenses/native/webp-COPYING +30 -0
- package/licenses/native/webp-PATENTS +23 -0
- package/licenses/native/xml2-Copyright +24 -0
- package/licenses/native/zlib-ng-LICENSE.md +19 -0
- package/licenses/parse5-LICENSE +19 -0
- package/licenses/picocolors-LICENSE +15 -0
- package/licenses/playwright-LICENSE +202 -0
- package/licenses/playwright-NOTICE +5 -0
- package/licenses/playwright-ThirdPartyNotices.txt +14 -0
- package/licenses/playwright-core-ThirdPartyNotices.txt +13 -0
- package/licenses/postcss-LICENSE +20 -0
- package/licenses/postcss-value-parser-LICENSE +22 -0
- package/licenses/semver-LICENSE +15 -0
- package/licenses/sharp-LICENSE +191 -0
- package/licenses/sharp-win32-x64-LICENSE +191 -0
- package/licenses/sharp-win32-x64-NOTICES.md +59 -0
- package/licenses/source-map-js-LICENSE +28 -0
- package/licenses/three-LICENSE +21 -0
- package/package.json +92 -6
- package/skills/multiplane/SKILL.md +213 -0
- package/src/alpha.mjs +69 -0
- package/src/artifacts.mjs +61 -0
- package/src/benchmark.mjs +140 -0
- package/src/browser-recovery.mjs +26 -0
- package/src/capture.mjs +232 -0
- package/src/config.mjs +79 -0
- package/src/doctor.mjs +56 -0
- package/src/draft.mjs +30 -0
- package/src/encoder.mjs +16 -0
- package/src/environment.mjs +56 -0
- package/src/errors.mjs +51 -0
- package/src/events.mjs +252 -0
- package/src/ffmpeg.mjs +32 -0
- package/src/format.mjs +27 -0
- package/src/frame-rate.mjs +28 -0
- package/src/home-migration.mjs +54 -0
- package/src/hosted.mjs +134 -0
- package/src/index.mjs +14 -0
- package/src/inspect.mjs +20 -0
- package/src/jobs.mjs +51 -0
- package/src/lint.mjs +248 -0
- package/src/output-alpha.mjs +36 -0
- package/src/output-check.mjs +110 -0
- package/src/pixel-comparison.mjs +43 -0
- package/src/platform.mjs +114 -0
- package/src/png-native.mjs +12 -0
- package/src/png.mjs +74 -0
- package/src/post.mjs +433 -0
- package/src/preview.mjs +23 -0
- package/src/process-identity.mjs +17 -0
- package/src/process.mjs +9 -0
- package/src/render-pool.mjs +93 -0
- package/src/render-worker.mjs +59 -0
- package/src/render.mjs +157 -0
- package/src/resource-path.mjs +29 -0
- package/src/resources.mjs +27 -0
- package/src/runtime/camera.js +37 -0
- package/src/runtime/shot.css +9 -0
- package/src/runtime/shot.js +71 -0
- package/src/serve.mjs +23 -0
- package/src/server.mjs +93 -0
- package/src/setup.mjs +51 -0
- package/src/skills.mjs +128 -0
- package/src/static-reuse.mjs +267 -0
- package/src/stills.mjs +81 -0
- package/src/three/index.js +348 -0
- package/src/user-home.mjs +105 -0
- package/src/verify.mjs +161 -0
- package/src/video-pipe.mjs +6 -0
- package/src/workers.mjs +62 -0
- package/tools/bench/shots/canvas-2d.html +1 -0
- package/tools/bench/shots/dom-light.html +1 -0
- package/tools/bench/shots/scene.js +45 -0
- package/tools/bench/shots/webgl.html +1 -0
- package/tools/install-browser.mjs +18 -0
package/src/workers.mjs
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { cpus, availableParallelism, freemem, totalmem, release, arch } from 'node:os';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { userHome } from './platform.mjs';
|
|
5
|
+
import { defaultWorkers } from './render-pool.mjs';
|
|
6
|
+
|
|
7
|
+
const packageVersion = JSON.parse(readFileSync(new URL('../package.json', import.meta.url))).version;
|
|
8
|
+
export const shotTypes = ['dom', 'canvas', 'three'];
|
|
9
|
+
|
|
10
|
+
export function memoryWorkers(scale = 1, hardware = {}) {
|
|
11
|
+
const free = Math.min(hardware.free ?? freemem(), hardware.total ?? totalmem());
|
|
12
|
+
return Math.max(0, Math.floor((free - 2 * 1024 ** 3) / ((0.25 + 0.1875 * scale * scale) * 1024 ** 3)));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function benchmarkMachine(info, raster = 'gpu') {
|
|
16
|
+
return { cpu: cpus().map(cpu => cpu.model), cores: availableParallelism(), memory: totalmem(),
|
|
17
|
+
gpu: info.gpu, os: { platform: process.platform, release: release(), arch: arch() },
|
|
18
|
+
chromium: info.chromium, version: packageVersion, raster };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function readBenchmark(home = userHome()) {
|
|
22
|
+
try { return JSON.parse(readFileSync(path.join(home, 'benchmark.json'), 'utf8')); }
|
|
23
|
+
catch { return null; }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function equal(a, b) {
|
|
27
|
+
if (a === b) return true;
|
|
28
|
+
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') return false;
|
|
29
|
+
const keys = Object.keys(a);
|
|
30
|
+
return keys.length === Object.keys(b).length && keys.every(key => Object.hasOwn(b, key) && equal(a[key], b[key]));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function validBenchmark(record, machine) {
|
|
34
|
+
return record?.schema === 'multiplane/1' && equal(record.machine, machine)
|
|
35
|
+
&& shotTypes.every(type => Number.isSafeInteger(record.shots?.[type]?.workers) && record.shots[type].workers > 0
|
|
36
|
+
&& Number.isFinite(record.shots[type].fps) && record.shots[type].fps > 0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function selectWorkers({ workers, source, record, machine, type, scale = 1, memoryScale = scale, hardware = {} }) {
|
|
40
|
+
if (workers !== undefined && ['option', 'config'].includes(source)) return { workers, source };
|
|
41
|
+
if (validBenchmark(record, machine) && shotTypes.includes(type)) {
|
|
42
|
+
return { workers: Math.max(1, Math.min(record.shots[type].workers, memoryWorkers(memoryScale, hardware))), source: 'benchmark' };
|
|
43
|
+
}
|
|
44
|
+
return { workers: defaultWorkers(scale, hardware), source: 'estimate' };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Inspect the loaded probe only; its context is discarded before real rendering.
|
|
48
|
+
export async function classifyShot(capture) {
|
|
49
|
+
return capture.page.evaluate(() => {
|
|
50
|
+
const canvases = [];
|
|
51
|
+
const visit = root => {
|
|
52
|
+
canvases.push(...root.querySelectorAll('canvas'));
|
|
53
|
+
for (const element of root.querySelectorAll('*')) if (element.shadowRoot) visit(element.shadowRoot);
|
|
54
|
+
};
|
|
55
|
+
visit(document);
|
|
56
|
+
if (canvases.some(canvas => {
|
|
57
|
+
try { return !canvas.getContext('2d') && !!(canvas.getContext('webgl2') || canvas.getContext('webgl') || canvas.getContext('experimental-webgl')); }
|
|
58
|
+
catch { return false; }
|
|
59
|
+
})) return 'three';
|
|
60
|
+
return canvases.length ? 'canvas' : 'dom';
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!doctype html><html><head><meta charset="utf-8"><link rel="icon" href="data:,"><title>canvas-2d</title><style>@font-face{font-family:Bench;src:url('../../../examples/assets/Roboto-Variable.ttf')}html,body{margin:0}#stage{width:1920px;height:1080px;position:relative;overflow:hidden;background:#18202c}canvas{position:absolute;inset:0;width:1920px;height:1080px}.tile{position:absolute;color:#fff;background:#375575;font:16px Bench;padding:4px;box-shadow:2px 3px 5px #0008}</style></head><body><div id="stage" data-kind="canvas-2d"></div><script src="scene.js"></script></body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!doctype html><html><head><meta charset="utf-8"><link rel="icon" href="data:,"><title>dom-light</title><style>@font-face{font-family:Bench;src:url('../../../examples/assets/Roboto-Variable.ttf')}html,body{margin:0}#stage{width:1920px;height:1080px;position:relative;overflow:hidden;background:#18202c}canvas{position:absolute;inset:0;width:1920px;height:1080px}.tile{position:absolute;color:#fff;background:#375575;font:16px Bench;padding:4px;box-shadow:2px 3px 5px #0008}</style></head><body><div id="stage" data-kind="dom-light"></div><script src="scene.js"></script></body></html>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const stage = document.querySelector('#stage');
|
|
2
|
+
const kind = stage.dataset.kind;
|
|
3
|
+
const tiles = [];
|
|
4
|
+
if (kind.startsWith('dom') || kind === 'mixed') {
|
|
5
|
+
const count = kind === 'dom-heavy' ? 1200 : 12;
|
|
6
|
+
for (let i = 0; i < count; i++) {
|
|
7
|
+
const tile = document.createElement('div');
|
|
8
|
+
tile.className = 'tile'; tile.textContent = `Item ${i}`;
|
|
9
|
+
tile.style.left = `${(i % 40) * 48}px`; tile.style.top = `${Math.floor(i / 40) * 35}px`;
|
|
10
|
+
if (kind === 'dom-heavy') tile.style.filter = `blur(${i % 3 * 0.15}px)`;
|
|
11
|
+
stage.append(tile); tiles.push(tile);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
let canvas, ctx, gl, program, timeLocation;
|
|
15
|
+
if (['canvas-2d', 'webgl', 'mixed'].includes(kind)) {
|
|
16
|
+
canvas = document.createElement('canvas'); stage.append(canvas);
|
|
17
|
+
canvas.width = 1920 * devicePixelRatio; canvas.height = 1080 * devicePixelRatio;
|
|
18
|
+
if (kind === 'canvas-2d') ctx = canvas.getContext('2d', { alpha: false, colorSpace: 'srgb' });
|
|
19
|
+
else {
|
|
20
|
+
gl = canvas.getContext('webgl', { alpha: kind === 'mixed', premultipliedAlpha: true, antialias: true, preserveDrawingBuffer: true });
|
|
21
|
+
if (!gl) throw Error('WebGL unavailable');
|
|
22
|
+
const shader = (type, source) => { const s = gl.createShader(type); gl.shaderSource(s, source); gl.compileShader(s); if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw Error(gl.getShaderInfoLog(s)); return s; };
|
|
23
|
+
program = gl.createProgram();
|
|
24
|
+
gl.attachShader(program, shader(gl.VERTEX_SHADER, 'attribute vec2 p;void main(){gl_Position=vec4(p,0.,1.);}'));
|
|
25
|
+
gl.attachShader(program, shader(gl.FRAGMENT_SHADER, `precision highp float;uniform float t;uniform vec2 size;void main(){vec2 p=gl_FragCoord.xy/size;float stripe=step(.5,fract((p.x+p.y)*12.+t));gl_FragColor=vec4(.15+.55*p.x,.2+.5*p.y,.25+.4*stripe,${kind === 'mixed' ? '.5' : '1.'});}`));
|
|
26
|
+
gl.linkProgram(program); if (!gl.getProgramParameter(program, gl.LINK_STATUS)) throw Error(gl.getProgramInfoLog(program));
|
|
27
|
+
gl.useProgram(program); const b = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,b); gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),gl.STATIC_DRAW);
|
|
28
|
+
const loc=gl.getAttribLocation(program,'p');gl.enableVertexAttribArray(loc);gl.vertexAttribPointer(loc,2,gl.FLOAT,false,0,0);
|
|
29
|
+
gl.uniform2f(gl.getUniformLocation(program,'size'),canvas.width,canvas.height);timeLocation=gl.getUniformLocation(program,'t');gl.viewport(0,0,canvas.width,canvas.height);
|
|
30
|
+
window.BENCH_GL = gl;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
window.SHOT = {
|
|
34
|
+
duration: 2, fps: 30, ready: document.fonts.load('16px Bench'),
|
|
35
|
+
render(t) {
|
|
36
|
+
for (let i=0;i<tiles.length;i++) tiles[i].style.transform=`translate(${Math.sin(t*3+i)*20}px,${Math.cos(t*2+i)*10}px)`;
|
|
37
|
+
if(ctx){
|
|
38
|
+
// Capture can temporarily change DPR; use the fixed backing-store dimensions.
|
|
39
|
+
ctx.setTransform(canvas.width/1920,0,0,canvas.height/1080,0,0);ctx.fillStyle='#18202c';ctx.fillRect(0,0,1920,1080);
|
|
40
|
+
for(let i=0;i<160;i++){ctx.fillStyle=`hsl(${i*17%360} 60% 60%)`;ctx.beginPath();ctx.arc((i%20)*96+48+Math.sin(t*3+i)*20,Math.floor(i/20)*130+65,28+i%9,0,Math.PI*2);ctx.fill();}
|
|
41
|
+
ctx.fillStyle='#ffffff80';ctx.fillRect(300+t*100,250,600,400);
|
|
42
|
+
}
|
|
43
|
+
if(gl){gl.uniform1f(timeLocation,t);gl.drawArrays(gl.TRIANGLES,0,6);gl.finish();}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!doctype html><html><head><meta charset="utf-8"><link rel="icon" href="data:,"><title>webgl</title><style>@font-face{font-family:Bench;src:url('../../../examples/assets/Roboto-Variable.ttf')}html,body{margin:0}#stage{width:1920px;height:1080px;position:relative;overflow:hidden;background:#18202c}canvas{position:absolute;inset:0;width:1920px;height:1080px}.tile{position:absolute;color:#fff;background:#375575;font:16px Bench;padding:4px;box-shadow:2px 3px 5px #0008}</style></head><body><div id="stage" data-kind="webgl"></div><script src="scene.js"></script></body></html>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
|
|
6
|
+
if (!process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD) {
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
const playwrightRequire = createRequire(require.resolve('playwright/package.json'));
|
|
9
|
+
// Avoid taking Playwright's installation lock when both pinned browsers already exist.
|
|
10
|
+
const { registry: { registry } } = playwrightRequire('playwright-core/lib/coreBundle');
|
|
11
|
+
const installed = ['chromium', 'chromium-headless-shell'].every(name => existsSync(registry.findExecutable(name).executablePath()));
|
|
12
|
+
if (installed) process.exit(0);
|
|
13
|
+
const result = spawnSync(process.execPath, [path.join(path.dirname(require.resolve('playwright/package.json')), 'cli.js'), 'install', 'chromium'],
|
|
14
|
+
{ stdio: 'inherit', windowsHide: true });
|
|
15
|
+
if (result.error) throw result.error;
|
|
16
|
+
if (process.platform === 'linux') console.error('If Chromium cannot launch because system libraries are missing, run npx playwright install-deps chromium.');
|
|
17
|
+
process.exitCode = result.status ?? 1;
|
|
18
|
+
}
|