Add Tiptap inline-preview editor, layout improvements, and README
Replace plain textarea with Tiptap (ProseMirror) WYSIWYG editor for notes and tasks. Headings, bold, lists, and code blocks now render inline while editing. Tags and wikilinks get visual highlighting via decoration plugins, and autocomplete uses @tiptap/suggestion with heading disambiguation. Layout: pin navbar with app-shell flex column (100dvh), sticky editor toolbar above scrolling content, AI Assist panel fixed at bottom 1/3 with full-height section selector and prompt. Increase max-width to 960px across all views. Hide assist controls during streaming/review. Other fixes: markdown paste handling, auto-syncing assist sections via body watcher, trailing newline on AI accept, ChatView height fix. Add README.md describing the app, usage guide, and technical overview. Update summary.md with Phase 5.2 roadmap and current status. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
import json
|
||||
import logging
|
||||
from datetime import date
|
||||
|
||||
from quart import Blueprint, jsonify, request
|
||||
from quart import Blueprint, Response, jsonify, request
|
||||
|
||||
from fabledassistant.auth import login_required, get_current_user_id
|
||||
from fabledassistant.config import Config
|
||||
from fabledassistant.services.assist import build_assist_messages
|
||||
from fabledassistant.services.llm import stream_chat
|
||||
from fabledassistant.services.notes import (
|
||||
convert_note_to_task,
|
||||
convert_task_to_note,
|
||||
@@ -16,8 +21,11 @@ from fabledassistant.services.notes import (
|
||||
list_notes,
|
||||
update_note,
|
||||
)
|
||||
from fabledassistant.services.settings import get_setting
|
||||
from fabledassistant.utils.tags import extract_tags
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
notes_bp = Blueprint("notes", __name__, url_prefix="/api/notes")
|
||||
|
||||
|
||||
@@ -178,3 +186,41 @@ async def get_backlinks_route(note_id: int):
|
||||
uid = get_current_user_id()
|
||||
links = await get_backlinks(uid, note_id)
|
||||
return jsonify({"backlinks": links})
|
||||
|
||||
|
||||
@notes_bp.route("/assist", methods=["POST"])
|
||||
@login_required
|
||||
async def assist_route():
|
||||
"""Stream an AI-assisted section edit via SSE."""
|
||||
uid = get_current_user_id()
|
||||
data = await request.get_json()
|
||||
|
||||
body = data.get("body", "")
|
||||
target_section = data.get("target_section", "")
|
||||
instruction = data.get("instruction", "")
|
||||
|
||||
if not target_section or not instruction:
|
||||
return jsonify({"error": "target_section and instruction are required"}), 400
|
||||
|
||||
model = await get_setting(uid, "default_model", Config.OLLAMA_MODEL)
|
||||
messages = build_assist_messages(body, target_section, instruction)
|
||||
|
||||
async def generate():
|
||||
full_text = ""
|
||||
try:
|
||||
async for chunk in stream_chat(messages, model):
|
||||
full_text += chunk
|
||||
yield f"data: {json.dumps({'chunk': chunk})}\n\n"
|
||||
yield f"data: {json.dumps({'done': True, 'full_text': full_text})}\n\n"
|
||||
except Exception as e:
|
||||
logger.exception("Assist generation failed")
|
||||
yield f"data: {json.dumps({'error': str(e)})}\n\n"
|
||||
|
||||
return Response(
|
||||
generate(),
|
||||
content_type="text/event-stream",
|
||||
headers={
|
||||
"Cache-Control": "no-cache",
|
||||
"X-Accel-Buffering": "no",
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user