From d96ccca803a438fbf93fc90ffd3a598a58196685 Mon Sep 17 00:00:00 2001 From: Aymeric Rabot Date: Fri, 11 Sep 2026 12:28:57 -0400 Subject: [PATCH] refactor(packages): fold capture packages into core and viewer Avoid npm package sprawl before 1.0.0: `@pascal-app/capture-protocol` becomes the `@pascal-app/core/capture` subpath and `@pascal-app/capture-viewer` becomes `@pascal-app/viewer/capture` (plus `@pascal-app/viewer/capture/preview`), so the release ships seven packages: core, viewer, editor, nodes, mcp, ifc-converter, cli. Neither package was ever published to npm, so no npm consumer migrates. The protocol code is pure zod/TS, so core keeps its no-Three.js layer rule; the runtime and its reference layers keep viewer's existing peers and now reach viewer internals through relative imports instead of a self-referential `@pascal-app/viewer` specifier. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/release.yml | 50 ++----------- AGENTS.md | 4 +- CHANGELOG.md | 1 + README.md | 16 ++--- bun.lock | 50 +------------ packages/capture-protocol/LICENSE | 21 ------ packages/capture-protocol/README.md | 30 -------- packages/capture-protocol/package.json | 51 ------------- packages/capture-protocol/tsconfig.json | 13 ---- packages/capture-viewer/LICENSE | 21 ------ packages/capture-viewer/README.md | 50 ------------- packages/capture-viewer/bunfig.toml | 4 -- packages/capture-viewer/package.json | 71 ------------------- packages/capture-viewer/tsconfig.json | 14 ---- packages/core/README.md | 23 ++++++ packages/core/package.json | 6 +- .../src => core/src/capture}/index.ts | 0 .../src => core/src/capture}/schema.test.ts | 0 .../src => core/src/capture}/schema.ts | 0 .../src => core/src/capture}/source.test.ts | 0 .../src => core/src/capture}/source.ts | 0 packages/core/src/schema/nodes/scan.ts | 2 +- packages/core/tsconfig.json | 3 +- packages/viewer/README.md | 55 ++++++++++++++ packages/viewer/package.json | 10 +++ .../src => viewer/src/capture}/asset-url.ts | 0 .../src/capture}/capture-runtime.tsx | 8 ++- .../src => viewer/src/capture}/frame.test.ts | 2 +- .../src => viewer/src/capture}/frame.ts | 2 +- .../src => viewer/src/capture}/index.ts | 0 .../src/capture}/layer-visibility.test.ts | 0 .../src/capture}/layer-visibility.ts | 4 +- .../src/capture}/layers/clay-matcap.ts | 0 .../capture}/layers/device-motion-layer.tsx | 0 .../src/capture}/layers/point-cloud-layer.tsx | 2 +- .../src/capture}/layers/room-model-layer.tsx | 2 +- .../src/capture}/layers/surface-mesh-data.ts | 2 +- .../capture}/layers/surface-mesh-layer.tsx | 0 .../src/capture}/point-cloud-layer.test.ts | 2 +- .../src => viewer/src/capture}/preview.ts | 0 .../src/capture}/source-state.test.ts | 2 +- .../src/capture}/source-state.ts | 2 +- .../src/capture}/stream-rendering.test.ts | 0 .../src/capture}/stream-rendering.ts | 2 +- .../src/capture}/surface-mesh-layer.test.ts | 0 .../src/capture}/trajectory.test.ts | 0 .../src => viewer/src/capture}/trajectory.ts | 2 +- wiki/architecture/capture-runtime.md | 21 +++--- 48 files changed, 141 insertions(+), 407 deletions(-) delete mode 100644 packages/capture-protocol/LICENSE delete mode 100644 packages/capture-protocol/README.md delete mode 100644 packages/capture-protocol/package.json delete mode 100644 packages/capture-protocol/tsconfig.json delete mode 100644 packages/capture-viewer/LICENSE delete mode 100644 packages/capture-viewer/README.md delete mode 100644 packages/capture-viewer/bunfig.toml delete mode 100644 packages/capture-viewer/package.json delete mode 100644 packages/capture-viewer/tsconfig.json rename packages/{capture-protocol/src => core/src/capture}/index.ts (100%) rename packages/{capture-protocol/src => core/src/capture}/schema.test.ts (100%) rename packages/{capture-protocol/src => core/src/capture}/schema.ts (100%) rename packages/{capture-protocol/src => core/src/capture}/source.test.ts (100%) rename packages/{capture-protocol/src => core/src/capture}/source.ts (100%) rename packages/{capture-viewer/src => viewer/src/capture}/asset-url.ts (100%) rename packages/{capture-viewer/src => viewer/src/capture}/capture-runtime.tsx (98%) rename packages/{capture-viewer/src => viewer/src/capture}/frame.test.ts (94%) rename packages/{capture-viewer/src => viewer/src/capture}/frame.ts (90%) rename packages/{capture-viewer/src => viewer/src/capture}/index.ts (100%) rename packages/{capture-viewer/src => viewer/src/capture}/layer-visibility.test.ts (100%) rename packages/{capture-viewer/src => viewer/src/capture}/layer-visibility.ts (84%) rename packages/{capture-viewer/src => viewer/src/capture}/layers/clay-matcap.ts (100%) rename packages/{capture-viewer/src => viewer/src/capture}/layers/device-motion-layer.tsx (100%) rename packages/{capture-viewer/src => viewer/src/capture}/layers/point-cloud-layer.tsx (98%) rename packages/{capture-viewer/src => viewer/src/capture}/layers/room-model-layer.tsx (98%) rename packages/{capture-viewer/src => viewer/src/capture}/layers/surface-mesh-data.ts (97%) rename packages/{capture-viewer/src => viewer/src/capture}/layers/surface-mesh-layer.tsx (100%) rename packages/{capture-viewer/src => viewer/src/capture}/point-cloud-layer.test.ts (94%) rename packages/{capture-viewer/src => viewer/src/capture}/preview.ts (100%) rename packages/{capture-viewer/src => viewer/src/capture}/source-state.test.ts (98%) rename packages/{capture-viewer/src => viewer/src/capture}/source-state.ts (99%) rename packages/{capture-viewer/src => viewer/src/capture}/stream-rendering.test.ts (100%) rename packages/{capture-viewer/src => viewer/src/capture}/stream-rendering.ts (98%) rename packages/{capture-viewer/src => viewer/src/capture}/surface-mesh-layer.test.ts (100%) rename packages/{capture-viewer/src => viewer/src/capture}/trajectory.test.ts (100%) rename packages/{capture-viewer/src => viewer/src/capture}/trajectory.ts (98%) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 001f446825..b18f92f5a1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,8 +8,6 @@ on: required: true type: choice options: - - capture-protocol - - capture-viewer - core - viewer - editor @@ -150,7 +148,7 @@ jobs: # peerDeps sync below must use shell vars, not env indirection. declare -A NEW_VERSIONS - for pkg in capture-protocol core viewer capture-viewer editor nodes mcp ifc-converter cli; do + for pkg in core viewer editor nodes mcp ifc-converter cli; do if [ "$TARGET" = "$pkg" ] || [ "$TARGET" = "all" ]; then CUR=$(jq -r '.version' packages/$pkg/package.json) NEW=$(bump_version "$CUR") @@ -165,9 +163,9 @@ jobs: # Sync inter-package references in dependencies, peerDependencies, and devDependencies. # Anything that references a bumped @pascal-app/* package is updated to ^NEW. - for pkg in capture-protocol core viewer capture-viewer editor nodes mcp ifc-converter cli; do + for pkg in core viewer editor nodes mcp ifc-converter cli; do FILE=packages/$pkg/package.json - for dep in capture-protocol core viewer capture-viewer editor nodes mcp ifc-converter cli; do + for dep in core viewer editor nodes mcp ifc-converter cli; do VAL="${NEW_VERSIONS[$dep]}" [ -z "$VAL" ] && continue jq --arg name "@pascal-app/$dep" --arg v "^$VAL" ' @@ -179,7 +177,7 @@ jobs: done echo "=== @pascal-app/* refs after sync ===" - for pkg in capture-protocol core viewer capture-viewer editor nodes mcp ifc-converter cli; do + for pkg in core viewer editor nodes mcp ifc-converter cli; do echo "--- packages/$pkg/package.json ---" jq '{ dependencies: (.dependencies // {} | with_entries(select(.key | startswith("@pascal-app/")))), peerDependencies: (.peerDependencies // {} | with_entries(select(.key | startswith("@pascal-app/")))), devDependencies: (.devDependencies // {} | with_entries(select(.key | startswith("@pascal-app/")))) }' packages/$pkg/package.json done @@ -191,7 +189,7 @@ jobs: # A single-package release may depend on another package introduced # by this monorepo. Refuse to publish an uninstallable package when # that dependency is not part of this run and is absent from npm. - RELEASE_PACKAGES="capture-protocol core viewer capture-viewer editor nodes mcp ifc-converter cli" + RELEASE_PACKAGES="core viewer editor nodes mcp ifc-converter cli" if [ "$TARGET" != "all" ]; then FILE="packages/$TARGET/package.json" while IFS=$'\t' read -r DEP RANGE; do @@ -217,21 +215,6 @@ jobs: ) fi - - name: Build & publish capture protocol - if: inputs.package == 'capture-protocol' || inputs.package == 'all' - working-directory: packages/capture-protocol - run: | - bun run build - if [ "${{ inputs.dry-run }}" = "true" ]; then - echo "🏜️ Dry run — would publish @pascal-app/capture-protocol@$CAPTURE_PROTOCOL_VERSION" - npm publish --dry-run --access public --tag "$NPM_TAG" - elif npm view "@pascal-app/capture-protocol@$CAPTURE_PROTOCOL_VERSION" version >/dev/null 2>&1; then - echo "📦 @pascal-app/capture-protocol@$CAPTURE_PROTOCOL_VERSION is already published; continuing release recovery" - else - npm publish --access public --tag "$NPM_TAG" - echo "📦 Published @pascal-app/capture-protocol@$CAPTURE_PROTOCOL_VERSION" - fi - - name: Validate portable editor runtime if: inputs.package == 'cli' || inputs.package == 'all' env: @@ -273,21 +256,6 @@ jobs: echo "📦 Published @pascal-app/viewer@$VIEWER_VERSION" fi - - name: Build & publish capture viewer - if: inputs.package == 'capture-viewer' || inputs.package == 'all' - working-directory: packages/capture-viewer - run: | - bun run build - if [ "${{ inputs.dry-run }}" = "true" ]; then - echo "🏜️ Dry run — would publish @pascal-app/capture-viewer@$CAPTURE_VIEWER_VERSION" - npm publish --dry-run --access public --tag "$NPM_TAG" - elif npm view "@pascal-app/capture-viewer@$CAPTURE_VIEWER_VERSION" version >/dev/null 2>&1; then - echo "📦 @pascal-app/capture-viewer@$CAPTURE_VIEWER_VERSION is already published; continuing release recovery" - else - npm publish --access public --tag "$NPM_TAG" - echo "📦 Published @pascal-app/capture-viewer@$CAPTURE_VIEWER_VERSION" - fi - - name: Publish editor if: inputs.package == 'editor' || inputs.package == 'all' working-directory: packages/editor @@ -378,10 +346,6 @@ jobs: PKGS="" TAGS="" - if [ -n "$CAPTURE_PROTOCOL_VERSION" ]; then - PKGS="$PKGS @pascal-app/capture-protocol@$CAPTURE_PROTOCOL_VERSION" - TAGS="$TAGS @pascal-app/capture-protocol@$CAPTURE_PROTOCOL_VERSION" - fi if [ -n "$CORE_VERSION" ]; then PKGS="$PKGS @pascal-app/core@$CORE_VERSION" TAGS="$TAGS @pascal-app/core@$CORE_VERSION" @@ -390,10 +354,6 @@ jobs: PKGS="$PKGS @pascal-app/viewer@$VIEWER_VERSION" TAGS="$TAGS @pascal-app/viewer@$VIEWER_VERSION" fi - if [ -n "$CAPTURE_VIEWER_VERSION" ]; then - PKGS="$PKGS @pascal-app/capture-viewer@$CAPTURE_VIEWER_VERSION" - TAGS="$TAGS @pascal-app/capture-viewer@$CAPTURE_VIEWER_VERSION" - fi if [ -n "$EDITOR_VERSION" ]; then PKGS="$PKGS @pascal-app/editor@$EDITOR_VERSION" TAGS="$TAGS @pascal-app/editor@$EDITOR_VERSION" diff --git a/AGENTS.md b/AGENTS.md index 9ddb52fb38..5329655586 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,8 +6,8 @@ Public, open-source home of `@pascal-app/{core,viewer,editor,mcp}` and the stand | Path | Purpose | |---|---| -| `packages/core` | Scene graph, node schemas, stores, event bus, core systems — pure logic, no Three.js | -| `packages/viewer` | Standalone 3D canvas: renderers, viewer systems, presentation state | +| `packages/core` | Scene graph, node schemas, stores, event bus, core systems — pure logic, no Three.js. `src/capture/` holds the capture-session contracts published as `@pascal-app/core/capture` | +| `packages/viewer` | Standalone 3D canvas: renderers, viewer systems, presentation state. `src/capture/` holds the capture runtime and reference layers published as `@pascal-app/viewer/capture` | | `packages/editor` | Editor UI components reused by the standalone app and embedders | | `packages/mcp` | MCP server and scene storage adapters | | `apps/editor` | Standalone editor app — composes `viewer` + `editor` + tools | diff --git a/CHANGELOG.md b/CHANGELOG.md index da75586262..c0c70fb203 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - **`pascal agent claim` and `pascal agent status`** — an agent can open a prefilled 15-minute human handoff and verify its hosted key without storing or printing it, available in the verified GitHub CLI preview ([#815](https://github.com/pascalorg/editor/pull/815), [#818](https://github.com/pascalorg/editor/pull/818), [#819](https://github.com/pascalorg/editor/pull/819), [#821](https://github.com/pascalorg/editor/pull/821), [#822](https://github.com/pascalorg/editor/pull/822)) - **Small CLI, downloaded web runtime** — `@pascal-app/cli` now installs from a 0.5 MB npm package instead of 65 MB: the MCP service ships inside it, so `pascal mcp connect` works with no editor process and no download, while the web editor runtime is fetched once per version from its release asset, verified against a SHA-256 digest published in the package, and installed atomically; `--runtime ` covers offline hosts, `HTTPS_PROXY`/`NO_PROXY` are honoured, and an editor started by a pre-split CLI keeps its own MCP child, which must be stopped once by hand after upgrading (#845) - **Optional hosted key in the Cursor plugin** — `.cursor-plugin/plugin.json` declares an optional `PASCAL_API_KEY` variable and points at a Cursor-dialect `.cursor-plugin/mcp.json` that adds a `pascal-hosted` server for `https://editor.pascal.app/api/mcp`, so a Cursor install can reach hosted projects, Capture scans, and shared workspaces while the credential-free local server keeps working; the portable `mcp.json` stays credential-free because Agent Plugins 1.0.0 forbids secrets and placeholder expansion in `headers`, so Codex configures the hosted endpoint with `codex mcp add --bearer-token-env-var PASCAL_API_KEY` instead ([#849](https://github.com/pascalorg/editor/pull/849)) +- **Capture packages folded into core and viewer** — `@pascal-app/capture-protocol` is now `@pascal-app/core/capture` and `@pascal-app/capture-viewer` is now `@pascal-app/viewer/capture` (plus `@pascal-app/viewer/capture/preview`), so 1.0.0 ships seven packages instead of nine. Neither package was ever published to npm, so there is no npm migration; in-repo and workspace consumers change their import paths only. ### Fixes diff --git a/README.md b/README.md index eb11be6f6e..8b10daac39 100644 --- a/README.md +++ b/README.md @@ -122,11 +122,11 @@ owner publishes an approved version from `main` with the official registry publi The viewer runtime and built-in node definitions are separate packages. Install the full built-in viewer set, then load the built-in plugin once before mounting ``. Capture sessions are an -optional transport-neutral extension: +optional extension shipped inside those packages as the `@pascal-app/core/capture` and +`@pascal-app/viewer/capture` subpaths: ```bash npm install @pascal-app/core @pascal-app/viewer @pascal-app/editor @pascal-app/nodes -npm install @pascal-app/capture-protocol @pascal-app/capture-viewer ``` ```typescript @@ -149,10 +149,8 @@ editor/ ├── apps/ │ └── editor/ # Next.js application ├── packages/ -│ ├── core/ # Schemas, scene state, and registry contracts -│ ├── viewer/ # 3D rendering runtime and shared systems -│ ├── capture-protocol/ # Static/live capture-session contracts -│ ├── capture-viewer/ # Capture source runtime and reference renderers +│ ├── core/ # Schemas, scene state, registry contracts, capture contracts +│ ├── viewer/ # 3D rendering runtime, shared systems, capture runtime │ ├── editor/ # Editing tools and UI components │ ├── nodes/ # Built-in node definitions, renderers, and systems │ ├── cli/ # Persistent local editor installer and process manager @@ -164,10 +162,8 @@ editor/ | Package | Responsibility | |---------|---------------| -| **@pascal-app/core** | Node schemas, scene state (Zustand), registry contracts, spatial queries, and event bus | -| **@pascal-app/viewer** | 3D rendering via React Three Fiber, shared render systems, default camera/controls, and post-processing | -| **@pascal-app/capture-protocol** | Versioned capture manifests, normalized streams, and transport-neutral static/live sources | -| **@pascal-app/capture-viewer** | Viewer child runtime and reference model, device-motion, and point-cloud layers | +| **@pascal-app/core** | Node schemas, scene state (Zustand), registry contracts, spatial queries, and event bus. `core/capture` adds versioned capture manifests, normalized streams, and transport-neutral static/live sources | +| **@pascal-app/viewer** | 3D rendering via React Three Fiber, shared render systems, default camera/controls, and post-processing. `viewer/capture` adds the capture runtime and reference model, device-motion, point-cloud, and surface-mesh layers | | **@pascal-app/editor** | Editing tools, panels, selection, and direct-manipulation UI | | **@pascal-app/nodes** | Built-in registry plugin with node definitions, renderers, geometry, and systems | | **@pascal-app/cli** | Installs and manages a versioned standalone editor runtime and persistent local data | diff --git a/bun.lock b/bun.lock index 8ba2bbec05..83be8e16bc 100644 --- a/bun.lock +++ b/bun.lock @@ -102,45 +102,6 @@ "typescript": "7.0.2", }, }, - "packages/capture-protocol": { - "name": "@pascal-app/capture-protocol", - "version": "1.0.0-beta.4", - "dependencies": { - "zod": ">=4.5.4 <4.6", - }, - "devDependencies": { - "@pascal/typescript-config": "*", - "@types/bun": "^1.3.0", - "typescript": "6.0.3", - }, - }, - "packages/capture-viewer": { - "name": "@pascal-app/capture-viewer", - "version": "1.0.0-beta.4", - "devDependencies": { - "@pascal-app/capture-protocol": "^1.0.0-beta.4", - "@pascal-app/core": "^1.0.0-beta.4", - "@pascal-app/viewer": "^1.0.0-beta.4", - "@pascal/typescript-config": "*", - "@react-three/drei": "^10.7.7", - "@react-three/fiber": "^9.5.0", - "@types/bun": "^1.3.0", - "@types/react": "^19.2.2", - "@types/three": "^0.184.0", - "react": "^19.2.4", - "three": "^0.186.0", - "typescript": "6.0.3", - }, - "peerDependencies": { - "@pascal-app/capture-protocol": "^1.0.0-beta.4", - "@pascal-app/core": "^1.0.0-beta.4", - "@pascal-app/viewer": "^1.0.0-beta.4", - "@react-three/drei": "^10", - "@react-three/fiber": "^9", - "react": "^18 || ^19", - "three": "^0.186", - }, - }, "packages/cli": { "name": "@pascal-app/cli", "version": "1.0.0-beta.1", @@ -160,7 +121,6 @@ "name": "@pascal-app/core", "version": "1.0.0-beta.5", "dependencies": { - "@pascal-app/capture-protocol": "^1.0.0-beta.4", "dedent": "^1.7.1", "idb-keyval": "^6.2.2", "mitt": "^3.0.1", @@ -774,10 +734,6 @@ "@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.69.0", "", { "os": "win32", "cpu": "x64" }, "sha512-w8SOXv3mT9Fi6jY8OXdXCfnvX/3KNLXGNr4HEz2TA7S4Mv/PYAOmpB8y/ge40mxvBMgGNaSaaDwZpAsQn7HtWA=="], - "@pascal-app/capture-protocol": ["@pascal-app/capture-protocol@workspace:packages/capture-protocol"], - - "@pascal-app/capture-viewer": ["@pascal-app/capture-viewer@workspace:packages/capture-viewer"], - "@pascal-app/cli": ["@pascal-app/cli@workspace:packages/cli"], "@pascal-app/core": ["@pascal-app/core@workspace:packages/core"], @@ -1532,7 +1488,7 @@ "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="], - "is-promise": ["is-promise@2.2.2", "", {}, "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="], + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], @@ -2180,6 +2136,8 @@ "postcss/nanoid": ["nanoid@3.3.17", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-xQLf0A3HOMlgHq0n247/LRuAOYmB7dXJ/DvAxGvsSBij45XtBSmQycu+F8ODbHwns/XyFZagyL1+J0Offw1E0g=="], + "promise-worker-transferable/is-promise": ["is-promise@2.2.2", "", {}, "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ=="], + "react-doctor/agent-install": ["agent-install@0.0.5", "", { "dependencies": { "@iarna/toml": "^2.2.5", "commander": "^14.0.0", "jsonc-parser": "^3.3.1", "picocolors": "^1.1.1", "prompts": "^2.4.2", "yaml": "^2.8.3" }, "bin": { "agent-install": "bin/agent-install.mjs" } }, "sha512-nHlms9BkP8ZiY79HrwCGiA2DcNaXrAaJrCM/BEqQ7MEsSKyCk+2A76xPGylIfASZSZE0SaU3T0bNSg4rBPIJAQ=="], "react-doctor/eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.1.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g=="], @@ -2188,8 +2146,6 @@ "react-scan/commander": ["commander@14.0.3", "", {}, "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw=="], - "router/is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], - "sharp/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="], "three-stdlib/fflate": ["fflate@0.6.10", "", {}, "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg=="], diff --git a/packages/capture-protocol/LICENSE b/packages/capture-protocol/LICENSE deleted file mode 100644 index 083fd9e323..0000000000 --- a/packages/capture-protocol/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Pascal Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/capture-protocol/README.md b/packages/capture-protocol/README.md deleted file mode 100644 index a569059558..0000000000 --- a/packages/capture-protocol/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# `@pascal-app/capture-protocol` - -Transport-neutral capture-session contracts for Pascal viewers and hosts. - -The package contains versioned static manifests, a normalized session descriptor, packet headers -for incremental data, and a `CaptureSource` interface that can be backed by HTTP, WebSocket, -WebRTC, local files, or an in-memory producer. It does not contain authentication, persistence, -React, Three.js, or a canonical network transport. - -```ts -import { - createHttpCaptureSource, - type CaptureSessionLocator, -} from '@pascal-app/capture-protocol' - -const locator: CaptureSessionLocator = { - sessionId: 'capture_123', - manifestUrl: '/api/captures/capture_123/manifest', -} - -const source = createHttpCaptureSource(locator, { credentials: 'include' }) -const descriptor = await source.describe() -``` - -For live producers, use `PushCaptureSource` directly or implement `CaptureSource.subscribe()` with -the same descriptor and packet event contract. - -The package exports its TypeScript source under the `react-native` condition so Metro can consume -the workspace package from a clean checkout. Web and Node consumers continue to use the compiled -ES module output. diff --git a/packages/capture-protocol/package.json b/packages/capture-protocol/package.json deleted file mode 100644 index 598b39e2ee..0000000000 --- a/packages/capture-protocol/package.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "@pascal-app/capture-protocol", - "version": "1.0.0-beta.4", - "description": "Transport-neutral capture-session manifests and live stream sources for Pascal", - "type": "module", - "main": "./dist/index.js", - "types": "./src/index.ts", - "exports": { - ".": { - "types": "./src/index.ts", - "react-native": "./src/index.ts", - "browser": "./src/index.ts", - "import": "./dist/index.js", - "default": "./dist/index.js" - } - }, - "files": [ - "dist", - "src", - "README.md" - ], - "scripts": { - "build": "tsc --build", - "dev": "tsgo --build --watch", - "test": "bun test src", - "prepublishOnly": "npm run build" - }, - "dependencies": { - "zod": ">=4.5.4 <4.6" - }, - "devDependencies": { - "@pascal/typescript-config": "*", - "@types/bun": "^1.3.0", - "typescript": "6.0.3" - }, - "keywords": [ - "3d", - "capture", - "point-cloud", - "sensor-fusion", - "streaming" - ], - "repository": { - "type": "git", - "url": "https://github.com/pascalorg/editor.git", - "directory": "packages/capture-protocol" - }, - "license": "MIT", - "homepage": "https://github.com/pascalorg/editor/tree/main/packages/capture-protocol#readme", - "bugs": "https://github.com/pascalorg/editor/issues" -} diff --git a/packages/capture-protocol/tsconfig.json b/packages/capture-protocol/tsconfig.json deleted file mode 100644 index 06b4bb5999..0000000000 --- a/packages/capture-protocol/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "@pascal/typescript-config/react-library.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src", - "noEmit": false, - "composite": true, - "incremental": true, - "types": ["bun"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist", "**/*.test.ts"] -} diff --git a/packages/capture-viewer/LICENSE b/packages/capture-viewer/LICENSE deleted file mode 100644 index 083fd9e323..0000000000 --- a/packages/capture-viewer/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 Pascal Group Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/packages/capture-viewer/README.md b/packages/capture-viewer/README.md deleted file mode 100644 index f6a63e3f01..0000000000 --- a/packages/capture-viewer/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# `@pascal-app/capture-viewer` - -Reference capture layers for `@pascal-app/viewer`. - -Mount `CaptureRuntime` as a child of `Viewer` and provide a source resolver. The host owns access -control and transport; the runtime owns source lifecycle, scan-node placement, layer visibility, -and reference renderers for RoomPlan models, device trajectories, and PLY/live point clouds. - -```tsx - - reportCaptureError(error, context)} - resolveSource={(locator) => - createHttpCaptureSource(locator, { credentials: 'include' }) - } - retryKey={retryVersion} - /> - -``` - -Unknown streams remain in the descriptor and can be rendered by passing a custom renderer keyed by -stream role or kind. A live transport implements `CaptureSource.subscribe()`; no particular -WebSocket, WebRTC, or collaboration backend is required by this package. - -`CaptureRuntime` keeps telemetry host-neutral: pass `onError` to report source or per-stream -failures in the host, then increment `retryKey` to reload every affected session. Direct -`useCaptureSource()` consumers can call its `retry()` function instead. - -Hosts can pass `defaultLayerVisibility` to keep expensive optional layers disabled until a user -enables them. Persisted values in the scan node's `layers` map always override those host defaults; -without host defaults, every available layer remains visible for backwards compatibility. -Hidden sessions and layers are unmounted rather than only made visually transparent, so they stop -raycasting, artifact work, animation, and live packet subscriptions while disabled. - -## Local surface previews - -`@pascal-app/capture-viewer/preview` exports `createSurfaceMeshGeometry` and `createClayMatcap` -without importing the React viewer runtime. A host can render a locally saved surface immediately, -before its archive is uploaded. Browser and React Native exports resolve source, so an embedded -DOM bundle does not depend on generated workspace `dist` files. - -The geometry decoder uses the shared capture-protocol validator, including the native -20,000-face budget, byte lengths, and index bounds. It returns `null` for invalid input. -The host owns the returned geometry and matcap texture and must dispose them on teardown. - -Direct `CaptureStreamLayer` consumers can pass -`meshPresentation={{ previewMaterial: 'clay', dollhouse: true }}`. Clay replaces preliminary -vertex colors; dollhouse enables front-face rendering for surface previews and room models, -revealing inward-facing room surfaces from outside. It changes per-instance materials, not -geometry or loader-cached materials. Omitting these options preserves the existing presentation. diff --git a/packages/capture-viewer/bunfig.toml b/packages/capture-viewer/bunfig.toml deleted file mode 100644 index eec7d338da..0000000000 --- a/packages/capture-viewer/bunfig.toml +++ /dev/null @@ -1,4 +0,0 @@ -preload = ["../../scripts/bun-preload-three.ts"] - -[test] -preload = ["../../scripts/bun-preload-three.ts"] diff --git a/packages/capture-viewer/package.json b/packages/capture-viewer/package.json deleted file mode 100644 index d59369f216..0000000000 --- a/packages/capture-viewer/package.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "name": "@pascal-app/capture-viewer", - "version": "1.0.0-beta.4", - "description": "Open capture-session runtime and reference renderers for the Pascal viewer", - "type": "module", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js", - "default": "./dist/index.js" - }, - "./preview": { - "types": "./src/preview.ts", - "react-native": "./src/preview.ts", - "browser": "./src/preview.ts", - "import": "./dist/preview.js", - "default": "./dist/preview.js" - } - }, - "files": [ - "dist", - "src", - "README.md" - ], - "scripts": { - "build": "tsc --build", - "dev": "tsgo --build --watch", - "test": "bun test src", - "prepublishOnly": "npm run build" - }, - "peerDependencies": { - "@pascal-app/capture-protocol": "^1.0.0-beta.4", - "@pascal-app/core": "^1.0.0-beta.4", - "@pascal-app/viewer": "^1.0.0-beta.4", - "@react-three/drei": "^10", - "@react-three/fiber": "^9", - "react": "^18 || ^19", - "three": "^0.186" - }, - "devDependencies": { - "@pascal-app/capture-protocol": "^1.0.0-beta.4", - "@pascal-app/core": "^1.0.0-beta.4", - "@pascal-app/viewer": "^1.0.0-beta.4", - "@pascal/typescript-config": "*", - "@react-three/drei": "^10.7.7", - "@react-three/fiber": "^9.5.0", - "@types/bun": "^1.3.0", - "@types/react": "^19.2.2", - "@types/three": "^0.184.0", - "react": "^19.2.4", - "three": "^0.186.0", - "typescript": "6.0.3" - }, - "keywords": [ - "3d", - "capture", - "point-cloud", - "react-three-fiber", - "viewer" - ], - "repository": { - "type": "git", - "url": "https://github.com/pascalorg/editor.git", - "directory": "packages/capture-viewer" - }, - "license": "MIT", - "homepage": "https://github.com/pascalorg/editor/tree/main/packages/capture-viewer#readme", - "bugs": "https://github.com/pascalorg/editor/issues" -} diff --git a/packages/capture-viewer/tsconfig.json b/packages/capture-viewer/tsconfig.json deleted file mode 100644 index 3b503afab8..0000000000 --- a/packages/capture-viewer/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "@pascal/typescript-config/react-library.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src", - "noEmit": false, - "composite": true, - "incremental": true, - "types": ["bun"] - }, - "include": ["src"], - "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"], - "references": [{ "path": "../capture-protocol" }, { "path": "../core" }, { "path": "../viewer" }] -} diff --git a/packages/core/README.md b/packages/core/README.md index 2f39c6ff83..e9edb80cf8 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -23,6 +23,9 @@ npm install react three @react-three/fiber @react-three/drei - **Spatial Grid** - Collision detection and placement validation - **Event Bus** - Typed event emitter for inter-component communication - **Asset Storage** - IndexedDB-based file storage for user-uploaded assets +- **Capture Contracts** (`@pascal-app/core/capture`) - Versioned capture-session manifests, + normalized stream descriptors, packet headers, and transport-neutral static/live `CaptureSource` + implementations ## Usage @@ -78,6 +81,26 @@ Load the plugin before mounting `@pascal-app/viewer`. See the [`@pascal-app/viewer` quick start](https://github.com/pascalorg/editor/tree/main/packages/viewer#usage) for a React example. +## Capture Sessions + +Capture contracts are a self-contained subpath — no React, no Three.js, no prescribed transport: + +```typescript +import { createHttpCaptureSource, type CaptureSessionLocator } from '@pascal-app/core/capture' + +const locator: CaptureSessionLocator = { + sessionId: 'capture_123', + manifestUrl: '/api/captures/capture_123/manifest', +} + +const source = createHttpCaptureSource(locator, { credentials: 'include' }) +const descriptor = await source.describe() +``` + +For live producers, use `PushCaptureSource` directly or implement `CaptureSource.subscribe()` with +the same descriptor and packet event contract. The reference renderers that consume these sources +ship in [`@pascal-app/viewer/capture`](https://github.com/pascalorg/editor/tree/main/packages/viewer#capture-sessions). + ## License MIT diff --git a/packages/core/package.json b/packages/core/package.json index 0a1f26ced7..c0e76369e8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -21,6 +21,11 @@ "import": "./dist/utils/scene-migrations.js", "default": "./dist/utils/scene-migrations.js" }, + "./capture": { + "types": "./dist/capture/index.d.ts", + "import": "./dist/capture/index.js", + "default": "./dist/capture/index.js" + }, "./registry": { "types": "./dist/registry/index.d.ts", "import": "./dist/registry/index.js", @@ -76,7 +81,6 @@ "three": "^0.186" }, "dependencies": { - "@pascal-app/capture-protocol": "^1.0.0-beta.4", "dedent": "^1.7.1", "idb-keyval": "^6.2.2", "mitt": "^3.0.1", diff --git a/packages/capture-protocol/src/index.ts b/packages/core/src/capture/index.ts similarity index 100% rename from packages/capture-protocol/src/index.ts rename to packages/core/src/capture/index.ts diff --git a/packages/capture-protocol/src/schema.test.ts b/packages/core/src/capture/schema.test.ts similarity index 100% rename from packages/capture-protocol/src/schema.test.ts rename to packages/core/src/capture/schema.test.ts diff --git a/packages/capture-protocol/src/schema.ts b/packages/core/src/capture/schema.ts similarity index 100% rename from packages/capture-protocol/src/schema.ts rename to packages/core/src/capture/schema.ts diff --git a/packages/capture-protocol/src/source.test.ts b/packages/core/src/capture/source.test.ts similarity index 100% rename from packages/capture-protocol/src/source.test.ts rename to packages/core/src/capture/source.test.ts diff --git a/packages/capture-protocol/src/source.ts b/packages/core/src/capture/source.ts similarity index 100% rename from packages/capture-protocol/src/source.ts rename to packages/core/src/capture/source.ts diff --git a/packages/core/src/schema/nodes/scan.ts b/packages/core/src/schema/nodes/scan.ts index 36e16a323a..75790a5cf7 100644 --- a/packages/core/src/schema/nodes/scan.ts +++ b/packages/core/src/schema/nodes/scan.ts @@ -1,5 +1,5 @@ -import { CaptureSessionLocatorSchema } from '@pascal-app/capture-protocol' import { z } from 'zod' +import { CaptureSessionLocatorSchema } from '../../capture/schema' import { AssetUrl } from '../asset-url' import { BaseNode, nodeType, objectId } from '../base' diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 957e2ce9cb..bf55849b88 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -9,6 +9,5 @@ "types": ["bun"] }, "include": ["src"], - "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"], - "references": [{ "path": "../capture-protocol" }] + "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"] } diff --git a/packages/viewer/README.md b/packages/viewer/README.md index 7ebe4fe8cd..f9f34263a6 100644 --- a/packages/viewer/README.md +++ b/packages/viewer/README.md @@ -120,6 +120,61 @@ floor-plan pan/zoom/rotation, and the compass synchronize through transient subs navigation does not require a React render per frame. Set `showCompass={false}` or `showSwitcher={false}` when the host supplies its own controls. +## Capture Sessions + +`@pascal-app/viewer/capture` holds the optional capture runtime and its reference layers. Mount +`CaptureRuntime` as a child of `Viewer` and provide a source resolver. The host owns access control +and transport; the runtime owns source lifecycle, scan-node placement, layer visibility, and +reference renderers for RoomPlan models, device trajectories, and PLY/live point clouds. The +session contracts it consumes live in `@pascal-app/core/capture`. + +```tsx +import { createHttpCaptureSource } from '@pascal-app/core/capture' +import { Viewer } from '@pascal-app/viewer' +import { CaptureRuntime } from '@pascal-app/viewer/capture' + +function CaptureViewer() { + return ( + + reportCaptureError(error, context)} + resolveSource={(locator) => createHttpCaptureSource(locator, { credentials: 'include' })} + retryKey={retryVersion} + /> + + ) +} +``` + +Unknown streams remain in the descriptor and can be rendered by passing a custom renderer keyed by +stream role or kind. A live transport implements `CaptureSource.subscribe()`; no particular +WebSocket, WebRTC, or collaboration backend is required. + +`CaptureRuntime` keeps telemetry host-neutral: pass `onError` to report source or per-stream +failures in the host, then increment `retryKey` to reload every affected session. Direct +`useCaptureSource()` consumers can call its `retry()` function instead. + +Hosts can pass `defaultLayerVisibility` to keep expensive optional layers disabled until a user +enables them. Persisted values in the scan node's `layers` map always override those host defaults; +without host defaults, every available layer remains visible for backwards compatibility. Hidden +sessions and layers are unmounted rather than only made visually transparent, so they stop +raycasting, artifact work, animation, and live packet subscriptions while disabled. + +### Local surface previews + +`@pascal-app/viewer/capture/preview` exports `createSurfaceMeshGeometry` and `createClayMatcap` +without importing the React viewer runtime, so a capture client can render a locally saved surface +immediately, before its archive is uploaded. The geometry decoder uses the shared +`@pascal-app/core/capture` validator, including the native 20,000-face budget, byte lengths, and +index bounds. It returns `null` for invalid input. The host owns the returned geometry and matcap +texture and must dispose them on teardown. + +Direct `CaptureStreamLayer` consumers can pass +`meshPresentation={{ previewMaterial: 'clay', dollhouse: true }}`. Clay replaces preliminary vertex +colors; dollhouse enables front-face rendering for surface previews and room models, revealing +inward-facing room surfaces from outside. It changes per-instance materials, not geometry or +loader-cached materials. Omitting these options preserves the existing presentation. + ## Viewer State ```typescript diff --git a/packages/viewer/package.json b/packages/viewer/package.json index b90acda6b9..abdf044279 100644 --- a/packages/viewer/package.json +++ b/packages/viewer/package.json @@ -10,6 +10,16 @@ "types": "./dist/index.d.ts", "import": "./dist/index.js", "default": "./dist/index.js" + }, + "./capture": { + "types": "./dist/capture/index.d.ts", + "import": "./dist/capture/index.js", + "default": "./dist/capture/index.js" + }, + "./capture/preview": { + "types": "./dist/capture/preview.d.ts", + "import": "./dist/capture/preview.js", + "default": "./dist/capture/preview.js" } }, "files": [ diff --git a/packages/capture-viewer/src/asset-url.ts b/packages/viewer/src/capture/asset-url.ts similarity index 100% rename from packages/capture-viewer/src/asset-url.ts rename to packages/viewer/src/capture/asset-url.ts diff --git a/packages/capture-viewer/src/capture-runtime.tsx b/packages/viewer/src/capture/capture-runtime.tsx similarity index 98% rename from packages/capture-viewer/src/capture-runtime.tsx rename to packages/viewer/src/capture/capture-runtime.tsx index 31cb22c909..cc2af86e0d 100644 --- a/packages/capture-viewer/src/capture-runtime.tsx +++ b/packages/viewer/src/capture/capture-runtime.tsx @@ -1,5 +1,6 @@ 'use client' +import { type ScanNode, sceneRegistry, useScene } from '@pascal-app/core' import { type CaptureArtifactReference, CaptureArtifactReferenceSchema, @@ -12,9 +13,7 @@ import { type CaptureStreamPacket, captureLayerKey, DeviceMotionTrajectorySchema, -} from '@pascal-app/capture-protocol' -import { type ScanNode, sceneRegistry, useScene } from '@pascal-app/core' -import { ErrorBoundary, useNodeEvents, useViewer } from '@pascal-app/viewer' +} from '@pascal-app/core/capture' import { createPortal, useFrame } from '@react-three/fiber' import { type ComponentType, @@ -27,6 +26,9 @@ import { useState, } from 'react' import type { Object3D } from 'three' +import { ErrorBoundary } from '../components/error-boundary' +import { useNodeEvents } from '../hooks/use-node-events' +import useViewer from '../store/use-viewer' import { rewriteLoopbackAssetUrl } from './asset-url' import { resolveCaptureFrameMatrix } from './frame' import { isCaptureSessionVisible, isCaptureStreamVisible } from './layer-visibility' diff --git a/packages/capture-viewer/src/frame.test.ts b/packages/viewer/src/capture/frame.test.ts similarity index 94% rename from packages/capture-viewer/src/frame.test.ts rename to packages/viewer/src/capture/frame.test.ts index 2dfe47e19a..c4462f28d5 100644 --- a/packages/capture-viewer/src/frame.test.ts +++ b/packages/viewer/src/capture/frame.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from 'bun:test' -import type { CaptureSessionDescriptor } from '@pascal-app/capture-protocol' +import type { CaptureSessionDescriptor } from '@pascal-app/core/capture' import { Vector3 } from 'three' import { resolveCaptureFrameMatrix } from './frame' diff --git a/packages/capture-viewer/src/frame.ts b/packages/viewer/src/capture/frame.ts similarity index 90% rename from packages/capture-viewer/src/frame.ts rename to packages/viewer/src/capture/frame.ts index 992c40e136..00b1b47b4e 100644 --- a/packages/capture-viewer/src/frame.ts +++ b/packages/viewer/src/capture/frame.ts @@ -1,4 +1,4 @@ -import type { CaptureSessionDescriptor } from '@pascal-app/capture-protocol' +import type { CaptureSessionDescriptor } from '@pascal-app/core/capture' import { Matrix4 } from 'three' export function resolveCaptureFrameMatrix( diff --git a/packages/capture-viewer/src/index.ts b/packages/viewer/src/capture/index.ts similarity index 100% rename from packages/capture-viewer/src/index.ts rename to packages/viewer/src/capture/index.ts diff --git a/packages/capture-viewer/src/layer-visibility.test.ts b/packages/viewer/src/capture/layer-visibility.test.ts similarity index 100% rename from packages/capture-viewer/src/layer-visibility.test.ts rename to packages/viewer/src/capture/layer-visibility.test.ts diff --git a/packages/capture-viewer/src/layer-visibility.ts b/packages/viewer/src/capture/layer-visibility.ts similarity index 84% rename from packages/capture-viewer/src/layer-visibility.ts rename to packages/viewer/src/capture/layer-visibility.ts index 5524e7fd86..9065e90985 100644 --- a/packages/capture-viewer/src/layer-visibility.ts +++ b/packages/viewer/src/capture/layer-visibility.ts @@ -1,5 +1,5 @@ -import type { CaptureStreamDescriptor } from '@pascal-app/capture-protocol' -import { captureLayerKey } from '@pascal-app/capture-protocol' +import type { CaptureStreamDescriptor } from '@pascal-app/core/capture' +import { captureLayerKey } from '@pascal-app/core/capture' const EMPTY_LAYER_VISIBILITY: Readonly> = {} diff --git a/packages/capture-viewer/src/layers/clay-matcap.ts b/packages/viewer/src/capture/layers/clay-matcap.ts similarity index 100% rename from packages/capture-viewer/src/layers/clay-matcap.ts rename to packages/viewer/src/capture/layers/clay-matcap.ts diff --git a/packages/capture-viewer/src/layers/device-motion-layer.tsx b/packages/viewer/src/capture/layers/device-motion-layer.tsx similarity index 100% rename from packages/capture-viewer/src/layers/device-motion-layer.tsx rename to packages/viewer/src/capture/layers/device-motion-layer.tsx diff --git a/packages/capture-viewer/src/layers/point-cloud-layer.tsx b/packages/viewer/src/capture/layers/point-cloud-layer.tsx similarity index 98% rename from packages/capture-viewer/src/layers/point-cloud-layer.tsx rename to packages/viewer/src/capture/layers/point-cloud-layer.tsx index 34274604c4..9a09a0cc00 100644 --- a/packages/capture-viewer/src/layers/point-cloud-layer.tsx +++ b/packages/viewer/src/capture/layers/point-cloud-layer.tsx @@ -1,6 +1,6 @@ 'use client' -import type { CaptureStreamPacket } from '@pascal-app/capture-protocol' +import type { CaptureStreamPacket } from '@pascal-app/core/capture' import { useLoader } from '@react-three/fiber' import { useEffect, useMemo } from 'react' import { BufferGeometry, Float32BufferAttribute } from 'three' diff --git a/packages/capture-viewer/src/layers/room-model-layer.tsx b/packages/viewer/src/capture/layers/room-model-layer.tsx similarity index 98% rename from packages/capture-viewer/src/layers/room-model-layer.tsx rename to packages/viewer/src/capture/layers/room-model-layer.tsx index 9f4621b9bb..e679096275 100644 --- a/packages/capture-viewer/src/layers/room-model-layer.tsx +++ b/packages/viewer/src/capture/layers/room-model-layer.tsx @@ -1,10 +1,10 @@ 'use client' -import { useGLTFKTX2 } from '@pascal-app/viewer' import { useLoader } from '@react-three/fiber' import { useEffect, useMemo } from 'react' import { DoubleSide, FrontSide, type Material, type Mesh, type Object3D } from 'three' import { USDLoader } from 'three/addons/loaders/USDLoader.js' +import { useGLTFKTX2 } from '../../hooks/use-gltf-ktx2' import { rewriteLoopbackAssetUrl } from '../asset-url' import type { CaptureModelFormat } from '../stream-rendering' diff --git a/packages/capture-viewer/src/layers/surface-mesh-data.ts b/packages/viewer/src/capture/layers/surface-mesh-data.ts similarity index 97% rename from packages/capture-viewer/src/layers/surface-mesh-data.ts rename to packages/viewer/src/capture/layers/surface-mesh-data.ts index 70141ed9ee..44256a9f41 100644 --- a/packages/capture-viewer/src/layers/surface-mesh-data.ts +++ b/packages/viewer/src/capture/layers/surface-mesh-data.ts @@ -1,4 +1,4 @@ -import { SurfaceMeshPayloadSchema } from '@pascal-app/capture-protocol' +import { SurfaceMeshPayloadSchema } from '@pascal-app/core/capture' import { BufferGeometry, Float32BufferAttribute, Uint16BufferAttribute } from 'three' export type SurfaceMeshData = { diff --git a/packages/capture-viewer/src/layers/surface-mesh-layer.tsx b/packages/viewer/src/capture/layers/surface-mesh-layer.tsx similarity index 100% rename from packages/capture-viewer/src/layers/surface-mesh-layer.tsx rename to packages/viewer/src/capture/layers/surface-mesh-layer.tsx diff --git a/packages/capture-viewer/src/point-cloud-layer.test.ts b/packages/viewer/src/capture/point-cloud-layer.test.ts similarity index 94% rename from packages/capture-viewer/src/point-cloud-layer.test.ts rename to packages/viewer/src/capture/point-cloud-layer.test.ts index bb47013797..f534e25ebd 100644 --- a/packages/capture-viewer/src/point-cloud-layer.test.ts +++ b/packages/viewer/src/capture/point-cloud-layer.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from 'bun:test' -import type { CaptureStreamPacket } from '@pascal-app/capture-protocol' +import type { CaptureStreamPacket } from '@pascal-app/core/capture' import { buildPointCloudData, buildPointCloudPayloadData } from './layers/point-cloud-layer' function packet(sequence: number, positions: number[], colors?: number[]): CaptureStreamPacket { diff --git a/packages/capture-viewer/src/preview.ts b/packages/viewer/src/capture/preview.ts similarity index 100% rename from packages/capture-viewer/src/preview.ts rename to packages/viewer/src/capture/preview.ts diff --git a/packages/capture-viewer/src/source-state.test.ts b/packages/viewer/src/capture/source-state.test.ts similarity index 98% rename from packages/capture-viewer/src/source-state.test.ts rename to packages/viewer/src/capture/source-state.test.ts index f2ba53acf1..35a189af7d 100644 --- a/packages/capture-viewer/src/source-state.test.ts +++ b/packages/viewer/src/capture/source-state.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from 'bun:test' -import type { CaptureStreamPacket } from '@pascal-app/capture-protocol' +import type { CaptureStreamPacket } from '@pascal-app/core/capture' import { appendCapturePacket, captureSubscriptionStreamIds, diff --git a/packages/capture-viewer/src/source-state.ts b/packages/viewer/src/capture/source-state.ts similarity index 99% rename from packages/capture-viewer/src/source-state.ts rename to packages/viewer/src/capture/source-state.ts index 434040771e..a76b93874c 100644 --- a/packages/capture-viewer/src/source-state.ts +++ b/packages/viewer/src/capture/source-state.ts @@ -5,7 +5,7 @@ import type { CaptureSourceResolver, CaptureStreamDescriptor, CaptureStreamPacket, -} from '@pascal-app/capture-protocol' +} from '@pascal-app/core/capture' import { useCallback, useEffect, useState } from 'react' export type CaptureSourceState = { diff --git a/packages/capture-viewer/src/stream-rendering.test.ts b/packages/viewer/src/capture/stream-rendering.test.ts similarity index 100% rename from packages/capture-viewer/src/stream-rendering.test.ts rename to packages/viewer/src/capture/stream-rendering.test.ts diff --git a/packages/capture-viewer/src/stream-rendering.ts b/packages/viewer/src/capture/stream-rendering.ts similarity index 98% rename from packages/capture-viewer/src/stream-rendering.ts rename to packages/viewer/src/capture/stream-rendering.ts index 2fcfea7aeb..663ccd6ce8 100644 --- a/packages/capture-viewer/src/stream-rendering.ts +++ b/packages/viewer/src/capture/stream-rendering.ts @@ -5,7 +5,7 @@ import { DeviceMotionTrajectorySchema, PointCloudPayloadSchema, SurfaceMeshPayloadSchema, -} from '@pascal-app/capture-protocol' +} from '@pascal-app/core/capture' const GLB_MEDIA_TYPES = new Set(['model/gltf-binary', 'model/gltf+json']) const USDZ_MEDIA_TYPES = new Set(['model/vnd.usdz+zip']) diff --git a/packages/capture-viewer/src/surface-mesh-layer.test.ts b/packages/viewer/src/capture/surface-mesh-layer.test.ts similarity index 100% rename from packages/capture-viewer/src/surface-mesh-layer.test.ts rename to packages/viewer/src/capture/surface-mesh-layer.test.ts diff --git a/packages/capture-viewer/src/trajectory.test.ts b/packages/viewer/src/capture/trajectory.test.ts similarity index 100% rename from packages/capture-viewer/src/trajectory.test.ts rename to packages/viewer/src/capture/trajectory.test.ts diff --git a/packages/capture-viewer/src/trajectory.ts b/packages/viewer/src/capture/trajectory.ts similarity index 98% rename from packages/capture-viewer/src/trajectory.ts rename to packages/viewer/src/capture/trajectory.ts index 688f2e6828..0d883b2907 100644 --- a/packages/capture-viewer/src/trajectory.ts +++ b/packages/viewer/src/capture/trajectory.ts @@ -2,7 +2,7 @@ import { DeviceMotionSampleSchema, type DeviceMotionTrajectoryPayload, DeviceMotionTrajectorySchema, -} from '@pascal-app/capture-protocol' +} from '@pascal-app/core/capture' import { Matrix4, Quaternion, Vector3 } from 'three' export type DeviceTrajectoryPose = { diff --git a/wiki/architecture/capture-runtime.md b/wiki/architecture/capture-runtime.md index 8df4a6b66b..d62314737c 100644 --- a/wiki/architecture/capture-runtime.md +++ b/wiki/architecture/capture-runtime.md @@ -3,15 +3,18 @@ Capture data is an optional viewer extension, not a private Community renderer and not a second scene graph. -## Package boundaries - -- `@pascal-app/capture-protocol` owns versioned manifests, normalized stream descriptors, stable - session locators, incremental packet headers, and the `CaptureSource` interface. It has no React, - Three.js, authentication, database, or prescribed transport. -- `@pascal-app/capture-viewer` mounts inside `Viewer` through its existing children slot. It resolves - `scan.captureSession`, portals layers into that scan node's registered group, honors per-layer - visibility, composes declared local-to-parent coordinate frames into session space, and supplies - reference model, device-motion, point-cloud, and compact color-surface renderers. +## Ownership boundaries + +- `@pascal-app/core/capture` (`packages/core/src/capture/`) owns versioned manifests, normalized + stream descriptors, stable session locators, incremental packet headers, and the `CaptureSource` + interface. It has no React, Three.js, authentication, database, or prescribed transport, so it + stays inside core's pure-logic layer rule. +- `@pascal-app/viewer/capture` (`packages/viewer/src/capture/`) mounts inside `Viewer` through its + existing children slot. It resolves `scan.captureSession`, portals layers into that scan node's + registered group, honors per-layer visibility, composes declared local-to-parent coordinate frames + into session space, and supplies reference model, device-motion, point-cloud, and compact + color-surface renderers. `@pascal-app/viewer/capture/preview` exposes the matcap and surface-mesh + geometry builders on their own for capture clients that render a local preview without the runtime. - `@pascal-app/core` stores only the scene anchor: session locator, optional current mesh URL, placement, opacity, and an extensible visibility map. Raw samples and artifact inventories never enter scene JSON.