Skip to content

Commit e488c9a

Browse files
authored
Single instance, fix titlebars, better Windows support (#169)
* adjust editor titlebar height, and radius incosistency in prev-recordings items * Open in Explorer, skip taskbar for prev-recordings * Add single-instance plugin, try to not open terminal when rendering in release mode * Don't show startup permissions screen on Windows * Adjust traffic lights position on Editor * Actually stop the terminal opening when rendering on Windows * Hide macOS specific setting on Windows * Add installer banners * Fix nsis and wix banners * Cleanup for PR * Adjust nsis banners and add installer icon.
1 parent 8649b7b commit e488c9a

File tree

15 files changed

+200
-157
lines changed

15 files changed

+200
-157
lines changed

Cargo.lock

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/desktop/src-tauri/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ tauri-plugin-updater = "2.0.0"
3434
tauri-plugin-notification = "2.0.0-rc"
3535
tauri-plugin-oauth = { git = "https://github.com/FabianLars/tauri-plugin-oauth", branch = "v2" }
3636
tauri-plugin-global-shortcut = "2.0.0"
37+
tauri-plugin-single-instance = "2.0.1"
3738
tauri-specta = { version = "=2.0.0-rc.20", features = ["derive", "typescript"] }
3839

3940
serde = { version = "1", features = ["derive"] }
123 KB
Binary file not shown.
151 KB
Binary file not shown.
112 KB
Binary file not shown.

apps/desktop/src-tauri/src/lib.rs

+4-12
Original file line numberDiff line numberDiff line change
@@ -949,7 +949,7 @@ async fn copy_screenshot_to_clipboard(app: AppHandle, path: PathBuf) -> Result<(
949949

950950
#[tauri::command]
951951
#[specta::specta]
952-
async fn open_file_path(app: AppHandle, path: PathBuf) -> Result<(), String> {
952+
async fn open_file_path(_app: AppHandle, path: PathBuf) -> Result<(), String> {
953953
let path_str = path.to_str().ok_or("Invalid path")?;
954954

955955
#[cfg(target_os = "windows")]
@@ -1693,16 +1693,6 @@ async fn set_project_config(app: AppHandle, video_id: String, config: ProjectCon
16931693
editor_instance.project_config.0.send(config).ok();
16941694
}
16951695

1696-
#[tauri::command(async)]
1697-
#[specta::specta]
1698-
fn open_in_finder(path: PathBuf) {
1699-
Command::new("open")
1700-
.arg("-R")
1701-
.arg(path)
1702-
.spawn()
1703-
.expect("Failed to open in Finder");
1704-
}
1705-
17061696
#[tauri::command]
17071697
#[specta::specta]
17081698
async fn list_audio_devices() -> Result<Vec<String>, ()> {
@@ -2441,7 +2431,6 @@ pub async fn run() {
24412431
start_playback,
24422432
stop_playback,
24432433
set_playhead_position,
2444-
open_in_finder,
24452434
set_project_config,
24462435
open_editor,
24472436
open_main_window,
@@ -2519,6 +2508,9 @@ pub async fn run() {
25192508
}
25202509

25212510
builder
2511+
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
2512+
let _ = CapWindow::Main.show(&app);
2513+
}))
25222514
.plugin(tauri_plugin_shell::init())
25232515
.plugin(tauri_plugin_dialog::init())
25242516
.plugin(tauri_plugin_store::Builder::new().build())

apps/desktop/src-tauri/src/windows.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl CapWindowId {
9292
pub fn traffic_lights_position(&self) -> Option<Option<LogicalPosition<f64>>> {
9393
match self {
9494
Self::Camera | Self::WindowCaptureOccluder | Self::PrevRecordings => None,
95-
Self::Editor { .. } => Some(Some(LogicalPosition::new(20.0, 48.0))),
95+
Self::Editor { .. } => Some(Some(LogicalPosition::new(20.0, 40.5))),
9696
Self::InProgressRecording => Some(Some(LogicalPosition::new(-100.0, -100.0))),
9797
_ => Some(None),
9898
}
@@ -263,6 +263,7 @@ impl CapWindow {
263263
)
264264
.visible(false)
265265
.theme(Some(tauri::Theme::Dark))
266+
.skip_taskbar(true)
266267
.build()?
267268
}
268269
Self::PrevRecordings => {

apps/desktop/src-tauri/tauri.conf.json

+10
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@
5353
"y": 140
5454
}
5555
}
56+
},
57+
"windows": {
58+
"nsis": {
59+
"headerImage": "assets/nsis-header.bmp",
60+
"sidebarImage": "assets/nsis-sidebar.bmp",
61+
"installerIcon": "icons/icon.ico"
62+
},
63+
"wix": {
64+
"bannerPath": "assets/wix-banner.bmp"
65+
}
5666
}
5767
}
5868
}

apps/desktop/src/components/titlebar/controls/CaptionControlsWindows11.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { cx } from "cva";
77
export default function (props: ComponentProps<"div">) {
88
const [local, otherProps] = splitProps(props, ["class"]);
99
const window = getCurrentWindow();
10-
10+
1111
return (
1212
<div
1313
class={`h-full align-baseline select-none *:outline-none *:transition-all *:duration-200 ${local.class}`}
@@ -21,7 +21,7 @@ export default function (props: ComponentProps<"div">) {
2121
? "text-black/90 dark:text-white"
2222
: "text-gray-50 dark:text-white",
2323
titlebarState.minimizable
24-
? "hover:bg-[#0000000D] active:bg-[#00000008] dark:hover:bg-[#FFFFFF1A] dark:active:bg-[#FFFFFF0D]"
24+
? "hover:bg-[#0000000D] active:bg-[#00000008]"
2525
: "[&>*]:opacity-30"
2626
)}
2727
>
@@ -42,7 +42,7 @@ export default function (props: ComponentProps<"div">) {
4242
? "text-black/90 dark:text-white"
4343
: "text-gray-50 dark:text-white",
4444
titlebarState.maximizable
45-
? "hover:bg-[#0000000D] active:bg-[#00000008] dark:hover:bg-[#FFFFFF1A] dark:active:bg-[#FFFFFF0D]"
45+
? "hover:bg-[#0000000D] active:bg-[#00000008]"
4646
: "[&>*]:opacity-30"
4747
)}
4848
>

apps/desktop/src/routes/(window-chrome)/settings/general.tsx

+85-80
Original file line numberDiff line numberDiff line change
@@ -6,51 +6,54 @@ import {
66
isPermissionGranted,
77
requestPermission,
88
} from "@tauri-apps/plugin-notification";
9+
import { OsType, Platform, type } from "@tauri-apps/plugin-os";
910

10-
const settingsList = [
11-
{
12-
key: "uploadIndividualFiles",
13-
label: "Upload individual recording files when creating shareable link",
14-
description:
15-
'Warning: this will cause shareable link uploads to become significantly slower, since all individual recording files will be uploaded. Shows "Download Assets" button in Share page.',
16-
},
17-
{
18-
key: "openEditorAfterRecording",
19-
label: "Open editor automatically after recording stops",
20-
description:
21-
"The editor will be shown immediately after you finish recording.",
22-
},
23-
{
24-
key: "hideDockIcon",
25-
label: "Hide dock icon",
26-
description:
27-
"The dock icon will be hidden when there are no windows available to close.",
28-
},
29-
{
30-
key: "autoCreateShareableLink",
31-
label: "Cap Pro: Automatically create shareable link after recording",
32-
description:
33-
"When enabled, a shareable link will be created automatically after stopping the recording. You'll be redirected to the URL while the upload continues in the background.",
34-
},
35-
{
36-
key: "disableAutoOpenLinks",
37-
label: "Cap Pro: Disable automatic link opening",
38-
description:
39-
"When enabled, Cap will not automatically open links in your browser (e.g. after creating a shareable link).",
40-
},
41-
{
42-
key: "enableNotifications",
43-
label: "Enable System Notifications",
44-
description:
45-
"Show system notifications for events like copying to clipboard, saving files, and more. You may need to manually allow Cap access via your system's notification settings.",
46-
requiresPermission: true,
47-
},
48-
] satisfies Array<{
11+
const settingsList: Array<{
4912
key: keyof GeneralSettingsStore;
5013
label: string;
5114
description: string;
15+
platforms?: OsType[],
5216
requiresPermission?: boolean;
53-
}>;
17+
}> = [
18+
{
19+
key: "uploadIndividualFiles",
20+
label: "Upload individual recording files when creating shareable link",
21+
description:
22+
'Warning: this will cause shareable link uploads to become significantly slower, since all individual recording files will be uploaded. Shows "Download Assets" button in Share page.',
23+
},
24+
{
25+
key: "openEditorAfterRecording",
26+
label: "Open editor automatically after recording stops",
27+
description:
28+
"The editor will be shown immediately after you finish recording.",
29+
},
30+
{
31+
key: "hideDockIcon",
32+
label: "Hide dock icon",
33+
platforms: ["macos"],
34+
description:
35+
"The dock icon will be hidden when there are no windows available to close.",
36+
},
37+
{
38+
key: "autoCreateShareableLink",
39+
label: "Cap Pro: Automatically create shareable link after recording",
40+
description:
41+
"When enabled, a shareable link will be created automatically after stopping the recording. You'll be redirected to the URL while the upload continues in the background.",
42+
},
43+
{
44+
key: "disableAutoOpenLinks",
45+
label: "Cap Pro: Disable automatic link opening",
46+
description:
47+
"When enabled, Cap will not automatically open links in your browser (e.g. after creating a shareable link).",
48+
},
49+
{
50+
key: "enableNotifications",
51+
label: "Enable System Notifications",
52+
description:
53+
"Show system notifications for events like copying to clipboard, saving files, and more. You may need to manually allow Cap access via your system's notification settings.",
54+
requiresPermission: true,
55+
},
56+
];
5457

5558
export default function GeneralSettings() {
5659
const [store] = createResource(() => generalSettingsStore.get());
@@ -101,61 +104,63 @@ function Inner(props: { initialStore: GeneralSettingsStore | null }) {
101104
generalSettingsStore.set({ [key]: value });
102105
};
103106

107+
const ostype: OsType = type();
108+
104109
return (
105110
<div class="flex flex-col w-full h-full">
106111
<div class="flex-1 overflow-y-auto">
107112
<div class="p-4 space-y-2 divide-y divide-gray-200">
108113
<For each={settingsList}>
109114
{(setting) => (
110-
<div class="space-y-2 py-3">
111-
<div class="flex items-center justify-between">
112-
<p>{setting.label}</p>
113-
<button
114-
type="button"
115-
role="switch"
116-
aria-checked={
117-
settings[setting.key as keyof GeneralSettingsStore]
118-
}
119-
data-state={
120-
settings[setting.key as keyof GeneralSettingsStore]
121-
? "checked"
122-
: "unchecked"
123-
}
124-
value={
125-
settings[setting.key as keyof GeneralSettingsStore]
126-
? "on"
127-
: "off"
128-
}
129-
class={`peer inline-flex h-4 w-8 shrink-0 cursor-pointer items-center rounded-full transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 ${
130-
settings[setting.key as keyof GeneralSettingsStore]
131-
? "bg-blue-400 border-blue-400"
132-
: "bg-gray-300 border-gray-300"
133-
}`}
134-
onClick={() =>
135-
handleChange(
136-
setting.key,
137-
!settings[setting.key as keyof GeneralSettingsStore]
138-
)
139-
}
140-
>
141-
<span
115+
<Show when={!setting.platforms || setting.platforms.includes(ostype)}>
116+
<div class="space-y-2 py-3">
117+
<div class="flex items-center justify-between">
118+
<p>{setting.label}</p>
119+
<button
120+
type="button"
121+
role="switch"
122+
aria-checked={
123+
settings[setting.key as keyof GeneralSettingsStore]
124+
}
142125
data-state={
143126
settings[setting.key as keyof GeneralSettingsStore]
144127
? "checked"
145128
: "unchecked"
146129
}
147-
class={`pointer-events-none block h-4 w-4 rounded-full bg-gray-50 shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0 border-2 ${
130+
value={
148131
settings[setting.key as keyof GeneralSettingsStore]
132+
? "on"
133+
: "off"
134+
}
135+
class={`peer inline-flex h-4 w-8 shrink-0 cursor-pointer items-center rounded-full transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 ${settings[setting.key as keyof GeneralSettingsStore]
136+
? "bg-blue-400 border-blue-400"
137+
: "bg-gray-300 border-gray-300"
138+
}`}
139+
onClick={() =>
140+
handleChange(
141+
setting.key,
142+
!settings[setting.key as keyof GeneralSettingsStore]
143+
)
144+
}
145+
>
146+
<span
147+
data-state={
148+
settings[setting.key as keyof GeneralSettingsStore]
149+
? "checked"
150+
: "unchecked"
151+
}
152+
class={`pointer-events-none block h-4 w-4 rounded-full bg-gray-50 shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0 border-2 ${settings[setting.key as keyof GeneralSettingsStore]
149153
? "border-blue-400"
150154
: "border-gray-300"
151-
}`}
152-
/>
153-
</button>
155+
}`}
156+
/>
157+
</button>
158+
</div>
159+
{setting.description && (
160+
<p class="text-xs text-gray-400">{setting.description}</p>
161+
)}
154162
</div>
155-
{setting.description && (
156-
<p class="text-xs text-gray-400">{setting.description}</p>
157-
)}
158-
</div>
163+
</Show>
159164
)}
160165
</For>
161166
</div>

0 commit comments

Comments
 (0)