Skip to content

feat: fumadocs - v4#24

Merged
steveiliop56 merged 26 commits into
mainfrom
feat/fumadocs
Oct 8, 2025
Merged

feat: fumadocs - v4#24
steveiliop56 merged 26 commits into
mainfrom
feat/fumadocs

Conversation

@steveiliop56
Copy link
Copy Markdown
Member

@steveiliop56 steveiliop56 commented Oct 3, 2025

Summary by CodeRabbit

  • New Features

    • Full docs site rebuild with responsive layouts, collapsible sidebar, enhanced in-page TOC (pane & popover), reusable UI controls (search dialog, theme & language toggles, navigation/menu components), MDX + Mermaid rendering, and a Discord redirect route.
  • Documentation

    • Large migration to MDX: many new guides, reference pages, a migration guide and metadata; several legacy Docusaurus pages removed.
  • Chores

    • Tooling overhaul: React Router + Vite migration, updated package/config and CI adjustments.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Oct 3, 2025

Walkthrough

Migrates the docs site from Docusaurus to a React Router + Vite + Fumadocs stack: adds an app shell, routes, layout/UI primitives, MDX+Mermaid support, many new components/utilities, and extensive MDX content; removes legacy Docusaurus configs, pages, and CSS; updates CI, tooling, and editor ignores/settings.

Changes

Cohort / File(s) Summary
Tooling & Config
package.json, vite.config.ts, react-router.config.ts, tsconfig.json, source.config.ts, source.generated.ts, .gitignore, .prettierignore, .vscode/settings.json
Replace Docusaurus toolchain with Vite + React Router + fumadocs-mdx; add prerender/mdx config, generated source wiring, update TypeScript and editor settings, and adjust ignore lists.
App Shell & Routing
New app entry: app/root.tsx, app/app.css, app/cli.json, app/routes.ts, app/routes/discord.tsx
Add root layout, error boundary, global CSS imports, CLI mapping file and top-level route declarations including home, docs, search, discord.
Docs Source & Pages
app/lib/source.ts, app/docs/page.tsx, app/docs/search.ts, app/routes/*.ts(x)
Wire fumadocs source, add docs page loader/renderer, search loader, and route components for docs and search.
Layouts & Navigation
app/components/layout/... (shared/*, home/*, docs/*, page.tsx, root-toggle.tsx, sidebar.tsx)
Add comprehensive layout system: shared navigation types, home header/menu/navbar, docs layout and page components (TOC, breadcrumb, footer), root tab toggle, and full Sidebar implementation.
UI Primitives
app/components/ui/* (button.tsx, collapsible.tsx, popover.tsx, scroll-area.tsx, toc.tsx, toc-clerk.tsx, toc-thumb.tsx)
New Radix-based and styled UI primitives: button variants, popovers, collapsible, scroll area, TOC provider/scroll area, Clerk TOC renderer and TOC thumb.
Navigation Menu & Navbar
app/components/navigation-menu.tsx, app/components/layout/home/navbar.tsx, app/components/layout/home/menu.tsx
Radix navigation menu wrappers and composed navbar/menu components with variants and wrappers for menu items/triggers/content.
Components & MDX Helpers
app/components/* (card.tsx, discord.tsx, github.tsx, language-toggle.tsx, theme-toggle.tsx, mdx-components.tsx, mdx/mermaid.tsx, search.tsx, root-toggle.tsx, search-toggle.tsx, etc.)
Add small UI components and icons, language/theme toggles, MDX component merging, client-side Mermaid renderer, search dialog, and other helpers.
Lib Utilities
app/lib/* (cn.ts, is-active.ts, layout.shared.tsx, merge-refs.ts)
Add cn alias to twMerge, path activity helpers, base layout options, and mergeRefs utility.
Routing config & Vite
routes.ts, vite.config.ts, react-router.config.ts
Add app routes, Vite config with MDX and plugin setup, and prerender configuration to enumerate static doc paths.
Content: Added MDX
content/docs/**/*.mdx, content/docs/meta.json, many new guides/reference pages
Add extensive MDX documentation: guides, community pages, reference pages, migration guide, and meta.json for docs navigation.
Legacy Removals (Docusaurus)
docusaurus.config.ts, sidebars.ts, docs/**, src/pages/*, src/css/custom.css
Remove Docusaurus config, sidebars, legacy docs and Docusaurus site pages/CSS; migrate content into content/docs MDX where applicable.
CI / Workflows
.github/workflows/deploy.yml, .github/workflows/preview.yml
Update deploy workflow (node/bun changes) and remove preview workflow.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Router as React Router
  participant Loader as Docs Loader
  participant Source as Fumadocs Source
  participant Renderer as MDX Renderer
  participant Layout as DocsLayout / DocsPage

  User->>Router: navigate to /docs/:slug
  Router->>Loader: call loader({ params })
  Loader->>Source: source.getPage(slug)
  Source-->>Loader: page + tree or 404
  Loader-->>Router: loader data
  Router->>Layout: render DocsLayout(tree, options)
  Layout->>Renderer: render MDX with getMDXComponents (includes Mermaid)
  Renderer-->>User: deliver rendered HTML/JS
Loading
sequenceDiagram
  autonumber
  actor User
  participant Toggle as SearchToggle
  participant Dialog as DefaultSearchDialog
  participant Hook as useSearchHook
  participant Index as Orama Index

  User->>Toggle: click search button
  Toggle->>Dialog: setOpen(true)
  Dialog->>Hook: query(term, locale)
  Hook->>Index: run search
  Index-->>Hook: results
  Hook-->>Dialog: data / loading
  Dialog-->>User: render results
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

Hop hop, new burrow, fresh routes I explore,
Vite winds in my whiskers, I sprint to the door.
TOC crumbs and Mermaid streams I chart with delight,
Sidebars pop, tabs toggle — docs shine bright at night.
🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title uses a conventional commit prefix and references “fumadocs v4” but does not clearly describe the primary change, which is migrating the documentation to the new Fumadocs version. As a result, it only partially captures the scope of the large migration. Consider rephrasing the title to a concise sentence that clearly describes the main change, for example “Migrate documentation to Fumadocs v4.”
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/fumadocs

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3b1babe and 8041555.

📒 Files selected for processing (1)
  • content/docs/changelog.mdx (2 hunks)

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

🧹 Nitpick comments (23)
content/docs/reference/headers.mdx (1)

29-30: Fix broken link target.

The link still points to /docs/guides/access-controls.md#access-controls-using-oidc-groups. In the new MDX routing the .md extension resolves to a 404 (you already use the extensionless form in the label-discovery callout). Drop the .md here so it matches the new path and stays consistent.

content/docs/guides/totp.mdx (1)

2-2: Hyphenate the title for readability.

Doc style elsewhere treats “two-factor” as a hyphenated compound, so tweak the title to match.

-title: Two factor authentication
+title: Two-factor authentication
content/docs/community/caddy.mdx (1)

22-28: Use caddyfile syntax highlighting for the snippet. Syntax highlighting is currently set to TypeScript, which miscolors the example. Switching to caddyfile (or caddy) keeps the snippet accurate and easier to read. Based on learnings

content/docs/guides/runtipi.mdx (1)

18-22: Consider using consistent image syntax.

The guide uses both HTML <img> tags (lines 18-22) and Markdown image syntax (line 30). For consistency and to leverage any MDX image optimization features, consider using a single approach throughout.

app/lib/merge-refs.ts (1)

10-11: Add type guard for RefObject.

The current check else if (ref) does not verify that ref is a mutable RefObject with a current property. While TypeScript's React.Ref<T> union type should prevent invalid refs, adding an explicit check improves runtime safety.

Apply this diff:

       if (typeof ref === 'function') {
         ref(value);
-      } else if (ref) {
+      } else if (ref && 'current' in ref) {
         ref.current = value;
       }
app/lib/is-active.ts (1)

3-6: Normalize search/hash too

Paths with ?query/#hash won’t match. Strip them in normalize to harden comparisons.

Apply:

 function normalize(url: string) {
-  if (url.length > 1 && url.endsWith('/')) return url.slice(0, -1);
-  return url;
+  const [path] = url.split(/[?#]/, 1);
+  if (path.length > 1 && path.endsWith('/')) return path.slice(0, -1);
+  return path;
 }
 
 export function isActive(
   url: string,
   pathname: string,
   nested = true,
 ): boolean {
   url = normalize(url);
   pathname = normalize(pathname);
 
   return url === pathname || (nested && pathname.startsWith(`${url}/`));
 }

Also applies to: 13-17

app/root.tsx (1)

34-38: Optional: add SRI/CSP for analytics script

Consider Subresource Integrity and/or a nonce-compatible setup if you enforce CSP.

package.json (1)

1-45: Declare Node engine (ESM + Fumadocs require modern Node)

Add engines to avoid unexpected CI/runtime issues.

   "devDependencies": {
     "@react-router/dev": "^7.8.2",
     "@tailwindcss/vite": "^4.1.12",
     "@types/mdx": "^2.0.13",
     "@types/node": "^24.3.0",
     "@types/react": "^19.1.12",
     "@types/react-dom": "^19.1.9",
     "react-router-devtools": "^5.1.3",
     "tailwindcss": "^4.1.12",
     "typescript": "^5.9.2",
     "vite": "^7.1.3",
     "vite-tsconfig-paths": "^5.1.4"
   }
+  ,
+  "engines": {
+    "node": ">=20"
+  }

Based on learnings

app/components/mdx/mermaid.tsx (1)

47-50: Cache key size

Using the full chart text as part of the cache key may grow memory. Consider hashing the chart string (e.g., stable SHA-1) for the key.

app/components/navigation-menu.tsx (1)

56-69: Align forwarded ref type with actual element

You forward the ref to the wrapper div but type it as Viewport’s ref. Either forward the ref to Primitive.Viewport or change the ref type to HTMLDivElement. Here’s the minimal typing fix.

-const NavigationMenuViewport = React.forwardRef<
-  React.ComponentRef<typeof Primitive.Viewport>,
-  React.ComponentPropsWithoutRef<typeof Primitive.Viewport>
+const NavigationMenuViewport = React.forwardRef<
+  HTMLDivElement,
+  React.ComponentPropsWithoutRef<typeof Primitive.Viewport>
 >(({ className, ...props }, ref) => (
   <div ref={ref} className="flex w-full justify-center">
     <Primitive.Viewport
       {...props}
       className={cn(
         'relative h-(--radix-navigation-menu-viewport-height) w-full origin-[top_center] overflow-hidden transition-[width,height] duration-300 data-[state=closed]:animate-fd-nav-menu-out data-[state=open]:animate-fd-nav-menu-in',
         className,
       )}
     />
   </div>
 ));
app/components/sidebar.tsx (1)

4-14: Clear pending timeout on unmount to avoid setState after unmount

A hovering timeout may fire after unmount. Add a cleanup and import useEffect.

 import {
   type ComponentProps,
   createContext,
   type FC,
   Fragment,
   type ReactNode,
   useContext,
+  useEffect,
   useMemo,
   useRef,
   useState,
 } from "react";
@@
   useOnChange(collapsed, () => {
     setHover(false);
     closeTimeRef.current = Date.now() + 150;
   });
 
+  useEffect(() => {
+    return () => {
+      window.clearTimeout(timerRef.current);
+    };
+  }, []);

Also applies to: 111-121

app/components/github.tsx (1)

3-18: Improve SVG accessibility (optional)

If decorative, mark as aria-hidden and non-focusable. If meaningful, pass an aria-label or title.

Example:

  • <svg aria-hidden="true" focusable="false" ... />
  • or <svg aria-label="GitHub" role="img" ... />
app/docs/search.ts (1)

5-8: Consider making language configurable.

The search language is hardcoded to 'english'. If you plan to support internationalization in the future, consider extracting this to a configuration file or environment variable.

+import { searchConfig } from '@/config/search';
+
 const server = createFromSource(source, {
-  language: 'english',
+  language: searchConfig.language ?? 'english',
 });
app/components/ui/button.tsx (1)

3-9: Consider extracting variants to a shared theme constant.

The variants object could be extracted to a centralized theme configuration if you plan to reuse these color schemes across other components (e.g., badges, cards).

+// In app/lib/theme.ts
+export const colorVariants = {
+  primary: 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80',
+  // ...
+} as const;

+// In button.tsx
+import { colorVariants } from '@/lib/theme';
+
-const variants = {
-  primary: 'bg-fd-primary text-fd-primary-foreground hover:bg-fd-primary/80',
-  // ...
-} as const;
source.config.ts (1)

4-6: Consider adding additional collection metadata.

While the minimal configuration is valid, you might want to add schema validation, frontmatter types, or collection metadata for better type safety and documentation structure.

 export const docs = defineDocs({
   dir: "content/docs",
+  schema: {
+    frontmatter: z.object({
+      title: z.string(),
+      description: z.string().optional(),
+    }),
+  },
 });
app/components/card.tsx (2)

14-23: Consider adding accessibility attributes.

The card div should have appropriate ARIA attributes if it's interactive or represents a distinct content region. Consider adding role="article" or wrapping in a semantic <article> tag.

   return (
-    <div className="flex flex-col gap-4 p-4 shadow-xs border border-fd-border rounded-md hover:border-fd-accent transition-colors max-w-xs">
+    <article className="flex flex-col gap-4 p-4 shadow-xs border border-fd-border rounded-md hover:border-fd-accent transition-colors max-w-xs">
       {icon}
       <div className="flex flex-col gap-2">
-        <p className="text-md font-semibold">{title}</p>
+        <h3 className="text-md font-semibold">{title}</h3>
         <p className="text-sm">{description}</p>
       </div>
       {children}
-    </div>
+    </article>
   );

15-15: Consider using a variant system for card styles.

Similar to the button component, you could introduce variants for different card sizes, styles, or hover effects using CVA for consistency across your UI components.

import { cva } from 'class-variance-authority';

const cardVariants = cva(
  'flex flex-col gap-4 p-4 shadow-xs border rounded-md transition-colors',
  {
    variants: {
      size: {
        sm: 'max-w-xs',
        md: 'max-w-md',
        lg: 'max-w-lg',
      },
      variant: {
        default: 'border-fd-border hover:border-fd-accent',
        featured: 'border-fd-primary hover:border-fd-primary/80',
      },
    },
    defaultVariants: {
      size: 'sm',
      variant: 'default',
    },
  },
);
app/components/ui/collapsible.tsx (1)

14-18: Mounted state prevents FOUC but adds re-render.

The mounted state pattern defers animation classes until after hydration, preventing flash-of-unstyled-content. However, this forces a re-render on every mount.

If animations are purely decorative and the FOUC is acceptable, you could remove the mounted state:

-  const [mounted, setMounted] = useState(false);
-
-  useEffect(() => {
-    setMounted(true);
-  }, []);
-
   return (
     <CollapsiblePrimitive.CollapsibleContent
       ref={ref}
       {...props}
       className={cn(
         'overflow-hidden',
-        mounted &&
-          'data-[state=closed]:animate-fd-collapsible-up data-[state=open]:animate-fd-collapsible-down',
+        'data-[state=closed]:animate-fd-collapsible-up data-[state=open]:animate-fd-collapsible-down',
         props.className,
       )}
     >

Keep the current implementation if avoiding FOUC is a priority.

app/components/language-toggle.tsx (1)

15-16: Error message could be more helpful.

The error thrown when locales are missing is generic. Consider providing guidance on how to fix the issue.

Apply this diff:

-  if (!context.locales) throw new Error('Missing `<I18nProvider />`');
+  if (!context.locales) {
+    throw new Error(
+      'Missing locales in I18nProvider. Ensure <I18nProvider> wraps this component and includes a locales prop.'
+    );
+  }
app/components/layout/docs/client.tsx (1)

58-66: Consider extracting magic numbers to CSS variables.

The hardcoded spacing calculation on line 64 could be more maintainable as a named CSS variable.

Define a CSS variable in your theme:

:root {
  --fd-collapsible-control-offset: calc(var(--fd-banner-height) + var(--fd-tocnav-height) + var(--spacing) * 4);
}

Then use it:

       style={{
-        top: 'calc(var(--fd-banner-height) + var(--fd-tocnav-height) + var(--spacing) * 4)',
+        top: 'var(--fd-collapsible-control-offset)',
       }}
app/components/search-toggle.tsx (1)

71-75: Use stable keys for hotKey items

Avoid array index as key; prefer a stable id (e.g., k.code or k.display).

-        {hotKey.map((k, i) => (
-          <kbd key={i} className="rounded-md border bg-fd-background px-1.5">
+        {hotKey.map((k) => (
+          <kbd key={k.display} className="rounded-md border bg-fd-background px-1.5">
             {k.display}
           </kbd>
         ))}
app/components/layout/home/navbar.tsx (1)

27-54: Type accuracy: Navbar renders a header but props typed as div

Use ComponentProps<'header'> for correctness and consistency (matches other Navbar usage in docs layout).

-export function Navbar(props: ComponentProps<"div">) {
+export function Navbar(props: ComponentProps<"header">) {
app/components/layout/docs/page-client.tsx (1)

59-63: ProgressCircle ARIA: use 0–100 scale for better SR output

Expose progress as percentage (0–100) and keep default max=100. Screen readers announce percentages more naturally.

-      <ProgressCircle
-        value={(selected + 1) / Math.max(1, items.length)}
-        max={1}
+      <ProgressCircle
+        value={((selected + 1) / Math.max(1, items.length)) * 100}
         className={cn('shrink-0', open && 'text-fd-primary')}
       />
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 19b6def and 606eee4.

⛔ Files ignored due to path filters (29)
  • bun.lock is excluded by !**/*.lock
  • public/favicon.ico is excluded by !**/*.ico
  • public/icon/logo.png is excluded by !**/*.png
  • public/screenshots/github-app-client-secret.png is excluded by !**/*.png
  • public/screenshots/github-app-email.png is excluded by !**/*.png
  • public/screenshots/github-app-home.png is excluded by !**/*.png
  • public/screenshots/github-app-new.png is excluded by !**/*.png
  • public/screenshots/github-new-oauth-app.png is excluded by !**/*.png
  • public/screenshots/github-oauth-app-homepage.png is excluded by !**/*.png
  • public/screenshots/github-oauth-client-secret.png is excluded by !**/*.png
  • public/screenshots/google-cloud-home.png is excluded by !**/*.png
  • public/screenshots/google-cloud-oauth-configure.png is excluded by !**/*.png
  • public/screenshots/google-cloud-oauth-created.png is excluded by !**/*.png
  • public/screenshots/google-cloud-oauth-home.png is excluded by !**/*.png
  • public/screenshots/lldap-create-user.png is excluded by !**/*.png
  • public/screenshots/lldap-groups.png is excluded by !**/*.png
  • public/screenshots/npm-nginx.png is excluded by !**/*.png
  • public/screenshots/npm-tinyauth.png is excluded by !**/*.png
  • public/screenshots/pocket-id-client-page.png is excluded by !**/*.png
  • public/screenshots/pocket-id-group-home.png is excluded by !**/*.png
  • public/screenshots/pocket-id-home.png is excluded by !**/*.png
  • public/screenshots/pocket-id-new-client.png is excluded by !**/*.png
  • public/screenshots/pocket-id-new-group.png is excluded by !**/*.png
  • public/screenshots/runtipi-enable-advanced-settings.png is excluded by !**/*.png
  • public/screenshots/runtipi-forward-auth-url.png is excluded by !**/*.png
  • public/screenshots/runtipi-install-app-example.png is excluded by !**/*.png
  • public/screenshots/tinyauth-dark.png is excluded by !**/*.png
  • public/screenshots/zitadel-configuration-view.png is excluded by !**/*.png
  • public/screenshots/zitadel-redirect-view.png is excluded by !**/*.png
📒 Files selected for processing (101)
  • .gitignore (1 hunks)
  • .prettierignore (1 hunks)
  • .vscode/settings.json (1 hunks)
  • app/app.css (1 hunks)
  • app/cli.json (1 hunks)
  • app/components/card.tsx (1 hunks)
  • app/components/discord.tsx (1 hunks)
  • app/components/github.tsx (1 hunks)
  • app/components/language-toggle.tsx (1 hunks)
  • app/components/layout/docs/client.tsx (1 hunks)
  • app/components/layout/docs/index.tsx (1 hunks)
  • app/components/layout/docs/page-client.tsx (1 hunks)
  • app/components/layout/docs/page.tsx (1 hunks)
  • app/components/layout/home/index.tsx (1 hunks)
  • app/components/layout/home/menu.tsx (1 hunks)
  • app/components/layout/home/navbar.tsx (1 hunks)
  • app/components/layout/page.tsx (1 hunks)
  • app/components/layout/shared/client.tsx (1 hunks)
  • app/components/layout/shared/index.tsx (1 hunks)
  • app/components/mdx-components.tsx (1 hunks)
  • app/components/mdx/mermaid.tsx (1 hunks)
  • app/components/navigation-menu.tsx (1 hunks)
  • app/components/root-toggle.tsx (1 hunks)
  • app/components/search-toggle.tsx (1 hunks)
  • app/components/sidebar.tsx (1 hunks)
  • app/components/theme-toggle.tsx (1 hunks)
  • app/components/ui/button.tsx (1 hunks)
  • app/components/ui/collapsible.tsx (1 hunks)
  • app/components/ui/popover.tsx (1 hunks)
  • app/components/ui/scroll-area.tsx (1 hunks)
  • app/components/ui/toc-clerk.tsx (1 hunks)
  • app/components/ui/toc-thumb.tsx (1 hunks)
  • app/components/ui/toc.tsx (1 hunks)
  • app/docs/page.tsx (1 hunks)
  • app/docs/search.ts (1 hunks)
  • app/lib/cn.ts (1 hunks)
  • app/lib/is-active.ts (1 hunks)
  • app/lib/layout.shared.tsx (1 hunks)
  • app/lib/merge-refs.ts (1 hunks)
  • app/lib/source.ts (1 hunks)
  • app/root.tsx (1 hunks)
  • app/routes.ts (1 hunks)
  • app/routes/home.tsx (1 hunks)
  • content/docs/about.mdx (1 hunks)
  • content/docs/changelog.mdx (2 hunks)
  • content/docs/community/caddy.mdx (1 hunks)
  • content/docs/community/kubernetes.mdx (6 hunks)
  • content/docs/community/zitadel-oauth.mdx (1 hunks)
  • content/docs/contributing.mdx (1 hunks)
  • content/docs/getting-started.mdx (1 hunks)
  • content/docs/guides/access-controls.mdx (1 hunks)
  • content/docs/guides/advanced.mdx (1 hunks)
  • content/docs/guides/github-app-oauth.mdx (1 hunks)
  • content/docs/guides/github-oauth.mdx (1 hunks)
  • content/docs/guides/google-oauth.mdx (1 hunks)
  • content/docs/guides/ldap.mdx (1 hunks)
  • content/docs/guides/nginx-proxy-manager.mdx (1 hunks)
  • content/docs/guides/pocket-id.mdx (1 hunks)
  • content/docs/guides/runtipi.mdx (1 hunks)
  • content/docs/guides/totp.mdx (1 hunks)
  • content/docs/guides/using-the-binary.mdx (1 hunks)
  • content/docs/meta.json (1 hunks)
  • content/docs/reference/cli.mdx (1 hunks)
  • content/docs/reference/configuration.mdx (1 hunks)
  • content/docs/reference/flow.mdx (1 hunks)
  • content/docs/reference/headers.mdx (1 hunks)
  • docs/about.md (0 hunks)
  • docs/community/_category_.yml (0 hunks)
  • docs/community/caddy.md (0 hunks)
  • docs/community/zitadel-oauth.md (0 hunks)
  • docs/contributing.md (0 hunks)
  • docs/getting-started.md (0 hunks)
  • docs/guides/_category_.yml (0 hunks)
  • docs/guides/access-controls.md (0 hunks)
  • docs/guides/advanced.md (0 hunks)
  • docs/guides/github-app-oauth.md (0 hunks)
  • docs/guides/github-oauth.md (0 hunks)
  • docs/guides/google-oauth.md (0 hunks)
  • docs/guides/ldap.md (0 hunks)
  • docs/guides/nginx-proxy-manager.md (0 hunks)
  • docs/guides/pocket-id.md (0 hunks)
  • docs/guides/runtipi.md (0 hunks)
  • docs/guides/totp.md (0 hunks)
  • docs/guides/using-the-binary.md (0 hunks)
  • docs/reference/_category_.yml (0 hunks)
  • docs/reference/api.md (0 hunks)
  • docs/reference/cli.md (0 hunks)
  • docs/reference/configuration.md (0 hunks)
  • docs/reference/flow.md (0 hunks)
  • docs/reference/headers.md (0 hunks)
  • docusaurus.config.ts (0 hunks)
  • package.json (1 hunks)
  • react-router.config.ts (1 hunks)
  • sidebars.ts (0 hunks)
  • source.config.ts (1 hunks)
  • source.generated.ts (1 hunks)
  • src/css/custom.css (0 hunks)
  • src/pages/index.module.css (0 hunks)
  • src/pages/index.tsx (0 hunks)
  • tsconfig.json (1 hunks)
  • vite.config.ts (1 hunks)
💤 Files with no reviewable changes (29)
  • docs/reference/api.md
  • docs/guides/ldap.md
  • docs/reference/flow.md
  • docs/about.md
  • docs/guides/google-oauth.md
  • docs/contributing.md
  • docs/guides/advanced.md
  • src/pages/index.tsx
  • docs/guides/using-the-binary.md
  • docs/guides/access-controls.md
  • docusaurus.config.ts
  • src/pages/index.module.css
  • docs/community/category.yml
  • docs/community/zitadel-oauth.md
  • docs/guides/totp.md
  • docs/guides/category.yml
  • docs/guides/nginx-proxy-manager.md
  • docs/reference/category.yml
  • sidebars.ts
  • docs/guides/pocket-id.md
  • docs/guides/github-oauth.md
  • docs/guides/runtipi.md
  • docs/reference/cli.md
  • docs/reference/configuration.md
  • docs/reference/headers.md
  • src/css/custom.css
  • docs/community/caddy.md
  • docs/guides/github-app-oauth.md
  • docs/getting-started.md
🧰 Additional context used
🧬 Code graph analysis (26)
app/lib/layout.shared.tsx (1)
app/components/layout/shared/index.tsx (1)
  • BaseLayoutProps (20-61)
app/components/root-toggle.tsx (1)
app/lib/is-active.ts (2)
  • isTabActive (19-23)
  • isActive (8-17)
app/components/ui/toc-clerk.tsx (3)
app/components/ui/toc.tsx (1)
  • useTOCItems (12-14)
app/components/ui/toc-thumb.tsx (1)
  • TocThumb (38-73)
app/lib/merge-refs.ts (1)
  • mergeRefs (3-15)
app/components/search-toggle.tsx (1)
app/components/ui/button.tsx (2)
  • ButtonProps (28-28)
  • buttonVariants (11-26)
app/routes/home.tsx (5)
app/components/layout/home/index.tsx (1)
  • HomeLayout (40-76)
app/lib/layout.shared.tsx (1)
  • baseOptions (3-9)
app/components/card.tsx (1)
  • Card (8-24)
app/components/github.tsx (1)
  • MdiGithub (3-18)
app/components/discord.tsx (1)
  • IcBaselineDiscord (3-18)
content/docs/about.mdx (1)
src/pages/index.tsx (1)
  • Home (113-122)
app/components/layout/home/menu.tsx (3)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
app/components/layout/shared/index.tsx (2)
  • LinkItemType (146-151)
  • BaseLinkItem (182-182)
app/components/layout/shared/client.tsx (1)
  • BaseLinkItem (8-30)
app/docs/search.ts (2)
app/lib/source.ts (1)
  • source (4-7)
app/docs/page.tsx (1)
  • loader (16-25)
source.config.ts (1)
source.generated.ts (1)
  • docs (7-21)
source.generated.ts (1)
source.config.ts (1)
  • docs (4-6)
app/components/layout/shared/index.tsx (1)
app/components/layout/docs/index.tsx (1)
  • LinkItemType (385-385)
app/components/card.tsx (1)
src/pages/index.tsx (2)
  • Header (9-111)
  • Home (113-122)
app/components/layout/home/index.tsx (8)
app/components/layout/shared/index.tsx (4)
  • BaseLayoutProps (20-61)
  • NavOptions (5-18)
  • getLinks (156-180)
  • LinkItemType (146-151)
app/components/layout/home/navbar.tsx (6)
  • Navbar (27-54)
  • NavbarMenuLink (83-97)
  • NavbarMenu (56-56)
  • NavbarMenuTrigger (72-81)
  • NavbarMenuContent (58-70)
  • NavbarLink (118-136)
app/components/search-toggle.tsx (2)
  • LargeSearchToggle (45-79)
  • SearchToggle (15-43)
app/components/theme-toggle.tsx (1)
  • ThemeToggle (27-88)
app/components/github.tsx (1)
  • MdiGithub (3-18)
app/components/language-toggle.tsx (2)
  • LanguageToggle (14-57)
  • LanguageToggleText (59-68)
app/components/layout/home/menu.tsx (4)
  • Menu (89-89)
  • MenuTrigger (91-108)
  • MenuContent (110-121)
  • MenuLinkItem (34-87)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
app/lib/source.ts (3)
app/docs/page.tsx (1)
  • loader (16-25)
source.generated.ts (2)
  • create (5-5)
  • docs (7-21)
source.config.ts (1)
  • docs (4-6)
app/components/layout/home/navbar.tsx (3)
app/components/layout/docs/client.tsx (1)
  • Navbar (12-28)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
app/components/layout/shared/client.tsx (1)
  • BaseLinkItem (8-30)
app/components/layout/shared/client.tsx (2)
app/components/layout/shared/index.tsx (1)
  • BaseLinkType (72-81)
app/lib/is-active.ts (1)
  • isActive (8-17)
app/root.tsx (1)
app/routes/home.tsx (1)
  • meta (9-17)
app/docs/page.tsx (5)
app/lib/source.ts (1)
  • source (4-7)
source.generated.ts (1)
  • docs (7-21)
app/components/layout/page.tsx (4)
  • DocsPage (113-203)
  • DocsTitle (270-282)
  • DocsDescription (250-266)
  • DocsBody (233-246)
app/components/mdx-components.tsx (1)
  • getMDXComponents (5-11)
app/lib/layout.shared.tsx (1)
  • baseOptions (3-9)
app/components/layout/docs/client.tsx (3)
app/components/sidebar.tsx (1)
  • SidebarCollapseTrigger (425-441)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
app/components/search-toggle.tsx (1)
  • SearchToggle (15-43)
app/components/sidebar.tsx (1)
app/lib/is-active.ts (1)
  • isActive (8-17)
app/components/layout/docs/page.tsx (2)
app/components/ui/toc.tsx (3)
  • TOCScrollArea (30-51)
  • TOCItems (53-85)
  • TOCProvider (16-28)
app/components/ui/toc-clerk.tsx (1)
  • ClerkTOCItems (11-117)
app/components/layout/page.tsx (3)
app/components/layout/docs/page.tsx (14)
  • BreadcrumbProps (102-102)
  • FooterProps (101-101)
  • PageRoot (75-91)
  • PageTOCPopover (98-98)
  • PageTOCPopoverTrigger (99-99)
  • PageTOCPopoverContent (100-100)
  • PageTOCPopoverItems (46-55)
  • PageArticle (57-69)
  • PageBreadcrumb (94-94)
  • PageLastUpdate (96-96)
  • PageFooter (95-95)
  • PageTOC (97-97)
  • PageTOCTitle (20-33)
  • PageTOCItems (35-44)
app/components/layout/docs/page-client.tsx (9)
  • BreadcrumbProps (340-340)
  • FooterProps (244-252)
  • PageTOCPopover (163-219)
  • PageTOCPopoverTrigger (39-91)
  • PageTOCPopoverContent (151-161)
  • PageBreadcrumb (342-392)
  • PageLastUpdate (221-241)
  • PageFooter (277-310)
  • PageTOC (394-417)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
app/components/language-toggle.tsx (1)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
app/components/ui/toc.tsx (2)
app/lib/merge-refs.ts (1)
  • mergeRefs (3-15)
app/components/ui/toc-thumb.tsx (1)
  • TocThumb (38-73)
app/components/layout/docs/index.tsx (6)
app/components/layout/shared/index.tsx (5)
  • BaseLayoutProps (20-61)
  • getLinks (156-180)
  • IconItemType (90-102)
  • BaseLinkItem (182-182)
  • LinkItemType (146-151)
app/components/sidebar.tsx (16)
  • SidebarProps (37-62)
  • SidebarComponents (457-461)
  • SidebarViewport (237-254)
  • SidebarPageTree (466-523)
  • SidebarContentMobile (178-213)
  • SidebarHeader (215-224)
  • SidebarTrigger (408-423)
  • SidebarFooter (226-235)
  • SidebarContent (111-176)
  • SidebarCollapseTrigger (425-441)
  • Sidebar (89-109)
  • SidebarFolder (294-315)
  • SidebarFolderLink (337-370)
  • SidebarFolderTrigger (317-335)
  • SidebarFolderContent (372-406)
  • SidebarItem (270-292)
app/components/root-toggle.tsx (2)
  • Option (12-14)
  • RootToggle (16-103)
app/components/layout/shared/client.tsx (1)
  • BaseLinkItem (8-30)
app/components/github.tsx (1)
  • MdiGithub (3-18)
app/components/layout/docs/client.tsx (3)
  • CollapsibleControl (54-81)
  • Navbar (12-28)
  • LayoutBody (30-52)
app/components/layout/docs/page-client.tsx (2)
app/components/ui/toc.tsx (1)
  • useTOCItems (12-14)
app/lib/is-active.ts (1)
  • isActive (8-17)
🪛 ast-grep (0.39.5)
app/components/mdx/mermaid.tsx

[warning] 56-56: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
app/routes/home.tsx

[error] 9-9: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

app/components/mdx/mermaid.tsx

[error] 57-57: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🔇 Additional comments (29)
app/components/layout/shared/client.tsx (1)

13-17: LGTM!

The active state computation correctly handles the three cases ('url', 'nested-url', 'none') and properly delegates to the isActive helper.

app/cli.json (1)

1-11: LGTM!

The CLI configuration structure is valid and provides clear directory aliases for the project. The empty baseDir and commands are acceptable defaults.

content/docs/guides/runtipi.mdx (1)

1-66: LGTM!

The documentation is comprehensive and well-structured. The callouts effectively highlight important information about Runtipi v4 migration and domain requirements.

content/docs/guides/github-app-oauth.mdx (2)

6-6: Verify internal documentation link extension.

The link to the OAuth Apps guide uses a .md extension: /docs/guides/github-oauth.md. Ensure this matches the actual file extension and routing configuration. If the project uses .mdx files, this should be /docs/guides/github-oauth (without extension) or /docs/guides/github-oauth.mdx.


1-69: LGTM!

The guide is well-structured with clear step-by-step instructions. The security warning about OAuth whitelisting and the tip about exclusive OAuth login are valuable additions.

vite.config.ts (1)

8-9: LGTM!

The plugin configuration is well-structured. The MDX plugin is correctly positioned first to handle content transformation before other build steps.

content/docs/guides/advanced.mdx (1)

1-90: LGTM!

The advanced guide is comprehensive and well-organized. The inclusion of Mermaid diagrams, practical YAML configurations, and references to related GitHub issues provides valuable context for troubleshooting complex setups.

app/lib/merge-refs.ts (1)

3-14: LGTM!

The utility correctly handles both function and object refs, providing a clean way to merge multiple refs into a single callback.

content/docs/contributing.mdx (1)

1-66: LGTM!

The contributing guide is well-structured and provides clear, actionable steps for setting up a development environment. The inclusion of tips about using sslip.io and creating a separate test compose file demonstrates good developer experience considerations.

content/docs/reference/cli.mdx (2)

22-25: Verify MDX Callout component availability

Ensure <Callout type="tip|info"> is registered in your MDX components; otherwise these won’t render.

If not registered, wire it in your MDX components map (app/components/mdx-components.tsx).

Also applies to: 66-68


18-20: Confirm Docker image tag

ghcr.io/steveiliop56/tinyauth:v4 should exist and be public. Consider adding --pull=always to ensure fresh pulls in docs.

app/root.tsx (1)

28-51: Solid root layout and error boundary

Structure, providers, and error handling look good.

Also applies to: 57-92

app/lib/layout.shared.tsx (1)

3-9: LGTM

Sane base options; title wired via nav.

app/docs/search.ts (1)

10-12: LGTM!

The search loader implementation correctly delegates to the FumaDocs server instance. The pattern matches the recommended approach from FumaDocs documentation.

app/routes.ts (1)

3-7: LGTM!

The routing configuration is well-structured with clear separation between the home page, documentation pages (with wildcard for nested routes), and API endpoints. This aligns with React Router v7 best practices.

app/components/ui/button.tsx (1)

11-28: LGTM!

The button variants implementation correctly uses class-variance-authority with proper TypeScript typing. The dual variant/color keys are appropriately documented for fumadocs compatibility.

Based on learnings: CVA usage follows best practices with compound variants and typed variant props.

app/components/mdx-components.tsx (1)

5-11: LGTM!

The component composition follows the correct precedence order: defaults → Mermaid override → user overrides. This allows consumers to further customize any component including Mermaid if needed.

source.config.ts (1)

8-12: LGTM!

The MDX configuration correctly integrates the Mermaid plugin. This aligns with the fumadocs-mdx setup documented in the learnings.

Based on learnings: fumadocs-mdx requires source.config.ts collection definitions and integrates with remark plugins.

app/lib/source.ts (2)

2-2: Import path is correct. ../../source.generated resolves to source.generated.ts at the repository root.


4-7: No changes needed for top-level await support. tsconfig.json uses "target": "esnext" and "module": "esnext", and Vite defaults to ESM—top-level await is supported.

app/components/discord.tsx (1)

1-18: Lucide-react doesn’t include brand icons
Lucide v0.544.0 explicitly excludes social/brand logos (like Discord), so retain this custom SVG or switch to a library that provides brand icons (e.g. react-icons/si or simple-icons/react).

Likely an incorrect or invalid review comment.

app/routes/home.tsx (1)

19-105: LGTM! Well-structured landing page.

The component follows React best practices with proper separation of concerns, accessible markup, and semantic HTML. The use of layout primitives (HomeLayout, Card) and the component composition is clean and maintainable.

app/components/ui/collapsible.tsx (1)

10-34: LGTM! Proper forwardRef implementation.

The component correctly uses forwardRef with proper TypeScript typing and forwards all props to the underlying Radix primitive. The displayName is appropriately set for debugging.

app/docs/page.tsx (1)

16-25: LGTM! Clean loader implementation.

The loader correctly parses the slug, fetches the page, and handles 404 cases with proper Response construction. The structure aligns well with React Router v7 patterns.

app/components/ui/toc-clerk.tsx (2)

26-70: ResizeObserver cleanup is correct.

The effect properly disconnects the ResizeObserver in the cleanup function, preventing memory leaks. The dependency array includes items, ensuring updates when the TOC changes.


87-92: Inline SVG generation is safe.

Using encodeURIComponent for the data URI properly escapes the SVG string, preventing injection. The SVG path is computed from DOM measurements and doesn't include user input.

app/components/language-toggle.tsx (1)

18-56: LGTM! Clean Popover-based language selector.

The component properly uses Popover primitives, highlights the active locale, and handles selection via context.onChange. The aria-label improves accessibility.

app/components/layout/docs/client.tsx (1)

44-46: Dynamic padding uses CSS custom properties correctly.

The inline style calculation for paddingInlineStart properly uses CSS custom properties to create responsive sidebar behavior. The logical property paddingInlineStart correctly supports RTL layouts.

app/components/ui/popover.tsx (1)

10-28: LGTM! Proper Radix wrapper implementation.

The component correctly wraps the Radix Popover with sensible defaults (centered alignment, 4px offset, bottom side). The forwardRef pattern is properly typed, and the Portal ensures correct stacking context. The displayName aids debugging.

Comment on lines +51 to +271
const githubLink: string = "https://github.com/steveiliop56/tinyauth";

export interface DocsLayoutProps extends BaseLayoutProps {
tree: PageTree.Root;

sidebar?: SidebarOptions;

/**
* Props for the `div` container
*/
containerProps?: HTMLAttributes<HTMLDivElement>;
}

interface SidebarOptions
extends ComponentProps<"aside">,
Pick<SidebarProps, "defaultOpenLevel" | "prefetch"> {
enabled?: boolean;
component?: ReactNode;
components?: Partial<SidebarComponents>;

/**
* Root Toggle options
*/
tabs?: Option[] | GetSidebarTabsOptions | false;

banner?: ReactNode;
footer?: ReactNode;

/**
* Support collapsing the sidebar on desktop mode
*
* @defaultValue true
*/
collapsible?: boolean;
}

export function DocsLayout({
nav: { transparentMode, ...nav } = {},
sidebar: {
tabs: sidebarTabs,
enabled: sidebarEnabled = true,
...sidebarProps
} = {},
searchToggle = {},
disableThemeSwitch = false,
themeSwitch = { enabled: !disableThemeSwitch },
i18n = false,
children,
...props
}: DocsLayoutProps) {
const tabs = useMemo(() => {
if (Array.isArray(sidebarTabs)) {
return sidebarTabs;
}
if (typeof sidebarTabs === "object") {
return getSidebarTabs(props.tree, sidebarTabs);
}
if (sidebarTabs !== false) {
return getSidebarTabs(props.tree);
}
return [];
}, [sidebarTabs, props.tree]);
const links = getLinks(props.links ?? [], props.githubUrl);
const sidebarVariables = cn(
"md:[--fd-sidebar-width:268px] lg:[--fd-sidebar-width:286px]"
);

function sidebar() {
const {
footer,
banner,
collapsible = true,
component,
components,
defaultOpenLevel,
prefetch,
...rest
} = sidebarProps;
if (component) return component;

const iconLinks = links.filter(
(item): item is IconItemType => item.type === "icon"
);

const viewport = (
<SidebarViewport>
{links
.filter((v) => v.type !== "icon")
.map((item, i, list) => (
<SidebarLinkItem
key={i}
item={item}
className={cn(i === list.length - 1 && "mb-4")}
/>
))}
<SidebarPageTree components={components} />
</SidebarViewport>
);

const mobile = (
<SidebarContentMobile {...rest}>
<SidebarHeader>
<div className="flex text-fd-muted-foreground items-center gap-1.5">
<div className="flex flex-1">
{iconLinks.map((item, i) => (
<BaseLinkItem
key={i}
item={item}
className={cn(
buttonVariants({
size: "icon-sm",
color: "ghost",
className: "p-2",
})
)}
aria-label={item.label}
>
{item.icon}
</BaseLinkItem>
))}
</div>
{i18n ? (
<LanguageToggle>
<Languages className="size-4.5" />
<LanguageToggleText />
</LanguageToggle>
) : null}
{themeSwitch.enabled !== false &&
(themeSwitch.component ?? (
<ThemeToggle className="p-0" mode={themeSwitch.mode} />
))}
<Link
href={githubLink}
className="max-w-7 rounded-full text-fd-muted-foreground border p-1 hover:opacity-80 transform-colors delay-100"
>
<MdiGithub className="size-full" />
</Link>
<SidebarTrigger
className={cn(
buttonVariants({
color: "ghost",
size: "icon-sm",
className: "p-2",
})
)}
>
<SidebarIcon />
</SidebarTrigger>
</div>
{tabs.length > 0 && <RootToggle options={tabs} />}
{banner}
</SidebarHeader>
{viewport}
<SidebarFooter className="empty:hidden">{footer}</SidebarFooter>
</SidebarContentMobile>
);

const content = (
<SidebarContent {...rest}>
<SidebarHeader>
<div className="flex flex-row gap-2.5 items-center">
<img className="size-5" src="/icon/logo.png" alt="Tinyauth" />
<Link
href={nav.url ?? "/"}
className="inline-flex text-[15px] items-center gap-2.5 font-medium me-auto"
>
{nav.title}
</Link>
{nav.children}
{collapsible && (
<SidebarCollapseTrigger
className={cn(
buttonVariants({
color: "ghost",
size: "icon-sm",
className: "mb-auto text-fd-muted-foreground",
})
)}
>
<SidebarIcon />
</SidebarCollapseTrigger>
)}
</div>
{searchToggle.enabled !== false &&
(searchToggle.components?.lg ?? (
<LargeSearchToggle hideIfDisabled />
))}
{tabs.length > 0 && <RootToggle options={tabs} />}

{banner}
</SidebarHeader>
{viewport}
<HideIfEmpty as={SidebarFooter}>
<div className="flex text-fd-muted-foreground items-center empty:hidden">
<Link
href={githubLink}
className="max-w-7 rounded-full text-fd-muted-foreground border p-1 hover:opacity-80 transform-colors delay-100"
>
<MdiGithub className="size-full" />
</Link>
{i18n ? (
<LanguageToggle>
<Languages className="size-4.5" />
</LanguageToggle>
) : null}
{iconLinks.map((item, i) => (
<BaseLinkItem
key={i}
item={item}
className={cn(
buttonVariants({ size: "icon-sm", color: "ghost" })
)}
aria-label={item.label}
>
{item.icon}
</BaseLinkItem>
))}
{themeSwitch.enabled !== false &&
(themeSwitch.component ?? (
<ThemeToggle className="ms-auto p-0" mode={themeSwitch.mode} />
))}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Respect the configurable docs githubUrl when rendering buttons

The GitHub buttons are fixed to https://github.com/steveiliop56/tinyauth, so providing githubUrl in DocsLayoutProps has no effect. That breaks the documented API (and duplicate links under iconLinks stay pointed at the wrong repo). Thread the actual value through.

-const githubLink: string = "https://github.com/steveiliop56/tinyauth";
+const DEFAULT_GITHUB_LINK = "https://github.com/steveiliop56/tinyauth";
@@
-  const tabs = useMemo(() => {
+  const tabs = useMemo(() => {
     if (Array.isArray(sidebarTabs)) {
       return sidebarTabs;
     }
@@
     return [];
   }, [sidebarTabs, props.tree]);
-  const links = getLinks(props.links ?? [], props.githubUrl);
+  const githubLink = props.githubUrl ?? DEFAULT_GITHUB_LINK;
+  const links = getLinks(props.links ?? [], githubLink);
@@
-            <Link
-              href={githubLink}
+            <Link
+              href={githubLink}
               className="max-w-7 rounded-full text-fd-muted-foreground border p-1 hover:opacity-80 transform-colors delay-100"
             >
               <MdiGithub className="size-full" />
             </Link>
@@
-            <Link
-              href={githubLink}
+            <Link
+              href={githubLink}
               className="max-w-7 rounded-full text-fd-muted-foreground border p-1 hover:opacity-80 transform-colors delay-100"
             >
               <MdiGithub className="size-full" />
             </Link>
🤖 Prompt for AI Agents
In app/components/layout/docs/index.tsx lines ~51-271, the GitHub buttons use
the hardcoded githubLink constant instead of the configurable githubUrl from
props; replace uses of githubLink with (props.githubUrl ?? githubLink) so the
provided githubUrl is respected (you can keep the existing githubLink as the
default fallback or remove it and use a string fallback inline), and ensure any
duplicate iconLinks that should point to the repo also use the same
props.githubUrl fallback.

Comment on lines +20 to +33
export function PageTOCTitle(props: ComponentProps<"h2">) {
return (
<h3
{...props}
className={cn(
"inline-flex items-center gap-1.5 text-sm text-fd-muted-foreground",
props.className,
)}
>
<Text className="size-4" />
<I18nLabel label="toc" />
</h3>
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Prop type mismatch: typed as h2, rendered as h3

Align the prop type with the rendered element to avoid incorrect attribute typings.

-export function PageTOCTitle(props: ComponentProps<"h2">) {
+export function PageTOCTitle(props: ComponentProps<"h3">) {
   return (
-    <h3
+    <h3
       {...props}
📝 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
export function PageTOCTitle(props: ComponentProps<"h2">) {
return (
<h3
{...props}
className={cn(
"inline-flex items-center gap-1.5 text-sm text-fd-muted-foreground",
props.className,
)}
>
<Text className="size-4" />
<I18nLabel label="toc" />
</h3>
);
}
export function PageTOCTitle(props: ComponentProps<"h3">) {
return (
<h3
{...props}
className={cn(
"inline-flex items-center gap-1.5 text-sm text-fd-muted-foreground",
props.className,
)}
>
<Text className="size-4" />
<I18nLabel label="toc" />
</h3>
);
}
🤖 Prompt for AI Agents
In app/components/layout/docs/page.tsx around lines 20 to 33, the component is
typed as ComponentProps<"h2"> but renders an h3, causing prop/attribute type
mismatch; update the component's prop type to ComponentProps<"h3"> (or
alternatively change the rendered element to h2) so the declared props match the
actual DOM element and ensure any imported types are adjusted accordingly.

Comment on lines +27 to +189
const githubLink: string = "https://github.com/steveiliop56/tinyauth";

export interface HomeLayoutProps extends BaseLayoutProps {
nav?: Partial<
NavOptions & {
/**
* Open mobile menu when hovering the trigger
*/
enableHoverToOpen?: boolean;
}
>;
}

export function HomeLayout(
props: HomeLayoutProps & HTMLAttributes<HTMLElement>
) {
const {
nav = {},
links,
githubUrl,
i18n,
disableThemeSwitch = false,
themeSwitch = { enabled: !disableThemeSwitch },
searchToggle,
...rest
} = props;

return (
<NavProvider transparentMode={nav?.transparentMode}>
<main
id="nd-home-layout"
{...rest}
className={cn("flex flex-1 flex-col pt-14", rest.className)}
>
{nav.enabled !== false &&
(nav.component ?? (
<Header
links={links}
nav={nav}
themeSwitch={themeSwitch}
searchToggle={searchToggle}
i18n={i18n}
githubUrl={githubUrl}
/>
))}
{props.children}
</main>
</NavProvider>
);
}

export function Header({
nav = {},
i18n = false,
links,
githubUrl,
themeSwitch = {},
searchToggle = {},
}: HomeLayoutProps) {
const finalLinks = useMemo(
() => getLinks(links, githubUrl),
[links, githubUrl]
);

const navItems = finalLinks.filter((item) =>
["nav", "all"].includes(item.on ?? "all")
);
const menuItems = finalLinks.filter((item) =>
["menu", "all"].includes(item.on ?? "all")
);

return (
<Navbar>
<div className="flex flex-row gap-1.5 items-center">
<img className="size-5" src="/icon/logo.png" alt="Tinyauth" />
<Link
href={nav.url ?? "/"}
className="inline-flex items-center gap-2.5 font-semibold"
>
{nav.title}
</Link>
</div>
{nav.children}
<ul className="flex flex-row items-center gap-2 px-6 max-sm:hidden">
{navItems
.filter((item) => !isSecondary(item))
.map((item, i) => (
<NavbarLinkItem key={i} item={item} className="text-sm" />
))}
</ul>
<div className="flex flex-row items-center justify-end gap-1.5 flex-1 max-lg:hidden">
{searchToggle.enabled !== false &&
(searchToggle.components?.lg ?? (
<LargeSearchToggle
className="w-full rounded-full ps-2.5 max-w-[240px]"
hideIfDisabled
/>
))}
{themeSwitch.enabled !== false &&
(themeSwitch.component ?? <ThemeToggle mode={themeSwitch?.mode} />)}
<Link
href={githubLink}
className="rounded-full text-fd-muted-foreground border p-1 hover:opacity-80 transform-colors delay-100"
>
<MdiGithub className="size-full" />
</Link>
{i18n ? (
<LanguageToggle>
<Languages className="size-5" />
</LanguageToggle>
) : null}
<div className="flex flex-row items-center empty:hidden">
{navItems.filter(isSecondary).map((item, i) => (
<NavbarLinkItem key={i} item={item} />
))}
</div>
</div>
<ul className="flex flex-row items-center ms-auto -me-1.5 lg:hidden">
{searchToggle.enabled !== false &&
(searchToggle.components?.sm ?? (
<SearchToggle className="p-2" hideIfDisabled />
))}
<Menu>
<MenuTrigger
aria-label="Toggle Menu"
className={cn(
buttonVariants({
size: "icon",
color: "ghost",
className: "group",
})
)}
enableHover={nav.enableHoverToOpen}
>
<ChevronDown className="!size-5.5 transition-transform duration-300 group-data-[state=open]:rotate-180" />
</MenuTrigger>
<MenuContent className="sm:flex-row sm:items-center sm:justify-end">
{menuItems
.filter((item) => !isSecondary(item))
.map((item, i) => (
<MenuLinkItem key={i} item={item} className="sm:hidden" />
))}
<div className="-ms-1.5 flex flex-row items-center gap-1.5 max-sm:mt-2">
{menuItems.filter(isSecondary).map((item, i) => (
<MenuLinkItem key={i} item={item} className="-me-1.5" />
))}
<div role="separator" className="flex-1" />
{i18n ? (
<LanguageToggle>
<Languages className="size-5" />
<LanguageToggleText />
<ChevronDown className="size-3 text-fd-muted-foreground" />
</LanguageToggle>
) : null}
{themeSwitch.enabled !== false &&
(themeSwitch.component ?? (
<ThemeToggle mode={themeSwitch?.mode} />
))}
<Link
href={githubLink}
className="rounded-full text-fd-muted-foreground border p-1 hover:opacity-80 transform-colors delay-100"
>
<MdiGithub className="size-full" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Respect the githubUrl prop when rendering GitHub links

The header always links to https://github.com/steveiliop56/tinyauth, ignoring any githubUrl passed through HomeLayoutProps. This breaks configurability (and can render duplicate/mismatched icons when consumers supply their own link via links or githubUrl). Please derive the GitHub href from the prop (with a sensible default) and keep the rendered link in sync with what you pass into getLinks.

-const githubLink: string = "https://github.com/steveiliop56/tinyauth";
+const DEFAULT_GITHUB_URL = "https://github.com/steveiliop56/tinyauth";
@@
-    githubUrl,
+    githubUrl = DEFAULT_GITHUB_URL,
@@
-  const finalLinks = useMemo(
-    () => getLinks(links, githubUrl),
-    [links, githubUrl]
-  );
+  const finalLinks = useMemo(
+    () => getLinks(links, githubUrl),
+    [links, githubUrl]
+  );
@@
-        <Link
-          href={githubLink}
-          className="rounded-full text-fd-muted-foreground border p-1 hover:opacity-80 transform-colors delay-100"
-        >
-          <MdiGithub className="size-full" />
-        </Link>
+        {githubUrl ? (
+          <Link
+            href={githubUrl}
+            className="rounded-full text-fd-muted-foreground border p-1 hover:opacity-80 transform-colors delay-100"
+          >
+            <MdiGithub className="size-full" />
+          </Link>
+        ) : null}
@@
-              <Link
-                href={githubLink}
-                className="rounded-full text-fd-muted-foreground border p-1 hover:opacity-80 transform-colors delay-100"
-              >
-                <MdiGithub className="size-full" />
-              </Link>
+              {githubUrl ? (
+                <Link
+                  href={githubUrl}
+                  className="rounded-full text-fd-muted-foreground border p-1 hover:opacity-80 transform-colors delay-100"
+                >
+                  <MdiGithub className="size-full" />
+                </Link>
+              ) : null}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/components/layout/home/index.tsx around lines 27 to 189, the Header
hardcodes githubLink to "https://github.com/steveiliop56/tinyauth" instead of
using the githubUrl prop passed into HomeLayoutProps; update the component to
derive a single finalGithubUrl (use githubUrl prop with the existing constant as
fallback) and replace all uses of the hardcoded githubLink with that
finalGithubUrl so both getLinks and rendered <Link> use the same resolved URL;
ensure finalGithubUrl is computed in HomeLayout and passed down to Header (or
computed in Header from props) and remove the top-level constant to avoid
divergence.

Comment on lines +183 to +185
<EditOnGitHub
href={`https://github.com/${editOnGithub.owner}/${editOnGithub.repo}/blob/${editOnGithub.sha}/${editOnGithub.path.startsWith("/") ? editOnGithub.path.slice(1) : editOnGithub.path}`}
/>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix missing default for the edit link SHA.

When editOnGithub.sha isn’t provided we end up linking to .../blob/undefined/..., breaking the “Edit on GitHub” shortcut. Default the SHA/ref to "main" (or the project’s default branch) before constructing the URL.

Apply this diff:

-          {editOnGithub && (
-            <EditOnGitHub
-              href={`https://github.com/${editOnGithub.owner}/${editOnGithub.repo}/blob/${editOnGithub.sha}/${editOnGithub.path.startsWith("/") ? editOnGithub.path.slice(1) : editOnGithub.path}`}
-            />
-          )}
+          {editOnGithub && (
+            <EditOnGitHub
+              href={`https://github.com/${editOnGithub.owner}/${editOnGithub.repo}/blob/${editOnGithub.sha ?? "main"}/${editOnGithub.path.startsWith("/") ? editOnGithub.path.slice(1) : editOnGithub.path}`}
+            />
+          )}
📝 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
<EditOnGitHub
href={`https://github.com/${editOnGithub.owner}/${editOnGithub.repo}/blob/${editOnGithub.sha}/${editOnGithub.path.startsWith("/") ? editOnGithub.path.slice(1) : editOnGithub.path}`}
/>
{editOnGithub && (
<EditOnGitHub
href={`https://github.com/${editOnGithub.owner}/${editOnGithub.repo}/blob/${editOnGithub.sha ?? "main"}/${editOnGithub.path.startsWith("/") ? editOnGithub.path.slice(1) : editOnGithub.path}`}
/>
)}
🤖 Prompt for AI Agents
In app/components/layout/page.tsx around lines 183 to 185, the URL template uses
editOnGithub.sha directly which can be undefined and produces a broken link;
before constructing the href, default the SHA/ref to "main" (e.g. const sha =
editOnGithub.sha ?? "main") and use that variable in the URL template, keeping
the existing path normalization (slice(1) if startsWith("/")) and avoid mutating
the original editOnGithub object.

import type { BaseLinkType } from './index';

export function BaseLinkItem({
ref,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Unused ref parameter.

The ref parameter is destructured but never used in the component. If forwarding refs is not required, remove it from the destructuring. If refs should be forwarded to the Link component, pass it via the ref prop.

Apply this diff if ref forwarding is not needed:

 export function BaseLinkItem({
-  ref,
   item,
   ...props
 }: Omit<ComponentProps<'a'>, 'href'> & { item: BaseLinkType }) {

Or if ref forwarding is intended, apply this diff:

   return (
     <Link
+      ref={ref}
       href={item.url}
📝 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
ref,
export function BaseLinkItem({
item,
...props
}: Omit<ComponentProps<'a'>, 'href'> & { item: BaseLinkType }) {
return (
<Link
href={item.url}
{...props}
>
{item.label}
</Link>
)
}
Suggested change
ref,
export function BaseLinkItem({
ref,
item,
...props
}: Omit<ComponentProps<'a'>, 'href'> & { item: BaseLinkType }) {
return (
<Link
ref={ref}
href={item.url}
{...props}
>
{item.label}
</Link>
)
}
🤖 Prompt for AI Agents
In app/components/layout/shared/client.tsx around line 9, the ref parameter is
destructured but never used; either remove ref from the component
props/destructuring if ref forwarding is not required, or implement ref
forwarding by accepting a forwarded ref and passing it to the underlying Link
(and wrap the component with React.forwardRef or the equivalent), ensuring prop
types are updated to include the ref type.

Comment on lines +21 to +47
| `USERS` | `--users` | Comma-separated list of users in the format `username:hash`. | `` | yes |
| `USERS_FILE` | `--users-file` | Path to a file containing users in the format `username:hash`. | `` | no |
| `SECURE_COOKIE` | `--secure-cookie` | Send cookie over secure connection only. | `false` | no |
| `OAUTH_WHITELIST` | `--oauth-whitelist` | Comma-separated list of email addresses to whitelist when using OAuth. | `` | no |
| `OAUTH_AUTO_REDIRECT` | `--oauth-auto-redirect` | Auto redirect to the specified OAuth provider. | `` | no |
| `SESSION_EXPIRY` | `--session-expiry` | Session (cookie) expiration time in seconds. | `86400` | no |
| `LOGIN_TIMEOUT` | `--login-timeout` | Login timeout in seconds after max retries reached (0 to disable). | `300` | no |
| `LOGIN_MAX_RETRIES` | `--login-max-retries` | Maximum login attempts before timeout (0 to disable). | `5` | no |
| `LOG_LEVEL` | `--log-level` | Log level (`trace`, `debug`, `info`, `warn`, `error`, `fatal`, `panic`). | `info` | no |
| `APP_TITLE` | `--app-title` | Title of the app. | `Tinyauth` | no |
| `FORGOT_PASSWORD_MESSAGE` | `--forgot-password-message` | Message to show on the forgot password page. | `` | no |
| `BACKGROUND_IMAGE` | `--background-image` | Background image URL for the login page. | `/background.jpg` | no |
| `RESOURCES_DIR` | `--resources-dir` | Path to a directory containing custom resources (e.g., background image). | `/data/resources` | no |
| `DATABASE_PATH` | `--database-path` | Path to the SQLite database file. | `/data/tinyauth.db` | no |
| `TRUSTED_PROXIES` | `--trusted-proxies` | Comma-separated list of trusted proxies (IP addresses or CIDRs) for correct client IP detection. | `` | no |
| `DISABLE_ANALYTICS` | `--disable-analytics` | Disable anonymous version collection. | `false` | no |
| `DISABLE_RESOURCES` | `--disable-resources` | Disable the resources server. | `false` | no |

## OAuth Config

OAuth configuration includes a provider ID in the environment variable or flag name. For example, `PROVIDERS_GOOGLE_CLIENT_ID`.

| Environment | Flag | Description | Default | Required |
| ------------------------------------- | --------------------------------------- | ---------------------------------------- | ------- | -------- |
| `PROVIDERS_[ID]_CLIENT_ID` | `--providers-[id]-client-id` | OAuth client ID. | `` | yes |
| `PROVIDERS_[ID]_CLIENT_SECRET` | `--providers-[id]-client-secret` | OAuth client secret. | `` | yes |
| `PROVIDERS_[ID]_CLIENT_SECRET_FILE` | `--providers-[id]-client-secret-file` | Path to a file containing client secret. | `` | no |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Clarify “Required” when a FILE_ alternative exists.

Rows like USERS (Lines 21-22) and PROVIDERS_[ID]_CLIENT_SECRET (Lines 45-47) are marked “Required: yes”, yet the adjacent FILE_ variants allow the same data to be provided via file. With the current wording, readers may think both values must be supplied simultaneously. Please adjust the Required column (e.g., “Yes — unless using USERS_FILE”) or add a footnote so the requirement is unambiguous.

🤖 Prompt for AI Agents
In content/docs/reference/configuration.mdx around lines 21 to 47, the
"Required" column incorrectly implies both primary and FILE_ variants must be
provided; update the table entries for rows like USERS/USERS_FILE and
PROVIDERS_[ID]_CLIENT_SECRET/PROVIDERS_[ID]_CLIENT_SECRET_FILE to indicate the
primary key is required unless the corresponding _FILE variant is used (e.g.,
change "yes" to "Yes — or provide USERS_FILE" or add a footnote explaining "Yes
unless using the corresponding _FILE variable"), and apply the same phrasing for
PROVIDERS_[ID]_CLIENT_SECRET so readers clearly understand the file alternative
satisfies the requirement.

Comment on lines +33 to +37
Custom headers can be set using the `tinyauth.headers` label on any container that uses the Tinyauth middleware. For example:

```yaml
tinyauth.apps.[app].response.headers: my-header=cool
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Align label name with actual configuration key.

Line 33 calls out the tinyauth.headers label, but the example immediately below uses tinyauth.apps.[app].response.headers. One of these names is wrong; as written it sends readers hunting for a label that doesn’t exist. Please update the text and example so they reference the same (correct) label.

🤖 Prompt for AI Agents
In content/docs/reference/headers.mdx around lines 33 to 37, the text calls out
the label `tinyauth.headers` while the example uses
`tinyauth.apps.[app].response.headers`; update both the descriptive sentence and
the YAML example to reference the same, correct configuration key (choose the
actual canonical key used by the codebase—likely
`tinyauth.apps.[app].response.headers`), replace `tinyauth.headers` with that
canonical key in the prose, keep the example using the same key format, and
ensure the placeholder `[app]` is left or explained consistently.

Comment thread package.json
{
"name": "tinyauth-docs",
"version": "0.1.0",
"name": "example-react-router",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Rename package to match the project

name is example-react-router. Consider tinyauth-docs for clarity.

-  "name": "example-react-router",
+  "name": "tinyauth-docs",
📝 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
"name": "example-react-router",
"name": "tinyauth-docs",
🤖 Prompt for AI Agents
In package.json at line 2, the project's "name" field is currently
"example-react-router" but should reflect the actual project; change it to
"tinyauth-docs". Update the name value to "tinyauth-docs" ensuring it follows
npm package name rules (lowercase, no spaces) and save the file; if this package
is published or used as a dependency, also update any related references or
documentation to match the new name.

Comment thread source.generated.ts
Comment on lines +1 to +21
/// <reference types="vite/client" />
import { fromConfig } from 'fumadocs-mdx/runtime/vite';
import type * as Config from './source.config';

export const create = fromConfig<typeof Config>();

export const docs = {
doc: create.doc("docs", "./content/docs", import.meta.glob(["./**/*.{mdx,md}"], {
"base": "./content/docs",
"query": {
"collection": "docs"
}
})),
meta: create.meta("docs", "./content/docs", import.meta.glob(["./**/*.{json,yaml}"], {
"import": "default",
"base": "./content/docs",
"query": {
"collection": "docs"
}
}))
}; No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify generated file is in .gitignore.

This appears to be a generated file based on the naming convention and structure. Ensure source.generated.ts is excluded from version control if it's auto-generated during build.

Run the following script to check:


🏁 Script executed:

#!/bin/bash
# Check if source.generated.ts is in .gitignore
if grep -q "source.generated.ts" .gitignore; then
  echo "✓ File is in .gitignore"
else
  echo "⚠ File is NOT in .gitignore - consider adding it if it's generated at build time"
  echo ""
  echo "Checking if fumadocs has a generation command:"
  cat package.json | jq -r '.scripts | to_entries[] | select(.value | contains("fumadocs")) | "\(.key): \(.value)"'
fi

Length of output: 128


Add source.generated.ts to .gitignore
Prevent committing this auto-generated file from fumadocs-mdx.

🤖 Prompt for AI Agents
In source.generated.ts around lines 1 to 21, this file is auto-generated by
fumadocs-mdx and should not be committed; add the path "source.generated.ts" (or
the exact relative path to this file) to the project's .gitignore (or update an
existing rule to match generated files, e.g., add a pattern for generated MDX
runtime outputs) so that Git ignores it going forward, then commit the updated
.gitignore.

Comment thread vite.config.ts
Comment on lines +10 to +12
server: {
allowedHosts: true,
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Review allowedHosts: true for security implications.

Setting allowedHosts: true disables host header validation, which can expose the dev server to DNS rebinding attacks. This setting should only be used in trusted development environments.

Consider restricting to specific hosts in production or shared environments:

   server: {
-    allowedHosts: true,
+    allowedHosts: ['.example.com', 'localhost'],
   },
🤖 Prompt for AI Agents
In vite.config.ts around lines 10 to 12, allowedHosts is set to true which
disables host header validation and can enable DNS rebinding attacks; change
this to a safer configuration by restricting allowedHosts to an explicit list of
trusted hostnames (e.g., ['localhost', '127.0.0.1', '.your-dev-domain.com']) and
conditionally enable the permissive setting only for a local/trusted environment
using an environment flag (NODE_ENV or a custom VITE_DEV_TRUSTED flag), ensuring
production or shared environments never use allowedHosts: true.

@steveiliop56
Copy link
Copy Markdown
Member Author

@Preview deploy

1 similar comment
@steveiliop56
Copy link
Copy Markdown
Member Author

@Preview deploy

Copy link
Copy Markdown
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: 10

♻️ Duplicate comments (27)
content/docs/community/kubernetes.mdx (1)

59-63: Fix the bcrypt example hash

Doubling the $ characters in the bcrypt string produces a completely different hash, so anyone copying this manifest won’t be able to log in. Restore the single-dollar bcrypt value ($2a$10$…) exactly as generated.

app/components/ui/scroll-area.tsx (1)

53-53: Critical: Fix the incorrect Radix Thumb API usage.

The component uses ScrollAreaPrimitive.ScrollAreaThumb which does not exist in the Radix UI API. This will cause a runtime crash. The correct primitive is ScrollAreaPrimitive.Thumb.

Apply this diff to fix the issue:

-    <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-fd-border" />
+    <ScrollAreaPrimitive.Thumb className="relative flex-1 rounded-full bg-fd-border" />

Based on learnings.

app/components/ui/toc.tsx (1)

22-26: Critical: TOCContext rendering issue remains unfixed

The previously reported critical issue is still present. TOCContext is a context object created by createContext, not a React component. Attempting to render <TOCContext value={toc}> will throw a runtime error. You must use TOCContext.Provider instead.

Apply this diff to fix:

-    <TOCContext value={toc}>
+    <TOCContext.Provider value={toc}>
       <Primitive.AnchorProvider toc={toc} {...props}>
         {children}
       </Primitive.AnchorProvider>
-    </TOCContext>
+    </TOCContext.Provider>
vite.config.ts (1)

10-12: Security concern with allowedHosts: true already flagged.

A previous review comment correctly identified that allowedHosts: true disables host header validation and can expose the dev server to DNS rebinding attacks.

package.json (1)

2-2: Package name issue already flagged.

A previous review comment correctly identified that the package name should be tinyauth-docs instead of example-react-router to match the project.

app/components/search-toggle.tsx (1)

15-79: Address prop forwarding and accessibility issues.

As noted in the previous review, both SearchToggle and LargeSearchToggle should:

  1. Forward all remaining button props to the rendered element
  2. Merge consumer onClick with internal handler (call consumer first)
  3. Add disabled={!enabled} to prevent interaction when search is unavailable

These changes improve component composability and accessibility semantics.

app/components/ui/toc-thumb.tsx (1)

13-31: Guard against unresolved anchors in calc function.

As identified in the previous review, when no active anchors are found in the container, upper remains Number.MAX_VALUE, resulting in invalid CSS variable values that cause visual glitches.

The fix is to check if anchors were resolved and return [0, 0] early, plus clamp the height to non-negative values.

app/components/ui/toc-clerk.tsx (1)

36-38: Escape CSS special characters in querySelector.

As noted in the previous review, the querySelector construction at line 37 can fail when TOC item URLs contain CSS special characters (:, ., [, etc.). The fragment portion must be escaped using CSS.escape() before interpolating into the selector string.

app/components/layout/page.tsx (1)

183-185: Default the GitHub SHA to prevent broken links.

As identified in the previous review, when editOnGithub.sha is undefined, the constructed URL contains /blob/undefined/..., creating a broken GitHub link. The SHA should default to "main" (or the project's default branch) before URL construction, as documented in the JSDoc comment.

app/components/layout/home/index.tsx (1)

27-27: Hardcoded GitHub link ignores githubUrl prop.

The githubLink constant is hardcoded and used in the rendered links (lines 128, 186), but the component receives githubUrl as a prop and passes it to getLinks (lines 86-89). This inconsistency means the rendered GitHub icon links will always point to the hardcoded URL, ignoring any custom githubUrl provided by consumers.

Also applies to: 128-128, 186-186

content/docs/guides/using-the-binary.mdx (1)

32-39: Replace the export/unset snippets with a robust .env loader.

The current export $(grep -v '^#' .env | xargs -d '\n') pattern and the corresponding unset snippet are brittle: they break on values containing spaces or quotes, and -d '\n' is GNU-specific. This issue was previously flagged.

Apply this diff to use a POSIX-safe pattern:

-```sh
-export $(grep -v '^#' .env | xargs -d '\n')
-```
-
-<Callout type="tip">
-  To unset the environment variables for security purposes, use: `unset $(grep
-  -v '^#' .env | sed -E 's/(.*)=.*/\1/' | xargs)`.
-</Callout>
+```sh
+set -a
+. ./.env
+set +a
+```
+
+<Callout type="tip">
+  To unset specific environment variables, use explicit `unset KEY` commands for each variable defined in `.env`.
+</Callout>
content/docs/reference/headers.mdx (1)

32-39: Align label name with actual configuration key.

Line 33 refers to the tinyauth.headers label, but Line 36 shows tinyauth.apps.[app].response.headers. This inconsistency was previously flagged and remains unresolved. One of these names is incorrect, leading to confusion.

Apply this diff to correct the reference:

-Custom headers can be set using the `tinyauth.headers` label on any container that uses the Tinyauth middleware. For example:
+Custom headers can be set using the `tinyauth.apps.[app].response.headers` label on any container that uses the Tinyauth middleware. For example:
app/routes/home.tsx (1)

9-17: Remove the empty object destructure in meta.

Line 9 still triggers Biome’s noEmptyPattern error. Drop the empty pattern or rename the unused arg to _args to unblock the linter. Based on static analysis hints.

-export function meta({}: Route.MetaArgs) {
+export function meta(_: Route.MetaArgs) {
content/docs/guides/google-oauth.mdx (1)

70-75: Update the warning to reference Google accounts.

This guide is about Google OAuth, but Line 71 still says “GitHub account,” which is misleading.

-  OAuth alone does not guarantee security. By default, any GitHub account can
+  OAuth alone does not guarantee security. By default, any Google account can
content/docs/getting-started.mdx (1)

102-105: Remove --api.insecure=true or add a clear non-production warning.

The --api.insecure=true flag exposes the Traefik dashboard/API without authentication. While port 8080 isn't published in this example, users often copy these configurations to production environments where it creates a security risk.

Consider one of these approaches:

Option 1 (preferred): Remove the insecure flag entirely:

-    command: --api.insecure=true --providers.docker
+    command: --providers.docker

Option 2: Add a prominent warning and secure alternative:

     command: --api.insecure=true --providers.docker
+    # WARNING: --api.insecure=true is for local testing only!
+    # For production, secure the dashboard with authentication or bind to 127.0.0.1
content/docs/guides/pocket-id.mdx (1)

84-89: Fix the relative link extension for MDX navigation.

The link ./access-controls.md#label-discovery will 404 because the target is now an .mdx file in the new docs system. Remove the .md extension so the link resolver can find the page correctly.

Apply this fix:

-  controls](./access-controls.md#label-discovery) guide for more information.
+  controls](./access-controls#label-discovery) guide for more information.
content/docs/guides/nginx-proxy-manager.mdx (1)

102-111: Add request body guards to prevent performance issues.

The /tinyauth auth_request location will stream full client request bodies (including large file uploads and POST data) to Tinyauth, causing significant latency and potential timeouts. Auth subrequests should not forward request bodies.

Apply these changes to optimize the auth subrequest:

 # Tinyauth auth request
 location /tinyauth {
+  internal;
+  proxy_pass_request_body off;
+  proxy_set_header Content-Length "";
   # Pass request to Tinyauth
   proxy_pass http://tinyauth:3000/api/auth/nginx;
 
   # Pass the request headers
   proxy_set_header x-forwarded-proto $scheme;
   proxy_set_header x-forwarded-host $http_host;
   proxy_set_header x-forwarded-uri $request_uri;
 }

This keeps auth checks fast and prevents timeouts on large uploads.

content/docs/reference/cli.mdx (1)

96-99: Fix copy-paste error in TOTP flag description.

The --user flag description in the Generate TOTP Command table is incorrect. It says "combination to verify" but this section generates TOTP secrets, not verifies credentials.

Apply this correction:

 | Flag            | Description                                        | Default | Required |
 | --------------- | -------------------------------------------------- | ------- | -------- |
-| `--user`        | The `username:hash` combination to verify.         | ``      | yes      |
+| `--user`        | The `username:hash` to generate the TOTP secret from. | ``   | yes      |
 | `--interactive` | Use an interactive TUI for creating the TOTP user. | `false` | no       |
app/lib/is-active.ts (1)

1-23: Stop importing private SidebarTab types

We’re still pulling SidebarTab from the internal fumadocs-ui/utils/get-sidebar-tabs path that can disappear in a patch release. Please swap to a public export or declare the minimal shape locally—this mirrors the earlier feedback.

-import type { SidebarTab } from 'fumadocs-ui/utils/get-sidebar-tabs';
+type SidebarTabLike = {
+  url: string;
+  urls?: Set<string>;
+};
...
-export function isTabActive(tab: SidebarTab, pathname: string) {
+export function isTabActive(tab: SidebarTabLike, pathname: string) {
app/docs/page.tsx (1)

27-52: Move metadata into the route’s meta export

Head tags are still rendered inside the component body, ignoring React Router’s meta API and the previous review. Please expose the frontmatter through the loader and return the descriptors from meta, then drop the inline <title>/<meta> elements.

 export async function loader({ params }: Route.LoaderArgs) {
   const slugs = params["*"].split("/").filter((v) => v.length > 0);
   const page = source.getPage(slugs);
   if (!page) throw new Response("Not found", { status: 404 });

   return {
     path: page.path,
     tree: source.pageTree,
+    frontmatter: page.data?.frontmatter ?? page.data,
   };
 }
 
+export function meta({ data }: Route.MetaArgs) {
+  const frontmatter = data?.frontmatter;
+  return [
+    { title: frontmatter?.title ?? "Docs" },
+    frontmatter?.description
+      ? { name: "description", content: frontmatter.description }
+      : null,
+  ].filter(Boolean);
+}
+
 const renderer = toClientRenderer(
   docs.doc,
   ({ toc, default: Mdx, frontmatter }) => {
     return (
       <DocsPage toc={toc}>
-        <title>{frontmatter.title}</title>
-        <meta name="description" content={frontmatter.description} />
         <DocsTitle>{frontmatter.title}</DocsTitle>
         <DocsDescription>{frontmatter.description}</DocsDescription>
app/components/mdx/mermaid.tsx (2)

3-15: Add a Suspense boundary and avoid returning undefined

use() throws a promise the first time the module renders, so without a Suspense boundary the component will crash. It also still returns undefined before mount. Please wrap MermaidContent in Suspense and return null, as already requested.

-import { use, useEffect, useId, useState } from "react";
+import { Suspense, use, useEffect, useId, useState } from "react";
...
-  if (!mounted) return;
-  return <MermaidContent chart={chart} />;
+  if (!mounted) return null;
+
+  return (
+    <Suspense fallback={<div aria-busy="true" className="h-8" />}>
+      <MermaidContent chart={chart} />
+    </Suspense>
+  );

38-59: Harden the Mermaid renderer against XSS

securityLevel: "loose" plus unsanitized dangerouslySetInnerHTML leaves the docs vulnerable to injected scripts—exactly the issue highlighted before. Switch Mermaid to securityLevel: "strict" and sanitize the SVG (e.g. with DOMPurify) before injecting it.

-  mermaid.initialize({
+  mermaid.initialize({
     startOnLoad: false,
-    securityLevel: "loose",
+    securityLevel: "strict",
     fontFamily: "inherit",
     themeCSS: "margin: 1.5rem auto 0;",
     theme: resolvedTheme === "dark" ? "dark" : "default",
   });
@@
-  const { svg, bindFunctions } = use(
+  const { svg, bindFunctions } = use(
     cachePromise(`${chart}-${resolvedTheme}`, () => {
       return mermaid.render(id, chart.replaceAll("\\n", "\n"));
     })
   );
 
-  return (
+  const sanitizedSvg = DOMPurify.sanitize(svg, { USE_PROFILES: { svg: true } });
+
+  return (
     <div
       ref={(container) => {
         if (container) bindFunctions?.(container);
       }}
-      dangerouslySetInnerHTML={{ __html: svg }}
+      dangerouslySetInnerHTML={{ __html: sanitizedSvg }}
     />
   );

Add import DOMPurify from "dompurify"; near the top.

app/components/root-toggle.tsx (1)

71-97: Compose consumer and internal click handlers

Spreading item.props after onClick={onClick} still allows a consumer-supplied onClick to overwrite the popover-closing logic—exactly what the previous review flagged. Extract the consumer handler, spread the rest, and invoke ours unless the event was prevented.

-          return (
+          const { onClick: itemOnClick, ...itemProps } = item.props ?? {};
+
+          return (
             <Link
               key={item.url}
               href={item.url}
-              onClick={onClick}
-              {...item.props}
+              {...itemProps}
+              onClick={(event) => {
+                itemOnClick?.(event);
+                if (!event.defaultPrevented) {
+                  onClick();
+                }
+              }}
app/components/navigation-menu.tsx (1)

10-23: Fix broken NavigationMenuItem primitive usage

Primitive.NavigationMenuItem isn’t exported by @radix-ui/react-navigation-menu; the correct primitive is Primitive.Item. As written, this component is undefined, causing type errors and runtime crashes. Swap every reference (types, JSX, displayName) over to Primitive.Item.

-const NavigationMenuItem = React.forwardRef<
-  React.ComponentRef<typeof Primitive.NavigationMenuItem>,
-  React.ComponentPropsWithoutRef<typeof Primitive.NavigationMenuItem>
->
+const NavigationMenuItem = React.forwardRef<
+  React.ComponentRef<typeof Primitive.Item>,
+  React.ComponentPropsWithoutRef<typeof Primitive.Item>
+>
   (({ className, children, ...props }, ref) => (
-  <Primitive.NavigationMenuItem
+  <Primitive.Item
       ref={ref}
       className={cn('list-none', className)}
       {...props}
     >
       {children}
-  </Primitive.NavigationMenuItem>
+  </Primitive.Item>
   ));
 
-NavigationMenuItem.displayName = Primitive.NavigationMenuItem.displayName;
+NavigationMenuItem.displayName = Primitive.Item.displayName;
app/components/layout/docs/page.tsx (1)

20-32: Align PageTOCTitle prop typing with the rendered element

PageTOCTitle renders an <h3> but its props are typed as ComponentProps<'h2'>. This leaks the wrong attributes to consumers and fights JSX inference. Update the prop typing (or render <h2>). The previous review already called this out.

-export function PageTOCTitle(props: ComponentProps<'h2'>) {
+export function PageTOCTitle(props: ComponentProps<'h3'>) {
app/components/layout/docs/index.tsx (1)

51-251: Honor githubUrl instead of hardcoding the repo

The layout lets callers supply githubUrl, but the rendered GitHub buttons still point to https://github.com/steveiliop56/tinyauth. That breaks the public API and duplicates icons when a custom repo is provided. Thread the computed URL through everything (including the sidebar buttons) instead of the constant.

-const githubLink: string = "https://github.com/steveiliop56/tinyauth";
+const DEFAULT_GITHUB_LINK = "https://github.com/steveiliop56/tinyauth";
@@
-  const links = getLinks(props.links ?? [], props.githubUrl);
+  const githubUrl = props.githubUrl ?? DEFAULT_GITHUB_LINK;
+  const links = getLinks(props.links ?? [], props.githubUrl);
@@
-            <Link
-              href={githubLink}
+            <Link
+              href={githubUrl}
               className="max-w-7 rounded-full text-fd-muted-foreground border p-1 hover:opacity-80 transform-colors delay-100"
             >
               <MdiGithub className="size-full" />
             </Link>
@@
-            <Link
-              href={githubLink}
+            <Link
+              href={githubUrl}
               className="max-w-7 rounded-full text-fd-muted-foreground border p-1 hover:opacity-80 transform-colors delay-100"
             >
               <MdiGithub className="size-full" />
             </Link>

Also consider reusing githubUrl when deriving links to avoid mismatched icons. Based on learnings.

app/components/layout/shared/index.tsx (1)

166-170: Fix GitHub capitalization

Change “Github” to “GitHub” for consistency.

       {
         type: 'icon',
         url: githubUrl,
-        text: 'Github',
+        text: 'GitHub',
         label: 'GitHub',
🧹 Nitpick comments (5)
package.json (1)

21-21: Consider using caret range for fumadocs-mdx.

Line 21 uses an exact version 12.0.1 for fumadocs-mdx while other fumadocs packages use caret ranges (^15.8.1). Unless this exact pin is intentional for compatibility reasons, consider using ^12.0.1 for consistency.

-    "fumadocs-mdx": "12.0.1",
+    "fumadocs-mdx": "^12.0.1",
app/components/language-toggle.tsx (1)

59-68: Consider early return for missing locale text.

LanguageToggleText renders an empty <span> when no matching locale is found. While not harmful, returning null would be more semantically correct when there's nothing to display.

Apply this diff:

 export function LanguageToggleText(
   props: HTMLAttributes<HTMLSpanElement>,
 ): React.ReactElement {
   const context = useI18n();
   const text = context.locales?.find(
     (item) => item.locale === context.locale,
   )?.name;
 
+  if (!text) return null;
+
   return <span {...props}>{text}</span>;
 }
app/components/theme-toggle.tsx (1)

57-67: Clarify the early return in map.

Line 58 uses if (key === "system") return; which implicitly returns undefined. While functional, explicitly returning null is clearer and more idiomatic for React rendering.

Apply this diff:

         {full.map(([key, Icon]) => {
-          if (key === "system") return;
+          if (key === "system") return null;
 
           return (
content/docs/guides/totp.mdx (1)

10-16: Clarify the user creation flow.

Line 10 states "This requires the current username:hash" but Line 13 instructs running totp generate --interactive, which prompts for a user. It's unclear whether:

  • The user must already exist in the Tinyauth user list before running totp generate, or
  • The command creates a new user entry (as suggested by "Use the Tinyauth CLI to create the new user").

If the user must already exist, state "Ensure the user is already configured in Tinyauth" before the command. If the command creates a new user, clarify that this is a user creation step, not an update. Additionally, Line 16 mentions copying the user "displayed after the user= log message" and adding it to the user list—this implies the command outputs a complete user string, but the phrasing "newly generated user" is ambiguous.

Consider revising to:

-A TOTP secret must first be generated. This requires the current `username:hash`. Use the Tinyauth CLI to create the new user:
+A TOTP secret must first be generated. Use the Tinyauth CLI to generate a TOTP-enabled user entry from an existing `username:hash`:

or, if this is a user creation step:

-A TOTP secret must first be generated. This requires the current `username:hash`. Use the Tinyauth CLI to create the new user:
+A TOTP secret must first be generated. Use the Tinyauth CLI to create a new TOTP-enabled user:

and clarify Line 16:

-The command prompts for the user and generates a QR code to scan with an authenticator app. Once added, copy the newly generated user (displayed after the `user=` log message) and include it in the Tinyauth user list.
+The command prompts for the username and password, generates a QR code to scan with an authenticator app, and outputs a complete user string (after the `user=` log message). Copy this string and add it to the Tinyauth user list (via `USERS` or `USERS_FILE`).
app/components/sidebar.tsx (1)

474-516: Use stable keys instead of array index in SidebarPageTree

Index keys (key={i}) can cause incorrect reconciliation on reorders. Prefer a stable identifier (e.g., item.url or ${level}:${item.name}) for separators/folders.

-          if (Separator) return <Separator key={i} item={item} />;
+          if (Separator) return <Separator key={`sep-${level}-${i}`} item={item} />;
@@
-            <PageTreeFolder key={i} item={item}>
+            <PageTreeFolder key={`folder-${level}-${item.name}-${i}`} item={item}>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18be168 and a46755c.

⛔ Files ignored due to path filters (29)
  • bun.lock is excluded by !**/*.lock
  • public/favicon.ico is excluded by !**/*.ico
  • public/icon/logo.png is excluded by !**/*.png
  • public/screenshots/github-app-client-secret.png is excluded by !**/*.png
  • public/screenshots/github-app-email.png is excluded by !**/*.png
  • public/screenshots/github-app-home.png is excluded by !**/*.png
  • public/screenshots/github-app-new.png is excluded by !**/*.png
  • public/screenshots/github-new-oauth-app.png is excluded by !**/*.png
  • public/screenshots/github-oauth-app-homepage.png is excluded by !**/*.png
  • public/screenshots/github-oauth-client-secret.png is excluded by !**/*.png
  • public/screenshots/google-cloud-home.png is excluded by !**/*.png
  • public/screenshots/google-cloud-oauth-configure.png is excluded by !**/*.png
  • public/screenshots/google-cloud-oauth-created.png is excluded by !**/*.png
  • public/screenshots/google-cloud-oauth-home.png is excluded by !**/*.png
  • public/screenshots/lldap-create-user.png is excluded by !**/*.png
  • public/screenshots/lldap-groups.png is excluded by !**/*.png
  • public/screenshots/npm-nginx.png is excluded by !**/*.png
  • public/screenshots/npm-tinyauth.png is excluded by !**/*.png
  • public/screenshots/pocket-id-client-page.png is excluded by !**/*.png
  • public/screenshots/pocket-id-group-home.png is excluded by !**/*.png
  • public/screenshots/pocket-id-home.png is excluded by !**/*.png
  • public/screenshots/pocket-id-new-client.png is excluded by !**/*.png
  • public/screenshots/pocket-id-new-group.png is excluded by !**/*.png
  • public/screenshots/runtipi-enable-advanced-settings.png is excluded by !**/*.png
  • public/screenshots/runtipi-forward-auth-url.png is excluded by !**/*.png
  • public/screenshots/runtipi-install-app-example.png is excluded by !**/*.png
  • public/screenshots/tinyauth-dark.png is excluded by !**/*.png
  • public/screenshots/zitadel-configuration-view.png is excluded by !**/*.png
  • public/screenshots/zitadel-redirect-view.png is excluded by !**/*.png
📒 Files selected for processing (101)
  • .gitignore (1 hunks)
  • .prettierignore (1 hunks)
  • .vscode/settings.json (1 hunks)
  • app/app.css (1 hunks)
  • app/cli.json (1 hunks)
  • app/components/card.tsx (1 hunks)
  • app/components/discord.tsx (1 hunks)
  • app/components/github.tsx (1 hunks)
  • app/components/language-toggle.tsx (1 hunks)
  • app/components/layout/docs/client.tsx (1 hunks)
  • app/components/layout/docs/index.tsx (1 hunks)
  • app/components/layout/docs/page-client.tsx (1 hunks)
  • app/components/layout/docs/page.tsx (1 hunks)
  • app/components/layout/home/index.tsx (1 hunks)
  • app/components/layout/home/menu.tsx (1 hunks)
  • app/components/layout/home/navbar.tsx (1 hunks)
  • app/components/layout/page.tsx (1 hunks)
  • app/components/layout/shared/client.tsx (1 hunks)
  • app/components/layout/shared/index.tsx (1 hunks)
  • app/components/mdx-components.tsx (1 hunks)
  • app/components/mdx/mermaid.tsx (1 hunks)
  • app/components/navigation-menu.tsx (1 hunks)
  • app/components/root-toggle.tsx (1 hunks)
  • app/components/search-toggle.tsx (1 hunks)
  • app/components/sidebar.tsx (1 hunks)
  • app/components/theme-toggle.tsx (1 hunks)
  • app/components/ui/button.tsx (1 hunks)
  • app/components/ui/collapsible.tsx (1 hunks)
  • app/components/ui/popover.tsx (1 hunks)
  • app/components/ui/scroll-area.tsx (1 hunks)
  • app/components/ui/toc-clerk.tsx (1 hunks)
  • app/components/ui/toc-thumb.tsx (1 hunks)
  • app/components/ui/toc.tsx (1 hunks)
  • app/docs/page.tsx (1 hunks)
  • app/docs/search.ts (1 hunks)
  • app/lib/cn.ts (1 hunks)
  • app/lib/is-active.ts (1 hunks)
  • app/lib/layout.shared.tsx (1 hunks)
  • app/lib/merge-refs.ts (1 hunks)
  • app/lib/source.ts (1 hunks)
  • app/root.tsx (1 hunks)
  • app/routes.ts (1 hunks)
  • app/routes/home.tsx (1 hunks)
  • content/docs/about.mdx (1 hunks)
  • content/docs/changelog.mdx (2 hunks)
  • content/docs/community/caddy.mdx (1 hunks)
  • content/docs/community/kubernetes.mdx (6 hunks)
  • content/docs/community/zitadel-oauth.mdx (1 hunks)
  • content/docs/contributing.mdx (1 hunks)
  • content/docs/getting-started.mdx (1 hunks)
  • content/docs/guides/access-controls.mdx (1 hunks)
  • content/docs/guides/advanced.mdx (1 hunks)
  • content/docs/guides/github-app-oauth.mdx (1 hunks)
  • content/docs/guides/github-oauth.mdx (1 hunks)
  • content/docs/guides/google-oauth.mdx (1 hunks)
  • content/docs/guides/ldap.mdx (1 hunks)
  • content/docs/guides/nginx-proxy-manager.mdx (1 hunks)
  • content/docs/guides/pocket-id.mdx (1 hunks)
  • content/docs/guides/runtipi.mdx (1 hunks)
  • content/docs/guides/totp.mdx (1 hunks)
  • content/docs/guides/using-the-binary.mdx (1 hunks)
  • content/docs/meta.json (1 hunks)
  • content/docs/reference/cli.mdx (1 hunks)
  • content/docs/reference/configuration.mdx (1 hunks)
  • content/docs/reference/flow.mdx (1 hunks)
  • content/docs/reference/headers.mdx (1 hunks)
  • docs/about.md (0 hunks)
  • docs/community/_category_.yml (0 hunks)
  • docs/community/caddy.md (0 hunks)
  • docs/community/zitadel-oauth.md (0 hunks)
  • docs/contributing.md (0 hunks)
  • docs/getting-started.md (0 hunks)
  • docs/guides/_category_.yml (0 hunks)
  • docs/guides/access-controls.md (0 hunks)
  • docs/guides/advanced.md (0 hunks)
  • docs/guides/github-app-oauth.md (0 hunks)
  • docs/guides/github-oauth.md (0 hunks)
  • docs/guides/google-oauth.md (0 hunks)
  • docs/guides/ldap.md (0 hunks)
  • docs/guides/nginx-proxy-manager.md (0 hunks)
  • docs/guides/pocket-id.md (0 hunks)
  • docs/guides/runtipi.md (0 hunks)
  • docs/guides/totp.md (0 hunks)
  • docs/guides/using-the-binary.md (0 hunks)
  • docs/reference/_category_.yml (0 hunks)
  • docs/reference/api.md (0 hunks)
  • docs/reference/cli.md (0 hunks)
  • docs/reference/configuration.md (0 hunks)
  • docs/reference/flow.md (0 hunks)
  • docs/reference/headers.md (0 hunks)
  • docusaurus.config.ts (0 hunks)
  • package.json (1 hunks)
  • react-router.config.ts (1 hunks)
  • sidebars.ts (0 hunks)
  • source.config.ts (1 hunks)
  • source.generated.ts (1 hunks)
  • src/css/custom.css (0 hunks)
  • src/pages/index.module.css (0 hunks)
  • src/pages/index.tsx (0 hunks)
  • tsconfig.json (1 hunks)
  • vite.config.ts (1 hunks)
💤 Files with no reviewable changes (29)
  • docs/guides/using-the-binary.md
  • docs/reference/category.yml
  • docs/guides/category.yml
  • docs/guides/ldap.md
  • docs/reference/api.md
  • docs/reference/configuration.md
  • docs/community/category.yml
  • docs/guides/runtipi.md
  • docs/guides/access-controls.md
  • docs/community/caddy.md
  • docs/guides/advanced.md
  • docs/guides/google-oauth.md
  • docs/reference/flow.md
  • docs/guides/pocket-id.md
  • docs/guides/nginx-proxy-manager.md
  • docs/guides/totp.md
  • docs/about.md
  • sidebars.ts
  • docs/guides/github-app-oauth.md
  • docs/reference/headers.md
  • docs/getting-started.md
  • src/pages/index.module.css
  • docs/contributing.md
  • src/pages/index.tsx
  • docs/guides/github-oauth.md
  • docusaurus.config.ts
  • docs/community/zitadel-oauth.md
  • docs/reference/cli.md
  • src/css/custom.css
🧰 Additional context used
🧬 Code graph analysis (24)
app/docs/search.ts (2)
app/lib/source.ts (1)
  • source (4-7)
app/docs/page.tsx (1)
  • loader (16-25)
app/root.tsx (1)
app/routes/home.tsx (1)
  • meta (9-17)
app/lib/source.ts (3)
app/docs/page.tsx (1)
  • loader (16-25)
source.generated.ts (2)
  • create (5-5)
  • docs (7-21)
source.config.ts (1)
  • docs (4-6)
source.config.ts (1)
source.generated.ts (1)
  • docs (7-21)
app/components/language-toggle.tsx (1)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
app/components/layout/docs/client.tsx (4)
app/components/layout/home/navbar.tsx (1)
  • Navbar (27-54)
app/components/sidebar.tsx (1)
  • SidebarCollapseTrigger (425-441)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
app/components/search-toggle.tsx (1)
  • SearchToggle (15-43)
app/components/ui/toc-clerk.tsx (3)
app/components/ui/toc.tsx (1)
  • useTOCItems (12-14)
app/components/ui/toc-thumb.tsx (1)
  • TocThumb (38-73)
app/lib/merge-refs.ts (1)
  • mergeRefs (3-15)
app/components/layout/shared/index.tsx (1)
app/components/layout/docs/index.tsx (1)
  • LinkItemType (385-385)
app/components/layout/docs/page.tsx (2)
app/components/ui/toc.tsx (3)
  • TOCScrollArea (30-51)
  • TOCItems (53-85)
  • TOCProvider (16-28)
app/components/ui/toc-clerk.tsx (1)
  • ClerkTOCItems (11-117)
app/components/layout/home/navbar.tsx (3)
app/components/layout/docs/client.tsx (1)
  • Navbar (12-28)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
app/components/layout/shared/client.tsx (1)
  • BaseLinkItem (8-30)
app/lib/layout.shared.tsx (1)
app/components/layout/shared/index.tsx (1)
  • BaseLayoutProps (20-61)
app/components/sidebar.tsx (1)
app/lib/is-active.ts (1)
  • isActive (8-17)
app/components/layout/docs/index.tsx (10)
app/components/layout/shared/index.tsx (5)
  • BaseLayoutProps (20-61)
  • getLinks (156-180)
  • IconItemType (90-102)
  • BaseLinkItem (182-182)
  • LinkItemType (146-151)
app/components/sidebar.tsx (15)
  • SidebarProps (37-62)
  • SidebarComponents (457-461)
  • SidebarViewport (237-254)
  • SidebarPageTree (466-523)
  • SidebarContentMobile (178-213)
  • SidebarHeader (215-224)
  • SidebarTrigger (408-423)
  • SidebarContent (111-176)
  • SidebarCollapseTrigger (425-441)
  • Sidebar (89-109)
  • SidebarFolder (294-315)
  • SidebarFolderLink (337-370)
  • SidebarFolderTrigger (317-335)
  • SidebarFolderContent (372-406)
  • SidebarItem (270-292)
app/components/root-toggle.tsx (2)
  • Option (12-14)
  • RootToggle (16-103)
app/components/layout/shared/client.tsx (1)
  • BaseLinkItem (8-30)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
app/components/language-toggle.tsx (2)
  • LanguageToggle (14-57)
  • LanguageToggleText (59-68)
app/components/theme-toggle.tsx (1)
  • ThemeToggle (27-88)
app/components/github.tsx (1)
  • MdiGithub (3-18)
app/components/search-toggle.tsx (2)
  • LargeSearchToggle (45-79)
  • SearchToggle (15-43)
app/components/layout/docs/client.tsx (3)
  • CollapsibleControl (54-81)
  • Navbar (12-28)
  • LayoutBody (30-52)
app/routes/home.tsx (5)
app/components/layout/home/index.tsx (1)
  • HomeLayout (40-76)
app/lib/layout.shared.tsx (1)
  • baseOptions (3-9)
app/components/card.tsx (1)
  • Card (8-24)
app/components/github.tsx (1)
  • MdiGithub (3-18)
app/components/discord.tsx (1)
  • IcBaselineDiscord (3-18)
app/components/layout/shared/client.tsx (2)
app/components/layout/shared/index.tsx (1)
  • BaseLinkType (72-81)
app/lib/is-active.ts (1)
  • isActive (8-17)
app/components/ui/toc.tsx (2)
app/lib/merge-refs.ts (1)
  • mergeRefs (3-15)
app/components/ui/toc-thumb.tsx (1)
  • TocThumb (38-73)
app/components/layout/home/index.tsx (8)
app/components/layout/shared/index.tsx (4)
  • BaseLayoutProps (20-61)
  • NavOptions (5-18)
  • getLinks (156-180)
  • LinkItemType (146-151)
app/components/layout/home/navbar.tsx (6)
  • Navbar (27-54)
  • NavbarMenuLink (83-97)
  • NavbarMenu (56-56)
  • NavbarMenuTrigger (72-81)
  • NavbarMenuContent (58-70)
  • NavbarLink (118-136)
app/components/search-toggle.tsx (2)
  • LargeSearchToggle (45-79)
  • SearchToggle (15-43)
app/components/theme-toggle.tsx (1)
  • ThemeToggle (27-88)
app/components/github.tsx (1)
  • MdiGithub (3-18)
app/components/language-toggle.tsx (2)
  • LanguageToggle (14-57)
  • LanguageToggleText (59-68)
app/components/layout/home/menu.tsx (4)
  • Menu (89-89)
  • MenuTrigger (91-108)
  • MenuContent (110-121)
  • MenuLinkItem (34-87)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
source.generated.ts (1)
source.config.ts (1)
  • docs (4-6)
app/docs/page.tsx (5)
app/lib/source.ts (1)
  • source (4-7)
source.generated.ts (1)
  • docs (7-21)
app/components/layout/page.tsx (4)
  • DocsPage (113-203)
  • DocsTitle (270-282)
  • DocsDescription (250-266)
  • DocsBody (233-246)
app/components/mdx-components.tsx (1)
  • getMDXComponents (5-11)
app/lib/layout.shared.tsx (1)
  • baseOptions (3-9)
app/components/search-toggle.tsx (1)
app/components/ui/button.tsx (2)
  • ButtonProps (28-28)
  • buttonVariants (11-26)
app/components/layout/docs/page-client.tsx (3)
app/components/ui/toc.tsx (1)
  • useTOCItems (12-14)
app/components/ui/collapsible.tsx (3)
  • CollapsibleTrigger (39-39)
  • CollapsibleContent (39-39)
  • Collapsible (39-39)
app/lib/is-active.ts (1)
  • isActive (8-17)
app/components/layout/home/menu.tsx (3)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
app/components/layout/shared/index.tsx (2)
  • LinkItemType (146-151)
  • BaseLinkItem (182-182)
app/components/layout/shared/client.tsx (1)
  • BaseLinkItem (8-30)
app/components/layout/page.tsx (3)
app/components/layout/docs/page.tsx (13)
  • BreadcrumbProps (102-102)
  • FooterProps (101-101)
  • PageRoot (75-91)
  • PageTOCPopover (98-98)
  • PageTOCPopoverTrigger (99-99)
  • PageTOCPopoverContent (100-100)
  • PageTOCPopoverItems (46-55)
  • PageArticle (57-69)
  • PageBreadcrumb (94-94)
  • PageLastUpdate (96-96)
  • PageFooter (95-95)
  • PageTOC (97-97)
  • PageTOCTitle (20-33)
app/components/layout/docs/page-client.tsx (9)
  • BreadcrumbProps (340-340)
  • FooterProps (244-252)
  • PageTOCPopover (163-219)
  • PageTOCPopoverTrigger (39-91)
  • PageTOCPopoverContent (151-161)
  • PageBreadcrumb (342-392)
  • PageLastUpdate (221-241)
  • PageFooter (277-310)
  • PageTOC (394-417)
app/components/ui/button.tsx (1)
  • buttonVariants (11-26)
app/components/root-toggle.tsx (1)
app/lib/is-active.ts (2)
  • isTabActive (19-23)
  • isActive (8-17)
🪛 ast-grep (0.39.5)
app/components/mdx/mermaid.tsx

[warning] 56-56: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
app/components/mdx/mermaid.tsx

[error] 57-57: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

app/routes/home.tsx

[error] 9-9: Unexpected empty object pattern.

(lint/correctness/noEmptyPattern)

🪛 GitHub Actions: Deploy PR previews
react-router.config.ts

[error] 17-17: PLUGIN_ERROR: react-router:virtual-modules Could not load virtual:react-router/server-build: (0 , vite_ssr_import_0.glob)(...) is not a function or its return value is not async iterable

🔇 Additional comments (38)
app/components/ui/scroll-area.tsx (2)

5-21: LGTM!

The ScrollArea wrapper correctly forwards refs, uses appropriate type inference from Radix primitives, and includes sensible defaults (Corner and vertical ScrollBar).


23-36: LGTM!

The ScrollViewport wrapper correctly forwards refs and applies appropriate sizing/border-radius classes for typical viewport behavior.

tsconfig.json (1)

2-25: Configuration looks consistent with the new tooling stack.

The include set and compiler options line up with Vite + React Router requirements, and the rootDirs/path mapping cover the generated Fumadocs types cleanly.

app/root.tsx (4)

1-13: LGTM!

The imports are well-organized and correctly leverage React Router v7 primitives along with FumaDocs providers for the documentation framework.


15-26: LGTM!

The font preloading configuration is correct. The crossOrigin: "anonymous" attribute on the gstatic preconnect is properly set for CORS compliance, and the Inter font is loaded with full variable weight support.


53-55: LGTM!

The App component correctly renders the React Router <Outlet />, which is the standard pattern for a root route component.


28-51: Verify provider nesting order. Confirm that ReactRouterProvider wrapping RootProvider follows FumaDocs guidance. Unless there’s a specific need to load analytics in <head>, consider moving the <script defer src="https://analytics.doesmycode.work/script.js"> to just before </body> for consistency.

app/components/ui/toc.tsx (3)

30-51: LGTM!

The TOCScrollArea component correctly handles ref merging and scroll container setup. The use of Primitive.ScrollProvider with the container ref is appropriate.


53-85: LGTM!

The TOCItems component properly handles the empty state and renders the TOC structure with appropriate styling and accessibility features.


87-101: LGTM!

The TOCItem component correctly applies depth-based indentation and styling using the fumadocs primitives.

.vscode/settings.json (1)

2-11: LGTM!

The spell checker configuration appropriately includes project-specific terms like "Tinyauth", "Runtipi", "TOTP", and "Traefik".

.prettierignore (1)

1-2: LGTM!

The ignore pattern update from .docusaurus to .react-router correctly reflects the framework migration described in the PR objectives.

app/app.css (1)

1-3: LGTM!

The CSS imports correctly set up Tailwind CSS alongside fumadocs-ui neutral and preset styles, establishing the styling foundation for the migrated documentation site.

.gitignore (1)

7-8: LGTM!

The ignore pattern update from .docusaurus to .react-router aligns with the framework migration to React Router.

app/lib/layout.shared.tsx (1)

1-9: LGTM!

The baseOptions function provides a clean, typed configuration for the base layout with the application title. The implementation correctly uses BaseLayoutProps from fumadocs-ui.

content/docs/changelog.mdx (2)

1-4: LGTM!

The addition of YAML front matter with title and description follows MDX best practices and aligns with the fumadocs documentation structure.


302-302: LGTM!

The heading change from "Migration instructions" to "Migration guide" improves consistency with the heading used at line 203 and provides clearer terminology.

app/components/discord.tsx (1)

1-18: LGTM! Clean icon component implementation.

The Discord icon component follows React best practices by forwarding SVG props and using currentColor for theme-friendly styling.

app/lib/cn.ts (1)

1-1: LGTM! Standard Tailwind utility alias.

This re-export creates a convenient cn alias for tailwind-merge, a common pattern in Tailwind-based projects for merging class names without conflicts.

vite.config.ts (1)

1-9: LGTM! Clean Vite configuration.

The plugin composition is correct, with MDX processing first, followed by Tailwind CSS integration, React Router dev tooling, and TypeScript path resolution.

app/components/ui/button.tsx (1)

1-28: LGTM! Well-structured button variants with intentional aliasing.

The implementation correctly uses class-variance-authority for type-safe variant composition. The dual variant and color keys mapping to the same variants object is intentional for FumaDocs compatibility, as noted in the inline comment.

app/lib/merge-refs.ts (1)

1-15: LGTM! Correct ref merging implementation.

The utility correctly handles both function refs and object refs with proper type safety, following standard React patterns for merging multiple refs into a single callback.

source.config.ts (1)

1-12: LGTM! Clean FumaDocs configuration.

The configuration correctly sets up the docs collection and enables Mermaid diagram support through the MDX pipeline. This aligns with the generated source file that imports from this config.

app/components/ui/collapsible.tsx (1)

1-39: LGTM! Proper Radix UI wrapper with hydration-safe animations.

The implementation correctly wraps Radix UI primitives with the 'use client' directive for React Router's SSR. The mounted state pattern at lines 14-18 prevents animation class hydration mismatches by deferring animation classes until after client-side mount, which is a best practice for SSR frameworks.

package.json (2)

6-9: LGTM! Clean migration to React Router tooling.

The scripts correctly reflect the new React Router + Vite workflow, including proper type generation with react-router typegen before TypeScript checking.


27-29: React 19 compatibility confirmed All React-based dependencies (React Router, Radix UI, next-themes, fumadocs-ui, lucide-react) declare peerDependencies including React 19.

app/components/card.tsx (1)

1-24: LGTM! Clean component structure.

The Card component is well-structured with clear prop types and appropriate hover effects. The implementation follows React best practices with proper TypeScript typing.

app/components/layout/page.tsx (1)

233-295: LGTM! Well-structured page composition utilities.

The DocsBody, DocsDescription, DocsTitle, and withArticle exports provide clean, reusable primitives for documentation page composition. The forwardRef pattern with displayName is correctly implemented, and the conditional rendering in DocsDescription appropriately handles undefined content.

app/components/ui/popover.tsx (1)

30-30: Confirm PopoverClose alias
PopoverPrimitive.PopoverClose may not exist—Radix UI’s Popover primitive exports Close, not PopoverClose. Verify in node_modules/@radix-ui/react-popover and update the alias to PopoverPrimitive.Close if needed.

content/docs/guides/runtipi.mdx (2)

18-22: LGTM!

The image tag is correctly formatted with width and alt attributes.


34-39: Verify fallback forward auth URL stability: the hardcoded tinyauth_migrated-tinyauth-1 container name is Docker Compose–specific and may differ across environments. Add a note instructing users to confirm their actual container name (e.g. via docker ps) or to set a stable Compose project name so the URL remains valid.

content/docs/guides/github-oauth.mdx (1)

1-63: LGTM!

The guide is clear, well-structured, and includes appropriate security warnings and configuration tips. The callback URL format and environment variable names are consistent with Tinyauth's conventions.

app/routes.ts (1)

1-7: LGTM – all referenced route modules exist and the configuration satisfies RouteConfig.

content/docs/guides/access-controls.mdx (1)

1-145: Comprehensive and well-structured documentation.

The access controls guide is clear, well-organized, and provides practical examples. The use of callouts effectively highlights security considerations, tips, and important information. The YAML examples are correct and the regex patterns in path ACLs are properly explained.

app/components/mdx-components.tsx (1)

1-11: Clean component composition pattern.

The getMDXComponents helper correctly merges the default MDX components with your custom Mermaid component and allows for optional overrides. The spread order ensures proper precedence: defaults → Mermaid override → user-provided components.

content/docs/contributing.mdx (1)

1-66: Clear contributing guide with practical setup instructions.

The contributing documentation is well-structured and provides a straightforward path for new contributors. The Docker-based workflow with domain configuration examples is helpful.

Verify that Golang v1.23.2 is still the minimum required version, as this might drift over time. If the project uses a go.mod file with a specific Go version directive, ensure this documentation stays aligned.

react-router.config.ts (1)

17-19: CI Node.js version supports fs.promises.glob — no code change needed

Verified Node.js v24.3.0 and fs.promises.glob is a function; the async iterable API is available. If CI still errors, update the pipeline’s Node version to ≥ v20.11.0.

app/components/root-toggle.tsx (1)

9-24: Drop the private SidebarTab import

This file also depends on fumadocs-ui/utils/get-sidebar-tabs, which is an internal path that can change without notice. Define the shape you need locally (url, icon, title, description, unlisted, urls) and extend that instead.

-import type { SidebarTab } from 'fumadocs-ui/utils/get-sidebar-tabs';
-
-export interface Option extends SidebarTab {
+type SidebarTabLike = {
+  url: string;
+  title: string;
+  description?: string;
+  icon?: ReactNode;
+  unlisted?: boolean;
+  urls?: Set<string>;
+};
+
+export interface Option extends SidebarTabLike {
   props?: ComponentProps<'a'>;
 }

[ suggest_essential_refactor ]

Comment thread app/cli.json
Comment on lines +1 to +11
{
"aliases": {
"uiDir": "./components/ui",
"componentsDir": "./components",
"blockDir": "./components",
"cssDir": "./styles",
"libDir": "./lib"
},
"baseDir": "",
"commands": {}
} No newline at end of file
Copy link
Copy Markdown
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

Verify the cssDir alias path

The cssDir alias points to "./styles", but the actual CSS file (app/app.css) is located in the root of the app/ directory. If this configuration is used by tooling to locate CSS files, this mismatch could cause issues.

Run the following script to check the actual CSS file locations:


🏁 Script executed:

#!/bin/bash
# Description: Verify CSS file locations vs. cssDir alias

# Check if ./styles directory exists
fd -t d -d 1 '^styles$' app/

# List all CSS files in the app directory
fd -e css -t f . app/

Length of output: 11


Update cssDir alias to match actual CSS location
In app/cli.json, change "cssDir": "./styles" to "cssDir": "./" so it points to app/app.css.

🤖 Prompt for AI Agents
In app/cli.json around lines 1 to 11 the cssDir alias currently points to
"./styles" but the actual CSS file is app/app.css; update the cssDir value to
"./" so the alias resolves to the project root where app.css lives, saving the
file after the change.

Comment on lines +8 to +29
export function BaseLinkItem({
ref,
item,
...props
}: Omit<ComponentProps<'a'>, 'href'> & { item: BaseLinkType }) {
const pathname = usePathname();
const activeType = item.active ?? 'url';
const active =
activeType !== 'none' &&
isActive(item.url, pathname, activeType === 'nested-url');

return (
<Link
ref={ref}
href={item.url}
external={item.external}
{...props}
data-active={active}
>
{props.children}
</Link>
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Wrap BaseLinkItem in forwardRef so refs actually work.

React doesn’t pass ref through props on function components. As written, ref is always undefined, and consumers will get “Function components cannot be given refs” when trying to use one. Convert the component to forwardRef (or drop ref support entirely).

-'use client';
-import type { ComponentProps } from 'react';
+'use client';
+import { forwardRef, type ComponentProps } from 'react';
@@
-export function BaseLinkItem({
-  ref,
-  item,
-  ...props
-}: Omit<ComponentProps<'a'>, 'href'> & { item: BaseLinkType }) {
+type BaseLinkItemProps = Omit<ComponentProps<'a'>, 'href'> & {
+  item: BaseLinkType;
+};
+
+export const BaseLinkItem = forwardRef<HTMLAnchorElement, BaseLinkItemProps>(
+  ({ item, ...props }, ref) => {
@@
-  return (
-    <Link
-      ref={ref}
-      href={item.url}
-      external={item.external}
-      {...props}
-      data-active={active}
-    >
-      {props.children}
-    </Link>
-  );
-}
+    return (
+      <Link
+        ref={ref}
+        href={item.url}
+        external={item.external}
+        {...props}
+        data-active={active}
+      >
+        {props.children}
+      </Link>
+    );
+  },
+);
+
+BaseLinkItem.displayName = 'BaseLinkItem';
📝 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
export function BaseLinkItem({
ref,
item,
...props
}: Omit<ComponentProps<'a'>, 'href'> & { item: BaseLinkType }) {
const pathname = usePathname();
const activeType = item.active ?? 'url';
const active =
activeType !== 'none' &&
isActive(item.url, pathname, activeType === 'nested-url');
return (
<Link
ref={ref}
href={item.url}
external={item.external}
{...props}
data-active={active}
>
{props.children}
</Link>
);
'use client';
import { forwardRef, type ComponentProps } from 'react';
type BaseLinkItemProps = Omit<ComponentProps<'a'>, 'href'> & {
item: BaseLinkType;
};
export const BaseLinkItem = forwardRef<HTMLAnchorElement, BaseLinkItemProps>(
({ item, ...props }, ref) => {
const pathname = usePathname();
const activeType = item.active ?? 'url';
const active =
activeType !== 'none' &&
isActive(item.url, pathname, activeType === 'nested-url');
return (
<Link
ref={ref}
href={item.url}
external={item.external}
{...props}
data-active={active}
>
{props.children}
</Link>
);
},
);
BaseLinkItem.displayName = 'BaseLinkItem';
🤖 Prompt for AI Agents
In app/components/layout/shared/client.tsx around lines 8 to 29, the function
component accepts a `ref` prop directly which React won't forward; wrap the
component with React.forwardRef so refs work: remove `ref` from the explicit
props, create a forwarded component using forwardRef<HTMLAnchorElement,
Omit<ComponentProps<'a'>, 'href'> & { item: BaseLinkType }>, accept (props, ref)
in the forwardRef callback, pass that ref into the <Link ref={ref} .../>
element, and export the forwarded result; also import forwardRef (or React) at
the top and adjust the prop typing accordingly.

Comment on lines +5 to +14
type ComponentProps,
createContext,
type FC,
Fragment,
type ReactNode,
useContext,
useMemo,
useRef,
useState,
} from "react";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

TS fixes: React namespace types + style casting

  • Using React.Dispatch/React.SetStateAction without importing the React namespace breaks under the automatic JSX runtime. Import the types directly.
  • Avoid as object for inline style; use CSSProperties for type safety.
 import {
   type ComponentProps,
   createContext,
   type FC,
   Fragment,
   type ReactNode,
   useContext,
   useMemo,
   useRef,
   useState,
+  type CSSProperties,
+  type Dispatch,
+  type SetStateAction,
 } from "react";
@@
 const FolderContext = createContext<{
   open: boolean;
-  setOpen: React.Dispatch<React.SetStateAction<boolean>>;
+  setOpen: Dispatch<SetStateAction<boolean>>;
 } | null>(null);
@@
       style={
         {
           ...props.style,
           "--fd-sidebar-offset": hover
             ? "calc(var(--spacing) * 2)"
             : "calc(16px - 100%)",
           "--fd-sidebar-margin": collapsed ? "0.5rem" : "0px",
           "--fd-sidebar-top": `calc(var(--fd-banner-height) + var(--fd-nav-height) + var(--fd-sidebar-margin))`,
           width: collapsed
             ? "var(--fd-sidebar-width)"
             : "calc(var(--spacing) + var(--fd-sidebar-width) + var(--fd-layout-offset))",
-        } as object
+        } as CSSProperties
       }
@@
       <ScrollViewport
         className="p-4 overscroll-contain"
         style={
           {
             "--sidebar-item-offset": "calc(var(--spacing) * 2)",
             maskImage:
               "linear-gradient(to bottom, transparent, white 12px, white calc(100% - 12px), transparent)",
-          } as object
+          } as CSSProperties
         }
       >
@@
       style={
         {
           "--sidebar-item-offset": `calc(var(--spacing) * ${(level + 1) * 3})`,
           ...props.style,
-        } as object
+        } as CSSProperties
       }

Also applies to: 84-87, 135-147, 242-248, 386-391

🤖 Prompt for AI Agents
In app/components/sidebar.tsx around lines 5-14 (and likewise apply to lines
84-87, 135-147, 242-248, 386-391), replace usages of
React.Dispatch/React.SetStateAction by importing the types directly (e.g., add
"import type { Dispatch, SetStateAction, CSSProperties } from 'react'") and
remove any "as object" style casts—use the CSSProperties type for inline style
props (e.g., annotate style variables/props as CSSProperties or type the style
prop as CSSProperties) and update any function/prop type signatures to use the
imported Dispatch/SetStateAction types so the file compiles under the automatic
JSX runtime.

Comment on lines +195 to +211
<Presence present={open}>
{({ present }) => (
<aside
id="nd-sidebar-mobile"
{...props}
data-state={state}
className={cn(
"fixed text-[15px] flex flex-col shadow-lg border-s end-0 inset-y-0 w-[85%] max-w-[380px] z-40 bg-fd-background data-[state=open]:animate-fd-sidebar-in data-[state=closed]:animate-fd-sidebar-out",
!present && "invisible",
className,
)}
>
{children}
</aside>
)}
</Presence>
</>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix: Presence is not a render‑prop; remove function child

@radix-ui/react-presence doesn’t pass { present } via render props. Rendering a function here will throw at runtime. Render the <aside> directly as the child.

-      <Presence present={open}>
-        {({ present }) => (
-          <aside
-            id="nd-sidebar-mobile"
-            {...props}
-            data-state={state}
-            className={cn(
-              "fixed text-[15px] flex flex-col shadow-lg border-s end-0 inset-y-0 w-[85%] max-w-[380px] z-40 bg-fd-background data-[state=open]:animate-fd-sidebar-in data-[state=closed]:animate-fd-sidebar-out",
-              !present && "invisible",
-              className,
-            )}
-          >
-            {children}
-          </aside>
-        )}
-      </Presence>
+      <Presence present={open}>
+        <aside
+          id="nd-sidebar-mobile"
+          {...props}
+          data-state={state}
+          className={cn(
+            "fixed text-[15px] flex flex-col shadow-lg border-s end-0 inset-y-0 w-[85%] max-w-[380px] z-40 bg-fd-background data-[state=open]:animate-fd-sidebar-in data-[state=closed]:animate-fd-sidebar-out",
+            className,
+          )}
+        >
+          {children}
+        </aside>
+      </Presence>
📝 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
<Presence present={open}>
{({ present }) => (
<aside
id="nd-sidebar-mobile"
{...props}
data-state={state}
className={cn(
"fixed text-[15px] flex flex-col shadow-lg border-s end-0 inset-y-0 w-[85%] max-w-[380px] z-40 bg-fd-background data-[state=open]:animate-fd-sidebar-in data-[state=closed]:animate-fd-sidebar-out",
!present && "invisible",
className,
)}
>
{children}
</aside>
)}
</Presence>
</>
<Presence present={open}>
<aside
id="nd-sidebar-mobile"
{...props}
data-state={state}
className={cn(
"fixed text-[15px] flex flex-col shadow-lg border-s end-0 inset-y-0 w-[85%] max-w-[380px] z-40 bg-fd-background data-[state=open]:animate-fd-sidebar-in data-[state=closed]:animate-fd-sidebar-out",
className,
)}
>
{children}
</aside>
</Presence>
</>
🤖 Prompt for AI Agents
In app/components/sidebar.tsx around lines 195 to 211, the Presence component is
being used with a function child expecting a render-prop ({ present }) but
@radix-ui/react-presence does not provide render props; change the child to
render the <aside> element directly (remove the function wrapper and use
Presence as a simple wrapper), and move any conditional logic that depended on {
present } to use the open prop/state or Presence’s semantics (e.g., keep using
the existing open/state variable to set data-state and conditional classes like
!open && "invisible"); ensure props, className and children are passed to the
aside as before.

Comment thread app/root.tsx
Comment on lines +57 to +92
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
let message = "Oops!";
let details = "An unexpected error occurred.";
let stack: string | undefined;

if (isRouteErrorResponse(error)) {
message = error.status === 404 ? "404" : "Error";
details =
error.status === 404
? "The requested page could not be found."
: error.statusText || details;
} else if (import.meta.env.DEV && error && error instanceof Error) {
details = error.message;
stack = error.stack;
}

return (
<main className="px-4 gap-6 flex flex-col items-center justify-center min-h-screen">
<div className="flex flex-col gap-3 text-center">
<h1 className="text-4xl font-black">{message}</h1>
<p>{details}</p>
<Link
to="/"
className="text-sm bg-fd-card text-fd-primary border border-fd-border rounded-md px-4 py-2.5 hover:opacity-80 hover:scale-105 transition-transform delay-100"
>
Go home
</Link>
</div>
{stack && (
<pre className="w-full p-4 overflow-x-auto">
<code>{stack}</code>
</pre>
)}
</main>
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Strengthen error handling and type safety.

The ErrorBoundary implementation has a few concerns:

  1. Type narrowing issue (lines 68-71): After the isRouteErrorResponse check fails, the code assumes the error is an Error instance. However, errors can be any type (string, number, null, object, etc.). The current logic doesn't handle non-Error throwables.

  2. Stack trace exposure (lines 85-89): While the stack trace rendering is gated by import.meta.env.DEV, if the stack variable is populated through other means in production, sensitive information could leak. Consider adding an additional runtime check.

  3. Redundant null check (line 68): The error && check is unnecessary since ErrorBoundaryProps guarantees error is defined.

Apply this diff to improve error handling:

-  } else if (import.meta.env.DEV && error && error instanceof Error) {
+  } else if (error instanceof Error) {
     details = error.message;
-    stack = error.stack;
+    stack = import.meta.env.DEV ? error.stack : undefined;
+  } else if (import.meta.env.DEV) {
+    // Handle non-Error throwables in development
+    details = String(error);
   }

This ensures:

  • Stack traces are only captured in DEV mode
  • Non-Error throwables are handled gracefully
  • Type narrowing is clearer
📝 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
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
let message = "Oops!";
let details = "An unexpected error occurred.";
let stack: string | undefined;
if (isRouteErrorResponse(error)) {
message = error.status === 404 ? "404" : "Error";
details =
error.status === 404
? "The requested page could not be found."
: error.statusText || details;
} else if (import.meta.env.DEV && error && error instanceof Error) {
details = error.message;
stack = error.stack;
}
return (
<main className="px-4 gap-6 flex flex-col items-center justify-center min-h-screen">
<div className="flex flex-col gap-3 text-center">
<h1 className="text-4xl font-black">{message}</h1>
<p>{details}</p>
<Link
to="/"
className="text-sm bg-fd-card text-fd-primary border border-fd-border rounded-md px-4 py-2.5 hover:opacity-80 hover:scale-105 transition-transform delay-100"
>
Go home
</Link>
</div>
{stack && (
<pre className="w-full p-4 overflow-x-auto">
<code>{stack}</code>
</pre>
)}
</main>
);
}
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
let message = "Oops!";
let details = "An unexpected error occurred.";
let stack: string | undefined;
if (isRouteErrorResponse(error)) {
message = error.status === 404 ? "404" : "Error";
details =
error.status === 404
? "The requested page could not be found."
: error.statusText || details;
} else if (error instanceof Error) {
details = error.message;
stack = import.meta.env.DEV ? error.stack : undefined;
} else if (import.meta.env.DEV) {
// Handle non-Error throwables in development
details = String(error);
}
return (
<main className="px-4 gap-6 flex flex-col items-center justify-center min-h-screen">
<div className="flex flex-col gap-3 text-center">
<h1 className="text-4xl font-black">{message}</h1>
<p>{details}</p>
<Link
to="/"
className="text-sm bg-fd-card text-fd-primary border border-fd-border rounded-md px-4 py-2.5 hover:opacity-80 hover:scale-105 transition-transform delay-100"
>
Go home
</Link>
</div>
{stack && (
<pre className="w-full p-4 overflow-x-auto">
<code>{stack}</code>
</pre>
)}
</main>
);
}
🤖 Prompt for AI Agents
In app/root.tsx around lines 57 to 92, the ErrorBoundary currently assumes
non-Route errors are Error instances, redundantly checks for error presence, and
could expose stack traces outside DEV; change the logic to (1) remove the
unnecessary `error &&` check, (2) explicitly narrow the non-Route error by
testing `error instanceof Error` (or via a small isError helper) and only then
assign details = error.message and stack = error.stack inside the
import.meta.env.DEV block, and (3) ensure stack is only ever populated when
import.meta.env.DEV is true so the render guard for the <pre> block can simply
check stack exists without an extra runtime mode check; also add a fallback for
non-Error throwables (e.g., coerce to string with String(error) for details) so
all throwables are handled gracefully.

Comment thread app/routes/home.tsx
Comment on lines +61 to +63
title="OAuth and LDAP support"
description="With Tinyauth you can you can easily login to your apps using your favorite OAuth providers or by using a centralized LDAP server."
/>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix duplicate wording in the “OAuth and LDAP support” copy.

The string currently reads “you can you can” and should be tightened up.

-              description="With Tinyauth you can you can easily login to your apps using your favorite OAuth providers or by using a centralized LDAP server."
+              description="With Tinyauth you can easily log in to your apps using your favorite OAuth providers or a centralized LDAP server."
📝 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
title="OAuth and LDAP support"
description="With Tinyauth you can you can easily login to your apps using your favorite OAuth providers or by using a centralized LDAP server."
/>
title="OAuth and LDAP support"
description="With Tinyauth you can easily log in to your apps using your favorite OAuth providers or a centralized LDAP server."
/>
🤖 Prompt for AI Agents
In app/routes/home.tsx around lines 61 to 63, the description string contains a
duplicated phrase "you can you can"; remove the duplicate so the sentence reads
cleanly (e.g., "With Tinyauth you can easily login to your apps using your
favorite OAuth providers or by using a centralized LDAP server.") and save the
file.

Comment on lines +63 to +64
- APP_URL=http://auth.example.com
- USERS=your-username-password-hash
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use an HTTPS APP_URL when Caddy terminates TLS.

With Caddy handling TLS, clients reach Tinyauth over https://. Keeping APP_URL as http://auth.example.com will mislead deployers into issuing insecure redirects and non-Secure cookies, breaking logins and weakening security. Please change the example to https://auth.example.com.

🤖 Prompt for AI Agents
In content/docs/community/caddy.mdx around lines 63 to 64, the example APP_URL
uses http://auth.example.com which is incorrect when Caddy terminates TLS;
update the example to use https://auth.example.com so generated redirects and
cookies are issued as Secure and match the actual client-facing scheme, ensuring
APP_URL is https and any related docs/text reflect the HTTPS URL.

Comment on lines +44 to +47
- PROVIDERS_ZITADEL_SCOPES=openid profile email preferred_username groups
- PROVIDERS_ZITADEL_AUTH_URL=https://zitadel.example.com/oauth/v2/authorize
- PROVIDERS_ZITADEL_TOKEN_URL=https://zitadel.example.com/oauth/v2/token
- PROVIDERS_ZITADEL_USER_INFO_URL=https://zitadel.example.com/oidc/v1/userinfo
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Invalid Zitadel scopes will break OAuth handshake

preferred_username and groups are claims, not valid Zitadel scopes. Zitadel rejects unknown scopes with invalid_scope, so Tinyauth will never complete the login flow as written.

-      - PROVIDERS_ZITADEL_SCOPES=openid profile email preferred_username groups
+      - PROVIDERS_ZITADEL_SCOPES=openid profile email

If you need organization roles or custom claims, request the proper urn:zitadel:iam:project:roles:... scopes documented by Zitadel instead of these claim names.

📝 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
- PROVIDERS_ZITADEL_SCOPES=openid profile email preferred_username groups
- PROVIDERS_ZITADEL_AUTH_URL=https://zitadel.example.com/oauth/v2/authorize
- PROVIDERS_ZITADEL_TOKEN_URL=https://zitadel.example.com/oauth/v2/token
- PROVIDERS_ZITADEL_USER_INFO_URL=https://zitadel.example.com/oidc/v1/userinfo
- PROVIDERS_ZITADEL_SCOPES=openid profile email
- PROVIDERS_ZITADEL_AUTH_URL=https://zitadel.example.com/oauth/v2/authorize
- PROVIDERS_ZITADEL_TOKEN_URL=https://zitadel.example.com/oauth/v2/token
- PROVIDERS_ZITADEL_USER_INFO_URL=https://zitadel.example.com/oidc/v1/userinfo
🤖 Prompt for AI Agents
In content/docs/community/zitadel-oauth.mdx around lines 44 to 47, the example
environment variable PROVIDERS_ZITADEL_SCOPES includes claim names
("preferred_username" and "groups") which are invalid Zitadel scopes and will
cause an invalid_scope error; remove those claim names from
PROVIDERS_ZITADEL_SCOPES (keep only valid scopes such as openid profile email)
and, if organization roles or custom claims are required, replace them with the
appropriate Zitadel URN scopes (e.g. urn:zitadel:iam:project:roles:...) as
documented by Zitadel.

description: Use the GitHub Apps OAuth screen for authenticating to Tinyauth.
---

Tinyauth also supports GitHub Apps for authentication instead of OAuth Apps. GitHub Apps allow more control over permissions and are slightly more complex to set up. For simpler setups, the [OAuth Apps](/docs/guides/github-oauth.md) guide is recommended.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the internal link extension.

Line 6 references /docs/guides/github-oauth.md, but the repository uses .mdx files. Update to /docs/guides/github-oauth.mdx or use a route-based link without extension (e.g., /docs/guides/github-oauth) if the routing layer strips extensions.

Apply this diff:

-Tinyauth also supports GitHub Apps for authentication instead of OAuth Apps. GitHub Apps allow more control over permissions and are slightly more complex to set up. For simpler setups, the [OAuth Apps](/docs/guides/github-oauth.md) guide is recommended.
+Tinyauth also supports GitHub Apps for authentication instead of OAuth Apps. GitHub Apps allow more control over permissions and are slightly more complex to set up. For simpler setups, the [OAuth Apps](/docs/guides/github-oauth) guide is recommended.
📝 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
Tinyauth also supports GitHub Apps for authentication instead of OAuth Apps. GitHub Apps allow more control over permissions and are slightly more complex to set up. For simpler setups, the [OAuth Apps](/docs/guides/github-oauth.md) guide is recommended.
Tinyauth also supports GitHub Apps for authentication instead of OAuth Apps. GitHub Apps allow more control over permissions and are slightly more complex to set up. For simpler setups, the [OAuth Apps](/docs/guides/github-oauth) guide is recommended.
🤖 Prompt for AI Agents
In content/docs/guides/github-app-oauth.mdx around line 6, the internal link
points to /docs/guides/github-oauth.md but the repo uses .mdx (or route-based
links); update the link target to /docs/guides/github-oauth.mdx or to the
extensionless route /docs/guides/github-oauth so it resolves correctly in the
site routing.

- LDAP_BIND_PASSWORD=your-observer-user-password
- LDAP_BASE_DN=dc=example,dc=com
- LDAP_SEARCH_FILTER=(uid=%s)
- LDAP_INSECURE=true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Document the security implications of LDAP_INSECURE=true.

Line 44 sets LDAP_INSECURE=true in the example without explaining the security trade-off. This flag disables TLS certificate validation, making the connection vulnerable to man-in-the-middle attacks.

Add a callout after the configuration block:

 ```yaml
 services:
   tinyauth:
     environment:
       - LDAP_ADDRESS=ldap://my-lldap-server:3890
       - LDAP_BIND_DN=uid=your-observer-user,ou=people,dc=example,dc=com
       - LDAP_BIND_PASSWORD=your-observer-user-password
       - LDAP_BASE_DN=dc=example,dc=com
       - LDAP_SEARCH_FILTER=(uid=%s)
       - LDAP_INSECURE=true

+

  • LDAP_INSECURE=true disables TLS certificate verification and should only be used for testing or in trusted networks. For production, use ldaps:// with valid certificates and omit this flag or set it to false.
    +

<details>
<summary>🤖 Prompt for AI Agents</summary>

content/docs/guides/ldap.mdx around line 44: the example sets LDAP_INSECURE=true
without warning; add a warning Callout immediately after the configuration block
that states this flag disables TLS certificate verification and is only for
testing or trusted networks, instruct readers to use ldaps:// with valid certs
and omit or set LDAP_INSECURE to false in production, and ensure the MDX callout
syntax is valid and placed right after the YAML block.


</details>

<!-- This is an auto-generated comment by CodeRabbit -->

@steveiliop56
Copy link
Copy Markdown
Member Author

/preview

2 similar comments
@steveiliop56
Copy link
Copy Markdown
Member Author

/preview

@steveiliop56
Copy link
Copy Markdown
Member Author

/preview

Copy link
Copy Markdown
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: 1

♻️ Duplicate comments (1)
package.json (1)

2-2: Rename the package to match the project.

The scaffold name example-react-router is still here; please align it with the actual docs project name.

Apply this diff:

-  "name": "example-react-router",
+  "name": "tinyauth-docs",
🧹 Nitpick comments (1)
app/components/search.tsx (1)

1-1: Pipeline failure: Sourcemap resolution error.

The build pipeline reports a sourcemap error: "Can't resolve original location of error." This is likely a build configuration issue rather than a code problem, but it may affect debugging experience.

Consider checking:

  • Vite/bundler sourcemap configuration
  • Whether all dependencies provide valid sourcemaps
  • Build output for any warnings about sourcemap generation

This does not block functionality but may make debugging more difficult in production.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a46755c and 38ef0d0.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • app/components/search.tsx (1 hunks)
  • app/root.tsx (1 hunks)
  • package.json (1 hunks)
  • react-router.config.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • react-router.config.ts
  • app/root.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
app/components/search.tsx (1)
source.generated.ts (1)
  • create (5-5)
🪛 GitHub Actions: Deploy PR previews
app/components/search.tsx

[error] 1-1: Error when using sourcemap for reporting an error: Can't resolve original location of error.

🔇 Additional comments (3)
app/components/search.tsx (3)

1-15: LGTM!

The imports are correct for a FumaDocs search dialog with Orama integration. The "use client" directive is appropriate for this interactive component.


25-46: LGTM!

The component structure and hook integration follow FumaDocs patterns correctly. The useDocsSearch hook is properly configured for static/client-side search with Orama.


47-47: Verify query.data empty-state sentinel
The check query.data !== "empty" assumes the hook returns "empty" as a sentinel, which is uncommon. Confirm the actual type/empty value returned by useDocsSearch in fumadocs-core/search/client. If it never returns "empty", replace with a standard nullish or length check, for example:

<SearchDialogList items={query.data ?? null} />

Comment thread app/components/search.tsx
Comment on lines +17 to +23
function initOrama() {
return create({
schema: { _: "string" },
// https://docs.orama.com/docs/orama-js/supported-languages
language: "english",
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Adjust the schema to match FumaDocs document structure.

The minimal schema { _: "string" } will not work correctly with FumaDocs search documents, which typically contain title, content, url, and other structured fields. The useDocsSearch hook expects documents with these standard fields.

Apply this diff to use a proper schema:

 function initOrama() {
   return create({
-    schema: { _: "string" },
+    schema: {
+      title: "string",
+      content: "string",
+      url: "string",
+      section: "string",
+    },
     // https://docs.orama.com/docs/orama-js/supported-languages
     language: "english",
   });
 }

Based on learnings and FumaDocs documentation patterns.

📝 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
function initOrama() {
return create({
schema: { _: "string" },
// https://docs.orama.com/docs/orama-js/supported-languages
language: "english",
});
}
function initOrama() {
return create({
schema: {
title: "string",
content: "string",
url: "string",
section: "string",
},
// https://docs.orama.com/docs/orama-js/supported-languages
language: "english",
});
}
🤖 Prompt for AI Agents
In app/components/search.tsx around lines 17 to 23, the Orama schema currently
uses a minimal placeholder { _: "string" } which doesn't match FumaDocs
documents; replace it with a schema that defines the real document shape (for
example fields like title: "string", content: "string", url: "string", summary:
"string", tags: "string[]", and any metadata/date fields as "string" or
appropriate types) and keep language: "english"; ensure the schema keys match
what useDocsSearch and stored documents expect so indexing/searching works
correctly.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Oct 3, 2025

PR Preview Action v1.6.2

🚀 View preview at
https://steveiliop56.github.io/tinyauth-docs/pr-preview/pr-24/

Built to branch gh-pages at 2025-10-03 19:17 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Copy link
Copy Markdown
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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38ef0d0 and 43c3077.

📒 Files selected for processing (2)
  • .github/workflows/deploy.yml (2 hunks)
  • react-router.config.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • react-router.config.ts

Comment on lines 26 to 31
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-web-${{ hashFiles('bun.lockb') }}
key: ${{ runner.os }}-bun-web-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-web-
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Restore cache key to hash the actual Bun lockfile.

Switching the cache key to hashFiles('bun.lock') breaks cache busting because Bun still generates bun.lockb by default, so the hash is now empty and the job falls back to a static key. That means dependency updates will keep reusing stale cache entries. Please keep hashing bun.lockb, or include both patterns if you’re intentionally migrating to the text lockfile.

Apply this diff (or equivalent) to fix it:

-          key: ${{ runner.os }}-bun-web-${{ hashFiles('bun.lock') }}
+          key: ${{ runner.os }}-bun-web-${{ hashFiles('bun.lockb') }}
📝 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
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-web-${{ hashFiles('bun.lockb') }}
key: ${{ runner.os }}-bun-web-${{ hashFiles('bun.lock') }}
restore-keys: |
${{ runner.os }}-bun-web-
uses: actions/cache@v4
with:
path: ~/.bun/install/cache
key: ${{ runner.os }}-bun-web-${{ hashFiles('bun.lockb') }}
restore-keys: |
${{ runner.os }}-bun-web-
🤖 Prompt for AI Agents
.github/workflows/deploy.yml around lines 26 to 31: the cache key currently
hashes bun.lock which is empty when Bun generates bun.lockb by default, causing
cache misses and stale reuse; change the key to hash the binary lockfile
(bun.lockb) or include both files so hashing falls back correctly (e.g., use
hashFiles('bun.lockb') or hashFiles('bun.lock','bun.lockb')) and update any
restore-keys accordingly so dependency changes produce new cache keys.

Copy link
Copy Markdown
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: 1

🧹 Nitpick comments (1)
content/docs/breaking-updates/3-to-4.mdx (1)

128-135: Tighten wording in migration example.

The sentence “You will have to change it too:” should be “change it to:” so the instruction reads naturally.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30d3596 and 1f9c358.

📒 Files selected for processing (2)
  • content/docs/breaking-updates/3-to-4.mdx (1 hunks)
  • content/docs/meta.json (1 hunks)

Comment on lines +53 to +64
| Environment | Flag | Description | Default | Required |
| ------------------------------------------- | --------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------ | -------- |
| `PROVIDERS_[PROVIDER]_CLIENT_ID` | `--providers-[provider]-client-id` | The client ID for the OAuth client. | `` | yes |
| `PROVIDERS_[PROVIDER]_CLIENT_SECRET` | `--providers-[provider]-client-secret` | The client secret for the OAuth client. | `` | yes |
| `PROVIDERS_[PROVIDER]_CLIENT_SECRET_FILE` | `--providers-[provider]-client-secret-file` | A path to a file containing the client secret for the OAuth client. | `` | no |
| `PROVIDERS_[PROVIDER]_SCOPES` | `--providers-[provider]-scopes` | The scopes needed for the OAuth provider. | `` | yes |
| `PROVIDERS_[PROVIDER]_AUTH_URL` | `--providers-[provider]-auth-url` | The authentication URL for the OAuth provider. | `` | yes |
| `PROVIDERS_[PROVIDER]_TOKEN_URL` | `--providers-[provider]-token-url` | The token URL for the OAuth provider. | `` | yes |
| `PROVIDERS_[PROVIDER]_USER_INFO_URL` | `--providers-[provider]-user-info-url` | The user information URL for the OAuth provider. | `` | yes |
| `PROVIDERS_[PROVIDER]_INSECURE_SKIP_VERIFY` | `--providers-[provider]-insecure-skip-verify` | Skip SSL certificate check for provider. | `false` | no |
| `PROVIDERS_[PROVIDER]_NAME` | `--providers-[provider]-name` | The name of the OAuth provider (for the UI button). | Provider ID with first letter capitalized. | no |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clarify whether scopes are actually required for built-in providers.

The table marks PROVIDERS_[PROVIDER]_SCOPES (and other fields) as “Required: yes,” but the callout immediately below says Google/GitHub IDs auto-populate data like scopes. Please reconcile these statements—either adjust the required column or clarify that the “yes” applies only to fully custom providers so users aren’t misled.

Also applies to: 79-81

🤖 Prompt for AI Agents
In content/docs/breaking-updates/3-to-4.mdx around lines 53 to 64 (and also
apply the same change at lines 79–81), the table currently marks
PROVIDERS_[PROVIDER]_SCOPES and other provider fields as “Required: yes” which
conflicts with the following callout that built-in Google/GitHub auto-populate
some values; update the table and adjacent text to reconcile this by either (a)
change the Required column for fields that are optional for built-in providers
to “yes (custom only)” or “no (built-in auto-populates)”, or (b) add a short
footnote on the table header clarifying “Required: yes applies to custom
providers; built-in providers may auto-populate these values”, and mirror that
clarification in the callout so readers understand which fields are mandatory
only for custom providers.

Copy link
Copy Markdown
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: 0

🧹 Nitpick comments (1)
app/routes/discord.ts (1)

3-5: LGTM! Consider removing unnecessary async keyword.

The loader correctly uses the throw redirect() pattern for React Router v7 data routers. The unconditional redirect to the Discord invite is appropriate for this use case.

The async keyword is unnecessary since the function doesn't use await:

-export async function loader() {
+export function loader() {
   throw redirect("https://discord.gg/eHzVaCzRRd");
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f3d330 and cdc140f.

📒 Files selected for processing (2)
  • app/routes.ts (1 hunks)
  • app/routes/discord.ts (1 hunks)
🔇 Additional comments (1)
app/routes.ts (1)

1-8: LGTM! Route configuration follows React Router v7 best practices.

The route definitions correctly use the @react-router/dev/routes API with proper index() and route() helper functions. The configuration includes:

  • Index route for the home page
  • Discord redirect route (leveraging the loader from app/routes/discord.ts)
  • Wildcard route for docs sections
  • API search route

The satisfies RouteConfig assertion ensures type safety while maintaining the required structure.

Based on learnings.

Copy link
Copy Markdown
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: 1

♻️ Duplicate comments (3)
content/docs/community/zitadel-oauth.mdx (1)

44-44: Fix invalid Zitadel scopes

preferred_username and groups are OpenID claims, not requestable Zitadel scopes. Zitadel will reject this configuration with invalid_scope, so the login flow fails. Drop those entries (or replace them with the documented urn:zitadel:iam:project:roles:... scopes if roles are required).

-      - PROVIDERS_ZITADEL_SCOPES=openid profile email preferred_username groups
+      - PROVIDERS_ZITADEL_SCOPES=openid profile email
content/docs/guides/using-the-binary.mdx (1)

33-38: Replace brittle .env export/unset snippet with a POSIX-safe pattern.

export $(grep … | xargs -d '\n') and the paired unset pipeline fall apart as soon as a value contains spaces/quotes, and -d '\n' is GNU-only. Readers with perfectly valid .env files will hit cryptic shell errors. Please switch to a portable approach (e.g. set -a; . ./.env; set +a) and drop the fragile unset pipeline in favor of explicit unset KEY guidance.

-```sh
-export $(grep -v '^#' .env | xargs -d '\n')
-```
-
-<Callout type="info">
-  To unset the environment variables for security purposes, use: `unset $(grep
-  -v '^#' .env | sed -E 's/(.*)=.*/\1/' | xargs)`.
-</Callout>
+```sh
+set -a
+. ./.env
+set +a
+```
+
+<Callout type="info">
+  Unset only the variables you exported, e.g. `unset TINYAUTH_SECRET`.
+</Callout>
content/docs/breaking-updates/3-to-4.mdx (1)

55-81: Resolve the “Required” column contradiction for built-in providers.

Rows for scopes/auth/token/user-info still say “Required: yes,” yet the callout explains Google/GitHub auto-fill those values so users only need ID/secret. Please align the table (and, if needed, the callout) so it’s unambiguous that these fields are only mandatory for fully custom providers—right now the guidance conflicts. Suggested patch:

-| `PROVIDERS_[PROVIDER]_SCOPES`               | `--providers-[provider]-scopes`               | The scopes needed for the OAuth provider.                           | ``                                         | yes      |
-| `PROVIDERS_[PROVIDER]_AUTH_URL`             | `--providers-[provider]-auth-url`             | The authentication URL for the OAuth provider.                      | ``                                         | yes      |
-| `PROVIDERS_[PROVIDER]_TOKEN_URL`            | `--providers-[provider]-token-url`            | The token URL for the OAuth provider.                               | ``                                         | yes      |
-| `PROVIDERS_[PROVIDER]_USER_INFO_URL`        | `--providers-[provider]-user-info-url`        | The user information URL for the OAuth provider.                    | ``                                         | yes      |
+| `PROVIDERS_[PROVIDER]_SCOPES`               | `--providers-[provider]-scopes`               | The scopes needed for the OAuth provider.                           | ``                                         | yes (custom) |
+| `PROVIDERS_[PROVIDER]_AUTH_URL`             | `--providers-[provider]-auth-url`             | The authentication URL for the OAuth provider.                      | ``                                         | yes (custom) |
+| `PROVIDERS_[PROVIDER]_TOKEN_URL`            | `--providers-[provider]-token-url`            | The token URL for the OAuth provider.                               | ``                                         | yes (custom) |
+| `PROVIDERS_[PROVIDER]_USER_INFO_URL`        | `--providers-[provider]-user-info-url`        | The user information URL for the OAuth provider.                    | ``                                         | yes (custom) |

…and add a short note clarifying built-in IDs auto-fill the defaults.

🧹 Nitpick comments (1)
content/docs/guides/advanced.mdx (1)

45-57: Scope the trusted IP range more narrowly.

Recommending 172.16.0.0/12 trusts a very large slice of private address space. Narrow it to the actual networks that sit in front of Traefik (e.g., the precise Docker bridge or host subnet) so you don’t accidentally accept spoofed forwarded headers from other internal actors. Please adjust the example and mention tailoring the CIDR to the environment.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 558c3b3 and af94f8d.

📒 Files selected for processing (13)
  • content/docs/breaking-updates/3-to-4.mdx (1 hunks)
  • content/docs/changelog.mdx (2 hunks)
  • content/docs/community/zitadel-oauth.mdx (1 hunks)
  • content/docs/contributing.mdx (1 hunks)
  • content/docs/guides/access-controls.mdx (1 hunks)
  • content/docs/guides/advanced.mdx (1 hunks)
  • content/docs/guides/github-app-oauth.mdx (1 hunks)
  • content/docs/guides/github-oauth.mdx (1 hunks)
  • content/docs/guides/google-oauth.mdx (1 hunks)
  • content/docs/guides/pocket-id.mdx (1 hunks)
  • content/docs/guides/using-the-binary.mdx (1 hunks)
  • content/docs/reference/cli.mdx (1 hunks)
  • content/docs/reference/configuration.mdx (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • content/docs/guides/pocket-id.mdx
  • content/docs/reference/configuration.mdx
🚧 Files skipped from review as they are similar to previous changes (4)
  • content/docs/guides/github-app-oauth.mdx
  • content/docs/guides/google-oauth.mdx
  • content/docs/reference/cli.mdx
  • content/docs/contributing.mdx
🔇 Additional comments (1)
content/docs/guides/access-controls.mdx (1)

1-145: Comprehensive addition looks solid. Clear structure, accurate callouts, and thorough coverage of label usage. Nicely done.

Comment thread content/docs/changelog.mdx
Copy link
Copy Markdown
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: 1

♻️ Duplicate comments (2)
content/docs/breaking-updates/3-to-4.mdx (1)

55-63: Clarify “Required” column for auto-populated providers.

The table still marks these fields as strictly required, yet the callout right below says Google/GitHub auto-fill them. Please reconcile so readers don’t assume they must supply values that are generated for built-ins.

Consider explicit wording such as “yes (custom providers)”:

-| `PROVIDERS_[PROVIDER]_SCOPES`               | `--providers-[provider]-scopes`               | The scopes needed for the OAuth provider.                           | ``                                         | yes      |
-| `PROVIDERS_[PROVIDER]_AUTH_URL`             | `--providers-[provider]-auth-url`             | The authentication URL for the OAuth provider.                      | ``                                         | yes      |
-| `PROVIDERS_[PROVIDER]_TOKEN_URL`            | `--providers-[provider]-token-url`            | The token URL for the OAuth provider.                               | ``                                         | yes      |
-| `PROVIDERS_[PROVIDER]_USER_INFO_URL`        | `--providers-[provider]-user-info-url`        | The user information URL for the OAuth provider.                    | ``                                         | yes      |
+| `PROVIDERS_[PROVIDER]_SCOPES`               | `--providers-[provider]-scopes`               | The scopes needed for the OAuth provider.                           | ``                                         | yes (custom providers) |
+| `PROVIDERS_[PROVIDER]_AUTH_URL`             | `--providers-[provider]-auth-url`             | The authentication URL for the OAuth provider.                      | ``                                         | yes (custom providers) |
+| `PROVIDERS_[PROVIDER]_TOKEN_URL`            | `--providers-[provider]-token-url`            | The token URL for the OAuth provider.                               | ``                                         | yes (custom providers) |
+| `PROVIDERS_[PROVIDER]_USER_INFO_URL`        | `--providers-[provider]-user-info-url`        | The user information URL for the OAuth provider.                    | ``                                         | yes (custom providers) |
content/docs/changelog.mdx (1)

8-11: Fix migration guide link slug.

Fumadocs strips file extensions, so this .mdx link 404s. Point it at the extensionless route.

-  [documentation](/docs/breaking-updates/3-to-4.mdx).
+  [documentation](/docs/breaking-updates/3-to-4).
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af94f8d and 05de658.

📒 Files selected for processing (5)
  • app/docs/search.ts (1 hunks)
  • app/routes.ts (1 hunks)
  • app/routes/discord.tsx (1 hunks)
  • content/docs/breaking-updates/3-to-4.mdx (1 hunks)
  • content/docs/changelog.mdx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/docs/search.ts
🔇 Additional comments (1)
app/routes.ts (1)

1-8: LGTM!

The route configuration follows React Router v7 best practices correctly:

  • Uses the @react-router/dev/routes API for route definitions
  • Employs TypeScript's satisfies operator for type safety
  • Route structure is clean and follows standard patterns (index route, named routes, catch-all docs route, and API resource route)

Comment thread app/routes/discord.tsx
Comment on lines +1 to +24
export default function Discord() {
const discordUrl = "https://discord.gg/eHzVaCzRRd";
return (
<html>
<head>
<meta httpEquiv="refresh" content={`0; url=${discordUrl}`} />
<title>Redirecting...</title>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div className="p-4">
<p className="mb-1">Redirecting to the Discord server...</p>
<a
className="text-fd-info underline hover:opacity-80"
href={discordUrl}
>
Redirect now
</a>
</div>
</body>
</html>
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Replace full HTML document with a loader redirect.

Returning a full <html> document from a React Router v7 route component bypasses the app shell, layout components, and React Router context. This breaks the routing pipeline and prevents Tailwind classes from being applied correctly.

The recommended pattern in React Router v7 is to use a loader that returns a redirect Response for server-side redirects:

+import { redirect } from "react-router";
+
+export function loader() {
+  return redirect("https://discord.gg/eHzVaCzRRd");
+}
+
 export default function Discord() {
-  const discordUrl = "https://discord.gg/eHzVaCzRRd";
-  return (
-    <html>
-      <head>
-        <meta httpEquiv="refresh" content={`0; url=${discordUrl}`} />
-        <title>Redirecting...</title>
-        <meta charSet="utf-8" />
-        <meta name="viewport" content="width=device-width, initial-scale=1" />
-      </head>
-      <body>
-        <div className="p-4">
-          <p className="mb-1">Redirecting to the Discord server...</p>
-          <a
-            className="text-fd-info underline hover:opacity-80"
-            href={discordUrl}
-          >
-            Redirect now
-          </a>
-        </div>
-      </body>
-    </html>
-  );
+  return null; // This component won't render due to loader redirect
 }

Alternatively, if you need a client-side redirect with a fallback UI:

import { useEffect } from "react";
import { useNavigate } from "react-router";

export default function Discord() {
  const navigate = useNavigate();
  const discordUrl = "https://discord.gg/eHzVaCzRRd";

  useEffect(() => {
    window.location.href = discordUrl;
  }, [discordUrl]);

  return (
    <div className="p-4">
      <p className="mb-1">Redirecting to the Discord server...</p>
      <a
        className="text-fd-info underline hover:opacity-80"
        href={discordUrl}
      >
        Redirect now
      </a>
    </div>
  );
}

Based on learnings: React Router v7 consolidates loader/redirect patterns for Data Mode routing.

🤖 Prompt for AI Agents
In app/routes/discord.tsx around lines 1-24 you're returning a full <html>
document which bypasses the app shell and React Router context; replace it with
a route component that either (a) uses a route loader to perform a server-side
redirect by returning a Redirect/Response to the Discord URL, or (b) performs a
client-side redirect: remove the <html>/<head>/<body> wrapper and export a
normal React component that renders only the small fallback UI (div with text
and link) and triggers window.location.href (or useNavigate/useEffect) to
navigate to the discordUrl on mount; update the file to implement one of these
two patterns and remove the full document markup so Tailwind and layouts work
correctly.

Copy link
Copy Markdown
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: 0

♻️ Duplicate comments (1)
.github/workflows/deploy.yml (1)

29-31: Restore cache key to hash the actual Bun lockfile (bun.lockb), or include both.

Hashing bun.lock breaks cache busting when Bun generates bun.lockb by default, leading to stale caches. Use the binary lockfile (or both) to ensure cache invalidation on dependency changes.

Apply one of these diffs:

Option A (binary lockfile only, current Bun default):

-          key: ${{ runner.os }}-bun-web-${{ hashFiles('bun.lock') }}
+          key: ${{ runner.os }}-bun-web-${{ hashFiles('bun.lockb') }}

Option B (future‑proof: hash both text and binary lockfiles):

-          key: ${{ runner.os }}-bun-web-${{ hashFiles('bun.lock') }}
+          key: ${{ runner.os }}-bun-web-${{ hashFiles('bun.lockb', 'bun.lock') }}

Nice to have: also include the Bun version in the key to avoid cross‑version cache reuse.

🧹 Nitpick comments (1)
.github/workflows/deploy.yml (1)

18-21: Consider dropping setup-node if not required.

If the build/test steps run entirely via Bun, actions/setup-node@v5 is unnecessary and slows CI. Remove it unless a step actually needs Node 20.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05de658 and 3b1babe.

📒 Files selected for processing (1)
  • .github/workflows/deploy.yml (1 hunks)
🔇 Additional comments (2)
.github/workflows/deploy.yml (2)

34-34: LGTM on frozen lockfile; verify a lockfile is committed.

bun install --frozen-lockfile is good for reproducibility. Ensure the repo contains the expected lockfile (bun.lockb unless you’ve switched to text) so this doesn’t fail unexpectedly.


42-42: Verify artifact path matches the new build output.

Confirm your build produces build/client with an index.html at its root. If your tool outputs elsewhere (e.g., dist or build), update the path accordingly to avoid deploying an empty site.

@steveiliop56 steveiliop56 merged commit 81f6ef0 into main Oct 8, 2025
1 check passed
@coderabbitai coderabbitai Bot mentioned this pull request Feb 27, 2026
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