mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-31 19:45:15 +02:00
feat: add PDF preview and export functionality for Typst-based reports, enhance report content handling
This commit is contained in:
parent
aac65e122e
commit
39aaf92f0e
2 changed files with 81 additions and 0 deletions
|
|
@ -71,4 +71,11 @@ SYSTEM_PROMPT_DEFAULTS: list[dict] = [
|
||||||
"prompt": "Search the web for information about:\n\n{selection}",
|
"prompt": "Search the web for information about:\n\n{selection}",
|
||||||
"mode": "explore",
|
"mode": "explore",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"slug": "build-resume",
|
||||||
|
"version": 1,
|
||||||
|
"name": "Build Resume",
|
||||||
|
"prompt": "Build me a professional resume. Here is my information:\n\n{selection}",
|
||||||
|
"mode": "explore",
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -279,6 +279,7 @@ async def read_report_content(
|
||||||
id=report.id,
|
id=report.id,
|
||||||
title=report.title,
|
title=report.title,
|
||||||
content=report.content,
|
content=report.content,
|
||||||
|
content_type=report.content_type,
|
||||||
report_metadata=report.report_metadata,
|
report_metadata=report.report_metadata,
|
||||||
report_group_id=report.report_group_id,
|
report_group_id=report.report_group_id,
|
||||||
versions=versions,
|
versions=versions,
|
||||||
|
|
@ -319,6 +320,7 @@ async def update_report_content(
|
||||||
id=report.id,
|
id=report.id,
|
||||||
title=report.title,
|
title=report.title,
|
||||||
content=report.content,
|
content=report.content,
|
||||||
|
content_type=report.content_type,
|
||||||
report_metadata=report.report_metadata,
|
report_metadata=report.report_metadata,
|
||||||
report_group_id=report.report_group_id,
|
report_group_id=report.report_group_id,
|
||||||
versions=versions,
|
versions=versions,
|
||||||
|
|
@ -333,6 +335,57 @@ async def update_report_content(
|
||||||
) from None
|
) from None
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/reports/{report_id}/preview")
|
||||||
|
async def preview_report_pdf(
|
||||||
|
report_id: int,
|
||||||
|
session: AsyncSession = Depends(get_async_session),
|
||||||
|
user: User = Depends(current_active_user),
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Return a compiled PDF preview for Typst-based reports (resumes).
|
||||||
|
|
||||||
|
Reads the Typst source from the database and compiles it to PDF bytes
|
||||||
|
on-the-fly. Only works for reports with content_type='typst'.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
report = await _get_report_with_access(report_id, session, user)
|
||||||
|
|
||||||
|
if not report.content:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400, detail="Report has no content to preview"
|
||||||
|
)
|
||||||
|
|
||||||
|
if report.content_type != "typst":
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Preview is only available for Typst-based reports",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _compile() -> bytes:
|
||||||
|
return typst.compile(report.content.encode("utf-8"))
|
||||||
|
|
||||||
|
pdf_bytes = await asyncio.to_thread(_compile)
|
||||||
|
|
||||||
|
safe_title = re.sub(r"[^\w\s-]", "", report.title or "Resume").strip()
|
||||||
|
filename = f"{safe_title}.pdf"
|
||||||
|
|
||||||
|
return StreamingResponse(
|
||||||
|
io.BytesIO(pdf_bytes),
|
||||||
|
media_type="application/pdf",
|
||||||
|
headers={
|
||||||
|
"Content-Disposition": f'inline; filename="{filename}"',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Failed to compile Typst preview for report %d", report_id)
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail="Failed to compile resume preview",
|
||||||
|
) from None
|
||||||
|
|
||||||
|
|
||||||
@router.get("/reports/{report_id}/export")
|
@router.get("/reports/{report_id}/export")
|
||||||
async def export_report(
|
async def export_report(
|
||||||
report_id: int,
|
report_id: int,
|
||||||
|
|
@ -354,6 +407,27 @@ async def export_report(
|
||||||
status_code=400, detail="Report has no content to export"
|
status_code=400, detail="Report has no content to export"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Typst-based reports (resumes): compile directly without Pandoc
|
||||||
|
if report.content_type == "typst":
|
||||||
|
if format != ExportFormat.PDF:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Typst-based reports currently only support PDF export",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _compile_typst() -> bytes:
|
||||||
|
return typst.compile(report.content.encode("utf-8"))
|
||||||
|
|
||||||
|
pdf_bytes = await asyncio.to_thread(_compile_typst)
|
||||||
|
safe_title = re.sub(r"[^\w\s-]", "", report.title or "Resume").strip()
|
||||||
|
return StreamingResponse(
|
||||||
|
io.BytesIO(pdf_bytes),
|
||||||
|
media_type="application/pdf",
|
||||||
|
headers={
|
||||||
|
"Content-Disposition": f'attachment; filename="{safe_title}.pdf"',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
# Strip wrapping code fences that LLMs sometimes add around Markdown.
|
# Strip wrapping code fences that LLMs sometimes add around Markdown.
|
||||||
# Without this, pandoc treats the entire content as a code block.
|
# Without this, pandoc treats the entire content as a code block.
|
||||||
markdown_content = _strip_wrapping_code_fences(report.content)
|
markdown_content = _strip_wrapping_code_fences(report.content)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue