85 lines
2.4 KiB
Python
85 lines
2.4 KiB
Python
"""µFrame compile, DSL metadata, and image upload endpoints."""
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from fastapi import APIRouter, HTTPException, UploadFile, File
|
|
from pydantic import BaseModel
|
|
|
|
import uframe
|
|
import uframe.keywords # noqa: F401 — triggers keyword registration
|
|
from uframe.errors import UFrameError
|
|
from uframe.registry import get_dsl_meta
|
|
|
|
router = APIRouter()
|
|
|
|
UPLOAD_DIR = Path(os.environ.get("SOURCES_DIR", "/data/sources")) / "images"
|
|
|
|
|
|
class CompileRequest(BaseModel):
|
|
source: str
|
|
width: int = 64
|
|
|
|
|
|
class CompileResponse(BaseModel):
|
|
ascii: str
|
|
micron: str
|
|
script: str
|
|
is_dynamic: bool
|
|
warnings: list[str]
|
|
|
|
|
|
@router.post("/compile", response_model=CompileResponse)
|
|
async def compile_source(req: CompileRequest):
|
|
"""Compile µFrame .uf source into ASCII and Micron output."""
|
|
try:
|
|
result = uframe.compile(req.source, width=req.width)
|
|
return CompileResponse(
|
|
ascii=result.ascii,
|
|
micron=result.micron,
|
|
script=result.script,
|
|
is_dynamic=result.is_dynamic,
|
|
warnings=[w.message for w in result.warnings],
|
|
)
|
|
except UFrameError as e:
|
|
raise HTTPException(status_code=422, detail=str(e))
|
|
|
|
|
|
@router.get("/dsl-meta")
|
|
async def dsl_meta():
|
|
"""Return DSL metadata for frontend syntax highlighting and autocomplete."""
|
|
return get_dsl_meta()
|
|
|
|
|
|
@router.post("/upload-image")
|
|
async def upload_image(file: UploadFile = File(...)):
|
|
"""Upload an image for use in .uf pages."""
|
|
UPLOAD_DIR.mkdir(parents=True, exist_ok=True)
|
|
# Sanitize filename
|
|
name = file.filename or "upload.png"
|
|
safe_name = "".join(c for c in name if c.isalnum() or c in "._-").rstrip(".")
|
|
if not safe_name:
|
|
safe_name = "upload.png"
|
|
dest = UPLOAD_DIR / safe_name
|
|
content = await file.read()
|
|
dest.write_bytes(content)
|
|
# Return the path relative to backend working directory
|
|
rel_path = str(dest)
|
|
return {"path": rel_path, "filename": safe_name, "size": len(content)}
|
|
|
|
|
|
@router.get("/images")
|
|
async def list_images():
|
|
"""List uploaded images available for embedding."""
|
|
if not UPLOAD_DIR.is_dir():
|
|
return []
|
|
images = []
|
|
for f in sorted(UPLOAD_DIR.iterdir()):
|
|
if f.suffix.lower() in (".png", ".jpg", ".jpeg", ".bmp", ".webp", ".gif"):
|
|
images.append({
|
|
"filename": f.name,
|
|
"path": str(f),
|
|
"size": f.stat().st_size,
|
|
})
|
|
return images
|