This commit is contained in:
2025-09-17 19:43:11 +02:00
parent 7c041e2a26
commit 0e9a655ea1
58 changed files with 2821 additions and 892 deletions

Binary file not shown.

View File

@@ -1,67 +0,0 @@
{
"features": {
"basic": {
"name": "Basic Text Generation",
"description": "Core text texture generation functionality",
"enabled_by_default": true,
"components": [
"core.text_processor",
"core.generation_engine",
"ui.panels.basic"
]
},
"preview": {
"name": "Live Preview",
"description": "Real-time preview of text textures",
"enabled_by_default": true,
"components": ["ui.preview", "core.text_fitting"]
},
"advanced_styling": {
"name": "Advanced Styling Options",
"description": "Extended styling and customization options",
"enabled_by_default": false,
"components": ["ui.panels.advanced", "core.advanced_styling"]
},
"normal_maps": {
"name": "Normal Map Generation",
"description": "Generate normal maps from text",
"enabled_by_default": false,
"components": ["core.normal_maps", "ui.panels.normal_maps"]
},
"presets": {
"name": "Preset Management",
"description": "Save and load text style presets",
"enabled_by_default": false,
"components": [
"presets.storage_system",
"operators.preset_ops",
"ui.panels.presets"
]
},
"batch_processing": {
"name": "Batch Processing",
"description": "Process multiple text textures at once",
"enabled_by_default": false,
"components": ["operators.batch_ops", "ui.panels.batch"]
}
},
"version_configs": {
"free": {
"enabled_features": ["basic", "preview"],
"max_texture_size": 1024,
"max_concurrent_operations": 1
},
"full": {
"enabled_features": [
"basic",
"preview",
"advanced_styling",
"normal_maps",
"presets",
"batch_processing"
],
"max_texture_size": 4096,
"max_concurrent_operations": 4
}
}
}

View File

@@ -6,11 +6,11 @@
"description": "Free version - core text texture generation only",
"exclude_files": [
"core/normal_maps.py",
"operators/preset_ops.py",
"presets/",
"operators/utility_ops.py",
"presets/"
"operators/preset_ops.py"
],
"exclude_patterns": ["*batch*", "*advanced*"],
"exclude_patterns": ["*batch*", "*advanced*", "*overlay*", "*utility*"],
"max_texture_size": 1024,
"max_concurrent_operations": 1
},
@@ -25,6 +25,10 @@
"feature_to_files_mapping": {
"normal_maps": ["core/normal_maps.py"],
"presets": ["presets/", "operators/preset_ops.py"],
"batch_processing": ["operators/utility_ops.py"]
"batch_processing": ["operators/utility_ops.py"],
"image_overlays": ["operators/utility_ops.py"],
"basic_utilities": ["operators/utility_ops.py"],
"advanced_utilities": ["operators/utility_ops.py"],
"advanced_styling": []
}
}

View File

@@ -66,6 +66,12 @@ def create_template_variables(config: Dict, version: str, version_type: str = "f
# Get version-specific configuration
version_config = exclusions_config['version_configs'][version_type]
# Define features based on version type
if version_type == "free":
enabled_features = ["basic", "preview", "image_overlays", "basic_utilities"]
else: # full version
enabled_features = ["basic", "preview", "image_overlays", "basic_utilities", "advanced_styling", "normal_maps", "presets", "batch_processing", "advanced_utilities"]
# Parse Blender version
blender_version = config['project']['blender_version_min']
blender_parts = blender_version.split('.')
@@ -98,6 +104,7 @@ def create_template_variables(config: Dict, version: str, version_type: str = "f
'BLENDER_VERSION_MINOR': str(blender_minor),
'BLENDER_VERSION_PATCH': str(blender_patch),
'DEPENDENCIES': dependencies_str,
'ENABLED_FEATURES': json.dumps(enabled_features),
'MAX_TEXTURE_SIZE': str(version_config['max_texture_size']),
'MAX_CONCURRENT_OPERATIONS': str(version_config['max_concurrent_operations'])
}

View File

@@ -2,26 +2,39 @@
Constants and metadata for the Text Texture Generator addon.
"""
# Version information
VERSION_TYPE = "{{VERSION_TYPE}}" # "free" or "full"
bl_info = {
"name": "{{PROJECT_NAME}}",
"name": "{{PROJECT_NAME}}" + (" Free" if VERSION_TYPE == "free" else " Full"),
"author": "{{PROJECT_AUTHOR}}",
"version": ({{VERSION_MAJOR}}, {{VERSION_MINOR}}, {{VERSION_PATCH}}),
"blender": ({{BLENDER_VERSION_MAJOR}}, {{BLENDER_VERSION_MINOR}}, {{BLENDER_VERSION_PATCH}}),
"location": "{{PROJECT_LOCATION}}",
"description": "{{PROJECT_DESCRIPTION}}",
"description": "{{PROJECT_DESCRIPTION}}" + (" - Free Version (Limited Features)" if VERSION_TYPE == "free" else " - Full Version"),
"category": "{{PROJECT_CATEGORY}}",
}
# Version information
VERSION_TYPE = "{{VERSION_TYPE}}" # "free" or "full"
# Feature configuration based on version type
ENABLED_FEATURES = ["basic", "preview"] if VERSION_TYPE == "free" else [
"basic", "preview", "presets", "normal_maps", "batch_processing",
"image_overlays", "basic_utilities", "advanced_utilities", "advanced_styling",
"stroke_effects", "blur_effects", "shadow_glow_effects", "shader_generation"
]
# Version-specific limits (applied at build time through file exclusions)
def get_version_limits():
"""Get version-specific limits."""
return {
"max_texture_size": {{MAX_TEXTURE_SIZE}},
"max_concurrent_operations": {{MAX_CONCURRENT_OPERATIONS}}
}
if VERSION_TYPE == "free":
return {
"max_texture_size": {{MAX_TEXTURE_SIZE}},
"max_concurrent_operations": {{MAX_CONCURRENT_OPERATIONS}}
}
else:
return {
"max_texture_size": 4096,
"max_concurrent_operations": 4
}
def is_free_version():
"""Check if this is the free version."""
@@ -29,4 +42,118 @@ def is_free_version():
def is_full_version():
"""Check if this is the full version."""
return VERSION_TYPE == "full"
return VERSION_TYPE == "full"
# UI Helper Functions for Version Display
def get_version_badge_text():
"""Get version badge text for UI display."""
return "FREE" if is_free_version() else "PRO"
def get_version_badge_icon():
"""Get appropriate icon for version badge."""
return 'GIFT' if is_free_version() else 'STAR'
def get_upgrade_hint_text(feature_name):
"""Get contextual upgrade hint text for specific features."""
if not is_free_version():
return ""
return f"Upgrade to Pro version to access {feature_name}"
# Feature availability functions
def has_feature(feature_name):
"""Check if a specific feature is available."""
return feature_name in ENABLED_FEATURES
def has_image_overlays():
"""Check if image overlay features are available"""
return has_feature("image_overlays")
def has_basic_utilities():
"""Check if basic utility operations are available"""
return has_feature("basic_utilities")
def has_presets():
"""Check if preset features are available"""
return has_feature("presets")
def has_normal_maps():
"""Check if normal map generation is available"""
return has_feature("normal_maps")
def has_batch_processing():
"""Check if batch processing is available"""
return has_feature("batch_processing")
def has_advanced_utilities():
"""Check if advanced utility operations are available"""
return has_feature("advanced_utilities")
def has_advanced_styling():
"""Check if advanced styling options are available"""
return has_feature("advanced_styling")
def has_stroke_effects():
"""Check if stroke effects are available"""
return has_feature("stroke_effects")
def has_blur_effects():
"""Check if blur effects are available"""
return has_feature("blur_effects")
def has_shadow_glow_effects():
"""Check if shadow and glow effects are available"""
return has_feature("shadow_glow_effects")
def has_shader_generation():
"""Check if shader generation is available"""
return has_feature("shader_generation")
def get_max_resolution():
"""Get maximum texture resolution based on version"""
limits = get_version_limits()
return limits.get("max_texture_size", 1024)
def should_show_feature_lock(feature_name):
"""Check if feature lock indicator should be shown."""
# Map feature names to their availability functions
feature_checks = {
'presets': has_presets,
'normal_maps': has_normal_maps,
'batch_processing': has_batch_processing,
'advanced_utilities': has_advanced_utilities,
'advanced_styling': has_advanced_styling,
'image_overlays': has_image_overlays,
'basic_utilities': has_basic_utilities,
'stroke_effects': has_stroke_effects,
'blur_effects': has_blur_effects,
'shadow_glow_effects': has_shadow_glow_effects,
'shader_generation': has_shader_generation
}
if is_full_version():
return False
# For free version, show lock if feature is not available
if feature_name in feature_checks:
return not feature_checks[feature_name]()
return True # Show lock for unknown features
def get_feature_availability_text():
"""Get text describing current feature availability."""
if is_free_version():
return "Free version - Basic features available"
return "Pro version - All features available"
def get_version_info_details():
"""Get detailed version information for display."""
limits = get_version_limits()
info = {
'version_type': VERSION_TYPE.upper(),
'max_texture_size': limits.get('max_texture_size', 'Unlimited'),
'max_concurrent_operations': limits.get('max_concurrent_operations', 'Unlimited'),
'features_enabled': len(ENABLED_FEATURES),
'upgrade_available': is_free_version()
}
return info

View File

@@ -186,6 +186,92 @@ def validate_constants(zip_path: Path, config: Dict, version_type: str) -> Valid
return result
def validate_version_differences(zip_path: Path, config: Dict, version_type: str) -> ValidationResult:
"""Validate version-specific content and file exclusions."""
result = ValidationResult()
addon_id = config['project']['id']
# Load exclusions configuration
exclusions_path = get_project_root() / "build" / "file_exclusions.json"
try:
with open(exclusions_path, 'r') as f:
exclusions_config = json.load(f)
except Exception as e:
result.add_error(f"Could not load file exclusions config: {e}")
return result
version_config = exclusions_config['version_configs'].get(version_type, {})
excluded_files = version_config.get('exclude_files', [])
excluded_patterns = version_config.get('exclude_patterns', [])
try:
with zipfile.ZipFile(zip_path, 'r') as zip_file:
file_list = zip_file.namelist()
# Check that excluded files are actually excluded in free version
if version_type == "free":
excluded_found = []
for excluded_file in excluded_files:
full_path = f"{addon_id}/{excluded_file}"
if full_path in file_list or any(f.startswith(full_path) for f in file_list):
excluded_found.append(excluded_file)
if excluded_found:
result.add_error(f"Free version contains excluded files: {excluded_found}")
else:
result.add_info(f"Correctly excluded {len(excluded_files)} premium files from free version")
# Check for excluded patterns
pattern_violations = []
for file_path in file_list:
for pattern in excluded_patterns:
if pattern.strip('*').lower() in file_path.lower():
pattern_violations.append((file_path, pattern))
if pattern_violations:
result.add_warning(f"Files matching excluded patterns found: {pattern_violations}")
elif version_type == "full":
# Full version should contain premium files
missing_premium = []
for premium_file in excluded_files:
full_path = f"{addon_id}/{premium_file}"
if full_path not in file_list and not any(f.startswith(full_path) for f in file_list):
missing_premium.append(premium_file)
if missing_premium:
result.add_error(f"Full version missing premium files: {missing_premium}")
else:
result.add_info(f"Full version correctly includes all {len(excluded_files)} premium features")
# Validate UI helper functions are present in constants.py
constants_path = f"{addon_id}/utils/constants.py"
if constants_path in file_list:
constants_content = zip_file.read(constants_path).decode('utf-8')
ui_functions = [
'get_version_badge_text',
'get_version_badge_icon',
'get_upgrade_hint_text',
'should_show_feature_lock',
'get_feature_availability_text',
'get_version_info_details'
]
missing_ui_functions = []
for func in ui_functions:
if func not in constants_content:
missing_ui_functions.append(func)
if missing_ui_functions:
result.add_error(f"Missing UI helper functions in constants.py: {missing_ui_functions}")
else:
result.add_info("All UI helper functions present in constants.py")
except Exception as e:
result.add_error(f"Error validating version differences: {e}")
return result
def validate_feature_flags(zip_path: Path, config: Dict) -> ValidationResult:
"""Validate the feature flags system."""
result = ValidationResult()
@@ -278,6 +364,14 @@ def validate_package(zip_path: Path, version_type: Optional[str] = None) -> bool
features_result.print_results()
overall_valid &= features_result.is_valid
print("\n" + "" * 60)
# Version differences validation
print("Validating version-specific content and exclusions...")
version_diff_result = validate_version_differences(zip_path, config, version_type)
version_diff_result.print_results()
overall_valid &= version_diff_result.is_valid
print("\n" + "=" * 60)
print(f"Overall Validation: {'✅ PASS' if overall_valid else '❌ FAIL'}")