Skip to content

chore: add swift helper #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 24, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 3 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Standard hoisting configuration
node-linker=hoisted
shamefully-hoist=true
40 changes: 40 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"configurations": [
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:amical}/packages/native-helpers/swift-helper",
"name": "Debug KeyTapHelper (packages/native-helpers/swift-helper)",
"program": "${workspaceFolder:amical}/packages/native-helpers/swift-helper/.build/debug/KeyTapHelper",
"preLaunchTask": "swift: Build Debug KeyTapHelper (packages/native-helpers/swift-helper)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:amical}/packages/native-helpers/swift-helper",
"name": "Release KeyTapHelper (packages/native-helpers/swift-helper)",
"program": "${workspaceFolder:amical}/packages/native-helpers/swift-helper/.build/release/KeyTapHelper",
"preLaunchTask": "swift: Build Release KeyTapHelper (packages/native-helpers/swift-helper)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:amical}/packages/native-helpers/swift-helper",
"name": "Debug SwiftHelper (packages/native-helpers/swift-helper)",
"program": "${workspaceFolder:amical}/packages/native-helpers/swift-helper/.build/debug/SwiftHelper",
"preLaunchTask": "swift: Build Debug SwiftHelper (packages/native-helpers/swift-helper)"
},
{
"type": "swift",
"request": "launch",
"args": [],
"cwd": "${workspaceFolder:amical}/packages/native-helpers/swift-helper",
"name": "Release SwiftHelper (packages/native-helpers/swift-helper)",
"program": "${workspaceFolder:amical}/packages/native-helpers/swift-helper/.build/release/SwiftHelper",
"preLaunchTask": "swift: Build Release SwiftHelper (packages/native-helpers/swift-helper)"
}
]
}
22 changes: 0 additions & 22 deletions apps/electron/.vscode/launch.json

This file was deleted.

3 changes: 0 additions & 3 deletions apps/electron/.vscode/settings.json

This file was deleted.

2 changes: 1 addition & 1 deletion apps/electron/forge.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { FuseV1Options, FuseVersion } from '@electron/fuses';
const config: ForgeConfig = {
packagerConfig: {
asar: true,
extraResource: ['src/helper/bin'],
extraResource: ['../../packages/native-helpers/swift-helper/bin'],
extendInfo: {
NSMicrophoneUsageDescription: "This app needs access to your microphone to record audio for transcription.",
},
Expand Down
12 changes: 7 additions & 5 deletions apps/electron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
"description": "My Electron application description",
"main": ".vite/build/main.js",
"scripts": {
"start": "electron-forge start",
"package": "electron-forge package",
"make": "electron-forge make",
"start": "pnpm build:swift-helper && electron-forge start",
"package": "pnpm build:swift-helper && electron-forge package",
"make": "pnpm build:swift-helper && electron-forge make",
"publish": "electron-forge publish",
"lint": "eslint --ext .ts,.tsx .",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
Expand All @@ -16,7 +16,9 @@
"schema:generate-json": "tsx scripts/generate-json-schema.ts",
"schema:generate-swift": "tsx scripts/generate-swift-models.js",
"schema:generate-all": "pnpm schema:generate-json && pnpm schema:generate-swift",
"build:swift-helper": "pnpm schema:generate-all && swift build --package-path src/helper/swift/KeyTapHelper --configuration release && mkdir -p src/helper/bin && cp src/helper/swift/KeyTapHelper/.build/release/KeyTapHelper src/helper/bin/"
"build:swift-helper": "pnpm schema:generate-all && pnpm --filter @amical/swift-helper build",
"dev": "pnpm start",
"prepare": "pnpm build:swift-helper"
},
"keywords": [],
"license": "MIT",
Expand Down Expand Up @@ -47,7 +49,7 @@
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.4.0",
"prettier": "^3.5.3",
"quicktype": "^23.1.4",
"quicktype": "23.1.4",
"tailwindcss": "^4.1.6",
"tsx": "^4.19.4",
"typescript": "~5.8.3",
Expand Down
4 changes: 2 additions & 2 deletions apps/electron/scripts/generate-swift-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');

const generatedDir = 'src/helper/swift/KeyTapHelper/Sources/KeyTapHelper/models/generated';
const generatedDir = '../../packages/native-helpers/swift-helper/Sources/SwiftHelper/models/generated';

try {
// Remove existing generated models and create the directory
Expand All @@ -16,7 +16,7 @@ try {
// Generate Swift models from JSON schemas using quicktype
const commands = [
'quicktype --src-lang schema --lang swift ' +
'-o src/helper/swift/KeyTapHelper/Sources/KeyTapHelper/models/generated/models.swift ' +
'-o ../../packages/native-helpers/swift-helper/Sources/SwiftHelper/models/generated/models.swift ' +
'generated/schemas/helper-envelopes/rpc-request.schema.json ' +
'generated/schemas/helper-envelopes/rpc-response.schema.json ' +
'generated/schemas/helper-requests/get-accessibility-tree-details-params.schema.json ' +
Expand Down
16 changes: 8 additions & 8 deletions apps/electron/src/main/swift-io-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,18 @@ export class SwiftIOBridge extends EventEmitter {
}

private determineHelperPath(): string {
const helperName = 'KeyTapHelper'; // Or your Swift executable name
const helperName = 'SwiftHelper'; // Swift native helper executable
return electronApp.isPackaged
? path.join(process.resourcesPath, 'bin', helperName)
: path.join(electronApp.getAppPath(), 'src', 'helper', 'bin', helperName);
: path.join(electronApp.getAppPath(), '..', '..', 'packages', 'native-helpers', 'swift-helper', 'bin', helperName);
}

private startHelperProcess(): void {
try {
fs.accessSync(this.helperPath, fs.constants.X_OK);
} catch (err) {
console.error(
`SwiftIOBridge: KeyTapHelper executable not found or not executable at ${this.helperPath}.`
`SwiftIOBridge: SwiftHelper executable not found or not executable at ${this.helperPath}.`
);
this.emit(
'error',
Expand All @@ -94,7 +94,7 @@ export class SwiftIOBridge extends EventEmitter {
return;
}

console.log(`SwiftIOBridge: Spawning KeyTapHelper from: ${this.helperPath}`);
console.log(`SwiftIOBridge: Spawning SwiftHelper from: ${this.helperPath}`);
this.proc = spawn(this.helperPath, [], { stdio: ['pipe', 'pipe', 'pipe'] });

this.proc.stdout.pipe(split2()).on('data', (line: string) => {
Expand Down Expand Up @@ -132,19 +132,19 @@ export class SwiftIOBridge extends EventEmitter {

this.proc.stderr.on('data', (data: Buffer) => {
const errorMsg = data.toString();
console.error(`SwiftIOBridge: KeyTapHelper stderr: ${errorMsg}`);
console.error(`SwiftIOBridge: SwiftHelper stderr: ${errorMsg}`);
this.emit('error', new Error(`Helper stderr: ${errorMsg}`));
});

this.proc.on('error', (err) => {
console.error('SwiftIOBridge: Failed to start KeyTapHelper process:', err);
console.error('SwiftIOBridge: Failed to start SwiftHelper process:', err);
this.emit('error', err);
this.proc = null;
});

this.proc.on('close', (code, signal) => {
console.log(
`SwiftIOBridge: KeyTapHelper process exited with code ${code} and signal ${signal}`
`SwiftIOBridge: SwiftHelper process exited with code ${code} and signal ${signal}`
);
this.emit('close', code, signal);
this.proc = null;
Expand Down Expand Up @@ -236,7 +236,7 @@ export class SwiftIOBridge extends EventEmitter {

public stopHelper(): void {
if (this.proc) {
console.log('SwiftIOBridge: Stopping KeyTapHelper process...');
console.log('SwiftIOBridge: Stopping SwiftHelper process...');
this.proc.kill();
this.proc = null;
}
Expand Down
17 changes: 15 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,21 @@
"turbo": "^2.5.3",
"typescript": "5.8.2"
},
"packageManager": "pnpm@9.0.0",
"packageManager": "pnpm@10.4.0",
"engines": {
"node": ">=18"
"node": ">=24"
},
"pnpm": {
"ignoredBuiltDependencies": [
"@tailwindcss/oxide",
"better-sqlite3",
"core-js-pure",
"electron",
"electron-winstaller",
"esbuild",
"keytar",
"protobufjs",
"sharp"
]
}
}
79 changes: 79 additions & 0 deletions packages/native-helpers/swift-helper/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Swift Package Manager
.build/
.swiftpm/
Package.resolved

# Built executables
bin/
*.app/
*.ipa

# Xcode
*.xcodeproj/
*.xcworkspace/
*.xcuserdata/
*.xccheckout
*.moved-aside
DerivedData/
*.hmap
*.ipa
*.dSYM.zip
*.dSYM

# macOS
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# Swift specific
*.swiftdoc
*.swiftmodule
*.swiftsourceinfo

# SPM Dependencies
.swift-version

# Turbo cache (for monorepo)
.turbo/

# IDE files
.vscode/
.idea/
*.swp
*.swo
*~

# Logs
*.log

# Coverage reports
*.profdata
*.profraw

# Swift testing
.coverage/

# Generated files
Sources/*/models/generated/
26 changes: 26 additions & 0 deletions packages/native-helpers/swift-helper/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "SwiftHelper",
platforms: [
.macOS(.v10_15) // Set a deployment target, e.g., macOS 10.15 or later
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "SwiftHelper",
dependencies: [],
resources: [
.process("Resources") // This line tells SPM to copy the Resources folder
]
)
]
)
Loading
Loading