@realitycollective/iwsdk-uiextensions 0.1.0-preview.4 → 0.1.0-preview.6
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/CHANGELOG.md +12 -1
- package/README.md +17 -1
- package/dist/scene-host.d.ts +8 -1
- package/dist/scene-host.js +18 -1
- package/dist/scene-host.js.map +1 -1
- package/dist/systems/dock-region-system.js +2 -1
- package/dist/systems/dock-region-system.js.map +1 -1
- package/dist/systems/dock-system.js +2 -1
- package/dist/systems/dock-system.js.map +1 -1
- package/dist/systems/drag-system.js +2 -1
- package/dist/systems/drag-system.js.map +1 -1
- package/dist/systems/window-system.js +2 -1
- package/dist/systems/window-system.js.map +1 -1
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
|
8
8
|
|
|
9
9
|
### Added
|
|
10
10
|
|
|
11
|
+
- `verify:pack` now lints the shape of what ships: publint over every package directory, with warnings counted as errors, and attw (Are The Types Wrong) over every packed tarball, resolving the published types under node10, node16 and bundler resolution. `cjs-resolves-to-esm` is ignored by design, because every package is ESM-only and a require() caller is expected to use a dynamic import. Both run offline on the tarballs the script already builds; `publint` and `@arethetypeswrong/cli` are dev dependencies. The script stays identical across the Reality Collective repositories.
|
|
11
12
|
- `@realitycollective/webxr-uiextensions` - engine-free core: window manager, dock state and regions, drag maths, hold-to-drag, control models (stepper/toggle/expandable/log), the `SceneDescriptor` scene format, window chrome conventions and the platform-adapter contract.
|
|
12
13
|
- `@realitycollective/iwsdk-uiextensions` - Meta IWSDK adapter binding the core onto IWSDK's ECS, UIKitML and interaction systems, with shipped examples.
|
|
13
14
|
- `@realitycollective/xrblocks-uiextensions` - EXPERIMENTAL Google XR Blocks / plain three.js adapter: panel document, window host, follow and scale maths, desktop controls and locomotion, pointer forwarding.
|
|
@@ -30,11 +31,21 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
|
30
31
|
- `compilePanelSource` in `@realitycollective/uix-devtools` serves the **source** on its `blob:` URL rather than compiled JSON, matching what 0.5 fetches. `CompiledPanel.json` is now `CompiledPanel.source`, and the `resolveFile` option is gone because the 0.5 parser has no `<link ref>` stylesheet resolution; put shared rules in a `<style>` block, of which several are allowed and merge. Validation now runs on `@drawcall/uikitml`, pinned to the version `@iwsdk/core` depends on, so a source that passes here is one `PanelUISystem` will accept and no second parser enters the tree.
|
|
31
32
|
- Example and demo markup uses `rgba()` or eight-digit hex rather than `background-opacity`, which the 0.5 parser removed. Both parsers accept the replacement, so one stylesheet serves every adapter.
|
|
32
33
|
- The IWSDK component kit is selected by name, `spatialUI: { kit: 'horizon' }`, rather than by passing an imported kit module. The kits now live inside the parser.
|
|
33
|
-
- `@types/three` is pinned to `0.184.0` across the workspace. IWSDK 0.5.3 and xrblocks resolved different copies, and two copies of the three typings make structurally identical `Object3D` types mutually unassignable, which surfaces as spurious errors far from their cause.
|
|
34
|
+
- `@types/three` is pinned to `0.184.0` across the workspace. IWSDK 0.5.3 and xrblocks resolved different copies, and two copies of the three typings make structurally identical `Object3D` types mutually unassignable, which surfaces as spurious errors far from their cause. One copy per workspace is now the rule in every Reality Collective repository that hosts IWSDK. This workspace stays on 0.184.0 rather than the 0.181.0 that matches its super-three: on 0.181.0 the `Object3D` augmentation that `@pmndrs/pointer-events` declares does not merge with three's `Object3D`, and TypeScript reports two incompatible `Object3D` types across the showcase and the XR Blocks adapter. 0.184.0 is the version this workspace is verified against.
|
|
34
35
|
- `onPanelReady` on the IWSDK host announces bare panels as well as managed windows. A `PanelUI` entity with no `UIWindow`, or with an empty `windowId`, used to be dropped silently; it is now announced with `kind: 'panel'` and its config path as the id. Listeners that only want managed windows should check `kind`.
|
|
35
36
|
- `id` is optional in the XR Blocks host's `CreateWindowOptions`; a window created without one is named `uix-window-<n>`, as on IWSDK. The options also accept `movable` for parity, but this host has no title-bar drag of its own yet, so the flag is recorded and not acted on.
|
|
36
37
|
- The XR Blocks window handle gains `panel` - the document, which already implements `PanelHandle` - and `onReady`, which fires synchronously because uikitml interprets the markup during `createWindow`. The interface is now named `XrBlocksWindowHandle`, with `WindowHandle` kept as an alias, because the core exports a `WindowHandle` of its own.
|
|
37
38
|
- Coverage gates `packages/iwsdk-uiextensions/src/factory.ts` and `src/scene-host.ts` at the same 100% as the core. `new World()` from `@iwsdk/core` constructs headlessly - no renderer, no WebGL, no XR session - so the factories, the scene host and its ECS query all run for real in a node test. The per-frame ECS systems still need a live world and stay outside the gate.
|
|
38
39
|
- `@realitycollective/webxr-uiextensions` takes its geometry vocabulary from `@realitycollective/webxr-input` at `^0.1.1` rather than redeclaring it. `Vec3Tuple`, `QuatTuple`, `HeadPose`, `HeadPoseSource` and `PointerSample` are now that package's types, re-exported under the same names, so no import changes for a consumer. `PointerSample` is its `RayTuple`, which is what lets an input provider written against the shared contracts feed this contract unchanged. It is the core's only runtime dependency: the contracts package is engine-free and carries none of its own, and `test/architecture.test.ts` now allows exactly that one name and fails on any other.
|
|
40
|
+
- `PointerSample` states its ownership rule: a delivered sample belongs to the listener and the source never writes to it again, so the core's hold-to-drag and drag maths may keep a press-time sample without copying. It mirrors the rule `@realitycollective/webxr-input` 0.1.3 writes on `InputSourceSnapshot`, so a provider feeding both contracts has one promise to keep.
|
|
39
41
|
|
|
40
42
|
[0.1.0]: https://github.com/realitycollective/WebXR-UIExtensions/commits/main
|
|
43
|
+
|
|
44
|
+
### Fixed
|
|
45
|
+
|
|
46
|
+
- `createSceneHost(world)` returns the same host on every call for a world, where it previously built a new one each time. A second host registered a second readiness ECS system on the same world and repeated the panel upgrade pass, so two modules each asking for the host quietly doubled that work. It now memoises per world exactly as the window manager registry does, which also means separate modules can ask for the host without coordinating or passing it around.
|
|
47
|
+
- Documented in `@realitycollective/iwsdk-uiextensions` that `onPanelReady` announces windows spawned by `createUIWindow`, not only by `host.createWindow`, so code holding factory entities has a readiness signal without changing how it spawns them. The readiness section now also warns against polling `getPanelHandle` on a timer, because a poll that gives up early leaves a window that draws and responds to nothing with no error, and records that a factory window created without an `id` is announced as `kind: 'panel'` with its config path, which a listener filtering on `kind === 'window'` will never see.
|
|
48
|
+
- `@realitycollective/uix-devtools/cli` resolved to no types under TypeScript's legacy `node10` module resolution, which ignores the `exports` map; `typesVersions` now maps the subpath to `dist/cli/lib.d.ts`. Found by the attw step `verify:pack` gained in this release.
|
|
49
|
+
- `@realitycollective/iwsdk-uiextensions` imported three.js's `Vector3`, `Quaternion`, `Euler` and `Object3D` through `@iwsdk/core`, which only provides them by `export * from 'three'`. That resolves in node and in every demo, and fails a consumer whose Vite config excludes `three` from dependency optimization, which any app that transforms three's source does: esbuild cannot enumerate a star re-export it is not bundling, and the dev server stops with `No matching export in "@iwsdk/core" for import "Vector3"`, a message that names neither the package nor the cause. The four systems modules now import those classes from `three`, declared as a peer dependency (`>=0.170.0`, the range the XR Blocks adapter carries), which every IWSDK application already has. Reported by the Anatomy Atlas XR client.
|
|
50
|
+
- `@realitycollective/xrblocks-uiextensions` imported `reversePainterSortStable` from `@pmndrs/uikit` without declaring it; it resolved only because another package hoisted it. The package now lists it as a dependency. Found by the new import-surface gate.
|
|
51
|
+
- Two gates, shared with every Reality Collective repository. `packages/webxr-uiextensions/test/import-surface.test.ts` runs `scripts/import-surface.mjs` over every published package and fails an import of a name that a dependency only re-exports from another package, and any bare import of a package the manifest does not declare. `packages/iwsdk-uiextensions/test/prebundle.test.ts` runs the dependency optimizer of every bundler `scripts/release.config.json` names (`scripts/prebundle-check.mjs`) over the adapter next to `@iwsdk/core`, once with defaults and once with every package `@iwsdk/core` re-exports wholesale excluded, so the consumer path is exercised on every `npm test`. It runs on **Vite 7 and Vite 8**, because this fault belongs to a bundler rather than to bundling: Vite 7 optimizes with esbuild, which requires every named import to be statically enumerable and so rejects a name behind an external `export *`, while Vite 8 optimizes with rolldown, which resolves the same import lazily through a namespace object and accepts it. Testing only one of them would say nothing about consumers on the other. The versions under test are ordinary devDependencies installed under npm aliases, `vite-7` and `vite-8`, the same mechanism the workspace already uses for `three`; add an entry to `prebundle.bundlers` to support another. Each run also feeds the optimizer a **deliberately broken copy** of the adapter, carrying one extra module that imports a name the host only re-exports wholesale, and asserts the bundler's verdict matches that bundler's `detectsStarHops` flag. The probed name is discovered from the host's own surface rather than hard-coded, so it stays valid as that surface changes. This is what stops the check becoming decoration: on a permissive bundler a gate that can only pass reports safety it never verified, and the suite now fails if a bundler's strictness moves in either direction, or if no supported bundler can detect the fault at all. Transpiling uses the TypeScript compiler the repository already builds with, so no bundler is a hidden requirement of the harness itself. Both were red before this fix.
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Everything is authored in plain UIKitML (HTML/CSS-like) - no new markup language
|
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
npm install @realitycollective/iwsdk-uiextensions
|
|
23
|
-
#
|
|
23
|
+
# peers: @iwsdk/core >=0.5.0 <0.6.0 and three >=0.170.0 (every IWSDK app already has both)
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
## Quick start
|
|
@@ -127,6 +127,8 @@ status.entity; // still the entity, for ECS work
|
|
|
127
127
|
|
|
128
128
|
`getPanelHandle(entity)` does the same lookup for an entity you already hold, and returns `undefined` while the document is still loading.
|
|
129
129
|
|
|
130
|
+
**Do not poll `getPanelHandle` on a timer.** It is a single synchronous read, not a wait, and there is no deadline you can safely guess: the markup is fetched over the network and parsed over later frames, so a cold cache on a headset takes far longer than a warm one on a desktop. A poll that gives up early leaves a window that draws correctly and responds to nothing, with no error and no log line, which is close to undiagnosable from the outside. Every window has a readiness signal already, so use one.
|
|
131
|
+
|
|
130
132
|
Across a whole scene, subscribe to the host instead:
|
|
131
133
|
|
|
132
134
|
```ts
|
|
@@ -139,6 +141,20 @@ host.onPanelReady(({ id, panel, kind }) => {
|
|
|
139
141
|
});
|
|
140
142
|
```
|
|
141
143
|
|
|
144
|
+
`onPanelReady` covers windows spawned by `createUIWindow` too, not only by `host.createWindow`. So code that already holds factory entities does not have to change how it spawns them: create the host once, subscribe, and match on the `id` you passed to the factory.
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
const entity = createUIWindow(world, { id: 'status', config: './ui/status.uikitml' });
|
|
148
|
+
|
|
149
|
+
createSceneHost(world).onPanelReady(({ id, panel }) => {
|
|
150
|
+
if (id === 'status') wireStatus(panel);
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Pass an `id` to `createUIWindow` if you intend to match on one. Without it the window has no id to announce, so it arrives as `kind: 'panel'` with its config path as the `id`, and a listener filtering on `kind === 'window'` will silently never see it. `host.createWindow` differs here: it invents `uix-window-<n>` when you omit the id.
|
|
155
|
+
|
|
156
|
+
`createSceneHost(world)` returns the same host every time it is called for a world, so separate modules can each ask for it without coordinating or passing it around.
|
|
157
|
+
|
|
142
158
|
Bare panels are announced as well as managed windows, which is how devtools and hand-built `PanelUI` entities show up in the same stream. `supportsStandalonePanels` is `false` on this host: IWSDK owns panel lifecycles through the ECS, so `createPanel()` throws rather than half-working. Spawn a window instead.
|
|
143
159
|
|
|
144
160
|
## Headless core
|
package/dist/scene-host.d.ts
CHANGED
|
@@ -39,6 +39,13 @@ export interface IwsdkSceneHost extends WindowHost, SceneTarget {
|
|
|
39
39
|
createWindow(options: CreateWindowOptions): IwsdkWindowHandle;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
42
|
+
* The host for a world: built on the first call, and the same instance every
|
|
43
|
+
* call after that. Call AFTER `registerUIExtensions(world)`.
|
|
44
|
+
*
|
|
45
|
+
* One host per world is deliberate. A host owns an ECS system, the record of
|
|
46
|
+
* which documents it has already announced, and the panel upgrade pass, so a
|
|
47
|
+
* second independent host on the same world would run a second copy of all
|
|
48
|
+
* three. Two modules can each ask for the host without coordinating, exactly
|
|
49
|
+
* as they do for the window manager registry.
|
|
43
50
|
*/
|
|
44
51
|
export declare function createSceneHost(world: World): IwsdkSceneHost;
|
package/dist/scene-host.js
CHANGED
|
@@ -55,10 +55,27 @@ function createReadySystem(onReady) {
|
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
+
const hosts = new WeakMap();
|
|
58
59
|
/**
|
|
59
|
-
*
|
|
60
|
+
* The host for a world: built on the first call, and the same instance every
|
|
61
|
+
* call after that. Call AFTER `registerUIExtensions(world)`.
|
|
62
|
+
*
|
|
63
|
+
* One host per world is deliberate. A host owns an ECS system, the record of
|
|
64
|
+
* which documents it has already announced, and the panel upgrade pass, so a
|
|
65
|
+
* second independent host on the same world would run a second copy of all
|
|
66
|
+
* three. Two modules can each ask for the host without coordinating, exactly
|
|
67
|
+
* as they do for the window manager registry.
|
|
60
68
|
*/
|
|
61
69
|
export function createSceneHost(world) {
|
|
70
|
+
const existing = hosts.get(world);
|
|
71
|
+
if (existing) {
|
|
72
|
+
return existing;
|
|
73
|
+
}
|
|
74
|
+
const host = buildSceneHost(world);
|
|
75
|
+
hosts.set(world, host);
|
|
76
|
+
return host;
|
|
77
|
+
}
|
|
78
|
+
function buildSceneHost(world) {
|
|
62
79
|
const readyListeners = new Set();
|
|
63
80
|
const ready = new Map();
|
|
64
81
|
const seen = new WeakSet();
|
package/dist/scene-host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scene-host.js","sourceRoot":"","sources":["../src/scene-host.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * IWSDK implementation of the core's `WindowHost` + `SceneTarget`.\n *\n * This is what makes a portable `SceneDescriptor` build the SAME playground\n * on IWSDK as on plain three.js, and lets app behaviour be wired through\n * `onPanelReady` instead of hand-written ECS queries:\n *\n * const host = createSceneHost(world);\n * applyScene(host, PLAYGROUND);\n * host.onPanelReady(({ id, panel }) => wireMyPanel(id, panel));\n *\n * Readiness is detected with a single ECS query on `PanelUI + PanelDocument`\n * - the engine-specific mechanism stays here, behind the portable interface.\n * Bare panels (a `PanelUI` entity with no `UIWindow`) are announced too, with\n * `kind: 'panel'` and their config path as the id, so devtools and hand-built\n * entities show up in the same stream as managed windows.\n */\nimport {\n PanelDocument,\n PanelUI,\n UIKitDocument,\n createSystem,\n type Entity,\n type World,\n} from '@iwsdk/core';\nimport {\n upgradePanel,\n type PanelHandle,\n type PanelReadyEvent,\n type SceneRegion,\n type SceneTarget,\n type SceneWindow,\n type UixElement,\n type WindowHandle,\n type WindowHost,\n} from '@realitycollective/webxr-uiextensions';\nimport {\n createDockRegion,\n createUIWindow,\n type CreateWindowOptions,\n} from './factory.js';\nimport { UIWindow } from './components.js';\n\n/** Panel handle over an IWSDK `UIKitDocument`. */\nfunction panelHandleFor(document: UIKitDocument): PanelHandle {\n return {\n get root(): UixElement {\n return document.rootElement as unknown as UixElement;\n },\n getElementById: (id) =>\n (document.getElementById(id) as UixElement | null) ?? undefined,\n setTargetDimensions: (width, height) =>\n document.setTargetDimensions(width, height),\n dispose: () => {\n /* IWSDK owns the document lifecycle via the entity. */\n },\n };\n}\n\n/**\n * The live panel on an entity, or `undefined` while IWSDK is still loading\n * its document. Use it to reach a panel from an entity you already hold;\n * `onPanelReady` stays the way to be TOLD when one appears.\n */\nexport function getPanelHandle(entity: Entity): PanelHandle | undefined {\n const document = PanelDocument.data.document[entity.index] as\n | UIKitDocument\n | undefined;\n return document ? panelHandleFor(document) : undefined;\n}\n\n/**\n * Bridges panel loading into `onPanelReady`. Registered once per host; the\n * query fires as each panel's document is attached by IWSDK's UI system.\n */\nfunction createReadySystem(onReady: (entity: Entity) => void) {\n return class UixPanelReadySystem extends createSystem({\n panels: { required: [PanelUI, PanelDocument] },\n }) {\n override init(): void {\n this.cleanupFuncs.push(\n this.queries.panels.subscribe('qualify', (entity) => onReady(entity)),\n );\n this.queries.panels.entities.forEach((entity) => onReady(entity));\n }\n };\n}\n\n/** A window spawned by {@link IwsdkSceneHost.createWindow}. */\nexport interface IwsdkWindowHandle extends WindowHandle {\n /** The ECS entity carrying `PanelUI`, `UIWindow` and the interaction tags. */\n readonly entity: Entity;\n}\n\nexport interface IwsdkSceneHost extends WindowHost, SceneTarget {\n readonly world: World;\n /**\n * Spawn a managed window and get a handle back straight away. IWSDK loads\n * the markup over later frames, so `handle.panel` is `undefined` at first\n * and `handle.onReady` is how you wait for it without an ECS query.\n */\n createWindow(options: CreateWindowOptions): IwsdkWindowHandle;\n}\n\n/**\n * Create the host for a world. Call AFTER `registerUIExtensions(world)`.\n */\nexport function createSceneHost(world: World): IwsdkSceneHost {\n const readyListeners = new Set<(event: PanelReadyEvent) => void>();\n const ready = new Map<string, PanelReadyEvent>();\n const seen = new WeakSet<object>();\n\n const announce = (entity: Entity): void => {\n const document = PanelDocument.data.document[entity.index] as\n | UIKitDocument\n | undefined;\n if (!document || seen.has(document)) {\n return;\n }\n seen.add(document);\n // Window id comes from the UIWindow component the factory attached. A\n // panel without one is a bare panel - still worth announcing, keyed by\n // its config path, which is the only stable id IWSDK gives it.\n const windowId = entity.hasComponent(UIWindow)\n ? (UIWindow.data.windowId[entity.index] as string)\n : '';\n const kind: 'window' | 'panel' = windowId ? 'window' : 'panel';\n const id = windowId || (PanelUI.data.config[entity.index] as string);\n const panel = panelHandleFor(document);\n // Upgrade `data-uix` controls exactly as the ECS controls system does,\n // so wiring code sees the same handles on every adapter (idempotent).\n upgradePanel(document, document.rootElement as unknown as UixElement);\n const event: PanelReadyEvent = { id, panel, kind };\n ready.set(id, event);\n for (const listener of readyListeners) {\n try {\n listener(event);\n } catch (error) {\n console.error(`[uix] panel-ready listener failed for \"${id}\":`, error);\n }\n }\n };\n\n world.registerSystem(createReadySystem(announce));\n\n let windowSequence = 0;\n\n const host: IwsdkSceneHost = {\n world,\n\n // IWSDK owns panel lifecycles through the ECS, so there is no bare-panel\n // path here - `createPanel` throws rather than half-working.\n supportsStandalonePanels: false,\n\n createPanel(): PanelHandle {\n throw new Error(\n '[uix] createPanel() is not supported on the IWSDK host - spawn a window ' +\n '(spawnWindow/createUIWindow) so IWSDK owns the panel lifecycle.',\n );\n },\n\n onPanelReady(listener) {\n readyListeners.add(listener);\n for (const event of ready.values()) {\n listener(event);\n }\n return () => void readyListeners.delete(listener);\n },\n\n createWindow(options: CreateWindowOptions): IwsdkWindowHandle {\n const id = options.id ?? `uix-window-${(windowSequence += 1)}`;\n const entity = createUIWindow(world, { ...options, id });\n return {\n entity,\n id,\n get panel(): PanelHandle | undefined {\n return ready.get(id)?.panel;\n },\n onReady(listener: (panel: PanelHandle) => void): () => void {\n const existing = ready.get(id)?.panel;\n if (existing) {\n listener(existing);\n return () => {};\n }\n // `onPanelReady` replays panels that are already live, but this id\n // is not one of them, so the callback only ever runs later.\n const unsubscribe = host.onPanelReady((event) => {\n if (event.id === id) {\n unsubscribe();\n listener(event.panel);\n }\n });\n return unsubscribe;\n },\n };\n },\n\n spawnRegion(region: SceneRegion): void {\n createDockRegion(world, {\n id: region.id,\n ...(region.flow !== undefined ? { flow: region.flow } : {}),\n ...(region.pitch !== undefined ? { pitch: region.pitch } : {}),\n ...(region.columns !== undefined ? { columns: region.columns } : {}),\n ...(region.capacity !== undefined ? { capacity: region.capacity } : {}),\n ...(region.snapRadius !== undefined\n ? { snapRadius: region.snapRadius }\n : {}),\n ...(region.position !== undefined\n ? { position: [...region.position] as [number, number, number] }\n : {}),\n ...(region.follow !== undefined ? { follow: region.follow } : {}),\n ...(region.followOffset !== undefined\n ? { followOffset: [...region.followOffset] as [number, number, number] }\n : {}),\n });\n },\n\n spawnWindow(window: SceneWindow): void {\n host.createWindow({\n id: window.id,\n title: window.title,\n config: window.config,\n ...(window.position !== undefined\n ? { position: [...window.position] as [number, number, number] }\n : {}),\n ...(window.maxWidth !== undefined ? { maxWidth: window.maxWidth } : {}),\n ...(window.maxHeight !== undefined ? { maxHeight: window.maxHeight } : {}),\n ...(window.dockMode !== undefined ? { dockMode: window.dockMode } : {}),\n ...(window.region !== undefined ? { region: window.region } : {}),\n ...(window.followOffset !== undefined\n ? { followOffset: [...window.followOffset] as [number, number, number] }\n : {}),\n ...(window.followSpeed !== undefined\n ? { followSpeed: window.followSpeed }\n : {}),\n ...(window.followTolerance !== undefined\n ? { followTolerance: window.followTolerance }\n : {}),\n ...(window.movable !== undefined ? { movable: window.movable } : {}),\n ...(window.closable !== undefined ? { closable: window.closable } : {}),\n ...(window.minimizable !== undefined\n ? { minimizable: window.minimizable }\n : {}),\n ...(window.pinnable !== undefined ? { pinnable: window.pinnable } : {}),\n });\n },\n };\n\n return host;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"scene-host.js","sourceRoot":"","sources":["../src/scene-host.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * IWSDK implementation of the core's `WindowHost` + `SceneTarget`.\n *\n * This is what makes a portable `SceneDescriptor` build the SAME playground\n * on IWSDK as on plain three.js, and lets app behaviour be wired through\n * `onPanelReady` instead of hand-written ECS queries:\n *\n * const host = createSceneHost(world);\n * applyScene(host, PLAYGROUND);\n * host.onPanelReady(({ id, panel }) => wireMyPanel(id, panel));\n *\n * Readiness is detected with a single ECS query on `PanelUI + PanelDocument`\n * - the engine-specific mechanism stays here, behind the portable interface.\n * Bare panels (a `PanelUI` entity with no `UIWindow`) are announced too, with\n * `kind: 'panel'` and their config path as the id, so devtools and hand-built\n * entities show up in the same stream as managed windows.\n */\nimport {\n PanelDocument,\n PanelUI,\n UIKitDocument,\n createSystem,\n type Entity,\n type World,\n} from '@iwsdk/core';\nimport {\n upgradePanel,\n type PanelHandle,\n type PanelReadyEvent,\n type SceneRegion,\n type SceneTarget,\n type SceneWindow,\n type UixElement,\n type WindowHandle,\n type WindowHost,\n} from '@realitycollective/webxr-uiextensions';\nimport {\n createDockRegion,\n createUIWindow,\n type CreateWindowOptions,\n} from './factory.js';\nimport { UIWindow } from './components.js';\n\n/** Panel handle over an IWSDK `UIKitDocument`. */\nfunction panelHandleFor(document: UIKitDocument): PanelHandle {\n return {\n get root(): UixElement {\n return document.rootElement as unknown as UixElement;\n },\n getElementById: (id) =>\n (document.getElementById(id) as UixElement | null) ?? undefined,\n setTargetDimensions: (width, height) =>\n document.setTargetDimensions(width, height),\n dispose: () => {\n /* IWSDK owns the document lifecycle via the entity. */\n },\n };\n}\n\n/**\n * The live panel on an entity, or `undefined` while IWSDK is still loading\n * its document. Use it to reach a panel from an entity you already hold;\n * `onPanelReady` stays the way to be TOLD when one appears.\n */\nexport function getPanelHandle(entity: Entity): PanelHandle | undefined {\n const document = PanelDocument.data.document[entity.index] as\n | UIKitDocument\n | undefined;\n return document ? panelHandleFor(document) : undefined;\n}\n\n/**\n * Bridges panel loading into `onPanelReady`. Registered once per host; the\n * query fires as each panel's document is attached by IWSDK's UI system.\n */\nfunction createReadySystem(onReady: (entity: Entity) => void) {\n return class UixPanelReadySystem extends createSystem({\n panels: { required: [PanelUI, PanelDocument] },\n }) {\n override init(): void {\n this.cleanupFuncs.push(\n this.queries.panels.subscribe('qualify', (entity) => onReady(entity)),\n );\n this.queries.panels.entities.forEach((entity) => onReady(entity));\n }\n };\n}\n\n/** A window spawned by {@link IwsdkSceneHost.createWindow}. */\nexport interface IwsdkWindowHandle extends WindowHandle {\n /** The ECS entity carrying `PanelUI`, `UIWindow` and the interaction tags. */\n readonly entity: Entity;\n}\n\nexport interface IwsdkSceneHost extends WindowHost, SceneTarget {\n readonly world: World;\n /**\n * Spawn a managed window and get a handle back straight away. IWSDK loads\n * the markup over later frames, so `handle.panel` is `undefined` at first\n * and `handle.onReady` is how you wait for it without an ECS query.\n */\n createWindow(options: CreateWindowOptions): IwsdkWindowHandle;\n}\n\nconst hosts = new WeakMap<World, IwsdkSceneHost>();\n\n/**\n * The host for a world: built on the first call, and the same instance every\n * call after that. Call AFTER `registerUIExtensions(world)`.\n *\n * One host per world is deliberate. A host owns an ECS system, the record of\n * which documents it has already announced, and the panel upgrade pass, so a\n * second independent host on the same world would run a second copy of all\n * three. Two modules can each ask for the host without coordinating, exactly\n * as they do for the window manager registry.\n */\nexport function createSceneHost(world: World): IwsdkSceneHost {\n const existing = hosts.get(world);\n if (existing) {\n return existing;\n }\n const host = buildSceneHost(world);\n hosts.set(world, host);\n return host;\n}\n\nfunction buildSceneHost(world: World): IwsdkSceneHost {\n const readyListeners = new Set<(event: PanelReadyEvent) => void>();\n const ready = new Map<string, PanelReadyEvent>();\n const seen = new WeakSet<object>();\n\n const announce = (entity: Entity): void => {\n const document = PanelDocument.data.document[entity.index] as\n | UIKitDocument\n | undefined;\n if (!document || seen.has(document)) {\n return;\n }\n seen.add(document);\n // Window id comes from the UIWindow component the factory attached. A\n // panel without one is a bare panel - still worth announcing, keyed by\n // its config path, which is the only stable id IWSDK gives it.\n const windowId = entity.hasComponent(UIWindow)\n ? (UIWindow.data.windowId[entity.index] as string)\n : '';\n const kind: 'window' | 'panel' = windowId ? 'window' : 'panel';\n const id = windowId || (PanelUI.data.config[entity.index] as string);\n const panel = panelHandleFor(document);\n // Upgrade `data-uix` controls exactly as the ECS controls system does,\n // so wiring code sees the same handles on every adapter (idempotent).\n upgradePanel(document, document.rootElement as unknown as UixElement);\n const event: PanelReadyEvent = { id, panel, kind };\n ready.set(id, event);\n for (const listener of readyListeners) {\n try {\n listener(event);\n } catch (error) {\n console.error(`[uix] panel-ready listener failed for \"${id}\":`, error);\n }\n }\n };\n\n world.registerSystem(createReadySystem(announce));\n\n let windowSequence = 0;\n\n const host: IwsdkSceneHost = {\n world,\n\n // IWSDK owns panel lifecycles through the ECS, so there is no bare-panel\n // path here - `createPanel` throws rather than half-working.\n supportsStandalonePanels: false,\n\n createPanel(): PanelHandle {\n throw new Error(\n '[uix] createPanel() is not supported on the IWSDK host - spawn a window ' +\n '(spawnWindow/createUIWindow) so IWSDK owns the panel lifecycle.',\n );\n },\n\n onPanelReady(listener) {\n readyListeners.add(listener);\n for (const event of ready.values()) {\n listener(event);\n }\n return () => void readyListeners.delete(listener);\n },\n\n createWindow(options: CreateWindowOptions): IwsdkWindowHandle {\n const id = options.id ?? `uix-window-${(windowSequence += 1)}`;\n const entity = createUIWindow(world, { ...options, id });\n return {\n entity,\n id,\n get panel(): PanelHandle | undefined {\n return ready.get(id)?.panel;\n },\n onReady(listener: (panel: PanelHandle) => void): () => void {\n const existing = ready.get(id)?.panel;\n if (existing) {\n listener(existing);\n return () => {};\n }\n // `onPanelReady` replays panels that are already live, but this id\n // is not one of them, so the callback only ever runs later.\n const unsubscribe = host.onPanelReady((event) => {\n if (event.id === id) {\n unsubscribe();\n listener(event.panel);\n }\n });\n return unsubscribe;\n },\n };\n },\n\n spawnRegion(region: SceneRegion): void {\n createDockRegion(world, {\n id: region.id,\n ...(region.flow !== undefined ? { flow: region.flow } : {}),\n ...(region.pitch !== undefined ? { pitch: region.pitch } : {}),\n ...(region.columns !== undefined ? { columns: region.columns } : {}),\n ...(region.capacity !== undefined ? { capacity: region.capacity } : {}),\n ...(region.snapRadius !== undefined\n ? { snapRadius: region.snapRadius }\n : {}),\n ...(region.position !== undefined\n ? { position: [...region.position] as [number, number, number] }\n : {}),\n ...(region.follow !== undefined ? { follow: region.follow } : {}),\n ...(region.followOffset !== undefined\n ? { followOffset: [...region.followOffset] as [number, number, number] }\n : {}),\n });\n },\n\n spawnWindow(window: SceneWindow): void {\n host.createWindow({\n id: window.id,\n title: window.title,\n config: window.config,\n ...(window.position !== undefined\n ? { position: [...window.position] as [number, number, number] }\n : {}),\n ...(window.maxWidth !== undefined ? { maxWidth: window.maxWidth } : {}),\n ...(window.maxHeight !== undefined ? { maxHeight: window.maxHeight } : {}),\n ...(window.dockMode !== undefined ? { dockMode: window.dockMode } : {}),\n ...(window.region !== undefined ? { region: window.region } : {}),\n ...(window.followOffset !== undefined\n ? { followOffset: [...window.followOffset] as [number, number, number] }\n : {}),\n ...(window.followSpeed !== undefined\n ? { followSpeed: window.followSpeed }\n : {}),\n ...(window.followTolerance !== undefined\n ? { followTolerance: window.followTolerance }\n : {}),\n ...(window.movable !== undefined ? { movable: window.movable } : {}),\n ...(window.closable !== undefined ? { closable: window.closable } : {}),\n ...(window.minimizable !== undefined\n ? { minimizable: window.minimizable }\n : {}),\n ...(window.pinnable !== undefined ? { pinnable: window.pinnable } : {}),\n });\n },\n };\n\n return host;\n}\n"]}
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* windows become a body-locked toolbar; leave it static for a world-locked
|
|
7
7
|
* console wall.
|
|
8
8
|
*/
|
|
9
|
-
import {
|
|
9
|
+
import { createSystem } from '@iwsdk/core';
|
|
10
|
+
import { Quaternion, Vector3 } from 'three';
|
|
10
11
|
import { UIDockRegion, UIDockedTo, UIWindow } from '../components.js';
|
|
11
12
|
import { DockMode } from '@realitycollective/webxr-uiextensions';
|
|
12
13
|
import { regionRegistryFor } from '../region-registry-store.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dock-region-system.js","sourceRoot":"","sources":["../../src/systems/dock-region-system.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"dock-region-system.js","sourceRoot":"","sources":["../../src/systems/dock-region-system.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AAEjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAGhE,IAAI,YAAY,GAAG,CAAC,CAAC;AAErB,MAAM,OAAO,kBAAmB,SAAQ,YAAY,CAAC;IACnD,OAAO,EAAE,EAAE,QAAQ,EAAE,CAAC,YAAY,CAAC,EAAE;IACrC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE;CAC7C,CAAC;IACQ,QAAQ,CAAkB;IAC1B,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,YAAY,GAAG,IAAI,OAAO,EAAE,CAAC;IAC7B,gBAAgB,GAAG,IAAI,UAAU,EAAE,CAAC;IAEnC,IAAI;QACX,IAAI,CAAC,QAAQ,GAAG,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAE9C,IAAI,CAAC,YAAY,CAAC,IAAI,CACpB,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAC7E,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EACnF,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAC7E,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CACnF,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1E,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5E,CAAC;IAEQ,MAAM;QACb,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAClD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAW,CAAC;YACjE,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAClD,MAAM,YAAY,GAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC5F,MAAM,YAAY,GAAG,YAAY,EAAE,QAAQ,CAAC;YAC5C,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;YAC/B,IAAI,CAAC,MAAM,IAAI,CAAC,YAAY,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBAC1D,SAAS;YACX,CAAC;YACD,wEAAwE;YACxE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YACvD,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC7C,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACxC,YAAY,CAAC,kBAAkB,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;YACvD,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,MAAc;QAC9B,IAAI,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAW,CAAC;QAC7D,IAAI,CAAC,EAAE,EAAE,CAAC;YACR,EAAE,GAAG,cAAc,YAAY,EAAE,EAAE,CAAC;YACpC,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;QAChD,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAC1B,OAAO;QACT,CAAC;QACD,MAAM,UAAU,GAA8B;YAC5C,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAe;YACzD,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAW;YACvD,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAW,CAAC,CAAC;YACpF,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAW,CAAC,CAAC;YACtF,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAW;SAClE,CAAC;QACF,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QACvC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAEO,YAAY,CAAC,MAAc;QACjC,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAW,CAAC;QAC/D,IAAI,EAAE,IAAI,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC;YACjD,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAC7B,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;IAEO,UAAU,CAAC,MAAc;QAC/B,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAW,CAAC;QACjE,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAW,CAAC;QACnE,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3D,yEAAyE;YACzE,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;YACnC,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,mCAAmC;YACnC,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;YACnC,OAAO;QACT,CAAC;QACD,yEAAyE;QACzE,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC,KAAK,QAAQ,CAAC,WAAW,EAAE,CAAC;YACnE,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAEO,YAAY,CAAC,MAAc;QACjC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAW,CAAC;QACjE,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACjC,CAAC;IACH,CAAC;CACF","sourcesContent":["/**\n * UIDockRegionSystem - keeps the region registry in sync with `UIDockRegion`\n * entities and lays docked windows out in their region's slots each frame.\n *\n * Regions are ordinary entities: give one a `Follower` and its docked\n * windows become a body-locked toolbar; leave it static for a world-locked\n * console wall.\n */\nimport { createSystem, type Entity } from '@iwsdk/core';\nimport { Quaternion, Vector3 } from 'three';\nimport { UIDockRegion, UIDockedTo, UIWindow } from '../components.js';\nimport { DockMode } from '@realitycollective/webxr-uiextensions';\nimport type { RegionDefinition, RegionFlow } from '@realitycollective/webxr-uiextensions';\nimport { regionRegistryFor } from '../region-registry-store.js';\nimport type { RegionRegistry } from '@realitycollective/webxr-uiextensions';\n\nlet autoRegionId = 0;\n\nexport class UIDockRegionSystem extends createSystem({\n regions: { required: [UIDockRegion] },\n docked: { required: [UIWindow, UIDockedTo] },\n}) {\n private registry!: RegionRegistry;\n private regionEntities = new Map<string, Entity>();\n private slotPosition = new Vector3();\n private regionQuaternion = new Quaternion();\n\n override init(): void {\n this.registry = regionRegistryFor(this.world);\n\n this.cleanupFuncs.push(\n this.queries.regions.subscribe('qualify', (entity) => this.addRegion(entity)),\n this.queries.regions.subscribe('disqualify', (entity) => this.removeRegion(entity)),\n this.queries.docked.subscribe('qualify', (entity) => this.dockWindow(entity)),\n this.queries.docked.subscribe('disqualify', (entity) => this.undockWindow(entity)),\n );\n this.queries.regions.entities.forEach((entity) => this.addRegion(entity));\n this.queries.docked.entities.forEach((entity) => this.dockWindow(entity));\n }\n\n override update(): void {\n for (const entity of this.queries.docked.entities) {\n const windowId = entity.getValue(UIWindow, 'windowId') as string;\n const offset = this.registry.offsetOf(windowId);\n const regionId = this.registry.regionOf(windowId);\n const regionEntity = regionId === undefined ? undefined : this.regionEntities.get(regionId);\n const regionObject = regionEntity?.object3D;\n const object = entity.object3D;\n if (!offset || !regionObject || !object || !object.parent) {\n continue;\n }\n // Slot offset is region-local; express it in the window's parent space.\n this.slotPosition.set(offset[0], offset[1], offset[2]);\n regionObject.localToWorld(this.slotPosition);\n object.parent.worldToLocal(this.slotPosition);\n object.position.copy(this.slotPosition);\n regionObject.getWorldQuaternion(this.regionQuaternion);\n object.quaternion.copy(this.regionQuaternion);\n }\n }\n\n private addRegion(entity: Entity): void {\n let id = entity.getValue(UIDockRegion, 'regionId') as string;\n if (!id) {\n id = `uix-region-${autoRegionId++}`;\n entity.setValue(UIDockRegion, 'regionId', id);\n }\n if (this.registry.get(id)) {\n return;\n }\n const definition: Partial<RegionDefinition> = {\n flow: entity.getValue(UIDockRegion, 'flow') as RegionFlow,\n pitch: entity.getValue(UIDockRegion, 'pitch') as number,\n columns: Math.max(1, Math.round(entity.getValue(UIDockRegion, 'columns') as number)),\n capacity: Math.max(0, Math.round(entity.getValue(UIDockRegion, 'capacity') as number)),\n snapRadius: entity.getValue(UIDockRegion, 'snapRadius') as number,\n };\n this.registry.register(id, definition);\n this.regionEntities.set(id, entity);\n }\n\n private removeRegion(entity: Entity): void {\n const id = entity.getValue(UIDockRegion, 'regionId') as string;\n if (id && this.regionEntities.get(id) === entity) {\n this.registry.unregister(id);\n this.regionEntities.delete(id);\n }\n }\n\n private dockWindow(entity: Entity): void {\n const windowId = entity.getValue(UIWindow, 'windowId') as string;\n const regionId = entity.getValue(UIDockedTo, 'regionId') as string;\n if (!windowId || !regionId || !this.registry.get(regionId)) {\n // Unknown region (or the window has no id yet) - docking is meaningless.\n entity.removeComponent(UIDockedTo);\n return;\n }\n try {\n this.registry.dock(windowId, regionId);\n } catch {\n // Region is full: reject the dock.\n entity.removeComponent(UIDockedTo);\n return;\n }\n // Docked windows are world-locked by definition (the region may follow).\n if (entity.getValue(UIWindow, 'dockMode') !== DockMode.WorldLocked) {\n entity.setValue(UIWindow, 'dockMode', DockMode.WorldLocked);\n }\n }\n\n private undockWindow(entity: Entity): void {\n const windowId = entity.getValue(UIWindow, 'windowId') as string;\n if (windowId) {\n this.registry.undock(windowId);\n }\n }\n}\n"]}
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* `Follower` / `ScreenSpace` components (reuse, not recreation); this system
|
|
6
6
|
* only plans and applies transitions with the pure dock state machine.
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
8
|
+
import { Follower, FollowBehavior, ScreenSpace, createSystem, } from '@iwsdk/core';
|
|
9
|
+
import { Euler, Quaternion, Vector3 } from 'three';
|
|
9
10
|
import { UIWindow, UIWindowState } from '../components.js';
|
|
10
11
|
import { DockMode, isDockMode, planTransition, recipeFor, } from '@realitycollective/webxr-uiextensions';
|
|
11
12
|
import { faceViewerYaw } from '@realitycollective/webxr-uiextensions';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dock-system.js","sourceRoot":"","sources":["../../src/systems/dock-system.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * UIDockSystem - realises dock modes by composing IWSDK components.\n *\n * `body-follow` and `head-locked` are implemented with the IWSDK's own\n * `Follower` / `ScreenSpace` components (reuse, not recreation); this system\n * only plans and applies transitions with the pure dock state machine.\n */\nimport {\n Euler,\n Follower,\n FollowBehavior,\n Quaternion,\n ScreenSpace,\n Vector3,\n createSystem,\n type Entity,\n} from '@iwsdk/core';\nimport { UIWindow, UIWindowState } from '../components.js';\nimport {\n DockMode,\n isDockMode,\n planTransition,\n recipeFor,\n type DockModeValue,\n} from '@realitycollective/webxr-uiextensions';\nimport { faceViewerYaw } from '@realitycollective/webxr-uiextensions';\nimport { windowManagerFor } from '../manager-registry.js';\nimport type { WindowManager } from '@realitycollective/webxr-uiextensions';\n\nexport class UIDockSystem extends createSystem({\n windows: { required: [UIWindow, UIWindowState] },\n}) {\n private manager!: WindowManager;\n private windowPosition = new Vector3();\n private viewerPosition = new Vector3();\n\n override init(): void {\n this.manager = windowManagerFor(this.world);\n }\n\n override update(): void {\n for (const entity of this.queries.windows.entities) {\n const requested = entity.getValue(UIWindow, 'dockMode') as DockModeValue;\n const applied = entity.getValue(UIWindowState, 'appliedDockMode') as string;\n if (!isDockMode(requested) || applied === requested) {\n continue;\n }\n this.apply(entity, applied, requested);\n }\n }\n\n private apply(entity: Entity, applied: string, requested: DockModeValue): void {\n if (isDockMode(applied)) {\n const plan = planTransition(applied, requested);\n if (!plan) {\n return;\n }\n if (plan.removeFollower && entity.hasComponent(Follower)) {\n entity.removeComponent(Follower);\n }\n if (plan.removeFollower && requested === DockMode.WorldLocked) {\n // Pinning in place: leave the window facing the user at the spot it\n // was pinned, instead of freezing whatever lagging follow rotation\n // it happened to have this frame.\n this.faceViewer(entity);\n }\n if (plan.removeScreenSpace && entity.hasComponent(ScreenSpace)) {\n entity.removeComponent(ScreenSpace);\n }\n if (plan.addFollower) {\n // Unpinning: keep the window where the user left it, expressed\n // relative to their head, instead of snapping to the default offset.\n this.addFollower(entity, { fromCurrentPose: true });\n }\n if (plan.addScreenSpace) {\n entity.addComponent(ScreenSpace);\n }\n if (plan.snap && entity.hasComponent(Follower)) {\n entity.setValue(Follower, 'needsPositionSync', true);\n }\n } else {\n // First application after adoption: build the requested recipe from\n // scratch (the entity carries no dock components we own yet).\n const recipe = recipeFor(requested);\n if (recipe.follower && !entity.hasComponent(Follower)) {\n this.addFollower(entity);\n }\n if (!recipe.follower && entity.hasComponent(Follower)) {\n entity.removeComponent(Follower);\n }\n if (recipe.screenSpace && !entity.hasComponent(ScreenSpace)) {\n entity.addComponent(ScreenSpace);\n }\n if (!recipe.screenSpace && entity.hasComponent(ScreenSpace)) {\n entity.removeComponent(ScreenSpace);\n }\n }\n\n entity.setValue(UIWindowState, 'appliedDockMode', requested);\n\n const id = entity.getValue(UIWindow, 'windowId') as string;\n if (id && this.manager.has(id)) {\n this.manager.setDockMode(id, requested);\n }\n }\n\n private faceViewer(entity: Entity): void {\n const object = entity.object3D;\n if (!object) {\n return;\n }\n object.getWorldPosition(this.windowPosition);\n this.camera.getWorldPosition(this.viewerPosition);\n const yaw = faceViewerYaw(\n [this.windowPosition.x, this.windowPosition.y, this.windowPosition.z],\n [this.viewerPosition.x, this.viewerPosition.y, this.viewerPosition.z],\n object.rotation.y,\n );\n object.rotation.set(0, yaw, 0);\n }\n\n private offsetHelper = new Vector3();\n private yawQuaternion = new Quaternion();\n private yawEuler = new Euler();\n\n private addFollower(entity: Entity, options: { fromCurrentPose?: boolean } = {}): void {\n const configured = entity.getVectorView(UIWindow, 'followOffset');\n let offset: [number, number, number] = [\n configured[0] ?? 0,\n configured[1] ?? 0,\n configured[2] ?? 0,\n ];\n\n const object = entity.object3D;\n if (options.fromCurrentPose && object) {\n // Current window position in the viewer's yaw-local frame (matching\n // how Follower's PivotY applies offsets), so following resumes from\n // the same bearing and distance the window was unpinned at.\n object.getWorldPosition(this.offsetHelper);\n this.camera.getWorldPosition(this.windowPosition);\n this.camera.getWorldQuaternion(this.yawQuaternion);\n this.yawEuler.setFromQuaternion(this.yawQuaternion, 'YXZ');\n this.yawEuler.x = 0;\n this.yawEuler.z = 0;\n this.yawQuaternion.setFromEuler(this.yawEuler).conjugate();\n this.offsetHelper.sub(this.windowPosition).applyQuaternion(this.yawQuaternion);\n // Degenerate (window at the viewer): keep the configured offset.\n if (this.offsetHelper.lengthSq() > 0.01) {\n offset = [this.offsetHelper.x, this.offsetHelper.y, this.offsetHelper.z];\n }\n }\n\n entity.addComponent(Follower, {\n // The camera, not player.head: three.js syncs the camera to the\n // headset pose in-session, while the head rig sits at the origin\n // outside XR (desktop) - following it would park windows at the floor.\n target: this.camera,\n offsetPosition: offset,\n behavior: FollowBehavior.PivotY,\n speed: entity.getValue(UIWindow, 'followSpeed') as number,\n tolerance: entity.getValue(UIWindow, 'followTolerance') as number,\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"dock-system.js","sourceRoot":"","sources":["../../src/systems/dock-system.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * UIDockSystem - realises dock modes by composing IWSDK components.\n *\n * `body-follow` and `head-locked` are implemented with the IWSDK's own\n * `Follower` / `ScreenSpace` components (reuse, not recreation); this system\n * only plans and applies transitions with the pure dock state machine.\n */\nimport {\n Follower,\n FollowBehavior,\n ScreenSpace,\n createSystem,\n type Entity,\n} from '@iwsdk/core';\nimport { Euler, Quaternion, Vector3 } from 'three';\nimport { UIWindow, UIWindowState } from '../components.js';\nimport {\n DockMode,\n isDockMode,\n planTransition,\n recipeFor,\n type DockModeValue,\n} from '@realitycollective/webxr-uiextensions';\nimport { faceViewerYaw } from '@realitycollective/webxr-uiextensions';\nimport { windowManagerFor } from '../manager-registry.js';\nimport type { WindowManager } from '@realitycollective/webxr-uiextensions';\n\nexport class UIDockSystem extends createSystem({\n windows: { required: [UIWindow, UIWindowState] },\n}) {\n private manager!: WindowManager;\n private windowPosition = new Vector3();\n private viewerPosition = new Vector3();\n\n override init(): void {\n this.manager = windowManagerFor(this.world);\n }\n\n override update(): void {\n for (const entity of this.queries.windows.entities) {\n const requested = entity.getValue(UIWindow, 'dockMode') as DockModeValue;\n const applied = entity.getValue(UIWindowState, 'appliedDockMode') as string;\n if (!isDockMode(requested) || applied === requested) {\n continue;\n }\n this.apply(entity, applied, requested);\n }\n }\n\n private apply(entity: Entity, applied: string, requested: DockModeValue): void {\n if (isDockMode(applied)) {\n const plan = planTransition(applied, requested);\n if (!plan) {\n return;\n }\n if (plan.removeFollower && entity.hasComponent(Follower)) {\n entity.removeComponent(Follower);\n }\n if (plan.removeFollower && requested === DockMode.WorldLocked) {\n // Pinning in place: leave the window facing the user at the spot it\n // was pinned, instead of freezing whatever lagging follow rotation\n // it happened to have this frame.\n this.faceViewer(entity);\n }\n if (plan.removeScreenSpace && entity.hasComponent(ScreenSpace)) {\n entity.removeComponent(ScreenSpace);\n }\n if (plan.addFollower) {\n // Unpinning: keep the window where the user left it, expressed\n // relative to their head, instead of snapping to the default offset.\n this.addFollower(entity, { fromCurrentPose: true });\n }\n if (plan.addScreenSpace) {\n entity.addComponent(ScreenSpace);\n }\n if (plan.snap && entity.hasComponent(Follower)) {\n entity.setValue(Follower, 'needsPositionSync', true);\n }\n } else {\n // First application after adoption: build the requested recipe from\n // scratch (the entity carries no dock components we own yet).\n const recipe = recipeFor(requested);\n if (recipe.follower && !entity.hasComponent(Follower)) {\n this.addFollower(entity);\n }\n if (!recipe.follower && entity.hasComponent(Follower)) {\n entity.removeComponent(Follower);\n }\n if (recipe.screenSpace && !entity.hasComponent(ScreenSpace)) {\n entity.addComponent(ScreenSpace);\n }\n if (!recipe.screenSpace && entity.hasComponent(ScreenSpace)) {\n entity.removeComponent(ScreenSpace);\n }\n }\n\n entity.setValue(UIWindowState, 'appliedDockMode', requested);\n\n const id = entity.getValue(UIWindow, 'windowId') as string;\n if (id && this.manager.has(id)) {\n this.manager.setDockMode(id, requested);\n }\n }\n\n private faceViewer(entity: Entity): void {\n const object = entity.object3D;\n if (!object) {\n return;\n }\n object.getWorldPosition(this.windowPosition);\n this.camera.getWorldPosition(this.viewerPosition);\n const yaw = faceViewerYaw(\n [this.windowPosition.x, this.windowPosition.y, this.windowPosition.z],\n [this.viewerPosition.x, this.viewerPosition.y, this.viewerPosition.z],\n object.rotation.y,\n );\n object.rotation.set(0, yaw, 0);\n }\n\n private offsetHelper = new Vector3();\n private yawQuaternion = new Quaternion();\n private yawEuler = new Euler();\n\n private addFollower(entity: Entity, options: { fromCurrentPose?: boolean } = {}): void {\n const configured = entity.getVectorView(UIWindow, 'followOffset');\n let offset: [number, number, number] = [\n configured[0] ?? 0,\n configured[1] ?? 0,\n configured[2] ?? 0,\n ];\n\n const object = entity.object3D;\n if (options.fromCurrentPose && object) {\n // Current window position in the viewer's yaw-local frame (matching\n // how Follower's PivotY applies offsets), so following resumes from\n // the same bearing and distance the window was unpinned at.\n object.getWorldPosition(this.offsetHelper);\n this.camera.getWorldPosition(this.windowPosition);\n this.camera.getWorldQuaternion(this.yawQuaternion);\n this.yawEuler.setFromQuaternion(this.yawQuaternion, 'YXZ');\n this.yawEuler.x = 0;\n this.yawEuler.z = 0;\n this.yawQuaternion.setFromEuler(this.yawEuler).conjugate();\n this.offsetHelper.sub(this.windowPosition).applyQuaternion(this.yawQuaternion);\n // Degenerate (window at the viewer): keep the configured offset.\n if (this.offsetHelper.lengthSq() > 0.01) {\n offset = [this.offsetHelper.x, this.offsetHelper.y, this.offsetHelper.z];\n }\n }\n\n entity.addComponent(Follower, {\n // The camera, not player.head: three.js syncs the camera to the\n // headset pose in-session, while the head rig sits at the origin\n // outside XR (desktop) - following it would park windows at the floor.\n target: this.camera,\n offsetPosition: offset,\n behavior: FollowBehavior.PivotY,\n speed: entity.getValue(UIWindow, 'followSpeed') as number,\n tolerance: entity.getValue(UIWindow, 'followTolerance') as number,\n });\n }\n}\n"]}
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* switches to `world-locked`; on release near a region it docks there.
|
|
13
13
|
*/
|
|
14
14
|
import { HandleStore } from '@pmndrs/handle';
|
|
15
|
-
import { PanelDocument,
|
|
15
|
+
import { PanelDocument, createSystem, } from '@iwsdk/core';
|
|
16
|
+
import { Vector3 } from 'three';
|
|
16
17
|
import { UIDockRegion, UIDockedTo, UIWindow, UIWindowState } from '../components.js';
|
|
17
18
|
import { WINDOW_CHROME_IDS } from '@realitycollective/webxr-uiextensions';
|
|
18
19
|
import { DockMode } from '@realitycollective/webxr-uiextensions';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drag-system.js","sourceRoot":"","sources":["../../src/systems/drag-system.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * UIDragSystem - drag windows by the title bar.\n *\n * Reuses `@pmndrs/handle` (the exact library behind the IWSDK's grab\n * systems): a `HandleStore` targets the window entity's object while being\n * *bound* to the title-bar uikit element, so grabbing the title bar with the\n * ray (or a pinch) moves the whole window with the same feel as native IWSDK\n * grabbing. The system pumps its own stores, so it works even when the app\n * doesn't enable `features.grabbing`.\n *\n * Dragging implies placing: on grab the window leaves any dock region and\n * switches to `world-locked`; on release near a region it docks there.\n */\nimport { HandleStore } from '@pmndrs/handle';\nimport {\n PanelDocument,\n UIKitDocument,\n Object3D,\n Vector3,\n createSystem,\n type Entity,\n} from '@iwsdk/core';\nimport { UIDockRegion, UIDockedTo, UIWindow, UIWindowState } from '../components.js';\nimport { WINDOW_CHROME_IDS } from '@realitycollective/webxr-uiextensions';\nimport { DockMode } from '@realitycollective/webxr-uiextensions';\nimport { faceViewerYaw } from '@realitycollective/webxr-uiextensions';\nimport { HoldToDrag } from '@realitycollective/webxr-uiextensions';\nimport type { Vec3 } from '@realitycollective/webxr-uiextensions';\nimport { regionRegistryFor } from '../region-registry-store.js';\nimport { windowManagerFor } from '../manager-registry.js';\n\ninterface DragState {\n store: HandleStore<unknown>;\n hold: HoldToDrag;\n}\n\nexport class UIDragSystem extends createSystem({\n windows: { required: [UIWindow, UIWindowState, PanelDocument] },\n regions: { required: [UIDockRegion] },\n}) {\n private states = new Map<Entity, DragState>();\n private worldPosition = new Vector3();\n private viewerPosition = new Vector3();\n\n override init(): void {\n this.cleanupFuncs.push(\n this.queries.windows.subscribe('qualify', (entity) => {\n this.attach(entity);\n }),\n this.queries.windows.subscribe('disqualify', (entity) => {\n this.detach(entity);\n }),\n );\n this.queries.windows.entities.forEach((entity) => this.attach(entity));\n }\n\n override update(delta: number): void {\n for (const [entity, state] of this.states) {\n const held = state.store.inputState.size > 0;\n const { phase, began, ended } = state.hold.update(held, delta);\n\n if (began) {\n this.onDragStart(entity);\n }\n if (phase === 'dragging') {\n // The store only runs while an actual drag is in progress: presses\n // shorter than the hold delay never move the window (they're\n // clicks), and skipping the release-frame update stops the handle\n // from restoring its grab-time rotation over our billboard yaw.\n state.store.update(delta);\n this.whileDragging(entity);\n }\n if (ended) {\n this.onDragEnd(entity);\n }\n }\n }\n\n private attach(entity: Entity): void {\n if (this.states.has(entity) || !entity.getValue(UIWindow, 'movable')) {\n return;\n }\n const document = PanelDocument.data.document[entity.index] as\n | UIKitDocument\n | undefined;\n const titlebar = document?.getElementById(WINDOW_CHROME_IDS.titlebar);\n const object = entity.object3D;\n if (!titlebar || !object) {\n return;\n }\n const store = new HandleStore(object, () => ({\n translate: true as const,\n rotate: false as const,\n scale: false as const,\n multitouch: false,\n projectRays: false,\n }));\n // The title-bar uikit element is an Object3D - bind it as the grab\n // surface while the store's target stays the window root.\n store.bind(titlebar as unknown as Object3D<never>);\n this.states.set(entity, {\n store,\n hold: new HoldToDrag(Math.max(0, entity.getValue(UIWindow, 'dragDelay') as number)),\n });\n }\n\n private detach(entity: Entity): void {\n const state = this.states.get(entity);\n if (!state) {\n return;\n }\n try {\n state.store.cancel();\n } catch {\n // Handle stores throw when cancelled with no active input; ignore.\n }\n this.states.delete(entity);\n }\n\n private onDragStart(entity: Entity): void {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n const manager = windowManagerFor(this.world);\n if (id && manager.has(id)) {\n manager.focus(id);\n // Mark dragging BEFORE the dock change so the pin label reads \"PIN\"\n // (loose in hand) throughout the drag, not \"UNPIN\".\n manager.setDragging(id, true);\n }\n // Grabbing takes the window out of any region and out of follow mode -\n // the user is placing it by hand.\n if (entity.hasComponent(UIDockedTo)) {\n entity.removeComponent(UIDockedTo);\n }\n if (entity.getValue(UIWindow, 'dockMode') !== DockMode.WorldLocked) {\n entity.setValue(UIWindow, 'dockMode', DockMode.WorldLocked);\n }\n }\n\n private whileDragging(entity: Entity): void {\n if (!entity.getValue(UIWindow, 'billboardWhileDragging')) {\n return;\n }\n const object = entity.object3D;\n if (!object) {\n return;\n }\n object.getWorldPosition(this.worldPosition);\n this.camera.getWorldPosition(this.viewerPosition);\n const yaw = faceViewerYaw(\n [this.worldPosition.x, this.worldPosition.y, this.worldPosition.z],\n [this.viewerPosition.x, this.viewerPosition.y, this.viewerPosition.z],\n object.rotation.y,\n );\n object.rotation.set(0, yaw, 0);\n }\n\n private onDragEnd(entity: Entity): void {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n const manager = windowManagerFor(this.world);\n if (id && manager.has(id)) {\n manager.setDragging(id, false); // released → placed → label \"UNPIN\"\n }\n // Settle facing the viewer at the drop spot - the window keeps the\n // orientation it was dragged in, not its pre-drag rotation.\n this.whileDragging(entity);\n const object = entity.object3D;\n if (!object) {\n return;\n }\n object.getWorldPosition(this.worldPosition);\n const dropPoint: Vec3 = [\n this.worldPosition.x,\n this.worldPosition.y,\n this.worldPosition.z,\n ];\n\n const registry = regionRegistryFor(this.world);\n const origins = new Map<string, Vec3>();\n this.queries.regions.entities.forEach((regionEntity) => {\n const regionId = regionEntity.getValue(UIDockRegion, 'regionId') as string;\n const regionObject = regionEntity.object3D;\n if (!regionId || !regionObject) {\n return;\n }\n regionObject.getWorldPosition(this.viewerPosition);\n origins.set(regionId, [\n this.viewerPosition.x,\n this.viewerPosition.y,\n this.viewerPosition.z,\n ]);\n });\n\n const captured = registry.capture(dropPoint, origins);\n if (captured !== undefined) {\n entity.addComponent(UIDockedTo, { regionId: captured });\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"drag-system.js","sourceRoot":"","sources":["../../src/systems/drag-system.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * UIDragSystem - drag windows by the title bar.\n *\n * Reuses `@pmndrs/handle` (the exact library behind the IWSDK's grab\n * systems): a `HandleStore` targets the window entity's object while being\n * *bound* to the title-bar uikit element, so grabbing the title bar with the\n * ray (or a pinch) moves the whole window with the same feel as native IWSDK\n * grabbing. The system pumps its own stores, so it works even when the app\n * doesn't enable `features.grabbing`.\n *\n * Dragging implies placing: on grab the window leaves any dock region and\n * switches to `world-locked`; on release near a region it docks there.\n */\nimport { HandleStore } from '@pmndrs/handle';\nimport {\n PanelDocument,\n UIKitDocument,\n createSystem,\n type Entity,\n} from '@iwsdk/core';\nimport { Vector3, type Object3D } from 'three';\nimport { UIDockRegion, UIDockedTo, UIWindow, UIWindowState } from '../components.js';\nimport { WINDOW_CHROME_IDS } from '@realitycollective/webxr-uiextensions';\nimport { DockMode } from '@realitycollective/webxr-uiextensions';\nimport { faceViewerYaw } from '@realitycollective/webxr-uiextensions';\nimport { HoldToDrag } from '@realitycollective/webxr-uiextensions';\nimport type { Vec3 } from '@realitycollective/webxr-uiextensions';\nimport { regionRegistryFor } from '../region-registry-store.js';\nimport { windowManagerFor } from '../manager-registry.js';\n\ninterface DragState {\n store: HandleStore<unknown>;\n hold: HoldToDrag;\n}\n\nexport class UIDragSystem extends createSystem({\n windows: { required: [UIWindow, UIWindowState, PanelDocument] },\n regions: { required: [UIDockRegion] },\n}) {\n private states = new Map<Entity, DragState>();\n private worldPosition = new Vector3();\n private viewerPosition = new Vector3();\n\n override init(): void {\n this.cleanupFuncs.push(\n this.queries.windows.subscribe('qualify', (entity) => {\n this.attach(entity);\n }),\n this.queries.windows.subscribe('disqualify', (entity) => {\n this.detach(entity);\n }),\n );\n this.queries.windows.entities.forEach((entity) => this.attach(entity));\n }\n\n override update(delta: number): void {\n for (const [entity, state] of this.states) {\n const held = state.store.inputState.size > 0;\n const { phase, began, ended } = state.hold.update(held, delta);\n\n if (began) {\n this.onDragStart(entity);\n }\n if (phase === 'dragging') {\n // The store only runs while an actual drag is in progress: presses\n // shorter than the hold delay never move the window (they're\n // clicks), and skipping the release-frame update stops the handle\n // from restoring its grab-time rotation over our billboard yaw.\n state.store.update(delta);\n this.whileDragging(entity);\n }\n if (ended) {\n this.onDragEnd(entity);\n }\n }\n }\n\n private attach(entity: Entity): void {\n if (this.states.has(entity) || !entity.getValue(UIWindow, 'movable')) {\n return;\n }\n const document = PanelDocument.data.document[entity.index] as\n | UIKitDocument\n | undefined;\n const titlebar = document?.getElementById(WINDOW_CHROME_IDS.titlebar);\n const object = entity.object3D;\n if (!titlebar || !object) {\n return;\n }\n const store = new HandleStore(object, () => ({\n translate: true as const,\n rotate: false as const,\n scale: false as const,\n multitouch: false,\n projectRays: false,\n }));\n // The title-bar uikit element is an Object3D - bind it as the grab\n // surface while the store's target stays the window root.\n store.bind(titlebar as unknown as Object3D<never>);\n this.states.set(entity, {\n store,\n hold: new HoldToDrag(Math.max(0, entity.getValue(UIWindow, 'dragDelay') as number)),\n });\n }\n\n private detach(entity: Entity): void {\n const state = this.states.get(entity);\n if (!state) {\n return;\n }\n try {\n state.store.cancel();\n } catch {\n // Handle stores throw when cancelled with no active input; ignore.\n }\n this.states.delete(entity);\n }\n\n private onDragStart(entity: Entity): void {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n const manager = windowManagerFor(this.world);\n if (id && manager.has(id)) {\n manager.focus(id);\n // Mark dragging BEFORE the dock change so the pin label reads \"PIN\"\n // (loose in hand) throughout the drag, not \"UNPIN\".\n manager.setDragging(id, true);\n }\n // Grabbing takes the window out of any region and out of follow mode -\n // the user is placing it by hand.\n if (entity.hasComponent(UIDockedTo)) {\n entity.removeComponent(UIDockedTo);\n }\n if (entity.getValue(UIWindow, 'dockMode') !== DockMode.WorldLocked) {\n entity.setValue(UIWindow, 'dockMode', DockMode.WorldLocked);\n }\n }\n\n private whileDragging(entity: Entity): void {\n if (!entity.getValue(UIWindow, 'billboardWhileDragging')) {\n return;\n }\n const object = entity.object3D;\n if (!object) {\n return;\n }\n object.getWorldPosition(this.worldPosition);\n this.camera.getWorldPosition(this.viewerPosition);\n const yaw = faceViewerYaw(\n [this.worldPosition.x, this.worldPosition.y, this.worldPosition.z],\n [this.viewerPosition.x, this.viewerPosition.y, this.viewerPosition.z],\n object.rotation.y,\n );\n object.rotation.set(0, yaw, 0);\n }\n\n private onDragEnd(entity: Entity): void {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n const manager = windowManagerFor(this.world);\n if (id && manager.has(id)) {\n manager.setDragging(id, false); // released → placed → label \"UNPIN\"\n }\n // Settle facing the viewer at the drop spot - the window keeps the\n // orientation it was dragged in, not its pre-drag rotation.\n this.whileDragging(entity);\n const object = entity.object3D;\n if (!object) {\n return;\n }\n object.getWorldPosition(this.worldPosition);\n const dropPoint: Vec3 = [\n this.worldPosition.x,\n this.worldPosition.y,\n this.worldPosition.z,\n ];\n\n const registry = regionRegistryFor(this.world);\n const origins = new Map<string, Vec3>();\n this.queries.regions.entities.forEach((regionEntity) => {\n const regionId = regionEntity.getValue(UIDockRegion, 'regionId') as string;\n const regionObject = regionEntity.object3D;\n if (!regionId || !regionObject) {\n return;\n }\n regionObject.getWorldPosition(this.viewerPosition);\n origins.set(regionId, [\n this.viewerPosition.x,\n this.viewerPosition.y,\n this.viewerPosition.z,\n ]);\n });\n\n const captured = registry.capture(dropPoint, origins);\n if (captured !== undefined) {\n entity.addComponent(UIDockedTo, { regionId: captured });\n }\n }\n}\n"]}
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* `PanelUISystem`; this system only discovers the chrome elements by their
|
|
7
7
|
* well-known ids and drives them.
|
|
8
8
|
*/
|
|
9
|
-
import { PanelDocument, PanelUI,
|
|
9
|
+
import { PanelDocument, PanelUI, createSystem, } from '@iwsdk/core';
|
|
10
|
+
import { Vector3 } from 'three';
|
|
10
11
|
import { UIDockedTo, UIWindow, UIWindowState } from '../components.js';
|
|
11
12
|
import { WINDOW_CHROME_IDS } from '@realitycollective/webxr-uiextensions';
|
|
12
13
|
import { windowManagerFor } from '../manager-registry.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"window-system.js","sourceRoot":"","sources":["../../src/systems/window-system.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * UIWindowSystem - wires window chrome, keeps the `WindowManager` in sync\n * with entities, applies focus ordering and minimize state.\n *\n * Reuse over recreation: the panel itself is loaded/rendered by the IWSDK's\n * `PanelUISystem`; this system only discovers the chrome elements by their\n * well-known ids and drives them.\n */\nimport {\n PanelDocument,\n PanelUI,\n UIKitDocument,\n Vector3,\n createSystem,\n type Entity,\n} from '@iwsdk/core';\nimport { UIDockedTo, UIWindow, UIWindowState } from '../components.js';\nimport { WINDOW_CHROME_IDS } from '@realitycollective/webxr-uiextensions';\nimport { windowManagerFor } from '../manager-registry.js';\nimport {\n minimizeLabelFor,\n pinLabelFor,\n type WindowManager,\n type WindowRecord,\n} from '@realitycollective/webxr-uiextensions';\nimport { DockMode, isDockMode, type DockModeValue } from '@realitycollective/webxr-uiextensions';\n\ntype UikitElement = {\n addEventListener: (type: string, listener: (event?: unknown) => void) => void;\n setProperties: (props: Record<string, unknown>) => void;\n};\n\nlet autoId = 0;\n\nexport class UIWindowSystem extends createSystem({\n pendingWindows: {\n required: [UIWindow, PanelUI, PanelDocument],\n excluded: [UIWindowState],\n },\n windows: { required: [UIWindow, UIWindowState, PanelDocument] },\n}) {\n private manager!: WindowManager;\n private entitiesById = new Map<string, Entity>();\n private localCamera = new Vector3();\n\n override init(): void {\n this.manager = windowManagerFor(this.world);\n\n this.cleanupFuncs.push(\n this.queries.pendingWindows.subscribe('qualify', (entity) => {\n this.adoptWindow(entity);\n }),\n this.queries.windows.subscribe('disqualify', (entity) => {\n this.releaseWindow(entity);\n }),\n this.manager.events.on('minimized', ({ id }) => this.applyMinimized(id, true)),\n this.manager.events.on('restored', ({ id }) => this.applyMinimized(id, false)),\n this.manager.events.on('minimized', (record) => this.syncMinimizeLabel(record)),\n this.manager.events.on('restored', (record) => this.syncMinimizeLabel(record)),\n this.manager.events.on('dockChanged', ({ window }) => {\n const entity = this.entitiesById.get(window.id);\n if (entity && entity.getValue(UIWindow, 'dockMode') !== window.dockMode) {\n entity.setValue(UIWindow, 'dockMode', window.dockMode);\n }\n this.syncPinLabel(window);\n }),\n this.manager.events.on('dragStarted', (window) => this.syncPinLabel(window)),\n this.manager.events.on('dragEnded', (window) => this.syncPinLabel(window)),\n );\n }\n\n override update(): void {\n // Focus bias: nudge each window's document toward the viewer by a small\n // amount inversely related to focus depth, so overlapping windows resolve\n // in focus order. Computed in entity-local space so it holds for any\n // window orientation.\n for (const entity of this.queries.windows.entities) {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n if (!this.manager.has(id)) {\n continue;\n }\n const document = this.documentOf(entity);\n const object = entity.object3D;\n if (!document || !object) {\n continue;\n }\n const bias = entity.getValue(UIWindow, 'focusBias') as number;\n const depth = this.manager.orderOf(id);\n const amount = bias * Math.max(0, this.manager.count - 1 - depth);\n if (amount === 0) {\n document.position.set(0, 0, 0);\n continue;\n }\n this.camera.getWorldPosition(this.localCamera);\n object.worldToLocal(this.localCamera);\n if (this.localCamera.lengthSq() === 0) {\n continue;\n }\n document.position.copy(this.localCamera.normalize().multiplyScalar(amount));\n }\n }\n\n entityFor(windowId: string): Entity | undefined {\n return this.entitiesById.get(windowId);\n }\n\n private adoptWindow(entity: Entity): void {\n let id = entity.getValue(UIWindow, 'windowId') as string;\n if (!id) {\n id = `uix-window-${autoId++}`;\n entity.setValue(UIWindow, 'windowId', id);\n }\n const title = entity.getValue(UIWindow, 'title') as string;\n const dockMode = entity.getValue(UIWindow, 'dockMode') as DockModeValue;\n this.entitiesById.set(id, entity);\n if (!this.manager.has(id)) {\n this.manager.open(id, { title, dockMode });\n }\n this.wireChrome(entity, id, title);\n\n // Snapshot \"home\" so the DOCK button can return the window: the spawn\n // region if it was born docked, otherwise its placement + dock mode.\n const object = entity.object3D;\n entity.addComponent(UIWindowState, {\n chromeWired: true,\n appliedDockMode: '',\n homeDockMode: dockMode,\n homeRegion: entity.hasComponent(UIDockedTo)\n ? (entity.getValue(UIDockedTo, 'regionId') as string)\n : '',\n homePosition: object ? [object.position.x, object.position.y, object.position.z] : [0, 0, 0],\n homeYaw: object ? object.rotation.y : 0,\n });\n this.syncPinLabel({ id, dockMode, dragging: false });\n }\n\n /** Return a window to its home: spawn region, or original placement/mode. */\n returnHome(entity: Entity): void {\n const homeRegion = entity.getValue(UIWindowState, 'homeRegion') as string;\n if (homeRegion) {\n if (\n entity.hasComponent(UIDockedTo) &&\n entity.getValue(UIDockedTo, 'regionId') === homeRegion\n ) {\n return; // already home\n }\n if (entity.hasComponent(UIDockedTo)) {\n entity.removeComponent(UIDockedTo);\n }\n entity.addComponent(UIDockedTo, { regionId: homeRegion });\n return;\n }\n if (entity.hasComponent(UIDockedTo)) {\n entity.removeComponent(UIDockedTo);\n }\n const homeDockMode = entity.getValue(UIWindowState, 'homeDockMode') as string;\n if (isDockMode(homeDockMode)) {\n entity.setValue(UIWindow, 'dockMode', homeDockMode);\n }\n if (homeDockMode === DockMode.WorldLocked) {\n const object = entity.object3D;\n const home = entity.getVectorView(UIWindowState, 'homePosition');\n if (object) {\n object.position.set(home[0] ?? 0, home[1] ?? 0, home[2] ?? 0);\n object.rotation.set(0, entity.getValue(UIWindowState, 'homeYaw') as number, 0);\n }\n }\n // Follow modes re-snap on their own (the dock system syncs the Follower).\n }\n\n /** MIN when open, MAX when minimized - the label names the next action. */\n private syncMinimizeLabel(record: Pick<WindowRecord, 'id' | 'minimized'>): void {\n const entity = this.entitiesById.get(record.id);\n const minimize = entity\n ? (this.documentOf(entity)?.getElementById(WINDOW_CHROME_IDS.minimize) as\n | UikitElement\n | undefined)\n : undefined;\n minimize?.setProperties({ text: minimizeLabelFor(record) });\n }\n\n private syncPinLabel(record: Pick<WindowRecord, 'id' | 'dockMode' | 'dragging'>): void {\n const entity = this.entitiesById.get(record.id);\n const pin = entity\n ? (this.documentOf(entity)?.getElementById(WINDOW_CHROME_IDS.pin) as\n | UikitElement\n | undefined)\n : undefined;\n pin?.setProperties({ text: pinLabelFor(record) });\n }\n\n private releaseWindow(entity: Entity): void {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n if (id && this.entitiesById.get(id) === entity) {\n this.entitiesById.delete(id);\n if (this.manager.has(id)) {\n this.manager.close(id);\n }\n }\n }\n\n private wireChrome(entity: Entity, id: string, title: string): void {\n const document = this.documentOf(entity);\n if (!document) {\n return;\n }\n\n // IWSDK 0.5 removed PanelUI.maxWidth/maxHeight; world size is now the\n // document's own concern. Fit into the requested box, preserving aspect\n // ratio. Zero means \"leave intrinsic\" so the entity transform owns scale.\n const targetWidth = entity.getValue(UIWindow, 'targetWidth') ?? 0;\n const targetHeight = entity.getValue(UIWindow, 'targetHeight') ?? 0;\n if (targetWidth > 0 && targetHeight > 0) {\n document.setTargetDimensions(targetWidth, targetHeight);\n }\n\n const element = (elementId: string): UikitElement | undefined =>\n document.getElementById(elementId) as UikitElement | undefined;\n\n const titleEl = element(WINDOW_CHROME_IDS.title);\n if (titleEl && title) {\n titleEl.setProperties({ text: title });\n }\n\n // Chrome buttons live on the drag surface - keep their presses out of\n // the title-bar drag handle so clicks never turn into (or arm) drags.\n const swallowPress = (button: UikitElement | undefined): void => {\n button?.addEventListener('pointerdown', (event) => {\n (event as { stopPropagation?: () => void } | undefined)?.stopPropagation?.();\n });\n };\n swallowPress(element(WINDOW_CHROME_IDS.pin));\n swallowPress(element(WINDOW_CHROME_IDS.dock));\n swallowPress(element(WINDOW_CHROME_IDS.minimize));\n swallowPress(element(WINDOW_CHROME_IDS.close));\n\n // Any press anywhere on the window brings it to the front.\n const root = element(WINDOW_CHROME_IDS.window);\n root?.addEventListener('pointerdown', () => {\n if (this.manager.has(id)) {\n this.manager.focus(id);\n }\n });\n\n const close = element(WINDOW_CHROME_IDS.close);\n if (close) {\n if (entity.getValue(UIWindow, 'closable')) {\n close.addEventListener('click', () => {\n // Destroying the entity disqualifies it everywhere; releaseWindow\n // then closes the manager record.\n entity.destroy();\n });\n } else {\n close.setProperties({ display: 'none' });\n }\n }\n\n const minimize = element(WINDOW_CHROME_IDS.minimize);\n if (minimize) {\n if (entity.getValue(UIWindow, 'minimizable')) {\n minimize.addEventListener('click', () => {\n if (this.manager.has(id)) {\n this.manager.toggleMinimized(id);\n }\n });\n } else {\n minimize.setProperties({ display: 'none' });\n }\n }\n\n const pin = element(WINDOW_CHROME_IDS.pin);\n if (pin) {\n if (entity.getValue(UIWindow, 'pinnable')) {\n pin.addEventListener('click', () => {\n if (this.manager.has(id)) {\n this.manager.togglePin(id);\n }\n });\n } else {\n pin.setProperties({ display: 'none' });\n }\n }\n\n const dock = element(WINDOW_CHROME_IDS.dock);\n dock?.addEventListener('click', () => this.returnHome(entity));\n }\n\n private applyMinimized(id: string, minimized: boolean): void {\n const entity = this.entitiesById.get(id);\n const document = entity ? this.documentOf(entity) : undefined;\n const content = document?.getElementById(WINDOW_CHROME_IDS.content) as\n | UikitElement\n | undefined;\n content?.setProperties({ display: minimized ? 'none' : 'flex' });\n }\n\n private documentOf(entity: Entity): UIKitDocument | undefined {\n return PanelDocument.data.document[entity.index] as UIKitDocument | undefined;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"window-system.js","sourceRoot":"","sources":["../../src/systems/window-system.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * UIWindowSystem - wires window chrome, keeps the `WindowManager` in sync\n * with entities, applies focus ordering and minimize state.\n *\n * Reuse over recreation: the panel itself is loaded/rendered by the IWSDK's\n * `PanelUISystem`; this system only discovers the chrome elements by their\n * well-known ids and drives them.\n */\nimport {\n PanelDocument,\n PanelUI,\n UIKitDocument,\n createSystem,\n type Entity,\n} from '@iwsdk/core';\nimport { Vector3 } from 'three';\nimport { UIDockedTo, UIWindow, UIWindowState } from '../components.js';\nimport { WINDOW_CHROME_IDS } from '@realitycollective/webxr-uiextensions';\nimport { windowManagerFor } from '../manager-registry.js';\nimport {\n minimizeLabelFor,\n pinLabelFor,\n type WindowManager,\n type WindowRecord,\n} from '@realitycollective/webxr-uiextensions';\nimport { DockMode, isDockMode, type DockModeValue } from '@realitycollective/webxr-uiextensions';\n\ntype UikitElement = {\n addEventListener: (type: string, listener: (event?: unknown) => void) => void;\n setProperties: (props: Record<string, unknown>) => void;\n};\n\nlet autoId = 0;\n\nexport class UIWindowSystem extends createSystem({\n pendingWindows: {\n required: [UIWindow, PanelUI, PanelDocument],\n excluded: [UIWindowState],\n },\n windows: { required: [UIWindow, UIWindowState, PanelDocument] },\n}) {\n private manager!: WindowManager;\n private entitiesById = new Map<string, Entity>();\n private localCamera = new Vector3();\n\n override init(): void {\n this.manager = windowManagerFor(this.world);\n\n this.cleanupFuncs.push(\n this.queries.pendingWindows.subscribe('qualify', (entity) => {\n this.adoptWindow(entity);\n }),\n this.queries.windows.subscribe('disqualify', (entity) => {\n this.releaseWindow(entity);\n }),\n this.manager.events.on('minimized', ({ id }) => this.applyMinimized(id, true)),\n this.manager.events.on('restored', ({ id }) => this.applyMinimized(id, false)),\n this.manager.events.on('minimized', (record) => this.syncMinimizeLabel(record)),\n this.manager.events.on('restored', (record) => this.syncMinimizeLabel(record)),\n this.manager.events.on('dockChanged', ({ window }) => {\n const entity = this.entitiesById.get(window.id);\n if (entity && entity.getValue(UIWindow, 'dockMode') !== window.dockMode) {\n entity.setValue(UIWindow, 'dockMode', window.dockMode);\n }\n this.syncPinLabel(window);\n }),\n this.manager.events.on('dragStarted', (window) => this.syncPinLabel(window)),\n this.manager.events.on('dragEnded', (window) => this.syncPinLabel(window)),\n );\n }\n\n override update(): void {\n // Focus bias: nudge each window's document toward the viewer by a small\n // amount inversely related to focus depth, so overlapping windows resolve\n // in focus order. Computed in entity-local space so it holds for any\n // window orientation.\n for (const entity of this.queries.windows.entities) {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n if (!this.manager.has(id)) {\n continue;\n }\n const document = this.documentOf(entity);\n const object = entity.object3D;\n if (!document || !object) {\n continue;\n }\n const bias = entity.getValue(UIWindow, 'focusBias') as number;\n const depth = this.manager.orderOf(id);\n const amount = bias * Math.max(0, this.manager.count - 1 - depth);\n if (amount === 0) {\n document.position.set(0, 0, 0);\n continue;\n }\n this.camera.getWorldPosition(this.localCamera);\n object.worldToLocal(this.localCamera);\n if (this.localCamera.lengthSq() === 0) {\n continue;\n }\n document.position.copy(this.localCamera.normalize().multiplyScalar(amount));\n }\n }\n\n entityFor(windowId: string): Entity | undefined {\n return this.entitiesById.get(windowId);\n }\n\n private adoptWindow(entity: Entity): void {\n let id = entity.getValue(UIWindow, 'windowId') as string;\n if (!id) {\n id = `uix-window-${autoId++}`;\n entity.setValue(UIWindow, 'windowId', id);\n }\n const title = entity.getValue(UIWindow, 'title') as string;\n const dockMode = entity.getValue(UIWindow, 'dockMode') as DockModeValue;\n this.entitiesById.set(id, entity);\n if (!this.manager.has(id)) {\n this.manager.open(id, { title, dockMode });\n }\n this.wireChrome(entity, id, title);\n\n // Snapshot \"home\" so the DOCK button can return the window: the spawn\n // region if it was born docked, otherwise its placement + dock mode.\n const object = entity.object3D;\n entity.addComponent(UIWindowState, {\n chromeWired: true,\n appliedDockMode: '',\n homeDockMode: dockMode,\n homeRegion: entity.hasComponent(UIDockedTo)\n ? (entity.getValue(UIDockedTo, 'regionId') as string)\n : '',\n homePosition: object ? [object.position.x, object.position.y, object.position.z] : [0, 0, 0],\n homeYaw: object ? object.rotation.y : 0,\n });\n this.syncPinLabel({ id, dockMode, dragging: false });\n }\n\n /** Return a window to its home: spawn region, or original placement/mode. */\n returnHome(entity: Entity): void {\n const homeRegion = entity.getValue(UIWindowState, 'homeRegion') as string;\n if (homeRegion) {\n if (\n entity.hasComponent(UIDockedTo) &&\n entity.getValue(UIDockedTo, 'regionId') === homeRegion\n ) {\n return; // already home\n }\n if (entity.hasComponent(UIDockedTo)) {\n entity.removeComponent(UIDockedTo);\n }\n entity.addComponent(UIDockedTo, { regionId: homeRegion });\n return;\n }\n if (entity.hasComponent(UIDockedTo)) {\n entity.removeComponent(UIDockedTo);\n }\n const homeDockMode = entity.getValue(UIWindowState, 'homeDockMode') as string;\n if (isDockMode(homeDockMode)) {\n entity.setValue(UIWindow, 'dockMode', homeDockMode);\n }\n if (homeDockMode === DockMode.WorldLocked) {\n const object = entity.object3D;\n const home = entity.getVectorView(UIWindowState, 'homePosition');\n if (object) {\n object.position.set(home[0] ?? 0, home[1] ?? 0, home[2] ?? 0);\n object.rotation.set(0, entity.getValue(UIWindowState, 'homeYaw') as number, 0);\n }\n }\n // Follow modes re-snap on their own (the dock system syncs the Follower).\n }\n\n /** MIN when open, MAX when minimized - the label names the next action. */\n private syncMinimizeLabel(record: Pick<WindowRecord, 'id' | 'minimized'>): void {\n const entity = this.entitiesById.get(record.id);\n const minimize = entity\n ? (this.documentOf(entity)?.getElementById(WINDOW_CHROME_IDS.minimize) as\n | UikitElement\n | undefined)\n : undefined;\n minimize?.setProperties({ text: minimizeLabelFor(record) });\n }\n\n private syncPinLabel(record: Pick<WindowRecord, 'id' | 'dockMode' | 'dragging'>): void {\n const entity = this.entitiesById.get(record.id);\n const pin = entity\n ? (this.documentOf(entity)?.getElementById(WINDOW_CHROME_IDS.pin) as\n | UikitElement\n | undefined)\n : undefined;\n pin?.setProperties({ text: pinLabelFor(record) });\n }\n\n private releaseWindow(entity: Entity): void {\n const id = entity.getValue(UIWindow, 'windowId') as string;\n if (id && this.entitiesById.get(id) === entity) {\n this.entitiesById.delete(id);\n if (this.manager.has(id)) {\n this.manager.close(id);\n }\n }\n }\n\n private wireChrome(entity: Entity, id: string, title: string): void {\n const document = this.documentOf(entity);\n if (!document) {\n return;\n }\n\n // IWSDK 0.5 removed PanelUI.maxWidth/maxHeight; world size is now the\n // document's own concern. Fit into the requested box, preserving aspect\n // ratio. Zero means \"leave intrinsic\" so the entity transform owns scale.\n const targetWidth = entity.getValue(UIWindow, 'targetWidth') ?? 0;\n const targetHeight = entity.getValue(UIWindow, 'targetHeight') ?? 0;\n if (targetWidth > 0 && targetHeight > 0) {\n document.setTargetDimensions(targetWidth, targetHeight);\n }\n\n const element = (elementId: string): UikitElement | undefined =>\n document.getElementById(elementId) as UikitElement | undefined;\n\n const titleEl = element(WINDOW_CHROME_IDS.title);\n if (titleEl && title) {\n titleEl.setProperties({ text: title });\n }\n\n // Chrome buttons live on the drag surface - keep their presses out of\n // the title-bar drag handle so clicks never turn into (or arm) drags.\n const swallowPress = (button: UikitElement | undefined): void => {\n button?.addEventListener('pointerdown', (event) => {\n (event as { stopPropagation?: () => void } | undefined)?.stopPropagation?.();\n });\n };\n swallowPress(element(WINDOW_CHROME_IDS.pin));\n swallowPress(element(WINDOW_CHROME_IDS.dock));\n swallowPress(element(WINDOW_CHROME_IDS.minimize));\n swallowPress(element(WINDOW_CHROME_IDS.close));\n\n // Any press anywhere on the window brings it to the front.\n const root = element(WINDOW_CHROME_IDS.window);\n root?.addEventListener('pointerdown', () => {\n if (this.manager.has(id)) {\n this.manager.focus(id);\n }\n });\n\n const close = element(WINDOW_CHROME_IDS.close);\n if (close) {\n if (entity.getValue(UIWindow, 'closable')) {\n close.addEventListener('click', () => {\n // Destroying the entity disqualifies it everywhere; releaseWindow\n // then closes the manager record.\n entity.destroy();\n });\n } else {\n close.setProperties({ display: 'none' });\n }\n }\n\n const minimize = element(WINDOW_CHROME_IDS.minimize);\n if (minimize) {\n if (entity.getValue(UIWindow, 'minimizable')) {\n minimize.addEventListener('click', () => {\n if (this.manager.has(id)) {\n this.manager.toggleMinimized(id);\n }\n });\n } else {\n minimize.setProperties({ display: 'none' });\n }\n }\n\n const pin = element(WINDOW_CHROME_IDS.pin);\n if (pin) {\n if (entity.getValue(UIWindow, 'pinnable')) {\n pin.addEventListener('click', () => {\n if (this.manager.has(id)) {\n this.manager.togglePin(id);\n }\n });\n } else {\n pin.setProperties({ display: 'none' });\n }\n }\n\n const dock = element(WINDOW_CHROME_IDS.dock);\n dock?.addEventListener('click', () => this.returnHome(entity));\n }\n\n private applyMinimized(id: string, minimized: boolean): void {\n const entity = this.entitiesById.get(id);\n const document = entity ? this.documentOf(entity) : undefined;\n const content = document?.getElementById(WINDOW_CHROME_IDS.content) as\n | UikitElement\n | undefined;\n content?.setProperties({ display: minimized ? 'none' : 'flex' });\n }\n\n private documentOf(entity: Entity): UIKitDocument | undefined {\n return PanelDocument.data.document[entity.index] as UIKitDocument | undefined;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@realitycollective/iwsdk-uiextensions",
|
|
3
|
-
"version": "0.1.0-preview.
|
|
3
|
+
"version": "0.1.0-preview.6",
|
|
4
4
|
"description": "Meta IWSDK adapter for the Reality Collective UI Extensions - windowing, docking, layout regions and extra controls, binding the engine-free @realitycollective/webxr-uiextensions core onto IWSDK's ECS, UIKitML and interaction systems.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"realitycollective",
|
|
@@ -39,10 +39,11 @@
|
|
|
39
39
|
"@drawcall/uikitml": "0.1.8",
|
|
40
40
|
"zod": "^4.4.3",
|
|
41
41
|
"@pmndrs/handle": "^6.6.16",
|
|
42
|
-
"@realitycollective/webxr-uiextensions": "^0.1.0-preview.
|
|
42
|
+
"@realitycollective/webxr-uiextensions": "^0.1.0-preview.6"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@iwsdk/core": ">=0.5.0 <0.6.0"
|
|
45
|
+
"@iwsdk/core": ">=0.5.0 <0.6.0",
|
|
46
|
+
"three": ">=0.170.0"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|
|
48
49
|
"@iwsdk/core": "0.5.3",
|