icebear0828 Claude Opus 4.6 (1M context) commited on
Commit
bdd4755
·
1 Parent(s): a438d76

fix: StableText uses both en+zh references for stable button width

Browse files

StableText previously used a single reference (either hardcoded English
or dynamic t()), so buttons always jumped width on language switch.
Now uses both translations.en[key] and translations.zh[key] as invisible
grid references — width is always max(en, zh), never changes.

Also fix self-update: run git checkout -- . before git pull to discard
local modifications (e.g. package-lock.json drift) that block the pull.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

src/self-update.ts CHANGED
@@ -311,6 +311,8 @@ export async function applyProxySelfUpdate(): Promise<{ started: boolean; restar
311
 
312
  try {
313
  console.log("[SelfUpdate] Pulling latest code...");
 
 
314
  await execFileAsync("git", ["pull", "origin", "master"], { cwd, timeout: 60000 });
315
 
316
  console.log("[SelfUpdate] Installing dependencies...");
 
311
 
312
  try {
313
  console.log("[SelfUpdate] Pulling latest code...");
314
+ // Discard local modifications (e.g. package-lock.json drift) that would block git pull
315
+ await execFileAsync("git", ["checkout", "--", "."], { cwd, timeout: 10000 }).catch(() => {});
316
  await execFileAsync("git", ["pull", "origin", "master"], { cwd, timeout: 60000 });
317
 
318
  console.log("[SelfUpdate] Installing dependencies...");
web/src/components/Header.tsx CHANGED
@@ -1,4 +1,5 @@
1
  import { useI18n } from "../../../shared/i18n/context";
 
2
  import { useTheme } from "../../../shared/theme/context";
3
 
4
  const SVG_MOON = (
@@ -13,11 +14,15 @@ const SVG_SUN = (
13
  </svg>
14
  );
15
 
16
- /** Stable-width text: invisible reference sets min-width, visible text overlays it. */
17
- function StableText({ reference, children, class: cls }: { reference: string; children: string; class?: string }) {
 
 
 
18
  return (
19
  <span class={`inline-grid ${cls ?? ""}`}>
20
- <span class="invisible col-start-1 row-start-1 whitespace-nowrap">{reference}</span>
 
21
  <span class="col-start-1 row-start-1 whitespace-nowrap">{children}</span>
22
  </span>
23
  );
@@ -74,7 +79,7 @@ export function Header({ onAddAccount, onCheckUpdate, onOpenUpdateModal, checkin
74
  <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75" />
75
  <span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-primary" />
76
  </span>
77
- <StableText reference={t("serverOnline")} class="text-xs font-semibold text-primary">{t("serverOnline")}</StableText>
78
  {version && (
79
  <span class="text-[0.65rem] font-mono text-primary/70 whitespace-nowrap">v{version}</span>
80
  )}
@@ -92,7 +97,7 @@ export function Header({ onAddAccount, onCheckUpdate, onOpenUpdateModal, checkin
92
  <svg class="size-3.5" viewBox="0 0 24 24" fill="currentColor">
93
  <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
94
  </svg>
95
- <StableText reference={t("starOnGithub")} class="text-xs font-semibold">{t("starOnGithub")}</StableText>
96
  </a>
97
  {/* Check for Updates */}
98
  <button
@@ -103,7 +108,7 @@ export function Header({ onAddAccount, onCheckUpdate, onOpenUpdateModal, checkin
103
  <svg class={`size-3.5 ${checking ? "animate-spin" : ""}`} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
104
  <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.992 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182M20.985 4.356v4.992" />
105
  </svg>
106
- <StableText reference={t("checkForUpdates")} class="text-xs font-semibold">{checking ? t("checkingUpdates") : t("checkForUpdates")}</StableText>
107
  </button>
108
  {/* Update status message */}
109
  {updateStatusMsg && !checking && (
@@ -140,7 +145,7 @@ export function Header({ onAddAccount, onCheckUpdate, onOpenUpdateModal, checkin
140
  <svg class="size-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
141
  <path stroke-linecap="round" stroke-linejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75" />
142
  </svg>
143
- <StableText reference={t("proxySettings")} class="text-xs font-semibold">{t("proxySettings")}</StableText>
144
  </a>
145
  <button
146
  onClick={onAddAccount}
@@ -149,7 +154,7 @@ export function Header({ onAddAccount, onCheckUpdate, onOpenUpdateModal, checkin
149
  <svg class="size-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
150
  <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
151
  </svg>
152
- <StableText reference={t("addAccount")}>{t("addAccount")}</StableText>
153
  </button>
154
  </>
155
  )}
 
1
  import { useI18n } from "../../../shared/i18n/context";
2
+ import { translations, type TranslationKey } from "../../../shared/i18n/translations";
3
  import { useTheme } from "../../../shared/theme/context";
4
 
5
  const SVG_MOON = (
 
14
  </svg>
15
  );
16
 
17
+ /**
18
+ * Stable-width text: two invisible references (en + zh) set min-width via grid overlap.
19
+ * The visible text overlays them, so the button never changes width on language switch.
20
+ */
21
+ function StableText({ tKey, children, class: cls }: { tKey: TranslationKey; children: string; class?: string }) {
22
  return (
23
  <span class={`inline-grid ${cls ?? ""}`}>
24
+ <span class="invisible col-start-1 row-start-1 whitespace-nowrap">{translations.en[tKey]}</span>
25
+ <span class="invisible col-start-1 row-start-1 whitespace-nowrap">{translations.zh[tKey]}</span>
26
  <span class="col-start-1 row-start-1 whitespace-nowrap">{children}</span>
27
  </span>
28
  );
 
79
  <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75" />
80
  <span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-primary" />
81
  </span>
82
+ <StableText tKey="serverOnline" class="text-xs font-semibold text-primary">{t("serverOnline")}</StableText>
83
  {version && (
84
  <span class="text-[0.65rem] font-mono text-primary/70 whitespace-nowrap">v{version}</span>
85
  )}
 
97
  <svg class="size-3.5" viewBox="0 0 24 24" fill="currentColor">
98
  <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
99
  </svg>
100
+ <StableText tKey="starOnGithub" class="text-xs font-semibold">{t("starOnGithub")}</StableText>
101
  </a>
102
  {/* Check for Updates */}
103
  <button
 
108
  <svg class={`size-3.5 ${checking ? "animate-spin" : ""}`} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
109
  <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.992 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182M20.985 4.356v4.992" />
110
  </svg>
111
+ <StableText tKey="checkForUpdates" class="text-xs font-semibold">{checking ? t("checkingUpdates") : t("checkForUpdates")}</StableText>
112
  </button>
113
  {/* Update status message */}
114
  {updateStatusMsg && !checking && (
 
145
  <svg class="size-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
146
  <path stroke-linecap="round" stroke-linejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75" />
147
  </svg>
148
+ <StableText tKey="proxySettings" class="text-xs font-semibold">{t("proxySettings")}</StableText>
149
  </a>
150
  <button
151
  onClick={onAddAccount}
 
154
  <svg class="size-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
155
  <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
156
  </svg>
157
+ <StableText tKey="addAccount">{t("addAccount")}</StableText>
158
  </button>
159
  </>
160
  )}