Fix phone keyboard and touch targets, render notes as Markdown
This commit is contained in:
@@ -439,7 +439,7 @@ private struct WebButton: View {
|
||||
.font(Fonts.font(13, weight: primary ? 700 : 400))
|
||||
.foregroundStyle(primary ? Palette.bg : danger ? Palette.danger : Palette.text)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 4)
|
||||
.padding(.vertical, touchOnly ? 7 : 4)
|
||||
.background(primary ? fill : Palette.raised, in: RoundedRectangle(cornerRadius: 6))
|
||||
.overlay(RoundedRectangle(cornerRadius: 6).stroke(primary ? fill : hover && enabled ? Palette.muted : Palette.border))
|
||||
.opacity(enabled ? (configuration.isPressed ? 0.8 : 1) : 0.45)
|
||||
@@ -458,7 +458,9 @@ struct SmallLinkButtonStyle: ButtonStyle {
|
||||
.font(Fonts.font(11))
|
||||
.foregroundStyle(color)
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, touchOnly ? 3 : 0)
|
||||
.overlay(RoundedRectangle(cornerRadius: 4).stroke(border))
|
||||
.padding(.vertical, touchOnly ? 5 : 0)
|
||||
.opacity(enabled ? 1 : 0.45)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
|
||||
@@ -124,11 +124,9 @@ private struct NoteRow: View {
|
||||
@ViewState private var frame: CGRect = .zero
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .firstTextBaseline, spacing: 12) {
|
||||
Text(note.text)
|
||||
.textSelection(.enabled)
|
||||
HStack(alignment: .top, spacing: 12) {
|
||||
MarkdownView(blocks: parseMarkdown(note.text))
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
Button("Delete") {
|
||||
Task {
|
||||
if !(await overlays.showConfirm(["Delete this note? Your lead stops applying it.", note.text], action: "Delete", danger: true, anchor: frame)) {
|
||||
|
||||
@@ -56,6 +56,7 @@ struct PhoneView: View {
|
||||
@Environment(Overlays.self) private var overlays
|
||||
@ViewState private var tab = PhoneTab.lead
|
||||
@ViewState private var path: [PhoneRoute] = []
|
||||
@ViewState private var typing = false
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
@@ -77,36 +78,17 @@ struct PhoneView: View {
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
HStack(spacing: 4) {
|
||||
tabButton("Lead", .lead)
|
||||
HStack(spacing: 0) {
|
||||
tabButton("Tasks", .tasks)
|
||||
if !store.inbox.isEmpty {
|
||||
Badge(count: store.inbox.count)
|
||||
}
|
||||
}
|
||||
tabButton("More", .more)
|
||||
Spacer(minLength: 0)
|
||||
ConnectionMark()
|
||||
Button {
|
||||
tab = .tasks
|
||||
path = []
|
||||
} label: {
|
||||
Image(systemName: "list.bullet")
|
||||
.font(Fonts.font(17, weight: 600))
|
||||
.foregroundStyle(Palette.accent)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 8)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel("Task list")
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
.overlay(alignment: .top) {
|
||||
Rectangle().fill(Palette.border).frame(height: 1)
|
||||
if !typing {
|
||||
tabBar
|
||||
}
|
||||
}
|
||||
.scrollDismissesKeyboard(.interactively)
|
||||
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)) { _ in
|
||||
typing = true
|
||||
}
|
||||
.onReceive(NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)) { _ in
|
||||
typing = false
|
||||
}
|
||||
.overlay {
|
||||
OverlayHost()
|
||||
}
|
||||
@@ -159,6 +141,39 @@ struct PhoneView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var tabBar: some View {
|
||||
HStack(spacing: 4) {
|
||||
tabButton("Lead", .lead)
|
||||
HStack(spacing: 0) {
|
||||
tabButton("Tasks", .tasks)
|
||||
if !store.inbox.isEmpty {
|
||||
Badge(count: store.inbox.count)
|
||||
}
|
||||
}
|
||||
tabButton("More", .more)
|
||||
Spacer(minLength: 0)
|
||||
ConnectionMark()
|
||||
Button {
|
||||
tab = .tasks
|
||||
path = []
|
||||
} label: {
|
||||
Image(systemName: "list.bullet")
|
||||
.font(Fonts.font(17, weight: 600))
|
||||
.foregroundStyle(Palette.accent)
|
||||
.padding(.horizontal, 10)
|
||||
.padding(.vertical, 8)
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel("Task list")
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 4)
|
||||
.overlay(alignment: .top) {
|
||||
Rectangle().fill(Palette.border).frame(height: 1)
|
||||
}
|
||||
}
|
||||
|
||||
private func shown(_ which: PhoneTab, @ViewBuilder _ content: () -> some View) -> some View {
|
||||
content()
|
||||
.opacity(tab == which ? 1 : 0)
|
||||
|
||||
@@ -83,7 +83,7 @@ struct ChecksView: View {
|
||||
case "required":
|
||||
return "Required"
|
||||
case "agent":
|
||||
return "One of claude, codex and agy required"
|
||||
return "One of claude, codex or agy required"
|
||||
default:
|
||||
return "Optional"
|
||||
}
|
||||
|
||||
@@ -303,7 +303,9 @@ final class SwitcherModel {
|
||||
if store.inbox.contains(where: { $0.taskId == taskId }) {
|
||||
store.showQuestion(taskId)
|
||||
} else {
|
||||
store.composerFocus = taskId
|
||||
if !touchOnly {
|
||||
store.composerFocus = taskId
|
||||
}
|
||||
store.focusTask(taskId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1054,7 +1054,11 @@ private func toolTarget(_ input: (any Sendable)?, _ root: String) -> String {
|
||||
guard let path = (i?["file_path"] ?? i?["path"]) as? String else {
|
||||
return ""
|
||||
}
|
||||
return relative(path, root)
|
||||
let short = relative(path, root)
|
||||
if short != path || !path.hasPrefix("/") {
|
||||
return short
|
||||
}
|
||||
return URL(fileURLWithPath: path).lastPathComponent
|
||||
}
|
||||
|
||||
private func relative(_ path: String, _ root: String) -> String {
|
||||
|
||||
Reference in New Issue
Block a user