Skip to content

Commit d5fc1f0

Browse files
committed
Updated more on how the transcript is shown in the no-detail view on
1 parent 362c6c6 commit d5fc1f0

8 files changed

Lines changed: 158 additions & 24 deletions

File tree

VoiceInk-ios/AppSettings.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,9 @@ final class AppSettings: ObservableObject {
228228
/// Get the effective custom prompt (from selected mode or first mode)
229229
var effectiveCustomPrompt: String {
230230
if let selectedMode = selectedMode {
231-
return selectedMode.customPrompt
231+
return selectedMode.effectivePrompt
232232
} else if let firstMode = modes.first {
233-
return firstMode.customPrompt
233+
return firstMode.effectivePrompt
234234
} else {
235235
return "" // Default fallback
236236
}

VoiceInk-ios/DefaultModeManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class DefaultModeManager {
3131
isPostProcessingEnabled: false, // No post-processing
3232
postProcessingProvider: .groq, // Doesn't matter since disabled
3333
postProcessingModel: "llama-3.1-8b-instant", // Doesn't matter since disabled
34-
customPrompt: "" // No custom prompt
34+
promptTemplate: PromptTemplate(type: .summary) // Default to summary template
3535
)
3636
}
3737

VoiceInk-ios/Mode.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,40 @@ struct Mode: Identifiable, Codable {
1212
var isPostProcessingEnabled: Bool
1313
var postProcessingProvider: Provider
1414
var postProcessingModel: String
15-
var customPrompt: String
15+
var promptTemplate: PromptTemplate
1616

1717
init(name: String,
1818
transcriptionProvider: Provider = .groq,
1919
transcriptionModel: String? = nil,
2020
isPostProcessingEnabled: Bool = false,
2121
postProcessingProvider: Provider = .groq,
2222
postProcessingModel: String? = nil,
23-
customPrompt: String = "") {
23+
promptTemplate: PromptTemplate? = nil) {
2424
self.id = UUID()
2525
self.name = name
2626
self.transcriptionProvider = transcriptionProvider
2727
self.transcriptionModel = transcriptionModel ?? transcriptionProvider.models(for: .transcription).first ?? "whisper-large-v3"
2828
self.isPostProcessingEnabled = isPostProcessingEnabled
2929
self.postProcessingProvider = postProcessingProvider
3030
self.postProcessingModel = postProcessingModel ?? postProcessingProvider.models(for: .postProcessing).first ?? "llama-3.1-8b-instant"
31-
self.customPrompt = customPrompt
31+
self.promptTemplate = promptTemplate ?? PromptTemplate(type: .summary)
32+
}
33+
34+
/// Legacy support for custom prompts - creates a custom template
35+
@available(*, deprecated, message: "Use promptTemplate instead")
36+
var customPrompt: String {
37+
get {
38+
return promptTemplate.type == .custom ? promptTemplate.customPrompt : ""
39+
}
40+
set {
41+
if !newValue.isEmpty {
42+
promptTemplate = PromptTemplate(type: .custom, customPrompt: newValue)
43+
}
44+
}
45+
}
46+
47+
/// Returns the effective prompt to use for post-processing
48+
var effectivePrompt: String {
49+
return promptTemplate.effectivePrompt
3250
}
3351
}

VoiceInk-ios/ModeConfigurationView.swift

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@ struct ModeConfigurationView: View {
66

77
@State private var mode: Mode
88
@State private var isEditing: Bool
9+
@State private var selectedTemplateType: PromptTemplateType
10+
@State private var customPromptText: String
911

1012
let onSave: (Mode) -> Void
1113

1214
init(mode: Mode? = nil, settings: AppSettings, onSave: @escaping (Mode) -> Void) {
1315
self.settings = settings
1416
self.onSave = onSave
1517
self.isEditing = mode != nil
16-
self._mode = State(initialValue: mode ?? Mode(name: ""))
18+
let initialMode = mode ?? Mode(name: "")
19+
self._mode = State(initialValue: initialMode)
20+
self._selectedTemplateType = State(initialValue: initialMode.promptTemplate.type)
21+
self._customPromptText = State(initialValue: initialMode.promptTemplate.customPrompt)
1722
}
1823

1924
/// Available transcription providers (those with valid API keys or downloaded local models)
@@ -96,8 +101,18 @@ struct ModeConfigurationView: View {
96101
}
97102
}
98103

99-
TextField("Custom Prompt (Optional)", text: $mode.customPrompt, axis: .vertical)
100-
.lineLimit(4, reservesSpace: true)
104+
// Prompt Template Selection
105+
Picker("Prompt Template", selection: $selectedTemplateType) {
106+
ForEach(PromptTemplateType.allCases, id: \.self) { templateType in
107+
Text(templateType.displayName).tag(templateType)
108+
}
109+
}
110+
111+
// Show custom prompt field only when Custom is selected
112+
if selectedTemplateType == .custom {
113+
TextField("Custom Prompt", text: $customPromptText, axis: .vertical)
114+
.lineLimit(4, reservesSpace: true)
115+
}
101116
}
102117
}
103118
}
@@ -106,10 +121,13 @@ struct ModeConfigurationView: View {
106121
.toolbar {
107122
ToolbarItem(placement: .confirmationAction) {
108123
Button("Save") {
124+
// Update the mode's prompt template before saving
125+
mode.promptTemplate = PromptTemplate(type: selectedTemplateType, customPrompt: customPromptText)
109126
onSave(mode)
110127
dismiss()
111128
}
112-
.disabled(mode.name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
129+
.disabled(mode.name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ||
130+
(selectedTemplateType == .custom && customPromptText.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty))
113131
}
114132
}
115133
.onChange(of: mode.transcriptionProvider) { _, _ in

VoiceInk-ios/NoteDetailView.swift

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,9 @@ struct NoteDetailView: View {
2424
transcriptionStatusView
2525
}
2626

27-
// Transcript content (moved to top)
27+
// Transcript content
2828
if note.transcriptionStatus == .completed {
29-
VStack(alignment: .leading, spacing: 12) {
30-
Text("Transcript")
31-
.font(.headline)
32-
33-
Text((note.enhancedText ?? note.text).isEmpty ? "No content" : (note.enhancedText ?? note.text))
34-
.font(.body)
35-
.textSelection(.enabled)
36-
.padding()
37-
.background(Color(.secondarySystemGroupedBackground))
38-
.clipShape(RoundedRectangle(cornerRadius: 12))
39-
}
29+
transcriptContentView
4030
}
4131

4232
// Add bottom padding to account for audio player
@@ -62,6 +52,39 @@ struct NoteDetailView: View {
6252
.navigationBarTitleDisplayMode(.inline)
6353
}
6454

55+
private var transcriptContentView: some View {
56+
VStack(alignment: .leading, spacing: 8) {
57+
HStack {
58+
Text("Transcript")
59+
.font(.headline)
60+
Spacer()
61+
Button(action: { copyToClipboard(displayedTranscriptText) }) {
62+
Image(systemName: "doc.on.doc")
63+
.font(.system(size: 16))
64+
.foregroundStyle(.blue)
65+
}
66+
}
67+
68+
Text(displayedTranscriptText)
69+
.font(.body)
70+
.textSelection(.enabled)
71+
.padding()
72+
.background(Color(.secondarySystemGroupedBackground))
73+
.clipShape(RoundedRectangle(cornerRadius: 12))
74+
}
75+
}
76+
77+
private var displayedTranscriptText: String {
78+
// Backend logic: Show post-processed result if available, otherwise show original
79+
if let enhancedText = note.enhancedText, !enhancedText.isEmpty {
80+
return enhancedText
81+
} else if !note.text.isEmpty {
82+
return note.text
83+
} else {
84+
return "No content available."
85+
}
86+
}
87+
6588
private var hasAudioFile: Bool {
6689
guard let audioPath = note.fullAudioPath,
6790
FileManager.default.fileExists(atPath: audioPath) else {
@@ -224,6 +247,16 @@ struct NoteDetailView: View {
224247
}
225248
}
226249
}
250+
251+
private func copyToClipboard(_ text: String) {
252+
UIPasteboard.general.string = text
253+
254+
// Optional: Add haptic feedback
255+
let impactFeedback = UIImpactFeedbackGenerator(style: .light)
256+
impactFeedback.impactOccurred()
257+
}
258+
259+
227260
}
228261

229262

VoiceInk-ios/NoteRowView.swift

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,14 @@ struct NoteRowView: View {
5151
case .failed:
5252
return "Transcription failed - tap to retry"
5353
case .completed:
54-
let displayText = note.enhancedText ?? note.text
55-
return displayText.isEmpty ? "No audible content detected." : displayText
54+
// Prioritize enhanced text (post-processed result) over original text
55+
if let enhancedText = note.enhancedText, !enhancedText.isEmpty {
56+
return enhancedText
57+
} else if !note.text.isEmpty {
58+
return note.text
59+
} else {
60+
return "No audible content detected."
61+
}
5662
}
5763
}
5864

VoiceInk-ios/PromptTemplate.swift

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import Foundation
2+
3+
enum PromptTemplateType: String, CaseIterable, Codable {
4+
case custom = "Custom"
5+
case summary = "Summary"
6+
case keyPoints = "Key Points"
7+
case rewrite = "Rewrite"
8+
case transcriptCleanup = "Clean Transcript"
9+
10+
var displayName: String {
11+
return self.rawValue
12+
}
13+
14+
var prompt: String {
15+
switch self {
16+
case .custom:
17+
return "" // Custom prompts are handled separately
18+
case .summary:
19+
return "Please provide a concise summary of the following transcription, capturing the main points and key information in a clear and organized manner:"
20+
case .keyPoints:
21+
return "Please extract the key points from the following transcription and present them as a bulleted list, highlighting the most important information:"
22+
case .rewrite:
23+
return "Please rewrite the following transcription to improve clarity, grammar, and flow while preserving the original meaning and intent:"
24+
case .transcriptCleanup:
25+
return "Please clean up the following transcription by correcting any errors, removing filler words, and improving readability while maintaining the speaker's original meaning and tone:"
26+
}
27+
}
28+
}
29+
30+
struct PromptTemplate: Identifiable, Codable {
31+
let id = UUID()
32+
let type: PromptTemplateType
33+
let customPrompt: String // Only used when type is .custom
34+
35+
init(type: PromptTemplateType, customPrompt: String = "") {
36+
self.type = type
37+
self.customPrompt = customPrompt
38+
}
39+
40+
/// Returns the effective prompt to use for post-processing
41+
var effectivePrompt: String {
42+
switch type {
43+
case .custom:
44+
return customPrompt
45+
default:
46+
return type.prompt
47+
}
48+
}
49+
50+
/// Returns a display-friendly description of the template
51+
var description: String {
52+
switch type {
53+
case .custom:
54+
return customPrompt.isEmpty ? "Custom prompt (empty)" : "Custom: \(customPrompt.prefix(50))..."
55+
default:
56+
return type.displayName
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)