Add floating canvas panels, theme settings, and SVG line-spacing analysis.

Canvas Studio now uses dockable floating panels, app settings/help navigation, and improved SVG export; the backend adds an SVG line-spacing analysis API with SciPy acceleration and new design templates.
This commit is contained in:
2026-07-13 22:01:01 +08:00
parent d5d8caef2f
commit f8a907e7c5
33 changed files with 4434 additions and 455 deletions
+43
View File
@@ -8,6 +8,7 @@ import re
import shutil
import sqlite3
import threading
import time
import uuid
from datetime import datetime, timezone
from pathlib import Path
@@ -21,6 +22,7 @@ from PIL import Image, ImageDraw, ImageFont
from core import config as wc_config
from core.fonts import get_cached_font
from .job_manager import JobManager
from .line_spacing import analyze_svg_line_spacing_file
from .log_config import get_logger
from .runner import JobRunner
from .schemas import (
@@ -32,6 +34,8 @@ from .schemas import (
JobLocationSearchResult,
JobResult,
JobStatus,
LineSpacingAnalysisRequest,
LineSpacingAnalysisSummary,
Project,
ProjectSummary,
Template,
@@ -973,6 +977,45 @@ def download_asset(asset_id: str):
return FileResponse(path, media_type=media, filename=f"{meta['name']}{ext}")
@app.post("/api/assets/{asset_id}/line-spacing", response_model=LineSpacingAnalysisSummary)
def analyze_asset_line_spacing(
asset_id: str,
request: LineSpacingAnalysisRequest,
) -> LineSpacingAnalysisSummary:
d = _asset_dir(asset_id)
meta = _read_asset_meta(d)
if not meta:
raise HTTPException(status_code=404, detail="asset not found")
if meta.get("mime_type") != "image/svg+xml":
raise HTTPException(status_code=400, detail="line spacing analysis only supports SVG assets")
path = d / "asset.svg"
if not path.exists():
raise HTTPException(status_code=404, detail="asset file missing on disk")
started = time.perf_counter()
try:
result = analyze_svg_line_spacing_file(
path,
percentile=request.percentile,
element_width=request.elementWidth,
element_height=request.elementHeight,
sample_step=request.sampleStep,
)
except ValueError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
elapsed = time.perf_counter() - started
log.info(
"[API] line-spacing asset=%s percentile=%s curves=%s segments=%s elapsed=%.2fs",
asset_id,
result.percentile,
result.curveCount,
result.segmentCount,
elapsed,
)
return LineSpacingAnalysisSummary(**result.as_dict())
@app.delete("/api/assets/{asset_id}", status_code=204)
def delete_asset(asset_id: str) -> None:
d = _asset_dir(asset_id)