Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const nextConfig = {
experimental: {
// better-sqlite3 is a native module, keep it out of the webpack bundle
serverComponentsExternalPackages: ["better-sqlite3"],
// don't reuse a cached dynamic page when navigating back, so the dashboard
// always reflects projects you just created or deleted
staleTimes: { dynamic: 0 },
},
};

Expand Down
4 changes: 2 additions & 2 deletions src/app/editor/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ export default function EditorPage({ params }: { params: { id: string } }) {
};
}, [id, compile]);

// open the log automatically when a compile fails
// open the log automatically when a compile fails or has errors
useEffect(() => {
if (status === "error") setShowLog(true);
if (status === "error" || status === "warning") setShowLog(true);
}, [status]);

// flush pending work when leaving the editor
Expand Down
17 changes: 9 additions & 8 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
--color-text: 15 18 23;
--color-text-muted: 92 99 112;

/* default accent is the LaTeX green (#50C878) */
--color-accent: 80 200 120;
/* default accent is the LaTeX green (#14B84B) */
--color-accent: 20 184 75;
--color-accent-fg: 255 255 255;
--color-accent-soft: 224 245 233;
--color-accent-soft: 222 246 230;

--color-danger: 225 76 76;
--color-warning: 217 119 6;

--font-sans: "Inter", system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
Expand All @@ -28,14 +29,14 @@
/* Per-engine accent. Set data-engine on a wrapper and everything tinted with
the accent token follows along. Typst lands here in a later version. */
[data-engine="latex"] {
--color-accent: 80 200 120;
--color-accent-soft: 224 245 233;
--color-accent: 20 184 75;
--color-accent-soft: 222 246 230;
}

[data-engine="typst"] {
/* placeholder blue until Typst support arrives */
--color-accent: 56 152 236;
--color-accent-soft: 222 238 252;
/* ready for when Typst support arrives */
--color-accent: 0 122 255;
--color-accent-soft: 220 235 255;
}

/* Dark theme tokens live here once we ship dark mode. Toggling will be a matter
Expand Down
8 changes: 7 additions & 1 deletion src/components/dashboard/ProjectsGallery.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { useState } from "react";
import { useEffect, useState } from "react";
import { ConfirmDialog } from "@/components/ui/ConfirmDialog";
import type { Project } from "@/lib/projects";
import { ProjectCard } from "./ProjectCard";
Expand All @@ -26,6 +26,12 @@ export function ProjectsGallery({
const [showAll, setShowAll] = useState(false);
const [pendingDelete, setPendingDelete] = useState<Project | null>(null);

// when the server sends a fresh list (e.g. after navigating back from a
// project), adopt it so deleted or newly created projects are reflected
useEffect(() => {
setProjects(initialProjects);
}, [initialProjects]);

async function togglePin(project: Project) {
const pinned = !project.pinned;
setProjects((prev) =>
Expand Down
2 changes: 2 additions & 0 deletions src/components/editor/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ function statusLabel(
: "Compiled",
className: "text-accent",
};
case "warning":
return { text: "Compiled with errors", className: "text-warning" };
case "error":
return { text: "Compile errors", className: "text-danger" };
case "empty":
Expand Down
9 changes: 7 additions & 2 deletions src/components/editor/useCompile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ export type CompileStatus =
| "idle"
| "running"
| "success"
// a PDF was produced but the log has errors (still worth showing the PDF)
| "warning"
| "error"
| "empty";

Expand All @@ -27,6 +29,7 @@ export function useCompile(projectId: string) {
setLog("");

let success = false;
let pdfProduced = false;
let empty = false;
let duration: number | null = null;

Expand Down Expand Up @@ -63,6 +66,7 @@ export function useCompile(projectId: string) {
else if (event.type === "error") setLog((prev) => prev + event.message);
else if (event.type === "done") {
success = event.success;
pdfProduced = event.pdfProduced;
empty = event.empty;
duration = event.durationMs;
}
Expand All @@ -74,9 +78,10 @@ export function useCompile(projectId: string) {
}

setDurationMs(duration);
if (success) {
setStatus("success");
// show the PDF whenever one was produced, even if the log has errors
if (pdfProduced) {
setPdfVersion((v) => v + 1);
setStatus(success ? "success" : "warning");
} else if (empty) {
setStatus("empty");
} else {
Expand Down
24 changes: 22 additions & 2 deletions src/lib/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { projectDir, projectOutputDir } from "./paths";
import { mainSourcePath, outputPdfPath, ensureProjectDirs } from "./storage";

export interface CompileResult {
// the compiler exited cleanly with a PDF
success: boolean;
// a PDF came out even if there were errors. LaTeX often still produces one,
// and we'd rather show it (with the errors) than hide it, like Overleaf does.
pdfProduced: boolean;
// the document compiled cleanly but had nothing to typeset (an empty body).
// not a failure, just nothing to preview yet.
empty: boolean;
Expand Down Expand Up @@ -69,14 +73,28 @@ export async function runCompile(
} catch {
const message = `Unknown engine: ${engineId}\n`;
onLog?.(message);
return { success: false, empty: false, log: message, durationMs: 0, passes: 0 };
return {
success: false,
pdfProduced: false,
empty: false,
log: message,
durationMs: 0,
passes: 0,
};
}

const mainPath = mainSourcePath(projectId, engine);
if (!fs.existsSync(mainPath)) {
const message = `No ${engine.mainFileName} found for this project.\n`;
onLog?.(message);
return { success: false, empty: false, log: message, durationMs: 0, passes: 0 };
return {
success: false,
pdfProduced: false,
empty: false,
log: message,
durationMs: 0,
passes: 0,
};
}

ensureProjectDirs(projectId);
Expand Down Expand Up @@ -106,6 +124,7 @@ export async function runCompile(
const empty = !pdfExists && /no pages of output/i.test(log);
return {
success: lastCode === 0 && pdfExists,
pdfProduced: pdfExists,
empty,
log,
durationMs: Date.now() - start,
Expand Down Expand Up @@ -138,6 +157,7 @@ export function compileStream(
send({
type: "done",
success: result.success,
pdfProduced: result.pdfProduced,
empty: result.empty,
durationMs: result.durationMs,
passes: result.passes,
Expand Down
1 change: 1 addition & 0 deletions tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const config: Config = {
soft: withAlpha("--color-accent-soft"),
},
danger: withAlpha("--color-danger"),
warning: withAlpha("--color-warning"),
},
fontFamily: {
sans: ["var(--font-sans)", "system-ui", "sans-serif"],
Expand Down
Loading