Skip to content

translation updates#502

Merged
cleech merged 12 commits intopwafrom
preview-src
Mar 31, 2026
Merged

translation updates#502
cleech merged 12 commits intopwafrom
preview-src

Conversation

@cleech
Copy link
Copy Markdown
Owner

@cleech cleech commented Mar 30, 2026

Summary by CodeRabbit

  • Bug Fixes

    • Fixed missing punctuation in Careless Whisper ability
    • Updated Wellington's Master Chef aura description formatting
  • Style

    • Enhanced visual styling for character play descriptions across multiple languages
    • Updated primary UI theme color and background gradient
    • Refined card text and icon rendering presentation
  • Chores

    • Updated development dependencies and build configuration

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

📝 Walkthrough

Walkthrough

This PR refactors the icon rendering pipeline from Handlebars helpers to a Markdown remark plugin, updates Wellington's aura text formatting across multiple playbook versions and languages, introduces template resolution for character text fields, refactors CSS typography rules for multilingual card display, and switches Vite configuration from Cloudflare to basic SSL plugin.

Changes

Cohort / File(s) Summary
Package & Build Config
package.json, vite.config.ts
Added @vitejs/plugin-basic-ssl devDependency and switched Vite plugin from Cloudflare to basic SSL; commented out global esbuild config.
Game Data Updates
public/data/GB-Playbook-4-3.json, public/data/GB-Playbook-4-4.json, public/data/GB-Playbook-4-5.json, public/data/GB-Playbook-4-6.json, public/data/GB-Playbook-4-6.fr.json, public/data/GB-Playbook-4-7.json, public/data/GB-Playbook-4-7.fr.json, public/data/GB-Playbook-4-8.json, public/data/GB-Playbook-4-8.fr.json
Corrected Wellington "Master Chef" aura formatting from [Aura 6"] to [6" Aura] across English and French playbooks; added punctuation to Careless Whisper effect text in 4.8.
Manifest Metadata
public/data/manifest.json, public/data/manifest.yaml
Updated timestamps and SHA256 hashes for playbook versions 4.3–4.8 and their translations (es/fr/zh); refreshed gameplan-2025 Chinese translation timestamp.
Icon & Text Resolution
src/components/CardUtils.tsx, src/utils/handlebars.ts, scripts/check.ts
Replaced Handlebars helper-based icon expansion with a remark Markdown plugin (remarkGBIcons); introduced iconMap for token conversion; added isEquivalentName helper for translation validation; created Handlebars utility module with async resolveText for templated text fields.
Component Rendering
src/components/Gameplan.tsx, src/pages/GamePlay/components/RosterList.tsx
Wrapped gameplan text rendering with CardText component; removed unused event parameters from react-aria press handlers.
Card Styling
src/components/CardFront.css, src/components/CardQuirks.css
Adjusted Chinese language selector targeting in character plays text; refactored card-specific typography rules for multiple languages and characters (es/zh), including font-size, letter-spacing, and flex-space adjustments.
Model & Database
src/models/gbdbTypes.ts
Updated character plays, traits, heroic, and legendary field population to use async resolveText resolution via Handlebars.
Theme & UI
src/pages/App.tsx
Updated Material-UI theme primary color to #4e91ba, activated dark background #121a22, commented out pseudo-element backdrop filter, and adjusted radial-gradient color stops.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR 501: Contains identical code-level changes across CardUtils, handlebars, manifest, and Vite config files.
  • PR 492: Introduces templated trait references in playbook JSON that depend on the new async Handlebars resolution utility.
  • PR 379: Refactors GBIcon component rendering to work in conjunction with the remark plugin icon conversion pipeline.

Poem

🐰 A remark-able transformation unfolds,
From helpers old to plugins new and bright,
Wellington's aura now stands arranged with care,
Swiss shields glow stable, ready for the fight! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'translation updates' is overly generic and does not clearly convey the main changes. While the PR does include translation file updates, it encompasses significantly more changes than just translations, including dependency updates, CSS modifications, configuration changes, and code refactoring across multiple components. Consider a more descriptive title that captures the primary change, such as 'Fix Wellington aura formatting and add icon rendering refactor' or break down the major changes more specifically.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch preview-src

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 and usage tips.

Copy link
Copy Markdown

@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/models/gbdbTypes.ts (1)

283-293: ⚠️ Potential issue | 🟠 Major

Fix parameter assignment when trait lookups return null entries.

The params array maintains original indices, but cts is a filtered array with removed nulls. At line 283, params[index] uses the filtered cts index instead of the original position, causing parameters to be assigned to the wrong traits when any trait lookup fails.

When a trait is missing (e.g., characterTraits[1] is null), the filtered cts array shrinks, but params stays the same length. Subsequent parameters then reference wrong indices—for instance, cts[1] would get params[1] instead of params[2].

Store the original indices when filtering, or sync the parameter array with the filtered traits:

const cts = characterTraits.map((ct, i) => ct !== null ? { ct, index: i } : null).filter(x => x !== null);
const result = await Promise.all(cts.map(async ({ ct, index }) => {
  const trait = Object.assign(ct.toMutableJSON(), {
    parameter: params[index],
  });
  // ...
}));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/models/gbdbTypes.ts` around lines 283 - 293, The params array is being
indexed using the filtered cts index, causing mismatched parameters when
characterTraits contains nulls; modify the filtering of characterTraits (cts) to
retain original indices (e.g., map to {ct, index} and filter non-null) and then
in the Promise.all map use that original index to pull the correct parameter
into the trait (set trait.parameter = params[index]); update places referencing
cts.map(...) and the trait construction in this block (and still call
resolveText(trait.text, db) and throw PartialError as before).
🧹 Nitpick comments (8)
src/pages/App.tsx (1)

85-93: Consider removing stale commented-out gradient alternatives.

The background styling changes look fine. However, there are multiple commented-out gradient options (lines 87-92) accumulating as dead code. If these are no longer candidates for the design, removing them would improve readability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/App.tsx` around lines 85 - 93, Remove the stale commented-out
gradient alternatives in the style block that sets backgroundImage in App.tsx
(the commented lines above the "radial-gradient..." value) to clean up dead
code; leave only the active backgroundColor and the chosen radial-gradient value
in the style object (ensure punctuation/commas around backgroundImage remain
valid after deletion).
vite.config.ts (2)

14-16: Delete the commented esbuild block for a single source of truth.

Line 14–16 is dead commented config. Removing it will make optimizeDeps.esbuildOptions the only active target config path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vite.config.ts` around lines 14 - 16, Remove the dead commented esbuild block
(the commented lines containing "esbuild: { target: 'es2022' }") so the project
uses a single source of truth for build target configuration; keep only the
active optimizeDeps.esbuildOptions configuration and delete the commented block
referencing esbuild to avoid confusion.

6-7: Remove commented-out plugin lines to avoid config drift.

At Line 6 and Line 23, the old path is retained as comments. Prefer deleting inactive config instead of comment-toggling; it keeps the active plugin chain unambiguous.

Also applies to: 23-24

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@vite.config.ts` around lines 6 - 7, Remove the commented-out plugin import
and any other commented plugin lines (e.g., the "// import { cloudflare } from
\"@cloudflare/vite-plugin\";" and the commented lines around the plugin chain)
so the active plugin list only contains real imports like basicSsl; locate these
comments near the top where the import is present and again in the plugin
array/chain (references: the commented cloudflare import and the plugin list
that includes basicSsl) and delete them to avoid config drift and keep the
config unambiguous.
package.json (1)

64-64: Remove the stale @cloudflare/vite-plugin dependency.

The Cloudflare plugin has been fully disabled in vite.config.ts (both import and usage are commented out), replaced by basicSsl(). The dependency remains in devDependencies but is no longer used. Removing it reduces maintenance burden and audit surface.

Proposed cleanup
  "devDependencies": {
-   "@cloudflare/vite-plugin": "^1.29.0",
    "@eslint/js": "^10.0.1",
    "@types/color": "^4.2.0",
    "@types/file-saver": "^2.0.7",
    "@types/react": "^19.2.14",
    "@types/react-detect-offline": "^2.4.6",
    "@types/react-dom": "^19.2.2",
    "@vitejs/plugin-basic-ssl": "^2.3.0",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` at line 64, Remove the unused devDependency
"@cloudflare/vite-plugin" from package.json's devDependencies (it is no longer
imported or used in vite.config.ts and was replaced by basicSsl()); after
removing the entry, run the project's package manager to update lockfile (npm
install / yarn install / pnpm install) so the dependency is removed from
node_modules and locks.
scripts/check.ts (1)

90-101: Consider adding a brief JSDoc comment explaining the equivalence semantics.

The function logic is correct for comparing names that may differ only in bracket-suffixed content (e.g., "Master Chef [Aura 4"] vs "Master Chef [Aura 4\"]"). However, a short comment would help future maintainers understand what "equivalent" means in this context.

Note: Empty prefixes match (e.g., "[foo]""[bar]"), and whitespace differences in the prefix (e.g., "foo [x]" vs "foo[x]") will cause a mismatch. Verify this matches intent.

📝 Optional: Add JSDoc for clarity
+/**
+ * Treats two strings as equivalent if they share the same prefix
+ * before the first `[` character, allowing bracket-suffixed annotations
+ * (e.g., aura text) to differ between base and translated strings.
+ */
 function isEquivalentName(base: string, trans: string): boolean {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/check.ts` around lines 90 - 101, Add a short JSDoc above the
isEquivalentName function that explains the equivalence semantics: treat two
names as equivalent if they are strictly equal or if both have a "[" and their
prefixes (substring before the first "[") are equal, noting that empty prefixes
match and that whitespace differences in the prefix will cause a mismatch; place
the comment immediately above the isEquivalentName declaration to help future
maintainers understand the bracket-suffix rule.
src/components/Gameplan.tsx (1)

129-136: Conditionally render the detail block when gameplan.detail is present.

While CardText gracefully handles undefined children by returning null, the empty <div className='detail'> is still rendered to the DOM. Wrapping the entire block with a conditional prevents unnecessary DOM nodes when there's no detail content.

♻️ Suggested improvement
-            <div
-              className='detail'
-              id={gameplan.title.replace(/[^a-zA-Z0-9]+/g, '')}
-            >
-              <CardText>
-                {gameplan.detail ? `(_${gameplan.detail}_)` : undefined}
-              </CardText>
-            </div>
+            {gameplan.detail && (
+              <div
+                className='detail'
+                id={gameplan.title.replace(/[^a-zA-Z0-9]+/g, '')}
+              >
+                <CardText>
+                  {`(_${gameplan.detail}_)`}
+                </CardText>
+              </div>
+            )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Gameplan.tsx` around lines 129 - 136, The div.detail (with id
generated from gameplan.title) is rendered even when gameplan.detail is falsy;
change the JSX so the entire block containing the div, id generation, and
CardText is only rendered when gameplan.detail is truthy (e.g., wrap the div and
its CardText in a conditional using gameplan.detail && ...), keeping the id
generation logic (gameplan.title.replace(...)) and the CardText usage intact.
src/utils/handlebars.ts (2)

61-65: Consider adding type safety for the db parameter.

The db parameter is typed as unknown, but it's passed directly to the template context where the helpers expect it to be a GBDatabase. While this works at runtime, it reduces type safety at compile time.

💡 Suggested improvement
-export async function resolveText(text: string | undefined, db: unknown, depth: number = 1): Promise<string> {
+export async function resolveText(text: string | undefined, db: GBDatabase, depth: number = 1): Promise<string> {
   if (!text) return "";
   const template = hb.compile(text);
   return await template({ db, depth });
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/handlebars.ts` around lines 61 - 65, The resolveText function
currently types the db parameter as unknown; change its signature to accept the
concrete GBDatabase type (or a generic constrained to GBDatabase) so the
template context has correct typings (update the resolveText declaration and any
callers to pass GBDatabase), and add/import the GBDatabase type where needed so
helpers that read properties from db get compile-time safety while keeping the
same runtime behavior in the function.

12-32: Qualifier extraction logic may be incorrect.

The check rest.length > 1 to extract the qualifier assumes that if there's more than one item in rest, the first is the qualifier. However, in Handlebars, the options hash object is always the last argument. With rest.length > 1, you're checking if there are additional positional arguments beyond just the options object.

If the template is {{trait "Name" "qualifier"}}, then rest would be ["qualifier", optionsObject], making rest.length === 2, so rest[0] would correctly be "qualifier". This appears correct.

However, if no qualifier is provided ({{trait "Name"}}), rest would be [optionsObject] with rest.length === 1, so the condition correctly returns undefined.

The logic is correct, but consider adding a comment to clarify the argument structure for maintainability.

💡 Suggested documentation
 hb.registerHelper("trait", async function (this: HelperContext | undefined, name: string, ...rest: unknown[]) {
   const db = this?.db;
   if (!db) {
     console.error(`Database not provided for trait lookup: ${name}`);
     return "";
   }
   const trait = await db.character_traits.findOne(name).exec().then(doc => doc?.toJSON());
   if (!trait) {
     console.error(`can not find trait ${name}`);
     return "";
   }
+  // rest contains [qualifier?, optionsObject] - qualifier is present if rest.length > 1
   const qualifier = (rest.length > 1) ? rest[0] as string : undefined;

Also applies to: 34-54

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/handlebars.ts` around lines 12 - 32, The qualifier extraction is
fragile/unclear; update the trait helper (hb.registerHelper("trait")) to
explicitly handle Handlebars' arguments by checking that the last item in rest
is the options hash and, if any positional args remain, use rest[0] as the
qualifier, and add a short comment explaining that rest contains
[positionalArgs..., optionsObject] so future readers understand why we
check/rest[0]; reference the qualifier variable and rest array in your change
and ensure behavior for both {{trait "Name"}} and {{trait "Name" "qualifier"}}
remains the same.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/CardQuirks.css`:
- Around line 174-180: The Stylelint failure is caused by placing a declaration
immediately after a nested rule; in the rules under the selector
.Skatha:lang(es) .character-plays (and the nested .ColdSnap .text and similar
blocks), insert a blank line before each letter-spacing declaration (the lines
setting "letter-spacing: -0.5px" and the other letter-spacing lines referenced
around the .ColdSnap .text blocks) so there is an empty line separating the
nested rule block (e.g., "& ::first-line { ... }") and the subsequent
"letter-spacing" declaration.

---

Outside diff comments:
In `@src/models/gbdbTypes.ts`:
- Around line 283-293: The params array is being indexed using the filtered cts
index, causing mismatched parameters when characterTraits contains nulls; modify
the filtering of characterTraits (cts) to retain original indices (e.g., map to
{ct, index} and filter non-null) and then in the Promise.all map use that
original index to pull the correct parameter into the trait (set trait.parameter
= params[index]); update places referencing cts.map(...) and the trait
construction in this block (and still call resolveText(trait.text, db) and throw
PartialError as before).

---

Nitpick comments:
In `@package.json`:
- Line 64: Remove the unused devDependency "@cloudflare/vite-plugin" from
package.json's devDependencies (it is no longer imported or used in
vite.config.ts and was replaced by basicSsl()); after removing the entry, run
the project's package manager to update lockfile (npm install / yarn install /
pnpm install) so the dependency is removed from node_modules and locks.

In `@scripts/check.ts`:
- Around line 90-101: Add a short JSDoc above the isEquivalentName function that
explains the equivalence semantics: treat two names as equivalent if they are
strictly equal or if both have a "[" and their prefixes (substring before the
first "[") are equal, noting that empty prefixes match and that whitespace
differences in the prefix will cause a mismatch; place the comment immediately
above the isEquivalentName declaration to help future maintainers understand the
bracket-suffix rule.

In `@src/components/Gameplan.tsx`:
- Around line 129-136: The div.detail (with id generated from gameplan.title) is
rendered even when gameplan.detail is falsy; change the JSX so the entire block
containing the div, id generation, and CardText is only rendered when
gameplan.detail is truthy (e.g., wrap the div and its CardText in a conditional
using gameplan.detail && ...), keeping the id generation logic
(gameplan.title.replace(...)) and the CardText usage intact.

In `@src/pages/App.tsx`:
- Around line 85-93: Remove the stale commented-out gradient alternatives in the
style block that sets backgroundImage in App.tsx (the commented lines above the
"radial-gradient..." value) to clean up dead code; leave only the active
backgroundColor and the chosen radial-gradient value in the style object (ensure
punctuation/commas around backgroundImage remain valid after deletion).

In `@src/utils/handlebars.ts`:
- Around line 61-65: The resolveText function currently types the db parameter
as unknown; change its signature to accept the concrete GBDatabase type (or a
generic constrained to GBDatabase) so the template context has correct typings
(update the resolveText declaration and any callers to pass GBDatabase), and
add/import the GBDatabase type where needed so helpers that read properties from
db get compile-time safety while keeping the same runtime behavior in the
function.
- Around line 12-32: The qualifier extraction is fragile/unclear; update the
trait helper (hb.registerHelper("trait")) to explicitly handle Handlebars'
arguments by checking that the last item in rest is the options hash and, if any
positional args remain, use rest[0] as the qualifier, and add a short comment
explaining that rest contains [positionalArgs..., optionsObject] so future
readers understand why we check/rest[0]; reference the qualifier variable and
rest array in your change and ensure behavior for both {{trait "Name"}} and
{{trait "Name" "qualifier"}} remains the same.

In `@vite.config.ts`:
- Around line 14-16: Remove the dead commented esbuild block (the commented
lines containing "esbuild: { target: 'es2022' }") so the project uses a single
source of truth for build target configuration; keep only the active
optimizeDeps.esbuildOptions configuration and delete the commented block
referencing esbuild to avoid confusion.
- Around line 6-7: Remove the commented-out plugin import and any other
commented plugin lines (e.g., the "// import { cloudflare } from
\"@cloudflare/vite-plugin\";" and the commented lines around the plugin chain)
so the active plugin list only contains real imports like basicSsl; locate these
comments near the top where the import is present and again in the plugin
array/chain (references: the commented cloudflare import and the plugin list
that includes basicSsl) and delete them to avoid config drift and keep the
config unambiguous.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 22d8c12b-fa54-46f4-ae5b-9371394f19ad

📥 Commits

Reviewing files that changed from the base of the PR and between 4bd0129 and bfd65b5.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (25)
  • package.json
  • public/data/GB-Playbook-4-3.json
  • public/data/GB-Playbook-4-4.json
  • public/data/GB-Playbook-4-5.json
  • public/data/GB-Playbook-4-6.fr.json
  • public/data/GB-Playbook-4-6.json
  • public/data/GB-Playbook-4-7.fr.json
  • public/data/GB-Playbook-4-7.json
  • public/data/GB-Playbook-4-7.zh.json
  • public/data/GB-Playbook-4-8.es.json
  • public/data/GB-Playbook-4-8.fr.json
  • public/data/GB-Playbook-4-8.json
  • public/data/GB-Playbook-4-8.zh.json
  • public/data/manifest.json
  • public/data/manifest.yaml
  • scripts/check.ts
  • src/components/CardFront.css
  • src/components/CardQuirks.css
  • src/components/CardUtils.tsx
  • src/components/Gameplan.tsx
  • src/models/gbdbTypes.ts
  • src/pages/App.tsx
  • src/pages/GamePlay/components/RosterList.tsx
  • src/utils/handlebars.ts
  • vite.config.ts

Comment on lines +174 to +180
.Skatha:lang(es) .character-plays {
& .text {
font-size: calc(6.6pt * 200/96);
}
& .ColdSnap .text {
& ::first-line { letter-spacing: normal; }
letter-spacing: -0.5px;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Stylelint will keep failing on these nested blocks.

Line 180, Line 203, and Line 227 place a declaration immediately after a nested rule, which trips declaration-empty-line-before. Add a blank line before each letter-spacing declaration.

🧹 Minimal lint fix
   & .ColdSnap .text {
     & ::first-line { letter-spacing: normal; }
+
     letter-spacing: -0.5px;
   }
@@
   & .FlareOut .text {
     & ::first-line { letter-spacing: normal; }
+
     letter-spacing: -0.7px;
   }
@@
   & .Reconnaissance .text {
     & ::first-line { letter-spacing: normal; }
+
     letter-spacing: -0.8px;
   }

Also applies to: 201-203, 225-227

🧰 Tools
🪛 Stylelint (17.5.0)

[error] 180-180: Expected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/CardQuirks.css` around lines 174 - 180, The Stylelint failure
is caused by placing a declaration immediately after a nested rule; in the rules
under the selector .Skatha:lang(es) .character-plays (and the nested .ColdSnap
.text and similar blocks), insert a blank line before each letter-spacing
declaration (the lines setting "letter-spacing: -0.5px" and the other
letter-spacing lines referenced around the .ColdSnap .text blocks) so there is
an empty line separating the nested rule block (e.g., "& ::first-line { ... }")
and the subsequent "letter-spacing" declaration.

@cleech cleech merged commit ffca1fa into pwa Mar 31, 2026
7 checks passed
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.

2 participants