oaktree-sapling 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.md +66 -0
- package/ci/run.sh +64 -0
- package/dist/cli.cjs +1230 -0
- package/package.json +50 -0
- package/paper-base.yml +29 -0
- package/plugins/gallery.mjs +224 -0
- package/templates/instance/brand/brand.yml +15 -0
- package/templates/instance/brand/logo.svg +4 -0
- package/templates/instance/editions/edition.yml +14 -0
- package/templates/instance/journal.yml +58 -0
- package/templates/instance/registry/papers.yml +20 -0
- package/templates/paper/.github/actions/engine/action.yml +59 -0
- package/templates/paper/.github/actions/engine/pins.yml +16 -0
- package/templates/paper/.github/workflows/check-post.yml +79 -0
- package/templates/paper/.github/workflows/check.yml +72 -0
- package/templates/paper/.github/workflows/ci.yml +64 -0
- package/templates/paper/.github/workflows/prepare.yml +34 -0
- package/templates/paper/.github/workflows/preview-deploy.yml +52 -0
- package/templates/paper/.github/workflows/publish.yml +33 -0
- package/templates/paper/.github/workflows/version-bump.yml +29 -0
- package/templates/paper/CODEOWNERS +9 -0
- package/templates/paper/bib.bib +6 -0
- package/templates/paper/gitignore +8 -0
- package/templates/paper/index.md +12 -0
- package/templates/paper/myst.yml +37 -0
- package/templates/site/.github/workflows/site.yml +73 -0
- package/templates/site/gitignore +5 -0
- package/templates/site/myst.yml +32 -0
- package/templates/site/package.json +9 -0
- package/templates/site/pages/index.md +25 -0
- package/templates/typst/LICENSE +21 -0
- package/templates/typst/frontmatter.typ +244 -0
- package/templates/typst/lapreprint.typ +310 -0
- package/templates/typst/template.typ +112 -0
- package/templates/typst/template.yml +82 -0
- package/typst.version +1 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#let orcidLogo(
|
|
2
|
+
// The ORCID identifier with no URL, e.g. `0000-0000-0000-0000`
|
|
3
|
+
orcid: none,
|
|
4
|
+
) = {
|
|
5
|
+
/* Logos */
|
|
6
|
+
let orcidSvg = ```<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 24 24"><path fill="#AECD54" d="M21.8,12c0,5.4-4.4,9.8-9.8,9.8S2.2,17.4,2.2,12S6.6,2.2,12,2.2S21.8,6.6,21.8,12z M8.2,5.8c-0.4,0-0.8,0.3-0.8,0.8s0.3,0.8,0.8,0.8S9,7,9,6.6S8.7,5.8,8.2,5.8z M10.5,15.4h1.2v-6c0,0-0.5,0,1.8,0s3.3,1.4,3.3,3s-1.5,3-3.3,3s-1.9,0-1.9,0H10.5v1.1H9V8.3H7.7v8.2h2.9c0,0-0.3,0,3,0s4.5-2.2,4.5-4.1s-1.2-4.1-4.3-4.1s-3.2,0-3.2,0L10.5,15.4z"/></svg>```.text
|
|
7
|
+
|
|
8
|
+
if (orcid == none) {
|
|
9
|
+
// Do not
|
|
10
|
+
box(height: 1.1em, baseline: 13.5%, [#image.decode(orcidSvg)])
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
link("https://orcid.org/" + orcid)[#box(height: 1.1em, baseline: 13.5%)[#image(bytes(orcidSvg))]]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
#let validateString(raw, name, alias: none) = {
|
|
18
|
+
if (name in raw) {
|
|
19
|
+
assert(type(raw.at(name)) == str, message: name + " must be a string")
|
|
20
|
+
return raw.at(name)
|
|
21
|
+
}
|
|
22
|
+
if (type(alias) != array) {
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
for a in alias {
|
|
26
|
+
if (a in raw) {
|
|
27
|
+
assert(type(raw.at(a)) == str, message: a + " must be a string")
|
|
28
|
+
return raw.at(a)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#let validateBoolean(raw, name, alias: none) = {
|
|
34
|
+
if (name in raw) {
|
|
35
|
+
assert(type(raw.at(name)) == bool, message: name + " must be a boolean")
|
|
36
|
+
return raw.at(name)
|
|
37
|
+
}
|
|
38
|
+
if (type(alias) != array) {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
for a in alias {
|
|
42
|
+
if (a in raw) {
|
|
43
|
+
assert(type(raw.at(a)) == bool, message: a + " must be a boolean")
|
|
44
|
+
return raw.at(a)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
#let validateAffiliation(raw) = {
|
|
51
|
+
let out = (:)
|
|
52
|
+
if (type(raw) == str) {
|
|
53
|
+
out.name = raw;
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
let id = validateString(raw, "id")
|
|
57
|
+
if (id != none) { out.id = id }
|
|
58
|
+
let name = validateString(raw, "name")
|
|
59
|
+
if (name != none) { out.name = name }
|
|
60
|
+
let institution = validateString(raw, "institution")
|
|
61
|
+
if (institution != none) { out.institution = institution }
|
|
62
|
+
let department = validateString(raw, "department")
|
|
63
|
+
if (department != none) { out.department = department }
|
|
64
|
+
let doi = validateString(raw, "doi")
|
|
65
|
+
if (doi != none) { out.doi = doi }
|
|
66
|
+
let ror = validateString(raw, "ror")
|
|
67
|
+
if (ror != none) { out.ror = ror }
|
|
68
|
+
let address = validateString(raw, "address")
|
|
69
|
+
if (address != none) { out.address = address }
|
|
70
|
+
let city = validateString(raw, "city")
|
|
71
|
+
if (city != none) { out.city = city }
|
|
72
|
+
let region = validateString(raw, "region", alias: ("state", "province"))
|
|
73
|
+
if (region != none) { out.region = region }
|
|
74
|
+
let postal-code = validateString(raw, "postal-code", alias: ("postal_code", "postalCode", "zip_code", "zip-code", "zipcode", "zipCode"))
|
|
75
|
+
if (postal-code != none) { out.postal-code = postal-code }
|
|
76
|
+
let country = validateString(raw, "country")
|
|
77
|
+
if (country != none) { out.country = country }
|
|
78
|
+
let phone = validateString(raw, "phone")
|
|
79
|
+
if (phone != none) { out.phone = phone }
|
|
80
|
+
let fax = validateString(raw, "fax")
|
|
81
|
+
if (fax != none) { out.fax = fax }
|
|
82
|
+
let email = validateString(raw, "email")
|
|
83
|
+
if (email != none) { out.email = email }
|
|
84
|
+
let url = validateString(raw, "url")
|
|
85
|
+
if (url != none) { out.url = url }
|
|
86
|
+
let collaboration = validateBoolean(raw, "collaboration")
|
|
87
|
+
if (collaboration != none) { out.collaboration = collaboration }
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
#let pickAffiliationsObject(raw) = {
|
|
92
|
+
if ("affiliation" in raw and "affiliations" in raw) {
|
|
93
|
+
panic("You can only use `affiliation` or `affiliations`, not both")
|
|
94
|
+
}
|
|
95
|
+
if ("affiliation" in raw) {
|
|
96
|
+
raw.affiliations = raw.affiliation
|
|
97
|
+
}
|
|
98
|
+
if ("affiliations" not in raw) { return; }
|
|
99
|
+
if (type(raw.affiliations) == str or type(raw.affiliations) == dictionary) {
|
|
100
|
+
// convert to a list
|
|
101
|
+
return (validateAffiliation(raw.affiliations),)
|
|
102
|
+
} else if (type(raw.affiliations) == array) {
|
|
103
|
+
// validate each entry
|
|
104
|
+
return raw.affiliations.map(validateAffiliation)
|
|
105
|
+
} else {
|
|
106
|
+
panic("The `affiliation` or `affiliations` must be a array, dictionary or string, got:", type(raw.affiliations))
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
#let validateAuthor(raw) = {
|
|
112
|
+
let out = (:)
|
|
113
|
+
if (type(raw) == str) {
|
|
114
|
+
out.name = raw;
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
let name = validateString(raw, "name")
|
|
118
|
+
if (name != none) { out.name = name }
|
|
119
|
+
let orcid = validateString(raw, "orcid")
|
|
120
|
+
if (orcid != none) { out.orcid = orcid }
|
|
121
|
+
let email = validateString(raw, "email")
|
|
122
|
+
if (email != none) { out.email = email }
|
|
123
|
+
let url = validateString(raw, "url")
|
|
124
|
+
if (url != none) { out.url = url }
|
|
125
|
+
|
|
126
|
+
let affiliations = pickAffiliationsObject(raw);
|
|
127
|
+
if (affiliations != none) { out.affiliations = affiliations } else { out.affiliations = () }
|
|
128
|
+
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#let consolidateAffiliations(authors, affiliations) = {
|
|
133
|
+
let cnt = 0
|
|
134
|
+
for affiliation in affiliations {
|
|
135
|
+
if ("id" not in affiliation) {
|
|
136
|
+
affiliation.insert("id", "aff-" + str(cnt + 1))
|
|
137
|
+
}
|
|
138
|
+
affiliations.at(cnt) = affiliation
|
|
139
|
+
cnt += 1
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
let authorCnt = 0
|
|
143
|
+
for author in authors {
|
|
144
|
+
let affCnt = 0
|
|
145
|
+
for affiliation in author.affiliations {
|
|
146
|
+
let pos = affiliations.position(item => { ("id" in item and item.id == affiliation.name) or ("name" in item and item.name == affiliation.name) })
|
|
147
|
+
if (pos != none) {
|
|
148
|
+
affiliation.remove("name")
|
|
149
|
+
affiliation.id = affiliations.at(pos).id
|
|
150
|
+
affiliations.at(pos) = affiliations.at(pos) + affiliation
|
|
151
|
+
} else {
|
|
152
|
+
affiliation.id = if ("id" in affiliation) { affiliation.id } else { affiliation.name }
|
|
153
|
+
affiliations.push(affiliation)
|
|
154
|
+
}
|
|
155
|
+
author.affiliations.at(affCnt) = (id: affiliation.id)
|
|
156
|
+
affCnt += 1
|
|
157
|
+
}
|
|
158
|
+
authors.at(authorCnt) = author
|
|
159
|
+
authorCnt += 1
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Now that they are normalized, loop again and update the numbers
|
|
163
|
+
let fullAffCnt = 0
|
|
164
|
+
let authorCnt = 0
|
|
165
|
+
for author in authors {
|
|
166
|
+
let affCnt = 0
|
|
167
|
+
for affiliation in author.affiliations {
|
|
168
|
+
let pos = affiliations.position(item => { item.id == affiliation.id })
|
|
169
|
+
let aff = affiliations.at(pos)
|
|
170
|
+
if ("index" not in aff) {
|
|
171
|
+
fullAffCnt += 1
|
|
172
|
+
aff.index = fullAffCnt
|
|
173
|
+
affiliations.at(pos) = affiliations.at(pos) + (index: fullAffCnt)
|
|
174
|
+
}
|
|
175
|
+
author.affiliations.at(affCnt) = (id: affiliation.id, index: aff.index)
|
|
176
|
+
affCnt += 1
|
|
177
|
+
}
|
|
178
|
+
authors.at(authorCnt) = author
|
|
179
|
+
authorCnt += 1
|
|
180
|
+
}
|
|
181
|
+
return (authors: authors, affiliations: affiliations)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
#let loadFrontmatter(raw) = {
|
|
185
|
+
let out = (:)
|
|
186
|
+
let title = validateString(raw, "title")
|
|
187
|
+
if (title != none) { out.title = title }
|
|
188
|
+
let subtitle = validateString(raw, "subtitle")
|
|
189
|
+
if (subtitle != none) { out.subtitle = subtitle }
|
|
190
|
+
let short-title = validateString(raw, "short-title", alias: ("short_title", "shortTitle", "runningHead",))
|
|
191
|
+
if (short-title != none) { out.short-title = short-title }
|
|
192
|
+
|
|
193
|
+
// author information
|
|
194
|
+
if ("author" in raw and "authors" in raw) {
|
|
195
|
+
panic("You can only use `author` or `authors`, not both")
|
|
196
|
+
}
|
|
197
|
+
if ("author" in raw) {
|
|
198
|
+
raw.authors = raw.author
|
|
199
|
+
}
|
|
200
|
+
if ("authors" in raw) {
|
|
201
|
+
if (type(raw.authors) == str or type(raw.authors) == dictionary) {
|
|
202
|
+
// convert to a list
|
|
203
|
+
out.authors = (validateAuthor(raw.authors),)
|
|
204
|
+
} else if (type(raw.authors) == array) {
|
|
205
|
+
// validate each entry
|
|
206
|
+
out.authors = raw.authors.map(validateAuthor)
|
|
207
|
+
} else {
|
|
208
|
+
panic("The `author` or `authors` must be a array, dictionary or string, got:", type(raw.authors))
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let affiliations = pickAffiliationsObject(raw);
|
|
213
|
+
if (affiliations != none) { out.affiliations = affiliations } else { out.affiliations = () }
|
|
214
|
+
|
|
215
|
+
let open-access = validateBoolean(raw, "open-access", alias: ("open_access", "openAccess",))
|
|
216
|
+
if (open-access != none) { out.open-access = open-access }
|
|
217
|
+
let doi = validateString(raw, "doi")
|
|
218
|
+
if (doi != none) {
|
|
219
|
+
assert(not doi.starts-with("http"), message: "DOIs should not include the link, use only the part after `https://doi.org/[]`")
|
|
220
|
+
out.doi = doi
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let date = none;
|
|
224
|
+
let citation = validateString(raw, "citation")
|
|
225
|
+
if (citation != none) {
|
|
226
|
+
out.citation = citation;
|
|
227
|
+
} else {
|
|
228
|
+
// Create a citation, e.g. Cockett et al., 2023
|
|
229
|
+
let year = if (date != none) { ", " + date.display("[year]") } else { ", " + datetime.today().display("[year]") }
|
|
230
|
+
if (out.authors.len() == 1) {
|
|
231
|
+
out.citation = out.authors.at(0).name.split(" ").last() + year
|
|
232
|
+
} else if (out.authors.len() == 2) {
|
|
233
|
+
out.citation = out.authors.at(0).name.split(" ").last() + " & " + out.authors.at(1).name.split(" ").last() + year
|
|
234
|
+
} else if (out.authors.len() > 2) {
|
|
235
|
+
out.citation = out.authors.at(0).name.split(" ").last() + " " + emph("et al.") + year
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let consolidated = consolidateAffiliations(out.authors, out.affiliations)
|
|
240
|
+
out.authors = consolidated.authors
|
|
241
|
+
out.affiliations = consolidated.affiliations
|
|
242
|
+
|
|
243
|
+
return out
|
|
244
|
+
}
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
#import "frontmatter.typ": orcidLogo, loadFrontmatter
|
|
2
|
+
|
|
3
|
+
#let template(
|
|
4
|
+
// The paper's title.
|
|
5
|
+
title: "Paper Title",
|
|
6
|
+
subtitle: none,
|
|
7
|
+
|
|
8
|
+
// An array of authors. For each author you can specify a name, orcid, and affiliations.
|
|
9
|
+
// affiliations should be content, e.g. "1", which is shown in superscript and should match the affiliations list.
|
|
10
|
+
// Everything but but the name is optional.
|
|
11
|
+
authors: (),
|
|
12
|
+
// This is the affiliations list. Include an id and `name` in each affiliation. These are shown below the authors.
|
|
13
|
+
affiliations: (),
|
|
14
|
+
// The paper's abstract. Can be omitted if you don't have one.
|
|
15
|
+
abstract: none,
|
|
16
|
+
// The short-title is shown in the running header
|
|
17
|
+
short-title: none,
|
|
18
|
+
// The short-citation is shown in the running header, if set to auto it will show the author(s) and the year in APA format.
|
|
19
|
+
short-citation: auto,
|
|
20
|
+
// The venue is show in the footer
|
|
21
|
+
venue: none,
|
|
22
|
+
// An image path that is shown in the top right of the page. Can also be content.
|
|
23
|
+
logo: none,
|
|
24
|
+
// A DOI link, shown in the header on the first page. Should be just the DOI, e.g. `10.10123/123456` ,not a URL
|
|
25
|
+
doi: none,
|
|
26
|
+
heading-numbering: "1.a.i",
|
|
27
|
+
// Show an Open Access badge on the first page, and support open science, default is true, because that is what the default should be.
|
|
28
|
+
open-access: true,
|
|
29
|
+
// A list of keywords to display after the abstract
|
|
30
|
+
keywords: (),
|
|
31
|
+
// The "kind" of the content, e.g. "Original Research", this is shown as the title of the margin content on the first page.
|
|
32
|
+
kind: none,
|
|
33
|
+
// Content to put on the margin of the first page
|
|
34
|
+
// Should be a list of dicts with `title` and `content`
|
|
35
|
+
margin: (),
|
|
36
|
+
paper-size: "us-letter",
|
|
37
|
+
// A color for the theme of the document
|
|
38
|
+
theme: blue.darken(30%),
|
|
39
|
+
// Date published, for example, when you publish your preprint to an archive server.
|
|
40
|
+
// To hide the date, set this to `none`. You can also supply a list of dicts with `title` and `date`.
|
|
41
|
+
date: datetime.today(),
|
|
42
|
+
// Feel free to change this, the font applies to the whole document
|
|
43
|
+
font-face: none,
|
|
44
|
+
// The path to a bibliography file if you want to cite some external works.
|
|
45
|
+
bibliography-file: none,
|
|
46
|
+
bibliography-style: "apa",
|
|
47
|
+
// The paper's content.
|
|
48
|
+
body
|
|
49
|
+
) = {
|
|
50
|
+
let spacer = text(fill: gray)[#h(8pt) | #h(8pt)]
|
|
51
|
+
|
|
52
|
+
let dates;
|
|
53
|
+
if (type(date) == datetime) {
|
|
54
|
+
dates = ((title: "Published", date: date),)
|
|
55
|
+
} else if (type(date) == dictionary) {
|
|
56
|
+
dates = (date,)
|
|
57
|
+
} else {
|
|
58
|
+
dates = date
|
|
59
|
+
}
|
|
60
|
+
date = dates.at(0).date
|
|
61
|
+
|
|
62
|
+
// Create a short-citation, e.g. Cockett et al., 2023
|
|
63
|
+
let year = if (date != none) { ", " + date.display("[year]") }
|
|
64
|
+
if (short-citation == auto and authors.len() == 1) {
|
|
65
|
+
short-citation = authors.at(0).name.split(" ").last() + year
|
|
66
|
+
} else if (short-citation == auto and authors.len() == 2) {
|
|
67
|
+
short-citation = authors.at(0).name.split(" ").last() + " & " + authors.at(1).name.split(" ").last() + year
|
|
68
|
+
} else if (short-citation == auto and authors.len() > 2) {
|
|
69
|
+
short-citation = authors.at(0).name.split(" ").last() + " " + emph("et al.") + year
|
|
70
|
+
} else if (short-citation == auto) {
|
|
71
|
+
short-citation = none
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Set document metadata.
|
|
75
|
+
set document(title: title, author: authors.map(author => author.name))
|
|
76
|
+
|
|
77
|
+
show link: it => [#text(fill: theme)[#it]]
|
|
78
|
+
show ref: it => [#text(fill: theme)[#it]]
|
|
79
|
+
|
|
80
|
+
set page(
|
|
81
|
+
paper-size,
|
|
82
|
+
margin: (left: 25%),
|
|
83
|
+
header: context {
|
|
84
|
+
let loc = here()
|
|
85
|
+
if(loc.page() == 1) {
|
|
86
|
+
let headers = (
|
|
87
|
+
if (open-access) {smallcaps[Open Access]},
|
|
88
|
+
if (doi != none) { link("https://doi.org/" + doi, "https://doi.org/" + doi)}
|
|
89
|
+
)
|
|
90
|
+
return align(left, text(size: 8pt, fill: gray, headers.filter(header => header != none).join(spacer)))
|
|
91
|
+
} else {
|
|
92
|
+
return align(right, text(size: 8pt, fill: gray.darken(50%),
|
|
93
|
+
(short-title, short-citation).join(spacer)
|
|
94
|
+
))
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
footer: block(
|
|
98
|
+
width: 100%,
|
|
99
|
+
stroke: (top: 1pt + gray),
|
|
100
|
+
inset: (top: 8pt, right: 2pt),
|
|
101
|
+
context [
|
|
102
|
+
#grid(columns: (75%, 25%),
|
|
103
|
+
align(left, text(size: 9pt, fill: gray.darken(50%),
|
|
104
|
+
(
|
|
105
|
+
if(venue != none) {emph(venue)},
|
|
106
|
+
if(date != none) {date.display("[month repr:long] [day], [year]")}
|
|
107
|
+
).filter(t => t != none).join(spacer)
|
|
108
|
+
)),
|
|
109
|
+
align(right)[
|
|
110
|
+
#text(
|
|
111
|
+
size: 9pt, fill: gray.darken(50%)
|
|
112
|
+
)[
|
|
113
|
+
#counter(page).display() of #counter(page).final().first()
|
|
114
|
+
]
|
|
115
|
+
]
|
|
116
|
+
)
|
|
117
|
+
]
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
// Set the body font.
|
|
122
|
+
if (font-face != none) {
|
|
123
|
+
set text(font: font-face, size: 10pt)
|
|
124
|
+
} else {
|
|
125
|
+
set text(size: 10pt)
|
|
126
|
+
}
|
|
127
|
+
// Configure equation numbering and spacing.
|
|
128
|
+
set math.equation(numbering: "(1)")
|
|
129
|
+
show math.equation: set block(spacing: 1em)
|
|
130
|
+
|
|
131
|
+
// Configure lists.
|
|
132
|
+
set enum(indent: 10pt, body-indent: 9pt)
|
|
133
|
+
set list(indent: 10pt, body-indent: 9pt)
|
|
134
|
+
|
|
135
|
+
// Configure headings.
|
|
136
|
+
set heading(numbering: heading-numbering)
|
|
137
|
+
show heading: it => context {
|
|
138
|
+
let loc = here()
|
|
139
|
+
// Find out the final number of the heading counter.
|
|
140
|
+
let levels = counter(heading).at(loc)
|
|
141
|
+
set text(10pt, weight: 400)
|
|
142
|
+
if it.level == 1 [
|
|
143
|
+
// First-level headings are centered smallcaps.
|
|
144
|
+
// We don't want to number of the acknowledgment section.
|
|
145
|
+
#let is-ack = it.body in ([Acknowledgment], [Acknowledgement])
|
|
146
|
+
// #set align(center)
|
|
147
|
+
#set text(if is-ack { 10pt } else { 12pt })
|
|
148
|
+
#show: smallcaps
|
|
149
|
+
#v(20pt, weak: true)
|
|
150
|
+
#if it.numbering != none and not is-ack {
|
|
151
|
+
numbering(heading-numbering, ..levels)
|
|
152
|
+
[.]
|
|
153
|
+
h(7pt, weak: true)
|
|
154
|
+
}
|
|
155
|
+
#it.body
|
|
156
|
+
#v(13.75pt, weak: true)
|
|
157
|
+
] else if it.level == 2 [
|
|
158
|
+
// Second-level headings are run-ins.
|
|
159
|
+
#set par(first-line-indent: 0pt)
|
|
160
|
+
#set text(style: "italic")
|
|
161
|
+
#v(10pt, weak: true)
|
|
162
|
+
#if it.numbering != none {
|
|
163
|
+
numbering(heading-numbering, ..levels)
|
|
164
|
+
[.]
|
|
165
|
+
h(7pt, weak: true)
|
|
166
|
+
}
|
|
167
|
+
#it.body
|
|
168
|
+
#v(10pt, weak: true)
|
|
169
|
+
] else [
|
|
170
|
+
// Third level headings are run-ins too, but different.
|
|
171
|
+
#if it.level == 3 {
|
|
172
|
+
numbering(heading-numbering, ..levels)
|
|
173
|
+
[. ]
|
|
174
|
+
}
|
|
175
|
+
_#(it.body):_
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
if (logo != none) {
|
|
181
|
+
place(
|
|
182
|
+
top,
|
|
183
|
+
dx: -33%,
|
|
184
|
+
float: false,
|
|
185
|
+
box(
|
|
186
|
+
width: 27%,
|
|
187
|
+
{
|
|
188
|
+
if (type(logo) == content) {
|
|
189
|
+
logo
|
|
190
|
+
} else {
|
|
191
|
+
image(logo, width: 100%)
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
),
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
// Title and subtitle
|
|
200
|
+
box(inset: (bottom: 2pt), width: 100%, text(17pt, weight: "bold", fill: theme, title))
|
|
201
|
+
if subtitle != none {
|
|
202
|
+
parbreak()
|
|
203
|
+
box(width: 100%, text(14pt, fill: gray.darken(30%), subtitle))
|
|
204
|
+
}
|
|
205
|
+
// Authors and affiliations
|
|
206
|
+
if authors.len() > 0 {
|
|
207
|
+
box(inset: (y: 10pt), {
|
|
208
|
+
authors.map(author => {
|
|
209
|
+
text(11pt, weight: "semibold", author.name)
|
|
210
|
+
h(1pt)
|
|
211
|
+
if "affiliations" in author {
|
|
212
|
+
super(author.affiliations)
|
|
213
|
+
}
|
|
214
|
+
if "orcid" in author {
|
|
215
|
+
orcidLogo(orcid: author.orcid)
|
|
216
|
+
}
|
|
217
|
+
}).join(", ", last: ", and ")
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
if affiliations.len() > 0 {
|
|
221
|
+
box(inset: (bottom: 10pt), {
|
|
222
|
+
affiliations.map(affiliation => {
|
|
223
|
+
super(affiliation.id)
|
|
224
|
+
h(1pt)
|
|
225
|
+
affiliation.name
|
|
226
|
+
}).join(", ")
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
place(
|
|
232
|
+
left + bottom,
|
|
233
|
+
dx: -33%,
|
|
234
|
+
dy: -10pt,
|
|
235
|
+
box(width: 27%, {
|
|
236
|
+
if (kind != none) {
|
|
237
|
+
show par: set par(spacing: 0em)
|
|
238
|
+
text(11pt, fill: theme, weight: "semibold", smallcaps(kind))
|
|
239
|
+
parbreak()
|
|
240
|
+
}
|
|
241
|
+
if (dates != none) {
|
|
242
|
+
let formatted-dates
|
|
243
|
+
|
|
244
|
+
grid(columns: (40%, 60%), gutter: 7pt,
|
|
245
|
+
..dates.zip(range(dates.len())).map((formatted-dates) => {
|
|
246
|
+
let d = formatted-dates.at(0);
|
|
247
|
+
let i = formatted-dates.at(1);
|
|
248
|
+
let weight = "light"
|
|
249
|
+
if (i == 0) {
|
|
250
|
+
weight = "bold"
|
|
251
|
+
}
|
|
252
|
+
return (
|
|
253
|
+
text(size: 7pt, fill: theme, weight: weight, d.title),
|
|
254
|
+
text(size: 7pt, d.date.display("[month repr:short] [day], [year]"))
|
|
255
|
+
)
|
|
256
|
+
}).flatten()
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
v(2em)
|
|
260
|
+
grid(columns: 1, gutter: 2em, ..margin.map(side => {
|
|
261
|
+
text(size: 7pt, {
|
|
262
|
+
if ("title" in side) {
|
|
263
|
+
text(fill: theme, weight: "bold", side.title)
|
|
264
|
+
[\ ]
|
|
265
|
+
}
|
|
266
|
+
set enum(indent: 0.1em, body-indent: 0.25em)
|
|
267
|
+
set list(indent: 0.1em, body-indent: 0.25em)
|
|
268
|
+
side.content
|
|
269
|
+
})
|
|
270
|
+
}))
|
|
271
|
+
}),
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
let abstracts
|
|
276
|
+
if (type(abstract) == content or type(abstract) == str) {
|
|
277
|
+
abstracts = ((title: "Abstract", content: abstract),)
|
|
278
|
+
} else {
|
|
279
|
+
abstracts = abstract
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (abstracts != none and abstracts.len() > 0) {
|
|
283
|
+
box(inset: (top: 16pt, bottom: 16pt), width: 100%, stroke: (top: 1pt + gray, bottom: 1pt + gray), {
|
|
284
|
+
abstracts.map(abs => {
|
|
285
|
+
set par(justify: true)
|
|
286
|
+
text(fill: theme, weight: "semibold", size: 9pt, abs.title)
|
|
287
|
+
parbreak()
|
|
288
|
+
abs.content
|
|
289
|
+
}).join(parbreak())
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
if (keywords.len() > 0) {
|
|
293
|
+
text(size: 9pt, {
|
|
294
|
+
text(fill: theme, weight: "semibold", "Keywords")
|
|
295
|
+
h(8pt)
|
|
296
|
+
keywords.join(", ")
|
|
297
|
+
})
|
|
298
|
+
}
|
|
299
|
+
v(10pt)
|
|
300
|
+
|
|
301
|
+
show par: set par(spacing: 1.5em)
|
|
302
|
+
|
|
303
|
+
// Display the paper's contents.
|
|
304
|
+
body
|
|
305
|
+
|
|
306
|
+
if (bibliography-file != none) {
|
|
307
|
+
show bibliography: set text(8pt)
|
|
308
|
+
bibliography(bibliography-file, title: text(10pt, "References"), style: bibliography-style)
|
|
309
|
+
}
|
|
310
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#import "lapreprint.typ": *
|
|
2
|
+
#show: template.with(
|
|
3
|
+
title: "[-doc.title-]",
|
|
4
|
+
[# if parts.abstract or parts.summary #]
|
|
5
|
+
abstract: (
|
|
6
|
+
[# if parts.abstract #]
|
|
7
|
+
(
|
|
8
|
+
title: "Abstract",
|
|
9
|
+
content: [
|
|
10
|
+
[-parts.abstract-]
|
|
11
|
+
]
|
|
12
|
+
),
|
|
13
|
+
[# endif #]
|
|
14
|
+
[# if parts.summary #]
|
|
15
|
+
(
|
|
16
|
+
title: "Plain Language Summary",
|
|
17
|
+
content: [
|
|
18
|
+
[-parts.summary-]
|
|
19
|
+
]
|
|
20
|
+
),
|
|
21
|
+
[# endif #]
|
|
22
|
+
),
|
|
23
|
+
[# endif #]
|
|
24
|
+
[# if doc.subtitle #]
|
|
25
|
+
subtitle: "[-doc.subtitle-]",
|
|
26
|
+
[# endif #]
|
|
27
|
+
[# if doc.short_title #]
|
|
28
|
+
short-title: "[-doc.short_title-]",
|
|
29
|
+
[# endif #]
|
|
30
|
+
[# if options.short_citation #]
|
|
31
|
+
short-citation: "[-options.short_citation-]",
|
|
32
|
+
[# endif #]
|
|
33
|
+
[# if options.heading_numbering #]
|
|
34
|
+
heading-numbering: "[-options.heading_numbering-]",
|
|
35
|
+
[# endif #]
|
|
36
|
+
[# if options.theme #]
|
|
37
|
+
theme: [-options.theme-],
|
|
38
|
+
[# endif #]
|
|
39
|
+
[# if doc.open_access !== undefined #]
|
|
40
|
+
open-access: [-doc.open_access-],
|
|
41
|
+
[# endif #]
|
|
42
|
+
[# if doc.doi #]
|
|
43
|
+
doi: "[-doc.doi-]",
|
|
44
|
+
[# endif #]
|
|
45
|
+
[# if doc.date #]
|
|
46
|
+
date: datetime(
|
|
47
|
+
year: [-doc.date.year-],
|
|
48
|
+
month: [-doc.date.month-],
|
|
49
|
+
day: [-doc.date.day-],
|
|
50
|
+
),
|
|
51
|
+
[# endif #]
|
|
52
|
+
[# if doc.keywords #]
|
|
53
|
+
keywords: (
|
|
54
|
+
[#- for keyword in doc.keywords -#]"[-keyword-]",[#- endfor -#]
|
|
55
|
+
),
|
|
56
|
+
[# endif #]
|
|
57
|
+
[# if doc.bibtex #]
|
|
58
|
+
bibliography-file: "[-doc.bibtex-]",
|
|
59
|
+
[# endif #]
|
|
60
|
+
authors: (
|
|
61
|
+
[# for author in doc.authors #]
|
|
62
|
+
(
|
|
63
|
+
name: "[-author.name-]",
|
|
64
|
+
[# if author.orcid #]
|
|
65
|
+
orcid: "[-author.orcid-]",
|
|
66
|
+
[# endif #]
|
|
67
|
+
[# if author.affiliations #]
|
|
68
|
+
affiliations: "[#- for aff in author.affiliations -#][-aff.index-][#- if not loop.last -#],[#- endif -#][#- endfor -#]",
|
|
69
|
+
[# endif #]
|
|
70
|
+
),
|
|
71
|
+
[# endfor #]
|
|
72
|
+
),
|
|
73
|
+
affiliations: (
|
|
74
|
+
[# for aff in doc.affiliations #]
|
|
75
|
+
(
|
|
76
|
+
id: "[-aff.index-]",
|
|
77
|
+
name: "[-aff.name-]",
|
|
78
|
+
),
|
|
79
|
+
[# endfor #]
|
|
80
|
+
),
|
|
81
|
+
[# if doc.venue.title #]
|
|
82
|
+
venue: "[-doc.venue.title-]",
|
|
83
|
+
[# endif #]
|
|
84
|
+
[# if options.logo #]
|
|
85
|
+
logo: "[-options.logo-]",
|
|
86
|
+
[# endif #]
|
|
87
|
+
[# if options.kind #]
|
|
88
|
+
kind: "[-options.kind-]",
|
|
89
|
+
[# endif #]
|
|
90
|
+
margin: (
|
|
91
|
+
[# if parts.acknowledgements #]
|
|
92
|
+
(
|
|
93
|
+
title: "Acknowledgements",
|
|
94
|
+
content: [
|
|
95
|
+
[-parts.acknowledgements-]
|
|
96
|
+
],
|
|
97
|
+
),
|
|
98
|
+
[# endif #]
|
|
99
|
+
[# if parts.availability #]
|
|
100
|
+
(
|
|
101
|
+
title: "Data Availability",
|
|
102
|
+
content: [
|
|
103
|
+
[-parts.availability-]
|
|
104
|
+
],
|
|
105
|
+
),
|
|
106
|
+
[# endif #]
|
|
107
|
+
),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
[-IMPORTS-]
|
|
111
|
+
|
|
112
|
+
[-CONTENT-]
|