@popmelt.com/core 0.16.0 → 0.18.0
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/README.md +17 -9
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/adapter-three.d.mts +71 -1
- package/dist/adapter-three.mjs +1 -1
- package/dist/bridge-entry.mjs +51 -51
- package/dist/{chunk-RYUNKCXK.mjs → chunk-GQOEG3E5.mjs} +1 -1
- package/dist/chunk-RS3XXK26.mjs +2 -0
- package/dist/cli.mjs +70 -70
- package/dist/index.d.mts +11 -4
- package/dist/index.mjs +40 -40
- package/dist/media.d.mts +20 -3
- package/dist/media.mjs +1 -1
- package/dist/plugin-astro.mjs +1 -1
- package/dist/plugin-vite.mjs +1 -1
- package/dist/{server-F4EKHTYF.mjs → server-MUVH7FWR.mjs} +53 -53
- package/dist/server.mjs +53 -53
- package/dist/{image-adapter-D8L8QumX.d.mts → video-adapter-DzNtG67g.d.mts} +324 -2
- package/package.json +1 -1
- package/dist/chunk-HS4EVUFK.mjs +0 -2
package/README.md
CHANGED
|
@@ -243,10 +243,10 @@ If the toolbar opens but cannot reach an AI, check that:
|
|
|
243
243
|
- The framework plugin or config wrapper is installed
|
|
244
244
|
- The development server was restarted after installation
|
|
245
245
|
|
|
246
|
-
##
|
|
246
|
+
## Media annotation
|
|
247
247
|
|
|
248
|
-
Manual image annotation
|
|
249
|
-
vision dependency or model download.
|
|
248
|
+
Manual image and video annotation are enabled by default in `PopmeltProvider`,
|
|
249
|
+
without a vision dependency or model download.
|
|
250
250
|
|
|
251
251
|
```tsx
|
|
252
252
|
<PopmeltProvider>{children}</PopmeltProvider>
|
|
@@ -254,23 +254,31 @@ vision dependency or model download.
|
|
|
254
254
|
// Optional: disable image annotation.
|
|
255
255
|
<PopmeltProvider imageAnnotation={false}>{children}</PopmeltProvider>
|
|
256
256
|
|
|
257
|
+
// Optional: disable video annotation.
|
|
258
|
+
<PopmeltProvider videoAnnotation={false}>{children}</PopmeltProvider>
|
|
259
|
+
|
|
257
260
|
// Optional: limit image discovery (all img elements are included by default).
|
|
258
261
|
<PopmeltProvider imageAnnotation={{ selector: 'img[data-popmelt-media]' }}>
|
|
259
262
|
{children}
|
|
260
263
|
</PopmeltProvider>
|
|
261
264
|
```
|
|
262
265
|
|
|
263
|
-
In
|
|
266
|
+
In Comment, drag over an image to create a named object. Double-click an object to
|
|
264
267
|
rename or resize it, single-click it to comment, and right-click its label to
|
|
265
268
|
delete it. Saved targets stay aligned as the image resizes.
|
|
266
269
|
|
|
270
|
+
The same interactions work on a paused video. A manual box stays at the saved
|
|
271
|
+
position unless a perception extension supplies motion tracking. Motion tracks
|
|
272
|
+
produced by an extension are persisted and remain replayable by Core alone.
|
|
273
|
+
|
|
267
274
|
Image targets use browser-local storage by default. Supply
|
|
268
275
|
`imageAnnotation.store` when the project needs another persistence layer. Add a
|
|
269
276
|
stable `data-popmelt-media-id` to images whose URL may change or whose source
|
|
270
|
-
appears more than once:
|
|
277
|
+
appears more than once. Use the same attribute for durable video identities:
|
|
271
278
|
|
|
272
279
|
```tsx
|
|
273
280
|
<img data-popmelt-media-id="hero-product" src={imageUrl} alt="Product" />
|
|
281
|
+
<video data-popmelt-media-id="hero-reel" src={videoUrl} controls />
|
|
274
282
|
```
|
|
275
283
|
|
|
276
284
|
No recognition or model download occurs unless See is installed and enabled.
|
|
@@ -278,8 +286,8 @@ No recognition or model download occurs unless See is installed and enabled.
|
|
|
278
286
|
## Add local object recognition with See
|
|
279
287
|
|
|
280
288
|
[`@popmelt.com/see`](https://www.npmjs.com/package/@popmelt.com/see) adds
|
|
281
|
-
browser-local object detection to the existing
|
|
282
|
-
requires Core 0.
|
|
289
|
+
browser-local object detection and optional video tracking to the existing media
|
|
290
|
+
annotation experience. It requires Core 0.17.0 or newer.
|
|
283
291
|
|
|
284
292
|
Install it:
|
|
285
293
|
|
|
@@ -297,12 +305,12 @@ import { see } from '@popmelt.com/see';
|
|
|
297
305
|
```
|
|
298
306
|
|
|
299
307
|
Recognition loads only when it is needed. Other extensions can be included in
|
|
300
|
-
the same array. Model selection, self-hosting, and
|
|
308
|
+
the same array. Model selection, self-hosting, recognition, and video tracking are
|
|
301
309
|
documented in the See package README.
|
|
302
310
|
|
|
303
311
|
## Data and privacy
|
|
304
312
|
|
|
305
|
-
Popmelt stores its annotations, screenshots, threads, UI preferences, and Imprint locally under `.popmelt/`. Unsent annotation drafts, preview state, and the default
|
|
313
|
+
Popmelt stores its annotations, screenshots, threads, UI preferences, and Imprint locally under `.popmelt/`. Unsent annotation drafts, preview state, and the default media-object stores use origin-scoped browser storage. Supply project-backed `imageAnnotation.store` and `videoAnnotation.store` implementations when media targets must survive browser-site-data removal or move between origins.
|
|
306
314
|
|
|
307
315
|
When you send a thread, the context needed to complete that work is passed to your selected AI provider and is subject to that provider's privacy policy.
|
|
308
316
|
|
package/dist/adapter-three.d.mts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { Camera, Object3D, Raycaster, WebGLRenderer } from 'three';
|
|
2
2
|
|
|
3
|
+
type AnnotationRevealStep = {
|
|
4
|
+
triggerSelector: string;
|
|
5
|
+
targetSelector?: string;
|
|
6
|
+
label: string;
|
|
7
|
+
};
|
|
8
|
+
type AnnotationRevealRecipe = {
|
|
9
|
+
steps: AnnotationRevealStep[];
|
|
10
|
+
location: string[];
|
|
11
|
+
};
|
|
12
|
+
type ReactSourceLocation = {
|
|
13
|
+
fileName: string;
|
|
14
|
+
lineNumber: number;
|
|
15
|
+
columnNumber?: number;
|
|
16
|
+
};
|
|
3
17
|
/** Coordinates refer to the original image, not its responsive DOM rectangle. */
|
|
4
18
|
type ImageObjectContext = {
|
|
5
19
|
src: string;
|
|
@@ -19,6 +33,47 @@ type ImageObjectContext = {
|
|
|
19
33
|
height: number;
|
|
20
34
|
};
|
|
21
35
|
};
|
|
36
|
+
/** Coordinates refer to the decoded video frame, not its responsive DOM box. */
|
|
37
|
+
type VideoObjectContext = {
|
|
38
|
+
src: string;
|
|
39
|
+
width: number;
|
|
40
|
+
height: number;
|
|
41
|
+
duration: number;
|
|
42
|
+
currentTime: number;
|
|
43
|
+
normalizedBox: {
|
|
44
|
+
x: number;
|
|
45
|
+
y: number;
|
|
46
|
+
width: number;
|
|
47
|
+
height: number;
|
|
48
|
+
};
|
|
49
|
+
box: {
|
|
50
|
+
x: number;
|
|
51
|
+
y: number;
|
|
52
|
+
width: number;
|
|
53
|
+
height: number;
|
|
54
|
+
};
|
|
55
|
+
trackStart: number;
|
|
56
|
+
trackEnd: number;
|
|
57
|
+
};
|
|
58
|
+
type ElementInfo = {
|
|
59
|
+
selector: string;
|
|
60
|
+
tagName: string;
|
|
61
|
+
/** Adapter-owned semantic name for a non-DOM subject. */
|
|
62
|
+
subjectLabel?: string;
|
|
63
|
+
id?: string;
|
|
64
|
+
className?: string;
|
|
65
|
+
textContent?: string;
|
|
66
|
+
dataAttributes?: Record<string, string>;
|
|
67
|
+
reactComponent?: string;
|
|
68
|
+
reactComponentPath?: string[];
|
|
69
|
+
reactSource?: ReactSourceLocation;
|
|
70
|
+
imageObject?: ImageObjectContext;
|
|
71
|
+
videoObject?: VideoObjectContext;
|
|
72
|
+
/** The real DOM surface, rather than the subject's hidden projection. */
|
|
73
|
+
sourceElement?: ElementInfo;
|
|
74
|
+
context?: string;
|
|
75
|
+
revealRecipe?: AnnotationRevealRecipe;
|
|
76
|
+
};
|
|
22
77
|
|
|
23
78
|
type PopmeltSubjectBounds = {
|
|
24
79
|
left: number;
|
|
@@ -26,6 +81,12 @@ type PopmeltSubjectBounds = {
|
|
|
26
81
|
width: number;
|
|
27
82
|
height: number;
|
|
28
83
|
};
|
|
84
|
+
type PopmeltSubjectTrackingAnchor = {
|
|
85
|
+
/** Horizontal position normalized within the subject bounds. */
|
|
86
|
+
x: number;
|
|
87
|
+
/** Vertical position normalized within the subject bounds. */
|
|
88
|
+
y: number;
|
|
89
|
+
};
|
|
29
90
|
type PopmeltSubject = {
|
|
30
91
|
key: string;
|
|
31
92
|
label: string;
|
|
@@ -37,6 +98,9 @@ type PopmeltSubject = {
|
|
|
37
98
|
/** Live owning surface; never serialized or included in model context. */
|
|
38
99
|
sourceElement?: Element;
|
|
39
100
|
imageObject?: ImageObjectContext;
|
|
101
|
+
videoObject?: VideoObjectContext;
|
|
102
|
+
/** Optional provider-neutral foreground anchors used by motion tracking. */
|
|
103
|
+
trackingAnchors?: readonly PopmeltSubjectTrackingAnchor[];
|
|
40
104
|
};
|
|
41
105
|
type PopmeltSurfaceSnapshot = {
|
|
42
106
|
element: HTMLCanvasElement;
|
|
@@ -58,12 +122,16 @@ type PopmeltSubjectCreationSession = {
|
|
|
58
122
|
type PopmeltSubjectAdapter = {
|
|
59
123
|
id: string;
|
|
60
124
|
/** Surface ownership; an explicit image adapter replaces Core's default. */
|
|
61
|
-
surfaceKind?: 'image';
|
|
125
|
+
surfaceKind?: 'image' | 'video';
|
|
126
|
+
/** Resolve once the adapter's persisted subject index is available. */
|
|
127
|
+
whenReady?(): Promise<void>;
|
|
62
128
|
hitTest(clientPoint: {
|
|
63
129
|
x: number;
|
|
64
130
|
y: number;
|
|
65
131
|
}): PopmeltSubject | null;
|
|
66
132
|
resolve(key: string): PopmeltSubject | null;
|
|
133
|
+
/** Recreate a missing durable subject from annotation-captured context. */
|
|
134
|
+
restoreSubject?(key: string, elementInfo: ElementInfo): PopmeltSubject | null;
|
|
67
135
|
/** Optional clean subject crop, without Popmelt's overlay. */
|
|
68
136
|
captureSubject?(key: string): Blob | null | Promise<Blob | null>;
|
|
69
137
|
/** Optional bitmap capture for renderer surfaces that DOM cloning cannot read. */
|
|
@@ -77,6 +145,8 @@ type PopmeltSubjectAdapter = {
|
|
|
77
145
|
renameSubject?(key: string, label: string): PopmeltSubject | null;
|
|
78
146
|
/** Replace a subject's viewport bounds, projected back into adapter space. */
|
|
79
147
|
updateSubjectBounds?(key: string, bounds: PopmeltSubjectBounds): PopmeltSubject | null;
|
|
148
|
+
/** Replace optional foreground anchors normalized within subject bounds. */
|
|
149
|
+
updateSubjectTrackingAnchors?(key: string, anchors: readonly PopmeltSubjectTrackingAnchor[]): PopmeltSubject | null;
|
|
80
150
|
/** Remove a user-created subject or hide an adapter-owned subject. */
|
|
81
151
|
removeSubject?(key: string): void;
|
|
82
152
|
};
|
package/dist/adapter-three.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{Box3 as y,Raycaster as x,Vector2 as O,Vector3 as j}from"three";function b(e){return[e.x,e.y,e.z].map(n=>Number(n.toFixed(4))).join(", ")}function h(e){let n=e.material;return n&&(Array.isArray(n)?n:[n]).map(r=>r.name?.trim()||r.type).filter(Boolean).join(", ")||void 0}function M(e,n,c){let s=e.domElement.width,r=e.domElement.height;if(s<=0||r<=0)return null;let l=e.getRenderTarget(),t=e.xr.enabled;try{return e.xr.enabled=!1,e.setRenderTarget(null),e.render(n,c),e.domElement.toDataURL("image/png")}catch{return null}finally{e.setRenderTarget(l),e.xr.enabled=t}}function D(e){let s=(e.parent?.children??[]).filter(t=>t.type===e.type&&t.name===e.name),r=Math.max(0,s.indexOf(e)),l=e.name.trim()||e.type;return`${encodeURIComponent(l)}[${r}]`}function f(e){let n=[],c=e;for(;c?.parent;)n.push(D(c)),c=c.parent;return n.reverse().join("/")}function v(e){let n=e.userData.popmeltId;return typeof n=="string"&&n.trim()?n.trim():f(e)}function T(e){let n=e.userData.popmeltLabel;return typeof n=="string"&&n.trim()?n.trim():e.name.trim()||e.type}function R(e){let n=e;for(;n.parent;){let c=n.name.trim(),s=c.match(/^(.*)_(?:alpha|normal|material)(?:_\d+)+$/i),r=n.parent;for(;r.parent&&!r.name.trim();)r=r.parent;let l=r.name.trim();if(!l||!c.startsWith(`${l}_`)&&!s)break;n=r}return n}function P(e,n,c){let s=new y().setFromObject(e,!0);if(s.isEmpty())return null;let r=c.getBoundingClientRect(),l=[];for(let p of[s.min.x,s.max.x])for(let d of[s.min.y,s.max.y])for(let g of[s.min.z,s.max.z])l.push(new j(p,d,g).project(n));let t=l.map(p=>r.left+(p.x+1)/2*r.width),a=l.map(p=>r.top+(1-p.y)/2*r.height),i=Math.max(r.left,Math.min(...t)),
|
|
1
|
+
import{Box3 as y,Raycaster as x,Vector2 as O,Vector3 as j}from"three";function b(e){return[e.x,e.y,e.z].map(n=>Number(n.toFixed(4))).join(", ")}function h(e){let n=e.material;return n&&(Array.isArray(n)?n:[n]).map(r=>r.name?.trim()||r.type).filter(Boolean).join(", ")||void 0}function M(e,n,c){let s=e.domElement.width,r=e.domElement.height;if(s<=0||r<=0)return null;let l=e.getRenderTarget(),t=e.xr.enabled;try{return e.xr.enabled=!1,e.setRenderTarget(null),e.render(n,c),e.domElement.toDataURL("image/png")}catch{return null}finally{e.setRenderTarget(l),e.xr.enabled=t}}function D(e){let s=(e.parent?.children??[]).filter(t=>t.type===e.type&&t.name===e.name),r=Math.max(0,s.indexOf(e)),l=e.name.trim()||e.type;return`${encodeURIComponent(l)}[${r}]`}function f(e){let n=[],c=e;for(;c?.parent;)n.push(D(c)),c=c.parent;return n.reverse().join("/")}function v(e){let n=e.userData.popmeltId;return typeof n=="string"&&n.trim()?n.trim():f(e)}function T(e){let n=e.userData.popmeltLabel;return typeof n=="string"&&n.trim()?n.trim():e.name.trim()||e.type}function R(e){let n=e;for(;n.parent;){let c=n.name.trim(),s=c.match(/^(.*)_(?:alpha|normal|material)(?:_\d+)+$/i),r=n.parent;for(;r.parent&&!r.name.trim();)r=r.parent;let l=r.name.trim();if(!l||!c.startsWith(`${l}_`)&&!s)break;n=r}return n}function P(e,n,c){let s=new y().setFromObject(e,!0);if(s.isEmpty())return null;let r=c.getBoundingClientRect(),l=[];for(let p of[s.min.x,s.max.x])for(let d of[s.min.y,s.max.y])for(let g of[s.min.z,s.max.z])l.push(new j(p,d,g).project(n));let t=l.map(p=>r.left+(p.x+1)/2*r.width),a=l.map(p=>r.top+(1-p.y)/2*r.height),i=Math.max(r.left,Math.min(...t)),u=Math.min(r.right,Math.max(...t)),m=Math.max(r.top,Math.min(...a)),o=Math.min(r.bottom,Math.max(...a));return u<=i||o<=m?null:{left:i,top:m,width:u-i,height:o-m}}function C(e){let n=e.raycaster??new x,c=new O,s=t=>e.key?.(t)??v(t),r=(t,a)=>{let i=s(t);if(!i)return null;let u=P(t,e.camera,e.canvas);return u?{key:i,label:e.label?.(t)??T(t),sourceElement:e.canvas,bounds:u,context:f(t),data:{renderer:"Three.js",objectType:t.type,...t.name?{objectName:t.name}:{},scenePath:f(t),...a&&a!==t?{renderObjectName:a.name.trim()||a.type,renderObjectType:a.type,renderScenePath:f(a)}:{},position:b(t.position),rotation:b(t.rotation),scale:b(t.scale),visible:String(t.visible),...h(t)?{material:h(t)}:{},...Object.fromEntries(Object.entries(e.describe?.(t)??{}).filter(m=>typeof m[1]=="string"&&m[1].length>0))}}:null},l=t=>{let a=null;return e.scene.traverse(i=>{!a&&s(i)===t&&(a=i)}),a};return{id:"three",hitTest({x:t,y:a}){let i=e.canvas.getBoundingClientRect();if(t<i.left||t>i.right||a<i.top||a>i.bottom)return null;c.set((t-i.left)/i.width*2-1,-((a-i.top)/i.height)*2+1),n.setFromCamera(c,e.camera);for(let u of n.intersectObject(e.scene,!0)){let m=e.select?e.select(u.object):R(u.object);if(!m||!m.visible)continue;let o=r(m,u.object);if(o)return o}return null},resolve(t){let a=l(t);return a?r(a):null},...e.captureFrame?{async captureSurfaces(){let t=await e.captureFrame?.();return t?[{element:e.canvas,dataUrl:t}]:[]}}:{}}}export{M as captureThreeRendererFrame,C as createThreeSubjectAdapter,R as selectThreeNamedPrimitiveParent,f as threeObjectPath};
|