Changeset 3446048
- Timestamp:
- 01/24/2026 09:59:43 AM (2 months ago)
- Location:
- technodrome-ai-content-assistant/trunk
- Files:
-
- 33 edited
-
CHANGELOG.md (modified) (1 diff)
-
changelog.txt (modified) (1 diff)
-
dashboard/modules/footer/footer.php (modified) (2 diffs)
-
features/content-rules-tab/profile-name-sync.js (modified) (6 diffs)
-
features/content-rules-tab/websources-input.js (modified) (23 diffs)
-
features/dashboard.js (modified) (12 diffs)
-
features/extras-tab/bulk-generator.js (modified) (11 diffs)
-
features/extras-tab/schedule-picker.js (modified) (4 diffs)
-
features/footer/api-status.js (modified) (8 diffs)
-
features/footer/credit-toggle.js (modified) (3 diffs)
-
features/footer/dark-mode-toggle.js (modified) (3 diffs)
-
features/footer/profile-buttons.js (modified) (28 diffs)
-
features/footer/publish-toggle.js (modified) (3 diffs)
-
features/footer/save-button.js (modified) (7 diffs)
-
features/generate-tab/add-content-title.js (modified) (3 diffs)
-
features/generate-tab/ai-image-toggle.js (modified) (3 diffs)
-
features/generate-tab/ai-provider-select.js (modified) (15 diffs)
-
features/generate-tab/content-type.js (modified) (3 diffs)
-
features/generate-tab/default-tone.js (modified) (6 diffs)
-
features/generate-tab/generation-mode.js (modified) (3 diffs)
-
features/generate-tab/language-select.js (modified) (4 diffs)
-
features/generate-tab/video-context-mode.js (modified) (7 diffs)
-
features/header/add-licence.js (modified) (2 diffs)
-
features/layout-templates-tab/advanced-template.js (modified) (5 diffs)
-
features/layout-templates-tab/photo-positions.js (modified) (3 diffs)
-
features/layout-templates-tab/template-selector.js (modified) (3 diffs)
-
features/layout-templates-tab/video-manager.js (modified) (10 diffs)
-
features/layout-templates-tab/video-slot-transformation.js (modified) (13 diffs)
-
features/notifications.js (modified) (2 diffs)
-
includes/class-ajax-handler.php (modified) (5 diffs)
-
includes/class-profile-manager.php (modified) (4 diffs)
-
readme.txt (modified) (3 diffs)
-
technodrome-ai-content-assistant.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
technodrome-ai-content-assistant/trunk/CHANGELOG.md
r3434643 r3446048 6 6 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 7 8 ## [4.0.0] - 2026-01-07 8 ## [4.0.4] - 2026-01-22 9 10 ### Added 11 - **Video Slot Renaming**: Slots now show descriptive names in video context modes: 12 - "GENERATE AI CONTENT FROM VIDEO URL" for Slot 1 in AI + Video URL Context mode 13 - "GENERATE AI CONTENT FROM VIDEO CHANNEL" for Slot 2 in AI + Video Channel Context mode 14 - **UNDER CONSTRUCTION Overlay**: Added visual overlay on Slot 2 when in video context modes, indicating that channel mode is not yet implemented 15 - **Mode-Specific Transformation**: Video slot renaming and overlays only apply when in video context modes, keeping regular modes unchanged 16 17 ### Changed 18 - Updated video-slot-transformation.js to handle slot renaming and UNDER CONSTRUCTION overlay 19 - Modified transformation logic to only apply in AI + Video URL Context and AI + Video Channel Context modes 20 - Added visual feedback with yellow/orange gradient UNDER CONSTRUCTION badge 21 22 ### Improved 23 - Better user guidance in video context modes 24 - Clearer distinction between active and locked slots 25 - Consistent UI experience across all modes 26 27 ## [4.0.3] - 2026-01-21 9 28 10 29 ### Added -
technodrome-ai-content-assistant/trunk/changelog.txt
r3444453 r3446048 3 3 ## Version 4.0.3 - 2026-01-21 4 4 5 ### BUG FIXES 6 - **Structure Name Not Persisting:** CRITICAL FIX - Structure Name field now properly saves and loads 7 - structure_name was missing from collectCompleteProfileData() - FIXED (added to line 540, 751) 8 - Now included in content_rules when autosave collects profile data 9 - Structure Name properly persists across profile switches 10 - Fixed 3000ms debounce delay - reduced to standard 500ms for faster saves (line 54-59) 11 - Fixed save_profile() treating update_user_meta false return as error - was blocking save when data identical 5 ### CRITICAL BUG FIXES 6 - **Structure Name Not Persisting + Profile Button Not Updating:** CRITICAL FIX - Structure Name now saves and updates profile button names 7 - Problem: structure_name was collected and saved but profile buttons weren't being updated with the value 8 - Root Cause: updateProfileButtonsStatus() didn't ažurirati button text; switchProfile() wasn't calling it 9 - FIX #1: Enhanced updateProfileButtonsStatus() to extract structure_name from profile and display on button (max 7 chars, uppercase) 10 - FIX #2: Added updateProfileButtonsStatus() call in switchProfile() to update button when profile changes 11 - FIX #3: Fixed 500ms debounce for structure_name changes (was 3000ms) for faster response 12 - FIX #4: Fixed save_profile() to properly handle update_user_meta false return (false = data identical, not an error) 13 - Result: Structure Name now serves as profile nickname - type "SPORT" and button shows "SPORT" instead of "Profile 1" 12 14 13 15 - **Default Tone AutoSave:** Fixed Default Tone field not saving to profile 14 - Added missing AutoSave event listener for default tone changes16 - Added missing DefaultTone sync in loadCompleteProfileData() 15 17 - Default Tone now persists correctly across profile switches 16 - AutoSave notification displays when default tone is changed17 18 ### IMPROVEMENTS19 - Enhanced field change detection in AutoSave system20 - Better profile data persistence for all fields21 - Faster AutoSave debounce timing for Structure Name field (500ms instead of 3000ms)22 18 23 19 --- -
technodrome-ai-content-assistant/trunk/dashboard/modules/footer/footer.php
r3444346 r3446048 78 78 $taics_color = taics_get_profile_color($taics_i); 79 79 $taics_is_active = ($taics_i == $taics_active_profile); 80 // v4.0.2: Get custom profile name from user_meta, fallback to "PROFILE"81 $taics_custom_name = get_user_meta($taics_user_id, 'taics_profile_' . $taics_i . '_name', true);82 $taics_profile_label = !empty($taics_custom_name) ? strtoupper(substr($taics_custom_name, 0, 7)) : 'PROFILE';83 80 /* translators: %d is the index number of the profile button */ 84 $taics_title_text = sprintf(esc_attr__('Profile %d (%s)', 'technodrome-ai-content-assistant'), $taics_i, $taics_profile_label);81 $taics_title_text = sprintf(esc_attr__('Profile %d', 'technodrome-ai-content-assistant'), $taics_i); 85 82 ?> 86 83 <button … … 89 86 data-color="<?php echo esc_attr($taics_color); ?>" 90 87 data-user-plan="<?php echo esc_attr($taics_user_plan); ?>" 91 title="<?php echo esc_attr($taics_title_text); ?>" 92 id="taics-profile-btn-<?php echo esc_attr($taics_i); ?>"> 88 title="<?php echo esc_attr($taics_title_text); ?>"> 93 89 <span class="taics-profile-number taics-profile-number-<?php echo esc_attr($taics_color); ?>"><?php echo esc_html($taics_i); ?></span> 94 <span class="taics-profile-text" id="taics-profile-label-<?php echo esc_attr($taics_i); ?>"><?php echo esc_html($taics_profile_label); ?></span>90 <span class="taics-profile-text">PROFILE</span> 95 91 </button> 96 92 <?php endfor; ?> -
technodrome-ai-content-assistant/trunk/features/content-rules-tab/profile-name-sync.js
r3444453 r3446048 6 6 isSaving: false, 7 7 syncTimeout: null, 8 isLoading: false, // FIX: Use this flag instead of checking non-existent flag 8 9 9 10 init: function() { 10 11 this.bindEvents(); 11 this.loadCurrentProfileName();12 12 }, 13 13 14 14 bindEvents: function() { 15 // Listen for profile changes to load the Structure Name from profile data 15 // DVT v4.0.5.2: Listen for ALL profiles loaded event - updates ALL button labels 16 $(document).off('taics_all_profiles_loaded.profile-name'); 17 $(document).on('taics_all_profiles_loaded.profile-name', (e, profiles) => { 18 this.updateAllProfileButtonLabels(); 19 }); 20 21 // DVT v4.0.5.2: Listen for ALL profile loads (both initial and changes) 22 $(document).off('taics_profile_loaded.taics-all-profiles'); 23 $(document).on('taics_profile_loaded.taics-all-profiles', (e) => { 24 // After any profile load, update ALL profile button labels 25 this.updateAllProfileButtonLabels(); 26 }); 27 28 // Listen for FIRST profile load to load the Structure Name from profile data 16 29 $(document).off('taics_profile_loaded.profile-name'); 17 30 $(document).on('taics_profile_loaded.profile-name', (e, profileNumber, profileData) => { 31 this.isLoading = true; 18 32 this.activeProfile = profileNumber; 19 // Load structure_name from profile data, not from button label20 33 this.loadStructureNameFromProfile(profileData); 34 setTimeout(() => { this.isLoading = false; }, 50); 35 }); 36 37 // DVT v4.0.5.2: Listen for PROFILE CHANGES to load the Structure Name from profile data 38 $(document).off('taics_profile_changed.profile-name'); 39 $(document).on('taics_profile_changed.profile-name', (e, profileNumber) => { 40 this.isLoading = true; 41 this.activeProfile = profileNumber; 42 43 // Get profile data from TAICS_Profile_Buttons 44 let actualProfileData = null; 45 if (window.TAICS_Profile_Buttons && window.TAICS_Profile_Buttons.profiles) { 46 actualProfileData = window.TAICS_Profile_Buttons.profiles[profileNumber]; 47 } 48 49 this.loadStructureNameFromProfile(actualProfileData); 50 setTimeout(() => { this.isLoading = false; }, 50); 21 51 }); 22 52 … … 25 55 $('#taics-structure-name').on('input.profile-name blur.profile-name', () => { 26 56 // Only sync if NOT currently loading profile data 27 // Check flag from profile-buttons.js 28 const isLoading = window.TAICS_Profile_Buttons && window.TAICS_Profile_Buttons.isLoadingProfileData; 29 if (!isLoading) { 57 if (!this.isLoading) { 30 58 this.syncProfileName(); 31 59 } … … 33 61 }, 34 62 63 // DVT v4.0.5.2: Update ALL profile button labels based on their Structure Names 64 updateAllProfileButtonLabels: function() { 65 if (!window.TAICS_Profile_Buttons || !window.TAICS_Profile_Buttons.profiles) { 66 console.log('TAICS: profiles not loaded yet, skipping'); 67 return; 68 } 69 70 const profiles = window.TAICS_Profile_Buttons.profiles; 71 for (const profileNum in profiles) { 72 if (profiles.hasOwnProperty(profileNum)) { 73 const profileData = profiles[profileNum]; 74 const structureName = profileData.content_rules?.structure_name || ''; 75 this.updateButtonLabelForProfile(profileNum, structureName); 76 } 77 } 78 }, 79 80 updateButtonLabelForProfile: function(profileNumber, customName) { 81 // DVT v4.0.5.2: Add defensive check - ensure button element exists 82 const $profileBtn = $(`.taics-profile-btn-wide[data-profile="${profileNumber}"]`); 83 if ($profileBtn.length === 0) { 84 console.log('TAICS: Button for profile ' + profileNumber + ' does not exist yet, skipping label update'); 85 return; 86 } 87 88 let displayLabel = customName.substring(0, 7).toUpperCase(); 89 displayLabel = displayLabel || 'PROFILE'; 90 91 // DVT v4.0.5.2: Direct DOM manipulation to ensure label updates 92 const $profileLabel = $profileBtn.find('.taics-profile-text'); 93 if ($profileLabel.length === 0) { 94 return; 95 } 96 97 // Force text update - using both jQuery and native DOM methods 98 $profileLabel.text(displayLabel); 99 $profileLabel[0].textContent = displayLabel; // Native DOM update 100 101 const titleText = `Profile ${profileNumber} (${displayLabel})`; 102 $profileBtn.attr('title', titleText); 103 $profileBtn[0].title = titleText; // Native DOM update 104 }, 105 35 106 loadStructureNameFromProfile: function(profileData) { 36 // Load structure_name from profile data37 107 if (profileData && profileData.content_rules && profileData.content_rules.structure_name) { 38 // Profile has a custom structure name - use it39 108 const structureName = profileData.content_rules.structure_name; 40 109 $('#taics-structure-name').val(structureName); 41 // Update button label to match loaded name42 110 this.updateButtonLabel(structureName); 43 111 } else { 44 // Profile doesn't have structure name - clear the field45 112 $('#taics-structure-name').val(''); 46 // Restore default button label47 113 this.updateButtonLabel(''); 48 114 } … … 50 116 51 117 updateButtonLabel: function(customName) { 52 // Apply 7-character limit and UPPERCASE conversion53 118 let displayLabel = customName.substring(0, 7).toUpperCase(); 54 // If empty, use "PROFILE" as fallback55 119 displayLabel = displayLabel || 'PROFILE'; 56 120 57 // Update the profile button label58 121 const $profileBtn = $(`.taics-profile-btn-wide[data-profile="${this.activeProfile}"]`); 59 122 const $profileLabel = $profileBtn.find('.taics-profile-text'); 60 123 $profileLabel.text(displayLabel); 61 124 62 // Update the title attribute63 125 const titleText = `Profile ${this.activeProfile} (${displayLabel})`; 64 126 $profileBtn.attr('title', titleText); 65 127 }, 66 128 67 loadCurrentProfileName: function() {68 // This method is no longer used - kept for backward compatibility69 },70 71 129 syncProfileName: function() { 72 // Get the value from Structure Name field73 130 let value = $('#taics-structure-name').val().trim(); 74 75 // Apply 7-character limit and UPPERCASE conversion76 131 value = value.substring(0, 7).toUpperCase(); 77 78 // If empty, use "PROFILE" as fallback79 132 const displayLabel = value || 'PROFILE'; 80 133 81 // Update the profile button label in real-time82 134 const $profileBtn = $(`.taics-profile-btn-wide[data-profile="${this.activeProfile}"]`); 83 135 const $profileLabel = $profileBtn.find('.taics-profile-text'); 84 136 $profileLabel.text(displayLabel); 85 137 86 // Update the title attribute87 138 const titleText = `Profile ${this.activeProfile} (${displayLabel})`; 88 139 $profileBtn.attr('title', titleText); 89 140 90 // Debounce the save to server (wait 1 second after user stops typing)141 // Simple debounced save 91 142 clearTimeout(this.syncTimeout); 92 143 this.syncTimeout = setTimeout(() => { 93 144 this.saveProfileNameToServer(value); 94 }, 1000);145 }, 500); // Reduced delay 95 146 }, 96 147 … … 122 173 success: (response) => { 123 174 if (response.success) { 124 // Show notification125 175 if (window.TAICS_Notifications && typeof window.TAICS_Notifications.show === 'function') { 126 176 const displayName = customName || 'PROFILE'; … … 154 204 155 205 $(document).ready(function() { 156 // Wait 200ms to ensure profile-buttons.js initializes first (it uses 100ms) 157 setTimeout(() => { 158 TAICS_Profile_Name_Sync.init(); 159 window.TAICS_Profile_Name_Sync = TAICS_Profile_Name_Sync; 160 }, 200); 206 TAICS_Profile_Name_Sync.init(); 207 window.TAICS_Profile_Name_Sync = TAICS_Profile_Name_Sync; 161 208 }); 162 209 -
technodrome-ai-content-assistant/trunk/features/content-rules-tab/websources-input.js
r3421276 r3446048 18 18 */ 19 19 init: function(forceInit = false) { 20 console.log('🚀 TAICS_Websources_Input: Initializing...'); 21 22 console.log('🔍 Step 1: Locating DOM elements...'); 20 23 21 24 22 this.$container = $('#taics-web-sources-list'); … … 27 25 28 26 // ENHANCED: Detailed DOM element logging 29 console.log('📦 DOM Elements Check:');30 console.log(' - Container (#taics-web-sources-list):', this.$container.length > 0 ? 'FOUND' : 'MISSING');31 console.log(' - Add Button (#taics-add-source):', this.$addButton.length > 0 ? 'FOUND' : 'MISSING');32 console.log(' - Counter (#taics-sources-counter):', this.$counter.length > 0 ? 'FOUND' : 'MISSING');33 27 34 28 if (!this.$container.length) { 35 console.log('❌ TAICS_Websources_Input: Container not found, aborting');36 29 return; 37 30 } … … 39 32 this.$list = this.$container; 40 33 41 console.log('🔍 Step 3: Setting up initial data...');42 34 43 35 // Load empty slots if list is empty 44 36 if (this.$list.children().length === 0) { 45 console.log('📝 TAICS_Websources_Input: Loading empty slots...');46 37 this.setValue(['', '', '', '', '']); 47 38 } 48 39 49 console.log('🔍 Step 4: Binding events...');50 40 this.bindEvents(); 51 41 52 console.log('🔍 Step 5: Updating UI...');53 42 this.updateCounter(); 54 43 this.updateAllStatusLights(); 55 44 56 console.log('✅ TAICS_Websources_Input: Initialized successfully');57 45 58 46 // Log final state for debugging … … 67 55 logFinalState: function() { 68 56 const itemCount = this.$list ? this.$list.find('.taics-web-source-item').length : 0; 69 console.log('📊 Final State:');70 console.log(' - Total source items:', itemCount);71 console.log(' - Container exists:', !!this.$container);72 console.log(' - Add button exists:', !!this.$addButton);73 console.log(' - Counter exists:', !!this.$counter);74 57 75 58 // Log each source item … … 79 62 const $input = $item.find('.taics-web-source-input'); 80 63 const $light = $item.find('.taics-url-status-light'); 81 console.log( ` Item ${index + 1}:`, {64 console.log('Source item:', index, { 82 65 value: $input.val(), 83 66 status: $light.attr('class'), … … 92 75 */ 93 76 bindEvents: function() { 94 console.log('TAICS_Websources_Input: Binding events...');95 77 96 78 // Add source button 97 79 if (this.$addButton.length) { 98 80 this.$addButton.on('click.taicsSources', this.handleAddSource.bind(this)); 99 console.log('TAICS_Websources_Input: Add source button event bound');100 81 } 101 82 … … 103 84 if ($('#taics-check-sources').length) { 104 85 $('#taics-check-sources').on('click.taicsSources', this.handleCheckAllSources.bind(this)); 105 console.log('TAICS_Websources_Input: Check sources button event bound');106 86 } 107 87 108 88 // Remove source buttons 109 89 $(document).on('click.taicsSources', '.taics-remove-source', this.handleRemoveSource.bind(this)); 110 console.log('TAICS_Websources_Input: Remove source events bound');111 90 112 91 // Input change - update counter AND real-time validation feedback … … 132 111 $(document).trigger('taics_websources_changed.autosave'); 133 112 }.bind(this)); 134 console.log('TAICS_Websources_Input: Input events bound');135 113 136 114 // Enter key to add new source 137 115 $(document).on('keypress.taicsSources', '.taics-web-source-input', this.handleKeyPress.bind(this)); 138 console.log('TAICS_Websources_Input: Keypress events bound');139 116 }, 140 117 … … 144 121 handleAddSource: function(e) { 145 122 e.preventDefault(); 146 console.log('TAICS_Websources_Input: Add source clicked');147 123 148 124 const currentCount = this.getSourcesCount(); 149 125 150 126 if (currentCount >= this.maxSources) { 151 console.log('TAICS_Websources_Input: Maximum sources reached');152 127 if (window.TAICS_Notifications) { 153 128 window.TAICS_Notifications.show('Maximum ' + this.maxSources + ' sources allowed', 'warning'); … … 208 183 handleCheckAllSources: function(e) { 209 184 e.preventDefault(); 210 console.log('🔍 TAICS_Websources_Input: Check sources clicked');211 185 212 186 if (!this.$list || !this.$list.length) { 213 console.log('❌ TAICS_Websources_Input: No sources list found');214 187 this.showNotificationWithFallback('No sources found to check', 'warning'); 215 188 return; … … 234 207 if (url) { 235 208 checkedCount++; 236 console.log(`🔍 TAICS_Websources_Input: Checking URL ${i + 1}/${$sources.length}:`, url);237 209 const isValid = this.performSimpleCheck(url); 238 210 const $light = $item.find('.taics-url-status-light'); … … 241 213 $light.removeClass('status-typing status-empty').addClass('status-valid'); 242 214 validCount++; 243 console.log(`✅ TAICS_Websources_Input: Valid URL ${i + 1} found`);244 215 } else { 245 216 $light.removeClass('status-typing status-valid').addClass('status-empty'); 246 console.log(`❌ TAICS_Websources_Input: Invalid URL ${i + 1} found`);247 217 } 248 218 } … … 252 222 try { 253 223 $checkButton.prop('disabled', false).text('🔍 Check the entered websources'); 254 console.log('✅ TAICS_Websources_Input: Check completed. Valid domains:', validCount, 'out of', checkedCount, 'checked');255 224 256 225 // Enhanced feedback - log the check process 257 console.log(`📊 Check Results Summary:`);258 console.log(` - Total sources found: ${$sources.length}`);259 console.log(` - Sources checked: ${checkedCount}`);260 console.log(` - Valid domains: ${validCount}`);261 console.log(` - Invalid domains: ${checkedCount - validCount}`);262 226 263 227 if (checkedCount === 0) { 264 console.log('🔍 No URLs entered - showing notification');265 228 this.showNotificationWithFallback('No URLs entered to check', 'warning'); 266 229 } else if (validCount > 0) { 267 console.log('✅ Valid domains found - showing success notification');268 230 this.showSaveNotificationSummaryWithFallback(validCount, $sources.length); 269 231 } else { 270 console.log('❌ No valid domains - showing error with suggestions');271 232 this.showNotificationWithFallback(`No valid domains found. Try: wikipedia.org, google.com, bbc.com`, 'warning'); 272 233 } … … 338 299 */ 339 300 performSimpleCheck: function(url) { 340 console.log('🔍 Checking domain:', url);341 301 342 302 // Clean URL (remove http://, https://, www.) … … 345 305 // Basic format validation - must match domain.tld pattern 346 306 if (!cleanDomain.match(/^[a-z0-9.-]+\.[a-z]{2,}$/i)) { 347 console.log('❌ Invalid domain format:', cleanDomain);348 307 return false; 349 308 } … … 352 311 const parts = cleanDomain.split('.'); 353 312 if (parts.length < 2) { 354 console.log('❌ Domain too short:', cleanDomain);355 313 return false; 356 314 } … … 359 317 for (let part of parts) { 360 318 if (part.length === 0 || part.length > 63) { 361 console.log('❌ Invalid part length:', part);362 319 return false; 363 320 } 364 321 if (part.startsWith('-') || part.endsWith('-')) { 365 console.log('❌ Invalid domain part (dash):', part);366 322 return false; 367 323 } 368 324 if (!/^[a-z0-9-]+$/i.test(part)) { 369 console.log('❌ Invalid characters in:', part);370 325 return false; 371 326 } … … 375 330 const tld = parts[parts.length - 1]; 376 331 if (tld.length < 2) { 377 console.log('❌ TLD too short:', tld); 378 return false; 379 } 380 381 console.log('✅ Valid domain:', cleanDomain); 332 return false; 333 } 334 382 335 return true; 383 336 }, … … 539 492 // v3.4.2: Debug logging for profile-save verification 540 493 if (window.console && window.console.log && window.taicsData && window.taicsData.debug_enabled) { 541 console.log('TAICS Web Sources getValue():', sources);542 494 } 543 495 … … 625 577 */ 626 578 showNotificationWithFallback: function(message, type = 'info') { 627 console.log(`🔔 Notification (${type}): ${message}`);628 579 629 580 // Try to use notifications system if available … … 633 584 return; 634 585 } catch (e) { 635 console.log('❌ TAICS_Notifications failed:', e);636 586 } 637 587 } … … 643 593 return; 644 594 } catch (e) { 645 console.log('❌ Dashboard notification failed:', e);646 595 } 647 596 } 648 597 649 598 // Fallback to browser console 650 console.log(`%cTAICS: ${message}`, `color: ${type === 'success' ? 'green' : type === 'error' ? 'red' : 'blue'}; font-weight: bold;`);651 599 652 600 // Last resort - simple alert … … 654 602 alert(message); 655 603 } catch (e) { 656 console.log('❌ All notification methods failed');657 604 } 658 605 }, -
technodrome-ai-content-assistant/trunk/features/dashboard.js
r3444346 r3446048 13 13 window.TAICS_Notifications.showNotification(message, type); 14 14 } else { 15 console.log(`📢 ${type.toUpperCase()}: ${message}`);16 15 } 17 16 } … … 27 26 if (this.isInitialized) return; 28 27 29 console.log('TAICS Dashboard v2.0.1 initializing...');30 28 31 29 this.bindCoreEvents(); … … 34 32 this.isInitialized = true; 35 33 36 console.log('TAICS Dashboard v2.0.1 initialized successfully');37 34 38 35 // Inicijalizuj licencno ograničavanje … … 51 48 // Proverava da li postoje potrebni podaci za AJAX 52 49 if (!window.taicsData || !window.taicsData.ajax_url) { 53 console.warn('TAICS: Missing AJAX data, using fallback restrictions');54 50 callback(this.getFallbackRestrictions()); 55 51 return; … … 65 61 success: function(response) { 66 62 if (response.success) { 67 console.log('License restrictions fetched successfully:', response.data);68 63 // CRITICAL FIX: Update the global state so all modules have the correct plan. 69 64 if (window.taicsData && window.taicsData.user) { … … 110 105 111 106 applyRestrictions: function(restrictions) { 112 console.log('Applying license restrictions:', restrictions);113 107 114 108 // Log web sources status specifically 115 console.log('TAICS: Web sources locked status:', restrictions.features_locked.web_sources);116 109 117 110 // Ensure core fields are always enabled regardless of plan … … 225 218 }, 100); 226 219 227 console.log(`TAICS: Switched to tab ${tabId}`);228 220 229 221 // v3.5.4: Scroll to top tab navigation smoothly … … 253 245 254 246 case 'tab-content-rules': 255 console.log('🔀 TAICS: Switching to content-rules tab, initializing modules...');256 247 257 248 // ENHANCED: Apply license restrictions first, then initialize modules … … 265 256 // ENHANCED: Web sources initialization with delay to ensure DOM is ready 266 257 setTimeout(() => { 267 console.log('🔄 TAICS: Web sources delayed initialization...');268 258 this.initModule('TAICS_Websources_Input'); 269 259 }, 200); … … 314 304 this.activeModules[moduleName] = true; 315 305 } else { 316 console.warn(`❌ TAICS: Module ${moduleName} not found or missing init method`);317 console.warn(`📋 TAICS: Available modules:`, Object.keys(window).filter(key => key.startsWith('TAICS_')));318 306 } 319 307 } catch (error) { … … 332 320 window[moduleName].cleanup(); 333 321 this.activeModules[moduleName] = false; 334 console.log(`TAICS: Cleaned up ${moduleName}`);335 322 } catch (error) { 336 323 console.error(`TAICS: Error cleaning up ${moduleName}:`, error); … … 471 458 setTimeout(function() { 472 459 if (window.TAICS_Websources_Input && typeof window.TAICS_Websources_Input.init === 'function') { 473 console.log('TAICS: Forcing web sources initialization...');474 460 window.TAICS_Websources_Input.init(); 475 461 } else { 476 console.log('TAICS: Web sources module not available for forced initialization');477 462 } 478 463 }, 500); -
technodrome-ai-content-assistant/trunk/features/extras-tab/bulk-generator.js
r3421276 r3446048 30 30 31 31 init: function() { 32 console.log('TAICS Bulk Generator initializing...');33 32 34 33 this.activeProfileId = window.TAICS_Profile_Buttons?.activeProfile || null; … … 56 55 $('#taics-bulk-batch-size').on('change', (e) => { 57 56 this.batchSize = parseInt($(e.target).val()); 58 console.log('Batch size changed to:', this.batchSize);59 57 }); 60 58 61 59 $('#taics-bulk-delay').on('change', (e) => { 62 60 this.delayBetweenBatches = parseInt($(e.target).val()) * 1000; 63 console.log('Delay changed to:', this.delayBetweenBatches, 'ms');64 61 }); 65 62 }, … … 161 158 162 159 if (!this.isProcessing) { 163 console.log('⛔ Processing stopped');164 160 return; 165 161 } … … 181 177 182 178 Promise.all(promises).then(() => { 183 console.log('✅ Batch completed');184 179 185 180 // Update stats … … 190 185 // Delay before next batch 191 186 if (this.delayBetweenBatches > 0) { 192 console.log('⏳ Waiting', this.delayBetweenBatches / 1000, 'seconds before next batch...');193 187 setTimeout(() => { 194 188 this.processBatch(); … … 216 210 this.renderQueue(); 217 211 218 console.log('🔄 Processing:', item.topic);219 212 220 213 // Make AJAX request … … 243 236 this.stats.completed++; 244 237 this.stats.completionTimes.push(duration); 245 console.log('✅ Completed:', item.topic, '(' + (duration / 1000).toFixed(1) + 's)');246 238 } else { 247 239 item.status = 'failed'; … … 275 267 */ 276 268 handleCompletion: function() { 277 console.log('🎉 Bulk generation completed!');278 269 this.isProcessing = false; 279 270 this.toggleButtons('idle'); … … 293 284 */ 294 285 handlePause: function() { 295 console.log('⏸️ Pausing queue...');296 286 this.isPaused = true; 297 287 this.toggleButtons('paused'); … … 303 293 */ 304 294 handleResume: function() { 305 console.log('▶️ Resuming queue...');306 295 this.isPaused = false; 307 296 this.toggleButtons('processing'); … … 318 307 } 319 308 320 console.log('⛔ Cancelling queue...');321 309 this.isProcessing = false; 322 310 this.isPaused = false; -
technodrome-ai-content-assistant/trunk/features/extras-tab/schedule-picker.js
r3415822 r3446048 10 10 this.updateSchedulerData(); // Initial load 11 11 this.schedulerInterval = setInterval(this.updateSchedulerData.bind(this), 20000); // Refresh every 20 seconds 12 console.log('TAICS Schedule Picker Initialized');13 12 }, 14 13 … … 17 16 $('#taics-clear-schedule').on('click', this.handleClearForm.bind(this)); 18 17 }, 19 20 handleScheduleClick: function(e) {18 19 handleScheduleClick: function(e) { 21 20 e.preventDefault(); 22 21 const self = this; // Save the correct context … … 31 30 32 31 // Debug: Log values to console 33 console.log('Schedule Click Debug:', {32 console.log('Schedule click debug:', { 34 33 title: title, 35 34 date: date, … … 146 145 clearInterval(this.schedulerInterval); 147 146 } 148 console.log('TAICS Schedule Picker Cleaned Up');149 147 } 150 148 }; -
technodrome-ai-content-assistant/trunk/features/footer/api-status.js
r3421276 r3446048 13 13 init: function() { 14 14 if (this.isInitialized) { 15 console.log('TAICS API Status already initialized, skipping...');16 15 return; 17 16 } 18 17 19 console.log('Initializing API Status module');20 18 21 19 // Cache DOM elements … … 30 28 31 29 this.isInitialized = true; 32 console.log('API Status module initialized successfully');33 30 }, 34 31 … … 102 99 if (keyChanged && !this.isValidating) { 103 100 // Only call API validation if key changed and we're not already validating 104 console.log('TAICS API Status: Key changed, performing API validation');105 101 this.lastCheckedApiKey = apiKey; 106 102 this.validateApiKeyWithAPI(provider, apiKey); … … 141 137 const nonce = (window.taics_license && window.taics_license.nonce) ? window.taics_license.nonce : ''; 142 138 143 console.log('TAICS API Status: validateApiKeyWithAPI called for provider:', provider);144 console.log('TAICS API Status: Nonce available:', !!nonce);145 139 146 140 if (!nonce) { 147 console.warn('TAICS API Status: Nonce not available, using format validation only');148 141 // Fall back to format validation 149 142 if (this.validateApiKeyFormat(provider, apiKey)) { … … 174 167 }, 175 168 success: function(response) { 176 console.log('TAICS API Status: AJAX success response:', response);177 169 self.isValidating = false; // Mark validation complete 178 170 … … 183 175 } else { 184 176 // API returned error 185 console.warn('TAICS API Status: API returned error or empty models:', response);186 177 self.lastValidStatus = 'error'; 187 178 self.setStatus('error', 'Invalid API Key'); … … 233 224 234 225 refresh: function() { 235 console.log('API Status refresh clicked');236 226 this.setStatus('checking', 'Checking...'); 237 227 … … 304 294 305 295 this.isInitialized = false; 306 console.log('API Status module cleaned up');307 296 } 308 297 }; -
technodrome-ai-content-assistant/trunk/features/footer/credit-toggle.js
r3431889 r3446048 20 20 21 21 init: function() { 22 console.log('Initializing credit toggle module');23 22 this.loadInitialState(); 24 23 this.bindEvents(); … … 57 56 this.saveCreditPreference(); 58 57 59 console.log('Credits toggled:', this.isCreditsEnabled ? 'ON' : 'OFF');60 58 }, 61 59 … … 109 107 success: (response) => { 110 108 if (response.success) { 111 console.log('Credit preference saved successfully');112 109 } else { 113 console.warn('Failed to save credit preference:', response.data);114 110 } 115 111 }, 116 112 error: (xhr, status, error) => { 117 console.warn('AJAX error saving credit preference:', error);118 113 }, 119 114 complete: () => { -
technodrome-ai-content-assistant/trunk/features/footer/dark-mode-toggle.js
r3401081 r3446048 13 13 14 14 init: function() { 15 console.log('Initializing dark mode toggle module');16 15 this.loadInitialState(); 17 16 this.bindEvents(); … … 41 40 this.saveDarkModePreference(); 42 41 43 console.log('Dark mode toggled:', this.isDarkModeEnabled ? 'ON' : 'OFF');44 42 }, 45 43 … … 102 100 success: (response) => { 103 101 if (response.success) { 104 console.log('Dark mode preference saved successfully');105 102 } else { 106 console.warn('Failed to save dark mode preference:', response.data);107 103 } 108 104 }, 109 105 error: (xhr, status, error) => { 110 console.warn('AJAX error saving dark mode preference:', error);111 106 } 112 107 }); -
technodrome-ai-content-assistant/trunk/features/footer/profile-buttons.js
r3444453 r3446048 9 9 apiKeys: {}, 10 10 isInitializing: false, 11 isFirstProfileLoad: true, 12 isLoadingProfileData: false, 11 isFirstProfileLoad: true, // Track if this is the first profile load (on page init) 12 isLoadingProfileData: false, // FIX: Flag for profile-name-sync.js compatibility 13 14 // AutoSave Revolution v3.4.0 - Properties 13 15 autoSaveTimeout: null, 14 16 autoSaveInProgress: false, 15 lastChangedField: null, 16 structureNameTimeout: null, 17 lastChangedField: null, // Za specifičnu notifikaciju 18 19 // v4.0.5 KRITIČNO FIX: Flag da sprečimo AutoSave pri promeni profila 20 // Bez ovoga, AutoSave može biti triggeran sa starim podacima pre nego što se novi profil učita 21 isProfileSwitching: false, 22 23 /** 24 * v4.0.4 KRITIČNO: Deep copy profila za sprečavanje dijeljenja referenci između profila 25 * Bez ovoga, izmjena u jedan profil može utjecati na drugi profil 26 */ 27 deepCopyProfile: function(profile) { 28 if (!profile || typeof profile !== 'object') { 29 return profile; 30 } 31 return JSON.parse(JSON.stringify(profile)); 32 }, 17 33 18 34 init: function() { … … 25 41 this.loadProfiles(); 26 42 this.setDefaultActiveProfile(); 27 }, 28 43 console.log('TAICS Profile Buttons initialized successfully'); 44 }, 45 29 46 bindEvents: function() { 30 47 $('.taics-profile-btn-wide').off('click.taics-profile'); 31 48 $('.taics-profile-btn-wide').on('click.taics-profile', this.handleProfileClick.bind(this)); 32 49 33 // AutoSave event listeners na svim poljima 50 // AUTOSAVE REVOLUCIJA: Save dugme je uklonjeno - AutoSave sistem je aktivan 51 // Komentarišem event listener jer više nije potreban 52 // Sve izmene se automatski čuvaju u profil kroz AutoSave sistem 53 /* 54 $('#taics-save-profile-btn').off('click.taics-save-profile'); 55 $('#taics-save-profile-btn').on('click.taics-save-profile', this.handleSaveProfileClick.bind(this)); 56 */ 57 58 // AutoSave Revolution v3.4.0 - Event listeners na svim poljima 34 59 $('#taics-ai-provider').on('change.autosave', () => this.autoSaveField('ai_settings.ai_provider')); 35 60 $('#taics-ai-model').on('change.autosave', () => this.autoSaveField('ai_settings.ai_model')); … … 50 75 $(document).on('taics_headings_changed.autosave', () => this.autoSaveField('content_rules.headings')); 51 76 52 // Content Rules fields - Structure Name with auto-sync to autosave 77 // v3.4.0: Content Rules fields - Structure Name with 3s debounce 78 let structureNameTimeout; 53 79 $('#taics-structure-name').on('input.autosave', () => { 54 // v4.0.3 FIX: Use consistent 500ms debounce with main autosave system 55 // This ensures structure_name is saved immediately when user stops typing 56 clearTimeout(this.structureNameTimeout); 57 this.structureNameTimeout = setTimeout(() => { 80 clearTimeout(structureNameTimeout); 81 structureNameTimeout = setTimeout(() => { 58 82 this.autoSaveField('content_rules.structure_name'); 59 }, 500);83 }, 3000); 60 84 }); 61 85 $(document).on('taics_guidelines_changed.autosave', () => this.autoSaveField('content_rules.guidelines')); … … 63 87 $(document).on('taics_canvas_changed.autosave', () => this.autoSaveField('layout_template.advanced_template_canvas')); 64 88 }, 65 89 66 90 setDefaultActiveProfile: function() { 67 91 const activeBtn = $('.taics-profile-btn-wide.active'); … … 72 96 this.activeProfile = profileNum; 73 97 this.updateProfileStatus(); 74 }, 75 98 // NOTE: We don't update button label here because profiles aren't loaded yet 99 // The label will be updated after profiles load in loadProfiles() AJAX callback 100 }, 101 76 102 loadSavedSettings: function() { 77 103 // Delegirano na TAICS_AI_Provider_Select … … 101 127 } 102 128 }, 103 129 104 130 getNonce: function() { 105 131 return (window.taicsData && window.taicsData.nonce) ? window.taicsData.nonce : ''; 106 132 }, 107 133 108 134 getAjaxUrl: function() { 109 135 return (window.taicsData && window.taicsData.ajax_url) ? window.taicsData.ajax_url : ajaxurl || '/wp-admin/admin-ajax.php'; 110 136 }, 111 137 112 138 loadProfiles: function() { 113 139 const nonce = this.getNonce(); … … 121 147 success: (response) => { 122 148 if (response.success) { 123 this.profiles = response.data || {}; 149 // v4.0.4 KRITIČNO FIX: Napravi deep copy svakog profila iz odgovora servera 150 const rawProfiles = response.data || {}; 151 this.profiles = {}; 152 for (const profileNum in rawProfiles) { 153 if (rawProfiles.hasOwnProperty(profileNum)) { 154 this.profiles[profileNum] = this.deepCopyProfile(rawProfiles[profileNum]); 155 } 156 } 124 157 this.updateProfileButtonsStatus(); 125 158 126 // CRITICAL FIX: Only load profile data on FIRST page load (during init)127 // Don't reload on subsequent calls (e.g., when returning to Generate tab)128 // This prevents resetting user's in-session provider/model changes129 159 if (this.isFirstProfileLoad) { 130 160 this.loadCompleteProfileData(this.activeProfile); 131 this.isFirstProfileLoad = false; // Mark that initial load is done 132 133 // FIX: Trigger profile_loaded event for first profile load 134 // This ensures video-context-mode.js and video-slot-transformation.js initialize properly 161 this.isFirstProfileLoad = false; 162 135 163 setTimeout(() => { 136 const profileData = this.profiles[this.activeProfile] || {}; 137 $(document).trigger('taics_profile_loaded', [this.activeProfile, profileData]); 164 $(document).trigger('taics_profile_loaded', [this.activeProfile]); 138 165 }, 100); 139 166 } 140 167 141 // Show notification about profiles loaded142 168 this.showNotification( 143 169 `Profiles loaded - ${Object.keys(this.profiles).length} saved profiles found`, 144 170 'info' 145 171 ); 172 173 $(document).trigger('taics_all_profiles_loaded', [this.profiles]); 146 174 } else { 147 175 console.error('Failed to load profiles:', response.data); … … 155 183 }); 156 184 }, 157 185 158 186 handleProfilesLoaded: function(response) { 159 187 if (response.success) { 160 this.profiles = response.data || {}; 188 // v4.0.4 KRITIČNO FIX: Napravi deep copy svakog profila iz odgovora servera 189 const rawProfiles = response.data || {}; 190 this.profiles = {}; 191 for (const profileNum in rawProfiles) { 192 if (rawProfiles.hasOwnProperty(profileNum)) { 193 this.profiles[profileNum] = this.deepCopyProfile(rawProfiles[profileNum]); 194 } 195 } 161 196 this.updateProfileButtonsStatus(); 162 197 this.loadCompleteProfileData(this.activeProfile); 163 198 } 164 199 }, 165 200 166 201 handleLoadError: function(_xhr, _status, _error) { 167 202 console.error('Failed to load profiles:', _error); 168 203 }, 169 204 170 205 updateProfileButtonsStatus: function() { 171 206 $('.taics-profile-btn-wide').each((_index, button) => { 172 207 const $button = $(button); 173 208 const profileNumber = $button.data('profile'); 174 const hasProfileData = this.profiles[profileNumber] && 209 const hasProfileData = this.profiles[profileNumber] && 175 210 Object.keys(this.profiles[profileNumber]).length > 0; 176 211 $button.toggleClass('taics-profile-has-data', hasProfileData); 177 212 }); 178 213 }, 179 214 180 215 handleProfileClick: function(e) { 181 216 e.preventDefault(); 182 217 const $button = $(e.currentTarget); 183 218 const profileNumber = parseInt($button.data('profile')); 219 220 // KRITIČNO: Spreči brzu promenu profila ako je prethodna promena još uvek u toku 221 if (this.isProfileSwitching) { 222 console.log('TAICS: Profile switch ignored - switching already in progress'); 223 return; 224 } 225 184 226 if (profileNumber === this.activeProfile) { 185 227 return; … … 189 231 190 232 switchProfile: function(profileNumber) { 191 // 🔥 v4.0.4 FIX: Set flag to prevent autosave while loading profile data 233 if (this.autoSaveTimeout) { 234 clearTimeout(this.autoSaveTimeout); 235 this.autoSaveTimeout = null; 236 } 237 238 this.isProfileSwitching = true; 192 239 this.isLoadingProfileData = true; 193 240 194 241 $('.taics-profile-btn-wide').removeClass('active'); 195 242 $(`.taics-profile-btn-wide[data-profile="${profileNumber}"]`).addClass('active'); 196 243 this.activeProfile = profileNumber; 197 244 198 const profileData = this. profiles[profileNumber];245 const profileData = this.deepCopyProfile(this.profiles[profileNumber]); 199 246 200 247 if (profileData && Object.keys(profileData).length > 0) { 201 248 this.loadCompleteProfileDataFromObject(profileData); 202 249 203 // Show detailed profile loaded notification 204 this.showProfileLoadedNotification(profileNumber, profileData); 250 setTimeout(() => { 251 this.showProfileLoadedNotification(profileNumber, profileData); 252 this.updateProfileStatus(); 253 $(document).trigger('taics_profile_changed', [profileNumber]); 254 255 this.isProfileSwitching = false; 256 this.isLoadingProfileData = false; 257 }, 500); 205 258 } else { 206 259 this.loadCompleteProfileData(profileNumber); 207 260 208 // Show empty profile notification209 261 this.showNotification( 210 262 `Profile ${profileNumber} loaded (empty profile)`, 211 263 'info' 212 264 ); 213 } 214 215 this.updateProfileStatus(); 216 $(document).trigger('taics_profile_changed', [profileNumber]); 217 218 // 🔥 FIX v4.0.3: Trigger taics_profile_loaded BEFORE clearing flag 219 // This ensures profile-name-sync.js receives the flag as true during load 220 const switchProfileData = profileData || {}; 221 $(document).trigger('taics_profile_loaded', [profileNumber, switchProfileData]); 222 223 // 🔥 v4.0.4 FIX: Clear flag after profile data loading completes 224 // This allows autosave to work again for user changes 225 this.isLoadingProfileData = false; 265 266 setTimeout(() => { 267 this.updateProfileStatus(); 268 $(document).trigger('taics_profile_changed', [profileNumber]); 269 270 this.isProfileSwitching = false; 271 this.isLoadingProfileData = false; 272 }, 500); 273 } 226 274 }, 227 275 … … 232 280 const language = profileData.language || 'en-US'; 233 281 const templateId = profileData.layout_template?.template_id || '1'; 234 282 235 283 // Show main notification 236 284 if (window.TAICS_Notifications && typeof window.TAICS_Notifications.showProfileLoaded === 'function') { 237 285 window.TAICS_Notifications.showProfileLoaded(profileNumber); 238 286 } 239 287 240 288 // Show detailed settings in a batch notification (slight delay for better UX) 241 289 setTimeout(() => { … … 246 294 `Template: ${templateId}` 247 295 ]; 248 296 249 297 if (window.TAICS_Notifications && typeof window.TAICS_Notifications.showBatchNotification === 'function') { 250 298 window.TAICS_Notifications.showBatchNotification(details, 'info', 4000); … … 262 310 window.TAICS_AI_Provider_Select.syncWithProfileData(profileData); 263 311 } 264 312 265 313 // FIX: Also sync Default Tone module with profile data 266 314 if (window.TAICS_Default_Tone && typeof window.TAICS_Default_Tone.setValue === 'function') { … … 268 316 } 269 317 }, 270 318 271 319 loadCompleteProfileData: function(profileNumber) { 272 const profileData = this. profiles[profileNumber]|| {};320 const profileData = this.deepCopyProfile(this.profiles[profileNumber]) || {}; 273 321 if (Object.keys(profileData).length === 0) { 274 322 this.resetAllFields(); … … 283 331 window.TAICS_AI_Provider_Select.syncWithProfileData(profileData); 284 332 } 285 }, 286 333 334 // DVT v4.0.5.2: DIRECT DOM UPDATE - Update Profile 1 button label directly 335 // This is a fallback that bypasses TAICS_Profile_Name_Sync to ensure it works on first load 336 if (profileNumber === 1 && profileData.content_rules && profileData.content_rules.structure_name) { 337 const structureName = profileData.content_rules.structure_name; 338 const displayLabel = structureName.substring(0, 7).toUpperCase() || 'PROFILE'; 339 const $profileBtn = $('.taics-profile-btn-wide[data-profile="1"]'); 340 const $profileLabel = $profileBtn.find('.taics-profile-text'); 341 if ($profileLabel.length > 0) { 342 $profileLabel.text(displayLabel); 343 $profileLabel[0].textContent = displayLabel; // Native DOM update 344 $profileBtn.attr('title', 'Profile 1 (' + displayLabel + ')'); 345 $profileBtn[0].title = 'Profile 1 (' + displayLabel + ')'; // Native DOM update 346 } 347 } 348 }, 349 350 updateProfileButtonLabel: function(profileNumber, structureName) { 351 if (!window.TAICS_Profile_Name_Sync) { 352 return; 353 } 354 window.TAICS_Profile_Name_Sync.updateButtonLabelForProfile(profileNumber, structureName); 355 }, 356 287 357 loadGenerateTabData: function(profileData) { 288 358 const aiSettings = profileData.ai_settings || {}; … … 324 394 } 325 395 }, 326 396 327 397 loadContentRulesData: function(contentRules) { 328 398 // Load Structure Name 329 const structureName = contentRules.structure_name || ''; 330 $('#taics-structure-name').val(structureName); 331 332 // 🔥 v4.0.4 FIX: Only trigger input if NOT currently loading profile data 333 // This allows profile-name-sync to detect the new value without triggering autosave 334 // when the field is being loaded from profile (not user-changed) 335 if (!this.isLoadingProfileData) { 336 $('#taics-structure-name').trigger('input.autosave'); 337 } 399 $('#taics-structure-name').val(contentRules.structure_name || ''); 338 400 339 401 if (window.TAICS_Headings_Editor && typeof window.TAICS_Headings_Editor.setValue === 'function') { … … 425 487 } 426 488 }, 427 489 428 490 resetAllFields: function() { 429 491 $('#taics-topic').val(''); … … 459 521 if (window.TAICS_AI_Provider_Select && typeof window.TAICS_AI_Provider_Select.updateProviderAndModel === 'function') { 460 522 // Note: Profile data should be loaded already, this just resets UI to defaults 461 const profileData = this.profiles[this.activeProfile]; 523 // v4.0.4 FIX: Koristi deep copy za sigurnost 524 const profileData = this.deepCopyProfile(this.profiles[this.activeProfile]); 462 525 if (profileData && profileData.ai_settings) { 463 526 window.TAICS_AI_Provider_Select.updateProviderAndModel( … … 549 612 window.TAICS_Default_Tone.getValue() : 'article-specific', 550 613 content_rules: { 614 // v4.0.4 KRITIČNO: Dodaj structure_name u prikupljanje podataka 551 615 structure_name: $('#taics-structure-name').val() || '', 552 616 headings: (window.TAICS_Headings_Editor && typeof window.TAICS_Headings_Editor.getValue === 'function') ? … … 579 643 this.showNotification('Profile saved successfully', 'success'); 580 644 } 581 582 this.profiles[profileNumber] = profileData; 645 646 // v4.0.4 KRITIČNO FIX: Koristi deep copy za cache update 647 this.profiles[profileNumber] = this.deepCopyProfile(profileData); 583 648 this.updateProfileButtonsStatus(); 584 649 this.updateProfileStatus(); 585 650 $(document).trigger('taics_profile_saved', [profileNumber, profileData]); 586 651 587 652 // Show additional details about what was saved 588 653 setTimeout(() => { … … 593 658 `Mode: ${profileData.generation_mode}` 594 659 ]; 595 660 596 661 if (window.TAICS_Notifications && typeof window.TAICS_Notifications.showBatchNotification === 'function') { 597 662 window.TAICS_Notifications.showBatchNotification(savedDetails, 'info', 3000); … … 633 698 return sources; 634 699 }, 635 700 636 701 collectLayoutTemplateData: function() { 637 702 // Get template ID from TAICS_Template_Selector module … … 686 751 const templateId = $('input[name="taics-template"]:checked').val() || '1'; 687 752 const photos = []; 688 753 689 754 $('.taics-photo-upload-slot.has-photo').each(function() { 690 755 const $slot = $(this); … … 705 770 }; 706 771 }, 707 772 708 773 collectExtrasData: function() { 709 774 return { … … 761 826 window.TAICS_Default_Tone.getValue() : 'article-specific', 762 827 content_rules: { 828 // v4.0.4 KRITIČNO: Dodaj structure_name u prikupljanje podataka 763 829 structure_name: $('#taics-structure-name').val() || '', 764 830 headings: (window.TAICS_Headings_Editor && typeof window.TAICS_Headings_Editor.getValue === 'function') ? … … 780 846 } 781 847 }, 782 848 783 849 saveCurrentSettings: function() { 784 850 if (window.TAICS_AI_Provider_Select && typeof window.TAICS_AI_Provider_Select.saveCurrentSettings === 'function') { … … 802 868 803 869 autoSaveField: function(fieldPath) { 870 // DVT v4.0.5.1: EXTRA SAFETY - Double check profile switching flag 871 // Even if isProfileSwitching is somehow not set, this provides additional protection 872 if (this.isProfileSwitching) { 873 console.log('TAICS: AutoSave BLOCKED - profile switching in progress (' + fieldPath + ')'); 874 return; 875 } 876 877 // v4.0.5 KRITIČNO FIX: Preskoči AutoSave ako se trenutno menja profil 878 // Bez ovoga, AutoSave će biti triggeran sa starim podacima pre nego što se novi profil učita 879 if (this.isProfileSwitching) { 880 console.log('TAICS: AutoSave skipped - profile switching in progress'); 881 return; 882 } 883 804 884 // Sačuvaj koje polje je izmenjeno (za specifičnu notifikaciju) 805 885 this.lastChangedField = fieldPath; … … 862 942 this.showNotification(message, 'success'); 863 943 864 // Ažuriraj lokalni cache 865 this.profiles[this.activeProfile] = profileData; 944 // v4.0.4 KRITIČNO FIX: Ažuriraj lokalni cache sa deep copy profila 945 // Bez ovoga, lokalni cache može biti kontaminiran jer dijeli iste reference 946 this.profiles[this.activeProfile] = this.deepCopyProfile(profileData); 866 947 this.updateProfileButtonsStatus(); 867 948 } else { … … 935 1016 }, 200); 936 1017 }); 937 1018 938 1019 window.TAICS_Profile_Buttons = TAICS_Profile_Buttons; 939 1020 -
technodrome-ai-content-assistant/trunk/features/footer/publish-toggle.js
r3401081 r3446048 6 6 7 7 init: function() { 8 console.log('Initializing publish toggle module');9 8 this.loadInitialState(); 10 9 this.bindEvents(); … … 35 34 $(document).trigger('taics_publish_toggle_changed', [this.isPublishEnabled]); 36 35 37 console.log('Auto-publish toggled:', this.isPublishEnabled ? 'ON' : 'OFF');38 36 }, 39 37 … … 85 83 success: (response) => { 86 84 if (response.success) { 87 console.log('Auto-publish preference saved successfully');88 85 } else { 89 console.warn('Failed to save auto-publish preference:', response.data);90 86 } 91 87 }, 92 88 error: (xhr, status, error) => { 93 console.warn('AJAX error saving auto-publish preference:', error);94 89 } 95 90 }); -
technodrome-ai-content-assistant/trunk/features/footer/save-button.js
r3401081 r3446048 14 14 init: function() { 15 15 this.bindEvents(); 16 console.log('TAICS Save Button initialized - no edit mode');17 16 }, 18 17 … … 26 25 27 26 if (this.isProcessing) { 28 console.log('TAICS: Save processing in progress, ignoring click');29 27 return; 30 28 } 31 29 32 30 this.isProcessing = true; 33 console.log('TAICS: Save clicked');34 31 35 32 // CRITICAL FIX: Save video data explicitly if Video Manager exists 36 33 if (window.taicsVideoManager && typeof window.taicsVideoManager.saveVideoData === 'function') { 37 console.log('TAICS: Triggering explicit video data save');38 34 window.taicsVideoManager.saveVideoData(); 39 35 } … … 41 37 this.saveProfile() 42 38 .then((response) => { 43 console.log('TAICS: Save promise resolved:', response);44 39 if (response && response.success) { 45 40 this.showNotification('Profile saved successfully', 'success'); … … 65 60 const profileData = this.collectProfileData(); 66 61 const activeProfile = this.getActiveProfile(); 67 console.log('TAICS Save: Preparing to send profile data:', profileData, 'for profile:', activeProfile);68 62 69 63 if (!window.taicsData || !window.taicsData.ajax_url || !window.taicsData.nonce) { … … 85 79 }, 86 80 success: (response) => { 87 console.log('TAICS Save: AJAX response:', response);88 81 if (response.success) { 89 console.log('TAICS: Profile saved, refreshing profiles cache');90 82 if (window.TAICS_Profile_Buttons) { 91 83 window.TAICS_Profile_Buttons.loadProfiles(); … … 197 189 if (parseInt(templateId) === 6 && window.TAICS_Advanced_Template && typeof window.TAICS_Advanced_Template.getValue === 'function') { 198 190 advancedTemplateData = window.TAICS_Advanced_Template.getValue(); 199 console.log('Advanced Template canvas data:', advancedTemplateData);200 191 } 201 192 202 193 // IMPORTANT: Photos are NOT saved in profile - they are global and persist in user_meta 203 194 // Photos and their links remain visible across all profiles until manually removed 204 205 console.log('Layout template data collected (save-button):', {206 template_id: parseInt(templateId),207 advanced_template_canvas: advancedTemplateData208 });209 195 210 196 return { … … 242 228 243 229 showNotification: function(message, type) { 244 console.log('TAICS: Attempting to show notification:', message, type);245 230 246 231 if (window.TAICS_Notifications && typeof window.TAICS_Notifications.showNotification === 'function') { 247 console.log('TAICS: Using TAICS_Notifications');248 232 window.TAICS_Notifications.showNotification(message, type); 249 233 } else if (window.TAICS_Dashboard && typeof window.TAICS_Dashboard.showNotification === 'function') { 250 console.log('TAICS: Using TAICS_Dashboard notifications');251 234 window.TAICS_Dashboard.showNotification(message, type); 252 235 } else { 253 console.log('TAICS: No notification system found, using console');254 console.log(`${type.toUpperCase()}: ${message}`);255 236 256 237 // Fallback: show alert for critical messages -
technodrome-ai-content-assistant/trunk/features/generate-tab/add-content-title.js
r3361244 r3446048 16 16 this.updateCharCount(); 17 17 this.updateMaxLengthAttribute(); 18 console.log('TAICS Content Title v2.0.0 initialized (max chars: ' + this.maxChars + ')');19 18 }, 20 19 … … 105 104 window.TAICS_Dashboard.showNotification(message, type, 2000); 106 105 } else { 107 console.log(`${type.toUpperCase()}: ${message}`);108 106 } 109 107 }, … … 139 137 cleanup: function() { 140 138 $('#taics-topic').off('input paste keydown'); 141 console.log('TAICS Content Title cleaned up');142 139 } 143 140 }; -
technodrome-ai-content-assistant/trunk/features/generate-tab/ai-image-toggle.js
r3421276 r3446048 114 114 this.isImageEnabled = false; 115 115 this.updateToggleUI(); 116 console.log('TAICS AI Image: Loaded from profile (default OFF)');117 116 return; 118 117 } … … 122 121 this.updateToggleUI(); 123 122 124 console.log('TAICS AI Image: Loaded from profile:', aiImageEnabled);125 123 }, 126 124 … … 163 161 this.isImageEnabled = false; 164 162 this.updateToggleUI(); 165 console.log('TAICS AI Image: Reset on profile load (legacy)');166 163 }, 167 164 -
technodrome-ai-content-assistant/trunk/features/generate-tab/ai-provider-select.js
r3434643 r3446048 22 22 } 23 23 24 console.log('TAICS AI Provider Select: INITIALIZING v3.5.1');25 24 this.isInitializing = true; 26 25 … … 31 30 // Check if already initialized - don't reset provider on tab switch 32 31 if (this.currentProvider && this.currentProvider !== 'google' && $('#taics-ai-provider').length) { 33 console.log('TAICS AI Provider Select: Already initialized with provider:', this.currentProvider);34 32 // v3.5.1: Update model options with newly loaded models 35 33 this.updateModelOptions(); … … 178 176 // Removed loadSavedSettings() - no longer needed as settings come from profile 179 177 loadSavedSettings: function() { 180 console.log('TAICS AI Provider Select: loadSavedSettings is deprecated. Using profile data.');181 178 // This function is no longer active. All settings should come from profile data. 182 179 }, … … 271 268 this.updateProviderInfo(); 272 269 273 console.log('Demo mode activated - API fields hidden');274 270 }, 275 271 … … 283 279 $('.taics-demo-info').hide(); 284 280 285 console.log('API mode activated - API fields shown');286 281 }, 287 282 … … 412 407 validateApiKey: function() { 413 408 if (this.currentProvider === 'demo') { 414 console.log('TAICS: Demo mode, API key not required for validation.');415 409 return true; 416 410 } … … 418 412 const apiKey = this.getApiKey(); 419 413 if (!apiKey) { 420 console.log('TAICS: No API key provided for validation.');421 414 return false; 422 415 } 423 416 424 417 const isValid = this.isValidApiKeyFormat(apiKey); 425 console.log('TAICS: API key validation result:', isValid ? 'Valid' : 'Invalid format');426 418 return isValid; 427 419 }, … … 464 456 const cacheMsg = response.data.cached ? ' (cached)' : ' (fresh)'; 465 457 self.showNotification('Updated model list from ' + provider + cacheMsg, 'success'); 466 console.log('TAICS: Using API models for ' + provider + ', count: ' + response.data.models.length);467 458 } else { 468 459 // Keep hardcoded models only on actual API error/failure 469 console.log('TAICS: API returned no models for ' + provider + ', using built-in models');470 460 self.showNotification('Using built-in model list for ' + provider, 'info'); 471 461 } … … 473 463 error: function() { 474 464 // Fallback to hardcoded models on AJAX error only 475 console.log('TAICS: API error for ' + provider + ', using built-in models');476 465 self.showNotification('Using built-in model list for ' + provider, 'info'); 477 466 } … … 606 595 }); 607 596 608 console.log('TAICS AI Provider Select: Internal settings updated.', {provider: this.currentProvider, model: currentModelFromDOM});609 597 }, 610 598 // --- END MODIFICATION --- … … 614 602 window.TAICS_Dashboard.showNotification(message, type); 615 603 } else { 616 console.log(`${type.toUpperCase()}: ${message}`);617 604 } 618 605 }, … … 709 696 syncWithProfileData: function(profileData) { 710 697 if (!profileData) { 698 console.log('TAICS: syncWithProfileData - no profile data provided'); 711 699 return; 712 700 } … … 721 709 const model = aiSettings.ai_model || ''; 722 710 const apiKey = aiSettings.api_key || ''; 723 711 712 // DVT v4.0.5.2: Log API key loading for debugging 713 console.log('TAICS: syncWithProfileData - API Key from profile: ' + (apiKey ? apiKey.substring(0, 10) + '...' : 'EMPTY')); 714 724 715 this.updateProviderAndModel(provider, model, apiKey); 725 716 … … 812 803 } 813 804 814 console.log('TAICS: AI Image enabled for', this.currentProvider, ':', selectedModel);815 805 } else { 816 806 // Disable toggle … … 835 825 } 836 826 837 console.log('TAICS: AI Image disabled for', this.currentProvider, ':', selectedModel);838 827 } 839 828 }, -
technodrome-ai-content-assistant/trunk/features/generate-tab/content-type.js
r3361244 r3446048 12 12 13 13 init: function() { 14 console.log('TAICS Content Type initialized');15 14 this.bindEvents(); 16 15 this.updateDisplay(); … … 24 23 handleContentTypeChange: function(e) { 25 24 const selectedType = $(e.target).val(); 26 console.log('TAICS: Content type changed to:', selectedType);27 25 28 26 // Update UI based on content type … … 72 70 cleanup: function() { 73 71 $('#taics-content-type').off('.taics-content-type'); 74 console.log('TAICS Content Type cleaned up');75 72 } 76 73 }; -
technodrome-ai-content-assistant/trunk/features/generate-tab/default-tone.js
r3444453 r3446048 21 21 }, 22 22 23 // State management 23 // State management - simplified, always unlocked for AutoSave 24 24 state: { 25 25 initialized: false, 26 currentValue: 'article-specific', 27 isLocked: true 26 currentValue: 'article-specific' 28 27 }, 29 28 … … 35 34 return; 36 35 } 37 38 // Bind events39 36 this.bindEvents(); 40 41 // Set initial field state based on save/edit button42 this.updateFieldState();43 44 37 this.state.initialized = true; 45 38 }, … … 57 50 }); 58 51 59 // Listen for profile switches60 $(document).on('taics_profile_switched.default-tone', function(e, profileId) {61 self.handleProfileSwitch(profileId);62 });63 64 52 // Listen for profile loaded events 65 // v4.0.3 FIX: Event now passes (profileNumber, profileData) as parameters66 53 $(document).on('taics_profile_loaded.default-tone', function(e, profileNumber, profileData) { 67 54 self.handleProfileLoaded(profileData); 68 });69 70 // Listen for field lock/unlock events71 $(document).on('taics_fields_locked.default-tone', function() {72 self.lockField();73 });74 75 $(document).on('taics_fields_unlocked.default-tone', function() {76 self.unlockField();77 55 }); 78 56 }, … … 83 61 */ 84 62 handleToneChange: function(selectedValue) { 85 if (this.state.isLocked) {86 return;87 }88 89 63 this.state.currentValue = selectedValue; 90 91 64 // Event is already triggered by the dropdown change - no need to trigger again! 92 65 // profile-buttons.js will catch the change.autosave event automatically 93 },94 95 /**96 * Handle profile switch97 */98 handleProfileSwitch: function() {99 // Profile data will be loaded via profile_loaded event100 66 }, 101 67 … … 124 90 125 91 /** 126 * Get current field value 127 * Used by AutoSave to collect profile data 92 * Get current field value - FIX: Added for AutoSave compatibility 128 93 */ 129 94 getValue: function() { 130 const $field = $(this.config.fieldId); 131 return $field.length ? $field.val() : this.config.defaultValue; 132 }, 133 134 /** 135 * Update field state based on save/edit button 136 */ 137 updateFieldState: function() { 138 if (window.TAICS_Save_Edit_Button && typeof window.TAICS_Save_Edit_Button.isInEditMode === 'function') { 139 const isEditMode = window.TAICS_Save_Edit_Button.isInEditMode(); 140 if (isEditMode) { 141 this.unlockField(); 142 } else { 143 this.lockField(); 144 } 145 } else { 146 // Default to locked if save/edit button not available 147 this.lockField(); 148 } 149 }, 150 151 /** 152 * Lock the field 153 */ 154 lockField: function() { 155 const $field = $(this.config.fieldId); 156 $field.prop('disabled', true).addClass('taics-field-locked'); 157 this.state.isLocked = true; 158 }, 159 160 /** 161 * Unlock the field 162 */ 163 unlockField: function() { 164 const $field = $(this.config.fieldId); 165 $field.prop('disabled', false).removeClass('taics-field-locked'); 166 this.state.isLocked = false; 95 return this.state.currentValue || this.config.defaultValue; 167 96 }, 168 97 … … 182 111 // Initialize when document is ready 183 112 $(document).ready(function() { 184 // Small delay to ensure other modules are loaded 185 setTimeout(function() { 186 if (!TAICS_Default_Tone.state.initialized) { 187 TAICS_Default_Tone.init(); 188 } 189 }, 150); 113 TAICS_Default_Tone.init(); 190 114 }); 191 115 -
technodrome-ai-content-assistant/trunk/features/generate-tab/generation-mode.js
r3421276 r3446048 7 7 this.bindEvents(); 8 8 // Profile data will be loaded by profile system, no localStorage 9 console.log('TAICS Generation Mode initialized (v3.4.0 - AutoSave)');10 9 }, 11 10 … … 19 18 20 19 // Listen for profile loaded events 21 $(document).on('taics_profile_loaded.generation-mode', function(e, data) { 22 if (data && data.generation_mode) { 23 $('#taics-generation-mode').val(data.generation_mode); 24 } 20 $(document).on('taics_profile_loaded.generation-mode', function(e, profileNumber) { 21 // v4.0.4 KRITIČNO FIX: Trigger mode change sa razlicitim delay vrijednostima 22 // Prvo triggeruj odmah, zatim ponovi nakon 150ms za sigurnost 23 const currentMode = $('#taics-generation-mode').val(); 24 25 // Immediate trigger 26 $(document).trigger('taics_generation_mode_changed', [currentMode]); 27 28 // Secondary trigger after delay to ensure video-context-mode.js processes it 29 setTimeout(function() { 30 $(document).trigger('taics_generation_mode_changed', [currentMode]); 31 }, 150); 25 32 }); 26 33 }, … … 31 38 // Trigger AutoSave event 32 39 $(document).trigger('taics_field_changed.autosave', ['generation_mode', mode]); 40 41 // Trigger video context mode change to update title field lock/unlock 42 $(document).trigger('taics_generation_mode_changed', [mode]); 33 43 34 44 // Check plan restrictions -
technodrome-ai-content-assistant/trunk/features/generate-tab/language-select.js
r3361244 r3446048 8 8 9 9 this.bindEvents(); 10 console.log('TAICS Language Select module initialized');11 10 }, 12 11 … … 21 20 // Trigger custom event for other modules 22 21 $(document).trigger('taics_language_changed', [selectedLanguage]); 23 console.log('Language changed to:', selectedLanguage);24 22 }, 25 23 … … 34 32 // Trigger change event to ensure other modules react 35 33 $(document).trigger('taics_language_changed', [languageCode]); 36 console.log('Language set to:', languageCode);37 34 }, 38 35 … … 41 38 this.$languageSelect.off('.taics-language-select'); 42 39 } 43 console.log('TAICS Language Select module cleaned up');44 40 } 45 41 }; -
technodrome-ai-content-assistant/trunk/features/generate-tab/video-context-mode.js
r3434643 r3446048 18 18 // Wait for DOM to be ready 19 19 $(document).ready(function() { 20 console.log('[VIDEO CONTEXT] Initializing video context mode handler...');21 20 22 21 // Generation mode dropdown … … 57 56 */ 58 57 function activateVideoContextMode(mode) { 59 console.log('[VIDEO CONTEXT] Activating video context mode:', mode); 60 58 61 59 // Lock the input 62 60 $topicInput.prop('disabled', true); 63 61 64 62 // Style the input to indicate it's locked - BIGGER FONT FOR VISIBILITY 65 63 $topicInput.css({ … … 103 101 */ 104 102 function deactivateVideoContextMode() { 105 console.log('[VIDEO CONTEXT] Deactivating video context mode'); 106 103 107 104 // Unlock the input 108 105 $topicInput.prop('disabled', false); 109 110 // Restore normal styling 111 $topicInput.css({ 112 'background-color': '', 113 'color': '', 114 'opacity': '', 115 'font-weight': '', 116 'font-size': '' 117 }); 118 106 107 // Restore normal styling - important to clear ALL inline styles 108 $topicInput.removeAttr('style'); 109 119 110 // Remove dynamic placeholder styles 120 111 var topicId = $topicInput.attr('id'); 121 112 var placeholderStyleId = 'taics-video-context-style-' + topicId; 122 $('#' + placeholderStyleId).remove(); 123 113 var styleElement = $('#' + placeholderStyleId); 114 if (styleElement.length) { 115 styleElement.remove(); 116 } 117 124 118 // Restore default placeholder 125 119 $topicInput.attr('placeholder', defaultPlaceholder); 126 120 127 121 // v4.0.1: Show video URL input for other modes (container may not exist) 128 122 if ($videoUrlContainer.length) { 129 123 $videoUrlContainer.show(); 130 124 } 131 125 132 126 // Trigger event to reset video slots 133 127 $(document).trigger('taics:videoContextModeChanged', [null]); 134 128 $(document).trigger('taics_videoContextModeChanged', [null]); 135 129 } 136 130 137 131 /** 138 132 * Handle generation mode change … … 140 134 function handleGenerationModeChange() { 141 135 var selectedMode = $generationMode.val(); 142 console.log('[VIDEO CONTEXT] Generation mode changed to:', selectedMode); 143 136 144 137 if (isVideoContextMode(selectedMode)) { 145 138 activateVideoContextMode(selectedMode); … … 154 147 function initializeVideoContextMode() { 155 148 var currentMode = $generationMode.val(); 156 console.log('[VIDEO CONTEXT] Initializing with mode:', currentMode); 157 149 158 150 if (isVideoContextMode(currentMode)) { 159 151 activateVideoContextMode(currentMode); 152 } else { 153 deactivateVideoContextMode(); 160 154 } 161 155 } … … 163 157 // Bind change event to generation mode dropdown 164 158 $generationMode.on('change', handleGenerationModeChange); 159 160 // v4.0.4 FIX: Also listen for programmatic mode changes via custom event 161 // This ensures title field unlock/lock works when mode is changed programmatically 162 $(document).on('taics_generation_mode_changed', function(e, mode) { 163 // v4.0.4 KRITIČNO: Proveri mode ODMAH, zatim sa delay za sigurnost 164 var currentMode = $generationMode.val(); 165 166 // Immediate check (mode je možda već ažuriran) 167 if (isVideoContextMode(currentMode)) { 168 activateVideoContextMode(currentMode); 169 } else { 170 deactivateVideoContextMode(); 171 } 172 173 // Secondary check after small delay to ensure value is fully updated 174 setTimeout(function() { 175 var updatedMode = $generationMode.val(); 176 if (isVideoContextMode(updatedMode)) { 177 activateVideoContextMode(updatedMode); 178 } else { 179 deactivateVideoContextMode(); 180 } 181 }, 50); 182 }); 165 183 166 184 // Listen for profile loaded event to re-initialize video context mode 167 185 $(document).on('taics_profile_loaded taics_profile_changed', function(e, profileNumber) { 168 console.log('[VIDEO CONTEXT] Profile loaded/changed:', profileNumber); 169 170 // Small delay to allow generation mode to be set from profile data 171 setTimeout(function() { 172 initializeVideoContextMode(); 173 }, 100); 186 187 // v4.0.4 FIX: Trebam izvršiti sa razlicitim delay vrijednostima za sigurnost 188 // Prvo nakon 50ms 189 setTimeout(function() { 190 initializeVideoContextMode(); 191 }, 50); 192 193 // Ponovi nakon 150ms (kada se generation-mode.js triggeruje event) 194 setTimeout(function() { 195 initializeVideoContextMode(); 196 }, 150); 197 198 // Finalno nakon 250ms za sigurnost 199 setTimeout(function() { 200 initializeVideoContextMode(); 201 }, 250); 174 202 }); 175 203 … … 212 240 }; 213 241 214 console.log('[VIDEO CONTEXT] Video context mode handler initialized');215 242 }); 216 243 -
technodrome-ai-content-assistant/trunk/features/header/add-licence.js
r3432273 r3446048 12 12 init: function() { 13 13 this.bindEvents(); 14 console.log('TAICS License Handler initialized');15 14 }, 16 15 … … 130 129 resetToFree: function() { 131 130 // v3.5.4: Auto-reset to FREE when license key is empty or invalid 132 console.log('Resetting license to FREE tier...');133 131 134 132 const $licenseInput = $('#taics-license-key'); -
technodrome-ai-content-assistant/trunk/features/layout-templates-tab/advanced-template.js
r3401188 r3446048 312 312 313 313 $(document).on('taics_template_changed.advanced-builder', function(_, templateId) { 314 console.log('🔄 Template changed to:', templateId);315 314 316 315 // Always show builder buttons (MAJOR FIX: Always visible in Layout Tab) … … 599 598 600 599 this.showNotification('Element removed', 'info'); 601 console.log('Element removed:', elementType);602 600 }, 603 601 … … 631 629 this.canvasData = []; 632 630 this.showNotification('Canvas cleared', 'success'); 633 console.log('Canvas cleared');634 631 } 635 632 }, … … 842 839 }); 843 840 844 console.log('Preview shown');845 841 }, 846 842 … … 849 845 window.TAICS_Dashboard.showNotification(message, type); 850 846 } else { 851 console.log('[' + type.toUpperCase() + '] ' + message);852 847 } 853 848 }, -
technodrome-ai-content-assistant/trunk/features/layout-templates-tab/photo-positions.js
r3434643 r3446048 102 102 $(document).trigger('taics_photo_link_changed.autosave', [position, this.getValue()]); 103 103 } else { 104 console.warn('Invalid URL format:', finalUrl);105 104 } 106 105 } … … 194 193 window.TAICS_Dashboard.showNotification(message, type); 195 194 } else { 196 console.warn(`[${type.toUpperCase()}] ${message}`);197 195 } 198 196 }, … … 227 225 // If no selected photos and no profile photos loaded yet, try to load from user_meta 228 226 if (Object.keys(this.selectedPhotos).length === 0) { 229 console.log('TAICS Photo Positions: No photos selected, loading from user_meta...');230 227 this.loadPhotosFromUserMeta(); 231 228 } -
technodrome-ai-content-assistant/trunk/features/layout-templates-tab/template-selector.js
r3401081 r3446048 18 18 this.bindEvents(); 19 19 this.loadInitialState(); 20 console.log('TAICS Template Selector module initialized');21 20 }, 22 21 … … 44 43 $selectedCard.addClass('selected'); 45 44 46 console.log(`Template changed to: ${templateId}`);47 45 $(document).trigger('taics_template_changed', [templateId]); 48 46 … … 121 119 this.$container.off('.taics-template-selector'); 122 120 } 123 console.log('TAICS Template Selector module cleaned up');124 121 } 125 122 }; -
technodrome-ai-content-assistant/trunk/features/layout-templates-tab/video-manager.js
r3421276 r3446048 24 24 */ 25 25 init: function() { 26 console.log('[VIDEO MANAGER] Initializing simplified version...');27 26 this.bindEvents(); 28 27 this.loadVideoData(); 29 console.log('[VIDEO MANAGER] Initialized.');30 28 }, 31 29 … … 41 39 const slot = $input.data('slot'); 42 40 const url = $input.val(); 43 console.log(`[VIDEO MANAGER] URL input changed for slot ${slot}:`, url);44 41 self.videoData[`video-url-${slot}`] = url; 45 42 }); … … 49 46 const slot = $input.data('slot'); 50 47 const title = $input.val(); 51 console.log(`[VIDEO MANAGER] Title input changed for slot ${slot}:`, title);52 48 self.videoData[`video-title-${slot}`] = title; 53 49 }); … … 57 53 const $input = $(e.target); 58 54 const slot = $input.data('slot'); 59 console.log(`[VIDEO MANAGER] Blur detected on slot ${slot}. Triggering autosave.`);60 55 // AUTOSAVE REVOLUCIJA: Triggeruj autosave event umesto direktnog čuvanja 61 56 $(document).trigger('taics_video_changed.autosave', [slot, self.getValue()]); … … 66 61 const $btn = $(e.target); 67 62 const slot = $btn.data('slot'); 68 console.log(`[VIDEO MANAGER] Remove clicked for slot ${slot}.`);69 63 self.removeVideo(slot); 70 64 }); 71 65 72 console.log('[VIDEO MANAGER] Events bound.');73 66 }, 74 67 … … 78 71 */ 79 72 loadVideoData: function() { 80 console.log('[VIDEO MANAGER] v3.4.0: Videos are loaded from profile via AutoSave system');81 73 // Initialize with empty data - profile will be loaded separately 82 74 this.videoData = { … … 94 86 */ 95 87 saveVideoData: function() { 96 console.log('[VIDEO MANAGER] v3.4.0: Videos saved through AutoSave system');97 88 // Saving is now handled by AutoSave, this method is kept for compatibility 98 89 }, … … 103 94 updateUI: function() { 104 95 const self = this; 105 console.log('[VIDEO MANAGER] Updating UI with data:', self.videoData);106 96 107 97 for (let i = 1; i <= 2; i++) { … … 183 173 */ 184 174 removeVideo: function(slot) { 185 console.log(`[VIDEO MANAGER] Removing video from slot ${slot}.`);186 175 this.videoData[`video-url-${slot}`] = ''; 187 176 this.videoData[`video-title-${slot}`] = ''; … … 280 269 281 270 })(jQuery); 282 console.log('[VIDEO MANAGER] CRITICAL: Adding high-priority card click handler.'); -
technodrome-ai-content-assistant/trunk/features/layout-templates-tab/video-slot-transformation.js
r3434643 r3446048 21 21 22 22 $(document).ready(function() { 23 console.log('[VIDEO TRANSFORM] Initializing video slot transformation...');24 23 25 24 var $generationMode = $('#taics-generation-mode'); … … 46 45 */ 47 46 function transformForVideoUrlContext() { 48 console.log('[VIDEO TRANSFORM] Applying Video URL Context transformation');49 47 50 48 // Slot 1 - ACTIVE with new name … … 87 85 ); 88 86 89 console.log('[VIDEO TRANSFORM] Video URL Context transformation complete');90 87 } 91 88 … … 94 91 */ 95 92 function transformForVideoChannelContext() { 96 console.log('[VIDEO TRANSFORM] Applying Video Channel Context transformation');97 93 98 94 // Slot 1 - LOCKED with info … … 132 128 } 133 129 134 console.log('[VIDEO TRANSFORM] Video Channel Context transformation complete');135 130 } 136 131 … … 139 134 */ 140 135 function resetSlotsToNormal() { 141 console.log('[VIDEO TRANSFORM] Resetting slots to normal mode');142 136 143 137 // Remove any UNDER CONSTRUCTION overlays … … 163 157 $slot2.find('.taics-locked-info').remove(); 164 158 165 console.log('[VIDEO TRANSFORM] Slots reset to normal mode');166 159 } 167 160 … … 171 164 function applyTransformation() { 172 165 var currentMode = $generationMode.val(); 173 console.log('[VIDEO TRANSFORM] Current generation mode:', currentMode);174 166 175 167 if (currentMode === VIDEO_URL_CONTEXT_MODE) { … … 191 183 */ 192 184 function initializeTransformation() { 193 console.log('[VIDEO TRANSFORM] Initializing transformation...');194 185 applyTransformation(); 195 186 } … … 202 193 // Listen for profile loaded/changed events 203 194 $(document).on('taics_profile_loaded taics_profile_changed', function(e, profileNumber) { 204 console.log('[VIDEO TRANSFORM] Profile loaded/changed:', profileNumber);205 195 // Small delay to ensure generation mode is set from profile 206 196 setTimeout(function() { … … 211 201 // Listen for video context mode changed events from video-context-mode.js 212 202 $(document).on('taics:videoContextModeChanged taics_videoContextModeChanged', function(e, mode) { 213 console.log('[VIDEO TRANSFORM] Video context mode changed event:', mode);214 203 if (mode) { 215 204 // Mode was set, apply transformation … … 243 232 // Handle window load event 244 233 $(window).on('load', function() { 245 console.log('[VIDEO TRANSFORM] Window loaded, re-applying transformation');246 234 applyTransformation(); 247 235 }); … … 252 240 }; 253 241 254 console.log('[VIDEO TRANSFORM] Video slot transformation initialized successfully');255 242 }); 256 243 -
technodrome-ai-content-assistant/trunk/features/notifications.js
r3379631 r3446048 17 17 }, 300); 18 18 19 console.log('TAICS_Notifications initialized');20 19 }, 21 20 … … 244 243 } 245 244 this.isInitialized = false; 246 console.log('TAICS_Notifications cleaned up');247 245 }, 248 246 -
technodrome-ai-content-assistant/trunk/includes/class-ajax-handler.php
r3444453 r3446048 403 403 $input_data = $decoded; 404 404 } else { 405 if (defined('WP_DEBUG') && WP_DEBUG) {406 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log407 error_log('TAICS AutoSave - JSON decode failed: ' . json_last_error_msg());408 }409 405 wp_send_json_error(array( 410 406 'message' => 'Invalid profile data format' … … 413 409 } 414 410 } else { 415 if (defined('WP_DEBUG') && WP_DEBUG) {416 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log417 error_log('TAICS AutoSave - profile_data missing from POST');418 }419 411 wp_send_json_error(array('message' => 'Profile data missing')); 420 412 return; … … 423 415 // Nonce verification - iz $_POST jer je FormData 424 416 if (empty($_POST['nonce'])) { 425 if (defined('WP_DEBUG') && WP_DEBUG) {426 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log427 error_log('TAICS AutoSave - Nonce missing from POST');428 }429 417 wp_send_json_error(array('message' => 'Nonce missing')); 430 418 return; … … 432 420 433 421 $nonce_result = wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'taics_ajax_nonce'); 434 if (defined('WP_DEBUG') && WP_DEBUG) {435 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log436 error_log('TAICS AutoSave - Nonce verification result: ' . $nonce_result);437 }438 422 439 423 if (!$nonce_result) { 440 if (defined('WP_DEBUG') && WP_DEBUG) {441 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log442 error_log('TAICS AutoSave - Nonce verification FAILED');443 }444 424 wp_send_json_error(array('message' => 'Security verification failed')); 445 425 return; … … 800 780 $profile_manager = new TAICS_Profile_Manager(); 801 781 $profiles = $profile_manager->get_all_profiles(); 802 782 803 783 wp_send_json_success($profiles); 804 784 805 785 } catch (Exception $e) { 806 786 wp_send_json_error(esc_html__('Failed to load profiles', 'technodrome-ai-content-assistant')); -
technodrome-ai-content-assistant/trunk/includes/class-profile-manager.php
r3444453 r3446048 251 251 return []; 252 252 } 253 253 254 254 if (!$user_id) { 255 255 $user_id = get_current_user_id(); … … 258 258 return []; 259 259 } 260 260 261 261 $meta_key = self::PROFILE_META_PREFIX . $profile_number; 262 262 $profile_data = get_user_meta($user_id, $meta_key, true); 263 263 if (!$profile_data) { 264 return self::$default_profile; // FIXED: Return default profile if not found 264 // v4.0.4 KRITIČNO FIX: Vrati KOPIJU default profila, ne referencu! 265 // Bez ovoga, svi profili dijele istu referencu na self::$default_profile 266 // što uzrokuje da promjena u jedan profil utiče na sve ostale 267 return json_decode(json_encode(self::$default_profile), true); 265 268 } 266 269 … … 268 271 $decoded = json_decode($profile_data, true); 269 272 if (json_last_error() !== JSON_ERROR_NONE) { 270 return self::$default_profile; // FIXED: Return default profile on JSON decode error 273 // v4.0.4 KRITIČNO FIX: Vrati KOPIJU default profila, ne referencu! 274 return json_decode(json_encode(self::$default_profile), true); 271 275 } 272 276 return $decoded; … … 274 278 return $profile_data; 275 279 } else { 276 return self::$default_profile; // FIXED: Return default profile for unexpected data type 280 // v4.0.4 KRITIČNO FIX: Vrati KOPIJU default profila, ne referencu! 281 return json_decode(json_encode(self::$default_profile), true); 277 282 } 278 283 } -
technodrome-ai-content-assistant/trunk/readme.txt
r3444453 r3446048 5 5 Tested up to: 6.9 6 6 Requires PHP: 8.0 7 Stable tag: 4.0. 37 Stable tag: 4.0.4 8 8 License: GPL v2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 46 46 * **IMPROVED**: Better control flow for profile data synchronization 47 47 48 = 4.0.4 (2026-01-21) = 49 * **FIX**: Structure Name field now clears properly when switching profiles 50 * **FIX**: Profile button labels update correctly with saved custom names 51 * **IMPROVED**: Better event listener timing for profile data synchronization 48 = 4.0.4 (2026-01-22) = 49 * **NEW**: Video slot renamed to "GENERATE AI CONTENT FROM VIDEO URL" in AI + Video URL mode 50 * **NEW**: Video slot renamed to "GENERATE AI CONTENT FROM VIDEO CHANNEL" in AI + Video Channel mode 51 * **NEW**: UNDER CONSTRUCTION overlay on Slot 2 when in Video URL mode (channel not yet implemented) 52 * **NEW**: UNDER CONSTRUCTION overlay on Slot 2 when in Video Channel mode 53 * **IMPROVED**: Video slot transformation only applies in video context modes 52 54 53 55 = 4.0.3 (2026-01-21) = … … 89 91 == Upgrade Notice == 90 92 91 = 4.0. 3=92 Important bug fix - Default Tone field now saves correctly with AutoSave. Recommended for all users.93 = 4.0.4 = 94 Video slot renaming and UNDER CONSTRUCTION overlay for video context modes. 93 95 94 96 = 4.0.2 = -
technodrome-ai-content-assistant/trunk/technodrome-ai-content-assistant.php
r3444453 r3446048 4 4 * Plugin URI: https://technodrome.org/ai-content-assistant 5 5 * Description: Advanced AI content generation plugin with multiple AI providers, profile system, layout templates, and content rules for WordPress. 6 * Version: 4.0. 36 * Version: 4.0.4 7 7 * Author: Technodrome Team 8 8 * Author URI: https://technodrome.org … … 30 30 31 31 // Plugin constants 32 define('TAICS_VERSION', '4.0. 3');32 define('TAICS_VERSION', '4.0.4'); 33 33 define('TAICS_PLUGIN_FILE', __FILE__); 34 34 define('TAICS_PLUGIN_DIR', plugin_dir_path(__FILE__));
Note: See TracChangeset
for help on using the changeset viewer.