From 7c40714d480126c6d80334a3680d8d83aae3e63a Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Thu, 11 Sep 2025 17:35:33 +0300 Subject: [PATCH] deployment --- DEPLOYMENT.md | 105 +++++++ README.md | 50 ++-- marketing/BLENDERMARKET_LISTING.md | 15 +- marketing/GUMROAD_LISTING.md | 2 +- .../__pycache__/preset_ops.cpython-311.pyc | Bin 0 -> 47474 bytes src/operators/preset_ops.py | 130 ++------- .../storage_system.cpython-311.pyc | Bin 0 -> 31970 bytes src/presets/storage_system.py | 257 ++++++++++++------ .../property_groups.cpython-311.pyc | Bin 26347 -> 26502 bytes src/properties/property_groups.py | 12 +- src/ui/__pycache__/panels.cpython-311.pyc | Bin 43879 -> 42622 bytes src/ui/panels.py | 100 +++---- 12 files changed, 387 insertions(+), 284 deletions(-) create mode 100644 DEPLOYMENT.md create mode 100644 src/operators/__pycache__/preset_ops.cpython-311.pyc create mode 100644 src/presets/__pycache__/storage_system.cpython-311.pyc diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..7b42e9a --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,105 @@ +# Deployment Guide + +Quick instructions for deploying a new version of the Text Texture Generator addon. + +## Prerequisites + +- Git repository with proper tags +- Python 3.10+ +- All changes committed and pushed to main branch + +## Deployment Steps + +### 1. Create Version Tag + +```bash +# Create and push a version tag +git tag v1.1.0 +git push origin v1.1.0 +``` + +### 2. Generate Changelog (Optional) + +```bash +# Generate changelog for the new version +python3 build/generate_changelog.py --version 1.1.0 --output CHANGELOG_v1.1.0.md + +# Or generate full changelog +python3 build/generate_changelog.py --full --output CHANGELOG.md +``` + +### 3. Build Packages + +```bash +# Build both versions (recommended) +python3 build/build_addon.py --type all --version 1.1.0 + +# Or build individually: +# python3 build/build_addon.py --type full --version 1.1.0 +# python3 build/build_addon.py --type free --version 1.1.0 +``` + +### 4. Validate Packages + +```bash +# Validate the generated packages +python3 build/validate_package.py dist/text_texture_generator_v1.1.0.zip --type full +python3 build/validate_package.py dist/text_texture_generator_v1.1.0_free.zip --type free +``` + +### 5. Deploy + +Upload the validated packages from the `dist/` directory: + +- `text_texture_generator_v1.1.0.zip` (Full version) +- `text_texture_generator_v1.1.0_free.zip` (Free version) + +## Build System Overview + +The deployment uses a sophisticated build-time file exclusion system: + +- **Version Sync**: [`sync_version.py`](build/sync_version.py) automatically updates version information from git tags +- **Package Build**: [`build_addon.py`](build/build_addon.py) creates separate packages with different feature sets +- **Validation**: [`validate_package.py`](build/validate_package.py) ensures package integrity +- **Changelog**: [`generate_changelog.py`](build/generate_changelog.py) creates release notes from git history + +## Configuration Files + +- [`build/config.json`](build/config.json) - Project metadata and build settings +- [`build/file_exclusions.json`](build/file_exclusions.json) - Defines which files are excluded from free builds + +## Troubleshooting + +### No Git Tags Found + +If version sync fails, ensure you have created and pushed the version tag: + +```bash +git tag -l # List existing tags +git tag v1.1.0 # Create new tag +git push origin v1.1.0 # Push to remote +``` + +### Build Validation Fails + +Check the validation output for specific errors: + +```bash +python3 build/validate_package.py dist/your_package.zip --type full +``` + +### Version Mismatch + +The build system automatically syncs versions from git tags. If you need to override: + +```bash +python3 build/sync_version.py --version 1.1.0 --type full +``` + +## Quick Deploy Command + +For experienced users, deploy in one command: + +```bash +git tag v1.1.0 && git push origin v1.1.0 && python3 build/build_addon.py --type all --version 1.1.0 && python3 build/validate_package.py dist/text_texture_generator_v1.1.0.zip && python3 build/validate_package.py dist/text_texture_generator_v1.1.0_free.zip +``` diff --git a/README.md b/README.md index 0bc1039..9a862bd 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,12 @@ A modern Blender addon that generates image textures from text with extensive cu - **Preset Management System**: - - Three-tier storage: Blend File, Persistent, Local presets - - All preset types now display properly in the interface - - Presets travel with .blend files (Blend File type) + - Simplified two-tier storage: Persistent File (default) + Blend File (optional) + - Unified preset list with clear storage indicators (🏠/📄) + - Presets travel with .blend files when "Save with .blend file" is checked - Import/export functionality for backup and sharing - - Automatic migration system for addon updates - - Conflict resolution for preset imports + - Automatic migration from legacy three-tier system + - Conflict resolution with blend file presets taking priority - **Preview System**: @@ -254,33 +254,39 @@ The addon automatically installs required dependencies: ## Preset System -The addon features a sophisticated three-tier preset system: +The addon features a streamlined two-tier preset system designed for simplicity and reliability: ### Storage Locations (Priority Order) -1. **Blend File Storage** (Highest Priority) +1. **Blend File Storage** (Higher Priority) - Presets stored in scene custom properties - - Travel with your .blend file + - Travel with your .blend file automatically - Perfect for project-specific presets + - Display with 📄 indicator in UI -2. **Persistent File Storage** (Medium Priority) +2. **Persistent File Storage** (Default Storage) - Survives addon updates and Blender upgrades - - Stored in Blender's user config directory + - Stored in Blender's user config directory (`~/.config/blender/text_texture_generator/presets/`) - Shared across all blend files + - Display with 🏠 indicator in UI -3. **Local File Storage** (Lowest Priority) - - Local addon-specific presets - - Automatically migrated to persistent storage when needed +### Key Features -### Preset Features - -- **Improved UI Display**: Recent bug fix ensures all three preset types (Blend File, Persistent, Local) display properly in the interface -- **Auto-Migration**: Seamlessly upgrades presets during addon updates +- **Simplified UI**: Single unified preset list with clear storage indicators +- **Easy Storage Selection**: Simple "Save with .blend file" checkbox controls destination +- **Priority System**: Blend file presets automatically override persistent ones with same name +- **Auto-Migration**: Seamlessly migrates old three-tier presets to new system - **Import/Export**: Backup and share presets across installations -- **Conflict Resolution**: Smart handling of duplicate preset names -- **Migration Reports**: Detailed logs of preset migration operations +- **User-Friendly Messages**: Clear, non-technical confirmation messages + +### How to Use + +- **Default Behavior**: Presets save to persistent storage (🏠) - survives addon updates +- **Project-Specific**: Check "Save with .blend file" to save with your project (📄) +- **Loading**: All presets appear in one list, blend file presets take priority +- **Migration**: Old presets are automatically migrated on first use ## Build System @@ -393,7 +399,7 @@ Marc Mintel - Normal map generation from text alpha channels with strength/blur controls - Advanced shader generation with Principled BSDF, Emission, and Transparent types - Light path controls for shadows, reflections, and backfacing geometry -- Three-tier preset system (blend file → persistent → legacy) with auto-migration +- Simplified two-tier preset system (persistent + blend file) with priority handling - Live preview with zoomable Image Editor integration and multiple backgrounds - Prepend/append text support with horizontal/vertical layouts - Custom font support with mixed-case validation and fallback systems @@ -416,7 +422,7 @@ Marc Mintel #### Full Version Features (Additional): - Normal map generation from text alpha channels -- Complete preset management system with import/export +- Complete preset management system with simplified two-tier storage - Advanced utility operations and batch processing - Extended UI components and advanced styling options - **File Count**: 23 files (60.1 KB package size) @@ -433,6 +439,6 @@ Marc Mintel - `core/normal_maps.py` - Normal map generation algorithms - `operators/preset_ops.py` - Preset management operations - `operators/utility_ops.py` - Batch processing and utility functions -- `presets/` - Entire preset storage and migration system +- `presets/` - Entire simplified preset storage and migration system This approach ensures the free version provides genuine value while the full version offers substantially more capabilities through additional source code modules. diff --git a/marketing/BLENDERMARKET_LISTING.md b/marketing/BLENDERMARKET_LISTING.md index fa4ceca..4243a45 100644 --- a/marketing/BLENDERMARKET_LISTING.md +++ b/marketing/BLENDERMARKET_LISTING.md @@ -43,21 +43,22 @@ Cross-platform compatibility across Windows, macOS, and Linux environments, comb - **Memory Management**: Efficient processing for large textures without system overload - **Batch Processing**: Multiple texture generation with consistent settings - ### Version Strategy #### **🆓 Free Version - Full Core Experience** + - **Complete Text Rendering Pipeline**: Professional typography with up to 1024×1024 resolution -- **Essential Positioning System**: 9-point anchor positioning with pixel-perfect alignment +- **Essential Positioning System**: 9-point anchor positioning with pixel-perfect alignment - **Seamless Blender Integration**: Automatic material node creation and assignment - **Professional Font Support**: System font detection and custom font file compatibility - **Full Color Control**: Complete RGBA customization with transparency support - **No Time Limits**: Use forever - perfect for testing and smaller projects #### **⭐ Full Version - Unleash Professional Power** + - **Ultra-High Resolution**: Generate stunning 4096×4096 textures for commercial work - **Advanced 3D Effects**: Automatic normal map generation with customizable depth and blur -- **Complete Preset Ecosystem**: Three-tier preset management with import/export for team workflows +- **Complete Preset Ecosystem**: Simplified two-tier preset management with import/export for team workflows - **Professional Batch Processing**: Advanced utility operations and workflow automation tools - **Extended Material Pipeline**: Advanced styling options and specialized production tools - **Future-Proof Investment**: All upcoming premium features included with purchase @@ -66,12 +67,14 @@ Cross-platform compatibility across Windows, macOS, and Linux environments, comb ### Smart Preset Management -- **Three-Tier Preset System**: Blend File, Persistent, and Local preset storage with proper UI display +- **Simplified Two-Tier System**: Streamlined Persistent (default) and Blend File storage with unified UI +- **Intuitive Storage Selection**: Simple "Save with .blend file" checkbox controls destination +- **Clear Visual Indicators**: 🏠 (persistent) and 📄 (blend file) icons for instant recognition +- **Priority System**: Blend file presets automatically override persistent ones with same name - **User-Defined Presets**: Save and organize custom configurations with improved interface - **Project-Specific Presets**: Maintain consistent styling across project assets - **Preset Import/Export**: Share configurations between team members -- **Version Control**: Preset versioning for workflow tracking -- **Enhanced UI Display**: Recent improvements ensure all preset types display correctly in the interface +- **Auto-Migration**: Seamlessly migrates old three-tier presets to new streamlined system ### Material Integration diff --git a/marketing/GUMROAD_LISTING.md b/marketing/GUMROAD_LISTING.md index e3e0406..8f1e0cb 100644 --- a/marketing/GUMROAD_LISTING.md +++ b/marketing/GUMROAD_LISTING.md @@ -44,7 +44,7 @@ From indie game developers to 3D artists and creative hobbyists, this tool saves - **Extended Resolution**: Generate up to 4096×4096 high-resolution textures - **Normal Map Generation**: Automatic height-to-normal conversion for realistic 3D depth and surface detail -- **Advanced Preset Management**: Complete preset system with import/export, sharing across three storage types (Blend File, Persistent, Local) +- **Advanced Preset Management**: Simplified two-tier preset system with import/export, easy storage selection with clear visual indicators - **Batch Processing**: Process multiple textures with consistent settings and advanced utility operations - **Professional Effects Suite**: Extended styling options and advanced material integration - **Premium Modules**: Additional specialized tools for professional workflows diff --git a/src/operators/__pycache__/preset_ops.cpython-311.pyc b/src/operators/__pycache__/preset_ops.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b275f0f427745732fa59af3f538b8393fc0a90fd GIT binary patch literal 47474 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 Persistent file - preset.source = 'BLEND_FILE' if blend_success else 'PERSISTENT_FILE' + preset.source = 'BLEND_FILE' if props.save_with_blend_file else 'PERSISTENT_FILE' else: - # Update source based on where it was successfully saved - existing_preset.source = 'BLEND_FILE' if blend_success else 'PERSISTENT_FILE' - - # Don't clear the input field - keep the name for easy re-saving - # props.new_preset_name = "New Preset" + # Update source based on where it was saved + existing_preset.source = 'BLEND_FILE' if props.save_with_blend_file else 'PERSISTENT_FILE' # Provide clear feedback - storage_info = "" - if blend_success and persistent_success: - storage_info = " (saved to blend file + persistent storage)" - elif blend_success: - storage_info = " (saved to blend file)" - elif persistent_success: - storage_info = " (saved to persistent storage)" + location = "with .blend file" if props.save_with_blend_file else "to persistent storage" if self.overwrite: - self.report({'INFO'}, f"✅ Preset '{preset_name}' updated successfully{storage_info}") + self.report({'INFO'}, f"✅ Preset '{preset_name}' updated {location}") else: - self.report({'INFO'}, f"✅ Preset '{preset_name}' saved successfully{storage_info}") + self.report({'INFO'}, f"✅ Preset '{preset_name}' saved {location}") except (OSError, IOError, json.JSONEncodeError) as e: self.report({'ERROR'}, f"Failed to save preset: {str(e)}") @@ -301,57 +257,17 @@ class TEXT_TEXTURE_OT_load_preset(Operator): print(f"[PRESET LOAD DEBUG] shader_connection: {props.shader_connection}") try: - # Three-tier loading priority: Blend File > Persistent File > Legacy File - blend_presets = get_blend_file_presets() - preset_data = None - source = 'BLEND_FILE' + # Get unified preset list and find the one to load + all_presets = get_unified_preset_list() - print(f"[PRESET LOAD DEBUG] Available blend file presets: {list(blend_presets.keys())}") - - if self.preset_name in blend_presets: - preset_data = blend_presets[self.preset_name] - source = 'BLEND_FILE' - print(f"[PRESET LOAD DEBUG] Loading preset '{self.preset_name}' from blend file") - print(f"[PRESET LOAD DEBUG] Blend file preset shader properties:") - print(f"[PRESET LOAD DEBUG] disable_shadows: {preset_data.get('disable_shadows', 'NOT FOUND')}") - print(f"[PRESET LOAD DEBUG] disable_reflections: {preset_data.get('disable_reflections', 'NOT FOUND')}") - print(f"[PRESET LOAD DEBUG] disable_backfacing: {preset_data.get('disable_backfacing', 'NOT FOUND')}") - else: - # Try persistent storage next - persistent_file = os.path.join(get_persistent_preset_path(), f"{self.preset_name}.json") - print(f"[PRESET LOAD DEBUG] Preset not in blend file, trying persistent file: {persistent_file}") - if os.path.exists(persistent_file): - with open(persistent_file, 'r') as f: - preset_data = json.load(f) - source = 'PERSISTENT_FILE' - print(f"[PRESET LOAD DEBUG] Loading preset '{self.preset_name}' from persistent file") - print(f"[PRESET LOAD DEBUG] Persistent file preset shader properties:") - print(f"[PRESET LOAD DEBUG] disable_shadows: {preset_data.get('disable_shadows', 'NOT FOUND')}") - print(f"[PRESET LOAD DEBUG] disable_reflections: {preset_data.get('disable_reflections', 'NOT FOUND')}") - print(f"[PRESET LOAD DEBUG] disable_backfacing: {preset_data.get('disable_backfacing', 'NOT FOUND')}") - else: - # Fallback to legacy local file - legacy_file = os.path.join(get_preset_path(), f"{self.preset_name}.json") - print(f"[PRESET LOAD DEBUG] Preset not in persistent storage, trying legacy file: {legacy_file}") - if os.path.exists(legacy_file): - with open(legacy_file, 'r') as f: - preset_data = json.load(f) - source = 'LOCAL_FILE' - print(f"[PRESET LOAD DEBUG] Loading preset '{self.preset_name}' from legacy file") - print(f"[PRESET LOAD DEBUG] Legacy file preset shader properties:") - print(f"[PRESET LOAD DEBUG] disable_shadows: {preset_data.get('disable_shadows', 'NOT FOUND')}") - print(f"[PRESET LOAD DEBUG] disable_reflections: {preset_data.get('disable_reflections', 'NOT FOUND')}") - print(f"[PRESET LOAD DEBUG] disable_backfacing: {preset_data.get('disable_backfacing', 'NOT FOUND')}") - else: - print(f"[PRESET LOAD DEBUG] Legacy file does not exist: {legacy_file}") - self.report({'ERROR'}, f"Preset not found in any storage location: {self.preset_name}") - return {'CANCELLED'} - - if not preset_data: - print(f"[PRESET LOAD DEBUG] No preset data found!") - self.report({'ERROR'}, f"No data found for preset: {self.preset_name}") + if self.preset_name not in all_presets: + self.report({'ERROR'}, f"Preset '{self.preset_name}' not found") return {'CANCELLED'} + preset_data, source = all_presets[self.preset_name] + + print(f"[PRESET LOAD DEBUG] Loading preset '{self.preset_name}' from {source}") + props.is_updating = True # Skip loading text to prevent overwriting current text diff --git a/src/presets/__pycache__/storage_system.cpython-311.pyc b/src/presets/__pycache__/storage_system.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc08d7bdddd1ae511403a0cc6495856a02d4d223 GIT binary patch literal 31970 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 F{{szx#moQz literal 0 HcmV?d00001 diff --git a/src/presets/storage_system.py b/src/presets/storage_system.py index ddf4856..a705733 100644 --- a/src/presets/storage_system.py +++ b/src/presets/storage_system.py @@ -5,37 +5,31 @@ import time import platform # ============================================================================ -# PRESET STORAGE SYSTEM - V2.0 (PERSISTENT STORAGE) +# PRESET STORAGE SYSTEM - V3.0 (SIMPLIFIED TWO-TIER) # ============================================================================ # -# The Text Texture Generator now uses a THREE-TIER preset storage system -# that ensures preset persistence across addon updates: +# The Text Texture Generator uses a SIMPLIFIED TWO-TIER preset storage system: # -# 1. BLEND FILE STORAGE (Highest Priority) -# - Presets travel with the .blend file -# - Perfect for project-specific presets -# - Stored in scene custom properties -# -# 2. PERSISTENT FILE STORAGE (Medium Priority) +# 1. PERSISTENT FILE STORAGE (Default) # - Survives addon updates and Blender upgrades # - Stored in Blender's user config directory # - Shared across all blend files +# - Default behavior when checkbox is unchecked # -# 3. LEGACY FILE STORAGE (Lowest Priority) -# - Old system for backward compatibility -# - Vulnerable to addon updates -# - Will be automatically migrated to persistent storage +# 2. BLEND FILE STORAGE (Optional) +# - Presets travel with the .blend file +# - Perfect for project-specific presets +# - Stored in scene custom properties +# - Used when "Save with .blend file" checkbox is checked +# +# PRIORITY SYSTEM: +# - Blend file presets ALWAYS take priority over persistent ones +# - Name conflicts show only the blend file version # # MIGRATION SYSTEM: -# - Automatically detects addon version changes -# - Migrates legacy presets to persistent storage +# - Automatically migrates LOCAL_FILE presets to PERSISTENT_FILE storage # - Creates backups before migration -# - Provides detailed migration reports -# -# IMPORT/EXPORT SYSTEM: -# - Export all presets for manual backup -# - Import presets with conflict resolution -# - Supports cross-machine preset sharing +# - One-time process with safety checks # # ============================================================================ @@ -298,8 +292,157 @@ def delete_blend_file_preset(preset_name): print(f"Error deleting preset from blend file: {e}") return False +def migrate_from_legacy_system(): + """ + One-time migration from old three-tier system to simplified two-tier system. + Moves LOCAL_FILE presets to PERSISTENT_FILE and creates backup. + """ + try: + legacy_dir = get_preset_path() + persistent_dir = get_persistent_preset_path() + + # Check if legacy directory exists and is different from persistent + if not os.path.exists(legacy_dir) or legacy_dir == persistent_dir: + print("[TTG] No separate legacy presets directory found, migration not needed") + return True + + # Check for migration marker file to avoid duplicate migrations + migration_marker = os.path.join(persistent_dir, ".migration_complete") + if os.path.exists(migration_marker): + print("[TTG] Migration already completed previously") + return True + + # Find legacy preset files + legacy_files = [] + for filename in os.listdir(legacy_dir): + if filename.endswith('.json'): + legacy_files.append(filename) + + if not legacy_files: + print("[TTG] No legacy presets to migrate") + # Create marker even if no files to migrate + with open(migration_marker, 'w') as f: + f.write("Migration completed - no legacy presets found") + return True + + print(f"[TTG] Found {len(legacy_files)} legacy presets to migrate") + + # Create backup directory in persistent location + backup_dir = os.path.join(persistent_dir, "legacy_backup") + os.makedirs(backup_dir, exist_ok=True) + + # Load existing persistent presets to avoid conflicts + existing_persistent = set() + if os.path.exists(persistent_dir): + for filename in os.listdir(persistent_dir): + if filename.endswith('.json'): + existing_persistent.add(filename) + + migrated_count = 0 + skipped_count = 0 + + # Migrate each legacy preset + for filename in legacy_files: + preset_name = os.path.splitext(filename)[0] + legacy_file = os.path.join(legacy_dir, filename) + persistent_file = os.path.join(persistent_dir, filename) + backup_file = os.path.join(backup_dir, filename) + + try: + # Create backup copy first + import shutil + shutil.copy2(legacy_file, backup_file) + + # If preset doesn't exist in persistent storage, migrate it + if filename not in existing_persistent: + shutil.copy2(legacy_file, persistent_file) + migrated_count += 1 + print(f"[TTG] Migrated preset '{preset_name}' to persistent storage") + else: + skipped_count += 1 + print(f"[TTG] Skipped preset '{preset_name}' (already exists in persistent storage)") + + except Exception as e: + print(f"[TTG] Error migrating preset '{preset_name}': {e}") + + # Create migration completion marker + with open(migration_marker, 'w') as f: + migration_info = { + "migration_date": time.time(), + "migrated_count": migrated_count, + "skipped_count": skipped_count, + "backup_location": backup_dir + } + import json + json.dump(migration_info, f, indent=2) + + print(f"[TTG] ✅ Migration completed: {migrated_count} migrated, {skipped_count} skipped") + print(f"[TTG] Backup created at: {backup_dir}") + return True + + except Exception as e: + print(f"[TTG] ❌ Error during migration: {e}") + return False + +def get_unified_preset_list(): + """ + Get unified preset list with two-tier loading and priority rules. + Returns dict with preset names as keys and (preset_data, source) as values. + + Priority order: BLEND_FILE > PERSISTENT_FILE + Blend file presets always take priority over persistent ones. + """ + all_presets = {} + + try: + # Load persistent presets first (lower priority) + persistent_dir = get_persistent_preset_path() + if os.path.exists(persistent_dir): + for filename in os.listdir(persistent_dir): + if filename.endswith('.json') and not filename.startswith('.'): + preset_name = os.path.splitext(filename)[0] + try: + with open(os.path.join(persistent_dir, filename), 'r') as f: + preset_data = json.load(f) + all_presets[preset_name] = (preset_data, 'PERSISTENT_FILE') + except (json.JSONDecodeError, OSError) as e: + print(f"[TTG] Error reading persistent preset {preset_name}: {e}") + + # Load blend file presets and override any conflicts (higher priority) + blend_presets = get_blend_file_presets() + for name, data in blend_presets.items(): + all_presets[name] = (data, 'BLEND_FILE') + + except Exception as e: + print(f"[TTG] Error in get_unified_preset_list: {e}") + + return all_presets + +def save_preset_unified(preset_data, name, save_to_blend_file=False): + """ + Save a preset to either persistent file or blend file based on user choice. + Default behavior saves to persistent file. + """ + if save_to_blend_file: + return save_blend_file_preset(name, preset_data) + else: + # Save to persistent file + try: + persistent_dir = get_persistent_preset_path() + persistent_file = os.path.join(persistent_dir, f"{name}.json") + + with open(persistent_file, 'w') as f: + json.dump(preset_data, f, indent=2) + + print(f"[TTG] Saved preset '{name}' to persistent storage") + return True + + except Exception as e: + print(f"[TTG] Error saving preset to persistent storage: {e}") + return False + def initialize_presets(): - """Initialize presets with simplified, reliable synchronization""" + """Initialize the simplified preset system and run migration if needed.""" try: import bpy if not hasattr(bpy.context, 'scene') or not bpy.context.scene: @@ -311,80 +454,26 @@ def initialize_presets(): print(f"[TTG] Starting preset initialization...") + # Run one-time migration from old system + migrate_from_legacy_system() + # Clear existing presets to avoid duplicates props.presets.clear() - # Collect all presets from all sources - all_presets = {} - - # 1. Load from blend file (highest priority - travels with file) - try: - blend_presets = get_blend_file_presets() - for name, data in blend_presets.items(): - all_presets[name] = { - 'data': data, - 'source': 'BLEND_FILE' - } - print(f"[TTG] Found {len(blend_presets)} presets in blend file") - except Exception as e: - print(f"[TTG] Error loading blend file presets: {e}") - - # 2. Load from persistent storage (survives addon updates) - try: - persistent_dir = get_persistent_preset_path() - if os.path.exists(persistent_dir): - for filename in os.listdir(persistent_dir): - if filename.endswith('.json'): - preset_name = os.path.splitext(filename)[0] - # Only add if not already in blend file (blend file has priority) - if preset_name not in all_presets: - try: - with open(os.path.join(persistent_dir, filename), 'r') as f: - preset_data = json.load(f) - all_presets[preset_name] = { - 'data': preset_data, - 'source': 'PERSISTENT_FILE' - } - except (json.JSONDecodeError, OSError) as e: - print(f"[TTG] Error reading persistent preset {preset_name}: {e}") - print(f"[TTG] Found {len([p for p in all_presets.values() if p['source'] == 'PERSISTENT_FILE'])} persistent presets") - except Exception as e: - print(f"[TTG] Error loading persistent presets: {e}") - - # 3. Load from legacy storage (backward compatibility) - try: - legacy_dir = get_preset_path() - if os.path.exists(legacy_dir) and legacy_dir != get_persistent_preset_path(): - for filename in os.listdir(legacy_dir): - if filename.endswith('.json'): - preset_name = os.path.splitext(filename)[0] - # Only add if not already found in higher priority sources - if preset_name not in all_presets: - try: - with open(os.path.join(legacy_dir, filename), 'r') as f: - preset_data = json.load(f) - all_presets[preset_name] = { - 'data': preset_data, - 'source': 'LOCAL_FILE' - } - except (json.JSONDecodeError, OSError) as e: - print(f"[TTG] Error reading legacy preset {preset_name}: {e}") - print(f"[TTG] Found {len([p for p in all_presets.values() if p['source'] == 'LOCAL_FILE'])} legacy presets") - except Exception as e: - print(f"[TTG] Error loading legacy presets: {e}") + # Get unified preset list + all_presets = get_unified_preset_list() # Add all presets to UI list - for preset_name, preset_info in all_presets.items(): + for preset_name, (preset_data, source) in all_presets.items(): preset = props.presets.add() preset.name = preset_name - preset.source = preset_info['source'] + preset.source = source # Report results blend_count = len([p for p in props.presets if p.source == 'BLEND_FILE']) persistent_count = len([p for p in props.presets if p.source == 'PERSISTENT_FILE']) - legacy_count = len([p for p in props.presets if p.source == 'LOCAL_FILE']) - print(f"[TTG] ✅ Initialized {len(props.presets)} presets ({blend_count} blend file, {persistent_count} persistent, {legacy_count} legacy)") + print(f"[TTG] ✅ Initialized {len(props.presets)} presets ({blend_count} blend file, {persistent_count} persistent)") except Exception as e: print(f"[TTG] Critical error in initialize_presets: {e}") diff --git a/src/properties/__pycache__/property_groups.cpython-311.pyc b/src/properties/__pycache__/property_groups.cpython-311.pyc index 0721841162a7c804357e330ff92261e2bbe917da..944b7563e92a5b7eea84ecf0aa312fdc7eb05a90 100644 GIT binary patch delta 1857 zcmZ`)drVVj6u;js#aif#QfhgYM_(!onXm`UjX4F>pj%a9#MQRnr7f@4w!jq0Mi$Ky zvrKZ5jl@5eLFWFj#F%TAC37+EKjW5#xCdp9xff$&F0k;=Ub6Gu7P#|qli#`Lp7Z<8 zIlu4wn$CYpPJc$U?`bq@4xj299|z&&CGDL&J6%uJ0iI?hvd?F5+yzAH5tLNZfG*X# zIhPJf%>eJ4FZ0oEnAjkYSHWm$t-GD=pURFcC>1*Of{HVgE$CF5%ta2(5;F^&N|`bk zOM4iDbCxo`P%mD!C{)S}dXs(*Bl)HsU$AaGCur!G^s4|zr^X0PCZ_M3>g!eBE&2`e zW{~&o3J*%ljO#M!cl3MO4#)D1wLhfU+4M*H6K$habT;wxc_k`Eq#jf{mu6+qU+Ax? zm6rK*Oih2Icc_(KO#Ggj1umAo0+4Ui{JpyBANntZi_ArLSGz5rC6uFWXbFNxDBw(a zi7p#GB_cC53yc)x!S(Vy7;&0msoV(VE)x`2sMcp7M~l$G8&1mseY&m&BL+lHfJ18> zW3_XItVI32iZE_knFakkgqhBw`bt_&hlQ-OB(VXA(+HOnw&BrF3fF|glv+8 zY<`H4%JdqxB~^$=@PKf;EY-4Ew_v=6?xlP2{V1E&vQ?I?#b{kR`xwibAD3mH!0eOK z%6hg9aLAs=j=}J>tnV|(X=I8`&&pPA$LMni1)Q@NICi8n?2vXMnj~$_lD3vLZJ=|Q zG&;=hM(m;csTZzU^P$lpb%kC>YLB1C>8wIq4RsQrVG)Xx|QhtPl&lkHbO6E!;!=U$@XptsS9m;lyq!91n(KR$Lns;wefzC|Lb{(WuZHm(#7G-dJ4l zQfs)w8WEzg5Pl&e77s_g0bx2fhCMv2hevARky_~7K`({or|yzZplRC{(jy+<_7+cu zL__0*5|v%u`4J(*Fx2cO@4#ep2^ojQW-EDD%xU?9C*z`W&+9xH9*0(57h!Ulxq^rd zievlEsC8Pdz%jacN>eeF>z&e@U{`yISb5mPtCP9zDZMe7txJk;9x3M6F^_$G=(*05 zB%Ja!NY(}OJ|lX|z9FeOANJJEb0@9@?`1f+Z8LeDo(WSLi WMW=>QA8bc^7NyM6L-J7@D*O+X@Dtbo delta 1878 zcmah~Z%kWN6o0o+kd{(5N?Tyk0;9A#5atFdh!PyEvjP(Y&42+tZ){M8w4gGUZL$PE z7!$H<;yxO-)Wmh!1{gkDVx8<(%!LHOcI_GLm%y*AA2wDmfh1UQOXn}Q_LJql%zAj@*^85wF)j-nk8Qv z&vS4-?RdY|moR4vG}hJx_SX3WI}VuwgTd~QyY4_pK2+EgmLu}8J2E^NmdPCwJE2jx zHzfCV2S?qJ(a5ko;0yQjKBvrH>5erC5>{g}E)I7?%y!G!JXMB`e=> z&B{NpJC(_-`Q)P(`kZE<{0k`mGWOLP<#D0hAjuY*r8)4v2Jf47c;9NgMEZ`tr}mgB znt&Mq%MWI7Rb!;m4|I*%sD!S^ex#@oG+>qX(`up{^z*uje$f~v`ju{yo6g1N*U0{D zHOnpfouALnFkaCUA%i-Ha9jb+Hni~G{D*`!KP0!*bV&c99sjNjk+2c zh;4Ko!zrdxdQ~=0vRG#HKIj5;y6oB+k<Wj=Y(@OAM%cDO*sdd#gHWNKe`I+8qTxq%r5{6F zi92B9PLKM{h!RieGCYa629QvuK^VzFL3ZiT-S@KXzQ z^9FaeD85Tq{At7iQT#-aTDuEzleV7dpczS7ZI0fb_ z&60PNNz$By-(e2rg742nHlds=Ih(*vDIHtK3@pZHw_Rq_{LJBAzhGpa@UhCK zq$|h}zQQk8b|uXScZOF~<+3nut-7k5e>{P)OZ-B089S)lsD8`9jwnZJ&Ki+nOZ`R0 z#`(>i&1{NS`(5lb@ASJ_R5|MZ!@y1};lLXPHa^v8unvi=!dwI*&nVX2ubY&Q_v|&8 z;VrK_ zv+8O&8{%)u{cLD@Uk>Zv!0->U)|@O}5UY#-ZFzzgL0#XT99U3y7VokjE<{iN0+r4K Ag8%>k diff --git a/src/properties/property_groups.py b/src/properties/property_groups.py index c5894b2..0cc2d05 100644 --- a/src/properties/property_groups.py +++ b/src/properties/property_groups.py @@ -180,10 +180,9 @@ class TEXT_TEXTURE_Preset(PropertyGroup): description="Where this preset is stored", items=[ ('BLEND_FILE', 'Blend File', 'Preset stored in the blend file (travels with the file)'), - ('PERSISTENT_FILE', 'Persistent File', 'Preset stored in persistent location (survives addon updates)'), - ('LOCAL_FILE', 'Local File', 'Preset stored locally (legacy system)') + ('PERSISTENT_FILE', 'Persistent File', 'Preset stored in persistent location (survives addon updates)') ], - default='BLEND_FILE' + default='PERSISTENT_FILE' ) class TEXT_TEXTURE_Properties(PropertyGroup): @@ -628,6 +627,13 @@ class TEXT_TEXTURE_Properties(PropertyGroup): default=False ) + # Save with blend file checkbox + save_with_blend_file: BoolProperty( + name="Save with .blend file", + description="Save this preset with the current .blend file instead of persistent storage", + default=False + ) + # ============================================================================ # SHADER GENERATION PROPERTIES # ============================================================================ diff --git a/src/ui/__pycache__/panels.cpython-311.pyc b/src/ui/__pycache__/panels.cpython-311.pyc index 775f114047fabe8b64ebbbbbad1672ebe3fca0e5..020f68bcf9734ab697dde71147958299e0a5ade1 100644 GIT binary patch delta 3433 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