Skip to content

Theme transition, Rage click effect, Playground page#1180

Merged
Developing-Gamer merged 6 commits intolist-component-redesignfrom
fun-shyt
Feb 11, 2026
Merged

Theme transition, Rage click effect, Playground page#1180
Developing-Gamer merged 6 commits intolist-component-redesignfrom
fun-shyt

Conversation

@Developing-Gamer
Copy link
Copy Markdown
Contributor

@Developing-Gamer Developing-Gamer commented Feb 10, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added interactive Component Playground to explore and customize design elements with live preview.
    • Introduced animated cursor blast effect triggered by rapid interactions.
    • Added glassmorphic styling option to cards and components.
    • Enhanced tabs and toggles with async operation support and loading indicators.
  • Improvements

    • Smoother theme switching with improved view transition animations.
    • Expanded component visibility controls (icon/label toggles, content display modes).
    • Enhanced design documentation with new section layouts and patterns.

@Developing-Gamer Developing-Gamer self-assigned this Feb 10, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented Feb 10, 2026

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

Project Deployment Actions Updated (UTC)
stack-backend Ready Ready Preview, Comment Feb 11, 2026 4:11am
stack-dashboard Ready Ready Preview, Comment Feb 11, 2026 4:11am
stack-demo Ready Ready Preview, Comment Feb 11, 2026 4:11am
stack-docs Ready Ready Preview, Comment Feb 11, 2026 4:11am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 10, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fun-shyt

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.

This commit updates the PageClient to integrate new features for the DesignBadge and DesignAlert components. The DesignBadge now supports a content mode prop, allowing for flexible display options (text, icon, or both). The DesignAlert component has been refactored to utilize a centralized icon mapping, improving code maintainability. Additionally, unnecessary props have been removed, and the props documentation has been updated to reflect these changes, enhancing the overall user experience in badge and alert interactions.
…eractions

This commit updates the PageClient by integrating new features across various design components, including DesignCard, DesignTabs, and DesignPillToggle. The card component now supports a glassmorphic prop for improved visual effects, while the tabs and pill toggle components have been refactored to handle loading states during asynchronous actions. Additionally, unnecessary props have been removed, and props documentation has been updated to enhance clarity and maintainability, resulting in a more cohesive user experience.
@Developing-Gamer Developing-Gamer marked this pull request as ready for review February 11, 2026 04:04
@Developing-Gamer Developing-Gamer merged commit b7f554b into list-component-redesign Feb 11, 2026
16 of 27 checks passed
@Developing-Gamer Developing-Gamer deleted the fun-shyt branch February 11, 2026 04:10
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Feb 11, 2026

Greptile Overview

Greptile Summary

This PR expands the dashboard’s design-language system with a new Playground page, a cursor “rage click” blast visual effect, and an updated theme toggle view-transition animation.

Key changes include:

  • New /playground route that lets you interactively configure and preview design-language components.
  • New CursorBlastEffect component exported from the design-language barrel and mounted on the design-language demo page.
  • Theme toggle now uses the View Transitions API and a temporary global .vt-disable-transitions class to avoid laggy component transitions.
  • Multiple design-language components were updated/refactored (card nesting glassmorphic defaults, menu/list integrations, badge content modes, async loading affordances for tabs/pill toggles, and optional table header).

Main merge blockers to address are API compatibility changes (notably DesignAlert icon handling and DesignButton size variants) that can break consumers or the new playground controls.

Confidence Score: 3/5

  • Moderate risk to merge due to API compatibility changes in shared design components.
  • Most changes are additive UI/demo work, but the PR includes breaking prop/variant changes in exported design-language components (e.g., DesignAlert icon prop removal; DesignButton size variants) that can break consumers and even the new playground options if not aligned.
  • apps/dashboard/src/components/design-language/alert.tsx, apps/dashboard/src/components/design-language/button.tsx, apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx

Important Files Changed

Filename Overview
apps/dashboard/src/components/design-language/alert.tsx Removes customizable icon prop and always renders a variant-based icon; this is a breaking API change that can crash runtime if callers still pass icon.
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx Adds a new component playground page for design-language components; note it uses DesignButton size/variant options including a plain size option that no longer exists in DesignButton types.
apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page.tsx Adds server page wrapper for the new Playground route with static metadata.
apps/dashboard/src/app/(main)/(protected)/projects/[projectId]/design-language/page-client.tsx Refactors design-language demo page and adds global CursorBlastEffect on the page, which adds a window click listener (triggers only on rage-click threshold).
apps/dashboard/src/app/globals.css Adds view-transition root transform-origin and a .vt-disable-transitions class that forces transition-duration:0 for all descendants during theme transitions.
apps/dashboard/src/components/design-language/badge.tsx Adds contentMode to support icon-only/text-only badges and enforces icon required for icon-only mode; adds aria-label/title for accessibility.
apps/dashboard/src/components/design-language/button.tsx Removes size: plain option from DesignButton variants; this can break any consumers using size="plain" (including the new playground if it exposes it).
apps/dashboard/src/components/design-language/card.tsx Adds nesting context to auto-default glassmorphic inside cards and changes default variant to "compact"; this can silently change UI where callers rely on default variant.
apps/dashboard/src/components/design-language/cursor-blast-effect.tsx Introduces CursorBlastEffect with optional container scoping; attaches click listener on window or container and spawns portal-based blasts.
apps/dashboard/src/components/design-language/editable-grid.tsx Updates editable grid UI, adds spinners during async updates, and replaces InlineSaveDiscard with a design-language styled save/discard footer.
apps/dashboard/src/components/design-language/index.ts Exports CursorBlastEffect from design-language barrel index.
apps/dashboard/src/components/design-language/input.tsx Restyles prefixed inputs to use a unified wrapper and removes inner input borders/shadows for prefix variant.
apps/dashboard/src/components/design-language/list.tsx Adds optional icon/avatar display controls and replaces delete dropdown with DesignMenu actions variant.
apps/dashboard/src/components/design-language/menu.tsx Switches menu trigger buttons to DesignButton and keeps toggles menus open by preventing default onSelect for checkbox items.
apps/dashboard/src/components/design-language/pill-toggle.tsx Allows async onSelect with loading state and adds showIcons/showLabels options; forces at least one visible by overriding false/false combination.
apps/dashboard/src/components/design-language/table.tsx Adds showHeader flag and forces DesignCard glassmorphic on data tables, changing default appearance of all tables.
apps/dashboard/src/components/design-language/tabs.tsx Allows async onSelect with loading spinner, removes size "lg" option, and uses glassmorphic-default inference via card context.
apps/dashboard/src/components/theme-toggle.tsx Reworks theme toggle to use view-transition animations with temporary global transition disable class; uses a fixed timeout to re-enable transitions.

Sequence Diagram

sequenceDiagram
  autonumber
  participant U as User
  participant TT as ThemeToggle
  participant D as document
  participant VT as ViewTransition
  participant CSS as globals.css

  U->>TT: click toggle button
  TT->>D: matchMedia(prefers-reduced-motion)
  alt no view transition or reduced motion
    TT->>TT: setTheme(nextTheme)
  else view transition supported
    TT->>D: addClass(vt-disable-transitions)
    TT->>D: startViewTransition(() => setTheme(nextTheme))
    D-->>VT: returns transition
    TT->>VT: await ready
    TT->>D: animate(::view-transition-old(root))
    TT->>D: animate(::view-transition-new(root))
    TT->>CSS: vt-disable-transitions forces transition-duration:0
    TT->>D: setTimeout(removeClass, 600ms)
  end

  participant DL as DesignLanguagePageClient
  participant CBE as CursorBlastEffect
  participant W as window

  DL->>CBE: mount <CursorBlastEffect/>
  CBE->>W: addEventListener(click)
  loop user clicks
    W-->>CBE: click events
    CBE->>CBE: track recent clicks
    alt rage threshold met
      CBE->>CBE: setState(add blast)
      CBE->>W: setTimeout(remove blast)
      CBE->>D: portal blast layer to body
    end
  end
  CBE->>W: removeEventListener(click) on unmount
Loading

Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

18 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Feb 11, 2026

Additional Comments (2)

apps/dashboard/src/components/design-language/alert.tsx
Breaking alert API

This PR removes the icon?: React.ElementType prop from DesignAlertProps and now always renders an icon based on variant. That’s a breaking change for any consumers (internal or external) that previously passed icon or relied on hiding the icon by omitting it. If the intent is to keep backwards compatibility, consider restoring the icon prop (and defaulting to the variant icon when icon is undefined).


apps/dashboard/src/components/design-language/button.tsx
Removed plain size

DesignButton dropped the size: plain variant, but the new playground still exposes btnSize options that include a "plain" size (and existing code elsewhere uses size="plain" on the UI Button). If DesignButton is meant to stay API-compatible with @/components/ui/Button, either keep plain in the size variants or update any DesignButton callers/options to avoid plain.

Also appears in: apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx (btnSize state/options).

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

Caution

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

⚠️ Outside diff range comments (2)
apps/dashboard/src/components/design-language/table.tsx (1)

53-89: ⚠️ Potential issue | 🟡 Minor

Avoid a stray divider when showHeader is false.

The content wrapper always applies a top border, so hiding the header still leaves a separator and no top padding. Consider gating the border (and adding top padding) when the header is suppressed.

Suggested tweak
-      <div
-        className={cn(
-          "border-t border-black/[0.12] dark:border-white/[0.06] px-5 pb-5 [&_div.rounded-md.border]:border-0 [&_div.rounded-md.border]:shadow-none",
-          contentClassName
-        )}
-      >
+      <div
+        className={cn(
+          "px-5 pb-5 [&_div.rounded-md.border]:border-0 [&_div.rounded-md.border]:shadow-none",
+          showHeader
+            ? "border-t border-black/[0.12] dark:border-white/[0.06]"
+            : "pt-5",
+          contentClassName
+        )}
+      >
apps/dashboard/src/components/design-language/list.tsx (1)

83-111: ⚠️ Potential issue | 🟠 Major

Replace the avatarGradients record and as const with a typed Map.

The current code violates two coding guidelines: using as const (type assertion) and using a record instead of ES6 Map. The Map-based refactor removes the type assertion and aligns with the preference for ES6 maps.

Suggested refactor
+type AvatarGradient = NonNullable<DesignUserListProps["gradient"]>;
+
-const avatarGradients = {
-  "blue-purple": "from-blue-500 to-purple-500",
-  "cyan-blue": "from-cyan-500 to-blue-500",
-  "none": "from-muted-foreground/30 to-muted-foreground/30",
-} as const;
+const avatarGradients = new Map<AvatarGradient, string>([
+  ["blue-purple", "from-blue-500 to-purple-500"],
+  ["cyan-blue", "from-cyan-500 to-blue-500"],
+  ["none", "from-muted-foreground/30 to-muted-foreground/30"],
+]);
 
 export function DesignUserList({
   users,
   onUserClick,
   showAvatar = true,
   gradient = "blue-purple",
   className,
 }: DesignUserListProps) {
+  const gradientClass = avatarGradients.get(gradient) ?? "from-blue-500 to-purple-500";
   return (
@@
-              avatarGradients[gradient]
+              gradientClass
             )}>
🤖 Fix all issues with AI agents
In
`@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/playground/page-client.tsx:
- Around line 163-167: Replace the Record-based constants with ES6 Maps: change
STATUS_BADGE from Record to a Map<DemoProduct["status"], {label: string, color:
DesignBadgeColor}> and initialize it with new Map([...]) and do the same for
menuToggles (use new Map([...]) with appropriate key/value types) at the three
locations flagged; update all usages to use Map.prototype.get(key) (and handle
undefined or provide defaults) instead of bracket indexing, and update any type
annotations or helper functions that assumed a Record to accept/handle a Map
(e.g., where STATUS_BADGE[...] or menuToggles[...] are referenced, replace with
.get(...) and add fallback handling).
- Around line 148-173: The demo data and table accessors use unsafe type
assertions; define a DemoUser type and update the DEMO_USERS constant to use
that type (remove all `as const`), update the table cell accessors to read from
row.original (use row.original.price instead of row.getValue("price") as number
and row.original.status instead of row.getValue("status") as
DemoProduct["status"]), and fix the grid columns state conversion by replacing
Number(v) as 1 | 2 with a conditional expression (e.g., v === "1" ? 1 : 2) so no
`as` casts remain (references: DemoUser, DEMO_USERS, DemoProduct, STATUS_BADGE,
row.original, and the grid columns state variable).

In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/design-language/page-client.tsx:
- Around line 840-850: The props table passed to PropsTable is inaccurate: it
lists props (subtitle, size, glassmorphic) that DesignListItemRow/DesignUserList
do not accept and omits actual props like showIcon, showAvatar, and users;
update the table data so each component's documented props match its real API by
either splitting into two tables or changing the array to reflect
DesignListItemRow and DesignUserList props precisely (edit the PropsTable
invocation near the existing PropsTable component usage), replacing/removing
incorrect entries and adding the missing showIcon, showAvatar, users entries
with correct types and descriptions.

In `@apps/dashboard/src/components/design-language/cursor-blast-effect.tsx`:
- Around line 153-159: The code uses explicit type assertions and an
eslint-disable when attaching listeners to `target`; remove the casts and the
eslint-disable and call `addEventListener`/`removeEventListener` directly on
`target` (the value computed from `containerRef?.current ?? window`) and pass
`onClick` (and `onClick` as the listener) as-is; also leave the rest of the
cleanup (iterating `timeoutIdsRef.current`) unchanged so `timeoutIds` and
`onClick` are used without bypassing the type system.

In `@apps/dashboard/src/components/design-language/tabs.tsx`:
- Around line 120-126: The handleSelect function currently uses a type assertion
(result as Promise<void>) to detect a Promise; change this to a runtime
instanceof check: if (result instanceof Promise) { ... } so you avoid the
no-cast violation, keep the setLoadingCategoryId(categoryId) and finally(() =>
setLoadingCategoryId(null)) behavior, and call
runAsynchronouslyWithAlert(result.finally(...)) (remove the outer
Promise.resolve wrapper) to ensure the Promise returned by onSelect is awaited
with the same cleanup logic.
🧹 Nitpick comments (4)
apps/dashboard/src/app/globals.css (1)

164-173: Consider merging duplicate selector blocks.

Lines 164-168 and 170-173 both target the same ::view-transition-old(root), ::view-transition-new(root) selectors. While functionally correct, consolidating them into a single rule block improves maintainability.

♻️ Proposed consolidation
 ::view-transition-old(root),
 ::view-transition-new(root) {
   animation: none;
   mix-blend-mode: normal;
+  transform-origin: center center;
 }
-
-::view-transition-old(root),
-::view-transition-new(root) {
-  transform-origin: center center;
-}
apps/dashboard/src/components/design-language/pill-toggle.tsx (1)

81-89: Avoid type cast; use a type guard or instanceof Promise.

Line 83 uses as Promise<void> to bypass the type system. Per coding guidelines, type casts should be avoided unless explicitly approved.

♻️ Proposed fix using instanceof
   const handleClick = (optionId: string) => {
     const result = onSelect(optionId);
-    if (result && typeof (result as Promise<void>).then === "function") {
+    if (result instanceof Promise) {
       setLoadingOptionId(optionId);
       runAsynchronouslyWithAlert(
-        Promise.resolve(result).finally(() => setLoadingOptionId(null))
+        result.finally(() => setLoadingOptionId(null))
       );
     }
   };
apps/dashboard/src/components/design-language/badge.tsx (1)

66-69: Redundant validation check.

The !showLabel && !showIcon check on lines 67-69 can never be true given the logic in getShowLabelShowIcon:

  • "both"showLabel: true
  • "text"showLabel: true
  • "icon"showIcon: true (or throws)

This is harmless as a defensive measure but could be removed for clarity.

apps/dashboard/src/components/design-language/editable-grid.tsx (1)

536-575: Minor: handleSave is double-wrapped by useAsyncCallback.

handleSave from useAsyncCallback (line 545) is passed to DesignButton's onClick, but DesignButton internally also uses useAsyncCallback (per button.tsx lines 67-69). This double-wrapping is harmless but redundant.

Since isSaving from line 545 is used for the disabled state, this approach is still valid. However, you could simplify by passing onSave directly to DesignButton and using its internal loading prop for the disabled state.

Comment on lines +148 to +173
type DemoProduct = {
id: string,
name: string,
category: string,
price: number,
status: "active" | "draft" | "archived",
};

const DEMO_PRODUCTS: DemoProduct[] = [
{ id: "1", name: "Widget Pro", category: "Hardware", price: 29.99, status: "active" },
{ id: "2", name: "Gadget Lite", category: "Accessories", price: 14.99, status: "draft" },
{ id: "3", name: "Tool Max", category: "Software", price: 49.99, status: "archived" },
{ id: "4", name: "Sensor Hub", category: "Hardware", price: 79.99, status: "active" },
];

const STATUS_BADGE: Record<DemoProduct["status"], { label: string, color: DesignBadgeColor }> = {
active: { label: "Active", color: "green" },
draft: { label: "Draft", color: "orange" },
archived: { label: "Archived", color: "red" },
};

const DEMO_USERS = [
{ name: "Ada Lovelace", email: "ada@example.com", time: "Active 1h ago", color: "cyan" as const },
{ name: "Grace Hopper", email: "grace@example.com", time: "Active 3h ago", color: "blue" as const },
{ name: "Alan Turing", email: "alan@example.com", time: "Active 5h ago", color: "cyan" as const },
];
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, check if the file exists and get its size
file_path="apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

if [ -f "$file_path" ]; then
  echo "=== File found ==="
  wc -l "$file_path"
else
  echo "=== File not found, searching for similar files ==="
  find apps/dashboard -name "page-client.tsx" -type f
fi

Repository: stack-auth/stack-auth

Length of output: 177


🏁 Script executed:

#!/bin/bash
# Extract the specific line ranges mentioned in the review
file_path="apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

echo "=== Lines 148-173 ==="
sed -n '148,173p' "$file_path" | cat -n

echo ""
echo "=== Lines 300-305 ==="
sed -n '300,305p' "$file_path" | cat -n

echo ""
echo "=== Lines 1031-1035 ==="
sed -n '1031,1035p' "$file_path" | cat -n

Repository: stack-auth/stack-auth

Length of output: 1994


🏁 Script executed:

#!/bin/bash
# Search for all 'as' type assertions in the file
file_path="apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

echo "=== All 'as' type assertions in file ==="
rg '\s+as\s+' "$file_path" -n

Repository: stack-auth/stack-auth

Length of output: 615


🏁 Script executed:

#!/bin/bash
# Get context around line 312
file_path="apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

echo "=== Context around line 312 (lines 305-320) ==="
sed -n '305,320p' "$file_path" | cat -n

Repository: stack-auth/stack-auth

Length of output: 718


🏁 Script executed:

#!/bin/bash
# Get context to understand the grid columns usage and state
file_path="apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

echo "=== Context around line 1034 (lines 1025-1040) ==="
sed -n '1025,1040p' "$file_path" | cat -n

echo ""
echo "=== Search for gridCols state declaration ==="
rg 'gridCols' "$file_path" -B 2 -A 2 | head -30

Repository: stack-auth/stack-auth

Length of output: 1496


🏁 Script executed:

#!/bin/bash
# Check how row.getValue is typed and if row.original is available
file_path="apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

echo "=== Search for row.original usage in file ==="
rg 'row\.original' "$file_path" -n | head -10

echo ""
echo "=== Search for table/columns definition ==="
rg -A 5 'const columns' "$file_path" | head -50

Repository: stack-auth/stack-auth

Length of output: 155


🏁 Script executed:

#!/bin/bash
# Get the full columns definition to understand table structure
file_path="apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

echo "=== Search for useMemo or columns variable (broader context) ==="
rg -B 5 'accessorKey: "price"' "$file_path" -A 30 | head -60

Repository: stack-auth/stack-auth

Length of output: 1466


🏁 Script executed:

#!/bin/bash
# Check imports to understand what table/components are used
file_path="apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

echo "=== Imports and type definitions ==="
head -50 "$file_path" | cat -n

Repository: stack-auth/stack-auth

Length of output: 1440


Remove all type assertions (as) to satisfy TS safety rules.

Six type assertions bypass the type system and violate the guideline "Never use as, any, or type casts to bypass the type system unless explicitly approved." Remove them by adding proper type definitions to demo data and using row.original for table cell accessors:

  1. DEMO_USERS array (lines 170-172): Add DemoUser type definition and remove as const annotations.
  2. Price cell (line 304): Use row.original.price instead of row.getValue("price") as number.
  3. Status cell (line 312): Use row.original.status instead of row.getValue("status") as DemoProduct["status"].
  4. Grid columns state (line 1034): Replace Number(v) as 1 | 2 with conditional ternary: v === "1" ? 1 : 2.
Proposed fixes
 type DemoProduct = {
   id: string,
   name: string,
   category: string,
   price: number,
   status: "active" | "draft" | "archived",
 };

-const DEMO_USERS = [
-  { name: "Ada Lovelace", email: "ada@example.com", time: "Active 1h ago", color: "cyan" as const },
-  { name: "Grace Hopper", email: "grace@example.com", time: "Active 3h ago", color: "blue" as const },
-  { name: "Alan Turing", email: "alan@example.com", time: "Active 5h ago", color: "cyan" as const },
-];
+type DemoUser = {
+  name: string;
+  email: string;
+  time: string;
+  color: "cyan" | "blue";
+};
+
+const DEMO_USERS: DemoUser[] = [
+  { name: "Ada Lovelace", email: "ada@example.com", time: "Active 1h ago", color: "cyan" },
+  { name: "Grace Hopper", email: "grace@example.com", time: "Active 3h ago", color: "blue" },
+  { name: "Alan Turing", email: "alan@example.com", time: "Active 5h ago", color: "cyan" },
+];

 // Price cell (line 304)
-            ${(row.getValue("price") as number).toFixed(2)}
+            ${row.original.price.toFixed(2)}

 // Status cell (line 312)
-          const s = row.getValue("status") as DemoProduct["status"];
+          const s = row.original.status;

 // Grid columns (line 1034)
-                  setGridCols(Number(v) as 1 | 2);
+                  setGridCols(v === "1" ? 1 : 2);
🤖 Prompt for AI Agents
In
`@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/playground/page-client.tsx
around lines 148 - 173, The demo data and table accessors use unsafe type
assertions; define a DemoUser type and update the DEMO_USERS constant to use
that type (remove all `as const`), update the table cell accessors to read from
row.original (use row.original.price instead of row.getValue("price") as number
and row.original.status instead of row.getValue("status") as
DemoProduct["status"]), and fix the grid columns state conversion by replacing
Number(v) as 1 | 2 with a conditional expression (e.g., v === "1" ? 1 : 2) so no
`as` casts remain (references: DemoUser, DEMO_USERS, DemoProduct, STATUS_BADGE,
row.original, and the grid columns state variable).

Comment on lines +163 to +167
const STATUS_BADGE: Record<DemoProduct["status"], { label: string, color: DesignBadgeColor }> = {
active: { label: "Active", color: "green" },
draft: { label: "Draft", color: "orange" },
archived: { label: "Archived", color: "red" },
};
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

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and its length
wc -l "apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

Repository: stack-auth/stack-auth

Length of output: 158


🏁 Script executed:

# Read the specific sections mentioned in the review
sed -n '163,167p' "apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

Repository: stack-auth/stack-auth

Length of output: 307


🏁 Script executed:

# Check the other mentioned locations
echo "=== Lines 256-257 ===" && sed -n '256,257p' "apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"
echo "=== Lines 309-313 ===" && sed -n '309,313p' "apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"
echo "=== Lines 622-626 ===" && sed -n '622,626p' "apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

Repository: stack-auth/stack-auth

Length of output: 995


🏁 Script executed:

# Get more context around STATUS_BADGE to understand usage
sed -n '160,200p' "apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

Repository: stack-auth/stack-auth

Length of output: 2071


🏁 Script executed:

# Get context around menuToggles
sed -n '250,270p' "apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

Repository: stack-auth/stack-auth

Length of output: 1290


🏁 Script executed:

# Get more context to find where STATUS_BADGE is used in the status cell
sed -n '300,330p' "apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

Repository: stack-auth/stack-auth

Length of output: 1143


🏁 Script executed:

# Find menu toggle usage around line 622
sed -n '615,640p' "apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

Repository: stack-auth/stack-auth

Length of output: 1165


🏁 Script executed:

# Search for all Record usages in the file to ensure the review caught everything
rg "Record<" "apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx"

Repository: stack-auth/stack-auth

Length of output: 278


🏁 Script executed:

# Also check if there are any other object/dictionary patterns that should be converted
# Look for useState with object initializers that might represent mappings
grep -n "useState.*{.*:.*}" "apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx" | head -20

Repository: stack-auth/stack-auth

Length of output: 184


Use Map instead of Record for STATUS_BADGE and menuToggles.

Repository guideline requires ES6 maps instead of records. All three locations where these are defined or used (lines 163–167, 256, 309–313, 622–626) need updating for consistency.

Proposed fix
-const STATUS_BADGE: Record<DemoProduct["status"], { label: string, color: DesignBadgeColor }> = {
-  active: { label: "Active", color: "green" },
-  draft: { label: "Draft", color: "orange" },
-  archived: { label: "Archived", color: "red" },
-};
+const STATUS_BADGE = new Map<DemoProduct["status"], { label: string; color: DesignBadgeColor }>([
+  ["active", { label: "Active", color: "green" }],
+  ["draft", { label: "Draft", color: "orange" }],
+  ["archived", { label: "Archived", color: "red" }],
+]);

-const [menuToggles, setMenuToggles] = useState<Record<string, boolean>>({ opt1: true, opt2: false, opt3: true });
+const [menuToggles, setMenuToggles] = useState(
+  () =>
+    new Map<string, boolean>([
+      ["opt1", true],
+      ["opt2", false],
+      ["opt3", true],
+    ])
+);

 // status cell
-const s = row.getValue("status") as DemoProduct["status"];
-return <DesignBadge label={STATUS_BADGE[s].label} color={STATUS_BADGE[s].color} size="sm" />;
+const status = row.original.status;
+const badge = STATUS_BADGE.get(status);
+if (!badge) throw new Error(`Unknown status "${status}"`);
+return <DesignBadge label={badge.label} color={badge.color} size="sm" />;

 // menu toggles
-              { id: "opt1", label: "Name", checked: !!menuToggles.opt1 },
-              { id: "opt2", label: "Status", checked: !!menuToggles.opt2 },
-              { id: "opt3", label: "Price", checked: !!menuToggles.opt3 },
+              { id: "opt1", label: "Name", checked: !!menuToggles.get("opt1") },
+              { id: "opt2", label: "Status", checked: !!menuToggles.get("opt2") },
+              { id: "opt3", label: "Price", checked: !!menuToggles.get("opt3") },
             ]}
-            onToggleChange={(id, checked) => setMenuToggles((prev) => ({ ...prev, [id]: checked }))}
+            onToggleChange={(id, checked) =>
+              setMenuToggles((prev) => {
+                const next = new Map(prev);
+                next.set(id, checked);
+                return next;
+              })
+            }
🤖 Prompt for AI Agents
In
`@apps/dashboard/src/app/`(main)/(protected)/(outside-dashboard)/playground/page-client.tsx
around lines 163 - 167, Replace the Record-based constants with ES6 Maps: change
STATUS_BADGE from Record to a Map<DemoProduct["status"], {label: string, color:
DesignBadgeColor}> and initialize it with new Map([...]) and do the same for
menuToggles (use new Map([...]) with appropriate key/value types) at the three
locations flagged; update all usages to use Map.prototype.get(key) (and handle
undefined or provide defaults) instead of bracket indexing, and update any type
annotations or helper functions that assumed a Record to accept/handle a Map
(e.g., where STATUS_BADGE[...] or menuToggles[...] are referenced, replace with
.get(...) and add fallback handling).

Comment on lines +840 to +850
<PropsTable props={[
{ name: "icon", type: "ReactElement", description: "Optional leading icon for list rows." },
{ name: "title", type: "string", description: "Primary row label." },
{ name: "subtitle", type: "string", description: "Optional supporting text." },
{ name: "onClick", type: "() => void", description: "Row click handler." },
{ name: "onEdit", type: "() => void", description: "Optional edit action for row variants." },
{ name: "onDelete", type: "() => void", description: "Optional delete action for row variants." },
{ name: "size", type: "'sm' | 'md' | 'lg' | ...", default: "'md'", description: "Controls row padding and density." },
{ name: "glassmorphic", type: "boolean", default: "true", description: "Use when list is outside a parent card." },
{ name: "gradient", type: "'blue' | 'cyan' | 'purple' | 'green' | 'orange' | 'default'", description: "Optional accent on hover." },
]} />
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

Update the List Components props table to match actual APIs.

The table lists props like subtitle, size, and glassmorphic that aren’t on DesignListItemRow/DesignUserList, and it omits showIcon, showAvatar, and users. Consider revising the list or splitting it into two tables.

Possible update
-              { name: "icon", type: "ReactElement", description: "Optional leading icon for list rows." },
-              { name: "title", type: "string", description: "Primary row label." },
-              { name: "subtitle", type: "string", description: "Optional supporting text." },
-              { name: "onClick", type: "() => void", description: "Row click handler." },
-              { name: "onEdit", type: "() => void", description: "Optional edit action for row variants." },
-              { name: "onDelete", type: "() => void", description: "Optional delete action for row variants." },
-              { name: "size", type: "'sm' | 'md' | 'lg' | ...", default: "'md'", description: "Controls row padding and density." },
-              { name: "glassmorphic", type: "boolean", default: "true", description: "Use when list is outside a parent card." },
-              { name: "gradient", type: "'blue' | 'cyan' | 'purple' | 'green' | 'orange' | 'default'", description: "Optional accent on hover." },
+              { name: "icon", type: "ReactElement", description: "Leading icon for DesignListItemRow." },
+              { name: "title", type: "string", description: "Primary row label." },
+              { name: "showIcon", type: "boolean", default: "true", description: "Toggle leading icon visibility." },
+              { name: "onEdit", type: "() => void", description: "Optional edit action for list rows." },
+              { name: "onDelete", type: "() => void", description: "Optional delete action for list rows." },
+              { name: "users", type: "DesignUserListRow[]", description: "Rows rendered by DesignUserList." },
+              { name: "onUserClick", type: "(user) => void", description: "Row click handler for DesignUserList." },
+              { name: "showAvatar", type: "boolean", default: "true", description: "Toggle avatar visibility." },
+              { name: "gradient", type: "'blue-purple' | 'cyan-blue' | 'none'", default: "'blue-purple'", description: "Avatar gradient style." },
📝 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
<PropsTable props={[
{ name: "icon", type: "ReactElement", description: "Optional leading icon for list rows." },
{ name: "title", type: "string", description: "Primary row label." },
{ name: "subtitle", type: "string", description: "Optional supporting text." },
{ name: "onClick", type: "() => void", description: "Row click handler." },
{ name: "onEdit", type: "() => void", description: "Optional edit action for row variants." },
{ name: "onDelete", type: "() => void", description: "Optional delete action for row variants." },
{ name: "size", type: "'sm' | 'md' | 'lg' | ...", default: "'md'", description: "Controls row padding and density." },
{ name: "glassmorphic", type: "boolean", default: "true", description: "Use when list is outside a parent card." },
{ name: "gradient", type: "'blue' | 'cyan' | 'purple' | 'green' | 'orange' | 'default'", description: "Optional accent on hover." },
]} />
<PropsTable props={[
{ name: "icon", type: "ReactElement", description: "Leading icon for DesignListItemRow." },
{ name: "title", type: "string", description: "Primary row label." },
{ name: "showIcon", type: "boolean", default: "true", description: "Toggle leading icon visibility." },
{ name: "onEdit", type: "() => void", description: "Optional edit action for list rows." },
{ name: "onDelete", type: "() => void", description: "Optional delete action for list rows." },
{ name: "users", type: "DesignUserListRow[]", description: "Rows rendered by DesignUserList." },
{ name: "onUserClick", type: "(user) => void", description: "Row click handler for DesignUserList." },
{ name: "showAvatar", type: "boolean", default: "true", description: "Toggle avatar visibility." },
{ name: "gradient", type: "'blue-purple' | 'cyan-blue' | 'none'", default: "'blue-purple'", description: "Avatar gradient style." },
]} />
🤖 Prompt for AI Agents
In
`@apps/dashboard/src/app/`(main)/(protected)/projects/[projectId]/design-language/page-client.tsx
around lines 840 - 850, The props table passed to PropsTable is inaccurate: it
lists props (subtitle, size, glassmorphic) that DesignListItemRow/DesignUserList
do not accept and omits actual props like showIcon, showAvatar, and users;
update the table data so each component's documented props match its real API by
either splitting into two tables or changing the array to reflect
DesignListItemRow and DesignUserList props precisely (edit the PropsTable
invocation near the existing PropsTable component usage), replacing/removing
incorrect entries and adding the missing showIcon, showAvatar, users entries
with correct types and descriptions.

Comment on lines +153 to +159
const target = containerRef?.current ?? window;
const timeoutIds = timeoutIdsRef.current;
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- EventTarget union requires cast
(target as EventTarget).addEventListener("click", onClick as EventListener);
return () => {
(target as EventTarget).removeEventListener("click", onClick as EventListener);
for (const timeoutId of timeoutIds.values()) {
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

Remove type assertions around the event target.

HTMLElement | Window already exposes addEventListener, so the casts (and eslint-disable) can be dropped without weakening types.

Suggested cleanup
-    const target = containerRef?.current ?? window;
-    const timeoutIds = timeoutIdsRef.current;
-    // eslint-disable-next-line `@typescript-eslint/no-unnecessary-type-assertion` -- EventTarget union requires cast
-    (target as EventTarget).addEventListener("click", onClick as EventListener);
+    const target: HTMLElement | Window = containerRef?.current ?? window;
+    const timeoutIds = timeoutIdsRef.current;
+    target.addEventListener("click", onClick);
     return () => {
-      (target as EventTarget).removeEventListener("click", onClick as EventListener);
+      target.removeEventListener("click", onClick);
       for (const timeoutId of timeoutIds.values()) {
         window.clearTimeout(timeoutId);
       }
As per coding guidelines, "Never use `as`, `any`, or type casts to bypass the type system unless explicitly approved. Avoid type casts wherever possible."
🤖 Prompt for AI Agents
In `@apps/dashboard/src/components/design-language/cursor-blast-effect.tsx` around
lines 153 - 159, The code uses explicit type assertions and an eslint-disable
when attaching listeners to `target`; remove the casts and the eslint-disable
and call `addEventListener`/`removeEventListener` directly on `target` (the
value computed from `containerRef?.current ?? window`) and pass `onClick` (and
`onClick` as the listener) as-is; also leave the rest of the cleanup (iterating
`timeoutIdsRef.current`) unchanged so `timeoutIds` and `onClick` are used
without bypassing the type system.

Comment on lines +120 to +126
const handleSelect = (categoryId: string) => {
const result = onSelect(categoryId);
if (result && typeof (result as Promise<void>).then === "function") {
setLoadingCategoryId(categoryId);
runAsynchronouslyWithAlert(
Promise.resolve(result).finally(() => setLoadingCategoryId(null))
);
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

🧩 Analysis chain

🏁 Script executed:

cat -n apps/dashboard/src/components/design-language/tabs.tsx | sed -n '115,135p'

Repository: stack-auth/stack-auth

Length of output: 1101


🏁 Script executed:

cat -n apps/dashboard/src/components/design-language/tabs.tsx | sed -n '100,140p'

Repository: stack-auth/stack-auth

Length of output: 1602


🏁 Script executed:

head -50 apps/dashboard/src/components/design-language/tabs.tsx

Repository: stack-auth/stack-auth

Length of output: 1500


Replace as Promise<void> type assertion with instanceof Promise.

The current code uses a type cast that violates the no-cast guideline. Use instanceof Promise for a type-safe runtime check instead, and remove the Promise.resolve() wrapper since we know the result is a Promise at that point.

Suggested change
     const result = onSelect(categoryId);
-    if (result && typeof (result as Promise<void>).then === "function") {
+    if (result instanceof Promise) {
       setLoadingCategoryId(categoryId);
       runAsynchronouslyWithAlert(
-        Promise.resolve(result).finally(() => setLoadingCategoryId(null))
+        result.finally(() => setLoadingCategoryId(null))
       );
     }
📝 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
const handleSelect = (categoryId: string) => {
const result = onSelect(categoryId);
if (result && typeof (result as Promise<void>).then === "function") {
setLoadingCategoryId(categoryId);
runAsynchronouslyWithAlert(
Promise.resolve(result).finally(() => setLoadingCategoryId(null))
);
const handleSelect = (categoryId: string) => {
const result = onSelect(categoryId);
if (result instanceof Promise) {
setLoadingCategoryId(categoryId);
runAsynchronouslyWithAlert(
result.finally(() => setLoadingCategoryId(null))
);
🤖 Prompt for AI Agents
In `@apps/dashboard/src/components/design-language/tabs.tsx` around lines 120 -
126, The handleSelect function currently uses a type assertion (result as
Promise<void>) to detect a Promise; change this to a runtime instanceof check:
if (result instanceof Promise) { ... } so you avoid the no-cast violation, keep
the setLoadingCategoryId(categoryId) and finally(() =>
setLoadingCategoryId(null)) behavior, and call
runAsynchronouslyWithAlert(result.finally(...)) (remove the outer
Promise.resolve wrapper) to ensure the Promise returned by onSelect is awaited
with the same cleanup logic.

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