Files
FabledScribe/src/fabledassistant/services/note_drafts.py
T
bvandeusen 9036dfd931 Note editor sidebar, full-doc assist, persistent drafts, version history
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>
2026-03-05 17:10:55 -05:00

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