feat: 业务逻辑对齐 + 首页/弹框七项减法 + 终端页四项体验修复
业务逻辑(用户定稿): - 点主机总是新建会话(会话:主机=多:1);activate 选中断线会话自动重连 - 「直接输入」凭据保存时自动落 Keychain(去重复用),主机只存引用 - 主机编辑页可就地新建 Tailscale 连接;删无人引用的旧 HostsListView 首页/弹框减法: - 删侧边栏「连接新主机」、无 tmux 横幅与安装引导、页头「新建连接」 - 搜索移到「全部主机」行;添加主机虚线卡与主机卡等高(骨架复刻) - 会话卡加 ✕ + 复用终端页同款二次确认 - 会话选择器 sheet → 项目风格居中弹框(去手柄/Header/最近) 终端页修复: - 侧边栏会话列表改创建顺序,切换不再重排抖动 - 焦点高亮上移到 pane 容器边框(四边完整,去负 padding) - 软键盘双重让位修复:画布忽略键盘 + TXKeyboardObserver 显式让位一次 - 自动聚焦:focusTick 脉冲强制翻转 FocusState + surface 就绪补聚焦; vendor 补 iPadOS 触摸板 indirectPointer 的 becomeFirstResponder Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,7 +37,6 @@ struct TerminalSidebar: View {
|
||||
let railWidth: CGFloat
|
||||
/// home indicator 安全区(沉浸页铺满物理屏,由底栏自己让位)。
|
||||
let safeBottom: CGFloat
|
||||
let onNewHost: () -> Void
|
||||
let onSettings: () -> Void
|
||||
@Binding var search: String
|
||||
@EnvironmentObject var theme: TXThemeManager
|
||||
@@ -64,7 +63,7 @@ struct TerminalSidebar: View {
|
||||
homeRow
|
||||
sectionRow(label: "活动会话", trailing: "\(manager.sessions.count)")
|
||||
sessionRows
|
||||
newHostRow
|
||||
// 「连接新主机」入口已删(用户裁决第一版减法):新建连接只在首页。
|
||||
Spacer(minLength: TX.Space.s)
|
||||
tailscaleRow
|
||||
footRow
|
||||
@@ -182,7 +181,7 @@ struct TerminalSidebar: View {
|
||||
// MARK: - 会话
|
||||
|
||||
@ViewBuilder private var sessionRows: some View {
|
||||
if manager.activeSessions.isEmpty {
|
||||
if manager.sessions.isEmpty {
|
||||
Text("没有在跑的会话")
|
||||
.font(TX.Font.mono(11.5))
|
||||
.foregroundStyle(p.textWeak)
|
||||
@@ -190,44 +189,20 @@ struct TerminalSidebar: View {
|
||||
.frame(height: 32, alignment: .leading)
|
||||
.opacity(expanded ? 1 : 0)
|
||||
}
|
||||
ForEach(manager.activeSessions) { s in
|
||||
// **创建顺序**而非最近活跃排序(用户裁决):点击切换会更新 lastActiveAt,
|
||||
// 按活跃排序的话每次切换整列都重排 —— 行跳来跳去像抖动。首页卡片仍按活跃排。
|
||||
ForEach(manager.sessions) { s in
|
||||
SidebarSessionRow(session: s,
|
||||
isSelected: router.route.sessionID == s.id,
|
||||
expanded: expanded, railWidth: railWidth,
|
||||
hitWidth: currentWidth) {
|
||||
manager.focus(s)
|
||||
// activate:聚焦 + 已断开的自动重连(点选会话 = 想用它,不是想看「重试」按钮)。
|
||||
manager.activate(s)
|
||||
router.enter(s.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 虚线 + 号:在当前主机之外新开会话(两态都可点)。
|
||||
private var newHostRow: some View {
|
||||
Button(action: onNewHost) {
|
||||
HStack(spacing: 0) {
|
||||
Image(systemName: "plus")
|
||||
.font(.system(size: 12, weight: .medium))
|
||||
.foregroundStyle(p.textWeak)
|
||||
.frame(width: 30, height: 30)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 9)
|
||||
.strokeBorder(style: StrokeStyle(lineWidth: 1, dash: [3, 3]))
|
||||
.foregroundStyle(p.border)
|
||||
)
|
||||
.frame(width: railWidth)
|
||||
Text("连接新主机…")
|
||||
.font(TX.Font.mono(12))
|
||||
.foregroundStyle(p.text4)
|
||||
.opacity(expanded ? 1 : 0)
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
.frame(height: 40)
|
||||
.contentShape(LeadingHitShape(width: currentWidth))
|
||||
}
|
||||
.buttonStyle(.txPress)
|
||||
.accessibilityLabel("连接新主机")
|
||||
}
|
||||
|
||||
// MARK: - 底部(自上而下:tailnet 状态 · 分割线 + 齿轮)
|
||||
|
||||
/// tailnet 状态:点在图标列(两态可见),文字是明细。
|
||||
|
||||
Reference in New Issue
Block a user