From eb94970a1d46ff24e996878d90f23c86c4aaced8 Mon Sep 17 00:00:00 2001
From: Remi  PLANEL <rplanel@pasteur.fr>
Date: Fri, 27 Oct 2023 10:33:01 +0200
Subject: [PATCH] Resolve "footnote markdown"

---
 .gitlab-ci.yml                                |  1 -
 components/Nav/Navbar.vue                     | 16 -----
 components/content/Alert/Danger.vue           | 15 +++++
 components/content/Alert/ExpansionDetails.vue | 29 ++++++++++
 components/content/Alert/Info.vue             | 15 +++++
 components/content/Alert/Tip.vue              | 15 +++++
 components/content/Alert/Warning.vue          | 14 +++++
 components/content/ArticleDoi.vue             |  1 +
 components/content/ArticleDoiList.vue         | 24 ++++++--
 components/content/Contributors.vue           |  2 +-
 components/content/Ref.vue                    | 17 ++++++
 composables/useFetchArticle.ts                |  2 +
 content/3.defense-systems/abi2.md             |  9 ++-
 content/3.defense-systems/abia.md             | 21 +++----
 content/3.defense-systems/detocs.md           | 15 +----
 nuxt.config.ts                                | 10 ++++
 server/plugins/content.ts                     | 58 +++++++++++--------
 17 files changed, 187 insertions(+), 77 deletions(-)
 create mode 100644 components/content/Alert/Danger.vue
 create mode 100644 components/content/Alert/ExpansionDetails.vue
 create mode 100644 components/content/Alert/Info.vue
 create mode 100644 components/content/Alert/Tip.vue
 create mode 100644 components/content/Alert/Warning.vue
 create mode 100644 components/content/Ref.vue

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d5e97b0e..29f69dff 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -27,7 +27,6 @@ stages:
     BASE_URL: /wiki/
     MEILI_HOST: "http://localhost:7700"
     MEILI_API_KEY: MASTER_KEY
-    CI_DEBUG_TRACE: "true"
   before_script:
     - i=0; while [ "$i" -lt 12 ]; do docker info && break; sleep 5; i=$(( i + 1 )) ; done
     - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
diff --git a/components/Nav/Navbar.vue b/components/Nav/Navbar.vue
index cf7f51d2..f580e4c8 100644
--- a/components/Nav/Navbar.vue
+++ b/components/Nav/Navbar.vue
@@ -13,8 +13,6 @@ function toggleTheme() {
   theme.global.name.value = theme.global.current.value.dark ? "light" : "dark";
 }
 
-
-
 const sections = ref([
   {
     id: "webservice",
@@ -27,20 +25,6 @@ const sections = ref([
 ]);
 
 
-// const computedSections = computed(() => {
-//   return sections.value.map(section => {
-
-//     if (section?.to) {
-//       const { refinedUrl } = useRefinedUrl(section.to)
-//       return { ...section, to: refinedUrl.value }
-//     }
-//     else {
-//       return section
-//     }
-
-
-//   })
-// })
 
 
 const drawer = ref(true);
diff --git a/components/content/Alert/Danger.vue b/components/content/Alert/Danger.vue
new file mode 100644
index 00000000..f66b8e8e
--- /dev/null
+++ b/components/content/Alert/Danger.vue
@@ -0,0 +1,15 @@
+<script setup lang="ts">
+export interface Props {
+    title?: string
+}
+
+const { title } = withDefaults(defineProps<Props>(), {
+    title: 'Danger'
+});
+</script>
+
+<template>
+    <v-alert type="error" border="start" variant="tonal" class="my-2" prominent :title="title">
+        <slot></slot>
+    </v-alert>
+</template>
diff --git a/components/content/Alert/ExpansionDetails.vue b/components/content/Alert/ExpansionDetails.vue
new file mode 100644
index 00000000..62823929
--- /dev/null
+++ b/components/content/Alert/ExpansionDetails.vue
@@ -0,0 +1,29 @@
+<script setup lang="ts">
+export interface Props {
+    title?: string
+}
+
+const { title } = withDefaults(defineProps<Props>(), {
+    title: 'Details'
+});
+
+
+import { useTheme } from "vuetify";
+const theme = useTheme();
+
+
+</script>
+<template>
+    <div>
+        <v-expansion-panels>
+            <v-expansion-panel>
+                <v-expansion-panel-title class=" text-h6">
+                    {{ title }}
+                </v-expansion-panel-title>
+                <v-expansion-panel-text>
+                    <slot></slot>
+                </v-expansion-panel-text>
+            </v-expansion-panel>
+        </v-expansion-panels>
+    </div>
+</template>
diff --git a/components/content/Alert/Info.vue b/components/content/Alert/Info.vue
new file mode 100644
index 00000000..4d804852
--- /dev/null
+++ b/components/content/Alert/Info.vue
@@ -0,0 +1,15 @@
+<script setup lang="ts">
+export interface Props {
+    title?: string
+}
+
+const { title } = withDefaults(defineProps<Props>(), {
+    title: 'Info'
+});
+</script>
+
+<template>
+    <v-alert type="info" border="start" variant="tonal" class="my-2" prominent :title="title">
+        <slot></slot>
+    </v-alert>
+</template>
diff --git a/components/content/Alert/Tip.vue b/components/content/Alert/Tip.vue
new file mode 100644
index 00000000..f9e390e2
--- /dev/null
+++ b/components/content/Alert/Tip.vue
@@ -0,0 +1,15 @@
+<script setup lang="ts">
+export interface Props {
+    title?: string
+}
+
+const { title } = withDefaults(defineProps<Props>(), {
+    title: 'Tip'
+});
+</script>
+<template>
+    <v-alert color="primary" border-color="primary" border="start" variant="tonal" class="my-2" prominent :title="title"
+        icon="md:lightbulb">
+        <slot></slot>
+    </v-alert>
+</template>
diff --git a/components/content/Alert/Warning.vue b/components/content/Alert/Warning.vue
new file mode 100644
index 00000000..c5fc34a2
--- /dev/null
+++ b/components/content/Alert/Warning.vue
@@ -0,0 +1,14 @@
+<script setup lang="ts">
+export interface Props {
+    title?: string
+}
+
+const { title } = withDefaults(defineProps<Props>(), {
+    title: 'Warning'
+});
+</script>
+<template>
+    <v-alert type="warning" border="start" variant="tonal" class="my-2" prominent :title="title">
+        <slot></slot>
+    </v-alert>
+</template>
diff --git a/components/content/ArticleDoi.vue b/components/content/ArticleDoi.vue
index 3cd6c619..ea79d240 100644
--- a/components/content/ArticleDoi.vue
+++ b/components/content/ArticleDoi.vue
@@ -28,6 +28,7 @@ const articleAbstract = computed(() => {
 <template>
   <v-list-item
     :href="article?.href"
+    :id="props.doi"
     :target="article?.target"
     density="compact"
     class="px-1"
diff --git a/components/content/ArticleDoiList.vue b/components/content/ArticleDoiList.vue
index fe7727a6..357e90ee 100644
--- a/components/content/ArticleDoiList.vue
+++ b/components/content/ArticleDoiList.vue
@@ -1,11 +1,25 @@
 <script setup lang="ts">
-const props = defineProps<{
-    items: { doi: string, title?: string, divider: boolean, abstract?: string }[];
-}>();
+import ProseH2 from '~/components/content/ProseH2'
+// const props = defineProps<{
+//   items: { doi: string; title?: string; divider: boolean; abstract?: string }[];
+// }>();
+
+const { page } = useContent();
+const computedDois = computed(() => {
+  if (page.value?.relevantAbstracts) {
+    return page.value.relevantAbstracts;
+  } else {
+    return [];
+  }
+});
 </script>
 <template>
+  <div v-if="computedDois?.length > 0">
+    <ProseH2 id="relevant-abstracts">Relevant abstracts</ProseH2>
+
     <v-list density="compact">
-        <ArticleDoi v-for="item, index in props.items" :key="item.doi" :index="index + 1" :doi="item.doi"
-            :title="item?.title" :divider="item.divider" :abstract="item?.abstract" />
+      <ArticleDoi v-for="(item, index) in computedDois" :key="item.doi" :index="index + 1" :doi="item.doi"
+        :title="item?.title" :divider="item.divider" :abstract="item?.abstract" />
     </v-list>
+  </div>
 </template>
\ No newline at end of file
diff --git a/components/content/Contributors.vue b/components/content/Contributors.vue
index 5c173d62..e7906086 100644
--- a/components/content/Contributors.vue
+++ b/components/content/Contributors.vue
@@ -9,6 +9,6 @@ const contributorsString = computed(() => {
 
 </script>
 <template>
-    <div v-if="contributorsString" class="mt-n6 mb-6 text-subtitle-1 text-info">Contributors: {{ contributorsString }}
+    <div v-if="contributorsString" class="mt-n5 mb-6 text-subtitle-1 text-info">Contributors: {{ contributorsString }}
     </div>
 </template>
\ No newline at end of file
diff --git a/components/content/Ref.vue b/components/content/Ref.vue
new file mode 100644
index 00000000..006afc08
--- /dev/null
+++ b/components/content/Ref.vue
@@ -0,0 +1,17 @@
+<script setup lang="ts">
+export interface Props {
+  doi: string;
+}
+const props = withDefaults(defineProps<Props>(), {});
+const { article } = useFetchArticle(props.doi);
+</script>
+<template>
+  <v-chip
+    v-if="article"
+    variant="text"
+    :href="`#${props.doi}`"
+    class="pa-0 text-caption font-italic"
+    >({{ article?.author[0]?.family ?? "test" }} et al,
+    {{ article?.year }})</v-chip
+  >
+</template>
\ No newline at end of file
diff --git a/composables/useFetchArticle.ts b/composables/useFetchArticle.ts
index 63439bd4..a829bc4a 100644
--- a/composables/useFetchArticle.ts
+++ b/composables/useFetchArticle.ts
@@ -25,6 +25,7 @@ export interface Article {
     DOI: string
     title: string
     subtitle: string
+    author: Array<{ family: string; given: string }>
     containerTitle: string
     abstract: string
     year: string
@@ -74,6 +75,7 @@ export function useFetchArticle(doi: string) {
                 DOI,
                 title: sanitizedTitle,
                 subtitle: toAuthorsString(author || []),
+                author,
                 containerTitle: sanitizedContainerTitle,
                 abstract: sanitizedAbstract,
                 year: published?.["date-parts"][0][0] ?? issued?.["date-parts"][0][0] ?? '',
diff --git a/content/3.defense-systems/abi2.md b/content/3.defense-systems/abi2.md
index 808a7367..7e1d4617 100644
--- a/content/3.defense-systems/abi2.md
+++ b/content/3.defense-systems/abi2.md
@@ -9,6 +9,8 @@ tableColumns:
     Activator: ''
     Effector: ''
     PFAM: PF07751
+relevantAbstracts:
+  - doi: 10.1016/j.mib.2005.06.006
 ---
 
 # Abi2
@@ -17,7 +19,7 @@ The Abi2 system is composed of one protein: Abi_2.
 
 Here is an example found in the RefSeq database: 
 
-![abi2](/abi2/Abi2.svg){max-width=750px}
+![abi2](/abi2/Abi2.svg)
 
 Abi2 system in the genome of *Clostridium butyricum* (GCF_014131795.1) is composed of 1 protein: Abi_2 (WP_035763709.1).
 
@@ -27,10 +29,7 @@ The Abi2 system is present in a total of 176 different species.
 
 Among the 22k complete genomes of RefSeq, this system is present in 1210 genomes (5.3 %).
 
-![abi2](/abi2/Distribution_Abi2.svg){max-width=750px}
+![abi2](/abi2/Distribution_Abi2.svg)
 
 *Proportion of genome encoding the Abi2 system for the 14 phyla with more than 50 genomes in the RefSeq database.* 
 
-## Relevant abstracts
-
-
diff --git a/content/3.defense-systems/abia.md b/content/3.defense-systems/abia.md
index 3e5d554c..3de86ab7 100644
--- a/content/3.defense-systems/abia.md
+++ b/content/3.defense-systems/abia.md
@@ -9,9 +9,14 @@ tableColumns:
     Activator: Unknown
     Effector: Unknown
     PFAM: PF00078, PF18160, PF18732
+relevantAbstracts:
+    - doi: 10.1023/A:1002027321171
+    - doi: 10.1016/j.mib.2005.06.006
+    - doi: 10.1093/nar/gkac467
 ---
 
-# AbiA
+# AbiA 
+
 The AbiA system have been describe in a total of 2 subsystems.
 
 Here is some example found in the RefSeq database:
@@ -24,10 +29,12 @@ AbiA_large subsystem in the genome of *Lactobacillus amylovorus* (GCF_002706375.
 
 AbiA_small subsystem in the genome of *Mesobacillus foraminis* (GCF_003667765.1) is composed of 2 proteins: AbiA_small (WP_121614402.1)and, AbiA_SLATT (WP_121614403.1).
 
-## Distribution of the system among prokaryotes
+## Distribution of the system among prokaryotes 
 
 The AbiA system is present in a total of 35 different species.
 
+
+
 Among the 22k complete genomes of RefSeq, this system is present in 50 genomes (0.2 %).
 
 ![abia](/abia/Distribution_AbiA.svg){max-width=750px}
@@ -67,15 +74,5 @@ AbiA systems were experimentally validated using:
 
 A system from *lactococcal plasmid* in *lactococci* has an anti-phage effect against 936, c2, P335 (Chopin et al., 2005)
 
-## Relevant abstracts
 
-::article-doi-list
----
-items:
-    - doi: 10.1023/A:1002027321171
-    - doi: 10.1016/j.mib.2005.06.006
-    - doi: 10.1093/nar/gkac467
-
----
-::
 
diff --git a/content/3.defense-systems/detocs.md b/content/3.defense-systems/detocs.md
index b715ca64..028de448 100644
--- a/content/3.defense-systems/detocs.md
+++ b/content/3.defense-systems/detocs.md
@@ -6,6 +6,8 @@ tableColumns:
       abstract: |
         During viral infection, cells can deploy immune strategies that deprive viruses of molecules essential for their replication. Here, we report a family of immune effectors in bacteria that, upon phage infection, degrade cellular adenosine triphosphate (ATP) and deoxyadenosine triphosphate (dATP) by cleaving the N-glycosidic bond between the adenine and sugar moieties. These ATP nucleosidase effectors are widely distributed within multiple bacterial defense systems, including cyclic oligonucleotide-based antiviral signaling systems (CBASS), prokaryotic argonautes, and nucleotide-binding leucine-rich repeat (NLR)-like proteins, and we show that ATP and dATP degradation during infection halts phage propagation. By analyzing homologs of the immune ATP nucleosidase domain, we discover and characterize Detocs, a family of bacterial defense systems with a two-component phosphotransfer-signaling architecture. The immune ATP nucleosidase domain is also encoded within diverse eukaryotic proteins with immune-like architectures, and we show biochemically that eukaryotic homologs preserve the ATP nucleosidase activity. Our findings suggest that ATP and dATP degradation is a cell-autonomous innate immune strategy conserved across the tree of life.
     PFAM: PF01048, PF18742
+relevantAbstracts: 
+    - doi: 10.1016/j.cell.2023.07.020
 contributors: 
     - François Rousset
 ---
@@ -83,16 +85,3 @@ dataUrl: /detocs/Detocs_hydrolase__dtcC-plddts_89.47253.pdb
 ---
 ::
 
-## Relevant abstract
-::article-doi-list
----
-items:
-    - doi: 10.1016/j.cell.2023.07.020
-
----
-::
-
-
-## References
-
-Rousset et al., A conserved family of immune effectors cleaves cellular ATP upon viral infection. Cell 186, 1–13 (2023). https://doi.org/10.1016/j.cell.2023.07.020
diff --git a/nuxt.config.ts b/nuxt.config.ts
index 2eac5520..2a61a770 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -11,6 +11,16 @@ export default defineNuxtConfig({
   content: {
     documentDriven: {
       injectPage: false,
+    },
+    highlight: {
+      theme: {
+        // Default theme (same as single string)        
+        default: 'github-light',
+        // Theme used if `html.dark`        
+        dark: 'github-dark',
+        // Theme used if `html.sepia`        
+        sepia: 'monokai'
+      }
     }
   },
   vuetify: {
diff --git a/server/plugins/content.ts b/server/plugins/content.ts
index 0f843c65..19dad94f 100644
--- a/server/plugins/content.ts
+++ b/server/plugins/content.ts
@@ -4,34 +4,44 @@ export default defineNitroPlugin((nitroApp) => {
     nitroApp.hooks.hook('content:file:beforeParse',
         (file) => {
             if (file?._id?.startsWith('content:3.defense-systems:') && file?._id?.endsWith('.md')) {
-                const regexp = /(?<=---\n).*?(?=\n---)/sg;
-                const match = file.body.match(regexp);
-                const frontMatter = match[0]
-                const parsedFrontMatter = YAML.parse(frontMatter)
-                if (parsedFrontMatter?.contributors?.length > 0) {
-                    file.body = file.body.replace(/(^#[\s+]\w*[\s\S])/gm, "$1:contributors\n\n")
+                const frontMatterRegex = /(?<=---\n).*?(?=\n---)/sg;
+
+                const fontMatterMatch = file.body.match(frontMatterRegex);
+                if (fontMatterMatch?.length > 0) {
+                    const frontMatter = fontMatterMatch[0]
+                    const parsedFrontMatter = YAML.parse(frontMatter)
+                    if (parsedFrontMatter?.contributors?.length > 0) {
+                        file.body = file.body.replace(/(^#[\s+]\w*[\s\S])/gm, "$1\n:contributors\n\n")
+                    }
+                    // try to look at :ref{doi=xxxx}
+                    const matchRef = file.body.match(/(?<=:ref{doi=).*(?=})/gm)
+                    if (matchRef?.length > 0) {
+                        if (parsedFrontMatter?.relevantAbstracts) {
+                            const relevantAbstractsSet = new Set(parsedFrontMatter.relevantAbstracts.map(({ doi }: { doi: string }) => doi))
+                            const filteredMatchRef = matchRef.filter((doi: string) => !relevantAbstractsSet.has(doi))
+                            parsedFrontMatter.relevantAbstracts = [...parsedFrontMatter.relevantAbstracts, ...filteredMatchRef.map((doi: string) => ({ doi }))]
+                        } else {
+                            parsedFrontMatter.relevantAbstracts = matchRef.map((doi: string) => ({ doi }))
+                        }
+                        const newFrontMatterStr = YAML.stringify(parsedFrontMatter)
+                        file.body = file.body.replace(/---\n(.*?)\n---/gs, `---\n${newFrontMatterStr}\n---`)
+                    }
 
                 }
             }
         })
 
 
-    // nitroApp.hooks.hook(
-    //     'content:file:afterParse',
-    //     (file) => {
-    //         if (file._id.endsWith('.md')) {
-
-    //             file.body.children.push({
-    //                 type: "element",
-    //                 tag: 'article-doi-list',
-    //                 props: {
-    //                     items: [
-    //                         { doi: '10.1016/j.mib.2005.06.006' },
-    //                         { doi: '10.1016/j.mib.2005.06.006' }]
-    //                 },
-    //                 children: []
-    //             })
-
-    //         }
-    //     })
+    nitroApp.hooks.hook(
+        'content:file:afterParse',
+        (file) => {
+            if (file?._id?.startsWith('content:3.defense-systems:') && file._id.endsWith('.md')) {
+                file.body.children.push({
+                    type: "element",
+                    tag: 'article-doi-list',
+                    props: {},
+                    children: []
+                })
+            }
+        })
 })
\ No newline at end of file
-- 
GitLab