Add Windows Electron desktop support (#1093)
* fix: make upstream sync actions target the selected remote Ensure fetch and pull actually honor upstream selection so fork maintenance works from the Git sidebar, and surface upstream branch status alongside the primary origin-tracking indicators. * feat: add Windows Electron desktop foundation * fix(electron): stabilize Windows desktop packaging * fix(electron): stabilize Windows desktop chrome Use native Windows titlebar behavior with an Alt-accessible hidden menu, and harden Windows dev command launching so the desktop app follows platform conventions. * fix(electron): stabilize Windows dev startup * fix(electron): clarify desktop artifact names * fix(electron): harden Windows desktop release and launch * fix(electron): address Windows release review * fix(electron): point updater and release links to org repo * Fix Windows settings persistence fallback * Fix Windows Electron dev startup * Add Windows Electron window controls * Fix Windows Electron install and opencode launch * fix: resolve git status for repositories without upstream Fixes repository detection stuck on Checking repository Handles git status when no upstream is configured Adds regression coverage for git status loading * Add Windows app menu button * fix: preserve file editor line endings * ci: add desktop release smoke workflow --------- Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
committed by
GitHub
co-authored by
Bohdan Triapitsyn
parent
cc7969ac00
commit
becd240168
@@ -51,6 +51,30 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
||||
}
|
||||
};
|
||||
|
||||
const resolveWindowsExecutablePath = (candidate) => {
|
||||
if (process.platform !== 'win32' || typeof candidate !== 'string' || candidate.trim().length === 0) {
|
||||
return candidate;
|
||||
}
|
||||
|
||||
const trimmed = candidate.trim();
|
||||
const ext = path.extname(trimmed).toLowerCase();
|
||||
if (ext) {
|
||||
return isExecutable(trimmed) ? trimmed : null;
|
||||
}
|
||||
|
||||
const pathExt = process.env.PATHEXT || process.env.PathExt || '.COM;.EXE;.BAT;.CMD';
|
||||
for (const rawExt of pathExt.split(';')) {
|
||||
const normalizedExt = rawExt.trim();
|
||||
if (!normalizedExt) continue;
|
||||
const withExt = `${trimmed}${normalizedExt.startsWith('.') ? normalizedExt : `.${normalizedExt}`}`;
|
||||
if (isExecutable(withExt)) {
|
||||
return withExt;
|
||||
}
|
||||
}
|
||||
|
||||
return isExecutable(trimmed) ? trimmed : null;
|
||||
};
|
||||
|
||||
const searchPathFor = (binaryName) => {
|
||||
const trimmed = typeof binaryName === 'string' ? binaryName.trim() : '';
|
||||
if (!trimmed) {
|
||||
@@ -59,7 +83,7 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
||||
|
||||
const current = process.env.PATH || '';
|
||||
const parts = current.split(path.delimiter).filter(Boolean);
|
||||
const candidateNames = [trimmed];
|
||||
const candidateNames = [];
|
||||
|
||||
if (process.platform === 'win32' && !path.extname(trimmed)) {
|
||||
const pathExt = process.env.PATHEXT || process.env.PathExt || '.COM;.EXE;.BAT;.CMD';
|
||||
@@ -73,6 +97,8 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
||||
}
|
||||
}
|
||||
|
||||
candidateNames.push(trimmed);
|
||||
|
||||
for (const dir of parts) {
|
||||
for (const candidateName of candidateNames) {
|
||||
const candidate = path.join(dir, candidateName);
|
||||
@@ -649,6 +675,9 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
if (process.platform === 'win32') {
|
||||
return resolveWindowsExecutablePath(trimmed);
|
||||
}
|
||||
return isExecutable(trimmed) ? trimmed : null;
|
||||
};
|
||||
|
||||
@@ -669,10 +698,20 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const packageShim = path.join(nodeModulesDir, 'opencode-ai', 'bin', 'opencode.exe');
|
||||
if (isExecutable(packageShim)) {
|
||||
return packageShim;
|
||||
}
|
||||
|
||||
for (const packageName of getWindowsNativeOpencodePackageNames()) {
|
||||
const candidate = path.join(nodeModulesDir, packageName, 'bin', 'opencode.exe');
|
||||
if (isExecutable(candidate)) {
|
||||
return candidate;
|
||||
const candidates = [
|
||||
path.join(nodeModulesDir, packageName, 'bin', 'opencode.exe'),
|
||||
path.join(nodeModulesDir, 'opencode-ai', 'node_modules', packageName, 'bin', 'opencode.exe'),
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (isExecutable(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -816,6 +855,15 @@ export const createOpenCodeEnvRuntime = (deps) => {
|
||||
|
||||
const directBinary = normalizeExecutableCandidate(candidate);
|
||||
if (directBinary) {
|
||||
const directExt = path.extname(directBinary).toLowerCase();
|
||||
if (WINDOWS_BATCH_EXTENSIONS.has(directExt)) {
|
||||
return {
|
||||
binary: process.env.ComSpec || 'cmd.exe',
|
||||
args: ['/d', '/s', '/c', 'call', directBinary],
|
||||
wrapperType: 'cmd-wrapper',
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
binary: directBinary,
|
||||
args: [],
|
||||
|
||||
Reference in New Issue
Block a user