From 508c9c507e8f311ee56c93c4aeee0c2ae9ba00cc Mon Sep 17 00:00:00 2001 From: Kiro Agent <244629292+kiro-agent@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:05:23 +0000 Subject: [PATCH 1/4] ci: harden pre-merge checks and supply chain - pin all GitHub Actions to commit SHAs across every workflow - add commitlint job validating PR commits (Conventional Commits) - add svelte-check type-check gate for editor-app and admin-app - add PHPCS (WordPress security/correctness ruleset) and PHPStan (level 5 with baseline) for the plugin PHP, replacing php -l alone - add npm audit job (root + both Svelte apps, high/critical gate) - add CI concurrency to cancel superseded PR runs - fix mispositioned phpcs:ignore in class-rebemer-rest.php so the safe $wpdb->prepare() IN(...) query passes the new PreparedSQL sniff Co-authored-by: Jack Granatowski --- .github/workflows/ci.yml | 89 +- .github/workflows/codeql.yml | 6 +- .github/workflows/framework-sync.yml | 8 +- .github/workflows/release.yml | 4 +- .gitignore | 5 + .../bricks/admin-app/jsconfig.json | 13 + .../bricks/admin-app/package-lock.json | 104 +++ .../bricks/admin-app/package.json | 5 +- .../bricks/editor-app/jsconfig.json | 13 + .../bricks/editor-app/package-lock.json | 104 +++ .../bricks/editor-app/package.json | 5 +- .../bricks/includes/class-rebemer-rest.php | 3 +- composer.json | 28 + composer.lock | 829 ++++++++++++++++++ phpcs.xml.dist | 47 + phpstan-baseline.neon | 232 +++++ phpstan.neon.dist | 21 + 17 files changed, 1495 insertions(+), 21 deletions(-) create mode 100644 SLASHED-for-WP/integrations/bricks/admin-app/jsconfig.json create mode 100644 SLASHED-for-WP/integrations/bricks/editor-app/jsconfig.json create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 phpcs.xml.dist create mode 100644 phpstan-baseline.neon create mode 100644 phpstan.neon.dist diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8fa21f5..55b4f477 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,14 +8,40 @@ on: permissions: contents: read +# Cancel superseded runs on the same PR ref so CI only spends minutes on the +# newest commit. Pushes to main run to completion. +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: + commitlint: + name: Lint commit messages + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 22 + cache: npm + - run: npm ci + - name: Validate PR commits against Conventional Commits + run: >- + npx --no -- commitlint + --from ${{ github.event.pull_request.base.sha }} + --to ${{ github.event.pull_request.head.sha }} + --verbose + build-editor-app: name: Build editor-app runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-node@v6 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22 cache: npm @@ -24,6 +50,9 @@ jobs: - name: Install dependencies run: npm ci --prefix SLASHED-for-WP/integrations/bricks/editor-app + - name: Type-check (svelte-check) + run: npm --prefix SLASHED-for-WP/integrations/bricks/editor-app run check + - name: Build run: npm --prefix SLASHED-for-WP/integrations/bricks/editor-app run build @@ -31,9 +60,9 @@ jobs: name: Build admin-app runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-node@v6 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22 cache: npm @@ -42,6 +71,9 @@ jobs: - name: Install dependencies run: npm ci --prefix SLASHED-for-WP/integrations/bricks/admin-app + - name: Type-check (svelte-check) + run: npm --prefix SLASHED-for-WP/integrations/bricks/admin-app run check + - name: Build run: npm --prefix SLASHED-for-WP/integrations/bricks/admin-app run build @@ -49,21 +81,25 @@ jobs: name: Lint, test, version & drift checks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-node@v6 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22 cache: npm - - uses: shivammathur/setup-php@v2 + - uses: shivammathur/setup-php@8358ee0e2e8afe63c2fb8253d1d52085811ab1e5 # v2 with: php-version: '8.2' coverage: none + tools: composer - - name: Install dependencies + - name: Install npm dependencies run: npm ci + - name: Install PHP dev dependencies + run: composer install --no-interaction --no-progress + - name: Unit tests (editor-app libs + version guard) run: npm test @@ -73,9 +109,20 @@ jobs: - name: Lint CSS (stylelint) run: npm run lint:css - - name: Lint PHP (php -l) + - name: Lint PHP syntax (php -l) run: npm run lint:php + - name: Register PHPCS standards + run: >- + vendor/bin/phpcs --config-set installed_paths + vendor/wp-coding-standards/wpcs,vendor/phpcsstandards/phpcsutils,vendor/phpcsstandards/phpcsextra,vendor/phpcompatibility/php-compatibility,vendor/phpcompatibility/phpcompatibility-paragonie,vendor/phpcompatibility/phpcompatibility-wp + + - name: Lint PHP (PHPCS / WordPress standards) + run: composer phpcs + + - name: Static analysis (PHPStan) + run: composer phpstan + - name: Cheatsheet coverage run: npm run check:cheatsheet @@ -85,3 +132,27 @@ jobs: echo "Cloning framework $REF for admin-app drift check" git clone --depth 1 --branch "$REF" https://github.com/codeslash-dev/SLASHED.git .framework SLASHED_FRAMEWORK_DIR=.framework npm run check:admin-app + + dependency-audit: + name: Dependency vulnerability audit + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 22 + cache: npm + + # Audit the root toolchain and both bundled Svelte apps. Fail only on + # high/critical advisories so routine low-severity noise doesn't block. + - name: Audit root dependencies + run: npm ci && npm audit --audit-level=high + - name: Audit editor-app dependencies + run: | + npm ci --prefix SLASHED-for-WP/integrations/bricks/editor-app + npm audit --audit-level=high --prefix SLASHED-for-WP/integrations/bricks/editor-app + - name: Audit admin-app dependencies + run: | + npm ci --prefix SLASHED-for-WP/integrations/bricks/admin-app + npm audit --audit-level=high --prefix SLASHED-for-WP/integrations/bricks/admin-app diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index efdca525..a0256120 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -21,13 +21,13 @@ jobs: matrix: language: [javascript-typescript, actions] steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: github/codeql-action/init@v3 + - uses: github/codeql-action/init@b0c4fd77f6c559021d78430ec4d0d169ae74a4eb # v3 with: languages: ${{ matrix.language }} queries: security-extended - - uses: github/codeql-action/analyze@v3 + - uses: github/codeql-action/analyze@b0c4fd77f6c559021d78430ec4d0d169ae74a4eb # v3 with: category: /language:${{ matrix.language }} diff --git a/.github/workflows/framework-sync.yml b/.github/workflows/framework-sync.yml index e1b05b65..cbd90d41 100644 --- a/.github/workflows/framework-sync.yml +++ b/.github/workflows/framework-sync.yml @@ -22,9 +22,9 @@ jobs: sync: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-node@v6 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22 cache: npm @@ -41,7 +41,7 @@ jobs: npm run update-framework -- --version="$VERSION" fi - - uses: shivammathur/setup-php@v2 + - uses: shivammathur/setup-php@8358ee0e2e8afe63c2fb8253d1d52085811ab1e5 # v2 with: php-version: '8.2' coverage: none @@ -59,7 +59,7 @@ jobs: echo "tag=$REF" >> "$GITHUB_OUTPUT" - name: Open PR if anything changed - uses: peter-evans/create-pull-request@v8 + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8 with: commit-message: "chore: sync bundled framework CSS to ${{ steps.ref.outputs.tag }}" branch: chore/framework-sync diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9b576440..c005ef30 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,13 +26,13 @@ jobs: name: Build & publish WordPress plugin zip runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 # For workflow_dispatch, check out the specified tag. ref: ${{ github.event.inputs.tag || github.ref }} - - uses: actions/setup-node@v6 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22 cache: npm diff --git a/.gitignore b/.gitignore index a98fad90..5b1f392a 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,8 @@ SLASHED-for-WP/integrations/bricks/assets/editor-app/*.map .DS_Store .agents/ .claude/ + + +# PHP Composer dependencies (PHPCS/WPCS + PHPStan tooling). +# composer.json + composer.lock are tracked; the installed tree is not. +/vendor/ diff --git a/SLASHED-for-WP/integrations/bricks/admin-app/jsconfig.json b/SLASHED-for-WP/integrations/bricks/admin-app/jsconfig.json new file mode 100644 index 00000000..be23135c --- /dev/null +++ b/SLASHED-for-WP/integrations/bricks/admin-app/jsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "moduleResolution": "bundler", + "target": "esnext", + "module": "esnext", + "allowJs": true, + "checkJs": false, + "strict": false, + "skipLibCheck": true, + "isolatedModules": true + }, + "include": ["src/**/*.js", "src/**/*.svelte"] +} diff --git a/SLASHED-for-WP/integrations/bricks/admin-app/package-lock.json b/SLASHED-for-WP/integrations/bricks/admin-app/package-lock.json index 7094c312..611b4667 100644 --- a/SLASHED-for-WP/integrations/bricks/admin-app/package-lock.json +++ b/SLASHED-for-WP/integrations/bricks/admin-app/package-lock.json @@ -10,6 +10,8 @@ "devDependencies": { "@sveltejs/vite-plugin-svelte": "^7.1.2", "svelte": "^5.56.3", + "svelte-check": "^4.6.0", + "typescript": "^6.0.3", "vite": "^8.0.16" } }, @@ -400,6 +402,16 @@ "acorn": "^8.9.0" } }, + "node_modules/@sveltejs/load-config": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.1.1.tgz", + "integrity": "sha512-BXXm+VOH/9X4N7Dd1iZ2MqA1h7M+9i2noI8QYuLDY8QcN2WHYn7D/VK/+IJNfcAmRw7ACNJ538UT9GXIhnBTiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + } + }, "node_modules/@sveltejs/vite-plugin-svelte": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.1.2.tgz", @@ -478,6 +490,22 @@ "node": ">= 0.4" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -861,6 +889,16 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/nanoid": { "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", @@ -943,6 +981,20 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/rolldown": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", @@ -977,6 +1029,19 @@ "@rolldown/binding-win32-x64-msvc": "1.0.3" } }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -1015,6 +1080,31 @@ "node": ">=18" } }, + "node_modules/svelte-check": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.6.0.tgz", + "integrity": "sha512-KhVnDFDSid57mmZtHz8gfW8AAGylOZ0vPnOIzVmAL+urzwK8sBYXRss953gD8T0OdgAQ11mdWhE6uadmtOz8TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "@sveltejs/load-config": "0.1.1", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -1040,6 +1130,20 @@ "license": "0BSD", "optional": true }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/vite": { "version": "8.0.16", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", diff --git a/SLASHED-for-WP/integrations/bricks/admin-app/package.json b/SLASHED-for-WP/integrations/bricks/admin-app/package.json index 23f41c30..136bb755 100644 --- a/SLASHED-for-WP/integrations/bricks/admin-app/package.json +++ b/SLASHED-for-WP/integrations/bricks/admin-app/package.json @@ -7,11 +7,14 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "check": "svelte-check --tsconfig ./jsconfig.json" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^7.1.2", "svelte": "^5.56.3", + "svelte-check": "^4.6.0", + "typescript": "^6.0.3", "vite": "^8.0.16" } } diff --git a/SLASHED-for-WP/integrations/bricks/editor-app/jsconfig.json b/SLASHED-for-WP/integrations/bricks/editor-app/jsconfig.json new file mode 100644 index 00000000..be23135c --- /dev/null +++ b/SLASHED-for-WP/integrations/bricks/editor-app/jsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "moduleResolution": "bundler", + "target": "esnext", + "module": "esnext", + "allowJs": true, + "checkJs": false, + "strict": false, + "skipLibCheck": true, + "isolatedModules": true + }, + "include": ["src/**/*.js", "src/**/*.svelte"] +} diff --git a/SLASHED-for-WP/integrations/bricks/editor-app/package-lock.json b/SLASHED-for-WP/integrations/bricks/editor-app/package-lock.json index 06dcd1ba..c5eb78cd 100644 --- a/SLASHED-for-WP/integrations/bricks/editor-app/package-lock.json +++ b/SLASHED-for-WP/integrations/bricks/editor-app/package-lock.json @@ -10,6 +10,8 @@ "devDependencies": { "@sveltejs/vite-plugin-svelte": "^7.1.2", "svelte": "^5.56.3", + "svelte-check": "^4.6.0", + "typescript": "^6.0.3", "vite": "^8.0.16" } }, @@ -400,6 +402,16 @@ "acorn": "^8.9.0" } }, + "node_modules/@sveltejs/load-config": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.1.1.tgz", + "integrity": "sha512-BXXm+VOH/9X4N7Dd1iZ2MqA1h7M+9i2noI8QYuLDY8QcN2WHYn7D/VK/+IJNfcAmRw7ACNJ538UT9GXIhnBTiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + } + }, "node_modules/@sveltejs/vite-plugin-svelte": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.1.2.tgz", @@ -478,6 +490,22 @@ "node": ">= 0.4" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/clsx": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", @@ -861,6 +889,16 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/nanoid": { "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", @@ -943,6 +981,20 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/rolldown": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", @@ -977,6 +1029,19 @@ "@rolldown/binding-win32-x64-msvc": "1.0.3" } }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -1015,6 +1080,31 @@ "node": ">=18" } }, + "node_modules/svelte-check": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.6.0.tgz", + "integrity": "sha512-KhVnDFDSid57mmZtHz8gfW8AAGylOZ0vPnOIzVmAL+urzwK8sBYXRss953gD8T0OdgAQ11mdWhE6uadmtOz8TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "@sveltejs/load-config": "0.1.1", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -1040,6 +1130,20 @@ "license": "0BSD", "optional": true }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/vite": { "version": "8.0.16", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", diff --git a/SLASHED-for-WP/integrations/bricks/editor-app/package.json b/SLASHED-for-WP/integrations/bricks/editor-app/package.json index 44423dbb..134987a4 100644 --- a/SLASHED-for-WP/integrations/bricks/editor-app/package.json +++ b/SLASHED-for-WP/integrations/bricks/editor-app/package.json @@ -7,11 +7,14 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "check": "svelte-check --tsconfig ./jsconfig.json" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "^7.1.2", "svelte": "^5.56.3", + "svelte-check": "^4.6.0", + "typescript": "^6.0.3", "vite": "^8.0.16" } } diff --git a/SLASHED-for-WP/integrations/bricks/includes/class-rebemer-rest.php b/SLASHED-for-WP/integrations/bricks/includes/class-rebemer-rest.php index 2b2a81ad..faf303bd 100644 --- a/SLASHED-for-WP/integrations/bricks/includes/class-rebemer-rest.php +++ b/SLASHED-for-WP/integrations/bricks/includes/class-rebemer-rest.php @@ -218,8 +218,8 @@ private function count_class_references( $classes ) { // with reproducible content rather than whatever order MySQL // happened to return rows in. $rows = $wpdb->get_col( + // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $placeholders is a list of literal %s built from a constant key list; the values are bound through prepare(). $wpdb->prepare( - // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared "SELECT meta_value FROM {$wpdb->postmeta} WHERE meta_key IN ($placeholders) AND meta_value != '' @@ -227,6 +227,7 @@ private function count_class_references( $classes ) { LIMIT %d", array_merge( self::BRICKS_CONTENT_META_KEYS, array( $cap + 1 ) ) ) + // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared ); if ( ! is_array( $rows ) ) { diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..99ea1a7b --- /dev/null +++ b/composer.json @@ -0,0 +1,28 @@ +{ + "name": "codeslash-dev/slashed-for-wp", + "description": "Dev tooling (static analysis + coding standards) for the SLASHED for WordPress plugin.", + "type": "wordpress-plugin", + "license": "MIT", + "require": { + "php": ">=7.4" + }, + "require-dev": { + "squizlabs/php_codesniffer": "^3.13", + "wp-coding-standards/wpcs": "^3.2", + "phpcompatibility/phpcompatibility-wp": "^2.1", + "dealerdirect/phpcodesniffer-composer-installer": "^1.1", + "phpstan/phpstan": "^2.1", + "szepeviktor/phpstan-wordpress": "^2.0", + "php-stubs/wordpress-stubs": "^6.7" + }, + "scripts": { + "phpcs": "phpcs", + "phpcbf": "phpcbf", + "phpstan": "phpstan analyse --memory-limit=1G" + }, + "config": { + "allow-plugins": { + "dealerdirect/phpcodesniffer-composer-installer": true + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000..7f4ca961 --- /dev/null +++ b/composer.lock @@ -0,0 +1,829 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "491c47c75ac08466a470741523a2e394", + "packages": [], + "packages-dev": [ + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v1.2.1", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd", + "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.2", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "^2.2", + "ext-json": "*", + "ext-zip": "*", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev", + "yoast/phpunit-polyfills": "^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "opensource@frenck.dev", + "homepage": "https://frenck.dev", + "role": "Open source developer" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcbf", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", + "source": "https://github.com/PHPCSStandards/composer-installer" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2026-05-06T08:26:05+00:00" + }, + { + "name": "php-stubs/wordpress-stubs", + "version": "v6.9.4", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/wordpress-stubs.git", + "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/90a9412826b9944f93b10bf41d795b5fe68abcd5", + "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5", + "shasum": "" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "5.6.1" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "nikic/php-parser": "^5.5", + "php": "^7.4 || ^8.0", + "php-stubs/generator": "^0.8.6", + "phpdocumentor/reflection-docblock": "^6.0", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^9.5", + "symfony/polyfill-php80": "*", + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", + "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" + }, + "suggest": { + "paragonie/sodium_compat": "Pure PHP implementation of libsodium", + "symfony/polyfill-php80": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WordPress function and class declaration stubs for static analysis.", + "homepage": "https://github.com/php-stubs/wordpress-stubs", + "keywords": [ + "PHPStan", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/php-stubs/wordpress-stubs/issues", + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.9.4" + }, + "time": "2026-05-01T20:36:01+00:00" + }, + { + "name": "phpcompatibility/php-compatibility", + "version": "9.3.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" + }, + "conflict": { + "squizlabs/php_codesniffer": "2.6.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "homepage": "https://github.com/wimg", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + } + ], + "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", + "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", + "keywords": [ + "compatibility", + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibility" + }, + "time": "2019-12-27T09:44:58+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-paragonie", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "paragonie/random_compat": "dev-master", + "paragonie/sodium_compat": "dev-master" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "paragonie", + "phpcs", + "polyfill", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-09-19T17:43:28+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-wp", + "version": "2.1.8", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/7c8d18b4d90dac9e86b0869a608fa09158e168fa", + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0", + "phpcompatibility/phpcompatibility-paragonie": "^1.0", + "squizlabs/php_codesniffer": "^3.3" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityWP/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-10-18T00:05:59+00:00" + }, + { + "name": "phpcsstandards/phpcsextra", + "version": "1.5.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", + "reference": "b598aa890815b8df16363271b659d73280129101" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/b598aa890815b8df16363271b659d73280129101", + "reference": "b598aa890815b8df16363271b659d73280129101", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "phpcsstandards/phpcsutils": "^1.2.0", + "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcsstandards/phpcsdevcs": "^1.2.0", + "phpcsstandards/phpcsdevtools": "^1.2.1", + "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors" + } + ], + "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues", + "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSExtra" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-11-12T23:06:57+00:00" + }, + { + "name": "phpcsstandards/phpcsutils", + "version": "1.2.2", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", + "reference": "c216317e96c8b3f5932808f9b0f1f7a14e3bbf55" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/c216317e96c8b3f5932808f9b0f1f7a14e3bbf55", + "reference": "c216317e96c8b3f5932808f9b0f1f7a14e3bbf55", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.13.5 || ^4.0.1" + }, + "require-dev": { + "ext-filter": "*", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcsstandards/phpcsdevcs": "^1.2.0", + "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0 || ^3.0.0" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPCSUtils/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" + } + ], + "description": "A suite of utility functions for use with PHP_CodeSniffer", + "homepage": "https://phpcsutils.com/", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "phpcs3", + "phpcs4", + "standards", + "static analysis", + "tokens", + "utility" + ], + "support": { + "docs": "https://phpcsutils.com/", + "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", + "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSUtils" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-12-08T14:27:58+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "2.2.2", + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e5cc34d491a90e79c216d824f60fe21fd4d93bd6", + "reference": "e5cc34d491a90e79c216d824f60fe21fd4d93bd6", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ondřej Mirtes" + }, + { + "name": "Markus Staab" + }, + { + "name": "Vincent Langlet" + } + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2026-06-05T09:00:01+00:00" + }, + { + "name": "squizlabs/php_codesniffer", + "version": "3.13.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "shasum": "" + }, + "require": { + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" + }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-11-04T16:30:35+00:00" + }, + { + "name": "szepeviktor/phpstan-wordpress", + "version": "v2.0.3", + "source": { + "type": "git", + "url": "https://github.com/szepeviktor/phpstan-wordpress.git", + "reference": "aa722f037b2d034828cd6c55ebe9e5c74961927e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/szepeviktor/phpstan-wordpress/zipball/aa722f037b2d034828cd6c55ebe9e5c74961927e", + "reference": "aa722f037b2d034828cd6c55ebe9e5c74961927e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "php-stubs/wordpress-stubs": "^6.6.2", + "phpstan/phpstan": "^2.0" + }, + "require-dev": { + "composer/composer": "^2.1.14", + "composer/semver": "^3.4", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.1", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.0", + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.0", + "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" + }, + "suggest": { + "swissspidy/phpstan-no-private": "Detect usage of internal core functions, classes and methods" + }, + "type": "phpstan-extension", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "psr-4": { + "SzepeViktor\\PHPStan\\WordPress\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WordPress extensions for PHPStan", + "keywords": [ + "PHPStan", + "code analyse", + "code analysis", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/szepeviktor/phpstan-wordpress/issues", + "source": "https://github.com/szepeviktor/phpstan-wordpress/tree/v2.0.3" + }, + "time": "2025-09-14T02:58:22+00:00" + }, + { + "name": "wp-coding-standards/wpcs", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", + "reference": "7795ec6fa05663d716a549d0b44e47ffc8b0d4a6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/7795ec6fa05663d716a549d0b44e47ffc8b0d4a6", + "reference": "7795ec6fa05663d716a549d0b44e47ffc8b0d4a6", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "ext-libxml": "*", + "ext-tokenizer": "*", + "ext-xmlreader": "*", + "php": ">=7.2", + "phpcsstandards/phpcsextra": "^1.5.0", + "phpcsstandards/phpcsutils": "^1.1.0", + "squizlabs/php_codesniffer": "^3.13.4" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcompatibility/php-compatibility": "^10.0.0@dev", + "phpcsstandards/phpcsdevtools": "^1.2.0", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "suggest": { + "ext-iconv": "For improved results", + "ext-mbstring": "For improved results" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", + "keywords": [ + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", + "source": "https://github.com/WordPress/WordPress-Coding-Standards", + "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/php_codesniffer", + "type": "custom" + } + ], + "time": "2025-11-25T12:08:04+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": {}, + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=7.4" + }, + "platform-dev": {}, + "plugin-api-version": "2.9.0" +} diff --git a/phpcs.xml.dist b/phpcs.xml.dist new file mode 100644 index 00000000..96dc2f8a --- /dev/null +++ b/phpcs.xml.dist @@ -0,0 +1,47 @@ + + + Coding standards for the SLASHED for WordPress plugin. + + + SLASHED-for-WP + + + */dist/* + */data/* + */vendor/* + */node_modules/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon new file mode 100644 index 00000000..d211db55 --- /dev/null +++ b/phpstan-baseline.neon @@ -0,0 +1,232 @@ +parameters: + ignoreErrors: + - + message: '#^Constant SLASHED_PATH not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/includes/class-admin.php + + - + message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: SLASHED-for-WP/includes/class-css-generator.php + + - + message: '#^Constant SLASHED_PATH not found\.$#' + identifier: constant.notFound + count: 2 + path: SLASHED-for-WP/includes/class-css-loader.php + + - + message: '#^Constant SLASHED_URL not found\.$#' + identifier: constant.notFound + count: 3 + path: SLASHED-for-WP/includes/class-css-loader.php + + - + message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: SLASHED-for-WP/includes/class-css-parser.php + + - + message: ''' + #^PHPDoc tag @property has invalid value \(initial\-value declarations\. Returns an associative array + mapping variable names to their raw declared values\.\)\: Unexpected token "declarations", expected variable at offset 169 on line 5$# + ''' + identifier: phpDoc.parseError + count: 1 + path: SLASHED-for-WP/includes/class-css-parser.php + + - + message: '#^Constant SLASHED_PATH not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/includes/class-framework-updater.php + + - + message: '#^Unreachable statement \- code above always terminates\.$#' + identifier: deadCode.unreachable + count: 4 + path: SLASHED-for-WP/includes/class-framework-updater.php + + - + message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 2 + path: SLASHED-for-WP/includes/class-inventory.php + + - + message: '#^Offset ''color_values'' on array\{variables\: array\, sf_classes\: array\, is_classes\: array\\} on left side of \?\? does not exist\.$#' + identifier: nullCoalesce.offset + count: 2 + path: SLASHED-for-WP/includes/class-inventory.php + + - + message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: SLASHED-for-WP/includes/class-rest-controller.php + + - + message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 2 + path: SLASHED-for-WP/includes/class-tab-registry.php + + - + message: '#^Constant SLASHED_BRICKS_PATH not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/includes/class-token-page.php + + - + message: '#^Constant SLASHED_BRICKS_URL not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/includes/class-token-page.php + + - + message: '#^Constant SLASHED_PATH not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/includes/class-token-page.php + + - + message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: SLASHED-for-WP/includes/class-token-sanitizer.php + + - + message: '#^Parameter \#1 \$array \(list\\) of array_values is already a list, call has no effect\.$#' + identifier: arrayValues.list + count: 1 + path: SLASHED-for-WP/integrations/bricks/includes/class-classes.php + + - + message: '#^Constant SLASHED_BRICKS_PATH not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/integrations/bricks/includes/class-color-resolver.php + + - + message: '#^Constant SLASHED_BRICKS_PATH not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/integrations/bricks/includes/class-css-parser.php + + - + message: '#^Constant SLASHED_BRICKS_URL not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/integrations/bricks/includes/class-enqueue.php + + - + message: '#^Constant SLASHED_BRICKS_PATH not found\.$#' + identifier: constant.notFound + count: 3 + path: SLASHED-for-WP/integrations/bricks/includes/class-inventory.php + + - + message: '#^Constant SLASHED_BRICKS_URL not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/integrations/bricks/includes/class-inventory.php + + - + message: '#^Constant SLASHED_BRICKS_PATH not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/integrations/bricks/includes/class-rebemer-enqueue.php + + - + message: '#^Constant SLASHED_BRICKS_URL not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/integrations/bricks/includes/class-rebemer-enqueue.php + + - + message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: SLASHED-for-WP/integrations/bricks/includes/class-rebemer-rest.php + + - + message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: SLASHED-for-WP/integrations/bricks/includes/class-variables.php + + - + message: '#^Parameter \#1 \$array \(list\\) of array_values is already a list, call has no effect\.$#' + identifier: arrayValues.list + count: 1 + path: SLASHED-for-WP/integrations/bricks/includes/class-variables.php + + - + message: '#^Constant SLASHED_BRICKS_PATH not found\.$#' + identifier: constant.notFound + count: 12 + path: SLASHED-for-WP/integrations/bricks/slashed-bricks.php + + - + message: '#^Constant SLASHED_BRICKS_URL not found\.$#' + identifier: constant.notFound + count: 2 + path: SLASHED-for-WP/integrations/bricks/slashed-bricks.php + + - + message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php + + - + message: '#^Call to function method_exists\(\) with ''Slashed_Token_Page'' and ''get_class_hints'' will always evaluate to true\.$#' + identifier: function.alreadyNarrowedType + count: 1 + path: SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php + + - + message: '#^Constant SLASHED_GUTENBERG_PATH not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php + + - + message: '#^Constant SLASHED_GUTENBERG_URL not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php + + - + message: '#^Constant SLASHED_GUTENBERG_PATH not found\.$#' + identifier: constant.notFound + count: 2 + path: SLASHED-for-WP/integrations/gutenberg/includes/class-enqueue.php + + - + message: '#^Constant SLASHED_GUTENBERG_PATH not found\.$#' + identifier: constant.notFound + count: 1 + path: SLASHED-for-WP/integrations/gutenberg/includes/class-inventory.php + + - + message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(string\)\: bool\)\|null, ''strlen'' given\.$#' + identifier: argument.type + count: 1 + path: SLASHED-for-WP/integrations/gutenberg/includes/class-presets.php + + - + message: '#^Constant SLASHED_GUTENBERG_PATH not found\.$#' + identifier: constant.notFound + count: 9 + path: SLASHED-for-WP/integrations/gutenberg/slashed-gutenberg.php + + - + message: '#^Constant SLASHED_GUTENBERG_URL not found\.$#' + identifier: constant.notFound + count: 2 + path: SLASHED-for-WP/integrations/gutenberg/slashed-gutenberg.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 00000000..751b5fc4 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,21 @@ +includes: + # WordPress function/class/constant stubs + WP-aware rules. + - vendor/szepeviktor/phpstan-wordpress/extension.neon + # Existing findings are frozen here so the gate fails only on NEW issues. + # Regenerate after intentionally addressing/adding code with: + # composer phpstan -- --generate-baseline phpstan-baseline.neon + - phpstan-baseline.neon + +parameters: + level: 5 + + paths: + - SLASHED-for-WP + + excludePaths: + - SLASHED-for-WP/dist/* + - SLASHED-for-WP/data/* + - SLASHED-for-WP/integrations/bricks/data/* + + # The plugin floor is PHP 7.4. + phpVersion: 70400 From 2989096547c317055c615152fd6188912fad686d Mon Sep 17 00:00:00 2001 From: Kiro Agent <244629292+kiro-agent@users.noreply.github.com> Date: Mon, 8 Jun 2026 14:22:12 +0000 Subject: [PATCH 2/4] style: adopt full WordPress coding standard and tighten PHPStan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PHPCS now enforces the complete WordPress-Extra ruleset (was a curated security/correctness subset): - phpcbf-formatted all PHP to WP conventions (tabs, array layout, spacing, pre-increment, single quotes) — whitespace/format only, no logic change - rename $_slashed_shared -> $slashed_shared so the bootstrap global is properly prefixed (PrefixAllGlobals) - configure i18n sniff for the three real text domains (slashed, slashed-bricks, slashed-gutenberg) matching the standalone-plugin layout - document-justified exclusions: class file-name convention, unused params in WP hook callbacks; advisory warnings stay non-blocking PHPStan: shrink the baseline from 71 to 9 by fixing root causes instead of freezing noise: - phpstan-bootstrap.php declares the runtime define() constants - ignore dynamic require_once path resolution - treatPhpDocTypesAsCertain: false (PHPDoc types aren't runtime-enforced) --- SLASHED-for-WP/includes/class-admin.php | 14 +- .../includes/class-color-resolver.php | 94 ++-- .../includes/class-css-generator.php | 16 +- .../includes/class-framework-updater.php | 20 +- SLASHED-for-WP/includes/class-hooks-page.php | 10 +- SLASHED-for-WP/includes/class-inventory.php | 8 +- .../includes/class-rest-controller.php | 84 +-- SLASHED-for-WP/includes/class-settings.php | 19 +- .../includes/class-token-defaults.php | 193 ++++--- SLASHED-for-WP/includes/class-token-page.php | 2 +- .../bricks/includes/class-classes.php | 368 ++++++------- .../bricks/includes/class-enqueue.php | 88 +-- .../bricks/includes/class-rebemer-rest.php | 4 +- .../bricks/includes/class-variables.php | 510 +++++++++--------- .../integrations/bricks/slashed-bricks.php | 262 ++++----- .../includes/class-editor-enqueue.php | 2 +- .../gutenberg/includes/class-presets.php | 41 +- .../gutenberg/slashed-gutenberg.php | 58 +- SLASHED-for-WP/slashed.php | 75 ++- phpcs.xml.dist | 74 ++- phpstan-baseline.neon | 192 ------- phpstan-bootstrap.php | 28 + phpstan.neon.dist | 14 + 23 files changed, 1110 insertions(+), 1066 deletions(-) create mode 100644 phpstan-bootstrap.php diff --git a/SLASHED-for-WP/includes/class-admin.php b/SLASHED-for-WP/includes/class-admin.php index 58145409..8f0faf7d 100644 --- a/SLASHED-for-WP/includes/class-admin.php +++ b/SLASHED-for-WP/includes/class-admin.php @@ -18,8 +18,8 @@ */ class Slashed_Admin { - const PAGE_SLUG = 'slashed'; - const NONCE_KEY = 'slashed_settings_save'; + const PAGE_SLUG = 'slashed'; + const NONCE_KEY = 'slashed_settings_save'; const NONCE_ACTION = 'slashed_save_settings'; public function __construct() { @@ -94,10 +94,12 @@ public function handle_save() { // phpcs:ignore WordPress.Security.NonceVerification.Missing $raw_class_hints = isset( $_POST['show_class_hints'] ); - Slashed_Token_Store::update_plugin_settings( array( - 'html_font_size' => in_array( $raw_font_size, $allowed_font_sizes, true ) ? $raw_font_size : '', - 'show_class_hints' => $raw_class_hints, - ) ); + Slashed_Token_Store::update_plugin_settings( + array( + 'html_font_size' => in_array( $raw_font_size, $allowed_font_sizes, true ) ? $raw_font_size : '', + 'show_class_hints' => $raw_class_hints, + ) + ); wp_safe_redirect( add_query_arg( 'slashed_saved', '1', admin_url( 'admin.php?page=' . self::PAGE_SLUG ) ) diff --git a/SLASHED-for-WP/includes/class-color-resolver.php b/SLASHED-for-WP/includes/class-color-resolver.php index f3737ab0..9b04ef47 100644 --- a/SLASHED-for-WP/includes/class-color-resolver.php +++ b/SLASHED-for-WP/includes/class-color-resolver.php @@ -326,7 +326,7 @@ private static function resolve_sources( $color_values ) { $sources = array(); foreach ( self::default_sources() as $family => $default_oklch ) { - $var_name = '--sf-color-' . $family . '-light'; + $var_name = '--sf-color-' . $family . '-light'; $oklch_str = $default_oklch; if ( isset( $color_values[ $var_name ] ) ) { @@ -398,10 +398,10 @@ private static function resolve_semantic_tokens( $hex_map, $sources ) { // ---- Border tokens ---- // Light-mode formula: oklch(from neutral_light, clamp(min, L + offset, max), chroma, hue). if ( isset( $sources['neutral'] ) ) { - list( $nl, , $nh ) = $sources['neutral']; + list( $nl, , $nh ) = $sources['neutral']; $hex_map['--sf-color-border'] = self::oklch_to_hex( max( 0.70, min( $nl + 0.35, 0.95 ) ), 0.005, $nh ); $hex_map['--sf-color-border--subtle'] = self::oklch_to_hex( max( 0.75, min( $nl + 0.40, 0.97 ) ), 0.005, $nh ); - $hex_map['--sf-color-border--strong'] = self::oklch_to_hex( max( 0.55, min( $nl + 0.10, 0.85 ) ), 0.02, $nh ); + $hex_map['--sf-color-border--strong'] = self::oklch_to_hex( max( 0.55, min( $nl + 0.10, 0.85 ) ), 0.02, $nh ); } else { $hex_map['--sf-color-border'] = '#d4d4de'; $hex_map['--sf-color-border--subtle'] = '#e5e5ec'; @@ -410,8 +410,8 @@ private static function resolve_semantic_tokens( $hex_map, $sources ) { $hex_map['--sf-color-border--muted'] = $hex_map['--sf-color-border--subtle']; // legacy alias $hex_map['--sf-color-border--focus'] = $hex_map['--sf-color-action'] ?? '#0097b4'; // disabled: desaturated border--subtle at 50% opacity over white. - $border_subtle_rgb = self::hex_to_rgb( $hex_map['--sf-color-border--subtle'] ); - $hex_map['--sf-color-border--disabled'] = self::rgb_to_hex( self::mix_rgb( $border_subtle_rgb, $white_rgb, 0.50 ) ); + $border_subtle_rgb = self::hex_to_rgb( $hex_map['--sf-color-border--subtle'] ); + $hex_map['--sf-color-border--disabled'] = self::rgb_to_hex( self::mix_rgb( $border_subtle_rgb, $white_rgb, 0.50 ) ); // translucent: neutral at 15% opacity over white. $hex_map['--sf-color-border--translucent'] = self::rgb_to_hex( self::mix_rgb( $neutral_rgb, $white_rgb, 0.15 ) ); @@ -419,19 +419,19 @@ private static function resolve_semantic_tokens( $hex_map, $sources ) { // Alpha-composited over white (opaque approximation of transparent overlays). $hex_map['--sf-color-bg--hover'] = self::rgb_to_hex( self::mix_rgb( $neutral_rgb, $white_rgb, 0.08 ) ); $hex_map['--sf-color-bg--active'] = self::rgb_to_hex( self::mix_rgb( $neutral_rgb, $white_rgb, 0.12 ) ); - $hex_map['--sf-color-bg--selected'] = self::rgb_to_hex( self::mix_rgb( $action_rgb, $white_rgb, 0.10 ) ); - $hex_map['--sf-color-bg--focus'] = self::rgb_to_hex( self::mix_rgb( $action_rgb, $white_rgb, 0.06 ) ); + $hex_map['--sf-color-bg--selected'] = self::rgb_to_hex( self::mix_rgb( $action_rgb, $white_rgb, 0.10 ) ); + $hex_map['--sf-color-bg--focus'] = self::rgb_to_hex( self::mix_rgb( $action_rgb, $white_rgb, 0.06 ) ); $hex_map['--sf-color-bg--disabled'] = $hex_map['--sf-color-surface']; // = well ≈ surface // ---- Well, raised, inverse, overlay ---- if ( isset( $sources['base'] ) ) { - list( $bl, $bc, $bh ) = $sources['base']; - $hex_map['--sf-color-inset'] = self::oklch_to_hex( max( 0.0, $bl - 0.02 ), $bc, $bh ); + list( $bl, $bc, $bh ) = $sources['base']; + $hex_map['--sf-color-inset'] = self::oklch_to_hex( max( 0.0, $bl - 0.02 ), $bc, $bh ); $hex_map['--sf-color-raised'] = self::oklch_to_hex( min( 1.0, $bl + 0.04 ), $bc, $bh ); $hex_map['--sf-color-inverse'] = self::oklch_to_hex( 1.0 - $bl, $bc, $bh ); $hex_map['--sf-color-overlay'] = $hex_map['--sf-color-surface']; } else { - $hex_map['--sf-color-inset'] = '#f0f2f5'; + $hex_map['--sf-color-inset'] = '#f0f2f5'; $hex_map['--sf-color-raised'] = '#ffffff'; $hex_map['--sf-color-inverse'] = '#0a0a12'; $hex_map['--sf-color-overlay'] = '#fafafa'; @@ -447,13 +447,13 @@ private static function resolve_semantic_tokens( $hex_map, $sources ) { // ---- Link states (light-mode approximation) ---- // Light-mode formula: min(L − offset, cap) keeps link contrast-safe vs page bg. if ( isset( $sources['action'] ) ) { - list( $al, $ac, $ah ) = $sources['action']; - $l_link = max( 0.0, min( $al - 0.07, 0.48 ) ); - $l_hover = max( 0.0, min( $al - 0.15, 0.40 ) ); - $l_active = max( 0.0, min( $al - 0.21, 0.34 ) ); - $hex_map['--sf-color-link'] = self::oklch_to_hex( $l_link, $ac, $ah ); - $hex_map['--sf-color-link--hover'] = self::oklch_to_hex( $l_hover, $ac, $ah ); - $hex_map['--sf-color-link--active'] = self::oklch_to_hex( $l_active, $ac, $ah ); + list( $al, $ac, $ah ) = $sources['action']; + $l_link = max( 0.0, min( $al - 0.07, 0.48 ) ); + $l_hover = max( 0.0, min( $al - 0.15, 0.40 ) ); + $l_active = max( 0.0, min( $al - 0.21, 0.34 ) ); + $hex_map['--sf-color-link'] = self::oklch_to_hex( $l_link, $ac, $ah ); + $hex_map['--sf-color-link--hover'] = self::oklch_to_hex( $l_hover, $ac, $ah ); + $hex_map['--sf-color-link--active'] = self::oklch_to_hex( $l_active, $ac, $ah ); // visited: same lightness clamp, +60° hue shift. $hex_map['--sf-color-link--visited'] = self::oklch_to_hex( $l_link, $ac, fmod( $ah + 60.0, 360.0 ) ); } else { @@ -465,7 +465,7 @@ private static function resolve_semantic_tokens( $hex_map, $sources ) { // underline: action at 30% opacity over white. $hex_map['--sf-color-link--underline'] = self::rgb_to_hex( self::mix_rgb( $action_rgb, $white_rgb, 0.30 ) ); // disabled: = text--disabled. - $hex_map['--sf-color-link--disabled'] = $hex_map['--sf-color-text--disabled']; + $hex_map['--sf-color-link--disabled'] = $hex_map['--sf-color-text--disabled']; // ---- Text-on-color contrast tokens ---- // CSS: oklch(clamp(0.1, sign(threshold − L) × 999, 0.95) 0 0) @@ -487,11 +487,11 @@ private static function resolve_semantic_tokens( $hex_map, $sources ) { // ---- Selection and mark ---- // selection-bg: action at light opacity; approximate as bg--selected. - $hex_map['--sf-color-selection-bg'] = $hex_map['--sf-color-bg--selected']; + $hex_map['--sf-color-selection-bg'] = $hex_map['--sf-color-bg--selected']; // selection-text / mark-text: both are `inherit` in CSS — show as current text color. $hex_map['--sf-color-selection-text'] = $dark_text; // mark-bg: warning at 25% opacity over white. - $hex_map['--sf-color-mark-bg'] = self::rgb_to_hex( self::mix_rgb( $warning_rgb, $white_rgb, 0.25 ) ); + $hex_map['--sf-color-mark-bg'] = self::rgb_to_hex( self::mix_rgb( $warning_rgb, $white_rgb, 0.25 ) ); $hex_map['--sf-color-mark-text'] = $dark_text; // ---- Status strong variants (light-mode: source L minus offset) ---- @@ -507,7 +507,7 @@ private static function resolve_semantic_tokens( $hex_map, $sources ) { if ( ! isset( $sources[ $family ] ) ) { continue; } - list( $sl, $sc, $sh ) = $sources[ $family ]; + list( $sl, $sc, $sh ) = $sources[ $family ]; $hex_map[ '--sf-color-' . $family . '-strong' ] = self::oklch_to_hex( max( 0.0, $sl - $l_offset ), $sc, @@ -549,7 +549,7 @@ private static function resolve_semantic_tokens_dark( $hex_map, $d, $light_sourc $hex_map['--sf-color-base'] = $surface_hex; $hex_map['--sf-color-surface'] = $surface_hex; $hex_map['--sf-color-bg'] = self::oklch_to_hex( min( 1.0, $bl + 0.02 ), $bc, $bh ); - $hex_map['--sf-color-inset'] = self::oklch_to_hex( max( 0.0, $bl - 0.02 ), $bc, $bh ); + $hex_map['--sf-color-inset'] = self::oklch_to_hex( max( 0.0, $bl - 0.02 ), $bc, $bh ); $hex_map['--sf-color-raised'] = self::oklch_to_hex( min( 1.0, $bl + 0.04 ), $bc, $bh ); $hex_map['--sf-color-overlay'] = $surface_hex; $hex_map['--sf-color-inverse'] = self::oklch_to_hex( max( 0.0, 1.0 - $bl ), $bc, $bh ); @@ -558,21 +558,21 @@ private static function resolve_semantic_tokens_dark( $hex_map, $d, $light_sourc // ---- Neutral-derived text + border (dark formulas) ---- if ( isset( $d['neutral'] ) ) { list( $nl, $nc, $nh ) = $d['neutral']; - $neutral_hex = isset( $hex_map['--sf-color-neutral'] ) ? $hex_map['--sf-color-neutral'] : self::oklch_to_hex( $nl, $nc, $nh ); - $neutral_rgb = self::hex_to_rgb( $neutral_hex ); - - $hex_map['--sf-color-text'] = self::oklch_to_hex( max( 0.70, min( $nl + 0.25, 1.0 ) ), $nc, $nh ); - $hex_map['--sf-color-heading'] = $hex_map['--sf-color-text']; - $hex_map['--sf-color-text--secondary'] = self::oklch_to_hex( max( 0.55, min( $nl + 0.1, 0.90 ) ), $nc, $nh ); - $hex_map['--sf-color-text--muted'] = $neutral_hex; - $hex_map['--sf-color-text--placeholder']= self::oklch_to_hex( max( 0.35, min( $nl - 0.1, 0.65 ) ), $nc, $nh ); - $hex_map['--sf-color-text--disabled'] = self::oklch_to_hex( max( 0.25, min( $nl - 0.2, 0.55 ) ), $nc, $nh ); - $hex_map['--sf-color-text--inverse'] = self::oklch_to_hex( max( 0.05, min( $nl - 0.4, 0.35 ) ), $nc, $nh ); - - $hex_map['--sf-color-border'] = self::oklch_to_hex( max( 0.25, min( $nl - 0.3, 0.55 ) ), 0.005, $nh ); - $hex_map['--sf-color-border--subtle'] = self::oklch_to_hex( max( 0.20, min( $nl - 0.38, 0.45 ) ), 0.005, $nh ); - $hex_map['--sf-color-border--strong'] = self::oklch_to_hex( max( 0.38, min( $nl - 0.1, 0.65 ) ), 0.02, $nh ); - $hex_map['--sf-color-border--muted'] = $hex_map['--sf-color-border--subtle']; + $neutral_hex = isset( $hex_map['--sf-color-neutral'] ) ? $hex_map['--sf-color-neutral'] : self::oklch_to_hex( $nl, $nc, $nh ); + $neutral_rgb = self::hex_to_rgb( $neutral_hex ); + + $hex_map['--sf-color-text'] = self::oklch_to_hex( max( 0.70, min( $nl + 0.25, 1.0 ) ), $nc, $nh ); + $hex_map['--sf-color-heading'] = $hex_map['--sf-color-text']; + $hex_map['--sf-color-text--secondary'] = self::oklch_to_hex( max( 0.55, min( $nl + 0.1, 0.90 ) ), $nc, $nh ); + $hex_map['--sf-color-text--muted'] = $neutral_hex; + $hex_map['--sf-color-text--placeholder'] = self::oklch_to_hex( max( 0.35, min( $nl - 0.1, 0.65 ) ), $nc, $nh ); + $hex_map['--sf-color-text--disabled'] = self::oklch_to_hex( max( 0.25, min( $nl - 0.2, 0.55 ) ), $nc, $nh ); + $hex_map['--sf-color-text--inverse'] = self::oklch_to_hex( max( 0.05, min( $nl - 0.4, 0.35 ) ), $nc, $nh ); + + $hex_map['--sf-color-border'] = self::oklch_to_hex( max( 0.25, min( $nl - 0.3, 0.55 ) ), 0.005, $nh ); + $hex_map['--sf-color-border--subtle'] = self::oklch_to_hex( max( 0.20, min( $nl - 0.38, 0.45 ) ), 0.005, $nh ); + $hex_map['--sf-color-border--strong'] = self::oklch_to_hex( max( 0.38, min( $nl - 0.1, 0.65 ) ), 0.02, $nh ); + $hex_map['--sf-color-border--muted'] = $hex_map['--sf-color-border--subtle']; $hex_map['--sf-color-border--translucent'] = self::rgb_to_hex( self::mix_rgb( $neutral_rgb, $surface_rgb, 0.15 ) ); $hex_map['--sf-color-border--disabled'] = self::rgb_to_hex( self::mix_rgb( self::hex_to_rgb( $hex_map['--sf-color-border--subtle'] ), $surface_rgb, 0.5 ) ); @@ -585,13 +585,13 @@ private static function resolve_semantic_tokens_dark( $hex_map, $d, $light_sourc // ---- Action-derived links (dark formulas: lighten toward a floor) ---- if ( isset( $d['action'] ) ) { list( $al, $ac, $ah ) = $d['action']; - $action_hex = isset( $hex_map['--sf-color-action'] ) ? $hex_map['--sf-color-action'] : self::oklch_to_hex( $al, $ac, $ah ); - $action_rgb = self::hex_to_rgb( $action_hex ); + $action_hex = isset( $hex_map['--sf-color-action'] ) ? $hex_map['--sf-color-action'] : self::oklch_to_hex( $al, $ac, $ah ); + $action_rgb = self::hex_to_rgb( $action_hex ); - $hex_map['--sf-color-link'] = self::oklch_to_hex( max( 0.68, $al ), $ac, $ah ); - $hex_map['--sf-color-link--hover'] = self::oklch_to_hex( max( $al + 0.10, 0.68 ), $ac, $ah ); - $hex_map['--sf-color-link--active'] = self::oklch_to_hex( max( $al + 0.15, 0.74 ), $ac, $ah ); - $hex_map['--sf-color-link--visited'] = self::oklch_to_hex( max( 0.68, $al ), $ac, fmod( $ah + 60.0, 360.0 ) ); + $hex_map['--sf-color-link'] = self::oklch_to_hex( max( 0.68, $al ), $ac, $ah ); + $hex_map['--sf-color-link--hover'] = self::oklch_to_hex( max( $al + 0.10, 0.68 ), $ac, $ah ); + $hex_map['--sf-color-link--active'] = self::oklch_to_hex( max( $al + 0.15, 0.74 ), $ac, $ah ); + $hex_map['--sf-color-link--visited'] = self::oklch_to_hex( max( 0.68, $al ), $ac, fmod( $ah + 60.0, 360.0 ) ); $hex_map['--sf-color-link--underline'] = self::rgb_to_hex( self::mix_rgb( $action_rgb, $surface_rgb, 0.30 ) ); $hex_map['--sf-color-bg--selected'] = self::rgb_to_hex( self::mix_rgb( $action_rgb, $surface_rgb, 0.10 ) ); @@ -620,9 +620,9 @@ private static function resolve_semantic_tokens_dark( $hex_map, $d, $light_sourc // ---- Selection + mark ---- // Dark selection-bg references action-LIGHT lightness; composite at ~0.55 over base. if ( isset( $light_sources['action'] ) ) { - list( $la, $lc, $lh ) = $light_sources['action']; - $sel_l = max( 0.62, min( 0.93 - $la * 0.4, 0.78 ) ); - $sel_rgb = self::hex_to_rgb( self::oklch_to_hex( $sel_l, $lc, $lh ) ); + list( $la, $lc, $lh ) = $light_sources['action']; + $sel_l = max( 0.62, min( 0.93 - $la * 0.4, 0.78 ) ); + $sel_rgb = self::hex_to_rgb( self::oklch_to_hex( $sel_l, $lc, $lh ) ); $hex_map['--sf-color-selection-bg'] = self::rgb_to_hex( self::mix_rgb( $sel_rgb, $surface_rgb, 0.55 ) ); } elseif ( isset( $hex_map['--sf-color-bg--selected'] ) ) { $hex_map['--sf-color-selection-bg'] = $hex_map['--sf-color-bg--selected']; @@ -646,7 +646,7 @@ private static function resolve_semantic_tokens_dark( $hex_map, $d, $light_sourc if ( ! isset( $d[ $family ] ) ) { continue; } - list( $sl, $sc, $sh ) = $d[ $family ]; + list( $sl, $sc, $sh ) = $d[ $family ]; $hex_map[ '--sf-color-' . $family . '-strong' ] = self::oklch_to_hex( max( 0.0, min( $sl + $l_offset, 1.0 ) ), $sc, $sh ); } diff --git a/SLASHED-for-WP/includes/class-css-generator.php b/SLASHED-for-WP/includes/class-css-generator.php index 8ee2eaa3..fffcbd40 100644 --- a/SLASHED-for-WP/includes/class-css-generator.php +++ b/SLASHED-for-WP/includes/class-css-generator.php @@ -428,10 +428,10 @@ private static function generate_layout_declarations( $settings, $vp_min = self: private static function generate_paired_clamp_declarations( $settings, $key_prefix, $mobile_var, $desktop_var, $computed_var, $vp_min = self::VIEWPORT_MIN, $vp_max = self::VIEWPORT_MAX ) { $declarations = array(); - $mobile_raw = $settings[ $key_prefix . '_mobile' ] ?? ''; + $mobile_raw = $settings[ $key_prefix . '_mobile' ] ?? ''; $desktop_raw = $settings[ $key_prefix . '_desktop' ] ?? ''; - $mobile_num = ( '' !== $mobile_raw && is_numeric( $mobile_raw ) ) ? (float) $mobile_raw : null; + $mobile_num = ( '' !== $mobile_raw && is_numeric( $mobile_raw ) ) ? (float) $mobile_raw : null; $desktop_num = ( '' !== $desktop_raw && is_numeric( $desktop_raw ) ) ? (float) $desktop_raw : null; if ( null === $mobile_num && null === $desktop_num ) { @@ -453,10 +453,10 @@ private static function generate_paired_clamp_declarations( $settings, $key_pref // zero (a collapsed header) and mobile may exceed desktop (a // header that shrinks as the viewport grows), so the clamp // bounds are the sorted pair, not mobile/desktop in order. - $slope = ( $desktop_num - $mobile_num ) / ( $vp_max - $vp_min ); - $lower = min( $mobile_num, $desktop_num ); - $upper = max( $mobile_num, $desktop_num ); - $middle = 'calc(' . round( $slope, 6 ) . ' * (100vw - ' . $vp_min . 'rem) + ' . round( $mobile_num, 4 ) . 'rem)'; + $slope = ( $desktop_num - $mobile_num ) / ( $vp_max - $vp_min ); + $lower = min( $mobile_num, $desktop_num ); + $upper = max( $mobile_num, $desktop_num ); + $middle = 'calc(' . round( $slope, 6 ) . ' * (100vw - ' . $vp_min . 'rem) + ' . round( $mobile_num, 4 ) . 'rem)'; $declarations[] = $computed_var . ': clamp(' . round( $lower, 4 ) . 'rem, ' . $middle . ', ' . round( $upper, 4 ) . 'rem);'; } } elseif ( null !== $mobile_num ) { @@ -506,9 +506,9 @@ private static function balanced_parens( $value ) { for ( $i = 0; $i < $len; $i++ ) { $ch = $value[ $i ]; if ( '(' === $ch ) { - $depth++; + ++$depth; } elseif ( ')' === $ch ) { - $depth--; + --$depth; if ( $depth < 0 ) { return false; } diff --git a/SLASHED-for-WP/includes/class-framework-updater.php b/SLASHED-for-WP/includes/class-framework-updater.php index 7ed90f6f..72ec2a94 100644 --- a/SLASHED-for-WP/includes/class-framework-updater.php +++ b/SLASHED-for-WP/includes/class-framework-updater.php @@ -24,14 +24,14 @@ class Slashed_Framework_Updater { const LOCAL_VER_OPTION = 'slashed_local_framework_version'; // Per-version CSS comes from the framework's GitHub Release assets (immutable // per tag). %1$s = version tag (e.g. v0.5.21), %2$s = bundle filename. - const CDN_BASE = 'https://github.com/codeslash-dev/SLASHED/releases/download/%s/%s'; + const CDN_BASE = 'https://github.com/codeslash-dev/SLASHED/releases/download/%s/%s'; // jsDelivr package metadata lists the framework repo's git tags (works // independently of where the built CSS lives). - const METADATA_URL = 'https://data.jsdelivr.com/v1/packages/gh/codeslash-dev/SLASHED'; + const METADATA_URL = 'https://data.jsdelivr.com/v1/packages/gh/codeslash-dev/SLASHED'; public function __construct() { - add_action( 'wp_ajax_slashed_check_framework_update', array( $this, 'ajax_check_update' ) ); - add_action( 'wp_ajax_slashed_do_framework_update', array( $this, 'ajax_do_update' ) ); + add_action( 'wp_ajax_slashed_check_framework_update', array( $this, 'ajax_check_update' ) ); + add_action( 'wp_ajax_slashed_do_framework_update', array( $this, 'ajax_do_update' ) ); add_action( 'wp_ajax_slashed_list_framework_versions', array( $this, 'ajax_list_versions' ) ); } @@ -126,11 +126,13 @@ public static function download_files( $version ) { ); if ( is_wp_error( $response ) ) { - foreach ( $staged as $f ) { $wp_filesystem->delete( $f ); } + foreach ( $staged as $f ) { + $wp_filesystem->delete( $f ); } return $response; } if ( 200 !== wp_remote_retrieve_response_code( $response ) ) { - foreach ( $staged as $f ) { $wp_filesystem->delete( $f ); } + foreach ( $staged as $f ) { + $wp_filesystem->delete( $f ); } return new WP_Error( 'download_failed', /* translators: %1$s: filename, %2$d: HTTP status code */ @@ -140,7 +142,8 @@ public static function download_files( $version ) { $content = wp_remote_retrieve_body( $response ); if ( ! $wp_filesystem->put_contents( $tmp_path, $content, FS_CHMOD_FILE ) ) { - foreach ( $staged as $f ) { $wp_filesystem->delete( $f ); } + foreach ( $staged as $f ) { + $wp_filesystem->delete( $f ); } return new WP_Error( 'write_failed', /* translators: %s: filename */ @@ -158,7 +161,8 @@ public static function download_files( $version ) { $final_path = $dist_dir . $filename; if ( ! $wp_filesystem->move( $tmp_path, $final_path, true ) ) { - foreach ( $staged as $f ) { $wp_filesystem->delete( $f ); } + foreach ( $staged as $f ) { + $wp_filesystem->delete( $f ); } return new WP_Error( 'rename_failed', /* translators: %s: filename */ diff --git a/SLASHED-for-WP/includes/class-hooks-page.php b/SLASHED-for-WP/includes/class-hooks-page.php index a5ab95b9..d3e229ab 100644 --- a/SLASHED-for-WP/includes/class-hooks-page.php +++ b/SLASHED-for-WP/includes/class-hooks-page.php @@ -68,31 +68,31 @@ private static function get_hooks() { array( 'name' => 'slashed_bricks/variables', 'description' => 'Filter the raw grouped variable map (category label → --sf-* names) before it is turned into Bricks entries. Lower-level than registered_variables.', - 'params' => "\$variables (array) — Map of category label to array of --sf-* names.", + 'params' => '$variables (array) — Map of category label to array of --sf-* names.', 'example' => "add_filter( 'slashed_bricks/variables', function( \$variables ) {\n // Drop a whole category from pickers + autocomplete.\n unset( \$variables['Z-Index'] );\n return \$variables;\n} );", ), array( 'name' => 'slashed_bricks/inventory', - 'description' => "Replace the resolved inventory wholesale. Useful for tests, custom forks of the framework, or sites that want to ship their own token list.", + 'description' => 'Replace the resolved inventory wholesale. Useful for tests, custom forks of the framework, or sites that want to ship their own token list.', 'params' => "\$inventory (array) — Array with keys 'variables', 'sf_classes', 'is_classes'.", 'example' => "add_filter( 'slashed_bricks/inventory', function( \$inventory ) {\n \$inventory['variables'][] = '--sf-color-my-custom';\n return \$inventory;\n} );", ), array( 'name' => 'slashed_bricks/inventory_local_path', 'description' => 'Override the local CSS path the inventory parses. Return a string for a specific path, false to skip local resolution, or null (default) to use bundled candidates.', - 'params' => "\$path (string|false|null) — Override path or control flag.", + 'params' => '$path (string|false|null) — Override path or control flag.', 'example' => "// Use a child-theme copy of the bundle.\nadd_filter( 'slashed_bricks/inventory_local_path', function() {\n return get_stylesheet_directory() . '/assets/slashed.optimal.css';\n} );", ), array( 'name' => 'slashed_bricks/show_color_swatches', 'description' => 'Toggle the colour squares painted next to --sf-color-* entries in the Bricks variable-picker dropdown (builder-side only). Defaults to true.', - 'params' => "\$show (bool) — Whether to paint the picker swatches.", + 'params' => '$show (bool) — Whether to paint the picker swatches.', 'example' => "// Hide the variable-picker swatches.\nadd_filter( 'slashed_bricks/show_color_swatches', '__return_false' );", ), array( 'name' => 'slashed_bricks/show_color_panel', 'description' => 'Toggle the in-builder Color System Panel and its launcher pill. Defaults to true.', - 'params' => "\$show (bool) — Whether to load the Color System Panel.", + 'params' => '$show (bool) — Whether to load the Color System Panel.', 'example' => "// Hide the Color System panel.\nadd_filter( 'slashed_bricks/show_color_panel', '__return_false' );", ), ), diff --git a/SLASHED-for-WP/includes/class-inventory.php b/SLASHED-for-WP/includes/class-inventory.php index cd69cc91..571057a9 100644 --- a/SLASHED-for-WP/includes/class-inventory.php +++ b/SLASHED-for-WP/includes/class-inventory.php @@ -47,8 +47,8 @@ class Slashed_Inventory { * * @var array */ - private static $cache = array(); - private static $hex_map_cache = array(); + private static $cache = array(); + private static $hex_map_cache = array(); private static $hex_map_dark_cache = array(); // --------------------------------------------------------------- @@ -192,7 +192,7 @@ private static function sanitize_inventory( $inventory ) { foreach ( array_keys( $base ) as $key ) { // color_values is an associative map, not a sortable list. if ( 'color_values' === $key ) { - $raw_map = isset( $inventory[ $key ] ) && is_array( $inventory[ $key ] ) + $raw_map = isset( $inventory[ $key ] ) && is_array( $inventory[ $key ] ) ? $inventory[ $key ] : array(); $normalized = array(); @@ -308,7 +308,7 @@ private static function get_admin_color_overrides() { return array(); } - $settings = $tokens['colors']; + $settings = $tokens['colors']; $overrides = array(); // Brand families: 'base' is the source token (--sf-color-base-light); diff --git a/SLASHED-for-WP/includes/class-rest-controller.php b/SLASHED-for-WP/includes/class-rest-controller.php index 75833b76..591891d3 100644 --- a/SLASHED-for-WP/includes/class-rest-controller.php +++ b/SLASHED-for-WP/includes/class-rest-controller.php @@ -131,7 +131,7 @@ public function register_routes() { 'type' => 'string', 'required' => false, 'sanitize_callback' => 'sanitize_text_field', - 'validate_callback' => function( $value ) { + 'validate_callback' => function ( $value ) { return in_array( (string) $value, array( '', '100', '62.5' ), true ); }, ), @@ -139,7 +139,7 @@ public function register_routes() { 'type' => 'string', 'required' => false, 'sanitize_callback' => 'sanitize_key', - 'validate_callback' => function( $value ) { + 'validate_callback' => function ( $value ) { return in_array( (string) $value, Slashed_Token_Store::ALLOWED_CSS_BUNDLES, true ); }, ), @@ -177,10 +177,12 @@ public function save_section( WP_REST_Request $request ) { $sanitized = Slashed_Token_Sanitizer::sanitize_section( $section, $values ); Slashed_Token_Store::update_section( $section, $sanitized ); - return rest_ensure_response( array( - 'section' => $section, - 'values' => $sanitized, - ) ); + return rest_ensure_response( + array( + 'section' => $section, + 'values' => $sanitized, + ) + ); } public function validate_section( WP_REST_Request $request ) { @@ -206,20 +208,28 @@ public function validate_section( WP_REST_Request $request ) { foreach ( $values as $key => $raw_value ) { $raw = (string) $raw_value; if ( ! array_key_exists( $key, $sanitized ) ) { - $changed[ $key ] = array( 'original' => $raw, 'sanitized' => '' ); + $changed[ $key ] = array( + 'original' => $raw, + 'sanitized' => '', + ); continue; } $clean = (string) $sanitized[ $key ]; if ( $raw !== $clean ) { - $changed[ $key ] = array( 'original' => $raw, 'sanitized' => $clean ); + $changed[ $key ] = array( + 'original' => $raw, + 'sanitized' => $clean, + ); } } - return rest_ensure_response( array( - 'section' => $section, - 'sanitized' => $sanitized, - 'changed' => $changed, - ) ); + return rest_ensure_response( + array( + 'section' => $section, + 'sanitized' => $sanitized, + 'changed' => $changed, + ) + ); } public function reset_section( WP_REST_Request $request ) { @@ -227,7 +237,12 @@ public function reset_section( WP_REST_Request $request ) { if ( '' === $section ) { Slashed_Token_Store::delete_settings(); - return rest_ensure_response( array( 'section' => '', 'settings' => array() ) ); + return rest_ensure_response( + array( + 'section' => '', + 'settings' => array(), + ) + ); } if ( ! $this->is_known_section( $section ) ) { @@ -240,7 +255,12 @@ public function reset_section( WP_REST_Request $request ) { } $all = Slashed_Token_Store::delete_section( $section ); - return rest_ensure_response( array( 'section' => $section, 'settings' => $all ) ); + return rest_ensure_response( + array( + 'section' => $section, + 'settings' => $all, + ) + ); } public function get_settings( WP_REST_Request $request ) { @@ -273,13 +293,15 @@ public function save_settings( WP_REST_Request $request ) { } public function export_tokens() { - return rest_ensure_response( array( - 'schema_version' => '1', - 'plugin_version' => SLASHED_VERSION, - 'exported_at' => gmdate( 'c' ), - 'tokens' => Slashed_Token_Store::get_settings(), - 'plugin_settings' => Slashed_Token_Store::get_plugin_settings(), - ) ); + return rest_ensure_response( + array( + 'schema_version' => '1', + 'plugin_version' => SLASHED_VERSION, + 'exported_at' => gmdate( 'c' ), + 'tokens' => Slashed_Token_Store::get_settings(), + 'plugin_settings' => Slashed_Token_Store::get_plugin_settings(), + ) + ); } public function import_tokens( WP_REST_Request $request ) { @@ -319,8 +341,8 @@ public function import_tokens( WP_REST_Request $request ) { if ( isset( $raw['css_bundle'] ) && in_array( (string) $raw['css_bundle'], Slashed_Token_Store::ALLOWED_CSS_BUNDLES, true ) ) { - $existing['css_bundle'] = (string) $raw['css_bundle']; - $settings_imported = true; + $existing['css_bundle'] = (string) $raw['css_bundle']; + $settings_imported = true; } if ( isset( $raw['html_font_size'] ) && in_array( (string) $raw['html_font_size'], array( '', '100', '62.5' ), true ) @@ -338,12 +360,14 @@ public function import_tokens( WP_REST_Request $request ) { } } - return rest_ensure_response( array( - 'imported' => $imported, - 'settings_imported' => $settings_imported, - 'tokens' => Slashed_Token_Store::get_settings(), - 'plugin_settings' => Slashed_Token_Store::get_plugin_settings(), - ) ); + return rest_ensure_response( + array( + 'imported' => $imported, + 'settings_imported' => $settings_imported, + 'tokens' => Slashed_Token_Store::get_settings(), + 'plugin_settings' => Slashed_Token_Store::get_plugin_settings(), + ) + ); } private function is_known_section( $section ) { diff --git a/SLASHED-for-WP/includes/class-settings.php b/SLASHED-for-WP/includes/class-settings.php index ed89cc90..5de29502 100644 --- a/SLASHED-for-WP/includes/class-settings.php +++ b/SLASHED-for-WP/includes/class-settings.php @@ -30,9 +30,9 @@ class Slashed_Settings { */ const KNOWN_INTEGRATIONS = array( 'bricks', 'gutenberg' ); - const ALLOWED_BUNDLES = array( 'essential', 'optimal', 'full' ); + const ALLOWED_BUNDLES = array( 'essential', 'optimal', 'full' ); - const ALLOWED_SOURCES = array( 'local', 'cdn' ); + const ALLOWED_SOURCES = array( 'local', 'cdn' ); /** * Read settings from the database, applying defaults. @@ -170,12 +170,15 @@ public static function save( array $data ) { $cdn_version = ''; } - return update_option( self::OPTION_KEY, array( - 'integrations' => $integrations, - 'css_bundle' => $bundle, - 'css_source' => $source, - 'cdn_version' => $cdn_version, - ) ); + return update_option( + self::OPTION_KEY, + array( + 'integrations' => $integrations, + 'css_bundle' => $bundle, + 'css_source' => $source, + 'cdn_version' => $cdn_version, + ) + ); } /** diff --git a/SLASHED-for-WP/includes/class-token-defaults.php b/SLASHED-for-WP/includes/class-token-defaults.php index 655cc217..4497e9c2 100644 --- a/SLASHED-for-WP/includes/class-token-defaults.php +++ b/SLASHED-for-WP/includes/class-token-defaults.php @@ -63,7 +63,7 @@ public static function get_section( $section ) { */ public static function get_colors() { return array( - 'brand' => array( + 'brand' => array( 'primary' => 'oklch(0.47 0.27 264)', 'secondary' => 'oklch(0.22 0.04 264)', 'tertiary' => 'oklch(0.42 0.22 295)', @@ -71,7 +71,7 @@ public static function get_colors() { 'neutral' => 'oklch(0.52 0.025 260)', 'base' => 'oklch(0.96 0.006 250)', ), - 'brand_dark' => array( + 'brand_dark' => array( 'primary' => '', 'secondary' => '', 'tertiary' => '', @@ -79,21 +79,21 @@ public static function get_colors() { 'neutral' => '', 'base' => '', ), - 'status' => array( + 'status' => array( 'success' => 'oklch(0.50 0.16 145)', 'warning' => 'oklch(0.75 0.17 80)', 'error' => 'oklch(0.50 0.20 25)', 'info' => 'oklch(0.48 0.18 235)', 'danger' => 'oklch(0.48 0.22 12)', ), - 'status_dark' => array( + 'status_dark' => array( 'success' => '', 'warning' => '', 'error' => '', 'info' => '', 'danger' => '', ), - 'brand_hex_hints' => array( + 'brand_hex_hints' => array( 'primary' => '#4a30d0', 'secondary' => '#141530', 'tertiary' => '#5c20cc', @@ -101,7 +101,7 @@ public static function get_colors() { 'neutral' => '#5e6880', 'base' => '#f8f9ff', ), - 'brand_dark_hex_hints' => array( + 'brand_dark_hex_hints' => array( 'primary' => '#9080f0', 'secondary' => '#c0c8e0', 'tertiary' => '#b07ae8', @@ -109,7 +109,7 @@ public static function get_colors() { 'neutral' => '#8a96b4', 'base' => '#141924', ), - 'status_hex_hints' => array( + 'status_hex_hints' => array( 'success' => '#1a8040', 'warning' => '#ca8a04', 'error' => '#c42010', @@ -133,7 +133,7 @@ public static function get_colors() { */ public static function get_typography() { return array( - 'font_families' => array( + 'font_families' => array( 'body' => 'system-ui, -apple-system, sans-serif', 'heading' => 'var(--sf-font-body)', 'mono' => 'ui-monospace, monospace', @@ -142,19 +142,55 @@ public static function get_typography() { 'geometric' => '"Avenir", "Montserrat", "Corbel", "URW Gothic", source-sans-pro, sans-serif', 'slab' => '"Rockwell", "Rockwell Nova", "Roboto Slab", "DejaVu Serif", "Sitka Small", serif', ), - 'font_sizes' => array( - '2xs' => array( 'min' => 0.51, 'max' => 0.53 ), - 'xs' => array( 'min' => 0.64, 'max' => 0.70 ), - 's' => array( 'min' => 0.80, 'max' => 0.94 ), - 'm' => array( 'min' => 1.00, 'max' => 1.25 ), - 'l' => array( 'min' => 1.25, 'max' => 1.67 ), - 'xl' => array( 'min' => 1.56, 'max' => 2.22 ), - '2xl' => array( 'min' => 1.95, 'max' => 2.96 ), - '3xl' => array( 'min' => 2.44, 'max' => 3.95 ), - '4xl' => array( 'min' => 3.05, 'max' => 5.26 ), - 'display-s' => array( 'min' => 2.40, 'max' => 3.00 ), - 'display-m' => array( 'min' => 3.00, 'max' => 4.00 ), - 'display-l' => array( 'min' => 3.75, 'max' => 5.33 ), + 'font_sizes' => array( + '2xs' => array( + 'min' => 0.51, + 'max' => 0.53, + ), + 'xs' => array( + 'min' => 0.64, + 'max' => 0.70, + ), + 's' => array( + 'min' => 0.80, + 'max' => 0.94, + ), + 'm' => array( + 'min' => 1.00, + 'max' => 1.25, + ), + 'l' => array( + 'min' => 1.25, + 'max' => 1.67, + ), + 'xl' => array( + 'min' => 1.56, + 'max' => 2.22, + ), + '2xl' => array( + 'min' => 1.95, + 'max' => 2.96, + ), + '3xl' => array( + 'min' => 2.44, + 'max' => 3.95, + ), + '4xl' => array( + 'min' => 3.05, + 'max' => 5.26, + ), + 'display-s' => array( + 'min' => 2.40, + 'max' => 3.00, + ), + 'display-m' => array( + 'min' => 3.00, + 'max' => 4.00, + ), + 'display-l' => array( + 'min' => 3.75, + 'max' => 5.33, + ), ), 'scale_multipliers' => array( 'text_scale' => 1, @@ -170,25 +206,52 @@ public static function get_typography() { */ public static function get_spacing() { return array( - 'space_scale' => 1, - 'viewport_min' => 22.5, - 'viewport_max' => 90, - 'space_sizes' => array( - '2xs' => array( 'min' => 0.51, 'max' => 0.84 ), - 'xs' => array( 'min' => 0.64, 'max' => 1.13 ), - 's' => array( 'min' => 0.80, 'max' => 1.50 ), - 'm' => array( 'min' => 1.00, 'max' => 2.00 ), - 'l' => array( 'min' => 1.25, 'max' => 2.67 ), - 'xl' => array( 'min' => 1.56, 'max' => 3.55 ), - '2xl' => array( 'min' => 1.95, 'max' => 4.74 ), - '3xl' => array( 'min' => 2.44, 'max' => 6.31 ), - '4xl' => array( 'min' => 3.05, 'max' => 8.42 ), + 'space_scale' => 1, + 'viewport_min' => 22.5, + 'viewport_max' => 90, + 'space_sizes' => array( + '2xs' => array( + 'min' => 0.51, + 'max' => 0.84, + ), + 'xs' => array( + 'min' => 0.64, + 'max' => 1.13, + ), + 's' => array( + 'min' => 0.80, + 'max' => 1.50, + ), + 'm' => array( + 'min' => 1.00, + 'max' => 2.00, + ), + 'l' => array( + 'min' => 1.25, + 'max' => 2.67, + ), + 'xl' => array( + 'min' => 1.56, + 'max' => 3.55, + ), + '2xl' => array( + 'min' => 1.95, + 'max' => 4.74, + ), + '3xl' => array( + 'min' => 2.44, + 'max' => 6.31, + ), + '4xl' => array( + 'min' => 3.05, + 'max' => 8.42, + ), ), - 'gutter' => 'var(--sf-space-l)', - 'gap' => 'var(--sf-space-m)', - 'content_gap' => 'var(--sf-space-s)', - 'component_pad' => 'var(--sf-space-m)', - 'section_pad' => 'var(--sf-section-pad--m)', + 'gutter' => 'var(--sf-space-l)', + 'gap' => 'var(--sf-space-m)', + 'content_gap' => 'var(--sf-space-s)', + 'component_pad' => 'var(--sf-space-m)', + 'section_pad' => 'var(--sf-section-pad--m)', ); } @@ -203,42 +266,42 @@ public static function get_spacing() { public static function get_layouts() { return array( // Containers - 'container_narrow' => '38rem', - 'container_prose' => '65ch', - 'container_default' => '75rem', - 'container_wide' => '90rem', + 'container_narrow' => '38rem', + 'container_prose' => '65ch', + 'container_default' => '75rem', + 'container_wide' => '90rem', // Grid - 'grid_min' => '16rem', - 'grid_min_xs' => '10rem', - 'grid_min_s' => '13rem', - 'grid_min_l' => '20rem', - 'grid_min_xl' => '24rem', - 'grid_min_2xl' => '28rem', + 'grid_min' => '16rem', + 'grid_min_xs' => '10rem', + 'grid_min_s' => '13rem', + 'grid_min_l' => '20rem', + 'grid_min_xl' => '24rem', + 'grid_min_2xl' => '28rem', // Switcher - 'switcher_threshold' => '30rem', + 'switcher_threshold' => '30rem', // Bento - 'bento_cols' => '3', - 'bento_row_default' => '10rem', - 'bento_row_compact' => '6rem', - 'bento_row_tall' => '16rem', + 'bento_cols' => '3', + 'bento_row_default' => '10rem', + 'bento_row_compact' => '6rem', + 'bento_row_tall' => '16rem', // Content grid - 'content_width' => 'var(--sf-container-default)', - 'breakout_width' => 'var(--sf-container-wide)', + 'content_width' => 'var(--sf-container-default)', + 'breakout_width' => 'var(--sf-container-wide)', // Sidebar - 'sidebar_width' => '18rem', - 'sidebar_min_width' => '50%', + 'sidebar_width' => '18rem', + 'sidebar_min_width' => '50%', // Cover - 'cover_min_height' => '100dvh', - 'cover_padding' => 'var(--sf-section-pad)', + 'cover_min_height' => '100dvh', + 'cover_padding' => 'var(--sf-section-pad)', // Frame - 'frame_ratio' => '16 / 9', + 'frame_ratio' => '16 / 9', // Reel - 'reel_item_width' => 'max-content', - 'reel_height' => 'auto', + 'reel_item_width' => 'max-content', + 'reel_height' => 'auto', // Imposter - 'imposter_margin' => 'var(--sf-space-m)', + 'imposter_margin' => 'var(--sf-space-m)', // Equal columns - 'equal_cols' => '2', + 'equal_cols' => '2', // Header & sticky (numeric rem values, empty = use framework default) 'header_height_mobile' => '', 'header_height_desktop' => '', diff --git a/SLASHED-for-WP/includes/class-token-page.php b/SLASHED-for-WP/includes/class-token-page.php index e5ceded9..42038f83 100644 --- a/SLASHED-for-WP/includes/class-token-page.php +++ b/SLASHED-for-WP/includes/class-token-page.php @@ -77,7 +77,7 @@ public function enqueue_assets( $hook_suffix ) { } if ( defined( 'SLASHED_URL' ) ) { - $plugin_url = SLASHED_URL . 'integrations/bricks/'; + $plugin_url = SLASHED_URL . 'integrations/bricks/'; $plugin_path = SLASHED_PATH . 'integrations/bricks/'; } else { // Standalone Bricks plugin: SLASHED_BRICKS_* already point to integrations/bricks/. diff --git a/SLASHED-for-WP/integrations/bricks/includes/class-classes.php b/SLASHED-for-WP/integrations/bricks/includes/class-classes.php index 5d76912d..81528980 100644 --- a/SLASHED-for-WP/integrations/bricks/includes/class-classes.php +++ b/SLASHED-for-WP/integrations/bricks/includes/class-classes.php @@ -6,7 +6,7 @@ */ if ( ! defined( 'ABSPATH' ) ) { - exit; + exit; } /** @@ -47,184 +47,190 @@ */ class Slashed_Bricks_Classes { - /** - * Prefix used on every class id and category id this integration injects. - * Used to identify our entries when stripping on save. - */ - const ID_PREFIX = 'slashed-'; - - /** - * Stable category ids. These live in `bricks_global_classes_categories` - * and each class entry references one via its `category` field. - */ - const CATEGORY_LAYOUT = 'slashed-cat-layout'; - const CATEGORY_STATE = 'slashed-cat-state'; - - /** - * Constructor. Register option filters. - */ - public function __construct() { - // Inject SLASHED classes when Bricks reads the option. Run late - // so any other plugin's additions are preserved. - add_filter( 'option_bricks_global_classes', array( $this, 'inject_classes' ), 20 ); - add_filter( 'default_option_bricks_global_classes', array( $this, 'inject_classes' ), 20 ); - - // Strip SLASHED classes before they are persisted back to the DB. - // pre_update_option_* hooks must never widen the stored type from - // array to scalar - that would break Bricks' own loader, which - // iterates over the option. - add_filter( 'pre_update_option_bricks_global_classes', array( $this, 'strip_classes' ), 10, 1 ); - - // Same managed/virtual pattern for class categories (Bricks 1.9.5+). - add_filter( 'option_bricks_global_classes_categories', array( $this, 'inject_categories' ), 20 ); - add_filter( 'default_option_bricks_global_classes_categories', array( $this, 'inject_categories' ), 20 ); - add_filter( 'pre_update_option_bricks_global_classes_categories', array( $this, 'strip_categories' ), 10, 1 ); - } - - /** - * Inject SLASHED classes into the Bricks global classes list. - * - * Idempotent: any existing SLASHED-prefixed entries are removed first - * so multiple read passes don't create duplicates. - * - * @param mixed $classes Existing value of bricks_global_classes option. - * @return array - */ - public function inject_classes( $classes ) { - if ( ! is_array( $classes ) ) { - $classes = array(); - } - - $classes = $this->strip_classes( $classes ); - - foreach ( $this->build_classes() as $entry ) { - $classes[] = $entry; - } - - return $classes; - } - - /** - * Remove SLASHED-prefixed entries from a global classes array. - * - * Always returns a clean array, even when the option is malformed. - * - * @param mixed $classes Value of bricks_global_classes option. - * @return array - */ - public function strip_classes( $classes ) { - return $this->strip_prefixed( $classes ); - } - - /** - * Inject SLASHED class category entries. - * - * @param mixed $categories Existing value of bricks_global_classes_categories option. - * @return array - */ - public function inject_categories( $categories ) { - if ( ! is_array( $categories ) ) { - $categories = array(); - } - - $categories = $this->strip_categories( $categories ); - - foreach ( $this->build_categories() as $cat ) { - $categories[] = $cat; - } - - return $categories; - } - - /** - * Remove SLASHED-prefixed categories from a categories array. - * - * @param mixed $categories Value of bricks_global_classes_categories option. - * @return array - */ - public function strip_categories( $categories ) { - return $this->strip_prefixed( $categories ); - } - - /** - * Shared "strip every entry whose id starts with our prefix" helper. - * - * @param mixed $entries Possibly malformed list of `{id,...}` entries. - * @return array - */ - private function strip_prefixed( $entries ) { - if ( ! is_array( $entries ) ) { - return array(); - } - - $kept = array(); - foreach ( $entries as $entry ) { - if ( is_array( $entry ) - && isset( $entry['id'] ) - && is_string( $entry['id'] ) - && 0 === strpos( $entry['id'], self::ID_PREFIX ) - ) { - continue; - } - $kept[] = $entry; - } - - return array_values( $kept ); - } - - /** - * Build SLASHED class entries from the inventory. - * - * Each entry follows the Bricks-native shape: - * { id, name, settings: { locked: true }, category } - * - * - `id` is a stable slashed-* slug so strip_classes() can find it - * reliably across read passes; Bricks accepts any unique string. - * - `name` is the actual class name users will apply (e.g. "sf-stack"). - * - `settings.locked` puts the entry in the Class Manager's Locked - * filter so framework classes aren't accidentally edited. - * - `category` references one of our stable category ids. - * - * @return array> - */ - public function build_classes() { - $entries = array(); - - foreach ( Slashed_Bricks_Inventory::get_sf_classes() as $name ) { - $entries[] = array( - 'id' => self::ID_PREFIX . sanitize_key( $name ), - 'name' => $name, - 'settings' => array( 'locked' => true ), - 'category' => self::CATEGORY_LAYOUT, - ); - } - - foreach ( Slashed_Bricks_Inventory::get_is_classes() as $name ) { - $entries[] = array( - 'id' => self::ID_PREFIX . sanitize_key( $name ), - 'name' => $name, - 'settings' => array( 'locked' => true ), - 'category' => self::CATEGORY_STATE, - ); - } - - /** - * Filter the SLASHED class entries before injection. - * - * @param array $entries Class entries. - */ - return apply_filters( 'slashed_bricks/registered_classes', $entries ); - } - - /** - * Build SLASHED class category entries. - * - * @return array> - */ - public function build_categories() { - return array( - array( 'id' => self::CATEGORY_LAYOUT, 'name' => __( 'SLASHED Layout', 'slashed-bricks' ) ), - array( 'id' => self::CATEGORY_STATE, 'name' => __( 'SLASHED State', 'slashed-bricks' ) ), - ); - } + /** + * Prefix used on every class id and category id this integration injects. + * Used to identify our entries when stripping on save. + */ + const ID_PREFIX = 'slashed-'; + + /** + * Stable category ids. These live in `bricks_global_classes_categories` + * and each class entry references one via its `category` field. + */ + const CATEGORY_LAYOUT = 'slashed-cat-layout'; + const CATEGORY_STATE = 'slashed-cat-state'; + + /** + * Constructor. Register option filters. + */ + public function __construct() { + // Inject SLASHED classes when Bricks reads the option. Run late + // so any other plugin's additions are preserved. + add_filter( 'option_bricks_global_classes', array( $this, 'inject_classes' ), 20 ); + add_filter( 'default_option_bricks_global_classes', array( $this, 'inject_classes' ), 20 ); + + // Strip SLASHED classes before they are persisted back to the DB. + // pre_update_option_* hooks must never widen the stored type from + // array to scalar - that would break Bricks' own loader, which + // iterates over the option. + add_filter( 'pre_update_option_bricks_global_classes', array( $this, 'strip_classes' ), 10, 1 ); + + // Same managed/virtual pattern for class categories (Bricks 1.9.5+). + add_filter( 'option_bricks_global_classes_categories', array( $this, 'inject_categories' ), 20 ); + add_filter( 'default_option_bricks_global_classes_categories', array( $this, 'inject_categories' ), 20 ); + add_filter( 'pre_update_option_bricks_global_classes_categories', array( $this, 'strip_categories' ), 10, 1 ); + } + + /** + * Inject SLASHED classes into the Bricks global classes list. + * + * Idempotent: any existing SLASHED-prefixed entries are removed first + * so multiple read passes don't create duplicates. + * + * @param mixed $classes Existing value of bricks_global_classes option. + * @return array + */ + public function inject_classes( $classes ) { + if ( ! is_array( $classes ) ) { + $classes = array(); + } + + $classes = $this->strip_classes( $classes ); + + foreach ( $this->build_classes() as $entry ) { + $classes[] = $entry; + } + + return $classes; + } + + /** + * Remove SLASHED-prefixed entries from a global classes array. + * + * Always returns a clean array, even when the option is malformed. + * + * @param mixed $classes Value of bricks_global_classes option. + * @return array + */ + public function strip_classes( $classes ) { + return $this->strip_prefixed( $classes ); + } + + /** + * Inject SLASHED class category entries. + * + * @param mixed $categories Existing value of bricks_global_classes_categories option. + * @return array + */ + public function inject_categories( $categories ) { + if ( ! is_array( $categories ) ) { + $categories = array(); + } + + $categories = $this->strip_categories( $categories ); + + foreach ( $this->build_categories() as $cat ) { + $categories[] = $cat; + } + + return $categories; + } + + /** + * Remove SLASHED-prefixed categories from a categories array. + * + * @param mixed $categories Value of bricks_global_classes_categories option. + * @return array + */ + public function strip_categories( $categories ) { + return $this->strip_prefixed( $categories ); + } + + /** + * Shared "strip every entry whose id starts with our prefix" helper. + * + * @param mixed $entries Possibly malformed list of `{id,...}` entries. + * @return array + */ + private function strip_prefixed( $entries ) { + if ( ! is_array( $entries ) ) { + return array(); + } + + $kept = array(); + foreach ( $entries as $entry ) { + if ( is_array( $entry ) + && isset( $entry['id'] ) + && is_string( $entry['id'] ) + && 0 === strpos( $entry['id'], self::ID_PREFIX ) + ) { + continue; + } + $kept[] = $entry; + } + + return array_values( $kept ); + } + + /** + * Build SLASHED class entries from the inventory. + * + * Each entry follows the Bricks-native shape: + * { id, name, settings: { locked: true }, category } + * + * - `id` is a stable slashed-* slug so strip_classes() can find it + * reliably across read passes; Bricks accepts any unique string. + * - `name` is the actual class name users will apply (e.g. "sf-stack"). + * - `settings.locked` puts the entry in the Class Manager's Locked + * filter so framework classes aren't accidentally edited. + * - `category` references one of our stable category ids. + * + * @return array> + */ + public function build_classes() { + $entries = array(); + + foreach ( Slashed_Bricks_Inventory::get_sf_classes() as $name ) { + $entries[] = array( + 'id' => self::ID_PREFIX . sanitize_key( $name ), + 'name' => $name, + 'settings' => array( 'locked' => true ), + 'category' => self::CATEGORY_LAYOUT, + ); + } + + foreach ( Slashed_Bricks_Inventory::get_is_classes() as $name ) { + $entries[] = array( + 'id' => self::ID_PREFIX . sanitize_key( $name ), + 'name' => $name, + 'settings' => array( 'locked' => true ), + 'category' => self::CATEGORY_STATE, + ); + } + + /** + * Filter the SLASHED class entries before injection. + * + * @param array $entries Class entries. + */ + return apply_filters( 'slashed_bricks/registered_classes', $entries ); + } + + /** + * Build SLASHED class category entries. + * + * @return array> + */ + public function build_categories() { + return array( + array( + 'id' => self::CATEGORY_LAYOUT, + 'name' => __( 'SLASHED Layout', 'slashed-bricks' ), + ), + array( + 'id' => self::CATEGORY_STATE, + 'name' => __( 'SLASHED State', 'slashed-bricks' ), + ), + ); + } } diff --git a/SLASHED-for-WP/integrations/bricks/includes/class-enqueue.php b/SLASHED-for-WP/integrations/bricks/includes/class-enqueue.php index 4156fc75..23782b2f 100644 --- a/SLASHED-for-WP/integrations/bricks/includes/class-enqueue.php +++ b/SLASHED-for-WP/integrations/bricks/includes/class-enqueue.php @@ -6,7 +6,7 @@ */ if ( ! defined( 'ABSPATH' ) ) { - exit; + exit; } /** @@ -17,53 +17,53 @@ */ class Slashed_Bricks_Enqueue { - public function __construct() { - add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_styles' ) ); - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_editor_styles' ) ); - } + public function __construct() { + add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_styles' ) ); + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_editor_styles' ) ); + } - /** - * Add Bricks-specific inline CSS on the frontend and canvas iframe. - * - * `slashed-framework` is registered globally by Slashed_Core_Enqueue. - * This method only adds the dark-mode bridge that maps Bricks' - * data-brx-theme attribute to SLASHED's colour-scheme / --sf-is-dark - * system. The builder-panel context is skipped for the same reason the - * global enqueue skips it (see Slashed_Core_Enqueue::enqueue_frontend). - */ - public function enqueue_frontend_styles() { - if ( function_exists( 'bricks_is_builder_main' ) && bricks_is_builder_main() ) { - return; - } + /** + * Add Bricks-specific inline CSS on the frontend and canvas iframe. + * + * `slashed-framework` is registered globally by Slashed_Core_Enqueue. + * This method only adds the dark-mode bridge that maps Bricks' + * data-brx-theme attribute to SLASHED's colour-scheme / --sf-is-dark + * system. The builder-panel context is skipped for the same reason the + * global enqueue skips it (see Slashed_Core_Enqueue::enqueue_frontend). + */ + public function enqueue_frontend_styles() { + if ( function_exists( 'bricks_is_builder_main' ) && bricks_is_builder_main() ) { + return; + } - // Bridge Bricks' dark mode toggle (data-brx-theme attribute) to SLASHED's theme system. - wp_add_inline_style( - 'slashed-framework', - '@layer slashed.themes{[data-brx-theme="light"]{color-scheme:light;--sf-is-dark:0}[data-brx-theme="dark"]{color-scheme:dark;--sf-is-dark:1}}' - ); - } + // Bridge Bricks' dark mode toggle (data-brx-theme attribute) to SLASHED's theme system. + wp_add_inline_style( + 'slashed-framework', + '@layer slashed.themes{[data-brx-theme="light"]{color-scheme:light;--sf-is-dark:0}[data-brx-theme="dark"]{color-scheme:dark;--sf-is-dark:1}}' + ); + } - /** - * Enqueue editor-specific styles in the Bricks admin panel. - * - * Only loads on Bricks builder pages for panel styling. - */ - public function enqueue_editor_styles() { - if ( ! function_exists( 'bricks_is_builder_main' ) ) { - return; - } + /** + * Enqueue editor-specific styles in the Bricks admin panel. + * + * Only loads on Bricks builder pages for panel styling. + */ + public function enqueue_editor_styles() { + if ( ! function_exists( 'bricks_is_builder_main' ) ) { + return; + } - if ( ! bricks_is_builder_main() ) { - return; - } + if ( ! bricks_is_builder_main() ) { + return; + } - $editor_css = SLASHED_BRICKS_URL . 'assets/editor.css'; + $editor_css = SLASHED_BRICKS_URL . 'assets/editor.css'; - wp_enqueue_style( - 'slashed-bricks-editor', - $editor_css, - array(), - SLASHED_BRICKS_VERSION - ); - } + wp_enqueue_style( + 'slashed-bricks-editor', + $editor_css, + array(), + SLASHED_BRICKS_VERSION + ); + } } diff --git a/SLASHED-for-WP/integrations/bricks/includes/class-rebemer-rest.php b/SLASHED-for-WP/integrations/bricks/includes/class-rebemer-rest.php index faf303bd..22957fb7 100644 --- a/SLASHED-for-WP/integrations/bricks/includes/class-rebemer-rest.php +++ b/SLASHED-for-WP/integrations/bricks/includes/class-rebemer-rest.php @@ -125,7 +125,7 @@ public function get_unused_classes() { if ( ! is_array( $cls ) || empty( $cls['id'] ) ) { continue; } - $processed_classes++; + ++$processed_classes; $id = (string) $cls['id']; $count = isset( $reference_counts['counts'][ $id ] ) ? (int) $reference_counts['counts'][ $id ] @@ -280,7 +280,7 @@ private function collect_class_ids_recursive( $tree, &$counts ) { if ( is_array( $ids ) ) { foreach ( $ids as $cid ) { if ( is_string( $cid ) && isset( $counts[ $cid ] ) ) { - $counts[ $cid ]++; + ++$counts[ $cid ]; } } } diff --git a/SLASHED-for-WP/integrations/bricks/includes/class-variables.php b/SLASHED-for-WP/integrations/bricks/includes/class-variables.php index d32854ea..b6a0e8d7 100644 --- a/SLASHED-for-WP/integrations/bricks/includes/class-variables.php +++ b/SLASHED-for-WP/integrations/bricks/includes/class-variables.php @@ -7,7 +7,7 @@ */ if ( ! defined( 'ABSPATH' ) ) { - exit; + exit; } /** @@ -49,258 +49,258 @@ */ class Slashed_Bricks_Variables { - /** - * Prefix used on every variable id this integration injects. - * Used to identify our entries when stripping on save. - */ - const ID_PREFIX = 'slashed-'; - - /** - * Prefix used on every category id this integration injects. - */ - const CAT_PREFIX = 'slashed-cat-'; - - /** - * Constructor. Register option filters and code editor hook. - */ - public function __construct() { - // Inject SLASHED variables when Bricks reads the option. - add_filter( 'option_bricks_global_variables', array( $this, 'inject_variables' ), 20 ); - add_filter( 'default_option_bricks_global_variables', array( $this, 'inject_variables' ), 20 ); - - // Strip SLASHED variables before they are persisted back to the DB. - add_filter( 'pre_update_option_bricks_global_variables', array( $this, 'strip_variables' ), 10, 1 ); - - // Same managed/virtual pattern for variable categories. - add_filter( 'option_bricks_global_variables_categories', array( $this, 'inject_categories' ), 20 ); - add_filter( 'default_option_bricks_global_variables_categories', array( $this, 'inject_categories' ), 20 ); - add_filter( 'pre_update_option_bricks_global_variables_categories', array( $this, 'strip_categories' ), 10, 1 ); - - // Code editor autocomplete (Bricks 1.9.2+): lists the original - // --sf-* names so authors typing custom CSS see the framework's - // native namespace directly. - add_filter( 'bricks/code/get_code_signatures', array( $this, 'register_code_signatures' ) ); - } - - // --------------------------------------------------------------- - // Variable Manager: inject / strip. - // --------------------------------------------------------------- - - /** - * Inject SLASHED variables into the Bricks global variables list. - * - * Idempotent: any existing SLASHED-prefixed entries are removed first - * so multiple read passes don't create duplicates. - * - * @param mixed $variables Existing value of bricks_global_variables option. - * @return array - */ - public function inject_variables( $variables ) { - if ( ! is_array( $variables ) ) { - $variables = array(); - } - - $variables = $this->strip_variables( $variables ); - - foreach ( $this->build_variables() as $entry ) { - $variables[] = $entry; - } - - return $variables; - } - - /** - * Remove SLASHED-prefixed entries from a global variables array. - * - * Always returns a clean array, even when the option is malformed. - * - * @param mixed $variables Value of bricks_global_variables option. - * @return array - */ - public function strip_variables( $variables ) { - return $this->strip_prefixed( $variables, self::ID_PREFIX ); - } - - /** - * Inject SLASHED variable category entries. - * - * @param mixed $categories Existing value of bricks_global_variables_categories option. - * @return array - */ - public function inject_categories( $categories ) { - if ( ! is_array( $categories ) ) { - $categories = array(); - } - - $categories = $this->strip_categories( $categories ); - - foreach ( $this->build_categories() as $cat ) { - $categories[] = $cat; - } - - return $categories; - } - - /** - * Remove SLASHED-prefixed categories from a categories array. - * - * @param mixed $categories Value of bricks_global_variables_categories option. - * @return array - */ - public function strip_categories( $categories ) { - return $this->strip_prefixed( $categories, self::CAT_PREFIX ); - } - - /** - * Shared "strip every entry whose id starts with the given prefix" helper. - * - * @param mixed $entries Possibly malformed list of `{id,...}` entries. - * @param string $prefix Prefix to match against. - * @return array - */ - private function strip_prefixed( $entries, $prefix ) { - if ( ! is_array( $entries ) ) { - return array(); - } - - $kept = array(); - foreach ( $entries as $entry ) { - if ( is_array( $entry ) - && isset( $entry['id'] ) - && is_string( $entry['id'] ) - && 0 === strpos( $entry['id'], $prefix ) - ) { - continue; - } - $kept[] = $entry; - } - - return array_values( $kept ); - } - - // --------------------------------------------------------------- - // Variable Manager: build entries. - // --------------------------------------------------------------- - - /** - * Build SLASHED variable entries from the inventory. - * - * Each entry follows the Bricks-native variable shape: - * { id, name, value, category } - * - * - `id` is a stable slashed-* slug so strip_variables() can find it. - * - `name` is the native property name WITHOUT the `--` prefix (Bricks - * adds `--` automatically), e.g. `sf-color-primary`. - * - `value` is empty string - Bricks skips empty values in :root output, - * so the framework remains the single source of truth. - * - `category` references one of our stable category ids. - * - * @return array> - */ - public function build_variables() { - $entries = array(); - - foreach ( $this->get_variables() as $category => $vars ) { - $cat_id = self::CAT_PREFIX . sanitize_key( $category ); - - foreach ( $vars as $var ) { - // Strip leading '--' to get the native name Bricks expects. - $native_name = ltrim( $var, '-' ); - - $entries[] = array( - 'id' => self::ID_PREFIX . $native_name, - 'name' => $native_name, - 'value' => '', - 'category' => $cat_id, - ); - } - } - - /** - * Filter the SLASHED variable entries before injection. - * - * @param array $entries Variable entries. - */ - return apply_filters( 'slashed_bricks/registered_variables', $entries ); - } - - /** - * Build SLASHED variable category entries. - * - * @return array> - */ - public function build_categories() { - $categories = array(); - - foreach ( $this->get_variables() as $category => $vars ) { - if ( empty( $vars ) ) { - continue; - } - - $categories[] = array( - 'id' => self::CAT_PREFIX . sanitize_key( $category ), - 'name' => 'SLASHED ' . $category, - ); - } - - return $categories; - } - - // --------------------------------------------------------------- - // Shared variable source. - // --------------------------------------------------------------- - - /** - * Get all SLASHED CSS variables grouped by category. - * - * @return array Map of category => variable names. - */ - public function get_variables() { - $variables = Slashed_Bricks_Inventory::get_variables_by_category(); - - /** - * Filter the registered CSS variables. - * - * @param array $variables Map of category => variable names. - */ - return apply_filters( 'slashed_bricks/variables', $variables ); - } - - // --------------------------------------------------------------- - // Code editor autocomplete. - // --------------------------------------------------------------- - - /** - * Register variables for the Bricks code editor autocomplete. - * - * Uses the original --sf-* names so authors writing CSS see the - * framework's native namespace directly. - * - * @param array $signatures Existing code signatures. - * @return array Modified code signatures. - */ - public function register_code_signatures( $signatures ) { - if ( ! is_array( $signatures ) ) { - $signatures = array(); - } - - $css_vars = array(); - foreach ( $this->get_variables() as $category => $vars ) { - foreach ( $vars as $var ) { - $css_vars[] = array( - 'label' => $var, - 'detail' => 'SLASHED ' . $category, - 'type' => 'variable', - ); - } - } - - if ( ! isset( $signatures['css'] ) || ! is_array( $signatures['css'] ) ) { - $signatures['css'] = array(); - } - - $signatures['css']['slashed_variables'] = $css_vars; - - return $signatures; - } + /** + * Prefix used on every variable id this integration injects. + * Used to identify our entries when stripping on save. + */ + const ID_PREFIX = 'slashed-'; + + /** + * Prefix used on every category id this integration injects. + */ + const CAT_PREFIX = 'slashed-cat-'; + + /** + * Constructor. Register option filters and code editor hook. + */ + public function __construct() { + // Inject SLASHED variables when Bricks reads the option. + add_filter( 'option_bricks_global_variables', array( $this, 'inject_variables' ), 20 ); + add_filter( 'default_option_bricks_global_variables', array( $this, 'inject_variables' ), 20 ); + + // Strip SLASHED variables before they are persisted back to the DB. + add_filter( 'pre_update_option_bricks_global_variables', array( $this, 'strip_variables' ), 10, 1 ); + + // Same managed/virtual pattern for variable categories. + add_filter( 'option_bricks_global_variables_categories', array( $this, 'inject_categories' ), 20 ); + add_filter( 'default_option_bricks_global_variables_categories', array( $this, 'inject_categories' ), 20 ); + add_filter( 'pre_update_option_bricks_global_variables_categories', array( $this, 'strip_categories' ), 10, 1 ); + + // Code editor autocomplete (Bricks 1.9.2+): lists the original + // --sf-* names so authors typing custom CSS see the framework's + // native namespace directly. + add_filter( 'bricks/code/get_code_signatures', array( $this, 'register_code_signatures' ) ); + } + + // --------------------------------------------------------------- + // Variable Manager: inject / strip. + // --------------------------------------------------------------- + + /** + * Inject SLASHED variables into the Bricks global variables list. + * + * Idempotent: any existing SLASHED-prefixed entries are removed first + * so multiple read passes don't create duplicates. + * + * @param mixed $variables Existing value of bricks_global_variables option. + * @return array + */ + public function inject_variables( $variables ) { + if ( ! is_array( $variables ) ) { + $variables = array(); + } + + $variables = $this->strip_variables( $variables ); + + foreach ( $this->build_variables() as $entry ) { + $variables[] = $entry; + } + + return $variables; + } + + /** + * Remove SLASHED-prefixed entries from a global variables array. + * + * Always returns a clean array, even when the option is malformed. + * + * @param mixed $variables Value of bricks_global_variables option. + * @return array + */ + public function strip_variables( $variables ) { + return $this->strip_prefixed( $variables, self::ID_PREFIX ); + } + + /** + * Inject SLASHED variable category entries. + * + * @param mixed $categories Existing value of bricks_global_variables_categories option. + * @return array + */ + public function inject_categories( $categories ) { + if ( ! is_array( $categories ) ) { + $categories = array(); + } + + $categories = $this->strip_categories( $categories ); + + foreach ( $this->build_categories() as $cat ) { + $categories[] = $cat; + } + + return $categories; + } + + /** + * Remove SLASHED-prefixed categories from a categories array. + * + * @param mixed $categories Value of bricks_global_variables_categories option. + * @return array + */ + public function strip_categories( $categories ) { + return $this->strip_prefixed( $categories, self::CAT_PREFIX ); + } + + /** + * Shared "strip every entry whose id starts with the given prefix" helper. + * + * @param mixed $entries Possibly malformed list of `{id,...}` entries. + * @param string $prefix Prefix to match against. + * @return array + */ + private function strip_prefixed( $entries, $prefix ) { + if ( ! is_array( $entries ) ) { + return array(); + } + + $kept = array(); + foreach ( $entries as $entry ) { + if ( is_array( $entry ) + && isset( $entry['id'] ) + && is_string( $entry['id'] ) + && 0 === strpos( $entry['id'], $prefix ) + ) { + continue; + } + $kept[] = $entry; + } + + return array_values( $kept ); + } + + // --------------------------------------------------------------- + // Variable Manager: build entries. + // --------------------------------------------------------------- + + /** + * Build SLASHED variable entries from the inventory. + * + * Each entry follows the Bricks-native variable shape: + * { id, name, value, category } + * + * - `id` is a stable slashed-* slug so strip_variables() can find it. + * - `name` is the native property name WITHOUT the `--` prefix (Bricks + * adds `--` automatically), e.g. `sf-color-primary`. + * - `value` is empty string - Bricks skips empty values in :root output, + * so the framework remains the single source of truth. + * - `category` references one of our stable category ids. + * + * @return array> + */ + public function build_variables() { + $entries = array(); + + foreach ( $this->get_variables() as $category => $vars ) { + $cat_id = self::CAT_PREFIX . sanitize_key( $category ); + + foreach ( $vars as $var ) { + // Strip leading '--' to get the native name Bricks expects. + $native_name = ltrim( $var, '-' ); + + $entries[] = array( + 'id' => self::ID_PREFIX . $native_name, + 'name' => $native_name, + 'value' => '', + 'category' => $cat_id, + ); + } + } + + /** + * Filter the SLASHED variable entries before injection. + * + * @param array $entries Variable entries. + */ + return apply_filters( 'slashed_bricks/registered_variables', $entries ); + } + + /** + * Build SLASHED variable category entries. + * + * @return array> + */ + public function build_categories() { + $categories = array(); + + foreach ( $this->get_variables() as $category => $vars ) { + if ( empty( $vars ) ) { + continue; + } + + $categories[] = array( + 'id' => self::CAT_PREFIX . sanitize_key( $category ), + 'name' => 'SLASHED ' . $category, + ); + } + + return $categories; + } + + // --------------------------------------------------------------- + // Shared variable source. + // --------------------------------------------------------------- + + /** + * Get all SLASHED CSS variables grouped by category. + * + * @return array Map of category => variable names. + */ + public function get_variables() { + $variables = Slashed_Bricks_Inventory::get_variables_by_category(); + + /** + * Filter the registered CSS variables. + * + * @param array $variables Map of category => variable names. + */ + return apply_filters( 'slashed_bricks/variables', $variables ); + } + + // --------------------------------------------------------------- + // Code editor autocomplete. + // --------------------------------------------------------------- + + /** + * Register variables for the Bricks code editor autocomplete. + * + * Uses the original --sf-* names so authors writing CSS see the + * framework's native namespace directly. + * + * @param array $signatures Existing code signatures. + * @return array Modified code signatures. + */ + public function register_code_signatures( $signatures ) { + if ( ! is_array( $signatures ) ) { + $signatures = array(); + } + + $css_vars = array(); + foreach ( $this->get_variables() as $category => $vars ) { + foreach ( $vars as $var ) { + $css_vars[] = array( + 'label' => $var, + 'detail' => 'SLASHED ' . $category, + 'type' => 'variable', + ); + } + } + + if ( ! isset( $signatures['css'] ) || ! is_array( $signatures['css'] ) ) { + $signatures['css'] = array(); + } + + $signatures['css']['slashed_variables'] = $css_vars; + + return $signatures; + } } diff --git a/SLASHED-for-WP/integrations/bricks/slashed-bricks.php b/SLASHED-for-WP/integrations/bricks/slashed-bricks.php index ebfafd66..dbece9a0 100644 --- a/SLASHED-for-WP/integrations/bricks/slashed-bricks.php +++ b/SLASHED-for-WP/integrations/bricks/slashed-bricks.php @@ -13,7 +13,7 @@ */ if ( ! defined( 'ABSPATH' ) ) { - exit; + exit; } /** @@ -25,10 +25,10 @@ * the standalone Bricks plugin and the unified SLASHED plugin are active. */ if ( ! defined( 'SLASHED_BRICKS_VERSION' ) ) { - define( 'SLASHED_BRICKS_VERSION', '0.0.1' ); - define( 'SLASHED_BRICKS_PATH', plugin_dir_path( __FILE__ ) ); - define( 'SLASHED_BRICKS_URL', plugin_dir_url( __FILE__ ) ); - define( 'SLASHED_BRICKS_CSS_REF', 'v0.5.23' ); + define( 'SLASHED_BRICKS_VERSION', '0.0.1' ); + define( 'SLASHED_BRICKS_PATH', plugin_dir_path( __FILE__ ) ); + define( 'SLASHED_BRICKS_URL', plugin_dir_url( __FILE__ ) ); + define( 'SLASHED_BRICKS_CSS_REF', 'v0.5.23' ); } /** @@ -37,24 +37,27 @@ * everything from the shared includes directory two levels up. */ if ( ! defined( 'SLASHED_VERSION' ) ) { - add_action( 'init', function () { - load_plugin_textdomain( 'slashed-bricks', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); - } ); + add_action( + 'init', + function () { + load_plugin_textdomain( 'slashed-bricks', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); + } + ); } if ( ! class_exists( 'Slashed_Token_Store' ) ) { - $_slashed_shared = SLASHED_BRICKS_PATH . '../../includes/'; - require_once $_slashed_shared . 'class-settings.php'; - require_once $_slashed_shared . 'class-css-loader.php'; - require_once $_slashed_shared . 'class-core-enqueue.php'; - require_once $_slashed_shared . 'class-token-store.php'; - require_once $_slashed_shared . 'class-token-sanitizer.php'; - require_once $_slashed_shared . 'class-token-defaults.php'; - require_once $_slashed_shared . 'class-tab-registry.php'; - require_once $_slashed_shared . 'class-css-generator.php'; - require_once $_slashed_shared . 'class-rest-controller.php'; - require_once $_slashed_shared . 'class-token-page.php'; - unset( $_slashed_shared ); + $slashed_shared = SLASHED_BRICKS_PATH . '../../includes/'; + require_once $slashed_shared . 'class-settings.php'; + require_once $slashed_shared . 'class-css-loader.php'; + require_once $slashed_shared . 'class-core-enqueue.php'; + require_once $slashed_shared . 'class-token-store.php'; + require_once $slashed_shared . 'class-token-sanitizer.php'; + require_once $slashed_shared . 'class-token-defaults.php'; + require_once $slashed_shared . 'class-tab-registry.php'; + require_once $slashed_shared . 'class-css-generator.php'; + require_once $slashed_shared . 'class-rest-controller.php'; + require_once $slashed_shared . 'class-token-page.php'; + unset( $slashed_shared ); } /** @@ -66,15 +69,15 @@ * @return string One of 'essential', 'optimal', 'full'. */ function slashed_bricks_get_css_bundle() { - if ( class_exists( 'Slashed_CSS_Loader' ) ) { - return Slashed_CSS_Loader::get_bundle(); - } - $settings = Slashed_Token_Store::get_plugin_settings(); - $bundle = isset( $settings['css_bundle'] ) ? (string) $settings['css_bundle'] : 'optimal'; - if ( ! in_array( $bundle, Slashed_Token_Store::ALLOWED_CSS_BUNDLES, true ) ) { - $bundle = 'optimal'; - } - return $bundle; + if ( class_exists( 'Slashed_CSS_Loader' ) ) { + return Slashed_CSS_Loader::get_bundle(); + } + $settings = Slashed_Token_Store::get_plugin_settings(); + $bundle = isset( $settings['css_bundle'] ) ? (string) $settings['css_bundle'] : 'optimal'; + if ( ! in_array( $bundle, Slashed_Token_Store::ALLOWED_CSS_BUNDLES, true ) ) { + $bundle = 'optimal'; + } + return $bundle; } /** @@ -90,28 +93,28 @@ function slashed_bricks_get_css_bundle() { * @return string URL to the CSS bundle. */ function slashed_bricks_get_css_url() { - if ( class_exists( 'Slashed_CSS_Loader' ) ) { - return apply_filters( 'slashed_bricks/css_bundle_url', Slashed_CSS_Loader::get_url() ); - } - - // Standalone fallback. - $bundle = slashed_bricks_get_css_bundle(); - $filename = 'slashed.' . $bundle . '.css'; - $default_url = sprintf( - 'https://github.com/codeslash-dev/SLASHED/releases/download/%s/%s', - rawurlencode( SLASHED_BRICKS_CSS_REF ), - $filename - ); - - // Check plugin root dist/ first (two levels up: integrations/bricks/ → plugin root). - $plugin_dist = SLASHED_BRICKS_PATH . '../../dist/' . $filename; - if ( file_exists( $plugin_dist ) ) { - $default_url = SLASHED_BRICKS_URL . '../../dist/' . $filename; - } elseif ( file_exists( SLASHED_BRICKS_PATH . 'dist/' . $filename ) ) { - $default_url = SLASHED_BRICKS_URL . 'dist/' . $filename; - } - - return apply_filters( 'slashed_bricks/css_bundle_url', $default_url ); + if ( class_exists( 'Slashed_CSS_Loader' ) ) { + return apply_filters( 'slashed_bricks/css_bundle_url', Slashed_CSS_Loader::get_url() ); + } + + // Standalone fallback. + $bundle = slashed_bricks_get_css_bundle(); + $filename = 'slashed.' . $bundle . '.css'; + $default_url = sprintf( + 'https://github.com/codeslash-dev/SLASHED/releases/download/%s/%s', + rawurlencode( SLASHED_BRICKS_CSS_REF ), + $filename + ); + + // Check plugin root dist/ first (two levels up: integrations/bricks/ → plugin root). + $plugin_dist = SLASHED_BRICKS_PATH . '../../dist/' . $filename; + if ( file_exists( $plugin_dist ) ) { + $default_url = SLASHED_BRICKS_URL . '../../dist/' . $filename; + } elseif ( file_exists( SLASHED_BRICKS_PATH . 'dist/' . $filename ) ) { + $default_url = SLASHED_BRICKS_URL . 'dist/' . $filename; + } + + return apply_filters( 'slashed_bricks/css_bundle_url', $default_url ); } /** @@ -125,22 +128,22 @@ function slashed_bricks_get_css_url() { * @return string */ function slashed_bricks_get_bricks_version() { - if ( defined( 'BRICKS_VERSION' ) ) { - return (string) BRICKS_VERSION; - } + if ( defined( 'BRICKS_VERSION' ) ) { + return (string) BRICKS_VERSION; + } - $theme = wp_get_theme(); + $theme = wp_get_theme(); - if ( 'bricks' === strtolower( $theme->get_template() ) ) { - $parent = $theme->parent(); - return $parent ? (string) $parent->get( 'Version' ) : (string) $theme->get( 'Version' ); - } + if ( 'bricks' === strtolower( $theme->get_template() ) ) { + $parent = $theme->parent(); + return $parent ? (string) $parent->get( 'Version' ) : (string) $theme->get( 'Version' ); + } - if ( 'bricks' === strtolower( $theme->get( 'Name' ) ) ) { - return (string) $theme->get( 'Version' ); - } + if ( 'bricks' === strtolower( $theme->get( 'Name' ) ) ) { + return (string) $theme->get( 'Version' ); + } - return ''; + return ''; } /** @@ -149,13 +152,13 @@ function slashed_bricks_get_bricks_version() { * @return bool */ function slashed_bricks_is_bricks_active() { - $version = slashed_bricks_get_bricks_version(); + $version = slashed_bricks_get_bricks_version(); - if ( '' === $version ) { - return false; - } + if ( '' === $version ) { + return false; + } - return version_compare( $version, '1.9.2', '>=' ); + return version_compare( $version, '1.9.2', '>=' ); } // Token admin page (Slashed_Token_Page) and the main REST controller @@ -166,14 +169,21 @@ function slashed_bricks_is_bricks_active() { // in unified mode — CSS delivery, token REST API, admin page, override injection. new Slashed_Core_Enqueue(); - add_action( 'rest_api_init', function () { - ( new Slashed_REST_Controller() )->register_routes(); - } ); + add_action( + 'rest_api_init', + function () { + ( new Slashed_REST_Controller() )->register_routes(); + } + ); if ( is_admin() ) { - add_action( 'plugins_loaded', function () { - new Slashed_Token_Page(); - }, 20 ); + add_action( + 'plugins_loaded', + function () { + new Slashed_Token_Page(); + }, + 20 + ); } if ( ! function_exists( 'slashed_inject_token_overrides' ) ) { @@ -199,13 +209,13 @@ function slashed_inject_token_overrides() { * Only Bricks-specific endpoints (reBEMer, fonts) are registered here. */ function slashed_bricks_rest_routes_init() { - // Slashed_REST_Controller (token CRUD) is registered globally by slashed.php. - // Register only the Bricks-specific endpoints here. - require_once SLASHED_BRICKS_PATH . 'includes/class-rebemer-rest.php'; - require_once SLASHED_BRICKS_PATH . 'includes/class-fonts-rest.php'; + // Slashed_REST_Controller (token CRUD) is registered globally by slashed.php. + // Register only the Bricks-specific endpoints here. + require_once SLASHED_BRICKS_PATH . 'includes/class-rebemer-rest.php'; + require_once SLASHED_BRICKS_PATH . 'includes/class-fonts-rest.php'; - ( new Slashed_Bricks_ReBEMer_REST() )->register_routes(); - ( new Slashed_Bricks_Fonts_REST() )->register_routes(); + ( new Slashed_Bricks_ReBEMer_REST() )->register_routes(); + ( new Slashed_Bricks_Fonts_REST() )->register_routes(); } add_action( 'rest_api_init', 'slashed_bricks_rest_routes_init' ); @@ -219,9 +229,9 @@ function slashed_bricks_rest_routes_init() { * other has already run. Idempotent via require_once. */ function slashed_bricks_require_data_classes() { - require_once SLASHED_BRICKS_PATH . 'includes/class-css-parser.php'; - require_once SLASHED_BRICKS_PATH . 'includes/class-color-resolver.php'; - require_once SLASHED_BRICKS_PATH . 'includes/class-inventory.php'; + require_once SLASHED_BRICKS_PATH . 'includes/class-css-parser.php'; + require_once SLASHED_BRICKS_PATH . 'includes/class-color-resolver.php'; + require_once SLASHED_BRICKS_PATH . 'includes/class-inventory.php'; } /** @@ -237,29 +247,29 @@ function slashed_bricks_require_data_classes() { * simply never fire, which is harmless. */ function slashed_bricks_data_init() { - // Bail early on non-Bricks sites to avoid loading classes needlessly. - if ( 'bricks' !== strtolower( (string) get_option( 'template', '' ) ) ) { - return; - } - - slashed_bricks_require_data_classes(); - require_once SLASHED_BRICKS_PATH . 'includes/class-variables.php'; - require_once SLASHED_BRICKS_PATH . 'includes/class-classes.php'; - - new Slashed_Bricks_Variables(); - new Slashed_Bricks_Classes(); - - // Invalidate the Bricks Font-Manager CPT cache on every custom-font save. - // Registered here (plugins_loaded, all request types) rather than from REST - // route registration so the cache is busted on normal admin saves too, not - // only during REST requests. The collector + transient live in the - // always-loaded Slashed_Token_Page. - if ( class_exists( 'Slashed_Token_Page' ) ) { - add_action( - 'save_post_' . Slashed_Token_Page::get_bricks_fonts_post_type(), - array( 'Slashed_Token_Page', 'flush_bricks_fonts_cache' ) - ); - } + // Bail early on non-Bricks sites to avoid loading classes needlessly. + if ( 'bricks' !== strtolower( (string) get_option( 'template', '' ) ) ) { + return; + } + + slashed_bricks_require_data_classes(); + require_once SLASHED_BRICKS_PATH . 'includes/class-variables.php'; + require_once SLASHED_BRICKS_PATH . 'includes/class-classes.php'; + + new Slashed_Bricks_Variables(); + new Slashed_Bricks_Classes(); + + // Invalidate the Bricks Font-Manager CPT cache on every custom-font save. + // Registered here (plugins_loaded, all request types) rather than from REST + // route registration so the cache is busted on normal admin saves too, not + // only during REST requests. The collector + transient live in the + // always-loaded Slashed_Token_Page. + if ( class_exists( 'Slashed_Token_Page' ) ) { + add_action( + 'save_post_' . Slashed_Token_Page::get_bricks_fonts_post_type(), + array( 'Slashed_Token_Page', 'flush_bricks_fonts_cache' ) + ); + } } add_action( 'plugins_loaded', 'slashed_bricks_data_init', 20 ); @@ -270,15 +280,15 @@ function slashed_bricks_data_init() { * Data managers (variables, classes, colors) are already initialized above. */ function slashed_bricks_init() { - if ( ! slashed_bricks_is_bricks_active() ) { - add_action( 'admin_notices', 'slashed_bricks_missing_bricks_notice' ); - return; - } + if ( ! slashed_bricks_is_bricks_active() ) { + add_action( 'admin_notices', 'slashed_bricks_missing_bricks_notice' ); + return; + } - slashed_bricks_require_data_classes(); - require_once SLASHED_BRICKS_PATH . 'includes/class-enqueue.php'; + slashed_bricks_require_data_classes(); + require_once SLASHED_BRICKS_PATH . 'includes/class-enqueue.php'; - new Slashed_Bricks_Enqueue(); + new Slashed_Bricks_Enqueue(); } add_action( 'after_setup_theme', 'slashed_bricks_init' ); @@ -294,12 +304,12 @@ function slashed_bricks_init() { * notice from slashed_bricks_init() already covers that case. */ function slashed_bricks_rebemer_init() { - if ( ! slashed_bricks_is_bricks_active() ) { - return; - } + if ( ! slashed_bricks_is_bricks_active() ) { + return; + } - require_once SLASHED_BRICKS_PATH . 'includes/class-rebemer-enqueue.php'; - new Slashed_Bricks_ReBEMer_Enqueue(); + require_once SLASHED_BRICKS_PATH . 'includes/class-rebemer-enqueue.php'; + new Slashed_Bricks_ReBEMer_Enqueue(); } add_action( 'after_setup_theme', 'slashed_bricks_rebemer_init', 20 ); @@ -307,13 +317,13 @@ function slashed_bricks_rebemer_init() { * Display admin notice when Bricks Builder is not active. */ function slashed_bricks_missing_bricks_notice() { - ?> -
-

- SLASHED for Bricks requires Bricks Builder to be installed and active. -

-
- +
+

+ SLASHED for Bricks requires Bricks Builder to be installed and active. +

+
+ color_variables(); // Bucket every token by family + kind. - $families = array(); // family => kind => [ [order, var] ] - $semantic = array(); // [ [key, var] ] + $families = array(); // family => kind => [ [order, var] ] + $semantic = array(); // [ [key, var] ] foreach ( $vars as $var ) { $info = $this->classify_color( $var ); @@ -246,7 +256,7 @@ static function ( $a, $b ) { // Semantic tokens: keep the inventory's natural (sorted) order. foreach ( $semantic as $item ) { - $info = array( + $info = array( 'family' => 'semantic', 'kind' => 'semantic', 'key' => $item[0], @@ -316,17 +326,32 @@ private function classify_color( $var ) { $suffix = ( false === $dash ) ? '' : substr( $key, $dash + 1 ); if ( '' === $suffix ) { - return array( 'family' => $family, 'kind' => 'base', 'key' => $key, 'order' => -1 ); + return array( + 'family' => $family, + 'kind' => 'base', + 'key' => $key, + 'order' => -1, + ); } // The `-light` source duplicates the resolved base swatch — skip. if ( 'light' === $suffix ) { return null; } if ( preg_match( '/^[0-9]+$/', $suffix ) ) { - return array( 'family' => $family, 'kind' => 'scale', 'key' => $key, 'order' => (int) $suffix ); + return array( + 'family' => $family, + 'kind' => 'scale', + 'key' => $key, + 'order' => (int) $suffix, + ); } if ( preg_match( '/^a[0-9]+$/', $suffix ) ) { - return array( 'family' => $family, 'kind' => 'alpha', 'key' => $key, 'order' => (int) substr( $suffix, 1 ) ); + return array( + 'family' => $family, + 'kind' => 'alpha', + 'key' => $key, + 'order' => (int) substr( $suffix, 1 ), + ); } $alias_idx = array_search( $suffix, self::ALIAS_ORDER, true ); return array( diff --git a/SLASHED-for-WP/integrations/gutenberg/slashed-gutenberg.php b/SLASHED-for-WP/integrations/gutenberg/slashed-gutenberg.php index d05eecd8..0d1ac93c 100644 --- a/SLASHED-for-WP/integrations/gutenberg/slashed-gutenberg.php +++ b/SLASHED-for-WP/integrations/gutenberg/slashed-gutenberg.php @@ -25,10 +25,10 @@ * plugin they are not yet set and are defined here as usual. */ if ( ! defined( 'SLASHED_GUTENBERG_VERSION' ) ) { - define( 'SLASHED_GUTENBERG_VERSION', '0.0.1' ); - define( 'SLASHED_GUTENBERG_PATH', plugin_dir_path( __FILE__ ) ); - define( 'SLASHED_GUTENBERG_URL', plugin_dir_url( __FILE__ ) ); - define( 'SLASHED_GUTENBERG_CSS_REF', 'v0.5.23' ); + define( 'SLASHED_GUTENBERG_VERSION', '0.0.1' ); + define( 'SLASHED_GUTENBERG_PATH', plugin_dir_path( __FILE__ ) ); + define( 'SLASHED_GUTENBERG_URL', plugin_dir_url( __FILE__ ) ); + define( 'SLASHED_GUTENBERG_CSS_REF', 'v0.5.23' ); } define( 'SLASHED_GUTENBERG_ALLOWED_BUNDLES', array( 'essential', 'optimal', 'full' ) ); @@ -51,17 +51,17 @@ * multiple integrations together. */ if ( ! class_exists( 'Slashed_Token_Store' ) ) { - $_slashed_shared = SLASHED_GUTENBERG_PATH . '../../includes/'; - require_once $_slashed_shared . 'class-settings.php'; - require_once $_slashed_shared . 'class-css-loader.php'; - require_once $_slashed_shared . 'class-token-store.php'; - require_once $_slashed_shared . 'class-token-sanitizer.php'; - require_once $_slashed_shared . 'class-token-defaults.php'; - require_once $_slashed_shared . 'class-tab-registry.php'; - require_once $_slashed_shared . 'class-css-generator.php'; - require_once $_slashed_shared . 'class-rest-controller.php'; - require_once $_slashed_shared . 'class-token-page.php'; - unset( $_slashed_shared ); + $slashed_shared = SLASHED_GUTENBERG_PATH . '../../includes/'; + require_once $slashed_shared . 'class-settings.php'; + require_once $slashed_shared . 'class-css-loader.php'; + require_once $slashed_shared . 'class-token-store.php'; + require_once $slashed_shared . 'class-token-sanitizer.php'; + require_once $slashed_shared . 'class-token-defaults.php'; + require_once $slashed_shared . 'class-tab-registry.php'; + require_once $slashed_shared . 'class-css-generator.php'; + require_once $slashed_shared . 'class-rest-controller.php'; + require_once $slashed_shared . 'class-token-page.php'; + unset( $slashed_shared ); } /** @@ -122,9 +122,12 @@ function slashed_gutenberg_get_css_url() { } if ( ! defined( 'SLASHED_VERSION' ) ) { - add_action( 'init', function () { - load_plugin_textdomain( 'slashed-gutenberg', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); - } ); + add_action( + 'init', + function () { + load_plugin_textdomain( 'slashed-gutenberg', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); + } + ); } /** @@ -138,14 +141,21 @@ function slashed_gutenberg_get_css_url() { * mode, so Slashed_Core_Enqueue is not instantiated here. */ if ( ! defined( 'SLASHED_VERSION' ) ) { - add_action( 'rest_api_init', function () { - ( new Slashed_REST_Controller() )->register_routes(); - } ); + add_action( + 'rest_api_init', + function () { + ( new Slashed_REST_Controller() )->register_routes(); + } + ); if ( is_admin() ) { - add_action( 'plugins_loaded', function () { - new Slashed_Token_Page(); - }, 20 ); + add_action( + 'plugins_loaded', + function () { + new Slashed_Token_Page(); + }, + 20 + ); } // Shared with the Bricks standalone bootstrap via the !function_exists guard, diff --git a/SLASHED-for-WP/slashed.php b/SLASHED-for-WP/slashed.php index e747b747..c092253f 100644 --- a/SLASHED-for-WP/slashed.php +++ b/SLASHED-for-WP/slashed.php @@ -18,9 +18,9 @@ // ─── Canonical constants ────────────────────────────────────────────────────── -define( 'SLASHED_VERSION', '0.0.1' ); -define( 'SLASHED_PATH', plugin_dir_path( __FILE__ ) ); -define( 'SLASHED_URL', plugin_dir_url( __FILE__ ) ); +define( 'SLASHED_VERSION', '0.0.1' ); +define( 'SLASHED_PATH', plugin_dir_path( __FILE__ ) ); +define( 'SLASHED_URL', plugin_dir_url( __FILE__ ) ); /** * Framework version this plugin ships with / tracks by default. @@ -29,11 +29,14 @@ * artifacts (GitHub Release assets per version; the `dist` branch for * "latest") — see Slashed_CSS_Loader and Slashed_Framework_Updater. */ -define( 'SLASHED_CSS_REF', 'v0.5.23' ); +define( 'SLASHED_CSS_REF', 'v0.5.23' ); -add_action( 'init', function () { - load_plugin_textdomain( 'slashed', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); -} ); +add_action( + 'init', + function () { + load_plugin_textdomain( 'slashed', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); + } +); // ─── Shared infrastructure ──────────────────────────────────────────────────── @@ -60,9 +63,12 @@ require_once SLASHED_PATH . 'includes/class-rest-controller.php'; // Token REST routes live under slashed/v1, independent of any builder. -add_action( 'rest_api_init', function () { - ( new Slashed_REST_Controller() )->register_routes(); -} ); +add_action( + 'rest_api_init', + function () { + ( new Slashed_REST_Controller() )->register_routes(); + } +); // Inject token override CSS after whichever integration enqueued slashed-framework. function slashed_inject_token_overrides() { @@ -80,12 +86,15 @@ function slashed_inject_token_overrides() { if ( is_admin() ) { require_once SLASHED_PATH . 'includes/class-admin.php'; require_once SLASHED_PATH . 'includes/class-hooks-page.php'; - add_action( 'plugins_loaded', function () { - new Slashed_Admin(); - new Slashed_Token_Page(); - new Slashed_Framework_Updater(); - new Slashed_Hooks_Page(); - } ); + add_action( + 'plugins_loaded', + function () { + new Slashed_Admin(); + new Slashed_Token_Page(); + new Slashed_Framework_Updater(); + new Slashed_Hooks_Page(); + } + ); } // ─── Integration bootstraps ─────────────────────────────────────────────────── @@ -105,19 +114,27 @@ function slashed_inject_token_overrides() { // ─── Activation / deactivation ─────────────────────────────────────────────── -register_deactivation_hook( __FILE__, function () { - // Clear every scheduled instance of the Bricks version-check cron, not just - // the next one — guards against an orphaned event being left behind if the - // Bricks integration was toggled off while an instance was still scheduled. - wp_clear_scheduled_hook( 'slashed_bricks_version_check' ); -} ); +register_deactivation_hook( + __FILE__, + function () { + // Clear every scheduled instance of the Bricks version-check cron, not just + // the next one — guards against an orphaned event being left behind if the + // Bricks integration was toggled off while an instance was still scheduled. + wp_clear_scheduled_hook( 'slashed_bricks_version_check' ); + } +); // When the Bricks integration is toggled OFF via settings (not just deactivation), // clear the cron immediately so it does not remain scheduled with no handler. -add_action( 'update_option_slashed_settings', function ( $old_value, $new_value ) { - $was_on = ! empty( $old_value['integrations']['bricks'] ); - $is_on = ! empty( $new_value['integrations']['bricks'] ); - if ( $was_on && ! $is_on ) { - wp_clear_scheduled_hook( 'slashed_bricks_version_check' ); - } -}, 10, 2 ); +add_action( + 'update_option_slashed_settings', + function ( $old_value, $new_value ) { + $was_on = ! empty( $old_value['integrations']['bricks'] ); + $is_on = ! empty( $new_value['integrations']['bricks'] ); + if ( $was_on && ! $is_on ) { + wp_clear_scheduled_hook( 'slashed_bricks_version_check' ); + } + }, + 10, + 2 +); diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 96dc2f8a..aa138625 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -1,6 +1,6 @@ - Coding standards for the SLASHED for WordPress plugin. + WordPress coding standards for the SLASHED for WordPress plugin. SLASHED-for-WP @@ -13,35 +13,65 @@ - + - - - - - - - - - - + + - - + + - + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + + + diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d211db55..72b2aaca 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,35 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Constant SLASHED_PATH not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/includes/class-admin.php - - - - message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: SLASHED-for-WP/includes/class-css-generator.php - - - - message: '#^Constant SLASHED_PATH not found\.$#' - identifier: constant.notFound - count: 2 - path: SLASHED-for-WP/includes/class-css-loader.php - - - - message: '#^Constant SLASHED_URL not found\.$#' - identifier: constant.notFound - count: 3 - path: SLASHED-for-WP/includes/class-css-loader.php - - - - message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: SLASHED-for-WP/includes/class-css-parser.php - - message: ''' #^PHPDoc tag @property has invalid value \(initial\-value declarations\. Returns an associative array @@ -39,194 +9,32 @@ parameters: count: 1 path: SLASHED-for-WP/includes/class-css-parser.php - - - message: '#^Constant SLASHED_PATH not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/includes/class-framework-updater.php - - message: '#^Unreachable statement \- code above always terminates\.$#' identifier: deadCode.unreachable count: 4 path: SLASHED-for-WP/includes/class-framework-updater.php - - - message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 2 - path: SLASHED-for-WP/includes/class-inventory.php - - - - message: '#^Offset ''color_values'' on array\{variables\: array\, sf_classes\: array\, is_classes\: array\\} on left side of \?\? does not exist\.$#' - identifier: nullCoalesce.offset - count: 2 - path: SLASHED-for-WP/includes/class-inventory.php - - - - message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: SLASHED-for-WP/includes/class-rest-controller.php - - - - message: '#^Call to function is_string\(\) with string will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 2 - path: SLASHED-for-WP/includes/class-tab-registry.php - - - - message: '#^Constant SLASHED_BRICKS_PATH not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/includes/class-token-page.php - - - - message: '#^Constant SLASHED_BRICKS_URL not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/includes/class-token-page.php - - - - message: '#^Constant SLASHED_PATH not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/includes/class-token-page.php - - - - message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: SLASHED-for-WP/includes/class-token-sanitizer.php - - message: '#^Parameter \#1 \$array \(list\\) of array_values is already a list, call has no effect\.$#' identifier: arrayValues.list count: 1 path: SLASHED-for-WP/integrations/bricks/includes/class-classes.php - - - message: '#^Constant SLASHED_BRICKS_PATH not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/integrations/bricks/includes/class-color-resolver.php - - - - message: '#^Constant SLASHED_BRICKS_PATH not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/integrations/bricks/includes/class-css-parser.php - - - - message: '#^Constant SLASHED_BRICKS_URL not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/integrations/bricks/includes/class-enqueue.php - - - - message: '#^Constant SLASHED_BRICKS_PATH not found\.$#' - identifier: constant.notFound - count: 3 - path: SLASHED-for-WP/integrations/bricks/includes/class-inventory.php - - - - message: '#^Constant SLASHED_BRICKS_URL not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/integrations/bricks/includes/class-inventory.php - - - - message: '#^Constant SLASHED_BRICKS_PATH not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/integrations/bricks/includes/class-rebemer-enqueue.php - - - - message: '#^Constant SLASHED_BRICKS_URL not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/integrations/bricks/includes/class-rebemer-enqueue.php - - - - message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: SLASHED-for-WP/integrations/bricks/includes/class-rebemer-rest.php - - - - message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: SLASHED-for-WP/integrations/bricks/includes/class-variables.php - - message: '#^Parameter \#1 \$array \(list\\) of array_values is already a list, call has no effect\.$#' identifier: arrayValues.list count: 1 path: SLASHED-for-WP/integrations/bricks/includes/class-variables.php - - - message: '#^Constant SLASHED_BRICKS_PATH not found\.$#' - identifier: constant.notFound - count: 12 - path: SLASHED-for-WP/integrations/bricks/slashed-bricks.php - - - - message: '#^Constant SLASHED_BRICKS_URL not found\.$#' - identifier: constant.notFound - count: 2 - path: SLASHED-for-WP/integrations/bricks/slashed-bricks.php - - - - message: '#^Call to function is_array\(\) with array will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php - - message: '#^Call to function method_exists\(\) with ''Slashed_Token_Page'' and ''get_class_hints'' will always evaluate to true\.$#' identifier: function.alreadyNarrowedType count: 1 path: SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php - - - message: '#^Constant SLASHED_GUTENBERG_PATH not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php - - - - message: '#^Constant SLASHED_GUTENBERG_URL not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php - - - - message: '#^Constant SLASHED_GUTENBERG_PATH not found\.$#' - identifier: constant.notFound - count: 2 - path: SLASHED-for-WP/integrations/gutenberg/includes/class-enqueue.php - - - - message: '#^Constant SLASHED_GUTENBERG_PATH not found\.$#' - identifier: constant.notFound - count: 1 - path: SLASHED-for-WP/integrations/gutenberg/includes/class-inventory.php - - message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(string\)\: bool\)\|null, ''strlen'' given\.$#' identifier: argument.type count: 1 path: SLASHED-for-WP/integrations/gutenberg/includes/class-presets.php - - - - message: '#^Constant SLASHED_GUTENBERG_PATH not found\.$#' - identifier: constant.notFound - count: 9 - path: SLASHED-for-WP/integrations/gutenberg/slashed-gutenberg.php - - - - message: '#^Constant SLASHED_GUTENBERG_URL not found\.$#' - identifier: constant.notFound - count: 2 - path: SLASHED-for-WP/integrations/gutenberg/slashed-gutenberg.php diff --git a/phpstan-bootstrap.php b/phpstan-bootstrap.php new file mode 100644 index 00000000..bc28f402 --- /dev/null +++ b/phpstan-bootstrap.php @@ -0,0 +1,28 @@ + Date: Mon, 8 Jun 2026 14:49:37 +0000 Subject: [PATCH 3/4] refactor: clear residual PHPCS warnings and the PHPStan baseline Resolve the leftover lint/analysis findings at the source so PHPStan now runs with no baseline (fully strict) and PHPCS reports zero warnings: - fix malformed @property PHPDoc that broke the doc parser - replace array_filter($parts, 'strlen') with a boolean callback - guard CSS reads with is_readable() and drop the @ error suppression; justify file_get_contents on a local file - remove redundant array_values() on already-list values - remove unreachable return; after wp_send_json_* (which always exit) - reword comments that tripped the commented-out-code heuristic - inline @phpstan-ignore for the legitimate standalone-mode method_exists guard, with an explanatory comment - phpcs.xml.dist: recognise Bricks' bricks_full_access capability; silence false-positive dynamic-hook-name and reserved-keyword ($var) advisories --- SLASHED-for-WP/includes/class-css-parser.php | 6 +-- .../includes/class-framework-updater.php | 4 -- SLASHED-for-WP/includes/class-inventory.php | 9 ++++- .../bricks/includes/class-classes.php | 2 +- .../bricks/includes/class-variables.php | 2 +- .../includes/class-editor-enqueue.php | 4 ++ .../gutenberg/includes/class-presets.php | 14 +++++-- phpcs.xml.dist | 18 ++++++++- phpstan-baseline.neon | 40 ------------------- phpstan.neon.dist | 4 -- 10 files changed, 44 insertions(+), 59 deletions(-) delete mode 100644 phpstan-baseline.neon diff --git a/SLASHED-for-WP/includes/class-css-parser.php b/SLASHED-for-WP/includes/class-css-parser.php index 2ddbd961..76b72ad8 100644 --- a/SLASHED-for-WP/includes/class-css-parser.php +++ b/SLASHED-for-WP/includes/class-css-parser.php @@ -138,9 +138,9 @@ public static function empty_inventory() { /** * Extract --sf-color-* variable names and their declared values. * - * Captures both regular declarations (--sf-color-X: value;) and - * @property initial-value declarations. Returns an associative array - * mapping variable names to their raw declared values. + * Captures both regular declarations (--sf-color-X: value;) and the CSS + * `@property` at-rule initial-value declarations. Returns an associative + * array mapping variable names to their raw declared values. * * @param string $css CSS with comments removed. * @return array Map of variable name to declared value. diff --git a/SLASHED-for-WP/includes/class-framework-updater.php b/SLASHED-for-WP/includes/class-framework-updater.php index 72ec2a94..3480fa36 100644 --- a/SLASHED-for-WP/includes/class-framework-updater.php +++ b/SLASHED-for-WP/includes/class-framework-updater.php @@ -197,13 +197,11 @@ public function ajax_list_versions() { if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { wp_send_json_error( array( 'message' => __( 'Could not fetch version list. Try again later.', 'slashed' ) ) ); - return; } $body = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $body ) || empty( $body['versions'] ) ) { wp_send_json_error( array( 'message' => __( 'No versions found.', 'slashed' ) ) ); - return; } $versions = array(); @@ -255,13 +253,11 @@ public function ajax_do_update() { } if ( ! $version ) { wp_send_json_error( array( 'message' => __( 'Could not determine version to download.', 'slashed' ) ) ); - return; } $result = self::download_files( $version ); if ( is_wp_error( $result ) ) { wp_send_json_error( array( 'message' => $result->get_error_message() ) ); - return; } wp_send_json_success( diff --git a/SLASHED-for-WP/includes/class-inventory.php b/SLASHED-for-WP/includes/class-inventory.php index 571057a9..1e5b4078 100644 --- a/SLASHED-for-WP/includes/class-inventory.php +++ b/SLASHED-for-WP/includes/class-inventory.php @@ -721,7 +721,11 @@ private static function derive_local_path_from_url( $url ) { * @return array Inventory shape (may be empty on read failure). */ private static function parse_path_with_cache( $path ) { - $mtime = @filemtime( $path ); + if ( ! is_readable( $path ) ) { + return Slashed_CSS_Parser::empty_inventory(); + } + + $mtime = filemtime( $path ); if ( false === $mtime ) { return Slashed_CSS_Parser::empty_inventory(); } @@ -732,7 +736,8 @@ private static function parse_path_with_cache( $path ) { return $cached; } - $css = @file_get_contents( $path ); + // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- reading a local bundled CSS file on disk, not a remote URL. + $css = file_get_contents( $path ); if ( false === $css || '' === $css ) { return Slashed_CSS_Parser::empty_inventory(); } diff --git a/SLASHED-for-WP/integrations/bricks/includes/class-classes.php b/SLASHED-for-WP/integrations/bricks/includes/class-classes.php index 81528980..c91ed522 100644 --- a/SLASHED-for-WP/integrations/bricks/includes/class-classes.php +++ b/SLASHED-for-WP/integrations/bricks/includes/class-classes.php @@ -169,7 +169,7 @@ private function strip_prefixed( $entries ) { $kept[] = $entry; } - return array_values( $kept ); + return $kept; } /** diff --git a/SLASHED-for-WP/integrations/bricks/includes/class-variables.php b/SLASHED-for-WP/integrations/bricks/includes/class-variables.php index b6a0e8d7..d54e8518 100644 --- a/SLASHED-for-WP/integrations/bricks/includes/class-variables.php +++ b/SLASHED-for-WP/integrations/bricks/includes/class-variables.php @@ -175,7 +175,7 @@ private function strip_prefixed( $entries, $prefix ) { $kept[] = $entry; } - return array_values( $kept ); + return $kept; } // --------------------------------------------------------------- diff --git a/SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php b/SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php index 2eaf18cd..4c2a0aae 100644 --- a/SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php +++ b/SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php @@ -83,6 +83,10 @@ private function build_data() { $inv = 'Slashed_Gutenberg_Inventory'; $class_hints = array(); + // Slashed_Token_Page ships in the shared plugin; in standalone Gutenberg + // mode it isn't loaded, so this guard is genuinely needed even though + // whole-project static analysis can see the class. + // @phpstan-ignore function.alreadyNarrowedType if ( class_exists( 'Slashed_Token_Page' ) && method_exists( 'Slashed_Token_Page', 'get_class_hints' ) ) { $hints = Slashed_Token_Page::get_class_hints(); if ( is_array( $hints ) ) { diff --git a/SLASHED-for-WP/integrations/gutenberg/includes/class-presets.php b/SLASHED-for-WP/integrations/gutenberg/includes/class-presets.php index bcd10341..f52e4b30 100644 --- a/SLASHED-for-WP/integrations/gutenberg/includes/class-presets.php +++ b/SLASHED-for-WP/integrations/gutenberg/includes/class-presets.php @@ -208,8 +208,8 @@ public function build_palette() { $vars = $this->color_variables(); // Bucket every token by family + kind. - $families = array(); // family => kind => [ [order, var] ] - $semantic = array(); // [ [key, var] ] + $families = array(); // Keyed by family, then kind; holds order/var/info tuples. + $semantic = array(); // Holds key/var tuples for semantic tokens. foreach ( $vars as $var ) { $info = $this->classify_color( $var ); @@ -406,7 +406,15 @@ private function color_label( $info ) { private function humanize( $key ) { $key = str_replace( '--', '-', $key ); $parts = preg_split( '/-+/', $key ); - $parts = array_map( 'ucfirst', array_filter( $parts, 'strlen' ) ); + $parts = array_map( + 'ucfirst', + array_filter( + $parts, + static function ( $part ) { + return '' !== $part; + } + ) + ); return implode( ' ', $parts ); } diff --git a/phpcs.xml.dist b/phpcs.xml.dist index aa138625..db7b2d2c 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -48,11 +48,13 @@ - + + @@ -65,6 +67,20 @@ + + + 0 + + + + + 0 + + diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon deleted file mode 100644 index 72b2aaca..00000000 --- a/phpstan-baseline.neon +++ /dev/null @@ -1,40 +0,0 @@ -parameters: - ignoreErrors: - - - message: ''' - #^PHPDoc tag @property has invalid value \(initial\-value declarations\. Returns an associative array - mapping variable names to their raw declared values\.\)\: Unexpected token "declarations", expected variable at offset 169 on line 5$# - ''' - identifier: phpDoc.parseError - count: 1 - path: SLASHED-for-WP/includes/class-css-parser.php - - - - message: '#^Unreachable statement \- code above always terminates\.$#' - identifier: deadCode.unreachable - count: 4 - path: SLASHED-for-WP/includes/class-framework-updater.php - - - - message: '#^Parameter \#1 \$array \(list\\) of array_values is already a list, call has no effect\.$#' - identifier: arrayValues.list - count: 1 - path: SLASHED-for-WP/integrations/bricks/includes/class-classes.php - - - - message: '#^Parameter \#1 \$array \(list\\) of array_values is already a list, call has no effect\.$#' - identifier: arrayValues.list - count: 1 - path: SLASHED-for-WP/integrations/bricks/includes/class-variables.php - - - - message: '#^Call to function method_exists\(\) with ''Slashed_Token_Page'' and ''get_class_hints'' will always evaluate to true\.$#' - identifier: function.alreadyNarrowedType - count: 1 - path: SLASHED-for-WP/integrations/gutenberg/includes/class-editor-enqueue.php - - - - message: '#^Parameter \#2 \$callback of function array_filter expects \(callable\(string\)\: bool\)\|null, ''strlen'' given\.$#' - identifier: argument.type - count: 1 - path: SLASHED-for-WP/integrations/gutenberg/includes/class-presets.php diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 268e6f8e..b5a664e6 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,10 +1,6 @@ includes: # WordPress function/class/constant stubs + WP-aware rules. - vendor/szepeviktor/phpstan-wordpress/extension.neon - # Existing findings are frozen here so the gate fails only on NEW issues. - # Regenerate after intentionally addressing/adding code with: - # composer phpstan -- --generate-baseline phpstan-baseline.neon - - phpstan-baseline.neon parameters: level: 5 From 02a99efacd03437af404cb9fcbf5462639c5cf9c Mon Sep 17 00:00:00 2001 From: Kiro Agent <244629292+kiro-agent@users.noreply.github.com> Date: Mon, 8 Jun 2026 15:08:45 +0000 Subject: [PATCH 4/4] ci: drop npm cache from release workflow Address CodeRabbit/zizmor cache-poisoning finding: a poisoned dependency cache could be baked into the published plugin zip. Release builds now fetch dependencies fresh from the registry with integrity verification. CI workflows keep their cache (they don't publish artifacts). --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c005ef30..afbb7e4f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,10 +32,12 @@ jobs: # For workflow_dispatch, check out the specified tag. ref: ${{ github.event.inputs.tag || github.ref }} + # No dependency cache on the release path: a poisoned cache could be + # baked into the published plugin zip. Fetch fresh from the registry + # with integrity verification (flagged by zizmor: cache-poisoning). - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 22 - cache: npm - name: Install dependencies run: npm ci