diff --git a/src-tauri/src/db.rs b/src-tauri/src/db.rs index 1016bf0..2f9f3f0 100644 --- a/src-tauri/src/db.rs +++ b/src-tauri/src/db.rs @@ -553,6 +553,7 @@ pub fn load_settings(connection: &Connection) -> Result, String> .map_err(|error| format!("failed to load settings: {error}")) } +#[allow(dead_code)] pub fn is_keychain_access_granted(connection: &Connection) -> Result { let Some(settings) = load_settings(connection)? else { return Ok(false); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4f8dcd1..abc516d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -3435,7 +3435,12 @@ fn hydrate_extension_pairing_token( app_state: tauri::State<'_, AppState>, ) -> Result { let mut connection = database.lock()?; - let skip_keychain = !crate::db::is_keychain_access_granted(&connection).unwrap_or(false); + // Always skip the keychain during automatic hydration so the operating + // system never presents a credential-access prompt before the app's own + // modal is visible. The frontend will display the KeychainPermissionModal + // and only call grant_keychain_access — which touches the keychain — after + // the user explicitly clicks "Grant Access". + let skip_keychain = true; match crate::db::hydrate_pairing_token(&mut connection, skip_keychain) { Ok((token, token_changed)) => { if let Ok(mut pairing_token) = app_state.extension_pairing_token.write() { @@ -4210,7 +4215,7 @@ mod tests { } } -static LOG_PAUSED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false); +static LOG_PAUSED: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true); #[tauri::command] fn toggle_log_pause(pause: bool) { diff --git a/src/components/DownloadTable.tsx b/src/components/DownloadTable.tsx index 29b0860..d2cc107 100644 --- a/src/components/DownloadTable.tsx +++ b/src/components/DownloadTable.tsx @@ -182,6 +182,12 @@ export const DownloadTable: React.FC = ({ filter }) => { default: return d.category === filter; } }); + + // Sort by queue position when viewing a specific queue so the visual + // order matches the queue order and move-up/down buttons reflect reality. + const sortedDownloads = filter.startsWith('queue:') + ? [...filteredDownloads].sort((left, right) => (left.queuePosition ?? 0) - (right.queuePosition ?? 0)) + : filteredDownloads; const handleItemClick = (e: React.MouseEvent, item: DownloadItem) => { if (e.metaKey || e.ctrlKey) { const newSelected = new Set(selectedIds); @@ -193,8 +199,8 @@ export const DownloadTable: React.FC = ({ filter }) => { setSelectedIds(newSelected); setLastSelectedId(item.id); } else if (e.shiftKey && lastSelectedId) { - const currentIndex = filteredDownloads.findIndex(d => d.id === item.id); - const lastIndex = filteredDownloads.findIndex(d => d.id === lastSelectedId); + const currentIndex = sortedDownloads.findIndex(d => d.id === item.id); + const lastIndex = sortedDownloads.findIndex(d => d.id === lastSelectedId); if (currentIndex !== -1 && lastIndex !== -1) { const start = Math.min(currentIndex, lastIndex); @@ -202,7 +208,7 @@ export const DownloadTable: React.FC = ({ filter }) => { const newSelected = new Set(selectedIds); for (let i = start; i <= end; i++) { - newSelected.add(filteredDownloads[i].id); + newSelected.add(sortedDownloads[i].id); } setSelectedIds(newSelected); } @@ -290,9 +296,9 @@ export const DownloadTable: React.FC = ({ filter }) => {