@vincentt-xr/sdk 2.0.0-alpha.0 → 2.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/GROUNDING.md +232 -14
- package/dist/{DebuggerUI-CEfdddkZ.cjs → DebuggerUI-CatYc-RG.cjs} +1 -1
- package/dist/{DebuggerUI-Dgop6Q6o.js → DebuggerUI-gLMKiVwJ.js} +1 -1
- package/dist/MediaSourceSwitcher-C-xYg0nQ.js +317 -0
- package/dist/MediaSourceSwitcher-Cq3g4IKw.cjs +31 -0
- package/dist/components/FaceMesh/canonical/CanonicalFaceMeshRenderer.d.ts +6 -1
- package/dist/components/FaceMesh/material/defaults.d.ts +4 -0
- package/dist/components/FaceMesh/material/types.d.ts +2 -0
- package/dist/components/FaceRetouch/Background.d.ts +6 -1
- package/dist/components/FaceRetouch/defaults.d.ts +13 -0
- package/dist/components/FaceRetouch/index.d.ts +3 -1
- package/dist/components/FaceRetouch/landmarkMask.d.ts +4 -1
- package/dist/components/FaceRetouch/useFaceRetouchMask.d.ts +2 -1
- package/dist/components/FaceWarp3D/FaceWarp3DHead.d.ts +15 -0
- package/dist/components/FaceWarp3D/defaults.d.ts +37 -0
- package/dist/components/FaceWarp3D/index.d.ts +3 -0
- package/dist/components/FaceWarp3D/warp.d.ts +13 -0
- package/dist/components/HandJoints2D/index.d.ts +6 -0
- package/dist/components/HandTracker/index.d.ts +2 -2
- package/dist/components/HandTracking2D/HandVisual.d.ts +12 -0
- package/dist/components/HandTracking2D/index.d.ts +5 -0
- package/dist/components/HandTracking2DController/index.d.ts +5 -0
- package/dist/components/HandTracking3D/index.d.ts +6 -0
- package/dist/components/ScreenImage/index.d.ts +87 -0
- package/dist/components/ScreenText/index.d.ts +20 -0
- package/dist/components/TextLabel/index.d.ts +99 -0
- package/dist/components/XRScene/index.d.ts +14 -3
- package/dist/components/XRViewport/index.d.ts +17 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/debug-ui/default-setting/index.d.ts +37 -0
- package/dist/debug-ui/default-setting.cjs +21 -0
- package/dist/debug-ui/default-setting.js +777 -0
- package/dist/debug-ui/face-warp-3d/index.d.ts +6 -0
- package/dist/debug-ui/hand-joints-2d/index.d.ts +6 -0
- package/dist/debug-ui/hand-tracking-2d/index.d.ts +6 -0
- package/dist/debug-ui/hand-tracking-2d-controller/index.d.ts +6 -0
- package/dist/debug-ui/hand-tracking-3d/index.d.ts +6 -0
- package/dist/debug-ui/index.d.ts +23 -0
- package/dist/debug-ui/layer-panel/index.d.ts +17 -0
- package/dist/debug-ui/media-source/MediaSourceSwitcher.d.ts +10 -0
- package/dist/debug-ui/media-source/index.d.ts +4 -0
- package/dist/debug-ui/media-source/media-preset.d.ts +9 -0
- package/dist/debug-ui/media-source.cjs +1 -0
- package/dist/debug-ui/media-source.js +7 -0
- package/dist/debug-ui/screen-image-settings/index.d.ts +7 -0
- package/dist/debug-ui/screen-text-settings/index.d.ts +122 -0
- package/dist/debug-ui/settings-panel/index.d.ts +38 -0
- package/dist/debug-ui/shared/MaterialSection.d.ts +13 -0
- package/dist/debug-ui/shared/ScreenTransformSection.d.ts +29 -0
- package/dist/debug-ui.cjs +20 -0
- package/dist/debug-ui.js +6063 -0
- package/dist/defaults-BLpcN3pG.cjs +1 -0
- package/dist/defaults-LtY4M7dg.js +55 -0
- package/dist/entries/core.d.ts +5 -1
- package/dist/entries/face-effects.d.ts +2 -0
- package/dist/entries/low-level.d.ts +3 -0
- package/dist/face-effects.cjs +113 -23
- package/dist/face-effects.js +1566 -1399
- package/dist/handJoints2d.d.ts +49 -0
- package/dist/handTracking2d.d.ts +33 -0
- package/dist/handTracking2dController.d.ts +65 -0
- package/dist/handTracking3d.d.ts +52 -0
- package/dist/{index-BD-JWMjE.js → index-BGNbrc4C.js} +113 -137
- package/dist/index-BXyObVHF.cjs +1 -0
- package/dist/index-CY3bUAcW.js +12 -0
- package/dist/index-C_Duh13c.cjs +1 -0
- package/dist/index-D8fpPQkC.cjs +1 -0
- package/dist/index-DxshgBu0.cjs +1 -0
- package/dist/index-O-2crZ3M.js +1102 -0
- package/dist/{index-CYMj26Ej.js → index-safCs_af.js} +426 -416
- package/dist/{internal-DH5d1Xnq.cjs → internal-BjyhBP8i.cjs} +1 -1
- package/dist/{internal-KOKJDxCG.js → internal-Cae1ps15.js} +3 -3
- package/dist/internal.cjs +1 -1
- package/dist/internal.js +2 -2
- package/dist/layerStack.d.ts +29 -0
- package/dist/low-level.cjs +1 -1
- package/dist/low-level.js +74 -890
- package/dist/main.cjs +73 -1
- package/dist/main.js +1656 -407
- package/dist/models/Hand/HandTracking.d.ts +2 -0
- package/dist/screen2d/index.d.ts +4 -0
- package/dist/screen2d/material-runtime.d.ts +32 -0
- package/dist/screen2d/texture.d.ts +10 -0
- package/dist/screen2d/transform.d.ts +25 -0
- package/dist/screen2d/types.d.ts +90 -0
- package/dist/screenImagePresets.d.ts +13 -0
- package/dist/screenImageSettings-6NRg4cx9.js +39 -0
- package/dist/screenImageSettings-y_B4_767.cjs +1 -0
- package/dist/screenImageSettings.d.ts +50 -0
- package/dist/screenTextPresets.d.ts +6 -0
- package/dist/screenTextSettings.d.ts +131 -0
- package/dist/{selectors-Bub0o3qy.cjs → selectors-BLl2JUWW.cjs} +1 -1
- package/dist/{selectors-BHO6TnoC.js → selectors-CXd_x-6t.js} +6 -6
- package/dist/tracking.cjs +1 -1
- package/dist/tracking.js +193 -168
- package/dist/types/hand-tracking.d.ts +8 -0
- package/dist/types/nodes.d.ts +2 -0
- package/dist/useXRModel-3DcUa28n.cjs +1 -0
- package/dist/useXRModel-BMJE1hrk.js +28 -0
- package/dist/x-Co8jPhP0.js +127 -0
- package/dist/x-KC8E4Gaj.cjs +41 -0
- package/package.json +19 -1
- package/dist/index-B5J4gzn2.cjs +0 -1
- package/dist/index-BVBVg96G.cjs +0 -1
- package/dist/index-DtwHElmG.js +0 -157
- package/dist/index-PtFmD6B8.cjs +0 -1
package/GROUNDING.md
CHANGED
|
@@ -54,7 +54,9 @@ Coordinates are normalized: -1 to +1, with +y up. The center of the screen is `(
|
|
|
54
54
|
<mesh name="frame"><planeGeometry args={[1, 1]} /><meshBasicMaterial map={frameTex} transparent /></mesh>
|
|
55
55
|
</ScreenTransform>
|
|
56
56
|
|
|
57
|
-
// Bottom-center prompt
|
|
57
|
+
// Bottom-center prompt.
|
|
58
|
+
// Prefer <ScreenText> for ordinary screen copy; use this low-level pairing only
|
|
59
|
+
// when you need custom anchors.
|
|
58
60
|
<ScreenTransform anchors={{ left: -0.4, right: 0.4, top: -0.5, bottom: -0.9 }}>
|
|
59
61
|
<TextLabel name="prompt" text="Show your hand" fontSize={220} />
|
|
60
62
|
</ScreenTransform>
|
|
@@ -84,6 +86,88 @@ const [sx, sy] = computeContainScale(rectAspect, 1200 / 800); // content w/h rat
|
|
|
84
86
|
</mesh>;
|
|
85
87
|
```
|
|
86
88
|
|
|
89
|
+
### `<ScreenImage>` — Effect House-style screen-space image object
|
|
90
|
+
|
|
91
|
+
Use this for screen-space image planes such as logos, stickers, frames, badges,
|
|
92
|
+
and photo placeholders. `ScreenImage` owns its `ScreenTransform`; for normal
|
|
93
|
+
authoring prefer `position`, `size`, and `rotation`. `anchors` still exist as
|
|
94
|
+
the lower-level layout escape hatch. If no source is provided, it renders the
|
|
95
|
+
SDK-owned square default image.
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import { ScreenImage, ScreenSpaceUI } from "@vincentt-xr/sdk";
|
|
99
|
+
|
|
100
|
+
<ScreenSpaceUI>
|
|
101
|
+
<ScreenImage
|
|
102
|
+
name="defaultSquareImage"
|
|
103
|
+
anchors={{ left: -0.35, right: 0.35, top: 0.35, bottom: -0.35 }}
|
|
104
|
+
fit="contain"
|
|
105
|
+
/>
|
|
106
|
+
</ScreenSpaceUI>
|
|
107
|
+
|
|
108
|
+
<ScreenSpaceUI>
|
|
109
|
+
<ScreenImage
|
|
110
|
+
name="logo"
|
|
111
|
+
source={{ kind: "image", src: "https://cdn.../logo.png" }}
|
|
112
|
+
coordinateSpace="canvas"
|
|
113
|
+
canvasSize={{ width: 720, height: 1280 }}
|
|
114
|
+
position={[-180, 464]}
|
|
115
|
+
size={[216, 160]}
|
|
116
|
+
stretchMode="fit"
|
|
117
|
+
opacity={0.95}
|
|
118
|
+
/>
|
|
119
|
+
</ScreenSpaceUI>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Props:
|
|
123
|
+
|
|
124
|
+
- `source`: `{ kind: "default" }`, `{ kind: "image", src }`, or `{ kind: "texture", texture }`.
|
|
125
|
+
- `src`: shorthand for `{ kind: "image", src }`.
|
|
126
|
+
- `fit`: `"stretch" | "contain" | "cover"`; default `"contain"`.
|
|
127
|
+
- `coordinateSpace`: `"normalized" | "canvas"`; default `"normalized"`.
|
|
128
|
+
- `canvasSize`: design canvas size for `coordinateSpace="canvas"`; default
|
|
129
|
+
`{ width: 720, height: 1280 }`.
|
|
130
|
+
- `position`, `size`, `rotation`: author-facing screen-object transform.
|
|
131
|
+
In normalized mode, `position` is the rect center in normalized screen space
|
|
132
|
+
and `size` is normalized rect width/height. In canvas mode, `position` and
|
|
133
|
+
`size` are design-canvas pixels with center origin and +y up, matching the
|
|
134
|
+
Effect House 720x1280 Screen Transform convention.
|
|
135
|
+
- `anchors`, `offsets`, `pivot`: forwarded to the owned `ScreenTransform`.
|
|
136
|
+
- `opacity`, `color`, `flipX`, `flipY`, `renderOrder`, `transparent`, `alphaTest`, `overlay`, `visible`.
|
|
137
|
+
|
|
138
|
+
Effect House transfer rule: Screen Transform `Size` is the screen-space
|
|
139
|
+
bounding rectangle, not the image's natural pixel size. The image then fits
|
|
140
|
+
inside that rectangle according to `stretchMode`. Fit/fill/stretch changes the
|
|
141
|
+
texture content inside the rectangle; it must not change the object's rigid
|
|
142
|
+
transform. Rotation rotates the whole rectangle around `pivot`.
|
|
143
|
+
|
|
144
|
+
Settings:
|
|
145
|
+
|
|
146
|
+
- `DEFAULT_SCREEN_IMAGE_SETTINGS.transformation`: coordinate space, canvas size,
|
|
147
|
+
position, size, rotation, pivot, render order, overlay/depth behavior, and visibility.
|
|
148
|
+
- `DEFAULT_SCREEN_IMAGE_SETTINGS.image`: source kind, src, fit, opacity, color,
|
|
149
|
+
flips, transparency, and alpha test.
|
|
150
|
+
- `sourceKind` includes future media categories (`video`, `sequence`, `gif`),
|
|
151
|
+
but the current component implementation supports `default`, static `image`,
|
|
152
|
+
and already-loaded Three `texture`.
|
|
153
|
+
|
|
154
|
+
Use `<ScreenImage>` instead of hand-authoring `<ScreenTransform>` + `<mesh>`
|
|
155
|
+
when the object is conceptually a 2D screen image. Use the lower-level mesh path
|
|
156
|
+
only when you need custom geometry or material behavior.
|
|
157
|
+
|
|
158
|
+
### Shared debug authoring surface for `ScreenImage`
|
|
159
|
+
|
|
160
|
+
The SDK ships a dev-only inspector at `lib/debug-ui/screen-image-settings` for
|
|
161
|
+
authoring `ScreenImage` settings in creator tooling and the v2 template
|
|
162
|
+
preview. It exposes the image renderer, transform, material, and fit controls
|
|
163
|
+
as one shared panel. End-user shipping surfaces should not include the debug
|
|
164
|
+
chrome.
|
|
165
|
+
|
|
166
|
+
The panel's transform copy button copies a JSON payload for the selected object
|
|
167
|
+
including `name`, `kind: "screenImage"`, `transform`, `image`, `material`, and
|
|
168
|
+
`screenSpaceUI`. The `transform` includes `coordinateSpace` and `canvasSize`.
|
|
169
|
+
Use that payload as the handoff from human layout tuning back to code.
|
|
170
|
+
|
|
87
171
|
### `<SpriteAnimation sheet={{ url, cols, rows, frames, fps }}>` — Animated transparent overlay
|
|
88
172
|
|
|
89
173
|
The primitive for an animated transparent overlay (a frame border, a gesture-instruction graphic, an animated sticker). One transparent WebP atlas of `cols`×`rows` cells; the first `frames` play left-to-right, top-to-bottom at `fps`. It is a single static GPU texture with a per-frame UV offset — no video decode — so it works in every native and in-app browser and costs ~zero heat.
|
|
@@ -115,6 +199,39 @@ import { SpriteAnimation, ScreenSpaceUI } from "@vincentt-xr/sdk";
|
|
|
115
199
|
|
|
116
200
|
Mount the tracker component; it registers the underlying ML model automatically. No `registerXRPipeline` call needed.
|
|
117
201
|
|
|
202
|
+
### 3D face warp — FBX head binding and face proportion deformation
|
|
203
|
+
|
|
204
|
+
Use `faceWarp3D` on `<CanonicalFaceMeshRenderer>` to deform the live canonical
|
|
205
|
+
face mesh before projection. Defaults are no-op; pass `FACE_WARP_3D_PRESET` for
|
|
206
|
+
a visible starter look, or provide partial settings.
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
import { CanonicalFaceMeshRenderer, FACE_WARP_3D_PRESET } from "@vincentt-xr/sdk/face-effects";
|
|
210
|
+
|
|
211
|
+
<CanonicalFaceMeshRenderer
|
|
212
|
+
materialType="shader"
|
|
213
|
+
shaderPreset="originalFace"
|
|
214
|
+
textureMap="none"
|
|
215
|
+
alphaMap="canonical"
|
|
216
|
+
alphaOpacity={1}
|
|
217
|
+
baseColor="#ffffff"
|
|
218
|
+
opacity={1}
|
|
219
|
+
side="front"
|
|
220
|
+
faceAlphaMaskUrl="https://cdn.vincentt.studio/assets/sdk/face/v1/Canonical_Face_Color_Mask.png"
|
|
221
|
+
faceWarp3D={FACE_WARP_3D_PRESET}
|
|
222
|
+
/>;
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Settings (`FaceWarp3DSettings`) include `faceWidth`, `faceHeight`,
|
|
226
|
+
`foreheadHeight`, `cheekWidth`, `eyeSize`, `eyeDistance`, `noseSize`,
|
|
227
|
+
`noseLength`, `mouthWidth`, `mouthHeight`, `jawWidth`, `chinHeight`, plus
|
|
228
|
+
`enabled` and `strength`. Setting values are normalized around zero; typical UI
|
|
229
|
+
sliders map `-100..100` to `-1..1`.
|
|
230
|
+
|
|
231
|
+
For local tuning, import `FaceWarp3DSettingsPanel` from
|
|
232
|
+
`@vincentt-xr/sdk/debug-ui`. It is a draggable debug panel and should not
|
|
233
|
+
be included in the final production scene.
|
|
234
|
+
|
|
118
235
|
### `<FaceTracker>` — Face tracking (468 landmarks)
|
|
119
236
|
|
|
120
237
|
Detects faces and provides face transform to children via context. Supports `faceIndex` (default 0) and `scaleWithFace`.
|
|
@@ -154,6 +271,10 @@ import { HandTracker } from "@vincentt-xr/sdk/tracking";
|
|
|
154
271
|
<HandTracker />;
|
|
155
272
|
```
|
|
156
273
|
|
|
274
|
+
Supports multiple detected hands: `hand` selects `"left"` or `"right"` when that
|
|
275
|
+
hand is available; `handIndex` falls back to positional selection. Existing
|
|
276
|
+
one-hand scenes keep working via the default first-hand selection.
|
|
277
|
+
|
|
157
278
|
### `<GestureTracker>` — Named gestures
|
|
158
279
|
|
|
159
280
|
The gesture door — the component that recognizes named gestures. Its `node.gesture` is one of these exact strings (copy verbatim — these are the only valid values):
|
|
@@ -373,29 +494,126 @@ const tex = useTexture("https://cdn.../badge.webp");
|
|
|
373
494
|
|
|
374
495
|
---
|
|
375
496
|
|
|
497
|
+
## `<ScreenText>` — Standard screen-space text
|
|
498
|
+
|
|
499
|
+
Import from `@vincentt-xr/sdk`. Use this when you want the standard
|
|
500
|
+
screen-text lane instead of wiring `ScreenTransform` + `TextLabel` yourself.
|
|
501
|
+
For future Vincentt scenes, prefer `ScreenText` over raw `TextLabel` for end-user
|
|
502
|
+
screen copy such as prompts, captions, banners, badges, and titles.
|
|
503
|
+
|
|
504
|
+
`ScreenText` composes:
|
|
505
|
+
|
|
506
|
+
- full-width screen-space lane presets
|
|
507
|
+
- `verticalAlign: "top" | "center" | "bottom"`
|
|
508
|
+
- `TextLabel` rendering props such as `textAlign`, overflow, background, stroke, and shadow
|
|
509
|
+
|
|
510
|
+
```tsx
|
|
511
|
+
import { ScreenSpaceUI, ScreenText } from "@vincentt-xr/sdk";
|
|
512
|
+
|
|
513
|
+
<ScreenSpaceUI>
|
|
514
|
+
<ScreenText
|
|
515
|
+
name="headline"
|
|
516
|
+
text="Head-Tilt Trivia"
|
|
517
|
+
coordinateSpace="canvas"
|
|
518
|
+
canvasSize={{ width: 720, height: 1280 }}
|
|
519
|
+
position={[0, 500]}
|
|
520
|
+
size={[620, 140]}
|
|
521
|
+
textAlign="center"
|
|
522
|
+
fontSize={270}
|
|
523
|
+
bgFit="text"
|
|
524
|
+
bgColor="transparent"
|
|
525
|
+
/>
|
|
526
|
+
</ScreenSpaceUI>;
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
`ScreenText` supports the same screen coordinate contract as `ScreenImage`:
|
|
530
|
+
`coordinateSpace="normalized"` uses normalized screen units, while
|
|
531
|
+
`coordinateSpace="canvas"` uses Effect House-style design-canvas pixels
|
|
532
|
+
(`720x1280` by default, center origin, +y up). Its `size` is the text bounding
|
|
533
|
+
rectangle. The text renderer then aligns, shrinks, clips, or backgrounds the
|
|
534
|
+
text inside that rectangle.
|
|
535
|
+
|
|
536
|
+
Use `ScreenText` when:
|
|
537
|
+
|
|
538
|
+
- you want top / center / bottom screen placement
|
|
539
|
+
- you want wide-lane text alignment without hand-authoring anchors
|
|
540
|
+
- you want the default end-user screen text behavior
|
|
541
|
+
|
|
542
|
+
Use `TextLabel` when:
|
|
543
|
+
|
|
544
|
+
- you need raw text rendering outside the screen-text presets
|
|
545
|
+
- you want to place text manually in world space or inside a custom transform
|
|
546
|
+
- you are building a lower-level composite where another component owns layout
|
|
547
|
+
|
|
548
|
+
### Shared debug authoring surface for `ScreenText`
|
|
549
|
+
|
|
550
|
+
The SDK also ships a dev-only inspector at `lib/debug-ui/screen-text-settings`
|
|
551
|
+
for `ScreenText` authoring. It groups the text renderer, transform, style, and
|
|
552
|
+
background controls into separate sections so creators can inspect the lane
|
|
553
|
+
without shipping the debug chrome in production.
|
|
554
|
+
|
|
555
|
+
The panel's transform copy button copies a JSON payload for the selected object
|
|
556
|
+
including `name`, `kind: "screenText"`, `transform`, `content`, `style`,
|
|
557
|
+
`layout`, and `screenSpaceUI`. The `transform` includes `coordinateSpace` and
|
|
558
|
+
`canvasSize`. Use that payload as the handoff from human layout tuning back to
|
|
559
|
+
code.
|
|
560
|
+
|
|
561
|
+
### Shared debug layer panel
|
|
562
|
+
|
|
563
|
+
The SDK ships a reusable `LayerPanel` in `lib/debug-ui/layer-panel` for
|
|
564
|
+
Photoshop-style stack management. It is intended for dev / creator tooling
|
|
565
|
+
only and is currently used by the template preview to manage `ScreenImage` and
|
|
566
|
+
`ScreenText` layers. Production shipping surfaces should not include the panel.
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
376
570
|
## `<TextLabel>` — Canvas-rendered text panel
|
|
377
571
|
|
|
378
572
|
Import from `@vincentt-xr/sdk`. Billboarded in 3D space.
|
|
379
573
|
|
|
380
|
-
| prop
|
|
381
|
-
|
|
|
382
|
-
| `text`
|
|
383
|
-
| `
|
|
384
|
-
| `
|
|
385
|
-
| `
|
|
386
|
-
| `
|
|
387
|
-
| `
|
|
388
|
-
| `
|
|
389
|
-
| `
|
|
390
|
-
| `
|
|
391
|
-
| `
|
|
574
|
+
| prop | type | default | notes |
|
|
575
|
+
| --------------- | ------------------------------------------------- | ---------------------- | --------------------------------------------------------------------------------------------- |
|
|
576
|
+
| `text` | `string` | **required** | The text to render. |
|
|
577
|
+
| `lines` | `string[]` | — | Optional precomputed lines. Skips automatic word wrapping. |
|
|
578
|
+
| `color` | `string` | `#ffffff` | Text color. |
|
|
579
|
+
| `strokeLayers` | `{ color, width, opacity? }[]` | — | Optional multi-stroke stack, drawn outer-to-inner. |
|
|
580
|
+
| `strokeColor` | `string` | — | Optional text stroke color. Pair with `strokeWidth`. |
|
|
581
|
+
| `strokeWidth` | `number` | `0` | Text stroke thickness in canvas px. |
|
|
582
|
+
| `shadowLayers` | `{ color, blur, offsetX?, offsetY?, opacity? }[]` | — | Optional multi-shadow/glow stack, drawn back-to-front. |
|
|
583
|
+
| `shadowColor` | `string` | — | Optional text shadow / glow color. |
|
|
584
|
+
| `shadowBlur` | `number` | `0` | Shadow blur radius in canvas px. |
|
|
585
|
+
| `shadowOffsetX` | `number` | `0` | Shadow X offset in canvas px. |
|
|
586
|
+
| `shadowOffsetY` | `number` | `0` | Shadow Y offset in canvas px. |
|
|
587
|
+
| `fontSize` | `number` | `48` | Canvas px at the default 512px canvas — NOT world units. Chips/badges typically need 200-320. |
|
|
588
|
+
| `textAlign` | `"left" \| "center" \| "right"` | `"center"` | Horizontal text alignment inside the available lane. |
|
|
589
|
+
| `overflow` | `"clip" \| "clamp" \| "ellipsis" \| "shrink"` | — | Overflow behavior. `clamp` keeps full visible lines, `clip` allows canvas-edge clipping. |
|
|
590
|
+
| `resizeToFit` | `boolean` | — | Keep shrinking until the text fits or `minFontSize` is reached. |
|
|
591
|
+
| `minFontSize` | `number` | `12` | Minimum shrink target in canvas px. |
|
|
592
|
+
| `bgColor` | `string` | `rgba(20,40,110,0.88)` | Panel background; use `"transparent"` for text-only. |
|
|
593
|
+
| `bgFit` | `"full" \| "text"` | `"full"` | Background fit mode. Use `"text"` for pill/capsule backgrounds that hug the text block. |
|
|
594
|
+
| `borderColor` | `string` | — | Border color; omit for no border. |
|
|
595
|
+
| `borderRadius` | `number` | `24` | Corner radius; 0 for square. |
|
|
596
|
+
| `aspect` | `number` | `2.5` | Panel shape (width ÷ height). >1 = wide/banner, <1 = tall. |
|
|
597
|
+
| `scale` | `[x,y,z] \| number` | `1` | World size. Combine with `aspect` to make wide banners. |
|
|
598
|
+
| `position` | `[x,y,z]` | `[0,0,0]` | World position. +Y up, -Y down. |
|
|
599
|
+
| `name` | `string` | — | Set on every element you create. |
|
|
392
600
|
|
|
393
601
|
**Gotchas:**
|
|
394
602
|
|
|
395
603
|
- `fontSize` is canvas px, not world units. Chips/badges need 200-320, not 16-48.
|
|
396
604
|
- `fontSize` does not change panel shape — use `aspect` (shape) and `scale` (size).
|
|
397
|
-
- `
|
|
605
|
+
- `textAlign` affects both glyph placement and `bgFit="text"` background placement.
|
|
606
|
+
- `TextLabel` respects the `visible` prop.
|
|
607
|
+
- For text-first 2D treatments, prefer `strokeColor` + `strokeWidth` and `shadowColor` + `shadowBlur` before stacking multiple `TextLabel`s. Native text effects keep the glyph edges aligned and resize cleanly.
|
|
608
|
+
- Use `strokeLayers` / `shadowLayers` when you need richer looks like sticker text, double-outline, or offset-drop-shadow. The single `stroke*` / `shadow*` props are shorthand for one layer and can coexist with the arrays.
|
|
609
|
+
- `TextLabel` now owns real overflow behavior. If you need exact authored line breaks, pass `text` with `\n` or provide `lines` directly; those lines will be rendered as the source layout instead of being re-wrapped from one flattened string.
|
|
610
|
+
- Overflow semantics:
|
|
611
|
+
- `clip`: draw all resolved lines and let the canvas bounds crop them, including partial last lines.
|
|
612
|
+
- `clamp`: keep only full visible lines within the line budget.
|
|
613
|
+
- `ellipsis`: same as clamp, but ellipsize the last visible line.
|
|
614
|
+
- `shrink`: reduce font size until the content fits or `minFontSize` is reached, then clamp if it still overflows.
|
|
398
615
|
- **Inside a `<ScreenTransform>`, the parent stretches the label to fill the anchor rect — `aspect` must equal the rect's aspect or the banner distorts and leaves dead space around the text.** The rect's aspect is `(right − left) / (top − bottom)`. So for a full-width top banner `anchors={{ left: -1, right: 1, top: 1, bottom: 0.76 }}` → aspect `= 2 / 0.24 ≈ 8.3`. Set `borderRadius={0}` for a true edge-to-edge banner. (The rect controls size + placement — don't add `scale`/`position` to fill it; set the anchors instead.)
|
|
616
|
+
- If you want the SDK to own that anchor math, use `<ScreenText>` instead of pairing `<ScreenTransform>` with `<TextLabel>` manually.
|
|
399
617
|
|
|
400
618
|
```tsx
|
|
401
619
|
// Full-width banner pinned to the top, INSIDE a ScreenTransform.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),x=require("react"),C=require("react-dom"),s=require("./internal-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),x=require("react"),C=require("react-dom"),s=require("./internal-BjyhBP8i.cjs"),P={position:"fixed",top:"10px",left:"10px",zIndex:9999,fontFamily:"'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace",fontSize:"12px",color:"#fff",pointerEvents:"auto",userSelect:"none"},h={background:"rgba(0, 0, 0, 0.85)",borderRadius:"6px",padding:"12px 16px",boxShadow:"0 4px 12px rgba(0, 0, 0, 0.5)",backdropFilter:"blur(10px)",border:"1px solid rgba(255, 255, 255, 0.1)",minWidth:"240px"},k={...h,padding:"8px 12px",minWidth:"auto"},F={display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"12px",paddingBottom:"8px",borderBottom:"1px solid rgba(255, 255, 255, 0.2)"},M={fontSize:"13px",fontWeight:"bold",color:"#00ff9f",letterSpacing:"0.5px",textTransform:"uppercase"},z={display:"flex",gap:"6px"},f={background:"rgba(255, 255, 255, 0.1)",border:"1px solid rgba(255, 255, 255, 0.2)",borderRadius:"4px",color:"#fff",padding:"4px 8px",fontSize:"11px",cursor:"pointer",transition:"all 0.2s"},R={display:"grid",gridTemplateColumns:"1fr 1fr",gap:"10px"},i={display:"flex",flexDirection:"column",gap:"4px"},o={fontSize:"10px",color:"rgba(255, 255, 255, 0.6)",textTransform:"uppercase",letterSpacing:"0.5px"},d={fontSize:"16px",fontWeight:"bold",color:"#fff"},u={...d,color:"#00ff9f"},g={...d,color:"#ffaa00"},m={...d,color:"#ff4444"},l={fontSize:"11px",color:"rgba(255, 255, 255, 0.5)",marginLeft:"4px"},N={width:"100%",height:"4px",background:"rgba(255, 255, 255, 0.2)",borderRadius:"2px",overflow:"hidden",marginTop:"4px"},w=t=>({width:`${t}%`,height:"100%",background:t>90?"#ff4444":t>70?"#ffaa00":"#00ff9f",transition:"width 0.3s ease, background 0.3s ease"}),B={fontSize:"9px",color:"rgba(255, 255, 255, 0.4)",marginTop:"8px",textAlign:"center",fontStyle:"italic"};function a(t,n){return n==="fps"?t>=30?u:t>=20?g:m:t<=40?u:t<=60?g:m}function D({metrics:t,collapsed:n,onCollapse:y,onClose:b,position:c="top-left",keyboardShortcut:j="ctrl+shift+d"}){const[p,v]=x.useState(null);if(x.useEffect(()=>{const r=document.createElement("div");return r.id="xr-debugger-overlay",document.body.appendChild(r),v(r),()=>{document.body.removeChild(r)}},[]),!p)return null;const S={...P,...c==="top-right"&&{left:"auto",right:"10px"},...c==="bottom-left"&&{top:"auto",bottom:"10px"},...c==="bottom-right"&&{top:"auto",bottom:"10px",left:"auto",right:"10px"}};return C.createPortal(e.jsx("div",{style:S,children:e.jsxs("div",{style:n?k:h,children:[e.jsxs("div",{style:F,children:[e.jsx("div",{style:M,children:n?"AR Debugger":"AR Performance"}),e.jsxs("div",{style:z,children:[e.jsx("button",{style:f,onClick:()=>y(!n),onMouseEnter:r=>{r.currentTarget.style.background="rgba(255, 255, 255, 0.2)"},onMouseLeave:r=>{r.currentTarget.style.background="rgba(255, 255, 255, 0.1)"},children:n?"+":"−"}),e.jsx("button",{style:f,onClick:b,onMouseEnter:r=>{r.currentTarget.style.background="rgba(255, 255, 255, 0.2)"},onMouseLeave:r=>{r.currentTarget.style.background="rgba(255, 255, 255, 0.1)"},children:"✕"})]})]}),!n&&e.jsxs(e.Fragment,{children:[e.jsxs("div",{style:R,children:[e.jsxs("div",{style:i,children:[e.jsx("div",{style:o,children:"Camera FPS"}),e.jsxs("div",{children:[e.jsx("span",{style:a(t.cameraFPS,"fps"),children:s.formatNumber(t.cameraFPS,0)}),e.jsx("span",{style:l,children:"fps"})]})]}),e.jsxs("div",{style:i,children:[e.jsx("div",{style:o,children:"Resolution"}),e.jsx("span",{style:d,children:t.resolution})]}),e.jsxs("div",{style:i,children:[e.jsx("div",{style:o,children:"Detection"}),e.jsxs("div",{children:[e.jsx("span",{style:a(t.detectionTime,"time"),children:s.formatNumber(t.detectionTime,1)}),e.jsx("span",{style:l,children:"ms"})]})]}),e.jsxs("div",{style:i,children:[e.jsx("div",{style:o,children:"Rendering"}),e.jsxs("div",{children:[e.jsx("span",{style:a(t.renderTime,"time"),children:s.formatNumber(t.renderTime,1)}),e.jsx("span",{style:l,children:"ms"})]})]}),e.jsxs("div",{style:i,children:[e.jsx("div",{style:o,children:"Effective FPS"}),e.jsxs("div",{children:[e.jsx("span",{style:a(t.effectiveFPS,"fps"),children:s.formatNumber(t.effectiveFPS,0)}),e.jsx("span",{style:l,children:"fps"})]})]}),e.jsxs("div",{style:i,children:[e.jsx("div",{style:o,children:"Memory"}),t.memory?e.jsxs(e.Fragment,{children:[e.jsxs("div",{children:[e.jsx("span",{style:d,children:s.formatNumber(t.memory.used,0)}),e.jsxs("span",{style:l,children:["/ ",s.formatNumber(t.memory.total,0)," MB"]})]}),e.jsx("div",{style:N,children:e.jsx("div",{style:w(t.memory.percentage)})})]}):e.jsx("span",{style:d,children:"N/A"})]})]}),t.modelPerformance.length>0&&e.jsx(e.Fragment,{children:e.jsxs("div",{style:{borderTop:"1px solid rgba(255, 255, 255, 0.15)",marginTop:"12px",paddingTop:"12px"},children:[e.jsx("div",{style:o,children:"Model Performance"}),e.jsx("div",{style:{marginTop:"8px",display:"flex",flexDirection:"column",gap:"6px"},children:t.modelPerformance.map((r,T)=>e.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",fontSize:"11px"},children:[e.jsx("span",{style:{color:"rgba(255, 255, 255, 0.7)"},children:r.name}),e.jsxs("div",{children:[e.jsx("span",{style:a(r.processingTime,"time"),children:s.formatNumber(r.processingTime,1)}),e.jsx("span",{style:l,children:"ms"})]})]},T))})]})}),e.jsxs("div",{style:B,children:["Press ",j.toUpperCase()," to toggle"]})]})]})}),p)}exports.DebuggerUI=D;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as e, jsxs as r, Fragment as f } from "react/jsx-runtime";
|
|
2
2
|
import { useState as P, useEffect as M } from "react";
|
|
3
3
|
import { createPortal as F } from "react-dom";
|
|
4
|
-
import { f as i } from "./internal-
|
|
4
|
+
import { f as i } from "./internal-Cae1ps15.js";
|
|
5
5
|
const z = {
|
|
6
6
|
position: "fixed",
|
|
7
7
|
top: "10px",
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { l as z } from "./index-CY3bUAcW.js";
|
|
2
|
+
import { jsx as o, jsxs as l } from "react/jsx-runtime";
|
|
3
|
+
import { createPortal as L } from "react-dom";
|
|
4
|
+
import { useState as p, useRef as R, useMemo as W, useEffect as b } from "react";
|
|
5
|
+
import { c as i, X as $ } from "./x-Co8jPhP0.js";
|
|
6
|
+
const w = (t, r) => new URL(`/assets/sdk/debug-ui/v1/${t}/${r}`, z.assetStorageEndpoint).href, v = (t) => t.replace(/\.[^.]+$/, "").replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim().replace(/\b\w/g, (r) => r.toUpperCase()), E = [
|
|
7
|
+
"person_fullbody.mp4",
|
|
8
|
+
"person_looking.mp4",
|
|
9
|
+
"person_shaking_head.mp4",
|
|
10
|
+
"person_thumbUp.mp4",
|
|
11
|
+
"two_people_smiling.mp4"
|
|
12
|
+
], D = [
|
|
13
|
+
"person_fist_sign.jpg",
|
|
14
|
+
"person_fullbody.jpg",
|
|
15
|
+
"person_peace_sign.jpg",
|
|
16
|
+
"person_portrait.jpg",
|
|
17
|
+
"person_side_portrait.jpg",
|
|
18
|
+
"three_people_smiling.jpg",
|
|
19
|
+
"two_people_smiling.jpg"
|
|
20
|
+
], N = (t, r) => ({
|
|
21
|
+
id: `sdk-video-${r + 1}`,
|
|
22
|
+
kind: "video",
|
|
23
|
+
label: v(t),
|
|
24
|
+
url: w("videos", t)
|
|
25
|
+
}), P = (t, r) => ({
|
|
26
|
+
id: `sdk-image-${r + 1}`,
|
|
27
|
+
kind: "image",
|
|
28
|
+
label: v(t),
|
|
29
|
+
url: w("images", t)
|
|
30
|
+
}), V = E.map(N), U = D.map(P), he = [
|
|
31
|
+
{ id: "webcam", kind: "webcam", label: "Webcam" },
|
|
32
|
+
...V,
|
|
33
|
+
...U
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* @license lucide-react v1.17.0 - ISC
|
|
37
|
+
*
|
|
38
|
+
* This source code is licensed under the ISC license.
|
|
39
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
40
|
+
*/
|
|
41
|
+
const F = [
|
|
42
|
+
[
|
|
43
|
+
"path",
|
|
44
|
+
{
|
|
45
|
+
d: "M13.997 4a2 2 0 0 1 1.76 1.05l.486.9A2 2 0 0 0 18.003 7H20a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2h1.997a2 2 0 0 0 1.759-1.048l.489-.904A2 2 0 0 1 10.004 4z",
|
|
46
|
+
key: "18u6gg"
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
["circle", { cx: "12", cy: "13", r: "3", key: "1vg3eu" }]
|
|
50
|
+
], B = i("camera", F);
|
|
51
|
+
/**
|
|
52
|
+
* @license lucide-react v1.17.0 - ISC
|
|
53
|
+
*
|
|
54
|
+
* This source code is licensed under the ISC license.
|
|
55
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
56
|
+
*/
|
|
57
|
+
const K = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]], O = i("chevron-down", K);
|
|
58
|
+
/**
|
|
59
|
+
* @license lucide-react v1.17.0 - ISC
|
|
60
|
+
*
|
|
61
|
+
* This source code is licensed under the ISC license.
|
|
62
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
63
|
+
*/
|
|
64
|
+
const X = [
|
|
65
|
+
["circle", { cx: "9", cy: "12", r: "1", key: "1vctgf" }],
|
|
66
|
+
["circle", { cx: "9", cy: "5", r: "1", key: "hp0tcf" }],
|
|
67
|
+
["circle", { cx: "9", cy: "19", r: "1", key: "fkjjf6" }],
|
|
68
|
+
["circle", { cx: "15", cy: "12", r: "1", key: "1tmaij" }],
|
|
69
|
+
["circle", { cx: "15", cy: "5", r: "1", key: "19l28e" }],
|
|
70
|
+
["circle", { cx: "15", cy: "19", r: "1", key: "f4zoj3" }]
|
|
71
|
+
], A = i("grip-vertical", X);
|
|
72
|
+
/**
|
|
73
|
+
* @license lucide-react v1.17.0 - ISC
|
|
74
|
+
*
|
|
75
|
+
* This source code is licensed under the ISC license.
|
|
76
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
77
|
+
*/
|
|
78
|
+
const G = [
|
|
79
|
+
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
|
|
80
|
+
["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
|
|
81
|
+
["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
|
|
82
|
+
], H = i("image", G);
|
|
83
|
+
/**
|
|
84
|
+
* @license lucide-react v1.17.0 - ISC
|
|
85
|
+
*
|
|
86
|
+
* This source code is licensed under the ISC license.
|
|
87
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
88
|
+
*/
|
|
89
|
+
const T = [
|
|
90
|
+
["path", { d: "M4 5h16", key: "1tepv9" }],
|
|
91
|
+
["path", { d: "M4 12h16", key: "1lakjw" }],
|
|
92
|
+
["path", { d: "M4 19h16", key: "1djgab" }]
|
|
93
|
+
], Y = i("menu", T);
|
|
94
|
+
/**
|
|
95
|
+
* @license lucide-react v1.17.0 - ISC
|
|
96
|
+
*
|
|
97
|
+
* This source code is licensed under the ISC license.
|
|
98
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
99
|
+
*/
|
|
100
|
+
const q = [
|
|
101
|
+
[
|
|
102
|
+
"path",
|
|
103
|
+
{
|
|
104
|
+
d: "m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5",
|
|
105
|
+
key: "ftymec"
|
|
106
|
+
}
|
|
107
|
+
],
|
|
108
|
+
["rect", { x: "2", y: "6", width: "14", height: "12", rx: "2", key: "158x01" }]
|
|
109
|
+
], J = i("video", q), Q = (t) => t === "webcam" ? "📷 " : t === "image" ? "🖼 " : "▶ ", Z = {
|
|
110
|
+
position: "fixed",
|
|
111
|
+
zIndex: 2e4,
|
|
112
|
+
pointerEvents: "none"
|
|
113
|
+
}, _ = {
|
|
114
|
+
pointerEvents: "auto",
|
|
115
|
+
border: "1px solid rgba(255, 255, 255, 0.12)",
|
|
116
|
+
background: "rgba(8, 15, 28, 0.58)",
|
|
117
|
+
boxShadow: "0 12px 32px rgba(0, 0, 0, 0.28)",
|
|
118
|
+
backdropFilter: "blur(20px) saturate(1.15)",
|
|
119
|
+
WebkitBackdropFilter: "blur(20px) saturate(1.15)",
|
|
120
|
+
color: "#f8fafc",
|
|
121
|
+
display: "flex"
|
|
122
|
+
}, ee = {
|
|
123
|
+
..._,
|
|
124
|
+
width: 34,
|
|
125
|
+
height: 34,
|
|
126
|
+
borderRadius: 17,
|
|
127
|
+
alignItems: "center",
|
|
128
|
+
justifyContent: "center",
|
|
129
|
+
cursor: "pointer",
|
|
130
|
+
transition: "transform 150ms ease"
|
|
131
|
+
}, te = {
|
|
132
|
+
..._,
|
|
133
|
+
flexDirection: "column",
|
|
134
|
+
borderRadius: 12,
|
|
135
|
+
overflow: "hidden",
|
|
136
|
+
padding: 4,
|
|
137
|
+
gap: 4
|
|
138
|
+
}, oe = {
|
|
139
|
+
display: "flex",
|
|
140
|
+
alignItems: "center",
|
|
141
|
+
gap: 3
|
|
142
|
+
}, ne = {
|
|
143
|
+
width: 1,
|
|
144
|
+
height: 12,
|
|
145
|
+
background: "rgba(255, 255, 255, 0.12)",
|
|
146
|
+
margin: "0 3px"
|
|
147
|
+
}, re = {
|
|
148
|
+
display: "flex",
|
|
149
|
+
width: 16,
|
|
150
|
+
height: 24,
|
|
151
|
+
alignItems: "center",
|
|
152
|
+
justifyContent: "center",
|
|
153
|
+
cursor: "grab",
|
|
154
|
+
color: "rgba(148, 163, 184, 0.8)",
|
|
155
|
+
flex: "0 0 auto"
|
|
156
|
+
}, x = (t = !1) => ({
|
|
157
|
+
display: "flex",
|
|
158
|
+
width: 24,
|
|
159
|
+
height: 24,
|
|
160
|
+
alignItems: "center",
|
|
161
|
+
justifyContent: "center",
|
|
162
|
+
borderRadius: 8,
|
|
163
|
+
border: `1px solid ${t ? "rgba(255, 255, 255, 0.12)" : "transparent"}`,
|
|
164
|
+
background: t ? "rgba(255, 255, 255, 0.08)" : "transparent",
|
|
165
|
+
color: t ? "#f8fafc" : "rgba(226, 232, 240, 0.72)",
|
|
166
|
+
cursor: "pointer",
|
|
167
|
+
flex: "0 0 auto",
|
|
168
|
+
transition: "background 120ms ease, color 120ms ease"
|
|
169
|
+
}), ie = {
|
|
170
|
+
position: "relative",
|
|
171
|
+
display: "flex",
|
|
172
|
+
alignItems: "center"
|
|
173
|
+
}, se = {
|
|
174
|
+
width: "100%",
|
|
175
|
+
minWidth: 140,
|
|
176
|
+
height: 24,
|
|
177
|
+
borderRadius: 8,
|
|
178
|
+
border: "1px solid rgba(255, 255, 255, 0.10)",
|
|
179
|
+
background: "rgba(15, 23, 42, 0.65)",
|
|
180
|
+
color: "#f8fafc",
|
|
181
|
+
fontSize: 11,
|
|
182
|
+
outline: "none",
|
|
183
|
+
paddingLeft: 7,
|
|
184
|
+
paddingRight: 20,
|
|
185
|
+
appearance: "none",
|
|
186
|
+
cursor: "pointer"
|
|
187
|
+
}, ae = {
|
|
188
|
+
pointerEvents: "none",
|
|
189
|
+
position: "absolute",
|
|
190
|
+
right: 5,
|
|
191
|
+
top: 0,
|
|
192
|
+
bottom: 0,
|
|
193
|
+
display: "flex",
|
|
194
|
+
alignItems: "center",
|
|
195
|
+
color: "rgba(148, 163, 184, 0.9)"
|
|
196
|
+
}, ce = {
|
|
197
|
+
height: 24,
|
|
198
|
+
display: "flex",
|
|
199
|
+
alignItems: "center",
|
|
200
|
+
padding: "0 8px",
|
|
201
|
+
borderRadius: 8,
|
|
202
|
+
border: "1px solid rgba(255, 255, 255, 0.08)",
|
|
203
|
+
background: "rgba(255, 255, 255, 0.04)",
|
|
204
|
+
color: "rgba(226, 232, 240, 0.72)",
|
|
205
|
+
fontSize: 11
|
|
206
|
+
}, k = { width: 14, height: 14 }, me = ({
|
|
207
|
+
value: t,
|
|
208
|
+
onChange: r,
|
|
209
|
+
sources: s,
|
|
210
|
+
style: S
|
|
211
|
+
}) => {
|
|
212
|
+
const [M, g] = p(!1), [u, j] = p({ x: 16, y: 16 }), [a, h] = p(t.kind), c = R(null), m = W(
|
|
213
|
+
() => ({
|
|
214
|
+
webcam: s.filter((e) => e.kind === "webcam"),
|
|
215
|
+
image: s.filter((e) => e.kind === "image"),
|
|
216
|
+
video: s.filter((e) => e.kind === "video")
|
|
217
|
+
}),
|
|
218
|
+
[s]
|
|
219
|
+
), y = m[a];
|
|
220
|
+
b(() => {
|
|
221
|
+
h(t.kind);
|
|
222
|
+
}, [t.kind]);
|
|
223
|
+
const C = (e) => {
|
|
224
|
+
h(e);
|
|
225
|
+
const n = m[e][0];
|
|
226
|
+
n && r(n);
|
|
227
|
+
};
|
|
228
|
+
b(() => {
|
|
229
|
+
const e = (d) => {
|
|
230
|
+
c.current && j({
|
|
231
|
+
x: Math.max(8, d.clientX - c.current.x),
|
|
232
|
+
y: Math.max(8, d.clientY - c.current.y)
|
|
233
|
+
});
|
|
234
|
+
}, n = () => {
|
|
235
|
+
c.current = null, document.body.classList.remove("select-none");
|
|
236
|
+
};
|
|
237
|
+
return window.addEventListener("pointermove", e), window.addEventListener("pointerup", n), () => {
|
|
238
|
+
window.removeEventListener("pointermove", e), window.removeEventListener("pointerup", n);
|
|
239
|
+
};
|
|
240
|
+
}, []);
|
|
241
|
+
const f = (e) => {
|
|
242
|
+
const n = e.currentTarget.getBoundingClientRect();
|
|
243
|
+
c.current = {
|
|
244
|
+
x: e.clientX - n.left,
|
|
245
|
+
y: e.clientY - n.top
|
|
246
|
+
}, document.body.classList.add("select-none"), e.currentTarget.setPointerCapture(e.pointerId);
|
|
247
|
+
}, I = () => y.length === 0 ? /* @__PURE__ */ o("div", { style: ce, children: a === "webcam" ? "No camera source" : a === "image" ? "No image preview" : "No video preview" }) : /* @__PURE__ */ l("div", { style: ie, children: [
|
|
248
|
+
/* @__PURE__ */ o(
|
|
249
|
+
"select",
|
|
250
|
+
{
|
|
251
|
+
value: t.id,
|
|
252
|
+
onChange: (e) => {
|
|
253
|
+
const n = s.find((d) => d.id === e.target.value);
|
|
254
|
+
n && r(n);
|
|
255
|
+
},
|
|
256
|
+
style: se,
|
|
257
|
+
children: y.map((e) => /* @__PURE__ */ l("option", { value: e.id, children: [
|
|
258
|
+
Q(a),
|
|
259
|
+
e.label
|
|
260
|
+
] }, e.id))
|
|
261
|
+
}
|
|
262
|
+
),
|
|
263
|
+
/* @__PURE__ */ o("div", { style: ae, children: /* @__PURE__ */ o("span", { style: { width: 10, height: 10 }, children: /* @__PURE__ */ o(O, { size: 10, strokeWidth: 1.8 }) }) })
|
|
264
|
+
] });
|
|
265
|
+
return typeof document > "u" ? null : L(
|
|
266
|
+
/* @__PURE__ */ o("div", { style: { ...Z, left: u.x, top: u.y, ...S }, children: M ? (
|
|
267
|
+
// --- Expanded State ---
|
|
268
|
+
/* @__PURE__ */ l("div", { style: te, children: [
|
|
269
|
+
/* @__PURE__ */ l("div", { style: oe, children: [
|
|
270
|
+
/* @__PURE__ */ o("div", { onPointerDown: f, style: re, title: "Drag to move", children: /* @__PURE__ */ o(A, { size: 11, strokeWidth: 2 }) }),
|
|
271
|
+
/* @__PURE__ */ o(
|
|
272
|
+
"button",
|
|
273
|
+
{
|
|
274
|
+
type: "button",
|
|
275
|
+
onClick: () => g(!1),
|
|
276
|
+
style: x(!1),
|
|
277
|
+
title: "Close Menu",
|
|
278
|
+
children: /* @__PURE__ */ o("span", { style: k, children: /* @__PURE__ */ o($, { size: 14, strokeWidth: 1.8 }) })
|
|
279
|
+
}
|
|
280
|
+
),
|
|
281
|
+
/* @__PURE__ */ o("div", { style: ne }),
|
|
282
|
+
["webcam", "image", "video"].map((e) => /* @__PURE__ */ o(
|
|
283
|
+
"button",
|
|
284
|
+
{
|
|
285
|
+
type: "button",
|
|
286
|
+
onClick: () => C(e),
|
|
287
|
+
style: x(a === e),
|
|
288
|
+
title: e.charAt(0).toUpperCase() + e.slice(1),
|
|
289
|
+
children: /* @__PURE__ */ o("span", { style: k, children: e === "webcam" ? /* @__PURE__ */ o(B, { size: 14, strokeWidth: 1.8 }) : e === "image" ? /* @__PURE__ */ o(H, { size: 14, strokeWidth: 1.8 }) : /* @__PURE__ */ o(J, { size: 14, strokeWidth: 1.8 }) })
|
|
290
|
+
},
|
|
291
|
+
e
|
|
292
|
+
))
|
|
293
|
+
] }),
|
|
294
|
+
I()
|
|
295
|
+
] })
|
|
296
|
+
) : (
|
|
297
|
+
// --- Collapsed State (Minimal FAB) ---
|
|
298
|
+
/* @__PURE__ */ o(
|
|
299
|
+
"div",
|
|
300
|
+
{
|
|
301
|
+
style: ee,
|
|
302
|
+
onClick: () => g(!0),
|
|
303
|
+
onPointerDown: f,
|
|
304
|
+
title: "Open Media Menu",
|
|
305
|
+
children: /* @__PURE__ */ o("span", { style: { width: 16, height: 16 }, children: /* @__PURE__ */ o(Y, { size: 14, strokeWidth: 1.8 }) })
|
|
306
|
+
}
|
|
307
|
+
)
|
|
308
|
+
) }),
|
|
309
|
+
document.body
|
|
310
|
+
);
|
|
311
|
+
};
|
|
312
|
+
export {
|
|
313
|
+
me as M,
|
|
314
|
+
V as a,
|
|
315
|
+
he as d,
|
|
316
|
+
U as s
|
|
317
|
+
};
|