[READ-ONLY] Mirror of https://github.com/probablykasper/kadium. App for staying ontop of YouTube channels' uploads kadium.kasper.space
linux macos notifications tauri windows youtube
0

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'tauri-v2'

Kasper (Aug 31, 2025, 12:41 AM +0200) 9506a7f0 7855ef73

+103 -39
+5
CHANGELOG.md
··· 1 1 # Changelog 2 2 3 + ## Next 4 + - Use Shift instead of Alt for Videos/Channels page shortcuts 5 + - Fix keyboard input working when modal is open 6 + - Remove old YouTube Email Notifier migration 7 + 3 8 ## 1.7.1 - 2024 Jan 28 4 9 - Fix selection being created on videos page refresh 5 10
+11
package-lock.json
··· 14 14 "@tauri-apps/api": "^2.8.0", 15 15 "@tauri-apps/cli": "^2.8.3", 16 16 "@tauri-apps/plugin-opener": "^2.5.0", 17 + "@tauri-apps/plugin-os": "^2.3.1", 17 18 "@typescript-eslint/eslint-plugin": "^6.18.1", 18 19 "@typescript-eslint/parser": "^6.18.1", 19 20 "autoprefixer": "^10.4.16", ··· 1325 1326 "version": "2.5.0", 1326 1327 "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-opener/-/plugin-opener-2.5.0.tgz", 1327 1328 "integrity": "sha512-B0LShOYae4CZjN8leiNDbnfjSrTwoZakqKaWpfoH6nXiJwt6Rgj6RnVIffG3DoJiKsffRhMkjmBV9VeilSb4TA==", 1329 + "dev": true, 1330 + "license": "MIT OR Apache-2.0", 1331 + "dependencies": { 1332 + "@tauri-apps/api": "^2.8.0" 1333 + } 1334 + }, 1335 + "node_modules/@tauri-apps/plugin-os": { 1336 + "version": "2.3.1", 1337 + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-os/-/plugin-os-2.3.1.tgz", 1338 + "integrity": "sha512-ty5V8XDUIFbSnrk3zsFoP3kzN+vAufYzalJSlmrVhQTImIZa1aL1a03bOaP2vuBvfR+WDRC6NgV2xBl8G07d+w==", 1328 1339 "dev": true, 1329 1340 "license": "MIT OR Apache-2.0", 1330 1341 "dependencies": {
+1
package.json
··· 20 20 "@tauri-apps/api": "^2.8.0", 21 21 "@tauri-apps/cli": "^2.8.3", 22 22 "@tauri-apps/plugin-opener": "^2.5.0", 23 + "@tauri-apps/plugin-os": "^2.3.1", 23 24 "@typescript-eslint/eslint-plugin": "^6.18.1", 24 25 "@typescript-eslint/parser": "^6.18.1", 25 26 "autoprefixer": "^10.4.16",
+50
src-tauri/Cargo.lock
··· 1503 1503 ] 1504 1504 1505 1505 [[package]] 1506 + name = "gethostname" 1507 + version = "1.0.2" 1508 + source = "registry+https://github.com/rust-lang/crates.io-index" 1509 + checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" 1510 + dependencies = [ 1511 + "rustix 1.0.8", 1512 + "windows-targets 0.52.6", 1513 + ] 1514 + 1515 + [[package]] 1506 1516 name = "getopts" 1507 1517 version = "0.2.24" 1508 1518 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2319 2329 "tauri-plugin-dialog", 2320 2330 "tauri-plugin-notification", 2321 2331 "tauri-plugin-opener", 2332 + "tauri-plugin-os", 2322 2333 "tauri-specta", 2323 2334 "tokio", 2324 2335 "url", ··· 3132 3143 dependencies = [ 3133 3144 "futures-core", 3134 3145 "pin-project-lite", 3146 + ] 3147 + 3148 + [[package]] 3149 + name = "os_info" 3150 + version = "3.12.0" 3151 + source = "registry+https://github.com/rust-lang/crates.io-index" 3152 + checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" 3153 + dependencies = [ 3154 + "log", 3155 + "plist", 3156 + "serde", 3157 + "windows-sys 0.52.0", 3135 3158 ] 3136 3159 3137 3160 [[package]] ··· 4859 4882 ] 4860 4883 4861 4884 [[package]] 4885 + name = "sys-locale" 4886 + version = "0.3.2" 4887 + source = "registry+https://github.com/rust-lang/crates.io-index" 4888 + checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" 4889 + dependencies = [ 4890 + "libc", 4891 + ] 4892 + 4893 + [[package]] 4862 4894 name = "system-configuration" 4863 4895 version = "0.5.1" 4864 4896 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5161 5193 "url", 5162 5194 "windows", 5163 5195 "zbus", 5196 + ] 5197 + 5198 + [[package]] 5199 + name = "tauri-plugin-os" 5200 + version = "2.3.1" 5201 + source = "registry+https://github.com/rust-lang/crates.io-index" 5202 + checksum = "77a1c77ebf6f20417ab2a74e8c310820ba52151406d0c80fbcea7df232e3f6ba" 5203 + dependencies = [ 5204 + "gethostname", 5205 + "log", 5206 + "os_info", 5207 + "serde", 5208 + "serde_json", 5209 + "serialize-to-javascript", 5210 + "sys-locale", 5211 + "tauri", 5212 + "tauri-plugin", 5213 + "thiserror 2.0.16", 5164 5214 ] 5165 5215 5166 5216 [[package]]
+1
src-tauri/Cargo.toml
··· 33 33 tauri-plugin-opener = "2" 34 34 tauri-plugin-dialog = "2" 35 35 dirs = "6.0.0" 36 + tauri-plugin-os = "2" 36 37 37 38 [target.'cfg(target_os = "macos")'.dependencies] 38 39 macos-app-nap = "0.0"
+15 -12
src-tauri/capabilities/main.json
··· 1 1 { 2 - "$schema": "../gen/schemas/desktop-schema.json", 3 - "identifier": "main", 4 - "description": "main window permissions", 5 - "local": true, 6 - "windows": ["main"], 7 - "permissions": [ 8 - "core:default", 9 - "opener:allow-open-url", 10 - "opener:allow-default-urls", 11 - "notification:default" 12 - ] 13 - } 2 + "$schema": "../gen/schemas/desktop-schema.json", 3 + "identifier": "main", 4 + "description": "main window permissions", 5 + "local": true, 6 + "windows": [ 7 + "main" 8 + ], 9 + "permissions": [ 10 + "core:default", 11 + "opener:allow-open-url", 12 + "opener:allow-default-urls", 13 + "notification:default", 14 + "os:default" 15 + ] 16 + }
+5 -17
src-tauri/src/lib.rs
··· 105 105 }; 106 106 107 107 let app = tauri::Builder::default() 108 + .plugin(tauri_plugin_os::init()) 108 109 .plugin(tauri_plugin_opener::init()) 109 110 .plugin(tauri_plugin_notification::init()) 110 111 .invoke_handler(specta_builder.invoke_handler()) ··· 112 113 let win = WebviewWindowBuilder::new(app, "main", WebviewUrl::default()) 113 114 .title("Kadium") 114 115 .inner_size(900.0, 800.0) 115 - .min_inner_size(400.0, 150.0); 116 + .min_inner_size(400.0, 150.0) 117 + .theme(Some(tauri::Theme::Dark)); 116 118 117 119 #[cfg(target_os = "macos")] 118 120 let win = win.title_bar_style(tauri::TitleBarStyle::Transparent); ··· 121 123 122 124 #[cfg(target_os = "macos")] 123 125 { 126 + // set window background color because Tauri's .background_color() doesn't work 124 127 use cocoa::appkit::NSWindow; 125 128 let nsw = win.ns_window().unwrap() as cocoa::base::id; 126 129 unsafe { 127 - // set window to always be dark mode 128 - use cocoa::appkit::NSAppearanceNameVibrantDark; 129 - use objc::*; 130 - let appearance: cocoa::base::id = msg_send![ 131 - class!(NSAppearance), 132 - appearanceNamed: NSAppearanceNameVibrantDark 133 - ]; 134 - let () = msg_send![nsw, setAppearance: appearance]; 135 - 136 - // set window background color 137 130 let bg_color = cocoa::appkit::NSColor::colorWithRed_green_blue_alpha_( 138 131 cocoa::base::nil, 139 132 34.0 / 255.0, ··· 165 158 tauri::WindowEvent::CloseRequested { api: _api, .. } => { 166 159 #[cfg(target_os = "macos")] 167 160 { 168 - // hide the application 169 - // manual for now (PR https://github.com/tauri-apps/tauri/pull/3689) 170 161 _api.prevent_close(); 171 - use objc::*; 172 - let cls = objc::runtime::Class::get("NSApplication").unwrap(); 173 - let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] }; 174 - unsafe { msg_send![app, hide: 0] } 162 + _app_handle.hide().unwrap(); 175 163 } 176 164 } 177 165 _ => {}
+2 -1
src/routes/+page.svelte
··· 8 8 import VideoBar from './_VideoBar.svelte' 9 9 import commands from '$lib/commands' 10 10 import { menu_actions } from './menu' 11 + import { modalCount } from 'modal-svelte' 11 12 12 13 let videos: Video[] = [] 13 14 let allLoaded = false ··· 186 187 187 188 function keydown(e: KeyboardEvent) { 188 189 let target = e.target as HTMLElement 189 - if (target.nodeName === 'INPUT') { 190 + if (target.nodeName === 'INPUT' || $modalCount > 0) { 190 191 return 191 192 } 192 193
+13 -9
src/routes/menu.ts
··· 3 3 import { Menu, Submenu, type SubmenuOptions } from '@tauri-apps/api/menu' 4 4 import { openUrl } from '@tauri-apps/plugin-opener' 5 5 import { show_get_started } from './+layout.svelte' 6 + import { platform } from '@tauri-apps/plugin-os' 6 7 7 8 export const menu_actions: Partial< 8 9 Record<'Find' | 'Open' | 'Open Channel' | 'Archive' | 'Unarchive', () => void> 9 10 > = {} 11 + 12 + function array_if<const T>(condition: boolean, value: T): T[] | [] { 13 + return condition ? [value] : [] 14 + } 10 15 11 16 export async function create_menu() { 12 17 const app_menu: SubmenuOptions = { ··· 64 69 accelerator: 'Shift+CmdOrCtrl+Backspace', 65 70 action: () => menu_actions.Unarchive?.(), 66 71 }, 67 - { item: 'Separator' }, 68 - { 72 + ...array_if(platform() !== 'macos', { item: 'Separator' }), 73 + ...array_if(platform() !== 'macos', { 69 74 text: 'Options...', 70 - id: 'Preferences...', 71 - accelerator: 'cmdOrControl+,', 75 + accelerator: 'cmdOrCtrl+,', 72 76 action() { 73 77 settingsOpen.set(true) 74 78 }, 75 - }, 79 + }), 76 80 { item: 'Separator' }, 77 81 { item: 'CloseWindow' }, 78 82 ], 79 83 } 84 + 80 85 const edit_menu: SubmenuOptions = { 81 86 text: 'Edit', 82 87 items: [ ··· 86 91 { item: 'Cut' }, 87 92 { item: 'Copy' }, 88 93 { item: 'Paste' }, 89 - // #[cfg(not(target_os = "macos"))] 90 - { item: 'Separator' }, 94 + ...array_if(platform() !== 'macos', { item: 'Separator' }), 91 95 { item: 'SelectAll' }, 92 96 { item: 'Separator' }, 93 97 { ··· 159 163 { item: 'Separator' }, 160 164 { 161 165 text: 'Videos', 162 - accelerator: 'Alt+CmdOrCtrl+1', 166 + accelerator: 'Shift+CmdOrCtrl+1', 163 167 action() { 164 168 goto('/', { replaceState: true }) 165 169 }, 166 170 }, 167 171 { 168 172 text: 'Channels', 169 - accelerator: 'Alt+CmdOrCtrl+2', 173 + accelerator: 'Shift+CmdOrCtrl+2', 170 174 action() { 171 175 goto('/channels', { replaceState: true }) 172 176 },