diff options
| author | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2025-07-22 13:23:25 -0400 |
|---|---|---|
| committer | xXJSONDeruloXx <danielhimebauch@gmail.com> | 2025-07-22 13:23:25 -0400 |
| commit | d063284dea10e82a23c2c332ecd4901d7254171b (patch) | |
| tree | 8433fc86936229eeb01d4b200fbb93da0dec8d18 | |
| parent | df0635f1bba611b8b44975057acd579102d209dd (diff) | |
| download | decky-lsfg-vk-d063284dea10e82a23c2c332ecd4901d7254171b.tar.gz decky-lsfg-vk-d063284dea10e82a23c2c332ecd4901d7254171b.zip | |
use generated kwargs and config in more hardcoded places
| -rw-r--r-- | .gitignore | 5 | ||||
| -rw-r--r-- | GENERATED_CODE_ANALYSIS.md | 145 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/config_schema.py | 36 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/config_schema_generated.py | 35 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/configuration.py | 36 | ||||
| -rw-r--r-- | py_modules/lsfg_vk/installation.py | 7 | ||||
| -rw-r--r-- | scripts/generate_python_boilerplate.py | 16 | ||||
| -rw-r--r-- | scripts/generate_ts_schema.py | 9 | ||||
| -rw-r--r-- | src/components/ConfigurationSection.tsx | 23 | ||||
| -rw-r--r-- | src/config/configSchema.ts | 6 | ||||
| -rw-r--r-- | src/config/generatedConfigSchema.ts | 13 |
11 files changed, 251 insertions, 80 deletions
@@ -55,3 +55,8 @@ out/* cli/ cli/* cli/decky + +# generated files +py_modules/lsfg_vk/config_schema_generated.py +py_modules/lsfg_vk/configuration_helpers_generated.py +src/config/generatedConfigSchema.ts diff --git a/GENERATED_CODE_ANALYSIS.md b/GENERATED_CODE_ANALYSIS.md new file mode 100644 index 0000000..86c37e6 --- /dev/null +++ b/GENERATED_CODE_ANALYSIS.md @@ -0,0 +1,145 @@ +# Generated Code Analysis and Improvement Opportunities + +## Generated Files Added to .gitignore ✅ + +The following auto-generated files are now properly ignored: +- `py_modules/lsfg_vk/config_schema_generated.py` +- `py_modules/lsfg_vk/configuration_helpers_generated.py` +- `src/config/generatedConfigSchema.ts` + +## Build Scripts Overview + +### 1. `scripts/generate_python_boilerplate.py` +**Generates:** +- `py_modules/lsfg_vk/config_schema_generated.py` - TypedDict, parsing logic, generation logic +- `py_modules/lsfg_vk/configuration_helpers_generated.py` - Helper functions for logging, field access + +### 2. `scripts/generate_ts_schema.py` +**Generates:** +- `src/config/generatedConfigSchema.ts` - TypeScript interfaces, schema constants, defaults + +## Current Hardcoded Areas That Could Use Generated Code + +### Python Side Opportunities + +#### 1. **CRITICAL - Function Signatures** 🚨 +**File:** `py_modules/lsfg_vk/configuration.py:107-113` +```python +def update_config(self, dll: str, multiplier: int, flow_scale: float, + performance_mode: bool, hdr_mode: bool, + experimental_present_mode: str = "fifo", + dxvk_frame_rate: int = 0, + enable_wow64: bool = False, + disable_steamdeck_mode: bool = False, + mangohud_workaround: bool = False, + disable_vkbasalt: bool = False) -> ConfigurationResponse: +``` + +**Improvement:** The generated code already creates `get_function_parameters()` that returns the exact signature string. This method signature should be replaced with a generated version or simplified to use `**kwargs` with schema validation. + +#### 2. **Field Name String Literals** ⚠️ +**Multiple locations** - Field names used as string literals: +- `config["dll"]` (multiple files) +- `config["multiplier"]` etc. + +**Improvement:** Use the generated field constants or create typed access methods. + +#### 3. **Configuration Defaults** ⚠️ +**File:** Various locations where defaults are hardcoded +**Improvement:** Always use `ConfigurationManager.get_defaults()` or generated default functions. + +#### 4. **Logging Format Strings** ✅ (Partially Done) +**Status:** The `log_configuration_update()` function is already generated and used in some places, but could be used more consistently. + +### TypeScript Side Opportunities + +#### 1. **CRITICAL - Hardcoded Field Names in UI** 🚨 +**File:** `src/components/ConfigurationSection.tsx:48,60,69,78,104,146,164,173,182` + +```tsx +onChange={(value) => onConfigChange('multiplier', value)} +onChange={(value) => onConfigChange('flow_scale', value)} +onChange={(value) => onConfigChange('performance_mode', value)} +// ... etc for all 11 fields +``` + +**Improvement:** These string literals should use schema constants: +```tsx +import { getFieldNames } from '../config/generatedConfigSchema'; +// or use constants like +onChange={(value) => onConfigChange('multiplier', value)} // fieldNames.multiplier +``` + +#### 2. **UI Component Generation** 🚀 +**Opportunity:** The entire `ConfigurationSection.tsx` could be generated from the schema definition since each field has: +- `fieldType` (determines UI component type) +- `description` (for labels/descriptions) +- `default` (for initial values) +- `name` (for display names) + +#### 3. **Manual Type Definitions** ✅ (Already Done) +**Status:** TypeScript types are already generated properly via `ConfigurationData` interface. + +#### 4. **Field Validation** 🚀 +**Opportunity:** Client-side validation could be generated from the schema field types and constraints. + +## High-Impact Improvements Recommended + +### Phase 1 - Critical Fixes (High Impact, Low Effort) + +1. **Replace hardcoded field name strings** with schema constants +2. **Use generated default functions** everywhere instead of hardcoded defaults +3. **Consistent use of generated logging functions** + +### Phase 2 - Major Refactoring (High Impact, Medium Effort) + +1. **Replace `update_config()` parameter list** with generated signature or schema-based validation +2. **Generate UI components** from schema instead of manual creation +3. **Create typed field accessor methods** to eliminate string-based field access + +### Phase 3 - Advanced Features (Medium Impact, High Effort) + +1. **Generate client-side validation** from schema constraints +2. **Auto-generate test cases** from schema definition +3. **Generate documentation** from schema descriptions + +## Specific Next Steps + +### Immediate (Phase 1) +1. Create field name constants in TypeScript and use them in ConfigurationSection +2. Replace remaining hardcoded defaults with generated functions +3. Add schema validation to Python configuration updates + +### Short Term (Phase 2) +1. Refactor Python `update_config()` method to use schema-driven approach +2. Generate UI field components from schema metadata +3. Create typed field accessor pattern + +### Long Term (Phase 3) +1. Build complete schema-driven UI generation system +2. Add schema versioning and migration support +3. Generate comprehensive test suites from schema + +## Files That Should Be Modified + +### Python Files +- `py_modules/lsfg_vk/configuration.py` - Method signatures, field access +- `py_modules/lsfg_vk/plugin.py` - Field access patterns +- `py_modules/lsfg_vk/config_schema.py` - Ensure all access uses generated code + +### TypeScript Files +- `src/components/ConfigurationSection.tsx` - Replace hardcoded field names +- `src/config/configSchema.ts` - Add field name constants export +- `src/api/lsfgApi.ts` - Ensure type safety with generated types + +### Build Process +- Consider adding the generation scripts to VS Code tasks or package.json scripts +- Add validation that generated files are up to date in CI/CD + +## Benefits of These Improvements + +1. **Single Source of Truth** - All configuration changes happen in `shared_config.py` +2. **Type Safety** - Reduce runtime errors from typos in field names +3. **Maintainability** - Adding new config fields requires minimal manual code changes +4. **Consistency** - Generated code ensures consistent patterns across Python/TypeScript +5. **Documentation** - Schema serves as living documentation of configuration options diff --git a/py_modules/lsfg_vk/config_schema.py b/py_modules/lsfg_vk/config_schema.py index a7827ae..bbace42 100644 --- a/py_modules/lsfg_vk/config_schema.py +++ b/py_modules/lsfg_vk/config_schema.py @@ -171,7 +171,9 @@ class ConfigurationManager: if config.get("dll"): lines.append("[global]") lines.append(f"# specify where Lossless.dll is stored") - lines.append(f'dll = "{config["dll"]}"') + # Generate TOML lines for TOML fields only - USE GENERATED CONSTANTS + from .config_schema_generated import DLL + lines.append(f'dll = "{config[DLL]}"') lines.append("") # Add game section with process name for LSFG_PROCESS approach @@ -248,9 +250,10 @@ class ConfigurationManager: elif value.startswith("'") and value.endswith("'"): value = value[1:-1] - # Handle global section (dll only) + # Handle global section (dll only) - USE GENERATED CONSTANTS if in_global_section and key == "dll": - config["dll"] = value + from .config_schema_generated import DLL + config[DLL] = value # Handle game section elif in_game_section: @@ -315,25 +318,8 @@ class ConfigurationManager: return cast(ConfigurationData, merged_config) @staticmethod - def create_config_from_args(dll: str, multiplier: int, flow_scale: float, - performance_mode: bool, hdr_mode: bool, - experimental_present_mode: str = "fifo", - dxvk_frame_rate: int = 0, - enable_wow64: bool = False, - disable_steamdeck_mode: bool = False, - mangohud_workaround: bool = False, - disable_vkbasalt: bool = False) -> ConfigurationData: - """Create configuration from individual arguments""" - return cast(ConfigurationData, { - "dll": dll, - "multiplier": multiplier, - "flow_scale": flow_scale, - "performance_mode": performance_mode, - "hdr_mode": hdr_mode, - "experimental_present_mode": experimental_present_mode, - "dxvk_frame_rate": dxvk_frame_rate, - "enable_wow64": enable_wow64, - "disable_steamdeck_mode": disable_steamdeck_mode, - "mangohud_workaround": mangohud_workaround, - "disable_vkbasalt": disable_vkbasalt - }) + @staticmethod + def create_config_from_args(**kwargs) -> ConfigurationData: + """Create configuration from keyword arguments - USES GENERATED CODE""" + from .config_schema_generated import create_config_dict + return create_config_dict(**kwargs) diff --git a/py_modules/lsfg_vk/config_schema_generated.py b/py_modules/lsfg_vk/config_schema_generated.py index 46bc58f..cc90207 100644 --- a/py_modules/lsfg_vk/config_schema_generated.py +++ b/py_modules/lsfg_vk/config_schema_generated.py @@ -12,6 +12,19 @@ from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent.parent)) from shared_config import CONFIG_SCHEMA_DEF, ConfigFieldType +# Field name constants for type-safe access +DLL = "dll" +MULTIPLIER = "multiplier" +FLOW_SCALE = "flow_scale" +PERFORMANCE_MODE = "performance_mode" +HDR_MODE = "hdr_mode" +EXPERIMENTAL_PRESENT_MODE = "experimental_present_mode" +DXVK_FRAME_RATE = "dxvk_frame_rate" +ENABLE_WOW64 = "enable_wow64" +DISABLE_STEAMDECK_MODE = "disable_steamdeck_mode" +MANGOHUD_WORKAROUND = "mangohud_workaround" +DISABLE_VKBASALT = "disable_vkbasalt" + class ConfigurationData(TypedDict): """Type-safe configuration data structure - AUTO-GENERATED""" @@ -98,17 +111,17 @@ def get_function_parameters() -> str: def create_config_dict(**kwargs) -> ConfigurationData: """Create configuration dictionary from keyword arguments""" return cast(ConfigurationData, { - "dll": dll, - "multiplier": multiplier, - "flow_scale": flow_scale, - "performance_mode": performance_mode, - "hdr_mode": hdr_mode, - "experimental_present_mode": experimental_present_mode, - "dxvk_frame_rate": dxvk_frame_rate, - "enable_wow64": enable_wow64, - "disable_steamdeck_mode": disable_steamdeck_mode, - "mangohud_workaround": mangohud_workaround, - "disable_vkbasalt": disable_vkbasalt, + "dll": kwargs.get("dll"), + "multiplier": kwargs.get("multiplier"), + "flow_scale": kwargs.get("flow_scale"), + "performance_mode": kwargs.get("performance_mode"), + "hdr_mode": kwargs.get("hdr_mode"), + "experimental_present_mode": kwargs.get("experimental_present_mode"), + "dxvk_frame_rate": kwargs.get("dxvk_frame_rate"), + "enable_wow64": kwargs.get("enable_wow64"), + "disable_steamdeck_mode": kwargs.get("disable_steamdeck_mode"), + "mangohud_workaround": kwargs.get("mangohud_workaround"), + "disable_vkbasalt": kwargs.get("disable_vkbasalt"), }) diff --git a/py_modules/lsfg_vk/configuration.py b/py_modules/lsfg_vk/configuration.py index e745e29..b9ee174 100644 --- a/py_modules/lsfg_vk/configuration.py +++ b/py_modules/lsfg_vk/configuration.py @@ -103,39 +103,18 @@ class ConfigurationService(BaseService): self.log.error(error_msg) return self._error_response(ConfigurationResponse, str(e), config=None) - def update_config(self, dll: str, multiplier: int, flow_scale: float, - performance_mode: bool, hdr_mode: bool, - experimental_present_mode: str = "fifo", - dxvk_frame_rate: int = 0, - enable_wow64: bool = False, - disable_steamdeck_mode: bool = False, - mangohud_workaround: bool = False, - disable_vkbasalt: bool = False) -> ConfigurationResponse: - """Update TOML configuration + def update_config(self, **kwargs) -> ConfigurationResponse: + """Update TOML configuration using generated schema - SIMPLIFIED WITH GENERATED CODE Args: - dll: Path to Lossless.dll - multiplier: LSFG multiplier value - flow_scale: LSFG flow scale value - performance_mode: Whether to enable performance mode - hdr_mode: Whether to enable HDR mode - experimental_present_mode: Experimental Vulkan present mode override - dxvk_frame_rate: Frame rate cap for DirectX games, before frame multiplier (0 = disabled) - enable_wow64: Whether to enable PROTON_USE_WOW64=1 for 32-bit games - disable_steamdeck_mode: Whether to disable Steam Deck mode - mangohud_workaround: Whether to enable MangoHud workaround with transparent overlay - disable_vkbasalt: Whether to disable vkBasalt layer + **kwargs: Configuration field values (see shared_config.py for available fields) Returns: ConfigurationResponse with success status """ try: - # Create configuration from individual arguments - config = ConfigurationManager.create_config_from_args( - dll, multiplier, flow_scale, performance_mode, hdr_mode, - experimental_present_mode, dxvk_frame_rate, enable_wow64, disable_steamdeck_mode, - mangohud_workaround, disable_vkbasalt - ) + # Create configuration from keyword arguments using generated function + config = ConfigurationManager.create_config_from_args(**kwargs) # Generate TOML content using centralized manager toml_content = ConfigurationManager.generate_toml_content(config) @@ -187,8 +166,9 @@ class ConfigurationService(BaseService): else: config = current_response["config"] - # Update just the DLL path - config["dll"] = dll_path + # Update just the DLL path - USE GENERATED CONSTANTS + from .config_schema_generated import DLL + config[DLL] = dll_path # Generate TOML content and write it toml_content = ConfigurationManager.generate_toml_content(config) diff --git a/py_modules/lsfg_vk/installation.py b/py_modules/lsfg_vk/installation.py index b340093..996a03f 100644 --- a/py_modules/lsfg_vk/installation.py +++ b/py_modules/lsfg_vk/installation.py @@ -121,9 +121,10 @@ class InstallationService(BaseService): self._write_file(self.config_file_path, toml_content, 0o644) self.log.info(f"Created config file at {self.config_file_path}") - # Log detected DLL path if found - if config["dll"]: - self.log.info(f"Configured DLL path: {config['dll']}") + # Log detected DLL path if found - USE GENERATED CONSTANTS + from .config_schema_generated import DLL + if config[DLL]: + self.log.info(f"Configured DLL path: {config[DLL]}") def _create_lsfg_launch_script(self) -> None: """Create the ~/lsfg launch script for easier game setup""" diff --git a/scripts/generate_python_boilerplate.py b/scripts/generate_python_boilerplate.py index 0101ae4..b16aa3f 100644 --- a/scripts/generate_python_boilerplate.py +++ b/scripts/generate_python_boilerplate.py @@ -76,12 +76,12 @@ def generate_function_signature() -> str: def generate_config_dict_creation() -> str: """Generate dictionary creation for create_config_from_args""" - lines = [" return cast(ConfigurationData, {"] + lines = [" return cast(ConfigurationData, {"] for field_name in CONFIG_SCHEMA_DEF.keys(): - lines.append(f' "{field_name}": {field_name},') + lines.append(f' "{field_name}": kwargs.get("{field_name}"),') - lines.append(" })") + lines.append(" })") return "\n".join(lines) @@ -180,6 +180,13 @@ def generate_log_statement() -> str: def generate_complete_schema_file() -> str: """Generate complete config_schema_generated.py file""" + + # Generate field name constants + field_constants = [] + for field_name in CONFIG_SCHEMA_DEF.keys(): + const_name = field_name.upper() + field_constants.append(f'{const_name} = "{field_name}"') + lines = [ '"""', 'Auto-generated configuration schema components from shared_config.py', @@ -195,6 +202,9 @@ def generate_complete_schema_file() -> str: 'sys.path.insert(0, str(Path(__file__).parent.parent.parent))', 'from shared_config import CONFIG_SCHEMA_DEF, ConfigFieldType', '', + '# Field name constants for type-safe access', + ] + field_constants + [ + '', '', generate_typed_dict(), '', diff --git a/scripts/generate_ts_schema.py b/scripts/generate_ts_schema.py index 1997c55..c4c0e8a 100644 --- a/scripts/generate_ts_schema.py +++ b/scripts/generate_ts_schema.py @@ -19,6 +19,12 @@ from shared_config import CONFIG_SCHEMA_DEF, ConfigFieldType def generate_typescript_schema(): """Generate generatedConfigSchema.ts from Python schema""" + # Generate field name constants + field_constants = [] + for field_name in CONFIG_SCHEMA_DEF.keys(): + const_name = field_name.upper() + field_constants.append(f'export const {const_name} = "{field_name}" as const;') + # Generate enum enum_lines = [ "// src/config/generatedConfigSchema.ts", @@ -30,6 +36,9 @@ def generate_typescript_schema(): " STRING = \"string\"", "}", "", + "// Field name constants for type-safe access", + ] + field_constants + [ + "", "// Configuration field definition", "export interface ConfigField {", " name: string;", diff --git a/src/components/ConfigurationSection.tsx b/src/components/ConfigurationSection.tsx index 1c0d2b2..c0b67fd 100644 --- a/src/components/ConfigurationSection.tsx +++ b/src/components/ConfigurationSection.tsx @@ -1,5 +1,10 @@ import { PanelSectionRow, ToggleField, SliderField, DropdownItem } from "@decky/ui"; import { ConfigurationData } from "../config/configSchema"; +import { + MULTIPLIER, FLOW_SCALE, PERFORMANCE_MODE, HDR_MODE, + EXPERIMENTAL_PRESENT_MODE, DXVK_FRAME_RATE, DISABLE_STEAMDECK_MODE, + MANGOHUD_WORKAROUND, DISABLE_VKBASALT +} from "../config/generatedConfigSchema"; interface ConfigurationSectionProps { config: ConfigurationData; @@ -45,7 +50,7 @@ export function ConfigurationSection({ ]} showValue={false} notchTicksVisible={true} - onChange={(value) => onConfigChange('multiplier', value)} + onChange={(value) => onConfigChange(MULTIPLIER, value)} /> </PanelSectionRow> @@ -57,7 +62,7 @@ export function ConfigurationSection({ min={0.25} max={1.0} step={0.01} - onChange={(value) => onConfigChange('flow_scale', value)} + onChange={(value) => onConfigChange(FLOW_SCALE, value)} /> </PanelSectionRow> @@ -66,7 +71,7 @@ export function ConfigurationSection({ label="Performance Mode" description="Uses a lighter model for FG (Recommended for most games)" checked={config.performance_mode} - onChange={(value) => onConfigChange('performance_mode', value)} + onChange={(value) => onConfigChange(PERFORMANCE_MODE, value)} /> </PanelSectionRow> @@ -75,7 +80,7 @@ export function ConfigurationSection({ label="HDR Mode" description="Enables HDR mode (only for games that support HDR)" checked={config.hdr_mode} - onChange={(value) => onConfigChange('hdr_mode', value)} + onChange={(value) => onConfigChange(HDR_MODE, value)} /> </PanelSectionRow> @@ -101,7 +106,7 @@ export function ConfigurationSection({ description="Select a specific Vulkan presentation mode for better performance or compatibility (May cause crashes)" menuLabel="Select presentation mode" selectedOption={config.experimental_present_mode || "fifo"} - onChange={(value) => onConfigChange('experimental_present_mode', value.data)} + onChange={(value) => onConfigChange(EXPERIMENTAL_PRESENT_MODE, value.data)} rgOptions={[ { data: "fifo", label: "FIFO (VSync) - Default" }, { data: "mailbox", label: "Mailbox" } @@ -143,7 +148,7 @@ export function ConfigurationSection({ min={0} max={60} step={1} - onChange={(value) => onConfigChange('dxvk_frame_rate', value)} + onChange={(value) => onConfigChange(DXVK_FRAME_RATE, value)} /> </PanelSectionRow> @@ -161,7 +166,7 @@ export function ConfigurationSection({ label="Disable Steam Deck Mode" description="Disables Steam Deck mode (Unlocks hidden settings in some games)" checked={config.disable_steamdeck_mode} - onChange={(value) => onConfigChange('disable_steamdeck_mode', value)} + onChange={(value) => onConfigChange(DISABLE_STEAMDECK_MODE, value)} /> </PanelSectionRow> @@ -170,7 +175,7 @@ export function ConfigurationSection({ label="MangoHud Workaround" description="Enables a transparent mangohud overlay, sometimes fixes issues with 2X multiplier in game mode" checked={config.mangohud_workaround} - onChange={(value) => onConfigChange('mangohud_workaround', value)} + onChange={(value) => onConfigChange(MANGOHUD_WORKAROUND, value)} /> </PanelSectionRow> @@ -179,7 +184,7 @@ export function ConfigurationSection({ label="Disable vkBasalt" description="Disables vkBasalt layer which can conflict with LSFG (Reshade, some Decky plugins)" checked={config.disable_vkbasalt} - onChange={(value) => onConfigChange('disable_vkbasalt', value)} + onChange={(value) => onConfigChange(DISABLE_VKBASALT, value)} /> </PanelSectionRow> </> diff --git a/src/config/configSchema.ts b/src/config/configSchema.ts index fdf6212..9568fd8 100644 --- a/src/config/configSchema.ts +++ b/src/config/configSchema.ts @@ -18,7 +18,11 @@ export { ConfigurationData, getFieldNames, getDefaults, - getFieldTypes + getFieldTypes, + // Field name constants for type-safe access + DLL, MULTIPLIER, FLOW_SCALE, PERFORMANCE_MODE, HDR_MODE, + EXPERIMENTAL_PRESENT_MODE, DXVK_FRAME_RATE, ENABLE_WOW64, + DISABLE_STEAMDECK_MODE, MANGOHUD_WORKAROUND, DISABLE_VKBASALT } from './generatedConfigSchema'; /** diff --git a/src/config/generatedConfigSchema.ts b/src/config/generatedConfigSchema.ts index cb08252..4a301a1 100644 --- a/src/config/generatedConfigSchema.ts +++ b/src/config/generatedConfigSchema.ts @@ -7,6 +7,19 @@ export enum ConfigFieldType { STRING = "string" } +// Field name constants for type-safe access +export const DLL = "dll" as const; +export const MULTIPLIER = "multiplier" as const; +export const FLOW_SCALE = "flow_scale" as const; +export const PERFORMANCE_MODE = "performance_mode" as const; +export const HDR_MODE = "hdr_mode" as const; +export const EXPERIMENTAL_PRESENT_MODE = "experimental_present_mode" as const; +export const DXVK_FRAME_RATE = "dxvk_frame_rate" as const; +export const ENABLE_WOW64 = "enable_wow64" as const; +export const DISABLE_STEAMDECK_MODE = "disable_steamdeck_mode" as const; +export const MANGOHUD_WORKAROUND = "mangohud_workaround" as const; +export const DISABLE_VKBASALT = "disable_vkbasalt" as const; + // Configuration field definition export interface ConfigField { name: string; |
