mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-11 01:51:33 -10:00
* Improve the desktop model switcher Problem: The desktop model picker was narrow, provider-scoped during search, and offered no way to keep frequently used models visible. Root cause: The picker rendered one flat grouped list and stored no user-level model preference. Fix: Add a provider rail, catalog-wide search, persisted favorites, provider metadata, responsive sizing, and localized labels. Verification: Run the focused model switcher and favorites tests, desktop typecheck, frontend production build, and browser interaction checks. * Open Overview for empty workspace docks Problem: Opening a session with an expanded but empty workspace dock showed a tab picker instead of useful context. Root cause: Workspace restoration deliberately left an empty dock unseeded and did not distinguish first session display from a user's later tab close. Fix: Seed the Overview tab once for each visible session while preserving deliberate tab closure until the session changes. Verification: Run the workspace panel command regression test, browser dock mode test, desktop typecheck, and frontend production build. * Add one-command desktop development Problem: Local contributors needed multiple manual commands to start the browser UI or complete Electron application. Root cause: The desktop workspace had build commands but no coordinated development entry points. Fix: Add pnpm dev for the browser frontend and pnpm dev:desktop to build the Go service, start or reuse Vite, launch Electron, and clean up child processes. Document both commands. Verification: Run the launcher syntax and help checks, desktop typecheck, Electron tests, and frontend production build. * Update browser gates for default Overview Problem: The desktop browser gate expected a fresh expanded dock to show the empty tab picker, which no longer matches the new default Overview behavior. Root cause: The end-to-end fixtures selected Files only through the empty-state picker and treated every recently closed tab as interchangeable. Fix: Assert the default Overview tab, open Files through the add-tab menu, and target the closed Files entry explicitly after Overview also enters tab history. Verification: Run the complete frontend app browser gate, including app lifecycle, dock view state, and runtime state scenarios.
109 lines
3.5 KiB
JavaScript
109 lines
3.5 KiB
JavaScript
#!/usr/bin/env node
|
|
import { spawn } from "node:child_process";
|
|
import { mkdir } from "node:fs/promises";
|
|
import { dirname, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const desktopRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
const pnpm = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
|
|
const serviceName = process.platform === "win32"
|
|
? "reasonix-desktop-service.exe"
|
|
: "reasonix-desktop-service";
|
|
const servicePath = resolve(desktopRoot, "build/bin", serviceName);
|
|
const port = process.env.REASONIX_DESKTOP_VITE_PORT || "5173";
|
|
const devUrl = process.env.REASONIX_ELECTRON_DEV_URL || `http://127.0.0.1:${port}`;
|
|
|
|
if (process.argv.includes("--help")) {
|
|
console.log("Build the Reasonix service, start Vite, and launch the Electron development shell.");
|
|
console.log("Usage: pnpm dev:desktop");
|
|
process.exit(0);
|
|
}
|
|
|
|
function start(command, args, env = process.env) {
|
|
return spawn(command, args, { cwd: desktopRoot, env, stdio: "inherit" });
|
|
}
|
|
|
|
function waitForExit(child, label) {
|
|
return new Promise((resolveExit, rejectExit) => {
|
|
child.once("error", rejectExit);
|
|
child.once("exit", (code, signal) => {
|
|
if (code === 0) resolveExit();
|
|
else rejectExit(new Error(`${label} exited with ${code ?? signal ?? "an unknown error"}`));
|
|
});
|
|
});
|
|
}
|
|
|
|
async function servesReasonix(url) {
|
|
try {
|
|
const response = await fetch(url);
|
|
if (!response.ok) return false;
|
|
const html = await response.text();
|
|
return /<title>\s*Reasonix\s*<\/title>/i.test(html);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
async function waitForVite(frontend) {
|
|
const deadline = Date.now() + 30_000;
|
|
while (Date.now() < deadline) {
|
|
if (frontend.exitCode !== null || frontend.signalCode) {
|
|
throw new Error(`Vite exited with ${frontend.exitCode ?? frontend.signalCode}`);
|
|
}
|
|
if (await servesReasonix(devUrl)) return;
|
|
await new Promise(resolveWait => setTimeout(resolveWait, 150));
|
|
}
|
|
throw new Error(`Vite did not become ready at ${devUrl}`);
|
|
}
|
|
|
|
let frontend;
|
|
let electron;
|
|
let exiting = false;
|
|
|
|
function stopChildren() {
|
|
if (electron?.exitCode === null) electron.kill("SIGTERM");
|
|
if (frontend?.exitCode === null) frontend.kill("SIGTERM");
|
|
}
|
|
|
|
function fail(error) {
|
|
console.error(error instanceof Error ? error.message : error);
|
|
stopChildren();
|
|
process.exitCode = 1;
|
|
}
|
|
|
|
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
process.once(signal, () => {
|
|
exiting = true;
|
|
stopChildren();
|
|
});
|
|
}
|
|
|
|
try {
|
|
await mkdir(dirname(servicePath), { recursive: true });
|
|
console.log("==> Building Reasonix desktop service");
|
|
await waitForExit(start("go", ["build", "-o", servicePath, "."]), "Go build");
|
|
|
|
if (await servesReasonix(devUrl)) {
|
|
console.log(`==> Reusing Reasonix Vite server at ${devUrl}`);
|
|
} else {
|
|
console.log(`==> Starting Reasonix Vite server at ${devUrl}`);
|
|
frontend = start(pnpm, ["--dir", "frontend", "dev", "--host", "127.0.0.1", "--port", port, "--strictPort"]);
|
|
await waitForVite(frontend);
|
|
}
|
|
|
|
console.log("==> Launching Reasonix Electron development shell");
|
|
electron = start(pnpm, ["--dir", "electron", "dev"], {
|
|
...process.env,
|
|
REASONIX_DESKTOP_SERVICE: servicePath,
|
|
REASONIX_ELECTRON_DEV_URL: devUrl,
|
|
});
|
|
const electronExit = waitForExit(electron, "Electron");
|
|
const frontendExit = frontend
|
|
? waitForExit(frontend, "Vite").then(() => { throw new Error("Vite stopped before Electron"); })
|
|
: new Promise(() => {});
|
|
await Promise.race([electronExit, frontendExit]);
|
|
if (frontend?.exitCode === null) frontend.kill("SIGTERM");
|
|
} catch (error) {
|
|
if (!exiting) fail(error);
|
|
}
|