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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ Thumbs.db
sidecar/dist/
sidecar/build/
sidecar/__pycache__/
sidecar/darwin/PeakFlowHelper/.build/
resources/darwin/peakflow-helper
nul
16 changes: 16 additions & 0 deletions electron-builder.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,27 @@ productName: PeakFlow
directories:
buildResources: resources
output: release
afterSign: scripts/notarize.js
files:
- '!**/.vscode/*'
- '!src/*'
- '!electron.vite.config.*'
- '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
- '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
- '!sidecar/*'
mac:
category: public.app-category.productivity
hardenedRuntime: true
gatekeeperAssess: false
entitlements: resources/entitlements.mac.plist
entitlementsInherit: resources/entitlements.mac.inherit.plist
target:
- dmg
- zip
extendInfo:
NSCameraUsageDescription: PeakFlow needs camera access for meeting prep and focus tools.
NSMicrophoneUsageDescription: PeakFlow needs microphone access for meeting prep and mic mute controls.
NSAppleEventsUsageDescription: PeakFlow needs automation access to send paste shortcuts and control supported workflows.
win:
target:
- target: nsis
Expand All @@ -31,6 +45,8 @@ extraResources:
to: icon.png
- from: resources/tray-icon.png
to: tray-icon.png
- from: resources/darwin/peakflow-helper
to: peakflow-helper
asarUnpack:
- "**/*.node"
- "node_modules/koffi/**/*"
Expand Down
5 changes: 3 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
"build": "electron-vite build",
"preview": "electron-vite preview",
"postinstall": "electron-builder install-app-deps",
"build:mac:helper": "node scripts/prepare-mac-helper.js",
"build:win": "npm run build && electron-builder --win",
"build:mac": "npm run build && npm run build:mac:helper && electron-builder --mac",
"build:mac:dir": "npm run build && npm run build:mac:helper && electron-builder --mac --dir",
"build:unpack": "npm run build && electron-builder --dir",
"typecheck:node": "tsc --noEmit -p tsconfig.node.json",
"typecheck:web": "tsc --noEmit -p tsconfig.web.json",
Expand All @@ -26,6 +29,7 @@
"node-ical": "^0.25.4"
},
"devDependencies": {
"@electron/notarize": "^2.5.0",
"@electron-toolkit/eslint-config-ts": "^3.0.0",
"@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/utils": "^4.0.0",
Expand Down
12 changes: 12 additions & 0 deletions resources/entitlements.mac.inherit.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>
</dict>
</plist>
14 changes: 14 additions & 0 deletions resources/entitlements.mac.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
</dict>
</plist>
Binary file modified resources/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions scripts/notarize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Post-sign notarization script for macOS builds.
*
* Called by electron-builder after code signing (configured via afterSign in
* electron-builder.yml). Submits the signed .app to Apple for notarization,
* which is required for Gatekeeper to allow the app to run.
*
* Requires environment variables:
* APPLE_ID — Apple Developer account email
* APPLE_APP_SPECIFIC_PASSWORD — App-specific password (not account password)
* APPLE_TEAM_ID — 10-character team identifier
*/

const { notarize } = require('@electron/notarize')

exports.default = async function notarizing(context) {
const { electronPlatformName, appOutDir } = context

// Only notarize macOS builds
if (electronPlatformName !== 'darwin') return

// Skip if signing credentials aren't available (local dev builds)
if (!process.env.APPLE_ID || !process.env.APPLE_APP_SPECIFIC_PASSWORD || !process.env.APPLE_TEAM_ID) {
console.log('[Notarize] Skipping — APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD, or APPLE_TEAM_ID not set')
return
}

const appName = context.packager.appInfo.productFilename
const appPath = `${appOutDir}/${appName}.app`

console.log(`[Notarize] Submitting ${appPath} to Apple...`)

await notarize({
appBundleId: 'pro.getpeakflow.core',
appPath,
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD,
teamId: process.env.APPLE_TEAM_ID
})

console.log('[Notarize] Done')
}
54 changes: 54 additions & 0 deletions scripts/prepare-mac-helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const fs = require('fs')
const path = require('path')
const { spawnSync } = require('child_process')

const projectRoot = path.resolve(__dirname, '..')
const helperRoot = path.join(projectRoot, 'sidecar', 'darwin', 'PeakFlowHelper')
const outputPath = path.join(projectRoot, 'resources', 'darwin', 'peakflow-helper')

function ensureMac() {
if (process.platform !== 'darwin') {
console.log('[prepare-mac-helper] Skipping helper build on non-macOS host')
return false
}

return true
}

function runSwiftBuild() {
const result = spawnSync('swift', ['build', '-c', 'release'], {
cwd: helperRoot,
stdio: 'inherit'
})

if (result.status !== 0) {
process.exit(result.status ?? 1)
}
}

function findBuiltHelper() {
const candidates = [
path.join(helperRoot, '.build', 'apple', 'Products', 'Release', 'PeakFlowHelper'),
path.join(helperRoot, '.build', 'release', 'PeakFlowHelper')
]

for (const candidate of candidates) {
if (fs.existsSync(candidate)) {
return candidate
}
}

throw new Error('PeakFlowHelper binary not found after swift build')
}

function copyHelper(sourcePath) {
fs.mkdirSync(path.dirname(outputPath), { recursive: true })
fs.copyFileSync(sourcePath, outputPath)
fs.chmodSync(outputPath, 0o755)
console.log(`[prepare-mac-helper] Copied helper to ${outputPath}`)
}

if (ensureMac()) {
runSwiftBuild()
copyHelper(findBuiltHelper())
}
18 changes: 18 additions & 0 deletions sidecar/darwin/PeakFlowHelper/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// swift-tools-version: 5.9
import PackageDescription

let package = Package(
name: "PeakFlowHelper",
platforms: [.macOS(.v13)],
targets: [
.executableTarget(
name: "PeakFlowHelper",
path: "Sources",
linkerSettings: [
.linkedFramework("AppKit"),
.linkedFramework("CoreAudio"),
.linkedFramework("ApplicationServices")
]
)
]
)
Loading