erudit 4.3.3 → 4.3.4-dev.2
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/app/components/main/MainBreadcrumbs.vue +27 -19
- package/app/components/main/MainKeyLinks.vue +13 -7
- package/app/components/main/MainSubTitle.vue +4 -3
- package/app/components/main/connections/MainConnections.vue +47 -40
- package/app/components/main/contentStats/MainContentStats.vue +16 -17
- package/app/composables/favicon.ts +1 -1
- package/app/composables/jsonLd.ts +23 -1
- package/app/composables/og.ts +2 -0
- package/package.json +5 -5
- package/server/erudit/build.ts +12 -29
- package/server/erudit/db/repository/pushProblemScript.ts +7 -4
- package/server/erudit/language/list/en.ts +1 -0
- package/server/erudit/language/list/ru.ts +1 -0
- package/shared/types/language.ts +1 -0
|
@@ -1,28 +1,36 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
2
|
defineProps<{ breadcrumbs: Breadcrumbs }>();
|
|
3
|
+
|
|
4
|
+
const phrase = await usePhrases('breadcrumb');
|
|
3
5
|
</script>
|
|
4
6
|
|
|
5
7
|
<template>
|
|
6
|
-
<
|
|
8
|
+
<nav
|
|
7
9
|
v-if="breadcrumbs.length > 0"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
:aria-label="phrase.breadcrumb"
|
|
11
|
+
class="px-main py-main-half"
|
|
10
12
|
>
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class="gap-small text-text-dimmed hocus:text-text-muted flex items-center
|
|
15
|
-
transition-[color]"
|
|
13
|
+
<ol
|
|
14
|
+
class="gap-small max-micro:justify-center m-0 flex list-none flex-wrap
|
|
15
|
+
p-0"
|
|
16
16
|
>
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
<li v-for="(breadcrumb, i) of breadcrumbs">
|
|
18
|
+
<EruditLink
|
|
19
|
+
:to="breadcrumb.link"
|
|
20
|
+
class="gap-small text-text-dimmed hocus:text-text-muted flex
|
|
21
|
+
items-center transition-[color]"
|
|
22
|
+
>
|
|
23
|
+
<MaybeMyIcon :name="breadcrumb.icon" class="text-[1.2em]" />
|
|
24
|
+
<span>{{ formatText(breadcrumb.title) }}</span>
|
|
25
|
+
<MyIcon
|
|
26
|
+
name="chevron-right"
|
|
27
|
+
:class="{
|
|
28
|
+
'relative -left-[3px]': true,
|
|
29
|
+
'rotate-90': i === breadcrumbs.length - 1,
|
|
30
|
+
}"
|
|
31
|
+
/>
|
|
32
|
+
</EruditLink>
|
|
33
|
+
</li>
|
|
34
|
+
</ol>
|
|
35
|
+
</nav>
|
|
28
36
|
</template>
|
|
@@ -19,16 +19,22 @@ const phrase = await usePhrases('key_elements');
|
|
|
19
19
|
|
|
20
20
|
<template>
|
|
21
21
|
<template v-if="keyLinks">
|
|
22
|
-
<
|
|
22
|
+
<nav
|
|
23
|
+
v-if="mode === 'single'"
|
|
24
|
+
:aria-label="phrase.key_elements"
|
|
25
|
+
class="px-main py-main-half"
|
|
26
|
+
>
|
|
23
27
|
<MainSubTitle :title="phrase.key_elements + ':'" />
|
|
24
|
-
<
|
|
28
|
+
<ul
|
|
25
29
|
:style="{ '--keyBg': 'var(--color-bg-aside)' }"
|
|
26
|
-
class="gap-small micro:gap-normal micro:justify-start flex
|
|
27
|
-
justify-center"
|
|
30
|
+
class="gap-small micro:gap-normal micro:justify-start m-0 flex list-none
|
|
31
|
+
flex-wrap justify-center p-0"
|
|
28
32
|
>
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
<li v-for="keyLink of keyLinks">
|
|
34
|
+
<MainKeyLink :keyLink />
|
|
35
|
+
</li>
|
|
36
|
+
</ul>
|
|
37
|
+
</nav>
|
|
32
38
|
<div
|
|
33
39
|
v-else
|
|
34
40
|
:style="{ '--keyBg': 'var(--color-bg-main)' }"
|
|
@@ -3,9 +3,10 @@ defineProps<{ title: string }>();
|
|
|
3
3
|
</script>
|
|
4
4
|
|
|
5
5
|
<template>
|
|
6
|
-
<
|
|
7
|
-
class="text-main-sm micro:text-left pb-main-half text-center
|
|
6
|
+
<h2
|
|
7
|
+
class="text-main-sm micro:text-left pb-main-half m-0 text-center
|
|
8
|
+
font-semibold"
|
|
8
9
|
>
|
|
9
10
|
{{ formatText(title) }}
|
|
10
|
-
</
|
|
11
|
+
</h2>
|
|
11
12
|
</template>
|
|
@@ -27,11 +27,15 @@ const parentExternalsCount = computed(() => {
|
|
|
27
27
|
</script>
|
|
28
28
|
|
|
29
29
|
<template>
|
|
30
|
-
<section
|
|
30
|
+
<section
|
|
31
|
+
v-if="connections"
|
|
32
|
+
:aria-label="phrase.connections"
|
|
33
|
+
class="px-main py-main-half"
|
|
34
|
+
>
|
|
31
35
|
<MainSubTitle :title="phrase.connections + ':'" />
|
|
32
|
-
<
|
|
33
|
-
class="gap-small micro:gap-normal micro:justify-start flex
|
|
34
|
-
justify-center"
|
|
36
|
+
<ul
|
|
37
|
+
class="gap-small micro:gap-normal micro:justify-start m-0 flex list-none
|
|
38
|
+
flex-wrap justify-center p-0"
|
|
35
39
|
>
|
|
36
40
|
<template
|
|
37
41
|
v-for="(items, type) of {
|
|
@@ -40,49 +44,52 @@ const parentExternalsCount = computed(() => {
|
|
|
40
44
|
dependents: connections.dependents,
|
|
41
45
|
}"
|
|
42
46
|
>
|
|
47
|
+
<li v-if="items && items.length > 0">
|
|
48
|
+
<MainConnectionsButton
|
|
49
|
+
:type="type"
|
|
50
|
+
:count="items.length"
|
|
51
|
+
:active="currentType === type"
|
|
52
|
+
@click="
|
|
53
|
+
currentType === type
|
|
54
|
+
? (currentType = undefined)
|
|
55
|
+
: (currentType = type)
|
|
56
|
+
"
|
|
57
|
+
/>
|
|
58
|
+
</li>
|
|
59
|
+
</template>
|
|
60
|
+
<li v-if="connections.externals">
|
|
43
61
|
<MainConnectionsButton
|
|
44
|
-
|
|
45
|
-
:
|
|
46
|
-
:count="items.length"
|
|
47
|
-
:active="currentType === type"
|
|
62
|
+
type="externals"
|
|
63
|
+
:active="currentType === 'externals'"
|
|
48
64
|
@click="
|
|
49
|
-
currentType ===
|
|
65
|
+
currentType === 'externals'
|
|
50
66
|
? (currentType = undefined)
|
|
51
|
-
: (currentType =
|
|
67
|
+
: (currentType = 'externals')
|
|
52
68
|
"
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
<MainConnectionsButton
|
|
56
|
-
v-if="connections.externals"
|
|
57
|
-
type="externals"
|
|
58
|
-
:active="currentType === 'externals'"
|
|
59
|
-
@click="
|
|
60
|
-
currentType === 'externals'
|
|
61
|
-
? (currentType = undefined)
|
|
62
|
-
: (currentType = 'externals')
|
|
63
|
-
"
|
|
64
|
-
>
|
|
65
|
-
<template #after>
|
|
66
|
-
<div
|
|
67
|
-
v-if="connections.externals"
|
|
68
|
-
class="gap-small *:border-border *:pl-small flex items-center
|
|
69
|
-
font-bold *:border-l"
|
|
70
|
-
>
|
|
69
|
+
>
|
|
70
|
+
<template #after>
|
|
71
71
|
<div
|
|
72
|
-
v-if="
|
|
73
|
-
class="
|
|
72
|
+
v-if="connections.externals"
|
|
73
|
+
class="gap-small *:border-border *:pl-small flex items-center
|
|
74
|
+
font-bold *:border-l"
|
|
74
75
|
>
|
|
75
|
-
<
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
<div
|
|
77
|
+
v-if="ownExternalsCount"
|
|
78
|
+
class="flex items-center gap-1 text-amber-600
|
|
79
|
+
dark:text-amber-400"
|
|
80
|
+
>
|
|
81
|
+
<MyIcon name="arrow/left" class="-scale-x-100" />
|
|
82
|
+
<span>{{ ownExternalsCount }}</span>
|
|
83
|
+
</div>
|
|
84
|
+
<div v-if="parentExternalsCount" class="flex items-center gap-1">
|
|
85
|
+
<MyIcon name="arrow/up-to-right" />
|
|
86
|
+
<span>{{ parentExternalsCount }}</span>
|
|
87
|
+
</div>
|
|
81
88
|
</div>
|
|
82
|
-
</
|
|
83
|
-
</
|
|
84
|
-
</
|
|
85
|
-
</
|
|
89
|
+
</template>
|
|
90
|
+
</MainConnectionsButton>
|
|
91
|
+
</li>
|
|
92
|
+
</ul>
|
|
86
93
|
<template v-if="currentType && connections[currentType]">
|
|
87
94
|
<Deps
|
|
88
95
|
v-if="currentType !== 'externals'"
|
|
@@ -15,27 +15,26 @@ const phrase = await usePhrases('stats');
|
|
|
15
15
|
<template>
|
|
16
16
|
<section
|
|
17
17
|
v-if="mode === 'single' && (stats || lastChangedDate)"
|
|
18
|
+
:aria-label="phrase.stats"
|
|
18
19
|
class="px-main py-main-half"
|
|
19
20
|
>
|
|
20
21
|
<MainSubTitle :title="phrase.stats + ':'" />
|
|
21
|
-
<
|
|
22
|
-
class="micro:justify-start gap-small micro:gap-normal flex
|
|
23
|
-
justify-center"
|
|
22
|
+
<ul
|
|
23
|
+
class="micro:justify-start gap-small micro:gap-normal m-0 flex list-none
|
|
24
|
+
flex-wrap justify-center p-0"
|
|
24
25
|
>
|
|
25
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<ItemLastChanged v-if="lastChangedDate" :date="lastChangedDate" />
|
|
38
|
-
</div>
|
|
26
|
+
<li v-if="stats?.materials">
|
|
27
|
+
<ItemMaterials :count="stats.materials" mode="detailed" />
|
|
28
|
+
</li>
|
|
29
|
+
<template v-if="stats?.elements">
|
|
30
|
+
<li v-for="(count, schemaName) of stats.elements">
|
|
31
|
+
<ItemElement :schemaName :count mode="detailed" />
|
|
32
|
+
</li>
|
|
33
|
+
</template>
|
|
34
|
+
<li v-if="lastChangedDate">
|
|
35
|
+
<ItemLastChanged :date="lastChangedDate" />
|
|
36
|
+
</li>
|
|
37
|
+
</ul>
|
|
39
38
|
</section>
|
|
40
39
|
<div
|
|
41
40
|
v-else-if="mode === 'children' && stats"
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Breadcrumbs } from '../../shared/types/breadcrumbs';
|
|
2
|
+
import type { ElementSnippet } from '../../shared/types/elementSnippet';
|
|
2
3
|
|
|
3
4
|
export function useJsonLd(key: string, data: Record<string, unknown>) {
|
|
4
5
|
useHead({
|
|
@@ -60,6 +61,8 @@ export function useContentArticleJsonLd(args: {
|
|
|
60
61
|
urlPath: string;
|
|
61
62
|
contentType: string;
|
|
62
63
|
lastmod?: string;
|
|
64
|
+
keyElements?: ElementSnippet[];
|
|
65
|
+
breadcrumbs?: Breadcrumbs;
|
|
63
66
|
}) {
|
|
64
67
|
const withSiteUrl = useSiteUrl();
|
|
65
68
|
|
|
@@ -90,12 +93,31 @@ export function useContentArticleJsonLd(args: {
|
|
|
90
93
|
data.dateModified = args.lastmod;
|
|
91
94
|
}
|
|
92
95
|
|
|
93
|
-
|
|
96
|
+
const parentBreadcrumb =
|
|
97
|
+
args.breadcrumbs && args.breadcrumbs.length >= 1
|
|
98
|
+
? args.breadcrumbs[args.breadcrumbs.length - 1]
|
|
99
|
+
: undefined;
|
|
100
|
+
|
|
101
|
+
if (parentBreadcrumb) {
|
|
102
|
+
data.isPartOf = {
|
|
103
|
+
'@type': 'WebPage',
|
|
104
|
+
name: formatText(parentBreadcrumb.title),
|
|
105
|
+
url: withSiteUrl(parentBreadcrumb.link),
|
|
106
|
+
};
|
|
107
|
+
} else if (siteTitle) {
|
|
94
108
|
data.isPartOf = {
|
|
95
109
|
'@type': 'WebSite',
|
|
96
110
|
name: siteTitle,
|
|
97
111
|
};
|
|
98
112
|
}
|
|
99
113
|
|
|
114
|
+
if (args.keyElements && args.keyElements.length > 0) {
|
|
115
|
+
data.hasPart = args.keyElements.map((el) => ({
|
|
116
|
+
'@type': 'DefinedTerm',
|
|
117
|
+
name: formatText(el.seo?.title || el.key?.title || el.title),
|
|
118
|
+
url: withSiteUrl(el.link),
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
|
|
100
122
|
useJsonLd('jsonld-content', data);
|
|
101
123
|
}
|
package/app/composables/og.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "erudit",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.4-dev.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "🤓 CMS for perfect educational sites.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@erudit-js/cli": "4.3.
|
|
28
|
-
"@erudit-js/core": "4.3.
|
|
29
|
-
"@erudit-js/prose": "4.3.
|
|
27
|
+
"@erudit-js/cli": "4.3.4-dev.2",
|
|
28
|
+
"@erudit-js/core": "4.3.4-dev.2",
|
|
29
|
+
"@erudit-js/prose": "4.3.4-dev.2",
|
|
30
30
|
"@floating-ui/vue": "^1.1.11",
|
|
31
31
|
"@resvg/resvg-js": "^2.6.2",
|
|
32
32
|
"@tailwindcss/vite": "^4.2.1",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"nuxt": "4.4.2",
|
|
44
44
|
"nuxt-my-icons": "1.2.2",
|
|
45
45
|
"perfect-debounce": "^2.1.0",
|
|
46
|
-
"satori": "^0.
|
|
46
|
+
"satori": "^0.26.0",
|
|
47
47
|
"sharp": "^0.34.5",
|
|
48
48
|
"tailwindcss": "^4.2.1",
|
|
49
49
|
"ts-xor": "^1.3.0",
|
package/server/erudit/build.ts
CHANGED
|
@@ -61,6 +61,14 @@ export async function buildServerErudit() {
|
|
|
61
61
|
// Watcher
|
|
62
62
|
//
|
|
63
63
|
|
|
64
|
+
const watchedProjectDirs = [
|
|
65
|
+
'content',
|
|
66
|
+
'contributors',
|
|
67
|
+
'cameos',
|
|
68
|
+
'sponsors',
|
|
69
|
+
'news',
|
|
70
|
+
] as const;
|
|
71
|
+
|
|
64
72
|
export async function tryServerWatchProject() {
|
|
65
73
|
if (ERUDIT.mode === 'static') {
|
|
66
74
|
return;
|
|
@@ -93,39 +101,14 @@ export async function tryServerWatchProject() {
|
|
|
93
101
|
}
|
|
94
102
|
}, 300);
|
|
95
103
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (path.startsWith(ERUDIT.paths.project('contributors') + '/')) {
|
|
102
|
-
return true;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (path.startsWith(ERUDIT.paths.project('cameos') + '/')) {
|
|
106
|
-
return true;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
if (path.startsWith(ERUDIT.paths.project('sponsors') + '/')) {
|
|
110
|
-
return true;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (path.startsWith(ERUDIT.paths.project('news') + '/')) {
|
|
114
|
-
return true;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const watcher = chokidar.watch(ERUDIT.paths.project(), {
|
|
119
|
-
ignoreInitial: true,
|
|
120
|
-
});
|
|
104
|
+
const watcher = chokidar.watch(
|
|
105
|
+
watchedProjectDirs.map((dir) => ERUDIT.paths.project(dir)),
|
|
106
|
+
{ ignoreInitial: true },
|
|
107
|
+
);
|
|
121
108
|
|
|
122
109
|
watcher.on('all', (_, path) => {
|
|
123
110
|
path = sn(path);
|
|
124
111
|
|
|
125
|
-
if (!isWatched(path)) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
112
|
if (pendingRebuild) {
|
|
130
113
|
return;
|
|
131
114
|
}
|
|
@@ -23,8 +23,11 @@ export async function pushProblemScript(
|
|
|
23
23
|
relativePath = problemScriptSrc;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
await ERUDIT.db
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
await ERUDIT.db
|
|
27
|
+
.insert(ERUDIT.db.schema.problemScripts)
|
|
28
|
+
.values({
|
|
29
|
+
problemScriptSrc: relativePath,
|
|
30
|
+
contentFullId,
|
|
31
|
+
})
|
|
32
|
+
.onConflictDoNothing();
|
|
30
33
|
}
|
|
@@ -46,6 +46,7 @@ export const phrases: LanguagePhrases = {
|
|
|
46
46
|
flag_secondary: 'Additional',
|
|
47
47
|
flag_secondary_description:
|
|
48
48
|
'This is an optional material is for learners who want to dive deeper and gain additional knowledge and context.',
|
|
49
|
+
breadcrumb: 'Breadcrumb',
|
|
49
50
|
key_elements: 'Key elements',
|
|
50
51
|
stats: 'Statistics',
|
|
51
52
|
connections: 'Connections',
|
|
@@ -47,6 +47,7 @@ export const phrases: LanguagePhrases = {
|
|
|
47
47
|
flag_secondary: 'Дополнение',
|
|
48
48
|
flag_secondary_description:
|
|
49
49
|
'Это дополнительный материал для тех, кто хочет глубже погрузиться в предмет и получить дополнительные знания и контекст.',
|
|
50
|
+
breadcrumb: 'Путь',
|
|
50
51
|
key_elements: 'Ключевые элементы',
|
|
51
52
|
stats: 'Статистика',
|
|
52
53
|
connections: 'Связи',
|
package/shared/types/language.ts
CHANGED