diff --git a/packages/preview/bookly/2.0.0/LICENSE b/packages/preview/bookly/2.0.0/LICENSE new file mode 100644 index 0000000000..d54b2b04a1 --- /dev/null +++ b/packages/preview/bookly/2.0.0/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Mathieu Aucejo + +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. diff --git a/packages/preview/bookly/2.0.0/README.md b/packages/preview/bookly/2.0.0/README.md new file mode 100644 index 0000000000..931cdcbdde --- /dev/null +++ b/packages/preview/bookly/2.0.0/README.md @@ -0,0 +1,111 @@ +# Book template + +[![Generic badge](https://img.shields.io/badge/Version-2.0.0-cornflowerblue.svg)]() +[![MIT License](https://img.shields.io/badge/License-MIT-forestgreen)](https://github.com/maucejo/book_template/blob/main/LICENSE) +[![User Manual](https://img.shields.io/badge/doc-.pdf-mediumpurple)](https://github.com/maucejo/bookly/blob/2c85e55a47588136738ab426bf62a1a1812860b9/docs/manual.pdf) + + +The `bookly` template is a Typst package designed for writing academic documents such as theses, French habilitations, or scientific books. It provides a structured format that adheres to academic standards, making it easier for authors to focus on content rather than formatting. + +## Basic usage + +This section provides the minimal amount of information to get started with the template. For more detailed information, see the [manual](https://github.com/maucejo/bookly/blob/2c85e55a47588136738ab426bf62a1a1812860b9/docs/manual.pdf)). + +To use the `bookly` template, you need to include the following line at the beginning of your typ file: + +```typ +#import "@preview/bookly:2.0.0": * +``` + +### Initializing the template + +After importing `bookly`, you have to initialize the template by a show rule with the `#bookly()` command. This function takes a set of argument to customize the document. + +* `title`: Title of the book +* `author`: Author of the book +* `book-config`: The dictionary allows you to customize various aspects of the book + +**Example** +```typ +#show: bookly.with( + author: "Author Name", + fonts: ( + body: "Lato", + math: "Lete Sans Math" + ), + theme: modern, + lang: "en", + title-page: book-title-page( + series: "Typst book series", + institution: "Typst community", + logo: image("images/typst-logo.svg"), + cover: image("images/book-cover.jpg", width: 45%) + ) +) +``` + +### Main features + +* Themes: `classic`, `modern`, `fancy`, `orly`, `pretty` +* Layout: "standard" and "tufte" +* Language support: English, Chinese, French, German, Italian, Portuguese, Spanish +* Font customization: Body, math and raw fonts can be customized +* Environments: `front-matter`, `main-matter`, `appendix`, `back-matter` +* Outlines: `tableofcontents`, `listoffigures`, `listoftables`, `minitoc` +* Part and chapter definition: `part`, `chapter`, `chapter-nonum` + +> **_Note:_** The chapters can be also written using the Typst standard markup syntax. + +### Helper functions + +* Subfigures - based on the `subpar` package + ```typ + #subfigure( + figure(image("image1.png"), caption: []), + figure(image("image2.png"), caption: []), , + columns: (1fr, 1fr), + caption: [Figure title], + label: , + ) + ``` + +* Equations + * Boxed equations + ```typ + $ + #boxeq[$p(A|B) prop p(B|A) space p(A)$] + $ + ``` + + * Unnumbered equations + ```typ + #nonumeq[$integral_0^1 f(x) dif x = F(1) - F(0)$] + ``` + + * Subequation numbering based on the `equate` package + +* Information boxes + * `#info-box` for remarks + * `#tip-box` for tips + * `#important-box` for important notes + * `proof-box` for proofs + * `question-box` for questions + * `custom-box` for user defined boxes + +* `book-title-page` for defining the title page of a book + +* `thesis-title-page` for defining the title page of a thesis + +* `back-cover` for defining the back cover of a book + + +For more information, please refer to the [manual](https://github.com/maucejo/book_template/blob/main/docs/manual.pdf). + +## Licence + +MIT licensed + +Copyright © 2026 Mathieu AUCEJO (maucejo) + + + diff --git a/packages/preview/bookly/2.0.0/src/bookly-components.typ b/packages/preview/bookly/2.0.0/src/bookly-components.typ new file mode 100644 index 0000000000..b74fc64c16 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/bookly-components.typ @@ -0,0 +1,92 @@ +#import "@preview/hydra:0.6.2": hydra +#import "bookly-defaults.typ": * +#import "bookly-helper.typ": * +#import "bookly-themes.typ": * + +// Chapter +#let chapter(title: none, abstract: none, toc: true, numbered: true, label: none, body) = context { + // Is the chapter numbered? + if not numbered { + numbering-heading = none + numbering-eq = "(1a)" + numbering-fig = "1" + + // Heading numbering + set heading(numbering: numbering-heading) + + // Equation numbering + set math.equation(numbering: numbering-eq) + + // Figure numbering + show figure.where(kind: image): set figure( + supplement: fig-supplement, + numbering: numbering-fig, + gap: 1.5em + ) + + // Table numbering + show figure.where(kind: table): set figure( + numbering: numbering-fig, + gap: 1.5em + ) + } + + let toc-header = states.localization.get().toc + if toc { + set page(header: none) + set align(horizon) + [#heading(title)#label] + + if abstract != none { + abstract + } + + minitoc + pagebreak() + } else { + [#heading(title)#label] + } + + body + } +} + +#let chapter-nonum(body) = { + let numbering-heading = none + let numbering-eq = "(1a)" + let numbering-fig = "1" + + // Figure numbering + show figure.where(kind: image): set figure( + supplement: fig-supplement, + numbering: numbering-fig, + gap: 1.5em + ) + + // Table numbering + show figure.where(kind: table): set figure( + numbering: numbering-fig, + gap: 1.5em + ) + + // Heading numbering + set heading(numbering: numbering-heading) + + // Equation numbering + set math.equation(numbering: numbering-eq) + + // Figure numbering + show figure.where(kind: image): set figure( + supplement: fig-supplement, + numbering: numbering-fig, + gap: 1.5em + ) + + // Table numbering + show figure.where(kind: table): set figure( + numbering: numbering-fig, + gap: 1.5em + ) + + body +} diff --git a/packages/preview/bookly/2.0.0/src/bookly-defaults.typ b/packages/preview/bookly/2.0.0/src/bookly-defaults.typ new file mode 100644 index 0000000000..94df2e6a4f --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/bookly-defaults.typ @@ -0,0 +1,63 @@ + + +#let fig-supplement = [Figure] +#let text-size = 11pt +#let paper-size = "a4" + +#let states = ( + author: state("author", none), + colors: state("theme-colors"), + counter-part: counter("part"), + in-outline: state("in-outline", false), + isappendix: state("isappendix", false), + isfrontmatter: state("isfrontmatter", false), + localization: state("localization"), + num-heading: state("num-heading", "1"), + num-pattern: state("num-pattern", "1.1."), + num-pattern-eq: state("num-pattern-eq", "(1.1)"), + num-pattern-fig: state("num-pattern-fig", "1.1"), + num-pattern-subfig: state("num-pattern-subfig", "1.1a"), + open-right: state("open-right", true), + page-numbering: state("page-numbering", "1/1"), + part-numbering: state("part-numbering", "1"), + sidenotecounter: counter("sidenotecounter"), + theme: state("theme", "fancy"), + title: state("title", none), + tufte: state("tufte", false), +) + +#let default-language = ("en", "de", "fr", "es", "it", "pt", "zh") + +#let default-config-options = ( + part-numbering: "1", + open-right: true, +) + +#let default-fonts = ( + body: "New Computer Modern", + math: "New Computer Modern Math", + raw: "Cascadia Code" +) + +#let default-colors = ( + primary: rgb("#c1002a"), + secondary: rgb("#dddddd").darken(15%), + boxeq: rgb("#dddddd"), + header: black, +) + +// Default Title page +#let default-title-page = context { + set page( + paper: paper-size, + header: none, + footer: none, + margin: auto + ) + + align(center + horizon)[ + #text(size: 3em, fill: states.colors.get().primary)[*#states.title.get()*] + #v(1em) + #text(size: 1.5em)[#states.author.get()] + ] +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/bookly-environments.typ b/packages/preview/bookly/2.0.0/src/bookly-environments.typ new file mode 100644 index 0000000000..8d14c2c4b7 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/bookly-environments.typ @@ -0,0 +1,65 @@ +#import "bookly-defaults.typ": * + +#let front-matter(body) = context { + set heading(numbering: none) + set page(numbering: "i") + states.page-numbering.update("i") + states.num-pattern.update(none) + states.isfrontmatter.update(true) + + counter(page).update(1) + + body +} + +// Main matter +#let main-matter(body) = context { + set heading(numbering: "1.1.") + + let numbering = "1/1" + set page(numbering: numbering) + + states.isfrontmatter.update(false) + states.page-numbering.update("1/1") + states.num-heading.update("1") + states.num-pattern.update("1.1.") + states.num-pattern-fig.update("1.1a") + states.num-pattern-eq.update("(1.1a)") + + if states.tufte.get() or not states.open-right.get() or not states.isfrontmatter.get() { + counter(page).update(1) + } else { + counter(page).update(0) + } + + body +} + +// Back matter +#let back-matter(body) = { + set page(header: none, footer: none) + + body +} + +// Appendix +#let appendix(body) = context { + set heading(numbering: "A.1.") + + // Reset heading counter + counter(heading.where(level: 1)).update(0) + + // Reset heading counter for the table of contents + counter(heading).update(0) + + // Update states for chapter function + states.isfrontmatter.update(false) + states.num-heading.update("A") + states.num-pattern.update("A.1.") + states.num-pattern-fig.update("A.1") + states.num-pattern-subfig.update("A.1a") + states.num-pattern-eq.update("(A.1a)") + states.isappendix.update(true) + + body +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/bookly-helper.typ b/packages/preview/bookly/2.0.0/src/bookly-helper.typ new file mode 100644 index 0000000000..a720d20665 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/bookly-helper.typ @@ -0,0 +1,387 @@ +#import "@preview/subpar:0.2.2" +#import "@preview/suboutline:0.3.0": * +#import "bookly-defaults.typ": * + +// Reset counters +#let reset-counters = context { + counter(math.equation).update(0) + counter(figure.where(kind: image)).update(0) + counter(figure.where(kind: table)).update(0) + if states.tufte.get(){ + states.sidenotecounter.update(1) + } + counter(footnote).update(0) +} + +// Conditional set-show +#let show-if(cond, func) = body => if cond { func(body) } else { body } + +// Fullwidth block +#let fullwidth(dx: 0%, body) = context if states.tufte.get() { + block(width: 144% + dx, body) +} else { + block(width: 100% + dx, body) +} + +// Headings +#let headings-on-odd-page(it) = { + show heading.where(level: 1): it => { + { + set page(header: none, footer: none) + pagebreak(to: "odd") + } + it + } + it +} + +// Equations +#let boxeq(body) = context { +set align(center) + box( + stroke: 1pt + states.colors.get().boxeq.darken(35%), + radius: 5pt, + inset: 0.6em, + fill: states.colors.get().boxeq, + )[#body] +} + +#let nonumeq(x) = { + set math.equation(numbering: none) + x +} + +// Subfigure +#let subfigure = subpar.grid.with( + gap: 1em, + numbering: n => {numbering(states.num-pattern-fig.get(), counter(heading).get().first() , n)}, + numbering-sub-ref: (m, n) => {numbering(states.num-pattern-subfig.get(), counter(heading).get().first(), m, n)}, + supplement: fig-supplement, + show-sub: it => {set figure.caption(position: bottom); it} +) + +// Long and short captions for figures or tables +#let ls-caption(long, short) = context if states.in-outline.get() { short } else { long } + +// Book title page +#let book-title-page( + subtitle: "Book subtitle", + edition: "First edition", + institution: "Institution", + series: "Discipline", + year: datetime.today().year(), + cover: none, + logo: none +) = context { + let header = { + box(fill: states.colors.get().primary, width: 100%, inset: 1em)[ + #set align(center + horizon) + #text(fill: white, size: 1.5em)[#strong(delta: 400)[#series]] + ] + } + + let footer = { + box(fill: states.colors.get().primary, width: 100%, inset: 1em)[ + #set align(center + horizon) + #text(fill: white, size: 1.5em)[#strong(delta: 400)[#institution]] + ] + } + + set page( + paper: paper-size, + header: header, + footer: footer, + margin: (left: 0em, right:0em, top: 4em, bottom: 4em) + ) + + let title-page = context { + + align(horizon)[ + #move(dx: 2em)[ + #line(stroke: 1.5pt + states.colors.get().primary, length: 90%) + #v(1em) + ] + + #move(dx: 4em)[ + #text(size: 3em)[*#states.title.get()*] + #linebreak() + + #if subtitle != none { + v(0.5em) + text(size: 1.75em)[#subtitle] + linebreak() + v(0.5em) + } + + #if edition != none { + v(0.5em) + text(size: 1.25em)[_ #edition _] + linebreak() + v(0.5em) + } + + #v(0.5em) + #text(size: 1.5em)[#states.author.get()] + ] + + #move(dx: 2em)[ + #v(1em) + #line(stroke: 1.5pt + states.colors.get().primary, length: 90%) + ] + + #if cover != none { + v(1.5em) + align(center)[#cover] + } + ] + + set page( + paper: paper-size, + header: none, + footer: none, + margin: auto + ) + + if states.open-right.get() { + pagebreak(to: "odd") + } + + align(center + horizon)[ + #text(size: 3em)[*#states.title.get()*] + + #if subtitle != none { + v(-1.5em) + text(size: 1.75em)[#subtitle] + linebreak() + v(0.5em) + } + + #if edition != none { + v(0.5em) + text(size: 1.25em)[_ #edition _] + linebreak() + v(0.5em) + } + ] + + if logo != none { + set image(width: 35%) + place(bottom + center, dy: -4em, logo) + } + + place(bottom)[ + #text(size: 0.85em)[#states.localization.get().version-usage \ #sym.copyright #states.author.get(), #year.] + ] + } + + title-page +} + +// Thesis title page +#let thesis-title-page( + type: "phd", + school: "School name", + doctoral-school: "Name of the doctoral school", + supervisor: ("Supervisor name",), + cosupervisor: none, + laboratory: "Laboratory name", + defense-date: "01 January 1970", + discipline: "Discipline", + specialty: "Specialty", + committee: (:), + logo: none +) = context { + set page( + paper: paper-size, + header: none, + footer: none, + margin: auto + ) + + place(top + left, dx: -16%, dy: -10%, + rect(fill: states.colors.get().primary, height: 121%, width: 20%) + ) + + let title-page = { + if logo != none { + set image(width: 35%) + place(top + right, dx: 0%, dy: -15%, logo) + } + text([#states.localization.get().doctoral-school #h(0.25em) #doctoral-school], size: 1.25em) + v(0.25em) + text(school, size: 1.25em) + + v(0.25em) + text(laboratory, size: 1.25em) + v(2em) + if type.contains("phd") { + text([*#states.localization.get().phd*], size: 1.5em) + } else { + text([*#states.localization.get().habilitation*], size: 1.5em) + } + v(0.25em) + text([_ #states.localization.get().authored _ *#states.author.get()*], size: 1.15em) + v(0.25em) + text([_ #states.localization.get().defended _ *#defense-date*], size: 1.15em) + v(0.25em) + text([_ #states.localization.get().discipline _ *#discipline*], size: 1.1em) + v(0.15em) + text([_ #states.localization.get().specialty _ *#specialty*], size: 1.1em) + v(2em) + line(stroke: 1.75pt + states.colors.get().primary, length: 104%) + align(center)[#text(strong(states.title.get()), size: 2em)] + line(stroke: 1.75pt + states.colors.get().primary, length: 104%) + v(1em) + + if supervisor != none { + let n = supervisor.len() + let dir = none + if n == 1 { + if type.contains("phd") { + dir = states.localization.get().supervisor + } else { + dir = states.localization.get().sponsor + } + } else { + if type.contains("phd") { + dir = states.localization.get().supervisors + } else { + dir = states.localization.get().sponsors + } + } + + let names-dir = () + for director in supervisor { + names-dir.push(director) + } + + text(1.15em)[#dir *#names-dir.join(",", last: states.localization.get().and)*] + v(0.5em) + } + + if cosupervisor != none { + let m = cosupervisor.len() + let codir = none + if m == 1 { + if type.contains("phd") { + codir = states.localization.get().cosupervisor + } else { + codir = states.localization.get().cosponsor + } + } else { + if type.contains("phd") { + codir = states.localization.get().cosupervisors + } else { + codir = states.localization.get().cosponsors + } + } + + let names-codir = () + for codirector in cosupervisor { + names-codir.push(codirector) + } + + text(1.15em)[#codir *#names-codir.join(",", last: states.localization.get().and)*] + v(0.5em) + } + + if committee.len() > 0 { + v(1fr) + align(center)[ + #text([*#states.localization.get().committee*]) + #v(0.5em) + #set text(size: 0.9em) + #grid( + columns: 4, + column-gutter: 1.5em, + row-gutter: 1em, + align: left, + stroke: none, + ..for (name, position, affiliation, role) in committee { + ([*#name*], position, affiliation, role) + }, + ) + ] + + v(1fr) + } + } + + place(dx: 8%, dy: 11%, + block( + height: 100%, + width: 100%, + breakable: false, + title-page + ) + ) +} + +// Back cover +#let back-cover(abstracts: (), logo: none) = context{ + set page(margin: auto, header: none, footer: none) + + if states.open-right.get() { + pagebreak(to: "even", weak: true) + } + + set align(horizon) + + if logo != none { + grid(columns : logo.len(), + column-gutter: 1fr, + ..logo.map((logos) => logos) + ) + } + + context{ + v(2em) + align(center)[ + #text([*#states.author.get()*], size: 1.5em, fill: states.colors.get().primary) + + #text([*#states.title.get()*], size: 1.25em) + + #v(1em) + ] + } + + for abstract in abstracts { + context{ + block( + width: 100%, + stroke: 1pt + states.colors.get().primary, + inset: 1em, + radius: 0.5em, + below: 2em + )[ + #text([*#abstract.title * #abstract.text], size: 0.9em) + ] + } + } +} + +// Boxes - Utility +#let box-title(a, b) = { + grid(columns: 2, column-gutter: 0.5em, align: (horizon), + a, + b + ) +} + +#let colorize(svg, color) = { + let blk = black.to-hex(); + if svg.contains(blk) { + svg.replace(blk, color.to-hex()) + } else { + svg.replace(" context { + let dxl = 0% + let dxr = 0% + if states.tufte.get() { + dxl = 8.17% + dxr = -17% + } + show: move.with(dx: dxl) + fullwidth(dx: dxr, it) + } + outline(title: context states.localization.get().toc, indent: 1em) +} + +// List of figures +#let listoffigures = context { + show outline.entry: it => context { + let dxl = 0% + let dxr = 0% + if states.tufte.get() { + dxl = 8.17% + dxr = -17% + } + show: move.with(dx: dxl) + let entry = context { + let prev-outline-state = states.in-outline.get() + states.in-outline.update(true) + it + states.in-outline.update(prev-outline-state) + } + fullwidth(dx: dxr, entry) + } + outline(title: context states.localization.get().lof, target: figure.where(kind: image)) +} + +// List of tables +#let listoftables = context { + show outline.entry: it => context { + let dxl = 0% + let dxr = 0% + if states.tufte.get() { + dxl = 8.17% + dxr = -17% + } + show: move.with(dx: dxl) + let entry = context { + let prev-outline-state = states.in-outline.get() + states.in-outline.update(true) + it + states.in-outline.update(prev-outline-state) + } + fullwidth(dx: dxr, entry) + } + outline(title: context states.localization.get().lot, target: figure.where(kind: table)) +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/bookly-themes.typ b/packages/preview/bookly/2.0.0/src/bookly-themes.typ new file mode 100644 index 0000000000..c4f8bb2a26 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/bookly-themes.typ @@ -0,0 +1,68 @@ +#import "themes/fancy.typ": * +#import "themes/modern.typ": * +#import "themes/classic.typ": * +#import "themes/orly.typ": * +#import "themes/pretty.typ": * + +// Part +#let part = it => context if states.theme.get().contains("fancy") { + part-fancy(it) +} else if states.theme.get().contains("modern") { + part-modern(it) +} else if states.theme.get().contains("classic") { + part-classic(it) +} else if states.theme.get().contains("orly") { + part-orly(it) +} else if states.theme.get().contains("pretty") { + part-pretty(it) +} else { + part-classic(it) +} + +// Mini table of contents +#let minitoc = context if states.theme.get().contains("fancy") { + minitoc-fancy +} else if states.theme.get().contains("modern") { + minitoc-modern +} else if states.theme.get().contains("classic") { + minitoc-classic +} else if states.theme.get().contains("orly") { + minitoc-orly +} else if states.theme.get().contains("pretty") { + minitoc-pretty +} else { + minitoc-classic +} + +// Custom box +#let custom-box(title: none, icon: "info", color: rgb(29, 144, 208), body) = context if states.theme.get().contains("fancy") { + custom-box-fancy(title: title, icon: icon, color: color, body) +} else if states.theme.get().contains("modern") { + custom-box-modern(title: title, icon: icon, color: color, body) +} else if states.theme.get().contains("classic") { + custom-box-classic(title: title, icon: icon, color: color, body) +} else if states.theme.get().contains("orly") { + custom-box-orly(title: title, icon: icon, color: color, body) +} else if states.theme.get().contains("pretty") { + custom-box-pretty(title: title, icon: icon, color: color, body) +} else { + custom-box-classic(title: title, icon: icon, color: color, body) +} + +// Information box +#let info-box = custom-box.with(title: context states.localization.get().note) + +// Tip box +#let tip-box = custom-box.with(title: context states.localization.get().tip, icon: "tip", color: rgb(0, 166, 81)) + +// Warning box +#let warning-box = custom-box.with(title: context states.localization.get().warning, icon: "alert", color: orange) + +// Important box +#let important-box = custom-box.with(title: "Important", icon: "stop", color: rgb("#f74242")) + +// Proof box +#let proof-box = custom-box.with(title: context states.localization.get().proof, icon: "report", color: eastern) + +// Question box +#let question-box = custom-box.with(title: "Question", icon: "question", color: purple) \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/bookly-tufte.typ b/packages/preview/bookly/2.0.0/src/bookly-tufte.typ new file mode 100644 index 0000000000..6e37a10a4c --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/bookly-tufte.typ @@ -0,0 +1,101 @@ +#import "@preview/drafting:0.2.2": * +#import "bookly-defaults.typ": * +#import "bookly-helper.typ": * + +#let sidefigure(content, label: none, caption: none, dy: - 1.5em) = context if states.tufte.get() { + margin-note( + context { + show figure.caption: it => context { + set align(left) + set text(0.9em) + let kind + if it.supplement.text.contains("Fig") { + kind = image + } else if it.supplement.text.contains("Tab") { + kind = table + } + [#it.supplement #counter(figure.where(kind: kind)).display() #it.separator #it.body] + } + set figure.caption(position: bottom) + [#figure( + content, + caption: caption + )#label] + }, dy: dy + ) +} else { + [#figure( + content, + caption: caption + )#label] +} + +#let fullfigure(content, caption: none, label: none) = context if states.tufte.get() { + fullwidth({ + set figure.caption(position: bottom) + show figure.caption: it => context move(dx: 37.6%, dy: -0.75em)[ + #set text(0.85em) + #set align(left) + #let kind = none + #if it.supplement.text.contains("Fig") { + kind = image + } else if it.supplement.text.contains("Tab") { + kind = table + } + #block(width: 4.5cm)[ + #it.supplement #counter(figure.where(kind: kind)).display() #it.separator #it.body + ] + ] + set figure.caption(position: bottom) + [#figure( + content, + caption: caption, + )#label] + } + ) +} else { + [#figure( + content, + caption: caption, + )#label] +} + +// Code from tufte-memo - thanks @nogula +#let sidenote(dy: -1.5em, numbered: true, label: none, content) = context if states.tufte.get() { + if numbered { + // Create a metadata entry for the sidenote + [#metadata("sidenote")#label] + + // Update the sidenote counter + states.sidenotecounter.step() + let n = states.sidenotecounter.display() + + // Display the sidenote reference + marginal content + super(n) + text(size: 0.9em, margin-note([#super(n) #content], dy: dy)) + } else { + text(size: 0.9em, margin-note(content, dy: dy)) + } +} else { + [#footnote(content)#label] +} + +#let sidecite(dy: -1.5em, supplement: none, key) = context if states.tufte.get() { + let elems = query(bibliography) + if elems.len() > 0 { + cite(key, supplement: supplement) + margin-note( + { + set text(0.9em) + cite(key, form: "full", style: "resources/short_ref.csl") + }, + dy: dy + ) + } +} else { + show cite: it => context{ + show regex("\[|\]"): it => text(fill: black)[#it] + text(fill: states.colors.get().primary)[#it] + } + cite(key, supplement: supplement) +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/bookly.typ b/packages/preview/bookly/2.0.0/src/bookly.typ new file mode 100644 index 0000000000..caf5bcd836 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/bookly.typ @@ -0,0 +1,173 @@ +// Exported packages +#import "@preview/equate:0.3.2": * +// Internals +#import "bookly-environments.typ": * +#import "bookly-outlines.typ": * +#import "bookly-components.typ": * +#import "bookly-helper.typ": * +#import "bookly-tufte.typ": * +#import "bookly-themes.typ": * + +// Template +#let bookly( + title: "Title", + author: "Author Name", + theme: fancy, + tufte: false, + logo: none, + lang: "en", + fonts: default-fonts, + colors: default-colors, + title-page: default-title-page, + config-options: default-config-options, + body +) = context { + // Document's properties + set document(author: author, title: title) + states.author.update(author) + states.title.update(title) + states.tufte.update(tufte) + + // Book colors + let book-colors = default-colors + colors + states.colors.update(book-colors) + + // Configuration options + let book-options = default-config-options + config-options + states.open-right.update(book-options.open-right) + states.part-numbering.update(book-options.part-numbering) + + // Fonts + set text(font: fonts.body, lang: lang, size: text-size, ligatures: false) + + // Math font + show math.equation: set text(font: fonts.math, stylistic-set: 1) + // Unnumbered equations + show selector(): set math.equation(numbering: none) + + // Equations + show: equate.with(breakable: true, sub-numbering: true) + + // Paragraphs + set par(justify: true) + + // Localization + let bookly-lang = if default-language.contains(lang) { + lang + } else { + "en" + } + states.localization.update(json("resources/i18n/" + bookly-lang + ".json")) + + + // References + set ref(supplement: none) + + show ref: it => { + if tufte { + let target = query(it.target).first() + if ( + type(target) != content + or target.func() != metadata + or target.value != "sidenote" + ) { return it } + let count = numbering("1", ..states.sidenotecounter.at(locate(it.target))) + link(it.target)[#super(count)] + } else { + it + } + } + + // Citations + show cite: it => { + show regex("\[|\]"): it => text(fill: black)[#it] + it + } + + // Footnotes + // show footnote.entry: it => { + // [#h(it.indent) #text(fill: book-colors.primary, it.note) #it.note.body] + // } + + // Outline entries + set outline(depth: 3) + + // Figures + let numbering-fig = n => { + let h1 = counter(heading).get().first() + numbering(states.num-pattern-fig.get(), h1, n) + } + + show figure.where(kind: image): set figure( + supplement: fig-supplement, + numbering: numbering-fig, + gap: 1.5em + ) + + set figure.caption(position: top) if tufte + show: show-if(tufte, it => { + show figure.caption: content => margin-note({ + text(size: 0.9em, content) + } + ) + it + }) + show figure: set figure.caption(separator: [ -- ]) + + // Equations + let numbering-eq = (..n) => { + let h1 = counter(heading).get().first() + numbering(states.num-pattern-eq.get(), h1, ..n) + } + + set math.equation(numbering: numbering-eq) + + // Tables + show figure.where(kind: table): set figure( + numbering: numbering-fig, + ) + + show figure.where(kind: table): it => { + set figure.caption(position: top) + it + } + + // Title page + if title-page != none { + title-page + } else { + default-title-page + } + + // Page properties for tufte layout + if tufte { + set-margin-note-defaults( + stroke: none, + side: right, + margin-right: 5.5cm, + margin-left: -1.5cm, + ) + } else { + set-margin-note-defaults(stroke: none) + } + + set page( + margin: ( + left: 1.47cm, + right: 6.93cm + ) + ) if tufte + + // Headings + show: theme.with(colors: book-colors) + show: show-if(book-options.open-right, it => { + show: headings-on-odd-page + it + }) + + // Unnumbered sections - Thanks to @bluss (Typst universe: How to have headings without numbers in a fluent way?) + show selector(): set heading(numbering: none) + + body +} + diff --git a/packages/preview/bookly/2.0.0/src/resources/i18n/de.json b/packages/preview/bookly/2.0.0/src/resources/i18n/de.json new file mode 100644 index 0000000000..8ead9a5915 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/i18n/de.json @@ -0,0 +1,28 @@ +{ + "and": " und ", + "appendix": "Anhang", + "authored": "vorgelegt von", + "chapter": "Kapitel", + "committee": "Zusammensetzung des Prüfungsausschusses", + "cosupervisor": "Ko-Betreuer:", + "cosupervisors": "Ko-Betreuer:", + "defended": "verteidigt am", + "discipline": "Fachrichtung:", + "doctoral-school": "GRADUIERTENSCHULE", + "habilitation": "Habilitation zur Leitung von Forschungsarbeiten", + "lof": "Abbildungsverzeichnis", + "lot": "Tabellenverzeichnis", + "note": "Hinweis", + "part": "Teil", + "phd": "Doktorarbeit", + "proof": "Beweis", + "specialty": "Spezialisierung:", + "sponsor": "Bürge:", + "sponsors": "Bürgen:", + "supervisor": "Betreuer:", + "supervisors": "Betreuer:", + "tip": "Tipp", + "toc": "Inhaltsverzeichnis", + "version-usage": "Diese Version kann kostenlos für den persönlichen Gebrauch eingesehen und heruntergeladen werden. Sie darf nicht weitergegeben, verkauft oder in abgeleiteten Arbeiten verwendet werden.", + "warning": "Warnung" +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/resources/i18n/en.json b/packages/preview/bookly/2.0.0/src/resources/i18n/en.json new file mode 100644 index 0000000000..015526d0ce --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/i18n/en.json @@ -0,0 +1,28 @@ +{ + "and": " and ", + "appendix": "Appendix", + "authored": "authored by", + "chapter": "Chapter", + "committee": "Defense committee", + "cosupervisor": "Co-supervisor:", + "cosupervisors": "Co-supervisors:", + "defended": "defended on", + "discipline": "Discipline:", + "doctoral-school": "DOCTORAL SCHOOL", + "habilitation": "French Habilitation to supervise research", + "lof": "List of figures", + "lot": "List of tables", + "note": "Note", + "part": "Part", + "phd": "Doctoral thesis", + "proof": "Proof", + "specialty": "Specialty:", + "sponsor": "Sponsor:", + "sponsors": "Sponsors:", + "supervisor": "Supervisor:", + "supervisors": "Supervisors:", + "tip": "Tip", + "toc": "Table of contents", + "version-usage": "This version of can be viewed and downloaded free of charge for personal use only. It must not be redistributed, sold, or used in derivative works.", + "warning": "Warning" +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/resources/i18n/es.json b/packages/preview/bookly/2.0.0/src/resources/i18n/es.json new file mode 100644 index 0000000000..794f803529 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/i18n/es.json @@ -0,0 +1,28 @@ +{ + "and": " y ", + "appendix": "Apéndice", + "authored": "realizada por", + "chapter": "Capítulo", + "committee": "Tribunal de tesis", + "cosupervisor": "Codirector de tesis:", + "cosupervisors": "Codirectores de tesis:", + "defended": "defendida en", + "discipline": "Disciplina:", + "doctoral-school": "ESCUELA DE DOCTORADO", + "habilitation": "Habilitación para dirigir investigación", + "lof": "Índice de figuras", + "lot": "Índice de tablas", + "note": "Nota", + "part": "Parte", + "phd": "Tesis doctoral", + "proof": "Prueba", + "sponsor": "Financiado por:", + "sponsors": "Financiado por:", + "specialty": "Especialidad:", + "supervisor": "Director de tesis:", + "supervisors": "Directores de tesis:", + "tip": "Recomendación", + "toc": "Índice general", + "version-usage": "Esta versión puede ser consultada y descargada de forma gratuita exclusivamente para uso personal. Queda prohibida su redistribución, venta o uso en obras derivadas.", + "warning": "Advertencia" +} diff --git a/packages/preview/bookly/2.0.0/src/resources/i18n/fr.json b/packages/preview/bookly/2.0.0/src/resources/i18n/fr.json new file mode 100644 index 0000000000..f6c68ab3ef --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/i18n/fr.json @@ -0,0 +1,28 @@ +{ + "and": " et ", + "appendix": "Annexe", + "authored": "présentée par", + "chapter": "Chapitre", + "committee": "Composition du jury", + "cosupervisor": "Co-encadrant :", + "cosupervisors": "Co-encadrants :", + "defended": "soutenue le", + "discipline": "Discipline :", + "doctoral-school": "ÉCOLE DOCTORALE", + "habilitation": "Habilitation à diriger des recherches", + "lof": "Table des figures", + "lot": "Table des tableaux", + "note": "Remarque", + "part": "Partie", + "phd": "Thèse de doctorat", + "proof": "Démonstration", + "specialty": "Spécialité :", + "sponsor": "Garant :", + "sponsors": "Garants :", + "supervisor": "Directeur de thèse :", + "supervisors": "Directeurs de thèse :", + "tip": "Astuce", + "toc": "Table des matières", + "version-usage": "Cette version de peut être consultée et téléchargée gratuitement pour un usage personnel uniquement. Elle ne doit pas être redistribuée, vendue ou utilisée dans des travaux dérivés.", + "warning": "Attention" +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/resources/i18n/it.json b/packages/preview/bookly/2.0.0/src/resources/i18n/it.json new file mode 100644 index 0000000000..90151d9f7d --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/i18n/it.json @@ -0,0 +1,28 @@ +{ + "and": " e ", + "appendix": "Appendice", + "authored": "presentata da", + "chapter": "Capitolo", + "committee": "Composizione della commissione", + "cosupervisor": "Co-relatore:", + "cosupervisors": "Co-relatori:", + "defended": "discussa il", + "discipline": "Disciplina:", + "doctoral-school": "SCUOLA DI DOTTORATO", + "habilitation": "Abilitazione alla direzione della ricerca", + "lof": "Elenco delle figure", + "lot": "Elenco delle tabelle", + "note": "Nota", + "part": "Parte", + "phd": "Tesi di dottorato", + "proof": "Dimostrazione", + "specialty": "Specialità:", + "sponsor": "Garante:", + "sponsors": "Garanti:", + "supervisor": "Direttore della tesi:", + "supervisors": "Direttori della tesi:", + "tip": "Suggerimento", + "toc": "Indice", + "version-usage": "Questa versione può essere consultata e scaricata gratuitamente solo per uso personale. Non deve essere ridistribuita, venduta o utilizzata in lavori derivati.", + "warning": "Attenzione" +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/resources/i18n/pt.json b/packages/preview/bookly/2.0.0/src/resources/i18n/pt.json new file mode 100644 index 0000000000..d7f34eef37 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/i18n/pt.json @@ -0,0 +1,28 @@ +{ + "and": " e ", + "appendix": "Apêndice", + "authored": "apresentada por", + "chapter": "Capítulo", + "committee": "Composição da banca", + "cosupervisor": "Co-orientador:", + "cosupervisors": "Co-orientadores:", + "defended": "defendida em", + "discipline": "Disciplina:", + "doctoral-school": "ESCOLA DOUTORAL", + "habilitation": "Habilitação para dirigir pesquisas", + "lof": "Lista de figuras", + "lot": "Lista de tabelas", + "note": "Nota", + "part": "Parte", + "phd": "Tese de doutorado", + "proof": "Demonstração", + "specialty": "Especialidade:", + "sponsor": "Garantidor:", + "sponsors": "Garantidores:", + "supervisor": "Orientador:", + "supervisors": "Orientadores:", + "tip": "Dica", + "toc": "Sumário", + "version-usage": "Esta versão pode ser consultada e baixada gratuitamente apenas para uso pessoal. Não deve ser redistribuída, vendida ou utilizada em trabalhos derivados.", + "warning": "Atenção" +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/resources/i18n/zh.json b/packages/preview/bookly/2.0.0/src/resources/i18n/zh.json new file mode 100644 index 0000000000..d64f0cab69 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/i18n/zh.json @@ -0,0 +1,28 @@ +{ + "and": " 和 ", + "appendix": "附录", + "authored": "作者为", + "chapter": "章节", + "committee": "答辩委员会", + "cosupervisor": "共同导师:", + "cosupervisors": "共同导师:", + "defended": "答辩时间", + "discipline": "学科:", + "doctoral-school": "博士学院", + "habilitation": "资格", + "lof": "图表目录", + "lot": "表格目录", + "note": "注释", + "part": "部分", + "phd": "博士论文", + "proof": "证明", + "specialty": "专业:", + "sponsor": "赞助商:", + "sponsors": "赞助商:", + "supervisor": "导师:", + "supervisors": "导师:", + "tip": "提示", + "toc": "目录", + "version-usage": "本版本可免费查看和下载,仅供个人使用。不得转售、转发或用于衍生作品。", + "warning": "警告" +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/resources/images/icons/alert.svg b/packages/preview/bookly/2.0.0/src/resources/images/icons/alert.svg new file mode 100644 index 0000000000..320217dae0 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/images/icons/alert.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/resources/images/icons/info.svg b/packages/preview/bookly/2.0.0/src/resources/images/icons/info.svg new file mode 100644 index 0000000000..ba409a6815 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/images/icons/info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/resources/images/icons/question.svg b/packages/preview/bookly/2.0.0/src/resources/images/icons/question.svg new file mode 100644 index 0000000000..14942352e4 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/images/icons/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/resources/images/icons/report.svg b/packages/preview/bookly/2.0.0/src/resources/images/icons/report.svg new file mode 100644 index 0000000000..66a48dd5a9 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/images/icons/report.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/resources/images/icons/stop.svg b/packages/preview/bookly/2.0.0/src/resources/images/icons/stop.svg new file mode 100644 index 0000000000..670147ea47 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/images/icons/stop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/resources/images/icons/tip.svg b/packages/preview/bookly/2.0.0/src/resources/images/icons/tip.svg new file mode 100644 index 0000000000..61ce790683 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/images/icons/tip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/resources/short_ref.csl b/packages/preview/bookly/2.0.0/src/resources/short_ref.csl new file mode 100644 index 0000000000..1e533b7c18 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/resources/short_ref.csl @@ -0,0 +1,62 @@ + + \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/themes/classic.typ b/packages/preview/bookly/2.0.0/src/themes/classic.typ new file mode 100644 index 0000000000..1b10916a22 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/themes/classic.typ @@ -0,0 +1,211 @@ + +#import "@preview/hydra:0.6.2": hydra +#import "@preview/showybox:2.0.4": * +#import "../bookly-helper.typ": * +#import "../bookly-defaults.typ": * + +#let classic(colors: default-colors, it) = { + states.theme.update("classic") + + // Headings + show heading.where(level: 1): it => { + if not states.open-right.get() { + pagebreak(weak: true) + } + + // Reset counters + reset-counters + + // Heading style + place(top)[ + #rect(fill: white, width: 1%, height: 1%) + ] + set align(left) + let type-chapter = if states.isappendix.get() {states.localization.get().appendix} else {states.localization.get().chapter} + if it.numbering != none { + v(4em) + block[ + #text(size: 1.5em)[#type-chapter #counter(heading).display(states.num-heading.get())] + + #text(2em)[#it.body] + ] + v(3em) + } else { + v(1em) + text(2em)[#it.body] + v(2em) + } + } + + show heading.where(level: 2): it => { + block(above: 2em, below: 1.25em)[ + #it + ] + } + + show heading.where(level: 3): it => { + block(above: 1.25em, below: 1.25em)[ + #it + ] + } + + // Tables + show table.cell.where(y: 0): set text(weight: "bold") + set table( + stroke: (_, y) => ( + top: if y <= 1 {0.75pt} else {0pt}, + bottom: 0.75pt + ), + ) + + // Outline + set outline.entry(fill: box(width: 1fr, repeat(gap: 0.25em)[.])) + show outline.entry: it => { + show linebreak: none + if it.element.func() == heading { + let number = it.prefix() + let section = it.element.body + let item = none + if it.level == 1 { + block(above: 1.25em, below: 0em) + v(0.5em) + item = [*#number #it.inner()*] + } else if it.level == 2 { + block(above: 1em, below: 0em) + item = [#h(1em) #number #it.inner()] + } else { + block(above: 1em, below: 0em) + item = [#h(2em) #number #it.inner()] + } + link(it.element.location(), item) + } else if it.element.func() == figure { + block(above: 1.25em, below: 0em) + v(0.25em) + link(it.element.location(), [#it.prefix(). #h(0.2em) #it.inner()]) + } else { + it + } + } + + // Page style + let page-header = context { + show linebreak: none + show: fullwidth + if calc.odd(here().page()) { + align(left, hydra(2, display: (_, it) => [ + #let head = none + #if it.numbering != none { + head = numbering(it.numbering, ..counter(heading).at(it.location())) + " " + it.body + } else { + head = it.body + } + #head + #place(dx: 0%, dy: 52%)[#line(length: 100%, stroke: 0.75pt)] + ])) + } else { + align(left, hydra(1, display: (_, it) => [ + #let head = counter(heading.where(level:1)).display() + " " + it.body + #if it.numbering == none { + head = it.body + } + #head + #place(dx: 0%, dy: 50%)[#line(length: 100%, stroke: 0.75pt)] + ])) + } + } + + let page-footer = context { + let cp = counter(page).get().first() + let current-page = counter(page).display() + let dx = 0% + if states.tufte.get() { + dx = 21.65% + } + set align(center) + move(dx: dx, current-page) + } + + set page( + paper: paper-size, + header: page-header, + footer: page-footer + ) + + it +} + +// Boxes - Definitions +#let custom-box-classic(title: none, icon: "info", color: rgb(29, 144, 208), body) = { + showybox( + title: box-title(color-svg("resources/images/icons/" + icon + ".svg", white, width: 1em), [*#title*]), + title-style: ( + boxed-style: ( + anchor: (x: left, y: horizon), + offset: (x: -1em, y: 1.15em), + radius: (top-left: 0pt, top-right: 0pt, bottom-left: 0pt, bottom-right: 5pt) + ) + ), + frame: ( + title-color: color, + border-color: color, + body-color: color.lighten(90%), + thickness: 2pt, + body-inset: (top:2em, left: 1em, right: 1em, bottom: 1em) + ), + align: center, + breakable: true + )[#body] +} + +// Part +#let part-classic(title) = context { + states.counter-part.update(i => i + 1) + set page( + header: none, + footer: none, + numbering: none + ) + + set align(center + horizon) + + if states.open-right.get() { + pagebreak(weak: true, to:"odd") + } + + let dx = 0% + if states.tufte.get() { + dx = 21.68% + } + + move(dx: dx)[ + #text(size: 2.5em)[#states.localization.get().part #states.counter-part.display(states.part-numbering.get())] + #v(1em) + #text(size: 3em)[*#title*] + ] + + show heading: none + heading(numbering: none)[ + #v(1em) + #box[#states.localization.get().part #states.counter-part.display(states.part-numbering.get()) -- #title] + ] + + if states.open-right.get() { + pagebreak(weak: true, to:"odd") + } +} + +#let minitoc-classic = context { + let toc-header = states.localization.get().toc + block(above: 3.5em)[ + #text([*#toc-header*]) + #v(-0.5em) + ] + + let miniline = line(stroke: 0.75pt, length: 100%) + + miniline + v(0.5em) + suboutline(target: heading.where(outlined: true, level: 2)) + miniline +} + diff --git a/packages/preview/bookly/2.0.0/src/themes/fancy.typ b/packages/preview/bookly/2.0.0/src/themes/fancy.typ new file mode 100644 index 0000000000..c56692b22a --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/themes/fancy.typ @@ -0,0 +1,242 @@ + +#import "@preview/hydra:0.6.2": hydra +#import "@preview/showybox:2.0.4": * +#import "@preview/itemize:0.2.0" as el +#import "../bookly-helper.typ": * +#import "../bookly-defaults.typ": * + +#let fancy(colors: default-colors, it) = { + states.theme.update("fancy") + + // Headings + show heading.where(level: 1): it => { + if not states.open-right.get() { + pagebreak(weak: true) + } + + // Reset counters + reset-counters + + // Heading style + place(top)[ + #rect(fill: white, width: 1%, height: 1%) + ] + set align(right) + set underline(stroke: 2pt + colors.secondary, offset: 8pt) + let dx = 0% + if states.tufte.get() { + dx = 35.2% + } + show: move.with(dx: dx) + if it.numbering != none { + v(5em) + block[ + #text(counter(heading).display(states.num-heading.get()), size: 4em, fill: colors.primary) + #v(-3em) + #text(underline(it.body), size: 1.5em) + ] + v(5em) + } else { + v(1em) + text(underline(it.body), size: 1.5em) + v(2.5em) + } + } + + show heading.where(level: 2): it => { + block(above: 1.5em)[ + #if it.numbering != none { + text(counter(heading).display(), fill: colors.primary) + h(0.25em) + } + #text(it.body) + #v(-0.5em) + #line(stroke: 1.5pt + colors.secondary, length: 100%) + #v(0.75em) + ] + } + + show heading.where(level: 3): it => { + block[ + #if it.numbering != none { + text(counter(heading).display(), fill: colors.primary) + h(0.25em) + } + #text(it.body) + #v(1em) + ] + } + + // Lists + show: el.default-enum-list + set list(marker: [#text(fill:colors.primary, size: 1.1em)[#sym.bullet]]) + set enum(numbering: n => text(fill:book-colors.primary)[#n.]) + + // Footnotes + set footnote.entry(separator: line(length: 30% + 0pt, stroke: 1pt + colors.secondary)) + + // References + show ref: set text(fill: colors.primary) + + // Tables + show table.cell.where(y: 0): set text(weight: "bold", fill: white) + set table( + fill: (_, y) => if y == 0 {colors.primary} else if calc.odd(y) { colors.secondary.lighten(60%)}, + stroke: (x, y) => ( + left: if x == 0 or y > 0 { (thickness: 1pt, paint: colors.secondary) } else { (thickness: 1pt, paint: colors.primary) }, + right: (thickness: 1pt, paint: colors.secondary), + top: if y <= 1 { (thickness: 1pt, paint: colors.secondary) } else { 0pt }, + bottom: (thickness: 1pt, paint: colors.secondary), + ) + ) + + // Outline + set outline.entry(fill: box(width: 1fr, repeat(gap: 0.25em)[.])) + show outline.entry: it => { + show linebreak: none + if it.element.func() == heading { + let number = it.prefix() + let section = it.element.body + let item = none + if it.level == 1 { + block(above: 1.25em, below: 0em) + v(0.5em) + item = [#text([*#number*], fill: colors.primary) *#it.inner()*] + } else if it.level == 2{ + block(above: 1em, below: 0em) + item = [#h(1em) #text([#number], fill: colors.primary) #it.inner()] + } else { + block(above: 1em, below: 0em) + item = [#h(2em) #text([#number], fill: colors.primary) #it.inner()] + } + link(it.element.location(), item) + } else if it.element.func() == figure { + block(above: 1.25em, below: 0em) + v(0.25em) + link(it.element.location(), [#text([#it.prefix().], fill: colors.primary) #h(0.2em) #it.inner()]) + } else { + it + } + } + + // Page style + let page-header = context { + show linebreak: none + show: fullwidth + set text(style: "italic", fill: colors.header) + if calc.odd(here().page()) { + align(right, hydra(2)) + } else { + align(left, hydra(1)) + } + } + + let page-footer = context { + let cp = counter(page).get().first() + let current-page = counter(page).display() + let dx = 0% + let page-final = counter(page).final().first() + if states.tufte.get() { + dx = 21.65% + } + set align(center) + if states.isfrontmatter.get() { + move(dx: dx)[#current-page] + } else { + move(dx: dx)[#current-page/#page-final] + } + } + + set page( + paper: paper-size, + header: page-header, + footer: page-footer + ) + + it +} + +// Boxes - Definitions +#let custom-box-fancy(title: none, icon: "info", color: rgb(29, 144, 208), body) = showybox( + title: grid( + columns: 2, + align: (left + horizon, right + horizon), + column-gutter: 0.5em, + [#color-svg("resources/images/icons/" + icon + ".svg", white)], + [#title] + ), + title-style: ( + boxed-style: ( + anchor: (x: left, y: horizon) + ) + ), + frame: ( + title-color: color, + border-color: color, + body-color: color.lighten(90%), + thickness: 1pt + ), + align: center +)[ + #body + #v(0.5em) +] + +// Part +#let part-fancy(title) = context { + states.counter-part.update(i => i + 1) + set page( + header: none, + footer: none, + numbering: none + ) + + set align(center + horizon) + + if states.open-right.get() { + pagebreak(weak: true, to:"odd") + } + + let dxl = 0% + let dxr = 0% + if states.tufte.get() { + dxl = 21.68% + dxr = 3.1% + } + + move(dx: dxl)[ + #fullwidth(dx: dxr)[ + #line(stroke: 1.75pt + states.colors.get().primary, length: 104%) + #text(size: 2.5em)[#states.localization.get().part #states.counter-part.display(states.part-numbering.get())] + #line(stroke: 1.75pt + states.colors.get().primary, length: 35%) + #text(size: 3em)[*#title*] + #line(stroke: 1.75pt + states.colors.get().primary, length: 104%) + ] + ] + + show heading: none + heading(numbering: none)[ + #v(1em) + #box[#text(fill:states.colors.get().primary)[#states.localization.get().part #states.counter-part.display(states.part-numbering.get()) -- #title]] + ] + + if states.open-right.get() { + pagebreak(weak: true, to:"odd") + } +} + +// Minitoc +#let minitoc-fancy = context { + let toc-header = states.localization.get().toc + block(above: 3.5em)[ + #text([*#toc-header*]) + #v(-0.5em) + ] + + let miniline = line(stroke: 1.5pt + states.colors.get().secondary, length: 100%) + + miniline + v(0.5em) + suboutline(target: heading.where(outlined: true, level: 2)) + miniline +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/src/themes/modern.typ b/packages/preview/bookly/2.0.0/src/themes/modern.typ new file mode 100644 index 0000000000..cf188988b6 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/themes/modern.typ @@ -0,0 +1,281 @@ +#import "@preview/hydra:0.6.2": hydra +#import "@preview/itemize:0.2.0" as el +#import "../bookly-helper.typ": * +#import "../bookly-defaults.typ": * + +#let modern(colors: default-colors, it) = { + states.theme.update("modern") + + // Headings + show heading.where(level: 1): it => context { + if not states.open-right.get() { + pagebreak(weak: true) + } + + // Reset counters + reset-counters + + // Heading style + let type-chapter = if states.isappendix.get() {states.localization.get().appendix} else {states.localization.get().chapter} + + let dxr = 0% + let dxb = 0% + let dxc = 0% + if states.tufte.get() { + dxr = 12.5% + dxb = 35.2% + dxc = 8.2% + } + + if it.numbering != none { + place(top, dx: -16%, dy: - 11%)[ + #fullwidth[#rect(fill: gradient.linear(colors.primary, colors.primary.transparentize(65%), dir: ltr), width: 132% - dxr, height: 35%)] + ] + + place(top, dx: dxc, dy: 10%)[ + #text(size: 2.5em, fill: white)[#type-chapter #counter(heading).display(states.num-heading.get())] + ] + + place(right, dx: dxb, dy: 22.75%)[ + #box(outset: 0.9em, radius: 5em, stroke: none, fill: states.colors.get().primary)[#text(size: 1.5em, fill: white)[#it.body]] + ] + v(15em) + } else { + place(top, dx: -16%, dy: -11%)[ + #fullwidth[#rect(fill: gradient.linear(colors.primary, colors.primary.transparentize(65%), dir: ltr), width: 132% - dxr, height: 10%)] + ] + + place(top + right, dx: dxb, dy: -2.25%)[ + #box(outset: 0.9em, radius: 5em, stroke: none, fill: colors.primary)[#text(size: 1.5em, fill: white)[#it.body]] + ] + + v(3em) + } + } + + show heading.where(level: 2): it => { + block(above: 1.5em)[ + #if it.numbering != none { + text(counter(heading).display(), fill: colors.primary) + h(0.25em) + } + #text(it.body) + #v(-0.75em) + #line(stroke: 0.75pt + colors.primary, length: 100%) + #v(0.75em) + ] + } + + show heading.where(level: 3): it => { + block[ + #if it.numbering != none { + text(counter(heading).display(), fill: colors.primary) + h(0.25em) + } + #text(it.body) + #v(1em) + ] + } + + // Tables + show table.cell.where(y: 0): set text(weight: "bold", fill: white) + set table( + fill: (_, y) => if y == 0 {colors.primary} else if calc.odd(y) {colors.secondary.lighten(60%)}, + stroke: none + ) + + // Lists + show: el.default-enum-list + set list(marker: [#text(fill:colors.primary, size: 1.1em)[#sym.bullet]]) + set enum(numbering: n => text(fill:book-colors.primary)[#n.]) + + // Footnotes + set footnote.entry(separator: line(length: 30% + 0pt, stroke: 0.75pt + colors.primary)) + + // References + show ref: set text(fill: colors.primary) + + // Outline + set outline.entry(fill: box(width: 1fr, repeat(gap: 0.25em)[.])) + show outline.entry: it => { + show linebreak: none + if it.element.func() == heading { + let number = it.prefix() + let section = it.element.body + let item = none + if it.level == 1 { + block(above: 1.25em, below: 0em) + v(0.5em) + item = [#text([*#number*], fill: colors.primary) *#it.inner()*] + } else if it.level == 2{ + block(above: 1em, below: 0em) + item = [#h(1em) #text([#number], fill: colors.primary) #it.inner()] + } else { + block(above: 1em, below: 0em) + item = [#h(2em) #text([#number], fill: colors.primary) #it.inner()] + } + link(it.element.location(), item) + } else if it.element.func() == figure { + block(above: 1.25em, below: 0em) + v(0.25em) + link(it.element.location(), [#text([#it.prefix().], fill: colors.primary) #h(0.2em) #it.inner()]) + } else { + it + } + } + + // Page style + let page-header = context { + show linebreak: none + show: fullwidth + set text(style: "italic", fill: colors.header) + if calc.odd(here().page()) { + align(right)[ + #hydra(2, display: (_, it) => [ + #let head = none + #if it.numbering != none { + head = numbering(it.numbering, ..counter(heading).at(it.location())) + " " + it.body + } else { + head = it.body + } + #let size = measure(head) + #head + #place(dx: -16%, dy: -40%)[#line(length: 115% - size.width, stroke: 0.5pt + colors.primary)] + #place(dx: 98.5% - size.width, dy: -75%)[#circle(fill: colors.primary, stroke: none, radius: 0.25em)] + ]) + ] + } else { + align(left)[ + #hydra(1, display: (_, it) => [ + #let head = counter(heading.where(level:1)).display() + " " + it.body + #if it.numbering == none { + head = it.body + } + #let size = measure(head) + #head + #place(dx: size.width + 1%, dy: -40%)[#line(length: 115%, stroke: 0.5pt + colors.primary)] + #place(dx: size.width, dy: -75%)[#circle(fill: colors.primary, stroke: none, radius: 0.25em)] + ] + ) + ] + } + } + + let page-footer = context { + let cp = counter(page).get().first() + let current-page = counter(page).display() + show: fullwidth + set text(fill: white, weight: "bold") + v(1.5em) + if calc.odd(cp) { + set align(right) + box(outset: 6pt, fill: colors.primary, width: 1.5em, height: 100%)[ + #set align(center) + #current-page + ] + } else { + let dx = 0% + if states.tufte.get() { + dx = 8.2% + } + set align(left) + box(outset: 6pt, fill: colors.primary, width: 1.5em, height: 100%)[ + #set align(center) + #current-page + ] + } + } + + set page( + paper: paper-size, + header: page-header, + footer: page-footer + ) + + it +} + +// Boxes - Definitions +#let custom-box-modern(title: none, icon: "info", color: rgb(29, 144, 208), body) = { + grid( + columns: (auto, 1fr), + column-gutter: 0.75em, + align: top + left, + [ + #v(0.5em) + #color-svg("resources/images/icons/" + icon + ".svg", color, width: 1.5em) + ], + [ + #box( + stroke: (left: 1.25pt + color), + fill: color.lighten(90%), + inset: 1em, + width: 100% + )[#body] + ] + ) +} + +// Part +#let part-modern(title) = context { + states.counter-part.update(i => i + 1) + set page( + header: none, + footer: none, + numbering: none + ) + + set align(center + horizon) + + if states.open-right.get() { + pagebreak(weak: true, to:"odd") + } + + let dxr = 0% + let dxb = 0% + if states.tufte.get() { + dxr = 21.68% + dxb = 36% + } + + place(top + center, dx: dxr, dy: -11%)[ + #fullwidth[#rect(fill: gradient.linear(states.colors.get().primary, states.colors.get().primary.transparentize(55%), dir: ttb), height: 61%, width: 135% + dxr)[ + #set align(center + horizon) + + #text(size: 5em, fill: white)[*#states.localization.get().part #states.counter-part.display(states.part-numbering.get())*] + ]] + ] + + place(center + horizon, dx: dxr)[ + #box(outset: 1.25em, stroke: none, radius: 5em, fill: states.colors.get().primary)[ + #set text(fill: white, weight: "bold", size: 3em) + #title + ] + ] + + show heading: none + heading(numbering: none)[ + #v(1em) + #box[#states.localization.get().part #states.counter-part.display(states.part-numbering.get()) -- #title] + ] + + if states.open-right.get() { + pagebreak(weak: true, to:"odd") + } +} + +#let minitoc-modern = context { + let toc-header = states.localization.get().toc + block(above: 3.5em)[ + #text([*#toc-header*]) + #v(-0.5em) + ] + + let miniline = line(stroke: 0.75pt + states.colors.get().primary, length: 100%) + + miniline + v(0.5em) + suboutline(target: heading.where(outlined: true, level: 2)) + miniline +} + diff --git a/packages/preview/bookly/2.0.0/src/themes/orly.typ b/packages/preview/bookly/2.0.0/src/themes/orly.typ new file mode 100644 index 0000000000..cd780aed0a --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/themes/orly.typ @@ -0,0 +1,185 @@ +#import "@preview/showybox:2.0.4": * +#import "@preview/hydra:0.6.2": hydra, anchor +#import "../bookly-helper.typ": * +#import "../bookly-defaults.typ": * + +#let orly(colors: default-colors, it) = { + states.theme.update("orly") + + show heading.where(level:1): it => { + if not states.open-right.get() { + pagebreak(weak: true) + } + + // Reset counters + reset-counters + + // Heading style + let type-chapter = if states.isappendix.get() {states.localization.get().appendix} else {states.localization.get().chapter} + + set align(right) + + let dx = 0% + if states.tufte.get() { + dx = 43.5% + } + + show: move.with(dx: dx) + if it.numbering != none { + v(1em) + fullwidth[ + #text(size: 1em)[#upper[#type-chapter] #counter(heading).display(states.num-heading.get())] + #v(-0.75em) + #line(length: 100%, stroke: 0.5pt) + #v(-0.1em) + #text(2em)[#it.body] + ] + } else { + fullwidth[ + #line(length: 100%, stroke: 0.5pt) + #v(-0.1em) + #text(2em)[#it.body] + ] + } + v(5em) + } + + // Tables + show table.cell.where(y: 0): set text(weight: "bold", fill: white) + set table( + fill: (_, y) => if y == 0 {black} , + stroke: (_, y) => ( + top: 0pt, + bottom: 0.75pt + ), + ) + + // Outline + set outline.entry(fill: box(width: 1fr, repeat(gap: 0.25em)[.])) + show outline.entry: it => { + show linebreak: none + if it.element.func() == heading { + let number = it.prefix() + let section = it.element.body + let item = none + if it.level == 1 { + v(1em) + item = [*#number #it.inner()*] + } else if it.level == 2 { + block(above: 1em, below: 0em) + item = [#h(1em) #number #it.inner()] + } else { + block(above: 1em, below: 0em) + item = [#h(2em) #number #it.inner()] + } + link(it.element.location(), item) + } else if it.element.func() == figure { + block(above: 1.25em, below: 0em) + link(it.element.location(), [#it.prefix(). #h(0.2em) #it.inner()]) + } else { + it + } + } + + // Page style + let page-footer = context { + let cp = counter(page).get().first() + let current-page = counter(page).display() + show: fullwidth + set text(0.85em, weight: "bold") + line(length: 100%, stroke: 0.5pt) + v(-0.5em) + if calc.odd(cp) { + align(right)[#hydra(2) #h(0.5em) | #h(0.5em) #current-page] + } else { + align(left)[#current-page #h(0.5em) | #h(0.5em) #hydra(1)] + } + } + + set page( + header: anchor(), + footer: page-footer, + ) + + it +} + +// Boxes - Definitions +#let custom-box-orly(title: none, icon: "info", color: rgb(29, 144, 208), body) = { + showybox( + title: box-title(color-svg("resources/images/icons/" + icon + ".svg", color, width: 1em), [*#title*]), + title-style: ( + color: color, + sep-thickness: 0pt, + ), + frame: ( + title-color: color.lighten(85%), + border-color: color, + body-color: none, + thickness: (left: 1.25pt), + radius: 0pt, + ), + breakable: true + )[#body] +} + +// Part +#let part-orly(title) = context { + states.counter-part.update(i => i + 1) + set page( + header: none, + footer: none, + numbering: none + ) + + set align(top + right) + + if states.open-right.get() { + pagebreak(weak: true, to:"odd") + } + + let dx = 0% + if states.tufte.get() { + dx = 43.5% + } + + move(dx: dx)[ + #fullwidth[ + #text(size: 1.75em)[*#upper[#states.localization.get().part] #states.counter-part.display(states.part-numbering.get())*] + #v(-0.75em) + #line(length: 100%, stroke: 0.5pt) + #v(-2.5em) + #text(size: 3em)[*#title*] + ]] + + show heading: none + heading(numbering: none)[ + #set text(1.15em) + #v(1em) + #line(length: 100%, stroke: 0.5pt) + #v(-0.5em) + #box[*#upper[#states.localization.get().part] #states.counter-part.display(states.part-numbering.get()) -- #title*] + ] + + if states.open-right.get() { + pagebreak(weak: true, to:"odd") + } +} + +#let minitoc-orly = context { + let miniline = line(stroke: 0.5pt, length: 100%) + let toc-header = states.localization.get().toc + + block(above: 3.5em)[ + #set align(right) + #miniline + #v(-0.5em) + #text([*#toc-header*]) + #v(0.5em) + ] + + // miniline + v(0.5em) + suboutline(target: heading.where(outlined: true, level: 2)) + miniline +} diff --git a/packages/preview/bookly/2.0.0/src/themes/pretty.typ b/packages/preview/bookly/2.0.0/src/themes/pretty.typ new file mode 100644 index 0000000000..40cd5d8397 --- /dev/null +++ b/packages/preview/bookly/2.0.0/src/themes/pretty.typ @@ -0,0 +1,271 @@ +#import "@preview/showybox:2.0.4": * +#import "@preview/hydra:0.6.2": hydra, anchor +#import "@preview/itemize:0.2.0" as el +#import "../bookly-helper.typ": * +#import "../bookly-defaults.typ": * + +#let pretty(colors: default-colors, it) = { + states.theme.update("pretty") + + // Headings + show heading.where(level: 1): it => { + if not states.open-right.get() { + pagebreak(weak: true) + } + + // Reset counters + reset-counters + + show: fullwidth + + // Heading style + place(top, dy: -2.5em)[ + #rect(fill: white, width: 102%, height: 4%) + ] + set align(left) + let type-chapter = if states.isappendix.get() {states.localization.get().appendix} else {states.localization.get().chapter} + if it.numbering != none { + v(2em) + block[ + #text(size: 1.25em)[#type-chapter] + #v(-1em) + #box(stroke: (top: 1.5pt + colors.primary))[ + #set text(1.6em) + #grid( + columns: (auto, 1fr), + align: (center, left), + inset: (0em, 0.5em), + [#box(fill: colors.primary, width: 1em, radius: (bottom: 0.4em), inset: 0.5em)[#text(fill: white)[#counter(heading).display(states.num-heading.get())]]], + [#it.body] + ) + ] + ] + v(3em) + } else { + set text(1.6em) + box(stroke: (top: 1.5pt + states.colors.get().primary))[ + #grid( + columns: (1em, 1fr), + align: (center, left), + inset: (0em, 0.5em), + [#box(fill: colors.primary, width: 1em, radius: (bottom: 0.4em), inset: 0.5em)[#text(fill: colors.primary)[#counter(heading).display(states.num-heading.get())]]], + [#it.body] + ) + ] + v(1em) + } + } + + show heading.where(level: 2): it => { + block(above: 1.5em)[ + #if it.numbering != none { + text(counter(heading).display(), fill: colors.primary) + h(0.25em) + } + #text(it.body) + #v(-0.75em) + #line(stroke: 0.75pt + colors.primary, length: 100%) + #v(0.75em) + ] + } + + show heading.where(level: 3): it => { + block[ + #if it.numbering != none { + text(counter(heading).display(), fill: colors.primary) + h(0.25em) + } + #text(it.body) + #v(1em) + ] + } + + // Lists + show: el.default-enum-list + set list(marker: [#text(fill:colors.primary, size: 1.1em)[#sym.bullet]]) + set enum(numbering: n => text(fill:book-colors.primary)[#n.]) + + // Footnotes + set footnote.entry(separator: none) + show footnote.entry: it => { + box(width: 100%, stroke: (top: 0.5pt + colors.primary, left: 0.5pt + colors.primary), inset: 0.5em, radius: (top-left: 0.5em))[#it] + } + + // References + show ref: set text(fill: colors.primary) + + // Tables + show table.cell.where(y: 0): set text(weight: "bold", fill: white) + set table( + fill: (_, y) => if y == 0 {colors.primary} , + stroke: (_, y) => if y == 0 {(bottom: 0pt)} else {(bottom: 01pt + colors.secondary)} + ) + show table: it => block( + stroke: 01pt + colors.primary, + radius: 1em, + clip: true + )[#it] + + // Outline + set outline.entry(fill: none) + show outline.entry: it => { + show linebreak: none + if it.element.func() == heading { + let number = it.prefix() + let section = it.element.body + let item = none + if it.level == 1 { + block(above: 1.25em, below: 0em) + v(0.5em) + item = [#text([*#number*], fill: colors.primary) *#it.inner()*] + } else if it.level == 2{ + block(above: 1em, below: 0em) + item = [#h(1em) #text([#number], fill: colors.primary) #it.inner()] + } else { + block(above: 1em, below: 0em) + item = [#h(2em) #text([#number], fill: colors.primary) #it.inner()] + } + link(it.element.location(), item) + } else if it.element.func() == figure { + block(above: 1.25em, below: 0em) + v(0.25em) + link(it.element.location(), [#text([#it.prefix().], fill: colors.primary) #h(0.2em) #it.inner()]) + } else { + it + } + } + + // Page style + let page-header = context { + show linebreak: none + show: fullwidth + set text(style: "italic", fill: white) + if calc.odd(here().page()) { + set align(right) + box(fill: states.colors.get().primary, inset: 0.5em, radius: (top-left: 2em, bottom-right: 2em))[#hydra(2)] + } else { + set align(left) + box(fill: states.colors.get().primary, inset: 0.5em, radius: (bottom-left: 2em, top-right: 2em))[#hydra(1)] + } + } + + let page-footer = context { + let cp = counter(page).get().first() + let current-page = counter(page).display() + let dx = 0% + if states.tufte.get() { + dx = 15.04% + } + set align(center) + show: fullwidth + move(dx: dx)[ + #grid( + columns: (1fr, auto, 1fr), + align: center + horizon, + [#line(length: 100%, stroke: 0.75pt + states.colors.get().primary)], + [#box(stroke: 1pt + states.colors.get().primary, inset: 0.3em, radius: 0.25em)[#current-page]], + [#line(length: 100%, stroke: 0.75pt + states.colors.get().primary)] + ) + ] + } + + set page( + paper: paper-size, + header: page-header, + footer: page-footer + ) + + it +} + +// Boxes - Definitions +#let custom-box-pretty(title: none, icon: "info", color: rgb(29, 144, 208), body) = { + showybox( + title: box-title(color-svg("resources/images/icons/" + icon + ".svg", color, width: 1em), [*#title*]), + title-style: ( + color: color, + sep-thickness: 0pt, + ), + frame: ( + title-color: color.lighten(85%), + border-color: color, + body-color: none, + thickness: 0.75pt, + radius: (top-left: 5em, bottom-right: 5em, rest: 0em), + ), + breakable: true + )[#body] +} + +// Part +#let part-pretty(title) = context { + states.counter-part.update(i => i + 1) + set page( + header: none, + footer: none, + numbering: none + ) + + set align(center + horizon) + + if states.open-right.get() { + pagebreak(weak: true, to:"odd") + } + + let dxl = 0% + let dxr = 0% + if states.tufte.get() { + dxl = 21.68% + dxr = 17% + } + move(dx: dxl)[ + #stack( + dir: ttb, + box(fill: states.colors.get().primary, inset: 1em, radius: (top: 2em))[ + #set text(size: 4.5em, fill: white, weight: "bold") + #states.localization.get().part #states.counter-part.display(states.part-numbering.get()) + ], + fullwidth(dx: -dxr, box(width: 90%, inset: 5em, stroke: 2pt + states.colors.get().primary, radius: 2em)[ + #set text(size: 3em) + + *#title* + ]) + ) + ] + + show heading: none + heading(numbering: none)[ + #v(1em) + #box(width: 95%, stroke: (top: 0.5pt + states.colors.get().primary, left: 0.5pt + states.colors.get().primary), inset: 00.5em, radius: (top-left: 0.5em))[#text(fill:states.colors.get().primary)[#states.localization.get().part #states.counter-part.display(states.part-numbering.get()) -- #title]] + ] + + if states.open-right.get() { + pagebreak(weak: true, to:"odd") + } +} + +// Minitoc +#let minitoc-pretty = context { + let toc-header = states.localization.get().toc + + let miniline = line(stroke: 1pt + states.colors.get().primary, length: 100%) + + let header = block[ + #set text(fill: white) + #box(fill: states.colors.get().primary, inset: 0.5em, radius: (top: 0.25em))[*#toc-header*] + ] + + let body = block[ + #miniline + #v(0.5em) + #suboutline(target: heading.where(outlined: true, level: 2)) + #miniline + ] + + v(3.5em) + stack( + dir: ttb, + header, + body + ) +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/appendix/app1.typ b/packages/preview/bookly/2.0.0/template/appendix/app1.typ new file mode 100644 index 0000000000..a226d23ee5 --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/appendix/app1.typ @@ -0,0 +1,38 @@ +#import "@preview/bookly:2.0.0": * + +// #show: chapter.with( +// title: "Algorithms", +// toc: false +// ) + += Algorithms + +#lorem(100) + +Figure @fig:A is a beautiful typst logo. + +#figure( +image("../images/typst-logo.svg", width: 75%), +caption: [#lorem(10)], +) + +#figure( +table( + columns: 3, + table.header( + [Substance], + [Subcritical °C], + [Supercritical °C], + ), + [Hydrochloric Acid], + [12.0], [92.1], + [Sodium Myreth Sulfate], + [16.6], [104], + [Potassium Hydroxide], + table.cell(colspan: 2)[24.7], +), caption: [#lorem(2)] +) + +== Test + +#lorem(100) \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/appendix/app2.typ b/packages/preview/bookly/2.0.0/template/appendix/app2.typ new file mode 100644 index 0000000000..3fdfd58617 --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/appendix/app2.typ @@ -0,0 +1,36 @@ +// #import "@preview/bookly:1.0.0": * +#import "@preview/bookly:2.0.0": * + +// #show: chapter.with( +// title: "Foundations", +// toc: false +// ) + += Foundations + +#lorem(100) + +$ + #boxeq($bold(y)_(k + 1) = bold(C) space.thin bold(x)_(k + 1)$) +$ + +#nonumeq($ +y(x) = f(x) +$) + +La Figure @fig:B + +#figure( +image("../images/typst-logo.svg", width: 75%), +caption: [#lorem(10)], +) + +La Figure @b3 présente la carte du Cnam. + +#subfigure( +figure(image("../images/typst-logo.svg"), caption: []), +figure(image("../images/typst-logo.svg"), caption: []), , +columns: (1fr, 1fr), +caption: [(a) Left image and (b) Right image], +label: , +) \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/appendix/app_main.typ b/packages/preview/bookly/2.0.0/template/appendix/app_main.typ new file mode 100644 index 0000000000..1414f92690 --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/appendix/app_main.typ @@ -0,0 +1,2 @@ +#include "app1.typ" +#include "app2.typ" \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/bibliography/sample.bib b/packages/preview/bookly/2.0.0/template/bibliography/sample.bib new file mode 100644 index 0000000000..2bcdad5136 --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/bibliography/sample.bib @@ -0,0 +1,20 @@ +@book{Smi21, + title = {{B}ook {T}itle}, + publisher = {Publisher}, + author = {J. M. Smith and A. B. Jones}, + year = {2021}, + edition = {7th}, +} + +@article{Jon22, + author = {A. B. Jones and J. M. Smith}, + title = {{A}rticle {T}itle}, + journal = {Journal title}, + year = {2022}, + volume = {13}, + pages = {123-456}, + number = {52}, + month = {3}, + publisher = {Publisher}, + doi = {10.1038/s41586-021-03616-x}, +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/bibliography/sample.yml b/packages/preview/bookly/2.0.0/template/bibliography/sample.yml new file mode 100644 index 0000000000..52af7cad0b --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/bibliography/sample.yml @@ -0,0 +1,21 @@ +Smi21: + type: Book + title: Book Title + author: ["Smith, J. M.", "Jones, A. B."] + date: 2021 + edition: 7th + publisher: Publisher + +Jon22: + type: Article + author: ["Jones, A. B.", "Smith, J. M."] + title: Article Title + page-range: 123-456 + serial-number: + doi: "10.1038/s41586-021-03616-x" + parent: + type: Periodical + title: Journal title + volume: 13 + issue: 52 + date: 2022 \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/chapters/ch1.typ b/packages/preview/bookly/2.0.0/template/chapters/ch1.typ new file mode 100644 index 0000000000..25a9b9603f --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/chapters/ch1.typ @@ -0,0 +1,92 @@ +#import "@preview/bookly:2.0.0": * + +// #show: chapter.with(title: "First chapter") + += First chapter + +#lorem(100) +#minitoc +#pagebreak() + +== Goals +#lorem(100) + +Equations @eq:1 and @eq:2 are very important. +$ +integral_0^1 f(x) dif x = F(1) - F(0) "et voilà" +$ + +$ +integral_0^1 f(x) dif x = F(1) - F(0) "et voilà" +$ + +#lorem(20) +== Code + +Figure @fig:1 is a beautiful typst logo. + +#figure( +image("../images/typst-logo.svg", width: 75%), +caption: [#ls-caption([#lorem(10)], [#lorem(2)])], +) + +Figure @fig:subfig the Typst logo. Figure @b is a Typst logo @Smi21. + +#subfigure( +figure(image("../images/typst-logo.svg"), caption: []), +figure(image("../images/typst-logo.svg"), caption: []), , +columns: (1fr, 1fr), +caption: [(a) Left image and (b) Right image], +label: , +) + +#figure( + table( + columns: 3, + table.header( + [Substance], + [Subcritical °C], + [Supercritical °C], + ), + [Hydrochloric Acid], + [12.0], [92.1], + [Sodium Myreth Sulfate], + [16.6], [104], + [Potassium Hydroxide], + table.cell(colspan: 2)[24.7], + ), caption: [#lorem(4)] +) + +== Boxes + +#lorem(10) + +=== Informations + +#info-box[ + #lorem(10) +] + +#tip-box[ + #lorem(10) +] + +#warning-box[ + #lorem(10) +] + +#important-box[ + #lorem(10) +] + +#proof-box[ + #lorem(10) +] + +#question-box[ + #lorem(10) +] + +#lorem(1000)#footnote("This is a footnote") + +dsflkv,dflkb \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/chapters/ch2.typ b/packages/preview/bookly/2.0.0/template/chapters/ch2.typ new file mode 100644 index 0000000000..606489b7e1 --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/chapters/ch2.typ @@ -0,0 +1,24 @@ +#import "@preview/bookly:2.0.0": * + +// #show: chapter.with(title: "Second chapter") + += Second chapter +#minitoc +#pagebreak() + +== Goals +#lorem(100) + +$ +arrow(V)(M slash R_0) = lr((d arrow(O M))/(d t)|)_(R_0) + theta +$ + +La Figure @b2 présente la carte du Cnam @Jon22. + +#subfigure( +figure(image("../images/typst-logo.svg"), caption: []), +figure(image("../images/typst-logo.svg"), caption: []), , +columns: (1fr, 1fr), +caption: [(a) Left image and (b) Right image], +label: , +) \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/chapters/ch_main.typ b/packages/preview/bookly/2.0.0/template/chapters/ch_main.typ new file mode 100644 index 0000000000..d9b462192e --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/chapters/ch_main.typ @@ -0,0 +1,4 @@ +#include "intro.typ" +#include "ch1.typ" +#include "ch2.typ" +#include "conclusion.typ" \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/chapters/conclusion.typ b/packages/preview/bookly/2.0.0/template/chapters/conclusion.typ new file mode 100644 index 0000000000..4375a0dd6d --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/chapters/conclusion.typ @@ -0,0 +1,8 @@ +// #import "@preview/bookly:1.0.0": * +#import "@preview/bookly:2.0.0": * + +// #show: chapter.with(title: "Conclusions et perspectives", toc: false) + += Conclusions and outlooks + +#lorem(100) \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/chapters/intro.typ b/packages/preview/bookly/2.0.0/template/chapters/intro.typ new file mode 100644 index 0000000000..f833abb7c9 --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/chapters/intro.typ @@ -0,0 +1,40 @@ +// #import "@preview/bookly:2.0.0": * +#import "@preview/bookly:2.0.0": * + +// #show: chapter.with( +// title: "Introduction", +// abstract: [#lorem(50)], +// numbered: false +// ) + +#show: chapter-nonum += Introduction + +== Goals +#lorem(100) + +#lorem(25) + +$ +y = f(x) \ +g = h(x) +$ + +#v(1.25em) +=== Sub-goals + +#figure( +image("../images/typst-logo.svg", width: 75%), +caption: [#ls-caption([#lorem(10)], [#lorem(2)])], +) + +#lorem(50) (cf. Figure @fig:intro) + +#pagebreak() +== Methodology + +#lorem(1000)#footnote[#lorem(10)] + +- #lorem(20) + +- #lorem(20) \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/custom-theme.typ b/packages/preview/bookly/2.0.0/template/custom-theme.typ new file mode 100644 index 0000000000..5b203d5921 --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/custom-theme.typ @@ -0,0 +1,184 @@ +#import "@preview/bookly:2.0.0": * + +#let custom(colors: default-colors, it) = { + states.theme.update("custom") + + // Headings + show heading.where(level: 1): it => { + if not states.open-right.get() { + pagebreak(weak: true) + } + + // Reset counters + reset-counters + + // Heading style + place(top)[ + #rect(fill: white, width: 1%, height: 1%) + ] + set align(left) + let type-chapter = if states.isappendix.get() {states.localization.get().appendix} else {states.localization.get().chapter} + if it.numbering != none { + v(4em) + block[ + #text(size: 1.5em)[#type-chapter #counter(heading).display(states.num-heading.get())] + + #text(2em)[#it.body] + ] + v(3em) + } else { + v(1em) + text(2em)[#it.body] + v(2em) + } + } + + show heading.where(level: 2): it => { + block(above: 2em, below: 1.25em)[ + #it + ] + } + + show heading.where(level: 3): it => { + block(above: 1.25em, below: 1.25em)[ + #it + ] + } + + // Tables + show table.cell.where(y: 0): set text(weight: "bold") + set table( + stroke: (_, y) => ( + top: if y <= 1 {0.75pt} else {0pt}, + bottom: 0.75pt + ), + ) + + // Outline + set outline.entry(fill: box(width: 1fr, repeat(gap: 0.25em)[.])) + show outline.entry: it => { + show linebreak: none + if it.element.func() == heading { + let number = it.prefix() + let section = it.element.body + let item = none + if it.level == 1 { + block(above: 1.25em, below: 0em) + v(0.5em) + item = [*#number #it.inner()*] + } else if it.level == 2 { + block(above: 1em, below: 0em) + item = [#h(1em) #number #it.inner()] + } else { + block(above: 1em, below: 0em) + item = [#h(2em) #number #it.inner()] + } + link(it.element.location(), item) + } else if it.element.func() == figure { + block(above: 1.25em, below: 0em) + v(0.25em) + link(it.element.location(), [#it.prefix(). #h(0.2em) #it.inner()]) + } else { + it + } + } + + // Page style + let page-header = context { + show linebreak: none + show: fullwidth + if calc.odd(here().page()) { + align(left, hydra(2, display: (_, it) => [ + #let head = none + #if it.numbering != none { + head = numbering(it.numbering, ..counter(heading).at(it.location())) + " " + it.body + } else { + head = it.body + } + #head + #place(dx: 0%, dy: 52%)[#line(length: 100%, stroke: 0.75pt)] + ])) + } else { + align(left, hydra(1, display: (_, it) => [ + #let head = counter(heading.where(level:1)).display() + " " + it.body + #if it.numbering == none { + head = it.body + } + #head + #place(dx: 0%, dy: 50%)[#line(length: 100%, stroke: 0.75pt)] + ])) + } + } + + let page-footer = context { + let cp = counter(page).get().first() + let current-page = counter(page).display() + let dx = 0% + if states.tufte.get() { + dx = 21.65% + } + set align(center) + move(dx: dx, current-page) + } + + set page( + paper: paper-size, + header: page-header, + footer: page-footer + ) + + it +} + + +// Part +#let part(title) = context { + states.counter-part.update(i => i + 1) + set page( + header: none, + footer: none, + numbering: none + ) + + set align(center + horizon) + + if states.open-right.get() { + pagebreak(weak: true, to:"odd") + } + + let dx = 0% + if states.tufte.get() { + dx = 21.68% + } + + move(dx: dx)[ + #text(size: 2.5em)[#states.localization.get().part #states.counter-part.display(states.part-numbering.get())] + #v(1em) + #text(size: 3em)[*#title*] + ] + + show heading: none + heading(numbering: none)[ + #v(1em) + #box[#states.localization.get().part #states.counter-part.display(states.part-numbering.get()) -- #title] + ] + + if states.open-right.get() { + pagebreak(weak: true, to:"odd") + } +} + +#let minitoc = context { + let toc-header = states.localization.get().toc + block(above: 3.5em)[ + #text([*#toc-header*]) + #v(-0.5em) + ] + + let miniline = line(stroke: 0.75pt, length: 100%) + + miniline + v(0.5em) + suboutline(target: heading.where(outlined: true, level: 2)) + miniline +} \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/front_matter/abstract.typ b/packages/preview/bookly/2.0.0/template/front_matter/abstract.typ new file mode 100644 index 0000000000..d1af2c32f2 --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/front_matter/abstract.typ @@ -0,0 +1,8 @@ +#import "@preview/bookly:2.0.0": * + +// #show: chapter.with(title: "Abstract", toc: false) +#show: chapter-nonum.with() + += Abstract + +#lorem(500) \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/front_matter/acknowledgments.typ b/packages/preview/bookly/2.0.0/template/front_matter/acknowledgments.typ new file mode 100644 index 0000000000..3ff7c2edb3 --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/front_matter/acknowledgments.typ @@ -0,0 +1,10 @@ +// #import "@preview/bookly:1.0.0": * +#import "@preview/bookly:2.0.0": * + +// #show: chapter.with(title: "Acknowledgments", toc: false) +#show: chapter-nonum += Acknowledgments + +#lorem(50) + + diff --git a/packages/preview/bookly/2.0.0/template/front_matter/front_main.typ b/packages/preview/bookly/2.0.0/template/front_matter/front_main.typ new file mode 100644 index 0000000000..6e7a96abcf --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/front_matter/front_main.typ @@ -0,0 +1,2 @@ +#include "acknowledgments.typ" +#include "abstract.typ" \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/template/images/book-cover.jpg b/packages/preview/bookly/2.0.0/template/images/book-cover.jpg new file mode 100644 index 0000000000..9f913ac9c2 Binary files /dev/null and b/packages/preview/bookly/2.0.0/template/images/book-cover.jpg differ diff --git a/packages/preview/bookly/2.0.0/template/images/typst-logo.svg b/packages/preview/bookly/2.0.0/template/images/typst-logo.svg new file mode 100644 index 0000000000..055a3aa9ad --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/images/typst-logo.svg @@ -0,0 +1,227 @@ + + + + diff --git a/packages/preview/bookly/2.0.0/template/main.typ b/packages/preview/bookly/2.0.0/template/main.typ new file mode 100644 index 0000000000..9457142f2d --- /dev/null +++ b/packages/preview/bookly/2.0.0/template/main.typ @@ -0,0 +1,77 @@ +#import "@preview/bookly:2.0.0": * + +#let config-colors = ( + primary: rgb("#1d90d0"), + secondary: rgb("#dddddd").darken(15%) +) + +#show: bookly.with( + author: "Author Name", + fonts: ( + body: "Lato", + math: "Lete Sans Math" + ), + // theme: custom, + // theme: classic, + // theme: fancy, + // theme: modern, + // theme: orly, + // theme: pretty, + // theme: custom, + // tufte: true, + lang: "fr", + // colors: config-colors, + title-page: book-title-page( + series: "Typst book series", + institution: "Typst community", + logo: image("images/typst-logo.svg"), + cover: image("images/book-cover.jpg", width: 45%) + ), + config-options: ( + open-right: true, + ) +) + +#show: front-matter + +#include "front_matter/front_main.typ" + +#show: main-matter +#states.isfrontmatter.update(true) + +#tableofcontents + +#listoffigures + +#listoftables + +#part("First part") + +#include "chapters/ch_main.typ" + +#part("Second part") + +#show: appendix + +#include "appendix/app_main.typ" + +// #bibliography("bibliography/sample.yml") +#bibliography("bibliography/sample.bib") + +#let abstracts-fr-en = ( + ( + title: [#set text(lang: "fr"); Résumé :], + text: [#lorem(100)] + ), + ( + title: [#set text(lang: "en", region: "gb"); Abstract:], + text: [#lorem(100)] + ), +) + +#let logos = ( + align(left)[#image("images/typst-logo.svg", width: 50%)], + align(right)[#image("images/typst-logo.svg", width: 50%)] +) + +#back-cover(abstracts: abstracts-fr-en, logo: logos) \ No newline at end of file diff --git a/packages/preview/bookly/2.0.0/thumbnails/main.png b/packages/preview/bookly/2.0.0/thumbnails/main.png new file mode 100644 index 0000000000..f272c2d560 Binary files /dev/null and b/packages/preview/bookly/2.0.0/thumbnails/main.png differ diff --git a/packages/preview/bookly/2.0.0/typst.toml b/packages/preview/bookly/2.0.0/typst.toml new file mode 100644 index 0000000000..20202025f0 --- /dev/null +++ b/packages/preview/bookly/2.0.0/typst.toml @@ -0,0 +1,15 @@ +[package] +name = "bookly" +version = "2.0.0" +authors = ["Mathieu Aucejo"] +license = "MIT" +description = "Book template for Typst" +entrypoint = "src/bookly.typ" +categories = ["book", "model"] +repository = "https://github.com/maucejo/bookly" +exclude = ["docs/manual.pdf"] + +[template] +path = "template" +entrypoint = "main.typ" +thumbnail = "thumbnails/main.png" \ No newline at end of file diff --git a/packages/preview/elsearticle/2.0.2/LICENSE b/packages/preview/elsearticle/2.0.2/LICENSE new file mode 100644 index 0000000000..d54b2b04a1 --- /dev/null +++ b/packages/preview/elsearticle/2.0.2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Mathieu Aucejo + +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. diff --git a/packages/preview/elsearticle/2.0.2/README.md b/packages/preview/elsearticle/2.0.2/README.md new file mode 100644 index 0000000000..7da21abe5d --- /dev/null +++ b/packages/preview/elsearticle/2.0.2/README.md @@ -0,0 +1,73 @@ +# elsearticle template + +[![Generic badge](https://img.shields.io/badge/Version-2.0.2-cornflowerblue.svg)]() +[![MIT License](https://img.shields.io/badge/License-MIT-forestgreen)](https://github.com/maucejo/elsearticle/blob/main/LICENSE) +[![User Manual](https://img.shields.io/badge/doc-.pdf-mediumpurple)](https://github.com/maucejo/bookly/blob/626f10e31c824901bac54034f9051dfd16925fed/docs/manual.pdf) + +`elsearticle` is a Typst template that aims to mimic the Elsevier article LaTeX class, a.k.a. `elsarticle.cls`, provided by Elsevier to format manuscript properly for submission to their journals. + +## Basic usage + +This section provides the minimal amount of information to get started with the template. For more detailed information, see the [manual](https://github.com/maucejo/bookly/blob/626f10e31c824901bac54034f9051dfd16925fed/docs/manual.pdf). + +To use the `elsearticle` template, you need to include the following line at the beginning of your `typ` file: + +```typ +#import "@preview/elsearticle:2.0.2": * +``` + +### Initializing the template + +After importing `elsearticle`, you have to initialize the template by a show rule with the `#elsearticle()` command. This function takes an optional argument to specify the title of the document. + +* `title`: Title of the paper +* `authors`: List of the authors of the paper +* `affiliations`: List of the affiliations of the authors +* `abstract`: Abstract of the paper +* `journal`: Name of the journal +* `keywords`: List of keywords of the paper +* `format`: Format of the paper. Possible values are `preprint`, `review`, `1p`, `3p`, `5p` +* `numcol`: Number of columns of the paper. Possible values are 1 and 2 +* `line-numbering`: Enable line numbering. Possible values are `true` and `false` + +## Additional features + +The `elsearticle` template provides additional features to help you format your document properly. + +### Appendix + +To activate the appendix environment, all you have to do is to place the following command in your document: +```typ +#show: appendix + +// Appendix content here +``` + +### Subfigures + +Subfigures are not built-in features of Typst, but the `elsearticle` template provides a way to handle them. It is based on the `subpar` package that allows you to create subfigures and properly reference them. + +```typ + #subfigure( + figure(image("image1.png"), caption: []), , + figure(image("image2.png"), caption: []), , + columns: (1fr, 1fr), + caption: [(a) Left image and (b) Right image], + label: + ) +``` + +### Equations + +The `elsearticle` template provides the `#nonumeq()` function to create unnmbered equations. The latter function can be used as follows: +```typ +#nonumeq[$ + y = f(x) + $ +] +``` + +## License +MIT licensed + +Copyright (C) 2026 Mathieu AUCEJO (maucejo) diff --git a/packages/preview/elsearticle/2.0.2/src/els-environment.typ b/packages/preview/elsearticle/2.0.2/src/els-environment.typ new file mode 100644 index 0000000000..ded55902ec --- /dev/null +++ b/packages/preview/elsearticle/2.0.2/src/els-environment.typ @@ -0,0 +1,27 @@ +#import "els-globals.typ": * + +// Appendix +#let appendix(body) = { + set heading(numbering: "A.1.", supplement: [Appendix]) + // Reset heading counter + counter(heading).update(0) + + // Equation numbering + let numbering-eq = (..n) => { + let h1 = counter(heading).get().first() + numbering("(A.1a)", h1, ..n) + } + set math.equation(numbering: numbering-eq) + + // Figure and Table numbering + let numbering-fig = n => { + let h1 = counter(heading).get().first() + numbering("A.1", h1, n) + } + show figure.where(kind: image): set figure(numbering: numbering-fig) + show figure.where(kind: table): set figure(numbering: numbering-fig) + + isappendix.update(true) + + body +} \ No newline at end of file diff --git a/packages/preview/elsearticle/2.0.2/src/els-globals.typ b/packages/preview/elsearticle/2.0.2/src/els-globals.typ new file mode 100644 index 0000000000..d8afd4b1eb --- /dev/null +++ b/packages/preview/elsearticle/2.0.2/src/els-globals.typ @@ -0,0 +1,73 @@ +#let textfont = ("XITS", "STIX Two Text", "New Computer Modern") +#let mathfont = ("XITS Math", "STIX Two Math", "New Computer Modern Math") + +#let font-size = ( + script: 7pt, + footnote: 8pt, + small: 10pt, + normal: 12pt, + author: 12pt, + title: 17.2pt, +) + +#let review = ( + indent: 1.5em, + paper: "us-letter", + type: "review", + margins: (left: 39.7mm, right: 39.7mm, top: 39.7mm, bottom: 39.7mm), + leading: 1.1em, + above: 1.6em, + below: 1.3em, + spacing: 2em, + footer-descent: 20% +) + +#let preprint = ( + indent: 1.5em, + paper: "us-letter", + type: "preprint", + margins: (left: 39.7mm, right: 39.7mm, top: 43.7mm, bottom: 61.5mm), + leading: 0.5em, + above: 1.4em, + below: 0.85em, + spacing: 1.75em, + footer-descent: 20% +) + +#let one-p = ( + indent: 1.5em, + paper: "a4", + type: "1p", + margins: (left: 37.5mm, right: 37.5mm, top: 45.2mm, bottom: 47.9mm), + leading: 0.5em, + above: 1.4em, + below: 0.85em, + spacing: 1.75em, + footer-descent: 5% +) + +#let three-p = ( + indent: 1.5em, + paper: "a4", + type: "3p", + margins: (left: 22.8mm, right: 22.8mm, top: 38.8mm, bottom: 38.3mm), + leading: 0.5em, + above: 1.4em, + below: 0.85em, + spacing: 1.75em, + footer-descent: 8% +) + +#let five-p = ( + indent: 1.24em, + paper: "a4", + type: "5p", + margins: (left: 12.9mm, right: 12.9mm, top: 80pt, bottom: 80pt), + leading: 0.5em, + above: 1.25em, + below: 0.85em, + spacing: 1.5em, + footer-descent: 10% +) + +#let isappendix = state("isappendix", false) \ No newline at end of file diff --git a/packages/preview/elsearticle/2.0.2/src/els-template-info.typ b/packages/preview/elsearticle/2.0.2/src/els-template-info.typ new file mode 100644 index 0000000000..34cb66942b --- /dev/null +++ b/packages/preview/elsearticle/2.0.2/src/els-template-info.typ @@ -0,0 +1,131 @@ +#import "els-globals.typ": * +#import "els-utils.typ": * + +#let default-author = ( + name: none, + affiliation: none, + corr: none, + id: "a" +) + +#let make-author(author) = box({ + author.name + + let auth-affiliation = if author.at("affiliations", default: none) == none { + none + } else { + author.affiliations.map((key) => {key}) + } + + let auth-rest + let iscorr + if author.at("corresponding", default: false) == true { + auth-rest = (sym.ast,) + iscorr = true + } else { + auth-rest = none + iscorr = false + } + + sym.space.thin + if auth-affiliation == none { + if iscorr {super(sym.ast)} + } else { + super((auth-affiliation + auth-rest).join([,])) + } +}) + +#let make-authors(authors) = par({ + set text(size: font-size.author) + authors.map(make-author).join(", ", last: " and ") +}) + +#let make-author-meta(authors) = { + let names = () + + if authors.len() == 0 {return ()} + + for author in authors { + if type(author.name) == content { + names.push(author.name.text) + } else { + names.push(author.name) + } + } + return names.join(" ") +} + +#let make-corresponding-author(authors, els-columns) = for author in authors { + if author.at("corresponding", default: false) == true { + place( + float: true, + bottom, + { + v(0.5em) + let line-length = if els-columns == 1 {9.6em} else {4.6em} + line(length: line-length, stroke: 0.25pt) + v(-0.75em) + set text(size: 10pt) + set par(leading: 0.5em,) + if els-columns == 1 { + [#h(1em);#super[#sym.ast]#h(0.1em);Corresponding author. E-mail address: #if (author.at("email", default: none)) != none {author.email} else {"No email provided"}] + } else { + [#h(1em);#super[#sym.ast]#h(0.1em);Corresponding author. #linebreak() #h(1.4em);E-mail address: #if (author.at("email", default: none)) != none {author.email} else {"No email provided"}] + } + } + ) + } +} + +#let make-affiliation(key, value) = { + super[#key] + if key != " " { + sym.space.thin + } + text(style:"italic", value) +} + +#let make-affiliations(affiliations) = { + for (key, value) in affiliations{ + make-affiliation(key, value) + linebreak() + } +} + +#let make-title(title: none, authors: (), affiliations: ()) = align(center, { + par(leading: 0.95em, text(size: font-size.title, title)) + v(0.9em) + text(size: font-size.author, make-authors(authors)) + v(0.2em) + par(leading: 0.65em, text(size: font-size.small, make-affiliations(affiliations), top-edge: 0.5em)) + v(1.75em) +}) + +// Format the abstract +#let make-abstract(abstract, keywords, els-format) = if abstract != none { + set par(justify: true) + line(length: 100%, stroke: 0.5pt) + v(-0.25em) + text(weight: "bold")[Abstract] + if els-format.type.contains("review") {v(0.5em)} else {v(-0.2em)} + abstract + if els-format.type.contains("review") {linebreak()} else {v(0em)} + if keywords != () { + let kw = () + for keyword in keywords{ + kw.push(keyword) + } + + let kw-string = if kw.len() > 1 { + kw.join(", ") + } else { + kw.first() + } + text((emph("Keywords: "), kw-string).join()) + } + v(-0.2em) + line(length: 100%, stroke: 0.5pt) + if els-format.type.contains("review") {v(-0.75em)} + else if els-format.type.contains("5p") {v(-0.25em)} + else {none} +} \ No newline at end of file diff --git a/packages/preview/elsearticle/2.0.2/src/els-utils.typ b/packages/preview/elsearticle/2.0.2/src/els-utils.typ new file mode 100644 index 0000000000..32e4bfea0f --- /dev/null +++ b/packages/preview/elsearticle/2.0.2/src/els-utils.typ @@ -0,0 +1,25 @@ +#import "els-globals.typ": * +#import "@preview/subpar:0.2.2" + +// Subfigures +#let subfigure = { + subpar.grid.with( + numbering: n => if isappendix.get() {numbering("A.1", counter(heading).get().first(), n) + } else { + numbering("1", n) + }, + numbering-sub-ref: (m, n) => if isappendix.get() {numbering("A.1a", counter(heading).get().first(), m, n) + } else { + numbering("1a", m, n) + } + ) +} + +// Equations +#let nonumeq(body) = { + set math.equation(numbering: none) + body +} + +// pagebreak +#let pagebreak(weak: false) = colbreak(weak: weak) \ No newline at end of file diff --git a/packages/preview/elsearticle/2.0.2/src/elsearticle.typ b/packages/preview/elsearticle/2.0.2/src/elsearticle.typ new file mode 100644 index 0000000000..66a370333e --- /dev/null +++ b/packages/preview/elsearticle/2.0.2/src/elsearticle.typ @@ -0,0 +1,190 @@ +// elsearticle.typ +// Author: Mathieu Aucejo +// Github: https://github.com/maucejo +// License: MIT +// Date : 11/2024 +#import "@preview/equate:0.3.2": * +#import "els-globals.typ": * +#import "els-environment.typ": * +#import "els-utils.typ": * +#import "els-template-info.typ": * + +#let elsearticle( + // The article's title. + title: none, + + // An array of authors. For each author you can specify a name, + // department, organization, location, and email. Everything but + // but the name is optional. + authors: (), + + affiliations: (), + + // Your article's abstract. Can be omitted if you don't have one. + abstract: none, + + // Journal name + journal: none, + + // Keywords + keywords: (), + + // For integrating future formats (1p, 3p, 5p, final) + format: "review", + + // Number of columns + numcol: 1, + + // Line numbering + line-numbering: false, + + // The document's content. + body, +) = { + // Text + set text(size: font-size.normal, font: textfont) + + // Conditional formatting + let els-format = if format.contains("review") {review} + else if format.contains("preprint") {preprint} + else if format.contains("1p") {one-p} + else if format.contains("3p") {three-p} + else if format.contains("5p") {five-p} + else {review} + + let els-columns = if format.contains("1p") {1} + else if format.contains("5p") {2} + else {if numcol > 2 {2} else {if numcol <= 0 {1} else {numcol}}} + + // Heading + set heading(numbering: "1.") + + show heading: it => block(above: els-format.above, below: els-format.below)[ + #if it.numbering != none { + if it.level == 1 { + set par(justify: true, first-line-indent: 0em) + set text(font-size.normal) + numbering(it.numbering, ..counter(heading).at(it.location())) + text((" ", it.body).join()) + + // Update math counter at each new appendix + if isappendix.get() { + counter(math.equation).update(0) + counter(figure.where(kind: image)).update(0) + counter(figure.where(kind: table)).update(0) + } + } else { + set text(font-size.normal, weight: "regular", style: "italic") + numbering(it.numbering, ..counter(heading).at(it.location())) + text((" ", it.body).join()) + } + } else { + text(size: font-size.normal, it.body) + } + ] + + // Equations + show: equate.with(breakable: true, sub-numbering: true) + show math.equation: set text(font: mathfont) + set math.equation(numbering: (..n) => text(font: textfont, numbering("(1a)", ..n)) , supplement: none) + + // Figures, subfigures, tables + show figure.where(kind: table): set figure.caption(position: top) + set ref(supplement: none) + show ref: set text(fill: rgb(0, 0, 255)) + + // Page + let footer = context{ + let i = counter(page).at(here()).first() + if i == 1 { + set text(size: font-size.small) + if journal != none { + emph(("Preprint submitted to ", journal).join()) + } + h(1fr) + emph(datetime.today().display("[month repr:long] [day], [year]")) + } else {align(center)[#i]} + } + + set rect( + width: 100%, + height: 100%, + ) + + set page( + paper: els-format.paper, + numbering: "1", + margin: els-format.margins, + footer: footer, + footer-descent: els-format.footer-descent, + // columns: els-columns + ) + + // Links + show link: set text(fill: rgb(0, 0, 255)) + + // Set document metadata. + set document( + title: title, + author: make-author-meta(authors), + keywords: keywords, + ) + + set par(justify: true, leading: els-format.leading) + + let front-matter = { + let els-title-above = if els-format.type.contains("review") {1.15*els-format.above} + else if els-format.type.contains("5p") { 1.49*els-format.above} + else {1.33*els-format.above} + + let els-title-below = if els-format.type.contains("review") {0.51em} + else if els-format.type.contains("5p") or (els-format.type.contains("3p") and els-columns == 2 ) {0em} + else {0.25em} + + v(els-title-above) + make-title(title: title, authors: authors, affiliations: affiliations) + make-abstract(abstract, keywords, els-format) + v(els-title-below) + } + + make-corresponding-author(authors, els-columns) + front-matter + + // Paragraph + let linenum = if line-numbering {"1"} else {none} + + set par(first-line-indent: (amount: els-format.indent, all:true), spacing: els-format.spacing) + set par.line(numbering: linenum, numbering-scope: "page") + + // Workaround to not indent the first paragraph after an equation + show math.equation: it => it + [#[ #[]]] + show par: it => { + if it.first-line-indent.amount == 0pt { + // Prevent recursion. + return it + } + + context { + let eq-end = query(selector().before(here())).at(-1, default: none) + if eq-end == none { return it } + if eq-end.location().position() != here().position() { return it } + + // Paragraph start aligns with end of last equation, so recreate + // the paragraph, but without indent. + let fields = it.fields() + let body = fields.remove("body") + return par( + ..fields, + first-line-indent: 0pt, + body + ) + } + } + + // bibliography + set bibliography(title: "References", style: "elsevier-with-titles") + show bibliography: set heading(numbering: none) + show bibliography: set text(size: font-size.normal) + + show: columns(els-columns, body) +} diff --git a/packages/preview/elsearticle/2.0.2/template/images/typst-logo.svg b/packages/preview/elsearticle/2.0.2/template/images/typst-logo.svg new file mode 100644 index 0000000000..055a3aa9ad --- /dev/null +++ b/packages/preview/elsearticle/2.0.2/template/images/typst-logo.svg @@ -0,0 +1,227 @@ + + + + diff --git a/packages/preview/elsearticle/2.0.2/template/main.typ b/packages/preview/elsearticle/2.0.2/template/main.typ new file mode 100644 index 0000000000..31c8a4c2da --- /dev/null +++ b/packages/preview/elsearticle/2.0.2/template/main.typ @@ -0,0 +1,146 @@ +#import "@preview/elsearticle:2.0.2": * + +#let abstract = lorem(250) + +#show: elsearticle.with( + title: "Title of the paper", + authors: ( + (name: [A. Author], affiliations: ("a", "b"), corresponding: true, email:"author@univa.edu"), + (name: [B. Author], affiliations: ("b",)), + ), + affiliations: ( + "a": [University A, City A, Country A], + "b": [University B, City B, Country B], + ), + journal: "Name of the Journal", + abstract: abstract, + keywords: ("keyword 1", "keyword 2"), + format: "review", + // numcol: 1, + // line-numbering: true, +) + += Introduction + +#lorem(100) + += Section 1 + +#lorem(50) + +== Subsection 1 + +#lorem(10) (see Eq. @eq1) @Aut10. + +$ +y = alpha x + beta tau integral_0^x d x +$ +where ... + +$ + x = integral_0^x d x #\ + (u v)' = u' v + v' u # +$ + +Eq. @eqa is a simple integral, while Eq. @eqb is the derivative of a product of two functions. These equations are grouped in Eq. @eq2. + +== Features + +=== Table + +Below is Table @tab:tab1. + +#let tab1 = { + table( + columns: 3, + table.header( + [*Header 1*], + [*Header 2*], + [*Header 3*], + ), + [Row 1], [12.0], [92.1], + [Row 2], [16.6], [104], +) +} + +#figure( + tab1, + kind: table, + caption : [Example] +) + +=== Figures + +Below is Fig. @fig:logo. + +#figure( + image("images/typst-logo.svg", width: 50%), + caption : [Typst logo - Credit: \@fenjalien] +) + +=== Subfigures + +Below are Figs. @figa and @figb, which are part of Fig. @fig:typst. + +#subfigure( +figure(image("images/typst-logo.svg"), caption: []), , +figure(image("images/typst-logo.svg"), caption: []), , +columns: (1fr, 1fr), +caption: [(a) Left image and (b) Right image], +label: , +) + +#show: appendix + += Appendix A + +== Figures + +In Fig. @fig:app + +#figure( + image("images/typst-logo.svg", width: 50%), + caption : [Books cover] +) + +== Subfigures + +Below are Figs. @figa-app and @figb-app, which are part of Fig. @fig:typst-app. + +#subfigure( +figure(image("images/typst-logo.svg"), caption: []), , +figure(image("images/typst-logo.svg"), caption: []), , +columns: (1fr, 1fr), +caption: [(a) Left image and (b) Right image], +label: , +) + +== Tables + +In Table @tab:app + +#figure( + tab1, + kind: table, + caption : [Example] +) + +== Equations + +In Eq. @eq + +$ +y = f(x) +$ + +#nonumeq[$ + y = g(x) + $ +] + +$ +y = f(x) \ +y = g(x) +$ + +#bibliography("refs.bib") \ No newline at end of file diff --git a/packages/preview/elsearticle/2.0.2/template/refs.bib b/packages/preview/elsearticle/2.0.2/template/refs.bib new file mode 100644 index 0000000000..79be3a35ae --- /dev/null +++ b/packages/preview/elsearticle/2.0.2/template/refs.bib @@ -0,0 +1,9 @@ +% Encoding: UTF-8 +@Article{Aut10, + author = {A. Author}, + journal = {Journal of Typst}, + title = {How to make a template in Typst}, + year = {2024}, + pages = {1-10}, + volume = {25 (1)}, +} \ No newline at end of file diff --git a/packages/preview/elsearticle/2.0.2/thumbnails/main.png b/packages/preview/elsearticle/2.0.2/thumbnails/main.png new file mode 100644 index 0000000000..aecd34103a Binary files /dev/null and b/packages/preview/elsearticle/2.0.2/thumbnails/main.png differ diff --git a/packages/preview/elsearticle/2.0.2/typst.toml b/packages/preview/elsearticle/2.0.2/typst.toml new file mode 100644 index 0000000000..c3b0ac7af8 --- /dev/null +++ b/packages/preview/elsearticle/2.0.2/typst.toml @@ -0,0 +1,14 @@ +[package] +name = "elsearticle" +version = "2.0.2" +authors = ["Mathieu Aucejo"] +license = "MIT" +description = "Conversion of the LaTeX elsearticle.cls" +entrypoint = "src/elsearticle.typ" +categories = ["report"] +repository = "https://github.com/maucejo/elsearticle" + +[template] +path = "template" +entrypoint = "main.typ" +thumbnail = "thumbnails/main.png" \ No newline at end of file