feat: add multi-arch image publishing
This commit is contained in:
+12
-1
@@ -1 +1,12 @@
|
||||
<div id="root"></div><script type="module" src="/src/main.tsx"></script>
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Codex Helper</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+26
-14
@@ -90,6 +90,7 @@ function App() {
|
||||
if (!s.initialized)
|
||||
return (
|
||||
<Setup
|
||||
version={s.version}
|
||||
onDone={() => {
|
||||
setS({ ...s, initialized: true });
|
||||
setAuth(true);
|
||||
@@ -100,9 +101,15 @@ function App() {
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
{auth ? (
|
||||
<Route path="*" element={<Shell logout={() => setAuth(false)} />} />
|
||||
<Route
|
||||
path="*"
|
||||
element={<Shell version={s.version} logout={() => setAuth(false)} />}
|
||||
/>
|
||||
) : (
|
||||
<Route path="*" element={<Login done={() => setAuth(true)} />} />
|
||||
<Route
|
||||
path="*"
|
||||
element={<Login version={s.version} done={() => setAuth(true)} />}
|
||||
/>
|
||||
)}
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
@@ -116,7 +123,15 @@ const Splash = () => (
|
||||
<div className="spinner" />
|
||||
</main>
|
||||
);
|
||||
function Setup({ onDone }: { onDone: () => void }) {
|
||||
function Brand({ version }: { version?: string }) {
|
||||
return (
|
||||
<div className="logo">
|
||||
<Zap /> Codex Helper
|
||||
{version && <span className="version-badge">v{version}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
function Setup({ version, onDone }: { version: string; onDone: () => void }) {
|
||||
const [form, set] = useState({
|
||||
username: "admin",
|
||||
password: "",
|
||||
@@ -135,9 +150,7 @@ function Setup({ onDone }: { onDone: () => void }) {
|
||||
return (
|
||||
<main className="setup">
|
||||
<section className="hero">
|
||||
<div className="logo">
|
||||
<Zap /> Codex Helper
|
||||
</div>
|
||||
<Brand version={version} />
|
||||
<h1>
|
||||
连接你的 Codex
|
||||
<br />
|
||||
@@ -146,6 +159,9 @@ function Setup({ onDone }: { onDone: () => void }) {
|
||||
<p>一个容器内完成用量洞察、重置提醒与账户管理。</p>
|
||||
</section>
|
||||
<form className="panel form" onSubmit={submit}>
|
||||
<div className="setup-mobile-brand">
|
||||
<Brand version={version} />
|
||||
</div>
|
||||
<small>首次初始化 · 1 / 1</small>
|
||||
<h2>创建管理员</h2>
|
||||
<label>
|
||||
@@ -181,7 +197,7 @@ function Setup({ onDone }: { onDone: () => void }) {
|
||||
</main>
|
||||
);
|
||||
}
|
||||
function Login({ done }: { done: () => void }) {
|
||||
function Login({ version, done }: { version: string; done: () => void }) {
|
||||
const [u, setU] = useState("admin"),
|
||||
[p, setP] = useState(""),
|
||||
[e, setE] = useState("");
|
||||
@@ -199,9 +215,7 @@ function Login({ done }: { done: () => void }) {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="logo">
|
||||
<Zap /> Codex Helper
|
||||
</div>
|
||||
<Brand version={version} />
|
||||
<h2>欢迎回来</h2>
|
||||
<label>
|
||||
用户名
|
||||
@@ -221,7 +235,7 @@ function Login({ done }: { done: () => void }) {
|
||||
</main>
|
||||
);
|
||||
}
|
||||
function Shell({ logout }: { logout: () => void }) {
|
||||
function Shell({ version, logout }: { version: string; logout: () => void }) {
|
||||
const nav = useNavigate();
|
||||
const [theme, setTheme] = useState(localStorage.theme || "system");
|
||||
useEffect(() => {
|
||||
@@ -236,9 +250,7 @@ function Shell({ logout }: { logout: () => void }) {
|
||||
return (
|
||||
<div className="app">
|
||||
<aside>
|
||||
<div className="logo">
|
||||
<Zap /> Codex Helper
|
||||
</div>
|
||||
<Brand version={version} />
|
||||
<nav>
|
||||
<button onClick={() => nav("/")}>
|
||||
<Activity />
|
||||
|
||||
@@ -76,6 +76,17 @@ select {
|
||||
color: var(--accent);
|
||||
width: 22px;
|
||||
}
|
||||
.version-badge {
|
||||
padding: 2px 6px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.setup {
|
||||
min-height: 100vh;
|
||||
display: grid;
|
||||
@@ -84,6 +95,9 @@ select {
|
||||
gap: 8vw;
|
||||
padding: 7vw;
|
||||
}
|
||||
.setup-mobile-brand {
|
||||
display: none;
|
||||
}
|
||||
.hero h1 {
|
||||
font-size: clamp(42px, 5vw, 76px);
|
||||
line-height: 1.05;
|
||||
@@ -576,6 +590,9 @@ a {
|
||||
.hero {
|
||||
display: none;
|
||||
}
|
||||
.setup-mobile-brand {
|
||||
display: block;
|
||||
}
|
||||
.app {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@@ -49,6 +49,40 @@ async function openSettings(page: Page) {
|
||||
await expect(page.locator(".settings-loading")).toHaveCount(0);
|
||||
}
|
||||
|
||||
test("shows the build version in the authenticated brand", async ({ page }) => {
|
||||
await openSettings(page);
|
||||
const badge = page.locator("aside .version-badge");
|
||||
await expect(badge).toBeVisible();
|
||||
await expect(badge).toHaveText("vtest");
|
||||
});
|
||||
|
||||
test("shows the build version before setup", async ({ page }) => {
|
||||
await page.route("**/api/v1/system/status", (route) =>
|
||||
route.fulfill({ json: { initialized: false, appServer: false, version: "test" } }),
|
||||
);
|
||||
await page.goto("/");
|
||||
const badge = page.locator(
|
||||
(page.viewportSize()?.width ?? 0) <= 800
|
||||
? ".setup-mobile-brand .version-badge"
|
||||
: ".hero .version-badge",
|
||||
);
|
||||
await expect(badge).toBeVisible();
|
||||
await expect(badge).toHaveText("vtest");
|
||||
});
|
||||
|
||||
test("shows the build version on login", async ({ page }) => {
|
||||
await page.route("**/api/v1/system/status", (route) =>
|
||||
route.fulfill({ json: { initialized: true, appServer: false, version: "test" } }),
|
||||
);
|
||||
await page.route("**/api/v1/auth/me", (route) =>
|
||||
route.fulfill({ status: 401, json: { error: "unauthorized" } }),
|
||||
);
|
||||
await page.goto("/");
|
||||
const badge = page.locator(".login .version-badge");
|
||||
await expect(badge).toBeVisible();
|
||||
await expect(badge).toHaveText("vtest");
|
||||
});
|
||||
|
||||
async function layout(page: Page) {
|
||||
return page.evaluate(() => {
|
||||
const box = (selector: string) => {
|
||||
|
||||
Reference in New Issue
Block a user