feat: upgrade Next.js deps and refresh settings modal - #48
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR updates GitHub Actions workflows from Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~28 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
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 Tip You can get early access to new features in CodeRabbit.Enable the |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
packages/ui/src/components/ai-elements/message.tsx (1)
32-32: ExtractstreamdownPluginsinto a shared ai-elements module to prevent drift.
MessageResponseandReasoningContentnow carry the same plugin map + cast. Centralize this into one exported constant so type-workaround changes happen once.♻️ Proposed refactor
+// packages/ui/src/components/ai-elements/streamdown-plugins.ts +import { cjk } from "@streamdown/cjk"; +import { code } from "@streamdown/code"; +import { math } from "@streamdown/math"; +import { mermaid } from "@streamdown/mermaid"; +import type { PluginConfig } from "streamdown"; + +export const streamdownPlugins: PluginConfig = { + cjk, + code: code as PluginConfig["code"], + math, + mermaid, +};- import { Streamdown, type PluginConfig } from "streamdown"; + import { Streamdown } from "streamdown"; + import { streamdownPlugins } from "./streamdown-plugins"; ... - const streamdownPlugins: PluginConfig = { cjk, code: code as PluginConfig["code"], math, mermaid };As per coding guidelines: "Keep components atomic and reusable".
Also applies to: 325-327
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/ui/src/components/ai-elements/message.tsx` at line 32, The plugin map and type-cast for Streamdown are duplicated in MessageResponse and ReasoningContent; extract the shared configuration into a single exported constant (e.g., streamdownPlugins) in the ai-elements module and import it into the component file so both MessageResponse and ReasoningContent use that constant instead of local, duplicated plugin definitions; update any type assertions to reference the centralized export to avoid future drift and ensure only one place needs changes.package.json (1)
24-36: Pin the Node floor with this Next 16 bump.Next.js 16 now requires Node.js
20.9.0+, so upgrading the shared catalog without anengines.nodeguard leaves unsupported local environments to fail later during install/build instead of immediately. Adding the floor at the root manifest will make this upgrade much less surprising for contributors. (nextjs.org)♻️ Proposed fix
{ "name": "atmos", "private": false, + "engines": { + "node": ">=20.9.0" + }, "workspaces": [🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@package.json` around lines 24 - 36, The package.json update bumped Next to "16.2.0" but did not add an engines.node floor; add an engines field at the root of package.json with "node": ">=20.9.0" (or ">=20.9.0 <21" if you prefer a strict upper bound) so installs/builds fail fast on unsupported Node versions—locate the root manifest near the "next": "16.2.0" dependency and add the engines.node entry.apps/web/src/components/ui/AtmosWordmark.tsx (1)
1-78: Move this out of the app-localui/layer.This is product branding, not a reusable primitive. Keeping it under
src/components/uimakes the app-local UI folder compete with@workspace/ui; acomponents/brand/or similar location would fit the repo boundary better.As per coding guidelines, "
apps/web/src/components/**/*.{ts,tsx}: Generic UI components should be consumed from@workspace/ui; business-specific components should live insrc/components/."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/components/ui/AtmosWordmark.tsx` around lines 1 - 78, The AtmosWordmark component is product branding and should be moved out of the app-local ui layer; relocate the AtmosWordmark React component (the exported AtmosWordmark symbol) from the current ui folder into a business-specific location such as components/brand/, update any imports across the app that reference AtmosWordmark to the new path, and ensure its module export remains the same so consumers still import { AtmosWordmark } without changing its API; also remove or adjust any barrel exports in the old ui index so the app-local ui folder no longer exposes this branding component.apps/web/src/components/dialogs/SettingsModal.tsx (2)
84-105: Expose the active section semantically.These buttons swap content in place, but the selected state is only visual right now. Please give the sidebar tab semantics (
role="tablist"/role="tab"plusaria-selected/aria-controls) or an equivalent pattern so keyboard and screen-reader users can track which section is active.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/components/dialogs/SettingsModal.tsx` around lines 84 - 105, Wrap the sidebar navigation (the nav rendering SETTINGS_SECTIONS) with role="tablist" and make each section button a proper tab by adding role="tab", a stable id like `${section.id}-tab`, aria-selected={isActive}, and aria-controls={`${section.id}-panel`} (keep the existing onClick that calls setActiveSection). Then ensure the corresponding content container that renders the activeSection has role="tabpanel", an id matching `${section.id}-panel`, and aria-labelledby pointing to `${section.id}-tab` so screen readers and keyboard users can track and focus the activeSection (use activeSection to set which panel is visible/focused).
181-185: Avoid the hardcoded success green here.
text-green-500is the only raw color in this modal, so it will drift from the design tokens in light/dark themes. Please swap it to the semantic success/positive token the design system already uses.As per coding guidelines, "
apps/web/src/components/**/*.{ts,tsx}: ALWAYS use semantic CSS variables (bg-background,text-muted-foreground,border-border, etc.) instead of hardcoded Tailwind colors likebg-zinc-900ortext-gray-500for layout components`."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/src/components/dialogs/SettingsModal.tsx` around lines 181 - 185, In SettingsModal, replace the hardcoded Tailwind color on the success state (the Check JSX element using className "size-4 text-green-500") with the design-system semantic success token (e.g., "text-success-foreground" or your project's equivalent) so it uses the theme-aware CSS variable instead of a raw color; update the Check component's className and any adjacent "Up to date" styling to use the semantic token (refer to existing tokens like "text-muted-foreground" for naming consistency).
🤖 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/web/src/components/dialogs/SettingsModal.tsx`:
- Line 67: The dialog's grid uses fixed 240px and repeated 320px column classes
which cause clipping on narrow viewports; update the grid classnames in
SettingsModal (the DialogContent wrapper and the internal grid containers that
define the 240px sidebar + 320px columns) to switch to a single-column layout on
small screens (e.g., grid-cols-1) and only apply the multi-column (240px + 320px
repeats) layout at lg/breakpoint sizes (e.g., lg:grid-cols-...), so the sidebar
and content stack and reflow instead of being cropped; apply this change to the
same grid class usages referenced (the DialogContent and the internal grids
around the 240px sidebar and 320px columns).
In `@apps/web/src/components/ui/AtmosWordmark.tsx`:
- Around line 22-67: The wordmark currently renders as separate letters and an
SVG which screen readers won’t read as “Atmos”; update the AtmosWordmark
component so the decorative row (the container div that holds the letter spans
and LogoSvg) is marked decorative by adding aria-hidden="true" (or
role="presentation") to that element, and add a visually-hidden text node for
the product name (e.g., a span with a visually-hidden/sr-only class containing
"Atmos") at the start or end of the component so assistive tech will announce
the brand; target the container that includes the letter spans and the LogoSvg
and add the hidden span alongside it.
---
Nitpick comments:
In `@apps/web/src/components/dialogs/SettingsModal.tsx`:
- Around line 84-105: Wrap the sidebar navigation (the nav rendering
SETTINGS_SECTIONS) with role="tablist" and make each section button a proper tab
by adding role="tab", a stable id like `${section.id}-tab`,
aria-selected={isActive}, and aria-controls={`${section.id}-panel`} (keep the
existing onClick that calls setActiveSection). Then ensure the corresponding
content container that renders the activeSection has role="tabpanel", an id
matching `${section.id}-panel`, and aria-labelledby pointing to
`${section.id}-tab` so screen readers and keyboard users can track and focus the
activeSection (use activeSection to set which panel is visible/focused).
- Around line 181-185: In SettingsModal, replace the hardcoded Tailwind color on
the success state (the Check JSX element using className "size-4
text-green-500") with the design-system semantic success token (e.g.,
"text-success-foreground" or your project's equivalent) so it uses the
theme-aware CSS variable instead of a raw color; update the Check component's
className and any adjacent "Up to date" styling to use the semantic token (refer
to existing tokens like "text-muted-foreground" for naming consistency).
In `@apps/web/src/components/ui/AtmosWordmark.tsx`:
- Around line 1-78: The AtmosWordmark component is product branding and should
be moved out of the app-local ui layer; relocate the AtmosWordmark React
component (the exported AtmosWordmark symbol) from the current ui folder into a
business-specific location such as components/brand/, update any imports across
the app that reference AtmosWordmark to the new path, and ensure its module
export remains the same so consumers still import { AtmosWordmark } without
changing its API; also remove or adjust any barrel exports in the old ui index
so the app-local ui folder no longer exposes this branding component.
In `@package.json`:
- Around line 24-36: The package.json update bumped Next to "16.2.0" but did not
add an engines.node floor; add an engines field at the root of package.json with
"node": ">=20.9.0" (or ">=20.9.0 <21" if you prefer a strict upper bound) so
installs/builds fail fast on unsupported Node versions—locate the root manifest
near the "next": "16.2.0" dependency and add the engines.node entry.
In `@packages/ui/src/components/ai-elements/message.tsx`:
- Line 32: The plugin map and type-cast for Streamdown are duplicated in
MessageResponse and ReasoningContent; extract the shared configuration into a
single exported constant (e.g., streamdownPlugins) in the ai-elements module and
import it into the component file so both MessageResponse and ReasoningContent
use that constant instead of local, duplicated plugin definitions; update any
type assertions to reference the centralized export to avoid future drift and
ensure only one place needs changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4081d3a1-af62-4e69-85d0-8b6ebb92b3ae
⛔ Files ignored due to path filters (2)
Cargo.lockis excluded by!**/*.lockbun.lockis excluded by!**/*.lock
📒 Files selected for processing (17)
.github/workflows/ci-backend.yml.github/workflows/ci-docs.yml.github/workflows/ci-landing.yml.github/workflows/ci-packages.yml.github/workflows/ci-web.yml.github/workflows/release-desktop.yml.github/workflows/sync-homebrew-tap.ymlapps/docs/package.jsonapps/landing/package.jsonapps/web/package.jsonapps/web/src/components/dialogs/SettingsModal.tsxapps/web/src/components/layout/PanelLayout.tsxapps/web/src/components/ui/AtmosWordmark.tsxapps/web/src/components/welcome/WelcomePage.tsxpackage.jsonpackages/ui/src/components/ai-elements/message.tsxpackages/ui/src/components/ai-elements/reasoning.tsx
| return ( | ||
| <Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}> | ||
| <DialogContent className="sm:max-w-lg"> | ||
| <DialogContent className="h-[min(90vh,820px)] w-[min(96vw,1360px)] max-w-[min(96vw,1360px)] overflow-hidden border-border bg-background p-0 sm:!max-w-[min(96vw,1360px)]"> |
There was a problem hiding this comment.
The fixed grids will clip on narrow viewports.
The dialog can shrink to 96vw, but this layout hard-codes a 240px sidebar and repeated 320px value columns. On smaller windows that leaves no usable width for the content column, and overflow-hidden on DialogContent will just crop the UI instead of reflowing it.
♻️ Proposed fix
- <DialogContent className="h-[min(90vh,820px)] w-[min(96vw,1360px)] max-w-[min(96vw,1360px)] overflow-hidden border-border bg-background p-0 sm:!max-w-[min(96vw,1360px)]">
+ <DialogContent className="h-[min(90vh,820px)] w-[min(96vw,1360px)] max-w-[min(96vw,1360px)] overflow-hidden border-border bg-background p-0 sm:!max-w-[min(96vw,1360px)]">
@@
- <div className="grid h-full grid-cols-[240px_minmax(0,1fr)]">
- <aside className="flex h-full flex-col border-r border-border bg-muted/20">
+ <div className="grid h-full grid-cols-1 lg:grid-cols-[240px_minmax(0,1fr)]">
+ <aside className="flex flex-col border-b border-border bg-muted/20 lg:h-full lg:border-b-0 lg:border-r">
@@
- <section className="flex min-h-0 flex-col">
+ <section className="flex min-h-0 min-w-0 flex-col">
@@
- <div className="grid grid-cols-[minmax(0,1fr)_320px] gap-8 border-b border-border px-6 py-5">
+ <div className="grid grid-cols-1 gap-4 border-b border-border px-6 py-5 lg:grid-cols-[minmax(0,1fr)_320px] lg:gap-8">Apply the same grid-cols-1 -> lg:grid-cols[...] pattern to the repeated 320px rows below.
Also applies to: 73-74, 125-126, 138-146, 152-160, 173-180
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/dialogs/SettingsModal.tsx` at line 67, The dialog's
grid uses fixed 240px and repeated 320px column classes which cause clipping on
narrow viewports; update the grid classnames in SettingsModal (the DialogContent
wrapper and the internal grid containers that define the 240px sidebar + 320px
columns) to switch to a single-column layout on small screens (e.g.,
grid-cols-1) and only apply the multi-column (240px + 320px repeats) layout at
lg/breakpoint sizes (e.g., lg:grid-cols-...), so the sidebar and content stack
and reflow instead of being cropped; apply this change to the same grid class
usages referenced (the DialogContent and the internal grids around the 240px
sidebar and 320px columns).
| <div className={cn('flex flex-col items-center', className)}> | ||
| <div | ||
| className={cn( | ||
| 'group flex w-full max-w-3xl items-center justify-between cursor-default select-none', | ||
| GeistPixelCircle.className | ||
| )} | ||
| > | ||
| <span | ||
| className={cn( | ||
| 'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm', | ||
| letterClassName | ||
| )} | ||
| > | ||
| A | ||
| </span> | ||
| <span | ||
| className={cn( | ||
| 'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm', | ||
| letterClassName | ||
| )} | ||
| > | ||
| t | ||
| </span> | ||
| <span | ||
| className={cn( | ||
| 'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm', | ||
| letterClassName | ||
| )} | ||
| > | ||
| m | ||
| </span> | ||
| <LogoSvg | ||
| className={cn( | ||
| 'size-36 shrink-0 text-foreground drop-shadow-sm transition-transform duration-1000 group-hover:rotate-90', | ||
| logoClassName | ||
| )} | ||
| /> | ||
| <span | ||
| className={cn( | ||
| 'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm', | ||
| letterClassName | ||
| )} | ||
| > | ||
| s | ||
| </span> | ||
| </div> |
There was a problem hiding this comment.
Expose the brand name to assistive tech.
Because the wordmark is rendered as individual letters plus a decorative SVG, assistive tech will not reliably announce “Atmos” from this markup. Add hidden text for the product name and mark the stylized letter row as decorative.
♿ Proposed fix
return (
<div className={cn('flex flex-col items-center', className)}>
+ <span className="sr-only">Atmos</span>
<div
+ aria-hidden="true"
className={cn(
'group flex w-full max-w-3xl items-center justify-between cursor-default select-none',
GeistPixelCircle.className📝 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.
| <div className={cn('flex flex-col items-center', className)}> | |
| <div | |
| className={cn( | |
| 'group flex w-full max-w-3xl items-center justify-between cursor-default select-none', | |
| GeistPixelCircle.className | |
| )} | |
| > | |
| <span | |
| className={cn( | |
| 'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm', | |
| letterClassName | |
| )} | |
| > | |
| A | |
| </span> | |
| <span | |
| className={cn( | |
| 'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm', | |
| letterClassName | |
| )} | |
| > | |
| t | |
| </span> | |
| <span | |
| className={cn( | |
| 'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm', | |
| letterClassName | |
| )} | |
| > | |
| m | |
| </span> | |
| <LogoSvg | |
| className={cn( | |
| 'size-36 shrink-0 text-foreground drop-shadow-sm transition-transform duration-1000 group-hover:rotate-90', | |
| logoClassName | |
| )} | |
| /> | |
| <span | |
| className={cn( | |
| 'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm', | |
| letterClassName | |
| )} | |
| > | |
| s | |
| </span> | |
| </div> | |
| <div className={cn('flex flex-col items-center', className)}> | |
| <span className="sr-only">Atmos</span> | |
| <div | |
| aria-hidden="true" | |
| className={cn( | |
| 'group flex w-full max-w-3xl items-center justify-between cursor-default select-none', | |
| GeistPixelCircle.className | |
| )} | |
| > | |
| <span | |
| className={cn( | |
| 'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm', | |
| letterClassName | |
| )} | |
| > | |
| A | |
| </span> | |
| <span | |
| className={cn( | |
| 'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm', | |
| letterClassName | |
| )} | |
| > | |
| t | |
| </span> | |
| <span | |
| className={cn( | |
| 'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm', | |
| letterClassName | |
| )} | |
| > | |
| m | |
| </span> | |
| <LogoSvg | |
| className={cn( | |
| 'size-36 shrink-0 text-foreground drop-shadow-sm transition-transform duration-1000 group-hover:rotate-90', | |
| logoClassName | |
| )} | |
| /> | |
| <span | |
| className={cn( | |
| 'text-[10rem] font-normal uppercase leading-[0.75] tracking-normal text-foreground drop-shadow-sm', | |
| letterClassName | |
| )} | |
| > | |
| s | |
| </span> | |
| </div> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/src/components/ui/AtmosWordmark.tsx` around lines 22 - 67, The
wordmark currently renders as separate letters and an SVG which screen readers
won’t read as “Atmos”; update the AtmosWordmark component so the decorative row
(the container div that holds the letter spans and LogoSvg) is marked decorative
by adding aria-hidden="true" (or role="presentation") to that element, and add a
visually-hidden text node for the product name (e.g., a span with a
visually-hidden/sr-only class containing "Atmos") at the start or end of the
component so assistive tech will announce the brand; target the container that
includes the letter spans and the LogoSvg and add the hidden span alongside it.
Summary
16.2.0and aligneslint-config-nextversionsactions/checkout@v4to@v5for Node 24 compatibilitystreamdowntype incompatibilities in@workspace/uiafter dependency resolution changesidandorderprops to the main resizable panel layout to address dynamic panel warningsRelated Issue
Closes #44
Type of Change
Validation
just lintjust testjust fmtAdditional checks:
bun installbun run typecheckinapps/webbun run buildinapps/webbun run typecheckinpackages/uibun run typecheckinapps/landingbun run buildinapps/landingbun run types:checkinapps/docsbun run buildinapps/docsChecklist
Summary by cubic
Upgrade the monorepo to Next.js
16.2.0and Node 24–ready Actions, and refresh the Settings modal with a new sidebar layout. Fixes panel warnings andstreamdowntypes; alignseslint-config-next. Closes #44.New Features
AtmosWordmark, flatter settings-style layout, and structured desktop update checks (runtime, version, status).idandorderon panels to stop dynamic panel warnings.AtmosWordmarkon Welcome.Dependencies
nextto16.2.0acrossapps/web,apps/landing,apps/docs; aligneslint-config-nextto16.2.0.actions/checkout@v5for Node 24 compatibility.0.2.1.streamdownplugin type compatibility in@workspace/ui.Written for commit ddb8905. Summary will update on new commits.
Summary by CodeRabbit
New Features
Chores