From 3188698ccb0dbcf31fc476b0973ce005d958ff2f Mon Sep 17 00:00:00 2001 From: Akshaj Tiwari Date: Tue, 16 Jun 2026 23:19:21 +0530 Subject: [PATCH] text search functionality --- docs/backend/backend_python/openapi.json | 345 ++++++++++++++---- .../components/Navigation/Navbar/Navbar.tsx | 9 +- frontend/src/features/searchSlice.ts | 21 +- frontend/src/pages/AITagging/AITagging.tsx | 43 ++- 4 files changed, 325 insertions(+), 93 deletions(-) diff --git a/docs/backend/backend_python/openapi.json b/docs/backend/backend_python/openapi.json index be4408477..6625cf8c8 100644 --- a/docs/backend/backend_python/openapi.json +++ b/docs/backend/backend_python/openapi.json @@ -18,7 +18,9 @@ "paths": { "/health": { "get": { - "tags": ["Health"], + "tags": [ + "Health" + ], "summary": "Root", "operationId": "root_health_get", "responses": { @@ -35,7 +37,9 @@ }, "/folders/add-folder": { "post": { - "tags": ["Folders"], + "tags": [ + "Folders" + ], "summary": "Add Folder", "operationId": "add_folder_folders_add_folder_post", "requestBody": { @@ -114,7 +118,9 @@ }, "/folders/enable-ai-tagging": { "post": { - "tags": ["Folders"], + "tags": [ + "Folders" + ], "summary": "Enable Ai Tagging", "description": "Enable AI tagging for multiple folders.", "operationId": "enable_ai_tagging_folders_enable_ai_tagging_post", @@ -174,7 +180,9 @@ }, "/folders/disable-ai-tagging": { "post": { - "tags": ["Folders"], + "tags": [ + "Folders" + ], "summary": "Disable Ai Tagging", "description": "Disable AI tagging for multiple folders.", "operationId": "disable_ai_tagging_folders_disable_ai_tagging_post", @@ -234,7 +242,9 @@ }, "/folders/delete-folders": { "delete": { - "tags": ["Folders"], + "tags": [ + "Folders" + ], "summary": "Delete Folders", "description": "Delete multiple folders by their IDs.", "operationId": "delete_folders_folders_delete_folders_delete", @@ -294,7 +304,9 @@ }, "/folders/sync-folder": { "post": { - "tags": ["Folders"], + "tags": [ + "Folders" + ], "summary": "Sync Folder", "description": "Sync a folder by comparing filesystem folders with database entries and removing extra DB entries.", "operationId": "sync_folder_folders_sync_folder_post", @@ -364,7 +376,9 @@ }, "/folders/all-folders": { "get": { - "tags": ["Folders"], + "tags": [ + "Folders" + ], "summary": "Get All Folders", "description": "Get details of all folders in the database.", "operationId": "get_all_folders_folders_all_folders_get", @@ -394,7 +408,9 @@ }, "/albums/": { "get": { - "tags": ["Albums"], + "tags": [ + "Albums" + ], "summary": "Get Albums", "operationId": "get_albums_albums__get", "parameters": [ @@ -433,7 +449,9 @@ } }, "post": { - "tags": ["Albums"], + "tags": [ + "Albums" + ], "summary": "Create Album", "operationId": "create_album_albums__post", "requestBody": { @@ -472,7 +490,9 @@ }, "/albums/{album_id}": { "get": { - "tags": ["Albums"], + "tags": [ + "Albums" + ], "summary": "Get Album", "operationId": "get_album_albums__album_id__get", "parameters": [ @@ -510,7 +530,9 @@ } }, "put": { - "tags": ["Albums"], + "tags": [ + "Albums" + ], "summary": "Update Album", "operationId": "update_album_albums__album_id__put", "parameters": [ @@ -558,7 +580,9 @@ } }, "delete": { - "tags": ["Albums"], + "tags": [ + "Albums" + ], "summary": "Delete Album", "operationId": "delete_album_albums__album_id__delete", "parameters": [ @@ -598,7 +622,9 @@ }, "/albums/{album_id}/images/get": { "post": { - "tags": ["Albums"], + "tags": [ + "Albums" + ], "summary": "Get Album Images", "operationId": "get_album_images_albums__album_id__images_get_post", "parameters": [ @@ -648,7 +674,9 @@ }, "/albums/{album_id}/images": { "post": { - "tags": ["Albums"], + "tags": [ + "Albums" + ], "summary": "Add Images To Album", "operationId": "add_images_to_album_albums__album_id__images_post", "parameters": [ @@ -696,7 +724,9 @@ } }, "delete": { - "tags": ["Albums"], + "tags": [ + "Albums" + ], "summary": "Remove Images From Album", "operationId": "remove_images_from_album_albums__album_id__images_delete", "parameters": [ @@ -746,7 +776,9 @@ }, "/albums/{album_id}/images/{image_id}": { "delete": { - "tags": ["Albums"], + "tags": [ + "Albums" + ], "summary": "Remove Image From Album", "operationId": "remove_image_from_album_albums__album_id__images__image_id__delete", "parameters": [ @@ -795,7 +827,9 @@ }, "/images/": { "get": { - "tags": ["Images"], + "tags": [ + "Images" + ], "summary": "Get All Images", "description": "Get all images from the database.", "operationId": "get_all_images_images__get", @@ -855,8 +889,11 @@ }, "/images/toggle-favourite": { "post": { - "tags": ["Images"], + "tags": [ + "Images" + ], "summary": "Toggle Favourite", + "description": "Toggle the favorite status of an image.", "operationId": "toggle_favourite_images_toggle_favourite_post", "requestBody": { "content": { @@ -892,7 +929,9 @@ }, "/face-clusters/{cluster_id}": { "put": { - "tags": ["Face Clusters"], + "tags": [ + "Face Clusters" + ], "summary": "Rename Cluster", "description": "Rename a face cluster by its ID.", "operationId": "rename_cluster_face_clusters__cluster_id__put", @@ -973,7 +1012,9 @@ }, "/face-clusters/": { "get": { - "tags": ["Face Clusters"], + "tags": [ + "Face Clusters" + ], "summary": "Get All Clusters", "description": "Get metadata for all face clusters including face counts.", "operationId": "get_all_clusters_face_clusters__get", @@ -1003,7 +1044,9 @@ }, "/face-clusters/{cluster_id}/images": { "get": { - "tags": ["Face Clusters"], + "tags": [ + "Face Clusters" + ], "summary": "Get Cluster Images", "description": "Get all images that contain faces belonging to a specific cluster.", "operationId": "get_cluster_images_face_clusters__cluster_id__images_get", @@ -1064,7 +1107,9 @@ }, "/face-clusters/face-search": { "post": { - "tags": ["Face Clusters"], + "tags": [ + "Face Clusters" + ], "summary": "Face Tagging", "operationId": "face_tagging_face_clusters_face_search_post", "parameters": [ @@ -1139,7 +1184,9 @@ }, "/face-clusters/global-recluster": { "post": { - "tags": ["Face Clusters"], + "tags": [ + "Face Clusters" + ], "summary": "Trigger Global Reclustering", "description": "Manually trigger global face reclustering.\nThis forces full reclustering regardless of the 24-hour rule.", "operationId": "trigger_global_reclustering_face_clusters_global_recluster_post", @@ -1169,7 +1216,9 @@ }, "/user-preferences/": { "get": { - "tags": ["User Preferences"], + "tags": [ + "User Preferences" + ], "summary": "Get User Preferences", "description": "Get user preferences from metadata.", "operationId": "get_user_preferences_user_preferences__get", @@ -1197,7 +1246,9 @@ } }, "put": { - "tags": ["User Preferences"], + "tags": [ + "User Preferences" + ], "summary": "Update User Preferences", "description": "Update user preferences in metadata.", "operationId": "update_user_preferences_user_preferences__put", @@ -1257,7 +1308,9 @@ }, "/api/memories/generate": { "post": { - "tags": ["memories"], + "tags": [ + "memories" + ], "summary": "Generate Memories", "description": "SIMPLIFIED: Generate memories from ALL images.\n- GPS images \u2192 location-based memories\n- Non-GPS images \u2192 monthly date-based memories\n\nReturns simple breakdown: {location_count, date_count, total}", "operationId": "generate_memories_api_memories_generate_post", @@ -1329,7 +1382,9 @@ }, "/api/memories/timeline": { "get": { - "tags": ["memories"], + "tags": [ + "memories" + ], "summary": "Get Timeline", "description": "Get memories from the past N days as a timeline.\n\nThis endpoint:\n1. Calculates date range (today - N days to today)\n2. Fetches images within that date range\n3. Clusters them into memories\n4. Returns timeline of memories\n\nArgs:\n days: Number of days to look back (default: 365 = 1 year)\n location_radius_km: Location clustering radius (default: 5km)\n date_tolerance_days: Date tolerance for temporal clustering (default: 3)\n\nReturns:\n TimelineResponse with memories ordered by date\n\nRaises:\n HTTPException: If database query fails", "operationId": "get_timeline_api_memories_timeline_get", @@ -1401,7 +1456,9 @@ }, "/api/memories/on-this-day": { "get": { - "tags": ["memories"], + "tags": [ + "memories" + ], "summary": "Get On This Day", "description": "Get photos taken on this date in previous years.\n\nThis endpoint:\n1. Gets current month and day\n2. Searches for images from this month-day in all previous years\n3. Groups by year\n4. Returns images sorted by year (most recent first)\n\nReturns:\n OnThisDayResponse with images from this date in previous years\n\nRaises:\n HTTPException: If database query fails", "operationId": "get_on_this_day_api_memories_on_this_day_get", @@ -1419,7 +1476,9 @@ }, "/api/memories/locations": { "get": { - "tags": ["memories"], + "tags": [ + "memories" + ], "summary": "Get Locations", "description": "Get all unique locations where photos were taken.\n\nThis endpoint:\n1. Fetches all images with GPS coordinates\n2. Clusters them by location\n3. Returns location clusters with photo counts\n4. Includes sample images for each location\n\nArgs:\n location_radius_km: Location clustering radius (default: 5km)\n max_sample_images: Maximum sample images per location (default: 5)\n\nReturns:\n LocationsResponse with list of location clusters\n\nRaises:\n HTTPException: If database query fails", "operationId": "get_locations_api_memories_locations_get", @@ -1477,7 +1536,10 @@ }, "/shutdown": { "post": { - "tags": ["Shutdown", "Shutdown"], + "tags": [ + "Shutdown", + "Shutdown" + ], "summary": "Shutdown", "description": "Gracefully shutdown the PictoPy backend.\n\nThis endpoint schedules backend server termination after response is sent.\nThe frontend is responsible for shutting down the sync service separately.\n\nReturns:\n ShutdownResponse with status and message", "operationId": "shutdown_shutdown_post", @@ -1497,7 +1559,9 @@ }, "/models/status": { "get": { - "tags": ["Models"], + "tags": [ + "Models" + ], "summary": "Get Model Status", "description": "Returns the installation status of all models in the registry.", "operationId": "get_model_status_models_status_get", @@ -1515,7 +1579,9 @@ }, "/models/hardware": { "get": { - "tags": ["Models"], + "tags": [ + "Models" + ], "summary": "Get Hardware Recommendation", "description": "Returns hardware specs and the recommended model tier.", "operationId": "get_hardware_recommendation_models_hardware_get", @@ -1533,7 +1599,9 @@ }, "/models/{model_key}": { "delete": { - "tags": ["Models"], + "tags": [ + "Models" + ], "summary": "Delete Model", "description": "Deletes a specific model from disk.", "operationId": "delete_model_models__model_key__delete", @@ -1572,7 +1640,9 @@ }, "/models/setup": { "post": { - "tags": ["Models"], + "tags": [ + "Models" + ], "summary": "Setup Models", "description": "Initializes setup by starting downloads for a specific tier + required models.\nReturns a single task_id to track overall progress.", "operationId": "setup_models_models_setup_post", @@ -1610,7 +1680,9 @@ }, "/models/download/{model_key}": { "post": { - "tags": ["Models"], + "tags": [ + "Models" + ], "summary": "Start Download Model", "description": "Starts download for a specific model by key. Returns a task_id.", "operationId": "start_download_model_models_download__model_key__post", @@ -1649,7 +1721,9 @@ }, "/models/download/{task_id}/progress": { "get": { - "tags": ["Models"], + "tags": [ + "Models" + ], "summary": "Download Progress", "description": "Streams SSE progress for a given download task_id.", "operationId": "download_progress_models_download__task_id__progress_get", @@ -1701,7 +1775,10 @@ } }, "type": "object", - "required": ["folder_id", "folder_path"], + "required": [ + "folder_id", + "folder_path" + ], "title": "AddFolderData" }, "AddFolderRequest": { @@ -1735,7 +1812,9 @@ } }, "type": "object", - "required": ["folder_path"], + "required": [ + "folder_path" + ], "title": "AddFolderRequest" }, "AddFolderResponse": { @@ -1778,7 +1857,9 @@ } }, "type": "object", - "required": ["success"], + "required": [ + "success" + ], "title": "AddFolderResponse" }, "Album": { @@ -1801,7 +1882,12 @@ } }, "type": "object", - "required": ["album_id", "album_name", "description", "is_hidden"], + "required": [ + "album_id", + "album_name", + "description", + "is_hidden" + ], "title": "Album" }, "ClusterMetadata": { @@ -1883,7 +1969,9 @@ } }, "type": "object", - "required": ["name"], + "required": [ + "name" + ], "title": "CreateAlbumRequest" }, "CreateAlbumResponse": { @@ -1898,7 +1986,10 @@ } }, "type": "object", - "required": ["success", "album_id"], + "required": [ + "success", + "album_id" + ], "title": "CreateAlbumResponse" }, "DeleteFoldersData": { @@ -1916,7 +2007,10 @@ } }, "type": "object", - "required": ["deleted_count", "folder_ids"], + "required": [ + "deleted_count", + "folder_ids" + ], "title": "DeleteFoldersData" }, "DeleteFoldersRequest": { @@ -1930,7 +2024,9 @@ } }, "type": "object", - "required": ["folder_ids"], + "required": [ + "folder_ids" + ], "title": "DeleteFoldersRequest" }, "DeleteFoldersResponse": { @@ -1973,7 +2069,9 @@ } }, "type": "object", - "required": ["success"], + "required": [ + "success" + ], "title": "DeleteFoldersResponse" }, "FaceSearchRequest": { @@ -2091,7 +2189,10 @@ } }, "type": "object", - "required": ["success", "image_ids"], + "required": [ + "success", + "image_ids" + ], "title": "GetAlbumImagesResponse" }, "GetAlbumResponse": { @@ -2105,7 +2206,10 @@ } }, "type": "object", - "required": ["success", "data"], + "required": [ + "success", + "data" + ], "title": "GetAlbumResponse" }, "GetAlbumsResponse": { @@ -2123,7 +2227,10 @@ } }, "type": "object", - "required": ["success", "albums"], + "required": [ + "success", + "albums" + ], "title": "GetAlbumsResponse" }, "GetAllFoldersData": { @@ -2141,7 +2248,10 @@ } }, "type": "object", - "required": ["folders", "total_count"], + "required": [ + "folders", + "total_count" + ], "title": "GetAllFoldersData" }, "GetAllFoldersResponse": { @@ -2184,7 +2294,9 @@ } }, "type": "object", - "required": ["success"], + "required": [ + "success" + ], "title": "GetAllFoldersResponse" }, "GetAllImagesResponse": { @@ -2206,7 +2318,11 @@ } }, "type": "object", - "required": ["success", "message", "data"], + "required": [ + "success", + "message", + "data" + ], "title": "GetAllImagesResponse" }, "GetClusterImagesData": { @@ -2239,7 +2355,11 @@ } }, "type": "object", - "required": ["cluster_id", "images", "total_images"], + "required": [ + "cluster_id", + "images", + "total_images" + ], "title": "GetClusterImagesData", "description": "Data model for cluster images response." }, @@ -2283,7 +2403,9 @@ } }, "type": "object", - "required": ["success"], + "required": [ + "success" + ], "title": "GetClusterImagesResponse", "description": "Response model for getting images in a cluster." }, @@ -2298,7 +2420,9 @@ } }, "type": "object", - "required": ["clusters"], + "required": [ + "clusters" + ], "title": "GetClustersData" }, "GetClustersResponse": { @@ -2341,7 +2465,9 @@ } }, "type": "object", - "required": ["success"], + "required": [ + "success" + ], "title": "GetClustersResponse" }, "GetUserPreferencesResponse": { @@ -2359,7 +2485,11 @@ } }, "type": "object", - "required": ["success", "message", "user_preferences"], + "required": [ + "success", + "message", + "user_preferences" + ], "title": "GetUserPreferencesResponse", "description": "Response model for getting user preferences" }, @@ -2431,7 +2561,9 @@ } }, "type": "object", - "required": ["success"], + "required": [ + "success" + ], "title": "GlobalReclusterResponse" }, "HTTPValidationError": { @@ -2514,7 +2646,9 @@ } }, "type": "object", - "required": ["image_ids"], + "required": [ + "image_ids" + ], "title": "ImageIdsRequest" }, "ImageInCluster": { @@ -2587,13 +2721,20 @@ } }, "type": "object", - "required": ["id", "path", "face_id"], + "required": [ + "id", + "path", + "face_id" + ], "title": "ImageInCluster", "description": "Represents an image that contains faces from a specific cluster." }, "InputType": { "type": "string", - "enum": ["path", "base64"], + "enum": [ + "path", + "base64" + ], "title": "InputType" }, "MetadataModel": { @@ -2691,7 +2832,10 @@ } }, "type": "object", - "required": ["cluster_id", "cluster_name"], + "required": [ + "cluster_id", + "cluster_name" + ], "title": "RenameClusterData" }, "RenameClusterRequest": { @@ -2702,7 +2846,9 @@ } }, "type": "object", - "required": ["cluster_name"], + "required": [ + "cluster_name" + ], "title": "RenameClusterRequest" }, "RenameClusterResponse": { @@ -2745,7 +2891,9 @@ } }, "type": "object", - "required": ["success"], + "required": [ + "success" + ], "title": "RenameClusterResponse" }, "SetupRequest": { @@ -2756,7 +2904,9 @@ } }, "type": "object", - "required": ["tier"], + "required": [ + "tier" + ], "title": "SetupRequest" }, "ShutdownResponse": { @@ -2771,7 +2921,10 @@ } }, "type": "object", - "required": ["status", "message"], + "required": [ + "status", + "message" + ], "title": "ShutdownResponse", "description": "Response model for shutdown endpoint." }, @@ -2787,7 +2940,10 @@ } }, "type": "object", - "required": ["success", "msg"], + "required": [ + "success", + "msg" + ], "title": "SuccessResponse" }, "SyncFolderData": { @@ -2846,7 +3002,10 @@ } }, "type": "object", - "required": ["folder_path", "folder_id"], + "required": [ + "folder_path", + "folder_id" + ], "title": "SyncFolderRequest" }, "SyncFolderResponse": { @@ -2889,7 +3048,9 @@ } }, "type": "object", - "required": ["success"], + "required": [ + "success" + ], "title": "SyncFolderResponse" }, "ToggleFavouriteRequest": { @@ -2900,7 +3061,9 @@ } }, "type": "object", - "required": ["image_id"], + "required": [ + "image_id" + ], "title": "ToggleFavouriteRequest" }, "UpdateAITaggingData": { @@ -2918,7 +3081,10 @@ } }, "type": "object", - "required": ["updated_count", "folder_ids"], + "required": [ + "updated_count", + "folder_ids" + ], "title": "UpdateAITaggingData" }, "UpdateAITaggingRequest": { @@ -2932,7 +3098,9 @@ } }, "type": "object", - "required": ["folder_ids"], + "required": [ + "folder_ids" + ], "title": "UpdateAITaggingRequest" }, "UpdateAITaggingResponse": { @@ -2975,7 +3143,9 @@ } }, "type": "object", - "required": ["success"], + "required": [ + "success" + ], "title": "UpdateAITaggingResponse" }, "UpdateAlbumRequest": { @@ -3024,7 +3194,10 @@ } }, "type": "object", - "required": ["name", "is_hidden"], + "required": [ + "name", + "is_hidden" + ], "title": "UpdateAlbumRequest" }, "UpdateUserPreferencesRequest": { @@ -3033,7 +3206,11 @@ "anyOf": [ { "type": "string", - "enum": ["nano", "small", "medium"] + "enum": [ + "nano", + "small", + "medium" + ] }, { "type": "null" @@ -3072,7 +3249,11 @@ } }, "type": "object", - "required": ["success", "message", "user_preferences"], + "required": [ + "success", + "message", + "user_preferences" + ], "title": "UpdateUserPreferencesResponse", "description": "Response model for updating user preferences" }, @@ -3080,7 +3261,11 @@ "properties": { "YOLO_model_size": { "type": "string", - "enum": ["nano", "small", "medium"], + "enum": [ + "nano", + "small", + "medium" + ], "title": "Yolo Model Size", "default": "small" }, @@ -3120,7 +3305,11 @@ } }, "type": "object", - "required": ["loc", "msg", "type"], + "required": [ + "loc", + "msg", + "type" + ], "title": "ValidationError" }, "app__schemas__face_clusters__ErrorResponse": { @@ -3238,4 +3427,4 @@ } } } -} +} \ No newline at end of file diff --git a/frontend/src/components/Navigation/Navbar/Navbar.tsx b/frontend/src/components/Navigation/Navbar/Navbar.tsx index 55a2ee6cd..16d537d47 100644 --- a/frontend/src/components/Navigation/Navbar/Navbar.tsx +++ b/frontend/src/components/Navigation/Navbar/Navbar.tsx @@ -3,7 +3,7 @@ import { ThemeSelector } from '@/components/ThemeToggle'; import { Search, Heart, ArrowRight } from 'lucide-react'; import { useDispatch, useSelector } from 'react-redux'; import { selectAvatar, selectName } from '@/features/onboardingSelectors'; -import { clearSearch } from '@/features/searchSlice'; +import { clearSearch, setTagSearchQuery, clearTagSearchQuery } from '@/features/searchSlice'; import { convertFileSrc } from '@tauri-apps/api/core'; import { FaceSearchDialog } from '@/components/Dialog/FaceSearchDialog'; import { Link, useNavigate } from 'react-router'; @@ -25,6 +25,7 @@ export function Navbar() { const searchState = useSelector((state: any) => state.search); const isSearchActive = searchState.active; const queryImage = searchState.queryImage; + const tagQuery = searchState.tagQuery; const dispatch = useDispatch(); const navigate = useNavigate(); @@ -107,7 +108,9 @@ export function Navbar() { /> {isSearchActive && (