Skip to content

Commit a0b9566

Browse files
committed
add siwa to portal pages
1 parent 317211a commit a0b9566

19 files changed

+1069
-96
lines changed

apps/portal/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525
"@next/mdx": "15.3.2",
2626
"@radix-ui/react-dialog": "1.1.10",
2727
"@radix-ui/react-dropdown-menu": "^2.1.11",
28+
"@radix-ui/react-popover": "^1.1.10",
2829
"@radix-ui/react-select": "^2.2.2",
2930
"@radix-ui/react-slot": "^1.2.0",
3031
"@radix-ui/react-tabs": "^1.1.8",
32+
"@radix-ui/react-tooltip": "1.2.3",
3133
"@tanstack/react-query": "5.74.4",
3234
"@tryghost/content-api": "^1.11.22",
3335
"class-variance-authority": "^0.7.1",

apps/portal/src/app/globals.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ html {
4747
--success-text: 142.09 70.56% 35.29%;
4848
--warning-text: 38 92% 40%;
4949
--destructive-text: 357.15deg 100% 68.72%;
50+
--nebula-pink-foreground: 321 90% 51%;
5051

5152
/* Borders */
5253
--border: 0 0% 85%;
@@ -83,6 +84,7 @@ html {
8384
--destructive-text: 360 72% 55%;
8485
--success-text: 142 75% 50%;
8586
--inverted-foreground: 0 0% 0%;
87+
--nebula-pink-foreground: 321 90% 51%;
8688

8789
/* Borders */
8890
--border: 0 0% 15%;
@@ -159,3 +161,11 @@ button {
159161
.hide-scrollbar::-webkit-scrollbar {
160162
display: none; /* Safari and Chrome */
161163
}
164+
165+
.no-scrollbar {
166+
scrollbar-width: none; /* Firefox */
167+
}
168+
169+
.no-scrollbar::-webkit-scrollbar {
170+
display: none; /* Safari and Chrome */
171+
}

apps/portal/src/app/layout.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { Fira_Code, Inter } from "next/font/google";
66
import Script from "next/script";
77
import NextTopLoader from "nextjs-toploader";
88
import { StickyTopContainer } from "../components/Document/StickyTopContainer";
9+
import { CustomChatButton } from "../components/SiwaChat/CustomChatButton";
10+
import { examplePrompts } from "../components/SiwaChat/examplePrompts";
911
import { Banner } from "../components/others/Banner";
1012
import { EnableSmoothScroll } from "../components/others/SmoothScroll";
1113
import { PHProvider } from "../lib/posthog/Posthog";
@@ -67,6 +69,17 @@ export default function RootLayout({
6769
/>
6870
<EnableSmoothScroll />
6971

72+
{/* Siwa AI Chat Widget Floating Button */}
73+
<CustomChatButton
74+
label="Ask AI"
75+
networks={null}
76+
pageType="support"
77+
customApiParams={{}}
78+
examplePrompts={examplePrompts}
79+
authToken={undefined}
80+
requireLogin={false}
81+
/>
82+
7083
<div className="relative flex min-h-screen flex-col">
7184
<StickyTopContainer>
7285
{/* Note: Please change id as well when changing text or href so that new banner is shown to user even if user dismissed the older one */}
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
"use client";
2+
3+
import { ArrowUpIcon, CircleStopIcon } from "lucide-react";
4+
import { useState } from "react";
5+
import { cn } from "../../lib/utils";
6+
// NOTE: You will need to update these imports to match your portal's structure or stub them if not available
7+
import { Button } from "../ui/button";
8+
import { AutoResizeTextarea } from "../ui/textarea";
9+
import type { NebulaUserMessage } from "./types";
10+
11+
// Define proper TypeScript interfaces
12+
interface ChatContext {
13+
chainId?: number;
14+
contractAddress?: string;
15+
functionName?: string;
16+
parameters?: Record<string, unknown>;
17+
[key: string]: unknown;
18+
}
19+
20+
interface ConnectedWallet {
21+
address: string;
22+
walletId: string;
23+
chainId?: number;
24+
isActive?: boolean;
25+
}
26+
27+
// Props interfaces for better organization
28+
interface MessageHandlingProps {
29+
sendMessage: (message: NebulaUserMessage) => void;
30+
prefillMessage?: string;
31+
placeholder: string;
32+
}
33+
34+
interface ChatStateProps {
35+
isChatStreaming: boolean;
36+
abortChatStream: () => void;
37+
isConnectingWallet: boolean;
38+
}
39+
40+
interface WalletProps {
41+
connectedWallets: ConnectedWallet[];
42+
setActiveWallet: (wallet: ConnectedWallet) => void;
43+
}
44+
45+
interface ContextProps {
46+
context: ChatContext | undefined;
47+
setContext: (context: ChatContext | undefined) => void;
48+
showContextSelector: boolean;
49+
}
50+
51+
interface UIProps {
52+
className?: string;
53+
onLoginClick?: () => void;
54+
}
55+
56+
// Combined props interface
57+
interface ChatBarProps
58+
extends MessageHandlingProps,
59+
ChatStateProps,
60+
Partial<WalletProps>,
61+
Partial<ContextProps>,
62+
UIProps {}
63+
64+
export function ChatBar(props: ChatBarProps) {
65+
const [message, setMessage] = useState(props.prefillMessage || "");
66+
67+
const handleSendMessage = () => {
68+
if (message.trim() === "") return;
69+
70+
const userMessage: NebulaUserMessage = {
71+
role: "user",
72+
content: [{ type: "text", text: message }],
73+
};
74+
75+
props.sendMessage(userMessage);
76+
setMessage("");
77+
};
78+
79+
return (
80+
<div
81+
className={cn(
82+
"overflow-hidden rounded-3xl border border-border bg-card",
83+
props.className,
84+
)}
85+
>
86+
<div className="p-2">
87+
<MessageInput
88+
message={message}
89+
setMessage={setMessage}
90+
placeholder={props.placeholder}
91+
isChatStreaming={props.isChatStreaming}
92+
onSend={handleSendMessage}
93+
/>
94+
<ChatActions
95+
isChatStreaming={props.isChatStreaming}
96+
isConnectingWallet={props.isConnectingWallet}
97+
abortChatStream={props.abortChatStream}
98+
onSendMessage={handleSendMessage}
99+
canSend={message.trim() !== ""}
100+
/>
101+
</div>
102+
</div>
103+
);
104+
}
105+
106+
// Updated MessageInput component
107+
interface MessageInputProps {
108+
message: string;
109+
setMessage: (message: string) => void;
110+
placeholder: string;
111+
isChatStreaming: boolean;
112+
onSend: () => void;
113+
}
114+
115+
function MessageInput({
116+
message,
117+
setMessage,
118+
placeholder,
119+
isChatStreaming,
120+
onSend,
121+
}: MessageInputProps) {
122+
return (
123+
<div className="max-h-[200px] overflow-y-auto">
124+
<AutoResizeTextarea
125+
placeholder={placeholder}
126+
value={message}
127+
onChange={(e) => setMessage(e.target.value)}
128+
onKeyDown={(e) => {
129+
if (e.shiftKey) return;
130+
if (e.key === "Enter" && !isChatStreaming) {
131+
e.preventDefault();
132+
onSend();
133+
}
134+
}}
135+
className="min-h-[60px] resize-none border-none bg-transparent pt-2 leading-relaxed focus-visible:ring-0 focus-visible:ring-offset-0"
136+
disabled={isChatStreaming}
137+
/>
138+
</div>
139+
);
140+
}
141+
142+
// Updated ChatActions component
143+
interface ChatActionsProps {
144+
isChatStreaming: boolean;
145+
isConnectingWallet: boolean;
146+
abortChatStream: () => void;
147+
onSendMessage: () => void;
148+
canSend: boolean;
149+
}
150+
151+
function ChatActions({
152+
isChatStreaming,
153+
isConnectingWallet,
154+
abortChatStream,
155+
onSendMessage,
156+
canSend,
157+
}: ChatActionsProps) {
158+
return (
159+
<div className="flex items-end justify-between gap-3 px-2 pb-2">
160+
<div className="grow" />
161+
<div className="flex items-center gap-2">
162+
{isChatStreaming ? (
163+
<StopButton onStop={abortChatStream} />
164+
) : (
165+
<SendButton
166+
onSend={onSendMessage}
167+
disabled={!canSend || isConnectingWallet}
168+
/>
169+
)}
170+
</div>
171+
</div>
172+
);
173+
}
174+
175+
// Decomposed component for stop button
176+
interface StopButtonProps {
177+
onStop: () => void;
178+
}
179+
180+
function StopButton({ onStop }: StopButtonProps) {
181+
return (
182+
<Button
183+
variant="default"
184+
className="!h-auto w-auto shrink-0 gap-2 p-2"
185+
onClick={onStop}
186+
aria-label="Stop generating"
187+
>
188+
<CircleStopIcon className="size-4" />
189+
Stop
190+
</Button>
191+
);
192+
}
193+
194+
// Decomposed component for send button
195+
interface SendButtonProps {
196+
onSend: () => void;
197+
disabled: boolean;
198+
}
199+
200+
function SendButton({ onSend, disabled }: SendButtonProps) {
201+
return (
202+
<Button
203+
aria-label="Send"
204+
disabled={disabled}
205+
className="!h-auto w-auto border border-nebula-pink-foreground p-2 disabled:opacity-100"
206+
variant="pink"
207+
onClick={onSend}
208+
>
209+
<ArrowUpIcon className="size-4" />
210+
</Button>
211+
);
212+
}

0 commit comments

Comments
 (0)