Squashed commit of the following:
commit 9cf278791848e0a618ebabca97d9fc7405047cde
Author: haritabh-z01 <haritabh.z01+github@gmail.com>
Date: Fri Sep 12 00:15:52 2025 +0530
fix: native dep coppying on windows
commit 4cdebf31659753f8f9bf0a01299474dc7a1a99a1
Author: haritabh-z01 <haritabh.z01+github@gmail.com>
Date: Thu Sep 11 23:21:06 2025 +0530
chore: set diff default shortcuts for windows
commit 9caf66fa7b5188cf445ace530ccf35033853909d
Author: haritabh-z01 <haritabh.z01+github@gmail.com>
Date: Thu Sep 11 23:10:16 2025 +0530
fix: windows shortcuts compatibility
This commit is contained in:
parent
93496a2bde
commit
f92d47df30
8 changed files with 271 additions and 176 deletions
|
|
@ -175,7 +175,10 @@ const config: ForgeConfig = {
|
|||
|
||||
// Read where the symlink points to
|
||||
const symlinkTarget = readlinkSync(localDepPath);
|
||||
const absoluteTarget = join(localDepPath, "..", symlinkTarget);
|
||||
let absoluteTarget = symlinkTarget;
|
||||
if (process.platform !== "win32") {
|
||||
absoluteTarget = join(localDepPath, "..", symlinkTarget);
|
||||
}
|
||||
const sourcePath = normalize(absoluteTarget);
|
||||
|
||||
console.log(` Symlink points to: ${sourcePath}`);
|
||||
|
|
|
|||
|
|
@ -1,156 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const https = require('https');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
const { createWriteStream, mkdirSync, chmodSync } = fs;
|
||||
|
||||
// Node.js version to download
|
||||
const NODE_VERSION = '24.4.0';
|
||||
|
||||
// Platform configurations
|
||||
const PLATFORMS = [
|
||||
{
|
||||
platform: 'darwin',
|
||||
arch: 'arm64',
|
||||
url: `https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-darwin-arm64.tar.gz`,
|
||||
binary: 'bin/node'
|
||||
},
|
||||
{
|
||||
platform: 'darwin',
|
||||
arch: 'x64',
|
||||
url: `https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-darwin-x64.tar.gz`,
|
||||
binary: 'bin/node'
|
||||
},
|
||||
{
|
||||
platform: 'win32',
|
||||
arch: 'x64',
|
||||
url: `https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-win-x64.zip`,
|
||||
binary: 'node.exe'
|
||||
},
|
||||
{
|
||||
platform: 'linux',
|
||||
arch: 'x64',
|
||||
url: `https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz`,
|
||||
binary: 'bin/node'
|
||||
}
|
||||
];
|
||||
|
||||
// Base directory for binaries
|
||||
const RESOURCES_DIR = path.join(__dirname, '..', 'node-binaries');
|
||||
|
||||
async function downloadFile(url, dest) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const file = createWriteStream(dest);
|
||||
|
||||
https.get(url, (response) => {
|
||||
if (response.statusCode === 302 || response.statusCode === 301) {
|
||||
// Handle redirect
|
||||
https.get(response.headers.location, (redirectResponse) => {
|
||||
redirectResponse.pipe(file);
|
||||
file.on('finish', () => {
|
||||
file.close(resolve);
|
||||
});
|
||||
}).on('error', reject);
|
||||
} else {
|
||||
response.pipe(file);
|
||||
file.on('finish', () => {
|
||||
file.close(resolve);
|
||||
});
|
||||
}
|
||||
}).on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
async function extractArchive(archivePath, platform) {
|
||||
const tempDir = path.join(path.dirname(archivePath), 'temp');
|
||||
mkdirSync(tempDir, { recursive: true });
|
||||
|
||||
if (platform === 'win32') {
|
||||
// Use unzip command (available on macOS) to extract zip files
|
||||
execSync(`unzip -q "${archivePath}" -d "${tempDir}"`, { stdio: 'inherit' });
|
||||
} else {
|
||||
// Use tar for Unix-like systems
|
||||
execSync(`tar -xzf "${archivePath}" -C "${tempDir}"`, { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
return tempDir;
|
||||
}
|
||||
|
||||
async function downloadNodeBinary(config) {
|
||||
const { platform, arch, url, binary } = config;
|
||||
const platformDir = path.join(RESOURCES_DIR, `${platform}-${arch}`);
|
||||
const binaryPath = path.join(platformDir, platform === 'win32' ? 'node.exe' : 'node');
|
||||
|
||||
// Skip if already exists
|
||||
if (fs.existsSync(binaryPath)) {
|
||||
console.log(`✓ ${platform}-${arch} binary already exists`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Downloading Node.js for ${platform}-${arch}...`);
|
||||
|
||||
// Create directory
|
||||
mkdirSync(platformDir, { recursive: true });
|
||||
|
||||
// Download archive
|
||||
const archiveExt = platform === 'win32' ? '.zip' : '.tar.gz';
|
||||
const archivePath = path.join(platformDir, `node-v${NODE_VERSION}${archiveExt}`);
|
||||
|
||||
try {
|
||||
await downloadFile(url, archivePath);
|
||||
console.log(`Downloaded archive for ${platform}-${arch}`);
|
||||
|
||||
// Extract archive
|
||||
const tempDir = await extractArchive(archivePath, platform);
|
||||
|
||||
// Find the node binary in extracted files
|
||||
// Windows uses different directory naming convention (win instead of win32)
|
||||
const extractedDirName = platform === 'win32'
|
||||
? `node-v${NODE_VERSION}-win-${arch}`
|
||||
: `node-v${NODE_VERSION}-${platform}-${arch}`;
|
||||
const extractedBinaryPath = path.join(tempDir, extractedDirName, binary);
|
||||
|
||||
// Copy binary to final location
|
||||
fs.copyFileSync(extractedBinaryPath, binaryPath);
|
||||
|
||||
// Make executable on Unix-like systems
|
||||
if (platform !== 'win32') {
|
||||
chmodSync(binaryPath, '755');
|
||||
}
|
||||
|
||||
// Clean up
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
fs.unlinkSync(archivePath);
|
||||
|
||||
console.log(`✓ Successfully installed ${platform}-${arch} binary`);
|
||||
} catch (error) {
|
||||
console.error(`✗ Failed to download ${platform}-${arch}:`, error.message);
|
||||
// Clean up on failure
|
||||
if (fs.existsSync(archivePath)) {
|
||||
fs.unlinkSync(archivePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log(`Downloading Node.js v${NODE_VERSION} binaries for all platforms...\n`);
|
||||
|
||||
// Create base directory
|
||||
mkdirSync(RESOURCES_DIR, { recursive: true });
|
||||
|
||||
// Download binaries for all platforms
|
||||
for (const platform of PLATFORMS) {
|
||||
await downloadNodeBinary(platform);
|
||||
}
|
||||
|
||||
console.log('\nDone! Node.js binaries downloaded to:', RESOURCES_DIR);
|
||||
}
|
||||
|
||||
// Run if called directly
|
||||
if (require.main === module) {
|
||||
main().catch(console.error);
|
||||
}
|
||||
|
||||
module.exports = { downloadNodeBinary, PLATFORMS, NODE_VERSION };
|
||||
|
|
@ -142,7 +142,10 @@ async function extractArchive(
|
|||
|
||||
if (platform === "win32") {
|
||||
// Use unzip command (available on macOS) to extract zip files
|
||||
execSync(`unzip -q "${archivePath}" -d "${tempDir}"`, { stdio: "inherit" });
|
||||
execSync(
|
||||
`powershell -Command "Expand-Archive -Path '${archivePath}' -DestinationPath '${tempDir}' -Force"`,
|
||||
{ stdio: "inherit" },
|
||||
);
|
||||
} else {
|
||||
// Use tar for Unix-like systems
|
||||
execSync(`tar -xzf "${archivePath}" -C "${tempDir}"`, { stdio: "inherit" });
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ interface ShortcutInputProps {
|
|||
onRecordingShortcutChange: (recording: boolean) => void;
|
||||
}
|
||||
|
||||
const MODIFIER_KEYS = ["Cmd", "Ctrl", "Alt", "Shift", "Fn"];
|
||||
const MODIFIER_KEYS = ["Cmd", "Win", "Ctrl", "Alt", "Shift", "Fn"];
|
||||
const MAX_KEY_COMBINATION_LENGTH = 3;
|
||||
|
||||
type ValidationResult = {
|
||||
|
|
@ -60,7 +60,7 @@ function validateShortcut(keys: string[]): ValidationResult {
|
|||
return {
|
||||
valid: false,
|
||||
error:
|
||||
"Multiple keys require at least one modifier (Cmd, Ctrl, Alt, Shift, or Fn)",
|
||||
"Multiple keys require at least one modifier (Cmd, Win, Ctrl, Alt, Shift, or Fn)",
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,10 +19,27 @@ import {
|
|||
type NewAppSettings,
|
||||
type AppSettingsData,
|
||||
} from "./schema";
|
||||
import { isWindows, isMacOS } from "../utils/platform";
|
||||
|
||||
// Singleton ID for app settings (we only have one settings record)
|
||||
const SETTINGS_ID = 1;
|
||||
|
||||
// Platform-specific default shortcuts
|
||||
const getDefaultShortcuts = () => {
|
||||
if (isMacOS()) {
|
||||
return {
|
||||
pushToTalk: "Fn",
|
||||
toggleRecording: "Fn+Space",
|
||||
};
|
||||
} else {
|
||||
// Windows and Linux
|
||||
return {
|
||||
pushToTalk: "Ctrl+Win",
|
||||
toggleRecording: "Ctrl+Win+Space",
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Default settings
|
||||
const defaultSettings: AppSettingsData = {
|
||||
formatterConfig: {
|
||||
|
|
@ -48,10 +65,7 @@ const defaultSettings: AppSettingsData = {
|
|||
silenceThreshold: 3,
|
||||
maxRecordingDuration: 60,
|
||||
},
|
||||
shortcuts: {
|
||||
pushToTalk: "Fn",
|
||||
toggleRecording: "",
|
||||
},
|
||||
shortcuts: getDefaultShortcuts(),
|
||||
modelProvidersConfig: {
|
||||
defaultSpeechModel: "",
|
||||
defaultLanguageModel: "",
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { globalShortcut } from "electron";
|
|||
import { SettingsService } from "@/services/settings-service";
|
||||
import { NativeBridge } from "@/services/platform/native-bridge-service";
|
||||
import { getKeyNameFromPayload } from "@/utils/keycode-map";
|
||||
import { isWindows } from "@/utils/platform";
|
||||
import { KeyEventPayload, HelperEvent } from "@amical/types";
|
||||
import { logger } from "@/main/logger";
|
||||
|
||||
|
|
@ -89,9 +90,9 @@ export class ShortcutManager extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
// Track modifier keys
|
||||
// Track modifier keys with platform-aware names
|
||||
const modifiers = [
|
||||
{ flag: payload.metaKey, name: "Cmd" },
|
||||
{ flag: payload.metaKey, name: isWindows() ? "Win" : "Cmd" },
|
||||
{ flag: payload.ctrlKey, name: "Ctrl" },
|
||||
{ flag: payload.altKey, name: "Alt" },
|
||||
{ flag: payload.shiftKey, name: "Shift" },
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
updateAppSettings,
|
||||
} from "../db/app-settings";
|
||||
import type { AppSettingsData } from "../db/schema";
|
||||
import { isWindows, isMacOS } from "../utils/platform";
|
||||
|
||||
/**
|
||||
* Database-backed settings service with typed configuration
|
||||
|
|
@ -123,10 +124,14 @@ export class SettingsService {
|
|||
*/
|
||||
async getShortcuts(): Promise<ShortcutsConfig> {
|
||||
const shortcuts = await getSettingsSection("shortcuts");
|
||||
// Return defaults if not set
|
||||
// Return platform-specific defaults if not set
|
||||
const defaults = isMacOS()
|
||||
? { pushToTalk: "Fn", toggleRecording: "Fn+Space" }
|
||||
: { pushToTalk: "Ctrl+Win", toggleRecording: "Ctrl+Win+Space" };
|
||||
|
||||
return {
|
||||
pushToTalk: shortcuts?.pushToTalk || "Fn",
|
||||
toggleRecording: shortcuts?.toggleRecording || "Fn+Space",
|
||||
pushToTalk: shortcuts?.pushToTalk || defaults.pushToTalk,
|
||||
toggleRecording: shortcuts?.toggleRecording || defaults.toggleRecording,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import { isWindows } from "./platform";
|
||||
|
||||
// macOS keycode mappings
|
||||
export const keycodeToKey: Record<number, string> = {
|
||||
const macOSKeycodeToKey: Record<number, string> = {
|
||||
// Letters
|
||||
0: "A",
|
||||
1: "S",
|
||||
|
|
@ -81,8 +83,202 @@ export const keycodeToKey: Record<number, string> = {
|
|||
50: "`",
|
||||
};
|
||||
|
||||
// Windows Virtual Key code mappings
|
||||
const windowsVKToKey: Record<number, string> = {
|
||||
// Mouse buttons (0x01-0x06)
|
||||
0x01: "LButton",
|
||||
0x02: "RButton",
|
||||
0x04: "MButton",
|
||||
0x05: "XButton1",
|
||||
0x06: "XButton2",
|
||||
|
||||
// Standard keys
|
||||
0x08: "Backspace",
|
||||
0x09: "Tab",
|
||||
0x0c: "Clear",
|
||||
0x0d: "Enter",
|
||||
0x10: "Shift",
|
||||
0x11: "Ctrl",
|
||||
0x12: "Alt",
|
||||
0x13: "Pause",
|
||||
0x14: "CapsLock",
|
||||
0x1b: "Escape",
|
||||
0x20: "Space",
|
||||
0x21: "PageUp",
|
||||
0x22: "PageDown",
|
||||
0x23: "End",
|
||||
0x24: "Home",
|
||||
0x25: "Left",
|
||||
0x26: "Up",
|
||||
0x27: "Right",
|
||||
0x28: "Down",
|
||||
0x29: "Select",
|
||||
0x2a: "Print",
|
||||
0x2b: "Execute",
|
||||
0x2c: "PrintScreen",
|
||||
0x2d: "Insert",
|
||||
0x2e: "Delete",
|
||||
0x2f: "Help",
|
||||
|
||||
// Numbers (0-9)
|
||||
0x30: "0",
|
||||
0x31: "1",
|
||||
0x32: "2",
|
||||
0x33: "3",
|
||||
0x34: "4",
|
||||
0x35: "5",
|
||||
0x36: "6",
|
||||
0x37: "7",
|
||||
0x38: "8",
|
||||
0x39: "9",
|
||||
|
||||
// Letters (A-Z)
|
||||
0x41: "A",
|
||||
0x42: "B",
|
||||
0x43: "C",
|
||||
0x44: "D",
|
||||
0x45: "E",
|
||||
0x46: "F",
|
||||
0x47: "G",
|
||||
0x48: "H",
|
||||
0x49: "I",
|
||||
0x4a: "J",
|
||||
0x4b: "K",
|
||||
0x4c: "L",
|
||||
0x4d: "M",
|
||||
0x4e: "N",
|
||||
0x4f: "O",
|
||||
0x50: "P",
|
||||
0x51: "Q",
|
||||
0x52: "R",
|
||||
0x53: "S",
|
||||
0x54: "T",
|
||||
0x55: "U",
|
||||
0x56: "V",
|
||||
0x57: "W",
|
||||
0x58: "X",
|
||||
0x59: "Y",
|
||||
0x5a: "Z",
|
||||
|
||||
// Windows keys
|
||||
0x5b: "LWin",
|
||||
0x5c: "RWin",
|
||||
0x5d: "Apps",
|
||||
0x5f: "Sleep",
|
||||
|
||||
// Numpad
|
||||
0x60: "Numpad0",
|
||||
0x61: "Numpad1",
|
||||
0x62: "Numpad2",
|
||||
0x63: "Numpad3",
|
||||
0x64: "Numpad4",
|
||||
0x65: "Numpad5",
|
||||
0x66: "Numpad6",
|
||||
0x67: "Numpad7",
|
||||
0x68: "Numpad8",
|
||||
0x69: "Numpad9",
|
||||
0x6a: "Multiply",
|
||||
0x6b: "Add",
|
||||
0x6c: "Separator",
|
||||
0x6d: "Subtract",
|
||||
0x6e: "Decimal",
|
||||
0x6f: "Divide",
|
||||
|
||||
// Function keys (F1-F24)
|
||||
0x70: "F1",
|
||||
0x71: "F2",
|
||||
0x72: "F3",
|
||||
0x73: "F4",
|
||||
0x74: "F5",
|
||||
0x75: "F6",
|
||||
0x76: "F7",
|
||||
0x77: "F8",
|
||||
0x78: "F9",
|
||||
0x79: "F10",
|
||||
0x7a: "F11",
|
||||
0x7b: "F12",
|
||||
0x7c: "F13",
|
||||
0x7d: "F14",
|
||||
0x7e: "F15",
|
||||
0x7f: "F16",
|
||||
0x80: "F17",
|
||||
0x81: "F18",
|
||||
0x82: "F19",
|
||||
0x83: "F20",
|
||||
0x84: "F21",
|
||||
0x85: "F22",
|
||||
0x86: "F23",
|
||||
0x87: "F24",
|
||||
|
||||
// Other keys
|
||||
0x90: "NumLock",
|
||||
0x91: "ScrollLock",
|
||||
0xa0: "LShift",
|
||||
0xa1: "RShift",
|
||||
0xa2: "LCtrl",
|
||||
0xa3: "RCtrl",
|
||||
0xa4: "LAlt",
|
||||
0xa5: "RAlt",
|
||||
|
||||
// Browser control keys
|
||||
0xa6: "BrowserBack",
|
||||
0xa7: "BrowserForward",
|
||||
0xa8: "BrowserRefresh",
|
||||
0xa9: "BrowserStop",
|
||||
0xaa: "BrowserSearch",
|
||||
0xab: "BrowserFavorites",
|
||||
0xac: "BrowserHome",
|
||||
|
||||
// Volume control keys
|
||||
0xad: "VolumeMute",
|
||||
0xae: "VolumeDown",
|
||||
0xaf: "VolumeUp",
|
||||
|
||||
// Media control keys
|
||||
0xb0: "MediaNextTrack",
|
||||
0xb1: "MediaPrevTrack",
|
||||
0xb2: "MediaStop",
|
||||
0xb3: "MediaPlayPause",
|
||||
|
||||
// Launch keys
|
||||
0xb4: "LaunchMail",
|
||||
0xb5: "LaunchMediaSelect",
|
||||
0xb6: "LaunchApp1",
|
||||
0xb7: "LaunchApp2",
|
||||
|
||||
// OEM keys (punctuation and symbols)
|
||||
0xba: ";", // OEM_1
|
||||
0xbb: "=", // OEM_PLUS
|
||||
0xbc: ",", // OEM_COMMA
|
||||
0xbd: "-", // OEM_MINUS
|
||||
0xbe: ".", // OEM_PERIOD
|
||||
0xbf: "/", // OEM_2
|
||||
0xc0: "`", // OEM_3
|
||||
0xdb: "[", // OEM_4
|
||||
0xdc: "\\", // OEM_5
|
||||
0xdd: "]", // OEM_6
|
||||
0xde: "'", // OEM_7
|
||||
0xdf: "OEM_8",
|
||||
|
||||
// Additional keys
|
||||
0xe1: "OEM_AX",
|
||||
0xe2: "OEM_102",
|
||||
0xe3: "ICOHelp",
|
||||
0xe4: "ICO00",
|
||||
0xe5: "ProcessKey",
|
||||
0xe6: "ICOClear",
|
||||
0xe7: "Packet",
|
||||
};
|
||||
|
||||
// Export the appropriate mapping based on platform
|
||||
export const keycodeToKey: Record<number, string> = isWindows()
|
||||
? windowsVKToKey
|
||||
: macOSKeycodeToKey;
|
||||
|
||||
export function getKeyFromKeycode(keycode: number): string | undefined {
|
||||
return keycodeToKey[keycode];
|
||||
// Use the appropriate mapping based on platform
|
||||
const mapping = isWindows() ? windowsVKToKey : macOSKeycodeToKey;
|
||||
return mapping[keycode];
|
||||
}
|
||||
|
||||
export function matchesShortcutKey(
|
||||
|
|
@ -91,17 +287,46 @@ export function matchesShortcutKey(
|
|||
): boolean {
|
||||
if (keycode === undefined) return false;
|
||||
|
||||
const mappedKey = keycodeToKey[keycode];
|
||||
const mappedKey = getKeyFromKeycode(keycode);
|
||||
if (!mappedKey) return false;
|
||||
|
||||
return mappedKey.toUpperCase() === keyName.toUpperCase();
|
||||
// Normalize Windows modifier key names for comparison
|
||||
const normalizedMappedKey = normalizeKeyName(mappedKey);
|
||||
const normalizedKeyName = normalizeKeyName(keyName);
|
||||
|
||||
return normalizedMappedKey.toUpperCase() === normalizedKeyName.toUpperCase();
|
||||
}
|
||||
|
||||
export function getKeyNameFromPayload(payload: any): string | undefined {
|
||||
export function getKeyNameFromPayload(payload: {
|
||||
key?: string;
|
||||
keyCode?: number;
|
||||
}): string | undefined {
|
||||
// Try to get key name from various sources
|
||||
if (payload.key) return payload.key;
|
||||
if (payload.keyCode !== undefined && keycodeToKey[payload.keyCode]) {
|
||||
return keycodeToKey[payload.keyCode];
|
||||
if (payload.keyCode !== undefined) {
|
||||
const keyName = getKeyFromKeycode(payload.keyCode);
|
||||
if (keyName) {
|
||||
// Normalize key names for consistency across platforms
|
||||
return normalizeKeyName(keyName);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Helper function to normalize key names across platforms
|
||||
function normalizeKeyName(keyName: string): string {
|
||||
// Normalize left/right variants to single names
|
||||
const normalizations: Record<string, string> = {
|
||||
LWin: "Win",
|
||||
RWin: "Win",
|
||||
LShift: "Shift",
|
||||
RShift: "Shift",
|
||||
LCtrl: "Ctrl",
|
||||
RCtrl: "Ctrl",
|
||||
LAlt: "Alt",
|
||||
RAlt: "Alt",
|
||||
// Keep other keys as-is
|
||||
};
|
||||
|
||||
return normalizations[keyName] || keyName;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue