@popmelt.com/core 0.15.0 → 0.17.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 +73 -1
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/adapter-three.d.mts +119 -0
- package/dist/bridge-entry.mjs +2 -2
- package/dist/{chunk-H4AAYYUP.mjs → chunk-D5CD4PIZ.mjs} +1 -1
- package/dist/chunk-RS3XXK26.mjs +2 -0
- package/dist/cli.mjs +32 -32
- package/dist/index.d.mts +29 -225
- package/dist/index.mjs +64 -39
- package/dist/media.d.mts +31 -0
- package/dist/media.mjs +2 -0
- package/dist/plugin-astro.mjs +1 -1
- package/dist/plugin-vite.mjs +1 -1
- package/dist/{server-GNK5OVVW.mjs → server-XGSUCWFZ.mjs} +38 -38
- package/dist/server.mjs +57 -57
- package/dist/video-adapter-cDwq4uNP.d.mts +666 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -29,6 +29,13 @@ From inside your project, give your coding agent this prompt:
|
|
|
29
29
|
|
|
30
30
|
Your agent can detect the framework, add the provider and integration, and preserve your existing development command. Restart the development server when it is finished.
|
|
31
31
|
|
|
32
|
+
To add local object recognition to an existing Popmelt project, use:
|
|
33
|
+
|
|
34
|
+
> Add `@popmelt.com/see` to this Popmelt Core project
|
|
35
|
+
|
|
36
|
+
The integration installs See and adds it to the existing Core provider's
|
|
37
|
+
`extensions` array without replacing Core.
|
|
38
|
+
|
|
32
39
|
## Use Popmelt
|
|
33
40
|
|
|
34
41
|
1. Run your project locally and open it in a browser
|
|
@@ -236,9 +243,74 @@ If the toolbar opens but cannot reach an AI, check that:
|
|
|
236
243
|
- The framework plugin or config wrapper is installed
|
|
237
244
|
- The development server was restarted after installation
|
|
238
245
|
|
|
246
|
+
## Media annotation
|
|
247
|
+
|
|
248
|
+
Manual image and video annotation are enabled by default in `PopmeltProvider`,
|
|
249
|
+
without a vision dependency or model download.
|
|
250
|
+
|
|
251
|
+
```tsx
|
|
252
|
+
<PopmeltProvider>{children}</PopmeltProvider>
|
|
253
|
+
|
|
254
|
+
// Optional: disable image annotation.
|
|
255
|
+
<PopmeltProvider imageAnnotation={false}>{children}</PopmeltProvider>
|
|
256
|
+
|
|
257
|
+
// Optional: disable video annotation.
|
|
258
|
+
<PopmeltProvider videoAnnotation={false}>{children}</PopmeltProvider>
|
|
259
|
+
|
|
260
|
+
// Optional: limit image discovery (all img elements are included by default).
|
|
261
|
+
<PopmeltProvider imageAnnotation={{ selector: 'img[data-popmelt-media]' }}>
|
|
262
|
+
{children}
|
|
263
|
+
</PopmeltProvider>
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
In Comment, drag over an image to create a named object. Double-click an object to
|
|
267
|
+
rename or resize it, single-click it to comment, and right-click its label to
|
|
268
|
+
delete it. Saved targets stay aligned as the image resizes.
|
|
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
|
+
|
|
274
|
+
Image targets use browser-local storage by default. Supply
|
|
275
|
+
`imageAnnotation.store` when the project needs another persistence layer. Add a
|
|
276
|
+
stable `data-popmelt-media-id` to images whose URL may change or whose source
|
|
277
|
+
appears more than once. Use the same attribute for durable video identities:
|
|
278
|
+
|
|
279
|
+
```tsx
|
|
280
|
+
<img data-popmelt-media-id="hero-product" src={imageUrl} alt="Product" />
|
|
281
|
+
<video data-popmelt-media-id="hero-reel" src={videoUrl} controls />
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
No recognition or model download occurs unless See is installed and enabled.
|
|
285
|
+
|
|
286
|
+
## Add local object recognition with See
|
|
287
|
+
|
|
288
|
+
[`@popmelt.com/see`](https://www.npmjs.com/package/@popmelt.com/see) adds
|
|
289
|
+
browser-local object detection and optional video tracking to the existing media
|
|
290
|
+
annotation experience. It requires Core 0.17.0 or newer.
|
|
291
|
+
|
|
292
|
+
Install it:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
npm install @popmelt.com/see
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Then add the extension to the existing Core provider:
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
import { PopmeltProvider } from '@popmelt.com/core';
|
|
302
|
+
import { see } from '@popmelt.com/see';
|
|
303
|
+
|
|
304
|
+
<PopmeltProvider extensions={[see]}>{children}</PopmeltProvider>;
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Recognition loads only when it is needed. Other extensions can be included in
|
|
308
|
+
the same array. Model selection, self-hosting, recognition, and video tracking are
|
|
309
|
+
documented in the See package README.
|
|
310
|
+
|
|
239
311
|
## Data and privacy
|
|
240
312
|
|
|
241
|
-
Popmelt stores its annotations, screenshots, threads, UI preferences, and Imprint locally under `.popmelt/`. Unsent annotation drafts
|
|
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.
|
|
242
314
|
|
|
243
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.
|
|
244
316
|
|
package/dist/adapter-three.d.mts
CHANGED
|
@@ -1,35 +1,154 @@
|
|
|
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
|
+
};
|
|
17
|
+
/** Coordinates refer to the original image, not its responsive DOM rectangle. */
|
|
18
|
+
type ImageObjectContext = {
|
|
19
|
+
src: string;
|
|
20
|
+
alt: string;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
box: {
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
};
|
|
29
|
+
normalizedBox: {
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
};
|
|
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
|
+
};
|
|
77
|
+
|
|
3
78
|
type PopmeltSubjectBounds = {
|
|
4
79
|
left: number;
|
|
5
80
|
top: number;
|
|
6
81
|
width: number;
|
|
7
82
|
height: number;
|
|
8
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
|
+
};
|
|
9
90
|
type PopmeltSubject = {
|
|
10
91
|
key: string;
|
|
11
92
|
label: string;
|
|
12
93
|
bounds: PopmeltSubjectBounds;
|
|
94
|
+
/** Viewport bounds of the renderer surface that owns this subject. */
|
|
95
|
+
surfaceBounds?: PopmeltSubjectBounds;
|
|
13
96
|
context?: string;
|
|
14
97
|
data?: Record<string, string>;
|
|
98
|
+
/** Live owning surface; never serialized or included in model context. */
|
|
99
|
+
sourceElement?: Element;
|
|
100
|
+
imageObject?: ImageObjectContext;
|
|
101
|
+
videoObject?: VideoObjectContext;
|
|
102
|
+
/** Optional provider-neutral foreground anchors used by motion tracking. */
|
|
103
|
+
trackingAnchors?: readonly PopmeltSubjectTrackingAnchor[];
|
|
15
104
|
};
|
|
16
105
|
type PopmeltSurfaceSnapshot = {
|
|
17
106
|
element: HTMLCanvasElement;
|
|
18
107
|
dataUrl: string;
|
|
19
108
|
};
|
|
109
|
+
type PopmeltSubjectCreationSession = {
|
|
110
|
+
key: string;
|
|
111
|
+
update(clientPoint: {
|
|
112
|
+
x: number;
|
|
113
|
+
y: number;
|
|
114
|
+
}): PopmeltSubject | null;
|
|
115
|
+
commit(): PopmeltSubject | null;
|
|
116
|
+
cancel(): void;
|
|
117
|
+
};
|
|
20
118
|
/**
|
|
21
119
|
* Minimal renderer boundary for non-DOM subjects. The adapter owns hit testing,
|
|
22
120
|
* durable keys, and viewport projection; Popmelt owns annotations and threads.
|
|
23
121
|
*/
|
|
24
122
|
type PopmeltSubjectAdapter = {
|
|
25
123
|
id: string;
|
|
124
|
+
/** Surface ownership; an explicit image adapter replaces Core's default. */
|
|
125
|
+
surfaceKind?: 'image' | 'video';
|
|
126
|
+
/** Resolve once the adapter's persisted subject index is available. */
|
|
127
|
+
whenReady?(): Promise<void>;
|
|
26
128
|
hitTest(clientPoint: {
|
|
27
129
|
x: number;
|
|
28
130
|
y: number;
|
|
29
131
|
}): PopmeltSubject | null;
|
|
30
132
|
resolve(key: string): PopmeltSubject | null;
|
|
133
|
+
/** Recreate a missing durable subject from annotation-captured context. */
|
|
134
|
+
restoreSubject?(key: string, elementInfo: ElementInfo): PopmeltSubject | null;
|
|
135
|
+
/** Optional clean subject crop, without Popmelt's overlay. */
|
|
136
|
+
captureSubject?(key: string): Blob | null | Promise<Blob | null>;
|
|
31
137
|
/** Optional bitmap capture for renderer surfaces that DOM cloning cannot read. */
|
|
32
138
|
captureSurfaces?(): readonly PopmeltSurfaceSnapshot[] | Promise<readonly PopmeltSurfaceSnapshot[]>;
|
|
139
|
+
/** Begin a context-aware subject-creation gesture at a viewport point. */
|
|
140
|
+
beginCreate?(clientPoint: {
|
|
141
|
+
x: number;
|
|
142
|
+
y: number;
|
|
143
|
+
}): PopmeltSubjectCreationSession | null;
|
|
144
|
+
/** Rename a subject without changing its durable key. */
|
|
145
|
+
renameSubject?(key: string, label: string): PopmeltSubject | null;
|
|
146
|
+
/** Replace a subject's viewport bounds, projected back into adapter space. */
|
|
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;
|
|
150
|
+
/** Remove a user-created subject or hide an adapter-owned subject. */
|
|
151
|
+
removeSubject?(key: string): void;
|
|
33
152
|
};
|
|
34
153
|
|
|
35
154
|
type ThreeSubjectAdapterOptions = {
|
package/dist/bridge-entry.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import{mkdir as iu,unlink as su,writeFile as au}from"fs/promises";import{join as
|
|
|
3
3
|
`);let o=new Map;for(let r of e){let i=r.pathname||"(unknown)",s=o.get(i)??[];s.push(r.instruction),o.set(i,s)}return[...o.entries()].map(([r,i])=>`\`${r}\`
|
|
4
4
|
${i.map(s=>`- ${s}`).join(`
|
|
5
5
|
`)}`).join(`
|
|
6
|
-
`)}var oi={name:"@popmelt.com/core",version:"0.
|
|
6
|
+
`)}var oi={name:"@popmelt.com/core",version:"0.17.0",description:"A tiny, powerful design layer for humans and AI",license:"PolyForm-Shield-1.0.0",author:"Popmelt <reb@popmelt.com> (https://popmelt.com)",homepage:"https://github.com/popmelt/core",bugs:{url:"https://github.com/popmelt/core/issues"},repository:{type:"git",url:"git+https://github.com/popmelt/core.git"},keywords:["ai","annotations","collaboration","devtools","react","visual-feedback"],bin:{popmelt:"dist/cli.mjs"},sideEffects:!1,engines:{node:">=18.0.0"},scripts:{build:"tsup",clean:"git clean -xdf .turbo node_modules dist",format:'prettier --check "**/*.{ts,tsx}"',lint:"eslint . && node scripts/check-eslint-suppressions.mjs","pack:smoke":"node scripts/pack-smoke.mjs","peer:smoke":"node scripts/peer-compat-smoke.mjs",prepublishOnly:"pnpm run verify",test:"vitest run","test:coverage":"vitest run --coverage.enabled","test:watch":"vitest",typecheck:"tsc --noEmit --incremental false",verify:"pnpm run typecheck && pnpm run format && pnpm run lint && pnpm run test:coverage && pnpm run build && pnpm run pack:smoke"},exports:{".":{types:"./dist/index.d.mts",import:"./dist/index.mjs"},"./provider":{types:"./dist/index.d.mts",import:"./dist/index.mjs"},"./server":{types:"./dist/server.d.mts",import:"./dist/server.mjs"},"./memory":{types:"./dist/memory.d.mts",import:"./dist/memory.mjs"},"./memory/legacy":{types:"./dist/memory-legacy.d.mts",import:"./dist/memory-legacy.mjs"},"./memory/store":{types:"./dist/memory-store.d.mts",import:"./dist/memory-store.mjs"},"./next":{types:"./dist/plugin-next.d.mts",import:"./dist/plugin-next.mjs",default:"./dist/plugin-next.mjs"},"./vite":{types:"./dist/plugin-vite.d.mts",import:"./dist/plugin-vite.mjs",default:"./dist/plugin-vite.mjs"},"./astro":{types:"./dist/plugin-astro.d.mts",import:"./dist/plugin-astro.mjs",default:"./dist/plugin-astro.mjs"},"./media":{types:"./dist/media.d.mts",import:"./dist/media.mjs"},"./three":{types:"./dist/adapter-three.d.mts",import:"./dist/adapter-three.mjs"}},files:["dist","codex-plugin","LICENSE","README.md","THIRD_PARTY_NOTICES.md"],dependencies:{"modern-screenshot":"4.7.0",ws:"8.21.3"},peerDependencies:{"lucide-react":">=1.23.0",next:">=14.0.0",react:">=18.0.0","react-dom":">=18.0.0",three:">=0.150.0"},peerDependenciesMeta:{next:{optional:!0},three:{optional:!0}},devDependencies:{"@kit/eslint-config":"workspace:^","@tanstack/react-router":"1.120.5","@types/react":"19.1.0","@types/react-dom":"19.1.2","@types/three":"^0.185.4","@types/ws":"8.18.1","@vitejs/plugin-react":"^5.1.1","@vitest/coverage-v8":"3.2.4",eslint:"^9.24.0",geist:"1.7.2",jsdom:"26.1.0","lucide-react":"^1.23.0",next:"15.3.8",prettier:"^3.5.3",react:"19.1.0","react-dom":"19.1.0",three:"^0.185.1",tsup:"^8.5.1",typescript:"^5.8.3",vite:"7.3.6",vitest:"^3.2.4"}};var yn=oi.version;var $a=new Set(["minimal","low","medium","high","xhigh","max","ultra"]),Oa=new Set(["default","flex","fast","priority"]);function Ut(n){let e=typeof n.reasoningEffort=="string"&&$a.has(n.reasoningEffort)?n.reasoningEffort:void 0,t=typeof n.serviceTier=="string"&&Oa.has(n.serviceTier)?n.serviceTier:void 0;return e||t?{reasoningEffort:e,serviceTier:t}:void 0}function eo(n){let e=[];return n?.reasoningEffort&&e.push(`model_reasoning_effort=${JSON.stringify(n.reasoningEffort)}`),n?.serviceTier&&e.push(`service_tier=${JSON.stringify(n.serviceTier)}`),e}import{createHash as Yd,randomUUID as Zd}from"crypto";import{isAbsolute as ec,relative as tc}from"path";import{randomUUID as ft}from"crypto";var ri={project:0,route:1,component:2,selector:2,file:3},ii={intention:0,principle:1,pattern:2};function zt(n,e){let t=ri[n.scope.kind]-ri[e.scope.kind];return t!==0?t:ii[n.kind]-ii[e.kind]}function si(n){return n==="expresses"||n==="excepts"}function Da(n,e){if(e.type==="expresses"||e.type==="excepts")return structuredClone(e);if(e.type==="operationalizes")return{...structuredClone(e),type:"expresses"};if(e.type==="instantiates")return{...structuredClone(e),type:"expresses",fromId:e.toId,toId:e.fromId};if(e.type==="refines"){let t=n.explanations.find(i=>i.id===e.fromId),o=n.explanations.find(i=>i.id===e.toId);if(!t||!o)return;let r=zt(t,o);return r===0?void 0:r>0?{...structuredClone(e),type:"expresses"}:{...structuredClone(e),type:"expresses",fromId:e.toId,toId:e.fromId}}}function Mt(n){let e=structuredClone(n),t=new Set;return e.relations=n.relations.flatMap(o=>{let r=Da(n,o);if(!r)return[];let i=`${r.type}:${r.fromId}:${r.toStoreId??""}:${r.toId}`;return t.has(i)?[]:(t.add(i),[r])}),e}import{createHash as Na}from"crypto";import{mkdir as Ba,open as Ja,readFile as ai,readdir as qa,rename as La,unlink as Fa}from"fs/promises";import{dirname as Ua,join as Gt}from"path";function Ge(n){return structuredClone(n)}function di(n){return Na("sha256").update(JSON.stringify(n)).digest("hex")}async function jt(n,e){await Ba(Ua(n),{recursive:!0});let t=`${n}.${process.pid}.${Date.now()}.tmp`,o=await Ja(t,"w");try{await o.writeFile(`${JSON.stringify(e,null,2)}
|
|
7
7
|
`,"utf8"),await o.sync()}finally{await o.close()}try{await La(t,n)}catch(r){throw await Fa(t).catch(()=>{}),r}}function za(n,e){return{version:1,projectId:n,createdAt:e,updatedAt:e,head:{sequence:0,hash:null},explanations:[],concrete:[],relations:[]}}function Ga(n,e){let t=n.explanations.findIndex(o=>o.id===e.id);t>=0?n.explanations[t]=Ge(e):n.explanations.push(Ge(e))}function Qo(n,e){let t=Ge(n);for(let o of e)if(o.op==="add_explanation"){if(t.explanations.some(r=>r.id===o.explanation.id))throw new Error(`Imprint explanation already exists: ${o.explanation.id}`);t.explanations.push(Ge(o.explanation))}else if(o.op==="replace_explanation")Ga(t,o.explanation);else if(o.op==="remove_explanation")t.explanations=t.explanations.filter(r=>r.id!==o.explanationId),t.relations=t.relations.filter(r=>r.fromId!==o.explanationId&&(r.toStoreId!==void 0||r.toId!==o.explanationId));else if(o.op==="add_concrete")t.concrete.some(r=>r.id===o.concrete.id)||t.concrete.push(Ge(o.concrete));else if(o.op==="remove_concrete")t.concrete=t.concrete.filter(r=>r.id!==o.concreteId),t.relations=t.relations.filter(r=>r.fromId!==o.concreteId&&(r.toStoreId!==void 0||r.toId!==o.concreteId));else if(o.op==="add_evidence"){let r=t.explanations.find(i=>i.id===o.explanationId);if(!r)throw new Error(`Unknown imprint explanation: ${o.explanationId}`);r.evidence.some(i=>i.id===o.evidence.id)||(r.evidence.push(Ge(o.evidence)),r.updatedAt=o.evidence.createdAt)}else if(o.op==="revise_explanation"){let r=t.explanations.find(i=>i.id===o.explanationId);if(!r)throw new Error(`Unknown imprint explanation: ${o.explanationId}`);Object.assign(r,Ge(o.patch),{updatedAt:o.updatedAt})}else if(o.op==="add_relation"){let r=o.relation,i=t.explanations.find(w=>w.id===r.fromId),s=!!r.toStoreId,a=s?void 0:t.explanations.find(w=>w.id===r.toId),d=t.concrete.some(w=>w.id===r.fromId),c=!s&&t.concrete.some(w=>w.id===r.toId);if(si(r.type)){if(!(r.type==="expresses"?s?!!i:!!((i||d)&&a):!!(i&&(a||s))))throw new Error(`Imprint ${r.type} relation has invalid endpoints: ${r.id}`);if(t.relations.some(v=>v.type===r.type&&v.fromId===r.fromId&&v.toStoreId===r.toStoreId&&v.toId===r.toId))throw new Error(`Duplicate imprint relation: ${r.type}:${r.fromId}:${r.toStoreId??""}:${r.toId}`);if(r.type==="expresses"&&i&&a){if(zt(i,a)<0)throw new Error(`Imprint expresses relation must point from specific to abstract: ${r.id}`);let v=new Map;for(let b of t.relations)b.type!=="expresses"||b.toStoreId||v.set(b.fromId,[...v.get(b.fromId)??[],b.toId]);let g=[r.toId],k=new Set;for(;g.length;){let b=g.pop();if(b===r.fromId)throw new Error(`Imprint expresses relation would create a cycle: ${r.id}`);k.has(b)||(k.add(b),g.push(...v.get(b)??[]))}}}else if(!i||!a&&!c)throw new Error(`Imprint relation references an unknown explanation: ${r.id}`);t.relations.some(w=>w.id===r.id)||t.relations.push(Ge(r))}else o.op==="remove_relation"&&(t.relations=t.relations.filter(r=>r.id!==o.relationId));return t}function Wa(n,e){let t=[],o=Ge(n);for(let r of e){if(r.op==="add_explanation")t.unshift({op:"remove_explanation",explanationId:r.explanation.id});else if(r.op==="remove_explanation"){let i=o.explanations.find(s=>s.id===r.explanationId);i&&t.unshift({op:"add_explanation",explanation:Ge(i)})}else if(r.op==="add_concrete")t.unshift({op:"remove_concrete",concreteId:r.concrete.id});else if(r.op==="remove_concrete"){let i=o.concrete.find(s=>s.id===r.concreteId);i&&t.unshift({op:"add_concrete",concrete:Ge(i)})}else if(r.op==="replace_explanation"||r.op==="revise_explanation"||r.op==="add_evidence"){let i=r.op==="replace_explanation"?r.explanation.id:r.explanationId,s=o.explanations.find(a=>a.id===i);s&&t.unshift({op:"replace_explanation",explanation:Ge(s)})}else if(r.op==="add_relation")t.unshift({op:"remove_relation",relationId:r.relation.id});else if(r.op==="remove_relation"){let i=o.relations.find(s=>s.id===r.relationId);i&&t.unshift({op:"add_relation",relation:Ge(i)})}o=Qo(o,[r])}return t}function ci(n){return n.op==="add_explanation"||n.op==="replace_explanation"?[n.explanation.id]:n.op==="remove_explanation"||n.op==="add_evidence"||n.op==="revise_explanation"?[n.explanationId]:n.op==="add_relation"?[n.relation.fromId,...n.relation.toStoreId?[]:[n.relation.toId]]:[]}var to=class{constructor(e,t){this.projectRoot=e;this.projectId=t;this.writeChain=Promise.resolve();this.imprintDir=Gt(e,".popmelt","imprint"),this.manifestPath=Gt(this.imprintDir,"manifest.json"),this.graphPath=Gt(this.imprintDir,"graph.json"),this.journalDir=Gt(this.imprintDir,"journal")}async readHead(){return(await this.readManifest()).head}async readManifest(){try{let e=JSON.parse(await ai(this.manifestPath,"utf8"));if(e.version!==1||e.projectId!==this.projectId)throw new Error("Imprint manifest does not match this project.");return e}catch(e){if(e.code!=="ENOENT")throw e;let t=new Date().toISOString();return{version:1,projectId:this.projectId,createdAt:t,updatedAt:t,head:{sequence:0,hash:null}}}}async loadRaw(){let e=await this.readManifest(),o=(await qa(this.journalDir).catch(a=>{if(a.code==="ENOENT")return[];throw a})).filter(a=>/^\d{12}-.+\.json$/.test(a)).sort(),r=za(this.projectId,e.createdAt),i=[],s=null;for(let a=0;a<o.length;a++){let d=JSON.parse(await ai(Gt(this.journalDir,o[a]),"utf8")),{hash:c,...w}=d;if(d.sequence!==a+1||d.previousHash!==s||c!==di(w))throw new Error(`Imprint journal hash mismatch at sequence ${d.sequence}.`);r=Qo(r,d.mutations),r.updatedAt=d.committedAt,r.head={sequence:d.sequence,hash:d.hash},i.push(d),s=d.hash}return{graph:r,transactions:i,recoveredTransactions:Math.max(0,i.length-e.head.sequence)}}async load(){let e=await this.loadRaw();return{...e,graph:Mt(e.graph)}}async migrateTopology(){let e=await this.readManifest();if(e.topologyVersion===2)return{migrated:!1,relationMutations:0};let t=await this.loadRaw(),o=Mt(t.graph),r=new Map(o.relations.map(s=>[s.id,s])),i=[];for(let s of t.graph.relations){let a=r.get(s.id);a&&a.type===s.type&&a.fromId===s.fromId&&a.toStoreId===s.toStoreId&&a.toId===s.toId||(i.push({op:"remove_relation",relationId:s.id}),a&&i.push({op:"add_relation",relation:a}))}if(i.length){let s=new Date().toISOString();return await this.commit({transactionId:`topology-v2-${t.graph.head.sequence+1}`,committedAt:s,source:{kind:"migration",threadId:"imprint-topology",jobId:"imprint-topology-v2"},summary:"Simplify Imprint topology to expresses and excepts",mutations:i}),{migrated:!0,relationMutations:i.length}}return await jt(this.graphPath,o),await jt(this.manifestPath,{...e,topologyVersion:2}),{migrated:!0,relationMutations:0}}async commit(e){let t;return this.writeChain=this.writeChain.then(async()=>{let o=await this.load();if(o.transactions.some(v=>v.transactionId===e.transactionId))throw new Error(`Imprint transaction already exists: ${e.transactionId}`);let r=o.graph.head.sequence+1,i=Wa(o.graph,e.mutations),s={version:1,sequence:r,transactionId:e.transactionId,projectId:this.projectId,committedAt:e.committedAt,previousHash:o.graph.head.hash,source:e.source,summary:e.summary,mutations:Ge(e.mutations),inverseMutations:i,...e.undoesTransactionId?{undoesTransactionId:e.undoesTransactionId}:{}},a={...s,hash:di(s)},d=Qo(o.graph,a.mutations);d.updatedAt=e.committedAt,d.head={sequence:r,hash:a.hash};let c=`${String(r).padStart(12,"0")}-${e.transactionId}.json`;await jt(Gt(this.journalDir,c),a);let w={version:1,topologyVersion:2,projectId:this.projectId,createdAt:d.createdAt,updatedAt:e.committedAt,head:d.head};await jt(this.graphPath,d),await jt(this.manifestPath,w),t={graph:d,transaction:a}}),await this.writeChain,t}async undo(e,t,o,r){let i=await this.load(),s=i.transactions.find(c=>c.transactionId===e);if(!s)throw new Error("The imprint update is no longer available to undo.");if(i.transactions.some(c=>c.undoesTransactionId===e))throw new Error("This imprint update has already been undone.");let a=new Set(s.mutations.flatMap(ci));if(i.transactions.filter(c=>c.sequence>s.sequence).some(c=>c.mutations.some(w=>ci(w).some(v=>a.has(v)))))throw new Error("This imprint update has newer dependent changes and cannot be undone cleanly. Edit or challenge the current explanation instead.");return this.commit({transactionId:r,committedAt:o,source:t,summary:`Undo: ${s.summary}`,mutations:s.inverseMutations,undoesTransactionId:e})}async recover(){let e=await this.load();return await jt(this.graphPath,e.graph),await jt(this.manifestPath,{version:1,topologyVersion:2,projectId:this.projectId,createdAt:e.graph.createdAt,updatedAt:e.graph.updatedAt,head:e.graph.head}),{...e,recoveredTransactions:0}}};var Ka=new Set(["application_only","transient_state","concrete_without_model_path","duplicate","insufficient_evidence","task_specific_detail","no_shared_understanding_change"]),Va=new Set(["about","after","again","before","being","change","from","have","into","make","should","that","the","their","this","with","would","here","only","consistent","match","matched","matching","mirror","mirrored","mirroring","align","aligned","aligning"]),Ha=[/\bconsistent\s+with\b/i,/\bmatch(?:es|ed|ing)?\b/i,/\bmirror(?:s|ed|ing)?\b/i,/\bthe\s+same\s+as\b/i,/\bborrow(?:s|ed|ing)?\s+from\b/i,/\balign(?:s|ed|ing)?\s+with\b/i,/\b(?:more|look|looks|feel|feels|behave|behaves)\s+like\b/i],Qa=/\b(all|any|every|everywhere|across|throughout|globally|project[- ]wide)\b/i;function Yo(n){return!!(n&&Qa.test(n))}function Xa(n){return n.scope.kind==="project"||Yo(`${n.statement} ${n.rationale}`)}function Ya(n){let e=n?.scope.values.filter(t=>t!=="*").join(", ");return e?`Does applying this understanding here match your intent for ${e}?`:"Does applying this understanding here match your intent?"}function Za(n){return n.replace(/([a-z0-9])([A-Z])/g,"$1 $2").toLowerCase().replace(/[^a-z0-9]+/g," ").trim().replace(/\s+/g," ")}function ed(n){return n.length>4&&n.endsWith("ies")?[n,`${n.slice(0,-3)}y`]:n.length>3&&n.endsWith("s")&&!n.endsWith("ss")?[n,n.slice(0,-1)]:[n]}function mt(n){return new Set(Za(n).split(" ").filter(e=>e.length>2&&!Va.has(e)).flatMap(ed))}function li(n){return n.replace(/^\s*(?:please\s+)?(?:let(?:'|’)s\s+)?(?:make|apply|use|keep|change|update)\s+/i,"").replace(/[\s,.:;!?]+$/g,"").trim().slice(0,240)}function td(n){if(n)for(let e of Ha){let t=e.exec(n);if(!t||t.index<=0)continue;let o=li(n.slice(0,t.index)),r=li(n.slice(t.index+t[0].length));if(o&&r)return{target:o,reference:r}}}function xt(n,e){if(!n.size||!e.size)return 0;let t=0;for(let o of n)e.has(o)&&(t+=1);return t/Math.min(n.size,e.size)}function Xo(n){return!n||!Array.isArray(n.values)||n.values.length===0?{kind:"project",values:["*"]}:{kind:n.kind,values:[...new Set(n.values.map(e=>e.trim()).filter(Boolean))]}}function St(n,e,t){return{id:ft(),kind:e==="excepts"?"exception":e==="challenges"?"correction":"observation",stance:e,summary:t,threadId:n.threadId,jobId:n.jobId,createdAt:n.occurredAt??new Date().toISOString()}}function Se(n,e=800){return typeof n=="string"?n.trim().slice(0,e):""}function pi(n){if(!n||typeof n!="object"||Array.isArray(n))return;let e=n;if(!["project","route","component","selector","file"].includes(String(e.kind))||!Array.isArray(e.values))return;let t=e.values.filter(o=>typeof o=="string"&&!!o.trim());return t.length?{kind:e.kind,values:t}:void 0}function ui(n,e,t){if(n&&typeof n=="object"&&!Array.isArray(n)){let i=n,s=Se(i.explanationId,100),a=Se(i.clientId,100),d=Se(i.storeId,100);if(s&&a)throw new Error("relation endpoint must use either explanationId or clientId, not both");let c=s||a;return c?{explanationId:c,...d?{storeId:d}:{}}:void 0}let o=Se(e,100),r=Se(t,100);return o?{explanationId:o,...r?{storeId:r}:{}}:void 0}function mi(n,e){if(!n||!e.trim())return{found:!1};let t=n;for(let o of e.split(".").filter(Boolean))if(Array.isArray(t)){let r=Number(o);if(!Number.isInteger(r)||r<0||r>=t.length)return{found:!1};t=t[r]}else if(t&&typeof t=="object"&&Object.prototype.hasOwnProperty.call(t,o))t=t[o];else return{found:!1};return{found:!0,value:t}}function In(n,e={}){let t=[...n.matchAll(/<imprint>([\s\S]*?)<\/imprint>/g)],o=n.replace(/<imprint>[\s\S]*?<\/imprint>/g,"").replace(/<imprint>[\s\S]*$/g,"").trim(),r=t.at(-1)?.[1],i,s=[];if(r)i=r;else{let d=[...n.matchAll(/<resolution>\s*([\s\S]*?)\s*<\/resolution>/g)].at(-1)?.[1];if(!d)return{visibleText:o,error:"imprint assessment is missing"};try{let c=JSON.parse(d);if(!c||typeof c!="object"||Array.isArray(c)||!Object.prototype.hasOwnProperty.call(c,"imprint"))return{visibleText:o,error:"imprint assessment is missing"};let w=c;i=w.imprint,s=Array.isArray(w.annotations)?w.annotations:[]}catch{return{visibleText:o,error:"resolution envelope is invalid"}}}try{let a=typeof i=="string"?JSON.parse(i):i;if(!a||typeof a!="object"||Array.isArray(a))throw new Error("imprint assessment is invalid");if(a.decision==="none"){let h=String(a.reason);if(!Ka.has(h))throw new Error("imprint no-op reason is invalid");if(h==="task_specific_detail"&&s.some(M=>{if(!M||typeof M!="object"||Array.isArray(M))return!1;let $=M;return($.finalScope??$.inferredScope)?.breadth==="pattern"}))throw new Error("pattern-scoped resolution cannot be classified as task_specific_detail");let p=Array.isArray(a.usedExplanationIds)?a.usedExplanationIds.filter(M=>typeof M=="string"&&!!M.trim()):[],y=Array.isArray(a.appliedExplanationIds)?a.appliedExplanationIds.filter(M=>typeof M=="string"&&!!M.trim()):[],I=new Set(e.context?.explanations.map(M=>M.id)??[]);if([...new Set([...p,...y])].some(M=>!I.has(M)))throw new Error("imprint use must cite only surfaced explanations");let x=[...new Set([...p,...e.applicationOccurred?[]:y])],m=e.applicationOccurred?y:[],f;if(m.length){f=m;let M=new Set(e.context?.pendingGroundingExplanationIds??[]),$=m.filter(E=>{let F=e.context?.explanations.find(Y=>Y.id===E);return I.has(E)&&F&&(F.state==="emerging"||F.state==="testing")&&!M.has(E)});if($.length){let E=a.grounding,F=typeof E?.explanationId=="string"&&$.includes(E.explanationId)?E.explanationId:$[0],Y=Se(E?.question,300);return{draft:{decision:"apply",summary:{title:Se(a.summary?.title,120)||"Applied shared understanding",detail:Se(a.summary?.detail,500)||Se(e.requestText||o,500)||"Applied an ungrounded explanation in a new context."},appliedExplanationIds:m,grounding:{explanationId:F,question:Y||"Does applying this understanding here match your intent?"}},visibleText:o}}}if(h==="duplicate"){let M=Se(a.duplicateOfExplanationId,100),$=a.scopeComparison;if(!M)throw new Error("duplicate assessment requires duplicateOfExplanationId");if($!=="equivalent"&&$!=="existing_broader")throw new Error("duplicate assessment requires an equivalent or existing_broader scopeComparison");let E=e.context?.explanations.find(F=>F.id===M);if(!E)throw new Error("duplicate assessment must cite a surfaced explanation");if(Yo(e.requestText)&&!Xa(E))throw new Error("duplicate explanation does not cover the requested broader scope");return{draft:{decision:"none",reason:h,duplicateOfExplanationId:M,scopeComparison:$,...x.length?{usedExplanationIds:x}:{},...f?{appliedExplanationIds:f}:{}},visibleText:o}}return{draft:{decision:"none",reason:h,...x.length?{usedExplanationIds:x}:{},...f?{appliedExplanationIds:f}:{}},visibleText:o}}if(a.decision==="recognize"){let h=Se(a.explanationId,100);if(!h)throw new Error("recognition requires explanationId");let p=e.context?.explanations.find(u=>u.id===h);if(!p)throw new Error("recognition must cite a surfaced explanation");if(p.state!=="grounded")throw new Error("recognition must cite a grounded explanation");let y=a.summary,I={title:Se(y?.title,120),detail:Se(y?.detail,500)};if(!I.title||!I.detail)throw new Error("recognition summary title and detail are required");return{draft:{decision:"recognize",explanationId:h,summary:I},visibleText:o}}if(a.decision==="apply"){let h=a.summary,p={title:Se(h?.title,120),detail:Se(h?.detail,500)};if(!p.title||!p.detail)throw new Error("application summary title and detail are required");let y=Array.isArray(a.appliedExplanationIds)?a.appliedExplanationIds.filter(M=>typeof M=="string"&&!!M.trim()):[];if(!y.length)throw new Error("application requires appliedExplanationIds");let I=new Map(e.context?.explanations.map(M=>[M.id,M])??[]);if(y.some(M=>!I.has(M)))throw new Error("application must cite only surfaced explanations");if(!e.applicationOccurred)return{draft:{decision:"none",reason:"application_only",usedExplanationIds:[...new Set(y)]},visibleText:o};let u=new Set(e.context?.pendingGroundingExplanationIds??[]),x=y.filter(M=>{let $=I.get(M);return $&&($.state==="emerging"||$.state==="testing")&&!u.has(M)});if(!x.length){if(y.every($=>I.get($)?.state==="grounded"||u.has($)))return{draft:{decision:"none",reason:"application_only",appliedExplanationIds:y},visibleText:o};throw new Error("application must cite a surfaced ungrounded explanation")}let m=a.grounding,f=typeof m?.explanationId=="string"&&x.includes(m.explanationId)?m.explanationId:x[0];return{draft:{decision:"apply",summary:p,appliedExplanationIds:y,grounding:{explanationId:f,question:Se(m?.question,300)||"Does applying this understanding here match your intent?"}},visibleText:o}}if(a.decision!==void 0&&a.decision!=="mutate")throw new Error("imprint decision is invalid");let d=a.summary,c=d?.kind;if(!["added","strengthened","refined","challenged","exception","consolidated"].includes(String(c)))throw new Error("summary.kind is invalid");let w={kind:c,title:Se(d?.title,120),detail:Se(d?.detail,500)};if(!w.title||!w.detail)throw new Error("summary title and detail are required");let v=Array.isArray(a.appliedExplanationIds)?a.appliedExplanationIds.filter(h=>typeof h=="string"&&!!h.trim()):[],g=[];for(let h of Array.isArray(a.mutations)?a.mutations:[]){if(!h||typeof h!="object")continue;let p=h;if(p.op==="add"&&["intention","principle","pattern"].includes(String(p.kind))){let y=Se(p.statement),I=Se(p.rationale);if(!y||!I)continue;g.push({op:"add",clientId:Se(p.clientId,100)||void 0,kind:p.kind,statement:y,rationale:I,scope:pi(p.scope),rootReason:Se(p.rootReason,300)||void 0})}else if(p.op==="revise"&&typeof p.explanationId=="string"){let y={op:"revise",explanationId:p.explanationId,statement:Se(p.statement)||void 0,rationale:Se(p.rationale)||void 0,scope:pi(p.scope),reopenGrounding:p.reopenGrounding===!0};(y.statement||y.rationale||y.scope||y.reopenGrounding)&&g.push(y)}else if(p.op==="evidence"&&typeof p.explanationId=="string"&&["supports","challenges","excepts"].includes(String(p.stance))){let y=Se(p.summary);y&&g.push({op:"evidence",explanationId:p.explanationId,stance:p.stance,summary:y})}else if(p.op==="relation"){if(!["expresses","excepts"].includes(String(p.type)))throw new Error("relation type is invalid");let y=Se(p.rationale),I=ui(p.from,p.fromId,p.fromStoreId),u=ui(p.to,p.toId,p.toStoreId);if(!y)throw new Error("relation rationale is required");if(!I||!u)throw new Error("relation endpoints require an explanationId or clientId");g.push({op:"relation",type:p.type,from:I,to:u,rationale:y})}else if(p.op==="supersede"&&typeof p.explanationId=="string"&&typeof p.replacementId=="string"){let y=Se(p.reason);y&&g.push({op:"supersede",explanationId:p.explanationId,replacementId:p.replacementId,reason:y})}else if(p.op==="bind_concrete"&&typeof p.explanationId=="string"){let y=Se(p.modelPath,300);y&&g.push({op:"bind_concrete",explanationId:p.explanationId,modelPath:y,rationale:Se(p.rationale)})}else p.op==="retire"&&typeof p.explanationId=="string"&&g.push({op:"retire",explanationId:p.explanationId,reason:Se(p.reason)})}let k=a.grounding,b=k&&typeof k.explanationId=="string"&&Se(k.question)?{explanationId:k.explanationId,question:Se(k.question,300)}:void 0;return{draft:{decision:"mutate",summary:w,appliedExplanationIds:v,mutations:g,grounding:b},visibleText:o}}catch(a){return{visibleText:o,error:a instanceof Error?a.message:String(a)}}}function wn(n,e={}){let t=n?.explanations??[],o=new Set(n?.pendingGroundingExplanationIds??[]),r=new Set(n?.correctionExplanationIds??[]),i=Yo(e.requestText),s=n?.retrievalIntent,a=n?.historicalCaveats??[],d=t.find(g=>g.storeId&&!g.readOnly)?.storeId,c=(g,k)=>{let b=g[k];return b?`[${b.storeId}] ${b.explanationId}`:k==="from"?g.fromId:g.toId},w=!!n?.relations?.some(g=>g.from&&g.to&&g.from.storeId!==g.to.storeId),v=t.some(g=>g.readOnly);return["","## Continuous imprinting","","An imprint is provisional shared understanding learned through human-AI collaboration. It is advisory, not authoritative product truth. Accepted product decisions above always win when they conflict.",...t.length?["","Relevant explanations from the project imprint:",...t.map((g,k)=>[`${k+1}. [${g.state}; ${g.kind}] ${g.statement}`,` - Why: ${g.rationale}`,` - Scope: ${g.scope.kind} ${g.scope.values.join(", ")}`,` - Explanation: ${g.id}`,...g.storeId?[` - Store: ${g.storeId}${g.readOnly?" (read-only)":" (writable primary)"}`]:[],...o.has(g.id)?[" - Grounding: already pending; do not ask again"]:[],...r.has(g.id)?[" - Correction: the developer selected No for this explanation and the current reply supplies the missing correction context"]:[],...g.concrete.map(b=>` - Concrete terminal: ${b.modelPath} = ${JSON.stringify(b.value)}`)].join(`
|
|
8
8
|
`)),...n?.truncated?["Additional imprint context exists outside this bounded packet."]:[]]:[],...n?.relations?.length?["","Known explanation topology:",...n.relations.map(g=>`- ${g.storeId?`[owned by ${g.storeId}${g.readOnly?"; read-only":""}] `:""}${c(g,"from")} --${g.type}--> ${c(g,"to")}: ${g.rationale}`)]:[],...v||w?["","Store-qualified topology remains locally owned: the relation must originate at an explanation in the writable primary store, while its target may be in a mounted read-only store. Never proxy a relation whose source belongs to a mounted store.",`For a cross-store relation, use qualified endpoints: {"op":"relation","type":"expresses|excepts","from":{"storeId":"${d??"<writable-primary-store>"}","clientId":"<new-specific-client-id>"},"to":{"storeId":"<mounted-target-store>","explanationId":"<existing-abstract-target-id>"},"rationale":"<why>"}. Use clientId for a new explanation created in this transaction and explanationId for an existing durable explanation.`]:[],...s?["","Explicit pattern transfer detected:",`- Target: ${s.target}`,`- Reference: ${s.reference}`,"Before editing, state which dimensions you intend to transfer from the reference and which target-specific behavior you will preserve. Compare the relevant surface, spacing, typography, radius, and interaction behavior; transfer only the dimensions supported by the request and evidence. If the intended transfer remains materially ambiguous after inspecting both anchors, ask one focused question."]:[],...a.length?["","Directly relevant challenged history (historical caveats, not current Imprint truth):",...a.map((g,k)=>[`${k+1}. [challenged; ${g.kind}] ${g.statement}`,` - Why it was previously proposed: ${g.rationale}`,` - Scope: ${g.scope.kind} ${g.scope.values.join(", ")}`,` - Historical explanation: ${g.id}`,...g.storeId?[` - Store: ${g.storeId}${g.readOnly?" (read-only)":" (writable primary)"}`]:[]].join(`
|
|
9
9
|
`)),"Use these caveats only to avoid repeating a rejected or over-broad transfer. Do not cite them as usedExplanationIds or treat them as active guidance."]:[],...t.some(g=>g.readOnly)?["","Mounted-store explanations are read-only context. They may inform the work and be cited as used understanding, but do not mutate, ground, revise, or add evidence to them without an explicit writable orientation for that store."]:[],...n?.unavailableStoreIds?.length?["",`Some mounted Imprint knowledge was unavailable during this bounded read: ${n.unavailableStoreIds.join(", ")}. Do not infer its contents.`]:[],...n?.unavailableTopologyStoreIds?.length?["",`Some mounted Imprint topology was unavailable during this bounded read: ${n.unavailableTopologyStoreIds.join(", ")}. Available explanations remain usable, but do not infer missing relationships.`]:[],"","Work normally and preserve developer agency. A testing explanation may guide action as a hypothesis without asking permission first. An emerging explanation is observation-only: use it to recognize recurrence, not to choose behavior. If the developer independently repeats an emerging observation in a meaningfully different occurrence, that new application may promote it into testing and request grounding. Silence is not confirmation.","Assess learning proactively as part of completing every thread. Do not wait for the developer to ask whether there was a learning. Before choosing none, look one abstraction level above the exact edit and ask: would the reasoning behind this outcome change how a future agent handles a meaningfully different occurrence?","The developer's original annotation, ordinary instruction, scope language, and later corrections are already human-supplied evidence. They can support learning when they establish a reusable dependency, boundary, or rationale; a separate post-completion learning prompt is not required. Mere acceptance of completed work is still insufficient.","The default outcome is no imprint mutation. Completing work or making a successful design choice does not by itself constitute learning.","Application evidence is distinct from changing shared understanding. When an emerging or testing explanation materially influenced a completed action, report decision apply even when no explanation wording changed.","Applying grounded understanding does not mutate the graph. Usually report none. Use decision recognize only when visibly acknowledging the grounded explanation closes a correction loop or otherwise makes consequential reuse legible to the developer. Recognition must cite one exact surfaced grounded explanation ID; never infer or invent an ID.","Application and recognition are whole-statement operations. Cite an existing explanation only when the completed behavior is compatible with every material clause, its scope, and its exceptions. Sharing one useful clause is not a match. If the durable behavior is narrower, add one atomic explanation linked to the broader explanation when the admission tests pass; otherwise report none.","Mutate the imprint only when this collaboration changed reusable shared understanding: a future agent facing a meaningfully different task would behave differently because of what was learned here.","Relationship-first admission: before calling any completed change task-specific, compare its requested behavior and effective scope with every surfaced explanation that could plausibly govern the same subject or treatment. The developer does not need to name an Imprint concept or relation. Infer the relationship from the ordinary instruction, the annotated context, the implementation, and the surfaced explanations.","A narrower completed behavior that departs from an applicable broader explanation establishes an applicability boundary even when the developer did not say exception. Add one narrow testing explanation and link it to the broader rule with excepts. If the behaviors concern distinct concepts rather than the same rule, assess the reusable behavior as a sibling explanation instead. Do not manufacture an exception merely because implementations differ.","Scope and Imprint assessment must agree. If any resolved annotation has an effective inferred or final breadth of pattern, do not classify the Imprint decision as task_specific_detail. Either capture the reusable conceptual change, report application of existing understanding, cite a valid duplicate, or use another accurate no-op reason. The bridge rejects pattern-scoped task_specific_detail assessments.","","Before proposing a mutation, apply every admission test:","1. Reusable: it guides a future decision beyond reproducing this exact edit.","2. Novel: it is not already expressed by an explanation or its evidence.","3. Durable: it is an intention, rationale, relationship, or recurring pattern, not temporary implementation state.","4. Atomic: it expresses one claim at one abstraction level.","5. Scoped: its applicability can be named without silently generalizing.","6. Supported: the human supplied evidence, correction, or confirmation anywhere in the thread, including the original instruction, not merely acceptance of completed work or the model's own implementation choice.","If any test fails, return a decision of none.","","Usually not learning: exact copy substitutions, one-pixel adjustments, element reordering, implementation mechanics, keeping something hidden temporarily, applying an already-known pattern, or a stylistic choice made without human feedback. Distinguish mechanics from the reusable reasoning they may reveal: a CSS property alone is not learning, but a human-directed causal rule such as keeping optical adjustment out of layout geometry may be. This heuristic never overrides relationship-first admission: a small local change that narrows or contradicts an applicable surfaced explanation may establish an exception or correction. A task-specific edit otherwise becomes learning only if the human establishes a reusable dependency, applicability boundary, or rationale beyond that edit.","When a candidate overlaps an existing explanation, consolidate before adding. Same meaning plus a new observation means evidence only. A concrete application means bind_concrete, not a broader explanation. A scope correction means revise scope. A direct disagreement means add the replacement and supersede the contradicted explanation. A different abstraction level means a separate related explanation.","Duplicate means the same reusable claim is already represented at an equivalent or broader conceptual scope. Shared code, a centralized component, or implementation reuse does not establish conceptual duplication. A narrower component-, route-, selector-, or file-scoped explanation is not a duplicate of newly broadened applicability.","Words such as all, every, across, throughout, globally, and project-wide are explicit applicability signals. When the human broadens a grounded explanation, preserve the grounded narrower explanation and add a broader testing explanation. Link the narrower explanation to the broader one with expresses. Do not substitute another no-op reason for that scope change.",...i?["CURRENT SCOPE SIGNAL: The developer used explicit broadening language in this instruction. Do not classify it as duplicate unless the cited existing explanation already expresses universal or project-wide applicability."]:[],"Keep correction identity explicit. If feedback refines an emerging or testing explanation without rejecting its underlying claim, revise it and set reopenGrounding true; do not also add challenging evidence to that same explanation. If feedback rejects the claim as written, preserve that rejected wording, add a distinct replacement, and supersede the old explanation. Never revise an explanation and mark that same identity challenged in one update.","When an explanation is marked Correction in the supplied context, the developer selected No but the graph has not yet been changed. Interpret the current reply before mutating anything. Decide whether it corrects only the implementation result, the explanation scope, or the reasoning. Do not challenge the whole explanation for a narrow correction, and do not recognize a different explanation merely because one clause overlaps.","Topology assessment is mandatory for every add mutation. Imprint topology has exactly two relation types: expresses and excepts. expresses always points from the more specific, concrete, or lower-level explanation to the more abstract explanation it expresses. excepts points from the narrower exception to the rule whose applicability it limits. Support and challenge belong in evidence; correction belongs in supersede; neither is a topology relation. If an addition is genuinely a new root with no meaningful relative, include rootReason explaining why. Unmapped additions without rootReason are rejected.","Never append implementation facts to an intention merely because they occurred in the same component. Grounded explanations are append-only: add evidence for a compatible observation, but never revise or demote the grounded explanation. A material change in meaning, causal rationale, abstraction level, or applicability must be a new testing explanation linked with expresses or excepts. Multiple specific explanations may express the same parent, and one explanation may express multiple parents; never create an expresses cycle.","Grounded explanations may support new higher-level theory. Emerging explanations must not be the sole basis for another durable explanation. Concrete project facts should remain concrete; do not turn a one-off value into a universal principle.","Request grounding when an ungrounded explanation materially influenced the action, this application produced genuinely new evidence, and that explanation does not already have grounding pending. Ask at most one grounding question per thread. The bridge supplies a fallback question when the application is otherwise valid.","","At the end, emit exactly one machine-readable <resolution> envelope after the normal response. Its `annotations` array reports annotation outcomes, and its required `imprint` field completes the learning assessment. The envelope is hidden from the developer. Do not emit a separate <imprint> block.",'As soon as one or more surfaced explanations become actively useful during the work, emit `<imprint-use>{"explanationIds":["<every actively useful surfaced explanation id>"]}</imprint-use>` immediately. This live receipt is hidden from conversational prose. Emit it at the point of use rather than waiting for the final response. Emit a later marker only when additional explanations become actively useful. Never emit it for explanations that were merely retrieved, read, searched, or considered.',"Treat Imprint as actively useful when a surfaced explanation materially informs an answer, recommendation, implementation choice, or other decision. Whenever it is actively useful, always cite every materially used surfaced explanation in `usedExplanationIds`. This produces a usage receipt only; it does not strengthen or mutate shared understanding. Do not cite explanations that were merely retrieved, read, or considered but did not influence the result.","`appliedExplanationIds` is narrower: include an explanation there only when it governed a completed implementation or other observable action that genuinely tests the explanation. Never use `appliedExplanationIds` for an answer-only, informational, research, or read-only request. An explanation may be used without being applied; every applied explanation is also understood to be used.","For no learning, include:",'"imprint":{"decision":"none","reason":"application_only|transient_state|concrete_without_model_path|duplicate|insufficient_evidence|task_specific_detail|no_shared_understanding_change","usedExplanationIds":["<every surfaced explanation that was actively useful, or omit when none>"]}',"A no-op reason cannot suppress genuine application evidence. When a completed action was governed by an ungrounded explanation, report decision apply directly so the summary and grounding question are intentional. Answering a question with that explanation is usage, not application.","When and only when reason is duplicate, also include `duplicateOfExplanationId` naming one surfaced explanation and `scopeComparison` set to `equivalent` or `existing_broader`. The bridge rejects uncited duplicates and duplicates whose cited explanation cannot cover explicit broadening language.","For application evidence without a conceptual change, include:",'"imprint":{"decision":"apply","summary":{"title":"<short expandable-summary title>","detail":"<how the explanation influenced this completed action>"},"appliedExplanationIds":["<surfaced emerging or testing explanation id>"],"grounding":{"explanationId":"<one applied id>","question":"<brief post-application confirmation question>"}}',"For a consequential, non-mutating reuse of grounded understanding that should be visible, include:",'"imprint":{"decision":"recognize","explanationId":"<one exact surfaced grounded explanation id>","summary":{"title":"<short name for the recognized understanding>","detail":"<how it guided this outcome>"}}',"Or, only after every admission test passes for a conceptual change:",'"imprint":{"decision":"mutate","summary":{"kind":"added|strengthened|refined|challenged|exception|consolidated","title":"<short expandable-summary title>","detail":"<what changed in shared understanding>"},"appliedExplanationIds":["<only an ungrounded explanation materially tested here>"],"mutations":[{"op":"add","clientId":"new-1","kind":"intention|principle|pattern","statement":"<one durable atomic explanation>","rationale":"<why the evidence supports it>","scope":{"kind":"project|route|component|selector|file","values":["<scope>"]},"rootReason":"<only when this is genuinely unrelated to every surfaced or new explanation>"},{"op":"revise","explanationId":"<only an emerging or testing id>","statement":"<optional>","rationale":"<optional>","scope":{"kind":"component","values":["CardTile"]},"reopenGrounding":false},{"op":"evidence","explanationId":"<id>","stance":"supports|challenges|excepts","summary":"<thread evidence>"},{"op":"relation","type":"expresses|excepts","from":{"clientId":"<new-specific-client-id>"},"to":{"explanationId":"<existing-abstract-id>"},"rationale":"<why; use clientId for new transaction-local explanations and explanationId for existing durable explanations>"},{"op":"supersede","explanationId":"<contradicted old id>","replacementId":"<new clientId or existing id>","reason":"<direct conflict>"},{"op":"bind_concrete","explanationId":"<id or clientId>","modelPath":"<exact existing model path>","rationale":"<how this fact expresses the explanation>"},{"op":"retire","explanationId":"<id>","reason":"<why>"}],"grounding":{"explanationId":"<one applied ungrounded id or clientId>","question":"<brief post-application confirmation question>"}}','The final shape is `<resolution>{"annotations":[...],"imprint":{...}}</resolution>`. The `imprint` field is required even when the decision is none; omission leaves the completed run explicitly unassessed.',"Include only mutation objects actually needed. A new explanation established through completed implementation must include its clientId in appliedExplanationIds; the bridge also derives this application from a completed change as a safeguard. Applying a grounded explanation never requires a mutation, but actively using it still requires its id in appliedExplanationIds. Never claim that a new explanation is grounded."].join(`
|
|
@@ -163,7 +163,7 @@ Search raw \`.popmelt\` records only when the supplied Imprint context is insuff
|
|
|
163
163
|
|
|
164
164
|
Use Popmelt history to explain why a decision was made or how shared understanding developed. Use git history only when the question depends on chronology or source provenance. Clearly distinguish recorded human or collaboration evidence from your own inference.
|
|
165
165
|
|
|
166
|
-
Apply an evidence-sufficiency stopping rule: stop researching as soon as the relevant Imprint and current source adequately explain the observation. If one corroborating collaboration record is needed, stop after finding it unless the evidence conflicts or the developer requested a fuller history. Do not reconstruct an exhaustive timeline for a present-tense explanation, and do not search the full history for routine implementation work.`;function _s(n){n.push(""),n.push("## User-facing communication"),n.push("Keep Popmelt implementation details private. In progress updates, questions, and final prose, never mention internal tracking attributes or their values, raw selectors, element identifiers, identity machinery, lifecycle machinery, completion metadata, resolution envelopes, or similar Popmelt protocol terminology."),n.push("Do not narrate searches for internal markers or explain how Popmelt locates, binds, continues, or resolves work. Refer instead to the visible element or component, the requested change, and the user-visible result."),n.push("Use these internal mechanisms silently. Required tagged metadata blocks are still mandatory, but do not introduce, describe, or refer to them in user-visible prose. Discuss Popmelt internals only when the developer explicitly asks about them.")}function Bl(n){return n.replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").toLowerCase()}function Jl(n){if(!n.subjectLabel){let t=[n.selector];return n.reactComponent&&t.push(`(${n.reactComponent})`),t.join(" ")}let e=Object.entries(n.dataAttributes??{}).filter(([t])=>t!=="adapter"&&t!=="key").map(([t,o])=>`${Bl(t)}: ${o}`);return[`${n.subjectLabel} (${n.tagName} rendered subject)`,n.context
|
|
166
|
+
Apply an evidence-sufficiency stopping rule: stop researching as soon as the relevant Imprint and current source adequately explain the observation. If one corroborating collaboration record is needed, stop after finding it unless the evidence conflicts or the developer requested a fuller history. Do not reconstruct an exhaustive timeline for a present-tense explanation, and do not search the full history for routine implementation work.`;function _s(n){n.push(""),n.push("## User-facing communication"),n.push("Keep Popmelt implementation details private. In progress updates, questions, and final prose, never mention internal tracking attributes or their values, raw selectors, element identifiers, identity machinery, lifecycle machinery, completion metadata, resolution envelopes, or similar Popmelt protocol terminology."),n.push("Do not narrate searches for internal markers or explain how Popmelt locates, binds, continues, or resolves work. Refer instead to the visible element or component, the requested change, and the user-visible result."),n.push("Use these internal mechanisms silently. Required tagged metadata blocks are still mandatory, but do not introduce, describe, or refer to them in user-visible prose. Discuss Popmelt internals only when the developer explicitly asks about them.")}function Bl(n){return n.replace(/([a-z0-9])([A-Z])/g,"$1 $2").replace(/[_-]+/g," ").toLowerCase()}function Jl(n){if(!n.subjectLabel){let t=[n.selector];return n.reactComponent&&t.push(`(${n.reactComponent})`),t.join(" ")}let e=Object.entries(n.dataAttributes??{}).filter(([t])=>t!=="adapter"&&t!=="key").map(([t,o])=>`${Bl(t)}: ${o}`);return[`${n.subjectLabel} (${n.tagName} rendered subject)`,n.context?`${n.imageObject||n.videoObject?"context":"scene path"}: ${n.context}`:"",...e].filter(Boolean).join(" | ")}function Bn(n,e,t){let o=[];if(n.annotations.length>0){o.push("## Annotations");for(let r of n.annotations){let i=r.elements.map(Jl).join(", "),s=r.instruction||"No text";o.push(`- id=${r.id} [${r.type}] ${s} \u2192 Elements: ${i||"none"}`);for(let c of r.elements){let w=c.imageObject;if(w){let k=h=>Number(h.toFixed(3)),b=h=>`x=${k(h.x)}, y=${k(h.y)}, width=${k(h.width)}, height=${k(h.height)}`;o.push(` Image object: ${JSON.stringify(c.subjectLabel||c.textContent||"object")}`),o.push(` Source image: ${w.src}${w.alt?` | alt: ${JSON.stringify(w.alt)}`:""}`),o.push(` Intrinsic image size: ${w.width} \xD7 ${w.height} px.`),o.push(` Bounding box in original-image pixels (top-left origin; x right, y down): ${b(w.box)}.`),o.push(` Normalized bounding box (0\u20131, same origin/axes): ${b(w.normalizedBox)}. These are image-space coordinates, not page/CSS coordinates.`)}let v=c.videoObject;if(v){let k=h=>Number(h.toFixed(3)),b=h=>`x=${k(h.x)}, y=${k(h.y)}, width=${k(h.width)}, height=${k(h.height)}`;o.push(` Video object: ${JSON.stringify(c.subjectLabel||c.textContent||"object")}`),o.push(` Source video: ${v.src}`),o.push(` Intrinsic video size: ${v.width} \xD7 ${v.height} px | duration: ${k(v.duration)}s | annotated frame: ${k(v.currentTime)}s.`),o.push(` Bounding box in source-frame pixels (top-left origin; x right, y down): ${b(v.box)}.`),o.push(` Normalized bounding box (0\u20131, same origin/axes): ${b(v.normalizedBox)}. These are video-frame coordinates, not page/CSS coordinates.`),o.push(` Saved track span: ${k(v.trackStart)}s\u2013${k(v.trackEnd)}s.`)}let g=c.sourceElement;if(g){o.push(` Source element: <${g.tagName}> ${g.selector}${g.context?` | context: ${g.context}`:""}`);let k=g.reactComponentPath?.join(" \u2192 ")||g.reactComponent;o.push(` React component: ${k||"unavailable in this build"}.`);let b=g.reactSource;o.push(b?` JSX source: ${b.fileName}:${b.lineNumber}${b.columnNumber?`:${b.columnNumber}`:""}`:" JSX file/line unavailable in this build; locate the source using the component, image URL, and element context above.")}}r.subjectFeedback?.warning&&o.push(` Object context warning: ${r.subjectFeedback.warning}`);let a=e?.[r.id];if(a&&a.length>0)for(let[c,w]of a.entries())o.push(c===r.subjectFeedback?.cropImageIndex?` Object crop (clean pixels of the bounding box above, longest side capped at 1024px): use the Read tool to view ${w}`:` Attached image: use the Read tool to view ${w}`);let d=t?.[r.id];if(d?.length)for(let c of d)o.push(` Attached context (${c.name}, ${c.mimeType}): use the Read tool to inspect ${c.path}`)}}if(n.styleModifications.length>0){o.push(""),o.push("## Style Changes (make permanent in source)"),o.push("The developer previewed these CSS changes via inline style overrides. Find the corresponding styles in the source files and update them so the changes persist:");for(let r of n.styleModifications){let i=Ho(r.element,r.selector);for(let s of r.changes){let a=s.token?` [${s.token}]`:"";o.push(`- ${r.selector} (${i}): ${s.property} ${s.original} \u2192 ${s.modified}${a}`)}}}if(n.textModifications?.length){o.push(""),o.push("## Text Changes (make permanent in source)"),o.push("The developer directly edited rendered DOM text in the browser. Find the source copy and update it so the text change persists:");for(let r of n.textModifications){let i=r.element?.reactComponent?`(${r.element.reactComponent})`:"",s=JSON.stringify(r.original),a=JSON.stringify(r.modified),d=r.textNodePath.length>0?` textNodePath=${r.textNodePath.join(".")}`:"";o.push(`- ${r.selector} ${i}${d}: ${s} \u2192 ${a}`)}}if(n.domModifications?.length){o.push(""),o.push("## DOM Structure Changes (make permanent in source)"),o.push("The developer rearranged or copied rendered elements in the browser as a visual reference. Update the source structure so this layout/content relationship persists:");for(let r of n.domModifications){let i=r.element?.reactComponent?`(${r.element.reactComponent})`:"",s=r.sourceElement?.reactComponent?` from ${r.sourceSelector} (${r.sourceElement.reactComponent})`:r.sourceSelector?` from ${r.sourceSelector}`:"",a=r.referenceSelector?` ${r.placement} ${r.referenceSelector}`:` at index ${r.targetIndex}`;o.push(`- ${r.operation}: ${r.selector} ${i}${s} \u2192 ${r.targetParentSelector}${a}`)}}if(n.spacingTokenChanges?.length){o.push(""),o.push("## Spacing Token Changes"),o.push("The developer adjusted these spacing tokens. Apply each change to the source code:");for(let r of n.spacingTokenChanges){o.push(`
|
|
167
167
|
### ${r.tokenName}: ${r.originalPx}px \u2192 ${r.newPx}px`);for(let i of r.affectedElements){let s=i.reactComponent?` (${i.reactComponent})`:"";i.matchedClass&&i.suggestedClass?o.push(`- ${i.selector}${s}: \`${i.matchedClass}\` \u2192 \`${i.suggestedClass}\``):o.push(`- ${i.selector}${s}: ${i.property} ${r.originalPx}px \u2192 ${r.newPx}px`),o.push(` class="${i.className}"`)}}}if(n.inspectedElement){let r=n.inspectedElement;o.push(""),o.push("## Inspected Element"),o.push("The developer has this element selected in the inspector:");let i=[r.selector];r.reactComponent&&i.push(`(${r.reactComponent})`),r.context&&i.push(`in ${r.context}`),r.textContent&&i.push(`"${r.textContent.slice(0,80)}"`),o.push(`- ${i.join(" ")}`)}return o.join(`
|
|
168
168
|
`)}function Cs(n,e,t){let o=[],i=new Set(e.annotations.map(s=>s.pathname).filter(Boolean)).size>1;if(o.push("You are reviewing a UI screenshot with developer annotations."),o.push(""),!i&&t?.provider!=="codex"&&(o.push(`IMPORTANT: First, use the Read tool to view the screenshot at: ${n}`),o.push("")),o.push(`The developer annotated their running app at ${e.url} (${e.viewport.width}x${e.viewport.height}).`),t?.threadHistory&&t.threadHistory.length>0){o.push(""),o.push("## Previous Conversation");let s=0;for(let a of t.threadHistory)if(a.role==="human")s++,a.replyToQuestion?(o.push(`### Round ${s} (human) \u2014 reply`),o.push(`"${a.replyToQuestion}"`)):(o.push(`### Round ${s} (human)`),a.feedbackSummary&&o.push(`Annotations: ${a.feedbackSummary}`),a.annotationIds&&a.annotationIds.length>0&&o.push(`Annotation IDs: ${a.annotationIds.join(", ")}`));else if(a.question)o.push(`### Round ${s} (assistant) \u2014 question`),o.push(`"${a.question}"`);else{if(o.push(`### Round ${s} (assistant)`),a.responseText&&o.push(`Response: ${a.responseText}`),a.resolutions&&a.resolutions.length>0)for(let d of a.resolutions){let c=d.finalScope??d.inferredScope,w=c?` [${c.breadth} ${c.target}]`:"";o.push(`- ${d.annotationId}: ${d.status}${w} \u2014 ${d.summary}`),d.filesModified&&d.filesModified.length>0&&o.push(` Files: ${d.filesModified.join(", ")}`)}a.toolsUsed&&a.toolsUsed.length>0&&o.push(`Tools used: ${a.toolsUsed.join(", ")}`)}o.push(""),o.push("The current round is shown in full below.")}if(t?.designModel){o.push(""),o.push("## Established Design Policies"),o.push("This project has an active product model projected only from accepted canonical decisions. When making changes, follow these patterns unless the developer explicitly overrides them.");let s=t.designModel.rules;if(Array.isArray(s)&&s.length>0)if(o.push(""),o.push("Rules:"),s.length>0&&typeof s[0]=="object"&&s[0]!==null&&"scope"in s[0]){let w=new Map;for(let v of s)if(typeof v=="object"&&v!==null&&"text"in v){let g=v,k=g.scope||"general";w.has(k)||w.set(k,[]),w.get(k).push(g.text)}for(let[v,g]of w){o.push(`**${v.charAt(0).toUpperCase()+v.slice(1)}**`);for(let k of g)o.push(`- ${k}`)}}else for(let w of s)typeof w=="string"&&o.push(`- ${w}`);let a=t.designModel.tokens;a&&typeof a=="object"&&(o.push(""),o.push("Design tokens:"),o.push("```json"),o.push(JSON.stringify(a,null,2)),o.push("```"));let d=t.designModel.components;d&&typeof d=="object"&&(o.push(""),o.push("Component patterns:"),o.push("```json"),o.push(JSON.stringify(d,null,2)),o.push("```")),o.push(""),o.push("### Applied Model-Gap Details"),o.push("Only when you actually modify the project and implement a design decision that has no matching policy in the model above (e.g., styling a component type not yet in the model, choosing a color with no token, picking spacing with no rule), describe that applied gap:"),o.push("<novel>"),o.push('[{"category":"component","element":"button","decision":"Used 8px border-radius, 12px 24px padding","reason":"No button pattern in design model"}]'),o.push("</novel>"),o.push('- `category`: "token" (color, spacing, typography), "component" (UI component pattern), or "element" (specific element style)'),o.push("- `element`: What you are styling or creating"),o.push("- `decision`: What you actually implemented (specific values)"),o.push("- `reason`: Why this is novel (what is missing from the model)"),o.push("Do not emit <novel> for answer-only, explanatory, investigative, advisory, planning, or question turns. Never describe a recommendation or proposed future change as applied."),o.push("This is an execution receipt, not a memory proposal. Continuous imprinting separately decides whether the collaboration produced reusable learning.")}if(t?.designModel||(o.push(""),o.push("## Design Context"),o.push("This project uses Popmelt for product governance. Accepted canonical decisions may be projected into a rebuildable working model.")),i){let s=t?.screenshotPaths??{},a=new Map;for(let d of e.annotations){let c=d.pathname||new URL(e.url).pathname;a.has(c)||a.set(c,[]),a.get(c).push(d)}o.push(""),o.push("The developer annotated multiple pages. Each page section includes its own screenshot where available.");for(let[d,c]of a){o.push(""),o.push(`## Page: ${d}`);let w=s[d];w&&t?.provider!=="codex"?o.push(`IMPORTANT: Use the Read tool to view the screenshot at: ${w}`):!w&&t?.provider!=="codex"&&(Object.keys(s).length===0?(o.push(`IMPORTANT: Use the Read tool to view the screenshot at: ${n}`),o.push("(This screenshot shows the page the developer was on when submitting \u2014 not this page)")):o.push("(No screenshot available for this page \u2014 work from the annotation text and selectors)"));let v={...e,annotations:c,styleModifications:e.styleModifications},g=Bn(v,t?.imagePaths,t?.attachmentFiles);g&&(o.push(""),o.push(g))}}else{let s=Bn(e,t?.imagePaths,t?.attachmentFiles);s&&(o.push(""),o.push(s))}return o.push(""),o.push("Follow the developer's instructions. If they ask for changes, apply them to the source files \u2014 the dev server has HMR so changes appear immediately. If they ask a question or request analysis, respond in text without modifying code."),o.push(""),o.push("INTERNAL IMPLEMENTATION REQUIREMENT: If any elements you modify have a `data-pm` attribute, preserve it in the source. Do not mention this attribute or requirement in user-visible prose."),_s(o),o.push(""),o.push("## Internal completion metadata (not user-visible)"),o.push("After completing all work, output one resolution envelope listing what you did for each annotation:"),o.push("<resolution>"),o.push('{"annotations":[{"annotationId":"<id>","status":"resolved","summary":"<what you did>","filesModified":["<file>"],"declaredScope":{"breadth":"...","target":"..."},"inferredScope":{"breadth":"...","target":"..."}}]}'),o.push("</resolution>"),o.push("Continuous imprinting may require an additional `imprint` field in this same envelope. Follow that contract when it appears below; do not emit a second resolution block."),o.push(`Use status "resolved" when the change is complete, or "needs_review" if you're unsure about the result.`),o.push(""),o.push("### Scope classification"),o.push("Each resolution MUST include scope fields:"),o.push("- `declaredScope`: What scope the user's instruction text implies. null if no signal."),o.push("- `inferredScope`: What scope the change actually has, based on what you modified."),o.push("Scope has two dimensions:"),o.push('- `breadth`: "instance" (just this occurrence) or "pattern" (all similar occurrences)'),o.push('- `target`: "element" (a specific DOM element), "component" (a React/UI component), or "token" (a design token \u2014 color, spacing, typography)'),o.push('Note: "instance" + "token" is invalid \u2014 tokens are inherently patterns.'),o.push("If you cannot confidently determine scope, set it to null."),o.push(""),o.push("## Questions"),o.push("If the annotation text is unclear, ambiguous, gibberish, or you are unsure what the developer wants, output a question:"),o.push('<question>What do you mean by "..."?</question>'),o.push("Do NOT guess what unclear instructions mean \u2014 ask instead."),o.push("You may output BOTH a <resolution> for clear annotations AND a <question> for unclear ones in the same response."),o.join(`
|
|
169
169
|
`)}function $s(n){return n.match(/<question>\s*([\s\S]*?)\s*<\/question>/)?.[1]??null}function Os(n,e,t,o){let r=[];r.push("You are continuing work on a UI based on the developer's reply to your question."),r.push(""),t!=="codex"&&r.push(`IMPORTANT: First, use the Read tool to view the screenshot at: ${n}`);let i=e.find(s=>s.role==="human"&&s.feedbackContext);if(i?.feedbackContext&&(r.push(""),r.push(i.feedbackContext)),e.length>0){r.push(""),r.push("## Conversation History");let s=0;for(let a of e)a.role==="human"?(s++,a.replyToQuestion?(r.push(`### Round ${s} (human) \u2014 reply`),r.push(`"${a.replyToQuestion}"`)):(r.push(`### Round ${s} (human)`),a.feedbackSummary&&r.push(`Annotations: ${a.feedbackSummary}`))):a.question?(r.push(`### Round ${s} (assistant) \u2014 question`),r.push(`"${a.question}"`)):(r.push(`### Round ${s} (assistant)`),a.responseText&&r.push(`Response: ${a.responseText}`))}if(r.push(""),r.push("The developer answered your question. Continue working based on their reply."),r.push("Follow their instructions \u2014 apply code changes only if requested. The dev server has HMR so changes appear immediately."),r.push(""),r.push("INTERNAL IMPLEMENTATION REQUIREMENT: If any elements you modify have a `data-pm` attribute, preserve it in the source. Do not mention this attribute or requirement in user-visible prose."),_s(r),o&&o.length>0){r.push(""),r.push("## Attached Images"),r.push("The developer attached reference images with their reply:");for(let s of o)r.push(`Attached image: use the Read tool to view the image at: ${s}`)}return r.push(""),r.push("## Internal completion metadata (not user-visible)"),r.push("After completing all work, output one resolution envelope listing what you did for each annotation:"),r.push("<resolution>"),r.push('{"annotations":[{"annotationId":"<id>","status":"resolved","summary":"<what you did>","filesModified":["<file>"],"declaredScope":{"breadth":"...","target":"..."},"inferredScope":{"breadth":"...","target":"..."}}]}'),r.push("</resolution>"),r.push("Continuous imprinting may require an additional `imprint` field in this same envelope. Follow that contract when it appears below; do not emit a second resolution block."),r.push(`Use status "resolved" when the change is complete, or "needs_review" if you're unsure about the result.`),r.push(""),r.push("### Scope classification"),r.push("Each resolution MUST include scope fields:"),r.push("- `declaredScope`: What scope the user's instruction text implies. null if no signal."),r.push("- `inferredScope`: What scope the change actually has, based on what you modified."),r.push("Scope has two dimensions:"),r.push('- `breadth`: "instance" (just this occurrence) or "pattern" (all similar occurrences)'),r.push('- `target`: "element" (a specific DOM element), "component" (a React/UI component), or "token" (a design token \u2014 color, spacing, typography)'),r.push('Note: "instance" + "token" is invalid \u2014 tokens are inherently patterns.'),r.push("If you cannot confidently determine scope, set it to null."),r.push('If the developer\'s reply corrects a prior scope classification (e.g., "this should apply everywhere" or "only fix this one"), set `finalScope` on your resolution to reflect their correction and apply the change at the corrected scope.'),r.push(""),r.push("## Questions"),r.push("If you still need clarification, output:"),r.push("<question>Your question here</question>"),r.push("You may output BOTH a <resolution> and a <question> in the same response."),r.join(`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as p}from"./chunk-NOHXTT24.mjs";import{request as c}from"http";function u(){let e=process.env.POPMELT_BRIDGE_URL;if(!e)return null;try{let t=new URL(e);return{port:t.port?Number.parseInt(t.port,10):null,projectId:process.env.POPMELT_PROJECT_ID||null,leaseEligible:!1}}catch{return null}}async function h(e,t,i){let r=u();if(e?.bridge===!1||r)return r??{port:null,projectId:null,leaseEligible:!1};try{let{startPopmelt:o}=await import("./server-
|
|
1
|
+
import{a as p}from"./chunk-NOHXTT24.mjs";import{request as c}from"http";function u(){let e=process.env.POPMELT_BRIDGE_URL;if(!e)return null;try{let t=new URL(e);return{port:t.port?Number.parseInt(t.port,10):null,projectId:process.env.POPMELT_PROJECT_ID||null,leaseEligible:!1}}catch{return null}}async function h(e,t,i){let r=u();if(e?.bridge===!1||r)return r??{port:null,projectId:null,leaseEligible:!1};try{let{startPopmelt:o}=await import("./server-XGSUCWFZ.mjs"),n=t.config?.server?.host,l=n===!0?"0.0.0.0":n||"localhost",s=t.config?.server?.port??i,d=`http://${l==="0.0.0.0"?"localhost":l}:${s}`,a=await o({port:e?.port,projectRoot:e?.projectRoot,devOrigin:d,force:!0,detached:!0,manageLease:!1});return console.log(`[popmelt] bridge ready at http://localhost:${a.port}`),{port:a.port,projectId:a.projectId,leaseEligible:!0}}catch(o){let n=o instanceof Error?o.message:String(o);return console.warn("[popmelt] bridge failed to start:",n),{port:null,projectId:null,leaseEligible:!1}}}async function v(e,t,i=fetch){if(!e.leaseEligible||e.port===null)return null;let r=await p({port:e.port,projectId:e.projectId},i);if(!r)return null;let o=()=>{n()},n=async()=>{process.removeListener("beforeExit",o),await r.release()};return process.once("beforeExit",o),t.httpServer?.once("close",()=>{n()}),{id:r.id,release:n}}function g(e,t,i){if(i.port===null)return null;let r=new URL(t,"http://popmelt.local");if(r.pathname!==`${e}/status`)return null;let o=new URL("/status",`http://localhost:${i.port}`);return r.searchParams.forEach((n,l)=>o.searchParams.append(l,n)),i.projectId&&o.searchParams.set("projectId",i.projectId),o}function b(e,t){return(i,r,o)=>{let n=g(e,i.url||"",t());if(!n)return o();let l=c(n,{method:i.method,headers:{...i.headers,host:n.host}},s=>{r.writeHead(s.statusCode||502,s.headers),s.pipe(r)});l.on("error",()=>{r.writeHead(502,{"Content-Type":"text/plain"}),r.end(`Popmelt bridge not running on port ${t().port}`)}),i.pipe(l)}}function P(e){let t=e.port===null?null:`http://localhost:${e.port}`;return`window.__POPMELT_BRIDGE_URL__=${JSON.stringify(t)};window.__POPMELT_PROJECT_ID__=${JSON.stringify(e.projectId)};`}export{h as a,v as b,b as c,P as d};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
var S=(n,e=0,i=1)=>Math.min(i,Math.max(e,n));function ie(n){let{width:e,height:i}=n.rect,t=Math.max(1,n.naturalWidth),s=Math.max(1,n.naturalHeight),r=Math.min(e/t,i/s),o=Math.max(e/t,i/s);switch(n.objectFit){case"contain":return{width:t*r,height:s*r};case"cover":return{width:t*o,height:s*o};case"none":return{width:t,height:s};case"scale-down":{let a=Math.min(1,r);return{width:t*a,height:s*a}}default:return{width:e,height:i}}}function D(n,e){let i=n.trim().toLowerCase();if(i.endsWith("%")){let t=Number.parseFloat(i);return Number.isFinite(t)?e*(t/100):0}if(i.endsWith("px")){let t=Number.parseFloat(i);return Number.isFinite(t)?t:0}return i==="center"?e/2:i==="right"||i==="bottom"?e:0}function se(n,e,i){let t=n.objectPosition.trim().split(/\s+/),s=t[0]||"50%",r=t[1]||"50%";return{x:D(s,n.rect.width-e),y:D(r,n.rect.height-i)}}function L(n){let e=n.getBoundingClientRect();if(e.width<=0||e.height<=0||n.naturalWidth<=0||n.naturalHeight<=0)return null;let i=n.ownerDocument.defaultView?.getComputedStyle(n)??getComputedStyle(n),t={rect:e,naturalWidth:n.naturalWidth,naturalHeight:n.naturalHeight,objectFit:i.objectFit||"fill",objectPosition:i.objectPosition||"50% 50%"},s=ie(t);return{layout:t,rendered:s,position:se(t,s.width,s.height)}}function E(n,e){let i=L(n);if(!i)return null;let{layout:t,rendered:s,position:r}=i;if(e.x<t.rect.left||e.x>t.rect.left+t.rect.width||e.y<t.rect.top||e.y>t.rect.top+t.rect.height)return null;let o=(e.x-t.rect.left-r.x)/s.width,a=(e.y-t.rect.top-r.y)/s.height;return o>=0&&o<=1&&a>=0&&a<=1?{x:o,y:a}:null}function N(n,e){let i=L(n);if(!i)return null;let{layout:t,rendered:s,position:r}=i,o=(e.left-t.rect.left-r.x)/s.width,a=(e.top-t.rect.top-r.y)/s.height,l=(e.left+e.width-t.rect.left-r.x)/s.width,d=(e.top+e.height-t.rect.top-r.y)/s.height,c=S(Math.min(o,l)),u=S(Math.min(a,d)),m=S(Math.max(o,l)),h=S(Math.max(a,d));return m<=c||h<=u?null:{left:c,top:u,width:m-c,height:h-u}}function H(n,e){if(![e.left,e.top,e.width,e.height].every(Number.isFinite))return null;let i=L(n);if(!i)return null;let{layout:t,rendered:s,position:r}=i,{rect:o}=t,a=o.left+r.x+S(e.left)*s.width,l=o.top+r.y+S(e.top)*s.height,d=o.left+r.x+S(e.left+Math.max(0,e.width))*s.width,c=o.top+r.y+S(e.top+Math.max(0,e.height))*s.height,u=Math.max(o.left,a),m=Math.max(o.top,l),h=Math.min(o.left+o.width,d),g=Math.min(o.top+o.height,c);return h<=u||g<=m?null:{left:u,top:m,width:h-u,height:g-m}}var ne="popmelt:see:v1";function K(n){if(!n||typeof n!="object")return!1;let e=n;return["left","top","width","height"].every(i=>typeof e[i]=="number"&&Number.isFinite(e[i]))}function re(n){if(!n||typeof n!="object")return!1;let e=n;return typeof e.id=="string"&&typeof e.label=="string"&&typeof e.score=="number"&&(e.origin==="detected"||e.origin==="manual")&&K(e.box)&&(e.detectedBox===void 0||K(e.detectedBox))}function oe(n){if(!n||typeof n!="object")return!1;let e=n;return typeof e.detectedLabel=="string"&&K(e.detectedBox)}function _(n){if(!n||typeof n!="object")return!1;let e=n;return e.schemaVersion===1&&typeof e.source=="string"&&typeof e.imageHash=="string"&&typeof e.inferenceKey=="string"&&typeof e.dedupeKey=="string"&&typeof e.updatedAt=="number"&&Array.isArray(e.subjects)&&e.subjects.every(re)&&(e.suppressions===void 0||Array.isArray(e.suppressions)&&e.suppressions.every(oe))}function Ie(n={}){let e=n.namespace??ne,i=()=>n.storage??window.localStorage,t=a=>`${e}:${a}`,s=a=>`${t(a)}:history`,r=a=>{try{let l=i().getItem(t(a));if(!l)return null;let d=JSON.parse(l);return _(d)?d:null}catch{return null}},o=a=>{try{let l=i().getItem(s(a));if(!l)return[];let d=JSON.parse(l);return Array.isArray(d)?d.filter(_).slice(0,10):[]}catch{return[]}};return{load:r,save(a,l){let d=r(a);if(d&&d.imageHash!==l.imageHash){let c=o(a).filter(u=>u.imageHash!==d.imageHash||u.updatedAt!==d.updatedAt);i().setItem(s(a),JSON.stringify([d,...c].slice(0,10)))}i().setItem(t(a),JSON.stringify(l))},remove(a){i().removeItem(t(a)),i().removeItem(s(a))},loadHistory:o}}function je(){let n=new Map,e=new Map;return{load:i=>n.get(i)??null,save:(i,t)=>{let s=n.get(i);if(s&&s.imageHash!==t.imageHash){let r=e.get(i)??[];e.set(i,[s,...r.filter(o=>o.imageHash!==s.imageHash||o.updatedAt!==s.updatedAt)].slice(0,10))}n.set(i,t)},remove:i=>{n.delete(i),e.delete(i)},loadHistory:i=>e.get(i)??[]}}function U(n){return{label:n.label,score:n.score,box:{...n.box}}}var R="img";function j(n){let e=2166136261;for(let i=0;i<n.length;i+=1)e^=n.charCodeAt(i),e=Math.imul(e,16777619);return(e>>>0).toString(36)}function B(n){return new URL(y(n),n.ownerDocument.baseURI).href}async function ae(n){let e=B(n);try{let i=await fetch(e,{cache:"force-cache",credentials:"same-origin"});if(!i.ok)throw new Error(`Unable to hash image (${i.status})`);let t=await i.arrayBuffer();if(!globalThis.crypto?.subtle)throw new Error("SubtleCrypto unavailable");let s=await globalThis.crypto.subtle.digest("SHA-256",t);return Array.from(new Uint8Array(s),r=>r.toString(16).padStart(2,"0")).join("")}catch{return`source:${j(e)}:${n.naturalWidth}x${n.naturalHeight}`}}function y(n){return n.currentSrc||n.src||n.getAttribute("src")||""}function de(n){return n.complete&&n.naturalWidth>0&&n.naturalHeight>0?Promise.resolve():new Promise((e,i)=>{let t=()=>{r(),e()},s=()=>{r(),i(new Error(`Unable to load image: ${y(n)}`))},r=()=>{n.removeEventListener("load",t),n.removeEventListener("error",s)};n.addEventListener("load",t,{once:!0}),n.addEventListener("error",s,{once:!0})})}function ce(n,e){return{id:e.id,image:n,detection:U(e),detectedLabel:e.detectedLabel,detectedBox:e.detectedBox?{...e.detectedBox}:void 0,boundsOverridden:e.boundsOverridden,manual:e.origin==="manual"}}function le(n){return{id:n.id,label:n.detection.label,detectedLabel:n.detectedLabel,score:n.detection.score,box:{...n.detection.box},detectedBox:n.detectedBox?{...n.detectedBox}:void 0,origin:n.manual?"manual":"detected",boundsOverridden:n.boundsOverridden}}function ue(n){return n.origin==="manual"||n.boundsOverridden===!0||!!(n.detectedLabel&&n.label!==n.detectedLabel)}function me(n,e){return e.x>=n.left&&e.x<=n.left+n.width&&e.y>=n.top&&e.y<=n.top+n.height}var O=class{constructor(e={}){this.options=e;this.surfaceKind="image";this.records=new Map;this.imageKeys=new Map;this.analyzedSources=new WeakMap;this.pending=new WeakMap;this.analysisMetadata=new WeakMap;this.suppressions=new WeakMap;this.writeChains=new WeakMap;this.pendingWrites=new Set;this.refreshQueued=!1;this.destroyed=!1;this.manualSequence=0;this.id=e.id??"see:image",this.readyPromise=this.refresh();let i=e.root??document;this.observer=e.observe!==!1&&typeof MutationObserver<"u"?new MutationObserver(()=>this.queueRefresh()):null,this.observer?.observe(i,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["src","srcset","sizes"]})}whenReady(){return this.readyPromise}listSubjects(){let e=[];for(let i of this.records.keys()){let t=this.resolve(i);t&&e.push(t)}return e}async refresh(){if(this.destroyed)return;let e=this.options.root??document,i=this.options.selector??R,t=new Set;e instanceof HTMLImageElement&&e.matches(i)&&t.add(e);for(let s of e.querySelectorAll(i))s instanceof HTMLImageElement&&t.add(s);for(let s of this.imageKeys.keys())(!t.has(s)||!s.isConnected)&&this.removeImage(s);if(this.options.recognition)for(let s of t)await this.analyze(s);else await Promise.all(Array.from(t,s=>this.analyze(s)))}hitTest(e){let i=null;for(let t of this.records.keys()){let s=this.resolve(t),r=this.records.get(t);if(!s||!r||!me(s.bounds,e))continue;let o=s.bounds.width*s.bounds.height;(!i||o<i.area||o===i.area&&r.detection.score>i.score)&&(i={subject:s,area:o,score:r.detection.score})}return i?.subject??null}resolve(e){let i=this.records.get(e);if(!i||!i.image.isConnected)return null;let t=H(i.image,i.detection.box);if(!t)return null;let s=i.image.getBoundingClientRect(),r=Math.round(i.detection.score*1e3)/1e3;return{key:e,label:i.detection.label,sourceElement:i.image,imageObject:this.imageObjectContext(i),bounds:t,surfaceBounds:{left:s.left,top:s.top,width:s.width,height:s.height},context:`${i.detection.label} ${i.manual?"labelled":"detected"} in ${i.image.alt||"image"}`,data:{kind:"image-object",label:i.detection.label,origin:i.manual?"manual":"detected",...i.detectedLabel?{detectedLabel:i.detectedLabel}:{},score:String(r),imageSrc:y(i.image)}}}restoreSubject(e,i){if(this.records.has(e))return this.resolve(e);let t=i.imageObject;if(!t)return null;let s=i.sourceElement?.dataAttributes,r=s?.["data-popmelt-media-id"]??s?.["data-popmelt-see-id"],o=this.matchingImages().find(p=>{let f=y(p),b=this.imageIdentity(p,f);return!(!e.startsWith(`${b}:`)||r&&b!==r)});if(!o)return null;let a=this.imageIdentity(o,y(o)),l=e.slice(a.length+1),d={left:t.normalizedBox.x,top:t.normalizedBox.y,width:t.normalizedBox.width,height:t.normalizedBox.height};if(!l||!Object.values(d).every(p=>Number.isFinite(p)&&p>=0&&p<=1))return null;let c=i.dataAttributes,u=i.subjectLabel??c?.label??i.textContent;if(!u)return null;let m=Number(c?.score),h=c?.origin==="manual";this.records.set(e,{id:l,image:o,detection:{label:u,score:Number.isFinite(m)?m:1,box:d},detectedLabel:c?.detectedLabel,detectedBox:h?void 0:{...d},boundsOverridden:!0,manual:h});let g=this.imageKeys.get(o)??new Set;if(g.add(e),this.imageKeys.set(o,g),!this.analysisMetadata.has(o)){let p=B(o);this.analysisMetadata.set(o,{storeKey:`${this.id}:${a}`,source:p,imageHash:`recovered:${j(p)}`,inferenceKey:"",dedupeKey:""}),this.suppressions.set(o,[])}return this.persistImage(o),this.resolve(e)}imageObjectContext(e){let{image:i,detection:{box:t}}=e;return{src:y(i),alt:i.alt,width:i.naturalWidth,height:i.naturalHeight,normalizedBox:{x:t.left,y:t.top,width:t.width,height:t.height},box:{x:t.left*i.naturalWidth,y:t.top*i.naturalHeight,width:t.width*i.naturalWidth,height:t.height*i.naturalHeight}}}async captureSubject(e){let i=this.records.get(e);if(!i?.image.isConnected||!i.image.complete)return null;let{box:t}=this.imageObjectContext(i);if(t.width<=0||t.height<=0)return null;let s=Math.min(1,1024/Math.max(t.width,t.height)),r=document.createElement("canvas");r.width=Math.max(1,Math.round(t.width*s)),r.height=Math.max(1,Math.round(t.height*s));let o=r.getContext("2d");return o?(o.drawImage(i.image,t.x,t.y,t.width,t.height,0,0,r.width,r.height),new Promise((a,l)=>{try{r.toBlob(a,"image/webp",.95)}catch(d){l(d)}})):null}beginCreate(e){let i=this.matchingImages();for(let t=i.length-1;t>=0;t-=1){let s=i[t];if(!s)continue;let r=E(s,e);if(!r)continue;let o=y(s),a=this.imageIdentity(s,o),l=`manual:${this.newObjectId()}`,d=`${a}:${l}`,c={label:"object",score:1,box:{left:r.x,top:r.y,width:0,height:0}},u=!1,m=!1,h=()=>{if(m)return;m=!0,this.records.set(d,{id:l,image:s,detection:c,manual:!0});let g=this.imageKeys.get(s)??new Set;g.add(d),this.imageKeys.set(s,g)};return{key:d,update:g=>{if(u)return null;h();let p=this.clampedImagePoint(s,g);return p?(c.box={left:Math.min(r.x,p.x),top:Math.min(r.y,p.y),width:Math.abs(p.x-r.x),height:Math.abs(p.y-r.y)},this.resolve(d)):this.resolve(d)},commit:()=>u?null:(h(),this.persistImage(s),this.resolve(d)),cancel:()=>{u||(u=!0,m&&this.removeSubject(d))}}}return null}renameSubject(e,i){let t=this.records.get(e),s=i.trim();return!t||!s?null:(!t.manual&&!t.detectedLabel&&(t.detectedLabel=t.detection.label),t.detection.label=s,this.persistImage(t.image),this.resolve(e))}updateSubjectBounds(e,i){let t=this.records.get(e);if(!t)return null;let s=N(t.image,i);return s?(t.detection.box=s,t.boundsOverridden=!0,this.persistImage(t.image),this.resolve(e)):null}removeSubject(e){let i=this.records.get(e);if(!i)return;if(!i.manual){let s=this.suppressions.get(i.image)??[];s.push({detectedLabel:i.detectedLabel??i.detection.label,detectedBox:{...i.detectedBox??i.detection.box}}),this.suppressions.set(i.image,s)}this.records.delete(e);let t=this.imageKeys.get(i.image);t?.delete(e),t?.size===0&&this.imageKeys.delete(i.image),this.persistImage(i.image)}async destroy(){this.destroyed||(this.destroyed=!0,this.observer?.disconnect(),this.records.clear(),this.imageKeys.clear(),await Promise.allSettled(this.pendingWrites),await this.options.recognition?.dispose?.())}queueRefresh(){this.refreshQueued||this.destroyed||(this.refreshQueued=!0,queueMicrotask(()=>{this.refreshQueued=!1,this.readyPromise=this.refresh()}))}async analyze(e){let i=y(e);if(!i||this.analyzedSources.get(e)===i)return;let t=this.pending.get(e);if(t)return t.source===i?t.promise:(await t.promise,this.analyze(e));this.analyzedSources.has(e)&&this.removeImage(e);let s={source:i,promise:Promise.resolve()},r=(async()=>{try{if(await de(e),this.destroyed)return;let o=B(e),a=this.imageIdentity(e,i),l=`${this.id}:${a}`,d=this.options.recognition,c=this.options.store?await this.options.store.load(l):null;if(this.destroyed||y(e)!==i)return;let u=d?.cacheKey??c?.inferenceKey??"",m=d?.configurationKey??c?.dedupeKey??"",h=c?.source===o,g=!!(e.dataset.popmeltMediaId||e.dataset.popmeltSeeId),p=h||g;if(c&&p){let I=new Map(c.subjects.map(T=>[T.id,T]));for(let T of this.imageSubjects(e))I.set(T.id,T);this.replaceImageRecords(e,a,[...I.values()]),this.suppressions.set(e,[...c.suppressions??[]]),this.analysisMetadata.set(e,{storeKey:l,source:o,imageHash:c.imageHash,inferenceKey:c.inferenceKey,dedupeKey:c.dedupeKey})}if(this.options.hydrateOnly){this.analyzedSources.set(e,i);return}let f=this.options.store?await(this.options.imageHasher??ae)(e):"ephemeral";if(this.destroyed||y(e)!==i)return;if(!!(c&&p&&c.imageHash===f&&(!d||c.inferenceKey===u&&c.dedupeKey===m))){this.analysisMetadata.set(e,{storeKey:l,source:o,imageHash:f,inferenceKey:u,dedupeKey:m}),this.analyzedSources.set(e,i),await this.saveImage(e);return}let V=p&&c?.imageHash===f;if(c&&!V){let I=new Set(c.subjects.map(A=>A.id)),T=c.subjects.filter(ue);this.replaceImageRecords(e,a,[...T,...this.imageSubjects(e).filter(A=>!I.has(A.id))]),this.suppressions.set(e,[])}if(this.analysisMetadata.set(e,{storeKey:l,source:o,imageHash:f,inferenceKey:V?c.inferenceKey:"",dedupeKey:V?c.dedupeKey:""}),!d){this.analyzedSources.set(e,i),await this.saveImage(e);return}let ee=await d.detect(e);if(this.destroyed||y(e)!==i)return;let W=this.suppressions.get(e)??[],te=d.reconcile(ee,this.imageSubjects(e),W);this.replaceImageRecords(e,a,te),this.suppressions.set(e,[...W]),this.analysisMetadata.set(e,{storeKey:l,source:o,imageHash:f,inferenceKey:u,dedupeKey:m}),this.analyzedSources.set(e,i),await this.saveImage(e)}catch(o){this.options.onError?.(o,e)}finally{this.pending.get(e)===s&&this.pending.delete(e)}})();return s.promise=r,this.pending.set(e,s),r}replaceImageRecords(e,i,t){this.removeImage(e);let s=new Set;for(let r of t){let o=`${i}:${r.id}`;s.add(o),this.records.set(o,ce(e,r))}this.imageKeys.set(e,s)}imageIdentity(e,i){if(e.dataset.popmeltMediaId)return e.dataset.popmeltMediaId;if(e.dataset.popmeltSeeId)return e.dataset.popmeltSeeId;let t=new URL(i,e.ownerDocument.baseURI).href,s=this.options.root??e.ownerDocument,r=this.options.selector??R,o=Array.from(s.querySelectorAll(r)).filter(l=>y(l)===i),a=Math.max(0,o.indexOf(e));return`${j(t)}:${a}`}matchingImages(){let e=this.options.root??document,i=this.options.selector??R,t=[];e instanceof HTMLImageElement&&e.matches(i)&&t.push(e);for(let s of e.querySelectorAll(i))s instanceof HTMLImageElement&&t.push(s);return t}clampedImagePoint(e,i){let t=H(e,{left:0,top:0,width:1,height:1});return t?E(e,{x:Math.max(t.left,Math.min(t.left+t.width,i.x)),y:Math.max(t.top,Math.min(t.top+t.height,i.y))}):null}newObjectId(){let e=globalThis.crypto?.randomUUID?.();return e||j(`${Date.now()}:${this.manualSequence++}:${Math.random().toString(36)}`)}persistImage(e){let i=this.persistencePayload(e);if(!i)return Promise.resolve();let s=(this.writeChains.get(e)??Promise.resolve()).catch(()=>{}).then(()=>i.store.save(i.key,i.analysis)).catch(r=>{this.options.onError?.(r,e)});return this.writeChains.set(e,s),this.pendingWrites.add(s),s.finally(()=>this.pendingWrites.delete(s)),s}async saveImage(e){await this.persistImage(e)}imageSubjects(e){return Array.from(this.imageKeys.get(e)??[]).map(i=>this.records.get(i)).filter(i=>!!i).map(le)}persistencePayload(e){let i=this.options.store,t=this.analysisMetadata.get(e);if(!i||!t)return null;let s=this.imageSubjects(e),r={schemaVersion:1,source:t.source,imageHash:t.imageHash,inferenceKey:t.inferenceKey,dedupeKey:t.dedupeKey,subjects:s,suppressions:this.suppressions.get(e)??[],updatedAt:Date.now()};return{store:i,key:t.storeKey,analysis:r}}removeImage(e){for(let i of this.imageKeys.get(e)??[])this.records.delete(i);this.imageKeys.delete(e),this.analyzedSources.delete(e),this.analysisMetadata.delete(e),this.suppressions.delete(e)}};function Le(n={}){return new O(n)}var w=(n,e=0,i=1)=>Math.min(i,Math.max(e,n));function F(n){let e=n.getBoundingClientRect();if(e.width<=0||e.height<=0||n.videoWidth<=0||n.videoHeight<=0)return null;let i=n.ownerDocument.defaultView?.getComputedStyle(n)??getComputedStyle(n),t=i.objectFit||"fill",s=Math.min(e.width/n.videoWidth,e.height/n.videoHeight),r=Math.max(e.width/n.videoWidth,e.height/n.videoHeight),o=t==="contain"?s:t==="cover"?r:t==="none"?1:t==="scale-down"?Math.min(1,s):null,a=o?{width:n.videoWidth*o,height:n.videoHeight*o}:{width:e.width,height:e.height},l=(i.objectPosition||"50% 50%").split(/\s+/),d=(c,u)=>c.endsWith("%")?u*(Number.parseFloat(c)/100):c.endsWith("px")?Number.parseFloat(c):c==="center"?u/2:c==="right"||c==="bottom"?u:0;return{rect:e,rendered:a,position:{x:d(l[0]||"50%",e.width-a.width),y:d(l[1]||"50%",e.height-a.height)}}}function z(n,e){let i=F(n);if(!i)return null;let{rect:t,rendered:s,position:r}=i;if(e.x<t.left||e.x>t.right||e.y<t.top||e.y>t.bottom)return null;let o=(e.x-t.left-r.x)/s.width,a=(e.y-t.top-r.y)/s.height;return o>=0&&o<=1&&a>=0&&a<=1?{x:o,y:a}:null}function q(n,e){let i=F(n);if(!i)return null;let{rect:t,rendered:s,position:r}=i,o=t.left+r.x+w(e.left)*s.width,a=t.top+r.y+w(e.top)*s.height,l=t.left+r.x+w(e.left+Math.max(0,e.width))*s.width,d=t.top+r.y+w(e.top+Math.max(0,e.height))*s.height,c=Math.max(t.left,o),u=Math.max(t.top,a),m=Math.min(t.right,l),h=Math.min(t.bottom,d);return m<=c||h<=u?null:{left:c,top:u,width:m-c,height:h-u}}function Q(n,e){let i=F(n);if(!i)return null;let{rect:t,rendered:s,position:r}=i,o=(e.left-t.left-r.x)/s.width,a=(e.top-t.top-r.y)/s.height,l=(e.left+e.width-t.left-r.x)/s.width,d=(e.top+e.height-t.top-r.y)/s.height,c=w(Math.min(o,l)),u=w(Math.min(a,d)),m=w(Math.max(o,l)),h=w(Math.max(a,d));return m<=c||h<=u?null:{left:c,top:u,width:m-c,height:h-u}}var he="video",pe=1/60,Y=30,ge=.9,fe=250,M=.001;function J(n){let e=2166136261;for(let i=0;i<n.length;i+=1)e^=n.charCodeAt(i),e=Math.imul(e,16777619);return(e>>>0).toString(36)}function v(n){return n.currentSrc||n.src||n.querySelector("source")?.src||n.getAttribute("src")||""}function ye(n){return n.readyState>=1&&n.videoWidth>0&&n.videoHeight>0?Promise.resolve():new Promise((e,i)=>{let t=()=>{r(),e()},s=()=>{r(),i(new Error(`Unable to load video: ${v(n)}`))},r=()=>{n.removeEventListener("loadedmetadata",t),n.removeEventListener("error",s)};n.addEventListener("loadedmetadata",t,{once:!0}),n.addEventListener("error",s,{once:!0})})}function be(n,e,i){let t=(s,r)=>s+(r-s)*i;return{left:t(n.left,e.left),top:t(n.top,e.top),width:t(n.width,e.width),height:t(n.height,e.height)}}function x(n,e){return n.find(i=>e>=i.start-.001&&(i.end===void 0||e<=i.end+.001))??null}function C(n){return!x(n.segments,n.video.currentTime)}function k(n){return n?.motion.createSmoother()??{smooth(e){return{...e,box:{...e.box}}}}}function X(n){return{visible:!C(n),misses:n.misses,hits:n.hits,lostAt:n.lostAt}}function ve(n,e){n.misses=e.misses,n.hits=e.hits,n.lostAt=e.lostAt}function Se(n,e){if(e?.length)return e.map(t=>({...t})).sort((t,s)=>t.start-s.start);let i=n.reduce((t,s)=>Math.min(t,s.time),Number.POSITIVE_INFINITY);return Number.isFinite(i)?[{start:i}]:[]}function P(n,e,i){if(n.length===0||i&&!x(i,e))return null;let t=[...n].sort((d,c)=>d.time-c.time),s=t.findIndex(d=>d.time>=e);if(s<=0)return{...t[0],box:{...t[0].box}};if(s<0){let d=t[t.length-1];return{...d,box:{...d.box}}}let r=t[s-1],o=t[s],a=Math.max(Number.EPSILON,o.time-r.time),l=Math.max(0,Math.min(1,(e-r.time)/a));return{time:e,box:be(r.box,o.box,l),score:r.score+(o.score-r.score)*l,source:r.source==="manual"||o.source==="manual"?"manual":"tracked"}}function G(n){return!n.video.paused&&n.liveFrame?{...n.liveFrame,box:{...n.liveFrame.box}}:P(n.presentationKeyframes,n.video.currentTime,n.segments)}function we(n,e){return e.x>=n.left&&e.x<=n.left+n.width&&e.y>=n.top&&e.y<=n.top+n.height}var $=class{constructor(e={}){this.options=e;this.surfaceKind="video";this.records=new Map;this.videoKeys=new Map;this.metadata=new WeakMap;this.analyzedSources=new WeakMap;this.listeners=new Map;this.frameHandles=new Map;this.persistTimers=new Map;this.writeChains=new WeakMap;this.pendingWrites=new Set;this.refreshQueued=!1;this.destroyed=!1;this.manualSequence=0;this.activeTrackingKey=null;this.id=e.id??"see:video",this.readyPromise=this.refresh();let i=e.root??document;this.observer=e.observe!==!1&&typeof MutationObserver<"u"?new MutationObserver(()=>this.queueRefresh()):null,this.observer?.observe(i,{childList:!0,subtree:!0,attributes:!0,attributeFilter:["src"]})}whenReady(){return this.readyPromise}listSubjects(){return Array.from(this.records.keys(),e=>this.resolve(e)).filter(e=>!!e)}async refresh(){if(this.destroyed)return;let e=new Set(this.matchingVideos());for(let i of this.videoKeys.keys())(!e.has(i)||!i.isConnected)&&this.removeVideo(i);await Promise.all(Array.from(e,i=>this.analyze(i)))}hitTest(e){let i=null;for(let t of this.records.keys()){let s=this.resolve(t);if(!s||!we(s.bounds,e))continue;let r=s.bounds.width*s.bounds.height;(!i||r<i.area)&&(i={subject:s,area:r})}return i?.subject??null}resolve(e){let i=this.records.get(e);if(!i?.video.isConnected)return null;let t=G(i);if(!t)return null;let s=q(i.video,t.box);if(!s)return null;let r=i.video.getBoundingClientRect();return{key:e,label:i.label,sourceElement:i.video,videoObject:this.videoObjectContext(i,t),trackingAnchors:i.anchors.map(o=>({...o})),bounds:s,surfaceBounds:{left:r.left,top:r.top,width:r.width,height:r.height},context:`${i.label} tracked in video at ${i.video.currentTime.toFixed(2)}s`,data:{kind:"video-object",label:i.label,origin:i.origin,score:String(Math.round(t.score*1e3)/1e3),videoSrc:v(i.video),currentTime:String(i.video.currentTime)}}}async captureSubject(e){let i=this.records.get(e);if(!i||i.video.readyState<2)return null;let t=G(i);if(!t)return null;let{video:s}=i,r={x:t.box.left*s.videoWidth,y:t.box.top*s.videoHeight,width:t.box.width*s.videoWidth,height:t.box.height*s.videoHeight},o=Math.min(1,1024/Math.max(r.width,r.height)),a=document.createElement("canvas");a.width=Math.max(1,Math.round(r.width*o)),a.height=Math.max(1,Math.round(r.height*o));let l=a.getContext("2d");return l?(l.drawImage(s,r.x,r.y,r.width,r.height,0,0,a.width,a.height),new Promise((d,c)=>{try{a.toBlob(d,"image/webp",.95)}catch(u){c(u)}})):null}beginCreate(e){let i=this.matchingVideos();for(let t=i.length-1;t>=0;t-=1){let s=i[t];if(!s)continue;let r=z(s,e);if(!r)continue;let o=this.videoIdentity(s),a=`manual:${this.newObjectId()}`,l=`${o}:${a}`,d=Date.now(),c={time:s.currentTime,box:{left:r.x,top:r.y,width:0,height:0},score:1,source:"manual"},u=k(this.options.tracking),m={key:l,id:a,video:s,label:"object",origin:"manual",keyframes:[c],segments:[{start:s.currentTime}],anchors:[],presentationKeyframes:[u.smooth(c)],smoother:u,createdAt:d,updatedAt:d,misses:0,hits:0,analyzedThrough:c.time},h=!1,g=!1,p=()=>{if(g)return;g=!0,this.records.set(l,m);let f=this.videoKeys.get(s)??new Set;f.add(l),this.videoKeys.set(s,f)};return{key:l,update:f=>{if(h)return null;p();let b=z(s,f);return b&&(c.box={left:Math.min(r.x,b.x),top:Math.min(r.y,b.y),width:Math.abs(b.x-r.x),height:Math.abs(b.y-r.y)},m.smoother=k(this.options.tracking),m.presentationKeyframes=[m.smoother.smooth(c)]),this.resolve(l)},commit:()=>h?null:(p(),m.backwardSeedTime=c.time,this.activateTrackingRecord(m),this.persistVideo(s),this.startSession(m,!0).then(()=>this.scheduleBackwardAnalysis(m)),this.resolve(l)),cancel:()=>{h||(h=!0,g&&this.removeSubject(l))}}}return null}renameSubject(e,i){let t=this.records.get(e),s=i.trim();return!t||!s?null:(t.label=s,t.updatedAt=Date.now(),this.activateTrackingRecord(t),this.persistVideo(t.video),this.resolve(e))}updateSubjectBounds(e,i){let t=this.records.get(e);if(!t)return null;let s=Q(t.video,i);if(!s)return null;this.openSegment(t,t.video.currentTime),this.insertKeyframe(t,{time:t.video.currentTime,box:s,score:1,source:"manual"}),t.liveFrame=void 0,t.trackingFailed=!1,t.misses=0,t.hits=0,t.lostAt=void 0,t.analyzedThrough=t.video.currentTime;let r=x(t.segments,t.video.currentTime);return r&&Math.abs(r.start-t.video.currentTime)<=.05&&(t.backwardSeedTime=t.video.currentTime,this.cancelBackwardAnalysis(t)),this.activateTrackingRecord(t),this.persistVideo(t.video),this.startSession(t,!0).then(()=>this.reseed(t)).then(()=>this.scheduleBackwardAnalysis(t)),this.resolve(e)}updateSubjectTrackingAnchors(e,i){let t=this.records.get(e);return t?(t.anchors=i.slice(0,3).map(s=>({x:Math.max(0,Math.min(1,s.x)),y:Math.max(0,Math.min(1,s.y))})),t.updatedAt=Date.now(),t.trackingFailed=!1,t.analyzedThrough=t.video.currentTime,t.backwardSeedTime!==void 0&&this.cancelBackwardAnalysis(t),this.activateTrackingRecord(t),this.persistVideo(t.video),this.startSession(t,!0).then(()=>this.reseed(t)).then(()=>this.scheduleBackwardAnalysis(t)),this.resolve(e)):null}removeSubject(e){let i=this.records.get(e);if(!i)return;this.cancelBackwardAnalysis(i),i.session?.dispose?.(),this.activeTrackingKey===e&&(this.activeTrackingKey=null),this.records.delete(e);let t=this.videoKeys.get(i.video);t?.delete(e),t?.size===0&&this.videoKeys.delete(i.video),this.persistVideo(i.video)}async destroy(){if(this.destroyed)return;this.destroyed=!0,this.observer?.disconnect();for(let i of this.listeners.keys())this.removeVideoListeners(i);await Promise.all(Array.from(this.persistTimers.keys(),i=>this.flushScheduledPersistence(i)));let e=Array.from(this.records.values(),i=>i.backwardAnalysis?.promise).filter(i=>!!i);await Promise.allSettled(Array.from(this.records.values(),i=>(this.cancelBackwardAnalysis(i),i.session?.dispose?.()))),await Promise.allSettled(e),this.records.clear(),this.videoKeys.clear(),await Promise.allSettled(this.pendingWrites)}async analyze(e){let i=v(e);if(!(!i||this.analyzedSources.get(e)===i))try{if(await ye(e),this.destroyed||v(e)!==i)return;let t=this.videoIdentity(e),s=new URL(i,e.ownerDocument.baseURI).href,r=`${this.id}:${t}`,o=this.options.store?await this.options.store.load(r):null;if(this.destroyed||v(e)!==i)return;this.metadata.set(e,{storeKey:r,source:s,identity:t});let a=!!e.dataset.popmeltMediaId;o&&(o.source===s||a)?this.replaceVideoRecords(e,t,o.tracks):this.videoKeys.has(e)||this.videoKeys.set(e,new Set),this.analyzedSources.set(e,i),this.addVideoListeners(e)}catch(t){this.options.onError?.(t,e)}}addVideoListeners(e){if(this.listeners.has(e))return;let i=()=>{this.clearLiveFrames(e),this.activateMostRecentRecord(e);let o=this.activeRecordFor(e);if(this.options.tracking&&o&&!o.session&&(o.analyzedThrough===void 0||o.analyzedThrough<e.duration-M)){let a=this.startSession(o,!0);e.pause(),a.then(()=>{!this.destroyed&&o.session&&e.play()});return}o&&this.scheduleBackwardAnalysis(o),this.scheduleFrame(e)},t=()=>{this.clearLiveFrames(e);let o=this.activeRecordFor(e);o&&this.scheduleBackwardAnalysis(o),this.flushScheduledPersistence(e)},s=()=>{let o=this.activeRecordFor(e);o?.lastTrackedMediaTime!==void 0&&(o.analyzedThrough=e.duration,o.lastTrackedMediaTime=void 0,o.session?.dispose?.(),o.session=void 0,this.schedulePersistence(e)),t()},r=()=>{let o=this.activeRecordFor(e);if(this.clearLiveFrames(e),!o)return;let a=o.lastTrackedMediaTime;if(e.loop&&a!==void 0&&a>=e.duration-.25&&e.currentTime+.25<a&&(o.analyzedThrough??0)>=a-.25){o.analyzedThrough=e.duration,o.lastTrackedMediaTime=void 0;let l=o.session;o.session=void 0,Promise.resolve(o.tracking).finally(()=>l?.dispose?.()),this.schedulePersistence(e);return}o.session&&this.reseed(o)};e.addEventListener("play",i),e.addEventListener("pause",t),e.addEventListener("ended",s),e.addEventListener("seeked",r),this.listeners.set(e,()=>{e.removeEventListener("play",i),e.removeEventListener("pause",t),e.removeEventListener("ended",s),e.removeEventListener("seeked",r)}),e.paused||(this.activateMostRecentRecord(e),this.scheduleFrame(e))}removeVideoListeners(e){this.listeners.get(e)?.(),this.listeners.delete(e);let i=e,t=this.frameHandles.get(e);t!==void 0&&(i.cancelVideoFrameCallback?.(t),cancelAnimationFrame(t)),this.frameHandles.delete(e)}scheduleFrame(e){if(this.destroyed||e.paused||this.frameHandles.has(e))return;let i=e,t=(s,r)=>{this.frameHandles.delete(e),this.trackVideo(e,r.mediaTime),this.scheduleFrame(e)};i.requestVideoFrameCallback?this.frameHandles.set(e,i.requestVideoFrameCallback(t)):this.frameHandles.set(e,requestAnimationFrame(()=>t(performance.now(),{mediaTime:e.currentTime})))}async trackVideo(e,i){let t=this.activeRecordFor(e);if(!t||t.tracking||t.trackingFailed||t.analyzedThrough!==void 0&&i<=t.analyzedThrough+M)return;let s=this.options.tracking?.motion;if(!s)return;let r=s.attempt({state:X(t),time:i,maximumFramesPerSecond:Math.max(1,this.options.maxTrackingFramesPerSecond??Y)});t.lastTrackedMediaTime!==void 0&&Math.abs(i-t.lastTrackedMediaTime)<ge/r.framesPerSecond||(t.session||await this.startSession(t),!(!t.session||this.activeTrackingKey!==t.key)&&(t.lastTrackedMediaTime=i,t.tracking=t.session.track(e,i,r.tracking).then(o=>{if(this.destroyed||this.activeTrackingKey!==t.key)return;t.analyzedThrough=Math.max(t.analyzedThrough??i,i);let a=s.observe({state:X(t),time:i,observation:o});ve(t,a.state),o&&a.action==="reopen"&&this.openSegment(t,i),o&&(a.action==="accept"||a.action==="reopen")?this.acceptObservation(t,{...o,source:"tracked"}):a.action==="close"&&(t.liveFrame=void 0,this.closeSegment(t,t.keyframes[t.keyframes.length-1]?.time??i)),this.schedulePersistence(e)}).catch(o=>this.failTracking(t,o)).finally(()=>{t.tracking=void 0}),await t.tracking))}async startSession(e,i=!1){if(e.initializing){await e.initializing;return}if(!this.options.tracking||e.session||e.trackingFailed||this.activeTrackingKey!==e.key||e.video.paused&&!i)return;let t=P(e.keyframes,e.video.currentTime,e.segments);!t||e.video.readyState<2||(e.initializing=this.options.tracking.createSession({video:e.video,time:e.video.currentTime,box:t.box,anchors:e.anchors}).then(s=>{this.destroyed||this.activeTrackingKey!==e.key?s.dispose?.():e.session=s}).catch(s=>this.failTracking(e,s)).finally(()=>{e.initializing=void 0}),await e.initializing)}async reseed(e){let i=P(e.keyframes,e.video.currentTime,e.segments);if(!i){e.session?.dispose?.(),e.session=void 0,e.lostAt=e.video.currentTime,e.liveFrame=void 0,e.misses=0,e.hits=0;return}e.lostAt=void 0,e.misses=0,e.hits=0;try{e.session?.reseed&&await e.session.reseed({video:e.video,time:e.video.currentTime,box:i.box,anchors:e.anchors})}catch(t){this.failTracking(e,t)}}failTracking(e,i){e.trackingFailed=!0,e.liveFrame=void 0,e.session?.dispose?.(),e.session=void 0,this.options.onError?.(i,e.video)}activeRecordFor(e){if(!this.activeTrackingKey)return null;let i=this.records.get(this.activeTrackingKey);return i?.video===e?i:null}activateMostRecentRecord(e){if(this.activeRecordFor(e))return;let t=Array.from(this.videoKeys.get(e)??[]).map(s=>this.records.get(s)).filter(s=>!!s).sort((s,r)=>r.updatedAt-s.updatedAt)[0];t&&this.activateTrackingRecord(t)}activateTrackingRecord(e){if(this.activeTrackingKey===e.key)return;let i=this.activeTrackingKey?this.records.get(this.activeTrackingKey):void 0;this.activeTrackingKey=e.key,i&&(this.cancelBackwardAnalysis(i),i.session?.dispose?.(),i.session=void 0,i.lastTrackedMediaTime=void 0,i.liveFrame=void 0),e.liveFrame=void 0,e.trackingFailed=!1,e.lastTrackedMediaTime=void 0,e.misses=0,e.hits=0,e.lostAt=C(e)?e.video.currentTime:void 0}scheduleBackwardAnalysis(e){if(this.destroyed||!this.options.tracking||e.backwardSeedTime===void 0||e.backwardSeedTime<=M||e.backwardAnalysis||e.initializing||this.activeTrackingKey!==e.key)return;let i={cancelled:!1,promise:Promise.resolve()};e.backwardAnalysis=i,i.promise=this.runBackwardAnalysis(e,i).catch(t=>{i.cancelled||this.options.onError?.(t,e.video)}).finally(()=>{e.backwardAnalysis===i&&(e.backwardAnalysis=void 0,!i.cancelled&&e.backwardSeedTime!==void 0&&(e.backwardSeedTime=void 0),i.cancelled&&!this.destroyed&&this.activeTrackingKey===e.key&&queueMicrotask(()=>this.scheduleBackwardAnalysis(e)))})}cancelBackwardAnalysis(e){let i=e.backwardAnalysis;i&&(i.cancelled=!0)}async runBackwardAnalysis(e,i){let t=e.backwardSeedTime;if(t===void 0)return;let s=await this.options.tracking.motion.analyzeBackward({source:e.video,seedTime:t,keyframes:e.keyframes,anchors:e.anchors,maximumFramesPerSecond:this.options.maxTrackingFramesPerSecond??Y,cancelled:()=>i.cancelled||this.destroyed,forwardWork:()=>e.initializing??e.tracking});if(i.cancelled||this.destroyed)return;let r=x(e.segments,t);r&&(e.keyframes=s.keyframes,r.start=Math.min(r.start,s.earliestTime),e.updatedAt=Date.now(),this.rebuildPresentationTrack(e),this.schedulePersistence(e.video))}acceptObservation(e,i){let t=this.insertKeyframe(e,i);return e.video.paused||(e.liveFrame={...t,box:{...t.box}}),!0}openSegment(e,i){if(x(e.segments,i))return!1;let t=e.segments[e.segments.length-1];return t&&t.end===void 0&&i>=t.start?!1:(e.segments.push({start:i}),e.segments.sort((s,r)=>s.start-r.start),e.smoother=k(this.options.tracking),!0)}closeSegment(e,i){let t=[...e.segments].reverse().find(s=>s.end===void 0);return t?(t.end=Math.max(t.start,i),e.updatedAt=Date.now(),!0):!1}insertKeyframe(e,i){let t={...i,box:{...i.box}},s=e.keyframes.findIndex(o=>Math.abs(o.time-i.time)<.001),r=!1;if(s>=0)e.keyframes[s]=t;else{let o=e.keyframes[e.keyframes.length-1];if(i.source==="tracked"&&o&&Math.abs(i.time-o.time)<pe)e.keyframes[e.keyframes.length-1]=t;else if(!o||i.time>o.time){e.keyframes.push(t);let a=e.smoother.smooth(t);e.presentationKeyframes.push(a),r=!0}else e.keyframes.push(t)}return r||this.rebuildPresentationTrack(e),e.updatedAt=Date.now(),P(e.presentationKeyframes,i.time)??{...t,box:{...t.box}}}rebuildPresentationTrack(e){e.keyframes.sort((s,r)=>s.time-r.time);let i=k(this.options.tracking),t=null;e.presentationKeyframes=e.keyframes.map(s=>{let r=x(e.segments,s.time);return r&&r!==t&&(i=k(this.options.tracking),t=r),i.smooth(s)}),e.smoother=i}clearLiveFrames(e){for(let i of this.videoKeys.get(e)??[]){let t=this.records.get(i);t&&(t.liveFrame=void 0)}}videoObjectContext(e,i){let{video:t}=e,s=e.keyframes.map(r=>r.time);return{src:v(t),width:t.videoWidth,height:t.videoHeight,duration:t.duration,currentTime:t.currentTime,normalizedBox:{x:i.box.left,y:i.box.top,width:i.box.width,height:i.box.height},box:{x:i.box.left*t.videoWidth,y:i.box.top*t.videoHeight,width:i.box.width*t.videoWidth,height:i.box.height*t.videoHeight},trackStart:Math.min(...s),trackEnd:Math.max(...s)}}replaceVideoRecords(e,i,t){this.removeVideoRecords(e);let s=new Set;for(let r of t){let o=`${i}:${r.id}`,a=r.keyframes.map(m=>({...m,box:{...m.box}})),l=Se(a,r.segments),d=a[0],c=k(this.options.tracking);s.add(o);let u={key:o,id:r.id,video:e,label:r.label,origin:r.origin,keyframes:a,segments:l,anchors:(r.anchors??[]).map(m=>({...m})),presentationKeyframes:[],smoother:c,createdAt:r.createdAt,updatedAt:r.updatedAt,misses:0,hits:0,analyzedThrough:r.analyzedThrough,backwardSeedTime:d?.source==="manual"&&d.time>M&&(l[0]?.start??-1)>=d.time-M?d.time:void 0};C(u)&&(u.lostAt=e.currentTime),this.rebuildPresentationTrack(u),r.presentationKeyframes?.length&&(u.presentationKeyframes=r.presentationKeyframes.map(m=>({...m,box:{...m.box}}))),this.records.set(o,u)}this.videoKeys.set(e,s)}persistVideo(e){let i=this.persistTimers.get(e);i!==void 0&&clearTimeout(i),this.persistTimers.delete(e);let t=this.options.store,s=this.metadata.get(e);if(!t||!s)return Promise.resolve();let r=Array.from(this.videoKeys.get(e)??[]).map(d=>this.records.get(d)).filter(d=>!!d).map(d=>({id:d.id,label:d.label,origin:d.origin,keyframes:d.keyframes.map(c=>({...c,box:{...c.box}})),presentationKeyframes:d.presentationKeyframes.map(c=>({...c,box:{...c.box}})),anchors:d.anchors.map(c=>({...c})),segments:d.segments.map(c=>({...c})),analyzedThrough:d.analyzedThrough,createdAt:d.createdAt,updatedAt:d.updatedAt})),o={schemaVersion:3,source:s.source,duration:Number.isFinite(e.duration)?e.duration:0,tracks:r,updatedAt:Date.now()},l=(this.writeChains.get(e)??Promise.resolve()).catch(()=>{}).then(()=>t.save(s.storeKey,o)).catch(d=>this.options.onError?.(d,e));return this.writeChains.set(e,l),this.pendingWrites.add(l),l.finally(()=>this.pendingWrites.delete(l)),l}schedulePersistence(e){!this.options.store||this.persistTimers.has(e)||this.persistTimers.set(e,setTimeout(()=>{this.persistTimers.delete(e),this.persistVideo(e)},fe))}flushScheduledPersistence(e){return this.persistTimers.has(e)?this.persistVideo(e):Promise.resolve()}matchingVideos(){let e=this.options.root??document,i=this.options.selector??he,t=[];e instanceof HTMLVideoElement&&e.matches(i)&&t.push(e);for(let s of e.querySelectorAll(i))s instanceof HTMLVideoElement&&t.push(s);return t}videoIdentity(e){if(e.dataset.popmeltMediaId)return e.dataset.popmeltMediaId;let i=new URL(v(e),e.ownerDocument.baseURI).href,t=this.matchingVideos().filter(s=>v(s)===v(e));return`${J(i)}:${Math.max(0,t.indexOf(e))}`}newObjectId(){return globalThis.crypto?.randomUUID?.()??J(`${Date.now()}:${this.manualSequence++}:${Math.random()}`)}removeVideoRecords(e){for(let i of this.videoKeys.get(e)??[]){let t=this.records.get(i);t&&this.cancelBackwardAnalysis(t),t?.session?.dispose?.(),this.activeTrackingKey===i&&(this.activeTrackingKey=null),this.records.delete(i)}this.videoKeys.delete(e)}removeVideo(e){this.removeVideoListeners(e),this.flushScheduledPersistence(e),this.removeVideoRecords(e),this.metadata.delete(e),this.analyzedSources.delete(e)}queueRefresh(){this.refreshQueued||this.destroyed||(this.refreshQueued=!0,queueMicrotask(()=>{this.refreshQueued=!1,this.readyPromise=this.refresh()}))}};function Re(n={}){return new $(n)}var Te="popmelt:media:video:v1";function Z(n){if(!n||typeof n!="object")return!1;let e=n;return["left","top","width","height"].every(i=>typeof e[i]=="number"&&Number.isFinite(e[i]))}function ke(n){if(!n||typeof n!="object")return!1;let e=n;return typeof e.start=="number"&&Number.isFinite(e.start)&&(e.end===void 0||typeof e.end=="number"&&Number.isFinite(e.end)&&e.end>=e.start)}function xe(n){if(!n||typeof n!="object")return!1;let e=n;return(e.schemaVersion===1||e.schemaVersion===2||e.schemaVersion===3)&&typeof e.source=="string"&&typeof e.duration=="number"&&typeof e.updatedAt=="number"&&Array.isArray(e.tracks)&&e.tracks.every(i=>{if(!i||typeof i!="object")return!1;let t=i;return typeof t.id=="string"&&typeof t.label=="string"&&(t.origin==="manual"||t.origin==="detected")&&typeof t.createdAt=="number"&&typeof t.updatedAt=="number"&&(t.analyzedThrough===void 0||typeof t.analyzedThrough=="number"&&Number.isFinite(t.analyzedThrough))&&(t.segments===void 0||Array.isArray(t.segments)&&t.segments.every(ke))&&Array.isArray(t.keyframes)&&t.keyframes.every(s=>{if(!s||typeof s!="object")return!1;let r=s;return typeof r.time=="number"&&typeof r.score=="number"&&(r.source==="manual"||r.source==="tracked")&&Z(r.box)})&&(t.presentationKeyframes===void 0||Array.isArray(t.presentationKeyframes)&&t.presentationKeyframes.every(s=>{if(!s||typeof s!="object")return!1;let r=s;return typeof r.time=="number"&&typeof r.score=="number"&&(r.source==="manual"||r.source==="tracked")&&Z(r.box)}))})}function Oe(n={}){let e=n.namespace??Te,i=()=>n.storage??window.localStorage,t=s=>`${e}:${s}`;return{load(s){try{let r=i().getItem(t(s));if(!r)return null;let o=JSON.parse(r);return xe(o)?o:null}catch{return null}},save(s,r){i().setItem(t(s),JSON.stringify(r))},remove(s){i().removeItem(t(s))}}}function Fe(){let n=new Map;return{load:e=>n.get(e)??null,save:(e,i)=>{n.set(e,i)},remove:e=>{n.delete(e)}}}export{E as a,N as b,H as c,Ie as d,je as e,O as f,Le as g,z as h,q as i,Q as j,$ as k,Re as l,Oe as m,Fe as n};
|