按 Claude Design 定稿(归档在 docs/design/)重做 UI,并把 tmux 从 「从不自动进」修成「连接前探测 → 让用户选会话」。 设计令牌与导航地基 - Theme 拆三层:TXAccent(恒定 blurple) / TXChrome(中性阶×4 表) / TXFlavor(Catppuccin 终端) - vendor JetBrains Mono 四权重(附 OFL 许可) - AppRouter + SessionManager:多会话并存,路由与会话生命周期解耦 - SessionCanvas 常驻挂载会话 surface(摘除即丢内容,也是缩回动画的前提) 按设计稿落地的屏 - 沉浸轨道页:56pt 轨道 + 浮起标题/状态胶囊 + 侧边栏三态(遮罩不 resize、Pin 各一次) - 首页:活动会话卡(readViewportText 文本镜像)+ 主机网格 + 筛选 chips - 关闭二次确认(tmux 仅断开 / 原生窗口两套文案)、分屏菜单、pane 拖拽条 - 空状态:首次运行 / 无会话 / 搜索无命中 / 连接中·失败·已关闭 tmux 真实链路(真机查出并修掉 4 个 bug) - 全代码库从来没人发起 attach → 连接前探测 + 会话选择器(接回 / 新建 / 原生终端) - format 分隔符 tab 经 PTY 变成下划线 → 改用 |:| - controller 变化不冒泡到 session → Combine 转发(否则数据解析对了 UI 不刷新) - 当前会话名不能靠 session_attached 反推 → 改用 display-message 传输层:SSH connect 加超时(原来阻塞到系统 TCP 超时 75s+) 无头验证设施:假会话 fixture · terminalx://ui/* 驱动 · 横屏截图脚本 · 对拍走查法 TXCore 45 tests 绿(新增 5 个会话探测单测) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
17 KiB
Handoff: terminalX — iPadOS 终端 App(沉浸轨道方案)
Overview
terminalX 是面向 iPadOS / iOS 的原生终端运维工具,解决 iPad 远程运维 macOS / Linux 的痛点。技术选型:libghostty(终端引擎)· mosh(抗断线传输)· Tailscale tsnet(用户态入网)· tmux control mode(会话复用)。平台优先级 iPadOS > iOS > macOS(Apple Silicon)。
本包描述的是终端主界面(沉浸轨道)+ 首页(主机管理)+ 设置这一套已定稿的 UI,含侧边栏三态、分屏模型、关闭/最小化语义、竖屏与 iPhone 降级规则。
About the Design Files
terminalX iPadOS.dc.html 是设计稿,不是生产代码。它用 HTML/CSS 表达最终视觉与交互意图(1:1 像素尺寸),目的是让你在目标工程里用原生技术重建——本项目的目标环境是 SwiftUI(iPadOS 26 / iOS 26),请使用工程已有的架构与组件模式实现,不要移植 HTML/CSS。
打开方式:浏览器直接打开该文件即可,画板可自由缩放平移。每块屏幕都带 data-screen-label,便于对照本文档。
Fidelity
High-fidelity(hifi)。颜色、字号、圆角、间距、行高均为最终值,可按本文档的精确数值实现。文案(简体中文,技术术语保留英文)也是最终稿。
唯一例外:终端内容区的命令输出是示意数据,用于验证排版密度与配色,不必照搬。
Design Tokens
颜色 — App chrome(Nocturne 设计系统)
| 用途 | Hex | Nocturne token |
|---|---|---|
| 画布底 / 屏幕底 | #161826 |
--color-bg |
| 侧边栏 / 轨道 / 卡片底 | #1b1d2b |
surface-dark |
| 控件底(输入框、次要按钮、徽标) | #232532 |
--color-surface |
| 舞台底(pane 之间的缝) | #11121c |
bg-deep |
| 分隔线(弱) | #2c2f3d |
neutral-800 |
| 描边(常规) | #3f424d |
neutral-700 |
| 描边(强调 / 对话框) | #595d6c、#4a4d5e |
neutral-600 |
| 强调色(唯一) | #9184d9 |
--color-accent |
| 强调 - 边框 | #796cbf |
accent-600 |
| 强调 - 选中底 | #2b2741 |
accent-900 |
| 强调 - 选中描边 | #423a6a |
accent-800 |
| 强调 - 文字 | #b5abfc / #d2cefd |
accent-400 / -300 |
| 强调 - 弱化文字 | #5d5294、#968ae0 |
accent-700 / -500 |
| 主文字 | #e9e9ed |
--color-text |
| 次文字 | #cfd3e5 / #b2b6ca |
neutral-300 / -400 |
| 三级文字 | #9397ab |
neutral-500 |
| 弱文字 / 占位 | #75798c |
neutral-600 |
| 极弱(提示、注脚) | #4a4d5e |
neutral-700 |
规则:强调色只做线、点、选中底和文字,绝不大面积铺色;不使用纯黑纯白。
颜色 — 终端内容区(Catppuccin Mocha,libghostty 渲染)
base #1e1e2e(pane 底)· mantle #181825 · surface0 #313244(vim 状态栏)· text #cdd6f4 · subtext #a6adc8 · overlay #6c7086(次要输出)· surface2 #585b70(禁用/缓存态)
语义色:green #a6e3a1(成功、提示符)· red #f38ba8(error)· yellow #f9e2af(warning、git 标记)· blue #89b4fa(路径、字段)· mauve #cba6f7(关键字)· teal #94e2d5(勾选)· peach #fab387(重连中)· pink #f5c2e7
设置页可切换 Mocha / Macchiato / Frappé / Latte,chrome 的中性阶随之推导,强调色恒为 Nocturne blurple #9184d9。
macOS 窗口按钮沿用系统色:关闭 #ff5f57,最小化 #febc2e(13pt 圆点,inset 0 0 0 .5px rgba(0,0,0,.25),仅悬停/触摸时显出符号)。
字体
全局 JetBrains Mono(400 / 500 / 600 / 700),chrome 与终端同源。中文走系统回退(PingFang SC)。
| 场景 | 规格 |
|---|---|
| 屏幕主标题(首页 terminalX) | 500 34px, letter-spacing -.025em |
| 页面标题(设置、总览) | 500 24–28px, ls -.02em |
| 分区标题 | 500 22px, ls -.02em |
| 分组小标题(活动会话 / TMUX) | 600 10px, ls .09em, uppercase, #75798c |
| 列表主行 | 400–500 13px |
| 列表副行 / 元信息 | 400 10.5–11px, #75798c |
| 徽标 / 胶囊 | 500 11.5–12px |
| 终端正文(主 pane) | 400 13.5px / 1.55 |
| 终端正文(次 pane / 缩略) | 400 11–12px / 1.5 |
| 终端 pane 头 | 500 11px, #6c7086 |
| 底部提示条 | 400 11.5px, #75798c |
间距 / 圆角 / 阴影
- 间距:2 / 6 / 8 / 9 / 10 / 12 / 14 / 16 / 18 / 20 / 26 / 34 / 40(px)
- 圆角:徽标 5–7 · 控件 8–9 · 卡片 11–13 · 对话框 14–15 · 屏幕卡 18 · 手机卡 38
- 阴影:浮起胶囊
0 10px 30px rgba(0,0,0,.55);侧边栏浮层24px 0 60px rgba(0,0,0,.55);对话框0 30px 80px rgba(0,0,0,.7);卡片0 6px 18px rgba(0,0,0,.4) - 遮罩:
rgba(10,11,18,.42–.5),浮层用backdrop-filter: blur(20px)
图标
Phosphor Icons(regular)。用到:magnifying-glass house plus gear-six caret-double-left caret-double-right caret-down caret-left push-pin columns terminal-window hard-drives desktop-tower cpu key shield-check lightning paint-brush text-aa keyboard export info warning x minus arrow-right arrow-left lightbulb moon check-circle wifi-high battery-high browsers rows git-branch plugs trash arrows-in-simple textbox sidebar-simple globe-hemisphere-west。
SwiftUI 实现请换成等义 SF Symbols(例如 house / magnifying-glass → house、magnifyingglass;caret-double-left → chevron.compact.left 或 sidebar.left;push-pin → pin)。
Layout 基准
- 画板尺寸:iPad Pro 13″ 横屏 1366×1024pt;竖屏 1024×1366pt;iPhone 393×852pt
- 折叠轨道宽 56pt(竖屏 48pt),元素中心线统一在 28pt(竖屏 24pt)
- 展开侧边栏宽 288pt
- pane 之间的缝 2pt(拖拽热区 12pt,把手 44×3pt 圆角 2pt)
- 触控命中区不低于 44pt(键盘附加行按键高 38–40pt + 6pt 间距 = 44pt 节距)
- 终端主 pane 顶部预留 86pt 给浮起胶囊(2c 因多一条横幅为 122pt);预留带用 padding-top 实现,内容容器
min-height:0; overflow:hidden防止向上溢出 - Home indicator 条:200×4pt(iPhone 140×5pt)
Screens / Views
1) 终端主界面 · 沉浸轨道 — 1b 终端-沉浸轨道
Purpose:真正干活的界面,终端面积最大化。
Layout:HStack:左 56pt 轨道 + 右舞台。舞台是 VStack:pane 区(flex)+ 硬件键盘提示条 34pt + home 区 14pt。pane 区 padding 2pt,上下两个 pane 比例 1.7 : 1,中间 12pt 拖拽条。
轨道自上而下:Logo 方块(34×34,圆角 11,底 #232532,inset 描边 #3f424d,字形 › 700 16px #b5abfc,不可点)→ 搜索图标 → 首页图标 → 1pt 分隔 → 主机头像(38×38 圆角 11,两字缩写;选中底 #2b2741 + inset 描边 #5d5294;右上角 8pt 状态点,box-shadow: 0 0 0 2px #1b1d2b 挖底)→ 虚线 + 号 → 分隔 → TMUX 标签(600 9px ls .1em)+ window 数字(34×30,选中底 #232532 + inset 描边 #4a4d5e)→ Spacer → 连接状态点 8pt → 展开键(caret-double-right,34×34 底 #232532 描边 #3f424d)→ 设置齿轮。
浮起 chrome(position: absolute,backdrop-filter: blur(20px),底 rgba(27,29,43,.86–.9),描边 #3f424d,圆角 11):
- 左上标题胶囊
top 14, left 16:红/黄圆点 → 1pt 竖分隔 → 6pt 绿点 → 主机名(500 12.5px)→ 「当前 window title · cwd」(400 11.5px#75798c) - 右上状态胶囊
top 14, right 16:三格分层徽标tsnet|mosh 41ms|tmux dev,格间 1×16pt 竖线
底部提示条:接硬件键盘时只留 34pt 提示条(快捷键清单);触控时替换为键盘附加行(两行,见 iPhone / 竖屏)。
2) 侧边栏三态 — 3a 侧边栏-遮罩态、3a 侧边栏-Pin 态
同一条栏的三个状态,元素不增不减,只是展示密度不同:
| 状态 | 宽度 | 行为 |
|---|---|---|
| 折叠 | 56pt | 只显图标与两字缩写 |
| 遮罩(默认展开) | 288pt | 面板盖住轨道(left: 0,不并排,避免两套入口),右侧终端压 rgba(10,11,18,.5) 遮罩,点终端任意处收起;终端不 reflow,不触发 PTY resize |
| Pin | 288pt | 点面板右上 push-pin 后钉住:遮罩消失,终端变窄,只在钉住/解除时各 resize 一次;再点解除回遮罩态 |
开合语义(务必唯一、不歧义):› 方块是 App logo,不可点。开合只有一枚按钮,两态方向相反——折叠态在轨道底部 caret-double-right(展开),展开态在标题右侧 caret-double-left(收起)。Pin 是独立按钮,只在展开态出现。快捷键 ⌘\ 开合。
面板内容自上而下:Logo + terminalX(500 14px)+ Pin + 收起 → 搜索框(32pt 高,圆角 8)→ 首页行(house + 「首页」+ ⌘⇧H)→ 分组「活动会话 n」→ 会话行 → 1pt 渐隐分隔 → 分组「TMUX · <主机> · <会话>」→ window 行 + 「新建 window ⌘T」→ Spacer → Tailscale 状态脚(绿点 + tailnet + 设备 IP + 齿轮)。
行规格(左缘必须对齐):会话行 = 26pt 头像列 + 10pt gap;window 行 = 26×22pt 徽标列 + 10pt gap;两者标题左缘同为 x=53。选中态用 box-shadow: inset 0 0 0 1px #423a6a 而非 border,避免选中时内容位移 1pt。
3) 首页 · 主机管理(落地页)— 1e 主机-会话卡
Purpose:App 冷启动落点;「回到刚才那个会话」是首要动作。
Layout:状态栏 24pt → 页头(terminalX 500 34px + 副行)+ 右侧搜索 220pt / 新建连接 / 设置 → 分组「活动会话」→ 3 列会话缩略卡(flex:1) → 分组「全部主机」+ 筛选 chips → 4 列主机卡(按内容高度) → home 区。左右留白 40pt。
会话缩略卡(圆角 13,选中卡描边 #423a6a + 0 6px 18px rgba(0,0,0,.4)):卡头 36pt(状态点 + 主机名 + tmux 会话 + RTT)→ 终端缩略(#1e1e2e,11px/1.5,内容底对齐,真实回滚)→ 卡脚 38pt(「n windows · 上次活跃」+ ⌘1/2/3 + 主动作「回到会话 →」)。离线卡加 rgba(24,26,40,.72) + blur(1.5px) 遮罩,内含 26pt 转圈 + 两行说明;遮罩必须是 pane 的最后一个子节点(pane 为 position: relative)。
最小化回来的卡:置顶并标「最小化中」,转场是从终端缩回该卡的动画。
4) 新建分屏 — 3b 新建分屏菜单
不问方向(竖分 ⌘D / 横分 ⌘⇧D 本来就是两个入口)。菜单只问「分到哪里」:
- 分组「tmux 会话」:当前会话(带「当前」标签)、其余 detached 会话(title · n windows · 上次活跃)、「新建 tmux 会话…」
- 分隔线
- 「原生窗口 / 不经 tmux · 关 App 即结束」
选完立即分屏。菜单 360pt 宽,圆角 14,#1b1d2b + 描边 #595d6c;行高 ~40pt,前导列统一 20pt(状态点也要包在 20pt 居中列里,保证五行文字左缘一致 x=50)。
模型约束:分屏只发生在同一台主机内。卡片内部的分割 = 真 tmux pane(split-window,服务端持有,断线原样恢复);跨主机分屏已取消(层级过深),要同时看两台机器就切会话或 Pin 侧边栏。
5) 关闭 / 最小化 — 3c 关闭确认
标题胶囊左侧 macOS 红黄圆点:
- 黄点(最小化):不确认,直接回首页,会话继续跑;卡片置顶标「最小化中」。手势:终端下滑。快捷键
⌘M。 - 红点(关闭):弹一次确认,按主机能力分两套文案:
- tmux 会话 → 主按钮「仅断开 ⏎」(
detach,服务端继续跑);左侧次要文字按钮「结束会话」(kill-session,需再确认) - 原生窗口 → 琥珀警告「没有 tmux 兜底,前台进程会随之结束」;推荐「取消」,危险按钮「关闭窗口」(描边
#8d5560,底rgba(92,58,63,.35),字#f5a0ac) - 快捷键
⌘W;esc取消;⏎选推荐项
- tmux 会话 → 主按钮「仅断开 ⏎」(
对话框 436pt / 404pt 宽,圆角 15。
6) 首页 ⇄ 终端 跳转关系 — 3d 页面跳转关系
进入终端(2 条):点活动会话卡 / 会话行 → 恢复到上一个焦点 pane;新建连接或主机 + → 建好即进终端,侧边栏同步出现该会话。
离开终端(3 条):侧边栏首页行(⌘⇧H,会话完全不动)· 黄点最小化(同上但语义是"待会儿还回来")· 红点关闭(二次确认,tmux detach / 原生窗口结束)。
换会话不必回首页——侧边栏直接切;回首页表示"换任务"。会话总览层(原 ⌘O 缩放)已取消。
7) 无 tmux 主机 — 2c 无 tmux 主机
轨道下半段的 TMUX 段换成「窗口」段(客户端窗口,每个 = 一条独立 SSH channel / mosh 会话,可随意新建)。状态胶囊第三格写「无 tmux · 客户端窗口」而非报错。终端顶部一条可关闭的行内横幅:说明窗口由 App 维持、mosh 兜底,但关掉 App 不会在服务端继续跑,右侧给「安装并启用 tmux」次要按钮。不弹窗、不强推。
8) 竖屏与 iPhone — 2d iPad 竖屏、2d iPhone 竖屏、1h iPhone-终端、1h iPhone-主机
- iPad 竖屏(1024×1366):轨道降到 48pt(元素中心 24pt,同样含搜索/首页/展开键);pane 由左右平铺改为上下堆叠,最多两层,多余 pane 折成顶部段控;状态胶囊压缩为「主机名 + RTT」两枚;底部常驻单行键盘附加行。
- iPhone(393×852):轨道下沉为底部主机条(ms / u2 / nh 胶囊);单 pane 全屏,顶部 3 段进度式 pane 指示 +「左右轻扫切 pane」;下拉标题打开会话列表;键盘附加行两行(esc/ctrl/tab/~///| 与 ↑↓←→/⌘K);主机页用底部 tab(主机 / 会话 / 密钥 / 设置)。
9) 设置 · 外观 — 1g 设置-外观
左 280pt 类别栏(外观与主题 / 终端与字体 / Tailscale / 密钥与安全 / mosh 与重连 / tmux 集成 / 键盘与手势 / 诊断日志,脚注版本行),右内容区 padding 28/40。内容:标题 + 说明 → 4 张 Catppuccin 主题卡(88pt 预览 + 5 色点 + 名称,选中描边 #796cbf + check-circle)→ 两栏(左「实时预览」终端框,右「排版」面板:字体、字号 13.5pt、行高 1.50、连字、CJK 等宽补偿、跟随系统深浅色)。
Interactions & Behavior
| 交互 | 行为 |
|---|---|
| 侧边栏开合 | ⌘\ 或开合键;遮罩态点终端收起;Pin 切换推挤/浮层 |
| 切会话 | 轨道主机头像 / 侧边栏会话行 / ⌘1…9;恢复到该会话上一个焦点 pane |
| 切 window | 轨道数字 / 侧边栏 window 行 / ⌃⇥ |
| 分屏 | ⌘D 竖分、⌘⇧D 横分 → 弹会话选择菜单 → 立即分屏 |
| 新建 | ⌘T 新 tmux window;⌘⌥T 原生窗口 |
| 调整 pane 比例 | 拖 12pt 分隔条(把手 44×3pt);松手才向 tmux 发 resize-pane |
| 回首页 | ⌘⇧H / 侧边栏首页行 |
| 最小化 | 黄点 / ⌘M / 终端下滑;无确认 |
| 关闭 | 红点 / ⌘W;二次确认,⏎ = 推荐项,esc 取消 |
| 命令面板 | ⌘K(模糊匹配主机、会话、动作) |
| 硬件键盘 | 检测到即收起附加行,只留 34pt 提示条;拔掉恢复附加行 |
长按 ⌘ |
显示全部快捷键面板(iPadOS 系统行为) |
动效:状态切换 200–260ms easeOut;侧边栏浮层从左滑入 + 遮罩淡入;最小化 = 终端缩回首页卡片(matched geometry);重连转圈 1s 线性循环。
状态徽标规则(分层,只染出问题的那层):
tsnet— 网络可达;断则本格转灰/琥珀mosh <RTT>— 数字实时跳动;≤120ms绿#a6e3a1,120–200ms中性,>200ms琥珀#fab387tmux <会话名>/ 「无 tmux · 客户端窗口」
重连:指数退避,上限 30s;卡片/胶囊显示「重连中 · 第 n 次 · ms 后」;屏幕内容本地缓存,恢复后原样接回(目标 <3s)。
安全:host key TOFU 固定在钥匙串;不匹配时拦截连接并告警;私钥存 Secure Enclave(不可导出),密码回退默认关闭。
State Management
AppRoute = .home | .terminal(sessionID)
SidebarState = .collapsed | .overlay | .pinned
Session { id, host, kind: .tmux(name) | .nativeWindow, windows[], focusedPaneID,
transport: .ssh | .sshOverTsnet | .mosh | .moshOverTsnet,
link: .direct(rtt) | .relay(derp, rtt) | .reconnecting(attempt, nextIn) | .offline,
lastActiveAt, isMinimized }
TmuxWindow { index, title, cwd, panes[] } // title 为 shell 默认值时 UI 回退显示 cwd
Host { id, name, group, user, addr, port, os, tsnetIP, hasTmux, hostKeyPinnedAt }
关键转换:选择会话 → .terminal;首页/最小化 → .home(会话保留);关闭 → detach 或 kill → .home;Pin → .pinned(触发一次 PTY resize)。
Files
terminalX iPadOS.dc.html— 全部 13 块屏幕的设计稿(画板,可缩放平移)CLAUDE.md/README.md(源项目文档,若已在仓库中则以仓库版本为准)
Assets
无位图资产。图标全部来自 Phosphor(实现时换 SF Symbols),字体 JetBrains Mono(Google Fonts),终端配色 Catppuccin。