"""Gradient data model. A Gradient defines a reusable color gradient as a list of color stops. Gradients are referenced by ID from effect, gradient, and audio color strip sources. Eight built-in gradients are seeded on first run. """ from dataclasses import dataclass, field from datetime import datetime from typing import List, Optional @dataclass class Gradient: """Persistent gradient definition with color stops.""" id: str name: str stops: list # [{"position": float, "color": [R, G, B]}, ...] is_builtin: bool created_at: datetime updated_at: datetime description: Optional[str] = None tags: List[str] = field(default_factory=list) def to_dict(self) -> dict: return { "id": self.id, "name": self.name, "stops": self.stops, "is_builtin": self.is_builtin, "description": self.description, "tags": self.tags, "created_at": self.created_at.isoformat(), "updated_at": self.updated_at.isoformat(), } @staticmethod def from_dict(data: dict) -> "Gradient": return Gradient( id=data["id"], name=data["name"], stops=data.get("stops", []), is_builtin=data.get("is_builtin", False), description=data.get("description"), tags=data.get("tags", []), created_at=datetime.fromisoformat(data["created_at"]), updated_at=datetime.fromisoformat(data["updated_at"]), ) @classmethod def create_from_kwargs( cls, *, id: str, name: str, stops: list, is_builtin: bool = False, created_at: datetime, updated_at: datetime, description: Optional[str] = None, tags: Optional[List[str]] = None, ) -> "Gradient": return cls( id=id, name=name, stops=stops, is_builtin=is_builtin, created_at=created_at, updated_at=updated_at, description=description, tags=tags or [], ) def apply_update(self, **kwargs) -> None: if kwargs.get("name") is not None: self.name = kwargs["name"] if kwargs.get("stops") is not None: self.stops = kwargs["stops"] if kwargs.get("description") is not None: self.description = kwargs["description"] if kwargs.get("tags") is not None: self.tags = kwargs["tags"]