feat: brain masonry card grid — Karakeep-style layout with Atelier aesthetics
- 3-column CSS masonry grid (2 on tablet, 1 on mobile) - Link cards show screenshot thumbnails - Note cards show content body inline - Tags as pills, folder/date in meta footer - Screenshot serving endpoint added to brain API - Auto-polling for pending items (3s interval) - Detail sheet shows raw_content for notes - Warm frosted glass card styling matching Atelier design Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ from app.models.schema import (
|
||||
HybridSearchQuery, SearchResult, ConfigOut,
|
||||
)
|
||||
from app.services.storage import storage
|
||||
from fastapi.responses import Response
|
||||
from app.worker.tasks import enqueue_process_item
|
||||
|
||||
router = APIRouter(prefix="/api", tags=["brain"])
|
||||
@@ -317,3 +318,22 @@ async def hybrid_search(
|
||||
folder=body.folder, tags=body.tags, item_type=body.type, limit=body.limit,
|
||||
)
|
||||
return SearchResult(items=items, total=len(items), query=body.q)
|
||||
|
||||
|
||||
# ── Serve stored files (screenshots, archived HTML) ──
|
||||
|
||||
@router.get("/storage/{item_id}/{asset_type}/{filename}")
|
||||
async def serve_asset(item_id: str, asset_type: str, filename: str):
|
||||
"""Serve a stored asset file."""
|
||||
path = f"{item_id}/{asset_type}/{filename}"
|
||||
if not storage.exists(path):
|
||||
raise HTTPException(status_code=404, detail="Asset not found")
|
||||
|
||||
data = storage.read(path)
|
||||
ct = "application/octet-stream"
|
||||
if filename.endswith(".png"): ct = "image/png"
|
||||
elif filename.endswith(".jpg") or filename.endswith(".jpeg"): ct = "image/jpeg"
|
||||
elif filename.endswith(".html"): ct = "text/html"
|
||||
elif filename.endswith(".pdf"): ct = "application/pdf"
|
||||
|
||||
return Response(content=data, media_type=ct, headers={"Cache-Control": "public, max-age=3600"})
|
||||
|
||||
Reference in New Issue
Block a user