diff --git a/client/src/App.jsx b/client/src/App.jsx
index d21850ebb..b3f3f40dc 100644
--- a/client/src/App.jsx
+++ b/client/src/App.jsx
@@ -10,6 +10,7 @@ import { logger } from "./Utils/Logger"; // Import the logger
import { networkService } from "./main";
import { Routes } from "./Routes";
import AppLayout from "@/Components/v1/Layouts/AppLayout";
+import { UserGuideSidebarProvider } from "@/Components/v1/UserGuide";
function App() {
const mode = useSelector((state) => state.ui.mode);
@@ -25,9 +26,11 @@ function App() {
return (
-
-
-
+
+
+
+
+
);
diff --git a/client/src/Components/v1/Layouts/HomeLayout/index.jsx b/client/src/Components/v1/Layouts/HomeLayout/index.jsx
index 3e63aff37..cb28ddc83 100644
--- a/client/src/Components/v1/Layouts/HomeLayout/index.jsx
+++ b/client/src/Components/v1/Layouts/HomeLayout/index.jsx
@@ -1,6 +1,7 @@
import Sidebar from "../../Sidebar/index.jsx";
import { Outlet } from "react-router";
import { Stack } from "@mui/material";
+import { UserGuideSidebar } from "../../UserGuide";
import "./index.css";
@@ -15,6 +16,7 @@ const HomeLayout = () => {
+
);
};
diff --git a/client/src/Components/v1/Sidebar/index.jsx b/client/src/Components/v1/Sidebar/index.jsx
index b66f081fb..cff8fb8c8 100644
--- a/client/src/Components/v1/Sidebar/index.jsx
+++ b/client/src/Components/v1/Sidebar/index.jsx
@@ -89,6 +89,8 @@ const Sidebar = () => {
gap={theme.spacing(6)}
sx={{
transition: "width 650ms cubic-bezier(0.36, -0.01, 0, 0.77)",
+ backgroundColor: theme.palette.primaryBackground.main,
+ zIndex: 1000,
}}
>
diff --git a/client/src/Components/v1/UserGuide/ArticlePage.css b/client/src/Components/v1/UserGuide/ArticlePage.css
new file mode 100644
index 000000000..fd1e1b7e6
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/ArticlePage.css
@@ -0,0 +1,157 @@
+/* ArticlePage styles - hover states and nested selectors (Dark theme) */
+
+/* Breadcrumb hover */
+.user-guide-breadcrumb-link {
+ transition: color 150ms ease;
+}
+
+.user-guide-breadcrumb-link:hover {
+ color: #1570EF;
+}
+
+/* ToC link hover */
+.user-guide-toc-link {
+ transition: all 150ms ease;
+}
+
+.user-guide-toc-link:hover {
+ color: #1570EF;
+ text-decoration: underline !important;
+}
+
+/* Navigation button hover */
+.user-guide-nav-button .nav-text {
+ transition: color 150ms ease;
+}
+
+.user-guide-nav-button:hover .nav-text {
+ color: #1570EF;
+}
+
+/* Article body typography */
+.user-guide-article-body h2 {
+ font-size: 18px;
+ font-weight: 600;
+ color: #ffffff;
+ margin-top: 32px;
+ margin-bottom: 16px;
+ line-height: 1.4;
+}
+
+.user-guide-article-body h3 {
+ font-size: 14px;
+ font-weight: 600;
+ color: #ffffff;
+ margin-top: 24px;
+ margin-bottom: 12px;
+ line-height: 1.4;
+}
+
+.user-guide-article-body p {
+ font-size: 13px;
+ color: #d0d5dd;
+ line-height: 1.5;
+ margin-bottom: 16px;
+ margin-top: 0;
+}
+
+.user-guide-article-body ul,
+.user-guide-article-body ol {
+ padding-left: 24px;
+ margin-bottom: 16px;
+ margin-top: 0;
+}
+
+.user-guide-article-body li {
+ font-size: 13px;
+ color: #d0d5dd;
+ line-height: 1.5;
+ margin-bottom: 8px;
+}
+
+.user-guide-article-body a {
+ color: #1570EF;
+ text-decoration: none;
+ font-weight: 500;
+}
+
+.user-guide-article-body a:hover {
+ text-decoration: underline;
+}
+
+.user-guide-article-body code {
+ background-color: #2d2d2d;
+ padding: 2px 6px;
+ border-radius: 4px;
+ font-size: 10px;
+ font-family: "Geist Mono", "Fira Code", "Consolas", monospace;
+ color: #d0d5dd;
+}
+
+.user-guide-article-body pre {
+ background-color: #1c2130;
+ border-radius: 4px;
+ padding: 16px;
+ overflow: auto;
+ margin-bottom: 16px;
+ margin-top: 0;
+}
+
+.user-guide-article-body pre code {
+ background-color: transparent;
+ padding: 0;
+ color: #e5e7eb;
+}
+
+.user-guide-article-body blockquote {
+ border-left: 3px solid #1570EF;
+ background-color: rgba(255, 255, 255, 0.05);
+ padding: 12px 16px;
+ border-radius: 0 4px 4px 0;
+ margin-bottom: 16px;
+ margin-top: 0;
+ margin-left: 0;
+ margin-right: 0;
+}
+
+.user-guide-article-body blockquote p {
+ margin-bottom: 0;
+ color: #d0d5dd;
+}
+
+.user-guide-article-body table {
+ width: 100%;
+ border-collapse: collapse;
+ margin-bottom: 16px;
+}
+
+.user-guide-article-body th,
+.user-guide-article-body td {
+ border: 1px solid #344054;
+ padding: 12px;
+ text-align: left;
+ font-size: 10px;
+}
+
+.user-guide-article-body th {
+ background-color: rgba(255, 255, 255, 0.05);
+ font-weight: 600;
+ color: #ffffff;
+}
+
+.user-guide-article-body td {
+ color: #d0d5dd;
+}
+
+.user-guide-article-body img {
+ max-width: 100%;
+ border-radius: 4px;
+ border: 1px solid #344054;
+ margin-bottom: 16px;
+}
+
+.user-guide-article-body hr {
+ border: none;
+ border-top: 1px solid #344054;
+ margin: 32px 0;
+}
diff --git a/client/src/Components/v1/UserGuide/ArticlePage.jsx b/client/src/Components/v1/UserGuide/ArticlePage.jsx
new file mode 100644
index 000000000..b90072a3f
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/ArticlePage.jsx
@@ -0,0 +1,329 @@
+import { useState, useEffect } from "react";
+import {
+ ChevronLeft,
+ ChevronRight,
+ Rocket,
+ Globe,
+ Gauge,
+ Link,
+ Bell,
+ AlertTriangle,
+ Settings,
+ FileText,
+} from "lucide-react";
+import { useUserGuideTheme } from "./styles/useUserGuideTheme";
+import "./ArticlePage.css";
+
+// Map icon names to actual Lucide components
+const iconMap = {
+ Rocket,
+ Globe,
+ Gauge,
+ Link,
+ Bell,
+ AlertTriangle,
+ Settings,
+ FileText,
+};
+
+const ArticlePage = ({
+ collection,
+ article,
+ onBack,
+ onBackToHome,
+ onNextArticle,
+ onPrevArticle,
+ nextArticle,
+ prevArticle,
+ tocItems,
+ children,
+ mode = "in-app",
+}) => {
+ const { colors, typography, spacing, border, isDark } = useUserGuideTheme();
+ const IconComponent = iconMap[collection.icon] || Rocket;
+ const isInApp = mode === "in-app";
+ const [activeSection, setActiveSection] = useState("");
+
+ // Track active section on scroll
+ useEffect(() => {
+ if (!tocItems || tocItems.length === 0) return;
+
+ const handleScroll = () => {
+ const sections = tocItems
+ .map((item) => {
+ const id = item.href.replace("#", "");
+ const element = document.getElementById(id);
+ return { id: item.href, element };
+ })
+ .filter((s) => s.element);
+
+ if (sections.length === 0) return;
+
+ let currentSection = sections[0].id;
+
+ for (const section of sections) {
+ if (section.element) {
+ const rect = section.element.getBoundingClientRect();
+ if (rect.top <= 150) {
+ currentSection = section.id;
+ }
+ }
+ }
+
+ setActiveSection(currentSection);
+ };
+
+ window.addEventListener("scroll", handleScroll, { passive: true });
+ document.addEventListener("scroll", handleScroll, { passive: true, capture: true });
+ handleScroll();
+
+ return () => {
+ window.removeEventListener("scroll", handleScroll);
+ document.removeEventListener("scroll", handleScroll, { capture: true });
+ };
+ }, [tocItems]);
+
+ return (
+
+ {/* Main content area */}
+
+ {/* Article Content */}
+
+ {/* Title */}
+
+ {article.title}
+
+
+ {/* Description */}
+
+ {article.description}
+
+
+ {/* Table of Contents - at top */}
+ {tocItems && tocItems.length > 0 && (
+
+
+
+ In this article
+
+
+
+
+ )}
+
+ {/* Article Body */}
+
+ {children || (
+
+
+ Article content coming soon.
+
+
+ )}
+
+
+ {/* Navigation */}
+ {(prevArticle || nextArticle) && (
+
+ {prevArticle ? (
+
+
+
+
+ Previous
+
+
+ {prevArticle.title}
+
+
+
+ ) : (
+
+ )}
+ {nextArticle && (
+
+
+
+ Next
+
+
+ {nextArticle.title}
+
+
+
+
+ )}
+
+ )}
+
+
+
+ );
+};
+
+export default ArticlePage;
diff --git a/client/src/Components/v1/UserGuide/CollectionPage.css b/client/src/Components/v1/UserGuide/CollectionPage.css
new file mode 100644
index 000000000..6adf5bf44
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/CollectionPage.css
@@ -0,0 +1,31 @@
+/* CollectionPage styles - hover states */
+
+/* Back link hover */
+.user-guide-back-link {
+ transition: color 150ms ease;
+}
+
+.user-guide-back-link:hover {
+ color: #1570EF;
+}
+
+/* Article row hover */
+.user-guide-article-row {
+ transition: all 150ms ease;
+}
+
+.user-guide-article-row:hover {
+ background-color: rgba(0, 0, 0, 0.04);
+}
+
+[data-mui-color-scheme="dark"] .user-guide-article-row:hover {
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.user-guide-article-row .arrow-icon {
+ transition: all 150ms ease;
+}
+
+.user-guide-article-row:hover .arrow-icon {
+ color: #1570EF;
+}
diff --git a/client/src/Components/v1/UserGuide/CollectionPage.jsx b/client/src/Components/v1/UserGuide/CollectionPage.jsx
new file mode 100644
index 000000000..fa2e83c7c
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/CollectionPage.jsx
@@ -0,0 +1,173 @@
+import {
+ ArrowRight,
+ FileText,
+ Rocket,
+ Globe,
+ Gauge,
+ Link,
+ Bell,
+ AlertTriangle,
+ Settings,
+} from "lucide-react";
+import { useUserGuideTheme } from "./styles/useUserGuideTheme";
+import "./CollectionPage.css";
+
+// Map icon names to actual Lucide components
+const iconMap = {
+ Rocket,
+ Globe,
+ Gauge,
+ Link,
+ Bell,
+ AlertTriangle,
+ Settings,
+ FileText,
+};
+
+const CollectionPage = ({ collection, onBack, onArticleClick }) => {
+ const { colors, typography, spacing, border } = useUserGuideTheme();
+ const IconComponent = iconMap[collection.icon] || Rocket;
+
+ return (
+
+ {/* Header */}
+
+
+ {/* Collection Header */}
+
+
+
+
+ {collection.title}
+
+
+ {collection.description}
+
+
+ {collection.articleCount} articles in this collection
+
+
+
+
+
+
+ {/* Articles List */}
+
+
+ {collection.articles.map((article, index) => (
+
onArticleClick(article.id)}
+ className="user-guide-article-row"
+ style={{
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "space-between",
+ padding: spacing.lg,
+ borderBottom:
+ index < collection.articles.length - 1 ? border.default : "none",
+ cursor: "pointer",
+ }}
+ >
+
+
+
+
+ {article.title}
+
+
+ {article.description}
+
+
+
+
+
+ ))}
+
+
+
+ );
+};
+
+export default CollectionPage;
diff --git a/client/src/Components/v1/UserGuide/ContentRenderer.css b/client/src/Components/v1/UserGuide/ContentRenderer.css
new file mode 100644
index 000000000..563563fe0
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/ContentRenderer.css
@@ -0,0 +1,45 @@
+/* ContentRenderer styles - hover states and list styling */
+
+/* List styling - colors handled by inline styles for theme support */
+.user-guide-content-list li {
+ /* Font styles handled inline for theme-aware colors */
+}
+
+/* Table row hover */
+.user-guide-table-row {
+ transition: background-color 150ms ease;
+}
+
+.user-guide-table-row:hover {
+ background-color: rgba(0, 0, 0, 0.04) !important;
+}
+
+[data-mui-color-scheme="dark"] .user-guide-table-row:hover {
+ background-color: rgba(255, 255, 255, 0.05) !important;
+}
+
+/* Article links hover */
+.user-guide-article-link-item {
+ transition: all 150ms ease;
+}
+
+.user-guide-article-link-item:hover {
+ background-color: rgba(0, 0, 0, 0.04);
+}
+
+[data-mui-color-scheme="dark"] .user-guide-article-link-item:hover {
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.user-guide-article-link-item:hover .link-title {
+ color: #1570EF;
+}
+
+.user-guide-article-link-item .arrow-icon {
+ transition: all 150ms ease;
+}
+
+.user-guide-article-link-item:hover .arrow-icon {
+ transform: translateX(4px);
+ color: #1570EF;
+}
diff --git a/client/src/Components/v1/UserGuide/ContentRenderer.jsx b/client/src/Components/v1/UserGuide/ContentRenderer.jsx
new file mode 100644
index 000000000..2dafd3485
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/ContentRenderer.jsx
@@ -0,0 +1,902 @@
+import { useState } from "react";
+import { useUserGuideTheme } from "./styles/useUserGuideTheme";
+import ImageLightbox from "./ImageLightbox";
+import {
+ CheckCircle,
+ Clock,
+ Rocket,
+ Shield,
+ Brain,
+ AlertTriangle,
+ Server,
+ Database,
+ Terminal,
+ FileText,
+ FolderKanban,
+ Gauge,
+ LayoutDashboard,
+ Menu,
+ Settings,
+ Info,
+ Lightbulb,
+ ArrowRight,
+ Globe,
+ Bell,
+ Link,
+} from "lucide-react";
+import "./ContentRenderer.css";
+
+// Icon mapping
+const iconMap = {
+ CheckCircle,
+ Clock,
+ Rocket,
+ Shield,
+ Brain,
+ AlertTriangle,
+ Server,
+ Database,
+ Terminal,
+ FileText,
+ FolderKanban,
+ Gauge,
+ LayoutDashboard,
+ Menu,
+ Settings,
+ Info,
+ Lightbulb,
+ Globe,
+ Bell,
+ Link,
+};
+
+const getIcon = (name) => iconMap[name] || Info;
+
+const ContentRenderer = ({ content, onNavigate }) => {
+ const { colors, typography, spacing, border, chipStyles, imageStyles, isDark } = useUserGuideTheme();
+ const [lightboxImage, setLightboxImage] = useState(null);
+
+ // Parse basic markdown in text (inside component to access onNavigate)
+ const parseMarkdown = (text) => {
+ const parts = [];
+ let key = 0;
+
+ // Supports: **bold**, `code`, [[link text]](collection/article)
+ const combinedRegex = /\*\*([^*]+)\*\*|`([^`]+)`|\[\[([^\]]+)\]\]\(([^)]+)\)/g;
+ let lastIndex = 0;
+ let match;
+
+ const getChipStyle = (codeContent) => {
+ const isUrl = /^https?:\/\//.test(codeContent);
+ return isUrl ? chipStyles.url : chipStyles.code;
+ };
+
+ while ((match = combinedRegex.exec(text)) !== null) {
+ if (match.index > lastIndex) {
+ parts.push(text.slice(lastIndex, match.index));
+ }
+
+ if (match[1]) {
+ // Bold: **text**
+ parts.push({match[1]});
+ } else if (match[2]) {
+ // Code: `text`
+ const style = getChipStyle(match[2]);
+ parts.push(
+
+ {match[2]}
+
+ );
+ } else if (match[3] && match[4]) {
+ // Article link: [[text]](collection/article)
+ const linkText = match[3];
+ const linkPath = match[4];
+ const [collectionId, articleId] = linkPath.split("/");
+ parts.push(
+ onNavigate?.(collectionId, articleId)}
+ style={{
+ color: colors.brand.primary,
+ cursor: "pointer",
+ textDecoration: "underline",
+ textDecorationColor: "transparent",
+ transition: "text-decoration-color 150ms ease",
+ }}
+ onMouseEnter={(e) => {
+ e.currentTarget.style.textDecorationColor = colors.brand.primary;
+ }}
+ onMouseLeave={(e) => {
+ e.currentTarget.style.textDecorationColor = "transparent";
+ }}
+ >
+ {linkText}
+
+ );
+ }
+
+ lastIndex = match.index + match[0].length;
+ }
+
+ if (lastIndex < text.length) {
+ parts.push(text.slice(lastIndex));
+ }
+
+ return parts.length > 0 ? parts : text;
+ };
+
+ const renderBlock = (block, index) => {
+ switch (block.type) {
+ case "heading":
+ return renderHeading(block, index);
+ case "paragraph":
+ return renderParagraph(block, index);
+ case "callout":
+ return renderCallout(block, index);
+ case "bullet-list":
+ return renderBulletList(block, index);
+ case "ordered-list":
+ return renderOrderedList(block, index);
+ case "checklist":
+ return renderChecklist(block, index);
+ case "icon-cards":
+ return renderIconCards(block, index);
+ case "grid-cards":
+ return renderGridCards(block, index);
+ case "time-estimate":
+ return renderTimeEstimate(block, index);
+ case "info-box":
+ return renderInfoBox(block, index);
+ case "code":
+ return renderCode(block, index);
+ case "requirements":
+ return renderRequirements(block, index);
+ case "table":
+ return renderTable(block, index);
+ case "image":
+ return renderImage(block, index);
+ case "article-links":
+ return renderArticleLinks(block, index);
+ default:
+ return null;
+ }
+ };
+
+ const renderHeading = (block, index) => {
+ const Tag = block.level === 2 ? "h2" : "h3";
+ return (
+ 0 ? spacing["2xl"] : 0,
+ lineHeight: typography.lineHeight.normal,
+ }}
+ >
+ {block.text}
+
+ );
+ };
+
+ const renderParagraph = (block, index) => (
+
+ {parseMarkdown(block.text)}
+
+ );
+
+ const renderCallout = (block, index) => {
+ const calloutColors = {
+ info: colors.brand.primary,
+ tip: colors.brand.primary,
+ warning: colors.brand.warning,
+ success: colors.brand.success,
+ };
+ const accentColor = calloutColors[block.variant || "info"];
+
+ return (
+
+ {block.title && (
+
+ {block.title}
+
+ )}
+
+ {parseMarkdown(block.text)}
+
+
+ );
+ };
+
+ const renderBulletList = (block, index) => (
+
+ {block.items.map((item, i) => (
+ -
+ {item.bold && (
+ {item.bold}
+ )}
+ {item.bold && — }
+
+ {item.bold ? parseMarkdown(item.text) : parseMarkdown(item.text)}
+
+
+ ))}
+
+ );
+
+ const renderOrderedList = (block, index) => (
+
+ {block.items.map((item, i) => (
+ -
+ {item.bold && (
+ {item.bold}
+ )}
+ {item.bold && " "}
+
+ {item.bold ? parseMarkdown(item.text) : parseMarkdown(item.text)}
+
+
+ ))}
+
+ );
+
+ const renderChecklist = (block, index) => (
+
+ {block.items.map((item, i) => (
+
+
+
+ {parseMarkdown(item)}
+
+
+ ))}
+
+ );
+
+ const renderIconCards = (block, index) => (
+
+ {block.items.map((item, i) => {
+ const IconComponent = getIcon(item.icon);
+ return (
+
+
+
+
+
+
+ {item.title}
+
+
+ {parseMarkdown(item.description)}
+
+
+
+ );
+ })}
+
+ );
+
+ const renderGridCards = (block, index) => (
+
+ {block.items.map((item, i) => {
+ const IconComponent = item.icon ? getIcon(item.icon) : null;
+ return (
+
+
+ {IconComponent && (
+
+
+
+ )}
+
+ {item.title}
+
+
+
+ {parseMarkdown(item.description)}
+
+
+ );
+ })}
+
+ );
+
+ const renderTimeEstimate = (block, index) => (
+
+
+
+
+
+ {parseMarkdown(block.text)}
+
+
+ );
+
+ const renderInfoBox = (block, index) => {
+ const IconComponent = block.icon ? getIcon(block.icon) : FolderKanban;
+ return (
+
+
+
+
+ {block.title}
+
+
+ {block.items.map((item, i) => (
+ -
+ {parseMarkdown(item)}
+
+ ))}
+
+
+
+ );
+ };
+
+ const renderCode = (block, index) => (
+
+ );
+
+ const renderRequirements = (block, index) => (
+
+ {block.items.map((req, i) => {
+ const IconComponent = getIcon(req.icon);
+ return (
+
+
+
+
+ {req.title}
+
+
+
+ {req.items.map((item, j) => (
+ -
+ {item}
+
+ ))}
+
+
+ );
+ })}
+
+ );
+
+ const renderTableCell = (value) => {
+ if (value === "✓") {
+ return (
+
+ ✓
+
+ );
+ }
+ if (value === "✗" || value === "—") {
+ return (
+
+ —
+
+ );
+ }
+ return parseMarkdown(value);
+ };
+
+ const renderTable = (block, index) => (
+
+ {/* Header */}
+
col.width || "1fr").join(" "),
+ backgroundColor: isDark ? "rgba(255, 255, 255, 0.05)" : "rgba(0, 0, 0, 0.03)",
+ borderBottom: border.default,
+ padding: `${spacing.md} ${spacing.lg}`,
+ gap: spacing.md,
+ }}
+ >
+ {block.columns.map((col) => (
+
+ {col.label}
+
+ ))}
+
+ {/* Rows */}
+ {block.rows.map((row, i) => (
+
col.width || "1fr").join(" "),
+ borderBottom: i < block.rows.length - 1 ? border.default : "none",
+ backgroundColor: i % 2 === 1 ? (isDark ? "rgba(255, 255, 255, 0.02)" : "rgba(0, 0, 0, 0.02)") : "transparent",
+ padding: `10px ${spacing.lg}`,
+ gap: spacing.md,
+ alignItems: "center",
+ }}
+ >
+ {block.columns.map((col, j) => (
+
+ {renderTableCell(row[col.key])}
+
+ ))}
+
+ ))}
+
+ );
+
+ const renderImage = (block, index) => (
+
+

+ setLightboxImage({ src: block.src, alt: block.alt, caption: block.caption })
+ }
+ style={{
+ ...imageStyles.image,
+ cursor: "zoom-in",
+ }}
+ />
+ {block.caption && (
+
+ {block.caption}
+
+ )}
+
+ );
+
+ const renderArticleLinks = (block, index) => (
+
+
+ {block.title && (
+
+ {block.title}
+
+ )}
+
+ {block.items.map((item, i) => (
+
onNavigate?.(item.collectionId, item.articleId)}
+ className="user-guide-article-link-item"
+ style={{
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "space-between",
+ padding: spacing.sm,
+ margin: `0 -${spacing.sm}`,
+ borderRadius: "4px",
+ cursor: "pointer",
+ }}
+ >
+
+ {item.title}
+
+
+
+ ))}
+
+
+ );
+
+ return (
+ <>
+ {content.blocks.map((block, index) => renderBlock(block, index))}
+
+ {/* Image Lightbox */}
+ {lightboxImage && (
+ setLightboxImage(null)}
+ />
+ )}
+ >
+ );
+};
+
+export default ContentRenderer;
diff --git a/client/src/Components/v1/UserGuide/HelpSection.css b/client/src/Components/v1/UserGuide/HelpSection.css
new file mode 100644
index 000000000..297490a74
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/HelpSection.css
@@ -0,0 +1,14 @@
+/* HelpSection styles */
+
+.user-guide-help-button {
+ transition: all 150ms ease;
+}
+
+.user-guide-help-button:hover {
+ border-color: #1570EF;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
+}
+
+[data-mui-color-scheme="dark"] .user-guide-help-button:hover {
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
+}
diff --git a/client/src/Components/v1/UserGuide/HelpSection.jsx b/client/src/Components/v1/UserGuide/HelpSection.jsx
new file mode 100644
index 000000000..453afefa4
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/HelpSection.jsx
@@ -0,0 +1,126 @@
+import { ExternalLink } from "lucide-react";
+import { useUserGuideTheme } from "./styles/useUserGuideTheme";
+import "./HelpSection.css";
+
+const HelpSection = () => {
+ const { colors, typography, spacing, border } = useUserGuideTheme();
+
+ return (
+
+ {/* Contact us */}
+
+
+ {/* Ask the community */}
+
+
+ GitHub discussions
+
+
+ Questions about features, how-tos, or use cases? Join the community discussion.
+
+
+ GitHub discussions
+
+
+
+
+ );
+};
+
+export default HelpSection;
diff --git a/client/src/Components/v1/UserGuide/ImageLightbox.jsx b/client/src/Components/v1/UserGuide/ImageLightbox.jsx
new file mode 100644
index 000000000..ab78b9501
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/ImageLightbox.jsx
@@ -0,0 +1,121 @@
+import { useEffect, useCallback } from "react";
+import { X } from "lucide-react";
+import { typography, spacing } from "./styles/theme";
+
+const ImageLightbox = ({ src, alt, caption, onClose }) => {
+ // Close on escape key
+ const handleKeyDown = useCallback(
+ (e) => {
+ if (e.key === "Escape") {
+ onClose();
+ }
+ },
+ [onClose]
+ );
+
+ useEffect(() => {
+ document.addEventListener("keydown", handleKeyDown);
+ document.body.style.overflow = "hidden";
+
+ return () => {
+ document.removeEventListener("keydown", handleKeyDown);
+ document.body.style.overflow = "";
+ };
+ }, [handleKeyDown]);
+
+ // Close when clicking backdrop
+ const handleBackdropClick = (e) => {
+ if (e.target === e.currentTarget) {
+ onClose();
+ }
+ };
+
+ return (
+
+ {/* Close button */}
+
+
+ {/* Image container */}
+
+

e.stopPropagation()}
+ />
+ {caption && (
+
+ {caption}
+
+ )}
+
+
+ );
+};
+
+export default ImageLightbox;
diff --git a/client/src/Components/v1/UserGuide/STYLE_GUIDE.md b/client/src/Components/v1/UserGuide/STYLE_GUIDE.md
new file mode 100644
index 000000000..5733595f8
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/STYLE_GUIDE.md
@@ -0,0 +1,606 @@
+# Checkmate User Guide Style Guide
+
+This document defines the writing standards, formatting rules, and content patterns for all Checkmate user documentation.
+
+---
+
+## Voice & Tone
+
+### Core Principles
+
+1. **Second person, active voice** - Address the reader directly
+ - ✅ "You can configure monitors from the dashboard"
+ - ❌ "Monitors can be configured from the dashboard"
+
+2. **Professional but approachable** - Clear and helpful, not stiff or overly casual
+ - ✅ "Select the notification channel you want to use"
+ - ❌ "Go ahead and pick whatever notification channel works for you!"
+ - ❌ "The user shall select the appropriate notification channel"
+
+3. **Direct and instructional** - Tell users what to do, not what the system does
+ - ✅ "Click Save to apply your changes"
+ - ❌ "The Save button will apply your changes"
+
+4. **User-focused** - Lead with what users can accomplish
+ - ✅ "You can track server performance with infrastructure monitors"
+ - ❌ "Infrastructure monitors use the Capture agent to collect metrics"
+
+### Language Patterns
+
+**Opening phrases:**
+- "The [Feature] allows you to..."
+- "[Feature] is..."
+- "You can..."
+- "To [action]:"
+
+**Navigation instructions:**
+- "Navigate to [location]"
+- "Click [element]"
+- "Select [option]"
+- "From the [page], click..."
+
+**Avoid:**
+- Passive voice where active is clearer
+- Jargon without explanation
+- Assuming prior knowledge
+- Marketing language ("powerful", "seamless", "robust")
+
+---
+
+## Content Structure
+
+### Standard Article Pattern
+
+Every article should follow this flow:
+
+```
+1. OVERVIEW (H2)
+ - 1-2 paragraphs explaining what and why
+ - Set context for the topic
+
+2. VISUAL (Optional)
+ - Screenshot showing the feature
+ - Include after intro, not before
+
+3. MAIN CONTENT (H2/H3 sections)
+ - Procedures with ordered lists
+ - Features with bullet lists
+ - Reference info in tables
+
+4. TIPS & WARNINGS (Callouts)
+ - Best practices
+ - Common pitfalls
+ - Important restrictions
+
+5. RELATED ARTICLES (Links)
+ - 2-4 related topics
+ - Logical next steps
+```
+
+### Heading Hierarchy
+
+Use only two heading levels within articles:
+
+- **H2** - Major sections (Overview, Configuration, Settings)
+- **H3** - Subsections within H2s (Creating a monitor, Editing settings)
+
+Never use H4 or deeper. If content requires more nesting, restructure into separate sections.
+
+### Section Length
+
+| Element | Target Length |
+|---------|---------------|
+| Paragraphs | 2-4 sentences |
+| H2 sections | 100-300 words |
+| H3 sections | 50-150 words |
+| Full articles | 800-1500 words |
+
+---
+
+## Formatting Rules
+
+### Lists
+
+**Bullet lists** - Use for features, options, characteristics (unordered items)
+```javascript
+{
+ type: 'bullet-list',
+ items: [
+ { text: 'HTTP monitors check website availability' },
+ { text: 'Ping monitors verify server reachability' },
+ { text: 'Port monitors test specific service ports' }
+ ]
+}
+```
+
+**Ordered lists** - Use for step-by-step procedures (sequential actions)
+```javascript
+{
+ type: 'ordered-list',
+ items: [
+ { text: 'Navigate to **Uptime** in the sidebar' },
+ { text: 'Click **Create monitor**' },
+ { text: 'Enter the URL you want to monitor' },
+ { text: 'Click **Save** to create the monitor' }
+ ]
+}
+```
+
+**List guidelines:**
+- Keep procedures to 4-6 steps maximum
+- If longer, break into multiple procedures
+- Start each item with an action verb for procedures
+- Use parallel structure (all items same grammatical form)
+
+### Callouts
+
+Use callouts sparingly for important information that shouldn't be missed.
+
+**Types:**
+
+| Variant | Use For | Example |
+|---------|---------|---------|
+| `info` | Additional context, clarification | "Monitors check at the interval you specify, starting from creation time." |
+| `tip` | Best practices, recommendations | "Use descriptive names to easily identify monitors in alerts." |
+| `warning` | Restrictions, potential issues | "Deleting a monitor removes all associated check history." |
+| `success` | Confirmations, positive outcomes | "Your monitor is now active and checking every 5 minutes." |
+
+```javascript
+{
+ type: 'callout',
+ variant: 'tip',
+ title: 'Best practice',
+ text: 'Group related monitors together for easier management.'
+}
+```
+
+**Callout guidelines:**
+- Maximum 2-3 callouts per article
+- Keep callout text concise (1-2 sentences)
+- Title is optional but helps scannability
+- Don't use callouts for routine information
+
+### Inline Formatting
+
+**Bold** - Use for:
+- UI element names: "Click **Save**"
+- Key terms on first use: "**Uptime** measures availability over time"
+- Emphasis in lists: "**Name** — The display name for your monitor"
+
+**Code/monospace** - Use for:
+- URLs: `https://example.com`
+- File paths: `/api/v1/monitors`
+- Technical values: `200`, `timeout`
+- Commands: `npm run dev`
+
+```javascript
+// In paragraph text, use backticks in markdown:
+{ text: 'Enter the URL (e.g., `https://example.com`) in the field' }
+```
+
+### Tables
+
+Use tables for:
+- Feature comparisons
+- Configuration options
+- Reference data with multiple attributes
+
+```javascript
+{
+ type: 'table',
+ columns: [
+ { key: 'type', label: 'Monitor Type', width: '1fr' },
+ { key: 'use', label: 'Best For', width: '2fr' }
+ ],
+ rows: [
+ { type: 'HTTP', use: 'Websites and APIs' },
+ { type: 'Ping', use: 'Server availability' },
+ { type: 'Port', use: 'Specific services (databases, mail servers)' }
+ ]
+}
+```
+
+---
+
+## Content Types
+
+### Block Types Reference
+
+| Type | Purpose | When to Use |
+|------|---------|-------------|
+| `heading` | Section titles | Start each major topic |
+| `paragraph` | Body text | Explanations, context |
+| `bullet-list` | Unordered items | Features, options |
+| `ordered-list` | Sequential steps | Procedures |
+| `checklist` | Checkable items | Requirements, checklists |
+| `callout` | Important notices | Tips, warnings |
+| `code` | Code blocks | Commands, config examples |
+| `table` | Structured data | Comparisons, reference |
+| `image` | Screenshots | UI demonstration |
+| `icon-cards` | Feature grids | Capability overviews |
+| `article-links` | Related content | End-of-article navigation |
+
+### Icon Cards
+
+Use for presenting 4-8 related features or concepts:
+
+```javascript
+{
+ type: 'icon-cards',
+ items: [
+ {
+ icon: 'Globe',
+ title: 'HTTP monitoring',
+ description: 'Check website availability and response times'
+ },
+ {
+ icon: 'Server',
+ title: 'Infrastructure',
+ description: 'Monitor CPU, memory, and disk usage'
+ }
+ ]
+}
+```
+
+### Article Links
+
+Always end articles with related content:
+
+```javascript
+{
+ type: 'article-links',
+ title: 'Related articles',
+ items: [
+ {
+ collectionId: 'uptime-monitoring',
+ articleId: 'http-monitors',
+ title: 'Creating HTTP monitors',
+ description: 'Set up your first website monitor'
+ }
+ ]
+}
+```
+
+---
+
+## Images & Screenshots
+
+### When to Include Images
+
+- After introducing a feature (not before)
+- For complex UI that benefits from visual reference
+- For multi-step workflows
+- Skip for simple, text-describable actions
+
+### Image Requirements
+
+```javascript
+{
+ type: 'image',
+ src: '/images/user-guide/monitor-create.png',
+ alt: 'Monitor creation form showing URL field, name field, and interval selector',
+ caption: 'The monitor creation form with required fields highlighted'
+}
+```
+
+**Alt text guidelines:**
+- Describe what the image shows (15-30 words)
+- Include key UI elements visible
+- Make it useful for screen readers
+
+**Caption guidelines:**
+- Explain the context or what to notice
+- Keep to one sentence
+- Optional but recommended
+
+### Screenshot Standards
+
+- Capture at consistent browser width (1200px recommended)
+- Use light theme for screenshots
+- Highlight relevant areas if needed
+- Avoid personal data in screenshots
+- Crop to show relevant UI only
+
+---
+
+## Writing Procedures
+
+### Step-by-Step Instructions
+
+**Format:**
+```javascript
+{
+ type: 'ordered-list',
+ items: [
+ { text: 'Navigate to **Uptime** in the sidebar' },
+ { text: 'Click **Create monitor** in the top right' },
+ { text: 'Enter the URL you want to monitor' },
+ { text: 'Set the check interval (default: 5 minutes)' },
+ { text: 'Click **Save** to create the monitor' }
+ ]
+}
+```
+
+**Guidelines:**
+- Start each step with an action verb (Click, Enter, Select, Navigate)
+- One action per step
+- Include the location of UI elements
+- Bold all clickable elements
+- Add context in parentheses when helpful
+
+### Describing Features
+
+**Format:**
+```javascript
+{
+ type: 'bullet-list',
+ items: [
+ { bold: 'Name', text: 'A descriptive label for your monitor' },
+ { bold: 'URL', text: 'The address to check (must include protocol)' },
+ { bold: 'Interval', text: 'How often to perform checks (1-60 minutes)' }
+ ]
+}
+```
+
+---
+
+## Terminology
+
+### Consistent Terms
+
+Use these terms consistently throughout documentation:
+
+| Term | Use | Don't Use |
+|------|-----|-----------|
+| Monitor | The configured check | Watcher, probe, check |
+| Check | A single monitoring event | Ping, request, poll |
+| Incident | A detected problem | Outage, alert, issue |
+| Notification | Alert sent to user | Message, email, ping |
+| Dashboard | Main overview page | Home, overview |
+| Sidebar | Left navigation | Menu, nav |
+
+### Feature Names
+
+Always match UI exactly:
+- **Uptime** (not "Uptime Monitoring")
+- **PageSpeed** (not "Page Speed" or "Pagespeed")
+- **Infrastructure** (not "Server Monitoring")
+- **Status pages** (not "Status Pages" - lowercase 'p')
+
+---
+
+## Article Templates
+
+### Getting Started Article
+
+```javascript
+{
+ blocks: [
+ {
+ type: 'heading',
+ level: 2,
+ text: 'Overview',
+ id: 'overview'
+ },
+ {
+ type: 'paragraph',
+ text: '[1-2 sentences: What this feature/concept is]'
+ },
+ {
+ type: 'paragraph',
+ text: '[1-2 sentences: Why it matters to users]'
+ },
+ {
+ type: 'heading',
+ level: 2,
+ text: '[Main topic]',
+ id: 'main-topic'
+ },
+ // ... content blocks
+ {
+ type: 'article-links',
+ title: 'Next steps',
+ items: [
+ // 2-3 logical next articles
+ ]
+ }
+ ]
+}
+```
+
+### How-To Article
+
+```javascript
+{
+ blocks: [
+ {
+ type: 'heading',
+ level: 2,
+ text: 'Overview',
+ id: 'overview'
+ },
+ {
+ type: 'paragraph',
+ text: '[Brief description of what user will accomplish]'
+ },
+ {
+ type: 'heading',
+ level: 2,
+ text: '[Action: Creating/Configuring/Setting up...]',
+ id: 'action'
+ },
+ {
+ type: 'ordered-list',
+ items: [
+ // 4-6 steps
+ ]
+ },
+ {
+ type: 'callout',
+ variant: 'tip',
+ text: '[Best practice or helpful hint]'
+ },
+ {
+ type: 'article-links',
+ title: 'Related articles',
+ items: []
+ }
+ ]
+}
+```
+
+### Reference Article
+
+```javascript
+{
+ blocks: [
+ {
+ type: 'heading',
+ level: 2,
+ text: 'Overview',
+ id: 'overview'
+ },
+ {
+ type: 'paragraph',
+ text: '[What this reference covers]'
+ },
+ {
+ type: 'heading',
+ level: 2,
+ text: '[Category 1]',
+ id: 'category-1'
+ },
+ {
+ type: 'table',
+ columns: [...],
+ rows: [...]
+ },
+ // More categories as needed
+ {
+ type: 'article-links',
+ title: 'Related articles',
+ items: []
+ }
+ ]
+}
+```
+
+---
+
+## Quality Checklist
+
+Before publishing any article, verify:
+
+### Content
+- [ ] Overview explains what AND why
+- [ ] Procedures are tested and accurate
+- [ ] All UI element names match exactly
+- [ ] No assumptions about user knowledge
+- [ ] Related articles are linked
+
+### Formatting
+- [ ] Only H2 and H3 headings used
+- [ ] All headings have unique IDs
+- [ ] Lists use correct type (bullet vs ordered)
+- [ ] Callouts are used sparingly (max 3)
+- [ ] Bold used for UI elements
+
+### Style
+- [ ] Active voice throughout
+- [ ] Second person ("you")
+- [ ] Action verbs start procedure steps
+- [ ] Consistent terminology
+- [ ] No marketing language
+
+### Technical
+- [ ] All links work
+- [ ] Images have alt text
+- [ ] Code examples are correct
+- [ ] No placeholder content
+
+---
+
+## Available Icons
+
+For icon-cards and other icon references, use these Lucide icon names:
+
+**Navigation:**
+- `Globe` - Uptime/HTTP
+- `Gauge` - PageSpeed
+- `Server` - Infrastructure
+- `Bell` - Notifications
+- `AlertTriangle` - Incidents
+- `Wifi` - Status pages
+- `Wrench` - Maintenance
+- `Settings` - Settings
+- `Users` - Team
+
+**Actions:**
+- `CheckCircle` - Success/complete
+- `Clock` - Time/scheduling
+- `Info` - Information
+- `Lightbulb` - Tips
+- `ArrowRight` - Navigation
+
+**Features:**
+- `Database` - Data/storage
+- `Terminal` - Commands
+- `FileText` - Documentation
+- `Shield` - Security
+- `Rocket` - Getting started
+
+---
+
+## File Structure
+
+Articles are stored in `/content/index.js`:
+
+```javascript
+export const articleContents = {
+ 'collection-id': {
+ 'article-id': {
+ blocks: [
+ // Content blocks
+ ]
+ }
+ }
+};
+```
+
+Article metadata is in `/content/userGuideConfig.js`:
+
+```javascript
+{
+ id: 'article-id',
+ title: 'Article Title',
+ description: 'Brief description for search and previews',
+ keywords: ['search', 'terms', 'for', 'this', 'article']
+}
+```
+
+---
+
+## Examples
+
+### Good Paragraph
+> "HTTP monitors check your websites and APIs at regular intervals. When a check fails—due to a timeout, error response, or connection issue—Checkmate creates an incident and sends notifications to your configured channels."
+
+### Good Procedure
+> 1. Navigate to **Uptime** in the sidebar
+> 2. Click **Create monitor** in the top right
+> 3. Enter the URL you want to monitor (include `https://`)
+> 4. Set your preferred check interval
+> 5. Click **Save** to create the monitor
+
+### Good Callout
+> **Tip:** Use descriptive monitor names like "Production API" or "Marketing Website" to quickly identify issues in notifications.
+
+### Good Feature List
+> - **Name** — A descriptive label for easy identification
+> - **URL** — The address to monitor (must include protocol)
+> - **Interval** — How often to check (1-60 minutes)
+> - **Timeout** — How long to wait before marking as failed
+
+---
+
+*Last updated: January 2025*
diff --git a/client/src/Components/v1/UserGuide/SearchResults.css b/client/src/Components/v1/UserGuide/SearchResults.css
new file mode 100644
index 000000000..3d027be4e
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/SearchResults.css
@@ -0,0 +1,10 @@
+/* SearchResults styles (Dark theme) */
+
+.user-guide-search-result-item {
+ transition: background-color 150ms ease, border-color 150ms ease;
+}
+
+.user-guide-search-result-item:hover {
+ background-color: rgba(255, 255, 255, 0.05) !important;
+ border-color: #4a5568 !important;
+}
diff --git a/client/src/Components/v1/UserGuide/SearchResults.jsx b/client/src/Components/v1/UserGuide/SearchResults.jsx
new file mode 100644
index 000000000..86cb2f4c5
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/SearchResults.jsx
@@ -0,0 +1,173 @@
+import { FileText, ArrowRight } from "lucide-react";
+import { searchArticles } from "./content/userGuideConfig";
+import { useUserGuideTheme } from "./styles/useUserGuideTheme";
+import "./SearchResults.css";
+
+const SearchResults = ({ query, onNavigate, onClearSearch }) => {
+ const { colors, typography, spacing, border } = useUserGuideTheme();
+ const results = searchArticles(query);
+
+ const handleResultClick = (result) => {
+ onNavigate(result.collectionId, result.articleId);
+ onClearSearch();
+ };
+
+ if (query.trim().length < 2) {
+ return (
+
+
+ Type at least 2 characters to search...
+
+
+ );
+ }
+
+ if (results.length === 0) {
+ return (
+
+
+ No results found for "{query}"
+
+
+ Try different keywords or browse by topic
+
+
+ );
+ }
+
+ return (
+
+
+
+ {results.length} result{results.length !== 1 ? "s" : ""} for "{query}"
+
+
+
+
+
+ {results.map((result) => (
+
handleResultClick(result)}
+ className="user-guide-search-result-item"
+ style={{
+ display: "flex",
+ alignItems: "flex-start",
+ gap: spacing.md,
+ padding: spacing.md,
+ backgroundColor: colors.background.white,
+ border: border.default,
+ borderRadius: border.radius,
+ cursor: "pointer",
+ }}
+ >
+
+
+
+ {result.articleTitle}
+
+
+ {result.articleDescription}
+
+
+ in {result.collectionTitle}
+
+
+
+
+ ))}
+
+
+ );
+};
+
+export default SearchResults;
diff --git a/client/src/Components/v1/UserGuide/SidebarHeader.css b/client/src/Components/v1/UserGuide/SidebarHeader.css
new file mode 100644
index 000000000..11b69c153
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/SidebarHeader.css
@@ -0,0 +1,40 @@
+/* SidebarHeader styles - Dark theme */
+
+.user-guide-header-icon-button {
+ transition: all 150ms ease;
+}
+
+.user-guide-header-icon-button:hover {
+ background-color: rgba(255, 255, 255, 0.1) !important;
+ color: #1570EF !important;
+}
+
+.user-guide-header-icon-button.active {
+ background-color: #0d0d0d;
+ color: #1570EF;
+}
+
+.user-guide-breadcrumb-dropdown-trigger {
+ transition: background-color 150ms ease;
+}
+
+.user-guide-breadcrumb-dropdown-trigger:hover {
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.user-guide-breadcrumb-item {
+ transition: background-color 150ms ease;
+}
+
+.user-guide-breadcrumb-item:hover {
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.user-guide-search-input:focus {
+ outline: none;
+ border-color: #1570EF;
+}
+
+.user-guide-search-input::placeholder {
+ color: #667085;
+}
diff --git a/client/src/Components/v1/UserGuide/SidebarHeader.jsx b/client/src/Components/v1/UserGuide/SidebarHeader.jsx
new file mode 100644
index 000000000..cc3137d3d
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/SidebarHeader.jsx
@@ -0,0 +1,381 @@
+import { useState, useRef, useEffect } from "react";
+import {
+ Home,
+ ChevronDown,
+ ChevronLeft,
+ ChevronRight,
+ Search,
+ ExternalLink,
+ X,
+} from "lucide-react";
+import { useUserGuideTheme } from "./styles/useUserGuideTheme";
+import "./SidebarHeader.css";
+
+const SidebarHeader = ({
+ breadcrumbs,
+ onHomeClick,
+ onBack,
+ onForward,
+ canGoBack,
+ canGoForward,
+ onClose,
+ onOpenInNewTab,
+ isSearchOpen,
+ onSearchToggle,
+ searchQuery,
+ onSearchChange,
+ showOpenInNewTab = true,
+ showNavigation = true,
+}) => {
+ const { colors, typography, spacing, border, isDark } = useUserGuideTheme();
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
+ const dropdownRef = useRef(null);
+ const searchInputRef = useRef(null);
+
+ // Close dropdown when clicking outside
+ useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
+ setIsDropdownOpen(false);
+ }
+ };
+
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => document.removeEventListener("mousedown", handleClickOutside);
+ }, []);
+
+ // Focus search input when opened
+ useEffect(() => {
+ if (isSearchOpen && searchInputRef.current) {
+ searchInputRef.current.focus();
+ }
+ }, [isSearchOpen]);
+
+ // Get current breadcrumb (last item)
+ const currentBreadcrumb = breadcrumbs[breadcrumbs.length - 1];
+
+ return (
+
+ {/* Main header row */}
+
+ {/* Left side: Navigation + Breadcrumb dropdown */}
+
+ {/* Navigation buttons - only shown when showNavigation is true */}
+ {showNavigation && (
+ <>
+ {/* Back button */}
+
+
+ {/* Forward button */}
+
+
+ {/* Home button */}
+
+ >
+ )}
+
+ {/* Breadcrumb dropdown */}
+
+
+
+ {/* Dropdown menu */}
+ {isDropdownOpen && breadcrumbs.length > 1 && (
+
+ {breadcrumbs.slice(0, -1).map((item, index) => (
+
+ ))}
+
+ )}
+
+
+
+ {/* Right side: Actions */}
+
+ {/* Open in new tab - only shown when showOpenInNewTab is true */}
+ {showOpenInNewTab && (
+
+ )}
+
+ {/* Search toggle */}
+
+
+ {/* Close button */}
+
+
+
+
+ {/* Search input (collapsible) */}
+ {isSearchOpen && (
+
+
+
+ onSearchChange(e.target.value)}
+ className="user-guide-search-input"
+ style={{
+ width: "100%",
+ height: 32,
+ fontSize: typography.fontSize.sm,
+ fontFamily: typography.fontFamily.sans,
+ backgroundColor: colors.background.alt,
+ borderRadius: border.radius,
+ border: border.default,
+ paddingLeft: 32,
+ paddingRight: 12,
+ boxSizing: "border-box",
+ color: colors.text.primary,
+ }}
+ />
+
+
+ )}
+
+ );
+};
+
+export default SidebarHeader;
diff --git a/client/src/Components/v1/UserGuide/SidebarWrapper.css b/client/src/Components/v1/UserGuide/SidebarWrapper.css
new file mode 100644
index 000000000..d5035982c
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/SidebarWrapper.css
@@ -0,0 +1,11 @@
+/* SidebarWrapper styles (Dark theme) */
+
+.user-guide-sidebar-container {
+ box-shadow: -2px 0 8px rgba(0, 0, 0, 0.3);
+}
+
+/* Push main content when sidebar is open */
+body.user-guide-sidebar-open .home-content-wrapper {
+ margin-right: var(--help-sidebar-padding, 440px);
+ transition: margin-right 300ms ease-in-out;
+}
diff --git a/client/src/Components/v1/UserGuide/SidebarWrapper.jsx b/client/src/Components/v1/UserGuide/SidebarWrapper.jsx
new file mode 100644
index 000000000..85ac7e9e5
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/SidebarWrapper.jsx
@@ -0,0 +1,475 @@
+import { useState, useEffect, useCallback, useRef } from "react";
+import { useUserGuideTheme } from "./styles/useUserGuideTheme";
+import TabBar from "./TabBar";
+import SidebarHeader from "./SidebarHeader";
+import UserGuideLanding from "./UserGuideLanding";
+import CollectionPage from "./CollectionPage";
+import ArticlePage from "./ArticlePage";
+import ContentRenderer from "./ContentRenderer";
+import HelpSection from "./HelpSection";
+import SearchResults from "./SearchResults";
+import { getCollection, getArticle } from "./content/userGuideConfig";
+import { getArticleContent } from "./content";
+import { extractToc } from "./content/contentTypes";
+import { useUserGuideSidebarContext, DEFAULT_CONTENT_WIDTH } from "./UserGuideSidebarContext";
+import "./SidebarWrapper.css";
+
+const STORAGE_KEY = "checkmate-sidebar-state";
+const MIN_CONTENT_WIDTH = DEFAULT_CONTENT_WIDTH;
+const MAX_CONTENT_WIDTH = DEFAULT_CONTENT_WIDTH * 2; // 100% wider
+
+const SidebarWrapper = ({ isOpen, onClose, onOpen, initialPath, onOpenInNewTab }) => {
+ const { setContentWidth: setContextContentWidth } = useUserGuideSidebarContext();
+ const { colors, border } = useUserGuideTheme();
+ const [activeTab, setActiveTab] = useState("user-guide");
+ const [collectionId, setCollectionId] = useState(undefined);
+ const [articleId, setArticleId] = useState(undefined);
+ const [searchQuery, setSearchQuery] = useState("");
+ const [isSearchOpen, setIsSearchOpen] = useState(false);
+ const [contentWidth, setContentWidthLocal] = useState(DEFAULT_CONTENT_WIDTH);
+ const [isResizing, setIsResizing] = useState(false);
+ const [isHoveringHandle, setIsHoveringHandle] = useState(false);
+ const [mouseY, setMouseY] = useState(0);
+ const resizeRef = useRef(null);
+ const handleRef = useRef(null);
+
+ // Sync content width with context whenever it changes
+ const setContentWidth = useCallback(
+ (width) => {
+ setContentWidthLocal(width);
+ setContextContentWidth(width);
+ },
+ [setContextContentWidth]
+ );
+
+ // Initialize context with current width on mount
+ useEffect(() => {
+ setContextContentWidth(DEFAULT_CONTENT_WIDTH);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []); // Only run once on mount
+
+ // Navigation history
+ const [history, setHistory] = useState([
+ { collectionId: undefined, articleId: undefined },
+ ]);
+ const [historyIndex, setHistoryIndex] = useState(0);
+ const isNavigatingRef = useRef(false);
+
+ // Parse initial path on mount
+ useEffect(() => {
+ if (initialPath) {
+ const parts = initialPath.split("/");
+ if (parts.length >= 1 && parts[0]) {
+ setCollectionId(parts[0]);
+ if (parts.length >= 2 && parts[1]) {
+ setArticleId(parts[1]);
+ }
+ }
+ }
+ }, [initialPath]);
+
+ // Persist sidebar state to localStorage
+ useEffect(() => {
+ if (isOpen) {
+ const state = { activeTab, collectionId, articleId };
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
+ }
+ }, [isOpen, activeTab, collectionId, articleId]);
+
+ // Restore state from localStorage on mount
+ useEffect(() => {
+ const savedState = localStorage.getItem(STORAGE_KEY);
+ if (savedState && !initialPath) {
+ try {
+ const {
+ activeTab: savedTab,
+ collectionId: savedCollection,
+ articleId: savedArticle,
+ } = JSON.parse(savedState);
+ if (savedTab) setActiveTab(savedTab);
+ if (savedCollection) setCollectionId(savedCollection);
+ if (savedArticle) setArticleId(savedArticle);
+ } catch (e) {
+ // Ignore parse errors
+ }
+ }
+ }, [initialPath]);
+
+ // Track navigation changes in history
+ useEffect(() => {
+ if (isNavigatingRef.current) {
+ isNavigatingRef.current = false;
+ return;
+ }
+ // Only track if this is a new navigation (not from back/forward)
+ const currentEntry = history[historyIndex];
+ if (
+ currentEntry?.collectionId !== collectionId ||
+ currentEntry?.articleId !== articleId
+ ) {
+ // Truncate forward history and add new entry
+ const newHistory = [
+ ...history.slice(0, historyIndex + 1),
+ { collectionId, articleId },
+ ];
+ setHistory(newHistory);
+ setHistoryIndex(newHistory.length - 1);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [collectionId, articleId]);
+
+ // Navigation handlers
+ const handleNavigate = useCallback((newCollectionId, newArticleId) => {
+ setCollectionId(newCollectionId);
+ setArticleId(newArticleId);
+ }, []);
+
+ const handleBackToHome = useCallback(() => {
+ setCollectionId(undefined);
+ setArticleId(undefined);
+ }, []);
+
+ // Back/Forward navigation
+ const canGoBack = historyIndex > 0;
+ const canGoForward = historyIndex < history.length - 1;
+
+ const handleBack = useCallback(() => {
+ if (historyIndex > 0) {
+ isNavigatingRef.current = true;
+ const prevIndex = historyIndex - 1;
+ const prevEntry = history[prevIndex];
+ setHistoryIndex(prevIndex);
+ setCollectionId(prevEntry.collectionId);
+ setArticleId(prevEntry.articleId);
+ }
+ }, [historyIndex, history]);
+
+ const handleForward = useCallback(() => {
+ if (historyIndex < history.length - 1) {
+ isNavigatingRef.current = true;
+ const nextIndex = historyIndex + 1;
+ const nextEntry = history[nextIndex];
+ setHistoryIndex(nextIndex);
+ setCollectionId(nextEntry.collectionId);
+ setArticleId(nextEntry.articleId);
+ }
+ }, [historyIndex, history]);
+
+ const handleBackToCollection = useCallback(() => {
+ setArticleId(undefined);
+ }, []);
+
+ const handleHomeClick = useCallback(() => {
+ setCollectionId(undefined);
+ setArticleId(undefined);
+ }, []);
+
+ // Get current collection and article
+ const collection = collectionId ? getCollection(collectionId) : undefined;
+ const article = collectionId && articleId ? getArticle(collectionId, articleId) : undefined;
+
+ // Get prev/next articles for navigation
+ const getAdjacentArticles = () => {
+ if (!collection || !articleId) return { prev: undefined, next: undefined };
+ const currentIndex = collection.articles.findIndex((a) => a.id === articleId);
+ return {
+ prev: currentIndex > 0 ? collection.articles[currentIndex - 1] : undefined,
+ next:
+ currentIndex < collection.articles.length - 1
+ ? collection.articles[currentIndex + 1]
+ : undefined,
+ };
+ };
+
+ const { prev: prevArticle, next: nextArticle } = getAdjacentArticles();
+
+ // Get article content
+ const articleContent =
+ collectionId && articleId ? getArticleContent(collectionId, articleId) : undefined;
+
+ // Auto-generate TOC from content headings
+ const tocItems = articleContent ? extractToc(articleContent.blocks) : undefined;
+
+ // Build breadcrumb items for dropdown
+ const buildBreadcrumbs = () => {
+ let items = [];
+ switch (activeTab) {
+ case "user-guide":
+ items = [{ label: "User guide", onClick: handleHomeClick }];
+ if (collection) {
+ items.push({ label: collection.title, onClick: () => setArticleId(undefined) });
+ }
+ if (article) {
+ items.push({ label: article.title, onClick: () => {} });
+ }
+ return items;
+ default:
+ return [{ label: "Help", onClick: () => {} }];
+ }
+ };
+
+ // Handle "Open in new tab"
+ const handleOpenInNewTab = () => {
+ let path = "https://bluewavelabs.gitbook.io/checkmate";
+ if (onOpenInNewTab) {
+ onOpenInNewTab();
+ } else {
+ window.open(path, "_blank", "noopener,noreferrer");
+ }
+ };
+
+ // Clear search and close search bar
+ const handleClearSearch = useCallback(() => {
+ setSearchQuery("");
+ setIsSearchOpen(false);
+ }, []);
+
+ // Render User Guide content
+ const renderUserGuideContent = () => {
+ // Show search results when search is active
+ if (isSearchOpen && searchQuery.trim().length > 0) {
+ return (
+ {
+ handleNavigate(colId, artId);
+ handleClearSearch();
+ }}
+ onClearSearch={handleClearSearch}
+ />
+ );
+ }
+
+ if (collection && article) {
+ return (
+ handleNavigate(collectionId, prevArticle.id) : undefined
+ }
+ onNextArticle={
+ nextArticle ? () => handleNavigate(collectionId, nextArticle.id) : undefined
+ }
+ tocItems={tocItems}
+ mode="in-app"
+ >
+ {articleContent && (
+
+ )}
+
+ );
+ }
+
+ if (collection) {
+ return (
+ handleNavigate(collectionId, id)}
+ />
+ );
+ }
+
+ return (
+
+ );
+ };
+
+ const contentArea = (tabValue) => {
+ switch (tabValue) {
+ case "user-guide":
+ return renderUserGuideContent();
+ default:
+ return ;
+ }
+ };
+
+ // Handle tab click - open sidebar if closed, close if clicking active tab, or switch tab
+ const handleTabClick = (tab) => {
+ if (!isOpen) {
+ onOpen();
+ setActiveTab(tab);
+ } else if (activeTab === tab) {
+ // Clicking the already-active tab closes the sidebar
+ onClose();
+ } else {
+ // Clicking a different tab switches to it
+ setActiveTab(tab);
+ }
+ };
+
+ // Resize handlers
+ const handleResizeStart = useCallback(
+ (e) => {
+ e.preventDefault();
+ setIsResizing(true);
+ resizeRef.current = { startX: e.clientX, startWidth: contentWidth };
+ },
+ [contentWidth]
+ );
+
+ const handleResizeMove = useCallback(
+ (e) => {
+ if (!isResizing || !resizeRef.current) return;
+
+ const deltaX = resizeRef.current.startX - e.clientX;
+ const newWidth = Math.min(
+ MAX_CONTENT_WIDTH,
+ Math.max(MIN_CONTENT_WIDTH, resizeRef.current.startWidth + deltaX)
+ );
+ setContentWidth(newWidth);
+ },
+ [isResizing, setContentWidth]
+ );
+
+ const handleResizeEnd = useCallback(() => {
+ setIsResizing(false);
+ setIsHoveringHandle(false);
+ resizeRef.current = null;
+ }, []);
+
+ // Add/remove resize event listeners
+ useEffect(() => {
+ if (isResizing) {
+ document.addEventListener("mousemove", handleResizeMove);
+ document.addEventListener("mouseup", handleResizeEnd);
+ document.body.style.cursor = "ew-resize";
+ document.body.style.userSelect = "none";
+ }
+ return () => {
+ document.removeEventListener("mousemove", handleResizeMove);
+ document.removeEventListener("mouseup", handleResizeEnd);
+ document.body.style.cursor = "";
+ document.body.style.userSelect = "";
+ };
+ }, [isResizing, handleResizeMove, handleResizeEnd]);
+
+ return (
+
+ {/* Resize Handle - positioned absolutely on the left edge */}
+
setIsHoveringHandle(true)}
+ onMouseLeave={() => !isResizing && setIsHoveringHandle(false)}
+ onMouseMove={(e) => {
+ if (handleRef.current) {
+ const rect = handleRef.current.getBoundingClientRect();
+ setMouseY(e.clientY - rect.top);
+ }
+ }}
+ style={{
+ position: "absolute",
+ left: 0,
+ top: 0,
+ width: 8,
+ height: "100%",
+ cursor: "ew-resize",
+ zIndex: 20,
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "flex-start",
+ }}
+ >
+ {/* Visual indicator line */}
+
+ {/* Grab indicator follows mouse */}
+ {(isHoveringHandle || isResizing) && (
+
+ )}
+
+
+
+ {/* Tab Bar - Always visible */}
+
+
+ {/* Main Sidebar Content - Slides in/out */}
+
+ {isOpen && (
+ <>
+ {/* Header */}
+
setIsSearchOpen(!isSearchOpen)}
+ searchQuery={searchQuery}
+ onSearchChange={setSearchQuery}
+ />
+
+ {/* Content Area */}
+
+ {contentArea(activeTab)}
+
+ >
+ )}
+
+
+ );
+};
+
+export default SidebarWrapper;
diff --git a/client/src/Components/v1/UserGuide/TabBar.css b/client/src/Components/v1/UserGuide/TabBar.css
new file mode 100644
index 000000000..733ac7106
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/TabBar.css
@@ -0,0 +1,13 @@
+/* TabBar styles */
+
+.user-guide-tab-item {
+ transition: all 150ms ease;
+}
+
+.user-guide-tab-item:hover {
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.user-guide-tab-item.active:hover {
+ background-color: #1a1a1a;
+}
diff --git a/client/src/Components/v1/UserGuide/TabBar.jsx b/client/src/Components/v1/UserGuide/TabBar.jsx
new file mode 100644
index 000000000..7a8999f94
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/TabBar.jsx
@@ -0,0 +1,86 @@
+import { BookOpen, HelpCircle } from "lucide-react";
+import { useUserGuideTheme } from "./styles/useUserGuideTheme";
+import "./TabBar.css";
+
+const TabItem = ({ label, icon, isActive, onClick, colors, typography }) => {
+ return (
+
+
+ {icon}
+
+
+ {label}
+
+
+ );
+};
+
+const TabBar = ({ activeTab, onTabChange }) => {
+ const { colors, typography } = useUserGuideTheme();
+
+ return (
+
+ }
+ isActive={activeTab === "user-guide"}
+ onClick={() => onTabChange("user-guide")}
+ colors={colors}
+ typography={typography}
+ />
+ }
+ isActive={activeTab === "help"}
+ onClick={() => onTabChange("help")}
+ colors={colors}
+ typography={typography}
+ />
+
+ );
+};
+
+export default TabBar;
diff --git a/client/src/Components/v1/UserGuide/UserGuideLanding.css b/client/src/Components/v1/UserGuide/UserGuideLanding.css
new file mode 100644
index 000000000..7fe9a6a9a
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/UserGuideLanding.css
@@ -0,0 +1,39 @@
+/* UserGuideLanding styles - hover states */
+
+/* Collection card hover */
+.user-guide-collection-card {
+ transition: all 150ms ease;
+}
+
+.user-guide-collection-card:hover {
+ border-color: rgba(21, 112, 239, 0.3) !important;
+ background-color: rgba(21, 112, 239, 0.02) !important;
+ box-shadow: 0px 2px 8px -2px rgba(0, 0, 0, 0.08);
+}
+
+[data-mui-color-scheme="dark"] .user-guide-collection-card:hover {
+ border-color: rgba(21, 112, 239, 0.5) !important;
+ background-color: rgba(21, 112, 239, 0.05) !important;
+ box-shadow: 0px 4px 16px -4px rgba(0, 0, 0, 0.4);
+}
+
+/* Fast find item hover */
+.user-guide-fast-find-item {
+ transition: all 150ms ease;
+}
+
+.user-guide-fast-find-item:hover {
+ background-color: rgba(0, 0, 0, 0.04);
+}
+
+[data-mui-color-scheme="dark"] .user-guide-fast-find-item:hover {
+ background-color: rgba(255, 255, 255, 0.05);
+}
+
+.user-guide-fast-find-item .arrow-icon {
+ transition: all 150ms ease;
+}
+
+.user-guide-fast-find-item:hover .arrow-icon {
+ color: #1570EF;
+}
diff --git a/client/src/Components/v1/UserGuide/UserGuideLanding.jsx b/client/src/Components/v1/UserGuide/UserGuideLanding.jsx
new file mode 100644
index 000000000..563d7ae31
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/UserGuideLanding.jsx
@@ -0,0 +1,210 @@
+import {
+ ArrowRight,
+ Rocket,
+ Globe,
+ Gauge,
+ Server,
+ Bell,
+ AlertTriangle,
+ Settings,
+ Wifi,
+ Wrench,
+ Users,
+} from "lucide-react";
+import { collections, fastFinds, getCollection } from "./content/userGuideConfig";
+import { useUserGuideTheme } from "./styles/useUserGuideTheme";
+import "./UserGuideLanding.css";
+
+// Map icon names to actual Lucide components
+const iconMap = {
+ Rocket,
+ Globe,
+ Gauge,
+ Server,
+ Bell,
+ AlertTriangle,
+ Settings,
+ Wifi,
+ Wrench,
+ Users,
+};
+
+const UserGuideLanding = ({ onNavigate, searchQuery, onSearchChange, mode = "in-app" }) => {
+ const { colors, typography, spacing, border } = useUserGuideTheme();
+ const isInApp = mode === "in-app";
+
+ return (
+
+ {/* Main Content */}
+
+ {/* Collections Grid */}
+
+ Browse by topic
+
+
+
+ {collections.map((collection) => {
+ const IconComponent = iconMap[collection.icon] || Rocket;
+ return (
+
onNavigate(collection.id)}
+ className="user-guide-collection-card"
+ style={{
+ backgroundColor: colors.background.white,
+ border: border.default,
+ borderRadius: border.radius,
+ padding: spacing.lg,
+ cursor: "pointer",
+ }}
+ >
+
+
+
+
+
+ {collection.title}
+
+
+ {collection.articleCount} articles
+
+
+
+ {collection.description}
+
+
+
+
+ );
+ })}
+
+
+ {/* About our docs - Only shown in in-app mode */}
+ {isInApp && (
+
+
+ About our docs
+
+
+ There are a few ways to explore our docs:
+
+
+
+
+ In the product
+
+
+ Look for help icons and tooltips throughout the interface.
+
+
+
+
+ )}
+
+
+ );
+};
+
+export default UserGuideLanding;
diff --git a/client/src/Components/v1/UserGuide/UserGuideSidebarContext.jsx b/client/src/Components/v1/UserGuide/UserGuideSidebarContext.jsx
new file mode 100644
index 000000000..bfcd09973
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/UserGuideSidebarContext.jsx
@@ -0,0 +1,99 @@
+import { createContext, useContext, useState, useCallback, useEffect } from "react";
+
+const SIDEBAR_STATE_KEY = "checkmate-sidebar-open";
+
+// Constants for sidebar dimensions
+export const TAB_BAR_WIDTH = 40;
+export const DEFAULT_CONTENT_WIDTH = 400;
+// Additional gap beyond the sidebar width
+export const MIN_GAP = 0;
+
+const UserGuideSidebarContext = createContext(null);
+
+export const UserGuideSidebarProvider = ({ children }) => {
+ const [isOpen, setIsOpen] = useState(() => {
+ if (typeof window === "undefined") return false;
+ const saved = localStorage.getItem(SIDEBAR_STATE_KEY);
+ return saved === "true";
+ });
+
+ const [currentPath, setCurrentPath] = useState(undefined);
+ const [contentWidth, setContentWidth] = useState(DEFAULT_CONTENT_WIDTH);
+
+ // Calculate total sidebar width and required padding
+ const totalSidebarWidth = isOpen ? TAB_BAR_WIDTH + contentWidth : TAB_BAR_WIDTH;
+ const requiredPaddingRight = totalSidebarWidth + MIN_GAP;
+
+ // Set CSS custom properties on document root for global access
+ useEffect(() => {
+ if (typeof document === "undefined") return;
+
+ const root = document.documentElement;
+ root.style.setProperty("--help-sidebar-width", `${totalSidebarWidth}px`);
+ root.style.setProperty("--help-sidebar-padding", `${requiredPaddingRight}px`);
+ }, [totalSidebarWidth, requiredPaddingRight]);
+
+ // Update body class for content push effect
+ useEffect(() => {
+ if (typeof document === "undefined") return;
+
+ if (isOpen) {
+ document.body.classList.add("user-guide-sidebar-open");
+ } else {
+ document.body.classList.remove("user-guide-sidebar-open");
+ }
+
+ return () => {
+ document.body.classList.remove("user-guide-sidebar-open");
+ };
+ }, [isOpen]);
+
+ // Persist state to localStorage
+ useEffect(() => {
+ if (typeof window === "undefined") return;
+ localStorage.setItem(SIDEBAR_STATE_KEY, String(isOpen));
+ }, [isOpen]);
+
+ const open = useCallback((path) => {
+ setCurrentPath(path);
+ setIsOpen(true);
+ }, []);
+
+ const close = useCallback(() => {
+ setIsOpen(false);
+ }, []);
+
+ const toggle = useCallback(() => {
+ setIsOpen((prev) => !prev);
+ }, []);
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useUserGuideSidebarContext = () => {
+ const context = useContext(UserGuideSidebarContext);
+ if (!context) {
+ throw new Error(
+ "useUserGuideSidebarContext must be used within a UserGuideSidebarProvider"
+ );
+ }
+ return context;
+};
+
+export default UserGuideSidebarContext;
diff --git a/client/src/Components/v1/UserGuide/content/contentTypes.js b/client/src/Components/v1/UserGuide/content/contentTypes.js
new file mode 100644
index 000000000..3348c9a26
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/content/contentTypes.js
@@ -0,0 +1,176 @@
+// Content block types for the templatic article system
+
+/**
+ * @typedef {'heading' | 'paragraph' | 'callout' | 'bullet-list' | 'ordered-list' | 'checklist' | 'icon-cards' | 'grid-cards' | 'time-estimate' | 'info-box' | 'code' | 'requirements' | 'table' | 'image' | 'article-links'} ContentBlockType
+ */
+
+/**
+ * @typedef {Object} HeadingBlock
+ * @property {'heading'} type
+ * @property {string} id - Used for TOC anchors
+ * @property {2 | 3} level - h2 or h3
+ * @property {string} text
+ */
+
+/**
+ * @typedef {Object} ParagraphBlock
+ * @property {'paragraph'} type
+ * @property {string} text - Supports basic markdown: **bold**, *italic*, `code`
+ */
+
+/**
+ * @typedef {Object} CalloutBlock
+ * @property {'callout'} type
+ * @property {'info' | 'tip' | 'warning' | 'success'} [variant]
+ * @property {string} [title]
+ * @property {string} text
+ */
+
+/**
+ * @typedef {Object} ListItem
+ * @property {string} text
+ * @property {string} [bold] - Optional bold prefix
+ */
+
+/**
+ * @typedef {Object} BulletListBlock
+ * @property {'bullet-list'} type
+ * @property {ListItem[]} items
+ */
+
+/**
+ * @typedef {Object} OrderedListBlock
+ * @property {'ordered-list'} type
+ * @property {ListItem[]} items
+ */
+
+/**
+ * @typedef {Object} ChecklistBlock
+ * @property {'checklist'} type
+ * @property {string[]} items
+ */
+
+/**
+ * @typedef {Object} IconCardItem
+ * @property {string} icon - Icon name from lucide-react
+ * @property {string} title
+ * @property {string} description
+ */
+
+/**
+ * @typedef {Object} IconCardsBlock
+ * @property {'icon-cards'} type
+ * @property {IconCardItem[]} items
+ */
+
+/**
+ * @typedef {Object} GridCardItem
+ * @property {string} title
+ * @property {string} description
+ * @property {string} [icon]
+ */
+
+/**
+ * @typedef {Object} GridCardsBlock
+ * @property {'grid-cards'} type
+ * @property {GridCardItem[]} items
+ * @property {number} [columns]
+ */
+
+/**
+ * @typedef {Object} TimeEstimateBlock
+ * @property {'time-estimate'} type
+ * @property {string} text
+ */
+
+/**
+ * @typedef {Object} InfoBoxBlock
+ * @property {'info-box'} type
+ * @property {string} [icon]
+ * @property {string} title
+ * @property {string[]} items
+ */
+
+/**
+ * @typedef {Object} CodeBlock
+ * @property {'code'} type
+ * @property {string} code
+ * @property {string} [language]
+ */
+
+/**
+ * @typedef {Object} RequirementItem
+ * @property {string} icon
+ * @property {string} title
+ * @property {string[]} items
+ */
+
+/**
+ * @typedef {Object} RequirementsBlock
+ * @property {'requirements'} type
+ * @property {RequirementItem[]} items
+ */
+
+/**
+ * @typedef {Object} TableColumn
+ * @property {string} key
+ * @property {string} label
+ * @property {string} [width]
+ */
+
+/**
+ * @typedef {Object} TableBlock
+ * @property {'table'} type
+ * @property {TableColumn[]} columns
+ * @property {Object[]} rows
+ */
+
+/**
+ * @typedef {Object} ImageBlock
+ * @property {'image'} type
+ * @property {string} src
+ * @property {string} alt
+ * @property {string} [caption]
+ */
+
+/**
+ * @typedef {Object} ArticleLink
+ * @property {string} collectionId
+ * @property {string} articleId
+ * @property {string} title
+ * @property {string} [description]
+ */
+
+/**
+ * @typedef {Object} ArticleLinksBlock
+ * @property {'article-links'} type
+ * @property {string} [title] - e.g., "Next steps", "Related articles"
+ * @property {ArticleLink[]} items
+ */
+
+/**
+ * @typedef {Object} ArticleContent
+ * @property {ContentBlock[]} blocks
+ */
+
+/**
+ * @typedef {Object} TocItem
+ * @property {string} label
+ * @property {string} href
+ * @property {number} level
+ */
+
+/**
+ * Helper to extract TOC from content blocks
+ * @param {ContentBlock[]} blocks
+ * @returns {TocItem[]}
+ */
+export const extractToc = (blocks) => {
+ return blocks
+ .filter((block) => block.type === "heading")
+ .map((block) => ({
+ label: block.text,
+ href: `#${block.id}`,
+ level: block.level,
+ }));
+};
diff --git a/client/src/Components/v1/UserGuide/content/index.js b/client/src/Components/v1/UserGuide/content/index.js
new file mode 100644
index 000000000..6d6ddaf18
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/content/index.js
@@ -0,0 +1,2666 @@
+// Article content - each article has its content defined here
+// Import content as needed to keep bundle size manageable
+
+const articleContents = {
+ // ============================================
+ // GETTING STARTED COLLECTION
+ // ============================================
+
+ "getting-started/what-is-checkmate": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Checkmate is an open-source uptime and infrastructure monitoring platform. It helps you track website availability, server performance, and service health—all from a single dashboard.",
+ },
+ {
+ type: "paragraph",
+ text: "Whether you're monitoring a personal project or managing enterprise infrastructure, Checkmate provides the tools you need to stay informed about your systems.",
+ },
+ {
+ type: "heading",
+ id: "core-features",
+ level: 2,
+ text: "Core features",
+ },
+ {
+ type: "icon-cards",
+ items: [
+ {
+ icon: "Globe",
+ title: "Uptime monitoring",
+ description:
+ "Monitor websites and APIs with HTTP, ping, and TCP port checks.",
+ },
+ {
+ icon: "Gauge",
+ title: "PageSpeed insights",
+ description:
+ "Track website performance with Google Lighthouse integration.",
+ },
+ {
+ icon: "Server",
+ title: "Infrastructure monitoring",
+ description: "Monitor CPU, memory, disk, and network with the Capture agent.",
+ },
+ {
+ icon: "Bell",
+ title: "Instant alerts",
+ description: "Get notified via email, Slack, Discord, PagerDuty, or webhooks.",
+ },
+ ],
+ },
+ {
+ type: "heading",
+ id: "monitoring-types",
+ level: 2,
+ text: "Monitoring types",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ {
+ bold: "HTTP/HTTPS",
+ text: "Check website availability and response validation",
+ },
+ {
+ bold: "Ping",
+ text: "Verify server reachability using ICMP",
+ },
+ {
+ bold: "TCP port",
+ text: "Monitor specific services like databases and mail servers",
+ },
+ {
+ bold: "Docker",
+ text: "Monitor Docker container status and health",
+ },
+ {
+ bold: "PageSpeed",
+ text: "Track Core Web Vitals and performance scores",
+ },
+ {
+ bold: "Infrastructure",
+ text: "Monitor hardware metrics with the Capture agent",
+ },
+ ],
+ },
+ {
+ type: "heading",
+ id: "next-steps",
+ level: 2,
+ text: "Next steps",
+ },
+ {
+ type: "article-links",
+ items: [
+ {
+ collectionId: "getting-started",
+ articleId: "quick-start",
+ title: "Quick start guide",
+ description: "Create your first monitor in 5 minutes",
+ },
+ {
+ collectionId: "getting-started",
+ articleId: "dashboard",
+ title: "Understanding the dashboard",
+ description: "Learn to navigate the interface",
+ },
+ ],
+ },
+ ],
+ },
+
+ "getting-started/quick-start": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "This guide walks you through creating your first monitor and setting up notifications. You'll have a working monitoring setup in about 5 minutes.",
+ },
+ {
+ type: "heading",
+ id: "create-monitor",
+ level: 2,
+ text: "Creating your first monitor",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Uptime** in the sidebar" },
+ { text: "Click **Create monitor** in the top right" },
+ { text: "Select **HTTP/HTTPS** as the monitor type" },
+ { text: "Enter the URL you want to monitor (e.g., `https://example.com`)" },
+ { text: "Set the check interval (1-5 minutes recommended for critical services)" },
+ { text: "Click **Create monitor** to start monitoring" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "tip",
+ text: "Use descriptive names like \"Production API\" or \"Marketing Website\" to easily identify monitors in alerts.",
+ },
+ {
+ type: "heading",
+ id: "verify-monitor",
+ level: 2,
+ text: "Verifying your monitor",
+ },
+ {
+ type: "paragraph",
+ text: "After creating a monitor, Checkmate immediately performs the first check. You can verify it's working by:",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Click on your new monitor to open the details page" },
+ { text: "Check the status indicator shows **Up** (green)" },
+ { text: "View the response time in the metrics panel" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "setup-notifications",
+ level: 2,
+ text: "Setting up notifications",
+ },
+ {
+ type: "paragraph",
+ text: "To receive alerts when your monitor goes down:",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Notifications** in the sidebar" },
+ { text: "Click **Create notification**" },
+ { text: "Select your preferred channel (email, Slack, Discord, etc.)" },
+ { text: "Configure the notification settings" },
+ { text: "Click **Test** to verify the notification works" },
+ ],
+ },
+ {
+ type: "article-links",
+ title: "Next steps",
+ items: [
+ {
+ collectionId: "uptime-monitoring",
+ articleId: "http-monitors",
+ title: "Creating HTTP monitors",
+ description: "Advanced configuration options",
+ },
+ {
+ collectionId: "notifications",
+ articleId: "email-notifications",
+ title: "Email notifications",
+ description: "Configure SMTP for email alerts",
+ },
+ ],
+ },
+ ],
+ },
+
+ "getting-started/dashboard": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "The Checkmate dashboard gives you a complete view of your monitoring status. This article explains the main navigation areas and key metrics.",
+ },
+ {
+ type: "heading",
+ id: "sidebar-navigation",
+ level: 2,
+ text: "Sidebar navigation",
+ },
+ {
+ type: "paragraph",
+ text: "The left sidebar provides access to all major features:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Uptime", text: "View and manage HTTP, ping, port, and Docker monitors" },
+ { bold: "PageSpeed", text: "Access performance monitoring and Lighthouse reports" },
+ { bold: "Infrastructure", text: "Monitor server hardware metrics" },
+ { bold: "Incidents", text: "View current and past downtime events" },
+ { bold: "Status pages", text: "Create and manage public status pages" },
+ { bold: "Maintenance", text: "Schedule planned maintenance windows" },
+ { bold: "Notifications", text: "Configure alert channels (email, Slack, Discord, etc.)" },
+ { bold: "Settings", text: "Manage account and global settings" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "monitors-list",
+ level: 2,
+ text: "Monitors list",
+ },
+ {
+ type: "paragraph",
+ text: "The main monitors view shows all your monitors with key information at a glance:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Status indicator", text: "Green (up), red (down), or yellow (degraded)" },
+ { bold: "Name", text: "The monitor's display name" },
+ { bold: "URL/Host", text: "The target being monitored" },
+ { bold: "Response time", text: "Latest response time in milliseconds" },
+ { bold: "Uptime", text: "Percentage uptime over the selected period" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "filtering-sorting",
+ level: 2,
+ text: "Filtering and sorting",
+ },
+ {
+ type: "paragraph",
+ text: "You can filter monitors by status (up, down, paused) and sort by name, status, or response time. Use the search box to quickly find specific monitors.",
+ },
+ {
+ type: "article-links",
+ title: "Related articles",
+ items: [
+ {
+ collectionId: "uptime-monitoring",
+ articleId: "uptime-metrics",
+ title: "Understanding uptime metrics",
+ description: "Learn what the numbers mean",
+ },
+ {
+ collectionId: "incidents",
+ articleId: "understanding-incidents",
+ title: "Understanding incidents",
+ description: "How downtime is tracked",
+ },
+ ],
+ },
+ ],
+ },
+
+ "getting-started/roles-permissions": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Checkmate uses a role-based access control system with three user roles: User, Admin, and Superadmin. Each role has different permissions for managing monitors, team members, and settings.",
+ },
+ {
+ type: "heading",
+ id: "user-role",
+ level: 2,
+ text: "User role",
+ },
+ {
+ type: "paragraph",
+ text: "Users have standard access to monitoring features:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { text: "View all monitors and their status" },
+ { text: "View incidents and history" },
+ { text: "Access status pages" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "admin-role",
+ level: 2,
+ text: "Admin role",
+ },
+ {
+ type: "paragraph",
+ text: "Admins have all User permissions plus management capabilities:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { text: "Create, edit, and delete monitors" },
+ { text: "Invite new team members" },
+ { text: "Remove team members" },
+ { text: "Change user roles (except Superadmin)" },
+ { text: "Configure notification channels" },
+ { text: "Manage status pages and maintenance windows" },
+ { text: "Access system diagnostics and logs" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "superadmin-role",
+ level: 2,
+ text: "Superadmin role",
+ },
+ {
+ type: "paragraph",
+ text: "Superadmins have full system access:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { text: "All Admin permissions" },
+ { text: "Configure global application settings" },
+ { text: "Set up SMTP email configuration" },
+ { text: "Manage user accounts and passwords" },
+ { text: "Access all administrative functions" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "The first user to register becomes the Superadmin. Only existing Superadmins can promote other users to Superadmin.",
+ },
+ {
+ type: "article-links",
+ title: "Related articles",
+ items: [
+ {
+ collectionId: "team-management",
+ articleId: "inviting-members",
+ title: "Inviting team members",
+ description: "Add people to your team",
+ },
+ {
+ collectionId: "team-management",
+ articleId: "user-roles",
+ title: "User roles",
+ description: "Detailed role comparison",
+ },
+ ],
+ },
+ ],
+ },
+
+ // ============================================
+ // UPTIME MONITORING COLLECTION
+ // ============================================
+
+ "uptime-monitoring/http-monitors": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "HTTP monitors check website and API availability by making HTTP GET requests and validating responses. They verify that the server returns a successful response (status 200-299).",
+ },
+ {
+ type: "heading",
+ id: "creating-http-monitor",
+ level: 2,
+ text: "Creating an HTTP monitor",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Uptime** in the sidebar" },
+ { text: "Click **Create monitor**" },
+ { text: "Select **HTTP/HTTPS** as the monitor type" },
+ { text: "Enter the target URL (include `https://` or `http://`)" },
+ { text: "Configure the settings below" },
+ { text: "Click **Create monitor**" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "configuration-options",
+ level: 2,
+ text: "Configuration options",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Name", text: "A descriptive name for the monitor (max 50 characters)" },
+ { bold: "URL", text: "The full URL to monitor (must include protocol)" },
+ { bold: "Check interval", text: "How often to check (15 seconds to 30 minutes)" },
+ { bold: "Ignore TLS errors", text: "Skip SSL certificate validation if needed" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "advanced-options",
+ level: 3,
+ text: "Response validation",
+ },
+ {
+ type: "paragraph",
+ text: "You can validate API response content using these options:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Match method", text: "How to compare: equal, include, or regex" },
+ { bold: "Expected value", text: "The value to match against" },
+ { bold: "JSON path", text: "JMESPath expression to extract data from JSON responses" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "HTTP monitors check for successful responses (status 200-299). The monitor uses GET requests by default.",
+ },
+ {
+ type: "article-links",
+ title: "Related articles",
+ items: [
+ {
+ collectionId: "uptime-monitoring",
+ articleId: "json-path-matching",
+ title: "JSON path matching",
+ description: "Validate API response content",
+ },
+ {
+ collectionId: "uptime-monitoring",
+ articleId: "ssl-tls-monitoring",
+ title: "SSL/TLS monitoring",
+ description: "Track certificate expiration",
+ },
+ ],
+ },
+ ],
+ },
+
+ "uptime-monitoring/ping-monitors": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Ping monitors verify server availability using ICMP echo requests. They're useful for monitoring servers, network devices, and infrastructure that doesn't expose HTTP endpoints.",
+ },
+ {
+ type: "heading",
+ id: "creating-ping-monitor",
+ level: 2,
+ text: "Creating a ping monitor",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Uptime** in the sidebar" },
+ { text: "Click **Create monitor**" },
+ { text: "Select **Ping** as the monitor type" },
+ { text: "Enter the hostname or IP address" },
+ { text: "Set the check interval" },
+ { text: "Click **Create monitor**" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "configuration",
+ level: 2,
+ text: "Configuration options",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Host", text: "IP address or hostname to ping" },
+ { bold: "Check interval", text: "How often to send ping requests" },
+ { bold: "Timeout", text: "Maximum time to wait for a response" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "Some cloud providers and firewalls block ICMP traffic. If ping monitors fail but the server is accessible, consider using HTTP or TCP port monitors instead.",
+ },
+ {
+ type: "article-links",
+ title: "Related articles",
+ items: [
+ {
+ collectionId: "uptime-monitoring",
+ articleId: "port-monitors",
+ title: "Port monitors",
+ description: "Monitor specific TCP ports",
+ },
+ ],
+ },
+ ],
+ },
+
+ "uptime-monitoring/port-monitors": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Port monitors check TCP port availability on servers. Use them to monitor databases, mail servers, game servers, and other services that listen on specific ports.",
+ },
+ {
+ type: "heading",
+ id: "creating-port-monitor",
+ level: 2,
+ text: "Creating a port monitor",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Uptime** in the sidebar" },
+ { text: "Click **Create monitor**" },
+ { text: "Select **TCP port** as the monitor type" },
+ { text: "Enter the hostname or IP address" },
+ { text: "Enter the port number" },
+ { text: "Set the check interval" },
+ { text: "Click **Create monitor**" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "common-ports",
+ level: 2,
+ text: "Common ports",
+ },
+ {
+ type: "table",
+ columns: [
+ { key: "port", label: "Port", width: "1fr" },
+ { key: "service", label: "Service", width: "2fr" },
+ ],
+ rows: [
+ { port: "22", service: "SSH" },
+ { port: "25", service: "SMTP (email)" },
+ { port: "443", service: "HTTPS" },
+ { port: "3306", service: "MySQL" },
+ { port: "5432", service: "PostgreSQL" },
+ { port: "6379", service: "Redis" },
+ { port: "27017", service: "MongoDB" },
+ ],
+ },
+ {
+ type: "article-links",
+ title: "Related articles",
+ items: [
+ {
+ collectionId: "uptime-monitoring",
+ articleId: "http-monitors",
+ title: "HTTP monitors",
+ description: "For web services and APIs",
+ },
+ ],
+ },
+ ],
+ },
+
+ "uptime-monitoring/intervals-timing": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "The check interval determines how often Checkmate tests your monitors. Choosing the right interval balances quick detection with resource usage.",
+ },
+ {
+ type: "heading",
+ id: "available-intervals",
+ level: 2,
+ text: "Available intervals",
+ },
+ {
+ type: "paragraph",
+ text: "Available intervals vary by monitor type:",
+ },
+ {
+ type: "heading",
+ id: "uptime-intervals",
+ level: 3,
+ text: "Uptime monitors",
+ },
+ {
+ type: "table",
+ columns: [
+ { key: "interval", label: "Interval", width: "1fr" },
+ { key: "use", label: "Best for", width: "2fr" },
+ ],
+ rows: [
+ { interval: "15 seconds", use: "Ultra-critical services" },
+ { interval: "30 seconds", use: "Critical production services" },
+ { interval: "1-5 minutes", use: "Standard production monitoring" },
+ { interval: "10-15 minutes", use: "Less critical services" },
+ { interval: "30 minutes", use: "Development/staging environments" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "pagespeed-intervals",
+ level: 3,
+ text: "PageSpeed monitors",
+ },
+ {
+ type: "paragraph",
+ text: "PageSpeed checks use longer intervals (3 minutes to 1 week) since Lighthouse audits are resource-intensive.",
+ },
+ {
+ type: "heading",
+ id: "choosing-interval",
+ level: 2,
+ text: "Choosing an interval",
+ },
+ {
+ type: "paragraph",
+ text: "Consider these factors when selecting an interval:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Service criticality", text: "Mission-critical services need shorter intervals" },
+ { bold: "Expected downtime impact", text: "High-traffic sites benefit from faster detection" },
+ { bold: "Resource usage", text: "Shorter intervals use more system resources" },
+ { bold: "False positive tolerance", text: "Longer intervals reduce noise from brief glitches" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "tip",
+ text: "Start with 3-minute intervals for most monitors. Decrease to 1 minute only for your most critical services.",
+ },
+ ],
+ },
+
+ "uptime-monitoring/json-path-matching": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "JSON path matching allows you to validate API response content beyond just status codes. You can verify that specific fields contain expected values.",
+ },
+ {
+ type: "heading",
+ id: "enabling-json-matching",
+ level: 2,
+ text: "Enabling JSON matching",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Create or edit an HTTP monitor" },
+ { text: "In advanced settings, enable **Response body validation**" },
+ { text: "Enter the JSON path expression" },
+ { text: "Enter the expected value" },
+ { text: "Save the monitor" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "json-path-syntax",
+ level: 2,
+ text: "JSON path syntax",
+ },
+ {
+ type: "paragraph",
+ text: "JSON path uses dot notation to navigate JSON structures:",
+ },
+ {
+ type: "code",
+ language: "json",
+ text: '// Example API response\n{\n "status": "healthy",\n "data": {\n "version": "2.1.0",\n "services": ["api", "database"]\n }\n}',
+ },
+ {
+ type: "table",
+ columns: [
+ { key: "path", label: "JSON path", width: "1fr" },
+ { key: "result", label: "Returns", width: "1fr" },
+ ],
+ rows: [
+ { path: "status", result: '"healthy"' },
+ { path: "data.version", result: '"2.1.0"' },
+ { path: "data.services[0]", result: '"api"' },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "warning",
+ text: "JSON path matching only works with APIs that return valid JSON. Ensure the response Content-Type is application/json.",
+ },
+ ],
+ },
+
+ "uptime-monitoring/ssl-tls-monitoring": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Checkmate automatically monitors SSL/TLS certificates for HTTPS monitors. You can track certificate expiration and receive alerts before certificates expire.",
+ },
+ {
+ type: "heading",
+ id: "certificate-tracking",
+ level: 2,
+ text: "Certificate tracking",
+ },
+ {
+ type: "paragraph",
+ text: "For every HTTPS monitor, Checkmate tracks:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Issuer", text: "The certificate authority that issued the certificate" },
+ { bold: "Valid from", text: "When the certificate became valid" },
+ { bold: "Expiration date", text: "When the certificate expires" },
+ { bold: "Days until expiry", text: "Countdown to expiration" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "expiration-alerts",
+ level: 2,
+ text: "Expiration alerts",
+ },
+ {
+ type: "paragraph",
+ text: "Checkmate sends notifications when certificates are approaching expiration. You can configure the warning threshold in monitor settings.",
+ },
+ {
+ type: "callout",
+ variant: "tip",
+ text: "Set up certificate expiration alerts at least 14 days before expiry to give yourself time to renew.",
+ },
+ {
+ type: "heading",
+ id: "certificate-errors",
+ level: 2,
+ text: "Certificate errors",
+ },
+ {
+ type: "paragraph",
+ text: "Checkmate detects common SSL/TLS issues:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { text: "Expired certificates" },
+ { text: "Self-signed certificates (unless explicitly allowed)" },
+ { text: "Certificate hostname mismatch" },
+ { text: "Incomplete certificate chains" },
+ ],
+ },
+ ],
+ },
+
+ "uptime-monitoring/uptime-metrics": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Checkmate tracks several metrics for each monitor to help you understand availability and performance. This article explains what each metric means.",
+ },
+ {
+ type: "heading",
+ id: "uptime-percentage",
+ level: 2,
+ text: "Uptime percentage",
+ },
+ {
+ type: "paragraph",
+ text: "Uptime percentage shows how often your service was available over a time period. It's calculated as:",
+ },
+ {
+ type: "code",
+ language: "text",
+ text: "Uptime % = (Successful checks / Total checks) × 100",
+ },
+ {
+ type: "table",
+ columns: [
+ { key: "uptime", label: "Uptime", width: "1fr" },
+ { key: "downtime", label: "Monthly downtime", width: "1fr" },
+ ],
+ rows: [
+ { uptime: "99.9%", downtime: "~43 minutes" },
+ { uptime: "99.5%", downtime: "~3.6 hours" },
+ { uptime: "99.0%", downtime: "~7.2 hours" },
+ { uptime: "95.0%", downtime: "~36 hours" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "response-time",
+ level: 2,
+ text: "Response time",
+ },
+ {
+ type: "paragraph",
+ text: "Response time measures how long it takes to receive a response from your service. Checkmate shows:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Current", text: "The most recent response time" },
+ { bold: "Average", text: "Mean response time over the period" },
+ { bold: "P95", text: "95th percentile (95% of requests were faster)" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "status-codes",
+ level: 2,
+ text: "Status codes",
+ },
+ {
+ type: "paragraph",
+ text: "For HTTP monitors, Checkmate tracks response status codes. A check is considered successful if the status code matches your expected codes (default: 200).",
+ },
+ {
+ type: "article-links",
+ title: "Related articles",
+ items: [
+ {
+ collectionId: "incidents",
+ articleId: "understanding-incidents",
+ title: "Understanding incidents",
+ description: "How downtime creates incidents",
+ },
+ ],
+ },
+ ],
+ },
+
+ // ============================================
+ // PAGESPEED MONITORING COLLECTION
+ // ============================================
+
+ "pagespeed/creating-pagespeed": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "PageSpeed monitors track website performance using Google Lighthouse. They measure loading speed, accessibility, SEO, and best practices to help you optimize your sites.",
+ },
+ {
+ type: "heading",
+ id: "creating-monitor",
+ level: 2,
+ text: "Creating a PageSpeed monitor",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **PageSpeed** in the sidebar" },
+ { text: "Click **Create monitor**" },
+ { text: "Enter the URL to monitor" },
+ { text: "Set the check frequency (daily recommended)" },
+ { text: "Click **Create monitor**" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "configuration",
+ level: 2,
+ text: "Configuration options",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "URL", text: "The page to analyze (must be publicly accessible)" },
+ { bold: "Display name", text: "Optional friendly name for the monitor" },
+ { bold: "Check frequency", text: "How often to run audits (3 min to 1 week)" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "PageSpeed checks use Google's PageSpeed Insights API. Configure an API key in Settings → PageSpeed for higher rate limits.",
+ },
+ {
+ type: "article-links",
+ title: "Related articles",
+ items: [
+ {
+ collectionId: "pagespeed",
+ articleId: "understanding-scores",
+ title: "Understanding scores",
+ description: "What each score means",
+ },
+ {
+ collectionId: "pagespeed",
+ articleId: "pagespeed-api",
+ title: "PageSpeed API configuration",
+ description: "Set up your API key",
+ },
+ ],
+ },
+ ],
+ },
+
+ "pagespeed/understanding-scores": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "PageSpeed monitors report four main scores from Google Lighthouse audits. Each score ranges from 0-100, with higher scores indicating better performance.",
+ },
+ {
+ type: "heading",
+ id: "score-categories",
+ level: 2,
+ text: "Score categories",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Performance", text: "Measures loading speed and interactivity" },
+ { bold: "Accessibility", text: "Evaluates usability for people with disabilities" },
+ { bold: "Best practices", text: "Checks for modern web development standards" },
+ { bold: "SEO", text: "Assesses search engine optimization basics" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "score-ranges",
+ level: 2,
+ text: "Score ranges",
+ },
+ {
+ type: "table",
+ columns: [
+ { key: "range", label: "Score", width: "1fr" },
+ { key: "rating", label: "Rating", width: "1fr" },
+ { key: "color", label: "Indicator", width: "1fr" },
+ ],
+ rows: [
+ { range: "90-100", rating: "Good", color: "Green" },
+ { range: "50-89", rating: "Needs improvement", color: "Orange" },
+ { range: "0-49", rating: "Poor", color: "Red" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "tip",
+ text: "Focus on Performance first, as it directly impacts user experience and SEO rankings.",
+ },
+ ],
+ },
+
+ "pagespeed/core-web-vitals": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Core Web Vitals are Google's key metrics for measuring user experience. Checkmate tracks these metrics to help you understand and improve page performance.",
+ },
+ {
+ type: "heading",
+ id: "lcp",
+ level: 2,
+ text: "Largest Contentful Paint (LCP)",
+ },
+ {
+ type: "paragraph",
+ text: "LCP measures how long it takes for the largest visible element to load. Target: under 2.5 seconds.",
+ },
+ {
+ type: "heading",
+ id: "fcp",
+ level: 2,
+ text: "First Contentful Paint (FCP)",
+ },
+ {
+ type: "paragraph",
+ text: "FCP measures when the first content appears on screen. Target: under 1.8 seconds.",
+ },
+ {
+ type: "heading",
+ id: "cls",
+ level: 2,
+ text: "Cumulative Layout Shift (CLS)",
+ },
+ {
+ type: "paragraph",
+ text: "CLS measures visual stability—how much the page layout shifts during loading. Target: under 0.1.",
+ },
+ {
+ type: "heading",
+ id: "tbt",
+ level: 2,
+ text: "Total Blocking Time (TBT)",
+ },
+ {
+ type: "paragraph",
+ text: "TBT measures how long the page is unresponsive to user input. Target: under 200 milliseconds.",
+ },
+ {
+ type: "heading",
+ id: "speed-index",
+ level: 2,
+ text: "Speed Index",
+ },
+ {
+ type: "paragraph",
+ text: "Speed Index measures how quickly content is visually displayed. Target: under 3.4 seconds.",
+ },
+ ],
+ },
+
+ "pagespeed/pagespeed-api": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Checkmate uses the Google PageSpeed Insights API to run Lighthouse audits. While the API works without a key, adding one increases your rate limits.",
+ },
+ {
+ type: "heading",
+ id: "getting-api-key",
+ level: 2,
+ text: "Getting an API key",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Go to the Google Cloud Console" },
+ { text: "Create a new project or select an existing one" },
+ { text: "Enable the PageSpeed Insights API" },
+ { text: "Create credentials (API key)" },
+ { text: "Copy the generated API key" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "configuring-key",
+ level: 2,
+ text: "Configuring the API key",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Settings** in the sidebar" },
+ { text: "Scroll to the **PageSpeed** section" },
+ { text: "Enter your API key in the **PageSpeed API key** field" },
+ { text: "Click **Save** at the bottom of the page" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "warning",
+ text: "Only Admins and Superadmins can configure the PageSpeed API key.",
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "Without an API key, you're limited to a few requests per minute. With a key, you can make thousands of requests per day.",
+ },
+ ],
+ },
+
+ "pagespeed/performance-trends": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Performance trends show how your PageSpeed scores change over time. Use them to track the impact of optimizations and catch regressions.",
+ },
+ {
+ type: "heading",
+ id: "viewing-trends",
+ level: 2,
+ text: "Viewing trends",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **PageSpeed** in the sidebar" },
+ { text: "Click on a monitor to view details" },
+ { text: "Select the time range (7 days, 30 days, etc.)" },
+ { text: "View the trend charts for each metric" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "interpreting-trends",
+ level: 2,
+ text: "Interpreting trends",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Upward trends", text: "Scores improving—your optimizations are working" },
+ { bold: "Downward trends", text: "Scores declining—investigate recent changes" },
+ { bold: "Flat trends", text: "Stable performance—no significant changes" },
+ { bold: "Spikes", text: "Temporary issues—check for one-time events" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "tip",
+ text: "Compare trends before and after deployments to measure the impact of code changes on performance.",
+ },
+ ],
+ },
+
+ // ============================================
+ // INFRASTRUCTURE MONITORING COLLECTION
+ // ============================================
+
+ "infrastructure/installing-capture": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Capture is the companion agent for infrastructure monitoring. Install it on your servers to collect CPU, memory, disk, and network metrics.",
+ },
+ {
+ type: "heading",
+ id: "requirements",
+ level: 2,
+ text: "Requirements",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { text: "Linux, macOS, or Windows server" },
+ { text: "Network access to your Checkmate instance" },
+ { text: "Minimal resources (< 50MB RAM, < 1% CPU)" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "installation",
+ level: 2,
+ text: "Installation",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Infrastructure** in the sidebar" },
+ { text: "Click **Add server**" },
+ { text: "Copy the installation command" },
+ { text: "Run the command on your server" },
+ { text: "The server appears automatically once connected" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "Capture is open source. View the source code at github.com/bluewave-labs/capture",
+ },
+ {
+ type: "article-links",
+ title: "Related articles",
+ items: [
+ {
+ collectionId: "infrastructure",
+ articleId: "cpu-monitoring",
+ title: "CPU monitoring",
+ description: "Track processor usage",
+ },
+ {
+ collectionId: "infrastructure",
+ articleId: "hardware-thresholds",
+ title: "Setting hardware thresholds",
+ description: "Configure alerts",
+ },
+ ],
+ },
+ ],
+ },
+
+ "infrastructure/cpu-monitoring": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "CPU monitoring tracks processor usage across your servers. High CPU usage can indicate performance issues, runaway processes, or capacity problems.",
+ },
+ {
+ type: "heading",
+ id: "metrics",
+ level: 2,
+ text: "CPU metrics",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Usage percentage", text: "Current CPU utilization (0-100%)" },
+ { bold: "Load average", text: "Average system load over 1, 5, and 15 minutes" },
+ { bold: "Core count", text: "Number of CPU cores available" },
+ { bold: "Temperature", text: "CPU temperature (where supported)" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "typical-values",
+ level: 2,
+ text: "Typical values",
+ },
+ {
+ type: "table",
+ columns: [
+ { key: "usage", label: "Usage", width: "1fr" },
+ { key: "status", label: "Status", width: "2fr" },
+ ],
+ rows: [
+ { usage: "0-30%", status: "Normal—plenty of headroom" },
+ { usage: "30-70%", status: "Moderate—acceptable for production" },
+ { usage: "70-90%", status: "High—consider scaling" },
+ { usage: "90-100%", status: "Critical—immediate attention needed" },
+ ],
+ },
+ ],
+ },
+
+ "infrastructure/memory-monitoring": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Memory monitoring tracks RAM usage on your servers. Running out of memory can cause crashes, slowdowns, and service failures.",
+ },
+ {
+ type: "heading",
+ id: "metrics",
+ level: 2,
+ text: "Memory metrics",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Total memory", text: "Total RAM installed" },
+ { bold: "Used memory", text: "RAM currently in use" },
+ { bold: "Available memory", text: "RAM available for new processes" },
+ { bold: "Usage percentage", text: "Percentage of RAM in use" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "Linux systems use available RAM for disk caching. High memory usage isn't always a problem if available memory remains adequate.",
+ },
+ ],
+ },
+
+ "infrastructure/disk-monitoring": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Disk monitoring tracks storage space and I/O performance. Running out of disk space can cause application failures and data loss.",
+ },
+ {
+ type: "heading",
+ id: "metrics",
+ level: 2,
+ text: "Disk metrics",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Total space", text: "Total disk capacity" },
+ { bold: "Used space", text: "Space currently in use" },
+ { bold: "Available space", text: "Free space remaining" },
+ { bold: "Usage percentage", text: "Percentage of disk used" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "warning",
+ text: "Set alerts at 80% disk usage to give yourself time to add storage or clean up files before running out of space.",
+ },
+ ],
+ },
+
+ "infrastructure/network-monitoring": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Network monitoring tracks bandwidth usage and packet statistics. Use it to identify bottlenecks and unusual traffic patterns.",
+ },
+ {
+ type: "heading",
+ id: "metrics",
+ level: 2,
+ text: "Network metrics",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Bytes received", text: "Incoming data volume" },
+ { bold: "Bytes sent", text: "Outgoing data volume" },
+ { bold: "Packets received", text: "Incoming packet count" },
+ { bold: "Packets sent", text: "Outgoing packet count" },
+ ],
+ },
+ ],
+ },
+
+ "infrastructure/hardware-thresholds": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Hardware thresholds define when Checkmate sends alerts about resource usage. Set thresholds to catch problems before they affect your services.",
+ },
+ {
+ type: "heading",
+ id: "configuring-thresholds",
+ level: 2,
+ text: "Configuring thresholds",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Infrastructure** in the sidebar" },
+ { text: "Click on a server to view details" },
+ { text: "Click **Settings** or the gear icon" },
+ { text: "Set threshold percentages for CPU, memory, and disk" },
+ { text: "Click **Save**" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "recommended-thresholds",
+ level: 2,
+ text: "Recommended thresholds",
+ },
+ {
+ type: "table",
+ columns: [
+ { key: "resource", label: "Resource", width: "1fr" },
+ { key: "warning", label: "Warning", width: "1fr" },
+ { key: "critical", label: "Critical", width: "1fr" },
+ ],
+ rows: [
+ { resource: "CPU", warning: "70%", critical: "90%" },
+ { resource: "Memory", warning: "80%", critical: "95%" },
+ { resource: "Disk", warning: "80%", critical: "90%" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "tip",
+ text: "Start with these defaults and adjust based on your application's normal behavior.",
+ },
+ ],
+ },
+
+ // ============================================
+ // INCIDENTS COLLECTION
+ // ============================================
+
+ "incidents/understanding-incidents": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "An incident is created when a monitor detects a failure. Checkmate tracks incident start time, duration, and resolution to help you analyze downtime patterns.",
+ },
+ {
+ type: "heading",
+ id: "incident-creation",
+ level: 2,
+ text: "When incidents are created",
+ },
+ {
+ type: "paragraph",
+ text: "Incidents are created when:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { text: "An HTTP monitor receives an unexpected status code" },
+ { text: "A ping or port monitor fails to connect" },
+ { text: "A monitor times out without response" },
+ { text: "Infrastructure thresholds are exceeded" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "incident-states",
+ level: 2,
+ text: "Incident states",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Active", text: "The monitor is currently down" },
+ { bold: "Resolved", text: "The monitor has recovered" },
+ ],
+ },
+ {
+ type: "article-links",
+ title: "Related articles",
+ items: [
+ {
+ collectionId: "incidents",
+ articleId: "incident-timeline",
+ title: "Incident timeline",
+ description: "Track incident progression",
+ },
+ {
+ collectionId: "incidents",
+ articleId: "resolving-incidents",
+ title: "Resolving incidents",
+ description: "Manual vs automatic resolution",
+ },
+ ],
+ },
+ ],
+ },
+
+ "incidents/incident-timeline": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "The incident timeline shows the progression of an incident from detection to resolution. Use it to understand what happened and when.",
+ },
+ {
+ type: "heading",
+ id: "timeline-events",
+ level: 2,
+ text: "Timeline events",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Started", text: "When the first failure was detected" },
+ { bold: "Notifications sent", text: "When alerts were dispatched" },
+ { bold: "Acknowledged", text: "When someone began investigating (if applicable)" },
+ { bold: "Resolved", text: "When the monitor recovered" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "duration",
+ level: 2,
+ text: "Duration tracking",
+ },
+ {
+ type: "paragraph",
+ text: "Checkmate calculates incident duration from the first failed check to the first successful check after the incident. This appears in both the timeline and incident summary.",
+ },
+ ],
+ },
+
+ "incidents/resolving-incidents": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Incidents can be resolved automatically when the monitor recovers, or manually if you want to mark an issue as fixed.",
+ },
+ {
+ type: "heading",
+ id: "automatic-resolution",
+ level: 2,
+ text: "Automatic resolution",
+ },
+ {
+ type: "paragraph",
+ text: "By default, incidents resolve automatically when the monitor returns to a healthy state. A recovery notification is sent to all configured channels.",
+ },
+ {
+ type: "heading",
+ id: "manual-resolution",
+ level: 2,
+ text: "Manual resolution",
+ },
+ {
+ type: "paragraph",
+ text: "You can also manually resolve incidents:",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Incidents** in the sidebar" },
+ { text: "Find the active incident" },
+ { text: "Click **Resolve**" },
+ { text: "Optionally add a resolution note" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "Manual resolution is useful when you know the underlying issue is fixed but the monitor hasn't run its next check yet.",
+ },
+ ],
+ },
+
+ "incidents/incident-history": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Incident history provides a record of all past outages. Use it to identify patterns, measure reliability, and report on SLA compliance.",
+ },
+ {
+ type: "heading",
+ id: "viewing-history",
+ level: 2,
+ text: "Viewing incident history",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Incidents** in the sidebar" },
+ { text: "Use filters to narrow by monitor, date range, or status" },
+ { text: "Click an incident to view its full timeline" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "analyzing-patterns",
+ level: 2,
+ text: "Analyzing patterns",
+ },
+ {
+ type: "paragraph",
+ text: "Look for these patterns in your incident history:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Recurring times", text: "Incidents at the same time might indicate scheduled jobs or traffic spikes" },
+ { bold: "Duration trends", text: "Longer incidents may indicate slower recovery processes" },
+ { bold: "Frequency changes", text: "Increasing incidents could signal infrastructure problems" },
+ ],
+ },
+ ],
+ },
+
+ // ============================================
+ // NOTIFICATIONS COLLECTION
+ // ============================================
+
+ "notifications/email-notifications": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Email notifications send alerts directly to your inbox when monitors detect issues. Configure SMTP settings to enable email delivery.",
+ },
+ {
+ type: "heading",
+ id: "smtp-setup",
+ level: 2,
+ text: "Setting up SMTP",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Settings** in the sidebar" },
+ { text: "Select the **Email** tab" },
+ { text: "Enter your SMTP server details" },
+ { text: "Configure authentication (username/password)" },
+ { text: "Set the sender email address" },
+ { text: "Click **Save** and **Test** to verify" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "smtp-settings",
+ level: 2,
+ text: "SMTP settings",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Host", text: "SMTP server address (e.g., smtp.gmail.com)" },
+ { bold: "Port", text: "SMTP port (typically 587 for TLS, 465 for SSL)" },
+ { bold: "Username", text: "Authentication username" },
+ { bold: "Password", text: "Authentication password or app password" },
+ { bold: "From address", text: "Email address that alerts come from" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "tip",
+ text: "For Gmail, use an App Password instead of your regular password. Enable 2FA and generate an app password in your Google Account settings.",
+ },
+ ],
+ },
+
+ "notifications/slack-integration": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Slack integration sends incident alerts to your Slack channels using webhooks. Your team can see and discuss issues in real-time.",
+ },
+ {
+ type: "heading",
+ id: "creating-webhook",
+ level: 2,
+ text: "Creating a Slack webhook",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Go to api.slack.com/apps and create a new app" },
+ { text: "Select **Incoming Webhooks** and enable them" },
+ { text: "Click **Add New Webhook to Workspace**" },
+ { text: "Choose the channel for alerts" },
+ { text: "Copy the webhook URL" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "configuring-checkmate",
+ level: 2,
+ text: "Configuring in Checkmate",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Integrations** in the sidebar" },
+ { text: "Click **Create integration**" },
+ { text: "Select **Slack**" },
+ { text: "Paste your webhook URL" },
+ { text: "Click **Test** to send a test message" },
+ { text: "Click **Save**" },
+ ],
+ },
+ ],
+ },
+
+ "notifications/discord-integration": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Discord integration sends alerts to your Discord server using webhooks. Ideal for development teams already using Discord for communication.",
+ },
+ {
+ type: "heading",
+ id: "creating-webhook",
+ level: 2,
+ text: "Creating a Discord webhook",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Open Discord and go to your server" },
+ { text: "Click the gear icon next to the target channel" },
+ { text: "Select **Integrations** → **Webhooks**" },
+ { text: "Click **New Webhook**" },
+ { text: "Name the webhook and copy the URL" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "configuring-checkmate",
+ level: 2,
+ text: "Configuring in Checkmate",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Integrations** in the sidebar" },
+ { text: "Click **Create integration**" },
+ { text: "Select **Discord**" },
+ { text: "Paste your webhook URL" },
+ { text: "Click **Test** then **Save**" },
+ ],
+ },
+ ],
+ },
+
+ "notifications/pagerduty-integration": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "PagerDuty integration connects Checkmate to your on-call schedules. Incidents trigger PagerDuty alerts with proper escalation policies.",
+ },
+ {
+ type: "heading",
+ id: "setup",
+ level: 2,
+ text: "Setting up PagerDuty",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "In PagerDuty, go to **Services** and create or select a service" },
+ { text: "Add a new integration of type **Events API v2**" },
+ { text: "Copy the Integration Key (routing key)" },
+ { text: "In Checkmate, navigate to **Integrations**" },
+ { text: "Create a new **PagerDuty** integration" },
+ { text: "Paste the integration key and save" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "PagerDuty incidents are automatically resolved when Checkmate detects recovery.",
+ },
+ ],
+ },
+
+ "notifications/webhook-notifications": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Webhook notifications send HTTP POST requests to your custom endpoint when incidents occur. Use them to integrate with any service.",
+ },
+ {
+ type: "heading",
+ id: "setup",
+ level: 2,
+ text: "Setting up webhooks",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Integrations** in the sidebar" },
+ { text: "Click **Create integration**" },
+ { text: "Select **Webhook**" },
+ { text: "Enter your endpoint URL" },
+ { text: "Click **Test** then **Save**" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "payload",
+ level: 2,
+ text: "Webhook payload",
+ },
+ {
+ type: "paragraph",
+ text: "Checkmate sends a JSON payload with incident details:",
+ },
+ {
+ type: "code",
+ language: "json",
+ text: '{\n "type": "incident.created",\n "monitor": {\n "id": "abc123",\n "name": "Production API",\n "url": "https://api.example.com"\n },\n "incident": {\n "id": "inc_456",\n "status": "active",\n "startedAt": "2024-01-15T10:30:00Z"\n }\n}',
+ },
+ ],
+ },
+
+ "notifications/matrix-integration": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Matrix integration sends alerts to Matrix rooms using the Matrix protocol. Ideal for teams using decentralized messaging.",
+ },
+ {
+ type: "heading",
+ id: "setup",
+ level: 2,
+ text: "Setting up Matrix",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Get your Matrix homeserver URL" },
+ { text: "Create an access token for your bot account" },
+ { text: "Get the room ID where alerts should be sent" },
+ { text: "In Checkmate, navigate to **Integrations**" },
+ { text: "Create a new **Matrix** integration" },
+ { text: "Enter the homeserver, token, and room ID" },
+ { text: "Click **Test** then **Save**" },
+ ],
+ },
+ ],
+ },
+
+ "notifications/testing-notifications": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Always test your notification integrations to ensure alerts reach your team. Checkmate provides a test function for every integration type.",
+ },
+ {
+ type: "heading",
+ id: "testing",
+ level: 2,
+ text: "Testing an integration",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Integrations** in the sidebar" },
+ { text: "Find the integration you want to test" },
+ { text: "Click the **Test** button" },
+ { text: "Check the destination for the test message" },
+ { text: "Verify the message arrived correctly" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "troubleshooting",
+ level: 2,
+ text: "Troubleshooting",
+ },
+ {
+ type: "paragraph",
+ text: "If test messages don't arrive:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { text: "Verify the webhook URL or credentials are correct" },
+ { text: "Check if your firewall allows outbound connections" },
+ { text: "Ensure the destination service is operational" },
+ { text: "Review server logs for error messages" },
+ ],
+ },
+ ],
+ },
+
+ // ============================================
+ // STATUS PAGES COLLECTION
+ // ============================================
+
+ "status-pages/creating-status-page": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Status pages provide a public view of your service health. Share them with customers to communicate uptime and incidents transparently.",
+ },
+ {
+ type: "heading",
+ id: "creating",
+ level: 2,
+ text: "Creating a status page",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Status pages** in the sidebar" },
+ { text: "Click **Create status page**" },
+ { text: "Enter a name for your status page" },
+ { text: "Configure the URL slug (e.g., status.yourcompany.com)" },
+ { text: "Click **Create**" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "adding-monitors",
+ level: 2,
+ text: "Adding monitors",
+ },
+ {
+ type: "paragraph",
+ text: "After creation, add monitors to display on the status page. Visitors can see the status of each service without accessing your dashboard.",
+ },
+ {
+ type: "article-links",
+ title: "Related articles",
+ items: [
+ {
+ collectionId: "status-pages",
+ articleId: "customizing-appearance",
+ title: "Customizing appearance",
+ description: "Brand your status page",
+ },
+ {
+ collectionId: "status-pages",
+ articleId: "adding-monitors",
+ title: "Adding monitors",
+ description: "Configure which services to show",
+ },
+ ],
+ },
+ ],
+ },
+
+ "status-pages/customizing-appearance": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Customize your status page to match your brand. Add your logo, choose colors, and configure the layout.",
+ },
+ {
+ type: "heading",
+ id: "branding",
+ level: 2,
+ text: "Branding options",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Logo", text: "Upload your company logo (JPEG/PNG, max 3MB)" },
+ { bold: "Company name", text: "Display name at the top of the page" },
+ { bold: "Color", text: "Primary color for the status page theme" },
+ { bold: "Timezone", text: "Timezone for displaying check times" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "applying",
+ level: 2,
+ text: "Applying customizations",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Status pages** and select your page" },
+ { text: "Click **Settings** or the gear icon" },
+ { text: "Upload your logo and configure branding" },
+ { text: "Click **Save**" },
+ ],
+ },
+ ],
+ },
+
+ "status-pages/adding-monitors": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Choose which monitors appear on your status page. You can group related services and display them with custom names.",
+ },
+ {
+ type: "heading",
+ id: "adding",
+ level: 2,
+ text: "Adding monitors",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Status pages** and select your page" },
+ { text: "Click **Add monitor**" },
+ { text: "Select monitors from the list" },
+ { text: "Optionally set display names" },
+ { text: "Click **Save**" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "organizing",
+ level: 2,
+ text: "Organizing monitors",
+ },
+ {
+ type: "paragraph",
+ text: "Drag monitors to reorder them. Group related services together for a logical presentation.",
+ },
+ ],
+ },
+
+ "status-pages/display-options": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Configure what information visitors see on your status page. Balance transparency with the level of detail you want to share.",
+ },
+ {
+ type: "heading",
+ id: "options",
+ level: 2,
+ text: "Display options",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Show uptime percentage", text: "Display overall uptime statistics for each monitor" },
+ { bold: "Show charts", text: "Display response time performance graphs" },
+ { bold: "Show admin login link", text: "Add a link to the Checkmate login page" },
+ { bold: "Publish/Unpublish", text: "Control whether the page is publicly accessible" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "Unpublished status pages are only accessible by team members logged into Checkmate.",
+ },
+ ],
+ },
+
+ "status-pages/sharing": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Share your status page with customers and stakeholders to communicate service health transparently.",
+ },
+ {
+ type: "heading",
+ id: "public-url",
+ level: 2,
+ text: "Public URL",
+ },
+ {
+ type: "paragraph",
+ text: "Each status page has a unique URL based on the slug you configured. Share this URL with anyone who needs to check your service status.",
+ },
+ {
+ type: "callout",
+ variant: "tip",
+ text: "Use a descriptive URL slug like 'api-status' or 'app-health' to make the link memorable.",
+ },
+ ],
+ },
+
+ // ============================================
+ // MAINTENANCE COLLECTION
+ // ============================================
+
+ "maintenance/creating-maintenance": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Maintenance windows suppress alerts during planned downtime. Schedule them before deployments or infrastructure work to avoid false alarms.",
+ },
+ {
+ type: "heading",
+ id: "creating",
+ level: 2,
+ text: "Creating a maintenance window",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Maintenance** in the sidebar" },
+ { text: "Click **Create maintenance window**" },
+ { text: "Enter a name describing the maintenance" },
+ { text: "Select the affected monitors" },
+ { text: "Set the start and end times" },
+ { text: "Click **Create**" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "Monitors continue running during maintenance, but incidents won't trigger notifications.",
+ },
+ ],
+ },
+
+ "maintenance/recurring-maintenance": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Choose between one-time and recurring maintenance windows based on your needs.",
+ },
+ {
+ type: "heading",
+ id: "one-time",
+ level: 2,
+ text: "One-time maintenance",
+ },
+ {
+ type: "paragraph",
+ text: "Use one-time windows for specific events like deployments, migrations, or emergency repairs. They run once at the scheduled time.",
+ },
+ {
+ type: "heading",
+ id: "recurring",
+ level: 2,
+ text: "Recurring maintenance",
+ },
+ {
+ type: "paragraph",
+ text: "Use recurring windows for regular maintenance like nightly backups or weekly updates. Options include:",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Daily", text: "Runs every day at the specified time" },
+ { bold: "Weekly", text: "Runs every 7 days at the specified time" },
+ ],
+ },
+ ],
+ },
+
+ "maintenance/managing-maintenance": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Manage active maintenance windows to extend, cancel, or modify them as needed.",
+ },
+ {
+ type: "heading",
+ id: "managing",
+ level: 2,
+ text: "Managing windows",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Maintenance** in the sidebar" },
+ { text: "Find the active or scheduled window" },
+ { text: "Click to view details" },
+ { text: "Use **Edit** to modify or **Cancel** to remove" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "extending",
+ level: 2,
+ text: "Extending maintenance",
+ },
+ {
+ type: "paragraph",
+ text: "If maintenance takes longer than expected, edit the window to extend the end time. This prevents alerts from firing while work continues.",
+ },
+ ],
+ },
+
+ // ============================================
+ // TEAM MANAGEMENT COLLECTION
+ // ============================================
+
+ "team-management/inviting-members": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Invite team members to collaborate on monitoring. New members receive an email invitation to join your workspace.",
+ },
+ {
+ type: "heading",
+ id: "inviting",
+ level: 2,
+ text: "Sending invitations",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Click your profile icon and select **Account**" },
+ { text: "Go to the **Team** section" },
+ { text: "Click **Invite member**" },
+ { text: "Enter their email address" },
+ { text: "Select their role (User or Admin)" },
+ { text: "Click **Send invitation**" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "warning",
+ text: "Invitations expire after 1 hour. Send a new invitation if the link has expired.",
+ },
+ ],
+ },
+
+ "team-management/user-roles": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Checkmate has three user roles with different permission levels. Assign roles based on what each team member needs to do.",
+ },
+ {
+ type: "heading",
+ id: "comparison",
+ level: 2,
+ text: "Role comparison",
+ },
+ {
+ type: "table",
+ columns: [
+ { key: "permission", label: "Permission", width: "2fr" },
+ { key: "user", label: "User", width: "1fr" },
+ { key: "admin", label: "Admin", width: "1fr" },
+ { key: "superadmin", label: "Superadmin", width: "1fr" },
+ ],
+ rows: [
+ { permission: "View monitors", user: "✓", admin: "✓", superadmin: "✓" },
+ { permission: "Create/edit monitors", user: "—", admin: "✓", superadmin: "✓" },
+ { permission: "Manage notifications", user: "—", admin: "✓", superadmin: "✓" },
+ { permission: "Invite team members", user: "—", admin: "✓", superadmin: "✓" },
+ { permission: "Configure SMTP", user: "—", admin: "—", superadmin: "✓" },
+ { permission: "Manage user accounts", user: "—", admin: "—", superadmin: "✓" },
+ ],
+ },
+ ],
+ },
+
+ "team-management/managing-access": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Manage team member access by changing roles or removing members who no longer need access.",
+ },
+ {
+ type: "heading",
+ id: "changing-roles",
+ level: 2,
+ text: "Changing roles",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Click your profile icon and select **Account**" },
+ { text: "Go to the **Team** section" },
+ { text: "Find the team member" },
+ { text: "Click their current role to change it" },
+ { text: "Select the new role and confirm" },
+ ],
+ },
+ {
+ type: "heading",
+ id: "removing",
+ level: 2,
+ text: "Removing members",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Click your profile icon and select **Account**" },
+ { text: "Go to the **Team** section" },
+ { text: "Find the team member" },
+ { text: "Click the remove button" },
+ { text: "Confirm removal" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "warning",
+ text: "Removed members lose access immediately but their past actions remain in the audit log.",
+ },
+ ],
+ },
+
+ // ============================================
+ // SETTINGS COLLECTION
+ // ============================================
+
+ "settings/email-configuration": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Configure SMTP settings to enable email notifications. This is required for email alerts and team invitations.",
+ },
+ {
+ type: "heading",
+ id: "configuration",
+ level: 2,
+ text: "SMTP configuration",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Settings** in the sidebar" },
+ { text: "Scroll to the **Email** section" },
+ { text: "Enter SMTP server hostname and port" },
+ { text: "Enter authentication credentials (username and password)" },
+ { text: "Set the from email address" },
+ { text: "Configure TLS/SSL options as needed" },
+ { text: "Click **Send test email** to verify, then **Save**" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "Only Admins and Superadmins can configure SMTP settings.",
+ },
+ ],
+ },
+
+ "settings/global-thresholds": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Global thresholds set default alert levels for infrastructure monitors. These thresholds determine when alerts are triggered for resource usage.",
+ },
+ {
+ type: "heading",
+ id: "configuring",
+ level: 2,
+ text: "Configuring thresholds",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Settings** in the sidebar" },
+ { text: "Scroll to the **Global thresholds** section" },
+ { text: "Set threshold percentages for CPU, memory, disk, and temperature" },
+ { text: "Click **Save** at the bottom of the page" },
+ ],
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "CPU threshold", text: "Alert when CPU usage exceeds this percentage (1-100%)" },
+ { bold: "Memory threshold", text: "Alert when memory usage exceeds this percentage (1-100%)" },
+ { bold: "Disk threshold", text: "Alert when disk usage exceeds this percentage (1-100%)" },
+ { bold: "Temperature threshold", text: "Alert when temperature exceeds this value (1-150°C)" },
+ ],
+ },
+ ],
+ },
+
+ "settings/appearance": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Customize the appearance of Checkmate to match your preferences.",
+ },
+ {
+ type: "heading",
+ id: "theme",
+ level: 2,
+ text: "Theme settings",
+ },
+ {
+ type: "bullet-list",
+ items: [
+ { bold: "Light/Dark mode", text: "Toggle between light and dark themes" },
+ { bold: "Language", text: "Select your preferred language" },
+ { bold: "Show URLs", text: "Toggle whether to display monitor URLs in the interface" },
+ ],
+ },
+ {
+ type: "paragraph",
+ text: "These settings are saved to your browser and persist across sessions.",
+ },
+ ],
+ },
+
+ "settings/timezone-settings": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Timezone settings control how dates and times are displayed throughout the application. Set this to your local timezone for easier incident correlation.",
+ },
+ {
+ type: "heading",
+ id: "configuring",
+ level: 2,
+ text: "Setting your timezone",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Settings** in the sidebar" },
+ { text: "Find the **Timezone** section at the top" },
+ { text: "Search for and select your timezone" },
+ { text: "Click **Save** at the bottom of the page" },
+ ],
+ },
+ ],
+ },
+
+ "settings/export-monitors": {
+ blocks: [
+ {
+ type: "heading",
+ id: "overview",
+ level: 2,
+ text: "Overview",
+ },
+ {
+ type: "paragraph",
+ text: "Export your monitors to backup your configuration or migrate to another Checkmate instance.",
+ },
+ {
+ type: "heading",
+ id: "exporting",
+ level: 2,
+ text: "Exporting monitors",
+ },
+ {
+ type: "ordered-list",
+ items: [
+ { text: "Navigate to **Settings** in the sidebar" },
+ { text: "Scroll to the **Export monitors** section" },
+ { text: "Click **Export monitors to JSON**" },
+ { text: "Save the downloaded file" },
+ ],
+ },
+ {
+ type: "callout",
+ variant: "info",
+ text: "Only Admins and Superadmins can export monitors.",
+ },
+ ],
+ },
+};
+
+/**
+ * Get article content by collection and article ID
+ * @param {string} collectionId
+ * @param {string} articleId
+ * @returns {Object|undefined}
+ */
+export const getArticleContent = (collectionId, articleId) => {
+ const key = `${collectionId}/${articleId}`;
+ return articleContents[key];
+};
+
+export default articleContents;
diff --git a/client/src/Components/v1/UserGuide/content/userGuideConfig.js b/client/src/Components/v1/UserGuide/content/userGuideConfig.js
new file mode 100644
index 000000000..420dcd44e
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/content/userGuideConfig.js
@@ -0,0 +1,524 @@
+// Icon names as strings - resolved to actual components in UserGuideLanding.jsx
+// Available icons: Rocket, Globe, Gauge, Server, Bell, AlertTriangle, Settings, Wifi, Wrench, Users
+
+/**
+ * @typedef {Object} Article
+ * @property {string} id
+ * @property {string} title
+ * @property {string} description
+ * @property {string[]} keywords
+ */
+
+/**
+ * @typedef {Object} Collection
+ * @property {string} id
+ * @property {string} title
+ * @property {string} description
+ * @property {string} icon
+ * @property {number} articleCount
+ * @property {Article[]} articles
+ */
+
+/**
+ * @typedef {Object} FastFind
+ * @property {string} id
+ * @property {string} title
+ * @property {string} collectionId
+ * @property {string} articleId
+ */
+
+// Collections configuration for Checkmate
+export const collections = [
+ {
+ id: "getting-started",
+ title: "Getting started",
+ description: "Essential first steps for new users.",
+ icon: "Rocket",
+ articleCount: 4,
+ articles: [
+ {
+ id: "what-is-checkmate",
+ title: "What is Checkmate",
+ description: "Overview of monitoring capabilities and core features.",
+ keywords: ["introduction", "overview", "platform", "monitoring", "uptime"],
+ },
+ {
+ id: "quick-start",
+ title: "Quick start guide",
+ description: "Create your first monitor in 5 minutes.",
+ keywords: ["quick", "start", "first", "monitor", "setup", "tutorial"],
+ },
+ {
+ id: "dashboard",
+ title: "Understanding the dashboard",
+ description: "Navigation and key metrics explained.",
+ keywords: ["dashboard", "navigation", "interface", "home", "metrics"],
+ },
+ {
+ id: "roles-permissions",
+ title: "User roles and permissions",
+ description: "User, Admin, and Superadmin roles explained.",
+ keywords: ["role", "permission", "admin", "superadmin", "user", "access"],
+ },
+ ],
+ },
+ {
+ id: "uptime-monitoring",
+ title: "Uptime monitoring",
+ description: "Monitor website and service availability.",
+ icon: "Globe",
+ articleCount: 7,
+ articles: [
+ {
+ id: "http-monitors",
+ title: "Creating HTTP monitors",
+ description: "URL monitoring with response validation.",
+ keywords: ["http", "https", "url", "website", "monitor", "create"],
+ },
+ {
+ id: "ping-monitors",
+ title: "Ping monitors",
+ description: "ICMP-based availability checks.",
+ keywords: ["ping", "icmp", "availability", "monitor"],
+ },
+ {
+ id: "port-monitors",
+ title: "Port monitors",
+ description: "TCP port availability monitoring.",
+ keywords: ["port", "tcp", "availability", "monitor", "service"],
+ },
+ {
+ id: "intervals-timing",
+ title: "Monitor intervals and timing",
+ description: "Configure how often to check your services.",
+ keywords: ["interval", "timing", "frequency", "check", "schedule"],
+ },
+ {
+ id: "json-path-matching",
+ title: "JSON path matching",
+ description: "Validate API response content.",
+ keywords: ["json", "api", "response", "validation", "match", "path"],
+ },
+ {
+ id: "ssl-tls-monitoring",
+ title: "SSL/TLS monitoring",
+ description: "Certificate tracking and error handling.",
+ keywords: ["ssl", "tls", "certificate", "https", "security", "expiry"],
+ },
+ {
+ id: "uptime-metrics",
+ title: "Understanding uptime metrics",
+ description: "Response time, status codes, and uptime percentage.",
+ keywords: ["metrics", "response", "time", "status", "code", "uptime", "percentage"],
+ },
+ ],
+ },
+ {
+ id: "pagespeed",
+ title: "PageSpeed monitoring",
+ description: "Track website performance and Core Web Vitals.",
+ icon: "Gauge",
+ articleCount: 5,
+ articles: [
+ {
+ id: "creating-pagespeed",
+ title: "Creating PageSpeed monitors",
+ description: "Setup and configuration for performance monitoring.",
+ keywords: ["pagespeed", "create", "setup", "performance", "lighthouse"],
+ },
+ {
+ id: "understanding-scores",
+ title: "Understanding scores",
+ description: "Performance, Accessibility, SEO, and Best Practices explained.",
+ keywords: ["score", "performance", "accessibility", "seo", "best", "practices"],
+ },
+ {
+ id: "core-web-vitals",
+ title: "Core Web Vitals explained",
+ description: "LCP, FCP, CLS, TBT, and Speed Index.",
+ keywords: ["core", "web", "vitals", "lcp", "fcp", "cls", "tbt", "speed", "index"],
+ },
+ {
+ id: "pagespeed-api",
+ title: "PageSpeed API configuration",
+ description: "Setting up your Google PageSpeed API key.",
+ keywords: ["api", "key", "google", "pagespeed", "configure", "setup"],
+ },
+ {
+ id: "performance-trends",
+ title: "Performance trends",
+ description: "Tracking improvements over time.",
+ keywords: ["trend", "history", "improvement", "tracking", "chart"],
+ },
+ ],
+ },
+ {
+ id: "infrastructure",
+ title: "Infrastructure monitoring",
+ description: "Monitor server hardware with Capture agent.",
+ icon: "Server",
+ articleCount: 6,
+ articles: [
+ {
+ id: "installing-capture",
+ title: "Installing Capture agent",
+ description: "Setup the monitoring agent on your servers.",
+ keywords: ["install", "capture", "agent", "setup", "server"],
+ },
+ {
+ id: "cpu-monitoring",
+ title: "CPU monitoring",
+ description: "Usage, temperature, and core metrics.",
+ keywords: ["cpu", "processor", "usage", "temperature", "cores"],
+ },
+ {
+ id: "memory-monitoring",
+ title: "Memory monitoring",
+ description: "RAM usage and availability tracking.",
+ keywords: ["memory", "ram", "usage", "available", "free"],
+ },
+ {
+ id: "disk-monitoring",
+ title: "Disk monitoring",
+ description: "Storage space and read/write speeds.",
+ keywords: ["disk", "storage", "space", "read", "write", "speed"],
+ },
+ {
+ id: "network-monitoring",
+ title: "Network monitoring",
+ description: "Bandwidth and packet statistics.",
+ keywords: ["network", "bandwidth", "packet", "interface", "traffic"],
+ },
+ {
+ id: "hardware-thresholds",
+ title: "Setting hardware thresholds",
+ description: "Alert when resources become critical.",
+ keywords: ["threshold", "alert", "critical", "warning", "limit"],
+ },
+ ],
+ },
+ {
+ id: "incidents",
+ title: "Incidents & alerts",
+ description: "Respond to downtime and issues.",
+ icon: "AlertTriangle",
+ articleCount: 4,
+ articles: [
+ {
+ id: "understanding-incidents",
+ title: "Understanding incidents",
+ description: "How incidents are detected and created.",
+ keywords: ["incident", "detection", "downtime", "outage", "alert"],
+ },
+ {
+ id: "incident-timeline",
+ title: "Incident timeline",
+ description: "Tracking start, duration, and resolution.",
+ keywords: ["timeline", "duration", "start", "end", "resolution"],
+ },
+ {
+ id: "resolving-incidents",
+ title: "Resolving incidents",
+ description: "Manual vs automatic resolution.",
+ keywords: ["resolve", "manual", "automatic", "acknowledge", "fix"],
+ },
+ {
+ id: "incident-history",
+ title: "Incident history and analysis",
+ description: "Learn from past outages.",
+ keywords: ["history", "analysis", "report", "past", "outage"],
+ },
+ ],
+ },
+ {
+ id: "notifications",
+ title: "Notifications",
+ description: "Get alerted when things go wrong.",
+ icon: "Bell",
+ articleCount: 7,
+ articles: [
+ {
+ id: "email-notifications",
+ title: "Email notifications",
+ description: "SMTP setup and configuration.",
+ keywords: ["email", "smtp", "mail", "setup", "configure"],
+ },
+ {
+ id: "slack-integration",
+ title: "Slack integration",
+ description: "Webhook setup for Slack channels.",
+ keywords: ["slack", "webhook", "integration", "channel"],
+ },
+ {
+ id: "discord-integration",
+ title: "Discord integration",
+ description: "Channel notifications via webhooks.",
+ keywords: ["discord", "webhook", "integration", "channel"],
+ },
+ {
+ id: "pagerduty-integration",
+ title: "PagerDuty integration",
+ description: "On-call management integration.",
+ keywords: ["pagerduty", "oncall", "integration", "escalation"],
+ },
+ {
+ id: "webhook-notifications",
+ title: "Webhook notifications",
+ description: "Custom HTTP callbacks for alerts.",
+ keywords: ["webhook", "http", "callback", "custom", "api"],
+ },
+ {
+ id: "matrix-integration",
+ title: "Matrix integration",
+ description: "Decentralized messaging notifications.",
+ keywords: ["matrix", "messaging", "integration", "homeserver"],
+ },
+ {
+ id: "testing-notifications",
+ title: "Testing notifications",
+ description: "Verify your notification setup works.",
+ keywords: ["test", "verify", "notification", "check"],
+ },
+ ],
+ },
+ {
+ id: "status-pages",
+ title: "Status pages",
+ description: "Share uptime with your users.",
+ icon: "Wifi",
+ articleCount: 5,
+ articles: [
+ {
+ id: "creating-status-page",
+ title: "Creating a status page",
+ description: "Public-facing uptime dashboard setup.",
+ keywords: ["status", "page", "create", "public", "dashboard"],
+ },
+ {
+ id: "customizing-appearance",
+ title: "Customizing appearance",
+ description: "Logo, colors, and branding options.",
+ keywords: ["customize", "logo", "color", "brand", "style"],
+ },
+ {
+ id: "adding-monitors",
+ title: "Adding monitors to status page",
+ description: "Main monitors and sub-components.",
+ keywords: ["add", "monitor", "component", "group"],
+ },
+ {
+ id: "display-options",
+ title: "Display options",
+ description: "Charts, uptime percentage, and login links.",
+ keywords: ["display", "chart", "uptime", "percentage", "option"],
+ },
+ {
+ id: "sharing",
+ title: "Sharing your status page",
+ description: "Share your status page URL with users.",
+ keywords: ["share", "url", "public", "link", "access"],
+ },
+ ],
+ },
+ {
+ id: "maintenance",
+ title: "Maintenance windows",
+ description: "Schedule planned downtime.",
+ icon: "Wrench",
+ articleCount: 3,
+ articles: [
+ {
+ id: "creating-maintenance",
+ title: "Creating maintenance windows",
+ description: "Suppress alerts during planned maintenance.",
+ keywords: ["maintenance", "create", "schedule", "suppress", "alert"],
+ },
+ {
+ id: "recurring-maintenance",
+ title: "One-time vs recurring",
+ description: "Different scheduling options.",
+ keywords: ["recurring", "one-time", "schedule", "repeat", "frequency"],
+ },
+ {
+ id: "managing-maintenance",
+ title: "Managing active maintenance",
+ description: "Edit, extend, or cancel maintenance windows.",
+ keywords: ["manage", "edit", "cancel", "extend", "active"],
+ },
+ ],
+ },
+ {
+ id: "team-management",
+ title: "Team management",
+ description: "Collaborate with your team.",
+ icon: "Users",
+ articleCount: 3,
+ articles: [
+ {
+ id: "inviting-members",
+ title: "Inviting team members",
+ description: "Send email invitations to your team.",
+ keywords: ["invite", "team", "member", "email", "add"],
+ },
+ {
+ id: "user-roles",
+ title: "User roles",
+ description: "Admin vs User permissions explained.",
+ keywords: ["role", "admin", "user", "permission", "access"],
+ },
+ {
+ id: "managing-access",
+ title: "Managing team access",
+ description: "Add and remove team members.",
+ keywords: ["manage", "access", "remove", "team", "member"],
+ },
+ ],
+ },
+ {
+ id: "settings",
+ title: "Settings & configuration",
+ description: "Customize your Checkmate instance.",
+ icon: "Settings",
+ articleCount: 5,
+ articles: [
+ {
+ id: "email-configuration",
+ title: "Email configuration",
+ description: "SMTP server setup for notifications.",
+ keywords: ["email", "smtp", "configure", "server", "mail"],
+ },
+ {
+ id: "global-thresholds",
+ title: "Global thresholds",
+ description: "Default alert levels for all monitors.",
+ keywords: ["threshold", "global", "default", "alert", "level"],
+ },
+ {
+ id: "appearance",
+ title: "Appearance settings",
+ description: "Theme and display preferences.",
+ keywords: ["appearance", "theme", "dark", "light", "display", "mode"],
+ },
+ {
+ id: "timezone-settings",
+ title: "Timezone settings",
+ description: "Display preferences for dates and times.",
+ keywords: ["timezone", "time", "date", "display", "preference"],
+ },
+ {
+ id: "export-monitors",
+ title: "Export monitors",
+ description: "Download your monitors as JSON.",
+ keywords: ["export", "download", "backup", "json", "monitors"],
+ },
+ ],
+ },
+];
+
+// Fast Finds - Popular/quick access articles
+export const fastFinds = [
+ {
+ id: "ff-1",
+ title: "Creating your first monitor",
+ collectionId: "getting-started",
+ articleId: "quick-start",
+ },
+ {
+ id: "ff-2",
+ title: "Setting up email notifications",
+ collectionId: "notifications",
+ articleId: "email-notifications",
+ },
+ {
+ id: "ff-3",
+ title: "Installing Capture agent",
+ collectionId: "infrastructure",
+ articleId: "installing-capture",
+ },
+ {
+ id: "ff-4",
+ title: "Creating a status page",
+ collectionId: "status-pages",
+ articleId: "creating-status-page",
+ },
+ {
+ id: "ff-5",
+ title: "Understanding Core Web Vitals",
+ collectionId: "pagespeed",
+ articleId: "core-web-vitals",
+ },
+];
+
+// Helper functions
+export const getCollection = (collectionId) => {
+ return collections.find((c) => c.id === collectionId);
+};
+
+export const getArticle = (collectionId, articleId) => {
+ const collection = getCollection(collectionId);
+ return collection?.articles.find((a) => a.id === articleId);
+};
+
+export const getTotalArticleCount = () => {
+ return collections.reduce((sum, collection) => sum + collection.articleCount, 0);
+};
+
+/**
+ * @typedef {Object} SearchResult
+ * @property {string} collectionId
+ * @property {string} collectionTitle
+ * @property {string} articleId
+ * @property {string} articleTitle
+ * @property {string} articleDescription
+ * @property {'title' | 'description' | 'keyword'} matchType
+ */
+
+/**
+ * Search articles by query
+ * @param {string} query
+ * @returns {SearchResult[]}
+ */
+export const searchArticles = (query) => {
+ if (!query || query.trim().length < 2) return [];
+
+ const normalizedQuery = query.toLowerCase().trim();
+ const results = [];
+
+ for (const collection of collections) {
+ for (const article of collection.articles) {
+ let matchType = null;
+
+ // Check title match (highest priority)
+ if (article.title.toLowerCase().includes(normalizedQuery)) {
+ matchType = "title";
+ }
+ // Check description match
+ else if (article.description.toLowerCase().includes(normalizedQuery)) {
+ matchType = "description";
+ }
+ // Check keywords match
+ else if (
+ article.keywords.some((kw) => kw.toLowerCase().includes(normalizedQuery))
+ ) {
+ matchType = "keyword";
+ }
+
+ if (matchType) {
+ results.push({
+ collectionId: collection.id,
+ collectionTitle: collection.title,
+ articleId: article.id,
+ articleTitle: article.title,
+ articleDescription: article.description,
+ matchType,
+ });
+ }
+ }
+ }
+
+ // Sort by match type priority: title > description > keyword
+ return results.sort((a, b) => {
+ const priority = { title: 0, description: 1, keyword: 2 };
+ return priority[a.matchType] - priority[b.matchType];
+ });
+};
diff --git a/client/src/Components/v1/UserGuide/index.jsx b/client/src/Components/v1/UserGuide/index.jsx
new file mode 100644
index 000000000..c30b5d724
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/index.jsx
@@ -0,0 +1,24 @@
+import SidebarWrapper from "./SidebarWrapper";
+import {
+ UserGuideSidebarProvider,
+ useUserGuideSidebarContext,
+ TAB_BAR_WIDTH,
+ DEFAULT_CONTENT_WIDTH,
+} from "./UserGuideSidebarContext";
+
+// Main component that wraps SidebarWrapper with context consumer
+const UserGuideSidebar = () => {
+ const { isOpen, open, close } = useUserGuideSidebarContext();
+
+ return ;
+};
+
+export {
+ UserGuideSidebar,
+ UserGuideSidebarProvider,
+ useUserGuideSidebarContext,
+ TAB_BAR_WIDTH,
+ DEFAULT_CONTENT_WIDTH,
+};
+
+export default UserGuideSidebar;
diff --git a/client/src/Components/v1/UserGuide/styles/theme.js b/client/src/Components/v1/UserGuide/styles/theme.js
new file mode 100644
index 000000000..f3746657d
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/styles/theme.js
@@ -0,0 +1,222 @@
+/**
+ * Design Tokens for UserGuide
+ * Supports both light and dark themes via MUI theme integration
+ */
+
+// Typography (theme-independent)
+export const typography = {
+ fontFamily: {
+ sans: "'Inter', system-ui, -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif",
+ mono: "'Geist Mono', 'Fira Code', 'Consolas', monospace",
+ },
+ fontSize: {
+ xs: 12,
+ sm: 12,
+ base: 13,
+ md: 14,
+ lg: 15,
+ xl: 16,
+ "2xl": 20,
+ "3xl": 22,
+ "4xl": 26,
+ "5xl": 30,
+ },
+ lineHeight: {
+ tight: 1.2,
+ snug: 1.3,
+ normal: 1.4,
+ relaxed: 1.5,
+ },
+ fontWeight: {
+ normal: 400,
+ medium: 500,
+ semibold: 600,
+ bold: 700,
+ },
+};
+
+// Spacing (theme-independent)
+export const spacing = {
+ xs: "4px",
+ sm: "8px",
+ md: "12px",
+ lg: "16px",
+ xl: "24px",
+ "2xl": "32px",
+ "3xl": "40px",
+ "4xl": "48px",
+};
+
+// Brand colors (theme-independent) - Checkmate blue
+export const brandColors = {
+ primary: "#1570EF",
+ primaryDark: "#1054B0",
+ primaryLight: "#5BA3F5",
+ success: "#17B26A",
+ warning: "#F79009",
+ error: "#F04438",
+};
+
+/**
+ * Get theme-aware colors based on MUI theme mode
+ * @param {'light' | 'dark'} mode - The current theme mode
+ * @returns {Object} Theme-aware color tokens
+ */
+export const getColors = (mode) => {
+ const isDark = mode === "dark";
+
+ return {
+ text: {
+ primary: isDark ? "#ffffff" : "#1C2130",
+ secondary: isDark ? "#d0d5dd" : "#475467",
+ muted: isDark ? "#98a2b3" : "#667085",
+ white: "#ffffff",
+ whiteMuted: "rgba(255, 255, 255, 0.8)",
+ },
+ brand: brandColors,
+ border: {
+ default: isDark ? "#344054" : "#d0d5dd",
+ },
+ background: {
+ white: isDark ? "#1a1a1a" : "#ffffff",
+ alt: isDark ? "#0d0d0d" : "#f9fafb",
+ code: isDark ? "#1C2130" : "#f3f4f6",
+ sidebar: isDark ? "#101010" : "#ffffff",
+ hover: isDark ? "#2a2a2a" : "#f3f4f6",
+ },
+ };
+};
+
+/**
+ * Get theme-aware border styles
+ * @param {'light' | 'dark'} mode - The current theme mode
+ * @returns {Object} Border styles
+ */
+export const getBorder = (mode) => {
+ const colors = getColors(mode);
+ return {
+ radius: "4px",
+ default: `1px solid ${colors.border.default}`,
+ };
+};
+
+/**
+ * Get theme-aware image styles
+ * @param {'light' | 'dark'} mode - The current theme mode
+ * @returns {Object} Image styles
+ */
+export const getImageStyles = (mode) => {
+ const colors = getColors(mode);
+ const border = getBorder(mode);
+ const isDark = mode === "dark";
+
+ return {
+ image: {
+ width: "100%",
+ borderRadius: border.radius,
+ border: border.default,
+ display: "block",
+ boxShadow: isDark ? "0 2px 8px rgba(0, 0, 0, 0.3)" : "0 2px 8px rgba(0, 0, 0, 0.1)",
+ },
+ caption: {
+ fontSize: 10,
+ color: colors.text.muted,
+ textAlign: "center",
+ fontStyle: "italic",
+ },
+ };
+};
+
+/**
+ * Get theme-aware chip styles
+ * @param {'light' | 'dark'} mode - The current theme mode
+ * @returns {Object} Chip styles
+ */
+export const getChipStyles = (mode) => {
+ const isDark = mode === "dark";
+
+ return {
+ url: {
+ backgroundColor: isDark ? "rgba(21, 112, 239, 0.2)" : "rgba(21, 112, 239, 0.1)",
+ color: isDark ? "#5BA3F5" : "#1570EF",
+ padding: "2px 8px",
+ borderRadius: "4px",
+ fontFamily: typography.fontFamily.mono,
+ fontSize: "0.9em",
+ },
+ code: {
+ backgroundColor: isDark ? "#2d2d2d" : "#f3f4f6",
+ color: isDark ? "#d0d5dd" : "#1C2130",
+ padding: "2px 8px",
+ borderRadius: "4px",
+ fontFamily: typography.fontFamily.mono,
+ fontSize: "0.9em",
+ },
+ };
+};
+
+/**
+ * Get theme-aware component styles
+ * @param {'light' | 'dark'} mode - The current theme mode
+ * @returns {Object} Component styles
+ */
+export const getComponentStyles = (mode) => {
+ const colors = getColors(mode);
+ const border = getBorder(mode);
+
+ return {
+ card: {
+ backgroundColor: colors.background.white,
+ border: border.default,
+ borderRadius: border.radius,
+ },
+ heading: {
+ h2: {
+ fontFamily: typography.fontFamily.sans,
+ fontSize: typography.fontSize["2xl"],
+ fontWeight: typography.fontWeight.semibold,
+ color: colors.text.primary,
+ lineHeight: typography.lineHeight.normal,
+ },
+ h3: {
+ fontFamily: typography.fontFamily.sans,
+ fontSize: typography.fontSize.lg,
+ fontWeight: typography.fontWeight.semibold,
+ color: colors.text.primary,
+ lineHeight: typography.lineHeight.normal,
+ },
+ },
+ paragraph: {
+ fontFamily: typography.fontFamily.sans,
+ fontSize: typography.fontSize.base,
+ color: colors.text.primary,
+ lineHeight: typography.lineHeight.relaxed,
+ },
+ listItem: {
+ fontSize: typography.fontSize.base,
+ color: colors.text.primary,
+ lineHeight: typography.lineHeight.snug,
+ },
+ };
+};
+
+// Legacy exports for backwards compatibility (dark mode defaults)
+// These will be replaced by useUserGuideTheme hook usage
+export const colors = getColors("dark");
+export const border = getBorder("dark");
+export const imageStyles = getImageStyles("dark");
+export const chipStyles = getChipStyles("dark");
+export const componentStyles = getComponentStyles("dark");
+
+// Export everything as a single theme object (dark mode default)
+export const theme = {
+ colors,
+ typography,
+ spacing,
+ border,
+ imageStyles,
+ chipStyles,
+ componentStyles,
+};
+
+export default theme;
diff --git a/client/src/Components/v1/UserGuide/styles/useUserGuideTheme.js b/client/src/Components/v1/UserGuide/styles/useUserGuideTheme.js
new file mode 100644
index 000000000..feb6d01dc
--- /dev/null
+++ b/client/src/Components/v1/UserGuide/styles/useUserGuideTheme.js
@@ -0,0 +1,123 @@
+/**
+ * Hook to get theme-aware styles for UserGuide components
+ * Uses MUI's theme system from Checkmate
+ */
+import { useTheme } from "@mui/material/styles";
+import { typography, spacing, brandColors } from "./theme";
+
+/**
+ * Returns theme-aware styles for UserGuide components
+ */
+export const useUserGuideTheme = () => {
+ const theme = useTheme();
+ const isDark = theme.palette.mode === "dark";
+
+ const colors = {
+ text: {
+ primary: theme.palette.primary.contrastText,
+ secondary: theme.palette.primary.contrastTextSecondary,
+ muted: theme.palette.primary.contrastTextTertiary,
+ white: "#ffffff",
+ },
+ brand: brandColors,
+ border: {
+ default: theme.palette.primary.lowContrast,
+ },
+ background: {
+ white: isDark ? "#1a1a1a" : "#ffffff",
+ alt: theme.palette.primaryBackground.main,
+ code: isDark ? "#1C2130" : "#f3f4f6",
+ sidebar: theme.palette.primaryBackground.main,
+ hover: isDark ? "#2a2a2a" : "#f3f4f6",
+ },
+ };
+
+ const border = {
+ radius: "4px",
+ default: `1px solid ${colors.border.default}`,
+ };
+
+ const imageStyles = {
+ image: {
+ width: "100%",
+ borderRadius: border.radius,
+ border: border.default,
+ display: "block",
+ boxShadow: isDark ? "0 2px 8px rgba(0, 0, 0, 0.3)" : "0 2px 8px rgba(0, 0, 0, 0.1)",
+ },
+ caption: {
+ fontSize: 10,
+ color: colors.text.muted,
+ textAlign: "center",
+ fontStyle: "italic",
+ },
+ };
+
+ const chipStyles = {
+ url: {
+ backgroundColor: isDark ? "rgba(21, 112, 239, 0.2)" : "rgba(21, 112, 239, 0.1)",
+ color: isDark ? "#5BA3F5" : "#1570EF",
+ padding: "2px 8px",
+ borderRadius: "4px",
+ fontFamily: typography.fontFamily.mono,
+ fontSize: "0.9em",
+ },
+ code: {
+ backgroundColor: isDark ? "#2d2d2d" : "#f3f4f6",
+ color: isDark ? "#d0d5dd" : "#1C2130",
+ padding: "2px 8px",
+ borderRadius: "4px",
+ fontFamily: typography.fontFamily.mono,
+ fontSize: "0.9em",
+ },
+ };
+
+ const componentStyles = {
+ card: {
+ backgroundColor: colors.background.white,
+ border: border.default,
+ borderRadius: border.radius,
+ },
+ heading: {
+ h2: {
+ fontFamily: typography.fontFamily.sans,
+ fontSize: typography.fontSize["2xl"],
+ fontWeight: typography.fontWeight.semibold,
+ color: colors.text.primary,
+ lineHeight: typography.lineHeight.normal,
+ },
+ h3: {
+ fontFamily: typography.fontFamily.sans,
+ fontSize: typography.fontSize.lg,
+ fontWeight: typography.fontWeight.semibold,
+ color: colors.text.primary,
+ lineHeight: typography.lineHeight.normal,
+ },
+ },
+ paragraph: {
+ fontFamily: typography.fontFamily.sans,
+ fontSize: typography.fontSize.base,
+ color: colors.text.primary,
+ lineHeight: typography.lineHeight.relaxed,
+ },
+ listItem: {
+ fontSize: typography.fontSize.base,
+ color: colors.text.primary,
+ lineHeight: typography.lineHeight.snug,
+ },
+ };
+
+ return {
+ colors,
+ typography,
+ spacing,
+ border,
+ imageStyles,
+ chipStyles,
+ componentStyles,
+ isDark,
+ theme,
+ };
+};
+
+export default useUserGuideTheme;