Skip to content

Conversation

kaeizen
Copy link
Contributor

@kaeizen kaeizen commented Oct 1, 2025

Summary by CodeRabbit

  • Performance

    • Improved preview scheduling using browser idle callbacks with per-item timeouts and memoized preview sizing to reduce redundant recalculations.
    • Defers expensive recalculations until preview styles finish loading for faster initial render.
  • Bug Fixes

    • More reliable scaling after styles load, reducing layout jumps and flicker.
    • Prevents duplicate style injection into previews.
  • Style

    • Spinner displays with a white background and correct stacking order.

- add timeout to requestIdleCallback
- scale on styles load
Copy link

coderabbitai bot commented Oct 1, 2025

Warning

Rate limit exceeded

@kaeizen has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 6 minutes and 41 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 92178b3 and a9b3ae6.

📒 Files selected for processing (1)
  • src/components/design-library-list/index.js (1 hunks)

Walkthrough

Tracks stylesheet load progress in the shadow root and passes it to the preview renderer; memoizes preview size in list items; tightens scale recompute logic with width/height guards; prefers requestIdleCallback for deferred previews (5000ms timeout in design-preview, per-item timeout in index); re-enables spinner z-index and background rules.

Changes

Cohort / File(s) Summary
Shadow root style management
src/components/design-library-list/use-shadow-root.js
Adds stylesLoaded state; resets on render; deduplicates style/link nodes via existingIds; injects global and design-library styles only if missing; converts :root:host; wires onload/onerror to increment stylesLoaded; returns { hostRef, shadowRoot, stylesLoaded }.
Preview renderer / scaling
src/components/design-library-list/use-preview-renderer.js
Adds stylesLoaded parameter; refactors adjustScale(force = 2, callCount = 0); adds isEqualWidth/isEqualHeight checks and a recompute guard to skip redundant shadow-DOM updates; updates RAF scheduling to pass (force, ++callCount); triggers recompute when stylesLoaded changes.
List item integration & sizing
src/components/design-library-list/design-library-list-item.js
Imports useMemo; replaces inline getDesignPreviewSize() with memoized designPreviewSize and uses it for container height/transform and class logic; passes stylesLoaded from useShadowRoot into usePreviewRenderer.
Deferred preview scheduling (design-preview)
src/components/design-library-list/design-preview.js
Replaces setTimeout-only scheduling with requestIdleCallback when available using a { timeout: 5000 } option; falls back to setTimeout(designIndex * 20) otherwise.
Deferred preview scheduling (index)
src/components/design-library-list/index.js
Uses requestIdleCallback with a per-item { timeout: (designIndex + 1) * 500 } option when available; preserves setTimeout fallback.
Spinner styling
src/components/design-library-list/editor.scss
Uncomments and enables background-color: #fff and z-index: 1 inside .stk-spinner; enables z-index: -1 under .stk-hide-spinner, making those rules active.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Item as DesignLibraryListItem
  participant SR as useShadowRoot
  participant PR as usePreviewRenderer
  participant DOM as Shadow DOM

  rect rgba(235,245,255,0.6)
    Note over Item: Mount
    Item->>SR: create shadow root, inject styles
    SR-->>Item: { hostRef, shadowRoot, stylesLoaded }
    Item->>PR: init renderer(shadowRoot, designPreviewSize, stylesLoaded)
  end

  rect rgba(240,255,240,0.6)
    Note over SR,DOM: Style injection & load tracking
    SR->>DOM: append style/link nodes (skip duplicates)
    DOM-->>SR: onload/onerror → increment stylesLoaded
    SR-->>Item: stylesLoaded updates
    Item->>PR: stylesLoaded changed
    PR->>PR: adjustScale(force, callCount) — check isEqualWidth/isEqualHeight
    alt recompute == true
      PR->>DOM: apply scale updates
    else
      PR-->>PR: skip DOM update
    end
  end
Loading
sequenceDiagram
  autonumber
  participant DP as DesignPreview
  participant IDX as IndexScheduler
  participant Browser as Window

  Note over DP,IDX: Schedule deferred preview work
  DP->>Browser: requestIdleCallback(callback, { timeout: 5000 }) or fallback setTimeout(designIndex * 20)
  IDX->>Browser: requestIdleCallback(callback, { timeout: (designIndex + 1) * 500 }) or fallback setTimeout(...)
  Browser-->>DP: idle callback / timeout fired
  Browser-->>IDX: idle callback / timeout fired
  DP-->>DP: run deferred render
  IDX-->>IDX: mark shouldRender true for item
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

I nibbled at styles under moonlit dom,
Counted loaded links until they were calm.
Idle callbacks hum, timeouts in tow,
Previews scale snug where rabbit feet go.
The spinner gleams — hop, the render’s a-go! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title “fix (design library): preview fixes” is overly generic and does not clearly convey the specific improvements made to the design library preview, such as memoizing the preview size, enhancing idle callback logic, and tracking style loading in the shadow root. Please revise the title to succinctly highlight the main changes, for example “fix(design library): memoize preview size and improve idle callback and style loading in preview renderer.”
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

github-actions bot commented Oct 1, 2025

🤖 Pull request artifacts

file commit
pr3606-stackable-3606-merge.zip a9b3ae6

github-actions bot added a commit that referenced this pull request Oct 1, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/components/design-library-list/design-preview.js (1)

96-113: Consider simplifying the nested scheduling.

The code nests requestAnimationFrame and requestIdleCallback, which adds complexity. Since requestIdleCallback already schedules work during idle time, the additional requestAnimationFrame wrapper may be unnecessary unless you specifically need to coordinate with the browser's paint cycle.

Consider this simpler approach:

-if ( selectedTab !== 'pages' || designIndex < 9 ) {
-  // insert HTML for patterns and for the first 9 pages
-  wrapper.innerHTML = sanitizedHTML
-  requestAnimationFrame( () => {
-    ric( () => setIsLoading( false ) )
-  } )
-  return
-}
-
-requestAnimationFrame( () => {
-  ric( () => {
-    wrapper.innerHTML = sanitizedHTML
-    updateShadowBodySize()
-    requestAnimationFrame( () => {
-      ric( () => setIsLoading( false ) )
-    } )
-  } )
-} )
+if ( selectedTab !== 'pages' || designIndex < 9 ) {
+  wrapper.innerHTML = sanitizedHTML
+  ric( () => setIsLoading( false ) )
+  return
+}
+
+ric( () => {
+  wrapper.innerHTML = sanitizedHTML
+  updateShadowBodySize()
+  ric( () => setIsLoading( false ) )
+} )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab1fd2a and 054299c.

📒 Files selected for processing (5)
  • src/components/design-library-list/design-library-list-item.js (6 hunks)
  • src/components/design-library-list/design-preview.js (1 hunks)
  • src/components/design-library-list/editor.scss (1 hunks)
  • src/components/design-library-list/use-preview-renderer.js (6 hunks)
  • src/components/design-library-list/use-shadow-root.js (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-25T08:17:15.543Z
Learnt from: kaeizen
PR: gambitph/Stackable#3601
File: src/components/design-library-list/use-preview-renderer.js:307-331
Timestamp: 2025-09-25T08:17:15.543Z
Learning: In the design library preview renderer hook (src/components/design-library-list/use-preview-renderer.js), selectedTab changes trigger content updates through a chain of effects rather than direct dependencies. The first useEffect (template processing) responds to selectedTab changes indirectly and updates content state, which then triggers the second useEffect (rendering/scaling) that depends on content. This eliminates the need to add selectedTab to the second effect's dependency array.

Applied to files:

  • src/components/design-library-list/use-preview-renderer.js
  • src/components/design-library-list/design-library-list-item.js
🧬 Code graph analysis (3)
src/components/design-library-list/use-shadow-root.js (2)
src/components/design-library-list/util.js (2)
  • getAdditionalStylesForPreview (338-391)
  • getAdditionalStylesForPreview (338-391)
src/components/design-library-list/index.js (1)
  • shouldRender (77-77)
src/components/design-library-list/design-preview.js (1)
src/higher-order/with-block-wrapper-is-hovered/util.js (1)
  • window (8-11)
src/components/design-library-list/use-preview-renderer.js (3)
src/components/design-library-list/design-preview.js (1)
  • ref (21-21)
src/components/design-library-list/use-shadow-root.js (3)
  • hostRef (11-11)
  • shadowRoot (12-12)
  • stylesLoaded (13-13)
src/components/design-library-list/index.js (2)
  • selectedTab (79-87)
  • shouldRender (77-77)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: PHP 7.3 and WP 6.5.5
  • GitHub Check: PHP 8.2 and WP latest
  • GitHub Check: PHP 8.2 and WP 6.6.2
  • GitHub Check: PHP 8.2 and WP 6.5.5
  • GitHub Check: PHP 8.2 and WP 6.7.2
  • GitHub Check: PHP 7.3 and WP latest
  • GitHub Check: build
🔇 Additional comments (6)
src/components/design-library-list/editor.scss (1)

249-254: Spinner layering fix looks correct

Re-enabling the white backdrop with z-index: 1 gives the loading overlay proper coverage again, and dropping it to -1 in the hidden state ensures interactions resume immediately after fade-out. Nice tidy alignment with the shadow-root style loading flow.

src/components/design-library-list/design-library-list-item.js (2)

23-23: LGTM! Clean integration of stylesLoaded.

The addition of stylesLoaded from useShadowRoot and its propagation to usePreviewRenderer properly coordinates style readiness with the rendering pipeline.

Also applies to: 47-49, 59-59


65-76: LGTM! Memoization prevents unnecessary recalculations.

The memoized designPreviewSize avoids redundant height calculations and the dependency array correctly captures all values that affect the computation.

src/components/design-library-list/use-shadow-root.js (1)

31-34: LGTM! Duplicate prevention and load tracking well implemented.

The existingIds Set prevents duplicate style injection, and the conditional guards ensure styles are only added once. The load/error handlers correctly track external stylesheets.

Also applies to: 58-72, 74-97

src/components/design-library-list/use-preview-renderer.js (2)

87-148: LGTM! Recompute guard prevents redundant scale updates.

The enhanced adjustScale logic with isEqualWidth and isEqualHeight checks efficiently skips unnecessary scaling when dimensions are already aligned. The force parameter provides a retry mechanism, and callCount prevents infinite loops.


349-359: LGTM! Style-load effect triggers necessary reflow.

The new effect correctly triggers scale adjustment when styles finish loading, ensuring previews render at the correct size once all styles are available. The guards prevent execution when blocks aren't ready.

github-actions bot added a commit that referenced this pull request Oct 1, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/components/design-library-list/design-preview.js (1)

93-94: Verify the scheduler fix addresses the previous concern.

The ternary operator correctly fixes the logical OR issue from the previous review. Both branches now return functions accepting a callback, and the requestIdleCallback timeout option is properly passed.

However, consider explicitly using window.setTimeout for consistency with how window.requestIdleCallback is referenced, and to match patterns seen elsewhere in the codebase (e.g., src/higher-order/with-block-wrapper-is-hovered/util.js).

Optional refactor for explicitness:

-const ric = window.requestIdleCallback ? ( cb => window.requestIdleCallback( cb, { timeout: 1500 } ) )
-	: ( cb => setTimeout( cb, designIndex * 20 ) )
+const ric = window.requestIdleCallback 
+	? ( cb => window.requestIdleCallback( cb, { timeout: 1500 } ) )
+	: ( cb => window.setTimeout( cb, designIndex * 20 ) )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 054299c and c9f3957.

📒 Files selected for processing (1)
  • src/components/design-library-list/design-preview.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/design-library-list/design-preview.js (1)
src/higher-order/with-block-wrapper-is-hovered/util.js (1)
  • window (8-11)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: PHP 8.2 and WP 6.5.5
  • GitHub Check: PHP 8.2 and WP latest
🔇 Additional comments (1)
src/components/design-library-list/design-preview.js (1)

84-115: Unable to verify loading–stylesLoaded coordination automatically
I couldn’t locate how setIsLoading in design-preview.js interacts with the stylesLoaded counter in your preview/render hooks. Please manually confirm that both the immediate‐insertion path (lines 97–104) and the deferred‐insertion path (lines 106–114) don’t race with style loading—i.e., setIsLoading(false) only fires after all required CSS has been appended to the shadow root.

github-actions bot added a commit that referenced this pull request Oct 1, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/components/design-library-list/index.js (1)

132-132: Simplify the idle callback to avoid unnecessary object allocation.

The conditional returns an empty object {} in the else branch. While functional, this is non-idiomatic and allocates an unnecessary object. Consider using a statement block or removing the else clause entirely.

Apply this diff for a cleaner implementation:

-			id = requestIdleCallback( () => ! shouldRender ? setShouldRender( true ) : {}, { timeout: 3000 } )
+			id = requestIdleCallback( () => {
+				if ( ! shouldRender ) {
+					setShouldRender( true )
+				}
+			}, { timeout: 3000 } )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c9f3957 and d722ba3.

📒 Files selected for processing (2)
  • src/components/design-library-list/design-preview.js (1 hunks)
  • src/components/design-library-list/index.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/design-library-list/design-preview.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: PHP 8.2 and WP latest
  • GitHub Check: PHP 8.2 and WP 6.7.2
  • GitHub Check: PHP 8.2 and WP 6.6.2
  • GitHub Check: PHP 7.3 and WP 6.5.5
  • GitHub Check: PHP 8.2 and WP 6.5.5
  • GitHub Check: PHP 7.3 and WP latest

github-actions bot added a commit that referenced this pull request Oct 1, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/components/design-library-list/index.js (1)

126-145: Consider guarding the effect when shouldRender is already true.

When selectedTab switches back to 'pages', this effect schedules an idle callback even if shouldRender is already true. While the callback itself checks ! shouldRender, scheduling unnecessary idle callbacks can still occur.

Consider adding an early return:

 	useEffect( () => {
 		if ( selectedTab !== 'pages' ) {
 			return
 		}
+		if ( shouldRender ) {
+			return
+		}
 		let id
 		if ( typeof requestIdleCallback !== 'undefined' ) {
-			id = requestIdleCallback( () => ! shouldRender ? setShouldRender( true ) : {}, { timeout: ( designIndex + 1 ) * 500 } )
+			id = requestIdleCallback( () => setShouldRender( true ), { timeout: ( designIndex + 1 ) * 500 } )
 		} else {
 			// fallback
-			id = setTimeout( () => setShouldRender( true ), designIndex * 20 )
+			id = setTimeout( () => setShouldRender( true ), ( designIndex + 1 ) * 500 )
 		}
 
 		return () => {
 			if ( typeof cancelIdleCallback !== 'undefined' ) {
 				cancelIdleCallback( id )
 			} else {
 				clearTimeout( id )
 			}
 		}
-	}, [ selectedTab ] )
+	}, [ selectedTab, shouldRender, designIndex ] )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d722ba3 and 92178b3.

📒 Files selected for processing (2)
  • src/components/design-library-list/design-preview.js (1 hunks)
  • src/components/design-library-list/index.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/design-library-list/design-preview.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: PHP 8.2 and WP 6.7.2
  • GitHub Check: PHP 8.2 and WP latest
  • GitHub Check: PHP 8.2 and WP 6.6.2
  • GitHub Check: PHP 7.3 and WP latest
  • GitHub Check: PHP 7.3 and WP 6.5.5
  • GitHub Check: PHP 8.2 and WP 6.5.5
  • GitHub Check: build

github-actions bot added a commit that referenced this pull request Oct 1, 2025
@bfintal bfintal merged commit f5782f6 into develop Oct 1, 2025
8 of 9 checks passed
@bfintal bfintal deleted the fix/design-library-preview-fixes branch October 1, 2025 04:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants