Add Display settings: article font, font size, table borders
New "Display" admin section with three settings: - Article Font (7 choices + theme default, Google Fonts loaded on demand) - Article Font Size (px, 0 = theme default) - Table Borders (toggle, on by default — adds borders, padding, striping) Inline CSS injected via wp_head only on singular posts with _prautoblogger_generated meta — zero impact on other pages. Extracted Image_Model_Registry to keep settings-fields-extended under 300 lines. Bump 0.6.3 → 0.7.0. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -116,7 +116,8 @@ prautoblogger/
|
||||
│ ├── admin/
|
||||
│ │ ├── class-admin-page.php # Main settings page (tabbed SaaS-style UI)
|
||||
│ │ ├── class-settings-fields.php # Declarative settings: sections + core fields (API/Models/Content/Sources)
|
||||
│ │ ├── class-settings-fields-extended.php # Operational fields: schedule, publishing, analytics, images
|
||||
│ │ ├── class-settings-fields-extended.php # Operational fields: schedule, publishing, display, analytics, images
|
||||
│ │ ├── class-image-model-registry.php # Static list of image generation models for the model picker
|
||||
│ │ ├── class-admin-notices.php # Onboarding notices, error alerts, budget warnings
|
||||
│ │ ├── class-dashboard-widget.php # WP Dashboard widget showing generation status
|
||||
│ │ ├── class-post-metabox.php # Metabox on posts showing generation metadata
|
||||
@@ -177,6 +178,7 @@ prautoblogger/
|
||||
│ │ └── (Phase 3: class-cloudflare-workers-ai-model-registry.php)
|
||||
│ │
|
||||
│ ├── frontend/
|
||||
│ │ ├── class-article-typography.php # Inline CSS for font, size, table borders on generated posts
|
||||
│ │ └── class-posts-widget.php # [prautoblogger_posts] shortcode + REST endpoint
|
||||
│ │
|
||||
│ └── models/
|
||||
@@ -430,6 +432,9 @@ All prefixed with `prautoblogger_`:
|
||||
| `prautoblogger_cloudflare_ai_token` | Encrypted Cloudflare Workers AI API token |
|
||||
| `prautoblogger_cloudflare_account_id` | Cloudflare account UUID (plaintext — identifier, not secret) |
|
||||
| `prautoblogger_image_model` | Image model alias: `flux-1-schnell` (default) or `flux-1-dev` |
|
||||
| `prautoblogger_article_font_family` | Font family key: 'default', 'inter', 'georgia', 'merriweather', 'lora', 'open_sans', 'roboto', 'system' |
|
||||
| `prautoblogger_article_font_size` | Body font size in px (0 = theme default). Recommended: 16–18. |
|
||||
| `prautoblogger_table_borders` | Toggle: add borders/padding/striping to tables (default: '1') |
|
||||
| `prautoblogger_image_style_suffix` | Text appended to every image prompt (default: CEO-locked 90s infomercial prompt) |
|
||||
| `prautoblogger_openrouter_model_registry` | Normalized OpenRouter model list (JSON array, daily refresh, serves as durable cache) |
|
||||
| `prautoblogger_openrouter_model_registry_fetched_at` | Unix timestamp of last successful model registry refresh |
|
||||
|
||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -5,6 +5,22 @@ All notable changes to PRAutoBlogger will be documented in this file.
|
||||
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project uses [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [0.7.0] — 2026-04-20
|
||||
|
||||
### Added
|
||||
- **Display settings section.** New "Display" tab in admin with three
|
||||
settings that control how generated articles render on the frontend:
|
||||
- **Article Font** — choose from 7 font families (Inter, Georgia,
|
||||
Merriweather, Lora, Open Sans, Roboto, System) or keep theme default.
|
||||
Google Fonts are loaded only on PRAutoBlogger posts that need them.
|
||||
- **Article Font Size** — set body text size in pixels (0 = theme
|
||||
default). Recommended 16–18px for comfortable reading.
|
||||
- **Table Borders** — adds visible borders, cell padding, header
|
||||
background, and alternating row colors to tables. Enabled by default.
|
||||
- **Article_Typography frontend class.** Injects inline CSS only on
|
||||
singular post pages with `_prautoblogger_generated` meta — zero
|
||||
impact on non-PRAutoBlogger pages.
|
||||
|
||||
## [0.6.3] — 2026-04-20
|
||||
|
||||
### Fixed
|
||||
|
||||
83
includes/admin/class-image-model-registry.php
Normal file
83
includes/admin/class-image-model-registry.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Static registry of available image generation models.
|
||||
*
|
||||
* What: Returns a hardcoded list of image models for the admin model picker.
|
||||
* No API discovery — updated manually when providers add/remove models.
|
||||
* Who calls it: PRAutoBlogger_Admin_Page (model picker UI) and
|
||||
* PRAutoBlogger_Image_Pipeline (model validation).
|
||||
* Dependencies: None.
|
||||
*
|
||||
* @see admin/class-settings-fields-extended.php — Image settings reference this registry.
|
||||
* @see core/class-image-pipeline.php — Validates selected model against this list.
|
||||
*/
|
||||
class PRAutoBlogger_Image_Model_Registry {
|
||||
|
||||
/**
|
||||
* Get all available image generation models.
|
||||
*
|
||||
* Each entry contains:
|
||||
* - id: Model identifier used in API calls.
|
||||
* - name: Human-readable display name.
|
||||
* - provider: 'openrouter' or 'cloudflare'.
|
||||
* - cost_per_image: Estimated USD cost per generation.
|
||||
* - capabilities: Array of capability tags.
|
||||
* - description: Short description for the admin UI.
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public static function get_models(): array {
|
||||
return [
|
||||
[
|
||||
'id' => 'google/gemini-2.5-flash-image',
|
||||
'name' => 'Gemini 2.5 Flash Image (Nano Banana)',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.005,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'description' => __( 'Good quality, low cost. Recommended.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'google/gemini-3.1-flash-image-preview',
|
||||
'name' => 'Gemini 3.1 Flash Image (Nano Banana 2)',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.008,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'description' => __( 'Latest Google. Better quality.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'google/gemini-3-pro-image-preview',
|
||||
'name' => 'Gemini 3 Pro Image (Nano Banana Pro)',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.03,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'description' => __( 'Highest quality Google. Premium.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'openai/gpt-5-image-mini',
|
||||
'name' => 'GPT-5 Image Mini',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.02,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'description' => __( 'OpenAI budget image model.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'openai/gpt-5-image',
|
||||
'name' => 'GPT-5 Image',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.08,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'description' => __( 'OpenAI premium. Photorealistic.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'flux-1-schnell',
|
||||
'name' => 'FLUX.1 schnell (Cloudflare)',
|
||||
'provider' => 'cloudflare',
|
||||
'cost_per_image' => 0.0007,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'description' => __( 'Cheapest. Low quality, 4-step.', 'prautoblogger' ),
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -2,75 +2,29 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Extended settings fields: schedule, publishing, analytics, and images.
|
||||
* Extended settings fields: schedule, publishing, analytics, display, and images.
|
||||
*
|
||||
* What: Declarative field definitions for operational settings sections.
|
||||
* Who calls it: PRAutoBlogger_Settings_Fields::get_fields() merges these in.
|
||||
* Dependencies: PRAUTOBLOGGER_DEFAULT_IMAGE_MODEL, PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX constants.
|
||||
* Dependencies: PRAUTOBLOGGER_DEFAULT_IMAGE_MODEL, PRAUTOBLOGGER_DEFAULT_IMAGE_STYLE_SUFFIX constants,
|
||||
* PRAutoBlogger_Article_Typography (font choices), PRAutoBlogger_Image_Model_Registry.
|
||||
*
|
||||
* @see admin/class-settings-fields.php — Core fields and sections; calls get_extended_fields().
|
||||
* @see admin/class-admin-page.php — Renders fields registered from these definitions.
|
||||
* @see CONVENTIONS.md — "How To: Add a New Admin Setting".
|
||||
* @see admin/class-settings-fields.php — Core fields and sections; calls get_extended_fields().
|
||||
* @see admin/class-admin-page.php — Renders fields registered from these definitions.
|
||||
* @see admin/class-image-model-registry.php — Static image model list extracted for 300-line compliance.
|
||||
* @see frontend/class-article-typography.php — Reads Display settings on the frontend.
|
||||
* @see CONVENTIONS.md — "How To: Add a New Admin Setting".
|
||||
*/
|
||||
class PRAutoBlogger_Settings_Fields_Extended {
|
||||
|
||||
/**
|
||||
* Static image model list for the admin model picker.
|
||||
* No registry API for these — they're hardcoded here and updated manually.
|
||||
* Delegates to Image_Model_Registry (extracted for 300-line file limit).
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
public static function get_image_models(): array {
|
||||
return [
|
||||
[
|
||||
'id' => 'google/gemini-2.5-flash-image',
|
||||
'name' => 'Gemini 2.5 Flash Image (Nano Banana)',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.005,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'description' => __( 'Good quality, low cost. Recommended.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'google/gemini-3.1-flash-image-preview',
|
||||
'name' => 'Gemini 3.1 Flash Image (Nano Banana 2)',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.008,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'description' => __( 'Latest Google. Better quality.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'google/gemini-3-pro-image-preview',
|
||||
'name' => 'Gemini 3 Pro Image (Nano Banana Pro)',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.03,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'description' => __( 'Highest quality Google. Premium.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'openai/gpt-5-image-mini',
|
||||
'name' => 'GPT-5 Image Mini',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.02,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'description' => __( 'OpenAI budget image model.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'openai/gpt-5-image',
|
||||
'name' => 'GPT-5 Image',
|
||||
'provider' => 'openrouter',
|
||||
'cost_per_image' => 0.08,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'description' => __( 'OpenAI premium. Photorealistic.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'flux-1-schnell',
|
||||
'name' => 'FLUX.1 schnell (Cloudflare)',
|
||||
'provider' => 'cloudflare',
|
||||
'cost_per_image' => 0.0007,
|
||||
'capabilities' => [ 'image_generation' ],
|
||||
'description' => __( 'Cheapest. Low quality, 4-step.', 'prautoblogger' ),
|
||||
],
|
||||
];
|
||||
return PRAutoBlogger_Image_Model_Registry::get_models();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,6 +164,35 @@ class PRAutoBlogger_Settings_Fields_Extended {
|
||||
'description' => __( 'Paste the full JSON key file for a service account with Analytics read access.', 'prautoblogger' ),
|
||||
],
|
||||
|
||||
// ── Display ────────────────────────────────────────────────
|
||||
[
|
||||
'id' => 'prautoblogger_article_font_family',
|
||||
'label' => __( 'Article Font', 'prautoblogger' ),
|
||||
'type' => 'select',
|
||||
'section' => 'prautoblogger_display',
|
||||
'default' => 'default',
|
||||
'options' => PRAutoBlogger_Article_Typography::get_font_choices(),
|
||||
'description' => __( 'Font family used for generated article body text. Serif fonts improve long-form readability.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'prautoblogger_article_font_size',
|
||||
'label' => __( 'Article Font Size (px)', 'prautoblogger' ),
|
||||
'type' => 'number',
|
||||
'section' => 'prautoblogger_display',
|
||||
'default' => 0,
|
||||
'min' => 0,
|
||||
'max' => 32,
|
||||
'description' => __( 'Base font size for article body text. 0 uses the theme default (13px). Recommended: 16–18px for comfortable reading.', 'prautoblogger' ),
|
||||
],
|
||||
[
|
||||
'id' => 'prautoblogger_table_borders',
|
||||
'label' => __( 'Table Borders', 'prautoblogger' ),
|
||||
'type' => 'toggle',
|
||||
'section' => 'prautoblogger_display',
|
||||
'default' => '1',
|
||||
'description' => __( 'Add visible borders, padding, and alternating row colors to tables in generated articles.', 'prautoblogger' ),
|
||||
],
|
||||
|
||||
// ── Images ─────────────────────────────────────────────────
|
||||
[
|
||||
'id' => 'prautoblogger_image_enabled',
|
||||
|
||||
@@ -60,6 +60,11 @@ class PRAutoBlogger_Settings_Fields {
|
||||
'icon' => 'dashicons-chart-area',
|
||||
'description' => __( 'Connect Google Analytics 4 for post performance scoring.', 'prautoblogger' ),
|
||||
],
|
||||
'prautoblogger_display' => [
|
||||
'title' => __( 'Display', 'prautoblogger' ),
|
||||
'icon' => 'dashicons-editor-textcolor',
|
||||
'description' => __( 'Control how generated articles look on the frontend — fonts, sizes, and table styling.', 'prautoblogger' ),
|
||||
],
|
||||
'prautoblogger_images' => [
|
||||
'title' => __( 'Images', 'prautoblogger' ),
|
||||
'icon' => 'dashicons-format-image',
|
||||
|
||||
@@ -169,6 +169,9 @@ class PRAutoBlogger_Activator {
|
||||
'prautoblogger_max_word_count' => 2000,
|
||||
'prautoblogger_topic_exclusions' => '[]',
|
||||
'prautoblogger_enabled_sources' => '["reddit"]',
|
||||
'prautoblogger_article_font_family' => 'default',
|
||||
'prautoblogger_article_font_size' => 0,
|
||||
'prautoblogger_table_borders' => '1',
|
||||
'prautoblogger_schedule_time' => '03:00',
|
||||
'prautoblogger_log_level' => 'info',
|
||||
];
|
||||
|
||||
@@ -83,11 +83,15 @@ class PRAutoBlogger {
|
||||
add_filter( 'site_transient_update_plugins', [ $this, 'filter_block_false_updates' ] );
|
||||
}
|
||||
|
||||
/** Register frontend hooks (shortcode + REST for posts widget). */
|
||||
/** Register frontend hooks (shortcode, REST, typography). */
|
||||
private function register_frontend_hooks(): void {
|
||||
$posts_widget = new PRAutoBlogger_Posts_Widget();
|
||||
add_action( 'init', [ $posts_widget, 'on_register_shortcode' ] );
|
||||
add_action( 'rest_api_init', [ $posts_widget, 'on_register_rest_route' ] );
|
||||
|
||||
$typography = new PRAutoBlogger_Article_Typography();
|
||||
add_action( 'wp_head', [ $typography, 'on_wp_head' ] );
|
||||
add_action( 'wp_enqueue_scripts', [ $typography, 'on_enqueue_fonts' ] );
|
||||
}
|
||||
|
||||
/** Register cron-triggered hooks for scheduled generation and metrics. */
|
||||
|
||||
196
includes/frontend/class-article-typography.php
Normal file
196
includes/frontend/class-article-typography.php
Normal file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Injects frontend typography and table styles for PRAutoBlogger-generated posts.
|
||||
*
|
||||
* What: Outputs inline CSS on single post pages that have the
|
||||
* _prautoblogger_generated meta flag, applying user-configured
|
||||
* font family, font size, and table border styles.
|
||||
* Who calls it: PRAutoBlogger::register_frontend_hooks() hooks this
|
||||
* into wp_head.
|
||||
* Dependencies: Reads wp_options for typography settings.
|
||||
*
|
||||
* @see class-prautoblogger.php — Registers the wp_head hook.
|
||||
* @see class-settings-fields.php — Defines the Display settings section.
|
||||
* @see admin/class-admin-page.php — Renders the settings form.
|
||||
*/
|
||||
class PRAutoBlogger_Article_Typography {
|
||||
|
||||
/** Option key for article font family. */
|
||||
private const OPT_FONT_FAMILY = 'prautoblogger_article_font_family';
|
||||
|
||||
/** Option key for article body font size in pixels. */
|
||||
private const OPT_FONT_SIZE = 'prautoblogger_article_font_size';
|
||||
|
||||
/** Option key for table border toggle. */
|
||||
private const OPT_TABLE_BORDERS = 'prautoblogger_table_borders';
|
||||
|
||||
/**
|
||||
* Output inline CSS in <head> for PRAutoBlogger posts.
|
||||
*
|
||||
* Only outputs on singular post pages generated by PRAutoBlogger.
|
||||
* The CSS targets .post-content (PR Theme's article body class)
|
||||
* and overrides the theme's CSS custom properties so the cascade
|
||||
* works naturally with headings and sub-elements.
|
||||
*
|
||||
* Side effects: echoes a <style> block.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function on_wp_head(): void {
|
||||
if ( ! is_singular( 'post' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post_id = get_queried_object_id();
|
||||
if ( '1' !== get_post_meta( $post_id, '_prautoblogger_generated', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$css = $this->build_css();
|
||||
if ( '' === $css ) {
|
||||
return;
|
||||
}
|
||||
|
||||
printf(
|
||||
"\n<style id=\"prautoblogger-typography\">\n%s\n</style>\n",
|
||||
$css // Already escaped — contains only sanitized option values.
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the CSS string from current settings.
|
||||
*
|
||||
* Reads each setting at call time (never cached) so changes
|
||||
* take effect immediately without cache invalidation.
|
||||
*
|
||||
* @return string CSS rules, or empty string if all defaults.
|
||||
*/
|
||||
private function build_css(): string {
|
||||
$rules = [];
|
||||
|
||||
$font_family = $this->get_font_family_stack();
|
||||
if ( '' !== $font_family ) {
|
||||
$rules[] = sprintf(
|
||||
".post-content,\n.post-content p,\n.post-content li {\n\tfont-family: %s;\n}",
|
||||
$font_family
|
||||
);
|
||||
}
|
||||
|
||||
$font_size = absint( get_option( self::OPT_FONT_SIZE, 0 ) );
|
||||
if ( $font_size > 0 ) {
|
||||
$rules[] = sprintf(
|
||||
".post-content,\n.post-content p,\n.post-content li {\n\tfont-size: %dpx;\n\tline-height: 1.7;\n}",
|
||||
$font_size
|
||||
);
|
||||
}
|
||||
|
||||
$table_borders = get_option( self::OPT_TABLE_BORDERS, '1' );
|
||||
if ( '1' === $table_borders ) {
|
||||
$rules[] = implode( "\n", [
|
||||
'.post-content table {',
|
||||
"\tborder-collapse: collapse;",
|
||||
"\twidth: 100%;",
|
||||
"\tmargin: 1.5em 0;",
|
||||
'}',
|
||||
'.post-content th,',
|
||||
'.post-content td {',
|
||||
"\tborder: 1px solid #d1d5db;",
|
||||
"\tpadding: 0.6em 1em;",
|
||||
"\ttext-align: left;",
|
||||
'}',
|
||||
'.post-content thead th {',
|
||||
"\tbackground: #f3f4f6;",
|
||||
"\tfont-weight: 600;",
|
||||
'}',
|
||||
'.post-content tbody tr:nth-child(even) {',
|
||||
"\tbackground: #f9fafb;",
|
||||
'}',
|
||||
] );
|
||||
}
|
||||
|
||||
return implode( "\n\n", $rules );
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the stored font-family key to a full CSS font stack.
|
||||
*
|
||||
* Returns empty string for 'default' (let the theme decide).
|
||||
*
|
||||
* @return string CSS font-family value, or empty for theme default.
|
||||
*/
|
||||
private function get_font_family_stack(): string {
|
||||
$key = sanitize_text_field( get_option( self::OPT_FONT_FAMILY, 'default' ) );
|
||||
|
||||
$stacks = [
|
||||
'default' => '',
|
||||
'inter' => '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||
'georgia' => '"Georgia", "Times New Roman", Times, serif',
|
||||
'merriweather' => '"Merriweather", Georgia, "Times New Roman", serif',
|
||||
'lora' => '"Lora", Georgia, "Times New Roman", serif',
|
||||
'open_sans' => '"Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||
'roboto' => '"Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
||||
'system' => '-apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", sans-serif',
|
||||
];
|
||||
|
||||
return $stacks[ $key ] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available font choices for the settings UI.
|
||||
*
|
||||
* @return array<string, string> Map of key => display label.
|
||||
*/
|
||||
public static function get_font_choices(): array {
|
||||
return [
|
||||
'default' => __( 'Theme Default (Inter)', 'prautoblogger' ),
|
||||
'inter' => __( 'Inter (Sans-serif)', 'prautoblogger' ),
|
||||
'georgia' => __( 'Georgia (Serif)', 'prautoblogger' ),
|
||||
'merriweather' => __( 'Merriweather (Serif)', 'prautoblogger' ),
|
||||
'lora' => __( 'Lora (Serif)', 'prautoblogger' ),
|
||||
'open_sans' => __( 'Open Sans (Sans-serif)', 'prautoblogger' ),
|
||||
'roboto' => __( 'Roboto (Sans-serif)', 'prautoblogger' ),
|
||||
'system' => __( 'System Default', 'prautoblogger' ),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue Google Fonts stylesheet when a web font is selected.
|
||||
*
|
||||
* Only loads on singular PRAutoBlogger posts that use a Google-hosted font.
|
||||
*
|
||||
* Side effects: enqueues a remote stylesheet.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function on_enqueue_fonts(): void {
|
||||
if ( ! is_singular( 'post' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$post_id = get_queried_object_id();
|
||||
if ( '1' !== get_post_meta( $post_id, '_prautoblogger_generated', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$font = sanitize_text_field( get_option( self::OPT_FONT_FAMILY, 'default' ) );
|
||||
|
||||
// Map font keys to Google Fonts URLs. Inter is already loaded by the theme.
|
||||
$google_fonts = [
|
||||
'merriweather' => 'https://fonts.googleapis.com/css2?family=Merriweather:wght@400;700&display=swap',
|
||||
'lora' => 'https://fonts.googleapis.com/css2?family=Lora:wght@400;700&display=swap',
|
||||
'open_sans' => 'https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600;700&display=swap',
|
||||
'roboto' => 'https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap',
|
||||
];
|
||||
|
||||
if ( isset( $google_fonts[ $font ] ) ) {
|
||||
wp_enqueue_style(
|
||||
'prautoblogger-google-font',
|
||||
$google_fonts[ $font ],
|
||||
[],
|
||||
null // No version for external CDN URLs.
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ declare(strict_types=1);
|
||||
* Plugin Name: PRAutoBlogger
|
||||
* Plugin URI: https://peptiderepo.com/prautoblogger
|
||||
* Description: Monitors social media for trending topics, generates SEO-friendly blog posts using AI, and publishes them on a daily schedule with full cost tracking and self-improvement metrics.
|
||||
* Version: 0.6.3
|
||||
* Version: 0.7.0
|
||||
* Requires at least: 6.0
|
||||
* Requires PHP: 7.4
|
||||
* Author: PeptideRepo
|
||||
@@ -35,7 +35,7 @@ if ( ! defined( 'ABSPATH' ) ) {
|
||||
| and limits without magic strings.
|
||||
*/
|
||||
|
||||
define( 'PRAUTOBLOGGER_VERSION', '0.6.3' );
|
||||
define( 'PRAUTOBLOGGER_VERSION', '0.7.0' );
|
||||
define( 'PRAUTOBLOGGER_DB_VERSION', '1.1.0' );
|
||||
define( 'PRAUTOBLOGGER_PLUGIN_FILE', __FILE__ );
|
||||
define( 'PRAUTOBLOGGER_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
|
||||
|
||||
Reference in New Issue
Block a user