diff --git a/AGENTS.md b/AGENTS.md index 6c0ed0d..5262625 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,8 +4,8 @@ This repository contains the Vite and React single-page frontend for the Agent-driven supply-network WebGIS. Runtime TypeScript lives under `src/`. -The application uses React 19, TypeScript, Tailwind CSS, MapLibre GL, deck.gl, -SWR, Vitest, and Playwright. +The application uses React 19, TypeScript, Tailwind CSS, MapLibre GL, SWR, +Vitest, and Playwright. Keep this project browser-first. Do not add Next.js APIs, `"use client"` directives, or general backend-for-frontend routes. Agent reasoning and tool diff --git a/README.md b/README.md index 06e58ff..d6afd80 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Next TJWater 是供水管网 WebGIS Agent 工作台的前端项目。界面以地图为主视图,集成供水管网图层、调度状态、事件处置、Agent 对话和受控前端动作。 -项目使用 Vite、React 19、TypeScript 和 Tailwind CSS 4。地图基于 MapLibre GL,流向效果使用 deck.gl,Agent 消息支持代码高亮、数学公式和 Mermaid 图表。 +项目使用 Vite、React 19、TypeScript 和 Tailwind CSS 4。地图与流向效果基于 MapLibre GL,Agent 消息支持代码高亮、数学公式和 Mermaid 图表。 ## 主要功能 @@ -87,4 +87,4 @@ src/ ## 构建说明 -生产构建可能提示 deck.gl 和 luma.gl 的第三方循环分块,以及 Shiki、Mermaid 语言包超过 Rollup 默认的 500 kB 阈值。当前构建和浏览器测试可以正常完成,这些提示不影响产物生成。 +生产构建可能提示 Shiki、Mermaid 语言包超过 Rollup 默认的 500 kB 阈值。当前构建和浏览器测试可以正常完成,这些提示不影响产物生成。 diff --git a/docs/status-language.md b/docs/status-language.md new file mode 100644 index 0000000..42903c4 --- /dev/null +++ b/docs/status-language.md @@ -0,0 +1,40 @@ +# 状态语言 + +系统使用五种语义状态。业务模块保留自己的领域枚举,并在展示边界映射到共享 +`StatusTone`,不要让展示文案、接口字符串或 Tailwind 原色类直接决定状态样式。 + +| Tone | 用途 | 典型状态 | +| --- | --- | --- | +| `neutral` | 尚未开始或不再活动 | 预约、草案、待办、取消、中止、跳过、未分级 | +| `info` | 正在发生的过程 | 执行中、提交中、加载中、连接中 | +| `success` | 已完成或健康 | 完成、在线、正常、已批准 | +| `warning` | 需要关注但未达到异常 | 关注、降级、待复核、待复令、待确认 | +| `danger` | 已异常或操作失败 | 异常、失败、离线、高风险、事故、权限拒绝 | + +## 报告风险 + +报告风险固定为三档: + +- `normal` 映射 `success`,显示“正常”。 +- `attention` 映射 `warning`,显示“关注”。 +- `critical` 映射 `danger`,显示“异常”。 + +`info` 只表示过程,不参与风险等级。不得把 `attention` 合并到正常或异常。 + +## 表达规则 + +- 状态标签使用 `StatusBadge`,小型指示使用 `StatusDot`,图标节点使用 `StatusIcon`。 +- 大面积容器只使用柔和背景,禁止实心红色或橙色状态卡片。 +- `warning` 和 `danger` 必须同时提供文字或图标,不能只依赖颜色。 +- 只有进行中的状态可以使用 `live` 或 `loading` 动效。 +- “复令”是提交处置结果的动作,执行完成但尚未提交时显示“待复令”,使用静态 + `warning`;不得继续显示为“执行中”或提前显示为完成态。 +- 动效必须在 `prefers-reduced-motion` 下停止。 +- 蓝色选中态属于交互反馈,不作为业务风险状态。 +- 紫色只表示 Agent 身份,不表示未知或更高风险。 + +## 地图状态 + +地图与面板共享语义,但保留独立色值以适应浅色底图、影像底图和白色 casing。 +新增地图状态时,应先补充 `map-colors.ts`,再通过对比度测试验证,不要在图层定义中 +写入新的颜色字面量。 diff --git a/index.html b/index.html index ac492c5..1be7703 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,13 @@ - TJWater WebGIS Agent + + 供水管网
diff --git a/package.json b/package.json index 9a28d69..6484e37 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,6 @@ }, "dependencies": { "@ai-sdk/react": "^4.0.9", - "@deck.gl/core": "^9.3.7", - "@deck.gl/layers": "^9.3.7", - "@deck.gl/mapbox": "^9.3.7", "@fontsource-variable/noto-sans-sc": "5.3.0", "@radix-ui/react-accordion": "^1.2.14", "@radix-ui/react-collapsible": "^1.1.14", @@ -73,14 +70,13 @@ "@types/react": "^19.0.2", "@types/react-dom": "^19.0.2", "@vitejs/plugin-react-swc": "^4.0.2", + "esbuild": "^0.28.0", "eslint": "^9.17.0", "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.16", - "esbuild": "^0.28.0", "globals": "^15.14.0", "jsdom": "^25.0.1", "msw": "^2.6.8", - "playwright": "^1.61.1", "prettier": "^3.4.2", "tailwindcss": "^4.1.17", "tw-animate-css": "^1.4.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 344dda0..345db01 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,15 +11,6 @@ importers: '@ai-sdk/react': specifier: ^4.0.9 version: 4.0.34(react@19.2.7)(zod@3.25.76) - '@deck.gl/core': - specifier: ^9.3.7 - version: 9.3.7 - '@deck.gl/layers': - specifier: ^9.3.7 - version: 9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))) - '@deck.gl/mapbox': - specifier: ^9.3.7 - version: 9.3.7(@deck.gl/core@9.3.7)(@luma.gl/core@9.3.6)(@math.gl/web-mercator@4.1.0) '@fontsource-variable/noto-sans-sc': specifier: 5.3.0 version: 5.3.0 @@ -192,9 +183,6 @@ importers: msw: specifier: ^2.6.8 version: 2.15.0(@types/node@24.13.3)(typescript@7.0.2) - playwright: - specifier: ^1.61.1 - version: 1.61.1 prettier: specifier: ^3.4.2 version: 3.9.5 @@ -457,24 +445,6 @@ packages: resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} - '@deck.gl/core@9.3.7': - resolution: {integrity: sha512-l+YPm1I0Fwj+b7xsu8FvyDT5MGPgEPJe2cQJUpQNgG742b2goB4tyYpteUhNAOeVo1gmenvqv36ZJGNsGxipJA==} - - '@deck.gl/layers@9.3.7': - resolution: {integrity: sha512-T2CnkEU1QEqmusrSdot7+8Nl09gzntG5DvhaogyBbTjmABhTh4rUnS6dLOjf5/ju3aJ+Q58/FvDzgBBcK4V7kg==} - peerDependencies: - '@deck.gl/core': ~9.3.0 - '@loaders.gl/core': ^4.4.3 - '@luma.gl/core': ~9.3.3 - '@luma.gl/engine': ~9.3.3 - - '@deck.gl/mapbox@9.3.7': - resolution: {integrity: sha512-hamgwBS2BbB8DHOjYWwPMrFki+KhcedjoHv/9cUpQHnjfXUswzmyhAURuyowO/tAVJb3vF+CmMzaDVUTlI40Sw==} - peerDependencies: - '@deck.gl/core': ~9.3.0 - '@luma.gl/core': ~9.3.3 - '@math.gl/web-mercator': ^4.1.0 - '@esbuild/aix-ppc64@0.28.1': resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} engines: {node: '>=18'} @@ -764,49 +734,6 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@loaders.gl/core@4.4.3': - resolution: {integrity: sha512-D6kXFdpUtYwoqS5OQLLaLELkHlS3qIdcSgzVvsh/MinRteeVIITJojqEc/lmDx1zVR3j6WND7yPGF4i4aOwiOg==} - - '@loaders.gl/images@4.4.3': - resolution: {integrity: sha512-a4C6x+4GZUchf+IwpdBOvyGeLGSbGOVob+WSWfGUezEQ4gqu1zS1pn3lCX3ZTL5P+Ch3v6KKwPGdO5YdTd52Ew==} - peerDependencies: - '@loaders.gl/core': ~4.4.0 - - '@loaders.gl/loader-utils@4.4.3': - resolution: {integrity: sha512-5yt1OqZPYR3d+xFAqtzSKO24Hd7019KqN+iOWp7XBKPQBS+sAEhAnmSTnO1axo45IEi14J3FhMDk+ndqBoSKEQ==} - - '@loaders.gl/schema-utils@4.4.3': - resolution: {integrity: sha512-TggzQWkzf9pIYbj8I9RlTk33kwXslvDkY1nnw/OL/hika6qTls1G127qK5m9skiAjElk5ZkvQLQcwhOboZXZeA==} - peerDependencies: - '@loaders.gl/core': ~4.4.0 - - '@loaders.gl/schema@4.4.3': - resolution: {integrity: sha512-tDb/rOn44nHWvMFSOaAk1/pQcQcusxirGCM1O5BMJR0PQ71UrCHey56rsbcf7/wGhvyRwPtX+oiMzlJ9TmN8vQ==} - - '@loaders.gl/worker-utils@4.4.3': - resolution: {integrity: sha512-RZt/NwFXm6Kx3Fn/rqiW3Alse9Z0XtBktH/EFAp8W6LvsKuxpRMjULqGfybTu2waRXRYUNYZ6zGs9aOWbtDRLw==} - peerDependencies: - '@loaders.gl/core': ~4.4.0 - - '@luma.gl/core@9.3.6': - resolution: {integrity: sha512-eqHnCPh2xHYkdd9rEkiIIkGMixNiJkq1ROrnTob6npvmZNVHXL0ubIw5KueL7rqW0+J1tm+p2+TXZaCmn0sP7Q==} - - '@luma.gl/engine@9.3.6': - resolution: {integrity: sha512-NYTdhn2NaH/MjN8qXCl2ukrT1Ac9iTKu1mgN0wz11XRd1QYnlMNBXswRROD7IZ2PUsBWdmYHc9qE8wKBQqMuIA==} - peerDependencies: - '@luma.gl/core': ~9.3.0 - '@luma.gl/shadertools': ~9.3.0 - - '@luma.gl/shadertools@9.3.6': - resolution: {integrity: sha512-dDuD8lCOkAE1L7hJGZEyZAi7upR1HG3wEEIQ1gCLaTTbJWC7tNtnVz853lqUA+VXgjgS9NiQFFRcosiIZmW4Vg==} - peerDependencies: - '@luma.gl/core': ~9.3.0 - - '@luma.gl/webgl@9.3.6': - resolution: {integrity: sha512-tGm7FGWPmJKxGZMvkRPRi219x3tKmBxR7Uke09nTp2ujNak/O5V9xPIQ9lUBGTxqAb8U2yjKkXG8j+f/4b2+sw==} - peerDependencies: - '@luma.gl/core': ~9.3.0 - '@mapbox/geojson-rewind@0.5.2': resolution: {integrity: sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==} hasBin: true @@ -835,21 +762,6 @@ packages: resolution: {integrity: sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==} hasBin: true - '@math.gl/core@4.1.0': - resolution: {integrity: sha512-FrdHBCVG3QdrworwrUSzXIaK+/9OCRLscxI2OUy6sLOHyHgBMyfnEGs99/m3KNvs+95BsnQLWklVfpKfQzfwKA==} - - '@math.gl/polygon@4.1.0': - resolution: {integrity: sha512-YA/9PzaCRHbIP5/0E9uTYrqe+jsYTQoqoDWhf6/b0Ixz8bPZBaGDEafLg3z7ffBomZLacUty9U3TlPjqMtzPjA==} - - '@math.gl/sun@4.1.0': - resolution: {integrity: sha512-i3q6OCBLSZ5wgZVhXg+X7gsjY/TUtuFW/2KBiq/U1ypLso3S4sEykoU/MGjxUv1xiiGtr+v8TeMbO1OBIh/HmA==} - - '@math.gl/types@4.1.0': - resolution: {integrity: sha512-clYZdHcmRvMzVK5fjeDkQlHUzXQSNdZ7s4xOqC3nJPgz4C/TZkUecTo9YS4PruZqtDda/ag4erndP0MIn40dGA==} - - '@math.gl/web-mercator@4.1.0': - resolution: {integrity: sha512-HZo3vO5GCMkXJThxRJ5/QYUYRr3XumfT8CzNNCwoJfinxy5NtKUd7dusNTXn7yJ40UoB8FMIwkVwNlqaiRZZAw==} - '@mermaid-js/parser@1.2.0': resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==} @@ -877,15 +789,6 @@ packages: engines: {node: '>=18'} hasBin: true - '@probe.gl/env@4.1.1': - resolution: {integrity: sha512-+68seNDMVsEegRB47pFA/Ws1Fjy8agcFYXxzorKToyPcD6zd+gZ5uhwoLd7TzsSw6Ydns//2KEszWn+EnNHTbA==} - - '@probe.gl/log@4.1.1': - resolution: {integrity: sha512-kcZs9BT44pL7hS1OkRGKYRXI/SN9KejUlPD+BY40DguRLzdC5tLG/28WGMyfKdn/51GT4a0p+0P8xvDn1Ez+Kg==} - - '@probe.gl/stats@4.1.1': - resolution: {integrity: sha512-4VpAyMHOqydSvPlEyHwXaE+AkIdR03nX+Qhlxsk2D/IW4OVmDZgIsvJB1cDzyEEtcfKcnaEbfXeiPgejBceT6g==} - '@radix-ui/number@1.1.2': resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==} @@ -1714,12 +1617,6 @@ packages: '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - '@types/command-line-args@5.2.3': - resolution: {integrity: sha512-uv0aG6R0Y8WHZLTamZwtfsDLVRnOa+n+n5rEvFWL5Na5gZ8V2Teab/duDPFzIIIhs9qizDpcavCusCLJZu62Kw==} - - '@types/command-line-usage@5.0.4': - resolution: {integrity: sha512-BwR5KP3Es/CSht0xqBcUXS3qCAUVXwpRKsV2+arxeb65atasuXG9LykC9Ab10Cw3s2raH92ZqOeILaQbsB2ACg==} - '@types/d3-array@3.2.2': resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} @@ -1855,9 +1752,6 @@ packages: '@types/node@24.13.3': resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==} - '@types/offscreencanvas@2019.7.3': - resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==} - '@types/pbf@3.0.5': resolution: {integrity: sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==} @@ -2090,10 +1984,6 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - apache-arrow@21.1.0: - resolution: {integrity: sha512-kQrYLxhC+NTVVZ4CCzGF6L/uPVOzJmD1T3XgbiUnP7oTeVFOFgEUu6IKNwCDkpFoBVqDKQivlX4RUFqqnWFlEA==} - hasBin: true - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -2108,10 +1998,6 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} - array-back@6.2.3: - resolution: {integrity: sha512-SGDvmg6QTYiTxCBkYVmThcoa67uLl35pyzRHdpCGBOcqFy6BtwnphoFPk7LhJshD+Yk1Kt35WGWeZPTgwR4Fhw==} - engines: {node: '>=12.17'} - assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -2156,10 +2042,6 @@ packages: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} - chalk-template@0.4.0: - resolution: {integrity: sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==} - engines: {node: '>=12'} - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2211,19 +2093,6 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - command-line-args@6.0.2: - resolution: {integrity: sha512-AIjYVxrV9X752LmPDLbVYv8aMCuHPSLZJXEo2qo/xJfv+NYhaZ4sMSF01rM+gHPaMgvPM0l5D/F+Qx+i2WfSmQ==} - engines: {node: '>=12.20'} - peerDependencies: - '@75lb/nature': latest - peerDependenciesMeta: - '@75lb/nature': - optional: true - - command-line-usage@7.0.4: - resolution: {integrity: sha512-85UdvzTNx/+s5CkSgBm/0hzP80RFHAa7PsfeADE5ezZF3uHz3/Tqj9gIKGT9PTtpycc3Ua64T0oVulGfKxzfqg==} - engines: {node: '>=12.20.0'} - commander@14.0.3: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} @@ -2481,9 +2350,6 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - earcut@2.2.4: - resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==} - earcut@3.2.3: resolution: {integrity: sha512-vnS4AVwp1KHAF13i1vp1/2D5evWy3k5u/iW/B81QVsUZtV8cv2tU0b2VNFlqvh4kYwrFMDdjPCfAmfyJW9y14Q==} @@ -2666,15 +2532,6 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - find-replace@5.0.2: - resolution: {integrity: sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==} - engines: {node: '>=14'} - peerDependencies: - '@75lb/nature': latest - peerDependenciesMeta: - '@75lb/nature': - optional: true - find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -2683,9 +2540,6 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatbuffers@25.9.23: - resolution: {integrity: sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==} - flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} @@ -2974,10 +2828,6 @@ packages: engines: {node: '>=6'} hasBin: true - json-bignum@0.0.3: - resolution: {integrity: sha512-2WHyXj3OfHSgNyuzDbSxI1w2jgw5gkWSWhS7Qg4bWXx1nLk3jnbwfUeS0PSba3IzpTUWdHxBieELUzXRjQB2zg==} - engines: {node: '>=0.8'} - json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -3110,9 +2960,6 @@ packages: lodash-es@4.18.1: resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} - lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} @@ -3361,9 +3208,6 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - mjolnir.js@3.0.1: - resolution: {integrity: sha512-/RMi8Jm3NKleOkVI8D2ai+1OVwtfRJsSVBVjbTXNm83nfsN4uORYaN3u1/hsg5CqVI+di8enTkvgDNDOywn6cQ==} - motion-dom@12.42.2: resolution: {integrity: sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA==} @@ -3805,10 +3649,6 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - table-layout@4.1.1: - resolution: {integrity: sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==} - engines: {node: '>=12.17'} - tagged-tag@1.0.0: resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} engines: {node: '>=20'} @@ -3903,10 +3743,6 @@ packages: engines: {node: '>=16.20.0'} hasBin: true - typical@7.3.0: - resolution: {integrity: sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==} - engines: {node: '>=12.17'} - undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} @@ -4120,10 +3956,6 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wordwrapjs@5.1.1: - resolution: {integrity: sha512-0yweIbkINJodk27gX9LBGMzyQdBDan3s/dEAiwBOj+Mf0PPyWL6/rikalkv8EeD0E8jm4o5RXEOrFTP3NXbhJg==} - engines: {node: '>=12.17'} - wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -4491,51 +4323,6 @@ snapshots: '@csstools/css-tokenizer@3.0.4': {} - '@deck.gl/core@9.3.7': - dependencies: - '@loaders.gl/core': 4.4.3 - '@loaders.gl/images': 4.4.3(@loaders.gl/core@4.4.3) - '@luma.gl/core': 9.3.6 - '@luma.gl/engine': 9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) - '@luma.gl/shadertools': 9.3.6(@luma.gl/core@9.3.6) - '@luma.gl/webgl': 9.3.6(@luma.gl/core@9.3.6) - '@math.gl/core': 4.1.0 - '@math.gl/sun': 4.1.0 - '@math.gl/types': 4.1.0 - '@math.gl/web-mercator': 4.1.0 - '@probe.gl/env': 4.1.1 - '@probe.gl/log': 4.1.1 - '@probe.gl/stats': 4.1.1 - '@types/offscreencanvas': 2019.7.3 - gl-matrix: 3.4.4 - mjolnir.js: 3.0.1 - transitivePeerDependencies: - - '@75lb/nature' - - '@deck.gl/layers@9.3.7(@deck.gl/core@9.3.7)(@loaders.gl/core@4.4.3)(@luma.gl/core@9.3.6)(@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)))': - dependencies: - '@deck.gl/core': 9.3.7 - '@loaders.gl/core': 4.4.3 - '@loaders.gl/images': 4.4.3(@loaders.gl/core@4.4.3) - '@loaders.gl/schema': 4.4.3 - '@luma.gl/core': 9.3.6 - '@luma.gl/engine': 9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)) - '@luma.gl/shadertools': 9.3.6(@luma.gl/core@9.3.6) - '@mapbox/tiny-sdf': 2.2.0 - '@math.gl/core': 4.1.0 - '@math.gl/polygon': 4.1.0 - '@math.gl/web-mercator': 4.1.0 - '@types/geojson': 7946.0.16 - earcut: 2.2.4 - transitivePeerDependencies: - - '@75lb/nature' - - '@deck.gl/mapbox@9.3.7(@deck.gl/core@9.3.7)(@luma.gl/core@9.3.6)(@math.gl/web-mercator@4.1.0)': - dependencies: - '@deck.gl/core': 9.3.7 - '@luma.gl/core': 9.3.6 - '@math.gl/web-mercator': 4.1.0 - '@esbuild/aix-ppc64@0.28.1': optional: true @@ -4749,82 +4536,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@loaders.gl/core@4.4.3': - dependencies: - '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) - '@loaders.gl/schema': 4.4.3 - '@loaders.gl/schema-utils': 4.4.3(@loaders.gl/core@4.4.3) - '@loaders.gl/worker-utils': 4.4.3(@loaders.gl/core@4.4.3) - '@probe.gl/log': 4.1.1 - transitivePeerDependencies: - - '@75lb/nature' - - '@loaders.gl/images@4.4.3(@loaders.gl/core@4.4.3)': - dependencies: - '@loaders.gl/core': 4.4.3 - '@loaders.gl/loader-utils': 4.4.3(@loaders.gl/core@4.4.3) - transitivePeerDependencies: - - '@75lb/nature' - - '@loaders.gl/loader-utils@4.4.3(@loaders.gl/core@4.4.3)': - dependencies: - '@loaders.gl/schema': 4.4.3 - '@loaders.gl/worker-utils': 4.4.3(@loaders.gl/core@4.4.3) - '@probe.gl/log': 4.1.1 - '@probe.gl/stats': 4.1.1 - transitivePeerDependencies: - - '@75lb/nature' - - '@loaders.gl/core' - - '@loaders.gl/schema-utils@4.4.3(@loaders.gl/core@4.4.3)': - dependencies: - '@loaders.gl/core': 4.4.3 - '@loaders.gl/schema': 4.4.3 - '@types/geojson': 7946.0.16 - apache-arrow: 21.1.0 - transitivePeerDependencies: - - '@75lb/nature' - - '@loaders.gl/schema@4.4.3': - dependencies: - '@types/geojson': 7946.0.16 - apache-arrow: 21.1.0 - transitivePeerDependencies: - - '@75lb/nature' - - '@loaders.gl/worker-utils@4.4.3(@loaders.gl/core@4.4.3)': - dependencies: - '@loaders.gl/core': 4.4.3 - - '@luma.gl/core@9.3.6': - dependencies: - '@math.gl/types': 4.1.0 - '@probe.gl/env': 4.1.1 - '@probe.gl/log': 4.1.1 - '@probe.gl/stats': 4.1.1 - '@types/offscreencanvas': 2019.7.3 - - '@luma.gl/engine@9.3.6(@luma.gl/core@9.3.6)(@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6))': - dependencies: - '@luma.gl/core': 9.3.6 - '@luma.gl/shadertools': 9.3.6(@luma.gl/core@9.3.6) - '@math.gl/core': 4.1.0 - '@math.gl/types': 4.1.0 - '@probe.gl/log': 4.1.1 - '@probe.gl/stats': 4.1.1 - - '@luma.gl/shadertools@9.3.6(@luma.gl/core@9.3.6)': - dependencies: - '@luma.gl/core': 9.3.6 - '@math.gl/core': 4.1.0 - '@math.gl/types': 4.1.0 - - '@luma.gl/webgl@9.3.6(@luma.gl/core@9.3.6)': - dependencies: - '@luma.gl/core': 9.3.6 - '@math.gl/types': 4.1.0 - '@probe.gl/env': 4.1.1 - '@mapbox/geojson-rewind@0.5.2': dependencies: get-stream: 6.0.1 @@ -4854,22 +4565,6 @@ snapshots: rw: 1.3.3 tinyqueue: 3.0.0 - '@math.gl/core@4.1.0': - dependencies: - '@math.gl/types': 4.1.0 - - '@math.gl/polygon@4.1.0': - dependencies: - '@math.gl/core': 4.1.0 - - '@math.gl/sun@4.1.0': {} - - '@math.gl/types@4.1.0': {} - - '@math.gl/web-mercator@4.1.0': - dependencies: - '@math.gl/core': 4.1.0 - '@mermaid-js/parser@1.2.0': dependencies: '@chevrotain/types': 11.1.2 @@ -4902,14 +4597,6 @@ snapshots: dependencies: playwright: 1.61.1 - '@probe.gl/env@4.1.1': {} - - '@probe.gl/log@4.1.1': - dependencies: - '@probe.gl/env': 4.1.1 - - '@probe.gl/stats@4.1.1': {} - '@radix-ui/number@1.1.2': {} '@radix-ui/primitive@1.1.6': {} @@ -5550,6 +5237,7 @@ snapshots: '@swc/helpers@0.5.23': dependencies: tslib: 2.8.1 + optional: true '@swc/types@0.1.27': dependencies: @@ -5664,10 +5352,6 @@ snapshots: '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 - '@types/command-line-args@5.2.3': {} - - '@types/command-line-usage@5.0.4': {} - '@types/d3-array@3.2.2': {} '@types/d3-axis@3.0.6': @@ -5829,8 +5513,6 @@ snapshots: dependencies: undici-types: 7.18.2 - '@types/offscreencanvas@2019.7.3': {} - '@types/pbf@3.0.5': {} '@types/react-dom@19.2.3(@types/react@19.2.17)': @@ -6009,20 +5691,6 @@ snapshots: ansi-styles@5.2.0: {} - apache-arrow@21.1.0: - dependencies: - '@swc/helpers': 0.5.23 - '@types/command-line-args': 5.2.3 - '@types/command-line-usage': 5.0.4 - '@types/node': 24.13.3 - command-line-args: 6.0.2 - command-line-usage: 7.0.4 - flatbuffers: 25.9.23 - json-bignum: 0.0.3 - tslib: 2.8.1 - transitivePeerDependencies: - - '@75lb/nature' - argparse@2.0.1: {} aria-hidden@1.2.6: @@ -6035,8 +5703,6 @@ snapshots: aria-query@5.3.2: {} - array-back@6.2.3: {} - assertion-error@2.0.1: {} asynckit@0.4.0: {} @@ -6073,10 +5739,6 @@ snapshots: chai@6.2.2: {} - chalk-template@0.4.0: - dependencies: - chalk: 4.1.2 - chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -6128,20 +5790,6 @@ snapshots: comma-separated-tokens@2.0.3: {} - command-line-args@6.0.2: - dependencies: - array-back: 6.2.3 - find-replace: 5.0.2 - lodash.camelcase: 4.3.0 - typical: 7.3.0 - - command-line-usage@7.0.4: - dependencies: - array-back: 6.2.3 - chalk-template: 0.4.0 - table-layout: 4.1.1 - typical: 7.3.0 - commander@14.0.3: {} commander@7.2.0: {} @@ -6410,8 +6058,6 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - earcut@2.2.4: {} - earcut@3.2.3: {} echarts-for-react@3.0.6(echarts@6.1.0)(react@19.2.7): @@ -6623,8 +6269,6 @@ snapshots: dependencies: flat-cache: 4.0.1 - find-replace@5.0.2: {} - find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -6635,8 +6279,6 @@ snapshots: flatted: 3.4.2 keyv: 4.5.4 - flatbuffers@25.9.23: {} - flatted@3.4.2: {} form-data@4.0.6: @@ -6981,8 +6623,6 @@ snapshots: jsesc@3.1.0: {} - json-bignum@0.0.3: {} - json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} @@ -7077,8 +6717,6 @@ snapshots: lodash-es@4.18.1: {} - lodash.camelcase@4.3.0: {} - lodash.merge@4.6.2: {} longest-streak@3.1.0: {} @@ -7594,8 +7232,6 @@ snapshots: minimist@1.2.8: {} - mjolnir.js@3.0.1: {} - motion-dom@12.42.2: dependencies: motion-utils: 12.39.0 @@ -8099,11 +7735,6 @@ snapshots: symbol-tree@3.2.4: {} - table-layout@4.1.1: - dependencies: - array-back: 6.2.3 - wordwrapjs: 5.1.1 - tagged-tag@1.0.0: {} tailwind-merge@3.6.0: {} @@ -8194,8 +7825,6 @@ snapshots: '@typescript/typescript-win32-arm64': 7.0.2 '@typescript/typescript-win32-x64': 7.0.2 - typical@7.3.0: {} - undici-types@7.18.2: {} unified@11.0.5: @@ -8377,8 +8006,6 @@ snapshots: word-wrap@1.2.5: {} - wordwrapjs@5.1.1: {} - wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 diff --git a/src/app/app.e2e.ts b/src/app/app.e2e.ts index f8aba22..54d13b2 100644 --- a/src/app/app.e2e.ts +++ b/src/app/app.e2e.ts @@ -80,7 +80,7 @@ test("preserves compact typography in header and agent controls", async ({ page await expect(approvalControl).toHaveCSS("font-size", "12px"); await expect(approvalControl).toHaveCSS("line-height", "16px"); await expect(suggestion).toHaveCSS("font-size", "12px"); - await expect(suggestion).toHaveCSS("font-weight", "500"); + await expect(suggestion).toHaveCSS("font-weight", "600"); }); test("lets interactive utilities override surface materials", async ({ page }) => { @@ -100,7 +100,7 @@ test("lets interactive utilities override surface materials", async ({ page }) = expect(hoverBackground).not.toBe(restingBackground); }); -test("preserves v3 menu and panel styling through Tailwind v4", async ({ page }) => { +test("keeps menu origin and integrated composer styling through Tailwind v4", async ({ page }) => { await page.setViewportSize({ width: 1440, height: 900 }); await page.goto("/"); @@ -116,6 +116,6 @@ test("preserves v3 menu and panel styling through Tailwind v4", async ({ page }) const commandShadow = await agentCommandControl.evaluate( (element) => window.getComputedStyle(element).boxShadow ); - expect(commandShadow).toContain("rgba(0, 0, 0, 0.05) 0px 1px 2px 0px"); - expect(commandShadow).not.toContain("0px 1px 3px 0px"); + expect(commandShadow).toBe("none"); + await expect(agentCommandControl).toHaveCSS("border-top-width", "0px"); }); diff --git a/src/features/agent/components/agent-collapsed-rail.tsx b/src/features/agent/components/agent-collapsed-rail.tsx index db3569c..f3d3a99 100644 --- a/src/features/agent/components/agent-collapsed-rail.tsx +++ b/src/features/agent/components/agent-collapsed-rail.tsx @@ -28,7 +28,7 @@ export function AgentCollapsedRail({ aria-label={expandLabel} title={expandLabel} onClick={onExpand} - className="agent-rail-item surface-control group relative flex h-14 w-full items-center justify-center rounded-xl border transition-[background-color,transform] hover:bg-white active:scale-95" + className="agent-rail-item surface-control group relative flex h-14 w-full items-center justify-center rounded-xl border hover:bg-white" > diff --git a/src/features/agent/components/agent-command-panel.tsx b/src/features/agent/components/agent-command-panel.tsx index f6e29fa..6ccd1bc 100644 --- a/src/features/agent/components/agent-command-panel.tsx +++ b/src/features/agent/components/agent-command-panel.tsx @@ -19,7 +19,7 @@ import { X } from "lucide-react"; import { AnimatePresence, MotionConfig, motion, useReducedMotion } from "motion/react"; -import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react"; +import { useCallback, useEffect, useId, useMemo, useRef, useState, type ReactNode } from "react"; import { createPortal } from "react-dom"; import { useStickToBottomContext } from "use-stick-to-bottom"; import { showMapNotice } from "@/features/map/core"; @@ -48,6 +48,7 @@ import { } from "@/shared/ui/dropdown-menu"; import { Button } from "@/shared/ui/button"; import { cn } from "@/shared/ui/cn"; +import { StatusBadge, StatusDot, type StatusTone } from "@/shared/ui/status"; import type { AgentChatSessionSummary } from "../api/client"; import { AgentPersona } from "./agent-persona"; import { @@ -70,6 +71,7 @@ import { import type { AgentApprovalMode, AgentChatMessage, + AgentConnectionStatus, AgentModelOption, AgentPermissionReply, AgentPermissionRequest, @@ -78,10 +80,7 @@ import type { AgentUiResult } from "../types"; -export type AgentCommandPanelPresentation = - | "desktop-dock" - | "desktop-floating" - | "mobile-sheet"; +export type AgentCommandPanelPresentation = "desktop-dock" | "desktop-floating" | "mobile-sheet"; type AgentCommandPanelProps = { presentation?: AgentCommandPanelPresentation; @@ -92,6 +91,7 @@ type AgentCommandPanelProps = { sessionHistoryLoading?: boolean; activeSessionId?: string | null; statusLabel?: string; + connectionStatus?: AgentConnectionStatus; streaming?: boolean; messages?: AgentChatMessage[]; streamRenderState?: AgentStreamRenderState; @@ -142,6 +142,7 @@ export function AgentCommandPanel({ sessionHistoryLoading = false, activeSessionId, statusLabel = "Agent 后端连接中", + connectionStatus = "connecting", streaming = false, messages = [], streamRenderState = {}, @@ -167,6 +168,12 @@ export function AgentCommandPanel({ const [historyOpen, setHistoryOpen] = useState(false); const [loadingSessionId, setLoadingSessionId] = useState(null); const [scrollRequestId, setScrollRequestId] = useState(0); + const historyPanelId = useId(); + const historyTriggerRef = useRef(null); + const historyPanelRef = useRef(null); + const desktopFloating = presentation === "desktop-floating"; + const floatingPresentation = desktopFloating || presentation === "mobile-sheet"; + const hasConversation = messages.length > 0 || streaming; const trimmedPrompt = prompt.trim(); const { speechState, @@ -202,6 +209,46 @@ export function AgentCommandPanel({ } }, [historyOpen, onRefreshHistory]); + useEffect(() => { + if (!historyOpen) { + return; + } + + const closeHistoryOnOutsidePointer = (event: PointerEvent) => { + const target = event.target; + if (!(target instanceof Node)) { + return; + } + + if ( + historyPanelRef.current?.contains(target) || + historyTriggerRef.current?.contains(target) + ) { + return; + } + + setHistoryOpen(false); + }; + const closeHistoryOnEscape = (event: KeyboardEvent) => { + if (event.key !== "Escape") { + return; + } + + event.preventDefault(); + event.stopPropagation(); + setHistoryOpen(false); + window.requestAnimationFrame(() => historyTriggerRef.current?.focus()); + }; + + document.addEventListener("pointerdown", closeHistoryOnOutsidePointer, true); + document.addEventListener("keydown", closeHistoryOnEscape); + + return () => { + document.removeEventListener("pointerdown", closeHistoryOnOutsidePointer, true); + document.removeEventListener("keydown", closeHistoryOnEscape); + }; + }, [historyOpen]); + useEffect(() => { stopSpeech(); }, [activeSessionId, stopSpeech]); @@ -221,155 +268,217 @@ export function AgentCommandPanel({ setPrompt(nextPrompt); }); }; + const historyPanel = ( + { + setLoadingSessionId(nextSessionId); + void Promise.resolve(onLoadHistorySession?.(nextSessionId)) + .then(() => setHistoryOpen(false)) + .finally(() => setLoadingSessionId(null)); + }} + onRenameSession={onRenameHistorySession} + onDeleteSession={onDeleteHistorySession} + /> + ); + const operationalBrief = ( + + ); return ( ); } -function PanelSection({ title, description, children }: { title: string; description: string; children: React.ReactNode }) { - return

{title}

{description}

{children}
; +function PanelSection({ + title, + description, + children +}: { + title: string; + description: string; + children: React.ReactNode; +}) { + return ( +
+
+

{title}

+

{description}

+
+ {children} +
+ ); } function Field({ label, children }: { label: string; children: React.ReactNode }) { - return ; + return ( + + ); } -function ColorField({ label, value, onChange }: { label: string; value: string; onChange: (value: string) => void }) { - return
onChange(event.target.value.toUpperCase())} className="h-5 w-7 cursor-pointer border-0 bg-transparent p-0" />{value}
; +function ToggleField({ + label, + checked, + onChange +}: { + label: string; + checked: boolean; + onChange: (checked: boolean) => void; +}) { + return ( + + ); } -function RangeField({ label, min, max, step, value, onChange }: { label: string; min: number; max: number; step: number; value: number; onChange: (value: number) => void }) { - return onChange(Number(event.target.value))} className="h-6 w-full accent-blue-600" />; +function ColorField({ + label, + value, + onChange +}: { + label: string; + value: string; + onChange: (value: string) => void; +}) { + return ( + +
+ onChange(event.target.value.toUpperCase())} + className="h-5 w-7 cursor-pointer border-0 bg-transparent p-0" + /> + {value} +
+
+ ); } -const inputClassName = "h-9 w-full rounded-lg border border-slate-200 bg-white/80 px-2.5 text-xs text-slate-800 outline-hidden transition focus:border-blue-400 focus:ring-2 focus:ring-blue-500/15"; -const primaryButtonClassName = "inline-flex h-9 items-center justify-center gap-1.5 rounded-lg bg-blue-600 px-3 text-xs font-semibold text-white transition hover:bg-blue-700 active:scale-95 disabled:cursor-not-allowed disabled:scale-100 disabled:opacity-45"; -const secondaryButtonClassName = "inline-flex h-9 items-center justify-center gap-1.5 rounded-lg border border-slate-200 bg-white/70 px-3 text-xs font-semibold text-slate-700 transition hover:bg-white active:scale-95 disabled:cursor-not-allowed disabled:scale-100 disabled:opacity-45"; +function RangeField({ + label, + min, + max, + step, + value, + onChange +}: { + label: string; + min: number; + max: number; + step: number; + value: number; + onChange: (value: number) => void; +}) { + return ( + + onChange(Number(event.target.value))} + className="h-6 w-full accent-blue-600" + /> + + ); +} + +function StatusText({ children }: { children: React.ReactNode }) { + return ( +

+ {children} +

+ ); +} + +const inputClassName = + "h-9 w-full rounded-lg border border-slate-200 bg-white/80 px-2.5 text-xs text-slate-800 outline-hidden transition focus:border-blue-400 focus:ring-2 focus:ring-blue-500/15"; diff --git a/src/features/workbench/components/mobile-workbench-sheet.tsx b/src/features/workbench/components/mobile-workbench-sheet.tsx index 2ecb775..c8e07c9 100644 --- a/src/features/workbench/components/mobile-workbench-sheet.tsx +++ b/src/features/workbench/components/mobile-workbench-sheet.tsx @@ -149,7 +149,7 @@ export function MobileWorkbenchSheet({ )} style={{ height: dragHeight ?? restingHeight }} > -
+
) : null} @@ -349,19 +357,19 @@ export function ScheduledConditionFeed({ mobileSheet ? "flex flex-1" : expanded - ? "scheduled-feed-layout-expanded" + ? "scheduled-feed-layout-expanded flex-1" : "scheduled-feed-layout-collapsed" )} >
-
+
); } @@ -469,7 +490,7 @@ function MenuAction({ "grid h-7 w-7 shrink-0 place-items-center border", MAP_ICON_CELL_RADIUS_CLASS_NAME, tone === "primary" && "border-blue-100 bg-blue-50 text-blue-700", - tone === "warning" && "border-orange-100 bg-orange-50 text-orange-700", + tone === "warning" && "status-icon status-tone-warning", tone === "default" && "border-slate-100 bg-slate-50 text-slate-600" )} > diff --git a/src/features/workbench/components/workbench-top-bar-styles.ts b/src/features/workbench/components/workbench-top-bar-styles.ts index 137274c..b130715 100644 --- a/src/features/workbench/components/workbench-top-bar-styles.ts +++ b/src/features/workbench/components/workbench-top-bar-styles.ts @@ -1,7 +1,16 @@ +import { buttonInteractionVariants } from "@/shared/ui/button-interaction"; +import { cn } from "@/shared/ui/cn"; + export const headerControlButtonClassName = - "inline-flex h-8 items-center gap-2 whitespace-nowrap rounded-full border border-transparent bg-transparent text-sm font-semibold text-slate-700 transition-colors hover:bg-white/70 hover:text-blue-700 focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-blue-600/20 focus-visible:ring-offset-2 data-[state=open]:bg-white/90 data-[state=open]:text-blue-700"; + cn( + buttonInteractionVariants({ tone: "neutral" }), + "inline-flex h-8 items-center gap-2 whitespace-nowrap rounded-full border border-transparent bg-transparent text-sm font-semibold after:absolute after:-inset-1 after:content-['']" + ); + +export const compactHeaderControlButtonClassName = + "h-10 w-10 justify-center px-0"; export const headerControlIconClassName = - "grid h-5 w-5 shrink-0 place-items-center rounded-full bg-slate-100/80 text-slate-500 transition-colors group-hover:bg-blue-50 group-hover:text-blue-700 group-data-[state=open]:bg-blue-50 group-data-[state=open]:text-blue-700"; + "header-control-icon grid h-5 w-5 shrink-0 place-items-center rounded-full bg-slate-100/80 text-slate-500 transition-colors group-hover:bg-blue-50 group-hover:text-blue-700 group-data-[state=open]:bg-blue-50 group-data-[state=open]:text-blue-700"; export const compactHeaderTextClassName = "hidden 2xl:inline"; diff --git a/src/features/workbench/components/workbench-top-bar.tsx b/src/features/workbench/components/workbench-top-bar.tsx index 01edba1..595bb5c 100644 --- a/src/features/workbench/components/workbench-top-bar.tsx +++ b/src/features/workbench/components/workbench-top-bar.tsx @@ -138,26 +138,18 @@ export function WorkbenchTopBar({ ) : null}
-
+
{devPanelEnabled ? (
@@ -996,7 +998,7 @@ export function MapWorkbenchPage() { aria-label="打开工况任务" title="打开工况任务" onClick={toggleConditionFeedForViewport} - className="acrylic-control grid h-11 w-11 shrink-0 place-items-center rounded-xl border text-blue-700 active:scale-95" + className="acrylic-control grid h-11 w-11 shrink-0 place-items-center rounded-xl border text-blue-700" >