Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Sources/UI/Settings/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ The combined settings page is card-based: every setting is an always-visible row
- `Pages/HomeSettingsPage.swift` - pure view assembly: header, scan-warning and activity rows, search, day-grouped list, expanded preview, inline failed-meeting rows. Takes day sections and every action as injected values/closures.
- `HomeViewModel.swift` - refresh, paging, scan-warning latch, search index and debounce, `groupByDay`, activation return-proxy.
- `HomeMeetingSearchIndex.swift` / `HomeSearchMatching.swift` - Foundation-pure index over every saved meeting (title, date words, named speakers), built off-main from `RecentMeetingsScanner.loadSearchIndex`; audio is resolved only for shown matches.
- `HomeView.swift` - row actions, the ⋯ menu, inline failed-meeting row, feedback sheet, Load more. Kept together because the contract test reads it.
- `HomeCaptureList.swift`, `HomeScanWarningCard.swift`, `HomeModels.swift`, `HomeFeedbackModels.swift` - list, warning card, value types, feedback types.
- `HomeView.swift` - row actions, the ⋯ menu, inline failed-meeting row, Load more. Kept together because the contract test reads it.
- `HomeCaptureList.swift`, `HomeScanWarningCard.swift`, `HomeModels.swift` - list, warning card, value types.
- `QuietHomeLibrary.swift` - Meetings rows and in-place expansion. `HomeMeetingExpansionLayout` keeps Copy/Finder/more in the expansion header; only its title/meta button collapses the card, leaving transcript selection and timestamp playback independent.
- `Pages/FileTranscriptionSettingsPage.swift` - the sidebar's Transcribe a file entry: the existing import picker, injected shared activity, a Meetings link for transcripts/retries, and the public CLI instructions. No separate pipeline or model preferences.
- `HomeMeetingSpeakerNamingViews.swift` - speaker label, picker, Name speakers sheet, and the transcript-only voiceprint notice.
Expand Down
66 changes: 0 additions & 66 deletions Sources/UI/Settings/HomeFeedbackModels.swift

This file was deleted.

12 changes: 3 additions & 9 deletions Sources/UI/Settings/HomeModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ struct HomeMeetingPreview: Identifiable {
let markdown: String
let content: HomeMeetingPreviewContent
let readError: String?
let feedbackTarget: HomeFeedbackTarget

/// Pass `content` when it was already parsed off main (see
/// `readMeetingMarkdown`); otherwise it's parsed here from `markdown`.
Expand All @@ -103,7 +102,6 @@ struct HomeMeetingPreview: Identifiable {
self.markdown = markdown
self.content = content ?? HomeMeetingPreviewContent.make(from: markdown)
self.readError = readError
feedbackTarget = HomeFeedbackTarget.meeting(item)
}

private init(
Expand All @@ -114,8 +112,7 @@ struct HomeMeetingPreview: Identifiable {
audio: MeetingAudioAttachment?,
markdown: String,
content: HomeMeetingPreviewContent,
readError: String?,
feedbackTarget: HomeFeedbackTarget
readError: String?
) {
self.id = id
self.title = title
Expand All @@ -125,7 +122,6 @@ struct HomeMeetingPreview: Identifiable {
self.markdown = markdown
self.content = content
self.readError = readError
self.feedbackTarget = feedbackTarget
}

/// Returns a copy reflecting a renamed transcript while keeping the stable `id`
Expand All @@ -143,8 +139,7 @@ struct HomeMeetingPreview: Identifiable {
audio: audio,
markdown: markdown,
content: content,
readError: readError,
feedbackTarget: feedbackTarget
readError: readError
)
}

Expand All @@ -162,8 +157,7 @@ struct HomeMeetingPreview: Identifiable {
audio: audio,
markdown: markdown,
content: content ?? HomeMeetingPreviewContent.make(from: markdown),
readError: readError,
feedbackTarget: feedbackTarget
readError: readError
)
}
}
Expand Down
19 changes: 0 additions & 19 deletions Sources/UI/Settings/HomePresentation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,25 +82,6 @@ enum HomeDaySectionLabel {
}()
}

// MARK: - Stable feedback reference ids

/// Foundation-pure stable id derivation for Home feedback targets.
///
/// `HomeFeedbackTarget` derives a deterministic short id from a capture's id
/// string via FNV-1a so feedback payloads stay stable without leaking the raw
/// identifier. The hashing lives here so it can be exercised by fast tests; the
/// god-object delegates to keep output identical.
enum HomeStableReferenceID {
static func id(for value: String) -> String {
var hash: UInt64 = 1_469_598_103_934_665_603
for byte in value.utf8 {
hash ^= UInt64(byte)
hash &*= 1_099_511_628_211
}
return String(hash, radix: 16)
}
}

// MARK: - Meeting speaker palette selection

/// Foundation-pure palette-slot selection for meeting speaker chips.
Expand Down
144 changes: 0 additions & 144 deletions Sources/UI/Settings/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -584,150 +584,6 @@ private extension View {
}
}

// MARK: - Row feedback

struct HomeFeedbackSheet: View {
let target: HomeFeedbackTarget
let onCancel: () -> Void
let onSubmit: (HomeFeedbackSubmission) -> Void

@State private var issueKind: HomeFeedbackIssueKind
@State private var notes = ""
@State private var includeDiagnostics = true

init(
target: HomeFeedbackTarget,
onCancel: @escaping () -> Void,
onSubmit: @escaping (HomeFeedbackSubmission) -> Void
) {
self.target = target
self.onCancel = onCancel
self.onSubmit = onSubmit
_issueKind = State(initialValue: target.suggestedIssue)
}

var body: some View {
VStack(alignment: .leading, spacing: 18) {
VStack(alignment: .leading, spacing: 6) {
Text("Report an issue")
.font(.system(size: 22, weight: .semibold))
Text(target.title)
.font(.callout)
.foregroundStyle(.secondary)
}

VStack(alignment: .leading, spacing: 8) {
Text("Issue")
.font(.subheadline.weight(.semibold))
HomeIssueKindSelector(selection: $issueKind)
}

VStack(alignment: .leading, spacing: 8) {
Text("What happened?")
.font(.subheadline.weight(.semibold))
TextEditor(text: $notes)
.font(.body)
.frame(minHeight: 120)
.scrollContentBackground(.hidden)
.background(
RoundedRectangle(cornerRadius: 10, style: .continuous)
.fill(Color.primary.opacity(0.035))
)
.overlay(
RoundedRectangle(cornerRadius: 10, style: .continuous)
.stroke(Color.primary.opacity(0.08), lineWidth: 1)
)
}

Toggle("Include safe diagnostics", isOn: $includeDiagnostics)

Text("Transcripted attaches the capture type, time, app version, a private reference ID, and recent scrubbed logs. It does not attach transcript text, audio, file paths, meeting titles, emails, or raw URLs.")
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)

HStack {
SettingsInlineActionButton(title: "Cancel", action: onCancel)
Spacer()
SettingsInlineActionButton(title: "Review report", tone: .accent) {
onSubmit(HomeFeedbackSubmission(
target: target,
issueKind: issueKind,
notes: notes,
includeDiagnostics: includeDiagnostics
))
}
}
}
.padding(24)
.frame(width: 520)
}
}

private struct HomeIssueKindSelector: View {
@Binding var selection: HomeFeedbackIssueKind

private let columns = [
GridItem(.flexible(minimum: 140), spacing: 8),
GridItem(.flexible(minimum: 140), spacing: 8),
]

var body: some View {
LazyVGrid(columns: columns, alignment: .leading, spacing: 8) {
ForEach(HomeFeedbackIssueKind.allCases) { kind in
HomeIssueKindButton(
kind: kind,
isSelected: selection == kind
) {
selection = kind
}
}
}
}
}

private struct HomeIssueKindButton: View {
let kind: HomeFeedbackIssueKind
let isSelected: Bool
let action: () -> Void

var body: some View {
Button(action: action) {
HStack(spacing: 8) {
Text(kind.label)
.font(.callout.weight(isSelected ? .semibold : .regular))
.lineLimit(1)
.minimumScaleFactor(0.9)

Spacer(minLength: 0)

if isSelected {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 13, weight: .semibold))
.foregroundStyle(Color.accentColor)
}
}
.padding(.horizontal, 12)
.frame(maxWidth: .infinity, minHeight: 36, alignment: .leading)
.contentShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
}
.buttonStyle(SettingsHoverButtonStyle(
tone: isSelected ? .accent : .neutral,
cornerRadius: 8,
normalFill: background,
normalStroke: stroke
))
}

private var background: Color {
isSelected ? Color.accentColor.opacity(0.12) : Color.primary.opacity(0.035)
}

private var stroke: Color {
isSelected ? Color.accentColor.opacity(0.35) : Color.primary.opacity(0.08)
}
}

// MARK: - Load more / failed meetings

struct HomeLoadMoreButton: View {
Expand Down
19 changes: 0 additions & 19 deletions Tests/HomePresentationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,6 @@ func testHomePresentation() {
assertTrue(small >= 0 && small < 3, "Custom slot count should be respected")
}

runSuite("HomeStableReferenceID.id — deterministic FNV-1a derivation") {
// Same input -> same id.
assertEqual(
HomeStableReferenceID.id(for: "297F08B7-62AE-4291-9EA3-41EB0B17A64A"),
HomeStableReferenceID.id(for: "297F08B7-62AE-4291-9EA3-41EB0B17A64A"),
"Same capture id should derive the same stable reference id"
)

// Distinct inputs -> distinct ids.
let a = HomeStableReferenceID.id(for: "meeting-1")
let b = HomeStableReferenceID.id(for: "meeting-2")
assertTrue(a != b, "Distinct capture ids should derive distinct stable reference ids")

// Pinned outputs so the on-disk/telemetry shape stays byte-stable
// (offset basis matches the literal used in HomeView, not canonical FNV).
assertEqual(HomeStableReferenceID.id(for: ""), "14650fb0739d0383", "Empty input returns the offset basis in hex")
assertEqual(HomeStableReferenceID.id(for: "a"), "44bd8ad473cd9906", "Single-byte hash is pinned")
}

runSuite("HomeDaySectionLabel.label — Today / Yesterday / formatted date") {
let calendar = Calendar.current
let now = Date()
Expand Down
2 changes: 1 addition & 1 deletion docs/repo-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ Split hotspots. These were over 1,500 lines until 2026-10; each is now a core fi
- `Sources/UI/Overlay/DictationSessionController.swift` plus `+RecordingStart`, `+Stop`, `+PasteBack`, `+Persistence`, `+Recovery`, `+Presses`, `+SessionCap`, `+Telemetry` and `DictationSessionDeliveryTypes.swift` — dictation session orchestration. `stopDictationAndPaste` is in `+Stop`, `installSessionTimeout` in `+SessionCap`.
- `Sources/Support/ClipboardRestoringTextPaster.swift` plus `+Pasteboard`, `+SavedClipboard`, `ClipboardPasteOutcome.swift`, `ClipboardPasteTarget.swift` and `FocusedTextPasteConfirmation.swift` — dictation paste-back: borrows the clipboard, pastes, waits for it to land, restores. Edits to any of the six also need `bash run-slow-pasteback-smoke.sh`.
- `Sources/UI/Settings/TranscriptedSettingsView.swift` (stored state, `init`, `body`, Home row actions) plus `+Pages`, `+HomeMeetingActions`, `+GeneralEditors`, `+Refresh`, `+Preferences` — the Settings window shell, page routing, and every Home side effect. Pages live under `Sources/UI/Settings/Pages/`; the shell keeps their bindings. The Home row actions in the core file are still pinned by source-text assertions in `Tests/UIAutomationSurfaceContractTests.swift`.
- `Sources/UI/Settings/HomeView.swift` plus `HomeViewModel.swift`, `HomeModels.swift`, `HomeFeedbackModels.swift`, `HomeScanWarningCard.swift`, `HomeCaptureList.swift` — the Meetings page (page id `home`).
- `Sources/UI/Settings/HomeView.swift` plus `HomeViewModel.swift`, `HomeModels.swift`, `HomeScanWarningCard.swift`, `HomeCaptureList.swift` — the Meetings page (page id `home`).
- `Sources/UI/Settings/SpeakerPeopleSettingsSection.swift` (the section view, empty state, shared play/link/icon controls) plus `SpeakerPeopleRows.swift` (the voice-to-name and person rows), `SpeakerPeopleSettingsViewModel.swift` (state, rename/merge/delete) and `SpeakerPeopleSettingsViewModel+Duplicates.swift` (duplicate detection and clip files) — the Speakers directory (review, rename, merge, delete).
- `Tools/TranscriptedQA/Sources/TranscriptedQA/Commands/PackagedAppSmoke.swift` plus `PackagedAppSmokeRunner.swift`, the `FirstRunReliability*.swift` files and `PrivacyLogScanner.swift` — the packaged-app release smoke; a break here blocks shipping.
- `Tools/TranscriptedMCP/Sources/TranscriptedMCP/TranscriptIndex.swift` (connection, schema gate, reconcile, indexing) plus `+MeetingQueries`, `+DictationQueries`, `+Context`, `+SummaryRollups`, `+Schema`, `+Writing` — the MCP server's SQLite surface.
Expand Down
Loading