From fccaeaedda6bb3ee12a798e0ab5c6019300ca72d Mon Sep 17 00:00:00 2001 From: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Tue, 4 Nov 2025 14:52:07 +0300 Subject: [PATCH 1/7] fix layout break when toggling border --- .../src/routes/editor/ConfigSidebar.tsx | 1316 +++++++++-------- 1 file changed, 665 insertions(+), 651 deletions(-) diff --git a/apps/desktop/src/routes/editor/ConfigSidebar.tsx b/apps/desktop/src/routes/editor/ConfigSidebar.tsx index f07cd47616..1b6b034f8a 100644 --- a/apps/desktop/src/routes/editor/ConfigSidebar.tsx +++ b/apps/desktop/src/routes/editor/ConfigSidebar.tsx @@ -256,319 +256,325 @@ export function ConfigSidebar() { let scrollRef!: HTMLDivElement; return ( - - - s.camera === null, - ), - }, - { id: TAB_IDS.audio, icon: IconCapAudioOn }, - { - id: TAB_IDS.cursor, - icon: IconCapCursor, - disabled: !( - meta().type === "multiple" && (meta() as any).segments[0].cursor - ), - }, - window.FLAGS.captions && { - id: "captions" as const, - icon: IconCapMessageBubble, - }, - // { id: "hotkeys" as const, icon: IconCapHotkeys }, - ].filter(Boolean)} - > - {(item) => ( - { - // Clear any active selection first - if (editorState.timeline.selection) { - setEditorState("timeline", "selection", null); - } - setState("selectedTab", item.id); - scrollRef.scrollTo({ - top: 0, - }); - }} - disabled={item.disabled} - > -
+ + + s.camera === null, + ), + }, + { id: TAB_IDS.audio, icon: IconCapAudioOn }, + { + id: TAB_IDS.cursor, + icon: IconCapCursor, + disabled: !( + meta().type === "multiple" && + (meta() as any).segments[0].cursor + ), + }, + window.FLAGS.captions && { + id: "captions" as const, + icon: IconCapMessageBubble, + }, + // { id: "hotkeys" as const, icon: IconCapHotkeys }, + ].filter(Boolean)} + > + {(item) => ( + { + // Clear any active selection first + if (editorState.timeline.selection) { + setEditorState("timeline", "selection", null); + } + setState("selectedTab", item.id); + scrollRef.scrollTo({ + top: 0, + }); + }} + disabled={item.disabled} > - -
-
- )} -
- - {/** Center the indicator with the icon */} - - -
- - - -
- - - - } - > - - setProject("audio", "mute", v)} - /> - - {editorInstance.recordings.segments[0].mic?.channels === 2 && ( - - - options={STEREO_MODES} - optionValue="value" - optionTextValue="name" - value={STEREO_MODES.find( - (v) => v.value === project.audio.micStereoMode, - )} - onChange={(v) => { - if (v) setProject("audio", "micStereoMode", v.value); - }} - disallowEmptySelection - itemComponent={(props) => ( - - as={KSelect.Item} - item={props.item} - > - - {props.item.rawValue.name} - - +
- - class="flex-1 text-sm text-left truncate text-[--gray-500] font-normal"> - {(state) => {state.selectedOption().name}} - - - as={(props) => ( - - )} - /> - - - - as={KSelect.Content} - class={cx(topSlideAnimateClasses, "z-50")} - > - - class="overflow-y-auto max-h-32" - as={KSelect.Listbox} - /> - - - - + +
+ )} + + + {/** Center the indicator with the icon */} + + +
+ + + +
+ + + + } + > + + setProject("audio", "mute", v)} + /> + + {editorInstance.recordings.segments[0].mic?.channels === 2 && ( + + + options={STEREO_MODES} + optionValue="value" + optionTextValue="name" + value={STEREO_MODES.find( + (v) => v.value === project.audio.micStereoMode, + )} + onChange={(v) => { + if (v) setProject("audio", "micStereoMode", v.value); + }} + disallowEmptySelection + itemComponent={(props) => ( + + as={KSelect.Item} + item={props.item} + > + + {props.item.rawValue.name} + + + )} + > + + class="flex-1 text-sm text-left truncate text-[--gray-500] font-normal"> + {(state) => {state.selectedOption().name}} + + + as={(props) => ( + + )} + /> + + + + as={KSelect.Content} + class={cx(topSlideAnimateClasses, "z-50")} + > + + class="overflow-y-auto max-h-32" + as={KSelect.Listbox} + /> + + + + + )} - {/* + {/* setProject("audio", "mute", v)} /> */} - {/* + {/* */} - - {meta().hasMicrophone && ( - } - > - setProject("audio", "micVolumeDb", v[0])} - minValue={-30} - maxValue={10} - step={0.1} - formatTooltip={(v) => - v <= -30 ? "Muted" : `${v > 0 ? "+" : ""}${v.toFixed(1)} dB` - } - /> - - )} - {meta().hasSystemAudio && ( - } - > - setProject("audio", "systemVolumeDb", v[0])} - minValue={-30} - maxValue={10} - step={0.1} - formatTooltip={(v) => - v <= -30 ? "Muted" : `${v > 0 ? "+" : ""}${v.toFixed(1)} dB` - } - /> - - )} - - - } - value={ - { - setProject("cursor", "hide", !v); - }} - /> - } - /> - - }> - setProject("cursor", "size", v[0])} - minValue={20} - maxValue={300} - step={1} - /> + {meta().hasMicrophone && ( + } + > + setProject("audio", "micVolumeDb", v[0])} + minValue={-30} + maxValue={10} + step={0.1} + formatTooltip={(v) => + v <= -30 ? "Muted" : `${v > 0 ? "+" : ""}${v.toFixed(1)} dB` + } + /> + + )} + {meta().hasSystemAudio && ( + } + > + setProject("audio", "systemVolumeDb", v[0])} + minValue={-30} + maxValue={10} + step={0.1} + formatTooltip={(v) => + v <= -30 ? "Muted" : `${v > 0 ? "+" : ""}${v.toFixed(1)} dB` + } + /> + + )} + + } + name="Cursor" + icon={} value={ - setProject("cursor", "hideWhenIdle", value) - } + checked={!project.cursor.hide} + onChange={(v) => { + setProject("cursor", "hide", !v); + }} /> } /> - - -
- { - const rounded = clampIdleDelay(v[0]); - setProject("cursor", "hideWhenIdleDelay" as any, rounded); - }} - minValue={0.5} - maxValue={5} - step={0.1} - formatTooltip={(value) => `${value.toFixed(1)}s`} - /> - - {cursorIdleDelay().toFixed(1)}s - -
-
-
- + + }> + setProject("cursor", "size", v[0])} + minValue={20} + maxValue={300} + step={1} + /> + } + name="Hide When Idle" + icon={} value={ { - setProject("cursor", "raw", !value); - }} + checked={project.cursor.hideWhenIdle} + onChange={(value) => + setProject("cursor", "hideWhenIdle", value) + } /> } /> - - {/* if Content has padding or margin the animation doesn't look as good */} -
- + + +
setProject("cursor", "tension", v[0])} - minValue={1} - maxValue={500} - step={1} - /> - - - setProject("cursor", "friction", v[0])} - minValue={0} - maxValue={50} + class="flex-1" + value={[cursorIdleDelay()]} + onChange={(v) => { + const rounded = clampIdleDelay(v[0]); + setProject( + "cursor", + "hideWhenIdleDelay" as any, + rounded, + ); + }} + minValue={0.5} + maxValue={5} step={0.1} + formatTooltip={(value) => `${value.toFixed(1)}s`} /> - - - setProject("cursor", "mass", v[0])} - minValue={0.1} - maxValue={10} - step={0.01} + + {cursorIdleDelay().toFixed(1)}s + +
+
+
+ + } + value={ + { + setProject("cursor", "raw", !value); + }} /> - -
-
-
- } - value={ - { - setProject("cursor", "useSvg" as any, value); - }} + } /> - } - /> - + + {/* if Content has padding or margin the animation doesn't look as good */} +
+ + setProject("cursor", "tension", v[0])} + minValue={1} + maxValue={500} + step={1} + /> + + + setProject("cursor", "friction", v[0])} + minValue={0} + maxValue={50} + step={0.1} + /> + + + setProject("cursor", "mass", v[0])} + minValue={0.1} + maxValue={10} + step={0.01} + /> + +
+
+ + } + value={ + { + setProject("cursor", "useSvg" as any, value); + }} + /> + } + /> + - {/* + {/* setProject("cursor", "motionBlur", v[0])} @@ -577,7 +583,7 @@ export function ConfigSidebar() { step={0.001} /> */} - {/* }> + {/* }> */} -
- - }> - - - - - - - - - - -
- - {(selection) => ( -
- - { - const zoomSelection = selection(); - if (zoomSelection.type !== "zoom") return; - - const segments = zoomSelection.indices - .map((index) => ({ - index, - segment: project.timeline?.zoomSegments?.[index], - })) - .filter( - (item): item is { index: number; segment: ZoomSegment } => - item.segment !== undefined, - ); - - if (segments.length === 0) { - setEditorState("timeline", "selection", null); - return; - } - return { selection: zoomSelection, segments }; - })()} - > - {(value) => ( -
-
-
- - setEditorState("timeline", "selection", null) - } - leftIcon={} - > - Done - - - {value().segments.length} zoom{" "} - {value().segments.length === 1 - ? "segment" - : "segments"}{" "} - selected - -
- { - projectActions.deleteZoomSegments( - value().segments.map((s) => s.index), - ); - }} - leftIcon={} - > - Delete - -
- - - {(item, index) => ( -
- -
- )} -
-
- } - > - - {(item) => ( -
- -
- )} -
-
-
- )} -
- { - const sceneSelection = selection(); - if (sceneSelection.type !== "scene") return; - - const segments = sceneSelection.indices - .map((idx) => ({ - segment: project.timeline?.sceneSegments?.[idx], - index: idx, - })) - .filter((s) => s.segment !== undefined); - - if (segments.length === 0) return; - return { selection: sceneSelection, segments }; - })()} - > - {(value) => ( - 1} - fallback={ - + + + }> + + + + + + + + + + +
+ + {(selection) => ( +
+ + { + const zoomSelection = selection(); + if (zoomSelection.type !== "zoom") return; + + const segments = zoomSelection.indices + .map((index) => ({ + index, + segment: project.timeline?.zoomSegments?.[index], + })) + .filter( + ( + item, + ): item is { index: number; segment: ZoomSegment } => + item.segment !== undefined, + ); + + if (segments.length === 0) { + setEditorState("timeline", "selection", null); + return; } - > + return { selection: zoomSelection, segments }; + })()} + > + {(value) => (
@@ -768,7 +688,7 @@ export function ConfigSidebar() { Done - {value().segments.length} scene{" "} + {value().segments.length} zoom{" "} {value().segments.length === 1 ? "segment" : "segments"}{" "} @@ -778,95 +698,184 @@ export function ConfigSidebar() { { - const indices = value().selection.indices; - - // Delete segments in reverse order to maintain indices - [...indices] - .sort((a, b) => b - a) - .forEach((idx) => { - projectActions.deleteSceneSegment(idx); - }); + projectActions.deleteZoomSegments( + value().segments.map((s) => s.index), + ); }} leftIcon={} > Delete
+ + + {(item, index) => ( +
+ +
+ )} +
+
+ } + > + + {(item) => ( +
+ +
+ )} +
+
-
- )} - - { - const clipSelection = selection(); - if (clipSelection.type !== "clip") return; - - const segments = clipSelection.indices - .map((idx) => ({ - segment: project.timeline?.segments?.[idx], - index: idx, - })) - .filter((s) => s.segment !== undefined); - - if (segments.length === 0) return; - return { selection: clipSelection, segments }; - })()} - > - {(value) => ( - 1} - fallback={ - - } - > -
-
-
+ )} + + { + const sceneSelection = selection(); + if (sceneSelection.type !== "scene") return; + + const segments = sceneSelection.indices + .map((idx) => ({ + segment: project.timeline?.sceneSegments?.[idx], + index: idx, + })) + .filter((s) => s.segment !== undefined); + + if (segments.length === 0) return; + return { selection: sceneSelection, segments }; + })()} + > + {(value) => ( + 1} + fallback={ + + } + > +
+
+
+ + setEditorState("timeline", "selection", null) + } + leftIcon={} + > + Done + + + {value().segments.length} scene{" "} + {value().segments.length === 1 + ? "segment" + : "segments"}{" "} + selected + +
- setEditorState("timeline", "selection", null) - } - leftIcon={} + variant="danger" + onClick={() => { + const indices = value().selection.indices; + + // Delete segments in reverse order to maintain indices + [...indices] + .sort((a, b) => b - a) + .forEach((idx) => { + projectActions.deleteSceneSegment(idx); + }); + }} + leftIcon={} > - Done + Delete - - {value().segments.length} clip{" "} - {value().segments.length === 1 - ? "segment" - : "segments"}{" "} - selected -
- { - const indices = value().selection.indices; - - // Delete segments in reverse order to maintain indices - [...indices] - .sort((a, b) => b - a) - .forEach((idx) => { - projectActions.deleteClipSegment(idx); - }); - }} - leftIcon={} - > - Delete -
-
- - )} - - -
- )} - - + + )} + + { + const clipSelection = selection(); + if (clipSelection.type !== "clip") return; + + const segments = clipSelection.indices + .map((idx) => ({ + segment: project.timeline?.segments?.[idx], + index: idx, + })) + .filter((s) => s.segment !== undefined); + + if (segments.length === 0) return; + return { selection: clipSelection, segments }; + })()} + > + {(value) => ( + 1} + fallback={ + + } + > +
+
+
+ + setEditorState("timeline", "selection", null) + } + leftIcon={} + > + Done + + + {value().segments.length} clip{" "} + {value().segments.length === 1 + ? "segment" + : "segments"}{" "} + selected + +
+ { + const indices = value().selection.indices; + + // Delete segments in reverse order to maintain indices + [...indices] + .sort((a, b) => b - a) + .forEach((idx) => { + projectActions.deleteClipSegment(idx); + }); + }} + leftIcon={} + > + Delete + +
+
+
+ )} +
+ +
+ )} +
+ +
); } @@ -1531,112 +1540,110 @@ function BackgroundConfig(props: { scrollRef: HTMLDivElement }) { const angle = () => source().angle ?? 90; return ( - <> -
-
- { - backgrounds.gradient.from = from; - setProject("background", "source", { - type: "gradient", - from, - }); - }} - /> - { - backgrounds.gradient.to = to; - setProject("background", "source", { - type: "gradient", - to, - }); - }} - /> -
{ - const start = angle(); - const resumeHistory = projectHistory.pause(); - - createRoot((dispose) => - createEventListenerMap(window, { - mouseup: () => dispose(), - mousemove: (moveEvent) => { - const rawNewAngle = - Math.round( - start + - (downEvent.clientY - moveEvent.clientY), - ) % max; - const newAngle = moveEvent.shiftKey - ? rawNewAngle - : Math.round(rawNewAngle / 45) * 45; - - if ( - !moveEvent.shiftKey && - hapticsEnabled() && - project.background.source.type === - "gradient" && - project.background.source.angle !== newAngle - ) { - commands.performHapticFeedback( - "Alignment", - "Now", - ); - } - - setProject("background", "source", { - type: "gradient", - angle: - newAngle < 0 ? newAngle + max : newAngle, - }); - }, - }), - ); - }} - > -
-
-
-
- - {(gradient) => ( -