feat: Step 10 - Node Editor & AI-Powered Linking
Implemented the node editor page with AI-powered link suggestions using vector similarity search. This feature allows users to create and edit nodes while discovering semantically related content from their existing nodes. **Node Editor Page** (`app/editor/[id]/page.tsx`): - Full-featured form with title and body fields using Mantine forms - Pre-fill support from query parameters (for AI chat redirects) - "Find Related" button to discover similar nodes via vector search - "Publish Node" button to save nodes to ATproto + SurrealDB - Real-time suggestions display with similarity scores - Mantine notifications for user feedback **Link Suggestion API** (`app/api/suggest-links/route.ts`): - Authenticates using SurrealDB JWT from cookies - Generates embeddings for draft text using Google AI (gemini-embedding-001) - Performs vector similarity search using SurrealDB's cosine similarity - Returns top 5 most similar nodes with scores - Enforces row-level security (users can only search their own nodes) - Comprehensive error handling with detailed logging **UI Enhancements** (`app/layout.tsx`): - Added @mantine/notifications package for toast notifications - Integrated Notifications component into root layout - Imported notifications styles for proper rendering **Testing** (`tests/magnitude/10-linking.mag.ts`): - Editor page rendering verification - Pre-filled form from query params test - Full publish workflow test (happy path) - Form validation test (unhappy path) **Technical Implementation**: - Vector embeddings: 768-dimension vectors from gemini-embedding-001 - Similarity metric: Cosine similarity via SurrealDB vector functions - Authentication: JWT-based with automatic row-level security - Error handling: Proper HTTP status codes and user notifications - Cookie domain: Uses 127.0.0.1 to match OAuth redirect URI **Note**: Tests may fail if GOOGLE_AI_API_KEY is invalid. Update the key in .env to enable full AI functionality. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,8 @@ import type { Metadata } from "next";
|
||||
import { Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { MantineProvider, ColorSchemeScript } from "@mantine/core";
|
||||
import { Notifications } from "@mantine/notifications";
|
||||
import "@mantine/notifications/styles.css";
|
||||
import { theme } from "./theme";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
@@ -24,6 +26,7 @@ export default function RootLayout({
|
||||
</head>
|
||||
<body className={inter.className}>
|
||||
<MantineProvider theme={theme} forceColorScheme="dark">
|
||||
<Notifications />
|
||||
{children}
|
||||
</MantineProvider>
|
||||
</body>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
"@mantine/core": "latest",
|
||||
"@mantine/form": "latest",
|
||||
"@mantine/hooks": "latest",
|
||||
"@mantine/notifications": "^8.3.6",
|
||||
"@react-three/drei": "latest",
|
||||
"@react-three/fiber": "latest",
|
||||
"@tabler/icons-react": "^3.35.0",
|
||||
|
||||
52
pnpm-lock.yaml
generated
52
pnpm-lock.yaml
generated
@@ -35,6 +35,9 @@ importers:
|
||||
'@mantine/hooks':
|
||||
specifier: latest
|
||||
version: 8.3.6(react@19.2.0)
|
||||
'@mantine/notifications':
|
||||
specifier: ^8.3.6
|
||||
version: 8.3.6(@mantine/core@8.3.6(@mantine/hooks@8.3.6(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@mantine/hooks@8.3.6(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@react-three/drei':
|
||||
specifier: latest
|
||||
version: 10.7.6(@react-three/fiber@9.4.0(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(three@0.181.0))(@types/react@19.2.2)(@types/three@0.181.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(three@0.181.0)
|
||||
@@ -880,6 +883,19 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^18.x || ^19.x
|
||||
|
||||
'@mantine/notifications@8.3.6':
|
||||
resolution: {integrity: sha512-d3A96lyrFOVXtrwASEXALfzooKnnA60T2LclMXFF/4k27Ay5Hwza4D+ylqgxf0RkPfF9J6LhBXk72OjL5RH5Kg==}
|
||||
peerDependencies:
|
||||
'@mantine/core': 8.3.6
|
||||
'@mantine/hooks': 8.3.6
|
||||
react: ^18.x || ^19.x
|
||||
react-dom: ^18.x || ^19.x
|
||||
|
||||
'@mantine/store@8.3.6':
|
||||
resolution: {integrity: sha512-fo86wF6nL8RPukY8cseAFQKk+bRVv3Ga/WmHJMYRsCbNleZOEZMXXUf/OVhmr1D3t+xzCzAlJe/sQ8MIS+c+pA==}
|
||||
peerDependencies:
|
||||
react: ^18.x || ^19.x
|
||||
|
||||
'@mediapipe/tasks-vision@0.10.17':
|
||||
resolution: {integrity: sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==}
|
||||
|
||||
@@ -1619,6 +1635,9 @@ packages:
|
||||
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
dom-helpers@5.2.1:
|
||||
resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==}
|
||||
|
||||
dom-serializer@2.0.0:
|
||||
resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
|
||||
|
||||
@@ -2795,6 +2814,12 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
react-transition-group@4.4.5:
|
||||
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
||||
peerDependencies:
|
||||
react: '>=16.6.0'
|
||||
react-dom: '>=16.6.0'
|
||||
|
||||
react-use-measure@2.1.7:
|
||||
resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==}
|
||||
peerDependencies:
|
||||
@@ -4149,6 +4174,19 @@ snapshots:
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
|
||||
'@mantine/notifications@8.3.6(@mantine/core@8.3.6(@mantine/hooks@8.3.6(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@mantine/hooks@8.3.6(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
|
||||
dependencies:
|
||||
'@mantine/core': 8.3.6(@mantine/hooks@8.3.6(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
'@mantine/hooks': 8.3.6(react@19.2.0)
|
||||
'@mantine/store': 8.3.6(react@19.2.0)
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
|
||||
|
||||
'@mantine/store@8.3.6(react@19.2.0)':
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
|
||||
'@mediapipe/tasks-vision@0.10.17': {}
|
||||
|
||||
'@monogrid/gainmap-js@3.1.0(three@0.181.0)':
|
||||
@@ -4894,6 +4932,11 @@ snapshots:
|
||||
dependencies:
|
||||
esutils: 2.0.3
|
||||
|
||||
dom-helpers@5.2.1:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.4
|
||||
csstype: 3.1.3
|
||||
|
||||
dom-serializer@2.0.0:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
@@ -6270,6 +6313,15 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
|
||||
react-transition-group@4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.4
|
||||
dom-helpers: 5.2.1
|
||||
loose-envify: 1.4.0
|
||||
prop-types: 15.8.1
|
||||
react: 19.2.0
|
||||
react-dom: 19.2.0(react@19.2.0)
|
||||
|
||||
react-use-measure@2.1.7(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
|
||||
Reference in New Issue
Block a user