@agent-native/core-corpus 0.218.0 → 0.219.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/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/hubspot/SKILL.md +5 -3
- package/corpus/templates/community/qr/package.json +1 -1
- package/corpus/templates/content/CHANGELOG.md +5 -0
- package/corpus/templates/content/app/components/editor/CommentStack.tsx +301 -0
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +651 -149
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +19 -14
- package/corpus/templates/content/app/components/editor/block-field-comment-threads.ts +15 -0
- package/corpus/templates/content/app/components/editor/block-field-comments.tsx +5 -9
- package/corpus/templates/content/app/components/editor/comment-stacks.ts +143 -0
- package/corpus/templates/content/app/i18n-data.ts +8 -0
- package/corpus/templates/content/changelog/2026-10-22-blocks-with-several-comment-threads-fold-into-one-stack-in-t.md +6 -0
- package/corpus/templates/content/changelog/2026-10-22-opening-a-page-no-longer-saves-an-edit-again-after-another-o.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +8 -1
- package/corpus/templates/design/.generated/bridge/interact-links.generated.ts +8 -8
- package/corpus/templates/design/CHANGELOG.md +5 -0
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +18 -1
- package/corpus/templates/design/app/components/design/bridge/interact-links.bridge.ts +8 -8
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +15 -13
- package/corpus/templates/design/app/i18n/en-US.ts +1 -1
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-modes.ts +15 -6
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-screen-rendering.tsx +29 -5
- package/corpus/templates/design/app/pages/design-editor/prototype-navigation-transition.ts +423 -0
- package/corpus/templates/design/app/pages/design-editor/view/single-screen-canvas.tsx +35 -9
- package/corpus/templates/design/changelog/2026-10-20-the-design-full-app-toggle-on-the-home-page-no-longer-hides-.md +6 -0
- package/corpus/templates/design/changelog/2026-10-22-prototype-links-can-play-code-authored-screen-transitions.md +6 -0
- package/corpus/templates/design/shared/motion-authored-css-import.ts +938 -7
- package/corpus/templates/design/shared/motion-html-source.ts +1689 -135
- package/corpus/templates/design/shared/motion-prototype-transition.ts +329 -0
- package/corpus/templates/design/shared/motion-react-source.ts +2154 -126
- package/corpus/templates/design/shared/motion-text-runtime-v1.fixture.txt +221 -0
- package/package.json +1 -1
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MotionPrototypeDirection,
|
|
3
|
+
MotionPrototypeEase,
|
|
4
|
+
MotionPrototypeNavigationTransition,
|
|
5
|
+
} from "@shared/motion-prototype-transition";
|
|
6
|
+
import { validateMotionPrototypeNavigationTransition } from "@shared/motion-prototype-transition";
|
|
7
|
+
import { flushSync } from "react-dom";
|
|
8
|
+
|
|
9
|
+
type PrototypeTransitionDocument = Pick<
|
|
10
|
+
Document,
|
|
11
|
+
"querySelector" | "querySelectorAll"
|
|
12
|
+
>;
|
|
13
|
+
type PrototypeNavigationTransitionOptions = {
|
|
14
|
+
sourceFrame?: HTMLIFrameElement;
|
|
15
|
+
destinationFrameId?: string;
|
|
16
|
+
};
|
|
17
|
+
type TransitionRun = {
|
|
18
|
+
generation: number;
|
|
19
|
+
animation?: Animation;
|
|
20
|
+
cleanup?: () => void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type PrototypeNavigationTransitionResult =
|
|
24
|
+
| "instant"
|
|
25
|
+
| "animated"
|
|
26
|
+
| "browser-fallback"
|
|
27
|
+
| "unsupported-transition";
|
|
28
|
+
|
|
29
|
+
const PREVIEW_FRAME_SELECTOR = "iframe[data-an-prototype-preview]";
|
|
30
|
+
const DIRECTION_OFFSETS: Record<
|
|
31
|
+
MotionPrototypeDirection,
|
|
32
|
+
readonly [string, string, string, string]
|
|
33
|
+
> = {
|
|
34
|
+
left: ["-100%", "0%", "100%", "0%"],
|
|
35
|
+
right: ["100%", "0%", "-100%", "0%"],
|
|
36
|
+
top: ["0%", "-100%", "0%", "100%"],
|
|
37
|
+
bottom: ["0%", "100%", "0%", "-100%"],
|
|
38
|
+
};
|
|
39
|
+
const transitionRuns = new WeakMap<object, TransitionRun>();
|
|
40
|
+
|
|
41
|
+
export function getPrototypeDestinationFrameId(
|
|
42
|
+
screenId: string,
|
|
43
|
+
sourceFrame: HTMLIFrameElement,
|
|
44
|
+
): string {
|
|
45
|
+
const breakpoint = sourceFrame
|
|
46
|
+
.getAttribute("data-screen-iframe-id")
|
|
47
|
+
?.match(/::bp-\d+$/)?.[0];
|
|
48
|
+
return `${screenId}${breakpoint ?? ""}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function findPreviewFrame(
|
|
52
|
+
documentRef: PrototypeTransitionDocument,
|
|
53
|
+
frameId?: string,
|
|
54
|
+
): HTMLIFrameElement | null {
|
|
55
|
+
const frames = documentRef.querySelectorAll<HTMLIFrameElement>(
|
|
56
|
+
PREVIEW_FRAME_SELECTOR,
|
|
57
|
+
);
|
|
58
|
+
if (!frameId) return frames[0] ?? null;
|
|
59
|
+
return (
|
|
60
|
+
Array.from(frames).find(
|
|
61
|
+
(frame) => frame.getAttribute("data-screen-iframe-id") === frameId,
|
|
62
|
+
) ?? null
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function beginTransitionRun(documentRef: PrototypeTransitionDocument): number {
|
|
67
|
+
const previous = transitionRuns.get(documentRef);
|
|
68
|
+
if (previous) {
|
|
69
|
+
previous.generation += 1;
|
|
70
|
+
previous.animation?.cancel();
|
|
71
|
+
previous.animation = undefined;
|
|
72
|
+
previous.cleanup?.();
|
|
73
|
+
previous.cleanup = undefined;
|
|
74
|
+
return previous.generation;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
transitionRuns.set(documentRef, { generation: 1 });
|
|
78
|
+
return 1;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function cancelPrototypeNavigationTransition(
|
|
82
|
+
documentRef: PrototypeTransitionDocument | undefined = typeof document ===
|
|
83
|
+
"undefined"
|
|
84
|
+
? undefined
|
|
85
|
+
: document,
|
|
86
|
+
): void {
|
|
87
|
+
if (documentRef) beginTransitionRun(documentRef);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function isCurrentTransitionRun(
|
|
91
|
+
documentRef: PrototypeTransitionDocument,
|
|
92
|
+
generation: number,
|
|
93
|
+
): boolean {
|
|
94
|
+
return transitionRuns.get(documentRef)?.generation === generation;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function setRunAnimation(
|
|
98
|
+
documentRef: PrototypeTransitionDocument,
|
|
99
|
+
generation: number,
|
|
100
|
+
animation: Animation | undefined,
|
|
101
|
+
): void {
|
|
102
|
+
const run = transitionRuns.get(documentRef);
|
|
103
|
+
if (run?.generation === generation) run.animation = animation;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function setRunCleanup(
|
|
107
|
+
documentRef: PrototypeTransitionDocument,
|
|
108
|
+
generation: number,
|
|
109
|
+
cleanup: (() => void) | undefined,
|
|
110
|
+
): void {
|
|
111
|
+
const run = transitionRuns.get(documentRef);
|
|
112
|
+
if (run?.generation === generation) run.cleanup = cleanup;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function easeToCss(ease: MotionPrototypeEase): string {
|
|
116
|
+
if (ease.kind === "preset") return ease.preset;
|
|
117
|
+
return `cubic-bezier(${ease.x1}, ${ease.y1}, ${ease.x2}, ${ease.y2})`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function transitionKeyframes(
|
|
121
|
+
transition: MotionPrototypeNavigationTransition,
|
|
122
|
+
phase: "enter" | "exit",
|
|
123
|
+
): Keyframe[] {
|
|
124
|
+
if (transition.transition === "dissolve") {
|
|
125
|
+
return phase === "enter"
|
|
126
|
+
? [{ opacity: 0 }, { opacity: 1 }]
|
|
127
|
+
: [{ opacity: 1 }, { opacity: 0 }];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const [enterX, enterY, exitX, exitY] = transition.direction
|
|
131
|
+
? DIRECTION_OFFSETS[transition.direction]
|
|
132
|
+
: ["0%", "0%", "0%", "0%"];
|
|
133
|
+
const offsetX =
|
|
134
|
+
phase === "exit" && transition.transition === "push" ? exitX : enterX;
|
|
135
|
+
const offsetY =
|
|
136
|
+
phase === "exit" && transition.transition === "push" ? exitY : enterY;
|
|
137
|
+
const transform = `translate(${offsetX}, ${offsetY})`;
|
|
138
|
+
|
|
139
|
+
return phase === "enter"
|
|
140
|
+
? [{ transform }, { transform: "translate(0%, 0%)" }]
|
|
141
|
+
: [
|
|
142
|
+
{ transform: "translate(0%, 0%)" },
|
|
143
|
+
{
|
|
144
|
+
transform:
|
|
145
|
+
transition.transition === "push"
|
|
146
|
+
? transform
|
|
147
|
+
: `translate(${enterX}, ${enterY})`,
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function startFrameAnimation(
|
|
153
|
+
frame: HTMLIFrameElement,
|
|
154
|
+
transition: MotionPrototypeNavigationTransition,
|
|
155
|
+
phase: "enter" | "exit",
|
|
156
|
+
durationMs: number,
|
|
157
|
+
): Animation {
|
|
158
|
+
return frame.animate(transitionKeyframes(transition, phase), {
|
|
159
|
+
duration: durationMs,
|
|
160
|
+
easing: easeToCss(transition.ease!),
|
|
161
|
+
fill: "both",
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
type FrameDocumentLoadState = "loaded" | "pending" | "invalid-source";
|
|
166
|
+
|
|
167
|
+
function getFrameDocumentLoadState(
|
|
168
|
+
frame: HTMLIFrameElement,
|
|
169
|
+
previousDocument: Document | null,
|
|
170
|
+
sourceChanged: boolean,
|
|
171
|
+
loadEventObserved = false,
|
|
172
|
+
): FrameDocumentLoadState {
|
|
173
|
+
const isSrcdoc = frame.hasAttribute("srcdoc");
|
|
174
|
+
const src = isSrcdoc ? null : frame.getAttribute("src");
|
|
175
|
+
const baseURI = frame.ownerDocument.baseURI;
|
|
176
|
+
if (src !== null && !URL.canParse(src, baseURI)) return "invalid-source";
|
|
177
|
+
|
|
178
|
+
const current = frame.contentDocument;
|
|
179
|
+
if (current === null) {
|
|
180
|
+
return sourceChanged && !loadEventObserved ? "pending" : "loaded";
|
|
181
|
+
}
|
|
182
|
+
if (
|
|
183
|
+
current.readyState !== "complete" ||
|
|
184
|
+
(sourceChanged && current === previousDocument)
|
|
185
|
+
) {
|
|
186
|
+
return "pending";
|
|
187
|
+
}
|
|
188
|
+
if (isSrcdoc) {
|
|
189
|
+
return current.URL === "about:srcdoc" ? "loaded" : "pending";
|
|
190
|
+
}
|
|
191
|
+
if (src === null) return "loaded";
|
|
192
|
+
const destinationUrl = new URL(src, baseURI);
|
|
193
|
+
if (current.URL === destinationUrl.href) return "loaded";
|
|
194
|
+
return loadEventObserved &&
|
|
195
|
+
new URL(current.URL).origin === destinationUrl.origin
|
|
196
|
+
? "loaded"
|
|
197
|
+
: "pending";
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function animateEntry(
|
|
201
|
+
transition: MotionPrototypeNavigationTransition,
|
|
202
|
+
update: () => void,
|
|
203
|
+
documentRef: PrototypeTransitionDocument,
|
|
204
|
+
durationMs: number,
|
|
205
|
+
generation: number,
|
|
206
|
+
options: PrototypeNavigationTransitionOptions,
|
|
207
|
+
beforeStart?: () => void,
|
|
208
|
+
): PrototypeNavigationTransitionResult {
|
|
209
|
+
const previousFrame = findPreviewFrame(
|
|
210
|
+
documentRef,
|
|
211
|
+
options.destinationFrameId,
|
|
212
|
+
);
|
|
213
|
+
const previousSource = previousFrame
|
|
214
|
+
? [
|
|
215
|
+
previousFrame.getAttribute("src"),
|
|
216
|
+
previousFrame.getAttribute("srcdoc"),
|
|
217
|
+
].join("\u0000")
|
|
218
|
+
: null;
|
|
219
|
+
const previousDocument = previousFrame?.contentDocument ?? null;
|
|
220
|
+
flushSync(update);
|
|
221
|
+
const frame = findPreviewFrame(documentRef, options.destinationFrameId);
|
|
222
|
+
if (!frame || typeof frame.animate !== "function") {
|
|
223
|
+
return "browser-fallback";
|
|
224
|
+
}
|
|
225
|
+
const sourceChanged =
|
|
226
|
+
frame !== previousFrame ||
|
|
227
|
+
[frame.getAttribute("src"), frame.getAttribute("srcdoc")].join("\u0000") !==
|
|
228
|
+
previousSource;
|
|
229
|
+
|
|
230
|
+
const start = (): boolean => {
|
|
231
|
+
if (!isCurrentTransitionRun(documentRef, generation)) return false;
|
|
232
|
+
setRunCleanup(documentRef, generation, undefined);
|
|
233
|
+
beforeStart?.();
|
|
234
|
+
try {
|
|
235
|
+
const animation = startFrameAnimation(
|
|
236
|
+
frame,
|
|
237
|
+
transition,
|
|
238
|
+
"enter",
|
|
239
|
+
durationMs,
|
|
240
|
+
);
|
|
241
|
+
setRunAnimation(documentRef, generation, animation);
|
|
242
|
+
void animation.finished.then(
|
|
243
|
+
() => {
|
|
244
|
+
animation.cancel();
|
|
245
|
+
setRunAnimation(documentRef, generation, undefined);
|
|
246
|
+
},
|
|
247
|
+
() => {},
|
|
248
|
+
);
|
|
249
|
+
return true;
|
|
250
|
+
} catch {
|
|
251
|
+
setRunAnimation(documentRef, generation, undefined);
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const loadState = getFrameDocumentLoadState(
|
|
257
|
+
frame,
|
|
258
|
+
previousDocument,
|
|
259
|
+
sourceChanged,
|
|
260
|
+
);
|
|
261
|
+
if (loadState === "invalid-source") return "browser-fallback";
|
|
262
|
+
if (loadState === "loaded") {
|
|
263
|
+
return start() ? "animated" : "browser-fallback";
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const onLoad = () => {
|
|
267
|
+
const loadState = getFrameDocumentLoadState(
|
|
268
|
+
frame,
|
|
269
|
+
previousDocument,
|
|
270
|
+
sourceChanged,
|
|
271
|
+
true,
|
|
272
|
+
);
|
|
273
|
+
if (loadState === "pending") return;
|
|
274
|
+
|
|
275
|
+
frame.removeEventListener("load", onLoad);
|
|
276
|
+
setRunCleanup(documentRef, generation, undefined);
|
|
277
|
+
if (loadState === "loaded") {
|
|
278
|
+
start();
|
|
279
|
+
} else {
|
|
280
|
+
beforeStart?.();
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
frame.addEventListener("load", onLoad);
|
|
284
|
+
setRunCleanup(documentRef, generation, () =>
|
|
285
|
+
frame.removeEventListener("load", onLoad),
|
|
286
|
+
);
|
|
287
|
+
return "animated";
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function animateExit(
|
|
291
|
+
transition: MotionPrototypeNavigationTransition,
|
|
292
|
+
update: () => void,
|
|
293
|
+
documentRef: PrototypeTransitionDocument,
|
|
294
|
+
durationMs: number,
|
|
295
|
+
continueWithEntry: boolean,
|
|
296
|
+
generation: number,
|
|
297
|
+
options: PrototypeNavigationTransitionOptions,
|
|
298
|
+
): PrototypeNavigationTransitionResult {
|
|
299
|
+
const frame = options.sourceFrame ?? findPreviewFrame(documentRef);
|
|
300
|
+
if (!frame || typeof frame.animate !== "function") {
|
|
301
|
+
flushSync(update);
|
|
302
|
+
return "browser-fallback";
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
let animation: Animation;
|
|
306
|
+
try {
|
|
307
|
+
animation = startFrameAnimation(frame, transition, "exit", durationMs);
|
|
308
|
+
} catch {
|
|
309
|
+
flushSync(update);
|
|
310
|
+
return "browser-fallback";
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
setRunAnimation(documentRef, generation, animation);
|
|
314
|
+
void animation.finished.then(
|
|
315
|
+
() => {
|
|
316
|
+
if (!isCurrentTransitionRun(documentRef, generation)) return;
|
|
317
|
+
if (continueWithEntry) {
|
|
318
|
+
let released = false;
|
|
319
|
+
const releaseExit = () => {
|
|
320
|
+
if (released) return;
|
|
321
|
+
released = true;
|
|
322
|
+
animation.cancel();
|
|
323
|
+
if (transitionRuns.get(documentRef)?.animation === animation) {
|
|
324
|
+
setRunAnimation(documentRef, generation, undefined);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
const result = animateEntry(
|
|
328
|
+
transition,
|
|
329
|
+
update,
|
|
330
|
+
documentRef,
|
|
331
|
+
transition.durationMs! - durationMs,
|
|
332
|
+
generation,
|
|
333
|
+
options,
|
|
334
|
+
releaseExit,
|
|
335
|
+
);
|
|
336
|
+
if (result !== "animated") releaseExit();
|
|
337
|
+
} else {
|
|
338
|
+
flushSync(update);
|
|
339
|
+
animation.cancel();
|
|
340
|
+
setRunAnimation(documentRef, generation, undefined);
|
|
341
|
+
}
|
|
342
|
+
},
|
|
343
|
+
() => {
|
|
344
|
+
if (isCurrentTransitionRun(documentRef, generation)) {
|
|
345
|
+
setRunAnimation(documentRef, generation, undefined);
|
|
346
|
+
flushSync(update);
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
);
|
|
350
|
+
return "animated";
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/** Animates the preview frame around a code-authored screen navigation. */
|
|
354
|
+
export function runPrototypeNavigationTransition(
|
|
355
|
+
transition: MotionPrototypeNavigationTransition | undefined,
|
|
356
|
+
update: () => void,
|
|
357
|
+
documentRef: PrototypeTransitionDocument | undefined = typeof document ===
|
|
358
|
+
"undefined"
|
|
359
|
+
? undefined
|
|
360
|
+
: document,
|
|
361
|
+
options: PrototypeNavigationTransitionOptions = {},
|
|
362
|
+
): PrototypeNavigationTransitionResult {
|
|
363
|
+
const generation = documentRef ? beginTransitionRun(documentRef) : 0;
|
|
364
|
+
if (!transition) {
|
|
365
|
+
update();
|
|
366
|
+
return "instant";
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const validation = validateMotionPrototypeNavigationTransition(transition);
|
|
370
|
+
if (!validation.valid) {
|
|
371
|
+
update();
|
|
372
|
+
return "unsupported-transition";
|
|
373
|
+
}
|
|
374
|
+
const config = validation.transition;
|
|
375
|
+
if (config.transition === "instant") {
|
|
376
|
+
update();
|
|
377
|
+
return "instant";
|
|
378
|
+
}
|
|
379
|
+
if (config.transition === "smart-animate") {
|
|
380
|
+
update();
|
|
381
|
+
return "unsupported-transition";
|
|
382
|
+
}
|
|
383
|
+
if (!documentRef) {
|
|
384
|
+
update();
|
|
385
|
+
return "browser-fallback";
|
|
386
|
+
}
|
|
387
|
+
if (
|
|
388
|
+
typeof window !== "undefined" &&
|
|
389
|
+
window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
|
|
390
|
+
) {
|
|
391
|
+
update();
|
|
392
|
+
return "browser-fallback";
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const durationMs = config.durationMs!;
|
|
396
|
+
const staged =
|
|
397
|
+
config.transition === "dissolve" || config.transition === "push";
|
|
398
|
+
const exitsFirst =
|
|
399
|
+
staged ||
|
|
400
|
+
config.transition === "move-out" ||
|
|
401
|
+
config.transition === "slide-out";
|
|
402
|
+
if (exitsFirst) {
|
|
403
|
+
const exitDuration = staged ? durationMs / 2 : durationMs;
|
|
404
|
+
return animateExit(
|
|
405
|
+
config,
|
|
406
|
+
update,
|
|
407
|
+
documentRef,
|
|
408
|
+
exitDuration,
|
|
409
|
+
staged,
|
|
410
|
+
generation,
|
|
411
|
+
options,
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return animateEntry(
|
|
416
|
+
config,
|
|
417
|
+
update,
|
|
418
|
+
documentRef,
|
|
419
|
+
durationMs,
|
|
420
|
+
generation,
|
|
421
|
+
options,
|
|
422
|
+
);
|
|
423
|
+
}
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
RecentEditHighlights,
|
|
4
4
|
LiveCursorOverlay,
|
|
5
5
|
} from "@agent-native/toolkit/collab-ui";
|
|
6
|
+
import type { MotionPrototypeTransitionDecodeResult } from "@shared/motion-prototype-transition";
|
|
6
7
|
|
|
7
8
|
import { DesignCanvas } from "@/components/design/DesignCanvas";
|
|
8
9
|
import { prettyScreenName } from "@/lib/screen-names";
|
|
@@ -28,6 +29,10 @@ import type { EditorScreenInspector } from "../domains/use-editor-screen-inspect
|
|
|
28
29
|
import type { EditorScreenRendering } from "../domains/use-editor-screen-rendering";
|
|
29
30
|
import type { EditorSelectionAndStyles } from "../domains/use-editor-selection-and-styles";
|
|
30
31
|
import type { EditorToolsAndVectors } from "../domains/use-editor-tools-and-vectors";
|
|
32
|
+
import {
|
|
33
|
+
getPrototypeDestinationFrameId,
|
|
34
|
+
runPrototypeNavigationTransition,
|
|
35
|
+
} from "../prototype-navigation-transition";
|
|
31
36
|
import type { DesignData } from "../types";
|
|
32
37
|
|
|
33
38
|
export function renderSingleScreenCanvas({
|
|
@@ -550,7 +555,12 @@ export function renderSingleScreenCanvas({
|
|
|
550
555
|
}
|
|
551
556
|
nodeRewriteCanvasTarget
|
|
552
557
|
onRepromptDraftConsumed={handleRepromptDraftConsumed}
|
|
553
|
-
onPrototypeNavigate={(
|
|
558
|
+
onPrototypeNavigate={(
|
|
559
|
+
screen,
|
|
560
|
+
_href,
|
|
561
|
+
transition: MotionPrototypeTransitionDecodeResult,
|
|
562
|
+
sourceFrame: HTMLIFrameElement,
|
|
563
|
+
) => {
|
|
554
564
|
if (!screen) return;
|
|
555
565
|
const norm = (s: string) =>
|
|
556
566
|
s
|
|
@@ -561,14 +571,30 @@ export function renderSingleScreenCanvas({
|
|
|
561
571
|
if (!target) return;
|
|
562
572
|
const match = files.find((f) => norm(f.filename) === target);
|
|
563
573
|
if (match) {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
574
|
+
runPrototypeNavigationTransition(
|
|
575
|
+
transition.status === "valid" ? transition.transition : undefined,
|
|
576
|
+
() => {
|
|
577
|
+
if (mode === "interact") {
|
|
578
|
+
handleModeChange("interact", {
|
|
579
|
+
targetFileId: match.id,
|
|
580
|
+
keepInteractDevice: true,
|
|
581
|
+
fromPrototypeNavigation: true,
|
|
582
|
+
});
|
|
583
|
+
} else {
|
|
584
|
+
handleSidebarScreenSelect(match.id, {
|
|
585
|
+
fromPrototypeNavigation: true,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
},
|
|
589
|
+
undefined,
|
|
590
|
+
{
|
|
591
|
+
sourceFrame,
|
|
592
|
+
destinationFrameId: getPrototypeDestinationFrameId(
|
|
593
|
+
match.id,
|
|
594
|
+
sourceFrame,
|
|
595
|
+
),
|
|
596
|
+
},
|
|
597
|
+
);
|
|
572
598
|
}
|
|
573
599
|
}}
|
|
574
600
|
/>
|