美化 AGC 客户端设置页
收敛账号与角色协作展示文案 增加账号状态检测动画和状态卡片样式 将流式输出与联网检索整理为单列 更新设置页回归测试与配置护栏
This commit is contained in:
@@ -1757,7 +1757,8 @@ for (const snippet of [
|
||||
"'read_game_creator_app_config'",
|
||||
"'write_game_creator_app_config'",
|
||||
'aria-label="运行时配置"',
|
||||
'官方账号服务(固定)',
|
||||
'智能创作',
|
||||
'账号状态',
|
||||
'External Editor Base URL',
|
||||
'External Editor API Key',
|
||||
'runtime_config.save',
|
||||
|
||||
@@ -124,7 +124,7 @@ const runtimeSettingsSections = [
|
||||
{
|
||||
id: 'agents',
|
||||
label: 'Agent 分工',
|
||||
description: '查看各角色运行状态',
|
||||
description: '角色协作状态',
|
||||
icon: Bot,
|
||||
},
|
||||
{
|
||||
@@ -258,6 +258,7 @@ export function RuntimeConfigDialog({
|
||||
const [runtimeConfigBusy, setRuntimeConfigBusy] = useState(false);
|
||||
const [effectiveLlmConfigStatus, setEffectiveLlmConfigStatus] =
|
||||
useState<GameCreatorLlmConfigStatus | null>(llmConfigStatus);
|
||||
const [llmConfigChecking, setLlmConfigChecking] = useState(false);
|
||||
const [activeSection, setActiveSection] =
|
||||
useState<RuntimeSettingsSection>('general');
|
||||
const [clientExtensions, setClientExtensions] = useState<
|
||||
@@ -502,6 +503,7 @@ export function RuntimeConfigDialog({
|
||||
);
|
||||
setRuntimeConfigDraft(config);
|
||||
setRuntimeConfigStatus(`已读取:${result.path}`);
|
||||
setLlmConfigChecking(true);
|
||||
try {
|
||||
const status = await invoke<GameCreatorLlmConfigStatus>(
|
||||
'check_game_creator_llm_config',
|
||||
@@ -510,6 +512,8 @@ export function RuntimeConfigDialog({
|
||||
} catch {
|
||||
// Older test fixtures and non-Tauri previews may not expose the
|
||||
// diagnostic command; the config read itself remains usable.
|
||||
} finally {
|
||||
setLlmConfigChecking(false);
|
||||
}
|
||||
onLog?.('runtime_config.read');
|
||||
} catch (error) {
|
||||
@@ -730,13 +734,7 @@ export function RuntimeConfigDialog({
|
||||
<>
|
||||
<div className="runtime-settings-readonly-field">
|
||||
<span>工作方式</span>
|
||||
<strong>陶泥儿智能创作(固定)</strong>
|
||||
<small>需求将由官方智能服务执行</small>
|
||||
</div>
|
||||
<div className="runtime-settings-readonly-field">
|
||||
<span>智能服务</span>
|
||||
<strong>官方账号服务(固定)</strong>
|
||||
<small>登录后自动使用当前账号权限。</small>
|
||||
<strong>智能创作</strong>
|
||||
</div>
|
||||
{(() => {
|
||||
const credential = formatAccountCredentialState(
|
||||
@@ -744,17 +742,28 @@ export function RuntimeConfigDialog({
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className="runtime-settings-readonly-field"
|
||||
className={`runtime-settings-readonly-field runtime-settings-account-state${
|
||||
llmConfigChecking ? ' is-checking' : ''
|
||||
}`}
|
||||
data-tone={credential.tone}
|
||||
data-checking={llmConfigChecking ? 'true' : 'false'}
|
||||
data-testid="runtime-settings-account-credential-state"
|
||||
>
|
||||
<span>账号权限</span>
|
||||
<strong>{credential.label}</strong>
|
||||
<small>
|
||||
{credential.tone === 'success'
|
||||
? '当前账号已具备智能创作权限。'
|
||||
: '请返回登录页完成登录或重新登录;普通用户无需填写任何智能服务凭据。'}
|
||||
</small>
|
||||
<span>账号状态</span>
|
||||
<strong>
|
||||
{llmConfigChecking ? (
|
||||
<>
|
||||
<LoaderCircle
|
||||
className="is-spinning"
|
||||
size={14}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
正在检测
|
||||
</>
|
||||
) : (
|
||||
credential.label
|
||||
)}
|
||||
</strong>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
@@ -780,34 +789,36 @@ export function RuntimeConfigDialog({
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className="settings-checkbox">
|
||||
<input
|
||||
aria-label="流式输出"
|
||||
type="checkbox"
|
||||
checked={runtimeConfigDraft.llm.stream}
|
||||
onChange={(event) =>
|
||||
updateRuntimeLlmConfig(
|
||||
'stream',
|
||||
event.currentTarget.checked,
|
||||
)
|
||||
}
|
||||
/>
|
||||
流式输出
|
||||
</label>
|
||||
<label className="settings-checkbox">
|
||||
<input
|
||||
aria-label="联网检索"
|
||||
type="checkbox"
|
||||
checked={runtimeConfigDraft.llm.webSearchEnabled}
|
||||
onChange={(event) =>
|
||||
updateRuntimeLlmConfig(
|
||||
'webSearchEnabled',
|
||||
event.currentTarget.checked,
|
||||
)
|
||||
}
|
||||
/>
|
||||
联网检索
|
||||
</label>
|
||||
<div className="runtime-settings-option-stack">
|
||||
<label className="settings-checkbox">
|
||||
<input
|
||||
aria-label="流式输出"
|
||||
type="checkbox"
|
||||
checked={runtimeConfigDraft.llm.stream}
|
||||
onChange={(event) =>
|
||||
updateRuntimeLlmConfig(
|
||||
'stream',
|
||||
event.currentTarget.checked,
|
||||
)
|
||||
}
|
||||
/>
|
||||
流式输出
|
||||
</label>
|
||||
<label className="settings-checkbox">
|
||||
<input
|
||||
aria-label="联网检索"
|
||||
type="checkbox"
|
||||
checked={runtimeConfigDraft.llm.webSearchEnabled}
|
||||
onChange={(event) =>
|
||||
updateRuntimeLlmConfig(
|
||||
'webSearchEnabled',
|
||||
event.currentTarget.checked,
|
||||
)
|
||||
}
|
||||
/>
|
||||
联网检索
|
||||
</label>
|
||||
</div>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
@@ -912,9 +923,8 @@ export function RuntimeConfigDialog({
|
||||
) : null}
|
||||
{activeSection === 'agents' ? (
|
||||
<div className="runtime-settings-readonly-field">
|
||||
<span>角色服务</span>
|
||||
<strong>所有角色使用统一账号服务</strong>
|
||||
<small>普通用户无需配置服务连接信息。</small>
|
||||
<span>角色协作</span>
|
||||
<strong>已启用</strong>
|
||||
</div>
|
||||
) : null}
|
||||
{activeSection === 'connections' ? (
|
||||
|
||||
@@ -3901,6 +3901,69 @@ h2 {
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.runtime-settings-readonly-field,
|
||||
.runtime-settings-option-stack > label {
|
||||
display: grid;
|
||||
gap: 7px;
|
||||
min-width: 0;
|
||||
padding: 13px;
|
||||
border: 1px solid var(--platform-subpanel-border);
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(
|
||||
145deg,
|
||||
var(--runtime-settings-subpanel-fill),
|
||||
color-mix(in srgb, var(--runtime-settings-subpanel-fill) 78%, #f0e7dc)
|
||||
);
|
||||
color: var(--platform-text-base);
|
||||
}
|
||||
|
||||
.runtime-settings-readonly-field > span,
|
||||
.runtime-settings-option-stack > label {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.runtime-settings-readonly-field > span {
|
||||
color: var(--platform-text-soft);
|
||||
}
|
||||
|
||||
.runtime-settings-readonly-field > strong {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-height: 20px;
|
||||
color: var(--platform-text-strong);
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.runtime-settings-readonly-field[data-tone='success'] {
|
||||
border-color: color-mix(
|
||||
in srgb,
|
||||
var(--platform-success-border) 70%,
|
||||
transparent
|
||||
);
|
||||
background: linear-gradient(145deg, #fbfffc, #f1faf2);
|
||||
}
|
||||
|
||||
.runtime-settings-readonly-field[data-checking='true'] {
|
||||
border-color: var(--platform-nav-active-border);
|
||||
background: linear-gradient(145deg, #fffdf9, #f8f1e7);
|
||||
}
|
||||
|
||||
.runtime-settings-account-state[data-checking='true'] > strong {
|
||||
color: var(--platform-accent);
|
||||
}
|
||||
|
||||
.runtime-settings-readonly-field .is-spinning {
|
||||
animation: runtime-settings-spin 0.9s linear infinite;
|
||||
}
|
||||
|
||||
.runtime-settings-option-stack {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.runtime-settings-fields > label {
|
||||
padding: 13px;
|
||||
border: 1px solid var(--platform-subpanel-border);
|
||||
|
||||
@@ -255,6 +255,69 @@ export function registerAgentStatusDerivationTests() {
|
||||
}
|
||||
|
||||
export function registerRuntimeSettingsTests() {
|
||||
it('shows a checking animation while account status is being verified', async () => {
|
||||
let resolveAccountCheck!: (status: unknown) => void;
|
||||
const accountCheck = new Promise<unknown>((resolve) => {
|
||||
resolveAccountCheck = resolve;
|
||||
});
|
||||
const invoke = vi.fn((command: string) => {
|
||||
if (command === 'read_game_creator_app_config') {
|
||||
return Promise.resolve({
|
||||
path: '/home/test/AppData/game-creator.config.json',
|
||||
config: {
|
||||
agentMode: 'codex_app_server',
|
||||
llm: {
|
||||
apiKey: '',
|
||||
baseUrl: '',
|
||||
model: '',
|
||||
apiKind: 'openai_responses',
|
||||
reasoningEffort: 'high',
|
||||
stream: true,
|
||||
webSearchEnabled: true,
|
||||
contextWindowTokens: 128000,
|
||||
autoCompactTokenLimit: 64000,
|
||||
toolOutputTokenLimit: 12000,
|
||||
requestTimeoutMs: 180000,
|
||||
maxRetries: 2,
|
||||
retryBackoffMs: 500,
|
||||
},
|
||||
agentLlm: {},
|
||||
editorApi: { baseUrl: '', apiKey: '' },
|
||||
},
|
||||
});
|
||||
}
|
||||
if (command === 'check_game_creator_llm_config') {
|
||||
return accountCheck;
|
||||
}
|
||||
throw new Error(`unexpected invoke ${command}`);
|
||||
});
|
||||
window.__TAURI__ = { core: { invoke } };
|
||||
renderLauncherAt('/?launcher');
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '配置' }));
|
||||
const accountState = await screen.findByTestId(
|
||||
'runtime-settings-account-credential-state',
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(accountState.getAttribute('data-checking')).toBe('true');
|
||||
});
|
||||
expect(accountState.querySelector('.is-spinning')).not.toBeNull();
|
||||
expect(screen.getByText('正在检测')).not.toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
resolveAccountCheck({
|
||||
accountCredentialState: 'ready',
|
||||
configured: true,
|
||||
});
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(accountState.getAttribute('data-checking')).toBe('false');
|
||||
expect(accountState.querySelector('.is-spinning')).toBeNull();
|
||||
});
|
||||
expect(screen.getByText('账号权限已就绪')).not.toBeNull();
|
||||
});
|
||||
|
||||
it('distinguishes loading the client extension list from an empty list', async () => {
|
||||
let resolveExtensions!: (items: unknown[]) => void;
|
||||
const extensionsRead = new Promise<unknown[]>((resolve) => {
|
||||
@@ -404,9 +467,11 @@ export function registerRuntimeSettingsTests() {
|
||||
renderLauncherAt('/?launcher');
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '配置' }));
|
||||
expect(await screen.findByText('陶泥儿智能创作(固定)')).not.toBeNull();
|
||||
expect(await screen.findByText('智能创作')).not.toBeNull();
|
||||
expect(screen.queryByLabelText('Agent 模式')).toBeNull();
|
||||
expect(screen.getByText('官方账号服务(固定)')).not.toBeNull();
|
||||
expect(screen.getByText('账号状态')).not.toBeNull();
|
||||
expect(screen.queryByText('官方账号服务(固定)')).toBeNull();
|
||||
expect(screen.queryByText('普通用户无需填写任何智能服务凭据。')).toBeNull();
|
||||
expect(
|
||||
screen.queryByText(/router\.genarrative\.world|gpt-5\.6-sol/),
|
||||
).toBeNull();
|
||||
@@ -477,7 +542,7 @@ export function registerRuntimeSettingsTests() {
|
||||
renderLauncherAt('/?launcher');
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '配置' }));
|
||||
expect(await screen.findByText('陶泥儿智能创作(固定)')).not.toBeNull();
|
||||
expect(await screen.findByText('智能创作')).not.toBeNull();
|
||||
expect(screen.queryByLabelText('Agent 模式')).toBeNull();
|
||||
fireEvent.click(screen.getByRole('button', { name: '保存' }));
|
||||
|
||||
@@ -531,7 +596,7 @@ export function registerRuntimeSettingsTests() {
|
||||
fireEvent.click(screen.getByRole('button', { name: '配置' }));
|
||||
|
||||
const dialog = await screen.findByRole('dialog', { name: '运行时配置' });
|
||||
expect(screen.getByText('官方账号服务(固定)')).not.toBeNull();
|
||||
expect(screen.getByText('账号状态')).not.toBeNull();
|
||||
expect(
|
||||
screen.queryByText(/router\.genarrative\.world|gpt-5\.6-sol/),
|
||||
).toBeNull();
|
||||
@@ -822,7 +887,7 @@ export function registerPublishedRuntimeSettingsTests() {
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '配置' }));
|
||||
|
||||
expect(await screen.findByText('官方账号服务(固定)')).not.toBeNull();
|
||||
expect(await screen.findByText('账号状态')).not.toBeNull();
|
||||
expect(
|
||||
screen.queryByText(/router\.genarrative\.world|gpt-5\.6-sol/),
|
||||
).toBeNull();
|
||||
@@ -847,7 +912,8 @@ export function registerPublishedRuntimeSettingsTests() {
|
||||
expect(screen.queryByLabelText('External Editor Base URL')).toBeNull();
|
||||
expect(screen.queryByLabelText('External Editor API Key')).toBeNull();
|
||||
fireEvent.click(screen.getByRole('button', { name: /Agent 分工/ }));
|
||||
expect(screen.getByText('所有角色使用统一账号服务')).not.toBeNull();
|
||||
expect(screen.getByText('已启用')).not.toBeNull();
|
||||
expect(screen.queryByText('所有角色使用统一账号服务')).toBeNull();
|
||||
fireEvent.click(screen.getByRole('button', { name: /常用设置/ }));
|
||||
expect(screen.getByLabelText('联网检索')).toHaveProperty('checked', false);
|
||||
fireEvent.click(screen.getByRole('button', { name: /高级参数/ }));
|
||||
@@ -864,7 +930,7 @@ export function registerPublishedRuntimeSettingsTests() {
|
||||
'12000',
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: /Agent 分工/ }));
|
||||
expect(screen.getByText('所有角色使用统一账号服务')).not.toBeNull();
|
||||
expect(screen.getByText('已启用')).not.toBeNull();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /常用设置/ }));
|
||||
expect(screen.getByLabelText('推理档')).toHaveProperty('value', 'medium');
|
||||
@@ -895,7 +961,7 @@ export function registerPublishedRuntimeSettingsTests() {
|
||||
target: { value: '800' },
|
||||
});
|
||||
fireEvent.click(screen.getByRole('button', { name: /Agent 分工/ }));
|
||||
expect(screen.getByText('所有角色使用统一账号服务')).not.toBeNull();
|
||||
expect(screen.getByText('已启用')).not.toBeNull();
|
||||
fireEvent.click(screen.getByRole('button', { name: /连接与工具/ }));
|
||||
expect(screen.queryByLabelText('External Editor Base URL')).toBeNull();
|
||||
expect(screen.queryByLabelText('External Editor API Key')).toBeNull();
|
||||
@@ -943,7 +1009,7 @@ export function registerPublishedRuntimeSettingsTests() {
|
||||
fireEvent.click(screen.getByRole('button', { name: /常用设置/ }));
|
||||
expect(screen.getByLabelText('联网检索')).toHaveProperty('checked', true);
|
||||
fireEvent.click(screen.getByRole('button', { name: /Agent 分工/ }));
|
||||
expect(screen.getByText('所有角色使用统一账号服务')).not.toBeNull();
|
||||
expect(screen.getByText('已启用')).not.toBeNull();
|
||||
expect(screen.getByLabelText('聊天').textContent).not.toContain(
|
||||
'unit-new-secret-value',
|
||||
);
|
||||
@@ -983,9 +1049,9 @@ export function registerPublishedRuntimeSettingsTests() {
|
||||
|
||||
expect(screen.getByText('已恢复默认配置,保存后生效')).not.toBeNull();
|
||||
fireEvent.click(screen.getByRole('button', { name: /常用设置/ }));
|
||||
expect(screen.getByText('陶泥儿智能创作(固定)')).not.toBeNull();
|
||||
expect(screen.getByText('智能创作')).not.toBeNull();
|
||||
expect(screen.queryByLabelText('Agent 模式')).toBeNull();
|
||||
expect(screen.getByText('官方账号服务(固定)')).not.toBeNull();
|
||||
expect(screen.getByText('账号状态')).not.toBeNull();
|
||||
expect(
|
||||
screen.queryByText(/router\.genarrative\.world|gpt-5\.6-sol/),
|
||||
).toBeNull();
|
||||
@@ -995,7 +1061,7 @@ export function registerPublishedRuntimeSettingsTests() {
|
||||
expect(screen.getByLabelText('推理档')).toHaveProperty('value', 'max');
|
||||
expect(screen.getByLabelText('联网检索')).toHaveProperty('checked', true);
|
||||
fireEvent.click(screen.getByRole('button', { name: /Agent 分工/ }));
|
||||
expect(screen.getByText('所有角色使用统一账号服务')).not.toBeNull();
|
||||
expect(screen.getByText('已启用')).not.toBeNull();
|
||||
fireEvent.click(screen.getByRole('button', { name: /高级参数/ }));
|
||||
expect(screen.getByLabelText('上下文窗口 tokens')).toHaveProperty(
|
||||
'value',
|
||||
@@ -1086,7 +1152,7 @@ export function registerPublishedRuntimeSettingsTests() {
|
||||
renderAppAt('/');
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '配置' }));
|
||||
expect(await screen.findByText('官方账号服务(固定)')).not.toBeNull();
|
||||
expect(await screen.findByText('账号状态')).not.toBeNull();
|
||||
expect(screen.queryByLabelText('LLM Provider')).toBeNull();
|
||||
expect(screen.queryByLabelText('LLM API Key')).toBeNull();
|
||||
expect(screen.queryByLabelText('LLM Base URL')).toBeNull();
|
||||
|
||||
Reference in New Issue
Block a user