Skip to content

Commit be624b4

Browse files
angusbezzinaclaude
andcommitted
feat(overlay): the toolbar gets its own window, so it can never move or vanish
The pill was drawn inside the catcher's ZStack, which made its position a function of the catcher panel's frame. That frame changes size on every open/close, is narrowed to the visible screen, and is dragged around by AppKit whenever the host moves -- three ways for a control that must never move to move -- and anything that stalled the catcher (a wedged scroll, a rebuilt SwiftUI root) took the pill down with it. The toolbar now has its own permanently mounted panel: fixed size, pinned to the host's bottom-right, re-placed only when the host moves, resizes or changes screen. Opening the menu creates a SEPARATE catcher panel over the host and closing it tears that panel down. The menu is now genuinely just open or closed, and the toolbar itself is untouched by the transition. Ordering matters and is not free: re-adding an existing child window does NOT re-stack it (measured -- childWindows still ended with the catcher, and the catcher sat on top, eating the click that closes the menu). The toolbar is detached and re-added, then ordered explicitly above the catcher. Probe updated for the two-panel world: childWindows.first is no longer "the overlay", and BOTH panels carry the AX identifier -- correctly, since the point query must skip both -- so 7g now picks the panel that actually spans the host rather than the first match. 191 tests, probe 11/11, iOS cross-compile clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 3267df7 commit be624b4

4 files changed

Lines changed: 322 additions & 86 deletions

File tree

Sources/AnnotKit/Overlay/OverlayView.swift

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ struct OverlayView: View {
8686
editCard(note: note, anchor: anchor)
8787
}
8888

89-
toolbar
9089
}
9190
.ignoresSafeArea()
9291
.accessibilityHidden(true)
@@ -577,9 +576,29 @@ struct OverlayView: View {
577576
)
578577
}
579578

580-
/// Pins the compact pill to the host window's bottom-right corner (margin 20,
581-
/// matching the idle child-window frame the controller sizes).
582-
private var toolbar: some View {
579+
}
580+
581+
/// The toolbar pill, in a window of its OWN.
582+
///
583+
/// The pill used to be drawn inside the catcher's ZStack, which meant its position
584+
/// was a function of the catcher panel's frame — and that frame changes size on
585+
/// every open/close, gets narrowed to the visible screen, and is dragged around by
586+
/// AppKit whenever the host window moves. Three ways for a control that must never
587+
/// move to move, and a fourth failure on top: anything that stalled the catcher
588+
/// (a wedged scroll, a rebuilt SwiftUI root) took the pill down with it.
589+
///
590+
/// Giving it a separate, permanently mounted panel makes the guarantee structural
591+
/// rather than incidental: the pill is always present, always at the host's
592+
/// bottom-right corner, and the catcher's geometry and lifetime cannot touch it.
593+
/// The menu is now genuinely just OPEN or CLOSED — the toolbar itself never moves
594+
/// between the two.
595+
struct ToolbarOverlayView: View {
596+
@ObservedObject var session: AnnotationSession
597+
let onToggle: () -> Void
598+
let onCopy: () -> Void
599+
let onExport: () -> Void
600+
601+
var body: some View {
583602
VStack {
584603
Spacer()
585604
HStack {
@@ -593,6 +612,8 @@ struct OverlayView: View {
593612
.padding(20)
594613
}
595614
}
615+
.ignoresSafeArea()
616+
.accessibilityHidden(true)
596617
}
597618
}
598619

@@ -741,7 +762,7 @@ private struct AnnotationCard<Footer: View>: View {
741762
/// The pill itself is rendered unconditionally and is NEVER gated on an entrance
742763
/// flag, so it stays visible across idle<->annotate; only its contents swap as
743764
/// annotate mode toggles.
744-
private struct ToolbarView: View {
765+
struct ToolbarView: View {
745766
@ObservedObject var session: AnnotationSession
746767
let onToggle: () -> Void
747768
let onCopy: () -> Void

0 commit comments

Comments
 (0)