UX: queue bubbles, dashboard queuing, duplicate confirm buttons, short-note fix

- Dashboard chat: allow typing/sending during streaming (queued in store)
  placeholder updates to "Type to queue…" during generation
- ChatView/WorkspaceView: replace queued chip with actual pending message
  bubbles — light grey, dashed border, "Queued" badge above content; clear
  button below the stack
- ToolCallCard: when tool returns requires_confirmation=True, show
  "Similar content found" label (not "Error"), link to the similar note,
  and "Create Anyway" / "Don't Create" buttons that auto-send the reply
- tools.py: fuzzy title match now returns requires_confirmation=True with
  similar_note data instead of a hard error, so numbered-series notes
  (Lore: X 0, Lore: X 1) can be created with one button click; semantic
  match responses also include similar_note for the link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 23:32:21 -04:00
parent 12644999c1
commit d7e1fe6aab
7 changed files with 165 additions and 54 deletions
+4 -4
View File
@@ -823,7 +823,7 @@ async def execute_tool(user_id: int, tool_name: str, arguments: dict) -> dict:
near, ratio = _fuzzy_title_match(task_title, candidates)
if near is not None:
item_type = "task" if near.status is not None else "note"
return {"success": False, "error": f"A {item_type} with a very similar title '{near.title}' already exists (id: {near.id}, similarity: {ratio:.0%}). Use update_note to modify it instead of creating a duplicate."}
return {"success": False, "requires_confirmation": True, "similar_note": {"id": near.id, "title": near.title}, "error": f"A {item_type} with a very similar title '{near.title}' already exists (similarity: {ratio:.0%}). Ask the user to confirm before creating a separate entry."}
if not arguments.get("confirmed") and len(task_body.strip()) >= 80:
from fabledassistant.services.embeddings import semantic_search_notes as _ssn
@@ -832,7 +832,7 @@ async def execute_tool(user_id: int, tool_name: str, arguments: dict) -> dict:
if sem_hits:
best_score, best_note = sem_hits[0]
item_type = "task" if best_note.status is not None else "note"
return {"success": False, "requires_confirmation": True, "error": f"A {item_type} with very similar content exists: '{best_note.title}' (id: {best_note.id}, semantic similarity: {best_score:.0%}). Ask the user: 'An existing {item_type} covers very similar ground. Shall I still create a separate task titled \"{task_title}\"?' Then retry with confirmed=true if they agree."}
return {"success": False, "requires_confirmation": True, "similar_note": {"id": best_note.id, "title": best_note.title}, "error": f"A {item_type} with very similar content exists: '{best_note.title}' (semantic similarity: {best_score:.0%}). Ask the user to confirm before creating a separate entry."}
note = await create_note(
user_id=user_id,
@@ -901,7 +901,7 @@ async def execute_tool(user_id: int, tool_name: str, arguments: dict) -> dict:
near, ratio = _fuzzy_title_match(note_title, candidates)
if near is not None:
item_type = "task" if near.status is not None else "note"
return {"success": False, "error": f"A {item_type} with a very similar title '{near.title}' already exists (id: {near.id}, similarity: {ratio:.0%}). Use update_note to modify it instead of creating a duplicate."}
return {"success": False, "requires_confirmation": True, "similar_note": {"id": near.id, "title": near.title}, "error": f"A {item_type} with a very similar title '{near.title}' already exists (similarity: {ratio:.0%}). Ask the user to confirm before creating a separate entry."}
if not arguments.get("confirmed") and len(note_body.strip()) >= 80:
from fabledassistant.services.embeddings import semantic_search_notes as _ssn
@@ -910,7 +910,7 @@ async def execute_tool(user_id: int, tool_name: str, arguments: dict) -> dict:
if sem_hits:
best_score, best_note = sem_hits[0]
item_type = "task" if best_note.status is not None else "note"
return {"success": False, "requires_confirmation": True, "error": f"A {item_type} with very similar content exists: '{best_note.title}' (id: {best_note.id}, semantic similarity: {best_score:.0%}). Ask the user: 'An existing {item_type} covers very similar ground. Shall I still create a separate note titled \"{note_title}\"?' Then retry with confirmed=true if they agree."}
return {"success": False, "requires_confirmation": True, "similar_note": {"id": best_note.id, "title": best_note.title}, "error": f"A {item_type} with very similar content exists: '{best_note.title}' (semantic similarity: {best_score:.0%}). Ask the user to confirm before creating a separate entry."}
note = await create_note(
user_id=user_id,