@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.
- package/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/AssistantChat.tsx +1 -1
- package/corpus/core/src/client/use-pinch-zoom.ts +76 -11
- package/corpus/core/src/collab/presence.ts +63 -3
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +245 -77
- package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +190 -13
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +16 -4
- package/corpus/templates/design/.agents/skills/export-handoff/SKILL.md +46 -3
- package/corpus/templates/design/AGENTS.md +40 -4
- package/corpus/templates/design/actions/apply-component-prop-edit.ts +20 -73
- package/corpus/templates/design/actions/apply-motion-edit.ts +29 -2
- package/corpus/templates/design/actions/apply-visual-edit.ts +4 -2
- package/corpus/templates/design/actions/connect-localhost.ts +25 -18
- package/corpus/templates/design/actions/edit-design.ts +5 -3
- package/corpus/templates/design/actions/export-pdf.ts +8 -1
- package/corpus/templates/design/actions/export-zip.ts +10 -1
- package/corpus/templates/design/actions/get-motion-timeline.ts +26 -20
- package/corpus/templates/design/actions/grant-localhost-write-consent.ts +10 -0
- package/corpus/templates/design/actions/insert-asset.ts +190 -7
- package/corpus/templates/design/actions/open-visual-edit.ts +27 -5
- package/corpus/templates/design/actions/present-design-variants.ts +12 -8
- package/corpus/templates/design/actions/revoke-localhost-write-consent.ts +18 -14
- package/corpus/templates/design/actions/run-design-audit.ts +7 -4
- package/corpus/templates/design/actions/write-local-file.ts +47 -22
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +221 -66
- package/corpus/templates/design/app/components/design/EditPanel.tsx +650 -237
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +526 -127
- package/corpus/templates/design/app/components/design/MotionDock.tsx +234 -46
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +1520 -410
- package/corpus/templates/design/app/components/design/StatesPanel.tsx +25 -2
- package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +640 -72
- package/corpus/templates/design/app/components/design/bridge/motion-preview.bridge.ts +605 -0
- package/corpus/templates/design/app/components/design/canvas-primitive-style.ts +50 -26
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +16 -4
- package/corpus/templates/design/app/components/design/inspector/DesignColorPicker.tsx +90 -103
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +111 -5
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +13 -2
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +42 -2
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +11 -2
- package/corpus/templates/design/app/components/design/types.ts +16 -0
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +24 -6
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +15 -1
- package/corpus/templates/design/app/hooks/use-question-flow.ts +2 -2
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +77 -0
- package/corpus/templates/design/app/i18n/zh-TW.ts +1 -0
- package/corpus/templates/design/app/i18n-data.ts +18 -0
- package/corpus/templates/design/app/lib/design-import.ts +95 -0
- package/corpus/templates/design/app/pages/DesignEditor.tsx +4015 -840
- package/corpus/templates/design/changelog/2026-07-02-creating-two-frames-in-a-row-no-longer-freezes-and-reloads-t.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-canvas-interactions-now-match-figma-rotation-aware-resizing-.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-design-retries-stalled-screen-edits-with-a-smaller-safer-fol.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-inspector-fixes-mixed-selections-show-mixed-instead-of-wrong.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-keyframe-timeline-works-reliably-drag-keyframes-smoothly-pic.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-layers-panel-matches-figma-top-layer-on-top-drag-with-auto-s.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-much-faster-editor-smooth-dragging-zooming-and-panel-updates.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-pen-tool-refinements-click-the-first-point-to-close-with-a-d.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-text-editing-enter-adds-a-line-break-international-ime-typin.md +6 -0
- package/corpus/templates/design/changelog/2026-07-03-undo-and-redo-are-dependable-across-agent-edits-screen-switc.md +6 -0
- package/corpus/templates/design/server/lib/design-export.ts +43 -1
- package/corpus/templates/design/server/plugins/agent-chat.ts +1 -1
- package/corpus/templates/design/shared/board-file.ts +25 -5
- package/corpus/templates/design/shared/canvas-math.ts +754 -9
- package/corpus/templates/design/shared/code-layer.ts +304 -26
- package/corpus/templates/design/shared/color-utils.ts +84 -0
- package/corpus/templates/design/shared/design-source-capabilities.ts +16 -7
- package/corpus/templates/design/shared/motion-compiler.ts +75 -31
- package/corpus/templates/design/shared/motion-timeline.ts +335 -0
- package/corpus/templates/design/shared/pen-path.ts +200 -23
- package/dist/client/AssistantChat.js +1 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/use-pinch-zoom.d.ts.map +1 -1
- package/dist/client/use-pinch-zoom.js +76 -11
- package/dist/client/use-pinch-zoom.js.map +1 -1
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/awareness.d.ts.map +1 -1
- package/dist/collab/presence.d.ts.map +1 -1
- package/dist/collab/presence.js +54 -3
- package/dist/collab/presence.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/observability/routes.d.ts +2 -2
- package/dist/secrets/routes.d.ts +3 -3
- package/package.json +1 -1
|
@@ -27,10 +27,27 @@ export function createCornerNode(point: PenPoint): PenNode {
|
|
|
27
27
|
return { point: { ...point } };
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Creates a smooth (symmetric-handle) anchor by default: `handleIn` mirrors
|
|
32
|
+
* `handleOut` across `anchor`, giving the anchor a continuous tangent on
|
|
33
|
+
* both sides — this is what dragging a fresh pen anchor normally produces.
|
|
34
|
+
*
|
|
35
|
+
* Pass `{ breakSymmetry: true }` (Figma: hold Alt/Option while dragging a
|
|
36
|
+
* newly placed anchor) to break that symmetry into a cusp: `handleOut`
|
|
37
|
+
* still follows the drag, but no mirrored `handleIn` is created, so the
|
|
38
|
+
* incoming segment stays a plain corner while the outgoing segment curves
|
|
39
|
+
* independently.
|
|
40
|
+
*/
|
|
41
|
+
export function createSmoothNode(
|
|
42
|
+
anchor: PenPoint,
|
|
43
|
+
handleOut: PenPoint,
|
|
44
|
+
options?: { breakSymmetry?: boolean },
|
|
45
|
+
): PenNode {
|
|
31
46
|
return {
|
|
32
47
|
point: { ...anchor },
|
|
33
|
-
handleIn:
|
|
48
|
+
handleIn: options?.breakSymmetry
|
|
49
|
+
? undefined
|
|
50
|
+
: mirrorPoint(anchor, handleOut),
|
|
34
51
|
handleOut: { ...handleOut },
|
|
35
52
|
};
|
|
36
53
|
}
|
|
@@ -62,17 +79,58 @@ export function constrainPointTo45Degrees(
|
|
|
62
79
|
): PenPoint {
|
|
63
80
|
const dx = point.x - origin.x;
|
|
64
81
|
const dy = point.y - origin.y;
|
|
65
|
-
|
|
66
|
-
if (distance === 0) return { ...point };
|
|
82
|
+
if (dx === 0 && dy === 0) return { ...point };
|
|
67
83
|
|
|
68
84
|
const angle = Math.atan2(dy, dx);
|
|
69
85
|
const snappedAngle = Math.round(angle / (Math.PI / 4)) * (Math.PI / 4);
|
|
86
|
+
const axisX = Math.cos(snappedAngle);
|
|
87
|
+
const axisY = Math.sin(snappedAngle);
|
|
88
|
+
|
|
89
|
+
// Project the drag vector onto the snapped axis component-wise (like
|
|
90
|
+
// Figma), rather than preserving the raw radial distance. For an
|
|
91
|
+
// axis-aligned snap (0/90/180/270) this reduces to keeping the dominant
|
|
92
|
+
// component and zeroing the other; for diagonal snaps it reduces to the
|
|
93
|
+
// usual equal-magnitude diagonal.
|
|
94
|
+
const projection = dx * axisX + dy * axisY;
|
|
70
95
|
return {
|
|
71
|
-
x: origin.x +
|
|
72
|
-
y: origin.y +
|
|
96
|
+
x: origin.x + axisX * projection,
|
|
97
|
+
y: origin.y + axisY * projection,
|
|
73
98
|
};
|
|
74
99
|
}
|
|
75
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Light pen-anchor snapping (P15): snap a candidate new-anchor point to any
|
|
103
|
+
* existing anchor point of the path currently being drawn (Figma snaps new
|
|
104
|
+
* anchors onto other anchors of the same path so you can precisely re-hit a
|
|
105
|
+
* prior point), and otherwise round to the nearest integer canvas px once
|
|
106
|
+
* the user is zoomed in to 100% or more (where sub-pixel placement is
|
|
107
|
+
* rarely intentional and hairline anti-aliasing becomes visible).
|
|
108
|
+
*
|
|
109
|
+
* This intentionally does not snap to *other* shapes/frames on the canvas —
|
|
110
|
+
* that's the existing computeMoveSnap/grid machinery's job for whole-object
|
|
111
|
+
* moves, not a per-anchor pen concern.
|
|
112
|
+
*/
|
|
113
|
+
export function snapPenAnchorPoint(
|
|
114
|
+
point: PenPoint,
|
|
115
|
+
path: PenPath | null,
|
|
116
|
+
options: { hitRadius: number; zoom: number },
|
|
117
|
+
): PenPoint {
|
|
118
|
+
const existingAnchor = path?.nodes.find(
|
|
119
|
+
(node) =>
|
|
120
|
+
Math.hypot(node.point.x - point.x, node.point.y - point.y) <=
|
|
121
|
+
options.hitRadius,
|
|
122
|
+
);
|
|
123
|
+
if (existingAnchor) {
|
|
124
|
+
return { ...existingAnchor.point };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (options.zoom >= 100) {
|
|
128
|
+
return { x: Math.round(point.x), y: Math.round(point.y) };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return point;
|
|
132
|
+
}
|
|
133
|
+
|
|
76
134
|
export function isPenCloseTarget(
|
|
77
135
|
path: PenPath | null,
|
|
78
136
|
point: PenPoint,
|
|
@@ -84,25 +142,151 @@ export function isPenCloseTarget(
|
|
|
84
142
|
}
|
|
85
143
|
|
|
86
144
|
export function getPenPathGeometry(path: PenPath): PenGeometry {
|
|
87
|
-
|
|
88
|
-
[node.point, node.handleIn, node.handleOut].filter(isPenPoint),
|
|
89
|
-
);
|
|
90
|
-
if (points.length === 0) {
|
|
145
|
+
if (path.nodes.length === 0) {
|
|
91
146
|
return { x: 0, y: 0, width: MIN_PATH_SIZE, height: MIN_PATH_SIZE };
|
|
92
147
|
}
|
|
93
148
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
149
|
+
// Tight bounds: rather than bounding all anchors *and* control handles
|
|
150
|
+
// (which over-counts — a handle that pulls a curve's tangent can sit well
|
|
151
|
+
// outside the curve's actual extent), walk each rendered segment and
|
|
152
|
+
// bound the real curve geometry: anchor endpoints plus any local extrema
|
|
153
|
+
// found by solving the cubic Bezier derivative per axis. This matches
|
|
154
|
+
// what `serializePenPath` actually draws (an `L` segment when handles
|
|
155
|
+
// coincide with their anchors, otherwise a `C` segment).
|
|
156
|
+
let left = Infinity;
|
|
157
|
+
let top = Infinity;
|
|
158
|
+
let right = -Infinity;
|
|
159
|
+
let bottom = -Infinity;
|
|
160
|
+
|
|
161
|
+
const include = (point: PenPoint) => {
|
|
162
|
+
if (point.x < left) left = point.x;
|
|
163
|
+
if (point.x > right) right = point.x;
|
|
164
|
+
if (point.y < top) top = point.y;
|
|
165
|
+
if (point.y > bottom) bottom = point.y;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const { nodes, closed } = path;
|
|
169
|
+
include(nodes[0].point);
|
|
170
|
+
|
|
171
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
172
|
+
includeSegmentBounds(nodes[i - 1], nodes[i], include);
|
|
173
|
+
}
|
|
174
|
+
if (closed && nodes.length > 1) {
|
|
175
|
+
includeSegmentBounds(nodes[nodes.length - 1], nodes[0], include);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!Number.isFinite(left)) {
|
|
179
|
+
return { x: 0, y: 0, width: MIN_PATH_SIZE, height: MIN_PATH_SIZE };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const width = right - left;
|
|
183
|
+
const height = bottom - top;
|
|
184
|
+
// Degenerate (zero-area) paths — e.g. a single anchor, or a perfectly
|
|
185
|
+
// straight horizontal/vertical two-point path — still need a visible
|
|
186
|
+
// selection box, so floor to a minimum size in that case only.
|
|
187
|
+
if (width <= 0 && height <= 0) {
|
|
188
|
+
return { x: left, y: top, width: MIN_PATH_SIZE, height: MIN_PATH_SIZE };
|
|
189
|
+
}
|
|
98
190
|
return {
|
|
99
191
|
x: left,
|
|
100
192
|
y: top,
|
|
101
|
-
width:
|
|
102
|
-
height:
|
|
193
|
+
width: width > 0 ? width : MIN_PATH_SIZE,
|
|
194
|
+
height: height > 0 ? height : MIN_PATH_SIZE,
|
|
103
195
|
};
|
|
104
196
|
}
|
|
105
197
|
|
|
198
|
+
function includeSegmentBounds(
|
|
199
|
+
from: PenNode,
|
|
200
|
+
to: PenNode,
|
|
201
|
+
include: (point: PenPoint) => void,
|
|
202
|
+
) {
|
|
203
|
+
const c1 = from.handleOut ?? from.point;
|
|
204
|
+
const c2 = to.handleIn ?? to.point;
|
|
205
|
+
include(to.point);
|
|
206
|
+
|
|
207
|
+
// Straight segment (serializePenPath emits `L` in this case) — the two
|
|
208
|
+
// anchors already bound it, no interior extrema to solve for.
|
|
209
|
+
if (samePoint(c1, from.point) && samePoint(c2, to.point)) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
include(c1);
|
|
214
|
+
include(c2);
|
|
215
|
+
for (const t of cubicBezierExtremaTs(from.point.x, c1.x, c2.x, to.point.x)) {
|
|
216
|
+
include({
|
|
217
|
+
x: cubicBezierValue(from.point.x, c1.x, c2.x, to.point.x, t),
|
|
218
|
+
y: cubicBezierValue(from.point.y, c1.y, c2.y, to.point.y, t),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
for (const t of cubicBezierExtremaTs(from.point.y, c1.y, c2.y, to.point.y)) {
|
|
222
|
+
include({
|
|
223
|
+
x: cubicBezierValue(from.point.x, c1.x, c2.x, to.point.x, t),
|
|
224
|
+
y: cubicBezierValue(from.point.y, c1.y, c2.y, to.point.y, t),
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Roots of B'(t) = 0 for a single-axis cubic Bezier with control points
|
|
231
|
+
* p0..p3, restricted to t in (0, 1) (endpoints are already included by the
|
|
232
|
+
* caller via the anchor points).
|
|
233
|
+
*
|
|
234
|
+
* B(t) = (1-t)^3 p0 + 3(1-t)^2 t p1 + 3(1-t) t^2 p2 + t^3 p3
|
|
235
|
+
* B'(t) = 3(1-t)^2 (p1-p0) + 6(1-t)t (p2-p1) + 3t^2 (p3-p2)
|
|
236
|
+
* = a t^2 + b t + c, with:
|
|
237
|
+
* a = 3 * (-p0 + 3p1 - 3p2 + p3)
|
|
238
|
+
* b = 6 * (p0 - 2p1 + p2)
|
|
239
|
+
* c = 3 * (p1 - p0)
|
|
240
|
+
*/
|
|
241
|
+
function cubicBezierExtremaTs(
|
|
242
|
+
p0: number,
|
|
243
|
+
p1: number,
|
|
244
|
+
p2: number,
|
|
245
|
+
p3: number,
|
|
246
|
+
): number[] {
|
|
247
|
+
const a = 3 * (-p0 + 3 * p1 - 3 * p2 + p3);
|
|
248
|
+
const b = 6 * (p0 - 2 * p1 + p2);
|
|
249
|
+
const c = 3 * (p1 - p0);
|
|
250
|
+
|
|
251
|
+
const roots: number[] = [];
|
|
252
|
+
const EPS = 1e-9;
|
|
253
|
+
|
|
254
|
+
if (Math.abs(a) < EPS) {
|
|
255
|
+
// Linear derivative: at most one root.
|
|
256
|
+
if (Math.abs(b) >= EPS) {
|
|
257
|
+
const t = -c / b;
|
|
258
|
+
if (t > 0 && t < 1) roots.push(t);
|
|
259
|
+
}
|
|
260
|
+
return roots;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const discriminant = b * b - 4 * a * c;
|
|
264
|
+
if (discriminant < 0) return roots;
|
|
265
|
+
|
|
266
|
+
const sqrtDisc = Math.sqrt(discriminant);
|
|
267
|
+
const t1 = (-b + sqrtDisc) / (2 * a);
|
|
268
|
+
const t2 = (-b - sqrtDisc) / (2 * a);
|
|
269
|
+
if (t1 > 0 && t1 < 1) roots.push(t1);
|
|
270
|
+
if (t2 > 0 && t2 < 1) roots.push(t2);
|
|
271
|
+
return roots;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function cubicBezierValue(
|
|
275
|
+
p0: number,
|
|
276
|
+
p1: number,
|
|
277
|
+
p2: number,
|
|
278
|
+
p3: number,
|
|
279
|
+
t: number,
|
|
280
|
+
): number {
|
|
281
|
+
const mt = 1 - t;
|
|
282
|
+
return (
|
|
283
|
+
mt * mt * mt * p0 +
|
|
284
|
+
3 * mt * mt * t * p1 +
|
|
285
|
+
3 * mt * t * t * p2 +
|
|
286
|
+
t * t * t * p3
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
106
290
|
export function serializePenPath(path: PenPath): string {
|
|
107
291
|
const [first, ...rest] = path.nodes;
|
|
108
292
|
if (!first) return "";
|
|
@@ -121,13 +305,6 @@ export function serializePenPath(path: PenPath): string {
|
|
|
121
305
|
return commands.join(" ");
|
|
122
306
|
}
|
|
123
307
|
|
|
124
|
-
export function offsetPenPath(path: PenPath, offset: PenPoint): PenPath {
|
|
125
|
-
return transformPenPath(path, (point) => ({
|
|
126
|
-
x: point.x - offset.x,
|
|
127
|
-
y: point.y - offset.y,
|
|
128
|
-
}));
|
|
129
|
-
}
|
|
130
|
-
|
|
131
308
|
export function translatePenPath(
|
|
132
309
|
path: PenPath,
|
|
133
310
|
dx: number,
|
|
@@ -71,7 +71,7 @@ const BACKGROUND_ACTIVE_RUN_STUCK_THRESHOLD_MS = 13 * 60_000;
|
|
|
71
71
|
const ACTIVE_RUN_POLL_INTERVAL_MS = 150;
|
|
72
72
|
const AUTO_RESUME_STATUS_TIMEOUT_MS = 30_000;
|
|
73
73
|
const MAX_RECONNECT_AUTO_RECOVERIES = 3;
|
|
74
|
-
const RECONNECT_NO_PROGRESS_CONTINUE_MESSAGE = "Continue from where you stopped. Use the partial work above, verify what succeeded, and finish the original request. Do not rerun the exact same failed tool input unless the failure was transient or the user explicitly asked for an exact rerun. Prefer dedicated app actions over raw database edits when they exist.";
|
|
74
|
+
const RECONNECT_NO_PROGRESS_CONTINUE_MESSAGE = "Continue from where you stopped. Use the partial work above, verify what succeeded, and finish the original request. If the last visible step was preparing an app action and no tool result was returned, treat that action input as stalled or too large: change strategy, use a smaller bounded input, and preserve optional details as visible affordances instead of repeating the same giant action. Do not rerun the exact same failed tool input unless the failure was transient or the user explicitly asked for an exact rerun. Prefer dedicated app actions over raw database edits when they exist.";
|
|
75
75
|
// How long a single activity (model call, tool prep, long tool) must stay
|
|
76
76
|
// in-flight before its label is surfaced in the running indicator. Below this
|
|
77
77
|
// the indicator stays a steady "Thinking" so normal fast turns don't flicker
|