+ {/* biome-ignore lint/a11y/useKeyWithClickEvents: Modal has keyboard support via Escape key */}
+
onOpenChange(false)} />
+
+
+ );
+}
diff --git a/apps/connect/src/index.css b/apps/connect/src/index.css
new file mode 100644
index 00000000..a5b733f7
--- /dev/null
+++ b/apps/connect/src/index.css
@@ -0,0 +1,129 @@
+@import "tailwindcss";
+
+@plugin 'tailwindcss-animate';
+
+@source '../../../packages/hypergraph-react/src/**/*.{js,ts,jsx,tsx}';
+
+@custom-variant dark (&:is(.dark *));
+
+@theme {
+ --radius-lg: var(--radius);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-sm: calc(var(--radius) - 4px);
+
+ --color-background: hsl(var(--background));
+ --color-foreground: hsl(var(--foreground));
+
+ --color-card: hsl(var(--card));
+ --color-card-foreground: hsl(var(--card-foreground));
+
+ --color-popover: hsl(var(--popover));
+ --color-popover-foreground: hsl(var(--popover-foreground));
+
+ --color-primary: hsl(var(--primary));
+ --color-primary-foreground: hsl(var(--primary-foreground));
+
+ --color-secondary: hsl(var(--secondary));
+ --color-secondary-foreground: hsl(var(--secondary-foreground));
+
+ --color-muted: hsl(var(--muted));
+ --color-muted-foreground: hsl(var(--muted-foreground));
+
+ --color-accent: hsl(var(--accent));
+ --color-accent-foreground: hsl(var(--accent-foreground));
+
+ --color-destructive: hsl(var(--destructive));
+ --color-destructive-foreground: hsl(var(--destructive-foreground));
+
+ --color-border: hsl(var(--border));
+ --color-input: hsl(var(--input));
+ --color-ring: hsl(var(--ring));
+
+ --color-chart-1: hsl(var(--chart-1));
+ --color-chart-2: hsl(var(--chart-2));
+ --color-chart-3: hsl(var(--chart-3));
+ --color-chart-4: hsl(var(--chart-4));
+ --color-chart-5: hsl(var(--chart-5));
+}
+
+/*
+ The default border color has changed to `currentcolor` in Tailwind CSS v4,
+ so we've added these compatibility styles to make sure everything still
+ looks the same as it did with Tailwind CSS v3.
+
+ If we ever want to remove these styles, we need to add an explicit border
+ color utility to any element that depends on these defaults.
+*/
+@layer base {
+ *,
+ ::after,
+ ::before,
+ ::backdrop,
+ ::file-selector-button {
+ border-color: var(--color-gray-200, currentcolor);
+ }
+}
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 0 0% 3.9%;
+ --card: 0 0% 100%;
+ --card-foreground: 0 0% 3.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 0 0% 3.9%;
+ --primary: 0 0% 9%;
+ --primary-foreground: 0 0% 98%;
+ --secondary: 0 0% 96.1%;
+ --secondary-foreground: 0 0% 9%;
+ --muted: 0 0% 96.1%;
+ --muted-foreground: 0 0% 45.1%;
+ --accent: 0 0% 96.1%;
+ --accent-foreground: 0 0% 9%;
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 0 0% 98%;
+ --border: 0 0% 89.8%;
+ --input: 0 0% 89.8%;
+ --ring: 0 0% 3.9%;
+ --chart-1: 12 76% 61%;
+ --chart-2: 173 58% 39%;
+ --chart-3: 197 37% 24%;
+ --chart-4: 43 74% 66%;
+ --chart-5: 27 87% 67%;
+ --radius: 0.5rem;
+ }
+ .dark {
+ --background: 0 0% 3.9%;
+ --foreground: 0 0% 98%;
+ --card: 0 0% 3.9%;
+ --card-foreground: 0 0% 98%;
+ --popover: 0 0% 3.9%;
+ --popover-foreground: 0 0% 98%;
+ --primary: 0 0% 98%;
+ --primary-foreground: 0 0% 9%;
+ --secondary: 0 0% 14.9%;
+ --secondary-foreground: 0 0% 98%;
+ --muted: 0 0% 14.9%;
+ --muted-foreground: 0 0% 63.9%;
+ --accent: 0 0% 14.9%;
+ --accent-foreground: 0 0% 98%;
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 0 0% 98%;
+ --border: 0 0% 14.9%;
+ --input: 0 0% 14.9%;
+ --ring: 0 0% 83.1%;
+ --chart-1: 220 70% 50%;
+ --chart-2: 160 60% 45%;
+ --chart-3: 30 80% 55%;
+ --chart-4: 280 65% 60%;
+ --chart-5: 340 75% 55%;
+ }
+}
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
diff --git a/apps/connect/src/lib/utils.ts b/apps/connect/src/lib/utils.ts
new file mode 100644
index 00000000..9ad0df42
--- /dev/null
+++ b/apps/connect/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from 'clsx';
+import { twMerge } from 'tailwind-merge';
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/apps/connect/src/main.test.ts b/apps/connect/src/main.test.ts
new file mode 100644
index 00000000..07b9190a
--- /dev/null
+++ b/apps/connect/src/main.test.ts
@@ -0,0 +1,5 @@
+import { expect, test } from 'vitest';
+
+test('hello', () => {
+ expect('Hello from hypergraph').toBe('Hello from hypergraph');
+});
diff --git a/apps/connect/src/main.tsx b/apps/connect/src/main.tsx
new file mode 100644
index 00000000..0f1639cb
--- /dev/null
+++ b/apps/connect/src/main.tsx
@@ -0,0 +1,11 @@
+import { StrictMode } from 'react';
+import { createRoot } from 'react-dom/client';
+import { Boot } from './Boot.js';
+import './index.css';
+
+// biome-ignore lint/style/noNonNullAssertion: root element is always there
+createRoot(document.getElementById('root')!).render(
+
+ ,
+ ,
+);
diff --git a/apps/connect/src/routeTree.gen.ts b/apps/connect/src/routeTree.gen.ts
new file mode 100644
index 00000000..1c08a62d
--- /dev/null
+++ b/apps/connect/src/routeTree.gen.ts
@@ -0,0 +1,116 @@
+/* eslint-disable */
+
+// @ts-nocheck
+
+// noinspection JSUnusedGlobalSymbols
+
+// This file was automatically generated by TanStack Router.
+// You should NOT make any changes in this file as it will be overwritten.
+// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
+
+import { createFileRoute } from '@tanstack/react-router'
+
+// Import Routes
+
+import { Route as rootRoute } from './routes/__root'
+import { Route as IndexImport } from './routes/index'
+
+// Create Virtual Routes
+
+const LoginLazyImport = createFileRoute('/login')()
+
+// Create/Update Routes
+
+const LoginLazyRoute = LoginLazyImport.update({
+ id: '/login',
+ path: '/login',
+ getParentRoute: () => rootRoute,
+} as any).lazy(() => import('./routes/login.lazy').then((d) => d.Route))
+
+const IndexRoute = IndexImport.update({
+ id: '/',
+ path: '/',
+ getParentRoute: () => rootRoute,
+} as any)
+
+// Populate the FileRoutesByPath interface
+
+declare module '@tanstack/react-router' {
+ interface FileRoutesByPath {
+ '/': {
+ id: '/'
+ path: '/'
+ fullPath: '/'
+ preLoaderRoute: typeof IndexImport
+ parentRoute: typeof rootRoute
+ }
+ '/login': {
+ id: '/login'
+ path: '/login'
+ fullPath: '/login'
+ preLoaderRoute: typeof LoginLazyImport
+ parentRoute: typeof rootRoute
+ }
+ }
+}
+
+// Create and export the route tree
+
+export interface FileRoutesByFullPath {
+ '/': typeof IndexRoute
+ '/login': typeof LoginLazyRoute
+}
+
+export interface FileRoutesByTo {
+ '/': typeof IndexRoute
+ '/login': typeof LoginLazyRoute
+}
+
+export interface FileRoutesById {
+ __root__: typeof rootRoute
+ '/': typeof IndexRoute
+ '/login': typeof LoginLazyRoute
+}
+
+export interface FileRouteTypes {
+ fileRoutesByFullPath: FileRoutesByFullPath
+ fullPaths: '/' | '/login'
+ fileRoutesByTo: FileRoutesByTo
+ to: '/' | '/login'
+ id: '__root__' | '/' | '/login'
+ fileRoutesById: FileRoutesById
+}
+
+export interface RootRouteChildren {
+ IndexRoute: typeof IndexRoute
+ LoginLazyRoute: typeof LoginLazyRoute
+}
+
+const rootRouteChildren: RootRouteChildren = {
+ IndexRoute: IndexRoute,
+ LoginLazyRoute: LoginLazyRoute,
+}
+
+export const routeTree = rootRoute
+ ._addFileChildren(rootRouteChildren)
+ ._addFileTypes
()
+
+/* ROUTE_MANIFEST_START
+{
+ "routes": {
+ "__root__": {
+ "filePath": "__root.tsx",
+ "children": [
+ "/",
+ "/login"
+ ]
+ },
+ "/": {
+ "filePath": "index.tsx"
+ },
+ "/login": {
+ "filePath": "login.lazy.tsx"
+ }
+ }
+}
+ROUTE_MANIFEST_END */
diff --git a/apps/connect/src/routes/__root.tsx b/apps/connect/src/routes/__root.tsx
new file mode 100644
index 00000000..1293cc47
--- /dev/null
+++ b/apps/connect/src/routes/__root.tsx
@@ -0,0 +1,61 @@
+import { Logout } from '@/components/logout';
+import { usePrivy } from '@privy-io/react-auth';
+import { Link, Outlet, createRootRoute, useLayoutEffect, useRouter } from '@tanstack/react-router';
+import { TanStackRouterDevtools } from '@tanstack/router-devtools';
+
+export const Route = createRootRoute({
+ component: () => {
+ const { authenticated, ready } = usePrivy();
+ const router = useRouter();
+
+ useLayoutEffect(() => {
+ if (router.state.location.href.startsWith('/login')) {
+ return;
+ }
+
+ if (ready && !authenticated) {
+ router.navigate({
+ to: '/login',
+ });
+ }
+ }, [authenticated, ready]);
+
+ return (
+ <>
+
+
+
+ Connect
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+ },
+});
diff --git a/apps/connect/src/routes/index.tsx b/apps/connect/src/routes/index.tsx
new file mode 100644
index 00000000..0c1668c7
--- /dev/null
+++ b/apps/connect/src/routes/index.tsx
@@ -0,0 +1,13 @@
+import { createFileRoute } from '@tanstack/react-router';
+
+export const Route = createFileRoute('/')({
+ component: Index,
+});
+
+function Index() {
+ return (
+
+
Welcome to Connect
+
+ );
+}
diff --git a/apps/connect/src/routes/login.lazy.tsx b/apps/connect/src/routes/login.lazy.tsx
new file mode 100644
index 00000000..252a772c
--- /dev/null
+++ b/apps/connect/src/routes/login.lazy.tsx
@@ -0,0 +1,36 @@
+import { usePrivy } from '@privy-io/react-auth';
+import { createLazyFileRoute, useRouter } from '@tanstack/react-router';
+
+import { Button } from '@/components/ui/button';
+import { useEffect } from 'react';
+
+export const Route = createLazyFileRoute('/login')({
+ component: () => ,
+});
+
+function Login() {
+ const { ready, login, authenticated } = usePrivy();
+ const { navigate } = useRouter();
+
+ useEffect(() => {
+ if (ready && authenticated) {
+ navigate({ to: '/' });
+ }
+ }, [authenticated, ready, navigate]);
+
+ return (
+
+
+
+
+
+ );
+}
diff --git a/apps/connect/src/vite-env.d.ts b/apps/connect/src/vite-env.d.ts
new file mode 100644
index 00000000..11f02fe2
--- /dev/null
+++ b/apps/connect/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/apps/connect/tsconfig.app.json b/apps/connect/tsconfig.app.json
new file mode 100644
index 00000000..de55dcc1
--- /dev/null
+++ b/apps/connect/tsconfig.app.json
@@ -0,0 +1,37 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "include": ["src"],
+ "compilerOptions": {
+ "target": "ES2021",
+ "useDefineForClassFields": true,
+ "lib": ["ES2021", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ "composite": false,
+ "incremental": false,
+ "declaration": false,
+ "declarationMap": false,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "exactOptionalPropertyTypes": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true,
+
+ /* Shadcn */
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/apps/connect/tsconfig.json b/apps/connect/tsconfig.json
new file mode 100644
index 00000000..1bfa43d4
--- /dev/null
+++ b/apps/connect/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "include": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/apps/connect/tsconfig.node.json b/apps/connect/tsconfig.node.json
new file mode 100644
index 00000000..132ac4b6
--- /dev/null
+++ b/apps/connect/tsconfig.node.json
@@ -0,0 +1,29 @@
+{
+ "extends": "../../tsconfig.base.json",
+ "include": ["vite.config.ts"],
+ "compilerOptions": {
+ "target": "ES2022",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ "composite": false,
+ "incremental": false,
+ "declaration": false,
+ "declarationMap": false,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "isolatedModules": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+
+ /* Linting */
+ "strict": true,
+ "exactOptionalPropertyTypes": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ }
+}
diff --git a/apps/connect/vite.config.ts b/apps/connect/vite.config.ts
new file mode 100644
index 00000000..6bf7dd40
--- /dev/null
+++ b/apps/connect/vite.config.ts
@@ -0,0 +1,15 @@
+import path from 'node:path';
+import tailwindcss from '@tailwindcss/vite';
+import { TanStackRouterVite } from '@tanstack/router-plugin/vite';
+import react from '@vitejs/plugin-react';
+import { defineConfig } from 'vite';
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [TanStackRouterVite(), react(), tailwindcss()],
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src'),
+ },
+ },
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 635f0925..98887b2d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -36,6 +36,85 @@ importers:
specifier: ^3.1.3
version: 3.1.3(@types/debug@4.1.12)(@types/node@22.15.15)(jiti@2.4.2)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.7.0)
+ apps/connect:
+ dependencies:
+ '@graphprotocol/grc-20':
+ specifier: ^0.11.5
+ version: 0.11.5(bufferutil@4.0.9)(graphql@16.11.0)(ox@0.6.7(typescript@5.8.3)(zod@3.24.2))(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.24.2)
+ '@privy-io/react-auth':
+ specifier: ^2.13.0
+ version: 2.13.0(@solana/web3.js@1.95.3(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10))(@types/react@19.1.3)(bs58@6.0.0)(bufferutil@4.0.9)(permissionless@0.2.44(ox@0.6.7(typescript@5.8.3)(zod@3.24.2))(viem@2.29.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.24.2)))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.5.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.24.2)
+ '@radix-ui/react-avatar':
+ specifier: ^1.1.9
+ version: 1.1.9(@types/react-dom@19.1.3(@types/react@19.1.3))(@types/react@19.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@radix-ui/react-icons':
+ specifier: ^1.3.2
+ version: 1.3.2(react@19.1.0)
+ '@radix-ui/react-slot':
+ specifier: ^1.2.2
+ version: 1.2.2(@types/react@19.1.3)(react@19.1.0)
+ '@tanstack/react-query':
+ specifier: ^5.75.5
+ version: 5.75.5(react@19.1.0)
+ '@tanstack/react-router':
+ specifier: ^1.120.2
+ version: 1.120.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ class-variance-authority:
+ specifier: ^0.7.1
+ version: 0.7.1
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
+ effect:
+ specifier: ^3.14.20
+ version: 3.15.1
+ framer-motion:
+ specifier: ^12.10.1
+ version: 12.10.1(@emotion/is-prop-valid@1.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ lucide-react:
+ specifier: ^0.508.0
+ version: 0.508.0(react@19.1.0)
+ react:
+ specifier: ^19.1.0
+ version: 19.1.0
+ react-dom:
+ specifier: ^19.1.0
+ version: 19.1.0(react@19.1.0)
+ tailwind-merge:
+ specifier: ^3.2.0
+ version: 3.2.0
+ tailwindcss-animate:
+ specifier: ^1.0.7
+ version: 1.0.7(tailwindcss@4.1.5)
+ vite:
+ specifier: ^6.3.5
+ version: 6.3.5(@types/node@22.15.15)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.7.0)
+ devDependencies:
+ '@tailwindcss/vite':
+ specifier: ^4.1.5
+ version: 4.1.5(vite@6.3.5(@types/node@22.15.15)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.7.0))
+ '@tanstack/router-devtools':
+ specifier: ^1.120.2
+ version: 1.120.2(@tanstack/react-router@1.120.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@tanstack/router-core@1.119.0)(csstype@3.1.3)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(tiny-invariant@1.3.3)
+ '@tanstack/router-plugin':
+ specifier: ^1.120.2
+ version: 1.120.2(@tanstack/react-router@1.120.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(vite@6.3.5(@types/node@22.15.15)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.7.0))
+ '@types/node':
+ specifier: ^22.15.15
+ version: 22.15.15
+ '@types/react':
+ specifier: ^19.1.3
+ version: 19.1.3
+ '@types/react-dom':
+ specifier: ^19.1.3
+ version: 19.1.3(@types/react@19.1.3)
+ '@vitejs/plugin-react':
+ specifier: ^4.4.1
+ version: 4.4.1(vite@6.3.5(@types/node@22.15.15)(jiti@2.4.2)(lightningcss@1.29.2)(tsx@4.19.4)(yaml@2.7.0))
+ tailwindcss:
+ specifier: ^4.1.5
+ version: 4.1.5
+
apps/events:
dependencies:
'@graphprotocol/grc-20':
@@ -4292,6 +4371,9 @@ packages:
effect@3.14.20:
resolution: {integrity: sha512-c3r1jpnev6epl1DU/baCsEU1P4571ExTEm9M8OTreIbMT5AFGC0lJoVrHFkjv0IMtIz8cwPObbjjsIhhSPPWKA==}
+ effect@3.15.1:
+ resolution: {integrity: sha512-n3bDF6K3R+FSVuH+dSVU3ya2pI4Wt/tnKzum3DC/3b5e0E9HfhrhbkonOkYU3AVJJOzCA6zZE2/y6EUgQNAY4g==}
+
electron-to-chromium@1.5.52:
resolution: {integrity: sha512-xtoijJTZ+qeucLBDNztDOuQBE1ksqjvNjvqFoST3nGC7fSpqJ+X6BdTBaY5BHG+IhWWmpc6b/KfpeuEDupEPOQ==}
@@ -8763,7 +8845,7 @@ snapshots:
dependencies:
'@bufbuild/protobuf': 1.10.1
'@changesets/cli': 2.29.3
- effect: 3.14.20
+ effect: 3.15.1
ethers: 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)
fflate: 0.8.2
graphql-request: 7.1.2(graphql@16.11.0)
@@ -12624,6 +12706,11 @@ snapshots:
'@standard-schema/spec': 1.0.0
fast-check: 3.23.2
+ effect@3.15.1:
+ dependencies:
+ '@standard-schema/spec': 1.0.0
+ fast-check: 3.23.2
+
electron-to-chromium@1.5.52: {}
elliptic@6.5.4: