按 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>
57 lines
1.7 KiB
Swift
57 lines
1.7 KiB
Swift
import SwiftUI
|
||
|
||
/// 编辑页共享样式:深色 Form 行背景 + 主文字色。
|
||
extension View {
|
||
func txRow(_ p: TXPalette) -> some View {
|
||
self.listRowBackground(p.surface).foregroundStyle(p.textPrimary)
|
||
}
|
||
}
|
||
|
||
/// 深色主题的表单文本字段。
|
||
struct TXTextField: View {
|
||
let title: String
|
||
@Binding var text: String
|
||
var placeholder: String = ""
|
||
var keyboard: UIKeyboardType = .default
|
||
var secure: Bool = false
|
||
@EnvironmentObject var theme: TXThemeManager
|
||
private var p: TXPalette { theme.palette }
|
||
|
||
var body: some View {
|
||
Group {
|
||
if secure {
|
||
SecureField(placeholder.isEmpty ? title : placeholder, text: $text)
|
||
} else {
|
||
TextField(placeholder.isEmpty ? title : placeholder, text: $text)
|
||
.keyboardType(keyboard)
|
||
.autocorrectionDisabled()
|
||
.textInputAutocapitalization(.never)
|
||
}
|
||
}
|
||
.foregroundStyle(p.textPrimary)
|
||
.txRow(p)
|
||
}
|
||
}
|
||
|
||
/// 空状态占位(列表为空时)。
|
||
struct TXEmptyState: View {
|
||
let icon: String
|
||
let title: String
|
||
let subtitle: String
|
||
@EnvironmentObject var theme: TXThemeManager
|
||
private var p: TXPalette { theme.palette }
|
||
|
||
var body: some View {
|
||
VStack(spacing: TX.Space.s) {
|
||
Image(systemName: icon)
|
||
.font(.system(size: 34, weight: .light))
|
||
.foregroundStyle(p.textTertiary)
|
||
Text(title).font(.system(size: 15)).foregroundStyle(p.textSecondary)
|
||
Text(subtitle).font(.system(size: 13)).foregroundStyle(p.textTertiary)
|
||
.multilineTextAlignment(.center)
|
||
}
|
||
.frame(maxWidth: .infinity)
|
||
.padding(.vertical, 48)
|
||
}
|
||
}
|