diff --git a/electron/.gitignore b/electron/.gitignore
index bd040a77..588efb1a 100644
--- a/electron/.gitignore
+++ b/electron/.gitignore
@@ -21,6 +21,13 @@ app_dist
resources/fastapi
resources/nextjs
dist
+eng.traineddata
+servers/fastapi/build/
+servers/fastapi/dist/
servers/fastapi/fastembed_cache/
electron/.cache/
-electron/.cache/export-runtime/
\ No newline at end of file
+electron/.cache/export-runtime/
+*.pkg
+*.toc
+*.zip
+*.pyc
\ No newline at end of file
diff --git a/electron/app/ipc/setup_install_handlers.ts b/electron/app/ipc/setup_install_handlers.ts
index 29ddb454..3ffa345b 100644
--- a/electron/app/ipc/setup_install_handlers.ts
+++ b/electron/app/ipc/setup_install_handlers.ts
@@ -20,6 +20,7 @@ import {
import { getSetupStatus } from "../utils/setup-dependencies";
import {
getImageMagickDownloadUrl,
+ getImageMagickManualInstallCommands,
isImageMagickInstalled,
} from "../utils/imagemagick-check";
@@ -72,6 +73,33 @@ function commandExists(command: string, versionArgs: string[] = ["--version"]):
return result.status === 0;
}
+function resolveBrewCommand(): string | null {
+ if (commandExists("brew")) {
+ return "brew";
+ }
+
+ const candidates = ["/opt/homebrew/bin/brew", "/usr/local/bin/brew"];
+ for (const candidate of candidates) {
+ if (fs.existsSync(candidate)) {
+ return candidate;
+ }
+ }
+ return null;
+}
+
+function resolveLinuxEscalationCommand(): string | null {
+ if (commandExists("pkexec", ["--version"])) return "pkexec";
+ if (commandExists("sudo", ["-V"])) return "sudo";
+ return null;
+}
+
+function logManualImageMagickCommands(wc: WebContents) {
+ for (const line of getImageMagickManualInstallCommands()) {
+ const level = line.endsWith(":") ? "info" : "cmd";
+ sendImageMagickLog(wc, level, line);
+ }
+}
+
function runInstallCommand(
wc: WebContents,
command: string,
@@ -210,7 +238,18 @@ export function setupSetupInstallHandlers() {
if (process.platform === "linux") {
if (commandExists("apt-get")) {
- await runInstallCommand(wc, "pkexec", [
+ const escalator = resolveLinuxEscalationCommand();
+ if (!escalator) {
+ throw new Error(
+ "Neither pkexec nor sudo is available to run apt-get install."
+ );
+ }
+
+ await runInstallCommand(wc, escalator, [
+ "apt-get",
+ "update",
+ ]);
+ await runInstallCommand(wc, escalator, [
"apt-get",
"install",
"-y",
@@ -218,17 +257,30 @@ export function setupSetupInstallHandlers() {
]);
} else {
throw new Error(
- "apt-get is unavailable. Install ImageMagick manually from the official download page."
+ "apt-get is unavailable. Install ImageMagick manually using your package manager."
);
}
} else if (process.platform === "darwin") {
- if (commandExists("brew")) {
- await runInstallCommand(wc, "brew", ["install", "imagemagick"]);
- } else {
+ let brewCommand = resolveBrewCommand();
+ if (!brewCommand) {
+ sendImageMagickLog(
+ wc,
+ "info",
+ "Homebrew not found. Installing Homebrew first..."
+ );
+ const installHomebrewCommand =
+ 'NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"';
+ await runInstallCommand(wc, "/bin/bash", ["-c", installHomebrewCommand]);
+ brewCommand = resolveBrewCommand();
+ }
+
+ if (!brewCommand) {
throw new Error(
- "Homebrew is not installed. Install ImageMagick manually from the official download page."
+ "Homebrew installation completed, but brew was not found on PATH."
);
}
+
+ await runInstallCommand(wc, brewCommand, ["install", "imagemagick"]);
} else if (process.platform === "win32") {
if (commandExists("choco", ["-v"])) {
await runInstallCommand(wc, "choco", [
@@ -238,7 +290,7 @@ export function setupSetupInstallHandlers() {
]);
} else {
throw new Error(
- "Chocolatey is not installed. Install ImageMagick manually from the official download page."
+ "Chocolatey is not installed. Falling back to direct installer download."
);
}
} else {
@@ -253,14 +305,21 @@ export function setupSetupInstallHandlers() {
const message =
error instanceof Error ? error.message : "ImageMagick install failed";
sendImageMagickLog(wc, "error", message);
+ logManualImageMagickCommands(wc);
const downloadUrl = getImageMagickDownloadUrl();
sendImageMagickLog(
wc,
"info",
- `Falling back to manual install page: ${downloadUrl}`
+ `Opening manual install link: ${downloadUrl}`
);
await shell.openExternal(downloadUrl);
- return { ok: true };
+ sendImageMagickProgress(
+ wc,
+ "error",
+ undefined,
+ "Finish manual installation, then click Retry."
+ );
+ return { ok: false, error: message };
}
}
);
diff --git a/electron/app/utils/imagemagick-check.ts b/electron/app/utils/imagemagick-check.ts
index 6495c7f8..38d01be5 100644
--- a/electron/app/utils/imagemagick-check.ts
+++ b/electron/app/utils/imagemagick-check.ts
@@ -18,10 +18,34 @@ export function isImageMagickInstalled(): boolean {
export function getImageMagickDownloadUrl(): string {
if (process.platform === "win32") {
- return "https://imagemagick.org/script/download.php#windows";
+ return "https://imagemagick.org/archive/binaries/ImageMagick-7.1.2-18-Q16-HDRI-x64-dll.exe";
}
if (process.platform === "darwin") {
- return "https://imagemagick.org/script/download.php#macosx";
+ return "https://brew.sh/";
}
return "https://imagemagick.org/script/download.php#linux";
}
+
+export function getImageMagickManualInstallCommands(): string[] {
+ if (process.platform === "win32") {
+ return [
+ "Download and run the installer:",
+ getImageMagickDownloadUrl(),
+ ];
+ }
+
+ if (process.platform === "darwin") {
+ return [
+ "Install Homebrew:",
+ '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"',
+ "Install ImageMagick:",
+ "brew install imagemagick",
+ ];
+ }
+
+ return [
+ "Install ImageMagick:",
+ "sudo apt-get update",
+ "sudo apt-get install -y imagemagick",
+ ];
+}
diff --git a/electron/resources/document-extraction/liteparse_runner.mjs b/electron/resources/document-extraction/liteparse_runner.mjs
index 6793610a..d7b68195 100644
--- a/electron/resources/document-extraction/liteparse_runner.mjs
+++ b/electron/resources/document-extraction/liteparse_runner.mjs
@@ -70,7 +70,7 @@ const ocrEnabled = parseBool(readArg("--ocr-enabled"), true);
const dpi = toNumber(readArg("--dpi"), 150, 72, 600);
const numWorkers = toNumber(
readArg("--num-workers"),
- Math.max(os.cpus().length - 4, 1),
+ Math.max(os.cpus().length - 2, 1),
1,
64
);
diff --git a/electron/resources/ui/setup-installer/index.html b/electron/resources/ui/setup-installer/index.html
index 5ff1397b..2d871226 100644
--- a/electron/resources/ui/setup-installer/index.html
+++ b/electron/resources/ui/setup-installer/index.html
@@ -288,7 +288,7 @@
? 'Presenton uses LibreOffice to generate custom templates from PPTX files.'
: step === 'chrome'
? 'Presenton uses Chromium for export and slide rendering. Download it now (~150 MB).'
- : 'Presenton uses ImageMagick for OCR/document conversion support. We will try automatic installation first, then open the download page if package manager tools are unavailable.';
+ : 'Presenton uses ImageMagick for OCR/document conversion support. Linux uses apt, macOS installs Homebrew first (if needed) and then runs brew install imagemagick, and Windows uses Chocolatey with a direct installer fallback.';
document.getElementById('btn-install').onclick = () => startInstall(step);
document.getElementById('btn-skip').onclick = () => handleSkip();
showState('prompt');
@@ -315,8 +315,17 @@
});
} else {
document.getElementById('dl-heading').textContent = 'Installing ImageMagick';
- document.getElementById('dl-phase').textContent = 'Automatic install (apt/brew/choco) with fallback to manual download';
- window.setupInstaller.installImageMagick().then(() => {
+ document.getElementById('dl-phase').textContent = 'Linux: apt-get | macOS: Homebrew + brew install | Windows: choco or direct installer';
+ window.setupInstaller.installImageMagick().then((installResult) => {
+ if (!installResult || !installResult.ok) {
+ if (currentStep !== 'imagemagick') return;
+ document.getElementById('err-msg').textContent = installResult?.error || 'ImageMagick installation needs manual completion. Follow the shown commands and then click Retry.';
+ showState('error');
+ document.getElementById('btn-retry').onclick = () => startInstall('imagemagick');
+ document.getElementById('btn-skip-error').onclick = () => nextOrDone();
+ return;
+ }
+
window.setupInstaller.checkImageMagick().then(res => {
if (!res.ok && currentStep === 'imagemagick') {
document.getElementById('err-msg').textContent = res.error || 'ImageMagick is not installed yet.';