Compare commits
40 Commits
56da014b9b
..
0.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
74d5651876
|
|||
|
8833ef2383
|
|||
| d70e9df912 | |||
|
0162eb8c40
|
|||
| 1ff68050bc | |||
| d6037872dd | |||
|
6bcc345717
|
|||
|
568e84729c
|
|||
| e066e96310 | |||
| 82732e7084 | |||
| f60fefc7b6 | |||
| c3be6125b4 | |||
| 983224a633 | |||
| f70669a2b7 | |||
| bddbd59557 | |||
| cf8c2c8338 | |||
| c9c0577ec8 | |||
|
4242825b32
|
|||
| 228c77d54f | |||
| ad48360c38 | |||
| 651f7ed293 | |||
| 9abbc7c1e1 | |||
| ffa3ce0bdc | |||
| ea4a5a007b | |||
| 1e3b1ce114 | |||
| 1723c5f051 | |||
| 1a7fe4a394 | |||
| af2ba45a16 | |||
| 450b94cede | |||
| f698299a2d | |||
| 9578b5e4f1 | |||
| 8df87de797 | |||
| 6d6e94f0ba | |||
|
a9475dbc94
|
|||
|
19c5fdf19c
|
|||
|
833a179ced
|
|||
| c1f128b528 | |||
| bbdc0d8209 | |||
| 7fb1fd1391 | |||
| cee212ae0a |
@@ -3,7 +3,7 @@
|
||||
|
||||
## General Info
|
||||
|
||||
This project contains two template functions and some utilities for writing NURE works. All functions include documentation comments inside them, so you can explore all possibilities using LSP.
|
||||
This project contains template functions and some utilities for writing NURE works. All functions include documentation comments inside them, so you can explore all possibilities using LSP.
|
||||
|
||||
### Templates
|
||||
|
||||
@@ -19,20 +19,25 @@ This template:
|
||||
- Typesets the bibliography according to ДСТУ 3008:2015 using custom CSL style;
|
||||
- Typesets the outline and appendices according to standard requirements.
|
||||
|
||||
#### `coursework-v2` - New Coursework Variant
|
||||
This template keeps the legacy coursework template intact while offering the newer title/task-page layout. See `template/default/coursework-v2.typ` for an example entrypoint.
|
||||
|
||||
### Utilities
|
||||
- `nheading` - For unnumbered headings, such as "Introduction" and "Conclusion".
|
||||
- `hfill` - Fills horizontal space with a filled box instead of just empty space; useful for creating underlines.
|
||||
- `uline` - Creates underlined fields that need to be filled, such as the name field on the task list.
|
||||
- `bold` - Inserts bold text inside functional environments.
|
||||
- `img` - Inserts images with a caption, automatically deriving the label from the image file name.
|
||||
- `img` - Inserts images with a caption, automatically deriving the label from the image file name (use via `#import "@local/nure:0.1.1": utils` and call `utils.img`).
|
||||
|
||||
**Note:** `img()` is provided in `utils.typ` in project's root directory for compatibility, until [path() type](https://github.com/typst/typst/pull/7555) is released.
|
||||
**Note:** `img()` is provided in `template/utils.typ` so you can copy it into your project root for compatibility, until [path() type](https://github.com/typst/typst/pull/7555) is released.
|
||||
|
||||
## Usage
|
||||
|
||||
### As a local typst package
|
||||
1. Clone this repository into ~/.local/share/typst/packages/:
|
||||
You can install the template as a local Typst package. Choose the instructions for your platform below.
|
||||
|
||||
#### Unix (Linux / macOS)
|
||||
1. Clone this repository into your local Typst packages folder:
|
||||
```bash
|
||||
git clone -b 0.1.1 https://gitea.linerds.us/pencelheimer/typst_nure_template.git ~/.local/share/typst/packages/local/nure/0.1.1
|
||||
```
|
||||
@@ -41,21 +46,67 @@ git clone -b 0.1.1 https://gitea.linerds.us/pencelheimer/typst_nure_template.git
|
||||
typst init @local/nure:0.1.1 project-name
|
||||
```
|
||||
|
||||
#### Windows (symlink method)
|
||||
This method creates a symbolic link between the cloned repository and Typst's local packages folder in `%APPDATA%`. The benefit: when the template is updated on GitHub you only need to `git pull` in the cloned folder — VS Code (and Typst) will use the updated files automatically.
|
||||
|
||||
Step 1 — clone the template (for example, into your Documents folder):
|
||||
```powershell
|
||||
cd $HOME\Documents
|
||||
git clone https://github.com/linerds/typst_nure_template.git
|
||||
```
|
||||
|
||||
Step 2 — create the symlink (PowerShell must be run as Administrator):
|
||||
1. Open Start, find PowerShell, right-click and choose "Run as Administrator".
|
||||
2. Run the following commands as a single block:
|
||||
```powershell
|
||||
# Create local packages folder (if it doesn't exist)
|
||||
New-Item -ItemType Directory -Force -Path "$env:APPDATA\typst\packages\local\nure"
|
||||
|
||||
# Point to the folder you cloned
|
||||
$gitFolder = "$HOME\Documents\typst_nure_template"
|
||||
|
||||
# Create a symbolic link for the package version (match the version in this repo)
|
||||
New-Item -ItemType SymbolicLink -Path "$env:APPDATA\typst\packages\local\nure\0.1.1" -Target $gitFolder
|
||||
```
|
||||
|
||||
Step 3 — use in VS Code
|
||||
Open your project folder (for example, your coursework or lab folder) in VS Code with the Tinymist Typst extension installed. Create `main.typ` and import the package using the official `@local` import:
|
||||
|
||||
```typst
|
||||
#import "@local/nure:0.1.1": nure-report, style
|
||||
// If you need specific style variables from the package:
|
||||
#import "@local/nure:0.1.1/src/style.typ": spacing
|
||||
|
||||
#show: nure-report.with(
|
||||
title: "Report for Lab Work",
|
||||
type: "Lab Work #1",
|
||||
discipline: "Object-Oriented Programming",
|
||||
author: "Surname I.I.",
|
||||
supervisor: "Assoc. Prof. Kuznetsov O.V.",
|
||||
)
|
||||
|
||||
= Work progress
|
||||
Your content...
|
||||
```
|
||||
|
||||
The Windows steps above are intentionally minimal and focused on the symlink workflow. If you prefer not to use symlinks, you can copy `src/` to your project root and import `lib.typ` directly (see the "As a standalone file" section).
|
||||
|
||||
### As a standalone file
|
||||
Copy `src/` to your project's root directory, optionally renaming `src/` to `lib/`.
|
||||
Copy `src/` to your project's root directory, optionally renaming `src/` to `lib/` (then import `src/lib.typ` or `lib/lib.typ` accordingly).
|
||||
|
||||
### In your project
|
||||
```typst
|
||||
// Import the template either from a local package...
|
||||
#import "@local/nure:0.1.0": *
|
||||
#import "@local/nure:0.1.1": *
|
||||
// ...or by importing a lib.typ directly
|
||||
// #import "/lib/lib.typ": *
|
||||
|
||||
// NOTE: all template arguments use kebab-case.
|
||||
|
||||
// 1. Setup the document
|
||||
// by setting values directly...
|
||||
#show: pz-lb.with(
|
||||
title: "Some title",
|
||||
// etc: "and so on",
|
||||
// ...
|
||||
)
|
||||
// ...or using a yaml/toml file
|
||||
@@ -73,7 +124,7 @@ Some text
|
||||
#include "chapters/chapter1.typ"
|
||||
#include "chapters/chapter2.typ"
|
||||
// NOTE: if you want to use variables or utils provided by the package,
|
||||
// you have to import the package or a lib.typ inside a module.
|
||||
// you have to import the package or a lib.typ inside a module (e.g. #import "@local/nure:0.1.1": utils).
|
||||
|
||||
|
||||
// If you ever need appendices in pz-lb template use the show rule
|
||||
@@ -81,6 +132,8 @@ Some text
|
||||
// so it can put bibliography before appendices
|
||||
#show: style.appendices
|
||||
|
||||
// For coursework appendices, pass them via `appendices:` argument instead.
|
||||
|
||||
= Quote
|
||||
#link("https://youtu.be/bJQj1uKtnus")[
|
||||
The art isn't the art, the art is never the art,
|
||||
@@ -122,7 +175,7 @@ semester = 4
|
||||
|
||||
### Notes:
|
||||
1. Use `#v(-spacing)` to remove vertical spacing between titles (this cannot be automatically handled by the template). Variable `spacing` used here is imported from the template.
|
||||
2. When importing `@local/nure:0.1.1` and specifying file paths in functions handled by the package, the path will relative to package's root directory, e.g. setting `#show: coursework.with(bib-path: "bibl.yml")` will evaluate to `~/.local/share/typst/packages/local/nure/0.1.1/bibl.yml`, the same is for `#img` function, which makes it quite annoying and forces one to import `lib.typ` file. Please open an issue or contact us in any other way if you have any advice.
|
||||
2. When importing `@local/nure:0.1.1` and specifying file paths in functions handled by the package, the path will be relative to the package root, e.g. setting `#show: coursework.with(bib-path: "bibl.yml")` will evaluate to `~/.local/share/typst/packages/local/nure/0.1.1/bibl.yml`. The same applies to `utils.img` unless you copy `template/utils.typ` into your project root and import from there.
|
||||
|
||||
### Bibliography Format
|
||||
The template uses a custom CSL (Citation Style Language) file located at `src/csl/dstu-3008-2015.csl` to format bibliography entries.
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
ХНУРЕ:
|
||||
name: Харківський національний університет радіоелектроніки
|
||||
name_en: Kharkiv National University of Radioelectronics
|
||||
edu_programs:
|
||||
ПЗПІ:
|
||||
name_long: Інженерія програмного забезпечення
|
||||
department_gen: Програмної інженерії
|
||||
code: 121 # TODO: change to F2?
|
||||
КУІБ:
|
||||
name_long: Управління інформаційною безпекою
|
||||
department_gen: Інфокомунікаційної інженерії ім. В. В. Поповського
|
||||
code: 125 # TODO: change to F5?
|
||||
КНТ:
|
||||
name_en: CST # computer sciences and technologies
|
||||
name_long: Комп'ютерні науки та технології
|
||||
department_gen: Системотехніки
|
||||
department_en: ST
|
||||
code: 122
|
||||
subjects:
|
||||
DMT: Decision making theory
|
||||
ODS: Основи Dаtа Sсіеnсе # NOTE: Eng O here
|
||||
ІМ: Іноземна мова
|
||||
ІТР: Information Technologies of Reengineering
|
||||
ІТРОІ: Інтернет-технології Розподіленої Обробки Інформації
|
||||
АВпЗ: Аналіз вимог до програмного забезпечення
|
||||
АДан: Аналітика даних
|
||||
АКтаК: Архітектура комп'ютера та комп'ютерних мереж
|
||||
АТСД: Алгоритми та структури даних
|
||||
АтаРК: Аналіз та рефакторинг коду
|
||||
БД: Бази даних
|
||||
БЖД: Безпека життєдіяльності
|
||||
ВДІТБ: Введення до ІТ-бізнесу # NOTE: all in UA
|
||||
ВМ: Вища математика
|
||||
ГТГ: Гіпертекст та гіпермедіа
|
||||
ДМ: Дискретна математика
|
||||
ДПК: Динаміка Проектних Команд
|
||||
ЕРВ: Електрорадіовимірювання
|
||||
КДМА: Комп'ютерна дискретна математика
|
||||
КЗВШ: Креативність з використанням штучного інтелекту
|
||||
КМ: Комп`ютерні мережі
|
||||
ЛМВ: Людино-машинна взаємодія
|
||||
ЛМтБ: Локальні мережі та їх безпека # бидло не знає що українською "їхня"
|
||||
МОКр: Математичні основи криптології
|
||||
МОТДО: Методи оптимізаціі та дослідження операцій
|
||||
МППС: Methodologies of designing software systems
|
||||
МС: Моделювання систем
|
||||
ОІМ: Основи IP-мереж
|
||||
ООАПС: Об'єктно-орієнтований аналіз в проектуванні систем
|
||||
ООП: Об'єктно-орієнтоване програмування
|
||||
ОП: Основи права
|
||||
ОПІ: Основи програмноі інженеріі
|
||||
ОПНJ: Основи програмування на Java
|
||||
ОПр: Основи програмування
|
||||
ОРвІТ: Оцінка Ризиків в IT-проектах
|
||||
ОС: Операційні системи
|
||||
ОТК: Основи теорії кіл
|
||||
ПБІП: Проектування та балансування ігрового процесу
|
||||
ПВJ: Поглиблене вивчення Java
|
||||
ПЕСЕ: Психологія екстремальних стосунків та ефективної адаптації
|
||||
ПНП: Програмування на платформі .NЕТ
|
||||
ПП: Проектний практикум
|
||||
ПРОГ: Програмування
|
||||
ПарП: Параллельне програмування
|
||||
СА: Системний аналіз
|
||||
СМП: Скриптові мови програмування
|
||||
СОАПЗ: Сервіс-Орієнтована Архітектура Програмного Забезпечення
|
||||
СРБД: Серверні рішення баз даних
|
||||
СхТ: Схемотехніка
|
||||
ТВО: Технології Високопродуктивних Обчислень
|
||||
ТЗІ: Технології захисту інформації
|
||||
ТЙтаМ: Теорія ймовірностей та математична # TODO: what?
|
||||
ТКП: Технології комп`ютерного проєктування
|
||||
УФМ: Українське фахове мовлення
|
||||
ФІЗ: Фізика
|
||||
ФІЛ: Філософія
|
||||
ФВС: Фізичне виховання та спорт
|
||||
ХТ: Хмарні технології
|
||||
@@ -1,701 +0,0 @@
|
||||
|
||||
// Academic aliases {{{1
|
||||
|
||||
#let universities = yaml("config/universities.yaml")
|
||||
|
||||
// Template formatting functions {{{1
|
||||
|
||||
/// bold text
|
||||
#let bold(content) = text(weight: "bold")[#content]
|
||||
|
||||
/// numberless heading
|
||||
#let nheading(title) = heading(depth: 1, numbering: none, title)
|
||||
|
||||
/// fill horizontal space with a box and not an empty space
|
||||
#let hfill(width) = box(
|
||||
width: width,
|
||||
repeat(" "),
|
||||
) // NOTE: This is a HAIR SPACE (U+200A), not a regular space
|
||||
|
||||
/// make underlined cell with filled value
|
||||
#let uline(align: center, content) = underline[
|
||||
#if align != left { hfill(1fr) }
|
||||
#content
|
||||
#if align != right { hfill(1fr) }
|
||||
]
|
||||
|
||||
/// month name from its number
|
||||
#let month_gen(month) = (
|
||||
"січня",
|
||||
"лютого",
|
||||
"березня",
|
||||
"квітня",
|
||||
"травня",
|
||||
"червня",
|
||||
"липня",
|
||||
"серпня",
|
||||
"вересня",
|
||||
"жовтня",
|
||||
"листопада",
|
||||
"грудня",
|
||||
).at(month - 1)
|
||||
|
||||
#let is-cyr(c) = regex("[\p{Cyrillic}]") in c
|
||||
|
||||
#let gender-form(verb, gender: "p") = {
|
||||
(
|
||||
"author": ("m": "Виконав", "f": "Виконала", "p": "Виконали"),
|
||||
"mentor": ("m": "Перевірив", "f": "Перевірила", "p": "Перевірили"),
|
||||
)
|
||||
.at(verb)
|
||||
.at(if gender == "m" or gender == "f" { gender } else { "p" }, default: "p")
|
||||
}
|
||||
|
||||
#let pz-lb-title(type, number: none) = {
|
||||
let type-title = (
|
||||
"ЛБ": [Звіт \ з лабораторної роботи],
|
||||
"ПЗ": [Звіт \ з практичної роботи],
|
||||
"КР": [Контрольна робота],
|
||||
"РФ": [Реферат], // зрада
|
||||
).at(type, default: type)
|
||||
if number != none { [#type-title №#number] } else { [#type-title] }
|
||||
}
|
||||
|
||||
// Helper functions {{{1
|
||||
|
||||
/// captioned image with label derived from path:
|
||||
/// - "image.png" = @image
|
||||
/// - "img/image.png" = @image
|
||||
/// - "img/foo/image.png" = @foo_image
|
||||
/// - "img/foo/foo_image.png" = @foo_image
|
||||
/// the caption will be modified based on a conditional positional value:
|
||||
/// - `none`: no change
|
||||
/// - some value: "`caption` (за даними `value`)"
|
||||
/// - no value: "`caption` (рисунок виконано самостійно)"
|
||||
/// additional named arguments will be passed to original `image` function
|
||||
#let img(path, caption, ..sink) = {
|
||||
let parts = path.split(".").first().split("/")
|
||||
|
||||
let label_string = if (
|
||||
parts.len() <= 2 or parts.at(-1).starts-with(parts.at(-2))
|
||||
) {
|
||||
// ("image",), (_, "image") and (.., "img", "img_image")
|
||||
parts.last()
|
||||
} else {
|
||||
// (.., "img", "image") = "img_image"
|
||||
parts.at(-2) + "_" + parts.at(-1)
|
||||
}.replace(" ", "_")
|
||||
|
||||
let caption = if sink.pos().len() == 0 {
|
||||
caption
|
||||
} else if sink.pos().first() == none {
|
||||
caption + " (рисунок виконано самостійно)"
|
||||
} else {
|
||||
[#caption (за даними #sink.pos().first())]
|
||||
}
|
||||
|
||||
[#figure(
|
||||
image(path, ..sink.named()),
|
||||
caption: caption,
|
||||
) #label(label_string)]
|
||||
}
|
||||
|
||||
#let spacing = 0.95em // spacing between lines
|
||||
#let num-to-alpha = "абвгдежиклмнпрстуфхцшщюя".split("") // 0 = "", 1 = "а"
|
||||
|
||||
/// DSTU 3008:2015 Style
|
||||
/// -> content
|
||||
/// - it (content): Content to apply the style to.
|
||||
/// - skip (int): Do not show page number for this number of pages.
|
||||
/// - offset (int): Adjust all page numbers by this amount.
|
||||
#let dstu-style(
|
||||
it,
|
||||
skip: 0,
|
||||
offset: 0,
|
||||
) = {
|
||||
// General Styling {{{1
|
||||
set page(
|
||||
paper: "a4",
|
||||
number-align: top + right,
|
||||
margin: (top: 20mm, right: 10mm, bottom: 20mm, left: 25mm),
|
||||
numbering: (i, ..) => if i > skip { numbering("1", i + offset) },
|
||||
)
|
||||
|
||||
set text(
|
||||
lang: "uk",
|
||||
size: 14pt,
|
||||
hyphenate: false,
|
||||
font: ("Times New Roman", "Liberation Serif"),
|
||||
)
|
||||
|
||||
set par(
|
||||
justify: true,
|
||||
spacing: spacing,
|
||||
leading: spacing,
|
||||
first-line-indent: (amount: 1.25cm, all: true),
|
||||
)
|
||||
|
||||
set block(spacing: spacing)
|
||||
set underline(evade: false)
|
||||
|
||||
// Enums & Lists {{{1
|
||||
// First level
|
||||
set enum(
|
||||
indent: 1.25cm,
|
||||
body-indent: 0.5cm,
|
||||
numbering: i => { num-to-alpha.at(i) + ")" },
|
||||
)
|
||||
|
||||
// Second level and further nesting
|
||||
show enum: it => {
|
||||
set enum(indent: 0em, numbering: "1)")
|
||||
it
|
||||
}
|
||||
|
||||
// Lists are not intended for multiple levels, use `enum`
|
||||
set list(indent: 1.35cm, body-indent: 0.5cm, marker: [--])
|
||||
|
||||
// Figures {{{1
|
||||
show figure: it => {
|
||||
v(spacing * 2, weak: true)
|
||||
it
|
||||
v(spacing * 2, weak: true)
|
||||
}
|
||||
|
||||
set figure.caption(separator: [ -- ])
|
||||
show figure.where(kind: table): set figure.caption(position: top)
|
||||
show figure.caption.where(kind: table): set align(left)
|
||||
show figure.where(kind: raw): set figure.caption(position: top)
|
||||
show figure.where(kind: raw): set align(left)
|
||||
|
||||
// Numbering {{{1
|
||||
show heading.where(level: 1): it => {
|
||||
counter(math.equation).update(0)
|
||||
counter(figure.where(kind: raw)).update(0)
|
||||
counter(figure.where(kind: image)).update(0)
|
||||
counter(figure.where(kind: table)).update(0)
|
||||
it
|
||||
}
|
||||
set figure(numbering: i => numbering("1.1", counter(heading).get().at(0), i))
|
||||
set math.equation(numbering: i => numbering("(1.1)", counter(heading).get().at(0), i))
|
||||
|
||||
// Headings {{{1
|
||||
set heading(numbering: "1.1")
|
||||
|
||||
show heading: it => if it.level == 1 {
|
||||
set align(center)
|
||||
set text(size: 14pt, weight: "semibold")
|
||||
|
||||
pagebreak(weak: true)
|
||||
upper(it)
|
||||
v(spacing * 2, weak: true)
|
||||
} else {
|
||||
set text(size: 14pt, weight: "regular")
|
||||
|
||||
v(spacing * 2, weak: true)
|
||||
block(width: 100%, spacing: 0em)[
|
||||
#h(1.25cm)
|
||||
#counter(heading).display(auto)
|
||||
#it.body
|
||||
]
|
||||
v(spacing * 2, weak: true)
|
||||
}
|
||||
|
||||
show heading.where(level: 3): it => {
|
||||
set text(size: 14pt, weight: "regular")
|
||||
|
||||
v(spacing * 2, weak: true)
|
||||
block(width: 100%, spacing: 0em)[
|
||||
#h(1.25cm)
|
||||
#counter(heading).display(it.numbering)
|
||||
#it.body
|
||||
]
|
||||
v(spacing * 2, weak: true)
|
||||
}
|
||||
|
||||
// listings {{{3
|
||||
show raw.where(block: true): it => {
|
||||
let new_spacing = 0.5em
|
||||
set block(spacing: new_spacing)
|
||||
set par(spacing: new_spacing, leading: new_spacing)
|
||||
set text(
|
||||
size: 11pt,
|
||||
weight: "semibold",
|
||||
font: ("Courier New", "Liberation Mono"),
|
||||
)
|
||||
|
||||
v(spacing * 2.5, weak: true)
|
||||
pad(it, left: 1.25cm)
|
||||
v(spacing * 2.5, weak: true)
|
||||
}
|
||||
|
||||
it
|
||||
// }}}
|
||||
}
|
||||
|
||||
/// DSTU 3008:2015 Appendices Style
|
||||
/// -> content
|
||||
/// - it (content): Content to apply the style to.
|
||||
#let appendices-style(it) = /* {{{ */ {
|
||||
// Numbering
|
||||
counter(heading).update(0)
|
||||
set heading(numbering: (i, ..n) => upper(num-to-alpha.at(i)) + numbering(".1.1", ..n))
|
||||
set figure(numbering: i => upper(num-to-alpha.at(counter(heading).get().at(0))).i)
|
||||
set math.equation(numbering: i => upper(num-to-alpha.at(counter(heading).get().at(0))).i)
|
||||
|
||||
// Heading supplement (Heading name shown when citing with @ref)
|
||||
set heading(supplement: [Додаток])
|
||||
|
||||
// Headings
|
||||
show heading: it => if it.level == 1 {
|
||||
set align(center)
|
||||
set text(size: 14pt, weight: "regular")
|
||||
|
||||
pagebreak(weak: true)
|
||||
bold([ДОДАТОК #counter(heading).display(auto)])
|
||||
linebreak()
|
||||
it.body
|
||||
v(spacing * 2, weak: true)
|
||||
} else {
|
||||
set text(size: 14pt, weight: "regular")
|
||||
|
||||
v(spacing * 2, weak: true)
|
||||
block(width: 100%, spacing: 0em)[
|
||||
#h(1.25cm)
|
||||
#counter(heading).display(auto)
|
||||
#it.body
|
||||
]
|
||||
v(spacing * 2, weak: true)
|
||||
}
|
||||
|
||||
it
|
||||
} // }}}
|
||||
|
||||
// Coursework template {{{1
|
||||
|
||||
/// DSTU 3008:2015 Template for NURE
|
||||
/// -> content
|
||||
/// - doc (content): Content to apply the template to.
|
||||
/// - title (str): Title of the document.
|
||||
/// - subject (str): Subject short name.
|
||||
/// - authors ((name: str, full_name_gen: str, variant: int, course: int, semester: int, group: str, gender: str),): List of authors.
|
||||
/// - mentors ((name: str, degree: str),): List of mentors.
|
||||
/// - task_list (done_date: datetime, initial_date: datetime, source: (content | str), content: (content | str), graphics: (content | str)): Task list object.
|
||||
/// - calendar_plan ( plan_table: (content | str), approval_date: datetime): Calendar plan object.
|
||||
/// - abstract (keywords: (str, ), text: (content | str)): Abstract object.
|
||||
/// - bib_path path: Path to the bibliography yaml file.
|
||||
/// - appendices (content): Content with appendices.
|
||||
#let coursework(
|
||||
doc,
|
||||
university: "ХНУРЕ",
|
||||
subject: none,
|
||||
title: none,
|
||||
authors: (),
|
||||
mentors: (),
|
||||
task_list: (),
|
||||
calendar_plan: (),
|
||||
abstract: (),
|
||||
bib_path: none,
|
||||
appendices: (),
|
||||
) = {
|
||||
set document(title: title, author: authors.map(c => c.name))
|
||||
|
||||
show: dstu-style.with(skip: 1)
|
||||
|
||||
let bib-count = state("citation-counter", ())
|
||||
show cite: it => {
|
||||
it
|
||||
bib-count.update(((..c)) => (..c, it.key))
|
||||
}
|
||||
show bibliography: it => {
|
||||
set text(size: 0pt)
|
||||
it
|
||||
}
|
||||
|
||||
let author = authors.first()
|
||||
let head_mentor = mentors.first()
|
||||
let uni = universities.at(university)
|
||||
let edu_prog = uni.edu_programs.at(author.edu_program)
|
||||
|
||||
// page 1 {{{2
|
||||
[
|
||||
#set align(center)
|
||||
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ\
|
||||
#upper(uni.name)
|
||||
|
||||
\
|
||||
|
||||
Кафедра #edu_prog.department_gen
|
||||
|
||||
\
|
||||
|
||||
ПОЯСНЮВАЛЬНА ЗАПИСКА\
|
||||
ДО КУРСОВОЇ РОБОТИ\
|
||||
з дисципліни: "#uni.subjects.at(subject, default: subject)"\
|
||||
Тема роботи: "#title"
|
||||
|
||||
\ \ \
|
||||
|
||||
#columns(2, gutter: 4cm)[
|
||||
#set align(left)
|
||||
#set par(first-line-indent: 0pt)
|
||||
|
||||
#gender-form("author", gender: author.gender) ст. гр. #author.edu_program\-#author.group
|
||||
|
||||
\
|
||||
Керівник:\
|
||||
#head_mentor.degree
|
||||
|
||||
\
|
||||
Робота захищена на оцінку
|
||||
|
||||
\
|
||||
Комісія:\
|
||||
#for m in mentors { [#m.degree\ ] }
|
||||
|
||||
#colbreak()
|
||||
#set align(left)
|
||||
|
||||
|
||||
#author.name
|
||||
|
||||
\ \
|
||||
#head_mentor.name
|
||||
|
||||
\
|
||||
#underline(" " * 35)
|
||||
|
||||
\ \
|
||||
#for m in mentors { [#m.name\ ] }
|
||||
]
|
||||
|
||||
#v(1fr)
|
||||
|
||||
Харків -- #task_list.done_date.display("[year]")
|
||||
|
||||
#pagebreak()
|
||||
]
|
||||
|
||||
// page 2 {{{2
|
||||
{
|
||||
uline[#uni.name]
|
||||
|
||||
linebreak()
|
||||
linebreak()
|
||||
|
||||
grid(
|
||||
columns: (100pt, 1fr),
|
||||
bold[
|
||||
Кафедра
|
||||
Дисципліна
|
||||
Спеціальність
|
||||
],
|
||||
{
|
||||
uline(align: left, edu_prog.department_gen)
|
||||
linebreak()
|
||||
uline(align: left, uni.subjects.at(subject, default: subject))
|
||||
linebreak()
|
||||
uline(align: left, [#edu_prog.code #edu_prog.name_long])
|
||||
},
|
||||
)
|
||||
grid(
|
||||
columns: (1fr, 1fr, 1fr),
|
||||
gutter: 0.3fr,
|
||||
[#bold[Курс] #uline(author.course)],
|
||||
[#bold[Група] #uline([#author.edu_program\-#author.group])],
|
||||
[#bold[Семестр] #uline(author.semester)],
|
||||
)
|
||||
|
||||
linebreak()
|
||||
linebreak()
|
||||
linebreak()
|
||||
|
||||
align(center, bold[ЗАВДАННЯ \ на курсову роботу студента])
|
||||
|
||||
linebreak()
|
||||
|
||||
uline(align: left)[_#author.full_name_gen _]
|
||||
|
||||
linebreak()
|
||||
linebreak()
|
||||
|
||||
bold[\1. Тема роботи:]
|
||||
uline[#title.]
|
||||
|
||||
linebreak()
|
||||
|
||||
{
|
||||
bold[\2. Строк здачі закінченої роботи:]
|
||||
uline(task_list.done_date.display("[day].[month].[year]"))
|
||||
hfill(10fr)
|
||||
}
|
||||
|
||||
linebreak()
|
||||
|
||||
bold[\3. Вихідні дані для роботи:]
|
||||
uline(task_list.source)
|
||||
|
||||
linebreak()
|
||||
|
||||
bold[\4. Зміст розрахунково-пояснювальної записки:]
|
||||
uline(task_list.content)
|
||||
|
||||
linebreak()
|
||||
|
||||
bold[\5. Перелік графічного матеріалу:]
|
||||
uline(task_list.graphics)
|
||||
|
||||
linebreak()
|
||||
|
||||
{
|
||||
bold[\6. Дата видачі завдання:]
|
||||
uline(task_list.initial_date.display("[day].[month].[year]"))
|
||||
hfill(10fr)
|
||||
}
|
||||
|
||||
pagebreak()
|
||||
}
|
||||
|
||||
// page 3 {{{2
|
||||
{
|
||||
align(center, bold[КАЛЕНДАРНИЙ ПЛАН])
|
||||
set par(first-line-indent: 0pt)
|
||||
|
||||
linebreak()
|
||||
|
||||
calendar_plan.plan_table
|
||||
|
||||
linebreak()
|
||||
|
||||
grid(
|
||||
columns: (5fr, 5fr),
|
||||
grid(
|
||||
columns: (1fr, 2fr, 1fr),
|
||||
gutter: 0.2fr,
|
||||
[
|
||||
Студент \
|
||||
Керівник \
|
||||
#align(center)["#underline[#calendar_plan.approval_date.day()]"]
|
||||
],
|
||||
[
|
||||
#uline(align: center, []) \
|
||||
#uline(align: center, []) \
|
||||
#uline(align: center, month_gen(calendar_plan.approval_date.month()))
|
||||
],
|
||||
[
|
||||
\ \
|
||||
#underline[#calendar_plan.approval_date.year()] р.
|
||||
],
|
||||
),
|
||||
[
|
||||
#author.name, \
|
||||
#head_mentor.degree
|
||||
#head_mentor.name.
|
||||
],
|
||||
)
|
||||
|
||||
pagebreak()
|
||||
}
|
||||
|
||||
// page 4 {{{2
|
||||
[
|
||||
#align(center, bold[РЕФЕРАТ]) \
|
||||
|
||||
#context [
|
||||
#let pages = counter(page).final().at(0)
|
||||
#let images = query(figure.where(kind: image)).len()
|
||||
#let tables = query(figure.where(kind: table)).len()
|
||||
#let bibs = bib-count.final().dedup().len()
|
||||
/* TODO: why this stopped working?
|
||||
#let tables = counter(figure.where(kind: table)).final().at(0)
|
||||
#let images = counter(figure.where(kind: image)).final().at(0)*/
|
||||
|
||||
#let counters = ()
|
||||
|
||||
#if pages != 0 { counters.push[#pages с.] }
|
||||
#if tables != 0 { counters.push[#tables табл.] }
|
||||
#if images != 0 { counters.push[#images рис.] }
|
||||
#if bibs != 0 { counters.push[#bibs джерел] }
|
||||
|
||||
Пояснювальна записка до курсової роботи: #counters.join(", ").
|
||||
]
|
||||
|
||||
\
|
||||
|
||||
#(
|
||||
abstract
|
||||
.keywords
|
||||
.map(upper)
|
||||
.sorted(by: (a, b) => {
|
||||
if is-cyr(a) != is-cyr(b) { true } else { a < b }
|
||||
})
|
||||
.join(", ")
|
||||
)
|
||||
|
||||
|
||||
\
|
||||
|
||||
#abstract.text
|
||||
]
|
||||
|
||||
// page 5 {{{2
|
||||
outline(
|
||||
title: [
|
||||
ЗМІСТ
|
||||
#v(spacing * 2, weak: true)
|
||||
],
|
||||
depth: 2,
|
||||
indent: auto,
|
||||
)
|
||||
|
||||
doc
|
||||
|
||||
// bibliography {{{2
|
||||
{
|
||||
heading(depth: 1, numbering: none)[Перелік джерел посилання]
|
||||
|
||||
bibliography(
|
||||
bib_path,
|
||||
style: "ieee",
|
||||
full: true,
|
||||
title: none,
|
||||
)
|
||||
|
||||
let bib_data = yaml(bib_path)
|
||||
|
||||
let format-entry(c) = {
|
||||
if (c.type == "Web") {
|
||||
let date_array = c.url.date.split("-")
|
||||
let date = datetime(
|
||||
year: int(date_array.at(0)),
|
||||
month: int(date_array.at(1)),
|
||||
day: int(date_array.at(2)),
|
||||
)
|
||||
[#c.title. #c.author. URL: #c.url.value (дата звернення: #date.display("[day].[month].[year]")).]
|
||||
} else if (
|
||||
c.type == "Book"
|
||||
) [#c.author #c.title. #c.publisher, #c.date. #c.page-total c. ] else [
|
||||
UNSUPPORTED BIBLIOGRAPHY ENTRY TYPE, PLEASE OPEN AN ISSUE
|
||||
]
|
||||
}
|
||||
|
||||
show enum.item: it => {
|
||||
set par(first-line-indent: 0pt)
|
||||
box(width: 1.25cm)
|
||||
box(width: 1em + 0.5cm)[#it.number.]
|
||||
it.body
|
||||
linebreak()
|
||||
}
|
||||
|
||||
context {
|
||||
for (i, citation) in query(ref.where(element: none))
|
||||
.map(r => str(r.target))
|
||||
.dedup()
|
||||
.enumerate() {
|
||||
enum.item(
|
||||
i + 1,
|
||||
format-entry(bib_data.at(citation)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appendices-style(appendices)
|
||||
}
|
||||
|
||||
// Practice and Laboratory works template {{{1
|
||||
|
||||
/// DSTU 3008:2015 Template for NURE
|
||||
/// -> content
|
||||
/// - doc (content): Content to apply the template to.
|
||||
/// - university: "ХНУРЕ": University metadata. Optional.
|
||||
/// - subject: str: Subject shortcode.
|
||||
/// - type: ("ЛБ" | "ПЗ" | "КР" | "РФ" | str): Work type.
|
||||
/// - number: int or none: Work number. Optional.
|
||||
/// - title: str or none: Work title. Optional.
|
||||
/// - authors ((name: str, full_name_gen: str or none, edu_program: str, group: str, gender: str, variant: int or none),): List of authors.
|
||||
/// - mentors ((name: str, degree: str, gender: ("m" | "f" | "p" | none)),): List of mentors. Optional.
|
||||
#let pz-lb(
|
||||
doc,
|
||||
university: "ХНУРЕ",
|
||||
subject: none,
|
||||
type: none,
|
||||
number: none,
|
||||
title: none,
|
||||
authors: (),
|
||||
mentors: (),
|
||||
) = {
|
||||
// TODO: add actually relevant asserts
|
||||
|
||||
let edu_program = authors.first().edu_program
|
||||
let uni = universities.at(university)
|
||||
|
||||
set document(title: title, author: authors.map(c => c.name))
|
||||
|
||||
show: dstu-style.with(skip: 1)
|
||||
|
||||
// page 1 {{{2
|
||||
|
||||
align(center)[
|
||||
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ \
|
||||
#upper(uni.name)
|
||||
|
||||
\ \
|
||||
Кафедра #uni.edu_programs.at(edu_program).department_gen
|
||||
|
||||
\ \ \
|
||||
#pz-lb-title(type, number: number)
|
||||
|
||||
з дисципліни: "#uni.subjects.at(subject, default: subject)"
|
||||
#if title != none [\ з теми: "#eval(title, mode: "markup")"]
|
||||
|
||||
|
||||
\ \ \ \
|
||||
#columns(2)[
|
||||
#set align(left)
|
||||
#set par(first-line-indent: 0pt)
|
||||
|
||||
#if authors.len() == 1 {
|
||||
[#gender-form("author", gender: if "gender" in a.keys() { a.gender } else { none }):\ ]
|
||||
let a = authors.first()
|
||||
[ст. гр. #a.edu_program\-#a.group\ #a.name\ ]
|
||||
if a.variant != none [Варіант: №#a.variant]
|
||||
} else if authors.len() > 1 [
|
||||
#gender-form("author"):\
|
||||
#for a in authors [ст. гр. #a.edu_program\-#a.group\ #a.name\ ]
|
||||
]
|
||||
|
||||
#colbreak()
|
||||
#set align(right)
|
||||
|
||||
#if mentors.len() == 1 {
|
||||
[#gender-form("mentor", gender: if "gender" in m.keys() { m.gender } else { none }):\ ]
|
||||
let m = mentors.first()
|
||||
if "degree" in m.keys() and m.degree != none [#m.degree\ ]
|
||||
[#m.name\ ]
|
||||
} else if mentors.len() > 1 [
|
||||
#gender-form("mentor"):\
|
||||
#for mentor in mentors { [#mentor.degree\ #mentor.name\ ] }]
|
||||
]
|
||||
|
||||
#v(1fr)
|
||||
|
||||
Харків -- #datetime.today().display("[year]")
|
||||
]
|
||||
|
||||
pagebreak(weak: true)
|
||||
|
||||
// TODO(unexplrd): wrap my head around the old way
|
||||
if title == none {
|
||||
if number == none { context counter(heading).update(1) } else {
|
||||
context counter(heading).update(number)
|
||||
}
|
||||
} else {
|
||||
if number != none { context counter(heading).update(number - 1) }
|
||||
heading(eval(title, mode: "markup"))
|
||||
}
|
||||
|
||||
doc
|
||||
}
|
||||
|
||||
// vim:sts=2:sw=2:fdm=marker:cms=/*%s*/
|
||||
@@ -19,49 +19,72 @@
|
||||
code: 122
|
||||
subjects:
|
||||
DMT: Decision making theory
|
||||
NoSQL: NoSQL-системи
|
||||
ODS: Основи Dаtа Sсіеnсе # NOTE: Eng O here
|
||||
ІМ: Іноземна мова
|
||||
ІТР: Information Technologies of Reengineering
|
||||
ІТРОІ: Інтернет-технології Розподіленої Обробки Інформації
|
||||
АВпЗ: Аналіз вимог до програмного забезпечення
|
||||
АДан: Аналітика даних
|
||||
АКС: Архітектура комп'ютерних систем
|
||||
АКтаК: Архітектура комп'ютера та комп'ютерних мереж
|
||||
АТСД: Алгоритми та структури даних
|
||||
АПЗ: Архітектура програмного забезпечення
|
||||
АтаРК: Аналіз та рефакторинг коду
|
||||
АТСД: Алгоритми та структури даних
|
||||
БД: Бази даних
|
||||
БЖД: Безпека життєдіяльності
|
||||
ВSAP: Введення до SAP-технологій
|
||||
ВДІТБ: Введення до ІТ-бізнесу # NOTE: all in UA
|
||||
ВМ: Вища математика
|
||||
ВМПтФ: Високорівневі мови програмування та фреймворки
|
||||
ГТГ: Гіпертекст та гіпермедіа
|
||||
ДМ: Дискретна математика
|
||||
ДПК: Динаміка Проектних Команд
|
||||
ЕРВ: Електрорадіовимірювання
|
||||
ІКС: Інформаційно-комунікаційні системи
|
||||
ІМ: Іноземна мова
|
||||
ІМпк: Іноземна мова для професійної комунікації
|
||||
ІНТ: Історія науки і техніки
|
||||
ІТР: Information Technologies of Reengineering
|
||||
ІТРОІ: Інтернет-технології Розподіленої Обробки Інформації
|
||||
КДМА: Комп'ютерна дискретна математика
|
||||
КЗВШ: Креативність з використанням штучного інтелекту
|
||||
КМ: Комп`ютерні мережі
|
||||
КМ: Комп'ютерні мережі
|
||||
ЛМВ: Людино-машинна взаємодія
|
||||
ЛМтБ: Локальні мережі та їх безпека # бидло не знає що українською "їхня"
|
||||
ЛМтБ: Локальні мережі та їх безпека
|
||||
Лог: Логіка
|
||||
МБ: Мережна безпека
|
||||
МКр: Мережна криміналістика
|
||||
МОКр: Математичні основи криптології
|
||||
МОТДО: Методи оптимізаціі та дослідження операцій
|
||||
МОТІ: Методи оптимізації та теорія ігор
|
||||
МППЗ: Менеджмент проектів програмного забезпечення
|
||||
МППС: Methodologies of designing software systems
|
||||
МС: Моделювання систем
|
||||
ОІДт: Обробка ігрових даних та звітів
|
||||
ОІМ: Основи IP-мереж
|
||||
ОКЗІ: Основи криптографічного захисту інформації
|
||||
ООАПС: Об'єктно-орієнтований аналіз в проектуванні систем
|
||||
ООП: Об'єктно-орієнтоване програмування
|
||||
ОП: Основи права
|
||||
ОПІ: Основи програмноі інженеріі
|
||||
ОПНJ: Основи програмування на Java
|
||||
ОП: Основи права
|
||||
ОПр: Основи програмування
|
||||
ОРвІТ: Оцінка Ризиків в IT-проектах
|
||||
ОС: Операційні системи
|
||||
ОТК: Основи теорії кіл
|
||||
ПPyt: Програмування мовою Python
|
||||
ПарП: Параллельне програмування
|
||||
ПБІП: Проектування та балансування ігрового процесу
|
||||
ПВJS: Поглиблене вивчення JavaScript
|
||||
ПВJ: Поглиблене вивчення Java
|
||||
ПвІТ: Підприємництво в ІТ
|
||||
ПЕСЕ: Психологія екстремальних стосунків та ефективної адаптації
|
||||
ПНП: Програмування на платформі .NЕТ
|
||||
ПП: Проектний практикум
|
||||
ПРОГ: Програмування
|
||||
ПарП: Параллельне програмування
|
||||
Про1: Програмування (ч. 1)
|
||||
Про2: Програмування (ч. 2)
|
||||
Про3: Програмування (ч. 3)
|
||||
Прог: Програмування # NOTE: was "ПРОГ" before
|
||||
РNet: Робота з даними на платформі .Net
|
||||
РХЗ: Розробка хмарних застосувань в AZURE
|
||||
СА: Системний аналіз
|
||||
СМП: Скриптові мови програмування
|
||||
СОАПЗ: Сервіс-Орієнтована Архітектура Програмного Забезпечення
|
||||
@@ -69,10 +92,11 @@
|
||||
СхТ: Схемотехніка
|
||||
ТВО: Технології Високопродуктивних Обчислень
|
||||
ТЗІ: Технології захисту інформації
|
||||
ТІК: Теорія інформації та кодування
|
||||
ТЙтаМ: Теорія ймовірностей та математична # TODO: what?
|
||||
ТКП: Технології комп`ютерного проєктування
|
||||
ТКП: Технології комп'ютерного проєктування
|
||||
УФМ: Українське фахове мовлення
|
||||
ФВС: Фізичне виховання та спорт
|
||||
ФІЗ: Фізика
|
||||
ФІЛ: Філософія
|
||||
ФВС: Фізичне виховання та спорт
|
||||
ХТ: Хмарні технології
|
||||
|
||||
@@ -0,0 +1,420 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<style xmlns="http://purl.org/net/xbiblio/csl"
|
||||
class="in-text"
|
||||
version="1.0"
|
||||
default-locale="uk-UA"
|
||||
demote-non-dropping-particle="never">
|
||||
|
||||
<info>
|
||||
<title>ДСТУ 8302:2015 — бібліографічні посилання</title>
|
||||
<title-short>ДСТУ 8302:2015</title-short>
|
||||
<id>dstu-8302-2015-typst</id>
|
||||
<link href="https://online.budstandart.com/ua/catalog/doc-page.html?id_doc=64411" rel="documentation"/>
|
||||
<author>
|
||||
<name>Custom Typst CSL</name>
|
||||
</author>
|
||||
<category citation-format="numeric"/>
|
||||
<category field="generic-base"/>
|
||||
<summary>CSL для оформлення переліку джерел за логікою ДСТУ 8302:2015 у Typst/Pandoc: числові посилання, українські скорочення, DOI/URL, дата звернення, коректні вебджерела, статті та нормативні акти.</summary>
|
||||
<updated>2026-05-25T00:00:00+00:00</updated>
|
||||
<rights license="http://creativecommons.org/licenses/by-sa/4.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 License</rights>
|
||||
</info>
|
||||
|
||||
<locale xml:lang="uk-UA">
|
||||
<terms>
|
||||
<term name="accessed">дата звернення</term>
|
||||
<term name="page" form="short">С.</term>
|
||||
<term name="page" form="symbol">с.</term>
|
||||
<term name="volume" form="short">Т.</term>
|
||||
<term name="issue" form="short">№</term>
|
||||
<term name="edition" form="short">вид.</term>
|
||||
<term name="editor" form="short">ред.</term>
|
||||
<term name="translator" form="short">пер.</term>
|
||||
<term name="et-al">та ін.</term>
|
||||
<term name="and">,</term>
|
||||
</terms>
|
||||
</locale>
|
||||
|
||||
<!-- Ініціали після прізвища: Vergara L. M., Park M. -->
|
||||
<macro name="contributors">
|
||||
<names variable="author">
|
||||
<name name-as-sort-order="all"
|
||||
sort-separator=" "
|
||||
initialize-with=". "
|
||||
delimiter=", "
|
||||
delimiter-precedes-last="never"/>
|
||||
<substitute>
|
||||
<names variable="editor">
|
||||
<name name-as-sort-order="all"
|
||||
sort-separator=" "
|
||||
initialize-with=". "
|
||||
delimiter=", "
|
||||
delimiter-precedes-last="never"/>
|
||||
<label form="short" prefix=" (" suffix=")"/>
|
||||
</names>
|
||||
<names variable="translator">
|
||||
<name name-as-sort-order="all"
|
||||
sort-separator=" "
|
||||
initialize-with=". "
|
||||
delimiter=", "
|
||||
delimiter-precedes-last="never"/>
|
||||
<label form="short" prefix=" (" suffix=")"/>
|
||||
</names>
|
||||
</substitute>
|
||||
</names>
|
||||
</macro>
|
||||
|
||||
<macro name="title">
|
||||
<text variable="title"/>
|
||||
</macro>
|
||||
|
||||
<macro name="container">
|
||||
<text variable="container-title"/>
|
||||
</macro>
|
||||
|
||||
<macro name="site-or-publisher">
|
||||
<choose>
|
||||
<if variable="container-title">
|
||||
<text variable="container-title"/>
|
||||
</if>
|
||||
<else>
|
||||
<text variable="publisher"/>
|
||||
</else>
|
||||
</choose>
|
||||
</macro>
|
||||
|
||||
<macro name="issued-year">
|
||||
<date variable="issued">
|
||||
<date-part name="year"/>
|
||||
</date>
|
||||
</macro>
|
||||
|
||||
<macro name="issued-date">
|
||||
<date variable="issued">
|
||||
<date-part name="day" form="numeric-leading-zeros" suffix="."/>
|
||||
<date-part name="month" form="numeric-leading-zeros" suffix="."/>
|
||||
<date-part name="year"/>
|
||||
</date>
|
||||
</macro>
|
||||
|
||||
<macro name="accessed-date">
|
||||
<date variable="accessed">
|
||||
<date-part name="day" form="numeric-leading-zeros" suffix="."/>
|
||||
<date-part name="month" form="numeric-leading-zeros" suffix="."/>
|
||||
<date-part name="year"/>
|
||||
</date>
|
||||
</macro>
|
||||
|
||||
<macro name="publisher-place">
|
||||
<group delimiter=" : ">
|
||||
<text variable="publisher-place"/>
|
||||
<text variable="publisher"/>
|
||||
</group>
|
||||
</macro>
|
||||
|
||||
<macro name="edition">
|
||||
<group delimiter=" ">
|
||||
<text variable="edition"/>
|
||||
<label variable="edition" form="short"/>
|
||||
</group>
|
||||
</macro>
|
||||
|
||||
<macro name="book-pages">
|
||||
<choose>
|
||||
<if variable="number-of-pages">
|
||||
<group delimiter=" ">
|
||||
<text variable="number-of-pages"/>
|
||||
<text term="page" form="symbol"/>
|
||||
</group>
|
||||
</if>
|
||||
<else-if variable="page">
|
||||
<group delimiter=" ">
|
||||
<text variable="page"/>
|
||||
<text term="page" form="symbol"/>
|
||||
</group>
|
||||
</else-if>
|
||||
</choose>
|
||||
</macro>
|
||||
|
||||
<macro name="pages">
|
||||
<choose>
|
||||
<if variable="page">
|
||||
<group delimiter=" ">
|
||||
<text term="page" form="short"/>
|
||||
<text variable="page"/>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
</macro>
|
||||
|
||||
<macro name="volume-issue">
|
||||
<group delimiter=", ">
|
||||
<choose>
|
||||
<if variable="volume">
|
||||
<group delimiter=" ">
|
||||
<text value="Т."/>
|
||||
<text variable="volume"/>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
<choose>
|
||||
<if variable="issue">
|
||||
<group delimiter=" ">
|
||||
<text value="№"/>
|
||||
<text variable="issue"/>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
</group>
|
||||
</macro>
|
||||
|
||||
<macro name="url-access">
|
||||
<choose>
|
||||
<if variable="URL">
|
||||
<group delimiter=" ">
|
||||
<text variable="URL" prefix="URL: "/>
|
||||
<choose>
|
||||
<if variable="accessed">
|
||||
<group delimiter=": " prefix="(" suffix=")">
|
||||
<text term="accessed"/>
|
||||
<text macro="accessed-date"/>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
</macro>
|
||||
|
||||
<macro name="doi-or-url">
|
||||
<choose>
|
||||
<if variable="DOI">
|
||||
<text variable="DOI" prefix="DOI: "/>
|
||||
</if>
|
||||
<else>
|
||||
<text macro="url-access"/>
|
||||
</else>
|
||||
</choose>
|
||||
</macro>
|
||||
|
||||
<macro name="legal-details">
|
||||
<choose>
|
||||
<if variable="genre">
|
||||
<group delimiter=" : ">
|
||||
<text macro="title"/>
|
||||
<group delimiter=" ">
|
||||
<text variable="genre"/>
|
||||
<choose>
|
||||
<if variable="issued">
|
||||
<group delimiter=" ">
|
||||
<text value="від"/>
|
||||
<text macro="issued-date"/>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
<choose>
|
||||
<if variable="number">
|
||||
<group delimiter=" ">
|
||||
<text value="№"/>
|
||||
<text variable="number"/>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
</group>
|
||||
</group>
|
||||
</if>
|
||||
<else>
|
||||
<group delimiter=". ">
|
||||
<text macro="title"/>
|
||||
<group delimiter=" ">
|
||||
<choose>
|
||||
<if variable="issued">
|
||||
<group delimiter=" ">
|
||||
<text value="від"/>
|
||||
<text macro="issued-date"/>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
<choose>
|
||||
<if variable="number">
|
||||
<group delimiter=" ">
|
||||
<text value="№"/>
|
||||
<text variable="number"/>
|
||||
</group>
|
||||
</if>
|
||||
</choose>
|
||||
</group>
|
||||
</group>
|
||||
</else>
|
||||
</choose>
|
||||
</macro>
|
||||
|
||||
<citation collapse="citation-number">
|
||||
<sort>
|
||||
<key variable="citation-number"/>
|
||||
</sort>
|
||||
<layout delimiter=", " prefix="[" suffix="]">
|
||||
<text variable="citation-number"/>
|
||||
</layout>
|
||||
</citation>
|
||||
|
||||
<bibliography hanging-indent="false" entry-spacing="0" line-spacing="1">
|
||||
<sort>
|
||||
<key variable="citation-number"/>
|
||||
</sort>
|
||||
<layout>
|
||||
<group display="block">
|
||||
<text display="left-margin" variable="citation-number" suffix=". "/>
|
||||
|
||||
<choose>
|
||||
<!-- Нормативні акти: Цивільний кодекс України : Закон України від 16.01.2003 № 435-IV. URL: ... -->
|
||||
<if type="legislation bill legal_case treaty" match="any">
|
||||
<group delimiter=". " suffix=".">
|
||||
<text macro="legal-details"/>
|
||||
<text macro="url-access"/>
|
||||
</group>
|
||||
</if>
|
||||
|
||||
<!-- Наукова стаття -->
|
||||
<else-if type="article-journal">
|
||||
<group delimiter=". " suffix=".">
|
||||
<text macro="contributors"/>
|
||||
<text macro="title"/>
|
||||
<text macro="container"/>
|
||||
<text macro="issued-year"/>
|
||||
<text macro="volume-issue"/>
|
||||
<text macro="pages"/>
|
||||
<text macro="doi-or-url"/>
|
||||
</group>
|
||||
</else-if>
|
||||
|
||||
<!-- Газетна / журнальна стаття без DOI -->
|
||||
<else-if type="article-newspaper article-magazine" match="any">
|
||||
<group delimiter=". " suffix=".">
|
||||
<text macro="contributors"/>
|
||||
<text macro="title"/>
|
||||
<text variable="container-title" font-style="italic"/>
|
||||
<text macro="issued-date"/>
|
||||
<text macro="pages"/>
|
||||
<text macro="url-access"/>
|
||||
</group>
|
||||
</else-if>
|
||||
|
||||
<!-- Вебсторінки: без дублювання назви замість автора -->
|
||||
<else-if type="webpage post post-weblog" match="any">
|
||||
<group delimiter=". " suffix=".">
|
||||
<text macro="contributors"/>
|
||||
<text macro="title"/>
|
||||
<text macro="site-or-publisher"/>
|
||||
<text macro="issued-date"/>
|
||||
<text macro="url-access"/>
|
||||
</group>
|
||||
</else-if>
|
||||
|
||||
<!-- Книга -->
|
||||
<else-if type="book">
|
||||
<group delimiter=". " suffix=".">
|
||||
<text macro="contributors"/>
|
||||
<text macro="title"/>
|
||||
<text macro="edition"/>
|
||||
<group delimiter=", ">
|
||||
<text macro="publisher-place"/>
|
||||
<text macro="issued-year"/>
|
||||
</group>
|
||||
<text macro="book-pages"/>
|
||||
<text macro="doi-or-url"/>
|
||||
</group>
|
||||
</else-if>
|
||||
|
||||
<!-- Розділ у книзі -->
|
||||
<else-if type="chapter">
|
||||
<group delimiter=". " suffix=".">
|
||||
<text macro="contributors"/>
|
||||
<text macro="title"/>
|
||||
<group delimiter=" ">
|
||||
<text value="In:"/>
|
||||
<text variable="container-title" font-style="italic"/>
|
||||
</group>
|
||||
<names variable="editor">
|
||||
<name name-as-sort-order="all"
|
||||
sort-separator=" "
|
||||
initialize-with=". "
|
||||
delimiter=", "
|
||||
delimiter-precedes-last="never"/>
|
||||
<label form="short" prefix=" (" suffix=")"/>
|
||||
</names>
|
||||
<group delimiter=", ">
|
||||
<text macro="publisher-place"/>
|
||||
<text macro="issued-year"/>
|
||||
</group>
|
||||
<text macro="pages"/>
|
||||
<text macro="doi-or-url"/>
|
||||
</group>
|
||||
</else-if>
|
||||
|
||||
<!-- Матеріали конференції -->
|
||||
<else-if type="paper-conference">
|
||||
<group delimiter=". " suffix=".">
|
||||
<text macro="contributors"/>
|
||||
<text macro="title"/>
|
||||
<group delimiter=" ">
|
||||
<text value="In:"/>
|
||||
<text variable="container-title"/>
|
||||
</group>
|
||||
<group delimiter=", ">
|
||||
<text macro="publisher-place"/>
|
||||
<text macro="issued-year"/>
|
||||
</group>
|
||||
<text macro="pages"/>
|
||||
<text macro="doi-or-url"/>
|
||||
</group>
|
||||
</else-if>
|
||||
|
||||
<!-- Дисертації, звіти -->
|
||||
<else-if type="thesis report" match="any">
|
||||
<group delimiter=". " suffix=".">
|
||||
<text macro="contributors"/>
|
||||
<text macro="title"/>
|
||||
<text variable="genre"/>
|
||||
<group delimiter=", ">
|
||||
<text macro="publisher-place"/>
|
||||
<text macro="issued-year"/>
|
||||
</group>
|
||||
<text macro="book-pages"/>
|
||||
<text macro="doi-or-url"/>
|
||||
</group>
|
||||
</else-if>
|
||||
|
||||
<!-- Патенти -->
|
||||
<else-if type="patent">
|
||||
<group delimiter=". " suffix=".">
|
||||
<text macro="contributors"/>
|
||||
<text macro="title"/>
|
||||
<group delimiter=" ">
|
||||
<text value="№"/>
|
||||
<text variable="number"/>
|
||||
</group>
|
||||
<text macro="issued-date"/>
|
||||
<text macro="url-access"/>
|
||||
</group>
|
||||
</else-if>
|
||||
|
||||
<!-- Універсальний fallback -->
|
||||
<else>
|
||||
<group delimiter=". " suffix=".">
|
||||
<text macro="contributors"/>
|
||||
<text macro="title"/>
|
||||
<text variable="container-title" font-style="italic"/>
|
||||
<group delimiter=", ">
|
||||
<text macro="publisher-place"/>
|
||||
<text macro="issued-year"/>
|
||||
</group>
|
||||
<text macro="pages"/>
|
||||
<text macro="doi-or-url"/>
|
||||
</group>
|
||||
</else>
|
||||
</choose>
|
||||
</group>
|
||||
</layout>
|
||||
</bibliography>
|
||||
</style>
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
"грудня",
|
||||
).at(month - 1)
|
||||
|
||||
#let is-cyr(c) = regex("[\p{Cyrillic}]") in c
|
||||
#let is-cyr(c) = regex("^\p{Cyrillic}") in c
|
||||
|
||||
/// type-safe emptiness check
|
||||
#let is-empty(val) = {
|
||||
|
||||
+93
-5
@@ -5,6 +5,9 @@
|
||||
#import "./style.typ"
|
||||
#import "./utils.typ"
|
||||
|
||||
#let dstu-table = style.dstu-table
|
||||
#let hfill = utils.hfill
|
||||
|
||||
/// Coursework template for NURE
|
||||
/// - university (str): University code, default "ХНУРЕ"
|
||||
/// - subject (str): Subject short name
|
||||
@@ -29,7 +32,8 @@
|
||||
bib-path: none,
|
||||
appendices: (),
|
||||
) = {
|
||||
set document(title: title, author: authors.map(c => c.name))
|
||||
let doc-title = if type(title) == array { title.join(" ") } else { title }
|
||||
set document(title: doc-title, author: authors.map(c => c.name))
|
||||
|
||||
show: style.dstu.with(skip: 1)
|
||||
|
||||
@@ -56,6 +60,86 @@
|
||||
style.appendices(appendices)
|
||||
}
|
||||
|
||||
/// Alternative coursework template for NURE.
|
||||
/// - university (str): University code, default "ХНУРЕ"
|
||||
/// - title (str): Work title
|
||||
/// - authors (array): List of author dictionaries
|
||||
/// - mentors (array): List of mentor dictionaries
|
||||
/// - committee-members (array): Optional list of commission member dictionaries for the title page
|
||||
/// - task-list (dict): Task metadata
|
||||
/// - calendar-plan (dict): Calendar plan table
|
||||
/// - abstract (dict): Keywords and abstract text
|
||||
/// - abstract-en (dict): Optional English keywords and abstract text
|
||||
/// - bib-path (str): Path to bibliography file
|
||||
/// - appendices (content): Appendix content
|
||||
#let coursework-v2(
|
||||
doc,
|
||||
university: "ХНУРЕ",
|
||||
title: none,
|
||||
authors: (),
|
||||
mentors: (),
|
||||
committee-members: none,
|
||||
task-list: (),
|
||||
calendar-plan: (),
|
||||
abstract: (),
|
||||
abstract-en: none,
|
||||
bib-path: none,
|
||||
appendices: (),
|
||||
faculty: "комп’ютерних наук",
|
||||
education-level: "перший (бакалаврський)",
|
||||
program-type: "освітньо-професійна",
|
||||
program-name: none,
|
||||
) = {
|
||||
assert(authors.len() > 0, message: "At least one author required")
|
||||
assert(mentors.len() > 0, message: "At least one mentor required")
|
||||
|
||||
let doc-title = if type(title) == array { title.join(" ") } else { title }
|
||||
set document(title: doc-title, author: authors.map(c => c.name))
|
||||
|
||||
show: style.dstu.with(skip: 1)
|
||||
|
||||
let bib-count = state("citation-counter", ())
|
||||
show cite: it => {
|
||||
it
|
||||
bib-count.update(((..c)) => (..c, it.key))
|
||||
}
|
||||
|
||||
let abstract = if abstract-en != none {
|
||||
abstract + (en: abstract-en)
|
||||
} else {
|
||||
abstract
|
||||
}
|
||||
let committee_members = committee-members
|
||||
|
||||
tp.cw-v2.nure(
|
||||
university,
|
||||
title,
|
||||
authors,
|
||||
mentors,
|
||||
committee_members,
|
||||
task-list,
|
||||
calendar-plan,
|
||||
abstract,
|
||||
bib-count,
|
||||
faculty: faculty,
|
||||
education-level: education-level,
|
||||
program-type: program-type,
|
||||
program-name: program-name,
|
||||
)
|
||||
|
||||
doc
|
||||
|
||||
|
||||
|
||||
{
|
||||
show regex("^\\d+\\."): it => [#it#h(0.5cm)]
|
||||
show block: it => [#it.body#parbreak()]
|
||||
bibliography(bib-path, title: [Перелік джерел посилання], style: "csl/dstu-8302-2015.csl", full: true)
|
||||
}
|
||||
|
||||
style.appendices(appendices)
|
||||
}
|
||||
|
||||
/// Practice and Laboratory works template
|
||||
/// - layout (str): "default", "minimal", or "complex"
|
||||
/// - university (str): University code
|
||||
@@ -77,6 +161,7 @@
|
||||
title: none,
|
||||
authors: (),
|
||||
mentors: (),
|
||||
skip-heading: false,
|
||||
) = {
|
||||
assert(authors.len() > 0, message: "At least one author required")
|
||||
|
||||
@@ -89,13 +174,14 @@
|
||||
|
||||
// Select layout variant
|
||||
let layouts = (
|
||||
"complex": tp.pz-lb.complex(uni, edu-program, subject, type, number, title, authors, mentors),
|
||||
"ХНУРЕ": tp.pz-lb.nure(uni, edu-program, subject, type, number, title, authors, mentors),
|
||||
"default": tp.pz-lb.nure(uni, edu-program, subject, type, number, title, authors, mentors),
|
||||
"complex": () => tp.pz-lb.complex(uni, edu-program, subject, type, number, title, authors, mentors),
|
||||
"ХНУРЕ": () => tp.pz-lb.nure(uni, edu-program, subject, type, number, title, authors, mentors),
|
||||
"default": () => tp.pz-lb.nure(uni, edu-program, subject, type, number, title, authors, mentors),
|
||||
)
|
||||
|
||||
layouts.at(university, default: layouts.default)
|
||||
(layouts.at(university, default: layouts.default))()
|
||||
|
||||
if not skip-heading {
|
||||
pagebreak(weak: true)
|
||||
|
||||
// Set heading counter based on title/number
|
||||
@@ -107,8 +193,10 @@
|
||||
if number != none {
|
||||
context counter(heading).update(number - 1)
|
||||
}
|
||||
|
||||
heading(eval(title, mode: "markup"))
|
||||
}
|
||||
}
|
||||
|
||||
doc
|
||||
}
|
||||
|
||||
+208
-9
@@ -6,7 +6,11 @@
|
||||
#let double-half-spacing = spacing * 2.5
|
||||
|
||||
/// Ukrainian alphabet for DSTU 3008:2015 numbering
|
||||
#let ukr-enum = "абвгдежиклмнпрстуфхцшщюя".split("")
|
||||
#let ukr-enum = "абвгдежиклмнпрстуфхцшщюя".clusters()
|
||||
|
||||
#let dstu-table-counter = counter("dstu-table")
|
||||
#let dstu-table-appendix = state("dstu-table-appendix", none)
|
||||
#let dstu-table-caption-gap = 0.65em
|
||||
|
||||
/// Helper for level 2/3 heading blocks
|
||||
#let heading-block(it, num: auto) = {
|
||||
@@ -19,6 +23,125 @@
|
||||
v(double-spacing, weak: true)
|
||||
}
|
||||
|
||||
#let _col-count(columns) = {
|
||||
if type(columns) == int {
|
||||
columns
|
||||
} else if type(columns) == array {
|
||||
columns.len()
|
||||
} else {
|
||||
panic("dstu-table: columns must be an int or array, e.g. 2 or (1fr, 3fr)")
|
||||
}
|
||||
}
|
||||
|
||||
#let _required(name, value) = {
|
||||
if value == none {
|
||||
panic("dstu-table: " + name + " is required")
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
#let dstu-table-label(it) = {
|
||||
set par(first-line-indent: 0pt)
|
||||
align(left)[#it]
|
||||
}
|
||||
|
||||
#let dstu-table(
|
||||
caption: none,
|
||||
columns: none,
|
||||
header: none,
|
||||
tag: none,
|
||||
..args,
|
||||
) = {
|
||||
let caption = _required("caption", caption)
|
||||
let columns = _required("columns", columns)
|
||||
let header = _required("header", header)
|
||||
|
||||
if type(header) != array {
|
||||
panic("dstu-table: header must be an array, e.g. ([A], [B])")
|
||||
}
|
||||
|
||||
dstu-table-counter.step()
|
||||
|
||||
let named = args.named()
|
||||
let body = args.pos()
|
||||
context {
|
||||
let h = counter(heading).get()
|
||||
let section = if h.len() > 0 { h.at(0) } else { 0 }
|
||||
let n = dstu-table-counter.get().first()
|
||||
let appendix = dstu-table-appendix.get()
|
||||
let num = if appendix == none {
|
||||
numbering("1.1", section, n)
|
||||
} else {
|
||||
upper(ukr-enum.at(appendix - 1)) + "." + str(n)
|
||||
}
|
||||
|
||||
let id = "dstu-table-" + str(section) + "-" + str(n)
|
||||
let start-marker = "start-" + id
|
||||
let end-marker = "end-" + id
|
||||
let cols = _col-count(columns)
|
||||
|
||||
v(double-spacing, weak: true)
|
||||
|
||||
{
|
||||
set block(spacing: dstu-table-caption-gap)
|
||||
|
||||
[#metadata((kind: "dstu-table", number: num)) #if tag != none { label(tag) }]
|
||||
|
||||
block(sticky: true)[
|
||||
#dstu-table-label[Таблиця #num -- #caption]
|
||||
]
|
||||
|
||||
table(
|
||||
columns: columns,
|
||||
..named,
|
||||
|
||||
table.header(
|
||||
repeat: true,
|
||||
|
||||
table.cell(
|
||||
colspan: cols,
|
||||
stroke: none,
|
||||
inset: 0pt,
|
||||
)[
|
||||
#metadata(start-marker)
|
||||
|
||||
#context {
|
||||
let starts = query(metadata.where(value: start-marker))
|
||||
let ends = query(metadata.where(value: end-marker))
|
||||
|
||||
if starts.len() > 0 and ends.len() > 0 {
|
||||
let start-page = starts.first().location().page()
|
||||
let end-page = ends.first().location().page()
|
||||
let current-page = here().page()
|
||||
|
||||
if current-page != start-page {
|
||||
let label = if current-page == end-page {
|
||||
[Кінець таблиці #num]
|
||||
} else {
|
||||
[Продовження таблиці #num]
|
||||
}
|
||||
|
||||
pad(top: dstu-table-caption-gap, bottom: dstu-table-caption-gap)[
|
||||
#dstu-table-label[#label]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
..header,
|
||||
),
|
||||
|
||||
..body,
|
||||
)
|
||||
}
|
||||
|
||||
metadata(end-marker)
|
||||
|
||||
v(double-spacing, weak: true)
|
||||
}
|
||||
}
|
||||
|
||||
/// DSTU 3008:2015 Style
|
||||
#let dstu(
|
||||
it,
|
||||
@@ -35,12 +158,15 @@
|
||||
|
||||
// Text and paragraph
|
||||
set text(lang: "uk", size: 14pt, hyphenate: false, font: ("Times New Roman", "Liberation Serif"))
|
||||
set par(justify: true, spacing: spacing, leading: spacing, first-line-indent: (amount: indent-size, all: true))
|
||||
set par(justify: true, spacing: spacing, leading: spacing, first-line-indent: (
|
||||
amount: indent-size,
|
||||
all: true,
|
||||
))
|
||||
set block(spacing: spacing)
|
||||
set underline(evade: false)
|
||||
|
||||
// Lists
|
||||
set enum(indent: indent-size, body-indent: 0.5cm, numbering: i => ukr-enum.at(i) + ")")
|
||||
set enum(indent: indent-size, body-indent: 0.5cm, numbering: i => ukr-enum.at(i - 1) + ")")
|
||||
show enum: it => {
|
||||
set enum(indent: 0em, numbering: "1)")
|
||||
it
|
||||
@@ -48,6 +174,63 @@
|
||||
set list(indent: indent-size + 0.1cm, body-indent: 0.5cm, marker: [--])
|
||||
|
||||
// Figures
|
||||
show ref: it => {
|
||||
let target = it.element
|
||||
|
||||
if target == none {
|
||||
return it
|
||||
}
|
||||
|
||||
// dstu-table refs carry a number already resolved at the table location.
|
||||
if (
|
||||
target.func() == metadata
|
||||
and type(target.value) == dictionary
|
||||
and target.value.at("kind", default: none) == "dstu-table"
|
||||
) {
|
||||
return link(target.location())[#target.value.at("number")]
|
||||
}
|
||||
|
||||
if target.func() != figure and target.func() != math.equation {
|
||||
return it
|
||||
}
|
||||
|
||||
// A figure or equation number must be read at the element itself, not at the citation site.
|
||||
// The default ref display re-runs the numbering function here, where its inner `counter(heading)`
|
||||
// context resolves to the citing chapter and prints the wrong section or appendix letter.
|
||||
// We sample every counter at the target location instead.
|
||||
let loc = target.location()
|
||||
|
||||
let appendix = dstu-table-appendix.at(loc)
|
||||
|
||||
let section = if appendix == none {
|
||||
str(counter(heading).at(loc).first())
|
||||
} else {
|
||||
upper(ukr-enum.at(appendix - 1))
|
||||
}
|
||||
|
||||
let number = if target.func() == figure {
|
||||
let idx = counter(figure.where(kind: target.kind)).at(loc).first()
|
||||
section + "." + str(idx)
|
||||
} else {
|
||||
let idx = counter(math.equation).at(loc).first()
|
||||
"(" + section + "." + str(idx) + ")"
|
||||
}
|
||||
|
||||
let supplement = if it.supplement == auto {
|
||||
target.supplement
|
||||
} else {
|
||||
it.supplement
|
||||
}
|
||||
|
||||
let body = if supplement != auto and supplement != none {
|
||||
[#supplement #number]
|
||||
} else {
|
||||
[#number]
|
||||
}
|
||||
|
||||
link(loc, body)
|
||||
}
|
||||
|
||||
show figure: it => {
|
||||
v(double-spacing, weak: true)
|
||||
it
|
||||
@@ -65,6 +248,7 @@
|
||||
counter(figure.where(kind: raw)).update(0)
|
||||
counter(figure.where(kind: image)).update(0)
|
||||
counter(figure.where(kind: table)).update(0)
|
||||
dstu-table-counter.update(0)
|
||||
it
|
||||
}
|
||||
set figure(numbering: i => context numbering("1.1", counter(heading).get().at(0), i))
|
||||
@@ -97,6 +281,15 @@
|
||||
v(double-half-spacing, weak: true)
|
||||
}
|
||||
|
||||
// blocks `like this` aren't welcome, so ` is replaced with "
|
||||
show raw.where(block: false): it => text(
|
||||
lang: "uk",
|
||||
size: 14pt,
|
||||
hyphenate: false,
|
||||
weight: "regular",
|
||||
font: ("Times New Roman", "Liberation Serif"),
|
||||
)["#it.text"]
|
||||
|
||||
it
|
||||
}
|
||||
|
||||
@@ -104,16 +297,23 @@
|
||||
#let appendices(it) = {
|
||||
counter(heading).update(0)
|
||||
|
||||
context {
|
||||
let app-letter = upper(ukr-enum.at(counter(heading).get().at(0)))
|
||||
set heading(numbering: (i, ..n) => upper(ukr-enum.at(i)) + numbering(".1.1", ..n))
|
||||
set figure(numbering: i => app-letter + "." + str(i))
|
||||
set math.equation(numbering: i => app-letter + "." + str(i))
|
||||
set heading(numbering: (i, ..n) => upper(ukr-enum.at(i - 1)) + numbering(".1.1", ..n))
|
||||
set heading(supplement: [Додаток])
|
||||
|
||||
let app-letter = context upper(ukr-enum.at(counter(heading).get().at(0) - 1))
|
||||
set figure(numbering: i => app-letter + "." + str(i))
|
||||
set math.equation(numbering: i => [(#app-letter.#str(i))])
|
||||
|
||||
show heading: h => {
|
||||
set text(size: 14pt)
|
||||
if h.level == 1 {
|
||||
counter(math.equation).update(0)
|
||||
counter(figure.where(kind: raw)).update(0)
|
||||
counter(figure.where(kind: image)).update(0)
|
||||
counter(figure.where(kind: table)).update(0)
|
||||
dstu-table-counter.update(0)
|
||||
dstu-table-appendix.update(counter(heading).get().at(0))
|
||||
|
||||
set align(center)
|
||||
set text(weight: "regular")
|
||||
pagebreak(weak: true)
|
||||
@@ -129,4 +329,3 @@
|
||||
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
#import "nure.typ": *
|
||||
@@ -0,0 +1,394 @@
|
||||
#import "../../shared.typ": universities
|
||||
#import "../../helpers.typ": *
|
||||
#import "../../style.typ": spacing
|
||||
#import "../../utils.typ": bold, uline, filled-lines, hfill
|
||||
|
||||
#let note(content) = block(width: 100%, above: 5pt, below: 0pt)[
|
||||
#set text(size: 10pt)
|
||||
#set par(first-line-indent: 0pt, spacing: 0pt)
|
||||
#align(center)[#content]
|
||||
]
|
||||
|
||||
#let form-field(alignment: center, content) = box(
|
||||
width: 100%,
|
||||
stroke: (bottom: 0.5pt),
|
||||
inset: (bottom: 1.5pt),
|
||||
)[
|
||||
#align(alignment)[#content]
|
||||
]
|
||||
|
||||
#let label-line(label, value, caption: none, label-width: auto) = {
|
||||
set par(first-line-indent: 0pt)
|
||||
if label-width == auto {
|
||||
[#label #form-field(alignment: center, value)]
|
||||
} else {
|
||||
grid(
|
||||
columns: (label-width, 1fr),
|
||||
gutter: 0pt,
|
||||
align: horizon,
|
||||
label, form-field(alignment: center, value),
|
||||
)
|
||||
}
|
||||
if caption != none {
|
||||
note(caption)
|
||||
}
|
||||
}
|
||||
|
||||
#let inline-field(value) = form-field(alignment: center, value)
|
||||
|
||||
#let inline-label-line(label, value) = {
|
||||
set par(first-line-indent: 0pt)
|
||||
block(width: 100%, below: 0pt)[
|
||||
#label #uline(align: center, value)
|
||||
]
|
||||
}
|
||||
|
||||
#let task-head-fields(fields) = {
|
||||
set par(first-line-indent: 0pt)
|
||||
let cells = ()
|
||||
for (label, value) in fields {
|
||||
if type(value) == array {
|
||||
for (i, line) in value.enumerate() {
|
||||
cells.push(if i == 0 { label } else { [] })
|
||||
cells.push(inline-field(line))
|
||||
}
|
||||
} else {
|
||||
cells.push(label)
|
||||
cells.push(inline-field(value))
|
||||
}
|
||||
}
|
||||
grid(
|
||||
columns: (auto, 1fr),
|
||||
gutter: 0pt,
|
||||
row-gutter: 0.65em,
|
||||
align: top,
|
||||
..cells,
|
||||
)
|
||||
}
|
||||
|
||||
#let task-num(n) = box(str(n) + ".")
|
||||
|
||||
#let title-field(value) = {
|
||||
uline(align: center, value.join())
|
||||
uline(align: center, [])
|
||||
note[(тема)]
|
||||
}
|
||||
|
||||
#let commission-lines(members) = {
|
||||
if members.len() > 0 {
|
||||
for (i, member) in members.enumerate() {
|
||||
if i > 0 {
|
||||
linebreak()
|
||||
}
|
||||
let member-display-name = member.at("display-name", default: member.name)
|
||||
let member-degree = member.at("degree", default: "")
|
||||
uline(align: left, [#member-degree #member-display-name])
|
||||
}
|
||||
} else {
|
||||
v(0.55em)
|
||||
line(length: 100%, stroke: 0.5pt)
|
||||
v(0.55em)
|
||||
line(length: 100%, stroke: 0.5pt)
|
||||
v(0.55em)
|
||||
line(length: 100%, stroke: 0.5pt)
|
||||
}
|
||||
}
|
||||
|
||||
#let nure(
|
||||
university,
|
||||
title,
|
||||
authors,
|
||||
mentors,
|
||||
committee_members,
|
||||
task-list,
|
||||
calendar-plan,
|
||||
abstract,
|
||||
bib-count,
|
||||
faculty: "комп'ютерних наук",
|
||||
education-level: "перший (бакалаврський)",
|
||||
program-type: "освітньо-професійна",
|
||||
program-name: none,
|
||||
) = {
|
||||
let author = authors.first()
|
||||
let head-mentor = mentors.first()
|
||||
let commission-members = if committee_members == none {
|
||||
mentors.slice(1)
|
||||
} else {
|
||||
committee_members
|
||||
}
|
||||
|
||||
let uni = universities.at(university)
|
||||
let edu-prog = uni.edu-programs.at(author.edu-program)
|
||||
let program-name = if program-name == none {
|
||||
edu-prog.at("program-name", default: edu-prog.name-long)
|
||||
} else {
|
||||
program-name
|
||||
}
|
||||
let group-name = if str(author.group).starts-with(author.edu-program) {
|
||||
str(author.group)
|
||||
} else {
|
||||
author.edu-program + "-" + str(author.group)
|
||||
}
|
||||
let executor-label = if author.gender == "f" or author.gender == "female" or author.gender == "ж" {
|
||||
"Виконала:"
|
||||
} else {
|
||||
"Виконав:"
|
||||
}
|
||||
let author-display-name = author.at("display-name", default: author.name)
|
||||
let author-full-name-dat = author.at("full-name-dat", default: author.full-name-gen)
|
||||
let mentor-display-name = head-mentor.at("display-name", default: head-mentor.name)
|
||||
let mentor-degree = head-mentor.at("degree", default: "")
|
||||
|
||||
[
|
||||
#set par(first-line-indent: 0pt, justify: false, leading: 0.45em)
|
||||
|
||||
#set text(size: 14pt)
|
||||
#set align(center)
|
||||
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ\
|
||||
#uni.name
|
||||
|
||||
#v(0.7em)
|
||||
|
||||
#set align(left)
|
||||
#inline-label-line(
|
||||
[Факультет],
|
||||
faculty,
|
||||
)
|
||||
#note([(повна назва)])
|
||||
|
||||
#v(0.8em)
|
||||
#inline-label-line(
|
||||
[Кафедра],
|
||||
lower(edu-prog.department-gen),
|
||||
)
|
||||
#note([(повна назва)])
|
||||
|
||||
#v(1.8em)
|
||||
|
||||
#set align(center)
|
||||
#text(size: 20pt, weight: "bold")[КОМПЛЕКСНИЙ КУРСОВИЙ ПРОЄКТ]\
|
||||
#text(size: 20pt, weight: "bold")[Пояснювальна записка]
|
||||
|
||||
#v(0.9em)
|
||||
|
||||
#set align(left)
|
||||
#label-line([рівень вищої освіти], education-level, label-width: 120pt)
|
||||
|
||||
#v(1.0em)
|
||||
#title-field(title)
|
||||
|
||||
#v(3em)
|
||||
|
||||
#grid(
|
||||
columns: (0.36fr, 0.64fr),
|
||||
[],
|
||||
[
|
||||
#executor-label\
|
||||
здобувач #underline([#author.course]) курсу, групи #underline(group-name)\
|
||||
#uline(align: center, author-display-name)
|
||||
#note[(Власне ім'я, ПРІЗВИЩЕ)]
|
||||
|
||||
#v(0.2em)
|
||||
#inline-label-line([Спеціальність], [#edu-prog.code -- #edu-prog.name-long])
|
||||
#note[(код і повна назва спеціальності)]
|
||||
#inline-label-line([Тип програми], program-type)
|
||||
#inline-label-line([Освітня програма], program-name)
|
||||
#note[(повна назва освітньої програми)]
|
||||
|
||||
#v(0.3em)
|
||||
#inline-label-line([Керівник], [#mentor-degree #mentor-display-name])
|
||||
#note[(посада, Власне ім'я, ПРІЗВИЩЕ)]
|
||||
|
||||
#v(0.3em)
|
||||
#pad(left: 75pt)[
|
||||
#set par(first-line-indent: 0pt)
|
||||
Члени комісії (#text(size: 10pt)[Власне ім'я, ПРІЗВИЩЕ, підпис])
|
||||
#v(0.15em)
|
||||
#commission-lines(commission-members)
|
||||
]
|
||||
],
|
||||
)
|
||||
|
||||
#v(1fr)
|
||||
|
||||
#set align(center)
|
||||
#task-list.done-date.display("[year]") р.
|
||||
|
||||
#pagebreak()
|
||||
]
|
||||
|
||||
[
|
||||
#set par(first-line-indent: 0pt, justify: false)
|
||||
#align(center)[#uni.name]
|
||||
|
||||
#v(1.1em)
|
||||
#task-head-fields((
|
||||
(
|
||||
[Факультет],
|
||||
(
|
||||
faculty,
|
||||
),
|
||||
),
|
||||
([Кафедра], lower(edu-prog.department-gen)),
|
||||
([Рівень вищої освіти], education-level),
|
||||
([Спеціальність], [#edu-prog.code -- #edu-prog.name-long]),
|
||||
([Тип програми], program-type),
|
||||
([Освітня програма], program-name),
|
||||
))
|
||||
#note[(шифр і назва)]
|
||||
|
||||
#v(1.7em)
|
||||
#grid(
|
||||
columns: (1.1fr, 1.1fr, 1.1fr, 1.7fr, 1.3fr, 1.1fr),
|
||||
gutter: 0pt,
|
||||
align: center + horizon,
|
||||
[Курс], uline(author.course), [Група], uline(group-name), [Семестр], uline(author.semester),
|
||||
)
|
||||
|
||||
#v(2.6em)
|
||||
|
||||
#align(center)[
|
||||
#bold[ЗАВДАННЯ]\
|
||||
#text(style: "italic", weight: "bold")[на курсовий проєкт (роботу) студента]
|
||||
]
|
||||
|
||||
#v(1.0em)
|
||||
|
||||
#label-line([здобувачеві], author-full-name-dat, caption: [(прізвище, ім'я, по батькові)], label-width: 95pt)
|
||||
|
||||
#v(1.0em)
|
||||
|
||||
#task-num(1) Тема роботи #uline(align: left, filled-lines(title))
|
||||
|
||||
#v(0.4em)
|
||||
#task-num(2) Термін здачі студентом закінченої роботи
|
||||
“#underline(task-list.done-date.display("[day]"))” #underline(month-gen(task-list.done-date.month())) #task-list.done-date.display("[year]")р.
|
||||
|
||||
#v(0.4em)
|
||||
#task-num(3) Вихідні дані до проєкту #uline(align: left, filled-lines(task-list.at("source", default: [])))
|
||||
#v(0.4em)
|
||||
#uline(align: left, [])
|
||||
|
||||
#v(0.4em)
|
||||
#task-num(4) Перелік питань, що потрібно опрацювати в роботі\
|
||||
#uline(align: left, filled-lines(task-list.at("content", default: [])))
|
||||
#v(0.4em)
|
||||
#uline(align: left, [])
|
||||
|
||||
#pagebreak()
|
||||
]
|
||||
|
||||
[
|
||||
#align(center, bold[КАЛЕНДАРНИЙ ПЛАН])
|
||||
#set par(first-line-indent: 0pt)
|
||||
|
||||
#v(1.4em)
|
||||
|
||||
#calendar-plan.plan-table
|
||||
|
||||
#v(5.0em)
|
||||
|
||||
Дата видачі завдання “#underline(task-list.initial-date.display("[day]"))” #underline(month-gen(task-list.initial-date.month())) #task-list.initial-date.display("[year]") р.
|
||||
|
||||
#v(1.4em)
|
||||
|
||||
Здобувач #underline([#hfill(6cm)])
|
||||
#note[(підпис) #h(8cm)]
|
||||
|
||||
#v(1.4em)
|
||||
|
||||
Керівник роботи #uline(align: center, []) #h(1cm) #underline[#mentor-degree #mentor-display-name]
|
||||
#note[(підпис) #h(4.5cm) (посада, Власне ім'я, ПРІЗВИЩЕ)]
|
||||
|
||||
#pagebreak()
|
||||
]
|
||||
|
||||
[
|
||||
#let header = if abstract.at("en", default: none) != none {
|
||||
bold[РЕФЕРАТ / ABSTRACT]
|
||||
} else {
|
||||
bold[РЕФЕРАТ]
|
||||
}
|
||||
#align(center, header) \
|
||||
|
||||
#context [
|
||||
#let pages = counter(page).final().at(0)
|
||||
#let images = query(figure.where(kind: image)).len()
|
||||
#let dstu-tables = query(metadata).filter(it => type(it.value) == dictionary and it.value.at("kind", default: none) == "dstu-table").len()
|
||||
#let tables = query(figure.where(kind: table)).len() + dstu-tables
|
||||
#let bibs = bib-count.final().dedup().len()
|
||||
|
||||
#let counters = ()
|
||||
#if pages != 0 { counters.push[#pages с.] }
|
||||
#if tables != 0 { counters.push[#tables табл.] }
|
||||
#if images != 0 { counters.push[#images рис.] }
|
||||
#if bibs != 0 { counters.push[#bibs джерел] }
|
||||
|
||||
Пояснювальна записка містить: #counters.join(", ").
|
||||
]
|
||||
|
||||
\
|
||||
|
||||
#let keyword-pairs = if abstract.keywords.len() > 0 and type(abstract.keywords.first()) == array {
|
||||
abstract.keywords.map(pair => (
|
||||
uk: pair.at(0),
|
||||
en: pair.at(1),
|
||||
))
|
||||
} else if abstract.at("en", default: none) != none and abstract.en.at("keywords", default: none) != none {
|
||||
abstract.keywords.enumerate().map(((i, uk)) => (
|
||||
uk: uk,
|
||||
en: abstract.en.keywords.at(i),
|
||||
))
|
||||
} else {
|
||||
abstract.keywords.map(uk => (uk: uk))
|
||||
}
|
||||
#let sorted-keyword-pairs = keyword-pairs.sorted(by: (a, b) => {
|
||||
if is-cyr(a.uk) != is-cyr(b.uk) { is-cyr(a.uk) } else { a.uk < b.uk }
|
||||
})
|
||||
|
||||
#(sorted-keyword-pairs.map(pair => upper(pair.uk)).join(", "))
|
||||
|
||||
\
|
||||
|
||||
#abstract.text
|
||||
|
||||
#if abstract.at("en", default: none) != none or (keyword-pairs.len() > 0 and keyword-pairs.first().at("en", default: none) != none) [
|
||||
\
|
||||
|
||||
#(sorted-keyword-pairs.map(pair => upper(pair.en)).join(", "))
|
||||
|
||||
\
|
||||
|
||||
#abstract.en.text
|
||||
]
|
||||
]
|
||||
|
||||
{
|
||||
show outline.entry: it => {
|
||||
let el = it.element
|
||||
|
||||
if el.func() == heading and el.supplement == [Додаток] {
|
||||
if el.level > 1 {
|
||||
none
|
||||
} else {
|
||||
block(width: 100%)[
|
||||
#link(el.location())[
|
||||
Додаток #it.prefix()#h(0.5em)#it.inner()
|
||||
]
|
||||
]
|
||||
}
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
outline(
|
||||
title: [
|
||||
ЗМІСТ
|
||||
#v(spacing * 2, weak: true)
|
||||
],
|
||||
depth: 2,
|
||||
indent: auto,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -210,7 +210,8 @@
|
||||
#context [
|
||||
#let pages = counter(page).final().at(0)
|
||||
#let images = query(figure.where(kind: image)).len()
|
||||
#let tables = query(figure.where(kind: table)).len()
|
||||
#let dstu-tables = query(metadata).filter(it => type(it.value) == dictionary and it.value.at("kind", default: none) == "dstu-table").len()
|
||||
#let tables = query(figure.where(kind: table)).len() + dstu-tables
|
||||
#let bibs = bib-count.final().dedup().len()
|
||||
/* TODO: why this stopped working?
|
||||
#let tables = counter(figure.where(kind: table)).final().at(0)
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
#import "pz-lb/main.typ" as pz-lb
|
||||
#import "coursework/main.typ" as cw
|
||||
#import "coursework-v2/main.typ" as cw-v2
|
||||
|
||||
@@ -20,12 +20,15 @@
|
||||
#if authors.len() == 1 {
|
||||
let a = authors.first()
|
||||
[#gender-form("author", dict: a):\ ]
|
||||
[ст. гр. #a.edu-program\-#a.group\ ]
|
||||
if ("edu-program" in a) and ("group" in a) [ст. гр. #a.edu-program\-#a.group\ ]
|
||||
[#a.name\ ]
|
||||
if not is-empty(a.variant) [Варіант: №#a.variant]
|
||||
if ("variant" in a) and (not is-empty(a.variant)) [Варіант: №#a.variant]
|
||||
} else if authors.len() > 1 [
|
||||
#gender-form("author"):\
|
||||
#for a in authors [ст. гр. #a.edu-program\-#a.group\ #a.name\ ]
|
||||
#for a in authors [
|
||||
#if ("edu-program" in a) and ("group" in a) [ ст. гр. #a.edu-program\-#a.group\ ]
|
||||
#a.name\
|
||||
]
|
||||
]
|
||||
|
||||
#colbreak()
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
/// fill horizontal space with a filled box
|
||||
#let hfill(width) = box(width: width, repeat(" ")) // HAIR SPACE (U+200A)
|
||||
|
||||
/// convert an array of lines into filled lines for underlined task fields
|
||||
#let filled-lines(content) = if type(content) == array {
|
||||
content.map(line => [#line #hfill(1fr)]).join(linebreak())
|
||||
} else {
|
||||
content
|
||||
}
|
||||
|
||||
/// underlined cell with centered content by default
|
||||
#let uline(align: center, content) = underline[
|
||||
#if align != left { hfill(1fr) }
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
#import "@local/nure:0.1.1": *
|
||||
#import style: spacing
|
||||
|
||||
#let authors = (
|
||||
(
|
||||
name: "Сокорчук І. П.",
|
||||
display-name: "Ігор СОКОРЧУК",
|
||||
full-name-gen: "Сокорчука Ігоря Петровича",
|
||||
full-name-dat: "Сокорчуку Ігорю Петровичу",
|
||||
edu-program: "ПЗПІ",
|
||||
group: "23-3",
|
||||
gender: "m",
|
||||
course: 3,
|
||||
semester: 6,
|
||||
variant: 13,
|
||||
),
|
||||
)
|
||||
|
||||
#let mentors = (
|
||||
(name: "Сокорчук І. П.", display-name: "Ігор СОКОРЧУК", degree: "ст.викл. кафедри ПІ"),
|
||||
)
|
||||
|
||||
#let committee_members = (
|
||||
(name: "Груздо І.В.", degree: "Доц."),
|
||||
(name: "Зибіна К.В.", degree: "Ст. викл."),
|
||||
(name: "Гребенюк В.О.", degree: "Ст. викл."),
|
||||
)
|
||||
|
||||
#let task-list = (
|
||||
done-date: datetime(year: 2026, month: 12, day: 27),
|
||||
initial-date: datetime(year: 2026, month: 9, day: 15),
|
||||
source: (
|
||||
[Узагальнити модель предметної області,],
|
||||
[визначити основні сутності та зв'язки між ними.],
|
||||
[Підготувати демонстраційний набір технологій для шаблону.],
|
||||
),
|
||||
content: (
|
||||
[Опис предметної галузі, формування вимог,],
|
||||
[архітектурне проєктування, тестування та],
|
||||
[перевірка верстки пояснювальної записки.],
|
||||
),
|
||||
)
|
||||
|
||||
#let calendar-plan = (
|
||||
plan-table: table(
|
||||
columns: (0.7fr, 5.8fr, 2.5fr, 1.6fr),
|
||||
align: (center, left, center, center),
|
||||
[№], [Назва етапів роботи], [Термін виконання етапів роботи], [Примітка],
|
||||
[1], [Аналіз предметної галузі], [], [виконано],
|
||||
[2], [Розробка постановки задачі], [], [виконано],
|
||||
[3], [Проєктування ПЗ], [], [виконано],
|
||||
[4], [Програмна реалізація], [], [виконано],
|
||||
[5], [Аналіз результатів], [], [виконано],
|
||||
[6], [Підготовка пояснювальної записки.], [], [виконано],
|
||||
[7], [Перевірка на наявність ознак академічного плагіату], [], [виконано],
|
||||
[8], [Захист роботи], [], [виконано],
|
||||
),
|
||||
)
|
||||
|
||||
#let abstract = (
|
||||
keywords: (
|
||||
("веб-застосунок", "WEB APPLICATION"),
|
||||
("інформаційна система", "INFORMATION SYSTEM"),
|
||||
("курсова робота", "COURSEWORK"),
|
||||
("програмна інженерія", "SOFTWARE ENGINEERING"),
|
||||
("тестовий приклад", "DEMO SAMPLE"),
|
||||
),
|
||||
text: [
|
||||
Мета даної роботи -- продемонструвати оформлення пояснювальної записки
|
||||
для комплексного курсового проєкту з використанням нового варіанта шаблону.
|
||||
Приклад містить узагальнену тему, типові сторінки завдання, календарного плану,
|
||||
реферату, змісту, переліку джерел посилання та додатків.
|
||||
|
||||
У роботі наведено умовну структуру програмної системи, що може бути
|
||||
адаптована під конкретну предметну область. Основний акцент зроблено на
|
||||
перевірці полів титульної сторінки, сторінки завдання, службових підписів,
|
||||
нумерації розділів і коректної роботи додатків.
|
||||
|
||||
Результатом є демонстраційний документ, який показує очікуване використання
|
||||
шаблону без прив'язки до реального студента, викладача або завершеної роботи.
|
||||
],
|
||||
)
|
||||
#let abstract_en = (
|
||||
text: [
|
||||
The purpose of this work is to demonstrate the formatting of a coursework
|
||||
explanatory note using the new template variant.
|
||||
|
||||
The sample contains a generalized topic, typical pages for the assignment,
|
||||
calendar plan, abstract, contents, bibliography, and appendices.
|
||||
|
||||
The work presents an illustrative structure of a software system that can be
|
||||
adapted to a specific subject area. The main focus is on checking title-page
|
||||
fields, assignment-page fields, signature blocks, section numbering, and
|
||||
appendix handling.
|
||||
|
||||
The result is a demonstration document that shows the expected template usage
|
||||
without being tied to a real student, supervisor, or completed project.
|
||||
],
|
||||
)
|
||||
|
||||
#let appendices = [
|
||||
= Приклад звіту 1
|
||||
#v(-spacing)
|
||||
== Частина 1
|
||||
#lorem(100)
|
||||
== Частина 2
|
||||
#lorem(200)
|
||||
|
||||
= Приклад звіту 2
|
||||
#lorem(200)
|
||||
|
||||
= Приклад звіту 3
|
||||
#lorem(200)
|
||||
]
|
||||
|
||||
#show: coursework-v2.with(
|
||||
title: (
|
||||
"Демонстраційна інформаційна система для комплексного ",
|
||||
"курсового проєкту",
|
||||
),
|
||||
authors: authors,
|
||||
mentors: mentors,
|
||||
committee-members: committee_members,
|
||||
task-list: task-list,
|
||||
calendar-plan: calendar-plan,
|
||||
abstract: abstract,
|
||||
abstract-en: abstract_en,
|
||||
bib-path: bytes(read("bibl.yml")),
|
||||
appendices: appendices,
|
||||
)
|
||||
|
||||
= Моделювання
|
||||
#lorem(250)
|
||||
|
||||
= Імплементація
|
||||
#v(-spacing)
|
||||
== Підготовка
|
||||
#lorem(200)
|
||||
== Процес
|
||||
#lorem(500)
|
||||
|
||||
= Тестування
|
||||
#lorem(300)
|
||||
@@ -1,4 +1,4 @@
|
||||
#import "@local/nure:0.1.0": *
|
||||
#import "@local/nure:0.1.1": *
|
||||
#import style: spacing
|
||||
|
||||
#import "utils.typ": img
|
||||
@@ -0,0 +1,66 @@
|
||||
[settings]
|
||||
quiet = true
|
||||
env_shell_expand = true
|
||||
lockfile = true
|
||||
|
||||
[tools]
|
||||
typst = "latest"
|
||||
typstyle = "latest"
|
||||
tinymist = "latest"
|
||||
yq = "latest"
|
||||
|
||||
[vars]
|
||||
vendor_dir = "{{config_root}}/vendor"
|
||||
package_dir = "{{vars.vendor_dir}}/typst-packages"
|
||||
|
||||
work_doc_config = "src/doc.toml"
|
||||
work_input_file = "src/main.typ"
|
||||
work_output_file = "{{config_root}}/main.pdf"
|
||||
|
||||
nure_package_repo = "https://gitea.linerds.us/pencelheimer/typst_nure_template.git"
|
||||
nure_package_ref = "0.1.1"
|
||||
nure_package_rev = "d6037872ddb6391afec147a679fa05f08cba6bb0"
|
||||
|
||||
nure_package_name = "vendor/nure"
|
||||
nure_package_ver = "{{vars.nure_package_ref}}"
|
||||
|
||||
nure_package_path = "{{vars.package_dir}}/{{vars.nure_package_name}}/{{vars.nure_package_ver}}"
|
||||
|
||||
[env]
|
||||
TYPST_PACKAGE_DIR = "{{vars.package_dir}}"
|
||||
TYPST_PACKAGE_PATH = "$TYPST_PACKAGE_DIR"
|
||||
|
||||
[tasks]
|
||||
compile.depends = ["fetch-nure-package"]
|
||||
watch.depends = ["fetch-nure-package"]
|
||||
compile.run = "mise exec -- typst compile {{vars.work_input_file}} {{vars.work_output_file}}"
|
||||
watch.run = "mise exec -- typst watch {{vars.work_input_file}} {{vars.work_output_file}}"
|
||||
format.run = "mise exec -- typstyle -l 120 -i $MISE_PROJECT_ROOT/src"
|
||||
clean.run = "rm -rvif *.pdf"
|
||||
|
||||
update-package-rev.run = "mise config set vars.nure_package_rev $(git ls-remote {{vars.nure_package_repo}} refs/heads/{{vars.nure_package_ref}} | cut -f1)"
|
||||
|
||||
[tasks.fetch-nure-package]
|
||||
silent = "stdout"
|
||||
run = """
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [ ! -d {{vars.nure_package_path}}/.git ]; then
|
||||
git clone --depth 1 --revision {{vars.nure_package_rev}} {{vars.nure_package_repo}} {{vars.nure_package_path}}
|
||||
else
|
||||
git -C {{vars.nure_package_path}} fetch --depth 1 origin {{vars.nure_package_rev}}
|
||||
git -C {{vars.nure_package_path}} checkout {{vars.nure_package_rev}}
|
||||
fi
|
||||
"""
|
||||
|
||||
[tasks.rename]
|
||||
depends = ["compile"]
|
||||
run = """
|
||||
#!/usr/bin/env bash
|
||||
# Generates the following name from doc.toml: ПЗ1_Прізвище_ПЗПІ-23-2_ПарП.pdf
|
||||
# Assumes authors.at(0).name to be in the following format: "Прізвище І. Б."
|
||||
|
||||
final_name=$(mise exec -- yq '.type + (.number | tostring) + "_" + (.authors[0].name | split(" "))[0] + "_" + (.authors[0].edu-program + "-" + .authors[0].group) + "_" + .subject + ".pdf"' "{{vars.work_doc_config}}");
|
||||
|
||||
cp -v "{{vars.work_output_file}}" "$final_name"
|
||||
"""
|
||||
@@ -0,0 +1,35 @@
|
||||
[[tools.tinymist]]
|
||||
version = "0.14.14"
|
||||
backend = "aqua:Myriad-Dreamin/tinymist"
|
||||
"platforms.linux-arm64" = { checksum = "sha256:39eb99d290fd1aa0e4508e5d5bbbf5a49648cf26da8e7bc3ff49f75c5fd0dc6c", url = "https://github.com/Myriad-Dreamin/tinymist/releases/download/v0.14.14/tinymist-linux-arm64"}
|
||||
"platforms.linux-x64" = { checksum = "sha256:cda2ba76a455f3577cf2a1bd6340b75491f0c673770f631cf4d3232663860776", url = "https://github.com/Myriad-Dreamin/tinymist/releases/download/v0.14.14/tinymist-linux-x64"}
|
||||
"platforms.macos-arm64" = { checksum = "sha256:ea7a22af0d54a71fbd1ee6c8c9f8434b521549926ae0f53a82391adeea741aba", url = "https://github.com/Myriad-Dreamin/tinymist/releases/download/v0.14.14/tinymist-darwin-arm64"}
|
||||
"platforms.macos-x64" = { checksum = "sha256:f640889414d5030de1f03b966ccb10a84941bc1176980ff956d66714508ccd85", url = "https://github.com/Myriad-Dreamin/tinymist/releases/download/v0.14.14/tinymist-darwin-x64"}
|
||||
"platforms.windows-x64" = { checksum = "sha256:3d39c7b417287d8689c69159b65fbc62427e141b9489500ad81039ee783f2549", url = "https://github.com/Myriad-Dreamin/tinymist/releases/download/v0.14.14/tinymist-win32-x64.exe"}
|
||||
|
||||
[[tools.typst]]
|
||||
version = "0.14.2"
|
||||
backend = "aqua:typst/typst"
|
||||
"platforms.linux-arm64" = { checksum = "sha256:491b101aa40a3a7ea82a3f8a6232cabb4e6a7e233810082e5ac812d43fdcd47a", url = "https://github.com/typst/typst/releases/download/v0.14.2/typst-aarch64-unknown-linux-musl.tar.xz"}
|
||||
"platforms.linux-x64" = { checksum = "sha256:a6044cbad2a954deb921167e257e120ac0a16b20339ec01121194ff9d394996d", url = "https://github.com/typst/typst/releases/download/v0.14.2/typst-x86_64-unknown-linux-musl.tar.xz"}
|
||||
"platforms.macos-arm64" = { checksum = "sha256:470aa49a2298d20b65c119a10e4ff8808550453e0cb4d85625b89caf0cedf048", url = "https://github.com/typst/typst/releases/download/v0.14.2/typst-aarch64-apple-darwin.tar.xz"}
|
||||
"platforms.macos-x64" = { checksum = "sha256:4e91d8e1e33ab164f949c5762e01ee3faa585c8615a2a6bd5e3677fa8506b249", url = "https://github.com/typst/typst/releases/download/v0.14.2/typst-x86_64-apple-darwin.tar.xz"}
|
||||
"platforms.windows-x64" = { checksum = "sha256:51353994ac83218c3497052e89b2c432c53b9d4439cdc1b361e2ea4798ebfc13", url = "https://github.com/typst/typst/releases/download/v0.14.2/typst-x86_64-pc-windows-msvc.zip"}
|
||||
|
||||
[[tools.typstyle]]
|
||||
version = "0.14.4"
|
||||
backend = "aqua:Enter-tainer/typstyle"
|
||||
"platforms.linux-arm64" = { checksum = "sha256:b8c220c0d940d7690fb15ef10415de35fdc115465a7534d8102459c68aeec74d", url = "https://github.com/typstyle-rs/typstyle/releases/download/v0.14.4/typstyle-aarch64-unknown-linux-gnu"}
|
||||
"platforms.linux-x64" = { checksum = "sha256:48d9a9a3885855f1b4f6c2b8ea9739623bc458b99c015b77c4f50a1f342ea091", url = "https://github.com/typstyle-rs/typstyle/releases/download/v0.14.4/typstyle-x86_64-unknown-linux-musl"}
|
||||
"platforms.macos-arm64" = { checksum = "sha256:dda3a162f2457a40570c3552cc9c01004138b33f2f921112b117d72f48bd6d56", url = "https://github.com/typstyle-rs/typstyle/releases/download/v0.14.4/typstyle-aarch64-apple-darwin"}
|
||||
"platforms.macos-x64" = { checksum = "sha256:2343f7a4801d9bbda5d7441e8fea95f98b590d6aa3083da5c4e4ca3de885350f", url = "https://github.com/typstyle-rs/typstyle/releases/download/v0.14.4/typstyle-x86_64-apple-darwin"}
|
||||
"platforms.windows-x64" = { checksum = "sha256:b16aebaed0aa296ef3e055d611f1e917f5849d3eb0f953bbcaa6f09f0503fb5b", url = "https://github.com/typstyle-rs/typstyle/releases/download/v0.14.4/typstyle-x86_64-pc-windows-msvc.exe"}
|
||||
|
||||
[[tools.yq]]
|
||||
version = "4.52.2"
|
||||
backend = "aqua:mikefarah/yq"
|
||||
"platforms.linux-arm64" = { checksum = "sha256:c82856ac30da522f50dcdd4f53065487b5a2927e9b87ff637956900986f1f7c2", url = "https://github.com/mikefarah/yq/releases/download/v4.52.2/yq_linux_arm64"}
|
||||
"platforms.linux-x64" = { checksum = "sha256:a74bd266990339e0c48a2103534aef692abf99f19390d12c2b0ce6830385c459", url = "https://github.com/mikefarah/yq/releases/download/v4.52.2/yq_linux_amd64"}
|
||||
"platforms.macos-arm64" = { checksum = "sha256:34613ea97c4c77e1894a8978dbf72588d187a69a6292c10dab396c767a1ecde7", url = "https://github.com/mikefarah/yq/releases/download/v4.52.2/yq_darwin_arm64"}
|
||||
"platforms.macos-x64" = { checksum = "sha256:54a63555210e73abed09108097072e28bf82a6bb20439a72b55509c4dd42378d", url = "https://github.com/mikefarah/yq/releases/download/v4.52.2/yq_darwin_amd64"}
|
||||
"platforms.windows-x64" = { checksum = "sha256:2b6cd8974004fa0511f6b6b359d2698214fadeb4599f0b00e8d85ae62b3922d4", url = "https://github.com/mikefarah/yq/releases/download/v4.52.2/yq_windows_amd64.exe"}
|
||||
@@ -0,0 +1,265 @@
|
||||
# unexplrd's example setup with mise
|
||||
|
||||
This example has the following structure:
|
||||
```
|
||||
.
|
||||
├── .mise/
|
||||
│ ├── config.toml
|
||||
│ └── mise.lock
|
||||
├── vendor/
|
||||
│ └── typst-packages/
|
||||
│ └── ...
|
||||
└── src/
|
||||
├── assets/
|
||||
│ ├── foo.csv
|
||||
│ ├── bar.c
|
||||
│ └── ...
|
||||
├── figures/
|
||||
│ ├── clojure-logo.png
|
||||
│ ├── error-log.jpg
|
||||
│ └── ...
|
||||
├── doc.toml
|
||||
├── main.typ
|
||||
└── utils.typ
|
||||
```
|
||||
|
||||
## Advantages
|
||||
- Minimal
|
||||
- All you need is [`mise`](https://mise.jdx.dev/) and `git`
|
||||
- Declarative
|
||||
- Sets up the environment and dependencies automatically
|
||||
- Every work config is separate to avoid breaking changes in Typst and `nure` package
|
||||
- Customizable
|
||||
- Add your own mise tasks, change existing tasks, it's just a `.toml` file
|
||||
|
||||
## Project structure
|
||||
All work contents are stored in `src/` to avoid clutter.
|
||||
|
||||
### Files
|
||||
- `src/doc.toml`: work-specific settings, could be auto-filled, useful for scripts, see (TODO: add a demo nushell script)
|
||||
- `src/utils.typ`: utilities and functions used across the project
|
||||
- `src/main.typ`: main entry file
|
||||
|
||||
### Directories
|
||||
- `src/chapters/`: for breaking up the project into multiple files, if necessary
|
||||
- `src/figures/`: for images and pictures
|
||||
- `src/assets/`: other non-image, non-typst files
|
||||
|
||||
## Mise
|
||||
|
||||
### Tasks
|
||||
- `compile`: Execute `typst compile` on `src/main.typ`
|
||||
- `watch`: Execute `typst watch` on `src/main.typ`
|
||||
- `rename`: Copy output `.pdf` file with an auto-generated name from metadata in `doc.toml`
|
||||
- `fetch-nure-package`: clone package git repository to a directory specified in `[vars]`
|
||||
- `update-package-rev`: updates `vars.nure_package_rev` with the last commit id from the specified branch (`vars.nure_package_ref`)
|
||||
|
||||
## Examples
|
||||
|
||||
### Templating
|
||||
|
||||
I have a following per-semester directory structure:
|
||||
```
|
||||
/home/user/Documents/nure/
|
||||
└── .config/
|
||||
├── defaults.yaml
|
||||
├── subjects.yaml
|
||||
├── mise/
|
||||
│ ├── config.toml
|
||||
│ └── mise.lock
|
||||
└── template/
|
||||
├── .mise
|
||||
│ ├── config.toml
|
||||
│ └── mise.lock
|
||||
└── src
|
||||
├── appendices.typ
|
||||
├── assets/
|
||||
├── figures/
|
||||
├── main.typ
|
||||
└── utils.typ
|
||||
```
|
||||
|
||||
<details><summary>Contents of <code>.config/default.yaml</code></summary><p>
|
||||
|
||||
```yaml
|
||||
university: ХНУРЕ
|
||||
default_author:
|
||||
name: Косач Л. П.
|
||||
edu-program: ПЗПІ
|
||||
group: 23-2
|
||||
gender: f
|
||||
variant: 8
|
||||
full-name-gen: Косач Лариси Петрівни
|
||||
course: 2
|
||||
semester: 4
|
||||
work_types:
|
||||
lb: ЛБ
|
||||
pz: ПЗ
|
||||
kr: КР
|
||||
```
|
||||
|
||||
</p></details>
|
||||
|
||||
<details><summary>Contents of <code>.config/subjects.yaml</code></summary><p>
|
||||
|
||||
```yaml
|
||||
smp:
|
||||
name: СМП
|
||||
full_name: Скриптові мови програмування
|
||||
mentors:
|
||||
- name: Шевченко Т. Г.
|
||||
degree: Доцент кафедри ПІ
|
||||
gender: m
|
||||
- name: Франко І. Я.
|
||||
degree: Асистент кафедри ПІ
|
||||
gender: m
|
||||
iks:
|
||||
name: ІКС
|
||||
full_name: Інформаційно-комунікаційні системи
|
||||
mentors:
|
||||
- name: Мартинчук О. О.
|
||||
degree: Доцент кафедри ІКІ
|
||||
gender: m
|
||||
# etc...
|
||||
```
|
||||
|
||||
</p></details>
|
||||
|
||||
<details><summary>Contents of <code>.config/mise/config.toml</code></summary><p>
|
||||
|
||||
```toml
|
||||
[settings]
|
||||
quiet = true
|
||||
env_shell_expand = true
|
||||
lockfile = true
|
||||
|
||||
[tools]
|
||||
"aqua:nushell/nushell" = "latest"
|
||||
|
||||
[tasks.work]
|
||||
usage = '''
|
||||
arg "<subject>" help="Target environment"
|
||||
arg "<type>" help="Target environment"
|
||||
arg "<number>" help="Target environment"
|
||||
'''
|
||||
|
||||
run = '''
|
||||
#!/usr/bin/env nu
|
||||
|
||||
let template_dir = ".config/template"
|
||||
let config_dir = ".config"
|
||||
# let out_dir = $"(pwd)"
|
||||
let out_dir = $"(pwd)/works"
|
||||
|
||||
def not_main [subject: string, shortcode: string, number?: int] {
|
||||
let subject_lower = ($subject | str downcase)
|
||||
let shortcode_lower = ($shortcode | str downcase)
|
||||
|
||||
validate-input $subject $shortcode $number
|
||||
|
||||
let temp_dir = $"/tmp/nure-work-(random chars -l 8)"
|
||||
let final_dir = $"($out_dir)/($subject_lower)/($shortcode_lower)($number)"
|
||||
let subject_dir = $"($out_dir)/($subject_lower)"
|
||||
|
||||
# TODO?: replace with git clone
|
||||
cp -r ($config_dir)/template $temp_dir
|
||||
generate-doc-toml $shortcode_lower $subject_lower $temp_dir $number
|
||||
check-mkdir $out_dir
|
||||
if ($final_dir | path exists) {
|
||||
error make -u {msg: $"Directory ($final_dir) already exists"}
|
||||
}
|
||||
|
||||
check-mkdir $subject_dir; cp -r $temp_dir $final_dir; rm -rf $temp_dir
|
||||
print $"=> Created new work: ($final_dir)"
|
||||
}
|
||||
|
||||
def check-mkdir [path: string] { if not ($path | path exists) { mkdir $path } }
|
||||
|
||||
def validate-input [subject: string, shortcode: string, number?: int] {
|
||||
if not ($config_dir | path exists) {
|
||||
error make -u {msg: $"($config_dir) not found"}
|
||||
}
|
||||
|
||||
let defaults_yaml = $"($config_dir)/defaults.yaml"
|
||||
let subjects_yaml = $"($config_dir)/subjects.yaml"
|
||||
|
||||
if not ($defaults_yaml | path exists) {
|
||||
error make -u {msg: $"defaults.yaml not found in ($config_dir)"}
|
||||
}
|
||||
if not ($subjects_yaml | path exists) {
|
||||
error make -u {msg: $"subjects.yaml not found in ($config_dir)"}
|
||||
}
|
||||
|
||||
if ($number != nothing and $number < 1 ) {
|
||||
error make -u {msg: "Work number should be greater than 0"}
|
||||
}
|
||||
|
||||
let subject_lower = ($subject | str downcase)
|
||||
let subjects = (open $subjects_yaml | columns)
|
||||
if $subject_lower not-in $subjects {
|
||||
error make -u {
|
||||
msg: $"Subject '($subject)' not found in configuration"
|
||||
help: $"Available subjects: ($subjects | str join ', ')"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def generate-doc-toml [shortcode: string, subject: string, target_dir: string, number?: int] {
|
||||
let defaults = (open $"($config_dir)/defaults.yaml")
|
||||
let subjects = (open $"($config_dir)/subjects.yaml")
|
||||
|
||||
let subject_data = ($subjects | get $subject)
|
||||
let work_type = ($defaults | get work_types | try { get $shortcode } catch { $shortcode })
|
||||
|
||||
{
|
||||
university: ($defaults | get university)
|
||||
subject: ($subject_data | get name), type: $work_type, number: $number
|
||||
mentors: ($subject_data | get mentors), authors: [($defaults | get default_author)]
|
||||
} | compact --empty | to toml | save $"($target_dir)/src/doc.toml" --force
|
||||
}
|
||||
|
||||
not_main {{usage.subject}} {{usage.type}} {{usage.number}}
|
||||
'''
|
||||
```
|
||||
|
||||
</p></details>
|
||||
|
||||
`template/` is this empty template without `doc.toml`
|
||||
|
||||
Nushell script does two things:
|
||||
- Creates a new work from a template: `works/smp/lb1`
|
||||
- Auto-fills `doc.toml` with data from `defaults.yaml` and mentor data from `subjects.yaml`
|
||||
|
||||
### Patching the package
|
||||
A patch to add new subjects:
|
||||
|
||||
```diff
|
||||
diff --git a/src/config/universities.yaml b/src/config/universities.yaml
|
||||
index 8855a07..1aefc96 100644
|
||||
--- a/src/config/universities.yaml
|
||||
+++ b/src/config/universities.yaml
|
||||
@@ -60,7 +60,6 @@
|
||||
ПЕСЕ: Психологія екстремальних стосунків та ефективної адаптації
|
||||
ПНП: Програмування на платформі .NЕТ
|
||||
ПП: Проектний практикум
|
||||
- ПРОГ: Програмування
|
||||
ПарП: Параллельне програмування
|
||||
СА: Системний аналіз
|
||||
СМП: Скриптові мови програмування
|
||||
@@ -76,3 +75,10 @@
|
||||
ФІЛ: Філософія
|
||||
ФВС: Фізичне виховання та спорт
|
||||
ХТ: Хмарні технології
|
||||
+ АКС: Архітектура комп'ютерних систем
|
||||
+ ІКС: Інформаційно-комунікаційні системи
|
||||
+ МБ: Мережна безпека
|
||||
+ МКр: Мережна криміналістика
|
||||
+ ОКЗІ: Основи криптографічного захисту інформації
|
||||
+ Прог: Програмування
|
||||
+ ТІК: Теорія інформації та кодування
|
||||
```
|
||||
|
||||
To apply it, add the following command to the end of `fetch_nure_package` task:
|
||||
```sh
|
||||
git -C {{vars.nure_package_path}} apply --check --apply --quiet $MISE_PROJECT_ROOT/custom-subjects.patch || exit 0
|
||||
```
|
||||
@@ -0,0 +1,19 @@
|
||||
university = "ХНУРЕ"
|
||||
subject = "АКС"
|
||||
type = "ЛБ"
|
||||
number = 1
|
||||
|
||||
[[mentors]]
|
||||
name = ""
|
||||
degree = ""
|
||||
gender = ""
|
||||
|
||||
[[authors]]
|
||||
name = ""
|
||||
edu-program = "КУІБ"
|
||||
group = "24-1"
|
||||
gender = "m"
|
||||
variant = 6
|
||||
full-name-gen = ""
|
||||
course = 2
|
||||
semester = 4
|
||||
@@ -0,0 +1,28 @@
|
||||
#import "@vendor/nure:0.1.1": *
|
||||
#import "utils.typ": *
|
||||
|
||||
#import style: spacing
|
||||
|
||||
// Apply custom rule from utils.typ
|
||||
// #show: correctly-indent-list-and-enum-items
|
||||
|
||||
// #show: style.dstu
|
||||
#show: pz-lb.with(..toml("doc.toml"), title: "") // set title to none if empty
|
||||
|
||||
/// Useful snippets
|
||||
|
||||
/// Import a .csv table
|
||||
// #figure(
|
||||
// caption: [],
|
||||
// table(
|
||||
// columns: 4,
|
||||
// table.header([], [], [], []),
|
||||
// ..csv("assets/table.csv").flatten(),
|
||||
// ),
|
||||
// )
|
||||
|
||||
/// Appendices
|
||||
// #style.appendices(include "chapters/appendices.typ")
|
||||
// or
|
||||
// #show: style.appendices
|
||||
// = ...
|
||||
@@ -0,0 +1,112 @@
|
||||
#import "@vendor/nure:0.1.1": utils
|
||||
|
||||
/// captioned image with auto-generated label from path
|
||||
/// Usage: img("path/to/image.png", "Caption")(optional: "source")
|
||||
#let img(path, caption, ..sink) = {
|
||||
let source = sink.pos().at(0, default: ())
|
||||
[ #figure(image(path, ..sink.named()), caption: utils.img-caption(caption, source)) #utils.img-label(path) ]
|
||||
}
|
||||
|
||||
/// takes in a string of code, e.g. #code(read("foo.c"))
|
||||
#let code(content) = raw(block: true, theme: none, content)
|
||||
|
||||
/// read path as bytes
|
||||
#let b(path) = bytes(read(path, encoding: none))
|
||||
|
||||
/// include chapters by file names from /chapters
|
||||
#let chapters(ch) = (
|
||||
array(ch).map(chapter => include str(chapter) + ".typ").join()
|
||||
)
|
||||
|
||||
/// https://forum.typst.app/t/how-to-make-bullet-list-item-bodies-flow-like-paragraphs/3756/3?u=andrew
|
||||
/// Spacing doesn't work the same way as native solution if par leading and
|
||||
/// spacing are different.
|
||||
#let correctly-indent-list-and-enum-items(doc) = {
|
||||
let first-line-indent() = if type(par.first-line-indent) == dictionary {
|
||||
par.first-line-indent.amount
|
||||
} else {
|
||||
par.first-line-indent
|
||||
}
|
||||
|
||||
show list: li => {
|
||||
for (i, it) in li.children.enumerate() {
|
||||
let nesting = state("list-nesting", 0)
|
||||
let indent = context h((nesting.get() + 1) * li.indent)
|
||||
let get-nesting() = calc.div-euclid(nesting.get(), 10)
|
||||
let marker = context {
|
||||
let n = get-nesting()
|
||||
if type(li.marker) == array {
|
||||
li.marker.at(calc.rem-euclid(n, li.marker.len()))
|
||||
} else if type(li.marker) == content {
|
||||
li.marker
|
||||
} else {
|
||||
li.marker(n)
|
||||
}
|
||||
}
|
||||
let parents = state("enum-parents", ()) // Support enum nesting.
|
||||
let body = {
|
||||
parents.update(arr => arr + (-1,))
|
||||
nesting.update(x => x + 10)
|
||||
it.body + parbreak()
|
||||
nesting.update(x => x - 10)
|
||||
parents.update(arr => arr.slice(0, -1))
|
||||
}
|
||||
let content = {
|
||||
marker
|
||||
h(li.body-indent)
|
||||
body
|
||||
}
|
||||
context pad(left: int(nesting.get() != 0) * li.indent, content)
|
||||
}
|
||||
}
|
||||
|
||||
show enum: en => {
|
||||
let start = if en.start == auto {
|
||||
if en.children.first().has("number") {
|
||||
if en.reversed { en.children.first().number } else { 1 }
|
||||
} else {
|
||||
if en.reversed { en.children.len() } else { 1 }
|
||||
}
|
||||
} else {
|
||||
en.start
|
||||
}
|
||||
let number = start
|
||||
for (i, it) in en.children.enumerate() {
|
||||
number = if it.number != auto { it.number } else { number }
|
||||
if en.reversed { number = start - i }
|
||||
let parents = state("enum-parents", ())
|
||||
let get-parents() = parents.get().filter(x => x >= 0)
|
||||
let indent = context h((get-parents().len() + 1) * en.indent)
|
||||
let num = if en.full {
|
||||
context numbering(en.numbering, ..get-parents(), number)
|
||||
} else {
|
||||
numbering(en.numbering, number)
|
||||
}
|
||||
let max-num = if en.full {
|
||||
context numbering(en.numbering, ..get-parents(), en.children.len())
|
||||
} else {
|
||||
numbering(en.numbering, en.children.len())
|
||||
}
|
||||
num = context box(
|
||||
width: measure(max-num).width,
|
||||
align(right, text(overhang: false, num)),
|
||||
)
|
||||
let list-nesting = state("list-nesting", 0) // Support list nesting.
|
||||
let body = {
|
||||
parents.update(arr => arr + (number,))
|
||||
list-nesting.update(x => x + 1)
|
||||
it.body + parbreak()
|
||||
list-nesting.update(x => x - 1)
|
||||
parents.update(arr => arr.slice(0, -1))
|
||||
}
|
||||
if not en.reversed { number += 1 }
|
||||
let content = {
|
||||
num
|
||||
h(en.body-indent)
|
||||
body
|
||||
}
|
||||
context pad(left: int(parents.get().len() != 0) * en.indent, content)
|
||||
}
|
||||
}
|
||||
doc
|
||||
}
|
||||
+1
-1
@@ -7,5 +7,5 @@ license = "GPL-3.0"
|
||||
description = "Typst NURE package"
|
||||
|
||||
[template]
|
||||
path = "template"
|
||||
path = "template/default"
|
||||
entrypoint = "lab.typ"
|
||||
|
||||
Reference in New Issue
Block a user