My extension: typst orange book added
This commit is contained in:
21
_extensions/MHellmund/julia-color/LICENSE_quarto_bookext.txt
Normal file
21
_extensions/MHellmund/julia-color/LICENSE_quarto_bookext.txt
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Posit Software, PBC
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
title: Julia-color
|
title: Julia-color
|
||||||
author: Meik Hellmund
|
author: Meik Hellmund
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
quarto-required: ">=99.9.0"
|
quarto-required: ">=1.9.0"
|
||||||
contributes:
|
contributes:
|
||||||
formats:
|
formats:
|
||||||
common:
|
common:
|
||||||
@@ -14,8 +14,18 @@ contributes:
|
|||||||
- "resources/css/juliamono.css"
|
- "resources/css/juliamono.css"
|
||||||
pdf:
|
pdf:
|
||||||
include-in-header:
|
include-in-header:
|
||||||
- "resources/tex/juliainc.tex"
|
- "resources/tex/juliainc.tex"
|
||||||
|
|
||||||
typst:
|
typst:
|
||||||
include-in-header:
|
include-in-header:
|
||||||
- "resources/typst/juliainc.typ"
|
- "resources/typst/juliainc.typ"
|
||||||
|
# the following is from
|
||||||
|
# quarto-cli/src/resources/extension-subtrees/orange-book/_extensions/orange-book
|
||||||
|
# comment out for an article
|
||||||
|
template-partials:
|
||||||
|
- numbering.typ
|
||||||
|
- page.typ
|
||||||
|
- typst-show.typ
|
||||||
|
filters:
|
||||||
|
- path: orange-book.lua
|
||||||
|
at: post-quarto
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
-- based on
|
-- based on
|
||||||
-- https://github.com/jupyter/nbconvert/blob/main/nbconvert/filters/ansi.py
|
-- https://github.com/jupyter/nbconvert/blob/main/nbconvert/filters/ansi.py
|
||||||
|
|
||||||
|
-- debug: pandoc -t native file.{typst,html,..}.md
|
||||||
|
|
||||||
local ANSI_COLORS = {
|
local ANSI_COLORS = {
|
||||||
"ansi-black",
|
"ansi-black",
|
||||||
"ansi-red",
|
"ansi-red",
|
||||||
@@ -224,18 +226,47 @@ local function HTMLconverter(fg, bg, bold, light, italic, underline, inverse)
|
|||||||
return starttag..">","</span>"
|
return starttag..">","</span>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function escapeTypstString(s)
|
||||||
|
s = s:gsub("\\", "\\\\")
|
||||||
|
s = s:gsub("%$", "\\$")
|
||||||
|
s = s:gsub("*", "\\*")
|
||||||
|
s = s:gsub([["]],[[\"]])
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
local function escapeTypstString2(s)
|
||||||
|
s = s:gsub("@", "\\@")
|
||||||
|
s = s:gsub("<", "\\<")
|
||||||
|
s = s:gsub("#", "\\#")
|
||||||
|
s = s:gsub(">", "\\>")
|
||||||
|
--s = s:gsub("]","\\]")
|
||||||
|
s = s:gsub("\n","\\\n")
|
||||||
|
s = s:gsub("+","\\+")
|
||||||
|
s = s:gsub("/","\\/")
|
||||||
|
s = s:gsub("=","\\=")
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function noescapeString(s)
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
local function codeBlockTrans(e)
|
local function codeBlockTrans(e)
|
||||||
local converter, fmt
|
local converter, fmt, escapeString
|
||||||
if quarto.doc.isFormat('latex') then
|
if quarto.doc.isFormat('latex') then
|
||||||
converter = LaTeXconverter
|
converter = LaTeXconverter
|
||||||
fmt = 'latex'
|
fmt = 'latex'
|
||||||
|
escapeString = noescapeString
|
||||||
elseif quarto.doc.isFormat('html') then
|
elseif quarto.doc.isFormat('html') then
|
||||||
converter = HTMLconverter
|
converter = HTMLconverter
|
||||||
fmt = 'html'
|
fmt = 'html'
|
||||||
|
escapeString = noescapeString
|
||||||
elseif quarto.doc.isFormat('typst') then
|
elseif quarto.doc.isFormat('typst') then
|
||||||
converter = Typstconverter
|
converter = Typstconverter
|
||||||
fmt = 'typst'
|
fmt = 'typst'
|
||||||
|
escapeString = escapeTypstString
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
@@ -264,7 +295,7 @@ local function codeBlockTrans(e)
|
|||||||
local out=""
|
local out=""
|
||||||
local text = e.text
|
local text = e.text
|
||||||
|
|
||||||
-- we remove links (eg in julia ParseErrors. THey link to local files, so they are useless anyway)
|
-- we remove links (eg in julia ParseErrors. They link to local files, so they are useless anyway)
|
||||||
text = text:gsub("\x1b%]8;.-\x1b\\", "")
|
text = text:gsub("\x1b%]8;.-\x1b\\", "")
|
||||||
|
|
||||||
if string.find(text, "\x1b%[") then
|
if string.find(text, "\x1b%[") then
|
||||||
@@ -305,7 +336,7 @@ local function codeBlockTrans(e)
|
|||||||
else
|
else
|
||||||
starttag, endtag = converter(fg, bg, bold, light, italic, underline, inverse)
|
starttag, endtag = converter(fg, bg, bold, light, italic, underline, inverse)
|
||||||
end
|
end
|
||||||
out = out .. starttag .. chunk .. endtag
|
out = out .. starttag .. escapeString(chunk) .. endtag
|
||||||
end
|
end
|
||||||
|
|
||||||
while next(numbers) ~= nil do
|
while next(numbers) ~= nil do
|
||||||
@@ -359,7 +390,7 @@ local function codeBlockTrans(e)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
out = text
|
out = escapeString(text)
|
||||||
end
|
end
|
||||||
if fmt == 'html' then
|
if fmt == 'html' then
|
||||||
return pandoc.RawBlock(fmt,
|
return pandoc.RawBlock(fmt,
|
||||||
@@ -369,7 +400,11 @@ local function codeBlockTrans(e)
|
|||||||
return pandoc.RawBlock(fmt, [[\begin{]]..texenv.."}\n"..out.."\n"..[[\end{]].. texenv .. "}")
|
return pandoc.RawBlock(fmt, [[\begin{]]..texenv.."}\n"..out.."\n"..[[\end{]].. texenv .. "}")
|
||||||
end
|
end
|
||||||
if fmt == 'typst' then
|
if fmt == 'typst' then
|
||||||
return pandoc.RawBlock(fmt, "#"..texenv.."[\n"..out.."\n]")
|
if texenv == "OutputCell" then
|
||||||
|
return pandoc.RawBlock(fmt, "#"..texenv.."[\n" .. escapeTypstString2(out) .. "\n]")
|
||||||
|
else
|
||||||
|
return pandoc.RawBlock(fmt, "#"..texenv.."[\n" .. out .. "\n]")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@@ -398,12 +433,52 @@ local function divCodeBlockNoHeader1(e)
|
|||||||
if el.t == 'Header' then
|
if el.t == 'Header' then
|
||||||
el.level = 6
|
el.level = 6
|
||||||
end
|
end
|
||||||
|
-- if el.t == 'CodeBlock' then -- attempt for typst, but we use an extra quarto cell
|
||||||
|
-- for i,v in ipairs(el.classes) do
|
||||||
|
-- if v=="julia" or v=="jldoctest" then -- example code and before
|
||||||
|
-- el.classes:remove(i)
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
end
|
end
|
||||||
return e
|
return e
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- test if two divs should be merged
|
||||||
|
local function testmerge(d1, d2)
|
||||||
|
return d1 and d1.t == "Div" and d1.classes:includes("cell-output") and #d1.content == 1
|
||||||
|
and d2 and d2.t == "Div" and d2.classes:includes("cell-output") and #d2.content == 1
|
||||||
|
and d1.content[1].t == "CodeBlock" and not d1.classes:includes("cell-output-stderr")
|
||||||
|
and d2.content[1].t == "CodeBlock" and not d2.classes:includes("cell-output-stderr")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- merge (div (codecell (text1)), div (codecell(text2))) to div(codecell(text1+text2))
|
||||||
|
local function blockMerge(es)
|
||||||
|
local nl = ""
|
||||||
|
for i = #es-1, 1, -1 do
|
||||||
|
if testmerge(es[i], es[i+1]) then
|
||||||
|
str1 = es[i].content[1].text
|
||||||
|
str2 = es[i+1].content[1].text
|
||||||
|
nl = "\n"
|
||||||
|
if es[i].classes:includes("cell-output-stdout") and es[i+1].classes:includes("cell-output-stdout") then
|
||||||
|
if str1:sub(-1) == "\n" then
|
||||||
|
nl = ""
|
||||||
|
end
|
||||||
|
if str2:sub(1, 1) == "\n" then
|
||||||
|
nl = ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
es[i].content[1].text = str1 .. nl .. str2
|
||||||
|
es:remove(i+1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return es
|
||||||
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
{Div = divStderr},
|
{Div = divStderr},
|
||||||
{Div = divCodeBlockNoHeader1},
|
{Div = divCodeBlockNoHeader1},
|
||||||
|
{Blocks = blockMerge},
|
||||||
{CodeBlock = codeBlockTrans},
|
{CodeBlock = codeBlockTrans},
|
||||||
}
|
}
|
||||||
|
|||||||
38
_extensions/MHellmund/julia-color/numbering.typ
Normal file
38
_extensions/MHellmund/julia-color/numbering.typ
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Chapter-based numbering for books with appendix support
|
||||||
|
#let equation-numbering = it => {
|
||||||
|
let pattern = if state("appendix-state", none).get() != none { "(A.1)" } else { "(1.1)" }
|
||||||
|
numbering(pattern, counter(heading).get().first(), it)
|
||||||
|
}
|
||||||
|
#let callout-numbering = it => {
|
||||||
|
let pattern = if state("appendix-state", none).get() != none { "A.1" } else { "1.1" }
|
||||||
|
numbering(pattern, counter(heading).get().first(), it)
|
||||||
|
}
|
||||||
|
#let subfloat-numbering(n-super, subfloat-idx) = {
|
||||||
|
let chapter = counter(heading).get().first()
|
||||||
|
let pattern = if state("appendix-state", none).get() != none { "A.1a" } else { "1.1a" }
|
||||||
|
numbering(pattern, chapter, n-super, subfloat-idx)
|
||||||
|
}
|
||||||
|
// Theorem configuration for theorion
|
||||||
|
// Chapter-based numbering (H1 = chapters)
|
||||||
|
#let theorem-inherited-levels = 1
|
||||||
|
|
||||||
|
// Appendix-aware theorem numbering
|
||||||
|
#let theorem-numbering(loc) = {
|
||||||
|
if state("appendix-state", none).at(loc) != none { "A.1" } else { "1.1" }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Theorem render function
|
||||||
|
// Note: brand-color is not available at this point in template processing
|
||||||
|
#let theorem-render(prefix: none, title: "", full-title: auto, body) = {
|
||||||
|
block(
|
||||||
|
width: 100%,
|
||||||
|
inset: (left: 1em),
|
||||||
|
stroke: (left: 2pt + black),
|
||||||
|
)[
|
||||||
|
#if full-title != "" and full-title != auto and full-title != none {
|
||||||
|
strong[#full-title]
|
||||||
|
linebreak()
|
||||||
|
}
|
||||||
|
#body
|
||||||
|
]
|
||||||
|
}
|
||||||
69
_extensions/MHellmund/julia-color/orange-book.lua
Normal file
69
_extensions/MHellmund/julia-color/orange-book.lua
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
-- orange-book.lua
|
||||||
|
-- Orange-book specific part and appendix handling for Typst books
|
||||||
|
|
||||||
|
local function is_typst_book()
|
||||||
|
local file_state = quarto.doc.file_metadata()
|
||||||
|
return quarto.doc.is_format("typst") and
|
||||||
|
file_state ~= nil and
|
||||||
|
file_state.file ~= nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local header_filter = {
|
||||||
|
Header = function(el)
|
||||||
|
local file_state = quarto.doc.file_metadata()
|
||||||
|
|
||||||
|
if not is_typst_book() then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if file_state == nil or file_state.file == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local file = file_state.file
|
||||||
|
local bookItemType = file.bookItemType
|
||||||
|
|
||||||
|
if el.level ~= 1 or bookItemType == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle parts
|
||||||
|
if bookItemType == "part" then
|
||||||
|
return pandoc.RawBlock('typst', '#part[' .. pandoc.utils.stringify(el.content) .. ']')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle appendices
|
||||||
|
if bookItemType == "appendix" then
|
||||||
|
-- First appendix triggers the show rule with localized "Appendices" title
|
||||||
|
if file.bookItemNumber == 1 or file.bookItemNumber == nil then
|
||||||
|
-- Get localized title from language settings
|
||||||
|
local language = quarto.doc.language
|
||||||
|
local appendicesTitle = (language and language["section-title-appendices"]) or "Appendices"
|
||||||
|
|
||||||
|
-- Use hide-parent: true to work around orange-book bug where unnumbered headings
|
||||||
|
-- (like Bibliography) trigger duplicate "Appendices" TOC entries.
|
||||||
|
local appendixStart = pandoc.RawBlock('typst',
|
||||||
|
'#show: appendices.with("' .. appendicesTitle .. '", hide-parent: true)')
|
||||||
|
|
||||||
|
-- If this is the synthetic "Appendices" divider heading (has .unnumbered class),
|
||||||
|
-- emit our own Appendices heading for TOC display
|
||||||
|
if el.classes:includes("unnumbered") then
|
||||||
|
local appendicesHeading = pandoc.RawBlock('typst',
|
||||||
|
'#heading(level: 1, numbering: none)[' .. appendicesTitle .. ']')
|
||||||
|
return {appendixStart, appendicesHeading}
|
||||||
|
end
|
||||||
|
|
||||||
|
return {appendixStart, el}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Combine with file_metadata_filter so book metadata markers are parsed
|
||||||
|
-- during this filter's document traversal (needed for bookItemType, etc.)
|
||||||
|
return quarto.utils.combineFilters({
|
||||||
|
quarto.utils.file_metadata_filter(),
|
||||||
|
header_filter
|
||||||
|
})
|
||||||
15
_extensions/MHellmund/julia-color/page.typ
Normal file
15
_extensions/MHellmund/julia-color/page.typ
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#set page(
|
||||||
|
paper: $if(papersize)$"$papersize$"$else$"us-letter"$endif$,
|
||||||
|
$if(margin-geometry)$
|
||||||
|
// Margins handled by marginalia.setup in typst-show.typ AFTER book.with()
|
||||||
|
$elseif(margin)$
|
||||||
|
margin: ($for(margin/pairs)$$margin.key$: $margin.value$,$endfor$),
|
||||||
|
$else$
|
||||||
|
margin: (x: 1.25in, y: 1.25in),
|
||||||
|
$endif$
|
||||||
|
numbering: $if(page-numbering)$"$page-numbering$"$else$none$endif$,
|
||||||
|
columns: $if(columns)$$columns$$else$1$endif$,
|
||||||
|
)
|
||||||
|
// Logo is handled by orange-book's cover page, not as a page background
|
||||||
|
// NOTE: marginalia.setup is called in typst-show.typ AFTER book.with()
|
||||||
|
// to ensure marginalia's margins override the book format's default margins
|
||||||
@@ -2,10 +2,45 @@
|
|||||||
#show raw: set text(font: "JuliaMono")
|
#show raw: set text(font: "JuliaMono")
|
||||||
|
|
||||||
// define cell layout
|
// define cell layout
|
||||||
#let OutputCell = block.with(width:100%, inset: 5pt)
|
|
||||||
#let AnsiOutputCell = block.with(width: 100%, inset: 5pt)
|
//#let EndLine() = raw("\n")
|
||||||
|
//#let OutputCell(lines) = {
|
||||||
|
// let blocks = []
|
||||||
|
// for ln in lines {
|
||||||
|
// blocks = blocks + ln + EndLine()
|
||||||
|
// }
|
||||||
|
// block(width:100%, inset:(x:5pt, y:0pt), stroke:( left: 2pt + gray), blocks)
|
||||||
|
//}
|
||||||
|
|
||||||
|
#let OutputCell(content) = block(
|
||||||
|
width: 100%,
|
||||||
|
inset: (x: 5pt, y: 5pt),
|
||||||
|
above: 0pt,
|
||||||
|
below: 8pt,
|
||||||
|
stroke: (left: 2pt + gray, bottom: .5pt + gray)
|
||||||
|
)[
|
||||||
|
#set text(font: "JuliaMono", size:8pt)
|
||||||
|
#content
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
#let AnsiOutputCell(content) = block(
|
||||||
|
width: 100%,
|
||||||
|
inset: (x: 5pt, y: 5pt),
|
||||||
|
above: 0pt,
|
||||||
|
below: 8pt,
|
||||||
|
stroke: (left: 2pt + gray, bottom: .5pt + gray)
|
||||||
|
)[
|
||||||
|
#set text(font: "JuliaMono", size:9pt)
|
||||||
|
#content
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// does not exist with julia engine
|
||||||
#let StderrOutputCell = block.with(width: 100%, stroke: 1pt + red, inset: 5pt)
|
#let StderrOutputCell = block.with(width: 100%, stroke: 1pt + red, inset: 5pt)
|
||||||
|
|
||||||
|
|
||||||
//#set highlight(top-edge: "ascender", bottom-edge: "descender")
|
//#set highlight(top-edge: "ascender", bottom-edge: "descender")
|
||||||
#let invertbox(color, c) = box(outset: (x: 0.05em, y: 0.25em), fill: color, c)
|
#let invertbox(color, c) = box(outset: (x: 0.05em, y: 0.25em), fill: color, c)
|
||||||
|
|
||||||
|
|||||||
61
_extensions/MHellmund/julia-color/typst-show.typ
Normal file
61
_extensions/MHellmund/julia-color/typst-show.typ
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#import "@preview/orange-book:0.7.1": book, part, chapter, appendices
|
||||||
|
|
||||||
|
#show: book.with(
|
||||||
|
$if(title)$
|
||||||
|
title: [$title$],
|
||||||
|
$endif$
|
||||||
|
$if(subtitle)$
|
||||||
|
subtitle: [$subtitle$],
|
||||||
|
$endif$
|
||||||
|
$if(by-author)$
|
||||||
|
author: "$for(by-author)$$it.name.literal$$sep$, $endfor$",
|
||||||
|
$endif$
|
||||||
|
$if(date)$
|
||||||
|
date: "$date$",
|
||||||
|
$endif$
|
||||||
|
$if(lang)$
|
||||||
|
lang: "$lang$",
|
||||||
|
$endif$
|
||||||
|
main-color: brand-color.at("primary", default: blue),
|
||||||
|
logo: {
|
||||||
|
let logo-info = brand-logo.at("medium", default: none)
|
||||||
|
if logo-info != none { image(logo-info.path, alt: logo-info.at("alt", default: none)) }
|
||||||
|
},
|
||||||
|
$if(toc-depth)$
|
||||||
|
outline-depth: $toc-depth$,
|
||||||
|
$endif$
|
||||||
|
$if(lof)$
|
||||||
|
list-of-figure-title: "$if(crossref.lof-title)$$crossref.lof-title$$else$$crossref-lof-title$$endif$",
|
||||||
|
$endif$
|
||||||
|
$if(lot)$
|
||||||
|
list-of-table-title: "$if(crossref.lot-title)$$crossref.lot-title$$else$$crossref-lot-title$$endif$",
|
||||||
|
$endif$
|
||||||
|
$if(margin-geometry)$
|
||||||
|
padded-heading-number: false,
|
||||||
|
$endif$
|
||||||
|
)
|
||||||
|
|
||||||
|
$if(margin-geometry)$
|
||||||
|
// Configure marginalia page geometry for book context
|
||||||
|
// Geometry computed by Quarto's meta.lua filter (typstGeometryFromPaperWidth)
|
||||||
|
// IMPORTANT: This must come AFTER book.with() to override the book format's margin settings
|
||||||
|
#import "@preview/marginalia:0.3.1" as marginalia
|
||||||
|
|
||||||
|
#show: marginalia.setup.with(
|
||||||
|
inner: (
|
||||||
|
far: $margin-geometry.inner.far$,
|
||||||
|
width: $margin-geometry.inner.width$,
|
||||||
|
sep: $margin-geometry.inner.separation$,
|
||||||
|
),
|
||||||
|
outer: (
|
||||||
|
far: $margin-geometry.outer.far$,
|
||||||
|
width: $margin-geometry.outer.width$,
|
||||||
|
sep: $margin-geometry.outer.separation$,
|
||||||
|
),
|
||||||
|
top: $if(margin.top)$$margin.top$$else$1.25in$endif$,
|
||||||
|
bottom: $if(margin.bottom)$$margin.bottom$$else$1.25in$endif$,
|
||||||
|
// CRITICAL: Enable book mode for recto/verso awareness
|
||||||
|
book: true,
|
||||||
|
clearance: $margin-geometry.clearance$,
|
||||||
|
)
|
||||||
|
$endif$
|
||||||
Reference in New Issue
Block a user