RX Theme folder structure for future use. This combines your original structure with all advanced modules we added step by step: frontend, backend/admin, SEO, performance, ads, app loader, React/Vite dist, accessibility, WooCommerce/Elementor, Disease A-Z, contact/newsletter, and unified plugin-style loader. Your original uploaded structure was the foundation checklist for this final structure.
rx-theme/
├── style.css
├── functions.php
├── index.php
├── screenshot.png
├── readme.txt
│
├── header.php
├── footer.php
├── sidebar.php
├── search.php
├── 404.php
├── archive.php
├── category.php
├── tag.php
├── author.php
├── single.php
├── page.php
├── comments.php
│
├── inc/
│ ├── autoload.php
│ ├── rx-v8-loader.php
│ ├── rx-v83-v84-loader.php
│ ├── rx-v85-v87-loader.php
│ │
│ ├── core/
│ │ ├── theme-setup.php
│ │ ├── enqueue.php
│ │ ├── menus.php
│ │ ├── sidebars.php
│ │ ├── image-sizes.php
│ │ ├── theme-supports.php
│ │ ├── cleanup.php
│ │ ├── security.php
│ │ └── performance.php
│ │
│ ├── helpers/
│ │ ├── options.php
│ │ ├── sanitization.php
│ │ ├── escaping.php
│ │ ├── conditionals.php
│ │ ├── media.php
│ │ ├── formatting.php
│ │ ├── compatibility.php
│ │ └── template-functions.php
│ │
│ ├── frontend/
│ │ ├── template-tags.php
│ │ ├── template-functions.php
│ │ ├── breadcrumbs.php
│ │ ├── pagination.php
│ │ ├── related-posts.php
│ │ ├── reading-time.php
│ │ ├── post-views.php
│ │ ├── author-box.php
│ │ ├── reviewer-box.php
│ │ ├── social-share.php
│ │ ├── social-share-tools.php
│ │ ├── table-of-contents.php
│ │ ├── medical-disclaimer.php
│ │ ├── medical-article.php
│ │ ├── homepage.php
│ │ ├── footer-builder.php
│ │ ├── search-overlay.php
│ │ ├── archive-layout.php
│ │ └── header-mobile-polish.php
│ │
│ ├── backend/
│ │ ├── admin-menu.php
│ │ ├── admin-page.php
│ │ ├── theme-options.php
│ │ ├── settings-api.php
│ │ ├── customizer.php
│ │ ├── metaboxes.php
│ │ ├── dashboard-widgets.php
│ │ ├── admin-notices.php
│ │ └── import-export.php
│ │
│ ├── admin/
│ │ ├── admin-menu.php
│ │ └── settings-register.php
│ │
│ ├── dynamic/
│ │ └── dynamic-css.php
│ │
│ ├── seo/
│ │ ├── seo-loader.php
│ │ ├── seo-helpers.php
│ │ ├── schema-output.php
│ │ ├── schema-article.php
│ │ ├── schema-medical-webpage.php
│ │ ├── schema-medical-condition.php
│ │ ├── schema-drug.php
│ │ ├── schema-faq.php
│ │ ├── schema-breadcrumb.php
│ │ ├── schema-organization.php
│ │ ├── schema-person.php
│ │ ├── schema-website.php
│ │ ├── schema-search-action.php
│ │ ├── open-graph.php
│ │ ├── twitter-card.php
│ │ ├── canonical.php
│ │ └── meta-robots.php
│ │
│ ├── performance/
│ │ ├── performance-loader.php
│ │ ├── preload.php
│ │ ├── preconnect.php
│ │ ├── defer-async.php
│ │ ├── defer-scripts.php
│ │ ├── critical-css.php
│ │ ├── lazy-loading.php
│ │ ├── resource-hints.php
│ │ ├── remove-bloat.php
│ │ ├── emoji-disable.php
│ │ ├── embeds-disable.php
│ │ ├── heartbeat-control.php
│ │ ├── web-vitals.php
│ │ ├── font-optimization.php
│ │ └── cache-headers.php
│ │
│ ├── ads/
│ │ ├── ads-loader.php
│ │ ├── ad-helpers.php
│ │ ├── ad-settings.php
│ │ ├── ad-output.php
│ │ └── ad-shortcodes.php
│ │
│ ├── tools/
│ │ └── import-export-settings.php
│ │
│ ├── customizer/
│ │ └── customizer-support.php
│ │
│ ├── blocks/
│ │ └── gutenberg-medical-blocks.php
│ │
│ ├── medical/
│ │ ├── shortcodes.php
│ │ └── disease-az.php
│ │
│ ├── contact/
│ │ └── contact-newsletter.php
│ │
│ ├── compatibility/
│ │ └── woocommerce-elementor.php
│ │
│ ├── accessibility/
│ │ └── accessibility-tools.php
│ │
│ ├── polish/
│ │ └── final-premium-polish.php
│ │
│ ├── app/
│ │ ├── app-loader.php
│ │ ├── admin-app-loader.php
│ │ ├── frontend-app-loader.php
│ │ ├── dist-assets-loader.php
│ │ ├── dist-assets-manager.php
│ │ ├── unified-loader.php
│ │ ├── unified-app-plugin-loader.php
│ │ └── plugin-analysis-summary.json
│ │
│ ├── rest/
│ │ └── app-rest-api.php
│ │
│ └── integrations/
│ ├── woocommerce.php
│ ├── elementor.php
│ ├── gutenberg.php
│ ├── rankmath.php
│ ├── yoast.php
│ └── contact-form-7.php
│
├── frontend/
│ ├── components/
│ │ ├── site-header.php
│ │ ├── site-footer.php
│ │ ├── nav-primary.php
│ │ ├── nav-mobile.php
│ │ ├── search-form.php
│ │ ├── post-card.php
│ │ ├── author-box.php
│ │ ├── related-post-card.php
│ │ ├── share-buttons.php
│ │ └── ad-slot.php
│ │
│ ├── layouts/
│ │ ├── layout-default.php
│ │ ├── layout-full-width.php
│ │ ├── layout-sidebar-right.php
│ │ ├── layout-sidebar-left.php
│ │ ├── layout-medical-article.php
│ │ └── layout-landing.php
│ │
│ └── parts/
│ ├── content.php
│ ├── content-single.php
│ ├── content-page.php
│ ├── content-search.php
│ ├── content-none.php
│ ├── entry-header.php
│ ├── entry-footer.php
│ └── entry-meta.php
│
├── backend/
│ ├── pages/
│ │ ├── rx-dashboard.php
│ │ ├── rx-general-settings.php
│ │ ├── rx-header-settings.php
│ │ ├── rx-footer-settings.php
│ │ ├── rx-seo-settings.php
│ │ ├── rx-performance-settings.php
│ │ ├── rx-social-settings.php
│ │ └── rx-import-export.php
│ │
│ ├── fields/
│ │ ├── field-text.php
│ │ ├── field-textarea.php
│ │ ├── field-checkbox.php
│ │ ├── field-radio.php
│ │ ├── field-select.php
│ │ ├── field-color.php
│ │ ├── field-image.php
│ │ └── field-repeater.php
│ │
│ └── assets/
│ ├── css/
│ │ └── admin.css
│ ├── js/
│ │ └── admin.js
│ └── images/
│ └── rx-logo.svg
│
├── template-parts/
│ ├── header/
│ │ ├── header-default.php
│ │ ├── header-medical.php
│ │ ├── header-centered.php
│ │ ├── header-transparent.php
│ │ ├── header-sticky.php
│ │ ├── header-mobile.php
│ │ ├── header-topbar.php
│ │ └── mobile-header.php
│ │
│ ├── footer/
│ │ ├── footer-default.php
│ │ ├── footer-widgets.php
│ │ ├── footer-minimal.php
│ │ └── footer-medical.php
│ │
│ ├── content/
│ │ ├── content.php
│ │ ├── content-single.php
│ │ ├── content-page.php
│ │ ├── content-search.php
│ │ └── content-none.php
│ │
│ ├── home/
│ │ ├── hero.php
│ │ ├── search-box.php
│ │ ├── featured-categories.php
│ │ ├── latest-articles.php
│ │ ├── popular-guides.php
│ │ ├── disease-az.php
│ │ ├── doctor-reviewed.php
│ │ ├── faq.php
│ │ └── footer-cta.php
│ │
│ ├── post/
│ │ ├── post-card.php
│ │ ├── post-list.php
│ │ ├── post-grid.php
│ │ ├── post-meta.php
│ │ ├── post-author.php
│ │ └── post-related.php
│ │
│ ├── page/
│ │ ├── page-header.php
│ │ ├── page-content.php
│ │ └── page-builder.php
│ │
│ ├── archive/
│ │ ├── archive-header.php
│ │ ├── archive-toolbar.php
│ │ ├── archive-card.php
│ │ ├── archive-grid.php
│ │ └── no-results.php
│ │
│ └── medical/
│ ├── disease-card.php
│ ├── symptom-box.php
│ ├── treatment-box.php
│ ├── diagnosis-box.php
│ ├── prevention-box.php
│ ├── faq-box.php
│ ├── reviewer-box.php
│ ├── references-box.php
│ └── medical-disclaimer.php
│
├── assets/
│ ├── css/
│ │ ├── frontend/
│ │ │ ├── main.css
│ │ │ ├── header.css
│ │ │ ├── footer.css
│ │ │ ├── blog.css
│ │ │ ├── single.css
│ │ │ ├── archive.css
│ │ │ ├── archive-layout.css
│ │ │ ├── medical.css
│ │ │ ├── homepage.css
│ │ │ ├── disease-az.css
│ │ │ ├── contact-newsletter.css
│ │ │ ├── ad-manager.css
│ │ │ ├── search-menu.css
│ │ │ ├── social-share-tools.css
│ │ │ ├── header-mobile-polish.css
│ │ │ ├── compatibility-polish.css
│ │ │ ├── accessibility.css
│ │ │ ├── accessibility-tools.css
│ │ │ ├── premium-polish.css
│ │ │ └── responsive.css
│ │ │
│ │ ├── backend/
│ │ │ ├── admin.css
│ │ │ ├── settings.css
│ │ │ ├── customizer.css
│ │ │ ├── rx-admin-tools.css
│ │ │ ├── dist-assets-manager.css
│ │ │ └── unified-app-loader-admin.css
│ │ │
│ │ ├── blocks/
│ │ │ └── medical-blocks.css
│ │ │
│ │ ├── dynamic/
│ │ │ ├── variables.css
│ │ │ ├── color-scheme.css
│ │ │ ├── typography.css
│ │ │ └── generated.css
│ │ │
│ │ └── vendor/
│ │ ├── normalize.css
│ │ └── icons.css
│ │
│ ├── js/
│ │ ├── frontend/
│ │ │ ├── main.js
│ │ │ ├── navigation.js
│ │ │ ├── mobile-menu.js
│ │ │ ├── search.js
│ │ │ ├── search-menu.js
│ │ │ ├── accessibility.js
│ │ │ ├── accessibility-tools.js
│ │ │ ├── lazyload.js
│ │ │ ├── toc.js
│ │ │ ├── dark-mode.js
│ │ │ ├── performance.js
│ │ │ ├── social-share-tools.js
│ │ │ ├── header-mobile-polish.js
│ │ │ ├── archive-layout.js
│ │ │ ├── disease-az.js
│ │ │ ├── contact-newsletter.js
│ │ │ └── premium-polish.js
│ │ │
│ │ ├── backend/
│ │ │ ├── admin.js
│ │ │ ├── settings.js
│ │ │ ├── media-upload.js
│ │ │ ├── customizer.js
│ │ │ └── customizer-preview.js
│ │ │
│ │ ├── blocks/
│ │ │ └── medical-blocks.js
│ │ │
│ │ ├── performance/
│ │ │ ├── idle-loader.js
│ │ │ ├── defer-third-party.js
│ │ │ └── web-vitals-monitor.js
│ │ │
│ │ └── vendor/
│ │ └── vendor.js
│ │
│ ├── build/
│ │ ├── admin/
│ │ │ ├── admin-app.js
│ │ │ └── admin-app.css
│ │ └── frontend/
│ │ ├── frontend-app.js
│ │ └── frontend-app.css
│ │
│ ├── app-dist/
│ │ ├── README.md
│ │ ├── rx-dist-manifest-example.json
│ │ ├── chunks/
│ │ │ ├── chunk-01.js
│ │ │ ├── chunk-02.js
│ │ │ └── chunk-50.js
│ │ ├── main-js/
│ │ │ ├── main-01.js
│ │ │ ├── main-02.js
│ │ │ └── main-50.js
│ │ ├── css/
│ │ │ ├── style-01.css
│ │ │ ├── style-02.css
│ │ │ └── style-50.css
│ │ ├── vendor/
│ │ ├── images/
│ │ └── fonts/
│ │
│ ├── app-loader/
│ │ ├── frontend/
│ │ │ ├── css/
│ │ │ │ ├── frontend-01.css
│ │ │ │ └── frontend-50.css
│ │ │ └── js/
│ │ │ ├── frontend-01.js
│ │ │ └── frontend-50.js
│ │ │
│ │ ├── admin/
│ │ │ ├── css/
│ │ │ │ ├── admin-01.css
│ │ │ │ └── admin-50.css
│ │ │ └── js/
│ │ │ ├── admin-01.js
│ │ │ └── admin-50.js
│ │ │
│ │ ├── both/
│ │ │ ├── css/
│ │ │ │ ├── both-01.css
│ │ │ │ └── both-50.css
│ │ │ └── js/
│ │ │ ├── both-01.js
│ │ │ └── both-50.js
│ │ │
│ │ ├── chunks/
│ │ │ ├── chunk-01.js
│ │ │ ├── chunk-02.js
│ │ │ └── chunk-100.js
│ │ ├── vendor/
│ │ └── media/
│ │
│ ├── images/
│ │ ├── logo/
│ │ ├── icons/
│ │ ├── backgrounds/
│ │ └── placeholders/
│ │
│ ├── fonts/
│ │ ├── inter/
│ │ └── rx-icons/
│ │
│ └── static-js/
│ ├── accessibility.js
│ ├── frontend-helpers.js
│ └── performance-helpers.js
│
├── app/
│ ├── README.md
│ │
│ ├── admin-react/
│ │ ├── package.json
│ │ ├── vite.config.js
│ │ ├── src/
│ │ │ ├── main.jsx
│ │ │ └── admin-app.css
│ │ └── dist/
│ │ ├── admin-app.js
│ │ └── admin-app.css
│ │
│ ├── frontend-vite/
│ │ ├── package.json
│ │ ├── vite.config.js
│ │ ├── src/
│ │ │ ├── main.js
│ │ │ └── frontend-app.css
│ │ └── dist/
│ │ ├── frontend-app.js
│ │ └── frontend-app.css
│ │
│ ├── astro-headless-note/
│ │ └── README.md
│ │
│ ├── rust-tools-note/
│ │ └── README.md
│ │
│ └── plugin-reference-readonly/
│ ├── README.md
│ ├── PLUGIN_FILES_LIST.txt
│ └── PLUGIN_HEADER.txt
│
├── templates/
│ ├── template-full-width.php
│ ├── template-left-sidebar.php
│ ├── template-right-sidebar.php
│ ├── template-no-sidebar.php
│ ├── template-medical-article.php
│ ├── template-homepage.php
│ ├── template-landing-page.php
│ ├── template-sitemap.php
│ ├── template-disease-az.php
│ └── template-contact-newsletter.php
│
├── blocks/
│ ├── rx-card/
│ │ ├── block.json
│ │ ├── index.js
│ │ ├── editor.css
│ │ └── style.css
│ │
│ ├── rx-faq/
│ │ ├── block.json
│ │ ├── index.js
│ │ ├── editor.css
│ │ └── style.css
│ │
│ ├── rx-medical-box/
│ │ ├── block.json
│ │ ├── index.js
│ │ ├── editor.css
│ │ └── style.css
│ │
│ └── rx-reviewer-box/
│ ├── block.json
│ ├── index.js
│ ├── editor.css
│ └── style.css
│
├── languages/
│ └── rx-theme.pot
│
└── build/
├── css/
├── js/
└── manifest.json
Simple map for finding things quickly
Theme engine → inc/core/
Reusable PHP helpers → inc/helpers/
Frontend features → inc/frontend/
Backend/admin pages → inc/backend/ and inc/admin/
SEO/schema → inc/seo/
Performance → inc/performance/
Ads/AdSense → inc/ads/
Customizer → inc/customizer/
Import/export → inc/tools/
Gutenberg blocks PHP → inc/blocks/
Medical systems → inc/medical/
Contact/newsletter → inc/contact/
WooCommerce/Elementor → inc/compatibility/
Accessibility → inc/accessibility/
Final polish → inc/polish/
React/Vite app loader → inc/app/
REST API bridge → inc/rest/
Header/footer templates → template-parts/header/ and template-parts/footer/
Post/page templates → template-parts/post/ and template-parts/page/
Medical UI parts → template-parts/medical/
Frontend CSS → assets/css/frontend/
Backend CSS → assets/css/backend/
Frontend JS → assets/js/frontend/
Backend JS → assets/js/backend/
React/Vite built files → assets/build/
Many app dist files → assets/app-dist/
Unified loader files → assets/app-loader/
Source app folders → app/
Page templates → templates/
Gutenberg block folders → blocks/
Translations → languages/
Most important rule for future
Do not put everything in functions.php.
Keep:
functions.php → only load inc/autoload.php
inc/autoload.php → load modules
each module folder → own job
assets/ → only CSS, JS, images, fonts, dist files
template-parts/ → reusable visual PHP parts
templates/ → selectable page templates
Your final stable direction is:
RX Theme = Medical WordPress Theme Framework
Not just a simple theme.
Yes dear friend — I’ll add these three as one safe milestone: RX Theme v8.2 Control + Customizer + Gutenberg Medical Blocks, built on top of your working v7.9 Ad Manager version.
from pathlib import Path
import zipfile, shutil, re, subprocess, json, os
base_zip = Path(“/mnt/data/rx-theme-v7-9-ad-manager.zip”)
fallbacks = [
Path(“/mnt/data/rx-theme-v7-8-advanced-seo-schema.zip”),
Path(“/mnt/data/rx-theme-v7-7-advanced-archive-layout.zip”),
Path(“/mnt/data/rx-theme-v7-6-performance-system.zip”),
]
if not base_zip.exists():
for f in fallbacks:
if f.exists():
base_zip = f
break
work_root = Path(“/mnt/data/rx_v82_control_customizer_blocks”)
extract_dir = work_root / “extract”
if work_root.exists():
shutil.rmtree(work_root)
work_root.mkdir(parents=True)
extract_dir.mkdir(parents=True)
with zipfile.ZipFile(base_zip, “r”) as z:
z.extractall(extract_dir)
theme_roots = [p.parent for p in extract_dir.rglob(“style.css”) if (p.parent / “functions.php”).exists()]
if not theme_roots:
raise RuntimeError(“Theme root not found.”)
src_root = theme_roots[0]
theme_root = work_root / “rx-theme-v8-2-control-customizer-blocks”
shutil.copytree(src_root, theme_root)
def read(path):
return path.read_text(encoding=”utf-8″, errors=”ignore”)
def write(path, text):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text, encoding=”utf-8″)
# Update style.css
style_path = theme_root / “style.css”
if style_path.exists():
s = read(style_path)
s = re.sub(r”Theme Name:\s*.*”, “Theme Name: RX Theme v8.2 Control Customizer Blocks”, s, count=1) if “Theme Name:” in s else “/*\nTheme Name: RX Theme v8.2 Control Customizer Blocks\nVersion: 8.2\n*/\n” + s
s = re.sub(r”Version:\s*.*”, “Version: 8.2”, s, count=1) if “Version:” in s else s.replace(“*/”, “Version: 8.2\n*/”, 1)
write(style_path, s)
# Directories
inc_tools = theme_root / “inc/tools”
inc_customizer = theme_root / “inc/customizer”
inc_blocks = theme_root / “inc/blocks”
assets_admin_css = theme_root / “assets/css/backend”
assets_admin_js = theme_root / “assets/js/backend”
assets_blocks_css = theme_root / “assets/css/blocks”
assets_blocks_js = theme_root / “assets/js/blocks”
blocks_dir = theme_root / “blocks”
for d in [inc_tools, inc_customizer, inc_blocks, assets_admin_css, assets_admin_js, assets_blocks_css, assets_blocks_js, blocks_dir]:
d.mkdir(parents=True, exist_ok=True)
# ========== v8.0 Import/export settings ==========
write(inc_tools / “import-export-settings.php”, r”'<?php
/**
* RX Theme v8.0 Import / Export Settings.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v80_import_export_option_keys() {
return apply_filters(
‘rx_theme_v80_import_export_option_keys’,
array(
‘rx_theme_options’,
‘rx_theme_v79_ads’,
‘theme_mods_’ . get_option( ‘stylesheet’ ),
)
);
}
function rx_theme_v80_export_data() {
$data = array(
‘theme’ => wp_get_theme()->get( ‘Name’ ),
‘version’ => wp_get_theme()->get( ‘Version’ ),
‘site_url’ => home_url( ‘/’ ),
‘exported_at’ => current_time( ‘mysql’ ),
‘options’ => array(),
);
foreach ( rx_theme_v80_import_export_option_keys() as $key ) {
$data[‘options’][ $key ] = get_option( $key );
}
return $data;
}
function rx_theme_v80_admin_menu() {
add_theme_page(
esc_html__( ‘RX Import / Export’, ‘rx-theme’ ),
esc_html__( ‘RX Import / Export’, ‘rx-theme’ ),
‘manage_options’,
‘rx-theme-import-export’,
‘rx_theme_v80_import_export_page’
);
}
add_action( ‘admin_menu’, ‘rx_theme_v80_admin_menu’, 40 );
function rx_theme_v80_handle_export() {
if ( empty( $_POST[‘rx_theme_v80_export’] ) || ! current_user_can( ‘manage_options’ ) ) {
return;
}
check_admin_referer( ‘rx_theme_v80_export_action’, ‘rx_theme_v80_export_nonce’ );
$data = rx_theme_v80_export_data();
$filename = ‘rx-theme-settings-‘ . gmdate( ‘Y-m-d-H-i-s’ ) . ‘.json’;
nocache_headers();
header( ‘Content-Type: application/json; charset=utf-8’ );
header( ‘Content-Disposition: attachment; filename=’ . $filename );
echo wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );
exit;
}
add_action( ‘admin_init’, ‘rx_theme_v80_handle_export’ );
function rx_theme_v80_handle_import() {
if ( empty( $_POST[‘rx_theme_v80_import’] ) || ! current_user_can( ‘manage_options’ ) ) {
return;
}
check_admin_referer( ‘rx_theme_v80_import_action’, ‘rx_theme_v80_import_nonce’ );
if ( empty( $_FILES[‘rx_theme_v80_import_file’][‘tmp_name’] ) ) {
add_settings_error( ‘rx_theme_v80_messages’, ‘rx_theme_v80_no_file’, esc_html__( ‘No import file selected.’, ‘rx-theme’ ), ‘error’ );
return;
}
$file = $_FILES[‘rx_theme_v80_import_file’];
if ( ! empty( $file[‘error’] ) ) {
add_settings_error( ‘rx_theme_v80_messages’, ‘rx_theme_v80_upload_error’, esc_html__( ‘Upload error. Please try again.’, ‘rx-theme’ ), ‘error’ );
return;
}
$raw = file_get_contents( $file[‘tmp_name’] );
$json = json_decode( $raw, true );
if ( ! is_array( $json ) || empty( $json[‘options’] ) || ! is_array( $json[‘options’] ) ) {
add_settings_error( ‘rx_theme_v80_messages’, ‘rx_theme_v80_invalid_file’, esc_html__( ‘Invalid RX Theme settings file.’, ‘rx-theme’ ), ‘error’ );
return;
}
$allowed = rx_theme_v80_import_export_option_keys();
$count = 0;
foreach ( $json[‘options’] as $key => $value ) {
if ( in_array( $key, $allowed, true ) ) {
update_option( $key, $value );
$count++;
}
}
add_settings_error(
‘rx_theme_v80_messages’,
‘rx_theme_v80_imported’,
sprintf( esc_html__( ‘Import completed. %d option groups restored.’, ‘rx-theme’ ), absint( $count ) ),
‘updated’
);
}
add_action( ‘admin_init’, ‘rx_theme_v80_handle_import’ );
function rx_theme_v80_import_export_page() {
if ( ! current_user_can( ‘manage_options’ ) ) {
return;
}
settings_errors( ‘rx_theme_v80_messages’ );
?>
<div class=”wrap rx-theme-control-page”>
<h1><?php esc_html_e( ‘RX Theme Import / Export Settings’, ‘rx-theme’ ); ?></h1>
<p><?php esc_html_e( ‘Backup and restore RX Theme options, Customizer settings, and ad manager settings.’, ‘rx-theme’ ); ?></p>
<div class=”rx-admin-grid”>
<div class=”rx-admin-card”>
<h2><?php esc_html_e( ‘Export Settings’, ‘rx-theme’ ); ?></h2>
<p><?php esc_html_e( ‘Download a JSON backup file of your current RX Theme settings.’, ‘rx-theme’ ); ?></p>
<form method=”post”>
<?php wp_nonce_field( ‘rx_theme_v80_export_action’, ‘rx_theme_v80_export_nonce’ ); ?>
<p>
<button type=”submit” name=”rx_theme_v80_export” value=”1″ class=”button button-primary”>
<?php esc_html_e( ‘Download Export File’, ‘rx-theme’ ); ?>
</button>
</p>
</form>
</div>
<div class=”rx-admin-card”>
<h2><?php esc_html_e( ‘Import Settings’, ‘rx-theme’ ); ?></h2>
<p><?php esc_html_e( ‘Upload a previously exported RX Theme JSON file.’, ‘rx-theme’ ); ?></p>
<form method=”post” enctype=”multipart/form-data”>
<?php wp_nonce_field( ‘rx_theme_v80_import_action’, ‘rx_theme_v80_import_nonce’ ); ?>
<input type=”file” name=”rx_theme_v80_import_file” accept=”application/json,.json” required>
<p>
<button type=”submit” name=”rx_theme_v80_import” value=”1″ class=”button button-primary”>
<?php esc_html_e( ‘Import Settings’, ‘rx-theme’ ); ?>
</button>
</p>
</form>
</div>
</div>
<div class=”rx-admin-card”>
<h2><?php esc_html_e( ‘Included Option Groups’, ‘rx-theme’ ); ?></h2>
<ul>
<?php foreach ( rx_theme_v80_import_export_option_keys() as $key ) : ?>
<li><code><?php echo esc_html( $key ); ?></code></li>
<?php endforeach; ?>
</ul>
</div>
</div>
<?php
}
function rx_theme_v80_admin_assets( $hook ) {
if ( false === strpos( $hook, ‘rx-theme-import-export’ ) ) {
return;
}
wp_enqueue_style(
‘rx-theme-admin-tools’,
get_template_directory_uri() . ‘/assets/css/backend/rx-admin-tools.css’,
array(),
filemtime( get_template_directory() . ‘/assets/css/backend/rx-admin-tools.css’ )
);
}
add_action( ‘admin_enqueue_scripts’, ‘rx_theme_v80_admin_assets’ );
”’)
write(assets_admin_css / “rx-admin-tools.css”, r”’/* RX Theme admin tools */
.rx-theme-control-page .rx-admin-grid{
display:grid;
grid-template-columns:repeat(2,minmax(0,1fr));
gap:18px;
max-width:1100px;
}
.rx-theme-control-page .rx-admin-card{
margin:18px 0;
padding:22px;
border:1px solid #dcdcde;
border-radius:14px;
background:#fff;
box-shadow:0 8px 20px rgba(0,0,0,.04);
}
.rx-theme-control-page .rx-admin-card h2{
margin-top:0;
}
@media(max-width:782px){
.rx-theme-control-page .rx-admin-grid{
grid-template-columns:1fr;
}
}
”’)
# ========== v8.1 Customizer support ==========
write(inc_customizer / “customizer-support.php”, r”'<?php
/**
* RX Theme v8.1 Customizer Support.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v81_sanitize_checkbox( $checked ) {
return ( isset( $checked ) && true == $checked ) ? true : false;
}
function rx_theme_v81_sanitize_select( $input, $setting ) {
$choices = $setting->manager->get_control( $setting->id )->choices;
return array_key_exists( $input, $choices ) ? $input : $setting->default;
}
function rx_theme_v81_customizer_register( $wp_customize ) {
$wp_customize->add_panel(
‘rx_theme_panel’,
array(
‘title’ => esc_html__( ‘RX Theme Controls’, ‘rx-theme’ ),
‘description’ => esc_html__( ‘Customize RX Theme layout, header, colors, typography, and medical settings.’, ‘rx-theme’ ),
‘priority’ => 30,
)
);
$wp_customize->add_section(
‘rx_theme_branding’,
array(
‘title’ => esc_html__( ‘RX Branding’, ‘rx-theme’ ),
‘panel’ => ‘rx_theme_panel’,
)
);
$wp_customize->add_setting(
‘rx_site_logo_url’,
array(
‘default’ => ”,
‘sanitize_callback’ => ‘esc_url_raw’,
‘transport’ => ‘refresh’,
)
);
$wp_customize->add_control(
‘rx_site_logo_url’,
array(
‘label’ => esc_html__( ‘Site Logo URL’, ‘rx-theme’ ),
‘section’ => ‘rx_theme_branding’,
‘type’ => ‘url’,
)
);
$wp_customize->add_section(
‘rx_theme_colors’,
array(
‘title’ => esc_html__( ‘RX Colors’, ‘rx-theme’ ),
‘panel’ => ‘rx_theme_panel’,
)
);
$color_settings = array(
‘rx_primary_color’ => array( esc_html__( ‘Primary Color’, ‘rx-theme’ ), ‘#0066cc’ ),
‘rx_secondary_color’ => array( esc_html__( ‘Secondary Color’, ‘rx-theme’ ), ‘#00a884’ ),
‘rx_heading_color’ => array( esc_html__( ‘Heading Color’, ‘rx-theme’ ), ‘#111827’ ),
‘rx_text_color’ => array( esc_html__( ‘Text Color’, ‘rx-theme’ ), ‘#1f2937’ ),
);
foreach ( $color_settings as $id => $data ) {
$wp_customize->add_setting(
$id,
array(
‘default’ => $data[1],
‘sanitize_callback’ => ‘sanitize_hex_color’,
‘transport’ => ‘postMessage’,
)
);
$wp_customize->add_control(
new WP_Customize_Color_Control(
$wp_customize,
$id,
array(
‘label’ => $data[0],
‘section’ => ‘rx_theme_colors’,
)
)
);
}
$wp_customize->add_section(
‘rx_theme_layout’,
array(
‘title’ => esc_html__( ‘RX Layout’, ‘rx-theme’ ),
‘panel’ => ‘rx_theme_panel’,
)
);
$wp_customize->add_setting(
‘rx_container_width’,
array(
‘default’ => ‘1200’,
‘sanitize_callback’ => ‘absint’,
‘transport’ => ‘postMessage’,
)
);
$wp_customize->add_control(
‘rx_container_width’,
array(
‘label’ => esc_html__( ‘Container Width’, ‘rx-theme’ ),
‘section’ => ‘rx_theme_layout’,
‘type’ => ‘number’,
‘input_attrs’ => array(
‘min’ => 960,
‘max’ => 1600,
‘step’ => 10,
),
)
);
$wp_customize->add_setting(
‘rx_sidebar_position’,
array(
‘default’ => ‘right’,
‘sanitize_callback’ => ‘rx_theme_v81_sanitize_select’,
)
);
$wp_customize->add_control(
‘rx_sidebar_position’,
array(
‘label’ => esc_html__( ‘Sidebar Position’, ‘rx-theme’ ),
‘section’ => ‘rx_theme_layout’,
‘type’ => ‘select’,
‘choices’ => array(
‘right’ => esc_html__( ‘Right’, ‘rx-theme’ ),
‘left’ => esc_html__( ‘Left’, ‘rx-theme’ ),
‘none’ => esc_html__( ‘No Sidebar’, ‘rx-theme’ ),
),
)
);
$wp_customize->add_section(
‘rx_theme_header’,
array(
‘title’ => esc_html__( ‘RX Header’, ‘rx-theme’ ),
‘panel’ => ‘rx_theme_panel’,
)
);
$wp_customize->add_setting(
‘rx_header_style’,
array(
‘default’ => ‘medical’,
‘sanitize_callback’ => ‘rx_theme_v81_sanitize_select’,
)
);
$wp_customize->add_control(
‘rx_header_style’,
array(
‘label’ => esc_html__( ‘Header Style’, ‘rx-theme’ ),
‘section’ => ‘rx_theme_header’,
‘type’ => ‘select’,
‘choices’ => array(
‘default’ => esc_html__( ‘Default’, ‘rx-theme’ ),
‘medical’ => esc_html__( ‘Medical’, ‘rx-theme’ ),
‘centered’ => esc_html__( ‘Centered’, ‘rx-theme’ ),
‘transparent’ => esc_html__( ‘Transparent’, ‘rx-theme’ ),
‘sticky’ => esc_html__( ‘Sticky’, ‘rx-theme’ ),
‘topbar’ => esc_html__( ‘Topbar’, ‘rx-theme’ ),
),
)
);
$wp_customize->add_setting(
‘rx_sticky_header’,
array(
‘default’ => true,
‘sanitize_callback’ => ‘rx_theme_v81_sanitize_checkbox’,
)
);
$wp_customize->add_control(
‘rx_sticky_header’,
array(
‘label’ => esc_html__( ‘Enable Sticky Header’, ‘rx-theme’ ),
‘section’ => ‘rx_theme_header’,
‘type’ => ‘checkbox’,
)
);
$wp_customize->add_section(
‘rx_theme_medical’,
array(
‘title’ => esc_html__( ‘RX Medical Settings’, ‘rx-theme’ ),
‘panel’ => ‘rx_theme_panel’,
)
);
$wp_customize->add_setting(
‘rx_medical_disclaimer’,
array(
‘default’ => esc_html__( ‘This content is for educational purposes only and should not replace professional medical advice.’, ‘rx-theme’ ),
‘sanitize_callback’ => ‘sanitize_textarea_field’,
)
);
$wp_customize->add_control(
‘rx_medical_disclaimer’,
array(
‘label’ => esc_html__( ‘Medical Disclaimer Text’, ‘rx-theme’ ),
‘section’ => ‘rx_theme_medical’,
‘type’ => ‘textarea’,
)
);
}
add_action( ‘customize_register’, ‘rx_theme_v81_customizer_register’ );
function rx_theme_v81_customizer_css() {
$primary = get_theme_mod( ‘rx_primary_color’, ‘#0066cc’ );
$secondary = get_theme_mod( ‘rx_secondary_color’, ‘#00a884’ );
$heading = get_theme_mod( ‘rx_heading_color’, ‘#111827’ );
$text = get_theme_mod( ‘rx_text_color’, ‘#1f2937’ );
$container = absint( get_theme_mod( ‘rx_container_width’, 1200 ) );
?>
<style id=”rx-theme-v81-customizer-css”>
:root{
–rx-primary-color: <?php echo esc_html( $primary ); ?>;
–rx-secondary-color: <?php echo esc_html( $secondary ); ?>;
–rx-heading-color: <?php echo esc_html( $heading ); ?>;
–rx-text-color: <?php echo esc_html( $text ); ?>;
–rx-container-width: <?php echo esc_html( $container ); ?>px;
}
</style>
<?php
}
add_action( ‘wp_head’, ‘rx_theme_v81_customizer_css’, 5 );
function rx_theme_v81_customizer_preview_js() {
wp_enqueue_script(
‘rx-theme-customizer-preview’,
get_template_directory_uri() . ‘/assets/js/backend/customizer-preview.js’,
array( ‘customize-preview’ ),
filemtime( get_template_directory() . ‘/assets/js/backend/customizer-preview.js’ ),
true
);
}
add_action( ‘customize_preview_init’, ‘rx_theme_v81_customizer_preview_js’ );
”’)
write(assets_admin_js / “customizer-preview.js”, r”’/* RX Theme v8.1 Customizer live preview */
(function(api){
‘use strict’;
const map = {
rx_primary_color: ‘–rx-primary-color’,
rx_secondary_color: ‘–rx-secondary-color’,
rx_heading_color: ‘–rx-heading-color’,
rx_text_color: ‘–rx-text-color’
};
Object.keys(map).forEach(function(setting){
api(setting, function(value){
value.bind(function(newValue){
document.documentElement.style.setProperty(map[setting], newValue);
});
});
});
api(‘rx_container_width’, function(value){
value.bind(function(newValue){
document.documentElement.style.setProperty(‘–rx-container-width’, parseInt(newValue || 1200, 10) + ‘px’);
});
});
})(wp.customize);
”’)
# ========== v8.2 Gutenberg medical blocks ==========
write(inc_blocks / “gutenberg-medical-blocks.php”, r”'<?php
/**
* RX Theme v8.2 Gutenberg Medical Blocks.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v82_blocks_assets() {
wp_register_style(
‘rx-theme-medical-blocks’,
get_template_directory_uri() . ‘/assets/css/blocks/medical-blocks.css’,
array(),
filemtime( get_template_directory() . ‘/assets/css/blocks/medical-blocks.css’ )
);
wp_register_script(
‘rx-theme-medical-blocks-editor’,
get_template_directory_uri() . ‘/assets/js/blocks/medical-blocks.js’,
array( ‘wp-blocks’, ‘wp-element’, ‘wp-components’, ‘wp-block-editor’, ‘wp-i18n’ ),
filemtime( get_template_directory() . ‘/assets/js/blocks/medical-blocks.js’ ),
true
);
register_block_type(
‘rx-theme/medical-box’,
array(
‘editor_script’ => ‘rx-theme-medical-blocks-editor’,
‘editor_style’ => ‘rx-theme-medical-blocks’,
‘style’ => ‘rx-theme-medical-blocks’,
‘render_callback’ => ‘rx_theme_v82_render_medical_box_block’,
‘attributes’ => array(
‘title’ => array(
‘type’ => ‘string’,
‘default’ => ‘Medical Note’,
),
‘type’ => array(
‘type’ => ‘string’,
‘default’ => ‘info’,
),
‘content’ => array(
‘type’ => ‘string’,
‘default’ => ‘Write helpful medical information here.’,
),
),
)
);
register_block_type(
‘rx-theme/faq’,
array(
‘editor_script’ => ‘rx-theme-medical-blocks-editor’,
‘editor_style’ => ‘rx-theme-medical-blocks’,
‘style’ => ‘rx-theme-medical-blocks’,
‘render_callback’ => ‘rx_theme_v82_render_faq_block’,
‘attributes’ => array(
‘question’ => array(
‘type’ => ‘string’,
‘default’ => ‘What is the main question?’,
),
‘answer’ => array(
‘type’ => ‘string’,
‘default’ => ‘Write the answer here.’,
),
),
)
);
register_block_type(
‘rx-theme/reviewer-box’,
array(
‘editor_script’ => ‘rx-theme-medical-blocks-editor’,
‘editor_style’ => ‘rx-theme-medical-blocks’,
‘style’ => ‘rx-theme-medical-blocks’,
‘render_callback’ => ‘rx_theme_v82_render_reviewer_box_block’,
‘attributes’ => array(
‘name’ => array(
‘type’ => ‘string’,
‘default’ => ‘Medical Reviewer’,
),
‘role’ => array(
‘type’ => ‘string’,
‘default’ => ‘Doctor / Medical Expert’,
),
‘note’ => array(
‘type’ => ‘string’,
‘default’ => ‘This article was reviewed for medical accuracy and clarity.’,
),
),
)
);
}
add_action( ‘init’, ‘rx_theme_v82_blocks_assets’ );
function rx_theme_v82_render_medical_box_block( $attributes ) {
$title = isset( $attributes[‘title’] ) ? sanitize_text_field( $attributes[‘title’] ) : ”;
$type = isset( $attributes[‘type’] ) ? sanitize_html_class( $attributes[‘type’] ) : ‘info’;
$content = isset( $attributes[‘content’] ) ? wp_kses_post( $attributes[‘content’] ) : ”;
return sprintf(
‘<section class=”wp-block-rx-theme-medical-box rx-medical-block rx-medical-block–%1$s”><h3>%2$s</h3><p>%3$s</p></section>’,
esc_attr( $type ),
esc_html( $title ),
wp_kses_post( $content )
);
}
function rx_theme_v82_render_faq_block( $attributes ) {
$question = isset( $attributes[‘question’] ) ? sanitize_text_field( $attributes[‘question’] ) : ”;
$answer = isset( $attributes[‘answer’] ) ? wp_kses_post( $attributes[‘answer’] ) : ”;
return sprintf(
‘<details class=”wp-block-rx-theme-faq rx-faq-block”><summary>%1$s</summary><div class=”rx-faq-block__answer”>%2$s</div></details>’,
esc_html( $question ),
wpautop( $answer )
);
}
function rx_theme_v82_render_reviewer_box_block( $attributes ) {
$name = isset( $attributes[‘name’] ) ? sanitize_text_field( $attributes[‘name’] ) : ”;
$role = isset( $attributes[‘role’] ) ? sanitize_text_field( $attributes[‘role’] ) : ”;
$note = isset( $attributes[‘note’] ) ? wp_kses_post( $attributes[‘note’] ) : ”;
return sprintf(
‘<aside class=”wp-block-rx-theme-reviewer-box rx-reviewer-block”><div class=”rx-reviewer-block__badge”>✓</div><div><strong>%1$s</strong><span>%2$s</span><p>%3$s</p></div></aside>’,
esc_html( $name ),
esc_html( $role ),
wp_kses_post( $note )
);
}
”’)
write(assets_blocks_css / “medical-blocks.css”, r”’/* RX Theme v8.2 Gutenberg Medical Blocks */
.rx-medical-block{
margin:24px 0;
padding:22px;
border:1px solid #dbeafe;
border-left:5px solid var(–rx-primary-color,#0066cc);
border-radius:18px;
background:#f8fbff;
box-shadow:0 12px 32px rgba(15,23,42,.05);
}
.rx-medical-block h3{
margin:0 0 10px;
color:var(–rx-heading-color,#111827);
}
.rx-medical-block p{
margin:0;
color:#475569;
line-height:1.75;
}
.rx-medical-block–warning{
border-color:#fed7aa;
border-left-color:#f97316;
background:#fff7ed;
}
.rx-medical-block–success{
border-color:#bbf7d0;
border-left-color:#16a34a;
background:#f0fdf4;
}
.rx-medical-block–danger{
border-color:#fecaca;
border-left-color:#dc2626;
background:#fef2f2;
}
.rx-faq-block{
margin:18px 0;
padding:0;
border:1px solid #e5e7eb;
border-radius:16px;
background:#fff;
overflow:hidden;
}
.rx-faq-block summary{
padding:16px 18px;
cursor:pointer;
font-weight:900;
color:var(–rx-heading-color,#111827);
background:#f8fafc;
}
.rx-faq-block__answer{
padding:18px;
color:#475569;
line-height:1.75;
}
.rx-reviewer-block{
display:flex;
gap:14px;
align-items:flex-start;
margin:24px 0;
padding:20px;
border:1px solid #dbeafe;
border-radius:20px;
background:#fff;
box-shadow:0 12px 32px rgba(15,23,42,.05);
}
.rx-reviewer-block__badge{
display:flex;
align-items:center;
justify-content:center;
width:42px;
height:42px;
border-radius:999px;
background:#ecfdf5;
color:#047857;
font-weight:1000;
}
.rx-reviewer-block strong{
display:block;
color:var(–rx-heading-color,#111827);
font-size:18px;
}
.rx-reviewer-block span{
display:block;
color:var(–rx-primary-color,#0066cc);
font-weight:800;
margin-top:3px;
}
.rx-reviewer-block p{
margin:8px 0 0;
color:#64748b;
}
body.rx-dark-mode .rx-medical-block,
body.rx-dark-mode .rx-faq-block,
body.rx-dark-mode .rx-reviewer-block{
background:#111827;
border-color:#243244;
}
body.rx-dark-mode .rx-faq-block summary{
background:#0f172a;
}
body.rx-dark-mode .rx-medical-block p,
body.rx-dark-mode .rx-faq-block__answer,
body.rx-dark-mode .rx-reviewer-block p{
color:#cbd5e1;
}
”’)
write(assets_blocks_js / “medical-blocks.js”, r”’/* RX Theme v8.2 Gutenberg Medical Blocks */
(function(blocks, element, blockEditor, components, i18n){
const el = element.createElement;
const RichText = blockEditor.RichText;
const InspectorControls = blockEditor.InspectorControls;
const PanelBody = components.PanelBody;
const SelectControl = components.SelectControl;
const TextControl = components.TextControl;
const TextareaControl = components.TextareaControl;
const __ = i18n.__;
blocks.registerBlockType(‘rx-theme/medical-box’, {
title: __(‘RX Medical Box’, ‘rx-theme’),
icon: ‘heart’,
category: ‘widgets’,
attributes: {
title: { type: ‘string’, default: ‘Medical Note’ },
type: { type: ‘string’, default: ‘info’ },
content: { type: ‘string’, default: ‘Write helpful medical information here.’ }
},
edit: function(props){
const attrs = props.attributes;
return [
el(InspectorControls, {},
el(PanelBody, { title: __(‘Medical Box Settings’, ‘rx-theme’) },
el(SelectControl, {
label: __(‘Box Type’, ‘rx-theme’),
value: attrs.type,
options: [
{ label: ‘Info’, value: ‘info’ },
{ label: ‘Warning’, value: ‘warning’ },
{ label: ‘Success’, value: ‘success’ },
{ label: ‘Danger’, value: ‘danger’ }
],
onChange: function(value){ props.setAttributes({ type: value }); }
})
)
),
el(‘section’, { className: ‘rx-medical-block rx-medical-block–‘ + attrs.type },
el(RichText, {
tagName: ‘h3’,
value: attrs.title,
onChange: function(value){ props.setAttributes({ title: value }); },
placeholder: __(‘Title…’, ‘rx-theme’)
}),
el(RichText, {
tagName: ‘p’,
value: attrs.content,
onChange: function(value){ props.setAttributes({ content: value }); },
placeholder: __(‘Medical content…’, ‘rx-theme’)
})
)
];
},
save: function(){ return null; }
});
blocks.registerBlockType(‘rx-theme/faq’, {
title: __(‘RX FAQ’, ‘rx-theme’),
icon: ‘editor-help’,
category: ‘widgets’,
attributes: {
question: { type: ‘string’, default: ‘What is the main question?’ },
answer: { type: ‘string’, default: ‘Write the answer here.’ }
},
edit: function(props){
const attrs = props.attributes;
return el(‘div’, { className: ‘rx-faq-block’ },
el(RichText, {
tagName: ‘strong’,
value: attrs.question,
onChange: function(value){ props.setAttributes({ question: value }); },
placeholder: __(‘Question…’, ‘rx-theme’)
}),
el(RichText, {
tagName: ‘p’,
value: attrs.answer,
onChange: function(value){ props.setAttributes({ answer: value }); },
placeholder: __(‘Answer…’, ‘rx-theme’)
})
);
},
save: function(){ return null; }
});
blocks.registerBlockType(‘rx-theme/reviewer-box’, {
title: __(‘RX Reviewer Box’, ‘rx-theme’),
icon: ‘yes-alt’,
category: ‘widgets’,
attributes: {
name: { type: ‘string’, default: ‘Medical Reviewer’ },
role: { type: ‘string’, default: ‘Doctor / Medical Expert’ },
note: { type: ‘string’, default: ‘This article was reviewed for medical accuracy and clarity.’ }
},
edit: function(props){
const attrs = props.attributes;
return [
el(InspectorControls, {},
el(PanelBody, { title: __(‘Reviewer Details’, ‘rx-theme’) },
el(TextControl, {
label: __(‘Reviewer Name’, ‘rx-theme’),
value: attrs.name,
onChange: function(value){ props.setAttributes({ name: value }); }
}),
el(TextControl, {
label: __(‘Reviewer Role’, ‘rx-theme’),
value: attrs.role,
onChange: function(value){ props.setAttributes({ role: value }); }
}),
el(TextareaControl, {
label: __(‘Review Note’, ‘rx-theme’),
value: attrs.note,
onChange: function(value){ props.setAttributes({ note: value }); }
})
)
),
el(‘aside’, { className: ‘rx-reviewer-block’ },
el(‘div’, { className: ‘rx-reviewer-block__badge’ }, ‘✓’),
el(‘div’, {},
el(‘strong’, {}, attrs.name),
el(‘span’, {}, attrs.role),
el(‘p’, {}, attrs.note)
)
)
];
},
save: function(){ return null; }
});
})(window.wp.blocks, window.wp.element, window.wp.blockEditor, window.wp.components, window.wp.i18n);
”’)
# Create block folder structure placeholders too
block_defs = {
“rx-medical-box”: {
“name”: “rx-theme/medical-box”,
“title”: “RX Medical Box”
},
“rx-faq”: {
“name”: “rx-theme/faq”,
“title”: “RX FAQ”
},
“rx-reviewer-box”: {
“name”: “rx-theme/reviewer-box”,
“title”: “RX Reviewer Box”
}
}
for folder, data in block_defs.items():
bdir = blocks_dir / folder
bdir.mkdir(parents=True, exist_ok=True)
write(bdir / “block.json”, json.dumps({
“apiVersion”: 2,
“name”: data[“name”],
“title”: data[“title”],
“category”: “widgets”,
“icon”: “heart”,
“description”: “RX Theme medical Gutenberg block.”,
“textdomain”: “rx-theme”
}, indent=2))
write(bdir / “index.js”, “// Registered by assets/js/blocks/medical-blocks.js\n”)
write(bdir / “editor.css”, “/* Editor styles loaded from assets/css/blocks/medical-blocks.css */\n”)
write(bdir / “style.css”, “/* Frontend styles loaded from assets/css/blocks/medical-blocks.css */\n”)
# Loader for all v8 modules
write(theme_root / “inc/rx-v8-loader.php”, r”'<?php
/**
* RX Theme v8.0-v8.2 feature loader.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
$rx_theme_v8_files = array(
‘/inc/tools/import-export-settings.php’,
‘/inc/customizer/customizer-support.php’,
‘/inc/blocks/gutenberg-medical-blocks.php’,
);
foreach ( $rx_theme_v8_files as $rx_theme_v8_file ) {
$rx_theme_v8_path = get_template_directory() . $rx_theme_v8_file;
if ( file_exists( $rx_theme_v8_path ) ) {
require_once $rx_theme_v8_path;
}
}
”’)
# Add loader to autoload
autoload = theme_root / “inc/autoload.php”
if autoload.exists():
a = read(autoload)
if “/inc/rx-v8-loader.php” not in a:
idx = a.find(“foreach”)
if idx != -1:
before, after = a[:idx], a[idx:]
pos = before.rfind(“);”)
if pos != -1:
before = before[:pos] + ” ‘/inc/rx-v8-loader.php’,\n” + before[pos:]
a = before + after
else:
a += “\nrequire_once RX_THEME_DIR . ‘/inc/rx-v8-loader.php’;\n”
else:
a += “\nrequire_once RX_THEME_DIR . ‘/inc/rx-v8-loader.php’;\n”
write(autoload, a)
else:
write(autoload, “<?php\nif ( ! defined( ‘ABSPATH’ ) ) { exit; }\nrequire_once get_template_directory() . ‘/inc/rx-v8-loader.php’;\n”)
# PHP syntax check
errors = []
for p in theme_root.rglob(“*.php”):
proc = subprocess.run([“php”, “-l”, str(p)], capture_output=True, text=True)
if proc.returncode != 0:
errors.append(str(p) + “\n” + proc.stdout + proc.stderr)
if errors:
raise RuntimeError(“\n”.join(errors[:10]))
# Zip
zip_path = Path(“/mnt/data/rx-theme-v8-2-control-customizer-blocks.zip”)
if zip_path.exists():
zip_path.unlink()
with zipfile.ZipFile(zip_path, “w”, zipfile.ZIP_DEFLATED) as z:
for p in theme_root.rglob(“*”):
z.write(p, p.relative_to(work_root))
with zipfile.ZipFile(zip_path, “r”) as z:
bad = z.testzip()
if bad:
raise RuntimeError(“Bad ZIP file member: ” + str(bad))
print(“Created:”, zip_path)
print(“PHP syntax check: Passed”)
print(“ZIP integrity test: Passed”)
print(“Added: v8.0 Import/Export, v8.1 Customizer, v8.2 Gutenberg Medical Blocks”)
Yes dear friend — I’ll add the next two modules safely on top of your working v8.2 version: Disease A-Z template improvement and Contact + Newsletter system.
from pathlib import Path
import zipfile, shutil, re, subprocess, json
base_zip = Path(“/mnt/data/rx-theme-v8-2-control-customizer-blocks.zip”)
fallbacks = [
Path(“/mnt/data/rx-theme-v7-9-ad-manager.zip”),
Path(“/mnt/data/rx-theme-v7-8-advanced-seo-schema.zip”),
]
if not base_zip.exists():
for f in fallbacks:
if f.exists():
base_zip = f
break
work_root = Path(“/mnt/data/rx_v84_disease_contact_build”)
extract_dir = work_root / “extract”
if work_root.exists():
shutil.rmtree(work_root)
work_root.mkdir(parents=True)
extract_dir.mkdir(parents=True)
with zipfile.ZipFile(base_zip, “r”) as z:
z.extractall(extract_dir)
theme_roots = [p.parent for p in extract_dir.rglob(“style.css”) if (p.parent / “functions.php”).exists()]
if not theme_roots:
raise RuntimeError(“Theme root not found.”)
src_root = theme_roots[0]
theme_root = work_root / “rx-theme-v8-4-disease-contact-newsletter”
shutil.copytree(src_root, theme_root)
def read(path):
return path.read_text(encoding=”utf-8″, errors=”ignore”)
def write(path, text):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text, encoding=”utf-8″)
# Update style.css
style_path = theme_root / “style.css”
if style_path.exists():
s = read(style_path)
s = re.sub(r”Theme Name:\s*.*”, “Theme Name: RX Theme v8.4 Disease Contact Newsletter”, s, count=1) if “Theme Name:” in s else “/*\nTheme Name: RX Theme v8.4 Disease Contact Newsletter\nVersion: 8.4\n*/\n” + s
s = re.sub(r”Version:\s*.*”, “Version: 8.4”, s, count=1) if “Version:” in s else s.replace(“*/”, “Version: 8.4\n*/”, 1)
write(style_path, s)
# Directories
inc_medical = theme_root / “inc/medical”
inc_contact = theme_root / “inc/contact”
templates_dir = theme_root / “templates”
assets_css = theme_root / “assets/css/frontend”
assets_js = theme_root / “assets/js/frontend”
for d in [inc_medical, inc_contact, templates_dir, assets_css, assets_js]:
d.mkdir(parents=True, exist_ok=True)
# v8.3 Disease A-Z
write(inc_medical / “disease-az.php”, r”'<?php
/**
* RX Theme v8.3 Disease A-Z Template Improvement.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v83_disease_az_assets() {
$theme_uri = get_template_directory_uri();
$theme_dir = get_template_directory();
$css_rel = ‘/assets/css/frontend/disease-az.css’;
$js_rel = ‘/assets/js/frontend/disease-az.js’;
if ( file_exists( $theme_dir . $css_rel ) ) {
wp_enqueue_style(
‘rx-theme-disease-az’,
$theme_uri . $css_rel,
array(),
filemtime( $theme_dir . $css_rel )
);
}
if ( file_exists( $theme_dir . $js_rel ) ) {
wp_enqueue_script(
‘rx-theme-disease-az’,
$theme_uri . $js_rel,
array(),
filemtime( $theme_dir . $js_rel ),
true
);
}
}
add_action( ‘wp_enqueue_scripts’, ‘rx_theme_v83_disease_az_assets’, 42 );
function rx_theme_v83_get_disease_posts( $post_type = ‘post’, $limit = 500 ) {
$query = new WP_Query(
array(
‘post_type’ => sanitize_key( $post_type ),
‘post_status’ => ‘publish’,
‘posts_per_page’ => absint( $limit ),
‘orderby’ => ‘title’,
‘order’ => ‘ASC’,
‘ignore_sticky_posts’ => true,
‘no_found_rows’ => true,
‘update_post_meta_cache’ => false,
)
);
return $query;
}
function rx_theme_v83_group_posts_by_letter( $posts ) {
$groups = array();
foreach ( range( ‘A’, ‘Z’ ) as $letter ) {
$groups[ $letter ] = array();
}
foreach ( $posts as $post ) {
$title = get_the_title( $post );
$first = strtoupper( substr( remove_accents( $title ), 0, 1 ) );
if ( ! preg_match( ‘/[A-Z]/’, $first ) ) {
$first = ‘#’;
}
if ( ! isset( $groups[ $first ] ) ) {
$groups[ $first ] = array();
}
$groups[ $first ][] = $post;
}
return $groups;
}
function rx_theme_v83_disease_az_render( $atts = array() ) {
$atts = shortcode_atts(
array(
‘post_type’ => ‘post’,
‘limit’ => 500,
‘title’ => __( ‘Disease A-Z Library’, ‘rx-theme’ ),
‘subtitle’ => __( ‘Browse health topics, diseases, symptoms, diagnosis guides, and treatment articles alphabetically.’, ‘rx-theme’ ),
‘search’ => ‘1’,
),
$atts,
‘rx_disease_az’
);
$query = rx_theme_v83_get_disease_posts( $atts[‘post_type’], $atts[‘limit’] );
$groups = rx_theme_v83_group_posts_by_letter( $query->posts );
ob_start();
?>
<section class=”rx-disease-az” data-rx-disease-az>
<header class=”rx-disease-az__hero”>
<span class=”rx-disease-az__eyebrow”><?php esc_html_e( ‘Medical Library’, ‘rx-theme’ ); ?></span>
<h1><?php echo esc_html( $atts[‘title’] ); ?></h1>
<p><?php echo esc_html( $atts[‘subtitle’] ); ?></p>
<?php if ( ‘1’ === $atts[‘search’] ) : ?>
<div class=”rx-disease-az__search”>
<label class=”screen-reader-text” for=”rx-disease-az-search”><?php esc_html_e( ‘Search Disease A-Z’, ‘rx-theme’ ); ?></label>
<input id=”rx-disease-az-search” type=”search” placeholder=”<?php esc_attr_e( ‘Search disease, symptom, treatment…’, ‘rx-theme’ ); ?>” data-rx-disease-search>
<span aria-hidden=”true”>⌕</span>
</div>
<?php endif; ?>
</header>
<nav class=”rx-disease-az__letters” aria-label=”<?php esc_attr_e( ‘Disease alphabet navigation’, ‘rx-theme’ ); ?>”>
<?php foreach ( range( ‘A’, ‘Z’ ) as $letter ) : ?>
<?php $has_posts = ! empty( $groups[ $letter ] ); ?>
<a class=”<?php echo $has_posts ? ‘has-posts’ : ‘is-empty’; ?>” href=”#rx-letter-<?php echo esc_attr( $letter ); ?>”>
<?php echo esc_html( $letter ); ?>
</a>
<?php endforeach; ?>
</nav>
<div class=”rx-disease-az__grid”>
<?php foreach ( $groups as $letter => $posts ) : ?>
<?php if ( empty( $posts ) ) { continue; } ?>
<section id=”rx-letter-<?php echo esc_attr( $letter ); ?>” class=”rx-disease-az__group”>
<h2><?php echo esc_html( $letter ); ?></h2>
<div class=”rx-disease-az__items”>
<?php foreach ( $posts as $post ) : ?>
<article class=”rx-disease-az__item” data-rx-disease-item data-title=”<?php echo esc_attr( strtolower( get_the_title( $post ) ) ); ?>”>
<a href=”<?php echo esc_url( get_permalink( $post ) ); ?>”>
<strong><?php echo esc_html( get_the_title( $post ) ); ?></strong>
<span><?php echo esc_html( wp_trim_words( get_the_excerpt( $post ), 16, ‘…’ ) ); ?></span>
</a>
</article>
<?php endforeach; ?>
</div>
</section>
<?php endforeach; ?>
</div>
<div class=”rx-disease-az__no-results” data-rx-disease-no-results hidden>
<strong><?php esc_html_e( ‘No matching topics found.’, ‘rx-theme’ ); ?></strong>
<p><?php esc_html_e( ‘Try another disease, symptom, or treatment keyword.’, ‘rx-theme’ ); ?></p>
</div>
</section>
<?php
wp_reset_postdata();
return ob_get_clean();
}
add_shortcode( ‘rx_disease_az’, ‘rx_theme_v83_disease_az_render’ );
”’)
write(templates_dir / “template-disease-az.php”, r”'<?php
/**
* Template Name: RX Disease A-Z
* Description: Advanced Disease A-Z medical library template.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
get_header();
?>
<main id=”primary” class=”rx-main rx-disease-az-template”>
<div class=”rx-container”>
<?php
while ( have_posts() ) :
the_post();
if ( trim( get_the_content() ) ) {
echo ‘<div class=”rx-page-intro”>’;
the_content();
echo ‘</div>’;
}
if ( function_exists( ‘rx_theme_v83_disease_az_render’ ) ) {
echo rx_theme_v83_disease_az_render(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
endwhile;
?>
</div>
</main>
<?php
get_footer();
”’)
write(assets_css / “disease-az.css”, r”’/* RX Theme v8.3 Disease A-Z */
.rx-disease-az{
padding:34px 0 70px;
}
.rx-disease-az__hero{
padding:36px;
border:1px solid #e5e7eb;
border-radius:32px;
background:
radial-gradient(circle at top right, rgba(0,168,132,.13), transparent 34%),
linear-gradient(135deg,#f8fbff,#ffffff);
box-shadow:0 18px 55px rgba(15,23,42,.06);
}
.rx-disease-az__eyebrow{
display:inline-flex;
padding:7px 12px;
border-radius:999px;
background:#eff6ff;
color:var(–rx-primary-color,#0066cc);
font-size:13px;
font-weight:900;
text-transform:uppercase;
letter-spacing:.06em;
}
.rx-disease-az__hero h1{
margin:14px 0 10px;
font-size:clamp(34px,5vw,60px);
line-height:1.04;
color:var(–rx-heading-color,#111827);
}
.rx-disease-az__hero p{
max-width:760px;
margin:0;
color:#475569;
font-size:18px;
line-height:1.75;
}
.rx-disease-az__search{
position:relative;
max-width:720px;
margin-top:24px;
border:1px solid #cfcfcf;
border-radius:999px;
overflow:hidden;
background:#fff;
}
.rx-disease-az__search input{
width:100%;
min-height:48px;
border:0;
padding:0 58px 0 20px;
background:transparent;
outline:none;
font-size:16px;
}
.rx-disease-az__search span{
position:absolute;
right:18px;
top:50%;
transform:translateY(-50%);
font-size:24px;
color:#111;
}
.rx-disease-az__letters{
position:sticky;
top:82px;
z-index:20;
display:flex;
flex-wrap:wrap;
gap:7px;
margin:24px 0;
padding:12px;
border:1px solid #e5e7eb;
border-radius:20px;
background:rgba(255,255,255,.96);
box-shadow:0 12px 32px rgba(15,23,42,.06);
backdrop-filter:blur(12px);
}
.rx-disease-az__letters a{
display:inline-flex;
align-items:center;
justify-content:center;
width:38px;
height:38px;
border-radius:999px;
background:#f8fafc;
color:#64748b;
font-weight:900;
text-decoration:none;
}
.rx-disease-az__letters a.has-posts{
background:#eff6ff;
color:var(–rx-primary-color,#0066cc);
}
.rx-disease-az__letters a:hover,
.rx-disease-az__letters a:focus{
background:var(–rx-primary-color,#0066cc);
color:#fff;
}
.rx-disease-az__grid{
display:grid;
gap:22px;
}
.rx-disease-az__group{
scroll-margin-top:150px;
padding:24px;
border:1px solid #e5e7eb;
border-radius:26px;
background:#fff;
}
.rx-disease-az__group h2{
display:inline-flex;
align-items:center;
justify-content:center;
width:52px;
height:52px;
margin:0 0 18px;
border-radius:18px;
background:linear-gradient(135deg,var(–rx-primary-color,#0066cc),var(–rx-secondary-color,#00a884));
color:#fff;
}
.rx-disease-az__items{
display:grid;
grid-template-columns:repeat(3,minmax(0,1fr));
gap:14px;
}
.rx-disease-az__item a{
display:block;
height:100%;
padding:16px;
border:1px solid #e5e7eb;
border-radius:18px;
background:#f8fafc;
text-decoration:none;
transition:transform .18s ease, box-shadow .18s ease, border-color .18s ease;
}
.rx-disease-az__item a:hover{
transform:translateY(-2px);
border-color:#bfdbfe;
box-shadow:0 14px 34px rgba(15,23,42,.09);
}
.rx-disease-az__item strong{
display:block;
color:var(–rx-heading-color,#111827);
font-size:17px;
line-height:1.25;
}
.rx-disease-az__item span{
display:block;
margin-top:8px;
color:#64748b;
font-size:14px;
line-height:1.55;
}
.rx-disease-az__no-results{
margin:24px 0;
padding:24px;
border:1px solid #fed7aa;
border-radius:22px;
background:#fff7ed;
color:#9a3412;
text-align:center;
}
body.rx-dark-mode .rx-disease-az__hero,
body.rx-dark-mode .rx-disease-az__letters,
body.rx-dark-mode .rx-disease-az__group{
background:#111827;
border-color:#243244;
}
body.rx-dark-mode .rx-disease-az__hero h1,
body.rx-dark-mode .rx-disease-az__item strong{
color:#fff;
}
body.rx-dark-mode .rx-disease-az__hero p,
body.rx-dark-mode .rx-disease-az__item span{
color:#cbd5e1;
}
body.rx-dark-mode .rx-disease-az__item a,
body.rx-dark-mode .rx-disease-az__search{
background:#0f172a;
border-color:#243244;
}
body.rx-dark-mode .rx-disease-az__search input{
color:#fff;
}
@media(max-width:980px){
.rx-disease-az__items{
grid-template-columns:repeat(2,minmax(0,1fr));
}
.rx-disease-az__letters{
top:70px;
}
}
@media(max-width:640px){
.rx-disease-az__hero{
padding:24px;
border-radius:24px;
}
.rx-disease-az__items{
grid-template-columns:1fr;
}
.rx-disease-az__group{
padding:18px;
border-radius:22px;
}
}
”’)
write(assets_js / “disease-az.js”, r”’/* RX Theme v8.3 Disease A-Z */
(function(){
‘use strict’;
document.querySelectorAll(‘[data-rx-disease-az]’).forEach(function(wrapper){
const input = wrapper.querySelector(‘[data-rx-disease-search]’);
const items = wrapper.querySelectorAll(‘[data-rx-disease-item]’);
const noResults = wrapper.querySelector(‘[data-rx-disease-no-results]’);
if (!input || !items.length) return;
input.addEventListener(‘input’, function(){
const query = input.value.trim().toLowerCase();
let visible = 0;
items.forEach(function(item){
const title = item.getAttribute(‘data-title’) || ”;
const match = !query || title.indexOf(query) !== -1;
item.hidden = !match;
if (match) visible++;
});
wrapper.querySelectorAll(‘.rx-disease-az__group’).forEach(function(group){
const visibleItems = group.querySelectorAll(‘[data-rx-disease-item]:not([hidden])’);
group.hidden = query && visibleItems.length === 0;
});
if (noResults) {
noResults.hidden = visible > 0;
}
});
});
})();
”’)
# v8.4 Contact + Newsletter
write(inc_contact / “contact-newsletter.php”, r”'<?php
/**
* RX Theme v8.4 Contact + Newsletter System.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v84_contact_assets() {
$theme_uri = get_template_directory_uri();
$theme_dir = get_template_directory();
$css_rel = ‘/assets/css/frontend/contact-newsletter.css’;
$js_rel = ‘/assets/js/frontend/contact-newsletter.js’;
if ( file_exists( $theme_dir . $css_rel ) ) {
wp_enqueue_style(
‘rx-theme-contact-newsletter’,
$theme_uri . $css_rel,
array(),
filemtime( $theme_dir . $css_rel )
);
}
if ( file_exists( $theme_dir . $js_rel ) ) {
wp_enqueue_script(
‘rx-theme-contact-newsletter’,
$theme_uri . $js_rel,
array(),
filemtime( $theme_dir . $js_rel ),
true
);
}
}
add_action( ‘wp_enqueue_scripts’, ‘rx_theme_v84_contact_assets’, 43 );
function rx_theme_v84_newsletter_emails() {
$emails = get_option( ‘rx_theme_newsletter_emails’, array() );
return is_array( $emails ) ? $emails : array();
}
function rx_theme_v84_save_newsletter_email( $email ) {
$email = sanitize_email( $email );
if ( ! is_email( $email ) ) {
return false;
}
$emails = rx_theme_v84_newsletter_emails();
if ( ! in_array( $email, $emails, true ) ) {
$emails[] = $email;
update_option( ‘rx_theme_newsletter_emails’, array_values( $emails ), false );
}
return true;
}
function rx_theme_v84_handle_contact_newsletter() {
if ( empty( $_POST[‘rx_theme_v84_action’] ) ) {
return;
}
$action = sanitize_key( wp_unslash( $_POST[‘rx_theme_v84_action’] ) );
if ( ‘contact’ === $action ) {
if ( empty( $_POST[‘rx_contact_nonce’] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[‘rx_contact_nonce’] ) ), ‘rx_contact_form’ ) ) {
wp_safe_redirect( add_query_arg( ‘rx_contact’, ‘security’, wp_get_referer() ?: home_url( ‘/’ ) ) );
exit;
}
if ( ! empty( $_POST[‘rx_website’] ) ) {
wp_safe_redirect( add_query_arg( ‘rx_contact’, ‘spam’, wp_get_referer() ?: home_url( ‘/’ ) ) );
exit;
}
$name = isset( $_POST[‘rx_name’] ) ? sanitize_text_field( wp_unslash( $_POST[‘rx_name’] ) ) : ”;
$email = isset( $_POST[‘rx_email’] ) ? sanitize_email( wp_unslash( $_POST[‘rx_email’] ) ) : ”;
$subject = isset( $_POST[‘rx_subject’] ) ? sanitize_text_field( wp_unslash( $_POST[‘rx_subject’] ) ) : ”;
$message = isset( $_POST[‘rx_message’] ) ? sanitize_textarea_field( wp_unslash( $_POST[‘rx_message’] ) ) : ”;
if ( ! $name || ! is_email( $email ) || ! $message ) {
wp_safe_redirect( add_query_arg( ‘rx_contact’, ‘missing’, wp_get_referer() ?: home_url( ‘/’ ) ) );
exit;
}
$to = get_option( ‘admin_email’ );
$headers = array( ‘Reply-To: ‘ . $name . ‘ <‘ . $email . ‘>’ );
$body = “Name: {$name}\nEmail: {$email}\nSubject: {$subject}\n\nMessage:\n{$message}”;
wp_mail( $to, ‘[RX Theme Contact] ‘ . ( $subject ? $subject : ‘New message’ ), $body, $headers );
wp_safe_redirect( add_query_arg( ‘rx_contact’, ‘success’, wp_get_referer() ?: home_url( ‘/’ ) ) );
exit;
}
if ( ‘newsletter’ === $action ) {
if ( empty( $_POST[‘rx_newsletter_nonce’] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[‘rx_newsletter_nonce’] ) ), ‘rx_newsletter_form’ ) ) {
wp_safe_redirect( add_query_arg( ‘rx_newsletter’, ‘security’, wp_get_referer() ?: home_url( ‘/’ ) ) );
exit;
}
$email = isset( $_POST[‘rx_newsletter_email’] ) ? sanitize_email( wp_unslash( $_POST[‘rx_newsletter_email’] ) ) : ”;
if ( ! rx_theme_v84_save_newsletter_email( $email ) ) {
wp_safe_redirect( add_query_arg( ‘rx_newsletter’, ‘invalid’, wp_get_referer() ?: home_url( ‘/’ ) ) );
exit;
}
wp_safe_redirect( add_query_arg( ‘rx_newsletter’, ‘success’, wp_get_referer() ?: home_url( ‘/’ ) ) );
exit;
}
}
add_action( ‘admin_post_nopriv_rx_theme_v84_form’, ‘rx_theme_v84_handle_contact_newsletter’ );
add_action( ‘admin_post_rx_theme_v84_form’, ‘rx_theme_v84_handle_contact_newsletter’ );
function rx_theme_v84_notice( $type, $success_text ) {
$value = isset( $_GET[ $type ] ) ? sanitize_key( wp_unslash( $_GET[ $type ] ) ) : ”;
if ( ! $value ) {
return ”;
}
if ( ‘success’ === $value ) {
return ‘<div class=”rx-form-notice rx-form-notice–success”>’ . esc_html( $success_text ) . ‘</div>’;
}
return ‘<div class=”rx-form-notice rx-form-notice–error”>’ . esc_html__( ‘Please check the form and try again.’, ‘rx-theme’ ) . ‘</div>’;
}
function rx_theme_v84_contact_form_shortcode( $atts ) {
$atts = shortcode_atts(
array(
‘title’ => __( ‘Contact RX Team’, ‘rx-theme’ ),
‘subtitle’ => __( ‘Send a message for website, article, or medical education related communication.’, ‘rx-theme’ ),
),
$atts,
‘rx_contact_form’
);
ob_start();
?>
<section class=”rx-contact-box”>
<div class=”rx-contact-box__intro”>
<span><?php esc_html_e( ‘Contact’, ‘rx-theme’ ); ?></span>
<h2><?php echo esc_html( $atts[‘title’] ); ?></h2>
<p><?php echo esc_html( $atts[‘subtitle’] ); ?></p>
</div>
<?php echo wp_kses_post( rx_theme_v84_notice( ‘rx_contact’, __( ‘Your message has been sent successfully.’, ‘rx-theme’ ) ) ); ?>
<form class=”rx-contact-form” method=”post” action=”<?php echo esc_url( admin_url( ‘admin-post.php’ ) ); ?>”>
<input type=”hidden” name=”action” value=”rx_theme_v84_form”>
<input type=”hidden” name=”rx_theme_v84_action” value=”contact”>
<?php wp_nonce_field( ‘rx_contact_form’, ‘rx_contact_nonce’ ); ?>
<p class=”rx-honeypot”>
<label><?php esc_html_e( ‘Website’, ‘rx-theme’ ); ?></label>
<input type=”text” name=”rx_website” tabindex=”-1″ autocomplete=”off”>
</p>
<div class=”rx-form-grid”>
<p>
<label for=”rx-name”><?php esc_html_e( ‘Name’, ‘rx-theme’ ); ?></label>
<input id=”rx-name” type=”text” name=”rx_name” required>
</p>
<p>
<label for=”rx-email”><?php esc_html_e( ‘Email’, ‘rx-theme’ ); ?></label>
<input id=”rx-email” type=”email” name=”rx_email” required>
</p>
</div>
<p>
<label for=”rx-subject”><?php esc_html_e( ‘Subject’, ‘rx-theme’ ); ?></label>
<input id=”rx-subject” type=”text” name=”rx_subject”>
</p>
<p>
<label for=”rx-message”><?php esc_html_e( ‘Message’, ‘rx-theme’ ); ?></label>
<textarea id=”rx-message” name=”rx_message” rows=”6″ required></textarea>
</p>
<button type=”submit”><?php esc_html_e( ‘Send Message’, ‘rx-theme’ ); ?></button>
</form>
</section>
<?php
return ob_get_clean();
}
add_shortcode( ‘rx_contact_form’, ‘rx_theme_v84_contact_form_shortcode’ );
function rx_theme_v84_newsletter_shortcode( $atts ) {
$atts = shortcode_atts(
array(
‘title’ => __( ‘Join RX Health Newsletter’, ‘rx-theme’ ),
‘subtitle’ => __( ‘Get useful medical education updates and new health guides.’, ‘rx-theme’ ),
‘button’ => __( ‘Subscribe’, ‘rx-theme’ ),
),
$atts,
‘rx_newsletter’
);
ob_start();
?>
<section class=”rx-newsletter-box”>
<div class=”rx-newsletter-box__content”>
<span><?php esc_html_e( ‘Newsletter’, ‘rx-theme’ ); ?></span>
<h2><?php echo esc_html( $atts[‘title’] ); ?></h2>
<p><?php echo esc_html( $atts[‘subtitle’] ); ?></p>
</div>
<?php echo wp_kses_post( rx_theme_v84_notice( ‘rx_newsletter’, __( ‘Thank you for subscribing.’, ‘rx-theme’ ) ) ); ?>
<form class=”rx-newsletter-form” method=”post” action=”<?php echo esc_url( admin_url( ‘admin-post.php’ ) ); ?>”>
<input type=”hidden” name=”action” value=”rx_theme_v84_form”>
<input type=”hidden” name=”rx_theme_v84_action” value=”newsletter”>
<?php wp_nonce_field( ‘rx_newsletter_form’, ‘rx_newsletter_nonce’ ); ?>
<label class=”screen-reader-text” for=”rx-newsletter-email”><?php esc_html_e( ‘Email address’, ‘rx-theme’ ); ?></label>
<input id=”rx-newsletter-email” type=”email” name=”rx_newsletter_email” placeholder=”<?php esc_attr_e( ‘Enter your email’, ‘rx-theme’ ); ?>” required>
<button type=”submit”><?php echo esc_html( $atts[‘button’] ); ?></button>
</form>
</section>
<?php
return ob_get_clean();
}
add_shortcode( ‘rx_newsletter’, ‘rx_theme_v84_newsletter_shortcode’ );
function rx_theme_v84_subscribers_menu() {
add_theme_page(
esc_html__( ‘RX Newsletter Subscribers’, ‘rx-theme’ ),
esc_html__( ‘RX Subscribers’, ‘rx-theme’ ),
‘manage_options’,
‘rx-theme-subscribers’,
‘rx_theme_v84_subscribers_page’
);
}
add_action( ‘admin_menu’, ‘rx_theme_v84_subscribers_menu’, 45 );
function rx_theme_v84_subscribers_page() {
if ( ! current_user_can( ‘manage_options’ ) ) {
return;
}
$emails = rx_theme_v84_newsletter_emails();
?>
<div class=”wrap”>
<h1><?php esc_html_e( ‘RX Newsletter Subscribers’, ‘rx-theme’ ); ?></h1>
<p><?php echo esc_html( sprintf( __( ‘Total subscribers: %d’, ‘rx-theme’ ), count( $emails ) ) ); ?></p>
<textarea class=”large-text code” rows=”14″ readonly><?php echo esc_textarea( implode( “\n”, $emails ) ); ?></textarea>
<p class=”description”><?php esc_html_e( ‘Copy these emails for your newsletter service. For large email campaigns, use a professional email platform.’, ‘rx-theme’ ); ?></p>
</div>
<?php
}
”’)
write(templates_dir / “template-contact-newsletter.php”, r”'<?php
/**
* Template Name: RX Contact + Newsletter
* Description: RX contact form and newsletter signup page.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
get_header();
?>
<main id=”primary” class=”rx-main rx-contact-newsletter-template”>
<div class=”rx-container”>
<?php
while ( have_posts() ) :
the_post();
echo ‘<header class=”rx-page-header”>’;
the_title( ‘<h1>’, ‘</h1>’ );
echo ‘</header>’;
if ( trim( get_the_content() ) ) {
echo ‘<div class=”rx-page-intro”>’;
the_content();
echo ‘</div>’;
}
echo do_shortcode( ‘[rx_contact_form]’ );
echo do_shortcode( ‘[rx_newsletter]’ );
endwhile;
?>
</div>
</main>
<?php
get_footer();
”’)
write(assets_css / “contact-newsletter.css”, r”’/* RX Theme v8.4 Contact + Newsletter */
.rx-contact-box,
.rx-newsletter-box{
margin:34px 0;
padding:30px;
border:1px solid #e5e7eb;
border-radius:30px;
background:#fff;
box-shadow:0 18px 55px rgba(15,23,42,.06);
}
.rx-contact-box__intro span,
.rx-newsletter-box__content span{
display:inline-flex;
margin-bottom:10px;
padding:7px 12px;
border-radius:999px;
background:#eff6ff;
color:var(–rx-primary-color,#0066cc);
font-weight:900;
font-size:13px;
letter-spacing:.06em;
text-transform:uppercase;
}
.rx-contact-box h2,
.rx-newsletter-box h2{
margin:0 0 10px;
color:var(–rx-heading-color,#111827);
font-size:clamp(28px,4vw,44px);
}
.rx-contact-box p,
.rx-newsletter-box p{
color:#64748b;
line-height:1.7;
}
.rx-form-grid{
display:grid;
grid-template-columns:repeat(2,minmax(0,1fr));
gap:16px;
}
.rx-contact-form label{
display:block;
margin-bottom:7px;
color:var(–rx-heading-color,#111827);
font-weight:900;
}
.rx-contact-form input,
.rx-contact-form textarea,
.rx-newsletter-form input{
width:100%;
border:1px solid #cbd5e1;
border-radius:16px;
padding:13px 15px;
background:#fff;
color:#111827;
outline:none;
}
.rx-contact-form input:focus,
.rx-contact-form textarea:focus,
.rx-newsletter-form input:focus{
border-color:var(–rx-primary-color,#0066cc);
box-shadow:0 0 0 4px rgba(0,102,204,.1);
}
.rx-contact-form button,
.rx-newsletter-form button{
border:0;
border-radius:999px;
padding:13px 22px;
background:linear-gradient(135deg,var(–rx-primary-color,#0066cc),var(–rx-secondary-color,#00a884));
color:#fff;
font-weight:900;
cursor:pointer;
}
.rx-newsletter-box{
display:grid;
grid-template-columns:minmax(0,1fr) minmax(300px,520px);
align-items:center;
gap:24px;
background:
radial-gradient(circle at top right, rgba(0,168,132,.14), transparent 36%),
linear-gradient(135deg,#f8fbff,#ffffff);
}
.rx-newsletter-form{
display:flex;
gap:10px;
padding:8px;
border:1px solid #cfcfcf;
border-radius:999px;
background:#fff;
}
.rx-newsletter-form input{
border:0;
border-radius:999px;
box-shadow:none!important;
}
.rx-newsletter-form button{
white-space:nowrap;
}
.rx-form-notice{
margin:18px 0;
padding:13px 16px;
border-radius:16px;
font-weight:800;
}
.rx-form-notice–success{
background:#ecfdf5;
color:#047857;
border:1px solid #bbf7d0;
}
.rx-form-notice–error{
background:#fef2f2;
color:#b91c1c;
border:1px solid #fecaca;
}
.rx-honeypot{
position:absolute!important;
left:-9999px!important;
opacity:0!important;
}
body.rx-dark-mode .rx-contact-box,
body.rx-dark-mode .rx-newsletter-box{
background:#111827;
border-color:#243244;
}
body.rx-dark-mode .rx-contact-box h2,
body.rx-dark-mode .rx-newsletter-box h2,
body.rx-dark-mode .rx-contact-form label{
color:#fff;
}
body.rx-dark-mode .rx-contact-form input,
body.rx-dark-mode .rx-contact-form textarea,
body.rx-dark-mode .rx-newsletter-form,
body.rx-dark-mode .rx-newsletter-form input{
background:#0f172a;
border-color:#243244;
color:#fff;
}
@media(max-width:800px){
.rx-form-grid,
.rx-newsletter-box{
grid-template-columns:1fr;
}
}
@media(max-width:560px){
.rx-contact-box,
.rx-newsletter-box{
padding:22px;
border-radius:22px;
}
.rx-newsletter-form{
display:block;
border-radius:22px;
}
.rx-newsletter-form button{
width:100%;
margin-top:8px;
}
}
”’)
write(assets_js / “contact-newsletter.js”, r”’/* RX Theme v8.4 Contact + Newsletter small enhancements */
(function(){
‘use strict’;
document.querySelectorAll(‘.rx-contact-form, .rx-newsletter-form’).forEach(function(form){
form.addEventListener(‘submit’, function(){
const button = form.querySelector(‘button[type=”submit”]’);
if (button) {
button.dataset.originalText = button.textContent;
button.textContent = ‘Sending…’;
button.disabled = true;
}
});
});
})();
”’)
# Loader
write(theme_root / “inc/rx-v83-v84-loader.php”, r”'<?php
/**
* RX Theme v8.3-v8.4 feature loader.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
$rx_theme_v83_v84_files = array(
‘/inc/medical/disease-az.php’,
‘/inc/contact/contact-newsletter.php’,
);
foreach ( $rx_theme_v83_v84_files as $rx_theme_v83_v84_file ) {
$rx_theme_v83_v84_path = get_template_directory() . $rx_theme_v83_v84_file;
if ( file_exists( $rx_theme_v83_v84_path ) ) {
require_once $rx_theme_v83_v84_path;
}
}
”’)
# Add to autoload
autoload = theme_root / “inc/autoload.php”
if autoload.exists():
a = read(autoload)
if “/inc/rx-v83-v84-loader.php” not in a:
idx = a.find(“foreach”)
if idx != -1:
before, after = a[:idx], a[idx:]
pos = before.rfind(“);”)
if pos != -1:
before = before[:pos] + ” ‘/inc/rx-v83-v84-loader.php’,\n” + before[pos:]
a = before + after
else:
a += “\nrequire_once RX_THEME_DIR . ‘/inc/rx-v83-v84-loader.php’;\n”
else:
a += “\nrequire_once RX_THEME_DIR . ‘/inc/rx-v83-v84-loader.php’;\n”
write(autoload, a)
else:
write(autoload, “<?php\nif ( ! defined( ‘ABSPATH’ ) ) { exit; }\nrequire_once get_template_directory() . ‘/inc/rx-v83-v84-loader.php’;\n”)
# PHP syntax check
errors = []
for p in theme_root.rglob(“*.php”):
proc = subprocess.run([“php”, “-l”, str(p)], capture_output=True, text=True)
if proc.returncode != 0:
errors.append(str(p) + “\n” + proc.stdout + proc.stderr)
if errors:
raise RuntimeError(“\n”.join(errors[:10]))
# Zip
zip_path = Path(“/mnt/data/rx-theme-v8-4-disease-contact-newsletter.zip”)
if zip_path.exists():
zip_path.unlink()
with zipfile.ZipFile(zip_path, “w”, zipfile.ZIP_DEFLATED) as z:
for p in theme_root.rglob(“*”):
z.write(p, p.relative_to(work_root))
with zipfile.ZipFile(zip_path, “r”) as z:
bad = z.testzip()
if bad:
raise RuntimeError(“Bad ZIP file member: ” + str(bad))
print(“Created:”, zip_path)
print(“PHP syntax check: Passed”)
print(“ZIP integrity test: Passed”)
print(“Added: v8.3 Disease A-Z Template Improvement + v8.4 Contact Newsletter System”)
============================================
Yes dear friend — I’ll complete these remaining RX Theme modules safely in one final polish version, building on your working v8.4 version: v8.5 WooCommerce/Elementor, v8.6 Accessibility tools, and v8.7 Final premium cleanup.
from pathlib import Path
import zipfile, shutil, re, subprocess
base_zip = Path(“/mnt/data/rx-theme-v8-4-disease-contact-newsletter.zip”)
fallbacks = [
Path(“/mnt/data/rx-theme-v8-2-control-customizer-blocks.zip”),
Path(“/mnt/data/rx-theme-v7-9-ad-manager.zip”),
]
if not base_zip.exists():
for f in fallbacks:
if f.exists():
base_zip = f
break
work_root = Path(“/mnt/data/rx_v87_final_build”)
extract_dir = work_root / “extract”
if work_root.exists():
shutil.rmtree(work_root)
work_root.mkdir(parents=True)
extract_dir.mkdir(parents=True)
with zipfile.ZipFile(base_zip, “r”) as z:
z.extractall(extract_dir)
theme_roots = [p.parent for p in extract_dir.rglob(“style.css”) if (p.parent / “functions.php”).exists()]
if not theme_roots:
raise RuntimeError(“Theme root not found.”)
src_root = theme_roots[0]
theme_root = work_root / “rx-theme-v8-7-final-premium-polish”
shutil.copytree(src_root, theme_root)
def read(path):
return path.read_text(encoding=”utf-8″, errors=”ignore”)
def write(path, text):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text, encoding=”utf-8″)
# Update style.css
style_path = theme_root / “style.css”
if style_path.exists():
s = read(style_path)
s = re.sub(r”Theme Name:\s*.*”, “Theme Name: RX Theme v8.7 Final Premium Polish”, s, count=1) if “Theme Name:” in s else “/*\nTheme Name: RX Theme v8.7 Final Premium Polish\nVersion: 8.7\n*/\n” + s
s = re.sub(r”Version:\s*.*”, “Version: 8.7”, s, count=1) if “Version:” in s else s.replace(“*/”, “Version: 8.7\n*/”, 1)
write(style_path, s)
# Directories
inc_compat = theme_root / “inc/compatibility”
inc_access = theme_root / “inc/accessibility”
inc_polish = theme_root / “inc/polish”
assets_css_front = theme_root / “assets/css/frontend”
assets_js_front = theme_root / “assets/js/frontend”
assets_css_backend = theme_root / “assets/css/backend”
for d in [inc_compat, inc_access, inc_polish, assets_css_front, assets_js_front, assets_css_backend]:
d.mkdir(parents=True, exist_ok=True)
# v8.5 WooCommerce / Elementor compatibility
write(inc_compat / “woocommerce-elementor.php”, r”'<?php
/**
* RX Theme v8.5 WooCommerce + Elementor Compatibility Polish.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v85_woocommerce_support() {
add_theme_support(
‘woocommerce’,
array(
‘thumbnail_image_width’ => 420,
‘single_image_width’ => 720,
‘product_grid’ => array(
‘default_rows’ => 3,
‘min_rows’ => 1,
‘max_rows’ => 6,
‘default_columns’ => 3,
‘min_columns’ => 1,
‘max_columns’ => 4,
),
)
);
add_theme_support( ‘wc-product-gallery-zoom’ );
add_theme_support( ‘wc-product-gallery-lightbox’ );
add_theme_support( ‘wc-product-gallery-slider’ );
}
add_action( ‘after_setup_theme’, ‘rx_theme_v85_woocommerce_support’ );
function rx_theme_v85_woocommerce_wrappers_start() {
echo ‘<main id=”primary” class=”rx-main rx-woocommerce-page”><div class=”rx-container”>’;
}
remove_action( ‘woocommerce_before_main_content’, ‘woocommerce_output_content_wrapper’, 10 );
add_action( ‘woocommerce_before_main_content’, ‘rx_theme_v85_woocommerce_wrappers_start’, 10 );
function rx_theme_v85_woocommerce_wrappers_end() {
echo ‘</div></main>’;
}
remove_action( ‘woocommerce_after_main_content’, ‘woocommerce_output_content_wrapper_end’, 10 );
add_action( ‘woocommerce_after_main_content’, ‘rx_theme_v85_woocommerce_wrappers_end’, 10 );
function rx_theme_v85_woocommerce_columns() {
return 3;
}
add_filter( ‘loop_shop_columns’, ‘rx_theme_v85_woocommerce_columns’ );
function rx_theme_v85_woocommerce_products_per_page() {
return 12;
}
add_filter( ‘loop_shop_per_page’, ‘rx_theme_v85_woocommerce_products_per_page’, 20 );
function rx_theme_v85_body_classes( $classes ) {
if ( class_exists( ‘WooCommerce’ ) && ( is_woocommerce() || is_cart() || is_checkout() || is_account_page() ) ) {
$classes[] = ‘rx-has-woocommerce’;
}
if ( did_action( ‘elementor/loaded’ ) ) {
$classes[] = ‘rx-has-elementor’;
}
return $classes;
}
add_filter( ‘body_class’, ‘rx_theme_v85_body_classes’ );
function rx_theme_v85_elementor_support() {
add_theme_support( ‘elementor’ );
}
add_action( ‘after_setup_theme’, ‘rx_theme_v85_elementor_support’ );
function rx_theme_v85_elementor_locations( $elementor_theme_manager ) {
if ( method_exists( $elementor_theme_manager, ‘register_all_core_location’ ) ) {
$elementor_theme_manager->register_all_core_location();
}
}
add_action( ‘elementor/theme/register_locations’, ‘rx_theme_v85_elementor_locations’ );
function rx_theme_v85_compat_assets() {
$theme_uri = get_template_directory_uri();
$theme_dir = get_template_directory();
$css_rel = ‘/assets/css/frontend/compatibility-polish.css’;
if ( file_exists( $theme_dir . $css_rel ) ) {
wp_enqueue_style(
‘rx-theme-compatibility-polish’,
$theme_uri . $css_rel,
array(),
filemtime( $theme_dir . $css_rel )
);
}
}
add_action( ‘wp_enqueue_scripts’, ‘rx_theme_v85_compat_assets’, 44 );
”’)
write(assets_css_front / “compatibility-polish.css”, r”’/* RX Theme v8.5 WooCommerce + Elementor Compatibility */
.rx-woocommerce-page{
padding:36px 0 64px;
}
.rx-has-woocommerce .woocommerce .products{
display:grid;
grid-template-columns:repeat(3,minmax(0,1fr));
gap:24px;
}
.rx-has-woocommerce .woocommerce ul.products::before,
.rx-has-woocommerce .woocommerce ul.products::after{
display:none;
}
.rx-has-woocommerce .woocommerce ul.products li.product{
width:auto!important;
margin:0!important;
padding:18px;
border:1px solid #e5e7eb;
border-radius:24px;
background:#fff;
box-shadow:0 14px 40px rgba(15,23,42,.06);
transition:transform .18s ease, box-shadow .18s ease;
}
.rx-has-woocommerce .woocommerce ul.products li.product:hover{
transform:translateY(-3px);
box-shadow:0 24px 60px rgba(15,23,42,.12);
}
.rx-has-woocommerce .woocommerce ul.products li.product img{
border-radius:18px;
}
.rx-has-woocommerce .woocommerce ul.products li.product .woocommerce-loop-product__title{
color:var(–rx-heading-color,#111827);
font-size:18px;
line-height:1.3;
}
.rx-has-woocommerce .woocommerce a.button,
.rx-has-woocommerce .woocommerce button.button,
.rx-has-woocommerce .woocommerce input.button,
.rx-has-woocommerce .woocommerce #respond input#submit{
border-radius:999px;
background:linear-gradient(135deg,var(–rx-primary-color,#0066cc),var(–rx-secondary-color,#00a884));
color:#fff;
font-weight:900;
border:0;
}
.rx-has-woocommerce .woocommerce-message,
.rx-has-woocommerce .woocommerce-info,
.rx-has-woocommerce .woocommerce-error{
border-radius:18px;
border-top:0;
box-shadow:0 12px 32px rgba(15,23,42,.07);
}
.rx-has-elementor .elementor-section.elementor-section-boxed > .elementor-container{
max-width:var(–rx-container-width,1200px);
}
body.rx-dark-mode.rx-has-woocommerce .woocommerce ul.products li.product,
body.rx-dark-mode.rx-has-woocommerce .woocommerce div.product,
body.rx-dark-mode.rx-has-woocommerce .woocommerce-message,
body.rx-dark-mode.rx-has-woocommerce .woocommerce-info{
background:#111827;
border-color:#243244;
color:#e5e7eb;
}
body.rx-dark-mode.rx-has-woocommerce .woocommerce ul.products li.product .woocommerce-loop-product__title{
color:#fff;
}
@media(max-width:980px){
.rx-has-woocommerce .woocommerce .products{
grid-template-columns:repeat(2,minmax(0,1fr));
}
}
@media(max-width:620px){
.rx-has-woocommerce .woocommerce .products{
grid-template-columns:1fr;
}
}
”’)
# v8.6 Accessibility advanced tools
write(inc_access / “accessibility-tools.php”, r”'<?php
/**
* RX Theme v8.6 Accessibility Advanced Tools.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v86_skip_links() {
?>
<a class=”rx-skip-link screen-reader-text” href=”#primary”><?php esc_html_e( ‘Skip to main content’, ‘rx-theme’ ); ?></a>
<a class=”rx-skip-link screen-reader-text” href=”#site-navigation”><?php esc_html_e( ‘Skip to navigation’, ‘rx-theme’ ); ?></a>
<?php
}
add_action( ‘wp_body_open’, ‘rx_theme_v86_skip_links’, 1 );
function rx_theme_v86_accessibility_toolbar() {
if ( is_admin() ) {
return;
}
?>
<div class=”rx-accessibility-toolbar” aria-label=”<?php esc_attr_e( ‘Accessibility tools’, ‘rx-theme’ ); ?>”>
<button type=”button” class=”rx-a11y-toggle” aria-expanded=”false” aria-controls=”rx-a11y-panel”>
<?php esc_html_e( ‘Accessibility’, ‘rx-theme’ ); ?>
</button>
<div id=”rx-a11y-panel” class=”rx-a11y-panel” hidden>
<button type=”button” data-rx-a11y=”font-plus”><?php esc_html_e( ‘A+’, ‘rx-theme’ ); ?></button>
<button type=”button” data-rx-a11y=”font-minus”><?php esc_html_e( ‘A-‘, ‘rx-theme’ ); ?></button>
<button type=”button” data-rx-a11y=”contrast”><?php esc_html_e( ‘High Contrast’, ‘rx-theme’ ); ?></button>
<button type=”button” data-rx-a11y=”underline”><?php esc_html_e( ‘Underline Links’, ‘rx-theme’ ); ?></button>
<button type=”button” data-rx-a11y=”motion”><?php esc_html_e( ‘Reduce Motion’, ‘rx-theme’ ); ?></button>
<button type=”button” data-rx-a11y=”reset”><?php esc_html_e( ‘Reset’, ‘rx-theme’ ); ?></button>
</div>
</div>
<?php
}
add_action( ‘wp_footer’, ‘rx_theme_v86_accessibility_toolbar’, 30 );
function rx_theme_v86_accessibility_assets() {
$theme_uri = get_template_directory_uri();
$theme_dir = get_template_directory();
$css_rel = ‘/assets/css/frontend/accessibility-tools.css’;
$js_rel = ‘/assets/js/frontend/accessibility-tools.js’;
if ( file_exists( $theme_dir . $css_rel ) ) {
wp_enqueue_style(
‘rx-theme-accessibility-tools’,
$theme_uri . $css_rel,
array(),
filemtime( $theme_dir . $css_rel )
);
}
if ( file_exists( $theme_dir . $js_rel ) ) {
wp_enqueue_script(
‘rx-theme-accessibility-tools’,
$theme_uri . $js_rel,
array(),
filemtime( $theme_dir . $js_rel ),
true
);
}
}
add_action( ‘wp_enqueue_scripts’, ‘rx_theme_v86_accessibility_assets’, 45 );
function rx_theme_v86_nav_id_fallback( $items, $args ) {
return $items;
}
add_filter( ‘wp_nav_menu_items’, ‘rx_theme_v86_nav_id_fallback’, 10, 2 );
”’)
write(assets_css_front / “accessibility-tools.css”, r”’/* RX Theme v8.6 Accessibility Advanced Tools */
.rx-skip-link:focus{
position:fixed!important;
top:12px;
left:12px;
z-index:100000;
width:auto;
height:auto;
padding:12px 16px;
clip:auto;
border-radius:10px;
background:#111827;
color:#fff;
font-weight:900;
text-decoration:none;
}
.rx-accessibility-toolbar{
position:fixed;
right:18px;
bottom:86px;
z-index:9998;
font-family:inherit;
}
.rx-a11y-toggle{
border:0;
border-radius:999px;
padding:11px 15px;
background:#111827;
color:#fff;
font-weight:900;
cursor:pointer;
box-shadow:0 14px 34px rgba(15,23,42,.24);
}
.rx-a11y-panel{
position:absolute;
right:0;
bottom:52px;
display:grid;
gap:8px;
width:210px;
padding:12px;
border:1px solid #e5e7eb;
border-radius:18px;
background:#fff;
box-shadow:0 24px 70px rgba(15,23,42,.2);
}
.rx-a11y-panel button{
width:100%;
border:1px solid #dbeafe;
border-radius:12px;
padding:10px 12px;
background:#eff6ff;
color:var(–rx-primary-color,#0066cc);
font-weight:900;
cursor:pointer;
}
html.rx-a11y-font-large body{
font-size:112.5%;
}
html.rx-a11y-font-small body{
font-size:93.75%;
}
html.rx-a11y-contrast{
filter:contrast(1.18);
}
html.rx-a11y-contrast body{
background:#fff!important;
color:#000!important;
}
html.rx-a11y-underline a{
text-decoration:underline!important;
text-underline-offset:3px;
}
html.rx-a11y-reduce-motion *,
html.rx-a11y-reduce-motion *::before,
html.rx-a11y-reduce-motion *::after{
animation:none!important;
transition:none!important;
scroll-behavior:auto!important;
}
:focus-visible{
outline:3px solid var(–rx-primary-color,#0066cc);
outline-offset:3px;
}
body.rx-dark-mode .rx-a11y-panel{
background:#111827;
border-color:#243244;
}
@media(max-width:640px){
.rx-accessibility-toolbar{
right:12px;
bottom:74px;
}
.rx-a11y-panel{
width:190px;
}
}
@media print{
.rx-accessibility-toolbar,
.rx-skip-link{
display:none!important;
}
}
”’)
write(assets_js_front / “accessibility-tools.js”, r”’/* RX Theme v8.6 Accessibility Tools */
(function(){
‘use strict’;
const root = document.documentElement;
const toggle = document.querySelector(‘.rx-a11y-toggle’);
const panel = document.getElementById(‘rx-a11y-panel’);
const storageKey = ‘rxA11ySettings’;
function getSettings(){
try {
return JSON.parse(localStorage.getItem(storageKey) || ‘{}’);
} catch(e) {
return {};
}
}
function saveSettings(settings){
try {
localStorage.setItem(storageKey, JSON.stringify(settings));
} catch(e) {}
}
function apply(settings){
root.classList.toggle(‘rx-a11y-font-large’, settings.font === ‘large’);
root.classList.toggle(‘rx-a11y-font-small’, settings.font === ‘small’);
root.classList.toggle(‘rx-a11y-contrast’, !!settings.contrast);
root.classList.toggle(‘rx-a11y-underline’, !!settings.underline);
root.classList.toggle(‘rx-a11y-reduce-motion’, !!settings.motion);
}
apply(getSettings());
if (toggle && panel) {
toggle.addEventListener(‘click’, function(){
const open = toggle.getAttribute(‘aria-expanded’) === ‘true’;
toggle.setAttribute(‘aria-expanded’, open ? ‘false’ : ‘true’);
panel.hidden = open;
});
document.addEventListener(‘keydown’, function(event){
if (event.key === ‘Escape’ && !panel.hidden) {
panel.hidden = true;
toggle.setAttribute(‘aria-expanded’, ‘false’);
toggle.focus();
}
});
}
document.addEventListener(‘click’, function(event){
const button = event.target.closest(‘[data-rx-a11y]’);
if (!button) return;
const action = button.getAttribute(‘data-rx-a11y’);
const settings = getSettings();
if (action === ‘font-plus’) {
settings.font = ‘large’;
} else if (action === ‘font-minus’) {
settings.font = ‘small’;
} else if (action === ‘contrast’) {
settings.contrast = !settings.contrast;
} else if (action === ‘underline’) {
settings.underline = !settings.underline;
} else if (action === ‘motion’) {
settings.motion = !settings.motion;
} else if (action === ‘reset’) {
Object.keys(settings).forEach(function(key){ delete settings[key]; });
}
saveSettings(settings);
apply(settings);
});
})();
”’)
# v8.7 Final cleanup + premium polish
write(inc_polish / “final-premium-polish.php”, r”'<?php
/**
* RX Theme v8.7 Final Cleanup + Premium Polish.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v87_premium_assets() {
$theme_uri = get_template_directory_uri();
$theme_dir = get_template_directory();
$css_rel = ‘/assets/css/frontend/premium-polish.css’;
$js_rel = ‘/assets/js/frontend/premium-polish.js’;
if ( file_exists( $theme_dir . $css_rel ) ) {
wp_enqueue_style(
‘rx-theme-premium-polish’,
$theme_uri . $css_rel,
array(),
filemtime( $theme_dir . $css_rel )
);
}
if ( file_exists( $theme_dir . $js_rel ) ) {
wp_enqueue_script(
‘rx-theme-premium-polish’,
$theme_uri . $js_rel,
array(),
filemtime( $theme_dir . $js_rel ),
true
);
}
}
add_action( ‘wp_enqueue_scripts’, ‘rx_theme_v87_premium_assets’, 99 );
function rx_theme_v87_body_classes( $classes ) {
$classes[] = ‘rx-theme-premium-polish’;
if ( is_singular() ) {
$classes[] = ‘rx-is-singular-premium’;
}
return $classes;
}
add_filter( ‘body_class’, ‘rx_theme_v87_body_classes’ );
function rx_theme_v87_admin_dashboard_notice() {
if ( ! current_user_can( ‘manage_options’ ) ) {
return;
}
$screen = get_current_screen();
if ( ! $screen || ‘appearance_page_rx-theme-import-export’ !== $screen->id ) {
return;
}
echo ‘<div class=”notice notice-success”><p><strong>RX Theme v8.7:</strong> ‘ . esc_html__( ‘Premium polish, compatibility, and accessibility modules are active.’, ‘rx-theme’ ) . ‘</p></div>’;
}
add_action( ‘admin_notices’, ‘rx_theme_v87_admin_dashboard_notice’ );
function rx_theme_v87_add_image_sizes() {
add_image_size( ‘rx-premium-card’, 640, 420, true );
add_image_size( ‘rx-premium-hero’, 1440, 720, true );
}
add_action( ‘after_setup_theme’, ‘rx_theme_v87_add_image_sizes’ );
function rx_theme_v87_excerpt_more() {
return ‘…’;
}
add_filter( ‘excerpt_more’, ‘rx_theme_v87_excerpt_more’ );
function rx_theme_v87_excerpt_length( $length ) {
return 26;
}
add_filter( ‘excerpt_length’, ‘rx_theme_v87_excerpt_length’, 20 );
”’)
write(assets_css_front / “premium-polish.css”, r”’/* RX Theme v8.7 Final Premium Polish */
:root{
–rx-radius-sm:12px;
–rx-radius-md:18px;
–rx-radius-lg:28px;
–rx-shadow-soft:0 14px 40px rgba(15,23,42,.07);
–rx-shadow-strong:0 24px 70px rgba(15,23,42,.14);
}
html{
scroll-behavior:smooth;
}
body.rx-theme-premium-polish{
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
}
.rx-container{
max-width:var(–rx-container-width,1200px);
}
.rx-card,
.rx-widget,
.widget,
.rx-main article{
overflow-wrap:anywhere;
}
.rx-main img,
.entry-content img{
border-radius:var(–rx-radius-md);
}
.rx-main table,
.entry-content table{
width:100%;
border-collapse:collapse;
overflow:hidden;
border-radius:var(–rx-radius-md);
}
.rx-main th,
.rx-main td,
.entry-content th,
.entry-content td{
padding:12px 14px;
border:1px solid #e5e7eb;
}
.rx-main th,
.entry-content th{
background:#f8fafc;
color:var(–rx-heading-color,#111827);
font-weight:900;
}
blockquote{
margin:26px 0;
padding:20px 22px;
border-left:5px solid var(–rx-primary-color,#0066cc);
border-radius:0 var(–rx-radius-md) var(–rx-radius-md) 0;
background:#f8fbff;
color:#334155;
}
.rx-back-to-top{
position:fixed;
right:18px;
bottom:22px;
z-index:9997;
width:46px;
height:46px;
border:0;
border-radius:999px;
background:linear-gradient(135deg,var(–rx-primary-color,#0066cc),var(–rx-secondary-color,#00a884));
color:#fff;
font-weight:1000;
cursor:pointer;
box-shadow:var(–rx-shadow-strong);
opacity:0;
visibility:hidden;
transform:translateY(8px);
transition:opacity .18s ease, visibility .18s ease, transform .18s ease;
}
.rx-back-to-top.is-visible{
opacity:1;
visibility:visible;
transform:translateY(0);
}
body.rx-dark-mode blockquote{
background:#111827;
color:#cbd5e1;
}
body.rx-dark-mode .rx-main th,
body.rx-dark-mode .entry-content th{
background:#111827;
color:#fff;
}
body.rx-dark-mode .rx-main th,
body.rx-dark-mode .rx-main td,
body.rx-dark-mode .entry-content th,
body.rx-dark-mode .entry-content td{
border-color:#243244;
}
@media(max-width:640px){
.rx-back-to-top{
right:12px;
bottom:18px;
width:42px;
height:42px;
}
}
@media print{
.rx-back-to-top,
.rx-accessibility-toolbar,
.rx-site-header,
.rx-site-footer,
.rx-social-share,
.rx-ad-slot{
display:none!important;
}
body{
background:#fff!important;
color:#000!important;
}
a{
color:#000!important;
text-decoration:underline!important;
}
}
”’)
write(assets_js_front / “premium-polish.js”, r”’/* RX Theme v8.7 Final Premium Polish */
(function(){
‘use strict’;
const button = document.createElement(‘button’);
button.type = ‘button’;
button.className = ‘rx-back-to-top’;
button.setAttribute(‘aria-label’, ‘Back to top’);
button.textContent = ‘↑’;
document.body.appendChild(button);
function updateButton(){
if (window.scrollY > 500) {
button.classList.add(‘is-visible’);
} else {
button.classList.remove(‘is-visible’);
}
}
window.addEventListener(‘scroll’, updateButton, { passive: true });
updateButton();
button.addEventListener(‘click’, function(){
window.scrollTo({ top: 0, behavior: ‘smooth’ });
});
document.querySelectorAll(‘a[target=”_blank”]’).forEach(function(link){
const rel = (link.getAttribute(‘rel’) || ”).split(‘ ‘);
if (rel.indexOf(‘noopener’) === -1) rel.push(‘noopener’);
if (rel.indexOf(‘noreferrer’) === -1) rel.push(‘noreferrer’);
link.setAttribute(‘rel’, rel.join(‘ ‘).trim());
});
})();
”’)
# Loader
write(theme_root / “inc/rx-v85-v87-loader.php”, r”'<?php
/**
* RX Theme v8.5-v8.7 final feature loader.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
$rx_theme_v85_v87_files = array(
‘/inc/compatibility/woocommerce-elementor.php’,
‘/inc/accessibility/accessibility-tools.php’,
‘/inc/polish/final-premium-polish.php’,
);
foreach ( $rx_theme_v85_v87_files as $rx_theme_v85_v87_file ) {
$rx_theme_v85_v87_path = get_template_directory() . $rx_theme_v85_v87_file;
if ( file_exists( $rx_theme_v85_v87_path ) ) {
require_once $rx_theme_v85_v87_path;
}
}
”’)
# Add loader to autoload
autoload = theme_root / “inc/autoload.php”
if autoload.exists():
a = read(autoload)
if “/inc/rx-v85-v87-loader.php” not in a:
idx = a.find(“foreach”)
if idx != -1:
before, after = a[:idx], a[idx:]
pos = before.rfind(“);”)
if pos != -1:
before = before[:pos] + ” ‘/inc/rx-v85-v87-loader.php’,\n” + before[pos:]
a = before + after
else:
a += “\nrequire_once RX_THEME_DIR . ‘/inc/rx-v85-v87-loader.php’;\n”
else:
a += “\nrequire_once RX_THEME_DIR . ‘/inc/rx-v85-v87-loader.php’;\n”
write(autoload, a)
else:
write(autoload, “<?php\nif ( ! defined( ‘ABSPATH’ ) ) { exit; }\nrequire_once get_template_directory() . ‘/inc/rx-v85-v87-loader.php’;\n”)
# PHP syntax check
errors = []
for p in theme_root.rglob(“*.php”):
proc = subprocess.run([“php”, “-l”, str(p)], capture_output=True, text=True)
if proc.returncode != 0:
errors.append(str(p) + “\n” + proc.stdout + proc.stderr)
if errors:
raise RuntimeError(“\n”.join(errors[:10]))
# Zip
zip_path = Path(“/mnt/data/rx-theme-v8-7-final-premium-polish.zip”)
if zip_path.exists():
zip_path.unlink()
with zipfile.ZipFile(zip_path, “w”, zipfile.ZIP_DEFLATED) as z:
for p in theme_root.rglob(“*”):
z.write(p, p.relative_to(work_root))
with zipfile.ZipFile(zip_path, “r”) as z:
bad = z.testzip()
if bad:
raise RuntimeError(“Bad ZIP file member: ” + str(bad))
print(“Created:”, zip_path)
print(“PHP syntax check: Passed”)
print(“ZIP integrity test: Passed”)
print(“Added: v8.5 WooCommerce/Elementor, v8.6 Accessibility Tools, v8.7 Final Premium Polish”)
===================================================
Yes dear friend — you can use React, React Router, Vite, Astro.js, and Rust with RX Theme, but not all of them should be added directly inside the WordPress theme.
For RX Theme, the best professional decision is:
React = best for admin panel, dashboard UI, settings UI
React Router = best for multi-page admin app inside WordPress dashboard
Vite = best build tool for CSS/JS/React assets
Astro.js = best for separate headless frontend, not necessary inside normal theme
Rust = best for external tools/API/CLI, not directly inside theme
My best recommendation for RX Theme
For your current WordPress theme, use:
React + React Router + Vite
inside the theme for:
RX Theme dashboard
Theme options UI
SEO settings UI
Ad manager UI
Performance settings UI
Medical article tools UI
Import/export UI
Do not add Astro and Rust directly inside the theme now. They are powerful, but they can make the theme too complex.
Best advanced structure
Add this inside RX Theme:
rx-theme/
├── app/
│ ├── admin/
│ │ ├── package.json
│ │ ├── vite.config.js
│ │ ├── index.html
│ │ └── src/
│ │ ├── main.jsx
│ │ ├── App.jsx
│ │ ├── routes/
│ │ │ ├── Dashboard.jsx
│ │ │ ├── GeneralSettings.jsx
│ │ │ ├── HeaderSettings.jsx
│ │ │ ├── FooterSettings.jsx
│ │ │ ├── SEOSettings.jsx
│ │ │ ├── PerformanceSettings.jsx
│ │ │ ├── AdManager.jsx
│ │ │ ├── MedicalSettings.jsx
│ │ │ └── ImportExport.jsx
│ │ ├── components/
│ │ │ ├── Layout.jsx
│ │ │ ├── Sidebar.jsx
│ │ │ ├── Topbar.jsx
│ │ │ ├── Field.jsx
│ │ │ ├── Toggle.jsx
│ │ │ ├── ColorPicker.jsx
│ │ │ └── SaveButton.jsx
│ │ ├── api/
│ │ │ └── wpApi.js
│ │ └── styles/
│ │ └── admin-app.css
│ │
│ └── frontend/
│ ├── package.json
│ ├── vite.config.js
│ └── src/
│ ├── main.js
│ ├── search.js
│ ├── accessibility.js
│ ├── dark-mode.js
│ ├── performance.js
│ └── styles/
│ └── frontend-app.css
│
├── assets/
│ ├── build/
│ │ ├── admin/
│ │ │ ├── admin-app.js
│ │ │ └── admin-app.css
│ │ └── frontend/
│ │ ├── frontend-app.js
│ │ └── frontend-app.css
│
├── inc/
│ ├── app/
│ │ ├── admin-app.php
│ │ ├── frontend-app.php
│ │ └── rest-api.php
│ │
│ └── rest/
│ ├── settings-rest.php
│ ├── seo-rest.php
│ ├── ads-rest.php
│ └── medical-rest.php
Best use of each app
1. React admin app
Use React for your RX Theme backend dashboard.
Good for:
Appearance → RX Theme
Appearance → RX Ad Manager
Appearance → RX SEO
Appearance → RX Performance
Appearance → RX Medical Settings
React will make the backend feel like a premium theme panel.
2. React Router
Use React Router only inside the admin app.
Example admin routes:
/rx-dashboard
/general
/header
/footer
/seo
/performance
/ads
/medical
/import-export
This gives you a modern dashboard without creating many separate PHP pages.
3. Vite
Vite is excellent for building your theme assets.
Use Vite to compile:
React admin JS
Admin CSS
Frontend JS
Frontend CSS
Search scripts
Accessibility scripts
Dark mode scripts
Performance scripts
Final built files should go here:
assets/build/admin/
assets/build/frontend/
WordPress should load only the built files, not the raw React source.
4. Astro.js
Astro is great, but for RX Theme I do not recommend adding it directly now.
Use Astro only if you want:
Headless WordPress frontend
Static medical website
Separate frontend hosted outside WordPress
Ultra-fast article frontend
Astro structure should be outside the theme:
rx-astro-frontend/
├── src/
├── public/
├── astro.config.mjs
└── package.json
Then it connects to WordPress by REST API.
5. Rust app
Rust is not for normal WordPress theme backend UI.
Use Rust separately for advanced tools:
Medical content analyzer
SEO crawler
Sitemap generator
Broken link checker
Image optimizer
Performance scanner
Keyword extractor
Rust should be outside the theme:
rx-rust-tools/
├── Cargo.toml
└── src/
└── main.rs
Then you run it locally or on server as a separate tool.
Best RX Theme app architecture
Use this:
WordPress Theme PHP
↓
WordPress REST API
↓
React Admin App
↓
Vite Build
↓
assets/build/admin/admin-app.js
This is the correct premium-theme architecture.
Do not do this
Avoid this:
Putting node_modules inside theme ❌
Putting Rust binary inside theme ❌
Putting Astro full app inside theme ❌
Loading React from development server on live site ❌
Replacing WordPress backend fully with React ❌
Best final structure for RX Theme
rx-theme/
├── app/
│ ├── admin-react/
│ ├── frontend-vite/
│ └── README.md
│
├── assets/
│ ├── build/
│ │ ├── admin/
│ │ └── frontend/
│ ├── css/
│ ├── js/
│ └── images/
│
├── inc/
│ ├── app/
│ │ ├── admin-react-loader.php
│ │ ├── frontend-vite-loader.php
│ │ └── rest-api-loader.php
│ ├── rest/
│ │ ├── settings-controller.php
│ │ ├── seo-controller.php
│ │ ├── ads-controller.php
│ │ └── medical-controller.php
│ └── ...
My honest advice
For RX Theme now:
Add React + Vite admin system ✅
Add React Router admin pages ✅
Keep Astro separate for future headless project ✅
Keep Rust separate for future developer tools ✅
This is the safest and most professional way.
Your next best version should be:
RX Theme v8.8 = React + Vite Admin App Foundation
It should add:
React admin dashboard structure
Vite build system
WordPress REST API base
Admin app loader
Settings API bridge
Modern dashboard UI placeholder
No node_modules in final ZIP
Safe fallback if build files missing
That will make RX Theme feel like a real premium theme framework, dear friend.
=======================================
from pathlib import Path
import zipfile, shutil, re, subprocess, json
base_zip = Path(“/mnt/data/rx-theme-v8-7-final-premium-polish.zip”)
fallbacks = [
Path(“/mnt/data/rx-theme-v8-4-disease-contact-newsletter.zip”),
Path(“/mnt/data/rx-theme-v8-2-control-customizer-blocks.zip”),
]
if not base_zip.exists():
for f in fallbacks:
if f.exists():
base_zip = f
break
work_root = Path(“/mnt/data/rx_v88_app_dist_build”)
extract_dir = work_root / “extract”
if work_root.exists():
shutil.rmtree(work_root)
work_root.mkdir(parents=True)
extract_dir.mkdir(parents=True)
with zipfile.ZipFile(base_zip, “r”) as z:
z.extractall(extract_dir)
theme_roots = [p.parent for p in extract_dir.rglob(“style.css”) if (p.parent / “functions.php”).exists()]
if not theme_roots:
raise RuntimeError(“Theme root not found.”)
src_root = theme_roots[0]
theme_root = work_root / “rx-theme-v8-8-app-dist-integration”
shutil.copytree(src_root, theme_root)
def read(path):
return path.read_text(encoding=”utf-8″, errors=”ignore”)
def write(path, text):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text, encoding=”utf-8″)
# Update style.css
style_path = theme_root / “style.css”
if style_path.exists():
s = read(style_path)
s = re.sub(r”Theme Name:\s*.*”, “Theme Name: RX Theme v8.8 App Dist Integration”, s, count=1) if “Theme Name:” in s else “/*\nTheme Name: RX Theme v8.8 App Dist Integration\nVersion: 8.8\n*/\n” + s
s = re.sub(r”Version:\s*.*”, “Version: 8.8”, s, count=1) if “Version:” in s else s.replace(“*/”, “Version: 8.8\n*/”, 1)
write(style_path, s)
# Directories
dirs = [
“app/admin-react/src”,
“app/admin-react/dist”,
“app/frontend-vite/src”,
“app/frontend-vite/dist”,
“app/astro-headless-note”,
“app/rust-tools-note”,
“assets/build/admin”,
“assets/build/frontend”,
“inc/app”,
“inc/rest”,
]
for d in dirs:
(theme_root / d).mkdir(parents=True, exist_ok=True)
# README
write(theme_root / “app/README.md”, “””# RX Theme App System
This folder is for modern app source files.
Recommended:
– `app/admin-react/` = React / React Router / Vite admin dashboard source.
– `app/frontend-vite/` = frontend Vite source.
– `assets/build/admin/` = final small built admin CSS/JS loaded by WordPress.
– `assets/build/frontend/` = final small built frontend CSS/JS loaded by WordPress.
Do not upload `node_modules` to WordPress hosting.
Build idea:
1. Work in app/admin-react or app/frontend-vite locally.
2. Run your build command.
3. Copy final dist CSS/JS into assets/build/admin or assets/build/frontend.
4. WordPress loads only the built files.
“””)
# package/vite placeholders
write(theme_root / “app/admin-react/package.json”, json.dumps({
“name”: “rx-theme-admin-react”,
“version”: “1.0.0”,
“private”: True,
“scripts”: {
“dev”: “vite”,
“build”: “vite build”
},
“dependencies”: {
“@vitejs/plugin-react”: “latest”,
“vite”: “latest”,
“react”: “latest”,
“react-dom”: “latest”,
“react-router-dom”: “latest”
},
“devDependencies”: {}
}, indent=2))
write(theme_root / “app/admin-react/vite.config.js”, “””import { defineConfig } from ‘vite’;
import react from ‘@vitejs/plugin-react’;
export default defineConfig({
plugins: [react()],
build: {
outDir: ‘../../assets/build/admin’,
emptyOutDir: false,
rollupOptions: {
input: ‘./src/main.jsx’,
output: {
entryFileNames: ‘admin-app.js’,
assetFileNames: ‘admin-app.css’
}
}
}
});
“””)
write(theme_root / “app/admin-react/src/main.jsx”, “””import React from ‘react’;
import { createRoot } from ‘react-dom/client’;
import ‘./admin-app.css’;
function App() {
return (
<div className=”rx-admin-app”>
<h1>RX Theme Admin App</h1>
<p>Build your React admin dashboard here, then copy dist files to assets/build/admin.</p>
</div>
);
}
createRoot(document.getElementById(‘rx-admin-app-root’)).render(<App />);
“””)
write(theme_root / “app/admin-react/src/admin-app.css”, “””.rx-admin-app{padding:24px;border-radius:18px;background:#fff;box-shadow:0 12px 32px rgba(15,23,42,.08)}”””)
write(theme_root / “app/frontend-vite/package.json”, json.dumps({
“name”: “rx-theme-frontend-vite”,
“version”: “1.0.0”,
“private”: True,
“scripts”: {
“dev”: “vite”,
“build”: “vite build”
},
“dependencies”: {
“vite”: “latest”
},
“devDependencies”: {}
}, indent=2))
write(theme_root / “app/frontend-vite/vite.config.js”, “””import { defineConfig } from ‘vite’;
export default defineConfig({
build: {
outDir: ‘../../assets/build/frontend’,
emptyOutDir: false,
rollupOptions: {
input: ‘./src/main.js’,
output: {
entryFileNames: ‘frontend-app.js’,
assetFileNames: ‘frontend-app.css’
}
}
}
});
“””)
write(theme_root / “app/frontend-vite/src/main.js”, “””import ‘./frontend-app.css’;
document.documentElement.classList.add(‘rx-vite-frontend-ready’);
console.log(‘RX Theme frontend Vite app ready’);
“””)
write(theme_root / “app/frontend-vite/src/frontend-app.css”, “””.rx-vite-frontend-ready body{scroll-behavior:smooth;}”””)
write(theme_root / “app/astro-headless-note/README.md”, “””# Astro.js note
Keep Astro outside the WordPress theme for a future headless frontend.
Recommended external structure:
rx-astro-frontend/
├── src/
├── public/
├── astro.config.mjs
└── package.json
Astro should fetch WordPress content through the REST API.
“””)
write(theme_root / “app/rust-tools-note/README.md”, “””# Rust tools note
Keep Rust outside the WordPress theme.
Good Rust tools for RX Theme:
– SEO crawler
– broken link checker
– image optimizer
– sitemap checker
– medical keyword extractor
Do not place Rust binaries inside the public WordPress theme unless you know the server environment.
“””)
# Built admin dist CSS/JS (small, no node_modules)
admin_css = r”’/* RX Theme v8.8 Built Admin App CSS */
.rx-app-admin-wrap{
margin:22px 20px 0 0;
}
.rx-app-shell{
display:grid;
grid-template-columns:260px minmax(0,1fr);
gap:20px;
max-width:1280px;
}
.rx-app-sidebar,
.rx-app-main-card{
background:#fff;
border:1px solid #dcdcde;
border-radius:18px;
box-shadow:0 12px 32px rgba(15,23,42,.06);
}
.rx-app-sidebar{
padding:18px;
}
.rx-app-sidebar h2{
margin:0 0 14px;
color:#0f172a;
}
.rx-app-nav{
display:grid;
gap:8px;
}
.rx-app-nav button{
width:100%;
text-align:left;
border:1px solid #e5e7eb;
border-radius:12px;
padding:11px 12px;
background:#f8fafc;
color:#0f172a;
font-weight:700;
cursor:pointer;
}
.rx-app-nav button.is-active{
background:#0066cc;
border-color:#0066cc;
color:#fff;
}
.rx-app-main-card{
padding:24px;
}
.rx-app-status-grid{
display:grid;
grid-template-columns:repeat(3,minmax(0,1fr));
gap:14px;
margin:18px 0;
}
.rx-app-stat{
padding:18px;
border-radius:16px;
background:#f8fafc;
border:1px solid #e5e7eb;
}
.rx-app-stat strong{
display:block;
font-size:26px;
color:#0066cc;
}
.rx-app-field{
margin:16px 0;
}
.rx-app-field label{
display:block;
margin-bottom:7px;
font-weight:800;
}
.rx-app-field input,
.rx-app-field textarea{
width:100%;
max-width:720px;
border:1px solid #cbd5e1;
border-radius:12px;
padding:10px 12px;
}
.rx-app-save{
border:0;
border-radius:999px;
padding:12px 18px;
background:#0066cc;
color:#fff;
font-weight:900;
cursor:pointer;
}
.rx-app-notice{
margin-top:12px;
font-weight:800;
color:#047857;
}
@media(max-width:900px){
.rx-app-shell{
grid-template-columns:1fr;
}
.rx-app-status-grid{
grid-template-columns:1fr;
}
}
”’
admin_js = r”’/* RX Theme v8.8 Built Admin App JS – small dist file */
(function(){
‘use strict’;
const root = document.getElementById(‘rx-admin-app-root’);
if (!root) return;
const pages = {
dashboard: {
title: ‘RX Theme Dashboard’,
body: `
<p>Modern app foundation is active. This is the safe built admin app loaded from <code>assets/build/admin/admin-app.js</code>.</p>
<div class=”rx-app-status-grid”>
<div class=”rx-app-stat”><strong>8.8</strong><span>Theme App Version</span></div>
<div class=”rx-app-stat”><strong>Safe</strong><span>No node_modules loaded</span></div>
<div class=”rx-app-stat”><strong>REST</strong><span>API bridge ready</span></div>
</div>
`
},
settings: {
title: ‘Quick Settings’,
body: `
<p>These fields demonstrate the app-to-WordPress REST bridge.</p>
<div class=”rx-app-field”>
<label for=”rx-app-primary”>Primary Color</label>
<input id=”rx-app-primary” type=”text” value=”#0066cc”>
</div>
<div class=”rx-app-field”>
<label for=”rx-app-note”>Admin Note</label>
<textarea id=”rx-app-note” rows=”4″>RX Theme app system is ready.</textarea>
</div>
<button class=”rx-app-save” type=”button” data-rx-save-demo>Save Demo Settings</button>
<div class=”rx-app-notice” aria-live=”polite”></div>
`
},
structure: {
title: ‘App Structure’,
body: `
<p>Use this professional structure:</p>
<pre>app/admin-react/dist/
app/frontend-vite/dist/
assets/build/admin/
assets/build/frontend/
inc/app/
inc/rest/</pre>
<p>WordPress loads only the small files from <code>assets/build</code>.</p>
`
},
future: {
title: ‘Astro + Rust Future’,
body: `
<p>Astro and Rust should stay outside the theme for future advanced tools.</p>
<ul>
<li>Astro = future headless frontend</li>
<li>Rust = SEO crawler, image optimizer, broken link checker</li>
</ul>
`
}
};
function render(page){
const data = pages[page] || pages.dashboard;
root.innerHTML = `
<div class=”rx-app-shell”>
<aside class=”rx-app-sidebar”>
<h2>RX App</h2>
<div class=”rx-app-nav”>
<button data-page=”dashboard”>Dashboard</button>
<button data-page=”settings”>Quick Settings</button>
<button data-page=”structure”>Structure</button>
<button data-page=”future”>Astro/Rust Future</button>
</div>
</aside>
<section class=”rx-app-main-card”>
<h1>${data.title}</h1>
${data.body}
</section>
</div>
`;
root.querySelectorAll(‘[data-page]’).forEach(function(btn){
btn.classList.toggle(‘is-active’, btn.getAttribute(‘data-page’) === page);
btn.addEventListener(‘click’, function(){
render(btn.getAttribute(‘data-page’));
});
});
const save = root.querySelector(‘[data-rx-save-demo]’);
if (save) {
save.addEventListener(‘click’, function(){
const notice = root.querySelector(‘.rx-app-notice’);
const payload = {
primary_color: document.getElementById(‘rx-app-primary’).value,
admin_note: document.getElementById(‘rx-app-note’).value
};
if (!window.rxThemeApp || !rxThemeApp.restUrl) {
notice.textContent = ‘REST config missing, but UI is working.’;
return;
}
fetch(rxThemeApp.restUrl + ‘settings’, {
method: ‘POST’,
headers: {
‘Content-Type’: ‘application/json’,
‘X-WP-Nonce’: rxThemeApp.nonce
},
body: JSON.stringify(payload)
}).then(function(res){
return res.json();
}).then(function(){
notice.textContent = ‘Demo settings saved.’;
}).catch(function(){
notice.textContent = ‘Could not save, but app UI is working.’;
});
});
}
}
render(‘dashboard’);
})();
”’
frontend_css = r”’/* RX Theme v8.8 Built Frontend App CSS */
.rx-vite-frontend-ready .rx-app-enhanced{
outline:0;
}
.rx-app-toast{
position:fixed;
left:50%;
bottom:24px;
transform:translateX(-50%);
z-index:10000;
padding:12px 16px;
border-radius:999px;
background:#111827;
color:#fff;
font-weight:800;
box-shadow:0 18px 48px rgba(15,23,42,.25);
opacity:0;
pointer-events:none;
transition:opacity .18s ease, transform .18s ease;
}
.rx-app-toast.is-visible{
opacity:1;
transform:translateX(-50%) translateY(-6px);
}
”’
frontend_js = r”’/* RX Theme v8.8 Built Frontend App JS – small dist file */
(function(){
‘use strict’;
document.documentElement.classList.add(‘rx-vite-frontend-ready’);
function toast(message){
let el = document.querySelector(‘.rx-app-toast’);
if (!el) {
el = document.createElement(‘div’);
el.className = ‘rx-app-toast’;
el.setAttribute(‘role’, ‘status’);
el.setAttribute(‘aria-live’, ‘polite’);
document.body.appendChild(el);
}
el.textContent = message;
el.classList.add(‘is-visible’);
setTimeout(function(){
el.classList.remove(‘is-visible’);
}, 2200);
}
document.addEventListener(‘click’, function(event){
const copy = event.target.closest(‘[data-rx-copy-current-url]’);
if (!copy) return;
event.preventDefault();
const url = window.location.href;
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(function(){
toast(‘Page link copied.’);
});
} else {
window.prompt(‘Copy this link:’, url);
}
});
})();
”’
# Write in assets/build and app dist
for path in [
theme_root / “assets/build/admin/admin-app.css”,
theme_root / “app/admin-react/dist/admin-app.css”,
]:
write(path, admin_css)
for path in [
theme_root / “assets/build/admin/admin-app.js”,
theme_root / “app/admin-react/dist/admin-app.js”,
]:
write(path, admin_js)
for path in [
theme_root / “assets/build/frontend/frontend-app.css”,
theme_root / “app/frontend-vite/dist/frontend-app.css”,
]:
write(path, frontend_css)
for path in [
theme_root / “assets/build/frontend/frontend-app.js”,
theme_root / “app/frontend-vite/dist/frontend-app.js”,
]:
write(path, frontend_js)
# PHP App loader
write(theme_root / “inc/app/admin-app-loader.php”, r”'<?php
/**
* RX Theme v8.8 Admin App Loader.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v88_admin_app_menu() {
add_theme_page(
esc_html__( ‘RX App Dashboard’, ‘rx-theme’ ),
esc_html__( ‘RX App Dashboard’, ‘rx-theme’ ),
‘manage_options’,
‘rx-theme-app-dashboard’,
‘rx_theme_v88_admin_app_page’
);
}
add_action( ‘admin_menu’, ‘rx_theme_v88_admin_app_menu’, 20 );
function rx_theme_v88_admin_app_page() {
if ( ! current_user_can( ‘manage_options’ ) ) {
return;
}
echo ‘<div class=”wrap rx-app-admin-wrap”>’;
echo ‘<div id=”rx-admin-app-root”>’;
echo ‘<h1>’ . esc_html__( ‘RX Theme App Dashboard’, ‘rx-theme’ ) . ‘</h1>’;
echo ‘<p>’ . esc_html__( ‘Loading RX Theme admin app…’, ‘rx-theme’ ) . ‘</p>’;
echo ‘</div>’;
echo ‘</div>’;
}
function rx_theme_v88_admin_app_assets( $hook ) {
if ( ‘appearance_page_rx-theme-app-dashboard’ !== $hook ) {
return;
}
$theme_uri = get_template_directory_uri();
$theme_dir = get_template_directory();
$css_rel = ‘/assets/build/admin/admin-app.css’;
$js_rel = ‘/assets/build/admin/admin-app.js’;
if ( file_exists( $theme_dir . $css_rel ) ) {
wp_enqueue_style(
‘rx-theme-admin-app’,
$theme_uri . $css_rel,
array(),
filemtime( $theme_dir . $css_rel )
);
}
if ( file_exists( $theme_dir . $js_rel ) ) {
wp_enqueue_script(
‘rx-theme-admin-app’,
$theme_uri . $js_rel,
array(),
filemtime( $theme_dir . $js_rel ),
true
);
wp_localize_script(
‘rx-theme-admin-app’,
‘rxThemeApp’,
array(
‘restUrl’ => esc_url_raw( rest_url( ‘rx-theme/v1/’ ) ),
‘nonce’ => wp_create_nonce( ‘wp_rest’ ),
)
);
}
}
add_action( ‘admin_enqueue_scripts’, ‘rx_theme_v88_admin_app_assets’ );
”’)
write(theme_root / “inc/app/frontend-app-loader.php”, r”'<?php
/**
* RX Theme v8.8 Frontend App Loader.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v88_frontend_app_assets() {
if ( is_admin() ) {
return;
}
$theme_uri = get_template_directory_uri();
$theme_dir = get_template_directory();
$css_rel = ‘/assets/build/frontend/frontend-app.css’;
$js_rel = ‘/assets/build/frontend/frontend-app.js’;
if ( file_exists( $theme_dir . $css_rel ) ) {
wp_enqueue_style(
‘rx-theme-frontend-app’,
$theme_uri . $css_rel,
array(),
filemtime( $theme_dir . $css_rel )
);
}
if ( file_exists( $theme_dir . $js_rel ) ) {
wp_enqueue_script(
‘rx-theme-frontend-app’,
$theme_uri . $js_rel,
array(),
filemtime( $theme_dir . $js_rel ),
true
);
}
}
add_action( ‘wp_enqueue_scripts’, ‘rx_theme_v88_frontend_app_assets’, 120 );
”’)
write(theme_root / “inc/rest/app-rest-api.php”, r”'<?php
/**
* RX Theme v8.8 REST API Bridge.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v88_register_rest_routes() {
register_rest_route(
‘rx-theme/v1’,
‘/settings’,
array(
array(
‘methods’ => WP_REST_Server::READABLE,
‘callback’ => ‘rx_theme_v88_rest_get_settings’,
‘permission_callback’ => ‘rx_theme_v88_rest_permissions’,
),
array(
‘methods’ => WP_REST_Server::CREATABLE,
‘callback’ => ‘rx_theme_v88_rest_save_settings’,
‘permission_callback’ => ‘rx_theme_v88_rest_permissions’,
),
)
);
}
add_action( ‘rest_api_init’, ‘rx_theme_v88_register_rest_routes’ );
function rx_theme_v88_rest_permissions() {
return current_user_can( ‘manage_options’ );
}
function rx_theme_v88_rest_get_settings() {
return rest_ensure_response(
array(
‘theme’ => wp_get_theme()->get( ‘Name’ ),
‘version’ => wp_get_theme()->get( ‘Version’ ),
‘demo’ => get_option( ‘rx_theme_v88_demo_settings’, array() ),
‘status’ => ‘ready’,
)
);
}
function rx_theme_v88_rest_save_settings( WP_REST_Request $request ) {
$params = $request->get_json_params();
$settings = array(
‘primary_color’ => isset( $params[‘primary_color’] ) ? sanitize_text_field( $params[‘primary_color’] ) : ”,
‘admin_note’ => isset( $params[‘admin_note’] ) ? sanitize_textarea_field( $params[‘admin_note’] ) : ”,
‘updated_at’ => current_time( ‘mysql’ ),
);
update_option( ‘rx_theme_v88_demo_settings’, $settings, false );
return rest_ensure_response(
array(
‘saved’ => true,
‘settings’ => $settings,
)
);
}
”’)
write(theme_root / “inc/app/app-loader.php”, r”'<?php
/**
* RX Theme v8.8 App Dist Integration Loader.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
$rx_theme_v88_app_files = array(
‘/inc/app/admin-app-loader.php’,
‘/inc/app/frontend-app-loader.php’,
‘/inc/rest/app-rest-api.php’,
);
foreach ( $rx_theme_v88_app_files as $rx_theme_v88_file ) {
$rx_theme_v88_path = get_template_directory() . $rx_theme_v88_file;
if ( file_exists( $rx_theme_v88_path ) ) {
require_once $rx_theme_v88_path;
}
}
”’)
# Add loader to autoload
autoload = theme_root / “inc/autoload.php”
if autoload.exists():
a = read(autoload)
if “/inc/app/app-loader.php” not in a:
idx = a.find(“foreach”)
if idx != -1:
before, after = a[:idx], a[idx:]
pos = before.rfind(“);”)
if pos != -1:
before = before[:pos] + ” ‘/inc/app/app-loader.php’,\n” + before[pos:]
a = before + after
else:
a += “\nrequire_once RX_THEME_DIR . ‘/inc/app/app-loader.php’;\n”
else:
a += “\nrequire_once RX_THEME_DIR . ‘/inc/app/app-loader.php’;\n”
write(autoload, a)
else:
write(autoload, “<?php\nif ( ! defined( ‘ABSPATH’ ) ) { exit; }\nrequire_once get_template_directory() . ‘/inc/app/app-loader.php’;\n”)
# PHP syntax check
errors = []
for p in theme_root.rglob(“*.php”):
proc = subprocess.run([“php”, “-l”, str(p)], capture_output=True, text=True)
if proc.returncode != 0:
errors.append(str(p) + “\n” + proc.stdout + proc.stderr)
if errors:
raise RuntimeError(“\n”.join(errors[:10]))
# Zip
zip_path = Path(“/mnt/data/rx-theme-v8-8-app-dist-integration.zip”)
if zip_path.exists():
zip_path.unlink()
with zipfile.ZipFile(zip_path, “w”, zipfile.ZIP_DEFLATED) as z:
for p in theme_root.rglob(“*”):
z.write(p, p.relative_to(work_root))
with zipfile.ZipFile(zip_path, “r”) as z:
bad = z.testzip()
if bad:
raise RuntimeError(“Bad ZIP file member: ” + str(bad))
print(“Created:”, zip_path)
print(“PHP syntax check: Passed”)
print(“ZIP integrity test: Passed”)
print(“Added app/dist structure, built CSS/JS files, admin app loader, frontend app loader, REST bridge”)
====================================================================
Yes dear friend — I understand. Your app build output may have many small files like 50 chunk JS, 50 main/app JS, and 50 CSS. I’ll add a safe loader system with a clean folder structure and an admin table form, so you can paste/add each file easily without editing PHP every time.
from pathlib import Path
import zipfile, shutil, re, subprocess, json
base_zip = Path(“/mnt/data/rx-theme-v8-8-app-dist-integration.zip”)
fallbacks = [
Path(“/mnt/data/rx-theme-v8-7-final-premium-polish.zip”),
Path(“/mnt/data/rx-theme-v8-4-disease-contact-newsletter.zip”),
]
if not base_zip.exists():
for f in fallbacks:
if f.exists():
base_zip = f
break
work_root = Path(“/mnt/data/rx_v89_dist_assets_manager”)
extract_dir = work_root / “extract”
if work_root.exists():
shutil.rmtree(work_root)
work_root.mkdir(parents=True)
extract_dir.mkdir(parents=True)
with zipfile.ZipFile(base_zip, “r”) as z:
z.extractall(extract_dir)
theme_roots = [p.parent for p in extract_dir.rglob(“style.css”) if (p.parent / “functions.php”).exists()]
if not theme_roots:
raise RuntimeError(“Theme root not found.”)
src_root = theme_roots[0]
theme_root = work_root / “rx-theme-v8-9-dist-assets-manager”
shutil.copytree(src_root, theme_root)
def read(path):
return path.read_text(encoding=”utf-8″, errors=”ignore”)
def write(path, text):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text, encoding=”utf-8″)
# Update style.css
style_path = theme_root / “style.css”
if style_path.exists():
s = read(style_path)
s = re.sub(r”Theme Name:\s*.*”, “Theme Name: RX Theme v8.9 Dist Assets Manager”, s, count=1) if “Theme Name:” in s else “/*\nTheme Name: RX Theme v8.9 Dist Assets Manager\nVersion: 8.9\n*/\n” + s
s = re.sub(r”Version:\s*.*”, “Version: 8.9”, s, count=1) if “Version:” in s else s.replace(“*/”, “Version: 8.9\n*/”, 1)
write(style_path, s)
# Directories for 50 chunk js, 50 main js, 50 css
folders = [
“assets/app-dist/chunks”,
“assets/app-dist/main-js”,
“assets/app-dist/css”,
“assets/app-dist/vendor”,
“assets/app-dist/images”,
“assets/app-dist/fonts”,
“assets/css/backend”,
“inc/app”,
]
for folder in folders:
(theme_root / folder).mkdir(parents=True, exist_ok=True)
# Add README/final structure notes
write(theme_root / “assets/app-dist/README.md”, “””# RX Theme App Dist Assets
Put your final build files here.
Recommended folders:
assets/app-dist/
├── chunks/ = chunk JS files from React/Vite/Next/app build
├── main-js/ = main/app JS files
├── css/ = CSS files
├── vendor/ = vendor JS/CSS if needed
├── images/ = built app images
└── fonts/ = built app fonts
Recommended names:
– chunks/chunk-01.js to chunks/chunk-50.js
– main-js/main-01.js to main-js/main-50.js
– css/style-01.css to css/style-50.css
Then go to:
Appearance → RX Dist Assets
You can enable files and paste local paths or full CDN URLs.
“””)
for folder in [“chunks”, “main-js”, “css”, “vendor”, “images”, “fonts”]:
write(theme_root / f”assets/app-dist/{folder}/.gitkeep”, “”)
# Generate sample manifest json with 50 each
manifest = {
“chunks”: [f”assets/app-dist/chunks/chunk-{i:02d}.js” for i in range(1, 51)],
“main_js”: [f”assets/app-dist/main-js/main-{i:02d}.js” for i in range(1, 51)],
“css”: [f”assets/app-dist/css/style-{i:02d}.css” for i in range(1, 51)],
}
write(theme_root / “assets/app-dist/rx-dist-manifest-example.json”, json.dumps(manifest, indent=2))
# Admin CSS
write(theme_root / “assets/css/backend/dist-assets-manager.css”, r”’/* RX Theme v8.9 Dist Assets Manager Admin */
.rx-dist-admin-wrap{
max-width:1400px;
}
.rx-dist-admin-grid{
display:grid;
grid-template-columns:1fr;
gap:20px;
}
.rx-dist-card{
padding:20px;
border:1px solid #dcdcde;
border-radius:16px;
background:#fff;
box-shadow:0 10px 28px rgba(0,0,0,.04);
}
.rx-dist-card h2{
margin-top:0;
}
.rx-dist-table{
width:100%;
border-collapse:collapse;
}
.rx-dist-table th,
.rx-dist-table td{
padding:8px;
border-bottom:1px solid #edf0f2;
vertical-align:middle;
}
.rx-dist-table th{
text-align:left;
font-weight:700;
}
.rx-dist-table input[type=”text”],
.rx-dist-table input[type=”url”],
.rx-dist-table select{
width:100%;
}
.rx-dist-mini{
width:72px!important;
}
.rx-dist-handle{
min-width:170px;
}
.rx-dist-src{
min-width:360px;
}
.rx-dist-note{
padding:12px 14px;
border-left:4px solid #2271b1;
background:#f0f6fc;
}
.rx-dist-tabs{
display:flex;
gap:8px;
margin:18px 0;
flex-wrap:wrap;
}
.rx-dist-tabs a{
padding:9px 12px;
border-radius:999px;
background:#f6f7f7;
text-decoration:none;
font-weight:700;
}
.rx-dist-tabs a:hover{
background:#2271b1;
color:#fff;
}
@media(max-width:1000px){
.rx-dist-table{
display:block;
overflow:auto;
}
}
”’)
# PHP Module
php = r”'<?php
/**
* RX Theme v8.9 Dist Assets Manager.
* Handles 50 chunk JS files, 50 main/app JS files, and 50 CSS files.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v89_dist_asset_types() {
return array(
‘chunks’ => array(
‘label’ => esc_html__( ‘Chunk JS Files’, ‘rx-theme’ ),
‘count’ => 50,
‘default_dir’ => ‘assets/app-dist/chunks’,
‘default_name’ => ‘chunk-%02d.js’,
‘kind’ => ‘script’,
),
‘main_js’ => array(
‘label’ => esc_html__( ‘Main / App JS Files’, ‘rx-theme’ ),
‘count’ => 50,
‘default_dir’ => ‘assets/app-dist/main-js’,
‘default_name’ => ‘main-%02d.js’,
‘kind’ => ‘script’,
),
‘css’ => array(
‘label’ => esc_html__( ‘CSS Files’, ‘rx-theme’ ),
‘count’ => 50,
‘default_dir’ => ‘assets/app-dist/css’,
‘default_name’ => ‘style-%02d.css’,
‘kind’ => ‘style’,
),
);
}
function rx_theme_v89_dist_default_options() {
$defaults = array(
‘enabled’ => ‘0’,
‘load_on_admin’ => ‘0’,
‘items’ => array(),
);
foreach ( rx_theme_v89_dist_asset_types() as $type => $config ) {
$defaults[‘items’][ $type ] = array();
for ( $i = 1; $i <= $config[‘count’]; $i++ ) {
$default_src = $config[‘default_dir’] . ‘/’ . sprintf( $config[‘default_name’], $i );
$defaults[‘items’][ $type ][ $i ] = array(
‘enabled’ => ‘0’,
‘handle’ => ‘rx-‘ . str_replace( ‘_’, ‘-‘, $type ) . ‘-‘ . sprintf( ‘%02d’, $i ),
‘src’ => $default_src,
‘location’ => ‘frontend’,
‘strategy’ => ‘defer’,
‘deps’ => ”,
‘version’ => ”,
);
}
}
return $defaults;
}
function rx_theme_v89_dist_get_options() {
$options = get_option( ‘rx_theme_v89_dist_assets’, array() );
if ( ! is_array( $options ) ) {
$options = array();
}
return wp_parse_args( $options, rx_theme_v89_dist_default_options() );
}
function rx_theme_v89_dist_sanitize_options( $input ) {
$defaults = rx_theme_v89_dist_default_options();
$output = array(
‘enabled’ => ! empty( $input[‘enabled’] ) ? ‘1’ : ‘0’,
‘load_on_admin’ => ! empty( $input[‘load_on_admin’] ) ? ‘1’ : ‘0’,
‘items’ => array(),
);
foreach ( rx_theme_v89_dist_asset_types() as $type => $config ) {
$output[‘items’][ $type ] = array();
for ( $i = 1; $i <= $config[‘count’]; $i++ ) {
$item = isset( $input[‘items’][ $type ][ $i ] ) && is_array( $input[‘items’][ $type ][ $i ] )
? $input[‘items’][ $type ][ $i ]
: array();
$default_item = $defaults[‘items’][ $type ][ $i ];
$location = isset( $item[‘location’] ) ? sanitize_key( $item[‘location’] ) : $default_item[‘location’];
if ( ! in_array( $location, array( ‘frontend’, ‘admin’, ‘both’ ), true ) ) {
$location = ‘frontend’;
}
$strategy = isset( $item[‘strategy’] ) ? sanitize_key( $item[‘strategy’] ) : $default_item[‘strategy’];
if ( ! in_array( $strategy, array( ‘none’, ‘defer’, ‘async’ ), true ) ) {
$strategy = ‘defer’;
}
$output[‘items’][ $type ][ $i ] = array(
‘enabled’ => ! empty( $item[‘enabled’] ) ? ‘1’ : ‘0’,
‘handle’ => isset( $item[‘handle’] ) ? sanitize_key( $item[‘handle’] ) : $default_item[‘handle’],
‘src’ => isset( $item[‘src’] ) ? esc_url_raw( trim( wp_unslash( $item[‘src’] ) ) ) : $default_item[‘src’],
‘location’ => $location,
‘strategy’ => $strategy,
‘deps’ => isset( $item[‘deps’] ) ? sanitize_text_field( wp_unslash( $item[‘deps’] ) ) : ”,
‘version’ => isset( $item[‘version’] ) ? sanitize_text_field( wp_unslash( $item[‘version’] ) ) : ”,
);
if ( empty( $output[‘items’][ $type ][ $i ][‘src’] ) ) {
$output[‘items’][ $type ][ $i ][‘src’] = $default_item[‘src’];
}
}
}
return $output;
}
function rx_theme_v89_dist_register_settings() {
register_setting(
‘rx_theme_v89_dist_assets_group’,
‘rx_theme_v89_dist_assets’,
array(
‘sanitize_callback’ => ‘rx_theme_v89_dist_sanitize_options’,
‘default’ => rx_theme_v89_dist_default_options(),
)
);
}
add_action( ‘admin_init’, ‘rx_theme_v89_dist_register_settings’ );
function rx_theme_v89_dist_admin_menu() {
add_theme_page(
esc_html__( ‘RX Dist Assets’, ‘rx-theme’ ),
esc_html__( ‘RX Dist Assets’, ‘rx-theme’ ),
‘manage_options’,
‘rx-theme-dist-assets’,
‘rx_theme_v89_dist_admin_page’
);
}
add_action( ‘admin_menu’, ‘rx_theme_v89_dist_admin_menu’, 55 );
function rx_theme_v89_dist_admin_assets( $hook ) {
if ( ‘appearance_page_rx-theme-dist-assets’ !== $hook ) {
return;
}
$path = get_template_directory() . ‘/assets/css/backend/dist-assets-manager.css’;
if ( file_exists( $path ) ) {
wp_enqueue_style(
‘rx-theme-dist-assets-manager’,
get_template_directory_uri() . ‘/assets/css/backend/dist-assets-manager.css’,
array(),
filemtime( $path )
);
}
}
add_action( ‘admin_enqueue_scripts’, ‘rx_theme_v89_dist_admin_assets’ );
function rx_theme_v89_dist_admin_page() {
if ( ! current_user_can( ‘manage_options’ ) ) {
return;
}
$options = rx_theme_v89_dist_get_options();
?>
<div class=”wrap rx-dist-admin-wrap”>
<h1><?php esc_html_e( ‘RX Theme Dist Assets Manager’, ‘rx-theme’ ); ?></h1>
<p class=”rx-dist-note”>
<?php esc_html_e( ‘Use this page to add up to 50 chunk JS files, 50 main/app JS files, and 50 CSS files from your React, Vite, Astro build, or other dist folder.’, ‘rx-theme’ ); ?>
</p>
<h2><?php esc_html_e( ‘Final Folder Structure’, ‘rx-theme’ ); ?></h2>
<pre class=”rx-dist-card”>assets/app-dist/
├── chunks/
│ ├── chunk-01.js
│ ├── chunk-02.js
│ └── chunk-50.js
├── main-js/
│ ├── main-01.js
│ ├── main-02.js
│ └── main-50.js
├── css/
│ ├── style-01.css
│ ├── style-02.css
│ └── style-50.css
├── vendor/
├── images/
└── fonts/</pre>
<form method=”post” action=”options.php”>
<?php settings_fields( ‘rx_theme_v89_dist_assets_group’ ); ?>
<div class=”rx-dist-card”>
<h2><?php esc_html_e( ‘Global Controls’, ‘rx-theme’ ); ?></h2>
<p>
<label>
<input type=”checkbox” name=”rx_theme_v89_dist_assets[enabled]” value=”1″ <?php checked( ‘1’, $options[‘enabled’] ); ?>>
<?php esc_html_e( ‘Enable dist asset loading on frontend’, ‘rx-theme’ ); ?>
</label>
</p>
<p>
<label>
<input type=”checkbox” name=”rx_theme_v89_dist_assets[load_on_admin]” value=”1″ <?php checked( ‘1’, $options[‘load_on_admin’] ); ?>>
<?php esc_html_e( ‘Allow enabled admin/both assets to load in WordPress admin’, ‘rx-theme’ ); ?>
</label>
</p>
</div>
<div class=”rx-dist-tabs”>
<a href=”#rx-dist-chunks”><?php esc_html_e( ’50 Chunk JS’, ‘rx-theme’ ); ?></a>
<a href=”#rx-dist-main-js”><?php esc_html_e( ’50 Main JS’, ‘rx-theme’ ); ?></a>
<a href=”#rx-dist-css”><?php esc_html_e( ’50 CSS’, ‘rx-theme’ ); ?></a>
</div>
<?php foreach ( rx_theme_v89_dist_asset_types() as $type => $config ) : ?>
<?php $section_id = ‘rx-dist-‘ . str_replace( ‘_’, ‘-‘, $type ); ?>
<div id=”<?php echo esc_attr( $section_id ); ?>” class=”rx-dist-card”>
<h2><?php echo esc_html( $config[‘label’] ); ?></h2>
<table class=”rx-dist-table”>
<thead>
<tr>
<th><?php esc_html_e( ‘No.’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘On’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘Handle’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘File Path or CDN URL’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘Location’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘Strategy’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘Deps’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘Ver’, ‘rx-theme’ ); ?></th>
</tr>
</thead>
<tbody>
<?php for ( $i = 1; $i <= $config[‘count’]; $i++ ) : ?>
<?php $item = $options[‘items’][ $type ][ $i ]; ?>
<tr>
<td><strong><?php echo esc_html( sprintf( ‘%02d’, $i ) ); ?></strong></td>
<td>
<input type=”checkbox” name=”rx_theme_v89_dist_assets[items][<?php echo esc_attr( $type ); ?>][<?php echo esc_attr( $i ); ?>][enabled]” value=”1″ <?php checked( ‘1’, $item[‘enabled’] ); ?>>
</td>
<td>
<input class=”rx-dist-handle” type=”text” name=”rx_theme_v89_dist_assets[items][<?php echo esc_attr( $type ); ?>][<?php echo esc_attr( $i ); ?>][handle]” value=”<?php echo esc_attr( $item[‘handle’] ); ?>”>
</td>
<td>
<input class=”rx-dist-src” type=”text” name=”rx_theme_v89_dist_assets[items][<?php echo esc_attr( $type ); ?>][<?php echo esc_attr( $i ); ?>][src]” value=”<?php echo esc_attr( $item[‘src’] ); ?>”>
</td>
<td>
<select name=”rx_theme_v89_dist_assets[items][<?php echo esc_attr( $type ); ?>][<?php echo esc_attr( $i ); ?>][location]”>
<option value=”frontend” <?php selected( ‘frontend’, $item[‘location’] ); ?>>Frontend</option>
<option value=”admin” <?php selected( ‘admin’, $item[‘location’] ); ?>>Admin</option>
<option value=”both” <?php selected( ‘both’, $item[‘location’] ); ?>>Both</option>
</select>
</td>
<td>
<select name=”rx_theme_v89_dist_assets[items][<?php echo esc_attr( $type ); ?>][<?php echo esc_attr( $i ); ?>][strategy]”>
<option value=”none” <?php selected( ‘none’, $item[‘strategy’] ); ?>>None</option>
<option value=”defer” <?php selected( ‘defer’, $item[‘strategy’] ); ?>>Defer</option>
<option value=”async” <?php selected( ‘async’, $item[‘strategy’] ); ?>>Async</option>
</select>
</td>
<td>
<input class=”rx-dist-mini” type=”text” name=”rx_theme_v89_dist_assets[items][<?php echo esc_attr( $type ); ?>][<?php echo esc_attr( $i ); ?>][deps]” value=”<?php echo esc_attr( $item[‘deps’] ); ?>” placeholder=”jquery”>
</td>
<td>
<input class=”rx-dist-mini” type=”text” name=”rx_theme_v89_dist_assets[items][<?php echo esc_attr( $type ); ?>][<?php echo esc_attr( $i ); ?>][version]” value=”<?php echo esc_attr( $item[‘version’] ); ?>” placeholder=”1.0″>
</td>
</tr>
<?php endfor; ?>
</tbody>
</table>
</div>
<?php endforeach; ?>
<?php submit_button( esc_html__( ‘Save Dist Assets’, ‘rx-theme’ ) ); ?>
</form>
</div>
<?php
}
function rx_theme_v89_dist_is_external_url( $src ) {
return (bool) preg_match( ‘#^https?://#i’, $src );
}
function rx_theme_v89_dist_src_to_url( $src ) {
$src = trim( (string) $src );
if ( ” === $src ) {
return ”;
}
if ( rx_theme_v89_dist_is_external_url( $src ) ) {
return esc_url_raw( $src );
}
$src = ltrim( $src, ‘/’ );
return get_template_directory_uri() . ‘/’ . $src;
}
function rx_theme_v89_dist_src_to_path( $src ) {
if ( rx_theme_v89_dist_is_external_url( $src ) ) {
return ”;
}
return get_template_directory() . ‘/’ . ltrim( $src, ‘/’ );
}
function rx_theme_v89_dist_deps_array( $deps ) {
$deps = trim( (string) $deps );
if ( ” === $deps ) {
return array();
}
$parts = array_map( ‘sanitize_key’, array_map( ‘trim’, explode( ‘,’, $deps ) ) );
return array_filter( $parts );
}
function rx_theme_v89_dist_register_frontend_assets() {
$options = rx_theme_v89_dist_get_options();
if ( ‘1’ !== $options[‘enabled’] || is_admin() ) {
return;
}
rx_theme_v89_dist_enqueue_by_location( ‘frontend’ );
}
add_action( ‘wp_enqueue_scripts’, ‘rx_theme_v89_dist_register_frontend_assets’, 130 );
function rx_theme_v89_dist_register_admin_assets( $hook ) {
$options = rx_theme_v89_dist_get_options();
if ( ‘1’ !== $options[‘enabled’] || ‘1’ !== $options[‘load_on_admin’] ) {
return;
}
rx_theme_v89_dist_enqueue_by_location( ‘admin’ );
}
add_action( ‘admin_enqueue_scripts’, ‘rx_theme_v89_dist_register_admin_assets’, 130 );
function rx_theme_v89_dist_enqueue_by_location( $context ) {
$options = rx_theme_v89_dist_get_options();
foreach ( rx_theme_v89_dist_asset_types() as $type => $config ) {
for ( $i = 1; $i <= $config[‘count’]; $i++ ) {
$item = $options[‘items’][ $type ][ $i ];
if ( ‘1’ !== $item[‘enabled’] ) {
continue;
}
if ( $item[‘location’] !== $context && ‘both’ !== $item[‘location’] ) {
continue;
}
$url = rx_theme_v89_dist_src_to_url( $item[‘src’] );
if ( ! $url ) {
continue;
}
$path = rx_theme_v89_dist_src_to_path( $item[‘src’] );
$version = $item[‘version’] ? $item[‘version’] : ( $path && file_exists( $path ) ? filemtime( $path ) : null );
$deps = rx_theme_v89_dist_deps_array( $item[‘deps’] );
$handle = sanitize_key( $item[‘handle’] );
if ( ‘style’ === $config[‘kind’] ) {
wp_enqueue_style( $handle, $url, $deps, $version );
} else {
wp_enqueue_script( $handle, $url, $deps, $version, true );
}
}
}
}
function rx_theme_v89_dist_script_strategy( $tag, $handle, $src ) {
$options = rx_theme_v89_dist_get_options();
foreach ( rx_theme_v89_dist_asset_types() as $type => $config ) {
if ( ‘script’ !== $config[‘kind’] ) {
continue;
}
for ( $i = 1; $i <= $config[‘count’]; $i++ ) {
$item = $options[‘items’][ $type ][ $i ];
if ( sanitize_key( $item[‘handle’] ) !== $handle || ‘1’ !== $item[‘enabled’] ) {
continue;
}
if ( ‘async’ === $item[‘strategy’] && false === strpos( $tag, ‘ async’ ) ) {
return str_replace( ‘ src=’, ‘ async src=’, $tag );
}
if ( ‘defer’ === $item[‘strategy’] && false === strpos( $tag, ‘ defer’ ) ) {
return str_replace( ‘ src=’, ‘ defer src=’, $tag );
}
}
}
return $tag;
}
add_filter( ‘script_loader_tag’, ‘rx_theme_v89_dist_script_strategy’, 20, 3 );
”’
write(theme_root / “inc/app/dist-assets-manager.php”, php)
# loader add
write(theme_root / “inc/app/dist-assets-loader.php”, r”'<?php
/**
* RX Theme v8.9 Dist Assets loader.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
$rx_theme_v89_dist_file = get_template_directory() . ‘/inc/app/dist-assets-manager.php’;
if ( file_exists( $rx_theme_v89_dist_file ) ) {
require_once $rx_theme_v89_dist_file;
}
”’)
# Add to autoload
autoload = theme_root / “inc/autoload.php”
if autoload.exists():
a = read(autoload)
if “/inc/app/dist-assets-loader.php” not in a:
idx = a.find(“foreach”)
if idx != -1:
before, after = a[:idx], a[idx:]
pos = before.rfind(“);”)
if pos != -1:
before = before[:pos] + ” ‘/inc/app/dist-assets-loader.php’,\n” + before[pos:]
a = before + after
else:
a += “\nrequire_once RX_THEME_DIR . ‘/inc/app/dist-assets-loader.php’;\n”
else:
a += “\nrequire_once RX_THEME_DIR . ‘/inc/app/dist-assets-loader.php’;\n”
write(autoload, a)
else:
write(autoload, “<?php\nif ( ! defined( ‘ABSPATH’ ) ) { exit; }\nrequire_once get_template_directory() . ‘/inc/app/dist-assets-loader.php’;\n”)
# PHP syntax check
errors = []
for p in theme_root.rglob(“*.php”):
proc = subprocess.run([“php”, “-l”, str(p)], capture_output=True, text=True)
if proc.returncode != 0:
errors.append(str(p) + “\n” + proc.stdout + proc.stderr)
if errors:
raise RuntimeError(“\n”.join(errors[:10]))
# Zip
zip_path = Path(“/mnt/data/rx-theme-v8-9-dist-assets-manager.zip”)
if zip_path.exists():
zip_path.unlink()
with zipfile.ZipFile(zip_path, “w”, zipfile.ZIP_DEFLATED) as z:
for p in theme_root.rglob(“*”):
z.write(p, p.relative_to(work_root))
with zipfile.ZipFile(zip_path, “r”) as z:
bad = z.testzip()
if bad:
raise RuntimeError(“Bad ZIP file member: ” + str(bad))
print(“Created:”, zip_path)
print(“PHP syntax check: Passed”)
print(“ZIP integrity test: Passed”)
print(“Added: 50 chunk JS table, 50 main JS table, 50 CSS table, final dist folder structure”)
=====================================================================
Yes dear friend — I’ll inspect the uploaded plugin ZIP, extract its loader idea, then merge the right enqueue/admin/frontend/backend system into RX Theme as a safe new version.
from pathlib import Path
import zipfile, shutil, re, subprocess, json, os
plugin_zip = Path(“/mnt/data/7d12cd8e-3dd1-42ff-b834-8eb8506e1df0.zip”)
base_zip = Path(“/mnt/data/rx-theme-v8-9-dist-assets-manager.zip”)
fallbacks = [
Path(“/mnt/data/rx-theme-v8-8-app-dist-integration.zip”),
Path(“/mnt/data/rx-theme-v8-7-final-premium-polish.zip”),
]
if not base_zip.exists():
for f in fallbacks:
if f.exists():
base_zip = f
break
work_root = Path(“/mnt/data/rx_v90_unified_loader_build”)
extract_theme = work_root / “theme_extract”
extract_plugin = work_root / “plugin_extract”
if work_root.exists():
shutil.rmtree(work_root)
work_root.mkdir(parents=True)
extract_theme.mkdir(parents=True)
extract_plugin.mkdir(parents=True)
with zipfile.ZipFile(base_zip, “r”) as z:
z.extractall(extract_theme)
with zipfile.ZipFile(plugin_zip, “r”) as z:
z.extractall(extract_plugin)
theme_roots = [p.parent for p in extract_theme.rglob(“style.css”) if (p.parent / “functions.php”).exists()]
if not theme_roots:
raise RuntimeError(“Theme root not found.”)
plugin_roots = []
for p in extract_plugin.rglob(“*.php”):
txt = p.read_text(encoding=”utf-8″, errors=”ignore”)[:2000]
if “Plugin Name:” in txt:
plugin_roots.append(p.parent)
plugin_root = plugin_roots[0] if plugin_roots else next((p for p in extract_plugin.iterdir() if p.is_dir()), extract_plugin)
src_root = theme_roots[0]
theme_root = work_root / “rx-theme-v9-0-unified-app-plugin-loader”
shutil.copytree(src_root, theme_root)
def read(path):
return path.read_text(encoding=”utf-8″, errors=”ignore”)
def write(path, text):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(text, encoding=”utf-8″)
# Update style.css
style_path = theme_root / “style.css”
if style_path.exists():
s = read(style_path)
s = re.sub(r”Theme Name:\s*.*”, “Theme Name: RX Theme v9.0 Unified App Plugin Loader”, s, count=1) if “Theme Name:” in s else “/*\nTheme Name: RX Theme v9.0 Unified App Plugin Loader\nVersion: 9.0\n*/\n” + s
s = re.sub(r”Version:\s*.*”, “Version: 9.0”, s, count=1) if “Version:” in s else s.replace(“*/”, “Version: 9.0\n*/”, 1)
write(style_path, s)
# Analyze plugin files for structure names
plugin_files = []
for p in plugin_root.rglob(“*”):
if p.is_file():
rel = str(p.relative_to(plugin_root))
plugin_files.append(rel)
analysis = {
“plugin_root”: str(plugin_root.name),
“file_count”: len(plugin_files),
“php_files”: [f for f in plugin_files if f.endswith(“.php”)][:60],
“css_files”: [f for f in plugin_files if f.endswith(“.css”)][:60],
“js_files”: [f for f in plugin_files if f.endswith(“.js”)][:60],
}
# write analysis file into theme
write(theme_root / “inc/app/plugin-analysis-summary.json”, json.dumps(analysis, indent=2))
# Create needed directories
dirs = [
“assets/app-loader/frontend/css”,
“assets/app-loader/frontend/js”,
“assets/app-loader/admin/css”,
“assets/app-loader/admin/js”,
“assets/app-loader/both/css”,
“assets/app-loader/both/js”,
“assets/app-loader/chunks”,
“assets/app-loader/vendor”,
“assets/app-loader/media”,
“assets/css/backend”,
“inc/app”,
]
for d in dirs:
(theme_root / d).mkdir(parents=True, exist_ok=True)
# Copy plugin non-PHP assets into a reference folder? For safety include under app-loader/plugin-reference not executable.
ref_dir = theme_root / “app/plugin-reference-readonly”
ref_dir.mkdir(parents=True, exist_ok=True)
# Only copy small text files summaries and list, not entire plugin to avoid duplicate plugin execution
write(ref_dir / “PLUGIN_FILES_LIST.txt”, “\n”.join(plugin_files))
write(ref_dir / “README.md”, f”””# Plugin reference
Uploaded plugin analyzed from: {plugin_root.name}
For safety, plugin PHP is not executed directly inside the theme.
The useful idea is converted into theme-safe enqueue/admin loader modules.
File count: {len(plugin_files)}
PHP files: {len(analysis[‘php_files’])}
CSS files: {len(analysis[‘css_files’])}
JS files: {len(analysis[‘js_files’])}
“””)
# Read plugin names maybe
plugin_header = “”
for p in plugin_root.rglob(“*.php”):
txt = read(p)
if “Plugin Name:” in txt:
plugin_header = txt[:1200]
break
write(ref_dir / “PLUGIN_HEADER.txt”, plugin_header)
# Create placeholder sample assets for loader dirs
write(theme_root / “assets/app-loader/frontend/css/frontend-sample.css”, “/* Frontend sample file. Replace with your app CSS or disable in admin. */\n”)
write(theme_root / “assets/app-loader/frontend/js/frontend-sample.js”, “/* Frontend sample file. */\n”)
write(theme_root / “assets/app-loader/admin/css/admin-sample.css”, “/* Admin sample file. Replace with your admin app CSS or disable in admin. */\n”)
write(theme_root / “assets/app-loader/admin/js/admin-sample.js”, “/* Admin sample file. */\n”)
write(theme_root / “assets/app-loader/both/css/both-sample.css”, “/* Both frontend/admin sample CSS. */\n”)
write(theme_root / “assets/app-loader/both/js/both-sample.js”, “/* Both frontend/admin sample JS. */\n”)
write(theme_root / “assets/app-loader/chunks/.gitkeep”, “”)
write(theme_root / “assets/app-loader/vendor/.gitkeep”, “”)
write(theme_root / “assets/app-loader/media/.gitkeep”, “”)
# Unified loader CSS for admin page
write(theme_root / “assets/css/backend/unified-app-loader-admin.css”, r”’/* RX Theme v9.0 Unified App Plugin Loader Admin */
.rx-unified-loader-wrap{max-width:1500px}
.rx-unified-card{margin:18px 0;padding:20px;border:1px solid #dcdcde;border-radius:16px;background:#fff;box-shadow:0 10px 28px rgba(0,0,0,.04)}
.rx-unified-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
.rx-unified-stat{padding:18px;border-radius:14px;background:#f6f7f7;border:1px solid #e5e7eb}
.rx-unified-stat strong{display:block;font-size:28px;color:#2271b1}
.rx-unified-table{width:100%;border-collapse:collapse}
.rx-unified-table th,.rx-unified-table td{padding:8px;border-bottom:1px solid #edf0f2;vertical-align:middle}
.rx-unified-table th{text-align:left}
.rx-unified-table input[type=text],.rx-unified-table select{width:100%}
.rx-unified-path{min-width:380px}
.rx-unified-small{width:76px!important}
.rx-unified-note{padding:12px 14px;border-left:4px solid #2271b1;background:#f0f6fc}
.rx-unified-danger{border-left-color:#d63638;background:#fcf0f1}
@media(max-width:1000px){.rx-unified-grid{grid-template-columns:1fr}.rx-unified-table{display:block;overflow:auto}}
”’)
# PHP unified loader
php = r”'<?php
/**
* RX Theme v9.0 Unified App + Plugin Loader.
* Theme-safe replacement for app/static CDN loader plugin workflow.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
function rx_theme_v90_loader_defaults() {
$defaults = array(
‘enabled’ => ‘0’,
‘frontend_on’ => ‘1’,
‘admin_on’ => ‘0’,
‘both_on’ => ‘0’,
‘module_type’ => ‘classic’,
‘items’ => array(),
);
$groups = rx_theme_v90_loader_groups();
foreach ( $groups as $group => $config ) {
$defaults[‘items’][ $group ] = array();
for ( $i = 1; $i <= $config[‘count’]; $i++ ) {
$defaults[‘items’][ $group ][ $i ] = array(
‘enabled’ => ‘0’,
‘handle’ => ‘rx-v90-‘ . $group . ‘-‘ . sprintf( ‘%02d’, $i ),
‘src’ => $config[‘dir’] . ‘/’ . sprintf( $config[‘file’], $i ),
‘deps’ => ”,
‘ver’ => ”,
‘strategy’ => $config[‘kind’] === ‘script’ ? ‘defer’ : ‘none’,
‘media’ => ‘all’,
‘priority’ => $i,
‘location’ => $config[‘location’],
);
}
}
return $defaults;
}
function rx_theme_v90_loader_groups() {
return array(
‘frontend_css’ => array(
‘label’ => esc_html__( ‘Frontend CSS’, ‘rx-theme’ ),
‘kind’ => ‘style’,
‘count’ => 50,
‘dir’ => ‘assets/app-loader/frontend/css’,
‘file’ => ‘frontend-%02d.css’,
‘location’ => ‘frontend’,
),
‘frontend_js’ => array(
‘label’ => esc_html__( ‘Frontend JS’, ‘rx-theme’ ),
‘kind’ => ‘script’,
‘count’ => 50,
‘dir’ => ‘assets/app-loader/frontend/js’,
‘file’ => ‘frontend-%02d.js’,
‘location’ => ‘frontend’,
),
‘admin_css’ => array(
‘label’ => esc_html__( ‘Admin CSS’, ‘rx-theme’ ),
‘kind’ => ‘style’,
‘count’ => 50,
‘dir’ => ‘assets/app-loader/admin/css’,
‘file’ => ‘admin-%02d.css’,
‘location’ => ‘admin’,
),
‘admin_js’ => array(
‘label’ => esc_html__( ‘Admin JS’, ‘rx-theme’ ),
‘kind’ => ‘script’,
‘count’ => 50,
‘dir’ => ‘assets/app-loader/admin/js’,
‘file’ => ‘admin-%02d.js’,
‘location’ => ‘admin’,
),
‘both_css’ => array(
‘label’ => esc_html__( ‘Both CSS’, ‘rx-theme’ ),
‘kind’ => ‘style’,
‘count’ => 50,
‘dir’ => ‘assets/app-loader/both/css’,
‘file’ => ‘both-%02d.css’,
‘location’ => ‘both’,
),
‘both_js’ => array(
‘label’ => esc_html__( ‘Both JS’, ‘rx-theme’ ),
‘kind’ => ‘script’,
‘count’ => 50,
‘dir’ => ‘assets/app-loader/both/js’,
‘file’ => ‘both-%02d.js’,
‘location’ => ‘both’,
),
‘chunks’ => array(
‘label’ => esc_html__( ‘Shared Chunk JS’, ‘rx-theme’ ),
‘kind’ => ‘script’,
‘count’ => 100,
‘dir’ => ‘assets/app-loader/chunks’,
‘file’ => ‘chunk-%02d.js’,
‘location’ => ‘frontend’,
),
);
}
function rx_theme_v90_get_options() {
$options = get_option( ‘rx_theme_v90_unified_loader’, array() );
if ( ! is_array( $options ) ) {
$options = array();
}
return wp_parse_args( $options, rx_theme_v90_loader_defaults() );
}
function rx_theme_v90_sanitize_options( $input ) {
$defaults = rx_theme_v90_loader_defaults();
$output = array(
‘enabled’ => ! empty( $input[‘enabled’] ) ? ‘1’ : ‘0’,
‘frontend_on’ => ! empty( $input[‘frontend_on’] ) ? ‘1’ : ‘0’,
‘admin_on’ => ! empty( $input[‘admin_on’] ) ? ‘1’ : ‘0’,
‘both_on’ => ! empty( $input[‘both_on’] ) ? ‘1’ : ‘0’,
‘module_type’ => isset( $input[‘module_type’] ) && ‘module’ === $input[‘module_type’] ? ‘module’ : ‘classic’,
‘items’ => array(),
);
foreach ( rx_theme_v90_loader_groups() as $group => $config ) {
$output[‘items’][ $group ] = array();
for ( $i = 1; $i <= $config[‘count’]; $i++ ) {
$item = isset( $input[‘items’][ $group ][ $i ] ) && is_array( $input[‘items’][ $group ][ $i ] )
? $input[‘items’][ $group ][ $i ]
: array();
$default = $defaults[‘items’][ $group ][ $i ];
$strategy = isset( $item[‘strategy’] ) ? sanitize_key( $item[‘strategy’] ) : $default[‘strategy’];
if ( ! in_array( $strategy, array( ‘none’, ‘defer’, ‘async’, ‘module’ ), true ) ) {
$strategy = $default[‘strategy’];
}
$location = isset( $item[‘location’] ) ? sanitize_key( $item[‘location’] ) : $default[‘location’];
if ( ! in_array( $location, array( ‘frontend’, ‘admin’, ‘both’ ), true ) ) {
$location = $default[‘location’];
}
$output[‘items’][ $group ][ $i ] = array(
‘enabled’ => ! empty( $item[‘enabled’] ) ? ‘1’ : ‘0’,
‘handle’ => isset( $item[‘handle’] ) ? sanitize_key( $item[‘handle’] ) : $default[‘handle’],
‘src’ => isset( $item[‘src’] ) ? esc_url_raw( trim( wp_unslash( $item[‘src’] ) ) ) : $default[‘src’],
‘deps’ => isset( $item[‘deps’] ) ? sanitize_text_field( wp_unslash( $item[‘deps’] ) ) : ”,
‘ver’ => isset( $item[‘ver’] ) ? sanitize_text_field( wp_unslash( $item[‘ver’] ) ) : ”,
‘strategy’ => $strategy,
‘media’ => isset( $item[‘media’] ) ? sanitize_key( $item[‘media’] ) : ‘all’,
‘priority’ => isset( $item[‘priority’] ) ? absint( $item[‘priority’] ) : $i,
‘location’ => $location,
);
if ( empty( $output[‘items’][ $group ][ $i ][‘src’] ) ) {
$output[‘items’][ $group ][ $i ][‘src’] = $default[‘src’];
}
}
}
return $output;
}
function rx_theme_v90_register_settings() {
register_setting(
‘rx_theme_v90_unified_loader_group’,
‘rx_theme_v90_unified_loader’,
array(
‘sanitize_callback’ => ‘rx_theme_v90_sanitize_options’,
‘default’ => rx_theme_v90_loader_defaults(),
)
);
}
add_action( ‘admin_init’, ‘rx_theme_v90_register_settings’ );
function rx_theme_v90_admin_menu() {
add_theme_page(
esc_html__( ‘RX Unified Loader’, ‘rx-theme’ ),
esc_html__( ‘RX Unified Loader’, ‘rx-theme’ ),
‘manage_options’,
‘rx-theme-unified-loader’,
‘rx_theme_v90_admin_page’
);
}
add_action( ‘admin_menu’, ‘rx_theme_v90_admin_menu’, 60 );
function rx_theme_v90_admin_assets( $hook ) {
if ( ‘appearance_page_rx-theme-unified-loader’ !== $hook ) {
return;
}
$path = get_template_directory() . ‘/assets/css/backend/unified-app-loader-admin.css’;
if ( file_exists( $path ) ) {
wp_enqueue_style(
‘rx-theme-unified-app-loader-admin’,
get_template_directory_uri() . ‘/assets/css/backend/unified-app-loader-admin.css’,
array(),
filemtime( $path )
);
}
}
add_action( ‘admin_enqueue_scripts’, ‘rx_theme_v90_admin_assets’ );
function rx_theme_v90_admin_page() {
if ( ! current_user_can( ‘manage_options’ ) ) {
return;
}
$options = rx_theme_v90_get_options();
?>
<div class=”wrap rx-unified-loader-wrap”>
<h1><?php esc_html_e( ‘RX Theme Unified App + Plugin Loader’, ‘rx-theme’ ); ?></h1>
<p class=”rx-unified-note”>
<?php esc_html_e( ‘This combines the useful plugin-style asset loader idea into RX Theme safely. Add frontend, backend/admin, or both-side CSS/JS assets without editing PHP.’, ‘rx-theme’ ); ?>
</p>
<div class=”rx-unified-grid”>
<div class=”rx-unified-stat”><strong>50</strong><span><?php esc_html_e( ‘Frontend CSS/JS each’, ‘rx-theme’ ); ?></span></div>
<div class=”rx-unified-stat”><strong>50</strong><span><?php esc_html_e( ‘Admin CSS/JS each’, ‘rx-theme’ ); ?></span></div>
<div class=”rx-unified-stat”><strong>100</strong><span><?php esc_html_e( ‘Shared chunk JS files’, ‘rx-theme’ ); ?></span></div>
</div>
<div class=”rx-unified-card”>
<h2><?php esc_html_e( ‘Recommended Final Folder Structure’, ‘rx-theme’ ); ?></h2>
<pre>assets/app-loader/
├── frontend/
│ ├── css/
│ │ ├── frontend-01.css … frontend-50.css
│ └── js/
│ ├── frontend-01.js … frontend-50.js
├── admin/
│ ├── css/
│ │ ├── admin-01.css … admin-50.css
│ └── js/
│ ├── admin-01.js … admin-50.js
├── both/
│ ├── css/
│ │ ├── both-01.css … both-50.css
│ └── js/
│ ├── both-01.js … both-50.js
├── chunks/
│ ├── chunk-01.js … chunk-100.js
├── vendor/
└── media/</pre>
</div>
<form method=”post” action=”options.php”>
<?php settings_fields( ‘rx_theme_v90_unified_loader_group’ ); ?>
<div class=”rx-unified-card”>
<h2><?php esc_html_e( ‘Global Controls’, ‘rx-theme’ ); ?></h2>
<p><label><input type=”checkbox” name=”rx_theme_v90_unified_loader[enabled]” value=”1″ <?php checked( ‘1’, $options[‘enabled’] ); ?>> <?php esc_html_e( ‘Enable unified loader’, ‘rx-theme’ ); ?></label></p>
<p><label><input type=”checkbox” name=”rx_theme_v90_unified_loader[frontend_on]” value=”1″ <?php checked( ‘1’, $options[‘frontend_on’] ); ?>> <?php esc_html_e( ‘Allow frontend assets’, ‘rx-theme’ ); ?></label></p>
<p><label><input type=”checkbox” name=”rx_theme_v90_unified_loader[admin_on]” value=”1″ <?php checked( ‘1’, $options[‘admin_on’] ); ?>> <?php esc_html_e( ‘Allow admin/backend assets’, ‘rx-theme’ ); ?></label></p>
<p><label><input type=”checkbox” name=”rx_theme_v90_unified_loader[both_on]” value=”1″ <?php checked( ‘1’, $options[‘both_on’] ); ?>> <?php esc_html_e( ‘Allow both-side assets’, ‘rx-theme’ ); ?></label></p>
<p>
<label><?php esc_html_e( ‘Default JS Type’, ‘rx-theme’ ); ?></label>
<select name=”rx_theme_v90_unified_loader[module_type]”>
<option value=”classic” <?php selected( ‘classic’, $options[‘module_type’] ); ?>>Classic</option>
<option value=”module” <?php selected( ‘module’, $options[‘module_type’] ); ?>>Module</option>
</select>
</p>
</div>
<?php foreach ( rx_theme_v90_loader_groups() as $group => $config ) : ?>
<div class=”rx-unified-card” id=”rx-v90-<?php echo esc_attr( $group ); ?>”>
<h2><?php echo esc_html( $config[‘label’] ); ?></h2>
<table class=”rx-unified-table”>
<thead>
<tr>
<th><?php esc_html_e( ‘No’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘On’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘Handle’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘File Path / CDN URL’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘Location’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘Strategy’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘Deps’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘Ver’, ‘rx-theme’ ); ?></th>
<th><?php esc_html_e( ‘Priority’, ‘rx-theme’ ); ?></th>
</tr>
</thead>
<tbody>
<?php for ( $i = 1; $i <= $config[‘count’]; $i++ ) : ?>
<?php $item = $options[‘items’][ $group ][ $i ]; ?>
<tr>
<td><strong><?php echo esc_html( sprintf( ‘%02d’, $i ) ); ?></strong></td>
<td><input type=”checkbox” name=”rx_theme_v90_unified_loader[items][<?php echo esc_attr( $group ); ?>][<?php echo esc_attr( $i ); ?>][enabled]” value=”1″ <?php checked( ‘1’, $item[‘enabled’] ); ?>></td>
<td><input type=”text” name=”rx_theme_v90_unified_loader[items][<?php echo esc_attr( $group ); ?>][<?php echo esc_attr( $i ); ?>][handle]” value=”<?php echo esc_attr( $item[‘handle’] ); ?>”></td>
<td><input class=”rx-unified-path” type=”text” name=”rx_theme_v90_unified_loader[items][<?php echo esc_attr( $group ); ?>][<?php echo esc_attr( $i ); ?>][src]” value=”<?php echo esc_attr( $item[‘src’] ); ?>”></td>
<td>
<select name=”rx_theme_v90_unified_loader[items][<?php echo esc_attr( $group ); ?>][<?php echo esc_attr( $i ); ?>][location]”>
<option value=”frontend” <?php selected( ‘frontend’, $item[‘location’] ); ?>>Frontend</option>
<option value=”admin” <?php selected( ‘admin’, $item[‘location’] ); ?>>Admin</option>
<option value=”both” <?php selected( ‘both’, $item[‘location’] ); ?>>Both</option>
</select>
</td>
<td>
<select name=”rx_theme_v90_unified_loader[items][<?php echo esc_attr( $group ); ?>][<?php echo esc_attr( $i ); ?>][strategy]”>
<option value=”none” <?php selected( ‘none’, $item[‘strategy’] ); ?>>None</option>
<option value=”defer” <?php selected( ‘defer’, $item[‘strategy’] ); ?>>Defer</option>
<option value=”async” <?php selected( ‘async’, $item[‘strategy’] ); ?>>Async</option>
<option value=”module” <?php selected( ‘module’, $item[‘strategy’] ); ?>>Module</option>
</select>
</td>
<td><input class=”rx-unified-small” type=”text” name=”rx_theme_v90_unified_loader[items][<?php echo esc_attr( $group ); ?>][<?php echo esc_attr( $i ); ?>][deps]” value=”<?php echo esc_attr( $item[‘deps’] ); ?>” placeholder=”jquery”></td>
<td><input class=”rx-unified-small” type=”text” name=”rx_theme_v90_unified_loader[items][<?php echo esc_attr( $group ); ?>][<?php echo esc_attr( $i ); ?>][ver]” value=”<?php echo esc_attr( $item[‘ver’] ); ?>” placeholder=”1.0″></td>
<td><input class=”rx-unified-small” type=”text” name=”rx_theme_v90_unified_loader[items][<?php echo esc_attr( $group ); ?>][<?php echo esc_attr( $i ); ?>][priority]” value=”<?php echo esc_attr( $item[‘priority’] ); ?>”></td>
</tr>
<?php endfor; ?>
</tbody>
</table>
</div>
<?php endforeach; ?>
<?php submit_button( esc_html__( ‘Save Unified Loader Settings’, ‘rx-theme’ ) ); ?>
</form>
</div>
<?php
}
function rx_theme_v90_is_external_url( $src ) {
return (bool) preg_match( ‘#^https?://#i’, $src );
}
function rx_theme_v90_src_url( $src ) {
$src = trim( (string) $src );
if ( ” === $src ) {
return ”;
}
if ( rx_theme_v90_is_external_url( $src ) ) {
return esc_url_raw( $src );
}
return get_template_directory_uri() . ‘/’ . ltrim( $src, ‘/’ );
}
function rx_theme_v90_src_path( $src ) {
if ( rx_theme_v90_is_external_url( $src ) ) {
return ”;
}
return get_template_directory() . ‘/’ . ltrim( $src, ‘/’ );
}
function rx_theme_v90_deps( $deps ) {
$deps = trim( (string) $deps );
if ( ” === $deps ) {
return array();
}
return array_filter( array_map( ‘sanitize_key’, array_map( ‘trim’, explode( ‘,’, $deps ) ) ) );
}
function rx_theme_v90_location_allowed( $location, $context, $options ) {
if ( ‘frontend’ === $context ) {
if ( ‘1’ !== $options[‘frontend_on’] && ‘frontend’ === $location ) {
return false;
}
if ( ‘1’ !== $options[‘both_on’] && ‘both’ === $location ) {
return false;
}
return ‘frontend’ === $location || ‘both’ === $location;
}
if ( ‘admin’ === $context ) {
if ( ‘1’ !== $options[‘admin_on’] && ‘admin’ === $location ) {
return false;
}
if ( ‘1’ !== $options[‘both_on’] && ‘both’ === $location ) {
return false;
}
return ‘admin’ === $location || ‘both’ === $location;
}
return false;
}
function rx_theme_v90_enqueue_context( $context ) {
$options = rx_theme_v90_get_options();
if ( ‘1’ !== $options[‘enabled’] ) {
return;
}
$queue = array();
foreach ( rx_theme_v90_loader_groups() as $group => $config ) {
for ( $i = 1; $i <= $config[‘count’]; $i++ ) {
$item = $options[‘items’][ $group ][ $i ];
if ( ‘1’ !== $item[‘enabled’] ) {
continue;
}
if ( ! rx_theme_v90_location_allowed( $item[‘location’], $context, $options ) ) {
continue;
}
$item[‘_kind’] = $config[‘kind’];
$queue[] = $item;
}
}
usort(
$queue,
function( $a, $b ) {
return absint( $a[‘priority’] ) <=> absint( $b[‘priority’] );
}
);
foreach ( $queue as $item ) {
$url = rx_theme_v90_src_url( $item[‘src’] );
if ( ! $url ) {
continue;
}
$path = rx_theme_v90_src_path( $item[‘src’] );
$version = $item[‘ver’] ? $item[‘ver’] : ( $path && file_exists( $path ) ? filemtime( $path ) : null );
$deps = rx_theme_v90_deps( $item[‘deps’] );
$handle = sanitize_key( $item[‘handle’] );
if ( ‘style’ === $item[‘_kind’] ) {
wp_enqueue_style( $handle, $url, $deps, $version, isset( $item[‘media’] ) ? sanitize_key( $item[‘media’] ) : ‘all’ );
} else {
wp_enqueue_script( $handle, $url, $deps, $version, true );
}
}
}
function rx_theme_v90_frontend_enqueue() {
rx_theme_v90_enqueue_context( ‘frontend’ );
}
add_action( ‘wp_enqueue_scripts’, ‘rx_theme_v90_frontend_enqueue’, 150 );
function rx_theme_v90_admin_enqueue() {
rx_theme_v90_enqueue_context( ‘admin’ );
}
add_action( ‘admin_enqueue_scripts’, ‘rx_theme_v90_admin_enqueue’, 150 );
function rx_theme_v90_script_tag_strategy( $tag, $handle, $src ) {
$options = rx_theme_v90_get_options();
foreach ( rx_theme_v90_loader_groups() as $group => $config ) {
if ( ‘script’ !== $config[‘kind’] ) {
continue;
}
for ( $i = 1; $i <= $config[‘count’]; $i++ ) {
$item = $options[‘items’][ $group ][ $i ];
if ( sanitize_key( $item[‘handle’] ) !== $handle || ‘1’ !== $item[‘enabled’] ) {
continue;
}
if ( ‘module’ === $item[‘strategy’] || ‘module’ === $options[‘module_type’] ) {
if ( false === strpos( $tag, ‘ type=’ ) ) {
$tag = str_replace( ‘<script ‘, ‘<script type=”module” ‘, $tag );
}
return $tag;
}
if ( ‘async’ === $item[‘strategy’] && false === strpos( $tag, ‘ async’ ) ) {
return str_replace( ‘ src=’, ‘ async src=’, $tag );
}
if ( ‘defer’ === $item[‘strategy’] && false === strpos( $tag, ‘ defer’ ) ) {
return str_replace( ‘ src=’, ‘ defer src=’, $tag );
}
}
}
return $tag;
}
add_filter( ‘script_loader_tag’, ‘rx_theme_v90_script_tag_strategy’, 30, 3 );
”’
write(theme_root / “inc/app/unified-app-plugin-loader.php”, php)
# loader file
write(theme_root / “inc/app/unified-loader.php”, r”'<?php
/**
* RX Theme v9.0 Unified App + Plugin Loader bootstrap.
*/
if ( ! defined( ‘ABSPATH’ ) ) {
exit;
}
$rx_theme_v90_file = get_template_directory() . ‘/inc/app/unified-app-plugin-loader.php’;
if ( file_exists( $rx_theme_v90_file ) ) {
require_once $rx_theme_v90_file;
}
”’)
# Add to autoload
autoload = theme_root / “inc/autoload.php”
if autoload.exists():
a = read(autoload)
if “/inc/app/unified-loader.php” not in a:
idx = a.find(“foreach”)
if idx != -1:
before, after = a[:idx], a[idx:]
pos = before.rfind(“);”)
if pos != -1:
before = before[:pos] + ” ‘/inc/app/unified-loader.php’,\n” + before[pos:]
a = before + after
else:
a += “\nrequire_once RX_THEME_DIR . ‘/inc/app/unified-loader.php’;\n”
else:
a += “\nrequire_once RX_THEME_DIR . ‘/inc/app/unified-loader.php’;\n”
write(autoload, a)
else:
write(autoload, “<?php\nif ( ! defined( ‘ABSPATH’ ) ) { exit; }\nrequire_once get_template_directory() . ‘/inc/app/unified-loader.php’;\n”)
# PHP syntax check
errors = []
for p in theme_root.rglob(“*.php”):
proc = subprocess.run([“php”, “-l”, str(p)], capture_output=True, text=True)
if proc.returncode != 0:
errors.append(str(p) + “\n” + proc.stdout + proc.stderr)
if errors:
raise RuntimeError(“\n”.join(errors[:10]))
# Zip
zip_path = Path(“/mnt/data/rx-theme-v9-0-unified-app-plugin-loader.zip”)
if zip_path.exists():
zip_path.unlink()
with zipfile.ZipFile(zip_path, “w”, zipfile.ZIP_DEFLATED) as z:
for p in theme_root.rglob(“*”):
z.write(p, p.relative_to(work_root))
with zipfile.ZipFile(zip_path, “r”) as z:
bad = z.testzip()
if bad:
raise RuntimeError(“Bad ZIP file member: ” + str(bad))
print(“Created:”, zip_path)
print(“PHP syntax check: Passed”)
print(“ZIP integrity test: Passed”)
print(“Plugin analyzed:”, analysis[“plugin_root”])
print(“Plugin files found:”, analysis[“file_count”])
print(“Added unified frontend/admin/both asset loader system.”)
Dear friend, do not load all chunk JS, main JS, and CSS everywhere. That will slow the site and may break WordPress admin.
The best rule is:
Frontend files → load only frontend
Backend/admin files → load only admin
Shared library files → load both only if truly needed
Chunk JS files → load only where the main app needs them
CSS files → load only where the design is needed
Best loading decision
| File type | Best location | Why |
|---|---|---|
| Frontend app JS | Frontend | For website visitors only |
| Frontend app CSS | Frontend | For public design only |
| Admin dashboard JS | Admin/backend | For WordPress dashboard only |
| Admin dashboard CSS | Admin/backend | For RX Theme Options/admin UI only |
| React admin app JS | Admin/backend | Never load it for visitors |
| React admin app CSS | Admin/backend | Only needed in dashboard |
| Vite frontend JS | Frontend | For public website features |
| Vite frontend CSS | Frontend | For public website style |
| Chunk JS | Usually frontend, sometimes admin | Depends which main JS imports it |
| Vendor JS | Same as parent app | If admin app uses it, admin only |
| Shared helper JS | Both, only if needed | Example: same dark mode helper |
| Medical block CSS | Both | Frontend display + editor preview |
| Gutenberg block JS | Admin/editor | Block editing only |
| WooCommerce JS/CSS | Frontend | Shop/cart/checkout only |
| Elementor compatibility CSS | Frontend | Page design only |
Best structure for your RX Theme
Use this:
assets/app-loader/
├── frontend/
│ ├── css/
│ │ ├── frontend-01.css
│ │ ├── frontend-02.css
│ │ └── frontend-50.css
│ └── js/
│ ├── frontend-01.js
│ ├── frontend-02.js
│ └── frontend-50.js
│
├── admin/
│ ├── css/
│ │ ├── admin-01.css
│ │ ├── admin-02.css
│ │ └── admin-50.css
│ └── js/
│ ├── admin-01.js
│ ├── admin-02.js
│ └── admin-50.js
│
├── both/
│ ├── css/
│ │ ├── both-01.css
│ │ └── both-50.css
│ └── js/
│ ├── both-01.js
│ └── both-50.js
│
└── chunks/
├── chunk-01.js
├── chunk-02.js
└── chunk-100.js
How to set in RX Unified Loader
Go to:
Appearance → RX Unified Loader
Then use this system:
1. For public website files
Use:
Location: Frontend
Strategy: Defer
Example:
frontend/css/frontend-01.css → Frontend
frontend/js/frontend-01.js → Frontend + Defer
chunks/chunk-01.js → Frontend + Defer
2. For WordPress admin dashboard files
Use:
Location: Admin
Strategy: Defer
Example:
admin/css/admin-01.css → Admin
admin/js/admin-01.js → Admin + Defer
3. For files needed everywhere
Use Both only for truly shared files:
Location: Both
Good examples:
shared icons CSS
shared utility CSS
small helper JS
design tokens CSS
Do not put big React/Vite app files in Both unless absolutely needed.
For chunk JS files
Most chunk files should follow their app.
If the chunks came from your frontend app, use:
Location: Frontend
Strategy: Defer
If the chunks came from your admin React app, use:
Location: Admin
Strategy: Defer
If the chunks are from both apps, better separate them:
assets/app-loader/frontend/chunks/
assets/app-loader/admin/chunks/
But with your current v9.0 structure, you can use the same chunks/ folder and set each row location manually.
My safest recommendation
For your RX Theme:
80% files → Frontend
15% files → Admin
5% files → Both
Do not load 150 files on every page. Load only what is needed.
Best practical setting
For most of your current app files:
CSS files from website app → Frontend
Main JS from website app → Frontend
Chunk JS from website app → Frontend
Admin dashboard JS/CSS → Admin
Theme settings panel JS/CSS → Admin
Shared small utility CSS/JS → Both
Very important
If a JS file is modern Vite/React module and contains import statements, use:
Strategy: Module
If normal browser JS, use:
Strategy: Defer
Use Async only for independent files like analytics. For app chunks, defer is safer than async.
My best answer: load frontend files on frontend, admin files on backend, and use both only for small shared helper files.