Fix phone keyboard and touch targets, render notes as Markdown

This commit is contained in:
Ian Gulliver
2026-10-22 06:33:03 -07:00
parent e2d747d7d3
commit 188a458930
7 changed files with 59 additions and 36 deletions
+3 -1
View File
@@ -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())
}
+2 -4
View File
@@ -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)) {
+43 -28
View File
@@ -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)
+1 -1
View File
@@ -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"
}
+3 -1
View File
@@ -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)
}
}
+5 -1
View File
@@ -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 {