From 8891b3743f94fe298b6a6896b2518b9ef10b0a7c Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 22 Feb 2026 10:05:30 +0000 Subject: [PATCH] =?UTF-8?q?Dateien=20nach=20=E2=80=9Ebavarian-rank-engine/?= =?UTF-8?q?assets=E2=80=9C=20hochladen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bavarian-rank-engine/assets/admin.css | 10 + bavarian-rank-engine/assets/admin.js | 48 +++++ bavarian-rank-engine/assets/bulk.js | 221 +++++++++++++++++++++ bavarian-rank-engine/assets/editor-meta.js | 32 +++ bavarian-rank-engine/assets/seo-widget.js | 102 ++++++++++ 5 files changed, 413 insertions(+) create mode 100644 bavarian-rank-engine/assets/admin.css create mode 100644 bavarian-rank-engine/assets/admin.js create mode 100644 bavarian-rank-engine/assets/bulk.js create mode 100644 bavarian-rank-engine/assets/editor-meta.js create mode 100644 bavarian-rank-engine/assets/seo-widget.js diff --git a/bavarian-rank-engine/assets/admin.css b/bavarian-rank-engine/assets/admin.css new file mode 100644 index 0000000..23fabcc --- /dev/null +++ b/bavarian-rank-engine/assets/admin.css @@ -0,0 +1,10 @@ +.bre-settings h2 { + border-bottom: 1px solid #ddd; + padding-bottom: 5px; + margin-top: 30px; +} +.bre-provider-row { display: none; } +.bre-provider-row.active { display: table-row; } +.bre-test-result { margin-left: 10px; font-weight: bold; } +.bre-test-result.success { color: #46b450; } +.bre-test-result.error { color: #dc3232; } diff --git a/bavarian-rank-engine/assets/admin.js b/bavarian-rank-engine/assets/admin.js new file mode 100644 index 0000000..f806b68 --- /dev/null +++ b/bavarian-rank-engine/assets/admin.js @@ -0,0 +1,48 @@ +/* global breAdmin */ +jQuery( function ( $ ) { + function updateProviderRows() { + var active = $( '#bre-provider' ).val(); + $( '.bre-provider-row' ).removeClass( 'active' ); + $( '.bre-provider-row[data-provider="' + active + '"]' ).addClass( 'active' ); + } + updateProviderRows(); + $( '#bre-provider' ).on( 'change', updateProviderRows ); + + $( document ).on( 'click', '.bre-test-btn', function () { + var btn = $( this ); + var providerId = btn.data( 'provider' ); + var resultEl = $( '#test-result-' + providerId ); + + resultEl.removeClass( 'success error' ).text( 'Teste\u2026' ); + btn.prop( 'disabled', true ); + + $.post( breAdmin.ajaxUrl, { + action: 'bre_test_connection', + nonce: breAdmin.nonce, + provider: providerId, + // api_key removed — server reads stored encrypted key + } ).done( function ( res ) { + if ( res.success ) { + resultEl.addClass( 'success' ).text( '\u2713 ' + res.data ); + } else { + resultEl.addClass( 'error' ).text( '\u2717 ' + res.data ); + } + } ).fail( function () { + resultEl.addClass( 'error' ).text( '\u2717 Netzwerkfehler' ); + } ).always( function () { + btn.prop( 'disabled', false ); + } ); + } ); + + $( '#bre-reset-prompt' ).on( 'click', function () { + if ( ! confirm( 'Prompt wirklich zur\u00fccksetzen?' ) ) return; + $.post( breAdmin.ajaxUrl, { + action: 'bre_get_default_prompt', + nonce: breAdmin.nonce, + } ).done( function ( res ) { + if ( res.success ) { + $( 'textarea[name*="prompt"]' ).val( res.data ); + } + } ); + } ); +} ); diff --git a/bavarian-rank-engine/assets/bulk.js b/bavarian-rank-engine/assets/bulk.js new file mode 100644 index 0000000..523f13b --- /dev/null +++ b/bavarian-rank-engine/assets/bulk.js @@ -0,0 +1,221 @@ +/* global breBulk */ +jQuery( function ( $ ) { + var running = false; + var stopFlag = false; + var processed = 0; + var total = 0; + var failedItems = []; + + if ( breBulk.isLocked ) { + showLockWarning( breBulk.lockAge ); + } + + loadStats(); + + function showLockWarning( age ) { + var msg = 'Ein Bulk-Prozess läuft bereits' + ( age ? ' (seit ' + age + 's)' : '' ) + '.'; + $( '#bre-lock-warning' ).text( msg ).show(); + $( '#bre-bulk-start' ).prop( 'disabled', true ); + } + + function hideLockWarning() { + $( '#bre-lock-warning' ).hide(); + $( '#bre-bulk-start' ).prop( 'disabled', false ); + } + + function loadStats() { + $.post( breBulk.ajaxUrl, { action: 'bre_bulk_stats', nonce: breBulk.nonce } ) + .done( function ( res ) { + if ( ! res.success ) return; + var html = 'Posts ohne Meta-Beschreibung:Gesamt: ' + t + ''; + total = t; + $( '#bre-bulk-stats' ).html( html ); + updateCostEstimate(); + } ); + } + + $( '#bre-bulk-limit, #bre-bulk-model, #bre-bulk-provider' ).on( 'change', updateCostEstimate ); + + function updateCostEstimate() { + var limit = parseInt( $( '#bre-bulk-limit' ).val(), 10 ) || 20; + var inputTokens = limit * 800; + var outputTokens = limit * 50; + var costHtml = '~' + inputTokens + ' Input-Token + ' + outputTokens + ' Output-Token'; + + var costData = breBulk.costs || {}; + var provider = $( '#bre-bulk-provider' ).val(); + var model = $( '#bre-bulk-model' ).val(); + + if ( costData[ provider ] && costData[ provider ][ model ] ) { + var c = costData[ provider ][ model ]; + var inCost = ( inputTokens / 1000000 ) * parseFloat( c.input || 0 ); + var outCost= ( outputTokens / 1000000 ) * parseFloat( c.output || 0 ); + var total = inCost + outCost; + if ( total > 0 ) { + costHtml += ' ≈ $' + total.toFixed( 4 ); + } + } + $( '#bre-cost-estimate' ).text( costHtml ); + } + + $( '#bre-bulk-start' ).on( 'click', function () { + if ( running ) return; + $.post( breBulk.ajaxUrl, { action: 'bre_bulk_status', nonce: breBulk.nonce } ) + .done( function ( res ) { + if ( res.success && res.data.locked ) { + showLockWarning( res.data.lock_age ); + return; + } + startRun(); + } ); + } ); + + function startRun() { + running = true; + stopFlag = false; + processed = 0; + failedItems = []; + + $( '#bre-bulk-start' ).prop( 'disabled', true ); + $( '#bre-bulk-stop' ).show(); + $( '#bre-progress-wrap' ).show(); + $( '#bre-bulk-log' ).show().html( '' ); + $( '#bre-failed-summary' ).hide().html( '' ); + hideLockWarning(); + + var limit = parseInt( $( '#bre-bulk-limit' ).val(), 10 ) || 20; + var provider = $( '#bre-bulk-provider' ).val(); + var model = $( '#bre-bulk-model' ).val(); + + log( '▶ Start — max ' + limit + ' Posts, Provider: ' + provider ); + runBatch( 'post', limit, provider, model, true ); + } + + $( '#bre-bulk-stop' ).on( 'click', function () { + stopFlag = true; + log( '⚠ Abbruch angefordert…', 'warn' ); + releaseLock(); + } ); + + function releaseLock() { + $.post( breBulk.ajaxUrl, { action: 'bre_bulk_release', nonce: breBulk.nonce } ); + } + + function runBatch( postType, remaining, provider, model, isFirst ) { + if ( stopFlag || remaining <= 0 ) { + finish(); + return; + } + + var batchSize = Math.min( 20, remaining ); + var isLast = ( remaining - batchSize ) <= 0; + + log( '↻ Verarbeite ' + batchSize + ' Posts… (' + remaining + ' verbleibend)' ); + + $.post( breBulk.ajaxUrl, { + action: 'bre_bulk_generate', + nonce: breBulk.nonce, + post_type: postType, + batch_size: batchSize, + provider: provider, + model: model, + is_first: isFirst ? 1 : 0, + is_last: isLast ? 1 : 0, + } ).done( function ( res ) { + if ( ! res.success ) { + if ( res.data && res.data.locked ) { + showLockWarning( res.data.lock_age ); + finish(); + return; + } + log( '✗ Fehler: ' + $( '' ).text( ( res.data && res.data.message ) || 'Unbekannter Fehler' ).html(), 'error' ); + finish(); + return; + } + + $.each( res.data.results, function ( i, item ) { + if ( item.success ) { + var note = item.attempts > 1 ? ' (Versuch ' + item.attempts + ')' : ''; + log( + '✓ [' + item.id + '] ' + + $( '' ).text( item.title ).html() + note + + '
' + + $( '' ).text( item.description ).html() + + '' + ); + } else { + failedItems.push( item ); + log( + '✗ [' + item.id + '] ' + + $( '' ).text( item.title ).html() + + ' — ' + $( '' ).text( item.error ).html(), + 'error' + ); + } + processed++; + } ); + + updateProgress( processed, total ); + + var newRemaining = remaining - batchSize; + if ( res.data.remaining > 0 && ! stopFlag && newRemaining > 0 ) { + setTimeout( function () { + runBatch( postType, newRemaining, provider, model, false ); + }, breBulk.rateDelay ); + } else { + if ( isLast || res.data.remaining === 0 ) releaseLock(); + finish(); + } + } ).fail( function () { + log( '✗ Netzwerkfehler', 'error' ); + releaseLock(); + finish(); + } ); + } + + function updateProgress( done, t ) { + var pct = t > 0 ? Math.round( ( done / t ) * 100 ) : 100; + $( '#bre-progress-bar' ).css( 'width', pct + '%' ); + $( '#bre-progress-text' ).text( done + ' / ' + t + ' verarbeitet' ); + } + + /** + * Append a line to the log console. + * @param {string} msg Pre-escaped HTML string. User data MUST be escaped via + * $('').text(val).html() before passing here. + * @param {string} type 'error' | 'warn' | undefined + */ + function log( msg, type ) { + var color = type === 'error' ? '#f48771' : type === 'warn' ? '#dcdcaa' : '#9cdcfe'; + $( '#bre-bulk-log' ).append( + '
' + msg + '
' + ); + var el = document.getElementById( 'bre-bulk-log' ); + el.scrollTop = el.scrollHeight; + } + + function finish() { + running = false; + $( '#bre-bulk-start' ).prop( 'disabled', false ); + $( '#bre-bulk-stop' ).hide(); + log( '— Fertig —' ); + + if ( failedItems.length > 0 ) { + var html = '⚠ ' + failedItems.length + ' Posts fehlgeschlagen:
    '; + $.each( failedItems, function ( i, item ) { + html += '
  • [' + item.id + '] ' + + $( '' ).text( item.title ).html() + + ': ' + $( '' ).text( item.error ).html() + '
  • '; + } ); + html += '
'; + $( '#bre-failed-summary' ).html( html ).show(); + } + loadStats(); + } +} ); diff --git a/bavarian-rank-engine/assets/editor-meta.js b/bavarian-rank-engine/assets/editor-meta.js new file mode 100644 index 0000000..5da315c --- /dev/null +++ b/bavarian-rank-engine/assets/editor-meta.js @@ -0,0 +1,32 @@ +/* global jQuery, ajaxurl */ +jQuery( function ( $ ) { + var $textarea = $( '#bre-meta-description' ); + var $count = $( '#bre-meta-count' ); + var $btn = $( '#bre-regen-meta' ); + + if ( ! $textarea.length ) return; + + $textarea.on( 'input', function () { + $count.text( $( this ).val().length + ' / 160' ); + } ); + + if ( ! $btn.length ) return; + + $btn.on( 'click', function () { + $btn.prop( 'disabled', true ).text( '…' ); + $.post( ajaxurl, { + action: 'bre_regen_meta', + nonce: $btn.data( 'nonce' ), + post_id: $btn.data( 'post-id' ), + } ).done( function ( res ) { + if ( res.success ) { + $textarea.val( res.data.description ); + $count.text( res.data.description.length + ' / 160' ); + } else { + alert( 'Fehler: ' + ( res.data || 'Unbekannt' ) ); + } + } ).always( function () { + $btn.prop( 'disabled', false ).text( 'Mit KI neu generieren' ); + } ); + } ); +} ); diff --git a/bavarian-rank-engine/assets/seo-widget.js b/bavarian-rank-engine/assets/seo-widget.js new file mode 100644 index 0000000..78da5c4 --- /dev/null +++ b/bavarian-rank-engine/assets/seo-widget.js @@ -0,0 +1,102 @@ +/* global jQuery, wp */ +jQuery( function ( $ ) { + var $widget = $( '#bre-seo-widget' ); + if ( ! $widget.length ) return; + + var siteUrl = $widget.data( 'site-url' ) || window.location.origin; + var debounce = null; + + function getContent() { + // Block editor + if ( window.wp && wp.data && wp.data.select( 'core/editor' ) ) { + try { + var blocks = wp.data.select( 'core/editor' ).getBlocks(); + return blocks.map( function ( b ) { + return ( b.attributes && b.attributes.content ) ? b.attributes.content : ''; + } ).join( ' ' ); + } catch ( e ) { return ''; } + } + // Classic editor (TinyMCE or textarea) + if ( typeof tinyMCE !== 'undefined' && tinyMCE.activeEditor && ! tinyMCE.activeEditor.isHidden() ) { + return tinyMCE.activeEditor.getContent(); + } + return $( '#content' ).val() || ''; + } + + function getTitle() { + if ( window.wp && wp.data && wp.data.select( 'core/editor' ) ) { + try { + return wp.data.select( 'core/editor' ).getEditedPostAttribute( 'title' ) || ''; + } catch ( e ) { return ''; } + } + return $( '#title' ).val() || ''; + } + + function analyse() { + var content = getContent(); + var title = getTitle(); + var plain = content.replace( /<[^>]+>/g, ' ' ).replace( /\s+/g, ' ' ).trim(); + var words = plain ? plain.split( /\s+/ ).length : 0; + var readMin = Math.max( 1, Math.ceil( words / 200 ) ); + + $( '#bre-title-stat' ).text( title.length + ' / 60' ); + $( '#bre-words-stat' ).text( words.toLocaleString( 'de-DE' ) ); + $( '#bre-read-stat' ).text( '~' + readMin + ' Min.' ); + + // Headings — count from HTML tags + var h = { h1: 0, h2: 0, h3: 0, h4: 0 }; + ( content.match( /]/gi ) || [] ).forEach( function ( tag ) { + var level = 'h' + tag.replace( / 0 ) hParts.push( h[ tag ] + '× ' + tag.toUpperCase() ); + } ); + $( '#bre-headings-stat' ).text( hParts.length ? hParts.join( ' ' ) : 'Keine' ); + + // Links + var allLinks = content.match( /href="([^"]+)"/gi ) || []; + var siteHost = siteUrl.replace( /https?:\/\//, '' ).replace( /\/$/, '' ); + var internal = 0; + var external = 0; + + allLinks.forEach( function ( tag ) { + var href = ( tag.match( /href="([^"]+)"/ ) || [] )[1] || ''; + if ( href.indexOf( '/' ) === 0 || href.indexOf( siteUrl ) === 0 || href.indexOf( siteHost ) !== -1 ) { + internal++; + } else if ( /^https?:\/\//.test( href ) ) { + external++; + } + } ); + + $( '#bre-links-stat' ).text( internal + ' intern ' + external + ' extern' ); + + // Warnings + var warnings = []; + if ( h.h1 === 0 ) warnings.push( '⚠ Keine H1-Überschrift' ); + if ( h.h1 > 1 ) warnings.push( '⚠ Mehrere H1-Überschriften (' + h.h1 + ')' ); + if ( internal === 0 && words > 50 ) warnings.push( '⚠ Keine internen Links' ); + $( '#bre-seo-warnings' ).html( warnings.join( '
' ) ); + } + + function scheduledAnalyse() { + clearTimeout( debounce ); + debounce = setTimeout( analyse, 500 ); + } + + // Block editor + if ( window.wp && wp.data ) { + wp.data.subscribe( scheduledAnalyse ); + } + + // Classic editor + $( document ).on( 'input change', '#content', scheduledAnalyse ); + $( document ).on( 'tinymce-editor-init', function ( event, editor ) { + editor.on( 'KeyUp Change SetContent', scheduledAnalyse ); + } ); + $( '#title' ).on( 'input', scheduledAnalyse ); + + analyse(); +} );