Skip to content

Add project rename feature to editor #566

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ device_query = "2.1.0"
base64 = "0.22.1"
reqwest = { version = "0.12.7", features = ["json", "stream", "multipart"] }
dotenvy_macro = "0.15.7"
either = "1.13.0"
global-hotkey = "0.6.3"
rand = "0.8.5"
cpal.workspace = true
Expand Down
18 changes: 18 additions & 0 deletions apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ use scap::frame::VideoFrame;
use serde::{Deserialize, Serialize};
use serde_json::json;
use specta::Type;
use either::Either;
use std::collections::BTreeMap;
use std::time::Duration;
use std::{
Expand Down Expand Up @@ -1170,6 +1171,22 @@ async fn set_project_config(
Ok(())
}

#[tauri::command]
#[specta::specta]
async fn rename_project(
editor_instance: WindowEditorInstance,
name: String,
) -> Result<(), String> {
let mut meta =
RecordingMeta::load_for_project(&editor_instance.project_path).map_err(|e| e.to_string())?;
meta.pretty_name = name;
meta.save_for_project()
.map_err(|e| match e {
Either::Left(e) => e.to_string(),
Either::Right(e) => e.to_string(),
})
}

#[tauri::command]
#[specta::specta]
async fn list_audio_devices() -> Result<Vec<String>, ()> {
Expand Down Expand Up @@ -1981,6 +1998,7 @@ pub async fn run(recording_logging_handle: LoggingHandle) {
stop_playback,
set_playhead_position,
set_project_config,
rename_project,
permissions::open_permission_settings,
permissions::do_permissions_check,
permissions::request_permission,
Expand Down
70 changes: 53 additions & 17 deletions apps/desktop/src/routes/editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import Cropper, { cropToFloor } from "~/components/Cropper";
import { Toggle } from "~/components/Toggle";
import Tooltip from "~/components/Tooltip";
import { events, type Crop } from "~/utils/tauri";
import { events, commands, type Crop } from "~/utils/tauri";
import { ConfigSidebar } from "./ConfigSidebar";
import {
EditorContextProvider,
Expand All @@ -34,13 +34,7 @@
import { Header } from "./Header";
import { Player } from "./Player";
import { Timeline } from "./Timeline";
import {
Dialog,
DialogContent,
EditorButton,
Input,
Subfield,
} from "./ui";
import { Dialog, DialogContent, EditorButton, Input, Subfield } from "./ui";

export function Editor() {
return (
Expand All @@ -58,7 +52,7 @@
const d = ctx.metaQuery.data;
if (!d)
throw new Error(
"metaQuery.data is undefined - how did this happen?"
"metaQuery.data is undefined - how did this happen?",
);
return d;
},
Expand All @@ -85,7 +79,7 @@
events.editorStateChanged.listen((e) => {
renderFrame.clear();
setEditorState("playbackTime", e.payload.playhead_position / FPS);
})
}),
);

const renderFrame = throttle((time: number) => {
Expand All @@ -108,14 +102,14 @@
on(frameNumberToRender, (number) => {
if (editorState.playing) return;
renderFrame(number);
})
}),
);

createEffect(
on(
() => trackDeep(project),
() => renderFrame(editorState.playbackTime)
)
() => renderFrame(editorState.playbackTime),
),
);

return (
Expand Down Expand Up @@ -222,7 +216,7 @@
>
{(dialog) => {
const [name, setName] = createSignal(
presets.query.data?.presets[dialog().presetIndex].name!
presets.query.data?.presets[dialog().presetIndex].name!,
);

const renamePreset = createMutation(() => ({
Expand Down Expand Up @@ -255,6 +249,48 @@
);
}}
</Match>
<Match
when={(() => {
const d = dialog();
if (d.type === "renameProject") return d;
})()}
>
{() => {

Check failure on line 258 in apps/desktop/src/routes/editor/Editor.tsx

View workflow job for this annotation

GitHub Actions / Typecheck

No overload matches this call.
const { meta, refetchMeta } = useEditorContext();
const [name, setName] = createSignal(meta().prettyName);

const renameProject = createMutation(() => ({
mutationFn: async () => {
await commands.renameProject(name());
await refetchMeta();
},
onSuccess: () => {
setDialog((d) => ({ ...d, open: false }));
},
}));

return (
<DialogContent
title="Rename Project"
confirm={
<Dialog.ConfirmButton
disabled={renameProject.isPending}
onClick={() => renameProject.mutate()}
>
Rename
</Dialog.ConfirmButton>
}
>
<Subfield name="Name" required />
<Input
class="mt-2"
value={name()}
onInput={(e) => setName(e.currentTarget.value)}
/>
</DialogContent>
);
}}
</Match>
<Match
when={(() => {
const d = dialog();
Expand Down Expand Up @@ -309,7 +345,7 @@
createStore({
showGrid: false,
}),
{ name: "cropOptionsState" }
{ name: "cropOptionsState" },
);

const display = editorInstance.recordings.segments[0].display;
Expand Down Expand Up @@ -398,7 +434,7 @@
"flex items-center bg-gray-3 justify-center text-center rounded-[0.5rem] h-[2rem] w-[2rem] border text-[0.875rem] focus:border-blue-9 outline-none transition-colors duration-200",
cropOptions.showGrid
? "bg-gray-3 text-blue-9 border-blue-9"
: "text-gray-12"
: "text-gray-12",
)}
onClick={() =>
setCropOptions("showGrid", (s) => !s)
Expand Down Expand Up @@ -440,7 +476,7 @@
class="shadow pointer-events-none max-h-[70vh]"
alt="screenshot"
src={convertFileSrc(
`${editorInstance.path}/screenshots/display.jpg`
`${editorInstance.path}/screenshots/display.jpg`,
)}
/>
</Cropper>
Expand Down
12 changes: 10 additions & 2 deletions apps/desktop/src/routes/editor/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ export function Header() {
leftIcon={<IconLucideFolder class="w-5" />}
/>

<EditorButton
onClick={() => setDialog({ type: "renameProject", open: true })}
tooltipText="Rename project"
leftIcon={<IconLucideEdit class="w-5" />}
/>

<p class="text-sm text-gray-12">
{meta().prettyName}
<span class="text-sm text-gray-11">.cap</span>
Expand Down Expand Up @@ -131,7 +137,7 @@ export function Header() {
data-tauri-drag-region
class={cx(
"flex-1 h-full flex flex-row items-center gap-2 pl-2",
ostype() !== "windows" && "pr-2"
ostype() !== "windows" && "pr-2",
)}
>
<EditorButton
Expand Down Expand Up @@ -196,7 +202,9 @@ const UploadIcon = (props: ComponentProps<"svg">) => {
stroke-linecap="round"
stroke-linejoin="round"
class={cx(
exportState.type !== "idle" && exportState.type !== "done" && "bounce"
exportState.type !== "idle" &&
exportState.type !== "done" &&
"bounce",
)}
/>
</svg>
Expand Down
31 changes: 16 additions & 15 deletions apps/desktop/src/routes/editor/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type CurrentDialog =
| { type: "createPreset" }
| { type: "renamePreset"; presetIndex: number }
| { type: "deletePreset"; presetIndex: number }
| { type: "renameProject" }
| { type: "crop"; position: XY<number>; size: XY<number> }
| { type: "export" };

Expand All @@ -60,7 +61,7 @@ export const [EditorContextProvider, useEditorContext] = createContextProvider(
}) => {
const editorInstanceContext = useEditorInstanceContext();
const [project, setProject] = createStore<ProjectConfiguration>(
props.editorInstance.savedProjectConfig
props.editorInstance.savedProjectConfig,
);

createEffect(
Expand All @@ -71,8 +72,8 @@ export const [EditorContextProvider, useEditorContext] = createContextProvider(
debounce(() => {
commands.setProjectConfig(project);
}),
{ defer: true }
)
{ defer: true },
),
);

const [dialog, setDialog] = createSignal<DialogState>({
Expand Down Expand Up @@ -105,7 +106,7 @@ export const [EditorContextProvider, useEditorContext] = createContextProvider(
? (exportState.progress.renderedCount /
exportState.progress.totalFrames) *
100
: undefined
: undefined,
);

createEffect(
Expand All @@ -114,16 +115,16 @@ export const [EditorContextProvider, useEditorContext] = createContextProvider(
(active) => {
if (!active)
commands.setPlayheadPosition(
Math.floor(editorState.playbackTime * FPS)
Math.floor(editorState.playbackTime * FPS),
);
}
)
},
),
);

const totalDuration = () =>
project.timeline?.segments.reduce(
(acc, s) => acc + (s.end - s.start) / s.timescale,
0
0,
) ?? props.editorInstance.recordingDuration;

type State = {
Expand Down Expand Up @@ -169,7 +170,7 @@ export const [EditorContextProvider, useEditorContext] = createContextProvider(
position: editorState.timeline.transform.position,
},
z,
origin
origin,
);

const transform = editorState.timeline.transform;
Expand All @@ -190,8 +191,8 @@ export const [EditorContextProvider, useEditorContext] = createContextProvider(
Math.max(p, 0),
Math.max(zoomOutLimit(), totalDuration()) +
4 -
editorState.timeline.transform.zoom
)
editorState.timeline.transform.zoom,
),
);
},
},
Expand Down Expand Up @@ -219,7 +220,7 @@ export const [EditorContextProvider, useEditorContext] = createContextProvider(
};
},
// biome-ignore lint/style/noNonNullAssertion: it's ok
null!
null!,
);

export type FrameData = { width: number; height: number; data: ImageData };
Expand Down Expand Up @@ -276,7 +277,7 @@ export const [EditorInstanceContextProvider, useEditorInstanceContext] =

const [_ws, isConnected] = createImageDataWS(
instance.framesSocketUrl,
setLatestFrame
setLatestFrame,
);

createEffect(() => {
Expand Down Expand Up @@ -380,7 +381,7 @@ export const [TimelineContextProvider, useTimelineContext] =
timelineBounds: props.timelineBounds,
};
},
null!
null!,
);

export const [TrackContextProvider, useTrackContext] = createContextProvider(
Expand All @@ -402,7 +403,7 @@ export const [TrackContextProvider, useTrackContext] = createContextProvider(
setTrackState,
};
},
null!
null!,
);

export const [SegmentContextProvider, useSegmentContext] =
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/src/utils/tauri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ async setPlayheadPosition(frameNumber: number) : Promise<null> {
async setProjectConfig(config: ProjectConfiguration) : Promise<null> {
return await TAURI_INVOKE("set_project_config", { config });
},
async renameProject(name: string) : Promise<null> {
return await TAURI_INVOKE("rename_project", { name });
},
async openPermissionSettings(permission: OSPermission) : Promise<void> {
await TAURI_INVOKE("open_permission_settings", { permission });
},
Expand Down
Loading