Optimize DynamicPageSurfaceView creation and extract into focused files#23276
Conversation
…(LUM-566) - Share a single WKProcessPool across all DynamicPageSurfaceView instances to avoid spawning a new web content process per creation (~30-40ms saving) - Pre-build static WKUserScript objects for design system CSS, widget JS, and edit animator to avoid redundant string processing per instance - Cache compiled WKContentRuleList for sandbox mode so rule compilation happens once instead of per-creation - Replace if/else version history toggle with opacity/hit-testing to preserve WKWebView across history panel transitions - Extract Coordinator into DynamicPageSurfaceCoordinator.swift (~478 lines) - Extract DynamicWorkspaceWrapper into its own file (~370 lines) Expected improvement: 58ms -> ~10-15ms (under 16.67ms 60fps budget) Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
⚙️ Control Options:
|
LUM-566 DynamicPageSurfaceView.update 58ms creation cost — slowest single event in trace
TL;DR
DoD:
Recommendation: Reduce or defer the creation cost so it doesn't block the main thread during navigation — whether through lazy initialization, pre-warming, or shared resource pooling. Instruments Trace DataEvent Details
Co-occurring Events in Same Row
Position in Overall Trace
Files
References
Triaged by Devin and Kite. Recommendations are based on automated investigation — feel free to diverge based on your own findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 88070b62f0
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…g nil Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
… add missing import, remove deprecated WKProcessPool, add main-thread dispatch for sandbox cache Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
The publish error banner used systemNegativeStrong for both text and background (at 0.8 opacity), making the text unreadable. Changed the background to systemNegativeWeak — the semantic weak variant designed for error backgrounds (light pink in light mode, dark brown in dark mode) — matching the pattern used by VBadge for error states. Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
…eadcrumbs Co-Authored-By: ashlee@vellum.ai <ashlee@vellum.ai>
Why
Fixes LUM-566:
DynamicPageSurfaceView.makeNSView()takes 58.15ms — 3.5× over the 16.67ms budget for 60fps rendering. The cost comes from redundant work on every WKWebView creation: re-parsing and allocating identicalWKUserScriptobjects (~5-10ms) and recompiling the sandbox content rule list from JSON (~3-5ms). Additionally, the version history toggle destroyed and recreated the WKWebView on every open/close, triggering the full creation cost unnecessarily.The file (
DynamicPageSurfaceView.swift) had also grown to ~1,100 lines, mixing view lifecycle, coordinator logic, and workspace UI — making it difficult to navigate and maintain.What Changed
Pre-built static
WKUserScriptobjects — Design system CSS, widget JS, and edit animator scripts are now built once as static properties and reused across all WKWebView instances, eliminating per-creation string processing and allocation.Cached sandbox
WKContentRuleList— The sandbox content rule list is compiled once on first access and cached. On compilation failure, the cache stays empty so subsequent surfaces retry rather than permanently losing network-blocking rules. The completion handler dispatches to the main thread to ensure thread-safe writes to the static cache.Version history toggle preservation — Changed from
if/else(which destroyed the WKWebView) toopacity/allowsHitTesting, keeping the view alive across version history transitions.File splitting — Extracted
Coordinator(~478 lines) intoDynamicPageSurfaceView+Coordinator.swiftandDynamicWorkspaceWrapper+ supporting views (~370 lines) intoDynamicWorkspaceWrapper.swift, following theTypeName+Purpose.swiftnaming convention.Error banner readability — Fixed a pre-existing bug where the publish error banner paired
VColor.systemNegativeStrongforeground withVColor.systemNegativeStrong.opacity(0.8)background (red on red). Changed background toVColor.systemNegativeWeak, matching the strong-text / weak-background pattern used byVBadgefor error states.Benefits
WKWebViewConfigurationinitialization, which cannot be reduced further without pooling/reusing WKWebView instances.nil. Static cache writes are thread-safe viaDispatchQueue.main.async.Safety
DynamicPageSurfaceViewinit,Coordinatorinterface,DynamicWorkspaceWrapperview) are unchanged.DynamicPageSurfaceViewand its workspace wrapper are affected.InlineVideoWebView,OffscreenPreviewCapture, andDocumentEditorVieware intentionally left unchanged.WKUserScriptreuse is safe:WKUserScriptis a value-like configuration object per WebKit's design — sharing instances acrossWKUserContentControllers is the intended usage pattern.References
makeNSViewis called once per view lifetime;updateNSViewhandles subsequent changesReview & Testing Checklist for Human
extension DynamicPageSurfaceView { class Coordinator }in a separate file — confirm the compiler resolves it correctly.DynamicPageSurfaceView.makeNSViewto measure improvement from pre-built scripts + cached rules.Notes
VColor.systemNegativeWeak(#F7DAC9light /#4E281Ddark) as defined inColorTokens.swift._cachedSandboxRuleList/_sandboxRuleListCompiledvars are read and written exclusively on the main thread (reads inmakeNSView, writes inDispatchQueue.main.asyncinside the WebKit completion handler).Link to Devin session: https://app.devin.ai/sessions/f662831a9225486aab6a54f718f13608
Requested by: @ashleeradka