@anja.codes/vitakit 0.1.0 → 0.3.0
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/README.md +52 -131
- package/dist/check.mjs +20 -11
- package/dist/chunks/authoring.mjs +372 -0
- package/dist/{shared/vitakit.CypCF-4Q.mjs → chunks/bibliography.mjs} +74 -237
- package/dist/chunks/build.mjs +178 -0
- package/dist/chunks/documents.mjs +152 -0
- package/dist/chunks/draft.mjs +222 -0
- package/dist/chunks/eject.mjs +59 -0
- package/dist/chunks/emit.mjs +216 -0
- package/dist/chunks/eta.mjs +16 -0
- package/dist/chunks/fields.mjs +79 -0
- package/dist/chunks/inventory.mjs +201 -0
- package/dist/chunks/migrations.mjs +218 -0
- package/dist/chunks/plan.mjs +172 -0
- package/dist/chunks/prompt.mjs +33 -0
- package/dist/chunks/publication-lists.mjs +202 -0
- package/dist/chunks/schema-source.mjs +12 -0
- package/dist/chunks/schemas.mjs +109 -0
- package/dist/chunks/sections.mjs +109 -0
- package/dist/chunks/snippets.mjs +199 -0
- package/dist/chunks/validate.mjs +133 -0
- package/dist/chunks/vue.mjs +153 -0
- package/dist/chunks/yaml.mjs +62 -0
- package/dist/cli.d.mts +33 -40
- package/dist/cli.mjs +7 -224
- package/dist/index.d.mts +379 -49
- package/dist/index.mjs +15 -7
- package/dist/nuxt.mjs +48 -10
- package/dist/runtime/components/VitaPublications.vue +21 -22
- package/dist/runtime/components/VitaSection.vue +94 -38
- package/dist/runtime/composables/useVitakit.ts +14 -3
- package/dist/schemas/education.ts +4 -4
- package/dist/schemas/employment.ts +3 -3
- package/dist/schemas/publications.ts +10 -8
- package/dist/schemas/supervision.ts +3 -3
- package/dist/schemas/talks.ts +1 -1
- package/dist/schemas/teaching.ts +3 -3
- package/dist/shared/index.d.mts +60 -9
- package/dist/shared/index.mjs +3 -57
- package/dist/shared/{vitakit.D8WRg_Xx.d.mts → vitakit.BfR_mUHm.d.mts} +25 -12
- package/dist/shared/vitakit.CCTDg7pz.mjs +199 -0
- package/dist/shared/vitakit.CfB3hMHl.mjs +60 -0
- package/dist/shared/{vitakit.BlU-p01e.mjs → vitakit.CtChKO0I.mjs} +1 -1
- package/dist/shared/vitakit.D-DdJaDZ.mjs +20 -0
- package/dist/shared/vitakit.D10HyDhF.mjs +19 -0
- package/dist/shared/vitakit.DCK90zq4.mjs +67 -0
- package/dist/shared/vitakit.DQ6PmGoL.mjs +34 -0
- package/dist/shared/vitakit.DcQOdM4I.mjs +57 -0
- package/dist/shared/vitakit.DteA9vLy.mjs +135 -0
- package/dist/shared/vitakit.DvABWuUc.mjs +121 -0
- package/dist/shared/vitakit.DvvUYUTS.mjs +200 -0
- package/dist/shared/vitakit.TSMTC5Jl.mjs +6 -0
- package/dist/shared/vitakit.UfoXdL7U.mjs +22 -0
- package/dist/shared/vitakit.zwr1ssO5.mjs +1850 -0
- package/dist/tex/templates/_generic.eta +6 -4
- package/dist/tex/templates/education.eta +10 -5
- package/dist/tex/templates/employment.eta +12 -5
- package/dist/tex/templates/publications.eta +7 -3
- package/dist/tex/templates/supervision.eta +10 -2
- package/dist/tex/templates/talks.eta +5 -1
- package/dist/tex/templates/teaching.eta +6 -4
- package/dist/tex/vitakit-cv.cls +36 -10
- package/dist/tex.d.mts +80 -11
- package/dist/tex.mjs +27 -117
- package/package.json +10 -7
- package/src/bin/vitakit.mjs +3 -153
- package/dist/shared/vitakit.BRhjqpAt.mjs +0 -250
- package/dist/shared/vitakit.BxcnlFGP.mjs +0 -325
- package/src/bin/vitakit-build-cv.mjs +0 -50
- package/src/bin/vitakit-check.mjs +0 -57
package/dist/nuxt.mjs
CHANGED
|
@@ -1,26 +1,58 @@
|
|
|
1
1
|
import { defineNuxtModule, createResolver, addTemplate, addTypeTemplate, addImportsDir, addComponentsDir, addPrerenderRoutes, updateTemplates } from '@nuxt/kit';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { l as loadContent } from './shared/vitakit.
|
|
4
|
-
import '
|
|
5
|
-
import '
|
|
3
|
+
import { l as loadContent } from './shared/vitakit.DteA9vLy.mjs';
|
|
4
|
+
import { f as fail } from './shared/vitakit.DCK90zq4.mjs';
|
|
5
|
+
import { b as findWebPlacements, p as publicationPlacementProblems } from './shared/vitakit.DvvUYUTS.mjs';
|
|
6
|
+
import { readdir } from 'node:fs/promises';
|
|
7
|
+
import { isAbsolute, resolve, relative } from 'node:path';
|
|
6
8
|
import 'gray-matter';
|
|
7
9
|
import 'yaml';
|
|
10
|
+
import './chunks/bibliography.mjs';
|
|
11
|
+
import 'node:fs';
|
|
12
|
+
import './shared/vitakit.DQ6PmGoL.mjs';
|
|
13
|
+
import 'node:url';
|
|
14
|
+
import '@behackl/citation-js-extras';
|
|
8
15
|
import 'unified';
|
|
16
|
+
import 'rehype-parse';
|
|
17
|
+
import 'rehype-sanitize';
|
|
18
|
+
import 'rehype-stringify';
|
|
9
19
|
import 'remark-parse';
|
|
10
20
|
import 'remark-gfm';
|
|
11
21
|
import 'remark-math';
|
|
12
22
|
import 'remark-rehype';
|
|
13
23
|
import 'rehype-katex';
|
|
14
24
|
import 'rehype-raw';
|
|
15
|
-
import 'rehype-stringify';
|
|
16
25
|
import 'unist-util-visit';
|
|
17
|
-
import './shared/vitakit.BxcnlFGP.mjs';
|
|
18
|
-
import 'node:fs';
|
|
19
|
-
import 'node:url';
|
|
20
26
|
import 'zod';
|
|
21
|
-
import '
|
|
22
|
-
import '
|
|
23
|
-
import '
|
|
27
|
+
import './chunks/publication-lists.mjs';
|
|
28
|
+
import './shared/vitakit.D10HyDhF.mjs';
|
|
29
|
+
import './chunks/sections.mjs';
|
|
30
|
+
import './chunks/schemas.mjs';
|
|
31
|
+
import './shared/vitakit.DvABWuUc.mjs';
|
|
32
|
+
import './shared/vitakit.TSMTC5Jl.mjs';
|
|
33
|
+
|
|
34
|
+
function isWithin(directory, path) {
|
|
35
|
+
const child = relative(directory, path);
|
|
36
|
+
return child === "" || !isAbsolute(child) && child.split(/[/\\]/)[0] !== "..";
|
|
37
|
+
}
|
|
38
|
+
async function watchComponentOverrides(nuxt, packagedDir) {
|
|
39
|
+
const names = new Set((await readdir(packagedDir)).filter((file) => file.endsWith(".vue")).map((file) => file.slice(0, -4)));
|
|
40
|
+
let previous;
|
|
41
|
+
let restarting = false;
|
|
42
|
+
nuxt.hook("components:extend", async (components) => {
|
|
43
|
+
const overrides = new Set(components.filter((component) => names.has(component.pascalName) && isAbsolute(component.filePath) && !isWithin(packagedDir, component.filePath) && !component.filePath.replace(/\\/g, "/").split("/").includes("node_modules") && (isWithin(nuxt.options.srcDir, component.filePath) || isWithin(nuxt.options.rootDir, component.filePath))).map((component) => resolve(component.filePath)));
|
|
44
|
+
const before = previous;
|
|
45
|
+
previous = overrides;
|
|
46
|
+
if (!before || restarting || before.size === overrides.size && [...before].every((path) => overrides.has(path))) return;
|
|
47
|
+
restarting = true;
|
|
48
|
+
try {
|
|
49
|
+
await nuxt.callHook("restart");
|
|
50
|
+
} catch (error) {
|
|
51
|
+
restarting = false;
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
24
56
|
|
|
25
57
|
function katexStylesheet() {
|
|
26
58
|
const renderer = createRequire(createRequire(import.meta.url).resolve("rehype-katex"));
|
|
@@ -54,6 +86,11 @@ const module$1 = defineNuxtModule({
|
|
|
54
86
|
});
|
|
55
87
|
let content = await load(!nuxt.options.dev);
|
|
56
88
|
report(content);
|
|
89
|
+
if (!nuxt.options.dev && !nuxt.options._prepare) {
|
|
90
|
+
const { publications } = await findWebPlacements(rootDir);
|
|
91
|
+
const [problem] = publicationPlacementProblems(publications, [], Object.keys(content.publications));
|
|
92
|
+
if (problem) fail(problem.where, problem.message);
|
|
93
|
+
}
|
|
57
94
|
nuxt.options.alias["#vitakit/content"] = `${nuxt.options.buildDir}/${TEMPLATE}`;
|
|
58
95
|
addTemplate({
|
|
59
96
|
filename: TEMPLATE,
|
|
@@ -78,6 +115,7 @@ const module$1 = defineNuxtModule({
|
|
|
78
115
|
await addComponentsDir({ path: resolver.resolve("./runtime/components"), prefix: "Vita", pathPrefix: false });
|
|
79
116
|
addPrerenderRoutes(content.pages.map((page) => page.route));
|
|
80
117
|
if (!nuxt.options.dev) return;
|
|
118
|
+
await watchComponentOverrides(nuxt, resolver.resolve("./runtime/components"));
|
|
81
119
|
nuxt.options.watch.push("../content", "../schemas");
|
|
82
120
|
nuxt.hook("builder:watch", async (_event, path) => {
|
|
83
121
|
if (!/[/\\](content|schemas)[/\\]/.test(path)) return;
|
|
@@ -1,36 +1,35 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* A publication list from `content/publication-lists.yml`, formatted from
|
|
4
|
+
* `content/publications.bib` at build time.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Which publications, in what order and in which groups is the list's
|
|
7
|
+
* business; this component decides where it appears and under which heading.
|
|
8
|
+
* Each entry is a `VitaPublication`, which can be ejected on its own to change
|
|
9
|
+
* how a reference looks. Nothing is shown for an empty list, heading included.
|
|
9
10
|
*/
|
|
10
11
|
import { computed } from 'vue'
|
|
11
|
-
import {
|
|
12
|
+
import { usePublicationList } from '#imports'
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
headings?: boolean
|
|
20
|
-
}>(), { groups: undefined, headings: true })
|
|
14
|
+
const props = defineProps<{
|
|
15
|
+
/** The list's name in `content/publication-lists.yml`. */
|
|
16
|
+
list: string
|
|
17
|
+
/** A heading above the list. */
|
|
18
|
+
title?: string
|
|
19
|
+
}>()
|
|
21
20
|
|
|
22
|
-
const
|
|
23
|
-
const shown = computed(() => props.groups
|
|
24
|
-
? props.groups.map(id => all.find(group => group.id === id)).filter(group => group !== undefined)
|
|
25
|
-
: all)
|
|
21
|
+
const publications = computed(() => usePublicationList(props.list))
|
|
26
22
|
</script>
|
|
27
23
|
|
|
28
24
|
<template>
|
|
29
|
-
<div class="vita-publications">
|
|
30
|
-
<
|
|
31
|
-
|
|
25
|
+
<div v-if="publications.count" class="vita-publications">
|
|
26
|
+
<h2 v-if="title !== undefined" class="vita-publications__title">
|
|
27
|
+
{{ title }}
|
|
28
|
+
</h2>
|
|
29
|
+
<section v-for="group in publications.groups" :key="group.id" class="vita-publications__group">
|
|
30
|
+
<component :is="title !== undefined ? 'h3' : 'h2'" v-if="group.label !== undefined" class="vita-publications__heading">
|
|
32
31
|
{{ group.label }}
|
|
33
|
-
</
|
|
32
|
+
</component>
|
|
34
33
|
<ol class="vita-bib csl-bib-body">
|
|
35
34
|
<VitaPublication v-for="entry in group.entries" :key="entry.key" :entry="entry" />
|
|
36
35
|
</ol>
|
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* A generic section with conventional field selection and optional overrides.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* title/meta/note slots replace those regions; the default slot replaces the
|
|
6
|
+
* whole entry body. The list, row and date column remain owned by vitakit.
|
|
7
|
+
*
|
|
8
|
+
* With `title`, the list comes with its heading, in a <section> carrying the
|
|
9
|
+
* section id as anchor, and neither appears when there are no entries -- the
|
|
10
|
+
* website's `\vitasection[Title]{id}`.
|
|
9
11
|
*/
|
|
10
|
-
import { computed } from 'vue'
|
|
12
|
+
import { computed, type VNodeChild } from 'vue'
|
|
11
13
|
import { useSection } from '#imports'
|
|
12
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
entrySecondaryParts,
|
|
16
|
+
entryTitleField,
|
|
17
|
+
filterWhere,
|
|
18
|
+
richHtml,
|
|
19
|
+
type DateRange,
|
|
20
|
+
type Entry,
|
|
21
|
+
type VitaSectionSlotProps,
|
|
22
|
+
type Where,
|
|
23
|
+
} from '@anja.codes/vitakit/shared'
|
|
13
24
|
|
|
14
25
|
const props = defineProps<{
|
|
15
26
|
/** Section id; ignored when `entries` is given. */
|
|
@@ -17,44 +28,89 @@ const props = defineProps<{
|
|
|
17
28
|
entries?: Entry[]
|
|
18
29
|
/** Fields to try, in order, for the main line. */
|
|
19
30
|
titleFields?: string[]
|
|
31
|
+
/** Only entries whose fields have these values, as `where` in a PDF document. */
|
|
32
|
+
where?: Where
|
|
20
33
|
limit?: number
|
|
34
|
+
/** A heading; the list is then wrapped in a <section> with the id as anchor. */
|
|
35
|
+
title?: string
|
|
21
36
|
}>()
|
|
22
37
|
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// `@anja.codes/vitakit/shared`, the same code the LaTeX emitter uses; only the
|
|
30
|
-
// separator and the markup are decided here.
|
|
31
|
-
const titleFieldOf = (entry: Entry): string | undefined =>
|
|
32
|
-
entryTitleField(entry, props.titleFields)
|
|
33
|
-
|
|
34
|
-
const titleOf = (entry: Entry): string => {
|
|
35
|
-
const field = titleFieldOf(entry)
|
|
36
|
-
return (field && richHtml(entry[field])) || ''
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const secondaryOf = (entry: Entry): string =>
|
|
40
|
-
entrySecondaryParts(entry, titleFieldOf(entry))
|
|
41
|
-
.map(part => (part.kind === 'institution' ? part.institution.name : part.text))
|
|
42
|
-
.join(' · ')
|
|
38
|
+
const slots = defineSlots<{
|
|
39
|
+
title?(props: VitaSectionSlotProps): VNodeChild
|
|
40
|
+
meta?(props: VitaSectionSlotProps): VNodeChild
|
|
41
|
+
note?(props: VitaSectionSlotProps): VNodeChild
|
|
42
|
+
default?(props: VitaSectionSlotProps): VNodeChild
|
|
43
|
+
}>()
|
|
43
44
|
|
|
44
|
-
const
|
|
45
|
-
|
|
45
|
+
const rows = computed(() => {
|
|
46
|
+
const source = filterWhere(props.entries ?? (props.id ? useSection(props.id) : []), props.where)
|
|
47
|
+
const items = props.limit ? source.slice(0, props.limit) : source
|
|
48
|
+
return items.map((entry, index): VitaSectionSlotProps => {
|
|
49
|
+
// Field selection remains shared with the emitter; only the web-specific
|
|
50
|
+
// presentation (such as a term replacing the date) is decided here.
|
|
51
|
+
const titleField = entryTitleField(entry, props.titleFields)
|
|
52
|
+
const term = typeof entry.term === 'string' && entry.term.length > 0 ? entry.term : undefined
|
|
53
|
+
return {
|
|
54
|
+
entry,
|
|
55
|
+
index,
|
|
56
|
+
display: {
|
|
57
|
+
titleHtml: (titleField && richHtml(entry[titleField])) || '',
|
|
58
|
+
titleUrl: typeof entry.url === 'string' ? entry.url : undefined,
|
|
59
|
+
date: entry.date as DateRange | undefined,
|
|
60
|
+
term,
|
|
61
|
+
meta: entrySecondaryParts(term ? { ...entry, term: undefined } : entry, titleField),
|
|
62
|
+
note: typeof entry.note === 'string' ? entry.note : undefined,
|
|
63
|
+
links: typeof entry.slides === 'string' && entry.slides ? [{ label: 'Slides', url: entry.slides }] : [],
|
|
64
|
+
ongoing: entry.ongoing === true,
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
})
|
|
46
69
|
</script>
|
|
47
70
|
|
|
48
71
|
<template>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
<!-- With a title: the same component without one, inside its section. -->
|
|
73
|
+
<section v-if="title !== undefined && rows.length" :id="id" class="vita-section-group">
|
|
74
|
+
<h2 class="vita-section-group__title">
|
|
75
|
+
{{ title }}
|
|
76
|
+
</h2>
|
|
77
|
+
<VitaSection :entries="rows.map(row => row.entry)" :title-fields="titleFields">
|
|
78
|
+
<template v-for="(_, name) in slots" #[name]="scope">
|
|
79
|
+
<slot :name="name" v-bind="scope" />
|
|
80
|
+
</template>
|
|
81
|
+
</VitaSection>
|
|
82
|
+
</section>
|
|
83
|
+
<ul v-else-if="title === undefined && rows.length" class="vita-section">
|
|
84
|
+
<VitaEntry v-for="row in rows" :key="row.entry.id" :date="row.display.date">
|
|
85
|
+
<template v-if="row.display.term" #date>{{ row.display.term }}</template>
|
|
86
|
+
<!-- Check presence explicitly: an intentionally empty slot must not
|
|
87
|
+
bring back Vue's slot fallback (or an empty default wrapper). -->
|
|
88
|
+
<slot v-if="slots.default" v-bind="row" />
|
|
89
|
+
<template v-else>
|
|
90
|
+
<slot v-if="slots.title" name="title" v-bind="row" />
|
|
91
|
+
<p v-else class="vita-entry__title">
|
|
92
|
+
<!-- eslint-disable vue/no-v-html -- build-time rendered Markdown -->
|
|
93
|
+
<a v-if="row.display.titleUrl" :href="row.display.titleUrl" v-html="row.display.titleHtml" />
|
|
94
|
+
<span v-else v-html="row.display.titleHtml" />
|
|
95
|
+
<span v-if="row.display.ongoing" class="vita-entry__status"> (in progress)</span>
|
|
96
|
+
</p>
|
|
97
|
+
<slot v-if="slots.meta" name="meta" v-bind="row" />
|
|
98
|
+
<p v-else-if="row.display.meta.length" class="vita-entry__meta">
|
|
99
|
+
<template v-for="(part, index) in row.display.meta" :key="index">
|
|
100
|
+
<template v-if="index"> · </template>
|
|
101
|
+
<template v-if="part.kind === 'linked'">
|
|
102
|
+
<a v-if="part.linked.url" :href="part.linked.url">{{ part.linked.label }}</a>
|
|
103
|
+
<template v-else>{{ part.linked.label }}</template>
|
|
104
|
+
</template>
|
|
105
|
+
<template v-else>{{ part.text }}</template>
|
|
106
|
+
</template>
|
|
107
|
+
</p>
|
|
108
|
+
<slot v-if="slots.note" name="note" v-bind="row" />
|
|
109
|
+
<p v-else-if="row.display.note !== undefined" class="vita-entry__note">{{ row.display.note }}</p>
|
|
110
|
+
<p v-if="row.display.links.length" class="vita-entry__links">
|
|
111
|
+
<a v-for="link in row.display.links" :key="link.url" :href="link.url">{{ link.label }}</a>
|
|
112
|
+
</p>
|
|
113
|
+
</template>
|
|
58
114
|
</VitaEntry>
|
|
59
115
|
</ul>
|
|
60
116
|
</template>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* chunk.
|
|
7
7
|
*/
|
|
8
8
|
import content from '#vitakit/content'
|
|
9
|
-
import type { Entry, Page, Profile,
|
|
9
|
+
import type { Entry, Page, Profile, PublicationList, VitakitContent } from '@anja.codes/vitakit/shared'
|
|
10
10
|
|
|
11
11
|
export function useVitakit(): VitakitContent {
|
|
12
12
|
return content
|
|
@@ -32,8 +32,19 @@ export function useSectionIds(): string[] {
|
|
|
32
32
|
return Object.keys(content.sections)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
/**
|
|
36
|
+
* A list declared in `content/publication-lists.yml`, formatted at build time.
|
|
37
|
+
*
|
|
38
|
+
* Unlike a section, an unknown name is an error: a list is declared on
|
|
39
|
+
* purpose, so a name that matches none is a typo. Builds report it before
|
|
40
|
+
* this runs.
|
|
41
|
+
*/
|
|
42
|
+
export function usePublicationList(name: string): PublicationList {
|
|
43
|
+
if (Object.hasOwn(content.publications, name)) return content.publications[name]!
|
|
44
|
+
const names = Object.keys(content.publications)
|
|
45
|
+
throw new Error(name
|
|
46
|
+
? `no publication list "${name}" in content/publication-lists.yml; the lists are ${names.join(', ') || '(none)'}`
|
|
47
|
+
: 'VitaPublications needs a list, e.g. <VitaPublications list="all" />; the lists are in content/publication-lists.yml')
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
/** The page for a route, or `undefined`. Routes always end with a slash. */
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shape of `content/education.yml`.
|
|
3
3
|
*
|
|
4
|
-
* Also the file `vitakit
|
|
4
|
+
* Also the file `vitakit customize education --schema --copy` writes into a project, with the import
|
|
5
5
|
* rewritten to `@anja.codes/vitakit`; it is therefore the single definition of
|
|
6
6
|
* this section, not a copy of one.
|
|
7
7
|
*/
|
|
8
|
-
import { dateRange,
|
|
8
|
+
import { dateRange, linked, richText, section, text, url, z } from '../primitives'
|
|
9
9
|
|
|
10
10
|
export default section({
|
|
11
11
|
date: dateRange,
|
|
12
12
|
degree: text,
|
|
13
|
-
institution,
|
|
13
|
+
institution: linked,
|
|
14
14
|
thesis: z.strictObject({
|
|
15
15
|
title: richText,
|
|
16
16
|
type: text.optional(),
|
|
17
|
-
supervisor:
|
|
17
|
+
supervisor: linked.optional(),
|
|
18
18
|
url: url.optional(),
|
|
19
19
|
}).optional(),
|
|
20
20
|
note: text.optional(),
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shape of `content/employment.yml`.
|
|
3
3
|
*
|
|
4
|
-
* Also the file `vitakit
|
|
4
|
+
* Also the file `vitakit customize employment --schema --copy` writes into a project, with the import
|
|
5
5
|
* rewritten to `@anja.codes/vitakit`; it is therefore the single definition of
|
|
6
6
|
* this section, not a copy of one.
|
|
7
7
|
*/
|
|
8
|
-
import { dateRange,
|
|
8
|
+
import { dateRange, linked, richText, section, text, url, z } from '../primitives'
|
|
9
9
|
|
|
10
10
|
export default section({
|
|
11
11
|
date: dateRange,
|
|
12
12
|
position: text,
|
|
13
|
-
institution,
|
|
13
|
+
institution: linked,
|
|
14
14
|
project: z.strictObject({
|
|
15
15
|
title: richText,
|
|
16
16
|
funder: text.optional(),
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* `vitakit
|
|
2
|
+
* The statuses of `content/publications.bib`, and how references are linked
|
|
3
|
+
* -- on the website, in `vitakit verify` and in the PDF alike. Which
|
|
4
|
+
* publications a list shows is decided in `content/publication-lists.yml`.
|
|
4
5
|
*
|
|
5
|
-
* Also the file `vitakit
|
|
6
|
+
* Also the file `vitakit customize publications` writes into a project, with the
|
|
6
7
|
* import rewritten to `@anja.codes/vitakit`; it is therefore the single definition
|
|
7
8
|
* of the defaults, not a copy of them. Every setting is optional: leave out
|
|
8
9
|
* what you do not change.
|
|
@@ -11,10 +12,11 @@ import { badgePresets, publications } from '../publications'
|
|
|
11
12
|
|
|
12
13
|
export default publications({
|
|
13
14
|
/**
|
|
14
|
-
* The values of the `status` field,
|
|
15
|
-
*
|
|
15
|
+
* The values of the `status` field, with the heading of their group, in
|
|
16
|
+
* the order the groups appear in a list grouped by status. A status that
|
|
17
|
+
* is not listed here is an error, in any capitalisation.
|
|
16
18
|
*/
|
|
17
|
-
|
|
19
|
+
statuses: [
|
|
18
20
|
{ id: 'published', label: 'Published' },
|
|
19
21
|
{ id: 'accepted', label: 'Accepted' },
|
|
20
22
|
{ id: 'preprint', label: 'Preprints' },
|
|
@@ -22,8 +24,8 @@ export default publications({
|
|
|
22
24
|
{ id: 'other', label: 'Other' },
|
|
23
25
|
],
|
|
24
26
|
|
|
25
|
-
/** The
|
|
26
|
-
|
|
27
|
+
/** The status of an entry without one; the first status if left out. */
|
|
28
|
+
defaultStatus: 'published',
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
31
|
* BibTeX fields that become links after a reference on the website. The
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shape of `content/supervision.yml`.
|
|
3
3
|
*
|
|
4
|
-
* Also the file `vitakit
|
|
4
|
+
* Also the file `vitakit customize supervision --schema --copy` writes into a project, with the import
|
|
5
5
|
* rewritten to `@anja.codes/vitakit`; it is therefore the single definition of
|
|
6
6
|
* this section, not a copy of one.
|
|
7
7
|
*/
|
|
8
|
-
import { dateRange,
|
|
8
|
+
import { dateRange, linked, richText, section, text, z } from '../primitives'
|
|
9
9
|
|
|
10
10
|
export default section({
|
|
11
11
|
date: dateRange,
|
|
@@ -15,6 +15,6 @@ export default section({
|
|
|
15
15
|
/** Markdown, math allowed. */
|
|
16
16
|
title: richText,
|
|
17
17
|
degree: text.optional(),
|
|
18
|
-
institution:
|
|
18
|
+
institution: linked.optional(),
|
|
19
19
|
coSupervisor: text.optional(),
|
|
20
20
|
})
|
package/dist/schemas/talks.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shape of `content/talks.yml`.
|
|
3
3
|
*
|
|
4
|
-
* Also the file `vitakit
|
|
4
|
+
* Also the file `vitakit customize talks --schema --copy` writes into a project, with the import
|
|
5
5
|
* rewritten to `@anja.codes/vitakit`; it is therefore the single definition of
|
|
6
6
|
* this section, not a copy of one.
|
|
7
7
|
*/
|
package/dist/schemas/teaching.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shape of `content/teaching.yml`.
|
|
3
3
|
*
|
|
4
|
-
* Also the file `vitakit
|
|
4
|
+
* Also the file `vitakit customize teaching --schema --copy` writes into a project, with the import
|
|
5
5
|
* rewritten to `@anja.codes/vitakit`; it is therefore the single definition of
|
|
6
6
|
* this section, not a copy of one.
|
|
7
7
|
*/
|
|
8
|
-
import { dateRange,
|
|
8
|
+
import { dateRange, linked, richText, section, text, url } from '../primitives'
|
|
9
9
|
|
|
10
10
|
export default section({
|
|
11
11
|
date: dateRange,
|
|
12
12
|
title: richText,
|
|
13
13
|
role: text.optional(),
|
|
14
|
-
institution:
|
|
14
|
+
institution: linked.optional(),
|
|
15
15
|
/** e.g. `2024S`, `Winter 2023/24` -- free text, shown as written. */
|
|
16
16
|
term: text.optional(),
|
|
17
17
|
url: url.optional(),
|
package/dist/shared/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as DateRange,
|
|
2
|
-
export { C as ContentIssue, b as DatePoint, c as EntryBase,
|
|
1
|
+
import { D as DateRange, d as Linked, L as Link, E as Entry } from './vitakit.BfR_mUHm.mjs';
|
|
2
|
+
export { C as ContentIssue, b as DatePoint, c as EntryBase, P as Page, e as Profile, f as Publication, g as PublicationBadge, h as PublicationGroup, i as PublicationList, j as RichText, R as RichTextMarker, V as Visibility, a as VitakitContent } from './vitakit.BfR_mUHm.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Presentation helpers shared by the website and the LaTeX side.
|
|
@@ -37,32 +37,83 @@ declare function dateSortKey(range: DateRange | undefined): string;
|
|
|
37
37
|
declare const TITLE_FIELDS: readonly string[];
|
|
38
38
|
/** Plain-text fields tried, in order, for the context line. */
|
|
39
39
|
declare const SECONDARY_FIELDS: readonly string[];
|
|
40
|
-
/**
|
|
41
|
-
declare const
|
|
40
|
+
/** `linked` fields tried, in order, for the place on the context line. */
|
|
41
|
+
declare const LINKED_FIELDS: readonly string[];
|
|
42
42
|
/**
|
|
43
43
|
* The field holding the entry's headline, or `undefined` when nothing fits.
|
|
44
44
|
* Returns the field *name* so callers can render its value themselves: the
|
|
45
45
|
* website needs the rendered HTML, the emitter the LaTeX source.
|
|
46
46
|
*/
|
|
47
47
|
declare function entryTitleField(entry: Entry, fields?: readonly string[]): string | undefined;
|
|
48
|
-
/** A piece of the context line: plain text, or
|
|
48
|
+
/** A piece of the context line: plain text, or a `linked` value that may link. */
|
|
49
49
|
type SecondaryPart = {
|
|
50
50
|
kind: 'text';
|
|
51
51
|
text: string;
|
|
52
52
|
} | {
|
|
53
|
-
kind: '
|
|
54
|
-
|
|
53
|
+
kind: 'linked';
|
|
54
|
+
linked: Linked;
|
|
55
55
|
};
|
|
56
|
+
/** Values used by VitaSection's default web rendering, also available to slots. */
|
|
57
|
+
interface VitaSectionDisplay {
|
|
58
|
+
/** Build-time rendered HTML, respecting the component's titleFields. */
|
|
59
|
+
titleHtml: string;
|
|
60
|
+
titleUrl?: string;
|
|
61
|
+
date?: DateRange;
|
|
62
|
+
/** A teaching term replaces the normal date label. */
|
|
63
|
+
term?: string;
|
|
64
|
+
/** Structured context, preserving links. */
|
|
65
|
+
meta: SecondaryPart[];
|
|
66
|
+
note?: string;
|
|
67
|
+
/** Additional links; does not include the title's link. */
|
|
68
|
+
links: Link[];
|
|
69
|
+
ongoing: boolean;
|
|
70
|
+
}
|
|
71
|
+
/** Shared context for VitaSection's title, meta, note and default (body) slots. */
|
|
72
|
+
interface VitaSectionSlotProps {
|
|
73
|
+
entry: Entry;
|
|
74
|
+
/** Zero-based position in the displayed list, after limiting. */
|
|
75
|
+
index: number;
|
|
76
|
+
display: VitaSectionDisplay;
|
|
77
|
+
}
|
|
56
78
|
/**
|
|
57
79
|
* The context line, as structured parts. The field used for the title is
|
|
58
80
|
* skipped, so nothing appears twice.
|
|
59
81
|
*/
|
|
60
82
|
declare function entrySecondaryParts(entry: Entry, titleField?: string): SecondaryPart[];
|
|
83
|
+
/**
|
|
84
|
+
* The `linked` values of a field, as a list: none, one, or several. For
|
|
85
|
+
* templates that show a field which may hold one link or a list of them.
|
|
86
|
+
*/
|
|
87
|
+
declare function asLinkedList(value: unknown): Linked[];
|
|
61
88
|
|
|
62
89
|
/** The plain-text form: Markdown source, for LaTeX, comparisons and `title=`. */
|
|
63
90
|
declare function richSource(value: unknown): string;
|
|
64
91
|
/** The rendered form, or `undefined` when there is nothing to render. */
|
|
65
92
|
declare function richHtml(value: unknown): string | undefined;
|
|
66
93
|
|
|
67
|
-
|
|
68
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Selecting entries by field: the `where` of a PDF document's `.yml` and of
|
|
96
|
+
* `<VitaSection where>`. One implementation, so that the website and the PDF
|
|
97
|
+
* cannot disagree about which entries match.
|
|
98
|
+
*
|
|
99
|
+
* Deliberately small: equality with one value, or with any of several. Values
|
|
100
|
+
* are compared as text, so `where: { year: 2024 }` and `{ year: '2024' }` mean
|
|
101
|
+
* the same.
|
|
102
|
+
*/
|
|
103
|
+
|
|
104
|
+
/** A value to compare with, or several of which any may match. */
|
|
105
|
+
type WhereValue = string | number | boolean | readonly (string | number | boolean)[];
|
|
106
|
+
/** Field name -> wanted value(s). Every field must match. */
|
|
107
|
+
type Where = Readonly<Record<string, WhereValue>>;
|
|
108
|
+
/**
|
|
109
|
+
* The comparable value of a field: `year` from the date, the source text of a
|
|
110
|
+
* formatted-text field, the label of a link.
|
|
111
|
+
*/
|
|
112
|
+
declare function fieldValue(entry: Entry, field: string): unknown;
|
|
113
|
+
/** Whether an entry matches every field of `where`. */
|
|
114
|
+
declare function matchesWhere(entry: Entry, where: Where): boolean;
|
|
115
|
+
/** The entries matching `where`; all of them when it is empty or absent. */
|
|
116
|
+
declare function filterWhere<T extends Entry>(entries: readonly T[], where: Where | undefined): T[];
|
|
117
|
+
|
|
118
|
+
export { DateRange, Entry, LINKED_FIELDS, Link, Linked, SECONDARY_FIELDS, TITLE_FIELDS, asLinkedList, dateSortKey, entrySecondaryParts, entryTitleField, fieldValue, filterWhere, formatDateRange, matchesWhere, richHtml, richSource };
|
|
119
|
+
export type { DateFormatOptions, SecondaryPart, VitaSectionDisplay, VitaSectionSlotProps, Where, WhereValue };
|
package/dist/shared/index.mjs
CHANGED
|
@@ -1,57 +1,3 @@
|
|
|
1
|
-
export { a as
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
if (precision === "year" || point.month === void 0) return String(point.year);
|
|
5
|
-
const date = new Date(Date.UTC(point.year, point.month - 1, point.day ?? 1));
|
|
6
|
-
return new Intl.DateTimeFormat(locale, {
|
|
7
|
-
year: "numeric",
|
|
8
|
-
month: "short",
|
|
9
|
-
...point.day === void 0 ? {} : { day: "numeric" },
|
|
10
|
-
timeZone: "UTC"
|
|
11
|
-
}).format(date);
|
|
12
|
-
}
|
|
13
|
-
function formatDateRange(range, options = {}) {
|
|
14
|
-
if (!range) return "";
|
|
15
|
-
const { presentLabel = "present" } = options;
|
|
16
|
-
const from = formatPoint(range.from, options);
|
|
17
|
-
if (range.point) return from;
|
|
18
|
-
if (range.ongoing) return `${from}\u2013${presentLabel}`;
|
|
19
|
-
if (!range.to) return from;
|
|
20
|
-
const to = formatPoint(range.to, options);
|
|
21
|
-
return from === to ? from : `${from}\u2013${to}`;
|
|
22
|
-
}
|
|
23
|
-
function dateSortKey(range) {
|
|
24
|
-
return range?.from.sortKey ?? "";
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const TITLE_FIELDS = ["title", "position", "degree", "role", "student"];
|
|
28
|
-
const SECONDARY_FIELDS = ["student", "role", "event", "term", "degree"];
|
|
29
|
-
const INSTITUTION_FIELDS = ["institution", "issuer", "organisation"];
|
|
30
|
-
function entryTitleField(entry, fields = TITLE_FIELDS) {
|
|
31
|
-
return fields.find((field) => {
|
|
32
|
-
const value = entry[field];
|
|
33
|
-
if (typeof value === "string") return value.length > 0;
|
|
34
|
-
return typeof value === "object" && value !== null && "html" in value;
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
function entrySecondaryParts(entry, titleField) {
|
|
38
|
-
const parts = [];
|
|
39
|
-
for (const field of SECONDARY_FIELDS) {
|
|
40
|
-
if (field === titleField) continue;
|
|
41
|
-
const value = entry[field];
|
|
42
|
-
if (typeof value === "string" && value.length > 0) parts.push({ kind: "text", text: value });
|
|
43
|
-
}
|
|
44
|
-
for (const field of INSTITUTION_FIELDS) {
|
|
45
|
-
const value = entry[field];
|
|
46
|
-
if (value?.name) {
|
|
47
|
-
parts.push({ kind: "institution", institution: value });
|
|
48
|
-
break;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (typeof entry.location === "string" && entry.location.length > 0) {
|
|
52
|
-
parts.push({ kind: "text", text: entry.location });
|
|
53
|
-
}
|
|
54
|
-
return parts;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export { INSTITUTION_FIELDS, SECONDARY_FIELDS, TITLE_FIELDS, dateSortKey, entrySecondaryParts, entryTitleField, formatDateRange };
|
|
1
|
+
export { L as LINKED_FIELDS, S as SECONDARY_FIELDS, T as TITLE_FIELDS, a as asLinkedList, d as dateSortKey, e as entrySecondaryParts, b as entryTitleField, f as formatDateRange } from './vitakit.CfB3hMHl.mjs';
|
|
2
|
+
export { r as richHtml, a as richSource } from './vitakit.CtChKO0I.mjs';
|
|
3
|
+
export { f as fieldValue, a as filterWhere, m as matchesWhere } from './vitakit.D-DdJaDZ.mjs';
|