Theme transition, Rage click effect, Playground page#1180
Theme transition, Rage click effect, Playground page#1180Developing-Gamer merged 6 commits intolist-component-redesignfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing touches🧪 Generate unit tests (beta)
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. Comment |
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.
b7f554b
into
list-component-redesign
Greptile OverviewGreptile SummaryThis 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:
Main merge blockers to address are API compatibility changes (notably Confidence Score: 3/5
Important Files Changed
Sequence DiagramsequenceDiagram
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
|
Additional Comments (2)
This PR removes the
Also appears in: apps/dashboard/src/app/(main)/(protected)/(outside-dashboard)/playground/page-client.tsx (btnSize state/options). |
There was a problem hiding this comment.
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 | 🟡 MinorAvoid a stray divider when
showHeaderis 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 | 🟠 MajorReplace the
avatarGradientsrecord andas constwith 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 orinstanceof 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 && !showIconcheck on lines 67-69 can never be true given the logic ingetShowLabelShowIcon:
"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:handleSaveis double-wrapped byuseAsyncCallback.
handleSavefromuseAsyncCallback(line 545) is passed toDesignButton'sonClick, butDesignButtoninternally also usesuseAsyncCallback(per button.tsx lines 67-69). This double-wrapping is harmless but redundant.Since
isSavingfrom line 545 is used for thedisabledstate, this approach is still valid. However, you could simplify by passingonSavedirectly toDesignButtonand using its internalloadingprop for the disabled state.
| 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 }, | ||
| ]; |
There was a problem hiding this comment.
🧩 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
fiRepository: 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 -nRepository: 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" -nRepository: 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 -nRepository: 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 -30Repository: 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 -50Repository: 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 -60Repository: 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 -nRepository: 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:
- DEMO_USERS array (lines 170-172): Add
DemoUsertype definition and removeas constannotations. - Price cell (line 304): Use
row.original.priceinstead ofrow.getValue("price") as number. - Status cell (line 312): Use
row.original.statusinstead ofrow.getValue("status") as DemoProduct["status"]. - Grid columns state (line 1034): Replace
Number(v) as 1 | 2with 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).
| 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" }, | ||
| }; |
There was a problem hiding this comment.
🧩 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 -20Repository: 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).
| <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." }, | ||
| ]} /> |
There was a problem hiding this comment.
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.
| <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.
| 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()) { |
There was a problem hiding this comment.
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);
}🤖 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.
| 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)) | ||
| ); |
There was a problem hiding this comment.
🧩 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.tsxRepository: 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.
| 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.
Summary by CodeRabbit
Release Notes
New Features
Improvements