diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d5e97b0e1c375dc0700baacc3bc5f9a84fca0374..29f69dff137571064de8173e9fc0bafabef2764d 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 cf7f51d22f26b73235369c51d1179faafe54b310..f580e4c8b290b5f5cb7b42c13076cc062341253c 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 0000000000000000000000000000000000000000..f66b8e8e6aa59c393fc10bde3c55000fcffca21a --- /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 0000000000000000000000000000000000000000..62823929718b0ffbd8a08ad8f419db08e79b4aea --- /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 0000000000000000000000000000000000000000..4d804852ccd50256b40ef214455e6911f7ee9bab --- /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 0000000000000000000000000000000000000000..f9e390e280b399f061e2d49c7e374b129d5a41de --- /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 0000000000000000000000000000000000000000..c5fc34a2bbe45c1f965f230e3ee569bf7dbfb354 --- /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 3cd6c619dca41bba746e4cbb37bd42647e6ffc70..ea79d24030e5faaeacba3c78f8caf8bafb9c9c40 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 fe7727a672fbe67a3741e520b42d97b36550dc03..357e90ee40bc9dc5a3ac068918d372e8a3091926 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 5c173d624212826b6e80db4755ee37639d1178ae..e79060863111661627017e26de255e35f3a2cc70 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 0000000000000000000000000000000000000000..006afc08445f05b49419d17bac3dc15f0df40cac --- /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 63439bd4e5e1af4ec70de0c0c062955ab7a101b0..a829bc4a2ab8b1ca7e9375119dcf18e0189d73e3 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 808a7367c8c6f20bb252a410e57a5cec2b8d6a54..7e1d4617a877197aec2c7a5405fa0227afffc01d 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: -{max-width=750px} + 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 %). -{max-width=750px} + *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 3e5d554c5cc9e22ff35d189a3ad04e22809a0dd7..3de86ab7e5ceebfb8521761e72cdbcdba832ccd7 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 %). {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 b715ca6408329a016b39d8cb29c7cd92d0152a84..028de448103f457b1351c16bebf4275d91030af0 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 2eac5520d11c49de27cc5e7662abf045e856d437..2a61a770fa262195880939a9c98360e6b615a732 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 0f843c65913eb850e528d7653738a8deb4ca9e55..19dad94f6b91ee1e41275d3b50c32c772861a4b7 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