From cb3fb47561bdf93c4b8f77681ee91aff9065d91d Mon Sep 17 00:00:00 2001 From: cen617-code <1057290604@qq.com> Date: Wed, 9 Sep 2026 13:15:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(web-platform):=20release=20V0.9.4=20?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E8=AE=BE=E8=AE=A1=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 8 + package-lock.json | 4 +- package.json | 2 +- web_platform/UI_CYBER_HUD.md | 48 ++++ web_platform/e2e/app.spec.ts | 4 - web_platform/e2e/ui-cyber-hud.spec.ts | 114 ++++++++ web_platform/e2e/ui-tuning-system.spec.ts | 23 +- web_platform/src/app/App.tsx | 2 +- .../components/SecondBatchComponents.test.tsx | 7 +- .../src/app/components/SidebarPanel.tsx | 2 +- .../src/app/components/SourceEditorDialog.tsx | 2 +- .../src/app/components/ViewportHUD.tsx | 3 +- .../app/components/WorkbenchHeader.test.tsx | 6 +- .../src/app/components/WorkbenchHeader.tsx | 39 +-- .../src/app/components/WorkspaceOverlays.tsx | 92 +++++-- .../app/components/WorkspaceToolsPanel.tsx | 4 + web_platform/src/components/ui/Button.tsx | 5 +- .../src/components/ui/CollapsibleSection.tsx | 2 +- web_platform/src/components/ui/Dialog.tsx | 2 +- web_platform/src/components/ui/IconButton.tsx | 2 +- web_platform/src/components/ui/Popover.tsx | 2 +- web_platform/src/components/ui/Tabs.tsx | 4 +- .../src/simulation/ActuatorControl.test.tsx | 17 ++ .../src/simulation/ActuatorControl.tsx | 22 +- web_platform/src/styles.css | 244 +++++++++++++++--- .../src/training/LocalTrainingPanel.test.tsx | 8 +- .../src/training/LocalTrainingPanel.tsx | 2 +- .../src/tuning/MetricsComparisonBoard.tsx | 2 +- web_platform/src/tuning/TuningApp.tsx | 10 +- web_platform/src/tuning/TuningConsole.tsx | 59 ++--- web_platform/src/tuning/TuningSessionRail.tsx | 4 +- 31 files changed, 563 insertions(+), 182 deletions(-) create mode 100644 web_platform/UI_CYBER_HUD.md create mode 100644 web_platform/e2e/ui-cyber-hud.spec.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c811f6c..6424a438 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ ## [未发布] +## [0.9.4] - 2026-09-09 + +- 前端设计优化:升级蓝黑、电光青与少量紫色的 Cyber HUD 视觉,保留亮色主题;统一工作台、欢迎界面、共享控件、浮层和源码/调参窗口材质。 +- 恢复自调参平台左侧会话与 Trial、中间指标与排行、右侧决策与审批的三栏布局;窄屏纵向排列,保留审批与 Monaco Diff 操作。 +- 增加运行状态呼吸与交互微光,支持减少动态效果、强制色彩和无模糊能力回退;侧栏采用静态材质,避免大面积实时模糊影响仿真性能。 +- 优化欢迎页与源代码入口、执行器控件密度和重复参数提示,将训练地图操作文案统一为“同步场景地图”。 +- 补充双主题材质、状态动效、回退及三栏布局回归测试和设计交付说明;性能数据仅为无头 Chromium 短时特效开关对照,不代表真实 GPU 或重型模型基准。 + ## [0.9.3] - 2026-09-08 - 重构绿色主工作台:紧凑全局入口、可收起资源/上下文侧栏,统一状态、仿真/地图工具、草稿、摄像头与风险槽位;窄屏临时布局不覆盖桌面偏好。 diff --git a/package-lock.json b/package-lock.json index c24f7520..6ee80ef1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mujoco-web-platform", - "version": "0.9.3", + "version": "0.9.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mujoco-web-platform", - "version": "0.9.3", + "version": "0.9.4", "license": "Apache-2.0", "dependencies": { "@monaco-editor/react": "^4.7.0", diff --git a/package.json b/package.json index 9ac2bbd4..2e45619c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mujoco-web-platform", - "version": "0.9.3", + "version": "0.9.4", "description": "基于 MuJoCo WebAssembly 的本地机器人仿真与控制平台", "private": true, "type": "module", diff --git a/web_platform/UI_CYBER_HUD.md b/web_platform/UI_CYBER_HUD.md new file mode 100644 index 00000000..0578fd9a --- /dev/null +++ b/web_platform/UI_CYBER_HUD.md @@ -0,0 +1,48 @@ +# Cyber HUD 界面样式 + +本次直接升级现有 React 界面的 JSX/CSS,不增加静态演示入口、外部字体或依赖,保留亮色主题和所有仿真业务行为。 + +## 源码 + +- `src/styles.css`:蓝黑/电光青语义色、紫色装饰、玻璃材质、交互微光、状态呼吸和可访问性降级。 +- `src/components/ui/{Button,IconButton,Tabs,CollapsibleSection,Dialog,Popover}.tsx`:共享视觉挂点,保留原有键盘交互与 Portal。 +- `src/app/App.tsx` 及 `src/app/components/{WorkbenchHeader,SidebarPanel,ViewportHUD,WorkspaceOverlays,SourceEditorDialog}.tsx`:工作台外壳、欢迎卡片、HUD 和编辑窗口。 +- `src/tuning/{TuningApp,TuningConsole,TuningSessionRail,MetricsComparisonBoard}.tsx`:调参页、导航、指标卡片;Monaco 原生亮/暗代码主题不变。 +- `e2e/ui-cyber-hud.spec.ts`:新增 5 个材质、主题持久化、动效及回退测试。 + +## v0.9.4 调参布局 + +自调参会话页恢复三栏:左侧会话与 Trial、中间指标与排行、右侧决策与审批。宽度达到 1280px 时三栏同时显示并分别滚动,更窄窗口按上述顺序纵向排列。原有主题、审批与 Diff 编辑行为保持不变。 + +## 材质与性能约束 + +- 暗色基底 `#060914` / `#0B1224`,强调 `#36E4F2`,装饰 `#A78BFA`;亮色强调 `#087887`。 +- 毛玻璃仅用于顶栏、欢迎卡片、小型 HUD 和浮层;侧栏使用静态渐变质感,避免长列表滚动时大面积背景模糊。 +- 3.6 秒呼吸动效仅绑定真实仿真运行状态;装饰层不拦截指针。 +- 减少动态效果偏好禁用动画;无背景模糊支持时使用实色表面;强制色彩模式保留清晰边框。 + +## 验证记录 + +- `npm run typecheck`、`npm run lint`、`npm run build` 通过。 +- `npm run test`:97 个测试文件、408 项测试通过。 +- 工作台布局、共享浮层、领域面板、调参系统及新增 HUD 测试:59 项端到端测试通过。 +- 双主题覆盖 1920×1080、1440×900、1366×768、1024×768、768×800;检查了源码/Diff、弹层、主题同步和视口避让。 +- 变更源码的 Prettier 检查与 `git diff --check` 通过。 +- 构建仍提示部分包超过 500kB;本次未改变加载架构。 + +性能取证:Headless Chromium、1440×900、24 刚体场景下按“关闭特效→开启→开启→关闭”采样,每段 2.5 秒并滚动侧栏。初版全侧栏模糊约 54 FPS,取消侧栏实时模糊后开/关特效均约 60 FPS,P95 帧间隔约 16.7ms。 + +**限制**:这是短时特效开关对照,不是完整重构前后的严格基准,也不代表真实桌面 GPU 或重型模型表现;未执行 Firefox/Safari 实机验证。无模糊回退测试通过修改 CSS 能力分支来模拟,不等同于旧浏览器实测。 + +## 本地证据 + +- `../test-results/cyber-hud-delivery/workbench-dark.png` +- `../test-results/cyber-hud-delivery/workbench-light.png` +- `../test-results/cyber-hud-delivery/tuning-dark.png` +- `../test-results/cyber-hud-delivery/tuning-light.png` +- `../test-results/cyber-hud-delivery/before-dark.png` +- `../test-results/cyber-hud-delivery/performance-final.json` + +截图与性能证据为本地生成产物,不纳入源码提交;再次运行 Playwright 可能清理 `test-results/`。详细命令日志保存在 `/tmp/cyber-hud-evidence/`。 + +预览:`npm run dev`,主入口 `/`,独立调参入口 `/tuning.html`。 diff --git a/web_platform/e2e/app.spec.ts b/web_platform/e2e/app.spec.ts index 1898b7c9..faf4693a 100644 --- a/web_platform/e2e/app.spec.ts +++ b/web_platform/e2e/app.spec.ts @@ -264,7 +264,6 @@ test('转换后的 MJCF 可编辑并重新载入', async ({ page }) => { .first() .setInputFiles({ name: 'model.xml', mimeType: 'text/xml', buffer: Buffer.from(SIMPLE_MODEL) }); await expect(page.getByRole('button', { name: '▶ 播放' })).toBeEnabled({ timeout: 30_000 }); - await page.getByRole('button', { name: '工程', exact: true }).click(); await page.getByRole('button', { name: '源代码' }).click(); const dialog = page.getByRole('dialog', { name: '转换后的 MJCF 编辑器' }); await expect(dialog).toBeVisible(); @@ -291,7 +290,6 @@ test('关闭已修改的 MJCF 前要求确认', async ({ page }) => { .first() .setInputFiles({ name: 'model.xml', mimeType: 'text/xml', buffer: Buffer.from(SIMPLE_MODEL) }); await expect(page.getByRole('button', { name: '▶ 播放' })).toBeEnabled({ timeout: 30_000 }); - await page.getByRole('button', { name: '工程', exact: true }).click(); await page.getByRole('button', { name: '源代码' }).click(); const editorDialog = page.getByRole('dialog', { name: '转换后的 MJCF 编辑器' }); await editorDialog.locator('.monaco-editor').click({ position: { x: 240, y: 120 } }); @@ -354,7 +352,6 @@ test('加载引用 OBJ 的 URDF 工程', async ({ page }) => { await expect(notifications).toContainText(/URDF 已转换为 MJCF(浮动基座),并整体平移/); await page.keyboard.press('Escape'); await expect(page.getByLabel('显示碰撞几何')).not.toBeChecked(); - await page.getByRole('button', { name: '工程', exact: true }).click(); await page.getByRole('button', { name: '源代码' }).click(); const sourceDialog = page.getByRole('dialog', { name: '转换后的 MJCF 编辑器' }); await expect(sourceDialog.getByText('缓存文件 · 可编辑')).toBeVisible(); @@ -434,7 +431,6 @@ test('转换后的 MJCF 保存时保留 DAE 转换缓存资源', async ({ page } .getByRole('button', { name: '转换并加载' }) .click(); await expect(page.getByRole('button', { name: '▶ 播放' })).toBeEnabled({ timeout: 30_000 }); - await page.getByRole('button', { name: '工程', exact: true }).click(); await page.getByRole('button', { name: '源代码' }).click(); const dialog = page.getByRole('dialog', { name: '转换后的 MJCF 编辑器' }); await dialog.locator('.monaco-editor').click({ position: { x: 240, y: 120 } }); diff --git a/web_platform/e2e/ui-cyber-hud.spec.ts b/web_platform/e2e/ui-cyber-hud.spec.ts new file mode 100644 index 00000000..700bb089 --- /dev/null +++ b/web_platform/e2e/ui-cyber-hud.spec.ts @@ -0,0 +1,114 @@ +import { expect, test } from '@playwright/test'; + +const MODEL = ` + +`; + +for (const theme of ['dark', 'light']) { + test(`Cyber HUD 材质与可读性 ${theme}`, async ({ page }, info) => { + await page.setViewportSize({ width: 1440, height: 900 }); + await page.addInitScript((value) => { + if (!localStorage.getItem('mujoco-platform-theme')) { + localStorage.setItem('mujoco-platform-theme', value); + } + }, theme); + await page.goto('/'); + const welcome = page.getByRole('region', { name: '导入模型工程' }); + await expect(welcome).toBeVisible(); + await expect(page.locator('.cyber-workspace')).toHaveClass(new RegExp(`theme-${theme}`)); + await expect(welcome).toHaveCSS('backdrop-filter', 'blur(12px) saturate(1.2)'); + const primary = page.getByRole('button', { name: '选择文件', exact: true }); + await primary.focus(); + await expect(primary).toBeFocused(); + await expect(primary).toHaveCSS('outline-style', 'solid'); + // 在真实浏览器中验证主按钮与正文的计算色对比度,而非仅比较 token。 + const contrast = await primary.evaluate((button) => { + function luminance(color: string) { + const rgb = color + .match(/[\d.]+/g)! + .slice(0, 3) + .map(Number); + const linear = rgb.map((value) => { + const v = value / 255; + return v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4; + }); + return linear[0] * 0.2126 + linear[1] * 0.7152 + linear[2] * 0.0722; + } + const style = getComputedStyle(button); + const a = luminance(style.color), + b = luminance(style.backgroundColor); + return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05); + }); + expect(contrast).toBeGreaterThanOrEqual(4.5); + const disabled = page.getByRole('button', { name: '源代码', exact: true }); + await expect(disabled).toBeDisabled(); + await expect(disabled).toHaveCSS('box-shadow', 'none'); + await page.screenshot({ + path: info.outputPath(`workbench-${theme}.png`), + animations: 'disabled', + }); + await page.goto('/tuning.html'); + await expect(page.locator('.cyber-workspace')).toHaveClass(new RegExp(`theme-${theme}`)); + await page + .getByRole('button', { name: `切换到${theme === 'dark' ? '白天' : '黑夜'}主题` }) + .click(); + await page.reload(); + await expect(page.locator('.cyber-workspace')).toHaveClass( + new RegExp(`theme-${theme === 'dark' ? 'light' : 'dark'}`), + ); + }); +} + +test('运行状态呼吸遵循减少动态效果,装饰不拦截交互', async ({ page }) => { + await page.goto('/'); + await page.locator('#mujoco-project-files').setInputFiles({ + name: 'hud.xml', + mimeType: 'text/xml', + buffer: Buffer.from(MODEL), + }); + const play = page.getByRole('button', { name: '▶ 播放' }); + await expect(play).toBeEnabled({ timeout: 30_000 }); + await play.click(); + const signal = page.locator('.hud-signal'); + await expect(signal).toHaveAttribute('data-running', 'true'); + const animation = () => signal.evaluate((el) => getComputedStyle(el, '::after').animationName); + expect(await animation()).toBe('hud-breathe'); + expect(await signal.evaluate((el) => getComputedStyle(el, '::after').pointerEvents)).toBe('none'); + await page.emulateMedia({ reducedMotion: 'reduce' }); + await expect.poll(animation).toBe('none'); + await page.getByRole('button', { name: '⏸ 暂停' }).click(); + await expect(signal).toHaveAttribute('data-running', 'false'); + await page.emulateMedia({ forcedColors: 'active' }); + await expect(page.locator('.engineering-glass').first()).toHaveCSS('backdrop-filter', 'none'); +}); + +for (const theme of ['dark', 'light']) { + test(`无模糊能力时使用实色回退 ${theme}`, async ({ page }) => { + // Chromium 支持模糊:使构建 CSS 的能力分支为 false,验证回退规则本身。 + let replaced = false; + await page.route('**/*.css', async (route) => { + const response = await route.fetch(); + const css = await response.text(); + const fallback = css.replace(/backdrop-filter:\s*blur\(1px\)/g, 'cyber-unsupported: none'); + replaced ||= css !== fallback; + await route.fulfill({ response, body: fallback }); + }); + await page.addInitScript( + (value) => localStorage.setItem('mujoco-platform-theme', value), + theme, + ); + await page.goto('/'); + const welcome = page.getByRole('region', { name: '导入模型工程' }); + await expect(welcome).toBeVisible(); + expect(replaced).toBe(true); + await expect(welcome).toHaveCSS('backdrop-filter', 'none'); + await expect(welcome).toHaveCSS( + 'background-color', + theme === 'dark' ? 'rgb(11, 18, 36)' : 'rgb(251, 252, 254)', + ); + await page.getByRole('button', { name: '打开命令面板' }).click(); + await expect(page.getByRole('dialog', { name: '命令面板' })).toBeVisible(); + await page.keyboard.press('Escape'); + await expect(page.getByRole('dialog', { name: '命令面板' })).toBeHidden(); + }); +} diff --git a/web_platform/e2e/ui-tuning-system.spec.ts b/web_platform/e2e/ui-tuning-system.spec.ts index ee2b2221..332e6afd 100644 --- a/web_platform/e2e/ui-tuning-system.spec.ts +++ b/web_platform/e2e/ui-tuning-system.spec.ts @@ -72,7 +72,24 @@ for (const theme of ['dark', 'light'] as const) await page.getByRole('button', { name: '连接/刷新' }).click(); await expect(page.getByRole('img', { name: /收敛曲线/ })).toBeVisible(); await expect(page.getByRole('button', { name: '停止 Session' })).toBeVisible(); - await expect(page.getByRole('button', { name: /决策与审批 · 待审批 1/ })).toBeVisible(); + const sessions = page.getByRole('region', { name: '会话与 Trial' }); + const metrics = page.getByRole('main', { name: '指标与排行' }); + const decisions = page.getByRole('complementary', { name: '决策与审批' }); + await expect(sessions).toBeVisible(); + await expect(metrics).toBeVisible(); + await expect(decisions).toBeVisible(); + const left = (await sessions.boundingBox())!; + const center = (await metrics.boundingBox())!; + const right = (await decisions.boundingBox())!; + if (width >= 1280) { + expect(Math.abs(left.y - center.y)).toBeLessThan(2); + expect(Math.abs(center.y - right.y)).toBeLessThan(2); + expect(left.x + left.width).toBeLessThanOrEqual(center.x + 1); + expect(center.x + center.width).toBeLessThanOrEqual(right.x + 1); + } else { + expect(left.y + left.height).toBeLessThanOrEqual(center.y + 1); + expect(center.y + center.height).toBeLessThanOrEqual(right.y + 1); + } await page.getByRole('button', { name: '收敛曲线放大', exact: true }).click(); await page .getByRole('button', { name: `切换到${theme === 'dark' ? '白天' : '黑夜'}主题` }) @@ -88,12 +105,11 @@ for (const theme of ['dark', 'light'] as const) await page.getByRole('heading', { name: 'Trial Leaderboard' }).scrollIntoViewIfNeeded(); await noOverflow(page); await page.screenshot({ path: info.outputPath('tuning-leaderboard.png') }); - await page.getByRole('button', { name: '会话与 Trial' }).click(); + await page.getByRole('button', { name: '导入', exact: true }).scrollIntoViewIfNeeded(); await expect(page.getByRole('button', { name: '导入', exact: true })).toBeVisible(); await expect(page.getByText('导入会替换主工作台策略;无主窗口时下载文件。')).toBeVisible(); await noOverflow(page); await page.screenshot({ path: info.outputPath('tuning-sessions.png') }); - await page.getByRole('button', { name: /决策与审批/ }).click(); await page.getByRole('button', { name: 'Monaco Diff 审查' }).scrollIntoViewIfNeeded(); await noOverflow(page); await page.screenshot({ path: info.outputPath('tuning-decisions.png') }); @@ -204,7 +220,6 @@ for (const theme of ['dark', 'light'] as const) await expect(page.getByRole('dialog', { name: '诊断与事件日志' })).toBeVisible(); await page.screenshot({ path: info.outputPath('diagnostics.png') }); await page.keyboard.press('Escape'); - await page.getByRole('button', { name: '工程', exact: true }).click(); await page.getByRole('button', { name: '源代码', exact: true }).click(); const source = page.getByRole('dialog', { name: '转换后的 MJCF 编辑器' }); await expect(source.locator('.monaco-editor')).toBeVisible(); diff --git a/web_platform/src/app/App.tsx b/web_platform/src/app/App.tsx index 84d5db59..1e8a74b2 100644 --- a/web_platform/src/app/App.tsx +++ b/web_platform/src/app/App.tsx @@ -2492,7 +2492,7 @@ export function App() { return (
{ it('空工作区解释导入到仿真的三步流程', () => { render(); expect(screen.getByRole('region', { name: '导入模型工程' })).toBeVisible(); - fireEvent.click(screen.getByText('导入与仿真流程')); + expect(screen.getByText('Local Simulation Workspace')).toBeVisible(); + expect(screen.getByText('本地解析、编译并运行机器人模型,无需上传资源')).toBeVisible(); + expect(screen.getByLabelText('支持格式')).toHaveTextContent('OBJ / STL / DAE'); + expect(screen.getByRole('button', { name: '选择文件' })).toBeVisible(); + expect(screen.getByRole('button', { name: '选择文件夹' })).toBeVisible(); + expect(screen.queryByText('导入与仿真流程')).not.toBeInTheDocument(); expect(screen.getByRole('list', { name: '仿真工作流程' })).toHaveTextContent('导入'); expect(screen.getByRole('list', { name: '仿真工作流程' })).toHaveTextContent('检查与配置'); expect(screen.getByRole('list', { name: '仿真工作流程' })).toHaveTextContent('运行与调试'); diff --git a/web_platform/src/app/components/SidebarPanel.tsx b/web_platform/src/app/components/SidebarPanel.tsx index 74dfd2e1..b1715135 100644 --- a/web_platform/src/app/components/SidebarPanel.tsx +++ b/web_platform/src/app/components/SidebarPanel.tsx @@ -18,7 +18,7 @@ export function SidebarPanel({ return (
); } diff --git a/web_platform/src/app/components/WorkspaceToolsPanel.tsx b/web_platform/src/app/components/WorkspaceToolsPanel.tsx index aeb7e1e7..a4703ab5 100644 --- a/web_platform/src/app/components/WorkspaceToolsPanel.tsx +++ b/web_platform/src/app/components/WorkspaceToolsPanel.tsx @@ -135,10 +135,14 @@ export function WorkspaceToolsPanel({ keepMounted badge={} > + {snapshot.actuators.some((actuator) => ['motor', 'position'].includes(actuator.kind)) && ( +

参数立即作用于模型,可随 MJCF 导出。

+ )} {snapshot.actuators.length ? ( snapshot.actuators.map((actuator) => ( onActuator(actuator.id, value)} onParameters={(parameters) => onActuatorParameters(actuator.id, parameters)} diff --git a/web_platform/src/components/ui/Button.tsx b/web_platform/src/components/ui/Button.tsx index 530335eb..72453df3 100644 --- a/web_platform/src/components/ui/Button.tsx +++ b/web_platform/src/components/ui/Button.tsx @@ -20,7 +20,8 @@ export function Button({ secondary: 'border-border bg-surface text-text-primary hover:bg-element-hover', ghost: 'border-transparent bg-transparent text-text-secondary hover:bg-element-hover hover:text-text-primary', - danger: 'border-danger-border bg-danger-soft text-danger hover:bg-danger hover:text-white', + danger: + 'border-danger-border bg-danger-soft text-danger hover:bg-danger hover:text-[var(--ui-danger-contrast)]', }; const sizes = { sm: 'h-control gap-1.5 rounded-md px-2 text-xs', @@ -30,7 +31,7 @@ export function Button({ return ( {terrainPreset === 'custom_boxes' && customTerrainBoxes && !sceneDirty && (

diff --git a/web_platform/src/tuning/MetricsComparisonBoard.tsx b/web_platform/src/tuning/MetricsComparisonBoard.tsx index e84d1371..3c83cb38 100644 --- a/web_platform/src/tuning/MetricsComparisonBoard.tsx +++ b/web_platform/src/tuning/MetricsComparisonBoard.tsx @@ -245,7 +245,7 @@ function MultiTrialPlot({ }; return ( -

+

diff --git a/web_platform/src/tuning/TuningApp.tsx b/web_platform/src/tuning/TuningApp.tsx index 179caa5c..3b2b4845 100644 --- a/web_platform/src/tuning/TuningApp.tsx +++ b/web_platform/src/tuning/TuningApp.tsx @@ -77,9 +77,9 @@ export function TuningApp() { return (

-
+

@@ -120,7 +120,7 @@ export function TuningApp() {

{!sessionId && ( -
+
-
+

新建 {taskId} 调参 Session

@@ -456,7 +456,7 @@ function NewSessionView({
-
+

历史 Sessions

{sessions.length} diff --git a/web_platform/src/tuning/TuningConsole.tsx b/web_platform/src/tuning/TuningConsole.tsx index fcb7bd18..581a4dfe 100644 --- a/web_platform/src/tuning/TuningConsole.tsx +++ b/web_platform/src/tuning/TuningConsole.tsx @@ -1,5 +1,3 @@ -import { useState } from 'react'; -import { Button } from '../components/ui'; import { PretrainedIdentity } from '../training/PretrainedSourceSelect'; import { useTuningStore } from './tuningStore'; import { AgentDecisionTimeline } from './AgentDecisionTimeline'; @@ -12,59 +10,34 @@ import { useTuningPolling } from './useTuningPolling'; export function TuningConsole() { useTuningPolling(); const pretrained = useTuningStore((state) => state.sessionConfig?.pretrained); - const pending = useTuningStore( - (state) => - state.proposalIds.filter((id) => state.proposalsById[id]?.state === 'pending').length, - ); - const [side, setSide] = useState<'sessions' | 'decisions' | null>(null); return ( -
+
- -
-
+ +
+
-
- - -
+ +
); diff --git a/web_platform/src/tuning/TuningSessionRail.tsx b/web_platform/src/tuning/TuningSessionRail.tsx index 20c74483..eee29f8b 100644 --- a/web_platform/src/tuning/TuningSessionRail.tsx +++ b/web_platform/src/tuning/TuningSessionRail.tsx @@ -37,7 +37,7 @@ function AshaPromotionTree() { }); void entitiesRevision; return ( -
+

ASHA 晋级树 @@ -164,7 +164,7 @@ export function TuningSessionRail() { void entitiesRevision; return ( -