Add prompt library with multiple LLM results per transcription#53
Add prompt library with multiple LLM results per transcription#53
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR implements instruction-aware summary generation with customizable templates. It restructures summary tab management to use ID-based navigation, introduces a generation popover with streaming support, reduces built-in prompts to two templates ("Concise Summary" and "Detailed Summary"), and refactors view-model callback wiring to coordinate summary and transcription state changes. Changes
Sequence DiagramssequenceDiagram
participant User
participant TranscriptResultView
participant SummaryViewModel
participant LLMService
participant TranscriptionViewModel
User->>TranscriptResultView: Tap "+" generate button
TranscriptResultView->>TranscriptResultView: Show generation popover
User->>TranscriptResultView: Select prompt & optionally add extra instructions
User->>TranscriptResultView: Tap generate
TranscriptResultView->>SummaryViewModel: generateSummary(transcript, promptID, extraInstructions)
SummaryViewModel->>SummaryViewModel: startGeneration() - cancel any existing stream
SummaryViewModel->>LLMService: Stream summary based on prompt
LLMService-->>SummaryViewModel: Streaming chunks
SummaryViewModel->>SummaryViewModel: Update isStreaming, streamingContent
SummaryViewModel->>TranscriptResultView: Notify state changes
TranscriptResultView->>TranscriptResultView: Render .streaming tab with live content
LLMService-->>SummaryViewModel: Streaming complete
SummaryViewModel->>SummaryViewModel: Save new Summary to repo
SummaryViewModel->>SummaryViewModel: Delete existing summary for same prompt
SummaryViewModel->>SummaryViewModel: Emit onDeletedSummary callback
SummaryViewModel->>SummaryViewModel: Emit onSelectSummaryTab(newSummaryID)
SummaryViewModel->>TranscriptionViewModel: Callback: select .summary(newSummaryID)
TranscriptionViewModel->>TranscriptResultView: Update selectedTab
TranscriptResultView->>TranscriptResultView: Switch to summary tab, clear badge
sequenceDiagram
participant User
participant SummaryTab
participant SummaryViewModel
participant TranscriptionViewModel
participant TranscriptResultView
User->>SummaryTab: Tap context menu "Delete Summary"
SummaryTab->>TranscriptResultView: Trigger delete action
TranscriptResultView->>TranscriptResultView: Show delete confirmation alert
User->>TranscriptResultView: Confirm deletion
TranscriptResultView->>SummaryViewModel: deleteSummary(summaryID)
SummaryViewModel->>SummaryViewModel: Delete from repo, remove from summaries array
SummaryViewModel->>SummaryViewModel: Emit onDeletedSummary(summaryID)
SummaryViewModel->>TranscriptionViewModel: Callback: handleSummaryDeleted(summaryID)
TranscriptionViewModel->>TranscriptionViewModel: Reset selectedTab to .transcript if matching deleted ID
TranscriptionViewModel->>TranscriptResultView: Notify selectedTab changed
TranscriptResultView->>TranscriptResultView: Update tab bar, show transcript content
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request implements a Prompt Library and multi-summary architecture, allowing users to generate and manage multiple summaries per transcript using built-in or custom prompts. Key changes include new database tables for prompts and summaries, dedicated view models for summary and prompt management, and a revamped summary UI with collapsible cards and prompt selection. Feedback includes a warning about a destructive database update that clears legacy summary data, a suggestion to replace a magic number with a named constant for the auto-summary threshold, and a fix for a redundant nil-coalescing operator in the summary generation logic.
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (3)
Tests/MacParakeetTests/ViewModels/PromptsViewModelTests.swift (1)
38-44: UseXCTUnwrapinstead of force-unwrap for safer test code.The force-unwrap on
first(where:)will crash the test if "Meeting Notes" isn't found, making failures harder to diagnose. The same pattern appears intestRestoreDefaultsShowsAllBuiltIns.🛡️ Safer unwrapping
func testToggleVisibilityChangesPromptState() { - let prompt = viewModel.prompts.first { $0.name == "Meeting Notes" }! + let prompt = try XCTUnwrap(viewModel.prompts.first { $0.name == "Meeting Notes" }) viewModel.toggleVisibility(prompt)Note: This requires changing the test to
throws.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Tests/MacParakeetTests/ViewModels/PromptsViewModelTests.swift` around lines 38 - 44, Replace force-unwraps in tests with XCTUnwrap to avoid crashes: in testToggleVisibilityChangesPromptState use let prompt = try XCTUnwrap(viewModel.prompts.first { $0.name == "Meeting Notes" }) and mark the test as throws; do the same for the other occurrence in testRestoreDefaultsShowsAllBuiltIns (unwrap the prompt/result using XCTUnwrap and make that test throw) so failures produce informative assertions rather than runtime crashes.Tests/MacParakeetTests/Database/PromptRepositoryTests.swift (1)
13-19: Hardcoded prompt count (7) may drift from source of truth.The assertion
XCTAssertEqual(prompts.count, 7)is coupled to the number of built-in prompts inPrompt.builtInSummaryPrompts(). If that array changes, this test will fail silently without indicating the root cause. Consider referencing the source directly.♻️ Reference source of truth
func testBuiltInPromptsSeededAfterMigration() throws { let prompts = try repo.fetchAll() - XCTAssertEqual(prompts.count, 7) + XCTAssertEqual(prompts.count, Prompt.builtInSummaryPrompts().count) XCTAssertTrue(prompts.allSatisfy(\.isBuiltIn))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Tests/MacParakeetTests/Database/PromptRepositoryTests.swift` around lines 13 - 19, The test testBuiltInPromptsSeededAfterMigration currently asserts a hardcoded count (7); change it to compare against the source-of-truth by using Prompt.builtInSummaryPrompts().count instead of 7 (keep the other assertions intact), i.e. fetch prompts via repo.fetchAll() and assert prompts.count == Prompt.builtInSummaryPrompts().count so the test follows the canonical data in Prompt.builtInSummaryPrompts().Sources/MacParakeet/Views/Transcription/SummaryPromptsView.swift (1)
119-131: Use DesignSystem spacing token instead of hardcoded padding.Line 122 uses
.padding(8)which should reference a DesignSystem spacing token for consistency. The same pattern appears on line 202 ineditSheet. As per coding guidelines, all views should reference DesignSystem, not hardcoded values.♻️ Use DesignSystem token
TextEditor(text: $viewModel.newContent) .font(DesignSystem.Typography.body) .frame(minHeight: 140) - .padding(8) + .padding(DesignSystem.Spacing.sm)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/MacParakeet/Views/Transcription/SummaryPromptsView.swift` around lines 119 - 131, Replace the hardcoded .padding(8) with the DesignSystem spacing token to follow the design tokens; locate the TextEditor using viewModel.newContent in SummaryPromptsView (and the matching padding in editSheet) and swap .padding(8) for the appropriate token from the design system (e.g., DesignSystem.Spacing.small or the equivalent spacing constant in DesignSystem.Layout) so the view uses the centralized spacing token instead of a literal value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@plans/active/prompt-library-multi-summary.md`:
- Around line 1-7: Move the "Prompt Library + Multi-Summary Implementation Plan"
document out of the active plans set into the completed plans archive and update
its metadata: change the Status header from **ACTIVE** to **IMPLEMENTED** and
rename the file to follow the completed naming convention including date and
feature, e.g. 2026-04-prompt-library-multi-summary.md; ensure the document
content and links remain unchanged apart from the header and filename update so
the plan is archived properly.
In `@Sources/MacParakeet/Views/Transcription/TranscriptResultView.swift`:
- Around line 907-923: Replace the view-wide Boolean summaryCopied and shared
copiedResetTask with a per-card identifier and reset task so only the card that
was copied shows "Copied": add `@State` private var copiedSummaryID: UUID? and
`@State` private var summaryCopiedResetTask: Task<Void, Never>?, then in the
Button action set copiedSummaryID = summaryID (or the UUID property for that
card), cancel and replace summaryCopiedResetTask with a Task that awaits 2
seconds and then sets copiedSummaryID = nil; update the Label condition to show
"Copied" when copiedSummaryID == summaryID (instead of using summaryCopied) and
keep the existing transcript/chat copy state (copiedResetTask) untouched to
avoid conflicts.
In `@Sources/MacParakeetCore/Database/PromptRepository.swift`:
- Around line 53-56: In delete(id:) ensure built-in prompts cannot be removed by
first reading the Prompt entry inside the dbQueue (e.g., fetch Prompt by id with
a read or write transaction), check its built-in flag/property (e.g.,
Prompt.isBuiltin or Prompt.builtin), and if true throw a repository-level error
(create/use something like PromptRepositoryError.cannotDeleteBuiltin); only call
Prompt.deleteOne(db, key: id) when the prompt is not built-in. This enforces the
“hide-only” invariant in the repository layer rather than relying on UI checks.
In `@Sources/MacParakeetViewModels/SummaryViewModel.swift`:
- Around line 251-276: The save+mirror must be atomic: either wrap both writes
in a DB transaction (if your repos support it) or make the legacy column update
best-effort so a failing transcriptionRepo.updateSummary(...) does not cause the
whole operation to appear failed after summaryRepo.save(summary) succeeded.
Concretely, prefer one of two fixes: 1) use a transaction API (e.g.
summaryRepo.transaction { try summaryRepo.save(summary); try
transcriptionRepo.updateSummary(id: targetTranscriptionID, summary:
summary.content) }) so both succeed or rollback together; or 2) call try
summaryRepo?.save(summary) first and then call
transcriptionRepo?.updateSummary(...) inside its own do-catch that logs the
error but does not rethrow (or if you must revert, explicitly delete the saved
summary on update failure), ensuring UI state updates (isStreaming, summaries
insert, onSummariesChanged, summaryBadge) proceed when the primary save
succeeded. Use the existing symbols summaryRepo.save,
transcriptionRepo.updateSummary(id:), targetTranscriptionID, and the surrounding
UI-update block when implementing.
In `@spec/12-processing-layer.md`:
- Around line 147-150: The spec omits the steady-state legacy mirror behavior
implemented in Sources/MacParakeetViewModels/SummaryViewModel.swift; update
spec/12-processing-layer.md to document that the active SummaryViewModel
continues to mirror the latest summary back into transcriptions.summary after
summary save and delete operations (the code paths around SummaryViewModel's
save/delete handlers at the blocks around lines 251-253 and 306-309), describing
that this ongoing compatibility contract keeps transcriptions.summary populated
for legacy consumers even after the migration/null-out.
In `@spec/13-agent-workflows.md`:
- Around line 34-59: The fenced code blocks in the ASCII diagrams (the
triple-backtick fences around the box starting with
"┌─────────────────────────────────────────────────────────────────┐" and
containing "┌─ Prompt Library ──────────────────────────────┐ SHIPPED │" )
are missing language identifiers; add an explicit language tag (e.g., ```text)
to each opening fence to satisfy MD040; apply the same fix to the other fenced
blocks called out in the comment (lines referenced around the second box and the
ranges 72-80, 94-104, 110-112, 118-125, 180-184) so every triple-backtick has a
language identifier.
- Line 3: Replace the invalid "Status: **DRAFT**" header in the file (the line
containing "Status: **DRAFT**") with one of the repository's allowed status
labels (ACTIVE, IMPLEMENTED, HISTORICAL, or PROPOSAL); update the header to the
appropriate canonical status (e.g., "Status: **PROPOSAL**") so it conforms to
the project's documentation guidelines.
In `@Tests/MacParakeetTests/ViewModels/SummaryViewModelTests.swift`:
- Line 106: Replace fixed Task.sleep calls in SummaryViewModelTests.swift with a
state-based waiting helper: add a private async
waitUntil(timeout:poll:condition:) helper in the test file that polls a
`@Sendable` condition using Task.sleep(poll) until it becomes true or times out,
then update each occurrence of try await Task.sleep(for: .milliseconds(200))
(and the other listed occurrences at 134, 141, 225, 249, 271) to call waitUntil
with an appropriate timeout and a condition closure that checks the actual test
state (e.g., viewModel.state, an expectation flag, or a published property)
instead of sleeping; ensure the helper calls XCTFail on timeout so failures are
deterministic and provide clear messages.
---
Nitpick comments:
In `@Sources/MacParakeet/Views/Transcription/SummaryPromptsView.swift`:
- Around line 119-131: Replace the hardcoded .padding(8) with the DesignSystem
spacing token to follow the design tokens; locate the TextEditor using
viewModel.newContent in SummaryPromptsView (and the matching padding in
editSheet) and swap .padding(8) for the appropriate token from the design system
(e.g., DesignSystem.Spacing.small or the equivalent spacing constant in
DesignSystem.Layout) so the view uses the centralized spacing token instead of a
literal value.
In `@Tests/MacParakeetTests/Database/PromptRepositoryTests.swift`:
- Around line 13-19: The test testBuiltInPromptsSeededAfterMigration currently
asserts a hardcoded count (7); change it to compare against the source-of-truth
by using Prompt.builtInSummaryPrompts().count instead of 7 (keep the other
assertions intact), i.e. fetch prompts via repo.fetchAll() and assert
prompts.count == Prompt.builtInSummaryPrompts().count so the test follows the
canonical data in Prompt.builtInSummaryPrompts().
In `@Tests/MacParakeetTests/ViewModels/PromptsViewModelTests.swift`:
- Around line 38-44: Replace force-unwraps in tests with XCTUnwrap to avoid
crashes: in testToggleVisibilityChangesPromptState use let prompt = try
XCTUnwrap(viewModel.prompts.first { $0.name == "Meeting Notes" }) and mark the
test as throws; do the same for the other occurrence in
testRestoreDefaultsShowsAllBuiltIns (unwrap the prompt/result using XCTUnwrap
and make that test throw) so failures produce informative assertions rather than
runtime crashes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ad12ed9e-6e87-4e80-a78b-6179d99f9d2c
📒 Files selected for processing (30)
CLAUDE.mdSources/MacParakeet/App/AppEnvironment.swiftSources/MacParakeet/AppDelegate.swiftSources/MacParakeet/Views/MainWindowView.swiftSources/MacParakeet/Views/Transcription/SummaryPromptsView.swiftSources/MacParakeet/Views/Transcription/TranscribeView.swiftSources/MacParakeet/Views/Transcription/TranscriptResultView.swiftSources/MacParakeetCore/Database/DatabaseManager.swiftSources/MacParakeetCore/Database/PromptRepository.swiftSources/MacParakeetCore/Database/SummaryRepository.swiftSources/MacParakeetCore/Models/Prompt.swiftSources/MacParakeetCore/Models/Summary.swiftSources/MacParakeetCore/Services/LLMService.swiftSources/MacParakeetViewModels/PromptsViewModel.swiftSources/MacParakeetViewModels/SummaryViewModel.swiftSources/MacParakeetViewModels/TranscriptionViewModel.swiftTests/MacParakeetTests/Database/DatabaseManagerTests.swiftTests/MacParakeetTests/Database/PromptRepositoryTests.swiftTests/MacParakeetTests/Database/SummaryRepositoryTests.swiftTests/MacParakeetTests/Services/LLMServicePromptTests.swiftTests/MacParakeetTests/ViewModels/PromptsViewModelTests.swiftTests/MacParakeetTests/ViewModels/SummaryViewModelTests.swiftTests/MacParakeetTests/ViewModels/TranscriptionViewModelTests.swiftTests/MacParakeetTests/ViewModels/ViewModelMocks.swiftplans/active/prompt-library-multi-summary.mdspec/11-llm-integration.mdspec/12-processing-layer.mdspec/13-agent-workflows.mdspec/README.mdspec/adr/013-prompt-library-multi-summary.md
Sources/MacParakeet/Views/Transcription/TranscriptResultView.swift
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Tests/MacParakeetTests/Database/DatabaseManagerTests.swift`:
- Around line 6-18: The prePromptLibraryMigrationIDs test seed is missing the
migration ID "v0.7-snippet-key-action", so update the
prePromptLibraryMigrationIDs array in DatabaseManagerTests (the variable
prePromptLibraryMigrationIDs) to include "v0.7-snippet-key-action" positioned
before "v0.7-prompts-and-summaries" so the test
(testPromptSummaryMigrationPreservesLegacySummaryColumn) will apply the same
migration order as DatabaseManager.swift where v0.7-snippet-key-action is
registered before v0.7-prompts-and-summaries.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f1dab373-921f-4f4d-bc9d-92bb1632ca77
📒 Files selected for processing (3)
Sources/MacParakeetCore/Database/DatabaseManager.swiftSources/MacParakeetViewModels/SummaryViewModel.swiftTests/MacParakeetTests/Database/DatabaseManagerTests.swift
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/MacParakeet/Views/Transcription/TranscriptResultView.swift`:
- Around line 939-944: The summary copy logic currently reuses copiedResetTask
(used by copyToClipboard()), causing one copy's "Copied!" indicator to be
cancelled by the other; introduce a dedicated Task state (e.g., add `@State`
private var summaryCopiedResetTask: Task<Void, Never>?) and update the summary
copy flow to set and cancel only summaryCopiedResetTask when scheduling the
two-second reset (instead of touching copiedResetTask), set/reset
copiedSummaryID as you already do, and ensure summaryCopiedResetTask is
cancelled and nilled in onDisappear alongside the existing cleanup so each copy
indicator has its own reset task.
- Around line 29-30: Remove the dead boolean state by deleting the unused
property summaryCopied from TranscriptResultView; the view already uses the
per-card identifier copiedSummaryID for copy-state tracking, so remove the
declaration "@State private var summaryCopied = false" and ensure no other code
references summaryCopied (if any references exist, switch them to use
copiedSummaryID instead).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f22c7bc3-432a-4545-b393-6af3d7b7ce89
📒 Files selected for processing (4)
Sources/MacParakeet/Views/Transcription/SummaryPromptsView.swiftSources/MacParakeet/Views/Transcription/TranscriptResultView.swiftSources/MacParakeetViewModels/PromptsViewModel.swiftTests/MacParakeetTests/ViewModels/PromptsViewModelTests.swift
✅ Files skipped from review due to trivial changes (1)
- Tests/MacParakeetTests/ViewModels/PromptsViewModelTests.swift
Sources/MacParakeet/Views/Transcription/TranscriptResultView.swift
Outdated
Show resolved
Hide resolved
Sources/MacParakeet/Views/Transcription/TranscriptResultView.swift
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@Sources/MacParakeet/Views/Transcription/TranscriptResultView.swift`:
- Around line 724-736: The context menu sets
summaryViewModel.pendingDeleteSummary from inside the TranscriptResultView
contextMenu (when case .summary(let id)), but the confirmation .alert is defined
inside summaryContentPane(summaryID:) which may not be mounted, causing the
delete confirmation to never appear; move the alert out of summaryContentPane
and into a parent view that is always mounted (e.g., TranscriptResultView or the
immediate container that owns summaryViewModel) so that the .alert observing
summaryViewModel.pendingDeleteSummary is always attached, keep the same binding
to summaryViewModel.pendingDeleteSummary and the same deletion action, and
remove the alert from summaryContentPane(summaryID:) to avoid duplicate alerts.
- Around line 665-685: orderedTabs currently always includes the .chat tab
causing a non-functional Chat to appear; update the orderedTabs computed
property (involving TranscriptionViewModel.TranscriptTab) to only append .chat
when the chat feature is actually available by gating it on
viewModel.llmAvailable || viewModel.hasConversations; keep the existing logic
that appends .transcript, summary tabs from
summaryViewModel.summaries.reversed(), and .streaming when
summaryViewModel.isStreaming, but move the .chat append behind the availability
check so the Chat tab is only present when usable.
In `@Sources/MacParakeetViewModels/SummaryViewModel.swift`:
- Around line 263-275: Currently the code deletes the existing summary (using
summaryRepo?.delete and removing from summaries) before ensuring the new summary
is persisted and mirrored, which can lose the last good summary on save/mirror
failure; change the order so you first persist the new summary via
summaryRepo.save(summary) and update the transcription via
transcriptionRepo.updateSummary(id:targetTranscriptionID,
summary:summary.content) (throwing on errors), then only after those succeed
locate and remove the old summary from summaries, call
summaryRepo.delete(id:existing.id) without swallowing errors, and invoke
onDeletedSummary(existing.id); if your repo supports transactions, perform the
save + delete in a single transaction to atomically swap the rows and avoid
duplicates on reload, and ensure onLegacySummaryChanged is called after the
successful mirror.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 58f4499b-3962-4b1e-98aa-202620787b18
📒 Files selected for processing (15)
.gitignoreSources/MacParakeet/AppDelegate.swiftSources/MacParakeet/Views/Transcription/SummaryPromptsView.swiftSources/MacParakeet/Views/Transcription/TranscriptResultView.swiftSources/MacParakeetCore/Models/Prompt.swiftSources/MacParakeetCore/Services/LLMService.swiftSources/MacParakeetViewModels/SummaryViewModel.swiftSources/MacParakeetViewModels/TranscriptionViewModel.swiftTests/MacParakeetTests/Database/PromptRepositoryTests.swiftTests/MacParakeetTests/Database/SummaryRepositoryTests.swiftTests/MacParakeetTests/Services/LLMServicePromptTests.swiftTests/MacParakeetTests/Services/LLMServiceTests.swiftTests/MacParakeetTests/ViewModels/PromptsViewModelTests.swiftTests/MacParakeetTests/ViewModels/SummaryViewModelTests.swiftTests/MacParakeetTests/ViewModels/TranscriptionViewModelTests.swift
✅ Files skipped from review due to trivial changes (5)
- .gitignore
- Tests/MacParakeetTests/Services/LLMServiceTests.swift
- Tests/MacParakeetTests/Services/LLMServicePromptTests.swift
- Tests/MacParakeetTests/Database/PromptRepositoryTests.swift
- Tests/MacParakeetTests/Database/SummaryRepositoryTests.swift
🚧 Files skipped from review as they are similar to previous changes (4)
- Sources/MacParakeetCore/Services/LLMService.swift
- Sources/MacParakeet/Views/Transcription/SummaryPromptsView.swift
- Sources/MacParakeetCore/Models/Prompt.swift
- Tests/MacParakeetTests/ViewModels/TranscriptionViewModelTests.swift
- Collapsed cards show content preview subtitle (first ~100 chars) - Copy/Delete buttons hidden when collapsed, visible when expanded - Dropdown separates built-in and custom prompts with divider - Extra instructions collapsed behind "+ Add instructions" disclosure - TextEditor placeholders and better contrast in management sheet - Smooth expand/collapse animation on summary cards Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Copy turns green with checkmark on success - Delete gets trash icon for visual alignment with Copy - Both buttons use consistent HStack(icon + text) layout - Remove collapsed card content preview (distracting) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix per-card copied state (was shared across all cards) - Copy shows green checkmark per individual card - Remove box-in-box markdown content (divider instead of nested card) - Zen empty state: minimal text, sparkles icon, breathing room - Keyboard shortcut: Cmd+Return on Generate and Save buttons - "Add instructions" styled as accent-colored link - Fix alert copy: "This action cannot be undone" - Management sheet: single primary button (Done), Add Prompt secondary Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a summary card is collapsed, the entire bar is now the click target — not just the chevron and text. Spacer moves inside the button label when collapsed, outside when expanded (so Copy/Delete buttons remain independently clickable). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Collapsed cards: entire card area is tappable via onTapGesture + contentShape(Rectangle()). Expanded cards: chevron + title row is tappable to collapse, Copy/Delete remain independent. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move Spacer() inside the tappable HStack so the entire header row (chevron + title + empty space) triggers collapse. Copy/Delete buttons sit outside the tap zone and remain independent. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7 built-in prompts was overwhelming. Condensed to just two — Concise Summary and Detailed Summary — with everything else available as user-created custom prompts. Also fixed TextEditor placeholder alignment in SummaryPromptsView. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Each generated summary now gets its own first-class tab in the tab bar instead of being nested as collapsible cards inside a single Summary tab. Generation controls moved to a "+" popover button in the tab bar. - TranscriptTab enum: dynamic .summary(id:) and .streaming cases - Scrollable tab bar with context menu (copy/delete) on summary tabs - Full-viewport summary content pane per tab - Streaming pane with cancel button and live markdown preview - Generation popover with prompt selector, model picker, extra instructions - Per-summary badge (badgedSummaryID) replaces global summaryBadge - Removed accordion state (expandedSummaryIDs, toggleExpanded) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tab capsule already shows streaming dots, so the content pane no longer needs AIStreamingIndicator or SummarySkeletonView. Replaced with a clean native ProgressView spinner + "Generating summary..." text while waiting for first token. Cancel button styled more subtly. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Chat is now a fixed tab right after Transcript. Summary tabs follow, with newest first. The + button stays at the end. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace bulky AIStreamingIndicator dots with pulsing sparkles icon via SF Symbols .symbolEffect(.pulse) — zero extra width, premium feel - New summary tabs now append to the right (oldest first in tab order) - Streaming tab always appears at the rightmost position before + Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cancel streaming now navigates to Transcript tab. Added safety fallbacks in contentArea: if selectedTab points to a streaming tab that's no longer active or a summary tab whose summary was deleted, falls back to Transcript. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## What Changed - SummaryViewModel: Allow cancelling in-flight stream to start new generation (removes !isStreaming guard, calls cancelStreaming() first). Added regenerateSummary() method for re-running with same prompt/instructions. Duplicate deletion moved AFTER successful save to prevent data loss if stream is cancelled mid-flight. - TranscriptResultView: Added Regenerate button to each summary content pane toolbar (before Copy/Delete). Prompt menu shows dot indicator next to prompts that already have summaries. Extracted promptMenuItem() helper. ## Root Intent Users could create duplicate tabs by generating the same prompt twice. Also needed a way to regenerate any existing summary from its tab without going through the "+" popover. Critical fix: pre-save duplicate deletion caused data loss when a stream was cancelled before completing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5ab4d6d to
0df3ff6
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
Sources/MacParakeetCore/Models/Prompt.swift (1)
46-72:⚠️ Potential issue | 🟠 MajorRetiring built-ins needs a cleanup migration.
Changing the built-in set here won't update existing databases: seeding still inserts built-ins by fresh identity, so users who already have the old defaults will keep those rows and also get these renamed ones. Please reconcile built-ins by a stable identifier/name mapping and explicitly remove retired defaults during migration or seeding.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/MacParakeetCore/Models/Prompt.swift` around lines 46 - 72, The built-in prompt change in builtInSummaryPrompts will not update existing DB rows and will duplicate/leave retired prompts; modify the seeding/migration logic to reconcile built-ins by a stable identifier (e.g., add and use a stable builtInID or canonical name) instead of relying on generated identity, update existing Prompt rows where Prompt.isBuiltIn == true and identifier matches to apply content/name changes, and remove or mark as retired any old defaults that are no longer in the built-in set; specifically update the code that seeds DB built-ins (the seeding/migration routine that inserts Prompt rows) to perform upsert-by-stable-id for Prompt entries returned by builtInSummaryPrompts and to delete/flag retired prompts.Sources/MacParakeet/Views/Transcription/SummaryPromptsView.swift (1)
204-249:⚠️ Potential issue | 🟠 MajorShow edit validation errors inside the sheet.
Any failure surfaced through
viewModel.errorMessageonly renders in the parent view at Lines 32-36, which is obscured while this sheet is open. A duplicate/empty save currently looks like a no-op; mirror the error text insideeditSheetor keep the edit draft/error state local to the sheet.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/MacParakeet/Views/Transcription/SummaryPromptsView.swift` around lines 204 - 249, The sheet’s validation/errors are only shown in the parent view (viewModel.errorMessage) and are hidden while editSheet(prompt:) is presented, so users see a silent no-op on invalid saves; surface the error inside the sheet by showing the error text under the editor and/or keep edit state local. Update editSheet(prompt:) to display viewModel.errorMessage (or a new local `@State` var editError that you set when update fails) beneath the TextEditor (near editContent/editName), clear that error when the sheet opens (when editingPrompt is assigned) and when Cancel is tapped, and ensure viewModel.updatePrompt call propagates or sets the visible error so duplicate/empty saves produce visible feedback to the user.
♻️ Duplicate comments (4)
Tests/MacParakeetTests/Database/DatabaseManagerTests.swift (1)
6-18:⚠️ Potential issue | 🟠 MajorMissing
v0.7-snippet-key-actionin pre-seeded migration IDs.
prePromptLibraryMigrationIDsstill omitsv0.7-snippet-key-action, even though it runs beforev0.7-prompts-and-summaries. This seeds an incorrect migration baseline fortestPromptSummaryMigrationPreservesLegacySummaryColumn.Suggested fix
private let prePromptLibraryMigrationIDs = [ "v0.1-dictations", "v0.1-transcriptions", "v0.2-custom-words", "v0.2-text-snippets", "v0.3-transcription-source-url", "v0.4-transcription-diarization-segments", "v0.4-transcription-llm-content", "v0.5-private-dictation", "v0.5-chat-conversations", "v0.5-drop-unused-fts", "v0.5-transcription-video-metadata", + "v0.7-snippet-key-action", ]🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Tests/MacParakeetTests/Database/DatabaseManagerTests.swift` around lines 6 - 18, The pre-seeded migration list prePromptLibraryMigrationIDs is missing "v0.7-snippet-key-action", which should be included before "v0.7-prompts-and-summaries" so the test baseline for testPromptSummaryMigrationPreservesLegacySummaryColumn is correct; update the array prePromptLibraryMigrationIDs to insert "v0.7-snippet-key-action" immediately before "v0.7-prompts-and-summaries" so migrations run in the intended order.Sources/MacParakeet/Views/Transcription/TranscriptResultView.swift (2)
724-735:⚠️ Potential issue | 🟠 MajorAttach the delete confirmation to an always-mounted parent.
The tab context menu can set
pendingDeleteSummarywhile Transcript or Chat is selected, but the.alertonly exists insidesummaryContentPane. In those states the confirmation never appears, or it can surface later on an unrelated pane.Also applies to: 864-879
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/MacParakeet/Views/Transcription/TranscriptResultView.swift` around lines 724 - 735, The context menu in TranscriptResultView currently sets summaryViewModel.pendingDeleteSummary while the delete confirmation .alert is only declared inside summaryContentPane, so the alert never appears when Transcript/Chat is selected; move the .alert that observes summaryViewModel.pendingDeleteSummary out of summaryContentPane and attach it to a parent view that is always mounted (e.g., the TranscriptResultView root container) so the confirmation is shown regardless of which tab is active; update references to pendingDeleteSummary and remove duplicate alert declarations (the one at lines ~864-879) so there is a single alert bound to summaryViewModel.pendingDeleteSummary.
814-824:⚠️ Potential issue | 🟡 MinorGive summary copy its own reset task.
This action still reuses
copiedResetTask, which the transcript and chat copy paths also use. Copying one thing cancels another path’s timer and can leave a stale “Copied” state behind.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/MacParakeet/Views/Transcription/TranscriptResultView.swift` around lines 814 - 824, The Button action for copying a summary reuses the shared copiedResetTask, causing cancellation conflicts with other copy paths; create and use a dedicated reset Task for summary copies (e.g., a new variable like copiedSummaryResetTask) instead of copiedResetTask, update the Button action to cancel/set that dedicated Task and leave copiedSummaryID management the same so other copy flows (transcript/chat) no longer interfere with summary copy timers.Sources/MacParakeetViewModels/SummaryViewModel.swift (1)
263-275:⚠️ Potential issue | 🟠 MajorMake regenerate-in-place atomic.
This still deletes the previous summary before the replacement row and legacy mirror are durable, and the delete failure is swallowed. A failing
save/updateSummaryloses the last good summary, while a failed delete can leave duplicate prompt entries because the database does not enforce uniqueness for(transcriptionId, promptName).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Sources/MacParakeetViewModels/SummaryViewModel.swift` around lines 263 - 275, Make regenerate-in-place atomic by not deleting the old summary until the new summary and legacy mirror are durably written: call summaryRepo.save(summary) first and ensure it returns/commits (don't swallow errors with try?), then call transcriptionRepo.updateSummary(id: targetTranscriptionID, summary: summary.content) and invoke onLegacySummaryChanged only after success; only then delete the existing summary (use summaryRepo.delete(id:)) and update the in-memory summaries and call onDeletedSummary. Prefer wrapping save/update/delete in a repository/DB transaction if summaryRepo supports it (e.g., summaryRepo.transaction { save; update; delete }) and remove the silent ignore of delete errors so failures are propagated/handled instead of leaving duplicates; refer to summaries, summaryRepo.save, summaryRepo.delete, transcriptionRepo.updateSummary, onLegacySummaryChanged, and onDeletedSummary when implementing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@Sources/MacParakeet/Views/Transcription/SummaryPromptsView.swift`:
- Around line 204-249: The sheet’s validation/errors are only shown in the
parent view (viewModel.errorMessage) and are hidden while editSheet(prompt:) is
presented, so users see a silent no-op on invalid saves; surface the error
inside the sheet by showing the error text under the editor and/or keep edit
state local. Update editSheet(prompt:) to display viewModel.errorMessage (or a
new local `@State` var editError that you set when update fails) beneath the
TextEditor (near editContent/editName), clear that error when the sheet opens
(when editingPrompt is assigned) and when Cancel is tapped, and ensure
viewModel.updatePrompt call propagates or sets the visible error so
duplicate/empty saves produce visible feedback to the user.
In `@Sources/MacParakeetCore/Models/Prompt.swift`:
- Around line 46-72: The built-in prompt change in builtInSummaryPrompts will
not update existing DB rows and will duplicate/leave retired prompts; modify the
seeding/migration logic to reconcile built-ins by a stable identifier (e.g., add
and use a stable builtInID or canonical name) instead of relying on generated
identity, update existing Prompt rows where Prompt.isBuiltIn == true and
identifier matches to apply content/name changes, and remove or mark as retired
any old defaults that are no longer in the built-in set; specifically update the
code that seeds DB built-ins (the seeding/migration routine that inserts Prompt
rows) to perform upsert-by-stable-id for Prompt entries returned by
builtInSummaryPrompts and to delete/flag retired prompts.
---
Duplicate comments:
In `@Sources/MacParakeet/Views/Transcription/TranscriptResultView.swift`:
- Around line 724-735: The context menu in TranscriptResultView currently sets
summaryViewModel.pendingDeleteSummary while the delete confirmation .alert is
only declared inside summaryContentPane, so the alert never appears when
Transcript/Chat is selected; move the .alert that observes
summaryViewModel.pendingDeleteSummary out of summaryContentPane and attach it to
a parent view that is always mounted (e.g., the TranscriptResultView root
container) so the confirmation is shown regardless of which tab is active;
update references to pendingDeleteSummary and remove duplicate alert
declarations (the one at lines ~864-879) so there is a single alert bound to
summaryViewModel.pendingDeleteSummary.
- Around line 814-824: The Button action for copying a summary reuses the shared
copiedResetTask, causing cancellation conflicts with other copy paths; create
and use a dedicated reset Task for summary copies (e.g., a new variable like
copiedSummaryResetTask) instead of copiedResetTask, update the Button action to
cancel/set that dedicated Task and leave copiedSummaryID management the same so
other copy flows (transcript/chat) no longer interfere with summary copy timers.
In `@Sources/MacParakeetViewModels/SummaryViewModel.swift`:
- Around line 263-275: Make regenerate-in-place atomic by not deleting the old
summary until the new summary and legacy mirror are durably written: call
summaryRepo.save(summary) first and ensure it returns/commits (don't swallow
errors with try?), then call transcriptionRepo.updateSummary(id:
targetTranscriptionID, summary: summary.content) and invoke
onLegacySummaryChanged only after success; only then delete the existing summary
(use summaryRepo.delete(id:)) and update the in-memory summaries and call
onDeletedSummary. Prefer wrapping save/update/delete in a repository/DB
transaction if summaryRepo supports it (e.g., summaryRepo.transaction { save;
update; delete }) and remove the silent ignore of delete errors so failures are
propagated/handled instead of leaving duplicates; refer to summaries,
summaryRepo.save, summaryRepo.delete, transcriptionRepo.updateSummary,
onLegacySummaryChanged, and onDeletedSummary when implementing.
In `@Tests/MacParakeetTests/Database/DatabaseManagerTests.swift`:
- Around line 6-18: The pre-seeded migration list prePromptLibraryMigrationIDs
is missing "v0.7-snippet-key-action", which should be included before
"v0.7-prompts-and-summaries" so the test baseline for
testPromptSummaryMigrationPreservesLegacySummaryColumn is correct; update the
array prePromptLibraryMigrationIDs to insert "v0.7-snippet-key-action"
immediately before "v0.7-prompts-and-summaries" so migrations run in the
intended order.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b343acba-cd6f-4b1f-8df7-ef551daad89f
📒 Files selected for processing (18)
.gitignoreSources/MacParakeet/AppDelegate.swiftSources/MacParakeet/Views/Transcription/SummaryPromptsView.swiftSources/MacParakeet/Views/Transcription/TranscriptResultView.swiftSources/MacParakeetCore/Database/DatabaseManager.swiftSources/MacParakeetCore/Models/Prompt.swiftSources/MacParakeetCore/Services/LLMService.swiftSources/MacParakeetViewModels/PromptsViewModel.swiftSources/MacParakeetViewModels/SummaryViewModel.swiftSources/MacParakeetViewModels/TranscriptionViewModel.swiftTests/MacParakeetTests/Database/DatabaseManagerTests.swiftTests/MacParakeetTests/Database/PromptRepositoryTests.swiftTests/MacParakeetTests/Database/SummaryRepositoryTests.swiftTests/MacParakeetTests/Services/LLMServicePromptTests.swiftTests/MacParakeetTests/Services/LLMServiceTests.swiftTests/MacParakeetTests/ViewModels/PromptsViewModelTests.swiftTests/MacParakeetTests/ViewModels/SummaryViewModelTests.swiftTests/MacParakeetTests/ViewModels/TranscriptionViewModelTests.swift
💤 Files with no reviewable changes (1)
- Sources/MacParakeetCore/Database/DatabaseManager.swift
✅ Files skipped from review due to trivial changes (4)
- .gitignore
- Tests/MacParakeetTests/Services/LLMServicePromptTests.swift
- Tests/MacParakeetTests/Database/SummaryRepositoryTests.swift
- Tests/MacParakeetTests/Database/PromptRepositoryTests.swift
🚧 Files skipped from review as they are similar to previous changes (5)
- Tests/MacParakeetTests/Services/LLMServiceTests.swift
- Tests/MacParakeetTests/ViewModels/SummaryViewModelTests.swift
- Sources/MacParakeetViewModels/PromptsViewModel.swift
- Sources/MacParakeet/AppDelegate.swift
- Sources/MacParakeetCore/Services/LLMService.swift
Move prompt definitions from hardcoded Swift to a bundled JSON file (community-prompts.json) so contributors can add new prompts via PR without touching Swift code. Rename "Built-In" to "Community" and "Custom" to "My Prompts" in the UI. Add "Suggest a prompt" link pointing to the JSON file on GitHub. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…utton, and streamlined popover
## What Changed
- Sources/MacParakeet/Views/Components/FlowLayout.swift: New reusable FlowLayout for
wrapping horizontal chip layouts
- Sources/MacParakeet/Views/Transcription/TranscriptResultView.swift:
- Replaced cryptic "+" tab button with labeled "Summarize" button (sparkles icon,
accent-tinted background capsule)
- Replaced dropdown Menu prompt picker with inline selectable prompt chips
(capsule style, selected state with accent highlight, dot indicator for existing summaries)
- Surfaced "Manage Prompts" as a persistent gear icon button next to prompt chips
(was buried 3 clicks deep inside dropdown)
- Extra instructions field always visible (removed toggle gate — empty placeholder
is self-documenting)
- Reordered tabs: Transcript | [summaries...] | Chat | +Summarize (summaries now
grouped next to transcript instead of separated by Chat)
- Widened popover from 380px to 420px for breathing room
- Added accessibility label on generate button
- Removed dead `showExtraInstructions` state and `promptMenuItem` helper
## Root Intent
The summary generation popover had multiple UX friction points: the "+" button was
an unlabeled icon, prompt selection required a dropdown menu, "Manage Prompts" was
buried inside the dropdown, extra instructions required an extra click to reveal,
and summaries were separated from the transcript by the Chat tab. These changes
reduce clicks-to-action, improve discoverability, and create a more premium feel.
## Prompt That Would Produce This Diff
Polish the summary generation UX in TranscriptResultView with these changes:
1. Replace the bare "+" icon tab button with a labeled "Summarize" capsule button
using sparkles icon and accent color tint
2. Replace the dropdown Menu prompt picker with inline selectable prompt chips using
a FlowLayout (create a new FlowLayout component). Chips show selection state and
a dot indicator when a summary already exists for that prompt
3. Surface "Manage Prompts" as a gear icon (slider.horizontal.3) button always
visible next to the prompt chips
4. Always show the extra instructions TextField (remove the toggle/reveal pattern)
5. Reorder tabs so summaries appear between Transcript and Chat
6. Remove the dead promptMenuItem function and showExtraInstructions state
## Files Changed
- Sources/MacParakeet/Views/Components/FlowLayout.swift (+60, new)
- Sources/MacParakeet/Views/Transcription/TranscriptResultView.swift (+64, -61)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two SummaryViewModelTests used tight timing margins (700ms and 450ms) that passed locally but failed on slower CI runners. Increased to 1500ms and 1000ms respectively to provide adequate headroom. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename the multi-prompt result layer away from the old summary-specific terminology now that custom prompts can produce many result tabs. This commit: - renames the core result model, repository, and view model to PromptResult, PromptResultRepository, and PromptResultsViewModel - updates app wiring, tab selection, and result rendering to use result-oriented naming throughout the feature - replaces single-slot badge state with per-result unread tracking so multiple completed tabs can remain unread at the same time - keeps compatibility boundaries intact by preserving the summaries table, the legacy transcription.summary mirror, and the stored prompt category raw value of "summary" - refreshes repository, view-model, and transcription tests and removes the obsolete Summary* source and test files Verified with: - swift test --filter PromptResultsViewModelTests - swift test --filter TranscriptionViewModelTests - swift test --filter PromptResultRepositoryTests
Align the prompt-library architecture with the shipped multi-result model. This commit: - reframes the prompt library UI around built-in prompts instead of misleading community/runtime wording - keeps community-prompts.json in sync with the shipped built-in prompt set and excludes it from the SwiftPM target as a repo artifact rather than a runtime resource - renames the LLM result-generation API from summary-oriented names to prompt-result names while preserving summarize wrappers for compatibility - renames prompt-result telemetry events away from summary-specific naming so analytics match the feature model - adds regression coverage for prompt artifact drift and updates telemetry/view-model test doubles accordingly Verified with: - swift test --filter PromptRepositoryTests - swift test --filter PromptResultsViewModelTests - swift test --filter LLMServiceTests - swift test --filter TelemetryServiceTests - swift test
## What Changed - Replaced 7 generic prompts with 6 intentional, high-quality prompts: Summary, Action Items & Decisions, Chapter Breakdown, Study Guide, Blog Post, and What Stood Out (reflection/observation wildcard) - Each prompt specifies exact output structure, conditional sections, and behavioral constraints that prevent filler - Synced community-prompts.json artifact with new prompt definitions - Updated all test references from old prompt names to new ones - Made isAutoRun an explicit parameter on makeBuiltInPrompt instead of hardcoding against "General Summary" name match - Added Merkaba watermark and polish to PromptLibraryView ## Root Intent The original 7 prompts were generic and overlapping (e.g., "Bullet Points" vs "General Summary" vs "Executive Brief" all produced similar output). Researched Granola, Fireflies, tl;dv, and open-source prompt libraries to identify what separates great built-in prompts from mediocre ones: specificity, conditional sections, and behavioral constraints. The new set covers distinct use cases with no overlap. ## Prompt That Would Produce This Diff Replace the 7 generic built-in prompts in Prompt.builtInPrompts() with 6 high-quality prompts: Summary (auto-run default), Action Items & Decisions, Chapter Breakdown, Study Guide, Blog Post, and What Stood Out. Each prompt should specify exact output structure with markdown headings, include behavioral constraints (omit sections when irrelevant, don't fabricate consensus, don't pad), and adapt to content type (monologue vs conversation). Update community-prompts.json to match. Fix all tests that reference old prompt names or counts. ## Files Changed - Sources/MacParakeet/Views/Transcription/PromptLibraryView.swift (~31) - Sources/MacParakeetCore/Resources/community-prompts.json (~38) - Tests/MacParakeetTests/Database/PromptRepositoryTests.swift (~52) - Tests/MacParakeetTests/ViewModels/PromptResultsViewModelTests.swift (~4) - Tests/MacParakeetTests/ViewModels/PromptsViewModelTests.swift (~12) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nsition overlap bug
Overview
Closes #51.
Replaces the single-summary model with a prompt library and persistent multi-result architecture. Users choose from community or custom prompts, layer on extra instructions, and keep every LLM result per transcription.
Architecture
Data model
Each transcription can have many LLM results. Each result snapshots the prompt that produced it so the record remains meaningful even if the prompt is later edited or deleted.
The
prompts→summarieslink is a denormalized snapshot (dashed), not a foreign key. This is intentional: results preserve the exact prompt wording at generation time.Generation pipeline
LLM results are queued and processed serially per transcription. Only one stream is active at a time.
Everything runs on
@MainActor— no concurrent mutation of the pending array.ViewModel ownership
PromptResultsViewModelcommunicates back toTranscriptionViewModelvia closures (onGenerationCompleted,onDeletedPromptResult,onLegacySummaryChanged) rather than direct references, keeping the dependency one-directional.Tab model
Each persisted result and in-flight generation gets its own tab:
Streaming tabs transition to result tabs on completion. Cancelled/failed generations remove their tab.
What Changed for Users
Implementation
Data model and migration
Promptmodel — name, content, category, visibility, auto-run flag, sort orderPromptResultmodel — transcription FK, snapshotted prompt name/content/extra instructions, generated content, timestampspromptsandsummariestables with cascade delete on transcription removaltranscriptions.summarydata as aPromptResultattributed to the default promptService changes
LLMServiceProtocolextended withgeneratePromptResultStream(transcript:systemPrompt:)for custom system promptstranscriptions.summarycolumn synced best-effort (non-fatal on failure)Spec and documentation
spec/12-processing-layer.mdupdated for the shipped v0.7 contractspec/13-agent-workflows.mdMigration and Compatibility
PromptResultrecords — no data losstranscriptions.summaryremains populated (best-effort) for export and serialization consumersTest Plan
swift test— 1156 tests passing (1143 XCTest + 13 Swift Testing)🤖 Generated with Claude Code