Skip to content

Conversation

evanpelle
Copy link
Collaborator

@evanpelle evanpelle commented Aug 25, 2025

Description:

To advertise patterns, show a random, purchasable pattern on the end screen.

  • Refactored the pattern button into a reusable PatternButton lit component
  • Used tailwind instead of CSS for styling because the CSS affects lit components due to using the light-dom
  • Removed the tooltip, didn't seem necessary since there is already a big "purchase" button under the pattern
Screenshot 2025-08-25 at 1 26 26 PM

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

evan

Copy link
Contributor

coderabbitai bot commented Aug 25, 2025

Walkthrough

Renames patternsfetchPatterns, adds two win_modal translation keys, introduces a reusable PatternButton component, refactors TerritoryPatternsModal to use it (null default), and updates WinModal to asynchronously load and render a purchasable pattern with purchase wiring and utility styling.

Changes

Cohort / File(s) Summary
Translations
resources/lang/en.json
Added win_modal.support_openfront and win_modal.territory_pattern strings.
Client API rename
src/client/Cosmetics.ts
Renamed export patterns(...)fetchPatterns(...); signature unchanged; callers updated.
Territory patterns modal refactor
src/client/TerritoryPatternsModal.ts
Replaced manual grid/hover/tooltip with PatternButton grid; switched to fetchPatterns; selectedPattern now `Pattern
New component
src/client/components/PatternButton.ts
Added PatternButton (light DOM) with `pattern: Pattern
Win modal update
src/client/graphics/layers/WinModal.ts
Converted modal styling to utility classes; show() is now async and preloads a random purchasable pattern via getUserMe + fetchPatterns; renders PatternButton and binds onPurchase to handlePurchase; added fade-in animation and patternContent state.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant WinModal
  participant UserService as getUserMe
  participant Cosmetics as fetchPatterns
  participant PatternBtn as PatternButton
  participant Purchase as handlePurchase

  User->>WinModal: open()
  activate WinModal
  WinModal->>UserService: getUserMe()
  UserService-->>WinModal: UserMeResponse|null
  WinModal->>Cosmetics: fetchPatterns(userMe)
  Cosmetics-->>WinModal: Map<string,Pattern>
  WinModal->>WinModal: filter purchasable, pick random
  WinModal->>PatternBtn: render PatternButton(pattern, onPurchase)
  WinModal-->>User: show modal (fade-in)
  User->>PatternBtn: click Purchase
  PatternBtn-->>WinModal: onPurchase(priceId)
  WinModal->>Purchase: handlePurchase(priceId)
  Purchase-->>WinModal: success/fail
  deactivate WinModal
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Feature - Frontend, UI/UX, Premium

Suggested reviewers

  • scottanderson
  • Aotumuri

Poem

Tiny tiles align in bright array,
A button to pick, a button to pay.
Modals bloom with patterns new,
Translations sing — “Support OpenFront!” too.
Click, preview, joy — a small UI fête. 🎉

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.


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.
    • 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.
  • 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 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/Issue comments)

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

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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

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

Caution

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

⚠️ Outside diff range comments (1)
src/client/Cosmetics.ts (1)

5-31: Fix null-access on flares and avoid mutating shared cosmetics objects

  • If userMe is null, userMe?.player.flares will still try to read .flares off undefined and crash. Also, new Set(undefined) would throw. Guard both.
  • Clobbering patternData.product = null mutates the object stored in cosmetics.patterns. That can leak into other consumers. Clone instead.

Apply:

   const patterns: Map<string, Pattern> = new Map();
-  const playerFlares = new Set(userMe?.player.flares);
+  // userMe can be null; guard the entire chain and default to empty.
+  const playerFlares = new Set(userMe?.player?.flares ?? []);

   for (const name in cosmetics.patterns) {
     const patternData = cosmetics.patterns[name];
     const hasAccess = playerFlares.has(`pattern:${name}`);
     if (hasAccess) {
-      // Remove product info because player already has access.
-      patternData.product = null;
-      patterns.set(name, patternData);
+      // Avoid mutating the source cosmetics object.
+      patterns.set(name, { ...patternData, product: null });
     } else if (patternData.product !== null) {
       // Player doesn't have access, but product is available for purchase.
       patterns.set(name, patternData);
     }
🧹 Nitpick comments (4)
src/client/TerritoryPatternsModal.ts (4)

23-23: Initialize state to avoid strict property init errors and undefined reads

Initialize selectedPattern to null so renders don’t see undefined and to satisfy strictPropertyInitialization.

-  @state() private selectedPattern: Pattern | null;
+  @state() private selectedPattern: Pattern | null = null;

97-104: Use Lit’s repeat + stable callbacks to avoid re-creating nodes and closures each render

  • The loop builds an array and creates new arrow functions every render; that changes function identity and can cause unnecessary re-renders in child elements.
  • Prefer the repeat directive keyed by pattern name, and hoist handlers as class fields.

Diff inside this block:

-      buttons.push(html`
-        <pattern-button
-          .pattern=${pattern}
-          .onSelect=${(p: Pattern | null) => this.selectPattern(p)}
-          .onPurchase=${(priceId: string) => handlePurchase(priceId)}
-        ></pattern-button>
-      `);
+      buttons.push(html`
+        <pattern-button
+          .pattern=${pattern}
+          .onSelect=${this.handleSelect}
+          .onPurchase=${this.handlePurchase}
+        ></pattern-button>
+      `);

And add once (outside this range):

// imports
import { repeat } from "lit/directives/repeat.js";

// class fields
private handleSelect = (p: Pattern | null) => this.selectPattern(p);
private handlePurchase = (priceId: string) => handlePurchase(priceId);

// renderPatternGrid replacement for the for..of:
const buttons = repeat(
  this.patterns,
  ([name]) => name,
  ([, pattern]) => html`
    <pattern-button
      .pattern=${pattern}
      .onSelect=${this.handleSelect}
      .onPurchase=${this.handlePurchase}
    ></pattern-button>
  `,
);

111-116: Default/null tile is good; minor cleanup and consistency with Tailwind

  • Keep the default tile first—good UX.
  • You don’t need to pass onPurchase when .pattern=${null}; it won’t be used.
  • Prefer Tailwind over inline style for the container above for consistency.
-        <pattern-button
-          .pattern=${null}
-          .onSelect=${(p: Pattern | null) => this.selectPattern(p)}
-          .onPurchase=${(priceId: string) => handlePurchase(priceId)}
-        ></pattern-button>
+        <pattern-button
+          .pattern=${null}
+          .onSelect=${this.handleSelect}
+        ></pattern-button>

Container (outside this range) suggestion:

<div class="flex flex-wrap gap-4 p-2 justify-center items-start">
  ...
</div>

145-150: Selection flow is clear; consider emitting a custom event for composition

Persist + refresh + close is fine. For better composition with parents (and tests), emit an event so listeners don’t need to reach into this element to track changes.

private selectPattern(pattern: Pattern | null) {
  this.userSettings.setSelectedPatternName(pattern?.name);
  this.selectedPattern = pattern;
  this.dispatchEvent(new CustomEvent("pattern-changed", { detail: { pattern } }));
  this.refresh();
  this.close();
}

I can also add a small test to assert the event fires with the right payload.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ed3acbc and 2800c15.

📒 Files selected for processing (5)
  • resources/lang/en.json (1 hunks)
  • src/client/Cosmetics.ts (1 hunks)
  • src/client/TerritoryPatternsModal.ts (5 hunks)
  • src/client/components/PatternButton.ts (1 hunks)
  • src/client/graphics/layers/WinModal.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/client/components/PatternButton.ts
  • src/client/graphics/layers/WinModal.ts
  • resources/lang/en.json
🧰 Additional context used
🧬 Code graph analysis (1)
src/client/TerritoryPatternsModal.ts (2)
src/core/CosmeticSchemas.ts (1)
  • Pattern (6-6)
src/client/Cosmetics.ts (2)
  • fetchPatterns (5-31)
  • handlePurchase (33-66)
🔇 Additional comments (2)
src/client/TerritoryPatternsModal.ts (2)

10-11: Good call: side-effect import for custom element + centralizing data access

Importing ./components/PatternButton registers the element, and pulling fetchPatterns/handlePurchase from one place keeps concerns separated. No issues spotted.


46-54: Logout path clears selection; restore path is safe

Setting storage to undefined and selectedPattern to null on logout is correct. Restoring from storage with ?? null is safe if the pattern was removed or becomes unavailable.

If storage might contain a now-unpurchasable pattern, confirm desired UX: keep selection (even if hidden) vs. coerce to null. Current code coerces to null. If you want to show a “previously selected, now unavailable” tile, we can surface a disabled card instead.

@github-project-automation github-project-automation bot moved this from Triage to Development in OpenFront Release Management Aug 25, 2025
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: 0

Caution

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

⚠️ Outside diff range comments (2)
src/client/TerritoryPatternsModal.ts (2)

151-159: Unused method should be removed.

The renderPatternPreview method appears to be dead code - it's only called in the refresh() method at line 162-166, but the result is stored in a local variable preview that is never used. The actual preview rendering has been delegated to the PatternButton component.

Remove the unused method and its invocation:

-  private renderPatternPreview(
-    pattern?: string,
-    width?: number,
-    height?: number,
-  ): TemplateResult {
-    return html`
-      <img src="${generatePreviewDataUrl(pattern, width, height)}"></img>
-    `;
-  }

   public async refresh() {
-    const preview = this.renderPatternPreview(
-      this.selectedPattern?.pattern,
-      48,
-      48,
-    );
     this.requestUpdate();

178-180: Dead code should be removed.

These lines try to render to this.previewButton which is never set (always null). This appears to be leftover code from the previous implementation.

Remove the dead code:

-    if (this.previewButton === null) return;
-    render(preview, this.previewButton);
-    this.requestUpdate();

Also remove the unused property declaration at line 21:

-  public previewButton: HTMLElement | null = null;
🧹 Nitpick comments (3)
src/client/TerritoryPatternsModal.ts (3)

56-70: Easter egg implementation is functional but could be improved.

The choco pattern easter egg works, but consider these improvements:

  1. The popup styling might not work without corresponding CSS
  2. The popup is appended directly to document.body without cleanup on component disconnect

Consider managing the popup lifecycle better:

 private triggerChocoEasterEgg() {
   console.log("🍫 Choco pattern unlocked!");
   this.showChocoPattern = true;
 
   const popup = document.createElement("div");
-  popup.className = "easter-egg-popup";
+  popup.className = "fixed top-4 left-1/2 transform -translate-x-1/2 bg-green-500 text-white px-6 py-3 rounded-lg shadow-lg z-50";
   popup.textContent = "🎉 You unlocked the Choco pattern!";
   document.body.appendChild(popup);
 
   setTimeout(() => {
-    popup.remove();
+    popup?.remove();
   }, 5000);
 
   this.requestUpdate();
 }

Also consider tracking and cleaning up the popup on disconnect:

+private easterEggPopup: HTMLElement | null = null;

 disconnectedCallback() {
   window.removeEventListener("keydown", this.handleKeyDown);
+  this.easterEggPopup?.remove();
   super.disconnectedCallback();
 }

108-109: Consider using Tailwind classes instead of inline styles.

Since you're switching to Tailwind, use utility classes for consistency.

-      <div
-        class="flex flex-wrap gap-4 p-2"
-        style="justify-content: center; align-items: flex-start;"
-      >
+      <div
+        class="flex flex-wrap gap-4 p-2 justify-center items-start"
+      >

88-90: Maintain awareness of global styles when using light DOM

The override of createRenderRoot() in TerritoryPatternsModal.ts (lines 88–90) disables Shadow DOM, just like in many other components across the codebase. This is a deliberate choice to let Tailwind utility classes penetrate the component, but it comes at the cost of style encapsulation.

• Add a brief comment above the override explaining that Shadow DOM is disabled intentionally for Tailwind integration.
• Be extra cautious with class names and selectors here to avoid unintended styling side-effects from global CSS.
• (Optional) Consider using more specific utility combinations or :host-scoped styles if you need partial encapsulation in the future.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 2800c15 and ce85386.

📒 Files selected for processing (1)
  • src/client/TerritoryPatternsModal.ts (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/client/TerritoryPatternsModal.ts (3)
src/core/CosmeticSchemas.ts (1)
  • Pattern (6-6)
src/client/Cosmetics.ts (2)
  • fetchPatterns (5-31)
  • handlePurchase (33-66)
src/client/components/PatternButton.ts (1)
  • handlePurchase (57-62)
🔇 Additional comments (4)
src/client/TerritoryPatternsModal.ts (4)

10-11: LGTM! Clean imports and good component-based refactoring.

The refactoring to use the reusable PatternButton component and renaming to fetchPatterns improves code organization and reusability.


23-23: Good type change from undefined to null for consistency.

Using null instead of undefined for the default pattern state is more explicit and aligns better with the component's API.


111-114: Default pattern button looks good.

Setting the pattern to null for the default option is clean and consistent with the new typing approach.


97-103: PatternButton translation logic confirmed

The PatternButton component still defines a private translatePatternName method and calls translateText(${prefix}.${patternName}) to localize each pattern name (and falls back to the default key), so name translation remains fully covered. No changes needed.

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

Caution

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

⚠️ Outside diff range comments (1)
src/client/TerritoryPatternsModal.ts (1)

121-128: Ensure translation keys are added to en.json

The modal and pattern buttons reference keys that aren’t defined in resources/lang/en.json, which will break translations at runtime. Please add the following entries with appropriate English values:

territory_patterns.title
territory_patterns.pattern.default
territory_patterns.purchase

These are used in:

  • src/client/TerritoryPatternsModal.ts (lines 121–128)
  • PatternButton components elsewhere
🧹 Nitpick comments (11)
src/client/TerritoryPatternsModal.ts (5)

22-22: Initialize reactive state to a safe default (null).

Without an initializer, TypeScript’s strictPropertyInitialization may complain, and Lit will start with undefined rather than your intended null.

Apply this diff:

-  @state() private selectedPattern: Pattern | null;
+  @state() private selectedPattern: Pattern | null = null;

2-2: Remove dead preview plumbing and unused imports. It’s no longer rendered.

previewButton is never assigned, and the preview generation/rendering in refresh() never reaches the DOM. This adds complexity and work on every refresh for no visible effect. Let’s remove it and the associated imports.

Apply this diff:

-import { html, LitElement, render } from "lit";
+import { html, LitElement } from "lit";
@@
-import { renderPatternPreview } from "./components/PatternButton";
@@
-  public previewButton: HTMLElement | null = null;
+  // (removed) previewButton: no longer needed; previews are handled by PatternButton tiles
@@
-  public async refresh() {
-    const preview = renderPatternPreview(
-      this.selectedPattern?.pattern ?? null,
-      48,
-      48,
-    );
-    this.requestUpdate();
+  public async refresh() {
+    this.requestUpdate();
@@
-    if (this.previewButton === null) return;
-    render(preview, this.previewButton);
-    this.requestUpdate();
+    // Preview injection removed; PatternButton handles previews.
   }

Also applies to: 9-9, 20-21, 151-170


143-148: Avoid modal flicker: don’t re-open the modal during selection.

selectPattern() calls refresh() (which opens the modal) and then close(). This can cause a quick reopen-close flicker. Persist selection and close directly.

Apply this diff:

-  private selectPattern(pattern: Pattern | null) {
-    this.userSettings.setSelectedPatternName(pattern?.name);
-    this.selectedPattern = pattern;
-    this.refresh();
-    this.close();
-  }
+  private selectPattern(pattern: Pattern | null) {
+    this.userSettings.setSelectedPatternName(pattern?.name);
+    this.selectedPattern = pattern;
+    // No need to refresh here; just close and let consumers react to settings.
+    this.close();
+    this.requestUpdate();
+  }

87-90: Light DOM rendering: confirm this is intentional and safe with your CSS strategy.

Returning this from createRenderRoot() opts out of Shadow DOM. This allows Tailwind/global CSS to style contents, but also allows global CSS to leak in. If isolation is desired later, prefer Shadow DOM and expose only needed parts via CSS parts or custom properties.

Would you like a variant that keeps Shadow DOM while still allowing Tailwind tokens via CSS custom properties?


71-85: Easter egg popup has no styles defined; consider minimal inline styling to ensure it’s readable.

You append a div with class easter-egg-popup, but no styles are guaranteed at runtime. A tiny inline style can ensure basic visibility without adding global CSS.

Example:

-    popup.className = "easter-egg-popup";
-    popup.textContent = "🎉 You unlocked the Choco pattern!";
+    popup.className = "easter-egg-popup";
+    popup.textContent = "🎉 You unlocked the Choco pattern!";
+    Object.assign(popup.style, {
+      position: "fixed",
+      bottom: "16px",
+      left: "50%",
+      transform: "translateX(-50%)",
+      background: "rgba(0,0,0,0.8)",
+      color: "#fff",
+      padding: "8px 12px",
+      borderRadius: "8px",
+      zIndex: "9999",
+      fontWeight: "600",
+    });

If you already have a Tailwind utility for this class elsewhere, feel free to ignore.

src/client/components/PatternButton.ts (6)

8-8: Match preview size with container: avoid extra scaling.

Container is w-[120px] h-[120px], but BUTTON_WIDTH is 150, causing browser downscaling every render. Align them to reduce work and sharpen pixels.

Apply this diff:

-export const BUTTON_WIDTH = 150;
+export const BUTTON_WIDTH = 120; // Keep in sync with container size

55-61: Buttons should be explicit and accessible: add type and ARIA.

  • Default button type is “submit” inside forms; make it type="button".
  • Mirror the disabled state with aria-disabled for ATs.

Apply this diff:

-        <button
+        <button
+          type="button"
           class="bg-white/90 border-2 border-black/10 rounded-lg p-2 cursor-pointer transition-all duration-200 w-full
                  hover:bg-white hover:-translate-y-0.5 hover:shadow-lg hover:shadow-black/20
                  disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:translate-y-0 disabled:hover:shadow-none"
           ?disabled=${isPurchasable}
+          aria-disabled="${isPurchasable ? "true" : "false"}"
           @click=${this.handleClick}
         >
@@
-              <button
+              <button
+                type="button"
                 class="w-full px-4 py-2 bg-green-500 text-white border-0 rounded-md text-sm font-semibold cursor-pointer transition-colors duration-200
                    hover:bg-green-600"
                 @click=${this.handlePurchase}
               >

Also applies to: 84-91


99-115: Graceful fallback on decode errors and more helpful alt text.

If decoding fails, generatePreviewDataUrl returns "", which produces a broken-image icon. Also, the alt text can include the pattern name for screen readers.

Apply this diff:

 export function renderPatternPreview(
   pattern: string | null,
   width: number,
   height: number,
 ): TemplateResult {
   if (pattern === null) {
     return renderBlankPreview(width, height);
   }
-  const dataUrl = generatePreviewDataUrl(pattern, width, height);
-  return html`<img
-    src="${dataUrl}"
-    alt="Pattern preview"
-    class="w-full h-full object-contain"
-    style="image-rendering: pixelated; image-rendering: -moz-crisp-edges; image-rendering: crisp-edges;"
-  />`;
+  const dataUrl = generatePreviewDataUrl(pattern, width, height);
+  if (!dataUrl) {
+    return renderBlankPreview(width, height);
+  }
+  const name = "pattern";
+  return html`<img
+    src="${dataUrl}"
+    alt="${name} preview"
+    class="w-full h-full object-contain"
+    style="image-rendering: pixelated; image-rendering: -moz-crisp-edges; image-rendering: crisp-edges;"
+  />`;
 }

If you want the actual name, you can pass it as a param or bind it at the call site.


21-24: Light DOM rendering: confirm trade-offs.

As with the modal, returning this disables Shadow DOM. If you hit style conflicts later, consider Shadow DOM with exposed parts (part=) for theming.


25-31: Minor: friendlier fallback for untranslated names.

Current fallback capitalizes only the first char. Replacing dashes/underscores improves readability (e.g., "star_field" → "Star field").

Example:

-  return patternName[0].toUpperCase() + patternName.substring(1);
+  const pretty = patternName.replace(/[-_]+/g, " ");
+  return pretty.charAt(0).toUpperCase() + pretty.slice(1);

153-216: Optional: add environment guard for tests/non-DOM contexts.

If this runs in a test or SSR context without document, canvas creation will fail. A quick guard avoids that.

Example:

-  // Create the canvas
+  // Create the canvas
+  if (typeof document === "undefined") {
+    return "";
+  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ce85386 and 72b89b3.

📒 Files selected for processing (3)
  • src/client/Cosmetics.ts (2 hunks)
  • src/client/TerritoryPatternsModal.ts (5 hunks)
  • src/client/components/PatternButton.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/client/Cosmetics.ts
🧰 Additional context used
🧬 Code graph analysis (2)
src/client/components/PatternButton.ts (2)
src/client/TerritoryPatternsModal.ts (1)
  • customElement (13-171)
src/core/PatternDecoder.ts (3)
  • PatternDecoder (1-56)
  • scaledWidth (53-55)
  • scaledHeight (49-51)
src/client/TerritoryPatternsModal.ts (3)
src/core/CosmeticSchemas.ts (1)
  • Pattern (6-6)
src/client/Cosmetics.ts (2)
  • fetchPatterns (5-31)
  • handlePurchase (33-66)
src/client/components/PatternButton.ts (2)
  • handlePurchase (40-45)
  • renderPatternPreview (99-114)
⏰ 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). (1)
  • GitHub Check: Deploy to openfront.dev
🔇 Additional comments (2)
src/client/TerritoryPatternsModal.ts (1)

91-105: Grid rendering looks clean and composable.

Good move to rely on <pattern-button> for composition instead of duplicating layout/preview logic here.

src/client/components/PatternButton.ts (1)

33-38: Good: selection logic uses composition.

Blocking selection when purchasable and delegating purchase via a callback is a clean composition pattern. Keeps purchase flow outside this component.

@evanpelle evanpelle added this to the v25 milestone Aug 25, 2025
@evanpelle evanpelle merged commit 1d84848 into v25 Aug 25, 2025
11 of 24 checks passed
@evanpelle evanpelle deleted the evan-pattern-end branch August 25, 2025 20:27
@github-project-automation github-project-automation bot moved this from Development to Complete in OpenFront Release Management Aug 25, 2025
@coderabbitai coderabbitai bot mentioned this pull request Sep 7, 2025
4 tasks
@coderabbitai coderabbitai bot mentioned this pull request Sep 19, 2025
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Complete
Development

Successfully merging this pull request may close these issues.

1 participant