Skip to content

Conversation

@yangxiaolang
Copy link
Collaborator

@yangxiaolang yangxiaolang commented Dec 17, 2025

Summary by CodeRabbit

  • New Features

    • Added Card and LinkCard components, a DocLayout, responsive sidebar/outline toggle, and per-link download capability for downloadable links.
  • Style / Theming

    • Large stylesheet overhaul: CSS variables, new UI primitives (cards, footer, breadcrumbs), responsive layout and dark/light readiness, and new doc-layout styles.
  • Chores

    • Dev dependency version bump and TypeScript/project path/include updates; virtual module declarations exported for consumption.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 17, 2025

Walkthrough

Adds Card and LinkCard components, a DocLayout and sidebar hook, large CSS/layout additions, download utilities with MDX anchor download gating, TypeScript path/include updates, multiple import/source reorganizations to @rspress/core/theme-original, and bumps devDependency @alauda/doom to ^1.17.6.

Changes

Cohort / File(s) Change Summary
Dependency
package.json
Bumps devDependency @alauda/doom from ^1.12.7 to ^1.17.6.
Global styles & doc layout CSS
styles/index.css, styles/doc-layout.css
Introduces extensive theming tokens, new component styles (.card, .link-card, .post-meta, .breadcrumb, .home-footer, etc.), responsive/menu/outline rules and a full doc-layout CSS sheet.
New presentational components
theme/components/Card/index.tsx, theme/components/LinkCard/index.tsx
Adds Card and LinkCard React components with typed props and simple rendering of title/content/description.
Home & minor component edits
theme/layout/HomeLayout/index.tsx, theme/components/HomeContent/index.tsx, theme/components/HomeBanner/index.tsx, theme/components/DocID/index.tsx, theme/components/BreadCrumb/index.tsx, theme/components/Pagination/index.tsx, theme/components/EditOnGithub/index.tsx, theme/components/Checkbox/index.tsx, theme/components/Empty/index.tsx, theme/components/PostList/index.tsx
Various adjustments: add explicit React imports, switch some imports to @rspress/core/theme-original, use typed useI18n<typeof import("i18n")>(), replace/rename utility classes to tokenized classes (e.g., post-meta, breadcrumb), remove some pageData usages, and swap Badge/useLocale usages.
Sidebar module & hook
theme/components/SidebarMenu/useSidebarMenu.tsx, theme/components/SidebarMenu/index.ts
Adds useSidebarMenu hook (sidebar/outline states, refs, mobile toggle UI) and re-exports it from the module index.
New layout: DocLayout
theme/layout/DocLayout/index.tsx
Adds DocLayout and DocLayoutProps with conditional sidebar/overview/outline rendering and integrations with TOC and sidebar hook.
Layout MDX anchor & download flow
theme/layout/index.tsx, theme/utils/download.ts
Adds downloadFile, getPathname, shouldDownload; MDX anchor handling now checks shouldDownload and triggers downloadFile for matching pathnames (same-origin fetch→blob or cross-origin anchor fallback).
Exports & runtime re-exports
theme/index.tsx, virtual-modules.d.ts
Re-exports from @rspress/core/theme-original, adds exports for DocLayout, Card, LinkCard; converts virtual module declarations to exported interfaces/consts.
TypeScript config
tsconfig.json
Adds "baseUrl": ".", path alias "i18n""./docs/i18n.json", expands include globs to include plugins/**/*, theme/**/*, and virtual declarations.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User as User (click)
    participant MDX as Layout / MDX anchor handler
    participant Util as download.ts
    participant Net as Network (fetch / browser)
    note right of MDX: Anchor click intercepted\nby MDX anchor handler
    User->>MDX: click anchor (href)
    MDX->>Util: getPathname(href)
    MDX->>Util: shouldDownload(pathname)?
    alt shouldDownload == false
        MDX->>User: allow normal navigation
    else shouldDownload == true
        MDX->>Util: downloadFile(href, filename)
        alt same-origin
            Util->>Net: fetch(normalized URL)
            Net-->>Util: Response (ok/blob)
            Util->>User: create blob URL & programmatic click (download)
            Util->>Net: revoke blob URL (cleanup)
        else cross-origin
            Util->>User: create temp anchor href & programmatic click (download)
            Util->>User: cleanup temp anchor
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • black89757

Poem

🐇 I stitched a card, a breadcrumb, and a footer bright,
A sidebar that toggles and an outline that takes flight,
Downloads that fetch and gently hand you files,
Doom nudged upward across a few versioned miles,
Hops and happy builds—celebrate with small delights!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'chore: upgrade alauda doom' is narrowly focused on upgrading a single dependency (@alauda/doom), but the actual changeset includes substantial refactoring: new React components (Card, LinkCard, DocLayout), layout restructuring, CSS expansions, utility functions, and widespread import path changes. The title does not reflect these major changes. Update the title to reflect the full scope of changes, such as 'refactor: restructure layout components and upgrade dependencies' or 'refactor: add DocLayout and new components with theme reorganization'.
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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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
Contributor

@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: 3

🧹 Nitpick comments (6)
theme/layout/index.tsx (1)

9-9: Consider removing explicit React import.

With React 17+ and the JSX transform configured in tsconfig.json ("jsx": "react-jsx"), the explicit import React from "react" is unnecessary. However, if you prefer to keep it for consistency or compatibility reasons, this is a minor stylistic choice.

theme/components/SidebarMenu/useSidebarMenu.tsx (1)

40-40: Consider extracting the mobile breakpoint as a constant.

The hardcoded 768 breakpoint appears here and might be used elsewhere in the theme. Consider extracting it to a shared constant or configuration file for maintainability.

// Example: theme/constants.ts or similar
export const MOBILE_BREAKPOINT = 768;

// Then import and use:
const isMobile = window.innerWidth <= MOBILE_BREAKPOINT;
theme/layout/HomeLayout/index.tsx (1)

6-6: Consider removing explicit React import.

With the JSX transform configured in tsconfig.json ("jsx": "react-jsx"), the explicit import React from "react" is unnecessary. However, this is a minor stylistic preference.

theme/components/HomeContent/index.tsx (1)

21-21: Optional: Explicit React import may be unnecessary.

Since React 17+, the explicit import React from "react" is not required for JSX transformation. The file already imports React hooks on Line 3, which is sufficient.

If you prefer to keep imports minimal, you can remove this line:

-import React from "react";
theme/components/Card/index.tsx (1)

3-9: Consider clarifying the content vs. children prop usage.

The Card component accepts both content and children props, and both will be rendered if provided (lines 28-29). While this offers flexibility, it may lead to confusion about which prop to use.

Consider either:

  • Documenting the intended usage pattern (e.g., "use content for simple content, children for complex layouts")
  • Or making them mutually exclusive if that's the intended behavior

If mutual exclusivity is preferred, you could apply this pattern:

 export const Card: React.FC<CardProps> = ({
   title,
   content,
   style,
   className = "",
   children,
 }) => {
   return (
     <div
       className={`rp-border rp-block rp-border-solid rp-border-divider-light rp-rounded-lg rp-bg-bg rp-p-4 ${className}`}
       style={style}
     >
       {title && (
         <div className="rp-text-lg rp-font-semibold rp-mb-3 rp-text-text-1">
           {title}
         </div>
       )}
-      {content && <div className="rp-text-text-2">{content}</div>}
-      {children && <div className="rp-text-text-2">{children}</div>}
+      <div className="rp-text-text-2">{content || children}</div>
     </div>
   );
 };

Alternatively, keep the current flexible approach but add a JSDoc comment to clarify the intended usage.

Also applies to: 28-29

theme/layout/DocLayout/index.tsx (1)

47-48: Consider making sidebar/footer visibility configurable or removing dead code.

Lines 47-48 hardcode showSidebar = false and showDocFooter = false, which makes several code paths unreachable:

  • The sidebar rendering logic (lines 70-88) will always render the placeholder
  • The DocFooter (line 116) will never be rendered
  • The beforeSidebar, afterSidebar, beforeDocFooter, and afterDocFooter props in DocLayoutProps become effectively unused

While the Chinese comments indicate this is intentional ("强制隐藏" = force hide), consider either:

  1. Making these configurable by adding them to DocLayoutProps:

    export interface DocLayoutProps {
      showSidebar?: boolean;
      showDocFooter?: boolean;
      // ... other props
    }
  2. Or removing the dead code entirely to improve maintainability:

    • Remove unused conditional branches
    • Remove unused props from the interface
    • Simplify the component logic

The current approach leaves dead code paths that may confuse future maintainers.

Also applies to: 70-88, 116-116

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e85f2e and 058a168.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (14)
  • package.json (1 hunks)
  • styles/index.css (1 hunks)
  • theme/components/Card/index.tsx (1 hunks)
  • theme/components/Empty/index.tsx (1 hunks)
  • theme/components/HomeContent/index.tsx (2 hunks)
  • theme/components/LinkCard/index.tsx (1 hunks)
  • theme/components/PostList/index.tsx (2 hunks)
  • theme/components/SidebarMenu/index.ts (1 hunks)
  • theme/components/SidebarMenu/useSidebarMenu.tsx (1 hunks)
  • theme/index.tsx (1 hunks)
  • theme/layout/DocLayout/index.tsx (1 hunks)
  • theme/layout/HomeLayout/index.tsx (1 hunks)
  • theme/layout/index.tsx (1 hunks)
  • tsconfig.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
theme/components/Card/index.tsx (1)
theme/index.tsx (1)
  • Card (10-10)
theme/components/LinkCard/index.tsx (1)
theme/index.tsx (1)
  • LinkCard (11-11)
theme/layout/DocLayout/index.tsx (3)
theme/index.tsx (1)
  • DocLayout (5-5)
theme/components/SidebarMenu/index.ts (1)
  • useSidebarMenu (1-1)
theme/components/SidebarMenu/useSidebarMenu.tsx (1)
  • useSidebarMenu (4-124)
theme/components/PostList/index.tsx (1)
plugins/plugin-post-resolver/types.ts (1)
  • PostInfo (5-16)
🪛 Biome (2.1.2)
styles/index.css

[error] 23-23: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

font-weight is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (15)
theme/components/SidebarMenu/index.ts (1)

1-2: LGTM!

Clean re-export pattern for the new sidebar menu hook.

theme/layout/index.tsx (1)

2-3: Verify theme-original exports are available.

The imports have been switched from @rspress/core/theme to @rspress/core/theme-original. Ensure that Badge, LastUpdated, and Layout are properly exported from the theme-original module in the upgraded version.

theme/components/PostList/index.tsx (1)

2-3: Import updates align with PR objectives.

The migration to theme-original for Badge and the addition of LinkCard import from the relative path are consistent with the broader refactoring in this PR.

Also applies to: 9-10

tsconfig.json (1)

13-14: LGTM! i18n alias supports improved type safety.

The new path alias enables type-safe i18n imports as seen in PostList. The alias correctly points to ./docs/i18n.json, and resolveJsonModule is already enabled in the compiler options.

theme/components/SidebarMenu/useSidebarMenu.tsx (1)

50-63: Well-structured hook with clear separation of concerns.

The toggle functions correctly ensure mutually exclusive visibility between sidebar and outline, and the generated menu JSX includes proper ARIA attributes for accessibility. The returned API is comprehensive and well-organized.

Also applies to: 66-113, 115-124

package.json (1)

5-5: Verify that version 1.17.1 of @alauda/doom exists.

The npm registry currently shows version 1.12.7 as the latest available version of @alauda/doom. Before proceeding with this upgrade, confirm that version 1.17.1 has been released and is accessible in your configured npm registry. If it exists, review the changelog for breaking changes and verify support for the theme-original import pattern in that specific version.

theme/components/HomeContent/index.tsx (2)

2-2: LGTM: Consistent migration to theme-original.

The import source change from @rspress/core/theme to @rspress/core/theme-original aligns with the PR's broader refactoring to use theme-original APIs.


16-16: LGTM: Using the new local Card component.

The local Card import from ../Card correctly integrates with the newly introduced Card component, ensuring consistency across the updated theme architecture.

theme/components/LinkCard/index.tsx (1)

1-35: LGTM: Well-structured LinkCard component.

The LinkCard component is cleanly implemented with:

  • Proper TypeScript interface definition
  • Appropriate use of semantic HTML (<a> tag)
  • Flexible props for customization (title, description, style, className)
  • Integration with the new RPpress v2 utility classes

The component follows React best practices and integrates well with the theme architecture.

theme/index.tsx (3)

3-3: LGTM: DocLayout properly integrated into theme exports.

The DocLayout import and export expand the public theme API appropriately, making the new layout component available alongside existing layouts.

Also applies to: 5-5


7-7: LGTM: Consistent theme-original migration.

The change from @rspress/core/theme to @rspress/core/theme-original aligns with the PR's broader migration strategy and maintains consistency across all theme files.


9-11: LGTM: Local components properly exposed.

The Card and LinkCard components are correctly exported from their respective modules, making them available as part of the public theme API.

styles/index.css (2)

22-23: False positive: Lines 22-23 are not duplicate properties.

The static analysis tool incorrectly flagged these lines as duplicates. They serve different purposes:

  • Line 22 defines a CSS custom property (--tw-font-weight)
  • Line 23 applies the actual font-weight style property

Both are necessary and not duplicates.


57-308: LGTM: Comprehensive and well-organized CSS utilities.

The RPpress v2 utility classes are well-structured with:

  • Clear organization by category (layout, spacing, borders, typography, etc.)
  • Consistent naming convention with rp- prefix
  • Proper responsive and dark mode support
  • Integration with CSS custom properties for theming

The utilities effectively support the new Card, LinkCard, and DocLayout components introduced in this PR.

theme/layout/DocLayout/index.tsx (1)

1-13: LGTM: Well-structured DocLayout implementation.

The DocLayout component demonstrates good architectural patterns:

  • Clear props interface with flexible before/after hooks for customization
  • Proper integration with rspress theme-original components
  • Effective use of React hooks (useFrontmatter, useWatchToc, useSidebarMenu)
  • Responsive design with mobile menu support via useSidebarMenu
  • Conditional rendering based on page type and frontmatter configuration

The component provides a solid foundation for document layout with appropriate customization points.

Also applies to: 14-46, 49-69, 89-115, 117-147

@@ -1,4 +1,4 @@
import { useThemeState } from "@rspress/core/theme";
import { useThemeState } from "@rspress/core/theme-original";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find package.json to check RSPress version
find . -name "package.json" -type f | head -5 | xargs head -30

# Search for theme-original usage in codebase
echo ""
echo "=== Searching for theme-original usage ==="
rg "theme-original" --type ts --type tsx -B 2 -A 2

# Search for useThemeState usage
echo ""
echo "=== Searching for useThemeState usage ==="
rg "useThemeState" --type ts --type tsx -B 2 -A 2

# Check if the import file mentioned exists
echo ""
echo "=== Checking the specific file ==="
cat -n "theme/components/Empty/index.tsx" 2>/dev/null || echo "File not found"

Repository: alauda/knowledge

Length of output: 4692


🏁 Script executed:

#!/bin/bash
# Check yarn.lock or package.json for @rspress/core version
echo "=== Checking for @rspress/core in package files ==="
rg "@rspress/core" . --type yaml --type toml --type json -A 2 | head -50

# Check if node_modules exists and search for theme-original
echo ""
echo "=== Searching node_modules for @rspress/core ==="
if [ -d "node_modules/@rspress/core" ]; then
  ls -la node_modules/@rspress/core/ | head -20
  echo ""
  echo "=== Package.json exports from @rspress/core ==="
  if [ -f "node_modules/@rspress/core/package.json" ]; then
    cat node_modules/@rspress/core/package.json | grep -A 50 '"exports"'
  fi
else
  echo "node_modules/@rspress/core not found"
  # Check what's in @alauda/doom dependencies
  echo ""
  echo "=== Checking @alauda/doom dependencies ==="
  if [ -d "node_modules/@alauda/doom" ]; then
    cat node_modules/@alauda/doom/package.json | head -50
  fi
fi

# Search for theme-original more broadly
echo ""
echo "=== Searching for any theme-original reference ==="
find node_modules -name "*theme*original*" -o -name "*theme-original*" 2>/dev/null | head -10

Repository: alauda/knowledge

Length of output: 294


🏁 Script executed:

#!/bin/bash
# Check for yarn.lock or package-lock.json
echo "=== Looking for lock files ==="
ls -la | grep -E "lock|yarn"

echo ""
echo "=== Checking yarn.lock for @rspress/core ==="
if [ -f "yarn.lock" ]; then
  grep -A 5 "@rspress/core" yarn.lock | head -50
fi

echo ""
echo "=== Checking @alauda/doom package.json for its dependencies ==="
# Check if there's a node_modules/@alauda/doom
if [ -d "node_modules/@alauda/doom" ]; then
  cat node_modules/@alauda/doom/package.json 2>/dev/null | jq '.dependencies, .devDependencies, .peerDependencies' | head -100
else
  echo "node_modules not available. Checking root package.json for all deps"
  cat package.json | jq '.' | head -100
fi

echo ""
echo "=== Checking if there are any RSPress references in codebase ==="
rg "rspress" -i . --type js --type ts --type tsx --type json -B 1 -A 1 | head -50

Repository: alauda/knowledge

Length of output: 2529


🏁 Script executed:

#!/bin/bash
# Check yarn cache or search for @rspress/core@2.0.0-rc.2 documentation
echo "=== Checking RSPress 2.0.0-rc.2 exports ==="

# Let's look at what the yarn.lock entry says about @rspress/core
echo "Getting full @rspress/core entry from yarn.lock:"
grep -A 30 '"@rspress/core@npm:2.0.0-rc.2":' yarn.lock

# Check if there's any reference to theme-original in the entire yarn.lock
echo ""
echo "=== Searching yarn.lock for 'theme-original' ==="
grep -i "theme-original" yarn.lock

# Check for 'useThemeState' anywhere
echo ""
echo "=== Searching yarn.lock for 'useThemeState' ==="
grep -i "useThemeState" yarn.lock

Repository: alauda/knowledge

Length of output: 1301


🏁 Script executed:

#!/bin/bash
# Let's search for any theme-related imports or exports in RSPress docs/examples
echo "=== Checking if there are other theme imports in project ==="
rg "from ['\"]@rspress" . -A 1 | head -30

# Look at the @alauda/doom version to understand the context better
echo ""
echo "=== Checking @alauda/doom version in yarn.lock ==="
grep -A 10 "@alauda/doom@npm:.*1.17" yarn.lock

Repository: alauda/knowledge

Length of output: 2349


🏁 Script executed:

#!/bin/bash
# Let's verify what exports actually exist in @rspress/core by checking the actual RSPress source
echo "=== Checking if @rspress/core really exports theme-original ==="

# Search for any reference to "theme-original" in the entire project
echo "Files using @rspress/core/theme-original:"
rg "@rspress/core/theme-original" . --type ts --type tsx | wc -l

echo ""
echo "All imports of this path:"
rg "@rspress/core/theme-original" . --type ts --type tsx

Repository: alauda/knowledge

Length of output: 267


🏁 Script executed:

#!/bin/bash
# Check without file type filtering
echo "=== Finding all theme-original references ==="
grep -r "theme-original" . --include="*.ts" --include="*.tsx" 2>/dev/null

echo ""
echo "=== Check if there's a theme-original entry point or custom export ==="
# Check the theme directory structure
find theme -type f -name "*.ts" -o -name "*.tsx" 2>/dev/null | head -20

echo ""
echo "=== Check theme/index.tsx to see what's exported ==="
cat theme/index.tsx

Repository: alauda/knowledge

Length of output: 1769


🏁 Script executed:

#!/bin/bash
# Check if @alauda/doom provides theme-original export
echo "=== Checking @alauda/doom's yarn.lock entry more thoroughly ==="
grep -B 5 -A 50 '"@alauda/doom@npm:1.17.1":' yarn.lock | head -100

echo ""
echo "=== Look for any @rspress references in @alauda/doom dependencies ==="
grep -A 50 '"@alauda/doom@npm:1.17.1":' yarn.lock | grep -i "rspress"

Repository: alauda/knowledge

Length of output: 194


🏁 Script executed:

#!/bin/bash
# Let's search the yarn.lock for @alauda/doom differently
echo "=== Searching for @alauda/doom in yarn.lock ==="
grep "@alauda/doom" yarn.lock | head -20

echo ""
echo "=== Get the full entry for @alauda/doom ==="
awk '/"@alauda\/doom@npm:1\.17\.1":/,/^"[^@]/' yarn.lock | head -100

echo ""
echo "=== Search entire yarn.lock for rspress in dependencies ==="
grep -B 10 "rspress" yarn.lock | grep -E "^\"|rspress"

Repository: alauda/knowledge

Length of output: 3102


🏁 Script executed:

#!/bin/bash
# Check if there's a TypeScript path alias or other configuration mapping theme-original
echo "=== Checking tsconfig.json for path aliases ==="
cat tsconfig.json 2>/dev/null | grep -A 20 "paths"

echo ""
echo "=== Checking rspress.config.ts for any alias configuration ==="
find . -name "rspress.config.ts" -o -name "rspress.config.js" | xargs cat 2>/dev/null

echo ""
echo "=== Check if theme-original might be re-exported from theme/index.tsx ==="
# We already know theme/index.tsx exports from theme-original, but let's confirm what it should be
cat theme/index.tsx

Repository: alauda/knowledge

Length of output: 707


🏁 Script executed:

#!/bin/bash
# Final verification - check what actually exists in @rspress/core exports
echo "=== Checking @rspress/core@2.0.0-rc.2 package structure from yarn.lock ==="
awk '/"@rspress\/core@npm:2\.0\.0-rc\.2":/,/^"/' yarn.lock | grep -E "exports|bin|main|types" | head -20

echo ""
echo "=== Summary of theme-original usage in codebase ==="
echo "Files importing from @rspress/core/theme-original:"
grep -r "@rspress/core/theme-original" . --include="*.ts" --include="*.tsx" 2>/dev/null | wc -l

echo ""
echo "Functions imported from this path:"
grep -r "from ['\"]@rspress/core/theme-original['\"]" . --include="*.ts" --include="*.tsx" -A 0 2>/dev/null | sed 's/.*from.*//' | sort -u

Repository: alauda/knowledge

Length of output: 280


🏁 Script executed:

#!/bin/bash
# Let's extract the exact imports from each file to understand what's being used
echo "=== Detailed import analysis ==="
grep -B 1 "from ['\"]@rspress/core/theme-original['\"]" . -r --include="*.ts" --include="*.tsx" 2>/dev/null

echo ""
echo "=== Check what components are in standard @rspress/core/theme ==="
# Look at how other @rspress/core/runtime imports work in the project for reference
grep -r "from ['\"]@rspress/core" . --include="*.ts" --include="*.tsx" 2>/dev/null | grep -o "from ['\"]@rspress/core[^'\"]*['\"]" | sort -u

Repository: alauda/knowledge

Length of output: 1199


Replace non-existent @rspress/core/theme-original imports with @rspress/core/theme.

In RSPress v2.0, the package migration is rspress/theme => @rspress/core/theme. The import path @rspress/core/theme-original does not exist in @rspress/core@2.0.0-rc.2 (the version declared in yarn.lock). Update all imports in the following files:

  • theme/components/Empty/index.tsx (useThemeState)
  • theme/components/HomeContent/index.tsx (useFullTextSearch)
  • theme/components/PostList/index.tsx (Badge)
  • theme/layout/DocLayout/index.tsx (multiple exports)
  • theme/layout/index.tsx (Badge, LastUpdated, Layout)
  • theme/index.tsx (wildcard export)

Change from @rspress/core/theme-original to @rspress/core/theme.

🤖 Prompt for AI Agents
theme/components/Empty/index.tsx (line 1) and the other files listed:
theme/components/HomeContent/index.tsx, theme/components/PostList/index.tsx,
theme/layout/DocLayout/index.tsx, theme/layout/index.tsx, theme/index.tsx —
replace any import source string "@rspress/core/theme-original" with
"@rspress/core/theme"; keep the same named imports (useThemeState,
useFullTextSearch, Badge, LastUpdated, Layout, and any wildcard exports) and
ensure import syntax remains valid for TypeScript/ES modules.

export const PostList: FC<PostListProps> = ({ postList }) => {
const notEmpty = useMemo(() => postList.length > 0, [postList]);
const t = useI18n();
const t = useI18n<typeof import("i18n")>();
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== Checking docs/i18n.json ==="
if [ -f "docs/i18n.json" ]; then
  echo "✓ docs/i18n.json exists"
  echo ""
  echo "=== File content preview ==="
  head -50 docs/i18n.json
  echo ""
  echo "=== Checking for lastUpdatedText key ==="
  if grep -q "lastUpdatedText" docs/i18n.json; then
    echo "✓ lastUpdatedText key found"
    grep "lastUpdatedText" docs/i18n.json | head -5
  else
    echo "✗ lastUpdatedText key NOT found"
  fi
else
  echo "✗ docs/i18n.json NOT found"
fi

echo ""
echo "=== Checking PostList component ==="
if [ -f "theme/components/PostList/index.tsx" ]; then
  echo "✓ PostList/index.tsx exists"
  echo ""
  echo "=== Lines 15-25 (context around line 18) ==="
  sed -n '15,25p' theme/components/PostList/index.tsx
  echo ""
  echo "=== Lines 48-57 (context around lines 52-53) ==="
  sed -n '48,57p' theme/components/PostList/index.tsx
else
  echo "✗ PostList/index.tsx NOT found"
fi

Repository: alauda/knowledge

Length of output: 1913


Add lastUpdatedText key to docs/i18n.json with translations for all supported locales.

The code at lines 52-53 calls t("lastUpdatedText"), but this key is missing from docs/i18n.json. Add the translation key in the same bilingual format as existing entries (with both "en" and "zh" values).

🤖 Prompt for AI Agents
In theme/components/PostList/index.tsx around line 18 and where
t("lastUpdatedText") is called (lines ~52-53), the i18n key "lastUpdatedText" is
referenced but missing from docs/i18n.json; add a new top-level
"lastUpdatedText" entry in docs/i18n.json with both "en" and "zh" values (and
any other supported locales) following the existing bilingual format used in the
file so t("lastUpdatedText") resolves for all locales.

Comment on lines 11 to 47
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
const target = event.target as HTMLElement;

// 如果点击的是菜单按钮,不关闭
if (target.closest('.rp-doc-layout__menu-button')) {
return;
}

// 如果点击在侧边栏外部,关闭侧边栏
if (
sidebarLayoutRef.current &&
!sidebarLayoutRef.current.contains(target) &&
!target.closest('.rp-doc-layout__menu')
) {
setIsSidebarOpen(false);
}

// 如果点击在大纲外部,关闭大纲
if (
asideLayoutRef.current &&
!asideLayoutRef.current.contains(target) &&
!target.closest('.rp-doc-layout__menu')
) {
setIsOutlineOpen(false);
}
};

// 只在移动端添加点击外部关闭逻辑
const isMobile = window.innerWidth <= 768;
if (isMobile) {
document.addEventListener('click', handleClickOutside);
return () => {
document.removeEventListener('click', handleClickOutside);
};
}
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Mobile detection doesn't respond to window resize.

The mobile check at Line 40 (window.innerWidth <= 768) only executes once when the component mounts. If a user resizes their browser window from desktop to mobile (or vice versa), the click-outside listener won't be added or removed accordingly, which could lead to inconsistent behavior.

Consider refactoring to handle window resize events:

 useEffect(() => {
   const handleClickOutside = (event: MouseEvent) => {
     const target = event.target as HTMLElement;
     
     // 如果点击的是菜单按钮,不关闭
     if (target.closest('.rp-doc-layout__menu-button')) {
       return;
     }

     // 如果点击在侧边栏外部,关闭侧边栏
     if (
       sidebarLayoutRef.current &&
       !sidebarLayoutRef.current.contains(target) &&
       !target.closest('.rp-doc-layout__menu')
     ) {
       setIsSidebarOpen(false);
     }

     // 如果点击在大纲外部,关闭大纲
     if (
       asideLayoutRef.current &&
       !asideLayoutRef.current.contains(target) &&
       !target.closest('.rp-doc-layout__menu')
     ) {
       setIsOutlineOpen(false);
     }
   };

-  // 只在移动端添加点击外部关闭逻辑
-  const isMobile = window.innerWidth <= 768;
-  if (isMobile) {
-    document.addEventListener('click', handleClickOutside);
-    return () => {
-      document.removeEventListener('click', handleClickOutside);
-    };
-  }
+  // 只在移动端添加点击外部关闭逻辑
+  const checkMobile = () => window.innerWidth <= 768;
+  
+  const updateListener = () => {
+    const isMobile = checkMobile();
+    if (isMobile) {
+      document.addEventListener('click', handleClickOutside);
+    } else {
+      document.removeEventListener('click', handleClickOutside);
+    }
+  };
+
+  updateListener();
+  window.addEventListener('resize', updateListener);
+  
+  return () => {
+    document.removeEventListener('click', handleClickOutside);
+    window.removeEventListener('resize', updateListener);
+  };
 }, []);
📝 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
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
const target = event.target as HTMLElement;
// 如果点击的是菜单按钮,不关闭
if (target.closest('.rp-doc-layout__menu-button')) {
return;
}
// 如果点击在侧边栏外部,关闭侧边栏
if (
sidebarLayoutRef.current &&
!sidebarLayoutRef.current.contains(target) &&
!target.closest('.rp-doc-layout__menu')
) {
setIsSidebarOpen(false);
}
// 如果点击在大纲外部,关闭大纲
if (
asideLayoutRef.current &&
!asideLayoutRef.current.contains(target) &&
!target.closest('.rp-doc-layout__menu')
) {
setIsOutlineOpen(false);
}
};
// 只在移动端添加点击外部关闭逻辑
const isMobile = window.innerWidth <= 768;
if (isMobile) {
document.addEventListener('click', handleClickOutside);
return () => {
document.removeEventListener('click', handleClickOutside);
};
}
}, []);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
const target = event.target as HTMLElement;
// 如果点击的是菜单按钮,不关闭
if (target.closest('.rp-doc-layout__menu-button')) {
return;
}
// 如果点击在侧边栏外部,关闭侧边栏
if (
sidebarLayoutRef.current &&
!sidebarLayoutRef.current.contains(target) &&
!target.closest('.rp-doc-layout__menu')
) {
setIsSidebarOpen(false);
}
// 如果点击在大纲外部,关闭大纲
if (
asideLayoutRef.current &&
!asideLayoutRef.current.contains(target) &&
!target.closest('.rp-doc-layout__menu')
) {
setIsOutlineOpen(false);
}
};
// 只在移动端添加点击外部关闭逻辑
const checkMobile = () => window.innerWidth <= 768;
const updateListener = () => {
const isMobile = checkMobile();
if (isMobile) {
document.addEventListener('click', handleClickOutside);
} else {
document.removeEventListener('click', handleClickOutside);
}
};
updateListener();
window.addEventListener('resize', updateListener);
return () => {
document.removeEventListener('click', handleClickOutside);
window.removeEventListener('resize', updateListener);
};
}, []);
🤖 Prompt for AI Agents
In theme/components/SidebarMenu/useSidebarMenu.tsx around lines 11 to 47, the
current mobile check (window.innerWidth <= 768) runs only on mount so the
click-outside listener is never added or removed when the user resizes the
window; change the effect to track viewport breakpoint changes by adding a
resize listener that updates an isMobile flag (or state/ref) and attach/detach
the document click handler whenever the breakpoint crosses the mobile threshold
(use a small debounce on resize), and ensure all listeners are cleaned up on
unmount to prevent leaks.

Copy link
Contributor

@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: 3

♻️ Duplicate comments (1)
theme/components/PostList/index.tsx (1)

49-53: Missing i18n key already flagged in previous review.

The missing lastUpdatedText translation key has already been identified in a previous review. Please ensure this key is added to docs/i18n.json with translations for all supported locales.

🧹 Nitpick comments (3)
theme/components/SidebarMenu/useSidebarMenu.tsx (2)

40-40: Address or clarify the TODO comment.

The TODO comment lacks context about what needs discussion. Consider either resolving the issue or adding more detail about what requires discussion.

Do you want me to help identify what might need discussion here, or would you like to open an issue to track this?


19-24: Consider memoizing the callback and JSX for better performance.

The toggleOutline function and sidebarMenu JSX are recreated on every render, which could cause unnecessary re-renders in consuming components.

Apply these optimizations:

-import { useRef, useState } from "react";
+import { useRef, useState, useCallback, useMemo } from "react";
 import { useI18n } from "@rspress/core/runtime";
 import {
   useDynamicToc,
   ReadPercent,
   SvgWrapper,
   IconArrowRight,
 } from "@rspress/core/theme-original";

 export function useSidebarMenu() {
   const [isSidebarOpen, setIsSidebarOpen] = useState(false);
   const [isOutlineOpen, setIsOutlineOpen] = useState(false);
   const sidebarLayoutRef = useRef<HTMLElement>(null);
   const asideLayoutRef = useRef<HTMLElement>(null);

   const headers = useDynamicToc();
   const t = useI18n();

-  const toggleOutline = () => {
+  const toggleOutline = useCallback(() => {
     setIsOutlineOpen((prev) => !prev);
     if (isSidebarOpen) {
       setIsSidebarOpen(false);
     }
-  };
+  }, [isSidebarOpen]);

-  const sidebarMenu = (
+  const sidebarMenu = useMemo(() => (
     <div className="rp-doc-layout__menu">
       <button
         type="button"
         disabled={headers.length === 0}
         onClick={(e) => {
           e.preventDefault();
           e.stopPropagation();
           toggleOutline();
         }}
         className="rp-sidebar-menu__right"
       >
         <span>{t("outlineTitle")}</span>
         <ReadPercent size={14} strokeWidth={2} />
         {/* TODO: discussion */}
         {headers.length !== 0 && (
           <SvgWrapper
             icon={IconArrowRight}
             style={{
               transform: isOutlineOpen ? "rotate(90deg)" : "rotate(0deg)",
               transition: "transform 0.2s ease-out",
             }}
           />
         )}
       </button>
     </div>
-  );
+  ), [headers.length, isOutlineOpen, toggleOutline, t]);

   return {
     isSidebarOpen,
     isOutlineOpen,
     sidebarMenu,
     asideLayoutRef,
     sidebarLayoutRef,
     toggleOutline,
   };
 }

Also applies to: 26-52

theme/layout/HomeLayout/index.tsx (1)

6-6: Consider removing the unused React import.

With "jsx": "react-jsx" configured in tsconfig.json, the explicit React import is unnecessary. The JSX transform handles this automatically.

Apply this diff to remove the unused import:

-import React from "react";
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 058a168 and efca7a3.

📒 Files selected for processing (16)
  • styles/index.css (1 hunks)
  • theme/components/BreadCrumb/index.tsx (2 hunks)
  • theme/components/Card/index.tsx (1 hunks)
  • theme/components/Checkbox/index.tsx (0 hunks)
  • theme/components/DocID/index.tsx (1 hunks)
  • theme/components/EditOnGithub/index.tsx (1 hunks)
  • theme/components/HomeBanner/index.tsx (2 hunks)
  • theme/components/HomeContent/index.tsx (3 hunks)
  • theme/components/LinkCard/index.tsx (1 hunks)
  • theme/components/Pagination/index.tsx (1 hunks)
  • theme/components/PostList/index.tsx (2 hunks)
  • theme/components/SidebarMenu/useSidebarMenu.tsx (1 hunks)
  • theme/layout/HomeLayout/index.tsx (2 hunks)
  • theme/layout/index.tsx (1 hunks)
  • tsconfig.json (1 hunks)
  • virtual-modules.d.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • theme/components/Checkbox/index.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • theme/components/LinkCard/index.tsx
  • theme/layout/index.tsx
  • theme/components/HomeContent/index.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
theme/components/Card/index.tsx (1)
theme/index.tsx (1)
  • Card (10-10)
theme/components/PostList/index.tsx (2)
virtual-modules.d.ts (1)
  • PostInfo (3-14)
plugins/plugin-post-resolver/types.ts (1)
  • PostInfo (5-16)
🪛 Biome (2.1.2)
styles/index.css

[error] 23-23: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

font-weight is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)

🔇 Additional comments (18)
theme/components/Pagination/index.tsx (1)

18-18: LGTM: Type safety improvement for i18n.

The explicit generic type parameter aligns with the new "i18n" path alias in tsconfig.json and provides better type inference for translation keys.

theme/components/EditOnGithub/index.tsx (1)

46-46: LGTM: Consistent i18n typing pattern.

The typing update is consistent with other components in this PR and improves type safety.

theme/components/HomeBanner/index.tsx (1)

12-12: LGTM: Consistent i18n typing.

The explicit type parameter follows the same pattern as other components in this PR.

tsconfig.json (1)

12-22: LGTM: TypeScript configuration supports the i18n typing pattern.

The addition of baseUrl and the "i18n" path alias correctly supports the typed useI18n<typeof import("i18n")>() pattern used throughout the codebase. The expanded include patterns appropriately cover the theme and plugin directories.

theme/layout/HomeLayout/index.tsx (1)

24-28: LGTM: Footer styling aligns with CSS theming updates.

The simplified class names (home-footer, home-footer__content) align with the token-based theming approach introduced in styles/index.css.

virtual-modules.d.ts (1)

1-26: LGTM: Well-structured type declarations for virtual modules.

The type declarations properly expose PostInfo and related exports for virtual modules. The PostInfo interface is consistent with plugins/plugin-post-resolver/types.ts.

theme/components/PostList/index.tsx (2)

1-9: LGTM: Import reorganization aligns with theme refactoring.

The import changes correctly source Badge from theme-original and add the necessary LinkCard dependency, consistent with the broader theme reorganization in this PR.


17-17: LGTM: Consistent i18n typing pattern.

theme/components/Card/index.tsx (1)

3-9: LGTM: Well-defined CardProps interface.

The interface provides a flexible API with optional properties for various use cases.

styles/index.css (6)

57-74: LGTM: Card component styling.

The card component styling is well-structured with consistent use of CSS variables for theming. The separation of card, card__title, and card__content provides good modularity.


76-103: LGTM: Link card component styling.

The link-card component is well-implemented with smooth hover transitions and consistent theming. The use of border-bottom instead of a full border is appropriate for list-style layouts.


105-117: LGTM: Post-meta styling aligns with component refactor.

The post-meta styling correctly replaces the utility classes previously used in the DocID component, providing centralized and maintainable styling with responsive behavior.


119-121: LGTM: Breadcrumb styling aligns with component refactor.

The breadcrumb styling appropriately replaces the rp-mb-10 utility class previously used in the BreadCrumb component.


123-146: LGTM: Home footer styling.

The home-footer component styling is well-implemented with proper responsive behavior and consistent theming through CSS variables.


148-194: LGTM: Responsive document layout menu.

The rp-doc-layout__menu styling is properly implemented with:

  • Appropriate fixed positioning and z-index
  • Responsive display behavior (visible only below 1280px)
  • Well-defined interactive states for the menu button
theme/components/BreadCrumb/index.tsx (2)

7-7: LGTM: Improved type safety with typed i18n hook.

Adding the type parameter to useI18n provides better type checking and IDE support, aligning with the PR-wide typing improvements.


17-17: LGTM: Semantic class name aligns with CSS refactor.

The className change from rp-mb-10 to breadcrumb aligns with the new centralized styling in styles/index.css and follows semantic naming conventions.

theme/components/DocID/index.tsx (1)

11-11: LGTM: Consolidates utility classes into semantic class.

The className change from multiple utility classes to the single post-meta class improves maintainability while preserving the visual styling. The new class in styles/index.css provides equivalent responsive behavior.

@@ -1,4 +1,4 @@
import { useI18n, usePageData } from "@rspress/runtime";
import { useI18n } from "@rspress/runtime";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check which files use which import paths for useI18n

echo "=== Files using @rspress/core/runtime ==="
rg -n "from ['\"]@rspress/core/runtime['\"]" --type=tsx --type=ts 2>/dev/null || echo "No matches found"

echo ""
echo "=== Files using @rspress/runtime ==="
rg -n "from ['\"]@rspress/runtime['\"]" --type=tsx --type=ts 2>/dev/null || echo "No matches found"

echo ""
echo "=== All @rspress imports ==="
rg -n "from ['\"]@rspress" --type=tsx --type=ts | head -30

echo ""
echo "=== Check package.json for @rspress dependencies ==="
if [ -f "package.json" ]; then
  jq '.dependencies, .devDependencies' package.json 2>/dev/null | grep -i rspress || echo "No rspress deps found"
fi

Repository: alauda/knowledge

Length of output: 324


🏁 Script executed:

#!/bin/bash
# Search more broadly for the file and imports

echo "=== Looking for HomeBanner file ==="
find . -name "HomeBanner" -o -name "index.tsx" | grep -i homebanner

echo ""
echo "=== Looking for any @rspress imports (no type filter) ==="
rg "@rspress" | head -20

echo ""
echo "=== Check if the file exists ==="
[ -f "theme/components/HomeBanner/index.tsx" ] && echo "File exists" || echo "File does not exist"

echo ""
echo "=== List files in theme/components directory ==="
ls -la theme/components/ 2>/dev/null || echo "Directory does not exist"

echo ""
echo "=== Check package.json content ==="
cat package.json 2>/dev/null | head -50

Repository: alauda/knowledge

Length of output: 2982


🏁 Script executed:

#!/bin/bash
# Check the HomeBanner file content
echo "=== Content of theme/components/HomeBanner/index.tsx ==="
cat -n theme/components/HomeBanner/index.tsx

echo ""
echo "=== Search for useI18n imports in codebase ==="
rg "useI18n" --max-count=50

echo ""
echo "=== Check what @rspress exports ==="
rg "import.*from.*@rspress" | head -30

Repository: alauda/knowledge

Length of output: 4277


Change import path to match codebase convention.

The import uses @rspress/runtime while all other components in this project (BreadCrumb, SidebarMenu, PostList, HomeContent, Pagination, EditOnGithub) import from @rspress/core/runtime. Update to import { useI18n } from "@rspress/core/runtime"; for consistency.

🤖 Prompt for AI Agents
In theme/components/HomeBanner/index.tsx at line 1 the import path uses
"@rspress/runtime" which is inconsistent with the project's convention; change
the import to "@rspress/core/runtime" so it reads import { useI18n } from
"@rspress/core/runtime"; to match other components and maintain consistent
module resolution.

Copy link
Contributor

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
theme/components/HomeContent/index.tsx (1)

126-130: Missing onSearchParamsChange in dependency array.

The onPageChange callback uses onSearchParamsChange but has an empty dependency array, which may cause stale closure issues.

🔎 Proposed fix
   const onPageChange = useCallback(
     (number: number) =>
       onSearchParamsChange(new URLSearchParams({ page: `${number}` })),
-    []
+    [onSearchParamsChange]
   );
♻️ Duplicate comments (4)
theme/components/HomeBanner/index.tsx (1)

1-1: Unresolved: Import path inconsistency remains.

This import path inconsistency was flagged in a previous review and has not been addressed. All other components in the project import from @rspress/core/runtime, but this file imports from @rspress/runtime. Update to maintain consistency across the codebase.

🔎 Proposed fix
-import { useI18n } from "@rspress/runtime";
+import { useI18n } from "@rspress/core/runtime";
theme/components/Card/index.tsx (1)

3-9: Unresolved: Ambiguous content vs children prop usage.

This design issue was flagged in a previous review and remains unaddressed. The component accepts both content and children props and renders them into separate <div className="card__content"> containers (lines 21-22), which creates confusing behavior and potential styling conflicts when both are provided.

Make content and children mutually exclusive to clarify the component's API.

🔎 Recommended fix (Option 1: Mutually exclusive)
 export const Card: React.FC<CardProps> = ({
   title,
   content,
   style,
   className = "",
   children,
 }) => {
   return (
     <div className={`card ${className}`} style={style}>
       {title && <div className="card__title">{title}</div>}
-      {content && <div className="card__content">{content}</div>}
-      {children && <div className="card__content">{children}</div>}
+      {(content || children) && (
+        <div className="card__content">{content || children}</div>
+      )}
     </div>
   );
 };

Also applies to: 21-22

styles/index.css (1)

16-30: Remove duplicate font-weight declaration.

Line 17 declares font-weight: 500 and line 23 declares it again with the CSS variable. The second declaration overrides the first, making line 17 redundant.

theme/components/PostList/index.tsx (1)

49-53: Ensure lastUpdatedText key exists in i18n configuration.

The code uses t("lastUpdatedText") but a previous review flagged that this key may be missing from the i18n configuration.

🧹 Nitpick comments (2)
theme/layout/HomeLayout/index.tsx (1)

6-6: Consider removing the redundant React import.

With React 17+ and the new JSX transform, explicitly importing React is no longer necessary for JSX usage. This import can be safely removed.

🔎 Proposed cleanup
 import { usePageData } from "@rspress/runtime";
 import { ReactNode } from "react";
 
 import { HomeBanner } from "../../components/HomeBanner";
 import { HomeContent } from "../../components/HomeContent";
-import React from "react";
theme/components/HomeContent/index.tsx (1)

21-21: Consider moving React import to the top of the file.

The React import is placed after other imports. Conventionally, React imports should be at the top, before other dependencies.

🔎 Suggested reorder
+import React from "react";
 import { useI18n, usePageData } from "@rspress/core/runtime";
 import { useFullTextSearch } from "@rspress/core/theme-original";
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { useCallback, useEffect, useMemo, useState } from "react";
 
 import { PostInfo, postInfos } from "virtual-post-data";
 ...
-import React from "react";
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between efca7a3 and 9b9f20d.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (22)
  • package.json
  • styles/index.css
  • theme/components/BreadCrumb/index.tsx
  • theme/components/Card/index.tsx
  • theme/components/Checkbox/index.tsx
  • theme/components/DocID/index.tsx
  • theme/components/EditOnGithub/index.tsx
  • theme/components/Empty/index.tsx
  • theme/components/HomeBanner/index.tsx
  • theme/components/HomeContent/index.tsx
  • theme/components/LinkCard/index.tsx
  • theme/components/Pagination/index.tsx
  • theme/components/PostList/index.tsx
  • theme/components/SidebarMenu/index.ts
  • theme/components/SidebarMenu/useSidebarMenu.tsx
  • theme/index.tsx
  • theme/layout/DocLayout/index.tsx
  • theme/layout/HomeLayout/index.tsx
  • theme/layout/index.tsx
  • theme/utils/download.ts
  • tsconfig.json
  • virtual-modules.d.ts
💤 Files with no reviewable changes (1)
  • theme/components/Checkbox/index.tsx
🚧 Files skipped from review as they are similar to previous changes (9)
  • theme/components/LinkCard/index.tsx
  • theme/components/BreadCrumb/index.tsx
  • tsconfig.json
  • theme/layout/DocLayout/index.tsx
  • theme/components/SidebarMenu/useSidebarMenu.tsx
  • theme/components/Pagination/index.tsx
  • theme/components/Empty/index.tsx
  • theme/components/SidebarMenu/index.ts
  • theme/components/EditOnGithub/index.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
theme/components/Card/index.tsx (1)
theme/index.tsx (1)
  • Card (10-10)
theme/layout/index.tsx (1)
theme/utils/download.ts (3)
  • getPathname (16-22)
  • shouldDownload (11-14)
  • downloadFile (24-57)
🪛 Biome (2.1.2)
styles/index.css

[error] 23-23: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

font-weight is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)

🔇 Additional comments (10)
theme/components/DocID/index.tsx (1)

11-11: LGTM! Semantic className improves maintainability.

The refactoring from utility classes to the semantic post-meta className aligns with the broader styling standardization in this PR and improves code maintainability.

theme/components/HomeBanner/index.tsx (1)

12-12: LGTM! Type parameter improves type safety.

Adding the type parameter to useI18n strengthens type checking and improves developer experience with better autocomplete.

theme/layout/HomeLayout/index.tsx (1)

24-28: LGTM! Footer refactoring improves maintainability.

The simplified footer structure with semantic class names (home-footer, home-footer__content) aligns well with the styling standardization throughout this PR.

virtual-modules.d.ts (1)

1-25: LGTM!

The virtual module type declarations are well-structured with proper exports for PostInfo interface and the related constants. The typing provides good type safety for consuming components.

styles/index.css (2)

57-103: LGTM!

The .card and .link-card component styles are well-implemented with consistent use of CSS variables for theming, proper transitions, and hover states that align with the new Card and LinkCard components.


148-194: LGTM!

The responsive menu styles are well-structured with appropriate breakpoint handling, consistent theming via CSS variables, and proper interactive states (hover, active).

theme/components/HomeContent/index.tsx (1)

44-72: Dependency array may be incomplete for searchFull.

The searchFull callback references onKeywordChange and search but they are not included in the dependency array. This could lead to stale closures if these functions change.

   const searchFull = useCallback(
     async (keyword: string) => {
       // ...
     },
-    [initialized, siteData.base, page.lang]
+    [initialized, siteData.base, page.lang, onKeywordChange, search]
   );
theme/components/PostList/index.tsx (1)

1-9: LGTM!

The import reorganization aligns with the PR's migration to @rspress/core/theme-original and properly uses the local LinkCard component.

theme/index.tsx (1)

1-11: LGTM!

The export structure correctly exposes DocLayout, Card, and LinkCard from local implementations, with the wildcard re-export from @rspress/core/theme-original providing base theme exports. Local named exports after the wildcard correctly shadow any conflicting names from the re-export.

theme/utils/download.ts (1)

48-56: Cross-origin downloads may silently fail.

For cross-origin URLs, the download attribute is often ignored by browsers due to security restrictions, causing a navigation instead of a download. Consider adding a note or warning to users, or opening in a new tab.

package.json Outdated
"packageManager": "yarn@4.12.0",
"devDependencies": {
"@alauda/doom": "^1.12.7",
"@alauda/doom": "^1.17.1",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

@alauda/doom npm package breaking changes 1.12.7 to 1.17.1

💡 Result:

I can't find an authoritative changelog for @alauda/doom covering 1.12.7 → 1.17.1 with the searches I ran. Could you confirm the package name and that those exact versions exist (or want me to search npm, GitHub repo, and the package's CHANGELOG.md for breaking changes)? If yes I will run a deeper search and report the breaking changes with sources.


🌐 Web query:

@alauda/doom changelog release notes version history

💡 Result:

You can find @alauda/doom's changelog and release history here:

  • Package page (versions list / links to source & CHANGELOG): Socket / npm mirror for @alauda/doom. [1]
  • Official docs site (Doom) — includes changelog/changes and links to docs: doom.js.org. [2]
  • Libraries.io / npm listing (version history and changelog note). [3]

If you want, I can fetch the CHANGELOG.md contents (or list recent release notes) and paste the latest entries here.


🌐 Web query:

@alauda/doom package documentation migration guide

💡 Result:

Short answer — use the official Doom docs (doom.js.org) migration docs and the package README. Key references and quick steps:

References

  • Doom docs site (usage & configuration) — shows new config formats, defineConfig import, theme/plugin guidance. [1]
  • @alauda/doom npm/package page (README, changelog links). [2]

Quick migration checklist

  1. Install/update package: npm install @alauda/doom (or the desired version). See package README/changelog. [2]
  2. Replace old config with doom.config.{yaml,yml,js,ts} and, for JS/TS configs, export via defineConfig from '@alauda/doom/config'. [1][2]
  3. Move site-wide options into new schema fields (lang, title, logo, base, outDir, api/export/lint/etc.) per Configuration docs. [1]
  4. If you used a custom theme or plugins, export/compose them via the new theme entry (e.g., theme/index.ts export * from '@alauda/doom/theme') and update plugin references to the new plugin API. [1]
  5. Update CLI usage/flags as documented (e.g., -R/--edit-repo, -a/--algolia, -S/--site-url) and rebuild. [1]
  6. Test MDX/MD pipelines and API docs (OpenAPI/CRD) per the API Documentation section; adjust references/export options if needed. [1]

If you want, I can:

  • fetch the specific changelog entries for the version you’re migrating from/to, or
  • produce a concrete patch (diff) for your current doom config if you paste it.

Sources

  1. Doom — Configuration & Usage docs. https://doom.js.org/en/usage/configuration.html. [1]
  2. @alauda/doom package/README (npm / package pages). [2]

Verify migration steps completed for @alauda/doom upgrade.

The upgrade from ^1.12.7 to ^1.17.1 includes breaking changes requiring configuration and setup updates:

  • Configuration format change to doom.config.{yaml,yml,js,ts} with defineConfig export
  • Site-wide options moved to new schema fields (lang, title, logo, base, outDir, api/export/lint, etc.)
  • Theme and plugin API updated—custom themes/plugins must be refactored per the new API
  • CLI flags may have changed

Confirm that all migration steps from the Doom documentation have been completed.

🤖 Prompt for AI Agents
In package.json around line 5, the dependency was upgraded from @alauda/doom
^1.12.7 to ^1.17.1 but the PR did not confirm migration steps; update and
validate the project to the new Doom v1.17 config and APIs by renaming/replacing
legacy config to doom.config.{yaml,yml,js,ts} and exporting via defineConfig,
migrate site-wide options to the new schema fields (lang, title, logo, base,
outDir, api/export/lint, etc.), refactor any custom themes/plugins to the new
theme/plugin API, adjust CLI flags and npm scripts if changed, run the Doom
migration checklist from the docs, build/lint/test the site to ensure no
runtime/config errors, and commit any transformed config/theme/plugin files and
updated scripts.

Comment on lines 69 to 93
components={{
a: (props: any) => {
const CustomMDXComponent = getCustomMDXComponent();
const pathname = getPathname(props.href);
if (!shouldDownload(pathname)) {
return <CustomMDXComponent.a {...props}></CustomMDXComponent.a>;
}

// <div className="flex justify-between" style={{marginTop:'-1.5rem'}}>
// <LastUpdated></LastUpdated>
// <DocID></DocID>
// </div>
// </>
// ) : (
// <CustomMDXComponent.h1 {...props} />
// );
// },
// }}
return (
<CustomMDXComponent.a
{...props}
href=""
onClick={(e: Event) => {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation?.();
downloadFile(
props.href,
pathname.split("/").pop() || "download"
);
}}
></CustomMDXComponent.a>
);
},
}}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Preserve href for accessibility and UX; use download attribute instead.

Setting href="" removes the link destination, breaking right-click "Copy link" and accessibility features. Consider keeping the original href and using the download attribute, or at minimum preserve the href for context.

Also, the event type should be React.MouseEvent for proper typing.

🔎 Proposed fix
           return (
             <CustomMDXComponent.a
               {...props}
-              href=""
-              onClick={(e: Event) => {
+              download={pathname.split("/").pop() || "download"}
+              onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
                 e.preventDefault();
                 e.stopPropagation();
-                e.stopImmediatePropagation?.();
                 downloadFile(
                   props.href,
                   pathname.split("/").pop() || "download"
                 );
               }}
             ></CustomMDXComponent.a>
           );
📝 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
components={{
a: (props: any) => {
const CustomMDXComponent = getCustomMDXComponent();
const pathname = getPathname(props.href);
if (!shouldDownload(pathname)) {
return <CustomMDXComponent.a {...props}></CustomMDXComponent.a>;
}
// <div className="flex justify-between" style={{marginTop:'-1.5rem'}}>
// <LastUpdated></LastUpdated>
// <DocID></DocID>
// </div>
// </>
// ) : (
// <CustomMDXComponent.h1 {...props} />
// );
// },
// }}
return (
<CustomMDXComponent.a
{...props}
href=""
onClick={(e: Event) => {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation?.();
downloadFile(
props.href,
pathname.split("/").pop() || "download"
);
}}
></CustomMDXComponent.a>
);
},
}}
components={{
a: (props: any) => {
const CustomMDXComponent = getCustomMDXComponent();
const pathname = getPathname(props.href);
if (!shouldDownload(pathname)) {
return <CustomMDXComponent.a {...props}></CustomMDXComponent.a>;
}
return (
<CustomMDXComponent.a
{...props}
download={pathname.split("/").pop() || "download"}
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault();
e.stopPropagation();
downloadFile(
props.href,
pathname.split("/").pop() || "download"
);
}}
></CustomMDXComponent.a>
);
},
}}
🤖 Prompt for AI Agents
In theme/layout/index.tsx around lines 69 to 93, the code clears href and uses a
raw Event type which breaks accessibility and typing; preserve the original href
(props.href) on the anchor so right-click "Copy link" and assistive tech still
see the destination, add a download attribute (e.g.,
download={pathname.split("/").pop() || "download"}) to trigger download
behavior, and change the onClick signature to use
React.MouseEvent<HTMLAnchorElement> (or React.MouseEvent) while still calling
e.preventDefault(), e.stopPropagation(), and e.stopImmediatePropagation?.();
call downloadFile(props.href, filename) inside the handler and do not overwrite
href.

Copy link
Contributor

@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: 5

🤖 Fix all issues with AI agents
In @package.json:
- Line 5: The package.json dependency entry for "@alauda/doom" uses a
non-existent version "^1.17.6"; update the dependency to a valid version (e.g.,
change "@alauda/doom": "^1.17.6" to the correct published version such as
"1.7.1" or the intended "1.7.6"), or correct the package name if that was the
mistake, then run npm view @alauda/doom versions to confirm the chosen version
exists and npm install to verify resolution.

In @styles/index.css:
- Around line 21-35: .paginationItem declares font-weight twice; remove the
redundant declaration so only one font-weight assignment remains. In the
.paginationItem rule, delete the first font-weight: var(--font-weight-medium)
(the standalone occurrence) and keep the later --tw-font-weight and font-weight
pair (or vice versa) so the class has a single clear font-weight definition.

In @theme/layout/index.tsx:
- Around line 77-93: The anchor currently forces href="#" which can break
UX/accessibility; change the rendered element in the CustomMDXComponent.a block
to use the original props.href (e.g., href={props.href}) instead of "#" so the
link remains valid for screen readers and fallback behavior, keep the existing
preventDefault()/stopPropagation() in the onClick handler and the download prop
and preserve calling downloadFile(props.href, ...), and optionally ensure
aria-label/role is set if the link needs a button-like announcement.

In @theme/utils/download.ts:
- Around line 24-31: getBasePath currently falls back to a hardcoded
"/knowledge/" which can drift from the build config; update getBasePath to
derive its fallback from the project's configuration instead of a literal string
by importing the exported base path (or a BUILD_BASE_PATH env var) from the
build config (e.g., the value exported in doom.config.ts) and using that as the
fallback; ensure getBasePath still attempts the pathname regex first, then
returns the imported/configured base path if the regex fails.
🧹 Nitpick comments (5)
styles/doc-layout.css (1)

63-79: Sidebar uses overflow: auto scroll which may cause unexpected behavior.

The overflow: auto scroll declaration sets overflow-x: auto and overflow-y: scroll. If the intent is to always show vertical scrollbar while auto-hiding horizontal, this is fine. However, scroll forces a scrollbar even when content fits, which may not be the desired UX.

Consider using `overflow-y: auto` if scrollbar should only appear when needed
 .rp-doc-layout__sidebar {
   ...
-  overflow: auto scroll;
+  overflow-x: auto;
+  overflow-y: auto;
 }
styles/index.css (1)

153-165: Potential z-index conflict with doc-layout.css.

The .rp-doc-layout__menu here uses z-index: 100 (line 159), but doc-layout.css defines --rp-z-index-nav-screen: 70 as the highest z-index token. This hardcoded value bypasses the z-index hierarchy established in the CSS variables.

Consider using the CSS variable system for consistency
 .rp-doc-layout__menu {
   display: none;
   position: fixed;
   top: 0;
   left: 0;
   right: 0;
-  z-index: 100;
+  z-index: var(--rp-z-index-nav-screen);
   ...
 }

Or define a new token in doc-layout.css if 100 is intentionally higher than nav-screen.

theme/layout/index.tsx (2)

69-95: Performance: getCustomMDXComponent() called on every anchor render.

getCustomMDXComponent() is invoked inside the a component, meaning it's called for every anchor element rendered in MDX content. Consider hoisting this call outside the component function or memoizing it.

Hoist getCustomMDXComponent call
+const CustomMDXComponent = getCustomMDXComponent();
+
 export default () => {
   useEffect(() => {
     window.parent.postMessage(window.location.href, "*");
   }, []);

   return (
     <Layout
       ...
       components={{
         a: (props: any) => {
-          const CustomMDXComponent = getCustomMDXComponent();
           const pathname = getPathname(props.href);
           if (!shouldDownload(pathname)) {
             return <CustomMDXComponent.a {...props}></CustomMDXComponent.a>;
           }
           ...
         },
       }}
     ></Layout>
   );
 };

82-85: Use React.MouseEvent instead of Event for type safety.

The event parameter type Event (line 82) should be React.MouseEvent<HTMLAnchorElement> for proper TypeScript type checking in React. Additionally, stopImmediatePropagation is not a standard React synthetic event method.

Fix event type
-              onClick={(e: Event) => {
+              onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
                 e.preventDefault();
                 e.stopPropagation();
-                e.stopImmediatePropagation?.();
                 downloadFile(
                   props.href,
                   pathname.split("/").pop() || "download"
                 );
-                return false;
               }}
theme/utils/download.ts (1)

47-51: Empty catch block silently swallows URL parsing errors.

The empty catch block provides no feedback when URL parsing fails. Consider logging a warning for debugging purposes.

Add minimal logging
       try {
         path = new URL(url).pathname;
       } catch {
+        // URL parsing failed, use url as-is
       }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b9f20d and c03da57.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (8)
  • package.json
  • styles/doc-layout.css
  • styles/index.css
  • theme/components/Card/index.tsx
  • theme/components/Checkbox/index.tsx
  • theme/layout/DocLayout/index.tsx
  • theme/layout/index.tsx
  • theme/utils/download.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • theme/components/Card/index.tsx
  • theme/components/Checkbox/index.tsx
  • theme/layout/DocLayout/index.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
theme/layout/index.tsx (1)
theme/utils/download.ts (3)
  • getPathname (16-22)
  • shouldDownload (11-14)
  • downloadFile (39-81)
🪛 Biome (2.1.2)
styles/index.css

[error] 28-28: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

font-weight is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)

🔇 Additional comments (6)
styles/doc-layout.css (3)

1-28: Well-structured CSS variables for layout tokens.

The root CSS variables establish a clear z-index hierarchy and dimension tokens. The calculated margins for sidebar and outline using max() and calc() are a good approach for centering content.


179-205: Mobile sidebar transition and visibility handling looks good.

The mobile breakpoint (≤768px) properly handles the sidebar with opacity, transform, and fixed positioning for a slide-in effect. The --open modifier correctly overrides visibility and positioning.


30-34: Media query syntax is widely supported—no changes needed.

The width >= 1280px and width <= 1279px syntax uses CSS Media Queries Level 4 range syntax, which is fully supported across all modern browsers (Chrome/Chromium 104+, Edge 104+, Firefox, Safari 16.4+, and iOS Safari 16.4+). The boundary definitions correctly avoid overlap.

styles/index.css (1)

62-109: Card and LinkCard styles are well-defined.

The component styles follow consistent patterns with proper use of CSS variables for theming and appropriate transition properties for interactive states.

theme/utils/download.ts (2)

1-9: Extension list may need expansion for common file types.

The DOWNLOAD_EXTENSIONS list includes code/data files but omits common document types like .pdf, .doc, .xlsx, .csv. Verify if this is intentional based on the documentation content.


39-71: Same-origin download flow is well-implemented.

The blob-based download approach for same-origin files correctly handles base path normalization, proper cleanup of blob URLs and DOM elements, and includes error handling with a console warning.

"packageManager": "yarn@4.12.0",
"devDependencies": {
"@alauda/doom": "^1.12.7",
"@alauda/doom": "^1.17.6",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

@alauda/doom npm package versions security

💡 Result:

Quick summary for @alauda/doom:

  • No public npm security advisories or CVEs specifically for @alauda/doom were found in my search. [1][2]
  • Package appears actively published/maintained (recent versions listed). [1]
  • Recommended action: run npm audit (or Snyk/Dependabot) against your project and pin to a specific safe version; review the package's changelog/CHANGELOG.md and repository before upgrading. (Tools: npm audit, Snyk, GitHub Dependabot).

Sources:

  1. Socket package page for @alauda/doom (versions, publish info). [1]
  2. Libraries.io / npm package listing for @alauda/doom. [2]

Would you like me to run an npm audit on your project or check a specific version?


🌐 Web query:

@alauda/doom v1.17.6 changelog breaking changes

💡 Result:

Do you want me to look up the official changelog for @alauda/doom v1.17.6 and list its breaking changes? (I can search the repo, npm, and CHANGELOG.md.)


🌐 Web query:

@alauda/doom GitHub repository changelog releases

💡 Result:

I couldn't find a public GitHub "Releases" page for an alauda/doom repository. The project appears published on npm and documented at doom.js.org; its changelog is maintained in the repository's CHANGELOG.md (referenced from the package/docs), not GitHub releases. See: (socket.dev)

Sources:

  • npm / package info mentioning CHANGELOG.md. (socket.dev)
  • Official docs/site (doom.js.org) which links project/docs. (doom.js.org)

If you want, I can search further (e.g., for a specific repo URL, GitHub user/org, or the CHANGELOG.md contents).

Citations:


🌐 Web query:

site:github.com @alauda doom package

💡 Result:

I couldn't find a GitHub repo named "doom" under the Alauda organization. Instead I found an npm package @alauda/doom and its docs site (Doom — a documentation tool by Alauda):

  • @alauda/doom (npm/package info). [1]
  • Official Doom docs / site (doom.js.org). [2]
  • Package metadata listing (libraries.io). [3]

If you want, I can search more broadly (other org/user names) or look for a specific GitHub repo file—tell me which.


🌐 Web query:

@alauda/doom npm registry version 1.17.6

💡 Result:

There is no @alauda/doom version 1.17.6 on the npm registry. The package's published versions (latest ~1.7.1 / historical list) are shown on npm and package trackers — 1.17.6 does not appear. [1][2]

Sources:
[1] npm package page for @alauda/doom.
[2] Package/version list for @alauda/doom (Socket / Libraries.io).


🌐 Web query:

"@alauda/doom" repository github alauda-io

💡 Result:

I found the @alauda/doom package (Doom docs tool) with official docs at doom.js.org and registry entries — the docs reference a GitHub repo path alauda/doom (e.g., editRepoBaseUrl: alauda/doom/tree/main/docs), but I couldn't locate a distinct public repository under an "alauda-io" org. Sources:

  • @alauda/doom package metadata (Libraries.io / npm summary). [1]
  • Official Doom documentation site (doom.js.org) which references the GitHub path alauda/doom. [2]
  • Package security/registry page showing versions and maintainers. [3]

Do you want me to (a) try to open github.com/alauda/doom directly, (b) search for a repo under a different Alauda org/name, or (c) fetch the package's npm registry page?


Version ^1.17.6 does not exist on the npm registry for @alauda/doom.

The latest available version is approximately 1.7.1. This will cause npm install to fail. Verify the intended version—it may be a typo (e.g., 1.7.6 or 1.7.1), or the package name may be incorrect.

🤖 Prompt for AI Agents
In @package.json at line 5, The package.json dependency entry for "@alauda/doom"
uses a non-existent version "^1.17.6"; update the dependency to a valid version
(e.g., change "@alauda/doom": "^1.17.6" to the correct published version such as
"1.7.1" or the intended "1.7.6"), or correct the package name if that was the
mistake, then run npm view @alauda/doom versions to confirm the chosen version
exists and npm install to verify resolution.

Comment on lines +21 to +35
.paginationItem {
font-weight: var(--font-weight-medium);
cursor: pointer;
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));

--tw-font-weight: var(--font-weight-medium) /* 500 */;
font-weight: var(--font-weight-medium) /* 500 */;

margin-inline: calc(var(--spacing) * 1.5) /* 0.375rem = 6px */;
padding-inline: calc(var(--spacing) * 3) /* 0.75rem = 12px */;
padding-block: calc(var(--spacing) * 2) /* 0.5rem = 8px */;
display: flex;
align-items: center;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove duplicate font-weight property.

Static analysis correctly identified that font-weight is declared twice (lines 22-23 and 27-28). This is redundant and could cause confusion.

Proposed fix
 .paginationItem {
-  font-weight: var(--font-weight-medium);
   cursor: pointer;
   font-size: var(--text-sm);
   line-height: var(--tw-leading, var(--text-sm--line-height));

   --tw-font-weight: var(--font-weight-medium) /* 500 */;
   font-weight: var(--font-weight-medium) /* 500 */;

   margin-inline: calc(var(--spacing) * 1.5) /* 0.375rem = 6px */;
   padding-inline: calc(var(--spacing) * 3) /* 0.75rem = 12px */;
   padding-block: calc(var(--spacing) * 2) /* 0.5rem = 8px */;
   display: flex;
   align-items: center;
 }
📝 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
.paginationItem {
font-weight: var(--font-weight-medium);
cursor: pointer;
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
--tw-font-weight: var(--font-weight-medium) /* 500 */;
font-weight: var(--font-weight-medium) /* 500 */;
margin-inline: calc(var(--spacing) * 1.5) /* 0.375rem = 6px */;
padding-inline: calc(var(--spacing) * 3) /* 0.75rem = 12px */;
padding-block: calc(var(--spacing) * 2) /* 0.5rem = 8px */;
display: flex;
align-items: center;
}
.paginationItem {
cursor: pointer;
font-size: var(--text-sm);
line-height: var(--tw-leading, var(--text-sm--line-height));
--tw-font-weight: var(--font-weight-medium) /* 500 */;
font-weight: var(--font-weight-medium) /* 500 */;
margin-inline: calc(var(--spacing) * 1.5) /* 0.375rem = 6px */;
padding-inline: calc(var(--spacing) * 3) /* 0.75rem = 12px */;
padding-block: calc(var(--spacing) * 2) /* 0.5rem = 8px */;
display: flex;
align-items: center;
}
🧰 Tools
🪛 Biome (2.1.2)

[error] 28-28: Duplicate properties can lead to unexpected behavior and may override previous declarations unintentionally.

font-weight is already defined here.

Remove or rename the duplicate property to ensure consistent styling.

(lint/suspicious/noDuplicateProperties)

🤖 Prompt for AI Agents
In @styles/index.css around lines 21 - 35, .paginationItem declares font-weight
twice; remove the redundant declaration so only one font-weight assignment
remains. In the .paginationItem rule, delete the first font-weight:
var(--font-weight-medium) (the standalone occurrence) and keep the later
--tw-font-weight and font-weight pair (or vice versa) so the class has a single
clear font-weight definition.

Comment on lines +77 to +93
return (
<CustomMDXComponent.a
{...props}
href="#"
download={pathname.split("/").pop() || "download"}
onClick={(e: Event) => {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation?.();
downloadFile(
props.href,
pathname.split("/").pop() || "download"
);
return false;
}}
></CustomMDXComponent.a>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Setting href="#" may cause accessibility and UX issues.

For download links, href="#" (line 80) can cause the page to scroll to top if the click handler fails, and screen readers may announce it incorrectly. Consider keeping the original href while relying on preventDefault().

Proposed fix
           return (
             <CustomMDXComponent.a
               {...props}
-              href="#"
+              href={props.href}
               download={pathname.split("/").pop() || "download"}
               onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
                 e.preventDefault();
-                e.stopPropagation();
-                e.stopImmediatePropagation?.();
                 downloadFile(
                   props.href,
                   pathname.split("/").pop() || "download"
                 );
-                return false;
               }}
             ></CustomMDXComponent.a>
           );
📝 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
return (
<CustomMDXComponent.a
{...props}
href="#"
download={pathname.split("/").pop() || "download"}
onClick={(e: Event) => {
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation?.();
downloadFile(
props.href,
pathname.split("/").pop() || "download"
);
return false;
}}
></CustomMDXComponent.a>
);
return (
<CustomMDXComponent.a
{...props}
href={props.href}
download={pathname.split("/").pop() || "download"}
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault();
downloadFile(
props.href,
pathname.split("/").pop() || "download"
);
}}
></CustomMDXComponent.a>
);
🤖 Prompt for AI Agents
In @theme/layout/index.tsx around lines 77 - 93, The anchor currently forces
href="#" which can break UX/accessibility; change the rendered element in the
CustomMDXComponent.a block to use the original props.href (e.g.,
href={props.href}) instead of "#" so the link remains valid for screen readers
and fallback behavior, keep the existing preventDefault()/stopPropagation() in
the onClick handler and the download prop and preserve calling
downloadFile(props.href, ...), and optionally ensure aria-label/role is set if
the link needs a button-like announcement.

Comment on lines +24 to +31
const getBasePath = (): string => {
const pathname = window.location.pathname;
const match = pathname.match(/^(\/[^/]+\/)/);
if (match) {
return match[1];
}
return "/knowledge/";
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for "knowledge" references to understand its usage
rg -n "knowledge" --type-list | grep -E "json|ts|js|config|env" || true
rg -n "knowledge" --type=ts --type=js --type=json -g '!node_modules' | head -50

Repository: alauda/knowledge

Length of output: 1366


🏁 Script executed:

#!/bin/bash
# Check the context of getBasePath function usage
rg -n "getBasePath" -A 3 -B 3 --type=ts --type=js -g '!node_modules'

Repository: alauda/knowledge

Length of output: 813


🏁 Script executed:

#!/bin/bash
# Look for base path, route prefix, or environment-related configuration
rg -n "basePath|BASE_PATH|baseUrl|BASE_URL|ROUTE|prefix" --type=ts --type=js --type=json -g '!node_modules' | head -40

Repository: alauda/knowledge

Length of output: 335


Consider deriving the fallback base path from project configuration.

The getBasePath() function attempts to dynamically extract the base path from the URL pathname, but falls back to the hardcoded "/knowledge/". While this fallback aligns with the current project configuration in doom.config.ts, it creates a maintenance risk: if the base path is changed in the build configuration, the fallback becomes stale and must be updated separately. Consider exporting the base path from build configuration or using an environment variable for the fallback to keep them synchronized.

🤖 Prompt for AI Agents
In @theme/utils/download.ts around lines 24 - 31, getBasePath currently falls
back to a hardcoded "/knowledge/" which can drift from the build config; update
getBasePath to derive its fallback from the project's configuration instead of a
literal string by importing the exported base path (or a BUILD_BASE_PATH env
var) from the build config (e.g., the value exported in doom.config.ts) and
using that as the fallback; ensure getBasePath still attempts the pathname regex
first, then returns the imported/configured base path if the regex fails.

Comment on lines +72 to +80
} else {
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.style.display = "none";
document.body.appendChild(a);
a.click();
setTimeout(() => document.body.removeChild(a), 100);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

HTML download attribute cross-origin same-origin policy behavior

💡 Result:

Short answer

  • The HTML anchor download attribute is only honored for same-origin, blob: and data: URLs; browsers ignore it for cross-origin http(s) URLs. [1][2]

Why

  • Browsers block cross-origin use of download to prevent user-mediated cross-origin info leaks / spoofing. Chromium removed support in Chrome 65; Firefox likewise only honors same-origin. [2][1]

Practical workarounds

  • Serve the file with a Content-Disposition: attachment; filename="..." header from the resource origin (or add a server-side ?dl-style endpoint that sets that header). This forces download and can control the filename. [3][4]
  • Proxy the file through your origin so the link is same-origin.
  • Fetch the resource via XHR/fetch (CORS-enabled response), create a Blob, then create an object URL and set that on an to trigger a client-side download.

Sources

  • MDN: element — download behavior and same-origin note. [1]
  • Chromium change / rationale (Chrome 65) and discussion. [2]
  • Guidance to use Content-Disposition header to force download. [3]
  • Example provider solution (append ?dl to force CDN to return Content-Disposition). [4]

🏁 Script executed:

cat -n theme/utils/download.ts | head -100

Repository: alauda/knowledge

Length of output: 2938


Cross-origin downloads will fail silently.

The download attribute is ignored by browsers for cross-origin URLs (Chrome 65+, Firefox). In the else branch (lines 72-80), the file will open in a new tab instead of downloading, and the filename will not be controlled. The same-origin branch correctly uses a blob URL to work around this, but the cross-origin case lacks any fallback or user-facing warning.

Consider adding a warning to users about cross-origin files, or fetch and proxy the file through your origin to enable proper downloads.

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