from typing import Optional from fastapi import APIRouter, Depends, Form, Request from fastapi.responses import HTMLResponse from pydantic import BaseModel from sqlmodel import Session, select from app.auth import require_user from app.database import get_session from app.models import OverlayLayout from app.templates import templates router = APIRouter(prefix="/layout") def _get_or_create(session: Session) -> OverlayLayout: layout = session.exec(select(OverlayLayout)).first() if not layout: layout = OverlayLayout() session.add(layout) session.commit() session.refresh(layout) return layout @router.get("", response_class=HTMLResponse) async def layout_page(request: Request, session: Session = Depends(get_session), user=Depends(require_user)): layout = _get_or_create(session) return templates.TemplateResponse("layout.html", {"request": request, "user": user, "layout": layout}) @router.post("", response_class=HTMLResponse) async def save_styles( request: Request, chat_font_size: int = Form(default=14), chat_bg_opacity: float = Form(default=0.55), chat_text_color: str = Form(default="#ffffff"), chat_max_messages: int = Form(default=20), alert_font_size: int = Form(default=24), alert_bg_opacity: float = Form(default=0.8), alert_text_color: str = Form(default="#ffffff"), alert_duration: int = Form(default=4), session: Session = Depends(get_session), user=Depends(require_user), ): layout = _get_or_create(session) layout.chat_font_size = max(8, min(48, chat_font_size)) layout.chat_bg_opacity = max(0.0, min(1.0, chat_bg_opacity)) layout.chat_text_color = chat_text_color layout.chat_max_messages = max(1, min(50, chat_max_messages)) layout.alert_font_size = max(8, min(72, alert_font_size)) layout.alert_bg_opacity = max(0.0, min(1.0, alert_bg_opacity)) layout.alert_text_color = alert_text_color layout.alert_duration = max(1, min(30, alert_duration)) session.add(layout) session.commit() session.refresh(layout) return templates.TemplateResponse("layout.html", { "request": request, "user": user, "layout": layout, "saved": True, }) class PositionUpdate(BaseModel): widget: str left: float top: float width: Optional[float] = None height: Optional[float] = None @router.post("/positions") async def save_positions( data: PositionUpdate, session: Session = Depends(get_session), user=Depends(require_user), ): layout = _get_or_create(session) if data.widget == "chat": layout.chat_left = round(max(0.0, min(95.0, data.left)), 2) layout.chat_top = round(max(0.0, min(95.0, data.top)), 2) if data.width is not None: layout.chat_width = round(max(5.0, min(100.0, data.width)), 2) if data.height is not None: layout.chat_height = round(max(5.0, min(100.0, data.height)), 2) elif data.widget == "alert": layout.alert_left = round(max(0.0, min(100.0, data.left)), 2) layout.alert_top = round(max(0.0, min(100.0, data.top)), 2) session.add(layout) session.commit() return {"ok": True}