9036dfd931
NoteEditorView: two-column sidebar layout (project/milestone/tags/assist always visible), removed assist toggle button, InlineAssistPanel removed. Writing assist: whole_doc mode rewrites entire document; DiffView.vue replaces editor during review showing full-document diff. Scope dropdown in sidebar switches between whole-document and section modes. Persistent drafts: migration 0022 adds note_drafts (UNIQUE per note+user) and note_versions (max 20, auto-pruned) tables. Draft saved after generation completes, restored on editor mount, cleared on accept/reject. Version snapshot created automatically whenever note body changes on save. HistoryPanel.vue: version list + DiffView modal, restore button writes body back to editor. Config: OLLAMA_NUM_CTX default raised to 65536; assist num_predict now tracks Config.OLLAMA_NUM_CTX instead of a hardcoded 4096. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
2.2 KiB
Python
67 lines
2.2 KiB
Python
from sqlalchemy import select, text
|
|
|
|
from fabledassistant.models import async_session
|
|
from fabledassistant.models.note_draft import NoteDraft
|
|
|
|
|
|
async def upsert_draft(
|
|
user_id: int,
|
|
note_id: int,
|
|
proposed_body: str,
|
|
original_body: str,
|
|
instruction: str,
|
|
scope: str,
|
|
) -> NoteDraft:
|
|
async with async_session() as session:
|
|
await session.execute(
|
|
text("""
|
|
INSERT INTO note_drafts (note_id, user_id, proposed_body, original_body, instruction, scope)
|
|
VALUES (:note_id, :user_id, :proposed_body, :original_body, :instruction, :scope)
|
|
ON CONFLICT (note_id, user_id) DO UPDATE SET
|
|
proposed_body = EXCLUDED.proposed_body,
|
|
original_body = EXCLUDED.original_body,
|
|
instruction = EXCLUDED.instruction,
|
|
scope = EXCLUDED.scope,
|
|
updated_at = NOW()
|
|
""").bindparams(
|
|
note_id=note_id,
|
|
user_id=user_id,
|
|
proposed_body=proposed_body,
|
|
original_body=original_body,
|
|
instruction=instruction,
|
|
scope=scope,
|
|
)
|
|
)
|
|
await session.commit()
|
|
result = await session.execute(
|
|
select(NoteDraft).where(
|
|
NoteDraft.note_id == note_id, NoteDraft.user_id == user_id
|
|
)
|
|
)
|
|
return result.scalars().first()
|
|
|
|
|
|
async def get_draft(user_id: int, note_id: int) -> NoteDraft | None:
|
|
async with async_session() as session:
|
|
result = await session.execute(
|
|
select(NoteDraft).where(
|
|
NoteDraft.note_id == note_id, NoteDraft.user_id == user_id
|
|
)
|
|
)
|
|
return result.scalars().first()
|
|
|
|
|
|
async def delete_draft(user_id: int, note_id: int) -> bool:
|
|
async with async_session() as session:
|
|
result = await session.execute(
|
|
select(NoteDraft).where(
|
|
NoteDraft.note_id == note_id, NoteDraft.user_id == user_id
|
|
)
|
|
)
|
|
draft = result.scalars().first()
|
|
if draft is None:
|
|
return False
|
|
await session.delete(draft)
|
|
await session.commit()
|
|
return True
|