Skip to content
Open
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
5 changes: 3 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ jobs:
run: pnpm run build

- name: Build release packages
env:
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
# No signing certificate yet: mac.identity "-" in package.json forces
# ad-hoc signing so Apple Silicon builds launch. Leaving auto-discovery
# at its default avoids short-circuiting that explicit ad-hoc request.
run: ${{ matrix.build_command }}
shell: bash

Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,17 @@ Stay on top of the conversation with live ASR:
Power Interview desktop client is supported on:

- Windows 10/11 (x64 installer build)
- macOS (Apple Silicon and Intel release artifacts)
- macOS 14.4+ (Apple Silicon and Intel release artifacts) - 14.4 is the floor for system-audio loopback capture

Release binaries are published on the [GitHub Releases](https://github.com/PowerInterviewAI/client-app/releases) page.

### Unsigned builds

Release artifacts are not yet code-signed, so the OS will warn on first launch:

- **macOS**: the app is ad-hoc signed (so it runs on Apple Silicon) but not notarized. If you see "app is damaged" or it is blocked, run `xattr -cr "/Applications/Power Interview AI.app"`, or right-click the app and choose Open.
- **Windows**: SmartScreen shows "Windows protected your PC". Click "More info" then "Run anyway".

## Architecture

Power Interview follows a **client-server architecture**.
Expand Down
2 changes: 1 addition & 1 deletion SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ electron-updater publishes to GitHub Releases under `PowerInterviewAI/client` (c
## Platform Support

- Windows 10/11 x64 (NSIS installer)
- macOS Apple Silicon and Intel (DMG + ZIP)
- macOS 14.4+ Apple Silicon and Intel (DMG + ZIP) - 14.4 is required for system-audio loopback capture

## Project Structure

Expand Down
Binary file added build/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 20 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,27 @@
},
"mac": {
"target": [
"dmg",
"zip"
{
"target": "dmg",
"arch": [
"arm64",
"x64"
]
},
{
"target": "zip",
"arch": [
"arm64",
"x64"
]
}
],
"icon": "build/icon.png",
"category": "public.app-category.productivity",
"minimumSystemVersion": "14.4.0",
"identity": "-",
"hardenedRuntime": false,
"notarize": false,
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.inherit.plist",
"extendInfo": {
Expand Down
9 changes: 8 additions & 1 deletion src/main/ipc/permissions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ipcMain, shell, systemPreferences } from 'electron';
import { app, ipcMain, shell, systemPreferences } from 'electron';

export function registerPermissionHandlers(): void {
ipcMain.handle('permissions:check-all', () => {
Expand All @@ -25,4 +25,11 @@ export function registerPermissionHandlers(): void {
};
if (urls[pane]) await shell.openExternal(urls[pane]).catch(() => {});
});

// macOS only applies a freshly-granted Screen Recording permission after the
// app is relaunched, so the first capture otherwise fails silently.
ipcMain.handle('permissions:relaunch', () => {
app.relaunch();
app.exit(0);
});
}
1 change: 1 addition & 0 deletions src/main/preload.cts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ const electronApi = {
requestMicrophone: () => ipcRenderer.invoke('permissions:request-microphone'),
openSettings: (pane: 'microphone' | 'screen') =>
ipcRenderer.invoke('permissions:open-settings', pane),
relaunch: () => ipcRenderer.invoke('permissions:relaunch'),
},

openExternal: (url: string) => ipcRenderer.invoke('external:open', url),
Expand Down
17 changes: 13 additions & 4 deletions src/renderer/components/custom/permission-gate-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export default function PermissionGateDialog({
getElectron()?.permissions.openSettings(pane);
};

const relaunch = () => {
getElectron()?.permissions.relaunch();
};

const handleProceed = () => {
onOpenChange(false);
onProceed();
Expand Down Expand Up @@ -92,13 +96,18 @@ export default function PermissionGateDialog({
? 'Access granted'
: status.screen === 'not-determined'
? 'Will be requested when recording starts'
: 'Enable in System Settings, then click Check Again'
: 'Enable in System Settings, then restart the app to apply'
}
action={
status.screen === 'denied' || status.screen === 'restricted' ? (
<Button size="sm" variant="outline" onClick={() => openSettings('screen')}>
Open Settings
</Button>
<div className="flex flex-col gap-1.5">
<Button size="sm" variant="outline" onClick={() => openSettings('screen')}>
Open Settings
</Button>
<Button size="sm" variant="ghost" onClick={relaunch}>
Restart App
</Button>
</div>
) : null
}
/>
Expand Down
1 change: 1 addition & 0 deletions src/renderer/types/electron-api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ declare global {
}>;
requestMicrophone: () => Promise<boolean>;
openSettings: (pane: 'microphone' | 'screen') => Promise<void>;
relaunch: () => Promise<void>;
};

// Open external URL in user's default browser
Expand Down