Fix which server the iPhone Action button uses

This commit is contained in:
Ian Gulliver
2026-10-22 17:24:42 -07:00
parent 9df0c800dc
commit 78880be9b6
4 changed files with 78 additions and 24 deletions
-4
View File
@@ -140,10 +140,6 @@ struct PhoneView: View {
.environment(overlays) .environment(overlays)
} }
} }
.fullScreenCover(isPresented: Binding(get: { QuickRunRequest.shared.shown }, set: { QuickRunRequest.shared.shown = $0 })) {
QuickRunPage()
.environment(store)
}
.onAppear { .onAppear {
store.paneFocus.attach() store.paneFocus.attach()
} }
+74 -19
View File
@@ -116,16 +116,46 @@ struct ByreShortcuts: AppShortcutsProvider {
} }
} }
@MainActor
private struct QuickRunChoice {
let origin: String
let store: Store
let overlays: Overlays
let run: Components.Schemas.QuickRun
var project: String {
store.projects.first { $0.id == run.projectId }?.name ?? run.projectId
}
}
struct QuickRunPage: View { struct QuickRunPage: View {
@Environment(Store.self) private var store @ViewState private var picked: String?
@ViewState private var text = "" @ViewState private var text = ""
@ViewState private var starting = false @ViewState private var starting = false
@ViewState private var failure: String? @ViewState private var failure: String?
@FocusState private var focused: Bool @FocusState private var focused: Bool
private var choices: [QuickRunChoice] {
Servers.shared.origins.compactMap { origin in
guard let connection = Servers.shared.connections[origin], let store = connection.store, let run = store.me?.quickRun, !run.workflow.isEmpty else {
return nil
}
return QuickRunChoice(origin: origin, store: store, overlays: connection.overlays, run: run)
}
}
private var connecting: Bool {
Servers.shared.connections.values.contains {
if case .starting = $0.phase {
return true
}
return false
}
}
var body: some View { var body: some View {
let q = store.me?.quickRun ?? .init(projectId: "", workflow: "") let choices = choices
let project = store.projects.first { $0.id == q.projectId } let choice = choices.first { $0.origin == picked } ?? (choices.count == 1 ? choices.first : nil)
let prompt = text.trimmingCharacters(in: .whitespacesAndNewlines) let prompt = text.trimmingCharacters(in: .whitespacesAndNewlines)
VStack(alignment: .leading, spacing: 12) { VStack(alignment: .leading, spacing: 12) {
HStack { HStack {
@@ -134,29 +164,55 @@ struct QuickRunPage: View {
} }
.disabled(starting) .disabled(starting)
Spacer() Spacer()
if !q.workflow.isEmpty { if let choice {
Button("Start") { Button("Start") {
start(q, prompt) start(choice, prompt)
} }
.buttonStyle(WebButtonStyle(primary: true)) .buttonStyle(WebButtonStyle(primary: true))
.disabled(prompt.isEmpty || starting) .disabled(prompt.isEmpty || starting)
} }
} }
.font(Fonts.font(15, weight: 700)) .font(Fonts.font(15, weight: 700))
if q.workflow.isEmpty { if let choice {
Text("Choose the workflow the Action button starts in More → Settings.")
.foregroundStyle(Palette.muted)
.fixedSize(horizontal: false, vertical: true)
} else {
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
Text(q.workflow) Text(choice.run.workflow)
.font(Fonts.font(17, weight: 700)) .font(Fonts.font(17, weight: 700))
Text(project?.name ?? q.projectId) Text("\(choice.project) · \(serverName(choice.origin))")
.foregroundStyle(Palette.muted) .foregroundStyle(Palette.muted)
} }
AgentEditor(text: $text, placeholder: "What should it do?", key: "quick-run", height: 220, focus: $focused, border: focused ? Palette.accent : Palette.border) { AgentEditor(text: $text, placeholder: "What should it do?", key: "quick-run", height: 220, focus: $focused, border: focused ? Palette.accent : Palette.border) {
start(q, prompt) start(choice, prompt)
} }
.environment(choice.store)
.environment(choice.overlays)
.onAppear {
focused = true
}
} else if !choices.isEmpty {
ForEach(choices, id: \.origin) { c in
Button {
picked = c.origin
} label: {
VStack(alignment: .leading, spacing: 2) {
Text(c.run.workflow)
.font(Fonts.font(17, weight: 700))
Text("\(c.project) · \(serverName(c.origin))")
.foregroundStyle(Palette.muted)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(12)
.overlay(RoundedRectangle(cornerRadius: 6).stroke(Servers.shared.color(c.origin)))
.contentShape(Rectangle())
}
.buttonStyle(.plain)
}
} else if connecting {
Text("Connecting to your servers…")
.foregroundStyle(Palette.muted)
} else {
Text("Choose the workflow the Action button starts in More → Settings.")
.foregroundStyle(Palette.muted)
.fixedSize(horizontal: false, vertical: true)
} }
if let failure { if let failure {
Text(failure) Text(failure)
@@ -172,12 +228,9 @@ struct QuickRunPage: View {
.tint(Palette.accent) .tint(Palette.accent)
.background(Palette.bg) .background(Palette.bg)
.font(Fonts.font(13)) .font(Fonts.font(13))
.onAppear {
focused = true
}
} }
private func start(_ q: Components.Schemas.QuickRun, _ prompt: String) { private func start(_ choice: QuickRunChoice, _ prompt: String) {
if prompt.isEmpty || starting { if prompt.isEmpty || starting {
return return
} }
@@ -185,9 +238,10 @@ struct QuickRunPage: View {
failure = nil failure = nil
Task { Task {
do { do {
let task = try await store.api.client.createTask(path: .init(projectId: q.projectId), body: .json(.init(prompt: prompt, workflow: q.workflow))).created.body.json let task = try await choice.store.api.client.createTask(path: .init(projectId: choice.run.projectId), body: .json(.init(prompt: prompt, workflow: choice.run.workflow))).created.body.json
close() close()
store.focusTask(task.id) Servers.shared.current = choice.origin
choice.store.focusTask(task.id)
} catch { } catch {
starting = false starting = false
failure = errorText(error) failure = errorText(error)
@@ -196,6 +250,7 @@ struct QuickRunPage: View {
} }
private func close() { private func close() {
picked = nil
text = "" text = ""
starting = false starting = false
failure = nil failure = nil
+1 -1
View File
@@ -55,7 +55,7 @@ struct SettingsView: View {
try await store.api.client.updateDefaultModel(body: .json(.init(model: m))).ok.body.json try await store.api.client.updateDefaultModel(body: .json(.init(model: m))).ok.body.json
} }
heading("Action button workflow") heading("Action button workflow")
Text("The iPhone's Action button, set to byre's Run workflow shortcut, opens a page to type a prompt and start this workflow with it.") Text("The iPhone's Action button, set to byre's Run workflow shortcut, opens a page to type a prompt and start this workflow with it. When more than one server has one set, the page asks which to start.")
.foregroundStyle(Palette.muted) .foregroundStyle(Palette.muted)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
QuickRunSetting() QuickRunSetting()
+3
View File
@@ -584,6 +584,9 @@ struct ServerWindow: View {
.tint(Palette.accent) .tint(Palette.accent)
.background(Palette.bg) .background(Palette.bg)
.font(Fonts.font(13)) .font(Fonts.font(13))
.fullScreenCover(isPresented: Binding(get: { QuickRunRequest.shared.shown }, set: { QuickRunRequest.shared.shown = $0 })) {
QuickRunPage()
}
} }
#endif #endif