Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
406 changes: 372 additions & 34 deletions templates/design/.generated/bridge/editor-chrome.generated.ts

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion templates/design/actions/update-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,16 @@ export default defineAction({
.limit(1);

if (!file) {
throw new Error(`File not found: ${id}`);
// Terminal (not transient): the row is gone or out of access scope, so a
// retry can never succeed. Mark it 404 so the client save-outbox drops the
// entry instead of retrying forever (which turns one orphaned/deleted
// screen into an update-file 500 storm — see design-save-outbox
// isTerminalSaveError).
const notFound = new Error(`File not found: ${id}`) as Error & {
status?: number;
};
notFound.status = 404;
throw notFound;
}

await assertAccess("design", file.designId, "editor");
Expand Down
31 changes: 30 additions & 1 deletion templates/design/app/components/design/DesignCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ import {
schedulePendingTextEditActivation,
} from "./design-canvas/pending-text-edit";
import { DeviceFrame } from "./DeviceFrame";
import { dndHostLog } from "./dnd-debug";
import { shapeClosingHandles } from "./multi-screen/draft-primitives";
import type {
ElementInfo,
Expand Down Expand Up @@ -304,6 +305,15 @@ ${editorChromeBridgeScript}
</script>
`;

/**
* Master switch for the Figma-parity live-reflow drag (hysteresis-stabilized
* targeting + size guard in Phase 0; transform lift/follow, live sibling
* reflow, and exact absolute commit in Phase 1). Flip to `true` to try the new
* drag feel; flip back to `false` for the previous behavior without a revert.
* Baked into the injected bridge as `__LIVE_REFLOW_ENABLED__`.
*/
const LIVE_REFLOW_ENABLED = true;

interface DesignCanvasProps {
content: string;
contentKey?: string;
Expand Down Expand Up @@ -931,6 +941,10 @@ function buildEditorChromeBridgeScript(args: {
"__RUNTIME_LAYER_SNAPSHOT_ENABLED__",
args.runtimeLayerSnapshotEnabled ? "true" : "false",
)
.replace(
"__LIVE_REFLOW_ENABLED__",
LIVE_REFLOW_ENABLED ? "true" : "false",
)
);
}

Expand Down Expand Up @@ -2106,7 +2120,11 @@ export function DesignCanvas({
"__DESIGN_CANVAS_CONTENT_OFFSET_Y__",
String(Math.round(embeddedFrame?.contentOffsetY ?? 0)),
)
.replace("__RUNTIME_LAYER_SNAPSHOT_ENABLED__", "false");
.replace("__RUNTIME_LAYER_SNAPSHOT_ENABLED__", "false")
.replace(
"__LIVE_REFLOW_ENABLED__",
LIVE_REFLOW_ENABLED ? "true" : "false",
);
// ALWAYS injected (like the other always-on bridges above) so
// MultiScreenCanvas's cross-screen drag hit-testing
// (agent-native:hit-test / agent-native:hit-test-result) resolves an
Expand Down Expand Up @@ -2479,6 +2497,12 @@ export function DesignCanvas({
const selector = String(e.data.selector || "");
const anchorSelector = String(e.data.anchorSelector || "");
const placement = String(e.data.placement || "after");
dndHostLog("recv:structure-change", {
selector,
anchorSelector,
placement,
dropMode: e.data.dropMode,
});
const requestId =
typeof e.data.requestId === "string" ? e.data.requestId : undefined;
const sourceId =
Expand Down Expand Up @@ -2546,6 +2570,11 @@ export function DesignCanvas({
: undefined,
},
);
dndHostLog("persist:result", {
applied,
requestId,
willAck: Boolean(requestId) && applied !== "pending",
});
if (requestId && applied !== "pending") {
iframeRef.current?.contentWindow?.postMessage(
{
Expand Down
15 changes: 14 additions & 1 deletion templates/design/app/components/design/MultiScreenCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ import {
} from "./canvas-primitive-style";
import { appendHitTestResponder } from "./design-canvas/hit-test";
import { DesignCanvas } from "./DesignCanvas";
import { dndHostLog } from "./dnd-debug";
import {
gradientToCss,
parseGradientCss,
Expand Down Expand Up @@ -154,6 +155,10 @@ const TRANSFORM_BADGE_MAX_WIDTH = 180;
// (frame z-order is a small per-design integer) while staying well under the
// reserved resize-handle stacking range (999_999+).
const TOP_SCREEN_Z_BOOST = 100_000;
// A live draw preview must paint above every screen — including the
// TOP_SCREEN_Z_BOOSTed active one — or its outline hides behind the opaque
// screen iframe until commit. Stays below the resize-handle range (999_999+).
const DRAFT_PREVIEW_Z = TOP_SCREEN_Z_BOOST + 50_000;
const EMPTY_SELECTED_LAYER_SELECTOR_GROUPS_BY_SCREEN: Record<
string,
string[][]
Expand Down Expand Up @@ -2269,6 +2274,13 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
return;
}

if (msg.phase !== "move") {
dndHostLog("overview:cross-screen", {
phase: msg.phase,
source: sourceScreenId,
selector: msg.selector,
});
}
if (msg.phase === "start") {
setCrossScreenSourceIsBoard(sourceScreenId === boardFileId);
crossScreenDragMsgRef.current = {
Expand Down Expand Up @@ -5167,6 +5179,7 @@ export const MultiScreenCanvas = memo(function MultiScreenCanvas({
frameGeometryWithOverrides(after, state.originFrames),
after,
);
dndHostLog("overview:frame-commit", { ids: state.targetIds });
suppressNextPick.current = true;
}
finishDrag();
Expand Down Expand Up @@ -8156,7 +8169,7 @@ function DraftPrimitiveLayer({
...frameStyleLeftTop(geometry),
width: geometry.width,
height: geometry.height,
zIndex: geometry.z ?? 40,
zIndex: preview ? DRAFT_PREVIEW_Z : (geometry.z ?? 40),
transform: geometry.rotation
? `rotate(${geometry.rotation}deg)`
: undefined,
Expand Down
Loading
Loading