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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/performance-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
pushd package/src
bundle_start=$(date +%s.%3N)
./quarto-bld prepare-dist
./quarto-bld prepare-dist --set-version $(cat ../../version.txt)
bundle_end=$(date +%s.%3N)
bundle_elapsed=$(printf '%.3f\n' $(echo "scale=3; $bundle_end - $bundle_start" | bc))
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test-bundle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
shell: bash
run: |
pushd package/src
./quarto-bld prepare-dist
./quarto-bld prepare-dist --set-version $(cat ../../version.txt)
mv ../../src/quarto.ts ../../src/quarto1.ts
pushd ../pkg-working/bin
./quarto check
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@ tools/sass-variable-explainer/_publish.yml

# generated by tests
tests/docs/callouts.pdf

# quarto cache directories (populated at render time)
.quarto
2 changes: 2 additions & 0 deletions news/changelog-1.9.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ All changes included in 1.9:
- Two-column layout now uses `set page(columns:)` instead of `columns()` function, fixing compatibility with landscape sections.
- Title block now properly spans both columns in multi-column layouts.
- ([#13870](https://github.com/quarto-dev/quarto-cli/issues/13870)): Add support for `alt` attribute on cross-referenced equations for improved accessibility. (author: @mcanouil)
- ([#13950](https://github.com/quarto-dev/quarto-cli/pull/13950)): Replace ctheorems with theorion package for theorem environments. Add `theorem-appearance` option to control styling: `simple` (default, classic LaTeX style), `fancy` (colored boxes with brand colors), `clouds` (rounded backgrounds), or `rainbow` (colored start border and colored title).
- ([#13954](https://github.com/quarto-dev/quarto-cli/issues/13954)): Add support for Typst book projects via format extensions. Quarto now bundles the `orange-book` extension which provides a textbook-style format with chapter numbering, cross-references, and professional styling. Book projects with `format: typst` automatically use this extension.

### `pdf`

Expand Down
5 changes: 0 additions & 5 deletions package/src/common/prepare-dist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,6 @@ export async function prepareDist(
inlineFilters(config);
info("");

// Appease the bundler testing by patching the bad version from `configuration`
if (config.version.split(".").length === 2) {
config.version = `${config.version}.1`;
}

// Write a version file to share
info(`Writing version: ${config.version}`);
Deno.writeTextFileSync(
Expand Down
7 changes: 6 additions & 1 deletion src/command/dev-call/pull-git-subtree/cmd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ const SUBTREES: SubtreeConfig[] = [
remoteUrl: "https://github.com/gordonwoodhull/quarto-julia-engine.git",
remoteBranch: "main",
},
// Add more subtrees here as needed
{
name: "orange-book",
prefix: "src/resources/extension-subtrees/orange-book",
remoteUrl: "https://github.com/quarto-ext/orange-book.git",
remoteBranch: "main",
},
];

async function findLastSplit(
Expand Down
4 changes: 4 additions & 0 deletions src/command/dev-call/typst-gather/typst-gather.toml
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
destination = "src/resources/formats/typst/packages"
discover = ["src/resources/formats/typst/pandoc/quarto"]

[preview]
fontawesome = "0.5.0"
theorion = "0.4.1"
8 changes: 7 additions & 1 deletion src/command/render/output-typst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import {
safeRemoveSync,
} from "../../deno_ral/fs.ts";
import {
builtinSubtreeExtensions,
inputExtensionDirs,
readExtensions,
readSubtreeExtensions,
} from "../../extension/extension.ts";
import { projectScratchPath } from "../../project/project-scratch.ts";
import { resourcePath } from "../../core/resources.ts";
Expand Down Expand Up @@ -62,8 +64,12 @@ async function stageTypstPackages(

// 2. Add packages from extensions (can override built-in)
const extensionDirs = inputExtensionDirs(input, projectDir);
const subtreePath = builtinSubtreeExtensions();
for (const extDir of extensionDirs) {
const extensions = await readExtensions(extDir);
// Use readSubtreeExtensions for subtree directory, readExtensions for others
const extensions = extDir === subtreePath
? await readSubtreeExtensions(extDir)
: await readExtensions(extDir);
for (const ext of extensions) {
const packagesDir = join(ext.path, "typst/packages");
if (existsSync(packagesDir)) {
Expand Down
21 changes: 17 additions & 4 deletions src/command/render/render-contexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -676,11 +676,24 @@ const readExtensionFormat = async (
extensionContext: ExtensionContext,
project?: ProjectContext,
) => {
// Determine effective extension - use default for certain project/format combinations
let effectiveExtension = formatDesc.extension;

// For book projects with typst format and no explicit extension,
// use orange-book as the default typst book template
if (
!effectiveExtension &&
formatDesc.baseFormat === "typst" &&
project?.config?.project?.[kProjectType] === "book"
) {
effectiveExtension = "orange-book";
}

// Read the format file and populate this
if (formatDesc.extension) {
if (effectiveExtension) {
// Find the yaml file
const extension = await extensionContext.extension(
formatDesc.extension,
effectiveExtension,
file,
project?.config,
project?.dir,
Expand All @@ -696,7 +709,7 @@ const readExtensionFormat = async (
(extensionFormat[fmtTarget] || extensionFormat[formatDesc.baseFormat] ||
{}) as Metadata;
extensionMetadata[kExtensionName] = extensionMetadata[kExtensionName] ||
formatDesc.extension;
effectiveExtension;

const formats = await resolveFormatsFromMetadata(
extensionMetadata,
Expand All @@ -707,7 +720,7 @@ const readExtensionFormat = async (
return formats;
} else {
throw new Error(
`No valid format ${formatDesc.baseFormat} is provided by the extension ${formatDesc.extension}`,
`No valid format ${formatDesc.baseFormat} is provided by the extension ${effectiveExtension}`,
);
}
} else {
Expand Down
26 changes: 22 additions & 4 deletions src/extension/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,9 @@ export function filterExtensions(

// Read git subtree extensions (pattern 3 only)
// Looks for top-level directories containing _extensions/ subdirectories
const readSubtreeExtensions = async (
export async function readSubtreeExtensions(
subtreeDir: string,
): Promise<Extension[]> => {
): Promise<Extension[]> {
const extensions: Extension[] = [];

const topLevelDirs = safeExistsSync(subtreeDir) &&
Expand All @@ -303,7 +303,7 @@ const readSubtreeExtensions = async (
}

return extensions;
};
}

// Loads all extensions for a given input
// (note this needs to be sure to return copies from
Expand Down Expand Up @@ -628,6 +628,24 @@ export function discoverExtensionPath(
return builtinExtensionDir;
}

// check for built-in subtree extensions (pattern: extension-subtrees/*/\_extensions/name)
const subtreePath = builtinSubtreeExtensions();
if (safeExistsSync(subtreePath)) {
for (const topLevelDir of Deno.readDirSync(subtreePath)) {
if (!topLevelDir.isDirectory) continue;
const subtreeExtDir = join(subtreePath, topLevelDir.name, kExtensionDir);
if (safeExistsSync(subtreeExtDir)) {
const subtreeExtensionDir = findExtensionDir(
subtreeExtDir,
extensionDirGlobs,
);
if (subtreeExtensionDir) {
return subtreeExtensionDir;
}
}
}
}

// Start in the source directory
const sourceDir = Deno.statSync(input).isDirectory ? input : dirname(input);
const sourceDirAbs = normalizePath(sourceDir);
Expand Down Expand Up @@ -661,7 +679,7 @@ function builtinExtensions() {
}

// Path for built-in subtree extensions
function builtinSubtreeExtensions() {
export function builtinSubtreeExtensions() {
return resourcePath("extension-subtrees");
}

Expand Down
15 changes: 15 additions & 0 deletions src/format/typst/format-typst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import { join } from "../../deno_ral/path.ts";

import { RenderServices } from "../../command/render/types.ts";
import { ProjectContext } from "../../project/types.ts";
import { BookExtension } from "../../project/types/book/book-shared.ts";
import {
kCiteproc,
kColumns,
Expand Down Expand Up @@ -38,6 +40,11 @@ import {
import { fillLogoPaths, resolveLogo } from "../../core/brand/brand.ts";
import { LogoLightDarkSpecifierPathOptional } from "../../resources/types/zod/schema-types.ts";

const typstBookExtension: BookExtension = {
selfContainedOutput: true,
// multiFile defaults to false (single-file book)
};

export function typstFormat(): Format {
return createFormat("Typst", "pdf", {
execute: {
Expand All @@ -51,6 +58,9 @@ export function typstFormat(): Format {
[kWrap]: "none",
[kCiteproc]: false,
},
extensions: {
book: typstBookExtension,
},
resolveFormat: typstResolveFormat,
formatExtras: async (
_input: string,
Expand All @@ -59,6 +69,8 @@ export function typstFormat(): Format {
format: Format,
_libDir: string,
_services: RenderServices,
_offset?: string,
_project?: ProjectContext,
): Promise<FormatExtras> => {
const pandoc: FormatPandoc = {};
const metadata: Metadata = {};
Expand Down Expand Up @@ -106,10 +118,13 @@ export function typstFormat(): Format {
}

// Provide a template and partials
// For Typst books, a book extension overrides these partials
const templateDir = formatResourcePath("typst", join("pandoc", "quarto"));

const templateContext = {
template: join(templateDir, "template.typ"),
partials: [
"numbering.typ",
"definitions.typ",
"typst-template.typ",
"page.typ",
Expand Down
12 changes: 12 additions & 0 deletions src/project/types/book/book.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
isEpubOutput,
isHtmlOutput,
isLatexOutput,
isTypstOutput,
} from "../../../config/format.ts";
import { PandocFlags } from "../../../config/types.ts";
import {
Expand Down Expand Up @@ -256,6 +257,17 @@ export const bookProjectType: ProjectType = {
},
},
);
} else if (isTypstOutput(format.pandoc)) {
// Typst book: use chapter divisions, disable Quarto TOC (orange-book generates its own)
extras = mergeConfigs(
extras,
{
pandoc: {
[kTopLevelDivision]: "chapter",
[kToc]: false,
},
},
);
}

// return
Expand Down
Loading
Loading