Skip to content

Updating theme of the website#436

Open
srinivaspendem wants to merge 9 commits intomasterfrom
new-theme
Open

Updating theme of the website#436
srinivaspendem wants to merge 9 commits intomasterfrom
new-theme

Conversation

@srinivaspendem
Copy link
Copy Markdown
Member

@srinivaspendem srinivaspendem commented Apr 23, 2026

Summary by CodeRabbit

  • New Features

    • Docs theme: dynamic layout slot forwarding, richer cards (link/description/CTA), and card-group column aliasing.
    • Homepage converted to document layout with card-driven feature tiles.
  • Style

    • Visual refresh: responsive layout tweaks, navbar and CTA styling, and improved dark-mode handling that applies earlier.
  • Chores

    • Tooling and typing updates: editor preferences, TypeScript/VitePress typings, theme dependency, and build/config adjustments.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Apr 24, 2026 4:08pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Switches the docs site to the VoidZero VitePress theme, adds a slot-forwarding Layout, enhances Card/CardGroup components, implements dark-mode pre-hydration sync and runtime sign-in CTA relocation, updates styles and TypeScript/VSCode configs, and converts the homepage to inline component usage.

Changes

Cohort / File(s) Summary
VitePress config
docs/.vitepress/config.ts
Refactors export to extendConfig(config), sets themeConfig.variant = "voidzero" and siteTitle = "Plane", tweaks Sign in nav entry, and injects a head script for pre-hydration dark-mode toggling.
Theme entry & boot
docs/.vitepress/theme/index.ts
Replaces DefaultTheme with @voidzero-dev/vitepress-theme (VoidZeroTheme), exports Layout, updates enhanceApp(ctx) to register components, provide planeThemeContext, synchronize dark-mode with DOM, and robustly relocate the sign-in CTA via selector retries, MutationObserver, debounced resize/route triggers.
Layout & slot forwarding
docs/.vitepress/theme/Layout.vue
New wrapper that imports VoidZero Layout as BaseLayout, enumerates and forwards named slots with bound props and local <slot> fallbacks.
Cards & grouping
docs/.vitepress/theme/components/Card.vue, docs/.vitepress/theme/components/CardGroup.vue
Card.vue: adds link, description, cta props; computes resolvedHref; conditionally renders <a> vs <div>; conditional description/CTA and updated icon stroke. CardGroup.vue: adds columns alias and uses computed columnCount.
Theme styles
docs/.vitepress/theme/style.css
Replaces Tailwind imports with @voidzero-dev/vitepress-theme stylesheet, scopes .hidden, restyles navbar/search/sign-in CTA, adds homepage feature card styles, and refactors desktop doc layout/aside behavior including conditional right-rail hiding.
Docs content
docs/index.md
Converts homepage from home layout to doc layout; replaces YAML hero/features with inline title/intro and CardGroup/Card usage (icons as names, cta fields).
Types & TS config
docs/.vitepress/types/modules.d.ts, tsconfig.json
Adds module declarations for *.vue and vitepress-plugin-tabs/client; switches TS moduleResolution to node; adds Node and VitePress client types; updates project include to docs/.vitepress/**.
Editor & package
.vscode/settings.json, package.json
Adds VSCode setting to ignore unknown at-rules for CSS/SCSS/LESS; adds @voidzero-dev/vitepress-theme dependency and @types/node devDependency.

Sequence Diagram(s)

sequenceDiagram
  participant App as VitePress App
  participant Theme as VoidZeroTheme
  participant Layout as Plane Layout
  participant DOM as Document / Header
  participant Router as Router

  App->>Theme: enhanceApp(ctx)
  Theme->>App: register components & provide planeThemeContext
  App->>Layout: mount Layout
  Layout->>DOM: locate header & sign-in CTA
  DOM-->>Layout: sign-in present / absent
  Layout->>DOM: relocate CTA into header (with retries & hidden state)
  Layout->>DOM: apply/remove `dark` class and `data-theme="dark"` (sync from isDark/localStorage)
  Router->>Layout: on route change -> trigger relocate and theme sync
  DOM->>Layout: MutationObserver/resize -> debounce relocate attempts
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through slots and pushed a sign-in near,
I nudged the dark class when the moon drew clear,
Cards grew labels, CTAs to show,
Styles stitched pages where the docs now glow,
A tiny rabbit left a gleeful ear.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Updating theme of the website' is partially related to the changeset and accurately describes a key aspect of the changes—migrating from DefaultTheme to VoidZeroTheme and updating visual styling. However, the title is somewhat generic and doesn't capture the specific scope of the work, such as layout restructuring, component updates, or the theme provider migration.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch new-theme

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
Copy Markdown

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

Caution

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

⚠️ Outside diff range comments (2)
docs/.vitepress/theme/components/Card.vue (1)

1-1: ⚠️ Potential issue | 🟡 Minor

Fix the formatting failure before merge.

CI reports oxfmt --check failures for this file; please run pnpm fix:format and confirm pnpm check:format passes. As per coding guidelines, Run pnpm fix:format before committing and ensure CI check via pnpm check:format passes; formatting is enforced.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/components/Card.vue` at line 1, CI flagged a formatting
error in docs/.vitepress/theme/components/Card.vue; run the project's formatter
and commit the result by running the repository scripts to fix and verify
formatting (execute pnpm fix:format then pnpm check:format) and ensure the
updated Card.vue (the <script setup> block) is saved/committed so the CI oxfmt
--check passes.
package.json (1)

31-35: ⚠️ Potential issue | 🟠 Major

Align Node typings with the declared runtime floor.

@types/node is pinned to version 25 major, but engines.node specifies >=24.0.0. This mismatch allows Node 25-only APIs to pass type-checking while failing at runtime on Node 24. Align the versions by using Node 24 typings or raising the minimum Node version.

Proposed fix
-    "@types/node": "^25.6.0",
+    "@types/node": "^24.0.0",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 31 - 35, The `@types/node` dev dependency (symbol:
"@types/node") does not match the declared runtime floor (symbol:
"engines.node"), causing potential type-checks for Node 25 APIs while the
runtime may be Node 24; fix by either downgrading "@types/node" to a 24.x major
(e.g., "^24.0.0") to match "engines.node": ">=24.0.0" or by raising
"engines.node" to ">=25.0.0" so it aligns with the installed "@types/node"
major—update package.json accordingly and run a reinstall/type-check to verify
no new type/runtime mismatches remain.
🧹 Nitpick comments (2)
docs/.vitepress/theme/index.ts (1)

129-139: Verify slot forwarding behavior with VoidZeroTheme defaults.

The Layout.vue file uses useSlots() with Object.keys(slots) to forward slots, which only forwards slots the parent actively provides. If VoidZeroTheme.Layout has default content for slots like home-hero-before that aren't explicitly overridden, those defaults won't render. Test with a commonly-used VoidZero slot to confirm the theme's default fallbacks still appear when not overridden, or explicitly enumerate known slots from the base theme to ensure all expected defaults are preserved.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/index.ts` around lines 129 - 139, The Layout
enhancement currently forwards only slots provided by the parent (via
useSlots/Object.keys) and can suppress VoidZeroTheme's default slot content
(e.g., home-hero-before); update enhanceApp/Layout handling to ensure theme
defaults are preserved by forwarding either the full list of known VoidZeroTheme
slots or delegating slot forwarding to VoidZeroTheme.Layout: locate the Layout
component and its slot forwarding logic (referencing Layout and VoidZeroTheme),
then replace Object.keys(slots) usage with an explicit array of base-theme slot
names (include commonly used names such as "home-hero-before",
"home-hero-after", etc.) or call through to VoidZeroTheme.Layout's slot
resolution so default content in the base theme still appears when parents don’t
override those slots.
docs/.vitepress/theme/style.css (1)

699-709: CSS selectors unnecessarily couple to Tailwind utility class names.

The rules targeting a[href="/"].flex.flex-col and img.h-4 will silently break if the upstream @voidzero-dev/vitepress-theme (currently ^4.8.4) changes how it renders the OSSHeader logo markup—for instance, swapping flex-col for flex-row or h-4 for a different size utility. No build-time error signals the breakage.

Consider requesting stable customization hooks from the theme (e.g., a custom class or data-* attribute on the logo container), or refactor these rules to target only header a[href="/"] and override flex-direction/height without depending on the presence of specific utility classes. If the theme provides no documented override point, this workaround may be necessary for now.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/style.css` around lines 699 - 709, The CSS selectors
a[href="/"].flex.flex-col and img.h-4 are too tightly coupled to upstream
Tailwind utilities and will break if the theme changes those classes; update the
selectors to target only the stable elements (e.g., header a[href="/"] and
header a[href="/"] img) and apply the needed overrides (flex-direction,
align-items, gap, height) without relying on .flex .flex-col or .h-4; if the
theme exposes a stable hook (class or data-*), switch selectors to that hook
instead and document the change so OSSHeader styling is resilient to
markup/utility-class changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/theme/components/Card.vue`:
- Line 18: The computed resolvedHref currently falls back to "#" which makes
non-link cards act like anchors; change the computed (resolvedHref) to return
null/undefined when neither props.link nor props.href are provided, and update
the template logic that renders the card (where an anchor element is used) to
render a non-anchor wrapper (e.g., a div/button or plain element) when
resolvedHref is falsy; ensure you reference props.link and props.href in the new
computed and adjust the conditional rendering used around the anchor (the same
spots that currently use resolvedHref) so informational cards are not clickable
nor navigate to "#" anymore.

In `@docs/.vitepress/theme/index.ts`:
- Around line 97-127: The relocation logic in moveSignInToUtilityArea is
race-prone and brittle (first-paint, header re-renders, selector changes);
update it to reliably relocate the sign-in link by observing the header DOM
until the target appears: use a MutationObserver on the header (or a short retry
loop) to look for the sign-in anchor and the appearance/extra menu elements,
perform the insertBefore and add the "sign-in-relocated" class once, then
disconnect the observer; also add a debounced window resize handler to re-run
the relocation with a guard to avoid repeated work, and emit a console.warn when
the function bails because selectors are not found so regressions are visible
(reference moveSignInToUtilityArea, ".sign-in-relocated", and the
appearance/extra menu selectors).
- Around line 132-139: The call to VoidZeroTheme.enhanceApp is currently invoked
directly in enhanceApp(ctx) and can throw if that method is undefined in future
versions; change the call to use optional chaining
(VoidZeroTheme.enhanceApp?.(ctx)) to guard against undefined, and optionally
move ctx.app.provide(themeContextKey, planeThemeContext) to after the
VoidZeroTheme.enhanceApp?.(ctx) call so the child theme's provided value cannot
be overwritten by the base theme; update the enhanceApp function accordingly
(refer to enhanceApp, VoidZeroTheme.enhanceApp, ctx.app.provide,
themeContextKey, planeThemeContext).

In `@docs/.vitepress/theme/style.css`:
- Around line 749-753: The CSS rule .docs-layout header
.VPLink[href*="sign-in"]:not(.sign-in-relocated) currently hides the Sign-in
link for no-JS or failed-JS cases; update the relocation flow so the link is
visible by default and only hidden while awaiting relocation: either (A) add an
initial "not-yet-relocated" marker class to the Sign-in element server-side or
from your startup JS and have moveSignInToUtilityArea() remove that marker and
add .sign-in-relocated, or (B) narrow the CSS selector to target only the
in-menu container (e.g., scope to .VPNavBarMenu
.VPLink[href*="sign-in"]:not(.sign-in-relocated)) so the link remains reachable
if relocation fails; modify the code that manipulates .sign-in-relocated in
index.ts (moveSignInToUtilityArea) accordingly.
- Around line 612-616: The selector uses the Vue SFC scoped-styles pseudo-class
:deep which is invalid in a global CSS file; update the rule targeting .vp-doc
.home-feature-cards .card-icon :deep(svg) to target the SVG directly (remove the
:deep wrapper) so the browser sees a valid selector and keep the
width/height/flex-shrink declarations for the svg element.

In `@docs/index.md`:
- Line 1: The repo-wide formatting failed for docs/index.md; run the project
formatter and checks: execute pnpm fix:format to apply oxfmt formatting changes
to docs/index.md, commit the updated file, and verify pnpm check:format passes
in your local CI; ensure you do not modify content beyond formatting and include
the formatted docs/index.md in the same commit so the CI oxfmt --check will
succeed.
- Line 11: The H1 currently includes the tagline ("# Plane Documentation Plan,
track, and ship your work with Plane.") — split them by making the H1 just the
title (e.g., "# Plane Documentation") and move the tagline ("Plan, track, and
ship your work with Plane.") into its own body paragraph directly under the H1
in docs/index.md so the heading and tagline render separately.

---

Outside diff comments:
In `@docs/.vitepress/theme/components/Card.vue`:
- Line 1: CI flagged a formatting error in
docs/.vitepress/theme/components/Card.vue; run the project's formatter and
commit the result by running the repository scripts to fix and verify formatting
(execute pnpm fix:format then pnpm check:format) and ensure the updated Card.vue
(the <script setup> block) is saved/committed so the CI oxfmt --check passes.

In `@package.json`:
- Around line 31-35: The `@types/node` dev dependency (symbol: "@types/node") does
not match the declared runtime floor (symbol: "engines.node"), causing potential
type-checks for Node 25 APIs while the runtime may be Node 24; fix by either
downgrading "@types/node" to a 24.x major (e.g., "^24.0.0") to match
"engines.node": ">=24.0.0" or by raising "engines.node" to ">=25.0.0" so it
aligns with the installed "@types/node" major—update package.json accordingly
and run a reinstall/type-check to verify no new type/runtime mismatches remain.

---

Nitpick comments:
In `@docs/.vitepress/theme/index.ts`:
- Around line 129-139: The Layout enhancement currently forwards only slots
provided by the parent (via useSlots/Object.keys) and can suppress
VoidZeroTheme's default slot content (e.g., home-hero-before); update
enhanceApp/Layout handling to ensure theme defaults are preserved by forwarding
either the full list of known VoidZeroTheme slots or delegating slot forwarding
to VoidZeroTheme.Layout: locate the Layout component and its slot forwarding
logic (referencing Layout and VoidZeroTheme), then replace Object.keys(slots)
usage with an explicit array of base-theme slot names (include commonly used
names such as "home-hero-before", "home-hero-after", etc.) or call through to
VoidZeroTheme.Layout's slot resolution so default content in the base theme
still appears when parents don’t override those slots.

In `@docs/.vitepress/theme/style.css`:
- Around line 699-709: The CSS selectors a[href="/"].flex.flex-col and img.h-4
are too tightly coupled to upstream Tailwind utilities and will break if the
theme changes those classes; update the selectors to target only the stable
elements (e.g., header a[href="/"] and header a[href="/"] img) and apply the
needed overrides (flex-direction, align-items, gap, height) without relying on
.flex .flex-col or .h-4; if the theme exposes a stable hook (class or data-*),
switch selectors to that hook instead and document the change so OSSHeader
styling is resilient to markup/utility-class changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: dcce00e2-11a6-4df6-982e-961846857dce

📥 Commits

Reviewing files that changed from the base of the PR and between dbacd6e and 781e2b9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • .vscode/settings.json
  • docs/.vitepress/config.ts
  • docs/.vitepress/theme/Layout.vue
  • docs/.vitepress/theme/components/Card.vue
  • docs/.vitepress/theme/components/CardGroup.vue
  • docs/.vitepress/theme/index.ts
  • docs/.vitepress/theme/style.css
  • docs/.vitepress/types/modules.d.ts
  • docs/index.md
  • package.json
  • tsconfig.json

Comment thread docs/.vitepress/theme/components/Card.vue Outdated
Comment thread docs/.vitepress/theme/index.ts Outdated
Comment thread docs/.vitepress/theme/index.ts
Comment thread docs/.vitepress/theme/style.css Outdated
Comment thread docs/.vitepress/theme/style.css Outdated
Comment thread docs/index.md
Comment thread docs/index.md Outdated
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (2)
docs/.vitepress/theme/index.ts (1)

244-263: Symmetric cleanup for the poll-fallback interval and the hashchange listener.

The rest of this onMounted block is careful about teardown (headerObserver?.disconnect(), resize listener removed), but two hooks leak past unmount in principle:

  • The setInterval(id, 120) / 5s setTimeout(...) fallback on Lines 245-251 captures id in closure; neither is tracked for onUnmounted.
  • The hashchange handler added on Lines 260-262 uses an anonymous callback and is never removed.

In practice the theme setup() only runs once per app load so this isn't user-visible, but mirroring the cleanup pattern you already established avoids surprise if this code is ever reused in a more dynamic host (HMR, Storybook, tests).

🧹 Suggested cleanup
-      const onHeaderMutations = debounce(() => {
+      const onHeaderMutations = debounce(() => {
         runSignInRelocationWithRetries();
       }, 100);
 
       const tryAttachHeaderObserver = () => {
         if (headerObserver) return;
         const h = document.querySelector(".docs-layout header");
         if (!h) return;
         headerObserver = new MutationObserver(onHeaderMutations);
         headerObserver.observe(h, { childList: true, subtree: true });
       };
       tryAttachHeaderObserver();
+      let pollIntervalId: number | undefined;
+      let pollTimeoutId: number | undefined;
       if (!headerObserver) {
-        const id = window.setInterval(() => {
+        pollIntervalId = window.setInterval(() => {
           tryAttachHeaderObserver();
           if (headerObserver) {
-            clearInterval(id);
+            if (pollIntervalId !== undefined) clearInterval(pollIntervalId);
           }
         }, 120);
-        window.setTimeout(() => clearInterval(id), 5000);
+        pollTimeoutId = window.setTimeout(() => {
+          if (pollIntervalId !== undefined) clearInterval(pollIntervalId);
+        }, 5000);
       }
 
       onResize = debounce(() => {
         runSignInRelocationWithRetries();
       }, 150);
       window.addEventListener("resize", onResize);
 
-      // Listen for hash changes
-      window.addEventListener("hashchange", () => {
-        nextTick(handleTabHash);
-      });
+      onHashChange = () => {
+        nextTick(handleTabHash);
+      };
+      window.addEventListener("hashchange", onHashChange);
     });
 
     onUnmounted(() => {
       headerObserver?.disconnect();
       headerObserver = null;
       if (onResize) {
         window.removeEventListener("resize", onResize);
         onResize = null;
       }
+      if (onHashChange) {
+        window.removeEventListener("hashchange", onHashChange);
+        onHashChange = null;
+      }
+      if (pollIntervalId !== undefined) clearInterval(pollIntervalId);
+      if (pollTimeoutId !== undefined) clearTimeout(pollTimeoutId);
     });

(Declare let onHashChange: (() => void) | null = null; alongside the existing let headerObserver / let onResize declarations.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/index.ts` around lines 244 - 263, The poll-fallback
interval/timeout and the anonymous hashchange listener are never cleaned up on
unmount; declare tracking variables (e.g., let pollId: number | null = null, let
pollTimeoutId: number | null = null, and let onHashChange: (() => void) | null =
null) alongside headerObserver and onResize, assign pollId/pollTimeoutId when
calling window.setInterval/window.setTimeout and assign the hash handler to
onHashChange (call nextTick(handleTabHash) inside it), then in the existing
onUnmounted cleanup block clearInterval(pollId), clearTimeout(pollTimeoutId),
and removeEventListener("hashchange", onHashChange) (and keep removing the
resize listener via onResize) so all resources (headerObserver, resize, poll
interval/timeout, and hashchange) are torn down symmetrically.
docs/.vitepress/theme/style.css (1)

699-709: Consider using stable selectors instead of Tailwind utility classes for OSSHeader overrides.

The CSS rules coupling to .flex.flex-col and .h-4 depend on the exact Tailwind utility classes emitted by @voidzero-dev/vitepress-theme's OSSHeader component. Since the package uses semver ^4.8.4, minor version updates (e.g., 4.9.x) can change component markup without breaking the version constraint. If OSSHeader's classes change, these rules will silently no-op, reverting the logo to its stacked layout.

Use more stable selectors instead: scope via [class*="Logo"] or target the anchor by aria-label attribute. Alternatively, pin @voidzero-dev/vitepress-theme to a specific version so these coupled rules are revisited and tested at upgrade time.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/style.css` around lines 699 - 709, The current CSS
targets fragile Tailwind utilities `.docs-layout header
a[href="/"].flex.flex-col` and `.docs-layout header a[href="/"] img.h-4` which
may break if OSSHeader markup changes; update these selectors to stable ones
(e.g., target the OSSHeader anchor via an ARIA attribute like
`a[aria-label="Home"]` or a class name pattern such as `[class*="Logo"]` and
adjust the image selector accordingly) so the layout override survives minor
theme updates, or alternatively pin the `@voidzero-dev/vitepress-theme`
dependency to a fixed patch version to ensure these utility classes are
re-evaluated on upgrade; modify the selectors referenced above (the anchor and
image rules) to use the chosen stable selector approach.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@docs/.vitepress/theme/index.ts`:
- Around line 244-263: The poll-fallback interval/timeout and the anonymous
hashchange listener are never cleaned up on unmount; declare tracking variables
(e.g., let pollId: number | null = null, let pollTimeoutId: number | null =
null, and let onHashChange: (() => void) | null = null) alongside headerObserver
and onResize, assign pollId/pollTimeoutId when calling
window.setInterval/window.setTimeout and assign the hash handler to onHashChange
(call nextTick(handleTabHash) inside it), then in the existing onUnmounted
cleanup block clearInterval(pollId), clearTimeout(pollTimeoutId), and
removeEventListener("hashchange", onHashChange) (and keep removing the resize
listener via onResize) so all resources (headerObserver, resize, poll
interval/timeout, and hashchange) are torn down symmetrically.

In `@docs/.vitepress/theme/style.css`:
- Around line 699-709: The current CSS targets fragile Tailwind utilities
`.docs-layout header a[href="/"].flex.flex-col` and `.docs-layout header
a[href="/"] img.h-4` which may break if OSSHeader markup changes; update these
selectors to stable ones (e.g., target the OSSHeader anchor via an ARIA
attribute like `a[aria-label="Home"]` or a class name pattern such as
`[class*="Logo"]` and adjust the image selector accordingly) so the layout
override survives minor theme updates, or alternatively pin the
`@voidzero-dev/vitepress-theme` dependency to a fixed patch version to ensure
these utility classes are re-evaluated on upgrade; modify the selectors
referenced above (the anchor and image rules) to use the chosen stable selector
approach.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bbb83923-feaa-4e26-8d3e-6d1c824ec11b

📥 Commits

Reviewing files that changed from the base of the PR and between 781e2b9 and b9b7b89.

📒 Files selected for processing (4)
  • docs/.vitepress/theme/components/Card.vue
  • docs/.vitepress/theme/index.ts
  • docs/.vitepress/theme/style.css
  • docs/index.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/index.md
  • docs/.vitepress/theme/components/Card.vue

Copy link
Copy Markdown

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

🧹 Nitpick comments (4)
docs/.vitepress/theme/index.ts (2)

275-287: hashchange listener isn't cleaned up.

The resize listener and MutationObserver are both torn down in onUnmounted, but the inline hashchange handler added on Line 275 is never removed. It is unlikely to cause user-visible issues since the theme setup() is called once per app, but HMR cycles in dev and any future teardown path will accumulate duplicate listeners.

🔧 Suggested fix
+      const onHashChange = () => {
+        nextTick(handleTabHash);
+      };
-      // Listen for hash changes
-      window.addEventListener("hashchange", () => {
-        nextTick(handleTabHash);
-      });
+      // Listen for hash changes
+      window.addEventListener("hashchange", onHashChange);
     });
 
     onUnmounted(() => {
       headerObserver?.disconnect();
       headerObserver = null;
       if (onResize) {
         window.removeEventListener("resize", onResize);
         onResize = null;
       }
+      window.removeEventListener("hashchange", onHashChange);
     });

(onHashChange will need to be hoisted to a scope visible to both hooks.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/index.ts` around lines 275 - 287, The hashchange
listener added via window.addEventListener("hashchange", () => {
nextTick(handleTabHash); }) is not removed in onUnmounted; hoist the handler
into a named function/const (e.g., onHashChange) in the outer setup scope so it
can be referenced in both the add and remove calls, replace the inline arrow
with that onHashChange when adding the listener, and call
window.removeEventListener("hashchange", onHashChange) inside onUnmounted
alongside headerObserver.disconnect() and the resize cleanup.

239-272: Minor: overlapping retry chains from multiple triggers.

runSignInRelocationWithRetries is invoked from the initial setTimeout, every debounced mutation, every debounced resize, and each route change. moveSignInToUtilityArea is idempotent (short-circuits on .sign-in-relocated), so nothing breaks, but in the worst case several independent 40×75ms retry chains can run concurrently. Consider a single in-flight flag or clearing any pending retry before scheduling the next chain to avoid redundant timers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/index.ts` around lines 239 - 272, Multiple concurrent
retry chains are started because runSignInRelocationWithRetries is called from
the initial setTimeout, mutation debounce, resize debounce, and route change; to
fix, add a single in-flight guard or a cancel mechanism inside
runSignInRelocationWithRetries (or a new wrapper like scheduleSignInRelocation)
that tracks a pendingRetry boolean/timeoutId and returns early if one is active
(or clears the existing timeout before scheduling a new chain), reference the
existing runSignInRelocationWithRetries and moveSignInToUtilityArea functions
and update callers (the setTimeout initializer, the debounce callbacks that call
runSignInRelocationWithRetries, and the route-change invoker) to use the
guarded/wrapper function so only one retry chain runs at a time.
docs/.vitepress/theme/style.css (1)

838-862: Consider consolidating the repeated :has() selector.

The long compound selector .VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds)) is repeated verbatim across three rules. A small refactor (CSS nesting, or a single rule with a comma-joined selector list for the child targets) improves maintainability and avoids drift if one branch is updated in isolation later.

♻️ Proposed refactor (comma-joined targets)
-  .docs-layout
-    .VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds))
-    .aside {
-    display: none !important;
-    flex: 0 0 0 !important;
-    width: 0 !important;
-    min-width: 0 !important;
-    max-width: 0 !important;
-    margin: 0 !important;
-    padding: 0 !important;
-    overflow: hidden !important;
-  }
-
-  .docs-layout
-    .VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds))
-    .content-container {
-    max-width: 100% !important;
-  }
-
-  .docs-layout
-    .VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds))
-    .content {
-    max-width: 100% !important;
-  }
+  .docs-layout
+    .VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds)) {
+    & .aside {
+      display: none !important;
+      flex: 0 0 0 !important;
+      width: 0 !important;
+      min-width: 0 !important;
+      max-width: 0 !important;
+      margin: 0 !important;
+      padding: 0 !important;
+      overflow: hidden !important;
+    }
+    & .content-container,
+    & .content {
+      max-width: 100% !important;
+    }
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/style.css` around lines 838 - 862, The three rules
repeat the long compound selector `.docs-layout
.VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds))`
for `.aside`, `.content-container`, and `.content`; consolidate by creating a
single rule for that parent selector and target the three child selectors
together (or use CSS nesting) so the shared selector exists only once, keeping
the same declarations for `.aside` and the max-width rules for
`.content-container` and `.content`; reference the selectors `.docs-layout`,
`.VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds))`,
and the child targets `.aside`, `.content-container`, `.content` when applying
the refactor.
docs/.vitepress/config.ts (1)

206-222: Small simplification: !p branch is unreachable.

Because p = localStorage.getItem(k) || "dark" guarantees p is a non-empty string, !p in d=!p||p==="auto"?m:p==="dark" is always false. Not a bug — the logic is still correct — but the expression can be simplified for clarity.

♻️ Proposed simplification
-var p=localStorage.getItem(k)||"dark";
+var p=localStorage.getItem(k);
 var m=matchMedia("(prefers-color-scheme: dark)").matches;
-var d=!p||p==="auto"?m:p==="dark";
+var d=!p||p==="auto"?m:p==="dark"; /* default to system when no preference stored */

Or, keep the default and drop !p:

-var p=localStorage.getItem(k)||"dark";
-var d=!p||p==="auto"?m:p==="dark";
+var p=localStorage.getItem(k)||"dark";
+var d=p==="auto"?m:p==="dark";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/config.ts` around lines 206 - 222, The ternary that computes
d uses an unreachable condition (!p) because p is set via p =
localStorage.getItem(k) || "dark" and thus always a non-empty string; simplify d
calculation by removing the !p branch and change d=!p||p==="auto"?m:p==="dark"
to a clearer expression that checks p === "auto" first (e.g., p === "auto" ? m :
p === "dark") so the variables p and d inside the inline IIFE and function bar
remain correct and behavior is unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/config.ts`:
- Around line 225-232: The themeConfig currently sets variant: "voidzero", which
is unsupported by `@voidzero-dev/vitepress-theme`; update the variant property
inside themeConfig to one of the supported values ("vite", "viteplus", "vitest",
"rolldown", or "oxc") that best matches your project (e.g., change variant in
themeConfig from "voidzero" to "vite" or another supported option) so the theme
renders and builds correctly.

In `@docs/.vitepress/theme/index.ts`:
- Line 1: Formatting failed for the module that contains the import line "import
type { Theme } from \"vitepress\""; run the repository formatter and commit the
result: execute pnpm fix:format, verify the formatter fixed the file, then run
pnpm check:format to ensure CI will pass and commit the formatted changes
(ensure the file with the import is included in the commit).

---

Nitpick comments:
In `@docs/.vitepress/config.ts`:
- Around line 206-222: The ternary that computes d uses an unreachable condition
(!p) because p is set via p = localStorage.getItem(k) || "dark" and thus always
a non-empty string; simplify d calculation by removing the !p branch and change
d=!p||p==="auto"?m:p==="dark" to a clearer expression that checks p === "auto"
first (e.g., p === "auto" ? m : p === "dark") so the variables p and d inside
the inline IIFE and function bar remain correct and behavior is unchanged.

In `@docs/.vitepress/theme/index.ts`:
- Around line 275-287: The hashchange listener added via
window.addEventListener("hashchange", () => { nextTick(handleTabHash); }) is not
removed in onUnmounted; hoist the handler into a named function/const (e.g.,
onHashChange) in the outer setup scope so it can be referenced in both the add
and remove calls, replace the inline arrow with that onHashChange when adding
the listener, and call window.removeEventListener("hashchange", onHashChange)
inside onUnmounted alongside headerObserver.disconnect() and the resize cleanup.
- Around line 239-272: Multiple concurrent retry chains are started because
runSignInRelocationWithRetries is called from the initial setTimeout, mutation
debounce, resize debounce, and route change; to fix, add a single in-flight
guard or a cancel mechanism inside runSignInRelocationWithRetries (or a new
wrapper like scheduleSignInRelocation) that tracks a pendingRetry
boolean/timeoutId and returns early if one is active (or clears the existing
timeout before scheduling a new chain), reference the existing
runSignInRelocationWithRetries and moveSignInToUtilityArea functions and update
callers (the setTimeout initializer, the debounce callbacks that call
runSignInRelocationWithRetries, and the route-change invoker) to use the
guarded/wrapper function so only one retry chain runs at a time.

In `@docs/.vitepress/theme/style.css`:
- Around line 838-862: The three rules repeat the long compound selector
`.docs-layout
.VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds))`
for `.aside`, `.content-container`, and `.content`; consolidate by creating a
single rule for that parent selector and target the three child selectors
together (or use CSS nesting) so the shared selector exists only once, keeping
the same declarations for `.aside` and the max-width rules for
`.content-container` and `.content`; reference the selectors `.docs-layout`,
`.VPDoc.has-aside:has(.VPDocAsideOutline:not(.has-outline)):not(:has(.VPDocAsideCarbonAds))`,
and the child targets `.aside`, `.content-container`, `.content` when applying
the refactor.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: af8cc70a-b4f4-4b5f-bb0a-4315aefd3b46

📥 Commits

Reviewing files that changed from the base of the PR and between b9b7b89 and 69f425d.

📒 Files selected for processing (4)
  • docs/.vitepress/config.ts
  • docs/.vitepress/theme/index.ts
  • docs/.vitepress/theme/style.css
  • docs/index.md

Comment thread docs/.vitepress/config.ts
Comment on lines 225 to 232
themeConfig: {
variant: "voidzero",
logo: {
light: "https://media.docs.plane.so/logo/new-logo-white.png",
dark: "https://media.docs.plane.so/logo/new-logo-dark.png",
},
siteTitle: "",
siteTitle: "Plane",

Copy link
Copy Markdown

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:

@voidzero-dev/vitepress-theme themeConfig.variant supported values list

💡 Result:

The supported values for themeConfig.variant in @voidzero-dev/vitepress-theme are strings corresponding to VoidZero projects, such as "viteplus", "vite", "vitest". Additional likely values based on project context include "rolldown" and "oxc", as the theme is designed for Vite+, Vite, Vitest, Rolldown, and Oxc. The variant controls branding colors and styling via CSS selectors like :root[data-variant="vite"]. Users import specific variants like '@voidzero-dev/vitepress-theme/src/vite' in the theme entry.

Citations:


Change variant: "voidzero" to a supported value.

The @voidzero-dev/vitepress-theme package only supports variants: "vite", "viteplus", "vitest", "rolldown", and "oxc". The variant "voidzero" is not recognized and will cause incorrect theme rendering or build failures. Use one of the supported variants that matches your target project.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/config.ts` around lines 225 - 232, The themeConfig currently
sets variant: "voidzero", which is unsupported by `@voidzero-dev/vitepress-theme`;
update the variant property inside themeConfig to one of the supported values
("vite", "viteplus", "vitest", "rolldown", or "oxc") that best matches your
project (e.g., change variant in themeConfig from "voidzero" to "vite" or
another supported option) so the theme renders and builds correctly.

Comment thread docs/.vitepress/theme/index.ts
Copy link
Copy Markdown

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

🧹 Nitpick comments (2)
docs/.vitepress/theme/index.ts (2)

272-276: hashchange listener is never removed.

The anonymous hashchange listener registered inside onMounted has no paired removeEventListener in onUnmounted (lines 278–285). Given the new onUnmounted cleanup block explicitly disposes the resize listener and the MutationObserver, this one is inconsistent and will leak if setup is ever re-run.

♻️ Suggested change
+      const onHashChange = () => {
+        nextTick(handleTabHash);
+      };
-      // Listen for hash changes
-      window.addEventListener("hashchange", () => {
-        nextTick(handleTabHash);
-      });
+      window.addEventListener("hashchange", onHashChange);
+      // store on closure variable declared alongside onResize for cleanup

And in onUnmounted, add a matching removeEventListener("hashchange", onHashChange).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/index.ts` around lines 272 - 276, The hashchange
listener added inside onMounted uses an anonymous callback and is never removed;
rename the callback to a stable function (e.g., onHashChange) that calls
nextTick(handleTabHash), register window.addEventListener("hashchange",
onHashChange) in onMounted, and remove it in onUnmounted via
window.removeEventListener("hashchange", onHashChange) alongside the existing
resize observer and MutationObserver cleanup.

152-175: Retry chains are not cancelable across route changes.

runSignInRelocationWithRetries schedules up to 40 self-chained setTimeouts (~3s). It is called from initial mount, the MutationObserver callback, resize, and every route.path change. Each call starts a fresh independent chain with no token to cancel earlier ones, so rapid navigation can stack multiple overlapping retry chains. They're harmless once the link has the sign-in-relocated class (early return in moveSignInToUtilityArea), but they keep running after unmount.

Consider tracking the current attempt's timer id in a module-scoped variable and clearing it at the start of each new invocation (and in onUnmounted) so retries are bounded to the latest call and don't outlive the component.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/index.ts` around lines 152 - 175,
runSignInRelocationWithRetries starts many independent setTimeout chains that
are never cancelled; to fix, add a module-scoped variable (e.g.,
currentSignInTimeoutId) and clearTimeout at the start of
runSignInRelocationWithRetries before scheduling tryOnce, then have tryOnce
assign each window.setTimeout return to that variable so the chain can be
cleared; also clear that timeout in the component's onUnmounted handler to
prevent retries after unmount; keep existing early return in
moveSignInToUtilityArea and preserve signInRelocateWarned logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/theme/index.ts`:
- Around line 257-265: The interval and timeout created when headerObserver is
not yet set (window.setInterval in the initialization block) aren't cleared on
component unmount, and a later-attached MutationObserver may outlive the
component; update the logic so that the interval id and timeout id are stored in
local variables accessible to the onUnmounted handler and ensure onUnmounted
clears both the interval and timeout and also disconnects headerObserver if set;
reference the interval/timeout creation around tryAttachHeaderObserver and the
existing onUnmounted handler that disconnects headerObserver to add the cleanup.

In `@docs/index.md`:
- Line 35: The card titles in docs/index.md are using inconsistent casing (e.g.,
"Pages and Wiki" vs "Import and export"); pick one convention (suggest sentence
case) and update all six card title strings — including "Pages and Wiki" and
"Import and export" — to follow that convention consistently across the homepage
grid; search for the other card title literals in the file and replace them so
every card uses the same casing style.

---

Nitpick comments:
In `@docs/.vitepress/theme/index.ts`:
- Around line 272-276: The hashchange listener added inside onMounted uses an
anonymous callback and is never removed; rename the callback to a stable
function (e.g., onHashChange) that calls nextTick(handleTabHash), register
window.addEventListener("hashchange", onHashChange) in onMounted, and remove it
in onUnmounted via window.removeEventListener("hashchange", onHashChange)
alongside the existing resize observer and MutationObserver cleanup.
- Around line 152-175: runSignInRelocationWithRetries starts many independent
setTimeout chains that are never cancelled; to fix, add a module-scoped variable
(e.g., currentSignInTimeoutId) and clearTimeout at the start of
runSignInRelocationWithRetries before scheduling tryOnce, then have tryOnce
assign each window.setTimeout return to that variable so the chain can be
cleared; also clear that timeout in the component's onUnmounted handler to
prevent retries after unmount; keep existing early return in
moveSignInToUtilityArea and preserve signInRelocateWarned logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ae115bfd-b25c-4d79-9db9-4a337d4c4185

📥 Commits

Reviewing files that changed from the base of the PR and between 69f425d and 19ebb86.

📒 Files selected for processing (3)
  • docs/.vitepress/config.ts
  • docs/.vitepress/theme/index.ts
  • docs/index.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/.vitepress/config.ts

Comment on lines +257 to +265
if (!headerObserver) {
const id = window.setInterval(() => {
tryAttachHeaderObserver();
if (headerObserver) {
clearInterval(id);
}
}, 120);
window.setTimeout(() => clearInterval(id), 5000);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Interval and orphan observer not cleaned up on unmount.

The fallback setInterval (line 258) and its safety setTimeout (line 264) are only cleared via their own internal logic. If the component unmounts before headerObserver is attached and before the 5s safety timeout fires, the interval keeps polling tryAttachHeaderObserver(), and if it does eventually attach, the resulting MutationObserver will never be disconnected (the onUnmounted handler at lines 278–285 already ran). The safety setTimeout also outlives unmount.

Given onUnmounted was explicitly added to disconnect the observer, it's worth closing this gap for consistency.

♻️ Suggested change
     let headerObserver: MutationObserver | null = null;
     let onResize: (() => void) | null = null;
+    let attachIntervalId: ReturnType<typeof setInterval> | undefined;
+    let attachTimeoutId: ReturnType<typeof setTimeout> | undefined;

     onMounted(() => {
@@
       tryAttachHeaderObserver();
       if (!headerObserver) {
-        const id = window.setInterval(() => {
+        attachIntervalId = window.setInterval(() => {
           tryAttachHeaderObserver();
           if (headerObserver) {
-            clearInterval(id);
+            clearInterval(attachIntervalId);
+            attachIntervalId = undefined;
           }
         }, 120);
-        window.setTimeout(() => clearInterval(id), 5000);
+        attachTimeoutId = window.setTimeout(() => {
+          if (attachIntervalId) clearInterval(attachIntervalId);
+          attachIntervalId = undefined;
+        }, 5000);
       }
@@
     onUnmounted(() => {
       headerObserver?.disconnect();
       headerObserver = null;
+      if (attachIntervalId) clearInterval(attachIntervalId);
+      if (attachTimeoutId) clearTimeout(attachTimeoutId);
+      attachIntervalId = undefined;
+      attachTimeoutId = undefined;
       if (onResize) {
         window.removeEventListener("resize", onResize);
         onResize = null;
       }
     });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/index.ts` around lines 257 - 265, The interval and
timeout created when headerObserver is not yet set (window.setInterval in the
initialization block) aren't cleared on component unmount, and a later-attached
MutationObserver may outlive the component; update the logic so that the
interval id and timeout id are stored in local variables accessible to the
onUnmounted handler and ensure onUnmounted clears both the interval and timeout
and also disconnects headerObserver if set; reference the interval/timeout
creation around tryAttachHeaderObserver and the existing onUnmounted handler
that disconnects headerObserver to add the cleanup.

Comment thread docs/index.md
cta="Manage projects"
/>
<Card
title="Pages and Wiki"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent casing across card titles.

"Pages and Wiki" capitalizes "Wiki" while "Import and export" uses lowercase "and export". Pick one convention (sentence case or title case) and apply it uniformly to all six cards so the homepage grid reads consistently.

🪄 Proposed fix (sentence case)
-    title="Pages and Wiki"
+    title="Pages and wiki"

Also applies to: 49-49

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/index.md` at line 35, The card titles in docs/index.md are using
inconsistent casing (e.g., "Pages and Wiki" vs "Import and export"); pick one
convention (suggest sentence case) and update all six card title strings —
including "Pages and Wiki" and "Import and export" — to follow that convention
consistently across the homepage grid; search for the other card title literals
in the file and replace them so every card uses the same casing style.

Copy link
Copy Markdown

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/theme/components/Card.vue`:
- Around line 63-68: The anchor rendered by Card.vue uses hasDestination and
resolvedHref but doesn't set target/rel for external URLs; update the component
rendering logic (using hasDestination, resolvedHref and cta) to detect external
links (e.g., resolvedHref startsWith('http') or origin !== current origin) and,
only for external destinations, set target="_blank" and rel="noopener
noreferrer" on the <a> element while leaving internal links unchanged.

In `@docs/.vitepress/theme/style.css`:
- Line 1: The CSS file docs/.vitepress/theme/style.css fails the formatting CI;
run the formatter and commit the changes by executing the project formatting
commands (run pnpm fix:format), then verify the check passes with pnpm
check:format; if the formatter updates docs/.vitepress/theme/style.css, stage
and commit that file so CI no longer reports the formatting error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 974bbd82-868b-40ff-b1df-a3b8ad26531e

📥 Commits

Reviewing files that changed from the base of the PR and between 19ebb86 and 742a8ab.

📒 Files selected for processing (3)
  • docs/.vitepress/theme/components/Card.vue
  • docs/.vitepress/theme/index.ts
  • docs/.vitepress/theme/style.css

Comment on lines +63 to +68
<component
:is="hasDestination ? 'a' : 'div'"
:href="hasDestination ? resolvedHref : undefined"
class="card-link"
:class="{ 'card-link--with-cta': cta }"
>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

External links open in same tab without rel="noopener".

The homepage uses link="https://developers.plane.so/self-hosting/overview" which is external. The rendered <a> lacks target and rel attributes, so external destinations open in the same tab (losing docs context) and miss rel="noopener noreferrer" hardening.

♻️ Proposed refinement
+<script setup>
+// ...existing code...
+const isExternal = computed(() => /^https?:\/\//i.test(resolvedHref.value));
+</script>

 <template>
   <component
     :is="hasDestination ? 'a' : 'div'"
     :href="hasDestination ? resolvedHref : undefined"
+    :target="hasDestination && isExternal ? '_blank' : undefined"
+    :rel="hasDestination && isExternal ? 'noopener noreferrer' : undefined"
     class="card-link"
     :class="{ 'card-link--with-cta': cta }"
   >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/components/Card.vue` around lines 63 - 68, The anchor
rendered by Card.vue uses hasDestination and resolvedHref but doesn't set
target/rel for external URLs; update the component rendering logic (using
hasDestination, resolvedHref and cta) to detect external links (e.g.,
resolvedHref startsWith('http') or origin !== current origin) and, only for
external destinations, set target="_blank" and rel="noopener noreferrer" on the
<a> element while leaving internal links unchanged.

VitePress's unlayered default theme styles. */
@import "tailwindcss/theme" layer(theme);
@import "tailwindcss/utilities" layer(utilities);
/** @format */
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Formatting CI is failing on this file.

oxfmt --check reports a formatting issue for docs/.vitepress/theme/style.css. Please run pnpm fix:format and confirm pnpm check:format passes.

As per coding guidelines, "Run pnpm fix:format before committing and ensure CI check via pnpm check:format passes; formatting is enforced".

🧰 Tools
🪛 GitHub Actions: CI

[error] 1-1: oxfmt --check failed: formatting issues found. Format issues found in above 1 files. Run without '--check' to fix.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.vitepress/theme/style.css` at line 1, The CSS file
docs/.vitepress/theme/style.css fails the formatting CI; run the formatter and
commit the changes by executing the project formatting commands (run pnpm
fix:format), then verify the check passes with pnpm check:format; if the
formatter updates docs/.vitepress/theme/style.css, stage and commit that file so
CI no longer reports the formatting error.

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.

1 participant