diff --git a/backend/alembic/versions/011bde04d871_add_selected_text_to_generation_points.py b/backend/alembic/versions/011bde04d871_add_selected_text_to_generation_points.py new file mode 100644 index 0000000..165504e --- /dev/null +++ b/backend/alembic/versions/011bde04d871_add_selected_text_to_generation_points.py @@ -0,0 +1,27 @@ +"""Add selected_text to generation_points + +Revision ID: 011bde04d871 +Revises: 857f5a3874dd +Create Date: 2026-07-06 18:19:15.771438 +""" +from typing import Sequence, Union +from alembic import op +import sqlalchemy as sa + + +revision: str = '011bde04d871' +down_revision: Union[str, None] = '857f5a3874dd' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('generation_points', sa.Column('selected_text', sa.Text(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('generation_points', 'selected_text') + # ### end Alembic commands ### diff --git a/backend/app/api/generation_points.py b/backend/app/api/generation_points.py index aa2c6e6..c60002b 100644 --- a/backend/app/api/generation_points.py +++ b/backend/app/api/generation_points.py @@ -25,6 +25,7 @@ async def create_generation_point( prompt: str = Form(...), model_id: uuid.UUID | None = Form(None), order: int = Form(0), + selected_text: str | None = Form(None), ref_file: UploadFile | None = File(None), db: AsyncSession = Depends(get_db), ): @@ -51,6 +52,7 @@ async def create_generation_point( model_id=model_id, order=order, ref_file_path=ref_file_path, + selected_text=selected_text, ) db.add(point) await db.flush() diff --git a/backend/app/models/generation_point.py b/backend/app/models/generation_point.py index be50a52..468b556 100644 --- a/backend/app/models/generation_point.py +++ b/backend/app/models/generation_point.py @@ -19,6 +19,7 @@ class GenerationPoint(Base, UUIDMixin, TimestampMixin): ) ref_file_path: Mapped[str | None] = mapped_column(String(500), nullable=True) order: Mapped[int] = mapped_column(Integer, default=0, nullable=False) + selected_text: Mapped[str | None] = mapped_column(Text, nullable=True) template = relationship("Template") model = relationship("AIModel") diff --git a/backend/app/schemas/generation_point.py b/backend/app/schemas/generation_point.py index 0f9369d..11ca537 100644 --- a/backend/app/schemas/generation_point.py +++ b/backend/app/schemas/generation_point.py @@ -9,6 +9,7 @@ class GenerationPointCreate(BaseModel): prompt: str model_id: uuid.UUID | None = None order: int = 0 + selected_text: str | None = None class GenerationPointUpdate(BaseModel): @@ -16,6 +17,7 @@ class GenerationPointUpdate(BaseModel): prompt: str | None = None model_id: uuid.UUID | None = None order: int | None = None + selected_text: str | None = None class GenerationPointResponse(BaseModel): @@ -26,6 +28,7 @@ class GenerationPointResponse(BaseModel): model_id: uuid.UUID | None = None ref_file_path: str | None = None order: int + selected_text: str | None = None created_at: datetime updated_at: datetime diff --git a/backend/app/tasks/generate.py b/backend/app/tasks/generate.py index 685c77c..647a559 100644 --- a/backend/app/tasks/generate.py +++ b/backend/app/tasks/generate.py @@ -26,23 +26,28 @@ def _create_db_session() -> async_sessionmaker[AsyncSession]: return async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) +def _strip_html(html: str) -> str: + from html.parser import HTMLParser + + class Stripper(HTMLParser): + def __init__(self): + super().__init__() + self.text = "" + def handle_data(self, data): + self.text += data + + s = Stripper() + s.feed(html) + return s.text + + def _get_selected_text(html_content: str, position: dict) -> str: start = position.get("start", 0) end = position.get("end", 0) - if 0 <= start < end <= len(html_content): - from html.parser import HTMLParser - class TextStripper(HTMLParser): - def __init__(self): - super().__init__() - self.text = "" - - def handle_data(self, data): - self.text += data - - stripper = TextStripper() - stripper.feed(html_content[start:end]) - return stripper.text.strip() + plain_text = _strip_html(html_content) + if 0 <= start < end <= len(plain_text): + return plain_text[start:end].strip() return "" @@ -141,7 +146,7 @@ async def _generate_document(task_id: str) -> None: try: for point in points: - selected_text = _get_selected_text(html_content, point.position) + selected_text = point.selected_text or _get_selected_text(html_content, point.position) if not selected_text: continue diff --git a/web/src/api/generationPoints.ts b/web/src/api/generationPoints.ts index 26c2a3c..1a77c58 100644 --- a/web/src/api/generationPoints.ts +++ b/web/src/api/generationPoints.ts @@ -14,6 +14,7 @@ export async function createGenerationPoint(body: { prompt: string; model_id?: string; order?: number; + selected_text?: string; ref_file?: File; }) { const formData = new FormData(); @@ -22,6 +23,7 @@ export async function createGenerationPoint(body: { formData.append("prompt", body.prompt); if (body.model_id) formData.append("model_id", body.model_id); if (body.order !== undefined) formData.append("order", String(body.order)); + if (body.selected_text) formData.append("selected_text", body.selected_text); if (body.ref_file) formData.append("ref_file", body.ref_file); const { data } = await request.post("/generation-points", formData); return data; diff --git a/web/src/pages/TemplateEditor.tsx b/web/src/pages/TemplateEditor.tsx index 2428fe7..abd2849 100644 --- a/web/src/pages/TemplateEditor.tsx +++ b/web/src/pages/TemplateEditor.tsx @@ -155,6 +155,7 @@ export default function TemplateEditor() { prompt: values.prompt, model_id: values.model_id, order: points.length, + selected_text: selectionRef.current?.text, ref_file: refFile, }); message.success("εˆ›ε»ΊζˆεŠŸ");