Progressive blur under fixed chrome (real variable blur like Craft) #32

Open
opened 2026-09-15 17:37:16 +00:00 by kayg · 1 comment
Owner

Request (user, 2026-09-15)

The progressive-blur edge ('glur') was never verified in the app; Craft ships a real progressive (variable) blur under its fixed chrome although the API is private. Calternal is self-distributed (AGPL), so using CoreAnimation's private variable blur is acceptable.

Decision

Implement one shared ProgressiveBlurEdgeView (AppKit, NSView-backed): a CALayer with CAFilter type variableBlur created through KVC (filterWithType:), inputRadius ~ 18, inputMaskImage = a vertical gradient CGImage (opaque at the chrome edge → clear over the reach; asymmetric per the progressive-blur-edge skill), inputNormalizeEdges true; runtime-checked — if the filter class/type is unavailable, fall back to the existing alpha-mask dissolve. Apply it under every fixed chrome where content scrolls beneath: sidebar header (Timeline/Analytics) top edge and the sidebar bottom/footer, the search capsule's result list edges, the editor's floating toolbar row. One implementation, every front.

Acceptance

  • VM screenshots light AND dark, over bright busy content (the A0 note with the colour-bar images, and the timeline), sidebar scrolled so rows pass under the header: rows visibly blur progressively before the header, no hard line, no banding; side-by-side with Craft's header blur on the VM.
  • Scroll-spam gate: key-repeat scroll under the blurred edge stays at frame rate (Release sample: no per-frame mask regeneration; the mask image is built once per size).
  • cua script Scripts/vm/progressive_blur_check.py: scroll, capture the header band, assert a monotonic blur/contrast gradient across the band (row edge sharpness decreasing toward the chrome) rather than a step.
## Request (user, 2026-09-15) The progressive-blur edge ('glur') was never verified in the app; Craft ships a real progressive (variable) blur under its fixed chrome although the API is private. Calternal is self-distributed (AGPL), so using CoreAnimation's private variable blur is acceptable. ## Decision Implement one shared `ProgressiveBlurEdgeView` (AppKit, NSView-backed): a `CALayer` with `CAFilter` type `variableBlur` created through KVC (`filterWithType:`), `inputRadius` ~ 18, `inputMaskImage` = a vertical gradient CGImage (opaque at the chrome edge → clear over the reach; asymmetric per the progressive-blur-edge skill), `inputNormalizeEdges` true; runtime-checked — if the filter class/type is unavailable, fall back to the existing alpha-mask dissolve. Apply it under every fixed chrome where content scrolls beneath: sidebar header (Timeline/Analytics) top edge and the sidebar bottom/footer, the search capsule's result list edges, the editor's floating toolbar row. One implementation, every front. ## Acceptance - VM screenshots light AND dark, over bright busy content (the A0 note with the colour-bar images, and the timeline), sidebar scrolled so rows pass under the header: rows visibly blur progressively before the header, no hard line, no banding; side-by-side with Craft's header blur on the VM. - Scroll-spam gate: key-repeat scroll under the blurred edge stays at frame rate (Release sample: no per-frame mask regeneration; the mask image is built once per size). - cua script `Scripts/vm/progressive_blur_check.py`: scroll, capture the header band, assert a monotonic blur/contrast gradient across the band (row edge sharpness decreasing toward the chrome) rather than a step.
Author
Owner

Overnight (Main): wave19/progressive-blur @ 4553333a merged into desktop-test (255df592). Shipping path is mailternal's mailWindowDissolve alpha mask (shared interface); the CABackdropLayer variableBlur attempt is behind CALTERNAL_BACKDROP_BLUR=1 because the probe never proved the mask is honoured (blurs uniformly + shows a material plate). VM dark evidence on the merged build: rows fade under the header and under the bottom Space bar, no black band (/tmp/vm/dt6-mid.png, /tmp/progressive-blur-evidence/sidebar-fallback-dark.png). Real progressive blur remains open.

Overnight (Main): `wave19/progressive-blur` @ 4553333a merged into desktop-test (255df592). Shipping path is mailternal's `mailWindowDissolve` alpha mask (shared interface); the CABackdropLayer `variableBlur` attempt is behind `CALTERNAL_BACKDROP_BLUR=1` because the probe never proved the mask is honoured (blurs uniformly + shows a material plate). VM dark evidence on the merged build: rows fade under the header and under the bottom Space bar, no black band (/tmp/vm/dt6-mid.png, /tmp/progressive-blur-evidence/sidebar-fallback-dark.png). Real progressive blur remains open.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal-macos#32
No description provided.