Remove the task Project tab; ⌘P selects the project
This commit is contained in:
@@ -52,7 +52,6 @@ final class Overlays {
|
||||
var switcher: SwitcherModel?
|
||||
var leftTab = LeftTab.lead
|
||||
var leadFocus = 0
|
||||
var projectFocus = 0
|
||||
@ObservationIgnored var pickerFrame: CGRect = .zero
|
||||
@ObservationIgnored var menuHover = false
|
||||
var paneFrame: CGRect = .zero
|
||||
|
||||
@@ -10,7 +10,6 @@ let tabLabels: [TaskTab: String] = [
|
||||
.page: "Page",
|
||||
.terminal: "Terminal",
|
||||
.workflow: "Workflow",
|
||||
.project: "Project",
|
||||
]
|
||||
|
||||
struct HalfSplit: Layout {
|
||||
@@ -191,9 +190,13 @@ private struct TaskPane: View {
|
||||
VStack(spacing: 0) {
|
||||
TaskBar()
|
||||
VStack(spacing: 0) {
|
||||
TabStrip()
|
||||
TaskTabBody(tab: store.primaryTab)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
if store.selectedTaskId == nil {
|
||||
ProjectView()
|
||||
} else {
|
||||
TabStrip()
|
||||
TaskTabBody(tab: store.primaryTab)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
if let sheet = store.sheet, sheet.owner == .lead {
|
||||
@@ -239,8 +242,6 @@ struct TaskTabBody: View {
|
||||
TerminalTab()
|
||||
case .workflow:
|
||||
WorkflowView()
|
||||
case .project:
|
||||
ProjectView()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -377,13 +378,13 @@ private struct TabStrip: View {
|
||||
private func info() -> Info {
|
||||
let tab = store.sheet.flatMap { $0.owner == .lead ? nil : $0.tab } ?? store.primaryTab
|
||||
guard let taskId = store.selectedTaskId else {
|
||||
return Info(order: [.project], tab: .project)
|
||||
return Info(order: [], tab: tab)
|
||||
}
|
||||
let run = store.findTask(taskId)?.workflow != nil
|
||||
let team = !store.team(taskId).isEmpty
|
||||
let order = taskTabs.filter { t in
|
||||
if run {
|
||||
return t == .workflow || t == .project
|
||||
return t == .workflow
|
||||
}
|
||||
if t == tab {
|
||||
return true
|
||||
|
||||
@@ -8,48 +8,22 @@ private let refreshKinds: Set<String> = ["task.merged", "project.created"]
|
||||
|
||||
struct ProjectView: View {
|
||||
@Environment(Store.self) private var store
|
||||
@Environment(Overlays.self) private var overlays
|
||||
@ViewState private var todos: [String: Result<Todo, ApiError>] = [:]
|
||||
@ViewState private var off: (() -> Void)?
|
||||
@FocusState private var keys: Bool
|
||||
|
||||
var body: some View {
|
||||
ScrollViewReader { proxy in
|
||||
ScrollView {
|
||||
if let p = store.focusedProject() {
|
||||
ProjectBlock(project: p, todo: todos[p.id])
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, 8)
|
||||
.padding(.bottom, 16)
|
||||
} else {
|
||||
Muted(text: "No project selected.")
|
||||
}
|
||||
}
|
||||
.onChange(of: store.selectedTaskId) {
|
||||
if let id = store.selectedTaskId {
|
||||
proxy.scrollTo(id)
|
||||
}
|
||||
ScrollView {
|
||||
if let p = store.focusedProject() {
|
||||
ProjectBlock(project: p, todo: todos[p.id])
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, 8)
|
||||
.padding(.bottom, 16)
|
||||
} else {
|
||||
Muted(text: "No project selected.")
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.focusable()
|
||||
.focusEffectDisabled()
|
||||
.focused($keys)
|
||||
.onKeyPress(keys: [.upArrow, .downArrow]) { press in
|
||||
step(press.key == .downArrow ? 1 : -1)
|
||||
return .handled
|
||||
}
|
||||
.onKeyPress(.return) {
|
||||
guard store.selectedTaskId != nil, store.focusedProject() != nil else {
|
||||
return .ignored
|
||||
}
|
||||
store.sheet = nil
|
||||
return .handled
|
||||
}
|
||||
.onAppear {
|
||||
if !(overlays.window?.firstResponder is NSTextView) {
|
||||
keys = true
|
||||
}
|
||||
off = store.events.add { ev in
|
||||
if refreshKinds.contains(ev.kind), let projectId = ev.projectId {
|
||||
load(projectId)
|
||||
@@ -57,9 +31,6 @@ struct ProjectView: View {
|
||||
}
|
||||
refresh()
|
||||
}
|
||||
.onChange(of: overlays.projectFocus) {
|
||||
keys = true
|
||||
}
|
||||
.onDisappear {
|
||||
off?()
|
||||
off = nil
|
||||
@@ -79,25 +50,6 @@ struct ProjectView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func step(_ by: Int) {
|
||||
guard let p = store.focusedProject() else {
|
||||
return
|
||||
}
|
||||
let active = (store.tasks[p.id] ?? []).filter { $0.status == .active }
|
||||
let rows = nested(store, active).map(\.task.id)
|
||||
if rows.isEmpty {
|
||||
return
|
||||
}
|
||||
let next: Int
|
||||
if let i = rows.firstIndex(where: { $0 == store.selectedTaskId }) {
|
||||
next = min(max(i + by, 0), rows.count - 1)
|
||||
} else {
|
||||
next = by > 0 ? 0 : rows.count - 1
|
||||
}
|
||||
store.sheet = Sheet(owner: .task(rows[next]), view: .tab(.project))
|
||||
store.focusTask(rows[next])
|
||||
}
|
||||
|
||||
private func refresh() {
|
||||
if let p = store.focusedProject() {
|
||||
load(p.id)
|
||||
|
||||
@@ -3,10 +3,10 @@ import Observation
|
||||
import SwiftData
|
||||
|
||||
enum TaskTab: String {
|
||||
case transcript, board, changes, files, page, terminal, workflow, project
|
||||
case transcript, board, changes, files, page, terminal, workflow
|
||||
}
|
||||
|
||||
let taskTabs: [TaskTab] = [.transcript, .board, .changes, .files, .page, .terminal, .workflow, .project]
|
||||
let taskTabs: [TaskTab] = [.transcript, .board, .changes, .files, .page, .terminal, .workflow]
|
||||
|
||||
enum JumpTarget: Equatable {
|
||||
case question(String)
|
||||
@@ -470,10 +470,7 @@ final class Store {
|
||||
}
|
||||
|
||||
var primaryTab: TaskTab {
|
||||
guard let id = selectedTaskId else {
|
||||
return .project
|
||||
}
|
||||
return findTask(id)?.workflow != nil ? .workflow : .transcript
|
||||
selectedTaskId.flatMap { findTask($0) }?.workflow != nil ? .workflow : .transcript
|
||||
}
|
||||
|
||||
func openTab(_ tab: TaskTab) {
|
||||
@@ -661,7 +658,7 @@ final class Store {
|
||||
|
||||
func selectTask(_ taskId: String?) {
|
||||
if case .task(let owner)? = sheet?.owner, owner != taskId {
|
||||
sheet = sheet?.tab == .project && taskId != nil ? Sheet(owner: .task(taskId), view: .tab(.project)) : nil
|
||||
sheet = nil
|
||||
}
|
||||
markVisited(selectedTaskId)
|
||||
markVisited(taskId)
|
||||
|
||||
@@ -17,10 +17,10 @@ func runGo(_ key: String, _ store: Store, _ overlays: Overlays) {
|
||||
createTask(project.id, .init(), store, overlays)
|
||||
}
|
||||
case "p":
|
||||
overlays.window?.makeFirstResponder(nil)
|
||||
overlays.projectFocus += 1
|
||||
if let id = store.selectedTaskId {
|
||||
store.sheet = Sheet(owner: .task(id), view: .tab(.project))
|
||||
overlays.switcher?.close()
|
||||
store.sheet = nil
|
||||
if let project = store.focusedProject() ?? store.projects.first {
|
||||
store.focusProject(project.id)
|
||||
}
|
||||
case "l":
|
||||
overlays.switcher?.close()
|
||||
@@ -71,7 +71,7 @@ private let shortcuts: [(String, [(String, String)])] = [
|
||||
("Anywhere", [
|
||||
("⌘K", "Switch task, or create one"),
|
||||
("⌘N", "New task in the current project"),
|
||||
("⌘P", "Open the Project tab"),
|
||||
("⌘P", "Show the current project's dashboard"),
|
||||
("⌘L", "Open the Lead tab and its composer, closing any view opened over a pane"),
|
||||
("⌘S", "Merge the right pane's task, or discard it when the agent suggests discarding"),
|
||||
("⌘[ ⌘]", "Bring the previous or next server's window forward"),
|
||||
|
||||
Reference in New Issue
Block a user