post_content, self::TAG ) ) {
return;
}
wp_register_script(
'sugarcalc-disclaimer-init',
false,
array(),
Plugin::version(),
false
);
wp_enqueue_script( 'sugarcalc-disclaimer-init' );
wp_add_inline_script( 'sugarcalc-disclaimer-init', "if(sessionStorage.getItem('sugarCalcDisclaimerAccepted')==='true'){document.documentElement.classList.add('sugarcalc-accepted');}" );
}
/**
* Render the shortcode output.
*
* @param mixed $atts Shortcode attributes (unused — SugarCalc takes no attributes).
* @return string HTML output.
*/
public static function render( mixed $atts ) : string {
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found -- Required WordPress shortcode callback signature.
// ponytail: one converter per page, because app.js binds fixed ids (#00054).
// True multi-instance support belongs with shortcode parameters (#00046).
if ( self::$rendered ) {
return ( current_user_can( 'edit_posts' ) ? '
' . esc_html__( 'SugarCalc: only one converter per page is supported. This notice is shown to editors only.', 'sugarcalc' ) . '
' : '' );
}
self::$rendered = true;
self::enqueue_assets();
$panel_texts = PanelTexts::get_content();
ob_start();
include SUGARCALC_DIR . 'templates/home.php';
include SUGARCALC_DIR . 'templates/converter-workspace.php';
$content = (string) ob_get_clean();
$container_class = 'sugarcalc-container';
return '' . $content . '
';
}
/**
* Enqueue CSS, JS, and inline data scripts on first shortcode render per request.
*/
private static function enqueue_assets() : void {
if ( self::$assets_enqueued ) {
return;
}
self::$assets_enqueued = true;
$base_url = SUGARCALC_URL . 'assets/';
wp_enqueue_style(
'sugarcalc-variables',
$base_url . 'css/sugarcalc-variables.css',
array(),
self::asset_version( 'assets/css/sugarcalc-variables.css' )
);
wp_enqueue_style(
'sugarcalc-main',
$base_url . 'css/sugarcalc-main.css',
array('sugarcalc-variables'),
self::asset_version( 'assets/css/sugarcalc-main.css' )
);
wp_enqueue_style(
'sugarcalc-components',
$base_url . 'css/sugarcalc-components.css',
array('sugarcalc-main'),
self::asset_version( 'assets/css/sugarcalc-components.css' )
);
wp_enqueue_script(
'sugarcalc-app',
$base_url . 'js/app.js',
array(),
self::asset_version( 'assets/js/app.js' ),
true
);
$thresholds = Thresholds::get();
wp_add_inline_script( 'sugarcalc-app', 'window.sugarcalcThresholds = ' . wp_json_encode( self::format_thresholds_for_js( $thresholds ) ) . ';', 'before' );
wp_add_inline_script( 'sugarcalc-app', 'window.sugarcalcPanelTexts = ' . wp_json_encode( PanelTexts::get_content() ) . ';', 'before' );
add_filter(
'script_loader_tag',
array(self::class, 'add_module_type'),
10,
2
);
}
/**
* Resolve the cache-busting version query for a plugin asset.
*
* Order of precedence (see compose_asset_version() for the pure logic):
* 1. SUGARCALC_BUILD_ID constant — unique per internal build, so every
* build fetches fresh even when the semver version string is unchanged.
* 2. File mtime under WP_DEBUG — busts in-place dev edits between builds.
* 3. Plain plugin version — clean semver for production releases.
*
* @param string $relative Asset path relative to the plugin root (e.g. 'assets/js/app.js').
* @return string Version string suitable for wp_enqueue_* $ver.
*/
private static function asset_version( string $relative ) : string {
$build_id = ( defined( 'SUGARCALC_BUILD_ID' ) && '' !== (string) SUGARCALC_BUILD_ID ? (string) SUGARCALC_BUILD_ID : null );
$file = SUGARCALC_DIR . $relative;
$mtime = ( is_readable( $file ) ? filemtime( $file ) : false );
$debug = defined( 'WP_DEBUG' ) && WP_DEBUG;
return self::compose_asset_version(
Plugin::version(),
$build_id,
( false === $mtime ? null : $mtime ),
$debug
);
}
/**
* Pure composition of an asset version string — no WordPress, unit-testable.
*
* @param string $base Base plugin version (clean semver).
* @param string|null $build_id Build identifier, or null/empty when none.
* @param int|null $mtime File modification time, or null when unavailable.
* @param bool $debug Whether WP_DEBUG is active.
* @return string Composed version string.
*/
public static function compose_asset_version(
string $base,
?string $build_id,
?int $mtime,
bool $debug
) : string {
if ( null !== $build_id && '' !== $build_id ) {
return $base . '+' . $build_id;
}
if ( $debug && null !== $mtime ) {
return $base . '.' . (string) $mtime;
}
return $base;
}
/**
* Add type="module" to the sugarcalc-app script tag.
*
* @param string $tag The script HTML tag.
* @param string $handle The registered script handle.
* @return string Modified tag.
*/
public static function add_module_type( string $tag, string $handle ) : string {
if ( 'sugarcalc-app' === $handle ) {
return str_replace( '