Changeset 3443619
- Timestamp:
- 01/20/2026 10:58:30 PM (2 months ago)
- Location:
- 365i-queue-optimizer/trunk
- Files:
-
- 6 edited
-
365i-queue-optimizer.php (modified) (2 diffs)
-
CHANGELOG.md (modified) (1 diff)
-
admin/class-settings-page.php (modified) (7 diffs)
-
assets/css/admin.css (modified) (1 diff)
-
assets/js/admin.js (modified) (2 diffs)
-
readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
365i-queue-optimizer/trunk/365i-queue-optimizer.php
r3443609 r3443619 4 4 * Plugin URI: https://www.365i.co.uk/blog/2025/04/20/fix-wordpress-6-8-slow-image-uploads-with-365i-queue-optimizer/ 5 5 * Description: A lightweight WordPress plugin to optimize ActionScheduler queue processing for faster image optimization and background tasks. 6 * Version: 1. 6.06 * Version: 1.7.0 7 7 * Author: 365i 8 8 * Author URI: https://www.mcneece.com/author/mark-mcneece/ … … 22 22 23 23 // Define plugin constants. 24 define( 'QUEUE_OPTIMIZER_VERSION', '1. 6.0' );24 define( 'QUEUE_OPTIMIZER_VERSION', '1.7.0' ); 25 25 define( 'QUEUE_OPTIMIZER_PLUGIN_DIR', plugin_dir_path( __FILE__ ) ); 26 26 define( 'QUEUE_OPTIMIZER_PLUGIN_URL', plugin_dir_url( __FILE__ ) ); -
365i-queue-optimizer/trunk/CHANGELOG.md
r3443609 r3443619 2 2 3 3 All notable changes to this project will be documented in this file. 4 5 ### [1.7.0] - 2025-01-20 6 7 #### Added 8 - Inline help popovers with detailed explanations for each setting 9 - Click (?) icon next to fields to learn what they do and see recommended values 10 - Server-specific recommendations and tips in help content 11 - Accessible keyboard navigation and screen reader support 4 12 5 13 ### [1.6.0] - 2025-01-20 -
365i-queue-optimizer/trunk/admin/class-settings-page.php
r3443609 r3443619 362 362 363 363 /** 364 * Render a help trigger button. 365 * 366 * @since 1.6.0 367 * @param string $help_id The help content identifier. 368 */ 369 private function render_help_trigger( $help_id ) { 370 ?> 371 <button type="button" 372 class="qo-help-trigger" 373 data-help="<?php echo esc_attr( $help_id ); ?>" 374 aria-expanded="false" 375 aria-label="<?php esc_attr_e( 'Help', '365i-queue-optimizer' ); ?>">?</button> 376 <?php 377 } 378 379 /** 364 380 * Render time limit field. 365 381 */ … … 367 383 $value = get_option( 'queue_optimizer_time_limit', 60 ); 368 384 ?> 369 <input type="number" 370 id="queue_optimizer_time_limit" 371 name="queue_optimizer_time_limit" 372 value="<?php echo esc_attr( $value ); ?>" 373 min="10" 374 max="300" 375 step="1" 376 class="small-text" /> 385 <div class="qo-field-with-help"> 386 <input type="number" 387 id="queue_optimizer_time_limit" 388 name="queue_optimizer_time_limit" 389 value="<?php echo esc_attr( $value ); ?>" 390 min="10" 391 max="300" 392 step="1" 393 class="small-text" /> 394 <?php $this->render_help_trigger( 'time_limit' ); ?> 395 </div> 377 396 <span class="description"> 378 <?php esc_html_e( 'seconds (10-300) . Maximum time for queue processing per run.', '365i-queue-optimizer' ); ?>397 <?php esc_html_e( 'seconds (10-300)', '365i-queue-optimizer' ); ?> 379 398 </span> 380 399 <?php … … 387 406 $value = get_option( 'queue_optimizer_concurrent_batches', 4 ); 388 407 ?> 389 <input type="number" 390 id="queue_optimizer_concurrent_batches" 391 name="queue_optimizer_concurrent_batches" 392 value="<?php echo esc_attr( $value ); ?>" 393 min="1" 394 max="10" 395 step="1" 396 class="small-text" /> 408 <div class="qo-field-with-help"> 409 <input type="number" 410 id="queue_optimizer_concurrent_batches" 411 name="queue_optimizer_concurrent_batches" 412 value="<?php echo esc_attr( $value ); ?>" 413 min="1" 414 max="10" 415 step="1" 416 class="small-text" /> 417 <?php $this->render_help_trigger( 'concurrent_batches' ); ?> 418 </div> 397 419 <span class="description"> 398 <?php esc_html_e( 'batches (1-10) . Simultaneous processing threads.', '365i-queue-optimizer' ); ?>420 <?php esc_html_e( 'batches (1-10)', '365i-queue-optimizer' ); ?> 399 421 </span> 400 422 <?php … … 409 431 $value = get_option( 'queue_optimizer_batch_size', 50 ); 410 432 ?> 411 <input type="number" 412 id="queue_optimizer_batch_size" 413 name="queue_optimizer_batch_size" 414 value="<?php echo esc_attr( $value ); ?>" 415 min="25" 416 max="200" 417 step="5" 418 class="small-text" /> 433 <div class="qo-field-with-help"> 434 <input type="number" 435 id="queue_optimizer_batch_size" 436 name="queue_optimizer_batch_size" 437 value="<?php echo esc_attr( $value ); ?>" 438 min="25" 439 max="200" 440 step="5" 441 class="small-text" /> 442 <?php $this->render_help_trigger( 'batch_size' ); ?> 443 </div> 419 444 <span class="description"> 420 <?php esc_html_e( 'actions (25-200) . Actions claimed per batch.', '365i-queue-optimizer' ); ?>445 <?php esc_html_e( 'actions (25-200)', '365i-queue-optimizer' ); ?> 421 446 </span> 422 447 <?php … … 431 456 $value = get_option( 'queue_optimizer_retention_days', 7 ); 432 457 ?> 433 <input type="number" 434 id="queue_optimizer_retention_days" 435 name="queue_optimizer_retention_days" 436 value="<?php echo esc_attr( $value ); ?>" 437 min="1" 438 max="30" 439 step="1" 440 class="small-text" /> 458 <div class="qo-field-with-help"> 459 <input type="number" 460 id="queue_optimizer_retention_days" 461 name="queue_optimizer_retention_days" 462 value="<?php echo esc_attr( $value ); ?>" 463 min="1" 464 max="30" 465 step="1" 466 class="small-text" /> 467 <?php $this->render_help_trigger( 'retention_days' ); ?> 468 </div> 441 469 <span class="description"> 442 <?php esc_html_e( 'days (1-30) . How long to keep completed action logs.', '365i-queue-optimizer' ); ?>470 <?php esc_html_e( 'days (1-30)', '365i-queue-optimizer' ); ?> 443 471 </span> 444 472 <?php … … 453 481 $gd_available = extension_loaded( 'gd' ) && function_exists( 'gd_info' ); 454 482 ?> 455 <select id="queue_optimizer_image_engine" name="queue_optimizer_image_engine"> 456 <option value="WP_Image_Editor_Imagick" <?php selected( $value, 'WP_Image_Editor_Imagick' ); ?>> 457 <?php 458 if ( $imagick_available ) { 459 esc_html_e( 'ImageMagick (Recommended)', '365i-queue-optimizer' ); 460 } else { 461 esc_html_e( 'ImageMagick (Not Available)', '365i-queue-optimizer' ); 462 } 463 ?> 464 </option> 465 <option value="WP_Image_Editor_GD" <?php selected( $value, 'WP_Image_Editor_GD' ); ?>> 466 <?php 467 if ( $gd_available ) { 468 esc_html_e( 'GD Library', '365i-queue-optimizer' ); 469 } else { 470 esc_html_e( 'GD Library (Not Available)', '365i-queue-optimizer' ); 471 } 472 ?> 473 </option> 474 </select> 483 <div class="qo-field-with-help"> 484 <select id="queue_optimizer_image_engine" name="queue_optimizer_image_engine"> 485 <option value="WP_Image_Editor_Imagick" <?php selected( $value, 'WP_Image_Editor_Imagick' ); ?>> 486 <?php 487 if ( $imagick_available ) { 488 esc_html_e( 'ImageMagick (Recommended)', '365i-queue-optimizer' ); 489 } else { 490 esc_html_e( 'ImageMagick (Not Available)', '365i-queue-optimizer' ); 491 } 492 ?> 493 </option> 494 <option value="WP_Image_Editor_GD" <?php selected( $value, 'WP_Image_Editor_GD' ); ?>> 495 <?php 496 if ( $gd_available ) { 497 esc_html_e( 'GD Library', '365i-queue-optimizer' ); 498 } else { 499 esc_html_e( 'GD Library (Not Available)', '365i-queue-optimizer' ); 500 } 501 ?> 502 </option> 503 </select> 504 <?php $this->render_help_trigger( 'image_engine' ); ?> 505 </div> 475 506 <?php if ( ! $imagick_available && 'WP_Image_Editor_Imagick' === $value ) : ?> 476 507 <span class="qo-warning"> … … 562 593 ); 563 594 ?> 564 <select id="queue_optimizer_server_type_override" name="queue_optimizer_server_type_override"> 565 <?php foreach ( $types as $type_value => $type_label ) : ?> 566 <option value="<?php echo esc_attr( $type_value ); ?>" <?php selected( $value, $type_value ); ?>> 567 <?php echo esc_html( $type_label ); ?> 568 </option> 569 <?php endforeach; ?> 570 </select> 595 <div class="qo-field-with-help"> 596 <select id="queue_optimizer_server_type_override" name="queue_optimizer_server_type_override"> 597 <?php foreach ( $types as $type_value => $type_label ) : ?> 598 <option value="<?php echo esc_attr( $type_value ); ?>" <?php selected( $value, $type_value ); ?>> 599 <?php echo esc_html( $type_label ); ?> 600 </option> 601 <?php endforeach; ?> 602 </select> 603 <?php $this->render_help_trigger( 'server_type' ); ?> 604 </div> 571 605 <span class="description"> 572 <?php esc_html_e( ' Override auto-detection if recommendations don\'t match your server.', '365i-queue-optimizer' ); ?>606 <?php esc_html_e( 'Changing this will auto-apply recommended settings.', '365i-queue-optimizer' ); ?> 573 607 </span> 574 608 <?php -
365i-queue-optimizer/trunk/assets/css/admin.css
r3436746 r3443619 409 409 } 410 410 } 411 412 /* ========================================================================== 413 HELP POPOVERS 414 ========================================================================== */ 415 416 /* Help trigger button */ 417 .qo-help-trigger { 418 display: inline-flex; 419 align-items: center; 420 justify-content: center; 421 width: 18px; 422 height: 18px; 423 margin-left: 6px; 424 padding: 0; 425 background: transparent; 426 border: 1.5px solid #8c8f94; 427 border-radius: 50%; 428 color: #8c8f94; 429 font-size: 11px; 430 font-weight: 600; 431 font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; 432 line-height: 1; 433 cursor: pointer; 434 transition: all 0.15s ease; 435 vertical-align: middle; 436 position: relative; 437 } 438 439 .qo-help-trigger:hover, 440 .qo-help-trigger:focus { 441 background: #2271b1; 442 border-color: #2271b1; 443 color: #fff; 444 outline: none; 445 } 446 447 .qo-help-trigger:focus { 448 box-shadow: 0 0 0 2px #fff, 0 0 0 4px #2271b1; 449 } 450 451 .qo-help-trigger[aria-expanded="true"] { 452 background: #1d2327; 453 border-color: #1d2327; 454 color: #fff; 455 } 456 457 /* Help popover container */ 458 .qo-help-popover { 459 position: absolute; 460 z-index: 100000; 461 width: 320px; 462 max-width: calc(100vw - 40px); 463 background: #fff; 464 border-radius: 6px; 465 box-shadow: 466 0 0 0 1px rgba(0, 0, 0, 0.04), 467 0 4px 8px rgba(0, 0, 0, 0.08), 468 0 12px 32px rgba(0, 0, 0, 0.12); 469 opacity: 0; 470 visibility: hidden; 471 transform: translateY(-8px); 472 transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); 473 } 474 475 .qo-help-popover.qo-popover-visible { 476 opacity: 1; 477 visibility: visible; 478 transform: translateY(0); 479 } 480 481 /* Popover arrow */ 482 .qo-help-popover::before { 483 content: ""; 484 position: absolute; 485 top: -6px; 486 left: 24px; 487 width: 12px; 488 height: 12px; 489 background: #fff; 490 border-top: 1px solid rgba(0, 0, 0, 0.08); 491 border-left: 1px solid rgba(0, 0, 0, 0.08); 492 transform: rotate(45deg); 493 border-radius: 2px 0 0 0; 494 } 495 496 /* Popover header */ 497 .qo-popover-header { 498 display: flex; 499 align-items: center; 500 justify-content: space-between; 501 padding: 12px 16px; 502 background: linear-gradient(to bottom, #f8f9fa 0%, #f0f0f1 100%); 503 border-bottom: 1px solid #e1e1e3; 504 border-radius: 6px 6px 0 0; 505 } 506 507 .qo-popover-title { 508 margin: 0; 509 font-size: 13px; 510 font-weight: 600; 511 color: #1d2327; 512 line-height: 1.4; 513 } 514 515 .qo-popover-close { 516 display: flex; 517 align-items: center; 518 justify-content: center; 519 width: 24px; 520 height: 24px; 521 padding: 0; 522 background: transparent; 523 border: none; 524 border-radius: 4px; 525 color: #646970; 526 font-size: 18px; 527 line-height: 1; 528 cursor: pointer; 529 transition: all 0.1s ease; 530 } 531 532 .qo-popover-close:hover { 533 background: #dcdcde; 534 color: #1d2327; 535 } 536 537 .qo-popover-close:focus { 538 outline: none; 539 box-shadow: 0 0 0 2px #2271b1; 540 } 541 542 /* Popover content */ 543 .qo-popover-content { 544 padding: 16px; 545 } 546 547 .qo-popover-content p { 548 margin: 0 0 12px 0; 549 font-size: 13px; 550 line-height: 1.6; 551 color: #3c434a; 552 } 553 554 .qo-popover-content p:last-child { 555 margin-bottom: 0; 556 } 557 558 .qo-popover-content strong { 559 color: #1d2327; 560 } 561 562 .qo-popover-content code { 563 background: #f0f0f1; 564 padding: 2px 6px; 565 border-radius: 3px; 566 font-size: 12px; 567 color: #1d2327; 568 } 569 570 /* Recommended values list */ 571 .qo-popover-recommendations { 572 margin: 12px 0 0 0; 573 padding: 12px; 574 background: #f8f9fa; 575 border-radius: 4px; 576 border-left: 3px solid #2271b1; 577 } 578 579 .qo-popover-recommendations strong { 580 display: block; 581 font-size: 11px; 582 text-transform: uppercase; 583 letter-spacing: 0.5px; 584 color: #646970; 585 margin-bottom: 8px; 586 } 587 588 .qo-popover-recommendations ul { 589 margin: 0; 590 padding: 0; 591 list-style: none; 592 } 593 594 .qo-popover-recommendations li { 595 display: flex; 596 justify-content: space-between; 597 padding: 3px 0; 598 font-size: 12px; 599 color: #3c434a; 600 } 601 602 .qo-popover-recommendations li span:last-child { 603 font-weight: 600; 604 color: #1d2327; 605 } 606 607 /* Tips/notes styling */ 608 .qo-popover-tip { 609 margin-top: 12px; 610 padding: 10px 12px; 611 background: #fff8e5; 612 border-radius: 4px; 613 font-size: 12px; 614 color: #996800; 615 } 616 617 .qo-popover-tip::before { 618 content: "💡"; 619 margin-right: 6px; 620 } 621 622 /* Mobile responsive */ 623 @media screen and (max-width: 782px) { 624 .qo-help-popover { 625 position: fixed; 626 left: 10px !important; 627 right: 10px !important; 628 top: auto !important; 629 bottom: 20px !important; 630 width: auto; 631 max-width: none; 632 transform: translateY(20px); 633 } 634 635 .qo-help-popover.qo-popover-visible { 636 transform: translateY(0); 637 } 638 639 .qo-help-popover::before { 640 display: none; 641 } 642 643 .qo-popover-content { 644 max-height: 50vh; 645 overflow-y: auto; 646 } 647 } 648 649 /* Field wrapper for positioning */ 650 .qo-field-with-help { 651 position: relative; 652 display: inline-flex; 653 align-items: center; 654 } -
365i-queue-optimizer/trunk/assets/js/admin.js
r3443609 r3443619 17 17 initApplyRecommended(); 18 18 initRunQueue(); 19 initHelp Text();19 initHelpPopovers(); 20 20 initSaveNotification(); 21 21 } … … 203 203 204 204 /** 205 * Initialize help text interactions 206 */ 207 function initHelpText() { 208 $('.description').each(function() { 209 $(this).attr('title', $(this).text().trim()); 210 }); 205 * Initialize help popovers 206 */ 207 function initHelpPopovers() { 208 var $activePopover = null; 209 210 // Close popover function 211 function closePopover() { 212 if ($activePopover) { 213 $activePopover.removeClass('qo-popover-visible'); 214 $activePopover.prev('.qo-help-trigger').attr('aria-expanded', 'false'); 215 setTimeout(function() { 216 $activePopover.remove(); 217 $activePopover = null; 218 }, 200); 219 } 220 } 221 222 // Handle help trigger clicks 223 $(document).on('click', '.qo-help-trigger', function(e) { 224 e.preventDefault(); 225 e.stopPropagation(); 226 227 var $trigger = $(this); 228 var helpId = $trigger.data('help'); 229 230 // If clicking same trigger, close it 231 if ($activePopover && $trigger.attr('aria-expanded') === 'true') { 232 closePopover(); 233 return; 234 } 235 236 // Close any existing popover 237 closePopover(); 238 239 // Get help content 240 var helpContent = getHelpContent(helpId); 241 if (!helpContent) { 242 return; 243 } 244 245 // Create popover 246 var $popover = $( 247 '<div class="qo-help-popover" role="dialog" aria-modal="true">' + 248 '<div class="qo-popover-header">' + 249 '<h4 class="qo-popover-title">' + helpContent.title + '</h4>' + 250 '<button type="button" class="qo-popover-close" aria-label="Close">×</button>' + 251 '</div>' + 252 '<div class="qo-popover-content">' + helpContent.content + '</div>' + 253 '</div>' 254 ); 255 256 // Position popover 257 $trigger.after($popover); 258 $trigger.attr('aria-expanded', 'true'); 259 260 // Calculate position 261 var triggerOffset = $trigger.offset(); 262 var triggerHeight = $trigger.outerHeight(); 263 264 $popover.css({ 265 position: 'absolute', 266 top: triggerHeight + 8, 267 left: 0 268 }); 269 270 // Show with animation 271 setTimeout(function() { 272 $popover.addClass('qo-popover-visible'); 273 }, 10); 274 275 $activePopover = $popover; 276 277 // Focus close button for accessibility 278 $popover.find('.qo-popover-close').focus(); 279 }); 280 281 // Close button click 282 $(document).on('click', '.qo-popover-close', function(e) { 283 e.preventDefault(); 284 closePopover(); 285 }); 286 287 // Close on outside click 288 $(document).on('click', function(e) { 289 if ($activePopover && !$(e.target).closest('.qo-help-popover, .qo-help-trigger').length) { 290 closePopover(); 291 } 292 }); 293 294 // Close on Escape key 295 $(document).on('keydown', function(e) { 296 if (e.key === 'Escape' && $activePopover) { 297 closePopover(); 298 } 299 }); 300 } 301 302 /** 303 * Get help content for a specific field 304 */ 305 function getHelpContent(helpId) { 306 var helpData = { 307 'time_limit': { 308 title: 'Time Limit', 309 content: '<p>Controls how long ActionScheduler is allowed to process tasks in a single run before stopping.</p>' + 310 '<p><strong>Higher values</strong> = more tasks processed per run, but longer server load.</p>' + 311 '<p><strong>Lower values</strong> = shorter processing bursts, better for shared hosting.</p>' + 312 '<div class="qo-popover-recommendations">' + 313 '<strong>Recommended by Server Type</strong>' + 314 '<ul>' + 315 '<li><span>Shared Hosting</span><span>30 seconds</span></li>' + 316 '<li><span>VPS / Managed</span><span>45 seconds</span></li>' + 317 '<li><span>Dedicated</span><span>60 seconds</span></li>' + 318 '</ul>' + 319 '</div>' 320 }, 321 'concurrent_batches': { 322 title: 'Concurrent Batches', 323 content: '<p>The number of simultaneous queue runners that can process actions at the same time.</p>' + 324 '<p><strong>Higher values</strong> = faster processing but more server resources used.</p>' + 325 '<p><strong>Lower values</strong> = slower processing but gentler on the server.</p>' + 326 '<div class="qo-popover-recommendations">' + 327 '<strong>Recommended by Server Type</strong>' + 328 '<ul>' + 329 '<li><span>Shared Hosting</span><span>1 batch</span></li>' + 330 '<li><span>VPS / Managed</span><span>2 batches</span></li>' + 331 '<li><span>Dedicated</span><span>4 batches</span></li>' + 332 '</ul>' + 333 '</div>' + 334 '<div class="qo-popover-tip">Start low and increase gradually while monitoring server performance.</div>' 335 }, 336 'batch_size': { 337 title: 'Batch Size', 338 content: '<p>The maximum number of actions that can be claimed and processed in each batch.</p>' + 339 '<p><strong>Higher values</strong> = more actions processed per batch, faster overall.</p>' + 340 '<p><strong>Lower values</strong> = smaller batches, reduces memory usage and timeout risk.</p>' + 341 '<div class="qo-popover-recommendations">' + 342 '<strong>Recommended by Server Type</strong>' + 343 '<ul>' + 344 '<li><span>Shared Hosting</span><span>25 actions</span></li>' + 345 '<li><span>VPS / Managed</span><span>35 actions</span></li>' + 346 '<li><span>Dedicated</span><span>50 actions</span></li>' + 347 '</ul>' + 348 '</div>' 349 }, 350 'retention_days': { 351 title: 'Data Retention', 352 content: '<p>How many days to keep completed action logs in the database before they are automatically deleted.</p>' + 353 '<p><strong>Higher values</strong> = longer history for debugging, but larger database.</p>' + 354 '<p><strong>Lower values</strong> = smaller database, faster queries.</p>' + 355 '<div class="qo-popover-recommendations">' + 356 '<strong>Recommended by Server Type</strong>' + 357 '<ul>' + 358 '<li><span>Shared Hosting</span><span>3 days</span></li>' + 359 '<li><span>VPS / Managed</span><span>5 days</span></li>' + 360 '<li><span>Dedicated</span><span>7 days</span></li>' + 361 '</ul>' + 362 '</div>' + 363 '<div class="qo-popover-tip">Shorter retention keeps your database lean and improves performance.</div>' 364 }, 365 'image_engine': { 366 title: 'Image Processing Engine', 367 content: '<p>Choose which PHP image library WordPress should prioritize for image processing.</p>' + 368 '<p><strong>ImageMagick</strong> (Recommended)</p>' + 369 '<ul style="margin: 8px 0 8px 20px; font-size: 12px;">' + 370 '<li>Better quality for resizing and compression</li>' + 371 '<li>More memory efficient for large images</li>' + 372 '<li>Better color profile handling</li>' + 373 '<li>Supports more image formats</li>' + 374 '</ul>' + 375 '<p><strong>GD Library</strong></p>' + 376 '<ul style="margin: 8px 0 8px 20px; font-size: 12px;">' + 377 '<li>More widely available on shared hosting</li>' + 378 '<li>Simpler, fewer dependencies</li>' + 379 '<li>May be faster for simple operations</li>' + 380 '</ul>' + 381 '<div class="qo-popover-tip">If ImageMagick is available, use it. Only switch to GD if you experience issues.</div>' 382 }, 383 'server_type': { 384 title: 'Server Type', 385 content: '<p>Select your hosting environment to get appropriate recommended settings.</p>' + 386 '<p><strong>Auto-detect</strong> analyzes your PHP memory limit and execution time to guess your server type.</p>' + 387 '<p><strong>Shared Hosting</strong> - Budget hosting with limited resources (e.g., Bluehost, SiteGround shared plans)</p>' + 388 '<p><strong>VPS / Managed</strong> - Virtual private server or managed WordPress hosting (e.g., Cloudways, Kinsta)</p>' + 389 '<p><strong>Dedicated</strong> - High-performance dedicated server or enterprise hosting</p>' + 390 '<div class="qo-popover-tip">If auto-detect gets it wrong, manually select your actual hosting type for better recommendations.</div>' 391 } 392 }; 393 394 return helpData[helpId] || null; 211 395 } 212 396 -
365i-queue-optimizer/trunk/readme.txt
r3443609 r3443619 4 4 Requires at least: 5.8 5 5 Tested up to: 6.9 6 Stable tag: 1. 6.06 Stable tag: 1.7.0 7 7 Requires PHP: 8.0 8 8 License: GPLv2 or later … … 191 191 192 192 == Changelog == 193 194 = 1.7.0 - 2025-01-20 = 195 196 **Inline Help Documentation** 197 198 * Added help popovers with detailed explanations for each setting 199 * Click the (?) icon next to any field to see what it does and recommended values 200 * Help content includes server-specific recommendations and tips 201 * Fully accessible with keyboard navigation and screen reader support 202 * Mobile-friendly design that works on all screen sizes 193 203 194 204 = 1.6.0 - 2025-01-20 =
Note: See TracChangeset
for help on using the changeset viewer.