From cc4ad9d668ef9d308e34c9fbf6eed2a24cc860aa Mon Sep 17 00:00:00 2001 From: delibae Date: Mon, 30 Mar 2026 12:08:46 +0900 Subject: [PATCH 1/2] fix: resolve PDF preview to correct root file in multi-file LaTeX projects resolveTexRoot now detects \documentclass to find the actual root file instead of treating the active sub-file as the compilation target. This fixes incorrect PDF preview when editing non-main .tex files. Closes #106 Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/desktop/src/lib/latex-compiler.ts | 12 ++----- apps/desktop/src/stores/document-store.ts | 38 +++++++++++++++++++++-- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/apps/desktop/src/lib/latex-compiler.ts b/apps/desktop/src/lib/latex-compiler.ts index 4333a07..dc2f230 100644 --- a/apps/desktop/src/lib/latex-compiler.ts +++ b/apps/desktop/src/lib/latex-compiler.ts @@ -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; } diff --git a/apps/desktop/src/stores/document-store.ts b/apps/desktop/src/stores/document-store.ts index dfe74a6..32cecc8 100644 --- a/apps/desktop/src/stores/document-store.ts +++ b/apps/desktop/src/stores/document-store.ts @@ -148,25 +148,57 @@ function getActiveFile(state: { files: ProjectFile[]; activeFileId: string }) { /** * Resolve the root .tex file for compilation. - * Parses `% !TEX root = ` 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 = ` 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; } From f69de4ef123cb75665f19f85f908ef0aad04989f Mon Sep 17 00:00:00 2001 From: delibae Date: Mon, 30 Mar 2026 12:09:01 +0900 Subject: [PATCH 2/2] chore: bump version to 1.1.2 Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/desktop/package.json | 2 +- apps/desktop/src-tauri/tauri.conf.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 6f3df54..4a26ba6 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,6 +1,6 @@ { "name": "@claude-prism/desktop", - "version": "1.1.1", + "version": "1.1.2", "private": true, "type": "module", "scripts": { diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index 61a68b1..7784ec0 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -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",