erudit 4.3.5-dev.1 → 4.3.5
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/ImageTint.vue +42 -0
- package/app/components/main/MainContentChild.vue +26 -16
- package/app/components/main/MainDecoration.vue +24 -10
- package/app/components/main/MainKeyLinks.vue +2 -1
- package/app/components/main/MainTitle.vue +19 -11
- package/app/components/main/MainTopicPartPage.vue +19 -11
- package/app/components/main/contentStats/MainContentStats.vue +2 -1
- package/app/pages/book/[...bookId].vue +14 -3
- package/app/pages/group/[...groupId].vue +13 -2
- package/app/pages/page/[...pageId].vue +15 -2
- package/package.json +5 -5
- package/server/erudit/content/repository/children.ts +7 -0
- package/server/erudit/content/repository/decoration.ts +17 -7
- package/server/erudit/content/repository/deps.ts +41 -25
- package/server/erudit/repository.ts +5 -1
- package/shared/types/mainContent.ts +1 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
const { src } = defineProps<{
|
|
3
|
+
src: string;
|
|
4
|
+
}>();
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<template>
|
|
8
|
+
<div :class="$style.imageTint">
|
|
9
|
+
<img :src />
|
|
10
|
+
<img :src />
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<style module>
|
|
15
|
+
.imageTint {
|
|
16
|
+
position: relative;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
isolation: isolate;
|
|
19
|
+
|
|
20
|
+
img {
|
|
21
|
+
width: 100%;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
img:first-child {
|
|
25
|
+
mix-blend-mode: multiply;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
img:last-child {
|
|
29
|
+
position: absolute;
|
|
30
|
+
inset: 0;
|
|
31
|
+
z-index: -1;
|
|
32
|
+
filter: drop-shadow(
|
|
33
|
+
0px 1000px 0
|
|
34
|
+
var(--tint, color-mix(in hsl, var(--color-brand), transparent 90%))
|
|
35
|
+
);
|
|
36
|
+
transform: translateY(-1000px);
|
|
37
|
+
opacity: var(--tint-opacity, 1);
|
|
38
|
+
transition: opacity var(--default-transition-timing-function)
|
|
39
|
+
var(--default-transition-duration);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
</style>
|
|
@@ -10,30 +10,40 @@ const hasExtra = child.stats || child.keyLinks;
|
|
|
10
10
|
dark:bg-bg-aside rounded border bg-neutral-100 ring-2 ring-transparent
|
|
11
11
|
transition-[border,box-shadow]"
|
|
12
12
|
>
|
|
13
|
+
<EruditLink
|
|
14
|
+
v-if="child.decoration"
|
|
15
|
+
:to="child.link"
|
|
16
|
+
class="p-normal max-micro:pb-0 not-group-hocus:[--tint-opacity:0]
|
|
17
|
+
micro:float-right block"
|
|
18
|
+
>
|
|
19
|
+
<ImageTint
|
|
20
|
+
:src="fileUrl(child.decoration)"
|
|
21
|
+
class="m-auto block aspect-square max-h-[90px] max-w-[90px]"
|
|
22
|
+
/>
|
|
23
|
+
</EruditLink>
|
|
24
|
+
|
|
13
25
|
<EruditLink :to="child.link" class="p-normal gap-small flex flex-col">
|
|
14
|
-
<
|
|
26
|
+
<h2
|
|
27
|
+
class="group-hocus:text-brand micro:text-main-lg micro:text-start
|
|
28
|
+
text-center font-bold transition-[color]"
|
|
29
|
+
>
|
|
15
30
|
<MyIcon
|
|
16
31
|
:name="ICONS[child.type]"
|
|
17
|
-
class="text-text-muted group-hocus:text-brand
|
|
18
|
-
transition-[color]"
|
|
32
|
+
class="text-text-muted group-hocus:text-brand mr-small relative
|
|
33
|
+
-top-[1px] inline shrink-0 text-[1.2em] transition-[color]"
|
|
19
34
|
/>
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
items-center font-bold transition-[color]"
|
|
23
|
-
>
|
|
24
|
-
{{ formatText(child.title) }}
|
|
25
|
-
</h2>
|
|
26
|
-
</div>
|
|
35
|
+
<span>{{ formatText(child.title) }}</span>
|
|
36
|
+
</h2>
|
|
27
37
|
|
|
28
|
-
<div
|
|
38
|
+
<div
|
|
39
|
+
v-if="child.description"
|
|
40
|
+
class="text-text-muted micro:text-start text-center"
|
|
41
|
+
>
|
|
29
42
|
{{ formatText(child.description) }}
|
|
30
43
|
</div>
|
|
31
44
|
</EruditLink>
|
|
32
|
-
<div
|
|
33
|
-
v-if="
|
|
34
|
-
class="border-t-border p-normal gap-normal flex flex-col border-t"
|
|
35
|
-
>
|
|
36
|
-
<div v-if="child.keyLinks" class="relative top-[1px]">
|
|
45
|
+
<div v-if="hasExtra" class="px-normal pb-normal gap-normal flex flex-col">
|
|
46
|
+
<div v-if="child.keyLinks">
|
|
37
47
|
<MainKeyLinks :elementSnippets="child.keyLinks" mode="children" />
|
|
38
48
|
</div>
|
|
39
49
|
<div v-if="child.stats">
|
|
@@ -1,15 +1,29 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
defineProps<{
|
|
2
|
+
defineProps<{
|
|
3
|
+
role: 'preamble-float' | 'preamble-static';
|
|
4
|
+
decoration?: string;
|
|
5
|
+
}>();
|
|
3
6
|
</script>
|
|
4
7
|
|
|
5
8
|
<template>
|
|
6
|
-
<
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
<template v-if="decoration">
|
|
10
|
+
<section
|
|
11
|
+
v-if="role === 'preamble-float'"
|
|
12
|
+
class="p-small pr-main float-right pt-3 max-[700px]:hidden"
|
|
13
|
+
>
|
|
14
|
+
<ImageTint
|
|
15
|
+
:src="fileUrl(decoration)"
|
|
16
|
+
class="block aspect-square max-h-[125px] max-w-[125px]"
|
|
17
|
+
/>
|
|
18
|
+
</section>
|
|
19
|
+
<section
|
|
20
|
+
v-else-if="role === 'preamble-static'"
|
|
21
|
+
class="px-main py-main-half min-[700px]:hidden"
|
|
22
|
+
>
|
|
23
|
+
<ImageTint
|
|
24
|
+
:src="fileUrl(decoration)"
|
|
25
|
+
class="micro:m-0 m-auto block aspect-square max-h-[125px] max-w-[125px]"
|
|
26
|
+
/>
|
|
27
|
+
</section>
|
|
28
|
+
</template>
|
|
15
29
|
</template>
|
|
@@ -38,7 +38,8 @@ const phrase = await usePhrases('key_elements');
|
|
|
38
38
|
<div
|
|
39
39
|
v-else
|
|
40
40
|
:style="{ '--keyBg': 'var(--color-bg-main)' }"
|
|
41
|
-
class="gap-small text-main-sm flex flex-wrap
|
|
41
|
+
class="gap-small text-main-sm micro:justify-start flex flex-wrap
|
|
42
|
+
justify-center"
|
|
42
43
|
>
|
|
43
44
|
<MainKeyLink v-for="keyLink of keyLinks" :keyLink />
|
|
44
45
|
</div>
|
|
@@ -5,27 +5,35 @@ const { color = 'var(--color-brand)' } = defineProps<{
|
|
|
5
5
|
icon: MaybeMyIconName;
|
|
6
6
|
title: string;
|
|
7
7
|
color?: string;
|
|
8
|
+
contentLabel?: string;
|
|
8
9
|
}>();
|
|
9
10
|
</script>
|
|
10
11
|
|
|
11
12
|
<template>
|
|
12
13
|
<section
|
|
13
|
-
:style="{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
:style="{
|
|
15
|
+
'--titleColor': color,
|
|
16
|
+
'--iconColor':
|
|
17
|
+
'color-mix(in srgb, var(--titleColor), var(--color-text) 70%)',
|
|
18
|
+
}"
|
|
19
|
+
class="gap-small micro:items-start px-main py-main-half relative flex
|
|
20
|
+
flex-col items-center justify-center leading-none"
|
|
17
21
|
>
|
|
18
22
|
<div
|
|
19
|
-
class="
|
|
20
|
-
|
|
23
|
+
class="gap-small micro:hidden flex items-center text-[1.1em] font-semibold
|
|
24
|
+
text-(--iconColor)"
|
|
21
25
|
>
|
|
22
|
-
<MaybeMyIcon
|
|
23
|
-
|
|
24
|
-
class="max-micro:text-white micro:text-[38px] text-[30px]
|
|
25
|
-
text-[color-mix(in_srgb,var(--titleColor),var(--color-text)_70%)]"
|
|
26
|
-
/>
|
|
26
|
+
<MaybeMyIcon :name="icon" class="text-[1.2em]" />
|
|
27
|
+
<span class="micro:hidden">{{ contentLabel }}</span>
|
|
27
28
|
</div>
|
|
28
29
|
<h1 class="text-size-h1 max-micro:text-center">
|
|
30
|
+
<span :title="contentLabel" class="mr-normal cursor-help">
|
|
31
|
+
<MaybeMyIcon
|
|
32
|
+
:name="icon"
|
|
33
|
+
class="max-micro:hidden relative -top-[1px] inline cursor-help
|
|
34
|
+
text-[1.1em] text-(--iconColor)"
|
|
35
|
+
/>
|
|
36
|
+
</span>
|
|
29
37
|
<FancyBold :text="title" :color="color" />
|
|
30
38
|
</h1>
|
|
31
39
|
</section>
|
|
@@ -18,6 +18,7 @@ async function proseMounted() {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const phrase = await usePhrases(
|
|
21
|
+
'article',
|
|
21
22
|
'summary',
|
|
22
23
|
'practice',
|
|
23
24
|
'article_seo_description',
|
|
@@ -30,21 +31,17 @@ await useContentSeo({
|
|
|
30
31
|
title: mainContent.title,
|
|
31
32
|
bookTitle: mainContent.bookTitle,
|
|
32
33
|
contentTypeSuffix:
|
|
33
|
-
mainContent.part !==
|
|
34
|
+
mainContent.part !== mainContent.parts[0]
|
|
35
|
+
? phrase[mainContent.part]
|
|
36
|
+
: undefined,
|
|
34
37
|
contentTypePath: {
|
|
35
38
|
type: 'topic',
|
|
36
39
|
topicPart: mainContent.part,
|
|
37
40
|
contentId: mainContent.shortId,
|
|
38
41
|
},
|
|
39
42
|
description:
|
|
40
|
-
mainContent.part ===
|
|
41
|
-
|
|
42
|
-
phrase.article_seo_description(mainContent.title)
|
|
43
|
-
: mainContent.part === 'summary'
|
|
44
|
-
? phrase.summary_seo_description(mainContent.title)
|
|
45
|
-
: mainContent.part === 'practice'
|
|
46
|
-
? phrase.practice_seo_description(mainContent.title)
|
|
47
|
-
: undefined,
|
|
43
|
+
(mainContent.part === mainContent.parts[0] && mainContent.description) ||
|
|
44
|
+
phrase[`${mainContent.part}_seo_description`](mainContent.title),
|
|
48
45
|
seo: mainContent.seo,
|
|
49
46
|
snippets: mainContent.snippets,
|
|
50
47
|
breadcrumbs: mainContent.breadcrumbs,
|
|
@@ -57,10 +54,21 @@ await useContentSeo({
|
|
|
57
54
|
|
|
58
55
|
<template>
|
|
59
56
|
<MainGlow />
|
|
60
|
-
<MainDecoration :decoration="mainContent.decoration" />
|
|
61
57
|
<MainSectionPreamble>
|
|
58
|
+
<MainDecoration
|
|
59
|
+
role="preamble-float"
|
|
60
|
+
:decoration="mainContent.decoration"
|
|
61
|
+
/>
|
|
62
62
|
<MainBreadcrumbs :breadcrumbs="mainContent.breadcrumbs" />
|
|
63
|
-
<
|
|
63
|
+
<MainDecoration
|
|
64
|
+
role="preamble-static"
|
|
65
|
+
:decoration="mainContent.decoration"
|
|
66
|
+
/>
|
|
67
|
+
<MainTitle
|
|
68
|
+
:icon="ICONS[mainContent.part]"
|
|
69
|
+
:title="mainContent.title"
|
|
70
|
+
:contentLabel="phrase[mainContent.part]"
|
|
71
|
+
/>
|
|
64
72
|
<MainFlags :flags="mainContent.flags" />
|
|
65
73
|
<MainDescription :description="mainContent.description" />
|
|
66
74
|
<MainKeyLinks mode="single" :elementSnippets="mainContent.snippets" />
|
|
@@ -38,7 +38,8 @@ const phrase = await usePhrases('stats');
|
|
|
38
38
|
</section>
|
|
39
39
|
<div
|
|
40
40
|
v-else-if="mode === 'children' && stats"
|
|
41
|
-
class="gap-small micro:gap-normal text-main-sm
|
|
41
|
+
class="gap-small micro:gap-normal text-main-sm micro:justify-start flex
|
|
42
|
+
flex-wrap justify-center"
|
|
42
43
|
>
|
|
43
44
|
<ItemMaterials
|
|
44
45
|
v-if="stats.materials"
|
|
@@ -18,7 +18,7 @@ if (ERUDIT.config.contributors?.enabled) {
|
|
|
18
18
|
showNewsAside();
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const phrase = await usePhrases('begin_learning');
|
|
21
|
+
const phrase = await usePhrases('book', 'begin_learning');
|
|
22
22
|
const lastChangedDate = useLastChanged(() => mainContent.lastmod);
|
|
23
23
|
|
|
24
24
|
await useContentSeo({
|
|
@@ -40,10 +40,21 @@ await useContentSeo({
|
|
|
40
40
|
|
|
41
41
|
<template>
|
|
42
42
|
<MainGlow />
|
|
43
|
-
<MainDecoration :decoration="mainContent.decoration" />
|
|
44
43
|
<MainSectionPreamble>
|
|
44
|
+
<MainDecoration
|
|
45
|
+
role="preamble-float"
|
|
46
|
+
:decoration="mainContent.decoration"
|
|
47
|
+
/>
|
|
45
48
|
<MainBreadcrumbs :breadcrumbs="mainContent.breadcrumbs" />
|
|
46
|
-
<
|
|
49
|
+
<MainDecoration
|
|
50
|
+
role="preamble-static"
|
|
51
|
+
:decoration="mainContent.decoration"
|
|
52
|
+
/>
|
|
53
|
+
<MainTitle
|
|
54
|
+
icon="book"
|
|
55
|
+
:title="mainContent.title"
|
|
56
|
+
:contentLabel="phrase.book"
|
|
57
|
+
/>
|
|
47
58
|
<MainFlags :flags="mainContent.flags" />
|
|
48
59
|
<MainDescription :description="mainContent.description" />
|
|
49
60
|
<MainConnections :connections="mainContent.connections" />
|
|
@@ -42,10 +42,21 @@ await useContentSeo({
|
|
|
42
42
|
|
|
43
43
|
<template>
|
|
44
44
|
<MainGlow />
|
|
45
|
-
<MainDecoration :decoration="mainContent.decoration" />
|
|
46
45
|
<MainSectionPreamble>
|
|
46
|
+
<MainDecoration
|
|
47
|
+
role="preamble-float"
|
|
48
|
+
:decoration="mainContent.decoration"
|
|
49
|
+
/>
|
|
47
50
|
<MainBreadcrumbs :breadcrumbs="mainContent.breadcrumbs" />
|
|
48
|
-
<
|
|
51
|
+
<MainDecoration
|
|
52
|
+
role="preamble-static"
|
|
53
|
+
:decoration="mainContent.decoration"
|
|
54
|
+
/>
|
|
55
|
+
<MainTitle
|
|
56
|
+
icon="folder-open"
|
|
57
|
+
:title="mainContent.title"
|
|
58
|
+
:contentLabel="phrase.group"
|
|
59
|
+
/>
|
|
49
60
|
<MainFlags :flags="mainContent.flags" />
|
|
50
61
|
<MainDescription :description="mainContent.description" />
|
|
51
62
|
<MainConnections :connections="mainContent.connections" />
|
|
@@ -35,14 +35,27 @@ await useContentSeo({
|
|
|
35
35
|
flags: mainContent.flags,
|
|
36
36
|
connections: mainContent.connections,
|
|
37
37
|
});
|
|
38
|
+
|
|
39
|
+
const phrase = await usePhrases('page');
|
|
38
40
|
</script>
|
|
39
41
|
|
|
40
42
|
<template>
|
|
41
43
|
<MainGlow />
|
|
42
|
-
<MainDecoration :decoration="mainContent.decoration" />
|
|
43
44
|
<MainSectionPreamble>
|
|
45
|
+
<MainDecoration
|
|
46
|
+
role="preamble-float"
|
|
47
|
+
:decoration="mainContent.decoration"
|
|
48
|
+
/>
|
|
44
49
|
<MainBreadcrumbs :breadcrumbs="mainContent.breadcrumbs" />
|
|
45
|
-
<
|
|
50
|
+
<MainDecoration
|
|
51
|
+
role="preamble-static"
|
|
52
|
+
:decoration="mainContent.decoration"
|
|
53
|
+
/>
|
|
54
|
+
<MainTitle
|
|
55
|
+
icon="lines"
|
|
56
|
+
:title="mainContent.title"
|
|
57
|
+
:contentLabel="phrase.page"
|
|
58
|
+
/>
|
|
46
59
|
<MainFlags :flags="mainContent.flags" />
|
|
47
60
|
<MainDescription :description="mainContent.description" />
|
|
48
61
|
<MainKeyLinks mode="single" :elementSnippets="mainContent.snippets" />
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "erudit",
|
|
3
|
-
"version": "4.3.5
|
|
3
|
+
"version": "4.3.5",
|
|
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.5
|
|
28
|
-
"@erudit-js/core": "4.3.5
|
|
29
|
-
"@erudit-js/prose": "4.3.5
|
|
27
|
+
"@erudit-js/cli": "4.3.5",
|
|
28
|
+
"@erudit-js/core": "4.3.5",
|
|
29
|
+
"@erudit-js/prose": "4.3.5",
|
|
30
30
|
"@floating-ui/vue": "^1.1.11",
|
|
31
31
|
"@resvg/resvg-js": "^2.6.2",
|
|
32
32
|
"@tailwindcss/vite": "^4.2.1",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"consola": "^3.4.2",
|
|
36
36
|
"drizzle-kit": "^0.31.10",
|
|
37
37
|
"drizzle-orm": "^0.45.2",
|
|
38
|
-
"esbuild": "^0.
|
|
38
|
+
"esbuild": "^0.28.0",
|
|
39
39
|
"flexsearch": "^0.8.212",
|
|
40
40
|
"glob": "^13.0.6",
|
|
41
41
|
"image-size": "^2.0.2",
|
|
@@ -21,6 +21,9 @@ export async function getContentChildren(
|
|
|
21
21
|
'nav',
|
|
22
22
|
);
|
|
23
23
|
const description = await getContentDescription(childNode.fullId);
|
|
24
|
+
const decoration = await ERUDIT.repository.content.ownDecoration(
|
|
25
|
+
childNode.fullId,
|
|
26
|
+
);
|
|
24
27
|
const elementSnippets = await ERUDIT.repository.content.elementSnippets(
|
|
25
28
|
childNode.fullId,
|
|
26
29
|
);
|
|
@@ -37,6 +40,10 @@ export async function getContentChildren(
|
|
|
37
40
|
child.description = description;
|
|
38
41
|
}
|
|
39
42
|
|
|
43
|
+
if (decoration) {
|
|
44
|
+
child.decoration = decoration;
|
|
45
|
+
}
|
|
46
|
+
|
|
40
47
|
if (keyLinks && keyLinks.length > 0) {
|
|
41
48
|
child.keyLinks = keyLinks;
|
|
42
49
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { eq } from 'drizzle-orm';
|
|
2
2
|
|
|
3
|
+
function buildDecorationPath(fullId: string, extension: string) {
|
|
4
|
+
const navNode = ERUDIT.contentNav.getNodeOrThrow(fullId);
|
|
5
|
+
return 'content/' + navNode.contentRelPath + '/decoration.' + extension;
|
|
6
|
+
}
|
|
7
|
+
|
|
3
8
|
export async function getContentDecoration(fullId: string) {
|
|
4
9
|
const parts = fullId.split('/');
|
|
5
10
|
for (let i = parts.length; i > 0; i--) {
|
|
@@ -11,13 +16,18 @@ export async function getContentDecoration(fullId: string) {
|
|
|
11
16
|
});
|
|
12
17
|
|
|
13
18
|
if (dbContent?.decorationExtension) {
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
'content/' +
|
|
17
|
-
navNode.contentRelPath +
|
|
18
|
-
'/decoration.' +
|
|
19
|
-
dbContent.decorationExtension
|
|
20
|
-
);
|
|
19
|
+
return buildDecorationPath(trimmedId, dbContent.decorationExtension);
|
|
21
20
|
}
|
|
22
21
|
}
|
|
23
22
|
}
|
|
23
|
+
|
|
24
|
+
export async function getContentOwnDecoration(fullId: string) {
|
|
25
|
+
const dbContent = await ERUDIT.db.query.content.findFirst({
|
|
26
|
+
columns: { decorationExtension: true },
|
|
27
|
+
where: eq(ERUDIT.db.schema.content.fullId, fullId),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
if (dbContent?.decorationExtension) {
|
|
31
|
+
return buildDecorationPath(fullId, dbContent.decorationExtension);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -35,6 +35,47 @@ export async function getContentDependencies(fullId: string) {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// Fetch auto deps early so we can merge their unique names into overlapping
|
|
39
|
+
// hard deps. On books/groups the hard dep row itself has no uniqueNames
|
|
40
|
+
// (declared at the container level) while children's auto deps carry the
|
|
41
|
+
// exact element references from prose <Dep> tags.
|
|
42
|
+
const dbAutoDependencies = await ERUDIT.db.query.contentDeps.findMany({
|
|
43
|
+
columns: { toFullId: true, hard: true, uniqueNames: true },
|
|
44
|
+
where: and(
|
|
45
|
+
or(
|
|
46
|
+
eq(ERUDIT.db.schema.contentDeps.fromFullId, fullId),
|
|
47
|
+
like(ERUDIT.db.schema.contentDeps.fromFullId, `${fullId}/%`),
|
|
48
|
+
),
|
|
49
|
+
eq(ERUDIT.db.schema.contentDeps.hard, false),
|
|
50
|
+
),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Merge unique names across rows that share the same toFullId
|
|
54
|
+
// (can happen when a topic and its children both dep on the same target).
|
|
55
|
+
const autoUniqueMap = new Map<string, Set<string>>();
|
|
56
|
+
for (const row of dbAutoDependencies) {
|
|
57
|
+
if (!autoUniqueMap.has(row.toFullId)) {
|
|
58
|
+
autoUniqueMap.set(row.toFullId, new Set());
|
|
59
|
+
}
|
|
60
|
+
if (row.uniqueNames) {
|
|
61
|
+
for (const name of row.uniqueNames.split(',')) {
|
|
62
|
+
autoUniqueMap.get(row.toFullId)!.add(name);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// When an auto dep targets the same content item as a hard dep, the auto
|
|
68
|
+
// dep will be filtered out later. Merge its unique names into the hard dep
|
|
69
|
+
// so the exact element sublisting is not lost.
|
|
70
|
+
for (const [toFullId, autoUniques] of autoUniqueMap) {
|
|
71
|
+
if (hardUniqueMap.has(toFullId)) {
|
|
72
|
+
const hardSet = hardUniqueMap.get(toFullId)!;
|
|
73
|
+
for (const name of autoUniques) {
|
|
74
|
+
hardSet.add(name);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
38
79
|
const hardToFullIds = ERUDIT.contentNav.orderIds(
|
|
39
80
|
externalToFullIds(dbHardDependencies),
|
|
40
81
|
);
|
|
@@ -61,31 +102,6 @@ export async function getContentDependencies(fullId: string) {
|
|
|
61
102
|
|
|
62
103
|
const autoDependencies: ContentAutoDep[] = [];
|
|
63
104
|
|
|
64
|
-
const dbAutoDependencies = await ERUDIT.db.query.contentDeps.findMany({
|
|
65
|
-
columns: { toFullId: true, hard: true, uniqueNames: true },
|
|
66
|
-
where: and(
|
|
67
|
-
or(
|
|
68
|
-
eq(ERUDIT.db.schema.contentDeps.fromFullId, fullId),
|
|
69
|
-
like(ERUDIT.db.schema.contentDeps.fromFullId, `${fullId}/%`),
|
|
70
|
-
),
|
|
71
|
-
eq(ERUDIT.db.schema.contentDeps.hard, false),
|
|
72
|
-
),
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
// Merge unique names across rows that share the same toFullId
|
|
76
|
-
// (can happen when a topic and its children both dep on the same target).
|
|
77
|
-
const autoUniqueMap = new Map<string, Set<string>>();
|
|
78
|
-
for (const row of dbAutoDependencies) {
|
|
79
|
-
if (!autoUniqueMap.has(row.toFullId)) {
|
|
80
|
-
autoUniqueMap.set(row.toFullId, new Set());
|
|
81
|
-
}
|
|
82
|
-
if (row.uniqueNames) {
|
|
83
|
-
for (const name of row.uniqueNames.split(',')) {
|
|
84
|
-
autoUniqueMap.get(row.toFullId)!.add(name);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
105
|
// Skip auto-dependency if a hard dependency from the same source exists
|
|
90
106
|
const autoToFullIds = ERUDIT.contentNav
|
|
91
107
|
.orderIds(externalToFullIds(dbAutoDependencies))
|
|
@@ -16,7 +16,10 @@ import {
|
|
|
16
16
|
getContentHeadingUnique,
|
|
17
17
|
getContentUnique,
|
|
18
18
|
} from './content/repository/unique';
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
getContentDecoration,
|
|
21
|
+
getContentOwnDecoration,
|
|
22
|
+
} from './content/repository/decoration';
|
|
20
23
|
import { getContentFlags } from './content/repository/flags';
|
|
21
24
|
import { getContentConnections } from './content/repository/connections';
|
|
22
25
|
import { getQuoteIds } from './quote/repository/ids';
|
|
@@ -70,6 +73,7 @@ export const repository = {
|
|
|
70
73
|
title: getContentTitle,
|
|
71
74
|
description: getContentDescription,
|
|
72
75
|
decoration: getContentDecoration,
|
|
76
|
+
ownDecoration: getContentOwnDecoration,
|
|
73
77
|
elementSnippets: getContentElementSnippets,
|
|
74
78
|
flags: getContentFlags,
|
|
75
79
|
connections: getContentConnections,
|