fix: images resize with window, blend toolbar into content

Images sized themselves once at restyle time and never got
re-measured on a pure window/pane resize (no text change, no image
fingerprint change) - so they'd stay whatever width they were last
styled at. The engine already had the fix for exactly this shape of
problem for wide tables (a stamped .scrollableBlockFullRange
attribute triggers a targeted restyle on width change), and the
shared image-rendering helper already had a restyleOnWidthChange flag
to opt into it - just never passed at the image call sites. Wired it
on for both ![]() and ![[embed]] rendering.

Also makes the titlebar/toolbar strip blend into the sidebar's
background instead of reading as a separate bar, matching Mail/Notes/
Finder - titlebarAppearsTransparent + fullSizeContentView via a small
NSViewRepresentable, same "reach into NSWindow directly" pattern
applyMacAppearance() already uses since SwiftUI's WindowGroup has no
direct API for either.
This commit is contained in:
2026-08-20 15:04:50 +01:00
parent ccbd84c05b
commit 1a58d91bb3
3 changed files with 38 additions and 5 deletions
+25
View File
@@ -34,6 +34,7 @@ struct OutpostApp: App {
.onAppear { applyMacAppearance() }
.onChange(of: appearance) { _, _ in applyMacAppearance() }
.logoutConfirmationDialog(isPresented: $isShowingLogoutConfirmation, session: session)
.background(TransparentTitlebarWindowAccessor())
#endif
}
#if os(macOS)
@@ -101,3 +102,27 @@ struct OutpostApp: App {
}
#endif
}
#if os(macOS)
/// Makes the titlebar/toolbar strip blend into the sidebar's own background
/// instead of reading as a separate bar same look as Mail/Notes/Finder.
/// SwiftUI's `WindowGroup` exposes no direct hook for this, so this reaches
/// into the underlying `NSWindow` the way `applyMacAppearance()` above
/// reaches into `NSApp` for the same reason (no SwiftUI-level API exists).
/// A `View` (not the window itself) is what actually needs to extend under
/// the now-transparent titlebar `.fullSizeContentView` just makes room;
/// the sidebar's `.background` already does the rest with no other change.
private struct TransparentTitlebarWindowAccessor: NSViewRepresentable {
func makeNSView(context: Context) -> NSView {
let view = NSView()
DispatchQueue.main.async {
guard let window = view.window else { return }
window.titlebarAppearsTransparent = true
window.styleMask.insert(.fullSizeContentView)
}
return view
}
func updateNSView(_ nsView: NSView, context: Context) {}
}
#endif
@@ -75,6 +75,7 @@ extension MarkdownStyler {
paragraphSpacing: imageEmbedConfig.paragraphSpacing,
alignment: .left,
mode: .visibleSource(imageGap: imageEmbedConfig.imageGap),
restyleOnWidthChange: true,
ctx: ctx,
attrs: &attrs
)
@@ -88,6 +89,7 @@ extension MarkdownStyler {
paragraphSpacing: imageEmbedConfig.paragraphSpacing,
alignment: .left,
mode: .collapsedSource(markerTexts: ["![", "]", "(", ")"]),
restyleOnWidthChange: true,
ctx: ctx,
attrs: &attrs
)
@@ -166,6 +168,7 @@ extension MarkdownStyler {
paragraphSpacing: imageEmbedConfig.paragraphSpacing,
alignment: .left,
mode: .visibleSource(imageGap: imageEmbedConfig.imageGap),
restyleOnWidthChange: true,
ctx: ctx,
attrs: &attrs
)
@@ -179,6 +182,7 @@ extension MarkdownStyler {
paragraphSpacing: imageEmbedConfig.paragraphSpacing,
alignment: .left,
mode: .collapsedSource(markerTexts: ["![[", "]]"]),
restyleOnWidthChange: true,
ctx: ctx,
attrs: &attrs
)
@@ -269,21 +269,25 @@ extension NativeTextView {
recalcOverscroll(for: scrollView, targetWidth: newSize.width, debugTag: "setFrameSize")
// Width change only rendered table paragraphs need restyling. Their image
// width can change, and an initially narrow table can become scrollable.
// Width change only paragraphs tagged `.scrollableBlockFullRange`
// (rendered tables and standalone images see appendRenderedStandaloneBlock's
// `restyleOnWidthChange`) need restyling. Their display size is measured
// off the container width, and an initially narrow table can become
// scrollable.
if widthChanged {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
if self.configuration.readingWidth == nil {
self.restyleTableParagraphsForWidthChange()
self.restyleWidthDependentParagraphsForWidthChange()
}
self.updateWideTableOverlays()
}
}
}
/// Restyle only table paragraphs via stamped anchor ranges; avoids re-tokenizing the doc.
private func restyleTableParagraphsForWidthChange() {
/// Restyle only width-dependent paragraphs (tables, standalone images) via
/// stamped anchor ranges; avoids re-tokenizing the doc.
private func restyleWidthDependentParagraphsForWidthChange() {
guard let storage = textStorage,
let coord = delegate as? NativeTextViewCoordinator else { return }
var ranges: [NSRange] = []