Skip to content

Commit 5943598

Browse files
Anyrainelclaude
andcommitted
Replace dynamic i18n keys with static lookups; use data-wn-card for pacman graph
- Replace all t.ui() template-string keys with static key lookups so keys are statically analyzable (whatsNew, accountData.insights, talents, v2Weights) - Add missing i18n keys: whatsNew.features/fixes, v2Weights.sands/goblet/circlet - Use data-wn-card attribute instead of fragile CSS class selector for pacman graph card detection, preventing repeated regressions - Center pacman on horizontal gap paths (0.5) - 10x pacman speed in dev mode for easier debugging Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 06d07d4 commit 5943598

File tree

6 files changed

+69
-13
lines changed

6 files changed

+69
-13
lines changed

src/components/account-data/ActionRecommendationCard.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,17 @@ function ActionRecommendationCardComponent({
135135
<div className="flex items-center gap-1 mt-0.5">
136136
<Icon className={cn("w-3.5 h-3.5 shrink-0", color)} />
137137
<span className={cn("text-xs font-medium", color)}>
138-
{t.ui(`accountData.insights.${rec.actionType}`)}
138+
{rec.actionType === "swap"
139+
? t.ui("accountData.insights.swap")
140+
: rec.actionType === "upgrade"
141+
? t.ui("accountData.insights.upgrade")
142+
: rec.actionType === "reroll"
143+
? t.ui("accountData.insights.reroll")
144+
: rec.actionType === "farm"
145+
? t.ui("accountData.insights.farm")
146+
: rec.actionType === "equip"
147+
? t.ui("accountData.insights.equip")
148+
: rec.actionType}
139149
</span>
140150
{subtitle && (
141151
<span className="text-xs text-muted-foreground">· {subtitle}</span>

src/components/account-data/CharacterEditDialog.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,13 @@ function OverviewPanel({
522522
className="flex items-center gap-1.5 sm:gap-2 shrink-0"
523523
>
524524
<div className="min-w-[28px] sm:min-w-[40px]">
525-
<FieldLabel>{t.ui(`accountData.talents.${key}`)}</FieldLabel>
525+
<FieldLabel>
526+
{key === "auto"
527+
? t.ui("accountData.talents.auto")
528+
: key === "skill"
529+
? t.ui("accountData.talents.skill")
530+
: t.ui("accountData.talents.burst")}
531+
</FieldLabel>
526532
</div>
527533
<NumberInput
528534
value={talents[key]}

src/components/artifact-builds/V2WeightsView.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,11 @@ function V2CharacterCard({
314314
>
315315
<Icon className="w-3.5 h-3.5 text-foreground/50 shrink-0" />
316316
<span className="text-foreground/60">
317-
{t.ui(`v2Weights.${slot}`)}
317+
{slot === "sands"
318+
? t.ui("v2Weights.sands")
319+
: slot === "goblet"
320+
? t.ui("v2Weights.goblet")
321+
: t.ui("v2Weights.circlet")}
318322
</span>
319323
{options.map((ms, i) => (
320324
<span

src/components/shared/WhatsNew.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ interface GEdge {
103103

104104
const S = 28;
105105
const OFF = S / 2 + 4;
106-
const SPEED = 60;
106+
const SPEED = import.meta.env.DEV ? 600 : 60;
107107

108108
// ── Graph builder ──────────────────────────────────────────────────────
109109

@@ -115,8 +115,9 @@ function buildGraph(
115115
const H = container.offsetHeight;
116116
const cRect = container.getBoundingClientRect();
117117

118-
// Collect card rects relative to container
119-
const cardEls = container.querySelectorAll('[class*="rounded-2xl"]');
118+
// Collect card rects relative to container.
119+
// Cards must have data-wn-card to be detected — never use fragile CSS selectors.
120+
const cardEls = container.querySelectorAll("[data-wn-card]");
120121
const cards: { x: number; y: number; r: number; b: number }[] = [];
121122
for (const el of cardEls) {
122123
if (el === skipEl || el.contains(skipEl) || skipEl.contains(el)) continue;
@@ -146,7 +147,7 @@ function buildGraph(
146147
const bot = Math.max(...rows[i].map((c) => c.b));
147148
const top = Math.min(...rows[i + 1].map((c) => c.y));
148149
hGaps.push({
149-
y: bot + (top - bot) * 0.38,
150+
y: bot + (top - bot) * 0.5,
150151
wide: top - bot >= MIN_INTERIOR_GAP,
151152
});
152153
}
@@ -550,7 +551,13 @@ export function WhatsNew({ children }: { children: React.ReactNode }) {
550551
"text-foreground"
551552
)}
552553
>
553-
{t.ui(`whatsNew.${section.category}`)}
554+
{section.category === "features"
555+
? t.ui("whatsNew.features")
556+
: section.category === "fixes"
557+
? t.ui("whatsNew.fixes")
558+
: section.category === "roadmap"
559+
? t.ui("whatsNew.roadmap")
560+
: section.category}
554561
</h3>
555562
<ul className="space-y-1">
556563
{section.items.map((item, i) => (

src/data/i18n-ui.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,12 +399,16 @@ export const i18nUiData = {
399399
scoreExplanation: {
400400
title: { en: "Artifact Score Calculation", zh: "圣遗物评分计算逻辑" },
401401
description: {
402-
en: "How we derive the score for each artifact.",
403-
zh: "圣遗物评分是如何计算的?",
402+
en: "How we derive the artifact score (out of 300) for each character.",
403+
zh: "角色的圣遗物评分(满分 300)是如何计算的?",
404404
},
405405
formula: {
406-
en: "Final Score = Σ( Stat Value × Normalization Factor × (Weight / 100) × [Punishment Factor] )",
407-
zh: "最终评分 = Σ( 属性数值 × 折算系数 × (权重 / 100) × [惩罚系数] )",
406+
en: "Score = ( Main Stat Score + Substat Score ) × Normalizer",
407+
zh: "评分 = ( 主属性分 + 副属性分 ) × 归一化系数",
408+
},
409+
subFormula: {
410+
en: "Substat Score = Σ( Value × CD-Equiv Factor × Weight/100 × [Punishment] )",
411+
zh: "副属性分 = Σ( 数值 × 暴伤折算系数 × 权重/100 × [惩罚系数] )",
408412
},
409413
normalization: {
410414
title: { en: "Normalization Factor", zh: "折算系数" },
@@ -427,6 +431,20 @@ export const i18nUiData = {
427431
zh: "仅适用于固定数值属性(小攻击、小生命、小防御),以反映其相对于百分比属性较低的有效性。对于 90-100 级角色,通常设置为 30% 到 40%。",
428432
},
429433
},
434+
mainStat: {
435+
title: { en: "Main Stat Scoring", zh: "主属性评分" },
436+
description: {
437+
en: "The sands, goblet, and circlet main stats are scored when they match the build's recommendation. A correct 5★ main stat is worth 62.1 CD-equivalent points (46.4 for 4★). Wrong main stats contribute 0.",
438+
zh: "当沙漏、杯子和头冠的主属性与配装推荐一致时,会计入评分。正确的 5★ 主属性等效 62.1 暴伤点(4★ 为 46.4)。错误的主属性贡献为 0。",
439+
},
440+
},
441+
scale300: {
442+
title: { en: "300-Point Scale", zh: "300 分制" },
443+
description: {
444+
en: "The total score (main stats + substats) is normalized to a 300-point scale. 300 represents a theoretically perfect artifact set — correct main stats on all 3 slots, plus ideal substat rolls distributed across your top weighted stats.",
445+
zh: "总分(主属性 + 副属性)被归一化为 300 分制。300 分代表理论上的完美圣遗物套装 — 3 个位置的主属性全部正确,且副属性词条完美分配到最高权重的属性上。",
446+
},
447+
},
430448
factors: {
431449
cr: { en: "Crit Rate: ×2", zh: "暴击率: ×2" },
432450
cd: { en: "Crit DMG: ×1", zh: "暴击伤害: ×1" },
@@ -1291,9 +1309,14 @@ export const i18nUiData = {
12911309
},
12921310
idealScore: { en: "Ideal", zh: "理想分" },
12931311
normalizer: { en: "Norm.", zh: "归一化" },
1312+
sands: { en: "Sands", zh: "时之沙" },
1313+
goblet: { en: "Goblet", zh: "空之杯" },
1314+
circlet: { en: "Circlet", zh: "理之冠" },
12941315
},
12951316
whatsNew: {
12961317
title: { en: "What's New", zh: "更新日志" },
12971318
roadmap: { en: "Roadmap", zh: "计划" },
1319+
features: { en: "Features", zh: "新功能" },
1320+
fixes: { en: "Fixes", zh: "修复" },
12981321
},
12991322
};

src/pages/Home.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,14 +388,20 @@ const FeatureCard = ({
388388
rel="noreferrer"
389389
className={sharedClassName}
390390
style={animationStyle}
391+
data-wn-card
391392
>
392393
{content}
393394
</a>
394395
);
395396
}
396397

397398
return (
398-
<Link to={link} className={sharedClassName} style={animationStyle}>
399+
<Link
400+
to={link}
401+
className={sharedClassName}
402+
style={animationStyle}
403+
data-wn-card
404+
>
399405
{content}
400406
</Link>
401407
);

0 commit comments

Comments
 (0)