-
-
Notifications
You must be signed in to change notification settings - Fork 47
feat: add CounterLoading component with demo and documentation #127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Implemented CounterLoading component with CSS grid and keyframe animations. - Created CounterLoadingDemo for showcasing the loading animation. - Added installation instructions and usage documentation in counter-loader.mdx. - Defined common types in common.type.ts for better type safety. - Updated registry.type.ts to include component entry structure. - Enhanced createEntry utility to dynamically import components and their demos.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. WalkthroughAdds bundle analysis support and ANALYZE flag, introduces a dynamic component registry via createEntry with asynchronous preview/code loading, removes CopyButton component and its registry/navigation entries, standardizes component export styles (default), updates Preview/CodeBlock to dynamic patterns with loading UI, renames several docs/paths, and adjusts imports. Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant Registry as components-registry (Index)
participant Factory as createEntry()
participant Next as Next.js Runtime
Dev->>Registry: Import { Index }
Registry->>Factory: createEntry("name", ["otherA","otherB"], ssr?)
Factory->>Next: dynamic(() => tryImport("name"), { ssr })
Note over Factory,Next: preview is dynamically resolved (-demo fallback to base)
Factory->>Factory: code(): import raw name-demo.tsx || name.tsx
Factory->>Factory: othersCode[]: raw imports for "otherA","otherB"
Factory-->>Registry: ComponentEntry { preview, code, othersCode, ssr }
Registry-->>Dev: Index[name] entry
sequenceDiagram
participant UI as ComponentPreview
participant Reg as Index[componentName]
participant Code as CodeBlock (lazy)
participant Loader as Spinner
UI->>Reg: const Demo = Reg.preview
UI->>Reg: Reg.code() to load TSX source
Reg-->>UI: Promise<{ default: string }>
UI->>Code: render with sourceCode when resolved
alt loading
UI->>Loader: show Spinner
else ready
Code-->>UI: render tabs: Preview | Code
end
sequenceDiagram
participant Dev as CLI/Script
participant Env as ENV ANALYZE
participant Next as next.config.ts
participant Analyzer as @next/bundle-analyzer
Dev->>Env: cross-env ANALYZE=true next build
Env-->>Next: ANALYZE=true
Next->>Analyzer: analyzer(withMDX(config))
Analyzer-->>Dev: Build with bundle analysis
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (6)
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/content/docs/counter-loader.mdx (1)
2-2: Fix title to match filename convention.The title remains "counter-loading" but the component has been renamed to "counter-loader" throughout the codebase (as evidenced by the fileName prop on Line 40 and the navigation update in
src/registry/component-navigation.tsx).Apply this diff:
-title: "counter-loading" +title: "counter-loader"
🧹 Nitpick comments (17)
src/components/ui/CopyButton.tsx (1)
100-100: Verify the necessity of such a high z-index value.The z-index was increased from
z-50(50) toz-[999](999), which is a very high stacking value. While this ensures the menu appears above most content, it may indicate a layering strategy issue or a "z-index arms race" pattern.Consider:
- Whether a more moderate value (e.g.,
z-[100]orz-[200]) would suffice for this dropdown menu- Establishing a systematic z-index scale across the application to avoid arbitrary high values
- Documenting the reason for this specific value if it's intentionally needed to override other high-z-index elements
Also note: there's a trailing space after
dark:text-whitewhich is minor cosmetic noise.tsconfig.json (1)
37-37: LGTM: Added generateMetadata.ts to TypeScript compilation.The inclusion of
src/scripts/generateMetadata.tsensures the script participates in TypeScript's type checking and project compilation.Consider formatting the array entry on its own line for consistency:
- "src/content/docs/animated-form.mdx" -, "src/scripts/generateMetadata.ts" ], + "src/content/docs/animated-form.mdx", + "src/scripts/generateMetadata.ts" + ],registry-cli.json (1)
473-473: LGTM: Registry updated as part of copy-button removal.The changes align with the broader PR refactor that removes the copy-button component and migrates to a dynamic registry system.
Minor: Line 473 contains trailing whitespace. Consider removing it for cleaner formatting.
src/components/nurui/following-eye-demo.tsx (1)
7-7: LGTM: Export style updated to default export.The change to a default export aligns with the project-wide migration to support the new dynamic registry system via
createEntry.Minor: Remove the extra space between
defaultandFollowingEyeDemo:-export default FollowingEyeDemo ; +export default FollowingEyeDemo;next.config.ts (1)
60-61: Remove commented-out code.The commented export on line 60 should be removed as it's no longer needed and adds clutter.
Apply this diff:
-// export default withMDX(nextConfig); export default analyzer(withMDX(nextConfig));src/types/registry.type.ts (2)
1-4: Consider exporting CodeEntry for external use.The
CodeEntryinterface is currently internal, but consumers of the registry may need to reference it when working withComponentEntry.othersCode. Consider exporting it unless there's a specific reason to keep it private.If it should remain internal, this is acceptable. If external access is needed:
-interface CodeEntry { +export interface CodeEntry { fileName: string; code: () => Promise<{ default: string }>; };
1-11: Add newline at end of file.Missing newline at end of file. While minor, this is a common formatting convention.
export interface ComponentEntry { preview: React.ComponentType; code: () => Promise<{ default: string }>; othersCode?: CodeEntry[]; ssr?: boolean; -}; +}; +src/components/ui/code-block/CodeBlock.tsx (3)
38-51: Add error handling for async code loading.The async code loading lacks error handling. If the loader fails (network issue, missing file, etc.), users will see an empty code block with no indication of what went wrong.
const loadCode = async () => { if (!code && componentName && fileName) { const matched = Index[componentName]?.othersCode?.find( (otherCode) => otherCode.fileName === fileName, ); const loader = matched?.code || Index[componentName]?.code; if (loader) { - const result = await loader(); - setSourceCode(result.default); + try { + const result = await loader(); + setSourceCode(result.default); + } catch (error) { + console.error(`Failed to load code for ${componentName}/${fileName}:`, error); + setSourceCode(`// Error loading component code: ${error instanceof Error ? error.message : 'Unknown error'}`); + } } } };
62-84: Consider loading state during language switch.When switching from TypeScript to JavaScript or vice versa, the async code loading could introduce a brief delay where the old code is displayed. Consider adding a loading state to improve UX during transitions.
Example approach:
const [isLoading, setIsLoading] = useState(false); const handleSelect = async (languageName: string) => { setSelectedLanguage(languageName); setIsOpen(false); setIsLoading(true); try { // ... existing logic ... } finally { setIsLoading(false); } }; // In JSX: {isLoading && <div className="loading-spinner">Loading...</div>}
77-81: Add error handling for language switch code loading.Similar to the initial load, the language switch path lacks error handling for failed async loads.
const loader = matched?.code || Index[componentName]?.code; if (loader) { - const result = await loader(); - setSourceCode(result.default); + try { + const result = await loader(); + setSourceCode(result.default); + } catch (error) { + console.error(`Failed to reload code for ${componentName}/${fileName}:`, error); + setSourceCode(`// Error loading component code: ${error instanceof Error ? error.message : 'Unknown error'}`); + } }src/utils/createEntry.ts (2)
4-12: Avoid brittle fallback to the first export; fail fast if no component foundUsing
Object.values(mod)[0]can select the wrong thing. Prefer default (or an explicit named export), otherwise throw to surface errors early.const tryImport = async (name: string) => { try { const mod = await import(`@/components/nurui/${name}-demo`); - return mod.default || mod[name] || Object.values(mod)[0]; + const component = mod.default || (mod as Record<string, unknown>)[name as never]; + if (!component) throw new Error(`[createEntry] No component export found for "${name}-demo"`); + return component as React.ComponentType; } catch { const mod = await import(`@/components/nurui/${name}`); - return mod.default || mod[name] || Object.values(mod)[0]; + const component = mod.default || (mod as Record<string, unknown>)[name as never]; + if (!component) throw new Error(`[createEntry] No component export found for "${name}"`); + return component as React.ComponentType; } };
19-20: Add a lightweight loading fallback for previewsImproves UX while the preview chunk loads.
- preview: dynamic(() => tryImport(name), { ssr }), + preview: dynamic(() => tryImport(name), { ssr, loading: () => null }),CONTRIBUTING.md (2)
170-185: Fix typos and correct registry file pathSmall doc fixes to reduce confusion.
- Nur/ui now uses a dynamic registry system useing the function createEntry(). + Nur/ui now uses a dynamic registry system using the function createEntry(). - Open `src/registry/componentRegistry.ts` and add your entry: + Open `src/registry/components-registry.tsx` and add your entry:
333-335: Typo in note and example- ## If this component is not default export then use likt this: + ## If this component is not a default export then use like this: import("@/components/nurui/shiny-button-demo").then((mod) => mod.TextButtonDemo)src/components/common/ComponentPreview.tsx (3)
8-9: Use exported ComponentName for strong typingAfter tightening Index typing, prefer the exported alias for clarity and safety.
-import { Index } from "@/registry/components-registry"; +import { Index } from "@/registry/components-registry"; +import type { ComponentName } from "@/registry/components-registry"; interface ComponentPreviewProps { - componentName: keyof typeof Index; + componentName: ComponentName; introName?: boolean; exampleName?: string; v0ComponentName?: string; previewComponentName?: string; }Also applies to: 15-21
10-13: Optional: add a loading fallback for the code tabconst LazyCodeBlock = dynamic<{ code: string }>( () => import("../ui/code-block/CodeBlock").then((mod) => mod.CodeBlock), - { ssr: false }, + { ssr: false, loading: () => <div className="text-sm text-muted-foreground">Loading code…</div> }, );
34-44: Handle importer errors to avoid unhandled rejectionsWrap in try/catch and guard against stale updates.
useEffect(() => { - const loadCode = async () => { - const importer = Index[componentName]?.code; - if (typeof importer === "function") { - const mod = await importer(); - setSourceCode(mod.default || ""); // ?raw exports default string - } - }; - loadCode(); + let cancelled = false; + const loadCode = async () => { + try { + const importer = Index[componentName]?.code; + if (typeof importer === "function") { + const mod = await importer(); + if (!cancelled) setSourceCode(mod.default || ""); + } + } catch { + if (!cancelled) setSourceCode(""); + } + }; + loadCode(); + return () => { + cancelled = true; + }; }, [componentName]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (35)
.env.example(1 hunks)CONTRIBUTING.md(5 hunks)next.config.ts(2 hunks)package.json(4 hunks)registry-cli.json(1 hunks)registry.json(0 hunks)src/app/(docs)/docs/layout.tsx(2 hunks)src/app/layout.tsx(0 hunks)src/app/preview/[component]/components-preview-registry.tsx(3 hunks)src/components/common/CLICommandButton.tsx(1 hunks)src/components/common/ComponentPreview.tsx(2 hunks)src/components/common/FullScreenPreview.tsx(1 hunks)src/components/common/Preview.tsx(2 hunks)src/components/layout/components-layout/ComponentsLayout.tsx(1 hunks)src/components/nurui/animated-list-demo.tsx(2 hunks)src/components/nurui/animated-pricing-demo.tsx(1 hunks)src/components/nurui/copy-button-demo.tsx(0 hunks)src/components/nurui/copy-button.tsx(0 hunks)src/components/nurui/counter-loader-demo.tsx(1 hunks)src/components/nurui/creative-pricing-demo.tsx(1 hunks)src/components/nurui/following-eye-demo.tsx(1 hunks)src/components/nurui/ripple-loader-demo.tsx(0 hunks)src/components/pages/Home/ComponentDemosSection.tsx(1 hunks)src/components/pages/components/ComponentsPage.tsx(1 hunks)src/components/ui/CopyButton.tsx(1 hunks)src/components/ui/code-block/CodeBlock.tsx(2 hunks)src/content/docs/counter-loader.mdx(3 hunks)src/content/docs/draw-cursor.mdx(2 hunks)src/content/docs/ripple-loader.mdx(3 hunks)src/context/AppContext.tsx(1 hunks)src/registry/component-navigation.tsx(1 hunks)src/registry/components-registry.tsx(1 hunks)src/types/registry.type.ts(1 hunks)src/utils/createEntry.ts(1 hunks)tsconfig.json(1 hunks)
💤 Files with no reviewable changes (5)
- src/app/layout.tsx
- registry.json
- src/components/nurui/copy-button.tsx
- src/components/nurui/ripple-loader-demo.tsx
- src/components/nurui/copy-button-demo.tsx
🧰 Additional context used
🧬 Code graph analysis (4)
src/components/ui/code-block/CodeBlock.tsx (1)
src/registry/components-registry.tsx (1)
Index(4-127)
src/utils/createEntry.ts (1)
src/types/registry.type.ts (1)
ComponentEntry(6-11)
src/registry/components-registry.tsx (2)
src/types/registry.type.ts (1)
ComponentEntry(6-11)src/utils/createEntry.ts (1)
createEntry(14-31)
src/components/common/ComponentPreview.tsx (1)
src/registry/components-registry.tsx (1)
Index(4-127)
🪛 dotenv-linter (3.3.0)
.env.example
[warning] 2-2: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
[warning] 2-2: [UnorderedKey] The ANALYZE key should go before the NODE_ENV key
(UnorderedKey)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: 🛠️ Build Project
🔇 Additional comments (25)
src/components/common/CLICommandButton.tsx (1)
6-6: LGTM: Type import path updated consistently.The import path update aligns with the project-wide refactor to consolidate common types under
@/types/common.type.src/context/AppContext.tsx (1)
2-2: LGTM: Type import path updated consistently.The IChildren import path update aligns with the project-wide migration to
@/types/common.type..env.example (1)
2-2: LGTM: ANALYZE flag added for bundle analysis.The new ANALYZE environment variable supports the Next.js bundle analyzer integration introduced in this PR.
Note: Static analysis tools suggest adding a blank line at the end of the file and ordering keys alphabetically (ANALYZE before NODE_ENV). While these are minor formatting suggestions, consider addressing them for consistency.
src/components/common/FullScreenPreview.tsx (1)
7-7: LGTM: Type import path updated consistently.The import path update is part of the project-wide consolidation of common types under
@/types/common.type.src/components/nurui/animated-pricing-demo.tsx (1)
59-59: LGTM: Export style updated to default export.The migration to a default export is consistent with other demo components and supports the new dynamic registry system introduced in this PR.
src/components/nurui/creative-pricing-demo.tsx (1)
56-56: LGTM! Export change aligns with registry refactor.The switch from named export to default export is consistent with the broader PR objective to support dynamic, entry-based component loading.
next.config.ts (1)
3-9: LGTM! Bundle analyzer integration is correct.The integration of
@next/bundle-analyzerwith environment-based enablement is properly implemented. TheisAnalyzerEnabledflag provides good control over when analysis runs.package.json (3)
10-10: LGTM! Analyze script properly integrates with bundle analyzer.The new "analyze" script correctly sets the
ANALYZEenvironment variable to enable bundle analysis during builds, aligning with the configuration innext.config.ts.
66-66: LGTM! DevDependencies align with new features.
@next/bundle-analyzer@^15.5.4matches the Next.js version and enables bundle analysis.ts-node@^10.9.2is appropriate for executing TypeScript scripts during the build process.Also applies to: 82-82
29-29: Ignore outdated version check
[email protected] exists in the npm registry; no change required.Likely an incorrect or invalid review comment.
src/components/pages/Home/ComponentDemosSection.tsx (1)
15-15: LGTM! Import change aligns with component export update.The switch to a default import is consistent with the component's new default export pattern and the broader registry refactor.
src/components/layout/components-layout/ComponentsLayout.tsx (1)
4-4: LGTM! Type import consolidation.The import path change to
@/types/common.typeis part of the type consolidation refactor across the codebase. The behavior remains unchanged.src/components/nurui/counter-loader-demo.tsx (1)
2-2: LGTM! Import path updated to match component rename.The import path change from
"./counter-loading"to"./counter-loader"aligns with the component renaming mentioned in the PR objectives.However, note that the imported component is still named
CounterLoading(line 2) while the file is nowcounter-loader. Consider whether the component should be renamed toCounterLoaderfor consistency:-import CounterLoading from "./counter-loader"; +import CounterLoader from "./counter-loader"; const CounterLoadingDemo = () => { return ( <div className="flex items-center justify-center min-h-[30rem]"> - <CounterLoading /> + <CounterLoader /> </div> ); };src/components/pages/components/ComponentsPage.tsx (1)
18-18: LGTM! Import change aligns with component export update.The switch to a default import is consistent with the broader registry refactor and matches the component's new export pattern.
src/app/(docs)/docs/layout.tsx (2)
3-3: LGTM! Type import consolidation.The import path change to
@/types/common.typeis consistent with the type consolidation refactor across the codebase.
25-25: LGTM! Formatting change preserves behavior.The JSX formatting change from multiline to single-line is cosmetic and preserves the component's functionality.
src/content/docs/draw-cursor.mdx (1)
6-6: LGTM! Naming convention update is consistent.The componentName updates from kebab-case to camelCase align with the new dynamic registry pattern observed across the PR. The separation between
componentName(camelCase) andfileName(kebab-case) is appropriate for the registry lookup system.Also applies to: 52-52
src/content/docs/counter-loader.mdx (1)
6-6: Component reference updates look correct.The componentName and fileName props correctly reflect the new camelCase/kebab-case naming conventions used by the dynamic registry system.
Also applies to: 40-40
src/content/docs/ripple-loader.mdx (1)
6-6: LGTM! Consistent naming convention.The componentName updates follow the same camelCase pattern used across other loader components in this PR.
Also applies to: 29-29
src/registry/component-navigation.tsx (1)
112-112: LGTM! Navigation path updated correctly.The href change from
/counter-loadingto/counter-loadercorrectly aligns with the component rename observed throughout the PR.src/components/common/Preview.tsx (1)
6-6: LGTM! Type safety improvement with component instantiation.The change from
React.ReactNodetoReact.ComponentTypeimproves type safety by ensuring only component constructors are passed, not pre-rendered elements. This aligns with the dynamic component loading pattern introduced across the PR.The capitalized prop name
Componentcorrectly follows React conventions for component references.Also applies to: 10-10, 24-24
src/types/registry.type.ts (1)
6-11: Well-structured type definitions.The
ComponentEntryinterface provides a clean contract for the dynamic registry system with clear separation of concerns:
preview: Component constructor for renderingcode: Async loader for main component sourceothersCode: Optional additional code variantsssr: Optional SSR flagsrc/components/nurui/animated-list-demo.tsx (1)
65-65: LGTM! Export pattern aligns with dynamic imports.The change from named to default export aligns with the dynamic component loading pattern used throughout the registry system. According to the AI summary, consuming files have been updated to use the default import.
Also applies to: 84-84
src/components/ui/code-block/CodeBlock.tsx (1)
32-32: Async code loading implementation is solid.The transition to async code loading via loader functions correctly integrates with the new dynamic registry system. The fallback to empty string and the dependency array in useEffect are appropriate.
Also applies to: 38-51
src/app/preview/[component]/components-preview-registry.tsx (1)
28-30: Dynamic imports verified: all referenced demo modules export default components, no changes required.
| @@ -1 +1,2 @@ | |||
| NODE_ENV=developmnet | |||
| NODE_ENV=developmnet | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix the typo in NODE_ENV value.
The value "developmnet" contains a typo and should be "development". This error will propagate to developers who copy this example file.
Apply this diff:
-NODE_ENV=developmnet
+NODE_ENV=development📝 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.
| NODE_ENV=developmnet | |
| NODE_ENV=development |
🤖 Prompt for AI Agents
In .env.example around lines 1 to 1, the NODE_ENV value has a typo
("developmnet"); update the value to the correct string "development" so example
consumers get the correct environment name.
| // loaders | ||
| "ripple-loader": { | ||
| component: dynamic(() => import("@/components/nurui/ripple-loader-demo")), | ||
| }, | ||
| "counter-loading": { | ||
| component: dynamic(() => import("@/components/nurui/counter-loading-demo")), | ||
| "counter-loader": { | ||
| component: dynamic(() => import("@/components/nurui/counter-loader-demo")), | ||
| }, |
There was a problem hiding this comment.
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
New loader entries look good; confirm slug consistency
The new "counter-loader" preview entry aligns with the rename. LGTM. Please also ensure no stale "counter-loading" references remain in routes/docs.
🏁 Script executed:
#!/bin/bash
# Find any lingering "counter-loading" references
rg -nP 'counter-loading'Length of output: 1359
Update stale “counter-loading” references in registry and docs
- Change all occurrences of
"counter-loading"to"counter-loader"(names, slugs, titles) - Update file paths in registry.json, registry-cli.json, public/r/*.json, and src/content/docs/counter-loader.mdx to use
counter-loader-demo.tsxandcounter-loader.tsx
🤖 Prompt for AI Agents
In src/app/preview/[component]/components-preview-registry.tsx around lines 140
to 146, the registry still references the old "counter-loading" naming and
possibly stale file paths; update the key/name/slug/title occurrences from
"counter-loading" to "counter-loader" and ensure the dynamic import paths point
to the new files (use "@/components/nurui/counter-loader-demo" for the demo
component and "@/components/nurui/counter-loader" where appropriate). Also apply
the same rename across registry.json and registry-cli.json, every
public/r/*.json entry, and update src/content/docs/counter-loader.mdx to
reference counter-loader-demo.tsx and counter-loader.tsx filenames and use the
new slug/title "counter-loader".
|
|
||
| <Step>Copy and paste the following code into your project.</Step> | ||
|
|
||
| `components/nurui/counter-loading.tsx` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update file path to match renamed component.
The path comment shows counter-loading.tsx but should reflect the new naming convention counter-loader.tsx to match the fileName prop on Line 40.
Apply this diff:
-`components/nurui/counter-loading.tsx`
+`components/nurui/counter-loader.tsx`📝 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.
| `components/nurui/counter-loading.tsx` | |
| `components/nurui/counter-loader.tsx` |
🤖 Prompt for AI Agents
In src/content/docs/counter-loader.mdx around line 38, update the inline path
comment which currently reads `components/nurui/counter-loading.tsx` to the
renamed component path `components/nurui/counter-loader.tsx` so it matches the
fileName prop on line 40; simply replace the old filename in that comment with
the new one.
| import { ComponentEntry } from "@/types/registry.type"; | ||
| import { createEntry } from "@/utils/createEntry"; | ||
|
|
||
| export const Index: Record<string, ComponentEntry> = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Preserve key literal union for strong typing (use satisfies instead of Record annotation)
The explicit Record<string, ComponentEntry> annotation widens keys to string, so keyof typeof Index becomes string. Switch to satisfies to keep the key union while validating value shape.
-export const Index: Record<string, ComponentEntry> = {
+export const Index = {
// ...entries...
-} as const;
+} as const satisfies Record<string, ComponentEntry>;
-export type ComponentName = keyof typeof Index;
+export type ComponentName = keyof typeof Index;Also applies to: 127-129
🤖 Prompt for AI Agents
In src/registry/components-registry.tsx around line 4 (and also at lines
127-129), remove the explicit Record<string, ComponentEntry> type annotation
which widens the key type, and instead let the object keep its literal key types
and validate its shape using TypeScript's satisfies operator; i.e. declare the
object without the Record annotation (optionally add as const for literal
preservation) and append "satisfies Record<string, ComponentEntry>" to the
object expression so keyof typeof Index retains the union of keys while values
are type-checked.
| { fileName: "frame", code: FrameCode }, | ||
| ], | ||
| }, | ||
| futureNavbar: createEntry("futrue-navbar", ["future-button", "frame"]), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo breaks import: "futrue-navbar" → "future-navbar"
This will fail dynamic import at runtime.
- futureNavbar: createEntry("futrue-navbar", ["future-button", "frame"]),
+ futureNavbar: createEntry("future-navbar", ["future-button", "frame"]),📝 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.
| futureNavbar: createEntry("futrue-navbar", ["future-button", "frame"]), | |
| futureNavbar: createEntry("future-navbar", ["future-button", "frame"]), |
🤖 Prompt for AI Agents
In src/registry/components-registry.tsx around line 28, the component entry key
has a typo "futrue-navbar" which will break dynamic imports; change it to
"future-navbar" (update the string passed to createEntry) so the registry key
matches the expected import name and dependent dynamic imports resolve
correctly.
| neuralBackground: createEntry("nural-background", ["neural-background"]), | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo breaks import: "nural-background" → "neural-background"
Name and others mismatch; current value won’t resolve.
- neuralBackground: createEntry("nural-background", ["neural-background"]),
+ neuralBackground: createEntry("neural-background", ["neural-background"]),📝 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.
| neuralBackground: createEntry("nural-background", ["neural-background"]), | |
| neuralBackground: createEntry("neural-background", ["neural-background"]), |
🤖 Prompt for AI Agents
In src/registry/components-registry.tsx around lines 109 to 110, the entry
key/value has a typo "nural-background" which prevents imports from resolving;
change the string to "neural-background" and ensure the slug array element(s)
also use "neural-background" so the component name and aliases match the actual
import path.
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/components/ui/code-block/CodeBlock.tsx (1)
76-89: Set loading state when reloading TypeScript code.When the user switches back to TypeScript, code is reloaded asynchronously (lines 83-87) but
loadingis not set totrue, unlike the initial load. This creates an inconsistent UX where the spinner appears on mount but not on language toggle.Apply this diff to show the spinner during reload:
} else { // Reload original TS code if (code) { setSourceCode(code); } else if (componentName && fileName) { + setLoading(true); + try { const matched = Index[componentName]?.othersCode?.find( (otherCode) => otherCode.fileName === fileName, ); const loader = matched?.code || Index[componentName]?.code; if (loader) { const result = await loader(); setSourceCode(result.default); } + } catch (error) { + console.error("Failed to reload code:", error); + setSourceCode(`// Error reloading code for ${componentName}/${fileName}`); + } finally { + setLoading(false); + } } }
♻️ Duplicate comments (3)
src/registry/components-registry.tsx (3)
4-130: Keep literal key union by switching tosatisfies.
AnnotatingIndexasRecord<string, ComponentEntry>widenskeyof typeof Indextostring, soComponentNameloses the intended literal union. Drop the explicitRecordannotation and usesatisfiesinstead to retain strong typing while still validating the entry shape.-export const Index: Record<string, ComponentEntry> = { +export const Index = { // … -} as const; +} as const satisfies Record<string, ComponentEntry>; export type ComponentName = keyof typeof Index;
28-28: Fix typo in dynamic import slug.
createEntry("futrue-navbar", …)will try to import@/components/nurui/futrue-navbar, which does not exist, so the preview/code loaders throw at runtime. Please correct the slug tofuture-navbar.- futureNavbar: createEntry("futrue-navbar", ["future-button", "frame"]), + futureNavbar: createEntry("future-navbar", ["future-button", "frame"]),
112-113: Correct “nural” → “neural” slug.
The registry currently points at@/components/nurui/nural-background, which misses the “e” and fails to load. Align both occurrences with the actualneural-backgroundfilename so dynamic imports resolve.- neuralBackground: createEntry("nural-background", ["neural-background"]), + neuralBackground: createEntry("neural-background", ["neural-background"]),
🧹 Nitpick comments (1)
src/components/nurui/animated-Input.tsx (1)
61-61: Consider aligning filename with component name convention.The export correctly reflects the renamed component. However, the filename
animated-Input.tsxuses an inconsistent mixed-case convention. Consider renaming the file to either:
AnimatedInput.tsx(PascalCase, matching the component name)animated-input.tsx(kebab-case, fully lowercase)This would improve consistency and make the file easier to locate.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
nurui-logo.pngis excluded by!**/*.png
📒 Files selected for processing (17)
README.md(1 hunks)src/app/page.tsx(2 hunks)src/components/nurui/animated-Input.tsx(2 hunks)src/components/nurui/marquee-testimonial-demo.tsx(1 hunks)src/components/nurui/news-letter.tsx(2 hunks)src/components/nurui/spinner.tsx(1 hunks)src/components/pages/Home/AllTechnologySection.tsx(1 hunks)src/components/pages/Home/ButtonsMarquee.tsx(1 hunks)src/components/pages/Home/ComponentDemosSection.tsx(1 hunks)src/components/pages/Home/HeroSection.tsx(1 hunks)src/components/pages/Home/MarqueeTestimonialSection.tsx(2 hunks)src/components/pages/components/ComponentsPage.tsx(3 hunks)src/components/ui/code-block/CodeBlock.tsx(3 hunks)src/components/ui/marquee/MarqueeDemo.tsx(1 hunks)src/content/docs/animated-list.mdx(1 hunks)src/registry/components-registry.tsx(1 hunks)src/utils/createEntry.ts(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- src/content/docs/animated-list.mdx
🚧 Files skipped from review as they are similar to previous changes (2)
- src/components/pages/Home/ComponentDemosSection.tsx
- src/utils/createEntry.ts
🧰 Additional context used
🧬 Code graph analysis (5)
src/components/nurui/spinner.tsx (1)
src/lib/utils.ts (1)
cn(4-6)
src/components/pages/Home/MarqueeTestimonialSection.tsx (1)
src/data/testimonial.ts (1)
testimonialData(1-248)
src/registry/components-registry.tsx (2)
src/types/registry.type.ts (1)
ComponentEntry(6-11)src/utils/createEntry.ts (1)
createEntry(14-37)
src/app/page.tsx (1)
src/components/pages/Home/AllTechnologySection.tsx (1)
AllTechnologySection(18-183)
src/components/ui/code-block/CodeBlock.tsx (2)
src/registry/components-registry.tsx (1)
Index(4-130)src/components/nurui/spinner.tsx (1)
Spinner(15-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: 🛠️ Build Project
🔇 Additional comments (12)
src/components/nurui/news-letter.tsx (2)
1-1: LGTM! Import updated to match renamed component.The import correctly references the renamed
AnimatedInputcomponent. The change from an alias import to a relative path is consistent with the component rename.
12-12: LGTM! JSX usage updated correctly.The component usage is properly updated to
AnimatedInput, maintaining consistency with the import and the renamed component.src/components/nurui/animated-Input.tsx (1)
12-17: LGTM! Component rename is an improvement.The rename from
AnimateInputtoAnimatedInputis more grammatically correct (using the adjective form). The component definition and props remain unchanged, making this a safe refactor.src/components/pages/Home/AllTechnologySection.tsx (1)
18-18: LGTM! Clear improvement in component naming.The rename from
AllComponentSectiontoAllTechnologySectionbetter reflects the component's purpose—displaying technology stack icons rather than component demos.src/app/page.tsx (2)
2-2: LGTM! Import correctly updated.The import statement properly reflects the component rename.
11-11: All references toAllComponentSectionhave been removed
No matches found in any files.src/components/pages/Home/MarqueeTestimonialSection.tsx (2)
1-1: LGTM! Import path corrected.The import path has been fixed from the incorrect "marque" to "marquee", aligning with similar corrections throughout the codebase.
32-38: LGTM! Formatting improved.The multiline formatting improves readability while maintaining the same slice operation behavior.
src/components/nurui/spinner.tsx (1)
1-15: LGTM! Well-structured loading indicator.The Spinner component is properly implemented with:
- Appropriate accessibility attributes (
role="status",aria-label="Loading")- Type-safe SVG props spreading
- Clean composition with lucide-react's LoaderIcon
src/components/pages/components/ComponentsPage.tsx (2)
9-9: LGTM! Import path corrected.The Marquee import path has been fixed to use the correct "marquee" module path.
18-18: LGTM! Import updated to match export change.The import has been correctly updated to use a default import, aligning with the AnimatedListDemo component now being exported as default.
src/components/ui/code-block/CodeBlock.tsx (1)
92-98: LGTM! Loading UI implemented correctly.The loading state with Spinner provides clear feedback during async code loading, improving the user experience.
| useEffect(() => { | ||
| if (!code && componentName && fileName) { | ||
| const matched = Index[componentName]?.othersCode?.find( | ||
| (otherCode) => otherCode.fileName === fileName, | ||
| ); | ||
| const content = matched?.code || Index[componentName]?.code || ""; | ||
| setSourceCode(content); | ||
| } | ||
| const loadCode = async () => { | ||
| if (!code && componentName && fileName) { | ||
| setLoading(true); | ||
| try { | ||
| const matched = Index[componentName]?.othersCode?.find( | ||
| (otherCode) => otherCode.fileName === fileName, | ||
| ); | ||
| const loader = matched?.code || Index[componentName]?.code; | ||
| if (loader) { | ||
| const result = await loader(); | ||
| setSourceCode(result.default); | ||
| } | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| } | ||
| }; | ||
| loadCode(); | ||
| }, [code, componentName, fileName]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add cleanup and error handling to prevent stale updates and improve UX.
The async code loading lacks two important safeguards:
- Race condition risk: If
componentNameorfileNamechanges while loading is in progress, stale state updates may occur. - Missing error handling: If the loader fails, the user sees neither the code nor an error message.
Apply this diff to add cleanup and error handling:
useEffect(() => {
+ let cancelled = false;
const loadCode = async () => {
if (!code && componentName && fileName) {
setLoading(true);
try {
const matched = Index[componentName]?.othersCode?.find(
(otherCode) => otherCode.fileName === fileName,
);
const loader = matched?.code || Index[componentName]?.code;
if (loader) {
const result = await loader();
+ if (!cancelled) {
setSourceCode(result.default);
+ }
}
+ } catch (error) {
+ if (!cancelled) {
+ console.error("Failed to load code:", error);
+ setSourceCode(`// Error loading code for ${componentName}/${fileName}`);
+ }
} finally {
- setLoading(false);
+ if (!cancelled) {
+ setLoading(false);
+ }
}
}
};
loadCode();
+ return () => {
+ cancelled = true;
+ };
}, [code, componentName, fileName]);📝 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.
| useEffect(() => { | |
| if (!code && componentName && fileName) { | |
| const matched = Index[componentName]?.othersCode?.find( | |
| (otherCode) => otherCode.fileName === fileName, | |
| ); | |
| const content = matched?.code || Index[componentName]?.code || ""; | |
| setSourceCode(content); | |
| } | |
| const loadCode = async () => { | |
| if (!code && componentName && fileName) { | |
| setLoading(true); | |
| try { | |
| const matched = Index[componentName]?.othersCode?.find( | |
| (otherCode) => otherCode.fileName === fileName, | |
| ); | |
| const loader = matched?.code || Index[componentName]?.code; | |
| if (loader) { | |
| const result = await loader(); | |
| setSourceCode(result.default); | |
| } | |
| } finally { | |
| setLoading(false); | |
| } | |
| } | |
| }; | |
| loadCode(); | |
| }, [code, componentName, fileName]); | |
| useEffect(() => { | |
| let cancelled = false; | |
| const loadCode = async () => { | |
| if (!code && componentName && fileName) { | |
| setLoading(true); | |
| try { | |
| const matched = Index[componentName]?.othersCode?.find( | |
| (otherCode) => otherCode.fileName === fileName, | |
| ); | |
| const loader = matched?.code || Index[componentName]?.code; | |
| if (loader) { | |
| const result = await loader(); | |
| if (!cancelled) { | |
| setSourceCode(result.default); | |
| } | |
| } | |
| } catch (error) { | |
| if (!cancelled) { | |
| console.error("Failed to load code:", error); | |
| setSourceCode(`// Error loading code for ${componentName}/${fileName}`); | |
| } | |
| } finally { | |
| if (!cancelled) { | |
| setLoading(false); | |
| } | |
| } | |
| } | |
| }; | |
| loadCode(); | |
| return () => { | |
| cancelled = true; | |
| }; | |
| }, [code, componentName, fileName]); |
🤖 Prompt for AI Agents
In src/components/ui/code-block/CodeBlock.tsx around lines 39 to 58, the async
loader lacks cancellation and error handling which can cause stale state updates
and silent failures; wrap the async work with a mounted/aborted flag (or
AbortController) captured by the effect to ignore results if
componentName/fileName changes or the component unmounts, add a try/catch around
the await to capture loader errors and set an error state (or setSourceCode to
an error message) so the UI can show feedback, and ensure loading is always
cleared in finally only if the effect is still current (check the
mounted/aborted flag before calling setLoading/setSourceCode/setError).
Summary by CodeRabbit
New Features
Refactor
Documentation
Chores