Files
terminalX/apps/TerminalX/iOS/TerminalDialogs.swift
kid 5d1abdc7ac feat: 修复终端换行错乱根因 + 顶带重设计 + 移除移动端分屏
- 换行错乱根因:tmux 客户端格子改以 ghostty 实测为权威(像素除法
  偏大 1 行/列 → 远端按更宽排版、本地自动换行掉字符),单 pane 实测
  格子直发 refresh-client;kickoff 用 raw surface 真实格子;顶栏
  格子数 tmux 模式读 clientGrid(模拟器连本机 sshd 端到端验证)
- 顶带落地为真实 chrome 行:✕/⌄ 独立按钮组 + 弱化标题 +
  TmuxWindowTabGroup window tab 组,pane 不再被悬浮胶囊遮内容
- 放弃移动端分屏:删 SplitMenu/分屏入口,tmux window 切换上移顶部
  tab,侧边栏改列活动会话;底部动作条合并状态行
- Info.plist 补 NSLocalNetworkUsageDescription(LAN 直连必需,
  缺失时 socket 静默超时)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 11:11:20 +08:00

140 lines
5.5 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import SwiftUI
// 稿 `3c ``3b `
//
//
// - ** / M / = **
// - ** / W = **`esc` ``
/// + /
///
/// 96% `TX.Motion.pop`
/// `.transition(.opacity)`
/// `AppRouter.present/dismissModal`
struct TXModalScrim<Content: View>: View {
let onDismiss: () -> Void
@ViewBuilder let content: Content
var body: some View {
ZStack {
TX.Scrim.color.opacity(TX.Scrim.dialog)
.ignoresSafeArea()
.contentShape(Rectangle())
.onTapGesture(perform: onDismiss)
.transition(.opacity)
content
.transition(TX.Motion.pop)
}
}
}
// MARK: - 3c
///
/// - **tmux **`detach-client`
/// `kill-session`****
/// - **** tmux
struct CloseConfirmDialog: View {
@ObservedObject var session: TerminalSession
/// tmux/
let onDetach: () -> Void
/// tmux kill-session
let onKill: () -> Void
let onCancel: () -> Void
@EnvironmentObject var theme: TXThemeManager
@State private var confirmingKill = false
private var p: TXPalette { theme.palette }
private var isTmux: Bool { session.tmuxSummary != nil }
var body: some View {
TXModalScrim(onDismiss: onCancel) {
VStack(alignment: .leading, spacing: 0) {
Text(title)
.font(TX.Font.mono(15, .medium))
.foregroundStyle(p.text)
.padding(.bottom, 8)
message
.padding(.bottom, TX.Space.l)
buttons
}
.padding(TX.Space.l)
.frame(width: isTmux ? 436 : 404)
.background(p.surfaceDark, in: RoundedRectangle(cornerRadius: TX.Radius.dialog))
.overlay(
RoundedRectangle(cornerRadius: TX.Radius.dialog)
.strokeBorder(p.borderStrong, lineWidth: 1)
)
.txShadow(TX.Elevation.dialog)
}
}
private var title: String {
if let t = session.tmuxSummary, !t.name.isEmpty {
return "关闭 \(session.displayName) · \(t.name)?"
}
// 1
return "关闭 \(session.displayName) · 窗口 1?"
}
@ViewBuilder private var message: some View {
if let t = session.tmuxSummary {
Text("tmux 会话 (\(t.windows) windows / \(t.panes) panes)。仅断开后服务端继续跑,下次原样接回。")
.font(TX.Font.mono(12))
.foregroundStyle(p.text3)
.lineSpacing(4)
} else {
Text("没有 tmux 兜底,")
.font(TX.Font.mono(12))
.foregroundStyle(p.text3)
+ Text("前台进程会随之结束")
.font(TX.Font.mono(12, .medium))
.foregroundStyle(p.warning)
+ Text("。想留着就点黄点最小化。")
.font(TX.Font.mono(12))
.foregroundStyle(p.text3)
}
}
@ViewBuilder private var buttons: some View {
HStack(spacing: TX.Space.s) {
if isTmux {
// kill-session
Button {
if confirmingKill { onKill() } else { confirmingKill = true }
} label: {
Text(confirmingKill ? "确认结束会话?" : "结束会话")
.font(TX.Font.mono(12, confirmingKill ? .semibold : .regular))
.foregroundStyle(p.danger)
}
.buttonStyle(.txPress)
}
Spacer()
TXButton(title: "取消", action: onCancel)
if isTmux {
TXButton(title: "仅断开 ⏎", emphasized: true, action: onDetach)
} else {
dangerButton
}
}
}
/// #8d5560 / rgba(92,58,63,.35) / #f5a0ac
private var dangerButton: some View {
Button(action: onDetach) {
Text("关闭窗口")
.font(TX.Font.mono(12, .medium))
.foregroundStyle(Color(hex: 0xF5A0AC))
.padding(.horizontal, 14)
.frame(height: 32)
.background(Color(hex: 0x5C3A3F).opacity(0.35),
in: RoundedRectangle(cornerRadius: TX.Radius.control))
.overlay(
RoundedRectangle(cornerRadius: TX.Radius.control)
.strokeBorder(Color(hex: 0x8D5560), lineWidth: 1)
)
}
.buttonStyle(.txPress)
}
}