Skip to content

Commit b9c60d8

Browse files
jkuriclaude
andcommitted
feat(captions): add caption history tracking and improve transcription
Add descriptive change tracking for all caption settings in the history popover. Improve transcription performance with .all compute options, higher concurrency, and monotonic progress reporting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a7c6c8e commit b9c60d8

2 files changed

Lines changed: 95 additions & 6 deletions

File tree

Reframed/Editor/History.swift

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,16 @@ final class History {
219219
changes.append("Camera background set to \(describeCameraBackground(new.cameraBackgroundStyle))")
220220
}
221221

222+
if old.captionSettings != new.captionSettings || old.captionSegments != new.captionSegments {
223+
describeCaptionChanges(
224+
from: old.captionSettings,
225+
to: new.captionSettings,
226+
oldSegments: old.captionSegments,
227+
newSegments: new.captionSegments,
228+
into: &changes
229+
)
230+
}
231+
222232
if old.videoRegions != new.videoRegions {
223233
let oldCount = old.videoRegions?.count ?? 0
224234
let newCount = new.videoRegions?.count ?? 0
@@ -239,6 +249,9 @@ final class History {
239249
changes.append("Animation settings updated")
240250
}
241251
if old.cameraLayout != new.cameraLayout { changes.append("Camera layout updated") }
252+
if old.captionSettings != new.captionSettings || old.captionSegments != new.captionSegments {
253+
changes.append("Caption settings updated")
254+
}
242255
}
243256

244257
if changes.isEmpty {
@@ -382,6 +395,76 @@ final class History {
382395
}
383396
}
384397

398+
private static func describeCaptionChanges(
399+
from old: CaptionSettingsData?,
400+
to new: CaptionSettingsData?,
401+
oldSegments: [CaptionSegment]?,
402+
newSegments: [CaptionSegment]?,
403+
into changes: inout [String]
404+
) {
405+
let oldSegs = oldSegments ?? []
406+
let newSegs = newSegments ?? []
407+
if oldSegs.isEmpty && !newSegs.isEmpty {
408+
changes.append("Captions generated (\(newSegs.count) segments)")
409+
return
410+
}
411+
if !oldSegs.isEmpty && newSegs.isEmpty {
412+
changes.append("Captions cleared")
413+
return
414+
}
415+
if oldSegs != newSegs && !oldSegs.isEmpty && !newSegs.isEmpty {
416+
if oldSegs.count != newSegs.count {
417+
changes.append("Caption segments updated (\(newSegs.count) segments)")
418+
} else {
419+
changes.append("Caption segments edited")
420+
}
421+
}
422+
423+
if old?.enabled != new?.enabled {
424+
let enabled = new?.enabled ?? true
425+
changes.append(enabled ? "Captions enabled" : "Captions disabled")
426+
}
427+
if old?.fontSize != new?.fontSize {
428+
changes.append("Caption font size set to \(Int(new?.fontSize ?? 48))px")
429+
}
430+
if old?.fontWeight != new?.fontWeight {
431+
let weight = new?.fontWeight ?? .bold
432+
changes.append("Caption font weight set to \(weight.label)")
433+
}
434+
if old?.position != new?.position {
435+
let pos = new?.position ?? .bottom
436+
changes.append("Caption position set to \(pos.label.lowercased())")
437+
}
438+
if old?.textColor != new?.textColor {
439+
changes.append("Caption text color updated")
440+
}
441+
if old?.backgroundColor != new?.backgroundColor {
442+
changes.append("Caption background color updated")
443+
}
444+
if old?.backgroundOpacity != new?.backgroundOpacity {
445+
changes.append("Caption background opacity set to \(Int((new?.backgroundOpacity ?? 0.6) * 100))%")
446+
}
447+
if old?.showBackground != new?.showBackground {
448+
let show = new?.showBackground ?? true
449+
changes.append(show ? "Caption background enabled" : "Caption background disabled")
450+
}
451+
if old?.maxWordsPerLine != new?.maxWordsPerLine {
452+
changes.append("Caption words per line set to \(new?.maxWordsPerLine ?? 6)")
453+
}
454+
if old?.model != new?.model {
455+
let modelName = WhisperModel(rawValue: new?.model ?? "")?.shortLabel ?? "unknown"
456+
changes.append("Caption model set to \(modelName)")
457+
}
458+
if old?.language != new?.language {
459+
let lang = new?.language ?? .auto
460+
changes.append("Caption language set to \(lang.label)")
461+
}
462+
if old?.audioSource != new?.audioSource {
463+
let source = new?.audioSource ?? .microphone
464+
changes.append("Caption audio source set to \(source.label)")
465+
}
466+
}
467+
385468
private static func describeAudioChanges(
386469
from old: AudioSettingsData?,
387470
to new: AudioSettingsData?,

Reframed/Utilities/TranscriptionService.swift

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ enum TranscriptionService {
2323
let modelsBase = FileManager.default.homeDirectoryForCurrentUser
2424
.appendingPathComponent(".reframed")
2525
let computeOptions = ModelComputeOptions(
26-
melCompute: .cpuAndGPU,
27-
audioEncoderCompute: .cpuAndNeuralEngine,
28-
textDecoderCompute: .cpuAndNeuralEngine,
29-
prefillCompute: .cpuAndGPU
26+
melCompute: .all,
27+
audioEncoderCompute: .all,
28+
textDecoderCompute: .all,
29+
prefillCompute: .all
3030
)
3131
let config = WhisperKitConfig(
3232
downloadBase: modelsBase,
@@ -41,8 +41,10 @@ enum TranscriptionService {
4141

4242
await onProgress?(0.15)
4343

44+
let workerCount = max(16, ProcessInfo.processInfo.activeProcessorCount)
4445
var options = DecodingOptions(
4546
wordTimestamps: true,
47+
concurrentWorkerCount: workerCount,
4648
chunkingStrategy: .vad
4749
)
4850
if let language {
@@ -51,11 +53,15 @@ enum TranscriptionService {
5153

5254
let progressCallback = onProgress
5355
let expectedWindows = totalWindows
56+
nonisolated(unsafe) var highWaterMark: Double = 0.15
5457
let callback: TranscriptionCallback = { (progress: TranscriptionProgress) -> Bool? in
5558
let windowProgress = Double(progress.windowId + 1) / Double(expectedWindows)
56-
let overall = 0.15 + windowProgress * 0.8
59+
let overall = min(0.15 + windowProgress * 0.8, 0.95)
60+
guard overall > highWaterMark else { return nil }
61+
highWaterMark = overall
62+
let value = overall
5763
Task { @MainActor in
58-
progressCallback?(min(overall, 0.95))
64+
progressCallback?(value)
5965
}
6066
return nil
6167
}

0 commit comments

Comments
 (0)