mirror of
https://github.com/priyanshujain/margin.git
synced 2026-10-02 19:17:03 +00:00
changes
This commit is contained in:
1 parent
9e714c8997
commit
f47e822497
11 files changed
+226
-47
No files matched your search
+28
-9
@@ -1,6 +1,6 @@
|
||||
import type { JSONContent } from "@tiptap/core";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { type Book, type FigurePlacement, TRIM_DIMS, FIGURE_WIDTH, bodyNumber, chapterKind, isResizablePlacement } from "../model/book";
|
||||
import { type Book, type FigurePlacement, TRIM_DIMS, FIGURE_WIDTH, bodyNumber, chapterKind, isResizablePlacement, partNumber, partRoman } from "../model/book";
|
||||
import { fontStack, fontsUsed } from "../model/fonts";
|
||||
|
||||
export interface EpubFile {
|
||||
@@ -338,9 +338,12 @@ function contentOpf(
|
||||
function navXhtml(book: Book): string {
|
||||
const items = book.chapters
|
||||
.map((chapter, i) => {
|
||||
if (chapter.noTitle && chapterKind(chapter) !== "body") return "";
|
||||
const fallback = chapterKind(chapter) === "body" ? `Chapter ${bodyNumber(book.chapters, i)}` : "Untitled";
|
||||
const label = chapter.noTitle ? fallback : chapter.title || fallback;
|
||||
const kind = chapterKind(chapter);
|
||||
if (chapter.noTitle && kind !== "body" && kind !== "part") return "";
|
||||
const partLabel = `Part ${partRoman(partNumber(book.chapters, i) ?? 0)}`;
|
||||
const fallback = kind === "body" ? `Chapter ${bodyNumber(book.chapters, i)}` : kind === "part" ? partLabel : "Untitled";
|
||||
const base = chapter.noTitle ? fallback : chapter.title || fallback;
|
||||
const label = kind === "part" && !chapter.noTitle && chapter.title ? `${partLabel}: ${chapter.title}` : base;
|
||||
return ` <li><a href="chapter-${i + 1}.xhtml">${esc(label)}</a></li>`;
|
||||
})
|
||||
.filter(Boolean)
|
||||
@@ -366,16 +369,20 @@ function chapterXhtml(book: Book, index: number, paths: Map<string, string>): st
|
||||
const chapter = book.chapters[index];
|
||||
const kind = chapterKind(chapter);
|
||||
const num = bodyNumber(book.chapters, index);
|
||||
const title = chapter.title || (kind === "body" ? `Chapter ${num}` : "Untitled");
|
||||
const eyebrow = kind === "body" ? `<p class="eyebrow">Chapter ${num}</p>` : "";
|
||||
const heading = chapter.noTitle ? "" : `<h1>${esc(title)}</h1>`;
|
||||
const partLabel = `Part ${partRoman(partNumber(book.chapters, index) ?? 0)}`;
|
||||
const eyebrow =
|
||||
kind === "body" ? `<p class="eyebrow">Chapter ${num}</p>` : kind === "part" ? `<p class="eyebrow">${esc(partLabel)}</p>` : "";
|
||||
const headingText = kind === "part" ? chapter.title : chapter.title || (kind === "body" ? `Chapter ${num}` : "Untitled");
|
||||
const heading = chapter.noTitle || (kind === "part" && !chapter.title) ? "" : `<h1>${esc(headingText)}</h1>`;
|
||||
const docTitle = chapter.title || (kind === "part" ? partLabel : kind === "body" ? `Chapter ${num}` : "Untitled");
|
||||
const epubType = kind === "body" ? "chapter" : kind === "part" ? "part" : kind === "front" ? "frontmatter" : "backmatter";
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" lang="${attr(book.metadata.language || "en")}">
|
||||
<head>
|
||||
<title>${esc(title)}</title>
|
||||
<title>${esc(docTitle)}</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css"/>
|
||||
</head>
|
||||
<body epub:type="${kind === "body" ? "chapter" : kind === "front" ? "frontmatter" : "backmatter"}">
|
||||
<body epub:type="${epubType}">
|
||||
<header class="chapter-opener">
|
||||
${eyebrow}
|
||||
${heading}
|
||||
@@ -435,6 +442,18 @@ h2, h3 {
|
||||
margin: 3em 0 2.2em;
|
||||
}
|
||||
|
||||
body[epub:type="part"] {
|
||||
page-break-before: always;
|
||||
}
|
||||
|
||||
body[epub:type="part"] .chapter-opener {
|
||||
margin-top: 32vh;
|
||||
}
|
||||
|
||||
body[epub:type="part"] .eyebrow {
|
||||
font-size: 0.82em;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.28em;
|
||||
|
||||
+31
-7
@@ -1,5 +1,5 @@
|
||||
import type { JSONContent } from "@tiptap/core";
|
||||
import { type Book, type FigurePlacement, type TrimSize, FIGURE_WIDTH, bodyNumber, chapterKind, isResizablePlacement } from "../model/book";
|
||||
import { type Book, type FigurePlacement, type TrimSize, FIGURE_WIDTH, bodyNumber, chapterKind, isResizablePlacement, partNumber, partRoman } from "../model/book";
|
||||
import { fontFamilyName, fontsUsed } from "../model/fonts";
|
||||
import type { ImageInput } from "../ipc";
|
||||
|
||||
@@ -166,6 +166,20 @@ function preamble(book: Book): string {
|
||||
v(1.5em)
|
||||
}
|
||||
|
||||
#let openpart(info) = {
|
||||
pagebreak(to: "odd", weak: true)
|
||||
[#metadata(info) <chap>]
|
||||
v(3.1in)
|
||||
align(center)[
|
||||
#text(font: "Hanken Grotesk", size: 10pt, weight: "semibold", tracking: 0.34em)[#upper("Part " + info.numeral)]
|
||||
#if not info.notitle and info.title != "" {
|
||||
v(0.85em)
|
||||
heading(level: 1, numbering: none, outlined: info.toc)[#info.title]
|
||||
}
|
||||
]
|
||||
v(1.5em)
|
||||
}
|
||||
|
||||
#let pageof(loc) = {
|
||||
let pat = loc.page-numbering()
|
||||
if pat == none { pat = "1" }
|
||||
@@ -186,6 +200,7 @@ function preamble(book: Book): string {
|
||||
let info = it.value
|
||||
let pg = pageof(it.location())
|
||||
let body = info.kind == "body"
|
||||
let part = info.kind == "part"
|
||||
let gap = if prev != none and prev != info.kind { 1.7em } else { 0.95em }
|
||||
prev = info.kind
|
||||
block(width: 100%, above: gap, below: 0.95em, {
|
||||
@@ -195,7 +210,9 @@ function preamble(book: Book): string {
|
||||
column-gutter: (0.65em, 1em),
|
||||
align: (right + top, left + top, right + top),
|
||||
if body { info.num } else { [] },
|
||||
if body { info.title } else { emph(info.title) },
|
||||
if part {
|
||||
strong(if info.title != "" { "Part " + info.numeral + ": " + info.title } else { "Part " + info.numeral })
|
||||
} else if body { info.title } else { emph(info.title) },
|
||||
pg,
|
||||
)]
|
||||
})
|
||||
@@ -312,16 +329,23 @@ function normTitle(title: string): string {
|
||||
|
||||
function inToc(book: Book, index: number): boolean {
|
||||
const chapter = book.chapters[index];
|
||||
if (chapter.noTitle && chapterKind(chapter) !== "body") return false;
|
||||
const kind = chapterKind(chapter);
|
||||
if (kind === "part") return true;
|
||||
if (chapter.noTitle && kind !== "body") return false;
|
||||
const title = cleanTitle(chapter.title) || "Untitled";
|
||||
return !(chapterKind(chapter) === "front" && normTitle(title) === normTitle(book.metadata.title));
|
||||
return !(kind === "front" && normTitle(title) === normTitle(book.metadata.title));
|
||||
}
|
||||
|
||||
function openerCall(book: Book, index: number): string {
|
||||
const chapter = book.chapters[index];
|
||||
const kind = chapterKind(chapter);
|
||||
const num = kind === "body" ? String(bodyNumber(book.chapters, index)) : "";
|
||||
const notitle = !!chapter.noTitle;
|
||||
if (kind === "part") {
|
||||
const numeral = partRoman(partNumber(book.chapters, index) ?? 0);
|
||||
const title = notitle ? "" : cleanTitle(chapter.title);
|
||||
return `#openpart((kind: "part", numeral: ${str(numeral)}, title: ${str(title)}, notitle: ${notitle}, toc: ${inToc(book, index)}))`;
|
||||
}
|
||||
const num = kind === "body" ? String(bodyNumber(book.chapters, index)) : "";
|
||||
const title = notitle ? "" : cleanTitle(chapter.title) || "Untitled";
|
||||
return `#openchapter((kind: ${str(kind)}, num: ${str(num)}, title: ${str(title)}, notitle: ${notitle}, toc: ${inToc(book, index)}))`;
|
||||
}
|
||||
@@ -335,13 +359,13 @@ export function bookToTypst(book: Book, paths: Map<string, string> = new Map(),
|
||||
#set page(numbering: "i")
|
||||
#counter(page).update(1)`;
|
||||
|
||||
const firstBody = book.chapters.findIndex((c) => chapterKind(c) === "body");
|
||||
const firstMain = book.chapters.findIndex((c) => chapterKind(c) === "body" || chapterKind(c) === "part");
|
||||
const firstToc = book.chapters.findIndex((_, i) => inToc(book, i));
|
||||
|
||||
const body = book.chapters
|
||||
.map((chapter, i) => {
|
||||
const toc = i === firstToc ? `#contents()\n\n` : "";
|
||||
const reset = i === firstBody ? `#set page(numbering: "1")\n#counter(page).update(1)\n` : "";
|
||||
const reset = i === firstMain ? `#set page(numbering: "1")\n#counter(page).update(1)\n` : "";
|
||||
return `${toc}${reset}${openerCall(book, i)}\n\n${chapterBody(chapter.content, paths)}`;
|
||||
})
|
||||
.join("\n\n");
|
||||
|
||||
Reference in new issue
Block a user