@hyperframes/studio 0.7.56 → 0.7.58
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/dist/assets/hyperframes-player-CtTDO63S.js +459 -0
- package/dist/assets/{index-CmVCjZjp.js → index-B_UvTX3E.js} +205 -205
- package/dist/assets/{index-BWnOxAiH.js → index-C47jAC3Q.js} +1 -1
- package/dist/assets/{index-C4csZims.js → index-DeQPzqwH.js} +1 -1
- package/dist/assets/index-uahwWkgw.css +1 -0
- package/dist/{chunk-5QSIMBEJ.js → chunk-OBAG3GWK.js} +33 -21
- package/dist/chunk-OBAG3GWK.js.map +1 -0
- package/dist/{domEditingLayers-6LQGKPOI.js → domEditingLayers-2CKWGEHS.js} +2 -2
- package/dist/index.d.ts +41 -4
- package/dist/index.html +2 -2
- package/dist/index.js +7436 -5423
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/App.tsx +3 -3
- package/src/components/StudioFeedbackBar.tsx +2 -2
- package/src/components/editor/CanvasContextMenu.test.tsx +166 -2
- package/src/components/editor/CanvasContextMenu.tsx +100 -20
- package/src/components/editor/DomEditOverlay.tsx +34 -62
- package/src/components/editor/LayersPanel.tsx +111 -91
- package/src/components/editor/canvasContextMenuZOrder.test.ts +243 -2
- package/src/components/editor/canvasContextMenuZOrder.ts +184 -23
- package/src/components/editor/domEditingDom.ts +5 -6
- package/src/components/editor/domEditingElement.ts +15 -32
- package/src/components/editor/layersPanelSort.ts +80 -0
- package/src/components/editor/propertyPanelColorGradingSection.tsx +15 -21
- package/src/components/editor/useCanvasContextMenuState.ts +92 -0
- package/src/components/editor/useLayerRevealOverride.test.ts +169 -0
- package/src/components/editor/useLayerRevealOverride.ts +389 -0
- package/src/components/editor/useZOrderCrossedFlash.tsx +68 -0
- package/src/components/nle/AssetPreviewOverlay.test.tsx +106 -0
- package/src/components/nle/AssetPreviewOverlay.tsx +61 -37
- package/src/components/nle/PreviewOverlays.tsx +61 -16
- package/src/components/nle/TimelinePane.test.ts +3 -0
- package/src/components/nle/TimelinePane.tsx +11 -1
- package/src/components/nle/useCanvasZOrderTimelineMirror.test.tsx +440 -0
- package/src/components/nle/useCanvasZOrderTimelineMirror.ts +191 -0
- package/src/components/nle/useTimelineEditCallbacks.ts +1 -0
- package/src/components/nle/zLaneGesture.test.ts +176 -0
- package/src/components/nle/zLaneGesture.ts +77 -0
- package/src/components/sidebar/AssetCard.test.tsx +106 -0
- package/src/components/sidebar/AssetCard.tsx +19 -2
- package/src/components/sidebar/AudioRow.tsx +19 -2
- package/src/hooks/domEditCommitTypes.ts +29 -2
- package/src/hooks/elementLifecycleOpsTestUtils.ts +25 -0
- package/src/hooks/fetchStubTestUtils.ts +16 -0
- package/src/hooks/timelineEditingHelpers.test.ts +183 -1
- package/src/hooks/timelineEditingHelpers.ts +63 -259
- package/src/hooks/timelineMoveAdapter.test.ts +44 -40
- package/src/hooks/timelineMoveAdapter.ts +7 -4
- package/src/hooks/timelineTimingSync.test.ts +820 -0
- package/src/hooks/timelineTimingSync.ts +597 -0
- package/src/hooks/timelineTrackVisibility.test.ts +3 -1
- package/src/hooks/timelineTrackVisibility.ts +4 -3
- package/src/hooks/useCaptionDetection.ts +2 -0
- package/src/hooks/useClipboard.ts +19 -17
- package/src/hooks/useCompositionDimensions.ts +32 -5
- package/src/hooks/useDomEditCommits.test.tsx +392 -23
- package/src/hooks/useDomEditCommits.ts +180 -53
- package/src/hooks/useDomEditSession.test.tsx +3 -1
- package/src/hooks/useDomEditSession.ts +1 -1
- package/src/hooks/useElementLifecycleOps.test.tsx +559 -28
- package/src/hooks/useElementLifecycleOps.ts +175 -69
- package/src/hooks/useElementPicker.ts +7 -8
- package/src/hooks/usePreviewPersistence.ts +2 -2
- package/src/hooks/useTimelineAssetDropOps.ts +175 -0
- package/src/hooks/useTimelineEditing.test.tsx +591 -277
- package/src/hooks/useTimelineEditing.ts +202 -282
- package/src/hooks/useTimelineEditingTypes.ts +1 -1
- package/src/hooks/useTimelineGroupEditing.ts +187 -134
- package/src/player/components/PlayerControls.tsx +5 -5
- package/src/player/components/PlayheadIndicator.tsx +7 -2
- package/src/player/components/Timeline.test.ts +105 -18
- package/src/player/components/Timeline.tsx +69 -98
- package/src/player/components/TimelineCanvas.tsx +47 -7
- package/src/player/components/TimelineLanes.tsx +32 -10
- package/src/player/components/TimelineOverlays.tsx +36 -0
- package/src/player/components/TimelineRuler.tsx +70 -47
- package/src/player/components/TrackGapContextMenu.tsx +116 -0
- package/src/player/components/timelineCallbacks.ts +4 -1
- package/src/player/components/timelineClipDragCommit.test.ts +370 -11
- package/src/player/components/timelineClipDragCommit.ts +267 -68
- package/src/player/components/timelineGapCommit.test.ts +163 -0
- package/src/player/components/timelineGapCommit.ts +103 -0
- package/src/player/components/timelineGaps.test.ts +221 -0
- package/src/player/components/timelineGaps.ts +182 -0
- package/src/player/components/timelineLayout.test.ts +2 -1
- package/src/player/components/timelineLayout.ts +119 -30
- package/src/player/components/timelineMarquee.test.ts +20 -10
- package/src/player/components/timelineMarquee.ts +9 -2
- package/src/player/components/timelineRevealScroll.test.ts +133 -0
- package/src/player/components/timelineRevealScroll.ts +91 -0
- package/src/player/components/timelineStackingSync.test.ts +102 -1
- package/src/player/components/timelineStackingSync.ts +45 -5
- package/src/player/components/timelineTrackPersistPipeline.test.ts +168 -0
- package/src/player/components/timelineZMirror.test.ts +667 -0
- package/src/player/components/timelineZMirror.ts +393 -0
- package/src/player/components/timelineZones.test.ts +122 -310
- package/src/player/components/timelineZones.ts +82 -135
- package/src/player/components/timelineZoom.test.ts +2 -2
- package/src/player/components/timelineZoom.ts +7 -3
- package/src/player/components/useTimelineClipDrag.ts +1 -1
- package/src/player/components/useTimelineGapHighlights.test.ts +119 -0
- package/src/player/components/useTimelineGapHighlights.ts +110 -0
- package/src/player/components/useTimelinePlayhead.ts +4 -3
- package/src/player/components/useTimelineRangeSelection.ts +6 -4
- package/src/player/components/useTimelineRevealClip.ts +56 -0
- package/src/player/components/useTimelineScrollViewport.ts +73 -0
- package/src/player/components/useTimelineStackingSync.test.tsx +11 -2
- package/src/player/components/useTimelineStackingSync.ts +17 -4
- package/src/player/components/useTimelineTrackDerivations.ts +38 -0
- package/src/player/components/useTrackGapMenu.ts +155 -0
- package/src/player/hooks/useExpandedTimelineElements.test.ts +67 -4
- package/src/player/hooks/useExpandedTimelineElements.ts +19 -1
- package/src/player/hooks/useTimelinePlayer.seek.test.ts +55 -0
- package/src/player/hooks/useTimelinePlayer.test.ts +36 -3
- package/src/player/hooks/useTimelinePlayer.ts +4 -0
- package/src/player/hooks/useTimelineSyncCallbacks.ts +11 -1
- package/src/player/index.ts +3 -1
- package/src/player/lib/layerOrdering.test.ts +1 -15
- package/src/player/lib/layerOrdering.ts +5 -28
- package/src/player/lib/runtimeProtocol.test.ts +48 -0
- package/src/player/lib/runtimeProtocol.ts +65 -0
- package/src/player/lib/timelineDOM.test.ts +72 -0
- package/src/player/lib/timelineDOM.ts +12 -0
- package/src/player/lib/timelineElementHelpers.ts +54 -11
- package/src/player/lib/timelineIframeHelpers.ts +2 -4
- package/src/player/store/playerStore.test.ts +38 -13
- package/src/player/store/playerStore.ts +49 -1
- package/src/telemetry/events.test.ts +10 -0
- package/src/telemetry/events.ts +1 -0
- package/src/utils/assetPreviewDismiss.test.ts +27 -0
- package/src/utils/assetPreviewDismiss.ts +29 -0
- package/src/utils/domEditSaveQueue.ts +4 -3
- package/src/utils/editHistory.test.ts +82 -0
- package/src/utils/editHistory.ts +14 -2
- package/src/utils/gsapSoftReload.test.ts +54 -115
- package/src/utils/gsapSoftReload.ts +42 -152
- package/src/utils/gsapUndoRestore.test.ts +269 -0
- package/src/utils/gsapUndoRestore.ts +269 -0
- package/src/utils/sourceScopedSelectorIndex.ts +29 -0
- package/src/utils/timelineAssetDrop.test.ts +144 -52
- package/src/utils/timelineAssetDrop.ts +62 -86
- package/dist/assets/hyperframes-player-BBrKzTGd.js +0 -459
- package/dist/assets/index-D-GyYi2d.css +0 -1
- package/dist/chunk-5QSIMBEJ.js.map +0 -1
- /package/dist/{domEditingLayers-6LQGKPOI.js.map → domEditingLayers-2CKWGEHS.js.map} +0 -0
|
@@ -28,7 +28,6 @@ import { useDomEditTextCommits } from "./useDomEditTextCommits";
|
|
|
28
28
|
import { useDomGeometryCommits } from "./useDomGeometryCommits";
|
|
29
29
|
import { useElementLifecycleOps } from "./useElementLifecycleOps";
|
|
30
30
|
import { formatFieldsSuffix } from "./gsapScriptCommitHelpers";
|
|
31
|
-
import { readProjectFileContent } from "../utils/studioFileHistory";
|
|
32
31
|
|
|
33
32
|
// ── Helpers ──
|
|
34
33
|
|
|
@@ -61,37 +60,143 @@ interface RecordEditInput {
|
|
|
61
60
|
files: Record<string, { before: string; after: string }>;
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
63
|
+
/** Human-readable identifier for a batch patch target (for the unmatched warning). */
|
|
64
|
+
function describeBatchPatchTarget(patch: DomEditPatchBatch["patches"][number]): string {
|
|
65
|
+
return patch.target.id ?? patch.target.hfId ?? patch.target.selector ?? "(unaddressed)";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Surface server-reported unmatched patches. The server atomically refuses the
|
|
70
|
+
* whole multi-file gesture; the caller uses `durable: false` to roll back and
|
|
71
|
+
* reload, so report the refusal without turning it into a second failure.
|
|
72
|
+
*/
|
|
73
|
+
function reportUnmatchedBatchPatches(batch: DomEditPatchBatch, matched: boolean[]): void {
|
|
74
|
+
const unmatchedIds = batch.patches
|
|
75
|
+
.filter((_, index) => matched[index] === false)
|
|
76
|
+
.map(describeBatchPatchTarget);
|
|
77
|
+
if (unmatchedIds.length === 0) return;
|
|
78
|
+
console.warn(
|
|
79
|
+
`[studio] z-index reorder: server could not match ${unmatchedIds.length} patch target(s) in ` +
|
|
80
|
+
`${batch.sourceFile} (the whole z-order gesture will revert on reload):`,
|
|
81
|
+
unmatchedIds.join(", "),
|
|
73
82
|
);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
83
|
+
trackStudioSaveFailure({
|
|
84
|
+
source: "dom_edit",
|
|
85
|
+
error: new Error(`Batch patch target(s) unmatched: ${unmatchedIds.join(", ")}`),
|
|
86
|
+
filePath: batch.sourceFile,
|
|
87
|
+
mutationType: "z-reorder-unmatched",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
interface AtomicElementPatchFile {
|
|
92
|
+
sourceFile: string;
|
|
93
|
+
changed: boolean;
|
|
94
|
+
matched?: boolean[];
|
|
95
|
+
before: string;
|
|
96
|
+
after: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
class AtomicElementPatchConvergenceError extends Error {
|
|
100
|
+
constructor(message: string, options?: { cause?: unknown }) {
|
|
101
|
+
super(message, options);
|
|
102
|
+
this.name = "AtomicElementPatchConvergenceError";
|
|
77
103
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Keep the atomic response contract in one guard so callers do not compose validity.
|
|
107
|
+
// fallow-ignore-next-line complexity
|
|
108
|
+
function isAtomicElementPatchFile(value: unknown): value is AtomicElementPatchFile {
|
|
109
|
+
return (
|
|
110
|
+
typeof value === "object" &&
|
|
111
|
+
value !== null &&
|
|
112
|
+
"sourceFile" in value &&
|
|
113
|
+
typeof value.sourceFile === "string" &&
|
|
114
|
+
"changed" in value &&
|
|
115
|
+
typeof value.changed === "boolean" &&
|
|
116
|
+
(!("matched" in value) ||
|
|
117
|
+
(Array.isArray(value.matched) &&
|
|
118
|
+
value.matched.every((matched) => typeof matched === "boolean"))) &&
|
|
119
|
+
"before" in value &&
|
|
120
|
+
typeof value.before === "string" &&
|
|
121
|
+
"after" in value &&
|
|
122
|
+
typeof value.after === "string" &&
|
|
123
|
+
value.changed === (value.before !== value.after)
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// This is the single client owner for dispatching and validating the aggregate
|
|
128
|
+
// atomic endpoint. Splitting validation from the request would weaken that wire contract.
|
|
129
|
+
// fallow-ignore-next-line complexity
|
|
130
|
+
async function patchElementBatches(projectId: string, batches: DomEditPatchBatch[]) {
|
|
131
|
+
const body = JSON.stringify({ batches });
|
|
132
|
+
try {
|
|
133
|
+
const response = await fetch(
|
|
134
|
+
`/api/projects/${encodeURIComponent(projectId)}/file-mutations/patch-element-batches`,
|
|
135
|
+
{
|
|
136
|
+
method: "POST",
|
|
137
|
+
headers: { "Content-Type": "application/json" },
|
|
138
|
+
body,
|
|
139
|
+
},
|
|
140
|
+
);
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
const rejection = await readErrorResponseBody(response);
|
|
143
|
+
throw new StudioSaveHttpError(formatPatchRejectionMessage(rejection), response.status);
|
|
144
|
+
}
|
|
145
|
+
const result: unknown = await response.json().catch(() => null);
|
|
146
|
+
if (
|
|
147
|
+
typeof result !== "object" ||
|
|
148
|
+
result === null ||
|
|
149
|
+
!("durable" in result) ||
|
|
150
|
+
typeof result.durable !== "boolean" ||
|
|
151
|
+
!("files" in result) ||
|
|
152
|
+
!Array.isArray(result.files) ||
|
|
153
|
+
result.files.length !== batches.length ||
|
|
154
|
+
!result.files.every(isAtomicElementPatchFile) ||
|
|
155
|
+
(!result.durable && result.files.some((file) => file.changed))
|
|
156
|
+
) {
|
|
157
|
+
throw new StudioSaveHttpError("Invalid atomic element patch response", 502);
|
|
158
|
+
}
|
|
159
|
+
const files = result.files.map((file, index) => {
|
|
160
|
+
const batch = batches[index];
|
|
161
|
+
const matched = file.matched ?? [];
|
|
162
|
+
if (
|
|
163
|
+
!batch ||
|
|
164
|
+
file.sourceFile !== batch.sourceFile ||
|
|
165
|
+
(matched.length !== 0 && matched.length !== batch.patches.length)
|
|
166
|
+
) {
|
|
167
|
+
throw new StudioSaveHttpError("Invalid atomic element patch response", 502);
|
|
168
|
+
}
|
|
169
|
+
reportUnmatchedBatchPatches(batch, matched);
|
|
170
|
+
return {
|
|
171
|
+
...file,
|
|
172
|
+
matched,
|
|
173
|
+
allMatched: matched.length === batch.patches.length && matched.every(Boolean),
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
return { durable: result.durable, files };
|
|
177
|
+
} catch (error) {
|
|
178
|
+
throw new AtomicElementPatchConvergenceError(getErrorDetail(error), { cause: error });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* A batch is reload-skippable only when it is style-only: every operation is an
|
|
184
|
+
* `inline-style` write. The z-reorder commit applies those exact styles to the
|
|
185
|
+
* live iframe DOM synchronously, so persisting them adds nothing the preview
|
|
186
|
+
* doesn't already show. Any other op type (attribute / text-content / …) can
|
|
187
|
+
* have server-side semantics the live DOM hasn't mirrored — reload for those.
|
|
188
|
+
*/
|
|
189
|
+
function batchesAreInlineStyleOnly(batches: DomEditPatchBatch[]): boolean {
|
|
190
|
+
return batches.every((batch) =>
|
|
191
|
+
batch.patches.every((patch) => patch.operations.every((op) => op.type === "inline-style")),
|
|
192
|
+
);
|
|
88
193
|
}
|
|
89
194
|
|
|
90
195
|
export interface UseDomEditCommitsParams {
|
|
91
196
|
activeCompPath: string | null;
|
|
92
197
|
previewIframeRef: React.MutableRefObject<HTMLIFrameElement | null>;
|
|
93
198
|
showToast: (message: string, tone?: "error" | "info") => void;
|
|
94
|
-
queueDomEditSave: (save: () => Promise<
|
|
199
|
+
queueDomEditSave: <T>(save: () => Promise<T>) => Promise<T>;
|
|
95
200
|
writeProjectFile: (path: string, content: string) => Promise<void>;
|
|
96
201
|
domEditSaveTimestampRef: React.MutableRefObject<number>;
|
|
97
202
|
editHistory: { recordEdit: (entry: RecordEditInput) => Promise<void> };
|
|
@@ -331,37 +436,59 @@ export function useDomEditCommits({
|
|
|
331
436
|
|
|
332
437
|
const commitDomEditPatchBatches: CommitDomEditPatchBatches = useCallback(
|
|
333
438
|
(batches, options) =>
|
|
334
|
-
queueDomEditSave(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
`DOM patch contains unsafe values: ${formatUnsafeFieldList(unsafeFields)}`,
|
|
344
|
-
{ alreadyToasted: true },
|
|
439
|
+
queueDomEditSave(
|
|
440
|
+
// One queued transaction owns validation, persistence, history, reload,
|
|
441
|
+
// and its durable result; splitting those phases risks partial commits.
|
|
442
|
+
// fallow-ignore-next-line complexity
|
|
443
|
+
async () => {
|
|
444
|
+
const pid = projectIdRef.current;
|
|
445
|
+
if (!pid) throw new Error("No active project");
|
|
446
|
+
const unsafeFields = batches.flatMap((batch) =>
|
|
447
|
+
batch.patches.flatMap((patch) => findUnsafeDomPatchValues(patch)),
|
|
345
448
|
);
|
|
346
|
-
|
|
449
|
+
if (unsafeFields.length > 0) {
|
|
450
|
+
showToast("Couldn't save edit because it contains invalid layout values", "error");
|
|
451
|
+
throw new DomEditPersistUnsafeValueError(
|
|
452
|
+
`DOM patch contains unsafe values: ${formatUnsafeFieldList(unsafeFields)}`,
|
|
453
|
+
{ alreadyToasted: true },
|
|
454
|
+
);
|
|
455
|
+
}
|
|
347
456
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
.
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
457
|
+
domEditSaveTimestampRef.current = Date.now();
|
|
458
|
+
const atomicResult = await patchElementBatches(pid, batches);
|
|
459
|
+
const allMatched =
|
|
460
|
+
atomicResult.durable && atomicResult.files.every((result) => result.allMatched);
|
|
461
|
+
const files = Object.fromEntries(
|
|
462
|
+
atomicResult.files
|
|
463
|
+
.filter((result) => result.changed)
|
|
464
|
+
.map((result) => [result.sourceFile, { before: result.before, after: result.after }]),
|
|
465
|
+
);
|
|
466
|
+
const changed = Object.keys(files).length > 0;
|
|
467
|
+
if (changed) {
|
|
468
|
+
await editHistory.recordEdit({
|
|
469
|
+
label: options.label,
|
|
470
|
+
kind: "manual",
|
|
471
|
+
coalesceKey: options.coalesceKey,
|
|
472
|
+
coalesceMs: options.coalesceMs,
|
|
473
|
+
files,
|
|
474
|
+
});
|
|
475
|
+
forceReloadSdkSession?.();
|
|
476
|
+
}
|
|
477
|
+
const durable = allMatched;
|
|
478
|
+
// A z-only reorder already applied its inline styles to the live iframe
|
|
479
|
+
// DOM (and the store) synchronously, so remounting the iframe here only
|
|
480
|
+
// produces a visible blink. Skip the reload when the caller asked for it
|
|
481
|
+
// AND the persist is provably in sync: style-only ops, every target
|
|
482
|
+
// matched. Any unmatched patch means the live DOM now shows state disk
|
|
483
|
+
// doesn't hold — reload so the preview reconverges. (The SSE/file-watcher
|
|
484
|
+
// reload is independently suppressed by domEditSaveTimestampRef above.)
|
|
485
|
+
const skipSafe =
|
|
486
|
+
options.skipReload === true && batchesAreInlineStyleOnly(batches) && durable;
|
|
487
|
+
if (!durable || (changed && !skipSafe)) reloadPreview();
|
|
488
|
+
return { durable, allMatched, changed };
|
|
489
|
+
},
|
|
490
|
+
).catch((error) => {
|
|
491
|
+
if (error instanceof AtomicElementPatchConvergenceError) reloadPreview();
|
|
365
492
|
const alreadyToasted =
|
|
366
493
|
(error instanceof StudioSaveHttpError ||
|
|
367
494
|
error instanceof DomEditPersistUnsafeValueError) &&
|
|
@@ -231,7 +231,9 @@ describe("onReorderShadow source filter", () => {
|
|
|
231
231
|
setRightPanelTab: vi.fn(),
|
|
232
232
|
showToast: vi.fn(),
|
|
233
233
|
refreshPreviewDocumentVersion: vi.fn(),
|
|
234
|
-
queueDomEditSave: vi.fn(async (save: () => Promise<
|
|
234
|
+
queueDomEditSave: vi.fn(async <T,>(save: () => Promise<T>) => save()) as <T>(
|
|
235
|
+
save: () => Promise<T>,
|
|
236
|
+
) => Promise<T>,
|
|
235
237
|
readProjectFile,
|
|
236
238
|
writeProjectFile: vi.fn(async () => {}),
|
|
237
239
|
updateEditingFileContent: vi.fn(),
|
|
@@ -43,7 +43,7 @@ export interface UseDomEditSessionParams {
|
|
|
43
43
|
setRightPanelTab: (tab: RightPanelTab) => void;
|
|
44
44
|
showToast: (message: string, tone?: "error" | "info") => void;
|
|
45
45
|
refreshPreviewDocumentVersion: () => void;
|
|
46
|
-
queueDomEditSave: (save: () => Promise<
|
|
46
|
+
queueDomEditSave: <T>(save: () => Promise<T>) => Promise<T>;
|
|
47
47
|
readProjectFile: (path: string) => Promise<string>;
|
|
48
48
|
writeProjectFile: (path: string, content: string) => Promise<void>;
|
|
49
49
|
updateEditingFileContent: (path: string, content: string) => void;
|