@bycrux/editor 1.2.2 → 1.4.0
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/package.json +1 -1
- package/src/ControlsInfoModal.tsx +5 -5
- package/src/__tests__/no-opacity-modifier-on-editor-vars.test.ts +84 -0
- package/src/__tests__/node-shims.d.ts +19 -0
- package/src/__tests__/portal-roots-set-editor-text.test.ts +55 -0
- package/src/carousel/AddElementMenu.tsx +1 -1
- package/src/carousel/CarouselEditor.tsx +11 -11
- package/src/carousel/CarouselRenderModal.tsx +14 -14
- package/src/carousel/OverlayPicker.tsx +4 -4
- package/src/carousel/SlidePropertyPanel.tsx +6 -6
- package/src/carousel/__tests__/CarouselEditor.test.tsx +12 -0
- package/src/components/FilmstripScrubber.tsx +1 -1
- package/src/crop/VideoSourceCropModal.tsx +5 -5
- package/src/state/__tests__/use-project-sync.test.tsx +76 -0
- package/src/state/use-project-sync.ts +21 -1
- package/src/text/FontPicker.tsx +3 -3
- package/src/text/TextFormattingToolbar.tsx +3 -3
- package/src/types.ts +18 -0
- package/src/ui/Loader.tsx +1 -1
- package/src/ui/NumberField.tsx +3 -3
- package/src/ui/SwatchInput.tsx +1 -1
- package/src/ui/Tooltip.tsx +1 -1
- package/src/ui/input.tsx +1 -1
- package/src/ui/label.tsx +1 -1
- package/src/ui/textarea.tsx +1 -1
- package/src/video/AudioPolishModal.tsx +22 -22
- package/src/video/CaptionListPanel.test.tsx +8 -7
- package/src/video/CaptionListPanel.tsx +17 -17
- package/src/video/CaptionRegenModal.tsx +8 -8
- package/src/video/CaptionStyleGallery.tsx +1 -1
- package/src/video/CommandPalette.tsx +8 -8
- package/src/video/ImageToneMenu.tsx +13 -14
- package/src/video/OverlayInspector.tsx +7 -7
- package/src/video/RenderModal.tsx +76 -78
- package/src/video/VersionCompare.tsx +10 -10
- package/src/video/VersionPanel.tsx +2 -2
- package/src/video/VideoEditor.tsx +48 -23
- package/src/video/__tests__/RenderModal.hostTheme.test.tsx +85 -0
- package/src/video/__tests__/VideoEditor.context.test.tsx +6 -0
- package/src/video/__tests__/VideoEditor.pendingSurface.test.tsx +233 -0
- package/src/video/__tests__/VideoEditor.saveError.test.tsx +105 -0
- package/src/video/__tests__/export-limits.test.ts +27 -2
- package/src/video/__tests__/use-report-context.test.tsx +22 -1
- package/src/video/export-limits.ts +22 -1
- package/src/video/panels/ClipPropertiesPanel.tsx +4 -4
- package/src/video/panels/OverlayContentPanel.tsx +5 -5
- package/src/video/preview/OverlayItemsLayer.tsx +7 -2
- package/src/video/preview/PreviewPlayer.tsx +14 -0
- package/src/video/preview/SocialPreviewMenu.tsx +6 -6
- package/src/video/preview/__tests__/PreviewPlayer.playing.test.tsx +62 -0
- package/src/video/preview/__tests__/resolveOverlayPropPaths.test.ts +21 -0
- package/src/video/timeline/EditableSegment.tsx +1 -1
- package/src/video/timeline/Scrubber.tsx +2 -2
- package/src/video/timeline/SpeedControl.tsx +3 -3
- package/src/video/timeline/Timeline.tsx +3 -3
- package/src/video/timeline/TrackGutter.tsx +6 -6
- package/src/video/timeline/TrackSettingsPopover.tsx +4 -4
- package/src/video/timeline/VolumeControl.tsx +3 -3
- package/src/video/use-report-context.ts +10 -7
|
@@ -115,12 +115,10 @@ interface RenderModalProps<P extends Project = Project> {
|
|
|
115
115
|
preRenderOptions?: PreRenderOptions
|
|
116
116
|
/** Editor theme mode — light/dark. The export dialog and progress/error
|
|
117
117
|
* panels follow `--editor-surface`, so warning/error hues need to darken
|
|
118
|
-
* in light mode. The render-log terminal boxes stay dark by design
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* values are exactly what the old translucent blacks composited to over the
|
|
123
|
-
* dark `--editor-surface`, so dark mode is unchanged. Absent -> dark,
|
|
118
|
+
* in light mode. The render-log terminal boxes stay dark by design: they are
|
|
119
|
+
* painted an OPAQUE near-black (`#0a0e17`) with fixed white text, so they
|
|
120
|
+
* read the same under either theme. The phase stepper and footer buttons
|
|
121
|
+
* follow the theme tokens instead. Absent -> dark,
|
|
124
122
|
* matching every existing caller. */
|
|
125
123
|
mode?: 'light' | 'dark'
|
|
126
124
|
}
|
|
@@ -290,10 +288,10 @@ function PhaseStepper({ current, phases }: { current: RenderPhase; phases: Rende
|
|
|
290
288
|
<span
|
|
291
289
|
className={
|
|
292
290
|
complete
|
|
293
|
-
? 'text-sm text-[var(--editor-text)]
|
|
291
|
+
? 'text-sm text-[color-mix(in_srgb,var(--editor-text)_70%,transparent)]'
|
|
294
292
|
: active
|
|
295
293
|
? 'text-sm font-semibold text-[var(--editor-text)]'
|
|
296
|
-
: 'text-sm text-[var(--editor-text)]
|
|
294
|
+
: 'text-sm text-[color-mix(in_srgb,var(--editor-text)_35%,transparent)]'
|
|
297
295
|
}
|
|
298
296
|
>
|
|
299
297
|
{phaseLabel(phase)}
|
|
@@ -309,15 +307,15 @@ function PhaseStepper({ current, phases }: { current: RenderPhase; phases: Rende
|
|
|
309
307
|
|
|
310
308
|
function LogLine({ text }: { text: string }) {
|
|
311
309
|
const t = text.replace(/^\[montaj render\]\s*/, '')
|
|
312
|
-
let color = 'text-
|
|
310
|
+
let color = 'text-white/60'
|
|
313
311
|
if (/ready|complete|done|encoded|assembled/i.test(t)) color = 'text-green-400'
|
|
314
312
|
else if (/rendering|bundling|launching|browsers/i.test(t)) color = 'text-sky-400'
|
|
315
313
|
else if (/trimming|building|composing/i.test(t)) color = 'text-amber-400'
|
|
316
|
-
else if (/frame\s+\d+\/\d+/i.test(t)) color = 'text-
|
|
314
|
+
else if (/frame\s+\d+\/\d+/i.test(t)) color = 'text-white/55'
|
|
317
315
|
else if (/error|fail|warn/i.test(t)) color = 'text-red-400'
|
|
318
316
|
|
|
319
317
|
const prefix = text.startsWith('[montaj render]')
|
|
320
|
-
? <span className="text-
|
|
318
|
+
? <span className="text-white/40">[render] </span>
|
|
321
319
|
: null
|
|
322
320
|
|
|
323
321
|
return (
|
|
@@ -385,7 +383,7 @@ function ProgressBar({ value }: { value: number | null }) {
|
|
|
385
383
|
return (
|
|
386
384
|
<div className="w-full max-w-xs" role="progressbar" aria-label="Render progress"
|
|
387
385
|
aria-valuenow={pct === null ? undefined : Math.round(pct)} aria-valuemin={0} aria-valuemax={100}>
|
|
388
|
-
<div className="h-1.5 w-full overflow-hidden rounded-full bg-[var(--editor-text)]
|
|
386
|
+
<div className="h-1.5 w-full overflow-hidden rounded-full bg-[color-mix(in_srgb,var(--editor-text)_10%,transparent)]">
|
|
389
387
|
{pct === null ? (
|
|
390
388
|
<>
|
|
391
389
|
<style>{'@keyframes montajRenderIndet{0%{transform:translateX(-100%)}100%{transform:translateX(320%)}}'}</style>
|
|
@@ -792,16 +790,16 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
792
790
|
}
|
|
793
791
|
return (<>
|
|
794
792
|
{createPortal(
|
|
795
|
-
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-md p-4">
|
|
796
|
-
<div className="w-full max-w-3xl bg-[var(--editor-surface)] border border-
|
|
793
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-md p-4 text-[var(--editor-text)]">
|
|
794
|
+
<div className="w-full max-w-3xl bg-[var(--editor-surface)] border border-[var(--editor-border)] rounded-2xl shadow-2xl flex flex-col overflow-hidden">
|
|
797
795
|
|
|
798
796
|
{/* Header */}
|
|
799
|
-
<div className="flex items-center justify-between px-5 py-4 border-b border-
|
|
797
|
+
<div className="flex items-center justify-between px-5 py-4 border-b border-[var(--editor-border)]">
|
|
800
798
|
<h2 className="text-sm font-semibold text-[var(--editor-text)]">Export</h2>
|
|
801
799
|
<button
|
|
802
800
|
onClick={handleCancel}
|
|
803
801
|
aria-label="Close"
|
|
804
|
-
className="text-[var(--editor-text)]
|
|
802
|
+
className="text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)] hover:text-[var(--editor-text)] transition-colors text-lg leading-none"
|
|
805
803
|
>
|
|
806
804
|
×
|
|
807
805
|
</button>
|
|
@@ -813,7 +811,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
813
811
|
{/* Left — cover preview + editor */}
|
|
814
812
|
<div className="md:w-64 shrink-0 flex flex-col gap-2">
|
|
815
813
|
<div
|
|
816
|
-
className="relative mx-auto max-w-full overflow-hidden rounded-lg border border-
|
|
814
|
+
className="relative mx-auto max-w-full overflow-hidden rounded-lg border border-[var(--editor-border)] bg-black/40 flex items-center justify-center"
|
|
817
815
|
style={coverPortrait
|
|
818
816
|
? { aspectRatio: coverAspect, height: 'min(46vh, 22rem)' }
|
|
819
817
|
: { aspectRatio: coverAspect, width: '100%' }}
|
|
@@ -827,7 +825,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
827
825
|
) : coverLoading ? (
|
|
828
826
|
<Loader size="md" label="Loading cover" />
|
|
829
827
|
) : (
|
|
830
|
-
<span className="text-[11px] text-[var(--editor-text)]
|
|
828
|
+
<span className="text-[11px] text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">No preview</span>
|
|
831
829
|
)}
|
|
832
830
|
</div>
|
|
833
831
|
{adapter.getSampleFrame && (
|
|
@@ -835,7 +833,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
835
833
|
<button
|
|
836
834
|
onClick={() => setCoverEditing(o => !o)}
|
|
837
835
|
aria-expanded={coverEditing}
|
|
838
|
-
className="self-start text-xs text-[var(--editor-text)]
|
|
836
|
+
className="self-start text-xs text-[color-mix(in_srgb,var(--editor-text)_70%,transparent)] hover:text-[var(--editor-text)] transition-colors"
|
|
839
837
|
>
|
|
840
838
|
{coverEditing ? 'Done' : 'Edit cover'}
|
|
841
839
|
</button>
|
|
@@ -857,7 +855,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
857
855
|
className={`relative shrink-0 overflow-hidden rounded border transition-colors ${
|
|
858
856
|
active
|
|
859
857
|
? 'border-[var(--editor-accent)] ring-1 ring-[var(--editor-accent)]'
|
|
860
|
-
: 'border-
|
|
858
|
+
: 'border-[var(--editor-border)] hover:border-[color-mix(in_srgb,var(--editor-text)_40%,transparent)]'
|
|
861
859
|
}`}
|
|
862
860
|
style={{ aspectRatio: coverAspect, height: 72 }}
|
|
863
861
|
>
|
|
@@ -869,7 +867,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
869
867
|
})}
|
|
870
868
|
</div>
|
|
871
869
|
) : (
|
|
872
|
-
<p className="text-[11px] text-[var(--editor-text)]
|
|
870
|
+
<p className="text-[11px] text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">No frames to pick from.</p>
|
|
873
871
|
)
|
|
874
872
|
)}
|
|
875
873
|
</>
|
|
@@ -880,7 +878,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
880
878
|
<div className="flex-1 min-w-0 flex flex-col gap-4">
|
|
881
879
|
{/* Name */}
|
|
882
880
|
<label className="flex flex-col gap-1.5">
|
|
883
|
-
<span className="text-[11px] font-semibold uppercase tracking-wide text-[var(--editor-text)]
|
|
881
|
+
<span className="text-[11px] font-semibold uppercase tracking-wide text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">Name</span>
|
|
884
882
|
<input
|
|
885
883
|
type="text"
|
|
886
884
|
value={name}
|
|
@@ -892,9 +890,9 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
892
890
|
|
|
893
891
|
{/* Save to */}
|
|
894
892
|
<div className="flex flex-col gap-1">
|
|
895
|
-
<span className="text-[11px] font-semibold uppercase tracking-wide text-[var(--editor-text)]
|
|
896
|
-
<span className="text-xs font-mono text-[var(--editor-text)]
|
|
897
|
-
<span className="text-[11px] text-[var(--editor-text)]
|
|
893
|
+
<span className="text-[11px] font-semibold uppercase tracking-wide text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">Save to</span>
|
|
894
|
+
<span className="text-xs font-mono text-[color-mix(in_srgb,var(--editor-text)_70%,transparent)] truncate">output/{outputName}.mp4</span>
|
|
895
|
+
<span className="text-[11px] text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">Download after export.</span>
|
|
898
896
|
</div>
|
|
899
897
|
|
|
900
898
|
{/* Resolution — source-capped tier picker. Only rendered when the
|
|
@@ -908,7 +906,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
908
906
|
const currentShort = Math.min(...res.value)
|
|
909
907
|
return (
|
|
910
908
|
<div className="flex flex-col gap-2">
|
|
911
|
-
<p className="text-[11px] font-semibold uppercase tracking-wide text-[var(--editor-text)]
|
|
909
|
+
<p className="text-[11px] font-semibold uppercase tracking-wide text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">Resolution</p>
|
|
912
910
|
<div role="radiogroup" aria-label="Resolution" className="grid grid-cols-2 sm:grid-cols-4 gap-2">
|
|
913
911
|
{res.available.map(([w, h]) => {
|
|
914
912
|
const short = Math.min(w, h)
|
|
@@ -921,12 +919,12 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
921
919
|
onClick={() => res.set([w, h])}
|
|
922
920
|
className={`flex flex-col gap-1 rounded-lg border p-2.5 text-left transition-colors ${
|
|
923
921
|
active
|
|
924
|
-
? 'border-
|
|
925
|
-
: 'border-[var(--editor-border)] hover:bg-[var(--editor-text)]
|
|
922
|
+
? 'border-[var(--editor-accent)] bg-[color-mix(in_srgb,var(--editor-accent)_12%,transparent)]'
|
|
923
|
+
: 'border-[var(--editor-border)] hover:bg-[color-mix(in_srgb,var(--editor-text)_5%,transparent)]'
|
|
926
924
|
}`}
|
|
927
925
|
>
|
|
928
926
|
<span className="text-xs font-semibold text-[var(--editor-text)]">{resLabel([w, h])}</span>
|
|
929
|
-
<span className="text-[10px] leading-snug text-[var(--editor-text)]
|
|
927
|
+
<span className="text-[10px] leading-snug text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">{w} × {h}</span>
|
|
930
928
|
</button>
|
|
931
929
|
)
|
|
932
930
|
})}
|
|
@@ -940,7 +938,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
940
938
|
const fps = preRenderOptions.fps
|
|
941
939
|
return (
|
|
942
940
|
<div className="flex flex-col gap-2">
|
|
943
|
-
<p className="text-[11px] font-semibold uppercase tracking-wide text-[var(--editor-text)]
|
|
941
|
+
<p className="text-[11px] font-semibold uppercase tracking-wide text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">Frame rate</p>
|
|
944
942
|
<div role="radiogroup" aria-label="Frame rate" className="grid grid-cols-3 gap-2">
|
|
945
943
|
{fps.available.map(f => {
|
|
946
944
|
const active = f === fps.value
|
|
@@ -953,12 +951,12 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
953
951
|
onClick={() => fps.set(f)}
|
|
954
952
|
className={`flex flex-col gap-1 rounded-lg border p-2.5 text-left transition-colors ${
|
|
955
953
|
active
|
|
956
|
-
? 'border-
|
|
957
|
-
: 'border-[var(--editor-border)] hover:bg-[var(--editor-text)]
|
|
954
|
+
? 'border-[var(--editor-accent)] bg-[color-mix(in_srgb,var(--editor-accent)_12%,transparent)]'
|
|
955
|
+
: 'border-[var(--editor-border)] hover:bg-[color-mix(in_srgb,var(--editor-text)_5%,transparent)]'
|
|
958
956
|
}`}
|
|
959
957
|
>
|
|
960
958
|
<span className="text-xs font-semibold text-[var(--editor-text)]">{f} fps</span>
|
|
961
|
-
<span className="text-[10px] leading-snug text-[var(--editor-text)]
|
|
959
|
+
<span className="text-[10px] leading-snug text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">{hint}</span>
|
|
962
960
|
</button>
|
|
963
961
|
)
|
|
964
962
|
})}
|
|
@@ -972,7 +970,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
972
970
|
<>
|
|
973
971
|
{/* Format */}
|
|
974
972
|
<div className="flex flex-col gap-2">
|
|
975
|
-
<p className="text-[11px] font-semibold uppercase tracking-wide text-[var(--editor-text)]
|
|
973
|
+
<p className="text-[11px] font-semibold uppercase tracking-wide text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">Format</p>
|
|
976
974
|
<div role="radiogroup" aria-label="Format" className="grid grid-cols-3 gap-2">
|
|
977
975
|
{EXPORT_CHOICES.map(choice => {
|
|
978
976
|
const active = choice.id === exportChoice
|
|
@@ -984,12 +982,12 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
984
982
|
onClick={() => setExport(choice.id)}
|
|
985
983
|
className={`flex flex-col gap-1 rounded-lg border p-2.5 text-left transition-colors ${
|
|
986
984
|
active
|
|
987
|
-
? 'border-
|
|
988
|
-
: 'border-[var(--editor-border)] hover:bg-[var(--editor-text)]
|
|
985
|
+
? 'border-[var(--editor-accent)] bg-[color-mix(in_srgb,var(--editor-accent)_12%,transparent)]'
|
|
986
|
+
: 'border-[var(--editor-border)] hover:bg-[color-mix(in_srgb,var(--editor-text)_5%,transparent)]'
|
|
989
987
|
}`}
|
|
990
988
|
>
|
|
991
989
|
<span className="text-xs font-semibold text-[var(--editor-text)]">{choice.label}</span>
|
|
992
|
-
<span className="text-[10px] leading-snug text-[var(--editor-text)]
|
|
990
|
+
<span className="text-[10px] leading-snug text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">{choice.blurb}</span>
|
|
993
991
|
</button>
|
|
994
992
|
)
|
|
995
993
|
})}
|
|
@@ -1010,7 +1008,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1010
1008
|
<button
|
|
1011
1009
|
onClick={() => setImageColor(o => !o)}
|
|
1012
1010
|
aria-expanded={imageColorOpen}
|
|
1013
|
-
className="flex items-center gap-1.5 self-start text-xs text-[var(--editor-text)]
|
|
1011
|
+
className="flex items-center gap-1.5 self-start text-xs text-[color-mix(in_srgb,var(--editor-text)_70%,transparent)] hover:text-[var(--editor-text)] transition-colors"
|
|
1014
1012
|
>
|
|
1015
1013
|
{imageColorOpen ? <ChevronDown size={13} /> : <ChevronRight size={13} />}
|
|
1016
1014
|
Image color
|
|
@@ -1018,7 +1016,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1018
1016
|
|
|
1019
1017
|
{imageColorOpen ? (
|
|
1020
1018
|
<div className="flex flex-col gap-2">
|
|
1021
|
-
<p className="text-[11px] font-semibold uppercase tracking-wide text-[var(--editor-text)]
|
|
1019
|
+
<p className="text-[11px] font-semibold uppercase tracking-wide text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">
|
|
1022
1020
|
How photos and logos convert for HDR
|
|
1023
1021
|
</p>
|
|
1024
1022
|
<div role="radiogroup" aria-label="Image color" className="grid grid-cols-2 gap-2">
|
|
@@ -1032,8 +1030,8 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1032
1030
|
onClick={() => it.set(t.id)}
|
|
1033
1031
|
className={`flex flex-col gap-1.5 rounded-lg border p-2 text-left transition-colors ${
|
|
1034
1032
|
active
|
|
1035
|
-
? 'border-
|
|
1036
|
-
: 'border-[var(--editor-border)] hover:bg-[var(--editor-text)]
|
|
1033
|
+
? 'border-[var(--editor-accent)] bg-[color-mix(in_srgb,var(--editor-accent)_12%,transparent)]'
|
|
1034
|
+
: 'border-[var(--editor-border)] hover:bg-[color-mix(in_srgb,var(--editor-text)_5%,transparent)]'
|
|
1037
1035
|
}`}
|
|
1038
1036
|
>
|
|
1039
1037
|
<img
|
|
@@ -1044,17 +1042,17 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1044
1042
|
<span className="text-xs font-semibold text-[var(--editor-text)] flex items-center gap-1.5">
|
|
1045
1043
|
{t.label}
|
|
1046
1044
|
{t.id === DEFAULT_IMAGE_TONE && (
|
|
1047
|
-
<span className="text-[9px] font-normal px-1 py-px rounded bg-[var(--editor-text)]
|
|
1045
|
+
<span className="text-[9px] font-normal px-1 py-px rounded bg-[color-mix(in_srgb,var(--editor-text)_10%,transparent)] text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">default</span>
|
|
1048
1046
|
)}
|
|
1049
1047
|
</span>
|
|
1050
|
-
<span className="text-[10px] leading-snug text-[var(--editor-text)]
|
|
1048
|
+
<span className="text-[10px] leading-snug text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">{t.summary}</span>
|
|
1051
1049
|
</button>
|
|
1052
1050
|
)
|
|
1053
1051
|
})}
|
|
1054
1052
|
</div>
|
|
1055
1053
|
</div>
|
|
1056
1054
|
) : (
|
|
1057
|
-
<p className="text-[11px] text-[var(--editor-text)]
|
|
1055
|
+
<p className="text-[11px] text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">
|
|
1058
1056
|
Photos and logos use the {toneInfo.label} conversion. Open to change.
|
|
1059
1057
|
</p>
|
|
1060
1058
|
)}
|
|
@@ -1067,7 +1065,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1067
1065
|
<button
|
|
1068
1066
|
onClick={() => setAdvanced(o => !o)}
|
|
1069
1067
|
aria-expanded={advancedOpen}
|
|
1070
|
-
className="flex items-center gap-1.5 self-start text-xs text-[var(--editor-text)]
|
|
1068
|
+
className="flex items-center gap-1.5 self-start text-xs text-[color-mix(in_srgb,var(--editor-text)_70%,transparent)] hover:text-[var(--editor-text)] transition-colors"
|
|
1071
1069
|
>
|
|
1072
1070
|
{advancedOpen ? <ChevronDown size={13} /> : <ChevronRight size={13} />}
|
|
1073
1071
|
Advanced
|
|
@@ -1075,7 +1073,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1075
1073
|
|
|
1076
1074
|
{advancedOpen ? (
|
|
1077
1075
|
<div className="flex flex-col gap-2">
|
|
1078
|
-
<p className="text-[11px] font-semibold uppercase tracking-wide text-[var(--editor-text)]
|
|
1076
|
+
<p className="text-[11px] font-semibold uppercase tracking-wide text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">
|
|
1079
1077
|
Tone curve for SDR
|
|
1080
1078
|
</p>
|
|
1081
1079
|
<div role="radiogroup" aria-label="Tone curve for SDR" className="grid grid-cols-2 gap-2">
|
|
@@ -1090,8 +1088,8 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1090
1088
|
onClick={() => setSdrCurve(curve.id)}
|
|
1091
1089
|
className={`flex flex-col gap-1.5 rounded-lg border p-2 text-left transition-colors ${
|
|
1092
1090
|
active
|
|
1093
|
-
? 'border-
|
|
1094
|
-
: 'border-[var(--editor-border)] hover:bg-[var(--editor-text)]
|
|
1091
|
+
? 'border-[var(--editor-accent)] bg-[color-mix(in_srgb,var(--editor-accent)_12%,transparent)]'
|
|
1092
|
+
: 'border-[var(--editor-border)] hover:bg-[color-mix(in_srgb,var(--editor-text)_5%,transparent)]'
|
|
1095
1093
|
}`}
|
|
1096
1094
|
>
|
|
1097
1095
|
{thumb ? (
|
|
@@ -1120,17 +1118,17 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1120
1118
|
</span>
|
|
1121
1119
|
</span>
|
|
1122
1120
|
) : thumbsPending ? (
|
|
1123
|
-
<span style={{ aspectRatio: coverAspect }} className="w-full rounded border border-[var(--editor-border)] bg-[var(--editor-text)]
|
|
1121
|
+
<span style={{ aspectRatio: coverAspect }} className="w-full rounded border border-[var(--editor-border)] bg-[color-mix(in_srgb,var(--editor-text)_5%,transparent)] flex items-center justify-center">
|
|
1124
1122
|
<Loader size="sm" />
|
|
1125
1123
|
</span>
|
|
1126
1124
|
) : null}
|
|
1127
1125
|
<span className="text-xs font-semibold text-[var(--editor-text)] flex items-center gap-1.5">
|
|
1128
1126
|
{curve.label}
|
|
1129
1127
|
{curve.id === DEFAULT_SDR_CURVE && (
|
|
1130
|
-
<span className="text-[9px] font-normal px-1 py-px rounded bg-[var(--editor-text)]
|
|
1128
|
+
<span className="text-[9px] font-normal px-1 py-px rounded bg-[color-mix(in_srgb,var(--editor-text)_10%,transparent)] text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">default</span>
|
|
1131
1129
|
)}
|
|
1132
1130
|
</span>
|
|
1133
|
-
<span className="text-[10px] leading-snug text-[var(--editor-text)]
|
|
1131
|
+
<span className="text-[10px] leading-snug text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">{curve.blurb}</span>
|
|
1134
1132
|
</button>
|
|
1135
1133
|
)
|
|
1136
1134
|
})}
|
|
@@ -1139,7 +1137,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1139
1137
|
data-testid="sdr-honesty-line"
|
|
1140
1138
|
className={`text-[11px] leading-snug ${
|
|
1141
1139
|
sdrCurve === DEFAULT_SDR_CURVE
|
|
1142
|
-
? 'text-[var(--editor-text)]
|
|
1140
|
+
? 'text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]'
|
|
1143
1141
|
: (mode === 'light' ? 'text-amber-700' : 'text-amber-400/90')
|
|
1144
1142
|
}`}
|
|
1145
1143
|
>
|
|
@@ -1147,7 +1145,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1147
1145
|
</p>
|
|
1148
1146
|
</div>
|
|
1149
1147
|
) : (
|
|
1150
|
-
<p className="text-[11px] text-[var(--editor-text)]
|
|
1148
|
+
<p className="text-[11px] text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">
|
|
1151
1149
|
SDR files use the {curveInfo.label} look. Open Advanced to compare curves.
|
|
1152
1150
|
</p>
|
|
1153
1151
|
)}
|
|
@@ -1159,13 +1157,13 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1159
1157
|
|
|
1160
1158
|
{/* Footer — duration on the left, actions on the right. */}
|
|
1161
1159
|
<div className="flex items-center justify-between gap-2 px-5 py-3 border-t border-[var(--editor-border)]">
|
|
1162
|
-
<span className="text-xs text-[var(--editor-text)]
|
|
1160
|
+
<span className="text-xs text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)] tabular-nums">
|
|
1163
1161
|
{durationSec > 0 ? formatTimecode(durationSec) : ''}
|
|
1164
1162
|
</span>
|
|
1165
1163
|
<div className="flex items-center gap-2">
|
|
1166
1164
|
<button
|
|
1167
1165
|
onClick={handleCancel}
|
|
1168
|
-
className="text-sm px-4 py-1.5 rounded-md bg-[var(--editor-surface)] border border-[var(--editor-border)] text-[var(--editor-text)]
|
|
1166
|
+
className="text-sm px-4 py-1.5 rounded-md bg-[var(--editor-surface)] border border-[var(--editor-border)] text-[color-mix(in_srgb,var(--editor-text)_80%,transparent)] hover:opacity-90 transition-colors"
|
|
1169
1167
|
>
|
|
1170
1168
|
Cancel
|
|
1171
1169
|
</button>
|
|
@@ -1183,7 +1181,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1183
1181
|
)}
|
|
1184
1182
|
{expandedCurve && expandedUrl && createPortal(
|
|
1185
1183
|
<div
|
|
1186
|
-
className="fixed inset-0 z-[60] flex flex-col items-center justify-center gap-3 bg-black/90 p-6"
|
|
1184
|
+
className="fixed inset-0 z-[60] flex flex-col items-center justify-center gap-3 bg-black/90 p-6 text-white"
|
|
1187
1185
|
onClick={() => setExpandedCurveId(null)}
|
|
1188
1186
|
role="dialog"
|
|
1189
1187
|
aria-label={`${expandedCurve.label} preview`}
|
|
@@ -1243,7 +1241,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1243
1241
|
// block for this `fixed` overlay, sizing it to the scrolled page height and
|
|
1244
1242
|
// centering the panel off-screen below the fold.
|
|
1245
1243
|
return createPortal(
|
|
1246
|
-
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black">
|
|
1244
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black text-[var(--editor-text)]">
|
|
1247
1245
|
<div className="w-[96vw] h-[96vh] bg-[var(--editor-surface)] border border-[var(--editor-border)] rounded-2xl shadow-2xl flex overflow-hidden">
|
|
1248
1246
|
|
|
1249
1247
|
{/* Left — video */}
|
|
@@ -1257,7 +1255,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1257
1255
|
className="h-full w-full object-contain"
|
|
1258
1256
|
/>
|
|
1259
1257
|
) : (
|
|
1260
|
-
<p className="text-sm text-
|
|
1258
|
+
<p className="text-sm text-white/60">Render complete.</p>
|
|
1261
1259
|
)}
|
|
1262
1260
|
</div>
|
|
1263
1261
|
|
|
@@ -1268,10 +1266,10 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1268
1266
|
<span className="w-2 h-2 rounded-full bg-green-400" />
|
|
1269
1267
|
<div>
|
|
1270
1268
|
<p className="text-sm font-semibold text-[var(--editor-text)]">Render complete</p>
|
|
1271
|
-
<p className="text-xs text-[var(--editor-text)]
|
|
1269
|
+
<p className="text-xs text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">Saved to your library.</p>
|
|
1272
1270
|
</div>
|
|
1273
1271
|
</div>
|
|
1274
|
-
<button onClick={onClose} className="text-[var(--editor-text)]
|
|
1272
|
+
<button onClick={onClose} className="text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)] hover:text-[var(--editor-text)] transition-colors text-lg leading-none">×</button>
|
|
1275
1273
|
</div>
|
|
1276
1274
|
|
|
1277
1275
|
<div className="flex flex-col gap-3 p-5 flex-1">
|
|
@@ -1281,7 +1279,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1281
1279
|
<a
|
|
1282
1280
|
href={videoUrl}
|
|
1283
1281
|
download={downloadName}
|
|
1284
|
-
className=
|
|
1282
|
+
className={`w-full text-center text-sm px-4 py-2.5 rounded-lg border transition-colors font-medium ${mode === 'light' ? 'bg-green-600 border-green-700 text-white hover:bg-green-700' : 'bg-green-800/60 border-green-700 text-green-200 hover:bg-green-700/60'}`}
|
|
1285
1283
|
>
|
|
1286
1284
|
{extraOutputs.length > 0 ? 'Download HDR master' : 'Download'}
|
|
1287
1285
|
</a>
|
|
@@ -1292,14 +1290,14 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1292
1290
|
key={p}
|
|
1293
1291
|
href={adapter.fileUrl(p)}
|
|
1294
1292
|
download={basename(p)}
|
|
1295
|
-
className=
|
|
1293
|
+
className={`w-full text-center text-sm px-4 py-2.5 rounded-lg bg-[var(--editor-surface)] border transition-colors font-medium ${mode === 'light' ? 'border-green-600 text-green-700 hover:bg-green-50' : 'border-green-800 text-green-200/90 hover:bg-green-900/40'}`}
|
|
1296
1294
|
>
|
|
1297
1295
|
Download SDR version
|
|
1298
1296
|
</a>
|
|
1299
1297
|
))}
|
|
1300
1298
|
<button
|
|
1301
1299
|
onClick={onClose}
|
|
1302
|
-
className="w-full text-center text-sm px-4 py-2.5 rounded-lg bg-[var(--editor-surface)] border border-[var(--editor-border)] text-[var(--editor-text)]
|
|
1300
|
+
className="w-full text-center text-sm px-4 py-2.5 rounded-lg bg-[var(--editor-surface)] border border-[var(--editor-border)] text-[color-mix(in_srgb,var(--editor-text)_80%,transparent)] hover:opacity-90 transition-colors"
|
|
1303
1301
|
>
|
|
1304
1302
|
Close
|
|
1305
1303
|
</button>
|
|
@@ -1312,11 +1310,11 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1312
1310
|
}
|
|
1313
1311
|
|
|
1314
1312
|
return createPortal(
|
|
1315
|
-
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-md p-4">
|
|
1316
|
-
<div className="w-full max-w-md bg-[var(--editor-surface)] border border-
|
|
1313
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-md p-4 text-[var(--editor-text)]">
|
|
1314
|
+
<div className="w-full max-w-md bg-[var(--editor-surface)] border border-[var(--editor-border)] rounded-2xl shadow-2xl flex flex-col overflow-hidden">
|
|
1317
1315
|
|
|
1318
1316
|
{/* Header */}
|
|
1319
|
-
<div className="flex items-center justify-between px-5 py-4 border-b border-
|
|
1317
|
+
<div className="flex items-center justify-between px-5 py-4 border-b border-[var(--editor-border)]">
|
|
1320
1318
|
<div className="flex items-center gap-2.5">
|
|
1321
1319
|
{status === 'running' && <span className="w-2 h-2 rounded-full bg-[var(--editor-accent)] animate-pulse" />}
|
|
1322
1320
|
{status === 'error' && <span className="w-2 h-2 rounded-full bg-red-400" />}
|
|
@@ -1325,7 +1323,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1325
1323
|
</h2>
|
|
1326
1324
|
</div>
|
|
1327
1325
|
{status !== 'running' && (
|
|
1328
|
-
<button onClick={onClose} className="text-[var(--editor-text)]
|
|
1326
|
+
<button onClick={onClose} className="text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)] hover:text-[var(--editor-text)] transition-colors text-lg leading-none">×</button>
|
|
1329
1327
|
)}
|
|
1330
1328
|
</div>
|
|
1331
1329
|
|
|
@@ -1336,7 +1334,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1336
1334
|
|
|
1337
1335
|
<div className="flex flex-col items-center gap-1 text-center">
|
|
1338
1336
|
<h3 className="text-base font-semibold text-[var(--editor-text)]">Exporting your video</h3>
|
|
1339
|
-
<p className="text-xs text-[var(--editor-text)]
|
|
1337
|
+
<p className="text-xs text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)]">This can take a few minutes for longer videos.</p>
|
|
1340
1338
|
</div>
|
|
1341
1339
|
|
|
1342
1340
|
{/* Progress bar — always visible while rendering, above the stepper
|
|
@@ -1355,7 +1353,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1355
1353
|
|
|
1356
1354
|
{/* Granular phase progress (poll hosts advance through phases). */}
|
|
1357
1355
|
{view !== 'logs' && (
|
|
1358
|
-
<div className="w-full max-w-xs rounded-xl border border-
|
|
1356
|
+
<div className="w-full max-w-xs rounded-xl border border-[var(--editor-border)] bg-[color-mix(in_srgb,var(--editor-text)_4%,var(--editor-surface))] px-4 py-3">
|
|
1359
1357
|
<PhaseStepper current={phase} phases={stepperPhases(renderOpts)} />
|
|
1360
1358
|
</div>
|
|
1361
1359
|
)}
|
|
@@ -1366,7 +1364,7 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1366
1364
|
<button
|
|
1367
1365
|
onClick={() => setShowLog(o => !o)}
|
|
1368
1366
|
aria-expanded={showLog}
|
|
1369
|
-
className="mx-auto flex items-center gap-1 text-[11px] text-[var(--editor-text)]
|
|
1367
|
+
className="mx-auto flex items-center gap-1 text-[11px] text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)] hover:text-[color-mix(in_srgb,var(--editor-text)_80%,transparent)] transition-colors"
|
|
1370
1368
|
>
|
|
1371
1369
|
{showLog ? <ChevronDown size={13} /> : <ChevronRight size={13} />}
|
|
1372
1370
|
{showLog ? 'Hide render log' : 'Show render log'}
|
|
@@ -1375,17 +1373,17 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1375
1373
|
<div className="relative mt-2">
|
|
1376
1374
|
<button
|
|
1377
1375
|
onClick={() => navigator.clipboard.writeText(logs.join('\n'))}
|
|
1378
|
-
className="absolute top-2 right-2 z-10 rounded border border-white/10 bg-[#0a0e17] px-2 py-0.5 text-[10px] text-
|
|
1376
|
+
className="absolute top-2 right-2 z-10 rounded border border-white/10 bg-[#0a0e17] px-2 py-0.5 text-[10px] text-white/60 hover:text-white transition-colors"
|
|
1379
1377
|
title="Copy logs"
|
|
1380
1378
|
>
|
|
1381
1379
|
Copy
|
|
1382
1380
|
</button>
|
|
1383
1381
|
<div
|
|
1384
1382
|
ref={logRef}
|
|
1385
|
-
className="flex h-52 flex-col gap-0.5 overflow-y-auto rounded-lg border border-white/10 bg-[#0a0e17] px-3 py-2 font-mono text-[11px] text-
|
|
1383
|
+
className="flex h-52 flex-col gap-0.5 overflow-y-auto rounded-lg border border-white/10 bg-[#0a0e17] px-3 py-2 font-mono text-[11px] text-white/80"
|
|
1386
1384
|
>
|
|
1387
1385
|
{logs.length === 0
|
|
1388
|
-
? <span className="italic text-
|
|
1386
|
+
? <span className="italic text-white/40">Starting render engine…</span>
|
|
1389
1387
|
: logs.map((line, i) => <LogLine key={i} text={line} />)}
|
|
1390
1388
|
</div>
|
|
1391
1389
|
</div>
|
|
@@ -1400,14 +1398,14 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1400
1398
|
<div className="relative">
|
|
1401
1399
|
<button
|
|
1402
1400
|
onClick={() => navigator.clipboard.writeText(logs.join('\n') + (errorMsg ? '\n' + errorMsg : ''))}
|
|
1403
|
-
className="absolute top-2 right-2 z-10 rounded border border-white/10 bg-[#0a0e17] px-2 py-0.5 text-[10px] text-
|
|
1401
|
+
className="absolute top-2 right-2 z-10 rounded border border-white/10 bg-[#0a0e17] px-2 py-0.5 text-[10px] text-white/60 hover:text-white transition-colors"
|
|
1404
1402
|
title="Copy logs"
|
|
1405
1403
|
>
|
|
1406
1404
|
Copy
|
|
1407
1405
|
</button>
|
|
1408
1406
|
<div
|
|
1409
1407
|
ref={logRef}
|
|
1410
|
-
className="flex h-72 flex-col gap-0.5 overflow-y-auto rounded-lg border border-white/10 bg-[#0a0e17] px-3 py-2 font-mono text-[11px] text-
|
|
1408
|
+
className="flex h-72 flex-col gap-0.5 overflow-y-auto rounded-lg border border-white/10 bg-[#0a0e17] px-3 py-2 font-mono text-[11px] text-white/80"
|
|
1411
1409
|
>
|
|
1412
1410
|
{logs.map((line, i) => <LogLine key={i} text={line} />)}
|
|
1413
1411
|
{errorMsg && <span className="mt-1 text-red-400">{errorMsg}</span>}
|
|
@@ -1422,18 +1420,18 @@ export default function RenderModal<P extends Project = Project>({ projectId, ad
|
|
|
1422
1420
|
)}
|
|
1423
1421
|
|
|
1424
1422
|
{/* Footer */}
|
|
1425
|
-
<div className="flex items-center justify-end gap-2 px-5 py-3 border-t border-
|
|
1423
|
+
<div className="flex items-center justify-end gap-2 px-5 py-3 border-t border-[var(--editor-border)]">
|
|
1426
1424
|
{status === 'running' ? (
|
|
1427
1425
|
<button
|
|
1428
1426
|
onClick={handleCancel}
|
|
1429
|
-
className={`text-sm px-4 py-1.5 rounded-md bg-[
|
|
1427
|
+
className={`text-sm px-4 py-1.5 rounded-md bg-[var(--editor-surface)] border border-[var(--editor-border)] text-[color-mix(in_srgb,var(--editor-text)_80%,transparent)] transition-colors ${mode === 'light' ? 'hover:bg-red-50 hover:border-red-300 hover:text-red-700' : 'hover:bg-red-900/40 hover:border-red-700 hover:text-red-300'}`}
|
|
1430
1428
|
>
|
|
1431
1429
|
Cancel
|
|
1432
1430
|
</button>
|
|
1433
1431
|
) : (
|
|
1434
1432
|
<button
|
|
1435
1433
|
onClick={onClose}
|
|
1436
|
-
className="text-sm px-4 py-1.5 rounded-md bg-[
|
|
1434
|
+
className="text-sm px-4 py-1.5 rounded-md bg-[var(--editor-surface)] border border-[var(--editor-border)] text-[var(--editor-text)] hover:opacity-90 transition-colors"
|
|
1437
1435
|
>
|
|
1438
1436
|
Close
|
|
1439
1437
|
</button>
|
|
@@ -86,7 +86,7 @@ function VersionFramePane({
|
|
|
86
86
|
|
|
87
87
|
return (
|
|
88
88
|
<div className="flex-1 min-w-0 flex flex-col gap-1.5">
|
|
89
|
-
<span className="text-[11px] font-medium text-[var(--editor-text)]
|
|
89
|
+
<span className="text-[11px] font-medium text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)] truncate" title={label}>
|
|
90
90
|
{label}
|
|
91
91
|
</span>
|
|
92
92
|
<div className="relative aspect-video w-full overflow-hidden rounded-lg border border-[var(--editor-border)] bg-[var(--editor-bg)] flex items-center justify-center">
|
|
@@ -101,7 +101,7 @@ function VersionFramePane({
|
|
|
101
101
|
style={error ? { display: 'none' } : undefined}
|
|
102
102
|
/>
|
|
103
103
|
{loading && !error && (
|
|
104
|
-
<span className="absolute inset-0 flex items-center justify-center text-xs text-[var(--editor-text)]
|
|
104
|
+
<span className="absolute inset-0 flex items-center justify-center text-xs text-[color-mix(in_srgb,var(--editor-text)_50%,transparent)]">
|
|
105
105
|
Loading…
|
|
106
106
|
</span>
|
|
107
107
|
)}
|
|
@@ -169,16 +169,16 @@ export default function VersionCompare({
|
|
|
169
169
|
}, [onClose])
|
|
170
170
|
|
|
171
171
|
return createPortal(
|
|
172
|
-
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-md p-4">
|
|
173
|
-
<div className="w-full max-w-5xl bg-[var(--editor-surface)] border border-
|
|
172
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-md p-4 text-[var(--editor-text)]">
|
|
173
|
+
<div className="w-full max-w-5xl bg-[var(--editor-surface)] border border-[var(--editor-border)] rounded-2xl shadow-2xl flex flex-col overflow-hidden">
|
|
174
174
|
|
|
175
175
|
{/* Header */}
|
|
176
|
-
<div className="flex items-center justify-between px-5 py-4 border-b border-
|
|
176
|
+
<div className="flex items-center justify-between px-5 py-4 border-b border-[var(--editor-border)]">
|
|
177
177
|
<h2 className="text-sm font-semibold text-[var(--editor-text)]">Compare versions</h2>
|
|
178
178
|
<button
|
|
179
179
|
onClick={onClose}
|
|
180
180
|
aria-label="Close"
|
|
181
|
-
className="text-[var(--editor-text)]
|
|
181
|
+
className="text-[color-mix(in_srgb,var(--editor-text)_55%,transparent)] hover:text-[var(--editor-text)] transition-colors text-lg leading-none"
|
|
182
182
|
>
|
|
183
183
|
×
|
|
184
184
|
</button>
|
|
@@ -190,7 +190,7 @@ export default function VersionCompare({
|
|
|
190
190
|
{/* Picker row */}
|
|
191
191
|
<div className="flex flex-col sm:flex-row gap-3">
|
|
192
192
|
<label className="flex-1 min-w-0 flex flex-col gap-1.5">
|
|
193
|
-
<span className="text-[11px] font-semibold uppercase tracking-wide text-[var(--editor-text)]
|
|
193
|
+
<span className="text-[11px] font-semibold uppercase tracking-wide text-[color-mix(in_srgb,var(--editor-text)_50%,transparent)]">Left</span>
|
|
194
194
|
<select
|
|
195
195
|
value={leftHash}
|
|
196
196
|
onChange={(e) => setLeftHash(e.target.value)}
|
|
@@ -202,7 +202,7 @@ export default function VersionCompare({
|
|
|
202
202
|
</select>
|
|
203
203
|
</label>
|
|
204
204
|
<label className="flex-1 min-w-0 flex flex-col gap-1.5">
|
|
205
|
-
<span className="text-[11px] font-semibold uppercase tracking-wide text-[var(--editor-text)]
|
|
205
|
+
<span className="text-[11px] font-semibold uppercase tracking-wide text-[color-mix(in_srgb,var(--editor-text)_50%,transparent)]">Right</span>
|
|
206
206
|
<select
|
|
207
207
|
value={rightHash}
|
|
208
208
|
onChange={(e) => setRightHash(e.target.value)}
|
|
@@ -238,8 +238,8 @@ export default function VersionCompare({
|
|
|
238
238
|
{/* Time-scrub slider */}
|
|
239
239
|
<div className="flex flex-col gap-1.5">
|
|
240
240
|
<div className="flex items-center justify-between">
|
|
241
|
-
<span className="text-[11px] font-semibold uppercase tracking-wide text-[var(--editor-text)]
|
|
242
|
-
<span className="text-xs text-[var(--editor-text)]
|
|
241
|
+
<span className="text-[11px] font-semibold uppercase tracking-wide text-[color-mix(in_srgb,var(--editor-text)_50%,transparent)]">Time</span>
|
|
242
|
+
<span className="text-xs text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)] tabular-nums">{formatT(t)}</span>
|
|
243
243
|
</div>
|
|
244
244
|
<Slider
|
|
245
245
|
min={0}
|
|
@@ -87,7 +87,7 @@ export default function VersionPanel({ versions, restoring, onRestore, onSaveVer
|
|
|
87
87
|
IS the list. The count sits on the right as an explicit "N versions"
|
|
88
88
|
label so it reads as a quantity rather than part of the title. */}
|
|
89
89
|
<div className="shrink-0 flex items-center justify-between px-3 py-2 border-b border-[var(--editor-border)]">
|
|
90
|
-
<span className="text-xs font-medium text-[var(--editor-text)]
|
|
90
|
+
<span className="text-xs font-medium text-[color-mix(in_srgb,var(--editor-text)_60%,transparent)] uppercase tracking-wide">Versions</span>
|
|
91
91
|
{rows.length > 0 && (
|
|
92
92
|
<span className="text-[10px] text-[var(--editor-text)] opacity-50">
|
|
93
93
|
{rows.length} {rows.length === 1 ? 'version' : 'versions'}
|
|
@@ -106,7 +106,7 @@ export default function VersionPanel({ versions, restoring, onRestore, onSaveVer
|
|
|
106
106
|
onChange={e => setNameInput(e.target.value)}
|
|
107
107
|
placeholder="Name (optional)"
|
|
108
108
|
disabled={saving || !onSaveVersion}
|
|
109
|
-
className="min-w-0 flex-1 h-8 text-[11px] bg-[var(--editor-bg)] border border-[var(--editor-border)] rounded-md px-2 text-[var(--editor-text)] placeholder:text-[var(--editor-text)]
|
|
109
|
+
className="min-w-0 flex-1 h-8 text-[11px] bg-[var(--editor-bg)] border border-[var(--editor-border)] rounded-md px-2 text-[var(--editor-text)] placeholder:text-[color-mix(in_srgb,var(--editor-text)_40%,transparent)] focus:outline-none focus:border-[var(--editor-accent)] disabled:opacity-40"
|
|
110
110
|
/>
|
|
111
111
|
<button
|
|
112
112
|
onClick={handleSaveClick}
|