@agent-native/core 0.85.5 → 0.85.7

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.
Files changed (86) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +12 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/client/AssistantChat.tsx +1 -1
  5. package/corpus/core/src/client/use-pinch-zoom.ts +76 -11
  6. package/corpus/core/src/collab/presence.ts +63 -3
  7. package/corpus/templates/clips/desktop/src/lib/recorder.ts +245 -77
  8. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +190 -13
  9. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +16 -4
  10. package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +46 -3
  11. package/corpus/templates/design/AGENTS.md +40 -4
  12. package/corpus/templates/design/actions/apply-component-prop-edit.ts +20 -73
  13. package/corpus/templates/design/actions/apply-motion-edit.ts +29 -2
  14. package/corpus/templates/design/actions/apply-visual-edit.ts +4 -2
  15. package/corpus/templates/design/actions/connect-localhost.ts +25 -18
  16. package/corpus/templates/design/actions/edit-design.ts +5 -3
  17. package/corpus/templates/design/actions/export-pdf.ts +8 -1
  18. package/corpus/templates/design/actions/export-zip.ts +10 -1
  19. package/corpus/templates/design/actions/get-motion-timeline.ts +26 -20
  20. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +10 -0
  21. package/corpus/templates/design/actions/insert-asset.ts +190 -7
  22. package/corpus/templates/design/actions/open-visual-edit.ts +27 -5
  23. package/corpus/templates/design/actions/present-design-variants.ts +12 -8
  24. package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +18 -14
  25. package/corpus/templates/design/actions/run-design-audit.ts +7 -4
  26. package/corpus/templates/design/actions/write-local-file.ts +47 -22
  27. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +221 -66
  28. package/corpus/templates/design/app/components/design/EditPanel.tsx +650 -237
  29. package/corpus/templates/design/app/components/design/LayersPanel.tsx +526 -127
  30. package/corpus/templates/design/app/components/design/MotionDock.tsx +234 -46
  31. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1520 -410
  32. package/corpus/templates/design/app/components/design/StatesPanel.tsx +25 -2
  33. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +640 -72
  34. package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +605 -0
  35. package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +50 -26
  36. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +16 -4
  37. package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +90 -103
  38. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +111 -5
  39. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +13 -2
  40. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +42 -2
  41. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +11 -2
  42. package/corpus/templates/design/app/components/design/types.ts +16 -0
  43. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +24 -6
  44. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +15 -1
  45. package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
  46. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +77 -0
  47. package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
  48. package/corpus/templates/design/app/i18n-data.ts +18 -0
  49. package/corpus/templates/design/app/lib/design-import.ts +95 -0
  50. package/corpus/templates/design/app/pages/DesignEditor.tsx +4015 -840
  51. package/corpus/templates/design/changelog/2026-07-02-creating-two-frames-in-a-row-no-longer-freezes-and-reloads-t.md +6 -0
  52. package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +6 -0
  53. package/corpus/templates/design/changelog/2026-07-03-design-retries-stalled-screen-edits-with-a-smaller-safer-fol.md +6 -0
  54. package/corpus/templates/design/changelog/2026-07-03-inspector-fixes-mixed-selections-show-mixed-instead-of-wrong.md +6 -0
  55. package/corpus/templates/design/changelog/2026-07-03-keyframe-timeline-works-reliably-drag-keyframes-smoothly-pic.md +6 -0
  56. package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +6 -0
  57. package/corpus/templates/design/changelog/2026-07-03-much-faster-editor-smooth-dragging-zooming-and-panel-updates.md +6 -0
  58. package/corpus/templates/design/changelog/2026-07-03-pen-tool-refinements-click-the-first-point-to-close-with-a-d.md +6 -0
  59. package/corpus/templates/design/changelog/2026-07-03-text-editing-enter-adds-a-line-break-international-ime-typin.md +6 -0
  60. package/corpus/templates/design/changelog/2026-07-03-undo-and-redo-are-dependable-across-agent-edits-screen-switc.md +6 -0
  61. package/corpus/templates/design/server/lib/design-export.ts +43 -1
  62. package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
  63. package/corpus/templates/design/shared/board-file.ts +25 -5
  64. package/corpus/templates/design/shared/canvas-math.ts +754 -9
  65. package/corpus/templates/design/shared/code-layer.ts +304 -26
  66. package/corpus/templates/design/shared/color-utils.ts +84 -0
  67. package/corpus/templates/design/shared/design-source-capabilities.ts +16 -7
  68. package/corpus/templates/design/shared/motion-compiler.ts +75 -31
  69. package/corpus/templates/design/shared/motion-timeline.ts +335 -0
  70. package/corpus/templates/design/shared/pen-path.ts +200 -23
  71. package/dist/client/AssistantChat.js +1 -1
  72. package/dist/client/AssistantChat.js.map +1 -1
  73. package/dist/client/use-pinch-zoom.d.ts.map +1 -1
  74. package/dist/client/use-pinch-zoom.js +76 -11
  75. package/dist/client/use-pinch-zoom.js.map +1 -1
  76. package/dist/collab/awareness.d.ts +2 -2
  77. package/dist/collab/awareness.d.ts.map +1 -1
  78. package/dist/collab/presence.d.ts.map +1 -1
  79. package/dist/collab/presence.js +54 -3
  80. package/dist/collab/presence.js.map +1 -1
  81. package/dist/collab/routes.d.ts +1 -1
  82. package/dist/collab/struct-routes.d.ts +1 -1
  83. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  84. package/dist/observability/routes.d.ts +2 -2
  85. package/dist/secrets/routes.d.ts +3 -3
  86. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"use-pinch-zoom.d.ts","sourceRoot":"","sources":["../../src/client/use-pinch-zoom.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC;oCACgC;IAChC,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAClD,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;kDAI8C;IAC9C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,EAC3B,YAAY,EACZ,IAAI,EACJ,OAAO,EACP,GAAQ,EACR,GAAS,EACT,YAAmB,EACnB,OAAc,GACf,EAAE,mBAAmB,QA4FrB"}
1
+ {"version":3,"file":"use-pinch-zoom.d.ts","sourceRoot":"","sources":["../../src/client/use-pinch-zoom.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC;oCACgC;IAChC,YAAY,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAClD,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,mEAAmE;IACnE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;kDAI8C;IAC9C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,uDAAuD;IACvD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,EAC3B,YAAY,EACZ,IAAI,EACJ,OAAO,EACP,GAAQ,EACR,GAAS,EACT,YAAmB,EACnB,OAAc,GACf,EAAE,mBAAmB,QA6JrB"}
@@ -23,32 +23,90 @@ export function usePinchZoom({ containerRef, zoom, setZoom, min = 25, max = 400,
23
23
  if (!container)
24
24
  return;
25
25
  const clamp = (n) => Math.max(min, Math.min(max, n));
26
+ // rAF coalescing: multiple wheel/pointermove events can fire per frame
27
+ // (trackpad pinch and touch pinch both deliver many events between
28
+ // paints). Instead of calling setZoom() synchronously per event — which
29
+ // schedules a React re-render per event — stash the latest pending zoom
30
+ // (and its cursor-anchored scroll delta) in a ref and flush once per
31
+ // animation frame with the last-wins value. This preserves the exact
32
+ // zoom-to-cursor math; it just applies it at most once per frame.
33
+ //
34
+ // Within a burst the DOM's real scrollLeft/scrollTop do NOT move until
35
+ // flush() runs, so per-event math must not read them directly — every
36
+ // event after the first in the same frame would anchor against the
37
+ // pre-burst scroll position instead of where the (not-yet-committed)
38
+ // previous events in the burst would have scrolled to. Track a simulated
39
+ // running scroll position (`simScrollLeft`/`simScrollTop`, seeded from the
40
+ // real scroll position when a new burst starts) and use that as the
41
+ // anchor base, so each event's math composes exactly as if the prior
42
+ // events in the burst had already been applied — matching the
43
+ // pre-coalescing, one-setZoom-per-event behavior.
44
+ let pendingZoom = null;
45
+ let pendingScrollDelta = null;
46
+ let simScrollLeft = 0;
47
+ let simScrollTop = 0;
48
+ let rafId = null;
49
+ const flush = () => {
50
+ rafId = null;
51
+ if (pendingZoom === null)
52
+ return;
53
+ const nextZoom = pendingZoom;
54
+ const scrollDelta = pendingScrollDelta;
55
+ pendingZoom = null;
56
+ pendingScrollDelta = null;
57
+ setZoomRef.current(nextZoom);
58
+ if (scrollDelta) {
59
+ container.scrollLeft += scrollDelta.dx;
60
+ container.scrollTop += scrollDelta.dy;
61
+ }
62
+ };
63
+ const scheduleFlush = () => {
64
+ if (rafId !== null)
65
+ return;
66
+ rafId = requestAnimationFrame(flush);
67
+ };
26
68
  const handleWheel = (e) => {
27
69
  if (!(e.ctrlKey || e.metaKey))
28
70
  return;
29
71
  e.preventDefault();
30
- const currentZoom = zoomRef.current;
72
+ // Use the latest not-yet-applied zoom (if a flush is pending) so rapid
73
+ // wheel events within the same frame compound correctly instead of
74
+ // each computing off the last-committed React state.
75
+ const currentZoom = pendingZoom ?? zoomRef.current;
31
76
  const clampedDelta = Math.max(-50, Math.min(50, e.deltaY));
32
77
  const factor = Math.exp(-clampedDelta * 0.01);
33
78
  const nextZoom = clamp(currentZoom * factor);
34
79
  if (nextZoom === currentZoom)
35
80
  return;
36
81
  if (zoomToCursor) {
82
+ // Starting a new burst (nothing pending yet): seed the simulated
83
+ // scroll position from the container's real, currently-committed
84
+ // scroll offset.
85
+ if (pendingScrollDelta === null) {
86
+ simScrollLeft = container.scrollLeft;
87
+ simScrollTop = container.scrollTop;
88
+ }
37
89
  const rect = container.getBoundingClientRect();
38
- const cx = e.clientX - rect.left + container.scrollLeft;
39
- const cy = e.clientY - rect.top + container.scrollTop;
90
+ const cx = e.clientX - rect.left + simScrollLeft;
91
+ const cy = e.clientY - rect.top + simScrollTop;
40
92
  const ratio = nextZoom / currentZoom;
41
93
  const dx = cx * (ratio - 1);
42
94
  const dy = cy * (ratio - 1);
43
- setZoomRef.current(nextZoom);
44
- requestAnimationFrame(() => {
45
- container.scrollLeft += dx;
46
- container.scrollTop += dy;
47
- });
95
+ // Advance the simulated scroll position so the next event in this
96
+ // same burst anchors against where this event would have left it.
97
+ simScrollLeft += dx;
98
+ simScrollTop += dy;
99
+ pendingZoom = nextZoom;
100
+ const prevDelta = pendingScrollDelta;
101
+ pendingScrollDelta = {
102
+ dx: (prevDelta?.dx ?? 0) + dx,
103
+ dy: (prevDelta?.dy ?? 0) + dy,
104
+ };
48
105
  }
49
106
  else {
50
- setZoomRef.current(nextZoom);
107
+ pendingZoom = nextZoom;
51
108
  }
109
+ scheduleFlush();
52
110
  };
53
111
  const activePointers = new Map();
54
112
  let initialDistance = 0;
@@ -73,8 +131,11 @@ export function usePinchZoom({ containerRef, zoom, setZoom, min = 25, max = 400,
73
131
  const [p1, p2] = Array.from(activePointers.values());
74
132
  const distance = Math.hypot(p2.x - p1.x, p2.y - p1.y);
75
133
  const nextZoom = clamp(initialZoom * (distance / initialDistance));
76
- if (nextZoom !== zoomRef.current) {
77
- setZoomRef.current(nextZoom);
134
+ if (nextZoom !== (pendingZoom ?? zoomRef.current)) {
135
+ // Touch pinch has no cursor-anchoring math, so last-wins is simply
136
+ // the newest zoom value — no scroll delta to accumulate.
137
+ pendingZoom = nextZoom;
138
+ scheduleFlush();
78
139
  }
79
140
  e.preventDefault();
80
141
  }
@@ -99,6 +160,10 @@ export function usePinchZoom({ containerRef, zoom, setZoom, min = 25, max = 400,
99
160
  container.removeEventListener("pointermove", handlePointerMove);
100
161
  container.removeEventListener("pointerup", handlePointerEnd);
101
162
  container.removeEventListener("pointercancel", handlePointerEnd);
163
+ if (rafId !== null)
164
+ cancelAnimationFrame(rafId);
165
+ pendingZoom = null;
166
+ pendingScrollDelta = null;
102
167
  };
103
168
  }, [containerRef, enabled, min, max, zoomToCursor]);
104
169
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-pinch-zoom.js","sourceRoot":"","sources":["../../src/client/use-pinch-zoom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAwB1C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAAC,EAC3B,YAAY,EACZ,IAAI,EACJ,OAAO,EACP,GAAG,GAAG,EAAE,EACR,GAAG,GAAG,GAAG,EACT,YAAY,GAAG,IAAI,EACnB,OAAO,GAAG,IAAI,GACM;IACpB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IACvB,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC;QACvC,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;QAE7D,MAAM,WAAW,GAAG,CAAC,CAAa,EAAE,EAAE;YACpC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC;gBAAE,OAAO;YACtC,CAAC,CAAC,cAAc,EAAE,CAAC;YAEnB,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;YACpC,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;YAC3D,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;YAC9C,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC,CAAC;YAE7C,IAAI,QAAQ,KAAK,WAAW;gBAAE,OAAO;YAErC,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,IAAI,GAAG,SAAS,CAAC,qBAAqB,EAAE,CAAC;gBAC/C,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC,UAAU,CAAC;gBACxD,MAAM,EAAE,GAAG,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC;gBACtD,MAAM,KAAK,GAAG,QAAQ,GAAG,WAAW,CAAC;gBACrC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;gBAC5B,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;gBAC5B,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAC7B,qBAAqB,CAAC,GAAG,EAAE;oBACzB,SAAS,CAAC,UAAU,IAAI,EAAE,CAAC;oBAC3B,SAAS,CAAC,SAAS,IAAI,EAAE,CAAC;gBAC5B,CAAC,CAAC,CAAC;YACL,CAAC;iBAAM,CAAC;gBACN,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,cAAc,GAAG,IAAI,GAAG,EAAoC,CAAC;QACnE,IAAI,eAAe,GAAG,CAAC,CAAC;QACxB,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,MAAM,iBAAiB,GAAG,CAAC,CAAe,EAAE,EAAE;YAC5C,IAAI,CAAC,CAAC,WAAW,KAAK,OAAO;gBAAE,OAAO;YACtC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAChE,IAAI,cAAc,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBAC9B,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC;gBACrD,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;gBACvD,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;YAChC,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,iBAAiB,GAAG,CAAC,CAAe,EAAE,EAAE;YAC5C,IAAI,CAAC,CAAC,WAAW,KAAK,OAAO;gBAAE,OAAO;YACtC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;gBAAE,OAAO;YAC7C,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAChE,IAAI,cAAc,CAAC,IAAI,KAAK,CAAC,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;gBACrD,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC;gBACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;gBACtD,MAAM,QAAQ,GAAG,KAAK,CAAC,WAAW,GAAG,CAAC,QAAQ,GAAG,eAAe,CAAC,CAAC,CAAC;gBACnE,IAAI,QAAQ,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC;oBACjC,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAC/B,CAAC;gBACD,CAAC,CAAC,cAAc,EAAE,CAAC;YACrB,CAAC;QACH,CAAC,CAAC;QAEF,MAAM,gBAAgB,GAAG,CAAC,CAAe,EAAE,EAAE;YAC3C,IAAI,CAAC,CAAC,WAAW,KAAK,OAAO;gBAAE,OAAO;YACtC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YACnC,IAAI,cAAc,CAAC,IAAI,GAAG,CAAC;gBAAE,eAAe,GAAG,CAAC,CAAC;QACnD,CAAC,CAAC;QAEF,SAAS,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;QACrE,SAAS,CAAC,gBAAgB,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;QAC7D,SAAS,CAAC,gBAAgB,CAAC,aAAa,EAAE,iBAAiB,EAAE;YAC3D,OAAO,EAAE,KAAK;SACf,CAAC,CAAC;QACH,SAAS,CAAC,gBAAgB,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;QAC1D,SAAS,CAAC,gBAAgB,CAAC,eAAe,EAAE,gBAAgB,CAAC,CAAC;QAE9D,OAAO,GAAG,EAAE;YACV,SAAS,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;YACpD,SAAS,CAAC,mBAAmB,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;YAChE,SAAS,CAAC,mBAAmB,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;YAChE,SAAS,CAAC,mBAAmB,CAAC,WAAW,EAAE,gBAAgB,CAAC,CAAC;YAC7D,SAAS,CAAC,mBAAmB,CAAC,eAAe,EAAE,gBAAgB,CAAC,CAAC;QACnE,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC;AACtD,CAAC","sourcesContent":["import { useEffect, useRef } from \"react\";\n\nexport interface UsePinchZoomOptions {\n /** Scrolling viewport that receives the gesture. The scaled content should\n * live inside this element. */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Current zoom as a percentage (100 = 100%). */\n zoom: number;\n /** Setter for the zoom value (called with the next percentage). */\n setZoom: (next: number) => void;\n /** Minimum zoom percentage. Default 25. */\n min?: number;\n /** Maximum zoom percentage. Default 400. */\n max?: number;\n /** When true (default), adjusts container scroll so the point under the\n * cursor stays under the cursor during wheel-zoom. Assumes the scaled\n * content uses `transform-origin: top left` (or equivalent — e.g. resizing\n * the inner container's width proportionally to zoom). Disable for layouts\n * with `transform-origin: center center`. */\n zoomToCursor?: boolean;\n /** Disable the hook entirely without unmounting it. */\n enabled?: boolean;\n}\n\n/**\n * Pinch-to-zoom for canvas-style editors. Wires the trackpad pinch / Cmd+scroll\n * wheel gesture and 2-pointer touchscreen pinch onto a scrolling container.\n *\n * Trackpad pinch is detected via `wheel` events with `ctrlKey: true` — browsers\n * have synthesized that since ~2015 specifically so web apps can intercept the\n * gesture. `metaKey` is also accepted so Cmd+scroll on Mac feels native.\n *\n * The hook only calls `setZoom(next)` — it doesn't render anything. Templates\n * decide how to translate the zoom percentage into visual scaling (CSS\n * `transform: scale()`, width/height, etc.).\n */\nexport function usePinchZoom({\n containerRef,\n zoom,\n setZoom,\n min = 25,\n max = 400,\n zoomToCursor = true,\n enabled = true,\n}: UsePinchZoomOptions) {\n const zoomRef = useRef(zoom);\n const setZoomRef = useRef(setZoom);\n zoomRef.current = zoom;\n setZoomRef.current = setZoom;\n\n useEffect(() => {\n if (!enabled) return;\n const container = containerRef.current;\n if (!container) return;\n\n const clamp = (n: number) => Math.max(min, Math.min(max, n));\n\n const handleWheel = (e: WheelEvent) => {\n if (!(e.ctrlKey || e.metaKey)) return;\n e.preventDefault();\n\n const currentZoom = zoomRef.current;\n const clampedDelta = Math.max(-50, Math.min(50, e.deltaY));\n const factor = Math.exp(-clampedDelta * 0.01);\n const nextZoom = clamp(currentZoom * factor);\n\n if (nextZoom === currentZoom) return;\n\n if (zoomToCursor) {\n const rect = container.getBoundingClientRect();\n const cx = e.clientX - rect.left + container.scrollLeft;\n const cy = e.clientY - rect.top + container.scrollTop;\n const ratio = nextZoom / currentZoom;\n const dx = cx * (ratio - 1);\n const dy = cy * (ratio - 1);\n setZoomRef.current(nextZoom);\n requestAnimationFrame(() => {\n container.scrollLeft += dx;\n container.scrollTop += dy;\n });\n } else {\n setZoomRef.current(nextZoom);\n }\n };\n\n const activePointers = new Map<number, { x: number; y: number }>();\n let initialDistance = 0;\n let initialZoom = 0;\n\n const handlePointerDown = (e: PointerEvent) => {\n if (e.pointerType !== \"touch\") return;\n activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });\n if (activePointers.size === 2) {\n const [p1, p2] = Array.from(activePointers.values());\n initialDistance = Math.hypot(p2.x - p1.x, p2.y - p1.y);\n initialZoom = zoomRef.current;\n }\n };\n\n const handlePointerMove = (e: PointerEvent) => {\n if (e.pointerType !== \"touch\") return;\n if (!activePointers.has(e.pointerId)) return;\n activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });\n if (activePointers.size === 2 && initialDistance > 0) {\n const [p1, p2] = Array.from(activePointers.values());\n const distance = Math.hypot(p2.x - p1.x, p2.y - p1.y);\n const nextZoom = clamp(initialZoom * (distance / initialDistance));\n if (nextZoom !== zoomRef.current) {\n setZoomRef.current(nextZoom);\n }\n e.preventDefault();\n }\n };\n\n const handlePointerEnd = (e: PointerEvent) => {\n if (e.pointerType !== \"touch\") return;\n activePointers.delete(e.pointerId);\n if (activePointers.size < 2) initialDistance = 0;\n };\n\n container.addEventListener(\"wheel\", handleWheel, { passive: false });\n container.addEventListener(\"pointerdown\", handlePointerDown);\n container.addEventListener(\"pointermove\", handlePointerMove, {\n passive: false,\n });\n container.addEventListener(\"pointerup\", handlePointerEnd);\n container.addEventListener(\"pointercancel\", handlePointerEnd);\n\n return () => {\n container.removeEventListener(\"wheel\", handleWheel);\n container.removeEventListener(\"pointerdown\", handlePointerDown);\n container.removeEventListener(\"pointermove\", handlePointerMove);\n container.removeEventListener(\"pointerup\", handlePointerEnd);\n container.removeEventListener(\"pointercancel\", handlePointerEnd);\n };\n }, [containerRef, enabled, min, max, zoomToCursor]);\n}\n"]}
1
+ {"version":3,"file":"use-pinch-zoom.js","sourceRoot":"","sources":["../../src/client/use-pinch-zoom.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useRef } from \"react\";\n\nexport interface UsePinchZoomOptions {\n /** Scrolling viewport that receives the gesture. The scaled content should\n * live inside this element. */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Current zoom as a percentage (100 = 100%). */\n zoom: number;\n /** Setter for the zoom value (called with the next percentage). */\n setZoom: (next: number) => void;\n /** Minimum zoom percentage. Default 25. */\n min?: number;\n /** Maximum zoom percentage. Default 400. */\n max?: number;\n /** When true (default), adjusts container scroll so the point under the\n * cursor stays under the cursor during wheel-zoom. Assumes the scaled\n * content uses `transform-origin: top left` (or equivalent — e.g. resizing\n * the inner container's width proportionally to zoom). Disable for layouts\n * with `transform-origin: center center`. */\n zoomToCursor?: boolean;\n /** Disable the hook entirely without unmounting it. */\n enabled?: boolean;\n}\n\n/**\n * Pinch-to-zoom for canvas-style editors. Wires the trackpad pinch / Cmd+scroll\n * wheel gesture and 2-pointer touchscreen pinch onto a scrolling container.\n *\n * Trackpad pinch is detected via `wheel` events with `ctrlKey: true` — browsers\n * have synthesized that since ~2015 specifically so web apps can intercept the\n * gesture. `metaKey` is also accepted so Cmd+scroll on Mac feels native.\n *\n * The hook only calls `setZoom(next)` — it doesn't render anything. Templates\n * decide how to translate the zoom percentage into visual scaling (CSS\n * `transform: scale()`, width/height, etc.).\n */\nexport function usePinchZoom({\n containerRef,\n zoom,\n setZoom,\n min = 25,\n max = 400,\n zoomToCursor = true,\n enabled = true,\n}: UsePinchZoomOptions) {\n const zoomRef = useRef(zoom);\n const setZoomRef = useRef(setZoom);\n zoomRef.current = zoom;\n setZoomRef.current = setZoom;\n\n useEffect(() => {\n if (!enabled) return;\n const container = containerRef.current;\n if (!container) return;\n\n const clamp = (n: number) => Math.max(min, Math.min(max, n));\n\n // rAF coalescing: multiple wheel/pointermove events can fire per frame\n // (trackpad pinch and touch pinch both deliver many events between\n // paints). Instead of calling setZoom() synchronously per event — which\n // schedules a React re-render per event — stash the latest pending zoom\n // (and its cursor-anchored scroll delta) in a ref and flush once per\n // animation frame with the last-wins value. This preserves the exact\n // zoom-to-cursor math; it just applies it at most once per frame.\n //\n // Within a burst the DOM's real scrollLeft/scrollTop do NOT move until\n // flush() runs, so per-event math must not read them directly — every\n // event after the first in the same frame would anchor against the\n // pre-burst scroll position instead of where the (not-yet-committed)\n // previous events in the burst would have scrolled to. Track a simulated\n // running scroll position (`simScrollLeft`/`simScrollTop`, seeded from the\n // real scroll position when a new burst starts) and use that as the\n // anchor base, so each event's math composes exactly as if the prior\n // events in the burst had already been applied — matching the\n // pre-coalescing, one-setZoom-per-event behavior.\n let pendingZoom: number | null = null;\n let pendingScrollDelta: { dx: number; dy: number } | null = null;\n let simScrollLeft = 0;\n let simScrollTop = 0;\n let rafId: number | null = null;\n\n const flush = () => {\n rafId = null;\n if (pendingZoom === null) return;\n const nextZoom = pendingZoom;\n const scrollDelta = pendingScrollDelta;\n pendingZoom = null;\n pendingScrollDelta = null;\n setZoomRef.current(nextZoom);\n if (scrollDelta) {\n container.scrollLeft += scrollDelta.dx;\n container.scrollTop += scrollDelta.dy;\n }\n };\n\n const scheduleFlush = () => {\n if (rafId !== null) return;\n rafId = requestAnimationFrame(flush);\n };\n\n const handleWheel = (e: WheelEvent) => {\n if (!(e.ctrlKey || e.metaKey)) return;\n e.preventDefault();\n\n // Use the latest not-yet-applied zoom (if a flush is pending) so rapid\n // wheel events within the same frame compound correctly instead of\n // each computing off the last-committed React state.\n const currentZoom = pendingZoom ?? zoomRef.current;\n const clampedDelta = Math.max(-50, Math.min(50, e.deltaY));\n const factor = Math.exp(-clampedDelta * 0.01);\n const nextZoom = clamp(currentZoom * factor);\n\n if (nextZoom === currentZoom) return;\n\n if (zoomToCursor) {\n // Starting a new burst (nothing pending yet): seed the simulated\n // scroll position from the container's real, currently-committed\n // scroll offset.\n if (pendingScrollDelta === null) {\n simScrollLeft = container.scrollLeft;\n simScrollTop = container.scrollTop;\n }\n const rect = container.getBoundingClientRect();\n const cx = e.clientX - rect.left + simScrollLeft;\n const cy = e.clientY - rect.top + simScrollTop;\n const ratio = nextZoom / currentZoom;\n const dx = cx * (ratio - 1);\n const dy = cy * (ratio - 1);\n // Advance the simulated scroll position so the next event in this\n // same burst anchors against where this event would have left it.\n simScrollLeft += dx;\n simScrollTop += dy;\n pendingZoom = nextZoom;\n const prevDelta = pendingScrollDelta;\n pendingScrollDelta = {\n dx: (prevDelta?.dx ?? 0) + dx,\n dy: (prevDelta?.dy ?? 0) + dy,\n };\n } else {\n pendingZoom = nextZoom;\n }\n scheduleFlush();\n };\n\n const activePointers = new Map<number, { x: number; y: number }>();\n let initialDistance = 0;\n let initialZoom = 0;\n\n const handlePointerDown = (e: PointerEvent) => {\n if (e.pointerType !== \"touch\") return;\n activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });\n if (activePointers.size === 2) {\n const [p1, p2] = Array.from(activePointers.values());\n initialDistance = Math.hypot(p2.x - p1.x, p2.y - p1.y);\n initialZoom = zoomRef.current;\n }\n };\n\n const handlePointerMove = (e: PointerEvent) => {\n if (e.pointerType !== \"touch\") return;\n if (!activePointers.has(e.pointerId)) return;\n activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });\n if (activePointers.size === 2 && initialDistance > 0) {\n const [p1, p2] = Array.from(activePointers.values());\n const distance = Math.hypot(p2.x - p1.x, p2.y - p1.y);\n const nextZoom = clamp(initialZoom * (distance / initialDistance));\n if (nextZoom !== (pendingZoom ?? zoomRef.current)) {\n // Touch pinch has no cursor-anchoring math, so last-wins is simply\n // the newest zoom value — no scroll delta to accumulate.\n pendingZoom = nextZoom;\n scheduleFlush();\n }\n e.preventDefault();\n }\n };\n\n const handlePointerEnd = (e: PointerEvent) => {\n if (e.pointerType !== \"touch\") return;\n activePointers.delete(e.pointerId);\n if (activePointers.size < 2) initialDistance = 0;\n };\n\n container.addEventListener(\"wheel\", handleWheel, { passive: false });\n container.addEventListener(\"pointerdown\", handlePointerDown);\n container.addEventListener(\"pointermove\", handlePointerMove, {\n passive: false,\n });\n container.addEventListener(\"pointerup\", handlePointerEnd);\n container.addEventListener(\"pointercancel\", handlePointerEnd);\n\n return () => {\n container.removeEventListener(\"wheel\", handleWheel);\n container.removeEventListener(\"pointerdown\", handlePointerDown);\n container.removeEventListener(\"pointermove\", handlePointerMove);\n container.removeEventListener(\"pointerup\", handlePointerEnd);\n container.removeEventListener(\"pointercancel\", handlePointerEnd);\n if (rafId !== null) cancelAnimationFrame(rafId);\n pendingZoom = null;\n pendingScrollDelta = null;\n };\n }, [containerRef, enabled, min, max, zoomToCursor]);\n}\n"]}
@@ -49,11 +49,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
49
49
  error: string;
50
50
  states?: undefined;
51
51
  } | {
52
- error?: undefined;
53
52
  states: {
54
53
  clientId: number;
55
54
  state: string;
56
55
  }[];
56
+ error?: undefined;
57
57
  }>>;
58
58
  /**
59
59
  * GET /_agent-native/collab/:docId/users
@@ -64,10 +64,10 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
64
64
  error: string;
65
65
  users?: undefined;
66
66
  } | {
67
- error?: undefined;
68
67
  users: {
69
68
  clientId: number;
70
69
  lastSeen: number;
71
70
  }[];
71
+ error?: undefined;
72
72
  }>>;
73
73
  //# sourceMappingURL=awareness.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"awareness.d.ts","sourceRoot":"","sources":["../../src/collab/awareness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAS3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAC;AAElE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,wBAAgB,mBAAmB,IAAI,YAAY,CAElD;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,KAAK,CAAC,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,GACb,IAAI,CAUN;AAKD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAO1E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI,CAOnE;AAUD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;;kBAuCa,MAAM;eAAS,MAAM;;GAa1D,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;kBAWM,MAAM;kBAAY,MAAM;;GAMvD,CAAC"}
1
+ {"version":3,"file":"awareness.d.ts","sourceRoot":"","sources":["../../src/collab/awareness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAS3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD,eAAO,MAAM,sBAAsB,EAAG,kBAA2B,CAAC;AAElE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,kBAAkB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,MAAM,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,wBAAgB,mBAAmB,IAAI,YAAY,CAElD;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,KAAK,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,KAAK,CAAC,EAAE,MAAM,EACd,KAAK,CAAC,EAAE,MAAM,GACb,IAAI,CAUN;AAKD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAO1E;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,IAAI,CAOnE;AAUD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa;;;;;kBAuCa,MAAM;eAAS,MAAM;;;GAa1D,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;kBAWM,MAAM;kBAAY,MAAM;;;GAMvD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../src/collab/presence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,kEAAkE;AAClE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,IAAI,EAAE,UAAU,CAAC;IACjB,gFAAgF;IAChF,QAAQ,EAAE,eAAe,CAAC;IAC1B,kDAAkD;IAClD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB;;;;OAIG;IACH,WAAW,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;CACjD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,EACvC,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACvC,iBAAiB,CAuEnB;AAMD,MAAM,WAAW,eAAe;IAC9B,uCAAuC;IACvC,CAAC,EAAE,MAAM,CAAC;IACV,wCAAwC;IACxC,CAAC,EAAE,MAAM,CAAC;CACX;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,OAAO,GACjB,eAAe,CAKjB;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,OAAO,GACjB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B"}
1
+ {"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../src/collab/presence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,kEAAkE;AAClE,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtD,qDAAqD;AACrD,MAAM,WAAW,aAAa;IAC5B,qBAAqB;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,mDAAmD;IACnD,IAAI,EAAE,UAAU,CAAC;IACjB,gFAAgF;IAChF,QAAQ,EAAE,eAAe,CAAC;IAC1B,kDAAkD;IAClD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB;;;;OAIG;IACH,WAAW,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;CACjD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,EACvC,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACvC,iBAAiB,CAmInB;AAMD,MAAM,WAAW,eAAe;IAC9B,uCAAuC;IACvC,CAAC,EAAE,MAAM,CAAC;IACV,wCAAwC;IACxC,CAAC,EAAE,MAAM,CAAC;CACX;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,OAAO,GACjB,eAAe,CAKjB;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAC5B,KAAK,EAAE,eAAe,EACtB,SAAS,EAAE,OAAO,GACjB;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAK1B"}
@@ -26,6 +26,36 @@ export function usePresence(awareness, localClientId) {
26
26
  setOthers([]);
27
27
  return;
28
28
  }
29
+ // Keep the last derived snapshot so genuinely no-op change events (e.g. a
30
+ // local-only awareness field flip that doesn't affect any remote entry)
31
+ // can bail out without triggering a subscriber re-render.
32
+ let lastOthers = [];
33
+ function shallowEqualOthers(a, b) {
34
+ if (a === b)
35
+ return true;
36
+ if (a.length !== b.length)
37
+ return false;
38
+ for (let i = 0; i < a.length; i += 1) {
39
+ const left = a[i];
40
+ const right = b[i];
41
+ if (left === right)
42
+ continue;
43
+ if (left.clientId !== right.clientId ||
44
+ left.isAgent !== right.isAgent ||
45
+ left.user.name !== right.user.name ||
46
+ left.user.email !== right.user.email ||
47
+ left.user.color !== right.user.color) {
48
+ return false;
49
+ }
50
+ // Compare presence payloads with a stable JSON.stringify — presence
51
+ // fields (cursor/selection/viewport) are small JSON-safe records, so
52
+ // this is cheap and avoids a re-render when nothing actually changed.
53
+ if (JSON.stringify(left.presence) !== JSON.stringify(right.presence)) {
54
+ return false;
55
+ }
56
+ }
57
+ return true;
58
+ }
29
59
  function derive() {
30
60
  const result = [];
31
61
  awareness.getStates().forEach((state, clientId) => {
@@ -61,11 +91,32 @@ export function usePresence(awareness, localClientId) {
61
91
  });
62
92
  return result;
63
93
  }
64
- function onAwarenessChange() {
65
- setOthers(derive());
94
+ function onAwarenessChange(changes) {
95
+ // The awareness "change" event fires for local-only state edits too
96
+ // (e.g. this hook's own setPresence() calls, or the doc's
97
+ // activeFileId/visible fields). When every changed client id is the
98
+ // local client, the derived `others` array (which excludes the local
99
+ // client) cannot have changed, so skip the re-render entirely.
100
+ if (changes) {
101
+ const changedIds = [
102
+ ...changes.added,
103
+ ...changes.updated,
104
+ ...changes.removed,
105
+ ];
106
+ if (changedIds.length > 0 &&
107
+ changedIds.every((id) => id === localClientId)) {
108
+ return;
109
+ }
110
+ }
111
+ const next = derive();
112
+ if (shallowEqualOthers(lastOthers, next))
113
+ return;
114
+ lastOthers = next;
115
+ setOthers(next);
66
116
  }
67
117
  // Derive immediately.
68
- setOthers(derive());
118
+ lastOthers = derive();
119
+ setOthers(lastOthers);
69
120
  awareness.on("change", onAwarenessChange);
70
121
  return () => {
71
122
  awareness.off("change", onAwarenessChange);
@@ -1 +1 @@
1
- {"version":3,"file":"presence.js","sourceRoot":"","sources":["../../src/collab/presence.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Presence kit — Liveblocks/collaboration-grade presence primitives.\n *\n * usePresence(awareness) returns:\n * - others: reactive array of remote participants (human + agent)\n * - setPresence(partial): merge fields into local awareness state\n *\n * The hook re-renders on every awareness change event and always includes\n * the agent participant (AGENT_CLIENT_ID) as isAgent: true.\n */\n\nimport { useState, useEffect, useCallback, useRef } from \"react\";\nimport type { Awareness } from \"y-protocols/awareness\";\n\nimport { AGENT_CLIENT_ID } from \"./agent-identity.js\";\nimport type { CollabUser } from \"./client.js\";\n\n/** Arbitrary JSON presence payload published by a participant. */\nexport type PresencePayload = Record<string, unknown>;\n\n/** A remote participant's full presence snapshot. */\nexport interface OtherPresence {\n /** Yjs client ID. */\n clientId: number;\n /** User identity (from awareness `user` field). */\n user: CollabUser;\n /** Arbitrary presence fields set via setPresence() / agentUpdateSelection(). */\n presence: PresencePayload;\n /** True when this participant is the AI agent. */\n isAgent: boolean;\n}\n\nexport interface UsePresenceResult {\n /** All remote participants (excludes local client). */\n others: OtherPresence[];\n /**\n * Merge fields into the local awareness state. These are broadcast to\n * peers by the fast-awareness path in useCollaborativeDoc.\n * Call this to publish cursor position, viewport, or selection.\n */\n setPresence: (partial: PresencePayload) => void;\n}\n\n/**\n * Derive OtherPresence entries from an Awareness instance.\n *\n * @param awareness Awareness instance from useCollaborativeDoc.\n * @param localClientId The local Yjs client ID (to exclude self).\n */\nexport function usePresence(\n awareness: Awareness | null | undefined,\n localClientId: number | null | undefined,\n): UsePresenceResult {\n const [others, setOthers] = useState<OtherPresence[]>([]);\n\n // Keep the latest awareness ref so setPresence closure doesn't go stale.\n const awarenessRef = useRef(awareness);\n awarenessRef.current = awareness;\n\n useEffect(() => {\n if (!awareness) {\n setOthers([]);\n return;\n }\n\n function derive(): OtherPresence[] {\n const result: OtherPresence[] = [];\n awareness!.getStates().forEach((state, clientId) => {\n if (clientId === localClientId) return; // skip self\n const s = state as Record<string, unknown>;\n const isAgent = clientId === AGENT_CLIENT_ID;\n\n // User identity — fall back to agent defaults or anonymous.\n let user: CollabUser;\n if (isAgent) {\n user = {\n name: (s.user as CollabUser)?.name ?? \"AI Assistant\",\n email: (s.user as CollabUser)?.email ?? \"agent@system\",\n color: (s.user as CollabUser)?.color ?? \"#00B5FF\",\n };\n } else {\n const u = s.user as CollabUser | undefined;\n user = {\n name: u?.name ?? \"Unknown\",\n email: u?.email ?? `client-${clientId}`,\n color: u?.color ?? \"#94a3b8\",\n };\n }\n\n // Everything that isn't `user` or `visible` is presence payload.\n const presence: PresencePayload = {};\n for (const [k, v] of Object.entries(s)) {\n if (k !== \"user\" && k !== \"visible\") {\n presence[k] = v;\n }\n }\n\n result.push({ clientId, user, presence, isAgent });\n });\n return result;\n }\n\n function onAwarenessChange() {\n setOthers(derive());\n }\n\n // Derive immediately.\n setOthers(derive());\n awareness.on(\"change\", onAwarenessChange);\n return () => {\n awareness.off(\"change\", onAwarenessChange);\n };\n }, [awareness, localClientId]);\n\n const setPresence = useCallback((partial: PresencePayload) => {\n const aw = awarenessRef.current;\n if (!aw) return;\n for (const [k, v] of Object.entries(partial)) {\n aw.setLocalStateField(k, v);\n }\n }, []);\n\n return { others, setPresence };\n}\n\n// ---------------------------------------------------------------------------\n// Normalized cursor coordinate helpers\n// ---------------------------------------------------------------------------\n\nexport interface NormalizedPoint {\n /** 0–1 fraction of container width. */\n x: number;\n /** 0–1 fraction of container height. */\n y: number;\n}\n\n/** Convert a pointer event offset to a normalized point. */\nexport function toNormalized(\n clientX: number,\n clientY: number,\n container: DOMRect,\n): NormalizedPoint {\n return {\n x: Math.max(0, Math.min(1, (clientX - container.left) / container.width)),\n y: Math.max(0, Math.min(1, (clientY - container.top) / container.height)),\n };\n}\n\n/** Convert a normalized point back to absolute offset within a container. */\nexport function fromNormalized(\n point: NormalizedPoint,\n container: DOMRect,\n): { x: number; y: number } {\n return {\n x: point.x * container.width,\n y: point.y * container.height,\n };\n}\n"]}
1
+ {"version":3,"file":"presence.js","sourceRoot":"","sources":["../../src/collab/presence.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Presence kit — Liveblocks/collaboration-grade presence primitives.\n *\n * usePresence(awareness) returns:\n * - others: reactive array of remote participants (human + agent)\n * - setPresence(partial): merge fields into local awareness state\n *\n * The hook re-renders on every awareness change event and always includes\n * the agent participant (AGENT_CLIENT_ID) as isAgent: true.\n */\n\nimport { useState, useEffect, useCallback, useRef } from \"react\";\nimport type { Awareness } from \"y-protocols/awareness\";\n\nimport { AGENT_CLIENT_ID } from \"./agent-identity.js\";\nimport type { CollabUser } from \"./client.js\";\n\n/** Arbitrary JSON presence payload published by a participant. */\nexport type PresencePayload = Record<string, unknown>;\n\n/** A remote participant's full presence snapshot. */\nexport interface OtherPresence {\n /** Yjs client ID. */\n clientId: number;\n /** User identity (from awareness `user` field). */\n user: CollabUser;\n /** Arbitrary presence fields set via setPresence() / agentUpdateSelection(). */\n presence: PresencePayload;\n /** True when this participant is the AI agent. */\n isAgent: boolean;\n}\n\nexport interface UsePresenceResult {\n /** All remote participants (excludes local client). */\n others: OtherPresence[];\n /**\n * Merge fields into the local awareness state. These are broadcast to\n * peers by the fast-awareness path in useCollaborativeDoc.\n * Call this to publish cursor position, viewport, or selection.\n */\n setPresence: (partial: PresencePayload) => void;\n}\n\n/**\n * Derive OtherPresence entries from an Awareness instance.\n *\n * @param awareness Awareness instance from useCollaborativeDoc.\n * @param localClientId The local Yjs client ID (to exclude self).\n */\nexport function usePresence(\n awareness: Awareness | null | undefined,\n localClientId: number | null | undefined,\n): UsePresenceResult {\n const [others, setOthers] = useState<OtherPresence[]>([]);\n\n // Keep the latest awareness ref so setPresence closure doesn't go stale.\n const awarenessRef = useRef(awareness);\n awarenessRef.current = awareness;\n\n useEffect(() => {\n if (!awareness) {\n setOthers([]);\n return;\n }\n\n // Keep the last derived snapshot so genuinely no-op change events (e.g. a\n // local-only awareness field flip that doesn't affect any remote entry)\n // can bail out without triggering a subscriber re-render.\n let lastOthers: OtherPresence[] = [];\n\n function shallowEqualOthers(\n a: readonly OtherPresence[],\n b: readonly OtherPresence[],\n ): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i += 1) {\n const left = a[i]!;\n const right = b[i]!;\n if (left === right) continue;\n if (\n left.clientId !== right.clientId ||\n left.isAgent !== right.isAgent ||\n left.user.name !== right.user.name ||\n left.user.email !== right.user.email ||\n left.user.color !== right.user.color\n ) {\n return false;\n }\n // Compare presence payloads with a stable JSON.stringify — presence\n // fields (cursor/selection/viewport) are small JSON-safe records, so\n // this is cheap and avoids a re-render when nothing actually changed.\n if (JSON.stringify(left.presence) !== JSON.stringify(right.presence)) {\n return false;\n }\n }\n return true;\n }\n\n function derive(): OtherPresence[] {\n const result: OtherPresence[] = [];\n awareness!.getStates().forEach((state, clientId) => {\n if (clientId === localClientId) return; // skip self\n const s = state as Record<string, unknown>;\n const isAgent = clientId === AGENT_CLIENT_ID;\n\n // User identity — fall back to agent defaults or anonymous.\n let user: CollabUser;\n if (isAgent) {\n user = {\n name: (s.user as CollabUser)?.name ?? \"AI Assistant\",\n email: (s.user as CollabUser)?.email ?? \"agent@system\",\n color: (s.user as CollabUser)?.color ?? \"#00B5FF\",\n };\n } else {\n const u = s.user as CollabUser | undefined;\n user = {\n name: u?.name ?? \"Unknown\",\n email: u?.email ?? `client-${clientId}`,\n color: u?.color ?? \"#94a3b8\",\n };\n }\n\n // Everything that isn't `user` or `visible` is presence payload.\n const presence: PresencePayload = {};\n for (const [k, v] of Object.entries(s)) {\n if (k !== \"user\" && k !== \"visible\") {\n presence[k] = v;\n }\n }\n\n result.push({ clientId, user, presence, isAgent });\n });\n return result;\n }\n\n function onAwarenessChange(changes?: {\n added: number[];\n updated: number[];\n removed: number[];\n }) {\n // The awareness \"change\" event fires for local-only state edits too\n // (e.g. this hook's own setPresence() calls, or the doc's\n // activeFileId/visible fields). When every changed client id is the\n // local client, the derived `others` array (which excludes the local\n // client) cannot have changed, so skip the re-render entirely.\n if (changes) {\n const changedIds = [\n ...changes.added,\n ...changes.updated,\n ...changes.removed,\n ];\n if (\n changedIds.length > 0 &&\n changedIds.every((id) => id === localClientId)\n ) {\n return;\n }\n }\n const next = derive();\n if (shallowEqualOthers(lastOthers, next)) return;\n lastOthers = next;\n setOthers(next);\n }\n\n // Derive immediately.\n lastOthers = derive();\n setOthers(lastOthers);\n awareness.on(\"change\", onAwarenessChange);\n return () => {\n awareness.off(\"change\", onAwarenessChange);\n };\n }, [awareness, localClientId]);\n\n const setPresence = useCallback((partial: PresencePayload) => {\n const aw = awarenessRef.current;\n if (!aw) return;\n for (const [k, v] of Object.entries(partial)) {\n aw.setLocalStateField(k, v);\n }\n }, []);\n\n return { others, setPresence };\n}\n\n// ---------------------------------------------------------------------------\n// Normalized cursor coordinate helpers\n// ---------------------------------------------------------------------------\n\nexport interface NormalizedPoint {\n /** 0–1 fraction of container width. */\n x: number;\n /** 0–1 fraction of container height. */\n y: number;\n}\n\n/** Convert a pointer event offset to a normalized point. */\nexport function toNormalized(\n clientX: number,\n clientY: number,\n container: DOMRect,\n): NormalizedPoint {\n return {\n x: Math.max(0, Math.min(1, (clientX - container.left) / container.width)),\n y: Math.max(0, Math.min(1, (clientY - container.top) / container.height)),\n };\n}\n\n/** Convert a normalized point back to absolute offset within a container. */\nexport function fromNormalized(\n point: NormalizedPoint,\n container: DOMRect,\n): { x: number; y: number } {\n return {\n x: point.x * container.width,\n y: point.y * container.height,\n };\n}\n"]}
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
26
26
  * Body: { update: string (base64), requestSource?: string }
27
27
  */
28
28
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- error: string;
30
29
  ok?: undefined;
30
+ error: string;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;
@@ -13,8 +13,8 @@
13
13
  * Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
14
14
  */
15
15
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- error: string;
17
16
  ok?: undefined;
17
+ error: string;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;
@@ -3,18 +3,18 @@ declare const _default: import("../../action.js").ActionDefinition<{
3
3
  url?: string;
4
4
  filename?: string;
5
5
  }, {
6
- id?: undefined;
7
6
  error: string;
8
7
  configured?: undefined;
9
8
  connectPath?: undefined;
10
9
  url?: undefined;
10
+ id?: undefined;
11
11
  provider?: undefined;
12
12
  } | {
13
- id?: undefined;
14
13
  error: string;
15
14
  configured: boolean;
16
15
  connectPath: string;
17
16
  url?: undefined;
17
+ id?: undefined;
18
18
  provider?: undefined;
19
19
  } | {
20
20
  error?: undefined;
@@ -56,13 +56,13 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
56
56
  ok?: undefined;
57
57
  summary?: undefined;
58
58
  spans?: undefined;
59
- id?: undefined;
60
59
  error: any;
60
+ id?: undefined;
61
61
  } | {
62
62
  error?: undefined;
63
63
  summary?: undefined;
64
64
  spans?: undefined;
65
- id?: undefined;
66
65
  ok: boolean;
66
+ id?: undefined;
67
67
  }>>;
68
68
  //# sourceMappingURL=routes.d.ts.map
@@ -34,16 +34,16 @@ export declare function createListSecretsHandler(): import("h3").EventHandlerWit
34
34
  /** POST /_agent-native/secrets/:key — write a secret. */
35
35
  export declare function createWriteSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
36
36
  error: string;
37
- status?: undefined;
38
37
  ok?: undefined;
38
+ status?: undefined;
39
39
  } | {
40
40
  error?: undefined;
41
41
  ok: boolean;
42
42
  status: string;
43
43
  } | {
44
+ ok?: undefined;
44
45
  error: string;
45
46
  removed?: undefined;
46
- ok?: undefined;
47
47
  } | {
48
48
  error?: undefined;
49
49
  ok: boolean;
@@ -54,9 +54,9 @@ export declare function createWriteSecretHandler(): import("h3").EventHandlerWit
54
54
  * current stored value without changing anything. Useful for the "Test" button.
55
55
  */
56
56
  export declare function createTestSecretHandler(): import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
57
+ ok?: undefined;
57
58
  error: string;
58
59
  note?: undefined;
59
- ok?: undefined;
60
60
  } | {
61
61
  error?: undefined;
62
62
  ok: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.85.5",
3
+ "version": "0.85.7",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {