Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:

strategy:
matrix:
node-version: [18.x]
node-version: [22.x]

steps:
- name: Checkout code
Expand All @@ -48,7 +48,7 @@ jobs:

strategy:
matrix:
node-version: [18.x]
node-version: [22.x]

steps:
- name: Checkout code
Expand Down
1 change: 1 addition & 0 deletions global.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/// <reference types="@webgpu/types" />
/// <reference types="wicg-file-system-access" />

interface FileSystemFileHandle {
remove(): Promise<void>;
Expand Down
936 changes: 366 additions & 570 deletions package-lock.json

Large diffs are not rendered by default.

19 changes: 11 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,19 @@
"develop:auto-launch": "concurrently --kill-others \"xdg-open 'http://localhost:3000/'\" \"npm run watch\" \"npm run serve\"",
"lint": "eslint src"
},
"engines": {
"node": ">=20.19.0"
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

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

The repo-level Node engine constraint (>=20.19.0) is looser than the strictest dependency engines in the lockfile (e.g. ESLint declares ^20.19.0 || ^22.13.0 || >=24, which explicitly excludes Node 21 and 23, and requires Node 22.13+ for the 22.x line). To avoid giving contributors a false sense of support, consider aligning package.json.engines.node to the strictest effective range (or at least excluding unsupported majors like 21/23).

Suggested change
"node": ">=20.19.0"
"node": "^20.19.0 || ^22.13.0 || >=24"

Copilot uses AI. Check for mistakes.
},
"devDependencies": {
"@playcanvas/eslint-config": "2.1.0",
"@playcanvas/splat-transform": "1.10.2",
"@playcanvas/pcui": "5.8.0",
"@playcanvas/splat-transform": "2.0.1",
"@playcanvas/pcui": "6.1.3",
"@rollup/plugin-alias": "6.0.0",
"@rollup/plugin-image": "3.0.3",
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-node-resolve": "16.0.3",
"@rollup/plugin-strip": "3.0.4",
"@rollup/plugin-terser": "0.4.4",
"@rollup/plugin-terser": "1.0.0",
"@rollup/plugin-typescript": "12.3.0",
"@types/wicg-file-system-access": "2023.10.7",
"@typescript-eslint/eslint-plugin": "8.59.0",
Expand All @@ -39,20 +42,20 @@
"concurrently": "9.2.1",
"cors": "2.8.6",
"cross-env": "10.1.0",
"eslint": "9.39.4",
"eslint": "10.2.1",
Comment thread
slimbuck marked this conversation as resolved.
"eslint-import-resolver-typescript": "4.4.4",
Comment thread
slimbuck marked this conversation as resolved.
"globals": "17.5.0",
"i18next": "25.10.10",
"i18next": "26.0.8",
"i18next-browser-languagedetector": "8.2.1",
"i18next-http-backend": "3.0.6",
"mediabunny": "1.40.1",
"mediabunny": "1.41.0",
"playcanvas": "2.18.0",
"postcss": "8.5.10",
"postcss": "8.5.12",
"rollup": "4.60.2",
"rollup-plugin-scss": "4.0.1",
"sass": "1.99.0",
"serve": "14.2.6",
"tslib": "2.8.1",
"typescript": "5.9.3"
"typescript": "6.0.3"
Comment thread
slimbuck marked this conversation as resolved.
}
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import './ui/scss/style.scss';
import { version as pcuiVersion, revision as pcuiRevision } from '@playcanvas/pcui';
import { version as stVersion, revision as stRevision } from '@playcanvas/splat-transform';
import { version as engineVersion, revision as engineRevision } from 'playcanvas';

import { main } from './main';
import { version as appVersion } from '../package.json';

// print out versions of dependent packages
// NOTE: add dummy style reference to prevent tree shaking
console.log(`SuperSplat v${appVersion} | PCUI v${pcuiVersion} (${pcuiRevision}) | Engine v${engineVersion} (${engineRevision})`);
console.log(`SuperSplat v${appVersion} | SplatTransform v${stVersion} (${stRevision}) | Engine v${engineVersion} (${engineRevision}) | PCUI v${pcuiVersion} (${pcuiRevision})`);

main();
8 changes: 8 additions & 0 deletions src/io/write/browser-file-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class BrowserFileWriter implements Writer {
this.ready = this.stream.seek(0);
}

get bytesWritten(): number {
return this.cursor;
}

async write(data: Uint8Array): Promise<void> {
await this.ready;
this.cursor += data.byteLength;
Expand Down Expand Up @@ -72,6 +76,10 @@ class BrowserDownloadWriter implements Writer {
this.innerWriter = this.memFs.createWriter(filename);
}

get bytesWritten(): number {
return this.innerWriter.bytesWritten;
}

write(data: Uint8Array): void {
this.innerWriter.write(data);
}
Expand Down
25 changes: 18 additions & 7 deletions src/io/write/writer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ class GZipWriter implements Writer {
write: (data: Uint8Array) => Promise<void>;
close: () => Promise<void>;

private cursor = 0;

get bytesWritten(): number {
return this.cursor;
}

constructor(writer: Writer) {
const stream = new CompressionStream('gzip');
const streamWriter = stream.writable.getWriter();
Expand All @@ -26,6 +32,7 @@ class GZipWriter implements Writer {
})();

this.write = async (data: Uint8Array) => {
this.cursor += data.byteLength;
await streamWriter.ready;
await streamWriter.write(data as unknown as ArrayBuffer);
};
Expand All @@ -47,20 +54,24 @@ class ProgressWriter implements Writer {
write: (data: Uint8Array) => Promise<void>;
close: () => void;

constructor(writer: Writer, totalBytes: number, progress?: (progress: number, total: number) => void) {
let cursor = 0;
private cursor = 0;

get bytesWritten(): number {
return this.cursor;
}

constructor(writer: Writer, totalBytes: number, progress?: (progress: number, total: number) => void) {
this.write = async (data: Uint8Array) => {
cursor += data.byteLength;
this.cursor += data.byteLength;
await writer.write(data);
progress?.(cursor, totalBytes);
progress?.(this.cursor, totalBytes);
};

this.close = () => {
if (cursor !== totalBytes) {
throw new Error(`ProgressWriter: expected ${totalBytes} bytes, but wrote ${cursor} bytes`);
if (this.cursor !== totalBytes) {
throw new Error(`ProgressWriter: expected ${totalBytes} bytes, but wrote ${this.cursor} bytes`);
}
progress?.(cursor, totalBytes);
progress?.(this.cursor, totalBytes);
};
}
}
Expand Down
12 changes: 6 additions & 6 deletions src/picker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ class Picker {
const { splatLayer } = this.scene;

// Hide non-selected elements
const splats = this.scene.getElementsByType(ElementType.splat);
splats.forEach((s: Splat) => {
const splats = this.scene.getElementsByType(ElementType.splat) as Splat[];
splats.forEach((s) => {
s.entity.enabled = s === splat;
});

Expand All @@ -116,7 +116,7 @@ class Picker {
this.renderPass.render();

// Re-enable all splats
splats.forEach((s: Splat) => {
splats.forEach((s) => {
s.entity.enabled = true;
});
}
Expand Down Expand Up @@ -181,8 +181,8 @@ class Picker {
const emptyMap = new Map();

// Hide non-selected elements
const splats = scene.getElementsByType(ElementType.splat);
splats.forEach((s: Splat) => {
const splats = scene.getElementsByType(ElementType.splat) as Splat[];
splats.forEach((s) => {
s.entity.enabled = s === splat;
});

Expand All @@ -198,7 +198,7 @@ class Picker {
this.renderPass.render();

// Re-enable all splats
splats.forEach((s: Splat) => {
splats.forEach((s) => {
s.entity.enabled = true;
});
}
Expand Down
13 changes: 12 additions & 1 deletion src/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ class WriterFileSystem implements FileSystem {
createWriter(_filename: string): Writer {
// Return a wrapper that delegates write but makes close a no-op
// The caller is responsible for closing the underlying writer
const inner = this.writer;
return {
write: (data: Uint8Array) => this.writer.write(data),
get bytesWritten() {
return inner.bytesWritten;
},
write: (data: Uint8Array) => inner.write(data),
close: () => Promise.resolve()
};
}
Expand Down Expand Up @@ -124,6 +128,12 @@ class PublishWriter implements Writer {
write: (data: Uint8Array) => void;
close: () => Promise<any>;

private cursor = 0;

get bytesWritten(): number {
return this.cursor;
}

static async create(publishSettings: PublishSettings) {
const { user } = publishSettings;

Expand Down Expand Up @@ -210,6 +220,7 @@ class PublishWriter implements Writer {
await upload();
}
}
result.cursor += data.byteLength;
};

result.close = async () => {
Expand Down
Loading