Skip to content

Commit 003cea2

Browse files
authored
[codex] refine startup loading copy (#86)
* refine startup loading copy * fix startup shell copy hydration * refine startup shell accessibility * harden startup shell config
1 parent 27b898c commit 003cea2

File tree

3 files changed

+165
-35
lines changed

3 files changed

+165
-35
lines changed
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { test } from 'node:test';
2+
import assert from 'node:assert/strict';
3+
import { readFile } from 'node:fs/promises';
4+
5+
const startupShellPath = new URL('../../ui/index.html', import.meta.url);
6+
const startupCopyConfigPath = new URL('../../ui/startup-copy.js', import.meta.url);
7+
8+
test('startup shell loads shared copy config, reuses applyStartupMode, and exposes a focused live region', async () => {
9+
const source = await readFile(startupShellPath, 'utf8');
10+
const configSource = await readFile(startupCopyConfigPath, 'utf8').catch(() => '');
11+
12+
assert.match(
13+
source,
14+
/<script src="\.\/startup-copy\.js"><\/script>/,
15+
'expected startup shell to load a dedicated startup copy config',
16+
);
17+
18+
assert.match(
19+
source,
20+
/<span id="startup-status"[^>]*role="status"[^>]*aria-live="polite"[^>]*><\/span>/,
21+
'expected the status text element to be the polite live region',
22+
);
23+
assert.doesNotMatch(
24+
source,
25+
/aria-atomic="true"/,
26+
'expected startup shell to avoid verbose atomic live region announcements',
27+
);
28+
29+
assert.doesNotMatch(
30+
source,
31+
/<h1 id="startup-title" class="title">[^<]+<\/h1>/,
32+
'expected startup title to be populated from STARTUP_COPY instead of duplicated static copy',
33+
);
34+
assert.doesNotMatch(
35+
source,
36+
/<p id="startup-desc" class="desc">[^<]+<\/p>/,
37+
'expected startup description to be populated from STARTUP_COPY instead of duplicated static copy',
38+
);
39+
assert.doesNotMatch(
40+
source,
41+
/<span id="startup-status">[^<]+<\/span>/,
42+
'expected startup status to be populated from STARTUP_COPY instead of duplicated static copy',
43+
);
44+
45+
assert.doesNotMatch(
46+
source,
47+
/const\s+STARTUP_COPY\s*=/,
48+
'expected startup copy to be defined in a dedicated config instead of inline',
49+
);
50+
assert.match(
51+
source,
52+
/const\s+startupShell\s*=\s*window\.astrbot\.startupShell;/,
53+
'expected startup shell to read its shared config from a named astrbot namespace',
54+
);
55+
assert.match(
56+
source,
57+
/const\s+\{\s*STARTUP_MODES,\s*STARTUP_COPY\s*\}\s*=\s*startupShell;/,
58+
'expected startup shell to destructure startup config from the namespaced object',
59+
);
60+
assert.doesNotMatch(
61+
source,
62+
/const\s+initialCopy\s*=/,
63+
'expected initial render to reuse applyStartupMode instead of duplicating copy application',
64+
);
65+
assert.match(
66+
source,
67+
/applyStartupMode\(STARTUP_MODES\.LOADING\);/,
68+
'expected startup shell to initialize through applyStartupMode',
69+
);
70+
assert.match(
71+
source,
72+
/if\s*\(status\.textContent\s*===\s*next\.status\)\s*return;/,
73+
'expected startup shell to skip duplicate status announcements',
74+
);
75+
76+
assert.match(
77+
configSource,
78+
/const\s+deepFreeze\s*=\s*\(obj\)\s*=>/,
79+
'expected shared startup copy config to use a deepFreeze helper',
80+
);
81+
assert.match(
82+
configSource,
83+
/const\s+STARTUP_MODES\s*=\s*\{/,
84+
'expected shared startup copy config to define startup modes',
85+
);
86+
assert.match(
87+
configSource,
88+
/window\.astrbot\s*=\s*window\.astrbot\s*\|\|\s*\{\};/,
89+
'expected shared startup copy config to allocate the astrbot namespace',
90+
);
91+
assert.match(
92+
configSource,
93+
/window\.astrbot\.startupShell\s*=\s*deepFreeze\(/,
94+
'expected shared startup copy config to expose startup shell under the astrbot namespace',
95+
);
96+
assert.match(
97+
configSource,
98+
/STARTUP_COPY:\s*\{/,
99+
'expected shared startup copy config to define localized startup copy',
100+
);
101+
assert.match(
102+
configSource,
103+
/en:\s*\{/,
104+
'expected shared startup copy config to include English startup copy',
105+
);
106+
assert.match(
107+
configSource,
108+
/zh:\s*\{/,
109+
'expected shared startup copy config to include Chinese startup copy',
110+
);
111+
});

ui/index.html

Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -133,56 +133,28 @@
133133
<main class="shell">
134134
<section class="panel">
135135
<img class="brand" src="./astrbot-logo.png" alt="AstrBot Logo" />
136-
<h1 id="startup-title" class="title">AstrBot Loading</h1>
137-
<p id="startup-desc" class="desc">Initializing runtime, please wait.</p>
136+
<h1 id="startup-title" class="title"></h1>
137+
<p id="startup-desc" class="desc"></p>
138138
<div class="bar-wrap" aria-hidden="true">
139139
<div class="bar"></div>
140140
</div>
141141
<div class="status">
142142
<span class="dot" aria-hidden="true"></span>
143-
<span id="startup-status">Starting AstrBot...</span>
143+
<span id="startup-status" role="status" aria-live="polite"></span>
144144
</div>
145145
</section>
146146
</main>
147+
<script src="./startup-copy.js"></script>
147148
<script>
148149
(() => {
149150
const title = document.getElementById("startup-title");
150151
const desc = document.getElementById("startup-desc");
151152
const status = document.getElementById("startup-status");
152153
if (!title || !desc || !status) return;
154+
if (!window.astrbot || !window.astrbot.startupShell) return;
153155

154-
// Keep in sync with startup mode constants in src-tauri/src/startup_mode.rs.
155-
const STARTUP_MODES = Object.freeze({
156-
LOADING: "loading",
157-
PANEL_UPDATE: "panel-update",
158-
});
159-
160-
const STARTUP_COPY = {
161-
en: {
162-
[STARTUP_MODES.LOADING]: {
163-
title: "AstrBot Loading",
164-
desc: "Initializing runtime, please wait.",
165-
status: "Starting AstrBot...",
166-
},
167-
[STARTUP_MODES.PANEL_UPDATE]: {
168-
title: "AstrBot Updating Panel",
169-
desc: "Panel updates detected. Downloading and applying now, please keep the app open.",
170-
status: "Downloading AstrBot panel updates...",
171-
},
172-
},
173-
zh: {
174-
[STARTUP_MODES.LOADING]: {
175-
title: "AstrBot 正在加载",
176-
desc: "正在启动,请稍候。",
177-
status: "正在启动 AstrBot...",
178-
},
179-
[STARTUP_MODES.PANEL_UPDATE]: {
180-
title: "AstrBot 正在更新面板",
181-
desc: "检测到面板更新,正在下载并应用,请耐心等待,不要关闭应用。",
182-
status: "正在下载 AstrBot 面板更新...",
183-
},
184-
},
185-
};
156+
const startupShell = window.astrbot.startupShell;
157+
const { STARTUP_MODES, STARTUP_COPY } = startupShell;
186158

187159
const resolveLocaleKey = () => {
188160
const locale =
@@ -202,6 +174,7 @@ <h1 id="startup-title" class="title">AstrBot Loading</h1>
202174
const next = resolveStartupCopy(mode);
203175
title.textContent = next.title;
204176
desc.textContent = next.desc;
177+
if (status.textContent === next.status) return;
205178
status.textContent = next.status;
206179
};
207180

ui/startup-copy.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
const deepFreeze = (obj) => {
2+
for (const key of Object.getOwnPropertyNames(obj)) {
3+
const value = obj[key];
4+
if (value && typeof value === 'object' && !Object.isFrozen(value)) {
5+
deepFreeze(value);
6+
}
7+
}
8+
9+
return Object.freeze(obj);
10+
};
11+
12+
const STARTUP_MODES = {
13+
LOADING: 'loading',
14+
PANEL_UPDATE: 'panel-update',
15+
};
16+
17+
window.astrbot = window.astrbot || {};
18+
window.astrbot.startupShell = deepFreeze({
19+
STARTUP_MODES,
20+
STARTUP_COPY: {
21+
en: {
22+
[STARTUP_MODES.LOADING]: {
23+
title: 'AstrBot Desktop',
24+
desc: 'Preparing the runtime, please wait.',
25+
status: 'Starting core services...',
26+
},
27+
[STARTUP_MODES.PANEL_UPDATE]: {
28+
title: 'AstrBot Desktop',
29+
desc: 'A new panel version is available and is being downloaded and applied.',
30+
status: 'Syncing panel assets...',
31+
},
32+
},
33+
zh: {
34+
[STARTUP_MODES.LOADING]: {
35+
title: 'AstrBot Desktop',
36+
desc: '正在准备运行环境,请稍候。',
37+
status: '正在启动核心服务...',
38+
},
39+
[STARTUP_MODES.PANEL_UPDATE]: {
40+
title: 'AstrBot Desktop',
41+
desc: '检测到新面板版本,正在下载并应用。',
42+
status: '正在同步面板资源...',
43+
},
44+
},
45+
},
46+
});

0 commit comments

Comments
 (0)