-
Notifications
You must be signed in to change notification settings - Fork 104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TRY] Optimization Detective debug helper #1759
base: trunk
Are you sure you want to change the base?
Changes from all commits
fc95f4a
651577e
ff3f120
9dd8159
12a3494
be0be49
e9bbe99
ac4d153
098191c
9288025
2f507a0
8ead9ae
ad09dea
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
<?php | ||
/** | ||
* Image Prioritizer: IP_Img_Tag_Visitor class | ||
* | ||
* @package image-prioritizer | ||
* @since n.e.x.t | ||
*/ | ||
|
||
// Exit if accessed directly. | ||
if ( ! defined( 'ABSPATH' ) ) { | ||
exit; | ||
} | ||
|
||
/** | ||
* Tag visitor that optimizes IMG tags. | ||
* | ||
* @phpstan-import-type LinkAttributes from OD_Link_Collection | ||
* | ||
* @since n.e.x.t | ||
* @access private | ||
*/ | ||
final class Optimization_Detective_Debug_Tag_Visitor { | ||
|
||
/** | ||
* Visits a tag. | ||
* | ||
* This tag visitor doesn't itself request elements to be tracked in URL Metrics, but will reuse tracking that other tag visitors have opted-in to. | ||
* | ||
* @since n.e.x.t | ||
* | ||
* @param OD_Tag_Visitor_Context $context Tag visitor context. | ||
* @return false Always returns false. | ||
*/ | ||
public function __invoke( OD_Tag_Visitor_Context $context ): bool { | ||
$processor = $context->processor; | ||
|
||
if ( ! $context->url_metric_group_collection->is_any_group_populated() ) { | ||
return false; | ||
} | ||
|
||
$xpath = $processor->get_xpath(); | ||
|
||
foreach ( $context->url_metric_group_collection as $group ) { | ||
// This is the LCP element for this group. | ||
if ( $group->get_lcp_element() instanceof OD_Element && $xpath === $group->get_lcp_element()->get_xpath() ) { | ||
$uuid = wp_generate_uuid4(); | ||
|
||
$processor->set_meta_attribute( | ||
'viewport', | ||
(string) $group->get_minimum_viewport_width() | ||
); | ||
|
||
$style = $processor->get_attribute( 'style' ); | ||
$style = is_string( $style ) ? $style : ''; | ||
$processor->set_attribute( | ||
'style', | ||
"--anchor-name: --od-debug-element-$uuid;" . $style | ||
); | ||
|
||
$processor->set_meta_attribute( | ||
'debug-is-lcp', | ||
true | ||
); | ||
|
||
$anchor_text = __( 'Optimization Detective', 'optimization-detective' ); | ||
$popover_text = __( 'LCP Element', 'optimization-detective' ); | ||
|
||
$processor->append_body_html( | ||
<<<HTML | ||
<button | ||
class="od-debug-dot" | ||
type="button" | ||
popovertarget="od-debug-popover-$uuid" | ||
popovertargetaction="toggle" | ||
style="--anchor-name: --od-debug-dot-$uuid; position-anchor: --od-debug-element-$uuid;" | ||
aria-details="od-debug-popover-$uuid" | ||
> | ||
$anchor_text | ||
</button> | ||
<div | ||
id="od-debug-popover-$uuid" | ||
popover | ||
class="od-debug-popover" | ||
style="position-anchor: --od-debug-dot-$uuid;" | ||
> | ||
$popover_text | ||
</div> | ||
HTML | ||
); | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,214 @@ | ||||||||||||||||||||||||||||||||||||||||||||
<?php | ||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||
* Debug helpers used for Optimization Detective. | ||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||
* @package optimization-detective | ||||||||||||||||||||||||||||||||||||||||||||
* @since n.e.x.t | ||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
if ( ! defined( 'ABSPATH' ) ) { | ||||||||||||||||||||||||||||||||||||||||||||
exit; // Exit if accessed directly. | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||
* Registers tag visitors. | ||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||
* @since n.e.x.t | ||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||
* @param OD_Tag_Visitor_Registry $registry Tag visitor registry. | ||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||
function od_debug_register_tag_visitors( OD_Tag_Visitor_Registry $registry ): void { | ||||||||||||||||||||||||||||||||||||||||||||
$debug_visitor = new Optimization_Detective_Debug_Tag_Visitor(); | ||||||||||||||||||||||||||||||||||||||||||||
$registry->register( 'optimization-detective/debug', $debug_visitor ); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
add_action( 'od_register_tag_visitors', 'od_debug_register_tag_visitors', PHP_INT_MAX ); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||
* Filters additional properties for the element item schema for Optimization Detective. | ||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||
* @since n.e.x.t | ||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||
* @param array<string, array{type: string}> $additional_properties Additional properties. | ||||||||||||||||||||||||||||||||||||||||||||
* @return array<string, array{type: string}> Additional properties. | ||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||
function od_debug_add_inp_schema_properties( array $additional_properties ): array { | ||||||||||||||||||||||||||||||||||||||||||||
$additional_properties['inpData'] = array( | ||||||||||||||||||||||||||||||||||||||||||||
'description' => __( 'INP metrics', 'optimization-detective' ), | ||||||||||||||||||||||||||||||||||||||||||||
'type' => 'array', | ||||||||||||||||||||||||||||||||||||||||||||
/* | ||||||||||||||||||||||||||||||||||||||||||||
* All extended properties must be optional so that URL Metrics are not all immediately invalidated once an extension is deactivated. | ||||||||||||||||||||||||||||||||||||||||||||
* Also, no INP data will be sent if the user never interacted with the page. | ||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||
'required' => false, | ||||||||||||||||||||||||||||||||||||||||||||
'items' => array( | ||||||||||||||||||||||||||||||||||||||||||||
'type' => 'object', | ||||||||||||||||||||||||||||||||||||||||||||
'required' => true, | ||||||||||||||||||||||||||||||||||||||||||||
'properties' => array( | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll want to include the LoAF data here at some point. |
||||||||||||||||||||||||||||||||||||||||||||
'value' => array( | ||||||||||||||||||||||||||||||||||||||||||||
'type' => 'number', | ||||||||||||||||||||||||||||||||||||||||||||
'required' => true, | ||||||||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||||||||
'rating' => array( | ||||||||||||||||||||||||||||||||||||||||||||
'type' => 'string', | ||||||||||||||||||||||||||||||||||||||||||||
'enum' => array( 'good', 'needs-improvement', 'poor' ), | ||||||||||||||||||||||||||||||||||||||||||||
'required' => true, | ||||||||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||||||||
'interactionTarget' => array( | ||||||||||||||||||||||||||||||||||||||||||||
'type' => 'string', | ||||||||||||||||||||||||||||||||||||||||||||
'required' => true, | ||||||||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||
return $additional_properties; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
add_filter( 'od_url_metric_schema_root_additional_properties', 'od_debug_add_inp_schema_properties' ); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||
* Adds a new admin bar menu item for Optimization Detective debug mode. | ||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||
* @since n.e.x.t | ||||||||||||||||||||||||||||||||||||||||||||
* | ||||||||||||||||||||||||||||||||||||||||||||
* @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance, passed by reference. | ||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||
function od_debug_add_admin_bar_menu_item( WP_Admin_Bar &$wp_admin_bar ): void { | ||||||||||||||||||||||||||||||||||||||||||||
if ( ! current_user_can( 'customize' ) && ! wp_is_development_mode( 'plugin' ) ) { | ||||||||||||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
if ( is_admin() ) { | ||||||||||||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
$wp_admin_bar->add_menu( | ||||||||||||||||||||||||||||||||||||||||||||
array( | ||||||||||||||||||||||||||||||||||||||||||||
'id' => 'optimization-detective-debug', | ||||||||||||||||||||||||||||||||||||||||||||
'parent' => null, | ||||||||||||||||||||||||||||||||||||||||||||
'group' => null, | ||||||||||||||||||||||||||||||||||||||||||||
'title' => __( 'Optimization Detective', 'optimization-detective' ), | ||||||||||||||||||||||||||||||||||||||||||||
'meta' => array( | ||||||||||||||||||||||||||||||||||||||||||||
'onclick' => 'document.body.classList.toggle("od-debug");', | ||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. O invoker commands, where art thou? |
||||||||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
add_action( 'admin_bar_menu', 'od_debug_add_admin_bar_menu_item', 100 ); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||
* Adds inline JS & CSS for debugging. | ||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||
function od_debug_add_assets(): void { | ||||||||||||||||||||||||||||||||||||||||||||
if ( ! od_can_optimize_response() ) { | ||||||||||||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+105
to
+107
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since pages aren't optimized by default for users who can Otherwise, URL Metrics are collected for logged-in users but they are stored discretely from those stored for logged-out users. See the performance/plugins/optimization-detective/storage/data.php Lines 66 to 86 in c446d8c
I'm not entirely happy with how I "abused" the query vars in this way. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, well, if we want to access the URL Metrics for unauthenticated requests then we just need to unset the The remaining issue is what to do about the XPaths from the unauthenticated URL Metrics not applying to the current authenticated response (e.g. due to the admin bar). I think we may need an alternative to how we currently check if the current XPath via Note the slight difference in the index of the - /*[1][self::HTML]/*[2][self::BODY]/*[1][self::DIV]
+ /*[1][self::HTML]/*[2][self::BODY]/*[2][self::DIV] Maybe we should have a helper that strips out the index from elements which appear as direct children of
Or more simply:
Then this would match the |
||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
$slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); | ||||||||||||||||||||||||||||||||||||||||||||
$post = OD_URL_Metrics_Post_Type::get_post( $slug ); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
global $wp_the_query; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
$tag_visitor_registry = new OD_Tag_Visitor_Registry(); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
$current_etag = od_get_current_url_metrics_etag( $tag_visitor_registry, $wp_the_query, od_get_current_theme_template() ); | ||||||||||||||||||||||||||||||||||||||||||||
$group_collection = new OD_URL_Metric_Group_Collection( | ||||||||||||||||||||||||||||||||||||||||||||
$post instanceof WP_Post ? OD_URL_Metrics_Post_Type::get_url_metrics_from_post( $post ) : array(), | ||||||||||||||||||||||||||||||||||||||||||||
$current_etag, | ||||||||||||||||||||||||||||||||||||||||||||
od_get_breakpoint_max_widths(), | ||||||||||||||||||||||||||||||||||||||||||||
od_get_url_metrics_breakpoint_sample_size(), | ||||||||||||||||||||||||||||||||||||||||||||
od_get_url_metric_freshness_ttl() | ||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+109
to
+123
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this code is duplicated with what is located in |
||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
$inp_dots = array(); | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
foreach ( $group_collection as $group ) { | ||||||||||||||||||||||||||||||||||||||||||||
foreach ( $group as $url_metric ) { | ||||||||||||||||||||||||||||||||||||||||||||
foreach ( $url_metric->get( 'inpData' ) as $inp_data ) { | ||||||||||||||||||||||||||||||||||||||||||||
if ( isset( $inp_dots[ $inp_data['interactionTarget'] ] ) ) { | ||||||||||||||||||||||||||||||||||||||||||||
$inp_dots[ $inp_data['interactionTarget'] ][] = $inp_data; | ||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||
$inp_dots[ $inp_data['interactionTarget'] ] = array( $inp_data ); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
?> | ||||||||||||||||||||||||||||||||||||||||||||
<script> | ||||||||||||||||||||||||||||||||||||||||||||
/* TODO: Add INP elements here */ | ||||||||||||||||||||||||||||||||||||||||||||
let count = 0; | ||||||||||||||||||||||||||||||||||||||||||||
for ( const [ interactionTarget, entries ] of Object.entries( <?php echo wp_json_encode( $inp_dots ); ?> ) ) { | ||||||||||||||||||||||||||||||||||||||||||||
const el = document.querySelector( interactionTarget ); | ||||||||||||||||||||||||||||||||||||||||||||
if ( ! el ) { | ||||||||||||||||||||||||||||||||||||||||||||
continue; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
count++; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const anchor = document.createElement( 'button' ); | ||||||||||||||||||||||||||||||||||||||||||||
anchor.setAttribute( 'class', 'od-debug-dot' ); | ||||||||||||||||||||||||||||||||||||||||||||
anchor.setAttribute( 'popovertarget', `od-debug-popover-${count}` ); | ||||||||||||||||||||||||||||||||||||||||||||
anchor.setAttribute( 'popovertargetaction', 'toggle' ); | ||||||||||||||||||||||||||||||||||||||||||||
anchor.setAttribute( 'style', `--anchor-name: --od-debug-dot-${count}; position-anchor: --od-debug-element-${count};` ); | ||||||||||||||||||||||||||||||||||||||||||||
anchor.setAttribute( 'aria-details', `od-debug-popover-${count}` ); | ||||||||||||||||||||||||||||||||||||||||||||
anchor.textContent = 'Optimization Detective'; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
const tooltip = document.createElement( 'div' ); | ||||||||||||||||||||||||||||||||||||||||||||
tooltip.setAttribute( 'id', `od-debug-popover-${count}` ); | ||||||||||||||||||||||||||||||||||||||||||||
tooltip.setAttribute( 'popover', '' ); | ||||||||||||||||||||||||||||||||||||||||||||
tooltip.setAttribute( 'class', 'od-debug-popover' ); | ||||||||||||||||||||||||||||||||||||||||||||
tooltip.setAttribute( 'style', `position-anchor: --od-debug-dot-${count};` ); | ||||||||||||||||||||||||||||||||||||||||||||
tooltip.textContent = `INP Element (Value: ${entries[0].value}) (Rating: ${entries[0].rating})`; | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
document.body.append(anchor); | ||||||||||||||||||||||||||||||||||||||||||||
document.body.append(tooltip); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
</script> | ||||||||||||||||||||||||||||||||||||||||||||
<style> | ||||||||||||||||||||||||||||||||||||||||||||
body:not(.od-debug) .od-debug-dot, | ||||||||||||||||||||||||||||||||||||||||||||
body:not(.od-debug) .od-debug-popover { | ||||||||||||||||||||||||||||||||||||||||||||
/*display: none;*/ | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
.od-debug-dot { | ||||||||||||||||||||||||||||||||||||||||||||
height: 2em; | ||||||||||||||||||||||||||||||||||||||||||||
width: 2em; | ||||||||||||||||||||||||||||||||||||||||||||
background: rebeccapurple; | ||||||||||||||||||||||||||||||||||||||||||||
border-radius: 50%; | ||||||||||||||||||||||||||||||||||||||||||||
animation: pulse 2s infinite; | ||||||||||||||||||||||||||||||||||||||||||||
position: absolute; | ||||||||||||||||||||||||||||||||||||||||||||
position-area: center center; | ||||||||||||||||||||||||||||||||||||||||||||
margin: 5px 0 0 5px; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
.od-debug-popover { | ||||||||||||||||||||||||||||||||||||||||||||
position: absolute; | ||||||||||||||||||||||||||||||||||||||||||||
position-area: right; | ||||||||||||||||||||||||||||||||||||||||||||
margin: 5px 0 0 5px; | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
@keyframes pulse { | ||||||||||||||||||||||||||||||||||||||||||||
0% { | ||||||||||||||||||||||||||||||||||||||||||||
transform: scale(0.8); | ||||||||||||||||||||||||||||||||||||||||||||
opacity: 0.5; | ||||||||||||||||||||||||||||||||||||||||||||
box-shadow: 0 0 0 0 rgba(102, 51, 153, 0.7); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
70% { | ||||||||||||||||||||||||||||||||||||||||||||
transform: scale(1); | ||||||||||||||||||||||||||||||||||||||||||||
opacity: 1; | ||||||||||||||||||||||||||||||||||||||||||||
box-shadow: 0 0 0 10px rgba(102, 51, 153, 0); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
100% { | ||||||||||||||||||||||||||||||||||||||||||||
transform: scale(0.8); | ||||||||||||||||||||||||||||||||||||||||||||
opacity: 0.5; | ||||||||||||||||||||||||||||||||||||||||||||
box-shadow: 0 0 0 0 rgba(102, 51, 153, 0); | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
</style> | ||||||||||||||||||||||||||||||||||||||||||||
<?php | ||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||
add_action( 'wp_footer', 'od_debug_add_assets' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So cool!