import SwiftUI struct ContentView: View { @EnvironmentObject var engine: PlayerEngine @State private var columnVisibility: NavigationSplitViewVisibility = .all @State private var showURLInput = false @State private var urlInput = "" var body: some View { Group { if engine.isFullscreen { PlayerView() .onTapGesture(count: 2) { engine.toggleFullscreen() } } else { NavigationSplitView(columnVisibility: $columnVisibility) { playlistSidebar } detail: { PlayerView() } .navigationSplitViewStyle(.balanced) .navigationSplitViewColumnWidth(min: 240, ideal: 300, max: 400) } } .alert("添加流媒体 URL", isPresented: $showURLInput) { TextField("M3U8 或其他流媒体 URL", text: $urlInput) Button("添加") { addStreamURL() } Button("取消", role: .cancel) {} } message: { Text("输入 M3U8、MP4 或其他流媒体地址") } .keyboardShortcut(.escape, action: { if engine.isFullscreen { engine.toggleFullscreen() } }) } // MARK: - 侧边栏 private var playlistSidebar: some View { VStack(spacing: 0) { PlaylistView() Divider() // 底部工具栏: 添加流URL + 循环模式 HStack(spacing: 12) { Button { urlInput = "" showURLInput = true } label: { Label("添加流 URL", systemImage: "link") .font(.caption) } .buttonStyle(.bordered) .controlSize(.small) Spacer() // 循环模式 Button { engine.repeatMode = engine.repeatMode.next } label: { HStack(spacing: 4) { Image(systemName: engine.repeatMode.icon) Text(repeatModeText) } .font(.caption) .foregroundStyle(engine.repeatMode == .none ? .secondary : .blue) } .buttonStyle(.plain) } .padding(.horizontal, 16) .padding(.vertical, 10) } } private var repeatModeText: String { switch engine.repeatMode { case .none: return "不循环" case .single: return "单曲" case .all: return "列表" } } private func addStreamURL() { guard !urlInput.isEmpty, let url = URL(string: urlInput) else { return } engine.addToQueue(urls: [url]) } } // ESC 快捷键辅助 extension View { func keyboardShortcut(_ key: KeyEquivalent, action: @escaping () -> Void) -> some View { self.background( KeyboardShortcutView(key: key, action: action) ) } } struct KeyboardShortcutView: View { let key: KeyEquivalent let action: () -> Void var body: some View { Button(action: action) { EmptyView() } .keyboardShortcut(key, modifiers: []) .hidden() } }