@hyperframes/studio 0.7.59 → 0.7.61
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-Xvx2hkrc.js +459 -0
- package/dist/assets/{index-CrkAdJkb.js → index-2mxh_HSy.js} +201 -201
- package/dist/assets/{index-B995FG46.js → index-BCpoiv9S.js} +1 -1
- package/dist/assets/index-BhWig0mx.css +1 -0
- package/dist/assets/{index-FvzmPhfG.js → index-D-uFclFj.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/index.js +1441 -770
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/App.tsx +4 -9
- package/src/components/DesignPanelPromoteProvider.tsx +27 -1
- package/src/components/StudioHeader.tsx +2 -3
- package/src/components/StudioRightPanel.tsx +47 -28
- package/src/components/editor/PromotableControl.tsx +4 -2
- package/src/components/editor/PropertyPanel.tsx +151 -149
- package/src/components/editor/PropertyPanelFlat.tsx +44 -42
- package/src/components/editor/manualEditingAvailability.test.ts +6 -6
- package/src/components/editor/manualEditingAvailability.ts +5 -3
- package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +2 -2
- package/src/components/editor/propertyPanelFlatMotionSection.tsx +1 -1
- package/src/components/editor/propertyPanelFlatPrimitives.test.tsx +10 -0
- package/src/components/editor/propertyPanelFlatPrimitives.tsx +3 -2
- package/src/components/editor/propertyPanelFlatSelectRow.tsx +7 -1
- package/src/components/editor/propertyPanelFlatStyleHelpers.ts +0 -13
- package/src/components/editor/propertyPanelFlatStyleSections.test.tsx +21 -9
- package/src/components/editor/propertyPanelFlatStyleSections.tsx +15 -32
- package/src/components/editor/propertyPanelFlatTextSection.tsx +3 -3
- package/src/components/editor/propertyPanelInputCoverage.test.tsx +14 -0
- package/src/components/editor/propertyPanelPrimitives.tsx +9 -1
- package/src/components/storyboard/AgentChatMessageButton.test.tsx +47 -0
- package/src/components/storyboard/AgentChatMessageButton.tsx +45 -0
- package/src/components/storyboard/StoryboardFrameFocus.tsx +189 -66
- package/src/components/storyboard/StoryboardLoaded.tsx +121 -23
- package/src/components/storyboard/StoryboardReviewGuide.tsx +300 -0
- package/src/components/storyboard/StoryboardViewModeGuard.test.tsx +170 -0
- package/src/components/storyboard/storyboardReviewStage.test.ts +40 -0
- package/src/components/storyboard/storyboardReviewStage.ts +45 -0
- package/src/components/storyboard/useFrameComments.ts +22 -6
- package/src/contexts/FileManagerContext.tsx +3 -0
- package/src/contexts/PanelLayoutContext.tsx +3 -0
- package/src/contexts/ViewModeContext.tsx +60 -7
- package/src/hooks/timelineEditingHelpers.ts +2 -2
- package/src/hooks/useAddAssetAtPlayhead.test.ts +44 -0
- package/src/hooks/useAddAssetAtPlayhead.ts +21 -0
- package/src/hooks/useDomEditCommits.test.tsx +41 -1
- package/src/hooks/useDomEditCommits.ts +2 -2
- package/src/hooks/useDomEditSession.ts +1 -1
- package/src/hooks/useEditorSave.ts +1 -1
- package/src/hooks/useFileManager.projectOwnership.test.tsx +14 -3
- package/src/hooks/useFileManager.ts +76 -12
- package/src/hooks/usePanelLayout.test.ts +118 -0
- package/src/hooks/usePanelLayout.ts +24 -1
- package/src/hooks/usePreviewPersistence.ts +4 -1
- package/src/hooks/useRazorSplit.history.test.tsx +12 -4
- package/src/hooks/useRazorSplit.test.tsx +88 -0
- package/src/hooks/useRazorSplit.testHelpers.ts +3 -2
- package/src/hooks/useRazorSplit.ts +50 -16
- package/src/hooks/useSlideshowTabState.test.ts +96 -0
- package/src/hooks/useSlideshowTabState.ts +61 -0
- package/src/hooks/useTimelineEditing.ts +2 -0
- package/src/hooks/useTimelineEditingTypes.ts +2 -1
- package/src/hooks/useTimelineGroupEditing.ts +1 -1
- package/src/player/lib/playbackTypes.ts +7 -0
- package/src/player/lib/runtimeProtocol.test.ts +6 -1
- package/src/player/lib/timelineDOM.ts +8 -11
- package/src/player/lib/timelineIframeHelpers.ts +153 -107
- package/src/utils/domEditSaveQueue.test.ts +19 -0
- package/src/utils/domEditSaveQueue.ts +2 -1
- package/src/utils/sdkCutover.test.ts +21 -5
- package/src/utils/sdkEditTransaction.ts +5 -4
- package/src/utils/studioFileHistory.ts +6 -4
- package/src/utils/studioFileVersion.test.ts +36 -0
- package/src/utils/studioFileVersion.ts +23 -0
- package/src/utils/studioSaveDiagnostics.test.ts +18 -0
- package/src/utils/studioSaveDiagnostics.ts +21 -0
- package/dist/assets/hyperframes-player-Bm07FLkl.js +0 -459
- package/dist/assets/index-Dj5p8U_A.css +0 -1
- package/src/components/editor/propertyPanelFlatStyleHelpers.test.ts +0 -23
|
@@ -197,11 +197,11 @@ export const PropertyPanel = memo(function PropertyPanel(props: PropertyPanelPro
|
|
|
197
197
|
const manualSizeEditingDisabled = !element.capabilities.canApplyManualSize;
|
|
198
198
|
const manualRotationEditingDisabled = !element.capabilities.canApplyManualRotation;
|
|
199
199
|
const sourceLabel = element.id ? `#${element.id}` : (element.selector ?? "");
|
|
200
|
-
const showEditableSections = element.capabilities.canEditStyles;
|
|
201
200
|
// Capabilities are already resolved on the selection; recompute only sections,
|
|
202
201
|
// feeding the live GSAP tween count (arrives on the gsapAnimations prop, not the
|
|
203
202
|
// selection) so the Timing section shows for pure-GSAP elements with no data-start.
|
|
204
203
|
const sections = resolveEditingSections(domEditSelectionToFacts(element, gsapAnimations.length));
|
|
204
|
+
const showEditableSections = element.capabilities.canEditStyles && sections.style;
|
|
205
205
|
const manualOffset = readStudioPathOffset(element.element);
|
|
206
206
|
const manualSize = readStudioBoxSize(element.element);
|
|
207
207
|
const resolvedWidth =
|
|
@@ -385,161 +385,163 @@ export const PropertyPanel = memo(function PropertyPanel(props: PropertyPanelPro
|
|
|
385
385
|
/>
|
|
386
386
|
)}
|
|
387
387
|
|
|
388
|
-
|
|
389
|
-
<
|
|
390
|
-
<div className=
|
|
391
|
-
<div className="flex-1">
|
|
392
|
-
<
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
388
|
+
{sections.layout && (
|
|
389
|
+
<Section title="Layout" icon={<Move size={15} />}>
|
|
390
|
+
<div className={RESPONSIVE_GRID}>
|
|
391
|
+
<div className="flex items-center gap-1">
|
|
392
|
+
<div className="flex-1">
|
|
393
|
+
<MetricField
|
|
394
|
+
label="X"
|
|
395
|
+
value={formatPxMetricValue(displayX)}
|
|
396
|
+
disabled={manualOffsetEditingDisabled}
|
|
397
|
+
scrub
|
|
398
|
+
onCommit={(next) => commitManualOffset("x", next)}
|
|
399
|
+
/>
|
|
400
|
+
</div>
|
|
401
|
+
{STUDIO_KEYFRAMES_ENABLED && gsapAnimId && (
|
|
402
|
+
<KeyframeNavigation
|
|
403
|
+
property="x"
|
|
404
|
+
keyframes={navKeyframes}
|
|
405
|
+
currentPercentage={currentPct}
|
|
406
|
+
onSeek={seekFromKfPct}
|
|
407
|
+
onAddKeyframe={() =>
|
|
408
|
+
onCommitAnimatedProperty &&
|
|
409
|
+
void onCommitAnimatedProperty(element, "x", displayX)
|
|
410
|
+
}
|
|
411
|
+
onRemoveKeyframe={(pct) => onRemoveKeyframe?.(animIdForProp("x"), pct)}
|
|
412
|
+
onConvertToKeyframes={() => onConvertToKeyframes?.(animIdForProp("x"))}
|
|
413
|
+
/>
|
|
414
|
+
)}
|
|
399
415
|
</div>
|
|
400
|
-
|
|
401
|
-
<
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
416
|
+
<div className="flex items-center gap-1">
|
|
417
|
+
<div className="flex-1">
|
|
418
|
+
<MetricField
|
|
419
|
+
label="Y"
|
|
420
|
+
value={formatPxMetricValue(displayY)}
|
|
421
|
+
disabled={manualOffsetEditingDisabled}
|
|
422
|
+
scrub
|
|
423
|
+
onCommit={(next) => commitManualOffset("y", next)}
|
|
424
|
+
/>
|
|
425
|
+
</div>
|
|
426
|
+
{STUDIO_KEYFRAMES_ENABLED && gsapAnimId && (
|
|
427
|
+
<KeyframeNavigation
|
|
428
|
+
property="y"
|
|
429
|
+
keyframes={navKeyframes}
|
|
430
|
+
currentPercentage={currentPct}
|
|
431
|
+
onSeek={seekFromKfPct}
|
|
432
|
+
onAddKeyframe={() =>
|
|
433
|
+
onCommitAnimatedProperty &&
|
|
434
|
+
void onCommitAnimatedProperty(element, "y", displayY)
|
|
435
|
+
}
|
|
436
|
+
onRemoveKeyframe={(pct) => onRemoveKeyframe?.(animIdForProp("y"), pct)}
|
|
437
|
+
onConvertToKeyframes={() => onConvertToKeyframes?.(animIdForProp("y"))}
|
|
438
|
+
/>
|
|
439
|
+
)}
|
|
424
440
|
</div>
|
|
425
|
-
|
|
426
|
-
<
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
441
|
+
<div className="flex items-center gap-1">
|
|
442
|
+
<div className="flex-1">
|
|
443
|
+
<MetricField
|
|
444
|
+
label="W"
|
|
445
|
+
value={formatPxMetricValue(displayW)}
|
|
446
|
+
disabled={manualSizeEditingDisabled}
|
|
447
|
+
scrub
|
|
448
|
+
onCommit={(next) => commitManualSize("width", next)}
|
|
449
|
+
/>
|
|
450
|
+
</div>
|
|
451
|
+
{STUDIO_KEYFRAMES_ENABLED && gsapAnimId && (
|
|
452
|
+
<KeyframeNavigation
|
|
453
|
+
property="width"
|
|
454
|
+
keyframes={navKeyframes}
|
|
455
|
+
currentPercentage={currentPct}
|
|
456
|
+
onSeek={seekFromKfPct}
|
|
457
|
+
onAddKeyframe={() =>
|
|
458
|
+
onCommitAnimatedProperty &&
|
|
459
|
+
void onCommitAnimatedProperty(element, "width", displayW)
|
|
460
|
+
}
|
|
461
|
+
onRemoveKeyframe={(pct) => onRemoveKeyframe?.(animIdForProp("width"), pct)}
|
|
462
|
+
onConvertToKeyframes={() => onConvertToKeyframes?.(animIdForProp("width"))}
|
|
463
|
+
/>
|
|
464
|
+
)}
|
|
449
465
|
</div>
|
|
450
|
-
|
|
451
|
-
<
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
466
|
+
<div className="flex items-center gap-1">
|
|
467
|
+
<div className="flex-1">
|
|
468
|
+
<MetricField
|
|
469
|
+
label="H"
|
|
470
|
+
value={formatPxMetricValue(displayH)}
|
|
471
|
+
disabled={manualSizeEditingDisabled}
|
|
472
|
+
scrub
|
|
473
|
+
onCommit={(next) => commitManualSize("height", next)}
|
|
474
|
+
/>
|
|
475
|
+
</div>
|
|
476
|
+
{STUDIO_KEYFRAMES_ENABLED && gsapAnimId && (
|
|
477
|
+
<KeyframeNavigation
|
|
478
|
+
property="height"
|
|
479
|
+
keyframes={navKeyframes}
|
|
480
|
+
currentPercentage={currentPct}
|
|
481
|
+
onSeek={seekFromKfPct}
|
|
482
|
+
onAddKeyframe={() =>
|
|
483
|
+
onCommitAnimatedProperty &&
|
|
484
|
+
void onCommitAnimatedProperty(element, "height", displayH)
|
|
485
|
+
}
|
|
486
|
+
onRemoveKeyframe={(pct) => onRemoveKeyframe?.(animIdForProp("height"), pct)}
|
|
487
|
+
onConvertToKeyframes={() => onConvertToKeyframes?.(animIdForProp("height"))}
|
|
488
|
+
/>
|
|
489
|
+
)}
|
|
474
490
|
</div>
|
|
475
|
-
|
|
476
|
-
<
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
491
|
+
<div className="flex items-center gap-1">
|
|
492
|
+
<div className="flex-1">
|
|
493
|
+
<MetricField
|
|
494
|
+
label="R"
|
|
495
|
+
value={`${displayR}°`}
|
|
496
|
+
disabled={manualRotationEditingDisabled}
|
|
497
|
+
onCommit={(next) => commitManualRotation(next.replace("°", ""))}
|
|
498
|
+
/>
|
|
499
|
+
</div>
|
|
500
|
+
{STUDIO_KEYFRAMES_ENABLED && gsapAnimId && (
|
|
501
|
+
<KeyframeNavigation
|
|
502
|
+
property="rotation"
|
|
503
|
+
keyframes={navKeyframes}
|
|
504
|
+
currentPercentage={currentPct}
|
|
505
|
+
onSeek={seekFromKfPct}
|
|
506
|
+
onAddKeyframe={() =>
|
|
507
|
+
onCommitAnimatedProperty &&
|
|
508
|
+
void onCommitAnimatedProperty(element, "rotation", displayR)
|
|
509
|
+
}
|
|
510
|
+
onRemoveKeyframe={(pct) => onRemoveKeyframe?.(animIdForProp("rotation"), pct)}
|
|
511
|
+
onConvertToKeyframes={() => onConvertToKeyframes?.(animIdForProp("rotation"))}
|
|
512
|
+
/>
|
|
513
|
+
)}
|
|
498
514
|
</div>
|
|
499
|
-
{STUDIO_KEYFRAMES_ENABLED && gsapAnimId && (
|
|
500
|
-
<KeyframeNavigation
|
|
501
|
-
property="rotation"
|
|
502
|
-
keyframes={navKeyframes}
|
|
503
|
-
currentPercentage={currentPct}
|
|
504
|
-
onSeek={seekFromKfPct}
|
|
505
|
-
onAddKeyframe={() =>
|
|
506
|
-
onCommitAnimatedProperty &&
|
|
507
|
-
void onCommitAnimatedProperty(element, "rotation", displayR)
|
|
508
|
-
}
|
|
509
|
-
onRemoveKeyframe={(pct) => onRemoveKeyframe?.(animIdForProp("rotation"), pct)}
|
|
510
|
-
onConvertToKeyframes={() => onConvertToKeyframes?.(animIdForProp("rotation"))}
|
|
511
|
-
/>
|
|
512
|
-
)}
|
|
513
515
|
</div>
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
onLivePreviewProps={createGsapLivePreview(iframeRef)}
|
|
530
|
-
/>
|
|
531
|
-
<div className="mt-3">
|
|
532
|
-
<div className="mb-2 text-[10px] font-medium uppercase tracking-wider text-neutral-600">
|
|
533
|
-
Stacking
|
|
534
|
-
</div>
|
|
535
|
-
<MetricField
|
|
536
|
-
label="Z-index"
|
|
537
|
-
value={String(parseInt(styles["z-index"] || "auto", 10) || 0)}
|
|
538
|
-
scrub
|
|
539
|
-
onCommit={(next) => onSetStyle("z-index", next)}
|
|
516
|
+
<PropertyPanel3dTransform
|
|
517
|
+
gsapRuntimeValues={gsap3dValues}
|
|
518
|
+
gsapAnimId={gsapAnimId}
|
|
519
|
+
resolveAnimIdForProp={animIdForProp}
|
|
520
|
+
gsapKeyframes={navKeyframes}
|
|
521
|
+
currentPct={currentPct}
|
|
522
|
+
elStart={elStart}
|
|
523
|
+
elDuration={elDuration}
|
|
524
|
+
element={element}
|
|
525
|
+
onCommitAnimatedProperty={onCommitAnimatedProperty}
|
|
526
|
+
onCommitAnimatedProperties={onCommitAnimatedProperties}
|
|
527
|
+
onSeekToTime={onSeekToTime}
|
|
528
|
+
onRemoveKeyframe={onRemoveKeyframe}
|
|
529
|
+
onConvertToKeyframes={onConvertToKeyframes}
|
|
530
|
+
onLivePreviewProps={createGsapLivePreview(iframeRef)}
|
|
540
531
|
/>
|
|
541
|
-
|
|
542
|
-
|
|
532
|
+
<div className="mt-3">
|
|
533
|
+
<div className="mb-2 text-[10px] font-medium uppercase tracking-wider text-neutral-600">
|
|
534
|
+
Stacking
|
|
535
|
+
</div>
|
|
536
|
+
<MetricField
|
|
537
|
+
label="Z-index"
|
|
538
|
+
value={String(parseInt(styles["z-index"] || "auto", 10) || 0)}
|
|
539
|
+
scrub
|
|
540
|
+
onCommit={(next) => onSetStyle("z-index", next)}
|
|
541
|
+
/>
|
|
542
|
+
</div>
|
|
543
|
+
</Section>
|
|
544
|
+
)}
|
|
543
545
|
|
|
544
546
|
{STUDIO_GSAP_PANEL_ENABLED &&
|
|
545
547
|
onUpdateGsapProperty &&
|
|
@@ -388,48 +388,50 @@ export function PropertyPanelFlat({
|
|
|
388
388
|
),
|
|
389
389
|
});
|
|
390
390
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
391
|
+
if (sections.layout) {
|
|
392
|
+
groups.push({
|
|
393
|
+
id: "layout",
|
|
394
|
+
title: "Layout",
|
|
395
|
+
// No scrub accessory: FlatRow/CommitField has no pointer-drag scrubbing
|
|
396
|
+
// (wheel/arrow keys only) — advertising "drag values to scrub" here lies.
|
|
397
|
+
summary: `${formatPxMetricValue(displayX)},${formatPxMetricValue(displayY)} · ${Math.round(displayW)}×${Math.round(displayH)}`,
|
|
398
|
+
content: (
|
|
399
|
+
<FlatLayoutSection
|
|
400
|
+
element={element}
|
|
401
|
+
styles={styles}
|
|
402
|
+
onSetStyle={onSetStyle}
|
|
403
|
+
disabled={!element.capabilities.canEditStyles}
|
|
404
|
+
displayX={displayX}
|
|
405
|
+
displayY={displayY}
|
|
406
|
+
displayW={displayW}
|
|
407
|
+
displayH={displayH}
|
|
408
|
+
displayR={displayR}
|
|
409
|
+
manualOffsetEditingDisabled={manualOffsetEditingDisabled}
|
|
410
|
+
manualSizeEditingDisabled={manualSizeEditingDisabled}
|
|
411
|
+
manualRotationEditingDisabled={manualRotationEditingDisabled}
|
|
412
|
+
commitManualOffset={commitManualOffset}
|
|
413
|
+
commitManualSize={commitManualSize}
|
|
414
|
+
commitManualRotation={commitManualRotation}
|
|
415
|
+
gsapAnimId={gsapAnimId}
|
|
416
|
+
navKeyframes={navKeyframes}
|
|
417
|
+
currentPct={currentPct}
|
|
418
|
+
seekFromKfPct={seekFromKfPct}
|
|
419
|
+
animIdForProp={animIdForProp}
|
|
420
|
+
resolveAnimIdForProp={animIdForProp}
|
|
421
|
+
gsapRuntimeValues={gsapRuntimeValues}
|
|
422
|
+
gsapKeyframes={navKeyframes}
|
|
423
|
+
elStart={elStart}
|
|
424
|
+
elDuration={elDuration}
|
|
425
|
+
onCommitAnimatedProperty={onCommitAnimatedProperty}
|
|
426
|
+
onCommitAnimatedProperties={onCommitAnimatedProperties}
|
|
427
|
+
onSeekToTime={onSeekToTime}
|
|
428
|
+
onRemoveKeyframe={onRemoveKeyframe}
|
|
429
|
+
onConvertToKeyframes={onConvertToKeyframes}
|
|
430
|
+
onLivePreviewProps={createGsapLivePreview(previewIframeRef ?? { current: null })}
|
|
431
|
+
/>
|
|
432
|
+
),
|
|
433
|
+
});
|
|
434
|
+
}
|
|
433
435
|
if (showMotionGroup) {
|
|
434
436
|
groups.push({
|
|
435
437
|
id: "motion",
|
|
@@ -106,13 +106,13 @@ describe("manual editing availability", () => {
|
|
|
106
106
|
expect(resolveStudioBooleanEnvFlag({ UNKNOWN: "maybe" }, ["UNKNOWN"], false)).toBe(false);
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
-
it("defaults the flat inspector flag to
|
|
110
|
-
const
|
|
111
|
-
expect(
|
|
109
|
+
it("defaults the flat inspector flag to true and honors an explicit override", async () => {
|
|
110
|
+
const on = await loadAvailabilityWithEnv({});
|
|
111
|
+
expect(on.STUDIO_FLAT_INSPECTOR_ENABLED).toBe(true);
|
|
112
112
|
|
|
113
|
-
const
|
|
114
|
-
VITE_STUDIO_FLAT_INSPECTOR_ENABLED: "
|
|
113
|
+
const off = await loadAvailabilityWithEnv({
|
|
114
|
+
VITE_STUDIO_FLAT_INSPECTOR_ENABLED: "false",
|
|
115
115
|
});
|
|
116
|
-
expect(
|
|
116
|
+
expect(off.STUDIO_FLAT_INSPECTOR_ENABLED).toBe(false);
|
|
117
117
|
});
|
|
118
118
|
});
|
|
@@ -98,12 +98,14 @@ export const STUDIO_SDK_RESOLVER_SHADOW_ENABLED = resolveStudioBooleanEnvFlag(
|
|
|
98
98
|
);
|
|
99
99
|
|
|
100
100
|
// Studio inspector redesign ("Ledger, flat" — design_handoff_studio_inspector):
|
|
101
|
-
// flat identity header/footer/groups
|
|
102
|
-
//
|
|
101
|
+
// flat identity header/footer/groups. Default true as of v0.7.59+ bug-fix pass
|
|
102
|
+
// (right-aligned values, Stroke select-only, promote-badge overlap, Layout/
|
|
103
|
+
// Style section gating); disable via VITE_STUDIO_FLAT_INSPECTOR_ENABLED=false
|
|
104
|
+
// to fall back to the legacy panel.
|
|
103
105
|
export const STUDIO_FLAT_INSPECTOR_ENABLED = resolveStudioBooleanEnvFlag(
|
|
104
106
|
env,
|
|
105
107
|
["VITE_STUDIO_ENABLE_FLAT_INSPECTOR", "VITE_STUDIO_FLAT_INSPECTOR_ENABLED"],
|
|
106
|
-
|
|
108
|
+
true,
|
|
107
109
|
);
|
|
108
110
|
|
|
109
111
|
export const STUDIO_MANUAL_EDITING_DISABLED_TITLE = "Manual editing is temporarily disabled";
|
|
@@ -377,7 +377,7 @@ export function FlatColorGradingSection({
|
|
|
377
377
|
track("select", "Custom LUT");
|
|
378
378
|
applyLut(src || null, src && lut?.src === src ? lut.intensity : 1);
|
|
379
379
|
}}
|
|
380
|
-
className="bg-transparent font-mono text-[10px] text-panel-text-3 outline-none"
|
|
380
|
+
className="border-b border-panel-border-input/50 bg-transparent font-mono text-[10px] text-panel-text-3 outline-none hover:border-panel-border-input"
|
|
381
381
|
>
|
|
382
382
|
<option value="">None</option>
|
|
383
383
|
{lutAssets.map((asset) => (
|
|
@@ -545,7 +545,7 @@ export function FlatColorGradingSection({
|
|
|
545
545
|
onSetApplyScope(e.target.value as "source-file" | "project");
|
|
546
546
|
}}
|
|
547
547
|
disabled={applyBusy}
|
|
548
|
-
className="bg-transparent font-mono text-[11px] text-panel-text-0 outline-none disabled:opacity-50"
|
|
548
|
+
className="border-b border-panel-border-input/50 bg-transparent font-mono text-[11px] text-panel-text-0 outline-none hover:border-panel-border-input disabled:opacity-50"
|
|
549
549
|
>
|
|
550
550
|
<option value="source-file">Current file media</option>
|
|
551
551
|
<option value="project">All project media</option>
|
|
@@ -85,7 +85,7 @@ export function FlatTimingRow({
|
|
|
85
85
|
const cell = (label: string, value: string, onCommit: (next: string) => void) => (
|
|
86
86
|
<div className="grid gap-px">
|
|
87
87
|
<span className="text-[9px] text-panel-text-4">{label}</span>
|
|
88
|
-
<span className="border-b border-
|
|
88
|
+
<span className="border-b border-panel-border-input/50 font-mono text-[11px] text-panel-text-0 hover:border-panel-border-input">
|
|
89
89
|
<CommitField
|
|
90
90
|
value={value}
|
|
91
91
|
onCommit={(next) => {
|
|
@@ -38,6 +38,16 @@ describe("FlatRow", () => {
|
|
|
38
38
|
act(() => root.unmount());
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
+
it("right-aligns the value input — the flat inspector's justify-between row layout leaves a left-aligned value looking stranded at the edge of its own box", () => {
|
|
42
|
+
const { host, root } = renderInto(
|
|
43
|
+
<FlatRow label="Size" value="72px" tier="explicitCustom" onCommit={vi.fn()} />,
|
|
44
|
+
);
|
|
45
|
+
const input = host.querySelector("input");
|
|
46
|
+
expect(input?.className).toContain("text-right");
|
|
47
|
+
expect(input?.className).not.toContain("text-left");
|
|
48
|
+
act(() => root.unmount());
|
|
49
|
+
});
|
|
50
|
+
|
|
41
51
|
it("renders the explicitCustom tier with a mint value and a reset button", () => {
|
|
42
52
|
const onReset = vi.fn();
|
|
43
53
|
const { host, root } = renderInto(
|
|
@@ -43,14 +43,15 @@ export function FlatRow({
|
|
|
43
43
|
data-flat-row-value="true"
|
|
44
44
|
className={`min-w-0 border-b pb-px font-mono text-[11px] ${VALUE_TIER_VALUE_CLASS[tier]} ${
|
|
45
45
|
tier === "explicitCustom"
|
|
46
|
-
? "border-
|
|
47
|
-
: "border-
|
|
46
|
+
? "border-panel-accent/30 group-hover:border-panel-accent/70"
|
|
47
|
+
: "border-panel-border-input/50 group-hover:border-panel-border-input"
|
|
48
48
|
}`}
|
|
49
49
|
>
|
|
50
50
|
<CommitField
|
|
51
51
|
value={value}
|
|
52
52
|
disabled={disabled}
|
|
53
53
|
liveCommit={liveCommit}
|
|
54
|
+
align="right"
|
|
54
55
|
onCommit={(nextValue) => {
|
|
55
56
|
track("metric", label);
|
|
56
57
|
onCommit(nextValue);
|
|
@@ -53,7 +53,13 @@ export function FlatSelectRow({
|
|
|
53
53
|
<div className="group flex min-h-[30px] items-center justify-between">
|
|
54
54
|
<span className={`text-[11px] ${VALUE_TIER_LABEL_CLASS[tier]}`}>{label}</span>
|
|
55
55
|
<span className="flex items-center gap-2">
|
|
56
|
-
<label
|
|
56
|
+
<label
|
|
57
|
+
className={`flex items-center gap-1.5 border-b pb-px ${
|
|
58
|
+
tier === "explicitCustom"
|
|
59
|
+
? "border-panel-accent/30 group-hover:border-panel-accent/70"
|
|
60
|
+
: "border-panel-border-input/50 group-hover:border-panel-border-input"
|
|
61
|
+
}`}
|
|
62
|
+
>
|
|
57
63
|
<select
|
|
58
64
|
value={value}
|
|
59
65
|
disabled={disabled}
|
|
@@ -12,16 +12,3 @@ export const STROKE_STYLE_OPTIONS: string[] = [
|
|
|
12
12
|
"inset",
|
|
13
13
|
"outset",
|
|
14
14
|
];
|
|
15
|
-
|
|
16
|
-
export function formatStrokeSummary(widthPx: number, style: string): string {
|
|
17
|
-
return `${widthPx}px ${style}`;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function parseStrokeSummary(text: string): { widthPx: number; style: string } | null {
|
|
21
|
-
const match = /^\s*(-?\d+(?:\.\d+)?)px\s+(\S+)\s*$/.exec(text);
|
|
22
|
-
if (!match) return null;
|
|
23
|
-
const widthPx = Number.parseFloat(match[1]);
|
|
24
|
-
const style = match[2];
|
|
25
|
-
if (!Number.isFinite(widthPx) || !style) return null;
|
|
26
|
-
return { widthPx, style };
|
|
27
|
-
}
|
|
@@ -175,31 +175,43 @@ function setInputValue(input: HTMLInputElement, nextValue: string) {
|
|
|
175
175
|
}
|
|
176
176
|
|
|
177
177
|
describe("FlatStyleSection — Stroke and Radius", () => {
|
|
178
|
-
it("renders
|
|
178
|
+
it("renders a width-only Stroke width row — style is only ever set through the Stroke style select below it", () => {
|
|
179
179
|
const { host, root } = renderSection(STROKE_STYLES);
|
|
180
|
-
expect(host.textContent).toContain("Stroke");
|
|
181
|
-
expect(
|
|
180
|
+
expect(host.textContent).toContain("Stroke width");
|
|
181
|
+
expect(host.textContent).toContain("Stroke style");
|
|
182
|
+
expect(getFlatRowInput(host, "Stroke width").value).toBe("1px");
|
|
182
183
|
act(() => root.unmount());
|
|
183
184
|
});
|
|
184
185
|
|
|
185
|
-
it("commits
|
|
186
|
+
it("commits a new stroke width without touching the existing style", async () => {
|
|
186
187
|
const { host, root, onSetStyle } = renderSection(STROKE_STYLES);
|
|
187
|
-
await commitFlatRowInput(host, "Stroke", "2px
|
|
188
|
+
await commitFlatRowInput(host, "Stroke width", "2px");
|
|
188
189
|
expect(onSetStyle).toHaveBeenCalledWith("border-width", "2px");
|
|
189
|
-
expect(onSetStyle).toHaveBeenCalledWith("border-style",
|
|
190
|
+
expect(onSetStyle).not.toHaveBeenCalledWith("border-style", expect.anything());
|
|
191
|
+
act(() => root.unmount());
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("defaults a from-zero width commit to solid, since a width with no style renders no visible border", async () => {
|
|
195
|
+
const { host, root, onSetStyle } = renderSection({
|
|
196
|
+
"border-width": "0px",
|
|
197
|
+
"border-style": "none",
|
|
198
|
+
"border-color": "rgba(255,255,255,.12)",
|
|
199
|
+
});
|
|
200
|
+
await commitFlatRowInput(host, "Stroke width", "3px");
|
|
201
|
+
expect(onSetStyle).toHaveBeenCalledWith("border-style", "solid");
|
|
190
202
|
act(() => root.unmount());
|
|
191
203
|
});
|
|
192
204
|
|
|
193
205
|
it("clamps an out-of-range stroke width commit to 200px (fix 2)", async () => {
|
|
194
206
|
const { host, root, onSetStyle } = renderSection(STROKE_STYLES);
|
|
195
|
-
await commitFlatRowInput(host, "Stroke", "9999px
|
|
207
|
+
await commitFlatRowInput(host, "Stroke width", "9999px");
|
|
196
208
|
expect(onSetStyle).toHaveBeenCalledWith("border-width", "200px");
|
|
197
209
|
act(() => root.unmount());
|
|
198
210
|
});
|
|
199
211
|
|
|
200
|
-
it("
|
|
212
|
+
it("ignores an unparseable stroke width commit", async () => {
|
|
201
213
|
const { host, root, onSetStyle } = renderSection(STROKE_STYLES);
|
|
202
|
-
await commitFlatRowInput(host, "Stroke", "
|
|
214
|
+
await commitFlatRowInput(host, "Stroke width", "bogus");
|
|
203
215
|
expect(onSetStyle).not.toHaveBeenCalled();
|
|
204
216
|
act(() => root.unmount());
|
|
205
217
|
});
|