Add source manager and inline citing with 'sourced' badges

New /trees/[id]/sources page (list + create sources). Person-detail page now loads tree sources + citations and shows a '✓ N sourced' badge with an inline cite picker (source + page) on each event and on the person. Tree view links to Sources. Regenerated the OpenAPI client.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Justin Paul <justin@jpaul.me>
This commit is contained in:
2026-06-06 13:17:33 -04:00
parent 064bb6ea65
commit 83f83ab641
5 changed files with 1449 additions and 46 deletions
+766
View File
@@ -849,10 +849,571 @@
}
}
}
},
"/api/v1/trees/{tree_id}/sources": {
"post": {
"tags": [
"sources"
],
"summary": "Create Source",
"operationId": "create_source_api_v1_trees__tree_id__sources_post",
"parameters": [
{
"name": "tree_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid",
"title": "Tree Id"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SourceCreate"
}
}
}
},
"responses": {
"201": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SourceRead"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
},
"get": {
"tags": [
"sources"
],
"summary": "List Sources",
"operationId": "list_sources_api_v1_trees__tree_id__sources_get",
"parameters": [
{
"name": "tree_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid",
"title": "Tree Id"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SourceRead"
},
"title": "Response List Sources Api V1 Trees Tree Id Sources Get"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/api/v1/trees/{tree_id}/sources/{source_id}": {
"get": {
"tags": [
"sources"
],
"summary": "Get Source",
"operationId": "get_source_api_v1_trees__tree_id__sources__source_id__get",
"parameters": [
{
"name": "tree_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid",
"title": "Tree Id"
}
},
{
"name": "source_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid",
"title": "Source Id"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SourceRead"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
},
"delete": {
"tags": [
"sources"
],
"summary": "Delete Source",
"operationId": "delete_source_api_v1_trees__tree_id__sources__source_id__delete",
"parameters": [
{
"name": "tree_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid",
"title": "Tree Id"
}
},
{
"name": "source_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid",
"title": "Source Id"
}
}
],
"responses": {
"204": {
"description": "Successful Response"
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/api/v1/trees/{tree_id}/citations": {
"post": {
"tags": [
"citations"
],
"summary": "Create Citation",
"operationId": "create_citation_api_v1_trees__tree_id__citations_post",
"parameters": [
{
"name": "tree_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid",
"title": "Tree Id"
}
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CitationCreate"
}
}
}
},
"responses": {
"201": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CitationRead"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
},
"get": {
"tags": [
"citations"
],
"summary": "List Citations",
"operationId": "list_citations_api_v1_trees__tree_id__citations_get",
"parameters": [
{
"name": "tree_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid",
"title": "Tree Id"
}
}
],
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/CitationRead"
},
"title": "Response List Citations Api V1 Trees Tree Id Citations Get"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
},
"/api/v1/trees/{tree_id}/citations/{citation_id}": {
"delete": {
"tags": [
"citations"
],
"summary": "Delete Citation",
"operationId": "delete_citation_api_v1_trees__tree_id__citations__citation_id__delete",
"parameters": [
{
"name": "tree_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid",
"title": "Tree Id"
}
},
{
"name": "citation_id",
"in": "path",
"required": true,
"schema": {
"type": "string",
"format": "uuid",
"title": "Citation Id"
}
}
],
"responses": {
"204": {
"description": "Successful Response"
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"CitationConfidence": {
"type": "string",
"enum": [
"high",
"medium",
"low"
],
"title": "CitationConfidence"
},
"CitationCreate": {
"properties": {
"source_id": {
"type": "string",
"format": "uuid",
"title": "Source Id"
},
"person_id": {
"anyOf": [
{
"type": "string",
"format": "uuid"
},
{
"type": "null"
}
],
"title": "Person Id"
},
"event_id": {
"anyOf": [
{
"type": "string",
"format": "uuid"
},
{
"type": "null"
}
],
"title": "Event Id"
},
"name_id": {
"anyOf": [
{
"type": "string",
"format": "uuid"
},
{
"type": "null"
}
],
"title": "Name Id"
},
"relationship_id": {
"anyOf": [
{
"type": "string",
"format": "uuid"
},
{
"type": "null"
}
],
"title": "Relationship Id"
},
"page": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Page"
},
"detail": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Detail"
},
"confidence": {
"anyOf": [
{
"$ref": "#/components/schemas/CitationConfidence"
},
{
"type": "null"
}
]
}
},
"type": "object",
"required": [
"source_id"
],
"title": "CitationCreate"
},
"CitationRead": {
"properties": {
"id": {
"type": "string",
"format": "uuid",
"title": "Id"
},
"tree_id": {
"type": "string",
"format": "uuid",
"title": "Tree Id"
},
"source_id": {
"type": "string",
"format": "uuid",
"title": "Source Id"
},
"person_id": {
"anyOf": [
{
"type": "string",
"format": "uuid"
},
{
"type": "null"
}
],
"title": "Person Id"
},
"event_id": {
"anyOf": [
{
"type": "string",
"format": "uuid"
},
{
"type": "null"
}
],
"title": "Event Id"
},
"name_id": {
"anyOf": [
{
"type": "string",
"format": "uuid"
},
{
"type": "null"
}
],
"title": "Name Id"
},
"relationship_id": {
"anyOf": [
{
"type": "string",
"format": "uuid"
},
{
"type": "null"
}
],
"title": "Relationship Id"
},
"page": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Page"
},
"detail": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Detail"
},
"confidence": {
"anyOf": [
{
"$ref": "#/components/schemas/CitationConfidence"
},
{
"type": "null"
}
]
},
"created_at": {
"type": "string",
"format": "date-time",
"title": "Created At"
}
},
"type": "object",
"required": [
"id",
"tree_id",
"source_id",
"person_id",
"event_id",
"name_id",
"relationship_id",
"page",
"detail",
"confidence",
"created_at"
],
"title": "CitationRead"
},
"EventCreate": {
"properties": {
"event_type": {
@@ -1512,6 +2073,211 @@
],
"title": "SessionRead"
},
"SourceCreate": {
"properties": {
"title": {
"type": "string",
"title": "Title"
},
"author": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Author"
},
"source_type": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Source Type"
},
"repository": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Repository"
},
"url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Url"
},
"citation_text": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Citation Text"
},
"publication_info": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Publication Info"
},
"quality_note": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Quality Note"
}
},
"type": "object",
"required": [
"title"
],
"title": "SourceCreate"
},
"SourceRead": {
"properties": {
"id": {
"type": "string",
"format": "uuid",
"title": "Id"
},
"tree_id": {
"type": "string",
"format": "uuid",
"title": "Tree Id"
},
"title": {
"type": "string",
"title": "Title"
},
"author": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Author"
},
"source_type": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Source Type"
},
"repository": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Repository"
},
"url": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Url"
},
"citation_text": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Citation Text"
},
"publication_info": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Publication Info"
},
"quality_note": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"title": "Quality Note"
},
"created_at": {
"type": "string",
"format": "date-time",
"title": "Created At"
}
},
"type": "object",
"required": [
"id",
"tree_id",
"title",
"author",
"source_type",
"repository",
"url",
"citation_text",
"publication_info",
"quality_note",
"created_at"
],
"title": "SourceRead"
},
"TokenRequest": {
"properties": {
"token": {