Skip to content
Closed
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
4 changes: 3 additions & 1 deletion .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ on:

jobs:
claude-review:
# Fork PRs do not receive the OIDC token/secrets required by
# anthropics/claude-code-action on pull_request workflows.
if: github.event.pull_request.head.repo.full_name == github.repository
# Optional: Filter by PR author
# if: |
# github.event.pull_request.user.login == 'external-contributor' ||
Expand Down Expand Up @@ -41,4 +44,3 @@ jobs:
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://code.claude.com/docs/en/cli-reference for available options

58 changes: 58 additions & 0 deletions .github/workflows/project-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Project Checks

on:
pull_request:
branches:
- main
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read

concurrency:
group: project-checks-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test-i18n-build:
name: Tests, i18n, and build
runs-on: ubuntu-latest
timeout-minutes: 15
env:
CI: true
ELECTRON_SKIP_BINARY_DOWNLOAD: '1'
SUPERCMD_SKIP_ELECTRON_TESTS: '1'

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: package-lock.json

- name: Install dependencies without native postinstall scripts
run: npm ci --ignore-scripts

- name: Run tests
run: npm test

- name: Check i18n
run: npm run check:i18n

- name: Build main process
run: npm run build:main

- name: Typecheck renderer
run: npm run typecheck:renderer

- name: Build renderer
run: npm run build:renderer
14 changes: 10 additions & 4 deletions package-lock.json

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

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
"start:electron": "wait-on dist/main/main.js && cross-env NODE_ENV=development SUPERCMD_OPEN_DEVTOOLS_ON_STARTUP=1 electron .",
"build": "npm run build:main && npm run build:renderer && npm run build:native",
"build:main": "tsc -p tsconfig.main.json && cp src/main/emoji-data.json dist/main/emoji-data.json",
"typecheck:renderer": "tsc -p tsconfig.renderer.json --noEmit --pretty false",
"build:renderer": "vite build",
"check:i18n": "node scripts/check-i18n.mjs",
"test": "node --test 'scripts/test-*.mjs'",
"build:native": "node scripts/build-native.mjs",
"ensure:cross-arch-esbuild": "node scripts/ensure-cross-arch-esbuild.mjs",
"postinstall": "node scripts/ensure-cross-arch-esbuild.mjs && electron-builder install-app-deps",
"postinstall": "node scripts/ensure-macos-runtime-deps.mjs && node scripts/ensure-cross-arch-esbuild.mjs && electron-builder install-app-deps",
"start": "electron .",
"package": "cross-env NODE_ENV=production npm run build && electron-builder",
"package:unsigned": "cross-env NODE_ENV=production CSC_IDENTITY_AUTO_DISCOVERY=false npm run build && electron-builder --mac dmg -c.mac.identity=null -c.mac.notarize=false"
Expand All @@ -42,12 +43,9 @@
},
"dependencies": {
"@aptabase/electron": "^0.3.1",
"@esbuild/darwin-arm64": "0.19.12",
"@esbuild/darwin-x64": "0.19.12",
"@phosphor-icons/react": "^2.1.10",
"@raycast/api": "^1.104.5",
"electron-builder-notarize": "^1.5.2",
"electron-liquid-glass": "^1.1.1",
"electron-updater": "^6.7.3",
"esbuild": "^0.19.12",
"lucide-react": "^0.312.0",
Expand All @@ -57,6 +55,9 @@
"react-dom": "^18.2.0",
"transliteration": "^2.6.1"
},
"optionalDependencies": {
"electron-liquid-glass": "^1.1.1"
},
"build": {
"appId": "com.supercmd.app",
"productName": "SuperCmd",
Expand Down
94 changes: 85 additions & 9 deletions scripts/build-native.mjs
Original file line number Diff line number Diff line change
@@ -1,19 +1,78 @@
#!/usr/bin/env node
import { execSync } from 'child_process';
import { mkdirSync } from 'fs';
import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'fs';
import { createRequire } from 'module';
import path from 'path';
import { fileURLToPath } from 'url';

const require = createRequire(import.meta.url);
const __filename = fileURLToPath(import.meta.url);

mkdirSync('dist/native', { recursive: true });

const electronVersion = require('../node_modules/electron/package.json').version;
const arch = process.arch;
const stampVersion = 1;

function run(cmd) {
execSync(cmd, { stdio: 'inherit' });
}

function readStamp(stampPath) {
try {
return JSON.parse(readFileSync(stampPath, 'utf8'));
} catch {
return null;
}
}

function inputState(filePath) {
if (!existsSync(filePath)) return null;
const stats = statSync(filePath);
return {
path: filePath,
mtimeMs: stats.mtimeMs,
size: stats.size,
};
}

function isUpToDate(out, stampPath, stamp) {
if (!existsSync(out)) return false;
const currentInputs = stamp.inputs.map(inputState);
if (currentInputs.some((input) => input === null)) return false;

const previous = readStamp(stampPath);
if (JSON.stringify(previous) !== JSON.stringify({ ...stamp, inputState: currentInputs })) {
return false;
}

const outputMtime = statSync(out).mtimeMs;
return currentInputs.every((input) => input.mtimeMs <= outputMtime);
}

function writeStamp(stampPath, stamp) {
const currentInputs = stamp.inputs.map(inputState);
writeFileSync(stampPath, JSON.stringify({ ...stamp, inputState: currentInputs }, null, 2));
}

function buildIfStale({ label, out, inputs, command }) {
const stampPath = `${out}.stamp.json`;
const stamp = {
version: stampVersion,
label,
command,
inputs,
};

if (isUpToDate(out, stampPath, stamp)) {
console.log(`[native] ${path.basename(out)} is up to date, skipping rebuild`);
return;
}

run(command);
writeStamp(stampPath, stamp);
}

const swift = [
['dist/native/get-selected-text', 'src/native/get-selected-text.swift',
'-framework Foundation -framework ApplicationServices -framework AppKit'],
Expand Down Expand Up @@ -49,17 +108,34 @@ const swift = [
];

for (const [out, src, frameworks] of swift) {
run(`swiftc -O -o ${out} ${src} ${frameworks}`);
const inputs = [...src.split(' '), __filename];
buildIfStale({
label: path.basename(out),
out,
inputs,
command: `swiftc -O -o ${out} ${src} ${frameworks}`,
});
}

// Build native Node addon (native_helpers.node)
run(
`cd src/native/native-helpers-addon && ` +
`HOME=~/.electron-gyp npx node-gyp rebuild ` +
`--target=${electronVersion} --arch=${arch} ` +
`--dist-url=https://electronjs.org/headers && ` +
`cp build/Release/native_helpers.node ../../../dist/native/native_helpers.node`
);
buildIfStale({
label: 'native_helpers.node',
out: 'dist/native/native_helpers.node',
inputs: [
'src/native/native-helpers-addon/binding.gyp',
'src/native/native-helpers-addon/native_helpers.mm',
'package.json',
'package-lock.json',
'node_modules/electron/package.json',
__filename,
],
command:
`cd src/native/native-helpers-addon && ` +
`HOME=~/.electron-gyp npx node-gyp rebuild ` +
`--target=${electronVersion} --arch=${arch} ` +
`--dist-url=https://electronjs.org/headers && ` +
`cp build/Release/native_helpers.node ../../../dist/native/native_helpers.node`,
});

run('node scripts/build-whispercpp.mjs');
run('node scripts/build-parakeet.mjs');
Expand Down
2 changes: 2 additions & 0 deletions scripts/build-parakeet.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ const distNativeDir = path.join(repoRoot, 'dist', 'native');
const binaryDest = path.join(distNativeDir, 'parakeet-transcriber');

const sourceFiles = [
__filename,
path.join(packageDir, 'Sources', 'main.swift'),
path.join(packageDir, 'Package.swift'),
path.join(packageDir, 'Package.resolved'),
];

function needsRebuild() {
Expand Down
2 changes: 2 additions & 0 deletions scripts/build-soulver-calculator.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ const binaryDest = path.join(outDir, 'soulver-calculator');
const frameworkDest = path.join(outDir, 'SoulverCore.framework');

const sourceFiles = [
__filename,
path.join(packageDir, 'Sources', 'main.swift'),
path.join(packageDir, 'Package.swift'),
path.join(packageDir, 'Package.resolved'),
];

function needsRebuild() {
Expand Down
Loading
Loading