@popmelt.com/core 0.14.1 → 0.15.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 CHANGED
@@ -50,13 +50,13 @@ Double-click visible text to edit it directly. Popmelt records the text change s
50
50
 
51
51
  Steer previews code-native design changes directly on the page. Point to an element, choose a family, narrow to a property, and choose a value. Click the target or press Enter at the value level to stage the preview.
52
52
 
53
- | Keys | Family |
54
- | --- | --- |
55
- | `P` / `M` | Padding / margin |
56
- | `B` / `R` | Borders / radii |
57
- | `F` / `G` | Flexbox / grid |
58
- | `FO` / `BG` | Type / background color |
59
- | `T` / `O` | Transform / opacity and blend mode |
53
+ | Keys | Family |
54
+ | ----------- | ---------------------------------- |
55
+ | `P` / `M` | Padding / margin |
56
+ | `B` / `R` | Borders / radii |
57
+ | `F` / `G` | Flexbox / grid |
58
+ | `FO` / `BG` | Type / background color |
59
+ | `T` / `O` | Transform / opacity and blend mode |
60
60
 
61
61
  The omnidial can be driven entirely from the keyboard: `Q` and `E` move between properties, `A` and `D` or the arrow keys move between values, `W` or Enter goes deeper or applies, and `S` or Escape goes back. Press Space to search families, properties, colors, and values by name.
62
62
 
@@ -79,15 +79,18 @@ Archiving hides a thread from the active canvas without deleting its stored conv
79
79
 
80
80
  ### Pointer gestures
81
81
 
82
- | Gesture | Action |
83
- | --- | --- |
84
- | Shake the pointer quickly | Switch between Chat and Steer |
85
- | Hold Command/Control while pointing | Pass through the default target to the deepest element under the pointer |
86
- | Double-click text in Chat | Edit the text directly |
87
- | Click the toolbar annotation counter | Cycle through annotation bundles, including other routes |
88
- | Scroll the annotation counter | Change the active annotation color |
89
- | Hold the annotation counter | Reset the active color |
90
- | Right-click Popmelt's canvas, then right-click again | Pass through to the page's native context menu |
82
+ | Gesture | Action |
83
+ | -------------------------------------------------- | ------------------------------------------------------------------------ |
84
+ | Shake the pointer quickly | Switch between Chat and Steer |
85
+ | Hold Command/Control while pointing | Pass through the default target to the deepest element under the pointer |
86
+ | Double-click text in Chat | Edit the text directly |
87
+ | Click the toolbar annotation counter | Cycle through annotation bundles, including other routes |
88
+ | Scroll the annotation counter | Change the active annotation color |
89
+ | Hold the annotation counter | Reset the active color |
90
+ | Right-click Popmelt's canvas | Enter persistent page passthrough while keeping Popmelt overlays visible |
91
+ | Drag, scroll, pinch, or right-click in passthrough | Interact with the page and open its native context menu |
92
+ | Click a valid target in passthrough | Apply the active Comment or Steer action without leaving passthrough |
93
+ | Press Escape or choose another tool | Exit passthrough |
91
94
 
92
95
  ## Ambient Popmelt in Codex
93
96
 
@@ -243,19 +246,19 @@ Popmelt is designed for local development. As with any AI-assisted coding, your
243
246
 
244
247
  ## Useful shortcuts
245
248
 
246
- | Shortcut | Action |
247
- | --- | --- |
248
- | Double-tap Command or Control | Open or collapse Popmelt |
249
- | `C` | Enter Chat mode |
250
- | `S` | Enter Steer mode |
251
- | `I` | Enter Imprint mode |
252
- | Command/Control+Enter | Send all pending work |
253
- | Command/Control+C | Capture a screenshot for pending work when no text is selected |
254
- | Command/Control+Z | Undo a pending change |
255
- | Command/Control+Shift+Z | Redo a pending change |
256
- | Backspace/Delete | Archive the open or selected historical thread |
257
- | Command/Control+Backspace/Delete | Clear all annotations and pending changes |
258
- | Escape | Go back, cancel, close, discard, or deselect in the current context |
249
+ | Shortcut | Action |
250
+ | -------------------------------- | ------------------------------------------------------------------- |
251
+ | Double-tap Command or Control | Open or collapse Popmelt |
252
+ | `C` | Enter Chat mode |
253
+ | `S` | Enter Steer mode |
254
+ | `I` | Enter Imprint mode |
255
+ | Command/Control+Enter | Send all pending work |
256
+ | Command/Control+C | Capture a screenshot for pending work when no text is selected |
257
+ | Command/Control+Z | Undo a pending change |
258
+ | Command/Control+Shift+Z | Redo a pending change |
259
+ | Backspace/Delete | Archive the open or selected historical thread |
260
+ | Command/Control+Backspace/Delete | Clear all annotations and pending changes |
261
+ | Escape | Go back, cancel, close, discard, or deselect in the current context |
259
262
 
260
263
  Steer additionally owns the unmodified letter, arrow, Enter, Escape, and Space keys described above while it is active. Popmelt leaves these shortcuts alone while you are typing in an input, textarea, or editable text surface.
261
264
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "popmelt-core",
3
- "version": "0.14.1",
3
+ "version": "0.15.0",
4
4
  "description": "Ambient project memory and shared understanding for Codex",
5
5
  "author": {
6
6
  "name": "Popmelt",
@@ -0,0 +1,66 @@
1
+ import { Camera, Object3D, Raycaster, WebGLRenderer } from 'three';
2
+
3
+ type PopmeltSubjectBounds = {
4
+ left: number;
5
+ top: number;
6
+ width: number;
7
+ height: number;
8
+ };
9
+ type PopmeltSubject = {
10
+ key: string;
11
+ label: string;
12
+ bounds: PopmeltSubjectBounds;
13
+ context?: string;
14
+ data?: Record<string, string>;
15
+ };
16
+ type PopmeltSurfaceSnapshot = {
17
+ element: HTMLCanvasElement;
18
+ dataUrl: string;
19
+ };
20
+ /**
21
+ * Minimal renderer boundary for non-DOM subjects. The adapter owns hit testing,
22
+ * durable keys, and viewport projection; Popmelt owns annotations and threads.
23
+ */
24
+ type PopmeltSubjectAdapter = {
25
+ id: string;
26
+ hitTest(clientPoint: {
27
+ x: number;
28
+ y: number;
29
+ }): PopmeltSubject | null;
30
+ resolve(key: string): PopmeltSubject | null;
31
+ /** Optional bitmap capture for renderer surfaces that DOM cloning cannot read. */
32
+ captureSurfaces?(): readonly PopmeltSurfaceSnapshot[] | Promise<readonly PopmeltSurfaceSnapshot[]>;
33
+ };
34
+
35
+ type ThreeSubjectAdapterOptions = {
36
+ canvas: HTMLCanvasElement;
37
+ camera: Camera;
38
+ scene: Object3D;
39
+ raycaster?: Raycaster;
40
+ /** Select a meaningful parent from the raw intersected render object. */
41
+ select?: (object: Object3D) => Object3D | null;
42
+ /** Override durable identity. Explicit application IDs are preferred. */
43
+ key?: (object: Object3D) => string | null | undefined;
44
+ label?: (object: Object3D) => string;
45
+ /** Add application-specific provenance or source hints to the model context. */
46
+ describe?: (object: Object3D) => Record<string, string | undefined>;
47
+ /** Capture the renderer's current frame for DOM-cloned screenshots. */
48
+ captureFrame?: () => string | null | Promise<string | null>;
49
+ };
50
+ /**
51
+ * Read the renderer's visible frame without requiring preserveDrawingBuffer.
52
+ * Rendering and reading synchronously preserves the application's real output
53
+ * pipeline while the previous render target and XR state are restored afterward.
54
+ */
55
+ declare function captureThreeRendererFrame(renderer: WebGLRenderer, scene: Object3D, camera: Camera): string | null;
56
+ /** Stable while the scene's semantic hierarchy remains stable across reloads. */
57
+ declare function threeObjectPath(object: Object3D): string;
58
+ /**
59
+ * Collapses glTF material primitives such as `Object649_normal_0` onto their
60
+ * named parent `Object649`. Apps opt into this policy through `select` so raw
61
+ * mesh targeting remains available when material-level annotation is useful.
62
+ */
63
+ declare function selectThreeNamedPrimitiveParent(object: Object3D): Object3D;
64
+ declare function createThreeSubjectAdapter(options: ThreeSubjectAdapterOptions): PopmeltSubjectAdapter;
65
+
66
+ export { type ThreeSubjectAdapterOptions, captureThreeRendererFrame, createThreeSubjectAdapter, selectThreeNamedPrimitiveParent, threeObjectPath };
@@ -0,0 +1 @@
1
+ import{Box3 as y,Raycaster as x,Vector2 as O,Vector3 as j}from"three";function b(e){return[e.x,e.y,e.z].map(n=>Number(n.toFixed(4))).join(", ")}function h(e){let n=e.material;return n&&(Array.isArray(n)?n:[n]).map(r=>r.name?.trim()||r.type).filter(Boolean).join(", ")||void 0}function M(e,n,c){let s=e.domElement.width,r=e.domElement.height;if(s<=0||r<=0)return null;let l=e.getRenderTarget(),t=e.xr.enabled;try{return e.xr.enabled=!1,e.setRenderTarget(null),e.render(n,c),e.domElement.toDataURL("image/png")}catch{return null}finally{e.setRenderTarget(l),e.xr.enabled=t}}function D(e){let s=(e.parent?.children??[]).filter(t=>t.type===e.type&&t.name===e.name),r=Math.max(0,s.indexOf(e)),l=e.name.trim()||e.type;return`${encodeURIComponent(l)}[${r}]`}function f(e){let n=[],c=e;for(;c?.parent;)n.push(D(c)),c=c.parent;return n.reverse().join("/")}function v(e){let n=e.userData.popmeltId;return typeof n=="string"&&n.trim()?n.trim():f(e)}function T(e){let n=e.userData.popmeltLabel;return typeof n=="string"&&n.trim()?n.trim():e.name.trim()||e.type}function R(e){let n=e;for(;n.parent;){let c=n.name.trim(),s=c.match(/^(.*)_(?:alpha|normal|material)(?:_\d+)+$/i),r=n.parent;for(;r.parent&&!r.name.trim();)r=r.parent;let l=r.name.trim();if(!l||!c.startsWith(`${l}_`)&&!s)break;n=r}return n}function P(e,n,c){let s=new y().setFromObject(e,!0);if(s.isEmpty())return null;let r=c.getBoundingClientRect(),l=[];for(let p of[s.min.x,s.max.x])for(let d of[s.min.y,s.max.y])for(let g of[s.min.z,s.max.z])l.push(new j(p,d,g).project(n));let t=l.map(p=>r.left+(p.x+1)/2*r.width),a=l.map(p=>r.top+(1-p.y)/2*r.height),i=Math.max(r.left,Math.min(...t)),o=Math.min(r.right,Math.max(...t)),m=Math.max(r.top,Math.min(...a)),u=Math.min(r.bottom,Math.max(...a));return o<=i||u<=m?null:{left:i,top:m,width:o-i,height:u-m}}function C(e){let n=e.raycaster??new x,c=new O,s=t=>e.key?.(t)??v(t),r=(t,a)=>{let i=s(t);if(!i)return null;let o=P(t,e.camera,e.canvas);return o?{key:i,label:e.label?.(t)??T(t),bounds:o,context:f(t),data:{renderer:"Three.js",objectType:t.type,...t.name?{objectName:t.name}:{},scenePath:f(t),...a&&a!==t?{renderObjectName:a.name.trim()||a.type,renderObjectType:a.type,renderScenePath:f(a)}:{},position:b(t.position),rotation:b(t.rotation),scale:b(t.scale),visible:String(t.visible),...h(t)?{material:h(t)}:{},...Object.fromEntries(Object.entries(e.describe?.(t)??{}).filter(m=>typeof m[1]=="string"&&m[1].length>0))}}:null},l=t=>{let a=null;return e.scene.traverse(i=>{!a&&s(i)===t&&(a=i)}),a};return{id:"three",hitTest({x:t,y:a}){let i=e.canvas.getBoundingClientRect();if(t<i.left||t>i.right||a<i.top||a>i.bottom)return null;c.set((t-i.left)/i.width*2-1,-((a-i.top)/i.height)*2+1),n.setFromCamera(c,e.camera);for(let o of n.intersectObject(e.scene,!0)){let m=e.select?e.select(o.object):R(o.object);if(!m||!m.visible)continue;let u=r(m,o.object);if(u)return u}return null},resolve(t){let a=l(t);return a?r(a):null},...e.captureFrame?{async captureSurfaces(){let t=await e.captureFrame?.();return t?[{element:e.canvas,dataUrl:t}]:[]}}:{}}}export{M as captureThreeRendererFrame,C as createThreeSubjectAdapter,R as selectThreeNamedPrimitiveParent,f as threeObjectPath};