diff --git a/packages/preview/touying-simpl-nwpu/0.1.0/LICENSE b/packages/preview/touying-simpl-nwpu/0.1.0/LICENSE new file mode 100644 index 0000000000..4c3648f0e0 --- /dev/null +++ b/packages/preview/touying-simpl-nwpu/0.1.0/LICENSE @@ -0,0 +1,20 @@ +MIT License +Copyright (c) 2024 tzhtaylor (Original Work) +Copyright (c) 2026 Ramana (Modifications) +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/touying-simpl-nwpu/0.1.0/README.md b/packages/preview/touying-simpl-nwpu/0.1.0/README.md new file mode 100644 index 0000000000..d3acebfc8b --- /dev/null +++ b/packages/preview/touying-simpl-nwpu/0.1.0/README.md @@ -0,0 +1,45 @@ +# 西北工业大学 Touying 幻灯片主题 + +基于 Touying 构建的西北工业大学(NWPU)演示模板,使用学校官网提供的蓝色/白色校标,并以 NWPU 蓝 `#0054A3` 作为默认主色。 + +## 快速使用 + +```typst +#import "@preview/touying:0.6.1": * +#import "@preview/touying-simpl-nwpu:0.1.0": * + +#show: nwpu-theme.with(config-info( + title: [Your Title], + subtitle: [Your Subtitle], + author: [Author], + date: datetime.today(), + institution: [Northwestern Polytechnical University], +)) + +#title-slide() +#outline-slide() + += Section + +== Slide Title + +Slide content. + +== End + +#end-slide[Thank you!] +``` + +也可运行: + +```sh +typst init @preview/touying-simpl-nwpu:0.1.0 +``` + +## 视觉资源 + +模板中的 NWPU 蓝色与白色校标来自西北工业大学官网。其他背景和装饰均由 Typst 原生图形生成,不依赖其他高校的视觉资源。 + +## License + +MIT License。学校名称与校标的使用应遵循西北工业大学相关规范。 diff --git a/packages/preview/touying-simpl-nwpu/0.1.0/lib.typ b/packages/preview/touying-simpl-nwpu/0.1.0/lib.typ new file mode 100644 index 0000000000..06a6ca6261 --- /dev/null +++ b/packages/preview/touying-simpl-nwpu/0.1.0/lib.typ @@ -0,0 +1,763 @@ +// This theme is inspired by https://github.com/zbowang/BeamerTheme and https://github.com/touying-typ/touying/blob/main/themes/dewdrop.typ + +#import "@preview/touying:0.6.1": * + +#let _typst-builtin-repeat = repeat + +#let mini-slides( + self: none, + fill: rgb("000000"), + alpha: 60%, + display-section: false, + display-subsection: true, + linebreaks: true, + short-heading: true, +) = ( + context { + let headings = query(heading.where(level: 1).or(heading.where(level: 2))) + let sections = headings.filter(it => it.level == 1) + if sections == () { + return + } + let first-page = sections.at(0).location().page() + headings = headings.filter(it => it.location().page() >= first-page) + let slides = query().filter(it => ( + utils.is-kind(it, "touying-new-slide") and it.location().page() >= first-page + )) + let current-page = here().page() + let current-index = sections.filter(it => it.location().page() <= current-page).len() - 1 + let cols = () + let col = () + for (hd, next-hd) in headings.zip(headings.slice(1) + (none,)) { + if hd.outlined == false { + continue + } + + let next-page = if next-hd != none { + next-hd.location().page() + } else { + calc.inf + } + if hd.level == 1 { + if col != () { + cols.push(align(left, col.sum())) + col = () + } + col.push({ + let body = if short-heading { + utils.short-heading(self: self, hd) + } else { + hd.body + } + [#link(hd.location(), body)] + linebreak() + while slides.len() > 0 and slides.at(0).location().page() < next-page { + let slide = slides.remove(0) + if display-section { + let next-slide-page = if slides.len() > 0 { + slides.at(0).location().page() + } else { + calc.inf + } + if slide.location().page() <= current-page and current-page < next-slide-page { + [#link(slide.location(), sym.circle.filled)] + } else { + [#link(slide.location(), sym.circle)] + } + } + } + if display-section and display-subsection and linebreaks { + linebreak() + } + }) + } else { + col.push({ + while slides.len() > 0 and slides.at(0).location().page() < next-page { + let slide = slides.remove(0) + if display-subsection { + let next-slide-page = if slides.len() > 0 { + slides.at(0).location().page() + } else { + calc.inf + } + if slide.location().page() <= current-page and current-page < next-slide-page { + [#link(slide.location(), sym.circle.filled)] + } else { + [#link(slide.location(), sym.circle)] + } + } + } + if display-subsection and linebreaks { + linebreak() + } + }) + } + } + if col != () { + cols.push(align(left, col.sum())) + col = () + } + if current-index < 0 or current-index >= cols.len() { + cols = cols.map(body => text(fill: fill, body)) + } else { + cols = cols + .enumerate() + .map(pair => { + let (idx, body) = pair + if idx == current-index { + text(fill: fill, body) + } else { + text(fill: utils.update-alpha(fill, alpha), body) + } + }) + } + set align(top) + show: block.with(inset: (top: .5em, x: 2em)) + show linebreak: it => it + v(-1em) + set text(size: .7em) + grid(columns: cols.map(_ => auto).intersperse(1fr), ..cols.intersperse([])) + } +) + +#let nwpu-header(self) = { + if self.store.navigation == "sidebar" { + place(right + top, { + v(4em) + show: block.with(width: self.store.sidebar.width, inset: (x: 1em)) + set align(left) + set par(justify: false) + set text(size: .9em) + components.custom-progressive-outline( + self: self, + level: auto, + alpha: self.store.alpha, + text-fill: (self.colors.primary, self.colors.neutral-darkest), + text-size: (1em, .9em), + vspace: (-.2em,), + indent: (0em, self.store.sidebar.at("indent", default: .5em)), + fill: (self.store.sidebar.at("fill", default: _typst-builtin-repeat[.]),), + filled: (self.store.sidebar.at("filled", default: false),), + paged: (self.store.sidebar.at("paged", default: false),), + short-heading: self.store.sidebar.at("short-heading", default: true), + ) + }) + } else if self.store.navigation == "mini-slides" { + mini-slides( + self: self, + fill: self.colors.primary, + alpha: self.store.alpha, + display-section: self.store.mini-slides.at("display-section", default: false), + display-subsection: self.store.mini-slides.at("display-subsection", default: true), + linebreaks: self.store.mini-slides.at("linebreaks", default: true), + short-heading: self.store.mini-slides.at("short-heading", default: true), + ) + grid( + inset: (x: 1.9em), + rows: (auto, auto, auto), + row-gutter: 15%, + grid( + columns: (75%, 25%), + align(left + horizon, utils.display-current-heading(depth: self.slide-level, style: auto)), + align(right + horizon, image("vi/nwpu-vi-logo.png", height: 0.72cm)), + ), + align(center + horizon, line(length: 100%, stroke: (paint: self.colors.primary, thickness: 1.5pt))), + ) + } else { + grid( + inset: (x: 1.9em), + rows: (auto, auto), + row-gutter: 15%, + grid( + columns: (75%, 25%), + align(left + horizon, utils.display-current-heading(depth: self.slide-level, style: auto)), + align(right + horizon, image("vi/nwpu-vi-logo.png", height: 0.72cm)), + ), + align(center + horizon, line(length: 100%, stroke: (paint: self.colors.primary, thickness: 1.5pt))), + ) + } +} + +#let nwpu-footer(self) = { + set align(bottom) + set text(size: if self.appendix { 0em } else { 0.8em }) + show: pad.with(.5em) + components.left-and-right( + block(inset: (left: 0.5em, bottom: 0.5em), text( + fill: self.colors.neutral-darkest.lighten(40%), + size: 0.7em, + utils.call-or-display(self, self.store.footer), + )), + block(inset: (right: 0.5em, bottom: 0.5em), text( + fill: self.colors.neutral-darkest.lighten(20%), + utils.call-or-display(self, self.store.footer-right), + )), + ) +} + +/// Default slide function for the presentation. +/// +/// - config (dictionary): The configuration of the slide. You can use `config-xxx` to set the configuration of the slide. For more several configurations, you can use `utils.merge-dicts` to merge them. +/// +/// - repeat (int, auto): The number of subslides. Default is `auto`, which means touying will automatically calculate the number of subslides. +/// +/// The `repeat` argument is necessary when you use `#slide(repeat: 3, self => [ .. ])` style code to create a slide. The callback-style `uncover` and `only` cannot be detected by touying automatically. +/// +/// - setting (function): The setting of the slide. You can use it to add some set/show rules for the slide. +/// +/// - composer (function, array): The composer of the slide. You can use it to set the layout of the slide. +/// +/// For example, `#slide(composer: (1fr, 2fr, 1fr))[A][B][C]` to split the slide into three parts. The first and the last parts will take 1/4 of the slide, and the second part will take 1/2 of the slide. +/// +/// If you pass a non-function value like `(1fr, 2fr, 1fr)`, it will be assumed to be the first argument of the `components.side-by-side` function. +/// +/// The `components.side-by-side` function is a simple wrapper of the `grid` function. It means you can use the `grid.cell(colspan: 2, ..)` to make the cell take 2 columns. +/// +/// For example, `#slide(composer: 2)[A][B][#grid.cell(colspan: 2)[Footer]]` will make the `Footer` cell take 2 columns. +/// +/// If you want to customize the composer, you can pass a function to the `composer` argument. The function should receive the contents of the slide and return the content of the slide, like `#slide(composer: grid.with(columns: 2))[A][B]`. +/// +/// - bodies (array): The contents of the slide. You can call the `slide` function with syntax like `#slide[A][B][C]` to create a slide. +#let slide( + config: (:), + repeat: auto, + setting: body => body, + composer: auto, + ..bodies, +) = touying-slide-wrapper(self => { + let self = utils.merge-dicts( + self, + config-page(header: nwpu-header, footer: nwpu-footer, margin: (x: 2.5cm)), + config-common(subslide-preamble: self.store.subslide-preamble), + ) + touying-slide(self: self, config: config, repeat: repeat, setting: setting, composer: composer, ..bodies) +}) + + +/// Title slide for the presentation. You should update the information in the `config-info` function. You can also pass the information directly to the `title-slide` function. +/// +/// Example: +/// +/// ```typst +/// #show: nwpu-theme.with( +/// config-info( +/// title: [Title], +/// logo: emoji.city, +/// ), +/// ) +/// +/// #title-slide(subtitle: [Subtitle], extra: [Extra information]) +/// ``` +/// +/// - config (dictionary): The configuration of the slide. You can use `config-xxx` to set the configuration of the slide. For more several configurations, you can use `utils.merge-dicts` to merge them. +/// +/// - extra (string, none): The extra information you want to display on the title slide. +#let title-slide( + config: (:), + extra: none, + ..args, +) = touying-slide-wrapper(self => { + self = utils.merge-dicts(self, config, config-common(freeze-slide-counter: true), config-page( + header: align(right + horizon, block(inset: (right: 0.5em, top: 0.45em), image("vi/nwpu-vi-logo.png", height: 0.85cm))), + margin: (top: 3.5em, bottom: 1.5em, x: 2em), + )) + let info = self.info + args.named() + let body = { + set par(leading: 1.6em) + set align(center + horizon) + set page(background: { + place(left + bottom, rect(width: 100%, height: 22%, fill: self.colors.primary.lighten(91%))) + place(right + top, circle(radius: 2.4cm, stroke: self.colors.primary.lighten(72%) + 1.2pt)) + place(right + top, circle(radius: 1.65cm, stroke: self.colors.primary.lighten(82%) + 0.8pt)) + }) + block(width: 100%, inset: 3em, { + block( + if info.subtitle == none { + linebreak() + } + + text( + size: if info.subtitle == none { 2em } else { 1.7em }, + fill: self.colors.primary, + weight: "bold", + info.title, + ) + + ( + if info.subtitle != none { + linebreak() + text( + size: 1.3em, + fill: self.colors.primary, + weight: "bold", + info.subtitle, + ) + } + ), + ) + v(1fr) + set text(size: 1.1em, fill: self.colors.neutral-dark, weight: "medium") + if info.author != none { + block(spacing: 1em, info.author) + } + v(1em) + if info.date != none { + block(spacing: 1em, utils.display-info-date(self)) + } + set text(size: .8em) + if info.institution != none { + block(spacing: 1em, info.institution) + } + if extra != none { + block(spacing: 1em, extra) + } + v(0.2fr) + }) + } + touying-slide(self: self, body) +}) + +#let title-slide-red( + config: (:), + extra: none, + ..args, +) = touying-slide-wrapper(self => { + self = utils.merge-dicts(self, config, config-common(freeze-slide-counter: true), config-page(margin: ( + top: 3.5em, + bottom: 0em, + x: 0em, + ))) + let info = self.info + args.named() + let body = { + set par(leading: 1.6em) + set align(left + bottom) + set page(fill: self.colors.primary) + line(length: 100%, stroke: (paint: self.colors.neutral-light, thickness: 1.5pt)) + v(-1.15em) + block(fill: self.colors.primary, width: 100%, { + block( + inset: (y: 1.6em, x: 3em), + text( + size: 1.6em, + fill: self.colors.neutral-light, + weight: "bold", + info.title, + ) + + ( + if info.subtitle != none { + linebreak() + text( + size: 1.2em, + fill: self.colors.neutral-light, + weight: "bold", + info.subtitle, + ) + } + ), + ) + grid( + columns: (65%, 35%), + block({ + set text(size: 1.1em, fill: self.colors.neutral-light, weight: "semibold") + if info.author != none { + block(inset: (x: 2.8em), spacing: 0.8em, info.author) + } + if info.date != none { + block(inset: (x: 2.8em), spacing: 0.8em, utils.display-info-date(self)) + } + set text(size: .8em) + if info.institution != none { + block(inset: (x: 3.4em), spacing: 0.8em, info.institution) + } + v(0.5em) + }), + align(right, block(inset: (x: 2em), image("vi/nwpu-vi-logo-white.png"))), + ) + v(2em) + }) + } + touying-slide(self: self, body) +}) + + +/// Outline slide for the presentation. +/// +/// - config (dictionary): The configuration of the slide. You can use `config-xxx` to set the configuration of the slide. For more several configurations, you can use `utils.merge-dicts` to merge them. +/// +/// - title (string): The title of the slide. Default is `utils.i18n-outline-title`. +#let outline-slide(config: (:), title: utils.i18n-outline-title, ..args) = touying-slide-wrapper(self => { + self = utils.merge-dicts(self, config-common(freeze-slide-counter: true), config-page(footer: nwpu-footer, margin: ( + top: 3em, + ))) + touying-slide(self: self, config: config, components.adaptive-columns( + start: text(1.7em, fill: self.colors.primary, weight: "bold", utils.call-or-display(self, title)), + text(fill: self.colors.neutral-darkest, outline(title: none, indent: 1em, depth: self.slide-level, ..args)), + )) +}) + + +#let new-section-mini-slides( + self: none, + fill: rgb("000000"), + alpha: 60%, + display-section: false, + display-subsection: true, + linebreaks: true, + short-heading: true, +) = ( + context { + let headings = query(heading.where(level: 1).or(heading.where(level: 2))) + let sections = headings.filter(it => it.level == 1) + if sections == () { + return + } + let first-page = sections.at(0).location().page() + headings = headings.filter(it => it.location().page() >= first-page) + let slides = query().filter(it => ( + utils.is-kind(it, "touying-new-slide") and it.location().page() >= first-page + )) + let current-page = here().page() + let current-index = sections.filter(it => it.location().page() <= current-page).len() - 1 + let cols = () + let col = () + for (hd, next-hd) in headings.zip(headings.slice(1) + (none,)) { + if hd.outlined == false { + continue + } + + let next-page = if next-hd != none { + next-hd.location().page() + } else { + calc.inf + } + if hd.level == 1 { + if col != () { + cols.push(align(left, col.sum())) + col = () + } + col.push({ + let body = if short-heading { + utils.short-heading(self: self, hd) + linebreak() + } else { + hd.body + linebreak() + } + [#link(hd.location(), body)] + linebreak() + while slides.len() > 0 and slides.at(0).location().page() < next-page { + let slide = slides.remove(0) + if display-section { + let next-slide-page = if slides.len() > 0 { + slides.at(0).location().page() + } else { + calc.inf + } + if slide.location().page() <= current-page and current-page < next-slide-page { + [#link(slide.location(), sym.circle.filled)] + } else { + [#link(slide.location(), sym.circle)] + } + } + } + if display-section and display-subsection and linebreaks { + linebreak() + } + }) + } else { + col.push({ + while slides.len() > 0 and slides.at(0).location().page() < next-page { + let slide = slides.remove(0) + if display-subsection { + if hd.level == 2 { + let next-slide-page = if slides.len() > 0 { + slides.at(0).location().page() + } else { + calc.inf + } + if slide.location().page() <= current-page { + [#link(slide.location(), text( + size: .7em, + v(0em) + box(height: 0.8em, sym.circle.filled) + " " + hd.body, + ))] + } else { + [#link(slide.location(), text(size: .7em, v(0em) + box(height: 0.8em, sym.circle) + " " + hd.body))] + } + } + } + } + if display-subsection and linebreaks { + linebreak() + } + }) + } + } + if col != () { + cols.push(align(left, col.sum())) + col = () + } + if current-index < 0 or current-index >= cols.len() { + cols = cols.map(body => text(fill: fill, body)) + } else { + cols = cols + .enumerate() + .map(pair => { + let (idx, body) = pair + if idx == current-index { + text(fill: fill, size: 1.1em, body) + } else { + text(fill: utils.update-alpha(fill, alpha), body) + } + }) + } + set align(top) + show: block.with(inset: (top: .5em, x: 2em)) + show linebreak: it => it + v(-1em) + set text(size: .7em) + grid(columns: cols.map(_ => auto).intersperse(1fr), ..cols.intersperse([])) + } +) + +/// New section slide for the presentation. You can update it by updating the `new-section-slide-fn` argument for `config-common` function. +/// +/// Example: `config-common(new-section-slide-fn: new-section-slide.with(numbered: false))` +/// +/// - config (dictionary): The configuration of the slide. You can use `config-xxx` to set the configuration of the slide. For more several configurations, you can use `utils.merge-dicts` to merge them. +/// +/// - title (string): The title of the slide. Default is `utils.i18n-outline-title`. +/// +/// - body (array): The contents of the slide. +#let new-section-slide(config: (:), title: utils.i18n-outline-title, ..args, body) = touying-slide-wrapper(self => { + self = utils.merge-dicts(self, config-page( + header: grid( + inset: (x: 1.9em), + rows: (auto, auto), + row-gutter: 15%, + grid( + columns: (1fr, 35%), + align(left + horizon, text(size: 1.9em, utils.display-current-heading(depth: self.slide-level, style: auto))), + align(right + horizon, image("vi/nwpu-vi-logo.png", height: 1.05cm)), + ), + v(-2cm), + align(center + horizon, line(length: 100%, stroke: (paint: self.colors.primary, thickness: 1.5pt))), + ), + footer: nwpu-footer, + margin: (top: 8cm, left: 0cm), + )) + touying-slide(self: self, config: config, components.adaptive-columns(text( + hyphenate: false, + size: 1.2em, + fill: self.colors.neutral-darkest, + new-section-mini-slides( + self: self, + fill: self.colors.primary, + alpha: self.store.alpha, + display-section: false, + display-subsection: true, + linebreaks: false, + short-heading: true, + ), + // components.progressive-outline( + // alpha: self.store.alpha, + // title: none, + // indent: 1em, + // depth: self.slide-level, + // transform: (cover: false, alpha: 60%, ..args, it) => if cover { + // if it.level == 1 { + // text(utils.update-alpha(text.fill, alpha), h(2em) + it.element.body + v(0em)) + // } + // } else { + // if it.level == 1 { + // text(weight: "semibold", fill: self.colors.primary, h(1.8em) + it.element.body + v(0em)) + // } else { + // text( + // size: 0.8em, + // weight: "semibold", + // fill: self.colors.primary, + // h(3em) + "- " + it.element.body + v(0em), + // ) + // } + // }, + // ..args, + // ), + ))) +}) + + +/// Focus on some content. +/// +/// Example: `#focus-slide[Wake up!]` +/// +/// - config (dictionary): The configuration of the slide. You can use `config-xxx` to set the configuration of the slide. For more several configurations, you can use `utils.merge-dicts` to merge them. +#let focus-slide(config: (:), body) = touying-slide-wrapper(self => { + self = utils.merge-dicts(self, config-common(freeze-slide-counter: true), config-page( + fill: self.colors.primary, + margin: 2em, + )) + set text(fill: self.colors.neutral-lightest, size: 1.5em) + touying-slide(self: self, config: config, align(horizon + center, body)) +}) + + +#let end-slide(config: (:), body) = touying-slide-wrapper(self => { + self = utils.merge-dicts(self, config-common(freeze-slide-counter: true, new-section-slide-fn: none), config-page( + margin: 2em, + )) + set text(fill: self.colors.primary, size: 1.65em, weight: "bold") + let body = { + set page(background: { + place(left + bottom, rect(width: 100%, height: 26%, fill: self.colors.primary.lighten(91%))) + place(right + top, circle(radius: 2.5cm, stroke: self.colors.primary.lighten(75%) + 1pt)) + }) + block(width: 80%, grid( + columns: (40%, 1fr), + column-gutter: 0pt, + image("vi/nwpu-vi-logo.png"), body, + )) + } + touying-slide(self: self, config: config, align(horizon + center, body)) +}) + + +#let end-slide-red(config: (:), body) = touying-slide-wrapper(self => { + self = utils.merge-dicts(self, config-common(freeze-slide-counter: true), config-page( + fill: self.colors.primary, + margin: 2em, + )) + set text(fill: self.colors.neutral-lightest, size: 1.75em, weight: "bold") + let body = { + align(center, image("vi/nwpu-vi-logo-white.png", width: 42%)) + v(1.8em) + body + } + touying-slide(self: self, config: config, align(top + center, body)) +}) + +/// Touying NWPU theme. +/// +/// Example: +/// +/// ```typst +/// #show: nwpu-theme.with(aspect-ratio: "16-9", config-colors(primary: blue))` +/// ``` +/// +/// The default colors: +/// +/// ```typ +/// config-colors( +/// neutral-darkest: rgb("#000000"), +/// neutral-dark: rgb("#202020"), +/// neutral-light: rgb("#f3f3f3"), +/// neutral-lightest: rgb("#ffffff"), +/// primary: rgb("#0c4842"), +/// ) +/// ``` +/// +/// - aspect-ratio (string): The aspect ratio of the slides. Default is `16-9`. +/// +/// - navigation (string): The navigation of the slides. You can choose from `"sidebar"`, `"mini-slides"`, and `none`. Default is `"sidebar"`. +/// +/// - sidebar (dictionary): The configuration of the sidebar. You can set the width, filled, numbered, indent, and short-heading of the sidebar. Default is `(width: 10em, filled: false, numbered: false, indent: .5em, short-heading: true)`. +/// - width (string): The width of the sidebar. +/// - filled (boolean): Whether the outline in the sidebar is filled. +/// - numbered (boolean): Whether the outline in the sidebar is numbered. +/// - indent (length): The indent of the outline in the sidebar. +/// - short-heading (boolean): Whether the outline in the sidebar is short. +/// +/// - mini-slides (dictionary): The configuration of the mini-slides. You can set the height, x, display-section, display-subsection, and short-heading of the mini-slides. Default is `(height: 4em, x: 2em, display-section: false, display-subsection: true, linebreaks: true, short-heading: true)`. +/// - height (length): The height of the mini-slides. +/// - x (length): The x position of the mini-slides. +/// - display-section (boolean): Whether the slides of sections are displayed in the mini-slides. +/// - display-subsection (boolean): Whether the slides of subsections are displayed in the mini-slides. +/// - linebreaks (boolean): Whether line breaks are in between links for sections and subsections in the mini-slides. +/// - short-heading (boolean): Whether the mini-slides are short. Default is `true`. +/// +/// - footer (content, function): The footer of the slides. Default is `none`. +/// +/// - footer-right (content, function): The right part of the footer. Default is `context utils.slide-counter.display() + " / " + utils.last-slide-number`. +/// +/// - primary (color): The primary color of the slides. Default is `rgb("#0c4842")`. +/// +/// - alpha (fraction, float): The alpha of transparency. Default is `60%`. +/// +/// - outline-title (content, function): The title of the outline. Default is `utils.i18n-outline-title`. +/// +/// - subslide-preamble (content, function): The preamble of the subslide. Default is `self => block(text(1.2em, weight: "bold", fill: self.colors.primary, utils.display-current-heading(depth: self.slide-level)))`. +#let nwpu-theme( + aspect-ratio: "16-9", + navigation: "mini-slides", + font: ("Libertinus Serif", "Microsoft YaHei"), + sidebar: ( + width: 10em, + filled: false, + numbered: false, + indent: .5em, + short-heading: true, + ), + mini-slides: ( + height: 4em, + x: 2em, + display-section: false, + display-subsection: true, + linebreaks: false, + short-heading: true, + ), + footer: none, + footer-right: context utils.slide-counter.display() + " / " + utils.last-slide-number, + primary: rgb("#0054A3"), + alpha: 40%, + subslide-preamble: none, + ..args, + body, +) = { + sidebar = utils.merge-dicts( + (width: 10em, filled: false, numbered: false, indent: .5em, short-heading: true), + sidebar, + ) + mini-slides = utils.merge-dicts( + (height: 4em, x: 2em, display-section: false, display-subsection: true, linebreaks: true, short-heading: true), + mini-slides, + ) + set text(size: 18pt) + set par(justify: true) + + show: touying-slides.with( + config-page(paper: "presentation-" + aspect-ratio, header-ascent: 1.5em, footer-descent: 0em, margin: if navigation + == "sidebar" { + (top: 2em, bottom: 1em, x: sidebar.width) + } else if navigation == "mini-slides" { + (top: if mini-slides.linebreaks { mini-slides.height } else { 5.5em }, bottom: 3em, x: mini-slides.x) + } else { + (top: 5em, bottom: 2em, x: mini-slides.x) + }), + config-common( + slide-fn: slide, + new-section-slide-fn: new-section-slide, + ), + config-methods( + init: (self: none, body) => { + set text(font: font) + show heading: set text(self.colors.primary) + show outline.entry: set block(above: 1em) + + body + }, + alert: utils.alert-with-primary-color, + ), + config-colors( + neutral-darkest: rgb("#000000"), + neutral-dark: rgb("#202020"), + neutral-light: rgb("#f3f3f3"), + neutral-lightest: rgb("#ffffff"), + primary: primary, + ), + // save the variables for later use + config-store( + navigation: navigation, + sidebar: sidebar, + mini-slides: mini-slides, + footer: footer, + footer-right: footer-right, + alpha: alpha, + subslide-preamble: subslide-preamble, + ), + ..args, + ) + + body +} diff --git a/packages/preview/touying-simpl-nwpu/0.1.0/template/main.typ b/packages/preview/touying-simpl-nwpu/0.1.0/template/main.typ new file mode 100644 index 0000000000..eac4241c9e --- /dev/null +++ b/packages/preview/touying-simpl-nwpu/0.1.0/template/main.typ @@ -0,0 +1,68 @@ +#import "@preview/touying:0.6.1": * +#import "@preview/touying-simpl-nwpu:0.1.0": * + +#show: nwpu-theme.with(config-info( + title: [Touying for NWPU: Customize Your Slide Title Here], + subtitle: [Customize Your Slide Subtitle Here], + author: [Authors], + date: datetime.today(), + institution: [Northwestern Polytechnical University], +)) + +#title-slide() + +#outline-slide() + += The section I + +== Slide I / i + +Slide content. + += The section II + +== Slide II / i + +Slide content. + +== Slide II / ii + +Slide content. + += The section III + +== Slide III / i + +Slide content. + +== Slide III / ii + +Slide content. + +== Slide III / iii + +Slide content. + += The section IV + +== Slide IV / i + +Slide content. + +== Slide IV / ii + +Slide content. + +== Slide IV / iii + +Slide content. + +== Slide IV / iv + +Slide content. + +== End + +#end-slide[ + Thanks for Listening! +] diff --git a/packages/preview/touying-simpl-nwpu/0.1.0/thumbnail.png b/packages/preview/touying-simpl-nwpu/0.1.0/thumbnail.png new file mode 100644 index 0000000000..41e443813e Binary files /dev/null and b/packages/preview/touying-simpl-nwpu/0.1.0/thumbnail.png differ diff --git a/packages/preview/touying-simpl-nwpu/0.1.0/typst.toml b/packages/preview/touying-simpl-nwpu/0.1.0/typst.toml new file mode 100644 index 0000000000..56d9a0e117 --- /dev/null +++ b/packages/preview/touying-simpl-nwpu/0.1.0/typst.toml @@ -0,0 +1,15 @@ +[package] +name = "touying-simpl-nwpu" +version = "0.1.0" +entrypoint = "lib.typ" +authors = ["Ramana"] +license = "MIT" +description = "西北工业大学 Touying 幻灯片主题 (Touying Slide Theme for NWPU)" +repository = "https://github.com/RamanaVais/touying-simpl-nwpu" +keywords = ["touying", "slide", "nwpu", "npu"] +categories = ["presentation"] + +[template] +path = "template" +entrypoint = "main.typ" +thumbnail = "thumbnail.png" diff --git a/packages/preview/touying-simpl-nwpu/0.1.0/vi/nwpu-vi-logo-white.png b/packages/preview/touying-simpl-nwpu/0.1.0/vi/nwpu-vi-logo-white.png new file mode 100644 index 0000000000..705d3312ea Binary files /dev/null and b/packages/preview/touying-simpl-nwpu/0.1.0/vi/nwpu-vi-logo-white.png differ diff --git a/packages/preview/touying-simpl-nwpu/0.1.0/vi/nwpu-vi-logo.png b/packages/preview/touying-simpl-nwpu/0.1.0/vi/nwpu-vi-logo.png new file mode 100644 index 0000000000..389f4ebcbe Binary files /dev/null and b/packages/preview/touying-simpl-nwpu/0.1.0/vi/nwpu-vi-logo.png differ