diff --git a/src/css/custom.css b/src/css/custom.css
index 3039cf530..4d021cd91 100644
--- a/src/css/custom.css
+++ b/src/css/custom.css
@@ -167,4 +167,34 @@
gap: 0.75rem; /* space between icons */
align-items: center;
}
+}
+
+.kmesh-version-badge {
+ display: inline-block;
+ margin-left: 0.5rem;
+ background: transparent;
+ color: inherit;
+ font-weight: 600;
+ padding: 0.08rem 0.28rem;
+ border-radius: 0.25rem;
+ font-size: 0.85rem;
+ line-height: 1;
+ box-shadow: none;
+ border: 1px solid rgba(0,0,0,0.06);
+ vertical-align: middle;
+}
+
+[data-theme="dark"] .kmesh-version-badge {
+ border-color: rgba(255,255,255,0.06);
+}
+
+.kmesh-version-container {
+ display: inline-flex;
+ align-items: center;
+ margin: 0;
+ padding: 0;
+}
+
+@media (max-width: 996px) {
+ .kmesh-version-badge { display: none; }
}
\ No newline at end of file
diff --git a/src/theme/Navbar/index.js b/src/theme/Navbar/index.js
new file mode 100644
index 000000000..3b0a73479
--- /dev/null
+++ b/src/theme/Navbar/index.js
@@ -0,0 +1,95 @@
+import React, { useEffect, useState } from 'react';
+import ReactDOM from 'react-dom';
+import OriginalNavbar from '@theme-original/Navbar';
+
+export default function Navbar(props) {
+ const [version, setVersion] = useState(null);
+
+ const [containerEl, setContainerEl] = useState(null);
+
+ useEffect(() => {
+ let mounted = true;
+ // fetch latest release tag
+ fetch('https://api.github.com/repos/kmesh-net/kmesh/releases/latest')
+ .then((res) => res.json())
+ .then((data) => {
+ if (!mounted) return;
+ if (data && data.tag_name) setVersion(data.tag_name);
+ })
+ .catch(() => {
+ // network or API error
+ });
+
+ if (typeof document !== 'undefined') {
+ const searchEl = document.querySelector('.navbar__search, .DocSearch-Button');
+ const navRight = document.querySelector('.navbar__items--right');
+ if (searchEl) {
+ const searchItem = searchEl.closest('.navbar__item') || searchEl.parentNode;
+ const container = navRight || (searchItem && searchItem.parentNode);
+ if (container && searchItem) {
+
+ let existing = container.querySelector('.kmesh-version-container');
+ if (!existing) {
+ const wrapper = document.createElement('div');
+ wrapper.className = 'kmesh-version-container';
+ wrapper.style.display = 'inline-flex';
+ wrapper.style.alignItems = 'center';
+ wrapper.style.margin = '0 0.5rem 0 0';
+ wrapper.style.padding = '0';
+ container.insertBefore(wrapper, searchItem);
+ setContainerEl(wrapper);
+ } else {
+ setContainerEl(existing);
+ }
+ } else if (navRight) {
+
+ let existing = navRight.querySelector('.kmesh-version-container');
+ if (!existing) {
+ const wrapper = document.createElement('div');
+ wrapper.className = 'kmesh-version-container';
+ wrapper.style.display = 'inline-flex';
+ wrapper.style.alignItems = 'center';
+ wrapper.style.margin = '0 0.5rem 0 0';
+ wrapper.style.padding = '0';
+ navRight.appendChild(wrapper);
+ setContainerEl(wrapper);
+ } else {
+ setContainerEl(existing);
+ }
+ }
+ } else if (navRight) {
+
+ let existing = navRight.querySelector('.kmesh-version-container');
+ if (!existing) {
+ const wrapper = document.createElement('div');
+ wrapper.className = 'kmesh-version-container';
+ wrapper.style.display = 'inline-flex';
+ wrapper.style.alignItems = 'center';
+ wrapper.style.margin = '0 0.5rem 0 0';
+ wrapper.style.padding = '0';
+ navRight.appendChild(wrapper);
+ setContainerEl(wrapper);
+ } else {
+ setContainerEl(existing);
+ }
+ }
+ }
+
+ return () => {
+ mounted = false;
+ };
+ }, []);
+
+ return (
+ <>
+