Skip to content

Commit 515075e

Browse files
committed
feat: add sidebar release label
1 parent 182bf7d commit 515075e

14 files changed

Lines changed: 181 additions & 9 deletions

File tree

.github/workflows/release.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,18 @@ jobs:
117117
sudo apt-get update -y
118118
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev
119119
120+
- name: Compute release label
121+
shell: bash
122+
run: |
123+
echo "RELEASE_LABEL=$(TZ=Asia/Shanghai date +'%Y.%m.%d.%H')" >> "$GITHUB_ENV"
124+
120125
# ── Build ─────────────────────────────────────────────────────────────
121126
- name: Wails build
122127
run: |
123128
wails build \
124129
-platform ${{ matrix.wails-platform }} \
125130
${{ matrix.wails-extra || '' }} \
126-
-ldflags "-X main.Version=${{ github.ref_name }}"
131+
-ldflags "-X main.Version=${{ github.ref_name }} -X main.ReleaseLabel=${RELEASE_LABEL}"
127132
128133
# ── Package ───────────────────────────────────────────────────────────
129134
- name: Package DMG (macOS)

app.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import (
1212
// Version is injected at build time via -ldflags
1313
var Version = "dev"
1414

15+
// ReleaseLabel is injected at build time for UI display, format: YYYY.MM.DD.HH
16+
var ReleaseLabel = ""
17+
1518
// GitHubRepo is the repository used for auto-update checks
1619
const GitHubRepo = "linhay/GetTokens"
1720

@@ -94,7 +97,7 @@ type CreateCodexAPIKeyInput struct {
9497

9598
func NewApp() *App {
9699
return &App{
97-
core: wailsapp.New(Version, GitHubRepo),
100+
core: wailsapp.New(Version, ReleaseLabel, GitHubRepo),
98101
}
99102
}
100103

@@ -114,6 +117,10 @@ func (a *App) GetVersion() string {
114117
return a.core.GetVersion()
115118
}
116119

120+
121+
func (a *App) GetReleaseLabel() string {
122+
return a.core.GetReleaseLabel()
123+
}
117124
func (a *App) CheckUpdate() (*updater.ReleaseInfo, error) {
118125
return a.core.CheckUpdate()
119126
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Release Label 与 Version 边界
2+
3+
## 背景
4+
本轮 Sidebar 版本号需求要求界面显示发布日期小时,如 `2026.04.06.11`。但应用现有 `Version` 同时被自动更新逻辑使用,直接改写 `Version` 会把 UI 展示格式和版本比较语义耦合到一起。
5+
6+
## 结论
7+
GetTokens 后续统一采用双轨元数据:
8+
9+
1. `Version`
10+
用于自动更新比较、Git tag / release 语义、对外版本标识。
11+
2. `ReleaseLabel`
12+
仅用于 UI 展示,格式固定为 `YYYY.MM.DD.HH`
13+
14+
## 规则
15+
1. 不要把 `Version` 直接用于 Sidebar、角标、构建时间标签等纯展示场景。
16+
2. 需要展示发布日期时,优先读取后端注入的 `ReleaseLabel`
17+
3. release 构建必须在 CI 中显式生成 `ReleaseLabel`,不要依赖运行时猜测。
18+
4. 生成 `ReleaseLabel` 时必须显式声明时区;当前采用 `Asia/Shanghai`
19+
5. 开发态允许前端对空值/`dev` 做本地 fallback,但 release 包必须优先展示注入值。
20+
21+
## 当前落地
22+
1. Go 入口新增 `ReleaseLabel` 全局变量,通过 `-ldflags` 注入。
23+
2. Wails 暴露 `GetReleaseLabel()` 给前端读取。
24+
3. Sidebar 仅消费 `ReleaseLabel`,不再直接消费 `Version`
25+
4. GitHub Release workflow 在构建前计算 `RELEASE_LABEL=$(TZ=Asia/Shanghai date +'%Y.%m.%d.%H')`
26+
27+
## 适用场景
28+
1. Sidebar / Header / About 面板里的构建标签。
29+
2. 未来需要区分“用户可见发布日期”与“机器可比较版本号”的任何 UI。
30+
31+
## 不适用场景
32+
1. 自动更新版本比较。
33+
2. Git tag、发布说明标题、兼容性判断。

docs-linhay/memory/2026-04-26.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# 2026-04-26
2+
3+
## Sidebar Version Label
4+
- 新增 `ReleaseLabel` 构建注入字段,保留 `Version` 给自动更新比较使用,避免界面发布日期格式影响更新逻辑。
5+
- `frontend/src/utils/version.ts` 将 Sidebar 版本展示统一格式化为 `YYYY.MM.DD.HH`
6+
- `Sidebar` 底部文案改为 `VERSION`,并展示格式化后的发布日期标签。
7+
- Release 工作流在构建阶段按 `Asia/Shanghai` 时区注入 `ReleaseLabel`
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Sidebar Version Label
2+
3+
## 背景
4+
Sidebar 底部原本直接展示原始 `version` 字符串,缺少统一的版本号展示规则,无法满足“发布日期 + hh”的产品要求。同时自动更新仍依赖原始版本比较,不能直接把界面展示格式塞回 `Version`
5+
6+
## 目标
7+
在 Sidebar 底部增加稳定的发布日期标签,格式统一为 `YYYY.MM.DD.HH`,且不影响自动更新的版本比较逻辑。
8+
9+
## 范围
10+
- 提取前端版本展示格式化逻辑
11+
- 新增仅供 UI 展示的 `ReleaseLabel` 构建注入字段
12+
- 在 Sidebar 底部显示格式化后的发布日期标签
13+
- 为版本格式化补充可执行测试
14+
15+
## 非目标
16+
- 不修改自动更新使用的原始 `Version` 语义
17+
- 不调整状态页上的版本展示
18+
- 不改动自动更新版本比较逻辑
19+
20+
## 验收标准
21+
- Sidebar 底部显示 `VERSION <YYYY.MM.DD.HH>`
22+
- Release 构建通过 `-ldflags` 注入真实发布日期标签
23+
- `dev` 构建可回退到当前日期小时格式展示
24+
- 日期型版本串会被规范化为 `YYYY.MM.DD.HH`
25+
- 新增测试可覆盖日期规范化、dev 回退和展示字段读取场景
26+
27+
## 相关链接
28+
- [Sidebar 组件](/Users/linhey/Desktop/linhay-open-sources/GetTokens/frontend/src/components/biz/Sidebar.tsx)
29+
- [版本格式化工具](/Users/linhey/Desktop/linhay-open-sources/GetTokens/frontend/src/utils/version.ts)
30+
- [Release 工作流](/Users/linhey/Desktop/linhay-open-sources/GetTokens/.github/workflows/release.yml)
31+
32+
## 当前状态
33+
- 状态:done
34+
- 最近更新:2026-04-26

frontend/src/App.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useEffect, useMemo, useState } from 'react';
2-
import { GetSidecarStatus, GetVersion } from '../wailsjs/go/main/App';
2+
import { GetReleaseLabel, GetSidecarStatus, GetVersion } from '../wailsjs/go/main/App';
33
import { EventsOn } from '../wailsjs/runtime/runtime';
44
import Sidebar from './components/biz/Sidebar';
55
import AccountsPage from './pages/AccountsPage';
@@ -24,6 +24,7 @@ function AppShell() {
2424
const [activePage, setActivePage] = useState<AppPage>('accounts');
2525
const [sidecarStatus, setSidecarStatus] = useState<SidecarStatus>(defaultSidecarStatus);
2626
const [version, setVersion] = useState('dev');
27+
const [releaseLabel, setReleaseLabel] = useState('');
2728

2829
useEffect(() => {
2930
const isDark =
@@ -37,12 +38,14 @@ function AppShell() {
3738

3839
async function loadInitialState() {
3940
try {
40-
const [currentVersion, currentStatus] = await Promise.all([
41+
const [currentVersion, currentReleaseLabel, currentStatus] = await Promise.all([
4142
trackRequest('GetVersion', { args: [] }, () => GetVersion()),
43+
trackRequest('GetReleaseLabel', { args: [] }, () => GetReleaseLabel()),
4244
trackRequest('GetSidecarStatus', { args: [] }, () => GetSidecarStatus()),
4345
]);
4446
if (!mounted) return;
4547
setVersion(currentVersion || 'dev');
48+
setReleaseLabel(currentReleaseLabel || '');
4649
if (currentStatus) {
4750
setSidecarStatus(currentStatus);
4851
}
@@ -81,7 +84,7 @@ function AppShell() {
8184
className="flex h-screen w-screen overflow-hidden bg-[var(--bg-main)] selection:bg-[var(--border-color)] selection:text-[var(--bg-main)]"
8285
data-collaboration-id="MAIN_FRAME"
8386
>
84-
<Sidebar activePage={activePage} setActivePage={setActivePage} version={version} />
87+
<Sidebar activePage={activePage} setActivePage={setActivePage} releaseLabel={releaseLabel} />
8588
<main className="flex-1 overflow-hidden bg-[var(--bg-surface)]">{page}</main>
8689
</div>
8790
);

frontend/src/components/biz/Sidebar.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useI18n } from '../../context/I18nContext';
22
import type { AppPage } from '../../types';
3+
import { formatSidebarVersion } from '../../utils/version';
34

45
const navItems = [
56
{ id: 'status', label: 'nav.status', icon: 'M12 12m-10 0a10 10 0 1 0 20 0a10 10 0 1 0 -20 0 M12 8v4l3 3' },
@@ -11,11 +12,12 @@ const navItems = [
1112
interface SidebarProps {
1213
activePage: AppPage;
1314
setActivePage: (page: AppPage) => void;
14-
version: string;
15+
releaseLabel: string;
1516
}
1617

17-
export default function Sidebar({ activePage, setActivePage, version }: SidebarProps) {
18+
export default function Sidebar({ activePage, setActivePage, releaseLabel }: SidebarProps) {
1819
const { t } = useI18n();
20+
const sidebarVersion = formatSidebarVersion(releaseLabel);
1921

2022
return (
2123
<aside
@@ -50,7 +52,7 @@ export default function Sidebar({ activePage, setActivePage, version }: SidebarP
5052

5153
<div className="border-t-2 border-[var(--border-color)] p-6">
5254
<div className="text-[9px] font-bold uppercase tracking-tighter text-[var(--text-muted)]">
53-
BUILD {version}
55+
VERSION {sidebarVersion}
5456
</div>
5557
</div>
5658
</aside>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import test from 'node:test';
2+
import assert from 'node:assert/strict';
3+
4+
import { buildDateHourLabel, formatSidebarVersion } from './version.ts';
5+
6+
test('buildDateHourLabel formats local date as YYYY.MM.DD.HH', () => {
7+
const date = new Date(2026, 3, 26, 8, 15, 0);
8+
9+
assert.equal(buildDateHourLabel(date), '2026.04.26.08');
10+
});
11+
12+
test('formatSidebarVersion normalizes date-like version strings', () => {
13+
assert.equal(formatSidebarVersion('2026-04-26 08:30:00'), '2026.04.26.08');
14+
assert.equal(formatSidebarVersion('2026042608'), '2026.04.26.08');
15+
assert.equal(formatSidebarVersion('2026.04.26.08'), '2026.04.26.08');
16+
});
17+
18+
test('formatSidebarVersion falls back to current date-hour for dev builds', () => {
19+
const now = new Date(2026, 3, 26, 9, 0, 0);
20+
21+
assert.equal(formatSidebarVersion('dev', now), '2026.04.26.09');
22+
});
23+
24+
test('formatSidebarVersion keeps opaque versions unchanged', () => {
25+
assert.equal(formatSidebarVersion('v0.1.0'), 'v0.1.0');
26+
});

frontend/src/utils/version.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const dateHourPattern =
2+
/^(?<year>\d{4})[./-]?(?<month>\d{2})[./-]?(?<day>\d{2})(?:[T\s_.:+/-]?(?<hour>\d{2}))(?:[:]\d{2}(?::\d{2})?)?/;
3+
4+
function pad(value: number): string {
5+
return String(value).padStart(2, '0');
6+
}
7+
8+
export function buildDateHourLabel(date: Date): string {
9+
return `${date.getFullYear()}.${pad(date.getMonth() + 1)}.${pad(date.getDate())}.${pad(date.getHours())}`;
10+
}
11+
12+
export function formatSidebarVersion(version: string, now: Date = new Date()): string {
13+
const normalized = version.trim();
14+
15+
if (!normalized || normalized === 'dev') {
16+
return buildDateHourLabel(now);
17+
}
18+
19+
const match = normalized.match(dateHourPattern);
20+
if (match?.groups) {
21+
const { year, month, day, hour } = match.groups;
22+
if (year && month && day && hour) {
23+
return `${year}.${month}.${day}.${hour}`;
24+
}
25+
}
26+
27+
return normalized;
28+
}

frontend/wailsjs/go/main/App.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export function GetAuthFileModels(arg1:string):Promise<Array<Record<string, any>
2020

2121
export function GetCodexQuota(arg1:string):Promise<main.CodexQuotaResponse>;
2222

23+
export function GetReleaseLabel():Promise<string>;
24+
2325
export function GetSidecarStatus():Promise<sidecar.Status>;
2426

2527
export function GetVersion():Promise<string>;

0 commit comments

Comments
 (0)