Add dictation-locale transcription (incl. Russian), rework list capture UI
- TranscriptionSettings/Service route per-locale to SpeechTranscriber or DictationTranscriber (union of both supported sets, speech preferred); neutral-confidence ranking fallback keeps mixed-engine auto-pick working - NotesListView: text + voice capture buttons above the list (glassProminent), search moved to the iOS 26 toolbar search accessory (.minimize) - Changelog entries for languages, capture buttons, search placement
This commit is contained in:
@@ -16,57 +16,20 @@ struct NotesListView: View {
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
if let error = syncEngine.lastSyncError {
|
||||
Section {
|
||||
Label(error, systemImage: "exclamationmark.icloud")
|
||||
.foregroundStyle(.red)
|
||||
.font(.footnote)
|
||||
}
|
||||
}
|
||||
|
||||
if searchText.isEmpty, !relevantNotes.isEmpty {
|
||||
Section("Right here, right now") {
|
||||
ForEach(relevantNotes) { note in
|
||||
row(note)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section(searchText.isEmpty ? "All notes" : "Results") {
|
||||
ForEach(filteredNotes) { note in
|
||||
row(note)
|
||||
}
|
||||
}
|
||||
VStack(spacing: 0) {
|
||||
captureButtons
|
||||
list
|
||||
}
|
||||
.navigationTitle("Notes")
|
||||
// iOS 26: attached at the NavigationStack root inside a TabView, this
|
||||
// renders as the Liquid-Glass toolbar search accessory (bottom, in the
|
||||
// tab bar). `.searchToolbarBehavior(.minimize)` collapses it to the
|
||||
// magnifier capsule until tapped — a modifier that only exists on iOS,
|
||||
// hence the guard; macOS keeps the standard toolbar search field.
|
||||
.searchable(text: $searchText, prompt: "Search notes and tags")
|
||||
.overlay {
|
||||
if notes.isEmpty {
|
||||
ContentUnavailableView(
|
||||
"No notes yet",
|
||||
systemImage: "square.and.pencil",
|
||||
description: Text("Jot something down — Notes remembers where and when, so it can resurface at the right moment.")
|
||||
)
|
||||
}
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button {
|
||||
recording = true
|
||||
} label: {
|
||||
Label("Record Note", systemImage: "mic")
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button {
|
||||
composing = true
|
||||
} label: {
|
||||
Label("New Note", systemImage: "square.and.pencil")
|
||||
}
|
||||
.keyboardShortcut("n", modifiers: .command)
|
||||
}
|
||||
}
|
||||
#if os(iOS)
|
||||
.searchToolbarBehavior(.minimize)
|
||||
#endif
|
||||
.sheet(isPresented: $composing) {
|
||||
NoteEditorView(mode: .create)
|
||||
}
|
||||
@@ -79,6 +42,68 @@ struct NotesListView: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// The two prominent capture actions, side by side above the list — the
|
||||
/// primary way to start a note, promoted out of the toolbar. Glass-prominent
|
||||
/// capsules inherit the app's red accent; each fills half the width.
|
||||
private var captureButtons: some View {
|
||||
HStack(spacing: 12) {
|
||||
Button {
|
||||
composing = true
|
||||
} label: {
|
||||
Label("Text", systemImage: "square.and.pencil")
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.keyboardShortcut("n", modifiers: .command)
|
||||
|
||||
Button {
|
||||
recording = true
|
||||
} label: {
|
||||
Label("Audio", systemImage: "mic.fill")
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.glassProminent)
|
||||
.controlSize(.large)
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 4)
|
||||
.padding(.bottom, 8)
|
||||
}
|
||||
|
||||
private var list: some View {
|
||||
List {
|
||||
if let error = syncEngine.lastSyncError {
|
||||
Section {
|
||||
Label(error, systemImage: "exclamationmark.icloud")
|
||||
.foregroundStyle(.red)
|
||||
.font(.footnote)
|
||||
}
|
||||
}
|
||||
|
||||
if searchText.isEmpty, !relevantNotes.isEmpty {
|
||||
Section("Right here, right now") {
|
||||
ForEach(relevantNotes) { note in
|
||||
row(note)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section(searchText.isEmpty ? "All notes" : "Results") {
|
||||
ForEach(filteredNotes) { note in
|
||||
row(note)
|
||||
}
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
if notes.isEmpty {
|
||||
ContentUnavailableView(
|
||||
"No notes yet",
|
||||
systemImage: "square.and.pencil",
|
||||
description: Text("Jot something down — Notes remembers where and when, so it can resurface at the right moment.")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func row(_ note: NoteEntity) -> some View {
|
||||
Button {
|
||||
editingNote = note
|
||||
|
||||
Reference in New Issue
Block a user