diff --git a/.gitignore b/.gitignore index 4684df71..3c555727 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,9 @@ test-output *.db* .mastra +# NX cache +.nx/ + # Vite cache node_modules/.vite **/.vite diff --git a/apps/agentic-chat/package.json b/apps/agentic-chat/package.json index 90dd628b..13537118 100644 --- a/apps/agentic-chat/package.json +++ b/apps/agentic-chat/package.json @@ -55,8 +55,10 @@ "rehype-katex": "^7.0.1", "remark-gfm": "^4.0.1", "remark-math": "^6.0.0", + "shaders": "^2.5.109", "sonner": "^2.0.7", "tailwind-merge": "^3.2.0", + "three": "^0.184.0", "viem": "*", "wagmi": "^2.15.6", "zod": "*", diff --git a/apps/agentic-chat/src/app/dashboard/page.tsx b/apps/agentic-chat/src/app/dashboard/page.tsx index db3e1a0e..d2886e3e 100644 --- a/apps/agentic-chat/src/app/dashboard/page.tsx +++ b/apps/agentic-chat/src/app/dashboard/page.tsx @@ -13,7 +13,7 @@ export const Dashboard = () => { {isSidebarLeftEnabled && } -
+
{isSidebarLeftEnabled && }
diff --git a/apps/agentic-chat/src/components/AuroraBackground.tsx b/apps/agentic-chat/src/components/AuroraBackground.tsx new file mode 100644 index 00000000..ba72985f --- /dev/null +++ b/apps/agentic-chat/src/components/AuroraBackground.tsx @@ -0,0 +1,107 @@ +import { useEffect, useRef, useState } from 'react' + +import { useIsMobile } from '@/hooks/use-mobile' + +let cachedWebGLAvailable: boolean | null = null +function isWebGLAvailable(): boolean { + if (cachedWebGLAvailable !== null) return cachedWebGLAvailable + try { + const canvas = document.createElement('canvas') + const ctx = window.WebGLRenderingContext && (canvas.getContext('webgl2') ?? canvas.getContext('webgl')) + if (ctx) { + // Release the test context immediately so we don't exhaust the browser's limit + const ext = (ctx as WebGLRenderingContext).getExtension('WEBGL_lose_context') + ext?.loseContext() + } + cachedWebGLAvailable = !!ctx + } catch { + cachedWebGLAvailable = false + } + return cachedWebGLAvailable +} + +function CSSFallback() { + return ( +
+ ) +} + +function AuroraCanvas({ onError }: { onError: () => void }) { + const canvasRef = useRef(null) + + useEffect(() => { + const canvas = canvasRef.current + if (!canvas) return + + let cleanup: (() => void) | null = null + let cancelled = false + + import('shaders/js') + .then(({ createShader }) => { + if (cancelled) return Promise.resolve(null) + return createShader( + canvas, + { + components: [ + { + id: 'aurora', + type: 'Aurora', + props: { + colorA: '#7B2FBE', + colorB: '#00CD98', + colorC: '#A855F7', + speed: 3.5, + waviness: 70, + intensity: 90, + curtainCount: 4, + rayDensity: 25, + height: 150, + balance: 40, + colorSpace: 'linear', + }, + }, + ], + }, + { disableTelemetry: true } + ) + }) + .then(shader => { + if (!shader) return + if (cancelled) { + shader.destroy() + return + } + cleanup = () => shader.destroy() + }) + .catch(err => { + console.error('[AuroraBackground] shader init failed, falling back to CSS:', err) + cleanup?.() + cleanup = null + if (!cancelled) onError() + }) + + return () => { + cancelled = true + cleanup?.() + } + }, [onError]) + + return +} + +export function AuroraBackground() { + const isMobile = useIsMobile() + const [shaderFailed, setShaderFailed] = useState(false) + + if (isMobile || !isWebGLAvailable() || shaderFailed) { + return + } + + return setShaderFailed(true)} /> +} diff --git a/apps/agentic-chat/src/components/Chat.tsx b/apps/agentic-chat/src/components/Chat.tsx index 5c1e1e75..6de5a268 100644 --- a/apps/agentic-chat/src/components/Chat.tsx +++ b/apps/agentic-chat/src/components/Chat.tsx @@ -6,6 +6,7 @@ import { useStreamPauseDetector } from '../hooks/useStreamPauseDetector' import { useChatContext } from '../providers/ChatProvider' import { AssistantMessage } from './AssistantMessage' +import { AuroraBackground } from './AuroraBackground' import { Composer } from './Composer' import { LoadingIndicator } from './LoadingIndicator' import { Button } from './ui/Button' @@ -93,8 +94,9 @@ export function Chat() { {/* Messages viewport */}
{isEmpty ? ( -
-
How can I help you today?
+
+ +
How can I help you today?
) : ( +
{WELCOME_SUGGESTIONS.map((suggestion, index) => (