Project Workspace view, abort button, session invalidation, workspace fixes
Workspace (/workspace/:projectId): - Three-panel layout (tasks / chat / notes) with CSS grid collapse toggles - WorkspaceTaskPanel: tasks grouped by milestone, collapsible groups, task detail slide-over with status cycling, TaskLogSection work log, and inline-confirm delete - WorkspaceNoteEditor: list view (sorted by updated_at, inline tag pills, inline-confirm delete) with editor view (TipTap, TagInput, Suggest tags, 60s autosave) - Persistent workspace conversation stored in localStorage per project; reused on return visits - Thinking enabled (think: true) with Reasoning block in streaming bubble - workspace_project_id backend pipeline: chat.py → generation_task.py → llm.py; system prompt uses project title so agent passes project="Title" to tools (fixes create_note failing with numeric project string) - SSE tool-call watcher bridges agent actions to panel updates - Height fix: workspace-root uses height 100%; app-content switches to overflow hidden via :has() selector - Entry point: "Open Workspace" button on ProjectView Abort button: - Stop button in ChatView header and WorkspaceView input bar - Calls existing cancelGeneration() / POST .../generation/cancel Session invalidation: - POST /api/auth/invalidate-sessions bumps session_version, keeps current session alive; useful after SSO/OAuth password rotation - Button in Settings → Active Sessions section Other: - Dashboard recent notes limit increased from 8 to 16 - Workspace chat abort replaces Send button while streaming Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,6 +18,7 @@ from fabledassistant.services.auth import (
|
||||
get_user_by_id,
|
||||
get_user_by_username,
|
||||
get_user_count,
|
||||
invalidate_other_sessions,
|
||||
is_registration_open,
|
||||
register_with_invitation,
|
||||
reset_password_with_token,
|
||||
@@ -166,6 +167,21 @@ async def update_password():
|
||||
return jsonify({"status": "ok"})
|
||||
|
||||
|
||||
@auth_bp.route("/invalidate-sessions", methods=["POST"])
|
||||
@login_required
|
||||
async def invalidate_sessions_route():
|
||||
"""Invalidate all other active sessions by bumping the session version.
|
||||
|
||||
The current session is kept alive. Useful after an SSO/OAuth password change
|
||||
where the app has no visibility into credential rotation.
|
||||
"""
|
||||
uid = get_current_user_id()
|
||||
new_version = await invalidate_other_sessions(uid)
|
||||
session["session_version"] = new_version
|
||||
await log_audit("sessions_invalidated", user_id=uid, username=g.user.username, ip_address=_client_ip())
|
||||
return jsonify({"status": "ok"})
|
||||
|
||||
|
||||
@auth_bp.route("/email", methods=["PUT"])
|
||||
@login_required
|
||||
async def update_email():
|
||||
|
||||
@@ -118,6 +118,8 @@ async def send_message_route(conv_id: int):
|
||||
excluded_note_ids = data.get("excluded_note_ids") or []
|
||||
think = bool(data.get("think", False))
|
||||
rag_project_id = data.get("rag_project_id") or None
|
||||
workspace_project_id = data.get("workspace_project_id") or None
|
||||
effective_rag_project_id = workspace_project_id or rag_project_id
|
||||
|
||||
# Reject if generation already running for this conversation
|
||||
existing = get_buffer(conv_id)
|
||||
@@ -151,7 +153,8 @@ async def send_message_route(conv_id: int):
|
||||
include_note_ids=include_note_ids,
|
||||
excluded_note_ids=excluded_note_ids,
|
||||
think=think,
|
||||
rag_project_id=rag_project_id,
|
||||
rag_project_id=effective_rag_project_id,
|
||||
workspace_project_id=workspace_project_id,
|
||||
))
|
||||
|
||||
return jsonify({
|
||||
|
||||
Reference in New Issue
Block a user