From eb56cd4e0b42ecefc2224270e7deebed7879c3aa Mon Sep 17 00:00:00 2001 From: Matthew Maxwell Date: Thu, 19 Mar 2026 22:49:10 -0500 Subject: [PATCH 1/3] Add MacOS hide/show shortcuts --- src-tauri/src/menu.rs | 51 ++++++++++++++++++++++++++++++++++-- src/lib/api/app.ts | 4 +++ src/lib/i18n/locales/de.json | 3 +++ src/lib/i18n/locales/en.json | 3 +++ src/lib/i18n/locales/es.json | 3 +++ src/lib/i18n/locales/fr.json | 3 +++ src/lib/i18n/locales/it.json | 3 +++ src/lib/i18n/locales/ja.json | 3 +++ src/lib/i18n/locales/ko.json | 3 +++ src/lib/i18n/locales/nl.json | 3 +++ src/lib/i18n/locales/pt.json | 3 +++ src/lib/i18n/locales/sv.json | 3 +++ src/lib/i18n/locales/zh.json | 3 +++ src/lib/stores/settings.ts | 4 +++ 14 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/menu.rs b/src-tauri/src/menu.rs index 152401f..5ab45d7 100644 --- a/src-tauri/src/menu.rs +++ b/src-tauri/src/menu.rs @@ -60,6 +60,13 @@ pub struct MenuTranslations { pub about: String, pub settings: String, pub quit: String, + // App menu items (macOS only: Hide, Hide Others, Show All) + #[serde(default)] + pub hide: String, + #[serde(default)] + pub hide_others: String, + #[serde(default)] + pub show_all: String, // File menu items pub import_tracks: String, pub add_release: String, @@ -202,7 +209,7 @@ pub fn build_menu(app: &AppHandle) -> Result, tauri::Error> { fn build_app_menu(app: &AppHandle) -> Result, tauri::Error> { let app_name = get_app_name(); - SubmenuBuilder::with_id(app, ids::APP_MENU, &app_name) + let mut builder = SubmenuBuilder::with_id(app, ids::APP_MENU, &app_name) .item(&MenuItem::with_id( app, ids::ABOUT, @@ -218,7 +225,21 @@ fn build_app_menu(app: &AppHandle) -> Result, tauri::Error> { true, Some("CmdOrCtrl+,"), )?) - .separator() + .separator(); + + #[cfg(target_os = "macos")] + { + builder = builder + .item(&PredefinedMenuItem::hide( + app, + Some(&format!("Hide {app_name}")), + )?) + .item(&PredefinedMenuItem::hide_others(app, Some("Hide Others"))?) + .item(&PredefinedMenuItem::show_all(app, Some("Show All"))?) + .separator(); + } + + builder .item(&MenuItem::with_id( app, ids::QUIT, @@ -708,6 +729,32 @@ pub fn update_menu_translations( update_item_text(&menu, ids::SETTINGS, &translations.settings)?; update_item_text(&menu, ids::QUIT, &translations.quit)?; + // Update App menu PredefinedMenuItems (Hide, Hide Others, Show All) — macOS only + #[cfg(target_os = "macos")] + if let Some(MenuItemKind::Submenu(app_submenu)) = menu.get(ids::APP_MENU) { + let app_texts: [&str; 3] = [ + &translations.hide, + &translations.hide_others, + &translations.show_all, + ]; + let predefined: Vec<_> = app_submenu + .items()? + .into_iter() + .filter_map(|item| match item { + MenuItemKind::Predefined(p) => { + if p.text().unwrap_or_default().is_empty() { + return None; + } + Some(p) + } + _ => None, + }) + .collect(); + for (item, text) in predefined.iter().zip(app_texts.iter()) { + item.set_text(*text)?; + } + } + // Update File menu items update_item_text(&menu, ids::IMPORT_TRACKS, &translations.import_tracks)?; update_item_text(&menu, ids::ADD_RELEASE, &translations.add_release)?; diff --git a/src/lib/api/app.ts b/src/lib/api/app.ts index 8d38f32..66a8a2f 100644 --- a/src/lib/api/app.ts +++ b/src/lib/api/app.ts @@ -63,6 +63,10 @@ export interface MenuTranslations { about: string settings: string quit: string + // App menu items (macOS only: Hide, Hide Others, Show All) + hide: string + hideOthers: string + showAll: string // File menu items importTracks: string addRelease: string diff --git a/src/lib/i18n/locales/de.json b/src/lib/i18n/locales/de.json index 5fdc96d..8c85aa9 100644 --- a/src/lib/i18n/locales/de.json +++ b/src/lib/i18n/locales/de.json @@ -600,6 +600,9 @@ "settingsSound": "Ton", "settingsDiagnostics": "Diagnose", "quit": "{appName} beenden", + "hide": "{appName} ausblenden", + "hideOthers": "Andere ausblenden", + "showAll": "Alle anzeigen", "importTracks": "Tracks importieren...", "addRelease": "Release hinzufügen...", "refreshMetadata": "Metadaten aktualisieren", diff --git a/src/lib/i18n/locales/en.json b/src/lib/i18n/locales/en.json index 59f159a..16c3203 100644 --- a/src/lib/i18n/locales/en.json +++ b/src/lib/i18n/locales/en.json @@ -601,6 +601,9 @@ "settingsSound": "Sound", "settingsDiagnostics": "Diagnostics", "quit": "Quit {appName}", + "hide": "Hide {appName}", + "hideOthers": "Hide Others", + "showAll": "Show All", "importTracks": "Import Tracks...", "addRelease": "Add Release...", "refreshMetadata": "Refresh Metadata", diff --git a/src/lib/i18n/locales/es.json b/src/lib/i18n/locales/es.json index 9f3d4b2..f41f666 100644 --- a/src/lib/i18n/locales/es.json +++ b/src/lib/i18n/locales/es.json @@ -600,6 +600,9 @@ "settingsSound": "Sonido", "settingsDiagnostics": "Diagnósticos", "quit": "Salir de {appName}", + "hide": "Ocultar {appName}", + "hideOthers": "Ocultar otros", + "showAll": "Mostrar todo", "importTracks": "Importar pistas...", "addRelease": "Añadir lanzamiento...", "refreshMetadata": "Actualizar metadatos", diff --git a/src/lib/i18n/locales/fr.json b/src/lib/i18n/locales/fr.json index 3f20452..ddbcb62 100644 --- a/src/lib/i18n/locales/fr.json +++ b/src/lib/i18n/locales/fr.json @@ -600,6 +600,9 @@ "settingsSound": "Son", "settingsDiagnostics": "Diagnostics", "quit": "Quitter {appName}", + "hide": "Masquer {appName}", + "hideOthers": "Masquer les autres", + "showAll": "Afficher tout", "importTracks": "Importer des pistes...", "addRelease": "Ajouter une sortie...", "refreshMetadata": "Actualiser les métadonnées", diff --git a/src/lib/i18n/locales/it.json b/src/lib/i18n/locales/it.json index eea81e0..8cc7926 100644 --- a/src/lib/i18n/locales/it.json +++ b/src/lib/i18n/locales/it.json @@ -600,6 +600,9 @@ "settingsSound": "Suono", "settingsDiagnostics": "Diagnostica", "quit": "Esci da {appName}", + "hide": "Nascondi {appName}", + "hideOthers": "Nascondi altri", + "showAll": "Mostra tutto", "importTracks": "Importa tracce...", "addRelease": "Aggiungi release...", "refreshMetadata": "Aggiorna metadati", diff --git a/src/lib/i18n/locales/ja.json b/src/lib/i18n/locales/ja.json index 16c34d6..da76d1d 100644 --- a/src/lib/i18n/locales/ja.json +++ b/src/lib/i18n/locales/ja.json @@ -601,6 +601,9 @@ "settingsSound": "サウンド", "settingsDiagnostics": "診断", "quit": "{appName}を終了", + "hide": "{appName}を隠す", + "hideOthers": "その他を隠す", + "showAll": "すべて表示", "importTracks": "トラックをインポート...", "addRelease": "リリースを追加...", "refreshMetadata": "メタデータを更新", diff --git a/src/lib/i18n/locales/ko.json b/src/lib/i18n/locales/ko.json index e4dc37a..60fa8ad 100644 --- a/src/lib/i18n/locales/ko.json +++ b/src/lib/i18n/locales/ko.json @@ -600,6 +600,9 @@ "settingsSound": "사운드", "settingsDiagnostics": "진단", "quit": "{appName} 종료", + "hide": "{appName} 숨기기", + "hideOthers": "기타 숨기기", + "showAll": "모두 표시", "importTracks": "트랙 가져오기...", "addRelease": "릴리스 추가...", "refreshMetadata": "메타데이터 새로 고침", diff --git a/src/lib/i18n/locales/nl.json b/src/lib/i18n/locales/nl.json index eefdedd..a6e7867 100644 --- a/src/lib/i18n/locales/nl.json +++ b/src/lib/i18n/locales/nl.json @@ -600,6 +600,9 @@ "settingsSound": "Geluid", "settingsDiagnostics": "Diagnostiek", "quit": "{appName} afsluiten", + "hide": "{appName} verbergen", + "hideOthers": "Anderen verbergen", + "showAll": "Alles weergeven", "importTracks": "Tracks importeren...", "addRelease": "Release toevoegen...", "refreshMetadata": "Metagegevens vernieuwen", diff --git a/src/lib/i18n/locales/pt.json b/src/lib/i18n/locales/pt.json index 99cbd67..a037017 100644 --- a/src/lib/i18n/locales/pt.json +++ b/src/lib/i18n/locales/pt.json @@ -600,6 +600,9 @@ "settingsSound": "Som", "settingsDiagnostics": "Diagnósticos", "quit": "Sair de {appName}", + "hide": "Ocultar {appName}", + "hideOthers": "Ocultar outros", + "showAll": "Mostrar tudo", "importTracks": "Importar faixas...", "addRelease": "Adicionar lançamento...", "refreshMetadata": "Atualizar metadados", diff --git a/src/lib/i18n/locales/sv.json b/src/lib/i18n/locales/sv.json index 9a56ae1..9c0fe30 100644 --- a/src/lib/i18n/locales/sv.json +++ b/src/lib/i18n/locales/sv.json @@ -600,6 +600,9 @@ "settingsSound": "Ljud", "settingsDiagnostics": "Diagnostik", "quit": "Avsluta {appName}", + "hide": "Dölj {appName}", + "hideOthers": "Dölj andra", + "showAll": "Visa alla", "importTracks": "Importera spår...", "addRelease": "Lägg till release...", "refreshMetadata": "Uppdatera metadata", diff --git a/src/lib/i18n/locales/zh.json b/src/lib/i18n/locales/zh.json index d854d11..4305c2c 100644 --- a/src/lib/i18n/locales/zh.json +++ b/src/lib/i18n/locales/zh.json @@ -600,6 +600,9 @@ "settingsSound": "声音", "settingsDiagnostics": "诊断", "quit": "退出 {appName}", + "hide": "隐藏 {appName}", + "hideOthers": "隐藏其他", + "showAll": "显示全部", "importTracks": "导入曲目...", "addRelease": "添加发行...", "refreshMetadata": "刷新元数据", diff --git a/src/lib/stores/settings.ts b/src/lib/stores/settings.ts index 1ca316b..347b2bf 100644 --- a/src/lib/stores/settings.ts +++ b/src/lib/stores/settings.ts @@ -183,6 +183,10 @@ function createSettingsStore() { about: t('menu.about', { values: { appName } }), settings: t('menu.settings'), quit: t('menu.quit', { values: { appName } }), + // App menu items (macOS only: Hide, Hide Others, Show All) + hide: t('menu.hide', { values: { appName } }), + hideOthers: t('menu.hideOthers'), + showAll: t('menu.showAll'), // File menu items importTracks: t('menu.importTracks'), addRelease: t('menu.addRelease'), From 8af2fc3b7dfbdb6e6fb9166a70bed7b82b38b2dc Mon Sep 17 00:00:00 2001 From: Matthew Maxwell Date: Thu, 19 Mar 2026 23:06:37 -0500 Subject: [PATCH 2/3] Format modal code --- src/lib/components/common/Modal.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/components/common/Modal.svelte b/src/lib/components/common/Modal.svelte index 98da38b..1fb9ec4 100644 --- a/src/lib/components/common/Modal.svelte +++ b/src/lib/components/common/Modal.svelte @@ -101,7 +101,8 @@ > {#if visible}
From b4f490366a20b902e8ab9e348dc06ada5a3af8bd Mon Sep 17 00:00:00 2001 From: Matthew Maxwell Date: Fri, 20 Mar 2026 09:46:54 -0500 Subject: [PATCH 3/3] Fix settings modal rendering --- src/lib/components/common/Modal.svelte | 14 +- .../components/settings/SettingsModal.svelte | 216 +++++------------- 2 files changed, 63 insertions(+), 167 deletions(-) diff --git a/src/lib/components/common/Modal.svelte b/src/lib/components/common/Modal.svelte index 1fb9ec4..be5854d 100644 --- a/src/lib/components/common/Modal.svelte +++ b/src/lib/components/common/Modal.svelte @@ -6,19 +6,21 @@ type Props = { open: boolean title?: string - size?: 'sm' | 'md' | 'lg' + size?: 'sm' | 'md' | 'lg' | 'xl' + flush?: boolean onClose: () => void onSubmit?: () => void children: Snippet footer?: Snippet } - let { open, title, size = 'sm', onClose, onSubmit, children, footer }: Props = $props() + let { open, title, size = 'sm', flush = false, onClose, onSubmit, children, footer }: Props = $props() const sizeClasses: Record = { sm: 'max-w-sm', md: 'max-w-md', lg: 'max-w-xl', + xl: 'max-w-2xl', } let dialogEl: HTMLDialogElement | undefined = $state() @@ -94,15 +96,15 @@ {#if visible}
@@ -112,7 +114,7 @@
{/if} -
+
{@render children()}
diff --git a/src/lib/components/settings/SettingsModal.svelte b/src/lib/components/settings/SettingsModal.svelte index c315048..b368c5c 100644 --- a/src/lib/components/settings/SettingsModal.svelte +++ b/src/lib/components/settings/SettingsModal.svelte @@ -3,8 +3,8 @@ import { settingsStore } from '$lib/stores/settings' import { diagnosticsStore } from '$lib/stores/diagnostics' import { Button, Text } from '$lib/components/common' + import Modal from '$lib/components/common/Modal.svelte' import Icon from '$lib/components/common/Icon.svelte' - import { scale } from 'svelte/transition' import { translate } from '$lib/i18n' import { GeneralTab, LibraryTab, DiscoveryTab, AppearanceTab, SoundTab, DiagnosticsTab, AboutTab } from './tabs' @@ -16,11 +16,18 @@ let { open, initialTab, onClose }: Props = $props() - let dialogEl: HTMLDialogElement | undefined = $state() let contentEl: HTMLDivElement | undefined = $state() let activePage: SettingsPage = $state('general') - let visible = $state(false) - let mousedownTarget: EventTarget | null = $state(null) + + const tabs: { page: SettingsPage; icon: string; fill?: boolean }[] = [ + { page: 'general', icon: 'sliders-horizontal' }, + { page: 'appearance', icon: 'palette' }, + { page: 'discovery', icon: 'globe' }, + { page: 'library', icon: 'library' }, + { page: 'sound', icon: 'volume-full', fill: true }, + { page: 'diagnostics', icon: 'terminal' }, + { page: 'about', icon: 'info' }, + ] // Set active page when opening (use initialTab if provided, otherwise default to 'general') $effect(() => { @@ -29,21 +36,6 @@ } }) - // Open dialog when open becomes true - $effect(() => { - if (!dialogEl) return - if (open) { - visible = true - dialogEl.showModal() - } - }) - - // Handle transition end to close dialog - function handleOutroEnd() { - dialogEl?.close() - visible = false - } - // Refresh audio devices when opening sound settings $effect(() => { if (open && activePage === 'sound') { @@ -64,149 +56,51 @@ activePage contentEl?.scrollTo(0, 0) }) - - function handleKeydown(e: KeyboardEvent) { - e.stopPropagation() - if (e.key === 'Escape') { - e.preventDefault() - onClose() - } - } - - function handleBackdropMousedown(e: MouseEvent) { - mousedownTarget = e.target - } - - function handleBackdropClick(e: MouseEvent) { - if (e.target === dialogEl && mousedownTarget === dialogEl) { - onClose() - } - } - - {#if visible} -
-
- -
- {$translate('settings.title')} - -
- - -
- {#if activePage === 'general'} - - {:else if activePage === 'appearance'} - - {:else if activePage === 'discovery'} - - {:else if activePage === 'library'} - - {:else if activePage === 'sound'} - - {:else if activePage === 'diagnostics'} - - {:else if activePage === 'about'} - - {/if} -
-
+ +
+ +
+ {$translate('settings.title')} + +
- -
- -
+ +
+ {#if activePage === 'general'} + + {:else if activePage === 'appearance'} + + {:else if activePage === 'discovery'} + + {:else if activePage === 'library'} + + {:else if activePage === 'sound'} + + {:else if activePage === 'diagnostics'} + + {:else if activePage === 'about'} + + {/if}
- {/if} -
+
+ + {#snippet footer()} + + {/snippet} +