From 20fcabaf469debe35670268dcfcb4c376d776b5a Mon Sep 17 00:00:00 2001 From: Tellsanguis Date: Wed, 3 Dec 2025 11:57:18 +0100 Subject: [PATCH] Ajout page Tags pour recherche articles par sujet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Créer page Tags unifiée affichant tous les tags des articles blog et docs - Mettre à jour tags dans toutes les pages pour être plus pertinents et recherchables --- blog/2025-01-14-bienvenue.md | 2 +- blog/2025-11-22-stockage-distribue-proxmox.md | 2 +- ...5-11-26-linstor-drbd-opentofu-problemes.md | 2 +- ...-30-reduire-taille-disque-vm-clonezilla.md | 2 +- .../2025-12-02-ssh-freeze-mtu-mss-clamping.md | 2 +- docs/homelab-actuel/docker-compose.md | 1 + docs/homelab-actuel/playbooks-ansible.md | 1 + docs/homelab-actuel/traefik.md | 1 + .../homelab-futur/cluster-3-noeuds-proxmox.md | 1 + .../premiere-version-ha-monomachine.md | 1 + docs/openwrt/backhaul-wifi-mesh.md | 1 + docs/openwrt/gretap-vlan.md | 1 + docusaurus.config.ts | 2 + i18n/en/code.json | 36 +++ .../2025-01-14-bienvenue.md | 2 +- .../2025-11-22-stockage-distribue-proxmox.md | 2 +- ...5-11-26-linstor-drbd-opentofu-problemes.md | 2 +- ...-30-reduire-taille-disque-vm-clonezilla.md | 2 +- .../2025-12-02-ssh-freeze-mtu-mss-clamping.md | 2 +- .../docusaurus-plugin-unified-tags/index.js | 132 +++++++++ src/pages/tags/index.tsx | 86 ++++++ src/pages/tags/styles.module.css | 264 ++++++++++++++++++ src/theme/TagPage.tsx | 146 ++++++++++ 23 files changed, 683 insertions(+), 10 deletions(-) create mode 100644 plugins/docusaurus-plugin-unified-tags/index.js create mode 100644 src/pages/tags/index.tsx create mode 100644 src/pages/tags/styles.module.css create mode 100644 src/theme/TagPage.tsx diff --git a/blog/2025-01-14-bienvenue.md b/blog/2025-01-14-bienvenue.md index 98ed8e4..aacb7bc 100644 --- a/blog/2025-01-14-bienvenue.md +++ b/blog/2025-01-14-bienvenue.md @@ -2,7 +2,7 @@ slug: bienvenue title: Bienvenue sur TellServ Tech Blog authors: [tellserv] -tags: [introduction, blog] +tags: [homelab, devops, infrastructure, cloud] --- # Bienvenue sur TellServ Tech Blog diff --git a/blog/2025-11-22-stockage-distribue-proxmox.md b/blog/2025-11-22-stockage-distribue-proxmox.md index b9d0dff..40d6cd4 100644 --- a/blog/2025-11-22-stockage-distribue-proxmox.md +++ b/blog/2025-11-22-stockage-distribue-proxmox.md @@ -2,7 +2,7 @@ slug: stockage-distribue-proxmox-ha title: "Choisir sa technologie de stockage distribué pour un cluster Proxmox HA" authors: [tellserv] -tags: [proxmox, stockage, ha, linstor, drbd, ceph, zfs, homelab] +tags: [proxmox, high-availability, linstor, drbd, ceph, zfs, storage, clustering, homelab] --- # Choisir sa technologie de stockage distribué pour un cluster Proxmox HA diff --git a/blog/2025-11-26-linstor-drbd-opentofu-problemes.md b/blog/2025-11-26-linstor-drbd-opentofu-problemes.md index befd10c..4ffedd7 100644 --- a/blog/2025-11-26-linstor-drbd-opentofu-problemes.md +++ b/blog/2025-11-26-linstor-drbd-opentofu-problemes.md @@ -2,7 +2,7 @@ slug: linstor-drbd-opentofu-problemes title: "Déploiement avec OpenTofu sur LINSTOR DRBD : le début des problèmes" authors: [tellserv] -tags: [homelab, proxmox, linstor, drbd, opentofu, terraform, iac, gitops, kubernetes] +tags: [opentofu, terraform, iac, infrastructure-as-code, proxmox, linstor, drbd, automation, devops] date: 2025-11-26 --- diff --git a/blog/2025-11-30-reduire-taille-disque-vm-clonezilla.md b/blog/2025-11-30-reduire-taille-disque-vm-clonezilla.md index 10cce68..e4fbe47 100644 --- a/blog/2025-11-30-reduire-taille-disque-vm-clonezilla.md +++ b/blog/2025-11-30-reduire-taille-disque-vm-clonezilla.md @@ -2,7 +2,7 @@ slug: reduire-taille-disque-vm-clonezilla title: Réduire la taille du disque d'une VM avec Clonezilla authors: [tellserv] -tags: [proxmox, clonezilla, vm, stockage, linstor-drbd] +tags: [proxmox, clonezilla, virtualization, storage, linstor, drbd, disk-management] image: /img/blog/2025-11-30-reduire-disque-vm/clonezilla-logo.svg --- diff --git a/blog/2025-12-02-ssh-freeze-mtu-mss-clamping.md b/blog/2025-12-02-ssh-freeze-mtu-mss-clamping.md index 079bef5..b378efb 100644 --- a/blog/2025-12-02-ssh-freeze-mtu-mss-clamping.md +++ b/blog/2025-12-02-ssh-freeze-mtu-mss-clamping.md @@ -2,7 +2,7 @@ slug: ssh-freeze-mtu-mss-clamping title: Freeze de session SSH, MTU et MSS clamping authors: [tellserv] -tags: [openwrt, gretap, ssh, mtu, networking] +tags: [openwrt, networking, ssh, mtu, mss-clamping, tunneling, gretap, troubleshooting] image: /img/blog/2025-12-02-ssh-freeze-mtu/freeze_session_ssh.png --- diff --git a/docs/homelab-actuel/docker-compose.md b/docs/homelab-actuel/docker-compose.md index f25e2aa..ff5fde0 100644 --- a/docs/homelab-actuel/docker-compose.md +++ b/docs/homelab-actuel/docker-compose.md @@ -1,5 +1,6 @@ --- sidebar_position: 3 +tags: [docker, docker-compose, conteneurisation, homelab] --- # Docker et Docker Compose diff --git a/docs/homelab-actuel/playbooks-ansible.md b/docs/homelab-actuel/playbooks-ansible.md index 4db7ee0..9bef051 100644 --- a/docs/homelab-actuel/playbooks-ansible.md +++ b/docs/homelab-actuel/playbooks-ansible.md @@ -1,5 +1,6 @@ --- sidebar_position: 2 +tags: [ansible, automation, iac, configuration-management, homelab] --- # Playbooks Ansible diff --git a/docs/homelab-actuel/traefik.md b/docs/homelab-actuel/traefik.md index 78379ca..4bca3a2 100644 --- a/docs/homelab-actuel/traefik.md +++ b/docs/homelab-actuel/traefik.md @@ -1,5 +1,6 @@ --- sidebar_position: 4 +tags: [traefik, docker, reverse-proxy, ssl, crowdsec, homelab] --- # Traefik - Reverse Proxy moderne diff --git a/docs/homelab-futur/cluster-3-noeuds-proxmox.md b/docs/homelab-futur/cluster-3-noeuds-proxmox.md index 7558585..61fa2c2 100644 --- a/docs/homelab-futur/cluster-3-noeuds-proxmox.md +++ b/docs/homelab-futur/cluster-3-noeuds-proxmox.md @@ -1,5 +1,6 @@ --- sidebar_position: 3 +tags: [proxmox, cluster, ha, homelab] --- # Cluster 3 noeuds Proxmox : un véritable Homelab HA diff --git a/docs/homelab-futur/premiere-version-ha-monomachine.md b/docs/homelab-futur/premiere-version-ha-monomachine.md index f62792e..4b79ad9 100644 --- a/docs/homelab-futur/premiere-version-ha-monomachine.md +++ b/docs/homelab-futur/premiere-version-ha-monomachine.md @@ -1,5 +1,6 @@ --- sidebar_position: 2 +tags: [proxmox, kubernetes, k3s, ha, homelab] --- # Première version : le Homelab "HA" monomachine (projet initial) diff --git a/docs/openwrt/backhaul-wifi-mesh.md b/docs/openwrt/backhaul-wifi-mesh.md index 0f17031..cb0f682 100644 --- a/docs/openwrt/backhaul-wifi-mesh.md +++ b/docs/openwrt/backhaul-wifi-mesh.md @@ -1,6 +1,7 @@ --- sidebar_position: 2 title: Backhaul WiFi Mesh avec 802.11s +tags: [openwrt, wifi, mesh-networking, wireless, networking] --- # Backhaul WiFi Mesh avec 802.11s diff --git a/docs/openwrt/gretap-vlan.md b/docs/openwrt/gretap-vlan.md index 44226d5..16b7ca4 100644 --- a/docs/openwrt/gretap-vlan.md +++ b/docs/openwrt/gretap-vlan.md @@ -1,6 +1,7 @@ --- sidebar_position: 3 title: Tunnels GREtap pour VLANs +tags: [openwrt, gretap, vlan, networking, tunneling] --- # Tunnels GREtap pour VLANs à travers le mesh diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 9e63b47..5691216 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -23,6 +23,7 @@ const config: Config = { plugins: [ 'docusaurus-plugin-image-zoom', + './plugins/docusaurus-plugin-unified-tags', ], title: 'TellServ Tech Blog', @@ -125,6 +126,7 @@ const config: Config = { label: 'Documentation', }, {to: '/blog', label: 'Blog', position: 'left'}, + {to: '/tags', label: 'Tags', position: 'left'}, { type: 'localeDropdown', position: 'right', diff --git a/i18n/en/code.json b/i18n/en/code.json index 555d459..6659b5b 100644 --- a/i18n/en/code.json +++ b/i18n/en/code.json @@ -51,5 +51,41 @@ "homepage.feature3.description": { "message": "Sharing experiences and solutions with the community.", "description": "Description of feature 3 (knowledge sharing) on the homepage" + }, + "tags.page.title": { + "message": "Tags", + "description": "Tags page title" + }, + "tags.page.description": { + "message": "Browse content by tags", + "description": "Tags page meta description" + }, + "tags.page.heading": { + "message": "Tags", + "description": "Tags page main heading" + }, + "tags.filtered.title": { + "message": "{count} posts tagged with \"{tagName}\"", + "description": "Filtered tag page title" + }, + "tags.filtered.description": { + "message": "Browse all content tagged with {tagName}", + "description": "Filtered tag page meta description" + }, + "tags.filtered.heading": { + "message": "{count} posts tagged with \"{tagName}\"", + "description": "Filtered tag page heading" + }, + "tags.viewAll": { + "message": "View All Tags", + "description": "View all tags link" + }, + "tags.notFound.title": { + "message": "Tag Not Found", + "description": "Tag not found title" + }, + "tags.notFound.description": { + "message": "The tag you are looking for does not exist.", + "description": "Tag not found description" } } diff --git a/i18n/en/docusaurus-plugin-content-blog/2025-01-14-bienvenue.md b/i18n/en/docusaurus-plugin-content-blog/2025-01-14-bienvenue.md index 91d5faf..0720abc 100644 --- a/i18n/en/docusaurus-plugin-content-blog/2025-01-14-bienvenue.md +++ b/i18n/en/docusaurus-plugin-content-blog/2025-01-14-bienvenue.md @@ -2,7 +2,7 @@ slug: bienvenue title: Welcome to TellServ Tech Blog authors: [tellserv] -tags: [introduction, blog] +tags: [homelab, devops, infrastructure, cloud] --- # Welcome to TellServ Tech Blog diff --git a/i18n/en/docusaurus-plugin-content-blog/2025-11-22-stockage-distribue-proxmox.md b/i18n/en/docusaurus-plugin-content-blog/2025-11-22-stockage-distribue-proxmox.md index c6a76ec..36fa7ab 100644 --- a/i18n/en/docusaurus-plugin-content-blog/2025-11-22-stockage-distribue-proxmox.md +++ b/i18n/en/docusaurus-plugin-content-blog/2025-11-22-stockage-distribue-proxmox.md @@ -2,7 +2,7 @@ slug: stockage-distribue-proxmox-ha title: "Choosing a Distributed Storage Technology for a Proxmox HA Cluster" authors: [tellserv] -tags: [proxmox, storage, ha, linstor, drbd, ceph, zfs, homelab] +tags: [proxmox, high-availability, linstor, drbd, ceph, zfs, storage, clustering, homelab] --- # Choosing a Distributed Storage Technology for a Proxmox HA Cluster diff --git a/i18n/en/docusaurus-plugin-content-blog/2025-11-26-linstor-drbd-opentofu-problemes.md b/i18n/en/docusaurus-plugin-content-blog/2025-11-26-linstor-drbd-opentofu-problemes.md index 2938d89..a0b0493 100644 --- a/i18n/en/docusaurus-plugin-content-blog/2025-11-26-linstor-drbd-opentofu-problemes.md +++ b/i18n/en/docusaurus-plugin-content-blog/2025-11-26-linstor-drbd-opentofu-problemes.md @@ -2,7 +2,7 @@ slug: linstor-drbd-opentofu-problemes title: "Deploying with OpenTofu on LINSTOR DRBD: the beginning of problems" authors: [tellserv] -tags: [homelab, proxmox, linstor, drbd, opentofu, terraform, iac, gitops, kubernetes] +tags: [opentofu, terraform, iac, infrastructure-as-code, proxmox, linstor, drbd, automation, devops] date: 2025-11-26 --- diff --git a/i18n/en/docusaurus-plugin-content-blog/2025-11-30-reduire-taille-disque-vm-clonezilla.md b/i18n/en/docusaurus-plugin-content-blog/2025-11-30-reduire-taille-disque-vm-clonezilla.md index c6aad2d..c5d7a6b 100644 --- a/i18n/en/docusaurus-plugin-content-blog/2025-11-30-reduire-taille-disque-vm-clonezilla.md +++ b/i18n/en/docusaurus-plugin-content-blog/2025-11-30-reduire-taille-disque-vm-clonezilla.md @@ -2,7 +2,7 @@ slug: reduire-taille-disque-vm-clonezilla title: Reducing VM Disk Size with Clonezilla authors: [tellserv] -tags: [proxmox, clonezilla, vm, storage, linstor-drbd] +tags: [proxmox, clonezilla, virtualization, storage, linstor, drbd, disk-management] image: /img/blog/2025-11-30-reduire-disque-vm/clonezilla-logo.svg --- diff --git a/i18n/en/docusaurus-plugin-content-blog/2025-12-02-ssh-freeze-mtu-mss-clamping.md b/i18n/en/docusaurus-plugin-content-blog/2025-12-02-ssh-freeze-mtu-mss-clamping.md index 14ad383..c53da14 100644 --- a/i18n/en/docusaurus-plugin-content-blog/2025-12-02-ssh-freeze-mtu-mss-clamping.md +++ b/i18n/en/docusaurus-plugin-content-blog/2025-12-02-ssh-freeze-mtu-mss-clamping.md @@ -2,7 +2,7 @@ slug: ssh-freeze-mtu-mss-clamping title: SSH Session Freeze, MTU and MSS Clamping authors: [tellserv] -tags: [openwrt, gretap, ssh, mtu, networking] +tags: [openwrt, networking, ssh, mtu, mss-clamping, tunneling, gretap, troubleshooting] image: /img/blog/2025-12-02-ssh-freeze-mtu/freeze_session_ssh.png --- diff --git a/plugins/docusaurus-plugin-unified-tags/index.js b/plugins/docusaurus-plugin-unified-tags/index.js new file mode 100644 index 0000000..9d50abe --- /dev/null +++ b/plugins/docusaurus-plugin-unified-tags/index.js @@ -0,0 +1,132 @@ +const path = require('path'); +const fs = require('fs-extra'); + +module.exports = function pluginUnifiedTags(context, options) { + return { + name: 'docusaurus-plugin-unified-tags', + + async allContentLoaded({actions, allContent}) { + const {addRoute} = actions; + const {setGlobalData} = actions; + + const blogPlugin = allContent?.['docusaurus-plugin-content-blog']; + const docsPlugin = allContent?.['docusaurus-plugin-content-docs']; + + const blogContent = blogPlugin?.default; + const docsContent = docsPlugin?.default; + + const tagsMap = new Map(); + + if (blogContent?.blogPosts) { + blogContent.blogPosts.forEach((post) => { + if (post.metadata.tags) { + post.metadata.tags.forEach((tag) => { + const tagKey = tag.label.toLowerCase(); + if (!tagsMap.has(tagKey)) { + tagsMap.set(tagKey, { + label: tag.label, + permalink: tag.permalink, + count: 0, + items: [] + }); + } + const tagData = tagsMap.get(tagKey); + tagData.count++; + tagData.items.push({ + type: 'blog', + title: post.metadata.title, + permalink: post.metadata.permalink, + date: post.metadata.date, + formattedDate: post.metadata.formattedDate, + description: post.metadata.description, + authors: post.metadata.authors, + tags: post.metadata.tags, + readingTime: post.metadata.readingTime + }); + }); + } + }); + } + + if (docsContent?.loadedVersions) { + docsContent.loadedVersions.forEach((version) => { + version.docs.forEach((doc) => { + if (doc.tags) { + doc.tags.forEach((tag) => { + const tagKey = tag.label.toLowerCase(); + if (!tagsMap.has(tagKey)) { + tagsMap.set(tagKey, { + label: tag.label, + permalink: tag.permalink, + count: 0, + items: [] + }); + } + const tagData = tagsMap.get(tagKey); + tagData.count++; + tagData.items.push({ + type: 'doc', + title: doc.title, + permalink: doc.permalink, + description: doc.description, + tags: doc.tags + }); + }); + } + }); + }); + } + + const allTags = Array.from(tagsMap.values()).sort((a, b) => + a.label.localeCompare(b.label) + ); + + const tagsByLetter = {}; + allTags.forEach((tag) => { + const firstLetter = tag.label.charAt(0).toUpperCase(); + if (!tagsByLetter[firstLetter]) { + tagsByLetter[firstLetter] = []; + } + tagsByLetter[firstLetter].push(tag); + }); + + const globalData = { + allTags, + tagsByLetter, + tagsMap: Object.fromEntries( + Array.from(tagsMap.entries()).map(([key, value]) => [ + key, + { + ...value, + items: value.items.sort((a, b) => { + if (a.type === 'blog' && b.type === 'blog') { + return new Date(b.date) - new Date(a.date); + } + if (a.type === 'doc' && b.type === 'doc') { + return a.title.localeCompare(b.title); + } + return a.type === 'blog' ? -1 : 1; + }) + } + ]) + ) + }; + + setGlobalData(globalData); + + const locale = context.i18n.currentLocale; + const baseUrl = locale === context.i18n.defaultLocale ? '/' : `/${locale}/`; + + Array.from(tagsMap.entries()).forEach(([tagKey, tagData]) => { + addRoute({ + path: `${baseUrl}tags/${tagKey}`, + component: '@site/src/theme/TagPage', + exact: true, + props: { + tagKey: tagKey, + }, + }); + }); + } + }; +}; diff --git a/src/pages/tags/index.tsx b/src/pages/tags/index.tsx new file mode 100644 index 0000000..4c35fc7 --- /dev/null +++ b/src/pages/tags/index.tsx @@ -0,0 +1,86 @@ +import React from 'react'; +import clsx from 'clsx'; +import Link from '@docusaurus/Link'; +import {usePluginData} from '@docusaurus/useGlobalData'; +import Layout from '@theme/Layout'; +import Translate, {translate} from '@docusaurus/Translate'; +import Heading from '@theme/Heading'; + +import styles from './styles.module.css'; + +interface Tag { + label: string; + permalink: string; + count: number; +} + +interface TagsByLetter { + [letter: string]: Tag[]; +} + +interface UnifiedTagsData { + allTags: Tag[]; + tagsByLetter: TagsByLetter; +} + +export default function TagsPage(): JSX.Element { + const pluginData = usePluginData('docusaurus-plugin-unified-tags') as UnifiedTagsData; + + console.log('Plugin data:', pluginData); + + if (!pluginData || !pluginData.tagsByLetter) { + return ( + +
+

No tags data available

+
+
+ ); + } + + const {tagsByLetter} = pluginData; + const letters = Object.keys(tagsByLetter).sort(); + + return ( + +
+ + + Tags + + + +
+ {letters.map((letter) => ( +
+ + {letter} + +
+ {tagsByLetter[letter].map((tag) => ( + + {tag.label} + {tag.count} + + ))} +
+
+ ))} +
+
+
+ ); +} diff --git a/src/pages/tags/styles.module.css b/src/pages/tags/styles.module.css new file mode 100644 index 0000000..39f26b3 --- /dev/null +++ b/src/pages/tags/styles.module.css @@ -0,0 +1,264 @@ +.tagsPageTitle { + text-align: center; + margin-bottom: 3rem; + font-size: 2.5rem; +} + +.tagsContainer { + max-width: 1200px; + margin: 0 auto; +} + +.letterSection { + margin-bottom: 3rem; +} + +.letterHeading { + font-size: 2rem; + font-weight: 700; + margin-bottom: 1rem; + padding-bottom: 0.5rem; + border-bottom: 2px solid var(--ifm-color-emphasis-300); +} + +.tagsRow { + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-top: 1.5rem; +} + +.tagLink { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + background: var(--ifm-color-emphasis-100); + border: 1px solid var(--ifm-color-emphasis-300); + border-radius: 6px; + text-decoration: none; + color: var(--ifm-font-color-base); + transition: all 0.2s ease; + clip-path: polygon(10px 0%, 100% 0%, calc(100% - 10px) 100%, 0% 100%); +} + +.tagLink:hover { + background: var(--ifm-color-primary); + color: white; + border-color: var(--ifm-color-primary); + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); + text-decoration: none; +} + +[data-theme='dark'] .tagLink { + background: var(--ifm-color-emphasis-200); + border-color: var(--ifm-color-emphasis-400); +} + +[data-theme='dark'] .tagLink:hover { + background: var(--ifm-color-primary); + border-color: var(--ifm-color-primary); +} + +.tagLabel { + font-weight: 500; + font-size: 0.95rem; +} + +.tagCount { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 24px; + height: 24px; + padding: 0 6px; + background: var(--ifm-color-emphasis-300); + border-radius: 12px; + font-size: 0.75rem; + font-weight: 600; + color: var(--ifm-font-color-base); +} + +.tagLink:hover .tagCount { + background: rgba(255, 255, 255, 0.3); + color: white; +} + +[data-theme='dark'] .tagCount { + background: var(--ifm-color-emphasis-500); +} + +.tagHeader { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 2rem; + flex-wrap: wrap; + gap: 1rem; +} + +.tagTitle { + margin: 0; + font-size: 2rem; +} + +.viewAllTagsLink { + color: var(--ifm-color-primary); + text-decoration: none; + font-weight: 500; + padding: 0.5rem 1rem; + border: 1px solid var(--ifm-color-primary); + border-radius: 6px; + transition: all 0.2s ease; +} + +.viewAllTagsLink:hover { + background: var(--ifm-color-primary); + color: white; + text-decoration: none; +} + +.itemsList { + display: flex; + flex-direction: column; + gap: 2rem; + max-width: 900px; + margin: 0 auto; +} + +.itemCard { + padding: 2rem; + background: var(--ifm-color-emphasis-100); + border: 1px solid var(--ifm-color-emphasis-300); + border-radius: 8px; + transition: all 0.2s ease; +} + +.itemCard:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + border-color: var(--ifm-color-primary); +} + +[data-theme='dark'] .itemCard { + background: var(--ifm-color-emphasis-200); + border-color: var(--ifm-color-emphasis-400); +} + +.itemTitle { + margin: 0 0 1rem 0; + font-size: 1.75rem; +} + +.itemTitle a { + color: var(--ifm-font-color-base); + text-decoration: none; +} + +.itemTitle a:hover { + color: var(--ifm-color-primary); + text-decoration: none; +} + +.itemMeta { + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-bottom: 1rem; + font-size: 0.9rem; + color: var(--ifm-color-emphasis-700); +} + +[data-theme='dark'] .itemMeta { + color: var(--ifm-color-emphasis-600); +} + +.itemDate, +.itemAuthors, +.itemReadingTime { + display: flex; + align-items: center; +} + +.itemDate::before { + content: '📅'; + margin-right: 0.3rem; +} + +.itemAuthors::before { + content: '✍️'; + margin-right: 0.3rem; +} + +.itemReadingTime::before { + content: '⏱️'; + margin-right: 0.3rem; +} + +.itemDescription { + margin: 1rem 0; + line-height: 1.6; + color: var(--ifm-color-emphasis-800); +} + +[data-theme='dark'] .itemDescription { + color: var(--ifm-color-emphasis-700); +} + +.itemTags { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin-top: 1rem; +} + +.itemTag { + display: inline-block; + padding: 0.25rem 0.75rem; + background: var(--ifm-color-emphasis-200); + border: 1px solid var(--ifm-color-emphasis-400); + border-radius: 4px; + font-size: 0.85rem; + text-decoration: none; + color: var(--ifm-font-color-base); + transition: all 0.2s ease; +} + +.itemTag:hover { + background: var(--ifm-color-primary-light); + border-color: var(--ifm-color-primary); + color: white; + text-decoration: none; +} + +[data-theme='dark'] .itemTag { + background: var(--ifm-color-emphasis-300); +} + +@media (max-width: 768px) { + .tagsPageTitle { + font-size: 2rem; + } + + .letterHeading { + font-size: 1.5rem; + } + + .tagHeader { + flex-direction: column; + align-items: flex-start; + } + + .tagTitle { + font-size: 1.5rem; + } + + .itemCard { + padding: 1.5rem; + } + + .itemTitle { + font-size: 1.5rem; + } +} diff --git a/src/theme/TagPage.tsx b/src/theme/TagPage.tsx new file mode 100644 index 0000000..5e6de44 --- /dev/null +++ b/src/theme/TagPage.tsx @@ -0,0 +1,146 @@ +import React from 'react'; +import clsx from 'clsx'; +import Link from '@docusaurus/Link'; +import {usePluginData} from '@docusaurus/useGlobalData'; +import Layout from '@theme/Layout'; +import Translate, {translate} from '@docusaurus/Translate'; +import Heading from '@theme/Heading'; + +import styles from '../pages/tags/styles.module.css'; + +interface TagItem { + type: 'blog' | 'doc'; + title: string; + permalink: string; + date?: string; + formattedDate?: string; + description?: string; + authors?: any[]; + tags?: any[]; + readingTime?: number; +} + +interface TagData { + label: string; + permalink: string; + count: number; + items: TagItem[]; +} + +interface TagsMapData { + [tagKey: string]: TagData; +} + +interface UnifiedTagsData { + tagsMap: TagsMapData; +} + +interface TagPageProps { + tagKey: string; +} + +export default function TagPage(props: TagPageProps): JSX.Element { + const {tagsMap} = usePluginData('docusaurus-plugin-unified-tags') as UnifiedTagsData; + const tagData = tagsMap[props.tagKey]; + + if (!tagData) { + return ( + +
+ Tag Not Found +

The tag you are looking for does not exist.

+ View All Tags +
+
+ ); + } + + const {label, count, items} = tagData; + + return ( + +
+
+ + + {'{count} posts tagged with "{tagName}"'} + + + + + View All Tags + + +
+ +
+ {items.map((item, idx) => ( +
+ + {item.title} + + + {item.type === 'blog' && ( +
+ {item.formattedDate && ( + + )} + {item.authors && item.authors.length > 0 && ( + + {item.authors.map((author, i) => ( + + {i > 0 && ', '} + {author.name} + + ))} + + )} + {item.readingTime && ( + + {Math.ceil(item.readingTime)} min read + + )} +
+ )} + + {item.description && ( +

{item.description}

+ )} + + {item.tags && item.tags.length > 0 && ( +
+ {item.tags.map((tag, i) => ( + + {tag.label} + + ))} +
+ )} +
+ ))} +
+
+
+ ); +}