diff --git a/oi-wiki-export-typst/constants.typ b/oi-wiki-export-typst/constants.typ
index 5a1c06e..8c78b97 100644
--- a/oi-wiki-export-typst/constants.typ
+++ b/oi-wiki-export-typst/constants.typ
@@ -9,5 +9,14 @@
#let RAW_EM = 1.125em
// Page dimensions minus margin
-#let VISIBLE_WIDTH = 21cm - 1in
-#let VISIBLE_HEIGHT = 29.7cm - 1.5in
+#let serif-font = (
+ "New Computer Modern",
+ "Noto Serif CJK SC",
+ "Source Han Serif SC",
+)
+#let sans-font = (
+ "New Computer Modern",
+ "Noto Sans CJK SC",
+ "Source Han Sans SC",
+)
+#let emph-font = ("New Computer Modern", "LXGW Wenkai")
diff --git a/oi-wiki-export-typst/oi-wiki-export.typ b/oi-wiki-export-typst/oi-wiki-export.typ
index 794728c..9f75ea1 100644
--- a/oi-wiki-export-typst/oi-wiki-export.typ
+++ b/oi-wiki-export-typst/oi-wiki-export.typ
@@ -4,6 +4,12 @@
#import "constants.typ": *
#import "oi-wiki.typ": page-header
/* END imports */
+#show ref: it => {
+ if query(it.target).len() == 0 {
+ return text(fill: red, "<未找到引用" + str(it.target) + ">")
+ }
+ it
+}
/* BEGIN meta */
#set text(
@@ -16,27 +22,33 @@
#set page(
header: none,
paper: "a4",
- margin: (top: .8in, inside: .4in, bottom: .7in, outside: .6in),
header-ascent: .3in,
fill: luma(95%),
)
#align(center + horizon)[
// OI-Wiki logo
- #image.decode("", height: 4cm)
+ #image.decode(
+ "",
+ height: 4cm,
+ )
#text(
25pt,
- font: ("New Computer Modern", "Noto Serif CJK SC"),
+ font: serif-font,
weight: 700,
)[OI Wiki (Beta)]
#v(4cm)
#text(
18pt,
- font: ("New Computer Modern", "Noto Serif CJK SC"),
+ font: serif-font,
)[
OI Wiki 项目组
- #datetime.today().display("[year] 年 [month padding:none] 月 [day padding:none] 日")
+ #(
+ datetime
+ .today()
+ .display("[year] 年 [month padding:none] 月 [day padding:none] 日")
+ )
]
]
@@ -56,7 +68,7 @@
#set text(
ROOT_EM,
- font: ("New Computer Modern", "Noto Serif CJK SC"),
+ font: serif-font,
)
#set par(
@@ -66,13 +78,16 @@
// issues: https://github.com/typst/typst/issues/311
// https://github.com/typst/typst/issues/1410
// first-line-indent: 2em,
+ linebreaks: "optimized",
+ justify: true,
)
+#show raw.where(block: true): set par(justify: false)
#set block(spacing: .8em)
#set strong(delta: 0)
#show strong: set text(
- font: ("New Computer Modern", "Noto Sans CJK SC"),
+ font: sans-font,
// New Computer Modern: 400 |----->700
// Noto Sans CJK: 400 500<-| 700
// DejaVu Sans Mono: 400 |----->700
@@ -82,75 +97,56 @@
)
#set heading(numbering: "1.1")
-#show heading: set block(spacing: 0em)
#show heading: set text(
- font: ("New Computer Modern", "Noto Sans CJK SC"),
+ font: sans-font,
weight: 551,
)
-#show heading.where(level: 1): set text(25pt)
-#show heading.where(level: 2): set text(20pt)
-#show heading.where(level: 3): set text(17pt)
-#show heading.where(level: 4): set text(14pt)
-#show heading.where(level: 5): set text(12pt)
-#show heading.where(level: 6): set text(10pt)
-#show heading: it => {
- // NOTE: dynamic spacing?
- // v(1fr, weak: true)
- v(1.4em)
- it
- v(.2em)
-}
+#show heading.where(level: 1): set text(18pt)
#show heading.where(level: 2): it => {
- v(2em)
- align(center)[#it]
- v(2em)
+ set text(16pt)
+ align(center, it)
}
+#show heading.where(level: 3): set text(14pt)
+#show heading.where(level: 4): set text(12pt)
+#show heading.where(level: 5): set text(11pt)
+#show heading.where(level: 6): set text(10pt)
-#show emph: set text(
- font: ("New Computer Modern", "LXGW Wenkai")
-)
+#show emph: set text(font: emph-font)
-#show math.equation: set text(
- font: ("New Computer Modern Math", "LXGW Wenkai")
-)
+#show math.equation: set text(font: ("New Computer Modern Math", "LXGW Wenkai"))
#show raw: set text(
RAW_EM,
font: ("DejaVu Sans Mono", "LXGW Wenkai"),
)
+
#show raw.where(block: false): it => highlight(
fill: luma(95%),
- it
+ it,
)
/* END article formatting */
/* BEGIN outline */
-#show outline.entry.where(
- level: 1
-): it => {
- v(20pt, weak: true)
- text(14pt)[#strong(it)]
+#show outline.entry.where(level: 1): it => {
+ v(2em, weak: true)
+ text(12pt, font: sans-font, it)
}
-#outline(indent: auto)
+#outline(indent: 2em)
/* END outline */
/* BEGIN main */
-#set page(
- header: page-header
-)
+#set page(header: page-header)
#counter(page).update(1)
#show heading.where(level: 1): it => {
set text(
25pt,
- font: ("New Computer Modern", "Noto Serif CJK SC"),
+ font: serif-font,
weight: 700,
)
- set par(
- first-line-indent: 0em,
- )
+ set par(first-line-indent: 0em)
align(horizon)[
第#counter(heading).display("一")章
@@ -176,7 +172,7 @@
#show list: set block(width: 100%)
#set enum(
indent: 1em,
- body-indent: -.5em -.278em + 1em,
+ body-indent: -.5em - .278em + 1em,
)
#show enum: set block(width: 100%)
@@ -185,7 +181,6 @@
#show footnote.entry: it => {
set text(9pt)
- show parbreak: none
it
}
@@ -202,6 +197,6 @@
#align(
center + horizon,
- text(17pt)[https://oi-wiki.org]
+ text(17pt)[https://oi-wiki.org],
)
/* END back cover */
diff --git a/oi-wiki-export-typst/oi-wiki.typ b/oi-wiki-export-typst/oi-wiki.typ
index b1406ce..7f0c95d 100644
--- a/oi-wiki-export-typst/oi-wiki.typ
+++ b/oi-wiki-export-typst/oi-wiki.typ
@@ -6,60 +6,66 @@
#import "@preview/tablex:0.0.8": tablex
#import "@preview/tiaoma:0.2.0"
-#import "@preview/mitex:0.2.3": mi, mitex
-/* END imports */
+#import "@preview/mitex:0.2.4": mi, mitex
+#import "@preview/codelst:2.0.2": sourcecode
-#let page-header = locate(
- loc => {
- if calc.odd(loc.page()) {
- // NOTE: not able to programatically hide headings on new chapters for now
- // issue: https://github.com/typst/typst/issues/1613
- let section = query(selector(heading.where(level: 2)).before(loc), loc)
- if section == () {
- return none
- }
- let sect-number(..headings) = {
- let levels = headings.pos()
+/* END imports */
- if levels.len() > 1 {
- [#levels.at(0).#levels.at(1)]
- } else {
- none
- }
- }
+#let page-header = context {
+ let loc = here()
+ if calc.odd(loc.page()) {
+ // NOTE: not able to programatically hide headings on new chapters for now
+ // issue: https://github.com/typst/typst/issues/1613
+ let section = query(selector(heading.where(level: 2)).before(loc))
+ if section == () {
+ return none
+ }
- text(9pt, number-width: "tabular")[
- #emph[
- #counter(heading).display(sect-number)
- #h(1em)
- #smallcaps(section.last().body)
- ]
- #h(1fr)
- #counter(page).display("1")
- ]
- } else {
- let chapters = query(selector(heading.where(level: 1)).before(loc), loc)
- // HACK: don't add headers in outlines (Chapter 0)
- // This is only a workaround. Detailed mechanism of typst's pagebreaks
- // needs to be further researched.
- let chapter-counter = counter(heading.where(level: 1)).at(loc)
- if chapter-counter == (0,) {
- return none
+ let sect-number(..headings) = {
+ let levels = headings.pos()
+
+ if levels.len() > 1 {
+ [#levels.at(0).#levels.at(1)]
+ } else {
+ none
}
+ }
- text(9pt, number-width: "tabular")[
- #counter(page).display("1")
- #h(1fr)
- 第#counter(heading.where(level: 1)).display("一")章
+ text(9pt, number-width: "tabular")[
+ #emph[
+ #counter(heading).display(sect-number)
#h(1em)
- #chapters.last().body
+ #smallcaps(section.last().body)
]
+ #h(1fr)
+ #counter(page).display("1")
+ ]
+ } else {
+ let chapters = query(selector(heading.where(level: 1)).before(loc))
+ // HACK: don't add headers in outlines (Chapter 0)
+ // This is only a workaround. Detailed mechanism of typst's pagebreaks
+ // needs to be further researched.
+ let chapter-counter = counter(heading.where(level: 1)).at(loc)
+ if chapter-counter == (0,) {
+ return none
}
- },
+
+ text(9pt, number-width: "tabular")[
+ #counter(page).display("1")
+ #h(1fr)
+ 第#counter(heading.where(level: 1)).display("一")章
+ #h(1em)
+ #chapters.last().body
+ ]
+ }
+}
)
-#let horizontalrule = align(center, block(sym.ast.op + h(1em) + sym.ast.op + h(1em) + sym.ast.op))
+#let horizontalrule = align(
+ center,
+ block(sym.ast.op + h(1em) + sym.ast.op + h(1em) + sym.ast.op),
+)
#let blockquote(content) = {
set text(fill: luma(50%))
@@ -75,135 +81,25 @@
#let authors(authors) = blockquote[*Authors:* #authors]
#let kbd(string) = {
- let key = box(outset: .2em, inset: (x: .1em), fill: luma(95%), stroke: (
- bottom: (paint: luma(50%), thickness: 2pt, cap: "round"),
- x: (paint: luma(50%), thickness: 1pt, cap: "round"),
- ), radius: .2em, raw(string))
+ let key = box(
+ outset: .2em,
+ inset: (x: .1em),
+ fill: luma(95%),
+ stroke: (
+ bottom: (paint: luma(50%), thickness: 2pt, cap: "round"),
+ x: (paint: luma(50%), thickness: 1pt, cap: "round"),
+ ),
+ radius: .2em,
+ raw(string),
+ )
h(.5em) + key + h(.5em)
}
-#let codeblock(code, lang: str, unwrapped: false) = {
- let radius = if unwrapped {
- (bottom: .2em)
- } else {
- .2em
- }
- let stroke = if unwrapped {
- (
- top: (thickness: 1pt, paint: luma(75%), dash: "dashed"),
- bottom: 1pt + luma(75%),
- x: 1pt + luma(75%),
- )
- } else {
- 1pt + luma(75%)
- }
-
- // Code block with line numbers
- // Issue: https://github.com/typst/typst/issues/344
- // Reference: https://gist.github.com/mpizenberg/c6ed7bc3992ee5dfed55edce508080bb
- let lines = code.replace("\t", " ").split("\n")
- let digits = str(lines.len()).len()
-
- // Width of glyphs in DejaVu Sans Mono is 1233 units
- let glyph-width = (1233 / 2048) * 0.8 * RAW_EM
- let number-width = (digits + 2) * glyph-width
- let track-width = (digits + 5) * glyph-width
-
- grid(
- columns: 2,
- column-gutter: -100%,
- // Background & line numbers
- block(
- width: 100%,
- radius: radius,
- inset: (left: track-width, y: .5em),
- fill: luma(95%),
- stroke: stroke,
- {
- set text(
- 0.8 * RAW_EM,
- font: ("DejaVu Sans Mono", "LXGW WenKai"),
- fill: luma(75%),
- )
-
- for (i, line) in lines.enumerate() {
- box(
- width: 0pt,
- inset: (right: track-width - number-width),
- align(right, str(i + 1)),
- )
- hide(line)
- linebreak()
- }
- },
- ),
- // The code itself
- block(
- width: 100%,
- inset: (left: track-width, y: .5em),
- raw(block: true, lang: lang, code),
- ),
- )
+#let img-auto(src, alt: str) = {
+ image(src, alt: alt)
}
-// Auto-sized image.
-// NOTE: optimized image sizing is in progress
-// issue: https://github.com/typst/typst/issues/436
-#let img-auto(src, alt: str) = style(styles => {
- let img = image(src)
- let (width, height) = measure(img, styles)
-
- let max-image-width = VISIBLE_WIDTH - ROOT_EM * 8
- let max-image-height = VISIBLE_HEIGHT / 2 - ROOT_EM * 8
-
- if width / height > max-image-width / max-image-height {
- set image(width: calc.min(width, max-image-width))
-
- v(.8em)
- align(center, img)
- v(.8em)
- } else {
- set image(height: calc.min(height, max-image-height))
-
- v(.8em)
- align(center, img)
- v(.8em)
- }
- // BEGIN trigonometric solution
- // let hori = VISIBLE_WIDTH.pt()
- // let radius = hori / 2
- // let ratio = width / height
- // let vert = hori / ratio
- // let diag = calc.sqrt(radius * radius + vert * vert)
- // let factor = diag / radius
- // set image(width: calc.min(width, VISIBLE_WIDTH / factor))
- // figure(img, caption: alt)
- // END trigonometric solution
- // BEGIN another trigonometric solution
- // let endpoint = MAX_IMAGE_HEIGHT - MAX_IMAGE_WIDTH / 2
- // let max-ratio = MAX_IMAGE_WIDTH / endpoint
- // if width / height > max-ratio {
- // set image(width: calc.min(width / 2, max-image-width))
- // figure(img, caption: alt)
- // } else {
- // let r = max-ratio / 2
- // let v = max-ratio / (width / height)
- // let b1 = calc.sqrt((v - 1) * (v - 1) + r * r)
- // let c1 = calc.sqrt(v * v + r * r)
- // let a = 1
- // let b = r
- // let B = calc.acos((a * a + c1 * c1 - b1 * b1) / (2 * a * c1))
- // let A = calc.asin(a * calc.sin(B) / b)
- // let C = 180deg - A - B
- // let c = (a * calc.sin(C) / calc.sin(A))
- // let f = c1 / c
- // set image(width: calc.min(width / 2, max-image-width / f))
- // figure(img, caption: alt)
- // }
- // END another trigonometric solution
-})
-
#let svg-math(svg, display: false) = style(styles => {
let img = image.decode(svg)
let (width, height) = measure(img, styles)
@@ -222,7 +118,11 @@
grid(columns: (1fr, .75in, 1fr, .5in), rows: .5in, ..content)
}
-#let links-cell(content) = block(width: 100%, height: 100%, align(horizon, content))
+#let links-cell(content) = block(
+ width: 100%,
+ height: 100%,
+ align(horizon, content),
+)
#let qrcode(arg) = tiaoma.qrcode(arg, width: .4in)
#let tablex-custom(columns: (), aligns: (), ..cells) = {
@@ -232,18 +132,11 @@
center,
block(
radius: .2em,
- inset: (x: .5em),
stroke: 1pt + luma(75%),
- tablex(
+ table(
columns: columns,
- column-gutter: 1fr,
- // NOTE: repeat-header has no effect when the table is in a container
- // it is also a little buggy right now, so we are not enabling it at this moment
- // issue: https://github.com/PgBiel/typst-tablex/issues/43
- repeat-header: false,
align: (col, row) => aligns.at(col),
stroke: 1pt + luma(75%),
- auto-vlines: false,
..cells,
),
),
@@ -265,8 +158,6 @@
inset: (x: 1em, y: .5em),
radius: (top: .2em),
)[
- #show parbreak: none
-
#strong(items.at(0))
]
diff --git a/oi-wiki-export-typst/pymdownx-details.typ b/oi-wiki-export-typst/pymdownx-details.typ
index d35818d..51f42c7 100644
--- a/oi-wiki-export-typst/pymdownx-details.typ
+++ b/oi-wiki-export-typst/pymdownx-details.typ
@@ -1,29 +1,55 @@
+#import "@preview/gentle-clues:1.1.0": *
+#import "constants.typ": *
/* BEGIN constants */
-#let note-color = (bright: cmyk(10%, 5%, 0%, 0%), dark: cmyk(40%, 20%, 0%, 0%))
-#let abstract-color = (bright: cmyk(10%, 0%, 0%, 0%), dark: cmyk(40%, 0%, 0%, 0%))
-#let info-color = (bright: cmyk(10%, 0%, 5%, 0%), dark: cmyk(40%, 0%, 20%, 0%))
-#let tip-color = (bright: cmyk(10%, 0%, 10%, 0%), dark: cmyk(40%, 0%, 40%, 0%))
-#let success-color = (bright: cmyk(5%, 0%, 10%, 0%), dark: cmyk(20%, 0%, 40%, 0%))
-#let question-color = (bright: cmyk(0%, 0%, 10%, 0%), dark: cmyk(0%, 0%, 40%, 0%))
-#let warning-color = (bright: cmyk(0%, 5%, 10%, 0%), dark: cmyk(0%, 20%, 40%, 0%))
-#let failure-color = (bright: cmyk(0%, 10%, 10%, 0%), dark: cmyk(0%, 40%, 40%, 0%))
-#let danger-color = (bright: cmyk(0%, 10%, 5%, 0%), dark: cmyk(0%, 40%, 20%, 0%))
-#let bug-color = (bright: cmyk(0%, 10%, 0%, 0%), dark: cmyk(0%, 40%, 0%, 0%))
-#let example-color = (bright: cmyk(5%, 10%, 0%, 0%), dark: cmyk(20%, 40%, 0%, 0%))
-#let quote-color = (bright: cmyk(10%, 10%, 0%, 0%), dark: cmyk(40%, 40%, 0%, 0%))
+#let note-color = (bright: cmyk(10%, 5%, 0%, 0%), dark: cmyk(40%, 20%, 0%, 0%))
+#let abstract-color = (
+ bright: cmyk(10%, 0%, 0%, 0%),
+ dark: cmyk(40%, 0%, 0%, 0%),
+)
+#let info-color = (bright: cmyk(10%, 0%, 5%, 0%), dark: cmyk(40%, 0%, 20%, 0%))
+#let tip-color = (bright: cmyk(10%, 0%, 10%, 0%), dark: cmyk(40%, 0%, 40%, 0%))
+#let success-color = (
+ bright: cmyk(5%, 0%, 10%, 0%),
+ dark: cmyk(20%, 0%, 40%, 0%),
+)
+#let question-color = (
+ bright: cmyk(0%, 0%, 10%, 0%),
+ dark: cmyk(0%, 0%, 40%, 0%),
+)
+#let warning-color = (
+ bright: cmyk(0%, 5%, 10%, 0%),
+ dark: cmyk(0%, 20%, 40%, 0%),
+)
+#let failure-color = (
+ bright: cmyk(0%, 10%, 10%, 0%),
+ dark: cmyk(0%, 40%, 40%, 0%),
+)
+#let danger-color = (
+ bright: cmyk(0%, 10%, 5%, 0%),
+ dark: cmyk(0%, 40%, 20%, 0%),
+)
+#let bug-color = (bright: cmyk(0%, 10%, 0%, 0%), dark: cmyk(0%, 40%, 0%, 0%))
+#let example-color = (
+ bright: cmyk(5%, 10%, 0%, 0%),
+ dark: cmyk(20%, 40%, 0%, 0%),
+)
+#let quote-color = (
+ bright: cmyk(10%, 10%, 0%, 0%),
+ dark: cmyk(40%, 40%, 0%, 0%),
+)
-#let note-icon = image.decode("")
-#let abstract-icon = image.decode("")
-#let info-icon = image.decode("")
-#let tip-icon = image.decode("")
-#let success-icon = image.decode("")
-#let question-icon = image.decode("")
-#let warning-icon = image.decode("")
-#let failure-icon = image.decode("")
-#let danger-icon = image.decode("")
-#let bug-icon = image.decode("")
-#let example-icon = image.decode("")
-#let quote-icon = image.decode("")
+#let note-icon = image.decode("")
+#let abstract-icon = image.decode("")
+#let info-icon = image.decode("")
+#let tip-icon = image.decode("")
+#let success-icon = image.decode("")
+#let question-icon = image.decode("")
+#let warning-icon = image.decode("")
+#let failure-icon = image.decode("")
+#let danger-icon = image.decode("")
+#let bug-icon = image.decode("")
+#let example-icon = image.decode("")
+#let quote-icon = image.decode("")
/* END constants */
#let details(type: str, unwrap: false, ..items) = {
@@ -31,7 +57,7 @@
if items.len() != 2 {
panic("#details receives exactly two content blocks")
}
-
+ let (title, content) = items
let (color, icon) = if type == "abstract" {
(abstract-color, abstract-icon)
} else if type == "info" {
@@ -57,42 +83,11 @@
} else {
(note-color, note-icon)
}
-
- block[
- #block(
- width: 100%,
- fill: color.bright,
- stroke: (
- top: 1pt + color.dark,
- x: 1pt + color.dark
- ),
- below: 0em,
- inset: (x: 1em, y: .5em),
- radius: (top: .2em),
- )[
- #show parbreak: []
-
- #box(height: 1.25em, baseline: .25em, icon)
- #h(.5em)
- #strong(items.at(0))
- ]
-
- #if not unwrap {
- block(
- width: 100%,
- stroke: (
- top: (thickness: 1pt, paint: color.dark, dash: "dashed"),
- bottom: 1pt + color.dark,
- x: 1pt + color.dark
- ),
- above: 0em,
- inset: (x: 1em, y: .5em),
- radius: (bottom: .2em),
-
- items.at(1)
- )
- } else {
- items.at(1)
- }
- ]
+ clue(
+ title: title,
+ icon: icon,
+ accent-color: color.dark,
+ title-font: sans-font,
+ content,
+ )
}