fix: default border token, dither-kit theme toggle + DialKit tweak panel - #72
Conversation
- Tailwind v4 has no --default-border-color mechanism; the bare border utility fell back to currentColor (white on dark). Base layer rule now points default borders at the border token. - /dither-kit gets its theme toggle back: page-scoped .dither-light token overrides + a dark wrapper class for dark: variants, persisted in localStorage. The app itself stays dark-only.
The hand-rolled sidebar (Field/Select/Range/TweakSidebar) is replaced by DialKit's floating control panel (https://joshpuckett.me/dialkit) — bloom folder (preset + custom sliders), per-series variant selects, stacking toggle, pie radius + entrance sliders, and a replay-all action. productionEnabled so visitors on tripwire.sh get the panel too; the settle-replay behavior is unchanged (any tweak replays after 300ms).
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
React Doctor found 1 issue in 1 file · 1 error · score 46 / 100 (Critical) · vs Errors
|
|
Warning Review limit reached
Next review available in: 49 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. 📝 WalkthroughWalkthroughThis pull request adds DialKit to the dither-kit page, replaces the old sidebar-driven tweak UI with floating dial controls, and moves docs/chart rendering into new shared content, UI, and section modules. The route now derives tweak state from DialKit params, keeps replay counters, and uses a persisted page theme hook. Styles add a light theme token set and update border-color handling. Biome overrides were expanded for the new docs component files. Sequence Diagram(s)See the hidden review stack artifact. Related PRs: None provided. Suggested labels: enhancement, dependencies, refactor Suggested reviewers: None provided. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
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.
Actionable comments posted: 3
🧹 Nitpick comments (2)
apps/web/src/routes/dither-kit.tsx (2)
1-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove React/core imports to the top of the import block.
This new DialKit import block leaves the React import after external packages, contrary to the project import order.
Proposed import order
+import { Fragment, type ReactNode, useEffect, useRef, useState } from "react" import { createFileRoute } from "`@tanstack/react-router`" import { DialRoot, useDialKit } from "dialkit" import "dialkit/styles.css" import { CheckIcon, CopyIcon, MoonIcon, RefreshCcwIcon, SunIcon, } from "lucide-react" -import { Fragment, type ReactNode, useEffect, useRef, useState } from "react"As per coding guidelines, "
**/*.{ts,tsx,js,jsx}: Import Order: React/core libraries, External libraries, UI components (#/components/ui/), Utilities (#/lib/), Feature imports, Package imports (@tripwire/*)."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/routes/dither-kit.tsx` around lines 1 - 11, The import block in dither-kit should follow the project import order by moving the React/core imports to the top before external packages like createFileRoute, dialkit, and lucide-react. Update the existing import grouping around the React import so the React symbols (Fragment, ReactNode, useEffect, useRef, useState) are listed first, keeping the rest of the DialKit and icon imports after them.Source: Coding guidelines
441-443: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTrim the decorative comment.
This new comment is section-style, verbose, and contains an em dash; the
useDialKitcall is self-descriptive enough without it.As per coding guidelines, "
Comments should be sparse, contain no em dashes or overly verbose language patterns."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/routes/dither-kit.tsx` around lines 441 - 443, The comment around the useDialKit configuration is too verbose and decorative; remove or trim it so the code remains self-descriptive without section-style prose. Update the nearby explanatory text in dither-kit.tsx to be sparse and neutral, and avoid em dashes or long descriptive phrasing while keeping any necessary context minimal.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/routes/dither-kit.tsx`:
- Around line 524-535: The icon-only theme button in the dither-kit route uses a
generic aria-label that does not indicate the resulting theme state. Update the
button in the theme toggle UI (the element wired to toggleTheme and rendering
MoonIcon/SunIcon) to use an action-specific accessible label that reflects what
will be activated, such as the next theme mode rather than “Toggle theme,” so
assistive tech users can understand the action.
- Around line 169-184: The usePageTheme hook needs safer persistence and an
explicit type. Update usePageTheme to catch any localStorage access errors
around the initial getItem and the toggle’s setItem so the page still works when
storage is blocked, and return a typed shape for light and toggle. Also remove
the JSDoc from this internal hook since the guideline only allows JSDoc for
exported functions/types, and keep the fix localized to usePageTheme.
- Around line 471-491: The snippet generators are missing the DialKit-driven
animation duration, so copied chart code does not reflect the current
`entranceMs`/`tweaks.duration` setting. Update the chart snippet template
helpers in `dither-kit.tsx` to include `animationDuration` by appending the
existing `durationAttr(t)` alongside `stackAttr(t)` and `bloomAttr(t)`, so the
generated snippets match the preview configuration.
---
Nitpick comments:
In `@apps/web/src/routes/dither-kit.tsx`:
- Around line 1-11: The import block in dither-kit should follow the project
import order by moving the React/core imports to the top before external
packages like createFileRoute, dialkit, and lucide-react. Update the existing
import grouping around the React import so the React symbols (Fragment,
ReactNode, useEffect, useRef, useState) are listed first, keeping the rest of
the DialKit and icon imports after them.
- Around line 441-443: The comment around the useDialKit configuration is too
verbose and decorative; remove or trim it so the code remains self-descriptive
without section-style prose. Update the nearby explanatory text in
dither-kit.tsx to be sparse and neutral, and avoid em dashes or long descriptive
phrasing while keeping any necessary context minimal.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: f25fce3d-594a-4730-9269-a870f30a1f92
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (3)
apps/web/package.jsonapps/web/src/routes/dither-kit.tsxapps/web/src/styles.css
| entranceMs: [900, 300, 2400, 100], | ||
| replayAll: { type: "action" }, | ||
| }, | ||
| { | ||
| onAction: (path: string) => { | ||
| if (path === "replayAll") replayAll() | ||
| }, | ||
| } | ||
| ) | ||
|
|
||
| const tweaks: Tweaks = { | ||
| bloomPreset: params.bloom.preset as BloomPreset, | ||
| blur: params.bloom.blur, | ||
| brightness: params.bloom.brightness, | ||
| opacity: params.bloom.opacity, | ||
| saturate: params.bloom.saturate, | ||
| primaryVariant: params.desktopVariant as AreaVariant, | ||
| secondaryVariant: params.mobileVariant as AreaVariant, | ||
| stacked: params.stacked, | ||
| donutRadius: params.pieInnerRadius, | ||
| duration: params.entranceMs, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Reflect entranceMs in the generated snippets.
The DialKit slider now drives tweaks.duration and the previews pass it as animationDuration, but the snippet helpers still omit animationDuration, so this control is not reflected in copied code.
Proposed direction
const stackAttr = (t: Tweaks) => (t.stacked ? ' stackType="stacked"' : "")
+const durationAttr = (t: Tweaks) =>
+ t.duration === 900 ? "" : ` animationDuration={${t.duration}}`Then append ${durationAttr(t)} in each chart snippet template alongside stackAttr(t)/bloomAttr(t).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/src/routes/dither-kit.tsx` around lines 471 - 491, The snippet
generators are missing the DialKit-driven animation duration, so copied chart
code does not reflect the current `entranceMs`/`tweaks.duration` setting. Update
the chart snippet template helpers in `dither-kit.tsx` to include
`animationDuration` by appending the existing `durationAttr(t)` alongside
`stackAttr(t)` and `bloomAttr(t)`, so the generated snippets match the preview
configuration.
| <button | ||
| type="button" | ||
| aria-label="Toggle theme" | ||
| onClick={toggleTheme} | ||
| className="rounded-md border p-2 text-muted-foreground transition-colors hover:border-foreground/25 hover:text-foreground" | ||
| > | ||
| {light ? ( | ||
| <MoonIcon className="size-4" /> | ||
| ) : ( | ||
| <SunIcon className="size-4" /> | ||
| )} | ||
| </button> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use an action-specific label for the icon-only theme button.
aria-label="Toggle theme" does not tell assistive tech users which theme will be activated.
Proposed fix
- aria-label="Toggle theme"
+ aria-label={light ? "Switch to dark theme" : "Switch to light theme"}📝 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.
| <button | |
| type="button" | |
| aria-label="Toggle theme" | |
| onClick={toggleTheme} | |
| className="rounded-md border p-2 text-muted-foreground transition-colors hover:border-foreground/25 hover:text-foreground" | |
| > | |
| {light ? ( | |
| <MoonIcon className="size-4" /> | |
| ) : ( | |
| <SunIcon className="size-4" /> | |
| )} | |
| </button> | |
| <button | |
| type="button" | |
| aria-label={light ? "Switch to dark theme" : "Switch to light theme"} | |
| onClick={toggleTheme} | |
| className="rounded-md border p-2 text-muted-foreground transition-colors hover:border-foreground/25 hover:text-foreground" | |
| > | |
| {light ? ( | |
| <MoonIcon className="size-4" /> | |
| ) : ( | |
| <SunIcon className="size-4" /> | |
| )} | |
| </button> |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/src/routes/dither-kit.tsx` around lines 524 - 535, The icon-only
theme button in the dither-kit route uses a generic aria-label that does not
indicate the resulting theme state. Update the button in the theme toggle UI
(the element wired to toggleTheme and rendering MoonIcon/SunIcon) to use an
action-specific accessible label that reflects what will be activated, such as
the next theme mode rather than “Toggle theme,” so assistive tech users can
understand the action.
Entrance animation now fires only on page load or the replay buttons — turning a dial adjusts the live chart without re-running the sweep.
|
|
||
| /* -------------------------------------------------------------- showcase */ | ||
|
|
||
| function Showcase({ |
There was a problem hiding this comment.
React Doctor · react-doctor/no-multi-comp (warning)
This file declares several components, so each component is harder to find, test, and change.
Fix → Move secondary components into their own files so each component stays easier to find, test, and change.
| @@ -658,10 +397,8 @@ function Showcase({ | |||
| /* ------------------------------------------------------------------ page */ | |||
|
|
|||
| function DitherKitDocs() { | |||
There was a problem hiding this comment.
React Doctor · react-doctor/no-giant-component (warning)
Component "DitherKitDocs" is 427 lines long, which is hard to read & change. Split it into a few smaller components.
Fix → Pull each section into its own component so the parent is easier to read, test, and change.
| @@ -658,10 +397,8 @@ function Showcase({ | |||
| /* ------------------------------------------------------------------ page */ | |||
|
|
|||
| function DitherKitDocs() { | |||
There was a problem hiding this comment.
React Doctor · react-doctor/no-multi-comp (warning)
This file declares several components, so each component is harder to find, test, and change.
Fix → Move secondary components into their own files so each component stays easier to find, test, and change.
- usePageTheme hardened: localStorage reads/writes wrapped so disabled
storage can't break the page, and the hook gets an explicit
PageThemeState return type (review: stability)
- snippets now reflect the entrance slider — non-default entranceMs
emits animationDuration={n} in every copied chart snippet (review:
functional correctness)
- the theme button's label says which theme it switches to instead of
a generic toggle (review: a11y)
- react-doctor's no-giant-component / no-multi-comp on the route: the
893-line file is now a ~140-line route composing
components/dither-kit-docs/{content,ui,sections} — data + snippet
builders, page primitives (strip, copy, highlighter, pills,
showcase), and the six page sections
|
Addressed all review comments:
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (7)
apps/web/src/components/dither-kit-docs/sections.tsx (2)
63-77: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract named props interfaces instead of inline anonymous types.
HeroSection(and likewiseInstallSectionat Lines 154-160 andChartGalleryat Lines 254-266) declare their props as inline object types. Define PascalCase*Propsinterfaces.♻️ Example for HeroSection
+interface HeroSectionProps { + light: boolean + onToggleTheme: () => void + tweaks: Tweaks + bloom: BloomInput + replayToken: number + onReplay: () => void +} + -export function HeroSection({ - light, - onToggleTheme, - tweaks, - bloom, - replayToken, - onReplay, -}: { - light: boolean - onToggleTheme: () => void - tweaks: Tweaks - bloom: BloomInput - replayToken: number - onReplay: () => void -}) { +export function HeroSection({ + light, + onToggleTheme, + tweaks, + bloom, + replayToken, + onReplay, +}: HeroSectionProps) {As per coding guidelines: "Always define props interface for components." and "Interfaces: PascalCase with suffix (
WorkflowEditorProps)."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/dither-kit-docs/sections.tsx` around lines 63 - 77, Extract the inline props object type from HeroSection into a named PascalCase interface (for example, HeroSectionProps) and use it in the function signature; apply the same pattern to InstallSection and ChartGallery so all component props follow the project guideline of defining explicit props interfaces. Keep the interface definitions close to their respective components in sections.tsx and ensure the names are suffixed with Props for easy identification.Source: Coding guidelines
51-51: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDecorative doc comment with an em dash violates comment guidelines.
This
/** */block is not attached to an exported function/type and also uses an em dash. Convert to a sparse plain comment (or drop it).♻️ Proposed change
-/** The docs page, section by section — the route composes these. */ +// Section components composed by the /dither-kit route.As per coding guidelines: "Use JSDoc for exported functions/types only. No ASCII divider comments or decorative multi-line doc comments." and "Comments should ... contain no em dashes."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/dither-kit-docs/sections.tsx` at line 51, The comment in sections.tsx is a decorative JSDoc block that is not documenting an exported function/type and also contains an em dash. Remove it or replace it with a simple plain single-line comment, and keep comments sparse and non-decorative; use JSDoc only for exported symbols such as functions or types.Source: Coding guidelines
apps/web/src/components/dither-kit-docs/ui.tsx (4)
16-22: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winInline
styleobject instead of Tailwind.The gradient/mask values are static strings and can be expressed as Tailwind arbitrary-value classes instead of the
styleprop.As per coding guidelines, "Use Tailwind only, no inline styles."
✏️ Suggested conversion
- className={`text-border ${className}`} - style={{ - backgroundImage: - "repeating-conic-gradient(currentColor 0% 25%, transparent 0% 50%)", - backgroundSize: "6px 6px", - maskImage: - "linear-gradient(to right, transparent, black 18%, black 82%, transparent)", - }} + className={`text-border bg-[repeating-conic-gradient(currentColor_0%_25%,transparent_0%_50%)] bg-[length:6px_6px] [mask-image:linear-gradient(to_right,transparent,black_18%,black_82%,transparent)] ${className}`}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/dither-kit-docs/ui.tsx` around lines 16 - 22, Replace the inline style object in the dither-kit docs UI component with Tailwind arbitrary-value classes, since the gradient and mask values are static. Update the relevant JSX in the UI component to express the background image, background size, and mask image using Tailwind-only className utilities instead of the style prop, keeping the same visual effect.Source: Coding guidelines
6-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winComment style deviates from repo conventions.
Several issues recur through the file:
- ASCII section dividers at Lines 27, 69, 105, 194, 235.
- A decorative, non-attached file-level doc comment at Lines 6-7.
- JSDoc on a non-exported function (
tokenize) at Lines 123-125.- Em dashes in comments at Lines 9-11, 34-35, 123-125, 151-153.
As per coding guidelines, "No ASCII divider comments or decorative multi-line doc comments" and "Comments should be sparse, contain no em dashes or overly verbose language patterns."
Also applies to: 9-11, 27-27, 34-35, 69-69, 105-105, 123-125, 151-153, 194-194, 235-235
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/dither-kit-docs/ui.tsx` around lines 6 - 7, The comments in ui.tsx should be brought in line with repo style by removing the decorative file-level doc comment, all ASCII divider comments, and the JSDoc on the non-exported tokenize helper. Also rewrite the remaining comments to be sparse and plain, eliminating em dashes and overly verbose phrasing while keeping only any truly necessary inline notes near the relevant symbols such as tokenize and the other component helpers.Source: Coding guidelines
11-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winInline prop type literals instead of named props interfaces.
DitherStrip,CopyLine,Pill,ReplayButton, andShowcaseall destructure against anonymous inline object types rather than named interfaces.Showcase(Lines 237-252) is the most impacted, with 6 fields typed inline.As per coding guidelines, "Always define props interface for components" and "Never introduce a raw inline type to a component-level or route-level file."
✏️ Example fix for `Showcase`
+interface ShowcaseProps { + title: string + install: string + code: string + toolbar?: ReactNode + children: ReactNode + tall?: boolean +} + -export function Showcase({ - title, - install, - code, - toolbar, - children, - tall = false, -}: { - title: string - install: string - code: string - /** Extra controls rendered in the card toolbar (e.g. replay). */ - toolbar?: ReactNode - children: ReactNode - tall?: boolean -}) { +export function Showcase({ + title, + install, + code, + toolbar, + children, + tall = false, +}: ShowcaseProps) {Also applies to: 82-82, 197-205, 222-222, 237-252
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/dither-kit-docs/ui.tsx` at line 11, Replace the anonymous inline prop type annotations in DitherStrip, CopyLine, Pill, ReplayButton, and Showcase with named props interfaces defined in the same module. Update each component signature to destructure from its interface type instead of a raw inline object, and make Showcase’s multi-field props especially use a dedicated interface to match the component props guideline.Source: Coding guidelines
211-215: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConditional classes built with raw ternaries instead of
cn().
Pill's active-state classes andShowcase'stall-driven height classes use manual template-literal ternaries rather than the sharedcn()helper.As per coding guidelines, "Use
cn()from@tripwire/ui/utilsfor conditional classes."Also applies to: 292-297
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/dither-kit-docs/ui.tsx` around lines 211 - 215, The conditional class logic in Pill and Showcase is using raw template-literal ternaries instead of the shared cn() helper. Update the relevant className construction in Pill and the tall-driven height handling in Showcase to use cn() from `@tripwire/ui/utils` for conditional merging, keeping the same active/tall behavior while moving the branching into cn()-based arguments.Source: Coding guidelines
apps/web/src/components/dither-kit-docs/content.ts (1)
7-8: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winComment style deviates from repo conventions.
ASCII section dividers (Lines 13, 29, 64, 99) and JSDoc placed on non-exported helpers or as a decorative file-level summary (Lines 7-8, 18, 101, 112) both conflict with the stated conventions.
As per coding guidelines, "Do not use ASCII divider comments (
// ─── Section Name ───────). Use whitespace and clear naming instead" and "Use JSDoc for exported functions/types only. No ASCII divider comments or decorative multi-line doc comments."✏️ Example cleanup
-/* ------------------------------------------------------- package manager */ - + export const PMS = ["npm", "pnpm", "yarn", "bun"] as const export type Pm = (typeof PMS)[number] -/** The runner each package manager uses for one-off CLIs. */ +// The runner each package manager uses for one-off CLIs. const PM_RUNNER: Record<Pm, string> = {Also applies to: 13-13, 18-18, 29-29, 64-64, 99-99, 101-101, 112-112
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/dither-kit-docs/content.ts` around lines 7 - 8, Remove the decorative file-level JSDoc summary and any JSDoc blocks attached to non-exported helpers in content.ts, since only exported functions/types should use JSDoc. Also replace the ASCII section divider comments with whitespace and clearer naming, keeping the structure readable without decorative separators; update the affected helpers and section markers consistently throughout the file.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/components/dither-kit-docs/content.ts`:
- Around line 113-114: The entrance duration default is hardcoded in both the
docs snippet generator and the live route, which can cause them to drift.
Extract a shared DEFAULT_ENTRANCE_MS constant and use it in content.ts’s
durationAttr helper and in the dither-kit route’s default value so both
references stay synchronized.
In `@apps/web/src/components/dither-kit-docs/sections.tsx`:
- Around line 89-100: The theme toggle in sections.tsx is using a raw <button>
instead of the shared Button primitive. Replace the existing button inside the
theme toggle component with the exported Button component from the UI package,
preserving the same aria-label, onClick handler, and MoonIcon/SunIcon rendering
while applying the appropriate icon/ghost styling through Button props or
variant classes.
In `@apps/web/src/components/dither-kit-docs/ui.tsx`:
- Around line 71-79: The useCopy hook shows success before the clipboard write
completes, so copy feedback can be false. Update useCopy().copy to handle the
async navigator.clipboard.writeText(text) promise, only call setCopied(true)
after a successful write, and avoid setting copied on failure by
catching/rejecting errors and keeping the current state unchanged or handling an
error state if needed.
---
Nitpick comments:
In `@apps/web/src/components/dither-kit-docs/content.ts`:
- Around line 7-8: Remove the decorative file-level JSDoc summary and any JSDoc
blocks attached to non-exported helpers in content.ts, since only exported
functions/types should use JSDoc. Also replace the ASCII section divider
comments with whitespace and clearer naming, keeping the structure readable
without decorative separators; update the affected helpers and section markers
consistently throughout the file.
In `@apps/web/src/components/dither-kit-docs/sections.tsx`:
- Around line 63-77: Extract the inline props object type from HeroSection into
a named PascalCase interface (for example, HeroSectionProps) and use it in the
function signature; apply the same pattern to InstallSection and ChartGallery so
all component props follow the project guideline of defining explicit props
interfaces. Keep the interface definitions close to their respective components
in sections.tsx and ensure the names are suffixed with Props for easy
identification.
- Line 51: The comment in sections.tsx is a decorative JSDoc block that is not
documenting an exported function/type and also contains an em dash. Remove it or
replace it with a simple plain single-line comment, and keep comments sparse and
non-decorative; use JSDoc only for exported symbols such as functions or types.
In `@apps/web/src/components/dither-kit-docs/ui.tsx`:
- Around line 16-22: Replace the inline style object in the dither-kit docs UI
component with Tailwind arbitrary-value classes, since the gradient and mask
values are static. Update the relevant JSX in the UI component to express the
background image, background size, and mask image using Tailwind-only className
utilities instead of the style prop, keeping the same visual effect.
- Around line 6-7: The comments in ui.tsx should be brought in line with repo
style by removing the decorative file-level doc comment, all ASCII divider
comments, and the JSDoc on the non-exported tokenize helper. Also rewrite the
remaining comments to be sparse and plain, eliminating em dashes and overly
verbose phrasing while keeping only any truly necessary inline notes near the
relevant symbols such as tokenize and the other component helpers.
- Line 11: Replace the anonymous inline prop type annotations in DitherStrip,
CopyLine, Pill, ReplayButton, and Showcase with named props interfaces defined
in the same module. Update each component signature to destructure from its
interface type instead of a raw inline object, and make Showcase’s multi-field
props especially use a dedicated interface to match the component props
guideline.
- Around line 211-215: The conditional class logic in Pill and Showcase is using
raw template-literal ternaries instead of the shared cn() helper. Update the
relevant className construction in Pill and the tall-driven height handling in
Showcase to use cn() from `@tripwire/ui/utils` for conditional merging, keeping
the same active/tall behavior while moving the branching into cn()-based
arguments.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 61731bd1-e9b6-4327-bdf7-5730763c14df
📒 Files selected for processing (5)
apps/web/biome.jsonapps/web/src/components/dither-kit-docs/content.tsapps/web/src/components/dither-kit-docs/sections.tsxapps/web/src/components/dither-kit-docs/ui.tsxapps/web/src/routes/dither-kit.tsx
| const durationAttr = (t: Tweaks): string => | ||
| t.duration === 900 ? "" : ` animationDuration={${t.duration}}` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## content.ts context\n'
sed -n '90,140p' apps/web/src/components/dither-kit-docs/content.ts | cat -n
printf '\n## dither-kit.tsx context\n'
sed -n '1,220p' apps/web/src/routes/dither-kit.tsx | cat -n
printf '\n## search for entranceMs and 900 defaults\n'
rg -n "entranceMs|900" apps/web/src/components/dither-kit-docs/content.ts apps/web/src/routes/dither-kit.tsxRepository: bountydotnew/tripwire
Length of output: 7403
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n## content.ts outline\n'
ast-grep outline apps/web/src/components/dither-kit-docs/content.ts --view expanded || true
printf '\n## dither-kit.tsx outline\n'
ast-grep outline apps/web/src/routes/dither-kit.tsx --view expanded || trueRepository: bountydotnew/tripwire
Length of output: 751
Keep the entrance duration default in one place. apps/web/src/components/dither-kit-docs/content.ts and apps/web/src/routes/dither-kit.tsx both hardcode 900, so the generated snippet can drift from the live default. Extract a shared DEFAULT_ENTRANCE_MS and use it in both spots.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/src/components/dither-kit-docs/content.ts` around lines 113 - 114,
The entrance duration default is hardcoded in both the docs snippet generator
and the live route, which can cause them to drift. Extract a shared
DEFAULT_ENTRANCE_MS constant and use it in content.ts’s durationAttr helper and
in the dither-kit route’s default value so both references stay synchronized.
| <button | ||
| type="button" | ||
| aria-label={light ? "Switch to dark theme" : "Switch to light theme"} | ||
| onClick={onToggleTheme} | ||
| className="rounded-md border p-2 text-muted-foreground transition-colors hover:border-foreground/25 hover:text-foreground" | ||
| > | ||
| {light ? ( | ||
| <MoonIcon className="size-4" /> | ||
| ) : ( | ||
| <SunIcon className="size-4" /> | ||
| )} | ||
| </button> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Confirm an icon/button primitive is exported from the ui barrel
fd -t f 'index.*' apps/web/src/components/ui | head
rg -nP '\bexport\s+.*\bButton\b' apps/web/src/components/uiRepository: bountydotnew/tripwire
Length of output: 340
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Top-level files/dirs:\n'
fd -t d -d 2 . | sed 's#^\./##' | sort | head -200
printf '\nLocate reviewed file:\n'
fd -t f 'sections.tsx' . | sed 's#^\./##'
printf '\nLocate Button exports / ui barrel files:\n'
fd -t f 'index.*' . | rg '/(components/ui|components/ui/|ui)/' || true
rg -n --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' '\bexport\b.*\bButton\b|\bexport\b.*\bbutton\b' .
printf '\nLocate raw button usage near dither-kit-docs:\n'
rg -n --glob 'apps/web/src/components/dither-kit-docs/**' '<button|Button' apps/web/src/components/dither-kit-docs || trueRepository: bountydotnew/tripwire
Length of output: 2299
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'packages/ui/src/button.tsx (outline + first 220 lines):\n'
ast-grep outline packages/ui/src/button.tsx --view expanded || true
sed -n '1,220p' packages/ui/src/button.tsx | cat -n
printf '\napps/web/src/components/dither-kit-docs/sections.tsx (top 140 lines):\n'
sed -n '1,140p' apps/web/src/components/dither-kit-docs/sections.tsx | cat -n
printf '\napps/web/src/components/dither-kit-docs/ui.tsx (button-related slices):\n'
sed -n '1,320p' apps/web/src/components/dither-kit-docs/ui.tsx | cat -n | sed -n '1,320p'Repository: bountydotnew/tripwire
Length of output: 23085
Use the shared Button primitive for the theme toggle. The UI package already exports Button with icon sizes/ghost styling, so this raw <button> can be replaced without changing behavior.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/src/components/dither-kit-docs/sections.tsx` around lines 89 - 100,
The theme toggle in sections.tsx is using a raw <button> instead of the shared
Button primitive. Replace the existing button inside the theme toggle component
with the exported Button component from the UI package, preserving the same
aria-label, onClick handler, and MoonIcon/SunIcon rendering while applying the
appropriate icon/ghost styling through Button props or variant classes.
Source: Coding guidelines
| function useCopy() { | ||
| const [copied, setCopied] = useState(false) | ||
| const copy = (text: string) => { | ||
| navigator.clipboard?.writeText(text) | ||
| setCopied(true) | ||
| setTimeout(() => setCopied(false), 1400) | ||
| } | ||
| return { copied, copy } | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Copy feedback fires even when the clipboard write fails.
navigator.clipboard?.writeText(text) isn't awaited/handled, so setCopied(true) runs unconditionally — including when clipboard access is unavailable or the write promise rejects, giving users false "copied" feedback.
🐛 Suggested fix
function useCopy() {
const [copied, setCopied] = useState(false)
const copy = (text: string) => {
- navigator.clipboard?.writeText(text)
- setCopied(true)
- setTimeout(() => setCopied(false), 1400)
+ navigator.clipboard
+ ?.writeText(text)
+ .then(() => {
+ setCopied(true)
+ setTimeout(() => setCopied(false), 1400)
+ })
+ .catch(() => {})
}
return { copied, copy }
}📝 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.
| function useCopy() { | |
| const [copied, setCopied] = useState(false) | |
| const copy = (text: string) => { | |
| navigator.clipboard?.writeText(text) | |
| setCopied(true) | |
| setTimeout(() => setCopied(false), 1400) | |
| } | |
| return { copied, copy } | |
| } | |
| function useCopy() { | |
| const [copied, setCopied] = useState(false) | |
| const copy = (text: string) => { | |
| navigator.clipboard | |
| ?.writeText(text) | |
| .then(() => { | |
| setCopied(true) | |
| setTimeout(() => setCopied(false), 1400) | |
| }) | |
| .catch(() => {}) | |
| } | |
| return { copied, copy } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/src/components/dither-kit-docs/ui.tsx` around lines 71 - 79, The
useCopy hook shows success before the clipboard write completes, so copy
feedback can be false. Update useCopy().copy to handle the async
navigator.clipboard.writeText(text) promise, only call setCopied(true) after a
successful write, and avoid setting copied on failure by catching/rejecting
errors and keeping the current state unchanged or handling an error state if
needed.
Three fixes for the deployed /dither-kit page:
White borders — Tailwind v4 has no
--default-border-colormechanism (that token was dead config), so bareborderutilities fell back tocurrentColor→ white hairlines on dark. A base-layer rule now points default borders at--color-borderapp-wide.Theme toggle restored — the app is dark-only, so the page scopes its own theme: a
.dither-lightclass overrides the shadcn-alias tokens on the page wrapper, and the wrapper'sdarkclass drivesdark:variants. Persisted per visitor.DialKit tweak panel — the hand-rolled tweak sidebar is replaced with DialKit: floating panel with a bloom folder (preset select + custom blur/brightness/opacity/saturate sliders), per-series variant selects, stacking toggle, pie inner-radius + entrance-duration sliders, and a replay-all action.
productionEnabledso it works for visitors. Code snippets still track every change.Summary by CodeRabbit