Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 5 additions & 8 deletions src/components/SettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1030,7 +1030,7 @@ export default function SettingsPage({ activeSection = "general" }: SettingsPage
title: t("settingsPage.permissions.resetAccessibility.title"),
description: message,
onConfirm: () => {
permissionsHook.openAccessibilitySettings();
permissionsHook.requestAccessibilityPermission();
},
});
};
Expand Down Expand Up @@ -3324,8 +3324,7 @@ EOF`,
description={t("settingsPage.permissions.microphoneDescription")}
granted={permissionsHook.micPermissionGranted}
onRequest={permissionsHook.requestMicPermission}
buttonText={t("settingsPage.permissions.test")}
onOpenSettings={permissionsHook.openMicPrivacySettings}
buttonText={t("settingsPage.permissions.grantAccess")}
/>

{platform === "darwin" && (
Expand All @@ -3335,9 +3334,8 @@ EOF`,
title={t("settingsPage.permissions.accessibilityTitle")}
description={t("settingsPage.permissions.accessibilityDescription")}
granted={permissionsHook.accessibilityPermissionGranted}
onRequest={permissionsHook.testAccessibilityPermission}
buttonText={t("settingsPage.permissions.testAndGrant")}
onOpenSettings={permissionsHook.openAccessibilitySettings}
onRequest={permissionsHook.requestAccessibilityPermission}
buttonText={t("settingsPage.permissions.grantAccess")}
/>
{systemAudio.mode === "native" && (
<PermissionCard
Expand All @@ -3346,8 +3344,7 @@ EOF`,
description={t("settingsPage.permissions.systemAudioDescription")}
granted={systemAudio.granted}
onRequest={systemAudio.request}
buttonText={t("settingsPage.permissions.test")}
onOpenSettings={systemAudio.openSettings}
buttonText={t("settingsPage.permissions.grantAccess")}
badge={t("settingsPage.permissions.optional")}
/>
)}
Expand Down
37 changes: 4 additions & 33 deletions src/components/ui/PermissionCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button } from "./button";
import { Check, ExternalLink, LucideIcon, Settings } from "lucide-react";
import { Check, LucideIcon } from "lucide-react";
import { cn } from "../lib/utils";

interface PermissionCardProps {
Expand All @@ -9,9 +9,7 @@ interface PermissionCardProps {
granted: boolean;
onRequest: () => void;
buttonText?: string;
onOpenSettings?: () => void;
badge?: string;
openSettingsText?: string;
hint?: string;
}

Expand All @@ -22,9 +20,7 @@ export default function PermissionCard({
granted,
onRequest,
buttonText = "Grant Access",
onOpenSettings,
badge,
openSettingsText,
hint,
}: PermissionCardProps) {
return (
Expand All @@ -38,7 +34,6 @@ export default function PermissionCard({
)}
>
<div className="flex items-center gap-3">
{/* Icon container */}
<div
className={cn(
"w-8 h-8 rounded-md flex items-center justify-center shrink-0 transition-colors duration-150",
Expand All @@ -54,7 +49,6 @@ export default function PermissionCard({
)}
</div>

{/* Content */}
<div className="flex-1 min-w-0">
<h3 className="text-xs font-medium text-foreground">
{title}
Expand All @@ -67,36 +61,13 @@ export default function PermissionCard({
<p className="text-xs text-muted-foreground leading-snug mt-0.5">{description}</p>
</div>

{/* Actions - only when not granted */}
{!granted && (
<div className="flex items-center gap-1.5 shrink-0">
{onOpenSettings && openSettingsText ? (
<Button onClick={onOpenSettings} size="sm" className="h-7 px-3 text-xs">
<ExternalLink className="w-3 h-3" />
{openSettingsText}
</Button>
) : (
<>
<Button onClick={onRequest} size="sm" className="h-7 px-3 text-xs">
{buttonText}
</Button>
{onOpenSettings && (
<Button
onClick={onOpenSettings}
size="sm"
variant="ghost"
className="h-7 w-7 p-0"
>
<Settings className="w-3.5 h-3.5" />
</Button>
)}
</>
)}
</div>
<Button onClick={onRequest} size="sm" className="h-7 px-3 text-xs shrink-0">
{buttonText}
</Button>
)}
</div>

{/* Troubleshooting hint */}
{hint && !granted && (
<p className="text-[11px] text-warning/80 leading-snug mt-2 pl-11">{hint}</p>
)}
Expand Down
12 changes: 4 additions & 8 deletions src/components/ui/PermissionsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ interface PermissionsSectionProps {
granted: boolean;
mode: "native" | "unsupported";
request: () => Promise<boolean>;
openSettings: () => Promise<void>;
};
}

Expand All @@ -29,7 +28,7 @@ export default function PermissionsSection({ permissions, systemAudio }: Permiss
description={t("onboarding.permissions.microphoneDescription")}
granted={permissions.micPermissionGranted}
onRequest={permissions.requestMicPermission}
buttonText={t("onboarding.permissions.grant")}
buttonText={t("onboarding.permissions.grantAccess")}
/>

{isMacOS && (
Expand All @@ -39,10 +38,8 @@ export default function PermissionsSection({ permissions, systemAudio }: Permiss
title={t("onboarding.permissions.accessibilityTitle")}
description={t("onboarding.permissions.accessibilityDescription")}
granted={permissions.accessibilityPermissionGranted}
onRequest={permissions.testAccessibilityPermission}
buttonText={t("onboarding.permissions.testAndGrant")}
onOpenSettings={permissions.openAccessibilitySettings}
openSettingsText={t("onboarding.permissions.openSystemSettings")}
onRequest={permissions.requestAccessibilityPermission}
buttonText={t("onboarding.permissions.grantAccess")}
badge={t("onboarding.permissions.recommended")}
hint={
permissions.accessibilityTroubleshooting
Expand All @@ -57,8 +54,7 @@ export default function PermissionsSection({ permissions, systemAudio }: Permiss
description={t("onboarding.permissions.systemAudioDescription")}
granted={systemAudio.granted}
onRequest={systemAudio.request}
buttonText={t("onboarding.permissions.grant")}
onOpenSettings={systemAudio.openSettings}
buttonText={t("onboarding.permissions.grantAccess")}
badge={t("onboarding.permissions.optional")}
/>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/audioTapManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const ARCH_CPU_TYPE = {
const DEFAULT_SAMPLE_RATE = 24000;
const DEFAULT_CHUNK_MS = 100;
const START_TIMEOUT_MS = 3000;
const REQUEST_TIMEOUT_MS = 15000;
const REQUEST_TIMEOUT_MS = 60000;
const STOP_TIMEOUT_MS = 5000;

function compareVersions(left, right) {
Expand Down
4 changes: 1 addition & 3 deletions src/helpers/clipboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,7 @@ class ClipboardManager {
}

const possiblePaths = [
...(process.resourcesPath
? [path.join(process.resourcesPath, "bin", "nircmd.exe")]
: []),
...(process.resourcesPath ? [path.join(process.resourcesPath, "bin", "nircmd.exe")] : []),
path.join(__dirname, "..", "..", "resources", "bin", "nircmd.exe"),
path.join(process.cwd(), "resources", "bin", "nircmd.exe"),
];
Expand Down
14 changes: 7 additions & 7 deletions src/helpers/hotkeyManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,9 +211,7 @@ class HotkeyManager {
slot.accelerator = null;
this.slots.set(slotName, slot);

debugLogger.log(
`[HotkeyManager] KDE slot "${slotName}" set to "${hotkey}"`
);
debugLogger.log(`[HotkeyManager] KDE slot "${slotName}" set to "${hotkey}"`);
return { success: true, hotkey };
}

Expand Down Expand Up @@ -483,7 +481,11 @@ class HotkeyManager {
}

async initializeKDEShortcuts(callback) {
if (process.platform !== "linux" || !KDEShortcutManager.isWayland() || !KDEShortcutManager.isKDE()) {
if (
process.platform !== "linux" ||
!KDEShortcutManager.isWayland() ||
!KDEShortcutManager.isKDE()
) {
return false;
}

Expand Down Expand Up @@ -606,9 +608,7 @@ class HotkeyManager {
const success = await this.kdeManager.registerKeybinding(hotkey, "dictation", callback);
if (success) {
this.currentHotkey = hotkey;
debugLogger.log(
`[HotkeyManager] KDE hotkey "${hotkey}" registered successfully`
);
debugLogger.log(`[HotkeyManager] KDE hotkey "${hotkey}" registered successfully`);
} else {
debugLogger.log(
"[HotkeyManager] KDE keybinding failed, falling back to globalShortcut"
Expand Down
38 changes: 27 additions & 11 deletions src/helpers/ipcHandlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2066,12 +2066,14 @@ class IPCHandlers {
return { granted: true, status: "granted", mode: "unsupported" };
}

if (this.audioTapManager?.isSupported()) {
const status = systemPreferences.getMediaAccessStatus("screen");
return { granted: status === "granted", status, mode: "native" };
if (!this.audioTapManager?.isSupported()) {
return { granted: false, status: "unsupported", mode: "unsupported" };
}

return { granted: false, status: "unsupported", mode: "unsupported" };
const screenStatus = systemPreferences.getMediaAccessStatus("screen");
const tapStatus = this.audioTapManager.getPermissionStatus();
const granted = screenStatus === "granted" || tapStatus === "granted";
return { granted, status: granted ? "granted" : screenStatus, mode: "native" };
});

ipcMain.handle("request-system-audio-access", async () => {
Expand All @@ -2083,16 +2085,24 @@ class IPCHandlers {
return { granted: false, status: "unsupported", mode: "unsupported" };
}

const status = systemPreferences.getMediaAccessStatus("screen");
if (status === "granted") {
const screenStatus = systemPreferences.getMediaAccessStatus("screen");
if (screenStatus === "granted") {
return { granted: true, status: "granted", mode: "native" };
}

// Permission not yet granted — open System Settings so the user can toggle it manually.
// Unlike microphone, macOS does not show a system prompt for screen/audio recording;
// the user must enable it in System Settings.
// Probe the binary — AudioHardwareCreateProcessTap triggers the native consent dialog
try {
const result = await this.audioTapManager.requestAccess();
if (result.granted) {
return { granted: true, status: "granted", mode: "native" };
}
} catch {
// Falls through to opening System Settings
}

// Fallback for older macOS or if the native prompt was denied
await openSystemSettings("systemAudio");
return { granted: false, status, mode: "native" };
return { granted: false, status: screenStatus, mode: "native" };
});

// Auth: clear all session cookies for sign-out.
Expand Down Expand Up @@ -2487,7 +2497,13 @@ class IPCHandlers {
{ ref: "_meetingSystemStreaming", secret: secrets[1], source: "system" },
];
} else {
pairs = [{ ref: "_meetingMicStreaming", secret: await fetchRealtimeToken(event, options), source: "mic" }];
pairs = [
{
ref: "_meetingMicStreaming",
secret: await fetchRealtimeToken(event, options),
source: "mic",
},
];
}

for (const { ref, source } of pairs) {
Expand Down
Loading
Loading