diff --git a/.agents/notes/assets/machine-online-motion/linear.gif b/.agents/notes/assets/machine-online-motion/linear.gif new file mode 100644 index 0000000000..a0c6f5814c Binary files /dev/null and b/.agents/notes/assets/machine-online-motion/linear.gif differ diff --git a/.agents/notes/implemented/feature/2026-10-20-machine-online-motion.md b/.agents/notes/implemented/feature/2026-10-20-machine-online-motion.md new file mode 100644 index 0000000000..bd4fb1e33a --- /dev/null +++ b/.agents/notes/implemented/feature/2026-10-20-machine-online-motion.md @@ -0,0 +1,52 @@ +# Breathing online machine indicator + +Status: implemented +Translation: current + +[中文](2026-10-20-machine-online-motion.zh.md) + +PR: [#1742](https://github.com/LodyAI/Lody/pull/1742) + +## Abstract + +The user requested motion for the static green online mark on machine cards. +The outer halo now expands and contracts at a constant speed over 2.8 seconds +while the solid centre stays green. Offline machines remain static, and reduced +motion retains the original stationary halo. This changes presentation only; the +existing presence value still determines whether a machine is online. + +## Decision + +`WorkspaceMachineAccordionRow` owns the same mark in collapsed and expanded +cards. Its separate decorative halo animates only transform and opacity, keeping +the dot's layout box, accessible status label, and card interactions unchanged. +Linear timing gives uniform scaling in each direction, with 1.4 seconds to +expand and 1.4 seconds to contract. The halo unmounts when `isOnline` becomes +false. CSS handles the loop and the reduced-motion preference without a timer, +React updates per frame, or network requests. The centre stays fully visible so +motion never makes an online machine look disconnected. + +The existing MachinesDesktopLayout stories gain a controlled presence preview +using the real collapsed card. It supports inspection of online/offline transitions +without requiring a real machine or transport. + +## Verification + +The existing accordion behavior test passes. Components type checking, targeted +typed lint, and formatting of the changed TypeScript files pass. A real browser +run of the OnlineStatusMotion story samples the halo at evenly spaced +deterministic animation times: equal time steps give equal scale changes in +each direction, while the centre colour, centre opacity and dot box remain +unchanged. It also +verifies online/offline/online transitions and that reduced motion removes the +animation and preserves a static halo. A two-cycle browser recording provides +the visual preview. + +The full `pnpm check` passes, including workspace type checking, typed and +Effect lint, all configured workspace tests, i18n validation, and boundary +guards. `pnpm format` also passes. The documentation check retains six existing +missing Kimi/Pi submodule links in the bilingual subagent-events note. + +A cropped recording of the real card with synthetic machine data: + +![Linear online halo animation](../../assets/machine-online-motion/linear.gif) diff --git a/.agents/notes/implemented/feature/2026-10-20-machine-online-motion.zh.md b/.agents/notes/implemented/feature/2026-10-20-machine-online-motion.zh.md new file mode 100644 index 0000000000..bc1c68f213 --- /dev/null +++ b/.agents/notes/implemented/feature/2026-10-20-machine-online-motion.zh.md @@ -0,0 +1,43 @@ +# 机器在线标记的呼吸动画 + +Status: implemented +Translation: current + +[English](2026-10-20-machine-online-motion.md) + +PR: [#1742](https://github.com/LodyAI/Lody/pull/1742) + +## 摘要 + +用户希望机器卡片原本静态的绿色在线标记具有动态效果。 +现在外圈以 2.8 秒为周期匀速放大、匀速缩小并改变透明度,中心绿点保持常亮。 +离线机器保持静态;启用减少动态效果时,在线外圈也恢复静态。 +本次只改变展示,机器是否在线仍由现有的 presence 状态决定。 + +## 决定 + +`WorkspaceMachineAccordionRow` 统一渲染折叠和展开卡片中的标记。 +独立的装饰外圈只动画化 transform 和 opacity,圆点的布局尺寸、可访问状态标签及 +卡片交互保持不变。使用线性时序,放大与缩小各用 1.4 秒,每个方向的缩放速度保持 +恒定。`isOnline` 变为 false 时卸载外圈。循环和减少动态效果偏好 +交给 CSS 处理,不增加定时器、逐帧 React 更新或网络请求。 +中心始终保持可见,避免动画让在线机器看起来断开了连接。 + +现有 MachinesDesktopLayout stories 增加受控在线状态预览,使用真实的折叠卡片, +无需真实机器或传输连接即可检查在线和离线切换。 + +## 验证 + +现有折叠卡片行为测试通过。组件类型检查、针对修改文件的类型感知 lint,以及修改的 +TypeScript 文件格式化通过。浏览器运行真实的 OnlineStatusMotion story,在等间隔的 +确定动画时间点取样:每个方向的等间隔时间产生等量缩放变化,中心颜色、中心透明度 +及圆点布局尺寸保持不变。另验证了在线/离线/在线切换,以及减少动态效果会取消动画 +并保留静态外圈。浏览器录制的两个循环用于动态预览。 + +完整的 `pnpm check` 通过,包括工作区类型检查、类型感知与 Effect lint、全部配置的 +工作区测试、国际化验证和边界检查;`pnpm format` 也通过。文档检查仍有 +subagent-events 双语笔记中原有的六个本地 Kimi/Pi 子模块缺失链接。 + +以下为使用合成机器数据的真实卡片局部录制: + +![Linear online halo animation](../../assets/machine-online-motion/linear.gif) diff --git a/packages/components/src/components/settings/README.md b/packages/components/src/components/settings/README.md new file mode 100644 index 0000000000..8a0abea10c --- /dev/null +++ b/packages/components/src/components/settings/README.md @@ -0,0 +1,15 @@ +# Settings surfaces + +Binding guidelines live in [AGENTS.md](AGENTS.md). + +## Machine connection marks + +`workspace-machine-accordion.tsx` renders the connection mark for collapsed and +expanded machine cards. An online machine keeps its solid green centre while +its halo expands and contracts at a constant speed over 2.8 seconds, using linear +CSS transform and opacity animation. Each direction takes 1.4 seconds. +Offline machines keep a static gray dot. Reduced motion keeps the online halo +static. Motion follows the existing `isOnline` value and adds no presence polling. + +The [OnlineStatusMotion story](../../stories/DevicesDesktopLayout.stories.tsx) +uses the real machine card with a controlled online/offline value for inspection. diff --git a/packages/components/src/components/settings/workspace-machine-accordion.tsx b/packages/components/src/components/settings/workspace-machine-accordion.tsx index 381bc2db07..4056ed2efc 100644 --- a/packages/components/src/components/settings/workspace-machine-accordion.tsx +++ b/packages/components/src/components/settings/workspace-machine-accordion.tsx @@ -23,6 +23,11 @@ export type WorkspaceMachineAccordionMeta = { agentCount: number; }; +const onlineHaloBreathe = stylex.keyframes({ + '0%, 100%': { transform: 'scale(0.875)', opacity: 0.12 }, + '50%': { transform: 'scale(1.2)', opacity: 0.28 }, +}); + const styles = stylex.create({ summary: { display: 'flex', @@ -102,6 +107,7 @@ const styles = stylex.create({ }, }, dot: { + position: 'relative', flexShrink: 0, width: '10px', height: '10px', @@ -111,7 +117,23 @@ const styles = stylex.create({ }, dotOnline: { backgroundColor: colors.success, - boxShadow: `0 0 0 3px color-mix(in oklab, ${colors.success} 20%, transparent)`, + }, + /** Only the halo breathes; the solid centre continues to say "online". */ + onlineHalo: { + position: 'absolute', + inset: '-3px', + borderRadius: radius.full, + cornerShape: corner.round, + backgroundColor: colors.success, + opacity: 0.2, + pointerEvents: 'none', + animationName: { + default: onlineHaloBreathe, + '@media (prefers-reduced-motion: reduce)': 'none', + }, + animationDuration: '2.8s', + animationTimingFunction: 'linear', + animationIterationCount: 'infinite', }, name: { flexGrow: 1, @@ -270,7 +292,9 @@ export function WorkspaceMachineAccordionRow({ role="img" aria-label={statusLabel} {...stylex.props(styles.dot, meta.isOnline && styles.dotOnline)} - /> + > + {meta.isOnline ? : null} + {machineName} + + + + ); +} + +export const OnlineStatusMotion: Story = { + render: () => , +};