Skip to content

UI Overhaul + Code Optimization + Security Secrets - #26

Merged
woopxwoop merged 33 commits into
mainfrom
optimization
Jul 23, 2026
Merged

UI Overhaul + Code Optimization + Security Secrets#26
woopxwoop merged 33 commits into
mainfrom
optimization

Conversation

@woopxwoop

@woopxwoop woopxwoop commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features
    • Added new character browsing with search, filter chips, sorting, and “NEW” badges, plus interactive flip-card navigation.
    • Introduced reusable UI components (sliding tabs, segmented controls, toggles, badges/chips, tooltips, loading/empty states) used across the app.
    • Refreshed Abyss, Stygian, Teams, Pulls, and Settings with cleaner solution-board/meta-team layouts.
  • Accessibility & Style
    • Updated typography/theme tokens, improved font loading, stronger focus-visible rings, and expanded reduced-motion behavior.
  • Performance
    • Improved caching and lazy loading so data is fetched only when needed.
  • Bug Fixes
    • Improved session behavior and reduced unnecessary auth/session and team requests.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@woopxwoop, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 42 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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.

Review details
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 72e7d5a7-0678-45a6-8e7c-6cd411f017fd

📥 Commits

Reviewing files that changed from the base of the PR and between b160e01 and 0787f60.

📒 Files selected for processing (4)
  • src/hooks.client.ts
  • src/instrumentation.server.ts
  • tests/debug.ts
  • tests/global-setup.ts
📝 Walkthrough

Walkthrough

This PR establishes shared design tokens and UI components, adds lazy server clients and Valkey-backed caching, reduces hydration payloads, consolidates settings navigation, and refactors character, recommendation, team, and build-detail pages with expanded Playwright coverage.

Changes

Application overhaul

Layer / File(s) Summary
Design system and shared foundations
src/app.css, src/lib/data/*, src/lib/*, src/lib/ui/components/*
Adds typography and surface tokens, character/build data helpers, filtering utilities, element colors, shared controls, cards, tooltips, and accessibility and motion behavior.
Server infrastructure and caching
src/lib/server/*, src/hooks.server.ts, src/routes/api/*
Adds lazy authentication and database clients, optional Valkey caching with singleflight misses, cookie-aware session lookup, cached team configuration retrieval, and E2E API branches.
Hydration, navigation, and settings
src/lib/app/bootstrapClient.ts, src/lib/stores.ts, src/lib/ui/NavBar.svelte, src/routes/+layout*, src/routes/settings/*
Reduces root payloads, lazily loads and invalidates team data, consolidates settings into query tabs, and adds roster, account, and display panels.
Character, recommendation, team, and build views
src/routes/characters/*, src/routes/abyss/*, src/routes/stygian/*, src/routes/pulls/*, src/routes/teams/*, src/routes/team-configs/*
Refactors page layouts around shared components, adds solution boards and meta lists, introduces build-sheet rendering, and updates team and character interactions.
Home page, gallery, and validation
src/routes/+page.svelte, src/routes/dev/ui/*, tests/*
Updates the home page and Discord linking, adds a UI gallery, and expands deterministic Playwright coverage for route behavior and lazy API requests.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Poem

A rabbit hops through tokens bright,
While cached teams rest just right.
New tabs bloom and portraits gleam,
Build sheets calculate each dream.
“Load and filter!” the bunny sings—
Shared paws make bigger things.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main themes of the PR: major UI redesign, performance/optimization work, and auth/secrets handling changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch optimization

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

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

@woopxwoop woopxwoop changed the title UI Overhaul + Code Optimization UI Overhaul + Code Optimization + Security Secrets Jul 22, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/ui/NavBar.svelte (1)

155-164: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Mobile drawer won't close when switching Settings tabs.

All settingsLinks point to /settings?tab=…, so navigating between them from an open drawer changes only the query string, not pathname. This effect tracks page.url.pathname alone, so mobileOpen stays true and the drawer keeps covering the page after a tab tap (main links close it because their pathname changes).

🐛 Track the full URL so query-only navigations also close the drawer
   $effect(() => {
-    page.url.pathname;
+    // Track the full URL so Settings tab switches (query-only nav) also close the drawer.
+    page.url.href;
     untrack(() => {
       if (mobileOpen) {
         closedByNavigation = true;
         mobileOpen = false;
         settingsDrawerExpanded = false;
       }
     });
   });
🤖 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 `@src/lib/ui/NavBar.svelte` around lines 155 - 164, Update the
navigation-tracking effect in NavBar.svelte to track the full page URL,
including query parameters, instead of only page.url.pathname. Preserve the
existing untracked drawer-closing logic so mobileOpen, closedByNavigation, and
settingsDrawerExpanded are updated on any navigation, including Settings tab
changes.
🤖 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 `@src/lib/equipment-data.ts`:
- Around line 50-52: Update isFourStarWeapon to return true only when the weapon
exists and weapon.stars equals 4; preserve false for missing weapons and exclude
lower-star weapons from the R0 fallback.

In `@src/lib/server/cache.ts`:
- Around line 85-90: Update the getOrSet cache-miss path so the valkeySetJson
call for rKey is fire-and-forget rather than awaited, while preserving the
existing key, value, and TTL arguments and returning the computed value
immediately.

In `@src/lib/server/supabaseServer.ts`:
- Around line 32-41: Update the Reflect.get call in the serverDb proxy handler
to forward the resolved Supabase client as the receiver instead of the outer
proxy. Preserve the existing method binding behavior so function properties
remain bound to client.

In `@src/lib/server/valkey.ts`:
- Around line 33-52: Update the Valkey client configuration in getValkey’s
connection flow to set a short socket.connectTimeout and a capped
reconnectStrategy, ensuring unreachable hosts fail promptly and do not trigger
unbounded retries. Preserve the existing error handling and memory-cache
fallback behavior.

In `@src/lib/ui/components/BrowseFlipCard.svelte`:
- Around line 227-259: Update the focus-visible styling in BrowseFlipCard so
keyboard focus always provides a visual card-state indication on coarse-pointer
or hover:none devices. Keep hover flipping restricted to fine pointers, but add
an appropriate focus-visible flip or reduced-cue rule outside that restriction
while preserving the reduced-motion behavior.

In `@src/lib/ui/components/CharacterIcon.svelte`:
- Around line 145-150: Remove the unused object-position declaration from the
.icon-container-tcg img rule; preserve the existing transform-origin and
transform behavior, and do not add object-fit unless intentionally changing the
cropping behavior and updating the related comment.

In `@src/lib/ui/components/SegmentedControl.svelte`:
- Around line 22-38: Implement the keyboard and focus contract for the composite
controls: in src/lib/ui/components/SegmentedControl.svelte lines 22-38, add
radiogroup arrow-key navigation with roving tabindex, or replace the radio roles
with toggle-button semantics using aria-pressed; in
src/lib/ui/components/SlidingTabs.svelte lines 35-64, add tab arrow/Home/End
navigation, roving tabindex, and aria-controls relationships to associated
panels, or replace the tab roles with semantics matching a simple selector.

In `@src/lib/ui/components/Surface.svelte`:
- Around line 1-17: Update the Surface component’s props typing to use
HTMLAttributes<HTMLDivElement> from svelte/elements for the rest attributes
instead of the [key: string]: unknown index signature, applying the same Omit
pattern used by Button.svelte to exclude component-owned props. Preserve the
existing variant, flush, class, and children prop types while retaining
type-safe forwarding of valid div attributes.

In `@src/lib/ui/components/Team.svelte`:
- Line 20: In Team.svelte, remove the local compactIcons derivation and import
the shared isIconCompact store from stores.ts. Update the team slot class
binding to use $isIconCompact, preserving the existing compact styling behavior
through the centralized store.

In `@src/routes/abyss/`+page.svelte:
- Around line 227-335: Extract the duplicated solution-board and
meta-leaderboard markup and styles from src/routes/abyss/+page.svelte:227-335
and src/routes/stygian/+page.svelte:208-310 into shared components, such as
SolutionBoard and MetaLeaderboard. Parameterize the shared UI with each page’s
slot set, labels, slotRate, solutions, and per-slot render content, then update
both pages to use it; pass the 3-slot configuration and stygianSlotLabel from
Stygian. Ensure pager handling, slotRate mapping, behavior, and styling are
implemented once and remain identical across both pages.

In `@src/routes/stygian/`+page.svelte:
- Around line 90-94: Align all Stygian slot mappings with stygianSlotLabel and
the schema: map middle to Field 2/field_2_rate and bottom to Field
3/field_3_rate. Update slotRate, teamsOwnedStygianMiddle,
teamsOwnedStygianBottom, solver assignments, fieldScore, and leaderboard
ordering consistently, preserving top as Field 1.

---

Outside diff comments:
In `@src/lib/ui/NavBar.svelte`:
- Around line 155-164: Update the navigation-tracking effect in NavBar.svelte to
track the full page URL, including query parameters, instead of only
page.url.pathname. Preserve the existing untracked drawer-closing logic so
mobileOpen, closedByNavigation, and settingsDrawerExpanded are updated on any
navigation, including Settings tab changes.
🪄 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 (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c85d3ddd-4274-419c-b329-d5e78f72ef50

📥 Commits

Reviewing files that changed from the base of the PR and between f4ebfde and d8c0612.

⛔ Files ignored due to path filters (16)
  • .github/workflows/deploy.yml is excluded by none and included by none
  • docker-compose.yml is excluded by none and included by none
  • dockerfile is excluded by none and included by none
  • package.json is excluded by none and included by none
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml and included by none
  • prometheus.yml is excluded by none and included by none
  • src/lib/assets/favicon.svg is excluded by !**/*.svg and included by src/**
  • static/adventurers-guild.png is excluded by !**/*.png and included by none
  • static/fonts/IBMPlexSans-Bold.woff2 is excluded by !**/*.woff2 and included by none
  • static/fonts/IBMPlexSans-Medium.woff2 is excluded by !**/*.woff2 and included by none
  • static/fonts/IBMPlexSans-Regular.woff2 is excluded by !**/*.woff2 and included by none
  • static/fonts/IBMPlexSans-SemiBold.woff2 is excluded by !**/*.woff2 and included by none
  • static/fonts/Manrope.ttf is excluded by !**/*.ttf and included by none
  • static/fonts/Manrope.woff2 is excluded by !**/*.woff2 and included by none
  • static/lightkeepers-mark.png is excluded by !**/*.png and included by none
  • svelte.config.js is excluded by none and included by none
📒 Files selected for processing (75)
  • src/app.css
  • src/app.html
  • src/hooks.server.ts
  • src/lib/app/bootstrapClient.ts
  • src/lib/build-stats.ts
  • src/lib/character-filter.ts
  • src/lib/data/character-bases.json
  • src/lib/element-colors.ts
  • src/lib/equipment-data.ts
  • src/lib/is-new-character.ts
  • src/lib/server/auth.ts
  • src/lib/server/cache.ts
  • src/lib/server/supabaseServer.ts
  • src/lib/server/team-config.ts
  • src/lib/server/valkey.ts
  • src/lib/site.ts
  • src/lib/stores.ts
  • src/lib/ui/NavBar.svelte
  • src/lib/ui/components/ArtifactTooltip.svelte
  • src/lib/ui/components/Badge.svelte
  • src/lib/ui/components/BrowseFlipCard.svelte
  • src/lib/ui/components/Button.svelte
  • src/lib/ui/components/CharacterFilterBar.svelte
  • src/lib/ui/components/CharacterIcon.svelte
  • src/lib/ui/components/CharacterPortraitCard.svelte
  • src/lib/ui/components/CharacterTagSearch.svelte
  • src/lib/ui/components/Chip.svelte
  • src/lib/ui/components/EmptyState.svelte
  • src/lib/ui/components/HoverTooltip.svelte
  • src/lib/ui/components/LoadingState.svelte
  • src/lib/ui/components/PageHeader.svelte
  • src/lib/ui/components/PageShell.svelte
  • src/lib/ui/components/SegmentedControl.svelte
  • src/lib/ui/components/SlidingTabs.svelte
  • src/lib/ui/components/SolutionDots.svelte
  • src/lib/ui/components/Surface.svelte
  • src/lib/ui/components/Team.svelte
  • src/lib/ui/components/TeamCardHand.svelte
  • src/lib/ui/components/Toggle.svelte
  • src/lib/ui/components/WeaponTooltip.svelte
  • src/lib/ui/pointer.ts
  • src/lib/utils.ts
  • src/routes/+layout.server.ts
  • src/routes/+layout.svelte
  • src/routes/+layout.ts
  • src/routes/+page.svelte
  • src/routes/abyss/+page.server.ts
  • src/routes/abyss/+page.svelte
  • src/routes/abyss/+page.ts
  • src/routes/api/auth/[...all]/+server.ts
  • src/routes/api/static/+server.ts
  • src/routes/api/teams/+server.ts
  • src/routes/characters/+page.svelte
  • src/routes/characters/[slug]/+page.svelte
  • src/routes/dev/ui/+page.svelte
  • src/routes/dev/ui/+page.ts
  • src/routes/pulls/+page.svelte
  • src/routes/settings/+layout.svelte
  • src/routes/settings/+page.svelte
  • src/routes/settings/account/+page.svelte
  • src/routes/settings/account/+page.ts
  • src/routes/settings/display/+page.ts
  • src/routes/settings/panels/AccountPanel.svelte
  • src/routes/settings/panels/DisplayPanel.svelte
  • src/routes/settings/panels/RosterPanel.svelte
  • src/routes/settings/roster/+page.svelte
  • src/routes/settings/roster/+page.ts
  • src/routes/stygian/+page.server.ts
  • src/routes/stygian/+page.svelte
  • src/routes/stygian/+page.ts
  • src/routes/team-configs/+page.ts
  • src/routes/team-configs/[slug]/+page.server.ts
  • src/routes/team-configs/[slug]/+page.svelte
  • src/routes/teams/+page.svelte
  • src/routes/teams/[slug]/+page.svelte
💤 Files with no reviewable changes (5)
  • src/routes/abyss/+page.ts
  • src/routes/stygian/+page.ts
  • src/routes/settings/account/+page.svelte
  • src/routes/settings/roster/+page.svelte
  • src/routes/+layout.ts

Comment thread src/lib/equipment-data.ts Outdated
Comment on lines +50 to +52
export function isFourStarWeapon(weaponKey: string): boolean {
const weapon = weaponByKey.get(weaponKey);
return weapon ? weapon.stars <= 4 : false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Restrict the R0 fallback to actual 4★ weapons.

<= 4 also classifies 3★ weapons as four-star, so hidden 3★ weapons incorrectly display as R0. Use weapon?.stars === 4.

Proposed fix
-  return weapon ? weapon.stars <= 4 : false;
+  return weapon?.stars === 4;
📝 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
export function isFourStarWeapon(weaponKey: string): boolean {
const weapon = weaponByKey.get(weaponKey);
return weapon ? weapon.stars <= 4 : false;
export function isFourStarWeapon(weaponKey: string): boolean {
const weapon = weaponByKey.get(weaponKey);
return weapon?.stars === 4;
🤖 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 `@src/lib/equipment-data.ts` around lines 50 - 52, Update isFourStarWeapon to
return true only when the weapon exists and weapon.stars equals 4; preserve
false for missing weapons and exclude lower-star weapons from the R0 fallback.

Comment thread src/lib/server/cache.ts
Comment thread src/lib/server/supabaseServer.ts
Comment thread src/lib/server/valkey.ts Outdated
Comment thread src/lib/ui/components/BrowseFlipCard.svelte
Comment thread src/lib/ui/components/SegmentedControl.svelte Outdated
Comment thread src/lib/ui/components/Surface.svelte Outdated
Comment thread src/lib/ui/components/Team.svelte Outdated
Comment on lines +227 to +335
<PageShell class="gap-6">
<header class="page-head">
<div class="page-head-text">
<h1 class="page-title">Spiral Abyss</h1>
{#if updatedLabel || abyssEnemies?.buffName}
<p class="page-meta">
{#if abyssEnemies?.buffName}
<span title="Abyssal Moon blessing">{abyssEnemies.buffName}</span>
{/if}
{#if updatedLabel && abyssEnemies?.buffName}
<span class="page-meta-sep" aria-hidden="true">·</span>
{/if}
{#if updatedLabel}
<span>Updated {updatedLabel}</span>
{/if}
</p>
{/if}
</div>
{/if}
</header>

<!-- ── Side panels ────────────────────────────────────────────────────── -->
{#if loading}
<div class="flex items-center justify-center min-h-[40vh]">
<p style="color: var(--foreground-mid);">Loading…</p>
</div>
<LoadingState />
{:else}
<div class="grid lg:grid-cols-2 gap-4 items-start">
{#each SLOTS as slot, slotIndex (slot)}
{#if isDesktop || slotIndex === activeSlotIndex}
<div
data-panel-slot={slot}
data-active-panel={slotIndex === activeSlotIndex}
<Surface flush class="solution-board">
<div class="board-head">
<div class="board-head-left">
<span class="board-eyebrow">
Solution {safeIndex + 1}
<span class="board-eyebrow-total">of {displaySolutions.length}</span>
</span>
</div>

<div class="board-actions">
<button
type="button"
class="enemy-toggle"
aria-expanded={enemiesExpanded}
aria-controls="abyss-enemies-top abyss-enemies-bottom"
aria-label={enemiesExpanded ? "Hide enemies" : "Show enemies"}
onclick={() => (enemiesExpanded = !enemiesExpanded)}
>
<span>Enemies</span>
<IconChevronDown size={14} strokeWidth={2.25} />
</button>

{#if displaySolutions.length > 1}
<div class="pager">
<button
type="button"
class="pager-btn"
aria-label="Previous solution"
disabled={safeIndex === 0}
onclick={() => stepSolution(-1)}
>
<IconChevronDown size={16} strokeWidth={2.25} />
</button>
<button
type="button"
class="pager-btn pager-btn-next"
aria-label="Next solution"
disabled={safeIndex === displaySolutions.length - 1}
onclick={() => stepSolution(1)}
>
<IconChevronDown size={16} strokeWidth={2.25} />
</button>
</div>
{/if}
</div>
</div>

<div class="board-body">
{#each SLOTS as slot (slot)}
<div class="board-cell" data-panel-slot={slot}>
{#key assignmentKey(slot)}
{@render slotPanel(slot)}
{@render halfColumn(slot)}
{/key}
</div>
{/if}
{/each}
</div>
{/each}
</div>
</Surface>

{#if solution?.isFallback && solution.neededCharacters.length > 0}
<p class="text-xs text-center" style="color: var(--foreground-mid);">
Unable to find floor 12 clears with your roster - try teams similar to
those suggested or look at your pull suggestions
<p class="fallback-note">
Unable to find floor 12 clears with your roster try teams similar to
those suggested, or check Pull suggestions.
</p>
{/if}

<section class="meta-section">
<header class="meta-head">
<h2 class="meta-title">Meta teams</h2>
<p class="meta-lede">
Teams with the highest
<span
class="meta-term"
title="usage rate × rate of appearing in this half"
>usage index</span
> in each side
</p>
</header>

<div class="meta-board">
{#each SLOTS as slot (slot)}
{@render metaColumn(slot)}
{/each}
</div>
</section>
{/if}
</main>
</PageShell>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Extract the shared solution-board / meta-leaderboard UI to avoid abyss↔stygian drift. Both pages reimplement the same board header, pager, per-slot column, meta leaderboard, and nearly identical <style> block; they differ only in slot set, labels, solver call, and slotRate. The duplication is already diverging (pager event handling and slotRate mapping differ between the two), which is exactly the maintenance hazard consolidation prevents.

  • src/routes/abyss/+page.svelte#L227-L335: replace the inline board/meta markup + CSS with a shared component (e.g. SolutionBoard + MetaLeaderboard) parameterized by slots, labels, slotRate, and solutions.
  • src/routes/stygian/+page.svelte#L208-L310: render the same shared component, passing the 3-slot config and stygianSlotLabel, so board/pager/meta behavior and styling stay identical to abyss.
📍 Affects 2 files
  • src/routes/abyss/+page.svelte#L227-L335 (this comment)
  • src/routes/stygian/+page.svelte#L208-L310
🤖 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 `@src/routes/abyss/`+page.svelte around lines 227 - 335, Extract the duplicated
solution-board and meta-leaderboard markup and styles from
src/routes/abyss/+page.svelte:227-335 and
src/routes/stygian/+page.svelte:208-310 into shared components, such as
SolutionBoard and MetaLeaderboard. Parameterize the shared UI with each page’s
slot set, labels, slotRate, solutions, and per-slot render content, then update
both pages to use it; pass the 3-slot configuration and stygianSlotLabel from
Stygian. Ensure pager handling, slotRate mapping, behavior, and styling are
implemented once and remain identical across both pages.

Comment on lines 90 to 94
function slotRate(team: StygianTeam, slot: Slot): number {
if (slot === "top") return team.field_1_rate ?? 0;
if (slot === "middle") return team.field_3_rate ?? 0;
return team.field_2_rate ?? 0;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm field_1_rate/field_2_rate/field_3_rate semantics and how they map to visual slots.
rg -nP 'field_[123]_rate' --type=ts -C2
fd -e ts 'database.types' --exec rg -nP 'field_[123]_rate' {}
# Inspect StygianTeam definition
rg -nP -C3 '\bStygianTeam\b' src/lib/definitions*

Repository: woopxwoop/lightkeepers

Length of output: 160


🏁 Script executed:

#!/bin/bash
set -u

echo "Files matching stygian/abyss and definitions:"
git ls-files | rg '(^src/routes/stygian/|^src/routes/abyss/|definitions|types|database\.types|styles)' | sed -n '1,200p'

echo
echo "Search for field_rate usages:"
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' 'field_[123]_rate|fieldScore|metaByField|bottom|middle|top.*rate|StygianTeam|AbyssTeam' . | sed -n '1,300p'

Repository: woopxwoop/lightkeepers

Length of output: 17396


🏁 Script executed:

#!/bin/bash
set -u

echo "slottLabels:"
cat -n src/lib/slotLabels.ts

echo
echo "definitions:"
cat -n src/lib/definitions.ts | sed -n '1,120p'

echo
echo "stygian route relevant functions:"
cat -n src/routes/stygian/+page.svelte | sed -n '80,120p'

echo
echo "stores relevant derived fields:"
cat -n src/lib/stores.ts | sed -n '220,235p'

echo
echo "solver relevant constants/functions:"
cat -n src/lib/solver.ts | sed -n '1,80p;150,175p;210,230p;335,360p;480,510p'

Repository: woopxwoop/lightkeepers

Length of output: 11436


Align Stygian field mappings before merge.

stygianSlotLabel says middle: "Field 2" and bottom: "Field 3", but slotRate, teamsOwnedStygianMiddle/teamsOwnedStygianBottom, the solver, fieldScore, and the leaderboard all map middlefield_3_rate and bottomfield_2_rate. Keep the label/schema semantics consistent so field-rate labels, ownership filters, assignments, and leaderboard ordering all use the same field ordering.

🤖 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 `@src/routes/stygian/`+page.svelte around lines 90 - 94, Align all Stygian slot
mappings with stygianSlotLabel and the schema: map middle to Field
2/field_2_rate and bottom to Field 3/field_3_rate. Update slotRate,
teamsOwnedStygianMiddle, teamsOwnedStygianBottom, solver assignments,
fieldScore, and leaderboard ordering consistently, preserving top as Field 1.

…uccess” change):

writeTeamsOwned / writeNearMissTeams swallowed errors → Pulls .catch never ran → stuck on “waiting”
“Try again” only re-ranked empty stores — now invalidates and re-fetches

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/lib/ui/NavBar.svelte (1)

43-46: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use a path-segment boundary for prefix matches.

startsWith(link.path) marks routes such as /teams-preview as active for /teams. Match the exact path or path + "/" instead.

Proposed fix
   function isMainActive(link: (typeof mainLinks)[number]): boolean {
     if (link.match === "exact") return page.url.pathname === link.path;
-    return page.url.pathname.startsWith(link.path);
+    return (
+      page.url.pathname === link.path ||
+      page.url.pathname.startsWith(`${link.path}/`)
+    );
   }
🤖 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 `@src/lib/ui/NavBar.svelte` around lines 43 - 46, Update isMainActive so
non-exact links match only when page.url.pathname equals link.path or begins
with link.path followed by “/”. Preserve the existing exact-match behavior and
prevent similarly prefixed paths such as /teams-preview from being marked
active.
src/lib/server/cache.ts (1)

90-101: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Defer async work until its tracking promise is registered.

A synchronous exception before the first await runs finally before the outer assignment completes, leaving inflight permanently rejected or connecting permanently resolved to null, which can block later retries. Defer the work so the shared async state is set before fn()/client setup executes.

  • src/lib/server/cache.ts#L90-L101
  • src/lib/server/valkey.ts#L34-59
🤖 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 `@src/lib/server/cache.ts` around lines 90 - 101, The async work in cache.ts
around the inflight promise must be deferred until after this.inflight.set(key,
pending) registers the shared promise, preventing synchronous fn() failures from
running finally too early; apply the same deferral to the client setup flow in
src/lib/server/valkey.ts lines 34-59 so connecting is registered before setup
executes, with no direct change needed beyond this shared-state ordering fix.
🤖 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 `@src/lib/stores.ts`:
- Around line 317-322: Consume the rejection from the cleanup promises created
by finally in the teams loading flow around writeTeamsOwned and in the near-miss
loading flow around nearMissInFlight. Update both src/lib/stores.ts sites at
lines 317-322 and 384-389 so the promise returned by finally is explicitly
handled, while preserving the existing in-flight identity checks and returned
original promises.

In `@src/lib/ui/components/SlidingTabs.svelte`:
- Around line 82-93: Update the tab buttons in SlidingTabs to establish an
accessible tab/panel relationship: add stable aria-controls values matching
corresponding panel IDs and ensure each controlled panel uses role="tabpanel"
with the matching ID. Preserve the existing role="tab" behavior, selection
state, and keyboard handling.

In `@tests/helpers.ts`:
- Around line 117-124: Update the ApiMockOptions type so abyssTeams,
stygianTeams, allTeamsAbyss, allTeamsStygian, nearMissTeams, and nearMissPairs
use the corresponding typed API endpoint item types instead of unknown[]. Apply
the existing endpoint payload types or satisfies-based validation so malformed
/api/teams, /api/nearmiss, and /api/static fixtures fail at compile time.
- Around line 126-130: Make SSR GET /api/static deterministic for E2E tests by
extending installApiMocks and the associated test setup to cover server-side
requests made during initial rendering of /abyss and /stygian, rather than only
Playwright browser routes. Add a server-side E2E fixture/test mode or
deterministic static fixture data used by src/lib/server/supabaseServer.ts,
ensuring SSR does not depend on Supabase secrets, cache state, or availability
while preserving existing client API mocks.

---

Outside diff comments:
In `@src/lib/server/cache.ts`:
- Around line 90-101: The async work in cache.ts around the inflight promise
must be deferred until after this.inflight.set(key, pending) registers the
shared promise, preventing synchronous fn() failures from running finally too
early; apply the same deferral to the client setup flow in
src/lib/server/valkey.ts lines 34-59 so connecting is registered before setup
executes, with no direct change needed beyond this shared-state ordering fix.

In `@src/lib/ui/NavBar.svelte`:
- Around line 43-46: Update isMainActive so non-exact links match only when
page.url.pathname equals link.path or begins with link.path followed by “/”.
Preserve the existing exact-match behavior and prevent similarly prefixed paths
such as /teams-preview from being marked active.
🪄 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 (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2da4f73a-bcd9-4a64-8691-9ea42cd46002

📥 Commits

Reviewing files that changed from the base of the PR and between d8c0612 and c67bca8.

⛔ Files ignored due to path filters (2)
  • .github/workflows/deploy.yml is excluded by none and included by none
  • tsconfig.json is excluded by none and included by none
📒 Files selected for processing (23)
  • src/lib/equipment-data.ts
  • src/lib/is-new-character.ts
  • src/lib/server/auth.ts
  • src/lib/server/cache.ts
  • src/lib/server/supabaseServer.ts
  • src/lib/server/team-config.ts
  • src/lib/server/valkey.ts
  • src/lib/stores.ts
  • src/lib/ui/NavBar.svelte
  • src/lib/ui/components/BrowseFlipCard.svelte
  • src/lib/ui/components/CharacterIcon.svelte
  • src/lib/ui/components/SegmentedControl.svelte
  • src/lib/ui/components/SlidingTabs.svelte
  • src/lib/ui/components/SolutionDots.svelte
  • src/lib/ui/components/Surface.svelte
  • src/lib/ui/components/Team.svelte
  • src/routes/pulls/+page.svelte
  • tests/abyss.spec.ts
  • tests/helpers.ts
  • tests/lazy-api.spec.ts
  • tests/pulls.spec.ts
  • tests/smoke.spec.ts
  • tests/stygian.spec.ts
💤 Files with no reviewable changes (1)
  • src/lib/ui/components/CharacterIcon.svelte

Comment thread src/lib/stores.ts
Comment on lines +82 to +93
{#each options as option, index (option.value)}
<button
type="button"
role="tab"
id="tab-{option.value}"
aria-selected={value === option.value}
tabindex={value === option.value ? 0 : -1}
onpointerdown={(event) =>
handlePointerAction(event, () => (value = option.value))}
onclick={(event) =>
handleKeyboardClick(event, () => (value = option.value))}
onkeydown={(event) => onTabKeydown(event, index)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Associate each tab with its controlled panel.

These buttons retain role="tab", but no aria-controls points to a corresponding role="tabpanel". Either add a stable tab/panel ID contract, or use role="group" with aria-pressed like SegmentedControl.

🤖 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 `@src/lib/ui/components/SlidingTabs.svelte` around lines 82 - 93, Update the
tab buttons in SlidingTabs to establish an accessible tab/panel relationship:
add stable aria-controls values matching corresponding panel IDs and ensure each
controlled panel uses role="tabpanel" with the matching ID. Preserve the
existing role="tab" behavior, selection state, and keyboard handling.

Comment thread tests/helpers.ts
Comment thread tests/helpers.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/ui/NavBar.svelte (1)

49-51: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

onSettingsPage doesn't get the same boundary fix as isMainActive.

isMainActive was just hardened to require an exact match or a /-bounded prefix, but onSettingsPage still uses a bare startsWith. No sibling route collides today, but this is inconsistent with the pattern just introduced a few lines above and drives aria-current/drawer auto-expand.

♻️ Suggested fix
-  const onSettingsPage = $derived(
-    page.url.pathname.startsWith(resolve("/settings")),
-  );
+  const onSettingsPage = $derived(
+    page.url.pathname === resolve("/settings") ||
+      page.url.pathname.startsWith(`${resolve("/settings")}/`),
+  );
🤖 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 `@src/lib/ui/NavBar.svelte` around lines 49 - 51, Update the onSettingsPage
derived expression to use the same exact-match or slash-bounded prefix logic as
isMainActive, preserving correct aria-current behavior and drawer auto-expansion
without matching similarly prefixed sibling routes.
🤖 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.

Outside diff comments:
In `@src/lib/ui/NavBar.svelte`:
- Around line 49-51: Update the onSettingsPage derived expression to use the
same exact-match or slash-bounded prefix logic as isMainActive, preserving
correct aria-current behavior and drawer auto-expansion without matching
similarly prefixed sibling routes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ed43bf61-1c98-4209-b12a-74bc69425f0c

📥 Commits

Reviewing files that changed from the base of the PR and between c67bca8 and f06e92f.

⛔ Files ignored due to path filters (4)
  • .github/workflows/playwright.yml is excluded by none and included by none
  • package.json is excluded by none and included by none
  • playwright.config.ts is excluded by none and included by none
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml and included by none
📒 Files selected for processing (24)
  • src/app.css
  • src/lib/e2e/fixtures.ts
  • src/lib/server/cache.ts
  • src/lib/server/e2e.ts
  • src/lib/server/valkey.ts
  • src/lib/stores.ts
  • src/lib/ui/NavBar.svelte
  • src/lib/ui/components/BrowseFlipCard.svelte
  • src/lib/ui/components/CharacterIcon.svelte
  • src/lib/ui/components/CharacterPortraitCard.svelte
  • src/lib/ui/components/SlidingTabs.svelte
  • src/routes/+layout.server.ts
  • src/routes/abyss/+page.svelte
  • src/routes/api/nearmiss/+server.ts
  • src/routes/api/static/+server.ts
  • src/routes/api/teams/+server.ts
  • src/routes/characters/[slug]/+page.svelte
  • src/routes/dev/ui/+page.svelte
  • src/routes/pulls/+page.svelte
  • src/routes/stygian/+page.svelte
  • tests/abyss.spec.ts
  • tests/helpers.ts
  • tests/lazy-api.spec.ts
  • tests/smoke.spec.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@tests/debug.ts`:
- Around line 28-35: Update the requestfailed handler to log only the failed
request’s pathname rather than the full req.url(), removing query strings and
credentials; also normalize the optional req.failure()?.errorText to a
single-line value or omit it when unavailable. Preserve the existing request
method and failure logging context.

In `@tests/global-setup.ts`:
- Around line 9-15: Update the warmup requests in the paths.map callback to
apply a per-request abort timeout, ensuring hung routes terminate and
Promise.all can complete. Preserve the existing best-effort catch and separate
webServer readiness behavior.
🪄 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 (base), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7bc4e332-7ab5-4371-9ace-ee149c2896b4

📥 Commits

Reviewing files that changed from the base of the PR and between f06e92f and b160e01.

⛔ Files ignored due to path filters (2)
  • .github/workflows/playwright.yml is excluded by none and included by none
  • playwright.config.ts is excluded by none and included by none
📒 Files selected for processing (7)
  • src/routes/pulls/+page.svelte
  • tests/abyss.spec.ts
  • tests/debug.ts
  • tests/global-setup.ts
  • tests/helpers.ts
  • tests/lazy-api.spec.ts
  • tests/pulls.spec.ts

Comment thread tests/debug.ts
Comment thread tests/global-setup.ts
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.

1 participant