diff --git a/hindsight-api/tests/test_observations.py b/hindsight-api/tests/test_observations.py index f66f16cf..0efb60ea 100644 --- a/hindsight-api/tests/test_observations.py +++ b/hindsight-api/tests/test_observations.py @@ -173,6 +173,107 @@ async def test_regenerate_entity_observations(memory, request_context): await conn.execute("DELETE FROM entities WHERE bank_id = $1", bank_id) +@pytest.mark.asyncio +async def test_manual_regenerate_with_few_facts(memory, request_context): + """ + Test that manual regeneration works even with fewer than 5 facts. + + This is important because: + - Automatic generation during retain requires MIN_FACTS_THRESHOLD (5) + - But manual regeneration via API should work with any number of facts + - The UI triggers manual regeneration, so it should work regardless of fact count + """ + bank_id = f"test_manual_regen_{datetime.now(timezone.utc).timestamp()}" + + try: + # Store only 2 facts - below the automatic threshold + await memory.retain_async( + bank_id=bank_id, + content="Alice works at Google as a senior software engineer.", + context="work info", + event_date=datetime(2024, 1, 15, tzinfo=timezone.utc), + request_context=request_context, + ) + await memory.retain_async( + bank_id=bank_id, + content="Alice loves hiking and outdoor photography.", + context="hobbies", + event_date=datetime(2024, 1, 16, tzinfo=timezone.utc), + request_context=request_context, + ) + + # Find the Alice entity + pool = await memory._get_pool() + async with pool.acquire() as conn: + entity_row = await conn.fetchrow( + """ + SELECT id, canonical_name + FROM entities + WHERE bank_id = $1 AND LOWER(canonical_name) LIKE '%alice%' + LIMIT 1 + """, + bank_id + ) + + assert entity_row is not None, "Alice entity should have been extracted" + + entity_id = str(entity_row['id']) + entity_name = entity_row['canonical_name'] + + # Check fact count - should be < 5 + async with pool.acquire() as conn: + fact_count = await conn.fetchval( + "SELECT COUNT(*) FROM unit_entities WHERE entity_id = $1", + entity_row['id'] + ) + + print(f"\n=== Manual Regeneration Test ===") + print(f"Entity: {entity_name} (id: {entity_id})") + print(f"Linked facts: {fact_count}") + + # Verify we're testing with fewer than the automatic threshold + assert fact_count < 5, f"Test requires < 5 facts, but entity has {fact_count}" + + # Before regeneration - should have no observations (auto threshold not met) + obs_before = await memory.get_entity_observations(bank_id, entity_id, limit=10, request_context=request_context) + print(f"Observations before manual regenerate: {len(obs_before)}") + + # Manually regenerate observations - this should work regardless of fact count + created_ids = await memory.regenerate_entity_observations( + bank_id=bank_id, + entity_id=entity_id, + entity_name=entity_name, + request_context=request_context, + ) + + print(f"Observations created by manual regenerate: {len(created_ids)}") + + # Get observations after regeneration + observations = await memory.get_entity_observations(bank_id, entity_id, limit=10, request_context=request_context) + print(f"Observations after manual regenerate: {len(observations)}") + for obs in observations: + print(f" - {obs.text}") + + # Manual regeneration should create observations even with < 5 facts + assert len(observations) > 0, \ + f"Manual regeneration should create observations even with only {fact_count} facts. " \ + f"The LLM should synthesize at least 1 observation from the available facts." + + # Verify observations contain relevant content + obs_texts = " ".join([o.text.lower() for o in observations]) + assert any(keyword in obs_texts for keyword in ["google", "engineer", "hiking", "photography", "alice"]), \ + "Observations should contain relevant information about Alice" + + print(f"✓ Manual regeneration works with {fact_count} facts (below automatic threshold of 5)") + + finally: + # Cleanup + pool = await memory._get_pool() + async with pool.acquire() as conn: + await conn.execute("DELETE FROM memory_units WHERE bank_id = $1", bank_id) + await conn.execute("DELETE FROM entities WHERE bank_id = $1", bank_id) + + @pytest.mark.asyncio async def test_search_with_include_entities(memory, request_context): """ diff --git a/hindsight-clients/python/tests/test_main_operations.py b/hindsight-clients/python/tests/test_main_operations.py index a82373e9..b1457b12 100644 --- a/hindsight-clients/python/tests/test_main_operations.py +++ b/hindsight-clients/python/tests/test_main_operations.py @@ -344,3 +344,160 @@ async def do_delete(): assert response.success is True assert response.document_id == doc_id assert response.memory_units_deleted >= 0 + + def test_get_document(self, client, bank_id): + """Test getting a document.""" + import asyncio + from hindsight_client_api import ApiClient, Configuration + from hindsight_client_api.api import DocumentsApi + + # First create a document + doc_id = f"test-doc-{uuid.uuid4().hex[:8]}" + client.retain( + bank_id=bank_id, + content="Test document content for retrieval", + document_id=doc_id, + ) + + async def do_get(): + config = Configuration(host=HINDSIGHT_API_URL) + api_client = ApiClient(config) + api = DocumentsApi(api_client) + return await api.get_document(bank_id=bank_id, document_id=doc_id) + + document = asyncio.get_event_loop().run_until_complete(do_get()) + + assert document is not None + assert document.id == doc_id + assert "Test document content" in document.original_text + + +class TestEntities: + """Tests for entity endpoints.""" + + @pytest.fixture(autouse=True) + def setup_memories(self, client, bank_id): + """Setup: Store memories that will generate entities.""" + client.retain_batch( + bank_id=bank_id, + items=[ + {"content": "Alice works at Google as a software engineer"}, + {"content": "Bob is friends with Alice and works at Microsoft"}, + ], + retain_async=False, + ) + + def test_list_entities(self, client, bank_id): + """Test listing entities.""" + import asyncio + from hindsight_client_api import ApiClient, Configuration + from hindsight_client_api.api import EntitiesApi + + async def do_list(): + config = Configuration(host=HINDSIGHT_API_URL) + api_client = ApiClient(config) + api = EntitiesApi(api_client) + return await api.list_entities(bank_id=bank_id) + + response = asyncio.get_event_loop().run_until_complete(do_list()) + + assert response is not None + assert response.items is not None + assert isinstance(response.items, list) + + def test_get_entity(self, client, bank_id): + """Test getting a specific entity.""" + import asyncio + from hindsight_client_api import ApiClient, Configuration + from hindsight_client_api.api import EntitiesApi + + async def do_test(): + config = Configuration(host=HINDSIGHT_API_URL) + api_client = ApiClient(config) + api = EntitiesApi(api_client) + + # First list entities to get an ID + list_response = await api.list_entities(bank_id=bank_id) + + if list_response.items and len(list_response.items) > 0: + entity_id = list_response.items[0].id + + # Get the entity + entity = await api.get_entity(bank_id=bank_id, entity_id=entity_id) + return entity_id, entity + return None, None + + entity_id, entity = asyncio.get_event_loop().run_until_complete(do_test()) + + if entity_id: + assert entity is not None + assert entity.id == entity_id + + def test_regenerate_entity_observations(self, client, bank_id): + """Test regenerating observations for an entity.""" + import asyncio + from hindsight_client_api import ApiClient, Configuration + from hindsight_client_api.api import EntitiesApi + + async def do_test(): + config = Configuration(host=HINDSIGHT_API_URL) + api_client = ApiClient(config) + api = EntitiesApi(api_client) + + # First list entities to get an ID + list_response = await api.list_entities(bank_id=bank_id) + + if list_response.items and len(list_response.items) > 0: + entity_id = list_response.items[0].id + + # Regenerate observations + result = await api.regenerate_entity_observations( + bank_id=bank_id, + entity_id=entity_id, + ) + return entity_id, result + return None, None + + entity_id, result = asyncio.get_event_loop().run_until_complete(do_test()) + + if entity_id: + assert result is not None + assert result.id == entity_id + + +class TestDeleteBank: + """Tests for bank deletion.""" + + def test_delete_bank(self, client): + """Test deleting a bank.""" + import asyncio + from hindsight_client_api import ApiClient, Configuration + from hindsight_client_api.api import BanksApi + + # Create a unique bank for this test + bank_id = f"test_bank_delete_{uuid.uuid4().hex[:12]}" + + # Create bank with some data + client.create_bank( + bank_id=bank_id, + background="This bank will be deleted", + ) + client.retain( + bank_id=bank_id, + content="Some memory to store", + ) + + async def do_delete(): + config = Configuration(host=HINDSIGHT_API_URL) + api_client = ApiClient(config) + api = BanksApi(api_client) + return await api.delete_bank(bank_id=bank_id) + + response = asyncio.get_event_loop().run_until_complete(do_delete()) + + assert response is not None + assert response.success is True + + # Verify bank data is deleted - memories should be gone + memories = client.list_memories(bank_id=bank_id) + assert memories.total == 0 diff --git a/hindsight-control-plane/package.json b/hindsight-control-plane/package.json index 30e90ae9..26b78867 100644 --- a/hindsight-control-plane/package.json +++ b/hindsight-control-plane/package.json @@ -18,10 +18,16 @@ "lint": "next lint", "prepublishOnly": "npm run build" }, - "keywords": ["hindsight", "memory", "semantic", "ai"], + "keywords": [ + "hindsight", + "memory", + "semantic", + "ai" + ], "author": "Hindsight Team", "license": "ISC", "dependencies": { + "@radix-ui/react-alert-dialog": "^1.1.15", "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-label": "^2.1.8", @@ -58,9 +64,9 @@ "typescript": "^5.9.3" }, "devDependencies": { - "@vectorize-io/hindsight-client": "file:../hindsight-clients/typescript", "@eslint/eslintrc": "^3.3.3", "@eslint/js": "^9.39.2", + "@vectorize-io/hindsight-client": "file:../hindsight-clients/typescript", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", "prettier": "^3.7.4", diff --git a/hindsight-control-plane/src/app/api/documents/[documentId]/route.ts b/hindsight-control-plane/src/app/api/documents/[documentId]/route.ts index 33283b81..6f3e9fcd 100644 --- a/hindsight-control-plane/src/app/api/documents/[documentId]/route.ts +++ b/hindsight-control-plane/src/app/api/documents/[documentId]/route.ts @@ -25,3 +25,28 @@ export async function GET( return NextResponse.json({ error: "Failed to fetch document" }, { status: 500 }); } } + +export async function DELETE( + request: NextRequest, + { params }: { params: Promise<{ documentId: string }> } +) { + try { + const { documentId } = await params; + const searchParams = request.nextUrl.searchParams; + const bankId = searchParams.get("bank_id"); + + if (!bankId) { + return NextResponse.json({ error: "bank_id is required" }, { status: 400 }); + } + + const response = await sdk.deleteDocument({ + client: lowLevelClient, + path: { bank_id: bankId, document_id: documentId }, + }); + + return NextResponse.json(response.data, { status: 200 }); + } catch (error) { + console.error("Error deleting document:", error); + return NextResponse.json({ error: "Failed to delete document" }, { status: 500 }); + } +} diff --git a/hindsight-control-plane/src/components/documents-view.tsx b/hindsight-control-plane/src/components/documents-view.tsx index 7006f27b..57c019d9 100644 --- a/hindsight-control-plane/src/components/documents-view.tsx +++ b/hindsight-control-plane/src/components/documents-view.tsx @@ -13,7 +13,17 @@ import { TableHeader, TableRow, } from "@/components/ui/table"; -import { X } from "lucide-react"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { X, Trash2 } from "lucide-react"; export function DocumentsView() { const { currentBank } = useBank(); @@ -25,6 +35,16 @@ export function DocumentsView() { // Document view panel state const [selectedDocument, setSelectedDocument] = useState(null); const [loadingDocument, setLoadingDocument] = useState(false); + const [deletingDocumentId, setDeletingDocumentId] = useState(null); + + // Delete confirmation dialog state + const [documentToDelete, setDocumentToDelete] = useState<{ + id: string; + memoryCount?: number; + } | null>(null); + const [deleteResult, setDeleteResult] = useState<{ success: boolean; message: string } | null>( + null + ); const loadDocuments = async () => { if (!currentBank) return; @@ -64,6 +84,42 @@ export function DocumentsView() { } }; + const confirmDeleteDocument = async () => { + if (!currentBank || !documentToDelete) return; + + const documentId = documentToDelete.id; + setDeletingDocumentId(documentId); + setDocumentToDelete(null); + + try { + const result = await client.deleteDocument(documentId, currentBank); + setDeleteResult({ + success: true, + message: `Deleted document and ${result.memory_units_deleted} memory units.`, + }); + + // Close panel if this document was selected + if (selectedDocument?.id === documentId) { + setSelectedDocument(null); + } + + // Reload documents list + loadDocuments(); + } catch (error) { + console.error("Error deleting document:", error); + setDeleteResult({ + success: false, + message: "Error deleting document: " + (error as Error).message, + }); + } finally { + setDeletingDocumentId(null); + } + }; + + const requestDeleteDocument = (documentId: string, memoryCount?: number) => { + setDocumentToDelete({ id: documentId, memoryCount }); + }; + // Auto-load documents when component mounts useEffect(() => { if (currentBank) { @@ -116,7 +172,6 @@ export function DocumentsView() { Context Text Length Memory Units - Actions @@ -142,24 +197,11 @@ export function DocumentsView() { {doc.memory_unit_count} - - - )) ) : ( - + Click "Load Documents" to view data @@ -276,6 +318,29 @@ export function DocumentsView() { )} + {/* Delete Button */} +
+ +
+ {/* Original Text */} {selectedDocument.original_text && (
@@ -296,6 +361,58 @@ export function DocumentsView() { )} )} + + {/* Delete Confirmation Dialog */} + !open && setDocumentToDelete(null)} + > + + + Delete Document + + Are you sure you want to delete document{" "} + "{documentToDelete?.id}"? +
+
+ This will also delete{" "} + {documentToDelete?.memoryCount !== undefined ? ( + {documentToDelete.memoryCount} memory units + ) : ( + "all memory units" + )}{" "} + extracted from this document. +
+
+ This action cannot be undone. +
+
+ + Cancel + + Delete + + +
+
+ + {/* Delete Result Dialog */} + !open && setDeleteResult(null)}> + + + + {deleteResult?.success ? "Document Deleted" : "Error"} + + {deleteResult?.message} + + + setDeleteResult(null)}>OK + + +
); } diff --git a/hindsight-control-plane/src/components/graph-2d.tsx b/hindsight-control-plane/src/components/graph-2d.tsx index 9fb59ca7..98f7ecea 100644 --- a/hindsight-control-plane/src/components/graph-2d.tsx +++ b/hindsight-control-plane/src/components/graph-2d.tsx @@ -512,7 +512,7 @@ export function Graph2D({ ref={containerRef} className="w-full h-full" style={{ - background: isDarkMode + backgroundImage: isDarkMode ? "radial-gradient(circle at 1px 1px, rgba(255,255,255,0.08) 1px, transparent 0)" : "radial-gradient(circle at 1px 1px, rgba(0,0,0,0.06) 1px, transparent 0)", backgroundSize: "20px 20px", diff --git a/hindsight-control-plane/src/components/ui/alert-dialog.tsx b/hindsight-control-plane/src/components/ui/alert-dialog.tsx new file mode 100644 index 00000000..77d5f358 --- /dev/null +++ b/hindsight-control-plane/src/components/ui/alert-dialog.tsx @@ -0,0 +1,117 @@ +"use client"; + +import * as React from "react"; +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; + +import { cn } from "@/lib/utils"; +import { buttonVariants } from "@/components/ui/button"; + +const AlertDialog = AlertDialogPrimitive.Root; + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger; + +const AlertDialogPortal = AlertDialogPrimitive.Portal; + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)); +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; + +const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +AlertDialogHeader.displayName = "AlertDialogHeader"; + +const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +AlertDialogFooter.displayName = "AlertDialogFooter"; + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName; + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +}; diff --git a/hindsight-control-plane/src/components/ui/button.tsx b/hindsight-control-plane/src/components/ui/button.tsx index 670899e6..f0eb9607 100644 --- a/hindsight-control-plane/src/components/ui/button.tsx +++ b/hindsight-control-plane/src/components/ui/button.tsx @@ -9,7 +9,7 @@ const buttonVariants = cva( { variants: { variant: { - default: "bg-primary-gradient text-white hover:opacity-90", + default: "bg-primary text-primary-foreground hover:bg-primary/90", destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90", outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", diff --git a/hindsight-control-plane/src/lib/api.ts b/hindsight-control-plane/src/lib/api.ts index 16a48864..27d6355a 100644 --- a/hindsight-control-plane/src/lib/api.ts +++ b/hindsight-control-plane/src/lib/api.ts @@ -165,6 +165,20 @@ export class ControlPlaneClient { return this.fetchApi(`/api/documents/${documentId}?bank_id=${bankId}`); } + /** + * Delete document and all its associated memory units + */ + async deleteDocument(documentId: string, bankId: string) { + return this.fetchApi<{ + success: boolean; + message: string; + document_id: string; + memory_units_deleted: number; + }>(`/api/documents/${documentId}?bank_id=${bankId}`, { + method: "DELETE", + }); + } + /** * Get chunk */ diff --git a/hindsight-docs/docusaurus.config.ts b/hindsight-docs/docusaurus.config.ts index 5fdac2f7..97bafeec 100644 --- a/hindsight-docs/docusaurus.config.ts +++ b/hindsight-docs/docusaurus.config.ts @@ -139,7 +139,7 @@ const config: Config = { isCloseable: false, }, }), - image: 'img/hindsight-social-card.jpg', + image: 'img/logo.png', colorMode: { defaultMode: 'dark', respectPrefersColorScheme: true, diff --git a/hindsight-docs/static/img/docusaurus-social-card.jpg b/hindsight-docs/static/img/docusaurus-social-card.jpg deleted file mode 100644 index ffcb4482..00000000 Binary files a/hindsight-docs/static/img/docusaurus-social-card.jpg and /dev/null differ diff --git a/hindsight-docs/static/img/docusaurus.png b/hindsight-docs/static/img/docusaurus.png deleted file mode 100644 index f458149e..00000000 Binary files a/hindsight-docs/static/img/docusaurus.png and /dev/null differ diff --git a/hindsight-docs/static/img/favicon.ico b/hindsight-docs/static/img/favicon.ico deleted file mode 100644 index c01d54bc..00000000 Binary files a/hindsight-docs/static/img/favicon.ico and /dev/null differ diff --git a/package-lock.json b/package-lock.json index 1d3ded2e..c1e93547 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ }, "hindsight-clients/typescript": { "name": "@vectorize-io/hindsight-client", - "version": "0.1.11", + "version": "0.1.14", "license": "MIT", "devDependencies": { "@hey-api/openapi-ts": "^0.88.0", @@ -26,9 +26,10 @@ }, "hindsight-control-plane": { "name": "@vectorize-io/hindsight-control-plane", - "version": "0.1.11", + "version": "0.1.14", "license": "ISC", "dependencies": { + "@radix-ui/react-alert-dialog": "^1.1.15", "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-label": "^2.1.8", @@ -5131,6 +5132,111 @@ "node": ">= 10" } }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.10.tgz", + "integrity": "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.10.tgz", + "integrity": "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.10.tgz", + "integrity": "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.10.tgz", + "integrity": "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.10.tgz", + "integrity": "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.10.tgz", + "integrity": "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.0.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.10.tgz", + "integrity": "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@node-rs/jieba": { "version": "1.10.4", "license": "MIT", @@ -5326,6 +5432,52 @@ "version": "1.1.3", "license": "MIT" }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", + "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dialog": "1.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.1.7", "license": "MIT", @@ -5934,6 +6086,8 @@ }, "node_modules/@radix-ui/react-slot": { "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" @@ -24202,111 +24356,6 @@ "type": "github", "url": "https://github.com/sponsors/wooorm" } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.0.10.tgz", - "integrity": "sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.0.10.tgz", - "integrity": "sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.0.10.tgz", - "integrity": "sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.0.10.tgz", - "integrity": "sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.0.10.tgz", - "integrity": "sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.0.10.tgz", - "integrity": "sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.0.10", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.0.10.tgz", - "integrity": "sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } } }