Skip to content

Commit 6645d55

Browse files
Merge pull request #13950 from quarto-dev/feature/typst-books
Typst Books Merge commit because you can’t rebase subtree changes!
2 parents e8fe22f + eb11bda commit 6645d55

306 files changed

Lines changed: 22511 additions & 294 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/performance-check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
pushd package/src
3939
4040
bundle_start=$(date +%s.%3N)
41-
./quarto-bld prepare-dist
41+
./quarto-bld prepare-dist --set-version $(cat ../../version.txt)
4242
bundle_end=$(date +%s.%3N)
4343
bundle_elapsed=$(printf '%.3f\n' $(echo "scale=3; $bundle_end - $bundle_start" | bc))
4444

.github/workflows/test-bundle.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
shell: bash
3434
run: |
3535
pushd package/src
36-
./quarto-bld prepare-dist
36+
./quarto-bld prepare-dist --set-version $(cat ../../version.txt)
3737
mv ../../src/quarto.ts ../../src/quarto1.ts
3838
pushd ../pkg-working/bin
3939
./quarto check

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,6 @@ tools/sass-variable-explainer/_publish.yml
5353

5454
# generated by tests
5555
tests/docs/callouts.pdf
56+
57+
# quarto cache directories (populated at render time)
58+
.quarto

news/changelog-1.9.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ All changes included in 1.9:
5454
- Two-column layout now uses `set page(columns:)` instead of `columns()` function, fixing compatibility with landscape sections.
5555
- Title block now properly spans both columns in multi-column layouts.
5656
- ([#13870](https://github.com/quarto-dev/quarto-cli/issues/13870)): Add support for `alt` attribute on cross-referenced equations for improved accessibility. (author: @mcanouil)
57+
- ([#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).
58+
- ([#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.
5759

5860
### `pdf`
5961

package/src/common/prepare-dist.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,6 @@ export async function prepareDist(
164164
inlineFilters(config);
165165
info("");
166166

167-
// Appease the bundler testing by patching the bad version from `configuration`
168-
if (config.version.split(".").length === 2) {
169-
config.version = `${config.version}.1`;
170-
}
171-
172167
// Write a version file to share
173168
info(`Writing version: ${config.version}`);
174169
Deno.writeTextFileSync(

src/command/dev-call/pull-git-subtree/cmd.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ const SUBTREES: SubtreeConfig[] = [
2424
remoteUrl: "https://github.com/gordonwoodhull/quarto-julia-engine.git",
2525
remoteBranch: "main",
2626
},
27-
// Add more subtrees here as needed
27+
{
28+
name: "orange-book",
29+
prefix: "src/resources/extension-subtrees/orange-book",
30+
remoteUrl: "https://github.com/quarto-ext/orange-book.git",
31+
remoteBranch: "main",
32+
},
2833
];
2934

3035
async function findLastSplit(
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
destination = "src/resources/formats/typst/packages"
22
discover = ["src/resources/formats/typst/pandoc/quarto"]
3+
4+
[preview]
5+
fontawesome = "0.5.0"
6+
theorion = "0.4.1"

src/command/render/output-typst.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ import {
1212
safeRemoveSync,
1313
} from "../../deno_ral/fs.ts";
1414
import {
15+
builtinSubtreeExtensions,
1516
inputExtensionDirs,
1617
readExtensions,
18+
readSubtreeExtensions,
1719
} from "../../extension/extension.ts";
1820
import { projectScratchPath } from "../../project/project-scratch.ts";
1921
import { resourcePath } from "../../core/resources.ts";
@@ -62,8 +64,12 @@ async function stageTypstPackages(
6264

6365
// 2. Add packages from extensions (can override built-in)
6466
const extensionDirs = inputExtensionDirs(input, projectDir);
67+
const subtreePath = builtinSubtreeExtensions();
6568
for (const extDir of extensionDirs) {
66-
const extensions = await readExtensions(extDir);
69+
// Use readSubtreeExtensions for subtree directory, readExtensions for others
70+
const extensions = extDir === subtreePath
71+
? await readSubtreeExtensions(extDir)
72+
: await readExtensions(extDir);
6773
for (const ext of extensions) {
6874
const packagesDir = join(ext.path, "typst/packages");
6975
if (existsSync(packagesDir)) {

src/command/render/render-contexts.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -676,11 +676,24 @@ const readExtensionFormat = async (
676676
extensionContext: ExtensionContext,
677677
project?: ProjectContext,
678678
) => {
679+
// Determine effective extension - use default for certain project/format combinations
680+
let effectiveExtension = formatDesc.extension;
681+
682+
// For book projects with typst format and no explicit extension,
683+
// use orange-book as the default typst book template
684+
if (
685+
!effectiveExtension &&
686+
formatDesc.baseFormat === "typst" &&
687+
project?.config?.project?.[kProjectType] === "book"
688+
) {
689+
effectiveExtension = "orange-book";
690+
}
691+
679692
// Read the format file and populate this
680-
if (formatDesc.extension) {
693+
if (effectiveExtension) {
681694
// Find the yaml file
682695
const extension = await extensionContext.extension(
683-
formatDesc.extension,
696+
effectiveExtension,
684697
file,
685698
project?.config,
686699
project?.dir,
@@ -696,7 +709,7 @@ const readExtensionFormat = async (
696709
(extensionFormat[fmtTarget] || extensionFormat[formatDesc.baseFormat] ||
697710
{}) as Metadata;
698711
extensionMetadata[kExtensionName] = extensionMetadata[kExtensionName] ||
699-
formatDesc.extension;
712+
effectiveExtension;
700713

701714
const formats = await resolveFormatsFromMetadata(
702715
extensionMetadata,
@@ -707,7 +720,7 @@ const readExtensionFormat = async (
707720
return formats;
708721
} else {
709722
throw new Error(
710-
`No valid format ${formatDesc.baseFormat} is provided by the extension ${formatDesc.extension}`,
723+
`No valid format ${formatDesc.baseFormat} is provided by the extension ${effectiveExtension}`,
711724
);
712725
}
713726
} else {

src/extension/extension.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,9 @@ export function filterExtensions(
279279

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

287287
const topLevelDirs = safeExistsSync(subtreeDir) &&
@@ -303,7 +303,7 @@ const readSubtreeExtensions = async (
303303
}
304304

305305
return extensions;
306-
};
306+
}
307307

308308
// Loads all extensions for a given input
309309
// (note this needs to be sure to return copies from
@@ -628,6 +628,24 @@ export function discoverExtensionPath(
628628
return builtinExtensionDir;
629629
}
630630

631+
// check for built-in subtree extensions (pattern: extension-subtrees/*/\_extensions/name)
632+
const subtreePath = builtinSubtreeExtensions();
633+
if (safeExistsSync(subtreePath)) {
634+
for (const topLevelDir of Deno.readDirSync(subtreePath)) {
635+
if (!topLevelDir.isDirectory) continue;
636+
const subtreeExtDir = join(subtreePath, topLevelDir.name, kExtensionDir);
637+
if (safeExistsSync(subtreeExtDir)) {
638+
const subtreeExtensionDir = findExtensionDir(
639+
subtreeExtDir,
640+
extensionDirGlobs,
641+
);
642+
if (subtreeExtensionDir) {
643+
return subtreeExtensionDir;
644+
}
645+
}
646+
}
647+
}
648+
631649
// Start in the source directory
632650
const sourceDir = Deno.statSync(input).isDirectory ? input : dirname(input);
633651
const sourceDirAbs = normalizePath(sourceDir);
@@ -661,7 +679,7 @@ function builtinExtensions() {
661679
}
662680

663681
// Path for built-in subtree extensions
664-
function builtinSubtreeExtensions() {
682+
export function builtinSubtreeExtensions() {
665683
return resourcePath("extension-subtrees");
666684
}
667685

0 commit comments

Comments
 (0)