Repository Wiki
zai-org/ZCode

多语言、平台适配与反馈

本页说明 ZCode 如何以共享契约承载多语言、桌面/Web 平台差异与用户反馈相关状态,并解释这些边界如何影响 renderer、main、preload 之间的通信设计。

Purpose and Scope

本页覆盖三类彼此相关的基础能力:

  • 多语言(i18n)约束:界面必须容纳翻译后的文本扩展,不能把英文短标签作为布局前提。
  • 平台适配:通过共享的 IPlatformService/平台契约抽象 Desktop、Web、本地与远程环境差异,并覆盖 Windows、macOS、Linux 等平台差异。
  • 反馈:平台通知协议中用于任务状态反馈的 feedback_update 状态,以及设计规范中反馈颜色、编译反馈行和反馈截图预览的视觉约束。

本页不替代具体的 UI 组件、远程连接实现、浏览器 tab 生命周期或发布打包文档;这些实现仅在解释平台契约边界时被引用。源码中未在本页读取范围内提供完整的语言包加载器、反馈提交 API 或具体平台 service 实现,因此这些部分不作推断。

Overview

该能力的核心不是为每个平台复制一套业务逻辑,而是把跨端事实收敛为稳定的类型合同。仓库指导文件明确要求组件通过 packages/ui/src/hooks/ 访问服务,平台操作通过 IPlatformService(packages/shared/src/platform.ts)完成,而不是直接调用 window.zcode;同时,Desktop、Web、本地和远程环境的差异应通过依赖注入处理,并兼顾 Windows、macOS 与 Linux。

共享平台契约具有三个重要设计意图:

  1. 边界集中:renderer 只依赖共享类型和服务接口,不需要知道某个能力由 Electron main、Web API 或远程服务提供。
  2. 安全最小化:例如 createOpenInEditorRemoteTarget 只转发打开编辑器所需的连接标识,不让 password 或 private-key passphrase 穿过 renderer/preload/main IPC。
  3. 可演进的兼容性:跨进程 payload 使用可选字段和明确的联合类型表达旧版本、不同平台或恢复路径,而不是依赖隐式约定。

Architecture

Loading diagram...

上图中的 Platform 是合同层,不是具体的 Electron 或 Web 实现。platform.ts 导入 AppSettings 与 Locale 类型,并同时声明任务通知、编辑器打开、文件保存、窗口 chrome、远程连接和嵌入式浏览器等跨端数据结构。这样做可以让上层组件以相同的数据形状处理平台能力;具体实现如何注册、注入和执行,必须在相应 sibling 页面中说明。

多语言边界与布局约束

仓库设计规范要求尊重 i18n 文本扩展,不能只为短英文标签设计固定宽度,也不能把紧密截断作为翻译后布局可用性的唯一保证。由此可以得到一个实现约束:组件应通过可伸缩布局、合理换行或可扩展容器承载 Locale 对应的文本,而不是在平台层硬编码某一种语言的长度假设。

platform.ts 将 Locale 作为共享 protocol.ts 类型导入,说明 locale 属于跨端协议的一部分,而不是某个 renderer 组件的私有状态。当前已读取的源码没有展示 locale 的枚举值、加载流程或 fallback 算法,因此本页只能确认类型依赖和布局约束,不能补充未验证的默认语言或切换 API。

平台适配的契约设计

通过联合类型表达平台差异

OpenInEditorRemoteTarget 将 SSH、WSL 与 Docker 的必要字段显式区分;ApplicationIconLocator 则把 macOS bundle id、Windows executable path 与 Windows AUMID 表达为不同的 kind。这种 discriminated union 让调用方在编译期处理平台分支,也避免把所有平台字段塞进一个含义模糊的对象。

typescript
1export type OpenInEditorRemoteTarget = 2 | Pick<SSHConnectOptions, "kind" | "host" | "port" | "username" | "sshConfigAlias"> 3 | Pick<WSLConnectOptions, "kind" | "distro" | "user"> 4 | Pick<DockerConnectOptions, "kind" | "container">; 5 6export type ApplicationIconLocator = 7 | { kind: "darwin-bundle-id"; value: string } 8 | { kind: "windows-executable-path"; value: string } 9 | { kind: "windows-aumid"; value: string };

Source: platform.ts

Source: platform.ts

在跨进程边界前裁剪敏感字段

createOpenInEditorRemoteTarget 根据 target.kind 构造最小的编辑器目标。SSH 只保留 host、port、username 和可选 alias;WSL 只保留 distro 与非空 user;Docker 只保留 container。函数注释明确说明 password 和 private-key passphrase 不应继续穿过 renderer/preload/main IPC,这既是安全边界,也是降低跨端合同复杂度的手段。

typescript
1export function createOpenInEditorRemoteTarget(target: RemoteTarget): OpenInEditorRemoteTarget { 2 switch (target.kind) { 3 case "ssh": 4 return { 5 kind: "ssh", 6 host: target.host, 7 port: target.port, 8 username: target.username, 9 ...(target.sshConfigAlias?.trim() ? { sshConfigAlias: target.sshConfigAlias.trim() } : {}), 10 }; 11 case "wsl": { 12 const user = target.user?.trim(); 13 return { 14 kind: "wsl", 15 distro: target.distro, 16 ...(user ? { user } : {}), 17 }; 18 } 19 case "docker": 20 return { 21 kind: "docker", 22 container: target.container, 23 }; 24 } 25}

Source: platform.ts

反馈在跨端通知中的表达

TaskNotificationPayload.status 是当前读取源码中最直接的反馈协议入口。它把任务状态建模为有限联合类型:completed、failed、permission_request、elicitation_request 和 feedback_update。通知同时携带 taskId、标题和正文,并允许用 requestId 关联需要继续交互的请求。

这意味着反馈不是一个无结构的字符串事件:消费者可以基于 status 做确定性的分支,避免把“任务完成”“失败”“需要权限”和“反馈更新”混为一类。源码没有展示通知的发送者、接收器或持久化策略,因此这里不声称其具体传输机制。

typescript
1export interface TaskNotificationPayload { 2 taskId: string; 3 status: "completed" | "failed" | "permission_request" | "elicitation_request" | "feedback_update"; 4 requestId?: string; 5 title: string; 6 body: string; 7}

Source: platform.ts

设计规范还规定了反馈视觉语义:语义反馈颜色应保持一致;编译失败、尚未执行的脚本使用 compile-feedback row 表达,并区分当前草稿与已有更新的状态;反馈截图预览对话框属于少数保留特定圆角 shell 的场景。这些规则属于视觉层,平台契约只负责传递状态和数据,不应在共享类型中嵌入 UI 样式。

Core Flow

Loading diagram...

这条流程强调的是类型边界而非未读取的具体调用链:调用方通过 service/hook 访问能力;renderer 使用共享 payload;运行时实现依据 Desktop、Web、本地或远程环境提供结果;反馈消费者按有限状态处理通知。若要追踪实际 IPC channel、DI 注册或 UI 状态存储,应转到对应实现页面。

Configuration Options

当前读取的相关源码没有发现可直接配置的语言 fallback、平台选择或反馈开关。可以确认的共享数据选项如下:

数据项类型默认值说明
TaskNotificationPayload.requestIdstring | undefined未提供将通知关联到交互请求;源码只声明可选,不声明生成策略。
OpenInEditorOptions.remoteTargetOpenInEditorRemoteTarget | undefined未提供指定 SSH、WSL 或 Docker 目标。
OpenInEditorOptions.pathKind"file" | "directory" | undefined未提供区分打开文件或目录。
ApplicationIconLocator.kind联合字面量必填选择 macOS bundle id、Windows executable path 或 Windows AUMID。

API Reference

createOpenInEditorRemoteTarget(target: RemoteTarget): OpenInEditorRemoteTarget

将完整的 RemoteTarget 转换为打开编辑器所需的最小远程目标。函数按 target.kind 分支:SSH 保留连接定位字段并裁剪 alias 空白;WSL 裁剪可选 user 的空白并在为空时省略;Docker 仅保留 container。

参数:

  • target(RemoteTarget):SSH、WSL 或 Docker 远程目标。

返回值: OpenInEditorRemoteTarget,不包含密码或 private-key passphrase 等凭据字段。

异常与边界: 源码没有声明显式 throw;实现通过 switch 处理已定义的 kind 联合成员。调用方仍应保证传入值符合 RemoteTarget 类型。

buildLocalMediaPreviewUrl(path: string): string

为本地媒体预览构造 zcode-media://local/preview URL,并将路径写入 path 查询参数。该函数把 scheme 和参数编码集中在共享层,避免各端重复拼接 URL。

typescript
1export const LOCAL_MEDIA_PREVIEW_SCHEME = "zcode-media"; 2 3export function buildLocalMediaPreviewUrl(path: string): string { 4 const url = new URL(`${LOCAL_MEDIA_PREVIEW_SCHEME}://local/preview`); 5 url.searchParams.set("path", path); 6 return url.toString(); 7}

Source: platform.ts

Failure Modes、边界与并发注意事项

  • 通知定位不足:关闭 browser tab 的旧 payload 只有 tabId,在用户切换 workspace 后可能无法找到原 side pane;当前契约因此允许携带 workspaceKey、remoteSessionId 和 sessionId,并保留缺省字段以兼容 recovery-orphan 路径。
  • 跨端状态不一致:BrowserTabResidencyState 明确区分 live-visible、live-background、suspend-pending、suspended 和 restoring,说明恢复与挂起不是一个布尔值可以表达的状态。相关状态机实现未在本次读取范围内展开。
  • 截图握手超时:共享常量 BROWSER_SCREENSHOT_SURFACE_PREPARE_TIMEOUT_MS 为 3,000 ms,payload 还可传递 timeoutMs;注释说明这样做是为了避免 main 与 renderer 使用漂移的 deadline。
  • 平台安全边界:编辑器目标转换会主动丢弃凭据;浏览器数据导入结果只返回数量、状态和受控错误类型,不跨进程返回 Cookie、LocalStorage 值或解密材料。
  • 并发/代际一致性:browser payload 使用 browserGeneration、generation 和 sessionId 等字段,拒绝结果可以返回 residency-generation-mismatch;这表明消费者必须校验目标实例代际,不能仅凭 tab id 接受迟到消息。

Extension Points 与操作建议

扩展新的平台能力时,应优先在 packages/shared/src/platform.ts 增加可判别的 request/result/event 类型,再由 IPlatformService 和注入实现承载,而不是让 UI 直接访问 window.zcode。新增平台分支应使用联合类型和明确的 kind,并在跨进程前裁剪敏感字段。

新增反馈状态时,应评估它是否确实是任务生命周期中的新状态,而不是把任意 UI 文案塞入 body;消费者依赖有限联合类型进行分支,因此改变状态集合会影响所有实现。多语言 UI 扩展则应遵循设计规范的 i18n expansion 要求,并在 Desktop 与 mobile Web、不同平台和主题下验证布局。

Sources

(1 files)