Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ba74ed5
feat(#1957): add Drawer component
DSanich Feb 26, 2026
23e28b1
feat(#1957): add useIsMobile hook
DSanich Feb 26, 2026
894aabb
feat(#1957): add AI left panel components
DSanich Feb 26, 2026
7d18130
feat(#1957): integrate AI left panel into app layout
DSanich Feb 26, 2026
d60f45c
feat(#1957): updated lock file
DSanich Feb 26, 2026
06516cc
feat(#1958): add mock data and types
DSanich Feb 26, 2026
688f81f
feat(#1958): add AiPanelHeader component
DSanich Feb 26, 2026
94d966c
feat(#1958): add AiPanelMessageBubble component
DSanich Feb 26, 2026
a7db782
feat(#1958): add AiPanelSuggestions component
DSanich Feb 26, 2026
f9fa9ac
feat(#1958): add AiPanelChatBar component
DSanich Feb 27, 2026
49a81c5
feat(#1958): add AiPanelMessages component
DSanich Feb 27, 2026
8f51f03
feat(#1958): add index and refactor AiLeftPanel
DSanich Feb 27, 2026
b380b0f
feat(#1958): format fix
DSanich Feb 27, 2026
a9e6781
feat(#1958): wrap header and chat bar elements on narrow width
DSanich Mar 2, 2026
7727bcc
feat(#1969): add AI SDK and update zod for AI panel
DSanich Mar 3, 2026
fc97d80
feat(#1969): add chat API route with Gemini 2.5 Flash
DSanich Mar 3, 2026
00f5084
feat(#1969): integrate useChat in AI panel with reset and streaming
DSanich Mar 3, 2026
98c986d
feat(#1969): add GOOGLE_GENERATIVE_AI_API_KEY to env template
DSanich Mar 3, 2026
52256d7
fix(#1969): formatting
DSanich Mar 3, 2026
c9bb1db
feat(#1969): restrict AI panel to authenticated users
DSanich Mar 3, 2026
b51de5c
feat(#1969): extend chat bar with multimodal input
DSanich Mar 3, 2026
9a10d3d
feat(#1969): add copy button for bot messages, remove feedback buttons
DSanich Mar 3, 2026
c2b773c
Added chats
evgenibir Mar 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions .github/workflows/deploy-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,10 @@ jobs:

- name: Run Migrations
if: needs.detect-changes.outputs.has_migrations == 'true'
env:
BRANCH_DB_URL: ${{ steps.create-branch.outputs.db_url_with_pooler }}
run: |
echo "BRANCH_DB_URL: ${{ steps.create-branch.outputs.db_url_with_pooler }}" >> apps/web/.env
vercel env pull --environment=preview --token=${{ env.VERCEL_TOKEN }}
pnpm run migrate

- name: Post Schema Diff Comment to PR
Expand All @@ -90,11 +92,17 @@ jobs:
api_key: ${{ secrets.NEON_API_KEY }}

- name: Build Project Artifacts
run: vercel build --token=${{ env.VERCEL_TOKEN }}
env:
BRANCH_DB_URL: ${{ steps.create-branch.outputs.db_url_with_pooler }}
run: |
vercel env pull --environment=preview --token=${{ env.VERCEL_TOKEN }}
vercel build --token=${{ env.VERCEL_TOKEN }}

- name: Deploy Preview to Vercel
id: deploy
run: echo preview_url=$(vercel deploy --prebuilt --token=${{ env.VERCEL_TOKEN }}) >> $GITHUB_OUTPUT
env:
BRANCH_DB_URL: ${{ steps.create-branch.outputs.db_url_with_pooler }}
run: echo preview_url=$(vercel deploy --prebuilt --token=${{ env.VERCEL_TOKEN }} --env BRANCH_DB_URL=${{ env.BRANCH_DB_URL }}) >> $GITHUB_OUTPUT

main:
needs: [detect-changes]
Expand Down
18 changes: 18 additions & 0 deletions apps/web/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ NEXT_PUBLIC_CONNECT_SOURCES="https://*.uploadthing.com, https://auth.privy.io"

# Privy
NEXT_PUBLIC_PRIVY_APP_ID=cm5y07p2z02napk1cutzzx7o6
PRIVY_APP_SECRET=

# Web3Auth
NEXT_PUBLIC_WEB3AUTH_CLIENT_ID=BBKg7QjLogP_fTLATXrdCIiyTguXFTKJkCLYIr51c_-P0y7EyLBIABEHW6psI2qm919Zk8broitgD66hZ2EySKY

# Hypha AI panel (Google Gemini)
GOOGLE_GENERATIVE_AI_API_KEY=

# Alchemy
ALCHEMY_API_KEY=

Expand Down Expand Up @@ -58,3 +62,17 @@ DISABLE_IMAGE_OPTIMIZATION=false
# SOCKS5 Proxy
SOCKS5_PROXY_HOST=
SOCKS5_PROXY_PORT=

# Matrix
NEXT_PUBLIC_ELEMENT_URL=https://threads.hypha.earth
NEXT_PUBLIC_MATRIX_HOMESERVER_URL=https://matrix.hypha.earth
MATRIX_DOMAIN=matrix.hypha.earth
MATRIX_ADMIN_TOKEN=
DEFAULT_ROOM_ID=
MATRIX_REGISTRATION_SHARED_SECRET=
# 32-byte in hex format
MATRIX_PASSWORD_SECRET=

# reCAPTCHA
RECAPTCHA_SITE_KEY=
RECAPTCHA_SECRET_KEY=
3 changes: 3 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
"postinstall": "if [ ! -f .env ]; then cp .env.template .env; fi"
},
"dependencies": {
"@ai-sdk/google": "^2.0.0",
"@ai-sdk/react": "^3.0.107",
"@hypha-platform/authentication": "workspace:*",
"@hypha-platform/cookie": "workspace:*",
"@hypha-platform/core": "workspace:*",
Expand All @@ -23,6 +25,7 @@
"@hypha-platform/storage-postgres": "workspace:*",
"@hypha-platform/ui": "workspace:*",
"@hypha-platform/ui-utils": "workspace:*",
"ai": "^6.0.105",
"d3": "^7.9.0",
"fetch-socks": "^1.3.2",
"next": "^15.4.8",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use client';

import {
ChatDetail,
ChatPageParams,
SidePanel,
useConversation,
} from '@hypha-platform/epics';
import { useParams } from 'next/navigation';
import { getDhoPathCoherence } from '../../../../@tab/coherence/constants';
import { usePersonById } from '@hypha-platform/core/client';

export default function ChatPage() {
const { lang, id: spaceId, chatId } = useParams<ChatPageParams>();
const {
conversation,
isLoading: isConversationLoading,
error,
} = useConversation({
chatId,
});
const { isLoading: isPersonLoading, person: creator } = usePersonById({
id: conversation?.creatorId,
});

const closeUrl = getDhoPathCoherence(lang, spaceId);

return (
<SidePanel>
{error ? (
<div className="text-error">{error}</div>
) : (
<ChatDetail
creator={{
avatar: creator?.avatarUrl || '',
name: creator?.name || '',
surname: creator?.surname || '',
address: creator?.address || '',
}}
isLoading={isConversationLoading || isPersonLoading}
conversation={conversation}
closeUrl={closeUrl}
/>
)}
</SidePanel>
);
}
29 changes: 29 additions & 0 deletions apps/web/src/app/[lang]/dho/[id]/@aside/[tab]/new-signal/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { CreateSignalForm, SidePanel } from '@hypha-platform/epics';
import { getDhoPathCoherence } from '../../../@tab/coherence/constants';
import { Locale } from '@hypha-platform/i18n';
import { findSpaceBySlug } from '@hypha-platform/core/server';
import { db } from '@hypha-platform/storage-postgres';
import { notFound } from 'next/navigation';

type PageProps = {
params: Promise<{ lang: Locale; id: string; tab: string }>;
};

export default async function NewSignalPage({ params }: PageProps) {
const { lang, id } = await params;

const spaceFromDb = await findSpaceBySlug({ slug: id }, { db });

if (!spaceFromDb) notFound();

const successfulUrl = getDhoPathCoherence(lang, id);
return (
<SidePanel>
<CreateSignalForm
successfulUrl={successfulUrl}
closeUrl={successfulUrl}
spaceId={spaceFromDb.id}
/>
</SidePanel>
);
}
2 changes: 2 additions & 0 deletions apps/web/src/app/[lang]/dho/[id]/@aside/coherence/default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Page from './page';
export default Page;
3 changes: 3 additions & 0 deletions apps/web/src/app/[lang]/dho/[id]/@aside/coherence/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default async function Index() {
return null;
}
5 changes: 5 additions & 0 deletions apps/web/src/app/[lang]/dho/[id]/@tab/coherence/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Locale } from '@hypha-platform/i18n';

export const getDhoPathCoherence = (lang: Locale, id: string) => {
return `/${lang}/dho/${id}/coherence`;
};
2 changes: 2 additions & 0 deletions apps/web/src/app/[lang]/dho/[id]/@tab/coherence/default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Page from './page';
export default Page;
19 changes: 19 additions & 0 deletions apps/web/src/app/[lang]/dho/[id]/@tab/coherence/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use client'; // Error boundaries must be Client Components

import { ErrorComponent } from '@web/components/error';

export default function ErrorBoundary({
error,
reset,
}: {
error: Error & { digest?: string };
reset: () => void;
}) {
return (
<ErrorComponent
message="Oops, something went wrong. Couldn't load coherence tab."
error={error}
reset={reset}
/>
);
}
29 changes: 29 additions & 0 deletions apps/web/src/app/[lang]/dho/[id]/@tab/coherence/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
COHERENCE_ORDERS,
CoherenceBlock,
CoherenceOrder,
} from '@hypha-platform/epics';
import { Locale } from '@hypha-platform/i18n';

type PageProps = {
params: Promise<{ lang: Locale; id: string }>;
searchParams?: Promise<{
order?: string;
type?: string;
}>;
};

export default async function CoherencePage(props: PageProps) {
const params = await props.params;
const searchParams = await props.searchParams;

const { lang, id } = params;

const orderRaw = searchParams?.order;
const order: CoherenceOrder =
orderRaw && COHERENCE_ORDERS.includes(orderRaw as CoherenceOrder)
? (orderRaw as CoherenceOrder)
: 'mostrecent';

return <CoherenceBlock lang={lang} spaceSlug={id} order={order} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { getDhoPathTreasury } from '../@tab/treasury/constants';
// import { getDhoPathOverview } from '../@tab/overview/constants'; // Overview tab removed
import { ScrollArea, ScrollBar } from '@hypha-platform/ui';
import { getActiveTabFromPath } from '@hypha-platform/epics';
import { getDhoPathCoherence } from '../@tab/coherence/constants';

export function NavigationTabs({ lang, id }: { lang: Locale; id: string }) {
const pathname = usePathname();
Expand All @@ -22,6 +23,11 @@ export function NavigationTabs({ lang, id }: { lang: Locale; id: string }) {
// name: 'overview',
// href: getDhoPathOverview(lang, id),
// },
{
title: 'Coherence',
name: 'coherence',
href: getDhoPathCoherence(lang, id),
},
{
title: 'Agreements',
name: 'agreements',
Expand Down
42 changes: 42 additions & 0 deletions apps/web/src/app/api/chat/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { convertToModelMessages, streamText } from 'ai';
import { google } from '@ai-sdk/google';
import type { UIMessage } from 'ai';
import { headers } from 'next/headers';
import { NextResponse } from 'next/server';

export const maxDuration = 30;

const SYSTEM_PROMPT =
'You are Hypha AI, a helpful assistant for the Hypha DAO platform. You help users analyze signals, draft proposals, understand community dynamics, and coordinate across spaces. Be concise and helpful.';

function getModel(modelId: string) {
switch (modelId) {
case 'gemini-2.5-flash':
return google('gemini-2.5-flash');
default:
return google('gemini-2.5-flash');
}
}

export async function POST(req: Request) {
const headersList = await headers();
const authToken = headersList.get('Authorization')?.split(' ')[1] || '';
if (!authToken) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

const {
messages,
modelId = 'gemini-2.5-flash',
}: { messages: UIMessage[]; modelId?: string } = await req.json();

const model = getModel(modelId);

const result = streamText({
model,
system: SYSTEM_PROMPT,
messages: await convertToModelMessages(messages),
});

return result.toUIMessageStreamResponse();
}
Loading
Loading