diff options
| author | Dhravya Shah <[email protected]> | 2025-09-18 20:34:18 -0700 |
|---|---|---|
| committer | Dhravya Shah <[email protected]> | 2025-09-18 21:03:49 -0700 |
| commit | 1fcb56908920da386900abb4ce2383374a625c72 (patch) | |
| tree | 0f9d7f695d4c9b1b85be3950fc869e0061dff3ed /packages/lib | |
| parent | refetching logic change (diff) | |
| download | supermemory-09-18-formatting.tar.xz supermemory-09-18-formatting.zip | |
formatting09-18-formatting
Diffstat (limited to 'packages/lib')
| -rw-r--r-- | packages/lib/api.ts | 18 | ||||
| -rw-r--r-- | packages/lib/auth-context.tsx | 77 | ||||
| -rw-r--r-- | packages/lib/auth.middleware.ts | 6 | ||||
| -rw-r--r-- | packages/lib/auth.ts | 14 | ||||
| -rw-r--r-- | packages/lib/constants.ts | 10 | ||||
| -rw-r--r-- | packages/lib/error-tracking.tsx | 103 | ||||
| -rw-r--r-- | packages/lib/generate-id.ts | 4 | ||||
| -rw-r--r-- | packages/lib/glass-effect-manager.ts | 196 | ||||
| -rw-r--r-- | packages/lib/posthog.tsx | 46 | ||||
| -rw-r--r-- | packages/lib/queries.ts | 94 | ||||
| -rw-r--r-- | packages/lib/query-client.tsx | 14 | ||||
| -rw-r--r-- | packages/lib/similarity.ts | 50 | ||||
| -rw-r--r-- | packages/lib/utils.ts | 8 |
13 files changed, 330 insertions, 310 deletions
diff --git a/packages/lib/api.ts b/packages/lib/api.ts index ad343050..82c0b8ef 100644 --- a/packages/lib/api.ts +++ b/packages/lib/api.ts @@ -10,6 +10,7 @@ import { DeleteProjectSchema, DocumentsWithMemoriesQuerySchema, DocumentsWithMemoriesResponseSchema, + GetMemoryResponseSchema, ListMemoriesResponseSchema, ListProjectsResponseSchema, MemoryAddSchema, @@ -126,7 +127,24 @@ export const apiSchema = createSchema({ input: SettingsRequestSchema, output: SettingsResponseSchema, }, + + "@get/documents/:id": { + output: GetMemoryResponseSchema, + params: z.object({ id: z.string() }), + }, + // Memory operations + "@get/documents": { + output: ListMemoriesResponseSchema, + query: z + .object({ + limit: z.number().optional(), + page: z.number().optional(), + status: z.string().optional(), + containerTags: z.array(z.string()).optional(), + }) + .optional(), + }, "@post/documents": { input: MemoryAddSchema, output: MemoryResponseSchema, diff --git a/packages/lib/auth-context.tsx b/packages/lib/auth-context.tsx index 66ff84bc..a88c73c8 100644 --- a/packages/lib/auth-context.tsx +++ b/packages/lib/auth-context.tsx @@ -1,4 +1,4 @@ -"use client" +"use client"; import { createContext, @@ -6,75 +6,74 @@ import { useContext, useEffect, useState, -} from "react" -import { authClient, useSession } from "./auth" +} from "react"; +import { authClient, useSession } from "./auth"; -type Organization = typeof authClient.$Infer.ActiveOrganization -type SessionData = NonNullable<ReturnType<typeof useSession>["data"]> +type Organization = typeof authClient.$Infer.ActiveOrganization; +type SessionData = NonNullable<ReturnType<typeof useSession>["data"]>; interface AuthContextType { - session: SessionData["session"] | null - user: SessionData["user"] | null - org: Organization | null - setActiveOrg: (orgSlug: string) => Promise<void> + session: SessionData["session"] | null; + user: SessionData["user"] | null; + org: Organization | null; + setActiveOrg: (orgSlug: string) => Promise<void>; } -const AuthContext = createContext<AuthContextType | undefined>(undefined) +const AuthContext = createContext<AuthContextType | undefined>(undefined); export function AuthProvider({ children }: { children: ReactNode }) { - const { data: session } = useSession() - const [org, setOrg] = useState<Organization | null>(null) + const { data: session } = useSession(); + const [org, setOrg] = useState<Organization | null>(null); useEffect(() => { if (session?.session.activeOrganizationId) { authClient.organization.getFullOrganization().then((org) => { - setOrg(org) - }) + setOrg(org); + }); } - }, [session?.session.activeOrganizationId]) + }, [session?.session.activeOrganizationId]); // When a session exists and there is a pending login method recorded, // promote it to the last-used method (successful login) and clear pending. useEffect(() => { - if (typeof window === "undefined") return - if (!session?.session) return + if (typeof window === "undefined") return; + if (!session?.session) return; try { const pendingMethod = localStorage.getItem( "supermemory-pending-login-method", - ) + ); const pendingTsRaw = localStorage.getItem( "supermemory-pending-login-timestamp", - ) + ); if (pendingMethod) { - const now = Date.now() - const ts = pendingTsRaw ? Number.parseInt(pendingTsRaw, 10) : NaN - const isFresh = Number.isFinite(ts) && now - ts < 10 * 60 * 1000 // 10 minutes TTL + const now = Date.now(); + const ts = pendingTsRaw + ? Number.parseInt(pendingTsRaw, 10) + : Number.NaN; + const isFresh = Number.isFinite(ts) && now - ts < 10 * 60 * 1000; // 10 minutes TTL if (isFresh) { - localStorage.setItem( - "supermemory-last-login-method", - pendingMethod, - ) + localStorage.setItem("supermemory-last-login-method", pendingMethod); } } - } catch { } + } catch {} // Always clear pending markers once a session is present try { - localStorage.removeItem("supermemory-pending-login-method") - localStorage.removeItem("supermemory-pending-login-timestamp") - } catch { } - }, [session?.session]) + localStorage.removeItem("supermemory-pending-login-method"); + localStorage.removeItem("supermemory-pending-login-timestamp"); + } catch {} + }, [session?.session]); const setActiveOrg = async (slug: string) => { - if (!slug) return + if (!slug) return; const activeOrg = await authClient.organization.setActive({ organizationSlug: slug, - }) - setOrg(activeOrg) - } + }); + setOrg(activeOrg); + }; return ( <AuthContext.Provider @@ -87,13 +86,13 @@ export function AuthProvider({ children }: { children: ReactNode }) { > {children} </AuthContext.Provider> - ) + ); } export function useAuth() { - const context = useContext(AuthContext) + const context = useContext(AuthContext); if (context === undefined) { - throw new Error("useAuth must be used within an AuthProvider") + throw new Error("useAuth must be used within an AuthProvider"); } - return context + return context; } diff --git a/packages/lib/auth.middleware.ts b/packages/lib/auth.middleware.ts index 3b1f1f40..884f14b8 100644 --- a/packages/lib/auth.middleware.ts +++ b/packages/lib/auth.middleware.ts @@ -1,4 +1,4 @@ -import { createAuthClient } from "better-auth/client" +import { createAuthClient } from "better-auth/client"; import { adminClient, anonymousClient, @@ -7,7 +7,7 @@ import { magicLinkClient, organizationClient, usernameClient, -} from "better-auth/client/plugins" +} from "better-auth/client/plugins"; export const middlewareAuthClient = createAuthClient({ baseURL: process.env.NEXT_PUBLIC_BACKEND_URL ?? "https://api.supermemory.ai", @@ -23,4 +23,4 @@ export const middlewareAuthClient = createAuthClient({ organizationClient(), anonymousClient(), ], -}) +}); diff --git a/packages/lib/auth.ts b/packages/lib/auth.ts index 4369bef1..02709e2c 100644 --- a/packages/lib/auth.ts +++ b/packages/lib/auth.ts @@ -6,8 +6,8 @@ import { magicLinkClient, organizationClient, usernameClient, -} from "better-auth/client/plugins" -import { createAuthClient } from "better-auth/react" +} from "better-auth/client/plugins"; +import { createAuthClient } from "better-auth/react"; export const authClient = createAuthClient({ baseURL: process.env.NEXT_PUBLIC_BACKEND_URL ?? "https://api.supermemory.ai", @@ -24,9 +24,9 @@ export const authClient = createAuthClient({ organizationClient(), anonymousClient(), ], -}) +}); -export const signIn = authClient.signIn -export const signOut = authClient.signOut -export const useSession = authClient.useSession -export const getSession = authClient.getSession +export const signIn = authClient.signIn; +export const signOut = authClient.signOut; +export const useSession = authClient.useSession; +export const getSession = authClient.getSession; diff --git a/packages/lib/constants.ts b/packages/lib/constants.ts index fde5bce1..74eb27ec 100644 --- a/packages/lib/constants.ts +++ b/packages/lib/constants.ts @@ -1,13 +1,13 @@ -const BIG_DIMENSIONS_NEW = 1536 -const DEFAULT_PROJECT_ID = "sm_project_default" +const BIG_DIMENSIONS_NEW = 1536; +const DEFAULT_PROJECT_ID = "sm_project_default"; const SEARCH_MEMORY_SHORTCUT_URL = - "https://www.icloud.com/shortcuts/f2b5c544372844a38ab4c6900e2a88de" + "https://www.icloud.com/shortcuts/f2b5c544372844a38ab4c6900e2a88de"; const ADD_MEMORY_SHORTCUT_URL = - "https://www.icloud.com/shortcuts/ec33b029b2c7481d89eda7640dbb7688" + "https://www.icloud.com/shortcuts/ec33b029b2c7481d89eda7640dbb7688"; export { BIG_DIMENSIONS_NEW, DEFAULT_PROJECT_ID, SEARCH_MEMORY_SHORTCUT_URL, ADD_MEMORY_SHORTCUT_URL, -} +}; diff --git a/packages/lib/error-tracking.tsx b/packages/lib/error-tracking.tsx index bf320271..cc3a9b3c 100644 --- a/packages/lib/error-tracking.tsx +++ b/packages/lib/error-tracking.tsx @@ -1,14 +1,14 @@ -"use client" +"use client"; -import { usePathname } from "next/navigation" -import { useEffect } from "react" -import { useSession } from "./auth" -import { usePostHog } from "./posthog" +import { usePathname } from "next/navigation"; +import { useEffect } from "react"; +import { useSession } from "./auth"; +import { usePostHog } from "./posthog"; export function useErrorTracking() { - const posthog = usePostHog() - const { data: session } = useSession() - const pathname = usePathname() + const posthog = usePostHog(); + const { data: session } = useSession(); + const pathname = usePathname(); const trackError = ( error: Error | unknown, @@ -23,10 +23,10 @@ export function useErrorTracking() { user_email: session?.user?.email, timestamp: new Date().toISOString(), ...context, - } + }; - posthog.capture("error_occurred", errorDetails) - } + posthog.capture("error_occurred", errorDetails); + }; const trackApiError = ( error: Error | unknown, @@ -37,8 +37,8 @@ export function useErrorTracking() { error_type: "api_error", api_endpoint: endpoint, api_method: method, - }) - } + }); + }; const trackComponentError = ( error: Error | unknown, @@ -47,8 +47,8 @@ export function useErrorTracking() { trackError(error, { error_type: "component_error", component_name: componentName, - }) - } + }); + }; const trackValidationError = ( error: Error | unknown, @@ -59,24 +59,24 @@ export function useErrorTracking() { error_type: "validation_error", form_name: formName, field_name: field, - }) - } + }); + }; return { trackError, trackApiError, trackComponentError, trackValidationError, - } + }; } // Global error boundary component export function ErrorTrackingProvider({ children, }: { - children: React.ReactNode + children: React.ReactNode; }) { - const { trackError } = useErrorTracking() + const { trackError } = useErrorTracking(); useEffect(() => { // Global error handler for unhandled errors @@ -87,34 +87,37 @@ export function ErrorTrackingProvider({ filename: event.filename, lineno: event.lineno, colno: event.colno, - }) - } + }); + }; // Global handler for unhandled promise rejections const handleUnhandledRejection = (event: PromiseRejectionEvent) => { trackError(event.reason, { error_type: "unhandled_promise_rejection", source: "promise_rejection", - }) - } + }); + }; - window.addEventListener("error", handleError) - window.addEventListener("unhandledrejection", handleUnhandledRejection) + window.addEventListener("error", handleError); + window.addEventListener("unhandledrejection", handleUnhandledRejection); return () => { - window.removeEventListener("error", handleError) - window.removeEventListener("unhandledrejection", handleUnhandledRejection) - } - }, [trackError]) - - return <>{children}</> + window.removeEventListener("error", handleError); + window.removeEventListener( + "unhandledrejection", + handleUnhandledRejection, + ); + }; + }, [trackError]); + + return <>{children}</>; } // Hook for tracking user interactions export function useInteractionTracking() { - const posthog = usePostHog() - const { data: session } = useSession() - const pathname = usePathname() + const posthog = usePostHog(); + const { data: session } = useSession(); + const pathname = usePathname(); const trackInteraction = (action: string, details?: Record<string, any>) => { posthog.capture("user_interaction", { @@ -123,8 +126,8 @@ export function useInteractionTracking() { user_id: session?.user?.id, timestamp: new Date().toISOString(), ...details, - }) - } + }); + }; const trackFormSubmission = ( formName: string, @@ -138,15 +141,15 @@ export function useInteractionTracking() { user_id: session?.user?.id, timestamp: new Date().toISOString(), ...details, - }) - } + }); + }; const trackButtonClick = (buttonName: string, context?: string) => { trackInteraction("button_click", { button_name: buttonName, context, - }) - } + }); + }; const trackLinkClick = ( url: string, @@ -157,27 +160,27 @@ export function useInteractionTracking() { url, link_text: linkText, external, - }) - } + }); + }; const trackModalOpen = (modalName: string) => { trackInteraction("modal_open", { modal_name: modalName, - }) - } + }); + }; const trackModalClose = (modalName: string) => { trackInteraction("modal_close", { modal_name: modalName, - }) - } + }); + }; const trackTabChange = (fromTab: string, toTab: string) => { trackInteraction("tab_change", { from_tab: fromTab, to_tab: toTab, - }) - } + }); + }; return { trackInteraction, @@ -187,5 +190,5 @@ export function useInteractionTracking() { trackModalOpen, trackModalClose, trackTabChange, - } + }; } diff --git a/packages/lib/generate-id.ts b/packages/lib/generate-id.ts index bbe201fd..6007027e 100644 --- a/packages/lib/generate-id.ts +++ b/packages/lib/generate-id.ts @@ -1,6 +1,6 @@ -import { customAlphabet } from "nanoid" +import { customAlphabet } from "nanoid"; export const generateId = () => customAlphabet("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")( 22, - ) + ); diff --git a/packages/lib/glass-effect-manager.ts b/packages/lib/glass-effect-manager.ts index 291a30aa..03a79734 100644 --- a/packages/lib/glass-effect-manager.ts +++ b/packages/lib/glass-effect-manager.ts @@ -1,56 +1,56 @@ // Singleton WebGL context manager for glass effects class GlassEffectManager { - private static instance: GlassEffectManager | null = null - private canvas: HTMLCanvasElement | null = null - private gl: WebGLRenderingContext | null = null - private program: WebGLProgram | null = null - private uniforms: Record<string, WebGLUniformLocation | null> = {} - private effects: Map<string, EffectInstance> = new Map() - private animationFrame: number | null = null - private startTime: number = performance.now() - private mousePositions: Map<string, { x: number; y: number }> = new Map() + private static instance: GlassEffectManager | null = null; + private canvas: HTMLCanvasElement | null = null; + private gl: WebGLRenderingContext | null = null; + private program: WebGLProgram | null = null; + private uniforms: Record<string, WebGLUniformLocation | null> = {}; + private effects: Map<string, EffectInstance> = new Map(); + private animationFrame: number | null = null; + private startTime: number = performance.now(); + private mousePositions: Map<string, { x: number; y: number }> = new Map(); static getInstance(): GlassEffectManager { if (!GlassEffectManager.instance) { - GlassEffectManager.instance = new GlassEffectManager() + GlassEffectManager.instance = new GlassEffectManager(); } - return GlassEffectManager.instance + return GlassEffectManager.instance; } private constructor() { - this.initializeContext() + this.initializeContext(); } private initializeContext() { // Create offscreen canvas - this.canvas = document.createElement("canvas") - this.canvas.width = 1024 // Default size, will be adjusted - this.canvas.height = 1024 + this.canvas = document.createElement("canvas"); + this.canvas.width = 1024; // Default size, will be adjusted + this.canvas.height = 1024; this.gl = this.canvas.getContext("webgl", { alpha: true, premultipliedAlpha: false, preserveDrawingBuffer: true, - }) + }); if (!this.gl) { - console.error("WebGL not supported") - return + console.error("WebGL not supported"); + return; } - this.setupShaders() - this.startRenderLoop() + this.setupShaders(); + this.startRenderLoop(); } private setupShaders() { - if (!this.gl) return + if (!this.gl) return; const vsSource = ` attribute vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); } - ` + `; const fsSource = ` precision mediump float; @@ -106,48 +106,48 @@ class GlassEffectManager { gl_FragColor = vec4(glassColor, alpha); } - ` + `; const createShader = (type: number, source: string) => { - const shader = this.gl!.createShader(type) - if (!shader) return null + const shader = this.gl!.createShader(type); + if (!shader) return null; - this.gl!.shaderSource(shader, source) - this.gl!.compileShader(shader) + this.gl!.shaderSource(shader, source); + this.gl!.compileShader(shader); if (!this.gl!.getShaderParameter(shader, this.gl!.COMPILE_STATUS)) { - console.error("Shader error:", this.gl!.getShaderInfoLog(shader)) - this.gl!.deleteShader(shader) - return null + console.error("Shader error:", this.gl!.getShaderInfoLog(shader)); + this.gl!.deleteShader(shader); + return null; } - return shader - } + return shader; + }; - const vs = createShader(this.gl.VERTEX_SHADER, vsSource) - const fs = createShader(this.gl.FRAGMENT_SHADER, fsSource) - if (!vs || !fs) return + const vs = createShader(this.gl.VERTEX_SHADER, vsSource); + const fs = createShader(this.gl.FRAGMENT_SHADER, fsSource); + if (!vs || !fs) return; - this.program = this.gl.createProgram() - if (!this.program) return + this.program = this.gl.createProgram(); + if (!this.program) return; - this.gl.attachShader(this.program, vs) - this.gl.attachShader(this.program, fs) - this.gl.linkProgram(this.program) + this.gl.attachShader(this.program, vs); + this.gl.attachShader(this.program, fs); + this.gl.linkProgram(this.program); // biome-ignore lint/correctness/useHookAtTopLevel: Well, not a hook - this.gl.useProgram(this.program) + this.gl.useProgram(this.program); // Buffer setup - const buffer = this.gl.createBuffer() - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer) + const buffer = this.gl.createBuffer(); + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer); this.gl.bufferData( this.gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), this.gl.STATIC_DRAW, - ) + ); - const position = this.gl.getAttribLocation(this.program, "position") - this.gl.enableVertexAttribArray(position) - this.gl.vertexAttribPointer(position, 2, this.gl.FLOAT, false, 0, 0) + const position = this.gl.getAttribLocation(this.program, "position"); + this.gl.enableVertexAttribArray(position); + this.gl.vertexAttribPointer(position, 2, this.gl.FLOAT, false, 0, 0); // Store uniform locations this.uniforms = { @@ -155,11 +155,11 @@ class GlassEffectManager { time: this.gl.getUniformLocation(this.program, "iTime"), mouse: this.gl.getUniformLocation(this.program, "iMouse"), expanded: this.gl.getUniformLocation(this.program, "iExpanded"), - } + }; // Enable blending - this.gl.enable(this.gl.BLEND) - this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA) + this.gl.enable(this.gl.BLEND); + this.gl.blendFunc(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA); } registerEffect( @@ -168,8 +168,8 @@ class GlassEffectManager { isExpanded: boolean, ): () => void { // Ensure minimum dimensions - const width = Math.max(1, targetCanvas.width) - const height = Math.max(1, targetCanvas.height) + const width = Math.max(1, targetCanvas.width); + const height = Math.max(1, targetCanvas.height); const effect: EffectInstance = { id, @@ -177,58 +177,58 @@ class GlassEffectManager { isExpanded, width, height, - } + }; - this.effects.set(id, effect) - this.mousePositions.set(id, { x: 0, y: 0 }) + this.effects.set(id, effect); + this.mousePositions.set(id, { x: 0, y: 0 }); // Return cleanup function return () => { - this.effects.delete(id) - this.mousePositions.delete(id) + this.effects.delete(id); + this.mousePositions.delete(id); if (this.effects.size === 0 && this.animationFrame) { - cancelAnimationFrame(this.animationFrame) - this.animationFrame = null + cancelAnimationFrame(this.animationFrame); + this.animationFrame = null; } - } + }; } updateMousePosition(id: string, x: number, y: number) { - this.mousePositions.set(id, { x, y }) + this.mousePositions.set(id, { x, y }); } updateExpanded(id: string, isExpanded: boolean) { - const effect = this.effects.get(id) + const effect = this.effects.get(id); if (effect) { - effect.isExpanded = isExpanded + effect.isExpanded = isExpanded; } } updateSize(id: string, width: number, height: number) { - const effect = this.effects.get(id) + const effect = this.effects.get(id); if (effect) { // Ensure minimum dimensions - effect.width = Math.max(1, width) - effect.height = Math.max(1, height) + effect.width = Math.max(1, width); + effect.height = Math.max(1, height); } } private startRenderLoop() { const render = () => { if (!this.gl || !this.program || this.effects.size === 0) { - this.animationFrame = requestAnimationFrame(render) - return + this.animationFrame = requestAnimationFrame(render); + return; } - const currentTime = (performance.now() - this.startTime) / 1000 + const currentTime = (performance.now() - this.startTime) / 1000; // Render each effect for (const [id, effect] of Array.from(this.effects)) { - const mousePos = this.mousePositions.get(id) || { x: 0, y: 0 } + const mousePos = this.mousePositions.get(id) || { x: 0, y: 0 }; // Skip rendering if dimensions are invalid if (effect.width <= 0 || effect.height <= 0) { - continue + continue; } // Set canvas size if needed @@ -236,14 +236,14 @@ class GlassEffectManager { this.canvas!.width !== effect.width || this.canvas!.height !== effect.height ) { - this.canvas!.width = effect.width - this.canvas!.height = effect.height - this.gl.viewport(0, 0, effect.width, effect.height) + this.canvas!.width = effect.width; + this.canvas!.height = effect.height; + this.gl.viewport(0, 0, effect.width, effect.height); } // Clear and render - this.gl.clearColor(0, 0, 0, 0) - this.gl.clear(this.gl.COLOR_BUFFER_BIT) + this.gl.clearColor(0, 0, 0, 0); + this.gl.clear(this.gl.COLOR_BUFFER_BIT); // Set uniforms if (this.uniforms.resolution) { @@ -251,58 +251,58 @@ class GlassEffectManager { this.uniforms.resolution, effect.width, effect.height, - ) + ); } if (this.uniforms.time) { - this.gl.uniform1f(this.uniforms.time, currentTime) + this.gl.uniform1f(this.uniforms.time, currentTime); } if (this.uniforms.mouse) { - this.gl.uniform2f(this.uniforms.mouse, mousePos.x, mousePos.y) + this.gl.uniform2f(this.uniforms.mouse, mousePos.x, mousePos.y); } if (this.uniforms.expanded) { this.gl.uniform1f( this.uniforms.expanded, effect.isExpanded ? 1.0 : 0.0, - ) + ); } // Draw - this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4) + this.gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 4); // Copy to target canvas - const targetCtx = effect.targetCanvas.getContext("2d") + const targetCtx = effect.targetCanvas.getContext("2d"); if (targetCtx) { - targetCtx.clearRect(0, 0, effect.width, effect.height) - targetCtx.drawImage(this.canvas!, 0, 0) + targetCtx.clearRect(0, 0, effect.width, effect.height); + targetCtx.drawImage(this.canvas!, 0, 0); } } - this.animationFrame = requestAnimationFrame(render) - } + this.animationFrame = requestAnimationFrame(render); + }; - render() + render(); } // Clean up method (optional, for when the app unmounts) destroy() { if (this.animationFrame) { - cancelAnimationFrame(this.animationFrame) + cancelAnimationFrame(this.animationFrame); } if (this.gl && this.program) { - this.gl.deleteProgram(this.program) + this.gl.deleteProgram(this.program); } - this.effects.clear() - this.mousePositions.clear() - GlassEffectManager.instance = null + this.effects.clear(); + this.mousePositions.clear(); + GlassEffectManager.instance = null; } } interface EffectInstance { - id: string - targetCanvas: HTMLCanvasElement - isExpanded: boolean - width: number - height: number + id: string; + targetCanvas: HTMLCanvasElement; + isExpanded: boolean; + width: number; + height: number; } -export default GlassEffectManager +export default GlassEffectManager; diff --git a/packages/lib/posthog.tsx b/packages/lib/posthog.tsx index ac563aae..d1105cbc 100644 --- a/packages/lib/posthog.tsx +++ b/packages/lib/posthog.tsx @@ -1,20 +1,20 @@ -"use client" +"use client"; -import { usePathname, useSearchParams } from "next/navigation" -import posthog from "posthog-js" -import { Suspense, useEffect } from "react" -import { useSession } from "./auth" +import { usePathname, useSearchParams } from "next/navigation"; +import posthog from "posthog-js"; +import { Suspense, useEffect } from "react"; +import { useSession } from "./auth"; function PostHogPageTracking() { - const pathname = usePathname() - const searchParams = useSearchParams() + const pathname = usePathname(); + const searchParams = useSearchParams(); // Page tracking useEffect(() => { if (pathname) { - let url = window.origin + pathname + let url = window.origin + pathname; if (searchParams.toString()) { - url = `${url}?${searchParams.toString()}` + url = `${url}?${searchParams.toString()}`; } // Extract page context for better tracking @@ -24,17 +24,17 @@ function PostHogPageTracking() { search_params: searchParams.toString(), page_type: getPageType(pathname), org_slug: getOrgSlug(pathname), - } + }; - posthog.capture("$pageview", pageContext) + posthog.capture("$pageview", pageContext); } - }, [pathname, searchParams]) + }, [pathname, searchParams]); - return null + return null; } export function PostHogProvider({ children }: { children: React.ReactNode }) { - const { data: session } = useSession() + const { data: session } = useSession(); useEffect(() => { if (typeof window !== "undefined") { @@ -44,9 +44,9 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) { person_profiles: "identified_only", capture_pageview: false, capture_pageleave: true, - }) + }); } - }, []) + }, []); // User identification useEffect(() => { @@ -56,9 +56,9 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) { name: session.user.name, userId: session.user.id, createdAt: session.user.createdAt, - }) + }); } - }, [session?.user]) + }, [session?.user]); return ( <> @@ -67,18 +67,18 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) { </Suspense> {children} </> - ) + ); } function getPageType(pathname: string): string { - return "other" + return "other"; } function getOrgSlug(pathname: string): string | null { - const match = pathname.match(/^\/([^/]+)\//) - return match ? (match[1] ?? null) : null + const match = pathname.match(/^\/([^/]+)\//); + return match ? (match[1] ?? null) : null; } export function usePostHog() { - return posthog + return posthog; } diff --git a/packages/lib/queries.ts b/packages/lib/queries.ts index 3e9e1ab9..d0f93915 100644 --- a/packages/lib/queries.ts +++ b/packages/lib/queries.ts @@ -1,12 +1,12 @@ -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" -import type { useCustomer } from "autumn-js/react" -import { toast } from "sonner" -import type { z } from "zod" -import type { DocumentsWithMemoriesResponseSchema } from "../validation/api" -import { $fetch } from "./api" +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import type { useCustomer } from "autumn-js/react"; +import { toast } from "sonner"; +import type { z } from "zod"; +import type { DocumentsWithMemoriesResponseSchema } from "../validation/api"; +import { $fetch } from "./api"; -type DocumentsResponse = z.infer<typeof DocumentsWithMemoriesResponseSchema> -type DocumentWithMemories = DocumentsResponse["documents"][0] +type DocumentsResponse = z.infer<typeof DocumentsWithMemoriesResponseSchema>; +type DocumentWithMemories = DocumentsResponse["documents"][0]; export const fetchSubscriptionStatus = ( autumn: ReturnType<typeof useCustomer>, @@ -18,54 +18,54 @@ export const fetchSubscriptionStatus = ( "memory_starter", "memory_growth", "consumer_pro", - ] - const statusMap: Record<string, boolean | null> = {} + ]; + const statusMap: Record<string, boolean | null> = {}; await Promise.all( allPlans.map(async (plan) => { try { const res = await autumn.check({ productId: plan, - }) - statusMap[plan] = res.data?.allowed ?? false + }); + statusMap[plan] = res.data?.allowed ?? false; } catch (error) { - console.error(`Error checking status for ${plan}:`, error) - statusMap[plan] = false + console.error(`Error checking status for ${plan}:`, error); + statusMap[plan] = false; } }), - ) + ); - return statusMap + return statusMap; }, queryKey: ["subscription-status"], refetchInterval: 5000, // Refetch every 5 seconds staleTime: 4000, // Consider data stale after 4 seconds - }) + }); // Feature checks export const fetchMemoriesFeature = (autumn: ReturnType<typeof useCustomer>) => useQuery({ queryFn: async () => { - const res = await autumn.check({ featureId: "memories" }) - return res.data + const res = await autumn.check({ featureId: "memories" }); + return res.data; }, queryKey: ["autumn-feature", "memories"], staleTime: 30 * 1000, // 30 seconds gcTime: 5 * 60 * 1000, // 5 minutes - }) + }); export const fetchConnectionsFeature = ( autumn: ReturnType<typeof useCustomer>, ) => useQuery({ queryFn: async () => { - const res = await autumn.check({ featureId: "connections" }) - return res.data + const res = await autumn.check({ featureId: "connections" }); + return res.data; }, queryKey: ["autumn-feature", "connections"], staleTime: 30 * 1000, // 30 seconds gcTime: 5 * 60 * 1000, // 5 minutes - }) + }); // Product checks export const fetchConsumerProProduct = ( @@ -73,54 +73,54 @@ export const fetchConsumerProProduct = ( ) => useQuery({ queryFn: async () => { - const res = await autumn.check({ productId: "consumer_pro" }) - return res.data + const res = await autumn.check({ productId: "consumer_pro" }); + return res.data; }, queryKey: ["autumn-product", "consumer_pro"], staleTime: 30 * 1000, // 30 seconds gcTime: 5 * 60 * 1000, // 5 minutes - }) + }); export const fetchProProduct = (autumn: ReturnType<typeof useCustomer>) => useQuery({ queryFn: async () => { - const res = await autumn.check({ productId: "pro" }) - return res.data + const res = await autumn.check({ productId: "pro" }); + return res.data; }, queryKey: ["autumn-product", "pro"], staleTime: 30 * 1000, // 30 seconds gcTime: 5 * 60 * 1000, // 5 minutes - }) + }); export const useDeleteDocument = (selectedProject: string) => { - const queryClient = useQueryClient() + const queryClient = useQueryClient(); return useMutation({ mutationFn: async (documentId: string) => { // context for LLM: delete/memories/:documentId is documents delete endpoint not memories delete endpoint - const response = await $fetch(`@delete/documents/${documentId}`) + const response = await $fetch(`@delete/documents/${documentId}`); if (response.error) { - throw new Error(response.error?.message || "Failed to delete document") + throw new Error(response.error?.message || "Failed to delete document"); } - return response.data + return response.data; }, onMutate: async (documentId: string) => { await queryClient.cancelQueries({ queryKey: ["documents-with-memories", selectedProject], - }) + }); const previousData = queryClient.getQueryData([ "documents-with-memories", selectedProject, - ]) + ]); queryClient.setQueryData( ["documents-with-memories", selectedProject], (old: unknown) => { - if (!old || typeof old !== "object") return old + if (!old || typeof old !== "object") return old; const typedOld = old as { - pages?: Array<{ documents?: DocumentWithMemories[] }> - } + pages?: Array<{ documents?: DocumentWithMemories[] }>; + }; return { ...typedOld, pages: typedOld.pages?.map((page) => ({ @@ -129,30 +129,30 @@ export const useDeleteDocument = (selectedProject: string) => { (doc: DocumentWithMemories) => doc.id !== documentId, ), })), - } + }; }, - ) + ); - return { previousData } + return { previousData }; }, onSuccess: () => { - toast.success("Memory deleted successfully") + toast.success("Memory deleted successfully"); }, onError: (error, _documentId, context) => { if (context?.previousData) { queryClient.setQueryData( ["documents-with-memories", selectedProject], context.previousData, - ) + ); } toast.error("Failed to delete memory", { description: error instanceof Error ? error.message : "Unknown error", - }) + }); }, onSettled: () => { queryClient.invalidateQueries({ queryKey: ["documents-with-memories", selectedProject], - }) + }); }, - }) -} + }); +}; diff --git a/packages/lib/query-client.tsx b/packages/lib/query-client.tsx index eb9c5a21..322e9f89 100644 --- a/packages/lib/query-client.tsx +++ b/packages/lib/query-client.tsx @@ -1,8 +1,8 @@ -"use client" +"use client"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query" -import { ReactQueryDevtools } from "@tanstack/react-query-devtools" -import { useState } from "react" +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { useState } from "react"; export const QueryProvider = ({ children }: { children: React.ReactNode }) => { const [queryClient] = useState( @@ -16,12 +16,12 @@ export const QueryProvider = ({ children }: { children: React.ReactNode }) => { }, }, }), - ) + ); return ( <QueryClientProvider client={queryClient}> {children} <ReactQueryDevtools initialIsOpen={false} /> </QueryClientProvider> - ) -} + ); +}; diff --git a/packages/lib/similarity.ts b/packages/lib/similarity.ts index 09d3a2cc..0d43710d 100644 --- a/packages/lib/similarity.ts +++ b/packages/lib/similarity.ts @@ -10,27 +10,27 @@ export const cosineSimilarity = ( vectorB: number[], ): number => { if (vectorA.length !== vectorB.length) { - throw new Error("Vectors must have the same length") + throw new Error("Vectors must have the same length"); } - let dotProduct = 0 + let dotProduct = 0; for (let i = 0; i < vectorA.length; i++) { - const vectorAi = vectorA[i] - const vectorBi = vectorB[i] + const vectorAi = vectorA[i]; + const vectorBi = vectorB[i]; if ( typeof vectorAi !== "number" || typeof vectorBi !== "number" || isNaN(vectorAi) || isNaN(vectorBi) ) { - throw new Error("Vectors must contain only numbers") + throw new Error("Vectors must contain only numbers"); } - dotProduct += vectorAi * vectorBi + dotProduct += vectorAi * vectorBi; } - return dotProduct -} + return dotProduct; +}; /** * Calculate semantic similarity between two documents @@ -47,13 +47,13 @@ export const calculateSemanticSimilarity = ( document1Embedding.length > 0 && document2Embedding.length > 0 ) { - const similarity = cosineSimilarity(document1Embedding, document2Embedding) + const similarity = cosineSimilarity(document1Embedding, document2Embedding); // Convert from [-1, 1] to [0, 1] range - return similarity >= 0 ? similarity : 0 + return similarity >= 0 ? similarity : 0; } - return 0 -} + return 0; +}; /** * Calculate semantic similarity between a document and memory entry @@ -71,34 +71,34 @@ export const calculateDocumentMemorySimilarity = ( documentEmbedding.length > 0 && memoryEmbedding.length > 0 ) { - const similarity = cosineSimilarity(documentEmbedding, memoryEmbedding) + const similarity = cosineSimilarity(documentEmbedding, memoryEmbedding); // Convert from [-1, 1] to [0, 1] range - return similarity >= 0 ? similarity : 0 + return similarity >= 0 ? similarity : 0; } // Fall back to relevance score from database (0-100 scale) if (relevanceScore !== null && relevanceScore !== undefined) { - return Math.max(0, Math.min(1, relevanceScore / 100)) + return Math.max(0, Math.min(1, relevanceScore / 100)); } // Default similarity for connections without embeddings or relevance scores - return 0.5 -} + return 0.5; +}; /** * Get visual properties for connection based on similarity */ export const getConnectionVisualProps = (similarity: number) => { // Ensure similarity is between 0 and 1 - const normalizedSimilarity = Math.max(0, Math.min(1, similarity)) + const normalizedSimilarity = Math.max(0, Math.min(1, similarity)); return { opacity: Math.max(0, normalizedSimilarity), // 0 to 1 range thickness: Math.max(1, normalizedSimilarity * 4), // 1 to 4 pixels glow: normalizedSimilarity * 0.6, // Glow intensity pulseDuration: 2000 + (1 - normalizedSimilarity) * 3000, // Faster pulse for higher similarity - } -} + }; +}; /** * Generate magical color based on similarity and connection type @@ -107,9 +107,9 @@ export const getMagicalConnectionColor = ( similarity: number, hue = 220, ): string => { - const normalizedSimilarity = Math.max(0, Math.min(1, similarity)) - const saturation = 60 + normalizedSimilarity * 40 // 60% to 100% - const lightness = 40 + normalizedSimilarity * 30 // 40% to 70% + const normalizedSimilarity = Math.max(0, Math.min(1, similarity)); + const saturation = 60 + normalizedSimilarity * 40; // 60% to 100% + const lightness = 40 + normalizedSimilarity * 30; // 40% to 70% - return `hsl(${hue}, ${saturation}%, ${lightness}%)` -} + return `hsl(${hue}, ${saturation}%, ${lightness}%)`; +}; diff --git a/packages/lib/utils.ts b/packages/lib/utils.ts index 02a2ddaf..59e86ae1 100644 --- a/packages/lib/utils.ts +++ b/packages/lib/utils.ts @@ -1,8 +1,8 @@ -import { type ClassValue, clsx } from "clsx" -import { twMerge } from "tailwind-merge" +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); } -export const isSelfHosted = process.env.NEXT_PUBLIC_HOST_ID !== "supermemory" +export const isSelfHosted = process.env.NEXT_PUBLIC_HOST_ID !== "supermemory"; |