Skip to content

Commit 9d5edc6

Browse files
committed
Release 0.1.4: document current limits and tighten volume control state
1 parent a4a485b commit 9d5edc6

13 files changed

Lines changed: 394 additions & 53 deletions

File tree

.plans/01-initial-plan.md

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
Version snapshot:
44

5-
- release version: `0.1.3`
6-
- build number: `4`
7-
- snapshot date: `2026-04-09`
5+
- release version: `0.1.4`
6+
- build number: `5`
7+
- snapshot date: `2026-04-10`
88

99
## Goal
1010

@@ -52,8 +52,8 @@ The extension is a view-based Quick Look preview extension with:
5252

5353
Current behavior:
5454

55-
- playback starts paused in all Quick Look contexts
5655
- compact Finder column preview is non-live summary mode
56+
- expanded Quick Look preview autoplays
5757
- large Quick Look preview is the live playback surface
5858

5959
This was a deliberate change. Autoplay in the small Finder preview was too disruptive and Finder does not expose a reliable public “column preview vs full preview” switch for the extension.
@@ -142,14 +142,14 @@ In practice, the custom owned UTIs are what made routing predictable.
142142
- Finder can route supported files to `MKVQuickLook`
143143
- `mkv`, `webm`, and Ogg video preview path are working
144144
- problematic `Reflections.mkv` is now selected through the extension and renders in the correct position
145-
- playback starts paused
146145
- large preview exposes manual controls
147146
- volume and seek controls exist
148147
- play/pause now lives in the control row beside the seek bar
149148
- diagnostics exist for volume and seek latency
150-
- dragging the volume knob applies immediately
149+
- dragging the volume knob lands at the release point correctly again
151150
- dragging the seek knob works
152151
- pure click behavior on the seek bar was repeatedly problematic in Finder-hosted Quick Look and required custom handling
152+
- autoplay in expanded Quick Look works again without re-enabling the old compact-preview autoplay bug
153153

154154
### Deliberate Constraints
155155

@@ -163,7 +163,7 @@ In practice, the custom owned UTIs are what made routing predictable.
163163
These findings are specific enough that they should remain documented here:
164164

165165
- volume drag is responsive because the app writes directly to `mediaPlayer.audio?.volume`
166-
- volume bar click had an observable lag even when drag felt immediate
166+
- volume bar click still has an observable lag on the target machine even after the handle-position regressions were corrected
167167
- seek drag and seek pure-click were not equivalent in Finder-hosted Quick Look
168168
- a plain click on the seek bar could trigger internal activity without moving the handle or visibly changing position
169169
- click-plus-small-drag on the seek bar behaved differently and did move to the target
@@ -174,10 +174,44 @@ Current engineering interpretation:
174174
- seek-bar click behavior is at least partly an interaction-delivery problem, not just a simple seek-timing problem
175175
- Finder-hosted Quick Look control behavior must be treated as distinct from generic AppKit assumptions
176176

177+
Implementation-level findings from the later volume investigation:
178+
179+
- the custom slider path must distinguish knob drags from track clicks; pre-applying an absolute value on knob drag start is wrong
180+
- track-click targeting must use AppKit slider bar geometry (`NSSliderCell.barRectFlipped(_:)` / `knobRectFlipped(_:)`), not raw `bounds.width`
181+
- volume needs the same kind of interaction protection that seek needed:
182+
- a persistent interaction identity during the drag/click
183+
- a pending requested value that stays authoritative
184+
- the deeper correction is stronger than that:
185+
- for this app, the displayed volume should not be driven by repeated player read-backs at all
186+
- volume is a local user command, not a media timeline
187+
- letting VLCKit metric publications re-drive the slider reintroduces drift and apparent delay after release
188+
- another concrete failure that actually happened:
189+
- the custom slider was still pre-applying an absolute value on knob drag start
190+
- that was wrong for knob drags and caused volume-handle movement to be inconsistent with the real drag gesture
191+
- otherwise a late metrics publication from VLCKit can overwrite the just-released handle position and make the control feel delayed or imprecise
192+
193+
Primary sources used for this conclusion:
194+
195+
- AppKit SDK header: `NSSliderCell.h`
196+
- `-knobRectFlipped:`
197+
- `-barRectFlipped:`
198+
- AppKit SDK header: `NSCell.h`
199+
- `-hitTestForEvent:inRect:ofView:`
200+
- repository-local playback diagnostics logs and the `TrackingSlider` / `VLCKitMediaPreviewPlayer` code paths
201+
177202
Current diagnostic support:
178203

179204
- timestamped unified logging exists for seek and volume UI events, controller handoff, player apply, and post-apply metrics
180205
- this must be used before making more speculative “speed” changes
206+
- those diagnostics already answer one key question:
207+
- the app does measure UI-change, controller handoff, player apply, and later metrics timestamps
208+
- so when the slider still feels wrong after `apply`, the remaining problem is not “missing measurement”; it is usually wrong control architecture or downstream audio/output behavior
209+
- local VLCKit volume notifications are now also logged, so future work must compare:
210+
- `ui-change`
211+
- `controller-change`
212+
- `apply`
213+
- `vlc-notification`
214+
- `metrics`
181215

182216
## Test Strategy That Must Stay In Place
183217

@@ -202,6 +236,14 @@ Important CI exception:
202236
- reason: they depend on visible AppKit/VLCKit rendering and are not reliable on hosted release runners
203237
- they remain mandatory for local verification
204238

239+
### Control Regression Tests
240+
241+
The test suite must also keep explicit regression coverage for control-state bugs that already happened:
242+
243+
- volume state must keep the latest requested value even if the player reports an older one
244+
- playback metrics must not overwrite the user-controlled volume slider position
245+
- active preview session replacement must stop the previous player
246+
205247
### Metadata Tests
206248

207249
These tests are also mandatory because Finder selection depends on metadata correctness:
@@ -535,7 +577,7 @@ Rule:
535577

536578
- keep and use the host-app playback lab for the same shared renderer path
537579

538-
### 4. Do Not Autoplay In Finder By Default
580+
### 4. Do Not Autoplay In Compact Finder Preview
539581

540582
What went wrong:
541583

@@ -548,12 +590,12 @@ Reason:
548590

549591
Rule:
550592

551-
- default to paused
552-
- require explicit user play
593+
- compact Finder preview must stay non-live
594+
- any autoplay behavior must be restricted to the expanded Quick Look path only
553595

554596
No-go:
555597

556-
- do not reintroduce autoplay unless there is a reliable host-context discriminator and a test for it
598+
- do not reintroduce autoplay in compact Finder preview unless there is a reliable host-context discriminator and a test for it
557599

558600
### 5. Do Not Trust VLC State Enums As The Only Playback Truth
559601

@@ -587,10 +629,13 @@ Rule:
587629
- for seek, test both while playback is active:
588630
- knob drag
589631
- bar click with no drag
632+
- for volume, backend playback metrics must not be allowed to re-drive the visible slider position during or after user interaction
633+
- for knob drags, do not pre-apply an absolute track-click value at mouse-down
590634

591635
No-go:
592636

593637
- do not claim control fixes complete until both interaction styles are verified in Finder
638+
- do not let player read-backs overwrite the user-commanded volume slider state
594639

595640
### 7. Do Not Use Clever Paused-Start State Machines Without End-To-End Playback Verification
596641

@@ -648,6 +693,22 @@ No-go:
648693
- no ffmpeg/remux/transcode fallback in the normal preview path
649694
- no persistent generated media files
650695

696+
### 11. Do Not Describe Work As Solved Before Evidence Exists
697+
698+
What went wrong:
699+
700+
- some explanations used probability language or confidence language before the underlying behavior was fully verified
701+
- that weakened trust and violated the repository communication standard
702+
703+
Rule:
704+
705+
- separate observed fact, evidence, hypothesis, fix, and remaining unverified risk
706+
- do not describe a fix as complete until tests and observed behavior support that claim
707+
708+
No-go:
709+
710+
- do not present guesses, partial reasoning, or under-verified fixes as if they were solid conclusions
711+
651712
## Recommended Debug Workflow For Future Changes
652713

653714
When something breaks, use this order:
@@ -712,6 +773,6 @@ These are reasonable future changes:
712773
These are risky and should be approached carefully:
713774

714775
- changing renderer integration again
715-
- reintroducing autoplay
776+
- changing expanded-preview autoplay behavior without re-checking compact-preview isolation
716777
- trying to force Finder compact preview sizing
717778
- adding conversion/remux fallback logic

AGENTS.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,31 @@
22

33
This file defines mandatory working rules for agents operating in this repository.
44

5+
## EXTREMELY IMPORTANT - MUST-OBEY RULE
6+
7+
Before giving any answer, proposing any fix, or claiming that a problem is solved, the agent must stop and ask itself:
8+
9+
`Did I really think through everything?`
10+
11+
This question must be answered honestly, without self-flattery, without face-saving language, and without pretending that partial understanding is enough.
12+
13+
If the honest answer is `no`, the agent must not present the work as complete.
14+
15+
It must instead:
16+
17+
- go back to the problem
18+
- dig deeper
19+
- verify more
20+
- reduce uncertainty
21+
- test the relevant behavior again
22+
- only answer once the result is genuinely thought through
23+
24+
The agent must never use reassuring language to cover incomplete reasoning.
25+
The agent must never present guesses, shallow interpretations, or half-verified fixes as if they were solid conclusions.
26+
The agent must never speak as if luck, hope, or impression is an acceptable substitute for understanding.
27+
28+
This rule is crucial. If it is violated, costs and rework can explode far beyond the original estimate.
29+
530
## Critical Rule
631

732
The standards below are not optional polish. They are crucial.
@@ -50,9 +75,12 @@ Agents must therefore optimize for correctness, depth, testability, and honesty,
5075
- Be honest, direct, and technically precise.
5176
- Do not flatter yourself.
5277
- Do not claim certainty that has not been earned.
78+
- Do not use language that suggests guessing, hoping, luck, or superficial confidence.
79+
- Do not hide incomplete reasoning behind smooth wording.
5380
- If something is broken, risky, unverified, poorly understood, or likely to regress, say so plainly.
5481
- When explaining a problem, prefer uncomfortable truth over reassuring vagueness.
5582
- If a previous approach was wrong, say so directly and explain the correction.
83+
- When answering, first apply the must-obey rule above and only speak after the answer has been honestly pressure-tested.
5684

5785
## User-Idea Evaluation
5886

CHANGELOG.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,31 @@
22

33
## Unreleased
44

5+
## 0.1.4 - 2026-04-10
6+
57
### Added
68

79
- Added owned `.opus` audio support in the app and extension metadata.
810
- Added an audio-only Quick Look mode that keeps playback controls visible without showing an empty video frame.
11+
- Added stronger regression tests for volume state handling and for preventing playback metrics from overwriting the user-controlled volume slider position.
12+
- Added local VLCKit volume-notification diagnostics so downstream audio timing can be measured instead of guessed.
13+
14+
### Changed
15+
16+
- Full Quick Look preview autoplays again in expanded mode; compact Finder preview remains a non-live summary.
17+
- Volume slider state is now driven by the last user-commanded value instead of repeated backend metric write-backs.
918

1019
### Fixed
1120

12-
- Switching to another file while a preview is already open now stops the previous player before the new one is attached, preventing stray audio from continuing in the background.
21+
- Selecting another file while a preview is already open now stops the previously active preview session before the new one loads, preventing stray audio from continuing in the background.
22+
- Pause now cuts audible output immediately before VLC finishes its own state transition, so the control feels more responsive.
23+
- Volume handle release now stays at the released position instead of snapping back to the earlier pickup position.
24+
- The custom slider no longer pre-applies an absolute value on knob drag start, which was a real source of incorrect volume-handle movement.
1325

14-
### Notes
26+
### Known Issues
1527

16-
- `.opus` is audio-only support, not video support hidden behind another extension.
28+
- Direct click on the volume bar still has an observable delay on the target machine even though handle placement is now exact.
29+
- The remaining volume-click latency is instrumented but not fully eliminated in Finder-hosted Quick Look.
1730

1831
## 0.1.3 - 2026-04-09
1932

MKVQuickLook.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@
548548
CLANG_WARN_UNREACHABLE_CODE = YES;
549549
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
550550
COPY_PHASE_STRIP = NO;
551-
CURRENT_PROJECT_VERSION = 4;
551+
CURRENT_PROJECT_VERSION = 5;
552552
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
553553
ENABLE_NS_ASSERTIONS = NO;
554554
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -561,7 +561,7 @@
561561
GCC_WARN_UNUSED_FUNCTION = YES;
562562
GCC_WARN_UNUSED_VARIABLE = YES;
563563
MACOSX_DEPLOYMENT_TARGET = 14.0;
564-
MARKETING_VERSION = 0.1.3;
564+
MARKETING_VERSION = 0.1.4;
565565
MTL_ENABLE_DEBUG_INFO = NO;
566566
MTL_FAST_MATH = YES;
567567
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -721,7 +721,7 @@
721721
CLANG_WARN_UNREACHABLE_CODE = YES;
722722
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
723723
COPY_PHASE_STRIP = NO;
724-
CURRENT_PROJECT_VERSION = 4;
724+
CURRENT_PROJECT_VERSION = 5;
725725
DEBUG_INFORMATION_FORMAT = dwarf;
726726
ENABLE_STRICT_OBJC_MSGSEND = YES;
727727
ENABLE_TESTABILITY = YES;
@@ -740,7 +740,7 @@
740740
GCC_WARN_UNUSED_FUNCTION = YES;
741741
GCC_WARN_UNUSED_VARIABLE = YES;
742742
MACOSX_DEPLOYMENT_TARGET = 14.0;
743-
MARKETING_VERSION = 0.1.3;
743+
MARKETING_VERSION = 0.1.4;
744744
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
745745
MTL_FAST_MATH = YES;
746746
ONLY_ACTIVE_ARCH = YES;

MKVQuickLookApp/Sources/PlaybackLabViewController.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ final class PlaybackLabViewController: NSViewController {
2323
override func viewDidDisappear() {
2424
super.viewDidDisappear()
2525
player.stop()
26+
MediaPreviewPlayerSession.deactivate(player)
2627
}
2728

2829
func loadFile(_ url: URL?) {
@@ -67,7 +68,12 @@ final class PlaybackLabViewController: NSViewController {
6768
)
6869
previewContentView.attachRenderView(player.renderView)
6970
previewContentView.playbackToggleHandler = { [weak self] in
70-
self?.player.togglePlayback()
71+
guard let self else {
72+
return
73+
}
74+
75+
MediaPreviewPlayerSession.activate(self.player)
76+
self.player.togglePlayback()
7177
}
7278
previewContentView.seekTrackingHandler = { [weak self] isTracking, interactionID in
7379
guard let self else {

MKVQuickLookPreviewExtension/Sources/PreviewViewController.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ final class PreviewViewController: NSViewController, QLPreviewingController {
2020
super.viewDidLoad()
2121
preferredContentSize = Self.expandedPreferredContentSize
2222
previewContentView.playbackToggleHandler = { [weak self] in
23-
self?.player?.togglePlayback()
23+
guard let self, let player = self.player else {
24+
return
25+
}
26+
27+
MediaPreviewPlayerSession.activate(player)
28+
player.togglePlayback()
2429
}
2530
previewContentView.seekTrackingHandler = { [weak self] isTracking, interactionID in
2631
guard let self else {
@@ -54,11 +59,13 @@ final class PreviewViewController: NSViewController, QLPreviewingController {
5459
override func viewDidDisappear() {
5560
super.viewDidDisappear()
5661
player?.stop()
62+
MediaPreviewPlayerSession.deactivate(player)
5763
isMediaLoaded = false
5864
}
5965

6066
func preparePreviewOfFile(at url: URL) async throws {
6167
do {
68+
MediaPreviewPlayerSession.stopActivePreview()
6269
let metadata = try PreviewMetadata(fileURL: url)
6370
let player = VLCKitMediaPreviewPlayer()
6471
player.playbackStateDidChange = { [weak self] state in
@@ -106,13 +113,17 @@ final class PreviewViewController: NSViewController, QLPreviewingController {
106113
case .compact:
107114
if isMediaLoaded && modeChanged {
108115
player?.stop()
116+
MediaPreviewPlayerSession.deactivate(player)
109117
isMediaLoaded = false
110118
}
111119
case .expanded:
112120
if !isMediaLoaded, let currentFileURL {
113121
player?.loadMedia(from: currentFileURL)
114122
isMediaLoaded = true
115-
player?.primeForPausedStart()
123+
if let player {
124+
MediaPreviewPlayerSession.activate(player)
125+
player.play()
126+
}
116127
}
117128
}
118129
}

0 commit comments

Comments
 (0)