Merge pull request #107 from delibae/fix/multi-file-pdf-preview

fix: resolve PDF preview to correct root file in multi-file LaTeX projects
This commit is contained in:
Hanjin Bae 2026-03-30 12:09:23 +09:00 committed by GitHub
commit 7e5e395e30
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 40 additions and 14 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@claude-prism/desktop",
"version": "1.1.1",
"version": "1.1.2",
"private": true,
"type": "module",
"scripts": {

View file

@ -2,7 +2,7 @@
"$schema": "https://raw.githubusercontent.com/nicegui-unofficial/nicegui-tauri-template/main/src-tauri/tauri.conf-v2-schema.json",
"identifier": "com.claude-prism.desktop",
"productName": "ClaudePrism",
"version": "1.1.1",
"version": "1.1.2",
"build": {
"beforeDevCommand": "pnpm dev",
"beforeBuildCommand": "pnpm build",

View file

@ -5,6 +5,8 @@ import { createLogger } from "@/lib/debug/logger";
const log = createLogger("latex");
/** Resolve which file to compile and the root ID for caching.
* resolveTexRoot now handles \documentclass detection and main.tex fallback,
* so the only remaining fallback here is for projects with no .tex files.
* Returns `null` when the project has no compilable .tex file. */
export function resolveCompileTarget(
activeFileId: string,
@ -15,19 +17,11 @@ export function resolveCompileTarget(
if (rootEntry?.type === "tex") {
return { rootId, targetPath: rootEntry.relativePath };
}
// Fallback: look for any well-known root tex file
const fallback = files.find(
(f) => f.name === "main.tex" || f.name === "document.tex",
);
if (fallback) {
return { rootId: fallback.id, targetPath: fallback.relativePath };
}
// Final fallback: use the first available .tex file in the project
// No .tex file exists — cannot compile
const anyTex = files.find((f) => f.type === "tex");
if (anyTex) {
return { rootId: anyTex.id, targetPath: anyTex.relativePath };
}
// No .tex file exists — cannot compile
return null;
}

View file

@ -148,25 +148,57 @@ function getActiveFile(state: { files: ProjectFile[]; activeFileId: string }) {
/**
* Resolve the root .tex file for compilation.
* Parses `% !TEX root = <file>` magic comment from the first 20 lines.
* Returns the root file's ID (relativePath), or the input fileId if no root is specified.
*
* Priority order:
* 1. `% !TEX root = <file>` magic comment in the first 20 lines of the active file
* 2. The file itself, if it contains `\documentclass`
* 3. `main.tex` or `document.tex` that contains `\documentclass`
* 4. Any other .tex file in the project that contains `\documentclass`
* 5. Fallback: the active file itself
*/
export function resolveTexRoot(fileId: string, files: ProjectFile[]): string {
const file = files.find((f) => f.id === fileId);
if (!file || file.type !== "tex" || !file.content) return fileId;
// 1. Check for % !TEX root magic comment
const lines = file.content.split("\n").slice(0, 20);
for (const line of lines) {
const match = line.match(/^%\s*!TEX\s+root\s*=\s*(.+)/i);
if (match) {
const rootPath = match[1].trim();
// Try matching by relative path first, then by filename
const target =
files.find((f) => f.relativePath === rootPath) ??
files.find((f) => f.name === rootPath);
if (target) return target.id;
}
}
// 2. If the current file contains \documentclass, it is a root file
if (/\\documentclass[\s{[]/.test(file.content)) {
return fileId;
}
// 3. Look for main.tex or document.tex with \documentclass
const wellKnown = files.find(
(f) =>
(f.name === "main.tex" || f.name === "document.tex") &&
f.type === "tex" &&
f.content &&
/\\documentclass[\s{[]/.test(f.content),
);
if (wellKnown) return wellKnown.id;
// 4. Any .tex file with \documentclass
const anyRoot = files.find(
(f) =>
f.type === "tex" &&
f.id !== fileId &&
f.content &&
/\\documentclass[\s{[]/.test(f.content),
);
if (anyRoot) return anyRoot.id;
// 5. Fallback: the active file itself
return fileId;
}