ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Hermes Desktop Office 空间交互解析:银行柜员/ATM 代表(Representative)系统与钱包面板实现

Hermes Desktop Office 空间交互解析:银行柜员/ATM 代表(Representative)系统与钱包面板实现 Hermes Desktop Office 空间交互解析银行柜员/ATM 代表Representative系统与钱包面板实现【免费下载链接】hermes-desktopDesktop Companion for Hermes Agent项目地址: https://gitcode.com/gh_mirrors/her/hermes-desktop本文围绕 Hermes DesktopHermes Agent 桌面伴侣Office 标签页中的空间代表Space Representative系统展开以银行柜员bank teller与自助 ATM 为第一个落地场景完整剖析从 3D 场景的可交互对象Interactable、代表注册表registry、交互面板RepInteractionPanel到主进程后端钱包调用wallet-actions的整条调用链。读者将掌握该模块一个注册表条目 一处 Interior 挂载 面板动作接线的可扩展架构以及账户作用域缓存、请求令牌防串号等关键实现细节。背景什么是空间代表系统在 Office 标签页的 3D 城市中银行是第一个交易空间transaction space。空间代表指的是空间中供 Agent 办理业务的 NPC——今天只有银行柜员未来还有展厅销售showroom sales、建筑空间building space等。点击银行柜员或自助 ATM会打开一个共享的动作模态框balances、account status 等针对所选 Agent 运行在 hermes-one 后端上的操作。从源码结构看新增一个空间代表只需四件事在 代表注册表 增加一条注册项在对应的 3D 室内场景Interior中用一个 Interactable 包裹代表模型在 RepInteractionPanel 中接线面板动作仅当新空间需要新能力时补齐 i18n 字符串。即将上线的动作如 ATM 的 withdraw/deposit通过disabled标志注册在面板中渲染为弱化的Soon 芯片直到对应流程落地。除点击外面板还能由 Agent 自己打开一条由聊天命令chat command驱动的差事errand会让 Agent 走到代表身边并自动打开面板执行指定动作——这一机制详见 office-world-actions。代表注册表Representative Registry注册表将每个代表的身份与菜单内容与 3D 场景解耦注册项只描述 id、所属空间、i18n 标签键和有序动作列表3D 场景通过 id 反查它。核心类型与条目registry.ts 定义了三个核心类型RepActionId动作 id 的联合类型含checkBalance、accountStatus、createAccount、sendMoney、withdraw、deposit六种RepAction动作描述含id、i18n 键labelKey位于office.命名空间和可选disabled标志SpaceRepresentative代表描述含id、spaceId、显示名labelKey、空间名spaceLabelKey和有序动作列表actions。当前注册的两个代表如下完整源码见 registry.tsidspaceId动作说明bank-tellerbankcheckBalance / accountStatus / createAccount银行柜员全部可执行atmbankcheckBalance / accountStatus / withdrawdisabled/ depositdisabled自助 ATM存取款为 coming-soon值得注意的细节sendMoneySend to agent在类型层面存在、在注册表中被刻意省略。源码注释明确说明在转账流程落地前该动作不会出现在任何注册项中但RepActionId类型与面板渲染已预留支持——这是先扩展类型、后落地功能的典型做法。getRepresentative(id)负责按 id 解析代表未知 id 或空值返回nullOffice 屏幕据此决定是否渲染面板。面板校验保证每个代表至少有 ≥1 个可执行动作避免出现纯 coming-soon 的死菜单见下文测试节。3D 交互物Interactable 与银行场景挂载Interactable 通用组件Interactable.tsx 是把室内物体ATM、展示车、办公桌变为可交互对象的基础设施行为如下悬停显示一个浮空的 Billboard 标签深色半透明底板 浅色文字和一个半透明的地面高亮环ringGeometry并切换指针光标useCursor点击触发onActivate回调enabled关闭时裸渲染子节点不挂任何交互——这样在城市视角下点击语义选中 Agent、开发者建筑移动器不会受室内交互影响。组件还暴露position、indicatorPosition、labelHeight、ringRadius等参数用于微调标签高度与高亮环半径如 ATM 用labelHeight{1.9}、ringRadius{0.75}柜员用labelHeight{2.1}、ringRadius{0.55}。银行室内柜员与 ATMBank.tsx 是银行室内的实现包含两个关键包装BankTellers在柜台后排布 3 个StaffPerson银行员工模型制服色调比顾客更沉稳见TELLER_TINTS每个都用Interactable包裹BankATMs按 4 个位置摆放atm.glb网格同样用Interactable包裹。两者都只在interactive室内模式下启用onTellerActivate/onAtmActivate事件从 Bank → Office3D → Office.tsx 逐层冒泡最终由 Office.tsx 设置当前激活的代表 idbank-teller或atm进出建筑会清空该值。一个值得注意的 i18n 边界柜员的悬停标签tellerLabel是在 Office.tsx 中预先翻译后通过 props 层层下传的因为 i18n 上下文无法跨越 r3f 的Canvas边界useI18n只能在 React 树外使用ATM 的标签则是静态字符串 ATM。另外从代码演进看ATM 之前打开的是个人资料模态框的钱包页现已改为打开atm代表面板与其他代表共用同一套模态机制。交互面板RepInteractionPanelRepInteractionPanel.tsx 是所有代表银行柜员、ATM共用的居中模态框半透明暗色背景 主题化卡片。面板采用纯扁平的主题 CSS 变量var(--bg-tertiary)、var(--border)等不使用渐变。Props 与打开方式Prop类型说明repSpaceRepresentative当前代表由getRepresentative(activeRepId)解析agentsOfficeAgent[]可选 Agent 列表initialAgentIdstring \| null初始选中 AgentvisiblebooleanOffice 标签页是否可见用于账户重解析autoActionRepActionId \| null差事驱动的自动动作chat world actionsonClose() void关闭回调Office 传入的初始 Agent 为selectedId ?? defaultAgentIddefaultAgentId是当前激活 profile按 agent id 匹配或第一个 Agent——这样打开时选择器预选当前 profile而不是空白的 Choose an agent…。布局与交互细节背景层fixed inset-0点击背景或按 Escape 关闭卡片挂载时淡入/缩放opacitytransform过渡布局全部用内联样式非 Tailwind class以自包含、防定位破坏。头部身份区按代表区分REP_ICONS图标柜员 银行地标LandmarkATM 卡片CreditCard下方是实时的状态行带绿点柜员显示 Open · serving {agent}ATM 显示 Online · {agent}未选 Agent 时显示 idle 文案。Agent 选择芯片头像 原生select组合保证键盘可达性appearance: none去掉默认样式视觉上是胶囊芯片。Hero 卡片随最近一次动作结果变化——loading骨架屏 旋转 Loaderbalance总额数字 装饰性扁平 sparklineBalanceSparkline 纯静态折线不携带任何数据因为后端不提供价格历史created成功卡地址缩写 · Baseerror错误卡 Retry按钮runAction(activeAction)重跑上次动作hint警告提示卡signed-out / unlinked / foreign空钱包卡$0.00 no funds in this account yet——注意不提供创建提示因为能达到余额说明账户已存在一个 Agent 只有一个账户。Token 行余额加载后渲染圆形的 symbol 徽章tokenBadge 按 symbol 着色HD/H1/HERMES 系用主黄色ETH/WETH 用主题强调色其余用通用色、名称、数量与 USD 估值。账户状态行渲染 Transactable / Receive-only 徽章。数字字体总额、Token 数量与 USD 均使用--font-numericSpace Grotesk渲染通过自托管的font-face引入src/renderer/src/assets/main.cssfonts/SpaceGrotesk-Variable.ttf无 CDN。动作分发逻辑动作渲染为flex-wrap 的芯片组visibleActions而非固定网格这样动作数量可变时永远整齐排布、不会出现空单元格check balance主强调色芯片优先读取缓存的可交易钱包 idreadBank缺失时才走syncWallets查钱包列表再渲染后端投资组合account status通过syncWallets列出链接云端 Agent 的钱包create account仅柜员在后端创建钱包后端幂等返回的 409 already provisioned 被映射为友好提示见下节disabled 动作ATM 的 withdraw/deposit渲染为弱化芯片 Soon 徽章。因为一个 Agent 恰好只有一个账户一旦确认该 Agent 已有账户Create account 芯片会被直接移除rememberBank记录的hasAccount标志驱动visibleActions过滤。signed-out、unlinked、foreign 三种状态渲染为提示而不是报错。并发安全每次请求都持有单调递增的requestSeq令牌。切换 Agent 选择器会使任何 in-flight 请求失效——晚到的响应永远不会把 A 的钱包渲染到 B 的上下文之下apply只在requestSeq.current request时生效。差事驱动的自动打开autoAction来自 office-world-actions 编排的差事式打开会传入autoAction面板在账户作用域解析完成后恰好执行一次该动作。这里的accountResolved门闩至关重要账户 id 的解析会改变缓存键、递增requestSeq若在挂载瞬间就触发动作结果会被当作过期请求丢弃。autoRanRef保证同一动作只跑一次切换autoAction时重置。会话缓存账户作用域 内存私有缓存结构与键每个 Agent 的银行状态唯一的钱包 最近一次投资组合 hasAccount缓存在进程级内存MapbankCache中键为${signed-in account id}::${agent id}。这样重新打开面板可免请求瞬间渲染已知组合缓存金融数据永不跨越登出或重新链接不同账户产生不同键纯内存存储不落盘任何云端钱包数据延续 wallet-token-balances 中 never persist cloud wallets 原则。readBank/rememberBank是唯二访问入口。账户 id 通过window.hermesAPI.getAccount()应用级解析后拼入cacheKeyid 为 null未解析或已登出时所有读都 miss、所有写都是 no-op——没有已知账户就既不提供也不存储任何金融数据。关键安全语义同 profile 重链接到不同 Hermes 账户键的账户半段变化上一账户的投资组合与钱包 id 永远不会被读回写按请求自身的账户 Agent 键控即使选择器已切换晚到的结果仍会缓存到正确条目Office 标签页每次变为可见时重新解析账户而非仅挂载时一次。原因面板可以保持挂载Office 只隐藏、从不卸载——见 office-3d-walk-mode而用户可能在别处切换 Hermes 账户。visibleprop 把 Office 的显示状态传下来隐藏时面板忘记已解析的账户accountId → null防止返回时在重新校验前闪现过期余额返回时重新运行getAccount账户变化则产生新键与缓存 miss。后端钱包动作主进程对 hermes-one 的调用面板的动作全部通过主进程调用 hermes-one 后端完成——桌面端不持有任何密钥、也不在本地读取链状态。IPC 通道与调用链wallet-actions.ts 是主进程侧实现通过 IPC 注册表 暴露两个通道IPC 通道主进程函数后端端点说明wallet-portfoliogetWalletPortfolio(profile, walletId)GET /api/wallets/:id/portfolio读取钱包投资组合wallet-provisionprovisionAgentWallet(profile)POST /api/wallets创建 Bankr 钱包preload 层src/preload/index.ts把这两个通道封装为window.hermesAPI.getWalletPortfolio/provisionCloudWallet结果类型WalletPortfolioResult/ProvisionWalletResult定义在 src/shared/wallets.ts。getWalletPortfolio读组合先经resolveLinkedAgent解析账户/令牌/链接 Agent id 前置信息请求GET /api/wallets/:id/portfolio要求是可交易钱包后端用钱包存储的密钥鉴权读取receive-only 钱包会返回后端错误字符串响应中的原始 token 行会被归一化缺失 symbol 补?、缺失 name 补Token、非数字余额归零——畸形行有默认值兜底而不是崩溃网络失败时返回Couldnt reach ${apiUrl}: …错误信息。provisionAgentWallet创建钱包请求体为{ agentId, kind: bankr }后端幂等重复创建返回 409被映射为status: existsUI 据此提示该 Agent 已存在账户而非报错。resolveLinkedAgent公共前置两个函数共用 resolveLinkedAgent从钱包同步流程中提取的公共前奏依次处理signed-out无账户或无令牌直接短路不发网络请求从未同步先自动执行一次syncAgents()获得 Agent idunlinked同步后仍无 Agent idforeign链接的 Agent 归属另一个 Hermes 账户或后端地址不匹配——钱包动作不得作用于他人账户的 Agent后端同样强制所有权客户端提前显式拒绝ok返回{ apiUrl, token, agentId }。测试保障模块配套三套 Vitest 测试registry.test.ts——校验每个代表都有标签且 ≥1 个可执行动作、id 唯一、银行柜员以 bank 空间注册了银行动作、未知 id 解析为 nullwallet-actions.test.ts——portfoliosigned-out 短路0 次网络调用、token 映射、畸形行默认值、后端错误字符串透传、网络失败provisioning请求体、409 → exists、自动同步失败后 unlinked、HTTP 错误RepInteractionPanel.test.tsx——面板的四个关键行为保证面板跟随 Office 选择选择变化时面板保持挂载新非空选择被跟随选择清空时保留面板自身选择动作不会静默作用于 UI 已离开的 Agent丢弃过期动作结果为 A 发起的动作在响应到达前切到 B则结果被丢弃B 的上下文永不显示 A 的钱包重跑 B 的动作则正常渲染 B 的数据钱包缓存按账户作用域同一 profile 重链接到另一账户后之前账户下缓存的余额不再展示重新打开渲染中性占位符缓存键含账户 id账户作用域在重新显示时刷新面板保持挂载期间账户变化Office 隐藏再显示、从不卸载重新显示会重新解析账户旧账户缓存余额让位于占位符。小结可扩展的空间交互模式银行是这套空间代表模式的第一个落地场景其核心抽象——注册表驱动身份与菜单、Interactable 统一 3D 交互、面板按动作 id 分发、账户作用域缓存保证金融数据隔离、主进程统一走后端 API——为未来的展厅销售showroom sales与建筑空间building space提供了直接可复用的骨架。开发者若要新增一个空间只需按文首的四步接入而sendMoney类型预留与 ATM 的 disabled 存取款则展示了先占位、后落地的迭代路径。进一步阅读办公室 3D 室内与可交互对象总览见 office-3d-interiors差事驱动的自动打开机制见 office-world-actions钱包同步与余额机制见 wallet-token-balances。【免费下载链接】hermes-desktopDesktop Companion for Hermes Agent项目地址: https://gitcode.com/gh_mirrors/her/hermes-desktop创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表