Skip to content

Commit d89bbdf

Browse files
committed
fix: bugfixes
1 parent b754d2a commit d89bbdf

File tree

16 files changed

+522
-78
lines changed

16 files changed

+522
-78
lines changed

src/.env.selfhost.template

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ OPENAI_API_KEY=ollama
3333
# [BUILD-TIME] The model to pull if Ollama is being installed. This should match the model in the server's Modelfile.
3434
OPENAI_MODEL_NAME=qwen3:4b
3535

36+
# --- Gemini API Key (for Server - Memory MCP & optional LiteLLM) ---
37+
# [RUNTIME] Required for memory embeddings and can be used for chat via LiteLLM.
38+
GEMINI_API_KEY=<your-gemini-api-key>
39+
3640
# --- MongoDB Credentials (for Server) ---
3741
MONGO_USER=test
3842
MONGO_PASS=<generate_a_strong_password_for_mongo>
@@ -43,4 +47,10 @@ POSTGRES_PASS=<generate_a_strong_password_for_postgres>
4347
POSTGRES_DB=sentient_memory_db
4448

4549
# --- Redis Password (for Server - Celery) ---
46-
REDIS_PASSWORD=<generate_a_strong_password_for_redis>
50+
REDIS_PASSWORD=<generate_a_strong_password_for_redis>
51+
52+
# --- WhatsApp (WAHA) Credentials (for WAHA Service) ---
53+
# These are used by the WAHA container for WhatsApp integration.
54+
WAHA_API_KEY=admin
55+
WAHA_DASHBOARD_USERNAME=admin
56+
WAHA_DASHBOARD_PASSWORD=admin

src/client/app/api/notifications/delete/route.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,25 @@ const appServerUrl =
88

99
export const POST = withAuth(async function POST(request, { authHeader }) {
1010
try {
11-
const { notification_id } = await request.json()
12-
if (!notification_id) {
11+
const body = await request.json()
12+
if (!body.notification_id && !body.delete_all) {
1313
return NextResponse.json(
14-
{ error: "Notification ID is required" },
14+
{
15+
error: "Either 'notification_id' or 'delete_all' must be provided"
16+
},
1517
{ status: 400 }
1618
)
1719
}
1820

1921
const response = await fetch(`${appServerUrl}/notifications/delete`, {
2022
method: "POST",
2123
headers: { "Content-Type": "application/json", ...authHeader },
22-
body: JSON.stringify({ notification_id })
24+
body: JSON.stringify(body)
2325
})
2426

2527
const data = await response.json()
2628
if (!response.ok) {
27-
throw new Error(data.detail || "Failed to delete notification")
29+
throw new Error(data.detail || "Failed to delete notification(s)")
2830
}
2931
return NextResponse.json(data)
3032
} catch (error) {

src/client/app/chat/page.js

Lines changed: 124 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
IconTool,
2626
IconInfoCircle,
2727
IconSparkles,
28+
IconCheck,
2829
IconClockHour4,
2930
IconMessageChatbot,
3031
IconMapPin,
@@ -56,6 +57,7 @@ import { usePostHog } from "posthog-js/react"
5657
import SiriSpheres from "@components/voice-visualization/SiriSpheres"
5758
import { WebRTCClient } from "@lib/webrtc-client"
5859
import useClickOutside from "@hooks/useClickOutside"
60+
import { usePlan } from "@hooks/usePlan"
5961

6062
const toolIcons = {
6163
gmail: IconGoogleMail,
@@ -77,6 +79,104 @@ const toolIcons = {
7779
default: IconTool
7880
}
7981

82+
const proPlanFeatures = [
83+
{ name: "Text Chat", limit: "100 messages per day" },
84+
{ name: "Voice Chat", limit: "10 minutes per day" },
85+
{ name: "One-Time Tasks", limit: "20 async tasks per day" },
86+
{ name: "Recurring Tasks", limit: "10 active recurring workflows" },
87+
{ name: "Triggered Tasks", limit: "10 triggered workflows" },
88+
{
89+
name: "Parallel Agents",
90+
limit: "5 complex tasks per day with 50 sub agents"
91+
},
92+
{ name: "File Uploads", limit: "20 files per day" },
93+
{ name: "Memories", limit: "Unlimited memories" },
94+
{
95+
name: "Other Integrations",
96+
limit: "Notion, GitHub, Slack, Discord, Trello"
97+
}
98+
]
99+
100+
const UpgradeToProModal = ({ isOpen, onClose }) => {
101+
if (!isOpen) return null
102+
103+
const handleUpgrade = () => {
104+
const dashboardUrl = process.env.NEXT_PUBLIC_LANDING_PAGE_URL
105+
if (dashboardUrl) {
106+
window.open(`${dashboardUrl}/dashboard`, "_blank")
107+
}
108+
onClose()
109+
}
110+
111+
return (
112+
<AnimatePresence>
113+
{isOpen && (
114+
<motion.div
115+
initial={{ opacity: 0 }}
116+
animate={{ opacity: 1 }}
117+
exit={{ opacity: 0 }}
118+
className="fixed inset-0 bg-black/70 backdrop-blur-md z-[100] flex items-center justify-center p-4"
119+
onClick={onClose}
120+
>
121+
<motion.div
122+
initial={{ scale: 0.95, y: 20 }}
123+
animate={{ scale: 1, y: 0 }}
124+
exit={{ scale: 0.95, y: -20 }}
125+
transition={{ duration: 0.2, ease: "easeInOut" }}
126+
onClick={(e) => e.stopPropagation()}
127+
className="relative bg-neutral-900/90 backdrop-blur-xl p-6 rounded-2xl shadow-2xl w-full max-w-lg border border-neutral-700 flex flex-col"
128+
>
129+
<header className="text-center mb-4">
130+
<h2 className="text-2xl font-bold text-white flex items-center justify-center gap-2">
131+
<IconSparkles className="text-brand-orange" />
132+
Upgrade to Pro
133+
</h2>
134+
<p className="text-neutral-400 mt-2">
135+
Unlock Voice Mode and other powerful features.
136+
</p>
137+
</header>
138+
<main className="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-4 my-4">
139+
{proPlanFeatures.map((feature) => (
140+
<div
141+
key={feature.name}
142+
className="flex items-start gap-2.5"
143+
>
144+
<IconCheck
145+
size={18}
146+
className="text-green-400 flex-shrink-0 mt-0.5"
147+
/>
148+
<div>
149+
<p className="text-white text-sm font-medium">
150+
{feature.name}
151+
</p>
152+
<p className="text-neutral-400 text-xs">
153+
{feature.limit}
154+
</p>
155+
</div>
156+
</div>
157+
))}
158+
</main>
159+
<footer className="mt-4 flex flex-col gap-2">
160+
<button
161+
onClick={handleUpgrade}
162+
className="w-full py-2.5 px-5 rounded-lg bg-brand-orange hover:bg-brand-orange/90 text-brand-black font-semibold transition-colors"
163+
>
164+
Upgrade to Pro - $9/month
165+
</button>
166+
<button
167+
onClick={onClose}
168+
className="w-full py-2 px-5 rounded-lg hover:bg-neutral-800 text-sm font-medium text-neutral-400"
169+
>
170+
Not now
171+
</button>
172+
</footer>
173+
</motion.div>
174+
</motion.div>
175+
)}
176+
</AnimatePresence>
177+
)
178+
}
179+
80180
const StorylaneDemoModal = ({ onClose }) => {
81181
// The script adds a global Storylane object. The button's onClick will use it.
82182
const embedHtml = `
@@ -159,12 +259,15 @@ export default function ChatPage() {
159259

160260
const searchParams = useSearchParams()
161261
const router = useRouter()
262+
const { isPro } = usePlan()
162263

163264
// --- File Upload State ---
164265
const [selectedFile, setSelectedFile] = useState(null)
165266
const [isUploading, setIsUploading] = useState(false)
166267
const [uploadedFilename, setUploadedFilename] = useState(null)
167268

269+
// --- Pro Feature Modal ---
270+
const [isUpgradeModalOpen, setUpgradeModalOpen] = useState(false)
168271
// --- Voice Mode State ---
169272
const [isMuted, setIsMuted] = useState(false)
170273
const [isVoiceMode, setIsVoiceMode] = useState(false)
@@ -663,10 +766,11 @@ export default function ChatPage() {
663766
}
664767

665768
useEffect(() => {
666-
if (chatEndRef.current) {
667-
chatEndRef.current.scrollIntoView({ behavior: "smooth" })
769+
if (chatEndRef.current && !isVoiceMode) {
770+
// Use 'auto' for an instant scroll, which feels better when switching modes.
771+
chatEndRef.current.scrollIntoView({ behavior: "auto" })
668772
}
669-
}, [displayedMessages, thinking])
773+
}, [displayedMessages, thinking, isVoiceMode])
670774

671775
const getGreeting = () => {
672776
const hour = new Date().getHours()
@@ -969,6 +1073,11 @@ export default function ChatPage() {
9691073
}
9701074

9711075
const toggleVoiceMode = async () => {
1076+
if (!isPro) {
1077+
setUpgradeModalOpen(true)
1078+
return
1079+
}
1080+
9721081
if (isVoiceMode) {
9731082
handleStopVoice()
9741083
setIsVoiceMode(false)
@@ -986,11 +1095,10 @@ export default function ChatPage() {
9861095
}
9871096

9881097
useEffect(() => {
989-
// This cleanup now only runs when the ChatPage component unmounts
1098+
// This cleanup now only runs when the ChatPage component unmounts.
1099+
// The handleStopVoice function is now the primary way to disconnect.
9901100
return () => {
991-
if (webrtcClientRef.current) {
992-
webrtcClientRef.current.disconnect()
993-
}
1101+
webrtcClientRef.current?.disconnect()
9941102
}
9951103
}, [])
9961104

@@ -1138,7 +1246,11 @@ export default function ChatPage() {
11381246
onClick={toggleVoiceMode}
11391247
className="p-2.5 rounded-full text-white bg-neutral-700 hover:bg-neutral-600 transition-colors"
11401248
data-tooltip-id="home-tooltip"
1141-
data-tooltip-content="Switch to Voice Mode"
1249+
data-tooltip-content={
1250+
isPro
1251+
? "Switch to Voice Mode"
1252+
: "Voice Mode (Pro Feature)"
1253+
}
11421254
>
11431255
<IconWaveSine size={18} />
11441256
</button>
@@ -1398,6 +1510,10 @@ export default function ChatPage() {
13981510
src="/audio/connected.mp3"
13991511
preload="auto"
14001512
></audio>
1513+
<UpgradeToProModal
1514+
isOpen={isUpgradeModalOpen}
1515+
onClose={() => setUpgradeModalOpen(false)}
1516+
></UpgradeToProModal>
14011517
<AnimatePresence>
14021518
{isDemoModalOpen && (
14031519
<StorylaneDemoModal onClose={handleCloseDemo} />

0 commit comments

Comments
 (0)