Skip to content

Commit 2377f9b

Browse files
committed
feat(studio): persist positions directly to HTML; remove JSON sidecar and manual positioning toggle
Replace the `.hyperframes/studio-manual-edits.json` sidecar with inline-style persistence baked directly into the HTML source. Drag/resize/rotation values are written as CSS custom properties (`--hf-studio-offset-x/y`, `--hf-studio-width/height`, `--hf-studio-rotation`) plus `translate`/`width`/`height`/`rotate` inline styles via `persistDomEditOperations` — no re-apply step needed on load. Key changes: - `sourcePatcher`: add `value: string | null` to `PatchOperation` — null removes the property/attribute from the HTML tag instead of setting it - `manualEditsDom`: add `build*Patches` / `buildClear*Patches` helpers that capture live element state into `PatchOperation[]` for HTML source writes; add `reapplyPositionEditsAfterSeek` (DOM-query-based seek hook, queries data-attribute markers) - `manualEdits.ts`: remove `applyStudioManualEditManifest` and all manifest target resolution; export `reapplyPositionEditsAfterSeek`; keep seek/play wrap infrastructure - `useManifestPersistence`: remove all JSON I/O — no disk read on load, no manifest state, no toggle state; `applyCurrentStudioManualEditsToPreview` now only installs seek hooks via `reapplyPositionEditsAfterSeek` - `useDomEditCommits`: replace `commitStudioManualEditManifestOptimistically` calls with direct DOM apply + `commitPositionPatchToHtml` (queued HTML patch write, skipRefresh) - `DomEditOverlay`: remove `manualEditsEnabled` prop; revert all `canMove || manualEditsEnabled` gates to just `canApplyManualOffset` — every draggable element is always draggable - `PropertyPanel`: remove `ManualPositioningToggle` component and all toggle props - `manualEditsParsing/manualEditsTypes`: remove manifest types, upsert functions, and `STUDIO_MANUAL_EDITS_PATH`; keep `finiteNumber`, `readStudioFileChangePath`, `roundRotationAngle`, and snapshot/CSS-property types
1 parent 81cba66 commit 2377f9b

19 files changed

Lines changed: 562 additions & 1334 deletions

.filesize-allowlist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
packages/studio/src/player/hooks/useTimelinePlayer.ts
22
packages/studio/src/hooks/useManifestPersistence.ts
33
packages/studio/src/player/components/PlayerControls.tsx
4+
packages/studio/src/components/editor/manualEdits.test.ts
5+
packages/studio/src/components/editor/manualEditsDom.ts

packages/studio/src/App.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,9 @@ export function StudioApp() {
169169
setRightPanelTab: panelLayout.setRightPanelTab,
170170
showToast,
171171
refreshPreviewDocumentVersion,
172-
commitStudioManualEditManifestOptimistically:
173-
manifestPersistence.commitStudioManualEditManifestOptimistically,
172+
queueDomEditSave: manifestPersistence.queueDomEditSave,
174173
commitStudioMotionManifestOptimistically:
175174
manifestPersistence.commitStudioMotionManifestOptimistically,
176-
applyCurrentStudioManualEditsToPreview:
177-
manifestPersistence.applyCurrentStudioManualEditsToPreview,
178175
applyCurrentStudioMotionToPreview: manifestPersistence.applyCurrentStudioMotionToPreview,
179176
readProjectFile: fileManager.readProjectFile,
180177
writeProjectFile: fileManager.writeProjectFile,
@@ -192,8 +189,6 @@ export function StudioApp() {
192189
syncPreviewHistoryHotkey: appHotkeys.syncPreviewHistoryHotkey,
193190
reloadPreview,
194191
setRefreshKey,
195-
manualEditsEnabled: manifestPersistence.manualEditsEnabled,
196-
setManualEditsEnabled: manifestPersistence.setManualEditsEnabled,
197192
});
198193

199194
domEditSelectionBridgeRef.current = domEditSession.domEditSelection;

packages/studio/src/components/StudioPreviewArea.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ export function StudioPreviewArea({
7575
domEditHoverSelection,
7676
domEditSelection,
7777
domEditGroupSelections,
78-
manualEditsEnabled,
7978
handleTimelineElementSelect,
8079
handlePreviewCanvasMouseDown,
8180
handlePreviewCanvasPointerMove,
@@ -135,7 +134,6 @@ export function StudioPreviewArea({
135134
selection={shouldShowSelectedDomBounds ? domEditSelection : null}
136135
groupSelections={shouldShowSelectedDomBounds ? domEditGroupSelections : []}
137136
allowCanvasMovement={STUDIO_PREVIEW_MANUAL_EDITING_ENABLED}
138-
manualEditsEnabled={manualEditsEnabled}
139137
onCanvasMouseDown={handlePreviewCanvasMouseDown}
140138
onCanvasPointerMove={handlePreviewCanvasPointerMove}
141139
onCanvasPointerLeave={handlePreviewCanvasPointerLeave}

packages/studio/src/components/StudioRightPanel.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,6 @@ export function StudioRightPanel({
6464
handleDomMotionCommit,
6565
handleDomMotionClear,
6666
applyDomSelection,
67-
manualEditsEnabled,
68-
setManualEditsEnabled,
6967
} = useDomEditContext();
7068

7169
const { assets, fontAssets, handleImportFiles, handleImportFonts } = useFileManagerContext();
@@ -182,8 +180,6 @@ export function StudioRightPanel({
182180
onImportFonts={handleImportFonts}
183181
activeCompositionPath={activeCompPath}
184182
onSelectLayer={handleSelectLayer}
185-
manualEditsEnabled={manualEditsEnabled}
186-
onSetManualEditsEnabled={setManualEditsEnabled}
187183
/>
188184
) : motionPanelActive ? (
189185
<MotionPanel

packages/studio/src/components/editor/DomEditOverlay.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ describe("DomEditOverlay", () => {
113113
capabilities: {
114114
canEditText: true,
115115
canEditLayout: true,
116+
canMove: true,
116117
canApplyManualOffset: true,
117118
canApplyManualSize: false,
118119
canApplyManualRotation: false,

packages/studio/src/components/editor/DomEditOverlay.tsx

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ interface DomEditOverlayProps {
3636
groupSelections?: DomEditSelection[];
3737
hoverSelection: DomEditSelection | null;
3838
allowCanvasMovement?: boolean;
39-
manualEditsEnabled?: boolean;
4039
onCanvasMouseDown: (
4140
event: React.MouseEvent<HTMLDivElement>,
4241
options?: { preferClipAncestor?: boolean },
@@ -71,7 +70,6 @@ export const DomEditOverlay = memo(function DomEditOverlay({
7170
groupSelections = [],
7271
hoverSelection,
7372
allowCanvasMovement = true,
74-
manualEditsEnabled = false,
7573
onCanvasMouseDown,
7674
onCanvasPointerMove,
7775
onCanvasPointerLeave,
@@ -216,7 +214,6 @@ export const DomEditOverlay = memo(function DomEditOverlay({
216214
onCanvasPointerMoveRef.current(event, { preferClipAncestor: false }) ??
217215
hoverSelectionRef.current;
218216
if (!candidate?.capabilities.canApplyManualOffset) return;
219-
if (!candidate.capabilities.canMove && !manualEditsEnabled) return;
220217

221218
const overlayEl = overlayRef.current;
222219
const iframe = iframeRef.current;
@@ -310,7 +307,7 @@ export const DomEditOverlay = memo(function DomEditOverlay({
310307
cursor: allowCanvasMovement && groupCanMove ? "move" : "default",
311308
}}
312309
onPointerDown={(e) => {
313-
if (!allowCanvasMovement || e.shiftKey) return;
310+
if (!allowCanvasMovement || !groupCanMove || e.shiftKey) return;
314311
gestures.startGroupDrag(e);
315312
}}
316313
onMouseDown={suppressBoxMouseDown}
@@ -356,18 +353,13 @@ export const DomEditOverlay = memo(function DomEditOverlay({
356353
width: overlayRect.width,
357354
height: overlayRect.height,
358355
cursor:
359-
allowCanvasMovement &&
360-
selection.capabilities.canApplyManualOffset &&
361-
(selection.capabilities.canMove || manualEditsEnabled)
356+
allowCanvasMovement && selection.capabilities.canApplyManualOffset
362357
? "move"
363358
: "default",
364359
}}
365360
onPointerDown={(e) => {
366361
if (!allowCanvasMovement || e.shiftKey) return;
367-
if (
368-
selection.capabilities.canApplyManualOffset &&
369-
(selection.capabilities.canMove || manualEditsEnabled)
370-
) {
362+
if (selection.capabilities.canApplyManualOffset) {
371363
gestures.startGesture("drag", e);
372364
return;
373365
}

packages/studio/src/components/editor/PropertyPanel.tsx

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ interface PropertyPanelProps {
5353
onImportFonts?: (files: FileList | File[]) => Promise<ImportedFontAsset[]>;
5454
activeCompositionPath?: string | null;
5555
onSelectLayer?: (layer: DomEditLayerItem) => void;
56-
manualEditsEnabled?: boolean;
57-
onSetManualEditsEnabled?: (enabled: boolean) => void;
5856
}
5957

6058
/* ------------------------------------------------------------------ */
@@ -117,42 +115,6 @@ function LayerTree({
117115
);
118116
}
119117

120-
/* ------------------------------------------------------------------ */
121-
/* ManualPositioningToggle */
122-
/* ------------------------------------------------------------------ */
123-
124-
function ManualPositioningToggle({
125-
enabled,
126-
onToggle,
127-
}: {
128-
enabled: boolean;
129-
onToggle: (enabled: boolean) => void;
130-
}) {
131-
return (
132-
<div className="flex-shrink-0 border-t border-neutral-800 px-4 py-3">
133-
<div className="flex items-center justify-between">
134-
<span className="text-[11px] text-neutral-400">Manual positioning</span>
135-
<button
136-
type="button"
137-
role="switch"
138-
aria-checked={enabled}
139-
onClick={() => onToggle(!enabled)}
140-
title={enabled ? "Disable drag-to-reposition" : "Enable drag-to-reposition"}
141-
className={`relative inline-flex h-5 w-9 flex-shrink-0 rounded-full transition-colors focus-visible:outline-none ${
142-
enabled ? "bg-studio-accent" : "bg-neutral-700"
143-
}`}
144-
>
145-
<span
146-
className={`pointer-events-none mt-0.5 inline-block h-4 w-4 transform rounded-full bg-white shadow-sm transition-transform ${
147-
enabled ? "translate-x-[18px]" : "translate-x-[2px]"
148-
}`}
149-
/>
150-
</button>
151-
</div>
152-
</div>
153-
);
154-
}
155-
156118
/* ------------------------------------------------------------------ */
157119
/* PropertyPanel */
158120
/* ------------------------------------------------------------------ */
@@ -179,8 +141,6 @@ export const PropertyPanel = memo(function PropertyPanel({
179141
onImportFonts,
180142
activeCompositionPath = null,
181143
onSelectLayer,
182-
manualEditsEnabled = true,
183-
onSetManualEditsEnabled,
184144
}: PropertyPanelProps) {
185145
const styles = element?.computedStyles ?? EMPTY_STYLES;
186146

@@ -212,21 +172,12 @@ export const PropertyPanel = memo(function PropertyPanel({
212172
</>
213173
)}
214174
</div>
215-
{onSetManualEditsEnabled && (
216-
<ManualPositioningToggle
217-
enabled={manualEditsEnabled}
218-
onToggle={onSetManualEditsEnabled}
219-
/>
220-
)}
221175
</div>
222176
);
223177
}
224178

225-
const needsToggleForElement = !element.capabilities.canMove && !manualEditsEnabled;
226-
const manualOffsetEditingDisabled =
227-
!element.capabilities.canApplyManualOffset || needsToggleForElement;
228-
const manualSizeEditingDisabled =
229-
!element.capabilities.canApplyManualSize || needsToggleForElement;
179+
const manualOffsetEditingDisabled = !element.capabilities.canApplyManualOffset;
180+
const manualSizeEditingDisabled = !element.capabilities.canApplyManualSize;
230181
const sourceLabel = element.id ? `#${element.id}` : element.selector;
231182
const showEditableSections = element.capabilities.canEditStyles;
232183
const manualOffset = readStudioPathOffset(element.element);
@@ -369,7 +320,6 @@ export const PropertyPanel = memo(function PropertyPanel({
369320
<MetricField
370321
label="R"
371322
value={`${manualRotation.angle}°`}
372-
disabled={needsToggleForElement}
373323
onCommit={(next) => commitManualRotation(next.replace("°", ""))}
374324
/>
375325
</div>
@@ -394,9 +344,6 @@ export const PropertyPanel = memo(function PropertyPanel({
394344
/>
395345
)}
396346
</div>
397-
{onSetManualEditsEnabled && (
398-
<ManualPositioningToggle enabled={manualEditsEnabled} onToggle={onSetManualEditsEnabled} />
399-
)}
400347
</div>
401348
);
402349
});

0 commit comments

Comments
 (0)