Skip to content

feat: DR-7749 client page#7703

Open
carlagn wants to merge 6 commits intomainfrom
feat/DR-7749-client
Open

feat: DR-7749 client page#7703
carlagn wants to merge 6 commits intomainfrom
feat/DR-7749-client

Conversation

@carlagn
Copy link
Contributor

@carlagn carlagn commented Mar 25, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a new client landing page featuring an interactive API explorer with code examples across multiple programming languages, plus showcases for supported databases and frameworks.
    • New technology component for displaying integrated tools with descriptive tooltips.
  • Style

    • Improved layout background styling and configurable image shadow effects for better visual consistency.

@vercel
Copy link

vercel bot commented Mar 25, 2026

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

Project Deployment Actions Updated (UTC)
blog Ready Ready Preview, Comment Mar 25, 2026 9:53pm
docs Ready Ready Preview, Comment Mar 25, 2026 9:53pm
eclipse Ready Ready Preview, Comment Mar 25, 2026 9:53pm
site Ready Ready Preview, Comment Mar 25, 2026 9:53pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 25, 2026

Walkthrough

This change introduces a new client-side API explorer feature for the Prisma site. It adds the shiki syntax highlighting dependency, creates an interactive API component with code examples, introduces supporting UI components, updates page layouts, and extends an existing card component interface.

Changes

Cohort / File(s) Summary
Configuration
apps/site/package.json
Added shiki version 3.22.0 to dependencies for syntax highlighting support.
Pages & Layouts
apps/site/src/app/layout.tsx, apps/site/src/app/client/page.tsx
Modified body background styling in layout by replacing an absolutely-positioned background div with direct body class styling. Created new Client page component featuring hero section, API explorer, and feature cards with database/framework icon grids.
API Explorer Feature
apps/site/src/components/client/api-data.ts, apps/site/src/components/client/api.tsx
Added static data file defining categorized API items with code examples (CRUD, relations, pagination, transactions). Implemented interactive API component with language selection (TypeScript/JavaScript), syntax highlighting via Shiki, and dynamically rendered code blocks.
New Components
apps/site/src/components/client/technology.tsx
Introduced reusable Technology component wrapping buttons with tooltip content from Eclipse primitives.
Component Enhancements
apps/site/src/components/homepage/card-section/card-section.tsx
Extended TwoColumnItem interface with optional noShadow flag to conditionally control image shadow rendering on desktop layouts.
Infrastructure
apps/site/src/lib/shiki_prisma.ts
Added Shiki highlighter module with custom prisma-dark theme (color mappings and token style scopes), lazy-initialized cached highlighter instance, and support for Prisma and related language syntax highlighting.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: DR-7749 client page' directly corresponds to the primary change: adding a new client page component with supporting infrastructure.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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


Comment @coderabbitai help to get the list of available commands and usage tips.

@argos-ci
Copy link

argos-ci bot commented Mar 25, 2026

The latest updates on your projects. Learn more about Argos notifications ↗︎

Build Status Details Updated (UTC)
default (Inspect) ✅ No changes detected - Mar 25, 2026, 9:59 PM

Copy link
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: 8

🧹 Nitpick comments (6)
apps/site/src/components/client/api.tsx (2)

148-173: CodeUIItems is defined inside the component — recreated every render.

Defining a component inside another component means it gets a new identity on each render. This can cause unnecessary unmounting/remounting of the sub-tree. Move it outside API or memoize it.

Move component outside or convert to a function

Either move CodeUIItems to module scope (outside API), passing funcSelected and setFuncSelected as props, or inline it as a render function (without the JSX component pattern):

+const CodeUIItems = ({
+  item,
+  blockType,
+  funcSelected,
+  setFuncSelected,
+}: {
+  item: any;
+  blockType: string;
+  funcSelected: any;
+  setFuncSelected: (func: any) => void;
+}) => {
+  const labelToDisplay = item.functions.filter(
+    (i: any) => i[blockType] && i[blockType].length > 0,
+  );
+  return (
+    <ul className="flex flex-wrap my-5 mx-0 gap-2 list-none p-0">
+      {labelToDisplay.map((func: any, fIndex: number) => (
+        <li key={fIndex}>
+          <div
+            className={cn(
+              "rounded-full px-3 py-2 bg-background-default border border-stroke-neutral text-base uppercase font-sans-display font-bold tracking-wide leading-4 cursor-pointer transition-colors duration-300 ease-in-out hover:bg-foreground-orm text-foreground-neutral hover:text-foreground-neutral-reverse",
+              funcSelected === func &&
+                "bg-foreground-orm text-foreground-reverse-neutral",
+            )}
+            onClick={() => setFuncSelected(func)}
+          >
+            {func.name}
+          </div>
+        </li>
+      ))}
+    </ul>
+  );
+};

 const API = () => {
   // ... existing code ...
-  const CodeUIItems = ({ item, blockType }: any) => {
-    // ... remove inline definition ...
-  };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/components/client/api.tsx` around lines 148 - 173, The
CodeUIItems component is recreated on every render because it's defined inside
API; move CodeUIItems to module scope (outside the API component) and change it
to accept props for funcSelected and setFuncSelected (and item, blockType) so it
no longer closes over API state, or alternatively wrap it with
React.memo/useCallback to memoize it; update where CodeUIItems is used inside
API to pass the props (funcSelected, setFuncSelected, item, blockType)
accordingly so the sub-tree is stable across renders.

100-110: JavaScript code is highlighted as TypeScript.

When the user selects JavaScript, the code is still highlighted with lang: "typescript" (line 105). While TypeScript highlighting usually works for JS, it can occasionally produce odd results for JS-specific patterns. Consider using the selected language:

Use the correct language for highlighting
           for (let index = 0; index < codeBlockSelected.length; index++) {
             const html = await highlighter.codeToHtml(
               codeBlockSelected[index],
               {
-                lang: "typescript",
+                lang: selectedLang.value === "js" ? "javascript" : "typescript",
                 theme: "prisma-dark",
               },
             );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/components/client/api.tsx` around lines 100 - 110, The
highlighter is hardcoded to lang: "typescript" in the block that checks
codeBlockSelected and funcSelected; change the call to highlighter.codeToHtml to
use the actual selected language (e.g., derive a language string from
funcSelected or the user's selection instead of the literal "typescript") so
JavaScript selections use "javascript" (or the appropriate value) — update the
lang property passed to highlighter.codeToHtml and ensure the code path that
checks for "prismaCodeBlock" in funcSelected still uses the dynamic language.
apps/site/src/components/homepage/card-section/card-section.tsx (1)

68-71: Intentional asymmetry? noShadow only affects desktop images.

The noShadow flag is applied to the desktop image (line 68-71) but not to the mobile image (line 81). If this is by design — perhaps mobile always needs the shadow for visual separation — then this is fine. Otherwise, you may want to apply the same conditional logic to the mobile image.

If mobile should also respect noShadow
                   {item.mobileImageUrl && (
                     <img
-                      className="w-full h-auto shadow-[0_10px_25px_-5px_rgba(0,0,0,0.1)] sm:hidden"
+                      className={cn(
+                        "w-full h-auto shadow-[0_10px_25px_-5px_rgba(0,0,0,0.1)] sm:hidden",
+                        item.noShadow && "shadow-none",
+                      )}
                       src={

Also applies to: 79-88

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

In `@apps/site/src/components/homepage/card-section/card-section.tsx` around lines
68 - 71, The desktop image className uses the item.noShadow conditional but the
mobile image does not, causing asymmetry; update the mobile image's className
(the one rendering with "sm:hidden" / mobile view) to include the same
conditional (item.noShadow && "shadow-none") so both images respect the noShadow
flag, and mirror this change for the other similar occurrence referenced around
lines 79-88 in card-section.tsx.
apps/site/src/lib/shiki_prisma.ts (2)

222-244: Potential race condition in highlighter initialization.

If getHighlighter() is called multiple times concurrently before the first call completes, each call will see prisma_highlighter as null and create a new highlighter. While not catastrophic (the last one assigned wins), it wastes resources loading the highlighter multiple times.

A simple fix is to cache the promise instead of the result:

Cache the promise to prevent duplicate initialization
-let prisma_highlighter: Awaited<ReturnType<typeof createHighlighter>> | null =
-  null;
+let highlighterPromise: ReturnType<typeof createHighlighter> | null = null;

 async function getHighlighter() {
-  if (!prisma_highlighter) {
-    prisma_highlighter = await createHighlighter({
+  if (!highlighterPromise) {
+    highlighterPromise = createHighlighter({
       themes: [prismaTheme],
       langs: [
         "typescript",
         "javascript",
         "jsx",
         "tsx",
         "json",
         "bash",
         "sh",
         "prisma",
         "sql",
         "diff",
       ],
     });
   }
-  return prisma_highlighter;
+  return highlighterPromise;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/lib/shiki_prisma.ts` around lines 222 - 244, The current
getHighlighter implementation can trigger multiple concurrent createHighlighter
calls because prisma_highlighter is null until the first await completes; change
the caching to store the initialization Promise instead of the resolved
result—e.g., make prisma_highlighter hold the Promise returned by
createHighlighter (or introduce prisma_highlighterPromise) and on first call
assign prisma_highlighter = createHighlighter({...}) and then await it in
getHighlighter before returning the resolved highlighter; update the
prisma_highlighter type accordingly so subsequent concurrent calls reuse the
same Promise and avoid duplicate initializations.

246-246: Confusing export alias.

Exporting getHighlighter as prisma_highlighter is misleading — consumers might expect prisma_highlighter to be the highlighter instance, not a function that returns one. Consider either:

  1. Export with a clearer name like getPrismaHighlighter, or
  2. Keep the original name getHighlighter
Clearer export naming
-export { getHighlighter as prisma_highlighter };
+export { getHighlighter };

Then update the import in api.tsx:

-import { prisma_highlighter as getHighlighter } from "@/lib/shiki_prisma";
+import { getHighlighter } from "@/lib/shiki_prisma";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/lib/shiki_prisma.ts` at line 246, The export alias
prisma_highlighter is misleading because it exports the function getHighlighter
rather than a highlighter instance; rename the export to a clearer function name
like getPrismaHighlighter (or re-export the original getHighlighter name) and
update any imports (e.g., in api.tsx) to use the new name; specifically change
the export statement exporting getHighlighter as prisma_highlighter to export it
as getPrismaHighlighter (or export { getHighlighter }) and then update all
usages/imports that reference prisma_highlighter to the new identifier.
apps/site/src/components/client/technology.tsx (1)

9-9: Remove unused useState import.

The useState hook is imported but never used in this component. This is dead code that should be cleaned up.

Remove unused import
-import { useState } from "react";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/components/client/technology.tsx` at line 9, Remove the unused
React hook import by deleting the named import "useState" (the import statement
`import { useState } from "react";`) from the component file so there is no dead
code; if other React imports are needed, keep only the necessary ones, otherwise
remove the entire import line.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/site/src/app/client/page.tsx`:
- Around line 169-173: The hero heading in apps/site/src/app/client/page.tsx
contains the incorrect second line "Database Migrations" inside the <h1> (the
element with className "stretch-display text-6xl font-bold text-center
font-sans-display z-2 relative max-w-223 mx-auto"); replace or remove that stray
text so the hero copy matches the Prisma Client page (e.g., remove "Database
Migrations" or change it to a Client-relevant phrase) and ensure the <h1>
content only contains accurate Prisma Client messaging.
- Around line 297-300: The button in the Migrate section has incorrect copy:
locate the Button element (the JSX element with variant="orm" size="3xl" and
className="w-fit") and update its inner span text from "Learn more about Prisma
Studio" to "Learn more about Prisma Migrate" (or equivalent migrate-specific
copy) so the CTA matches the Prisma Migrate section.
- Around line 239-251: The mapped list is missing a React key on the outer
Technology component causing reconciliation issues; move the key prop from the
inner Action to the Technology element in the frameworks.list.map callback
(e.g., set key={fw.name} or key={fw.id} on <Technology ...>) and remove it from
<Action> so each Technology child has a stable unique key during rendering.
- Around line 216-227: The map rendering uses databases.list.map with the outer
component Technology but the key prop is incorrectly placed on the inner Action;
move the unique key (e.g., key={db.name} or a more robust unique id) from the
Action component to the Technology component so the Technology element is the
keyed list item, and remove the duplicate key from Action (update the JSX where
Technology and Action are rendered).

In `@apps/site/src/components/client/api-data.ts`:
- Line 146: The example transaction snippet is truncated and starts mid-function
(showing locate, geo, tx.user.update) so update the source in ui/explore to
produce a complete interactive transaction example: ensure the snippet includes
the opening prisma.$transaction(async (tx) => { ... }), creation or lookup of
bob (e.g., await tx.user.create or await tx.user.findUnique to set bob), the
locate function definition (locate) and the final tx.user.update call so the
flow is contiguous; then regenerate the auto-generated file
(apps/site/src/components/client/api-data.ts) so the rendered snippet contains
the full transaction sequence (prisma.$transaction, bob, locate, geo,
tx.user.update).
- Line 56: The variable prolificAuthors is typed incorrectly: the result of
prisma.user.findMany(...) is annotated as Category[] but should be User[];
update the type annotation for prolificAuthors (and any related declarations)
from Category[] to User[] to match the return type of prisma.user.findMany and
avoid confusing mismatched types in the example.

In `@apps/site/src/components/client/api.tsx`:
- Around line 231-236: The onValueChange handler calls
handleCodeBlockChange(apiItems.find(...)) without checking the find result;
since Array.prototype.find can return undefined and handleCodeBlockChange
assumes a valid item (it accesses item.functions[0]), guard this by storing the
result of apiItems.find(...) in a local variable, check that it is not undefined
(and optionally that item.functions?.length > 0) before calling
handleCodeBlockChange, and only call handleCodeBlockChange with a valid item or
handle the missing-case (e.g., no-op or show fallback).

In `@apps/site/src/components/client/technology.tsx`:
- Line 27: The className on the JSX element in
apps/site/src/components/client/technology.tsx contains conflicting font utility
classes (font-sans-display! and font-mono!); pick the intended font for this
component (e.g., if you want the display font keep font-sans-display! and remove
font-mono!, or vice versa) and update the className on the element (the
className prop in the Technology component) so only the chosen font utility
remains.

---

Nitpick comments:
In `@apps/site/src/components/client/api.tsx`:
- Around line 148-173: The CodeUIItems component is recreated on every render
because it's defined inside API; move CodeUIItems to module scope (outside the
API component) and change it to accept props for funcSelected and
setFuncSelected (and item, blockType) so it no longer closes over API state, or
alternatively wrap it with React.memo/useCallback to memoize it; update where
CodeUIItems is used inside API to pass the props (funcSelected, setFuncSelected,
item, blockType) accordingly so the sub-tree is stable across renders.
- Around line 100-110: The highlighter is hardcoded to lang: "typescript" in the
block that checks codeBlockSelected and funcSelected; change the call to
highlighter.codeToHtml to use the actual selected language (e.g., derive a
language string from funcSelected or the user's selection instead of the literal
"typescript") so JavaScript selections use "javascript" (or the appropriate
value) — update the lang property passed to highlighter.codeToHtml and ensure
the code path that checks for "prismaCodeBlock" in funcSelected still uses the
dynamic language.

In `@apps/site/src/components/client/technology.tsx`:
- Line 9: Remove the unused React hook import by deleting the named import
"useState" (the import statement `import { useState } from "react";`) from the
component file so there is no dead code; if other React imports are needed, keep
only the necessary ones, otherwise remove the entire import line.

In `@apps/site/src/components/homepage/card-section/card-section.tsx`:
- Around line 68-71: The desktop image className uses the item.noShadow
conditional but the mobile image does not, causing asymmetry; update the mobile
image's className (the one rendering with "sm:hidden" / mobile view) to include
the same conditional (item.noShadow && "shadow-none") so both images respect the
noShadow flag, and mirror this change for the other similar occurrence
referenced around lines 79-88 in card-section.tsx.

In `@apps/site/src/lib/shiki_prisma.ts`:
- Around line 222-244: The current getHighlighter implementation can trigger
multiple concurrent createHighlighter calls because prisma_highlighter is null
until the first await completes; change the caching to store the initialization
Promise instead of the resolved result—e.g., make prisma_highlighter hold the
Promise returned by createHighlighter (or introduce prisma_highlighterPromise)
and on first call assign prisma_highlighter = createHighlighter({...}) and then
await it in getHighlighter before returning the resolved highlighter; update the
prisma_highlighter type accordingly so subsequent concurrent calls reuse the
same Promise and avoid duplicate initializations.
- Line 246: The export alias prisma_highlighter is misleading because it exports
the function getHighlighter rather than a highlighter instance; rename the
export to a clearer function name like getPrismaHighlighter (or re-export the
original getHighlighter name) and update any imports (e.g., in api.tsx) to use
the new name; specifically change the export statement exporting getHighlighter
as prisma_highlighter to export it as getPrismaHighlighter (or export {
getHighlighter }) and then update all usages/imports that reference
prisma_highlighter to the new identifier.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: faa94581-bd12-4e50-b6a2-68091d7d5c68

📥 Commits

Reviewing files that changed from the base of the PR and between edda9c3 and 2779863.

⛔ Files ignored due to path filters (8)
  • apps/site/public/icons/technologies/mariadb.svg is excluded by !**/*.svg
  • apps/site/public/icons/technologies/mongodbsimple.svg is excluded by !**/*.svg
  • apps/site/public/icons/technologies/mysqlsimple.svg is excluded by !**/*.svg
  • apps/site/public/illustrations/client/client_0.svg is excluded by !**/*.svg
  • apps/site/public/illustrations/client/client_0_light.svg is excluded by !**/*.svg
  • apps/site/public/illustrations/client/client_1.svg is excluded by !**/*.svg
  • apps/site/public/illustrations/client/client_1_light.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • apps/site/package.json
  • apps/site/src/app/client/page.tsx
  • apps/site/src/app/layout.tsx
  • apps/site/src/components/client/api-data.ts
  • apps/site/src/components/client/api.tsx
  • apps/site/src/components/client/technology.tsx
  • apps/site/src/components/homepage/card-section/card-section.tsx
  • apps/site/src/lib/shiki_prisma.ts

Comment on lines +169 to +173
<h1 className="stretch-display text-6xl font-bold text-center font-sans-display z-2 relative max-w-223 mx-auto">
Intuitive database client for TypeScript and Node.js
<br />
Database Migrations
</h1>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Inconsistent hero copy: "Database Migrations" on the Client page?

The hero heading includes "Database Migrations" but this is the Prisma Client page. This looks like leftover text that should either be removed or changed to something relevant to Prisma Client.

Suggested fix
           <h1 className="stretch-display text-6xl font-bold text-center font-sans-display z-2 relative max-w-223 mx-auto">
             Intuitive database client for TypeScript and Node.js
-            <br />
-            Database Migrations
           </h1>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/client/page.tsx` around lines 169 - 173, The hero heading
in apps/site/src/app/client/page.tsx contains the incorrect second line
"Database Migrations" inside the <h1> (the element with className
"stretch-display text-6xl font-bold text-center font-sans-display z-2 relative
max-w-223 mx-auto"); replace or remove that stray text so the hero copy matches
the Prisma Client page (e.g., remove "Database Migrations" or change it to a
Client-relevant phrase) and ensure the <h1> content only contains accurate
Prisma Client messaging.

Comment on lines +216 to +227
{databases.list.map((db) => (
<Technology text={db.name} url={db.url}>
<Action
color="neutral"
size="4xl"
key={db.name}
className="h-[75px]! w-[75px]! hover:bg-background-neutral-strong"
>
<img src={db.icon} alt={db.name} />
</Action>
</Technology>
))}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing key prop on Technology component.

React requires a unique key when rendering lists. The key is currently on the Action component inside Technology, but it should be on the outermost element in the map — which is Technology.

Move key to Technology
               {databases.list.map((db) => (
-                  <Technology text={db.name} url={db.url}>
+                  <Technology key={db.name} text={db.name} url={db.url}>
                     <Action
                       color="neutral"
                       size="4xl"
-                      key={db.name}
                       className="h-[75px]! w-[75px]! hover:bg-background-neutral-strong"
                     >
📝 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
{databases.list.map((db) => (
<Technology text={db.name} url={db.url}>
<Action
color="neutral"
size="4xl"
key={db.name}
className="h-[75px]! w-[75px]! hover:bg-background-neutral-strong"
>
<img src={db.icon} alt={db.name} />
</Action>
</Technology>
))}
{databases.list.map((db) => (
<Technology key={db.name} text={db.name} url={db.url}>
<Action
color="neutral"
size="4xl"
className="h-[75px]! w-[75px]! hover:bg-background-neutral-strong"
>
<img src={db.icon} alt={db.name} />
</Action>
</Technology>
))}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/client/page.tsx` around lines 216 - 227, The map rendering
uses databases.list.map with the outer component Technology but the key prop is
incorrectly placed on the inner Action; move the unique key (e.g., key={db.name}
or a more robust unique id) from the Action component to the Technology
component so the Technology element is the keyed list item, and remove the
duplicate key from Action (update the JSX where Technology and Action are
rendered).

Comment on lines +239 to +251
<div className="flex gap-1 flex-wrap">
{frameworks.list.map((fw) => (
<Technology text={fw.name} url={fw.url}>
<Action
color="neutral"
size="4xl"
key={fw.name}
className="h-[75px]! w-[75px]! hover:bg-background-neutral-strong"
>
<img src={fw.icon} alt={fw.name} />
</Action>
</Technology>
))}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Same issue: missing key on Technology in frameworks list.

Move key to Technology
               {frameworks.list.map((fw) => (
-                  <Technology text={fw.name} url={fw.url}>
+                  <Technology key={fw.name} text={fw.name} url={fw.url}>
                     <Action
                       color="neutral"
                       size="4xl"
-                      key={fw.name}
                       className="h-[75px]! w-[75px]! hover:bg-background-neutral-strong"
                     >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/client/page.tsx` around lines 239 - 251, The mapped list is
missing a React key on the outer Technology component causing reconciliation
issues; move the key prop from the inner Action to the Technology element in the
frameworks.list.map callback (e.g., set key={fw.name} or key={fw.id} on
<Technology ...>) and remove it from <Action> so each Technology child has a
stable unique key during rendering.

Comment on lines +297 to +300
<Button variant="orm" size="3xl" className="w-fit">
<span>Learn more about Prisma Studio</span>
<i className="fa-regular fa-arrow-right ml-2" />
</Button>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Copy-paste error: Button says "Prisma Studio" but this is the Migrate section.

This button is in the "Prisma Migrate" section but the text still references "Prisma Studio".

Fix the button text
               <Button variant="orm" size="3xl" className="w-fit">
-                <span>Learn more about Prisma Studio</span>
+                <span>Learn more about Prisma Migrate</span>
                 <i className="fa-regular fa-arrow-right ml-2" />
               </Button>
📝 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
<Button variant="orm" size="3xl" className="w-fit">
<span>Learn more about Prisma Studio</span>
<i className="fa-regular fa-arrow-right ml-2" />
</Button>
<Button variant="orm" size="3xl" className="w-fit">
<span>Learn more about Prisma Migrate</span>
<i className="fa-regular fa-arrow-right ml-2" />
</Button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/app/client/page.tsx` around lines 297 - 300, The button in the
Migrate section has incorrect copy: locate the Button element (the JSX element
with variant="orm" size="3xl" and className="w-fit") and update its inner span
text from "Learn more about Prisma Studio" to "Learn more about Prisma Migrate"
(or equivalent migrate-specific copy) so the CTA matches the Prisma Migrate
section.

],
tsCodeBlocks: [
"// Sort posts alphabetically\nconst alphabeticPosts: Post[] = await prisma.post.findMany({\n orderBy: { title: 'asc' },\n})",
"// Order by most prolific authors\nconst prolificAuthors: Category[] = await prisma.user.findMany({\n orderBy: {\n posts: {\n _count: 'asc',\n },\n },\n})",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incorrect type annotation: Category[] should be User[]

The code queries prisma.user.findMany(...) but the TypeScript annotation declares the result as Category[]. This is incorrect and would confuse developers reading the examples.

Fix the type annotation
-          "// Order by most prolific authors\nconst prolificAuthors: Category[] = await prisma.user.findMany({\n  orderBy: {\n    posts: {\n      _count: 'asc',\n    },\n  },\n})",
+          "// Order by most prolific authors\nconst prolificAuthors: User[] = await prisma.user.findMany({\n  orderBy: {\n    posts: {\n      _count: 'asc',\n    },\n  },\n})",
📝 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
"// Order by most prolific authors\nconst prolificAuthors: Category[] = await prisma.user.findMany({\n orderBy: {\n posts: {\n _count: 'asc',\n },\n },\n})",
"// Order by most prolific authors\nconst prolificAuthors: User[] = await prisma.user.findMany({\n orderBy: {\n posts: {\n _count: 'asc',\n },\n },\n})",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/components/client/api-data.ts` at line 56, The variable
prolificAuthors is typed incorrectly: the result of prisma.user.findMany(...) is
annotated as Category[] but should be User[]; update the type annotation for
prolificAuthors (and any related declarations) from Category[] to User[] to
match the return type of prisma.user.findMany and avoid confusing mismatched
types in the example.

name: "Transactions",
jsCodeBlocks: [
'// Create Bob and update Carol as a batch within a transaction\nawait prisma.$transaction([\n prisma.user.create({\n data: {\n name: "Bob",\n email: "bob@prisma.io",\n age: 49,\n country: "USA",\n },\n }),\n prisma.user.update({\n where: {\n email: "carol@prisma.io"\n },\n data: {\n country: "Germany",\n },\n }),\n])',
" // Example function turning an email into a country\n const locate = async (email) => ({ country: 'Germany' })\n const geo = await locate(bob.email)\n return await tx.user.update({\n where: {\n id: bob.id,\n },\n data: {\n country: geo.country,\n },\n })\n})",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Incomplete code snippet — appears to be truncated.

This transaction example starts mid-function with const locate = async (email) => ... — it's missing the opening part of the interactive transaction (e.g., await prisma.$transaction(async (tx) => { const bob = await tx.user.create(...)).

This will render as a confusing, incomplete snippet in the UI. Since this file is auto-generated, you'll need to fix the source in ui/explore and regenerate.

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

In `@apps/site/src/components/client/api-data.ts` at line 146, The example
transaction snippet is truncated and starts mid-function (showing locate, geo,
tx.user.update) so update the source in ui/explore to produce a complete
interactive transaction example: ensure the snippet includes the opening
prisma.$transaction(async (tx) => { ... }), creation or lookup of bob (e.g.,
await tx.user.create or await tx.user.findUnique to set bob), the locate
function definition (locate) and the final tx.user.update call so the flow is
contiguous; then regenerate the auto-generated file
(apps/site/src/components/client/api-data.ts) so the rendered snippet contains
the full transaction sequence (prisma.$transaction, bob, locate, geo,
tx.user.update).

Comment on lines +231 to +236
onValueChange={(value: string | null) => {
if (value) {
handleCodeBlockChange(
apiItems.find((item: any) => item.value === value),
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

find() can return undefined, leading to a runtime error.

If apiItems.find(...) returns undefined (e.g., due to a value mismatch), passing it to handleCodeBlockChange would cause an error when accessing item.functions[0] on line 177.

Guard against undefined
           onValueChange={(value: string | null) => {
             if (value) {
-              handleCodeBlockChange(
-                apiItems.find((item: any) => item.value === value),
-              );
+              const item = apiItems.find((i: any) => i.value === value);
+              if (item) {
+                handleCodeBlockChange(item);
+              }
             }
           }}
📝 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
onValueChange={(value: string | null) => {
if (value) {
handleCodeBlockChange(
apiItems.find((item: any) => item.value === value),
);
}
onValueChange={(value: string | null) => {
if (value) {
const item = apiItems.find((i: any) => i.value === value);
if (item) {
handleCodeBlockChange(item);
}
}
}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/components/client/api.tsx` around lines 231 - 236, The
onValueChange handler calls handleCodeBlockChange(apiItems.find(...)) without
checking the find result; since Array.prototype.find can return undefined and
handleCodeBlockChange assumes a valid item (it accesses item.functions[0]),
guard this by storing the result of apiItems.find(...) in a local variable,
check that it is not undefined (and optionally that item.functions?.length > 0)
before calling handleCodeBlockChange, and only call handleCodeBlockChange with a
valid item or handle the missing-case (e.g., no-op or show fallback).

<Button
variant="default-stronger"
href={url}
className="font-sans-display! font-normal! text-base! font-mono! w-[75px]! h-[75px]!"
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Conflicting font classes: font-sans-display! vs font-mono!

This className includes both font-sans-display! and font-mono! — these are mutually exclusive font-family declarations. Whichever appears last in the CSS will win, but this is almost certainly unintentional.

Decide which font you actually want and remove the other.

If you want the display font
-            className="font-sans-display! font-normal! text-base! font-mono! w-[75px]! h-[75px]!"
+            className="font-sans-display! font-normal! text-base! w-[75px]! h-[75px]!"
📝 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
className="font-sans-display! font-normal! text-base! font-mono! w-[75px]! h-[75px]!"
className="font-sans-display! font-normal! text-base! w-[75px]! h-[75px]!"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/site/src/components/client/technology.tsx` at line 27, The className on
the JSX element in apps/site/src/components/client/technology.tsx contains
conflicting font utility classes (font-sans-display! and font-mono!); pick the
intended font for this component (e.g., if you want the display font keep
font-sans-display! and remove font-mono!, or vice versa) and update the
className on the element (the className prop in the Technology component) so
only the chosen font utility remains.

@ArthurGamby
Copy link
Contributor

Nice one also!
Love the overall look, really.

A few comments:

when clicking on the dropdown to select the stack we have a horizontal window width jump due to scrollbar being hide/shown. Same behaviour with the second dropdown
the hight of the stack dropdown is bigger than the button "get started". I think if they are on the same line they can be same height.
Maybe just a nit, but I feel like the tags are quite big comparing the rest of the text / titles. Is this on the figma file ? (again maybe I am missing some context, just my opinion).

slack thread here

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.

2 participants