feat: added field "title" and switched to it to free up caption for its normal use
This commit is contained in:
1
drizzle/0002_rainy_luckman.sql
Normal file
1
drizzle/0002_rainy_luckman.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE `media` ADD `title` text;
|
||||
759
drizzle/meta/0002_snapshot.json
Normal file
759
drizzle/meta/0002_snapshot.json
Normal file
@@ -0,0 +1,759 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "26aa5345-b6d8-4426-a144-0199140a896a",
|
||||
"prevId": "c9e34b7f-92a5-4549-99c9-e5a680004bfc",
|
||||
"tables": {
|
||||
"chat_conversations": {
|
||||
"name": "chat_conversations",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"copilot_session_id": {
|
||||
"name": "copilot_session_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"chat_messages": {
|
||||
"name": "chat_messages",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"conversation_id": {
|
||||
"name": "conversation_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"content": {
|
||||
"name": "content",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"tool_call_id": {
|
||||
"name": "tool_call_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"tool_calls": {
|
||||
"name": "tool_calls",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"import_definitions": {
|
||||
"name": "import_definitions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"project_id": {
|
||||
"name": "project_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"wxr_file_path": {
|
||||
"name": "wxr_file_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"uploads_folder_path": {
|
||||
"name": "uploads_folder_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"last_analysis_result": {
|
||||
"name": "last_analysis_result",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"media": {
|
||||
"name": "media",
|
||||
"columns": {
|
||||
"project_id": {
|
||||
"name": "project_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"filename": {
|
||||
"name": "filename",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"original_name": {
|
||||
"name": "original_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"mime_type": {
|
||||
"name": "mime_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"size": {
|
||||
"name": "size",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"width": {
|
||||
"name": "width",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"height": {
|
||||
"name": "height",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"alt": {
|
||||
"name": "alt",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"caption": {
|
||||
"name": "caption",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"file_path": {
|
||||
"name": "file_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"sidecar_path": {
|
||||
"name": "sidecar_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"checksum": {
|
||||
"name": "checksum",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"tags": {
|
||||
"name": "tags",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"post_links": {
|
||||
"name": "post_links",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"source_post_id": {
|
||||
"name": "source_post_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"target_post_id": {
|
||||
"name": "target_post_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"link_text": {
|
||||
"name": "link_text",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"post_media": {
|
||||
"name": "post_media",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"project_id": {
|
||||
"name": "project_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"post_id": {
|
||||
"name": "post_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"media_id": {
|
||||
"name": "media_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "sort_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"post_media_post_media_idx": {
|
||||
"name": "post_media_post_media_idx",
|
||||
"columns": [
|
||||
"post_id",
|
||||
"media_id"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"posts": {
|
||||
"name": "posts",
|
||||
"columns": {
|
||||
"project_id": {
|
||||
"name": "project_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"excerpt": {
|
||||
"name": "excerpt",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"content": {
|
||||
"name": "content",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "'draft'"
|
||||
},
|
||||
"author": {
|
||||
"name": "author",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"published_at": {
|
||||
"name": "published_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"file_path": {
|
||||
"name": "file_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": "''"
|
||||
},
|
||||
"checksum": {
|
||||
"name": "checksum",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"tags": {
|
||||
"name": "tags",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"categories": {
|
||||
"name": "categories",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"published_title": {
|
||||
"name": "published_title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"published_content": {
|
||||
"name": "published_content",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"published_tags": {
|
||||
"name": "published_tags",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"published_categories": {
|
||||
"name": "published_categories",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"published_excerpt": {
|
||||
"name": "published_excerpt",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"posts_project_slug_idx": {
|
||||
"name": "posts_project_slug_idx",
|
||||
"columns": [
|
||||
"project_id",
|
||||
"slug"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"projects": {
|
||||
"name": "projects",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"data_path": {
|
||||
"name": "data_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"is_active": {
|
||||
"name": "is_active",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"projects_slug_unique": {
|
||||
"name": "projects_slug_unique",
|
||||
"columns": [
|
||||
"slug"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"settings": {
|
||||
"name": "settings",
|
||||
"columns": {
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"tags": {
|
||||
"name": "tags",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"project_id": {
|
||||
"name": "project_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"color": {
|
||||
"name": "color",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"tags_project_name_idx": {
|
||||
"name": "tags_project_name_idx",
|
||||
"columns": [
|
||||
"project_id",
|
||||
"name"
|
||||
],
|
||||
"isUnique": true
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,13 @@
|
||||
"when": 1771088786493,
|
||||
"tag": "0001_narrow_black_bolt",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "6",
|
||||
"when": 1771141922712,
|
||||
"tag": "0002_rainy_luckman",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -54,6 +54,7 @@ export const media = sqliteTable('media', {
|
||||
size: integer('size').notNull(),
|
||||
width: integer('width'),
|
||||
height: integer('height'),
|
||||
title: text('title'),
|
||||
alt: text('alt'),
|
||||
caption: text('caption'),
|
||||
filePath: text('file_path').notNull(),
|
||||
|
||||
@@ -313,7 +313,7 @@ Available Tools:
|
||||
- list_media: List media files with optional MIME type filtering.
|
||||
- view_image: View an image to analyze its visual content. Use this when you need to describe or analyze what an image looks like.
|
||||
- update_post_metadata: Update a post's title, excerpt, tags, or categories.
|
||||
- update_media_metadata: Update a media file's alt text, caption, or tags.
|
||||
- update_media_metadata: Update a media file's title, alt text, caption, or tags.
|
||||
- list_tags: List all tags with post counts.
|
||||
- list_categories: List all categories with post counts.
|
||||
- get_post_backlinks: Get posts that link TO a given post (backlinks). Use to discover what references a post.
|
||||
|
||||
@@ -573,7 +573,7 @@ export class ImportExecutionEngine extends EventEmitter {
|
||||
// Import the media file
|
||||
const mediaEngine = getMediaEngine();
|
||||
await mediaEngine.importMedia(sourcePath, {
|
||||
caption: wxrMedia.title || undefined,
|
||||
title: wxrMedia.title || undefined,
|
||||
alt: wxrMedia.description || undefined,
|
||||
mimeType: wxrMedia.mimeType,
|
||||
tags: [],
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface MediaData {
|
||||
size: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
title?: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
createdAt: Date;
|
||||
@@ -42,6 +43,7 @@ export interface MediaMetadata {
|
||||
size: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
title?: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
createdAt: string;
|
||||
@@ -61,7 +63,7 @@ export interface MediaFilter {
|
||||
export interface MediaSearchResult {
|
||||
id: string;
|
||||
originalName: string;
|
||||
caption?: string;
|
||||
title?: string;
|
||||
mimeType: string;
|
||||
createdAt: Date;
|
||||
}
|
||||
@@ -92,12 +94,13 @@ export class MediaEngine extends EventEmitter {
|
||||
|
||||
/**
|
||||
* Update the FTS index for a media item.
|
||||
* Stores stemmed content from original_name, alt, caption, and tags.
|
||||
* Stores stemmed content from original_name, title, alt, caption, and tags.
|
||||
*/
|
||||
private async updateFTSIndex(item: {
|
||||
id: string;
|
||||
projectId: string;
|
||||
originalName: string;
|
||||
title?: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
tags: string[];
|
||||
@@ -111,6 +114,7 @@ export class MediaEngine extends EventEmitter {
|
||||
// Combine all searchable fields and stem them
|
||||
const allText = [
|
||||
item.originalName,
|
||||
item.title || '',
|
||||
item.alt || '',
|
||||
item.caption || '',
|
||||
item.tags.join(' '),
|
||||
@@ -300,6 +304,7 @@ export class MediaEngine extends EventEmitter {
|
||||
size: mediaData.size,
|
||||
width: mediaData.width,
|
||||
height: mediaData.height,
|
||||
title: mediaData.title,
|
||||
alt: mediaData.alt,
|
||||
caption: mediaData.caption,
|
||||
createdAt: mediaData.createdAt.toISOString(),
|
||||
@@ -319,6 +324,7 @@ export class MediaEngine extends EventEmitter {
|
||||
|
||||
if (metadata.width) lines.push(`width: ${metadata.width}`);
|
||||
if (metadata.height) lines.push(`height: ${metadata.height}`);
|
||||
if (metadata.title) lines.push(`title: "${metadata.title}"`);
|
||||
if (metadata.alt) lines.push(`alt: "${metadata.alt}"`);
|
||||
if (metadata.caption) lines.push(`caption: "${metadata.caption}"`);
|
||||
|
||||
@@ -385,6 +391,9 @@ export class MediaEngine extends EventEmitter {
|
||||
case 'height':
|
||||
metadata.height = parseInt(value, 10);
|
||||
break;
|
||||
case 'title':
|
||||
metadata.title = value;
|
||||
break;
|
||||
case 'alt':
|
||||
metadata.alt = value;
|
||||
break;
|
||||
@@ -492,6 +501,7 @@ export class MediaEngine extends EventEmitter {
|
||||
size: sourceBuffer.length,
|
||||
width,
|
||||
height,
|
||||
title: metadata?.title,
|
||||
alt: metadata?.alt,
|
||||
caption: metadata?.caption,
|
||||
createdAt,
|
||||
@@ -518,6 +528,7 @@ export class MediaEngine extends EventEmitter {
|
||||
size: mediaData.size,
|
||||
width: mediaData.width,
|
||||
height: mediaData.height,
|
||||
title: mediaData.title,
|
||||
alt: mediaData.alt,
|
||||
caption: mediaData.caption,
|
||||
filePath: destPath,
|
||||
@@ -535,6 +546,7 @@ export class MediaEngine extends EventEmitter {
|
||||
id: mediaData.id,
|
||||
projectId: this.currentProjectId,
|
||||
originalName: mediaData.originalName,
|
||||
title: mediaData.title,
|
||||
alt: mediaData.alt,
|
||||
caption: mediaData.caption,
|
||||
tags: mediaData.tags,
|
||||
@@ -566,6 +578,7 @@ export class MediaEngine extends EventEmitter {
|
||||
|
||||
await db.update(media)
|
||||
.set({
|
||||
title: updated.title,
|
||||
alt: updated.alt,
|
||||
caption: updated.caption,
|
||||
updatedAt: updated.updatedAt,
|
||||
@@ -578,6 +591,7 @@ export class MediaEngine extends EventEmitter {
|
||||
id: updated.id,
|
||||
projectId: this.currentProjectId,
|
||||
originalName: updated.originalName,
|
||||
title: updated.title,
|
||||
alt: updated.alt,
|
||||
caption: updated.caption,
|
||||
tags: updated.tags,
|
||||
@@ -641,6 +655,7 @@ export class MediaEngine extends EventEmitter {
|
||||
size: dbMedia.size,
|
||||
width: dbMedia.width || undefined,
|
||||
height: dbMedia.height || undefined,
|
||||
title: dbMedia.title || undefined,
|
||||
alt: dbMedia.alt || undefined,
|
||||
caption: dbMedia.caption || undefined,
|
||||
createdAt: dbMedia.createdAt,
|
||||
@@ -666,6 +681,7 @@ export class MediaEngine extends EventEmitter {
|
||||
size: dbMedia.size,
|
||||
width: dbMedia.width || undefined,
|
||||
height: dbMedia.height || undefined,
|
||||
title: dbMedia.title || undefined,
|
||||
alt: dbMedia.alt || undefined,
|
||||
caption: dbMedia.caption || undefined,
|
||||
createdAt: dbMedia.createdAt,
|
||||
@@ -726,6 +742,7 @@ export class MediaEngine extends EventEmitter {
|
||||
size: dbMedia.size,
|
||||
width: dbMedia.width || undefined,
|
||||
height: dbMedia.height || undefined,
|
||||
title: dbMedia.title || undefined,
|
||||
alt: dbMedia.alt || undefined,
|
||||
caption: dbMedia.caption || undefined,
|
||||
createdAt: dbMedia.createdAt,
|
||||
@@ -770,7 +787,7 @@ export class MediaEngine extends EventEmitter {
|
||||
searchResults.push({
|
||||
id: item.id,
|
||||
originalName: item.originalName,
|
||||
caption: item.caption || undefined,
|
||||
title: item.title || undefined,
|
||||
mimeType: item.mimeType,
|
||||
createdAt: item.createdAt,
|
||||
});
|
||||
|
||||
@@ -739,11 +739,12 @@ export class OpenCodeManager {
|
||||
},
|
||||
{
|
||||
name: 'update_media_metadata',
|
||||
description: 'Update metadata for a media file (alt text, caption, tags).',
|
||||
description: 'Update metadata for a media file (title, alt text, caption, tags).',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
mediaId: { type: 'string', description: 'The unique ID of the media to update' },
|
||||
title: { type: 'string', description: 'New title for display in lists and search results' },
|
||||
alt: { type: 'string', description: 'New alt text for the image' },
|
||||
caption: { type: 'string', description: 'New caption for the image' },
|
||||
tags: { type: 'array', items: { type: 'string' }, description: 'New tags for the media' },
|
||||
@@ -926,7 +927,7 @@ export class OpenCodeManager {
|
||||
id: media.id, filename: media.filename,
|
||||
originalName: media.originalName, mimeType: media.mimeType,
|
||||
size: media.size, width: media.width, height: media.height,
|
||||
alt: media.alt, caption: media.caption, tags: media.tags,
|
||||
title: media.title, alt: media.alt, caption: media.caption, tags: media.tags,
|
||||
createdAt: media.createdAt, updatedAt: media.updatedAt,
|
||||
},
|
||||
};
|
||||
@@ -945,7 +946,7 @@ export class OpenCodeManager {
|
||||
media: mediaList.map(m => ({
|
||||
id: m.id, filename: m.filename,
|
||||
originalName: m.originalName, mimeType: m.mimeType,
|
||||
alt: m.alt, tags: m.tags,
|
||||
title: m.title, alt: m.alt, tags: m.tags,
|
||||
})),
|
||||
};
|
||||
}
|
||||
@@ -967,6 +968,7 @@ export class OpenCodeManager {
|
||||
|
||||
case 'update_media_metadata': {
|
||||
const updates: Record<string, unknown> = {};
|
||||
if (args.title !== undefined) updates.title = args.title;
|
||||
if (args.alt !== undefined) updates.alt = args.alt;
|
||||
if (args.caption !== undefined) updates.caption = args.caption;
|
||||
if (args.tags !== undefined) updates.tags = args.tags;
|
||||
@@ -1033,6 +1035,7 @@ export class OpenCodeManager {
|
||||
originalName: mediaItem.originalName,
|
||||
width: mediaItem.width,
|
||||
height: mediaItem.height,
|
||||
title: mediaItem.title,
|
||||
alt: mediaItem.alt,
|
||||
caption: mediaItem.caption,
|
||||
size: size,
|
||||
@@ -1080,6 +1083,7 @@ export class OpenCodeManager {
|
||||
filename: link.media.filename,
|
||||
originalName: link.media.originalName,
|
||||
mimeType: link.media.mimeType,
|
||||
title: link.media.title,
|
||||
alt: link.media.alt,
|
||||
caption: link.media.caption,
|
||||
width: link.media.width,
|
||||
@@ -1451,11 +1455,12 @@ Remember: Only suggest mappings from NEW items to EXISTING items. Consider langu
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze a media image and generate alt text and caption using AI
|
||||
* Analyze a media image and generate title, alt text, and caption using AI
|
||||
* This is a one-shot request that looks at the image and suggests metadata
|
||||
*/
|
||||
async analyzeMediaImage(mediaId: string, language: string = 'en'): Promise<{
|
||||
success: boolean;
|
||||
title?: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
error?: string;
|
||||
@@ -1496,12 +1501,13 @@ Remember: Only suggest mappings from NEW items to EXISTING items. Consider langu
|
||||
};
|
||||
const languageName = languageNames[language] || language;
|
||||
|
||||
const systemPrompt = `Generate alt text and caption for this image in ${languageName}.
|
||||
const systemPrompt = `Generate title, alt text, and caption for this image in ${languageName}.
|
||||
|
||||
TITLE: A short, descriptive title for display in lists and search results (3-8 words). Should identify the main subject.
|
||||
ALT: Describe ONLY what is visually present in the image. Be factual, neutral, and concise (5-12 words max). No interpretations, emotions, or "Image of" prefix. Example: "Red bicycle leaning against white brick wall"
|
||||
CAPTION: Short, engaging blog caption (5-20 words).
|
||||
|
||||
Respond with JSON only: {"alt": "...", "caption": "..."}`;
|
||||
Respond with JSON only: {"title": "...", "alt": "...", "caption": "..."}`;
|
||||
|
||||
try {
|
||||
// Using Claude Sonnet 4.5 for best image analysis
|
||||
@@ -1570,6 +1576,7 @@ Respond with JSON only: {"alt": "...", "caption": "..."}`;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
title: result.title || undefined,
|
||||
alt: result.alt || undefined,
|
||||
caption: result.caption || undefined,
|
||||
};
|
||||
|
||||
@@ -333,7 +333,7 @@ export function registerChatHandlers(): void {
|
||||
|
||||
// ============ Media Analysis ============
|
||||
|
||||
// Analyze a media image and generate alt text and caption
|
||||
// Analyze a media image and generate title, alt text, and caption
|
||||
ipcMain.handle('chat:analyzeMediaImage', async (_, mediaId: string, language?: string) => {
|
||||
try {
|
||||
const manager = getOpenCodeManager();
|
||||
|
||||
@@ -1426,6 +1426,7 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
||||
const { media, updateMedia, showErrorModal, showConfirmDeleteModal, openTab } = useAppStore();
|
||||
const item = media.find(m => m.id === mediaId);
|
||||
|
||||
const [title, setTitle] = useState(item?.title || '');
|
||||
const [alt, setAlt] = useState(item?.alt || '');
|
||||
const [caption, setCaption] = useState(item?.caption || '');
|
||||
const [tags, setTags] = useState(item?.tags.join(', ') || '');
|
||||
@@ -1474,6 +1475,7 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
||||
const result = await window.electronAPI?.chat.analyzeMediaImage(item.id, projectLanguage);
|
||||
|
||||
if (result?.success) {
|
||||
if (result.title) setTitle(result.title);
|
||||
if (result.alt) setAlt(result.alt);
|
||||
if (result.caption) setCaption(result.caption);
|
||||
showToast.success('AI analysis complete');
|
||||
@@ -1581,6 +1583,7 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (item) {
|
||||
setTitle(item.title || '');
|
||||
setAlt(item.alt || '');
|
||||
setCaption(item.caption || '');
|
||||
setTags(item.tags.join(', '));
|
||||
@@ -1594,6 +1597,7 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
const updated = await window.electronAPI?.media.update(item.id, {
|
||||
title,
|
||||
alt,
|
||||
caption,
|
||||
tags: tags.split(',').map(t => t.trim()).filter(t => t.length > 0),
|
||||
@@ -1696,8 +1700,8 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
||||
>
|
||||
<span className="quick-action-icon">🤖</span>
|
||||
<span className="quick-action-text">
|
||||
<strong>AI: Generate Alt & Caption</strong>
|
||||
<small>Uses Claude Sonnet 4.5 to analyze the image</small>
|
||||
<strong>AI: Generate Title, Alt & Caption</strong>
|
||||
<small>Analyzes the image to suggest metadata</small>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -1755,6 +1759,15 @@ const MediaEditor: React.FC<{ mediaId: string }> = ({ mediaId }) => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="editor-field">
|
||||
<label>Title</label>
|
||||
<input
|
||||
type="text"
|
||||
value={title}
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
placeholder="Title for lists and search results"
|
||||
/>
|
||||
</div>
|
||||
<div className="editor-field">
|
||||
<label>Alt Text</label>
|
||||
<input
|
||||
|
||||
@@ -11,17 +11,17 @@ interface PostSearchResult {
|
||||
interface MediaSearchResult {
|
||||
id: string;
|
||||
originalName: string;
|
||||
caption?: string;
|
||||
title?: string;
|
||||
mimeType: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
/** Get display name for media: caption (truncated to 60 chars) or fallback to filename */
|
||||
/** Get display name for media: title (truncated to 60 chars) or fallback to filename */
|
||||
function getMediaDisplayName(media: MediaSearchResult): string {
|
||||
if (media.caption) {
|
||||
return media.caption.length > 60
|
||||
? media.caption.substring(0, 60) + '...'
|
||||
: media.caption;
|
||||
if (media.title) {
|
||||
return media.title.length > 60
|
||||
? media.title.substring(0, 60) + '...'
|
||||
: media.title;
|
||||
}
|
||||
return media.originalName;
|
||||
}
|
||||
@@ -187,7 +187,7 @@ export const InsertModal: React.FC<InsertModalProps> = ({
|
||||
const externalLabel = mode === 'link' ? 'External URL' : 'External Image';
|
||||
const searchPlaceholder = mode === 'link'
|
||||
? 'Search posts by title or content...'
|
||||
: 'Search media by name, caption, or alt text...';
|
||||
: 'Search media by name, title, or alt text...';
|
||||
|
||||
return (
|
||||
<div className="insert-modal-backdrop" onClick={handleBackdropClick}>
|
||||
|
||||
@@ -14,12 +14,12 @@ import { useAppStore, MediaData } from '../../store';
|
||||
import { showToast } from '../Toast';
|
||||
import './LinkedMediaPanel.css';
|
||||
|
||||
/** Get display name for media: caption (truncated to 60 chars) or fallback to filename */
|
||||
/** Get display name for media: title (truncated to 60 chars) or fallback to filename */
|
||||
function getMediaDisplayName(media: MediaData): string {
|
||||
if (media.caption) {
|
||||
return media.caption.length > 60
|
||||
? media.caption.substring(0, 60) + '...'
|
||||
: media.caption;
|
||||
if (media.title) {
|
||||
return media.title.length > 60
|
||||
? media.title.substring(0, 60) + '...'
|
||||
: media.title;
|
||||
}
|
||||
return media.originalName;
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@ export const SettingsView: React.FC = () => {
|
||||
<SettingRow
|
||||
id="project-language"
|
||||
label="Main Language"
|
||||
description="The primary language for your blog content. AI-generated alt text and captions will use this language."
|
||||
description="The primary language for your blog content. AI-generated titles, alt text, and captions will use this language."
|
||||
>
|
||||
<select
|
||||
id="project-language"
|
||||
|
||||
@@ -5,12 +5,12 @@ import { groupPostsByStatus } from '../../utils';
|
||||
import type { ChatConversation, ImportDefinitionData } from '../../types/electron';
|
||||
import './Sidebar.css';
|
||||
|
||||
/** Get display name for media: caption (truncated to 60 chars) or fallback to filename */
|
||||
/** Get display name for media: title (truncated to 60 chars) or fallback to filename */
|
||||
function getMediaDisplayName(media: MediaData): string {
|
||||
if (media.caption) {
|
||||
return media.caption.length > 60
|
||||
? media.caption.substring(0, 60) + '...'
|
||||
: media.caption;
|
||||
if (media.title) {
|
||||
return media.title.length > 60
|
||||
? media.title.substring(0, 60) + '...'
|
||||
: media.title;
|
||||
}
|
||||
return media.originalName;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ export interface MediaData {
|
||||
size: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
title?: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
createdAt: string;
|
||||
|
||||
4
src/renderer/types/electron.d.ts
vendored
4
src/renderer/types/electron.d.ts
vendored
@@ -85,6 +85,7 @@ export interface MediaData {
|
||||
size: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
title?: string;
|
||||
alt?: string;
|
||||
caption?: string;
|
||||
createdAt: string;
|
||||
@@ -101,6 +102,7 @@ export interface MediaFilter {
|
||||
export interface MediaSearchResult {
|
||||
id: string;
|
||||
originalName: string;
|
||||
title?: string;
|
||||
mimeType: string;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -424,7 +426,7 @@ export interface ElectronAPI {
|
||||
analyzeTaxonomy: (categories: Array<{ name: string; slug: string; existsInProject: boolean }>, tags: Array<{ name: string; slug: string; existsInProject: boolean }>, modelId: string) => Promise<{ success: boolean; categoryMappings?: Record<string, string>; tagMappings?: Record<string, string>; error?: string }>;
|
||||
|
||||
// Media Analysis
|
||||
analyzeMediaImage: (mediaId: string, language?: string) => Promise<{ success: boolean; alt?: string; caption?: string; error?: string }>;
|
||||
analyzeMediaImage: (mediaId: string, language?: string) => Promise<{ success: boolean; title?: string; alt?: string; caption?: string; error?: string }>;
|
||||
|
||||
// Event listeners for streaming/progress
|
||||
onStreamDelta: (callback: (data: ChatStreamDelta) => void) => () => void;
|
||||
|
||||
@@ -52,7 +52,7 @@ const insertedPosts: Array<{
|
||||
const insertedMedia: Array<{
|
||||
id: string;
|
||||
linkedPostIds: string[];
|
||||
caption?: string;
|
||||
title?: string;
|
||||
}> = [];
|
||||
|
||||
const createdTags: string[] = [];
|
||||
@@ -167,7 +167,7 @@ const mockMediaEngine = {
|
||||
id: `media-${Math.random().toString(36).substr(2, 9)}`,
|
||||
filename: path.basename(sourcePath),
|
||||
originalName: metadata?.originalName || path.basename(sourcePath),
|
||||
caption: metadata?.caption,
|
||||
title: metadata?.title,
|
||||
linkedPostIds: metadata?.linkedPostIds || [],
|
||||
};
|
||||
insertedMedia.push(result);
|
||||
@@ -1044,9 +1044,9 @@ describe('ImportExecutionEngine E2E Tests', () => {
|
||||
|
||||
expect(result.media.imported).toBe(1);
|
||||
|
||||
// Should be imported with caption from WXR title
|
||||
// Should be imported with title from WXR title
|
||||
expect(insertedMedia.length).toBe(1);
|
||||
expect(insertedMedia[0].caption).toBe('standalone-logo');
|
||||
expect(insertedMedia[0].title).toBe('standalone-logo');
|
||||
|
||||
// No linked posts (standalone)
|
||||
expect(insertedMedia[0].linkedPostIds.length).toBe(0);
|
||||
|
||||
@@ -1016,7 +1016,7 @@ describe('ImportExecutionEngine', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should set caption from WXR title', async () => {
|
||||
it('should set title from WXR title', async () => {
|
||||
const wxrMedia = createMockWxrMedia({ title: 'Beautiful Sunset' });
|
||||
const report = createMockAnalysisReport({
|
||||
media: {
|
||||
@@ -1035,7 +1035,7 @@ describe('ImportExecutionEngine', () => {
|
||||
expect(mockMediaEngine.importMedia).toHaveBeenCalledWith(
|
||||
expect.any(String),
|
||||
expect.objectContaining({
|
||||
caption: 'Beautiful Sunset',
|
||||
title: 'Beautiful Sunset',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -535,11 +535,19 @@ describe('MediaEngine', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Alt Text and Caption', () => {
|
||||
describe('Title, Alt Text and Caption', () => {
|
||||
beforeEach(() => {
|
||||
mockFiles.set('/source/image.jpg', Buffer.from('image-data'));
|
||||
});
|
||||
|
||||
it('should store title for display in lists and search', async () => {
|
||||
const media = await mediaEngine.importMedia('/source/image.jpg', {
|
||||
title: 'Mountain Sunrise Photo',
|
||||
});
|
||||
|
||||
expect(media.title).toBe('Mountain Sunrise Photo');
|
||||
});
|
||||
|
||||
it('should store alt text for accessibility', async () => {
|
||||
const media = await mediaEngine.importMedia('/source/image.jpg', {
|
||||
alt: 'A scenic mountain view',
|
||||
@@ -556,9 +564,10 @@ describe('MediaEngine', () => {
|
||||
expect(media.caption).toBe('Photo taken at Mt. Rainier, 2024');
|
||||
});
|
||||
|
||||
it('should handle media without alt or caption', async () => {
|
||||
it('should handle media without title, alt or caption', async () => {
|
||||
const media = await mediaEngine.importMedia('/source/image.jpg');
|
||||
|
||||
expect(media.title).toBeUndefined();
|
||||
expect(media.alt).toBeUndefined();
|
||||
expect(media.caption).toBeUndefined();
|
||||
});
|
||||
@@ -847,10 +856,36 @@ linkedPostIds: ["post-a", "post-b", "post-c"]`;
|
||||
return chain;
|
||||
});
|
||||
|
||||
const result = await mediaEngine.updateMedia('non-existent-id', { caption: 'New caption' });
|
||||
const result = await mediaEngine.updateMedia('non-existent-id', { title: 'New title' });
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should update media title', async () => {
|
||||
vi.mocked(mockLocalDb.select).mockImplementation(() => {
|
||||
const chain = createSelectChain();
|
||||
chain.where = vi.fn().mockReturnValue({
|
||||
...chain,
|
||||
get: vi.fn().mockResolvedValue({
|
||||
id: 'media-id',
|
||||
projectId: 'default',
|
||||
originalName: 'test.jpg',
|
||||
mimeType: 'image/jpeg',
|
||||
size: 1024,
|
||||
filePath: '/mock/media/test.jpg',
|
||||
title: 'Old title',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
}),
|
||||
});
|
||||
return chain;
|
||||
});
|
||||
|
||||
const result = await mediaEngine.updateMedia('media-id', { title: 'New title' });
|
||||
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.title).toBe('New title');
|
||||
});
|
||||
|
||||
it('should update media caption', async () => {
|
||||
vi.mocked(mockLocalDb.select).mockImplementation(() => {
|
||||
const chain = createSelectChain();
|
||||
|
||||
@@ -64,6 +64,7 @@ export function createMockMedia(overrides?: Partial<MediaData>): MediaData {
|
||||
size: 1024 * 100, // 100KB
|
||||
width: 800,
|
||||
height: 600,
|
||||
title: 'Test Image Title',
|
||||
alt: 'Test image',
|
||||
caption: 'A test image caption',
|
||||
createdAt: now,
|
||||
|
||||
Reference in New Issue
Block a user