Skip to content

Conversation

@ingoau
Copy link
Member

@ingoau ingoau commented Aug 10, 2025

Summary by CodeRabbit

  • New Features

    • Added "Stopwatch & Timer" tool with stopwatch, timer, laps, presets and audio; particle background animation on homepage; "Report Bug" button in play area.
  • Enhancements

    • Improved sidebar presentation, mobile behavior and branch badge; avatar loading binding; UI styling and accessibility refinements across components; sidebar width increased.
  • Bug Fixes

    • More precise fallback and conditional checks in charts/tooltips; improved interactive element accessibility.
  • Chores

    • Dependency updates and build-time branch name exposure.

ingoau and others added 30 commits August 8, 2025 13:29
Bumps [paneforge](https://github.com/svecosystem/paneforge/tree/HEAD/packages/paneforge) from 1.0.0-next.5 to 1.0.2.
- [Release notes](https://github.com/svecosystem/paneforge/releases)
- [Changelog](https://github.com/svecosystem/paneforge/blob/main/packages/paneforge/CHANGELOG.md)
- [Commits](https://github.com/svecosystem/paneforge/commits/paneforge@1.0.2/packages/paneforge)

---
updated-dependencies:
- dependency-name: paneforge
  dependency-version: 1.0.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [tailwind-variants](https://github.com/heroui-inc/tailwind-variants) from 1.0.0 to 2.1.0.
- [Release notes](https://github.com/heroui-inc/tailwind-variants/releases)
- [Changelog](https://github.com/heroui-inc/tailwind-variants/blob/main/CHANGELOG.md)
- [Commits](heroui-inc/tailwind-variants@v1.0.0...v2.1.0)

---
updated-dependencies:
- dependency-name: tailwind-variants
  dependency-version: 2.1.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Inglan <96573515+Inglan@users.noreply.github.com>
…of groupItem.url

Co-authored-by: Inglan <96573515+Inglan@users.noreply.github.com>
…33-4a02-b60b-4a2bd7551ac4

Fix sidebar sub-item active state to check individual URLs instead of parent group URL
…/tailwind-variants-2.1.0

Bump tailwind-variants from 1.0.0 to 2.1.0
…/paneforge-1.0.2

Bump paneforge from 1.0.0-next.5 to 1.0.2
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 10, 2025

Walkthrough

Adds a Stopwatch & Timer page, integrates tsparticles particle backgrounds, bumps multiple dependencies, exposes BRANCH_NAME at build time, and applies targeted UI, accessibility, and styling changes across sidebar, play, chart, tooltip, table, and various UI primitives.

Changes

Cohort / File(s) Change Summary
Dependency & Build
package.json, vite.config.ts
Multiple dependency bumps and swaps (adds tsparticles ecosystem, current-git-branch, updates Svelte/tooling); exposes process.env.BRANCH_NAME via Vite define.
New Feature: Stopwatch & Timer
src/routes/tools/stopwatch-timer/+page.svelte
New Svelte page implementing stopwatch and timer with start/pause/reset, laps, presets, progress, completion badge and beep via Web Audio API.
Particles Integration & Layout
src/routes/+layout.svelte, src/routes/+page.svelte
Adds @tsparticles/svelte and engine init; renders particle background beneath hero/content; updates Sidebar.Trigger styling.
Sidebar & Navigation
Files:
src/lib/components/app-sidebar.svelte, src/lib/components/sidebar-auth.svelte, src/lib/components/ui/sidebar/sidebar.svelte, src/lib/components/ui/sidebar/sidebar-rail.svelte, src/lib/navigation.ts, src/lib/components/ui/sidebar/constants.ts
Sidebar set to floating variant, Sheet→Drawer for mobile, group triggers call sidebar.setOpen(true), simplified active-path checks, mobile toggle conditional, branch badge added, SIDEBAR_WIDTH 16rem→18rem, added "Stopwatch & Timer" nav item.
Play Component UI
src/lib/components/play.svelte
Removed comments drawer; adjusted padding and iframe radius; added "Report Bug" button linking to GitHub with Bug icon.
Avatar & Button
src/lib/components/ui/avatar/avatar.svelte, src/lib/components/ui/button/button.svelte
Avatar: added bindable loadingStatus bound to Avatar root. Button: border radius changed rounded-mdrounded-lg.
Chart: style, tooltip, utils
src/lib/components/ui/chart/chart-style.svelte, src/lib/components/ui/chart/chart-tooltip.svelte, src/lib/components/ui/chart/chart-utils.ts
Replaced raw {@html} style injection with <svelte:element this="style">; switched fallback logic to nullish coalescing (??) and used explicit undefined checks.
Carousel & Accessibility
src/lib/components/ui/carousel/carousel-previous.svelte
Changed disabled attribute to aria-disabled so button remains interactive but exposes state to assistive tech.
Dropdown CSS vars & outline
src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte, src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
Renamed custom properties from --radix-...--bits-... and added outline-none to content classes.
Command Item Disabled Selector
src/lib/components/ui/command/command-item.svelte
Broadened disabled selector from data-[disabled=true]data-[disabled].
Table styling
src/lib/components/ui/table/table-cell.svelte, src/lib/components/ui/table/table-head.svelte, src/lib/components/ui/table/table-row.svelte
Added bg-clip-padding to cells/heads; extended hover background to child wrappers for consistent row hover.
Tooltip Arrow Transforms
src/lib/components/ui/tooltip/tooltip-content.svelte
Adjusted arrow translation directions for bottom and left placements.
Sonner CSS vars
src/lib/components/ui/sonner/sonner.svelte
Switched inline CSS variable references to --color-popover* and --color-border.
Minor styling/accessibility tweaks
Multiple UI files (avatar, dropdown, tooltip, table, sidebar-auth, sidebar-rail, etc.)
Small class and selector tweaks (e.g., list-none on Dialog.Trigger, bg-clip-padding, pseudo-element offset tweak, outline-none, aria improvements).
Routing / Game loader
src/routes/g/[id]/+page.ts
Added load({ url }) that extracts a game id from the pathname and calls getGameById, throws 404 if not found.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Router
    participant StopwatchPage
    participant UI
    participant WebAudio

    User->>Router: Navigate to /tools/stopwatch-timer
    Router->>StopwatchPage: mount page
    User->>StopwatchPage: Start / Pause / Reset / Lap
    StopwatchPage->>UI: update display, laps, progress
    StopwatchPage->>WebAudio: play beep on timer finish
Loading
sequenceDiagram
    participant Layout
    participant ParticlesEngine
    participant Content

    Layout->>ParticlesEngine: particlesInit(loadSlim) (module scope)
    ParticlesEngine-->>Layout: engine ready
    Layout->>ParticlesEngine: render Particles(options)
    Layout->>Content: layer UI above particles
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Suggested reviewers

  • nathblade16
✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch main

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ingoau ingoau requested a review from nathblade16 August 10, 2025 22:42
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: 6

🧹 Nitpick comments (14)
src/lib/components/ui/avatar/avatar.svelte (1)

7-7: Defaulting to 'loading' may be semantically off; consider 'idle' or leaving it uncontrolled

Most image/asset components start as idle and only become loading when a fetch starts. Defaulting to loading can misrepresent state on mount and affect fallbacks/UX.

Consider one of:

- loadingStatus = $bindable<'idle' | 'loading' | 'loaded' | 'error'>('loading'),
+ loadingStatus = $bindable<'idle' | 'loading' | 'loaded' | 'error'>('idle'),

or, to avoid forcing an initial state and let the primitive drive it:

- loadingStatus = $bindable<'idle' | 'loading' | 'loaded' | 'error'>('loading'),
+ loadingStatus = $bindable<'idle' | 'loading' | 'loaded' | 'error'>(),
src/lib/components/ui/button/button.svelte (1)

7-7: Border radius inconsistency across sizes (base vs. sm/lg).

Base now uses rounded-lg, but size variants sm and lg still include rounded-md, overriding the base. Decide on a single radius and keep it consistent.

Options:

  • Remove radius from size variants and rely on base.
  • Or change sm/lg to rounded-lg.

Example change outside this hunk:

- sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
- lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
+ sm: 'h-8 gap-1.5 px-3 has-[>svg]:px-2.5',
+ lg: 'h-10 px-6 has-[>svg]:px-4',

or

- sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
- lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
+ sm: 'h-8 gap-1.5 rounded-lg px-3 has-[>svg]:px-2.5',
+ lg: 'h-10 rounded-lg px-6 has-[>svg]:px-4',
src/lib/components/ui/sidebar/sidebar-rail.svelte (1)

25-25: Simplify calc for readability and reduce parsing pitfalls.

after:left-[calc(1/2*100%-1px)] is equivalent to after:left-[calc(50%-1px)] and is easier to parse/read.

- '... after:left-[calc(1/2*100%-1px)] ...'
+ '... after:left-[calc(50%-1px)] ...'
src/lib/components/play.svelte (1)

26-26: Icon import style inconsistency (optional).

Other icons import from @lucide/svelte/icons/... while this one imports from @lucide/svelte. Consider standardizing one approach across the file for consistency.

src/lib/components/ui/sidebar/sidebar.svelte (1)

75-75: Width calc tweak: document the +2px magic number (optional).

The added +2px likely compensates for borders/spacing in floating/inset variants. A short comment near this line would help future maintainers.

- ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'
+ ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]' /* extra 2px to account for border/rounding in floating/inset */
src/lib/components/ui/table/table-row.svelte (1)

17-17: Arbitrary variant with comma-separated selectors may not compile reliably in Tailwind JIT

The nested arbitrary variant uses comma-separated selectors twice. Tailwind's arbitrary variants generally don't support comma-separated lists; classes may be dropped at build time. This could cause the hover effect to silently fail.

Consider switching to a group-based approach, which is simpler and robust across wrappers.

Proposed change here (add group, simplify classes):

- class={cn(
-   'hover:[&,&>svelte-css-wrapper]:[&>th,td]:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors',
-   className
- )}
+ class={cn(
+   'group data-[state=selected]:bg-muted border-b transition-colors',
+   className
+ )}

And in table cell/head components (outside this file), ensure cells react to row hover:

- <td class="... bg-clip-padding ...">
+ <td class="... bg-clip-padding group-hover:bg-muted/50 transition-colors ...">

This applies the hover background to all cells, regardless of intermediate wrappers. Also verify that hover does not override the selected state visual (consider group-hover only when not selected if needed).

src/routes/+layout.svelte (1)

52-54: Consider a lighter particles bundle and defer initialization to reduce payload

loadFull pulls the entire tsParticles engine, which is heavy. If you’re only using basic shapes/links, loadSlim or targeted feature loaders can substantially cut bundle size. Also consider deferring init until the page that uses Particles is visible.

Options:

  • Use slim build:
- import { loadFull } from 'tsparticles';
+ import { loadSlim } from 'tsparticles-slim';

- void particlesInit(async (engine) => {
-   await loadFull(engine);
- });
+ void particlesInit(async (engine) => {
+   await loadSlim(engine);
+ });
  • Or lazy-init on client only:
onMount(async () => {
  const { loadSlim } = await import('tsparticles-slim');
  void particlesInit(async (engine) => {
    await loadSlim(engine);
  });
});

Please measure before/after with your bundler’s analyzer and check FCP on mobile.

src/lib/components/app-sidebar.svelte (2)

131-135: Opening the sidebar on any click within the collapsible item may be too broad

Attaching onclick to <Sidebar.MenuItem> will fire on any click in the item (including nested links), which might conflict with Collapsible.Trigger behavior and keyboard interactions.

Prefer scoping the open call to the trigger/button only:

- <Sidebar.MenuItem onclick={() => { sidebar.setOpen(true); }}>
+ <Sidebar.MenuItem>
    <Collapsible.Trigger onclick={() => { sidebar.setOpen(true); }}>
      ...
    </Collapsible.Trigger>
  </Sidebar.MenuItem>

Ensure this still passes a11y (Enter/Space toggles via the trigger).


140-141: Route active check is brittle for trailing slashes and search/hash

Using strict equality with page.url.pathname can fail for URLs with or without a trailing slash and ignores search/hash. Consider normalizing paths.

Example:

{@const normalize = (p: string) => p.replace(/\/+$/, '') || '/'}
<Sidebar.MenuButton
  isActive={normalize(groupItem.url) === normalize(page.url.pathname)}
  {...props}
/>

Apply similarly to sub-items.

src/lib/components/ui/chart/chart-tooltip.svelte (1)

144-148: Guard toLocaleString against non-number values (edge case)

If a dataset provides a non-number item.value, calling .toLocaleString() could be unexpected. Minor safeguard:

-{item.value.toLocaleString()}
+{typeof item.value === 'number' ? item.value.toLocaleString() : String(item.value)}
src/routes/+page.svelte (1)

186-201: Dead CSS: .hero-background keyframes never applied

You defined .hero-background animation but didn't apply that class. Either apply it to the Particles element or remove the CSS.

Apply:

- class="absolute top-0 right-0 bottom-0 left-0 pointer-events-none"
+ class="absolute top-0 right-0 bottom-0 left-0 pointer-events-none hero-background"

Or remove the style block if not needed.

src/routes/tools/stopwatch-timer/+page.svelte (3)

27-38: Displayed precision vs. implementation mismatch (centiseconds vs. milliseconds)

The comment says MM:SS.mmm, but you display centiseconds (two digits). Either:

  • show milliseconds (3 digits), or
  • update the comment/UI copy to reflect centiseconds.

To display milliseconds:

- const milliseconds = Math.floor((ms % 1000) / 10);
+ const milliseconds = Math.floor(ms % 1000);

- return `${minutes...}:${seconds...}.${milliseconds.toString().padStart(2, '0')}`;
+ return `${minutes...}:${seconds...}.${milliseconds.toString().padStart(3, '0')}`;

If you keep centiseconds (better for readability), clarify the comment and label (no code change required).


41-49: High-frequency setInterval re-renders: consider rAF for smoother updates

Updating state every 10ms can be CPU-intensive and janky on low-end devices. Using requestAnimationFrame keeps UI smooth while preserving accuracy by computing from startTime.

Example rAF loop:

let rafId: number | null = null;
function startStopwatch() {
  if (stopwatchRunning) return;
  stopwatchRunning = true;
  const startTime = Date.now() - stopwatchTime;
  const tick = () => {
    stopwatchTime = Date.now() - startTime;
    if (stopwatchRunning) rafId = requestAnimationFrame(tick);
  };
  rafId = requestAnimationFrame(tick);
}
function pauseStopwatch() {
  stopwatchRunning = false;
  if (rafId) cancelAnimationFrame(rafId);
  rafId = null;
}
onDestroy(() => { if (rafId) cancelAnimationFrame(rafId); });

309-321: Quick presets: ensure numeric assignment

If bind:value starts returning strings, ensure presets assign numbers. Current code likely works, but explicitly casting avoids surprises.

- timerMinutes = preset.minutes;
- timerSeconds = preset.seconds;
+ timerMinutes = Number(preset.minutes);
+ timerSeconds = Number(preset.seconds);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e1c19c5 and b6d56f0.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (25)
  • package.json (2 hunks)
  • src/lib/components/app-sidebar.svelte (5 hunks)
  • src/lib/components/play.svelte (4 hunks)
  • src/lib/components/sidebar-auth.svelte (1 hunks)
  • src/lib/components/ui/avatar/avatar.svelte (1 hunks)
  • src/lib/components/ui/button/button.svelte (1 hunks)
  • src/lib/components/ui/carousel/carousel-previous.svelte (1 hunks)
  • src/lib/components/ui/chart/chart-style.svelte (1 hunks)
  • src/lib/components/ui/chart/chart-tooltip.svelte (2 hunks)
  • src/lib/components/ui/chart/chart-utils.ts (1 hunks)
  • src/lib/components/ui/command/command-item.svelte (1 hunks)
  • src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte (1 hunks)
  • src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte (1 hunks)
  • src/lib/components/ui/sidebar/sidebar-rail.svelte (1 hunks)
  • src/lib/components/ui/sidebar/sidebar.svelte (3 hunks)
  • src/lib/components/ui/sonner/sonner.svelte (1 hunks)
  • src/lib/components/ui/table/table-cell.svelte (1 hunks)
  • src/lib/components/ui/table/table-head.svelte (1 hunks)
  • src/lib/components/ui/table/table-row.svelte (1 hunks)
  • src/lib/components/ui/tooltip/tooltip-content.svelte (1 hunks)
  • src/lib/navigation.ts (1 hunks)
  • src/routes/+layout.svelte (3 hunks)
  • src/routes/+page.svelte (2 hunks)
  • src/routes/g/[id]/+page.ts (1 hunks)
  • src/routes/tools/stopwatch-timer/+page.svelte (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.*

⚙️ CodeRabbit Configuration File

**/*.*: Do not correct spelling errors or grammar mistakes.

Files:

  • src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte
  • src/lib/components/ui/button/button.svelte
  • src/lib/components/ui/chart/chart-utils.ts
  • src/lib/components/ui/table/table-head.svelte
  • src/lib/navigation.ts
  • src/routes/+layout.svelte
  • src/lib/components/ui/table/table-row.svelte
  • src/lib/components/sidebar-auth.svelte
  • src/lib/components/ui/command/command-item.svelte
  • src/lib/components/ui/sidebar/sidebar-rail.svelte
  • src/lib/components/ui/sonner/sonner.svelte
  • src/routes/g/[id]/+page.ts
  • src/lib/components/ui/table/table-cell.svelte
  • src/lib/components/ui/chart/chart-style.svelte
  • src/lib/components/ui/carousel/carousel-previous.svelte
  • src/lib/components/ui/tooltip/tooltip-content.svelte
  • src/lib/components/ui/avatar/avatar.svelte
  • src/lib/components/app-sidebar.svelte
  • package.json
  • src/lib/components/ui/sidebar/sidebar.svelte
  • src/lib/components/ui/chart/chart-tooltip.svelte
  • src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte
  • src/routes/+page.svelte
  • src/lib/components/play.svelte
  • src/routes/tools/stopwatch-timer/+page.svelte
🧬 Code Graph Analysis (1)
src/routes/g/[id]/+page.ts (1)
src/lib/gmaes.ts (1)
  • getGameById (391-393)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (26)
src/lib/components/ui/avatar/avatar.svelte (1)

13-19: AvatarPrimitive.Root supports bind:loadingStatus – no change needed

According to the bits-ui docs, AvatarPrimitive.Root (aka Avatar.Root) exposes a bindable loadingStatus prop (and an onLoadingStatusChange callback). Binding loadingStatus on the root is correct; you don’t need to move it to AvatarPrimitive.Image.

Likely an incorrect or invalid review comment.

src/lib/components/ui/command/command-item.svelte (1)

16-16: Broader, more robust disabled selector.

Switching to data-[disabled] improves compatibility with presence-only attributes and keeps disabled styling consistent.

src/lib/components/ui/sonner/sonner.svelte (1)

11-11: Confirmed global definitions for new CSS variables—no immediate changes required.

We found all three variables defined in src/app.css:

  • Lines 569–570: --color-popover, --color-popover-foreground
  • Line 580: --color-border

These cover the defaults used in sonner.svelte. If you introduce additional themes or override app.css in the future, consider adding fallbacks then; for now, this is safe to merge.

src/lib/components/ui/chart/chart-style.svelte (1)

33-35: Safer style injection (no raw HTML).

Replacing {@html} with <svelte:element this="style"> avoids raw HTML, keeping the same behavior while improving safety.

package.json (1)

18-21: @lucide/svelte imports verified as client-only
All occurrences of @lucide/svelte are in .svelte or other client modules—no imports in +*.server.ts or src/lib/server/**.

– ✅ No changes needed for @lucide/svelte in devDependencies
⚠️ Please confirm that layerchart@2.0.0-next.27 works with your SvelteKit v2.27.3 setup and any APIs you rely on.

src/lib/components/play.svelte (2)

69-71: LGTM: Layout/style adjustments.

Padding and rounded-lg updates look consistent with the rest of the UI changes.


97-97: LGTM: Grid height fit.

Adding h-fit to the control grid is appropriate to prevent unnecessary vertical stretching.

src/lib/components/ui/sidebar/sidebar.svelte (5)

2-2: LGTM: Drawer import.

Import switch to Drawer aligns with the new mobile sidebar behavior.


37-40: Verify Svelte function binding syntax for bind:open.

bind:open={() => sidebar.openMobile, (v) => sidebar.setOpenMobile(v)} uses function-based binding. Ensure your Svelte version/tooling supports this pattern.

If needed, consider a fallback that separates get/set:

<!-- If Drawer supports `open` prop and `openChange` event: -->
<Drawer.Root
  open={sidebar.openMobile}
  on:openChange={(e) => sidebar.setOpenMobile(e.detail)}
  direction={side}
/>

Or keep the function-binding if confirmed supported by your setup.


41-47: Mobile content container: check CSS variables availability.

The inline style uses --sidebar-width and --initial-transform. Confirm both vars are respected by your CSS/runtime and the slide-in/out animation honors --initial-transform.


48-52: LGTM: A11y-friendly Drawer header.

The sr-only header/title/description provide accessible labels without visual clutter.


55-56: LGTM: Drawer structure.

Content/root closure sequence is correct.

src/lib/components/ui/table/table-head.svelte (1)

17-17: LGTM: Visual consistency.

Adding bg-clip-padding matches cell styling and prevents bleed on rounded/hover backgrounds.

src/lib/components/ui/chart/chart-utils.ts (1)

44-44: LGTM: Safer check for nested payload.

payloadPayload !== undefined narrows types correctly and avoids false negatives for falsy-but-valid objects.

src/lib/navigation.ts (1)

64-68: ✅ Route verification passed for /tools/stopwatch-timer.
Found src/routes/tools/stopwatch-timer/+page.svelte.
LGTM—approving these changes.

src/lib/components/sidebar-auth.svelte (1)

101-102: LGTM: Styling refinement.

list-none on the trigger removes unwanted list markers for stacked menu items.

src/lib/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte (1)

16-16: CSS variable rename verified – no old references found

I searched the entire codebase for --radix-dropdown-menu-content-transform-origin and found zero occurrences. All dropdown components now consistently use --bits-dropdown-menu-content-transform-origin, so this change is safe to merge.

src/lib/components/ui/table/table-cell.svelte (1)

16-19: LGTM: Table cell clipping matches header.

Helps maintain consistent background clipping with rounded/hovered rows.

src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte (1)

22-22: Add an accessible focus‐visible indicator to DropdownMenuContent

The outline-none on line 22 removes the native focus ring, which can impede keyboard users. Introduce a visible focus-visible ring to match the rest of our UI components:

• File: src/lib/components/ui/dropdown-menu/dropdown-menu-content.svelte
Line 22

-   '… overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md outline-none',
+   '… overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md outline-none \
+    focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring \
+    focus-visible:ring-offset-2 focus-visible:ring-offset-background',

• Please verify that the CSS variables
--bits-dropdown-menu-content-available-height and
--bits-dropdown-menu-content-transform-origin
are included in your installed bits-ui version to prevent any regressions.

src/lib/components/ui/tooltip/tooltip-content.svelte (1)

37-40: Arrow offset direction changes: verify alignment on all sides and at different scales

The bottom arrow switched to -translate-x-1/2 and the left arrow to negative translate-y. Visually this seems correct, but these offsets can be sensitive to zoom, density, and font scaling.

Please verify:

  • All sides (top/right/bottom/left) align the arrow tip precisely at the content edge.
  • No clipping occurs with rounded-[2px] at high zoom.
  • RTL locales still position as intended (if applicable).
src/routes/+layout.svelte (1)

77-82: Sidebar trigger: LGTM

The fixed trigger with button variants and responsive hide (md:hidden) is a clean improvement for mobile UX.

src/lib/components/app-sidebar.svelte (2)

70-70: New variant="floating": verify support in Sidebar.Root

If variant is a constrained prop, ensure "floating" is supported in the underlying component/theme; otherwise this could fall back to defaults or break styles.


275-293: Hide toggle on mobile: LGTM

Conditionally hiding the sidebar toggle on mobile avoids redundant controls now that mobile uses a Drawer. Event capture with PostHog is a nice touch.

src/lib/components/ui/chart/chart-tooltip.svelte (1)

57-67: Nullish coalescing and explicit undefined checks: LGTM

Replacing || with ?? and checking value === undefined prevents dropping valid falsy values like 0 or empty strings. This is the correct behavior for tooltips.

src/routes/+page.svelte (1)

26-104: tsparticles options: validate keys against the installed version

Some options (e.g., number.limit, interactivity.detectsOn, links.frequency) can change between tsParticles versions. Mismatched keys are silently ignored.

Please confirm with the docs for your exact version that:

  • interactivity.detectsOn is valid (v3 changed some interactivity props),
  • number.limit is supported for your use-case,
  • links.frequency exists.

If not, adjust to the current option names to avoid confusion.

src/routes/tools/stopwatch-timer/+page.svelte (1)

151-156: Interval cleanup: LGTM

Intervals are cleared on destroy; good resource hygiene.

@nathblade16
Copy link
Contributor

I should probably do my school work first but will make sure to review this

ingoau and others added 2 commits August 11, 2025 09:54
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Aug 11, 2025

Deploying edutools-testing with  Cloudflare Pages  Cloudflare Pages

Latest commit: 08fcfc0
Status: ✅  Deploy successful!
Preview URL: https://7365c291.edutools-testing.pages.dev
Branch Preview URL: https://main.edutools-testing.pages.dev

View logs

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: 6

🧹 Nitpick comments (5)
vite.config.ts (1)

12-15: Environment exposure consistency

You’re defining process.env.* keys for client usage. Consider standardizing on one approach repo-wide (e.g., Vite define vs SvelteKit $env/static/public). Mixed patterns can confuse contributors. No action required if this is intentional and documented.

src/lib/components/app-sidebar.svelte (4)

133-137: MenuItem-level onclick to setOpen(true) — consider scoping to the trigger only

Setting sidebar.open on the entire MenuItem is fine but may fire on clicks inside the collapsible content too (bubbling). Scoping the handler to Collapsible.Trigger reduces incidental activation.

-<Sidebar.MenuItem
-  onclick={() => {
-    sidebar.setOpen(true);
-  }}
->
+<Sidebar.MenuItem>
+  <Collapsible.Trigger
+    onclick={() => {
+      sidebar.setOpen(true);
+    }}
+  >
     ...
-  </Collapsible.Trigger>
+  </Collapsible.Trigger>

174-175: Sub-item active state may need to tolerate nested paths

If sub-items have deeper children (e.g., tabs or nested routes), strict equality won’t highlight. If that’s expected, ignore; if not, mirror the group approach with startsWith.

-<Sidebar.MenuSubButton isActive={item.url === page.url.pathname}>
+<Sidebar.MenuSubButton
+  isActive={page.url.pathname === item.url || page.url.pathname.startsWith(`${item.url}/`)}>

262-262: Avoid empty MenuItem as a spacer

Using an empty interactive item for spacing hurts semantics and a11y. Prefer margin utilities or a non-interactive spacer.

-<Sidebar.MenuItem></Sidebar.MenuItem>
+<!-- spacer -->
+<li aria-hidden="true" class="h-2"></li>

Alternatively, add mt-2 to the following MenuItem.


283-301: Desktop toggle: minor a11y enhancement

Consider reflecting state via aria-pressed and a descriptive aria-label.

-<Sidebar.MenuButton
+<Sidebar.MenuButton
+  aria-pressed={sidebar.open}
+  aria-label={sidebar.open ? 'Collapse sidebar' : 'Expand sidebar'}
   onclick={() => {
     posthog.capture('sidebar_toggle', { state: sidebar.open });
     sidebar.toggle();
   }}
>
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c498082 and 8a1d431.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • package.json (2 hunks)
  • src/lib/components/app-sidebar.svelte (7 hunks)
  • src/lib/components/ui/sidebar/constants.ts (1 hunks)
  • vite.config.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.*

⚙️ CodeRabbit Configuration File

**/*.*: Do not correct spelling errors or grammar mistakes.

Files:

  • vite.config.ts
  • src/lib/components/ui/sidebar/constants.ts
  • src/lib/components/app-sidebar.svelte
  • package.json
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build
  • GitHub Check: Cloudflare Pages
🔇 Additional comments (2)
src/lib/components/ui/sidebar/constants.ts (1)

3-5: Width increase to 18rem: verify downstream layout math and responsive behavior

The bump to 18rem looks fine. Please double-check:

  • Any calc() that assumed the previous width (e.g., desktop gap calculations) still align.
  • Mobile width equals desktop width (both 18rem). If intentional, great; otherwise consider a narrower SIDEBAR_WIDTH_MOBILE.
src/lib/components/app-sidebar.svelte (1)

72-72: Floating variant adoption looks good

The switch to variant="floating" aligns with the widened SIDEBAR_WIDTH. No issues spotted here.

Comment on lines +18 to +21
"@lucide/svelte": "^0.515.0",
"@sveltejs/adapter-auto": "^6.0.2",
"@sveltejs/adapter-static": "^3.0.9",
"@sveltejs/kit": "^2.27.3",
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Move @lucide/svelte to dependencies (used at runtime/SSR), not devDependencies

@lucide/svelte is imported in app code (e.g., src/lib/components/app-sidebar.svelte). Keeping it in devDependencies can break production installs that only install dependencies (SSR/node adapters). Recommend moving it to dependencies.

-  "devDependencies": {
-    "@lucide/svelte": "^0.515.0",
+  "devDependencies": {
     ...
   },
   "dependencies": {
+    "@lucide/svelte": "^0.515.0",
     ...
   }

Optionally align the version with what worked previously (you removed ^0.525.0). If 0.515.0 is intentional due to compatibility, keep it; otherwise bump.

Verification script (finds usages and shows where the package is declared):

Also applies to: 51-68


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Usages of @lucide/svelte:"
rg -n "@lucide/svelte" -A 2 -B 2 || true
echo
echo "Declared in dependencies:"
jq -r '.dependencies["@lucide/svelte"] // empty' package.json
echo "Declared in devDependencies:"
jq -r '.devDependencies["@lucide/svelte"] // empty' package.json

Length of output: 32026


Move @lucide/svelte to dependencies
It’s imported in production/SSR code (e.g. many src/routes//.svelte and src/lib/.svelte/.ts files), but currently only in devDependencies. This will break installs that skip devDeps.

Locations using @lucide/svelte at runtime:

  • src/routes/**/+page.svelte (multiple icons)
  • src/lib/components/app-sidebar.svelte (GitBranch, ChevronRight, etc.)
  • src/lib/navigation.ts (Home, Wrench, Game, etc.)
    …among many others.

Suggested change in package.json:

   "devDependencies": {
-    "@lucide/svelte": "^0.515.0",
     …
   },
   "dependencies": {
+    "@lucide/svelte": "^0.515.0",
     …
   }

Keep the ^0.515.0 version to match your lockfile or bump if you need the latest.

📝 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
"@lucide/svelte": "^0.515.0",
"@sveltejs/adapter-auto": "^6.0.2",
"@sveltejs/adapter-static": "^3.0.9",
"@sveltejs/kit": "^2.27.3",
"devDependencies": {
- "@lucide/svelte": "^0.515.0",
"@sveltejs/adapter-auto": "^6.0.2",
"@sveltejs/adapter-static": "^3.0.9",
"@sveltejs/kit": "^2.27.3",
},
"dependencies": {
+ "@lucide/svelte": "^0.515.0",
// …other dependencies
}
🤖 Prompt for AI Agents
In package.json around lines 18 to 21, @lucide/svelte is listed under
devDependencies but is imported in runtime/SSR code; move it into the
"dependencies" section keeping the version ^0.515.0 (remove it from
devDependencies and add it to dependencies), then update the lockfile by running
the package manager install (e.g., npm install or npm install
@lucide/svelte@^0.515.0) so production installs include the package.

Comment on lines 51 to 68
"@clerk/backend": "^2.7.0",
"@clerk/themes": "^2.4.6",
"@friendofsvelte/tipex": "0.0.7",
"@lucide/svelte": "^0.525.0",
"@tanstack/match-sorter-utils": "^8.19.4",
"@tsparticles/svelte": "^3.1.1",
"convex": "^1.25.4",
"convex-svelte": "^0.0.11",
"current-git-branch": "^2.0.1",
"dayjs": "^1.11.13",
"formsnap": "^2.0.1",
"jose": "^6.0.12",
"jsonwebtoken": "^9.0.2",
"md5": "^2.3.0",
"posthog-js": "^1.257.0",
"svelte-clerk": "^0.16.0",
"svelte-persisted-store": "^0.12.0"
"posthog-js": "^1.259.0",
"svelte-clerk": "^0.16.5",
"svelte-persisted-store": "^0.12.0",
"tsparticles": "^3.9.1"
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

current-git-branch belongs in devDependencies (build-time only)

It’s only used in vite.config.ts (build-time). Move to devDependencies to avoid shipping it in production images/lambdas.

-  "dependencies": {
-    ...
-    "current-git-branch": "^2.0.1",
-    ...
-  }
+  "dependencies": {
+    ...
+  },
+  "devDependencies": {
+    ...
+    "current-git-branch": "^2.0.1",
+    ...
+  }

Verification script:

Also applies to: 16-49


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Where current-git-branch is used:"
rg -n "current-git-branch" -A 2 -B 2 || true

Length of output: 1463


Move current-git-branch into devDependencies

current-git-branch is only imported in vite.config.ts (build-time), so it shouldn’t be shipped in production images or lambdas.

• Usage location
vite.config.ts line 4: import branchName from 'current-git-branch';
• Change in package.json
– Relocate the "current-git-branch": "^2.0.1" entry from dependencies into devDependencies

-  "dependencies": {
-    ...
-    "current-git-branch": "^2.0.1",
-    ...
-  }
+  "dependencies": {
+    ...
+  },
+  "devDependencies": {
+    ...
+    "current-git-branch": "^2.0.1",
+    ...
+  }
📝 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
"@clerk/backend": "^2.7.0",
"@clerk/themes": "^2.4.6",
"@friendofsvelte/tipex": "0.0.7",
"@lucide/svelte": "^0.525.0",
"@tanstack/match-sorter-utils": "^8.19.4",
"@tsparticles/svelte": "^3.1.1",
"convex": "^1.25.4",
"convex-svelte": "^0.0.11",
"current-git-branch": "^2.0.1",
"dayjs": "^1.11.13",
"formsnap": "^2.0.1",
"jose": "^6.0.12",
"jsonwebtoken": "^9.0.2",
"md5": "^2.3.0",
"posthog-js": "^1.257.0",
"svelte-clerk": "^0.16.0",
"svelte-persisted-store": "^0.12.0"
"posthog-js": "^1.259.0",
"svelte-clerk": "^0.16.5",
"svelte-persisted-store": "^0.12.0",
"tsparticles": "^3.9.1"
}
"dependencies": {
"@clerk/backend": "^2.7.0",
"@clerk/themes": "^2.4.6",
"@friendofsvelte/tipex": "0.0.7",
"@tanstack/match-sorter-utils": "^8.19.4",
"@tsparticles/svelte": "^3.1.1",
"convex": "^1.25.4",
"convex-svelte": "^0.0.11",
"dayjs": "^1.11.13",
"formsnap": "^2.0.1",
"jose": "^6.0.12",
"jsonwebtoken": "^9.0.2",
"md5": "^2.3.0",
"posthog-js": "^1.259.0",
"svelte-clerk": "^0.16.5",
"svelte-persisted-store": "^0.12.0",
"tsparticles": "^3.9.1"
},
"devDependencies": {
"current-git-branch": "^2.0.1"
}
🤖 Prompt for AI Agents
In package.json around lines 51 to 68, the "current-git-branch": "^2.0.1" entry
is currently listed under dependencies but is only used at build time in
vite.config.ts; remove the "current-git-branch" entry from the dependencies
object and add the same entry under devDependencies, then run your package
manager to update node_modules and the lockfile (npm install or yarn install) so
the lockfile reflects the move; ensure no other runtime code imports it and
commit the updated package.json and lockfile.

Comment on lines +35 to +36
import { GitBranch } from '@lucide/svelte';
import Badge from './ui/badge/badge.svelte';
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Confirm @lucide/svelte is a runtime dependency

This import implies a runtime/SSR dependency. Ensure @lucide/svelte is listed under dependencies (not only devDependencies) to avoid production-only installs failing.

See package.json comment for a proposed diff and a verification script.


🏁 Script executed:

#!/bin/bash
# Check if @lucide/svelte is listed under dependencies or devDependencies
grep -R -n '"@lucide/svelte"' -C 5 package.json || echo "@lucide/svelte not found in package.json"

Length of output: 436


Move @lucide/svelte to runtime dependencies

The icon import in src/lib/components/app-sidebar.svelte runs at runtime/SSR, so @lucide/svelte must be under dependencies, not just devDependencies.

• File to update: package.json
• Current location: line 18, under devDependencies
• Action: remove it from devDependencies and add it to dependencies

Suggested diff:

--- package.json
@@
-  "devDependencies": {
-    "@lucide/svelte": "^0.515.0",
+  "devDependencies": {
     ...other dev deps...
  },
+  "dependencies": {
+    ...existing deps...,
+    "@lucide/svelte": "^0.515.0"
+  }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/lib/components/app-sidebar.svelte around lines 35 to 36 the GitBranch
icon is imported from @lucide/svelte and thus that package is required at
runtime/SSR; update package.json (currently listing @lucide/svelte at line 18
under devDependencies) by removing the entry from devDependencies and adding the
same package/version under dependencies so it is installed for production/SSR
builds.

Comment on lines +142 to 143
isActive={groupItem.url === page.url.pathname}
{...props}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Active state for groups should account for nested routes

Exact equality can fail to highlight a group when on a sub-route (e.g., pathname /tools/stopwatch-timer while group url is /tools). Recommend prefix-based match.

-<Sidebar.MenuButton
-  class="cursor-pointer"
-  isActive={groupItem.url === page.url.pathname}
+<Sidebar.MenuButton
+  class="cursor-pointer"
+  isActive={page.url.pathname === groupItem.url || page.url.pathname.startsWith(`${groupItem.url}/`)}
📝 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
isActive={groupItem.url === page.url.pathname}
{...props}
<Sidebar.MenuButton
class="cursor-pointer"
isActive={page.url.pathname === groupItem.url || page.url.pathname.startsWith(`${groupItem.url}/`)}
{...props}
🤖 Prompt for AI Agents
In src/lib/components/app-sidebar.svelte around lines 142-143, the active state
currently uses exact equality (groupItem.url === page.url.pathname) which misses
nested routes; change the check to a prefix match using
page.url.pathname.startsWith(groupItem.url) and normalize trailing slashes
(e.g., ensure groupItem.url has no trailing slash or compare with a trailing
slash variant) so a group like "/tools" is considered active for
"/tools/stopwatch-timer"; keep the rest of the props spread as-is.

Comment on lines +253 to +257
<div class="grow"></div>
<Badge>
<GitBranch />
{process.env.BRANCH_NAME}</Badge
>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Make BRANCH_NAME badge resilient and avoid “undefined/null” in UI

If resolve fails in CI/detached HEAD, the UI may show “undefined” or “null”. Use a safe fallback in both Vite config and here.

  1. With the Vite config change suggested earlier, BRANCH_NAME will always be a string. Still, guard in the template:
-<Badge>
-  <GitBranch />
-  {process.env.BRANCH_NAME}
-</Badge>
+<Badge>
+  <GitBranch />
+  {process.env.BRANCH_NAME ?? 'detached'}
+</Badge>
  1. Optionally show a short SHA instead if you add it later; otherwise “detached” is a reasonable default.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/lib/components/app-sidebar.svelte around lines 253 to 257, the
BRANCH_NAME badge can render "undefined"/"null" when CI/detached HEAD resolution
fails; update the template to guard the value and render a safe fallback (e.g.,
process.env.BRANCH_NAME || 'detached') and, if you later include a short commit
SHA env, prefer showing that (e.g., shortSHA || branchName) — ensure the
expression always yields a non-empty string so the Badge never displays
undefined/null.

import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig, loadEnv } from 'vite';
import tailwindcss from '@tailwindcss/vite';
import branchName from 'current-git-branch';
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Harden BRANCH_NAME resolution for CI/detached HEAD/no-.git environments

current-git-branch can return undefined or throw when HEAD is detached or .git isn’t available (common in CI/CD). Add a safe resolver with environment fallbacks to avoid build-time failures and “undefined” in the UI.

-import branchName from 'current-git-branch';
+import branchName from 'current-git-branch';

+function resolveBranchName(): string {
+  // Prefer CI-provided refs if present
+  const envBranch =
+    process.env.BRANCH_NAME ||
+    process.env.GITHUB_HEAD_REF ||
+    process.env.GITHUB_REF_NAME ||
+    process.env.VERCEL_GIT_COMMIT_REF ||
+    process.env.NETLIFY_BRANCH ||
+    process.env.CF_PAGES_BRANCH;
+  try {
+    return envBranch || branchName() || 'detached';
+  } catch {
+    return envBranch || 'detached';
+  }
+}
     define: {
       'process.env.PUBLIC_CONVEX_URL': JSON.stringify(env.PUBLIC_CONVEX_URL),
-      'process.env.PUBLIC_CLERK_PUBLISHABLE_KEY': JSON.stringify(env.PUBLIC_CLERK_PUBLISHABLE_KEY),
-      'process.env.BRANCH_NAME': JSON.stringify(branchName())
+      'process.env.PUBLIC_CLERK_PUBLISHABLE_KEY': JSON.stringify(env.PUBLIC_CLERK_PUBLISHABLE_KEY),
+      'process.env.BRANCH_NAME': JSON.stringify(resolveBranchName()),
     }

Also applies to: 14-15

🤖 Prompt for AI Agents
In vite.config.ts around lines 4 and 14-15, resolution of BRANCH_NAME using
current-git-branch is unsafe for CI/detached HEAD/no-.git setups and can return
undefined or throw; replace that direct usage with a safe resolver: call
current-git-branch inside a try/catch, fall back to env vars like
CI_COMMIT_REF_NAME, GITHUB_REF_NAME, GITHUB_SHA (or process.env.BRANCH_NAME),
and finally a default like "unknown-branch"; ensure the resolved value is
normalized (trimmed) and used instead of the raw call so builds won’t fail and
the UI won’t show "undefined".

Copy link
Contributor

@nathblade16 nathblade16 left a comment

Choose a reason for hiding this comment

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

code seems fine and beta seems fine

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: 3

♻️ Duplicate comments (2)
src/lib/components/play.svelte (1)

174-182: Reverse tabnabbing mitigated — nice fix

target="_blank" correctly paired with rel="noopener noreferrer".

src/routes/+page.svelte (1)

26-29: Background particles should not intercept input and can be hidden from a11y

Add pointer-events-none and aria-hidden so the decorative canvas doesn’t capture input or get announced by screen readers.

-    <Particles
+    <Particles
       id="hero-background"
-      class="absolute top-0 right-0 bottom-0 left-0"
+      class="absolute top-0 right-0 bottom-0 left-0 pointer-events-none"
+      aria-hidden="true"
       options={{
         ...
       }}
     />
🧹 Nitpick comments (3)
src/lib/components/play.svelte (2)

97-97: Avoid potential mobile overflow from min-w-72

On very small viewports, min-w-72 (18rem) can trigger horizontal scrolling. Make it responsive.

-    <div class="grid h-fit min-w-72 grid-cols-2 gap-3">
+    <div class="grid h-fit min-w-full md:min-w-72 grid-cols-2 gap-3">

180-180: A11y: mark decorative icon as hidden from screen readers

Prevent duplicate announcements by hiding the icon; the visible text already conveys meaning.

-        <Bug />
+        <Bug aria-hidden="true" />
src/routes/+page.svelte (1)

108-110: Make the overlay non-interactive

Prevent the overlay from accidentally blocking interactions on elements layered beneath.

-  class="to-background absolute top-0 right-0 bottom-0 left-0 bg-gradient-to-b from-transparent"
+  class="to-background pointer-events-none absolute top-0 right-0 bottom-0 left-0 bg-gradient-to-b from-transparent"
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8a1d431 and 08fcfc0.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • package.json (2 hunks)
  • src/lib/components/play.svelte (4 hunks)
  • src/routes/+layout.svelte (3 hunks)
  • src/routes/+page.svelte (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
🧰 Additional context used
📓 Path-based instructions (1)
**/*.*

⚙️ CodeRabbit Configuration File

**/*.*: Do not correct spelling errors or grammar mistakes.

Files:

  • src/routes/+layout.svelte
  • src/lib/components/play.svelte
  • src/routes/+page.svelte
🔇 Additional comments (6)
src/lib/components/play.svelte (1)

19-19: Bug icon import — LGTM

Import is correct and aligns with the new button usage.

src/routes/+layout.svelte (1)

27-29: tsparticles dependencies are correctly declared in package.json

Both runtime packages you’re importing—

  • @tsparticles/slim @ ^3.9.1
  • @tsparticles/svelte @ ^3.1.1

are listed under the dependencies section of package.json. No further changes are needed.

src/routes/+page.svelte (4)

9-9: Particles import looks good for the new hero background

Matches the tsparticles integration used elsewhere. No issues here.


17-19: New tools entries added

Entries for Random Number Generator and Stopwatch & Timer look correct and consistent with the existing structure.


113-118: Responsive grid change looks correct

The conditional grid-cols behavior aligned with experimentalFeatures is clear and matches the layout intent.


5-5: No change needed: ‘$lib/gmaes’ is the correct module path
The file src/lib/gmaes.ts exists (no games.ts), and imports of $lib/gmaes are used throughout the codebase. Changing to $lib/games would break references.

Likely an incorrect or invalid review comment.

Comment on lines +69 to +71
<div class="flex h-full w-full flex-col gap-3 p-2 md:pl-0">
<iframe src={gmaedata?.url} frameborder="0" class="grow rounded-lg" title={gmaedata?.name}
></iframe>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Modernize iframe markup: drop obsolete frameborder and enable lazy loading

frameborder is obsolete in HTML. Prefer CSS class border-0 and add loading="lazy" for perf.

-  <iframe src={gmaedata?.url} frameborder="0" class="grow rounded-lg" title={gmaedata?.name}
+  <iframe src={gmaedata?.url} loading="lazy" class="grow rounded-lg border-0" title={gmaedata?.name}
   ></iframe>
📝 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
<div class="flex h-full w-full flex-col gap-3 p-2 md:pl-0">
<iframe src={gmaedata?.url} frameborder="0" class="grow rounded-lg" title={gmaedata?.name}
></iframe>
<div class="flex h-full w-full flex-col gap-3 p-2 md:pl-0">
<iframe
src={gmaedata?.url}
loading="lazy"
class="grow rounded-lg border-0"
title={gmaedata?.name}
></iframe>
🤖 Prompt for AI Agents
In src/lib/components/play.svelte around lines 69 to 71, the iframe uses the
obsolete frameborder attribute and lacks lazy loading; replace frameborder="0"
by applying a CSS class (e.g., add or ensure 'border-0' in the class list) and
add loading="lazy" to the iframe element to defer offscreen loads for better
performance, keeping the existing title and responsive classes intact.

Comment on lines +52 to +54
void particlesInit(async (engine) => {
await loadSlim(engine);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Guard particles initialization for SSR safety

Initialize the engine only in the browser to avoid possible SSR issues if a dependency touches window or document.

-void particlesInit(async (engine) => {
-  await loadSlim(engine);
-});
+if (typeof window !== 'undefined') {
+  void particlesInit(async (engine) => {
+    await loadSlim(engine);
+  });
+}

🏁 Script executed:

#!/bin/bash
# Show the context where particlesInit is called
rg 'particlesInit' -C5 src/routes/+layout.svelte

# Find how loadSlim (and particlesInit) are imported
rg '^import .*loadSlim' -n src/routes/+layout.svelte

Length of output: 761


Guard client-only initialization to prevent SSR errors

Use SvelteKit’s browser flag from $app/environment to ensure particlesInit (and its loadSlim call) only runs in the browser:

-import { loadSlim } from '@tsparticles/slim';
-import { particlesInit } from '@tsparticles/svelte';
+import { loadSlim } from '@tsparticles/slim';
+import { particlesInit } from '@tsparticles/svelte';
+import { browser } from '$app/environment';

-void particlesInit(async (engine) => {
-  await loadSlim(engine);
-});
+if (browser) {
+  void particlesInit(async (engine) => {
+    await loadSlim(engine);
+  });
+}

• File: src/routes/+layout.svelte
• Lines: 52–54

This change prevents SSR from executing code that may touch window or document, avoiding runtime errors.

📝 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
void particlesInit(async (engine) => {
await loadSlim(engine);
});
import { loadSlim } from '@tsparticles/slim';
import { particlesInit } from '@tsparticles/svelte';
import { browser } from '$app/environment';
if (browser) {
void particlesInit(async (engine) => {
await loadSlim(engine);
});
}
🤖 Prompt for AI Agents
In src/routes/+layout.svelte around lines 52 to 54, the particlesInit call is
running during SSR and can access browser-only globals; import the `browser`
flag from `$app/environment` and wrap the particlesInit (and its await loadSlim
call) inside a browser check (or run it inside onMount) so it only executes in
the client; update imports accordingly and ensure the async init is only invoked
when `browser` is true.

Comment on lines +77 to +82
<Sidebar.Trigger
class={clsx(
'fixed bottom-3 left-3 z-50 !rounded-lg md:hidden',
buttonVariants({ variant: 'default', size: 'icon' })
)}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add an aria-label to the icon-only sidebar trigger

Icon-only controls should advertise their purpose to assistive tech.

-<Sidebar.Trigger
+<Sidebar.Trigger
+  aria-label="Open sidebar"
   class={clsx(
     'fixed bottom-3 left-3 z-50 !rounded-lg md:hidden',
     buttonVariants({ variant: 'default', size: 'icon' })
   )}
 />
📝 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
<Sidebar.Trigger
class={clsx(
'fixed bottom-3 left-3 z-50 !rounded-lg md:hidden',
buttonVariants({ variant: 'default', size: 'icon' })
)}
/>
<Sidebar.Trigger
aria-label="Open sidebar"
class={clsx(
'fixed bottom-3 left-3 z-50 !rounded-lg md:hidden',
buttonVariants({ variant: 'default', size: 'icon' })
)}
/>
🤖 Prompt for AI Agents
In src/routes/+layout.svelte around lines 77 to 82, the Sidebar.Trigger is an
icon-only control and lacks an accessible name; add an aria-label (or
aria-labelledby) to the Sidebar.Trigger element that clearly describes its
action (e.g., "Open sidebar" or "Toggle sidebar") so assistive technologies can
announce its purpose, keeping the label concise and matching the control's
behavior.

@ingoau ingoau merged commit d71a417 into prod Aug 11, 2025
6 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Aug 12, 2025
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.

3 participants