feat: 侧边栏两态重构(统一视图身份)+ 第四轮触控化走查
侧边栏(真机验证): - 砍掉悬浮态,只留收起⇄固定展开;TerminalSidebar 单一视图身份, 内容恒按 288pt 排版、收起=左侧裁切(clipped 宽度动画), 图标列两态同尺寸同位置,明细淡入淡出,终端随宽度盖上/让开 - 开合入口收敛:Logo 位(收起态=»展开键/展开态=Logo)+ 头部«; 删底部开合键与 Pin;LeadingHitShape 修 clipped 不裁触摸 - 修 GridBox 从不通知 UI:onGridChange→objectWillChange, 标题胶囊格子数随开合实时刷新 触控化走查(上一轮,真机部署验证): - 窗口按钮 13pt 色点→22pt 圆角方块常显字形;动作条改可点按钮 - TXTech 拼写/配色规范(SSH/mosh/tmux 官方写法+品牌色映射) - 首页会话卡固定 3 列 4:3;去掉未实现的 ⌘K 提示 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import GhosttyTerminal
|
||||
import GhosttyTheme
|
||||
|
||||
@@ -257,6 +258,9 @@ enum TX {
|
||||
static let dialog: CGFloat = 15
|
||||
/// 浮起 chrome(标题/状态胶囊)
|
||||
static let capsule: CGFloat = 11
|
||||
/// 终端 pane 容器。**只用在朝 app chrome 的那几个角**——贴着屏幕物理边缘的角必须留直角,
|
||||
/// 否则会与设备自身的大圆角打架(详见 `TerminalScreen.paneShape`)。
|
||||
static let pane: CGFloat = 6
|
||||
|
||||
// 兼容旧名
|
||||
static let chip: CGFloat = 9
|
||||
@@ -291,8 +295,94 @@ enum TX {
|
||||
static let minTouch: CGFloat = 44
|
||||
/// 终端主 pane 顶部给浮起胶囊预留
|
||||
static let capsuleInset: CGFloat = 86
|
||||
/// 底部区高度:**舞台提示条与侧边栏脚共用**,故两侧的分割线落在同一条 y 上。
|
||||
/// 底部区**内容带**高度:舞台提示条 / 侧边栏脚 / 轨道齿轮共用,故三者中心线与上方那条
|
||||
/// 分割线都落在同一条 y 上。真机上还要在下面叠 home indicator 的安全区
|
||||
/// —— 沉浸页把 chrome 铺到物理边缘,安全区由底栏自己让出(见 `TX.Layout.footBand`)。
|
||||
static let footHeight: CGFloat = 34
|
||||
|
||||
/// 底部区总高 = 内容带 + 安全区。内容在其中**居中**:既贴住物理底边不留悬空的死带,
|
||||
/// 又不会压到 home indicator 上。
|
||||
static func footBand(_ safeBottom: CGFloat) -> CGFloat { footHeight + safeBottom }
|
||||
|
||||
/// 沉浸态浮起胶囊距**物理**顶边的距离。
|
||||
///
|
||||
/// iPad 隐藏状态栏后顶边没有遮挡 → 用设计值 14。灵动岛/刘海机型的安全区读数很大
|
||||
/// (≥30),此时按硬件让位,避免胶囊钻到岛下面。
|
||||
static func capsuleTop(_ safeTop: CGFloat) -> CGFloat {
|
||||
safeTop > 30 ? safeTop - 12 : 14
|
||||
}
|
||||
}
|
||||
|
||||
/// 动效。设计定稿:状态切换 200–260ms `easeOut`。
|
||||
///
|
||||
/// 所有过渡都从这里取曲线 —— 点击、快捷键、无头 URL 驱动的同一个状态变更拿到同一条动画,
|
||||
/// 并统一响应系统「减弱动态」(位移退化为纯淡入淡出)。
|
||||
enum Motion {
|
||||
/// 常规状态切换:路由、侧边栏三态、模态浮层。
|
||||
@MainActor static var standard: Animation {
|
||||
.easeOut(duration: (reduceMotion ? 0.12 : 0.24) * slowFactor)
|
||||
}
|
||||
/// 轻量反馈:选中态、状态点变色、按压回弹。短一点,避免拖泥带水。
|
||||
@MainActor static var quick: Animation {
|
||||
.easeOut(duration: (reduceMotion ? 0.08 : 0.14) * slowFactor)
|
||||
}
|
||||
@MainActor static var reduceMotion: Bool { UIAccessibility.isReduceMotionEnabled }
|
||||
|
||||
/// 无头验证用的慢放(`-txSlowMotion 1` → ×12)。
|
||||
///
|
||||
/// `simctl io screenshot` 本身要几百毫秒,抓不到 240ms 的过渡中间帧 —— 想用截图证明
|
||||
/// "动效真的在跑"(而不是又退化成硬切)就得先把它放慢。仅 DEBUG。
|
||||
private static var slowFactor: Double {
|
||||
#if DEBUG
|
||||
UserDefaults.standard.bool(forKey: "txSlowMotion") ? 12 : 1
|
||||
#else
|
||||
1
|
||||
#endif
|
||||
}
|
||||
|
||||
/// 有位移的转场(侧边栏浮层从左滑入)。开了「减弱动态」只淡入淡出。
|
||||
@MainActor static func slide(_ edge: Edge) -> AnyTransition {
|
||||
reduceMotion ? .opacity : .move(edge: edge)
|
||||
}
|
||||
/// 浮层弹出:淡入 + 极轻的缩放(96% → 100%),让对话框"长出来"而不是硬切。
|
||||
@MainActor static var pop: AnyTransition {
|
||||
reduceMotion ? .opacity : .opacity.combined(with: .scale(scale: 0.96))
|
||||
}
|
||||
/// 首页 ⇄ 终端:首页略微推远、终端从 97% 放大到位,读作"推进到会话里"。
|
||||
@MainActor static var pushBack: AnyTransition {
|
||||
reduceMotion ? .opacity : .opacity.combined(with: .scale(scale: 1.02))
|
||||
}
|
||||
}
|
||||
|
||||
/// 阴影。**刻意比设计初稿扁平**:暗色 chrome 靠描边分层,阴影只做极轻的浮起暗示。
|
||||
/// 初稿那套 CSS 值(30–80px / .55–.7 黑)在深色底上会糊成一团脏斑,浮起胶囊尤其明显
|
||||
/// —— 终端文字从投影里穿过去,观感是"胶囊在漏墨"。故统一收到小半径、低透明度。
|
||||
struct Shadow {
|
||||
let opacity: Double
|
||||
let radius: CGFloat
|
||||
var x: CGFloat = 0
|
||||
var y: CGFloat = 0
|
||||
}
|
||||
|
||||
enum Elevation {
|
||||
/// 浮起 chrome(标题 / 状态胶囊):贴在终端内容之上,投影必须几乎不可见。
|
||||
static let floating = Shadow(opacity: 0.22, radius: 7, y: 2)
|
||||
/// 侧边栏浮层:唯一带方向感的投影(往右推),让面板与被压暗的终端分层。
|
||||
static let panel = Shadow(opacity: 0.28, radius: 16, x: 4)
|
||||
/// 对话框 / 菜单。
|
||||
static let dialog = Shadow(opacity: 0.34, radius: 16, y: 6)
|
||||
/// 会话缩略卡(聚焦时略强)。
|
||||
static let card = Shadow(opacity: 0.16, radius: 4, y: 2)
|
||||
static let cardFocused = Shadow(opacity: 0.24, radius: 8, y: 3)
|
||||
}
|
||||
|
||||
/// 浮层遮罩:对话框与侧边栏浮层共用同一底色,只有浓度不同。
|
||||
enum Scrim {
|
||||
static let color = Color(hex: 0x0A0B12)
|
||||
/// 对话框:要求"必须先处理它",压得更实。
|
||||
static let dialog: Double = 0.5
|
||||
/// 侧边栏浮层:面板本身已占掉一大片,遮罩轻一点。
|
||||
static let sidebar: Double = 0.42
|
||||
}
|
||||
|
||||
enum Font {
|
||||
|
||||
Reference in New Issue
Block a user