Rescue Paperjet v1 implementation

This commit is contained in:
Elijah 2026-08-14 21:05:22 -07:00
parent 7ff5c8130d
commit db9e2ca51b
83 changed files with 6186 additions and 3894 deletions

View file

@ -1,14 +1,17 @@
from datetime import datetime
from typing import Literal, Union, List, Tuple, Optional
from pydantic import BaseModel, Field
from typing import Any, Literal
from uuid import UUID
from pydantic import BaseModel, Field
class Rect(BaseModel):
x: float
y: float
width: float
height: float
class AnnotationBase(BaseModel):
id: UUID
page: int = Field(ge=0)
@ -19,6 +22,7 @@ class AnnotationBase(BaseModel):
createdAt: datetime
updatedAt: datetime
class TextProps(BaseModel):
text: str
fontFamily: str = "Liberation Sans"
@ -28,82 +32,105 @@ class TextProps(BaseModel):
bold: bool = False
italic: bool = False
lineHeight: float = 1.2
highlightColor: Optional[str] = None
styles: Optional[dict] = None
highlightColor: str | None = None
styles: dict[str, Any] | None = None
class TextAnnotation(AnnotationBase):
type: Literal["text"]
props: TextProps
class DrawProps(BaseModel):
paths: List[Tuple[float, float]] = []
svgPath: Optional[str] = None
paths: list[tuple[float, float]] = Field(default_factory=list)
svgPath: str | None = None
strokeColor: str = "#000000"
strokeWidth: float = 2
opacity: float = 1.0
class DrawAnnotation(AnnotationBase):
type: Literal["draw"]
props: DrawProps
class SignatureDrawProps(BaseModel):
mode: Literal["draw"]
ref: str
strokeColor: str = "#000000"
class SignatureTypeProps(BaseModel):
mode: Literal["type"]
text: str
fontFamily: str
color: str = "#000000"
class SignatureAnnotation(AnnotationBase):
type: Literal["signature"]
props: Union[SignatureDrawProps, SignatureTypeProps] = Field(discriminator="mode")
props: SignatureDrawProps | SignatureTypeProps = Field(discriminator="mode")
class ImageProps(BaseModel):
ref: str
naturalWidth: float
naturalHeight: float
class ImageAnnotation(AnnotationBase):
type: Literal["image"]
props: ImageProps
class HighlightProps(BaseModel):
color: str = "#FFEB3B"
opacity: float = 0.3
class HighlightAnnotation(AnnotationBase):
type: Literal["highlight"]
props: HighlightProps
class ShapeProps(BaseModel):
kind: Literal["rect", "ellipse", "line", "arrow"]
strokeColor: str = "#000000"
fillColor: str = "transparent"
strokeWidth: float = 2
start: tuple[float, float] | None = None
end: tuple[float, float] | None = None
class ShapeAnnotation(AnnotationBase):
type: Literal["shape"]
props: ShapeProps
Annotation = Union[
TextAnnotation,
DrawAnnotation,
SignatureAnnotation,
ImageAnnotation,
HighlightAnnotation,
ShapeAnnotation
]
KnownAnnotation = (
TextAnnotation
| DrawAnnotation
| SignatureAnnotation
| ImageAnnotation
| HighlightAnnotation
| ShapeAnnotation
)
# Working state is intentionally opaque. Keeping this payload as dictionaries
# lets newer clients round-trip annotation types that this server cannot export
# yet; the export registry skips those types with a warning.
AnnotationPayload = dict[str, Any]
class AnnotationStateResponse(BaseModel):
data: List[Annotation]
data: list[AnnotationPayload]
updatedAt: datetime
class AnnotationStateUpdateRequest(BaseModel):
data: List[Annotation]
data: list[AnnotationPayload]
baseUpdatedAt: datetime | None = None
class AnnotationStateUpdateResponse(BaseModel):
updatedAt: datetime

View file

@ -2,6 +2,7 @@
from pydantic import BaseModel, Field
class SetupRequest(BaseModel):
password: str = Field(..., min_length=8)

View file

@ -1,7 +1,8 @@
"""Pydantic schemas for Document APIs."""
from pydantic import BaseModel
from typing import Optional
class DocumentMeta(BaseModel):
id: str
@ -12,7 +13,7 @@ class DocumentMeta(BaseModel):
in_trash: bool
created_at: str
updated_at: str
deleted_at: Optional[str]
deleted_at: str | None
model_config = {"from_attributes": True}
@ -27,5 +28,5 @@ class BulkRestoreRequest(BaseModel):
ids: list[str]
class DocumentUpdateRequest(BaseModel):
title: Optional[str] = None
in_trash: Optional[bool] = None
title: str | None = None
in_trash: bool | None = None

View file

@ -0,0 +1,8 @@
"""PDF export request schema."""
from pydantic import BaseModel
class ExportRequest(BaseModel):
versionId: str | None = None
flatten: bool = True

View file

@ -0,0 +1,35 @@
"""Version checkpoint schemas."""
from datetime import datetime
from typing import Literal
from pydantic import BaseModel, Field
from app.schemas.annotations import AnnotationPayload
class VersionCreateRequest(BaseModel):
label: str | None = Field(default=None, max_length=120)
kind: Literal["manual", "auto"] = "manual"
class VersionMeta(BaseModel):
id: str
documentId: str
label: str | None
kind: Literal["manual", "auto"]
createdAt: datetime
annotationCount: int
class VersionListResponse(BaseModel):
items: list[VersionMeta]
class VersionDataResponse(BaseModel):
data: list[AnnotationPayload]
meta: VersionMeta
class VersionRestoreResponse(BaseModel):
updatedAt: datetime