feat: conversation state machine, mic meter, history, wake lock (Phase 3)

This commit is contained in:
snowspeeder committed 2026-08-30 07:36:13 -04:00
1 parent 4986b6d7dc
commit 5483e6ed48
8 files changed
+348 -29

No files matched your search

+44 -13
View File
@@ -49,20 +49,46 @@ struct OrbView: View {
struct TranscriptListView: View {
let events: [TranscriptEvent]
@ObservedObject var history: TranscriptHistory
var onClear: () -> Void
private static let deliveryMarkers = #"\[(?:appreciative|calm|concerned|curious|encouraging|excited|happy|joking|quiet|reassuring|reflective|sad|serious|surprised|sympathetic|urgent|whispering|laughter|sighing|breathing|neutral)\]\s*"#
private static let timestampFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
return formatter
}()
var body: some View {
ScrollViewReader { proxy in
ScrollView {
LazyVStack(alignment: .leading, spacing: 8) {
ForEach(events) { event in
transcriptRow(event)
VStack(alignment: .leading, spacing: 0) {
HStack {
Text("Transcript")
.font(.caption.bold())
.foregroundStyle(.secondary)
Spacer()
Button(role: .destructive, action: onClear) {
Label("Clear", systemImage: "trash")
.font(.caption)
}
}
.padding(.horizontal)
}
.onChange(of: events.count) { _, count in
if let last = events.last {
withAnimation { proxy.scrollTo(last.id, anchor: .bottom) }
.padding(.bottom, 4)
ScrollView {
LazyVStack(alignment: .leading, spacing: 8) {
ForEach(events) { event in
transcriptRow(event)
}
}
.padding(.horizontal)
}
.onChange(of: events.count) { _, count in
if let last = events.last {
withAnimation { proxy.scrollTo(last.id, anchor: .bottom) }
}
}
}
}
@@ -76,9 +102,15 @@ struct TranscriptListView: View {
: Color.blue.opacity(0.08)
let textOpacity: Double = event.isFinal ? 1.0 : 0.55
return VStack(alignment: .leading, spacing: 2) {
Text(roleName)
.font(.caption2.bold())
.foregroundStyle(nameColor)
HStack {
Text(roleName)
.font(.caption2.bold())
.foregroundStyle(nameColor)
Spacer()
Text(Self.timestampFormatter.string(from: event.timestamp))
.font(.caption2)
.foregroundStyle(.tertiary)
}
Text(stripped(event.text))
.font(.body)
.opacity(textOpacity)
@@ -91,9 +123,8 @@ struct TranscriptListView: View {
}
private func stripped(_ text: String) -> String {
// Strip delivery-style markers like [appreciative], [pauses] etc.
text.replacingOccurrences(
of: #"\[[a-zA-Z ,'-]+\]"#,
of: Self.deliveryMarkers,
with: "",
options: .regularExpression
).trimmingCharacters(in: .whitespaces)