feat: Knowledge view + entity types (People, Places, Lists)
Data model: - Migration 0036: adds note_type TEXT (default 'note') and metadata JSONB to the notes table; index on note_type - Note model: entity_type property, note_type/metadata in to_dict() - create_note() accepts note_type and metadata params Backend: - /api/knowledge — unified paginated endpoint: type/tag/sort/q filters, semantic search via embeddings, excludes tasks - /api/knowledge/tags — distinct tags across knowledge objects - New LLM tools: create_person, create_place, create_list, add_to_list, clear_checked_items — all wired into execute_tool() - People and places auto-injected as compact summary into LLM system prompt Frontend: - KnowledgeView replaces HomeView at /; left filter panel (type+tag), toolbar (search, sort, graph toggle), card grid with type-aware cards (indigo=note, emerald=person, amber=place, sky=list), load-more pagination - Today bar: upcoming events, overdue task count, Briefing/Chat links - Floating mini-chat sticky to bottom: creates/continues a conversation inline, message history expands upward, close button ends session - Graph panel: toggles as a 420px right panel at full viewport width - AppHeader: Knowledge, Chat, Briefing, Calendar, Tasks, Projects - Router: / → KnowledgeView; /knowledge redirect; HomeView import removed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,10 @@ class Note(Base, TimestampMixin):
|
||||
recurrence_next_spawn_at: Mapped[datetime | None] = mapped_column(
|
||||
DateTime(timezone=True), nullable=True
|
||||
)
|
||||
# Entity type — 'note' (default), 'person', 'place', 'list'
|
||||
note_type: Mapped[str] = mapped_column(Text, default="note", server_default="note")
|
||||
# Structured metadata for entity types (person/place/list)
|
||||
metadata: Mapped[dict | None] = mapped_column(JSONB, nullable=True)
|
||||
|
||||
__table_args__ = (
|
||||
Index("ix_notes_tags", "tags", postgresql_using="gin"),
|
||||
@@ -59,12 +63,18 @@ class Note(Base, TimestampMixin):
|
||||
Index("ix_notes_user_id", "user_id"),
|
||||
Index("ix_notes_project_id", "project_id"),
|
||||
Index("ix_notes_milestone_id", "milestone_id"),
|
||||
Index("ix_notes_note_type", "note_type"),
|
||||
)
|
||||
|
||||
@property
|
||||
def is_task(self) -> bool:
|
||||
return self.status is not None
|
||||
|
||||
@property
|
||||
def entity_type(self) -> str:
|
||||
"""Normalised type: 'note', 'person', 'place', or 'list'."""
|
||||
return self.note_type or "note"
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
return {
|
||||
"id": self.id,
|
||||
@@ -86,6 +96,8 @@ class Note(Base, TimestampMixin):
|
||||
else None
|
||||
),
|
||||
"is_task": self.is_task,
|
||||
"note_type": self.entity_type,
|
||||
"metadata": self.metadata or {},
|
||||
"created_at": self.created_at.isoformat(),
|
||||
"updated_at": self.updated_at.isoformat(),
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user