Skip to content

Commit a6bb75d

Browse files
committed
fix(studio): let a failed text or style commit report itself
`runDomEditCommit` catches a persist failure, reverts, fires `onError` and then resolves. That contract is deliberate and its docstring says so: the human path learns the write failed from the toast `onError` puts on screen, so a rejection would be redundant. It also means a caller awaiting `handleDomTextCommit` or `handleDomStyleCommit` cannot tell a landed write from a reverted one, because both resolve with `undefined`. The runner already offers `onSettled` as the way out. Text and style were the two commits that never got it wired. Add `runReportedDomEditCommit`, which owns `onSettled` (forwarding to a caller-supplied one rather than dropping it) and returns whether the write landed. Both handlers now return a tagged outcome, so the three preconditions that previously returned early and silently are each distinguishable: no selection, a manual-geometry property the style path refuses, and a selection that cannot edit styles. Same for text: no selection versus not text-editable. Human-facing behaviour is unchanged and the tests assert that: the toast still fires and the optimistic DOM change is still reverted. The callback props that carry these handlers ignore the result, so their declared type widens from `Promise<void>` to `Promise<unknown>`. That type is hand-copied in fourteen places; consolidating it is worth its own change. `useDomEditTextCommits.ts` is now 593 lines against the 600-line cap. The next change to it needs a split.
1 parent 3202f3f commit a6bb75d

18 files changed

Lines changed: 259 additions & 54 deletions

packages/studio/src/components/editor/DomEditCropHandles.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function makeEl(id: string, clip: string): HTMLElement {
3535

3636
function render(
3737
el: HTMLElement,
38-
onStyleCommit: (property: string, value: string) => Promise<void> | void = () => undefined,
38+
onStyleCommit: (property: string, value: string) => Promise<unknown> | void = () => undefined,
3939
): { root: Root; rerender: (next: HTMLElement) => void } {
4040
const host = document.createElement("div");
4141
document.body.append(host);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ interface CropGestureState {
2828
interface DomEditCropHandlesProps {
2929
selection: DomEditSelection;
3030
overlayRect: OverlayRect;
31-
onStyleCommit?: (property: string, value: string) => Promise<void> | void;
31+
onStyleCommit?: (property: string, value: string) => Promise<unknown> | void;
3232
}
3333

3434
// Hit-strip size (px) for an edge crop handle: THICKNESS extends outward from

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ interface DomEditOverlayProps {
8383
restore?: () => void,
8484
) => Promise<void> | void;
8585
onRotationCommit: (selection: DomEditSelection, next: { angle: number }) => Promise<void> | void;
86-
onStyleCommit?: (property: string, value: string) => Promise<void> | void;
86+
onStyleCommit?: (property: string, value: string) => Promise<unknown> | void;
8787
gridVisible?: boolean;
8888
gridSpacing?: number;
8989
recordingState?: GestureRecordingState;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ interface DomEditSelectionChromeProps {
123123
groupSelectionCount: number;
124124
blockedMoveRef: RefObject<BlockedMoveState | null>;
125125
gestures: GestureHandlers;
126-
onStyleCommit?: (property: string, value: string) => Promise<void> | void;
126+
onStyleCommit?: (property: string, value: string) => Promise<unknown> | void;
127127
onBoxMouseDown: (e: React.MouseEvent) => void;
128128
onBoxClick: (event: React.MouseEvent<HTMLDivElement>) => void;
129129
/** The canvas' text-editing session: what opens one, and whether one is open. */

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export function CommitField({
2121
liveCommit?: boolean;
2222
align?: "left" | "right";
2323
onPreview?: (nextValue: string) => void;
24-
onCommit: (nextValue: string) => void | Promise<void>;
24+
onCommit: (nextValue: string) => void | Promise<unknown>;
2525
}) {
2626
const [draft, setDraft] = useState(value);
2727
const valueRef = useRef(value);

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ export function LayoutZIndexRow({
181181
onSetStyle,
182182
}: {
183183
styles: Record<string, string>;
184-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
184+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
185185
}) {
186186
const zIndex = String(parseInt(styles["z-index"] || "auto", 10) || 0);
187187
return (
@@ -200,7 +200,7 @@ export function LayoutFlexBlock({
200200
disabled,
201201
}: {
202202
styles: Record<string, string>;
203-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
203+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
204204
disabled: boolean;
205205
}) {
206206
const isFlex = styles.display === "flex" || styles.display === "inline-flex";
@@ -337,7 +337,7 @@ interface FlatLayoutSectionProps
337337
> {
338338
element: DomEditSelection;
339339
styles: Record<string, string>;
340-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
340+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
341341
disabled: boolean;
342342
}
343343

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function FlatMaskInsetRows({
2727
clipPathValue: string;
2828
radiusValue: number;
2929
disabled: boolean;
30-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
30+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
3131
}) {
3232
const clipPathPreset = inferClipPathPreset(clipPathValue);
3333
const parsedClipInsets = parseInsetClipPathSides(clipPathValue);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export function FlatMediaSection({
3838
projectDir: string | null;
3939
element: DomEditSelection;
4040
styles: Record<string, string>;
41-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
41+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
4242
onSetAttribute: (attr: string, value: string) => void | Promise<void>;
4343
onSetHtmlAttribute: (attr: string, value: string | null) => void | Promise<void>;
4444
/** A volume lane in the timeline drives the level; the slider cannot. */

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export function FlatRow({
3535
/** Renders a trailing 10px caret-down, for select-backed rows. */
3636
dropdown?: boolean;
3737
onPreview?: (nextValue: string) => void;
38-
onCommit: (nextValue: string) => void | Promise<void>;
38+
onCommit: (nextValue: string) => void | Promise<unknown>;
3939
onReset?: () => void;
4040
}) {
4141
const track = useTrackDesignInput();

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function FlatFillFields({
5050
element: DomEditSelection;
5151
styles: Record<string, string>;
5252
assets: string[];
53-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
53+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
5454
onPreviewStyle?: (prop: string, value: string) => void;
5555
onImportAssets?: (files: FileList) => Promise<string[]>;
5656
}) {
@@ -156,7 +156,7 @@ function FlatStrokeRow({
156156
}: {
157157
styles: Record<string, string>;
158158
disabled: boolean;
159-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
159+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
160160
}) {
161161
const borderWidthValue =
162162
parsePxMetricValue(styles["border-width"] ?? "") ??
@@ -236,7 +236,7 @@ function FlatRadiusRow({
236236
styles: Record<string, string>;
237237
gsapBorderRadius?: { tl: number; tr: number; br: number; bl: number } | null;
238238
disabled: boolean;
239-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
239+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
240240
}) {
241241
const radiusValue = parseNumericValue(styles["border-radius"]) ?? 0;
242242
const radiusTL =
@@ -286,7 +286,7 @@ function FlatShadowBlendRows({
286286
}: {
287287
styles: Record<string, string>;
288288
disabled: boolean;
289-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
289+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
290290
}) {
291291
const boxShadowPreset = inferBoxShadowPreset(styles["box-shadow"]);
292292
const blendValue = styles["mix-blend-mode"] || "normal";
@@ -332,7 +332,7 @@ function FlatBlurSliders({
332332
}: {
333333
styles: Record<string, string>;
334334
disabled: boolean;
335-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
335+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
336336
}) {
337337
const filterBlurValue = getCssFilterFunctionPx(styles.filter, "blur");
338338
const backdropBlurValue = getCssFilterFunctionPx(styles["backdrop-filter"], "blur");
@@ -378,7 +378,7 @@ function FlatOverflowMaskRows({
378378
}: {
379379
styles: Record<string, string>;
380380
disabled: boolean;
381-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
381+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
382382
}) {
383383
const radiusValue = parseNumericValue(styles["border-radius"]) ?? 0;
384384
const clipPathValue = styles["clip-path"] || "none";
@@ -432,7 +432,7 @@ function FlatOpacitySlider({
432432
}: {
433433
styles: Record<string, string>;
434434
disabled: boolean;
435-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
435+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
436436
}) {
437437
const opacityValue = Math.round((parseNumericValue(styles.opacity) ?? 1) * 100);
438438

@@ -464,7 +464,7 @@ export function FlatStyleSection({
464464
element: DomEditSelection;
465465
styles: Record<string, string>;
466466
assets: string[];
467-
onSetStyle: (prop: string, value: string) => void | Promise<void>;
467+
onSetStyle: (prop: string, value: string) => void | Promise<unknown>;
468468
onPreviewStyle?: (prop: string, value: string) => void;
469469
onImportAssets?: (files: FileList) => Promise<string[]>;
470470
gsapBorderRadius?: { tl: number; tr: number; br: number; bl: number } | null;

0 commit comments

Comments
 (0)