Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/desktop/src-tauri/src/recording.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1018,7 +1018,7 @@ fn project_config_from_recording(
} else {
Vec::new()
},
layout_segments: Vec::new(),
scene_segments: Vec::new(),
}),
..default_config.unwrap_or_default()
}
Expand Down
11 changes: 7 additions & 4 deletions apps/desktop/src/routes/(window-chrome)/(main).tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,12 @@ function Page() {

if (rawOptions.captureTarget.variant === "display") {
const screenId = rawOptions.captureTarget.id;
screen = _screens()?.find((s) => s.id === screenId) ?? _screens()?.[0];
screen =
_screens()?.find((s: any) => s.id === screenId) ?? _screens()?.[0];
} else if (rawOptions.captureTarget.variant === "area") {
const screenId = rawOptions.captureTarget.screen;
screen = _screens()?.find((s) => s.id === screenId) ?? _screens()?.[0];
screen =
_screens()?.find((s: any) => s.id === screenId) ?? _screens()?.[0];
}

return screen;
Expand All @@ -160,7 +162,8 @@ function Page() {

if (rawOptions.captureTarget.variant === "window") {
const windowId = rawOptions.captureTarget.id;
win = _windows()?.find((s) => s.id === windowId) ?? _windows()?.[0];
win =
_windows()?.find((s: any) => s.id === windowId) ?? _windows()?.[0];
}

return win;
Expand All @@ -173,7 +176,7 @@ function Page() {
if ("DeviceID" in cameraID && c.device_id === cameraID.DeviceID)
return c;
}),
micName: () => mics.data?.find((name) => name === rawOptions.micName),
micName: () => mics.data?.find((name: any) => name === rawOptions.micName),
};

// if target is window and no windows are available, switch to screen capture
Expand Down
24 changes: 13 additions & 11 deletions apps/desktop/src/routes/editor/ConfigSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
type BackgroundSource,
type CameraShape,
commands,
type LayoutSegment,
type SceneSegment,
type StereoMode,
type TimelineSegment,
type ZoomSegment,
Expand Down Expand Up @@ -671,18 +671,18 @@ export function ConfigSidebar() {
</Show>
<Show
when={(() => {
const layoutSelection = selection();
if (layoutSelection.type !== "layout") return;
const sceneSelection = selection();
if (sceneSelection.type !== "scene") return;

const segment =
project.timeline?.layoutSegments?.[layoutSelection.index];
project.timeline?.sceneSegments?.[sceneSelection.index];
if (!segment) return;

return { selection: layoutSelection, segment };
return { selection: sceneSelection, segment };
})()}
>
{(value) => (
<LayoutSegmentConfig
<SceneSegmentConfig
segment={value().segment}
segmentIndex={value().selection.index}
/>
Expand Down Expand Up @@ -1839,7 +1839,9 @@ function ZoomSegmentPreview(props: {
const video = document.createElement("video");
createEffect(() => {
const path = convertFileSrc(
`${editorInstance.path}/content/segments/segment-${segmentIndex()}/display.mp4`,
`${
editorInstance.path
}/content/segments/segment-${segmentIndex()}/display.mp4`,
);
video.src = path;
video.preload = "auto";
Expand Down Expand Up @@ -2282,9 +2284,9 @@ function ClipSegmentConfig(props: {
);
}

function LayoutSegmentConfig(props: {
function SceneSegmentConfig(props: {
segmentIndex: number;
segment: LayoutSegment;
segment: SceneSegment;
}) {
const { setProject, setEditorState, projectActions } = useEditorContext();

Expand All @@ -2302,7 +2304,7 @@ function LayoutSegmentConfig(props: {
<EditorButton
variant="danger"
onClick={() => {
projectActions.deleteLayoutSegment(props.segmentIndex);
projectActions.deleteSceneSegment(props.segmentIndex);
}}
leftIcon={<IconCapTrash />}
>
Expand All @@ -2316,7 +2318,7 @@ function LayoutSegmentConfig(props: {
onChange={(v) => {
setProject(
"timeline",
"layoutSegments",
"sceneSegments",
props.segmentIndex,
"mode",
v as "default" | "cameraOnly" | "hideCamera",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ import {
Show,
} from "solid-js";
import { produce } from "solid-js/store";
import IconLucideEyeOff from "~icons/lucide/eye-off";
import IconLucideMonitor from "~icons/lucide/monitor";
import IconLucideVideo from "~icons/lucide/video";

import { useEditorContext } from "../context";
import {
useSegmentContext,
Expand All @@ -24,13 +22,13 @@ import {
} from "./context";
import { SegmentContent, SegmentHandle, SegmentRoot, TrackRoot } from "./Track";

export type LayoutSegmentDragState =
export type SceneSegmentDragState =
| { type: "idle" }
| { type: "movePending" }
| { type: "moving" };

export function LayoutTrack(props: {
onDragStateChanged: (v: LayoutSegmentDragState) => void;
export function SceneTrack(props: {
onDragStateChanged: (v: SceneSegmentDragState) => void;
handleUpdatePlayhead: (e: MouseEvent) => void;
}) {
const { project, setProject, projectHistory, setEditorState, editorState } =
Expand All @@ -49,13 +47,13 @@ export function LayoutTrack(props: {
// users from creating new segments. This effect ensures we reset the hover state
// when all segments are deleted.
createEffect(() => {
const segments = project.timeline?.layoutSegments;
const segments = project.timeline?.sceneSegments;
if (!segments || segments.length === 0) {
setHoveringSegment(false);
}
});

const getLayoutIcon = (mode: string | undefined) => {
const getSceneIcon = (mode: string | undefined) => {
switch (mode) {
case "cameraOnly":
return <IconLucideVideo class="size-3.5" />;
Expand All @@ -66,7 +64,7 @@ export function LayoutTrack(props: {
}
};

const getLayoutLabel = (mode: string | undefined) => {
const getSceneLabel = (mode: string | undefined) => {
switch (mode) {
case "cameraOnly":
return "Camera Only";
Expand All @@ -91,7 +89,7 @@ export function LayoutTrack(props: {
(e.clientX - bounds.left) * secsPerPixel() +
editorState.timeline.transform.position;

const segments = project.timeline?.layoutSegments || [];
const segments = project.timeline?.sceneSegments || [];
const nextSegmentIndex = segments.findIndex((s) => time < s.start);

let maxDuration = 3; // Default duration
Expand Down Expand Up @@ -158,23 +156,23 @@ export function LayoutTrack(props: {

e.stopPropagation();
batch(() => {
setProject("timeline", "layoutSegments", (v) => v ?? []);
setProject("timeline", "sceneSegments", (v) => v ?? []);
setProject(
"timeline",
"layoutSegments",
produce((layoutSegments) => {
layoutSegments ??= [];
"sceneSegments",
produce((sceneSegments) => {
sceneSegments ??= [];

let index = layoutSegments.length;
let index = sceneSegments.length;

for (let i = layoutSegments.length - 1; i >= 0; i--) {
if (layoutSegments[i].start > time) {
for (let i = sceneSegments.length - 1; i >= 0; i--) {
if (sceneSegments[i].start > time) {
index = i;
break;
}
}

layoutSegments.splice(index, 0, {
sceneSegments.splice(index, 0, {
start: time,
Comment on lines 160 to 176
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Insertion index search is incorrect; can insert after the wrong neighbor

The backward scan picks the highest index with start > time, which inserts after segments that also satisfy the condition (e.g., time=4 with starts [2,5,8] inserts at 2, before 8 but after 5). You want the first index where start > time.

Apply:

-                                  let index = sceneSegments.length;
-
-                                  for (let i = sceneSegments.length - 1; i >= 0; i--) {
-                                    if (sceneSegments[i].start > time) {
-                                      index = i;
-                                      break;
-                                    }
-                                  }
+                                  let index = sceneSegments.findIndex((s) => s.start > time);
+                                  if (index === -1) index = sceneSegments.length;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setProject(
"timeline",
"layoutSegments",
produce((layoutSegments) => {
layoutSegments ??= [];
"sceneSegments",
produce((sceneSegments) => {
sceneSegments ??= [];
let index = layoutSegments.length;
let index = sceneSegments.length;
for (let i = layoutSegments.length - 1; i >= 0; i--) {
if (layoutSegments[i].start > time) {
for (let i = sceneSegments.length - 1; i >= 0; i--) {
if (sceneSegments[i].start > time) {
index = i;
break;
}
}
layoutSegments.splice(index, 0, {
sceneSegments.splice(index, 0, {
start: time,
setProject(
"timeline",
"sceneSegments",
produce((sceneSegments) => {
sceneSegments ??= [];
let index = sceneSegments.findIndex((s) => s.start > time);
if (index === -1) index = sceneSegments.length;
sceneSegments.splice(index, 0, {
start: time,
// …
});
})
);
🤖 Prompt for AI Agents
In apps/desktop/src/routes/editor/Timeline/SceneTrack.tsx around lines 160–176,
the backward loop currently finds the highest index with start > time (causing
incorrect insertion after the wrong neighbor); change the search to find the
first index where sceneSegments[i].start > time — for example, iterate forward
from i=0..len-1, set index = i and break when sceneSegments[i].start > time
(keep default index = sceneSegments.length), or alternatively adjust the
backward scan to detect the last start <= time and set index = i+1; then splice
at that computed index.

end: time + maxDuration,
mode: "cameraOnly",
Expand All @@ -187,10 +185,10 @@ export function LayoutTrack(props: {
}}
>
<For
each={project.timeline?.layoutSegments}
each={project.timeline?.sceneSegments}
fallback={
<div class="text-center text-sm text-[--text-tertiary] flex flex-col justify-center items-center inset-0 w-full bg-gray-3/20 dark:bg-gray-3/10 hover:bg-gray-3/30 dark:hover:bg-gray-3/20 transition-colors rounded-xl pointer-events-none">
<div>Click to add layout segment</div>
<div>Click to add scene segment</div>
<div class="text-[10px] text-[--text-tertiary]/40 mt-0.5">
(Make the camera full screen, or hide it)
</div>
Expand All @@ -200,7 +198,7 @@ export function LayoutTrack(props: {
{(segment, i) => {
const { setTrackState } = useTrackContext();

const layoutSegments = () => project.timeline!.layoutSegments!;
const sceneSegments = () => project.timeline!.sceneSegments!;

function createMouseDownDrag<T>(
setup: () => T,
Expand All @@ -225,13 +223,13 @@ export function LayoutTrack(props: {
if (!moved) {
e.stopPropagation();
setEditorState("timeline", "selection", {
type: "layout",
type: "scene",
index: i(),
});
props.handleUpdatePlayhead(e);
} else {
setEditorState("timeline", "selection", {
type: "layout",
type: "scene",
index: i(),
});
}
Expand Down Expand Up @@ -272,9 +270,9 @@ export function LayoutTrack(props: {

const isSelected = createMemo(() => {
const selection = editorState.timeline.selection;
if (!selection || selection.type !== "layout") return false;
if (!selection || selection.type !== "scene") return false;

const segmentIndex = project.timeline?.layoutSegments?.findIndex(
const segmentIndex = project.timeline?.sceneSegments?.findIndex(
(s) => s.start === segment.start && s.end === segment.end,
);

Expand Down Expand Up @@ -309,8 +307,8 @@ export function LayoutTrack(props: {

const maxValue = segment.end - 1;

for (let i = layoutSegments().length - 1; i >= 0; i--) {
const segment = layoutSegments()[i]!;
for (let i = sceneSegments().length - 1; i >= 0; i--) {
const segment = sceneSegments()[i]!;
if (segment.end <= start) {
minValue = segment.end;
break;
Expand All @@ -326,7 +324,7 @@ export function LayoutTrack(props: {

setProject(
"timeline",
"layoutSegments",
"sceneSegments",
i(),
"start",
Math.min(
Expand All @@ -337,7 +335,7 @@ export function LayoutTrack(props: {

setProject(
"timeline",
"layoutSegments",
"sceneSegments",
produce((s) => {
if (s) {
s.sort((a, b) => a.start - b.start);
Expand All @@ -353,8 +351,8 @@ export function LayoutTrack(props: {
() => {
const original = { ...segment };

const prevSegment = layoutSegments()[i() - 1];
const nextSegment = layoutSegments()[i() + 1];
const prevSegment = sceneSegments()[i() - 1];
const nextSegment = sceneSegments()[i() + 1];

const minStart = prevSegment?.end ?? 0;
const maxEnd = nextSegment?.start ?? duration();
Expand All @@ -379,7 +377,7 @@ export function LayoutTrack(props: {
else if (newEnd > value.maxEnd)
delta = value.maxEnd - value.original.end;

setProject("timeline", "layoutSegments", i(), {
setProject("timeline", "sceneSegments", i(), {
start: value.original.start + delta,
end: value.original.end + delta,
});
Expand All @@ -392,12 +390,12 @@ export function LayoutTrack(props: {
return (
<Show when={ctx.width() > 80}>
<div class="flex flex-col gap-1 justify-center items-center text-xs whitespace-nowrap text-gray-1 dark:text-gray-12 animate-in fade-in">
<span class="opacity-70">Layout</span>
<span class="opacity-70">Scene</span>
<div class="flex gap-1 items-center text-md">
{getLayoutIcon(segment.mode)}
{getSceneIcon(segment.mode)}
{ctx.width() > 120 && (
<span class="text-xs">
{getLayoutLabel(segment.mode)}
{getSceneLabel(segment.mode)}
</span>
)}
</div>
Expand All @@ -416,8 +414,8 @@ export function LayoutTrack(props: {

let maxValue = duration();

for (let i = 0; i < layoutSegments().length; i++) {
const segment = layoutSegments()[i]!;
for (let i = 0; i < sceneSegments().length; i++) {
const segment = sceneSegments()[i]!;
if (segment.start > end) {
maxValue = segment.start;
break;
Expand All @@ -432,7 +430,7 @@ export function LayoutTrack(props: {

setProject(
"timeline",
"layoutSegments",
"sceneSegments",
i(),
"end",
Math.min(
Expand All @@ -443,7 +441,7 @@ export function LayoutTrack(props: {

setProject(
"timeline",
"layoutSegments",
"sceneSegments",
produce((s) => {
if (s) {
s.sort((a, b) => a.start - b.start);
Expand Down
Loading
Loading