diff --git a/functions.php b/functions.php
index 4ae8467..a0b9742 100644
--- a/functions.php
+++ b/functions.php
@@ -6,7 +6,7 @@
*/
if ( ! defined( 'ABSPATH' ) ) {
- exit;
+ exit;
}
/**
@@ -15,11 +15,11 @@ if ( ! defined( 'ABSPATH' ) ) {
* ------------------------------------------------------------------------
*/
if ( ! defined( 'ZEITFRESSER_VERSION' ) ) {
- define( 'ZEITFRESSER_VERSION', '2.3.6' );
+ define( 'ZEITFRESSER_VERSION', '2.3.6' );
}
if ( ! defined( 'ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION' ) ) {
- define( 'ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION', '1.0' );
+ define( 'ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION', '1.0' );
}
/**
@@ -27,82 +27,38 @@ if ( ! defined( 'ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION' ) ) {
* Customizer
* ------------------------------------------------------------------------
*/
-require get_template_directory() . '/inc/customizer/core-settings.php';
-require get_template_directory() . '/inc/customizer/general-settings.php';
-require get_template_directory() . '/inc/customizer/layout-settings.php';
-require get_template_directory() . '/inc/customizer/toc-settings.php';
-require get_template_directory() . '/inc/customizer/social-settings.php';
-require get_template_directory() . '/inc/customizer/image-optimizer-settings.php';
+require_once get_template_directory() . '/inc/customizer/core-settings.php';
+require_once get_template_directory() . '/inc/customizer/general-settings.php';
+require_once get_template_directory() . '/inc/customizer/layout-settings.php';
+require_once get_template_directory() . '/inc/customizer/toc-settings.php';
+require_once get_template_directory() . '/inc/customizer/social-settings.php';
+require_once get_template_directory() . '/inc/customizer/image-optimizer-settings.php';
/**
* ------------------------------------------------------------------------
* Utilities
* ------------------------------------------------------------------------
*/
-require get_template_directory() . '/inc/utilities/helpers.php';
-require get_template_directory() . '/inc/utilities/template-tags.php';
-require get_template_directory() . '/inc/utilities/template-functions.php';
-require get_template_directory() . '/inc/utilities/pagination.php';
-require get_template_directory() . '/inc/utilities/toc.php';
+require_once get_template_directory() . '/inc/utilities/helpers.php';
+require_once get_template_directory() . '/inc/utilities/template-tags.php';
+require_once get_template_directory() . '/inc/utilities/template-functions.php';
+require_once get_template_directory() . '/inc/utilities/pagination.php';
+require_once get_template_directory() . '/inc/utilities/toc.php';
/**
* ------------------------------------------------------------------------
* Tools
* ------------------------------------------------------------------------
*/
-require get_template_directory() . '/inc/tools/image-optimizer.php';
-require get_template_directory() . '/inc/tools/code-block.php';
+require_once get_template_directory() . '/inc/tools/image-optimizer.php';
+require_once get_template_directory() . '/inc/tools/code-block.php';
/**
* ------------------------------------------------------------------------
- * Upload Handling (Original File Tracking)
+ * Performance
* ------------------------------------------------------------------------
*/
-function zeitfresser_capture_original_upload( $upload, $context ) {
-
- if ( empty( $upload['file'] ) ) {
- return $upload;
- }
-
- // Store temporarily (request-scoped)
- $GLOBALS['zeitfresser_last_uploaded_file'] = $upload['file'];
-
- return $upload;
-}
-add_filter( 'wp_handle_upload', 'zeitfresser_capture_original_upload', 10, 2 );
-
-/**
- * Persist original file path to attachment meta
- */
-function zeitfresser_store_original_file( $attachment_id ) {
-
- if ( ! wp_attachment_is_image( $attachment_id ) ) {
- return;
- }
-
- if ( empty( $GLOBALS['zeitfresser_last_uploaded_file'] ) ) {
- return;
- }
-
- $file = $GLOBALS['zeitfresser_last_uploaded_file'];
-
- // Safety: ensure file still exists
- if ( ! file_exists( $file ) ) {
- return;
- }
-
- // Prevent overwrite if already set
- if ( get_post_meta( $attachment_id, '_zeitfresser_original_file', true ) ) {
- return;
- }
-
- update_post_meta(
- $attachment_id,
- '_zeitfresser_original_file',
- $file
- );
-}
-add_action( 'add_attachment', 'zeitfresser_store_original_file' );
+require_once get_template_directory() . '/inc/performance/performance.php';
/**
* ------------------------------------------------------------------------
@@ -111,38 +67,42 @@ add_action( 'add_attachment', 'zeitfresser_store_original_file' );
*/
function zeitfresser_setup() {
- load_theme_textdomain( 'zeitfresser', get_template_directory() . '/languages' );
+ load_theme_textdomain( 'zeitfresser', get_template_directory() . '/languages' );
- add_theme_support( 'automatic-feed-links' );
- add_theme_support( 'title-tag' );
- add_theme_support( 'post-thumbnails' );
+ add_theme_support( 'automatic-feed-links' );
+ add_theme_support( 'title-tag' );
+ add_theme_support( 'post-thumbnails' );
- add_theme_support( 'html5', array(
- 'search-form',
- 'comment-form',
- 'comment-list',
- 'gallery',
- 'caption',
- 'style',
- 'script',
- ));
+ // Editor Styles
+ add_theme_support( 'editor-styles' );
+ add_editor_style( 'assets/css/editor.css' );
- add_theme_support( 'customize-selective-refresh-widgets' );
+ add_theme_support( 'html5', array(
+ 'search-form',
+ 'comment-form',
+ 'comment-list',
+ 'gallery',
+ 'caption',
+ 'style',
+ 'script',
+ ));
- add_theme_support( 'custom-logo', array(
- 'height' => 250,
- 'width' => 250,
- 'flex-width' => true,
- 'flex-height' => true,
- ));
+ add_theme_support( 'customize-selective-refresh-widgets' );
- add_theme_support( 'align-wide' );
- add_theme_support( 'wp-block-styles' );
- add_theme_support( 'responsive-embeds' );
+ add_theme_support( 'custom-logo', array(
+ 'height' => 250,
+ 'width' => 250,
+ 'flex-width' => true,
+ 'flex-height' => true,
+ ));
- register_nav_menus( array(
- 'menu-1' => esc_html__( 'Primary', 'zeitfresser' ),
- ));
+ add_theme_support( 'align-wide' );
+ add_theme_support( 'wp-block-styles' );
+ add_theme_support( 'responsive-embeds' );
+
+ register_nav_menus( array(
+ 'menu-1' => esc_html__( 'Primary', 'zeitfresser' ),
+ ));
}
add_action( 'after_setup_theme', 'zeitfresser_setup' );
@@ -152,8 +112,8 @@ add_action( 'after_setup_theme', 'zeitfresser_setup' );
* ------------------------------------------------------------------------
*/
function zeitfresser_custom_image_sizes() {
- add_image_size( 'zeitfresser-content', 720, 0, false );
- add_image_size( 'zeitfresser-card', 480, 0, false );
+ add_image_size( 'zeitfresser-content', 720, 0, false );
+ add_image_size( 'zeitfresser-card', 480, 0, false );
}
add_action( 'after_setup_theme', 'zeitfresser_custom_image_sizes' );
@@ -163,7 +123,7 @@ add_action( 'after_setup_theme', 'zeitfresser_custom_image_sizes' );
* ------------------------------------------------------------------------
*/
function zeitfresser_content_width() {
- $GLOBALS['content_width'] = apply_filters( 'zeitfresser_content_width', 640 );
+ $GLOBALS['content_width'] = apply_filters( 'zeitfresser_content_width', 640 );
}
add_action( 'after_setup_theme', 'zeitfresser_content_width', 0 );
@@ -173,15 +133,15 @@ add_action( 'after_setup_theme', 'zeitfresser_content_width', 0 );
* ------------------------------------------------------------------------
*/
function zeitfresser_widgets_init() {
- register_sidebar( array(
- 'name' => esc_html__( 'Sidebar', 'zeitfresser' ),
- 'id' => 'sidebar-1',
- 'description' => esc_html__( 'Add widgets here.', 'zeitfresser' ),
- 'before_widget' => '',
- 'before_title' => '
',
- ));
+ register_sidebar( array(
+ 'name' => esc_html__( 'Sidebar', 'zeitfresser' ),
+ 'id' => 'sidebar-1',
+ 'description' => esc_html__( 'Add widgets here.', 'zeitfresser' ),
+ 'before_widget' => '',
+ 'before_title' => '',
+ ));
}
add_action( 'widgets_init', 'zeitfresser_widgets_init' );
@@ -192,510 +152,65 @@ add_action( 'widgets_init', 'zeitfresser_widgets_init' );
*/
function zeitfresser_scripts() {
- // Base stylesheet (theme root)
- wp_enqueue_style(
- 'zeitfresser',
- get_template_directory_uri() . '/style.css',
- [],
- file_exists( get_template_directory() . '/style.css' )
- ? filemtime( get_template_directory() . '/style.css' )
- : ZEITFRESSER_VERSION
- );
+ /**
+ * Base stylesheet
+ */
+ wp_enqueue_style(
+ 'zeitfresser',
+ get_template_directory_uri() . '/style.css',
+ [],
+ file_exists( get_template_directory() . '/style.css' )
+ ? filemtime( get_template_directory() . '/style.css' )
+ : ZEITFRESSER_VERSION
+ );
- // Styles
- $fonts = zeitfresser_asset_versioned('/css/fonts.css');
- $colors = zeitfresser_asset_versioned('/css/colors.css');
+ /**
+ * Additional styles (versioned helper)
+ */
+ $fonts = zeitfresser_asset_versioned('/css/fonts.css');
+ $colors = zeitfresser_asset_versioned('/css/colors.css');
- wp_enqueue_style(
- 'zeitfresser-fonts',
- $fonts['url'],
- [],
- $fonts['version']
- );
+ wp_enqueue_style(
+ 'zeitfresser-fonts',
+ $fonts['url'],
+ ['zeitfresser'],
+ $fonts['version']
+ );
- wp_enqueue_style(
- 'zeitfresser-colors',
- $colors['url'],
- ['zeitfresser'],
- $colors['version']
- );
+ wp_enqueue_style(
+ 'zeitfresser-colors',
+ $colors['url'],
+ ['zeitfresser'],
+ $colors['version']
+ );
- // Scripts
- $nav = zeitfresser_asset_versioned('/js/navigation.js');
- $scripts = zeitfresser_asset_versioned('/js/scripts.js');
+ /**
+ * Scripts
+ */
+ $nav = zeitfresser_asset_versioned('/js/navigation.js');
+ $scripts = zeitfresser_asset_versioned('/js/scripts.js');
- wp_enqueue_script(
- 'zeitfresser-navigation',
- $nav['url'],
- [],
- $nav['version'],
- true
- );
+ wp_enqueue_script(
+ 'zeitfresser-navigation',
+ $nav['url'],
+ [],
+ $nav['version'],
+ true
+ );
- wp_enqueue_script(
- 'zeitfresser-scripts',
- $scripts['url'],
- [],
- $scripts['version'],
- true
- );
+ wp_enqueue_script(
+ 'zeitfresser-scripts',
+ $scripts['url'],
+ ['zeitfresser-navigation'],
+ $scripts['version'],
+ true
+ );
- // WordPress native threaded comments
- if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
- wp_enqueue_script( 'comment-reply' );
- }
+ /**
+ * WordPress native threaded comments
+ */
+ if ( is_singular() && comments_open() && get_option( 'thread_comments' ) ) {
+ wp_enqueue_script( 'comment-reply' );
+ }
}
add_action( 'wp_enqueue_scripts', 'zeitfresser_scripts', 10 );
-
-// Editor Styles
-function zeitfresser_editor_styles_setup() {
- add_theme_support( 'editor-styles' );
- add_editor_style( 'assets/css/editor.css' );
-}
-add_action( 'after_setup_theme', 'zeitfresser_editor_styles_setup' );
-
-/**
- * ------------------------------------------------------------------------
- * Performance Tweaks
- * ------------------------------------------------------------------------
- */
-
- /**
- * ------------------------------------------------------------------------
- * Defer non-critical JavaScript
- * ------------------------------------------------------------------------
- *
- * Prevents JS from blocking page rendering.
- */
-function zeitfresser_defer_scripts( $tag, $handle, $src ) {
-
- $defer_scripts = array(
- 'zeitfresser-navigation',
- 'zeitfresser-scripts',
- );
-
- if ( in_array( $handle, $defer_scripts, true ) ) {
- return str_replace( ' src=', ' defer src=', $tag );
- }
-
- return $tag;
-}
-add_filter( 'script_loader_tag', 'zeitfresser_defer_scripts', 10, 3 );
-
-/**
- * ------------------------------------------------------------------------
- * Image Loading Optimization (LCP + Lazy Loading)
- * ------------------------------------------------------------------------
- *
- * Ensures the first visible image loads immediately (LCP),
- * while all other images are lazy-loaded for performance.
- */
-function zeitfresser_optimize_image_attributes( $attr, $attachment, $size ) {
-
- static $is_first = true;
-
- if ( ! is_admin() ) {
-
- if ( $is_first ) {
- // First image (critical for LCP)
- $attr['loading'] = 'eager';
- $is_first = false;
- } else {
- // All other images
- $attr['loading'] = 'lazy';
- }
-
- // Improve decoding performance
- $attr['decoding'] = 'async';
- }
-
- return $attr;
-}
-add_filter( 'wp_get_attachment_image_attributes', 'zeitfresser_optimize_image_attributes', 10, 3 );
-
-/**
- * Lower the threshold for WordPress scaled originals when auto optimization is enabled.
- *
- * When automatic optimization is disabled, original uploads should remain untouched.
- *
- * @return int|false
- */
-function zeitfresser_big_image_size_threshold() {
-
- $auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
- $force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
-
- if ( ! $auto_enabled && ! $force_enabled ) {
- return false;
- }
-
- return 1800;
-}
-add_filter( 'big_image_size_threshold', 'zeitfresser_big_image_size_threshold' );
-
-/**
- * Skip generating oversized core intermediate sizes we do not use.
- *
- * @param array $sizes Registered intermediate sizes.
- * @return array
- */
-function zeitfresser_filter_intermediate_image_sizes( $sizes ) {
-
- $auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
- $force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
-
- if ( ! $auto_enabled && ! $force_enabled ) {
- return $sizes;
- }
-
- unset(
- $sizes['1536x1536'],
- $sizes['2048x2048']
- );
-
- return $sizes;
-}
-add_filter( 'intermediate_image_sizes_advanced', 'zeitfresser_filter_intermediate_image_sizes' );
-
-/**
- * Improve attachment image attributes for layout stability and fetch priority.
- *
- * @param array $attr Image markup attributes.
- * @param WP_Post $attachment Attachment post object.
- * @param string|array $size Requested image size.
- * @return array
- */
-function zeitfresser_improve_attachment_dimensions( $attr, $attachment, $size ) {
-
- if ( empty( $attr['width'] ) || empty( $attr['height'] ) ) {
- $metadata = wp_get_attachment_metadata( $attachment->ID );
-
- if ( is_array( $metadata ) && ! empty( $metadata['width'] ) && ! empty( $metadata['height'] ) ) {
- if ( empty( $attr['width'] ) ) {
- $attr['width'] = (int) $metadata['width'];
- }
-
- if ( empty( $attr['height'] ) ) {
- $attr['height'] = (int) $metadata['height'];
- }
- }
- }
-
- if ( empty( $attr['fetchpriority'] ) && ! is_admin() && ! is_feed() ) {
- static $did_set_high_priority = false;
-
- if ( ! $did_set_high_priority && is_singular() ) {
- $attr['fetchpriority'] = 'high';
- $did_set_high_priority = true;
- }
- }
-
- return $attr;
-}
-add_filter( 'wp_get_attachment_image_attributes', 'zeitfresser_improve_attachment_dimensions', 11, 3 );
-
-/**
- * ------------------------------------------------------------------------
- * Preload critical fonts
- * ------------------------------------------------------------------------
- *
- * Preloads only the fonts that are needed for initial rendering
- * (headings + body text). This improves LCP and avoids render delays.
- */
-function zeitfresser_preload_fonts() {
- ?>
-
-
-
-
-
- get_template_directory_uri(),
- 'crossorigin' => 'anonymous',
- ];
- }
-
- return $urls;
-
-}, 10, 2 );
-
-/**
- * ------------------------------------------------------------------------
- * Critical CSS (inline for faster first render)
- * ------------------------------------------------------------------------
- *
- * We inline only the minimal CSS required for initial layout.
- * This ensures the page structure renders immediately without
- * waiting for the full stylesheet.
- */
-function zeitfresser_inline_critical_css() {
- ?>
-
- 'image/avif' ) ) ) {
- $formats['image/jpeg'] = 'image/avif';
- $formats['image/png'] = 'image/avif';
-
- // Fallback to WebP.
- } elseif ( wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
- $formats['image/jpeg'] = 'image/webp';
- $formats['image/png'] = 'image/webp';
- }
- }
-
- return $formats;
-}
-add_filter( 'image_editor_output_format', 'zeitfresser_image_output_format' );
-
-/**
- * Mark images as optimized only when optimization is actually active.
- *
- * @param array $metadata Attachment metadata.
- * @param int $attachment_id Attachment ID.
- * @return array
- */
-function zeitfresser_mark_new_images_optimized( $metadata, $attachment_id ) {
-
- if ( ! wp_attachment_is_image( $attachment_id ) ) {
- return $metadata;
- }
-
- $auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
- $force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
-
- if ( ! $auto_enabled && ! $force_enabled ) {
- return $metadata;
- }
-
- update_post_meta(
- $attachment_id,
- '_zeitfresser_media_optimized_version',
- ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION
- );
-
- return $metadata;
-}
-add_filter( 'wp_generate_attachment_metadata', 'zeitfresser_mark_new_images_optimized', 20, 2 );
-
-/**
- * Auto Optimize Hook
- */
-add_filter(
- 'wp_generate_attachment_metadata',
- 'zeitfresser_auto_optimize_on_upload',
- 15,
- 2
-);
-
-function zeitfresser_auto_optimize_on_upload( $metadata, $attachment_id ) {
-
- // Only images
- if ( ! wp_attachment_is_image( $attachment_id ) ) {
- return $metadata;
- }
-
- // Feature toggle
- if ( ! get_theme_mod( 'ztfr_auto_optimize', true ) ) {
- return $metadata;
- }
-
- $file = get_attached_file( $attachment_id );
-
- if ( ! $file || ! file_exists( $file ) ) {
- return $metadata;
- }
-
- // DO NOT overwrite captured original
- if ( ! get_post_meta( $attachment_id, '_zeitfresser_original_file', true ) ) {
- update_post_meta( $attachment_id, '_zeitfresser_original_file', $file );
- }
-
- // Mark optimized (IMPORTANT: no re-trigger here)
- update_post_meta(
- $attachment_id,
- '_zeitfresser_media_optimized_version',
- ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION
- );
-
- return $metadata;
-}
-
-/**
- * Auto Delete Hook
- */
-add_filter(
- 'wp_generate_attachment_metadata',
- 'zeitfresser_auto_delete_original_after_upload',
- 30,
- 2
-);
-
-function zeitfresser_auto_delete_original_after_upload( $metadata, $attachment_id ) {
-
- if ( ! wp_attachment_is_image( $attachment_id ) ) {
- return $metadata;
- }
-
- $auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
- $delete_enabled = get_theme_mod( 'ztfr_auto_delete', false );
-
- if ( ! $auto_enabled || ! $delete_enabled ) {
- return $metadata;
- }
-
- $original = get_post_meta(
- $attachment_id,
- '_zeitfresser_original_file',
- true
- );
-
- if ( ! $original ) {
- return $metadata;
- }
-
- $ext = strtolower( pathinfo( $original, PATHINFO_EXTENSION ) );
-
- // Skip modern formats
- if ( in_array( $ext, [ 'webp', 'avif' ], true ) ) {
- update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
- return $metadata;
- }
-
- // 🔥 Wenn nichts mehr existiert → fertig
- if ( ! zeitfresser_original_family_exists( $attachment_id, $original ) ) {
- update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
- return $metadata;
- }
-
- // 🔥 HIER passiert der Fix
- zeitfresser_delete_original_family_files( $attachment_id, $original );
-
- // Final check
- if ( ! zeitfresser_original_family_exists( $attachment_id, $original ) ) {
- update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
- }
-
- return $metadata;
-}
-
-/**
- * Keep generated image quality balanced for file size and visual fidelity.
- *
- * @param int $quality Proposed image quality.
- * @param string $mime_type Image mime type.
- * @return int
- */
-function zeitfresser_image_quality( $quality, $mime_type = 'image/jpeg' ) {
-
- $auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
- $force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
-
- if ( ! $auto_enabled && ! $force_enabled ) {
- return $quality;
- }
-
- return match ($mime_type) {
- 'image/avif' => 50,
- 'image/webp' => 75,
- default => 82,
- };
-}
-add_filter( 'wp_editor_set_quality', 'zeitfresser_image_quality', 10, 2 );
-
-/**
- * Improve responsive image sizes attribute.
- *
- * @param string $sizes Existing sizes attribute.
- * @param array $size Requested image size.
- * @return string
- */
-function zeitfresser_responsive_image_sizes( $sizes, $size ) {
-
- // Single post content
- if ( is_singular() ) {
- return '(max-width: 768px) 100vw, (max-width: 1200px) 720px, 720px';
- }
-
- // Archive / blog overview
- if ( is_home() || is_front_page() || is_archive() || is_search() ) {
- return '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 400px';
- }
-
- return $sizes;
-}
-add_filter( 'wp_calculate_image_sizes', 'zeitfresser_responsive_image_sizes', 10, 2 );
diff --git a/inc/performance/performance.php b/inc/performance/performance.php
new file mode 100644
index 0000000..ba60efd
--- /dev/null
+++ b/inc/performance/performance.php
@@ -0,0 +1,236 @@
+ID );
+
+ if ( is_array( $metadata ) && ! empty( $metadata['width'] ) && ! empty( $metadata['height'] ) ) {
+ if ( empty( $attr['width'] ) ) {
+ $attr['width'] = (int) $metadata['width'];
+ }
+
+ if ( empty( $attr['height'] ) ) {
+ $attr['height'] = (int) $metadata['height'];
+ }
+ }
+ }
+
+ if ( empty( $attr['fetchpriority'] ) && ! is_admin() && ! is_feed() ) {
+ static $did_set_high_priority = false;
+
+ if ( ! $did_set_high_priority && is_singular() ) {
+ $attr['fetchpriority'] = 'high';
+ $did_set_high_priority = true;
+ }
+ }
+
+ return $attr;
+}
+add_filter( 'wp_get_attachment_image_attributes', 'zeitfresser_improve_attachment_dimensions', 11, 3 );
+
+/**
+ * ------------------------------------------------------------------------
+ * Preload critical fonts
+ * ------------------------------------------------------------------------
+ *
+ * Preloads only the fonts that are needed for initial rendering
+ * (headings + body text). This improves LCP and avoids render delays.
+ */
+function zeitfresser_preload_fonts() {
+ ?>
+
+
+
+
+
+ get_template_directory_uri(),
+ 'crossorigin' => 'anonymous',
+ ];
+ }
+
+ return $urls;
+
+}, 10, 2 );
+
+/**
+ * ------------------------------------------------------------------------
+ * Critical CSS (inline for faster first render)
+ * ------------------------------------------------------------------------
+ *
+ * We inline only the minimal CSS required for initial layout.
+ * This ensures the page structure renders immediately without
+ * waiting for the full stylesheet.
+ */
+function zeitfresser_inline_critical_css() {
+ ?>
+
+ 'image/avif' ) ) ) {
+ $formats['image/jpeg'] = 'image/avif';
+ $formats['image/png'] = 'image/avif';
+
+ // Fallback to WebP.
+ } elseif ( wp_image_editor_supports( array( 'mime_type' => 'image/webp' ) ) ) {
+ $formats['image/jpeg'] = 'image/webp';
+ $formats['image/png'] = 'image/webp';
+ }
+ }
+
+ return $formats;
+}
+add_filter( 'image_editor_output_format', 'zeitfresser_image_output_format' );
+
+/**
+ * Mark images as optimized only when optimization is actually active.
+ *
+ * @param array $metadata Attachment metadata.
+ * @param int $attachment_id Attachment ID.
+ * @return array
+ */
+function zeitfresser_mark_new_images_optimized( $metadata, $attachment_id ) {
+
+ if ( ! wp_attachment_is_image( $attachment_id ) ) {
+ return $metadata;
+ }
+
+ $auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
+ $force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
+
+ if ( ! $auto_enabled && ! $force_enabled ) {
+ return $metadata;
+ }
+
+ update_post_meta(
+ $attachment_id,
+ '_zeitfresser_media_optimized_version',
+ ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION
+ );
+
+ return $metadata;
+}
+add_filter( 'wp_generate_attachment_metadata', 'zeitfresser_mark_new_images_optimized', 20, 2 );
+
+/**
+ * Auto Optimize Hook
+ */
+add_filter(
+ 'wp_generate_attachment_metadata',
+ 'zeitfresser_auto_optimize_on_upload',
+ 15,
+ 2
+);
+
+function zeitfresser_auto_optimize_on_upload( $metadata, $attachment_id ) {
+
+ // Only images
+ if ( ! wp_attachment_is_image( $attachment_id ) ) {
+ return $metadata;
+ }
+
+ // Feature toggle
+ if ( ! get_theme_mod( 'ztfr_auto_optimize', true ) ) {
+ return $metadata;
+ }
+
+ $file = get_attached_file( $attachment_id );
+
+ if ( ! $file || ! file_exists( $file ) ) {
+ return $metadata;
+ }
+
+ // DO NOT overwrite captured original
+ if ( ! get_post_meta( $attachment_id, '_zeitfresser_original_file', true ) ) {
+ update_post_meta( $attachment_id, '_zeitfresser_original_file', $file );
+ }
+
+ // Mark optimized (IMPORTANT: no re-trigger here)
+ update_post_meta(
+ $attachment_id,
+ '_zeitfresser_media_optimized_version',
+ ZEITFRESSER_IMAGE_OPTIMIZATION_VERSION
+ );
+
+ return $metadata;
+}
+
+/**
+ * Auto Delete Hook
+ */
+add_filter(
+ 'wp_generate_attachment_metadata',
+ 'zeitfresser_auto_delete_original_after_upload',
+ 30,
+ 2
+);
+
+function zeitfresser_auto_delete_original_after_upload( $metadata, $attachment_id ) {
+
+ if ( ! wp_attachment_is_image( $attachment_id ) ) {
+ return $metadata;
+ }
+
+ $auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
+ $delete_enabled = get_theme_mod( 'ztfr_auto_delete', false );
+
+ if ( ! $auto_enabled || ! $delete_enabled ) {
+ return $metadata;
+ }
+
+ $original = get_post_meta(
+ $attachment_id,
+ '_zeitfresser_original_file',
+ true
+ );
+
+ if ( ! $original ) {
+ return $metadata;
+ }
+
+ $ext = strtolower( pathinfo( $original, PATHINFO_EXTENSION ) );
+
+ // Skip modern formats
+ if ( in_array( $ext, [ 'webp', 'avif' ], true ) ) {
+ update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
+ return $metadata;
+ }
+
+ // 🔥 Wenn nichts mehr existiert → fertig
+ if ( ! zeitfresser_original_family_exists( $attachment_id, $original ) ) {
+ update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
+ return $metadata;
+ }
+
+ // 🔥 HIER passiert der Fix
+ zeitfresser_delete_original_family_files( $attachment_id, $original );
+
+ // Final check
+ if ( ! zeitfresser_original_family_exists( $attachment_id, $original ) ) {
+ update_post_meta( $attachment_id, '_zeitfresser_original_deleted', 1 );
+ }
+
+ return $metadata;
+}
+
+/**
+ * Keep generated image quality balanced for file size and visual fidelity.
+ *
+ * @param int $quality Proposed image quality.
+ * @param string $mime_type Image mime type.
+ * @return int
+ */
+function zeitfresser_image_quality( $quality, $mime_type = 'image/jpeg' ) {
+
+ $auto_enabled = get_theme_mod( 'ztfr_auto_optimize', true );
+ $force_enabled = ! empty( $GLOBALS['zeitfresser_force_image_optimization'] );
+
+ if ( ! $auto_enabled && ! $force_enabled ) {
+ return $quality;
+ }
+
+ return match ($mime_type) {
+ 'image/avif' => 50,
+ 'image/webp' => 75,
+ default => 82,
+ };
+}
+add_filter( 'wp_editor_set_quality', 'zeitfresser_image_quality', 10, 2 );
+
+/**
+ * Improve responsive image sizes attribute.
+ *
+ * @param string $sizes Existing sizes attribute.
+ * @param array $size Requested image size.
+ * @return string
+ */
+function zeitfresser_responsive_image_sizes( $sizes, $size ) {
+
+ // Single post content
+ if ( is_singular() ) {
+ return '(max-width: 768px) 100vw, (max-width: 1200px) 720px, 720px';
+ }
+
+ // Archive / blog overview
+ if ( is_home() || is_front_page() || is_archive() || is_search() ) {
+ return '(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 400px';
+ }
+
+ return $sizes;
+}
+add_filter( 'wp_calculate_image_sizes', 'zeitfresser_responsive_image_sizes', 10, 2 );
+
+
/**
* Register admin page
*/