From 1f3501070268b68fa75de2ca554fe5a0ce913035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Thu, 4 Jun 2026 11:48:12 -0400 Subject: [PATCH 01/11] feat(studio): add grid overlay component and snap preferences Add snapEnabled, gridVisible, gridSpacing, snapToGrid fields to studioUiPreferences for persistence. Create GridOverlay component using CSS repeating-linear-gradient for zero-JS-cost grid rendering. --- .../src/components/editor/GridOverlay.tsx | 50 +++++++++++++++++++ .../studio/src/utils/studioUiPreferences.ts | 17 +++++++ 2 files changed, 67 insertions(+) create mode 100644 packages/studio/src/components/editor/GridOverlay.tsx diff --git a/packages/studio/src/components/editor/GridOverlay.tsx b/packages/studio/src/components/editor/GridOverlay.tsx new file mode 100644 index 0000000000..1ca973a806 --- /dev/null +++ b/packages/studio/src/components/editor/GridOverlay.tsx @@ -0,0 +1,50 @@ +// fallow-ignore-file unused-file +import { memo } from "react"; + +interface GridOverlayProps { + visible: boolean; + spacing: number; + scaleX: number; + scaleY: number; + compositionLeft: number; + compositionTop: number; + compositionWidth: number; + compositionHeight: number; +} + +// fallow-ignore-next-line complexity +export const GridOverlay = memo(function GridOverlay({ + visible, + spacing, + scaleX, + scaleY, + compositionLeft, + compositionTop, + compositionWidth, + compositionHeight, +}: GridOverlayProps) { + if (!visible || spacing <= 0) return null; + + const overlaySpacingX = spacing * scaleX; + const overlaySpacingY = spacing * scaleY; + + if (overlaySpacingX < 4 || overlaySpacingY < 4) return null; + + return ( + + {element.capabilities.canApplyManualSize && ( +
+ +
+ )} {STUDIO_GSAP_PANEL_ENABLED && From feff5b1d35a47ca950c0ee8bb5bd38cfad8a4fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Thu, 4 Jun 2026 19:50:41 -0400 Subject: [PATCH 09/11] fix(studio): replace fit-content button with inline Fixed/Fit toggle Segmented control between W/H fields and Z-index instead of standalone button. Matches the visual language of the rest of the property panel. --- .../src/components/editor/PropertyPanel.tsx | 67 +++++++++++-------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/packages/studio/src/components/editor/PropertyPanel.tsx b/packages/studio/src/components/editor/PropertyPanel.tsx index 2cc9a21174..0fec0dc737 100644 --- a/packages/studio/src/components/editor/PropertyPanel.tsx +++ b/packages/studio/src/components/editor/PropertyPanel.tsx @@ -354,6 +354,46 @@ export const PropertyPanel = memo(function PropertyPanel({ onCommit={(next) => commitManualRotation(next.replace("°", ""))} /> + {element.capabilities.canApplyManualSize && ( +
+ Size +
+ + +
+
+ )}
onSetStyle("z-index", next)} />
- {element.capabilities.canApplyManualSize && ( -
- -
- )} {STUDIO_GSAP_PANEL_ENABLED && From ceb6c204e244f14a13691f8ea26ff274054d8c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Thu, 4 Jun 2026 19:55:52 -0400 Subject: [PATCH 10/11] fix(studio): replace fit-content toggle with icon button and fix sizing --- .../src/components/editor/PropertyPanel.tsx | 135 ++++++++++-------- 1 file changed, 78 insertions(+), 57 deletions(-) diff --git a/packages/studio/src/components/editor/PropertyPanel.tsx b/packages/studio/src/components/editor/PropertyPanel.tsx index 0fec0dc737..c2c751ab47 100644 --- a/packages/studio/src/components/editor/PropertyPanel.tsx +++ b/packages/studio/src/components/editor/PropertyPanel.tsx @@ -1,7 +1,12 @@ import { memo } from "react"; import { Clock, Eye, Layers, MessageSquare, Move, X } from "../../icons/SystemIcons"; import { type DomEditSelection } from "./domEditing"; -import { readStudioBoxSize, readStudioPathOffset, readStudioRotation } from "./manualEdits"; +import { + readStudioBoxSize, + readStudioPathOffset, + readStudioRotation, + clearStudioBoxSize, +} from "./manualEdits"; import type { ImportedFontAsset } from "./fontAssets"; import { EMPTY_STYLES, @@ -334,67 +339,83 @@ export const PropertyPanel = memo(function PropertyPanel({ scrub onCommit={(next) => commitManualOffset("y", next)} /> - commitManualSize("width", next)} - /> - commitManualSize("height", next)} - /> + +
+
+ commitManualSize("width", next)} + /> +
+
+ commitManualSize("height", next)} + /> +
+ {element.capabilities.canApplyManualSize && ( + + )} +
+
commitManualRotation(next.replace("°", ""))} /> -
- {element.capabilities.canApplyManualSize && ( -
- Size -
- - -
-
- )} -
Date: Thu, 4 Jun 2026 20:06:58 -0400 Subject: [PATCH 11/11] fix(studio): fit-to-children uses BCR union and snap commit uses snapped deltas --- .../src/components/editor/PropertyPanel.tsx | 58 +++++++++---------- .../editor/domEditOverlayGestures.ts | 4 ++ .../editor/useDomEditOverlayGestures.ts | 16 +++-- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/packages/studio/src/components/editor/PropertyPanel.tsx b/packages/studio/src/components/editor/PropertyPanel.tsx index c2c751ab47..0dc4e873d6 100644 --- a/packages/studio/src/components/editor/PropertyPanel.tsx +++ b/packages/studio/src/components/editor/PropertyPanel.tsx @@ -1,12 +1,7 @@ import { memo } from "react"; import { Clock, Eye, Layers, MessageSquare, Move, X } from "../../icons/SystemIcons"; import { type DomEditSelection } from "./domEditing"; -import { - readStudioBoxSize, - readStudioPathOffset, - readStudioRotation, - clearStudioBoxSize, -} from "./manualEdits"; +import { readStudioBoxSize, readStudioPathOffset, readStudioRotation } from "./manualEdits"; import type { ImportedFontAsset } from "./fontAssets"; import { EMPTY_STYLES, @@ -213,9 +208,6 @@ export const PropertyPanel = memo(function PropertyPanel({ const manualOffsetEditingDisabled = !element.capabilities.canApplyManualOffset; const manualSizeEditingDisabled = !element.capabilities.canApplyManualSize; const sourceLabel = element.id ? `#${element.id}` : element.selector; - const isFitWidth = styles.width === "fit-content"; - const isFitHeight = styles.height === "fit-content"; - const isFitContent = isFitWidth && isFitHeight; const showEditableSections = element.capabilities.canEditStyles; const manualOffset = readStudioPathOffset(element.element); const manualSize = readStudioBoxSize(element.element); @@ -344,43 +336,47 @@ export const PropertyPanel = memo(function PropertyPanel({
commitManualSize("width", next)} />
commitManualSize("height", next)} />
{element.capabilities.canApplyManualSize && (