icebear0828 Claude Opus 4.6 commited on
Commit
d712fd9
·
1 Parent(s): 66b06ea

fix: stable i18n layout + version sync from electron to master

Browse files

- Header: extract StableText component using inline-grid + whitespace-nowrap
to prevent layout shift AND line wrapping when switching languages
- getProxyInfo(): try git tag first, fall back to package.json (skip 1.0.0)
- sync-electron.yml: after bumping electron, sync version to master's
package.json with [skip ci] so git-mode users see real version number
- Export NEW_VERSION to GITHUB_ENV for cross-step access

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

.github/workflows/sync-electron.yml CHANGED
@@ -83,13 +83,29 @@ jobs:
83
 
84
  echo "BUMP=true" >> "$GITHUB_ENV"
85
  echo "NEW_TAG=$NEW_TAG" >> "$GITHUB_ENV"
 
86
  echo "Bumped to $NEW_VERSION, tagged $NEW_TAG"
87
 
88
- - name: Push electron branch (with tags if bumped)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  if: env.CONFLICT != 'true'
90
  run: |
91
  if [ "$BUMP" = "true" ]; then
92
  git push origin electron --follow-tags
 
93
  else
94
  git push origin electron
95
  fi
 
83
 
84
  echo "BUMP=true" >> "$GITHUB_ENV"
85
  echo "NEW_TAG=$NEW_TAG" >> "$GITHUB_ENV"
86
+ echo "NEW_VERSION=$NEW_VERSION" >> "$GITHUB_ENV"
87
  echo "Bumped to $NEW_VERSION, tagged $NEW_TAG"
88
 
89
+ - name: Sync version to master
90
+ if: env.BUMP == 'true'
91
+ run: |
92
+ git checkout master
93
+ node -e "
94
+ const fs = require('fs');
95
+ const pkg = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
96
+ pkg.version = process.env.NEW_VERSION;
97
+ fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
98
+ "
99
+ git add package.json
100
+ git commit -m "chore: sync version to ${NEW_VERSION} [skip ci]"
101
+ git checkout electron
102
+
103
+ - name: Push electron + master + tags
104
  if: env.CONFLICT != 'true'
105
  run: |
106
  if [ "$BUMP" = "true" ]; then
107
  git push origin electron --follow-tags
108
+ git push origin master
109
  else
110
  git push origin electron
111
  fi
web/src/components/Header.tsx CHANGED
@@ -13,6 +13,16 @@ const SVG_SUN = (
13
  </svg>
14
  );
15
 
 
 
 
 
 
 
 
 
 
 
16
  interface HeaderProps {
17
  onAddAccount: () => void;
18
  onCheckUpdate: () => void;
@@ -64,9 +74,9 @@ export function Header({ onAddAccount, onCheckUpdate, onOpenUpdateModal, checkin
64
  <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-primary opacity-75" />
65
  <span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-primary" />
66
  </span>
67
- <span class="text-xs font-semibold text-primary whitespace-nowrap">{t("serverOnline")}</span>
68
  {version && (
69
- <span class="text-[0.65rem] font-mono text-primary/70">v{version}</span>
70
  )}
71
  {commit && (
72
  <span class="text-[0.6rem] font-mono text-primary/40">{commit.slice(0, 7)}</span>
@@ -82,7 +92,7 @@ export function Header({ onAddAccount, onCheckUpdate, onOpenUpdateModal, checkin
82
  <svg class="size-3.5" viewBox="0 0 24 24" fill="currentColor">
83
  <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" />
84
  </svg>
85
- <span class="text-xs font-semibold whitespace-nowrap">{t("starOnGithub")}</span>
86
  </a>
87
  {/* Check for Updates */}
88
  <button
@@ -93,7 +103,7 @@ export function Header({ onAddAccount, onCheckUpdate, onOpenUpdateModal, checkin
93
  <svg class={`size-3.5 ${checking ? "animate-spin" : ""}`} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
94
  <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" />
95
  </svg>
96
- <span class="text-xs font-semibold whitespace-nowrap">{checking ? t("checkingUpdates") : t("checkForUpdates")}</span>
97
  </button>
98
  {/* Update status message */}
99
  {updateStatusMsg && !checking && (
@@ -130,7 +140,7 @@ export function Header({ onAddAccount, onCheckUpdate, onOpenUpdateModal, checkin
130
  <svg class="size-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
131
  <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" />
132
  </svg>
133
- <span class="text-xs font-semibold whitespace-nowrap">{t("proxySettings")}</span>
134
  </a>
135
  <button
136
  onClick={onAddAccount}
@@ -139,7 +149,7 @@ export function Header({ onAddAccount, onCheckUpdate, onOpenUpdateModal, checkin
139
  <svg class="size-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
140
  <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
141
  </svg>
142
- <span class="whitespace-nowrap">{t("addAccount")}</span>
143
  </button>
144
  </>
145
  )}
 
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
+ );
24
+ }
25
+
26
  interface HeaderProps {
27
  onAddAccount: () => void;
28
  onCheckUpdate: () => void;
 
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="Server Online" 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
  )}
81
  {commit && (
82
  <span class="text-[0.6rem] font-mono text-primary/40">{commit.slice(0, 7)}</span>
 
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="Star" class="text-xs font-semibold">{t("starOnGithub")}</StableText>
96
  </a>
97
  {/* Check for Updates */}
98
  <button
 
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="Check for Updates" class="text-xs font-semibold">{checking ? t("checkingUpdates") : t("checkForUpdates")}</StableText>
107
  </button>
108
  {/* Update status message */}
109
  {updateStatusMsg && !checking && (
 
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="Proxy Assignment" class="text-xs font-semibold">{t("proxySettings")}</StableText>
144
  </a>
145
  <button
146
  onClick={onAddAccount}
 
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="Add Account">{t("addAccount")}</StableText>
153
  </button>
154
  </>
155
  )}