Skip to content

feat: add user guide documentation sidebar#3148

Open
gorkem-bwl wants to merge 15 commits intodevelopfrom
feat/user-guide-documentation
Open

feat: add user guide documentation sidebar#3148
gorkem-bwl wants to merge 15 commits intodevelopfrom
feat/user-guide-documentation

Conversation

@gorkem-bwl
Copy link
Contributor

@gorkem-bwl gorkem-bwl commented Jan 17, 2026

Summary

  • Add in-app user guide with comprehensive documentation for all Checkmate features
  • Implement collapsible sidebar that integrates with the main application layout
  • Include search functionality across all documentation articles

Features

  • 10 documentation collections covering getting started, uptime monitoring, PageSpeed, infrastructure, incidents, notifications, status pages, maintenance, team management, and settings
  • 49 articles with accurate, codebase-verified content
  • Theme support for light and dark modes with proper color tokens
  • Search across all articles by title, description, and keywords
  • Image lightbox for viewing documentation screenshots
  • Multiple content block types including paragraphs, lists, tables, code blocks, callouts, and images

Changes

  • src/Components/v1/UserGuide/ - New user guide component directory
  • src/Components/v1/Layouts/HomeLayout/index.jsx - Integrate user guide sidebar
  • src/Components/v1/Sidebar/index.jsx - Add background color and z-index for proper layering
  • src/App.jsx - Add user guide context provider

Summary by CodeRabbit

  • New Features

    • Integrated in-app, resizable user guide sidebar with persistent open/width state and tab navigation (User Guide / Help).
    • Browse documentation by collections and articles with per-article TOC, prev/next navigation, and image lightbox.
    • Instant search with results and direct navigation; Help section links to community resources.
    • Dark-mode-aware styling across the user guide.
  • Documentation

    • Added comprehensive user-guide content and a style guide for consistent docs.

✏️ Tip: You can customize this high-level summary in your review settings.

- Add comprehensive user guide with 10 documentation collections
- Include 49 articles covering all Checkmate features
- Implement sidebar navigation with search functionality
- Support light/dark theme with proper color tokens
- Add article content renderer with multiple block types
- Include image lightbox for documentation screenshots
@coderabbitai
Copy link

coderabbitai bot commented Jan 17, 2026

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between c7107a5 and b196d0d.

📒 Files selected for processing (20)
  • client/src/Components/v1/UserGuide/ArticlePage.css
  • client/src/Components/v1/UserGuide/ArticlePage.jsx
  • client/src/Components/v1/UserGuide/CollectionPage.css
  • client/src/Components/v1/UserGuide/CollectionPage.jsx
  • client/src/Components/v1/UserGuide/ContentRenderer.css
  • client/src/Components/v1/UserGuide/ContentRenderer.jsx
  • client/src/Components/v1/UserGuide/HelpSection.css
  • client/src/Components/v1/UserGuide/HelpSection.jsx
  • client/src/Components/v1/UserGuide/ImageLightbox.jsx
  • client/src/Components/v1/UserGuide/STYLE_GUIDE.md
  • client/src/Components/v1/UserGuide/SearchResults.css
  • client/src/Components/v1/UserGuide/SidebarHeader.css
  • client/src/Components/v1/UserGuide/SidebarHeader.jsx
  • client/src/Components/v1/UserGuide/SidebarWrapper.jsx
  • client/src/Components/v1/UserGuide/TabBar.jsx
  • client/src/Components/v1/UserGuide/UserGuideLanding.css
  • client/src/Components/v1/UserGuide/UserGuideLanding.jsx
  • client/src/Components/v1/UserGuide/content/index.js
  • client/src/Components/v1/UserGuide/content/userGuideConfig.js
  • client/src/Components/v1/UserGuide/index.jsx
 __________________________________________
< Given enough GPUs, all bugs are shallow. >
 ------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ

✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.

Tip

CodeRabbit can enforce grammar and style rules using `languagetool`.

Configure languagetool in your project's settings in CodeRabbit enable/disable rules and categories. Refer to the LanguageTool Community to learn more.

Note

.coderabbit.yml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'release_notes'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

This PR introduces a comprehensive User Guide feature with a resizable sidebar component, supporting navigation through collections and articles, integrated search, customizable content rendering with multiple block types, theme-aware styling, state management via React Context, and documentation content structures.

Changes

Cohort / File(s) Summary
App Integration
client/src/App.jsx, client/src/Components/v1/Layouts/HomeLayout/index.jsx
Wraps application with UserGuideSidebarProvider and renders UserGuideSidebar within HomeLayout; establishes provider pattern for sidebar state management.
Sidebar Context & State
client/src/Components/v1/UserGuide/UserGuideSidebarContext.jsx, client/src/Components/v1/UserGuide/index.jsx
Introduces context provider managing sidebar open/close state, current path, content width; persists state to localStorage; exports context utilities and constants for theme dimensions.
Sidebar Structure & Navigation
client/src/Components/v1/UserGuide/SidebarWrapper..., client/src/Components/v1/UserGuide/TabBar..., client/src/Components/v1/UserGuide/SidebarHeader...
Implements resizable sidebar container with draggable handle, tab switching (User Guide/Help), navigation controls, breadcrumbs, search toggle, and history management with back/forward buttons.
Content Pages & Layouts
client/src/Components/v1/UserGuide/UserGuideLanding..., client/src/Components/v1/UserGuide/CollectionPage..., client/src/Components/v1/UserGuide/ArticlePage...
Three-level content hierarchy: landing page with collections grid, collection pages displaying article lists, and article pages with dynamic table of contents and content rendering; includes CSS styling with hover transitions.
Content Rendering System
client/src/Components/v1/UserGuide/ContentRenderer...
Comprehensive content block renderer supporting headings, paragraphs, callouts, lists, icon cards, grid cards, tables, images with lightbox, code blocks, and article links; includes custom Markdown-like parser for inline formatting and internal article link syntax.
Search & Discovery
client/src/Components/v1/UserGuide/SearchResults...
Search results page displaying prioritized matches across articles by title, description, and keywords; integrates with navigation callback.
Supporting Components
client/src/Components/v1/UserGuide/ImageLightbox.jsx, client/src/Components/v1/UserGuide/HelpSection...
Image lightbox modal with keyboard accessibility and scroll lock; Help section with Discord and GitHub discussion links.
Theme & Styling System
client/src/Components/v1/UserGuide/styles/theme.js, client/src/Components/v1/UserGuide/styles/useUserGuideTheme.js
Centralized design tokens (typography, spacing, brand colors) and theme-aware styling hook; supports light/dark mode switching via MUI theme integration; exports color palette and component styles.
Configuration & Content Data
client/src/Components/v1/UserGuide/content/userGuideConfig.js, client/src/Components/v1/UserGuide/content/index.js, client/src/Components/v1/UserGuide/content/contentTypes.js
Defines collection metadata, article structure, and search configuration; exports retrieval helpers (getCollection, getArticle, searchArticles); provides JSDoc content block type definitions and article content registry.
Documentation & Styling
client/src/Components/v1/Sidebar/index.jsx, client/src/Components/v1/UserGuide/STYLE_GUIDE.md
Adds z-index and background color to existing Sidebar; includes comprehensive style guide for documentation voice, structure, formatting, templates, and quality checklist.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant App as App.jsx
    participant Provider as UserGuideSidebarProvider
    participant SidebarWrapper as SidebarWrapper
    participant Context as UserGuideSidebarContext
    participant ContentPages as ContentPages<br/>(Landing/Collection/Article)
    
    User->>App: Load application
    App->>Provider: Wrap with provider
    Provider->>Context: Initialize (open/close state, path)
    
    User->>SidebarWrapper: Toggle sidebar open
    SidebarWrapper->>Context: Update isOpen = true
    Context->>Context: Persist to localStorage
    Context->>SidebarWrapper: Notify state change
    SidebarWrapper->>ContentPages: Render UserGuideLanding
    
    User->>ContentPages: Click collection
    ContentPages->>SidebarWrapper: Call onNavigate(collectionId)
    SidebarWrapper->>Context: Update currentPath
    Context->>SidebarWrapper: Notify change
    SidebarWrapper->>ContentPages: Render CollectionPage
    
    User->>ContentPages: Click article
    ContentPages->>SidebarWrapper: Call onNavigate(collectionId, articleId)
    SidebarWrapper->>Context: Update currentPath
    SidebarWrapper->>ContentPages: Render ArticlePage with ContentRenderer
    ContentPages->>User: Display article with styled blocks
Loading
sequenceDiagram
    actor User
    participant SearchUI as Search Input
    participant SearchResults as SearchResults
    participant Config as userGuideConfig
    participant Navigation as Article Navigation
    
    User->>SearchUI: Type query (≥2 chars)
    SearchUI->>SearchResults: Pass query
    SearchResults->>Config: searchArticles(query)
    Config->>Config: Filter & rank by title/description/keywords
    Config->>SearchResults: Return results array
    SearchResults->>SearchResults: Render result list
    
    User->>SearchResults: Click result item
    SearchResults->>Navigation: onNavigate(collectionId, articleId)
    Navigation->>SearchResults: onClearSearch()
    SearchResults->>SearchUI: Clear search, close results
Loading
sequenceDiagram
    participant Article as ArticlePage
    participant ContentRenderer as ContentRenderer
    participant Theme as useUserGuideTheme
    participant Parser as parseMarkdown
    
    Article->>ContentRenderer: Pass content blocks
    ContentRenderer->>Theme: Get theme colors/tokens
    Theme->>ContentRenderer: Return isDark, colors, typography
    
    loop For each content block
        ContentRenderer->>Parser: parseMarkdown(text)
        Parser->>Parser: Extract [[text]](collection/article) links
        Parser->>ContentRenderer: Return mixed nodes/strings
        ContentRenderer->>ContentRenderer: Render block with theme styles
    end
    
    ContentRenderer->>Article: Render styled content tree
    Article->>Article: Track TOC scroll position
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

The PR introduces a substantial feature spanning 20+ interdependent files with varying complexity. Key components like ContentRenderer (902 lines with multiple rendering helpers and custom parsing logic), SidebarWrapper (475 lines with navigation history and resize management), and the content configuration system (2666 lines) require careful review. Multiple new patterns (context provider, theme hook, content block types) and integration points with existing code add to the cognitive load. Many files follow consistent patterns (CSS styling, simple wrapper components), but the overall coherence and state management flows require holistic understanding.

Poem

🐰 A sidebar springs to life with grace,
Collections dancing in their place,
With articles to guide the way,
And search to light a brighter day,
Dark themes gleam and tables shine,
Documentation—simply divine!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the summary, features, and changes but lacks completion of required checklist items from the template, which should all be marked before submission. Complete all required checklist items by marking [ ] as [x], including local deployment testing, self-review, issue number inclusion, and code formatting verification.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main feature being added: a user guide documentation sidebar for the application.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🤖 Fix all issues with AI agents
In `@client/src/Components/v1/UserGuide/ArticlePage.jsx`:
- Around line 157-158: The JSX in ArticlePage.jsx contains hardcoded user-facing
strings that must be localized; update the ArticlePage component to replace the
literal texts ("In this article", "Article content coming soon.", "Previous",
"Next") with the i18n translation function t(...) (or useTranslation hook) and
ensure t is imported/available in the component, then wrap each literal in t
calls (e.g., the span rendering "In this article" and the elements rendering the
article placeholder and pagination labels) so the UI uses translated strings.

In `@client/src/Components/v1/UserGuide/CollectionPage.jsx`:
- Line 82: The hardcoded string "{collection.articleCount} articles in this
collection" in CollectionPage.jsx must be internationalized; replace it with a
call to the translation function (e.g., use t('collection.articleCount', {
count: collection.articleCount }) or an appropriate key) and ensure the
component imports/receives the t function (from react-i18next or project i18n)
so the number is passed as an interpolation/pluralization parameter; update the
translation key in your locale files accordingly.

In `@client/src/Components/v1/UserGuide/content/index.js`:
- Around line 1-117: The articleContents object currently hardcodes all
user-facing strings (headings, paragraphs, list items, etc.) which breaks
internationalization; update the content to use translation keys and the app's
translation function (or per-locale content files) so strings are resolved at
render time (e.g., replace literal texts inside articleContents with keys like
"getting-started.what-is-checkmate.overview" and call i18n/t or ContentRenderer
to resolve them), or split articleContents into locale-specific modules (e.g.,
articleContents.en.js/articleContents.de.js) and load by locale; locate the
articleContents constant in this file and adjust the consumers (ContentRenderer
or any component that renders these blocks) to call the translation function for
block.text, item.title, item.description, list items, and other user-facing
fields instead of embedding English literals.

In `@client/src/Components/v1/UserGuide/content/userGuideConfig.js`:
- Around line 31-64: Replace hardcoded user-facing strings in the exported
collections array (the collections constant and its nested article objects) with
translation keys: add fields like titleKey and descriptionKey on each collection
and article instead of using literal title/description values (and optionally
convert keywords to keywordKey if they need translation), and update callers to
resolve them with the i18n function (e.g., t(collection.titleKey) /
t(article.descriptionKey)); ensure the exported structure still provides IDs and
articleCount but uses keys for all UI text so the consuming component (which
currently reads collection.title and article.title) can switch to calling t(...)
on the new titleKey/descriptionKey fields.

In `@client/src/Components/v1/UserGuide/HelpSection.jsx`:
- Around line 17-119: Several user-facing strings in HelpSection.jsx (e.g., the
"Contact us" and "GitHub discussions" headings, their paragraph bodies like
"Can't find what you need? Our support team is here to help." and "Questions
about features, how-tos, or use cases? Join the community discussion.", and CTA
labels "Join our Discord" and "GitHub discussions") are hardcoded; wrap each
visible string in the i18n translation function t(...) and ensure the component
imports/uses the translation hook (e.g., useTranslation -> t) so headings,
paragraphs, and anchor text use t("...") instead of literal strings; update any
inline aria/alt/text variants the same way and keep the ExternalLink usage and
className="user-guide-help-button" intact.

In `@client/src/Components/v1/UserGuide/ImageLightbox.jsx`:
- Around line 33-78: The modal lacks dialog semantics and the close button is
icon-only/unlocalized; update the backdrop container (the element using
handleBackdropClick) to include role="dialog" and aria-modal="true" (and
optionally aria-labelledby/aria-describedby if headers/descriptions exist), and
change the close button (onClick={onClose}, X icon) to provide an accessible,
localized name by using the i18n t(...) function for the label (e.g. t("Close
(Esc)") or t("Close")) via aria-label (and keep or remove the title) so screen
readers announce the button; ensure you import/use the same t used across the
app.

In `@client/src/Components/v1/UserGuide/SearchResults.jsx`:
- Around line 100-166: Replace the clickable result row div with a semantic
interactive element (preferably a <button type="button"> or an <a> if
navigating) so keyboard focus and activation work; keep the existing className
"user-guide-search-result-item", inline styles, and the onClick handler
(handleResultClick) but move it to the button/a and ensure any child icons
(FileText, ArrowRight) and text (result.articleTitle, result.articleDescription,
result.collectionTitle) remain unchanged; also add an accessible name (either
the visible title or an aria-label) and ensure focus styles are preserved so
keyboard users can activate rows.
- Around line 15-158: The UI strings in SearchResults.jsx (e.g., the placeholder
"Type at least 2 characters to search...", the no-results message "No results
found for \"{query}\"", "Try different keywords or browse by topic", the "Clear
search" button label, the per-result "in {result.collectionTitle}", and the
results count span that currently renders {results.length} result(s) for
"{query}") must be wrapped with the i18n translator t(...) and use
pluralization/interpolation where appropriate; update the SearchResults
component to import t (or use useTranslation) and replace literal strings with t
keys, use t('search.resultsCount', { count: results.length, query }) or the i18n
pluralization API for the results count, and use t('search.noResults', { query
}) and t('search.typeAtLeast', { min: 2 }) etc., ensuring all visible text,
including the "Clear search" button and "in {result.collectionTitle}", use t
with interpolation rather than hard-coded strings.

In `@client/src/Components/v1/UserGuide/SidebarHeader.jsx`:
- Around line 106-107: Add i18n by importing and using the translation hook
(const { t } = useTranslation()) in the SidebarHeader component and replace all
hardcoded user-facing strings with t() calls: update prop values title="Back",
title="Forward", title="Home", title="Open in new tab", title="Search",
title="Close" to use t('userGuide.back') / t('userGuide.forward') /
t('userGuide.home') / t('userGuide.openInNewTab') / t('userGuide.search') /
t('userGuide.close'), replace placeholder="Search articles..." with
t('userGuide.searchPlaceholder'), and replace the fallback "User guide" with
t('userGuide.title'); ensure keys are consistent and add corresponding entries
to your locales if missing.

In `@client/src/Components/v1/UserGuide/SidebarWrapper.jsx`:
- Around line 198-208: Replace the hardcoded UI strings in the breadcrumbs
builder with translation keys via the project's translation function (e.g.,
useTranslation()/t) instead of literal "User guide" and "Help": update the items
array construction where items = [{ label: "User guide", onClick:
handleHomeClick }], the default return [{ label: "Help", onClick: () => {} }],
and any other literal labels in that switch block to call t('userGuide') and
t('help') (or appropriate i18n keys); import/use the translation hook at the top
of SidebarWrapper.jsx and keep existing handlers (handleHomeClick, setArticleId,
article, collection, items) unchanged.

In `@client/src/Components/v1/UserGuide/TabBar.jsx`:
- Around line 64-81: The TabItem labels "User guide" and "Help" are hardcoded in
TabBar.jsx; update them to use the translation function by replacing the label
props with t("user_guide") and t("help") (or the appropriate translation keys)
so TabItem calls use label={t("...")}; locate the TabItem components in the
TabBar component (props: activeTab, onTabChange, colors, typography) and ensure
the translation function t is imported/available in that module before using it.
- Around line 7-45: The tab item currently rendered as a non-focusable <div>
with onClick must be made keyboard-accessible: replace the outer div that uses
onClick/isActive/icon/label with a semantic <button type="button"> (or if you
must keep a div, add role="button" tabIndex={0} and a onKeyDown handler that
triggers onClick on Enter/Space) and ensure to preserve the existing className
and inline style logic; also add an appropriate ARIA state such as aria-pressed
or aria-selected when isActive to communicate selection to assistive tech.

In `@client/src/Components/v1/UserGuide/UserGuideLanding.jsx`:
- Around line 73-137: The collection card uses a clickable div which is not
keyboard-accessible; replace the outer <div key={collection.id}> with a semantic
interactive element (preferably a <button type="button> or an <a> if it
navigates) and wire the existing onClick={() => onNavigate(collection.id)} to
that element so keyboard activation works, preserve styling currently applied to
the div, and ensure the element has an accessible name (e.g., aria-label or use
the visible collection.title) and proper focus styles; if you must keep a
non-semantic element, add tabIndex={0} and handle onKeyDown to call
onNavigate(collection.id) for Enter/Space, but using a <button> is the preferred
fix.
- Around line 47-200: Replace hard-coded user-facing strings in the
UserGuideLanding component with i18n calls: import/use the translation hook
(e.g., t) and wrap headings ("Browse by topic", "About our docs", "There are a
few ways to explore our docs:", "In the product", helper text "Look for help
icons and tooltips throughout the interface.") and collection text
(collection.title, collection.description) with t(...), and change the article
count badge to use pluralization/interpolation (e.g., t('articles', { count:
collection.articleCount })) so the JSX inside the collections.map block and the
in-app info card all use t keys; add appropriate keys (e.g., collections.title,
collections.description, collections.articles) to your locale files and ensure
plural forms are defined.

In `@client/src/Components/v1/UserGuide/UserGuideSidebarContext.jsx`:
- Around line 14-18: The initializer for isOpen and the places that read/write
SIDEBAR_STATE_KEY should guard against localStorage throwing by wrapping all
localStorage accesses in try/catch and using safe fallbacks; specifically,
update the useState initializer that reads
localStorage.getItem(SIDEBAR_STATE_KEY) to catch errors and return false if
access fails, and update the code paths that call
localStorage.setItem/removeItem (the setter logic around setIsOpen and any
effect or handler at lines ~52-55) to perform writes inside try/catch and
silently ignore or fallback when storage is unavailable. Ensure you reference
SIDEBAR_STATE_KEY, isOpen and setIsOpen when making these changes so behavior
remains the same when localStorage is available.
🟡 Minor comments (8)
client/src/Components/v1/UserGuide/UserGuideSidebarContext.jsx-89-94 (1)

89-94: Localize the error string.

Frontend user-facing strings should use t("..."). This error can surface via error boundaries; please route it through i18n.

client/src/Components/v1/UserGuide/content/contentTypes.js-168-175 (1)

168-175: Guard extractToc against non-array inputs.

If blocks is undefined/null, this will throw. A small guard keeps it safe.

🛠️ Proposed fix
-export const extractToc = (blocks) => {
-	return blocks
+export const extractToc = (blocks = []) => {
+	if (!Array.isArray(blocks)) return [];
+	return blocks
 		.filter((block) => block.type === "heading")
 		.map((block) => ({
 			label: block.text,
 			href: `#${block.id}`,
 			level: block.level,
 		}));
 };
client/src/Components/v1/UserGuide/SearchResults.css-1-10 (1)

1-10: Theme selector missing for dark-mode-specific styles.

The comment indicates this is for "Dark theme," but unlike other CSS files in this PR (e.g., ContentRenderer.css), there's no [data-mui-color-scheme="dark"] selector. The rgba(255, 255, 255, 0.05) hover background is a white overlay that only works well on dark backgrounds—it will be nearly invisible on light theme.

Consider adding theme-specific selectors for consistency:

Suggested fix
 /* 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;
+.user-guide-search-result-item:hover {
+	background-color: rgba(0, 0, 0, 0.04) !important;
+	border-color: `#d0d5dd` !important;
+}
+
+[data-mui-color-scheme="dark"] .user-guide-search-result-item:hover {
+	background-color: rgba(255, 255, 255, 0.05) !important;
+	border-color: `#4a5568` !important;
 }
client/src/Components/v1/UserGuide/TabBar.css-7-13 (1)

7-13: Theme-specific selectors needed for light/dark mode compatibility.

Similar to SearchResults.css, these hover styles use dark-theme colors (rgba(255, 255, 255, 0.05) and #1a1a1a) without theme selectors. On light theme, the hover effect will be barely visible and the active state may look jarring.

Suggested fix
 .user-guide-tab-item:hover {
-	background-color: rgba(255, 255, 255, 0.05);
+	background-color: rgba(0, 0, 0, 0.04);
 }

-.user-guide-tab-item.active:hover {
-	background-color: `#1a1a1a`;
+[data-mui-color-scheme="dark"] .user-guide-tab-item:hover {
+	background-color: rgba(255, 255, 255, 0.05);
+}
+
+.user-guide-tab-item.active:hover {
+	background-color: `#f5f5f5`;
+}
+
+[data-mui-color-scheme="dark"] .user-guide-tab-item.active:hover {
+	background-color: `#1a1a1a`;
 }
client/src/Components/v1/UserGuide/SearchResults.jsx-6-16 (1)

6-16: Guard against undefined query before calling trim().

If query is ever null/undefined, this throws.

🔧 Suggested change
-const SearchResults = ({ query, onNavigate, onClearSearch }) => {
+const SearchResults = ({ query = "", onNavigate, onClearSearch }) => {
client/src/Components/v1/UserGuide/ArticlePage.jsx-29-41 (1)

29-41: Unused props: onBack and onBackToHome.

These props are destructured but never used in the component. Remove them if they're not needed, or implement the intended functionality.

Suggested fix
 const ArticlePage = ({
 	collection,
 	article,
-	onBack,
-	onBackToHome,
 	onNextArticle,
 	onPrevArticle,
 	nextArticle,
 	prevArticle,
 	tocItems,
 	children,
 	mode = "in-app",
 }) => {
client/src/Components/v1/UserGuide/CollectionPage.jsx-27-27 (1)

27-27: Unused onBack prop.

The onBack prop is destructured but never used in this component. Either remove it from the props or implement the intended back navigation functionality.

Suggested fix
-const CollectionPage = ({ collection, onBack, onArticleClick }) => {
+const CollectionPage = ({ collection, onArticleClick }) => {
client/src/Components/v1/UserGuide/ArticlePage.jsx-43-43 (1)

43-43: Unused IconComponent variable.

IconComponent is computed from collection.icon but is never used in the render output. Either remove it or add the intended icon rendering.

Suggested fix if not needed
 const { colors, typography, spacing, border, isDark } = useUserGuideTheme();
-const IconComponent = iconMap[collection.icon] || Rocket;
 const isInApp = mode === "in-app";
🧹 Nitpick comments (14)
client/src/Components/v1/UserGuide/content/userGuideConfig.js (1)

37-37: articleCount may drift out of sync with actual article count.

The articleCount property is manually maintained and could become inconsistent with the actual articles.length. Consider deriving it dynamically:

♻️ Suggested approach

Remove the static articleCount property and compute it dynamically in getTotalArticleCount:

 export const getTotalArticleCount = () => {
-  return collections.reduce((sum, collection) => sum + collection.articleCount, 0);
+  return collections.reduce((sum, collection) => sum + collection.articles.length, 0);
 };

Or add a getter function for individual collections:

export const getCollectionArticleCount = (collectionId) => {
  const collection = getCollection(collectionId);
  return collection?.articles.length ?? 0;
};

Also applies to: 70-70, 121-121, 160-160, 205-205, 238-238, 289-289, 328-328, 355-355, 382-382

client/src/Components/v1/UserGuide/styles/theme.js (2)

12-14: Duplicate font size values for xs and sm.

Both fontSize.xs and fontSize.sm are set to 12. If this is intentional for consistency, consider adding a comment. Otherwise, xs might be intended to be smaller (e.g., 10 or 11).

fontSize: {
  xs: 12,  // Same as sm - intentional?
  sm: 12,
  ...
}

203-209: Legacy exports default to dark mode - document expected migration path.

The comment mentions these will be replaced by useUserGuideTheme hook usage. Consider adding a deprecation note or TODO to track this migration.

 // Legacy exports for backwards compatibility (dark mode defaults)
-// These will be replaced by useUserGuideTheme hook usage
+// `@deprecated` Use useUserGuideTheme hook instead for theme-aware styling
+// TODO: Remove these exports once all components migrate to the hook
 export const colors = getColors("dark");
client/src/Components/v1/UserGuide/SidebarHeader.css (2)

7-15: Consider using CSS custom properties instead of hardcoded colors.

The hardcoded color values (#1570EF, #0d0d0d) duplicate values from theme.js. Using CSS custom properties would ensure consistency and support theme switching.

♻️ Suggested approach

Define CSS custom properties at the root (or component level) and reference them:

.user-guide-header-icon-button:hover {
  background-color: var(--user-guide-hover-bg, rgba(255, 255, 255, 0.1)) !important;
  color: var(--user-guide-brand-primary, `#1570EF`) !important;
}

.user-guide-header-icon-button.active {
  background-color: var(--user-guide-bg-alt, `#0d0d0d`);
  color: var(--user-guide-brand-primary, `#1570EF`);
}

Then set these variables from JavaScript based on the current theme mode.


7-10: !important may conflict with inline styles.

The !important declarations are likely needed to override the inline styles set in SidebarHeader.jsx. Consider moving more styles to CSS to reduce the need for !important, which can make debugging harder.

client/src/Components/v1/UserGuide/SidebarHeader.jsx (1)

86-161: Consider extracting repeated button styles into a shared style object.

The navigation buttons (Back, Forward, Home) share identical styling. Extract to reduce duplication:

♻️ Suggested refactor
const iconButtonStyle = (enabled) => ({
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  width: 28,
  height: 28,
  backgroundColor: "transparent",
  border: "none",
  borderRadius: border.radius,
  cursor: enabled ? "pointer" : "default",
  color: enabled ? colors.text.secondary : colors.border.default,
  flexShrink: 0,
  opacity: enabled ? 1 : 0.5,
});

// Usage:
<button
  onClick={onBack}
  disabled={!canGoBack}
  className="user-guide-header-icon-button"
  style={iconButtonStyle(canGoBack)}
  title={t("userGuide.navigation.back")}
>
client/src/Components/v1/UserGuide/ContentRenderer.css (1)

4-6: Remove empty CSS rule.

This rule block is empty with only a comment. If inline styles handle these properties, the rule can be removed entirely.

Suggested fix
 /* 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 */
client/src/Components/v1/UserGuide/CollectionPage.jsx (1)

16-25: Duplicate iconMap definition.

This iconMap is nearly identical to the one in ArticlePage.jsx (lines 17-26). Consider extracting it to a shared module (e.g., utils/iconMap.js) to avoid duplication and ensure consistency.

client/src/Components/v1/UserGuide/ContentRenderer.jsx (4)

279-281: Redundant ternary expression.

Both branches produce the same result. Simplify to just the expression.

Suggested fix
 				<span style={{ color: colors.text.secondary }}>
-					{item.bold ? parseMarkdown(item.text) : parseMarkdown(item.text)}
+					{parseMarkdown(item.text)}
 				</span>

311-312: Same redundant ternary pattern.

Apply the same simplification here.

Suggested fix
 				<span style={{ color: colors.text.secondary }}>
-					{item.bold ? parseMarkdown(item.text) : parseMarkdown(item.text)}
+					{parseMarkdown(item.text)}
 				</span>

596-604: Hardcoded color bypasses theme.

The code block text color #E5E7EB is hardcoded, which won't adapt to theme changes. Consider using a theme token for consistency.

Suggested fix
 			<pre
 				style={{
 					fontSize: typography.fontSize.sm,
 					fontFamily: typography.fontFamily.mono,
-					color: "#E5E7EB",
+					color: isDark ? "#E5E7EB" : colors.text.primary,
 					margin: 0,
 					whiteSpace: "pre-wrap",
 					wordBreak: "break-all",
 				}}
 			>

29-51: Third instance of iconMap duplication.

This is the third file with a similar iconMap definition (also in CollectionPage.jsx and ArticlePage.jsx). Extract to a shared utility module to maintain a single source of truth.

client/src/Components/v1/UserGuide/SidebarWrapper.jsx (2)

92-94: Silent error handling may hide issues.

Consider logging the parse error in development to aid debugging.

Suggested fix
 			} catch (e) {
-				// Ignore parse errors
+				console.warn('Failed to parse saved sidebar state:', e);
 			}

212-219: Hardcoded external URL.

Consider moving this URL to a configuration file or environment variable for easier maintenance.

Suggested fix
+// At top of file or in a config module
+const GITBOOK_URL = "https://bluewavelabs.gitbook.io/checkmate";
+
 const handleOpenInNewTab = () => {
-	let path = "https://bluewavelabs.gitbook.io/checkmate";
 	if (onOpenInNewTab) {
 		onOpenInNewTab();
 	} else {
-		window.open(path, "_blank", "noopener,noreferrer");
+		window.open(GITBOOK_URL, "_blank", "noopener,noreferrer");
 	}
 };

Comment on lines +157 to +158
In this article
</span>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hardcoded user-facing strings require internationalization.

Per coding guidelines, the following strings should use the translation function t():

  • Line 157: "In this article"
  • Line 225: "Article content coming soon."
  • Line 262: "Previous"
  • Line 300: "Next"
Example fixes
-								In this article
+								{t('userGuide.inThisArticle')}
-									Article content coming soon.
+									{t('userGuide.contentComingSoon')}
-											Previous
+											{t('common.previous')}
-											Next
+											{t('common.next')}
🤖 Prompt for AI Agents
In `@client/src/Components/v1/UserGuide/ArticlePage.jsx` around lines 157 - 158,
The JSX in ArticlePage.jsx contains hardcoded user-facing strings that must be
localized; update the ArticlePage component to replace the literal texts ("In
this article", "Article content coming soon.", "Previous", "Next") with the i18n
translation function t(...) (or useTranslation hook) and ensure t is
imported/available in the component, then wrap each literal in t calls (e.g.,
the span rendering "In this article" and the elements rendering the article
placeholder and pagination labels) so the UI uses translated strings.

marginTop: spacing.sm,
}}
>
{collection.articleCount} articles in this collection
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hardcoded user-facing string.

Per coding guidelines, all user-facing strings must use the translation function t('your.key'). This string should be internationalized.

Suggested fix
-								{collection.articleCount} articles in this collection
+								{t('userGuide.articlesInCollection', { count: collection.articleCount })}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{collection.articleCount} articles in this collection
{t('userGuide.articlesInCollection', { count: collection.articleCount })}
🤖 Prompt for AI Agents
In `@client/src/Components/v1/UserGuide/CollectionPage.jsx` at line 82, The
hardcoded string "{collection.articleCount} articles in this collection" in
CollectionPage.jsx must be internationalized; replace it with a call to the
translation function (e.g., use t('collection.articleCount', { count:
collection.articleCount }) or an appropriate key) and ensure the component
imports/receives the t function (from react-i18next or project i18n) so the
number is passed as an interpolation/pluralization parameter; update the
translation key in your locale files accordingly.

Comment on lines 1 to 117
// 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",
},
],
},
],
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Documentation content lacks internationalization support.

Similar to userGuideConfig.js, all article content (headings, paragraphs, list items, callouts, etc.) is hardcoded in English. For a fully internationalized app, this content should be translatable.

Consider one of these approaches:

  1. Use translation keys and resolve them in ContentRenderer
  2. Organize content by locale (e.g., articleContents.en.js, articleContents.de.js)
  3. Use a CMS or external content source that supports multiple languages

This is a significant undertaking given the volume of content (~49 articles), so it may be acceptable to defer to a future iteration.

As per coding guidelines, user-facing strings should use the translation function.

🤖 Prompt for AI Agents
In `@client/src/Components/v1/UserGuide/content/index.js` around lines 1 - 117,
The articleContents object currently hardcodes all user-facing strings
(headings, paragraphs, list items, etc.) which breaks internationalization;
update the content to use translation keys and the app's translation function
(or per-locale content files) so strings are resolved at render time (e.g.,
replace literal texts inside articleContents with keys like
"getting-started.what-is-checkmate.overview" and call i18n/t or ContentRenderer
to resolve them), or split articleContents into locale-specific modules (e.g.,
articleContents.en.js/articleContents.de.js) and load by locale; locate the
articleContents constant in this file and adjust the consumers (ContentRenderer
or any component that renders these blocks) to call the translation function for
block.text, item.title, item.description, list items, and other user-facing
fields instead of embedding English literals.

Comment on lines +31 to +64
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"],
},
],
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Hardcoded UI strings should use translation keys.

All user-facing strings like title and description in the collections and articles are hardcoded in English. Per coding guidelines, these should use translation function t('your.key') for internationalization support.

Consider restructuring to use translation keys:

 {
   id: "getting-started",
-  title: "Getting started",
-  description: "Essential first steps for new users.",
+  titleKey: "userGuide.gettingStarted.title",
+  descriptionKey: "userGuide.gettingStarted.description",
   icon: "Rocket",
   ...
 }

Then resolve the translations in the consuming component using t(collection.titleKey).

As per coding guidelines, all user-facing strings must use the translation function.

🤖 Prompt for AI Agents
In `@client/src/Components/v1/UserGuide/content/userGuideConfig.js` around lines
31 - 64, Replace hardcoded user-facing strings in the exported collections array
(the collections constant and its nested article objects) with translation keys:
add fields like titleKey and descriptionKey on each collection and article
instead of using literal title/description values (and optionally convert
keywords to keywordKey if they need translation), and update callers to resolve
them with the i18n function (e.g., t(collection.titleKey) /
t(article.descriptionKey)); ensure the exported structure still provides IDs and
articleCount but uses keys for all UI text so the consuming component (which
currently reads collection.title and article.title) can switch to calling t(...)
on the new titleKey/descriptionKey fields.

Comment on lines 17 to 119
<h2
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
marginBottom: spacing.sm,
marginTop: 0,
}}
>
Contact us
</h2>
<p
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
color: colors.text.secondary,
lineHeight: typography.lineHeight.normal,
margin: 0,
marginBottom: spacing.md,
}}
>
Can't find what you need? Our support team is here to help.
</p>
<a
href="https://discord.com/invite/NAb6H3UTjK"
target="_blank"
rel="noopener noreferrer"
className="user-guide-help-button"
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: spacing.sm,
width: "100%",
padding: `${spacing.md} ${spacing.lg}`,
backgroundColor: colors.background.white,
border: border.default,
borderRadius: border.radius,
textDecoration: "none",
cursor: "pointer",
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
fontWeight: typography.fontWeight.medium,
color: colors.text.primary,
}}
>
Join our Discord
<ExternalLink size={14} strokeWidth={1.5} color={colors.text.muted} />
</a>
</div>

{/* Ask the community */}
<div>
<h2
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
marginBottom: spacing.sm,
marginTop: 0,
}}
>
GitHub discussions
</h2>
<p
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
color: colors.text.secondary,
lineHeight: typography.lineHeight.normal,
margin: 0,
marginBottom: spacing.md,
}}
>
Questions about features, how-tos, or use cases? Join the community discussion.
</p>
<a
href="https://github.com/bluewave-labs/checkmate/discussions"
target="_blank"
rel="noopener noreferrer"
className="user-guide-help-button"
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: spacing.sm,
width: "100%",
padding: `${spacing.md} ${spacing.lg}`,
backgroundColor: colors.background.white,
border: border.default,
borderRadius: border.radius,
textDecoration: "none",
cursor: "pointer",
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
fontWeight: typography.fontWeight.medium,
color: colors.text.primary,
}}
>
GitHub discussions
<ExternalLink size={14} strokeWidth={1.5} color={colors.text.muted} />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace hardcoded copy with translations.

Headings, body text, and CTA labels should use t(...).

🔧 Suggested change
-					Contact us
+					{t("userGuide.help.contactTitle")}
@@
-					Can't find what you need? Our support team is here to help.
+					{t("userGuide.help.contactBody")}
@@
-					Join our Discord
+					{t("userGuide.help.discordCta")}
@@
-					GitHub discussions
+					{t("userGuide.help.githubTitle")}
@@
-					Questions about features, how-tos, or use cases? Join the community discussion.
+					{t("userGuide.help.githubBody")}
@@
-					GitHub discussions
+					{t("userGuide.help.githubCta")}

As per coding guidelines, all user-facing strings must use t("...").

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<h2
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
marginBottom: spacing.sm,
marginTop: 0,
}}
>
Contact us
</h2>
<p
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
color: colors.text.secondary,
lineHeight: typography.lineHeight.normal,
margin: 0,
marginBottom: spacing.md,
}}
>
Can't find what you need? Our support team is here to help.
</p>
<a
href="https://discord.com/invite/NAb6H3UTjK"
target="_blank"
rel="noopener noreferrer"
className="user-guide-help-button"
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: spacing.sm,
width: "100%",
padding: `${spacing.md} ${spacing.lg}`,
backgroundColor: colors.background.white,
border: border.default,
borderRadius: border.radius,
textDecoration: "none",
cursor: "pointer",
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
fontWeight: typography.fontWeight.medium,
color: colors.text.primary,
}}
>
Join our Discord
<ExternalLink size={14} strokeWidth={1.5} color={colors.text.muted} />
</a>
</div>
{/* Ask the community */}
<div>
<h2
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
marginBottom: spacing.sm,
marginTop: 0,
}}
>
GitHub discussions
</h2>
<p
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
color: colors.text.secondary,
lineHeight: typography.lineHeight.normal,
margin: 0,
marginBottom: spacing.md,
}}
>
Questions about features, how-tos, or use cases? Join the community discussion.
</p>
<a
href="https://github.com/bluewave-labs/checkmate/discussions"
target="_blank"
rel="noopener noreferrer"
className="user-guide-help-button"
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: spacing.sm,
width: "100%",
padding: `${spacing.md} ${spacing.lg}`,
backgroundColor: colors.background.white,
border: border.default,
borderRadius: border.radius,
textDecoration: "none",
cursor: "pointer",
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
fontWeight: typography.fontWeight.medium,
color: colors.text.primary,
}}
>
GitHub discussions
<ExternalLink size={14} strokeWidth={1.5} color={colors.text.muted} />
<h2
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
marginBottom: spacing.sm,
marginTop: 0,
}}
>
{t("userGuide.help.contactTitle")}
</h2>
<p
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
color: colors.text.secondary,
lineHeight: typography.lineHeight.normal,
margin: 0,
marginBottom: spacing.md,
}}
>
{t("userGuide.help.contactBody")}
</p>
<a
href="https://discord.com/invite/NAb6H3UTjK"
target="_blank"
rel="noopener noreferrer"
className="user-guide-help-button"
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: spacing.sm,
width: "100%",
padding: `${spacing.md} ${spacing.lg}`,
backgroundColor: colors.background.white,
border: border.default,
borderRadius: border.radius,
textDecoration: "none",
cursor: "pointer",
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
fontWeight: typography.fontWeight.medium,
color: colors.text.primary,
}}
>
{t("userGuide.help.discordCta")}
<ExternalLink size={14} strokeWidth={1.5} color={colors.text.muted} />
</a>
</div>
{/* Ask the community */}
<div>
<h2
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
marginBottom: spacing.sm,
marginTop: 0,
}}
>
{t("userGuide.help.githubTitle")}
</h2>
<p
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
color: colors.text.secondary,
lineHeight: typography.lineHeight.normal,
margin: 0,
marginBottom: spacing.md,
}}
>
{t("userGuide.help.githubBody")}
</p>
<a
href="https://github.com/bluewave-labs/checkmate/discussions"
target="_blank"
rel="noopener noreferrer"
className="user-guide-help-button"
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
gap: spacing.sm,
width: "100%",
padding: `${spacing.md} ${spacing.lg}`,
backgroundColor: colors.background.white,
border: border.default,
borderRadius: border.radius,
textDecoration: "none",
cursor: "pointer",
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
fontWeight: typography.fontWeight.medium,
color: colors.text.primary,
}}
>
{t("userGuide.help.githubCta")}
<ExternalLink size={14} strokeWidth={1.5} color={colors.text.muted} />
🤖 Prompt for AI Agents
In `@client/src/Components/v1/UserGuide/HelpSection.jsx` around lines 17 - 119,
Several user-facing strings in HelpSection.jsx (e.g., the "Contact us" and
"GitHub discussions" headings, their paragraph bodies like "Can't find what you
need? Our support team is here to help." and "Questions about features, how-tos,
or use cases? Join the community discussion.", and CTA labels "Join our Discord"
and "GitHub discussions") are hardcoded; wrap each visible string in the i18n
translation function t(...) and ensure the component imports/uses the
translation hook (e.g., useTranslation -> t) so headings, paragraphs, and anchor
text use t("...") instead of literal strings; update any inline aria/alt/text
variants the same way and keep the ExternalLink usage and
className="user-guide-help-button" intact.

Comment on lines +7 to +45
<div
onClick={onClick}
className={`user-guide-tab-item ${isActive ? "active" : ""}`}
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: "16px 8px",
cursor: "pointer",
backgroundColor: isActive ? colors.background.white : "transparent",
borderLeft: isActive
? `2px solid ${colors.brand.primary}`
: "2px solid transparent",
borderBottom: `1px solid ${colors.border.default}`,
}}
>
<div
style={{
color: isActive ? colors.brand.primary : colors.text.muted,
marginBottom: 8,
}}
>
{icon}
</div>
<span
style={{
writingMode: "vertical-rl",
textOrientation: "mixed",
fontFamily: typography.fontFamily.sans,
fontSize: 12,
fontWeight: typography.fontWeight.normal,
color: isActive ? colors.brand.primary : colors.text.muted,
letterSpacing: "0.5px",
}}
>
{label}
</span>
</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make tab items keyboard-accessible.

div with onClick is not focusable or operable via keyboard. Use a semantic button (or add role/tabIndex and key handlers) to avoid an accessibility blocker.

🔧 Suggested change
-		<div
-			onClick={onClick}
-			className={`user-guide-tab-item ${isActive ? "active" : ""}`}
+		<button
+			type="button"
+			onClick={onClick}
+			aria-pressed={isActive}
+			className={`user-guide-tab-item ${isActive ? "active" : ""}`}
 			style={{
 				display: "flex",
 				flexDirection: "column",
 				alignItems: "center",
 				justifyContent: "center",
 				padding: "16px 8px",
 				cursor: "pointer",
+				border: "none",
 				backgroundColor: isActive ? colors.background.white : "transparent",
 				borderLeft: isActive
 					? `2px solid ${colors.brand.primary}`
 					: "2px solid transparent",
 				borderBottom: `1px solid ${colors.border.default}`,
 			}}
 		>
@@
-		</div>
+		</button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div
onClick={onClick}
className={`user-guide-tab-item ${isActive ? "active" : ""}`}
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: "16px 8px",
cursor: "pointer",
backgroundColor: isActive ? colors.background.white : "transparent",
borderLeft: isActive
? `2px solid ${colors.brand.primary}`
: "2px solid transparent",
borderBottom: `1px solid ${colors.border.default}`,
}}
>
<div
style={{
color: isActive ? colors.brand.primary : colors.text.muted,
marginBottom: 8,
}}
>
{icon}
</div>
<span
style={{
writingMode: "vertical-rl",
textOrientation: "mixed",
fontFamily: typography.fontFamily.sans,
fontSize: 12,
fontWeight: typography.fontWeight.normal,
color: isActive ? colors.brand.primary : colors.text.muted,
letterSpacing: "0.5px",
}}
>
{label}
</span>
</div>
<button
type="button"
onClick={onClick}
aria-pressed={isActive}
className={`user-guide-tab-item ${isActive ? "active" : ""}`}
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: "16px 8px",
cursor: "pointer",
border: "none",
backgroundColor: isActive ? colors.background.white : "transparent",
borderLeft: isActive
? `2px solid ${colors.brand.primary}`
: "2px solid transparent",
borderBottom: `1px solid ${colors.border.default}`,
}}
>
<div
style={{
color: isActive ? colors.brand.primary : colors.text.muted,
marginBottom: 8,
}}
>
{icon}
</div>
<span
style={{
writingMode: "vertical-rl",
textOrientation: "mixed",
fontFamily: typography.fontFamily.sans,
fontSize: 12,
fontWeight: typography.fontWeight.normal,
color: isActive ? colors.brand.primary : colors.text.muted,
letterSpacing: "0.5px",
}}
>
{label}
</span>
</button>
🤖 Prompt for AI Agents
In `@client/src/Components/v1/UserGuide/TabBar.jsx` around lines 7 - 45, The tab
item currently rendered as a non-focusable <div> with onClick must be made
keyboard-accessible: replace the outer div that uses onClick/isActive/icon/label
with a semantic <button type="button"> (or if you must keep a div, add
role="button" tabIndex={0} and a onKeyDown handler that triggers onClick on
Enter/Space) and ensure to preserve the existing className and inline style
logic; also add an appropriate ARIA state such as aria-pressed or aria-selected
when isActive to communicate selection to assistive tech.

Comment on lines 64 to 81
<TabItem
id="user-guide"
label="User guide"
icon={<BookOpen size={18} strokeWidth={1.5} />}
isActive={activeTab === "user-guide"}
onClick={() => onTabChange("user-guide")}
colors={colors}
typography={typography}
/>
<TabItem
id="help"
label="Help"
icon={<HelpCircle size={18} strokeWidth={1.5} />}
isActive={activeTab === "help"}
onClick={() => onTabChange("help")}
colors={colors}
typography={typography}
/>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Localize tab labels with t(...).

These user-facing strings are hardcoded; please route them through the translation function.

🔧 Suggested change
-				label="User guide"
+				label={t("userGuide.tabs.userGuide")}
@@
-				label="Help"
+				label={t("userGuide.tabs.help")}

As per coding guidelines, all user-facing strings must use t("...").

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<TabItem
id="user-guide"
label="User guide"
icon={<BookOpen size={18} strokeWidth={1.5} />}
isActive={activeTab === "user-guide"}
onClick={() => onTabChange("user-guide")}
colors={colors}
typography={typography}
/>
<TabItem
id="help"
label="Help"
icon={<HelpCircle size={18} strokeWidth={1.5} />}
isActive={activeTab === "help"}
onClick={() => onTabChange("help")}
colors={colors}
typography={typography}
/>
<TabItem
id="user-guide"
label={t("userGuide.tabs.userGuide")}
icon={<BookOpen size={18} strokeWidth={1.5} />}
isActive={activeTab === "user-guide"}
onClick={() => onTabChange("user-guide")}
colors={colors}
typography={typography}
/>
<TabItem
id="help"
label={t("userGuide.tabs.help")}
icon={<HelpCircle size={18} strokeWidth={1.5} />}
isActive={activeTab === "help"}
onClick={() => onTabChange("help")}
colors={colors}
typography={typography}
/>
🤖 Prompt for AI Agents
In `@client/src/Components/v1/UserGuide/TabBar.jsx` around lines 64 - 81, The
TabItem labels "User guide" and "Help" are hardcoded in TabBar.jsx; update them
to use the translation function by replacing the label props with
t("user_guide") and t("help") (or the appropriate translation keys) so TabItem
calls use label={t("...")}; locate the TabItem components in the TabBar
component (props: activeTab, onTabChange, colors, typography) and ensure the
translation function t is imported/available in that module before using it.

Comment on lines +47 to +200
<h2
style={{
fontFamily: typography.fontFamily.sans,
fontSize: isInApp ? typography.fontSize.lg : typography.fontSize.xl,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
marginBottom: isInApp ? spacing.md : spacing.xl,
marginTop: 0,
}}
>
Browse by topic
</h2>

<div
style={{
display: "grid",
gridTemplateColumns: isInApp
? "1fr"
: "repeat(auto-fill, minmax(340px, 1fr))",
gap: isInApp ? spacing.md : spacing.lg,
marginBottom: isInApp ? spacing.xl : spacing["4xl"],
}}
>
{collections.map((collection) => {
const IconComponent = iconMap[collection.icon] || Rocket;
return (
<div
key={collection.id}
onClick={() => onNavigate(collection.id)}
className="user-guide-collection-card"
style={{
backgroundColor: colors.background.white,
border: border.default,
borderRadius: border.radius,
padding: spacing.lg,
cursor: "pointer",
}}
>
<div
style={{ display: "flex", alignItems: "flex-start", gap: spacing.md }}
>
<IconComponent
size={20}
strokeWidth={1.5}
color={colors.brand.primary}
/>
<div style={{ flex: 1 }}>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginBottom: spacing.xs,
}}
>
<span
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
}}
>
{collection.title}
</span>
<span
style={{
fontSize: typography.fontSize.xs,
color: colors.text.secondary,
backgroundColor: colors.background.alt,
padding: "2px 6px",
borderRadius: border.radius,
}}
>
{collection.articleCount} articles
</span>
</div>
<span
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
color: colors.text.secondary,
lineHeight: typography.lineHeight.normal,
display: "block",
}}
>
{collection.description}
</span>
</div>
</div>
</div>
);
})}
</div>

{/* About our docs - Only shown in in-app mode */}
{isInApp && (
<div
style={{
backgroundColor: colors.background.white,
border: border.default,
borderRadius: border.radius,
padding: spacing.lg,
}}
>
<h3
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
marginBottom: spacing.md,
marginTop: 0,
}}
>
About our docs
</h3>
<p
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.sm,
color: colors.text.secondary,
lineHeight: typography.lineHeight.normal,
margin: 0,
marginBottom: spacing.md,
}}
>
There are a few ways to explore our docs:
</p>

<div style={{ marginBottom: spacing.md }}>
<h4
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.sm,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
marginBottom: spacing.xs,
marginTop: 0,
}}
>
In the product
</h4>
<p
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.sm,
color: colors.text.secondary,
lineHeight: typography.lineHeight.normal,
margin: 0,
}}
>
Look for help icons and tooltips throughout the interface.
</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Localize static copy and pluralization.

Strings like headings, helper text, and “{count} articles” should use t(...) with pluralization/interpolation.

🔧 Suggested change
-					Browse by topic
+					{t("userGuide.landing.browseByTopic")}
@@
-												{collection.articleCount} articles
+												{t("userGuide.landing.articlesCount", {
+													count: collection.articleCount,
+												})}
@@
-							About our docs
+							{t("userGuide.landing.aboutTitle")}
@@
-							There are a few ways to explore our docs:
+							{t("userGuide.landing.aboutIntro")}
@@
-								In the product
+								{t("userGuide.landing.inProductTitle")}
@@
-								Look for help icons and tooltips throughout the interface.
+								{t("userGuide.landing.inProductBody")}

As per coding guidelines, all user-facing strings must use t("...").

🤖 Prompt for AI Agents
In `@client/src/Components/v1/UserGuide/UserGuideLanding.jsx` around lines 47 -
200, Replace hard-coded user-facing strings in the UserGuideLanding component
with i18n calls: import/use the translation hook (e.g., t) and wrap headings
("Browse by topic", "About our docs", "There are a few ways to explore our
docs:", "In the product", helper text "Look for help icons and tooltips
throughout the interface.") and collection text (collection.title,
collection.description) with t(...), and change the article count badge to use
pluralization/interpolation (e.g., t('articles', { count:
collection.articleCount })) so the JSX inside the collections.map block and the
in-app info card all use t keys; add appropriate keys (e.g., collections.title,
collections.description, collections.articles) to your locale files and ensure
plural forms are defined.

Comment on lines +73 to +137
<div
key={collection.id}
onClick={() => onNavigate(collection.id)}
className="user-guide-collection-card"
style={{
backgroundColor: colors.background.white,
border: border.default,
borderRadius: border.radius,
padding: spacing.lg,
cursor: "pointer",
}}
>
<div
style={{ display: "flex", alignItems: "flex-start", gap: spacing.md }}
>
<IconComponent
size={20}
strokeWidth={1.5}
color={colors.brand.primary}
/>
<div style={{ flex: 1 }}>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginBottom: spacing.xs,
}}
>
<span
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
}}
>
{collection.title}
</span>
<span
style={{
fontSize: typography.fontSize.xs,
color: colors.text.secondary,
backgroundColor: colors.background.alt,
padding: "2px 6px",
borderRadius: border.radius,
}}
>
{collection.articleCount} articles
</span>
</div>
<span
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
color: colors.text.secondary,
lineHeight: typography.lineHeight.normal,
display: "block",
}}
>
{collection.description}
</span>
</div>
</div>
</div>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make collection cards keyboard-accessible.

Clickable divs aren’t focusable or operable via keyboard. Use a semantic button or link to avoid an accessibility blocker.

🔧 Suggested change
-							<div
+							<button
+								type="button"
 								key={collection.id}
 								onClick={() => onNavigate(collection.id)}
 								className="user-guide-collection-card"
 								style={{
 									backgroundColor: colors.background.white,
 									border: border.default,
 									borderRadius: border.radius,
 									padding: spacing.lg,
 									cursor: "pointer",
+									width: "100%",
 								}}
 							>
@@
-							</div>
+							</button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div
key={collection.id}
onClick={() => onNavigate(collection.id)}
className="user-guide-collection-card"
style={{
backgroundColor: colors.background.white,
border: border.default,
borderRadius: border.radius,
padding: spacing.lg,
cursor: "pointer",
}}
>
<div
style={{ display: "flex", alignItems: "flex-start", gap: spacing.md }}
>
<IconComponent
size={20}
strokeWidth={1.5}
color={colors.brand.primary}
/>
<div style={{ flex: 1 }}>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginBottom: spacing.xs,
}}
>
<span
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
}}
>
{collection.title}
</span>
<span
style={{
fontSize: typography.fontSize.xs,
color: colors.text.secondary,
backgroundColor: colors.background.alt,
padding: "2px 6px",
borderRadius: border.radius,
}}
>
{collection.articleCount} articles
</span>
</div>
<span
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
color: colors.text.secondary,
lineHeight: typography.lineHeight.normal,
display: "block",
}}
>
{collection.description}
</span>
</div>
</div>
</div>
<button
type="button"
key={collection.id}
onClick={() => onNavigate(collection.id)}
className="user-guide-collection-card"
style={{
backgroundColor: colors.background.white,
border: border.default,
borderRadius: border.radius,
padding: spacing.lg,
cursor: "pointer",
width: "100%",
}}
>
<div
style={{ display: "flex", alignItems: "flex-start", gap: spacing.md }}
>
<IconComponent
size={20}
strokeWidth={1.5}
color={colors.brand.primary}
/>
<div style={{ flex: 1 }}>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
marginBottom: spacing.xs,
}}
>
<span
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
fontWeight: typography.fontWeight.semibold,
color: colors.text.primary,
}}
>
{collection.title}
</span>
<span
style={{
fontSize: typography.fontSize.xs,
color: colors.text.secondary,
backgroundColor: colors.background.alt,
padding: "2px 6px",
borderRadius: border.radius,
}}
>
{collection.articleCount} articles
</span>
</div>
<span
style={{
fontFamily: typography.fontFamily.sans,
fontSize: typography.fontSize.base,
color: colors.text.secondary,
lineHeight: typography.lineHeight.normal,
display: "block",
}}
>
{collection.description}
</span>
</div>
</div>
</button>
🤖 Prompt for AI Agents
In `@client/src/Components/v1/UserGuide/UserGuideLanding.jsx` around lines 73 -
137, The collection card uses a clickable div which is not keyboard-accessible;
replace the outer <div key={collection.id}> with a semantic interactive element
(preferably a <button type="button> or an <a> if it navigates) and wire the
existing onClick={() => onNavigate(collection.id)} to that element so keyboard
activation works, preserve styling currently applied to the div, and ensure the
element has an accessible name (e.g., aria-label or use the visible
collection.title) and proper focus styles; if you must keep a non-semantic
element, add tabIndex={0} and handle onKeyDown to call onNavigate(collection.id)
for Enter/Space, but using a <button> is the preferred fix.

Comment on lines +14 to +18
const [isOpen, setIsOpen] = useState(() => {
if (typeof window === "undefined") return false;
const saved = localStorage.getItem(SIDEBAR_STATE_KEY);
return saved === "true";
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard localStorage access to avoid runtime crashes.

localStorage can throw in private mode or restricted environments, and the initializer runs during render. Please wrap read/write in try/catch with safe fallbacks.

🛠️ Proposed fix
-	const [isOpen, setIsOpen] = useState(() => {
-		if (typeof window === "undefined") return false;
-		const saved = localStorage.getItem(SIDEBAR_STATE_KEY);
-		return saved === "true";
-	});
+	const [isOpen, setIsOpen] = useState(() => {
+		if (typeof window === "undefined") return false;
+		try {
+			const saved = localStorage.getItem(SIDEBAR_STATE_KEY);
+			return saved === "true";
+		} catch {
+			return false;
+		}
+	});
@@
-	useEffect(() => {
-		if (typeof window === "undefined") return;
-		localStorage.setItem(SIDEBAR_STATE_KEY, String(isOpen));
-	}, [isOpen]);
+	useEffect(() => {
+		if (typeof window === "undefined") return;
+		try {
+			localStorage.setItem(SIDEBAR_STATE_KEY, String(isOpen));
+		} catch {
+			// noop: storage unavailable
+		}
+	}, [isOpen]);

Also applies to: 52-55

🤖 Prompt for AI Agents
In `@client/src/Components/v1/UserGuide/UserGuideSidebarContext.jsx` around lines
14 - 18, The initializer for isOpen and the places that read/write
SIDEBAR_STATE_KEY should guard against localStorage throwing by wrapping all
localStorage accesses in try/catch and using safe fallbacks; specifically,
update the useState initializer that reads
localStorage.getItem(SIDEBAR_STATE_KEY) to catch errors and return false if
access fails, and update the code paths that call
localStorage.setItem/removeItem (the setter logic around setIsOpen and any
effect or handler at lines ~52-55) to perform writes inside try/catch and
silently ignore or fallback when storage is unavailable. Ensure you reference
SIDEBAR_STATE_KEY, isOpen and setIsOpen when making these changes so behavior
remains the same when localStorage is available.

Copy link

@llamapreview llamapreview bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Code Review by LlamaPReview

🎯 TL;DR & Recommendation

Recommendation: Approve with suggestions.
This PR adds a comprehensive in-app user guide with minor performance and robustness suggestions.

📄 Documentation Diagram

This diagram illustrates the new user guide sidebar interaction flow from opening to content navigation.

sequenceDiagram
    participant U as User
    participant SW as SidebarWrapper
    participant CTX as UserGuideContext
    participant CR as ContentRenderer
    U->>SW: Open sidebar
    SW->>CTX: Update open state
    CTX->>SW: State updated
    SW->>U: Render sidebar UI
    U->>SW: Navigate to article
    SW->>CR: Load article content
    CR->>U: Display content
    note over SW,CR: PR #35;3148 added user guide documentation system
    U->>SW: Close sidebar
    SW->>CTX: Update closed state
Loading

🌟 Strengths

  • Solid implementation of a feature-rich documentation sidebar with search, theming, and persistent state.
  • Well-structured component separation and clean integration into the existing app layout.
Priority File Category Impact Summary (≤12 words) Anchors
P2 UserGuide/ArticlePage.jsx Performance Redundant scroll listeners may cause double execution and minor performance hit.
P2 UserGuide/SidebarWrapper.jsx Robustness localStorage without error handling could break state persistence in edge cases.
P2 UserGuide/ContentRenderer.jsx Security Custom markdown parser vulnerable to ReDoS if exposed to malicious input.
P2 UserGuide/UserGuideSidebarContext.jsx Architecture Global sidebar state introduces architectural coupling with layout CSS. path:App.jsx, path:HomeLayout/index.jsx
P2 UserGuide/index.jsx Maintainability Large new module requires careful maintenance and clear architectural boundaries. path:App.jsx, path:HomeLayout/index.jsx

💡 Have feedback? We'd love to hear it in our GitHub Discussions.
✨ This review was generated by LlamaPReview Advanced, which is free for all open-source projects. Learn more.

Comment on lines +76 to +77
window.addEventListener("scroll", handleScroll, { passive: true });
document.addEventListener("scroll", handleScroll, { passive: true, capture: true });
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 | Confidence: High

Speculative: The scroll event listener is attached to both window and document with the same handler. This is redundant and may cause the handler to be called twice for a single scroll event, leading to unnecessary function executions. Additionally, using capture: true on the document listener is unnecessary for scroll events and adds complexity. This could cause minor performance degradation, especially on low-end devices or with many active articles.

Suggested change
window.addEventListener("scroll", handleScroll, { passive: true });
document.addEventListener("scroll", handleScroll, { passive: true, capture: true });
window.addEventListener("scroll", handleScroll, { passive: true });

Comment on lines +14 to +18
const [isOpen, setIsOpen] = useState(() => {
if (typeof window === "undefined") return false;
const saved = localStorage.getItem(SIDEBAR_STATE_KEY);
return saved === "true";
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 | Confidence: High

The sidebar state is persisted in localStorage and managed via React context provided at the app's root. The related_context shows integration in src/App.jsx (UserGuideSidebarProvider) and src/Components/v1/Layouts/HomeLayout/index.jsx (UserGuideSidebar). This creates a global, persistent UI state. The architectural impact is significant: it introduces a fixed-position, resizable sidebar that manipulates global CSS variables (--help-sidebar-padding) and body classes. This design could conflict with other global layout manipulations in the codebase. The ripple effect is that any component relying on the home-content-wrapper class (which gets a dynamic right margin) is now implicitly coupled to the sidebar's state.

Comment on lines +1 to +7
import SidebarWrapper from "./SidebarWrapper";
import {
UserGuideSidebarProvider,
useUserGuideSidebarContext,
TAB_BAR_WIDTH,
DEFAULT_CONTENT_WIDTH,
} from "./UserGuideSidebarContext";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 | Confidence: High

The new UserGuide component directory is a large, self-contained feature module (10+ files, ~5000 lines). This is generally good for separation of concerns. However, the index.jsx exports both a default component (UserGuideSidebar) and named exports (provider, context hook, constants). The related_context shows the provider is added to src/App.jsx and the component to HomeLayout/index.jsx. This creates a clear dependency chain. The maintainability concern is the sheer size and complexity of the module introduced in a single PR. Future changes to documentation structure, theming, or sidebar behavior will be concentrated here. Ensure there are clear boundaries between the presentation layer (components), content layer (content/), and state layer (context) as the feature evolves.


// Restore state from localStorage on mount
useEffect(() => {
const savedState = localStorage.getItem(STORAGE_KEY);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 | Confidence: Medium

Speculative: The code uses localStorage without error handling. In environments where localStorage is unavailable (e.g., some privacy modes, strict CSP) or throws errors (e.g., quota exceeded), these uncaught exceptions could break the sidebar's state persistence and navigation history. This could lead to a degraded user experience where the sidebar fails to remember its open/closed state or navigation position.

Suggested change
const savedState = localStorage.getItem(STORAGE_KEY);
let savedState = null;
try {
savedState = localStorage.getItem(STORAGE_KEY);
} catch (error) {
console.warn('Failed to read from localStorage:', error);
}
// Proceed with savedState, which may be null

let key = 0;

// Supports: **bold**, `code`, [[link text]](collection/article)
const combinedRegex = /\*\*([^*]+)\*\*|`([^`]+)`|\[\[([^\]]+)\]\]\(([^)]+)\)/g;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 | Confidence: Medium

Speculative: The custom markdown parser uses a regular expression to identify and transform text patterns (bold, inline code, article links). This approach is vulnerable to ReDoS (Regular Expression Denial of Service) if a maliciously crafted article content contains nested or malformed patterns (e.g., many consecutive asterisks). While the risk is low as content is controlled internally, it's a potential attack vector if the content ingestion pipeline is ever exposed to user-generated input. A more robust parser or a sanitization library should be considered.

Suggested change
const combinedRegex = /\*\*([^*]+)\*\*|`([^`]+)`|\[\[([^\]]+)\]\]\(([^)]+)\)/g;
const MAX_INLINE_LENGTH = 1000; // Define a reasonable limit
const parseMarkdown = (text) => {
if (text.length > MAX_INLINE_LENGTH) {
// Truncate or return plain text for safety
return text.substring(0, MAX_INLINE_LENGTH);
}
// ... existing parsing logic
};

Copy link
Collaborator

@ajhollid ajhollid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't had a chance to do a full review yet, but we should not have any css files on the frontend, styling is done by theme and sx props.

This is a massive pr as well, 7k lines changed?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants