From eb7c3f6633169fc41e1a2c62b85d4c9f627618f5 Mon Sep 17 00:00:00 2001 From: r3dbars Date: Tue, 10 Nov 2026 20:59:34 -0600 Subject: [PATCH] Remove obsolete Home issue-reporting sheet and models (UI-SETTINGS-A-4) --- Sources/UI/Settings/AGENTS.md | 4 +- Sources/UI/Settings/HomeFeedbackModels.swift | 66 --------- Sources/UI/Settings/HomeModels.swift | 12 +- Sources/UI/Settings/HomePresentation.swift | 19 --- Sources/UI/Settings/HomeView.swift | 144 ------------------- Tests/HomePresentationTests.swift | 19 --- docs/repo-layout.md | 2 +- 7 files changed, 6 insertions(+), 260 deletions(-) delete mode 100644 Sources/UI/Settings/HomeFeedbackModels.swift diff --git a/Sources/UI/Settings/AGENTS.md b/Sources/UI/Settings/AGENTS.md index 1cef42855..d176e8a5d 100644 --- a/Sources/UI/Settings/AGENTS.md +++ b/Sources/UI/Settings/AGENTS.md @@ -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. diff --git a/Sources/UI/Settings/HomeFeedbackModels.swift b/Sources/UI/Settings/HomeFeedbackModels.swift deleted file mode 100644 index 26484251a..000000000 --- a/Sources/UI/Settings/HomeFeedbackModels.swift +++ /dev/null @@ -1,66 +0,0 @@ -import SwiftUI -import TranscriptedCore - -enum HomeFeedbackIssueKind: String, CaseIterable, Identifiable { - case wrongWords - case missingText - case badFormatting - case wrongSpeakers - case audioProblem - case other - - var id: String { rawValue } - - var label: String { - switch self { - case .wrongWords: return "Wrong words" - case .missingText: return "Missing text" - case .badFormatting: return "Bad formatting" - case .wrongSpeakers: return "Speaker issue" - case .audioProblem: return "Audio issue" - case .other: return "Other" - } - } -} - -struct HomeFeedbackTarget: Identifiable { - let id: String - let sourceKind: String - let title: String - let createdAt: Date - let referenceID: String - let suggestedIssue: HomeFeedbackIssueKind - - static func dictation(_ entry: SavedDictationEntry) -> HomeFeedbackTarget { - HomeFeedbackTarget( - id: "dictation-\(stableReferenceID(for: entry.id))", - sourceKind: "dictation", - title: "Dictation at \(HomeActivityRowFormatting.timeFormatter.string(from: entry.createdAt))", - createdAt: entry.createdAt, - referenceID: stableReferenceID(for: entry.id), - suggestedIssue: .wrongWords - ) - } - - static func meeting(_ item: RecentMeetingItem) -> HomeFeedbackTarget { - HomeFeedbackTarget( - id: "meeting-\(stableReferenceID(for: item.id))", - sourceKind: "meeting", - title: "Meeting at \(HomeActivityRowFormatting.timeFormatter.string(from: item.date))", - createdAt: item.date, - referenceID: stableReferenceID(for: item.id), - suggestedIssue: .wrongSpeakers - ) - } - - private static func stableReferenceID(for value: String) -> String { - HomeStableReferenceID.id(for: value) - } -} - -struct HomeFeedbackSubmission { - let target: HomeFeedbackTarget - let issueKind: HomeFeedbackIssueKind - let notes: String - let includeDiagnostics: Bool -} diff --git a/Sources/UI/Settings/HomeModels.swift b/Sources/UI/Settings/HomeModels.swift index 2abcf732a..7962e4887 100644 --- a/Sources/UI/Settings/HomeModels.swift +++ b/Sources/UI/Settings/HomeModels.swift @@ -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`. @@ -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( @@ -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 @@ -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` @@ -143,8 +139,7 @@ struct HomeMeetingPreview: Identifiable { audio: audio, markdown: markdown, content: content, - readError: readError, - feedbackTarget: feedbackTarget + readError: readError ) } @@ -162,8 +157,7 @@ struct HomeMeetingPreview: Identifiable { audio: audio, markdown: markdown, content: content ?? HomeMeetingPreviewContent.make(from: markdown), - readError: readError, - feedbackTarget: feedbackTarget + readError: readError ) } } diff --git a/Sources/UI/Settings/HomePresentation.swift b/Sources/UI/Settings/HomePresentation.swift index cc5dfb42d..32041c1ca 100644 --- a/Sources/UI/Settings/HomePresentation.swift +++ b/Sources/UI/Settings/HomePresentation.swift @@ -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. diff --git a/Sources/UI/Settings/HomeView.swift b/Sources/UI/Settings/HomeView.swift index 169640229..9d65284ea 100644 --- a/Sources/UI/Settings/HomeView.swift +++ b/Sources/UI/Settings/HomeView.swift @@ -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 { diff --git a/Tests/HomePresentationTests.swift b/Tests/HomePresentationTests.swift index fb40977b1..88863314b 100644 --- a/Tests/HomePresentationTests.swift +++ b/Tests/HomePresentationTests.swift @@ -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() diff --git a/docs/repo-layout.md b/docs/repo-layout.md index d5ed92c43..f9822ff3b 100644 --- a/docs/repo-layout.md +++ b/docs/repo-layout.md @@ -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.