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
11 changes: 5 additions & 6 deletions macos/LCTMac/Services/AudioCaptureService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,14 @@ class AudioCaptureService: NSObject, ObservableObject, @unchecked Sendable {
}

appLog("[AudioCaptureService] --------- Permission Check Failed ---------")
appLog("[AudioCaptureService] Showing error and opening settings.")


// Pure query: don't set lastError or open System Settings here. The caller
// (TranscriptionViewModel) decides whether this is fatal — if microphone
// capture is enabled it silently falls back instead of surfacing an error.
Task { @MainActor in
self.hasPermission = false
self.lastError = "Screen recording permission denied. Please:\n1. Open System Settings > Privacy & Security > Screen & System Audio Recording\n2. Enable LCTMac\n3. Restart the application"
}

AudioCaptureService.openScreenRecordingSettings()


return false
}

Expand Down
7 changes: 7 additions & 0 deletions macos/LCTMac/ViewModels/TranscriptionVM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ class TranscriptionViewModel: ObservableObject {
if let persistenceError = AppSettings.consumeLastPersistenceError() {
notice = .warning(persistenceError, autoDismiss: false)
}

// Probe Ollama on launch so the status indicator reflects reality
// immediately, instead of showing the default "stopped" until first use.
Task {
await ollamaGuardian.checkStatus()
isOllamaConnected = await ollamaService.checkHealth()
}
}

// MARK: - Setup
Expand Down
51 changes: 51 additions & 0 deletions macos/Scripts/dev-cert.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/bash
# Create a stable local code-signing identity ("LCT Dev") for development.
#
# Why: ad-hoc signatures (codesign --sign -) change on every build, and macOS
# ties TCC permissions (screen recording, microphone, speech) to the signing
# identity. So each rebuild looks like a brand-new app and forces you to
# re-grant permissions. A stable self-signed identity fixes that — grant once,
# then permissions persist across rebuilds.
#
# Run once: ./scripts/dev-cert.sh (then rebuild with ./package-app.sh)
# The certificate is local-only, needs no Apple account, and never leaves your Mac.
set -euo pipefail

IDENTITY="LCT Dev"

if security find-identity -p codesigning 2>/dev/null | grep -q "$IDENTITY"; then
echo "Identity '$IDENTITY' already exists — nothing to do."
exit 0
fi

WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT

cat > "$WORK/cert.conf" <<'EOF'
[req]
distinguished_name = dn
x509_extensions = ext
prompt = no
[dn]
CN = LCT Dev
[ext]
basicConstraints = critical, CA:false
keyUsage = critical, digitalSignature
extendedKeyUsage = critical, codeSigning
EOF

openssl req -x509 -newkey rsa:2048 -keyout "$WORK/key.pem" -out "$WORK/cert.pem" \
-days 3650 -nodes -config "$WORK/cert.conf" 2>/dev/null

# macOS `security` can't read modern OpenSSL PKCS#12: force legacy algorithms
# and a non-empty password (empty-password p12 import is buggy on macOS).
openssl pkcs12 -export -legacy -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1 \
-inkey "$WORK/key.pem" -in "$WORK/cert.pem" -out "$WORK/cert.p12" \
-name "$IDENTITY" -passout pass:lctdev 2>/dev/null

# -A lets codesign use the private key without a keychain prompt on every build.
security import "$WORK/cert.p12" -k ~/Library/Keychains/login.keychain-db -P lctdev -A

echo "Created code-signing identity '$IDENTITY'."
echo "Rebuild with ./package-app.sh. You'll re-grant permissions ONCE more"
echo "(new identity), after which they persist across all future rebuilds."
15 changes: 13 additions & 2 deletions macos/package-app.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,16 @@ BUILD=$(git rev-list --count HEAD 2>/dev/null || echo "1")
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION" "$APP/Contents/Info.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD" "$APP/Contents/Info.plist"

codesign --force --sign - "$APP"
echo "Packaged $APP $VERSION ($BUILD) — launch with: open $APP"
# Sign with a stable local identity if one exists, so macOS TCC permissions
# (screen recording, microphone, speech) survive rebuilds instead of being
# invalidated every time — ad-hoc signatures change on each build, which forces
# re-authorization. Create the dev identity once with: ./scripts/dev-cert.sh
# Falls back to ad-hoc on CI / machines without the cert.
SIGN_IDENTITY="${LCT_SIGN_IDENTITY:-LCT Dev}"
if security find-identity -p codesigning 2>/dev/null | grep -q "$SIGN_IDENTITY"; then
codesign --force --sign "$SIGN_IDENTITY" "$APP"
echo "Packaged $APP $VERSION ($BUILD), signed as '$SIGN_IDENTITY' — launch with: open $APP"
else
codesign --force --sign - "$APP"
echo "Packaged $APP $VERSION ($BUILD), ad-hoc signed — launch with: open $APP"
fi
Loading