优化运行视窗播放入口
将桌面运行视窗播放按钮移至顶部中间 资源管理点击播放后直接启动本地预览并切换运行视窗 更新播放链路回归测试与工作台规则
This commit is contained in:
@@ -1141,6 +1141,9 @@ export function App({
|
||||
const initialProjectOpenedRef = useRef(false);
|
||||
const pendingUiConfirmationActionRef = useRef<(() => void) | null>(null);
|
||||
const queueRunLocalShortcutRef = useRef<() => void>(() => undefined);
|
||||
const executeRunLocalRef = useRef<(announceToChat: boolean) => void>(
|
||||
() => undefined,
|
||||
);
|
||||
|
||||
function requestRuntimeConfigOpen() {
|
||||
if (projectSupervisorOnly && !supervisorChatOnly && !gameChatOnly) {
|
||||
@@ -3045,6 +3048,7 @@ export function App({
|
||||
]);
|
||||
}
|
||||
queueRunLocalShortcutRef.current = queueRunLocalShortcut;
|
||||
executeRunLocalRef.current = executeRunLocal;
|
||||
|
||||
useEffect(() => {
|
||||
const nextProjectPath = localProject?.projectPath;
|
||||
@@ -3062,7 +3066,7 @@ export function App({
|
||||
}
|
||||
handledPlayRequestRef.current = requestKey;
|
||||
onPlayRequestHandled?.(playRequest.requestId);
|
||||
queueRunLocalShortcutRef.current();
|
||||
void executeRunLocalRef.current(true);
|
||||
}, [
|
||||
localProject?.projectPath,
|
||||
onPlayRequestHandled,
|
||||
|
||||
@@ -5087,6 +5087,7 @@ iframe.preview-frame {
|
||||
}
|
||||
|
||||
.game-workbench-toolbar {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -5183,6 +5184,9 @@ iframe.preview-frame {
|
||||
}
|
||||
|
||||
.game-workbench-view-actions .game-workbench-play-button {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border-color: var(--platform-button-primary-border);
|
||||
background: var(--platform-button-primary-fill);
|
||||
color: var(--platform-button-primary-text);
|
||||
@@ -7377,6 +7381,11 @@ iframe.preview-frame {
|
||||
.game-workbench-view-actions {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.game-workbench-view-actions .game-workbench-play-button {
|
||||
position: static;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
@@ -7407,6 +7416,11 @@ iframe.preview-frame {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.game-workbench-view-actions .game-workbench-play-button {
|
||||
position: static;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.game-workbench-tabs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
|
||||
@@ -773,7 +773,9 @@ export function registerProjectWorkbenchFoundationTests() {
|
||||
'workbench-art-viewport-memory',
|
||||
'美术资源视口记忆',
|
||||
);
|
||||
const codeTask = manifest.tasks.find((task) => task.id === 'code-prototype');
|
||||
const codeTask = manifest.tasks.find(
|
||||
(task) => task.id === 'code-prototype',
|
||||
);
|
||||
expect(codeTask).toBeDefined();
|
||||
codeTask!.status = 'completed';
|
||||
manifest.assets = [
|
||||
@@ -1071,7 +1073,9 @@ export function registerProjectWorkbenchFoundationTests() {
|
||||
).not.toBeNull();
|
||||
fireEvent.click(within(outline).getByRole('button', { name: /设计文档/ }));
|
||||
|
||||
dispatchPageWheel(screen.getByRole('button', { name: /下一页\s*美术资源/ }));
|
||||
dispatchPageWheel(
|
||||
screen.getByRole('button', { name: /下一页\s*美术资源/ }),
|
||||
);
|
||||
expect(
|
||||
screen.getByRole('region', { name: '美术资源资源画布' }),
|
||||
).not.toBeNull();
|
||||
@@ -1134,9 +1138,7 @@ export function registerProjectWorkbenchFoundationTests() {
|
||||
name: /打开资源详情:设计文档 section\.md/,
|
||||
}),
|
||||
);
|
||||
expect(
|
||||
screen.getByRole('dialog', { name: 'section.md' }),
|
||||
).not.toBeNull();
|
||||
expect(screen.getByRole('dialog', { name: 'section.md' })).not.toBeNull();
|
||||
dispatchPageWheel(dependencyCanvas);
|
||||
expect(
|
||||
screen.getByRole('region', { name: '美术资源资源画布' }),
|
||||
@@ -1278,9 +1280,7 @@ export function registerProjectWorkbenchFoundationTests() {
|
||||
}),
|
||||
);
|
||||
});
|
||||
fireEvent.click(
|
||||
within(outline).getByRole('button', { name: /项目版本/ }),
|
||||
);
|
||||
fireEvent.click(within(outline).getByRole('button', { name: /项目版本/ }));
|
||||
await act(async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 220));
|
||||
});
|
||||
@@ -1288,9 +1288,7 @@ export function registerProjectWorkbenchFoundationTests() {
|
||||
screen.getByRole('region', { name: '项目版本资源画布' }),
|
||||
).not.toBeNull();
|
||||
|
||||
fireEvent.click(
|
||||
within(outline).getByRole('button', { name: /设计文档/ }),
|
||||
);
|
||||
fireEvent.click(within(outline).getByRole('button', { name: /设计文档/ }));
|
||||
act(() => {
|
||||
dependencyCanvas.dispatchEvent(
|
||||
new WheelEvent('wheel', {
|
||||
@@ -3704,10 +3702,7 @@ export function registerProjectWorkbenchFoundationTests() {
|
||||
'[data-resource-viewport]',
|
||||
);
|
||||
const viewportScale = Number(
|
||||
viewportElement
|
||||
?.getAttribute('data-resource-viewport')
|
||||
?.split(',')
|
||||
.at(2),
|
||||
viewportElement?.getAttribute('data-resource-viewport')?.split(',').at(2),
|
||||
);
|
||||
expect(viewportScale).toBeGreaterThan(0);
|
||||
const expectedX = Math.round(12 + (-100 - 20) / viewportScale);
|
||||
@@ -4727,7 +4722,7 @@ export function registerUserSurfaceBoundaryTests() {
|
||||
}
|
||||
|
||||
export function registerProjectSupervisorSurfaceTests() {
|
||||
it('routes a top workbench play request into the existing game.run_local confirmation', async () => {
|
||||
it('runs a top workbench play request without a second confirmation', async () => {
|
||||
const projectPath = '/tmp/top-play-request';
|
||||
const supervisorHarness = createProjectSupervisorRuntimeHarness({
|
||||
projectPath,
|
||||
@@ -4745,6 +4740,19 @@ export function registerProjectSupervisorSurfaceTests() {
|
||||
recentRunStopReason: null,
|
||||
};
|
||||
}
|
||||
if (command === 'start_local_game_preview') {
|
||||
return {
|
||||
url: 'http://127.0.0.1:43127/',
|
||||
port: 43127,
|
||||
root: projectPath,
|
||||
};
|
||||
}
|
||||
if (command === 'get_local_game_manifest') {
|
||||
return createGameCreationAppManifest(
|
||||
'top-play-request',
|
||||
'顶部播放请求',
|
||||
);
|
||||
}
|
||||
return supervisorHarness.invoke(command, args);
|
||||
},
|
||||
);
|
||||
@@ -4764,14 +4772,17 @@ export function registerProjectSupervisorSurfaceTests() {
|
||||
);
|
||||
|
||||
expect(
|
||||
await screen.findByText('game.run_local', {}, { timeout: 3_000 }),
|
||||
await screen.findByText(
|
||||
/运行通过,已载入客户端运行视图/,
|
||||
{},
|
||||
{ timeout: 3_000 },
|
||||
),
|
||||
).not.toBeNull();
|
||||
expect(handled).toHaveBeenCalledWith(7);
|
||||
expect(screen.getByText(`启动 ${projectPath}/game/`)).not.toBeNull();
|
||||
expect(invoke).not.toHaveBeenCalledWith(
|
||||
'start_local_game_preview',
|
||||
expect.anything(),
|
||||
);
|
||||
expect(invoke).toHaveBeenCalledWith('start_local_game_preview', {
|
||||
projectPath,
|
||||
});
|
||||
expect(screen.queryByText('game.run_local')).toBeNull();
|
||||
});
|
||||
|
||||
it('starts a direct product preview without the legacy Canvas-only static smoke', async () => {
|
||||
@@ -4812,9 +4823,6 @@ export function registerProjectSupervisorSurfaceTests() {
|
||||
}),
|
||||
);
|
||||
|
||||
expect(await screen.findByText('game.run_local')).not.toBeNull();
|
||||
fireEvent.click(screen.getByRole('button', { name: '确认' }));
|
||||
|
||||
expect(
|
||||
await screen.findByText(
|
||||
'运行通过,已载入客户端运行视图:http://127.0.0.1:43126/',
|
||||
@@ -9961,7 +9969,8 @@ export function registerProjectSupervisorSurfaceTests() {
|
||||
projectPath,
|
||||
});
|
||||
let directTurnUpdateHandler:
|
||||
((event: { payload: Record<string, unknown> }) => void) | null = null;
|
||||
| ((event: { payload: Record<string, unknown> }) => void)
|
||||
| null = null;
|
||||
const listen = vi.fn(
|
||||
async (
|
||||
eventName: string,
|
||||
@@ -10919,7 +10928,8 @@ export function registerProjectAgentStatusTests() {
|
||||
},
|
||||
);
|
||||
let runtimeUpdateHandler:
|
||||
((event: { payload: Record<string, unknown> }) => void) | null = null;
|
||||
| ((event: { payload: Record<string, unknown> }) => void)
|
||||
| null = null;
|
||||
const listen = vi.fn(
|
||||
async (
|
||||
eventName: string,
|
||||
|
||||
@@ -116,6 +116,7 @@
|
||||
- 资源总览的“资源依赖 / 资源类型”视图切换使用连通的分段按钮组,相邻选项共享边界并保持唯一选中语义。每个分段都必须有清晰的键盘焦点指示,焦点环不得被分段容器的圆角或 `overflow` 裁切。
|
||||
- 右侧 Supervisor 对话中,用户消息使用右对齐、最大宽度受限的主题暖色气泡,assistant 消息保持左对齐;消息换行不得产生水平溢出,执行过程卡继续占满消息区可用宽度。气泡正文在 light / dark 平台主题下均须满足 WCAG AA 普通文本 `4.5:1` 对比度。
|
||||
- 客户端正式产品仍只按最小 `1280×720` 横屏合同交付,并保留 `1280×800` 默认窗口与既有基线验收;更窄浏览器样式只负责不崩溃和开发兼容,不改成移动端创作工作台。
|
||||
- 工作台顶部播放按钮在桌面视窗中水平居中;资源管理视窗触发播放时直接切换到运行视窗并启动本地预览,不再弹出 `game.run_local` 二次确认。
|
||||
- 普通用户界面不默认展示内部错误码或本机绝对路径,也不展示 External Editor Base URL 或 Developer API Key;官方服务地址由客户端构建固定,远端编辑自动使用当前陶泥儿登录态。仅独立 standalone game-chat release 或显式高级自定义模式在隔离配置页维护 External v1 URL/Key,确需诊断的信息进入受控详情或开发模式。
|
||||
- 创建模式素材画布的“素材名称”是用户可编辑的正式输出名称;“资源用途”是 manifest subtype,不向普通用户开放自由文本。新增资源默认“普通游戏美术”,可从普通游戏美术、统一视觉规范、游戏界面原型、核心美术图集四项中选择。图片精修继承源名称和用途,不显示创建模式保存设置;候选图片只从选中图片的“设为最终图”提交。精修顶栏只保留返回、导入、定位当前最终图、撤销和重做,删除进入图片上下文工具栏,通用 AI 生成只保留给创建模式。图片输出统一使用 PNG;创建模式工具动作与保存设置分层展示,“保存到项目”在 `1280×800` 和窄容器中都必须完整可见。
|
||||
- 首页创作输入区与“最近项目”之间不展示共享项目状态文本,“最近项目”标题下也不追加解释性副标题;默认、成功、进行中或失败状态均不得在该位置形成文字行,项目管理页继续保留自己的状态反馈。
|
||||
|
||||
Reference in New Issue
Block a user