Skip to content

Commit 2da01f7

Browse files
authored
update new flow with mode tips (#970)
1 parent 42a1d47 commit 2da01f7

File tree

3 files changed

+117
-77
lines changed

3 files changed

+117
-77
lines changed

apps/desktop/src/components/ModeSelect.tsx

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { cx } from "cva";
2-
import type { JSX } from "solid-js";
2+
import { type JSX, Show } from "solid-js";
33
import { createOptionsQuery } from "~/utils/queries";
44
import type { RecordingMode } from "~/utils/tauri";
55

66
interface ModeOptionProps {
77
mode: RecordingMode;
88
title: string;
99
description: string;
10-
icon: (props: { class: string; style: JSX.CSSProperties }) => JSX.Element;
10+
standalone?: boolean;
11+
icon: (props: { class: string; style?: JSX.CSSProperties }) => JSX.Element;
1112
isSelected: boolean;
1213
onSelect: (mode: RecordingMode) => void;
1314
}
@@ -17,26 +18,12 @@ const ModeOption = (props: ModeOptionProps) => {
1718
<div
1819
onClick={() => props.onSelect(props.mode)}
1920
class={cx(
20-
`p-4 space-y-3 rounded-lg bg-gray-2 transition-all duration-200`,
21-
{
22-
"ring-2 ring-offset-2 hover:bg-gray-2 cursor-default ring-blue-9 ring-offset-gray-100":
23-
props.isSelected,
24-
"ring-2 ring-transparent ring-offset-transparent hover:bg-gray-3 cursor-pointer":
25-
!props.isSelected,
26-
},
21+
`p-5 space-y-3 rounded-lg border transition-all duration-200 border-gray-4 dark:border-gray-3 h-fit bg-gray-3 dark:bg-gray-2`,
2722
)}
2823
>
2924
<div class="flex flex-col items-center mb-2 text-center">
3025
{props.icon({
31-
class: cx(
32-
"size-12 mb-3",
33-
props.isSelected ? "opacity-100" : "opacity-30",
34-
),
35-
style: {
36-
filter: props.isSelected
37-
? "drop-shadow(0 0 0.5rem rgba(255, 255, 255, 0.5))"
38-
: "none",
39-
},
26+
class: "size-12 mb-5 invert dark:invert-0",
4027
})}
4128
<h3 class="text-lg font-medium text-gray-12">{props.title}</h3>
4229
</div>
@@ -48,7 +35,7 @@ const ModeOption = (props: ModeOptionProps) => {
4835
);
4936
};
5037

51-
const ModeSelect = () => {
38+
const ModeSelect = (props: { onClose?: () => void; standalone?: boolean }) => {
5239
const { rawOptions, setOptions } = createOptionsQuery();
5340

5441
const handleModeChange = (mode: RecordingMode) => {
@@ -73,7 +60,28 @@ const ModeSelect = () => {
7360
];
7461

7562
return (
76-
<div class="grid grid-cols-2 gap-8 text-center">
63+
<div
64+
class={cx(
65+
"grid grid-cols-2 gap-8 items-center text-center bg-gray-1",
66+
props.standalone
67+
? "absolute z-10 border border-gray-3 p-16 rounded-xl"
68+
: "",
69+
)}
70+
onClick={(e) => e.stopPropagation()}
71+
>
72+
<Show when={props.onClose}>
73+
<div
74+
onClick={() => props.onClose?.()}
75+
class="absolute -top-2.5 -right-2.5 p-2 rounded-full border duration-200 bg-gray-2 border-gray-3 hover:bg-gray-3 transition-duration"
76+
>
77+
<IconCapX class="invert-1 size-2 dark:invert" />
78+
</div>
79+
</Show>
80+
{props.standalone && (
81+
<h2 class="text-[24px] col-span-2 font-medium text-center text-gray-12">
82+
Recording Modes
83+
</h2>
84+
)}
7785
{modeOptions.map((option) => (
7886
<ModeOption
7987
mode={option.mode}

apps/desktop/src/routes/mode-select.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const ModeSelectWindow = () => {
2323
>
2424
<div class="relative z-10 space-y-10 w-full max-w-3xl">
2525
<h2 class="text-[24px] font-medium text-center text-gray-12">
26-
Select your recording mode
26+
Recording Modes
2727
</h2>
2828
<ModeSelect />
2929
</div>

apps/desktop/src/routes/target-select-overlay.tsx

Lines changed: 88 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
Switch,
2222
} from "solid-js";
2323
import { createStore, reconcile } from "solid-js/store";
24+
import ModeSelect from "~/components/ModeSelect";
2425
import { authStore, generalSettingsStore } from "~/store";
2526
import { createOptionsQuery } from "~/utils/queries";
2627
import {
@@ -31,9 +32,14 @@ import {
3132
type TargetUnderCursor,
3233
} from "~/utils/tauri";
3334

35+
const capitalize = (str: string) => {
36+
return str.charAt(0).toUpperCase() + str.slice(1);
37+
};
38+
3439
export default function () {
3540
const [params] = useSearchParams<{ displayId: DisplayId }>();
3641
const { rawOptions, setOptions } = createOptionsQuery();
42+
const [toggleModeSelect, setToggleModeSelect] = createSignal(false);
3743

3844
const [targetUnderCursor, setTargetUnderCursor] =
3945
createStore<TargetUnderCursor>({
@@ -125,15 +131,16 @@ export default function () {
125131
class="relative w-screen h-screen flex flex-col items-center justify-center data-[over='true']:bg-blue-600/40 transition-colors"
126132
>
127133
<div class="absolute inset-0 bg-black/50 -z-10" />
134+
128135
<Show when={displayInformation.data} keyed>
129136
{(display) => (
130137
<>
131-
<span class="text-3xl font-semibold mb-2 text-white">
138+
<span class="mb-2 text-3xl font-semibold text-white">
132139
{display.name || "Monitor"}
133140
</span>
134141
<Show when={display.physical_size}>
135142
{(size) => (
136-
<span class="text-xs mb-2 text-white">
143+
<span class="mb-2 text-xs text-white">
137144
{`${size().width}x${size().height} · ${display.refresh_rate}FPS`}
138145
</span>
139146
)}
@@ -142,7 +149,20 @@ export default function () {
142149
)}
143150
</Show>
144151

152+
<Show when={toggleModeSelect()}>
153+
{/* Transparent overlay to capture outside clicks */}
154+
<div
155+
class="absolute inset-0 z-10"
156+
onClick={() => setToggleModeSelect(false)}
157+
/>
158+
<ModeSelect
159+
standalone
160+
onClose={() => setToggleModeSelect(false)}
161+
/>
162+
</Show>
163+
145164
<RecordingControls
165+
setToggleModeSelect={setToggleModeSelect}
146166
target={{ variant: "display", id: params.displayId! }}
147167
/>
148168
</div>
@@ -191,14 +211,15 @@ export default function () {
191211
</span>
192212
</div>
193213
<RecordingControls
214+
setToggleModeSelect={setToggleModeSelect}
194215
target={{
195216
variant: "window",
196217
id: windowUnderCursor.id,
197218
}}
198219
/>
199220

200221
<Button
201-
variant="primary"
222+
variant="dark"
202223
size="sm"
203224
onClick={() => {
204225
setBounds(windowUnderCursor.bounds);
@@ -657,16 +678,15 @@ export default function () {
657678
);
658679
}
659680

660-
function RecordingControls(props: { target: ScreenCaptureTarget }) {
681+
function RecordingControls(props: {
682+
target: ScreenCaptureTarget;
683+
setToggleModeSelect?: (value: boolean) => void;
684+
}) {
661685
const auth = authStore.createQuery();
662686
const { rawOptions, setOptions } = createOptionsQuery();
663687

664688
const generalSetings = generalSettingsStore.createQuery();
665689

666-
const capitalize = (str: string) => {
667-
return str.charAt(0).toUpperCase() + str.slice(1);
668-
};
669-
670690
const menuModes = async () =>
671691
await Menu.new({
672692
items: [
@@ -721,66 +741,78 @@ function RecordingControls(props: { target: ScreenCaptureTarget }) {
721741
};
722742

723743
return (
724-
<div class="flex gap-2.5 items-center p-2.5 my-2.5 rounded-xl border min-w-fit w-fit bg-gray-2 border-gray-4">
725-
<div
726-
onClick={() => setOptions("targetMode", null)}
727-
class="flex justify-center items-center bg-white rounded-full transition-opacity size-9 hover:opacity-80"
728-
>
729-
<IconCapX class="will-change-transform size-3" />
730-
</div>
731-
<div
732-
data-inactive={rawOptions.mode === "instant" && !auth.data}
733-
class="flex flex-row rounded-full bg-blue-9 overflow-hidden group h-11"
734-
onClick={() => {
735-
if (rawOptions.mode === "instant" && !auth.data) {
736-
emit("start-sign-in");
737-
return;
738-
}
739-
740-
commands.startRecording({
741-
capture_target: props.target,
742-
mode: rawOptions.mode,
743-
capture_system_audio: rawOptions.captureSystemAudio,
744-
});
745-
}}
746-
>
747-
<div class="flex items-center pl-4 py-1 hover:bg-blue-10 transition-colors">
748-
{rawOptions.mode === "studio" ? (
749-
<IconCapFilmCut class="size-4" />
750-
) : (
751-
<IconCapInstant class="size-4" />
752-
)}
753-
<div class="flex flex-col ml-3 mr-2">
754-
<span class="text-sm text-white font-medium text-nowrap">
755-
{rawOptions.mode === "instant" && !auth.data
756-
? "Sign In To Use"
757-
: "Start Recording"}
758-
</span>
759-
<span class="text-xs text-nowrap text-white font-light -mt-0.5 opacity-90">
760-
{`${capitalize(rawOptions.mode)} Mode`}
761-
</span>
744+
<>
745+
<div class="flex gap-2.5 items-center p-2.5 my-2.5 rounded-xl border min-w-fit w-fit bg-gray-2 border-gray-4">
746+
<div
747+
onClick={() => setOptions("targetMode", null)}
748+
class="flex justify-center items-center rounded-full transition-opacity bg-gray-12 size-9 hover:opacity-80"
749+
>
750+
<IconCapX class="invert will-change-transform size-3 dark:invert-0" />
751+
</div>
752+
<div
753+
data-inactive={rawOptions.mode === "instant" && !auth.data}
754+
class="flex overflow-hidden flex-row h-11 rounded-full bg-blue-9 group"
755+
onClick={() => {
756+
if (rawOptions.mode === "instant" && !auth.data) {
757+
emit("start-sign-in");
758+
return;
759+
}
760+
761+
commands.startRecording({
762+
capture_target: props.target,
763+
mode: rawOptions.mode,
764+
capture_system_audio: rawOptions.captureSystemAudio,
765+
});
766+
}}
767+
>
768+
<div class="flex items-center py-1 pl-4 transition-colors hover:bg-blue-10">
769+
{rawOptions.mode === "studio" ? (
770+
<IconCapFilmCut class="size-4" />
771+
) : (
772+
<IconCapInstant class="size-4" />
773+
)}
774+
<div class="flex flex-col mr-2 ml-3">
775+
<span class="text-sm font-medium text-white text-nowrap">
776+
{rawOptions.mode === "instant" && !auth.data
777+
? "Sign In To Use"
778+
: "Start Recording"}
779+
</span>
780+
<span class="text-xs flex items-center text-nowrap gap-1 transition-opacity duration-200 text-white font-light -mt-0.5 opacity-90">
781+
{`${capitalize(rawOptions.mode)} Mode`}
782+
</span>
783+
</div>
784+
</div>
785+
<div
786+
class="pl-2.5 group-hover:bg-blue-10 transition-colors pr-3 py-1.5 flex items-center"
787+
onClick={(e) => {
788+
e.stopPropagation();
789+
menuModes().then((menu) => menu.popup());
790+
}}
791+
>
792+
<IconCapCaretDown class="focus:rotate-90" />
762793
</div>
763794
</div>
764795
<div
765-
class="pl-2.5 group-hover:bg-blue-10 transition-colors pr-3 py-1.5 flex items-center"
766796
onClick={(e) => {
767797
e.stopPropagation();
768-
menuModes().then((menu) => menu.popup());
798+
preRecordingMenu().then((menu) => menu.popup());
769799
}}
800+
class="flex justify-center items-center rounded-full border transition-opacity bg-gray-6 text-gray-12 size-9 hover:opacity-80"
770801
>
771-
<IconCapCaretDown class="focus:rotate-90" />
802+
<IconCapGear class="will-change-transform size-5" />
772803
</div>
773804
</div>
774805
<div
775-
onClick={(e) => {
776-
e.stopPropagation();
777-
preRecordingMenu().then((menu) => menu.popup());
778-
}}
779-
class="flex justify-center items-center rounded-full border transition-opacity bg-gray-6 text-gray-12 size-9 hover:opacity-80"
806+
onClick={() => props.setToggleModeSelect?.(true)}
807+
class="flex gap-1 items-center mb-5 transition-opacity duration-200 hover:opacity-60"
780808
>
781-
<IconCapGear class="will-change-transform size-5" />
809+
<IconCapInfo class="opacity-70 will-change-transform size-3" />
810+
<p class="text-sm text-white">
811+
<span class="opacity-70">What is </span>
812+
<span class="font-medium">{capitalize(rawOptions.mode)} Mode</span>?
813+
</p>
782814
</div>
783-
</div>
815+
</>
784816
);
785817
}
786818

0 commit comments

Comments
 (0)