diff --git a/README.md b/README.md index 77b64d116..af303567d 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,16 @@ v∆v - Tracks filament usage during prints - Third-party spools (SpoolEase, etc.) gracefully skipped - **Cloud Profiles Sync** - Access your Bambu Cloud slicer presets + - View and manage filament, printer, and process presets + - **Template system** for quick preset creation: + - Save any preset as a reusable template + - Visibility control to choose which templates appear in modals + - Apply templates when creating or editing presets + - **Preset comparison/diff view**: + - Compare any preset against its base preset + - Compare any two presets of the same type side-by-side + - Highlights added, removed, and changed settings + - Searchable diff with change statistics - **File Manager** - Browse and manage files on your printer's SD card - **Re-print** - Send archived prints back to any connected printer - **Dark/Light Theme** - Easy on the eyes, day or night @@ -892,6 +902,7 @@ To fix the printer's clock: - [x] Maintenance tracker - [x] Multi-language support (English, German) - [x] Auto updates from GitHub releases +- [x] Cloud Profiles template system and diff view - [ ] Full printer control - [ ] Mobile-optimized UI - [ ] docs: readme -> wiki diff --git a/backend/app/api/routes/cloud.py b/backend/app/api/routes/cloud.py index 4364eff68..e9a5c85f6 100644 --- a/backend/app/api/routes/cloud.py +++ b/backend/app/api/routes/cloud.py @@ -4,6 +4,10 @@ Bambu Lab Cloud API Routes Handles authentication and profile management with Bambu Cloud. """ +import json +from pathlib import Path +from typing import Literal + from fastapi import APIRouter, HTTPException, Depends from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select @@ -172,7 +176,7 @@ async def logout(db: AsyncSession = Depends(get_db)): @router.get("/settings", response_model=SlicerSettingsResponse) async def get_slicer_settings( - version: str = "01.09.00.00", + version: str = "02.04.00.70", db: AsyncSession = Depends(get_db), ): """ @@ -391,3 +395,71 @@ async def delete_setting(setting_id: str, db: AsyncSession = Depends(get_db)): raise HTTPException(status_code=401, detail="Authentication expired") except BambuCloudError as e: raise HTTPException(status_code=500, detail=str(e)) + + +# Path to field definition files +FIELDS_DATA_DIR = Path(__file__).parent.parent.parent / "data" + +# Cache for field definitions (loaded once) +_fields_cache: dict[str, dict] = {} + + +def _load_fields(preset_type: str) -> dict: + """Load field definitions from JSON file.""" + if preset_type in _fields_cache: + return _fields_cache[preset_type] + + # Map API type names to file names + file_map = { + "filament": "filament_fields.json", + "print": "process_fields.json", + "process": "process_fields.json", + "printer": "printer_fields.json", + } + + filename = file_map.get(preset_type) + if not filename: + raise HTTPException(status_code=400, detail=f"Unknown preset type: {preset_type}") + + file_path = FIELDS_DATA_DIR / filename + if not file_path.exists(): + raise HTTPException(status_code=404, detail=f"Field definitions not found for: {preset_type}") + + with open(file_path, "r") as f: + data = json.load(f) + + _fields_cache[preset_type] = data + return data + + +@router.get("/fields/{preset_type}") +async def get_preset_fields(preset_type: Literal["filament", "print", "process", "printer"]): + """ + Get field definitions for a preset type. + + Returns a list of field definitions including: + - key: The setting key name + - label: Human-readable label + - type: Field type (text, number, boolean, select) + - category: Grouping category + - description: Field description + - options: For select fields, available options + - unit: Unit of measurement (if applicable) + - min/max/step: For number fields, validation constraints + """ + data = _load_fields(preset_type) + return data + + +@router.get("/fields") +async def get_all_preset_fields(): + """ + Get all field definitions for all preset types. + + Returns field definitions organized by type. + """ + return { + "filament": _load_fields("filament"), + "process": _load_fields("process"), + "printer": _load_fields("printer"), + } diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 276af5674..c6f77633c 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -2,7 +2,7 @@ from pathlib import Path from pydantic_settings import BaseSettings # Application version - single source of truth -APP_VERSION = "0.1.4" +APP_VERSION = "0.1.5b" GITHUB_REPO = "maziggy/bambusy" diff --git a/backend/app/data/filament_fields.json b/backend/app/data/filament_fields.json new file mode 100644 index 000000000..99d55501c --- /dev/null +++ b/backend/app/data/filament_fields.json @@ -0,0 +1,625 @@ +{ + "version": "1.0.0", + "description": "Filament preset field definitions for Bambu Lab printers", + "fields": [ + { + "key": "filament_vendor", + "label": "Vendor", + "type": "text", + "category": "basic", + "description": "Filament manufacturer name" + }, + { + "key": "filament_type", + "label": "Filament Type", + "type": "select", + "category": "basic", + "description": "Material type", + "options": [ + {"value": "PLA", "label": "PLA"}, + {"value": "ABS", "label": "ABS"}, + {"value": "PETG", "label": "PETG"}, + {"value": "TPU", "label": "TPU"}, + {"value": "PA", "label": "PA (Nylon)"}, + {"value": "PA-CF", "label": "PA-CF"}, + {"value": "PET-CF", "label": "PET-CF"}, + {"value": "PC", "label": "PC"}, + {"value": "ASA", "label": "ASA"}, + {"value": "PVA", "label": "PVA"}, + {"value": "HIPS", "label": "HIPS"} + ] + }, + { + "key": "filament_cost", + "label": "Filament Cost", + "type": "number", + "category": "basic", + "description": "Cost per kg", + "unit": "$/kg" + }, + { + "key": "filament_density", + "label": "Density", + "type": "number", + "category": "basic", + "description": "Material density", + "unit": "g/cm³", + "step": 0.01 + }, + { + "key": "filament_diameter", + "label": "Filament Diameter", + "type": "number", + "category": "basic", + "description": "Filament diameter", + "unit": "mm", + "step": 0.01, + "min": 1.5, + "max": 3.0 + }, + { + "key": "nozzle_temperature", + "label": "Nozzle Temperature", + "type": "text", + "category": "temperature", + "description": "Printing temperature (comma-separated: normal,first layer)", + "unit": "°C" + }, + { + "key": "nozzle_temperature_initial_layer", + "label": "Initial Layer Nozzle Temp", + "type": "text", + "category": "temperature", + "description": "First layer nozzle temperature", + "unit": "°C" + }, + { + "key": "nozzle_temperature_range_low", + "label": "Min Nozzle Temp", + "type": "number", + "category": "temperature", + "description": "Minimum recommended nozzle temperature", + "unit": "°C", + "min": 150, + "max": 350 + }, + { + "key": "nozzle_temperature_range_high", + "label": "Max Nozzle Temp", + "type": "number", + "category": "temperature", + "description": "Maximum recommended nozzle temperature", + "unit": "°C", + "min": 150, + "max": 350 + }, + { + "key": "hot_plate_temp", + "label": "Bed Temperature (Hot Plate)", + "type": "number", + "category": "temperature", + "description": "Bed temperature for standard plate", + "unit": "°C", + "min": 0, + "max": 120 + }, + { + "key": "hot_plate_temp_initial_layer", + "label": "Initial Layer Bed Temp (Hot Plate)", + "type": "number", + "category": "temperature", + "description": "First layer bed temperature for standard plate", + "unit": "°C", + "min": 0, + "max": 120 + }, + { + "key": "cool_plate_temp", + "label": "Bed Temperature (Cool Plate)", + "type": "number", + "category": "temperature", + "description": "Bed temperature for cool plate", + "unit": "°C", + "min": 0, + "max": 120 + }, + { + "key": "cool_plate_temp_initial_layer", + "label": "Initial Layer Bed Temp (Cool Plate)", + "type": "number", + "category": "temperature", + "description": "First layer bed temperature for cool plate", + "unit": "°C", + "min": 0, + "max": 120 + }, + { + "key": "eng_plate_temp", + "label": "Bed Temperature (Engineering Plate)", + "type": "number", + "category": "temperature", + "description": "Bed temperature for engineering plate", + "unit": "°C", + "min": 0, + "max": 120 + }, + { + "key": "eng_plate_temp_initial_layer", + "label": "Initial Layer Bed Temp (Eng Plate)", + "type": "number", + "category": "temperature", + "description": "First layer bed temperature for engineering plate", + "unit": "°C", + "min": 0, + "max": 120 + }, + { + "key": "textured_plate_temp", + "label": "Bed Temperature (Textured Plate)", + "type": "number", + "category": "temperature", + "description": "Bed temperature for textured plate", + "unit": "°C", + "min": 0, + "max": 120 + }, + { + "key": "textured_plate_temp_initial_layer", + "label": "Initial Layer Bed Temp (Textured)", + "type": "number", + "category": "temperature", + "description": "First layer bed temperature for textured plate", + "unit": "°C", + "min": 0, + "max": 120 + }, + { + "key": "temperature_vitrification", + "label": "Glass Transition Temperature", + "type": "number", + "category": "temperature", + "description": "Glass transition temperature of material", + "unit": "°C" + }, + { + "key": "pressure_advance", + "label": "Pressure Advance", + "type": "number", + "category": "flow", + "description": "Pressure advance value for linear advance", + "step": 0.001, + "min": 0, + "max": 0.2 + }, + { + "key": "enable_pressure_advance", + "label": "Enable Pressure Advance", + "type": "boolean", + "category": "flow", + "description": "Enable pressure advance compensation" + }, + { + "key": "filament_flow_ratio", + "label": "Flow Ratio", + "type": "number", + "category": "flow", + "description": "Flow rate multiplier", + "step": 0.01, + "min": 0.8, + "max": 1.2 + }, + { + "key": "filament_max_volumetric_speed", + "label": "Max Volumetric Speed", + "type": "text", + "category": "flow", + "description": "Maximum volumetric flow rate (comma-separated values)", + "unit": "mm³/s" + }, + { + "key": "fan_min_speed", + "label": "Min Fan Speed", + "type": "number", + "category": "cooling", + "description": "Minimum part cooling fan speed", + "unit": "%", + "min": 0, + "max": 100 + }, + { + "key": "fan_max_speed", + "label": "Max Fan Speed", + "type": "number", + "category": "cooling", + "description": "Maximum part cooling fan speed", + "unit": "%", + "min": 0, + "max": 100 + }, + { + "key": "fan_cooling_layer_time", + "label": "Fan Cooling Layer Time", + "type": "number", + "category": "cooling", + "description": "Layer time threshold for max fan", + "unit": "s", + "min": 0, + "max": 120 + }, + { + "key": "slow_down_layer_time", + "label": "Slow Down Layer Time", + "type": "number", + "category": "cooling", + "description": "Minimum layer time before slowing down", + "unit": "s", + "min": 0, + "max": 60 + }, + { + "key": "slow_down_min_speed", + "label": "Slow Down Min Speed", + "type": "number", + "category": "cooling", + "description": "Minimum speed when slowing down for cooling", + "unit": "mm/s", + "min": 5, + "max": 100 + }, + { + "key": "close_fan_the_first_x_layers", + "label": "Disable Fan First Layers", + "type": "number", + "category": "cooling", + "description": "Number of initial layers without fan", + "min": 0, + "max": 10 + }, + { + "key": "overhang_fan_threshold", + "label": "Overhang Fan Threshold", + "type": "text", + "category": "cooling", + "description": "Overhang angle to trigger fan boost" + }, + { + "key": "overhang_fan_speed", + "label": "Overhang Fan Speed", + "type": "number", + "category": "cooling", + "description": "Fan speed for overhangs", + "unit": "%", + "min": 0, + "max": 100 + }, + { + "key": "reduce_fan_stop_start_freq", + "label": "Reduce Fan Stop/Start Frequency", + "type": "boolean", + "category": "cooling", + "description": "Prevent frequent fan speed changes" + }, + { + "key": "activate_air_filtration", + "label": "Activate Air Filtration", + "type": "boolean", + "category": "chamber", + "description": "Enable chamber air filtration during print" + }, + { + "key": "during_print_exhaust_fan_speed", + "label": "Exhaust Fan Speed (During Print)", + "type": "number", + "category": "chamber", + "description": "Exhaust fan speed during printing", + "unit": "%", + "min": 0, + "max": 100 + }, + { + "key": "complete_print_exhaust_fan_speed", + "label": "Exhaust Fan Speed (After Print)", + "type": "number", + "category": "chamber", + "description": "Exhaust fan speed after print completes", + "unit": "%", + "min": 0, + "max": 100 + }, + { + "key": "activate_chamber_temp_control", + "label": "Chamber Temp Control", + "type": "boolean", + "category": "chamber", + "description": "Enable active chamber temperature control" + }, + { + "key": "chamber_temperature", + "label": "Chamber Temperature", + "type": "number", + "category": "chamber", + "description": "Target chamber temperature", + "unit": "°C", + "min": 0, + "max": 60 + }, + { + "key": "filament_retraction_length", + "label": "Retraction Length", + "type": "number", + "category": "retraction", + "description": "Length of filament to retract", + "unit": "mm", + "step": 0.1, + "min": 0, + "max": 10 + }, + { + "key": "filament_retract_before_wipe", + "label": "Retract Before Wipe", + "type": "number", + "category": "retraction", + "description": "Percentage of retraction before wipe", + "unit": "%", + "min": 0, + "max": 100 + }, + { + "key": "filament_retraction_speed", + "label": "Retraction Speed", + "type": "number", + "category": "retraction", + "description": "Speed for retraction moves", + "unit": "mm/s", + "min": 10, + "max": 120 + }, + { + "key": "filament_deretraction_speed", + "label": "Deretraction Speed", + "type": "number", + "category": "retraction", + "description": "Speed for priming after retraction", + "unit": "mm/s", + "min": 10, + "max": 120 + }, + { + "key": "filament_retract_restart_extra", + "label": "Extra Length on Restart", + "type": "number", + "category": "retraction", + "description": "Extra filament to prime after retraction", + "unit": "mm", + "step": 0.01 + }, + { + "key": "filament_retract_when_changing_layer", + "label": "Retract on Layer Change", + "type": "boolean", + "category": "retraction", + "description": "Force retraction when changing layers" + }, + { + "key": "filament_wipe", + "label": "Enable Wipe", + "type": "boolean", + "category": "retraction", + "description": "Enable nozzle wipe during retraction" + }, + { + "key": "filament_wipe_distance", + "label": "Wipe Distance", + "type": "number", + "category": "retraction", + "description": "Distance to wipe while retracting", + "unit": "mm", + "step": 0.1 + }, + { + "key": "filament_z_hop", + "label": "Z Hop Height", + "type": "number", + "category": "retraction", + "description": "Lift height during travel moves", + "unit": "mm", + "step": 0.1, + "min": 0, + "max": 2 + }, + { + "key": "filament_z_hop_types", + "label": "Z Hop Type", + "type": "select", + "category": "retraction", + "description": "Type of Z hop motion", + "options": [ + {"value": "Normal Lift", "label": "Normal Lift"}, + {"value": "Slope Lift", "label": "Slope Lift"}, + {"value": "Spiral Lift", "label": "Spiral Lift"} + ] + }, + { + "key": "filament_retraction_minimum_travel", + "label": "Min Travel After Retraction", + "type": "number", + "category": "retraction", + "description": "Minimum travel distance to trigger retraction", + "unit": "mm", + "step": 0.1 + }, + { + "key": "filament_retract_layer_change", + "label": "Retract on Layer Change", + "type": "boolean", + "category": "retraction", + "description": "Retract when layer changes" + }, + { + "key": "filament_colour", + "label": "Filament Color", + "type": "text", + "category": "appearance", + "description": "Filament color (hex format #RRGGBB)" + }, + { + "key": "default_filament_colour", + "label": "Default Filament Color", + "type": "text", + "category": "appearance", + "description": "Default color for this filament type" + }, + { + "key": "filament_is_support", + "label": "Is Support Material", + "type": "boolean", + "category": "advanced", + "description": "Mark as support material filament" + }, + { + "key": "filament_soluble", + "label": "Soluble Material", + "type": "boolean", + "category": "advanced", + "description": "Material dissolves in liquid" + }, + { + "key": "required_nozzle_HRC", + "label": "Required Nozzle HRC", + "type": "number", + "category": "advanced", + "description": "Minimum nozzle hardness required", + "min": 0, + "max": 100 + }, + { + "key": "filament_start_gcode", + "label": "Filament Start G-code", + "type": "text", + "category": "gcode", + "description": "G-code executed when switching to this filament" + }, + { + "key": "filament_end_gcode", + "label": "Filament End G-code", + "type": "text", + "category": "gcode", + "description": "G-code executed when switching from this filament" + }, + { + "key": "filament_loading_speed", + "label": "Loading Speed", + "type": "number", + "category": "ams", + "description": "Speed for loading filament", + "unit": "mm/s" + }, + { + "key": "filament_loading_speed_start", + "label": "Loading Start Speed", + "type": "number", + "category": "ams", + "description": "Initial speed when loading filament", + "unit": "mm/s" + }, + { + "key": "filament_unloading_speed", + "label": "Unloading Speed", + "type": "number", + "category": "ams", + "description": "Speed for unloading filament", + "unit": "mm/s" + }, + { + "key": "filament_unloading_speed_start", + "label": "Unloading Start Speed", + "type": "number", + "category": "ams", + "description": "Initial speed when unloading filament", + "unit": "mm/s" + }, + { + "key": "filament_toolchange_delay", + "label": "Toolchange Delay", + "type": "number", + "category": "ams", + "description": "Delay after tool change", + "unit": "s" + }, + { + "key": "filament_cooling_moves", + "label": "Cooling Moves", + "type": "number", + "category": "ams", + "description": "Number of cooling moves during unload" + }, + { + "key": "filament_cooling_initial_speed", + "label": "Cooling Initial Speed", + "type": "number", + "category": "ams", + "description": "Initial speed for tip shaping cooling moves", + "unit": "mm/s" + }, + { + "key": "filament_cooling_final_speed", + "label": "Cooling Final Speed", + "type": "number", + "category": "ams", + "description": "Final speed for tip shaping cooling moves", + "unit": "mm/s" + }, + { + "key": "filament_ramming_parameters", + "label": "Ramming Parameters", + "type": "text", + "category": "ams", + "description": "Ramming sequence parameters" + }, + { + "key": "filament_minimal_purge_on_wipe_tower", + "label": "Min Purge on Wipe Tower", + "type": "number", + "category": "ams", + "description": "Minimum purge volume on wipe tower", + "unit": "mm³" + }, + { + "key": "filament_multitool_ramming", + "label": "Multitool Ramming", + "type": "boolean", + "category": "ams", + "description": "Enable ramming for multi-tool" + }, + { + "key": "filament_multitool_ramming_volume", + "label": "Multitool Ramming Volume", + "type": "number", + "category": "ams", + "description": "Volume to ram during multi-tool change", + "unit": "mm³" + }, + { + "key": "filament_multitool_ramming_flow", + "label": "Multitool Ramming Flow", + "type": "number", + "category": "ams", + "description": "Flow rate during multi-tool ramming", + "unit": "mm³/s" + }, + { + "key": "filament_long_retractions_when_cut", + "label": "Long Retractions When Cut", + "type": "boolean", + "category": "ams", + "description": "Use long retraction when cutting filament" + }, + { + "key": "filament_retraction_distances_when_cut", + "label": "Retraction Distance When Cut", + "type": "text", + "category": "ams", + "description": "Retraction distance during filament cut" + } + ] +} diff --git a/backend/app/data/printer_fields.json b/backend/app/data/printer_fields.json new file mode 100644 index 000000000..695160fa3 --- /dev/null +++ b/backend/app/data/printer_fields.json @@ -0,0 +1,574 @@ +{ + "version": "1.0.0", + "description": "Printer/machine preset field definitions for Bambu Lab printers", + "fields": [ + { + "key": "printer_model", + "label": "Printer Model", + "type": "text", + "category": "basic", + "description": "Printer model identifier" + }, + { + "key": "printer_variant", + "label": "Printer Variant", + "type": "text", + "category": "basic", + "description": "Nozzle variant (e.g., 0.4 nozzle)" + }, + { + "key": "printer_notes", + "label": "Printer Notes", + "type": "text", + "category": "basic", + "description": "Notes about this printer profile" + }, + { + "key": "printable_area", + "label": "Printable Area", + "type": "text", + "category": "dimensions", + "description": "Bed shape coordinates" + }, + { + "key": "printable_height", + "label": "Printable Height", + "type": "number", + "category": "dimensions", + "description": "Maximum print height", + "unit": "mm" + }, + { + "key": "bed_exclude_area", + "label": "Bed Exclude Area", + "type": "text", + "category": "dimensions", + "description": "Coordinates of excluded bed areas" + }, + { + "key": "nozzle_diameter", + "label": "Nozzle Diameter", + "type": "number", + "category": "extruder", + "description": "Diameter of the nozzle", + "unit": "mm", + "step": 0.1, + "min": 0.1, + "max": 1.2 + }, + { + "key": "nozzle_type", + "label": "Nozzle Type", + "type": "select", + "category": "extruder", + "description": "Type of nozzle installed", + "options": [ + {"value": "brass", "label": "Brass"}, + {"value": "stainless_steel", "label": "Stainless Steel"}, + {"value": "hardened_steel", "label": "Hardened Steel"} + ] + }, + { + "key": "nozzle_hrc", + "label": "Nozzle HRC", + "type": "number", + "category": "extruder", + "description": "Hardness rating of nozzle" + }, + { + "key": "nozzle_volume", + "label": "Nozzle Volume", + "type": "number", + "category": "extruder", + "description": "Melt zone volume", + "unit": "mm³" + }, + { + "key": "extruder_type", + "label": "Extruder Type", + "type": "select", + "category": "extruder", + "description": "Type of extruder", + "options": [ + {"value": "DirectDrive", "label": "Direct Drive"}, + {"value": "Bowden", "label": "Bowden"} + ] + }, + { + "key": "retraction_length", + "label": "Retraction Length", + "type": "number", + "category": "retraction", + "description": "Default retraction length", + "unit": "mm", + "step": 0.1, + "min": 0, + "max": 10 + }, + { + "key": "retraction_speed", + "label": "Retraction Speed", + "type": "number", + "category": "retraction", + "description": "Speed for retraction", + "unit": "mm/s" + }, + { + "key": "deretraction_speed", + "label": "Deretraction Speed", + "type": "number", + "category": "retraction", + "description": "Speed for deretraction", + "unit": "mm/s" + }, + { + "key": "retract_before_wipe", + "label": "Retract Before Wipe", + "type": "number", + "category": "retraction", + "description": "Percentage to retract before wipe", + "unit": "%" + }, + { + "key": "retract_when_changing_layer", + "label": "Retract on Layer Change", + "type": "boolean", + "category": "retraction", + "description": "Retract when changing layers" + }, + { + "key": "wipe", + "label": "Enable Wipe", + "type": "boolean", + "category": "retraction", + "description": "Enable wipe during retraction" + }, + { + "key": "wipe_distance", + "label": "Wipe Distance", + "type": "number", + "category": "retraction", + "description": "Distance to wipe", + "unit": "mm" + }, + { + "key": "z_hop", + "label": "Z Hop Height", + "type": "number", + "category": "retraction", + "description": "Height to lift during travel", + "unit": "mm", + "step": 0.1, + "min": 0, + "max": 2 + }, + { + "key": "z_hop_types", + "label": "Z Hop Type", + "type": "select", + "category": "retraction", + "description": "Type of Z hop motion", + "options": [ + {"value": "Normal Lift", "label": "Normal Lift"}, + {"value": "Slope Lift", "label": "Slope Lift"}, + {"value": "Spiral Lift", "label": "Spiral Lift"} + ] + }, + { + "key": "retraction_minimum_travel", + "label": "Min Travel for Retraction", + "type": "number", + "category": "retraction", + "description": "Minimum travel to trigger retraction", + "unit": "mm" + }, + { + "key": "retract_lift_above", + "label": "Retract Lift Above", + "type": "number", + "category": "retraction", + "description": "Only lift Z above this height", + "unit": "mm" + }, + { + "key": "retract_lift_below", + "label": "Retract Lift Below", + "type": "number", + "category": "retraction", + "description": "Only lift Z below this height", + "unit": "mm" + }, + { + "key": "machine_max_speed_x", + "label": "Max Speed X", + "type": "number", + "category": "limits", + "description": "Maximum X axis speed", + "unit": "mm/s" + }, + { + "key": "machine_max_speed_y", + "label": "Max Speed Y", + "type": "number", + "category": "limits", + "description": "Maximum Y axis speed", + "unit": "mm/s" + }, + { + "key": "machine_max_speed_z", + "label": "Max Speed Z", + "type": "number", + "category": "limits", + "description": "Maximum Z axis speed", + "unit": "mm/s" + }, + { + "key": "machine_max_speed_e", + "label": "Max Speed E", + "type": "number", + "category": "limits", + "description": "Maximum extruder speed", + "unit": "mm/s" + }, + { + "key": "machine_max_acceleration_x", + "label": "Max Acceleration X", + "type": "number", + "category": "limits", + "description": "Maximum X axis acceleration", + "unit": "mm/s²" + }, + { + "key": "machine_max_acceleration_y", + "label": "Max Acceleration Y", + "type": "number", + "category": "limits", + "description": "Maximum Y axis acceleration", + "unit": "mm/s²" + }, + { + "key": "machine_max_acceleration_z", + "label": "Max Acceleration Z", + "type": "number", + "category": "limits", + "description": "Maximum Z axis acceleration", + "unit": "mm/s²" + }, + { + "key": "machine_max_acceleration_e", + "label": "Max Acceleration E", + "type": "number", + "category": "limits", + "description": "Maximum extruder acceleration", + "unit": "mm/s²" + }, + { + "key": "machine_max_acceleration_extruding", + "label": "Max Print Acceleration", + "type": "number", + "category": "limits", + "description": "Maximum acceleration while printing", + "unit": "mm/s²" + }, + { + "key": "machine_max_acceleration_retracting", + "label": "Max Retract Acceleration", + "type": "number", + "category": "limits", + "description": "Maximum acceleration during retraction", + "unit": "mm/s²" + }, + { + "key": "machine_max_acceleration_travel", + "label": "Max Travel Acceleration", + "type": "number", + "category": "limits", + "description": "Maximum acceleration during travel", + "unit": "mm/s²" + }, + { + "key": "machine_max_jerk_x", + "label": "Max Jerk X", + "type": "number", + "category": "limits", + "description": "Maximum X axis jerk", + "unit": "mm/s" + }, + { + "key": "machine_max_jerk_y", + "label": "Max Jerk Y", + "type": "number", + "category": "limits", + "description": "Maximum Y axis jerk", + "unit": "mm/s" + }, + { + "key": "machine_max_jerk_z", + "label": "Max Jerk Z", + "type": "number", + "category": "limits", + "description": "Maximum Z axis jerk", + "unit": "mm/s" + }, + { + "key": "machine_max_jerk_e", + "label": "Max Jerk E", + "type": "number", + "category": "limits", + "description": "Maximum extruder jerk", + "unit": "mm/s" + }, + { + "key": "machine_min_extruding_rate", + "label": "Min Extruding Rate", + "type": "number", + "category": "limits", + "description": "Minimum extrusion flow rate", + "unit": "mm/s" + }, + { + "key": "machine_min_travel_rate", + "label": "Min Travel Rate", + "type": "number", + "category": "limits", + "description": "Minimum travel speed", + "unit": "mm/s" + }, + { + "key": "machine_start_gcode", + "label": "Start G-code", + "type": "text", + "category": "gcode", + "description": "G-code executed at print start" + }, + { + "key": "machine_end_gcode", + "label": "End G-code", + "type": "text", + "category": "gcode", + "description": "G-code executed at print end" + }, + { + "key": "before_layer_change_gcode", + "label": "Before Layer Change G-code", + "type": "text", + "category": "gcode", + "description": "G-code before each layer change" + }, + { + "key": "layer_change_gcode", + "label": "Layer Change G-code", + "type": "text", + "category": "gcode", + "description": "G-code after each layer change" + }, + { + "key": "change_filament_gcode", + "label": "Filament Change G-code", + "type": "text", + "category": "gcode", + "description": "G-code for filament changes" + }, + { + "key": "machine_pause_gcode", + "label": "Pause G-code", + "type": "text", + "category": "gcode", + "description": "G-code for pause command" + }, + { + "key": "template_custom_gcode", + "label": "Template Custom G-code", + "type": "text", + "category": "gcode", + "description": "Custom G-code template" + }, + { + "key": "gcode_flavor", + "label": "G-code Flavor", + "type": "select", + "category": "gcode", + "description": "G-code dialect", + "options": [ + {"value": "marlin", "label": "Marlin"}, + {"value": "marlin2", "label": "Marlin 2"}, + {"value": "reprap", "label": "RepRap"}, + {"value": "klipper", "label": "Klipper"}, + {"value": "smoothie", "label": "Smoothieware"} + ] + }, + { + "key": "thumbnails", + "label": "Thumbnail Sizes", + "type": "text", + "category": "output", + "description": "Sizes for embedded thumbnails" + }, + { + "key": "thumbnails_format", + "label": "Thumbnail Format", + "type": "select", + "category": "output", + "description": "Format for thumbnails", + "options": [ + {"value": "PNG", "label": "PNG"}, + {"value": "JPG", "label": "JPG"}, + {"value": "QOI", "label": "QOI"} + ] + }, + { + "key": "use_relative_e_distances", + "label": "Use Relative E Distances", + "type": "boolean", + "category": "output", + "description": "Use relative extrusion" + }, + { + "key": "use_firmware_retraction", + "label": "Use Firmware Retraction", + "type": "boolean", + "category": "output", + "description": "Use G10/G11 for retraction" + }, + { + "key": "silent_mode", + "label": "Silent Mode", + "type": "boolean", + "category": "output", + "description": "Enable silent mode profile" + }, + { + "key": "fan_kickstart", + "label": "Fan Kickstart", + "type": "number", + "category": "cooling", + "description": "Time to kickstart fan", + "unit": "s" + }, + { + "key": "auxiliary_fan", + "label": "Auxiliary Fan", + "type": "boolean", + "category": "cooling", + "description": "Printer has auxiliary fan" + }, + { + "key": "support_air_filtration", + "label": "Support Air Filtration", + "type": "boolean", + "category": "features", + "description": "Printer has air filtration" + }, + { + "key": "support_chamber_temp_control", + "label": "Support Chamber Temp Control", + "type": "boolean", + "category": "features", + "description": "Printer has chamber heating" + }, + { + "key": "support_multi_bed_types", + "label": "Support Multiple Bed Types", + "type": "boolean", + "category": "features", + "description": "Printer supports multiple bed plates" + }, + { + "key": "upward_compatible_machine", + "label": "Upward Compatible Machines", + "type": "text", + "category": "compatibility", + "description": "List of compatible machine models" + }, + { + "key": "single_extruder_multi_material", + "label": "Single Extruder Multi-Material", + "type": "boolean", + "category": "multimaterial", + "description": "Single extruder with MMU/AMS" + }, + { + "key": "cooling_tube_length", + "label": "Cooling Tube Length", + "type": "number", + "category": "multimaterial", + "description": "Length of cooling tube", + "unit": "mm" + }, + { + "key": "cooling_tube_retraction", + "label": "Cooling Tube Retraction", + "type": "number", + "category": "multimaterial", + "description": "Retraction into cooling tube", + "unit": "mm" + }, + { + "key": "extra_loading_move", + "label": "Extra Loading Move", + "type": "number", + "category": "multimaterial", + "description": "Extra move after loading", + "unit": "mm" + }, + { + "key": "high_current_on_filament_swap", + "label": "High Current on Swap", + "type": "boolean", + "category": "multimaterial", + "description": "Use high current during swap" + }, + { + "key": "parking_pos_retraction", + "label": "Parking Position Retraction", + "type": "number", + "category": "multimaterial", + "description": "Retraction for parking", + "unit": "mm" + }, + { + "key": "bed_custom_model", + "label": "Custom Bed Model", + "type": "text", + "category": "dimensions", + "description": "Path to custom bed STL model" + }, + { + "key": "bed_custom_texture", + "label": "Custom Bed Texture", + "type": "text", + "category": "dimensions", + "description": "Path to custom bed texture" + }, + { + "key": "scan_first_layer", + "label": "Scan First Layer", + "type": "boolean", + "category": "features", + "description": "Enable first layer scanning" + }, + { + "key": "time_cost", + "label": "Time Cost Factor", + "type": "number", + "category": "advanced", + "description": "Cost per hour of operation" + }, + { + "key": "machine_load_filament_time", + "label": "Filament Load Time", + "type": "number", + "category": "multimaterial", + "description": "Time to load filament", + "unit": "s" + }, + { + "key": "machine_unload_filament_time", + "label": "Filament Unload Time", + "type": "number", + "category": "multimaterial", + "description": "Time to unload filament", + "unit": "s" + } + ] +} diff --git a/backend/app/data/process_fields.json b/backend/app/data/process_fields.json new file mode 100644 index 000000000..ec82f31cc --- /dev/null +++ b/backend/app/data/process_fields.json @@ -0,0 +1,923 @@ +{ + "version": "1.0.0", + "description": "Print process preset field definitions for Bambu Lab printers", + "fields": [ + { + "key": "layer_height", + "label": "Layer Height", + "type": "number", + "category": "quality", + "description": "Height of each printed layer", + "unit": "mm", + "step": 0.01, + "min": 0.04, + "max": 0.6 + }, + { + "key": "initial_layer_height", + "label": "Initial Layer Height", + "type": "number", + "category": "quality", + "description": "Height of the first layer", + "unit": "mm", + "step": 0.01, + "min": 0.1, + "max": 0.5 + }, + { + "key": "line_width", + "label": "Line Width", + "type": "number", + "category": "quality", + "description": "Default extrusion width", + "unit": "mm", + "step": 0.01 + }, + { + "key": "initial_layer_line_width", + "label": "Initial Layer Line Width", + "type": "number", + "category": "quality", + "description": "First layer extrusion width", + "unit": "mm", + "step": 0.01 + }, + { + "key": "outer_wall_line_width", + "label": "Outer Wall Line Width", + "type": "number", + "category": "quality", + "description": "Outer perimeter extrusion width", + "unit": "mm", + "step": 0.01 + }, + { + "key": "inner_wall_line_width", + "label": "Inner Wall Line Width", + "type": "number", + "category": "quality", + "description": "Inner perimeter extrusion width", + "unit": "mm", + "step": 0.01 + }, + { + "key": "sparse_infill_line_width", + "label": "Infill Line Width", + "type": "number", + "category": "quality", + "description": "Sparse infill extrusion width", + "unit": "mm", + "step": 0.01 + }, + { + "key": "top_surface_line_width", + "label": "Top Surface Line Width", + "type": "number", + "category": "quality", + "description": "Top surface extrusion width", + "unit": "mm", + "step": 0.01 + }, + { + "key": "support_line_width", + "label": "Support Line Width", + "type": "number", + "category": "quality", + "description": "Support structure extrusion width", + "unit": "mm", + "step": 0.01 + }, + { + "key": "wall_loops", + "label": "Wall Loops", + "type": "number", + "category": "strength", + "description": "Number of perimeter walls", + "min": 1, + "max": 10 + }, + { + "key": "top_shell_layers", + "label": "Top Layers", + "type": "number", + "category": "strength", + "description": "Number of solid top layers", + "min": 1, + "max": 20 + }, + { + "key": "bottom_shell_layers", + "label": "Bottom Layers", + "type": "number", + "category": "strength", + "description": "Number of solid bottom layers", + "min": 1, + "max": 20 + }, + { + "key": "top_shell_thickness", + "label": "Top Shell Thickness", + "type": "number", + "category": "strength", + "description": "Minimum top shell thickness", + "unit": "mm", + "step": 0.1 + }, + { + "key": "bottom_shell_thickness", + "label": "Bottom Shell Thickness", + "type": "number", + "category": "strength", + "description": "Minimum bottom shell thickness", + "unit": "mm", + "step": 0.1 + }, + { + "key": "sparse_infill_density", + "label": "Infill Density", + "type": "number", + "category": "infill", + "description": "Percentage of infill", + "unit": "%", + "min": 0, + "max": 100 + }, + { + "key": "sparse_infill_pattern", + "label": "Infill Pattern", + "type": "select", + "category": "infill", + "description": "Pattern for sparse infill", + "options": [ + {"value": "grid", "label": "Grid"}, + {"value": "triangles", "label": "Triangles"}, + {"value": "tri-hexagon", "label": "Tri-Hexagon"}, + {"value": "cubic", "label": "Cubic"}, + {"value": "gyroid", "label": "Gyroid"}, + {"value": "honeycomb", "label": "Honeycomb"}, + {"value": "3dhoneycomb", "label": "3D Honeycomb"}, + {"value": "line", "label": "Line"}, + {"value": "rectilinear", "label": "Rectilinear"}, + {"value": "concentric", "label": "Concentric"}, + {"value": "zig-zag", "label": "Zig-Zag"}, + {"value": "crosshatch", "label": "Cross Hatch"}, + {"value": "lightning", "label": "Lightning"}, + {"value": "supportcubic", "label": "Support Cubic"}, + {"value": "adaptivecubic", "label": "Adaptive Cubic"} + ] + }, + { + "key": "top_surface_pattern", + "label": "Top Surface Pattern", + "type": "select", + "category": "infill", + "description": "Pattern for top solid layers", + "options": [ + {"value": "monotonic", "label": "Monotonic"}, + {"value": "monotoniclines", "label": "Monotonic Lines"}, + {"value": "alignedrectilinear", "label": "Aligned Rectilinear"}, + {"value": "rectilinear", "label": "Rectilinear"}, + {"value": "concentric", "label": "Concentric"}, + {"value": "hilbertcurve", "label": "Hilbert Curve"}, + {"value": "archimedeanchords", "label": "Archimedean Chords"}, + {"value": "octagramspiral", "label": "Octagram Spiral"} + ] + }, + { + "key": "bottom_surface_pattern", + "label": "Bottom Surface Pattern", + "type": "select", + "category": "infill", + "description": "Pattern for bottom solid layers", + "options": [ + {"value": "monotonic", "label": "Monotonic"}, + {"value": "monotoniclines", "label": "Monotonic Lines"}, + {"value": "rectilinear", "label": "Rectilinear"}, + {"value": "concentric", "label": "Concentric"} + ] + }, + { + "key": "infill_direction", + "label": "Infill Direction", + "type": "number", + "category": "infill", + "description": "Angle of infill pattern", + "unit": "°", + "min": 0, + "max": 360 + }, + { + "key": "infill_anchor", + "label": "Infill Anchor Length", + "type": "number", + "category": "infill", + "description": "Connect infill to walls", + "unit": "mm" + }, + { + "key": "infill_anchor_max", + "label": "Max Infill Anchor Length", + "type": "number", + "category": "infill", + "description": "Maximum anchor length", + "unit": "mm" + }, + { + "key": "infill_combination", + "label": "Combine Infill Layers", + "type": "boolean", + "category": "infill", + "description": "Print infill at higher layer heights" + }, + { + "key": "outer_wall_speed", + "label": "Outer Wall Speed", + "type": "number", + "category": "speed", + "description": "Speed for outer perimeters", + "unit": "mm/s", + "min": 10, + "max": 500 + }, + { + "key": "inner_wall_speed", + "label": "Inner Wall Speed", + "type": "number", + "category": "speed", + "description": "Speed for inner perimeters", + "unit": "mm/s", + "min": 10, + "max": 500 + }, + { + "key": "sparse_infill_speed", + "label": "Infill Speed", + "type": "number", + "category": "speed", + "description": "Speed for sparse infill", + "unit": "mm/s", + "min": 10, + "max": 500 + }, + { + "key": "internal_solid_infill_speed", + "label": "Internal Solid Infill Speed", + "type": "number", + "category": "speed", + "description": "Speed for internal solid layers", + "unit": "mm/s" + }, + { + "key": "top_surface_speed", + "label": "Top Surface Speed", + "type": "number", + "category": "speed", + "description": "Speed for top surface", + "unit": "mm/s" + }, + { + "key": "bridge_speed", + "label": "Bridge Speed", + "type": "number", + "category": "speed", + "description": "Speed for bridging", + "unit": "mm/s" + }, + { + "key": "gap_infill_speed", + "label": "Gap Fill Speed", + "type": "number", + "category": "speed", + "description": "Speed for gap filling", + "unit": "mm/s" + }, + { + "key": "travel_speed", + "label": "Travel Speed", + "type": "number", + "category": "speed", + "description": "Speed for non-printing moves", + "unit": "mm/s", + "min": 50, + "max": 1000 + }, + { + "key": "initial_layer_speed", + "label": "Initial Layer Speed", + "type": "number", + "category": "speed", + "description": "Speed for first layer", + "unit": "mm/s" + }, + { + "key": "initial_layer_infill_speed", + "label": "Initial Layer Infill Speed", + "type": "number", + "category": "speed", + "description": "Speed for first layer infill", + "unit": "mm/s" + }, + { + "key": "support_speed", + "label": "Support Speed", + "type": "number", + "category": "speed", + "description": "Speed for support structures", + "unit": "mm/s" + }, + { + "key": "support_interface_speed", + "label": "Support Interface Speed", + "type": "number", + "category": "speed", + "description": "Speed for support interface", + "unit": "mm/s" + }, + { + "key": "outer_wall_acceleration", + "label": "Outer Wall Acceleration", + "type": "number", + "category": "acceleration", + "description": "Acceleration for outer perimeters", + "unit": "mm/s²" + }, + { + "key": "inner_wall_acceleration", + "label": "Inner Wall Acceleration", + "type": "number", + "category": "acceleration", + "description": "Acceleration for inner perimeters", + "unit": "mm/s²" + }, + { + "key": "default_acceleration", + "label": "Default Acceleration", + "type": "number", + "category": "acceleration", + "description": "Default print acceleration", + "unit": "mm/s²" + }, + { + "key": "travel_acceleration", + "label": "Travel Acceleration", + "type": "number", + "category": "acceleration", + "description": "Acceleration for travel moves", + "unit": "mm/s²" + }, + { + "key": "initial_layer_acceleration", + "label": "Initial Layer Acceleration", + "type": "number", + "category": "acceleration", + "description": "Acceleration for first layer", + "unit": "mm/s²" + }, + { + "key": "top_surface_acceleration", + "label": "Top Surface Acceleration", + "type": "number", + "category": "acceleration", + "description": "Acceleration for top surface", + "unit": "mm/s²" + }, + { + "key": "bridge_acceleration", + "label": "Bridge Acceleration", + "type": "number", + "category": "acceleration", + "description": "Acceleration for bridging", + "unit": "mm/s²" + }, + { + "key": "sparse_infill_acceleration", + "label": "Infill Acceleration", + "type": "number", + "category": "acceleration", + "description": "Acceleration for infill", + "unit": "mm/s²" + }, + { + "key": "outer_wall_jerk", + "label": "Outer Wall Jerk", + "type": "number", + "category": "acceleration", + "description": "Jerk for outer perimeters", + "unit": "mm/s" + }, + { + "key": "inner_wall_jerk", + "label": "Inner Wall Jerk", + "type": "number", + "category": "acceleration", + "description": "Jerk for inner perimeters", + "unit": "mm/s" + }, + { + "key": "default_jerk", + "label": "Default Jerk", + "type": "number", + "category": "acceleration", + "description": "Default jerk value", + "unit": "mm/s" + }, + { + "key": "travel_jerk", + "label": "Travel Jerk", + "type": "number", + "category": "acceleration", + "description": "Jerk for travel moves", + "unit": "mm/s" + }, + { + "key": "initial_layer_jerk", + "label": "Initial Layer Jerk", + "type": "number", + "category": "acceleration", + "description": "Jerk for first layer", + "unit": "mm/s" + }, + { + "key": "top_surface_jerk", + "label": "Top Surface Jerk", + "type": "number", + "category": "acceleration", + "description": "Jerk for top surface", + "unit": "mm/s" + }, + { + "key": "enable_support", + "label": "Enable Support", + "type": "boolean", + "category": "support", + "description": "Generate support structures" + }, + { + "key": "support_type", + "label": "Support Type", + "type": "select", + "category": "support", + "description": "Type of support generation", + "options": [ + {"value": "normal(auto)", "label": "Normal (Auto)"}, + {"value": "tree(auto)", "label": "Tree (Auto)"}, + {"value": "hybrid(auto)", "label": "Hybrid (Auto)"} + ] + }, + { + "key": "support_style", + "label": "Support Style", + "type": "select", + "category": "support", + "description": "Style of support structure", + "options": [ + {"value": "default", "label": "Default"}, + {"value": "grid", "label": "Grid"}, + {"value": "snug", "label": "Snug"}, + {"value": "organic", "label": "Organic"} + ] + }, + { + "key": "support_threshold_angle", + "label": "Support Overhang Angle", + "type": "number", + "category": "support", + "description": "Minimum angle for support generation", + "unit": "°", + "min": 0, + "max": 90 + }, + { + "key": "support_base_pattern", + "label": "Support Pattern", + "type": "select", + "category": "support", + "description": "Pattern for support base", + "options": [ + {"value": "rectilinear", "label": "Rectilinear"}, + {"value": "rectilinear-grid", "label": "Rectilinear Grid"}, + {"value": "honeycomb", "label": "Honeycomb"}, + {"value": "lightning", "label": "Lightning"}, + {"value": "default", "label": "Default"}, + {"value": "hollow", "label": "Hollow"} + ] + }, + { + "key": "support_base_pattern_spacing", + "label": "Support Pattern Spacing", + "type": "number", + "category": "support", + "description": "Spacing between support lines", + "unit": "mm", + "step": 0.1 + }, + { + "key": "support_interface_top_layers", + "label": "Support Interface Top Layers", + "type": "number", + "category": "support", + "description": "Number of interface layers on top" + }, + { + "key": "support_interface_bottom_layers", + "label": "Support Interface Bottom Layers", + "type": "number", + "category": "support", + "description": "Number of interface layers on bottom" + }, + { + "key": "support_interface_spacing", + "label": "Support Interface Spacing", + "type": "number", + "category": "support", + "description": "Spacing of interface pattern", + "unit": "mm", + "step": 0.1 + }, + { + "key": "support_object_xy_distance", + "label": "Support XY Distance", + "type": "number", + "category": "support", + "description": "Gap between support and object", + "unit": "mm", + "step": 0.1 + }, + { + "key": "support_top_z_distance", + "label": "Support Top Z Distance", + "type": "number", + "category": "support", + "description": "Gap between support top and object", + "unit": "mm", + "step": 0.01 + }, + { + "key": "support_bottom_z_distance", + "label": "Support Bottom Z Distance", + "type": "number", + "category": "support", + "description": "Gap between object and support top", + "unit": "mm", + "step": 0.01 + }, + { + "key": "support_on_build_plate_only", + "label": "Support On Build Plate Only", + "type": "boolean", + "category": "support", + "description": "Only generate support from build plate" + }, + { + "key": "support_critical_regions_only", + "label": "Support Critical Regions Only", + "type": "boolean", + "category": "support", + "description": "Only support critical overhangs" + }, + { + "key": "brim_type", + "label": "Brim Type", + "type": "select", + "category": "adhesion", + "description": "Type of brim to generate", + "options": [ + {"value": "no_brim", "label": "No Brim"}, + {"value": "auto_brim", "label": "Auto"}, + {"value": "outer_only", "label": "Outer Only"}, + {"value": "inner_only", "label": "Inner Only"}, + {"value": "outer_and_inner", "label": "Outer and Inner"}, + {"value": "brim_ears", "label": "Brim Ears"} + ] + }, + { + "key": "brim_width", + "label": "Brim Width", + "type": "number", + "category": "adhesion", + "description": "Width of the brim", + "unit": "mm", + "min": 0, + "max": 20 + }, + { + "key": "brim_object_gap", + "label": "Brim-Object Gap", + "type": "number", + "category": "adhesion", + "description": "Gap between brim and object", + "unit": "mm", + "step": 0.01 + }, + { + "key": "skirt_loops", + "label": "Skirt Loops", + "type": "number", + "category": "adhesion", + "description": "Number of skirt loops", + "min": 0, + "max": 10 + }, + { + "key": "skirt_distance", + "label": "Skirt Distance", + "type": "number", + "category": "adhesion", + "description": "Distance from object to skirt", + "unit": "mm" + }, + { + "key": "skirt_height", + "label": "Skirt Height", + "type": "number", + "category": "adhesion", + "description": "Height of skirt in layers" + }, + { + "key": "raft_layers", + "label": "Raft Layers", + "type": "number", + "category": "adhesion", + "description": "Number of raft layers" + }, + { + "key": "prime_tower_enable", + "label": "Enable Prime Tower", + "type": "boolean", + "category": "multimaterial", + "description": "Generate prime/wipe tower" + }, + { + "key": "prime_tower_width", + "label": "Prime Tower Width", + "type": "number", + "category": "multimaterial", + "description": "Width of prime tower", + "unit": "mm" + }, + { + "key": "prime_tower_brim_width", + "label": "Prime Tower Brim Width", + "type": "number", + "category": "multimaterial", + "description": "Brim width for prime tower", + "unit": "mm" + }, + { + "key": "wipe_tower_no_sparse_layers", + "label": "No Sparse Layers in Tower", + "type": "boolean", + "category": "multimaterial", + "description": "Disable sparse layers in wipe tower" + }, + { + "key": "flush_into_infill", + "label": "Flush Into Infill", + "type": "boolean", + "category": "multimaterial", + "description": "Purge into infill instead of tower" + }, + { + "key": "flush_into_support", + "label": "Flush Into Support", + "type": "boolean", + "category": "multimaterial", + "description": "Purge into support instead of tower" + }, + { + "key": "flush_into_objects", + "label": "Flush Into Objects", + "type": "boolean", + "category": "multimaterial", + "description": "Purge into object infill" + }, + { + "key": "prime_volume", + "label": "Prime Volume", + "type": "number", + "category": "multimaterial", + "description": "Volume to prime after tool change", + "unit": "mm³" + }, + { + "key": "seam_position", + "label": "Seam Position", + "type": "select", + "category": "quality", + "description": "Position of layer start/seam", + "options": [ + {"value": "nearest", "label": "Nearest"}, + {"value": "aligned", "label": "Aligned"}, + {"value": "back", "label": "Back"}, + {"value": "random", "label": "Random"} + ] + }, + { + "key": "staggered_inner_seams", + "label": "Staggered Inner Seams", + "type": "boolean", + "category": "quality", + "description": "Stagger seams on inner walls" + }, + { + "key": "wall_sequence", + "label": "Wall Sequence", + "type": "select", + "category": "quality", + "description": "Order of printing walls", + "options": [ + {"value": "inner wall/outer wall", "label": "Inner/Outer"}, + {"value": "outer wall/inner wall", "label": "Outer/Inner"}, + {"value": "inner-outer-inner wall", "label": "Inner-Outer-Inner"} + ] + }, + { + "key": "only_one_wall_top", + "label": "Only One Wall on Top", + "type": "boolean", + "category": "quality", + "description": "Use single wall on top surface" + }, + { + "key": "detect_thin_wall", + "label": "Detect Thin Walls", + "type": "boolean", + "category": "quality", + "description": "Enable thin wall detection" + }, + { + "key": "enable_overhang_speed", + "label": "Enable Overhang Speed", + "type": "boolean", + "category": "quality", + "description": "Slow down for overhangs" + }, + { + "key": "overhang_speed_classic", + "label": "Classic Overhang Speed", + "type": "boolean", + "category": "quality", + "description": "Use classic overhang speed calculation" + }, + { + "key": "bridge_flow", + "label": "Bridge Flow Ratio", + "type": "number", + "category": "quality", + "description": "Flow ratio for bridges", + "step": 0.01 + }, + { + "key": "thick_bridges", + "label": "Thick Bridges", + "type": "boolean", + "category": "quality", + "description": "Use thicker bridges" + }, + { + "key": "ironing_type", + "label": "Ironing Type", + "type": "select", + "category": "quality", + "description": "Type of ironing", + "options": [ + {"value": "no ironing", "label": "No Ironing"}, + {"value": "top", "label": "Top Surface Only"}, + {"value": "topmost", "label": "Topmost Surface Only"}, + {"value": "allsolid", "label": "All Solid Layers"} + ] + }, + { + "key": "ironing_speed", + "label": "Ironing Speed", + "type": "number", + "category": "quality", + "description": "Speed for ironing", + "unit": "mm/s" + }, + { + "key": "ironing_flow", + "label": "Ironing Flow", + "type": "number", + "category": "quality", + "description": "Flow rate for ironing", + "unit": "%" + }, + { + "key": "ironing_spacing", + "label": "Ironing Spacing", + "type": "number", + "category": "quality", + "description": "Spacing between ironing lines", + "unit": "mm", + "step": 0.01 + }, + { + "key": "fuzzy_skin", + "label": "Fuzzy Skin", + "type": "select", + "category": "quality", + "description": "Add texture to outer walls", + "options": [ + {"value": "none", "label": "None"}, + {"value": "external", "label": "External"}, + {"value": "all", "label": "All Walls"}, + {"value": "allwalls", "label": "All Walls"} + ] + }, + { + "key": "fuzzy_skin_thickness", + "label": "Fuzzy Skin Thickness", + "type": "number", + "category": "quality", + "description": "Thickness of fuzzy skin texture", + "unit": "mm", + "step": 0.01 + }, + { + "key": "fuzzy_skin_point_dist", + "label": "Fuzzy Skin Point Distance", + "type": "number", + "category": "quality", + "description": "Distance between fuzzy skin points", + "unit": "mm", + "step": 0.1 + }, + { + "key": "xy_hole_compensation", + "label": "XY Hole Compensation", + "type": "number", + "category": "quality", + "description": "Compensation for holes", + "unit": "mm", + "step": 0.01 + }, + { + "key": "xy_contour_compensation", + "label": "XY Contour Compensation", + "type": "number", + "category": "quality", + "description": "Compensation for contours", + "unit": "mm", + "step": 0.01 + }, + { + "key": "elefant_foot_compensation", + "label": "Elephant Foot Compensation", + "type": "number", + "category": "quality", + "description": "Compensation for first layer expansion", + "unit": "mm", + "step": 0.01 + }, + { + "key": "resolution", + "label": "Resolution", + "type": "number", + "category": "advanced", + "description": "G-code resolution", + "unit": "mm", + "step": 0.001 + }, + { + "key": "gcode_comments", + "label": "Verbose G-code", + "type": "boolean", + "category": "advanced", + "description": "Add comments to G-code" + }, + { + "key": "reduce_crossing_wall", + "label": "Avoid Crossing Walls", + "type": "boolean", + "category": "advanced", + "description": "Avoid crossing walls during travel" + }, + { + "key": "max_travel_detour_distance", + "label": "Max Travel Detour", + "type": "number", + "category": "advanced", + "description": "Maximum detour to avoid crossing walls", + "unit": "mm" + }, + { + "key": "timelapse_type", + "label": "Timelapse Type", + "type": "select", + "category": "special", + "description": "Type of timelapse recording", + "options": [ + {"value": "0", "label": "Traditional"}, + {"value": "1", "label": "Smooth"} + ] + }, + { + "key": "enable_arc_fitting", + "label": "Enable Arc Fitting", + "type": "boolean", + "category": "advanced", + "description": "Convert linear moves to arcs" + } + ] +} diff --git a/backend/app/services/bambu_cloud.py b/backend/app/services/bambu_cloud.py index 7bef50fbc..b6f383ed9 100644 --- a/backend/app/services/bambu_cloud.py +++ b/backend/app/services/bambu_cloud.py @@ -174,7 +174,7 @@ class BambuCloudService: except httpx.RequestError as e: raise BambuCloudError(f"Request failed: {e}") - async def get_slicer_settings(self, version: str = "01.09.00.00") -> dict: + async def get_slicer_settings(self, version: str = "02.04.00.70") -> dict: """ Get all slicer settings (filament, printer, process presets). diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index b95b6b102..fda2350ad 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -316,6 +316,30 @@ export interface SlicerSettingDeleteResponse { message: string; } +export interface FieldOption { + value: string; + label: string; +} + +export interface FieldDefinition { + key: string; + label: string; + type: 'text' | 'number' | 'boolean' | 'select'; + category: string; + description?: string; + options?: FieldOption[]; + unit?: string; + min?: number; + max?: number; + step?: number; +} + +export interface FieldDefinitionsResponse { + version: string; + description: string; + fields: FieldDefinition[]; +} + export interface CloudDevice { dev_id: string; name: string; @@ -1044,7 +1068,7 @@ export const api = { }), cloudLogout: () => request<{ success: boolean }>('/cloud/logout', { method: 'POST' }), - getCloudSettings: (version = '01.09.00.00') => + getCloudSettings: (version = '02.04.00.70') => request(`/cloud/settings?version=${version}`), getCloudSettingDetail: (settingId: string) => request(`/cloud/settings/${settingId}`), @@ -1063,6 +1087,10 @@ export const api = { method: 'DELETE', }), getCloudDevices: () => request('/cloud/devices'), + getCloudFields: (presetType: 'filament' | 'print' | 'process' | 'printer') => + request(`/cloud/fields/${presetType}`), + getAllCloudFields: () => + request>('/cloud/fields'), // Smart Plugs getSmartPlugs: () => request('/smart-plugs/'), diff --git a/frontend/src/pages/ProfilesPage.tsx b/frontend/src/pages/ProfilesPage.tsx index 18bb022b4..126ad8169 100644 --- a/frontend/src/pages/ProfilesPage.tsx +++ b/frontend/src/pages/ProfilesPage.tsx @@ -24,18 +24,24 @@ import { Filter, ChevronDown, ArrowUp, - HelpCircle, Upload, - FileJson, + Download, Sparkles, Check, AlertCircle, Code, Sliders, List, + Eye, + EyeOff, + GitCompare, + ArrowRight, + Equal, + Minus as MinusIcon, + Plus as PlusIcon, } from 'lucide-react'; import { api } from '../api/client'; -import type { SlicerSetting, SlicerSettingsResponse, SlicerSettingDetail, SlicerSettingCreate, Printer } from '../api/client'; +import type { SlicerSetting, SlicerSettingsResponse, SlicerSettingDetail, SlicerSettingCreate, Printer, FieldDefinition } from '../api/client'; import { Card, CardContent } from '../components/Card'; import { Button } from '../components/Button'; import { useToast } from '../contexts/ToastContext'; @@ -366,89 +372,78 @@ function ScrollToTop() { } // ============================================================================ -// PRESET CARD +// PRESET LIST ITEM (compact row style like K-Profiles) // ============================================================================ -function PresetCard({ +function PresetListItem({ setting, onClick, onDuplicate, + compareMode, + isCompareSelected, + compareIndex, + compareDisabled, }: { setting: SlicerSetting; onClick: () => void; onDuplicate: () => void; + compareMode?: boolean; + isCompareSelected?: boolean; + compareIndex?: number; + compareDisabled?: boolean; }) { const metadata = extractMetadata(setting.name); const isEditable = isUserPreset(setting.setting_id); - const typeConfig = { - filament: { icon: Droplet, color: 'text-amber-400', bg: 'bg-amber-500/10' }, - printer: { icon: PrinterIcon, color: 'text-purple-400', bg: 'bg-purple-500/10' }, - process: { icon: Settings2, color: 'text-blue-400', bg: 'bg-blue-500/10' }, - }; - - const config = typeConfig[setting.type as keyof typeof typeConfig] || typeConfig.process; - const TypeIcon = config.icon; - return ( -
+
- - {/* Quick action on hover */}
); @@ -458,21 +453,66 @@ function PresetCard({ // PRESET DETAIL MODAL // ============================================================================ +// Format JSON for display, converting escaped newlines to real newlines in string values +function formatJsonForDisplay(obj: unknown, indent = 0): string { + const spaces = ' '.repeat(indent); + + if (obj === null) return 'null'; + if (obj === undefined) return 'undefined'; + + if (typeof obj === 'string') { + // Convert escaped newlines to actual newlines for readability + if (obj.includes('\\n') || obj.includes('\n')) { + const formatted = obj + .replace(/\\n/g, '\n') + .replace(/\\"/g, '"') + .replace(/\\t/g, '\t'); + // For multi-line strings, show them nicely indented + const lines = formatted.split('\n'); + if (lines.length > 1) { + return '"""\n' + lines.map(l => spaces + ' ' + l).join('\n') + '\n' + spaces + '"""'; + } + } + return JSON.stringify(obj); + } + + if (typeof obj === 'number' || typeof obj === 'boolean') { + return String(obj); + } + + if (Array.isArray(obj)) { + if (obj.length === 0) return '[]'; + const items = obj.map(item => spaces + ' ' + formatJsonForDisplay(item, indent + 1)); + return '[\n' + items.join(',\n') + '\n' + spaces + ']'; + } + + if (typeof obj === 'object') { + const entries = Object.entries(obj); + if (entries.length === 0) return '{}'; + const items = entries.map(([key, val]) => + spaces + ' ' + JSON.stringify(key) + ': ' + formatJsonForDisplay(val, indent + 1) + ); + return '{\n' + items.join(',\n') + '\n' + spaces + '}'; + } + + return String(obj); +} + function PresetDetailModal({ setting, onClose, onDeleted, onDuplicate, + onEdit, }: { setting: SlicerSetting; onClose: () => void; onDeleted: () => void; onDuplicate: () => void; + onEdit: () => void; }) { const { showToast } = useToast(); const queryClient = useQueryClient(); - const [isEditing, setIsEditing] = useState(false); - const [editedSettings, setEditedSettings] = useState(''); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const { data: detail, isLoading } = useQuery({ @@ -480,31 +520,6 @@ function PresetDetailModal({ queryFn: () => api.getCloudSettingDetail(setting.setting_id), }); - useEffect(() => { - if (detail && editedSettings === '') { - setEditedSettings(JSON.stringify(detail.setting || {}, null, 2)); - } - }, [detail, editedSettings]); - - const updateMutation = useMutation({ - mutationFn: () => { - let parsedSettings: Record | undefined; - try { - parsedSettings = JSON.parse(editedSettings); - } catch { - throw new Error('Invalid JSON in settings'); - } - return api.updateCloudSetting(setting.setting_id, { setting: parsedSettings }); - }, - onSuccess: () => { - showToast('Preset updated successfully'); - queryClient.removeQueries({ queryKey: ['cloudSettingDetail', setting.setting_id] }); - queryClient.invalidateQueries({ queryKey: ['cloudSettings'] }); - onClose(); - }, - onError: (error: Error) => showToast(error.message, 'error'), - }); - const deleteMutation = useMutation({ mutationFn: () => api.deleteCloudSetting(setting.setting_id), onSuccess: () => { @@ -520,10 +535,10 @@ function PresetDetailModal({ return (
- - + + {/* Header */} -
+

{setting.name}

@@ -544,34 +559,15 @@ function PresetDetailModal({
{/* Content */} -
+
{isLoading ? (
) : detail ? ( - isEditing ? ( -
-
- -