-
Notifications
You must be signed in to change notification settings - Fork 1
feat(gutenberg): add standalone Gutenberg integration plugin (v1) #168
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
jackgranatowski
merged 5 commits into
main
from
claude/gutenberg-integration-scope-jSOZX
May 31, 2026
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
211c175
feat(gutenberg): add standalone Gutenberg integration plugin (v1)
claude ae76e4e
feat: unified SLASHED plugin with per-builder integration toggles
claude 1253590
refactor: extract shared CSS loader; remove builder-specific aliases …
claude 6a046c8
build: unify zip packaging and version-sync for slashed + gutenberg
claude 529d14b
fix: address CodeRabbit review findings on PR #168
claude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,183 @@ | ||
| <?php | ||
| /** | ||
| * Unified SLASHED admin page. | ||
| * | ||
| * @package SLASHED | ||
| */ | ||
|
|
||
| if ( ! defined( 'ABSPATH' ) ) { | ||
| exit; | ||
| } | ||
|
|
||
| /** | ||
| * Class Slashed_Admin | ||
| * | ||
| * Registers the top-level "SLASHED" menu and renders the integration | ||
| * toggle page. Builder-specific settings pages (e.g. Bricks token | ||
| * overrides) appear as sub-pages when their integration is active. | ||
| */ | ||
| class Slashed_Admin { | ||
|
|
||
| const PAGE_SLUG = 'slashed'; | ||
| const NONCE_KEY = 'slashed_settings_save'; | ||
| const NONCE_ACTION = 'slashed_save_settings'; | ||
|
|
||
| public function __construct() { | ||
| add_action( 'admin_menu', array( $this, 'register_menu' ) ); | ||
| add_action( 'admin_post_slashed_save_settings', array( $this, 'handle_save' ) ); | ||
| } | ||
|
|
||
| public function register_menu() { | ||
| add_menu_page( | ||
| __( 'SLASHED', 'slashed' ), | ||
| __( 'SLASHED', 'slashed' ), | ||
| 'manage_options', | ||
| self::PAGE_SLUG, | ||
| array( $this, 'render_page' ), | ||
| 'dashicons-art', | ||
| 59 | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Handle the settings form POST. | ||
| * Redirects back to the settings page with a status flag. | ||
| */ | ||
| public function handle_save() { | ||
| if ( ! current_user_can( 'manage_options' ) ) { | ||
| wp_die( esc_html__( 'Insufficient permissions.', 'slashed' ) ); | ||
| } | ||
|
|
||
| check_admin_referer( self::NONCE_ACTION, self::NONCE_KEY ); | ||
|
|
||
| // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified above via check_admin_referer. | ||
| $raw_integrations = isset( $_POST['integrations'] ) && is_array( $_POST['integrations'] ) | ||
| ? array_map( 'sanitize_key', $_POST['integrations'] ) | ||
| : array(); | ||
|
|
||
| // phpcs:ignore WordPress.Security.NonceVerification.Missing | ||
| $raw_bundle = isset( $_POST['css_bundle'] ) ? sanitize_key( $_POST['css_bundle'] ) : 'optimal'; | ||
|
|
||
| $data = array( | ||
| 'css_bundle' => in_array( $raw_bundle, Slashed_Settings::ALLOWED_BUNDLES, true ) ? $raw_bundle : 'optimal', | ||
| 'integrations' => array(), | ||
| ); | ||
| foreach ( Slashed_Settings::KNOWN_INTEGRATIONS as $slug ) { | ||
| $data['integrations'][ $slug ] = array_key_exists( $slug, $raw_integrations ); | ||
| } | ||
|
|
||
| Slashed_Settings::save( $data ); | ||
|
|
||
| wp_safe_redirect( | ||
| add_query_arg( 'slashed_saved', '1', admin_url( 'admin.php?page=' . self::PAGE_SLUG ) ) | ||
| ); | ||
| exit; | ||
| } | ||
|
|
||
| public function render_page() { | ||
| if ( ! current_user_can( 'manage_options' ) ) { | ||
| return; | ||
| } | ||
|
|
||
| $settings = Slashed_Settings::get(); | ||
| $integrations = $settings['integrations']; | ||
| $css_bundle = $settings['css_bundle']; | ||
| $saved = ! empty( $_GET['slashed_saved'] ); // phpcs:ignore WordPress.Security.NonceVerification | ||
| ?> | ||
| <div class="wrap"> | ||
| <h1><?php esc_html_e( 'SLASHED', 'slashed' ); ?> <span style="font-weight:400;font-size:13px;color:#999;"><?php echo esc_html( SLASHED_VERSION ); ?></span></h1> | ||
|
|
||
| <?php if ( $saved ) : ?> | ||
| <div class="notice notice-success is-dismissible"><p><?php esc_html_e( 'Settings saved.', 'slashed' ); ?></p></div> | ||
| <?php endif; ?> | ||
|
|
||
| <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> | ||
| <input type="hidden" name="action" value="slashed_save_settings"> | ||
| <?php wp_nonce_field( self::NONCE_ACTION, self::NONCE_KEY ); ?> | ||
|
|
||
| <h2 style="margin-top:1.5em;"><?php esc_html_e( 'Active integrations', 'slashed' ); ?></h2> | ||
| <p class="description"><?php esc_html_e( 'Enable the integrations for the builders you use. SLASHED loads its CSS and syncs design tokens only for active integrations.', 'slashed' ); ?></p> | ||
|
|
||
| <table class="form-table" role="presentation"> | ||
| <tr> | ||
| <th scope="row"><?php esc_html_e( 'Bricks Builder', 'slashed' ); ?></th> | ||
| <td> | ||
| <label> | ||
| <input type="checkbox" name="integrations[bricks]" | ||
| <?php checked( $integrations['bricks'] ); ?>> | ||
| <?php esc_html_e( 'Enable', 'slashed' ); ?> | ||
| </label> | ||
| <p class="description"> | ||
| <?php | ||
| if ( defined( 'BRICKS_VERSION' ) ) { | ||
| printf( | ||
| /* translators: %s: Bricks version */ | ||
| esc_html__( 'Bricks %s detected.', 'slashed' ), | ||
| esc_html( BRICKS_VERSION ) | ||
| ); | ||
| } else { | ||
| esc_html_e( 'Bricks Builder not detected — integration will have no effect until Bricks is installed.', 'slashed' ); | ||
| } | ||
| ?> | ||
| <?php if ( $integrations['bricks'] ) : ?> | ||
| — <a href="<?php echo esc_url( admin_url( 'admin.php?page=slashed-bricks' ) ); ?>"><?php esc_html_e( 'Manage Bricks token overrides →', 'slashed' ); ?></a> | ||
| <?php endif; ?> | ||
| </p> | ||
| </td> | ||
| </tr> | ||
| <tr> | ||
| <th scope="row"><?php esc_html_e( 'Gutenberg / Block Editor', 'slashed' ); ?></th> | ||
| <td> | ||
| <label> | ||
| <input type="checkbox" name="integrations[gutenberg]" | ||
| <?php checked( $integrations['gutenberg'] ); ?>> | ||
| <?php esc_html_e( 'Enable', 'slashed' ); ?> | ||
| </label> | ||
| <p class="description"><?php esc_html_e( 'Loads SLASHED CSS in the block editor canvas and syncs the color palette.', 'slashed' ); ?></p> | ||
| </td> | ||
| </tr> | ||
| </table> | ||
|
|
||
| <h2><?php esc_html_e( 'CSS bundle', 'slashed' ); ?></h2> | ||
| <p class="description"><?php esc_html_e( 'All active integrations load the same bundle. Choose the smallest bundle that covers your needs.', 'slashed' ); ?></p> | ||
|
|
||
| <table class="form-table" role="presentation"> | ||
| <?php | ||
| $bundles = array( | ||
| 'essential' => __( 'Essential — reset + design tokens only', 'slashed' ), | ||
| 'optimal' => __( 'Optimal — + layout, typography, states, forms (recommended)', 'slashed' ), | ||
| 'full' => __( 'Full — + utilities layer', 'slashed' ), | ||
| ); | ||
| foreach ( $bundles as $value => $label ) : | ||
| ?> | ||
| <tr> | ||
| <th scope="row" style="padding-top:6px;padding-bottom:6px;"> | ||
| <label for="bundle-<?php echo esc_attr( $value ); ?>"><?php echo esc_html( $label ); ?></label> | ||
| </th> | ||
| <td style="padding-top:6px;padding-bottom:6px;"> | ||
| <input type="radio" id="bundle-<?php echo esc_attr( $value ); ?>" | ||
| name="css_bundle" value="<?php echo esc_attr( $value ); ?>" | ||
| <?php checked( $css_bundle, $value ); ?>> | ||
| </td> | ||
| </tr> | ||
| <?php endforeach; ?> | ||
| </table> | ||
|
|
||
| <?php submit_button( __( 'Save settings', 'slashed' ) ); ?> | ||
| </form> | ||
|
|
||
| <hr> | ||
| <p style="color:#999;font-size:12px;"> | ||
| <?php | ||
| printf( | ||
| /* translators: %s: CSS ref tag */ | ||
| esc_html__( 'Framework: %s · CSS dist SHA: %s', 'slashed' ), | ||
| esc_html( SLASHED_CSS_REF ), | ||
| '<code>' . esc_html( substr( SLASHED_DIST_SHA, 0, 8 ) ) . '</code>' | ||
| ); | ||
| ?> | ||
| </p> | ||
| </div> | ||
| <?php | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| <?php | ||
| /** | ||
| * Shared CSS bundle resolution for all SLASHED integrations. | ||
| * | ||
| * @package SLASHED | ||
| */ | ||
|
|
||
| if ( ! defined( 'ABSPATH' ) ) { | ||
| exit; | ||
| } | ||
|
|
||
| /** | ||
| * Class Slashed_CSS_Loader | ||
| * | ||
| * Builder-agnostic helper used by every integration to locate the correct | ||
| * SLASHED CSS bundle. Integrations call these methods instead of duplicating | ||
| * CDN/local-path logic. They may then apply their own filter on top of the | ||
| * returned URL to allow per-integration overrides. | ||
| * | ||
| * In standalone mode (integration plugin activated without slashed.php) this | ||
| * class is not loaded and each integration falls back to its own inline logic. | ||
| * Adding a new integration never touches this file. | ||
| */ | ||
| class Slashed_CSS_Loader { | ||
|
|
||
| /** | ||
| * Get the configured CSS bundle variant. | ||
| * | ||
| * Delegates to Slashed_Settings::get_css_bundle(), which validates the | ||
| * value against the canonical allowlist and falls back to 'optimal'. | ||
| * | ||
| * @return string One of 'essential', 'optimal', 'full'. | ||
| */ | ||
| public static function get_bundle() { | ||
| return Slashed_Settings::get_css_bundle(); | ||
| } | ||
|
|
||
| /** | ||
| * Get the URL for the SLASHED CSS bundle. | ||
| * | ||
| * Resolution order: | ||
| * 1. Local dist/ under the unified plugin root (dev / packaged-dist mode). | ||
| * 2. jsDelivr CDN pinned to the immutable dist-branch SHA. | ||
| * | ||
| * Applies the 'slashed/css_bundle_url' filter so site owners can override | ||
| * the URL globally. Integrations apply their own filter on top of this | ||
| * return value when per-integration overrides are needed. | ||
| * | ||
| * @return string | ||
| */ | ||
| public static function get_url() { | ||
| $bundle = self::get_bundle(); | ||
| $filename = 'slashed.' . $bundle . '.css'; | ||
|
|
||
| $url = sprintf( | ||
| 'https://cdn.jsdelivr.net/gh/codeslash-dev/SLASHED@%s/%s', | ||
| SLASHED_DIST_SHA, | ||
| $filename | ||
| ); | ||
|
|
||
| $local = SLASHED_PATH . 'dist/' . $filename; | ||
| if ( file_exists( $local ) ) { | ||
| $url = SLASHED_URL . 'dist/' . $filename; | ||
| } | ||
|
|
||
| /** | ||
| * Filter the SLASHED CSS bundle URL (all integrations). | ||
| * | ||
| * For a per-integration override, use slashed_bricks/css_bundle_url, | ||
| * slashed_gutenberg/css_bundle_url, etc. instead. | ||
| * | ||
| * @param string $url URL to the CSS bundle file. | ||
| */ | ||
| return apply_filters( 'slashed/css_bundle_url', $url ); | ||
| } | ||
|
|
||
| /** | ||
| * Derive a cache-busting version string for a resolved CSS URL. | ||
| * | ||
| * Returns the file's mtime when the URL maps to a local file under | ||
| * SLASHED_PATH; falls back to SLASHED_VERSION for CDN or unknown URLs. | ||
| * | ||
| * @param string $url Resolved CSS bundle URL. | ||
| * @return string | ||
| */ | ||
| public static function get_version( $url ) { | ||
| // Check if URL maps to the local dist/ directory. | ||
| if ( 0 === strpos( $url, SLASHED_URL . 'dist/' ) ) { | ||
| $relative = substr( $url, strlen( SLASHED_URL . 'dist/' ) ); | ||
| $path = SLASHED_PATH . 'dist/' . $relative; | ||
| if ( file_exists( $path ) ) { | ||
| return (string) filemtime( $path ); | ||
| } | ||
| } | ||
| return SLASHED_VERSION; | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.