feat: add gradient entity with CRUD API and storage
Reusable gradient definitions with built-in presets (rainbow, sunset, ocean, etc.) and user-created gradients. Includes model, JSON store, Pydantic schemas, REST routes (list/create/update/clone/delete), and backup/restore integration.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
"""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"]
|
||||
Reference in New Issue
Block a user